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 8d4684d6b6..aae62941de 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 @@ -280,7 +280,7 @@ fun MetadataProvider.newCallExpression( */ @JvmOverloads fun MetadataProvider.newMemberCallExpression( - callee: Expression?, + callee: Expression? = null, isStatic: Boolean = false, rawNode: Any? = null ): MemberCallExpression { @@ -314,7 +314,7 @@ fun MetadataProvider.newMemberCallExpression( @JvmOverloads fun MetadataProvider.newMemberExpression( name: CharSequence?, - base: Expression, + base: Expression? = null, memberType: Type = unknownType(), operatorCode: String? = ".", rawNode: Any? = null @@ -322,7 +322,7 @@ fun MetadataProvider.newMemberExpression( val node = MemberExpression() node.applyMetadata(this, name, rawNode, true) - node.base = base + base?.let { node.base = it } node.operatorCode = operatorCode node.type = memberType diff --git a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontend.kt b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontend.kt index eec0f337ad..df2c50f454 100644 --- a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontend.kt +++ b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontend.kt @@ -33,7 +33,6 @@ import de.fraunhofer.aisec.cpg.frontends.TranslationException import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.Annotation import de.fraunhofer.aisec.cpg.graph.declarations.* -import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression import de.fraunhofer.aisec.cpg.graph.types.* import de.fraunhofer.aisec.cpg.helpers.Benchmark import de.fraunhofer.aisec.cpg.helpers.CommentMatcher @@ -385,13 +384,14 @@ open class CXXLanguageFrontend(language: Language, ctx: Tra private fun handleAttributes(owner: IASTAttributeOwner): List { val list: MutableList = ArrayList() for (attribute in owner.attributes) { - val annotation = newAnnotation(String(attribute.name), rawNode = owner) - - // go over the parameters - if (attribute.argumentClause is IASTTokenList) { - val members = handleTokenList(attribute.argumentClause as IASTTokenList) - annotation.members = members - } + val annotation = + newAnnotation(String(attribute.name), rawNode = owner).withChildren { + // go over the parameters + if (attribute.argumentClause is IASTTokenList) { + val members = handleTokenList(attribute.argumentClause as IASTTokenList) + it.members = members + } + } list.add(annotation) } return list @@ -411,21 +411,25 @@ open class CXXLanguageFrontend(language: Language, ctx: Tra private fun handleToken(token: IASTToken): AnnotationMember { val code = String(token.tokenCharImage) - val expression: Expression = - when (token.tokenType) { - 1 -> // a variable - newReference(code, unknownType(), rawNode = token) - 2 -> // an integer - newLiteral(code.toInt(), primitiveType("int"), rawNode = token) - 130 -> // a string - newLiteral( - if (code.length >= 2) code.substring(1, code.length - 1) else "", - primitiveType("char").pointer(), - rawNode = token - ) - else -> newLiteral(code, primitiveType("char").pointer(), rawNode = token) + val annotationMember = + newAnnotationMember("", rawNode = token).withChildren { + it.value = + when (token.tokenType) { + 1 -> // a variable + newReference(code, unknownType(), rawNode = token) + 2 -> // an integer + newLiteral(code.toInt(), primitiveType("int"), rawNode = token) + 130 -> // a string + newLiteral( + if (code.length >= 2) code.substring(1, code.length - 1) else "", + primitiveType("char").pointer(), + rawNode = token + ) + else -> newLiteral(code, primitiveType("char").pointer(), rawNode = token) + } } - return newAnnotationMember("", expression, rawNode = token) + + return annotationMember } @Throws(NoSuchFieldException::class) diff --git a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/DeclarationHandler.kt b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/DeclarationHandler.kt index 74451bf7ca..6abe2f4a68 100644 --- a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/DeclarationHandler.kt +++ b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/DeclarationHandler.kt @@ -130,19 +130,15 @@ class DeclarationHandler(lang: CXXLanguageFrontend) : * into a [NamespaceDeclaration]. */ private fun handleNamespace(ctx: CPPASTNamespaceDefinition): NamespaceDeclaration { - val declaration = newNamespaceDeclaration(ctx.name.toString(), rawNode = ctx) - - frontend.scopeManager.addDeclaration(declaration) - // Enter the namespace scope - frontend.scopeManager.enterScope(declaration) - - // Finally, handle all declarations within that namespace - for (child in ctx.declarations) { - handle(child) - } - - frontend.scopeManager.leaveScope(declaration) + val declaration = + newNamespaceDeclaration(ctx.name.toString(), rawNode = ctx).withChildren(true) { + // Finally, handle all declarations within that namespace + for (child in ctx.declarations) { + handle(child) + } + } + frontend.scopeManager.addDeclaration(declaration) return declaration } @@ -191,42 +187,42 @@ class DeclarationHandler(lang: CXXLanguageFrontend) : // Store the reference to a declaration holder of a named scope. val holder = (declaration.scope as? NameScope)?.astNode - if (holder != null && outsideOfScope) { + if (holder != null && outsideOfScope) { // This is a case where we may want a withScope // everything inside the method is within the scope of its record or namespace frontend.scopeManager.enterScope(holder) } // Enter the scope of our function - frontend.scopeManager.enterScope(declaration) - - // Since this is a definition, the body should always be there, but we need to make sure in - // case of parsing errors. - if (ctx.body != null) { - // Let the statement handler take care of the function body. The outcome should (always) - // be a compound statement, holding all other statements. - val bodyStatement = frontend.statementHandler.handle(ctx.body) - if (bodyStatement is Block) { - val statements = bodyStatement.statementEdges - - // get the last statement - var lastStatement: Statement? = null - if (statements.isNotEmpty()) { - lastStatement = statements[statements.size - 1].end - } + declaration.withChildren(true) { + // Since this is a definition, the body should always be there, but we need to make sure + // in + // case of parsing errors. + if (ctx.body != null) { + // Let the statement handler take care of the function body. The outcome should + // (always) + // be a compound statement, holding all other statements. + val bodyStatement = frontend.statementHandler.handle(ctx.body) + if (bodyStatement is Block) { + val statements = bodyStatement.statementEdges + + // get the last statement + var lastStatement: Statement? = null + if (statements.isNotEmpty()) { + lastStatement = statements[statements.size - 1].end + } - // add an implicit return statement, if there is none - if (lastStatement !is ReturnStatement) { - val returnStatement = newReturnStatement() - returnStatement.isImplicit = true - bodyStatement.addStatement(returnStatement) + // add an implicit return statement, if there is none + if (lastStatement !is ReturnStatement) { + val returnStatement = newReturnStatement() + returnStatement.isImplicit = true + bodyStatement.addStatement(returnStatement) + } + declaration.body = bodyStatement } - declaration.body = bodyStatement } - } - frontend.processAttributes(declaration, ctx) - - frontend.scopeManager.leaveScope(declaration) + frontend.processAttributes(declaration, ctx) + } if (holder != null && outsideOfScope) { frontend.scopeManager.leaveScope(holder) @@ -269,12 +265,12 @@ class DeclarationHandler(lang: CXXLanguageFrontend) : templateDeclaration.location = frontend.locationOf(ctx) frontend.scopeManager.addDeclaration(templateDeclaration) - frontend.scopeManager.enterScope(templateDeclaration) - addTemplateParameters(ctx, templateDeclaration) - - // Handle Template - val innerDeclaration = frontend.declarationHandler.handle(ctx.declaration) - frontend.scopeManager.leaveScope(templateDeclaration) + var innerDeclaration: Declaration? = null + templateDeclaration.withChildren(true) { + addTemplateParameters(ctx, templateDeclaration) + // Handle Template + innerDeclaration = frontend.declarationHandler.handle(ctx.declaration) + } if (templateDeclaration is FunctionTemplateDeclaration) { // Fix typeName templateDeclaration.name = templateDeclaration.realizations[0].name.clone() @@ -421,6 +417,9 @@ class DeclarationHandler(lang: CXXLanguageFrontend) : val (primaryDeclaration, useNameOfDeclarator) = handleDeclarationSpecifier(declSpecifier, ctx, sequence) + // Todo These are added to the declarators instead of the declaration, those however are + // created afterwards and + // Todo therefore have no astParent, we need to solve this. // Fill template params, if needed val templateParams: List? = extractTemplateParams(ctx, declSpecifier) @@ -454,53 +453,57 @@ class DeclarationHandler(lang: CXXLanguageFrontend) : val declaration = frontend.declaratorHandler.handle(declarator) as? ValueDeclaration ?: continue - // Parse the type (with some hints) - type = frontend.typeOf(declarator, declSpecifierToUse, declaration) + declaration.withChildren { + // Parse the type (with some hints) + type = frontend.typeOf(declarator, declSpecifierToUse, declaration) - // For function *declarations*, we need to update the return types based on the - // function type. For function *definitions*, this is done in - // [handleFunctionDefinition]. - if (declaration is FunctionDeclaration) { - declaration.returnTypes = - (type as? FunctionType)?.returnTypes ?: listOf(IncompleteType()) - } - - // We also need to set the type, based on the declarator type. - declaration.type = type - - // process attributes - frontend.processAttributes(declaration, ctx) - sequence.addDeclaration(declaration) + // For function *declarations*, we need to update the return types based on the + // function type. For function *definitions*, this is done in + // [handleFunctionDefinition]. + if (declaration is FunctionDeclaration) { + declaration.returnTypes = + (type as? FunctionType)?.returnTypes ?: listOf(IncompleteType()) + } - // We want to make sure that we parse the initializer *after* we have set the - // type. This has several advantages: - // * This way we can deduce, whether our initializer needs to have the - // declared type (in case of a ConstructExpression); - // * or if the declaration needs to have the same type as the initializer (when - // an auto-type is used). The latter case is done internally by the - // VariableDeclaration class and its type observer. - // * Additionally, it makes sure that the type is known before parsing the - // initializer. This allows us to guess cast vs. call expression in the - // initializer. - if (declaration is VariableDeclaration) { - // Set template parameters of the variable (if any) - declaration.templateParameters = templateParams - - // Parse the initializer, if we have one - declarator.initializer?.let { - val initializer = frontend.initializerHandler.handle(it) - when { - // We need to set a resolution "helper" for function pointers, so that a - // reference to this declaration can resolve the function pointer (using - // the type of this declaration). The typical (and only) scenario we - // support here is the assignment of a `&ref` as initializer. - initializer is UnaryOperator && type is FunctionPointerType -> { - val ref = initializer.input as? Reference - ref?.resolutionHelper = declaration + // We also need to set the type, based on the declarator type. + declaration.type = type + + // process attributes + frontend.processAttributes(declaration, ctx) + sequence.addDeclaration(declaration) + + // We want to make sure that we parse the initializer *after* we have set the + // type. This has several advantages: + // * This way we can deduce, whether our initializer needs to have the + // declared type (in case of a ConstructExpression); + // * or if the declaration needs to have the same type as the initializer (when + // an auto-type is used). The latter case is done internally by the + // VariableDeclaration class and its type observer. + // * Additionally, it makes sure that the type is known before parsing the + // initializer. This allows us to guess cast vs. call expression in the + // initializer. + if (declaration is VariableDeclaration) { + // Set template parameters of the variable (if any) + declaration.templateParameters = templateParams + + // Parse the initializer, if we have one + declarator.initializer?.let { + val initializer = frontend.initializerHandler.handle(it) + when { + // We need to set a resolution "helper" for function pointers, so + // that a + // reference to this declaration can resolve the function pointer + // (using + // the type of this declaration). The typical (and only) scenario we + // support here is the assignment of a `&ref` as initializer. + initializer is UnaryOperator && type is FunctionPointerType -> { + val ref = initializer.input as? Reference + ref?.resolutionHelper = declaration + } } - } - declaration.initializer = initializer + declaration.initializer = initializer + } } } } @@ -641,32 +644,28 @@ class DeclarationHandler(lang: CXXLanguageFrontend) : declSpecifier: IASTEnumerationSpecifier ): EnumDeclaration { val entries = mutableListOf() - val enum = newEnumDeclaration(name = declSpecifier.name.toString(), rawNode = ctx) - - // Loop through its members - for (enumerator in declSpecifier.enumerators) { - // Enums are a bit complicated. Their fully qualified name (in C++) includes the enum - // class, so e.g. `MyEnum::THIS'. In order to do that, we need to be in the `MyEnum` - // scope when we create it. But, the symbol of the enum can both be resolved using just - // the enum constant `THIS` as well as `MyEnum::THIS` (at least in C++11). So we need to - // put the symbol both in the outer scope as well as the enum's scope. - frontend.scopeManager.enterScope(enum) - val enumConst = - newEnumConstantDeclaration( - enumerator.name.toString(), - rawNode = enumerator, - ) - frontend.scopeManager.addDeclaration(enumConst) - frontend.scopeManager.leaveScope(enum) + val enum = + newEnumDeclaration(name = declSpecifier.name.toString(), rawNode = ctx).withChildren { + // Loop through its members + for (enumerator in declSpecifier.enumerators) { + val enumConst = + newEnumConstantDeclaration( + enumerator.name.toString(), + rawNode = enumerator, + ) - // In C/C++, default enums are of type int - enumConst.type = primitiveType("int") + // In C/C++, default enums are of type int + enumConst.type = primitiveType("int") - // Also put the symbol in the outer scope (but do not add AST nodes) - frontend.scopeManager.addDeclaration(enumConst, false) - } + // We need to make them visible to the enclosing scope. However, we do NOT + // want to add it to the AST of the enclosing scope, but to the AST of the + // EnumDeclaration + frontend.scopeManager.addDeclaration(enumConst, false) - enum.entries = entries + entries += enumConst + } + it.entries = entries + } return enum } @@ -718,28 +717,33 @@ class DeclarationHandler(lang: CXXLanguageFrontend) : fun handleTranslationUnit(translationUnit: IASTTranslationUnit): TranslationUnitDeclaration { val node = newTranslationUnitDeclaration(translationUnit.filePath, rawNode = translationUnit) - - // There might have been errors in the previous translation unit and in any case - // we need to reset the scope manager scope to global, to avoid spilling scope errors into - // other translation units - frontend.scopeManager.resetToGlobal(node) - frontend.currentTU = node - val problematicIncludes = HashMap>() - - for (declaration in translationUnit.declarations) { - val decl = handle(declaration) ?: continue - (decl as? ProblemDeclaration)?.location?.let { - val problems = - problematicIncludes.computeIfAbsent(it.artifactLocation.toString()) { - HashSet() + .withChildren { + // There might have been errors in the previous translation unit and in any case + // we need to reset the scope manager scope to global, to avoid spilling scope + // errors + // into + // other translation units + frontend.scopeManager.resetToGlobal(it) + frontend.currentTU = it + val problematicIncludes = HashMap>() + + for (declaration in translationUnit.declarations) { + val decl = handle(declaration) ?: continue + (decl as? ProblemDeclaration)?.location?.let { + val problems = + problematicIncludes.computeIfAbsent( + it.artifactLocation.toString() + ) { + HashSet() + } + problems.add(decl) + } } - problems.add(decl) - } - } - if (frontend.config.addIncludesToGraph) { - addIncludes(translationUnit, problematicIncludes, node) - } + if (frontend.config.addIncludesToGraph) { + addIncludes(translationUnit, problematicIncludes, it) + } + } return node } @@ -760,6 +764,8 @@ class DeclarationHandler(lang: CXXLanguageFrontend) : node: TranslationUnitDeclaration ) { // TODO: Remark CB: I am not quite sure, what the point of the code beyond this line is. + // TODO: Remark KW: Yes, indeed, we should also investigate why include declarations have + // other include declarations // Probably needs to be refactored // this tree is a bit problematic: If a file was already included before, it will not be diff --git a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/DeclaratorHandler.kt b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/DeclaratorHandler.kt index 0fd21a046c..a176620cf3 100644 --- a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/DeclaratorHandler.kt +++ b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/DeclaratorHandler.kt @@ -293,62 +293,65 @@ class DeclaratorHandler(lang: CXXLanguageFrontend) : } // Enter the scope of the function itself - frontend.scopeManager.enterScope(declaration) + declaration.withChildren(true) { - // Create the method receiver (if this is a method) - if (declaration is MethodDeclaration) { - createMethodReceiver(declaration) - } + // Create the method receiver (if this is a method) + if (declaration is MethodDeclaration) { + createMethodReceiver(declaration) + } - var i = 0 - for (param in ctx.parameters) { - val arg = frontend.parameterDeclarationHandler.handle(param) - - if (arg is ParameterDeclaration) { - // check for void type parameters - if (arg.type is IncompleteType) { - if (arg.name.isNotEmpty()) { - Util.warnWithFileLocation( - declaration, - log, - "Named parameter cannot have void type" - ) - } else { - // specifying void as first parameter is ok and means that the function has - // no parameters - if (i == 0) { - continue - } else { + var i = 0 + for (param in ctx.parameters) { + val arg = frontend.parameterDeclarationHandler.handle(param) + + if (arg is ParameterDeclaration) { + // check for void type parameters + if (arg.type is IncompleteType) { + if (arg.name.isNotEmpty()) { Util.warnWithFileLocation( declaration, log, - "void parameter must be the first and only parameter" + "Named parameter cannot have void type" ) + } else { + // specifying void as first parameter is ok and means that the function + // has + // no parameters + if (i == 0) { + continue + } else { + Util.warnWithFileLocation( + declaration, + log, + "void parameter must be the first and only parameter" + ) + } } } - } - arg.argumentIndex = i + arg.argumentIndex = i + } + // Note that this .addValueDeclaration call already adds arg to the function's + // parameters. + // This is why the following line has been commented out by @KW + frontend.scopeManager.addDeclaration(arg) + // declaration.getParameters().add(arg); + i++ } - // Note that this .addValueDeclaration call already adds arg to the function's - // parameters. - // This is why the following line has been commented out by @KW - frontend.scopeManager.addDeclaration(arg) - // declaration.getParameters().add(arg); - i++ - } - // Check for varargs. Note the difference to Java: here, we don't have a named array - // containing the varargs, but they are rather treated as kind of an invisible arg list that - // is appended to the original ones. For coherent graph behaviour, we introduce an implicit - // declaration that wraps this list - if (ctx.takesVarArgs()) { - val varargs = newParameterDeclaration("va_args", unknownType(), true) - varargs.isImplicit = true - varargs.argumentIndex = i - frontend.scopeManager.addDeclaration(varargs) + // Check for varargs. Note the difference to Java: here, we don't have a named array + // containing the varargs, but they are rather treated as kind of an invisible arg list + // that + // is appended to the original ones. For coherent graph behaviour, we introduce an + // implicit + // declaration that wraps this list + if (ctx.takesVarArgs()) { + val varargs = newParameterDeclaration("va_args", unknownType(), true) + varargs.isImplicit = true + varargs.argumentIndex = i + frontend.scopeManager.addDeclaration(varargs) + } } - frontend.scopeManager.leaveScope(declaration) // if we know our record declaration, but are outside the actual record, we // need to leave the record scope again afterwards @@ -461,29 +464,28 @@ class DeclaratorHandler(lang: CXXLanguageFrontend) : frontend.scopeManager.addDeclaration(recordDeclaration) - frontend.scopeManager.enterScope(recordDeclaration) - - processMembers(ctx) + recordDeclaration.withChildren(true) { + processMembers(ctx) - if (recordDeclaration.constructors.isEmpty()) { - // create an implicit constructor declaration with the same name as the record - val constructorDeclaration = - newConstructorDeclaration( - recordDeclaration.name.localName, - recordDeclaration, - ) - .implicit(code = recordDeclaration.name.localName) + if (recordDeclaration.constructors.isEmpty()) { + // create an implicit constructor declaration with the same name as the record + val constructorDeclaration = + newConstructorDeclaration( + recordDeclaration.name.localName, + recordDeclaration, + ) + .implicit(code = recordDeclaration.name.localName) - createMethodReceiver(constructorDeclaration) + createMethodReceiver(constructorDeclaration) - // and set the type, constructors always have implicitly the return type of their class - constructorDeclaration.type = FunctionType.computeType(constructorDeclaration) - recordDeclaration.addConstructor(constructorDeclaration) - frontend.scopeManager.addDeclaration(constructorDeclaration) + // and set the type, constructors always have implicitly the return type of their + // class + constructorDeclaration.type = FunctionType.computeType(constructorDeclaration) + recordDeclaration.addConstructor(constructorDeclaration) + frontend.scopeManager.addDeclaration(constructorDeclaration) + } } - frontend.scopeManager.leaveScope(recordDeclaration) - return recordDeclaration } diff --git a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/ExpressionHandler.kt b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/ExpressionHandler.kt index 88393994a0..eb8a42c672 100644 --- a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/ExpressionHandler.kt +++ b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/ExpressionHandler.kt @@ -102,17 +102,26 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : node: CPPASTSimpleTypeConstructorExpression ): Expression { return if (node.declSpecifier is IASTSimpleDeclSpecifier) { - val cast = newCastExpression(rawNode = node) - cast.castType = frontend.typeOf(node.declSpecifier) - - // The actual expression that is cast is nested in an initializer. We could forward - // this to our initializer handler, but this would create a lot of construct expressions - // just for simple type casts, which we want to avoid, so we take a shortcut and do a - // direct unwrapping here. - val single = - (node.initializer as? ICPPASTConstructorInitializer)?.arguments?.singleOrNull() - cast.expression = - single?.let { handle(it) } ?: newProblemExpression("could not parse initializer") + val cast = + newCastExpression(rawNode = node).withChildren { + it.castType = frontend.typeOf(node.declSpecifier) + + // The actual expression that is cast is nested in an initializer. We could + // forward + // this to our initializer handler, but this would create a lot of construct + // expressions + // just for simple type casts, which we want to avoid, so we take a shortcut and + // do + // a + // direct unwrapping here. + val single = + (node.initializer as? ICPPASTConstructorInitializer) + ?.arguments + ?.singleOrNull() + it.expression = + single?.let { handle(it) } + ?: newProblemExpression("could not parse initializer") + } cast } else { // Otherwise, we try to parse it as an initializer, which must either be an initializer @@ -121,50 +130,60 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : if (initializer is InitializerListExpression) { val construct = newConstructExpression(rawNode = node) construct.arguments = initializer.initializers.toList() + // We have to set the ast Parent manually here because we have to construct the + // initializer first before inferring if we have to wrap it in a construct + // expressions + initializer.astParent = construct construct } else initializer ?: newProblemExpression("could not parse initializer") } } private fun handleLambdaExpression(node: CPPASTLambdaExpression): Expression { - val lambda = newLambdaExpression(rawNode = node) - - // Variables passed by reference are mutable. If we have initializers, we have to model the - // variable explicitly. - for (capture in node.captures) { - if (capture is CPPASTInitCapture) { - // TODO: The scope manager isn't able to resolve this correctly. - frontend.declaratorHandler.handle(capture.declarator)?.let { - it.isImplicit = true - lambda.addDeclaration(it) - } - } else { - if (capture.isByReference) { - val valueDeclaration = - frontend.scopeManager.resolveReference( - newReference(capture?.identifier?.toString()) - ) - valueDeclaration?.let { lambda.mutableVariables.add(it) } + val lambda = + newLambdaExpression(rawNode = node).withChildren { lambda -> + // Variables passed by reference are mutable. If we have initializers, we have to + // model + // the + // variable explicitly. + for (capture in node.captures) { + if (capture is CPPASTInitCapture) { + // TODO: The scope manager isn't able to resolve this correctly. + frontend.declaratorHandler.handle(capture.declarator)?.let { + it.isImplicit = true + lambda.addDeclaration(it) + } + } else { + if (capture.isByReference) { + val valueDeclaration = + frontend.scopeManager.resolveReference( + newReference(capture?.identifier?.toString()) + ) + valueDeclaration?.let { lambda.mutableVariables.add(it) } + } + } } - } - } - // By default, the outer variables passed by value to the lambda are immutable. But we can - // either make the function "mutable" or pass everything by reference. - lambda.areVariablesMutable = - (node.declarator as? CPPASTFunctionDeclarator)?.isMutable == true || - node.captureDefault == ICPPASTLambdaExpression.CaptureDefault.BY_REFERENCE - - val anonymousFunction = - node.declarator?.let { frontend.declaratorHandler.handle(it) as? FunctionDeclaration } - ?: newFunctionDeclaration("lambda${lambda.hashCode()}") - anonymousFunction.type = FunctionType.computeType(anonymousFunction) - - frontend.scopeManager.enterScope(anonymousFunction) - anonymousFunction.body = frontend.statementHandler.handle(node.body) - frontend.scopeManager.leaveScope(anonymousFunction) + // By default, the outer variables passed by value to the lambda are immutable. But + // we + // can + // either make the function "mutable" or pass everything by reference. + lambda.areVariablesMutable = + (node.declarator as? CPPASTFunctionDeclarator)?.isMutable == true || + node.captureDefault == ICPPASTLambdaExpression.CaptureDefault.BY_REFERENCE + + val anonymousFunction = + node.declarator?.let { + frontend.declaratorHandler.handle(it) as? FunctionDeclaration + } ?: newFunctionDeclaration("lambda${lambda.hashCode()}") + anonymousFunction.type = FunctionType.computeType(anonymousFunction) + + anonymousFunction.withChildren(true) { + anonymousFunction.body = frontend.statementHandler.handle(node.body) + } - lambda.function = anonymousFunction + lambda.function = anonymousFunction + } return lambda } @@ -211,9 +230,11 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : } private fun handleArraySubscriptExpression(ctx: IASTArraySubscriptExpression): Expression { - val arraySubsExpression = newSubscriptExpression(rawNode = ctx) - handle(ctx.arrayExpression)?.let { arraySubsExpression.arrayExpression = it } - handle(ctx.argument)?.let { arraySubsExpression.subscriptExpression = it } + val arraySubsExpression = + newSubscriptExpression(rawNode = ctx).withChildren { arraySubsExpression -> + handle(ctx.arrayExpression)?.let { arraySubsExpression.arrayExpression = it } + handle(ctx.argument)?.let { arraySubsExpression.subscriptExpression = it } + } return arraySubsExpression } @@ -225,51 +246,64 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : return if (ctx.isArrayAllocation) { t.array() val arrayMods = (ctx.typeId.abstractDeclarator as IASTArrayDeclarator).arrayModifiers - val arrayCreate = newNewArrayExpression(rawNode = ctx) - arrayCreate.type = t - for (arrayMod in arrayMods) { - val constant = handle(arrayMod.constantExpression) - constant?.let { arrayCreate.addDimension(it) } - } - if (init != null) { - arrayCreate.initializer = frontend.initializerHandler.handle(init) - } + val arrayCreate = + newNewArrayExpression(rawNode = ctx).withChildren { arrayCreate -> + arrayCreate.type = t + for (arrayMod in arrayMods) { + val constant = handle(arrayMod.constantExpression) + constant?.let { arrayCreate.addDimension(it) } + } + if (init != null) { + arrayCreate.initializer = frontend.initializerHandler.handle(init) + } + } arrayCreate } else { // new returns a pointer, so we need to reference the type by pointer - val newExpression = newNewExpression(type = t.pointer(), rawNode = ctx) - val declSpecifier = ctx.typeId.declSpecifier as? IASTNamedTypeSpecifier - // Resolve possible templates - if (declSpecifier?.name is CPPASTTemplateId) { - newExpression.templateParameters = - getTemplateArguments(declSpecifier.name as CPPASTTemplateId) - } + val newExpression = + newNewExpression(type = t.pointer(), rawNode = ctx).withChildren { + val declSpecifier = ctx.typeId.declSpecifier as? IASTNamedTypeSpecifier + // Resolve possible templates + if (declSpecifier?.name is CPPASTTemplateId) { + it.templateParameters = + getTemplateArguments(declSpecifier.name as CPPASTTemplateId) + } - val initializer: Expression? - if (init != null) { - initializer = frontend.initializerHandler.handle(init) - } else { - // in C++, it is possible to omit the `()` part, when creating an object, such as - // `new A`. - // Therefore, CDT does not have an explicit construct expression, so we need create - // an implicit one - initializer = - newConstructExpression(t.name.localName) - .implicit(code = "${t.name.localName}()") - initializer.type = t - } + val initializer: Expression? + if (init != null) { + initializer = frontend.initializerHandler.handle(init) + } else { + // in C++, it is possible to omit the `()` part, when creating an object, + // such + // as + // `new A`. + // Therefore, CDT does not have an explicit construct expression, so we need + // create + // an implicit one + initializer = + newConstructExpression(t.name.localName) + .implicit(code = "${t.name.localName}()") + initializer.type = t + } - // we also need to "forward" our template parameters (if we have any) to the construct - // expression since the construct expression will do the actual template instantiation - if (newExpression.templateParameters?.isNotEmpty() == true) { - newExpression.templateParameters?.let { - addImplicitTemplateParametersToCall(it, initializer as ConstructExpression) - } - } + // we also need to "forward" our template parameters (if we have any) to the + // construct + // expression since the construct expression will do the actual template + // instantiation + if (it.templateParameters?.isNotEmpty() == true) { + it.templateParameters?.let { + addImplicitTemplateParametersToCall( + it, + initializer as ConstructExpression + ) + } + } - // our initializer, such as a construct expression, will have the non-pointer type - initializer?.type = t - newExpression.initializer = initializer + // our initializer, such as a construct expression, will have the non-pointer + // type + initializer?.type = t + it.initializer = initializer + } newExpression } } @@ -304,36 +338,41 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : } private fun handleConditionalExpression(ctx: IASTConditionalExpression): ConditionalExpression { - val condition = - handle(ctx.logicalConditionExpression) - ?: ProblemExpression("could not parse condition expression") - return newConditionalExpression( - condition, - if (ctx.positiveResultExpression != null) handle(ctx.positiveResultExpression) - else condition, - handle(ctx.negativeResultExpression) - ) + val conditionalExpression = + newConditionalExpression().withChildren { + it.condition = + handle(ctx.logicalConditionExpression) + ?: ProblemExpression("could not parse condition expression") + it.thenExpression = + if (ctx.positiveResultExpression != null) handle(ctx.positiveResultExpression) + else it.condition + it.elseExpression = handle(ctx.negativeResultExpression) + } + + return conditionalExpression } private fun handleDeleteExpression(ctx: CPPASTDeleteExpression): DeleteExpression { - val deleteExpression = newDeleteExpression(rawNode = ctx) + val deleteExpression = + newDeleteExpression(rawNode = ctx).withChildren { it.operand = handle(ctx.operand) } for (name in ctx.implicitDestructorNames) { log.debug("Implicit constructor name {}", name) } - deleteExpression.operand = handle(ctx.operand) return deleteExpression } private fun handleCastExpression(ctx: IASTCastExpression): Expression { - val castExpression = newCastExpression(rawNode = ctx) - castExpression.expression = - handle(ctx.operand) ?: ProblemExpression("could not parse inner expression") - castExpression.setCastOperator(ctx.operator) - castExpression.castType = frontend.typeOf(ctx.typeId) - - if (isPrimitive(castExpression.castType) || ctx.operator == 4) { - castExpression.type = castExpression.castType - } + val castExpression = + newCastExpression(rawNode = ctx).withChildren { + it.expression = + handle(ctx.operand) ?: ProblemExpression("could not parse inner expression") + it.setCastOperator(ctx.operator) + it.castType = frontend.typeOf(ctx.typeId) + + if (isPrimitive(it.castType) || ctx.operator == 4) { + it.type = it.castType + } + } return castExpression } @@ -344,32 +383,34 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : * [MemberExpression]. */ private fun handleFieldReference(ctx: IASTFieldReference): Expression { - val base = handle(ctx.fieldOwner) ?: return newProblemExpression("base of field is null") - // We need some special handling for templates (of course). Since we only want the basic // name without any arguments as a name - val name = - if (ctx.fieldName is CPPASTTemplateId) { - (ctx.fieldName as CPPASTTemplateId).templateName.toString() - } else { - ctx.fieldName.toString() - } - - return newMemberExpression( - name, - base, - unknownType(), - if (ctx.isPointerDereference) "->" else ".", - rawNode = ctx - ) + var base: Expression? = null + val memberExpression = + newMemberExpression( + if (ctx.fieldName is CPPASTTemplateId) { + (ctx.fieldName as CPPASTTemplateId).templateName.toString() + } else { + ctx.fieldName.toString() + }, + memberType = unknownType(), + operatorCode = if (ctx.isPointerDereference) "->" else ".", + rawNode = ctx + ) + .withChildren { base = handle(ctx.fieldOwner) } + if (base == null) { + return newProblemExpression("base of field is null") + } else { + memberExpression.base = base as Expression + } + return memberExpression } private fun handleUnaryExpression(ctx: IASTUnaryExpression): Expression { var input: Expression? = null - if (ctx.operand != null) { // can be null e.g. for "throw;" - input = handle(ctx.operand) - } + var operatorCode = "" + var unaryOperator: UnaryOperator? = null when (ctx.operator) { IASTUnaryExpression.op_prefixIncr, IASTUnaryExpression.op_postFixIncr -> operatorCode = "++" @@ -387,17 +428,21 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : // something similar, we want to keep the information that this is an expression // wrapped in parentheses. The best way to do this is to create a unary expression if (ctx.operand is IASTIdExpression && ctx.parent !is IASTFunctionCallExpression) { - val op = newUnaryOperator("()", postfix = true, prefix = true, rawNode = ctx) - if (input != null) { - op.input = input - } - return op + unaryOperator = + newUnaryOperator("()", postfix = true, prefix = true, rawNode = ctx) + .withChildren { + if (ctx.operand != null) { + it.input = handle(ctx.operand) ?: it.input + } + } + + return unaryOperator } // In all other cases, e.g., if the parenthesis is nested or part of a function // call, we just return the unwrapped expression, because in this case we do not // need to information about the parenthesis. - return input as Expression + return handle(ctx.operand) as Expression } IASTUnaryExpression.op_throw -> operatorCode = "throw" IASTUnaryExpression.op_typeid -> operatorCode = "typeid" @@ -408,15 +453,19 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : else -> Util.errorWithFileLocation(frontend, ctx, log, "unknown operator {}", ctx.operator) } - val unaryOperator = - newUnaryOperator( - operatorCode, - ctx.isPostfixOperator, - !ctx.isPostfixOperator, - rawNode = ctx - ) - if (input != null) { - unaryOperator.input = input + if (unaryOperator == null) { + unaryOperator = + newUnaryOperator( + operatorCode, + ctx.isPostfixOperator, + !ctx.isPostfixOperator, + rawNode = ctx + ) + } + unaryOperator.withChildren { + if (ctx.operand != null) { // can be null e.g. for "throw;" + handle(ctx.operand)?.let { unaryOperator.input = it } + } } return unaryOperator } @@ -489,6 +538,10 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : // Important: we don't really need the reference node, but even its temporary creation might // leave unwanted artifacts behind in the final graph! reference?.disconnectFromGraph() + // Unfortunately when the construction of the parent depends on the type of children, we + // have to set the parent manully + callExpression.astChildren.forEach { it.astParent = callExpression } + return callExpression } @@ -528,10 +581,12 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : } private fun handleExpressionList(exprList: IASTExpressionList): ExpressionList { - val expressionList = newExpressionList(rawNode = exprList) - for (expr in exprList.expressions) { - handle(expr)?.let { expressionList.addExpression(it) } - } + val expressionList = + newExpressionList(rawNode = exprList).withChildren { expressionList -> + for (expr in exprList.expressions) { + handle(expr)?.let { expressionList.addExpression(it) } + } + } return expressionList } @@ -556,35 +611,38 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : else -> String(ASTStringUtil.getBinaryOperatorString(ctx)) } - val binaryOperator = newBinaryOperator(operatorCode, rawNode = ctx) - val lhs = handle(ctx.operand1) ?: newProblemExpression("could not parse lhs") - val rhs = - if (ctx.operand2 != null) { - handle(ctx.operand2) - } else { - handle(ctx.initOperand2) - } ?: newProblemExpression("could not parse rhs") - - binaryOperator.lhs = lhs - binaryOperator.rhs = rhs + val binaryOperator = + newBinaryOperator(operatorCode, rawNode = ctx).withChildren { + it.lhs = handle(ctx.operand1) ?: newProblemExpression("could not parse lhs") + it.rhs = + if (ctx.operand2 != null) { + handle(ctx.operand2) + } else { + handle(ctx.initOperand2) + } ?: newProblemExpression("could not parse rhs") + } return binaryOperator } private fun handleAssignment(ctx: IASTBinaryExpression): Expression { - val lhs = handle(ctx.operand1) ?: newProblemExpression("missing LHS") - val rhs = - if (ctx.operand2 != null) { - handle(ctx.operand2) - } else { - handle(ctx.initOperand2) - } ?: newProblemExpression("missing RHS") - val operatorCode = String(ASTStringUtil.getBinaryOperatorString(ctx)) - val assign = newAssignExpression(operatorCode, listOf(lhs), listOf(rhs), rawNode = ctx) - if (rhs is UnaryOperator && rhs.input is Reference) { - (rhs.input as Reference).resolutionHelper = lhs - } + val assign = + newAssignExpression(operatorCode, rawNode = ctx).withChildren { + val lhs = handle(ctx.operand1) ?: newProblemExpression("missing LHS") + val rhs = + if (ctx.operand2 != null) { + handle(ctx.operand2) + } else { + handle(ctx.initOperand2) + } ?: newProblemExpression("missing RHS") + + if (rhs is UnaryOperator && rhs.input is Reference) { + (rhs.input as Reference).resolutionHelper = lhs + } + it.lhs = listOf(lhs) + it.rhs = listOf(rhs) + } return assign } @@ -736,7 +794,6 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : } private fun handleCXXDesignatedInitializer(ctx: CPPASTDesignatedInitializer): Expression { - val rhs = handle(ctx.operand) // We need to check the first designator first val des = ctx.designators.firstOrNull() @@ -752,47 +809,56 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : as? IASTDeclarator) ?.name .toString() - var ref = newReference(baseName) - - val lhs = - when (des) { - is CPPASTArrayDesignator -> { - val sub = newSubscriptExpression() - sub.arrayExpression = ref - handle(des.subscriptExpression)?.let { sub.subscriptExpression = it } - sub - } - is CPPASTFieldDesignator -> { - // Then we loop through all designators and chain them. Only field designators - // can be chained in this way - for (field in - ctx.designators.toList().filterIsInstance()) { - // the old ref is our new base - ref = newMemberExpression(field.name.toString(), ref, rawNode = field) + var ref: Reference? = null + + val assignExpression = + newAssignExpression(rawNode = ctx).withChildren { + val lhs = + when (des) { + is CPPASTArrayDesignator -> { + val sub = + newSubscriptExpression().withChildren { sub -> + sub.arrayExpression = newReference(baseName) + handle(des.subscriptExpression)?.let { + sub.subscriptExpression = it + } + } + sub + } + is CPPASTFieldDesignator -> { + // Then we loop through all designators and chain them. Only field + // designators + // can be chained in this way + for (field in + ctx.designators + .toList() + .filterIsInstance()) { + // the old ref is our new base + ref = + newMemberExpression(field.name.toString(), rawNode = field) + .withChildren { it.base = newReference(baseName) } + } + ref ?: newReference(baseName) + } + else -> { + Util.errorWithFileLocation( + frontend, + ctx, + log, + "Unknown designated lhs {}", + des.javaClass.toGenericString() + ) + null + } } - ref - } - else -> { - Util.errorWithFileLocation( - frontend, - ctx, - log, - "Unknown designated lhs {}", - des.javaClass.toGenericString() - ) - null - } + it.lhs = listOfNotNull(lhs) + it.rhs = listOfNotNull(handle(ctx.operand)) } - return newAssignExpression( - lhs = listOfNotNull(lhs), - rhs = listOfNotNull(rhs), - rawNode = ctx - ) + return assignExpression } private fun handleCDesignatedInitializer(ctx: CASTDesignatedInitializer): Expression { - val rhs = handle(ctx.operand) // We need to check the first designator first val des = ctx.designators.firstOrNull() @@ -808,54 +874,65 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : as? IASTDeclarator) ?.name .toString() - var ref = newReference(baseName) - - val lhs = - when (des) { - is CASTArrayDesignator -> { - val sub = newSubscriptExpression(rawNode = des) - sub.arrayExpression = ref - handle(des.subscriptExpression)?.let { sub.subscriptExpression = it } - sub - } - is CASTArrayRangeDesignator -> { - val sub = newSubscriptExpression(rawNode = des) - sub.arrayExpression = ref - - val range = newRangeExpression(rawNode = des) - des.rangeFloor?.let { range.floor = handle(it) } - des.rangeCeiling?.let { range.ceiling = handle(it) } - range.operatorCode = "..." - sub.subscriptExpression = range - sub - } - is CASTFieldDesignator -> { - // Then we loop through all designators and chain them. Only field designators - // can be chained in this way - for (field in - ctx.designators.toList().filterIsInstance()) { - // the old ref is our new base - ref = newMemberExpression(field.name.toString(), ref, rawNode = field) + + val assignExpression = + newAssignExpression(rawNode = ctx).withChildren { + val lhs = + when (des) { + is CASTArrayDesignator -> { + val sub = newSubscriptExpression(rawNode = des) + sub.withChildren { + sub.arrayExpression = newReference(baseName) + handle(des.subscriptExpression)?.let { + sub.subscriptExpression = it + } + } + sub + } + is CASTArrayRangeDesignator -> { + val sub = newSubscriptExpression(rawNode = des) + sub.withChildren { + sub.arrayExpression = newReference(baseName) + + val range = newRangeExpression(rawNode = des) + range.withChildren { + des.rangeFloor?.let { range.floor = handle(it) } + des.rangeCeiling?.let { range.ceiling = handle(it) } + range.operatorCode = "..." + } + sub.subscriptExpression = range + } + sub + } + is CASTFieldDesignator -> { + // Then we loop through all designators and chain them. Only field + // designators + // can be chained in this way + var ref: Reference? = null + for (field in + ctx.designators.toList().filterIsInstance()) { + // the old ref is our new base + ref = newMemberExpression(field.name.toString(), rawNode = field) + ref.withChildren { ref.base = newReference(baseName) } + } + ref ?: newReference(baseName) + } + else -> { + Util.errorWithFileLocation( + frontend, + ctx, + log, + "Unknown designated lhs {}", + des.javaClass.toGenericString() + ) + null + } } - ref - } - else -> { - Util.errorWithFileLocation( - frontend, - ctx, - log, - "Unknown designated lhs {}", - des.javaClass.toGenericString() - ) - null - } + it.lhs = listOfNotNull(lhs) + it.rhs = listOfNotNull(handle(ctx.operand)) } - return newAssignExpression( - lhs = listOfNotNull(lhs), - rhs = listOfNotNull(rhs), - rawNode = ctx - ) + return assignExpression } private fun handleTypeIdInitializerExpression( @@ -863,13 +940,16 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : ): ConstructExpression { val type = frontend.typeOf(ctx.typeId) - val construct = newConstructExpression(type.name, rawNode = ctx) - - // The only supported initializer is an initializer list - (ctx.initializer as? IASTInitializerList)?.let { - construct.arguments = - it.clauses.map { handle(it) ?: newProblemExpression("could not parse argument") } - } + val construct = + newConstructExpression(type.name, rawNode = ctx).withChildren { construct -> + // The only supported initializer is an initializer list + (ctx.initializer as? IASTInitializerList)?.let { + construct.arguments = + it.clauses.map { + handle(it) ?: newProblemExpression("could not parse argument") + } + } + } return construct } diff --git a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/InitializerHandler.kt b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/InitializerHandler.kt index d2c4cdcb8f..61481cf609 100644 --- a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/InitializerHandler.kt +++ b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/InitializerHandler.kt @@ -25,17 +25,14 @@ */ package de.fraunhofer.aisec.cpg.frontends.cxx +import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.ValueDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration import de.fraunhofer.aisec.cpg.graph.edge.Properties import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge -import de.fraunhofer.aisec.cpg.graph.newConstructExpression -import de.fraunhofer.aisec.cpg.graph.newInitializerListExpression -import de.fraunhofer.aisec.cpg.graph.newProblemExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression import de.fraunhofer.aisec.cpg.graph.statements.expressions.InitializerListExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.ProblemExpression -import de.fraunhofer.aisec.cpg.graph.unknownType import java.util.function.Supplier import org.eclipse.cdt.core.dom.ast.IASTEqualsInitializer import org.eclipse.cdt.core.dom.ast.IASTInitializer @@ -57,18 +54,19 @@ class InitializerHandler(lang: CXXLanguageFrontend) : } private fun handleConstructorInitializer(ctx: CPPASTConstructorInitializer): Expression { - val constructExpression = newConstructExpression(rawNode = ctx) + val constructExpression = + newConstructExpression(rawNode = ctx).withChildren { constructExpression -> + for ((i, argument) in ctx.arguments.withIndex()) { + val arg = frontend.expressionHandler.handle(argument) + arg?.let { + it.argumentIndex = i + constructExpression.addArgument(it) + } + } + } constructExpression.type = (frontend.declaratorHandler.lastNode as? VariableDeclaration)?.type ?: unknownType() - for ((i, argument) in ctx.arguments.withIndex()) { - val arg = frontend.expressionHandler.handle(argument) - arg?.let { - it.argumentIndex = i - constructExpression.addArgument(it) - } - } - return constructExpression } @@ -79,17 +77,18 @@ class InitializerHandler(lang: CXXLanguageFrontend) : val targetType = (frontend.declaratorHandler.lastNode as? ValueDeclaration)?.type ?: unknownType() - val expression = newInitializerListExpression(targetType, rawNode = ctx) - - for (clause in ctx.clauses) { - frontend.expressionHandler.handle(clause)?.let { - val edge = PropertyEdge(expression, it) - edge.addProperty(Properties.INDEX, expression.initializerEdges.size) + val expression = + newInitializerListExpression(targetType, rawNode = ctx).withChildren { expression -> + for (clause in ctx.clauses) { + frontend.expressionHandler.handle(clause)?.let { + val edge = PropertyEdge(expression, it) + edge.addProperty(Properties.INDEX, expression.initializerEdges.size) - expression.initializerEdges.add(edge) - expression.addPrevDFG(it) + expression.initializerEdges.add(edge) + expression.addPrevDFG(it) + } + } } - } return expression } diff --git a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/ParameterDeclarationHandler.kt b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/ParameterDeclarationHandler.kt index bc85f17f31..926d615185 100644 --- a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/ParameterDeclarationHandler.kt +++ b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/ParameterDeclarationHandler.kt @@ -29,6 +29,7 @@ import de.fraunhofer.aisec.cpg.graph.declarations.Declaration import de.fraunhofer.aisec.cpg.graph.declarations.ParameterDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.ProblemDeclaration import de.fraunhofer.aisec.cpg.graph.newParameterDeclaration +import de.fraunhofer.aisec.cpg.graph.withChildren import java.util.function.Supplier import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier import org.eclipse.cdt.core.dom.ast.IASTParameterDeclaration @@ -67,25 +68,26 @@ class ParameterDeclarationHandler(lang: CXXLanguageFrontend) : frontend.typeOf(ctx.declarator, ctx.declSpecifier) } - val paramVariableDeclaration = newParameterDeclaration(name, type, false, rawNode = ctx) + val paramVariableDeclaration = + newParameterDeclaration(name, type, false, rawNode = ctx).withChildren { + // We cannot really model "const" as part of the type, but we can model it as part + // of + // the + // parameter, so we can use it later + if (ctx.declSpecifier.isConst) { + it.modifiers += CONST + } - // We cannot really model "const" as part of the type, but we can model it as part of the - // parameter, so we can use it later - if (ctx.declSpecifier.isConst) { - paramVariableDeclaration.modifiers += CONST - } - - // Add default values - if (ctx.declarator.initializer != null) { - paramVariableDeclaration.default = - frontend.initializerHandler.handle(ctx.declarator.initializer) - } + // Add default values + if (ctx.declarator.initializer != null) { + it.default = frontend.initializerHandler.handle(ctx.declarator.initializer) + } - // Add default values - if (ctx.declarator.initializer != null) { - paramVariableDeclaration.default = - frontend.initializerHandler.handle(ctx.declarator.initializer) - } + // Add default values + if (ctx.declarator.initializer != null) { + it.default = frontend.initializerHandler.handle(ctx.declarator.initializer) + } + } return paramVariableDeclaration } diff --git a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/StatementHandler.kt b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/StatementHandler.kt index 986f46bcd3..abe3235104 100644 --- a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/StatementHandler.kt +++ b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/StatementHandler.kt @@ -92,73 +92,72 @@ class StatementHandler(lang: CXXLanguageFrontend) : } private fun handleTryBlockStatement(tryBlockStatement: CPPASTTryBlockStatement): TryStatement { - val tryStatement = newTryStatement() - frontend.scopeManager.enterScope(tryStatement) - val statement = handle(tryBlockStatement.tryBody) as Block? - val catchClauses = - Arrays.stream(tryBlockStatement.catchHandlers) - .map { handleCatchHandler(it) } - .collect(Collectors.toList()) - tryStatement.tryBlock = statement - tryStatement.catchClauses = catchClauses - frontend.scopeManager.leaveScope(tryStatement) + val tryStatement = + newTryStatement().withChildren(true) { + val statement = handle(tryBlockStatement.tryBody) as Block? + val catchClauses = + Arrays.stream(tryBlockStatement.catchHandlers) + .map { handleCatchHandler(it) } + .collect(Collectors.toList()) + it.tryBlock = statement + it.catchClauses = catchClauses + } return tryStatement } private fun handleCatchHandler(catchHandler: ICPPASTCatchHandler): CatchClause { - val catchClause = newCatchClause(rawNode = catchHandler) - frontend.scopeManager.enterScope(catchClause) - - val body = frontend.statementHandler.handle(catchHandler.catchBody) - - // TODO: can also be an 'unnamed' parameter. In this case we should not declare a variable - var decl: Declaration? = null - if (catchHandler.declaration != null) { // can be null for "catch(...)" - decl = frontend.declarationHandler.handle(catchHandler.declaration) - } - - catchClause.body = body as? Block - - if (decl != null) { - catchClause.parameter = decl as? VariableDeclaration - } - frontend.scopeManager.leaveScope(catchClause) + val catchClause = + newCatchClause(rawNode = catchHandler).withChildren(true) { + val body = frontend.statementHandler.handle(catchHandler.catchBody) + + // TODO: can also be an 'unnamed' parameter. In this case we should not declare a + // variable + var decl: Declaration? = null + if (catchHandler.declaration != null) { // can be null for "catch(...)" + decl = frontend.declarationHandler.handle(catchHandler.declaration) + } + + it.body = body as? Block + + if (decl != null) { + it.parameter = decl as? VariableDeclaration + } + } return catchClause } private fun handleIfStatement(ctx: IASTIfStatement): IfStatement { - val statement = newIfStatement(rawNode = ctx) - - frontend.scopeManager.enterScope(statement) - - // We need some special treatment for C++ IfStatements - if (ctx is CPPASTIfStatement) { - if (ctx.initializerStatement != null) { - statement.initializerStatement = handle(ctx.initializerStatement) + val statement = + newIfStatement(rawNode = ctx).withChildren(true) { + // We need some special treatment for C++ IfStatements + if (ctx is CPPASTIfStatement) { + if (ctx.initializerStatement != null) { + it.initializerStatement = handle(ctx.initializerStatement) + } + if (ctx.conditionDeclaration != null) { + it.conditionDeclaration = + frontend.declarationHandler.handle(ctx.conditionDeclaration) + } + + it.isConstExpression = ctx.isConstexpr + } + + if (ctx.conditionExpression != null) + it.condition = frontend.expressionHandler.handle(ctx.conditionExpression) + it.thenStatement = handle(ctx.thenClause) + if (ctx.elseClause != null) { + it.elseStatement = handle(ctx.elseClause) + } } - if (ctx.conditionDeclaration != null) { - statement.conditionDeclaration = - frontend.declarationHandler.handle(ctx.conditionDeclaration) - } - - statement.isConstExpression = ctx.isConstexpr - } - - if (ctx.conditionExpression != null) - statement.condition = frontend.expressionHandler.handle(ctx.conditionExpression) - statement.thenStatement = handle(ctx.thenClause) - if (ctx.elseClause != null) { - statement.elseStatement = handle(ctx.elseClause) - } - - frontend.scopeManager.leaveScope(statement) return statement } private fun handleLabelStatement(ctx: IASTLabelStatement): LabelStatement { - val statement = newLabelStatement(rawNode = ctx) - statement.subStatement = handle(ctx.nestedStatement) + val statement = + newLabelStatement(rawNode = ctx).withChildren { + it.subStatement = handle(ctx.nestedStatement) + } statement.label = ctx.name.toString() return statement } @@ -189,84 +188,79 @@ class StatementHandler(lang: CXXLanguageFrontend) : } private fun handleWhileStatement(ctx: IASTWhileStatement): WhileStatement { - val statement = newWhileStatement(rawNode = ctx) - - frontend.scopeManager.enterScope(statement) - - // Special treatment for C++ while - if (ctx is CPPASTWhileStatement && ctx.conditionDeclaration != null) { - statement.conditionDeclaration = - frontend.declarationHandler.handle(ctx.conditionDeclaration) - } - - if (ctx.condition != null) { - statement.condition = frontend.expressionHandler.handle(ctx.condition) - } - - statement.statement = handle(ctx.body) - - frontend.scopeManager.leaveScope(statement) + val statement = + newWhileStatement(rawNode = ctx).withChildren(true) { + // Special treatment for C++ while + if (ctx is CPPASTWhileStatement && ctx.conditionDeclaration != null) { + it.conditionDeclaration = + frontend.declarationHandler.handle(ctx.conditionDeclaration) + } + + if (ctx.condition != null) { + it.condition = frontend.expressionHandler.handle(ctx.condition) + } + + it.statement = handle(ctx.body) + } return statement } private fun handleDoStatement(ctx: IASTDoStatement): DoStatement { - val statement = newDoStatement(rawNode = ctx) - frontend.scopeManager.enterScope(statement) - statement.condition = frontend.expressionHandler.handle(ctx.condition) - statement.statement = handle(ctx.body) - frontend.scopeManager.leaveScope(statement) + val statement = + newDoStatement(rawNode = ctx).withChildren(true) { + it.condition = frontend.expressionHandler.handle(ctx.condition) + it.statement = handle(ctx.body) + } return statement } private fun handleForStatement(ctx: IASTForStatement): ForStatement { - val statement = newForStatement(rawNode = ctx) - - frontend.scopeManager.enterScope(statement) - - statement.initializerStatement = handle(ctx.initializerStatement) - - // Special treatment for C++ while - if (ctx is CPPASTForStatement && ctx.conditionDeclaration != null) { - statement.conditionDeclaration = - frontend.declarationHandler.handle(ctx.conditionDeclaration) - } - - if (ctx.conditionExpression != null) { - statement.condition = frontend.expressionHandler.handle(ctx.conditionExpression) - } - - // Adds true expression node where default empty condition evaluates to true, remove here - // and in java StatementAnalyzer - if (statement.conditionDeclaration == null && statement.condition == null) { - val literal: Literal<*> = - newLiteral(true, primitiveType("bool")).implicit(code = "true") - statement.condition = literal - } - - if (ctx.iterationExpression != null) { - statement.iterationStatement = - frontend.expressionHandler.handle(ctx.iterationExpression) - } - - statement.statement = handle(ctx.body) - - frontend.scopeManager.leaveScope(statement) + val statement = + newForStatement(rawNode = ctx).withChildren(true) { + it.initializerStatement = handle(ctx.initializerStatement) + + // Special treatment for C++ while + if (ctx is CPPASTForStatement && ctx.conditionDeclaration != null) { + it.conditionDeclaration = + frontend.declarationHandler.handle(ctx.conditionDeclaration) + } + + if (ctx.conditionExpression != null) { + it.condition = frontend.expressionHandler.handle(ctx.conditionExpression) + } + + // Adds true expression node where default empty condition evaluates to true, remove + // here + // and in java StatementAnalyzer + if (it.conditionDeclaration == null && it.condition == null) { + val literal: Literal<*> = + newLiteral(true, primitiveType("bool")).implicit(code = "true") + it.condition = literal + } + + if (ctx.iterationExpression != null) { + it.iterationStatement = + frontend.expressionHandler.handle(ctx.iterationExpression) + } + + it.statement = handle(ctx.body) + } return statement } private fun handleForEachStatement(ctx: CPPASTRangeBasedForStatement): ForEachStatement { - val statement = newForEachStatement(rawNode = ctx) - frontend.scopeManager.enterScope(statement) - val decl = frontend.declarationHandler.handle(ctx.declaration) - val `var` = newDeclarationStatement() - `var`.singleDeclaration = decl - val iterable: Statement? = frontend.expressionHandler.handle(ctx.initializerClause) - statement.variable = `var` - statement.iterable = iterable - statement.statement = handle(ctx.body) - frontend.scopeManager.leaveScope(statement) + val statement = + newForEachStatement(rawNode = ctx).withChildren(true) { + val decl = frontend.declarationHandler.handle(ctx.declaration) + val `var` = newDeclarationStatement() + `var`.singleDeclaration = decl + val iterable: Statement? = frontend.expressionHandler.handle(ctx.initializerClause) + it.variable = `var` + it.iterable = iterable + it.statement = handle(ctx.body) + } return statement } @@ -294,75 +288,73 @@ class StatementHandler(lang: CXXLanguageFrontend) : // frontend for sub-block if available newDistinctLanguageBlock(rawNode = ctx) } else { - val declarationStatement = newDeclarationStatement(rawNode = ctx) - val declaration = frontend.declarationHandler.handle(ctx.declaration) - if (declaration is DeclarationSequence) { - declarationStatement.declarations = declaration.asList() - } else { - declarationStatement.singleDeclaration = declaration - } + val declarationStatement = + newDeclarationStatement(rawNode = ctx).withChildren { + val declaration = frontend.declarationHandler.handle(ctx.declaration) + if (declaration is DeclarationSequence) { + it.declarations = declaration.asList() + } else { + it.singleDeclaration = declaration + } + } declarationStatement } } private fun handleReturnStatement(ctx: IASTReturnStatement): ReturnStatement { - val returnStatement = newReturnStatement(rawNode = ctx) - - // Parse the return value - if (ctx.returnValue != null) { - returnStatement.returnValue = frontend.expressionHandler.handle(ctx.returnValue) - } - + val returnStatement = + newReturnStatement(rawNode = ctx).withChildren { + // Parse the return value + if (ctx.returnValue != null) { + it.returnValue = frontend.expressionHandler.handle(ctx.returnValue) + } + } return returnStatement } private fun handleCompoundStatement(ctx: IASTCompoundStatement): Block { - val block = newBlock(rawNode = ctx) - - frontend.scopeManager.enterScope(block) - - for (statement in ctx.statements) { - val handled = handle(statement) - if (handled != null) { - block.addStatement(handled) + val block = + newBlock(rawNode = ctx).withChildren(true) { + for (statement in ctx.statements) { + val handled = handle(statement) + if (handled != null) { + it.addStatement(handled) + } + } } - } - - frontend.scopeManager.leaveScope(block) return block } private fun handleSwitchStatement(ctx: IASTSwitchStatement): SwitchStatement { - val switchStatement = newSwitchStatement(rawNode = ctx) - - frontend.scopeManager.enterScope(switchStatement) - - // Special treatment for C++ switch - if (ctx is CPPASTSwitchStatement) { - if (ctx.initializerStatement != null) { - switchStatement.initializerStatement = handle(ctx.initializerStatement) + val switchStatement = + newSwitchStatement(rawNode = ctx).withChildren(true) { + // Special treatment for C++ switch + if (ctx is CPPASTSwitchStatement) { + if (ctx.initializerStatement != null) { + it.initializerStatement = handle(ctx.initializerStatement) + } + if (ctx.controllerDeclaration != null) { + it.selectorDeclaration = + frontend.declarationHandler.handle(ctx.controllerDeclaration) + } + } + + if (ctx.controllerExpression != null) { + it.selector = frontend.expressionHandler.handle(ctx.controllerExpression) + } + + it.statement = handle(ctx.body) } - if (ctx.controllerDeclaration != null) { - switchStatement.selectorDeclaration = - frontend.declarationHandler.handle(ctx.controllerDeclaration) - } - } - - if (ctx.controllerExpression != null) { - switchStatement.selector = frontend.expressionHandler.handle(ctx.controllerExpression) - } - - switchStatement.statement = handle(ctx.body) - - frontend.scopeManager.leaveScope(switchStatement) return switchStatement } private fun handleCaseStatement(ctx: IASTCaseStatement): CaseStatement { - val caseStatement = newCaseStatement(rawNode = ctx) - caseStatement.caseExpression = frontend.expressionHandler.handle(ctx.expression) + val caseStatement = + newCaseStatement(rawNode = ctx).withChildren { + it.caseExpression = frontend.expressionHandler.handle(ctx.expression) + } return caseStatement } 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 e8119feac9..8db787f9c7 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 @@ -67,39 +67,41 @@ open class DeclarationHandler(lang: JavaLanguageFrontend) : rawNode = constructorDeclaration ) frontend.scopeManager.addDeclaration(declaration) - frontend.scopeManager.enterScope(declaration) - createMethodReceiver(currentRecordDecl, declaration) - declaration.addThrowTypes( - constructorDeclaration.thrownExceptions.map { type: ReferenceType -> - frontend.typeOf(type) + declaration.withChildren(hasScope = true) { + frontend.scopeManager.enterScope(declaration) + createMethodReceiver(currentRecordDecl, declaration) + declaration.addThrowTypes( + constructorDeclaration.thrownExceptions.map { type: ReferenceType -> + frontend.typeOf(type) + } + ) + for (parameter in constructorDeclaration.parameters) { + val param = + this.newParameterDeclaration( + parameter.nameAsString, + frontend.getTypeAsGoodAsPossible(parameter, parameter.resolve()), + parameter.isVarArgs, + rawNode = parameter + ) + declaration.addParameter(param) + frontend.scopeManager.addDeclaration(param) + } + + val record = + frontend.scopeManager.firstScopeOrNull { it is RecordScope }?.astNode + as? RecordDeclaration + if (record != null) { + val type = record.toType() + declaration.type = type } - ) - for (parameter in constructorDeclaration.parameters) { - val param = - this.newParameterDeclaration( - parameter.nameAsString, - frontend.getTypeAsGoodAsPossible(parameter, parameter.resolve()), - parameter.isVarArgs, - rawNode = parameter - ) - declaration.addParameter(param) - frontend.scopeManager.addDeclaration(param) - } - val record = - frontend.scopeManager.firstScopeOrNull { it is RecordScope }?.astNode - as? RecordDeclaration - if (record != null) { - val type = record.toType() - declaration.type = type + // check, if constructor has body (i.e. it's not abstract or something) + val body = constructorDeclaration.body + addImplicitReturn(body) + declaration.body = frontend.statementHandler.handle(body) + frontend.processAnnotations(declaration, constructorDeclaration) } - // check, if constructor has body (i.e. it's not abstract or something) - val body = constructorDeclaration.body - addImplicitReturn(body) - declaration.body = frontend.statementHandler.handle(body) - frontend.processAnnotations(declaration, constructorDeclaration) - frontend.scopeManager.leaveScope(declaration) return declaration } @@ -115,47 +117,47 @@ open class DeclarationHandler(lang: JavaLanguageFrontend) : currentRecordDecl, rawNode = methodDecl ) - frontend.scopeManager.enterScope(functionDeclaration) - createMethodReceiver(currentRecordDecl, functionDeclaration) - functionDeclaration.addThrowTypes( - methodDecl.thrownExceptions.map { type: ReferenceType -> frontend.typeOf(type) } - ) - for (parameter in methodDecl.parameters) { - var resolvedType: Type? = - frontend.typeManager.getTypeParameter( - functionDeclaration.recordDeclaration, - parameter.type.toString() - ) - if (resolvedType == null) { - resolvedType = frontend.getTypeAsGoodAsPossible(parameter, parameter.resolve()) + functionDeclaration.withChildren(hasScope = true) { + createMethodReceiver(currentRecordDecl, functionDeclaration) + functionDeclaration.addThrowTypes( + methodDecl.thrownExceptions.map { type: ReferenceType -> frontend.typeOf(type) } + ) + for (parameter in methodDecl.parameters) { + var resolvedType: Type? = + frontend.typeManager.getTypeParameter( + functionDeclaration.recordDeclaration, + parameter.type.toString() + ) + if (resolvedType == null) { + resolvedType = frontend.getTypeAsGoodAsPossible(parameter, parameter.resolve()) + } + val param = + this.newParameterDeclaration( + parameter.nameAsString, + resolvedType, + parameter.isVarArgs, + rawNode = parameter + ) + functionDeclaration.addParameter(param) + frontend.processAnnotations(param, parameter) + frontend.scopeManager.addDeclaration(param) + } + val returnTypes = + listOf(frontend.getReturnTypeAsGoodAsPossible(methodDecl, resolvedMethod)) + functionDeclaration.returnTypes = returnTypes + val type = computeType(functionDeclaration) + functionDeclaration.type = type + + // check, if method has body (i.e., it's not abstract or something) + val o = methodDecl.body + if (!o.isEmpty) { + val body = o.get() + addImplicitReturn(body) + functionDeclaration.body = frontend.statementHandler.handle(body) + frontend.processAnnotations(functionDeclaration, methodDecl) } - val param = - this.newParameterDeclaration( - parameter.nameAsString, - resolvedType, - parameter.isVarArgs, - rawNode = parameter - ) - functionDeclaration.addParameter(param) - frontend.processAnnotations(param, parameter) - frontend.scopeManager.addDeclaration(param) - } - val returnTypes = listOf(frontend.getReturnTypeAsGoodAsPossible(methodDecl, resolvedMethod)) - functionDeclaration.returnTypes = returnTypes - val type = computeType(functionDeclaration) - functionDeclaration.type = type - - // check, if method has body (i.e., it's not abstract or something) - val o = methodDecl.body - if (o.isEmpty) { - frontend.scopeManager.leaveScope(functionDeclaration) - return functionDeclaration } - val body = o.get() - addImplicitReturn(body) - functionDeclaration.body = frontend.statementHandler.handle(body) - frontend.processAnnotations(functionDeclaration, methodDecl) - frontend.scopeManager.leaveScope(functionDeclaration) + return functionDeclaration } @@ -201,9 +203,9 @@ open class DeclarationHandler(lang: JavaLanguageFrontend) : processImportDeclarations(recordDeclaration) - frontend.scopeManager.enterScope(recordDeclaration) - processRecordMembers(classInterDecl, recordDeclaration) - frontend.scopeManager.leaveScope(recordDeclaration) + recordDeclaration.withChildren(hasScope = true) { + processRecordMembers(classInterDecl, recordDeclaration) + } if (frontend.scopeManager.currentScope is RecordScope) { // We need special handling if this is a so called "inner class". In this case we need @@ -228,6 +230,7 @@ open class DeclarationHandler(lang: JavaLanguageFrontend) : val field = this.newFieldDeclaration("this$" + scope.name?.localName, fieldType, listOf()) .implicit("this$" + scope.name?.localName) + field.astParent = recordDeclaration frontend.scopeManager.addDeclaration(field) frontend.scopeManager.leaveScope(recordDeclaration) } @@ -241,11 +244,7 @@ open class DeclarationHandler(lang: JavaLanguageFrontend) : val modifiers = fieldDecl.modifiers.map { modifier -> modifier.keyword.asString() } for (variable in fieldDecl.variables) { - val initializer = - variable.initializer - .map { ctx: Expression -> frontend.expressionHandler.handle(ctx) } - .orElse(null) - as? de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression + var type: Type try { // Resolve type first with ParameterizedType @@ -284,12 +283,18 @@ open class DeclarationHandler(lang: JavaLanguageFrontend) : } val fieldDeclaration = this.newFieldDeclaration( - variable.name.asString(), - type, - modifiers, - initializer, - rawNode = fieldDecl - ) + variable.name.asString(), + type, + modifiers, + rawNode = fieldDecl + ) + .withChildren { + it.initializer = + variable.initializer + .map { ctx: Expression -> frontend.expressionHandler.handle(ctx) } + .orElse(null) + as? de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression + } frontend.scopeManager.addDeclaration(fieldDeclaration) frontend.processAnnotations(fieldDeclaration, fieldDecl) declarationSequence.addDeclaration(fieldDeclaration) @@ -308,15 +313,13 @@ open class DeclarationHandler(lang: JavaLanguageFrontend) : processImportDeclarations(enumDeclaration) - frontend.scopeManager.enterScope(enumDeclaration) - - processRecordMembers(enumDecl, enumDeclaration) + enumDeclaration.withChildren(true) { + processRecordMembers(enumDecl, enumDeclaration) - val entries = enumDecl.entries.mapNotNull { handle(it) as EnumConstantDeclaration? } - entries.forEach { it.type = this.objectType(enumDeclaration.name) } - enumDeclaration.entries = entries - - frontend.scopeManager.leaveScope(enumDeclaration) + val entries = enumDecl.entries.mapNotNull { handle(it) as EnumConstantDeclaration? } + entries.forEach { it.type = this.objectType(enumDeclaration.name) } + enumDeclaration.entries = entries + } if (frontend.scopeManager.currentScope is RecordScope) { // We need special handling if this is a so called "inner class". In this case we need @@ -424,12 +427,13 @@ open class DeclarationHandler(lang: JavaLanguageFrontend) : currentEnum?.constructors?.singleOrNull { it.matchesSignature(arguments.map { it.type }).isDirectMatch } - - val constructExpr = - newConstructExpression(matchingConstructor?.name ?: currentEnum?.name) - arguments.forEach { constructExpr.addArgument(it) } - matchingConstructor?.let { constructExpr.constructor = matchingConstructor } - result.initializer = constructExpr + result.withChildren { + val constructExpr = + newConstructExpression(matchingConstructor?.name ?: currentEnum?.name) + arguments.forEach { constructExpr.addArgument(it) } + matchingConstructor?.let { constructExpr.constructor = matchingConstructor } + it.initializer = constructExpr + } } return result } @@ -462,13 +466,15 @@ open class DeclarationHandler(lang: JavaLanguageFrontend) : } val oInitializer = variable.initializer if (oInitializer.isPresent) { - val initializer = - frontend.expressionHandler.handle(oInitializer.get()) - as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression? - if (initializer is NewArrayExpression) { - declaration.isArray = true + declaration.withChildren { + val initializer = + frontend.expressionHandler.handle(oInitializer.get()) + as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression? + if (initializer is NewArrayExpression) { + declaration.isArray = true + } + declaration.initializer = initializer } - declaration.initializer = initializer } return declaration 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 a78c169526..6c044b95bd 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 @@ -53,49 +53,58 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : private fun handleLambdaExpr(expr: Expression): Statement { val lambdaExpr = expr.asLambdaExpr() - val lambda = newLambdaExpression(rawNode = lambdaExpr) - val anonymousFunction = newFunctionDeclaration("", rawNode = lambdaExpr) - frontend.scopeManager.enterScope(anonymousFunction) - for (parameter in lambdaExpr.parameters) { - val resolvedType = frontend.getTypeAsGoodAsPossible(parameter.type) - val param = - newParameterDeclaration(parameter.nameAsString, resolvedType, parameter.isVarArgs) - anonymousFunction.addParameter(param) - frontend.processAnnotations(param, parameter) - frontend.scopeManager.addDeclaration(param) - } + val lambda = + newLambdaExpression(rawNode = lambdaExpr).withChildren { + val anonymousFunction = + newFunctionDeclaration("", rawNode = lambdaExpr).withChildren(true) { + for (parameter in lambdaExpr.parameters) { + val resolvedType = frontend.getTypeAsGoodAsPossible(parameter.type) + val param = + newParameterDeclaration( + parameter.nameAsString, + resolvedType, + parameter.isVarArgs + ) + it.addParameter(param) + frontend.processAnnotations(param, parameter) + frontend.scopeManager.addDeclaration(param) + } - // TODO: We cannot easily identify the signature of the lambda - // val type = lambdaExpr.calculateResolvedType() - val functionType = FunctionType.computeType(anonymousFunction) - anonymousFunction.type = functionType - anonymousFunction.body = frontend.statementHandler.handle(lambdaExpr.body) - frontend.scopeManager.leaveScope(anonymousFunction) + // TODO: We cannot easily identify the signature of the lambda + // val type = lambdaExpr.calculateResolvedType() + val functionType = FunctionType.computeType(it) + it.type = functionType + it.body = frontend.statementHandler.handle(lambdaExpr.body) + } - lambda.function = anonymousFunction + it.function = anonymousFunction + } return lambda } private fun handleCastExpr(expr: Expression): Statement { val castExpr = expr.asCastExpr() - val castExpression = newCastExpression(rawNode = expr) - val expression = - handle(castExpr.expression) - as? de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression - ?: newProblemExpression("could not parse expression") - castExpression.expression = expression - castExpression.setCastOperator(2) - val t = frontend.getTypeAsGoodAsPossible(castExpr.type) - castExpression.castType = t - if (castExpr.type.isPrimitiveType) { - // Set Type based on the Casting type as it will result in a conversion for primitive - // types - castExpression.type = frontend.typeOf(castExpr.type.resolve().asPrimitive()) - } else { - // Get Runtime type from cast expression for complex types; - // castExpression.expression.registerTypeListener(castExpression) - } + val castExpression = + newCastExpression(rawNode = expr).withChildren { + val expression = + handle(castExpr.expression) + as? de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression + ?: newProblemExpression("could not parse expression") + it.expression = expression + it.setCastOperator(2) + val t = frontend.getTypeAsGoodAsPossible(castExpr.type) + it.castType = t + if (castExpr.type.isPrimitiveType) { + // Set Type based on the Casting type as it will result in a conversion for + // primitive + // types + it.type = frontend.typeOf(castExpr.type.resolve().asPrimitive()) + } else { + // Get Runtime type from cast expression for complex types; + // castExpression.expression.registerTypeListener(castExpression) + } + } return castExpression } @@ -108,22 +117,26 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : */ private fun handleArrayCreationExpr(expr: Expression): Statement { val arrayCreationExpr = expr as ArrayCreationExpr - val creationExpression = newNewArrayExpression(rawNode = expr) - - // in Java, an array creation expression either specifies an initializer or dimensions - - // parse initializer, if present - arrayCreationExpr.initializer.ifPresent { - creationExpression.initializer = handle(it) as? InitializerListExpression - } + val creationExpression = + newNewArrayExpression(rawNode = expr).withChildren { creationExpression -> + // in Java, an array creation expression either specifies an initializer or + // dimensions + + // parse initializer, if present + arrayCreationExpr.initializer.ifPresent { + creationExpression.initializer = handle(it) as? InitializerListExpression + } - // dimensions are only present if you specify them explicitly, such as new int[1] - for (lvl in arrayCreationExpr.levels) { - lvl.dimension.ifPresent { - (handle(it) as? de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression?) - ?.let { creationExpression.addDimension(it) } + // dimensions are only present if you specify them explicitly, such as new int[1] + for (lvl in arrayCreationExpr.levels) { + lvl.dimension.ifPresent { + (handle(it) + as? + de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression?) + ?.let { creationExpression.addDimension(it) } + } + } } - } return creationExpression } @@ -140,30 +153,33 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : // ArrayInitializerExpressions are converted into InitializerListExpressions to reduce the // syntactic distance a CPP and JAVA CPG - val initList = newInitializerListExpression(arrayType, rawNode = expr) - val initializers = - arrayInitializerExpr.values - .map { handle(it) } - .map { - de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression::class - .java - .cast(it) - } - initList.initializers = initializers + val initList = + newInitializerListExpression(arrayType, rawNode = expr).withChildren { + val initializers = + arrayInitializerExpr.values + .map { handle(it) } + .map { + de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression::class + .java + .cast(it) + } + it.initializers = initializers + } return initList } private fun handleArrayAccessExpr(expr: Expression): SubscriptExpression { val arrayAccessExpr = expr as ArrayAccessExpr - val arraySubsExpression = newSubscriptExpression(rawNode = expr) - (handle(arrayAccessExpr.name) - as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression?) - ?.let { arraySubsExpression.arrayExpression = it } - - (handle(arrayAccessExpr.index) - as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression?) - ?.let { arraySubsExpression.subscriptExpression = it } - + val arraySubsExpression = + newSubscriptExpression(rawNode = expr).withChildren { arraySubsExpression -> + (handle(arrayAccessExpr.name) + as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression?) + ?.let { arraySubsExpression.arrayExpression = it } + + (handle(arrayAccessExpr.index) + as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression?) + ?.let { arraySubsExpression.subscriptExpression = it } + } return arraySubsExpression } @@ -191,51 +207,59 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : unknownType() } } - val condition = - handle(conditionalExpr.condition) - as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression? - ?: newProblemExpression("Could not parse condition") - val thenExpr = - handle(conditionalExpr.thenExpr) - as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression? - val elseExpr = - handle(conditionalExpr.elseExpr) - as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression? - return newConditionalExpression(condition, thenExpr, elseExpr, superType) + val conditionExpression = + newConditionalExpression(superType, expr).withChildren { + it.condition = + handle(conditionalExpr.condition) + as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression? + ?: newProblemExpression("Could not parse condition") + it.thenExpression = + handle(conditionalExpr.thenExpr) + as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression? + it.elseExpression = + handle(conditionalExpr.elseExpr) + as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression? + } + return conditionExpression } private fun handleAssignmentExpression(expr: Expression): AssignExpression { val assignExpr = expr.asAssignExpr() - // first, handle the target. this is the first argument of the operator call - val lhs = - handle(assignExpr.target) - as? de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression - ?: newProblemExpression("could not parse lhs") + val assign = + newAssignExpression(assignExpr.operator.asString(), rawNode = assignExpr).withChildren { + // first, handle the target. this is the first argument of the operator call + it.lhs = + listOf( + handle(assignExpr.target) + as? de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression + ?: newProblemExpression("could not parse lhs") + ) - // second, handle the value. this is the second argument of the operator call - val rhs = - handle(assignExpr.value) - as? de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression - ?: newProblemExpression("could not parse lhs") - return newAssignExpression( - assignExpr.operator.asString(), - listOf(lhs), - listOf(rhs), - rawNode = assignExpr - ) + // second, handle the value. this is the second argument of the operator call + it.rhs = + listOf( + handle(assignExpr.value) + as? de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression + ?: newProblemExpression("could not parse lhs") + ) + } + + return assign } // Not sure how to handle this exactly yet private fun handleVariableDeclarationExpr(expr: Expression): DeclarationStatement { val variableDeclarationExpr = expr.asVariableDeclarationExpr() - val declarationStatement = newDeclarationStatement(rawNode = expr) - for (variable in variableDeclarationExpr.variables) { - val declaration = frontend.declarationHandler.handleVariableDeclarator(variable) - declarationStatement.addToPropertyEdgeDeclaration(declaration) - frontend.processAnnotations(declaration, variableDeclarationExpr) - frontend.scopeManager.addDeclaration(declaration) - } + val declarationStatement = + newDeclarationStatement(rawNode = expr).withChildren { + for (variable in variableDeclarationExpr.variables) { + val declaration = frontend.declarationHandler.handleVariableDeclarator(variable) + it.addToPropertyEdgeDeclaration(declaration) + frontend.processAnnotations(declaration, variableDeclarationExpr) + frontend.scopeManager.addDeclaration(declaration) + } + } return declarationStatement } @@ -243,109 +267,10 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : expr: Expression ): de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression { val fieldAccessExpr = expr.asFieldAccessExpr() - var base: de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression - // first, resolve the scope. this adds the necessary nodes, such as IDENTIFIER for the - // scope. - // it also acts as the first argument of the operator call - val scope = fieldAccessExpr.scope - if (scope.isNameExpr) { - var isStaticAccess = false - var baseType: Type - try { - val resolve = fieldAccessExpr.resolve() - if (resolve.asField().isStatic) { - isStaticAccess = true - } - baseType = this.objectType(resolve.asField().declaringType().qualifiedName) - } catch (ex: RuntimeException) { - isStaticAccess = true - val typeString = frontend.recoverTypeFromUnsolvedException(ex) - if (typeString != null) { - baseType = this.objectType(typeString) - } else { - // try to get the name - val name: String - val tokenRange = scope.asNameExpr().tokenRange - name = - if (tokenRange.isPresent) { - tokenRange.get().toString() - } else { - scope.asNameExpr().nameAsString - } - val qualifiedNameFromImports = frontend.getQualifiedNameFromImports(name) - baseType = - if (qualifiedNameFromImports != null) { - this.objectType(qualifiedNameFromImports) - } else { - log.info("Unknown base type 1 for {}", fieldAccessExpr) - unknownType() - } - } - } catch (ex: NoClassDefFoundError) { - isStaticAccess = true - val typeString = frontend.recoverTypeFromUnsolvedException(ex) - if (typeString != null) { - baseType = this.objectType(typeString) - } else { - val name: String - val tokenRange = scope.asNameExpr().tokenRange - name = - if (tokenRange.isPresent) { - tokenRange.get().toString() - } else { - scope.asNameExpr().nameAsString - } - val qualifiedNameFromImports = frontend.getQualifiedNameFromImports(name) - baseType = - if (qualifiedNameFromImports != null) { - this.objectType(qualifiedNameFromImports) - } else { - log.info("Unknown base type 1 for {}", fieldAccessExpr) - unknownType() - } - } - } - base = newReference(scope.asNameExpr().nameAsString, baseType, rawNode = scope) - base.isStaticAccess = isStaticAccess - } else if (scope.isFieldAccessExpr) { - base = - handle(scope) as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression? - ?: newProblemExpression("Could not parse base") - var tester = base - while (tester is MemberExpression) { - // we need to check if any base is only a static access, otherwise, this is a member - // access - // to this base - tester = tester.base - } - if (tester is Reference && tester.isStaticAccess) { - // try to get the name - val name: String - val tokenRange = scope.asFieldAccessExpr().tokenRange - name = - if (tokenRange.isPresent) { - tokenRange.get().toString() - } else { - scope.asFieldAccessExpr().nameAsString - } - val qualifiedNameFromImports = frontend.getQualifiedNameFromImports(name) - val baseType = - if (qualifiedNameFromImports != null) { - this.objectType(qualifiedNameFromImports) - } else { - log.info("Unknown base type 2 for {}", fieldAccessExpr) - unknownType() - } - base = - newReference(scope.asFieldAccessExpr().nameAsString, baseType, rawNode = scope) - base.isStaticAccess = true - } - } else { - base = - handle(scope) as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression? - ?: newProblemExpression("Could not parse base") - } + + var memberExpression: MemberExpression? = null var fieldType: Type? + var isStaticAccess = false try { val symbol = fieldAccessExpr.resolve() fieldType = @@ -367,16 +292,7 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : log.info("Unknown field type for {}", fieldAccessExpr) unknownType() } - val memberExpression = - newMemberExpression( - fieldAccessExpr.name.identifier, - base, - fieldType, - ".", // there is only "." in java - rawNode = expr - ) - memberExpression.isStaticAccess = true - return memberExpression + isStaticAccess = true } catch (ex: NoClassDefFoundError) { val typeString = frontend.recoverTypeFromUnsolvedException(ex) fieldType = @@ -388,27 +304,140 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : log.info("Unknown field type for {}", fieldAccessExpr) unknownType() } - val memberExpression = - newMemberExpression( + isStaticAccess = true + } + memberExpression = + newMemberExpression( fieldAccessExpr.name.identifier, - base, - fieldType, - ".", + memberType = fieldType ?: unknownType(), + operatorCode = ".", rawNode = expr ) - memberExpression.isStaticAccess = true - return memberExpression - } - if (base.location == null) { - base.location = frontend.locationOf(fieldAccessExpr) - } - return newMemberExpression( - fieldAccessExpr.name.identifier, - base, - fieldType, - ".", - rawNode = expr - ) + .withChildren { memberExpression -> + var base: de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression + // first, resolve the scope. this adds the necessary nodes, such as IDENTIFIER + // for the + // scope. + // it also acts as the first argument of the operator call + val scope = fieldAccessExpr.scope + if (scope.isNameExpr) { + var isStaticAccess = false + var baseType: Type + try { + val resolve = fieldAccessExpr.resolve() + if (resolve.asField().isStatic) { + isStaticAccess = true + } + baseType = + this.objectType(resolve.asField().declaringType().qualifiedName) + } catch (ex: RuntimeException) { + isStaticAccess = true + val typeString = frontend.recoverTypeFromUnsolvedException(ex) + if (typeString != null) { + baseType = this.objectType(typeString) + } else { + // try to get the name + val name: String + val tokenRange = scope.asNameExpr().tokenRange + name = + if (tokenRange.isPresent) { + tokenRange.get().toString() + } else { + scope.asNameExpr().nameAsString + } + val qualifiedNameFromImports = + frontend.getQualifiedNameFromImports(name) + baseType = + if (qualifiedNameFromImports != null) { + this.objectType(qualifiedNameFromImports) + } else { + log.info("Unknown base type 1 for {}", fieldAccessExpr) + unknownType() + } + } + } catch (ex: NoClassDefFoundError) { + isStaticAccess = true + val typeString = frontend.recoverTypeFromUnsolvedException(ex) + if (typeString != null) { + baseType = this.objectType(typeString) + } else { + val name: String + val tokenRange = scope.asNameExpr().tokenRange + name = + if (tokenRange.isPresent) { + tokenRange.get().toString() + } else { + scope.asNameExpr().nameAsString + } + val qualifiedNameFromImports = + frontend.getQualifiedNameFromImports(name) + baseType = + if (qualifiedNameFromImports != null) { + this.objectType(qualifiedNameFromImports) + } else { + log.info("Unknown base type 1 for {}", fieldAccessExpr) + unknownType() + } + } + } + base = + newReference(scope.asNameExpr().nameAsString, baseType, rawNode = scope) + base.isStaticAccess = isStaticAccess + } else if (scope.isFieldAccessExpr) { + base = + handle(scope) + as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression? + ?: newProblemExpression("Could not parse base") + var tester = base + while (tester is MemberExpression) { + // we need to check if any base is only a static access, otherwise, this + // is a + // member + // access + // to this base + tester = tester.base + } + if (tester is Reference && tester.isStaticAccess) { + // try to get the name + val name: String + val tokenRange = scope.asFieldAccessExpr().tokenRange + name = + if (tokenRange.isPresent) { + tokenRange.get().toString() + } else { + scope.asFieldAccessExpr().nameAsString + } + val qualifiedNameFromImports = + frontend.getQualifiedNameFromImports(name) + val baseType = + if (qualifiedNameFromImports != null) { + this.objectType(qualifiedNameFromImports) + } else { + log.info("Unknown base type 2 for {}", fieldAccessExpr) + unknownType() + } + base = + newReference( + scope.asFieldAccessExpr().nameAsString, + baseType, + rawNode = scope + ) + base.isStaticAccess = true + } + } else { + base = + handle(scope) + as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression? + ?: newProblemExpression("Could not parse base") + } + if (base.location == null) { + base.location = frontend.locationOf(fieldAccessExpr) + } + memberExpression.base = base + } + memberExpression.isStaticAccess = isStaticAccess + + return memberExpression } private fun handleLiteralExpression(expr: Expression): Literal<*>? { @@ -634,40 +663,40 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : private fun handleUnaryExpression(expr: Expression): UnaryOperator { val unaryExpr = expr.asUnaryExpr() - - // handle the 'inner' expression, which is affected by the unary expression - val expression = - handle(unaryExpr.expression) - as? de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression - ?: newProblemExpression("could not parse input") val unaryOperator = newUnaryOperator( - unaryExpr.operator.asString(), - unaryExpr.isPostfix, - unaryExpr.isPrefix, - rawNode = unaryExpr - ) - unaryOperator.input = expression + unaryExpr.operator.asString(), + unaryExpr.isPostfix, + unaryExpr.isPrefix, + rawNode = unaryExpr + ) + .withChildren { // handle the 'inner' expression, which is affected by the unary + // expression + it.input = + handle(unaryExpr.expression) + as? de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression + ?: newProblemExpression("could not parse input") + } return unaryOperator } private fun handleBinaryExpression(expr: Expression): BinaryOperator { val binaryExpr = expr.asBinaryExpr() + val binaryOperator = + newBinaryOperator(binaryExpr.operator.asString(), rawNode = binaryExpr).withChildren { + // first, handle the target. this is the first argument of the operator call + it.lhs = + handle(binaryExpr.left) + as? de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression + ?: newProblemExpression("could not parse lhs") + + // second, handle the value. this is the second argument of the operator call + it.rhs = + handle(binaryExpr.right) + as? de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression + ?: newProblemExpression("could not parse rhs") + } - // first, handle the target. this is the first argument of the operator call - val lhs = - handle(binaryExpr.left) - as? de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression - ?: newProblemExpression("could not parse lhs") - - // second, handle the value. this is the second argument of the operator call - val rhs = - handle(binaryExpr.right) - as? de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression - ?: newProblemExpression("could not parse rhs") - val binaryOperator = newBinaryOperator(binaryExpr.operator.asString(), rawNode = binaryExpr) - binaryOperator.lhs = lhs - binaryOperator.rhs = rhs return binaryOperator } @@ -701,64 +730,79 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : if (frontend.getQualifiedNameFromImports(qualifiedName) != null) { isStatic = true } - val base: de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression - // the scope could either be a variable or also the class name (static call!) - // thus, only because the scope is present, this is not automatically a member call - if (o.isPresent) { - val scope = o.get() - base = - handle(scope) as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression? - ?: newProblemExpression("Could not parse base") - - // If the base directly refers to a record, then this is a static call - if (base is Reference && base.refersTo is RecordDeclaration) { - isStatic = true - } - } else { - // If the call does not have any base, there are two possibilities: - // a) The "this" could be omitted, making it a member call to the current class - // b) It could refer to some method that was statically imported using an asterisks - // import - // - // Luckily, the resolved method already hints whether this is a static call - if (isStatic) { - // In case this is a static call, we need some additional information from the - // resolved - // method - val baseType: Type - val baseName = - if (resolved != null) { - this.parseName(resolved.declaringType().qualifiedName) + + callExpression = + newMemberCallExpression(isStatic = isStatic, rawNode = expr).withChildren { + val base: de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression + // the scope could either be a variable or also the class name (static call!) + // thus, only because the scope is present, this is not automatically a member call + if (o.isPresent) { + val scope = o.get() + base = + handle(scope) + as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression? + ?: newProblemExpression("Could not parse base") + + // If the base directly refers to a record, then this is a static call + if (base is Reference && base.refersTo is RecordDeclaration) { + isStatic = true + } + } else { + // If the call does not have any base, there are two possibilities: + // a) The "this" could be omitted, making it a member call to the current class + // b) It could refer to some method that was statically imported using an + // asterisks + // import + // + // Luckily, the resolved method already hints whether this is a static call + if (isStatic) { + // In case this is a static call, we need some additional information from + // the + // resolved + // method + val baseType: Type + val baseName = + if (resolved != null) { + this.parseName(resolved.declaringType().qualifiedName) + } else { + this.parseName(qualifiedName).parent + } + baseType = this.objectType(baseName ?: Type.UNKNOWN_TYPE_STRING) + base = newReference(baseName, baseType) } else { - this.parseName(qualifiedName).parent + // Since it is possible to omit the "this" keyword, some methods in java do + // not + // have + // a base. + // However, they are still scoped to the local class, meaning we should + // insert + // an + // implicit + // "this" reference, to make the life for our call resolver easier. + base = createImplicitThis() } - baseType = this.objectType(baseName ?: Type.UNKNOWN_TYPE_STRING) - base = newReference(baseName, baseType) - } else { - // Since it is possible to omit the "this" keyword, some methods in java do not have - // a base. - // However, they are still scoped to the local class, meaning we should insert an - // implicit - // "this" reference, to make the life for our call resolver easier. - base = createImplicitThis() + } + it.callee = + newMemberExpression( + name, + base, + unknownType(), + ".", + rawNode = methodCallExpr.name + ) + val arguments = methodCallExpr.arguments + + // handle the arguments + for (i in arguments.indices) { + val argument = + handle(arguments[i]) + as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression? + argument?.argumentIndex = i + it.addArgument(argument ?: newProblemExpression("Could not parse the argument")) + } } - } - val member = - newMemberExpression(name, base, unknownType(), ".", rawNode = methodCallExpr.name) - callExpression = newMemberCallExpression(member, isStatic, rawNode = expr) callExpression.type = typeString?.let { this.objectType(it) } ?: unknownType() - val arguments = methodCallExpr.arguments - - // handle the arguments - for (i in arguments.indices) { - val argument = - handle(arguments[i]) - as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression? - argument?.argumentIndex = i - callExpression.addArgument( - argument ?: newProblemExpression("Could not parse the argument") - ) - } + return callExpression } @@ -788,66 +832,78 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : val t = frontend.getTypeAsGoodAsPossible(objectCreationExpr.type) val constructorName = t.name.localName + val arguments = objectCreationExpr.arguments // To be consistent with other languages, we need to create a NewExpression (for the "new X" // part) as well as a ConstructExpression (for the constructor call) - val newExpression = newNewExpression(t, rawNode = expr) - val arguments = objectCreationExpr.arguments - - val ctor = newConstructExpression(rawNode = expr) - ctor.type = t - - // handle the arguments - for (i in arguments.indices) { - val argument = - handle(arguments[i]) - as? de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression ?: continue - argument.argumentIndex = i - ctor.addArgument(argument) - } - newExpression.initializer = ctor - - if (objectCreationExpr.anonymousClassBody.isPresent) { - // We have an anonymous class and will create a RecordDeclaration for it and add all the - // implemented methods. - val locationHash = frontend.locationOf(objectCreationExpr)?.hashCode() - - // We use the hash of the location to distinguish multiple instances of the anonymous - // class' superclass - val anonymousClassName = "$constructorName$locationHash" - val anonymousRecord = newRecordDeclaration(anonymousClassName, "class") - anonymousRecord.isImplicit = true - - frontend.scopeManager.enterScope(anonymousRecord) - - anonymousRecord.addSuperClass(objectType(constructorName)) - val anonymousClassBody = objectCreationExpr.anonymousClassBody.get() - for (classBody in anonymousClassBody) { - // Whatever is implemented in the anonymous class has to be added to the record - // declaration - val classBodyDecl = frontend.declarationHandler.handle(classBody) - classBodyDecl?.let { anonymousRecord.addDeclaration(it) } - } - - if (anonymousRecord.constructors.isEmpty()) { - val constructorDeclaration = - newConstructorDeclaration( - anonymousRecord.name.localName, - anonymousRecord, - ) - .implicit(anonymousRecord.name.localName) - - ctor.arguments.forEachIndexed { i, arg -> - constructorDeclaration.addParameter( - newParameterDeclaration("arg${i}", arg.type) - ) - } - anonymousRecord.addConstructor(constructorDeclaration) - ctor.anonymousClass = anonymousRecord + val newExpression = + newNewExpression(t, rawNode = expr).withChildren { newExpression -> + newExpression.initializer = + newConstructExpression(rawNode = expr).withChildren { ctor -> + ctor.type = t + + // handle the arguments + for (i in arguments.indices) { + val argument = + handle(arguments[i]) + as? + de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression + ?: continue + argument.argumentIndex = i + ctor.addArgument(argument) + } - frontend.scopeManager.addDeclaration(constructorDeclaration) - frontend.scopeManager.leaveScope(anonymousRecord) + if (objectCreationExpr.anonymousClassBody.isPresent) { + // We have an anonymous class and will create a RecordDeclaration for it + // and add + // all the + // implemented methods. + val locationHash = frontend.locationOf(objectCreationExpr)?.hashCode() + + // We use the hash of the location to distinguish multiple instances of + // the + // anonymous + // class' superclass + val anonymousClassName = "$constructorName$locationHash" + val anonymousRecord = + newRecordDeclaration(anonymousClassName, "class").withChildren( + true + ) { anonymousRecord -> + anonymousRecord.addSuperClass(objectType(constructorName)) + val anonymousClassBody = + objectCreationExpr.anonymousClassBody.get() + for (classBody in anonymousClassBody) { + // Whatever is implemented in the anonymous class has to be + // added to the + // record + // declaration + val classBodyDecl = + frontend.declarationHandler.handle(classBody) + classBodyDecl?.let { anonymousRecord.addDeclaration(it) } + } + + if (anonymousRecord.constructors.isEmpty()) { + val constructorDeclaration = + newConstructorDeclaration( + anonymousRecord.name.localName, + anonymousRecord, + ) + .implicit(anonymousRecord.name.localName) + + ctor.arguments.forEachIndexed { i, arg -> + constructorDeclaration.addParameter( + newParameterDeclaration("arg${i}", arg.type) + ) + } + anonymousRecord.addConstructor(constructorDeclaration) + ctor.anonymousClass = anonymousRecord + + frontend.scopeManager.addDeclaration(constructorDeclaration) + } + } + anonymousRecord.isImplicit = true + } + } } - } return newExpression } 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 ad108ff396..a765c29261 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 @@ -119,30 +119,32 @@ open class JavaLanguageFrontend(language: Language, ctx: T val fileDeclaration = newTranslationUnitDeclaration(file.toString(), rawNode = context) currentTU = fileDeclaration scopeManager.resetToGlobal(fileDeclaration) - val packDecl = context?.packageDeclaration?.orElse(null) - var namespaceDeclaration: NamespaceDeclaration? = null - if (packDecl != null) { - namespaceDeclaration = - newNamespaceDeclaration(packDecl.name.asString(), rawNode = packDecl) - scopeManager.addDeclaration(namespaceDeclaration) - scopeManager.enterScope(namespaceDeclaration) - } - - for (type in context?.types ?: listOf()) { - // handle each type. all declaration in this type will be added by the scope manager - // along - // the way - val declaration = declarationHandler.handle(type) - scopeManager.addDeclaration(declaration) - } + var currentNodeWithChildren: de.fraunhofer.aisec.cpg.graph.Node = fileDeclaration + fileDeclaration.withChildren { + val packDecl = context?.packageDeclaration?.orElse(null) + var namespaceDeclaration: NamespaceDeclaration? = null + if (packDecl != null) { + namespaceDeclaration = + newNamespaceDeclaration(packDecl.name.asString(), rawNode = packDecl) + scopeManager.addDeclaration(namespaceDeclaration) + currentNodeWithChildren = namespaceDeclaration + } - for (anImport in context?.imports ?: listOf()) { - val incl = newIncludeDeclaration(anImport.nameAsString) - scopeManager.addDeclaration(incl) - } + currentNodeWithChildren.withChildren(namespaceDeclaration != null) { + for (type in context?.types ?: listOf()) { + // handle each type. all declaration in this type will be added by the scope + // manager + // along + // the way + val declaration = declarationHandler.handle(type) + scopeManager.addDeclaration(declaration) + } - if (namespaceDeclaration != null) { - scopeManager.leaveScope(namespaceDeclaration) + for (anImport in context?.imports ?: listOf()) { + val incl = newIncludeDeclaration(anImport.nameAsString) + scopeManager.addDeclaration(incl) + } + } } bench.addMeasurement() fileDeclaration @@ -447,34 +449,36 @@ open class JavaLanguageFrontend(language: Language, ctx: T private fun handleAnnotations(owner: NodeWithAnnotations<*>): List { val list = ArrayList() for (expr in owner.annotations) { - val annotation = newAnnotation(expr.nameAsString, rawNode = expr) - val members = ArrayList() - - // annotations can be specified as member / value pairs - if (expr.isNormalAnnotationExpr) { - for (pair in expr.asNormalAnnotationExpr().pairs) { - val member = - newAnnotationMember( - pair.nameAsString, - expressionHandler.handle(pair.value) as Expression, - rawNode = pair.value - ) - members.add(member) - } - } else if (expr.isSingleMemberAnnotationExpr) { - val value = expr.asSingleMemberAnnotationExpr().memberValue - if (value != null) { - // or as a literal. in this case it is assigned to the annotation member 'value' - val member = - newAnnotationMember( - ANNOTATION_MEMBER_VALUE, - expressionHandler.handle(value) as Expression, - rawNode = value - ) - members.add(member) + val annotation = + newAnnotation(expr.nameAsString, rawNode = expr).withChildren { + val members = ArrayList() + + // annotations can be specified as member / value pairs + if (expr.isNormalAnnotationExpr) { + for (pair in expr.asNormalAnnotationExpr().pairs) { + val member = + newAnnotationMember(pair.nameAsString, rawNode = pair.value) + member.withChildren { + it.value = expressionHandler.handle(pair.value) as Expression + } + members.add(member) + } + } else if (expr.isSingleMemberAnnotationExpr) { + val value = expr.asSingleMemberAnnotationExpr().memberValue + if (value != null) { + // or as a literal. in this case it is assigned to the annotation member + // 'value' + val member = + newAnnotationMember(ANNOTATION_MEMBER_VALUE, rawNode = value) + .withChildren { + it.value = expressionHandler.handle(value) as Expression + } + members.add(member) + } + } + it.members = members } - } - annotation.members = members + list.add(annotation) } return list diff --git a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/StatementHandler.kt b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/StatementHandler.kt index 4e75aef65a..7229e32acb 100644 --- a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/StatementHandler.kt +++ b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/StatementHandler.kt @@ -81,9 +81,11 @@ class StatementHandler(lang: JavaLanguageFrontend?) : val throwStmt = stmt as ThrowStmt val throwOperation = this.newUnaryOperator("throw", postfix = false, prefix = true, rawNode = stmt) - throwOperation.input = - frontend.expressionHandler.handle(throwStmt.expression) - as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression + .withChildren { + it.input = + frontend.expressionHandler.handle(throwStmt.expression) + as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression + } return throwOperation } @@ -91,22 +93,24 @@ class StatementHandler(lang: JavaLanguageFrontend?) : val returnStmt = stmt.asReturnStmt() val optionalExpression = returnStmt.expression var expression: de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression? = null - if (optionalExpression.isPresent) { - val expr = optionalExpression.get() - // handle the expression as the first argument - expression = - frontend.expressionHandler.handle(expr) - as? de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression - } - val returnStatement = this.newReturnStatement(rawNode = stmt) + val returnStatement = + this.newReturnStatement(rawNode = stmt).withChildren { returnStatement -> + if (optionalExpression.isPresent) { + val expr = optionalExpression.get() + + // handle the expression as the first argument + expression = + frontend.expressionHandler.handle(expr) + as? de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression + } + + // expressionRefersToDeclaration to arguments, if there are any + expression?.let { returnStatement.returnValue = it } + } // JavaParser seems to add implicit return statements, that are not part of the original // source code. We mark it as such returnStatement.isImplicit = !returnStmt.tokenRange.isPresent - - // expressionRefersToDeclaration to arguments, if there are any - expression?.let { returnStatement.returnValue = it } - return returnStatement } @@ -115,14 +119,14 @@ class StatementHandler(lang: JavaLanguageFrontend?) : val conditionExpression = ifStmt.condition val thenStatement = ifStmt.thenStmt val optionalElseStatement = ifStmt.elseStmt - val ifStatement = newIfStatement(rawNode = stmt) - frontend.scopeManager.enterScope(ifStatement) - ifStatement.thenStatement = handle(thenStatement) - ifStatement.condition = - frontend.expressionHandler.handle(conditionExpression) - as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression - optionalElseStatement.ifPresent { ifStatement.elseStatement = handle(it) } - frontend.scopeManager.leaveScope(ifStatement) + val ifStatement = + newIfStatement(rawNode = stmt).withChildren(true) { ifStatement -> + ifStatement.thenStatement = handle(thenStatement) + ifStatement.condition = + frontend.expressionHandler.handle(conditionExpression) + as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression + optionalElseStatement.ifPresent { ifStatement.elseStatement = handle(it) } + } return ifStatement } @@ -130,13 +134,15 @@ class StatementHandler(lang: JavaLanguageFrontend?) : val assertStmt = stmt.asAssertStmt() val conditionExpression = assertStmt.check val thenStatement = assertStmt.message - val assertStatement = newAssertStatement(rawNode = stmt) - assertStatement.condition = - frontend.expressionHandler.handle(conditionExpression) - as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression - thenStatement.ifPresent { - assertStatement.message = frontend.expressionHandler.handle(thenStatement.get()) - } + val assertStatement = + newAssertStatement(rawNode = stmt).withChildren { assertStatement -> + assertStatement.condition = + frontend.expressionHandler.handle(conditionExpression) + as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression + thenStatement.ifPresent { + assertStatement.message = frontend.expressionHandler.handle(thenStatement.get()) + } + } return assertStatement } @@ -144,90 +150,97 @@ class StatementHandler(lang: JavaLanguageFrontend?) : val whileStmt = stmt.asWhileStmt() val conditionExpression = whileStmt.condition val statement = whileStmt.body - val whileStatement = newWhileStatement(rawNode = stmt) - frontend.scopeManager.enterScope(whileStatement) - whileStatement.statement = handle(statement) - whileStatement.condition = - frontend.expressionHandler.handle(conditionExpression) - as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression - frontend.scopeManager.leaveScope(whileStatement) + val whileStatement = + newWhileStatement(rawNode = stmt).withChildren(true) { + it.statement = handle(statement) + it.condition = + frontend.expressionHandler.handle(conditionExpression) + as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression + } return whileStatement } private fun handleForEachStatement(stmt: Statement): ForEachStatement { - val statement = newForEachStatement(rawNode = stmt) - frontend.scopeManager.enterScope(statement) - val forEachStmt = stmt.asForEachStmt() - val variable = frontend.expressionHandler.handle(forEachStmt.variable) - val iterable = frontend.expressionHandler.handle(forEachStmt.iterable) - if (variable !is DeclarationStatement) { - log.error("Expected a DeclarationStatement but received: {}", variable?.name) - } else { - statement.variable = variable - } - statement.iterable = iterable - statement.statement = handle(forEachStmt.body) - frontend.scopeManager.leaveScope(statement) + val statement = + newForEachStatement(rawNode = stmt).withChildren(true) { + val forEachStmt = stmt.asForEachStmt() + val variable = frontend.expressionHandler.handle(forEachStmt.variable) + val iterable = frontend.expressionHandler.handle(forEachStmt.iterable) + if (variable !is DeclarationStatement) { + log.error("Expected a DeclarationStatement but received: {}", variable?.name) + } else { + it.variable = variable + } + it.iterable = iterable + it.statement = handle(forEachStmt.body) + } return statement } private fun handleForStatement(stmt: Statement): ForStatement { val forStmt = stmt.asForStmt() - val statement = this.newForStatement(rawNode = stmt) - frontend.scopeManager.enterScope(statement) - if (forStmt.initialization.size > 1) { - // code will be set later - val initExprList = this.newExpressionList() - for (initExpr in forStmt.initialization) { - val s = frontend.expressionHandler.handle(initExpr) - s?.let { initExprList.addExpression(it) } - - // can not update location - if (s?.location == null) { - continue + val statement = + this.newForStatement(rawNode = stmt).withChildren(true) { statement -> + if (forStmt.initialization.size > 1) { + // code will be set later + val initExprList = + this.newExpressionList().withChildren { initExprList -> + for (initExpr in forStmt.initialization) { + val s = frontend.expressionHandler.handle(initExpr) + s?.let { initExprList.addExpression(it) } + + // can not update location + if (s?.location == null) { + continue + } + } + } + + statement.initializerStatement = initExprList.codeAndLocationFromChildren(stmt) + } else if (forStmt.initialization.size == 1) { + statement.initializerStatement = + frontend.expressionHandler.handle(forStmt.initialization[0]) } - } - - statement.initializerStatement = initExprList.codeAndLocationFromChildren(stmt) - } else if (forStmt.initialization.size == 1) { - statement.initializerStatement = - frontend.expressionHandler.handle(forStmt.initialization[0]) - } - forStmt.compare.ifPresent { condition: Expression -> - statement.condition = - frontend.expressionHandler.handle(condition) - as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression - } - - // Adds true expression node where default empty condition evaluates to true, remove here - // and in cpp StatementHandler - if (statement.condition == null) { - val literal: Literal<*> = - this.newLiteral(true, this.primitiveType("boolean")).implicit("true") - statement.condition = literal - } - if (forStmt.update.size > 1) { - // code will be set later - val iterationExprList = this.newExpressionList() - for (updateExpr in forStmt.update) { - val s = frontend.expressionHandler.handle(updateExpr) - s?.let { - // make sure location is set - iterationExprList.addExpression(it) + forStmt.compare.ifPresent { condition: Expression -> + statement.condition = + frontend.expressionHandler.handle(condition) + as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression } - // can not update location - if (s?.location == null) { - continue + // Adds true expression node where default empty condition evaluates to true, remove + // here + // and in cpp StatementHandler + if (statement.condition == null) { + val literal: Literal<*> = + this.newLiteral(true, this.primitiveType("boolean")).implicit("true") + statement.condition = literal } + if (forStmt.update.size > 1) { + // code will be set later + val iterationExprList = + this.newExpressionList().withChildren { iterationExprList -> + for (updateExpr in forStmt.update) { + val s = frontend.expressionHandler.handle(updateExpr) + s?.let { + // make sure location is set + iterationExprList.addExpression(it) + } + + // can not update location + if (s?.location == null) { + continue + } + } + } + + statement.iterationStatement = + iterationExprList.codeAndLocationFromChildren(stmt) + } else if (forStmt.update.size == 1) { + statement.iterationStatement = + frontend.expressionHandler.handle(forStmt.update[0]) + } + statement.statement = handle(forStmt.body) } - - statement.iterationStatement = iterationExprList.codeAndLocationFromChildren(stmt) - } else if (forStmt.update.size == 1) { - statement.iterationStatement = frontend.expressionHandler.handle(forStmt.update[0]) - } - statement.statement = handle(forStmt.body) - frontend.scopeManager.leaveScope(statement) return statement } @@ -235,13 +248,13 @@ class StatementHandler(lang: JavaLanguageFrontend?) : val doStmt = stmt.asDoStmt() val conditionExpression = doStmt.condition val statement = doStmt.body - val doStatement = newDoStatement(rawNode = stmt) - frontend.scopeManager.enterScope(doStatement) - doStatement.statement = handle(statement) - doStatement.condition = - frontend.expressionHandler.handle(conditionExpression) - as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression - frontend.scopeManager.leaveScope(doStatement) + val doStatement = + newDoStatement(rawNode = stmt).withChildren(true) { + it.statement = handle(statement) + it.condition = + frontend.expressionHandler.handle(conditionExpression) + as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression + } return doStatement } @@ -251,11 +264,13 @@ class StatementHandler(lang: JavaLanguageFrontend?) : private fun handleSynchronizedStatement(stmt: Statement): SynchronizedStatement { val synchronizedJava = stmt.asSynchronizedStmt() - val synchronizedCPG = newSynchronizedStatement(rawNode = stmt) - synchronizedCPG.expression = - frontend.expressionHandler.handle(synchronizedJava.expression) - as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression - synchronizedCPG.block = handle(synchronizedJava.body) as Block? + val synchronizedCPG = + newSynchronizedStatement(rawNode = stmt).withChildren { + it.expression = + frontend.expressionHandler.handle(synchronizedJava.expression) + as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression + it.block = handle(synchronizedJava.body) as Block? + } return synchronizedCPG } @@ -263,9 +278,11 @@ class StatementHandler(lang: JavaLanguageFrontend?) : val labelStmt = stmt.asLabeledStmt() val label = labelStmt.label.identifier val statement = labelStmt.statement - val labelStatement = newLabelStatement(rawNode = stmt) - labelStatement.subStatement = handle(statement) - labelStatement.label = label + val labelStatement = + newLabelStatement(rawNode = stmt).withChildren { + it.subStatement = handle(statement) + it.label = label + } return labelStatement } @@ -287,14 +304,14 @@ class StatementHandler(lang: JavaLanguageFrontend?) : val blockStmt = stmt.asBlockStmt() // first of, all we need a compound statement - val compoundStatement = newBlock(rawNode = stmt) - frontend.scopeManager.enterScope(compoundStatement) - for (child in blockStmt.statements) { - val statement = handle(child) - statement?.let { compoundStatement.addStatement(it) } - } - frontend.scopeManager.leaveScope(compoundStatement) - return compoundStatement + val block = + newBlock(rawNode = stmt).withChildren(true) { block -> + for (child in blockStmt.statements) { + val statement = handle(child) + statement?.let { block.addStatement(it) } + } + } + return block } fun handleCaseDefaultStatement( @@ -336,10 +353,12 @@ class StatementHandler(lang: JavaLanguageFrontend?) : getNextTokenWith(":", caseExprTokenRange.get().end) ) } - val caseStatement = this.newCaseStatement() - caseStatement.caseExpression = - frontend.expressionHandler.handle(caseExpression) - as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression + val caseStatement = + this.newCaseStatement().withChildren { + it.caseExpression = + frontend.expressionHandler.handle(caseExpression) + as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression + } caseStatement.location = getLocationsFromTokens(parentLocation, caseTokens.a, caseTokens.b) return caseStatement } @@ -410,40 +429,43 @@ class StatementHandler(lang: JavaLanguageFrontend?) : fun handleSwitchStatement(stmt: Statement): SwitchStatement { val switchStmt = stmt.asSwitchStmt() - val switchStatement = newSwitchStatement(rawNode = stmt) - - frontend.scopeManager.enterScope(switchStatement) - switchStatement.selector = - frontend.expressionHandler.handle(switchStmt.selector) - as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression - - // Compute region and code for self generated compound statement to match the c++ versions - var start: JavaToken? = null - var end: JavaToken? = null - val tokenRange = switchStmt.tokenRange - val tokenRangeSelector = switchStmt.selector.tokenRange - if (tokenRange.isPresent && tokenRangeSelector.isPresent) { - start = getNextTokenWith("{", tokenRangeSelector.get().end) - end = getPreviousTokenWith("}", tokenRange.get().end) - } - val compoundStatement = this.newBlock() - compoundStatement.code = getCodeBetweenTokens(start, end) - compoundStatement.location = getLocationsFromTokens(switchStatement.location, start, end) - for (sentry in switchStmt.entries) { - if (sentry.labels.isEmpty()) { - compoundStatement.addStatement(handleCaseDefaultStatement(null, sentry)) - } - for (caseExp in sentry.labels) { - compoundStatement.addStatement(handleCaseDefaultStatement(caseExp, sentry)) - } - for (subStmt in sentry.statements) { - compoundStatement.addStatement( - handle(subStmt) ?: ProblemExpression("Could not parse statement") - ) + val switchStatement = + newSwitchStatement(rawNode = stmt).withChildren(true) { + it.selector = + frontend.expressionHandler.handle(switchStmt.selector) + as de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression + + // Compute region and code for self generated compound statement to match the c++ + // versions + var start: JavaToken? = null + var end: JavaToken? = null + val tokenRange = switchStmt.tokenRange + val tokenRangeSelector = switchStmt.selector.tokenRange + if (tokenRange.isPresent && tokenRangeSelector.isPresent) { + start = getNextTokenWith("{", tokenRangeSelector.get().end) + end = getPreviousTokenWith("}", tokenRange.get().end) + } + val compoundStatement = + this.newBlock().withChildren { + for (sentry in switchStmt.entries) { + if (sentry.labels.isEmpty()) { + it.addStatement(handleCaseDefaultStatement(null, sentry)) + } + for (caseExp in sentry.labels) { + it.addStatement(handleCaseDefaultStatement(caseExp, sentry)) + } + for (subStmt in sentry.statements) { + it.addStatement( + handle(subStmt) + ?: ProblemExpression("Could not parse statement") + ) + } + } + } + compoundStatement.code = getCodeBetweenTokens(start, end) + compoundStatement.location = getLocationsFromTokens(it.location, start, end) + it.statement = compoundStatement } - } - switchStatement.statement = compoundStatement - frontend.scopeManager.leaveScope(switchStatement) return switchStatement } @@ -460,87 +482,92 @@ class StatementHandler(lang: JavaLanguageFrontend?) : } val name = containingClass - val node = this.newConstructExpression(name, rawNode = null) - node.type = unknownType() + val constructExpr = + this.newConstructExpression(name, rawNode = null).withChildren { constructExpr -> + constructExpr.type = unknownType() + + // Create a reference either to "this" + if (explicitConstructorInvocationStmt.isThis) { + frontend.scopeManager.currentRecord?.toType()?.let { constructExpr.type = it } + constructExpr.callee = this.newReference(name) + } else { + // or to our direct (first) super type + frontend.scopeManager.currentRecord?.superTypes?.firstOrNull()?.let { + constructExpr.type = it + constructExpr.callee = this.newReference(it.name) + } + } - // Create a reference either to "this" - if (explicitConstructorInvocationStmt.isThis) { - frontend.scopeManager.currentRecord?.toType()?.let { node.type = it } - node.callee = this.newReference(name) - } else { - // or to our direct (first) super type - frontend.scopeManager.currentRecord?.superTypes?.firstOrNull()?.let { - node.type = it - node.callee = this.newReference(it.name) + val arguments = + explicitConstructorInvocationStmt.arguments + .map(frontend.expressionHandler::handle) + .filterIsInstance< + de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression + >() + constructExpr.arguments = arguments } - } - - val arguments = - explicitConstructorInvocationStmt.arguments - .map(frontend.expressionHandler::handle) - .filterIsInstance() - node.arguments = arguments - - return node + return constructExpr } private fun handleTryStatement(stmt: Statement): TryStatement { val tryStmt = stmt.asTryStmt() - val tryStatement = newTryStatement(rawNode = stmt) - frontend.scopeManager.enterScope(tryStatement) - val resources = - tryStmt.resources.mapNotNull { ctx -> frontend.expressionHandler.handle(ctx) } - val tryBlock = handleBlockStatement(tryStmt.tryBlock) - val catchClauses = tryStmt.catchClauses.map(::handleCatchClause) - val finallyBlock = tryStmt.finallyBlock.map(::handleBlockStatement).orElse(null) - frontend.scopeManager.leaveScope(tryStatement) - tryStatement.resources = resources - tryStatement.tryBlock = tryBlock - tryStatement.finallyBlock = finallyBlock - tryStatement.catchClauses = catchClauses - for (r in resources) { - if (r is DeclarationStatement) { - for (d in r.declarations) { - if (d is VariableDeclaration) { - frontend.scopeManager.addDeclaration(d) + val tryStatement = + newTryStatement(rawNode = stmt).withChildren(true) { + val resources = + tryStmt.resources.mapNotNull { ctx -> frontend.expressionHandler.handle(ctx) } + val tryBlock = handleBlockStatement(tryStmt.tryBlock) + val catchClauses = tryStmt.catchClauses.map(::handleCatchClause) + val finallyBlock = tryStmt.finallyBlock.map(::handleBlockStatement).orElse(null) + it.resources = resources + it.tryBlock = tryBlock + it.finallyBlock = finallyBlock + it.catchClauses = catchClauses + for (r in resources) { + if (r is DeclarationStatement) { + for (d in r.declarations) { + if (d is VariableDeclaration) { + frontend.scopeManager.addDeclaration(d) + } + } } } } - } + return tryStatement } private fun handleCatchClause( catchCls: CatchClause ): de.fraunhofer.aisec.cpg.graph.statements.CatchClause { - val cClause = newCatchClause(rawNode = catchCls) - frontend.scopeManager.enterScope(cClause) - val possibleTypes = mutableSetOf() - val concreteType: Type - if (catchCls.parameter.type is UnionType) { - for (t in (catchCls.parameter.type as UnionType).elements) { - possibleTypes.add(frontend.getTypeAsGoodAsPossible(t)) + val cClause = + newCatchClause(rawNode = catchCls).withChildren(true) { + val possibleTypes = mutableSetOf() + val concreteType: Type + if (catchCls.parameter.type is UnionType) { + for (t in (catchCls.parameter.type as UnionType).elements) { + possibleTypes.add(frontend.getTypeAsGoodAsPossible(t)) + } + // we do not know which of the exceptions was actually thrown, so we assume this + // might + // be any + concreteType = this.objectType("java.lang.Throwable") + concreteType.typeOrigin = Type.Origin.GUESSED + } else { + concreteType = frontend.getTypeAsGoodAsPossible(catchCls.parameter.type) + possibleTypes.add(concreteType) + } + val parameter = + this.newVariableDeclaration( + catchCls.parameter.name.toString(), + concreteType, + rawNode = catchCls.parameter + ) + parameter.addAssignedTypes(possibleTypes) + val body = handleBlockStatement(catchCls.body) + it.body = body + it.parameter = parameter + frontend.scopeManager.addDeclaration(parameter) } - // we do not know which of the exceptions was actually thrown, so we assume this might - // be any - concreteType = this.objectType("java.lang.Throwable") - concreteType.typeOrigin = Type.Origin.GUESSED - } else { - concreteType = frontend.getTypeAsGoodAsPossible(catchCls.parameter.type) - possibleTypes.add(concreteType) - } - val parameter = - this.newVariableDeclaration( - catchCls.parameter.name.toString(), - concreteType, - rawNode = catchCls.parameter - ) - parameter.addAssignedTypes(possibleTypes) - val body = handleBlockStatement(catchCls.body) - cClause.body = body - cClause.parameter = parameter - frontend.scopeManager.addDeclaration(parameter) - frontend.scopeManager.leaveScope(cClause) return cClause }