Skip to content

Commit

Permalink
Trying to get rid of weird type parser functions in C++
Browse files Browse the repository at this point in the history
  • Loading branch information
oxisto committed Jun 11, 2023
1 parent 8560548 commit 5c6b31c
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 87 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -429,14 +429,13 @@ class TypeManager {
val others = allAncestors[i]
val newCommonAncestors: MutableSet<Ancestor> = HashSet()
// like Collection#retainAll but swaps relevant items out if the other set's
// matching
// ancestor has a higher depth
// matching ancestor has a higher depth
for (curr in commonAncestors) {
val toRetain =
others
.stream()
.filter { a: Ancestor -> a == curr }
.map<Ancestor> { a: Ancestor -> if (curr.depth >= a.depth) curr else a }
.map { a: Ancestor -> if (curr.depth >= a.depth) curr else a }
.findFirst()
toRetain.ifPresent { e: Ancestor -> newCommonAncestors.add(e) }
}
Expand All @@ -446,7 +445,7 @@ class TypeManager {
val lca =
commonAncestors
.stream()
.max(Comparator.comparingInt(ToIntFunction { obj: Ancestor -> obj.depth }))
.max(Comparator.comparingInt(Ancestor::depth))
val commonType =
lca.map { a: Ancestor ->
TypeParser.createFrom(a.record?.name.toString(), a.record?.language, false, ctx)
Expand Down Expand Up @@ -639,14 +638,12 @@ class TypeManager {
val finalToCheck = alias.root
val applicable =
scopeManager.currentTypedefs
.stream()
.filter { t: TypedefDeclaration -> t.alias.root.equals(finalToCheck) }
.findAny()
.map(TypedefDeclaration::type)
return if (applicable.isEmpty) {
.firstOrNull { t: TypedefDeclaration -> t.alias.root == finalToCheck }
?.type
return if (applicable == null) {
alias
} else {
TypeParser.reWrapType(alias, applicable.get())
TypeParser.reWrapType(alias, applicable)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -650,41 +650,6 @@ private static Type createFromUnsafe(
return finalType;
}

/**
* A specialized version of the type parsing function that needs a language frontend and does
* magic with generics and typedefs. This is legacy code and currently only used for CXX frontend
* and should be removed at some point.
*/
public static Type createFrom(
@NotNull String type, boolean resolveAlias, LanguageFrontend frontend) {
Type templateType =
searchForTemplateTypes(type, frontend.getScopeManager(), frontend.getTypeManager());
if (templateType != null) {
return templateType;
}

Type createdType = createFrom(type, frontend.getLanguage(), resolveAlias, frontend.getCtx());

if (createdType instanceof SecondOrderType) {
templateType =
searchForTemplateTypes(
createdType.getRoot().getName().toString(),
frontend.getScopeManager(),
frontend.getTypeManager());
if (templateType != null) {
createdType.setRoot(templateType);
}
}

return createdType;
}

private static Type searchForTemplateTypes(
@NotNull String type, ScopeManager scopeManager, TypeManager typeManager) {
return typeManager.searchTemplateScopeForDefinedParameterizedTypes(
scopeManager.getCurrentScope(), type);
}

/**
* Use this function for parsing new types and obtaining a new Type the TypeParser creates from
* the typeString.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import de.fraunhofer.aisec.cpg.graph.NodeBuilder.log
import de.fraunhofer.aisec.cpg.graph.scopes.Scope
import de.fraunhofer.aisec.cpg.graph.statements.expressions.*
import de.fraunhofer.aisec.cpg.graph.types.*
import de.fraunhofer.aisec.cpg.graph.types.PointerType.PointerOrigin.ARRAY
import de.fraunhofer.aisec.cpg.graph.types.PointerType.PointerOrigin.POINTER
import de.fraunhofer.aisec.cpg.passes.inference.IsInferredProvider
import org.slf4j.LoggerFactory

Expand Down Expand Up @@ -240,6 +242,27 @@ fun ContextProvider.newType(name: CharSequence): Type {
return type
}

fun ContextProvider.newArrayType(elementType: Type): Type {
val c = ctx ?: throw TranslationException("context not available")
val type = elementType.reference(ARRAY)

return c.typeManager.registerType(type)
}

fun ContextProvider.newPointerType(elementType: Type): Type {
val c = ctx ?: throw TranslationException("context not available")
val type = elementType.reference(POINTER)

return c.typeManager.registerType(type)
}

fun ContextProvider.newObjectType(name: CharSequence, generics: List<Type> = listOf()): Type {
val c = ctx ?: throw TranslationException("context not available")
val type = ObjectType(name, generics, false, (this as? LanguageProvider)?.language)

return c.typeManager.registerType(type)
}

fun LanguageProvider.newPrimitiveType(name: CharSequence, vararg modifier: String): Type {
// Prepend modifiers before the name. This does create a little bit of a problem for C++, where
// modifiers and type names can be specified in ANY order, meh.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration
import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression
import de.fraunhofer.aisec.cpg.graph.types.*
import de.fraunhofer.aisec.cpg.graph.types.FunctionType
import de.fraunhofer.aisec.cpg.helpers.Benchmark
import de.fraunhofer.aisec.cpg.passes.FunctionPointerCallResolver
import de.fraunhofer.aisec.cpg.passes.order.RegisterExtraPass
Expand All @@ -61,7 +60,10 @@ import org.eclipse.cdt.core.parser.IncludeFileContentProvider
import org.eclipse.cdt.core.parser.ScannerInfo
import org.eclipse.cdt.internal.core.dom.parser.ASTNode
import org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTLiteralExpression
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTQualifiedName
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTTemplateId
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTTypeId
import org.eclipse.cdt.internal.core.model.ASTStringUtil
import org.eclipse.cdt.internal.core.parser.IMacroDictionary
import org.eclipse.cdt.internal.core.parser.scanner.InternalFileContent
Expand Down Expand Up @@ -399,15 +401,10 @@ class CXXLanguageFrontend(language: Language<CXXLanguageFrontend>, ctx: Translat
130 -> // a string
newLiteral(
if (code.length >= 2) code.substring(1, code.length - 1) else "",
newPrimitiveType("char").reference(PointerType.PointerOrigin.POINTER),
code
)
else ->
newLiteral(
code,
newPrimitiveType("char").reference(PointerType.PointerOrigin.POINTER),
newPointerType(newPrimitiveType("char")),
code
)
else -> newLiteral(code, newPointerType(newPrimitiveType("char")), code)
}
return newAnnotationMember("", expression, code)
}
Expand Down Expand Up @@ -487,18 +484,21 @@ class CXXLanguageFrontend(language: Language<CXXLanguageFrontend>, ctx: Translat
hint: Declaration? = null
): Type {
// Retrieve the "name" of this type, including qualifiers.
// TODO: In the future, we should parse the qualifiers, such as const here, instead of in
// the TypeParser
val name = ASTStringUtil.getSignatureString(specifier, null)

var resolveAlias = false

var type =
when (specifier) {
is IASTSimpleDeclSpecifier -> {
if (hint is ConstructorDeclaration && hint.name.parent != null) {
parseType(hint.name.parent!!)
newObjectType(hint.name.parent!!)
} else if (name == "void") {
IncompleteType()
} else {
// A primitive type
parseType(name)
// We need to remove qualifiers such as "const" from the name here, because
// we model them as part of the variable declaration and not the type
newPrimitiveType(name.replace("const", "").trim())
}
}
is IASTNamedTypeSpecifier -> {
Expand All @@ -509,50 +509,94 @@ class CXXLanguageFrontend(language: Language<CXXLanguageFrontend>, ctx: Translat
// refers to a symbol in our current namespace. This means that we are doing
// some resolving in the frontend, which we actually want to avoid since it
// has limited view.
//
// Note: we cannot use parseType here, because of typedefs (and templates?) the
// TypeParser still needs to have access directly to the language frontend
// (meh!)
if (specifier.name is CPPASTQualifiedName) {
// Case a: FQN
TypeParser.createFrom(name, true, this)
resolveAlias = true
typeOf(specifier.name as CPPASTQualifiedName)
} else if (specifier.name is CPPASTTemplateId) {
typeOf(specifier.name as CPPASTTemplateId)
} else {
// Case b: Peek into our symbols. This is most likely limited to our current
// translation unit
resolveAlias = true

val decl =
scopeManager.currentScope?.let {
scopeManager.getRecordForName(it, Name(name))
}

// We found a symbol, so we can use its name
if (decl != null) {
TypeParser.createFrom(decl.name.toString(), true, this)
newObjectType(decl.name)
} else {
// It could be, that this is a parameterized type
val paramType =
typeManager.searchTemplateScopeForDefinedParameterizedTypes(
scopeManager.currentScope,
specifier.name.toString()
)
// Otherwise, we keep it as a local name and hope for the best
TypeParser.createFrom(name, true, this)
paramType ?: newObjectType(specifier.name.toString())
}
}
}
is IASTCompositeTypeSpecifier -> {
// A class. This actually also declares the class. At the moment, we handle this
// in handleSimpleDeclaration, but we might want to move it here
TypeParser.createFrom(name, true, this)
resolveAlias = true

newObjectType(specifier.name.toString())
}
is IASTElaboratedTypeSpecifier -> {
resolveAlias = true

// A class or struct
TypeParser.createFrom(name, true, this)
newObjectType(specifier.name.toString())
}
else -> {
newUnknownType()
}
}

type = typeManager.registerType(type)
type =
if (resolveAlias) {
typeManager.registerType(typeManager.resolvePossibleTypedef(type, scopeManager))
} else {
typeManager.registerType(type)
}
type = this.adjustType(declarator, type)

return type
}

private fun typeOf(name: CPPASTQualifiedName): Type {
val last = name.lastName
return if (last is CPPASTTemplateId) {
typeOf(last, name.qualifier.joinToString("::", postfix = "::"))
} else {
newObjectType(name.toString())
}
}

private fun typeOf(name: CPPASTTemplateId, prefix: String? = ""): Type {
// Build fqn
val fqn = prefix + name.templateName.toString()
val generics = mutableListOf<Type>()

// Loop through template arguments
for (arg in name.templateArguments) {
if (arg is CPPASTTypeId) {
generics += typeOf(arg)
} else if (arg is CPPASTLiteralExpression) {
// This is most likely a constant in a template class definition, but we need to
// model this somehow
generics += newObjectType(arg.rawSignature)
}
}

return newObjectType(fqn, generics)
}

/**
* This is a little helper function, primarily used by [typeOf]. It's primary purpose is to
* "adjust" the [incoming] type based on the [declarator]. This is needed because the type
Expand Down Expand Up @@ -627,8 +671,22 @@ class CXXLanguageFrontend(language: Language<CXXLanguageFrontend>, ctx: Translat
type = adjustType(declarator.nestedDeclarator, type)
}

type = typeManager.registerType(type)

// Check for parameterized types
if (type is SecondOrderType) {
val templateType =
typeManager.searchTemplateScopeForDefinedParameterizedTypes(
scopeManager.currentScope,
type.root.name.toString()
)
if (templateType != null) {
type.root = templateType
}
}

// Make sure, the type manager knows about this type
return typeManager.registerType(type)
return type
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,12 +319,7 @@ class DeclarationHandler(lang: CXXLanguageFrontend) :
)
typeParamDeclaration.type = parameterizedType
if (templateParameter.defaultType != null) {
val defaultType =
TypeParser.createFrom(
templateParameter.defaultType.declSpecifier.rawSignature,
false,
frontend
)
val defaultType = frontend.typeOf(templateParameter.defaultType)
typeParamDeclaration.default = defaultType
}
templateDeclaration.addParameter(typeParamDeclaration)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,8 @@ import de.fraunhofer.aisec.cpg.helpers.Util
import java.util.*
import java.util.function.Supplier
import java.util.regex.Pattern
import java.util.stream.Collectors
import org.eclipse.cdt.core.dom.ast.*
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier.ICPPASTBaseSpecifier
import org.eclipse.cdt.core.dom.ast.gnu.cpp.GPPLanguage
import org.eclipse.cdt.internal.core.dom.parser.cpp.*

Expand Down Expand Up @@ -461,14 +459,12 @@ class DeclaratorHandler(lang: CXXLanguageFrontend) :
ctx.rawSignature,
)

// Handle c++ classes
// Handle C++ classes
if (ctx is CPPASTCompositeTypeSpecifier) {
recordDeclaration.superClasses =
Arrays.stream(ctx.baseSpecifiers)
.map { b: ICPPASTBaseSpecifier ->
TypeParser.createFrom(b.nameSpecifier.toString(), true, frontend)
}
.collect(Collectors.toList())
ctx.baseSpecifiers
.map { newObjectType(it.nameSpecifier.toString()) }
.toMutableList()
}

frontend.scopeManager.addDeclaration(recordDeclaration)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,9 +174,8 @@ class ExpressionHandler(lang: CXXLanguageFrontend) :
}

private fun handleNewExpression(ctx: CPPASTNewExpression): Expression {
val name = ctx.typeId.declSpecifier.toString()
val code = ctx.rawSignature
val t = TypeParser.createFrom(name, true, frontend)
val t = frontend.typeOf(ctx.typeId)
val init = ctx.initializer

// we need to check, whether this is an array initialization or a single new expression
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ internal class CXXLanguageFrontendTest : BaseTest() {
(statement.statements[0] as DeclarationStatement).singleDeclaration
as VariableDeclaration
assertNotNull(x)
assertEquals(tu.newPrimitiveType("int").reference(ARRAY), x.type)
assertEquals(tu.newArrayType(tu.newPrimitiveType("int")), x.type)

// initializer is an initializer list expression
val ile = x.initializer as? InitializerListExpression
Expand Down Expand Up @@ -881,7 +881,8 @@ internal class CXXLanguageFrontendTest : BaseTest() {

// int z[] = { 2, 3, 4 };
val z = tu.getDeclarationAs(2, VariableDeclaration::class.java)
assertEquals(tu.newPrimitiveType("int").reference(ARRAY), z!!.type)
assertNotNull(z)
assertEquals(tu.newArrayType(tu.newPrimitiveType("int")), z.type)

initializer = z.initializer
assertNotNull(initializer)
Expand Down

0 comments on commit 5c6b31c

Please sign in to comment.