From 284856cec0195c55b5ec9faf83aceb0b80a69192 Mon Sep 17 00:00:00 2001 From: Christian Banse Date: Fri, 26 May 2023 16:39:16 +0200 Subject: [PATCH 1/5] Moving TypeManager towards non-singleton --- .../aisec/cpg/passes/UnreachableEOGPass.kt | 8 +- .../aisec/cpg/analysis/ValueEvaluatorTest.kt | 11 +- .../aisec/cpg/graph/TypeManager.java | 57 +- .../aisec/cpg/graph/types/TypeParser.java | 74 ++- .../de/fraunhofer/aisec/cpg/ScopeManager.kt | 11 +- .../aisec/cpg/TranslationContext.kt | 51 ++ .../aisec/cpg/TranslationManager.kt | 85 +-- .../fraunhofer/aisec/cpg/TranslationResult.kt | 37 +- .../fraunhofer/aisec/cpg/frontends/Handler.kt | 7 +- .../aisec/cpg/frontends/Language.kt | 17 +- .../aisec/cpg/frontends/LanguageFrontend.kt | 23 +- .../aisec/cpg/frontends/LanguageTraits.kt | 10 +- .../aisec/cpg/frontends/cpp/CLanguage.kt | 29 +- .../aisec/cpg/frontends/cpp/CPPLanguage.kt | 44 +- .../cpg/frontends/cpp/CXXLanguageFrontend.kt | 14 +- .../cpg/frontends/cpp/DeclarationHandler.kt | 27 +- .../cpg/frontends/cpp/ExpressionHandler.kt | 2 +- .../aisec/cpg/graph/ExpressionBuilder.kt | 2 + .../de/fraunhofer/aisec/cpg/graph/Node.kt | 18 +- .../fraunhofer/aisec/cpg/graph/NodeBuilder.kt | 27 +- .../aisec/cpg/graph/builder/Fluent.kt | 18 +- .../declarations/ConstructorDeclaration.kt | 4 +- .../graph/declarations/FieldDeclaration.kt | 14 +- .../graph/declarations/FunctionDeclaration.kt | 7 +- .../graph/declarations/RecordDeclaration.kt | 4 +- .../graph/declarations/ValueDeclaration.kt | 49 +- .../graph/declarations/VariableDeclaration.kt | 9 +- .../expressions/ArrayCreationExpression.kt | 6 +- .../ArraySubscriptionExpression.kt | 4 +- .../expressions/AssignExpression.kt | 4 +- .../statements/expressions/BinaryOperator.kt | 4 +- .../statements/expressions/CallExpression.kt | 6 +- .../statements/expressions/CastExpression.kt | 6 +- .../expressions/ConditionalExpression.kt | 6 +- .../expressions/ConstructExpression.kt | 8 +- .../DeclaredReferenceExpression.kt | 6 +- .../statements/expressions/Expression.kt | 41 +- .../statements/expressions/ExpressionList.kt | 6 +- .../expressions/InitializerListExpression.kt | 21 +- .../expressions/LambdaExpression.kt | 7 +- .../statements/expressions/UnaryOperator.kt | 6 +- .../aisec/cpg/graph/types/FunctionType.kt | 4 +- .../aisec/cpg/graph/types/HasType.kt | 34 +- .../aisec/cpg/passes/CXXCallResolverHelper.kt | 67 ++- .../aisec/cpg/passes/CallResolver.kt | 34 +- .../cpg/passes/ControlFlowSensitiveDFGPass.kt | 9 +- .../de/fraunhofer/aisec/cpg/passes/DFGPass.kt | 6 +- .../aisec/cpg/passes/EdgeCachePass.kt | 6 +- .../cpg/passes/EvaluationOrderGraphPass.kt | 11 +- .../aisec/cpg/passes/FilenameMapper.kt | 6 +- .../cpg/passes/FunctionPointerCallResolver.kt | 8 +- .../aisec/cpg/passes/ImportResolver.kt | 11 +- .../de/fraunhofer/aisec/cpg/passes/Pass.kt | 55 +- .../cpg/passes/StatisticsCollectionPass.kt | 8 +- .../aisec/cpg/passes/SymbolResolverPass.kt | 8 +- .../aisec/cpg/passes/TypeHierarchyResolver.kt | 6 +- .../aisec/cpg/passes/TypeResolver.kt | 9 +- .../aisec/cpg/passes/VariableUsageResolver.kt | 27 +- .../aisec/cpg/passes/inference/Inference.kt | 54 +- .../de/fraunhofer/aisec/cpg/GraphExamples.kt | 129 +++-- .../frontends/cpp/CXXLanguageFrontendTest.kt | 90 ++- .../aisec/cpg/frontends/cpp/CXXLiteralTest.kt | 39 +- .../cpp/CXXSymbolConfigurationTest.kt | 30 +- .../fraunhofer/aisec/cpg/graph/FluentTest.kt | 8 +- .../expressions/AssignExpressionTest.kt | 8 +- .../cpg/graph/types/TypePropagationTest.kt | 11 +- .../aisec/cpg/graph/types/TypeTests.kt | 529 ++++++++++-------- .../aisec/cpg/graph/types/TypedefTest.kt | 10 +- .../aisec/cpg/passes/CallResolverTest.kt | 9 +- .../aisec/cpg/passes/ReplaceTest.kt | 10 +- .../aisec/cpg/passes/UnresolvedDFGPassTest.kt | 31 +- .../cpg/passes/scopes/ScopeManagerTest.kt | 49 +- .../de/fraunhofer/aisec/cpg/BaseTest.kt | 12 - .../de/fraunhofer/aisec/cpg/TestUtils.kt | 10 - .../aisec/cpg/frontends/TestLanguage.kt | 36 +- .../src/main/golang/frontend/frontend.go | 10 + .../src/main/golang/frontend/handler.go | 18 +- cpg-language-go/src/main/golang/language.go | 19 + cpg-language-go/src/main/golang/types.go | 6 +- .../aisec/cpg/frontends/golang/GoLanguage.kt | 9 - .../frontends/golang/GoLanguageFrontend.kt | 10 +- .../aisec/cpg/passes/GoExtraPass.kt | 22 +- .../golang/GoLanguageFrontendTest.kt | 31 +- .../cpg/frontends/java/DeclarationHandler.kt | 27 +- .../cpg/frontends/java/ExpressionHandler.kt | 20 +- .../aisec/cpg/frontends/java/JavaLanguage.kt | 8 - .../frontends/java/JavaLanguageFrontend.kt | 16 +- .../cpg/passes/JavaCallResolverHelper.kt | 6 +- .../JavaExternalTypeHierarchyResolver.kt | 24 +- .../java/JavaLanguageFrontendTest.kt | 38 +- .../aisec/cpg/graph/types/TypeTests.kt | 238 ++++---- .../aisec/cpg/passes/CallResolverTest.kt | 8 +- .../cpg/frontends/llvm/LLVMIRLanguage.kt | 9 - .../frontends/llvm/LLVMIRLanguageFrontend.kt | 28 +- .../aisec/cpg/passes/CompressLLVMPass.kt | 11 +- .../llvm/LLVMIRLanguageFrontendTest.kt | 28 +- .../cpg/frontends/python/PythonLanguage.kt | 9 - .../python/PythonLanguageFrontend.kt | 10 +- .../frontends/python/PythonFrontendTest.kt | 33 +- .../typescript/JavaScriptLanguage.kt | 9 - .../typescript/TypeScriptLanguageFrontend.kt | 16 +- .../TypescriptLanguageFrontendTest.kt | 12 +- 102 files changed, 1403 insertions(+), 1436 deletions(-) create mode 100644 cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationContext.kt diff --git a/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/UnreachableEOGPass.kt b/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/UnreachableEOGPass.kt index 204c0ebc46..6631cdfb00 100644 --- a/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/UnreachableEOGPass.kt +++ b/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/UnreachableEOGPass.kt @@ -25,8 +25,7 @@ */ package de.fraunhofer.aisec.cpg.passes -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.analysis.ValueEvaluator import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration @@ -42,10 +41,7 @@ import de.fraunhofer.aisec.cpg.processing.strategy.Strategy * by setting the [Properties.UNREACHABLE] property of an eog-edge to true. */ @DependsOn(ControlFlowSensitiveDFGPass::class) -class UnreachableEOGPass( - config: TranslationConfiguration, - scopeManager: ScopeManager, -) : TranslationUnitPass(config, scopeManager) { +class UnreachableEOGPass(ctx: TranslationContext) : TranslationUnitPass(ctx) { override fun accept(tu: TranslationUnitDeclaration) { tu.accept( Strategy::AST_FORWARD, diff --git a/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/analysis/ValueEvaluatorTest.kt b/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/analysis/ValueEvaluatorTest.kt index 88d54c8d9d..eea8ccebcd 100644 --- a/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/analysis/ValueEvaluatorTest.kt +++ b/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/analysis/ValueEvaluatorTest.kt @@ -27,6 +27,7 @@ package de.fraunhofer.aisec.cpg.analysis import de.fraunhofer.aisec.cpg.TestUtils import de.fraunhofer.aisec.cpg.frontends.TestHandler +import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend import de.fraunhofer.aisec.cpg.frontends.java.JavaLanguage import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration @@ -181,7 +182,7 @@ class ValueEvaluatorTest { @Test fun testHandlePlus() { - with(TestHandler()) { + with(TestHandler(TestLanguageFrontend())) { val binOp = newBinaryOperator("+") binOp.lhs = newLiteral(3, parseType("int")) binOp.rhs = newLiteral(2, parseType("int")) @@ -242,7 +243,7 @@ class ValueEvaluatorTest { @Test fun testHandleMinus() { - with(TestHandler()) { + with(TestHandler(TestLanguageFrontend())) { val binOp = newBinaryOperator("-") binOp.lhs = newLiteral(3, parseType("int")) binOp.rhs = newLiteral(2, parseType("int")) @@ -300,7 +301,7 @@ class ValueEvaluatorTest { @Test fun testHandleTimes() { - with(TestHandler()) { + with(TestHandler(TestLanguageFrontend())) { val binOp = newBinaryOperator("*") binOp.lhs = newLiteral(3, parseType("int")) binOp.rhs = newLiteral(2, parseType("int")) @@ -358,7 +359,7 @@ class ValueEvaluatorTest { @Test fun testHandleDiv() { - with(TestHandler()) { + with(TestHandler(TestLanguageFrontend())) { // For two integer values, we keep the result as a long. val binOp = newBinaryOperator("/") binOp.lhs = newLiteral(3, parseType("int")) @@ -421,7 +422,7 @@ class ValueEvaluatorTest { @Test fun testHandleUnary() { - with(TestHandler()) { + with(TestHandler(TestLanguageFrontend())) { val neg = newUnaryOperator("-", false, true) neg.input = newLiteral(3, parseType("int")) assertEquals(-3, ValueEvaluator().evaluate(neg)) diff --git a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/TypeManager.java b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/TypeManager.java index 674fea298e..853a378d4a 100644 --- a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/TypeManager.java +++ b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/TypeManager.java @@ -28,6 +28,7 @@ import static de.fraunhofer.aisec.cpg.graph.DeclarationBuilderKt.newTypedefDeclaration; import de.fraunhofer.aisec.cpg.ScopeManager; +import de.fraunhofer.aisec.cpg.TranslationContext; import de.fraunhofer.aisec.cpg.frontends.Language; import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend; import de.fraunhofer.aisec.cpg.frontends.cpp.CLanguage; @@ -56,7 +57,7 @@ public class TypeManager { // TODO: document/remove this regexp, merge with other pattern private static final Pattern funPointerPattern = Pattern.compile("\\(?\\*(?[^()]+)\\)?\\(.*\\)"); - @NotNull private static TypeManager instance = new TypeManager(); + private static boolean typeSystemActive = true; @NotNull @@ -90,10 +91,6 @@ public class TypeManager { private final Set firstOrderTypes = Collections.synchronizedSet(new HashSet<>()); private final Set secondOrderTypes = Collections.synchronizedSet(new HashSet<>()); - public static void reset() { - instance = new TypeManager(); - } - /** * @param recordDeclaration that is instantiated by a template containing parameterizedtypes * @param name of the ParameterizedType we want to get @@ -121,7 +118,6 @@ public ParameterizedType getTypeParameter(RecordDeclaration recordDeclaration, S * @param typeParameters List containing all ParameterizedTypes used by the recordDeclaration and * will be stored as value in the map */ - @Deprecated public void addTypeParameter( RecordDeclaration recordDeclaration, List typeParameters) { this.recordToTypeParameters.put(recordDeclaration, typeParameters); @@ -136,7 +132,6 @@ public void addTypeParameter( * @return */ @Nullable - @Deprecated public ParameterizedType getTypeParameter(TemplateDeclaration templateDeclaration, String name) { if (this.templateToTypeParameters.containsKey(templateDeclaration)) { for (ParameterizedType parameterizedType : @@ -259,11 +254,7 @@ public boolean typeExists(String name) { .anyMatch(type -> type.getRoot().getName().toString().equals(name)); } - private TypeManager() {} - - public static @NotNull TypeManager getInstance() { - return instance; - } + public TypeManager() {} public static boolean isTypeSystemActive() { return typeSystemActive; @@ -413,11 +404,13 @@ private Set unwrapTypes(Collection types, WrapState wrapState) { * information and their record declarations. We want to get rid of that in the future. * * @param types the types to compare - * @param provider a {@link ScopeProvider}. + * @param ctx a {@link TranslationContext}. * @return the common type */ @NotNull - public Optional getCommonType(@NotNull Collection types, ScopeProvider provider) { + public Optional getCommonType(@NotNull Collection types, TranslationContext ctx) { + var provider = ctx.getScopeManager(); + // TODO: Documentation needed. boolean sameType = types.stream().map(t -> t.getClass().getCanonicalName()).collect(Collectors.toSet()).size() @@ -513,7 +506,10 @@ public Optional getCommonType(@NotNull Collection types, ScopeProvid Optional lca = commonAncestors.stream().max(Comparator.comparingInt(Ancestor::getDepth)); Optional commonType = - lca.map(a -> TypeParser.createFrom(a.getRecord().getName(), a.getRecord().getLanguage())); + lca.map( + a -> + TypeParser.createFrom( + a.getRecord().getName().toString(), a.getRecord().getLanguage(), false, ctx)); Type finalType; if (commonType.isPresent()) { @@ -549,6 +545,7 @@ private Set getAncestors(RecordDeclaration recordDeclaration, int dept public boolean isSupertypeOf(Type superType, Type subType, MetadataProvider provider) { Language language = null; + TranslationContext ctx; if (superType instanceof UnknownType && subType instanceof UnknownType) return true; @@ -560,6 +557,13 @@ public boolean isSupertypeOf(Type superType, Type subType, MetadataProvider prov language = languageProvider.getLanguage(); } + if (provider instanceof ContextProvider contextProvider) { + ctx = contextProvider.getCtx(); + } else { + log.error("Missing context provider"); + return false; + } + // arrays and pointers match in C/C++ // TODO: Make this independent from the specific language if (language instanceof CLanguage && checkArrayAndPointer(superType, subType)) { @@ -576,8 +580,7 @@ public boolean isSupertypeOf(Type superType, Type subType, MetadataProvider prov return false; } - Optional commonType = - getCommonType(new HashSet<>(List.of(superType, subType)), scopeProvider); + Optional commonType = getCommonType(new HashSet<>(List.of(superType, subType)), ctx); if (commonType.isPresent()) { return commonType.get().equals(superType); } else { @@ -608,10 +611,11 @@ public void cleanup() { this.typeToRecord.clear(); } - private Type getTargetType(Type currTarget, String alias) { + private Type getTargetType(Type currTarget, String alias, TranslationContext ctx) { if (alias.contains("(") && alias.contains("*")) { // function pointer - return TypeParser.createFrom(currTarget.getName() + " " + alias, currTarget.getLanguage()); + return TypeParser.createFrom( + currTarget.getName() + " " + alias, currTarget.getLanguage(), false, ctx); } else if (alias.endsWith("]")) { // array type return currTarget.reference(PointerType.PointerOrigin.ARRAY); @@ -627,20 +631,23 @@ private Type getTargetType(Type currTarget, String alias) { } } - private Type getAlias(String alias, @NotNull Language language) { + private Type getAlias( + String alias, + @NotNull Language language, + TranslationContext ctx) { if (alias.contains("(") && alias.contains("*")) { // function pointer Matcher matcher = funPointerPattern.matcher(alias); if (matcher.find()) { - return TypeParser.createIgnoringAlias(matcher.group("alias"), language); + return TypeParser.createIgnoringAlias(matcher.group("alias"), language, ctx); } else { log.error("Could not find alias name in function pointer typedef: {}", alias); - return TypeParser.createIgnoringAlias(alias, language); + return TypeParser.createIgnoringAlias(alias, language, ctx); } } else { alias = alias.split("\\[")[0]; alias = alias.replace("*", ""); - return TypeParser.createIgnoringAlias(alias, language); + return TypeParser.createIgnoringAlias(alias, language, ctx); } } @@ -658,9 +665,9 @@ private Type getAlias(String alias, @NotNull Language separate( } private static List getParameterList( - String parameterList, Language language) { + String parameterList, Language language, TranslationContext ctx) { if (parameterList.startsWith("(") && parameterList.endsWith(")")) { parameterList = parameterList.trim().substring(1, parameterList.trim().length() - 1); } @@ -291,7 +292,7 @@ private static List getParameterList( for (String parameter : parametersSplit) { // ignore void parameters // TODO: WHY?? if (parameter.length() > 0 && !parameter.trim().equals("void")) { - parameters.add(createFrom(parameter.trim(), language)); + parameters.add(createFrom(parameter.trim(), language, false, ctx)); } } @@ -299,7 +300,7 @@ private static List getParameterList( } private static List getGenerics( - String typeName, Language language) { + String typeName, Language language, TranslationContext ctx) { List genericList = new ArrayList<>(); if (language instanceof HasGenerics hasGenerics && typeName.indexOf(hasGenerics.getStartCharacter()) > -1 @@ -311,7 +312,7 @@ private static List getGenerics( String[] parametersSplit = generics.split(","); for (String parameter : parametersSplit) { - genericList.add(createFrom(parameter.trim(), language)); + genericList.add(createFrom(parameter.trim(), language, false, ctx)); } } return genericList; @@ -457,16 +458,18 @@ public static Type reWrapType(@NotNull Type oldChain, @NotNull Type newRoot) { } /** - * Does the same as {@link #createIgnoringAlias(String, Language)} but explicitly does not use - * type alias resolution. This is usually not what you want. Use with care! + * Does the same as createFrom but explicitly does not use type alias resolution. This is usually + * not what you want. Use with care! * * @param string the string representation of the type * @return the type */ @NotNull public static Type createIgnoringAlias( - @NotNull String string, @NotNull Language language) { - return createFrom(string, language); + @NotNull String string, + @NotNull Language language, + TranslationContext ctx) { + return createFrom(string, language, false, ctx); } @NotNull @@ -547,7 +550,10 @@ private static Type createFromUnsafe( @NotNull String type, boolean resolveAlias, @NotNull Language language, - @Nullable ScopeManager scopeManager) { + TranslationContext ctx) { + var typeManager = ctx.getTypeManager(); + var scopeManager = ctx.getScopeManager(); + // Check if Problems during Parsing if (!checkValidTypeString(type)) { return UnknownType.getUnknownType(language); @@ -597,7 +603,6 @@ private static Type createFromUnsafe( counter++; Type finalType; - TypeManager typeManager = TypeManager.getInstance(); // Check if type is FunctionPointer Matcher funcptr = getFunctionPtrMatcher(typeBlocks.subList(counter, typeBlocks.size())); @@ -606,8 +611,8 @@ private static Type createFromUnsafe( if (finalType != null) { // Nothing to do here } else if (funcptr != null) { - Type returnType = createFrom(typeName, language); - List parameterList = getParameterList(funcptr.group("args"), language); + Type returnType = createFrom(typeName, language, false, ctx); + List parameterList = getParameterList(funcptr.group("args"), language, ctx); return typeManager.registerType(new FunctionPointerType(parameterList, language, returnType)); } else if (isIncompleteType(typeName)) { @@ -619,7 +624,7 @@ private static Type createFromUnsafe( } else { // ObjectType // Obtain possible generic List from TypeString - List generics = getGenerics(typeName, language); + List generics = getGenerics(typeName, language, ctx); typeName = removeGenerics(typeName, language); finalType = new ObjectType(typeName, generics, primitiveType, language); } @@ -638,7 +643,7 @@ private static Type createFromUnsafe( // the graph representing the type finalType = typeManager.registerType(finalType); - if (resolveAlias && scopeManager != null) { + if (resolveAlias) { return typeManager.registerType(typeManager.resolvePossibleTypedef(finalType, scopeManager)); } @@ -650,18 +655,22 @@ private static Type createFromUnsafe( * 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 lang) { - Type templateType = searchForTemplateTypes(type, lang.getScopeManager()); + 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, lang.getLanguage(), resolveAlias, lang.getScopeManager()); + Type createdType = createFrom(type, frontend.getLanguage(), resolveAlias, frontend.getCtx()); if (createdType instanceof SecondOrderType) { templateType = searchForTemplateTypes( - createdType.getRoot().getName().toString(), lang.getScopeManager()); + createdType.getRoot().getName().toString(), + frontend.getScopeManager(), + frontend.getTypeManager()); if (templateType != null) { createdType.setRoot(templateType); } @@ -670,9 +679,10 @@ public static Type createFrom(@NotNull String type, boolean resolveAlias, Langua return createdType; } - private static Type searchForTemplateTypes(@NotNull String type, ScopeManager scopeManager) { - return TypeManager.getInstance() - .searchTemplateScopeForDefinedParameterizedTypes(scopeManager.getCurrentScope(), type); + private static Type searchForTemplateTypes( + @NotNull String type, ScopeManager scopeManager, TypeManager typeManager) { + return typeManager.searchTemplateScopeForDefinedParameterizedTypes( + scopeManager.getCurrentScope(), type); } /** @@ -682,7 +692,7 @@ private static Type searchForTemplateTypes(@NotNull String type, ScopeManager sc * @param type string with type information * @param language the language in which the type exists. * @param resolveAlias should replace with original type in typedefs - * @param scopeManager optional, but required if resolveAlias is true + * @param ctx the translation context * @return new type representing the type string. If an exception occurs during the parsing, * UnknownType is returned */ @@ -691,28 +701,12 @@ public static Type createFrom( @NotNull String type, Language language, boolean resolveAlias, - ScopeManager scopeManager) { + TranslationContext ctx) { try { - return createFromUnsafe(type, resolveAlias, language, scopeManager); + return createFromUnsafe(type, resolveAlias, language, ctx); } catch (Exception e) { log.error("Could not parse the type correctly", e); return UnknownType.getUnknownType(language); } } - - /** Parses the type from a char sequence and the supplied language. */ - @NotNull - public static Type createFrom( - @NotNull CharSequence name, - Boolean resolveAlias, - Language language) { - return createFrom(name.toString(), language, resolveAlias, null); - } - - /** Parses the type from a char sequence and the supplied language. */ - @NotNull - public static Type createFrom( - @NotNull CharSequence name, Language language) { - return createFrom(name.toString(), language, false, null); - } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/ScopeManager.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/ScopeManager.kt index 1ca4756958..42eb46de39 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/ScopeManager.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/ScopeManager.kt @@ -791,9 +791,9 @@ class ScopeManager : ScopeProvider { override val scope: Scope? get() = currentScope - fun activateTypes(node: Node) { + fun activateTypes(node: Node, typeManager: TypeManager) { val num = AtomicInteger() - val typeCache = TypeManager.getInstance().typeCache + val typeCache = typeManager.typeCache node.accept( Strategy::AST_FORWARD, object : IVisitor() { @@ -802,8 +802,7 @@ class ScopeManager : ScopeProvider { val typeNode = t as HasType typeCache.getOrDefault(typeNode, emptyList()).forEach { (t as HasType).type = - TypeManager.getInstance() - .resolvePossibleTypedef(it, this@ScopeManager) + typeManager.resolvePossibleTypedef(it, this@ScopeManager) } typeCache.remove(t as HasType) num.getAndIncrement() @@ -816,9 +815,7 @@ class ScopeManager : ScopeProvider { // For some nodes it may happen that they are not reachable via AST, but we still need to // set their type to the requested value typeCache.forEach { (n, types) -> - types.forEach { t -> - n.type = TypeManager.getInstance().resolvePossibleTypedef(t, this) - } + types.forEach { t -> n.type = typeManager.resolvePossibleTypedef(t, this) } } } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationContext.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationContext.kt new file mode 100644 index 0000000000..0cdfc1333c --- /dev/null +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationContext.kt @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2023, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +package de.fraunhofer.aisec.cpg + +import de.fraunhofer.aisec.cpg.graph.TypeManager + +/** + * The translation context holds all necessary managers and configurations needed during the + * translation process. + */ +class TranslationContext( + /** The configuration for this translation. */ + val config: TranslationConfiguration, + + /** + * The scope manager which comprises the complete translation result. In case of sequential + * parsing, this scope manager is passed to the individual frontends one after another. In case + * of sequential parsing, individual scope managers will be passed to each language frontend + * (through individual contexts) and then finally merged into a final one. + */ + val scopeManager: ScopeManager, + + /** + * The type manager is responsible for managing type information. Currently, we have one + * instance of a [TypeManager] for the overall [TranslationResult]. + */ + val typeManager: TypeManager +) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationManager.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationManager.kt index f58640cea8..5b7f2b1cbc 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationManager.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationManager.kt @@ -71,27 +71,32 @@ private constructor( * @return a [CompletableFuture] with the [TranslationResult]. */ fun analyze(): CompletableFuture { - val result = TranslationResult(this, ScopeManager()) - // We wrap the analysis in a CompletableFuture, i.e. in an async task. - return CompletableFuture.supplyAsync { analyze2(result) } + return CompletableFuture.supplyAsync { analyzeNonAsync() } } - fun analyze2(result: TranslationResult): TranslationResult { + private fun analyzeNonAsync(): TranslationResult { + var executedFrontends = setOf() + + // Build a new global translation context + val ctx = TranslationContext(config, ScopeManager(), TypeManager()) + + // Build a new translation result + val result = TranslationResult(this, ctx) + val outerBench = Benchmark(TranslationManager::class.java, "Translation into full graph", false, result) - var executedFrontends = setOf() try { // Parse Java/C/CPP files var bench = Benchmark(this.javaClass, "Executing Language Frontend", false, result) - executedFrontends = runFrontends(result, config) + executedFrontends = runFrontends(ctx, result) bench.addMeasurement() // Apply passes for (pass in config.registeredPasses) { bench = Benchmark(pass.java, "Executing Pass", false, result) - executePassSequential(pass, result, executedFrontends) + executePassSequential(pass, ctx, result, executedFrontends) bench.addMeasurement() if (result.isCancelled) { @@ -106,7 +111,7 @@ private constructor( log.debug("Cleaning up {} Frontends", executedFrontends.size) executedFrontends.forEach { it.cleanup() } - TypeManager.getInstance().cleanup() + ctx.typeManager.cleanup() } } @@ -122,25 +127,25 @@ private constructor( * of AST nodes. * * @param result the translation result that is being mutated - * @param config the translation configuration + * @param ctx the translation context * @throws TranslationException if the language front-end runs into an error and * [TranslationConfiguration.failOnError] * * is `true`. */ @Throws(TranslationException::class) private fun runFrontends( - result: TranslationResult, - config: TranslationConfiguration, + ctx: TranslationContext, + result: TranslationResult ): Set { val usedFrontends = mutableSetOf() - for (sc in this.config.softwareComponents.keys) { + for (sc in ctx.config.softwareComponents.keys) { val component = Component() component.name = Name(sc) result.addComponent(component) - var sourceLocations: List = this.config.softwareComponents[sc] ?: listOf() + var sourceLocations: List = ctx.config.softwareComponents[sc] ?: listOf() - var useParallelFrontends = config.useParallelFrontends + var useParallelFrontends = ctx.config.useParallelFrontends val list = sourceLocations.flatMap { file -> @@ -174,15 +179,15 @@ private constructor( listOf(file) } } - if (config.useUnityBuild) { + if (ctx.config.useUnityBuild) { val tmpFile = Files.createTempFile("compile", ".cpp").toFile() tmpFile.deleteOnExit() PrintWriter(tmpFile).use { writer -> list.forEach { if (CXXLanguageFrontend.CXX_EXTENSIONS.contains(Util.getExtension(it))) { - if (config.topLevel != null) { - val topLevel = config.topLevel.toPath() + if (ctx.config.topLevel != null) { + val topLevel = ctx.config.topLevel.toPath() writer.write( """ #include "${topLevel.relativize(it.toPath())}" @@ -204,24 +209,24 @@ private constructor( } sourceLocations = listOf(tmpFile) - if (config.compilationDatabase != null) { + if (ctx.config.compilationDatabase != null) { // merge include paths from all translation units - config.compilationDatabase.addIncludePath( + ctx.config.compilationDatabase.addIncludePath( tmpFile, - config.compilationDatabase.allIncludePaths + ctx.config.compilationDatabase.allIncludePaths ) } } else { sourceLocations = list } - TypeManager.setTypeSystemActive(config.typeSystemActiveInFrontend) + TypeManager.setTypeSystemActive(ctx.config.typeSystemActiveInFrontend) usedFrontends.addAll( if (useParallelFrontends) { - parseParallel(component, result, sourceLocations) + parseParallel(component, result, ctx, sourceLocations) } else { - parseSequentially(component, result, sourceLocations) + parseSequentially(component, result, ctx, sourceLocations) } ) @@ -232,7 +237,7 @@ private constructor( s.translationUnits.forEach { val bench = Benchmark(this.javaClass, "Activating types for ${it.name}", true) - result.scopeManager.activateTypes(it) + ctx.scopeManager.activateTypes(it, ctx.typeManager) bench.stop() } } @@ -245,25 +250,29 @@ private constructor( private fun parseParallel( component: Component, result: TranslationResult, + globalCtx: TranslationContext, sourceLocations: Collection ): Set { val usedFrontends = mutableSetOf() log.info("Parallel parsing started") val futures = mutableListOf>>() - val parallelScopeManagers = mutableListOf() + val parallelContexts = mutableListOf() val futureToFile: MutableMap>, File> = IdentityHashMap() for (sourceLocation in sourceLocations) { - val scopeManager = ScopeManager() - parallelScopeManagers.add(scopeManager) + // Build a new translation context for this parallel parsing process. We need to do this + // until we can use a single scope manager concurrently. We can re-use the global + // configuration and type manager. + val ctx = TranslationContext(globalCtx.config, ScopeManager(), globalCtx.typeManager) + parallelContexts.add(ctx) val future = CompletableFuture.supplyAsync { try { - return@supplyAsync parse(component, scopeManager, sourceLocation) + return@supplyAsync parse(component, ctx, sourceLocation) } catch (e: TranslationException) { throw RuntimeException("Error parsing $sourceLocation", e) } @@ -288,7 +297,7 @@ private constructor( } // We want to merge everything into the final scope manager of the result - result.scopeManager.mergeFrom(parallelScopeManagers) + globalCtx.scopeManager.mergeFrom(parallelContexts.map { it.scopeManager }) log.info("Parallel parsing completed") @@ -299,6 +308,7 @@ private constructor( private fun parseSequentially( component: Component, result: TranslationResult, + ctx: TranslationContext, sourceLocations: Collection ): Set { val usedFrontends = mutableSetOf() @@ -306,7 +316,7 @@ private constructor( for (sourceLocation in sourceLocations) { log.info("Parsing {}", sourceLocation.absolutePath) - parse(component, result.scopeManager, sourceLocation).ifPresent { f: LanguageFrontend -> + parse(component, ctx, sourceLocation).ifPresent { f: LanguageFrontend -> handleCompletion(result, usedFrontends, sourceLocation, f) } } @@ -333,12 +343,12 @@ private constructor( @Throws(TranslationException::class) private fun parse( component: Component, - scopeManager: ScopeManager, - sourceLocation: File + ctx: TranslationContext, + sourceLocation: File, ): Optional { var frontend: LanguageFrontend? = null try { - frontend = getFrontend(sourceLocation, scopeManager) + frontend = getFrontend(sourceLocation, ctx) if (frontend == null) { log.error("Found no parser frontend for ${sourceLocation.name}") @@ -360,19 +370,16 @@ private constructor( return Optional.ofNullable(frontend) } - private fun getFrontend( - file: File, - scopeManager: ScopeManager, - ): LanguageFrontend? { + private fun getFrontend(file: File, ctx: TranslationContext): LanguageFrontend? { val language = file.language return if (language != null) { try { // Make sure, that our simple types are also known to the type manager - language.builtInTypes.values.forEach { TypeManager.getInstance().registerType(it) } + language.builtInTypes.values.forEach { ctx.typeManager.registerType(it) } // Return a new language frontend - language.newFrontend(config, scopeManager) + language.newFrontend(ctx) } catch (e: Exception) { when (e) { is InstantiationException, diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationResult.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationResult.kt index 9fe035d606..c2e9f360df 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationResult.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationResult.kt @@ -29,25 +29,24 @@ import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration import de.fraunhofer.aisec.cpg.helpers.MeasurementHolder import de.fraunhofer.aisec.cpg.helpers.StatisticsHolder +import de.fraunhofer.aisec.cpg.passes.Pass import de.fraunhofer.aisec.cpg.passes.PassTarget import java.util.* import java.util.concurrent.ConcurrentHashMap -import java.util.function.Consumer -import java.util.stream.Collectors /** - * The global (intermediate) result of the translation. A [ ] will initially populate it and a [ ] - * can extend it. + * The global (intermediate) result of the translation. A [TranslationResult] will initially + * populate it and a [Pass] can extend it. */ class TranslationResult( - val translationManager: TranslationManager, + /** A reference to our [TranslationManager]. */ + private val translationManager: TranslationManager, /** - * The scope manager which comprises the complete translation result. In case of sequential - * parsing, this scope manager is passed to the individual frontends one after another. In case - * of sequential parsing, individual scope managers will be spawned by each language frontend - * and then finally merged into this one. + * The final [TranslationContext] of this translation result. Currently, for parallel + * processing, we are creating one translation context for each parsed file (containing a + * dedicated [ScopeManager] each). This property will contain the final, merged context. */ - val scopeManager: ScopeManager + var finalCtx: TranslationContext, ) : Node(), StatisticsHolder, PassTarget { /** @@ -150,22 +149,16 @@ class TranslationResult( override val translatedFiles: List get() { val result: MutableList = ArrayList() - components.forEach( - Consumer { sc: Component -> - result.addAll( - sc.translationUnits - .stream() - .map(TranslationUnitDeclaration::name) - .map { obj: Name -> obj.toString() } - .collect(Collectors.toList()) - ) - } - ) + components.forEach { sc: Component -> + result.addAll( + sc.translationUnits.map(TranslationUnitDeclaration::name).map(Name::toString) + ) + } return result } override val config: TranslationConfiguration - get() = translationManager.config + get() = finalCtx.config companion object { const val SOURCE_LOCATIONS_TO_FRONTEND = "sourceLocationsToFrontend" diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Handler.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Handler.kt index fefe5b5cd4..c5857a75cc 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Handler.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Handler.kt @@ -25,8 +25,8 @@ */ package de.fraunhofer.aisec.cpg.frontends +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.graph.* -import de.fraunhofer.aisec.cpg.graph.newCallExpression import de.fraunhofer.aisec.cpg.graph.scopes.Scope import de.fraunhofer.aisec.cpg.helpers.Util.errorWithFileLocation import java.lang.reflect.ParameterizedType @@ -51,7 +51,7 @@ abstract class Handler( protected val configConstructor: Supplier, /** Returns the frontend which used this handler. */ var frontend: L -) : LanguageProvider, CodeAndLocationProvider, ScopeProvider, NamespaceProvider { +) : LanguageProvider, CodeAndLocationProvider, ScopeProvider, NamespaceProvider, ContextProvider { protected val map = HashMap, HandlerInterface>() private val typeOfT: Class<*>? @@ -181,6 +181,9 @@ abstract class Handler( override val namespace: Name? get() = frontend.namespace + override val ctx: TranslationContext + get() = frontend.ctx + companion object { @JvmStatic protected val log: Logger = LoggerFactory.getLogger(Handler::class.java) } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt index d9a4ad0661..c332229432 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt @@ -30,8 +30,7 @@ import com.fasterxml.jackson.core.JsonGenerator import com.fasterxml.jackson.databind.SerializerProvider import com.fasterxml.jackson.databind.annotation.JsonSerialize import com.fasterxml.jackson.databind.ser.std.StdSerializer -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.newUnknownType import de.fraunhofer.aisec.cpg.graph.statements.expressions.BinaryOperator @@ -39,6 +38,7 @@ import de.fraunhofer.aisec.cpg.graph.types.* import de.fraunhofer.aisec.cpg.graph.types.Type import java.io.File import kotlin.reflect.KClass +import kotlin.reflect.full.primaryConstructor /** * Represents a programming language. When creating new languages in the CPG, one must derive custom @@ -78,11 +78,14 @@ abstract class Language : Node() { /** All operators which perform and assignment and an operation using lhs and rhs. */ abstract val compoundAssignmentOperators: Set - /** Creates a new [LanguageFrontend] object to parse the language. */ - abstract fun newFrontend( - config: TranslationConfiguration, - scopeManager: ScopeManager = ScopeManager(), - ): T + /** + * Creates a new [LanguageFrontend] object to parse the language. It requires the + * [TranslationContext], which holds the necessary managers. + */ + open fun newFrontend(ctx: TranslationContext): T { + return this.frontend.primaryConstructor?.call(this, ctx) + ?: throw TranslationException("could not instantiate language frontend") + } /** * Returns the type conforming to the given [typeString]. If no matching type is found in the diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageFrontend.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageFrontend.kt index 71910958d6..383462388f 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageFrontend.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageFrontend.kt @@ -27,6 +27,7 @@ package de.fraunhofer.aisec.cpg.frontends import de.fraunhofer.aisec.cpg.ScopeManager import de.fraunhofer.aisec.cpg.TranslationConfiguration +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.TranslationResult import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration @@ -47,23 +48,27 @@ import org.slf4j.LoggerFactory * [github wiki page](https://github.com/Fraunhofer-AISEC/cpg/wiki/Language-Frontends). */ abstract class LanguageFrontend( + /** The language this frontend works for. */ override val language: Language, - val config: TranslationConfiguration, - scopeManager: ScopeManager, + + /** + * The translation context, which contains all necessary managers used in this frontend parsing + * process. Note, that different contexts could passed to frontends, e.g., in parallel parsing + * to supply different managers to different frontends. + */ + final override var ctx: TranslationContext, ) : ProcessedListener(), CodeAndLocationProvider, LanguageProvider, ScopeProvider, - NamespaceProvider { - var scopeManager: ScopeManager = scopeManager - set(scopeManager) { - field = scopeManager - field.lang = this - } + NamespaceProvider, + ContextProvider { + val scopeManager: ScopeManager = ctx.scopeManager + val typeManager: TypeManager = ctx.typeManager + val config: TranslationConfiguration = ctx.config init { - // this.scopeManager = scopeManager this.scopeManager.lang = this } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageTraits.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageTraits.kt index c1d962c284..956dbb8a81 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageTraits.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageTraits.kt @@ -26,6 +26,7 @@ package de.fraunhofer.aisec.cpg.frontends import de.fraunhofer.aisec.cpg.ScopeManager +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.graph.Name import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration @@ -71,7 +72,7 @@ interface HasTemplates : HasGenerics { curClass: RecordDeclaration?, templateCall: CallExpression, applyInference: Boolean, - scopeManager: ScopeManager, + ctx: TranslationContext, currentTU: TranslationUnitDeclaration ): Pair> } @@ -97,7 +98,7 @@ interface HasComplexCallResolution : LanguageTrait { */ fun refineNormalCallResolution( call: CallExpression, - scopeManager: ScopeManager, + ctx: TranslationContext, currentTU: TranslationUnitDeclaration ): List @@ -113,7 +114,7 @@ interface HasComplexCallResolution : LanguageTrait { curClass: RecordDeclaration?, possibleContainingTypes: Set, call: CallExpression, - scopeManager: ScopeManager, + ctx: TranslationContext, currentTU: TranslationUnitDeclaration, callResolver: CallResolver ): List @@ -130,7 +131,8 @@ interface HasComplexCallResolution : LanguageTrait { fun refineInvocationCandidatesFromRecord( recordDeclaration: RecordDeclaration, call: CallExpression, - namePattern: Pattern + namePattern: Pattern, + ctx: TranslationContext ): List } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CLanguage.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CLanguage.kt index 83007ad5a7..244b0da599 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CLanguage.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CLanguage.kt @@ -26,8 +26,7 @@ package de.fraunhofer.aisec.cpg.frontends.cpp import com.fasterxml.jackson.annotation.JsonIgnore -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.* import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration @@ -113,22 +112,15 @@ open class CLanguage : IntegerType("unsigned long long int", 64, this, NumericType.Modifier.UNSIGNED) ) - override fun newFrontend( - config: TranslationConfiguration, - scopeManager: ScopeManager, - ): CXXLanguageFrontend { - return CXXLanguageFrontend(this, config, scopeManager) - } - override fun refineNormalCallResolution( call: CallExpression, - scopeManager: ScopeManager, + ctx: TranslationContext, currentTU: TranslationUnitDeclaration ): List { - val invocationCandidates = scopeManager.resolveFunction(call).toMutableList() + val invocationCandidates = ctx.scopeManager.resolveFunction(call).toMutableList() if (invocationCandidates.isEmpty()) { // Check for implicit casts - invocationCandidates.addAll(resolveWithImplicitCastFunc(call, scopeManager)) + invocationCandidates.addAll(resolveWithImplicitCastFunc(call, ctx)) } return invocationCandidates } @@ -137,7 +129,7 @@ open class CLanguage : curClass: RecordDeclaration?, possibleContainingTypes: Set, call: CallExpression, - scopeManager: ScopeManager, + ctx: TranslationContext, currentTU: TranslationUnitDeclaration, callResolver: CallResolver ): List = emptyList() @@ -145,7 +137,8 @@ open class CLanguage : override fun refineInvocationCandidatesFromRecord( recordDeclaration: RecordDeclaration, call: CallExpression, - namePattern: Pattern + namePattern: Pattern, + ctx: TranslationContext ): List = emptyList() /** @@ -155,10 +148,12 @@ open class CLanguage : */ protected fun resolveWithImplicitCastFunc( call: CallExpression, - scopeManager: ScopeManager + ctx: TranslationContext, ): List { val initialInvocationCandidates = - listOf(*scopeManager.resolveFunctionStopScopeTraversalOnDefinition(call).toTypedArray()) - return resolveWithImplicitCast(call, initialInvocationCandidates) + listOf( + *ctx.scopeManager.resolveFunctionStopScopeTraversalOnDefinition(call).toTypedArray() + ) + return resolveWithImplicitCast(call, initialInvocationCandidates, ctx) } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CPPLanguage.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CPPLanguage.kt index 09af4db425..b986a512fb 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CPPLanguage.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CPPLanguage.kt @@ -25,7 +25,7 @@ */ package de.fraunhofer.aisec.cpg.frontends.cpp -import de.fraunhofer.aisec.cpg.ScopeManager +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.* import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.declarations.* @@ -93,7 +93,7 @@ class CPPLanguage : curClass: RecordDeclaration?, possibleContainingTypes: Set, call: CallExpression, - scopeManager: ScopeManager, + ctx: TranslationContext, currentTU: TranslationUnitDeclaration, callResolver: CallResolver ): List { @@ -107,11 +107,11 @@ class CPPLanguage : } if (invocationCandidates.isEmpty()) { // Check for usage of default args - invocationCandidates.addAll(resolveWithDefaultArgsFunc(call, scopeManager)) + invocationCandidates.addAll(resolveWithDefaultArgsFunc(call, ctx)) } if (invocationCandidates.isEmpty()) { val (ok, candidates) = - handleTemplateFunctionCalls(curClass, call, false, scopeManager, currentTU) + handleTemplateFunctionCalls(curClass, call, false, ctx, currentTU) if (ok) { return candidates } @@ -120,7 +120,7 @@ class CPPLanguage : } if (invocationCandidates.isEmpty()) { // Check for usage of implicit cast - invocationCandidates.addAll(resolveWithImplicitCastFunc(call, scopeManager)) + invocationCandidates.addAll(resolveWithImplicitCastFunc(call, ctx)) } // Make sure, that our invocation candidates for member call expressions are really METHODS, @@ -136,7 +136,8 @@ class CPPLanguage : override fun refineInvocationCandidatesFromRecord( recordDeclaration: RecordDeclaration, call: CallExpression, - namePattern: Pattern + namePattern: Pattern, + ctx: TranslationContext ): List { val invocationCandidate = mutableListOf( @@ -165,7 +166,8 @@ class CPPLanguage : call, recordDeclaration.methods.filter { m -> namePattern.matcher(m.name).matches() /*&& !m.isImplicit()*/ - } + }, + ctx ) ) } @@ -174,21 +176,20 @@ class CPPLanguage : override fun refineNormalCallResolution( call: CallExpression, - scopeManager: ScopeManager, + ctx: TranslationContext, currentTU: TranslationUnitDeclaration ): List { - val invocationCandidates = scopeManager.resolveFunction(call).toMutableList() + val invocationCandidates = ctx.scopeManager.resolveFunction(call).toMutableList() if (invocationCandidates.isEmpty()) { // Check for usage of default args - invocationCandidates.addAll(resolveWithDefaultArgsFunc(call, scopeManager)) + invocationCandidates.addAll(resolveWithDefaultArgsFunc(call, ctx)) } if (invocationCandidates.isEmpty()) { // Check if the call can be resolved to a function template instantiation. If it can be // resolver, we resolve the call. Otherwise, there won't be an inferred template, we // will do an inferred FunctionDeclaration instead. call.templateParameterEdges = mutableListOf() - val (ok, candidates) = - handleTemplateFunctionCalls(null, call, false, scopeManager, currentTU) + val (ok, candidates) = handleTemplateFunctionCalls(null, call, false, ctx, currentTU) if (ok) { return candidates } @@ -198,7 +199,7 @@ class CPPLanguage : if (invocationCandidates.isEmpty()) { // If we don't find any candidate and our current language is c/c++ we check if there is // a candidate with an implicit cast - invocationCandidates.addAll(resolveWithImplicitCastFunc(call, scopeManager)) + invocationCandidates.addAll(resolveWithImplicitCastFunc(call, ctx)) } return invocationCandidates @@ -213,10 +214,10 @@ class CPPLanguage : */ private fun resolveWithDefaultArgsFunc( call: CallExpression, - scopeManager: ScopeManager + ctx: TranslationContext ): List { val invocationCandidates = - scopeManager.resolveFunctionStopScopeTraversalOnDefinition(call).filter { + ctx.scopeManager.resolveFunctionStopScopeTraversalOnDefinition(call).filter { call.signature.size < it.signatureTypes.size } return resolveWithDefaultArgs(call, invocationCandidates) @@ -238,10 +239,11 @@ class CPPLanguage : curClass: RecordDeclaration?, templateCall: CallExpression, applyInference: Boolean, - scopeManager: ScopeManager, + ctx: TranslationContext, currentTU: TranslationUnitDeclaration ): Pair> { - val instantiationCandidates = scopeManager.resolveFunctionTemplateDeclaration(templateCall) + val instantiationCandidates = + ctx.scopeManager.resolveFunctionTemplateDeclaration(templateCall) for (functionTemplateDeclaration in instantiationCandidates) { val initializationType = mutableMapOf() @@ -259,7 +261,8 @@ class CPPLanguage : templateCall, initializationType, orderedInitializationSignature, - explicitInstantiation + explicitInstantiation, + ctx ) val function = functionTemplateDeclaration.realization[0] if ( @@ -285,7 +288,8 @@ class CPPLanguage : function, initializationSignature, initializationType, - orderedInitializationSignature + orderedInitializationSignature, + ctx ) return Pair(true, candidates) } @@ -297,7 +301,7 @@ class CPPLanguage : // If we want to use an inferred functionTemplateDeclaration, this needs to be provided. // Otherwise, we could not resolve to a template and no modifications are made val functionTemplateDeclaration = - holder.startInference(scopeManager).createInferredFunctionTemplate(templateCall) + holder.startInference(ctx).createInferredFunctionTemplate(templateCall) templateCall.templateInstantiation = functionTemplateDeclaration val edges = templateCall.templateParameterEdges // Set instantiation propertyEdges diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXLanguageFrontend.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXLanguageFrontend.kt index c6d613849d..cf8fd8cc4d 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXLanguageFrontend.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXLanguageFrontend.kt @@ -26,8 +26,7 @@ package de.fraunhofer.aisec.cpg.frontends.cpp import de.fraunhofer.aisec.cpg.ResolveInFrontend -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend import de.fraunhofer.aisec.cpg.frontends.TranslationException @@ -76,11 +75,8 @@ import org.slf4j.LoggerFactory * ad [GPPLanguage]). This enables us (to some degree) to deal with the finer difference between C * and C++ code. */ -class CXXLanguageFrontend( - language: Language, - config: TranslationConfiguration, - scopeManager: ScopeManager, -) : LanguageFrontend(language, config, scopeManager) { +class CXXLanguageFrontend(language: Language, ctx: TranslationContext) : + LanguageFrontend(language, ctx) { /** * The dialect used by this language frontend, either [GCCLanguage] for C or [GPPLanguage] for @@ -556,7 +552,7 @@ class CXXLanguageFrontend( } } - type = TypeManager.getInstance().registerType(type) + type = typeManager.registerType(type) type = this.adjustType(declarator, type) return type @@ -637,7 +633,7 @@ class CXXLanguageFrontend( } // Make sure, the type manager knows about this type - return TypeManager.getInstance().registerType(type) + return typeManager.registerType(type) } companion object { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/DeclarationHandler.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/DeclarationHandler.kt index c7f5b82151..a314cdd2bf 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/DeclarationHandler.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/DeclarationHandler.kt @@ -312,12 +312,11 @@ class DeclarationHandler(lang: CXXLanguageFrontend) : val typeParamDeclaration = frontend.declaratorHandler.handle(templateParameter) as TypeParamDeclaration val parameterizedType = - TypeManager.getInstance() - .createOrGetTypeParameter( - templateDeclaration, - templateParameter.name.toString(), - language - ) + frontend.typeManager.createOrGetTypeParameter( + templateDeclaration, + templateParameter.name.toString(), + language + ) typeParamDeclaration.type = parameterizedType if (templateParameter.defaultType != null) { val defaultType = @@ -377,8 +376,7 @@ class DeclarationHandler(lang: CXXLanguageFrontend) : templateDeclaration: TemplateDeclaration, innerDeclaration: RecordDeclaration ) { - val parameterizedTypes = - TypeManager.getInstance().getAllParameterizedType(templateDeclaration) + val parameterizedTypes = frontend.typeManager.getAllParameterizedType(templateDeclaration) // Loop through all the methods and adjust their receiver types for (method in (innerDeclaration as? RecordDeclaration)?.methods ?: listOf()) { @@ -543,13 +541,12 @@ class DeclarationHandler(lang: CXXLanguageFrontend) : val (nameDecl: IASTDeclarator, _) = declarator.realName() val declaration = - TypeManager.getInstance() - .createTypeAlias( - frontend, - frontend.getCodeFromRawNode(ctx), - type, - nameDecl.name.toString() - ) + frontend.typeManager.createTypeAlias( + frontend, + frontend.getCodeFromRawNode(ctx), + type, + nameDecl.name.toString() + ) // Add the declaration to the current scope frontend.scopeManager.addDeclaration(declaration) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/ExpressionHandler.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/ExpressionHandler.kt index a16882a344..bc23a553c0 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/ExpressionHandler.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/ExpressionHandler.kt @@ -356,7 +356,7 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : ) { // this can either be just a meaningless bracket or it can be a cast expression val typeName = (ctx.operand as IASTIdExpression).name.toString() - if (TypeManager.getInstance().typeExists(typeName)) { + if (frontend.typeManager.typeExists(typeName)) { val cast = newCastExpression(frontend.getCodeFromRawNode(ctx)) cast.castType = parseType(typeName) cast.expression = input ?: newProblemExpression("could not parse input") diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/ExpressionBuilder.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/ExpressionBuilder.kt index ca734943b3..d072d2359a 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/ExpressionBuilder.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/ExpressionBuilder.kt @@ -588,6 +588,7 @@ fun MetadataProvider.newProblemExpression( fun Literal.duplicate(implicit: Boolean): Literal { val duplicate = Literal() + duplicate.ctx = this.ctx duplicate.language = this.language duplicate.value = this.value duplicate.type = this.type @@ -610,6 +611,7 @@ fun Literal.duplicate(implicit: Boolean): Literal { fun TypeExpression.duplicate(implicit: Boolean): TypeExpression { val duplicate = TypeExpression() + duplicate.ctx = this.ctx duplicate.name = this.name.clone() duplicate.language = this.language duplicate.type = this.type diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Node.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Node.kt index 05f4144bb7..788468fbb0 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Node.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Node.kt @@ -26,7 +26,9 @@ package de.fraunhofer.aisec.cpg.graph import com.fasterxml.jackson.annotation.JsonBackReference +import com.fasterxml.jackson.annotation.JsonIgnore import de.fraunhofer.aisec.cpg.PopulatedByPass +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.Handler import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend @@ -52,15 +54,21 @@ import de.fraunhofer.aisec.cpg.sarif.PhysicalLocation import java.util.* import org.apache.commons.lang3.builder.ToStringBuilder import org.apache.commons.lang3.builder.ToStringStyle -import org.neo4j.ogm.annotation.GeneratedValue -import org.neo4j.ogm.annotation.Id -import org.neo4j.ogm.annotation.Relationship +import org.neo4j.ogm.annotation.* import org.neo4j.ogm.annotation.typeconversion.Convert import org.slf4j.Logger import org.slf4j.LoggerFactory /** The base class for all graph objects that are going to be persisted in the database. */ -open class Node : IVisitable, Persistable, LanguageProvider, ScopeProvider { +open class Node : IVisitable, Persistable, LanguageProvider, ScopeProvider, ContextProvider { + /** + * Because we are updating type information in the properties of the node, we need a reference + * to managers such as the [TypeManager] instance which is responsible for this particular node. + * All managers are bundled in [TranslationContext]. It is set in [Node.applyMetadata] when a + * [ContextProvider] is provided. + */ + @get:JsonIgnore @Transient override var ctx: TranslationContext? = null + /** * This property holds the full name using our new [Name] class. It is currently not persisted * in the graph database. @@ -112,7 +120,7 @@ open class Node : IVisitable, Persistable, LanguageProvider, ScopeProvider var prevEOGEdges: MutableList> = ArrayList() protected set - /** outgoing control flow edges. */ + /** Outgoing control flow edges. */ @PopulatedByPass(EvaluationOrderGraphPass::class) @Relationship(value = "EOG", direction = Relationship.Direction.OUTGOING) var nextEOGEdges: MutableList> = ArrayList() diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/NodeBuilder.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/NodeBuilder.kt index 6e25d2c6c7..5d947622fa 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/NodeBuilder.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/NodeBuilder.kt @@ -25,11 +25,13 @@ */ package de.fraunhofer.aisec.cpg.graph +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.* import de.fraunhofer.aisec.cpg.graph.Node.Companion.EMPTY_NAME 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.Type import de.fraunhofer.aisec.cpg.graph.types.TypeParser import de.fraunhofer.aisec.cpg.graph.types.UnknownType import de.fraunhofer.aisec.cpg.passes.inference.IsInferredProvider @@ -38,7 +40,7 @@ import org.slf4j.LoggerFactory object NodeBuilder { internal val LOGGER = LoggerFactory.getLogger(NodeBuilder::class.java) - fun log(node: Node?) { + fun log(node: Node) { LOGGER.trace("Creating {}", node) } } @@ -117,6 +119,16 @@ fun Node.applyMetadata( this.scope = provider.scope } + if (provider is ContextProvider) { + this.ctx = provider.ctx + } + + if (this.ctx == null) { + throw TranslationException( + "Trying to create a node without a ContextProvider. This will fail." + ) + } + if (name != null) { val namespace = if (provider is NamespaceProvider) { @@ -224,10 +236,19 @@ fun MetadataProvider?.newUnknownType(): UnknownType { * since we are moving away from the [TypeParser] altogether. */ @JvmOverloads -fun LanguageProvider.parseType(name: CharSequence, resolveAlias: Boolean = false) = - TypeParser.createFrom(name, resolveAlias, language) +fun LanguageProvider.parseType(name: CharSequence, resolveAlias: Boolean = false): Type { + return if (this is ContextProvider) { + TypeParser.createFrom(name.toString(), language, resolveAlias, this.ctx) + } else { + throw TranslationException("Cannot parse type without translation context") + } +} /** Returns a new [Name] based on the [localName] and the current namespace as parent. */ fun NamespaceProvider.fqn(localName: String): Name { return this.namespace.fqn(localName) } + +interface ContextProvider : MetadataProvider { + val ctx: TranslationContext? +} diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/builder/Fluent.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/builder/Fluent.kt index 33a430b198..1ac97cad94 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/builder/Fluent.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/builder/Fluent.kt @@ -25,10 +25,7 @@ */ package de.fraunhofer.aisec.cpg.graph.builder -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration -import de.fraunhofer.aisec.cpg.TranslationManager -import de.fraunhofer.aisec.cpg.TranslationResult +import de.fraunhofer.aisec.cpg.* import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.* @@ -38,16 +35,17 @@ import de.fraunhofer.aisec.cpg.graph.statements.expressions.* import de.fraunhofer.aisec.cpg.graph.types.Type import de.fraunhofer.aisec.cpg.passes.executePassSequential -fun LanguageFrontend.translationResult( - config: TranslationConfiguration, - init: TranslationResult.() -> Unit -): TranslationResult { - val node = TranslationResult(TranslationManager.builder().config(config).build(), scopeManager) +fun LanguageFrontend.translationResult(init: TranslationResult.() -> Unit): TranslationResult { + val node = + TranslationResult( + TranslationManager.builder().config(ctx.config).build(), + ctx, + ) val component = Component() node.addComponent(component) init(node) - config.registeredPasses.forEach { executePassSequential(it, node, listOf()) } + ctx.config.registeredPasses.forEach { executePassSequential(it, ctx, node, listOf()) } return node } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/ConstructorDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/ConstructorDeclaration.kt index 138c3df43e..98ce6fe469 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/ConstructorDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/ConstructorDeclaration.kt @@ -25,7 +25,7 @@ */ package de.fraunhofer.aisec.cpg.graph.declarations -import de.fraunhofer.aisec.cpg.graph.types.TypeParser +import de.fraunhofer.aisec.cpg.graph.parseType /** * The declaration of a constructor within a [RecordDeclaration]. Is it essentially a special case @@ -39,7 +39,7 @@ class ConstructorDeclaration : MethodDeclaration() { super.recordDeclaration = recordDeclaration if (recordDeclaration != null) { // constructors always have implicitly the return type of their class - returnTypes = listOf(TypeParser.createFrom(recordDeclaration.name, language)) + returnTypes = listOf(parseType(recordDeclaration.name)) } } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/FieldDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/FieldDeclaration.kt index f657ed9ec8..b2f3c985f2 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/FieldDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/FieldDeclaration.kt @@ -25,13 +25,11 @@ */ package de.fraunhofer.aisec.cpg.graph.declarations -import de.fraunhofer.aisec.cpg.graph.AST -import de.fraunhofer.aisec.cpg.graph.HasInitializer -import de.fraunhofer.aisec.cpg.graph.HasType -import de.fraunhofer.aisec.cpg.graph.TypeManager +import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression import de.fraunhofer.aisec.cpg.graph.statements.expressions.InitializerListExpression import de.fraunhofer.aisec.cpg.graph.types.Type +import de.fraunhofer.aisec.cpg.graph.types.UnknownType import java.util.* import org.apache.commons.lang3.builder.ToStringBuilder import org.neo4j.ogm.annotation.Relationship @@ -81,10 +79,10 @@ class FieldDeclaration : ValueDeclaration(), HasType.TypeListener, HasInitialize var modifiers: List = mutableListOf() override fun typeChanged(src: HasType, root: MutableList, oldType: Type) { - if (!TypeManager.isTypeSystemActive()) { + if (!isTypeSystemActive) { return } - if (!TypeManager.getInstance().isUnknown(type) && src.propagationType == oldType) { + if (type !is UnknownType && src.propagationType == oldType) { return } val previous = type @@ -98,7 +96,7 @@ class FieldDeclaration : ValueDeclaration(), HasType.TypeListener, HasInitialize // can be ignored once we have a type if (isArray) { src.type - } else if (!TypeManager.getInstance().isUnknown(type)) { + } else if (type !is UnknownType) { return } else { src.type.dereference() @@ -113,7 +111,7 @@ class FieldDeclaration : ValueDeclaration(), HasType.TypeListener, HasInitialize } override fun possibleSubTypesChanged(src: HasType, root: MutableList) { - if (!TypeManager.isTypeSystemActive()) { + if (!isTypeSystemActive) { return } val subTypes: MutableList = ArrayList(possibleSubTypes) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/FunctionDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/FunctionDeclaration.kt index 12ec422885..fa5ab715b2 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/FunctionDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/FunctionDeclaration.kt @@ -25,14 +25,11 @@ */ package de.fraunhofer.aisec.cpg.graph.declarations -import de.fraunhofer.aisec.cpg.graph.AST -import de.fraunhofer.aisec.cpg.graph.DeclarationHolder -import de.fraunhofer.aisec.cpg.graph.TypeManager +import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.edge.Properties import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.propertyEqualsList import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeDelegate -import de.fraunhofer.aisec.cpg.graph.newUnknownType import de.fraunhofer.aisec.cpg.graph.statements.CompoundStatement import de.fraunhofer.aisec.cpg.graph.statements.Statement import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression @@ -133,7 +130,7 @@ open class FunctionDeclaration : ValueDeclaration(), DeclarationHolder { return true } val provided = targetSignature[i] - if (!TypeManager.getInstance().isSupertypeOf(declared.type, provided, this)) { + if (!isSupertypeOf(declared.type, provided)) { return false } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/RecordDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/RecordDeclaration.kt index 0b997e3357..839b75cd91 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/RecordDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/RecordDeclaration.kt @@ -31,10 +31,10 @@ import de.fraunhofer.aisec.cpg.graph.StatementHolder import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.propertyEqualsList import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeDelegate +import de.fraunhofer.aisec.cpg.graph.parseType import de.fraunhofer.aisec.cpg.graph.statements.Statement import de.fraunhofer.aisec.cpg.graph.types.ObjectType import de.fraunhofer.aisec.cpg.graph.types.Type -import de.fraunhofer.aisec.cpg.graph.types.TypeParser import java.util.stream.Collectors import java.util.stream.Stream import org.apache.commons.lang3.builder.ToStringBuilder @@ -216,7 +216,7 @@ class RecordDeclaration : Declaration(), DeclarationHolder, StatementHolder { * @return the type */ fun toType(): Type { - val type = TypeParser.createFrom(name, language) + val type = parseType(name) if (type is ObjectType) { // as a shortcut, directly set the record declaration. This will be otherwise done // later by a pass, but for some frontends we need this immediately, so we set diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/ValueDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/ValueDeclaration.kt index f48bc6d454..ab7b1106f6 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/ValueDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/ValueDeclaration.kt @@ -26,12 +26,10 @@ package de.fraunhofer.aisec.cpg.graph.declarations import de.fraunhofer.aisec.cpg.PopulatedByPass -import de.fraunhofer.aisec.cpg.graph.HasType -import de.fraunhofer.aisec.cpg.graph.TypeManager +import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.edge.Properties import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.unwrap -import de.fraunhofer.aisec.cpg.graph.newUnknownType import de.fraunhofer.aisec.cpg.graph.statements.expressions.DeclaredReferenceExpression import de.fraunhofer.aisec.cpg.graph.types.FunctionPointerType import de.fraunhofer.aisec.cpg.graph.types.ReferenceType @@ -59,13 +57,13 @@ abstract class ValueDeclaration : Declaration(), HasType { override var type: Type get() { val result: Type = - if (TypeManager.isTypeSystemActive()) { + if (isTypeSystemActive) { _type } else { - TypeManager.getInstance() - .typeCache - .computeIfAbsent(this) { mutableListOf() } - .firstOrNull() + ctx?.typeManager + ?.typeCache + ?.computeIfAbsent(this) { mutableListOf() } + ?.firstOrNull() ?: newUnknownType() } return result @@ -78,8 +76,8 @@ abstract class ValueDeclaration : Declaration(), HasType { @Relationship("POSSIBLE_SUB_TYPES") protected var _possibleSubTypes = mutableListOf() override var possibleSubTypes: List get() { - return if (!TypeManager.isTypeSystemActive()) { - TypeManager.getInstance().typeCache.getOrDefault(this, emptyList()) + return if (!isTypeSystemActive) { + ctx?.typeManager?.typeCache?.getOrDefault(this, emptyList()) ?: listOf() } else _possibleSubTypes } set(value) { @@ -134,39 +132,36 @@ abstract class ValueDeclaration : Declaration(), HasType { } override fun setType(type: Type, root: MutableList?) { - var t: Type? = type + var t: Type = type var r: MutableList? = root - if (!TypeManager.isTypeSystemActive()) { - TypeManager.getInstance().cacheType(this, t) + if (!isTypeSystemActive) { + cacheType(t) return } if (r == null) { r = ArrayList() } if ( - t == null || - r.contains(this) || - TypeManager.getInstance().isUnknown(t) || + r.contains(this) || + t is UnknownType || this._type is FunctionPointerType && t !is FunctionPointerType ) { return } val oldType = this.type t = t.duplicate() - val subTypes: MutableSet = HashSet() + val subTypes = mutableSetOf() for (t in possibleSubTypes) { if (!t.isSimilar(t)) { subTypes.add(t) } } subTypes.add(t) - this._type = - TypeManager.getInstance() - .registerType(TypeManager.getInstance().getCommonType(subTypes, this).orElse(t)) + this._type = registerType(getCommonType(subTypes).orElse(t)) val newSubtypes: MutableList = ArrayList() for (s in subTypes) { - if (TypeManager.getInstance().isSupertypeOf(this.type, s, this)) { - newSubtypes.add(TypeManager.getInstance().registerType(s)) + if (isSupertypeOf(this.type, s)) { + newSubtypes.add(registerType(s)) } } possibleSubTypes = newSubtypes @@ -185,13 +180,9 @@ abstract class ValueDeclaration : Declaration(), HasType { override fun setPossibleSubTypes(possibleSubTypes: List, root: MutableList) { var list = possibleSubTypes - list = - list - .filterNot { type -> TypeManager.getInstance().isUnknown(type) } - .distinct() - .toMutableList() - if (!TypeManager.isTypeSystemActive()) { - list.forEach { t -> TypeManager.getInstance().cacheType(this, t) } + list = list.filterNot { type -> type is UnknownType }.distinct().toMutableList() + if (!isTypeSystemActive) { + list.forEach { t -> cacheType(t) } return } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/VariableDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/VariableDeclaration.kt index 94b8476f18..3ca71622bb 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/VariableDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/VariableDeclaration.kt @@ -29,6 +29,7 @@ import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression import de.fraunhofer.aisec.cpg.graph.statements.expressions.InitializerListExpression import de.fraunhofer.aisec.cpg.graph.types.Type +import de.fraunhofer.aisec.cpg.graph.types.UnknownType import org.apache.commons.lang3.builder.ToStringBuilder import org.neo4j.ogm.annotation.Relationship @@ -81,10 +82,10 @@ class VariableDeclaration : ValueDeclaration(), HasType.TypeListener, HasInitial } override fun typeChanged(src: HasType, root: MutableList, oldType: Type) { - if (!TypeManager.isTypeSystemActive()) { + if (!isTypeSystemActive) { return } - if (!TypeManager.getInstance().isUnknown(type) && src.propagationType == oldType) { + if (type !is UnknownType && src.propagationType == oldType) { return } val previous = type @@ -97,7 +98,7 @@ class VariableDeclaration : ValueDeclaration(), HasType.TypeListener, HasInitial // otherwise it can be ignored once we have a type if (isArray) { src.type - } else if (!TypeManager.getInstance().isUnknown(type)) { + } else if (type !is UnknownType) { return } else { src.type.dereference() @@ -112,7 +113,7 @@ class VariableDeclaration : ValueDeclaration(), HasType.TypeListener, HasInitial } override fun possibleSubTypesChanged(src: HasType, root: MutableList) { - if (!TypeManager.isTypeSystemActive()) { + if (!isTypeSystemActive) { return } val subTypes: MutableList = ArrayList(possibleSubTypes) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ArrayCreationExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ArrayCreationExpression.kt index 395db86cdc..258a9e9251 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ArrayCreationExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ArrayCreationExpression.kt @@ -27,11 +27,11 @@ package de.fraunhofer.aisec.cpg.graph.statements.expressions import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.HasType -import de.fraunhofer.aisec.cpg.graph.TypeManager import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.propertyEqualsList import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeDelegate +import de.fraunhofer.aisec.cpg.graph.isTypeSystemActive import de.fraunhofer.aisec.cpg.graph.types.Type import java.util.* import org.neo4j.ogm.annotation.Relationship @@ -82,7 +82,7 @@ class ArrayCreationExpression : Expression(), HasType.TypeListener { override fun hashCode() = Objects.hash(super.hashCode(), initializer, dimensions) override fun typeChanged(src: HasType, root: MutableList, oldType: Type) { - if (!TypeManager.isTypeSystemActive()) { + if (!isTypeSystemActive) { return } val previous = type @@ -93,7 +93,7 @@ class ArrayCreationExpression : Expression(), HasType.TypeListener { } override fun possibleSubTypesChanged(src: HasType, root: MutableList) { - if (!TypeManager.isTypeSystemActive()) { + if (!isTypeSystemActive) { return } val subTypes: MutableList = ArrayList(possibleSubTypes) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ArraySubscriptionExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ArraySubscriptionExpression.kt index 0485da095c..7cc3d05d6c 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ArraySubscriptionExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ArraySubscriptionExpression.kt @@ -76,7 +76,7 @@ class ArraySubscriptionExpression : Expression(), HasType.TypeListener, HasBase } override fun typeChanged(src: HasType, root: MutableList, oldType: Type) { - if (!TypeManager.isTypeSystemActive()) { + if (!isTypeSystemActive) { return } val previous = type @@ -87,7 +87,7 @@ class ArraySubscriptionExpression : Expression(), HasType.TypeListener, HasBase } override fun possibleSubTypesChanged(src: HasType, root: MutableList) { - if (!TypeManager.isTypeSystemActive()) { + if (!isTypeSystemActive) { return } val subTypes: MutableList = ArrayList(possibleSubTypes) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/AssignExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/AssignExpression.kt index 18a1867162..e240f0e67e 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/AssignExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/AssignExpression.kt @@ -113,7 +113,7 @@ class AssignExpression : Expression(), AssignmentHolder, HasType.TypeListener { override var declarations = mutableListOf() override fun typeChanged(src: HasType, root: MutableList, oldType: Type) { - if (!TypeManager.isTypeSystemActive()) { + if (!isTypeSystemActive) { return } @@ -138,7 +138,7 @@ class AssignExpression : Expression(), AssignmentHolder, HasType.TypeListener { } override fun possibleSubTypesChanged(src: HasType, root: MutableList) { - if (!TypeManager.isTypeSystemActive()) { + if (!isTypeSystemActive) { return } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/BinaryOperator.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/BinaryOperator.kt index cf6e13ca5f..c3185f0954 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/BinaryOperator.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/BinaryOperator.kt @@ -125,7 +125,7 @@ class BinaryOperator : } override fun typeChanged(src: HasType, root: MutableList, oldType: Type) { - if (!TypeManager.isTypeSystemActive()) { + if (!isTypeSystemActive) { return } val previous = type @@ -164,7 +164,7 @@ class BinaryOperator : } override fun possibleSubTypesChanged(src: HasType, root: MutableList) { - if (!TypeManager.isTypeSystemActive()) { + if (!isTypeSystemActive) { return } val subTypes: MutableList = ArrayList(possibleSubTypes) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.kt index d31602de2d..4a434eb14d 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.kt @@ -267,7 +267,7 @@ open class CallExpression : Expression(), HasType.TypeListener, SecondaryTypeEdg } override fun typeChanged(src: HasType, root: MutableList, oldType: Type) { - if (!TypeManager.isTypeSystemActive()) { + if (!isTypeSystemActive) { return } @@ -288,7 +288,7 @@ open class CallExpression : Expression(), HasType.TypeListener, SecondaryTypeEdg null } val alternative = if (types.isNotEmpty()) types[0] else newUnknownType() - val commonType = TypeManager.getInstance().getCommonType(types, this).orElse(alternative) + val commonType = getCommonType(types).orElse(alternative) val subTypes: MutableList = ArrayList(possibleSubTypes) subTypes.remove(oldType) @@ -301,7 +301,7 @@ open class CallExpression : Expression(), HasType.TypeListener, SecondaryTypeEdg } override fun possibleSubTypesChanged(src: HasType, root: MutableList) { - if (!TypeManager.isTypeSystemActive()) { + if (!isTypeSystemActive) { return } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CastExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CastExpression.kt index 13dc0014c1..30024d3bd3 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CastExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CastExpression.kt @@ -46,11 +46,11 @@ class CastExpression : Expression(), HasType.TypeListener { } override fun typeChanged(src: HasType, root: MutableList, oldType: Type) { - if (!TypeManager.isTypeSystemActive()) { + if (!isTypeSystemActive) { return } val previous = type - if (TypeManager.getInstance().isSupertypeOf(castType, src.propagationType, this)) { + if (isSupertypeOf(castType, src.propagationType)) { setType(src.propagationType, root) } else { resetTypes(castType) @@ -61,7 +61,7 @@ class CastExpression : Expression(), HasType.TypeListener { } override fun possibleSubTypesChanged(src: HasType, root: MutableList) { - if (!TypeManager.isTypeSystemActive()) { + if (!isTypeSystemActive) { return } setPossibleSubTypes(ArrayList(src.possibleSubTypes), root) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConditionalExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConditionalExpression.kt index 2ff4a459c7..e53e5539aa 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConditionalExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConditionalExpression.kt @@ -55,7 +55,7 @@ class ConditionalExpression : Expression(), HasType.TypeListener, ArgumentHolder } override fun typeChanged(src: HasType, root: MutableList, oldType: Type) { - if (!TypeManager.isTypeSystemActive()) { + if (!isTypeSystemActive) { return } val previous = type @@ -68,7 +68,7 @@ class ConditionalExpression : Expression(), HasType.TypeListener, ArgumentHolder subTypes.remove(oldType) subTypes.addAll(types) val alternative = if (types.isNotEmpty()) types[0] else newUnknownType() - setType(TypeManager.getInstance().getCommonType(types, this).orElse(alternative), root) + setType(getCommonType(types).orElse(alternative), root) setPossibleSubTypes(subTypes, root) if (previous != type) { type.typeOrigin = Type.Origin.DATAFLOW @@ -76,7 +76,7 @@ class ConditionalExpression : Expression(), HasType.TypeListener, ArgumentHolder } override fun possibleSubTypesChanged(src: HasType, root: MutableList) { - if (!TypeManager.isTypeSystemActive()) { + if (!isTypeSystemActive) { return } val subTypes: MutableList = ArrayList(possibleSubTypes) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConstructExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConstructExpression.kt index ef53ed66a9..421b0730da 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConstructExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConstructExpression.kt @@ -28,11 +28,11 @@ package de.fraunhofer.aisec.cpg.graph.statements.expressions import de.fraunhofer.aisec.cpg.PopulatedByPass import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.HasType -import de.fraunhofer.aisec.cpg.graph.TypeManager import de.fraunhofer.aisec.cpg.graph.declarations.* +import de.fraunhofer.aisec.cpg.graph.isTypeSystemActive +import de.fraunhofer.aisec.cpg.graph.parseType import de.fraunhofer.aisec.cpg.graph.types.FunctionType import de.fraunhofer.aisec.cpg.graph.types.Type -import de.fraunhofer.aisec.cpg.graph.types.TypeParser import de.fraunhofer.aisec.cpg.graph.types.UnknownType import de.fraunhofer.aisec.cpg.passes.CallResolver import java.util.* @@ -80,7 +80,7 @@ class ConstructExpression : CallExpression(), HasType.TypeListener { set(value) { field = value if (value != null && this.type is UnknownType) { - type = TypeParser.createFrom(value.name, language) + type = parseType(value.name) } } @@ -107,7 +107,7 @@ class ConstructExpression : CallExpression(), HasType.TypeListener { * work around the first case in a different way. */ override fun typeChanged(src: HasType, root: MutableList, oldType: Type) { - if (!TypeManager.isTypeSystemActive()) { + if (!isTypeSystemActive) { return } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/DeclaredReferenceExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/DeclaredReferenceExpression.kt index 3c9b839fc8..10c72885bd 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/DeclaredReferenceExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/DeclaredReferenceExpression.kt @@ -28,10 +28,10 @@ package de.fraunhofer.aisec.cpg.graph.statements.expressions import de.fraunhofer.aisec.cpg.PopulatedByPass import de.fraunhofer.aisec.cpg.graph.AccessValues import de.fraunhofer.aisec.cpg.graph.HasType -import de.fraunhofer.aisec.cpg.graph.TypeManager import de.fraunhofer.aisec.cpg.graph.declarations.Declaration import de.fraunhofer.aisec.cpg.graph.declarations.ValueDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration +import de.fraunhofer.aisec.cpg.graph.isTypeSystemActive import de.fraunhofer.aisec.cpg.graph.types.Type import de.fraunhofer.aisec.cpg.passes.VariableUsageResolver import java.util.* @@ -100,7 +100,7 @@ open class DeclaredReferenceExpression : Expression(), HasType.TypeListener { } override fun typeChanged(src: HasType, root: MutableList, oldType: Type) { - if (!TypeManager.isTypeSystemActive()) { + if (!isTypeSystemActive) { return } val previous = type @@ -111,7 +111,7 @@ open class DeclaredReferenceExpression : Expression(), HasType.TypeListener { } override fun possibleSubTypesChanged(src: HasType, root: MutableList) { - if (!TypeManager.isTypeSystemActive()) { + if (!isTypeSystemActive) { return } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/Expression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/Expression.kt index 5ed5e3ecb1..28f7c18f49 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/Expression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/Expression.kt @@ -30,6 +30,7 @@ import de.fraunhofer.aisec.cpg.graph.statements.Statement import de.fraunhofer.aisec.cpg.graph.types.FunctionPointerType import de.fraunhofer.aisec.cpg.graph.types.ReferenceType import de.fraunhofer.aisec.cpg.graph.types.Type +import de.fraunhofer.aisec.cpg.graph.types.UnknownType import java.util.* import org.apache.commons.lang3.builder.ToStringBuilder import org.neo4j.ogm.annotation.Relationship @@ -54,13 +55,13 @@ abstract class Expression : Statement(), HasType { override var type: Type get() { val result: Type = - if (TypeManager.isTypeSystemActive()) { + if (isTypeSystemActive) { _type } else { - TypeManager.getInstance() - .typeCache - .computeIfAbsent(this) { mutableListOf() } - .firstOrNull() + ctx?.typeManager + ?.typeCache + ?.computeIfAbsent(this) { mutableListOf() } + ?.firstOrNull() ?: newUnknownType() } return result @@ -74,8 +75,8 @@ abstract class Expression : Statement(), HasType { override var possibleSubTypes: List get() { - return if (!TypeManager.isTypeSystemActive()) { - TypeManager.getInstance().typeCache.getOrDefault(this, emptyList()) + return if (!isTypeSystemActive) { + ctx?.typeManager?.typeCache?.getOrDefault(this, emptyList()) ?: listOf() } else _possibleSubTypes } set(value) { @@ -98,9 +99,9 @@ abstract class Expression : Statement(), HasType { // TODO Document this method. It is called very often (potentially for each AST node) and // performs less than optimal. - if (!TypeManager.isTypeSystemActive()) { + if (!isTypeSystemActive) { this._type = t - TypeManager.getInstance().cacheType(this, t) + cacheType(t) return } @@ -112,8 +113,8 @@ abstract class Expression : Statement(), HasType { // do. if ( r.contains(this) || - TypeManager.getInstance().isUnknown(t) || - TypeManager.getInstance().stopPropagation(this.type, t) || + t is UnknownType || + stopPropagation(this.type, t) || (this.type is FunctionPointerType && t !is FunctionPointerType) ) { return @@ -135,16 +136,14 @@ abstract class Expression : Statement(), HasType { subTypes.add(t) // Probably tries to get something like the best supertype of all possible subtypes. - this._type = - TypeManager.getInstance() - .registerType(TypeManager.getInstance().getCommonType(subTypes, this).orElse(t)) + this._type = registerType(getCommonType(subTypes).orElse(t)) // TODO: Why do we need this loop? Shouldn't the condition be ensured by the previous line // getting the common type?? val newSubtypes = mutableListOf() for (s in subTypes) { - if (TypeManager.getInstance().isSupertypeOf(this.type, s, this)) { - newSubtypes.add(TypeManager.getInstance().registerType(s)) + if (isSupertypeOf(this.type, s)) { + newSubtypes.add(registerType(s)) } } @@ -168,13 +167,9 @@ abstract class Expression : Statement(), HasType { override fun setPossibleSubTypes(possibleSubTypes: List, root: MutableList) { var list = possibleSubTypes - list = - list - .filterNot { type -> TypeManager.getInstance().isUnknown(type) } - .distinct() - .toMutableList() - if (!TypeManager.isTypeSystemActive()) { - list.forEach { t -> TypeManager.getInstance().cacheType(this, t) } + list = list.filterNot { type -> type is UnknownType }.distinct().toMutableList() + if (!isTypeSystemActive) { + list.forEach { t -> cacheType(t) } return } if (root.contains(this)) { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ExpressionList.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ExpressionList.kt index a943e759b0..a82869e10d 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ExpressionList.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ExpressionList.kt @@ -27,12 +27,12 @@ package de.fraunhofer.aisec.cpg.graph.statements.expressions import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.HasType -import de.fraunhofer.aisec.cpg.graph.TypeManager import de.fraunhofer.aisec.cpg.graph.edge.Properties import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.propertyEqualsList import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.transformIntoOutgoingPropertyEdgeList import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.unwrap +import de.fraunhofer.aisec.cpg.graph.isTypeSystemActive import de.fraunhofer.aisec.cpg.graph.statements.Statement import de.fraunhofer.aisec.cpg.graph.types.Type import java.util.* @@ -76,7 +76,7 @@ class ExpressionList : Expression(), HasType.TypeListener { } override fun typeChanged(src: HasType, root: MutableList, oldType: Type) { - if (!TypeManager.isTypeSystemActive()) { + if (!isTypeSystemActive) { return } val previous = type @@ -88,7 +88,7 @@ class ExpressionList : Expression(), HasType.TypeListener { } override fun possibleSubTypesChanged(src: HasType, root: MutableList) { - if (!TypeManager.isTypeSystemActive()) { + if (!isTypeSystemActive) { return } setPossibleSubTypes(ArrayList(src.possibleSubTypes), root) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/InitializerListExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/InitializerListExpression.kt index 224cdb92c1..c7c02ddd28 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/InitializerListExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/InitializerListExpression.kt @@ -25,15 +25,13 @@ */ package de.fraunhofer.aisec.cpg.graph.statements.expressions -import de.fraunhofer.aisec.cpg.graph.AST -import de.fraunhofer.aisec.cpg.graph.HasType -import de.fraunhofer.aisec.cpg.graph.TypeManager +import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.propertyEqualsList import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeDelegate -import de.fraunhofer.aisec.cpg.graph.newUnknownType import de.fraunhofer.aisec.cpg.graph.types.PointerType.PointerOrigin import de.fraunhofer.aisec.cpg.graph.types.Type +import de.fraunhofer.aisec.cpg.graph.types.UnknownType import java.util.* import org.apache.commons.lang3.builder.ToStringBuilder import org.neo4j.ogm.annotation.Relationship @@ -60,10 +58,10 @@ class InitializerListExpression : Expression(), HasType.TypeListener { var initializers by PropertyEdgeDelegate(InitializerListExpression::initializerEdges) override fun typeChanged(src: HasType, root: MutableList, oldType: Type) { - if (!TypeManager.isTypeSystemActive()) { + if (!isTypeSystemActive) { return } - if (!TypeManager.getInstance().isUnknown(type) && src.propagationType == oldType) { + if (type !is UnknownType && src.propagationType == oldType) { return } val previous = type @@ -71,14 +69,9 @@ class InitializerListExpression : Expression(), HasType.TypeListener { val subTypes: MutableList if (initializers.contains(src)) { val types = - initializers - .map { - TypeManager.getInstance() - .registerType(it.type.reference(PointerOrigin.ARRAY)) - } - .toSet() + initializers.map { registerType(it.type.reference(PointerOrigin.ARRAY)) }.toSet() val alternative = if (types.isNotEmpty()) types.iterator().next() else newUnknownType() - newType = TypeManager.getInstance().getCommonType(types, this).orElse(alternative) + newType = getCommonType(types).orElse(alternative) subTypes = ArrayList(possibleSubTypes) subTypes.remove(oldType) subTypes.addAll(types) @@ -96,7 +89,7 @@ class InitializerListExpression : Expression(), HasType.TypeListener { } override fun possibleSubTypesChanged(src: HasType, root: MutableList) { - if (!TypeManager.isTypeSystemActive()) { + if (!isTypeSystemActive) { return } val subTypes: MutableList = ArrayList(possibleSubTypes) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/LambdaExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/LambdaExpression.kt index c866e25b2b..08a1fd0bbd 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/LambdaExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/LambdaExpression.kt @@ -27,11 +27,12 @@ package de.fraunhofer.aisec.cpg.graph.statements.expressions import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.HasType -import de.fraunhofer.aisec.cpg.graph.TypeManager import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.ValueDeclaration +import de.fraunhofer.aisec.cpg.graph.isTypeSystemActive import de.fraunhofer.aisec.cpg.graph.types.FunctionPointerType import de.fraunhofer.aisec.cpg.graph.types.Type +import de.fraunhofer.aisec.cpg.graph.types.UnknownType /** * This expression denotes the usage of an anonymous / lambda function. It connects the inner @@ -62,11 +63,11 @@ class LambdaExpression : Expression(), HasType.TypeListener { } override fun typeChanged(src: HasType, root: MutableList, oldType: Type) { - if (!TypeManager.isTypeSystemActive()) { + if (!isTypeSystemActive) { return } - if (!TypeManager.getInstance().isUnknown(type) && src.propagationType == oldType) { + if (type !is UnknownType && src.propagationType == oldType) { return } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/UnaryOperator.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/UnaryOperator.kt index 3c1c44db28..be71e58f86 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/UnaryOperator.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/UnaryOperator.kt @@ -28,7 +28,7 @@ package de.fraunhofer.aisec.cpg.graph.statements.expressions import de.fraunhofer.aisec.cpg.graph.AST import de.fraunhofer.aisec.cpg.graph.AccessValues import de.fraunhofer.aisec.cpg.graph.HasType -import de.fraunhofer.aisec.cpg.graph.TypeManager +import de.fraunhofer.aisec.cpg.graph.isTypeSystemActive import de.fraunhofer.aisec.cpg.graph.types.PointerType import de.fraunhofer.aisec.cpg.graph.types.Type import de.fraunhofer.aisec.cpg.helpers.Util.distinctBy @@ -103,7 +103,7 @@ class UnaryOperator : Expression(), HasType.TypeListener { } override fun typeChanged(src: HasType, root: MutableList, oldType: Type) { - if (!TypeManager.isTypeSystemActive()) { + if (!isTypeSystemActive) { return } val previous = type @@ -135,7 +135,7 @@ class UnaryOperator : Expression(), HasType.TypeListener { } override fun possibleSubTypesChanged(src: HasType, root: MutableList) { - if (!TypeManager.isTypeSystemActive()) { + if (!isTypeSystemActive) { return } if (src is HasType.TypeListener && getsDataFromInput(src as HasType.TypeListener)) { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/FunctionType.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/FunctionType.kt index db94a55534..182a2b2d47 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/FunctionType.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/FunctionType.kt @@ -27,9 +27,9 @@ package de.fraunhofer.aisec.cpg.graph.types import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend -import de.fraunhofer.aisec.cpg.graph.TypeManager import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.newUnknownType +import de.fraunhofer.aisec.cpg.graph.registerType /** * A type representing a function. It contains a list of parameters and one or more return types. @@ -80,7 +80,7 @@ constructor( func.language ) - return TypeManager.getInstance().registerType(type) + return func.registerType(type) } } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/HasType.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/HasType.kt index 4d28388065..b8c3081e9c 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/HasType.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/HasType.kt @@ -25,9 +25,11 @@ */ package de.fraunhofer.aisec.cpg.graph +import de.fraunhofer.aisec.cpg.frontends.TranslationException import de.fraunhofer.aisec.cpg.graph.types.Type +import java.util.Optional -interface HasType { +interface HasType : ContextProvider { var type: Type /** @@ -100,3 +102,33 @@ interface HasType { fun updateType(typeState: Collection) } } + +val Node.isTypeSystemActive: Boolean + get() { + return TypeManager.isTypeSystemActive() + } + +fun Node.isSupertypeOf(superType: Type, subType: Type?): Boolean { + val c = ctx ?: throw TranslationException("context not available") + return c.typeManager.isSupertypeOf(superType, subType, this) +} + +fun HasType.cacheType(type: Type) { + val c = ctx ?: throw TranslationException("context not available") + c.typeManager.cacheType(this, type) +} + +fun Node.registerType(type: T): T { + val c = ctx ?: throw TranslationException("context not available") + return c.typeManager.registerType(type) +} + +fun Node.getCommonType(types: Collection): Optional { + val c = ctx ?: throw TranslationException("context not available") + return c.typeManager.getCommonType(types, this.ctx) +} + +fun Node.stopPropagation(type: Type, newType: Type): Boolean { + val c = ctx ?: throw TranslationException("context not available") + return c.typeManager.stopPropagation(type, newType) +} diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CXXCallResolverHelper.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CXXCallResolverHelper.kt index f3285238d5..e58db03f34 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CXXCallResolverHelper.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CXXCallResolverHelper.kt @@ -25,6 +25,7 @@ */ package de.fraunhofer.aisec.cpg.passes +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.* @@ -44,14 +45,17 @@ import java.util.regex.Pattern fun compatibleSignatures( callSignature: List, functionSignature: List, - provider: ScopeProvider + ctx: TranslationContext ): Boolean { return if (callSignature.size == functionSignature.size) { for (i in callSignature.indices) { if ( callSignature[i]?.isPrimitive != functionSignature[i].isPrimitive && - !TypeManager.getInstance() - .isSupertypeOf(functionSignature[i], callSignature[i], provider) + !ctx.typeManager.isSupertypeOf( + functionSignature[i], + callSignature[i], + ctx.scopeManager + ) ) { return false } @@ -92,7 +96,8 @@ fun getCallSignatureWithDefaults( */ fun resolveWithImplicitCast( call: CallExpression, - initialInvocationCandidates: List + initialInvocationCandidates: List, + ctx: TranslationContext ): List { // Output list for invocationTargets obtaining a valid signature by performing implicit @@ -105,12 +110,13 @@ fun resolveWithImplicitCast( for (functionDeclaration in initialInvocationCandidates) { val callSignature = getCallSignatureWithDefaults(call, functionDeclaration) // Check if the signatures match by implicit casts - if (compatibleSignatures(callSignature, functionDeclaration.signatureTypes, call)) { + if (compatibleSignatures(callSignature, functionDeclaration.signatureTypes, ctx)) { val implicitCastTargets = signatureWithImplicitCastTransformation( getCallSignatureWithDefaults(call, functionDeclaration), call.arguments, - functionDeclaration.signatureTypes + functionDeclaration.signatureTypes, + ctx ) if (implicitCasts == null) { implicitCasts = implicitCastTargets @@ -119,7 +125,7 @@ fun resolveWithImplicitCast( // to the same target type checkMostCommonImplicitCast(implicitCasts, implicitCastTargets) } - if (compatibleSignatures(call.signature, functionDeclaration.signatureTypes, call)) { + if (compatibleSignatures(call.signature, functionDeclaration.signatureTypes, ctx)) { invocationTargetsWithImplicitCast.add(functionDeclaration) } else { invocationTargetsWithImplicitCastAndDefaults.add(functionDeclaration) @@ -255,7 +261,8 @@ fun shouldContinueSearchInParent(recordDeclaration: RecordDeclaration?, name: St */ fun resolveConstructorWithImplicitCast( constructExpression: ConstructExpression, - recordDeclaration: RecordDeclaration + recordDeclaration: RecordDeclaration, + ctx: TranslationContext ): ConstructorDeclaration? { for (constructorDeclaration in recordDeclaration.constructors) { val workingSignature = mutableListOf(*constructExpression.signature.toTypedArray()) @@ -272,29 +279,27 @@ fun resolveConstructorWithImplicitCast( compatibleSignatures( constructExpression.signature, constructorDeclaration.signatureTypes, - constructExpression + ctx ) ) { val implicitCasts = signatureWithImplicitCastTransformation( constructExpression.signature, constructExpression.arguments, - constructorDeclaration.signatureTypes + constructorDeclaration.signatureTypes, + ctx ) applyImplicitCastToArguments(constructExpression, implicitCasts) return constructorDeclaration } else if ( - compatibleSignatures( - workingSignature, - constructorDeclaration.signatureTypes, - constructExpression - ) + compatibleSignatures(workingSignature, constructorDeclaration.signatureTypes, ctx) ) { val implicitCasts = signatureWithImplicitCastTransformation( getCallSignatureWithDefaults(constructExpression, constructorDeclaration), constructExpression.arguments, - constructorDeclaration.signatureTypes + constructorDeclaration.signatureTypes, + ctx ) applyImplicitCastToArguments(constructExpression, implicitCasts) return constructorDeclaration @@ -327,7 +332,8 @@ fun applyTemplateInstantiation( function: FunctionDeclaration, initializationSignature: Map, initializationType: Map, - orderedInitializationSignature: Map + orderedInitializationSignature: Map, + ctx: TranslationContext ): List { val templateInstantiationParameters = mutableListOf(*orderedInitializationSignature.keys.toTypedArray()) @@ -357,7 +363,8 @@ fun applyTemplateInstantiation( signatureWithImplicitCastTransformation( templateCallSignature, templateCall.arguments, - templateFunctionSignature + templateFunctionSignature, + ctx ) for (i in callSignatureImplicit.indices) { val cast = callSignatureImplicit[i] @@ -396,7 +403,8 @@ fun applyTemplateInstantiation( fun signatureWithImplicitCastTransformation( callSignature: List, arguments: List, - functionSignature: List + functionSignature: List, + ctx: TranslationContext ): MutableList { val implicitCasts = mutableListOf() if (callSignature.size != functionSignature.size) return implicitCasts @@ -406,6 +414,7 @@ fun signatureWithImplicitCastTransformation( val funcType = functionSignature[i] if (callType?.isPrimitive == true && funcType.isPrimitive && callType != funcType) { val implicitCast = CastExpression() + implicitCast.ctx = ctx implicitCast.isImplicit = true implicitCast.castType = funcType implicitCast.language = funcType.language @@ -466,7 +475,8 @@ fun getTemplateInitializationSignature( templateCall: CallExpression, instantiationType: MutableMap, orderedInitializationSignature: MutableMap, - explicitInstantiated: MutableList + explicitInstantiated: MutableList, + ctx: TranslationContext ): Map? { // Construct Signature val signature = @@ -475,7 +485,8 @@ fun getTemplateInitializationSignature( templateCall, instantiationType, orderedInitializationSignature, - explicitInstantiated + explicitInstantiated, + ctx ) ?: return null val parameterizedTypeResolution = getParameterizedSignaturesFromInitialization(signature) @@ -528,14 +539,15 @@ fun constructTemplateInitializationSignatureFromTemplateParameters( templateCall: CallExpression, instantiationType: MutableMap, orderedInitializationSignature: MutableMap, - explicitInstantiated: MutableList + explicitInstantiated: MutableList, + ctx: TranslationContext ): MutableMap? { val instantiationSignature: MutableMap = HashMap() for (i in functionTemplateDeclaration.parameters.indices) { if (i < templateCall.templateParameters.size) { val callParameter = templateCall.templateParameters[i] val templateParameter = functionTemplateDeclaration.parameters[i] - if (isInstantiated(callParameter, templateParameter)) { + if (isInstantiated(callParameter, templateParameter, ctx.typeManager)) { instantiationSignature[templateParameter] = callParameter instantiationType[callParameter] = TemplateDeclaration.TemplateInitialization.EXPLICIT @@ -570,7 +582,11 @@ fun constructTemplateInitializationSignatureFromTemplateParameters( * callParameterArg must be an Expression and its type must match the type of the * ParamVariableDeclaration (same type or subtype) => returns true Otherwise return false */ -fun isInstantiated(callParameterArg: Node, templateParameter: Declaration?): Boolean { +fun isInstantiated( + callParameterArg: Node, + templateParameter: Declaration?, + typeManager: TypeManager +): Boolean { var callParameter = callParameterArg if (callParameter is TypeExpression) { callParameter = callParameter.type @@ -579,8 +595,7 @@ fun isInstantiated(callParameterArg: Node, templateParameter: Declaration?): Boo callParameter is ObjectType } else if (callParameter is Expression && templateParameter is ParamVariableDeclaration) { callParameter.type == templateParameter.type || - TypeManager.getInstance() - .isSupertypeOf(templateParameter.type, callParameter.type, callParameterArg) + typeManager.isSupertypeOf(templateParameter.type, callParameter.type, callParameterArg) } else { false } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CallResolver.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CallResolver.kt index 2db61abddd..5381390552 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CallResolver.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CallResolver.kt @@ -25,8 +25,7 @@ */ package de.fraunhofer.aisec.cpg.passes -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.HasComplexCallResolution import de.fraunhofer.aisec.cpg.frontends.HasDefaultArguments import de.fraunhofer.aisec.cpg.frontends.HasSuperClasses @@ -65,8 +64,7 @@ import org.slf4j.LoggerFactory * This pass should NOT use any DFG edges because they are computed / adjusted in a later stage. */ @DependsOn(VariableUsageResolver::class) -open class CallResolver(config: TranslationConfiguration, scopeManager: ScopeManager) : - SymbolResolverPass(config, scopeManager) { +open class CallResolver(ctx: TranslationContext) : SymbolResolverPass(ctx) { /** * This seems to be a map between function declarations (more likely method declarations) and * their parent record (more accurately their type). Seems to be only used by @@ -104,8 +102,7 @@ open class CallResolver(config: TranslationConfiguration, scopeManager: ScopeMan private fun registerMethods(currentClass: RecordDeclaration?, currentNode: Node?) { if (currentNode is MethodDeclaration && currentClass != null) { - containingType[currentNode] = - TypeParser.createFrom(currentClass.name, currentClass.language) + containingType[currentNode] = currentNode.parseType(currentClass.name) } } @@ -185,7 +182,7 @@ open class CallResolver(config: TranslationConfiguration, scopeManager: ScopeMan curClass, call, true, - scopeManager, + ctx, currentTU ) @@ -212,10 +209,8 @@ open class CallResolver(config: TranslationConfiguration, scopeManager: ScopeMan // unit. Nothing else is allowed (fow now) val func = when (val start = scope?.astNode) { - is TranslationUnitDeclaration -> - start.inferFunction(call, scopeManager = scopeManager) - is NamespaceDeclaration -> - start.inferFunction(call, scopeManager = scopeManager) + is TranslationUnitDeclaration -> start.inferFunction(call, ctx = ctx) + is NamespaceDeclaration -> start.inferFunction(call, ctx = ctx) else -> null } @@ -306,7 +301,7 @@ open class CallResolver(config: TranslationConfiguration, scopeManager: ScopeMan if (language is HasComplexCallResolution) { // Handle CXX normal call resolution externally, otherwise it leads to increased // complexity - language.refineNormalCallResolution(call, scopeManager, currentTU) + language.refineNormalCallResolution(call, ctx, currentTU) } else { scopeManager.resolveFunction(call).toMutableList() } @@ -385,7 +380,7 @@ open class CallResolver(config: TranslationConfiguration, scopeManager: ScopeMan curClass, possibleContainingTypes, call, - scopeManager, + ctx, currentTU, this ) @@ -409,13 +404,13 @@ open class CallResolver(config: TranslationConfiguration, scopeManager: ScopeMan .mapNotNull { var record = recordMap[it.root.name] if (record == null && config?.inferenceConfiguration?.inferRecords == true) { - record = it.startInference(scopeManager).inferRecordDeclaration(it, currentTU) + record = it.startInference(ctx).inferRecordDeclaration(it, currentTU) // update the record map if (record != null) it.root.name.let { name -> recordMap[name] = record } } record } - .map { record -> record.inferMethod(call, scopeManager = scopeManager) } + .map { record -> record.inferMethod(call, ctx = ctx) } } /** @@ -522,7 +517,8 @@ open class CallResolver(config: TranslationConfiguration, scopeManager: ScopeMan (call.language as HasComplexCallResolution).refineInvocationCandidatesFromRecord( recordDeclaration, call, - namePattern + namePattern, + ctx ) } else { recordDeclaration.methods.filter { @@ -612,12 +608,12 @@ open class CallResolver(config: TranslationConfiguration, scopeManager: ScopeMan // If we don't find any candidate and our current language is c/c++ we check if there is // a candidate with an implicit cast constructorCandidate = - resolveConstructorWithImplicitCast(constructExpression, recordDeclaration) + resolveConstructorWithImplicitCast(constructExpression, recordDeclaration, ctx) } return constructorCandidate ?: recordDeclaration - .startInference(scopeManager) + .startInference(ctx) .createInferredConstructor(constructExpression.signature) } @@ -626,7 +622,7 @@ open class CallResolver(config: TranslationConfiguration, scopeManager: ScopeMan recordDeclaration: RecordDeclaration ): ConstructorDeclaration { return recordDeclaration.constructors.firstOrNull { it.hasSignature(signature) } - ?: recordDeclaration.startInference(scopeManager).createInferredConstructor(signature) + ?: recordDeclaration.startInference(ctx).createInferredConstructor(signature) } companion object { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPass.kt index 945cef99c6..d800dbe8b8 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPass.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPass.kt @@ -25,8 +25,7 @@ */ package de.fraunhofer.aisec.cpg.passes -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.* import de.fraunhofer.aisec.cpg.graph.edge.Properties @@ -46,10 +45,8 @@ import de.fraunhofer.aisec.cpg.passes.order.DependsOn */ @DependsOn(EvaluationOrderGraphPass::class) @DependsOn(DFGPass::class) -open class ControlFlowSensitiveDFGPass( - config: TranslationConfiguration, - scopeManager: ScopeManager, -) : TranslationUnitPass(config, scopeManager) { +open class ControlFlowSensitiveDFGPass(ctx: TranslationContext) : TranslationUnitPass(ctx) { + override fun cleanup() { // Nothing to do } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/DFGPass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/DFGPass.kt index 6a5a223be0..d1d3ec7202 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/DFGPass.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/DFGPass.kt @@ -25,8 +25,7 @@ */ package de.fraunhofer.aisec.cpg.passes -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.FieldDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration @@ -40,8 +39,7 @@ import de.fraunhofer.aisec.cpg.passes.order.DependsOn /** Adds the DFG edges for various types of nodes. */ @DependsOn(VariableUsageResolver::class) @DependsOn(CallResolver::class) -class DFGPass(config: TranslationConfiguration, scopeManager: ScopeManager) : - ComponentPass(config, scopeManager) { +class DFGPass(ctx: TranslationContext) : ComponentPass(ctx) { override fun accept(component: Component) { val inferDfgForUnresolvedCalls = config.inferenceConfiguration.inferDfgForUnresolvedSymbols val walker = IterativeGraphWalker() diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EdgeCachePass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EdgeCachePass.kt index daf12316d3..88ebf574b6 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EdgeCachePass.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EdgeCachePass.kt @@ -25,8 +25,7 @@ */ package de.fraunhofer.aisec.cpg.passes -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.graph.Component import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker @@ -83,8 +82,7 @@ object Edges { * * The cache itself is stored in the [Edges] object. */ -class EdgeCachePass(config: TranslationConfiguration, scopeManager: ScopeManager) : - ComponentPass(config, scopeManager) { +class EdgeCachePass(ctx: TranslationContext) : ComponentPass(ctx) { override fun accept(component: Component) { Edges.clear() diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EvaluationOrderGraphPass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EvaluationOrderGraphPass.kt index 7b13a853a4..9c837fcb1c 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EvaluationOrderGraphPass.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EvaluationOrderGraphPass.kt @@ -25,13 +25,11 @@ */ package de.fraunhofer.aisec.cpg.passes -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.HasShortCircuitOperators import de.fraunhofer.aisec.cpg.frontends.ProcessedListener import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.StatementHolder -import de.fraunhofer.aisec.cpg.graph.TypeManager import de.fraunhofer.aisec.cpg.graph.declarations.* import de.fraunhofer.aisec.cpg.graph.edge.Properties import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge @@ -71,8 +69,7 @@ import org.slf4j.LoggerFactory */ @Suppress("MemberVisibilityCanBePrivate") @DependsOn(CallResolver::class) -open class EvaluationOrderGraphPass(config: TranslationConfiguration, scopeManager: ScopeManager) : - TranslationUnitPass(config, scopeManager) { +open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPass(ctx) { protected val map = mutableMapOf, (Node) -> Unit>() private var currentPredecessors = mutableListOf() private val nextEdgeProperties = EnumMap(Properties::class.java) @@ -580,9 +577,7 @@ open class EvaluationOrderGraphPass(config: TranslationConfiguration, scopeManag val catchParam = catchClause.parameter if (catchParam == null) { // e.g. catch (...) currentPredecessors.addAll(eogEdges) - } else if ( - TypeManager.getInstance().isSupertypeOf(catchParam.type, throwType, node) - ) { + } else if (typeManager.isSupertypeOf(catchParam.type, throwType, node)) { currentPredecessors.addAll(eogEdges) toRemove.add(throwType) } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/FilenameMapper.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/FilenameMapper.kt index d715c99467..03746a283b 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/FilenameMapper.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/FilenameMapper.kt @@ -25,8 +25,7 @@ */ package de.fraunhofer.aisec.cpg.passes -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration import de.fraunhofer.aisec.cpg.passes.order.ExecuteLast @@ -34,8 +33,7 @@ import de.fraunhofer.aisec.cpg.processing.IVisitor import de.fraunhofer.aisec.cpg.processing.strategy.Strategy @ExecuteLast -class FilenameMapper(config: TranslationConfiguration, scopeManager: ScopeManager) : - TranslationUnitPass(config, scopeManager) { +class FilenameMapper(ctx: TranslationContext) : TranslationUnitPass(ctx) { override fun accept(tu: TranslationUnitDeclaration) { val file = tu.name.toString() tu.file = file diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/FunctionPointerCallResolver.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/FunctionPointerCallResolver.kt index 4951f52ada..cc50cdff5b 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/FunctionPointerCallResolver.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/FunctionPointerCallResolver.kt @@ -25,8 +25,7 @@ */ package de.fraunhofer.aisec.cpg.passes -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.cpp.CXXLanguageFrontend import de.fraunhofer.aisec.cpg.graph.Component import de.fraunhofer.aisec.cpg.graph.Node @@ -57,8 +56,7 @@ import java.util.function.Consumer @DependsOn(CallResolver::class) @DependsOn(DFGPass::class) @RequiredFrontend(CXXLanguageFrontend::class) -class FunctionPointerCallResolver(config: TranslationConfiguration, scopeManager: ScopeManager) : - ComponentPass(config, scopeManager) { +class FunctionPointerCallResolver(ctx: TranslationContext) : ComponentPass(ctx) { private lateinit var walker: ScopedWalker private var inferDfgForUnresolvedCalls = false @@ -152,7 +150,7 @@ class FunctionPointerCallResolver(config: TranslationConfiguration, scopeManager call.invokes = invocationCandidates // We have to update the dfg edges because this call could now be resolved (which was not // the case before). - DFGPass(config, scopeManager).handleCallExpression(call, inferDfgForUnresolvedCalls) + DFGPass(ctx).handleCallExpression(call, inferDfgForUnresolvedCalls) } override fun cleanup() { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ImportResolver.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ImportResolver.kt index fb3c4360ae..65ae374e2e 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ImportResolver.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ImportResolver.kt @@ -25,13 +25,9 @@ */ package de.fraunhofer.aisec.cpg.passes -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration -import de.fraunhofer.aisec.cpg.graph.Component -import de.fraunhofer.aisec.cpg.graph.Node +import de.fraunhofer.aisec.cpg.TranslationContext +import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.* -import de.fraunhofer.aisec.cpg.graph.newFieldDeclaration -import de.fraunhofer.aisec.cpg.graph.newMethodDeclaration import de.fraunhofer.aisec.cpg.graph.types.UnknownType import de.fraunhofer.aisec.cpg.passes.order.DependsOn import de.fraunhofer.aisec.cpg.processing.IVisitor @@ -40,8 +36,7 @@ import java.util.* import java.util.regex.Pattern @DependsOn(TypeHierarchyResolver::class) -open class ImportResolver(config: TranslationConfiguration, scopeManager: ScopeManager) : - ComponentPass(config, scopeManager) { +open class ImportResolver(ctx: TranslationContext) : ComponentPass(ctx) { protected val records: MutableList = ArrayList() protected val importables: MutableMap = HashMap() diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/Pass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/Pass.kt index bc54757691..5a466b8c40 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/Pass.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/Pass.kt @@ -27,15 +27,14 @@ package de.fraunhofer.aisec.cpg.passes import de.fraunhofer.aisec.cpg.ScopeManager import de.fraunhofer.aisec.cpg.TranslationConfiguration +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.TranslationResult import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend import de.fraunhofer.aisec.cpg.frontends.TranslationException -import de.fraunhofer.aisec.cpg.graph.Component -import de.fraunhofer.aisec.cpg.graph.LanguageProvider -import de.fraunhofer.aisec.cpg.graph.Node +import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration -import de.fraunhofer.aisec.cpg.passes.order.* +import de.fraunhofer.aisec.cpg.passes.order.RequiredFrontend import java.util.function.Consumer import kotlin.reflect.KClass import kotlin.reflect.full.primaryConstructor @@ -46,23 +45,20 @@ import org.slf4j.LoggerFactory * A [TranslationResultPass] is a pass that operates on a [TranslationResult]. If used with * [executePassSequential], one [Pass] object is instantiated for the whole [TranslationResult]. */ -abstract class TranslationResultPass(config: TranslationConfiguration, scopeManager: ScopeManager) : - Pass(config, scopeManager) +abstract class TranslationResultPass(ctx: TranslationContext) : Pass(ctx) /** * A [ComponentPass] is a pass that operates on a [Component]. If used with [executePassSequential], * one [Pass] object is instantiated for each [Component] in a [TranslationResult]. */ -abstract class ComponentPass(config: TranslationConfiguration, scopeManager: ScopeManager) : - Pass(config, scopeManager) +abstract class ComponentPass(ctx: TranslationContext) : Pass(ctx) /** * A [TranslationUnitPass] is a pass that operates on a [TranslationUnitDeclaration]. If used with * [executePassSequential], one [Pass] object is instantiated for each [TranslationUnitDeclaration] * in a [Component]. */ -abstract class TranslationUnitPass(config: TranslationConfiguration, scopeManager: ScopeManager) : - Pass(config, scopeManager) +abstract class TranslationUnitPass(ctx: TranslationContext) : Pass(ctx) /** * A pass target is an interface for a [Node] on which a [Pass] can operate, it should only be @@ -81,13 +77,15 @@ interface PassTarget * passes. Instead of directly subclassing this type, one of the types [TranslationResultPass], * [ComponentPass] or [TranslationUnitPass] must be used. */ -sealed class Pass( - val config: TranslationConfiguration, - val scopeManager: ScopeManager -) : Consumer { +sealed class Pass(final override val ctx: TranslationContext) : + Consumer, ContextProvider { var name: String protected set + val config: TranslationConfiguration = ctx.config + val scopeManager: ScopeManager = ctx.scopeManager + val typeManager: TypeManager = ctx.typeManager + init { name = this.javaClass.name } @@ -123,6 +121,7 @@ sealed class Pass( */ fun executePassSequential( cls: KClass>, + ctx: TranslationContext, result: TranslationResult, executedFrontends: Collection ) { @@ -130,28 +129,16 @@ fun executePassSequential( // "prototype" instance of our pass class, so we can deduce certain type information more // easily. val prototype = - cls.primaryConstructor?.call(result.config, result.scopeManager) + cls.primaryConstructor?.call(ctx) ?: throw TranslationException("Could not create prototype pass") when (prototype) { is TranslationResultPass -> { - executePass( - (prototype as TranslationResultPass)::class, - result, - result.config, - result.scopeManager, - executedFrontends - ) + executePass((prototype as TranslationResultPass)::class, ctx, result, executedFrontends) } is ComponentPass -> { for (component in result.components) { - executePass( - (prototype as ComponentPass)::class, - component, - result.config, - result.scopeManager, - executedFrontends - ) + executePass((prototype as ComponentPass)::class, ctx, component, executedFrontends) } } is TranslationUnitPass -> { @@ -159,9 +146,8 @@ fun executePassSequential( for (tu in component.translationUnits) { executePass( (prototype as TranslationUnitPass)::class, + ctx, tu, - result.config, - result.scopeManager, executedFrontends ) } @@ -172,9 +158,8 @@ fun executePassSequential( inline fun executePass( cls: KClass>, + ctx: TranslationContext, target: T, - config: TranslationConfiguration, - scopeManager: ScopeManager, executedFrontends: Collection ): Pass? { val language = @@ -184,9 +169,9 @@ inline fun executePass( null } - val realClass = checkForReplacement(cls, language, config) + val realClass = checkForReplacement(cls, language, ctx.config) - val pass = realClass.primaryConstructor?.call(config, scopeManager) + val pass = realClass.primaryConstructor?.call(ctx) if (pass?.runsWithCurrentFrontend(executedFrontends) == true) { pass.accept(target) pass.cleanup() diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/StatisticsCollectionPass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/StatisticsCollectionPass.kt index 452a5e1597..7606341d1f 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/StatisticsCollectionPass.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/StatisticsCollectionPass.kt @@ -25,8 +25,7 @@ */ package de.fraunhofer.aisec.cpg.passes -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.TranslationResult import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.ProblemNode @@ -38,14 +37,13 @@ import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker.ScopedWalker * A [Pass] collecting statistics for the graph. Currently, it collects the number of nodes and the * number of problem nodes (i.e., nodes where the translation failed for some reason). */ -class StatisticsCollectionPass(config: TranslationConfiguration, scopeManager: ScopeManager) : - TranslationResultPass(config, scopeManager) { +class StatisticsCollectionPass(ctx: TranslationContext) : TranslationResultPass(ctx) { /** Iterates the nodes of the [result] to collect statistics. */ override fun accept(result: TranslationResult) { var problemNodes = 0 var nodes = 0 - val walker = ScopedWalker(result.scopeManager) + val walker = ScopedWalker(ctx.scopeManager) walker.registerHandler { _: RecordDeclaration?, _: Node?, currNode: Node? -> nodes++ if (currNode is ProblemNode) { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolverPass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolverPass.kt index 5851eed767..4ca2deb0ee 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolverPass.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolverPass.kt @@ -25,8 +25,7 @@ */ package de.fraunhofer.aisec.cpg.passes -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.HasSuperClasses import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.* @@ -34,8 +33,7 @@ import de.fraunhofer.aisec.cpg.graph.statements.expressions.DeclaredReferenceExp import de.fraunhofer.aisec.cpg.graph.types.* import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker -abstract class SymbolResolverPass(config: TranslationConfiguration, scopeManager: ScopeManager) : - ComponentPass(config, scopeManager) { +abstract class SymbolResolverPass(ctx: TranslationContext) : ComponentPass(ctx) { protected lateinit var walker: SubgraphWalker.ScopedWalker lateinit var currentTU: TranslationUnitDeclaration @@ -55,7 +53,7 @@ abstract class SymbolResolverPass(config: TranslationConfiguration, scopeManager protected fun findEnums(node: Node?) { if (node is EnumDeclaration) { // TODO: Use the name instead of the type. - val type = TypeParser.createFrom(node.name, node.language) + val type = node.parseType(node.name) enumMap.putIfAbsent(type, node) } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/TypeHierarchyResolver.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/TypeHierarchyResolver.kt index 61684954ee..a8ce5ba3db 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/TypeHierarchyResolver.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/TypeHierarchyResolver.kt @@ -25,8 +25,7 @@ */ package de.fraunhofer.aisec.cpg.passes -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.graph.Component import de.fraunhofer.aisec.cpg.graph.Name import de.fraunhofer.aisec.cpg.graph.Node @@ -56,8 +55,7 @@ import java.util.* * at places where it is crucial to have parsed all [RecordDeclaration]s. Otherwise, type * information in the graph might not be fully correct */ -open class TypeHierarchyResolver(config: TranslationConfiguration, scopeManager: ScopeManager) : - ComponentPass(config, scopeManager) { +open class TypeHierarchyResolver(ctx: TranslationContext) : ComponentPass(ctx) { protected val recordMap = mutableMapOf() protected val enums = mutableListOf() diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/TypeResolver.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/TypeResolver.kt index 15bc3c637c..5a0d702e97 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/TypeResolver.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/TypeResolver.kt @@ -25,21 +25,18 @@ */ package de.fraunhofer.aisec.cpg.passes -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.graph.Component import de.fraunhofer.aisec.cpg.graph.HasType import de.fraunhofer.aisec.cpg.graph.HasType.SecondaryTypeEdge import de.fraunhofer.aisec.cpg.graph.Node -import de.fraunhofer.aisec.cpg.graph.TypeManager import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration import de.fraunhofer.aisec.cpg.graph.types.* import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker.IterativeGraphWalker import de.fraunhofer.aisec.cpg.passes.order.DependsOn @DependsOn(CallResolver::class) -open class TypeResolver(config: TranslationConfiguration, scopeManager: ScopeManager) : - ComponentPass(config, scopeManager) { +open class TypeResolver(ctx: TranslationContext) : ComponentPass(ctx) { protected val firstOrderTypes = mutableSetOf() protected val typeState = mutableMapOf>() @@ -108,7 +105,6 @@ open class TypeResolver(config: TranslationConfiguration, scopeManager: ScopeMan } protected fun removeDuplicateTypes() { - val typeManager = TypeManager.getInstance() // Remove duplicate firstOrderTypes firstOrderTypes.addAll(typeManager.firstOrderTypes) @@ -241,6 +237,5 @@ open class TypeResolver(config: TranslationConfiguration, scopeManager: ScopeMan override fun cleanup() { firstOrderTypes.clear() typeState.clear() - TypeManager.reset() } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/VariableUsageResolver.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/VariableUsageResolver.kt index 3a28492440..b9155175f5 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/VariableUsageResolver.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/VariableUsageResolver.kt @@ -25,8 +25,7 @@ */ package de.fraunhofer.aisec.cpg.passes -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.HasStructs import de.fraunhofer.aisec.cpg.frontends.HasSuperClasses import de.fraunhofer.aisec.cpg.graph.* @@ -59,8 +58,7 @@ import org.slf4j.LoggerFactory * rather makes their "refersTo" point to the appropriate [ValueDeclaration]. */ @DependsOn(TypeHierarchyResolver::class) -open class VariableUsageResolver(config: TranslationConfiguration, scopeManager: ScopeManager) : - SymbolResolverPass(config, scopeManager) { +open class VariableUsageResolver(ctx: TranslationContext) : SymbolResolverPass(ctx) { override fun accept(component: Component) { walker = ScopedWalker(scopeManager) @@ -144,7 +142,7 @@ open class VariableUsageResolver(config: TranslationConfiguration, scopeManager: var recordDeclType: Type? = null if (currentClass != null) { - recordDeclType = TypeParser.createFrom(currentClass.name, currentClass.language) + recordDeclType = currentClass.parseType(currentClass.name) } if (current.type is FunctionPointerType && refersTo == null) { refersTo = resolveFunctionPtr(current) @@ -200,9 +198,9 @@ open class VariableUsageResolver(config: TranslationConfiguration, scopeManager: return if (language != null && language.namespaceDelimiter.isNotEmpty()) { val parentName = (current.name.parent ?: current.name).toString() - TypeParser.createFrom(parentName, language) + current.parseType(parentName) } else { - current.language.newUnknownType() + current.newUnknownType() } } @@ -236,7 +234,7 @@ open class VariableUsageResolver(config: TranslationConfiguration, scopeManager: "Could not find referring super type ${superType.typeName} for ${curClass.name} in the record map. Will set the super type to java.lang.Object" ) // TODO: Should be more generic! - base.type = TypeParser.createFrom(Any::class.java.name, current.language) + base.type = current.parseType(Any::class.java.name) } else { // We need to connect this super reference to the receiver of this // method @@ -256,7 +254,7 @@ open class VariableUsageResolver(config: TranslationConfiguration, scopeManager: } else { // no explicit super type -> java.lang.Object // TODO: Should be more generic - val objectType = TypeParser.createFrom(Any::class.java.name, current.language) + val objectType = current.parseType(Any::class.java.name) base.type = objectType } } else { @@ -271,13 +269,13 @@ open class VariableUsageResolver(config: TranslationConfiguration, scopeManager: return } } else if (baseTarget is RecordDeclaration) { - var baseType = TypeParser.createFrom(baseTarget.name, baseTarget.language) + var baseType = baseTarget.parseType(baseTarget.name) if (baseType.name !in recordMap) { val containingT = baseType val fqnResolvedType = recordMap.keys.firstOrNull { it.lastPartsMatch(containingT.name) } if (fqnResolvedType != null) { - baseType = TypeParser.createFrom(fqnResolvedType, baseTarget.language) + baseType = baseTarget.parseType(fqnResolvedType) } } current.refersTo = resolveMember(baseType, current) @@ -288,7 +286,7 @@ open class VariableUsageResolver(config: TranslationConfiguration, scopeManager: if (baseType.name !in recordMap) { val fqnResolvedType = recordMap.keys.firstOrNull { it.lastPartsMatch(baseType.name) } if (fqnResolvedType != null) { - baseType = TypeParser.createFrom(fqnResolvedType, baseType.language) + baseType = current.base.parseType(fqnResolvedType) } } current.refersTo = resolveMember(baseType, current) @@ -361,8 +359,7 @@ open class VariableUsageResolver(config: TranslationConfiguration, scopeManager: } else { "class" } - val record = - base.startInference(scopeManager).inferRecordDeclaration(base, currentTU, kind) + val record = base.startInference(ctx).inferRecordDeclaration(base, currentTU, kind) // update the record map if (record != null) recordMap[base.name] = record } @@ -421,7 +418,7 @@ open class VariableUsageResolver(config: TranslationConfiguration, scopeManager: // If we didn't find anything, we create a new function or method declaration return target ?: (declarationHolder ?: currentTU) - .startInference(scopeManager) + .startInference(ctx) .createInferredFunctionDeclaration( name, null, diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/Inference.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/Inference.kt index f1856e6606..4048ce0d7e 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/Inference.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/Inference.kt @@ -25,7 +25,7 @@ */ package de.fraunhofer.aisec.cpg.passes.inference -import de.fraunhofer.aisec.cpg.ScopeManager +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.HasClasses import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend @@ -50,8 +50,8 @@ import org.slf4j.LoggerFactory * Since this class implements [IsInferredProvider], all nodes that are created using the node * builder functions, will automatically have [Node.isInferred] set to true. */ -class Inference(val start: Node, val scopeManager: ScopeManager) : - LanguageProvider, ScopeProvider, IsInferredProvider { +class Inference(val start: Node, override val ctx: TranslationContext) : + LanguageProvider, ScopeProvider, IsInferredProvider, ContextProvider { val log: Logger = LoggerFactory.getLogger(Inference::class.java) override val language: Language? @@ -60,6 +60,9 @@ class Inference(val start: Node, val scopeManager: ScopeManager) : override val isInferred: Boolean get() = true + val scopeManager = ctx.scopeManager + val typeManager = ctx.typeManager + override val scope: Scope? get() = scopeManager.currentScope @@ -84,7 +87,9 @@ class Inference(val start: Node, val scopeManager: ScopeManager) : return inferInScopeOf(start) { log.debug( - "Inferring a new function declaration $name with parameter types ${signature.map { it?.name }}" + "Inferring a new function declaration {} with parameter types {}", + name, + signature.map { it?.name } ) // "upgrade" our struct to a class, if it was inferred by us, since we are calling @@ -229,8 +234,7 @@ class Inference(val start: Node, val scopeManager: ScopeManager) : name: String, ): TypeParamDeclaration { val parameterizedType = ParameterizedType(name, language) - TypeManager.getInstance() - .addTypeParameter(start as? FunctionTemplateDeclaration, parameterizedType) + typeManager.addTypeParameter(start as? FunctionTemplateDeclaration, parameterizedType) val decl = newTypeParamDeclaration(name, name) decl.type = parameterizedType @@ -265,10 +269,10 @@ class Inference(val start: Node, val scopeManager: ScopeManager) : val inferredRealization = if (record != null) { record.addDeclaration(inferred) - record.inferMethod(call, scopeManager = scopeManager) + record.inferMethod(call, ctx = ctx) } else { tu?.addDeclaration(inferred) - tu?.inferFunction(call, scopeManager = scopeManager) + tu?.inferFunction(call, ctx = ctx) } inferredRealization?.let { inferred.addRealization(it) } @@ -280,16 +284,14 @@ class Inference(val start: Node, val scopeManager: ScopeManager) : // Template Parameter val inferredTypeIdentifier = "T$typeCounter" val typeParamDeclaration = - inferred - .startInference(scopeManager) - .inferTemplateParameter(inferredTypeIdentifier) + inferred.startInference(ctx).inferTemplateParameter(inferredTypeIdentifier) typeCounter++ inferred.addParameter(typeParamDeclaration) } else if (node is Expression) { val inferredNonTypeIdentifier = "N$nonTypeCounter" val paramVariableDeclaration = node - .startInference(scopeManager) + .startInference(ctx) .inferNonTypeTemplateParameter(inferredNonTypeIdentifier) paramVariableDeclaration.addPrevDFG(node) @@ -339,13 +341,13 @@ class Inference(val start: Node, val scopeManager: ScopeManager) : // restore it. return inferInScopeOf(start) { log.debug( - "Inferring a new namespace declaration $name ${ - if (path != null) { - "with path '$path'" - } else { - "" - } - }" + "Inferring a new namespace declaration {} {}", + name, + if (path != null) { + "with path '$path'" + } else { + "" + } ) val inferred = newNamespaceDeclaration(name) @@ -367,15 +369,15 @@ interface IsInferredProvider : MetadataProvider { } /** Returns a new [Inference] object starting from this node. */ -fun Node.startInference(scopeManager: ScopeManager) = Inference(this, scopeManager) +fun Node.startInference(ctx: TranslationContext) = Inference(this, ctx) /** Tries to infer a [FunctionDeclaration] from a [CallExpression]. */ fun TranslationUnitDeclaration.inferFunction( call: CallExpression, isStatic: Boolean = false, - scopeManager: ScopeManager, + ctx: TranslationContext ): FunctionDeclaration { - return Inference(this, scopeManager) + return Inference(this, ctx) .createInferredFunctionDeclaration( call.name.localName, call.code, @@ -390,9 +392,9 @@ fun TranslationUnitDeclaration.inferFunction( fun NamespaceDeclaration.inferFunction( call: CallExpression, isStatic: Boolean = false, - scopeManager: ScopeManager, + ctx: TranslationContext ): FunctionDeclaration { - return Inference(this, scopeManager) + return Inference(this, ctx) .createInferredFunctionDeclaration( call.name, call.code, @@ -407,9 +409,9 @@ fun NamespaceDeclaration.inferFunction( fun RecordDeclaration.inferMethod( call: CallExpression, isStatic: Boolean = false, - scopeManager: ScopeManager + ctx: TranslationContext ): MethodDeclaration { - return Inference(this, scopeManager) + return Inference(this, ctx) .createInferredFunctionDeclaration( call.name.localName, call.code, diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/GraphExamples.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/GraphExamples.kt index 91ea8727d0..d1eb129f32 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/GraphExamples.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/GraphExamples.kt @@ -28,6 +28,7 @@ package de.fraunhofer.aisec.cpg import de.fraunhofer.aisec.cpg.frontends.StructTestLanguage import de.fraunhofer.aisec.cpg.frontends.TestLanguage import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend +import de.fraunhofer.aisec.cpg.graph.TypeManager import de.fraunhofer.aisec.cpg.graph.builder.* import de.fraunhofer.aisec.cpg.graph.newVariableDeclaration import de.fraunhofer.aisec.cpg.sarif.PhysicalLocation @@ -37,6 +38,12 @@ import java.net.URI class GraphExamples { companion object { + fun testFrontend(config: TranslationConfiguration): TestLanguageFrontend { + val ctx = TranslationContext(config, ScopeManager(), TypeManager()) + val language = config.languages.filterIsInstance().first() + return TestLanguageFrontend(language.namespaceDelimiter, language, ctx) + } + fun getInferenceRecordPtr( config: TranslationConfiguration = TranslationConfiguration.builder() @@ -47,30 +54,25 @@ class GraphExamples { ) .build() ) = - TestLanguageFrontend( - ScopeManager(), - config.languages.first().namespaceDelimiter, - config.languages.first() - ) - .build { - translationResult(config) { - translationUnit("record.cpp") { - // The main method - function("main", t("int")) { - body { - declare { variable("node", t("T*")) } - member("value", ref("node"), "->") assign literal(42, t("int")) - member("next", ref("node"), "->") assign ref("node") - memberCall( - "dump", - ref("node") - ) // TODO: Do we have to encode the "->" here? - returnStmt { isImplicit = true } - } + testFrontend(config).build { + translationResult { + translationUnit("record.cpp") { + // The main method + function("main", t("int")) { + body { + declare { variable("node", t("T*")) } + member("value", ref("node"), "->") assign literal(42, t("int")) + member("next", ref("node"), "->") assign ref("node") + memberCall( + "dump", + ref("node") + ) // TODO: Do we have to encode the "->" here? + returnStmt { isImplicit = true } } } } } + } fun getInferenceRecord( config: TranslationConfiguration = @@ -82,26 +84,21 @@ class GraphExamples { ) .build() ) = - TestLanguageFrontend( - ScopeManager(), - config.languages.first().namespaceDelimiter, - config.languages.first() - ) - .build { - translationResult(config) { - translationUnit("record.cpp") { - // The main method - function("main", t("int")) { - body { - declare { variable("node", t("T")) } - member("value", ref("node")) assign literal(42, t("int")) - member("next", ref("node")) assign { reference(ref("node")) } - returnStmt { isImplicit = true } - } + testFrontend(config).build { + translationResult { + translationUnit("record.cpp") { + // The main method + function("main", t("int")) { + body { + declare { variable("node", t("T")) } + member("value", ref("node")) assign literal(42, t("int")) + member("next", ref("node")) assign { reference(ref("node")) } + returnStmt { isImplicit = true } } } } } + } fun getVariables( config: TranslationConfiguration = @@ -110,8 +107,8 @@ class GraphExamples { .registerLanguage(TestLanguage(".")) .build() ) = - TestLanguageFrontend(ScopeManager(), ".").build { - translationResult(config) { + testFrontend(config).build { + translationResult { translationUnit("Variables.java") { record("Variables") { field("field", t("int")) { @@ -161,8 +158,8 @@ class GraphExamples { .registerLanguage(TestLanguage(".")) .build() ) = - TestLanguageFrontend(ScopeManager(), ".").build { - translationResult(config) { + testFrontend(config).build { + translationResult { translationUnit("unaryoperator.cpp") { // The main method function("somefunc") { @@ -183,8 +180,8 @@ class GraphExamples { .registerLanguage(TestLanguage(".")) .build() ) = - TestLanguageFrontend(ScopeManager(), ".").build { - translationResult(config) { + testFrontend(config).build { + translationResult { translationUnit("compoundoperator.cpp") { // The main method function("somefunc") { @@ -205,8 +202,8 @@ class GraphExamples { .registerLanguage(TestLanguage(".")) .build() ) = - TestLanguageFrontend(ScopeManager(), ".").build { - translationResult(config) { + testFrontend(config).build { + translationResult { translationUnit("conditional_expression.cpp") { // The main method function("main", t("int")) { @@ -281,8 +278,8 @@ class GraphExamples { .registerLanguage(TestLanguage(".")) .build() ) = - TestLanguageFrontend(ScopeManager(), ".").build { - translationResult(config) { + testFrontend(config).build { + translationResult { translationUnit("BasicSlice.java") { record("BasicSlice") { // The main method @@ -367,8 +364,8 @@ class GraphExamples { .registerLanguage(TestLanguage(".")) .build() ) = - TestLanguageFrontend(ScopeManager(), ".").build { - translationResult(config) { + testFrontend(config).build { + translationResult { translationUnit("ControlFlowSensitiveDFGIfMerge.java") { record("ControlFlowSensitiveDFGIfMerge") { field("bla", t("int")) {} @@ -439,8 +436,8 @@ class GraphExamples { .registerLanguage(TestLanguage(".")) .build() ) = - TestLanguageFrontend(ScopeManager(), ".").build { - translationResult(config) { + testFrontend(config).build { + translationResult { translationUnit("ControlFlowSesitiveDFGSwitch.java") { record("ControlFlowSesitiveDFGSwitch") { // The main method @@ -513,8 +510,8 @@ class GraphExamples { .registerLanguage(TestLanguage(".")) .build() ) = - TestLanguageFrontend(ScopeManager(), ".").build { - translationResult(config) { + testFrontend(config).build { + translationResult { translationUnit("ControlFlowSensitiveDFGIfNoMerge.java") { record("ControlFlowSensitiveDFGIfNoMerge") { // The main method @@ -551,8 +548,8 @@ class GraphExamples { .registerLanguage(TestLanguage(".")) .build() ) = - TestLanguageFrontend(ScopeManager(), ".").build { - translationResult(config) { + testFrontend(config).build { + translationResult { translationUnit("LoopDFGs.java") { record("LoopDFGs") { // The main method @@ -632,8 +629,8 @@ class GraphExamples { .registerLanguage(TestLanguage(".")) .build() ) = - TestLanguageFrontend(ScopeManager(), ".").build { - translationResult(config) { + testFrontend(config).build { + translationResult { translationUnit("LoopDFGs.java") { record("LoopDFGs") { // The main method @@ -683,8 +680,8 @@ class GraphExamples { .registerLanguage(TestLanguage(".")) .build() ) = - TestLanguageFrontend(ScopeManager(), ".").build { - translationResult(config) { + testFrontend(config).build { + translationResult { translationUnit("DelayedAssignmentAfterRHS.java") { record("DelayedAssignmentAfterRHS") { // The main method @@ -709,8 +706,8 @@ class GraphExamples { .registerLanguage(TestLanguage(".")) .build() ) = - TestLanguageFrontend(ScopeManager(), ".").build { - translationResult(config) { + testFrontend(config).build { + translationResult { translationUnit("ReturnTest.java") { record("ReturnTest", "class") { method("testReturn", t("int")) { @@ -755,8 +752,8 @@ class GraphExamples { .registerLanguage(TestLanguage(".")) .build() ) = - TestLanguageFrontend(ScopeManager(), ".").build { - translationResult(config) { + testFrontend(config).build { + translationResult { translationUnit("RecordDeclaration.java") { namespace("compiling") { record("SimpleClass", "class") { @@ -801,8 +798,8 @@ class GraphExamples { .registerLanguage(TestLanguage(".")) .build() ) = - TestLanguageFrontend(ScopeManager(), ".").build { - translationResult(config) { + testFrontend(config).build { + translationResult { translationUnit("Dataflow.java") { record("Dataflow") { field("attr", t("String")) { literal("", t("String")) } @@ -864,8 +861,8 @@ class GraphExamples { .registerLanguage(TestLanguage(".")) .build() ) = - TestLanguageFrontend(ScopeManager(), ".").build { - translationResult(config) { + testFrontend(config).build { + translationResult { translationUnit("ShortcutClass.java") { record("ShortcutClass") { field("attr", t("int")) { literal(0, t("int")) } diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXLanguageFrontendTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXLanguageFrontendTest.kt index 24ae3164c3..3af1e05838 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXLanguageFrontendTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXLanguageFrontendTest.kt @@ -63,7 +63,7 @@ internal class CXXLanguageFrontendTest : BaseTest() { val decl = main.iterator().next() val ls = decl.variables["ls"] assertNotNull(ls) - assertEquals(createTypeFrom("std::vector", true), ls.type) + assertEquals(tu.parseType("std::vector", true), ls.type) assertLocalName("ls", ls) val forEachStatement = decl.getBodyStatementAs(1, ForEachStatement::class.java) @@ -132,7 +132,7 @@ internal class CXXLanguageFrontendTest : BaseTest() { val sizeof = i.initializer as? TypeIdExpression assertNotNull(sizeof) assertLocalName("sizeof", sizeof) - assertEquals(createTypeFrom("std::size_t", true), sizeof.type) + assertEquals(tu.parseType("std::size_t", true), sizeof.type) val typeInfo = funcDecl.variables["typeInfo"] assertNotNull(typeInfo) @@ -140,7 +140,7 @@ internal class CXXLanguageFrontendTest : BaseTest() { val typeid = typeInfo.initializer as? TypeIdExpression assertNotNull(typeid) assertLocalName("typeid", typeid) - assertEquals(createTypeFrom("const std::type_info&", true), typeid.type) + assertEquals(tu.parseType("const std::type_info&", true), typeid.type) val j = funcDecl.variables["j"] assertNotNull(j) @@ -149,7 +149,7 @@ internal class CXXLanguageFrontendTest : BaseTest() { assertNotNull(sizeof) assertNotNull(alignOf) assertLocalName("alignof", alignOf) - assertEquals(createTypeFrom("std::size_t", true), alignOf.type) + assertEquals(tu.parseType("std::size_t", true), alignOf.type) } @Test @@ -162,18 +162,18 @@ internal class CXXLanguageFrontendTest : BaseTest() { Objects.requireNonNull(main!!.getBodyStatementAs(0, DeclarationStatement::class.java)) ?.singleDeclaration as VariableDeclaration assertNotNull(e) - assertEquals(createTypeFrom("ExtendedClass*", true), e.type) + assertEquals(tu.parseType("ExtendedClass*", true), e.type) val b = Objects.requireNonNull(main.getBodyStatementAs(1, DeclarationStatement::class.java)) ?.singleDeclaration as VariableDeclaration assertNotNull(b) - assertEquals(createTypeFrom("BaseClass*", true), b.type) + assertEquals(tu.parseType("BaseClass*", true), b.type) // initializer var cast = b.initializer as? CastExpression assertNotNull(cast) - assertEquals(createTypeFrom("BaseClass*", true), cast.castType) + assertEquals(tu.parseType("BaseClass*", true), cast.castType) val staticCast = main.getBodyStatementAs(2, BinaryOperator::class.java) assertNotNull(staticCast) @@ -194,7 +194,7 @@ internal class CXXLanguageFrontendTest : BaseTest() { cast = d.initializer as? CastExpression assertNotNull(cast) - assertEquals(createTypeFrom("int", true), cast.castType) + assertEquals(tu.parseType("int", true), cast.castType) } @Test @@ -213,7 +213,7 @@ internal class CXXLanguageFrontendTest : BaseTest() { (statement.statements[0] as DeclarationStatement).singleDeclaration as VariableDeclaration assertNotNull(x) - assertEquals(createTypeFrom("int[]", true), x.type) + assertEquals(tu.parseType("int[]", true), x.type) // initializer is an initializer list expression val ile = x.initializer as? InitializerListExpression @@ -414,8 +414,8 @@ internal class CXXLanguageFrontendTest : BaseTest() { @Throws(Exception::class) fun testDeclarationStatement() { val file = File("src/test/resources/cxx/declstmt.cpp") - val declaration = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) - val function = declaration.getDeclarationAs(0, FunctionDeclaration::class.java) + val tu = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) + val function = tu.getDeclarationAs(0, FunctionDeclaration::class.java) val statements = function?.statements assertNotNull(statements) statements.forEach( @@ -432,7 +432,7 @@ internal class CXXLanguageFrontendTest : BaseTest() { (statements[0] as DeclarationStatement).getSingleDeclarationAs( VariableDeclaration::class.java ) - assertEquals(createTypeFrom("SSL_CTX*", true), declFromMultiplicateExpression.type) + assertEquals(tu.parseType("SSL_CTX*", true), declFromMultiplicateExpression.type) assertLocalName("ptr", declFromMultiplicateExpression) val withInitializer = @@ -450,19 +450,19 @@ internal class CXXLanguageFrontendTest : BaseTest() { val b = twoDeclarations[0] as VariableDeclaration assertNotNull(b) assertLocalName("b", b) - assertEquals(createTypeFrom("int*", false), b.type) + assertEquals(tu.parseType("int*", false), b.type) val c = twoDeclarations[1] as VariableDeclaration assertNotNull(c) assertLocalName("c", c) - assertEquals(createTypeFrom("int", false), c.type) + assertEquals(tu.parseType("int", false), c.type) val withoutInitializer = (statements[3] as DeclarationStatement).getSingleDeclarationAs( VariableDeclaration::class.java ) initializer = withoutInitializer.initializer - assertEquals(createTypeFrom("int*", true), withoutInitializer.type) + assertEquals(tu.parseType("int*", true), withoutInitializer.type) assertLocalName("d", withoutInitializer) assertNull(initializer) @@ -470,7 +470,7 @@ internal class CXXLanguageFrontendTest : BaseTest() { (statements[4] as DeclarationStatement).getSingleDeclarationAs( VariableDeclaration::class.java ) - assertEquals(createTypeFrom("std::string", true), qualifiedType.type) + assertEquals(tu.parseType("std::string", true), qualifiedType.type) assertLocalName("text", qualifiedType) assertTrue(qualifiedType.initializer is Literal<*>) assertEquals("some text", (qualifiedType.initializer as? Literal<*>)?.value) @@ -479,7 +479,7 @@ internal class CXXLanguageFrontendTest : BaseTest() { (statements[5] as DeclarationStatement).getSingleDeclarationAs( VariableDeclaration::class.java ) - assertEquals(createTypeFrom("void*", true), pointerWithAssign.type) + assertEquals(tu.parseType("void*", true), pointerWithAssign.type) assertLocalName("ptr2", pointerWithAssign) assertLocalName("NULL", pointerWithAssign.initializer) @@ -623,9 +623,8 @@ internal class CXXLanguageFrontendTest : BaseTest() { @Throws(Exception::class) fun testBinaryOperator() { val file = File("src/test/resources/binaryoperator.cpp") - val declaration = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) - val statements = - declaration.getDeclarationAs(0, FunctionDeclaration::class.java)?.statements + val tu = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) + val statements = tu.getDeclarationAs(0, FunctionDeclaration::class.java)?.statements assertNotNull(statements) // first two statements are just declarations @@ -658,7 +657,7 @@ internal class CXXLanguageFrontendTest : BaseTest() { // syntactically no different than the previous ones val stmt = statements[4] as DeclarationStatement val decl = stmt.singleDeclaration as VariableDeclaration - assertEquals(createTypeFrom("std::string*", true), decl.type) + assertEquals(tu.parseType("std::string*", true), decl.type) assertLocalName("notMultiplication", decl) assertTrue(decl.initializer is BinaryOperator) @@ -687,7 +686,7 @@ internal class CXXLanguageFrontendTest : BaseTest() { val constant = recordDeclaration.fields["CONSTANT"] assertNotNull(constant) - assertEquals(createTypeFrom("void*", true), field.type) + assertEquals(tu.parseType("void*", true), field.type) assertEquals(3, recordDeclaration.methods.size) val method = recordDeclaration.methods[0] @@ -705,12 +704,12 @@ internal class CXXLanguageFrontendTest : BaseTest() { val methodWithParam = recordDeclaration.methods[1] assertLocalName("method", methodWithParam) assertEquals(1, methodWithParam.parameters.size) - assertEquals(createTypeFrom("int", true), methodWithParam.parameters[0].type) + assertEquals(tu.parseType("int", true), methodWithParam.parameters[0].type) assertEquals( FunctionType( "(int)void*", - listOf(createTypeFrom("int", true)), - listOf(createTypeFrom("void*", true)), + listOf(tu.parseType("int", true)), + listOf(tu.parseType("void*", true)), CPPLanguage() ), methodWithParam.type @@ -726,7 +725,7 @@ internal class CXXLanguageFrontendTest : BaseTest() { val inlineMethod = recordDeclaration.methods[2] assertLocalName("inlineMethod", inlineMethod) assertEquals( - FunctionType("()void*", listOf(), listOf(createTypeFrom("void*", true)), CPPLanguage()), + FunctionType("()void*", listOf(), listOf(tu.parseType("void*", true)), CPPLanguage()), inlineMethod.type ) assertTrue(inlineMethod.hasBody()) @@ -737,7 +736,7 @@ internal class CXXLanguageFrontendTest : BaseTest() { FunctionType( "()SomeClass", listOf(), - listOf(createTypeFrom("SomeClass", true)), + listOf(tu.parseType("SomeClass", true)), CPPLanguage() ), inlineConstructor.type @@ -747,12 +746,12 @@ internal class CXXLanguageFrontendTest : BaseTest() { val constructorDefinition = tu.getDeclarationAs(3, ConstructorDeclaration::class.java) assertNotNull(constructorDefinition) assertEquals(1, constructorDefinition.parameters.size) - assertEquals(createTypeFrom("int", true), constructorDefinition.parameters[0].type) + assertEquals(tu.parseType("int", true), constructorDefinition.parameters[0].type) assertEquals( FunctionType( "(int)SomeClass", - listOf(createTypeFrom("int", false)), - listOf(createTypeFrom("SomeClass", true)), + listOf(tu.parseType("int", false)), + listOf(tu.parseType("SomeClass", true)), CPPLanguage() ), constructorDefinition.type @@ -857,10 +856,10 @@ internal class CXXLanguageFrontendTest : BaseTest() { @Throws(Exception::class) fun testInitListExpression() { val file = File("src/test/resources/initlistexpression.cpp") - val declaration = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) + val tu = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) // x y = { 1, 2 }; - val y = declaration.getDeclarationAs(1, VariableDeclaration::class.java) + val y = tu.getDeclarationAs(1, VariableDeclaration::class.java) assertNotNull(y) assertLocalName("y", y) @@ -877,8 +876,8 @@ internal class CXXLanguageFrontendTest : BaseTest() { assertEquals(2, b.value) // int z[] = { 2, 3, 4 }; - val z = declaration.getDeclarationAs(2, VariableDeclaration::class.java) - assertEquals(createTypeFrom("int[]", true), z!!.type) + val z = tu.getDeclarationAs(2, VariableDeclaration::class.java) + assertEquals(tu.parseType("int[]", true), z!!.type) initializer = z.initializer assertNotNull(initializer) @@ -892,11 +891,11 @@ internal class CXXLanguageFrontendTest : BaseTest() { @Throws(Exception::class) fun testObjectCreation() { val file = File("src/test/resources/cxx/objcreation.cpp") - val declaration = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) - assertNotNull(declaration) + val tu = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) + assertNotNull(tu) // get the main method - val main = declaration.getDeclarationAs(3, FunctionDeclaration::class.java) + val main = tu.getDeclarationAs(3, FunctionDeclaration::class.java) val statement = main!!.body as CompoundStatement // Integer i @@ -904,20 +903,20 @@ internal class CXXLanguageFrontendTest : BaseTest() { (statement.statements[0] as DeclarationStatement).singleDeclaration as VariableDeclaration // type should be Integer - assertEquals(createTypeFrom("Integer", true), i.type) + assertEquals(tu.parseType("Integer", true), i.type) // initializer should be a construct expression var constructExpression = i.initializer as? ConstructExpression assertNotNull(constructExpression) // type of the construct expression should also be Integer - assertEquals(createTypeFrom("Integer", true), constructExpression.type) + assertEquals(tu.parseType("Integer", true), constructExpression.type) // auto (Integer) m val m = (statement.statements[6] as DeclarationStatement).singleDeclaration as VariableDeclaration // type should be Integer* - assertEquals(createTypeFrom("Integer*", true), m.type) + assertEquals(tu.parseType("Integer*", true), m.type) val constructor = constructExpression.constructor assertNotNull(constructor) @@ -928,19 +927,19 @@ internal class CXXLanguageFrontendTest : BaseTest() { val newExpression = m.initializer as? NewExpression assertNotNull(newExpression) // type of the new expression should also be Integer* - assertEquals(createTypeFrom("Integer*", true), newExpression.type) + assertEquals(tu.parseType("Integer*", true), newExpression.type) // initializer should be a construct expression constructExpression = newExpression.initializer as? ConstructExpression assertNotNull(constructExpression) // type of the construct expression should be Integer - assertEquals(createTypeFrom("Integer", true), constructExpression.type) + assertEquals(tu.parseType("Integer", true), constructExpression.type) // argument should be named k and of type m val k = constructExpression.arguments[0] as DeclaredReferenceExpression assertLocalName("k", k) // type of the construct expression should also be Integer - assertEquals(createTypeFrom("int", true), k.type) + assertEquals(tu.parseType("int", true), k.type) } private val FunctionDeclaration.statements: List? @@ -1330,7 +1329,7 @@ internal class CXXLanguageFrontendTest : BaseTest() { val file = File("src/test/resources/c/typedef_in_header/main.c") val result = analyze(listOf(file), file.parentFile.toPath(), true) - val typedefs = result.scopeManager.currentTypedefs + val typedefs = result.finalCtx.scopeManager.currentTypedefs assertNotNull(typedefs) assertTrue(typedefs.isNotEmpty()) @@ -1548,7 +1547,4 @@ internal class CXXLanguageFrontendTest : BaseTest() { val tu = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) assertNotNull(tu) } - - private fun createTypeFrom(typename: String, resolveAlias: Boolean) = - TypeParser.createFrom(typename, CPPLanguage(), resolveAlias, null) } diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXLiteralTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXLiteralTest.kt index 74090fff97..f5580e1259 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXLiteralTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXLiteralTest.kt @@ -33,7 +33,6 @@ import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.statements.expressions.Literal import de.fraunhofer.aisec.cpg.graph.statements.expressions.UnaryOperator import de.fraunhofer.aisec.cpg.graph.types.Type -import de.fraunhofer.aisec.cpg.graph.types.TypeParser import java.io.File import java.math.BigInteger import kotlin.test.Test @@ -54,12 +53,12 @@ internal class CXXLiteralTest : BaseTest() { val funcDecl = zero.iterator().next() assertLocalName("zero", funcDecl) - assertLiteral(0, createTypeFrom("int"), funcDecl, "i") - assertLiteral(0L, createTypeFrom("long"), funcDecl, "l_with_suffix") - assertLiteral(0L, createTypeFrom("long long"), funcDecl, "l_long_long_with_suffix") + assertLiteral(0, tu.parseType("int"), funcDecl, "i") + assertLiteral(0L, tu.parseType("long"), funcDecl, "l_with_suffix") + assertLiteral(0L, tu.parseType("long long"), funcDecl, "l_long_long_with_suffix") assertLiteral( BigInteger.valueOf(0), - createTypeFrom("unsigned long long"), + tu.parseType("unsigned long long"), funcDecl, "l_unsigned_long_long_with_suffix" ) @@ -74,31 +73,31 @@ internal class CXXLiteralTest : BaseTest() { assertFalse(decimal.isEmpty()) val funcDecl = decimal.iterator().next() assertLocalName("decimal", funcDecl) - assertLiteral(42, createTypeFrom("int"), funcDecl, "i") - assertLiteral(1000, createTypeFrom("int"), funcDecl, "i_with_literal") - assertLiteral(9223372036854775807L, createTypeFrom("long"), funcDecl, "l") - assertLiteral(9223372036854775807L, createTypeFrom("long"), funcDecl, "l_with_suffix") + assertLiteral(42, tu.parseType("int"), funcDecl, "i") + assertLiteral(1000, tu.parseType("int"), funcDecl, "i_with_literal") + assertLiteral(9223372036854775807L, tu.parseType("long"), funcDecl, "l") + assertLiteral(9223372036854775807L, tu.parseType("long"), funcDecl, "l_with_suffix") assertLiteral( 9223372036854775807L, - createTypeFrom("long long"), + tu.parseType("long long"), funcDecl, "l_long_long_with_suffix" ) assertLiteral( BigInteger("9223372036854775809"), - createTypeFrom("unsigned long"), + tu.parseType("unsigned long"), funcDecl, "l_unsigned_long_with_suffix" ) assertLiteral( BigInteger("9223372036854775808"), - createTypeFrom("unsigned long long"), + tu.parseType("unsigned long long"), funcDecl, "l_long_long_implicit" ) assertLiteral( BigInteger("9223372036854775809"), - createTypeFrom("unsigned long long"), + tu.parseType("unsigned long long"), funcDecl, "l_unsigned_long_long_with_suffix" ) @@ -113,11 +112,11 @@ internal class CXXLiteralTest : BaseTest() { assertFalse(octal.isEmpty()) val funcDecl = octal.iterator().next() assertLocalName("octal", funcDecl) - assertLiteral(42, createTypeFrom("int"), funcDecl, "i") - assertLiteral(42L, createTypeFrom("long"), funcDecl, "l_with_suffix") + assertLiteral(42, tu.parseType("int"), funcDecl, "i") + assertLiteral(42L, tu.parseType("long"), funcDecl, "l_with_suffix") assertLiteral( BigInteger.valueOf(42), - createTypeFrom("unsigned long long"), + tu.parseType("unsigned long long"), funcDecl, "l_unsigned_long_long_with_suffix" ) @@ -133,11 +132,11 @@ internal class CXXLiteralTest : BaseTest() { assertFalse(hex.isEmpty()) val funcDecl = hex.iterator().next() assertLocalName("hex", funcDecl) - assertLiteral(42, createTypeFrom("int"), funcDecl, "i") - assertLiteral(42L, createTypeFrom("long"), funcDecl, "l_with_suffix") + assertLiteral(42, tu.parseType("int"), funcDecl, "i") + assertLiteral(42L, tu.parseType("long"), funcDecl, "l_with_suffix") assertLiteral( BigInteger.valueOf(42), - createTypeFrom("unsigned long long"), + tu.parseType("unsigned long long"), funcDecl, "l_unsigned_long_long_with_suffix" ) @@ -197,6 +196,4 @@ internal class CXXLiteralTest : BaseTest() { assertEquals(expectedType, literal.type) assertEquals(expectedValue, literal.value) } - - private fun createTypeFrom(typename: String) = TypeParser.createFrom(typename, CPPLanguage()) } diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXSymbolConfigurationTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXSymbolConfigurationTest.kt index 15b3afff40..39a4cb7465 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXSymbolConfigurationTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXSymbolConfigurationTest.kt @@ -25,11 +25,9 @@ */ package de.fraunhofer.aisec.cpg.frontends.cpp -import de.fraunhofer.aisec.cpg.BaseTest -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration -import de.fraunhofer.aisec.cpg.assertLocalName +import de.fraunhofer.aisec.cpg.* import de.fraunhofer.aisec.cpg.frontends.TranslationException +import de.fraunhofer.aisec.cpg.graph.TypeManager import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.statements.expressions.BinaryOperator import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression @@ -49,8 +47,11 @@ internal class CXXSymbolConfigurationTest : BaseTest() { val tu = CXXLanguageFrontend( CPPLanguage(), - TranslationConfiguration.builder().defaultPasses().build(), - ScopeManager(), + TranslationContext( + TranslationConfiguration.builder().build(), + ScopeManager(), + TypeManager() + ) ) .parse(File("src/test/resources/symbols.cpp")) val main = tu.getDeclarationsByName("main", FunctionDeclaration::class.java) @@ -79,20 +80,17 @@ internal class CXXSymbolConfigurationTest : BaseTest() { @Test @Throws(TranslationException::class) fun testWithSymbols() { + val config = + TranslationConfiguration.builder() + .symbols(mapOf(Pair("HELLO_WORLD", "\"Hello World\""), Pair("INCREASE(X)", "X+1"))) + .defaultPasses() + .build() + // let's try with symbol definitions val tu = CXXLanguageFrontend( CPPLanguage(), - TranslationConfiguration.builder() - .symbols( - mapOf( - Pair("HELLO_WORLD", "\"Hello World\""), - Pair("INCREASE(X)", "X+1") - ) - ) - .defaultPasses() - .build(), - ScopeManager(), + TranslationContext(config, ScopeManager(), TypeManager()) ) .parse(File("src/test/resources/symbols.cpp")) val main = tu.getDeclarationsByName("main", FunctionDeclaration::class.java) diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/FluentTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/FluentTest.kt index 0c08fddd10..470dbdde28 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/FluentTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/FluentTest.kt @@ -44,10 +44,9 @@ import kotlin.test.* class FluentTest { @Test fun test() { - val scopeManager = ScopeManager() val result = - TestLanguageFrontend(scopeManager).build { - translationResult(TranslationConfiguration.builder().build()) { + TestLanguageFrontend().build { + translationResult { translationUnit("file.cpp") { function("main", t("int")) { param("argc", t("int")) @@ -170,8 +169,7 @@ class FluentTest { assertNotNull(lit2.scope) assertEquals(2, lit2.value) - VariableUsageResolver(TranslationConfiguration.builder().build(), scopeManager) - .accept(result.components.first()) + VariableUsageResolver(result.finalCtx).accept(result.components.first()) // Now the reference should be resolved assertRefersTo(ref, variable) diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/AssignExpressionTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/AssignExpressionTest.kt index 8fe39f93b3..c9d96d6df7 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/AssignExpressionTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/AssignExpressionTest.kt @@ -25,9 +25,7 @@ */ package de.fraunhofer.aisec.cpg.graph.statements.expressions -import de.fraunhofer.aisec.cpg.TranslationConfiguration import de.fraunhofer.aisec.cpg.assertLocalName -import de.fraunhofer.aisec.cpg.frontends.TestLanguage import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.builder.function @@ -41,7 +39,7 @@ import kotlin.test.* class AssignExpressionTest { @Test fun propagateSimple() { - with(TestLanguage()) { + with(TestLanguageFrontend()) { val refA = newDeclaredReferenceExpression("a") val refB = newDeclaredReferenceExpression("b") @@ -65,7 +63,7 @@ class AssignExpressionTest { fun propagateTuple() { with(TestLanguageFrontend()) { val result = build { - translationResult(TranslationConfiguration.builder().build()) { + translationResult { translationUnit { val func = function( @@ -109,7 +107,7 @@ class AssignExpressionTest { assertLocalName("error", refErr.type) // Invoke the DFG pass - DFGPass(result.config, result.scopeManager).accept(result.components.first()) + DFGPass(ctx).accept(result.components.first()) assertTrue(refA.prevDFG.contains(call)) assertTrue(refErr.prevDFG.contains(call)) diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypePropagationTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypePropagationTest.kt index cf08b107ea..2faaebd0ab 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypePropagationTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypePropagationTest.kt @@ -25,8 +25,6 @@ */ package de.fraunhofer.aisec.cpg.graph.types -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.builder.* @@ -43,7 +41,7 @@ class TypePropagationTest { fun testBinopTypePropagation() { val result = TestLanguageFrontend().build { - translationResult(TranslationConfiguration.builder().build()) { + translationResult { translationUnit("test") { function("main", t("int")) { body { @@ -76,10 +74,9 @@ class TypePropagationTest { @Test fun testAssignTypePropagation() { // TODO: This test is related to issue 1071 (it models case 2). - val scopeManager = ScopeManager() val result = - TestLanguageFrontend(scopeManager).build { - translationResult(TranslationConfiguration.builder().build()) { + TestLanguageFrontend().build { + translationResult { translationUnit("test") { function("main", t("int")) { body { @@ -92,7 +89,7 @@ class TypePropagationTest { } } } - VariableUsageResolver(result.config, result.scopeManager).accept(result.components.first()) + VariableUsageResolver(result.finalCtx).accept(result.components.first()) val binaryOp = (result.functions["main"]?.body as? CompoundStatement)?.statements?.get(2) diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypeTests.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypeTests.kt index 96cb16f5e3..cc8da1792c 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypeTests.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypeTests.kt @@ -25,13 +25,12 @@ */ package de.fraunhofer.aisec.cpg.graph.types -import de.fraunhofer.aisec.cpg.BaseTest +import de.fraunhofer.aisec.cpg.* import de.fraunhofer.aisec.cpg.TestUtils.analyze import de.fraunhofer.aisec.cpg.TestUtils.analyzeAndGetFirstTU -import de.fraunhofer.aisec.cpg.TestUtils.disableTypeManagerCleanup import de.fraunhofer.aisec.cpg.TestUtils.findByUniqueName -import de.fraunhofer.aisec.cpg.TranslationResult import de.fraunhofer.aisec.cpg.frontends.cpp.CPPLanguage +import de.fraunhofer.aisec.cpg.frontends.cpp.CXXLanguageFrontend import de.fraunhofer.aisec.cpg.graph.* import java.nio.file.Path import java.util.* @@ -110,186 +109,208 @@ internal class TypeTests : BaseTest() { fun createFromCPP() { var result: Type - // Test 1: Function pointer - var typeString = "void (*single_param)(int)" - result = TypeParser.createFrom(typeString, CPPLanguage()) - val parameterList = - listOf(IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.SIGNED)) - var expected: Type = FunctionPointerType(parameterList, CPPLanguage(), IncompleteType()) - assertEquals(expected, result) - - // Test 1.1: interleaved brackets in function pointer - typeString = "void ((*single_param)(int))" - result = TypeParser.createFrom(typeString, CPPLanguage()) - assertEquals(result, expected) - - // Test 2: Stronger binding of brackets and pointer - typeString = "char (* const a)[]" - result = TypeParser.createFrom(typeString, CPPLanguage()) - expected = - PointerType( + with( + CXXLanguageFrontend( + CPPLanguage(), + TranslationContext( + TranslationConfiguration.builder().build(), + ScopeManager(), + TypeManager() + ) + ) + ) { + // Test 1: Function pointer + var typeString = "void (*single_param)(int)" + result = parseType(typeString) + val parameterList = + listOf(IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.SIGNED)) + var expected: Type = FunctionPointerType(parameterList, CPPLanguage(), IncompleteType()) + assertEquals(expected, result) + + // Test 1.1: interleaved brackets in function pointer + typeString = "void ((*single_param)(int))" + result = parseType(typeString) + assertEquals(result, expected) + + // Test 2: Stronger binding of brackets and pointer + typeString = "char (* const a)[]" + result = parseType(typeString) + expected = + PointerType( + PointerType( + IntegerType("char", 8, CPPLanguage(), NumericType.Modifier.NOT_APPLICABLE), + PointerType.PointerOrigin.ARRAY + ), + PointerType.PointerOrigin.POINTER + ) + assertEquals(expected, result) + + // Test 3: Mutable pointer to a mutable char + typeString = "char *p" + result = parseType(typeString) + expected = PointerType( IntegerType("char", 8, CPPLanguage(), NumericType.Modifier.NOT_APPLICABLE), - PointerType.PointerOrigin.ARRAY - ), - PointerType.PointerOrigin.POINTER - ) - assertEquals(expected, result) - - // Test 3: Mutable pointer to a mutable char - typeString = "char *p" - result = TypeParser.createFrom(typeString, CPPLanguage()) - expected = - PointerType( - IntegerType("char", 8, CPPLanguage(), NumericType.Modifier.NOT_APPLICABLE), - PointerType.PointerOrigin.POINTER - ) - assertEquals(expected, result) - - // Test 3.1: Different Whitespaces - typeString = "char* p" - result = TypeParser.createFrom(typeString, CPPLanguage()) - assertEquals(expected, result) - - // Test 3.2: Different Whitespaces - typeString = "char * p" - result = TypeParser.createFrom(typeString, CPPLanguage()) - assertEquals(expected, result) - - // Test 4: Mutable pointer to a constant char - typeString = "const char *p;" - result = TypeParser.createFrom(typeString, CPPLanguage()) - expected = - PointerType( - IntegerType("char", 8, CPPLanguage(), NumericType.Modifier.NOT_APPLICABLE), - PointerType.PointerOrigin.POINTER - ) - assertEquals(expected, result) - - // Test 5: Constant pointer to a mutable char - typeString = "char * const p;" - result = TypeParser.createFrom(typeString, CPPLanguage()) - expected = - PointerType( - IntegerType("char", 8, CPPLanguage(), NumericType.Modifier.NOT_APPLICABLE), - PointerType.PointerOrigin.POINTER - ) - assertEquals(expected, result) - - // Test 6: Constant pointer to a constant char - typeString = "const char * const p;" - result = TypeParser.createFrom(typeString, CPPLanguage()) - expected = - PointerType( - IntegerType("char", 8, CPPLanguage(), NumericType.Modifier.NOT_APPLICABLE), - PointerType.PointerOrigin.POINTER - ) - assertEquals(expected, result) + PointerType.PointerOrigin.POINTER + ) + assertEquals(expected, result) + + // Test 3.1: Different Whitespaces + typeString = "char* p" + result = parseType(typeString) + assertEquals(expected, result) + + // Test 3.2: Different Whitespaces + typeString = "char * p" + result = parseType(typeString) + assertEquals(expected, result) + + // Test 4: Mutable pointer to a constant char + typeString = "const char *p;" + result = parseType(typeString) + expected = + PointerType( + IntegerType("char", 8, CPPLanguage(), NumericType.Modifier.NOT_APPLICABLE), + PointerType.PointerOrigin.POINTER + ) + assertEquals(expected, result) - // Test 7: Array of const pointer to static const char - typeString = "static const char * const somearray []" - result = TypeParser.createFrom(typeString, CPPLanguage()) - expected = - PointerType( + // Test 5: Constant pointer to a mutable char + typeString = "char * const p;" + result = parseType(typeString) + expected = PointerType( IntegerType("char", 8, CPPLanguage(), NumericType.Modifier.NOT_APPLICABLE), PointerType.PointerOrigin.POINTER - ), - PointerType.PointerOrigin.ARRAY - ) - assertEquals(expected, result) + ) + assertEquals(expected, result) + + // Test 6: Constant pointer to a constant char + typeString = "const char * const p;" + result = parseType(typeString) + expected = + PointerType( + IntegerType("char", 8, CPPLanguage(), NumericType.Modifier.NOT_APPLICABLE), + PointerType.PointerOrigin.POINTER + ) + assertEquals(expected, result) - // Test 7.1: Array of array of pointer to static const char - typeString = "static const char * somearray[][]" - result = TypeParser.createFrom(typeString, CPPLanguage()) - expected = - PointerType( + // Test 7: Array of const pointer to static const char + typeString = "static const char * const somearray []" + result = parseType(typeString) + expected = PointerType( PointerType( IntegerType("char", 8, CPPLanguage(), NumericType.Modifier.NOT_APPLICABLE), PointerType.PointerOrigin.POINTER ), PointerType.PointerOrigin.ARRAY - ), - PointerType.PointerOrigin.ARRAY - ) - assertEquals(expected, result) - - // Test 8: Generics - typeString = "Array array" - result = TypeParser.createFrom(typeString, CPPLanguage()) - var generics = mutableListOf() - generics.add(IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.SIGNED)) - expected = ObjectType("Array", generics, false, CPPLanguage()) - assertEquals(expected, result) - - // Test 9: Compound Primitive Types - typeString = "long long int" - result = TypeParser.createFrom(typeString, CPPLanguage()) - expected = IntegerType("long long int", 64, CPPLanguage(), NumericType.Modifier.SIGNED) - assertEquals(expected, result) - - // Test 10: Unsigned/Signed Types - typeString = "unsigned int" - result = TypeParser.createFrom(typeString, CPPLanguage()) - expected = IntegerType("unsigned int", 32, CPPLanguage(), NumericType.Modifier.UNSIGNED) - assertEquals(expected, result) - typeString = "signed int" - result = TypeParser.createFrom(typeString, CPPLanguage()) - expected = IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.SIGNED) - assertEquals(expected, result) - typeString = "A a" - result = TypeParser.createFrom(typeString, CPPLanguage()) - expected = ObjectType("A", ArrayList(), false, CPPLanguage()) - assertEquals(expected, result) - - // Test 11: Unsigned + const + compound primitive Types - expected = - IntegerType("unsigned long long int", 64, CPPLanguage(), NumericType.Modifier.UNSIGNED) - typeString = "const unsigned long long int a = 1" - result = TypeParser.createFrom(typeString, CPPLanguage()) - assertEquals(expected, result) - - typeString = "unsigned const long long int b = 1" - result = TypeParser.createFrom(typeString, CPPLanguage()) - assertEquals(expected, result) - - typeString = "unsigned long const long int c = 1" - result = TypeParser.createFrom(typeString, CPPLanguage()) - assertEquals(expected, result) - - typeString = "unsigned long long const int d = 1" - result = TypeParser.createFrom(typeString, CPPLanguage()) - assertEquals(expected, result) - - typeString = "unsigned long long int const e = 1" - result = TypeParser.createFrom(typeString, CPPLanguage()) - assertEquals(expected, result) - - // Test 12: C++ Reference Types - typeString = "const int& ref = a" - result = TypeParser.createFrom(typeString, CPPLanguage()) - expected = ReferenceType(IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.SIGNED)) - assertEquals(expected, result) - - typeString = "int const &ref2 = a" - result = TypeParser.createFrom(typeString, CPPLanguage()) - assertEquals(expected, result) - - // Test 13: Elaborated Type in Generics - result = TypeParser.createFrom("Array", CPPLanguage()) - generics = ArrayList() - var generic = ObjectType("Node", ArrayList(), false, CPPLanguage()) - generics.add(generic) - expected = ObjectType("Array", generics, false, CPPLanguage()) - assertEquals(expected, result) - - result = TypeParser.createFrom("Array", CPPLanguage()) - generics = ArrayList() - generic = ObjectType("myclass", ArrayList(), false, CPPLanguage()) - generics.add(generic) - expected = ObjectType("Array", generics, false, CPPLanguage()) - assertEquals(expected, result) + ) + assertEquals(expected, result) + + // Test 7.1: Array of array of pointer to static const char + typeString = "static const char * somearray[][]" + result = parseType(typeString) + expected = + PointerType( + PointerType( + PointerType( + IntegerType( + "char", + 8, + CPPLanguage(), + NumericType.Modifier.NOT_APPLICABLE + ), + PointerType.PointerOrigin.POINTER + ), + PointerType.PointerOrigin.ARRAY + ), + PointerType.PointerOrigin.ARRAY + ) + assertEquals(expected, result) + + // Test 8: Generics + typeString = "Array array" + result = parseType(typeString) + var generics = mutableListOf() + generics.add(IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.SIGNED)) + expected = ObjectType("Array", generics, false, CPPLanguage()) + assertEquals(expected, result) + + // Test 9: Compound Primitive Types + typeString = "long long int" + result = parseType(typeString) + expected = IntegerType("long long int", 64, CPPLanguage(), NumericType.Modifier.SIGNED) + assertEquals(expected, result) + + // Test 10: Unsigned/Signed Types + typeString = "unsigned int" + result = parseType(typeString) + expected = IntegerType("unsigned int", 32, CPPLanguage(), NumericType.Modifier.UNSIGNED) + assertEquals(expected, result) + typeString = "signed int" + result = parseType(typeString) + expected = IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.SIGNED) + assertEquals(expected, result) + typeString = "A a" + result = parseType(typeString) + expected = ObjectType("A", ArrayList(), false, CPPLanguage()) + assertEquals(expected, result) + + // Test 11: Unsigned + const + compound primitive Types + expected = + IntegerType( + "unsigned long long int", + 64, + CPPLanguage(), + NumericType.Modifier.UNSIGNED + ) + typeString = "const unsigned long long int a = 1" + result = parseType(typeString) + assertEquals(expected, result) + + typeString = "unsigned const long long int b = 1" + result = parseType(typeString) + assertEquals(expected, result) + + typeString = "unsigned long const long int c = 1" + result = parseType(typeString) + assertEquals(expected, result) + + typeString = "unsigned long long const int d = 1" + result = parseType(typeString) + assertEquals(expected, result) + + typeString = "unsigned long long int const e = 1" + result = parseType(typeString) + assertEquals(expected, result) + + // Test 12: C++ Reference Types + typeString = "const int& ref = a" + result = parseType(typeString) + expected = + ReferenceType(IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.SIGNED)) + assertEquals(expected, result) + + typeString = "int const &ref2 = a" + result = parseType(typeString) + assertEquals(expected, result) + + // Test 13: Elaborated Type in Generics + result = parseType("Array") + generics = ArrayList() + var generic = ObjectType("Node", ArrayList(), false, CPPLanguage()) + generics.add(generic) + expected = ObjectType("Array", generics, false, CPPLanguage()) + assertEquals(expected, result) + + result = parseType("Array") + generics = ArrayList() + generic = ObjectType("myclass", ArrayList(), false, CPPLanguage()) + generics.add(generic) + expected = ObjectType("Array", generics, false, CPPLanguage()) + assertEquals(expected, result) + } } /** @@ -348,82 +369,100 @@ internal class TypeTests : BaseTest() { @Throws(Exception::class) @Test fun testCommonTypeTestCpp() { - disableTypeManagerCleanup() - val topLevel = Path.of("src", "test", "resources", "compiling", "hierarchy", "multistep") - val result = analyze("simple_inheritance.cpp", topLevel, true) - val root = TypeParser.createFrom("Root", CPPLanguage()) - val level0 = TypeParser.createFrom("Level0", CPPLanguage()) - val level1 = TypeParser.createFrom("Level1", CPPLanguage()) - val level1b = TypeParser.createFrom("Level1B", CPPLanguage()) - val level2 = TypeParser.createFrom("Level2", CPPLanguage()) - val unrelated = TypeParser.createFrom("Unrelated", CPPLanguage()) - getCommonTypeTestGeneral(root, level0, level1, level1b, level2, unrelated, result) + with( + CXXLanguageFrontend( + CPPLanguage(), + TranslationContext( + TranslationConfiguration.builder().build(), + ScopeManager(), + TypeManager() + ) + ) + ) { + val topLevel = + Path.of("src", "test", "resources", "compiling", "hierarchy", "multistep") + val result = analyze("simple_inheritance.cpp", topLevel, true) + val root = parseType("Root") + val level0 = parseType("Level0") + val level1 = parseType("Level1") + val level1b = parseType("Level1B") + val level2 = parseType("Level2") + val unrelated = parseType("Unrelated") + getCommonTypeTestGeneral(root, level0, level1, level1b, level2, unrelated, result) + } } // level2 and level2b have two intersections, both root and level0 -> level0 is lower @Throws(Exception::class) @Test fun testCommonTypeTestCppMultiInheritance() { - disableTypeManagerCleanup() - val topLevel = Path.of("src", "test", "resources", "compiling", "hierarchy", "multistep") - val result = analyze("multi_inheritance.cpp", topLevel, true) - - val root = TypeParser.createFrom("Root", CPPLanguage()) - val level0 = TypeParser.createFrom("Level0", CPPLanguage()) - val level0b = TypeParser.createFrom("Level0B", CPPLanguage()) - val level1 = TypeParser.createFrom("Level1", CPPLanguage()) - val level1b = TypeParser.createFrom("Level1B", CPPLanguage()) - val level1c = TypeParser.createFrom("Level1C", CPPLanguage()) - val level2 = TypeParser.createFrom("Level2", CPPLanguage()) - val level2b = TypeParser.createFrom("Level2B", CPPLanguage()) - - val provider = result.scopeManager - /* - Type hierarchy: - Root------------ - | | - Level0 Level0B | - / \ / \ | - Level1 Level1B Level1C - | \ / - Level2 Level2B - */ - // Root is the top, but unrelated to Level0B - for (t in listOf(root, level0, level1, level1b, level1c, level2, level2b)) { - assertEquals( - Optional.of(t), - TypeManager.getInstance().getCommonType(listOf(t), provider) + with( + CXXLanguageFrontend( + CPPLanguage(), + TranslationContext( + TranslationConfiguration.builder().build(), + ScopeManager(), + TypeManager() + ) ) - } - assertEquals( - Optional.empty(), - TypeManager.getInstance().getCommonType(listOf(root, level0b), provider) - ) - for (t in listOf(level0, level1, level2)) { + ) { + val topLevel = + Path.of("src", "test", "resources", "compiling", "hierarchy", "multistep") + val result = analyze("multi_inheritance.cpp", topLevel, true) + + val root = parseType("Root") + val level0 = parseType("Level0") + val level0b = parseType("Level0B") + val level1 = parseType("Level1") + val level1b = parseType("Level1B") + val level1c = parseType("Level1C") + val level2 = parseType("Level2") + val level2b = parseType("Level2B") + + val typeManager = result.finalCtx.typeManager + /* + Type hierarchy: + Root------------ + | | + Level0 Level0B | + / \ / \ | + Level1 Level1B Level1C + | \ / + Level2 Level2B + */ + // Root is the top, but unrelated to Level0B + for (t in listOf(root, level0, level1, level1b, level1c, level2, level2b)) { + assertEquals(Optional.of(t), typeManager.getCommonType(listOf(t), result.finalCtx)) + } assertEquals( Optional.empty(), - TypeManager.getInstance().getCommonType(listOf(t, level0b), provider) + typeManager.getCommonType(listOf(root, level0b), result.finalCtx) + ) + for (t in listOf(level0, level1, level2)) { + assertEquals( + Optional.empty(), + typeManager.getCommonType(listOf(t, level0b), result.finalCtx) + ) + } + assertEquals( + Optional.of(level0b), + typeManager.getCommonType(listOf(level1b, level1c), result.finalCtx) + ) + assertEquals( + Optional.of(level0), + typeManager.getCommonType(listOf(level1, level1b, level2, level2b), result.finalCtx) + ) + assertEquals( + Optional.of(root), + typeManager.getCommonType(listOf(level1, level1c), result.finalCtx) ) - } - assertEquals( - Optional.of(level0b), - TypeManager.getInstance().getCommonType(listOf(level1b, level1c), provider) - ) - assertEquals( - Optional.of(level0), - TypeManager.getInstance() - .getCommonType(listOf(level1, level1b, level2, level2b), provider) - ) - assertEquals( - Optional.of(root), - TypeManager.getInstance().getCommonType(listOf(level1, level1c), provider) - ) - // level2 and level2b have two intersections, both root and level0 -> level0 is lower - assertEquals( - Optional.of(level0), - TypeManager.getInstance().getCommonType(listOf(level2, level2b), provider) - ) + // level2 and level2b have two intersections, both root and level0 -> level0 is lower + assertEquals( + Optional.of(level0), + typeManager.getCommonType(listOf(level2, level2b), result.finalCtx) + ) + } } @Test @@ -467,21 +506,19 @@ internal class TypeTests : BaseTest() { | Level2 */ - val provider = result.scopeManager + val provider = result.finalCtx.scopeManager + val typeManager = result.finalCtx.typeManager // A single type is its own least common ancestor for (t in listOf(root, level0, level1, level1b, level2)) { - assertEquals( - Optional.of(t), - TypeManager.getInstance().getCommonType(listOf(t), provider) - ) + assertEquals(Optional.of(t), typeManager.getCommonType(listOf(t), result.finalCtx)) } // Root is the root of all types for (t in listOf(level0, level1, level1b, level2)) { assertEquals( Optional.of(root), - TypeManager.getInstance().getCommonType(listOf(t, root), provider) + typeManager.getCommonType(listOf(t, root), result.finalCtx) ) } @@ -489,33 +526,33 @@ internal class TypeTests : BaseTest() { for (t in listOf(level1, level1b, level2)) { assertEquals( Optional.of(level0), - TypeManager.getInstance().getCommonType(listOf(t, level0), provider) + typeManager.getCommonType(listOf(t, level0), result.finalCtx) ) } // Level1 and Level1B have Level0 as common ancestor assertEquals( Optional.of(level0), - TypeManager.getInstance().getCommonType(listOf(level1, level1b), provider) + typeManager.getCommonType(listOf(level1, level1b), result.finalCtx) ) // Level2 and Level1B have Level0 as common ancestor assertEquals( Optional.of(level0), - TypeManager.getInstance().getCommonType(listOf(level2, level1b), provider) + typeManager.getCommonType(listOf(level2, level1b), result.finalCtx) ) // Level1 and Level2 have Level1 as common ancestor assertEquals( Optional.of(level1), - TypeManager.getInstance().getCommonType(listOf(level1, level2), provider) + typeManager.getCommonType(listOf(level1, level2), result.finalCtx) ) // Check unrelated type behavior: No common root class for (t in listOf(root, level0, level1, level1b, level2)) { assertEquals( Optional.empty(), - TypeManager.getInstance().getCommonType(listOf(unrelated, t), provider) + typeManager.getCommonType(listOf(unrelated, t), result.finalCtx) ) } } diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypedefTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypedefTest.kt index 4b1acdd0de..c4d8e52cc1 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypedefTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypedefTest.kt @@ -29,7 +29,6 @@ import de.fraunhofer.aisec.cpg.BaseTest import de.fraunhofer.aisec.cpg.TestUtils.analyze import de.fraunhofer.aisec.cpg.TestUtils.findByUniqueName import de.fraunhofer.aisec.cpg.TestUtils.findByUniquePredicate -import de.fraunhofer.aisec.cpg.graph.TypeManager import de.fraunhofer.aisec.cpg.graph.declarations.ValueDeclaration import de.fraunhofer.aisec.cpg.graph.records import de.fraunhofer.aisec.cpg.graph.variables @@ -72,7 +71,7 @@ internal class TypedefTest : BaseTest() { assertEquals(NumericType.Modifier.UNSIGNED, returnType.modifier) assertEquals(uintfp1.type, uintfp2.type) - val typedefs = result.scopeManager.currentTypedefs + val typedefs = result.finalCtx.scopeManager.currentTypedefs val def = typedefs.stream().filter { it.alias.name.localName == "test" }.findAny().orElse(null) assertNotNull(def) @@ -83,6 +82,7 @@ internal class TypedefTest : BaseTest() { fun testWithModifier() { val result = analyze("cpp", topLevel, true) val variables = result.variables + val typeManager = result.finalCtx.typeManager // pointer val l1ptr = findByUniqueName(variables, "l1ptr") @@ -98,9 +98,9 @@ internal class TypedefTest : BaseTest() { val l2arr = findByUniqueName(variables, "l2arr") val l3arr = findByUniqueName(variables, "l3arr") val l4arr = findByUniqueName(variables, "l4arr") - assertTrue(TypeManager.getInstance().checkArrayAndPointer(l1arr.type, l2arr.type)) - assertTrue(TypeManager.getInstance().checkArrayAndPointer(l1arr.type, l3arr.type)) - assertTrue(TypeManager.getInstance().checkArrayAndPointer(l1arr.type, l4arr.type)) + assertTrue(typeManager.checkArrayAndPointer(l1arr.type, l2arr.type)) + assertTrue(typeManager.checkArrayAndPointer(l1arr.type, l3arr.type)) + assertTrue(typeManager.checkArrayAndPointer(l1arr.type, l4arr.type)) } @Test diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/CallResolverTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/CallResolverTest.kt index 13faeffb4a..2a5c0cdb0e 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/CallResolverTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/CallResolverTest.kt @@ -43,7 +43,6 @@ import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.CastExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.Literal import de.fraunhofer.aisec.cpg.graph.types.Type -import de.fraunhofer.aisec.cpg.graph.types.TypeParser import de.fraunhofer.aisec.cpg.graph.types.UnknownType import java.io.File import java.nio.file.Path @@ -154,9 +153,13 @@ class CallResolverTest : BaseTest() { topLevel, true ) + val tu = result.translationUnits.firstOrNull() + assertNotNull(tu) + val records = result.records - val intType = TypeParser.createFrom("int", CPPLanguage()) - val stringType = TypeParser.createFrom("char*", CPPLanguage()) + + val intType = tu.parseType("int") + val stringType = tu.parseType("char*") testMethods(records, intType, stringType) testOverriding(records) diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ReplaceTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ReplaceTest.kt index ddf9e1fee8..8270b09cf3 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ReplaceTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ReplaceTest.kt @@ -25,8 +25,8 @@ */ package de.fraunhofer.aisec.cpg.passes -import de.fraunhofer.aisec.cpg.ScopeManager import de.fraunhofer.aisec.cpg.TranslationConfiguration +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.StructTestLanguage import de.fraunhofer.aisec.cpg.frontends.TestLanguage import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend @@ -43,16 +43,12 @@ class ReplaceTest { override val frontend: KClass get() = ReplaceTestLanguageFrontend::class - override fun newFrontend( - config: TranslationConfiguration, - scopeManager: ScopeManager - ): TestLanguageFrontend { + override fun newFrontend(ctx: TranslationContext): TestLanguageFrontend { return ReplaceTestLanguageFrontend() } } - class ReplacedPass(config: TranslationConfiguration, scopeManager: ScopeManager) : - EvaluationOrderGraphPass(config, scopeManager) + class ReplacedPass(ctx: TranslationContext) : EvaluationOrderGraphPass(ctx) @Test fun testReplaceAnnotation() { diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/UnresolvedDFGPassTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/UnresolvedDFGPassTest.kt index 4d27c86679..72eb1dfee4 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/UnresolvedDFGPassTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/UnresolvedDFGPassTest.kt @@ -25,11 +25,11 @@ */ package de.fraunhofer.aisec.cpg.passes +import de.fraunhofer.aisec.cpg.GraphExamples.Companion.testFrontend import de.fraunhofer.aisec.cpg.InferenceConfiguration -import de.fraunhofer.aisec.cpg.ScopeManager import de.fraunhofer.aisec.cpg.TranslationConfiguration +import de.fraunhofer.aisec.cpg.TranslationResult import de.fraunhofer.aisec.cpg.frontends.TestLanguage -import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.builder.* import de.fraunhofer.aisec.cpg.graph.declarations.MethodDeclaration @@ -97,19 +97,19 @@ class UnresolvedDFGPassTest { companion object { - fun getDfgUnresolvedCalls(inferUnresolved: Boolean) = - TestLanguageFrontend(ScopeManager(), ".").build { - translationResult( - TranslationConfiguration.builder() - .defaultPasses() - .registerLanguage(TestLanguage(".")) - .inferenceConfiguration( - InferenceConfiguration.builder() - .inferDfgForUnresolvedCalls(inferUnresolved) - .build() - ) - .build() - ) { + fun getDfgUnresolvedCalls(inferUnresolved: Boolean): TranslationResult { + val config = + TranslationConfiguration.builder() + .defaultPasses() + .registerLanguage(TestLanguage(".")) + .inferenceConfiguration( + InferenceConfiguration.builder() + .inferDfgForUnresolvedCalls(inferUnresolved) + .build() + ) + .build() + return testFrontend(config).build { + translationResult { translationUnit("DfgUnresolvedCalls.java") { record("DfgUnresolvedCalls") { field("i", t("int")) { modifiers = listOf("private") } @@ -176,5 +176,6 @@ class UnresolvedDFGPassTest { } } } + } } } diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/scopes/ScopeManagerTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/scopes/ScopeManagerTest.kt index 6068a24d66..d6dbeb8ca7 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/scopes/ScopeManagerTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/scopes/ScopeManagerTest.kt @@ -25,10 +25,7 @@ */ package de.fraunhofer.aisec.cpg.passes.scopes -import de.fraunhofer.aisec.cpg.BaseTest -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration -import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend +import de.fraunhofer.aisec.cpg.* import de.fraunhofer.aisec.cpg.frontends.TranslationException import de.fraunhofer.aisec.cpg.frontends.cpp.CPPLanguage import de.fraunhofer.aisec.cpg.frontends.cpp.CXXLanguageFrontend @@ -48,21 +45,15 @@ internal class ScopeManagerTest : BaseTest() { config = TranslationConfiguration.builder().defaultPasses().build() } - @Test - @Throws(TranslationException::class) - fun testSetScope() { - val frontend: LanguageFrontend = CXXLanguageFrontend(CPPLanguage(), config, ScopeManager()) - assertEquals(frontend, frontend.scopeManager.lang) - - frontend.scopeManager = ScopeManager() - assertEquals(frontend, frontend.scopeManager.lang) - } - @Test @Throws(TranslationException::class) fun testReplaceNode() { val scopeManager = ScopeManager() - val frontend = CXXLanguageFrontend(CPPLanguage(), config, scopeManager) + val frontend = + CXXLanguageFrontend( + CPPLanguage(), + TranslationContext(config, scopeManager, TypeManager()) + ) val tu = frontend.parse(File("src/test/resources/cxx/recordstmt.cpp")) val methods = tu.allChildren().filter { it !is ConstructorDeclaration } assertFalse(methods.isEmpty()) @@ -86,13 +77,9 @@ internal class ScopeManagerTest : BaseTest() { @Test fun testMerge() { + val tm = TypeManager() val s1 = ScopeManager() - val frontend1 = - CXXLanguageFrontend( - CPPLanguage(), - TranslationConfiguration.builder().build(), - s1, - ) + val frontend1 = CXXLanguageFrontend(CPPLanguage(), TranslationContext(config, s1, tm)) s1.resetToGlobal(frontend1.newTranslationUnitDeclaration("f1.cpp", null)) // build a namespace declaration in f1.cpp with the namespace A @@ -103,12 +90,7 @@ internal class ScopeManagerTest : BaseTest() { s1.leaveScope(namespaceA1) val s2 = ScopeManager() - val frontend2 = - CXXLanguageFrontend( - CPPLanguage(), - TranslationConfiguration.builder().build(), - s2, - ) + val frontend2 = CXXLanguageFrontend(CPPLanguage(), TranslationContext(config, s2, tm)) s2.resetToGlobal(frontend2.newTranslationUnitDeclaration("f1.cpp", null)) // and do the same in the other file @@ -120,12 +102,7 @@ internal class ScopeManagerTest : BaseTest() { // merge the two scopes. this replicates the behaviour of parseParallel val final = ScopeManager() - val frontend = - CXXLanguageFrontend( - CPPLanguage(), - TranslationConfiguration.builder().build(), - final, - ) + val frontend = CXXLanguageFrontend(CPPLanguage(), TranslationContext(config, final, tm)) final.mergeFrom(listOf(s1, s2)) // in the final scope manager, there should only be one NameScope "A" @@ -163,11 +140,7 @@ internal class ScopeManagerTest : BaseTest() { fun testScopeFQN() { val s = ScopeManager() val frontend = - CXXLanguageFrontend( - CPPLanguage(), - TranslationConfiguration.builder().build(), - s, - ) + CXXLanguageFrontend(CPPLanguage(), TranslationContext(config, s, TypeManager())) s.resetToGlobal(frontend.newTranslationUnitDeclaration("file.cpp", null)) assertNull(s.currentNamespace) diff --git a/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/BaseTest.kt b/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/BaseTest.kt index 012b445b00..7baf5c8e67 100644 --- a/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/BaseTest.kt +++ b/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/BaseTest.kt @@ -25,21 +25,9 @@ */ package de.fraunhofer.aisec.cpg -import de.fraunhofer.aisec.cpg.graph.TypeManager -import de.fraunhofer.aisec.cpg.graph.types.TypeParser -import kotlin.test.BeforeTest import org.slf4j.Logger import org.slf4j.LoggerFactory abstract class BaseTest { protected var log: Logger = LoggerFactory.getLogger(this.javaClass) - - /** - * [TypeParser] and [TypeManager] hold static state. This needs to be cleared before all tests - * in order to avoid strange errors - */ - @BeforeTest - protected fun resetPersistentState() { - TypeManager.reset() - } } diff --git a/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/TestUtils.kt b/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/TestUtils.kt index 0e2907a606..d36e20e5b1 100644 --- a/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/TestUtils.kt +++ b/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/TestUtils.kt @@ -27,7 +27,6 @@ package de.fraunhofer.aisec.cpg import de.fraunhofer.aisec.cpg.frontends.CompilationDatabase import de.fraunhofer.aisec.cpg.graph.Node -import de.fraunhofer.aisec.cpg.graph.TypeManager import de.fraunhofer.aisec.cpg.graph.declarations.Declaration import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration @@ -39,8 +38,6 @@ import java.util.function.Consumer import java.util.function.Predicate import java.util.stream.Collectors import kotlin.test.* -import org.apache.commons.lang3.reflect.FieldUtils -import org.mockito.Mockito object TestUtils { @@ -191,13 +188,6 @@ object TestUtils { } } - @Throws(IllegalAccessException::class) - fun disableTypeManagerCleanup() { - val spy = Mockito.spy(TypeManager.getInstance()) - Mockito.doNothing().`when`(spy).cleanup() - FieldUtils.writeStaticField(TypeManager::class.java, "instance", spy, true) - } - /** * Compare the given parameter `toCompare` to the start- or end-line of the given node. If the * node has no location `false` is returned. `startLine` is used to specify if the start-line or diff --git a/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/frontends/TestLanguage.kt b/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/frontends/TestLanguage.kt index ce9ba9c91e..86d8459301 100644 --- a/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/frontends/TestLanguage.kt +++ b/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/frontends/TestLanguage.kt @@ -25,9 +25,9 @@ */ package de.fraunhofer.aisec.cpg.frontends -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration +import de.fraunhofer.aisec.cpg.* import de.fraunhofer.aisec.cpg.graph.Node +import de.fraunhofer.aisec.cpg.graph.TypeManager import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration import de.fraunhofer.aisec.cpg.graph.statements.expressions.ProblemExpression import de.fraunhofer.aisec.cpg.graph.types.FloatingPointType @@ -45,7 +45,7 @@ import kotlin.reflect.KClass */ open class TestLanguage(namespaceDelimiter: String = "::") : Language() { override val fileExtensions: List = listOf() - override val namespaceDelimiter: String + final override val namespaceDelimiter: String override val frontend: KClass = TestLanguageFrontend::class override val compoundAssignmentOperators = setOf("+=", "-=", "*=", "/=", "%=", "<<=", ">>=", "&=", "|=", "^=") @@ -66,11 +66,8 @@ open class TestLanguage(namespaceDelimiter: String = "::") : Language = TestLanguage(namespaceDelimiter) -) : - LanguageFrontend( - language, - TranslationConfiguration.builder().build(), - scopeManager, - ) { + language: Language = TestLanguage(namespaceDelimiter), + ctx: TranslationContext = + TranslationContext( + TranslationConfiguration.builder().build(), + ScopeManager(), + TypeManager() + ), +) : LanguageFrontend(language, ctx) { override fun parse(file: File): TranslationUnitDeclaration { TODO("Not yet implemented") } @@ -104,8 +101,5 @@ open class TestLanguageFrontend( } } -class TestHandler : - Handler( - Supplier { ProblemExpression() }, - TestLanguageFrontend() - ) +class TestHandler(frontend: TestLanguageFrontend) : + Handler(Supplier { ProblemExpression() }, frontend) diff --git a/cpg-language-go/src/main/golang/frontend/frontend.go b/cpg-language-go/src/main/golang/frontend/frontend.go index 4dbe97468f..f6d788f34a 100644 --- a/cpg-language-go/src/main/golang/frontend/frontend.go +++ b/cpg-language-go/src/main/golang/frontend/frontend.go @@ -133,6 +133,16 @@ func (g *GoLanguageFrontend) GetLanguage() (l *cpg.Language, err error) { return } +func (g *GoLanguageFrontend) GetCtx() (ctx *cpg.TranslationContext) { + ctx = new(cpg.TranslationContext) + err := g.ObjectRef.CallMethod(env, "getCtx", ctx) + if err != nil { + panic(err) + } + + return +} + func updateCode(fset *token.FileSet, node *cpg.Node, astNode ast.Node) { node.SetCode(code(fset, astNode)) } diff --git a/cpg-language-go/src/main/golang/frontend/handler.go b/cpg-language-go/src/main/golang/frontend/handler.go index 923ced3e45..163cea3897 100644 --- a/cpg-language-go/src/main/golang/frontend/handler.go +++ b/cpg-language-go/src/main/golang/frontend/handler.go @@ -1235,22 +1235,22 @@ func (this *GoLanguageFrontend) handleBasicLit(fset *token.FileSet, lit *ast.Bas case token.STRING: // strip the " value = cpg.NewString(lit.Value[1 : len(lit.Value)-1]) - t = cpg.TypeParser_createFrom("string", lang) + t = cpg.TypeParser_createFrom("string", lang, this.GetCtx()) case token.INT: i, _ := strconv.ParseInt(lit.Value, 10, 64) value = cpg.NewInteger(int(i)) - t = cpg.TypeParser_createFrom("int", lang) + t = cpg.TypeParser_createFrom("int", lang, this.GetCtx()) case token.FLOAT: // default seems to be float64 f, _ := strconv.ParseFloat(lit.Value, 64) value = cpg.NewDouble(f) - t = cpg.TypeParser_createFrom("float64", lang) + t = cpg.TypeParser_createFrom("float64", lang, this.GetCtx()) case token.IMAG: // TODO t = &cpg.UnknownType_getUnknown(lang).Type case token.CHAR: value = cpg.NewString(lit.Value) - t = cpg.TypeParser_createFrom("rune", lang) + t = cpg.TypeParser_createFrom("rune", lang, this.GetCtx()) break } @@ -1384,12 +1384,12 @@ func (this *GoLanguageFrontend) handleType(fset *token.FileSet, typeExpr ast.Exp this.LogTrace("fqn type: %s", name) } - return cpg.TypeParser_createFrom(name, lang) + return cpg.TypeParser_createFrom(name, lang, this.GetCtx()) case *ast.SelectorExpr: // small shortcut fqn := fmt.Sprintf("%s.%s", v.X.(*ast.Ident).Name, v.Sel.Name) this.LogTrace("FQN type: %s", fqn) - return cpg.TypeParser_createFrom(fqn, lang) + return cpg.TypeParser_createFrom(fqn, lang, this.GetCtx()) case *ast.StarExpr: t := this.handleType(fset, v.X) @@ -1417,7 +1417,7 @@ func (this *GoLanguageFrontend) handleType(fset *token.FileSet, typeExpr ast.Exp case *ast.MapType: // we cannot properly represent Golangs built-in map types, yet so we have // to make a shortcut here and represent it as a Java-like map type. - t := cpg.TypeParser_createFrom("map", lang) + t := cpg.TypeParser_createFrom("map", lang, this.GetCtx()) keyType := this.handleType(fset, v.Key) valueType := this.handleType(fset, v.Value) @@ -1428,7 +1428,7 @@ func (this *GoLanguageFrontend) handleType(fset *token.FileSet, typeExpr ast.Exp return t case *ast.ChanType: // handle them similar to maps - t := cpg.TypeParser_createFrom("chan", lang) + t := cpg.TypeParser_createFrom("chan", lang, this.GetCtx()) chanType := this.handleType(fset, v.Value) (*cpg.ObjectType)(t).AddGeneric(chanType) @@ -1484,7 +1484,7 @@ func (this *GoLanguageFrontend) handleType(fset *token.FileSet, typeExpr ast.Exp name += "}" - return cpg.TypeParser_createFrom(name, lang) + return cpg.TypeParser_createFrom(name, lang, this.GetCtx()) case *ast.IndexExpr: // This is a type with one type parameter. First we need to parse the "X" expression as a type var t = this.handleType(fset, v.X) diff --git a/cpg-language-go/src/main/golang/language.go b/cpg-language-go/src/main/golang/language.go index b0cb95bf1b..36d0d504a5 100644 --- a/cpg-language-go/src/main/golang/language.go +++ b/cpg-language-go/src/main/golang/language.go @@ -31,6 +31,8 @@ import ( type Language Node +const TranslationContextClass = CPGPackage + "/TranslationContext" + const FrontendsPackage = CPGPackage + "/frontends" const GolangPackage = FrontendsPackage + "/golang" const LanguageClass = FrontendsPackage + "/Language" @@ -53,3 +55,20 @@ func (l *Language) GetClassName() string { func (l *Language) IsArray() bool { return false } + +func (ctx *TranslationContext) ConvertToGo(o *jnigi.ObjectRef) error { + *ctx = (TranslationContext)(*o) + return nil +} + +func (ctx *TranslationContext) ConvertToJava() (obj *jnigi.ObjectRef, err error) { + return (*jnigi.ObjectRef)(ctx), nil +} + +func (*TranslationContext) GetClassName() string { + return TranslationContextClass +} + +func (*TranslationContext) IsArray() bool { + return false +} diff --git a/cpg-language-go/src/main/golang/types.go b/cpg-language-go/src/main/golang/types.go index 0c03ef5abd..fe1485c284 100644 --- a/cpg-language-go/src/main/golang/types.go +++ b/cpg-language-go/src/main/golang/types.go @@ -39,6 +39,8 @@ var env *jnigi.Env type Type struct{ *jnigi.ObjectRef } type ObjectType Type +type TranslationContext jnigi.ObjectRef + const TypesPackage = GraphPackage + "/types" const TypeClass = TypesPackage + "/Type" const ObjectTypeClass = TypesPackage + "/ObjectType" @@ -83,9 +85,9 @@ func InitEnv(e *jnigi.Env) { env = e } -func TypeParser_createFrom(s string, l *Language) *Type { +func TypeParser_createFrom(s string, l *Language, ctx *TranslationContext) *Type { var t Type - err := env.CallStaticMethod(TypeParserClass, "createFrom", &t, NewCharSequence(s), l) + err := env.CallStaticMethod(TypeParserClass, "createFrom", &t, NewString(s), l, false, ctx) if err != nil { log.Fatal(err) diff --git a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguage.kt b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguage.kt index 15af1a3147..eaa4dc6863 100644 --- a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguage.kt +++ b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguage.kt @@ -25,8 +25,6 @@ */ package de.fraunhofer.aisec.cpg.frontends.golang -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration import de.fraunhofer.aisec.cpg.frontends.HasGenerics import de.fraunhofer.aisec.cpg.frontends.HasShortCircuitOperators import de.fraunhofer.aisec.cpg.frontends.HasStructs @@ -110,11 +108,4 @@ class GoLanguage : // https://pkg.go.dev/builtin#string "string" to StringType("string", this) ) - - override fun newFrontend( - config: TranslationConfiguration, - scopeManager: ScopeManager, - ): GoLanguageFrontend { - return GoLanguageFrontend(this, config, scopeManager) - } } diff --git a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontend.kt b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontend.kt index 82e8ebafb3..a615d99d00 100644 --- a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontend.kt +++ b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontend.kt @@ -25,8 +25,7 @@ */ package de.fraunhofer.aisec.cpg.frontends.golang -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend import de.fraunhofer.aisec.cpg.frontends.SupportsParallelParsing @@ -40,11 +39,8 @@ import java.io.FileOutputStream @SupportsParallelParsing(false) @RegisterExtraPass(GoExtraPass::class) -class GoLanguageFrontend( - language: Language, - config: TranslationConfiguration, - scopeManager: ScopeManager, -) : LanguageFrontend(language, config, scopeManager) { +class GoLanguageFrontend(language: Language, ctx: TranslationContext) : + LanguageFrontend(language, ctx) { companion object { init { diff --git a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoExtraPass.kt b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoExtraPass.kt index 4709f809cd..c422085675 100644 --- a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoExtraPass.kt +++ b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoExtraPass.kt @@ -25,10 +25,7 @@ */ package de.fraunhofer.aisec.cpg.passes -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration -import de.fraunhofer.aisec.cpg.frontends.Language -import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.golang.GoLanguage import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.IncludeDeclaration @@ -43,7 +40,6 @@ import de.fraunhofer.aisec.cpg.graph.statements.expressions.CastExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.DeclaredReferenceExpression import de.fraunhofer.aisec.cpg.graph.types.PointerType import de.fraunhofer.aisec.cpg.graph.types.Type -import de.fraunhofer.aisec.cpg.graph.types.TypeParser import de.fraunhofer.aisec.cpg.graph.types.UnknownType import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker import de.fraunhofer.aisec.cpg.passes.inference.startInference @@ -108,8 +104,7 @@ import de.fraunhofer.aisec.cpg.passes.order.ExecuteBefore @ExecuteBefore(VariableUsageResolver::class) @ExecuteBefore(CallResolver::class) @ExecuteBefore(DFGPass::class) -class GoExtraPass(config: TranslationConfiguration, scopeManager: ScopeManager) : - ComponentPass(config, scopeManager), ScopeProvider { +class GoExtraPass(ctx: TranslationContext) : ComponentPass(ctx), ScopeProvider { override val scope: Scope? get() = scopeManager.currentScope @@ -148,7 +143,7 @@ class GoExtraPass(config: TranslationConfiguration, scopeManager: ScopeManager) // The key is the first variable. It is always an int val keyVariable = variable.declarations.firstOrNull() as? VariableDeclaration - keyVariable?.type = TypeParser.createFrom("int", forEach.language) + keyVariable?.type = forEach.parseType("int") // The value is the second one. Its type depends on the array type val valueVariable = @@ -215,7 +210,7 @@ class GoExtraPass(config: TranslationConfiguration, scopeManager: ScopeManager) if (namespace.isEmpty()) { scopeManager.globalScope ?.astNode - ?.startInference(scopeManager) + ?.startInference(ctx) ?.createInferredNamespaceDeclaration(include.name, include.filename) } } @@ -234,7 +229,7 @@ class GoExtraPass(config: TranslationConfiguration, scopeManager: ScopeManager) // First, check if this is a built-in type if (language.builtInTypes.contains(callee.name.toString())) { - replaceCallWithCast(callee.name.toString(), language, parent, call) + replaceCallWithCast(callee.name.toString(), parent, call) } else { // If not, then this could still refer to an existing type. We need to make sure // that we take the current namespace into account @@ -245,8 +240,8 @@ class GoExtraPass(config: TranslationConfiguration, scopeManager: ScopeManager) callee.name } - if (TypeManager.getInstance().typeExists(fqn.toString())) { - replaceCallWithCast(fqn, language, parent, call) + if (typeManager.typeExists(fqn.toString())) { + replaceCallWithCast(fqn, parent, call) } } } @@ -254,13 +249,12 @@ class GoExtraPass(config: TranslationConfiguration, scopeManager: ScopeManager) private fun replaceCallWithCast( typeName: CharSequence, - language: Language, parent: Node, call: CallExpression, ) { val cast = parent.newCastExpression(call.code) cast.location = call.location - cast.castType = TypeParser.createFrom(typeName, false, language) + cast.castType = call.parseType(typeName.toString()) cast.expression = call.arguments.single() if (parent !is ArgumentHolder) { diff --git a/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt b/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt index d9dd581779..c2dab38819 100644 --- a/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt +++ b/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt @@ -38,7 +38,6 @@ import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration import de.fraunhofer.aisec.cpg.graph.statements.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.* import de.fraunhofer.aisec.cpg.graph.types.FunctionType -import de.fraunhofer.aisec.cpg.graph.types.TypeParser import java.nio.file.Path import kotlin.test.* @@ -129,7 +128,7 @@ class GoLanguageFrontendTest : BaseTest() { assertNotNull(decl) val new = assertIs(decl.firstAssignment) - assertEquals(TypeParser.createFrom("p.MyStruct*", GoLanguage()), new.type) + assertEquals(tu.parseType("p.MyStruct*"), new.type) val construct = new.initializer as? ConstructExpression assertNotNull(construct) @@ -142,7 +141,7 @@ class GoLanguageFrontendTest : BaseTest() { var make = assertIs(decl.firstAssignment) assertNotNull(make) - assertEquals(TypeParser.createFrom("int[]", GoLanguage()), make.type) + assertEquals(tu.parseType("int[]"), make.type) assertTrue(make is ArrayCreationExpression) @@ -160,7 +159,7 @@ class GoLanguageFrontendTest : BaseTest() { assertTrue(make is ConstructExpression) // TODO: Maps can have dedicated types and parsing them as a generic here is only a // temporary solution. This should be fixed in the future. - assertEquals(TypeParser.createFrom("map[string,string]", GoLanguage()), make.type) + assertEquals(tu.parseType("map[string,string]"), make.type) // make channel @@ -170,7 +169,7 @@ class GoLanguageFrontendTest : BaseTest() { make = assertIs(decl.firstAssignment) assertNotNull(make) assertTrue(make is ConstructExpression) - assertEquals(TypeParser.createFrom("chan[int]", GoLanguage()), make.type) + assertEquals(tu.parseType("chan[int]"), make.type) } @Test @@ -191,26 +190,26 @@ class GoLanguageFrontendTest : BaseTest() { assertNotNull(a.location) assertLocalName("a", a) - assertEquals(TypeParser.createFrom("int", GoLanguage()), a.type) + assertEquals(tu.parseType("int"), a.type) val s = p.variables["s"] assertNotNull(s) assertLocalName("s", s) - assertEquals(TypeParser.createFrom("string", GoLanguage()), s.type) + assertEquals(tu.parseType("string"), s.type) val f = p.variables["f"] assertNotNull(f) assertLocalName("f", f) - assertEquals(TypeParser.createFrom("float64", GoLanguage()), f.type) + assertEquals(tu.parseType("float64"), f.type) val f32 = p.variables["f32"] assertNotNull(f32) assertLocalName("f32", f32) - assertEquals(TypeParser.createFrom("float32", GoLanguage()), f32.type) + assertEquals(tu.parseType("float32"), f32.type) val n = p.variables["n"] assertNotNull(n) - assertEquals(TypeParser.createFrom("int*", GoLanguage()), n.type) + assertEquals(tu.parseType("int*"), n.type) val nil = n.initializer as? Literal<*> assertNotNull(nil) @@ -276,7 +275,7 @@ class GoLanguageFrontendTest : BaseTest() { val s = myTest.parameters.first() assertNotNull(s) assertLocalName("s", s) - assertEquals(TypeParser.createFrom("string", GoLanguage()), s.type) + assertEquals(tu.parseType("string"), s.type) assertLocalName("myTest", myTest) @@ -293,7 +292,7 @@ class GoLanguageFrontendTest : BaseTest() { assertNotNull(literal) assertEquals("%s", literal.value) - assertEquals(TypeParser.createFrom("string", GoLanguage()), literal.type) + assertEquals(tu.parseType("string"), literal.type) val ref = callExpression.arguments[1] as? DeclaredReferenceExpression assertNotNull(ref) @@ -360,7 +359,7 @@ class GoLanguageFrontendTest : BaseTest() { val myField = fields.first() assertLocalName("MyField", myField) - assertEquals(TypeParser.createFrom("int", GoLanguage()), myField.type) + assertEquals(tu.parseType("int"), myField.type) val myInterface = p.getDeclarationsByName("p.MyInterface", RecordDeclaration::class.java) @@ -461,7 +460,7 @@ class GoLanguageFrontendTest : BaseTest() { assertNotNull(lhs) assertEquals(myFunc.receiver, (lhs.base as? DeclaredReferenceExpression)?.refersTo) assertLocalName("Field", lhs) - assertEquals(TypeParser.createFrom("int", GoLanguage()), lhs.type) + assertEquals(tu.parseType("int"), lhs.type) val rhs = assign.rhs.firstOrNull() as? DeclaredReferenceExpression assertNotNull(rhs) @@ -489,7 +488,7 @@ class GoLanguageFrontendTest : BaseTest() { assertNotNull(b) assertLocalName("b", b) - assertEquals(TypeParser.createFrom("bool", GoLanguage()), b.type) + assertEquals(tu.parseType("bool"), b.type) // true, false are builtin variables, NOT literals in Golang // we might need to parse this special case differently @@ -588,7 +587,7 @@ class GoLanguageFrontendTest : BaseTest() { assertNotNull(c) // type will be inferred from the function declaration - assertEquals(TypeParser.createFrom("p.MyStruct*", GoLanguage()), c.type) + assertEquals(tu.parseType("p.MyStruct*"), c.type) val newMyStruct = assertIs(c.firstAssignment) diff --git a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/DeclarationHandler.kt b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/DeclarationHandler.kt index 934d03b3b8..59900c62db 100644 --- a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/DeclarationHandler.kt +++ b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/DeclarationHandler.kt @@ -125,11 +125,10 @@ open class DeclarationHandler(lang: JavaLanguageFrontend) : ) for (parameter in methodDecl.parameters) { var resolvedType: Type? = - TypeManager.getInstance() - .getTypeParameter( - functionDeclaration.recordDeclaration, - parameter.type.toString() - ) + frontend.typeManager.getTypeParameter( + functionDeclaration.recordDeclaration, + parameter.type.toString() + ) if (resolvedType == null) { resolvedType = frontend.getTypeAsGoodAsPossible(parameter, parameter.resolve()) } @@ -196,11 +195,10 @@ open class DeclarationHandler(lang: JavaLanguageFrontend) : recordDeclaration.implementedInterfaces = classInterDecl.implementedTypes.map { type -> frontend.getTypeAsGoodAsPossible(type) } - TypeManager.getInstance() - .addTypeParameter( - recordDeclaration, - classInterDecl.typeParameters.map { ParameterizedType(it.nameAsString, language) } - ) + frontend.typeManager.addTypeParameter( + recordDeclaration, + classInterDecl.typeParameters.map { ParameterizedType(it.nameAsString, language) } + ) // TODO: I cannot replicate the old partionedBy logic val staticImports = @@ -334,11 +332,10 @@ open class DeclarationHandler(lang: JavaLanguageFrontend) : try { // Resolve type first with ParameterizedType type = - TypeManager.getInstance() - .getTypeParameter( - frontend.scopeManager.currentRecord, - variable.resolve().type.describe() - ) + frontend.typeManager.getTypeParameter( + frontend.scopeManager.currentRecord, + variable.resolve().type.describe() + ) ?: this.parseType(joinedModifiers + variable.resolve().type.describe()) } catch (e: UnsolvedSymbolException) { val t = frontend.recoverTypeFromUnsolvedException(e) diff --git a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/ExpressionHandler.kt b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/ExpressionHandler.kt index 42bed2495a..cacbab7f16 100644 --- a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/ExpressionHandler.kt +++ b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/ExpressionHandler.kt @@ -379,11 +379,10 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : try { val symbol = fieldAccessExpr.resolve() fieldType = - TypeManager.getInstance() - .getTypeParameter( - frontend.scopeManager.currentRecord, - symbol.asField().type.describe() - ) + frontend.typeManager.getTypeParameter( + frontend.scopeManager.currentRecord, + symbol.asField().type.describe() + ) if (fieldType == null) { fieldType = this.parseType(symbol.asField().type.describe()) } @@ -583,11 +582,10 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : } else { // Resolve type first with ParameterizedType var type: Type? = - TypeManager.getInstance() - .getTypeParameter( - frontend.scopeManager.currentRecord, - symbol.type.describe() - ) + frontend.typeManager.getTypeParameter( + frontend.scopeManager.currentRecord, + symbol.type.describe() + ) if (type == null) { type = this.parseType(symbol.type.describe()) } @@ -850,7 +848,7 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : frontend.scopeManager.enterScope(anonymousRecord) - anonymousRecord.addSuperClass(TypeParser.createFrom(constructorName, language)) + anonymousRecord.addSuperClass(parseType(constructorName)) val anonymousClassBody = objectCreationExpr.anonymousClassBody.get() for (classBody in anonymousClassBody) { // Whatever is implemented in the anonymous class has to be added to the record diff --git a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguage.kt b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguage.kt index 8e5de553e8..e932fdbc63 100644 --- a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguage.kt +++ b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguage.kt @@ -27,7 +27,6 @@ package de.fraunhofer.aisec.cpg.frontends.java import com.fasterxml.jackson.annotation.JsonIgnore import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration import de.fraunhofer.aisec.cpg.frontends.* import de.fraunhofer.aisec.cpg.graph.Name import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration @@ -107,13 +106,6 @@ open class JavaLanguage : } else super.propagateTypeOfBinaryOperation(operation) } - override fun newFrontend( - config: TranslationConfiguration, - scopeManager: ScopeManager, - ): JavaLanguageFrontend { - return JavaLanguageFrontend(this, config, scopeManager) - } - override fun handleSuperCall( callee: MemberExpression, curClass: RecordDeclaration, diff --git a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontend.kt b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontend.kt index 1aa4ea007d..049f92382e 100644 --- a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontend.kt +++ b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontend.kt @@ -45,8 +45,7 @@ import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver import com.github.javaparser.symbolsolver.resolution.typesolvers.JavaParserTypeSolver import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend import de.fraunhofer.aisec.cpg.frontends.TranslationException @@ -71,11 +70,8 @@ import java.util.function.Consumer @RegisterExtraPass( JavaExternalTypeHierarchyResolver::class ) // this pass is always required for Java -open class JavaLanguageFrontend( - language: Language, - config: TranslationConfiguration, - scopeManager: ScopeManager, -) : LanguageFrontend(language, config, scopeManager) { +open class JavaLanguageFrontend(language: Language, ctx: TranslationContext) : + LanguageFrontend(language, ctx) { var context: CompilationUnit? = null var javaSymbolResolver: JavaSymbolSolver? @@ -351,8 +347,10 @@ open class JavaLanguageFrontend( return try { // Resolve type first with ParameterizedType var type: de.fraunhofer.aisec.cpg.graph.types.Type? = - TypeManager.getInstance() - .getTypeParameter(scopeManager.currentRecord, resolved.returnType.describe()) + typeManager.getTypeParameter( + scopeManager.currentRecord, + resolved.returnType.describe() + ) if (type == null) { type = parseType(resolved.returnType.describe()) } diff --git a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/JavaCallResolverHelper.kt b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/JavaCallResolverHelper.kt index 9ef45aa1a5..13a334c34c 100644 --- a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/JavaCallResolverHelper.kt +++ b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/JavaCallResolverHelper.kt @@ -30,10 +30,10 @@ import de.fraunhofer.aisec.cpg.frontends.java.JavaLanguage import de.fraunhofer.aisec.cpg.graph.Name import de.fraunhofer.aisec.cpg.graph.declarations.MethodDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration +import de.fraunhofer.aisec.cpg.graph.parseType import de.fraunhofer.aisec.cpg.graph.statements.expressions.DeclaredReferenceExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.MemberCallExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.MemberExpression -import de.fraunhofer.aisec.cpg.graph.types.TypeParser import de.fraunhofer.aisec.cpg.helpers.Util import de.fraunhofer.aisec.cpg.passes.CallResolver.Companion.LOGGER @@ -111,9 +111,7 @@ class JavaCallResolverHelper { ): RecordDeclaration? { val baseName = callee.base.name.parent ?: return null - if ( - TypeParser.createFrom(baseName, curClass.language) in curClass.implementedInterfaces - ) { + if (curClass.parseType(baseName) in curClass.implementedInterfaces) { // Basename is an interface -> BaseName.super refers to BaseName itself return recordMap[baseName] } else { diff --git a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/JavaExternalTypeHierarchyResolver.kt b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/JavaExternalTypeHierarchyResolver.kt index 3d85b4b1d8..55834f49d1 100644 --- a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/JavaExternalTypeHierarchyResolver.kt +++ b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/JavaExternalTypeHierarchyResolver.kt @@ -29,13 +29,11 @@ import com.github.javaparser.resolution.UnsolvedSymbolException import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver import com.github.javaparser.symbolsolver.resolution.typesolvers.JavaParserTypeSolver import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration +import de.fraunhofer.aisec.cpg.TranslationContext +import de.fraunhofer.aisec.cpg.frontends.java.JavaLanguage import de.fraunhofer.aisec.cpg.frontends.java.JavaLanguageFrontend -import de.fraunhofer.aisec.cpg.graph.Component -import de.fraunhofer.aisec.cpg.graph.TypeManager +import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.types.Type -import de.fraunhofer.aisec.cpg.graph.types.TypeParser import de.fraunhofer.aisec.cpg.helpers.CommonPath import de.fraunhofer.aisec.cpg.passes.order.DependsOn import de.fraunhofer.aisec.cpg.passes.order.ExecuteBefore @@ -45,11 +43,13 @@ import org.slf4j.LoggerFactory @DependsOn(TypeHierarchyResolver::class) @ExecuteBefore(ImportResolver::class) @RequiredFrontend(JavaLanguageFrontend::class) -class JavaExternalTypeHierarchyResolver( - config: TranslationConfiguration, - scopeManager: ScopeManager -) : ComponentPass(config, scopeManager) { +class JavaExternalTypeHierarchyResolver(ctx: TranslationContext) : ComponentPass(ctx) { override fun accept(component: Component) { + val provider = + object : ContextProvider, LanguageProvider { + override val language = JavaLanguage() + override val ctx: TranslationContext = this@JavaExternalTypeHierarchyResolver.ctx + } val resolver = CombinedTypeSolver() resolver.add(ReflectionTypeSolver()) @@ -67,10 +67,8 @@ class JavaExternalTypeHierarchyResolver( resolver.add(JavaParserTypeSolver(root)) } - val tm = TypeManager.getInstance() - // Iterate over all known types and add their (direct) supertypes. - for (t in HashSet(tm.firstOrderTypes)) { + for (t in HashSet(typeManager.firstOrderTypes)) { // TODO: Do we have to check if the type's language is JavaLanguage? val symbol = resolver.tryToSolveType(t.typeName) if (symbol.isSolved) { @@ -78,7 +76,7 @@ class JavaExternalTypeHierarchyResolver( val resolvedSuperTypes = symbol.correspondingDeclaration.getAncestors(true) for (anc in resolvedSuperTypes) { // Add all resolved supertypes to the type. - val superType = TypeParser.createFrom(anc.qualifiedName, t.language) + val superType = provider.parseType(anc.qualifiedName) superType.typeOrigin = Type.Origin.RESOLVED t.superTypes.add(superType) } diff --git a/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontendTest.kt b/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontendTest.kt index 6ecb9932d2..5f9a5b5810 100644 --- a/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontendTest.kt +++ b/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontendTest.kt @@ -27,7 +27,6 @@ package de.fraunhofer.aisec.cpg.frontends.java import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration import de.fraunhofer.aisec.cpg.* -import de.fraunhofer.aisec.cpg.ScopeManager import de.fraunhofer.aisec.cpg.TestUtils.analyzeAndGetFirstTU import de.fraunhofer.aisec.cpg.TestUtils.analyzeWithBuilder import de.fraunhofer.aisec.cpg.TestUtils.findByUniqueName @@ -38,7 +37,6 @@ import de.fraunhofer.aisec.cpg.graph.declarations.* import de.fraunhofer.aisec.cpg.graph.statements.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.* import de.fraunhofer.aisec.cpg.graph.types.FunctionType -import de.fraunhofer.aisec.cpg.graph.types.TypeParser import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker import de.fraunhofer.aisec.cpg.sarif.Region import java.io.File @@ -144,7 +142,7 @@ internal class JavaLanguageFrontendTest : BaseTest() { val sDecl = s.singleDeclaration as? VariableDeclaration assertNotNull(sDecl) assertLocalName("s", sDecl) - assertEquals(createTypeFrom("java.lang.String"), sDecl.type) + assertEquals(tu.parseType("java.lang.String"), sDecl.type) // should contain a single statement val sce = forEachStatement.statement as? MemberCallExpression @@ -191,11 +189,11 @@ internal class JavaLanguageFrontendTest : BaseTest() { assertNotNull(scope) // first exception type was? resolved, so we can expect a FQN - assertEquals(createTypeFrom("java.lang.NumberFormatException"), firstCatch.parameter?.type) + assertEquals(tu.parseType("java.lang.NumberFormatException"), firstCatch.parameter?.type) // second one could not be resolved so we do not have an FQN - assertEquals(createTypeFrom("NotResolvableTypeException"), catchClauses[1].parameter?.type) + assertEquals(tu.parseType("NotResolvableTypeException"), catchClauses[1].parameter?.type) // third type should have been resolved through the import - assertEquals(createTypeFrom("some.ImportedException"), (catchClauses[2].parameter)?.type) + assertEquals(tu.parseType("some.ImportedException"), (catchClauses[2].parameter)?.type) // and 1 finally val finallyBlock = tryStatement.finallyBlock @@ -285,14 +283,14 @@ internal class JavaLanguageFrontendTest : BaseTest() { @Test fun testRecordDeclaration() { val file = File("src/test/resources/compiling/RecordDeclaration.java") - val declaration = + val tu = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { it.registerLanguage(JavaLanguage()) } // TODO: Use GraphExamples here as well. - assertNotNull(declaration) + assertNotNull(tu) - val namespaceDeclaration = declaration.getDeclarationAs(0, NamespaceDeclaration::class.java) + val namespaceDeclaration = tu.getDeclarationAs(0, NamespaceDeclaration::class.java) val recordDeclaration = namespaceDeclaration?.getDeclarationAs(0, RecordDeclaration::class.java) @@ -305,7 +303,7 @@ internal class JavaLanguageFrontendTest : BaseTest() { assertNotNull(method) assertEquals(recordDeclaration, method.recordDeclaration) assertLocalName("method", method) - assertEquals(createTypeFrom("java.lang.Integer"), method.returnTypes.firstOrNull()) + assertEquals(tu.parseType("java.lang.Integer"), method.returnTypes.firstOrNull()) val functionType = method.type as? FunctionType assertNotNull(functionType) @@ -427,7 +425,7 @@ internal class JavaLanguageFrontendTest : BaseTest() { assertNotNull(a) // type should be Integer[] - assertEquals(createTypeFrom("int[]"), a.type) + assertEquals(tu.parseType("int[]"), a.type) // it has an array creation initializer val ace = a.initializer as? ArrayCreationExpression @@ -635,17 +633,14 @@ internal class JavaLanguageFrontendTest : BaseTest() { (lhs?.base as? DeclaredReferenceExpression)?.refersTo as? VariableDeclaration? assertNotNull(receiver) assertLocalName("this", receiver) - assertEquals(createTypeFrom("my.Animal"), receiver.type) + assertEquals(tu.parseType("my.Animal"), receiver.type) } @Test fun testOverrideHandler() { /** A simple extension of the [JavaLanguageFrontend] to demonstrate handler overriding. */ - class MyJavaLanguageFrontend( - language: JavaLanguage, - config: TranslationConfiguration, - scopeManager: ScopeManager, - ) : JavaLanguageFrontend(language, config, scopeManager) { + class MyJavaLanguageFrontend(language: JavaLanguage, ctx: TranslationContext) : + JavaLanguageFrontend(language, ctx) { init { this.declarationHandler = object : DeclarationHandler(this@MyJavaLanguageFrontend) { @@ -673,13 +668,6 @@ internal class JavaLanguageFrontendTest : BaseTest() { override val namespaceDelimiter = "." override val superClassKeyword = "super" override val frontend = MyJavaLanguageFrontend::class - - override fun newFrontend( - config: TranslationConfiguration, - scopeManager: ScopeManager, - ): MyJavaLanguageFrontend { - return MyJavaLanguageFrontend(this, config, scopeManager) - } } val file = File("src/test/resources/compiling/RecordDeclaration.java") @@ -786,8 +774,6 @@ internal class JavaLanguageFrontendTest : BaseTest() { assertSame(ref, thisOuterClass) } - private fun createTypeFrom(typename: String) = TypeParser.createFrom(typename, JavaLanguage()) - @Test fun testForEach() { val file = File("src/test/resources/compiling/ForEach.java") diff --git a/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypeTests.kt b/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypeTests.kt index 97d6e6aff2..65ebf1fbf3 100644 --- a/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypeTests.kt +++ b/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypeTests.kt @@ -25,14 +25,12 @@ */ package de.fraunhofer.aisec.cpg.graph.types -import de.fraunhofer.aisec.cpg.BaseTest +import de.fraunhofer.aisec.cpg.* import de.fraunhofer.aisec.cpg.TestUtils.analyze -import de.fraunhofer.aisec.cpg.TestUtils.disableTypeManagerCleanup import de.fraunhofer.aisec.cpg.TestUtils.findByName import de.fraunhofer.aisec.cpg.TestUtils.findByUniqueName -import de.fraunhofer.aisec.cpg.TranslationResult -import de.fraunhofer.aisec.cpg.assertLocalName import de.fraunhofer.aisec.cpg.frontends.java.JavaLanguage +import de.fraunhofer.aisec.cpg.frontends.java.JavaLanguageFrontend import de.fraunhofer.aisec.cpg.graph.* import java.nio.file.Path import java.util.* @@ -45,78 +43,89 @@ internal class TypeTests : BaseTest() { var result: Type var expected: Type - // Test 1: Ignore Access Modifier Keyword (public, private, protected) - var typeString = "private int a" - result = TypeParser.createFrom(typeString, JavaLanguage()) - expected = IntegerType("int", 32, JavaLanguage(), NumericType.Modifier.SIGNED) - assertEquals(expected, result) - - // Test 2: constant type using final - typeString = "final int a" - result = TypeParser.createFrom(typeString, JavaLanguage()) - expected = IntegerType("int", 32, JavaLanguage(), NumericType.Modifier.SIGNED) - assertEquals(expected, result) - - // Test 3: static type - typeString = "static int a" - result = TypeParser.createFrom(typeString, JavaLanguage()) - expected = IntegerType("int", 32, JavaLanguage(), NumericType.Modifier.SIGNED) - assertEquals(expected, result) - - // Test 4: volatile type - typeString = "public volatile int a" - result = TypeParser.createFrom(typeString, JavaLanguage()) - expected = IntegerType("int", 32, JavaLanguage(), NumericType.Modifier.SIGNED) - assertEquals(expected, result) - - // Test 5: combining a storage type and a qualifier - typeString = "private static final String a" - result = TypeParser.createFrom(typeString, JavaLanguage()) - expected = StringType("java.lang.String", JavaLanguage()) - assertEquals(expected, result) - - // Test 6: using two different qualifiers - typeString = "public final volatile int a" - result = TypeParser.createFrom(typeString, JavaLanguage()) - expected = IntegerType("int", 32, JavaLanguage(), NumericType.Modifier.SIGNED) - assertEquals(expected, result) - - // Test 7: Reference level using arrays - typeString = "int[] a" - result = TypeParser.createFrom(typeString, JavaLanguage()) - expected = - PointerType( - IntegerType("int", 32, JavaLanguage(), NumericType.Modifier.SIGNED), - PointerType.PointerOrigin.ARRAY + with( + JavaLanguageFrontend( + JavaLanguage(), + TranslationContext( + TranslationConfiguration.builder().build(), + ScopeManager(), + TypeManager() + ) ) - assertEquals(expected, result) - - // Test 8: generics - typeString = "List list" - result = TypeParser.createFrom(typeString, JavaLanguage()) - var generics = mutableListOf() - generics.add(StringType("java.lang.String", JavaLanguage())) - expected = ObjectType("List", generics, false, JavaLanguage()) - assertEquals(expected, result) - - // Test 9: more generics - typeString = "List>, List> data" - result = TypeParser.createFrom(typeString, JavaLanguage()) - val genericStringType = StringType("java.lang.String", JavaLanguage()) - val generics3: MutableList = ArrayList() - generics3.add(genericStringType) - val genericElement3 = ObjectType("List", generics3, false, JavaLanguage()) - val generics2a: MutableList = ArrayList() - generics2a.add(genericElement3) - val generics2b: MutableList = ArrayList() - generics2b.add(genericStringType) - val genericElement1 = ObjectType("List", generics2a, false, JavaLanguage()) - val genericElement2 = ObjectType("List", generics2b, false, JavaLanguage()) - generics = ArrayList() - generics.add(genericElement1) - generics.add(genericElement2) - expected = ObjectType("List", generics, false, JavaLanguage()) - assertEquals(expected, result) + ) { + // Test 1: Ignore Access Modifier Keyword (public, private, protected) + var typeString = "private int a" + result = parseType(typeString) + expected = IntegerType("int", 32, JavaLanguage(), NumericType.Modifier.SIGNED) + assertEquals(expected, result) + + // Test 2: constant type using final + typeString = "final int a" + result = parseType(typeString) + expected = IntegerType("int", 32, JavaLanguage(), NumericType.Modifier.SIGNED) + assertEquals(expected, result) + + // Test 3: static type + typeString = "static int a" + result = parseType(typeString) + expected = IntegerType("int", 32, JavaLanguage(), NumericType.Modifier.SIGNED) + assertEquals(expected, result) + + // Test 4: volatile type + typeString = "public volatile int a" + result = parseType(typeString) + expected = IntegerType("int", 32, JavaLanguage(), NumericType.Modifier.SIGNED) + assertEquals(expected, result) + + // Test 5: combining a storage type and a qualifier + typeString = "private static final String a" + result = parseType(typeString) + expected = StringType("java.lang.String", JavaLanguage()) + assertEquals(expected, result) + + // Test 6: using two different qualifiers + typeString = "public final volatile int a" + result = parseType(typeString) + expected = IntegerType("int", 32, JavaLanguage(), NumericType.Modifier.SIGNED) + assertEquals(expected, result) + + // Test 7: Reference level using arrays + typeString = "int[] a" + result = parseType(typeString) + expected = + PointerType( + IntegerType("int", 32, JavaLanguage(), NumericType.Modifier.SIGNED), + PointerType.PointerOrigin.ARRAY + ) + assertEquals(expected, result) + + // Test 8: generics + typeString = "List list" + result = parseType(typeString) + var generics = mutableListOf() + generics.add(StringType("java.lang.String", JavaLanguage())) + expected = ObjectType("List", generics, false, JavaLanguage()) + assertEquals(expected, result) + + // Test 9: more generics + typeString = "List>, List> data" + result = parseType(typeString) + val genericStringType = StringType("java.lang.String", JavaLanguage()) + val generics3: MutableList = ArrayList() + generics3.add(genericStringType) + val genericElement3 = ObjectType("List", generics3, false, JavaLanguage()) + val generics2a: MutableList = ArrayList() + generics2a.add(genericElement3) + val generics2b: MutableList = ArrayList() + generics2b.add(genericStringType) + val genericElement1 = ObjectType("List", generics2a, false, JavaLanguage()) + val genericElement2 = ObjectType("List", generics2b, false, JavaLanguage()) + generics = ArrayList() + generics.add(genericElement1) + generics.add(genericElement2) + expected = ObjectType("List", generics, false, JavaLanguage()) + assertEquals(expected, result) + } } // Tests on the resulting graph @@ -127,12 +136,17 @@ internal class TypeTests : BaseTest() { val result = analyze("java", topLevel, true) { it.registerLanguage(JavaLanguage()) } // Check Parameterized + val recordDeclarations = result.records + val recordDeclarationBox = findByUniqueName(recordDeclarations, "Box") + val typeT = result.finalCtx.typeManager.getTypeParameter(recordDeclarationBox, "T") + assertNotNull(typeT) + assertIs(typeT) + assertLocalName("T", typeT) + assertEquals(typeT, result.finalCtx.typeManager.getTypeParameter(recordDeclarationBox, "T")) + // Type of field t val fieldDeclarations = result.fields val fieldDeclarationT = findByUniqueName(fieldDeclarations, "t") - val typeT = fieldDeclarationT.type - assertIs(typeT) - assertLocalName("T", typeT) assertTrue(fieldDeclarationT.possibleSubTypes.contains(typeT)) // Parameter of set Method @@ -198,16 +212,34 @@ internal class TypeTests : BaseTest() { @Throws(Exception::class) @Test fun testCommonTypeTestJava() { - disableTypeManagerCleanup() - val topLevel = Path.of("src", "test", "resources", "compiling", "hierarchy") - val result = analyze("java", topLevel, true) { it.registerLanguage(JavaLanguage()) } - val root = TypeParser.createFrom("multistep.Root", JavaLanguage()) - val level0 = TypeParser.createFrom("multistep.Level0", JavaLanguage()) - val level1 = TypeParser.createFrom("multistep.Level1", JavaLanguage()) - val level1b = TypeParser.createFrom("multistep.Level1B", JavaLanguage()) - val level2 = TypeParser.createFrom("multistep.Level2", JavaLanguage()) - val unrelated = TypeParser.createFrom("multistep.Unrelated", JavaLanguage()) - getCommonTypeTestGeneral(root, level0, level1, level1b, level2, unrelated, result) + with( + JavaLanguageFrontend( + JavaLanguage(), + TranslationContext( + TranslationConfiguration.builder().build(), + ScopeManager(), + TypeManager() + ) + ) + ) { + val topLevel = Path.of("src", "test", "resources", "compiling", "hierarchy") + val result = analyze("java", topLevel, true) { it.registerLanguage(JavaLanguage()) } + val root = parseType("multistep.Root") + val level0 = parseType("multistep.Level0") + val level1 = parseType("multistep.Level1") + val level1b = parseType("multistep.Level1B") + val level2 = parseType("multistep.Level2") + val unrelated = parseType("multistep.Unrelated") + getCommonTypeTestGeneral( + root, + level0, + level1, + level1b, + level2, + unrelated, + result.finalCtx + ) + } } private fun getCommonTypeTestGeneral( @@ -217,7 +249,7 @@ internal class TypeTests : BaseTest() { level1b: Type, level2: Type, unrelated: Type, - result: TranslationResult + ctx: TranslationContext ) { /* Type hierarchy: @@ -229,56 +261,44 @@ internal class TypeTests : BaseTest() { | Level2 */ - val provider = result.scopeManager + val provider = ctx.scopeManager // A single type is its own least common ancestor for (t in listOf(root, level0, level1, level1b, level2)) { - assertEquals( - Optional.of(t), - TypeManager.getInstance().getCommonType(listOf(t), provider) - ) + assertEquals(Optional.of(t), ctx.typeManager.getCommonType(listOf(t), ctx)) } // Root is the root of all types for (t in listOf(level0, level1, level1b, level2)) { - assertEquals( - Optional.of(root), - TypeManager.getInstance().getCommonType(listOf(t, root), provider) - ) + assertEquals(Optional.of(root), ctx.typeManager.getCommonType(listOf(t, root), ctx)) } // Level0 is above all types but Root for (t in listOf(level1, level1b, level2)) { - assertEquals( - Optional.of(level0), - TypeManager.getInstance().getCommonType(listOf(t, level0), provider) - ) + assertEquals(Optional.of(level0), ctx.typeManager.getCommonType(listOf(t, level0), ctx)) } // Level1 and Level1B have Level0 as common ancestor assertEquals( Optional.of(level0), - TypeManager.getInstance().getCommonType(listOf(level1, level1b), provider) + ctx.typeManager.getCommonType(listOf(level1, level1b), ctx) ) // Level2 and Level1B have Level0 as common ancestor assertEquals( Optional.of(level0), - TypeManager.getInstance().getCommonType(listOf(level2, level1b), provider) + ctx.typeManager.getCommonType(listOf(level2, level1b), ctx) ) // Level1 and Level2 have Level1 as common ancestor assertEquals( Optional.of(level1), - TypeManager.getInstance().getCommonType(listOf(level1, level2), provider) + ctx.typeManager.getCommonType(listOf(level1, level2), ctx) ) // Check unrelated type behavior: No common root class for (t in listOf(root, level0, level1, level1b, level2)) { - assertEquals( - Optional.empty(), - TypeManager.getInstance().getCommonType(listOf(unrelated, t), provider) - ) + assertEquals(Optional.empty(), ctx.typeManager.getCommonType(listOf(unrelated, t), ctx)) } } } diff --git a/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/CallResolverTest.kt b/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/CallResolverTest.kt index ec5fbbb98a..8bd719e38c 100644 --- a/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/CallResolverTest.kt +++ b/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/CallResolverTest.kt @@ -36,7 +36,6 @@ import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression import de.fraunhofer.aisec.cpg.graph.types.Type -import de.fraunhofer.aisec.cpg.graph.types.TypeParser import java.nio.file.Path import kotlin.test.* @@ -121,9 +120,12 @@ class CallResolverTest : BaseTest() { fun testJava() { val result = TestUtils.analyze("java", topLevel, true) { it.registerLanguage(JavaLanguage()) } + val tu = result.translationUnits.firstOrNull() + assertNotNull(tu) + val records = result.records - val intType = TypeParser.createFrom("int", JavaLanguage()) - val stringType = TypeParser.createFrom("java.lang.String", JavaLanguage()) + val intType = tu.parseType("int") + val stringType = tu.parseType(("java.lang.String")) testMethods(records, intType, stringType) testOverriding(records) ensureNoUnknownClassDummies(records) diff --git a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguage.kt b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguage.kt index 0106a8f49d..abb40b6366 100644 --- a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguage.kt +++ b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguage.kt @@ -25,8 +25,6 @@ */ package de.fraunhofer.aisec.cpg.frontends.llvm -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.graph.types.FloatingPointType import de.fraunhofer.aisec.cpg.graph.types.IntegerType @@ -60,11 +58,4 @@ class LLVMIRLanguage : Language() { "x86_fp80" to FloatingPointType("x86_fp80", 80, this, NumericType.Modifier.SIGNED), "ppc_fp128" to FloatingPointType("ppc_fp128", 128, this, NumericType.Modifier.SIGNED), ) - - override fun newFrontend( - config: TranslationConfiguration, - scopeManager: ScopeManager, - ): LLVMIRLanguageFrontend { - return LLVMIRLanguageFrontend(this, config, scopeManager) - } } diff --git a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontend.kt b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontend.kt index ea3591d8c9..9eb7bf5080 100644 --- a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontend.kt +++ b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontend.kt @@ -25,14 +25,14 @@ */ package de.fraunhofer.aisec.cpg.frontends.llvm -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend import de.fraunhofer.aisec.cpg.frontends.TranslationException import de.fraunhofer.aisec.cpg.graph.declarations.Declaration import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration +import de.fraunhofer.aisec.cpg.graph.newTranslationUnitDeclaration import de.fraunhofer.aisec.cpg.graph.newUnknownType import de.fraunhofer.aisec.cpg.graph.parseType import de.fraunhofer.aisec.cpg.graph.statements.expressions.DeclaredReferenceExpression @@ -51,11 +51,8 @@ import org.bytedeco.llvm.LLVM.* import org.bytedeco.llvm.global.LLVM.* @RegisterExtraPass(CompressLLVMPass::class) -class LLVMIRLanguageFrontend( - language: Language, - config: TranslationConfiguration, - scopeManager: ScopeManager, -) : LanguageFrontend(language, config, scopeManager) { +class LLVMIRLanguageFrontend(language: Language, ctx: TranslationContext) : + LanguageFrontend(language, ctx) { val statementHandler = StatementHandler(this) val declarationHandler = DeclarationHandler(this) @@ -64,7 +61,7 @@ class LLVMIRLanguageFrontend( val phiList = mutableListOf() - var ctx: LLVMContextRef? = null + var ctxRef: LLVMContextRef? = null /** * This contains a cache binding between an LLVMValueRef (representing a variable) and its @@ -90,11 +87,11 @@ class LLVMIRLanguageFrontend( val buf = LLVMMemoryBufferRef() // create a new LLVM context - ctx = LLVMContextCreate() + ctxRef = LLVMContextCreate() // disable opaque pointers, until all necessary new functions are available in the C API. // See https://llvm.org/docs/OpaquePointers.html - LLVMContextSetOpaquePointers(ctx, 0) + LLVMContextSetOpaquePointers(ctxRef, 0) // allocate a buffer for a possible error message val errorMessage = ByteBuffer.allocate(10000) @@ -108,22 +105,21 @@ class LLVMIRLanguageFrontend( if (result != 0) { // something went wrong val errorMsg = String(errorMessage.array()) - LLVMContextDispose(ctx) + LLVMContextDispose(ctxRef) throw TranslationException("Could not create memory buffer: $errorMsg") } - result = LLVMParseIRInContext(ctx, buf, mod, errorMessage) + result = LLVMParseIRInContext(ctxRef, buf, mod, errorMessage) if (result != 0) { // something went wrong val errorMsg = String(errorMessage.array()) - LLVMContextDispose(ctx) + LLVMContextDispose(ctxRef) throw TranslationException("Could not parse IR: $errorMsg") } bench.addMeasurement() bench = Benchmark(this.javaClass, "Transform to CPG") - val tu = TranslationUnitDeclaration() - tu.language = language + val tu = newTranslationUnitDeclaration(file.name) // we need to set our translation unit as the global scope scopeManager.resetToGlobal(tu) @@ -157,7 +153,7 @@ class LLVMIRLanguageFrontend( counter++ } - LLVMContextDispose(ctx) + LLVMContextDispose(ctxRef) bench.addMeasurement() return tu diff --git a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CompressLLVMPass.kt b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CompressLLVMPass.kt index 5984b08c16..e15b3f5bac 100644 --- a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CompressLLVMPass.kt +++ b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CompressLLVMPass.kt @@ -25,13 +25,9 @@ */ package de.fraunhofer.aisec.cpg.passes -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.llvm.LLVMIRLanguageFrontend -import de.fraunhofer.aisec.cpg.graph.Component -import de.fraunhofer.aisec.cpg.graph.Node -import de.fraunhofer.aisec.cpg.graph.newDeclaredReferenceExpression -import de.fraunhofer.aisec.cpg.graph.newVariableDeclaration +import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.statements.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.ProblemExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.UnaryOperator @@ -43,8 +39,7 @@ import java.util.* @ExecuteFirst @RequiredFrontend(LLVMIRLanguageFrontend::class) -class CompressLLVMPass(config: TranslationConfiguration, scopeManager: ScopeManager) : - ComponentPass(config, scopeManager) { +class CompressLLVMPass(ctx: TranslationContext) : ComponentPass(ctx) { override fun accept(component: Component) { val flatAST = SubgraphWalker.flattenAST(component) // Get all goto statements diff --git a/cpg-language-llvm/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontendTest.kt b/cpg-language-llvm/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontendTest.kt index ae05556baf..8f2c0448ab 100644 --- a/cpg-language-llvm/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontendTest.kt +++ b/cpg-language-llvm/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontendTest.kt @@ -25,20 +25,17 @@ */ package de.fraunhofer.aisec.cpg.frontends.llvm -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TestUtils -import de.fraunhofer.aisec.cpg.TranslationConfiguration -import de.fraunhofer.aisec.cpg.assertFullName -import de.fraunhofer.aisec.cpg.assertLocalName +import de.fraunhofer.aisec.cpg.* +import de.fraunhofer.aisec.cpg.graph.TypeManager import de.fraunhofer.aisec.cpg.graph.bodyOrNull import de.fraunhofer.aisec.cpg.graph.byNameOrNull import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration +import de.fraunhofer.aisec.cpg.graph.parseType import de.fraunhofer.aisec.cpg.graph.statements.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.* import de.fraunhofer.aisec.cpg.graph.types.ObjectType -import de.fraunhofer.aisec.cpg.graph.types.TypeParser import java.nio.file.Path import kotlin.test.* import kotlin.test.Test @@ -51,8 +48,11 @@ class LLVMIRLanguageFrontendTest { val frontend = LLVMIRLanguageFrontend( LLVMIRLanguage(), - TranslationConfiguration.builder().build(), - ScopeManager(), + TranslationContext( + TranslationConfiguration.builder().build(), + ScopeManager(), + TypeManager() + ) ) frontend.parse(topLevel.resolve("main.ll").toFile()) } @@ -312,10 +312,10 @@ class LLVMIRLanguageFrontendTest { val rhs = (comparison.rhs as Literal<*>) val lhs = (comparison.lhs as DeclaredReferenceExpression).refersTo as VariableDeclaration assertEquals(10L, (rhs.value as Long)) - assertEquals(TypeParser.createFrom("i32", LLVMIRLanguage()), rhs.type) + assertEquals(tu.parseType("i32"), rhs.type) assertLocalName("x", comparison.lhs as DeclaredReferenceExpression) assertLocalName("x", lhs) - assertEquals(TypeParser.createFrom("i32", LLVMIRLanguage()), lhs.type) + assertEquals(tu.parseType("i32"), lhs.type) // Check that the jump targets are set correctly val ifStatement = main.bodyOrNull(0) @@ -346,11 +346,11 @@ class LLVMIRLanguageFrontendTest { assertTrue(ifBranchComp.lhs is CastExpression) val ifBranchCompRhs = ifBranchComp.rhs as CastExpression - assertEquals(TypeParser.createFrom("ui32", LLVMIRLanguage()), ifBranchCompRhs.castType) - assertEquals(TypeParser.createFrom("ui32", LLVMIRLanguage()), ifBranchCompRhs.type) + assertEquals(tu.parseType("ui32"), ifBranchCompRhs.castType) + assertEquals(tu.parseType("ui32"), ifBranchCompRhs.type) val ifBranchCompLhs = ifBranchComp.lhs as CastExpression - assertEquals(TypeParser.createFrom("ui32", LLVMIRLanguage()), ifBranchCompLhs.castType) - assertEquals(TypeParser.createFrom("ui32", LLVMIRLanguage()), ifBranchCompLhs.type) + assertEquals(tu.parseType("ui32"), ifBranchCompLhs.castType) + assertEquals(tu.parseType("ui32"), ifBranchCompLhs.type) val declRefExpr = ifBranchCompLhs.expression as DeclaredReferenceExpression assertEquals(-3, ((ifBranchCompRhs.expression as Literal<*>).value as Long)) diff --git a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguage.kt b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguage.kt index d69d9a27f6..15a48176d8 100644 --- a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguage.kt +++ b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguage.kt @@ -25,8 +25,6 @@ */ package de.fraunhofer.aisec.cpg.frontends.python -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration import de.fraunhofer.aisec.cpg.frontends.HasShortCircuitOperators import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.graph.statements.expressions.BinaryOperator @@ -79,13 +77,6 @@ class PythonLanguage : Language(), HasShortCircuitOperat "str" to StringType("str", this, listOf()) ) - override fun newFrontend( - config: TranslationConfiguration, - scopeManager: ScopeManager, - ): PythonLanguageFrontend { - return PythonLanguageFrontend(this, config, scopeManager) - } - override fun propagateTypeOfBinaryOperation(operation: BinaryOperator): Type { val unknownType = UnknownType.getUnknownType(this) if ( diff --git a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguageFrontend.kt b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguageFrontend.kt index 9392392fa3..f4db007664 100644 --- a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguageFrontend.kt +++ b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguageFrontend.kt @@ -25,8 +25,7 @@ */ package de.fraunhofer.aisec.cpg.frontends.python -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend import de.fraunhofer.aisec.cpg.frontends.TranslationException @@ -37,11 +36,8 @@ import java.nio.file.Paths import jep.JepException import kotlin.io.path.absolutePathString -class PythonLanguageFrontend( - language: Language, - config: TranslationConfiguration, - scopeManager: ScopeManager, -) : LanguageFrontend(language, config, scopeManager) { +class PythonLanguageFrontend(language: Language, ctx: TranslationContext) : + LanguageFrontend(language, ctx) { private val jep = JepSingleton // configure Jep @Throws(TranslationException::class) diff --git a/cpg-language-python/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonFrontendTest.kt b/cpg-language-python/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonFrontendTest.kt index adad8e12d3..b5f808b10f 100644 --- a/cpg-language-python/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonFrontendTest.kt +++ b/cpg-language-python/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonFrontendTest.kt @@ -37,7 +37,6 @@ import de.fraunhofer.aisec.cpg.graph.statements.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.* import de.fraunhofer.aisec.cpg.graph.types.NumericType import de.fraunhofer.aisec.cpg.graph.types.ObjectType -import de.fraunhofer.aisec.cpg.graph.types.TypeParser import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker import de.fraunhofer.aisec.cpg.sarif.PhysicalLocation import de.fraunhofer.aisec.cpg.sarif.Region @@ -70,19 +69,19 @@ class PythonFrontendTest : BaseTest() { val b = p.variables["b"] assertNotNull(b) assertLocalName("b", b) - assertEquals(TypeParser.createFrom("bool", PythonLanguage()), b.type) + assertEquals(tu.parseType("bool"), b.type) assertEquals(true, (b.initializer as? Literal<*>)?.value) val i = p.variables["i"] assertNotNull(i) assertLocalName("i", i) - assertEquals(TypeParser.createFrom("int", PythonLanguage()), i.type) + assertEquals(tu.parseType("int"), i.type) assertEquals(42L, (i.initializer as? Literal<*>)?.value) val f = p.variables["f"] assertNotNull(f) assertLocalName("f", f) - assertEquals(TypeParser.createFrom("float", PythonLanguage()), f.type) + assertEquals(tu.parseType("float"), f.type) assertEquals(1.0, (f.initializer as? Literal<*>)?.value) val c = p.variables["c"] @@ -97,13 +96,13 @@ class PythonFrontendTest : BaseTest() { val t = p.variables["t"] assertNotNull(t) assertLocalName("t", t) - assertEquals(TypeParser.createFrom("str", PythonLanguage()), t.type) + assertEquals(tu.parseType("str"), t.type) assertEquals("Hello", (t.initializer as? Literal<*>)?.value) val n = p.variables["n"] assertNotNull(n) assertLocalName("n", n) - assertEquals(TypeParser.createFrom("None", PythonLanguage()), n.type) + assertEquals(tu.parseType("None"), n.type) assertEquals(null, (n.initializer as? Literal<*>)?.value) } @@ -143,7 +142,7 @@ class PythonFrontendTest : BaseTest() { val s = bar.parameters.first() assertNotNull(s) assertLocalName("s", s) - assertEquals(TypeParser.createFrom("str", PythonLanguage()), s.type) + assertEquals(tu.parseType("str"), s.type) assertLocalName("bar", bar) @@ -160,7 +159,7 @@ class PythonFrontendTest : BaseTest() { assertNotNull(literal) assertEquals("bar(s) here: ", literal.value) - assertEquals(TypeParser.createFrom("str", PythonLanguage()), literal.type) + assertEquals(tu.parseType("str"), literal.type) val ref = callExpression.arguments[1] as? DeclaredReferenceExpression assertNotNull(ref) @@ -220,11 +219,11 @@ class PythonFrontendTest : BaseTest() { as? VariableDeclaration assertNotNull(sel) assertLocalName("sel", sel) - assertEquals(TypeParser.createFrom("bool", PythonLanguage()), sel.type) + assertEquals(tu.parseType("bool"), sel.type) val initializer = sel.initializer as? Literal<*> assertNotNull(initializer) - assertEquals(TypeParser.createFrom("bool", PythonLanguage()), initializer.type) + assertEquals(tu.parseType("bool"), initializer.type) assertEquals("True", initializer.code) val `if` = body.statements[1] as? IfStatement @@ -309,11 +308,11 @@ class PythonFrontendTest : BaseTest() { val foo = body.singleDeclaration as? VariableDeclaration assertNotNull(foo) assertLocalName("foo", foo) - assertEquals(TypeParser.createFrom("int", PythonLanguage()), foo.type) + assertEquals(tu.parseType("int"), foo.type) val initializer = foo.initializer as? ConditionalExpression assertNotNull(initializer) - assertEquals(TypeParser.createFrom("int", PythonLanguage()), initializer.type) + assertEquals(tu.parseType("int"), initializer.type) val ifCond = initializer.condition as? Literal<*> assertNotNull(ifCond) @@ -322,13 +321,13 @@ class PythonFrontendTest : BaseTest() { val elseExpr = initializer.elseExpr as? Literal<*> assertNotNull(elseExpr) - assertEquals(TypeParser.createFrom("bool", PythonLanguage()), ifCond.type) + assertEquals(tu.parseType("bool"), ifCond.type) assertEquals(false, ifCond.value) - assertEquals(TypeParser.createFrom("int", PythonLanguage()), thenExpr.type) + assertEquals(tu.parseType("int"), thenExpr.type) assertEquals(21, (thenExpr.value as? Long)?.toInt()) - assertEquals(TypeParser.createFrom("int", PythonLanguage()), elseExpr.type) + assertEquals(tu.parseType("int"), elseExpr.type) assertEquals(42, (elseExpr.value as? Long)?.toInt()) } @@ -412,7 +411,7 @@ class PythonFrontendTest : BaseTest() { val somevar = recordFoo.fields[0] assertNotNull(somevar) assertLocalName("somevar", somevar) - // assertEquals(TypeParser.createFrom("int", false), somevar.type) TODO fix type deduction + // assertEquals(tu.parseType("int", false), somevar.type) TODO fix type deduction assertEquals(2, recordFoo.methods.size) val bar = recordFoo.methods[0] @@ -434,7 +433,7 @@ class PythonFrontendTest : BaseTest() { assertNotNull(i) assertLocalName("i", i) - assertEquals(TypeParser.createFrom("int", PythonLanguage()), i.type) + assertEquals(tu.parseType("int"), i.type) // self.somevar = i val someVarDeclaration = diff --git a/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/JavaScriptLanguage.kt b/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/JavaScriptLanguage.kt index e3a727a716..b0e7e0eb0a 100644 --- a/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/JavaScriptLanguage.kt +++ b/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/JavaScriptLanguage.kt @@ -25,8 +25,6 @@ */ package de.fraunhofer.aisec.cpg.frontends.typescript -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration import de.fraunhofer.aisec.cpg.frontends.HasShortCircuitOperators import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.graph.types.* @@ -78,11 +76,4 @@ open class JavaScriptLanguage : Language(), HasShort "bigint" to IntegerType("bigint", null, this, NumericType.Modifier.SIGNED), "string" to StringType("string", this), ) - - override fun newFrontend( - config: TranslationConfiguration, - scopeManager: ScopeManager, - ): TypeScriptLanguageFrontend { - return TypeScriptLanguageFrontend(this, config, scopeManager) - } } diff --git a/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypeScriptLanguageFrontend.kt b/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypeScriptLanguageFrontend.kt index f814cad6ff..edaf51eb6c 100644 --- a/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypeScriptLanguageFrontend.kt +++ b/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypeScriptLanguageFrontend.kt @@ -26,8 +26,7 @@ package de.fraunhofer.aisec.cpg.frontends.typescript import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.FrontendUtils import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend @@ -58,24 +57,19 @@ import java.nio.file.StandardCopyOption */ class TypeScriptLanguageFrontend( language: Language, - config: TranslationConfiguration, - scopeManager: ScopeManager, -) : LanguageFrontend(language, config, scopeManager) { + ctx: TranslationContext +) : LanguageFrontend(language, ctx) { val declarationHandler = DeclarationHandler(this) val statementHandler = StatementHandler(this) val expressionHandler = ExpressionHandler(this) val typeHandler = TypeHandler(this) - var currentFileContent: String? = null + private var currentFileContent: String? = null - val mapper = jacksonObjectMapper() + private val mapper = jacksonObjectMapper() companion object { - @JvmField var TYPESCRIPT_EXTENSIONS: List = listOf(".ts", ".tsx") - - @JvmField var JAVASCRIPT_EXTENSIONS: List = listOf(".js", ".jsx") - private val parserFile: File = createTempFile("parser", ".js") init { diff --git a/cpg-language-typescript/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypescriptLanguageFrontendTest.kt b/cpg-language-typescript/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypescriptLanguageFrontendTest.kt index 7f3467d698..65d8bf2726 100644 --- a/cpg-language-typescript/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypescriptLanguageFrontendTest.kt +++ b/cpg-language-typescript/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypescriptLanguageFrontendTest.kt @@ -32,10 +32,10 @@ import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration import de.fraunhofer.aisec.cpg.graph.get +import de.fraunhofer.aisec.cpg.graph.parseType import de.fraunhofer.aisec.cpg.graph.statements.DeclarationStatement import de.fraunhofer.aisec.cpg.graph.statements.ReturnStatement import de.fraunhofer.aisec.cpg.graph.statements.expressions.* -import de.fraunhofer.aisec.cpg.graph.types.TypeParser import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker import java.nio.file.Path import kotlin.test.Test @@ -66,11 +66,11 @@ class TypeScriptLanguageFrontendTest { val someFunction = functions.first() assertLocalName("someFunction", someFunction) - assertEquals(TypeParser.createFrom("Number", TypeScriptLanguage()), someFunction.type) + assertEquals(tu.parseType("Number"), someFunction.type) val someOtherFunction = functions.last() assertLocalName("someOtherFunction", someOtherFunction) - assertEquals(TypeParser.createFrom("Number", TypeScriptLanguage()), someOtherFunction.type) + assertEquals(tu.parseType("Number"), someOtherFunction.type) val parameters = someOtherFunction.parameters assertNotNull(parameters) @@ -79,7 +79,7 @@ class TypeScriptLanguageFrontendTest { val parameter = parameters.first() assertLocalName("s", parameter) - assertEquals(TypeParser.createFrom("String", TypeScriptLanguage()), parameter.type) + assertEquals(tu.parseType("String"), parameter.type) } @Test @@ -278,7 +278,7 @@ class TypeScriptLanguageFrontendTest { val lastName = user.fields.lastOrNull() assertNotNull(lastName) assertLocalName("lastName", lastName) - assertEquals(TypeParser.createFrom("string", TypeScriptLanguage()), lastName.type) + assertEquals(tu.parseType("string"), lastName.type) val usersState = tu.getDeclarationsByName("UsersState", RecordDeclaration::class.java).iterator().next() @@ -291,7 +291,7 @@ class TypeScriptLanguageFrontendTest { val users = usersState.fields.firstOrNull() assertNotNull(users) assertLocalName("users", users) - assertEquals(TypeParser.createFrom("User[]", TypeScriptLanguage()), users.type) + assertEquals(tu.parseType("User[]"), users.type) val usersComponent = tu.getDeclarationsByName("Users", RecordDeclaration::class.java).iterator().next() From 1cedab005f80bdd23cec76daadf97885e352bda6 Mon Sep 17 00:00:00 2001 From: Christian Banse Date: Thu, 20 Apr 2023 21:40:03 +0200 Subject: [PATCH 2/5] More Go optimizations This PR adds more optimizations to the Go frontend. I am currently trying to parse https://github.com/clouditor/clouditor as a Go benchmark. --- .../aisec/cpg/TranslationConfiguration.kt | 4 +- .../ArraySubscriptionExpression.kt | 15 +- .../expressions/AssignExpression.kt | 16 +- .../statements/expressions/CastExpression.kt | 15 +- .../expressions/InitializerListExpression.kt | 16 +- .../expressions/KeyValueExpression.kt | 16 +- .../cpg/passes/EvaluationOrderGraphPass.kt | 79 ++- .../cpg/passes/order/RegisterExtraPass.kt | 2 +- .../aisec/cpg/passes/order/ReplacePass.kt | 2 +- .../golang/frontend/declaration_builder.go | 42 +- .../golang/frontend/expression_builder.go | 78 +-- .../src/main/golang/frontend/handler.go | 627 +++++++++--------- .../main/golang/frontend/statement_builder.go | 42 +- .../frontends/golang/GoLanguageFrontend.kt | 13 + .../aisec/cpg/passes/GoEvaluationGraphPass.kt | 4 + .../cpg/passes/GoEvaluationOrderGraphPass.kt | 47 ++ .../aisec/cpg/passes/GoExtraPass.kt | 14 +- .../cpg/frontends/golang/ExpressionTest.kt | 23 + .../src/test/resources/golang/defer.go | 25 + 19 files changed, 664 insertions(+), 416 deletions(-) create mode 100644 cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoEvaluationGraphPass.kt create mode 100644 cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoEvaluationOrderGraphPass.kt create mode 100644 cpg-language-go/src/test/resources/golang/defer.go diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationConfiguration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationConfiguration.kt index 1a8729aed1..a628eb2347 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationConfiguration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationConfiguration.kt @@ -502,10 +502,10 @@ private constructor( val replacedPasses = frontend.findAnnotations() if (replacedPasses.isNotEmpty()) { for (p in replacedPasses) { - replacePass(p.old, p.lang, p.with) + replacePass(p.value, p.lang, p.with) log.info( "Registered an extra (frontend dependent) default dependency, which replaced an existing pass: {}", - p.old + p.value ) } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ArraySubscriptionExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ArraySubscriptionExpression.kt index 7cc3d05d6c..c7aa3aaf53 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ArraySubscriptionExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ArraySubscriptionExpression.kt @@ -35,7 +35,7 @@ import java.util.stream.Collectors * ([arrayExpression]) and `index` ([subscriptExpression]) are of type [Expression]. CPP can * overload operators thus changing semantics of array access. */ -class ArraySubscriptionExpression : Expression(), HasType.TypeListener, HasBase { +class ArraySubscriptionExpression : Expression(), HasType.TypeListener, HasBase, ArgumentHolder { /** * The array on which the access is happening. This is most likely a * [DeclaredReferenceExpression]. @@ -100,6 +100,19 @@ class ArraySubscriptionExpression : Expression(), HasType.TypeListener, HasBase setPossibleSubTypes(subTypes, root) } + override fun addArgument(expression: Expression) { + this.subscriptExpression = expression + } + + override fun replaceArgument(old: Expression, new: Expression): Boolean { + if (this.subscriptExpression == old) { + this.subscriptExpression = new + return true + } + + return false + } + override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is ArraySubscriptionExpression) return false diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/AssignExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/AssignExpression.kt index e240f0e67e..6a18f6ac85 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/AssignExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/AssignExpression.kt @@ -49,7 +49,7 @@ import org.slf4j.LoggerFactory * [usedAsExpression]. When this property is set to true (it defaults to false), we model a dataflow * from the (first) rhs to the [AssignExpression] itself. */ -class AssignExpression : Expression(), AssignmentHolder, HasType.TypeListener { +class AssignExpression : Expression(), AssignmentHolder, ArgumentHolder, HasType.TypeListener { var operatorCode: String = "=" @@ -197,6 +197,20 @@ class AssignExpression : Expression(), AssignmentHolder, HasType.TypeListener { } } + override fun addArgument(expression: Expression) { + rhs += expression + } + + override fun replaceArgument(old: Expression, new: Expression): Boolean { + if (rhs.contains(old)) { + rhs -= old + rhs += new + return true + } + + return false + } + override val assignments: List get() { val list = mutableListOf() diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CastExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CastExpression.kt index 30024d3bd3..909f8b8065 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CastExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CastExpression.kt @@ -31,7 +31,7 @@ import java.util.* import kotlin.collections.ArrayList import org.slf4j.LoggerFactory -class CastExpression : Expression(), HasType.TypeListener { +class CastExpression : Expression(), HasType.TypeListener, ArgumentHolder { @AST var expression: Expression = ProblemExpression("could not parse inner expression") var castType: Type = newUnknownType() @@ -82,6 +82,19 @@ class CastExpression : Expression(), HasType.TypeListener { } } + override fun addArgument(expression: Expression) { + this.expression = expression + } + + override fun replaceArgument(old: Expression, new: Expression): Boolean { + if (this.expression == old) { + this.expression = new + return true + } + + return false + } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/InitializerListExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/InitializerListExpression.kt index c7c02ddd28..7c48483a9b 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/InitializerListExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/InitializerListExpression.kt @@ -37,7 +37,7 @@ import org.apache.commons.lang3.builder.ToStringBuilder import org.neo4j.ogm.annotation.Relationship /** A list of initializer expressions. */ -class InitializerListExpression : Expression(), HasType.TypeListener { +class InitializerListExpression : Expression(), HasType.TypeListener, ArgumentHolder { /** The list of initializers. */ @Relationship(value = "INITIALIZERS", direction = Relationship.Direction.OUTGOING) @AST @@ -104,6 +104,20 @@ class InitializerListExpression : Expression(), HasType.TypeListener { .toString() } + override fun addArgument(expression: Expression) { + this.initializers += expression + } + + override fun replaceArgument(old: Expression, new: Expression): Boolean { + if (this.initializers.contains(old)) { + this.initializers -= old + this.initializers += new + return true + } + + return false + } + override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is InitializerListExpression) return false diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/KeyValueExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/KeyValueExpression.kt index 7ca1b0d42d..5241101b8f 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/KeyValueExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/KeyValueExpression.kt @@ -26,6 +26,7 @@ package de.fraunhofer.aisec.cpg.graph.statements.expressions import de.fraunhofer.aisec.cpg.graph.AST +import de.fraunhofer.aisec.cpg.graph.ArgumentHolder import java.util.* /** @@ -35,7 +36,7 @@ import java.util.* * Most often used in combination with an [InitializerListExpression] to represent the creation of * an array. */ -class KeyValueExpression : Expression() { +class KeyValueExpression : Expression(), ArgumentHolder { /** * The key of this pair. It is usually a literal, but some languages even allow references to @@ -46,6 +47,19 @@ class KeyValueExpression : Expression() { /** The value of this pair. It can be any expression */ @AST var value: Expression? = null + override fun addArgument(expression: Expression) { + this.value = expression + } + + override fun replaceArgument(old: Expression, new: Expression): Boolean { + if (this.value == old) { + this.value = new + return true + } + + return false + } + override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is KeyValueExpression) return false diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EvaluationOrderGraphPass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EvaluationOrderGraphPass.kt index 9c837fcb1c..39aff7c47e 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EvaluationOrderGraphPass.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EvaluationOrderGraphPass.kt @@ -40,6 +40,7 @@ import de.fraunhofer.aisec.cpg.graph.types.Type import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker import de.fraunhofer.aisec.cpg.helpers.Util import de.fraunhofer.aisec.cpg.passes.order.DependsOn +import de.fraunhofer.aisec.cpg.passes.order.ReplacePass import java.util.* import org.slf4j.LoggerFactory @@ -74,6 +75,13 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa private var currentPredecessors = mutableListOf() private val nextEdgeProperties = EnumMap(Properties::class.java) + /** + * Certain languages (e.g. Go) allow the automatic execution of certain cleanup calls before we + * exit the function. We need to gather the appropriate call expressions and then connect them + * in [handleFunctionDeclaration]. + */ + private var cleanupCalls = mutableMapOf>() + /** * Allows to register EOG creation logic when a currently visited node can depend on future * visited nodes. Currently used to connect goto statements and the target labeled statements. @@ -290,7 +298,7 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa pushToEOG(node) } - protected fun handleFunctionDeclaration(node: FunctionDeclaration) { + protected open fun handleFunctionDeclaration(node: FunctionDeclaration) { // reset EOG currentPredecessors.clear() var needToLeaveRecord = false @@ -353,6 +361,16 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa } } currentPredecessors.clear() + + // Before we exit, we need to call any cleanup calls + val calls = cleanupCalls[node] + calls?.forEach { call -> + createEOG(call) + node.body?.let { body -> addEOGEdge(call, body) } + } + + // Clear them afterwards + calls?.clear() } /** @@ -384,7 +402,7 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa pushToEOG(node) } - protected fun handleCallExpression(node: CallExpression) { + protected open fun handleCallExpression(node: CallExpression) { // Todo add call as throwexpression to outer scope of call can throw (which is trivial to // find out for java, but impossible for c++) @@ -452,7 +470,7 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa pushToEOG(node) } - protected fun handleReturnStatement(node: ReturnStatement) { + protected open fun handleReturnStatement(node: ReturnStatement) { // analyze the return value createEOG(node.returnValue) @@ -525,25 +543,50 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa } protected fun handleUnaryOperator(node: UnaryOperator) { - val input = node.input - createEOG(input) + // TODO(oxisto): These operator codes are highly language specific and might be more suited + // to be handled differently (see https://github.com/Fraunhofer-AISEC/cpg/issues/1161) if (node.operatorCode == "throw") { - val catchingScope = - scopeManager.firstScopeOrNull { scope -> - scope is TryScope || scope is FunctionScope - } - - val throwType = input.type - pushToEOG(node) - if (catchingScope is TryScope) { - catchingScope.catchesOrRelays[throwType] = ArrayList(currentPredecessors) - } else if (catchingScope is FunctionScope) { - catchingScope.catchesOrRelays[throwType] = ArrayList(currentPredecessors) + handleThrowOperator(node) + } else if (node.operatorCode == "defer" && node.input is CallExpression) { + // Add to the cleanup calls. We are intentionally not creating the EOG for the input + // yet, as this would mess up the EOG at this point. + scopeManager.currentFunction?.let { + val list = cleanupCalls.computeIfAbsent(it) { mutableListOf() } + list += node.input as CallExpression } - currentPredecessors.clear() } else { - pushToEOG(node) + handleUnspecificUnaryOperator(node) + } + } + + protected fun handleThrowOperator(node: UnaryOperator) { + val input = node.input + createEOG(input) + + val catchingScope = + scopeManager.firstScopeOrNull { scope -> scope is TryScope || scope is FunctionScope } + + val throwType = input.type + pushToEOG(node) + if (catchingScope is TryScope) { + catchingScope.catchesOrRelays[throwType] = ArrayList(currentPredecessors) + } else if (catchingScope is FunctionScope) { + catchingScope.catchesOrRelays[throwType] = ArrayList(currentPredecessors) } + currentPredecessors.clear() + } + + /** + * This function handles all regular unary operators that do not receive any special handling + * (such as [handleThrowOperator]). This gives language frontends a chance to override this + * function using [ReplacePass], handle specific operators on their own and delegate the rest to + * this function. + */ + protected fun handleUnspecificUnaryOperator(node: UnaryOperator) { + val input = node.input + createEOG(input) + + pushToEOG(node) } protected fun handleCompoundStatementExpression(node: CompoundStatementExpression) { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/order/RegisterExtraPass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/order/RegisterExtraPass.kt index 7d2824504f..6cec51d9a9 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/order/RegisterExtraPass.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/order/RegisterExtraPass.kt @@ -28,7 +28,7 @@ package de.fraunhofer.aisec.cpg.passes.order import de.fraunhofer.aisec.cpg.passes.Pass import kotlin.reflect.KClass -/** Register a new pass required by a fronted. */ +/** Register a new pass required by a frontend. */ @Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.CLASS) @Repeatable diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/order/ReplacePass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/order/ReplacePass.kt index 8becb30951..0456407ff4 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/order/ReplacePass.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/order/ReplacePass.kt @@ -41,7 +41,7 @@ import kotlin.reflect.KClass @Target(AnnotationTarget.CLASS) @Repeatable annotation class ReplacePass( - val old: KClass>, + val value: KClass>, val lang: KClass>, val with: KClass> ) diff --git a/cpg-language-go/src/main/golang/frontend/declaration_builder.go b/cpg-language-go/src/main/golang/frontend/declaration_builder.go index 817ce7faf9..bdccb06665 100644 --- a/cpg-language-go/src/main/golang/frontend/declaration_builder.go +++ b/cpg-language-go/src/main/golang/frontend/declaration_builder.go @@ -34,51 +34,51 @@ import ( "tekao.net/jnigi" ) -func (frontend *GoLanguageFrontend) NewTranslationUnitDeclaration(fset *token.FileSet, astNode ast.Node, name string) *cpg.TranslationUnitDeclaration { - return (*cpg.TranslationUnitDeclaration)(frontend.NewDeclaration("TranslationUnitDeclaration", fset, astNode, name)) +func (g *GoLanguageFrontend) NewTranslationUnitDeclaration(fset *token.FileSet, astNode ast.Node, name string) *cpg.TranslationUnitDeclaration { + return (*cpg.TranslationUnitDeclaration)(g.NewDeclaration("TranslationUnitDeclaration", fset, astNode, name)) } -func (frontend *GoLanguageFrontend) NewNamespaceDeclaration(fset *token.FileSet, astNode ast.Node, name string) *cpg.NamespaceDeclaration { - return (*cpg.NamespaceDeclaration)(frontend.NewDeclaration("NamespaceDeclaration", fset, astNode, name)) +func (g *GoLanguageFrontend) NewNamespaceDeclaration(fset *token.FileSet, astNode ast.Node, name string) *cpg.NamespaceDeclaration { + return (*cpg.NamespaceDeclaration)(g.NewDeclaration("NamespaceDeclaration", fset, astNode, name)) } -func (frontend *GoLanguageFrontend) NewIncludeDeclaration(fset *token.FileSet, astNode ast.Node, name string) *cpg.IncludeDeclaration { - return (*cpg.IncludeDeclaration)(frontend.NewDeclaration("IncludeDeclaration", fset, astNode, name)) +func (g *GoLanguageFrontend) NewIncludeDeclaration(fset *token.FileSet, astNode ast.Node, name string) *cpg.IncludeDeclaration { + return (*cpg.IncludeDeclaration)(g.NewDeclaration("IncludeDeclaration", fset, astNode, name)) } -func (frontend *GoLanguageFrontend) NewFunctionDeclaration(fset *token.FileSet, astNode ast.Node, name string, code string, localNameOnly bool) *cpg.FunctionDeclaration { - return (*cpg.FunctionDeclaration)(frontend.NewDeclaration("FunctionDeclaration", fset, astNode, name, +func (g *GoLanguageFrontend) NewFunctionDeclaration(fset *token.FileSet, astNode ast.Node, name string, code string, localNameOnly bool) *cpg.FunctionDeclaration { + return (*cpg.FunctionDeclaration)(g.NewDeclaration("FunctionDeclaration", fset, astNode, name, cpg.NewString(code), jnigi.NewObjectRef("java/lang/Object"), localNameOnly, )) } -func (frontend *GoLanguageFrontend) NewMethodDeclaration(fset *token.FileSet, astNode ast.Node, name string) *cpg.MethodDeclaration { - return (*cpg.MethodDeclaration)(frontend.NewDeclaration("MethodDeclaration", fset, astNode, name)) +func (g *GoLanguageFrontend) NewMethodDeclaration(fset *token.FileSet, astNode ast.Node, name string) *cpg.MethodDeclaration { + return (*cpg.MethodDeclaration)(g.NewDeclaration("MethodDeclaration", fset, astNode, name)) } -func (frontend *GoLanguageFrontend) NewRecordDeclaration(fset *token.FileSet, astNode ast.Node, name string, kind string) *cpg.RecordDeclaration { - return (*cpg.RecordDeclaration)(frontend.NewDeclaration("RecordDeclaration", fset, astNode, name, cpg.NewString(kind))) +func (g *GoLanguageFrontend) NewRecordDeclaration(fset *token.FileSet, astNode ast.Node, name string, kind string) *cpg.RecordDeclaration { + return (*cpg.RecordDeclaration)(g.NewDeclaration("RecordDeclaration", fset, astNode, name, cpg.NewString(kind))) } -func (frontend *GoLanguageFrontend) NewVariableDeclaration(fset *token.FileSet, astNode ast.Node, name string) *cpg.VariableDeclaration { - return (*cpg.VariableDeclaration)(frontend.NewDeclaration("VariableDeclaration", fset, astNode, name)) +func (g *GoLanguageFrontend) NewVariableDeclaration(fset *token.FileSet, astNode ast.Node, name string) *cpg.VariableDeclaration { + return (*cpg.VariableDeclaration)(g.NewDeclaration("VariableDeclaration", fset, astNode, name)) } -func (frontend *GoLanguageFrontend) NewParamVariableDeclaration(fset *token.FileSet, astNode ast.Node, name string) *cpg.ParamVariableDeclaration { - return (*cpg.ParamVariableDeclaration)(frontend.NewDeclaration("ParamVariableDeclaration", fset, astNode, name)) +func (g *GoLanguageFrontend) NewParamVariableDeclaration(fset *token.FileSet, astNode ast.Node, name string) *cpg.ParamVariableDeclaration { + return (*cpg.ParamVariableDeclaration)(g.NewDeclaration("ParamVariableDeclaration", fset, astNode, name)) } -func (frontend *GoLanguageFrontend) NewFieldDeclaration(fset *token.FileSet, astNode ast.Node, name string) *cpg.FieldDeclaration { - return (*cpg.FieldDeclaration)(frontend.NewDeclaration("FieldDeclaration", fset, astNode, name)) +func (g *GoLanguageFrontend) NewFieldDeclaration(fset *token.FileSet, astNode ast.Node, name string) *cpg.FieldDeclaration { + return (*cpg.FieldDeclaration)(g.NewDeclaration("FieldDeclaration", fset, astNode, name)) } -func (frontend *GoLanguageFrontend) NewDeclaration(typ string, fset *token.FileSet, astNode ast.Node, name string, args ...any) *jnigi.ObjectRef { +func (g *GoLanguageFrontend) NewDeclaration(typ string, fset *token.FileSet, astNode ast.Node, name string, args ...any) *jnigi.ObjectRef { var node = jnigi.NewObjectRef(fmt.Sprintf("%s/%s", cpg.DeclarationsPackage, typ)) - // Prepend the frontend and the name as the receiver and the first argument - args = append([]any{frontend.Cast(MetadataProviderClass), cpg.NewCharSequence(name)}, args...) + // Prepend the g and the name as the receiver and the first argument + args = append([]any{g.Cast(MetadataProviderClass), cpg.NewCharSequence(name)}, args...) err := env.CallStaticMethod( cpg.GraphPackage+"/DeclarationBuilderKt", diff --git a/cpg-language-go/src/main/golang/frontend/expression_builder.go b/cpg-language-go/src/main/golang/frontend/expression_builder.go index 9b248e90e1..b5061b945b 100644 --- a/cpg-language-go/src/main/golang/frontend/expression_builder.go +++ b/cpg-language-go/src/main/golang/frontend/expression_builder.go @@ -34,74 +34,74 @@ import ( "tekao.net/jnigi" ) -func (frontend *GoLanguageFrontend) NewCallExpression(fset *token.FileSet, astNode ast.Node, callee cpg.Castable, name string) *cpg.CallExpression { +func (g *GoLanguageFrontend) NewCallExpression(fset *token.FileSet, astNode ast.Node, callee cpg.Castable, name string) *cpg.CallExpression { if callee == nil { callee = jnigi.NewObjectRef(cpg.ExpressionClass) } else { callee = callee.Cast(cpg.ExpressionClass) } - return (*cpg.CallExpression)(frontend.NewExpression("CallExpression", fset, astNode, callee, cpg.NewCharSequence(name))) + return (*cpg.CallExpression)(g.NewExpression("CallExpression", fset, astNode, callee, cpg.NewCharSequence(name))) } -func (frontend *GoLanguageFrontend) NewCastExpression(fset *token.FileSet, astNode ast.Node) *cpg.CastExpression { - return (*cpg.CastExpression)(frontend.NewExpression("CastExpression", fset, astNode)) +func (g *GoLanguageFrontend) NewCastExpression(fset *token.FileSet, astNode ast.Node) *cpg.CastExpression { + return (*cpg.CastExpression)(g.NewExpression("CastExpression", fset, astNode)) } -func (frontend *GoLanguageFrontend) NewMemberExpression(fset *token.FileSet, astNode ast.Node, name string, base cpg.Castable) *cpg.MemberExpression { - return (*cpg.MemberExpression)(frontend.NewExpression("MemberExpression", fset, astNode, cpg.NewCharSequence(name), base.Cast(cpg.ExpressionClass))) +func (g *GoLanguageFrontend) NewMemberExpression(fset *token.FileSet, astNode ast.Node, name string, base cpg.Castable) *cpg.MemberExpression { + return (*cpg.MemberExpression)(g.NewExpression("MemberExpression", fset, astNode, cpg.NewCharSequence(name), base.Cast(cpg.ExpressionClass))) } -func (frontend *GoLanguageFrontend) NewMemberCallExpression(fset *token.FileSet, astNode ast.Node, callee *cpg.Expression) *cpg.MemberCallExpression { - return (*cpg.MemberCallExpression)(frontend.NewExpression("MemberCallExpression", fset, astNode, +func (g *GoLanguageFrontend) NewMemberCallExpression(fset *token.FileSet, astNode ast.Node, callee *cpg.Expression) *cpg.MemberCallExpression { + return (*cpg.MemberCallExpression)(g.NewExpression("MemberCallExpression", fset, astNode, callee.Cast(cpg.ExpressionClass), )) } -func (frontend *GoLanguageFrontend) NewNewExpression(fset *token.FileSet, astNode ast.Node) *cpg.NewExpression { - return (*cpg.NewExpression)(frontend.NewExpression("NewExpression", fset, astNode)) +func (g *GoLanguageFrontend) NewNewExpression(fset *token.FileSet, astNode ast.Node) *cpg.NewExpression { + return (*cpg.NewExpression)(g.NewExpression("NewExpression", fset, astNode)) } -func (frontend *GoLanguageFrontend) NewArrayCreationExpression(fset *token.FileSet, astNode ast.Node) *cpg.ArrayCreationExpression { - return (*cpg.ArrayCreationExpression)(frontend.NewExpression("ArrayCreationExpression", fset, astNode)) +func (g *GoLanguageFrontend) NewArrayCreationExpression(fset *token.FileSet, astNode ast.Node) *cpg.ArrayCreationExpression { + return (*cpg.ArrayCreationExpression)(g.NewExpression("ArrayCreationExpression", fset, astNode)) } -func (frontend *GoLanguageFrontend) NewArraySubscriptionExpression(fset *token.FileSet, astNode ast.Node) *cpg.ArraySubscriptionExpression { - return (*cpg.ArraySubscriptionExpression)(frontend.NewExpression("ArraySubscriptionExpression", fset, astNode)) +func (g *GoLanguageFrontend) NewArraySubscriptionExpression(fset *token.FileSet, astNode ast.Node) *cpg.ArraySubscriptionExpression { + return (*cpg.ArraySubscriptionExpression)(g.NewExpression("ArraySubscriptionExpression", fset, astNode)) } -func (frontend *GoLanguageFrontend) NewRangeExpression(fset *token.FileSet, astNode ast.Node) *cpg.RangeExpression { - return (*cpg.RangeExpression)(frontend.NewExpression("RangeExpression", fset, astNode)) +func (g *GoLanguageFrontend) NewRangeExpression(fset *token.FileSet, astNode ast.Node) *cpg.RangeExpression { + return (*cpg.RangeExpression)(g.NewExpression("RangeExpression", fset, astNode)) } -func (frontend *GoLanguageFrontend) NewConstructExpression(fset *token.FileSet, astNode ast.Node) *cpg.ConstructExpression { - return (*cpg.ConstructExpression)(frontend.NewExpression("ConstructExpression", fset, astNode)) +func (g *GoLanguageFrontend) NewConstructExpression(fset *token.FileSet, astNode ast.Node) *cpg.ConstructExpression { + return (*cpg.ConstructExpression)(g.NewExpression("ConstructExpression", fset, astNode)) } -func (frontend *GoLanguageFrontend) NewInitializerListExpression(fset *token.FileSet, astNode ast.Node) *cpg.InitializerListExpression { - return (*cpg.InitializerListExpression)(frontend.NewExpression("InitializerListExpression", fset, astNode)) +func (g *GoLanguageFrontend) NewInitializerListExpression(fset *token.FileSet, astNode ast.Node) *cpg.InitializerListExpression { + return (*cpg.InitializerListExpression)(g.NewExpression("InitializerListExpression", fset, astNode)) } -func (frontend *GoLanguageFrontend) NewBinaryOperator(fset *token.FileSet, astNode ast.Node, opCode string) *cpg.BinaryOperator { - return (*cpg.BinaryOperator)(frontend.NewExpression("BinaryOperator", fset, astNode, +func (g *GoLanguageFrontend) NewBinaryOperator(fset *token.FileSet, astNode ast.Node, opCode string) *cpg.BinaryOperator { + return (*cpg.BinaryOperator)(g.NewExpression("BinaryOperator", fset, astNode, cpg.NewString(opCode), )) } -func (frontend *GoLanguageFrontend) NewAssignExpression(fset *token.FileSet, astNode ast.Node, opCode string) *cpg.AssignExpression { - return (*cpg.AssignExpression)(frontend.NewExpression("AssignExpression", fset, astNode, +func (g *GoLanguageFrontend) NewAssignExpression(fset *token.FileSet, astNode ast.Node, opCode string) *cpg.AssignExpression { + return (*cpg.AssignExpression)(g.NewExpression("AssignExpression", fset, astNode, cpg.NewString(opCode), )) } -func (frontend *GoLanguageFrontend) NewUnaryOperator(fset *token.FileSet, astNode ast.Node, opCode string, postfix bool, prefix bool) *cpg.UnaryOperator { - return (*cpg.UnaryOperator)(frontend.NewExpression("UnaryOperator", fset, astNode, +func (g *GoLanguageFrontend) NewUnaryOperator(fset *token.FileSet, astNode ast.Node, opCode string, postfix bool, prefix bool) *cpg.UnaryOperator { + return (*cpg.UnaryOperator)(g.NewExpression("UnaryOperator", fset, astNode, cpg.NewString(opCode), postfix, prefix, )) } -func (frontend *GoLanguageFrontend) NewLiteral(fset *token.FileSet, astNode ast.Node, value cpg.Castable, typ *cpg.Type) *cpg.Literal { +func (g *GoLanguageFrontend) NewLiteral(fset *token.FileSet, astNode ast.Node, value cpg.Castable, typ *cpg.Type) *cpg.Literal { if value == nil { value = jnigi.NewObjectRef("java/lang/Object") } else { @@ -112,30 +112,30 @@ func (frontend *GoLanguageFrontend) NewLiteral(fset *token.FileSet, astNode ast. panic("typ is nil") } - return (*cpg.Literal)(frontend.NewExpression("Literal", fset, astNode, value, typ.Cast(cpg.TypeClass))) + return (*cpg.Literal)(g.NewExpression("Literal", fset, astNode, value, typ.Cast(cpg.TypeClass))) } -func (frontend *GoLanguageFrontend) NewDeclaredReferenceExpression(fset *token.FileSet, astNode ast.Node, name string) *cpg.DeclaredReferenceExpression { - return (*cpg.DeclaredReferenceExpression)(frontend.NewExpression("DeclaredReferenceExpression", fset, astNode, cpg.NewCharSequence(name))) +func (g *GoLanguageFrontend) NewDeclaredReferenceExpression(fset *token.FileSet, astNode ast.Node, name string) *cpg.DeclaredReferenceExpression { + return (*cpg.DeclaredReferenceExpression)(g.NewExpression("DeclaredReferenceExpression", fset, astNode, cpg.NewCharSequence(name))) } -func (frontend *GoLanguageFrontend) NewKeyValueExpression(fset *token.FileSet, astNode ast.Node) *cpg.KeyValueExpression { - return (*cpg.KeyValueExpression)(frontend.NewExpression("KeyValueExpression", fset, astNode)) +func (g *GoLanguageFrontend) NewKeyValueExpression(fset *token.FileSet, astNode ast.Node) *cpg.KeyValueExpression { + return (*cpg.KeyValueExpression)(g.NewExpression("KeyValueExpression", fset, astNode)) } -func (frontend *GoLanguageFrontend) NewLambdaExpression(fset *token.FileSet, astNode ast.Node) *cpg.LambdaExpression { - return (*cpg.LambdaExpression)(frontend.NewExpression("LambdaExpression", fset, astNode)) +func (g *GoLanguageFrontend) NewLambdaExpression(fset *token.FileSet, astNode ast.Node) *cpg.LambdaExpression { + return (*cpg.LambdaExpression)(g.NewExpression("LambdaExpression", fset, astNode)) } -func (frontend *GoLanguageFrontend) NewProblemExpression(fset *token.FileSet, astNode ast.Node, problem string) *cpg.ProblemExpression { - return (*cpg.ProblemExpression)(frontend.NewExpression("ProblemExpression", fset, astNode, cpg.NewString(problem))) +func (g *GoLanguageFrontend) NewProblemExpression(fset *token.FileSet, astNode ast.Node, problem string) *cpg.ProblemExpression { + return (*cpg.ProblemExpression)(g.NewExpression("ProblemExpression", fset, astNode, cpg.NewString(problem))) } -func (frontend *GoLanguageFrontend) NewExpression(typ string, fset *token.FileSet, astNode ast.Node, args ...any) *jnigi.ObjectRef { +func (g *GoLanguageFrontend) NewExpression(typ string, fset *token.FileSet, astNode ast.Node, args ...any) *jnigi.ObjectRef { var node = jnigi.NewObjectRef(fmt.Sprintf("%s/%s", cpg.ExpressionsPackage, typ)) - // Prepend the frontend as the receiver - args = append([]any{frontend.Cast(cpg.GraphPackage + "/MetadataProvider")}, args...) + // Prepend the g as the receiver + args = append([]any{g.Cast(cpg.GraphPackage + "/MetadataProvider")}, args...) err := env.CallStaticMethod( cpg.GraphPackage+"/ExpressionBuilderKt", diff --git a/cpg-language-go/src/main/golang/frontend/handler.go b/cpg-language-go/src/main/golang/frontend/handler.go index 163cea3897..bf2aa15375 100644 --- a/cpg-language-go/src/main/golang/frontend/handler.go +++ b/cpg-language-go/src/main/golang/frontend/handler.go @@ -56,14 +56,14 @@ func getImportName(spec *ast.ImportSpec) string { return paths[len(paths)-1] } -func (frontend *GoLanguageFrontend) ParseModule(topLevel string) (exists bool, err error) { - frontend.LogInfo("Looking for a go.mod file in %s", topLevel) +func (g *GoLanguageFrontend) ParseModule(topLevel string) (exists bool, err error) { + g.LogInfo("Looking for a go.mod file in %s", topLevel) mod := path.Join(topLevel, "go.mod") if _, err := os.Stat(mod); err != nil { if os.IsNotExist(err) { - frontend.LogInfo("%s does not exist", mod) + g.LogInfo("%s does not exist", mod) return false, nil } @@ -79,25 +79,25 @@ func (frontend *GoLanguageFrontend) ParseModule(topLevel string) (exists bool, e return true, fmt.Errorf("could not parse mod file: %w", err) } - frontend.Module = module + g.Module = module - frontend.LogInfo("Go application has module support with path %s", module.Module.Mod.Path) + g.LogInfo("Go application has module support with path %s", module.Module.Mod.Path) return true, nil } -func (this *GoLanguageFrontend) HandleFile(fset *token.FileSet, file *ast.File, path string) (tu *cpg.TranslationUnitDeclaration, err error) { - tu = this.NewTranslationUnitDeclaration(fset, file, path) +func (g *GoLanguageFrontend) HandleFile(fset *token.FileSet, file *ast.File, path string) (tu *cpg.TranslationUnitDeclaration, err error) { + tu = g.NewTranslationUnitDeclaration(fset, file, path) - scope := this.GetScopeManager() + scope := g.GetScopeManager() // reset scope scope.ResetToGlobal((*cpg.Node)(tu)) - this.CurrentTU = tu + g.CurrentTU = tu for _, imprt := range file.Imports { - i := this.handleImportSpec(fset, imprt) + i := g.handleImportSpec(fset, imprt) err = scope.AddDeclaration((*cpg.Declaration)(i)) if err != nil { @@ -106,23 +106,23 @@ func (this *GoLanguageFrontend) HandleFile(fset *token.FileSet, file *ast.File, } // Create a new namespace declaration, representing the package - p := this.NewNamespaceDeclaration(fset, nil, file.Name.Name) + p := g.NewNamespaceDeclaration(fset, nil, file.Name.Name) // we need to construct the package "path" (e.g. "encoding/json") out of the // module path as well as the current directory in relation to the topLevel packagePath := filepath.Dir(path) // Construct a relative path starting from the top level - packagePath, err = filepath.Rel(this.TopLevel, packagePath) + packagePath, err = filepath.Rel(g.TopLevel, packagePath) if err == nil { // If we are in a module, we need to prepend the module path to it - if this.Module != nil { - packagePath = filepath.Join(this.Module.Module.Mod.Path, packagePath) + if g.Module != nil { + packagePath = filepath.Join(g.Module.Module.Mod.Path, packagePath) } p.SetPath(packagePath) } else { - this.LogError("Could not relativize package path to top level. Cannot set package path: %v", err) + g.LogError("Could not relativize package path to top level. Cannot set package path: %v", err) } // enter scope @@ -131,7 +131,7 @@ func (this *GoLanguageFrontend) HandleFile(fset *token.FileSet, file *ast.File, for _, decl := range file.Decls { // Retrieve all top level declarations. One "Decl" could potentially // contain multiple CPG declarations. - decls := this.handleDecl(fset, decl) + decls := g.handleDecl(fset, decl) for _, d := range decls { if d != nil { @@ -156,15 +156,15 @@ func (this *GoLanguageFrontend) HandleFile(fset *token.FileSet, file *ast.File, } // handleComments maps comments from ast.Node to a cpg.Node by using ast.CommentMap. -func (this *GoLanguageFrontend) handleComments(node *cpg.Node, astNode ast.Node) { - this.LogTrace("Handling comments for %+v", astNode) +func (g *GoLanguageFrontend) handleComments(node *cpg.Node, astNode ast.Node) { + g.LogTrace("Handling comments for %+v", astNode) var comment = "" // Lookup ast node in comment map. One cannot use Filter() because this would actually filter all the comments // that are "below" this AST node as well, e.g. in its children. We only want the comments on the node itself. // Therefore we must convert the CommentMap back into an actual map to access the stored entry for the node. - comments, ok := (map[ast.Node][]*ast.CommentGroup)(this.CommentMap)[astNode] + comments, ok := (map[ast.Node][]*ast.CommentGroup)(g.CommentMap)[astNode] if !ok { return } @@ -177,27 +177,27 @@ func (this *GoLanguageFrontend) handleComments(node *cpg.Node, astNode ast.Node) if comment != "" { node.SetComment(comment) - this.LogTrace("Comments: %+v", comment) + g.LogTrace("Comments: %+v", comment) } } // handleDecl parses an [ast.Decl]. Note, that in a "Decl", one or more actual // declarations can be found. Therefore, this function returns a slice of // [cpg.Declaration]. -func (this *GoLanguageFrontend) handleDecl(fset *token.FileSet, decl ast.Decl) (decls []*cpg.Declaration) { - this.LogTrace("Handling declaration (%T): %+v", decl, decl) +func (g *GoLanguageFrontend) handleDecl(fset *token.FileSet, decl ast.Decl) (decls []*cpg.Declaration) { + g.LogTrace("Handling declaration (%T): %+v", decl, decl) decls = []*cpg.Declaration{} switch v := decl.(type) { case *ast.FuncDecl: // There can be only a single function declaration - decls = append(decls, (*cpg.Declaration)(this.handleFuncDecl(fset, v))) + decls = append(decls, (*cpg.Declaration)(g.handleFuncDecl(fset, v))) case *ast.GenDecl: // GenDecl can hold multiple declarations - decls = this.handleGenDecl(fset, v) + decls = g.handleGenDecl(fset, v) default: - this.LogError("Not parsing declaration of type %T yet: %+v", v, v) + g.LogError("Not parsing declaration of type %T yet: %+v", v, v) // TODO: Return a ProblemDeclaration } @@ -205,34 +205,34 @@ func (this *GoLanguageFrontend) handleDecl(fset *token.FileSet, decl ast.Decl) ( for _, d := range decls { // TODO: This is problematic because we are assigning it the wrong node if d != nil { - this.handleComments((*cpg.Node)(d), decl) + g.handleComments((*cpg.Node)(d), decl) } } return } -func (this *GoLanguageFrontend) handleFuncDecl(fset *token.FileSet, funcDecl *ast.FuncDecl) *cpg.FunctionDeclaration { - this.LogTrace("Handling func Decl: %+v", *funcDecl) +func (g *GoLanguageFrontend) handleFuncDecl(fset *token.FileSet, funcDecl *ast.FuncDecl) *cpg.FunctionDeclaration { + g.LogTrace("Handling func Decl: %+v", *funcDecl) - var scope = this.GetScopeManager() + var scope = g.GetScopeManager() var receiver *cpg.VariableDeclaration var f *cpg.FunctionDeclaration if funcDecl.Recv != nil { - m := this.NewMethodDeclaration(fset, funcDecl, funcDecl.Name.Name) + m := g.NewMethodDeclaration(fset, funcDecl, funcDecl.Name.Name) // TODO: why is this a list? var recv = funcDecl.Recv.List[0] - var recordType = this.handleType(fset, recv.Type) + var recordType = g.handleType(fset, recv.Type) // The name of the Go receiver is optional. In fact, if the name is not // specified we probably do not need any receiver variable at all, // because the syntax is only there to ensure that this method is part // of the struct, but it is not modifying the receiver. if len(recv.Names) > 0 { - receiver = this.NewVariableDeclaration(fset, nil, recv.Names[0].Name) + receiver = g.NewVariableDeclaration(fset, nil, recv.Names[0].Name) // TODO: should we use the FQN here? FQNs are a mess in the CPG... receiver.SetType(recordType) @@ -248,8 +248,8 @@ func (this *GoLanguageFrontend) handleFuncDecl(fset *token.FileSet, funcDecl *as // TODO: this will only find methods within the current translation unit // this is a limitation that we have for C++ as well - record, err := this.GetScopeManager().GetRecordForName( - this.GetScopeManager().GetCurrentScope(), + record, err := g.GetScopeManager().GetRecordForName( + g.GetScopeManager().GetCurrentScope(), recordName) if err != nil { @@ -262,7 +262,7 @@ func (this *GoLanguageFrontend) handleFuncDecl(fset *token.FileSet, funcDecl *as // marked as AST and in Go a method is not part of the struct's AST but is declared // outside. In the future, we need to differentiate between just the associated members // of the class and the pure AST nodes declared in the struct itself - this.LogTrace("Record: %+v", record) + g.LogTrace("Record: %+v", record) err = record.AddMethod(m) if err != nil { @@ -280,39 +280,39 @@ func (this *GoLanguageFrontend) handleFuncDecl(fset *token.FileSet, funcDecl *as localNameOnly = true } - f = this.NewFunctionDeclaration(fset, funcDecl, funcDecl.Name.Name, "", localNameOnly) + f = g.NewFunctionDeclaration(fset, funcDecl, funcDecl.Name.Name, "", localNameOnly) } // enter scope for function scope.EnterScope((*cpg.Node)(f)) if receiver != nil { - this.LogTrace("Adding receiver %s", (*cpg.Node)(receiver).GetName()) + g.LogTrace("Adding receiver %s", (*cpg.Node)(receiver).GetName()) // add the receiver do the scope manager, so we can resolve the receiver value - this.GetScopeManager().AddDeclaration((*cpg.Declaration)(receiver)) + g.GetScopeManager().AddDeclaration((*cpg.Declaration)(receiver)) } - var t *cpg.Type = this.handleType(fset, funcDecl.Type) + var t *cpg.Type = g.handleType(fset, funcDecl.Type) var returnTypes []*cpg.Type = []*cpg.Type{} if funcDecl.Type.Results != nil { for _, returnVariable := range funcDecl.Type.Results.List { - returnTypes = append(returnTypes, this.handleType(fset, returnVariable.Type)) + returnTypes = append(returnTypes, g.handleType(fset, returnVariable.Type)) // if the function has named return variables, be sure to declare them as well if returnVariable.Names != nil { - p := this.NewVariableDeclaration(fset, returnVariable, returnVariable.Names[0].Name) + p := g.NewVariableDeclaration(fset, returnVariable, returnVariable.Names[0].Name) - p.SetType(this.handleType(fset, returnVariable.Type)) + p.SetType(g.handleType(fset, returnVariable.Type)) // add parameter to scope - this.GetScopeManager().AddDeclaration((*cpg.Declaration)(p)) + g.GetScopeManager().AddDeclaration((*cpg.Declaration)(p)) } } } - this.LogTrace("Function has type %s", t.GetName()) + g.LogTrace("Function has type %s", t.GetName()) f.SetType(t) f.SetReturnTypes(returnTypes) @@ -322,7 +322,7 @@ func (this *GoLanguageFrontend) handleFuncDecl(fset *token.FileSet, funcDecl *as // go, since we do not have a 'this', but rather a named receiver for _, param := range funcDecl.Type.Params.List { - this.LogTrace("Parsing param: %+v", param) + g.LogTrace("Parsing param: %+v", param) var name string // Somehow parameters end up having no name sometimes, have not fully understood why. @@ -338,16 +338,16 @@ func (this *GoLanguageFrontend) handleFuncDecl(fset *token.FileSet, funcDecl *as name = "" } } else { - this.LogError("Some param has no name, which is a bit weird: %+v", param) + g.LogError("Some param has no name, which is a bit weird: %+v", param) } - p := this.NewParamVariableDeclaration(fset, param, name) + p := g.NewParamVariableDeclaration(fset, param, name) // Check for varargs. In this case we want to parse the element type // (and make it an array afterwards) if ell, ok := param.Type.(*ast.Ellipsis); ok { p.SetVariadic(true) - var t = this.handleType(fset, ell.Elt) + var t = g.handleType(fset, ell.Elt) var i = jnigi.NewObjectRef(cpg.PointerOriginClass) err := env.GetStaticField(cpg.PointerOriginClass, "ARRAY", i) @@ -357,19 +357,19 @@ func (this *GoLanguageFrontend) handleFuncDecl(fset *token.FileSet, funcDecl *as p.SetType(t.Reference(i)) } else { - p.SetType(this.handleType(fset, param.Type)) + p.SetType(g.handleType(fset, param.Type)) } // add parameter to scope - this.GetScopeManager().AddDeclaration((*cpg.Declaration)(p)) + g.GetScopeManager().AddDeclaration((*cpg.Declaration)(p)) - this.handleComments((*cpg.Node)(p), param) + g.handleComments((*cpg.Node)(p), param) } - this.LogTrace("Parsing function body of %s", (*cpg.Node)(f).GetName()) + g.LogTrace("Parsing function body of %s", (*cpg.Node)(f).GetName()) // parse body - s := this.handleBlockStmt(fset, funcDecl.Body) + s := g.handleBlockStmt(fset, funcDecl.Body) err := f.SetBody((*cpg.Statement)(s)) if err != nil { @@ -387,19 +387,19 @@ func (this *GoLanguageFrontend) handleFuncDecl(fset *token.FileSet, funcDecl *as return f } -func (this *GoLanguageFrontend) handleGenDecl(fset *token.FileSet, genDecl *ast.GenDecl) (decls []*cpg.Declaration) { +func (g *GoLanguageFrontend) handleGenDecl(fset *token.FileSet, genDecl *ast.GenDecl) (decls []*cpg.Declaration) { decls = []*cpg.Declaration{} for _, spec := range genDecl.Specs { switch v := spec.(type) { case *ast.ValueSpec: - decls = append(decls, this.handleValueSpec(fset, v)...) + decls = append(decls, g.handleValueSpec(fset, v)...) case *ast.TypeSpec: - decls = append(decls, this.handleTypeSpec(fset, v)) + decls = append(decls, g.handleTypeSpec(fset, v)) case *ast.ImportSpec: // Somehow these end up duplicate in the AST, so do not handle them here default: - this.LogError("Not parsing specification of type %T yet: %+v", v, v) + g.LogError("Not parsing specification of type %T yet: %+v", v, v) } } @@ -409,16 +409,16 @@ func (this *GoLanguageFrontend) handleGenDecl(fset *token.FileSet, genDecl *ast. // handleValueSpec handles parsing of an [ast.ValueSpec], which is a variable // declaration. Since this can potentially declare multiple variables with one // "spec", this returns a slice of [cpg.Declaration]. -func (this *GoLanguageFrontend) handleValueSpec(fset *token.FileSet, valueDecl *ast.ValueSpec) (decls []*cpg.Declaration) { +func (g *GoLanguageFrontend) handleValueSpec(fset *token.FileSet, valueDecl *ast.ValueSpec) (decls []*cpg.Declaration) { decls = []*cpg.Declaration{} // We need to declare one variable for each name for idx, ident := range valueDecl.Names { - d := this.NewVariableDeclaration(fset, valueDecl, ident.Name) + d := g.NewVariableDeclaration(fset, valueDecl, ident.Name) // Handle the type (if its there) if valueDecl.Type != nil { - t := this.handleType(fset, valueDecl.Type) + t := g.handleType(fset, valueDecl.Type) d.SetType(t) } @@ -427,12 +427,12 @@ func (this *GoLanguageFrontend) handleValueSpec(fset *token.FileSet, valueDecl * // must match the names lenValues := len(valueDecl.Values) if lenValues != 0 && lenValues != len(valueDecl.Names) { - this.LogError("Number of initializers does not match number of names. Initializers might be incomplete") + g.LogError("Number of initializers does not match number of names. Initializers might be incomplete") } // The initializer is in the "Values" slice with the respective index if len(valueDecl.Values) > idx { - var expr = this.handleExpr(fset, valueDecl.Values[idx]) + var expr = g.handleExpr(fset, valueDecl.Values[idx]) err := d.SetInitializer(expr) if err != nil { @@ -448,28 +448,28 @@ func (this *GoLanguageFrontend) handleValueSpec(fset *token.FileSet, valueDecl * // handleTypeSpec handles an [ast.TypeSec], which defines either a struct or an // interface. It returns a single [cpg.Declaration]. -func (this *GoLanguageFrontend) handleTypeSpec(fset *token.FileSet, typeDecl *ast.TypeSpec) *cpg.Declaration { - err := this.LogInfo("Type specifier with name %s and type (%T, %+v)", typeDecl.Name.Name, typeDecl.Type, typeDecl.Type) +func (g *GoLanguageFrontend) handleTypeSpec(fset *token.FileSet, typeDecl *ast.TypeSpec) *cpg.Declaration { + err := g.LogInfo("Type specifier with name %s and type (%T, %+v)", typeDecl.Name.Name, typeDecl.Type, typeDecl.Type) if err != nil { log.Fatal(err) } switch v := typeDecl.Type.(type) { case *ast.StructType: - return (*cpg.Declaration)(this.handleStructTypeSpec(fset, typeDecl, v)) + return (*cpg.Declaration)(g.handleStructTypeSpec(fset, typeDecl, v)) case *ast.InterfaceType: - return (*cpg.Declaration)(this.handleInterfaceTypeSpec(fset, typeDecl, v)) + return (*cpg.Declaration)(g.handleInterfaceTypeSpec(fset, typeDecl, v)) } return nil } -func (this *GoLanguageFrontend) handleImportSpec(fset *token.FileSet, importSpec *ast.ImportSpec) *cpg.Declaration { - this.LogTrace("Import specifier with: %+v)", *importSpec) +func (g *GoLanguageFrontend) handleImportSpec(fset *token.FileSet, importSpec *ast.ImportSpec) *cpg.Declaration { + g.LogTrace("Import specifier with: %+v)", *importSpec) - i := this.NewIncludeDeclaration(fset, importSpec, getImportName(importSpec)) + i := g.NewIncludeDeclaration(fset, importSpec, getImportName(importSpec)) - var scope = this.GetScopeManager() + var scope = g.GetScopeManager() i.SetFilename(importSpec.Path.Value[1 : len(importSpec.Path.Value)-1]) @@ -481,14 +481,14 @@ func (this *GoLanguageFrontend) handleImportSpec(fset *token.FileSet, importSpec return (*cpg.Declaration)(i) } -func (this *GoLanguageFrontend) handleIdentAsName(ident *ast.Ident) string { +func (g *GoLanguageFrontend) handleIdentAsName(ident *ast.Ident) string { return ident.Name } -func (this *GoLanguageFrontend) handleStructTypeSpec(fset *token.FileSet, typeDecl *ast.TypeSpec, structType *ast.StructType) *cpg.RecordDeclaration { - r := this.NewRecordDeclaration(fset, typeDecl, this.handleIdentAsName(typeDecl.Name), "struct") +func (g *GoLanguageFrontend) handleStructTypeSpec(fset *token.FileSet, typeDecl *ast.TypeSpec, structType *ast.StructType) *cpg.RecordDeclaration { + r := g.NewRecordDeclaration(fset, typeDecl, g.handleIdentAsName(typeDecl.Name), "struct") - var scope = this.GetScopeManager() + var scope = g.GetScopeManager() scope.EnterScope((*cpg.Node)(r)) @@ -500,23 +500,23 @@ func (this *GoLanguageFrontend) handleStructTypeSpec(fset *token.FileSet, typeDe // by its type, it could make sense to name the field according to the type var name string - t := this.handleType(fset, field.Type) + t := g.handleType(fset, field.Type) if field.Names == nil { // retrieve the root type name var typeName = t.GetRoot().GetName().ToString() - this.LogTrace("Handling embedded field of type %s", typeName) + g.LogTrace("Handling embedded field of type %s", typeName) name = typeName } else { - this.LogTrace("Handling field %s", field.Names[0].Name) + g.LogTrace("Handling field %s", field.Names[0].Name) // TODO: Multiple names? name = field.Names[0].Name } - f := this.NewFieldDeclaration(fset, field, name) + f := g.NewFieldDeclaration(fset, field, name) f.SetType(t) @@ -529,28 +529,28 @@ func (this *GoLanguageFrontend) handleStructTypeSpec(fset *token.FileSet, typeDe return r } -func (this *GoLanguageFrontend) handleInterfaceTypeSpec(fset *token.FileSet, typeDecl *ast.TypeSpec, interfaceType *ast.InterfaceType) *cpg.RecordDeclaration { - r := this.NewRecordDeclaration(fset, typeDecl, this.handleIdentAsName(typeDecl.Name), "interface") +func (g *GoLanguageFrontend) handleInterfaceTypeSpec(fset *token.FileSet, typeDecl *ast.TypeSpec, interfaceType *ast.InterfaceType) *cpg.RecordDeclaration { + r := g.NewRecordDeclaration(fset, typeDecl, g.handleIdentAsName(typeDecl.Name), "interface") - var scope = this.GetScopeManager() + var scope = g.GetScopeManager() scope.EnterScope((*cpg.Node)(r)) if !interfaceType.Incomplete { for _, method := range interfaceType.Methods.List { - t := this.handleType(fset, method.Type) + t := g.handleType(fset, method.Type) // Even though this list is called "Methods", it contains all kinds // of things, so we need to proceed with caution. Only if the // "method" actually has a name, we declare a new method // declaration. if len(method.Names) > 0 { - m := this.NewMethodDeclaration(fset, method, method.Names[0].Name) + m := g.NewMethodDeclaration(fset, method, method.Names[0].Name) m.SetType(t) scope.AddDeclaration((*cpg.Declaration)(m)) } else { - this.LogTrace("Adding %s as super class of interface %s", t.GetName(), (*cpg.Node)(r).GetName()) + g.LogTrace("Adding %s as super class of interface %s", t.GetName(), (*cpg.Node)(r).GetName()) // Otherwise, it contains either types or interfaces. For now we // hope that it only has interfaces. We consider embedded // interfaces as sort of super types for this interface. @@ -564,18 +564,18 @@ func (this *GoLanguageFrontend) handleInterfaceTypeSpec(fset *token.FileSet, typ return r } -func (this *GoLanguageFrontend) handleBlockStmt(fset *token.FileSet, blockStmt *ast.BlockStmt) *cpg.CompoundStatement { - this.LogTrace("Handling block statement: %+v", *blockStmt) +func (g *GoLanguageFrontend) handleBlockStmt(fset *token.FileSet, blockStmt *ast.BlockStmt) *cpg.CompoundStatement { + g.LogTrace("Handling block statement: %+v", *blockStmt) - c := this.NewCompoundStatement(fset, blockStmt) + c := g.NewCompoundStatement(fset, blockStmt) // enter scope - this.GetScopeManager().EnterScope((*cpg.Node)(c)) + g.GetScopeManager().EnterScope((*cpg.Node)(c)) for _, stmt := range blockStmt.List { var s *cpg.Statement - s = this.handleStmt(fset, stmt, blockStmt) + s = g.handleStmt(fset, stmt, blockStmt) if s != nil { // add statement @@ -584,36 +584,36 @@ func (this *GoLanguageFrontend) handleBlockStmt(fset *token.FileSet, blockStmt * } // leave scope - this.GetScopeManager().LeaveScope((*cpg.Node)(c)) + g.GetScopeManager().LeaveScope((*cpg.Node)(c)) return c } -func (this *GoLanguageFrontend) handleForStmt(fset *token.FileSet, forStmt *ast.ForStmt) *cpg.ForStatement { - this.LogTrace("Handling for statement: %+v", *forStmt) +func (g *GoLanguageFrontend) handleForStmt(fset *token.FileSet, forStmt *ast.ForStmt) *cpg.ForStatement { + g.LogTrace("Handling for statement: %+v", *forStmt) - f := this.NewForStatement(fset, forStmt) + f := g.NewForStatement(fset, forStmt) - var scope = this.GetScopeManager() + var scope = g.GetScopeManager() scope.EnterScope((*cpg.Node)(f)) if forStmt.Init != nil { - initStatement := this.handleStmt(fset, forStmt.Init, forStmt) + initStatement := g.handleStmt(fset, forStmt.Init, forStmt) f.SetInitializerStatement(initStatement) } if forStmt.Cond != nil { - condition := this.handleExpr(fset, forStmt.Cond) + condition := g.handleExpr(fset, forStmt.Cond) f.SetCondition(condition) } if forStmt.Post != nil { - iter := this.handleStmt(fset, forStmt.Post, forStmt) + iter := g.handleStmt(fset, forStmt.Post, forStmt) f.SetIterationStatement(iter) } - if body := this.handleStmt(fset, forStmt.Body, forStmt); body != nil { + if body := g.handleStmt(fset, forStmt.Body, forStmt); body != nil { f.SetStatement(body) } @@ -622,25 +622,25 @@ func (this *GoLanguageFrontend) handleForStmt(fset *token.FileSet, forStmt *ast. return f } -func (this *GoLanguageFrontend) handleRangeStmt(fset *token.FileSet, rangeStmt *ast.RangeStmt) *cpg.ForEachStatement { - this.LogTrace("Handling range statement: %+v", *rangeStmt) +func (g *GoLanguageFrontend) handleRangeStmt(fset *token.FileSet, rangeStmt *ast.RangeStmt) *cpg.ForEachStatement { + g.LogTrace("Handling range statement: %+v", *rangeStmt) - f := this.NewForEachStatement(fset, rangeStmt) + f := g.NewForEachStatement(fset, rangeStmt) - var scope = this.GetScopeManager() + var scope = g.GetScopeManager() scope.EnterScope((*cpg.Node)(f)) // TODO: Support other use cases that do not use DEFINE if rangeStmt.Tok == token.DEFINE { - stmt := this.NewDeclarationStatement(fset, rangeStmt) + stmt := g.NewDeclarationStatement(fset, rangeStmt) // TODO: not really the best way to deal with this // TODO: key type is always int. we could set this var keyName = rangeStmt.Key.(*ast.Ident).Name - key := this.NewVariableDeclaration(fset, rangeStmt.Key, keyName) - this.GetScopeManager().AddDeclaration((*cpg.Declaration)(key)) + key := g.NewVariableDeclaration(fset, rangeStmt.Key, keyName) + g.GetScopeManager().AddDeclaration((*cpg.Declaration)(key)) stmt.AddToPropertyEdgeDeclaration((*cpg.Declaration)(key)) if rangeStmt.Value != nil { @@ -648,18 +648,18 @@ func (this *GoLanguageFrontend) handleRangeStmt(fset *token.FileSet, rangeStmt * // TODO: key type is always int. we could set this var valueName = rangeStmt.Value.(*ast.Ident).Name - value := this.NewVariableDeclaration(fset, rangeStmt.Key, valueName) - this.GetScopeManager().AddDeclaration((*cpg.Declaration)(value)) + value := g.NewVariableDeclaration(fset, rangeStmt.Key, valueName) + g.GetScopeManager().AddDeclaration((*cpg.Declaration)(value)) stmt.AddToPropertyEdgeDeclaration((*cpg.Declaration)(value)) } f.SetVariable((*cpg.Statement)(stmt)) } - iterable := (*cpg.Statement)(this.handleExpr(fset, rangeStmt.X)) + iterable := (*cpg.Statement)(g.handleExpr(fset, rangeStmt.X)) f.SetIterable(iterable) - body := this.handleStmt(fset, rangeStmt.Body, rangeStmt) + body := g.handleStmt(fset, rangeStmt.Body, rangeStmt) f.SetStatement(body) scope.LeaveScope((*cpg.Node)(f)) @@ -667,13 +667,13 @@ func (this *GoLanguageFrontend) handleRangeStmt(fset *token.FileSet, rangeStmt * return f } -func (this *GoLanguageFrontend) handleReturnStmt(fset *token.FileSet, returnStmt *ast.ReturnStmt) *cpg.ReturnStatement { - this.LogTrace("Handling return statement: %+v", *returnStmt) +func (g *GoLanguageFrontend) handleReturnStmt(fset *token.FileSet, returnStmt *ast.ReturnStmt) *cpg.ReturnStatement { + g.LogTrace("Handling return statement: %+v", *returnStmt) - r := this.NewReturnStatement(fset, returnStmt) + r := g.NewReturnStatement(fset, returnStmt) if returnStmt.Results != nil && len(returnStmt.Results) > 0 { - e := this.handleExpr(fset, returnStmt.Results[0]) + e := g.handleExpr(fset, returnStmt.Results[0]) // TODO: parse more than one result expression @@ -687,8 +687,8 @@ func (this *GoLanguageFrontend) handleReturnStmt(fset *token.FileSet, returnStmt return r } -func (this *GoLanguageFrontend) handleIncDecStmt(fset *token.FileSet, incDecStmt *ast.IncDecStmt) *cpg.UnaryOperator { - this.LogTrace("Handling decimal increment statement: %+v", *incDecStmt) +func (g *GoLanguageFrontend) handleIncDecStmt(fset *token.FileSet, incDecStmt *ast.IncDecStmt) *cpg.UnaryOperator { + g.LogTrace("Handling decimal increment statement: %+v", *incDecStmt) var opCode string if incDecStmt.Tok == token.INC { @@ -699,119 +699,121 @@ func (this *GoLanguageFrontend) handleIncDecStmt(fset *token.FileSet, incDecStmt opCode = "--" } - u := this.NewUnaryOperator(fset, incDecStmt, opCode, true, false) + u := g.NewUnaryOperator(fset, incDecStmt, opCode, true, false) - if input := this.handleExpr(fset, incDecStmt.X); input != nil { + if input := g.handleExpr(fset, incDecStmt.X); input != nil { u.SetInput(input) } return u } -func (this *GoLanguageFrontend) handleStmt(fset *token.FileSet, stmt ast.Stmt, parent ast.Stmt) (s *cpg.Statement) { - this.LogTrace("Handling statement (%T): %+v", stmt, stmt) +func (g *GoLanguageFrontend) handleStmt(fset *token.FileSet, stmt ast.Stmt, parent ast.Stmt) (s *cpg.Statement) { + g.LogTrace("Handling statement (%T): %+v", stmt, stmt) switch v := stmt.(type) { case *ast.ExprStmt: // in our cpg, each expression is also a statement, // so we do not need an expression statement wrapper - s = (*cpg.Statement)(this.handleExpr(fset, v.X)) + s = (*cpg.Statement)(g.handleExpr(fset, v.X)) case *ast.AssignStmt: - s = (*cpg.Statement)(this.handleAssignStmt(fset, v, parent)) + s = (*cpg.Statement)(g.handleAssignStmt(fset, v, parent)) case *ast.DeclStmt: - s = (*cpg.Statement)(this.handleDeclStmt(fset, v)) + s = (*cpg.Statement)(g.handleDeclStmt(fset, v)) case *ast.GoStmt: - s = (*cpg.Statement)(this.handleGoStmt(fset, v)) + s = (*cpg.Statement)(g.handleGoStmt(fset, v)) + case *ast.DeferStmt: + s = (*cpg.Statement)(g.handleDeferStmt(fset, v)) case *ast.IfStmt: - s = (*cpg.Statement)(this.handleIfStmt(fset, v)) + s = (*cpg.Statement)(g.handleIfStmt(fset, v)) case *ast.SwitchStmt: - s = (*cpg.Statement)(this.handleSwitchStmt(fset, v)) + s = (*cpg.Statement)(g.handleSwitchStmt(fset, v)) case *ast.CaseClause: - s = (*cpg.Statement)(this.handleCaseClause(fset, v)) + s = (*cpg.Statement)(g.handleCaseClause(fset, v)) case *ast.BlockStmt: - s = (*cpg.Statement)(this.handleBlockStmt(fset, v)) + s = (*cpg.Statement)(g.handleBlockStmt(fset, v)) case *ast.ForStmt: - s = (*cpg.Statement)(this.handleForStmt(fset, v)) + s = (*cpg.Statement)(g.handleForStmt(fset, v)) case *ast.RangeStmt: - s = (*cpg.Statement)(this.handleRangeStmt(fset, v)) + s = (*cpg.Statement)(g.handleRangeStmt(fset, v)) case *ast.ReturnStmt: - s = (*cpg.Statement)(this.handleReturnStmt(fset, v)) + s = (*cpg.Statement)(g.handleReturnStmt(fset, v)) case *ast.IncDecStmt: - s = (*cpg.Statement)(this.handleIncDecStmt(fset, v)) + s = (*cpg.Statement)(g.handleIncDecStmt(fset, v)) default: msg := fmt.Sprintf("Not parsing statement of type %T yet: %s", v, code(fset, v)) - this.LogError(msg) - s = (*cpg.Statement)(this.NewProblemExpression(fset, v, msg)) + g.LogError(msg) + s = (*cpg.Statement)(g.NewProblemExpression(fset, v, msg)) } if s != nil { - this.handleComments((*cpg.Node)(s), stmt) + g.handleComments((*cpg.Node)(s), stmt) } return } -func (this *GoLanguageFrontend) handleExpr(fset *token.FileSet, expr ast.Expr) (e *cpg.Expression) { - this.LogTrace("Handling expression (%T): %+v", expr, expr) +func (g *GoLanguageFrontend) handleExpr(fset *token.FileSet, expr ast.Expr) (e *cpg.Expression) { + g.LogTrace("Handling expression (%T): %+v", expr, expr) switch v := expr.(type) { case *ast.CallExpr: - e = (*cpg.Expression)(this.handleCallExpr(fset, v)) + e = (*cpg.Expression)(g.handleCallExpr(fset, v)) case *ast.IndexExpr: - e = (*cpg.Expression)(this.handleIndexExpr(fset, v)) + e = (*cpg.Expression)(g.handleIndexExpr(fset, v)) case *ast.BinaryExpr: - e = (*cpg.Expression)(this.handleBinaryExpr(fset, v)) + e = (*cpg.Expression)(g.handleBinaryExpr(fset, v)) case *ast.UnaryExpr: - e = (*cpg.Expression)(this.handleUnaryExpr(fset, v)) + e = (*cpg.Expression)(g.handleUnaryExpr(fset, v)) case *ast.StarExpr: - e = (*cpg.Expression)(this.handleStarExpr(fset, v)) + e = (*cpg.Expression)(g.handleStarExpr(fset, v)) case *ast.SelectorExpr: - e = (*cpg.Expression)(this.handleSelectorExpr(fset, v)) + e = (*cpg.Expression)(g.handleSelectorExpr(fset, v)) case *ast.SliceExpr: - e = (*cpg.Expression)(this.handleSliceExpr(fset, v)) + e = (*cpg.Expression)(g.handleSliceExpr(fset, v)) case *ast.KeyValueExpr: - e = (*cpg.Expression)(this.handleKeyValueExpr(fset, v)) + e = (*cpg.Expression)(g.handleKeyValueExpr(fset, v)) case *ast.BasicLit: - e = (*cpg.Expression)(this.handleBasicLit(fset, v)) + e = (*cpg.Expression)(g.handleBasicLit(fset, v)) case *ast.CompositeLit: - e = (*cpg.Expression)(this.handleCompositeLit(fset, v)) + e = (*cpg.Expression)(g.handleCompositeLit(fset, v)) case *ast.FuncLit: - e = (*cpg.Expression)(this.handleFuncLit(fset, v)) + e = (*cpg.Expression)(g.handleFuncLit(fset, v)) case *ast.Ident: - e = (*cpg.Expression)(this.handleIdent(fset, v)) + e = (*cpg.Expression)(g.handleIdent(fset, v)) case *ast.TypeAssertExpr: - e = (*cpg.Expression)(this.handleTypeAssertExpr(fset, v)) + e = (*cpg.Expression)(g.handleTypeAssertExpr(fset, v)) case *ast.ParenExpr: - e = this.handleExpr(fset, v.X) + e = g.handleExpr(fset, v.X) default: msg := fmt.Sprintf("Not parsing expression of type %T yet: %s", v, code(fset, v)) - this.LogError(msg) - e = (*cpg.Expression)(this.NewProblemExpression(fset, v, msg)) + g.LogError(msg) + e = (*cpg.Expression)(g.NewProblemExpression(fset, v, msg)) } if e != nil { - this.handleComments((*cpg.Node)(e), expr) + g.handleComments((*cpg.Node)(e), expr) } return } -func (this *GoLanguageFrontend) handleAssignStmt(fset *token.FileSet, assignStmt *ast.AssignStmt, parent ast.Stmt) (expr *cpg.Expression) { - this.LogTrace("Handling assignment statement: %+v", assignStmt) +func (g *GoLanguageFrontend) handleAssignStmt(fset *token.FileSet, assignStmt *ast.AssignStmt, parent ast.Stmt) (expr *cpg.Expression) { + g.LogTrace("Handling assignment statement: %+v", assignStmt) - this.LogDebug("Parent: %#v", parent) + g.LogDebug("Parent: %#v", parent) var rhs = []*cpg.Expression{} var lhs = []*cpg.Expression{} for _, expr := range assignStmt.Lhs { - lhs = append(lhs, this.handleExpr(fset, expr)) + lhs = append(lhs, g.handleExpr(fset, expr)) } for _, expr := range assignStmt.Rhs { - rhs = append(rhs, this.handleExpr(fset, expr)) + rhs = append(rhs, g.handleExpr(fset, expr)) } - a := this.NewAssignExpression(fset, assignStmt, "=") + a := g.NewAssignExpression(fset, assignStmt, "=") a.SetLHS(lhs) a.SetRHS(rhs) @@ -828,19 +830,19 @@ func (this *GoLanguageFrontend) handleAssignStmt(fset *token.FileSet, assignStmt return } -func (this *GoLanguageFrontend) handleDeclStmt(fset *token.FileSet, declStmt *ast.DeclStmt) (expr *cpg.Expression) { - this.LogTrace("Handling declaration statement: %+v", *declStmt) +func (g *GoLanguageFrontend) handleDeclStmt(fset *token.FileSet, declStmt *ast.DeclStmt) (expr *cpg.Expression) { + g.LogTrace("Handling declaration statement: %+v", *declStmt) // Lets create a variable declaration (wrapped with a declaration stmt) with // this, because we define the variable here - stmt := this.NewDeclarationStatement(fset, declStmt) + stmt := g.NewDeclarationStatement(fset, declStmt) - decls := this.handleDecl(fset, declStmt.Decl) + decls := g.handleDecl(fset, declStmt.Decl) // Loop over the declarations and add them to the scope as well as the statement. for _, d := range decls { stmt.AddToPropertyEdgeDeclaration(d) - this.GetScopeManager().AddDeclaration(d) + g.GetScopeManager().AddDeclaration(d) } return (*cpg.Expression)(stmt) @@ -849,42 +851,55 @@ func (this *GoLanguageFrontend) handleDeclStmt(fset *token.FileSet, declStmt *as // handleGoStmt handles the `go` statement, which is a special keyword in go // that starts the supplied call expression in a separate Go routine. We cannot // model this 1:1, so we basically we create a call expression to a built-in call. -func (this *GoLanguageFrontend) handleGoStmt(fset *token.FileSet, goStmt *ast.GoStmt) (expr *cpg.Expression) { - this.LogTrace("Handling go statement: %+v", *goStmt) +func (g *GoLanguageFrontend) handleGoStmt(fset *token.FileSet, goStmt *ast.GoStmt) (expr *cpg.Expression) { + g.LogTrace("Handling go statement: %+v", *goStmt) - ref := (*cpg.Expression)(this.NewDeclaredReferenceExpression(fset, nil, "go")) + ref := (*cpg.Expression)(g.NewDeclaredReferenceExpression(fset, nil, "go")) - call := this.NewCallExpression(fset, goStmt, ref, "go") - call.AddArgument(this.handleCallExpr(fset, goStmt.Call)) + call := g.NewCallExpression(fset, goStmt, ref, "go") + call.AddArgument(g.handleCallExpr(fset, goStmt.Call)) return (*cpg.Expression)(call) } -func (this *GoLanguageFrontend) handleIfStmt(fset *token.FileSet, ifStmt *ast.IfStmt) (expr *cpg.Expression) { - this.LogTrace("Handling if statement: %+v", *ifStmt) +// handleDeferStmt handles the `defer` statement, which is a special keyword in go +// that the supplied callee is executed once the function it is called in exists. +// We cannot model this 1:1, so we basically we create a call expression to a built-in call. +// We adjust the EOG of the call later in an extra pass. +func (g *GoLanguageFrontend) handleDeferStmt(fset *token.FileSet, deferStmt *ast.DeferStmt) (expr *cpg.Expression) { + g.LogTrace("Handling defer statement: %+v", *deferStmt) - stmt := this.NewIfStatement(fset, ifStmt) + call := g.NewUnaryOperator(fset, deferStmt, "defer", false, true) + call.SetInput(g.handleCallExpr(fset, deferStmt.Call)) - var scope = this.GetScopeManager() + return (*cpg.Expression)(call) +} + +func (g *GoLanguageFrontend) handleIfStmt(fset *token.FileSet, ifStmt *ast.IfStmt) (expr *cpg.Expression) { + g.LogTrace("Handling if statement: %+v", *ifStmt) + + stmt := g.NewIfStatement(fset, ifStmt) + + var scope = g.GetScopeManager() scope.EnterScope((*cpg.Node)(stmt)) if ifStmt.Init != nil { - init := this.handleStmt(fset, ifStmt.Init, ifStmt) + init := g.handleStmt(fset, ifStmt.Init, ifStmt) stmt.SetInitializerStatement(init) } - cond := this.handleExpr(fset, ifStmt.Cond) + cond := g.handleExpr(fset, ifStmt.Cond) stmt.SetCondition(cond) - then := this.handleBlockStmt(fset, ifStmt.Body) + then := g.handleBlockStmt(fset, ifStmt.Body) // Somehow this can be nil-ish? if !then.IsNil() { stmt.SetThenStatement((*cpg.Statement)(then)) } if ifStmt.Else != nil { - els := this.handleStmt(fset, ifStmt.Else, ifStmt) + els := g.handleStmt(fset, ifStmt.Else, ifStmt) stmt.SetElseStatement((*cpg.Statement)(els)) } @@ -893,40 +908,40 @@ func (this *GoLanguageFrontend) handleIfStmt(fset *token.FileSet, ifStmt *ast.If return (*cpg.Expression)(stmt) } -func (this *GoLanguageFrontend) handleSwitchStmt(fset *token.FileSet, switchStmt *ast.SwitchStmt) (expr *cpg.Expression) { - this.LogTrace("Handling switch statement: %+v", *switchStmt) +func (g *GoLanguageFrontend) handleSwitchStmt(fset *token.FileSet, switchStmt *ast.SwitchStmt) (expr *cpg.Expression) { + g.LogTrace("Handling switch statement: %+v", *switchStmt) - s := this.NewSwitchStatement(fset, switchStmt) + s := g.NewSwitchStatement(fset, switchStmt) if switchStmt.Init != nil { - s.SetInitializerStatement(this.handleStmt(fset, switchStmt.Init, switchStmt)) + s.SetInitializerStatement(g.handleStmt(fset, switchStmt.Init, switchStmt)) } if switchStmt.Tag != nil { - s.SetCondition(this.handleExpr(fset, switchStmt.Tag)) + s.SetCondition(g.handleExpr(fset, switchStmt.Tag)) } - s.SetStatement((*cpg.Statement)(this.handleBlockStmt(fset, switchStmt.Body))) // should only contain case clauses + s.SetStatement((*cpg.Statement)(g.handleBlockStmt(fset, switchStmt.Body))) // should only contain case clauses return (*cpg.Expression)(s) } -func (this *GoLanguageFrontend) handleCaseClause(fset *token.FileSet, caseClause *ast.CaseClause) (expr *cpg.Expression) { - this.LogTrace("Handling case clause: %+v", *caseClause) +func (g *GoLanguageFrontend) handleCaseClause(fset *token.FileSet, caseClause *ast.CaseClause) (expr *cpg.Expression) { + g.LogTrace("Handling case clause: %+v", *caseClause) var s *cpg.Statement if caseClause.List == nil { - s = (*cpg.Statement)(this.NewDefaultStatement(fset, nil)) + s = (*cpg.Statement)(g.NewDefaultStatement(fset, nil)) } else { - c := this.NewCaseStatement(fset, caseClause) - c.SetCaseExpression(this.handleExpr(fset, caseClause.List[0])) + c := g.NewCaseStatement(fset, caseClause) + c.SetCaseExpression(g.handleExpr(fset, caseClause.List[0])) s = (*cpg.Statement)(c) } // need to find the current block / scope and add the statements to it - block := this.GetScopeManager().GetCurrentBlock() + block := g.GetScopeManager().GetCurrentBlock() // add the case statement if s != nil && block != nil && !block.IsNil() { @@ -934,7 +949,7 @@ func (this *GoLanguageFrontend) handleCaseClause(fset *token.FileSet, caseClause } for _, stmt := range caseClause.Body { - s = this.handleStmt(fset, stmt, caseClause) + s = g.handleStmt(fset, stmt, caseClause) if s != nil && block != nil && !block.IsNil() { // add statement @@ -947,7 +962,7 @@ func (this *GoLanguageFrontend) handleCaseClause(fset *token.FileSet, caseClause return nil } -func (this *GoLanguageFrontend) handleCallExpr(fset *token.FileSet, callExpr *ast.CallExpr) *cpg.Expression { +func (g *GoLanguageFrontend) handleCallExpr(fset *token.FileSet, callExpr *ast.CallExpr) *cpg.Expression { var c *cpg.CallExpression // In Go, regular cast expressions (not type asserts are modelled as calls). @@ -959,20 +974,20 @@ func (this *GoLanguageFrontend) handleCallExpr(fset *token.FileSet, callExpr *as *ast.InterfaceType, *ast.MapType, *ast.ChanType: - this.LogDebug("Handling cast expression: %#v", callExpr) + g.LogDebug("Handling cast expression: %#v", callExpr) - cast := this.NewCastExpression(fset, callExpr) - cast.SetCastType(this.handleType(fset, v)) + cast := g.NewCastExpression(fset, callExpr) + cast.SetCastType(g.handleType(fset, v)) if len(callExpr.Args) > 1 { - cast.SetExpression(this.handleExpr(fset, callExpr.Args[0])) + cast.SetExpression(g.handleExpr(fset, callExpr.Args[0])) } return (*cpg.Expression)(cast) } // parse the Fun field, to see which kind of expression it is - var reference = this.handleExpr(fset, callExpr.Fun) + var reference = g.handleExpr(fset, callExpr.Fun) if reference == nil { return nil @@ -981,9 +996,9 @@ func (this *GoLanguageFrontend) handleCallExpr(fset *token.FileSet, callExpr *as name := reference.GetName().GetLocalName() if name == "new" { - return this.handleNewExpr(fset, callExpr) + return g.handleNewExpr(fset, callExpr) } else if name == "make" { - return this.handleMakeExpr(fset, callExpr) + return g.handleMakeExpr(fset, callExpr) } isMemberExpression, err := (*jnigi.ObjectRef)(reference).IsInstanceOf(env, cpg.MemberExpressionClass) @@ -993,19 +1008,19 @@ func (this *GoLanguageFrontend) handleCallExpr(fset *token.FileSet, callExpr *as } if isMemberExpression { - this.LogTrace("Fun is a member call to %s", name) + g.LogTrace("Fun is a member call to %s", name) - m := this.NewMemberCallExpression(fset, callExpr, reference) + m := g.NewMemberCallExpression(fset, callExpr, reference) c = (*cpg.CallExpression)(m) } else { - this.LogTrace("Handling regular call expression to %s", name) + g.LogTrace("Handling regular call expression to %s", name) - c = this.NewCallExpression(fset, callExpr, reference, name) + c = g.NewCallExpression(fset, callExpr, reference, name) } for _, arg := range callExpr.Args { - e := this.handleExpr(fset, arg) + e := g.handleExpr(fset, arg) if e != nil { c.AddArgument(e) @@ -1015,11 +1030,11 @@ func (this *GoLanguageFrontend) handleCallExpr(fset *token.FileSet, callExpr *as return (*cpg.Expression)(c) } -func (this *GoLanguageFrontend) handleIndexExpr(fset *token.FileSet, indexExpr *ast.IndexExpr) *cpg.ArraySubscriptionExpression { - a := this.NewArraySubscriptionExpression(fset, indexExpr) +func (g *GoLanguageFrontend) handleIndexExpr(fset *token.FileSet, indexExpr *ast.IndexExpr) *cpg.ArraySubscriptionExpression { + a := g.NewArraySubscriptionExpression(fset, indexExpr) - a.SetArrayExpression(this.handleExpr(fset, indexExpr.X)) - a.SetSubscriptExpression(this.handleExpr(fset, indexExpr.Index)) + a.SetArrayExpression(g.handleExpr(fset, indexExpr.X)) + a.SetSubscriptExpression(g.handleExpr(fset, indexExpr.Index)) return a } @@ -1029,21 +1044,21 @@ func (this *GoLanguageFrontend) handleIndexExpr(fset *token.FileSet, indexExpr * // [cpg.ArraySubscriptionExpression] that contains a [cpg.RangeExpression] as // its subscriptExpression to share some code between this and an index // expression. -func (this *GoLanguageFrontend) handleSliceExpr(fset *token.FileSet, sliceExpr *ast.SliceExpr) *cpg.ArraySubscriptionExpression { - a := this.NewArraySubscriptionExpression(fset, sliceExpr) +func (g *GoLanguageFrontend) handleSliceExpr(fset *token.FileSet, sliceExpr *ast.SliceExpr) *cpg.ArraySubscriptionExpression { + a := g.NewArraySubscriptionExpression(fset, sliceExpr) - a.SetArrayExpression(this.handleExpr(fset, sliceExpr.X)) + a.SetArrayExpression(g.handleExpr(fset, sliceExpr.X)) // Build the slice expression - s := this.NewRangeExpression(fset, sliceExpr) + s := g.NewRangeExpression(fset, sliceExpr) if sliceExpr.Low != nil { - s.SetFloor(this.handleExpr(fset, sliceExpr.Low)) + s.SetFloor(g.handleExpr(fset, sliceExpr.Low)) } if sliceExpr.High != nil { - s.SetCeiling(this.handleExpr(fset, sliceExpr.High)) + s.SetCeiling(g.handleExpr(fset, sliceExpr.High)) } if sliceExpr.Max != nil { - s.SetThird(this.handleExpr(fset, sliceExpr.Max)) + s.SetThird(g.handleExpr(fset, sliceExpr.Max)) } a.SetSubscriptExpression((*cpg.Expression)(s)) @@ -1051,11 +1066,11 @@ func (this *GoLanguageFrontend) handleSliceExpr(fset *token.FileSet, sliceExpr * return a } -func (this *GoLanguageFrontend) handleNewExpr(fset *token.FileSet, callExpr *ast.CallExpr) *cpg.Expression { - n := this.NewNewExpression(fset, callExpr) +func (g *GoLanguageFrontend) handleNewExpr(fset *token.FileSet, callExpr *ast.CallExpr) *cpg.Expression { + n := g.NewNewExpression(fset, callExpr) // first argument is type - t := this.handleType(fset, callExpr.Args[0]) + t := g.handleType(fset, callExpr.Args[0]) // new is a pointer, so need to reference the type with a pointer var pointer = jnigi.NewObjectRef(cpg.PointerOriginClass) @@ -1067,7 +1082,7 @@ func (this *GoLanguageFrontend) handleNewExpr(fset *token.FileSet, callExpr *ast (*cpg.HasType)(n).SetType(t.Reference(pointer)) // a new expression also needs an initializer, which is usually a constructexpression - c := this.NewConstructExpression(fset, callExpr) + c := g.NewConstructExpression(fset, callExpr) (*cpg.HasType)(c).SetType(t) n.SetInitializer((*cpg.Expression)(c)) @@ -1075,7 +1090,7 @@ func (this *GoLanguageFrontend) handleNewExpr(fset *token.FileSet, callExpr *ast return (*cpg.Expression)(n) } -func (this *GoLanguageFrontend) handleMakeExpr(fset *token.FileSet, callExpr *ast.CallExpr) *cpg.Expression { +func (g *GoLanguageFrontend) handleMakeExpr(fset *token.FileSet, callExpr *ast.CallExpr) *cpg.Expression { var n *cpg.Expression if callExpr.Args == nil || len(callExpr.Args) < 1 { @@ -1083,15 +1098,15 @@ func (this *GoLanguageFrontend) handleMakeExpr(fset *token.FileSet, callExpr *as } // first argument is always the type, handle it - t := this.handleType(fset, callExpr.Args[0]) + t := g.handleType(fset, callExpr.Args[0]) // actually make() can make more than just arrays, i.e. channels and maps if _, isArray := callExpr.Args[0].(*ast.ArrayType); isArray { - r := this.NewArrayCreationExpression(fset, callExpr) + r := g.NewArrayCreationExpression(fset, callExpr) // second argument is a dimension (if this is an array), usually a literal if len(callExpr.Args) > 1 { - d := this.handleExpr(fset, callExpr.Args[1]) + d := g.handleExpr(fset, callExpr.Args[1]) r.AddDimension(d) } @@ -1101,11 +1116,11 @@ func (this *GoLanguageFrontend) handleMakeExpr(fset *token.FileSet, callExpr *as // create at least a generic construct expression for the given map or channel type // and provide the remaining arguments - c := this.NewConstructExpression(fset, callExpr) + c := g.NewConstructExpression(fset, callExpr) // pass the remaining arguments for _, arg := range callExpr.Args[1:] { - a := this.handleExpr(fset, arg) + a := g.handleExpr(fset, arg) c.AddArgument(a) } @@ -1119,11 +1134,11 @@ func (this *GoLanguageFrontend) handleMakeExpr(fset *token.FileSet, callExpr *as return n } -func (this *GoLanguageFrontend) handleBinaryExpr(fset *token.FileSet, binaryExpr *ast.BinaryExpr) *cpg.BinaryOperator { - b := this.NewBinaryOperator(fset, binaryExpr, binaryExpr.Op.String()) +func (g *GoLanguageFrontend) handleBinaryExpr(fset *token.FileSet, binaryExpr *ast.BinaryExpr) *cpg.BinaryOperator { + b := g.NewBinaryOperator(fset, binaryExpr, binaryExpr.Op.String()) - lhs := this.handleExpr(fset, binaryExpr.X) - rhs := this.handleExpr(fset, binaryExpr.Y) + lhs := g.handleExpr(fset, binaryExpr.X) + rhs := g.handleExpr(fset, binaryExpr.Y) b.SetLHS(lhs) b.SetRHS(rhs) @@ -1131,10 +1146,10 @@ func (this *GoLanguageFrontend) handleBinaryExpr(fset *token.FileSet, binaryExpr return b } -func (this *GoLanguageFrontend) handleUnaryExpr(fset *token.FileSet, unaryExpr *ast.UnaryExpr) *cpg.UnaryOperator { - u := this.NewUnaryOperator(fset, unaryExpr, unaryExpr.Op.String(), false, false) +func (g *GoLanguageFrontend) handleUnaryExpr(fset *token.FileSet, unaryExpr *ast.UnaryExpr) *cpg.UnaryOperator { + u := g.NewUnaryOperator(fset, unaryExpr, unaryExpr.Op.String(), false, false) - input := this.handleExpr(fset, unaryExpr.X) + input := g.handleExpr(fset, unaryExpr.X) if input != nil { u.SetInput(input) } @@ -1142,10 +1157,10 @@ func (this *GoLanguageFrontend) handleUnaryExpr(fset *token.FileSet, unaryExpr * return u } -func (this *GoLanguageFrontend) handleStarExpr(fset *token.FileSet, unaryExpr *ast.StarExpr) *cpg.UnaryOperator { - u := this.NewUnaryOperator(fset, unaryExpr, "*", false, true) +func (g *GoLanguageFrontend) handleStarExpr(fset *token.FileSet, unaryExpr *ast.StarExpr) *cpg.UnaryOperator { + u := g.NewUnaryOperator(fset, unaryExpr, "*", false, true) - input := this.handleExpr(fset, unaryExpr.X) + input := g.handleExpr(fset, unaryExpr.X) if input != nil { u.SetInput(input) } @@ -1153,12 +1168,12 @@ func (this *GoLanguageFrontend) handleStarExpr(fset *token.FileSet, unaryExpr *a return u } -func (this *GoLanguageFrontend) handleSelectorExpr(fset *token.FileSet, selectorExpr *ast.SelectorExpr) *cpg.DeclaredReferenceExpression { - base := this.handleExpr(fset, selectorExpr.X) +func (g *GoLanguageFrontend) handleSelectorExpr(fset *token.FileSet, selectorExpr *ast.SelectorExpr) *cpg.DeclaredReferenceExpression { + base := g.handleExpr(fset, selectorExpr.X) // check, if this just a regular reference to a variable with a package scope and not a member expression var isMemberExpression bool = true - for _, imp := range this.File.Imports { + for _, imp := range g.File.Imports { if base.GetName().GetLocalName() == getImportName(imp) { // found a package name, so this is NOT a member expression isMemberExpression = false @@ -1167,18 +1182,18 @@ func (this *GoLanguageFrontend) handleSelectorExpr(fset *token.FileSet, selector var decl *cpg.DeclaredReferenceExpression if isMemberExpression { - m := this.NewMemberExpression(fset, selectorExpr, selectorExpr.Sel.Name, base) + m := g.NewMemberExpression(fset, selectorExpr, selectorExpr.Sel.Name, base) decl = (*cpg.DeclaredReferenceExpression)(m) } else { // we need to set the name to a FQN-style, including the package scope. the call resolver will then resolve this fqn := fmt.Sprintf("%s.%s", base.GetName(), selectorExpr.Sel.Name) - this.LogTrace("Trying to parse the fqn '%s'", fqn) + g.LogTrace("Trying to parse the fqn '%s'", fqn) - name := this.ParseName(fqn) + name := g.ParseName(fqn) - decl = this.NewDeclaredReferenceExpression(fset, selectorExpr, fqn) + decl = g.NewDeclaredReferenceExpression(fset, selectorExpr, fqn) decl.Node().SetName(name) } @@ -1202,17 +1217,17 @@ func (this *GoLanguageFrontend) handleSelectorExpr(fset *token.FileSet, selector return decl } -func (this *GoLanguageFrontend) handleKeyValueExpr(fset *token.FileSet, expr *ast.KeyValueExpr) *cpg.KeyValueExpression { - this.LogTrace("Handling key value expression %+v", *expr) +func (g *GoLanguageFrontend) handleKeyValueExpr(fset *token.FileSet, expr *ast.KeyValueExpr) *cpg.KeyValueExpression { + g.LogTrace("Handling key value expression %+v", *expr) - k := this.NewKeyValueExpression(fset, expr) + k := g.NewKeyValueExpression(fset, expr) - keyExpr := this.handleExpr(fset, expr.Key) + keyExpr := g.handleExpr(fset, expr.Key) if keyExpr != nil { k.SetKey(keyExpr) } - valueExpr := this.handleExpr(fset, expr.Value) + valueExpr := g.handleExpr(fset, expr.Value) if valueExpr != nil { k.SetValue(valueExpr) } @@ -1220,13 +1235,13 @@ func (this *GoLanguageFrontend) handleKeyValueExpr(fset *token.FileSet, expr *as return k } -func (this *GoLanguageFrontend) handleBasicLit(fset *token.FileSet, lit *ast.BasicLit) *cpg.Literal { - this.LogTrace("Handling literal %+v", *lit) +func (g *GoLanguageFrontend) handleBasicLit(fset *token.FileSet, lit *ast.BasicLit) *cpg.Literal { + g.LogTrace("Handling literal %+v", *lit) var value cpg.Castable var t *cpg.Type - lang, err := this.GetLanguage() + lang, err := g.GetLanguage() if err != nil { panic(err) } @@ -1235,26 +1250,26 @@ func (this *GoLanguageFrontend) handleBasicLit(fset *token.FileSet, lit *ast.Bas case token.STRING: // strip the " value = cpg.NewString(lit.Value[1 : len(lit.Value)-1]) - t = cpg.TypeParser_createFrom("string", lang, this.GetCtx()) + t = cpg.TypeParser_createFrom("string", lang, g.GetCtx()) case token.INT: i, _ := strconv.ParseInt(lit.Value, 10, 64) value = cpg.NewInteger(int(i)) - t = cpg.TypeParser_createFrom("int", lang, this.GetCtx()) + t = cpg.TypeParser_createFrom("int", lang, g.GetCtx()) case token.FLOAT: // default seems to be float64 f, _ := strconv.ParseFloat(lit.Value, 64) value = cpg.NewDouble(f) - t = cpg.TypeParser_createFrom("float64", lang, this.GetCtx()) + t = cpg.TypeParser_createFrom("float64", lang, g.GetCtx()) case token.IMAG: // TODO t = &cpg.UnknownType_getUnknown(lang).Type case token.CHAR: value = cpg.NewString(lit.Value) - t = cpg.TypeParser_createFrom("rune", lang, this.GetCtx()) + t = cpg.TypeParser_createFrom("rune", lang, g.GetCtx()) break } - l := this.NewLiteral(fset, lit, value, t) + l := g.NewLiteral(fset, lit, value, t) return l } @@ -1262,20 +1277,20 @@ func (this *GoLanguageFrontend) handleBasicLit(fset *token.FileSet, lit *ast.Bas // handleCompositeLit handles a composite literal, which we need to translate into a combination of a // ConstructExpression and a list of KeyValueExpressions. The problem is that we need to add the list // as a first argument of the construct expression. -func (this *GoLanguageFrontend) handleCompositeLit(fset *token.FileSet, lit *ast.CompositeLit) *cpg.ConstructExpression { - this.LogTrace("Handling composite literal %+v", *lit) +func (g *GoLanguageFrontend) handleCompositeLit(fset *token.FileSet, lit *ast.CompositeLit) *cpg.ConstructExpression { + g.LogTrace("Handling composite literal %+v", *lit) - c := this.NewConstructExpression(fset, lit) + c := g.NewConstructExpression(fset, lit) // parse the type field, to see which kind of expression it is - var typ = this.handleType(fset, lit.Type) + var typ = g.handleType(fset, lit.Type) if typ != nil { (*cpg.Node)(c).SetName(typ.GetName()) (*cpg.Expression)(c).SetType(typ) } - l := this.NewInitializerListExpression(fset, lit) + l := g.NewInitializerListExpression(fset, lit) c.AddArgument((*cpg.Expression)(l)) @@ -1286,7 +1301,7 @@ func (this *GoLanguageFrontend) handleCompositeLit(fset *token.FileSet, lit *ast var exprs = []*cpg.Expression{} for _, elem := range lit.Elts { - expr := this.handleExpr(fset, elem) + expr := g.handleExpr(fset, elem) if expr != nil { exprs = append(exprs, expr) @@ -1300,39 +1315,39 @@ func (this *GoLanguageFrontend) handleCompositeLit(fset *token.FileSet, lit *ast // handleFuncLit handles a function literal, which we need to translate into a combination of a // LambdaExpression and a function declaration. -func (this *GoLanguageFrontend) handleFuncLit(fset *token.FileSet, lit *ast.FuncLit) *cpg.LambdaExpression { - this.LogTrace("Handling function literal %#v", *lit) +func (g *GoLanguageFrontend) handleFuncLit(fset *token.FileSet, lit *ast.FuncLit) *cpg.LambdaExpression { + g.LogTrace("Handling function literal %#v", *lit) - l := this.NewLambdaExpression(fset, lit) + l := g.NewLambdaExpression(fset, lit) // Parse the expression as a function declaration with a little trick - funcDecl := this.handleFuncDecl(fset, &ast.FuncDecl{Type: lit.Type, Body: lit.Body, Name: ast.NewIdent("")}) + funcDecl := g.handleFuncDecl(fset, &ast.FuncDecl{Type: lit.Type, Body: lit.Body, Name: ast.NewIdent("")}) - this.LogTrace("Function of literal is: %#v", funcDecl) + g.LogTrace("Function of literal is: %#v", funcDecl) l.SetFunction(funcDecl) return l } -func (this *GoLanguageFrontend) handleIdent(fset *token.FileSet, ident *ast.Ident) *cpg.Expression { - lang, err := this.GetLanguage() +func (g *GoLanguageFrontend) handleIdent(fset *token.FileSet, ident *ast.Ident) *cpg.Expression { + lang, err := g.GetLanguage() if err != nil { panic(err) } // Check, if this is 'nil', because then we handle it as a literal in the graph if ident.Name == "nil" { - lit := this.NewLiteral(fset, ident, nil, &cpg.UnknownType_getUnknown(lang).Type) + lit := g.NewLiteral(fset, ident, nil, &cpg.UnknownType_getUnknown(lang).Type) - (*cpg.Node)(lit).SetName(this.ParseName(ident.Name)) + (*cpg.Node)(lit).SetName(g.ParseName(ident.Name)) return (*cpg.Expression)(lit) } - ref := this.NewDeclaredReferenceExpression(fset, ident, ident.Name) + ref := g.NewDeclaredReferenceExpression(fset, ident, ident.Name) - tu := this.CurrentTU + tu := g.CurrentTU // check, if this refers to a package import i := tu.GetIncludeByName(ident.Name) @@ -1345,14 +1360,14 @@ func (this *GoLanguageFrontend) handleIdent(fset *token.FileSet, ident *ast.Iden return (*cpg.Expression)(ref) } -func (this *GoLanguageFrontend) handleTypeAssertExpr(fset *token.FileSet, assert *ast.TypeAssertExpr) *cpg.CastExpression { - cast := this.NewCastExpression(fset, assert) +func (g *GoLanguageFrontend) handleTypeAssertExpr(fset *token.FileSet, assert *ast.TypeAssertExpr) *cpg.CastExpression { + cast := g.NewCastExpression(fset, assert) // Parse the inner expression - expr := this.handleExpr(fset, assert.X) + expr := g.handleExpr(fset, assert.X) // Parse the type - typ := this.handleType(fset, assert.Type) + typ := g.handleType(fset, assert.Type) cast.SetExpression(expr) @@ -1363,12 +1378,12 @@ func (this *GoLanguageFrontend) handleTypeAssertExpr(fset *token.FileSet, assert return cast } -func (this *GoLanguageFrontend) handleType(fset *token.FileSet, typeExpr ast.Expr) *cpg.Type { +func (g *GoLanguageFrontend) handleType(fset *token.FileSet, typeExpr ast.Expr) *cpg.Type { var err error - this.LogTrace("Parsing type %T: %s", typeExpr, code(fset, typeExpr)) + g.LogTrace("Parsing type %T: %s", typeExpr, code(fset, typeExpr)) - lang, err := this.GetLanguage() + lang, err := g.GetLanguage() if err != nil { panic(err) } @@ -1376,22 +1391,22 @@ func (this *GoLanguageFrontend) handleType(fset *token.FileSet, typeExpr ast.Exp switch v := typeExpr.(type) { case *ast.Ident: var name string - if this.isBuiltinType(v.Name) { + if g.isBuiltinType(v.Name) { name = v.Name - this.LogTrace("non-fqn type: %s", name) + g.LogTrace("non-fqn type: %s", name) } else { - name = fmt.Sprintf("%s.%s", this.File.Name.Name, v.Name) - this.LogTrace("fqn type: %s", name) + name = fmt.Sprintf("%s.%s", g.File.Name.Name, v.Name) + g.LogTrace("fqn type: %s", name) } - return cpg.TypeParser_createFrom(name, lang, this.GetCtx()) + return cpg.TypeParser_createFrom(name, lang, g.GetCtx()) case *ast.SelectorExpr: // small shortcut fqn := fmt.Sprintf("%s.%s", v.X.(*ast.Ident).Name, v.Sel.Name) - this.LogTrace("FQN type: %s", fqn) - return cpg.TypeParser_createFrom(fqn, lang, this.GetCtx()) + g.LogTrace("FQN type: %s", fqn) + return cpg.TypeParser_createFrom(fqn, lang, g.GetCtx()) case *ast.StarExpr: - t := this.handleType(fset, v.X) + t := g.handleType(fset, v.X) var i = jnigi.NewObjectRef(cpg.PointerOriginClass) err = env.GetStaticField(cpg.PointerOriginClass, "POINTER", i) @@ -1399,11 +1414,11 @@ func (this *GoLanguageFrontend) handleType(fset *token.FileSet, typeExpr ast.Exp log.Fatal(err) } - this.LogTrace("Pointer to %s", t.GetName()) + g.LogTrace("Pointer to %s", t.GetName()) return t.Reference(i) case *ast.ArrayType: - t := this.handleType(fset, v.Elt) + t := g.handleType(fset, v.Elt) var i = jnigi.NewObjectRef(cpg.PointerOriginClass) err = env.GetStaticField(cpg.PointerOriginClass, "ARRAY", i) @@ -1411,15 +1426,15 @@ func (this *GoLanguageFrontend) handleType(fset *token.FileSet, typeExpr ast.Exp log.Fatal(err) } - this.LogTrace("Array of %s", t.GetName()) + g.LogTrace("Array of %s", t.GetName()) return t.Reference(i) case *ast.MapType: // we cannot properly represent Golangs built-in map types, yet so we have // to make a shortcut here and represent it as a Java-like map type. - t := cpg.TypeParser_createFrom("map", lang, this.GetCtx()) - keyType := this.handleType(fset, v.Key) - valueType := this.handleType(fset, v.Value) + t := cpg.TypeParser_createFrom("map", lang, g.GetCtx()) + keyType := g.handleType(fset, v.Key) + valueType := g.handleType(fset, v.Value) // TODO(oxisto): Find a better way to represent casts (*cpg.ObjectType)(t).AddGeneric(keyType) @@ -1428,8 +1443,8 @@ func (this *GoLanguageFrontend) handleType(fset *token.FileSet, typeExpr ast.Exp return t case *ast.ChanType: // handle them similar to maps - t := cpg.TypeParser_createFrom("chan", lang, this.GetCtx()) - chanType := this.handleType(fset, v.Value) + t := cpg.TypeParser_createFrom("chan", lang, g.GetCtx()) + chanType := g.handleType(fset, v.Value) (*cpg.ObjectType)(t).AddGeneric(chanType) @@ -1440,7 +1455,7 @@ func (this *GoLanguageFrontend) handleType(fset *token.FileSet, typeExpr ast.Exp var returnTypes = []*cpg.Type{} for _, param := range v.Params.List { - parameterTypes = append(parameterTypes, this.handleType(fset, param.Type)) + parameterTypes = append(parameterTypes, g.handleType(fset, param.Type)) } parametersTypesList, err = cpg.ListOf(parameterTypes) @@ -1450,7 +1465,7 @@ func (this *GoLanguageFrontend) handleType(fset *token.FileSet, typeExpr ast.Exp if v.Results != nil { for _, ret := range v.Results.List { - returnTypes = append(returnTypes, this.handleType(fset, ret.Type)) + returnTypes = append(returnTypes, g.handleType(fset, ret.Type)) } } @@ -1479,42 +1494,42 @@ func (this *GoLanguageFrontend) handleType(fset *token.FileSet, typeExpr ast.Exp // We do not really support dedicated interfaces types, so all we can for now // is parse it as an object type with a pseudo-name for _, method := range v.Methods.List { - name += this.handleType(fset, method.Type).GetName().ToString() + name += g.handleType(fset, method.Type).GetName().ToString() } name += "}" - return cpg.TypeParser_createFrom(name, lang, this.GetCtx()) + return cpg.TypeParser_createFrom(name, lang, g.GetCtx()) case *ast.IndexExpr: // This is a type with one type parameter. First we need to parse the "X" expression as a type - var t = this.handleType(fset, v.X) + var t = g.handleType(fset, v.X) // Then we parse the "Index" as a type parameter - var genericType = this.handleType(fset, v.Index) + var genericType = g.handleType(fset, v.Index) (*cpg.ObjectType)(t).AddGeneric(genericType) return t case *ast.IndexListExpr: // This is a type with two type parameters. First we need to parse the "X" expression as a type - var t = this.handleType(fset, v.X) + var t = g.handleType(fset, v.X) // Then we parse the "Indices" as a type parameter for _, index := range v.Indices { - var genericType = this.handleType(fset, index) + var genericType = g.handleType(fset, index) (*cpg.ObjectType)(t).AddGeneric(genericType) } return t default: - this.LogError("Not parsing type of type %T yet. Defaulting to unknown type", v) + g.LogError("Not parsing type of type %T yet. Defaulting to unknown type", v) } return &cpg.UnknownType_getUnknown(lang).Type } -func (this *GoLanguageFrontend) isBuiltinType(s string) bool { +func (g *GoLanguageFrontend) isBuiltinType(s string) bool { switch s { case "bool": fallthrough @@ -1561,9 +1576,9 @@ func (this *GoLanguageFrontend) isBuiltinType(s string) bool { } } -func (this *GoLanguageFrontend) ParseName(fqn string) *cpg.Name { +func (g *GoLanguageFrontend) ParseName(fqn string) *cpg.Name { var n *cpg.Name = (*cpg.Name)(jnigi.NewObjectRef(cpg.NameClass)) - err := env.CallStaticMethod(cpg.NameKtClass, "parseName", n, this.Cast(LanguageProviderClass), cpg.NewCharSequence(fqn)) + err := env.CallStaticMethod(cpg.NameKtClass, "parseName", n, g.Cast(LanguageProviderClass), cpg.NewCharSequence(fqn)) if err != nil { log.Fatal(err) } diff --git a/cpg-language-go/src/main/golang/frontend/statement_builder.go b/cpg-language-go/src/main/golang/frontend/statement_builder.go index f951d41392..adee1086aa 100644 --- a/cpg-language-go/src/main/golang/frontend/statement_builder.go +++ b/cpg-language-go/src/main/golang/frontend/statement_builder.go @@ -34,47 +34,47 @@ import ( "tekao.net/jnigi" ) -func (frontend *GoLanguageFrontend) NewCompoundStatement(fset *token.FileSet, astNode ast.Node) *cpg.CompoundStatement { - return (*cpg.CompoundStatement)(frontend.NewStatement("CompoundStatement", fset, astNode)) +func (g *GoLanguageFrontend) NewCompoundStatement(fset *token.FileSet, astNode ast.Node) *cpg.CompoundStatement { + return (*cpg.CompoundStatement)(g.NewStatement("CompoundStatement", fset, astNode)) } -func (frontend *GoLanguageFrontend) NewReturnStatement(fset *token.FileSet, astNode ast.Node) *cpg.ReturnStatement { - return (*cpg.ReturnStatement)(frontend.NewStatement("ReturnStatement", fset, astNode)) +func (g *GoLanguageFrontend) NewReturnStatement(fset *token.FileSet, astNode ast.Node) *cpg.ReturnStatement { + return (*cpg.ReturnStatement)(g.NewStatement("ReturnStatement", fset, astNode)) } -func (frontend *GoLanguageFrontend) NewDeclarationStatement(fset *token.FileSet, astNode ast.Node) *cpg.DeclarationStatement { - return (*cpg.DeclarationStatement)(frontend.NewStatement("DeclarationStatement", fset, astNode)) +func (g *GoLanguageFrontend) NewDeclarationStatement(fset *token.FileSet, astNode ast.Node) *cpg.DeclarationStatement { + return (*cpg.DeclarationStatement)(g.NewStatement("DeclarationStatement", fset, astNode)) } -func (frontend *GoLanguageFrontend) NewIfStatement(fset *token.FileSet, astNode ast.Node) *cpg.IfStatement { - return (*cpg.IfStatement)(frontend.NewStatement("IfStatement", fset, astNode)) +func (g *GoLanguageFrontend) NewIfStatement(fset *token.FileSet, astNode ast.Node) *cpg.IfStatement { + return (*cpg.IfStatement)(g.NewStatement("IfStatement", fset, astNode)) } -func (frontend *GoLanguageFrontend) NewForStatement(fset *token.FileSet, astNode ast.Node) *cpg.ForStatement { - return (*cpg.ForStatement)(frontend.NewStatement("ForStatement", fset, astNode)) +func (g *GoLanguageFrontend) NewForStatement(fset *token.FileSet, astNode ast.Node) *cpg.ForStatement { + return (*cpg.ForStatement)(g.NewStatement("ForStatement", fset, astNode)) } -func (frontend *GoLanguageFrontend) NewForEachStatement(fset *token.FileSet, astNode ast.Node) *cpg.ForEachStatement { - return (*cpg.ForEachStatement)(frontend.NewStatement("ForEachStatement", fset, astNode)) +func (g *GoLanguageFrontend) NewForEachStatement(fset *token.FileSet, astNode ast.Node) *cpg.ForEachStatement { + return (*cpg.ForEachStatement)(g.NewStatement("ForEachStatement", fset, astNode)) } -func (frontend *GoLanguageFrontend) NewSwitchStatement(fset *token.FileSet, astNode ast.Node) *cpg.SwitchStatement { - return (*cpg.SwitchStatement)(frontend.NewStatement("SwitchStatement", fset, astNode)) +func (g *GoLanguageFrontend) NewSwitchStatement(fset *token.FileSet, astNode ast.Node) *cpg.SwitchStatement { + return (*cpg.SwitchStatement)(g.NewStatement("SwitchStatement", fset, astNode)) } -func (frontend *GoLanguageFrontend) NewCaseStatement(fset *token.FileSet, astNode ast.Node) *cpg.CaseStatement { - return (*cpg.CaseStatement)(frontend.NewStatement("CaseStatement", fset, astNode)) +func (g *GoLanguageFrontend) NewCaseStatement(fset *token.FileSet, astNode ast.Node) *cpg.CaseStatement { + return (*cpg.CaseStatement)(g.NewStatement("CaseStatement", fset, astNode)) } -func (frontend *GoLanguageFrontend) NewDefaultStatement(fset *token.FileSet, astNode ast.Node) *cpg.DefaultStatement { - return (*cpg.DefaultStatement)(frontend.NewStatement("DefaultStatement", fset, astNode)) +func (g *GoLanguageFrontend) NewDefaultStatement(fset *token.FileSet, astNode ast.Node) *cpg.DefaultStatement { + return (*cpg.DefaultStatement)(g.NewStatement("DefaultStatement", fset, astNode)) } -func (frontend *GoLanguageFrontend) NewStatement(typ string, fset *token.FileSet, astNode ast.Node, args ...any) *jnigi.ObjectRef { +func (g *GoLanguageFrontend) NewStatement(typ string, fset *token.FileSet, astNode ast.Node, args ...any) *jnigi.ObjectRef { var node = jnigi.NewObjectRef(fmt.Sprintf("%s/%s", cpg.StatementsPackage, typ)) - // Prepend the frontend as the receiver - args = append([]any{frontend.Cast(cpg.GraphPackage + "/MetadataProvider")}, args...) + // Prepend the g as the receiver + args = append([]any{g.Cast(cpg.GraphPackage + "/MetadataProvider")}, args...) err := env.CallStaticMethod( cpg.GraphPackage+"/StatementBuilderKt", diff --git a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontend.kt b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontend.kt index a615d99d00..6efd5fce31 100644 --- a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontend.kt +++ b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontend.kt @@ -39,8 +39,21 @@ import java.io.FileOutputStream @SupportsParallelParsing(false) @RegisterExtraPass(GoExtraPass::class) +<<<<<<< HEAD +@ReplacePass( + EvaluationOrderGraphPass::class, + lang = GoLanguage::class, + with = GoEvaluationOrderGraphPass::class +) class GoLanguageFrontend(language: Language, ctx: TranslationContext) : LanguageFrontend(language, ctx) { +======= +class GoLanguageFrontend( + language: Language, + config: TranslationConfiguration, + scopeManager: ScopeManager, +) : LanguageFrontend(language, config, scopeManager) { +>>>>>>> 8dc857ab7 (Cleanup calls) companion object { init { diff --git a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoEvaluationGraphPass.kt b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoEvaluationGraphPass.kt new file mode 100644 index 0000000000..2934825f16 --- /dev/null +++ b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoEvaluationGraphPass.kt @@ -0,0 +1,4 @@ +package de.fraunhofer.aisec.cpg.passes + +class GoEvaluationGraphPass(ctx: TranslationContext) : EvaluationOrderGraphPass(ctx) { +} \ No newline at end of file diff --git a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoEvaluationOrderGraphPass.kt b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoEvaluationOrderGraphPass.kt new file mode 100644 index 0000000000..35e1f22798 --- /dev/null +++ b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoEvaluationOrderGraphPass.kt @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +package de.fraunhofer.aisec.cpg.passes + +import de.fraunhofer.aisec.cpg.TranslationContext +import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration +import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression + +class GoEvaluationOrderGraphPass(ctx: TranslationContext) : EvaluationOrderGraphPass(ctx) { + + private var deferredCalls = mutableMapOf>() + + + override fun handleFunctionDeclaration(node: FunctionDeclaration) { + super.handleFunctionDeclaration(node) + + // Before we exit, we need to call our deferred calls + val calls = deferredCalls[scopeManager.currentFunction] + calls?.forEach { pushToEOG(it) } + + // Clear them afterwards + calls?.clear() + } +} diff --git a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoExtraPass.kt b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoExtraPass.kt index c422085675..58b74543a0 100644 --- a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoExtraPass.kt +++ b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoExtraPass.kt @@ -199,6 +199,12 @@ class GoExtraPass(ctx: TranslationContext) : ComponentPass(ctx), ScopeProvider { */ // TODO: Somehow, this gets called twice?! private fun handleInclude(include: IncludeDeclaration) { + // If the namespace is included as _, we can ignore it, as its only included as a runtime + // dependency + if (include.name.localName == "_") { + return + } + // Try to see if we already know about this namespace somehow val namespace = scopeManager.resolve(scopeManager.globalScope, true) { @@ -218,6 +224,8 @@ class GoExtraPass(ctx: TranslationContext) : ComponentPass(ctx), ScopeProvider { /** * This function gets called for every [CallExpression] and checks, whether this is actually a * "calling" a type and is thus a [CastExpression] rather than a [CallExpression]. + * + * This also "fixes" the EOG of defer call expressions. */ private fun handleCall(call: CallExpression, parent: Node?) { // We need to check, whether the "callee" refers to a type and if yes, convert it into a @@ -259,7 +267,8 @@ class GoExtraPass(ctx: TranslationContext) : ComponentPass(ctx), ScopeProvider { if (parent !is ArgumentHolder) { log.error( - "Parent AST node of call expression is not an argument holder. Cannot convert to cast expression. Further analysis might not be entirely accurate." + "Parent AST node of call expression is not an argument holder. " + + "Cannot convert to cast expression. Further analysis might not be entirely accurate." ) return } @@ -267,7 +276,8 @@ class GoExtraPass(ctx: TranslationContext) : ComponentPass(ctx), ScopeProvider { val success = parent.replaceArgument(call, cast) if (!success) { log.error( - "Replacing call expression with cast expression was not successful. Further analysis might not be entirely accurate." + "Replacing call expression with cast expression was not successful. " + + "Further analysis might not be entirely accurate." ) } else { call.disconnectFromGraph() diff --git a/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/ExpressionTest.kt b/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/ExpressionTest.kt index 7921cec047..e691d96a21 100644 --- a/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/ExpressionTest.kt +++ b/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/ExpressionTest.kt @@ -136,4 +136,27 @@ class ExpressionTest { assertLiteralValue(1, slice.ceiling) assertLiteralValue(1, slice.third) } + + @Test + fun testDeferStatement() { + val topLevel = Path.of("src", "test", "resources", "golang") + val tu = + TestUtils.analyzeAndGetFirstTU( + listOf(topLevel.resolve("defer.go").toFile()), + topLevel, + true + ) { + it.registerLanguage() + } + assertNotNull(tu) + + val p = tu.namespaces["p"] + assertNotNull(p) + + val main = p.functions["main"] + assertNotNull(main) + + val op = main.allChildren { it.name.localName == "defer" } + assertTrue(op.isNotEmpty()) + } } diff --git a/cpg-language-go/src/test/resources/golang/defer.go b/cpg-language-go/src/test/resources/golang/defer.go new file mode 100644 index 0000000000..8b47812f30 --- /dev/null +++ b/cpg-language-go/src/test/resources/golang/defer.go @@ -0,0 +1,25 @@ +package p + +import ( + "fmt" + "os" +) + +func main() { + do() + defer that() + + if len(os.Args) == 2 { + return + } + + fmt.Println("Still here, yay!") +} + +func do() { + +} + +func that() { + +} From 490bd0c78b47cedd344106305acda0428b143b37 Mon Sep 17 00:00:00 2001 From: Christian Banse Date: Sun, 28 May 2023 19:44:23 +0200 Subject: [PATCH 3/5] Trying to handle defer --- .../cpg/passes/EvaluationOrderGraphPass.kt | 30 +--------- .../src/main/golang/frontend/handler.go | 6 +- .../frontends/golang/GoLanguageFrontend.kt | 12 ++-- .../aisec/cpg/passes/GoEvaluationGraphPass.kt | 4 -- .../cpg/passes/GoEvaluationOrderGraphPass.kt | 59 +++++++++++++++++-- .../src/test/resources/golang/defer.go | 10 +++- 6 files changed, 70 insertions(+), 51 deletions(-) delete mode 100644 cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoEvaluationGraphPass.kt diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EvaluationOrderGraphPass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EvaluationOrderGraphPass.kt index 39aff7c47e..c68b4d76a5 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EvaluationOrderGraphPass.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EvaluationOrderGraphPass.kt @@ -75,13 +75,6 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa private var currentPredecessors = mutableListOf() private val nextEdgeProperties = EnumMap(Properties::class.java) - /** - * Certain languages (e.g. Go) allow the automatic execution of certain cleanup calls before we - * exit the function. We need to gather the appropriate call expressions and then connect them - * in [handleFunctionDeclaration]. - */ - private var cleanupCalls = mutableMapOf>() - /** * Allows to register EOG creation logic when a currently visited node can depend on future * visited nodes. Currently used to connect goto statements and the target labeled statements. @@ -361,23 +354,13 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa } } currentPredecessors.clear() - - // Before we exit, we need to call any cleanup calls - val calls = cleanupCalls[node] - calls?.forEach { call -> - createEOG(call) - node.body?.let { body -> addEOGEdge(call, body) } - } - - // Clear them afterwards - calls?.clear() } /** * Tries to create the necessary EOG edges for the [node] (if it is non-null) by looking up the * appropriate handler function of the node's class in [map] and calling it. */ - private fun createEOG(node: Node?) { + protected fun createEOG(node: Node?) { if (node == null) { // nothing to do return @@ -547,13 +530,6 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa // to be handled differently (see https://github.com/Fraunhofer-AISEC/cpg/issues/1161) if (node.operatorCode == "throw") { handleThrowOperator(node) - } else if (node.operatorCode == "defer" && node.input is CallExpression) { - // Add to the cleanup calls. We are intentionally not creating the EOG for the input - // yet, as this would mess up the EOG at this point. - scopeManager.currentFunction?.let { - val list = cleanupCalls.computeIfAbsent(it) { mutableListOf() } - list += node.input as CallExpression - } } else { handleUnspecificUnaryOperator(node) } @@ -582,7 +558,7 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa * function using [ReplacePass], handle specific operators on their own and delegate the rest to * this function. */ - protected fun handleUnspecificUnaryOperator(node: UnaryOperator) { + protected open fun handleUnspecificUnaryOperator(node: UnaryOperator) { val input = node.input createEOG(input) @@ -811,7 +787,7 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa * @param prev the previous node * @param next the next node */ - private fun addEOGEdge(prev: Node, next: Node) { + protected fun addEOGEdge(prev: Node, next: Node) { val propertyEdge = PropertyEdge(prev, next) propertyEdge.addProperties(nextEdgeProperties) propertyEdge.addProperty(Properties.INDEX, prev.nextEOG.size) diff --git a/cpg-language-go/src/main/golang/frontend/handler.go b/cpg-language-go/src/main/golang/frontend/handler.go index bf2aa15375..1f16d47497 100644 --- a/cpg-language-go/src/main/golang/frontend/handler.go +++ b/cpg-language-go/src/main/golang/frontend/handler.go @@ -247,7 +247,7 @@ func (g *GoLanguageFrontend) handleFuncDecl(fset *token.FileSet, funcDecl *ast.F var recordName = recordType.GetName() // TODO: this will only find methods within the current translation unit - // this is a limitation that we have for C++ as well + // this is a limitation that we have for C++ as well record, err := g.GetScopeManager().GetRecordForName( g.GetScopeManager().GetCurrentScope(), recordName) @@ -293,8 +293,8 @@ func (g *GoLanguageFrontend) handleFuncDecl(fset *token.FileSet, funcDecl *ast.F g.GetScopeManager().AddDeclaration((*cpg.Declaration)(receiver)) } - var t *cpg.Type = g.handleType(fset, funcDecl.Type) - var returnTypes []*cpg.Type = []*cpg.Type{} + var t = g.handleType(fset, funcDecl.Type) + var returnTypes = []*cpg.Type{} if funcDecl.Type.Results != nil { for _, returnVariable := range funcDecl.Type.Results.List { diff --git a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontend.kt b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontend.kt index 6efd5fce31..d574a64dba 100644 --- a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontend.kt +++ b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontend.kt @@ -31,15 +31,17 @@ import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend import de.fraunhofer.aisec.cpg.frontends.SupportsParallelParsing import de.fraunhofer.aisec.cpg.frontends.TranslationException import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration +import de.fraunhofer.aisec.cpg.passes.EvaluationOrderGraphPass +import de.fraunhofer.aisec.cpg.passes.GoEvaluationOrderGraphPass import de.fraunhofer.aisec.cpg.passes.GoExtraPass import de.fraunhofer.aisec.cpg.passes.order.RegisterExtraPass +import de.fraunhofer.aisec.cpg.passes.order.ReplacePass import de.fraunhofer.aisec.cpg.sarif.PhysicalLocation import java.io.File import java.io.FileOutputStream @SupportsParallelParsing(false) @RegisterExtraPass(GoExtraPass::class) -<<<<<<< HEAD @ReplacePass( EvaluationOrderGraphPass::class, lang = GoLanguage::class, @@ -47,13 +49,7 @@ import java.io.FileOutputStream ) class GoLanguageFrontend(language: Language, ctx: TranslationContext) : LanguageFrontend(language, ctx) { -======= -class GoLanguageFrontend( - language: Language, - config: TranslationConfiguration, - scopeManager: ScopeManager, -) : LanguageFrontend(language, config, scopeManager) { ->>>>>>> 8dc857ab7 (Cleanup calls) + companion object { init { diff --git a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoEvaluationGraphPass.kt b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoEvaluationGraphPass.kt deleted file mode 100644 index 2934825f16..0000000000 --- a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoEvaluationGraphPass.kt +++ /dev/null @@ -1,4 +0,0 @@ -package de.fraunhofer.aisec.cpg.passes - -class GoEvaluationGraphPass(ctx: TranslationContext) : EvaluationOrderGraphPass(ctx) { -} \ No newline at end of file diff --git a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoEvaluationOrderGraphPass.kt b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoEvaluationOrderGraphPass.kt index 35e1f22798..5efe118e6c 100644 --- a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoEvaluationOrderGraphPass.kt +++ b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoEvaluationOrderGraphPass.kt @@ -27,21 +27,68 @@ package de.fraunhofer.aisec.cpg.passes import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration +import de.fraunhofer.aisec.cpg.graph.followNextEOGEdgesUntilHit +import de.fraunhofer.aisec.cpg.graph.statements.ReturnStatement import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression +import de.fraunhofer.aisec.cpg.graph.statements.expressions.UnaryOperator class GoEvaluationOrderGraphPass(ctx: TranslationContext) : EvaluationOrderGraphPass(ctx) { - private var deferredCalls = mutableMapOf>() + /** + * Go allows the automatic execution of certain cleanup calls before we exit the function (using + * `defer`). We need to gather the appropriate call expressions and then connect them in + * [handleFunctionDeclaration]. + */ + private var deferredCalls = mutableMapOf>() + + override fun handleUnspecificUnaryOperator(node: UnaryOperator) { + val input = node.input + if (node.operatorCode == "defer" && input is CallExpression) { + val function = scopeManager.currentFunction + if (function != null) { + // We need to disrupt the regular EOG handling here and store this deferred call. We + // will then pick it up again in handleFunctionDeclaration. + val calls = deferredCalls.computeIfAbsent(function) { mutableListOf() } + calls += node + + // Push the node itself to the EOG, not its "input" (the deferred call). However, it + // seems It seems that the arguments of the deferred call are evaluated at + // the point of the deferred statement, duh! + pushToEOG(node) + + // evaluate the callee + input.callee?.let { createEOG(it) } + + // then the arguments + for (arg in input.arguments) { + createEOG(arg) + } + } else { + log.error( + "Tried to parse a defer statement but could not retrieve current function from scope manager." + ) + } + } else { + super.handleUnspecificUnaryOperator(node) + } + } - override fun handleFunctionDeclaration(node: FunctionDeclaration) { super.handleFunctionDeclaration(node) // Before we exit, we need to call our deferred calls - val calls = deferredCalls[scopeManager.currentFunction] - calls?.forEach { pushToEOG(it) } + val defers = deferredCalls[node] - // Clear them afterwards - calls?.clear() + // We need to follow the path from the defer statement to all return statements that are + // reachable from this point. + for (defer in defers ?: listOf()) { + val paths = defer.followNextEOGEdgesUntilHit { it is ReturnStatement } + for (path in paths.fulfilled) { + // It is a bit philosophical whether the deferred call happens before or after the + // return statement in the EOG. For now it is easier to have it as the last node + // AFTER the return statement + addEOGEdge(path.last(), defer.input) + } + } } } diff --git a/cpg-language-go/src/test/resources/golang/defer.go b/cpg-language-go/src/test/resources/golang/defer.go index 8b47812f30..f81fe64733 100644 --- a/cpg-language-go/src/test/resources/golang/defer.go +++ b/cpg-language-go/src/test/resources/golang/defer.go @@ -6,20 +6,24 @@ import ( ) func main() { + i := 1 do() - defer that() + defer that(i) if len(os.Args) == 2 { + i++ return } + i++ fmt.Println("Still here, yay!") + return } func do() { } -func that() { - +func that(i int) { + fmt.Println(i) } From 63701cdb9fae035ce24383c2e657bfe3fd3f4a46 Mon Sep 17 00:00:00 2001 From: Christian Banse Date: Sun, 28 May 2023 21:01:49 +0200 Subject: [PATCH 4/5] Added test for defer statement --- .../cpg/graph/statements/CompoundStatement.kt | 5 ++++ .../src/main/golang/frontend/handler.go | 13 ++++++++- cpg-language-go/src/main/golang/statements.go | 27 +++++++++++++++++++ .../cpg/passes/GoEvaluationOrderGraphPass.kt | 16 ++++++----- .../aisec/cpg/passes/GoExtraPass.kt | 1 + .../cpg/frontends/golang/ExpressionTest.kt | 15 +++++++++-- .../src/test/resources/golang/defer.go | 1 - 7 files changed, 67 insertions(+), 11 deletions(-) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/CompoundStatement.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/CompoundStatement.kt index 8f8257be8a..6ccc10fe41 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/CompoundStatement.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/CompoundStatement.kt @@ -71,4 +71,9 @@ class CompoundStatement : Statement(), StatementHolder { operator fun get(n: Int): Statement { return statements[n] } + + /** Returns the last statement in this list of statements or null. */ + fun lastOrNull(): Statement? { + return statements.lastOrNull() + } } diff --git a/cpg-language-go/src/main/golang/frontend/handler.go b/cpg-language-go/src/main/golang/frontend/handler.go index 1f16d47497..78af68e778 100644 --- a/cpg-language-go/src/main/golang/frontend/handler.go +++ b/cpg-language-go/src/main/golang/frontend/handler.go @@ -371,10 +371,21 @@ func (g *GoLanguageFrontend) handleFuncDecl(fset *token.FileSet, funcDecl *ast.F // parse body s := g.handleBlockStmt(fset, funcDecl.Body) - err := f.SetBody((*cpg.Statement)(s)) + // Check, if the last statement is a return statement, otherwise we insert an implicit one + last := s.LastOrNull() + ok, err := (*jnigi.ObjectRef)(last).IsInstanceOf(env, cpg.RecordDeclarationClass) if err != nil { log.Fatal(err) + } + + if !ok { + r := g.NewReturnStatement(fset, nil) + s.AddStatement((*cpg.Statement)(r)) + } + err = f.SetBody((*cpg.Statement)(s)) + if err != nil { + log.Fatal(err) } // leave scope diff --git a/cpg-language-go/src/main/golang/statements.go b/cpg-language-go/src/main/golang/statements.go index 35bfcb001a..f8a1e0ab06 100644 --- a/cpg-language-go/src/main/golang/statements.go +++ b/cpg-language-go/src/main/golang/statements.go @@ -48,6 +48,16 @@ func (f *CompoundStatement) AddStatement(s *Statement) { (*jnigi.ObjectRef)(f).CallMethod(env, "addStatement", nil, (*jnigi.ObjectRef)(s).Cast(StatementClass)) } +func (c *CompoundStatement) LastOrNull() (s *Statement) { + s = new(Statement) + err := (*jnigi.ObjectRef)(c).CallMethod(env, "lastOrNull", s) + if err != nil { + panic(err) + } + + return +} + func (f *DeclarationStatement) SetSingleDeclaration(d *Declaration) { (*jnigi.ObjectRef)(f).CallMethod(env, "setSingleDeclaration", nil, (*jnigi.ObjectRef)(d).Cast(DeclarationClass)) } @@ -115,3 +125,20 @@ func (fw *ForStatement) SetIterationStatement(s *Statement) { func (r *ReturnStatement) SetReturnValue(e *Expression) { (*jnigi.ObjectRef)(r).CallMethod(env, "setReturnValue", nil, (*jnigi.ObjectRef)(e).Cast(ExpressionClass)) } + +func (s *Statement) ConvertToGo(o *jnigi.ObjectRef) error { + *s = (Statement)(*o) + return nil +} + +func (s *Statement) ConvertToJava() (obj *jnigi.ObjectRef, err error) { + return (*jnigi.ObjectRef)(s), nil +} + +func (s *Statement) GetClassName() string { + return StatementClass +} + +func (s *Statement) IsArray() bool { + return false +} diff --git a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoEvaluationOrderGraphPass.kt b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoEvaluationOrderGraphPass.kt index 5efe118e6c..fde50b71ea 100644 --- a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoEvaluationOrderGraphPass.kt +++ b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoEvaluationOrderGraphPass.kt @@ -26,18 +26,20 @@ package de.fraunhofer.aisec.cpg.passes import de.fraunhofer.aisec.cpg.TranslationContext +import de.fraunhofer.aisec.cpg.frontends.golang.GoLanguage import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.followNextEOGEdgesUntilHit import de.fraunhofer.aisec.cpg.graph.statements.ReturnStatement import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.UnaryOperator +/** This pass contains fine-grained improvements to the EOG for the [GoLanguage]. */ class GoEvaluationOrderGraphPass(ctx: TranslationContext) : EvaluationOrderGraphPass(ctx) { /** * Go allows the automatic execution of certain cleanup calls before we exit the function (using - * `defer`). We need to gather the appropriate call expressions and then connect them in - * [handleFunctionDeclaration]. + * `defer`). We need to gather the appropriate deferred call expressions and then connect them + * in [handleFunctionDeclaration]. */ private var deferredCalls = mutableMapOf>() @@ -52,14 +54,14 @@ class GoEvaluationOrderGraphPass(ctx: TranslationContext) : EvaluationOrderGraph calls += node // Push the node itself to the EOG, not its "input" (the deferred call). However, it - // seems It seems that the arguments of the deferred call are evaluated at - // the point of the deferred statement, duh! + // seems that the arguments of the deferred call are evaluated at the point of the + // deferred statement, duh! pushToEOG(node) - // evaluate the callee + // Evaluate the callee input.callee?.let { createEOG(it) } - // then the arguments + // Then the arguments for (arg in input.arguments) { createEOG(arg) } @@ -85,7 +87,7 @@ class GoEvaluationOrderGraphPass(ctx: TranslationContext) : EvaluationOrderGraph val paths = defer.followNextEOGEdgesUntilHit { it is ReturnStatement } for (path in paths.fulfilled) { // It is a bit philosophical whether the deferred call happens before or after the - // return statement in the EOG. For now it is easier to have it as the last node + // return statement in the EOG. For now, it is easier to have it as the last node // AFTER the return statement addEOGEdge(path.last(), defer.input) } diff --git a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoExtraPass.kt b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoExtraPass.kt index 58b74543a0..457248abfa 100644 --- a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoExtraPass.kt +++ b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoExtraPass.kt @@ -104,6 +104,7 @@ import de.fraunhofer.aisec.cpg.passes.order.ExecuteBefore @ExecuteBefore(VariableUsageResolver::class) @ExecuteBefore(CallResolver::class) @ExecuteBefore(DFGPass::class) +@ExecuteBefore(EvaluationOrderGraphPass::class) class GoExtraPass(ctx: TranslationContext) : ComponentPass(ctx), ScopeProvider { override val scope: Scope? diff --git a/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/ExpressionTest.kt b/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/ExpressionTest.kt index e691d96a21..b38928610c 100644 --- a/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/ExpressionTest.kt +++ b/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/ExpressionTest.kt @@ -33,6 +33,7 @@ import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.bodyOrNull import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration import de.fraunhofer.aisec.cpg.graph.statements.DeclarationStatement +import de.fraunhofer.aisec.cpg.graph.statements.ReturnStatement import de.fraunhofer.aisec.cpg.graph.statements.expressions.* import java.nio.file.Path import kotlin.test.* @@ -156,7 +157,17 @@ class ExpressionTest { val main = p.functions["main"] assertNotNull(main) - val op = main.allChildren { it.name.localName == "defer" } - assertTrue(op.isNotEmpty()) + val op = main.allChildren { it.name.localName == "defer" }.firstOrNull() + assertNotNull(op) + + // The EOG for the defer statement itself should be in the regular EOG path + op.prevEOG.any { it is CallExpression && it.name.localName == "do" } + op.nextEOG.any { it is DeclaredReferenceExpression && it.name.localName == "that" } + + // It should NOT connect to the call expression + op.nextEOG.none { it is CallExpression } + + // Its call expression should connect to the return statement + op.input.prevEOG.all { it is ReturnStatement } } } diff --git a/cpg-language-go/src/test/resources/golang/defer.go b/cpg-language-go/src/test/resources/golang/defer.go index f81fe64733..4493840aab 100644 --- a/cpg-language-go/src/test/resources/golang/defer.go +++ b/cpg-language-go/src/test/resources/golang/defer.go @@ -17,7 +17,6 @@ func main() { i++ fmt.Println("Still here, yay!") - return } func do() { From ecc5d5a8d0b6ec89c41dada97d998ab5fbbab375 Mon Sep 17 00:00:00 2001 From: Christian Banse Date: Mon, 29 May 2023 23:52:57 +0200 Subject: [PATCH 5/5] Starting to parse branch statement --- .../src/main/golang/frontend/handler.go | 69 +++++++++++++--- .../main/golang/frontend/statement_builder.go | 16 ++++ cpg-language-go/src/main/golang/statements.go | 24 ++++++ .../aisec/cpg/passes/GoExtraPass.kt | 4 + .../golang/GoLanguageFrontendTest.kt | 2 +- .../cpg/frontends/golang/StatementTest.kt | 81 +++++++++++++++++++ .../src/test/resources/golang/branch.go | 26 ++++++ 7 files changed, 210 insertions(+), 12 deletions(-) create mode 100644 cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/StatementTest.kt create mode 100644 cpg-language-go/src/test/resources/golang/branch.go diff --git a/cpg-language-go/src/main/golang/frontend/handler.go b/cpg-language-go/src/main/golang/frontend/handler.go index 78af68e778..f189fef67f 100644 --- a/cpg-language-go/src/main/golang/frontend/handler.go +++ b/cpg-language-go/src/main/golang/frontend/handler.go @@ -434,11 +434,14 @@ func (g *GoLanguageFrontend) handleValueSpec(fset *token.FileSet, valueDecl *ast d.SetType(t) } - // There could either be no initializers, otherwise the amount of values - // must match the names lenValues := len(valueDecl.Values) if lenValues != 0 && lenValues != len(valueDecl.Names) { - g.LogError("Number of initializers does not match number of names. Initializers might be incomplete") + var names []string + for _, n := range valueDecl.Names { + names = append(names, n.String()) + } + + g.LogError("Number of initializers (%d) does not match number of names (%s). Initializers might be incomplete", lenValues, names) } // The initializer is in the "Values" slice with the respective index @@ -729,6 +732,10 @@ func (g *GoLanguageFrontend) handleStmt(fset *token.FileSet, stmt ast.Stmt, pare s = (*cpg.Statement)(g.handleExpr(fset, v.X)) case *ast.AssignStmt: s = (*cpg.Statement)(g.handleAssignStmt(fset, v, parent)) + case *ast.BranchStmt: + s = (*cpg.Statement)(g.handleBranchStmt(fset, v, parent)) + case *ast.LabeledStmt: + s = (*cpg.Statement)(g.handleLabeledStmt(fset, v, parent)) case *ast.DeclStmt: s = (*cpg.Statement)(g.handleDeclStmt(fset, v)) case *ast.GoStmt: @@ -812,10 +819,8 @@ func (g *GoLanguageFrontend) handleExpr(fset *token.FileSet, expr ast.Expr) (e * func (g *GoLanguageFrontend) handleAssignStmt(fset *token.FileSet, assignStmt *ast.AssignStmt, parent ast.Stmt) (expr *cpg.Expression) { g.LogTrace("Handling assignment statement: %+v", assignStmt) - g.LogDebug("Parent: %#v", parent) - - var rhs = []*cpg.Expression{} - var lhs = []*cpg.Expression{} + var rhs []*cpg.Expression + var lhs []*cpg.Expression for _, expr := range assignStmt.Lhs { lhs = append(lhs, g.handleExpr(fset, expr)) } @@ -841,6 +846,46 @@ func (g *GoLanguageFrontend) handleAssignStmt(fset *token.FileSet, assignStmt *a return } +func (g *GoLanguageFrontend) handleBranchStmt(fset *token.FileSet, branchStmt *ast.BranchStmt, parent ast.Stmt) (expr *cpg.Statement) { + g.LogTrace("Handling branch statement: %+v", branchStmt) + + switch branchStmt.Tok.String() { + case "break": + stmt := g.NewBreakStatement(fset, branchStmt) + if branchStmt.Label != nil { + stmt.SetLabel(branchStmt.Label.Name) + } + + return (*cpg.Statement)(stmt) + case "continue": + stmt := g.NewContinueStatement(fset, branchStmt) + if branchStmt.Label != nil { + stmt.SetLabel(branchStmt.Label.Name) + } + + return (*cpg.Statement)(stmt) + case "goto": + stmt := g.NewGotoStatement(fset, branchStmt) + stmt.SetLabelName(branchStmt.Label.Name) + + return (*cpg.Statement)(stmt) + default: + g.LogError("Unknown token in branch statement: %s", branchStmt.Tok) + } + + return +} + +func (g *GoLanguageFrontend) handleLabeledStmt(fset *token.FileSet, labeledStmt *ast.LabeledStmt, parent ast.Stmt) (expr *cpg.Statement) { + g.LogTrace("Handling labeled statement: %+v", labeledStmt) + + stmt := g.NewLabelStatement(fset, labeledStmt) + stmt.SetSubStatement(g.handleStmt(fset, labeledStmt.Stmt, labeledStmt)) + stmt.SetLabel(labeledStmt.Label.Name) + + return (*cpg.Statement)(stmt) +} + func (g *GoLanguageFrontend) handleDeclStmt(fset *token.FileSet, declStmt *ast.DeclStmt) (expr *cpg.Expression) { g.LogTrace("Handling declaration statement: %+v", *declStmt) @@ -865,10 +910,8 @@ func (g *GoLanguageFrontend) handleDeclStmt(fset *token.FileSet, declStmt *ast.D func (g *GoLanguageFrontend) handleGoStmt(fset *token.FileSet, goStmt *ast.GoStmt) (expr *cpg.Expression) { g.LogTrace("Handling go statement: %+v", *goStmt) - ref := (*cpg.Expression)(g.NewDeclaredReferenceExpression(fset, nil, "go")) - - call := g.NewCallExpression(fset, goStmt, ref, "go") - call.AddArgument(g.handleCallExpr(fset, goStmt.Call)) + call := g.NewUnaryOperator(fset, goStmt, "go", false, true) + call.SetInput(g.handleCallExpr(fset, goStmt.Call)) return (*cpg.Expression)(call) } @@ -924,6 +967,8 @@ func (g *GoLanguageFrontend) handleSwitchStmt(fset *token.FileSet, switchStmt *a s := g.NewSwitchStatement(fset, switchStmt) + g.GetScopeManager().EnterScope((*cpg.Node)(s)) + if switchStmt.Init != nil { s.SetInitializerStatement(g.handleStmt(fset, switchStmt.Init, switchStmt)) } @@ -934,6 +979,8 @@ func (g *GoLanguageFrontend) handleSwitchStmt(fset *token.FileSet, switchStmt *a s.SetStatement((*cpg.Statement)(g.handleBlockStmt(fset, switchStmt.Body))) // should only contain case clauses + g.GetScopeManager().LeaveScope((*cpg.Node)(s)) + return (*cpg.Expression)(s) } diff --git a/cpg-language-go/src/main/golang/frontend/statement_builder.go b/cpg-language-go/src/main/golang/frontend/statement_builder.go index adee1086aa..6783c30531 100644 --- a/cpg-language-go/src/main/golang/frontend/statement_builder.go +++ b/cpg-language-go/src/main/golang/frontend/statement_builder.go @@ -70,6 +70,22 @@ func (g *GoLanguageFrontend) NewDefaultStatement(fset *token.FileSet, astNode as return (*cpg.DefaultStatement)(g.NewStatement("DefaultStatement", fset, astNode)) } +func (g *GoLanguageFrontend) NewLabelStatement(fset *token.FileSet, astNode ast.Node) *cpg.LabelStatement { + return (*cpg.LabelStatement)(g.NewStatement("LabelStatement", fset, astNode)) +} + +func (g *GoLanguageFrontend) NewBreakStatement(fset *token.FileSet, astNode ast.Node) *cpg.BreakStatement { + return (*cpg.BreakStatement)(g.NewStatement("BreakStatement", fset, astNode)) +} + +func (g *GoLanguageFrontend) NewContinueStatement(fset *token.FileSet, astNode ast.Node) *cpg.ContinueStatement { + return (*cpg.ContinueStatement)(g.NewStatement("ContinueStatement", fset, astNode)) +} + +func (g *GoLanguageFrontend) NewGotoStatement(fset *token.FileSet, astNode ast.Node) *cpg.GotoStatement { + return (*cpg.GotoStatement)(g.NewStatement("GotoStatement", fset, astNode)) +} + func (g *GoLanguageFrontend) NewStatement(typ string, fset *token.FileSet, astNode ast.Node, args ...any) *jnigi.ObjectRef { var node = jnigi.NewObjectRef(fmt.Sprintf("%s/%s", cpg.StatementsPackage, typ)) diff --git a/cpg-language-go/src/main/golang/statements.go b/cpg-language-go/src/main/golang/statements.go index f8a1e0ab06..400fbcad0d 100644 --- a/cpg-language-go/src/main/golang/statements.go +++ b/cpg-language-go/src/main/golang/statements.go @@ -39,6 +39,10 @@ type CaseStatement Statement type DefaultStatement Statement type ForStatement Statement type ForEachStatement Statement +type LabelStatement Statement +type BreakStatement Statement +type ContinueStatement Statement +type GotoStatement Statement const StatementsPackage = GraphPackage + "/statements" const StatementClass = StatementsPackage + "/Statement" @@ -126,6 +130,26 @@ func (r *ReturnStatement) SetReturnValue(e *Expression) { (*jnigi.ObjectRef)(r).CallMethod(env, "setReturnValue", nil, (*jnigi.ObjectRef)(e).Cast(ExpressionClass)) } +func (l *LabelStatement) SetSubStatement(s *Statement) { + (*jnigi.ObjectRef)(l).CallMethod(env, "setSubStatement", nil, (*jnigi.ObjectRef)(s).Cast(StatementClass)) +} + +func (l *LabelStatement) SetLabel(s string) { + (*jnigi.ObjectRef)(l).CallMethod(env, "setLabel", nil, NewString(s)) +} + +func (b *BreakStatement) SetLabel(s string) { + (*jnigi.ObjectRef)(b).CallMethod(env, "setLabel", nil, NewString(s)) +} + +func (c *ContinueStatement) SetLabel(s string) { + (*jnigi.ObjectRef)(c).CallMethod(env, "setLabel", nil, NewString(s)) +} + +func (c *GotoStatement) SetLabelName(s string) { + (*jnigi.ObjectRef)(c).CallMethod(env, "setLabelName", nil, NewString(s)) +} + func (s *Statement) ConvertToGo(o *jnigi.ObjectRef) error { *s = (Statement)(*o) return nil diff --git a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoExtraPass.kt b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoExtraPass.kt index 457248abfa..4043d02931 100644 --- a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoExtraPass.kt +++ b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoExtraPass.kt @@ -100,6 +100,10 @@ import de.fraunhofer.aisec.cpg.passes.order.ExecuteBefore * they are compatible. Because types in the same package can be defined in multiple files, we * cannot decide during the frontend run. Therefore, we need to execute this pass before the * [CallResolver] and convert certain [CallExpression] nodes into a [CastExpression]. + * + * ## Resolve Labels of Goto Statements + * + * TBD */ @ExecuteBefore(VariableUsageResolver::class) @ExecuteBefore(CallResolver::class) diff --git a/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt b/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt index c2dab38819..422cfc981a 100644 --- a/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt +++ b/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt @@ -607,7 +607,7 @@ class GoLanguageFrontendTest : BaseTest() { assertNotNull(base) assertEquals(c, base.refersTo) - val go = main.calls["go"] + val go = main.allChildren() { it.operatorCode == "go" } assertNotNull(go) } diff --git a/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/StatementTest.kt b/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/StatementTest.kt new file mode 100644 index 0000000000..2e837fd40d --- /dev/null +++ b/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/StatementTest.kt @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2023, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +package de.fraunhofer.aisec.cpg.frontends.golang + +import de.fraunhofer.aisec.cpg.TestUtils.analyzeAndGetFirstTU +import de.fraunhofer.aisec.cpg.graph.* +import de.fraunhofer.aisec.cpg.graph.statements.* +import de.fraunhofer.aisec.cpg.graph.statements.expressions.Literal +import java.nio.file.Path +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertIs +import kotlin.test.assertNotNull + +class StatementTest { + + @Test + fun testBranchStatement() { + val topLevel = Path.of("src", "test", "resources", "golang") + val tu = + analyzeAndGetFirstTU(listOf(topLevel.resolve("branch.go").toFile()), topLevel, true) { + it.registerLanguage() + } + + assertNotNull(tu) + + val p = tu.namespaces["p"] + assertNotNull(p) + + val main = p.functions["main"] + assertNotNull(main) + + val start = main.allChildren().firstOrNull { it.label == "start" } + assertNotNull(start) + + val cases = start.allChildren() + assertEquals(4, cases.size) + + val case0 = cases.firstOrNull { (it.caseExpression as? Literal<*>)?.value == 0 } + assertNotNull(case0) + + var stmt = case0.nextEOG.firstOrNull() + assertIs(stmt) + + val case1 = cases.firstOrNull { (it.caseExpression as? Literal<*>)?.value == 1 } + assertNotNull(case1) + + stmt = case1.nextEOG.firstOrNull() + val breakStatement = assertIs(stmt) + assertEquals("start", breakStatement.label) + + val default = start.allChildren().firstOrNull() + assertNotNull(default) + + val end = main.allChildren().firstOrNull { it.label == "end" } + assertNotNull(end) + } +} diff --git a/cpg-language-go/src/test/resources/golang/branch.go b/cpg-language-go/src/test/resources/golang/branch.go new file mode 100644 index 0000000000..e0e9e9c12f --- /dev/null +++ b/cpg-language-go/src/test/resources/golang/branch.go @@ -0,0 +1,26 @@ +package p + +import "fmt" + +func main() { + i := 0 + +start: + for { + switch i { + case 0: + continue + case 1: + break start // will break out of the switch and the for-loop + case 2: + fallthrough // will fall through case 3 + case 3: + fmt.Printf("%d", i) + default: + goto end + } + i++ + } + +end: +}