From ee38bd0f776a60ef7bf42bc5fb39d78b6754290d Mon Sep 17 00:00:00 2001 From: Christian Banse Date: Fri, 9 Jun 2023 21:18:43 +0200 Subject: [PATCH] Removed Type Parser --- .../kotlin/cpg.common-conventions.gradle.kts | 2 +- .../cpg.formatting-conventions.gradle.kts | 35 +- .../aisec/cpg/analysis/ValueEvaluatorTest.kt | 182 ++--- .../aisec/cpg/graph/TypeManager.java | 754 ------------------ .../aisec/cpg/graph/types/TypeParser.java | 712 ----------------- .../aisec/cpg/TranslationContext.kt | 2 - .../aisec/cpg/TranslationManager.kt | 5 +- .../de/fraunhofer/aisec/cpg/TypeManager.kt | 650 +++++++++++++++ .../aisec/cpg/frontends/Language.kt | 16 +- .../aisec/cpg/frontends/LanguageFrontend.kt | 5 +- .../aisec/cpg/graph/DeclarationBuilder.kt | 6 +- .../aisec/cpg/graph/ExpressionBuilder.kt | 25 +- .../fraunhofer/aisec/cpg/graph/NodeBuilder.kt | 99 ++- .../aisec/cpg/graph/builder/Fluent.kt | 24 +- .../declarations/ConstructorDeclaration.kt | 4 +- .../graph/declarations/FunctionDeclaration.kt | 4 +- .../graph/declarations/RecordDeclaration.kt | 7 +- .../graph/declarations/TypedefDeclaration.kt | 5 +- .../graph/declarations/ValueDeclaration.kt | 15 +- .../statements/expressions/BinaryOperator.kt | 2 +- .../statements/expressions/CallExpression.kt | 2 +- .../statements/expressions/CastExpression.kt | 2 +- .../expressions/ConditionalExpression.kt | 2 +- .../expressions/ConstructExpression.kt | 7 +- .../statements/expressions/Expression.kt | 17 +- .../expressions/InitializerListExpression.kt | 2 +- .../aisec/cpg/graph/types/FunctionType.kt | 6 +- .../aisec/cpg/graph/types/HasType.kt | 18 +- .../aisec/cpg/graph/types/ProblemType.kt | 40 + .../aisec/cpg/graph/types/ReferenceType.kt | 4 +- .../fraunhofer/aisec/cpg/graph/types/Type.kt | 9 + .../aisec/cpg/graph/types/UnknownType.kt | 13 +- .../aisec/cpg/graph/types/WrapState.kt | 4 +- .../aisec/cpg/passes/CXXCallResolverHelper.kt | 7 +- .../aisec/cpg/passes/CallResolver.kt | 10 +- .../de/fraunhofer/aisec/cpg/passes/Pass.kt | 5 +- .../aisec/cpg/passes/SymbolResolverPass.kt | 4 +- .../aisec/cpg/passes/VariableUsageResolver.kt | 16 +- .../aisec/cpg/passes/inference/Inference.kt | 2 +- .../de/fraunhofer/aisec/cpg/GraphExamples.kt | 4 +- .../de/fraunhofer/aisec/cpg/graph/TypeTest.kt | 58 ++ .../expressions/AssignExpressionTest.kt | 4 +- .../cpg/passes/scopes/ScopeManagerTest.kt | 5 +- .../aisec/cpg/frontends/TestLanguage.kt | 12 +- cpg-language-cxx/build.gradle.kts | 6 - .../aisec/cpg/frontends/cxx/CLanguage.kt | 59 +- .../aisec/cpg/frontends/cxx/CPPLanguage.kt | 52 +- .../cpg/frontends/cxx/CXXLanguageFrontend.kt | 234 +++++- .../cpg/frontends/cxx/DeclarationHandler.kt | 56 +- .../cpg/frontends/cxx/DeclaratorHandler.kt | 54 +- .../cpg/frontends/cxx/ExpressionHandler.kt | 74 +- .../cpg/frontends/cxx/StatementHandler.kt | 2 +- .../cpg/passes/FunctionPointerCallResolver.kt | 5 +- .../aisec/cpg/ScopeManagerCXXTest.kt | 1 - .../aisec/cpg/enhancements/types/TypeTests.kt | 243 +----- .../aisec/cpg/frontends/cxx/CXXIncludeTest.kt | 14 +- .../frontends/cxx/CXXLanguageFrontendTest.kt | 499 ++++++------ .../aisec/cpg/frontends/cxx/CXXLiteralTest.kt | 36 +- .../cxx/CXXSymbolConfigurationTest.kt | 2 +- .../aisec/cpg/passes/CallResolverTest.kt | 5 +- cpg-language-cxx/src/test/resources/c/types.c | 5 + .../src/main/golang/basic_types.go | 51 +- .../src/main/golang/declarations.go | 51 +- .../src/main/golang/expressions.go | 59 +- .../golang/frontend/declaration_builder.go | 51 +- .../golang/frontend/expression_builder.go | 51 +- .../src/main/golang/frontend/frontend.go | 51 +- .../src/main/golang/frontend/handler.go | 117 ++- .../main/golang/frontend/statement_builder.go | 51 +- .../src/main/golang/frontend/type_builder.go | 91 +++ cpg-language-go/src/main/golang/helper.go | 51 +- cpg-language-go/src/main/golang/language.go | 51 +- .../src/main/golang/lib/cpg/main.go | 51 +- cpg-language-go/src/main/golang/location.go | 51 +- cpg-language-go/src/main/golang/node.go | 51 +- cpg-language-go/src/main/golang/scope.go | 51 +- cpg-language-go/src/main/golang/statements.go | 51 +- cpg-language-go/src/main/golang/types.go | 103 +-- .../frontends/golang/GoLanguageFrontend.kt | 4 +- .../aisec/cpg/passes/GoExtraPass.kt | 4 +- .../golang/GoLanguageFrontendTest.kt | 37 +- cpg-language-go/src/test/resources/log4j2.xml | 2 +- .../cpg/frontends/java/DeclarationHandler.kt | 41 +- .../cpg/frontends/java/ExpressionHandler.kt | 90 ++- .../aisec/cpg/frontends/java/JavaLanguage.kt | 2 + .../frontends/java/JavaLanguageFrontend.kt | 64 +- .../cpg/frontends/java/StatementHandler.kt | 4 +- .../cpg/passes/JavaCallResolverHelper.kt | 4 +- .../JavaExternalTypeHierarchyResolver.kt | 2 +- .../java/JavaLanguageFrontendTest.kt | 18 +- .../aisec/cpg/graph/types/TypeTests.kt | 102 +-- .../aisec/cpg/passes/CallResolverTest.kt | 4 +- .../cpg/frontends/llvm/DeclarationHandler.kt | 6 +- .../cpg/frontends/llvm/ExpressionHandler.kt | 7 +- .../frontends/llvm/LLVMIRLanguageFrontend.kt | 23 +- .../cpg/frontends/llvm/StatementHandler.kt | 34 +- .../llvm/LLVMIRLanguageFrontendTest.kt | 17 +- .../python/PythonLanguageFrontend.kt | 4 +- .../src/main/python/CPGPython/_expressions.py | 29 +- .../src/main/python/CPGPython/_statements.py | 26 +- .../frontends/python/PythonFrontendTest.kt | 30 +- .../typescript/DeclarationHandler.kt | 13 +- .../frontends/typescript/ExpressionHandler.kt | 10 +- .../cpg/frontends/typescript/TypeHandler.kt | 23 +- .../typescript/TypeScriptLanguageFrontend.kt | 3 +- .../TypescriptLanguageFrontendTest.kt | 21 +- .../src/test/resources/log4j2.xml | 14 + .../src/test/resources/typescript/function.ts | 4 +- 108 files changed, 2585 insertions(+), 3244 deletions(-) delete mode 100644 cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/TypeManager.java delete mode 100644 cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/TypeParser.java create mode 100644 cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TypeManager.kt create mode 100644 cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/ProblemType.kt create mode 100644 cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/TypeTest.kt create mode 100644 cpg-language-cxx/src/test/resources/c/types.c create mode 100644 cpg-language-go/src/main/golang/frontend/type_builder.go create mode 100644 cpg-language-typescript/src/test/resources/log4j2.xml diff --git a/buildSrc/src/main/kotlin/cpg.common-conventions.gradle.kts b/buildSrc/src/main/kotlin/cpg.common-conventions.gradle.kts index 1983d0fc7d..161cd9f3bb 100644 --- a/buildSrc/src/main/kotlin/cpg.common-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/cpg.common-conventions.gradle.kts @@ -120,7 +120,7 @@ kotlin { tasks.withType { kotlinOptions { - freeCompilerArgs = listOf("-opt-in=kotlin.RequiresOptIn") + freeCompilerArgs = listOf("-opt-in=kotlin.RequiresOptIn", "-Xcontext-receivers") } } diff --git a/buildSrc/src/main/kotlin/cpg.formatting-conventions.gradle.kts b/buildSrc/src/main/kotlin/cpg.formatting-conventions.gradle.kts index 813189c6c8..ab526156a5 100644 --- a/buildSrc/src/main/kotlin/cpg.formatting-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/cpg.formatting-conventions.gradle.kts @@ -62,6 +62,34 @@ val headerWithStars = """/* */ """ +val headerWithSlashes = """// +// Copyright (c) ${"$"}YEAR, Fraunhofer AISEC. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ${'$'}${'$'}${'$'}${'$'}${'$'}${'$'}\ ${'$'}${'$'}${'$'}${'$'}${'$'}${'$'}${'$'}\ ${'$'}${'$'}${'$'}${'$'}${'$'}${'$'}\ +// ${'$'}${'$'} __${'$'}${'$'}\ ${'$'}${'$'} __${'$'}${'$'}\ ${'$'}${'$'} __${'$'}${'$'}\ +// ${'$'}${'$'} / \__|${'$'}${'$'} | ${'$'}${'$'} |${'$'}${'$'} / \__| +// ${'$'}${'$'} | ${'$'}${'$'}${'$'}${'$'}${'$'}${'$'}${'$'} |${'$'}${'$'} |${'$'}${'$'}${'$'}${'$'}\ +// ${'$'}${'$'} | ${'$'}${'$'} ____/ ${'$'}${'$'} |\_${'$'}${'$'} | +// ${'$'}${'$'} | ${'$'}${'$'}\ ${'$'}${'$'} | ${'$'}${'$'} | ${'$'}${'$'} | +// \${'$'}${'$'}${'$'}${'$'}${'$'} |${'$'}${'$'} | \${'$'}${'$'}${'$'}${'$'}${'$'} | +// \______/ \__| \______/ +// +// + +""" + val headerWithHashes = """# # Copyright (c) ${"$"}YEAR, Fraunhofer AISEC. All rights reserved. # @@ -101,11 +129,16 @@ spotless { } ) target("src/main/**/*.py") + targetExclude( + fileTree(project.projectDir) { + include("src/main/nodejs/node_modules") + } + ) licenseHeader(headerWithHashes, "from").yearSeparator(" - ") } format("golang") { target("src/main/golang/**/*.go") - licenseHeader(headerWithStars, "package").yearSeparator(" - ") + licenseHeader(headerWithSlashes, "package").yearSeparator(" - ") } } diff --git a/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/analysis/ValueEvaluatorTest.kt b/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/analysis/ValueEvaluatorTest.kt index eea8ccebcd..ded94caf96 100644 --- a/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/analysis/ValueEvaluatorTest.kt +++ b/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/analysis/ValueEvaluatorTest.kt @@ -184,59 +184,59 @@ class ValueEvaluatorTest { fun testHandlePlus() { with(TestHandler(TestLanguageFrontend())) { val binOp = newBinaryOperator("+") - binOp.lhs = newLiteral(3, parseType("int")) - binOp.rhs = newLiteral(2, parseType("int")) + binOp.lhs = newLiteral(3, primitiveType("int")) + binOp.rhs = newLiteral(2, primitiveType("int")) assertEquals(5L, ValueEvaluator().evaluate(binOp)) - binOp.rhs = newLiteral(2.4, parseType("double")) + binOp.rhs = newLiteral(2.4, primitiveType("double")) assertEquals(5.4, ValueEvaluator().evaluate(binOp)) - binOp.lhs = newLiteral(3L, parseType("long")) - binOp.rhs = newLiteral(2, parseType("int")) + binOp.lhs = newLiteral(3L, primitiveType("long")) + binOp.rhs = newLiteral(2, primitiveType("int")) assertEquals(5L, ValueEvaluator().evaluate(binOp)) - binOp.rhs = newLiteral(2.4, parseType("double")) + binOp.rhs = newLiteral(2.4, primitiveType("double")) assertEquals(5.4, ValueEvaluator().evaluate(binOp)) - binOp.lhs = newLiteral((3).toShort(), parseType("short")) - binOp.rhs = newLiteral(2, parseType("int")) + binOp.lhs = newLiteral((3).toShort(), primitiveType("short")) + binOp.rhs = newLiteral(2, primitiveType("int")) assertEquals(5L, ValueEvaluator().evaluate(binOp)) - binOp.rhs = newLiteral(2.4, parseType("double")) + binOp.rhs = newLiteral(2.4, primitiveType("double")) assertEquals(5.4, ValueEvaluator().evaluate(binOp)) - binOp.lhs = newLiteral((3).toByte(), parseType("byte")) - binOp.rhs = newLiteral(2, parseType("int")) + binOp.lhs = newLiteral((3).toByte(), primitiveType("byte")) + binOp.rhs = newLiteral(2, primitiveType("int")) assertEquals(5L, ValueEvaluator().evaluate(binOp)) - binOp.rhs = newLiteral(2.4, parseType("double")) + binOp.rhs = newLiteral(2.4, primitiveType("double")) assertEquals(5.4, ValueEvaluator().evaluate(binOp)) - binOp.lhs = newLiteral(3.0, parseType("double")) - binOp.rhs = newLiteral(2, parseType("int")) + binOp.lhs = newLiteral(3.0, primitiveType("double")) + binOp.rhs = newLiteral(2, primitiveType("int")) assertEquals(5.0, ValueEvaluator().evaluate(binOp)) - binOp.rhs = newLiteral(2.4, parseType("double")) + binOp.rhs = newLiteral(2.4, primitiveType("double")) assertEquals(5.4, ValueEvaluator().evaluate(binOp)) - binOp.lhs = newLiteral(3.0f, parseType("float")) - binOp.rhs = newLiteral(2, parseType("int")) + binOp.lhs = newLiteral(3.0f, primitiveType("float")) + binOp.rhs = newLiteral(2, primitiveType("int")) assertEquals(5.0, ValueEvaluator().evaluate(binOp)) - binOp.rhs = newLiteral(2.4, parseType("double")) + binOp.rhs = newLiteral(2.4, primitiveType("double")) assertEquals(5.4, ValueEvaluator().evaluate(binOp)) - binOp.lhs = newLiteral("Hello", parseType("String")) - binOp.rhs = newLiteral(" world", parseType("String")) + binOp.lhs = newLiteral("Hello", primitiveType("String")) + binOp.rhs = newLiteral(" world", primitiveType("String")) assertEquals("Hello world", ValueEvaluator().evaluate(binOp)) - binOp.rhs = newLiteral(2, parseType("int")) + binOp.rhs = newLiteral(2, primitiveType("int")) assertEquals("Hello2", ValueEvaluator().evaluate(binOp)) } } @@ -245,56 +245,56 @@ class ValueEvaluatorTest { fun testHandleMinus() { with(TestHandler(TestLanguageFrontend())) { val binOp = newBinaryOperator("-") - binOp.lhs = newLiteral(3, parseType("int")) - binOp.rhs = newLiteral(2, parseType("int")) + binOp.lhs = newLiteral(3, primitiveType("int")) + binOp.rhs = newLiteral(2, primitiveType("int")) assertEquals(1L, ValueEvaluator().evaluate(binOp)) - binOp.rhs = newLiteral(2.4, parseType("double")) + binOp.rhs = newLiteral(2.4, primitiveType("double")) assertEquals(3 - 2.4, ValueEvaluator().evaluate(binOp)) - binOp.lhs = newLiteral(3L, parseType("long")) - binOp.rhs = newLiteral(2, parseType("int")) + binOp.lhs = newLiteral(3L, primitiveType("long")) + binOp.rhs = newLiteral(2, primitiveType("int")) assertEquals(1L, ValueEvaluator().evaluate(binOp)) - binOp.rhs = newLiteral(2.4, parseType("double")) + binOp.rhs = newLiteral(2.4, primitiveType("double")) assertEquals(3 - 2.4, ValueEvaluator().evaluate(binOp)) - binOp.lhs = newLiteral((3).toShort(), parseType("short")) - binOp.rhs = newLiteral(2, parseType("int")) + binOp.lhs = newLiteral((3).toShort(), primitiveType("short")) + binOp.rhs = newLiteral(2, primitiveType("int")) assertEquals(1L, ValueEvaluator().evaluate(binOp)) - binOp.rhs = newLiteral(2.4, parseType("double")) + binOp.rhs = newLiteral(2.4, primitiveType("double")) assertEquals(3 - 2.4, ValueEvaluator().evaluate(binOp)) - binOp.lhs = newLiteral((3).toByte(), parseType("byte")) - binOp.rhs = newLiteral(2, parseType("int")) + binOp.lhs = newLiteral((3).toByte(), primitiveType("byte")) + binOp.rhs = newLiteral(2, primitiveType("int")) assertEquals(1L, ValueEvaluator().evaluate(binOp)) - binOp.rhs = newLiteral(2.4, parseType("double")) + binOp.rhs = newLiteral(2.4, primitiveType("double")) assertEquals(3 - 2.4, ValueEvaluator().evaluate(binOp)) - binOp.lhs = newLiteral(3.0, parseType("double")) - binOp.rhs = newLiteral(2, parseType("int")) + binOp.lhs = newLiteral(3.0, primitiveType("double")) + binOp.rhs = newLiteral(2, primitiveType("int")) assertEquals(1.0, ValueEvaluator().evaluate(binOp)) - binOp.rhs = newLiteral(2.4, parseType("double")) + binOp.rhs = newLiteral(2.4, primitiveType("double")) assertEquals(3 - 2.4, ValueEvaluator().evaluate(binOp)) - binOp.lhs = newLiteral(3.0f, parseType("float")) - binOp.rhs = newLiteral(2, parseType("int")) + binOp.lhs = newLiteral(3.0f, primitiveType("float")) + binOp.rhs = newLiteral(2, primitiveType("int")) assertEquals(1.0, ValueEvaluator().evaluate(binOp)) - binOp.rhs = newLiteral(2.4, parseType("double")) + binOp.rhs = newLiteral(2.4, primitiveType("double")) assertEquals(3 - 2.4, ValueEvaluator().evaluate(binOp)) - binOp.lhs = newLiteral("Hello", parseType("String")) - binOp.rhs = newLiteral(" world", parseType("String")) + binOp.lhs = newLiteral("Hello", primitiveType("String")) + binOp.rhs = newLiteral(" world", primitiveType("String")) assertEquals("{-}", ValueEvaluator().evaluate(binOp)) } } @@ -303,56 +303,56 @@ class ValueEvaluatorTest { fun testHandleTimes() { with(TestHandler(TestLanguageFrontend())) { val binOp = newBinaryOperator("*") - binOp.lhs = newLiteral(3, parseType("int")) - binOp.rhs = newLiteral(2, parseType("int")) + binOp.lhs = newLiteral(3, primitiveType("int")) + binOp.rhs = newLiteral(2, primitiveType("int")) assertEquals(6L, ValueEvaluator().evaluate(binOp)) - binOp.rhs = newLiteral(2.4, parseType("double")) + binOp.rhs = newLiteral(2.4, primitiveType("double")) assertEquals(3 * 2.4, ValueEvaluator().evaluate(binOp)) - binOp.lhs = newLiteral(3L, parseType("long")) - binOp.rhs = newLiteral(2, parseType("int")) + binOp.lhs = newLiteral(3L, primitiveType("long")) + binOp.rhs = newLiteral(2, primitiveType("int")) assertEquals(6L, ValueEvaluator().evaluate(binOp)) - binOp.rhs = newLiteral(2.4, parseType("double")) + binOp.rhs = newLiteral(2.4, primitiveType("double")) assertEquals(3 * 2.4, ValueEvaluator().evaluate(binOp)) - binOp.lhs = newLiteral((3).toShort(), parseType("short")) - binOp.rhs = newLiteral(2, parseType("int")) + binOp.lhs = newLiteral((3).toShort(), primitiveType("short")) + binOp.rhs = newLiteral(2, primitiveType("int")) assertEquals(6L, ValueEvaluator().evaluate(binOp)) - binOp.rhs = newLiteral(2.4, parseType("double")) + binOp.rhs = newLiteral(2.4, primitiveType("double")) assertEquals(3 * 2.4, ValueEvaluator().evaluate(binOp)) - binOp.lhs = newLiteral((3).toByte(), parseType("byte")) - binOp.rhs = newLiteral(2, parseType("int")) + binOp.lhs = newLiteral((3).toByte(), primitiveType("byte")) + binOp.rhs = newLiteral(2, primitiveType("int")) assertEquals(6L, ValueEvaluator().evaluate(binOp)) - binOp.rhs = newLiteral(2.4, parseType("double")) + binOp.rhs = newLiteral(2.4, primitiveType("double")) assertEquals(3 * 2.4, ValueEvaluator().evaluate(binOp)) - binOp.lhs = newLiteral(3.0, parseType("double")) - binOp.rhs = newLiteral(2, parseType("int")) + binOp.lhs = newLiteral(3.0, primitiveType("double")) + binOp.rhs = newLiteral(2, primitiveType("int")) assertEquals(6.0, ValueEvaluator().evaluate(binOp)) - binOp.rhs = newLiteral(2.4, parseType("double")) + binOp.rhs = newLiteral(2.4, primitiveType("double")) assertEquals(3 * 2.4, ValueEvaluator().evaluate(binOp)) - binOp.lhs = newLiteral(3.0f, parseType("float")) - binOp.rhs = newLiteral(2, parseType("int")) + binOp.lhs = newLiteral(3.0f, primitiveType("float")) + binOp.rhs = newLiteral(2, primitiveType("int")) assertEquals(6.0, ValueEvaluator().evaluate(binOp)) - binOp.rhs = newLiteral(2.4, parseType("double")) + binOp.rhs = newLiteral(2.4, primitiveType("double")) assertEquals(3 * 2.4, ValueEvaluator().evaluate(binOp)) - binOp.lhs = newLiteral("Hello", parseType("String")) - binOp.rhs = newLiteral(" world", parseType("String")) + binOp.lhs = newLiteral("Hello", primitiveType("String")) + binOp.rhs = newLiteral(" world", primitiveType("String")) assertEquals("{*}", ValueEvaluator().evaluate(binOp)) } } @@ -362,60 +362,60 @@ class ValueEvaluatorTest { with(TestHandler(TestLanguageFrontend())) { // For two integer values, we keep the result as a long. val binOp = newBinaryOperator("/") - binOp.lhs = newLiteral(3, parseType("int")) - binOp.rhs = newLiteral(0, parseType("int")) + binOp.lhs = newLiteral(3, primitiveType("int")) + binOp.rhs = newLiteral(0, primitiveType("int")) assertEquals("{/}", ValueEvaluator().evaluate(binOp)) - binOp.lhs = newLiteral(3, parseType("int")) - binOp.rhs = newLiteral(2, parseType("int")) + binOp.lhs = newLiteral(3, primitiveType("int")) + binOp.rhs = newLiteral(2, primitiveType("int")) assertEquals(1L, ValueEvaluator().evaluate(binOp)) - binOp.rhs = newLiteral(2.4, parseType("double")) + binOp.rhs = newLiteral(2.4, primitiveType("double")) assertEquals(3 / 2.4, ValueEvaluator().evaluate(binOp)) - binOp.lhs = newLiteral(3L, parseType("long")) - binOp.rhs = newLiteral(2, parseType("int")) + binOp.lhs = newLiteral(3L, primitiveType("long")) + binOp.rhs = newLiteral(2, primitiveType("int")) assertEquals(1L, ValueEvaluator().evaluate(binOp)) - binOp.rhs = newLiteral(2.4, parseType("double")) + binOp.rhs = newLiteral(2.4, primitiveType("double")) assertEquals(3 / 2.4, ValueEvaluator().evaluate(binOp)) - binOp.lhs = newLiteral((3).toShort(), parseType("short")) - binOp.rhs = newLiteral(2, parseType("int")) + binOp.lhs = newLiteral((3).toShort(), primitiveType("short")) + binOp.rhs = newLiteral(2, primitiveType("int")) assertEquals(1L, ValueEvaluator().evaluate(binOp)) - binOp.rhs = newLiteral(2.4, parseType("double")) + binOp.rhs = newLiteral(2.4, primitiveType("double")) assertEquals(3 / 2.4, ValueEvaluator().evaluate(binOp)) - binOp.lhs = newLiteral((3).toByte(), parseType("byte")) - binOp.rhs = newLiteral(2, parseType("int")) + binOp.lhs = newLiteral((3).toByte(), primitiveType("byte")) + binOp.rhs = newLiteral(2, primitiveType("int")) assertEquals(1L, ValueEvaluator().evaluate(binOp)) - binOp.rhs = newLiteral(2.4, parseType("double")) + binOp.rhs = newLiteral(2.4, primitiveType("double")) assertEquals(3 / 2.4, ValueEvaluator().evaluate(binOp)) - binOp.lhs = newLiteral(3.0, parseType("double")) - binOp.rhs = newLiteral(2, parseType("int")) + binOp.lhs = newLiteral(3.0, primitiveType("double")) + binOp.rhs = newLiteral(2, primitiveType("int")) assertEquals(1.5, ValueEvaluator().evaluate(binOp)) - binOp.rhs = newLiteral(2.4, parseType("double")) + binOp.rhs = newLiteral(2.4, primitiveType("double")) assertEquals(3 / 2.4, ValueEvaluator().evaluate(binOp)) - binOp.lhs = newLiteral(3.0f, parseType("float")) - binOp.rhs = newLiteral(2, parseType("int")) + binOp.lhs = newLiteral(3.0f, primitiveType("float")) + binOp.rhs = newLiteral(2, primitiveType("int")) assertEquals(1.5, ValueEvaluator().evaluate(binOp)) - binOp.rhs = newLiteral(2.4, parseType("double")) + binOp.rhs = newLiteral(2.4, primitiveType("double")) assertEquals(3 / 2.4, ValueEvaluator().evaluate(binOp)) - binOp.lhs = newLiteral("Hello", parseType("String")) - binOp.rhs = newLiteral(" world", parseType("String")) + binOp.lhs = newLiteral("Hello", primitiveType("String")) + binOp.rhs = newLiteral(" world", primitiveType("String")) assertEquals("{/}", ValueEvaluator().evaluate(binOp)) } } @@ -424,30 +424,30 @@ class ValueEvaluatorTest { fun testHandleUnary() { with(TestHandler(TestLanguageFrontend())) { val neg = newUnaryOperator("-", false, true) - neg.input = newLiteral(3, parseType("int")) + neg.input = newLiteral(3, primitiveType("int")) assertEquals(-3, ValueEvaluator().evaluate(neg)) - neg.input = newLiteral(3.5, parseType("double")) + neg.input = newLiteral(3.5, primitiveType("double")) assertEquals(-3.5, ValueEvaluator().evaluate(neg)) val plusplus = newUnaryOperator("++", true, false) - plusplus.input = newLiteral(3, parseType("int")) + plusplus.input = newLiteral(3, primitiveType("int")) assertEquals(4, ValueEvaluator().evaluate(plusplus)) - plusplus.input = newLiteral(3.5, parseType("double")) + plusplus.input = newLiteral(3.5, primitiveType("double")) assertEquals(4.5, ValueEvaluator().evaluate(plusplus)) - plusplus.input = newLiteral(3.5f, parseType("float")) + plusplus.input = newLiteral(3.5f, primitiveType("float")) assertEquals(4.5f, ValueEvaluator().evaluate(plusplus)) val minusminus = newUnaryOperator("--", true, false) - minusminus.input = newLiteral(3, parseType("int")) + minusminus.input = newLiteral(3, primitiveType("int")) assertEquals(2, ValueEvaluator().evaluate(minusminus)) - minusminus.input = newLiteral(3.5, parseType("double")) + minusminus.input = newLiteral(3.5, primitiveType("double")) assertEquals(2.5, ValueEvaluator().evaluate(minusminus)) - minusminus.input = newLiteral(3.5f, parseType("float")) + minusminus.input = newLiteral(3.5f, primitiveType("float")) assertEquals(2.5f, ValueEvaluator().evaluate(minusminus)) } } diff --git a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/TypeManager.java b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/TypeManager.java deleted file mode 100644 index f5a92b99eb..0000000000 --- a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/TypeManager.java +++ /dev/null @@ -1,754 +0,0 @@ -/* - * Copyright (c) 2019, Fraunhofer AISEC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * $$$$$$\ $$$$$$$\ $$$$$$\ - * $$ __$$\ $$ __$$\ $$ __$$\ - * $$ / \__|$$ | $$ |$$ / \__| - * $$ | $$$$$$$ |$$ |$$$$\ - * $$ | $$ ____/ $$ |\_$$ | - * $$ | $$\ $$ | $$ | $$ | - * \$$$$$ |$$ | \$$$$$ | - * \______/ \__| \______/ - * - */ -package de.fraunhofer.aisec.cpg.graph; - -import static de.fraunhofer.aisec.cpg.graph.DeclarationBuilderKt.newTypedefDeclaration; - -import de.fraunhofer.aisec.cpg.ScopeManager; -import de.fraunhofer.aisec.cpg.TranslationContext; -import de.fraunhofer.aisec.cpg.frontends.Language; -import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend; -import de.fraunhofer.aisec.cpg.graph.declarations.*; -import de.fraunhofer.aisec.cpg.graph.scopes.NameScope; -import de.fraunhofer.aisec.cpg.graph.scopes.RecordScope; -import de.fraunhofer.aisec.cpg.graph.scopes.Scope; -import de.fraunhofer.aisec.cpg.graph.scopes.TemplateScope; -import de.fraunhofer.aisec.cpg.graph.types.*; -import de.fraunhofer.aisec.cpg.helpers.Util; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class TypeManager { - - private static final Logger log = LoggerFactory.getLogger(TypeManager.class); - - // TODO: document/remove this regexp, merge with other pattern - private static final Pattern funPointerPattern = - Pattern.compile("\\(?\\*(?[^()]+)\\)?\\(.*\\)"); - - private static boolean typeSystemActive = true; - - @NotNull - private final Map> typeCache = - Collections.synchronizedMap(new IdentityHashMap<>()); - - @NotNull - private final Map typeToRecord = - Collections.synchronizedMap(new HashMap<>()); - - /** - * Stores the relationship between parameterized RecordDeclarations (e.g. Classes using Generics) - * to the ParameterizedType to be able to resolve the Type of the fields, since ParameterizedTypes - * are unique to the RecordDeclaration and are not merged. - */ - @NotNull - private final Map> recordToTypeParameters = - Collections.synchronizedMap(new HashMap<>()); - - @NotNull - private final Map> templateToTypeParameters = - Collections.synchronizedMap(new HashMap<>()); - - @NotNull - private final Map> typeState = - Collections.synchronizedMap(new HashMap<>()); // Stores all the unique types ObjectType as - // Key and - // Reference-/PointerTypes - // as Values - - private final Set firstOrderTypes = Collections.synchronizedSet(new HashSet<>()); - private final Set secondOrderTypes = Collections.synchronizedSet(new HashSet<>()); - - /** - * @param recordDeclaration that is instantiated by a template containing parameterizedtypes - * @param name of the ParameterizedType we want to get - * @return ParameterizedType if there is a parameterized type defined in the recordDeclaration - * with matching name, null instead - */ - @Nullable - public ParameterizedType getTypeParameter(RecordDeclaration recordDeclaration, String name) { - if (this.recordToTypeParameters.containsKey(recordDeclaration)) { - for (ParameterizedType parameterizedType : - this.recordToTypeParameters.get(recordDeclaration)) { - if (parameterizedType.getName().toString().equals(name)) { - return parameterizedType; - } - } - } - - return null; - } - - /** - * Adds a List of ParameterizedType to {@link TypeManager#recordToTypeParameters} - * - * @param recordDeclaration will be stored as key for the map - * @param typeParameters List containing all ParameterizedTypes used by the recordDeclaration and - * will be stored as value in the map - */ - public void addTypeParameter( - RecordDeclaration recordDeclaration, List typeParameters) { - this.recordToTypeParameters.put(recordDeclaration, typeParameters); - } - - /** - * Searches {@link TypeManager#templateToTypeParameters} for ParameterizedTypes that were defined - * in a template matching the provided name - * - * @param templateDeclaration that includes the ParameterizedType we are looking for - * @param name name of the ParameterizedType we are looking for - * @return - */ - @Nullable - public ParameterizedType getTypeParameter(TemplateDeclaration templateDeclaration, String name) { - if (this.templateToTypeParameters.containsKey(templateDeclaration)) { - for (ParameterizedType parameterizedType : - this.templateToTypeParameters.get(templateDeclaration)) { - if (parameterizedType.getName().toString().equals(name)) { - return parameterizedType; - } - } - } - return null; - } - - /** - * @param templateDeclaration - * @return List containing all ParameterizedTypes the templateDeclaration defines. If the - * templateDeclaration is not registered, an empty list is returned. - */ - @NotNull - public List getAllParameterizedType(TemplateDeclaration templateDeclaration) { - if (this.templateToTypeParameters.containsKey(templateDeclaration)) { - return this.templateToTypeParameters.get(templateDeclaration); - } - return new ArrayList<>(); - } - - /** - * Searches for ParameterizedType if the scope is a TemplateScope. If not we search the parent - * scope until we reach the top. - * - * @param scope in which we are searching for the defined ParameterizedTypes - * @param name of the ParameterizedType - * @return ParameterizedType that is found within the scope (or any parent scope) and matches the - * provided name. Null if we reach the top of the scope without finding a matching - * ParameterizedType - */ - public ParameterizedType searchTemplateScopeForDefinedParameterizedTypes( - Scope scope, String name) { - if (scope instanceof TemplateScope) { - var node = scope.getAstNode(); - - // We need an additional check here, because of parsing or other errors, the AST node might - // not necessarily be a template declaration. - if (node instanceof TemplateDeclaration templateDeclaration) { - ParameterizedType parameterizedType = getTypeParameter(templateDeclaration, name); - if (parameterizedType != null) { - return parameterizedType; - } - } - } - - return scope.getParent() != null - ? searchTemplateScopeForDefinedParameterizedTypes(scope.getParent(), name) - : null; - } - - /** - * Adds ParameterizedType to the {@link TypeManager#templateToTypeParameters} to be able to - * resolve this type when it is used - * - * @param templateDeclaration key for {@link TypeManager#templateToTypeParameters} - * @param typeParameter ParameterizedType we want to register - */ - public void addTypeParameter( - TemplateDeclaration templateDeclaration, ParameterizedType typeParameter) { - if (this.templateToTypeParameters.containsKey(templateDeclaration)) { - this.templateToTypeParameters.get(templateDeclaration).add(typeParameter); - } else { - List typeParameters = new ArrayList<>(); - typeParameters.add(typeParameter); - this.templateToTypeParameters.put(templateDeclaration, typeParameters); - } - } - - /** - * Check if a ParameterizedType with name typeName is already registered. If so we return the - * already created ParameterizedType. If not, we create and return a new ParameterizedType - * - * @param templateDeclaration in which the ParameterizedType is defined - * @param typeName name of the ParameterizedType - * @return - */ - public ParameterizedType createOrGetTypeParameter( - TemplateDeclaration templateDeclaration, - String typeName, - Language language) { - ParameterizedType parameterizedType = getTypeParameter(templateDeclaration, typeName); - if (parameterizedType == null) { - parameterizedType = new ParameterizedType(typeName, language); - addTypeParameter(templateDeclaration, parameterizedType); - } - return parameterizedType; - } - - @NotNull - public Map> getTypeState() { - return typeState; - } - - @NotNull - public T registerType(T t) { - if (t.isFirstOrderType()) { - this.firstOrderTypes.add(t); - } else { - this.secondOrderTypes.add(t); - registerType(((SecondOrderType) t).getElementType()); - } - return t; - } - - public Set getFirstOrderTypes() { - return firstOrderTypes; - } - - public Set getSecondOrderTypes() { - return secondOrderTypes; - } - - public boolean typeExists(String name) { - return firstOrderTypes.stream() - .anyMatch(type -> type.getRoot().getName().toString().equals(name)); - } - - public TypeManager() {} - - public static boolean isTypeSystemActive() { - return typeSystemActive; - } - - public static void setTypeSystemActive(boolean active) { - typeSystemActive = active; - } - - @NotNull - public Map> getTypeCache() { - return typeCache; - } - - public synchronized void cacheType(HasType node, Type type) { - if (!isUnknown(type)) { - List types = typeCache.computeIfAbsent(node, n -> new ArrayList<>()); - if (!types.contains(type)) { - types.add(type); - } - } - } - - public static boolean isPrimitive(Type type, Language language) { - return language.getPrimitiveTypeNames().contains(type.getTypeName()); - } - - public boolean isUnknown(Type type) { - return type instanceof UnknownType; - } - - /** - * @param generics the list of parameter types - * @return true if the generics contain parameterized Types - */ - public boolean containsParameterizedType(List generics) { - for (Type t : generics) { - if (t instanceof ParameterizedType) { - return true; - } - } - return false; - } - - /** - * @param type oldType that we want to replace - * @param newType newType - * @return true if an objectType with instantiated generics is replaced by the same objectType - * with parameterizedTypes as generics false otherwise - */ - public boolean stopPropagation(Type type, Type newType) { - if (type instanceof ObjectType typeObjectType - && newType instanceof ObjectType newObjectType - && type.getName().equals(newType.getName())) { - return containsParameterizedType(newObjectType.getGenerics()) - && !(containsParameterizedType(typeObjectType.getGenerics())); - } - return false; - } - - private Optional rewrapType( - Type type, - int depth, - PointerType.PointerOrigin[] pointerOrigins, - boolean reference, - ReferenceType referenceType) { - if (depth > 0) { - for (int i = depth - 1; i >= 0; i--) { - type = type.reference(pointerOrigins[i]); - } - } - if (reference) { - referenceType.setElementType(type); - return Optional.of(referenceType); - } - return Optional.of(type); - } - - private Set unwrapTypes(Collection types, WrapState wrapState) { - // TODO Performance: This method is called very often (for each setType()) and does four - // iterations over "types". Reduce number of iterations. - Set original = new HashSet<>(types); - Set unwrappedTypes = new HashSet<>(); - PointerType.PointerOrigin[] pointerOrigins = new PointerType.PointerOrigin[0]; - int depth = 0; - int counter = 0; - boolean reference = false; - ReferenceType referenceType = null; - - Type t1 = types.stream().findAny().orElse(null); - - if (t1 instanceof ReferenceType) { - for (Type t : types) { - referenceType = (ReferenceType) t; - if (!referenceType.isSimilar(t)) { - return Collections.emptySet(); - } - unwrappedTypes.add(((ReferenceType) t).getElementType()); - reference = true; - } - types = unwrappedTypes; - } - - Type t2 = types.stream().findAny().orElse(null); - - if (t2 instanceof PointerType) { - for (Type t : types) { - if (counter == 0) { - depth = t.getReferenceDepth(); - counter++; - } - if (t.getReferenceDepth() != depth) { - return Collections.emptySet(); - } - unwrappedTypes.add(t.getRoot()); - - pointerOrigins = new PointerType.PointerOrigin[depth]; - var containedType = t2; - int i = 0; - pointerOrigins[i] = ((PointerType) containedType).getPointerOrigin(); - while (containedType instanceof PointerType) { - containedType = ((PointerType) containedType).getElementType(); - if (containedType instanceof PointerType) { - pointerOrigins[++i] = ((PointerType) containedType).getPointerOrigin(); - } - } - } - } - - wrapState.depth = depth; - wrapState.setPointerOrigin(pointerOrigins); - wrapState.setReference(reference); - wrapState.referenceType = referenceType; - - if (unwrappedTypes.isEmpty() && !original.isEmpty()) { - return original; - } else { - return unwrappedTypes; - } - } - - /** - * This function is a relict from the old ages. It iterates through a collection of types and - * returns the type they have in *common*. For example, if two types `A` and `B` both derive from - * the interface `C`` then `C` would be returned. Because this contains some legacy code that does - * crazy stuff, we need access to scope information, so we can build a map between type - * information and their record declarations. We want to get rid of that in the future. - * - * @param types the types to compare - * @param ctx a {@link TranslationContext}. - * @return the common type - */ - @NotNull - public Optional getCommonType(@NotNull Collection types, TranslationContext ctx) { - var provider = ctx.getScopeManager(); - - // TODO: Documentation needed. - boolean sameType = - types.stream().map(t -> t.getClass().getCanonicalName()).collect(Collectors.toSet()).size() - == 1; - if (!sameType) { - // No commonType for different Types - return Optional.empty(); - } - WrapState wrapState = new WrapState(); - - types = unwrapTypes(types, wrapState); - - if (types.isEmpty()) { - return Optional.empty(); - } else if (types.size() == 1) { - return rewrapType( - types.iterator().next(), - wrapState.depth, - wrapState.pointerOrigins, - wrapState.isReference(), - wrapState.referenceType); - } - - var scope = provider.getScope(); - - if (scope == null) { - return Optional.empty(); - } - - // We need to find the global scope - var globalScope = provider.getScope().getGlobalScope(); - if (globalScope == null) { - return Optional.empty(); - } - - for (var child : globalScope.getChildren()) { - if (child instanceof RecordScope && child.getAstNode() instanceof RecordDeclaration) { - typeToRecord.put( - ((RecordDeclaration) child.getAstNode()).toType(), - (RecordDeclaration) child.getAstNode()); - } - - // HACKY HACK HACK - if (child instanceof NameScope) { - for (var child2 : child.getChildren()) { - if (child2 instanceof RecordScope && child2.getAstNode() instanceof RecordDeclaration) { - typeToRecord.put( - ((RecordDeclaration) child2.getAstNode()).toType(), - (RecordDeclaration) child2.getAstNode()); - } - } - } - } - - List> allAncestors = - types.stream() - .map(t -> typeToRecord.getOrDefault(t, null)) - .filter(Objects::nonNull) - .map(r -> getAncestors(r, 0)) - .toList(); - - // normalize/reverse depth: roots start at 0, increasing on each level - for (Set ancestors : allAncestors) { - Optional farthest = - ancestors.stream().max(Comparator.comparingInt(Ancestor::getDepth)); - if (farthest.isPresent()) { - int maxDepth = farthest.get().getDepth(); - ancestors.forEach(a -> a.setDepth(maxDepth - a.getDepth())); - } - } - - Set commonAncestors = new HashSet<>(); - for (int i = 0; i < allAncestors.size(); i++) { - if (i == 0) { - commonAncestors.addAll(allAncestors.get(i)); - } else { - Set others = allAncestors.get(i); - Set newCommonAncestors = new HashSet<>(); - // like Collection#retainAll but swaps relevant items out if the other set's matching - // ancestor has a higher depth - for (Ancestor curr : commonAncestors) { - Optional toRetain = - others.stream() - .filter(a -> a.equals(curr)) - .map(a -> curr.getDepth() >= a.getDepth() ? curr : a) - .findFirst(); - toRetain.ifPresent(newCommonAncestors::add); - } - commonAncestors = newCommonAncestors; - } - } - - Optional lca = - commonAncestors.stream().max(Comparator.comparingInt(Ancestor::getDepth)); - Optional commonType = - lca.map( - a -> - TypeParser.createFrom( - a.getRecord().getName().toString(), a.getRecord().getLanguage(), false, ctx)); - - Type finalType; - if (commonType.isPresent()) { - finalType = commonType.get(); - } else { - return commonType; - } - - return rewrapType( - finalType, - wrapState.depth, - wrapState.pointerOrigins, - wrapState.isReference(), - wrapState.referenceType); - } - - private Set getAncestors(RecordDeclaration recordDeclaration, int depth) { - if (recordDeclaration.getSuperTypes().isEmpty()) { - HashSet ret = new HashSet<>(); - ret.add(new Ancestor(recordDeclaration, depth)); - return ret; - } - Set ancestors = - recordDeclaration.getSuperTypes().stream() - .map(s -> typeToRecord.getOrDefault(s, null)) - .filter(Objects::nonNull) - .map(s -> getAncestors(s, depth + 1)) - .flatMap(Collection::stream) - .collect(Collectors.toSet()); - ancestors.add(new Ancestor(recordDeclaration, depth)); - return ancestors; - } - - public boolean isSupertypeOf(Type superType, Type subType, MetadataProvider provider) { - Language language = null; - TranslationContext ctx; - - if (superType instanceof UnknownType && subType instanceof UnknownType) return true; - - if (superType.getReferenceDepth() != subType.getReferenceDepth()) { - return false; - } - - if (provider instanceof LanguageProvider languageProvider) { - language = languageProvider.getLanguage(); - } - - if (provider instanceof ContextProvider contextProvider) { - ctx = contextProvider.getCtx(); - } else { - log.error("Missing context provider"); - return false; - } - - // arrays and pointers match in C/C++ - // TODO: Make this independent from the specific language - if (isCXX(language) && checkArrayAndPointer(superType, subType)) { - return true; - } - - // ObjectTypes can be passed as ReferenceTypes - if (superType instanceof ReferenceType referenceType) { - return isSupertypeOf(referenceType.getElementType(), subType, provider); - } - - // We cannot proceed without a scope provider - if (!(provider instanceof ScopeProvider scopeProvider)) { - return false; - } - - Optional commonType = getCommonType(new HashSet<>(List.of(superType, subType)), ctx); - if (commonType.isPresent()) { - return commonType.get().equals(superType); - } else { - // If array depth matches: check whether these are types from the standard library - try { - Class superCls = Class.forName(superType.getTypeName()); - Class subCls = Class.forName(subType.getTypeName()); - return superCls.isAssignableFrom(subCls); - } catch (ClassNotFoundException | NoClassDefFoundError e) { - // Not in the class path or other linkage exception, can't help here - return false; - } - } - } - - private boolean isCXX(Language language) { - return language != null - && (language.getClass().getSimpleName().equals("CLanguage") - || language.getClass().getSimpleName().equals("CPPLanguage")); - } - - public boolean checkArrayAndPointer(Type first, Type second) { - int firstDepth = first.getReferenceDepth(); - int secondDepth = second.getReferenceDepth(); - if (firstDepth == secondDepth) { - return first.getRoot().getName().equals(second.getRoot().getName()) - && first.isSimilar(second); - } else { - return false; - } - } - - public void cleanup() { - this.typeToRecord.clear(); - } - - private Type getTargetType(Type currTarget, String alias, TranslationContext ctx) { - if (alias.contains("(") && alias.contains("*")) { - // function pointer - return TypeParser.createFrom( - currTarget.getName() + " " + alias, currTarget.getLanguage(), false, ctx); - } else if (alias.endsWith("]")) { - // array type - return currTarget.reference(PointerType.PointerOrigin.ARRAY); - } else if (alias.contains("*")) { - // pointer - int depth = StringUtils.countMatches(alias, '*'); - for (int i = 0; i < depth; i++) { - currTarget = currTarget.reference(PointerType.PointerOrigin.POINTER); - } - return currTarget; - } else { - return currTarget; - } - } - - private Type getAlias( - String alias, - @NotNull Language language, - TranslationContext ctx) { - if (alias.contains("(") && alias.contains("*")) { - // function pointer - Matcher matcher = funPointerPattern.matcher(alias); - if (matcher.find()) { - return TypeParser.createIgnoringAlias(matcher.group("alias"), language, ctx); - } else { - log.error("Could not find alias name in function pointer typedef: {}", alias); - return TypeParser.createIgnoringAlias(alias, language, ctx); - } - } else { - alias = alias.split("\\[")[0]; - alias = alias.replace("*", ""); - return TypeParser.createIgnoringAlias(alias, language, ctx); - } - } - - /** - * Creates a typedef / type alias in the form of a {@link TypedefDeclaration} to the scope manager - * and returns it. - * - * @param frontend the language frontend - * @param rawCode the raw code - * @param target the target type - * @param aliasString the alias / name of the typedef - * @return the typedef declaration - */ - @NotNull - public Declaration createTypeAlias( - @NotNull LanguageFrontend frontend, String rawCode, Type target, String aliasString) { - String cleanedPart = Util.removeRedundantParentheses(aliasString); - Type currTarget = getTargetType(target, cleanedPart, frontend.getCtx()); - Type alias; - alias = getAlias(cleanedPart, frontend.getLanguage(), frontend.getCtx()); - - if (alias instanceof SecondOrderType) { - Type chain = alias.duplicate(); - chain.setRoot(currTarget); - currTarget = chain; - currTarget.refreshNames(); - alias = alias.getRoot(); - } - - TypedefDeclaration typedef = newTypedefDeclaration(frontend, currTarget, alias, rawCode); - - frontend.getScopeManager().addTypedef(typedef); - - return typedef; - } - - public Type resolvePossibleTypedef(Type alias, ScopeManager scopeManager) { - Type finalToCheck = alias.getRoot(); - Optional applicable = - scopeManager.getCurrentTypedefs().stream() - .filter(t -> t.getAlias().getRoot().equals(finalToCheck)) - .findAny() - .map(TypedefDeclaration::getType); - - if (applicable.isEmpty()) { - return alias; - } else { - return TypeParser.reWrapType(alias, applicable.get()); - } - } - - private static class Ancestor { - - private final RecordDeclaration recordDeclaration; - private int depth; - - public Ancestor(RecordDeclaration recordDeclaration, int depth) { - this.recordDeclaration = recordDeclaration; - this.depth = depth; - } - - public RecordDeclaration getRecord() { - return recordDeclaration; - } - - public int getDepth() { - return depth; - } - - public void setDepth(int depth) { - this.depth = depth; - } - - @Override - public int hashCode() { - return Objects.hash(recordDeclaration); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof Ancestor)) { - return false; - } - Ancestor ancestor = (Ancestor) o; - return Objects.equals(recordDeclaration, ancestor.recordDeclaration); - } - - @Override - public String toString() { - return new ToStringBuilder(this, Node.TO_STRING_STYLE) - .append("record", recordDeclaration.getName()) - .append("depth", depth) - .toString(); - } - } -} diff --git a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/TypeParser.java b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/TypeParser.java deleted file mode 100644 index 7a25eea063..0000000000 --- a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/TypeParser.java +++ /dev/null @@ -1,712 +0,0 @@ -/* - * Copyright (c) 2019, Fraunhofer AISEC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * $$$$$$\ $$$$$$$\ $$$$$$\ - * $$ __$$\ $$ __$$\ $$ __$$\ - * $$ / \__|$$ | $$ |$$ / \__| - * $$ | $$$$$$$ |$$ |$$$$\ - * $$ | $$ ____/ $$ |\_$$ | - * $$ | $$\ $$ | $$ | $$ | - * \$$$$$ |$$ | \$$$$$ | - * \______/ \__| \______/ - * - */ -package de.fraunhofer.aisec.cpg.graph.types; - -import de.fraunhofer.aisec.cpg.ScopeManager; -import de.fraunhofer.aisec.cpg.TranslationContext; -import de.fraunhofer.aisec.cpg.frontends.*; -import de.fraunhofer.aisec.cpg.graph.TypeManager; -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Class responsible for parsing the type definition and create the same Type as described by the - * type string, but complying to the CPG TypeSystem - */ -public class TypeParser { - - private static final Logger log = LoggerFactory.getLogger(TypeParser.class); - - // TODO: document/remove this regexp - private static final Pattern functionPtrRegex = - Pattern.compile( - "(?:(?[\\h(]+[a-zA-Z0-9_$.<>:]*\\*\\h*[a-zA-Z0-9_$.<>:]*[\\h)]+)\\h*)(?\\(+[a-zA-Z0-9_$.<>,\\*\\&\\h]*\\))"); - private static final List potentialKeywords = - List.of( - "STATIC", - "EXTERN", - "REGISTER", - "AUTO", - "FINAL", - "CONST", - "RESTRICT", - "VOLATILE", - "ATOMIC"); - - private TypeParser() { - throw new IllegalStateException("Do not instantiate the TypeParser"); - } - - /** - * Returns whether the specifier is part of an elaborated type specifier. This only applies to C++ - * and can be used to declare that a type is a class / struct or union even though the type is not - * visible in the scope. - * - * @param specifier the specifier - * @return true, if it is part of an elaborated type. false, otherwise - */ - public static boolean isElaboratedTypeSpecifier( - String specifier, Language language) { - return language instanceof HasElaboratedTypeSpecifier hasElaboratedTypeSpecifier - && hasElaboratedTypeSpecifier.getElaboratedTypeSpecifier().contains(specifier); - } - - /** - * searching closing bracket - * - * @param openBracket opening bracket char - * @param closeBracket closing bracket char - * @param string substring without the openening bracket - * @return position of the closing bracket - */ - private static int findMatching(char openBracket, char closeBracket, String string) { - int counter = 1; - int i = 0; - - while (counter != 0) { - if (i >= string.length()) { - // dirty hack for now - return string.length(); - } - - char actualChar = string.charAt(i); - if (actualChar == openBracket) { - counter++; - } else if (actualChar == closeBracket) { - counter--; - } - i++; - } - - return i; - } - - /** - * Matches the type blocks and checks if the typeString has the structure of a function pointer - * - * @param type separated type string - * @return the Matcher of the functionPointer or null - */ - @Nullable - private static Matcher getFunctionPtrMatcher(@NotNull List type) { - - StringBuilder typeStringBuilder = new StringBuilder(); - for (String typePart : type) { - typeStringBuilder.append(typePart); - } - - String typeString = typeStringBuilder.toString().trim(); - - Matcher matcher = functionPtrRegex.matcher(typeString); - if (matcher.find()) { - return matcher; - } - return null; - } - - /** - * Right now IncompleteTypes are only defined as void {@link IncompleteType} - * - * @param typeName String with the type - * @return true if the type is void, false otherwise - */ - private static boolean isIncompleteType(String typeName) { - return typeName.trim().equals("void"); - } - - private static boolean isUnknownType( - String typeName, @NotNull Language language) { - return typeName.equals(Type.UNKNOWN_TYPE_STRING) - || (language instanceof HasUnknownType hasUnknownType - && hasUnknownType.getUnknownTypeString().contains(typeName)); - } - - /** - * Removes spaces between a generics Expression, i.e. between "<" and ">" and the preceding Type - * information Required since, afterwards typeString get split by spaces - * - * @param type typeString - * @return typeString without spaces in the generic Expression - */ - @NotNull - private static String fixGenerics( - @NotNull String type, @NotNull Language language) { - if (language instanceof HasGenerics hasGenerics - && type.indexOf(hasGenerics.getStartCharacter()) > -1 - && type.indexOf(hasGenerics.getEndCharacter()) > -1) { - - char startCharacter = hasGenerics.getStartCharacter(); - char endCharacter = hasGenerics.getEndCharacter(); - - // Get the generic string between startCharacter and endCharacter. - String generics = - type.substring(type.indexOf(startCharacter) + 1, type.lastIndexOf(endCharacter)); - if (language instanceof HasElaboratedTypeSpecifier hasElaboratedTypeSpecifier) { - /* We can have elaborate type specifiers (e.g. struct) inside this string. We want to remove it. - * We remove this specifier from the generic string. - * To do so, this regex checks that a specifier is preceded by "<" (or whatever is the startCharacter), "," or a whitespace and is also followed by a whitespace (to avoid removing other strings by mistake). - */ - generics = - generics.replaceAll( - "((^|[\\h," - + hasGenerics.getStartCharacter() - + "])\\h*)((" - + String.join("|", hasElaboratedTypeSpecifier.getElaboratedTypeSpecifier()) - + ")\\h+)", - "$1"); - } - // Add the generic to the original string again but also remove whitespaces in the generic. - type = - type.substring(0, type.indexOf(startCharacter) + 1) - + generics.replaceAll("\\h", "").trim() - + type.substring(type.lastIndexOf(endCharacter)); - // Remove unnecessary whitespace around the start and end characters. - type = type.replaceAll("\\h*(" + startCharacter + "|" + endCharacter + "\\h?)\\h*", "$1"); - } - - return type; - } - - private static void processBlockUntilLastSplit( - @NotNull String type, int lastSplit, int newPosition, @NotNull List typeBlocks) { - String substr = type.substring(lastSplit, newPosition); - if (substr.length() != 0) { - typeBlocks.add(substr); - } - } - - /** - * Separates typeString into the different Parts that make up the type information - * - * @param type string with the entire type definition - * @return list of strings in which every piece of type information is one element of the list - */ - @NotNull - public static List separate( - @NotNull String type, Language language) { - type = type.split("=")[0]; - - // Guarantee that there is no arbitrary number of whitespaces - String[] typeSubpart = type.split(" "); - type = String.join(" ", typeSubpart).trim(); - - List typeBlocks = new ArrayList<>(); - - // Splits TypeString into relevant information blocks - int lastSplit = 0; - - for (int i = 0; i < type.length(); i++) { - char ch = type.charAt(i); - switch (ch) { - case ' ' -> { - // handle space create element - processBlockUntilLastSplit(type, lastSplit, i, typeBlocks); - lastSplit = i + 1; - } - case '(' -> { - // handle ( find matching closing ignore content (not relevant type information) - processBlockUntilLastSplit(type, lastSplit, i, typeBlocks); - int finishPosition = findMatching('(', ')', type.substring(i + 1)); - typeBlocks.add(type.substring(i, i + finishPosition + 1)); - i = finishPosition + i; - lastSplit = i + 1; - } - case '[' -> { - // handle [ find matching closing ignore content (not relevant type information) - int finishPosition = findMatching('[', ']', type.substring(i + 1)); - Pattern onlyNumbers = Pattern.compile("^\\[[0-9]*\\]$"); - // If a language uses '[‘ for its generics, we want to make sure that only numbers (e.g. - // for array sizes) are between the brackets. We assume that a type cannot be a number - // here. - if (!(language instanceof HasGenerics hasGenerics - && hasGenerics.getStartCharacter() == '[') - || onlyNumbers.matcher(type.substring(i, i + finishPosition + 1)).matches()) { - processBlockUntilLastSplit(type, lastSplit, i, typeBlocks); - - typeBlocks.add("[]"); // type.substring(i, i+finishPosition+1) - i = finishPosition + i; - lastSplit = i + 1; - } - } - case '*' -> { - // handle * operator - processBlockUntilLastSplit(type, lastSplit, i, typeBlocks); - typeBlocks.add("*"); - lastSplit = i + 1; - } - case '&' -> { - // handle & operator - processBlockUntilLastSplit(type, lastSplit, i, typeBlocks); - typeBlocks.add("&"); - lastSplit = i + 1; - } - default -> { - // everything else - String substr = type.substring(lastSplit); - if (substr.length() != 0 && i == type.length() - 1) { - typeBlocks.add(substr); - } - } - } - } - - return typeBlocks; - } - - private static List getParameterList( - String parameterList, Language language, TranslationContext ctx) { - if (parameterList.startsWith("(") && parameterList.endsWith(")")) { - parameterList = parameterList.trim().substring(1, parameterList.trim().length() - 1); - } - List parameters = new ArrayList<>(); - String[] parametersSplit = parameterList.split(","); - for (String parameter : parametersSplit) { - // ignore void parameters // TODO: WHY?? - if (parameter.length() > 0 && !parameter.trim().equals("void")) { - parameters.add(createFrom(parameter.trim(), language, false, ctx)); - } - } - - return parameters; - } - - private static List getGenerics( - String typeName, Language language, TranslationContext ctx) { - List genericList = new ArrayList<>(); - if (language instanceof HasGenerics hasGenerics - && typeName.indexOf(hasGenerics.getStartCharacter()) > -1 - && typeName.indexOf(hasGenerics.getEndCharacter()) > -1) { - String generics = - typeName.substring( - typeName.indexOf(hasGenerics.getStartCharacter()) + 1, - typeName.lastIndexOf(hasGenerics.getEndCharacter())); - - String[] parametersSplit = generics.split(","); - for (String parameter : parametersSplit) { - genericList.add(createFrom(parameter.trim(), language, false, ctx)); - } - } - return genericList; - } - - private static Type performBracketContentAction( - Type finalType, String part, Language language) { - if (part.equals("*")) { - return finalType.reference(PointerType.PointerOrigin.POINTER); - } - - if (part.equals("&")) { - return finalType.dereference(); - } - - if (part.startsWith("[") && part.endsWith("]")) { - return finalType.reference(PointerType.PointerOrigin.ARRAY); - } - - if (part.startsWith("(") && part.endsWith(")")) { - return resolveBracketExpression(finalType, List.of(part), language); - } - - return finalType; - } - - /** - * Makes sure to apply Expressions containing brackets that change the binding of operators e.g. - * () can change the binding order of operators - * - * @param finalType Modifications are applied to this type which is the result of the preceding - * type calculations - * @param bracketExpressions List of Strings containing bracket expressions - * @return modified finalType performing the resolution of the bracket expressions - */ - private static Type resolveBracketExpression( - @NotNull Type finalType, - @NotNull List bracketExpressions, - @NotNull Language language) { - for (String bracketExpression : bracketExpressions) { - List splitExpression = - separate(bracketExpression.substring(1, bracketExpression.length() - 1), language); - for (String part : splitExpression) { - finalType = performBracketContentAction(finalType, part, language); - } - } - - return finalType; - } - - /** - * Helper function that removes access modifier from the typeString. - * - * @param type provided typeString - * @return typeString without access modifier - */ - private static String removeAccessModifier( - @NotNull String type, @NotNull Language language) { - return type.replaceAll(String.join("|", language.getAccessModifiers()), "").trim(); - } - - /** - * Checks if the List of separated parts of the typeString contains an element indicating that it - * is a primitive type - * - * @param stringList - * @return - */ - private static boolean isPrimitiveType( - @NotNull List stringList, @NotNull Language language) { - return stringList.stream().anyMatch(s -> language.getPrimitiveTypeNames().contains(s)); - } - - /** - * Joins compound primitive data types such as long long int and consolidates those information - * blocks - * - * @param typeBlocks - * @return separated words of compound types are joined into one string - */ - @NotNull - private static List joinPrimitive( - @NotNull List typeBlocks, @NotNull Language language) { - List joinedTypeBlocks = new ArrayList<>(); - StringBuilder primitiveType = new StringBuilder(); - int index = 0; - - for (String s : typeBlocks) { - if (language.getPrimitiveTypeNames().contains(s)) { - if (primitiveType.length() > 0) { - primitiveType.append(" "); - } else { - index = joinedTypeBlocks.size(); - } - primitiveType.append(s); - } else { - joinedTypeBlocks.add(s); - } - } - - if (!primitiveType.isEmpty()) { - joinedTypeBlocks.add(index, primitiveType.toString()); - } - - return joinedTypeBlocks; - } - - /** - * Reconstructs the type chain when the root node is modified e.g. when swapping with alias - * (typedef) - * - * @param oldChain containing all types until the root - * @param newRoot root the chain is swapped with - * @return oldchain but root replaced with newRoot - */ - @NotNull - public static Type reWrapType(@NotNull Type oldChain, @NotNull Type newRoot) { - if (oldChain.isFirstOrderType()) { - newRoot.setTypeOrigin(oldChain.getTypeOrigin()); - } - - if (!newRoot.isFirstOrderType()) { - return newRoot; - } - - if (oldChain instanceof ObjectType && newRoot instanceof ObjectType) { - ((ObjectType) newRoot.getRoot()).setGenerics(((ObjectType) oldChain).getGenerics()); - return newRoot; - } else if (oldChain instanceof ReferenceType referenceType) { - Type reference = reWrapType(referenceType.getElementType(), newRoot); - ReferenceType newChain = (ReferenceType) oldChain.duplicate(); - newChain.setElementType(reference); - newChain.refreshName(); - return newChain; - } else if (oldChain instanceof PointerType) { - PointerType newChain = (PointerType) oldChain.duplicate(); - newChain.setRoot(reWrapType(oldChain.getRoot(), newRoot)); - newChain.refreshNames(); - return newChain; - } else { - return newRoot; - } - } - - /** - * Does the same as createFrom but explicitly does not use type alias resolution. This is usually - * not what you want. Use with care! - * - * @param string the string representation of the type - * @return the type - */ - @NotNull - public static Type createIgnoringAlias( - @NotNull String string, - @NotNull Language language, - TranslationContext ctx) { - return createFrom(string, language, false, ctx); - } - - @NotNull - private static Type postTypeParsing( - @NotNull List subPart, - @NotNull Type finalType, - @NotNull List bracketExpressions) { - for (String part : subPart) { - if (part.equals("*")) { - // Creates a Pointer to the finalType - finalType = finalType.reference(PointerType.PointerOrigin.POINTER); - } - - if (part.equals("&")) { - // CPP ReferenceTypes are indicated by an & at the end of the typeName e.g. int&, and are - // handled differently to a pointer - finalType = new ReferenceType(finalType); - } - - if (part.startsWith("[") && part.endsWith("]")) { - // Arrays are equal to pointer, create a reference - finalType = finalType.reference(PointerType.PointerOrigin.ARRAY); - } - - if (part.startsWith("(") && part.endsWith(")")) { - // BracketExpressions change the binding of operators they are stored in order to be - // processed afterwards - bracketExpressions.add(part); - } - } - return finalType; - } - - private static String removeGenerics( - String typeName, @NotNull Language language) { - if (language instanceof HasGenerics hasGenerics - && typeName.contains(hasGenerics.getStartCharacter() + "") - && typeName.contains(hasGenerics.getEndCharacter() + "")) { - typeName = typeName.substring(0, typeName.indexOf(hasGenerics.getStartCharacter())); - } - return typeName; - } - - private static String determineModifier(List typeBlocks, boolean primitiveType) { - // Default is signed, unless unsigned keyword is specified. For other classes that are not - // primitive this is NOT_APPLICABLE - String modifier = ""; - if (primitiveType) { - if (typeBlocks.contains("unsigned")) { - modifier = "unsigned "; - typeBlocks.remove("unsigned"); - } else if (typeBlocks.contains("signed")) { - modifier = "signed "; - typeBlocks.remove("signed"); - } - } - return modifier; - } - - private static boolean checkValidTypeString(String type) { - // Todo ? can be part of generic string -> more fine-grained analysis necessary - return !type.contains("?") - && !type.contains("org.eclipse.cdt.internal.core.dom.parser.ProblemType@") - && type.trim().length() != 0; - } - - /** - * Warning: This function might crash, when a type cannot be parsed. Use createFrom instead Use - * this function for parsing new types and obtaining a new Type the TypeParser creates from the - * typeString - * - * @param type string with type information - * @param resolveAlias should replace with original type in typedefs - * @return new type representing the type string - */ - @NotNull - private static Type createFromUnsafe( - @NotNull String type, - boolean resolveAlias, - @NotNull Language language, - TranslationContext ctx) { - var typeManager = ctx.getTypeManager(); - var scopeManager = ctx.getScopeManager(); - - // Check if Problems during Parsing - if (!checkValidTypeString(type)) { - return UnknownType.getUnknownType(language); - } - - // Preprocessing of the typeString - type = removeAccessModifier(type, language); - - // Determine if inner class - - type = fixGenerics(type, language); - - // Separate typeString into a List containing each part of the typeString - List typeBlocks = separate(type, language); - - // Depending on if the Type is primitive or not signed/unsigned must be set differently (only - // relevant for ObjectTypes) - boolean primitiveType = isPrimitiveType(typeBlocks, language); - - // Default is signed, unless unsigned keyword is specified. For other classes that are not - // primitive this is NOT_APPLICABLE - String modifier = determineModifier(typeBlocks, primitiveType); - - // Join compound primitive types into one block i.e. types consisting of more than one word e.g. - // long long int (only primitive types) - typeBlocks = joinPrimitive(typeBlocks, language); - - // Handle preceding qualifier or storage specifier to the type name e.g. static const int - int counter = 0; - - for (String part : typeBlocks) { - if (potentialKeywords.contains(part.toUpperCase()) - || isElaboratedTypeSpecifier(part, language)) { - // We only want to get rid of these parts for the remaining method. - counter++; - } else { - break; - } - } - - // Once all preceding known keywords (if any) are handled the next word must be the TypeName - if (counter >= typeBlocks.size()) { - // Note that "const auto ..." will end here with typeName="const" as auto is not supported. - return UnknownType.getUnknownType(language); - } - String typeName = typeBlocks.get(counter); - counter++; - - Type finalType; - - // Check if type is FunctionPointer - Matcher funcptr = getFunctionPtrMatcher(typeBlocks.subList(counter, typeBlocks.size())); - - finalType = language.getSimpleTypeOf(modifier + typeName); - if (finalType != null) { - // Nothing to do here - } else if (funcptr != null) { - Type returnType = createFrom(typeName, language, false, ctx); - List parameterList = getParameterList(funcptr.group("args"), language, ctx); - - return typeManager.registerType(new FunctionPointerType(parameterList, language, returnType)); - } else if (isIncompleteType(typeName)) { - // IncompleteType e.g. void - finalType = new IncompleteType(); - } else if (isUnknownType(typeName, language)) { - // UnknownType -> no information on how to process this type - finalType = new UnknownType(Type.UNKNOWN_TYPE_STRING); - } else { - // ObjectType - // Obtain possible generic List from TypeString - List generics = getGenerics(typeName, language, ctx); - typeName = removeGenerics(typeName, language); - finalType = new ObjectType(typeName, generics, primitiveType, language); - } - - // Process Keywords / Operators (*, &) after typeName - List subPart = typeBlocks.subList(counter, typeBlocks.size()); - - List bracketExpressions = new ArrayList<>(); - - finalType = postTypeParsing(subPart, finalType, bracketExpressions); - - // Resolve BracketExpressions that were identified previously - finalType = resolveBracketExpression(finalType, bracketExpressions, language); - - // Make sure, that only one real instance exists for a type in order to have just one node in - // the graph representing the type - finalType = typeManager.registerType(finalType); - - if (resolveAlias) { - return typeManager.registerType(typeManager.resolvePossibleTypedef(finalType, scopeManager)); - } - - return finalType; - } - - /** - * A specialized version of the type parsing function that needs a language frontend and does - * magic with generics and typedefs. This is legacy code and currently only used for CXX frontend - * and should be removed at some point. - */ - public static Type createFrom( - @NotNull String type, boolean resolveAlias, LanguageFrontend frontend) { - Type templateType = - searchForTemplateTypes(type, frontend.getScopeManager(), frontend.getTypeManager()); - if (templateType != null) { - return templateType; - } - - Type createdType = createFrom(type, frontend.getLanguage(), resolveAlias, frontend.getCtx()); - - if (createdType instanceof SecondOrderType) { - templateType = - searchForTemplateTypes( - createdType.getRoot().getName().toString(), - frontend.getScopeManager(), - frontend.getTypeManager()); - if (templateType != null) { - createdType.setRoot(templateType); - } - } - - return createdType; - } - - private static Type searchForTemplateTypes( - @NotNull String type, ScopeManager scopeManager, TypeManager typeManager) { - return typeManager.searchTemplateScopeForDefinedParameterizedTypes( - scopeManager.getCurrentScope(), type); - } - - /** - * Use this function for parsing new types and obtaining a new Type the TypeParser creates from - * the typeString. - * - * @param type string with type information - * @param language the language in which the type exists. - * @param resolveAlias should replace with original type in typedefs - * @param ctx the translation context - * @return new type representing the type string. If an exception occurs during the parsing, - * UnknownType is returned - */ - @NotNull - public static Type createFrom( - @NotNull String type, - Language language, - boolean resolveAlias, - TranslationContext ctx) { - try { - return createFromUnsafe(type, resolveAlias, language, ctx); - } catch (Exception e) { - log.error("Could not parse the type correctly", e); - return UnknownType.getUnknownType(language); - } - } -} diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationContext.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationContext.kt index 0cdfc1333c..c309225e62 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationContext.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationContext.kt @@ -25,8 +25,6 @@ */ package de.fraunhofer.aisec.cpg -import de.fraunhofer.aisec.cpg.graph.TypeManager - /** * The translation context holds all necessary managers and configurations needed during the * translation process. diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationManager.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationManager.kt index da2eccc9cb..9e92b11164 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationManager.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationManager.kt @@ -31,7 +31,6 @@ import de.fraunhofer.aisec.cpg.frontends.SupportsParallelParsing import de.fraunhofer.aisec.cpg.frontends.TranslationException import de.fraunhofer.aisec.cpg.graph.Component import de.fraunhofer.aisec.cpg.graph.Name -import de.fraunhofer.aisec.cpg.graph.TypeManager import de.fraunhofer.aisec.cpg.helpers.Benchmark import de.fraunhofer.aisec.cpg.helpers.Util import de.fraunhofer.aisec.cpg.passes.* @@ -220,7 +219,7 @@ private constructor( sourceLocations = list } - TypeManager.setTypeSystemActive(ctx.config.typeSystemActiveInFrontend) + TypeManager.isTypeSystemActive = ctx.config.typeSystemActiveInFrontend usedFrontends.addAll( if (useParallelFrontends) { @@ -231,7 +230,7 @@ private constructor( ) if (!config.typeSystemActiveInFrontend) { - TypeManager.setTypeSystemActive(true) + TypeManager.isTypeSystemActive = true result.components.forEach { s -> s.translationUnits.forEach { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TypeManager.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TypeManager.kt new file mode 100644 index 0000000000..69097c059d --- /dev/null +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TypeManager.kt @@ -0,0 +1,650 @@ +/* + * Copyright (c) 2019, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +package de.fraunhofer.aisec.cpg + +import de.fraunhofer.aisec.cpg.frontends.Language +import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend +import de.fraunhofer.aisec.cpg.graph.* +import de.fraunhofer.aisec.cpg.graph.declarations.Declaration +import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration +import de.fraunhofer.aisec.cpg.graph.declarations.TemplateDeclaration +import de.fraunhofer.aisec.cpg.graph.declarations.TypedefDeclaration +import de.fraunhofer.aisec.cpg.graph.scopes.NameScope +import de.fraunhofer.aisec.cpg.graph.scopes.RecordScope +import de.fraunhofer.aisec.cpg.graph.scopes.Scope +import de.fraunhofer.aisec.cpg.graph.scopes.TemplateScope +import de.fraunhofer.aisec.cpg.graph.types.* +import de.fraunhofer.aisec.cpg.graph.types.PointerType.PointerOrigin +import java.util.* +import java.util.function.Consumer +import java.util.regex.Pattern +import java.util.stream.Collectors +import org.apache.commons.lang3.builder.ToStringBuilder +import org.slf4j.LoggerFactory + +class TypeManager { + val typeCache: MutableMap> = + Collections.synchronizedMap(IdentityHashMap()) + + private val typeToRecord = Collections.synchronizedMap(HashMap()) + + /** + * Stores the relationship between parameterized RecordDeclarations (e.g. Classes using + * Generics) to the ParameterizedType to be able to resolve the Type of the fields, since + * ParameterizedTypes are unique to the RecordDeclaration and are not merged. + */ + private val recordToTypeParameters = + Collections.synchronizedMap(mutableMapOf>()) + private val templateToTypeParameters = + Collections.synchronizedMap( + mutableMapOf>() + ) + + val firstOrderTypes = Collections.synchronizedSet(HashSet()) + val secondOrderTypes = Collections.synchronizedSet(HashSet()) + + /** + * @param recordDeclaration that is instantiated by a template containing parameterizedtypes + * @param name of the ParameterizedType we want to get + * @return ParameterizedType if there is a parameterized type defined in the recordDeclaration + * with matching name, null instead + */ + fun getTypeParameter(recordDeclaration: RecordDeclaration?, name: String): ParameterizedType? { + if (recordToTypeParameters.containsKey(recordDeclaration)) { + for (parameterizedType in recordToTypeParameters[recordDeclaration] ?: listOf()) { + if (parameterizedType.name.toString() == name) { + return parameterizedType + } + } + } + return null + } + + /** + * Adds a List of ParameterizedType to [TypeManager.recordToTypeParameters] + * + * @param recordDeclaration will be stored as key for the map + * @param typeParameters List containing all ParameterizedTypes used by the recordDeclaration + * and will be stored as value in the map + */ + fun addTypeParameter( + recordDeclaration: RecordDeclaration, + typeParameters: List + ) { + recordToTypeParameters[recordDeclaration] = typeParameters + } + + /** + * Searches [TypeManager.templateToTypeParameters] for ParameterizedTypes that were defined in a + * template matching the provided name + * + * @param templateDeclaration that includes the ParameterizedType we are looking for + * @param name name of the ParameterizedType we are looking for + * @return + */ + fun getTypeParameter( + templateDeclaration: TemplateDeclaration, + name: String + ): ParameterizedType? { + if (templateToTypeParameters.containsKey(templateDeclaration)) { + for (parameterizedType in templateToTypeParameters[templateDeclaration] ?: listOf()) { + if (parameterizedType.name.toString() == name) { + return parameterizedType + } + } + } + return null + } + + /** + * @param templateDeclaration + * @return List containing all ParameterizedTypes the templateDeclaration defines. If the + * templateDeclaration is not registered, an empty list is returned. + */ + fun getAllParameterizedType(templateDeclaration: TemplateDeclaration): List { + return if (templateToTypeParameters.containsKey(templateDeclaration)) { + templateToTypeParameters[templateDeclaration] ?: listOf() + } else ArrayList() + } + + /** + * Searches for ParameterizedType if the scope is a TemplateScope. If not we search the parent + * scope until we reach the top. + * + * @param scope in which we are searching for the defined ParameterizedTypes + * @param name of the ParameterizedType + * @return ParameterizedType that is found within the scope (or any parent scope) and matches + * the provided name. Null if we reach the top of the scope without finding a matching + * ParameterizedType + */ + fun searchTemplateScopeForDefinedParameterizedTypes( + scope: Scope?, + name: String + ): ParameterizedType? { + if (scope is TemplateScope) { + val node = scope.astNode + + // We need an additional check here, because of parsing or other errors, the AST node + // might + // not necessarily be a template declaration. + if (node is TemplateDeclaration) { + val parameterizedType = getTypeParameter(node, name) + if (parameterizedType != null) { + return parameterizedType + } + } + } + return if (scope!!.parent != null) + searchTemplateScopeForDefinedParameterizedTypes(scope.parent, name) + else null + } + + /** + * Adds ParameterizedType to the [TypeManager.templateToTypeParameters] to be able to resolve + * this type when it is used + * + * @param templateDeclaration key for [TypeManager.templateToTypeParameters] + * @param typeParameter ParameterizedType we want to register + */ + fun addTypeParameter( + templateDeclaration: TemplateDeclaration, + typeParameter: ParameterizedType + ) { + val parameters = + templateToTypeParameters.computeIfAbsent(templateDeclaration) { mutableListOf() } + + parameters += typeParameter + } + + /** + * Check if a ParameterizedType with name typeName is already registered. If so we return the + * already created ParameterizedType. If not, we create and return a new ParameterizedType + * + * @param templateDeclaration in which the ParameterizedType is defined + * @param typeName name of the ParameterizedType + * @return + */ + fun createOrGetTypeParameter( + templateDeclaration: TemplateDeclaration, + typeName: String, + language: Language<*>? + ): ParameterizedType { + var parameterizedType = getTypeParameter(templateDeclaration, typeName) + if (parameterizedType == null) { + parameterizedType = ParameterizedType(typeName, language) + addTypeParameter(templateDeclaration, parameterizedType) + } + return parameterizedType + } + + fun registerType(t: T): T { + if (t!!.isFirstOrderType) { + firstOrderTypes.add(t) + } else { + secondOrderTypes.add(t) + registerType((t as SecondOrderType).elementType) + } + return t + } + + fun typeExists(name: String): Boolean { + return firstOrderTypes.stream().anyMatch { type: Type -> type.root.name.toString() == name } + } + + @Synchronized + fun cacheType(node: HasType, type: Type) { + if (!isUnknown(type)) { + val types = typeCache.computeIfAbsent(node) { n: HasType? -> ArrayList() } + if (!types.contains(type)) { + types.add(type) + } + } + } + + fun isUnknown(type: Type?): Boolean { + return type is UnknownType + } + + /** + * @param generics the list of parameter types + * @return true if the generics contain parameterized Types + */ + fun containsParameterizedType(generics: List): Boolean { + for (t in generics) { + if (t is ParameterizedType) { + return true + } + } + return false + } + + /** + * @param type oldType that we want to replace + * @param newType newType + * @return true if an objectType with instantiated generics is replaced by the same objectType + * with parameterizedTypes as generics false otherwise + */ + fun stopPropagation(type: Type, newType: Type): Boolean { + return if (type is ObjectType && newType is ObjectType && type.name.equals(newType.name)) { + (containsParameterizedType(newType.generics) && + !containsParameterizedType(type.generics)) + } else false + } + + private fun rewrapType( + type: Type, + depth: Int, + pointerOrigins: Array, + reference: Boolean, + referenceType: ReferenceType? + ): Optional { + var type = type + if (depth > 0) { + for (i in depth - 1 downTo 0) { + type = type.reference(pointerOrigins[i]) + } + } + if (reference) { + referenceType!!.elementType = type + return Optional.of(referenceType) + } + return Optional.of(type) + } + + private fun unwrapTypes(types: Collection, wrapState: WrapState): Set { + // TODO Performance: This method is called very often (for each setType()) and does four + // iterations over "types". Reduce number of iterations. + var types = types + val original: Set = HashSet(types) + val unwrappedTypes = mutableSetOf() + var pointerOrigins = arrayOfNulls(0) + var depth = 0 + var counter = 0 + var reference = false + var referenceType: ReferenceType? = null + val t1 = types.stream().findAny().orElse(null) + if (t1 is ReferenceType) { + for (t in types) { + referenceType = t as ReferenceType? + if (!referenceType!!.isSimilar(t)) { + return emptySet() + } + unwrappedTypes.add(t.elementType) + reference = true + } + types = unwrappedTypes + } + val t2 = types.stream().findAny().orElse(null) + if (t2 is PointerType) { + for (t in types) { + if (counter == 0) { + depth = t.referenceDepth + counter++ + } + if (t.referenceDepth != depth) { + return emptySet() + } + unwrappedTypes.add(t.root) + pointerOrigins = arrayOfNulls(depth) + var containedType: Type = t2 + var i = 0 + pointerOrigins[i] = (containedType as PointerType).pointerOrigin + while (containedType is PointerType) { + containedType = containedType.elementType + if (containedType is PointerType) { + pointerOrigins[++i] = containedType.pointerOrigin + } + } + } + } + wrapState.depth = depth + wrapState.setPointerOrigin(pointerOrigins) + wrapState.isReference = reference + wrapState.referenceType = referenceType + return if (unwrappedTypes.isEmpty() && !original.isEmpty()) { + original + } else { + unwrappedTypes + } + } + + /** + * This function is a relict from the old ages. It iterates through a collection of types and + * returns the type they have in *common*. For example, if two types `A` and `B` both derive + * from the interface `C`` then `C` would be returned. Because this contains some legacy code + * that does crazy stuff, we need access to scope information, so we can build a map between + * type information and their record declarations. We want to get rid of that in the future. + * + * @param types the types to compare + * @param ctx a [TranslationContext]. + * @return the common type + */ + fun getCommonType(types: Collection, ctx: TranslationContext?): Optional { + var types = types + val provider = ctx!!.scopeManager + + // TODO: Documentation needed. + val sameType = + (types + .stream() + .map { t: Type -> t.javaClass.canonicalName } + .collect(Collectors.toSet()) + .size == 1) + if (!sameType) { + // No commonType for different Types + return Optional.empty() + } + val wrapState = WrapState() + types = unwrapTypes(types, wrapState) + if (types.isEmpty()) { + return Optional.empty() + } else if (types.size == 1) { + return rewrapType( + types.iterator().next(), + wrapState.depth, + wrapState.pointerOrigins, + wrapState.isReference, + wrapState.referenceType + ) + } + val scope = provider.scope ?: return Optional.empty() + + // We need to find the global scope + val globalScope = provider.scope!!.globalScope ?: return Optional.empty() + for (child in globalScope.children) { + if (child is RecordScope && child.astNode is RecordDeclaration) { + typeToRecord[(child.astNode as RecordDeclaration?)!!.toType()] = + child.astNode as RecordDeclaration? + } + + // HACKY HACK HACK + if (child is NameScope) { + for (child2 in child.children) { + if (child2 is RecordScope && child2.astNode is RecordDeclaration) { + typeToRecord[(child2.astNode as RecordDeclaration?)!!.toType()] = + child2.astNode as RecordDeclaration? + } + } + } + } + val allAncestors = + types + .stream() + .map { t: Type? -> typeToRecord.getOrDefault(t, null) } + .filter { obj: RecordDeclaration? -> Objects.nonNull(obj) } + .map { r: RecordDeclaration? -> getAncestors(r, 0) } + .toList() + + // normalize/reverse depth: roots start at 0, increasing on each level + for (ancestors in allAncestors) { + val farthest = ancestors.stream().max(Comparator.comparingInt(Ancestor::depth)) + if (farthest.isPresent) { + val maxDepth: Int = farthest.get().depth + ancestors.forEach(Consumer { a: Ancestor -> a.depth = (maxDepth - a.depth) }) + } + } + var commonAncestors: MutableSet = HashSet() + for (i in allAncestors.indices) { + if (i == 0) { + commonAncestors.addAll(allAncestors[i]) + } else { + val others = allAncestors[i] + val newCommonAncestors: MutableSet = HashSet() + // like Collection#retainAll but swaps relevant items out if the other set's + // matching ancestor has a higher depth + for (curr in commonAncestors) { + val toRetain = + others + .stream() + .filter { a: Ancestor -> a == curr } + .map { a: Ancestor -> if (curr.depth >= a.depth) curr else a } + .findFirst() + toRetain.ifPresent { e: Ancestor -> newCommonAncestors.add(e) } + } + commonAncestors = newCommonAncestors + } + } + val lca = commonAncestors.stream().max(Comparator.comparingInt(Ancestor::depth)) + val commonType = lca.map { it.record?.toType() ?: it.record.unknownType() } + val finalType: Type + finalType = + if (commonType.isPresent) { + commonType.get() + } else { + return commonType + } + return rewrapType( + finalType, + wrapState.depth, + wrapState.pointerOrigins, + wrapState.isReference, + wrapState.referenceType + ) + } + + private fun getAncestors(recordDeclaration: RecordDeclaration?, depth: Int): Set { + if (recordDeclaration!!.superTypes.isEmpty()) { + val ret = HashSet() + ret.add(Ancestor(recordDeclaration, depth)) + return ret + } + val ancestors = + recordDeclaration.superTypes + .stream() + .map { s: Type? -> typeToRecord.getOrDefault(s, null) } + .filter { obj: RecordDeclaration? -> Objects.nonNull(obj) } + .map { s: RecordDeclaration? -> getAncestors(s, depth + 1) } + .flatMap { obj: Set -> obj.stream() } + .collect(Collectors.toSet()) + ancestors.add(Ancestor(recordDeclaration, depth)) + return ancestors + } + + fun isSupertypeOf(superType: Type, subType: Type, provider: MetadataProvider): Boolean { + var language: Language<*>? = null + val ctx: TranslationContext? + if (superType is UnknownType && subType is UnknownType) return true + if (superType.referenceDepth != subType.referenceDepth) { + return false + } + if (provider is LanguageProvider) { + language = provider.language + } + ctx = + if (provider is ContextProvider) { + provider.ctx + } else { + log.error("Missing context provider") + return false + } + + // arrays and pointers match in C/C++ + // TODO: Make this independent from the specific language + if (isCXX(language) && checkArrayAndPointer(superType, subType)) { + return true + } + + // ObjectTypes can be passed as ReferenceTypes + if (superType is ReferenceType) { + return isSupertypeOf(superType.elementType, subType, provider) + } + + // We cannot proceed without a scope provider + if (provider !is ScopeProvider) { + return false + } + val commonType = getCommonType(HashSet(java.util.List.of(superType, subType)), ctx) + return if (commonType.isPresent) { + commonType.get().equals(superType) + } else { + // If array depth matches: check whether these are types from the standard library + try { + val superCls = Class.forName(superType.typeName) + val subCls = Class.forName(subType.typeName) + superCls.isAssignableFrom(subCls) + } catch (e: ClassNotFoundException) { + // Not in the class path or other linkage exception, can't help here + false + } catch (e: NoClassDefFoundError) { + false + } + } + } + + private fun isCXX(language: Language<*>?): Boolean { + return (language != null && + (language.javaClass.simpleName == "CLanguage" || + language.javaClass.simpleName == "CPPLanguage")) + } + + fun checkArrayAndPointer(first: Type, second: Type): Boolean { + val firstDepth = first.referenceDepth + val secondDepth = second.referenceDepth + return if (firstDepth == secondDepth) { + (first.root.name.equals(second.root.name) && first.isSimilar(second)) + } else { + false + } + } + + fun cleanup() { + typeToRecord.clear() + } + + /** + * Creates a typedef / type alias in the form of a [TypedefDeclaration] to the scope manager and + * returns it. + * + * @param frontend the language frontend + * @param rawCode the raw code + * @param target the target type + * @param alias the alias type + * @return the typedef declaration + */ + fun createTypeAlias( + frontend: LanguageFrontend<*, *>, + rawCode: String?, + target: Type, + alias: Type, + ): Declaration { + var currTarget = target + var currAlias = alias + if (alias is SecondOrderType) { + // TODO: I have NO clue what the following lines do and why they are necessary + val chain = alias.duplicate() + chain.root = currTarget + currTarget = chain + currTarget.refreshNames() + currAlias = alias.root + } + val typedef = frontend.newTypedefDeclaration(currTarget, currAlias, rawCode) + frontend.scopeManager.addTypedef(typedef) + return typedef + } + + fun resolvePossibleTypedef(alias: Type, scopeManager: ScopeManager): Type { + val finalToCheck = alias.root + val applicable = + scopeManager.currentTypedefs + .firstOrNull { t: TypedefDeclaration -> t.alias.root == finalToCheck } + ?.type + return if (applicable == null) { + alias + } else { + reWrapType(alias, applicable) + } + } + + /** + * Reconstructs the type chain when the root node is modified e.g. when swapping with alias + * (typedef) + * + * @param oldChain containing all types until the root + * @param newRoot root the chain is swapped with + * @return oldchain but root replaced with newRoot + */ + private fun reWrapType(oldChain: Type, newRoot: Type): Type { + if (oldChain.isFirstOrderType) { + newRoot.typeOrigin = oldChain.typeOrigin + } + if (!newRoot.isFirstOrderType) { + return newRoot + } + return if (oldChain is ObjectType && newRoot is ObjectType) { + (newRoot.root as ObjectType).generics = oldChain.generics + newRoot + } else if (oldChain is ReferenceType) { + val reference = reWrapType(oldChain.elementType, newRoot) + val newChain = oldChain.duplicate() as ReferenceType + newChain.elementType = reference + newChain.refreshName() + newChain + } else if (oldChain is PointerType) { + val newChain = oldChain.duplicate() as PointerType + newChain.root = reWrapType(oldChain.root, newRoot) + newChain.refreshNames() + newChain + } else { + newRoot + } + } + + private class Ancestor(val record: RecordDeclaration?, var depth: Int) { + + override fun hashCode(): Int { + return Objects.hash(record) + } + + override fun equals(o: Any?): Boolean { + if (this === o) { + return true + } + if (o !is Ancestor) { + return false + } + return record == o.record + } + + override fun toString(): String { + return ToStringBuilder(this, Node.TO_STRING_STYLE) + .append("record", record!!.name) + .append("depth", depth) + .toString() + } + } + + companion object { + private val log = LoggerFactory.getLogger(TypeManager::class.java) + + // TODO: document/remove this regexp, merge with other pattern + private val funPointerPattern = Pattern.compile("\\(?\\*(?[^()]+)\\)?\\(.*\\)") + var isTypeSystemActive = true + + fun isPrimitive(type: Type, language: Language<*>): Boolean { + return language.primitiveTypeNames.contains(type.typeName) + } + } +} diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt index 8fef40a6fc..b157d8447b 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt @@ -31,11 +31,12 @@ import com.fasterxml.jackson.databind.SerializerProvider import com.fasterxml.jackson.databind.annotation.JsonSerialize import com.fasterxml.jackson.databind.ser.std.StdSerializer import de.fraunhofer.aisec.cpg.TranslationContext +import de.fraunhofer.aisec.cpg.graph.Name import de.fraunhofer.aisec.cpg.graph.Node -import de.fraunhofer.aisec.cpg.graph.newUnknownType import de.fraunhofer.aisec.cpg.graph.statements.expressions.BinaryOperator import de.fraunhofer.aisec.cpg.graph.types.* import de.fraunhofer.aisec.cpg.graph.types.Type +import de.fraunhofer.aisec.cpg.graph.unknownType import java.io.File import kotlin.reflect.KClass import kotlin.reflect.full.primaryConstructor @@ -114,7 +115,10 @@ abstract class Language> : Node() { } init { - this.also { this.language = it } + this.also { language -> + this.language = language + language::class.simpleName?.let { this.name = Name(it) } + } } private fun arithmeticOpTypePropagation(lhs: Type, rhs: Type): Type { @@ -129,7 +133,7 @@ abstract class Language> : Node() { } else { rhs } - else -> newUnknownType() + else -> unknownType() } } @@ -142,7 +146,7 @@ abstract class Language> : Node() { // A comparison, so we return the type "boolean" return this.builtInTypes.values.firstOrNull { it is BooleanType } ?: this.builtInTypes.values.firstOrNull { it.name.localName.startsWith("bool") } - ?: newUnknownType() + ?: unknownType() } return when (operation.operatorCode) { @@ -175,9 +179,9 @@ abstract class Language> : Node() { // primitive type 1 OP primitive type 2 => primitive type 1 operation.lhs.propagationType } else { - newUnknownType() + unknownType() } - else -> newUnknownType() // We don't know what is this thing + else -> unknownType() // We don't know what is this thing } } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageFrontend.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageFrontend.kt index d1e8e12453..6ecb61fcf7 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageFrontend.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageFrontend.kt @@ -25,10 +25,7 @@ */ package de.fraunhofer.aisec.cpg.frontends -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration -import de.fraunhofer.aisec.cpg.TranslationContext -import de.fraunhofer.aisec.cpg.TranslationResult +import de.fraunhofer.aisec.cpg.* import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration import de.fraunhofer.aisec.cpg.graph.scopes.Scope diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/DeclarationBuilder.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/DeclarationBuilder.kt index 70b77bf692..ad95413862 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/DeclarationBuilder.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/DeclarationBuilder.kt @@ -131,7 +131,7 @@ fun MetadataProvider.newConstructorDeclaration( @JvmOverloads fun MetadataProvider.newParamVariableDeclaration( name: CharSequence?, - type: Type = newUnknownType(), + type: Type = unknownType(), variadic: Boolean = false, code: String? = null, rawNode: Any? = null @@ -155,7 +155,7 @@ fun MetadataProvider.newParamVariableDeclaration( @JvmOverloads fun MetadataProvider.newVariableDeclaration( name: CharSequence?, - type: Type = newUnknownType(), + type: Type = unknownType(), code: String? = null, implicitInitializerAllowed: Boolean = false, rawNode: Any? = null @@ -325,7 +325,7 @@ fun MetadataProvider.newEnumConstantDeclaration( @JvmOverloads fun MetadataProvider.newFieldDeclaration( name: CharSequence?, - type: Type = newUnknownType(), + type: Type = unknownType(), modifiers: List? = listOf(), code: String? = null, location: PhysicalLocation? = null, 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 d072d2359a..ee43910124 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 @@ -31,6 +31,7 @@ import de.fraunhofer.aisec.cpg.graph.Node.Companion.EMPTY_NAME import de.fraunhofer.aisec.cpg.graph.NodeBuilder.log import de.fraunhofer.aisec.cpg.graph.statements.expressions.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.AssignExpression +import de.fraunhofer.aisec.cpg.graph.types.ProblemType import de.fraunhofer.aisec.cpg.graph.types.Type /** @@ -42,7 +43,7 @@ import de.fraunhofer.aisec.cpg.graph.types.Type @JvmOverloads fun MetadataProvider.newLiteral( value: T, - type: Type = newUnknownType(), + type: Type = unknownType(), code: String? = null, rawNode: Any? = null, ): Literal { @@ -138,7 +139,7 @@ fun MetadataProvider.newAssignExpression( @JvmOverloads fun MetadataProvider.newNewExpression( code: String? = null, - type: Type = newUnknownType(), + type: Type = unknownType(), rawNode: Any? = null ): NewExpression { val node = NewExpression() @@ -180,7 +181,7 @@ fun MetadataProvider.newConditionalExpression( condition: Expression, thenExpr: Expression?, elseExpr: Expression?, - type: Type = newUnknownType(), + type: Type = unknownType(), code: String? = null, rawNode: Any? = null ): ConditionalExpression { @@ -338,7 +339,7 @@ fun MetadataProvider.newMemberCallExpression( fun MetadataProvider.newMemberExpression( name: CharSequence?, base: Expression, - memberType: Type = newUnknownType(), + memberType: Type = unknownType(), operatorCode: String? = ".", code: String? = null, rawNode: Any? = null @@ -378,8 +379,8 @@ fun MetadataProvider.newCastExpression(code: String? = null, rawNode: Any? = nul @JvmOverloads fun MetadataProvider.newTypeIdExpression( operatorCode: String, - type: Type = newUnknownType(), - referencedType: Type = newUnknownType(), + type: Type = unknownType(), + referencedType: Type = unknownType(), code: String? = null, rawNode: Any? = null ): TypeIdExpression { @@ -462,7 +463,7 @@ fun MetadataProvider.newArrayCreationExpression( @JvmOverloads fun MetadataProvider.newDeclaredReferenceExpression( name: CharSequence?, - type: Type = newUnknownType(), + type: Type = unknownType(), code: String? = null, rawNode: Any? = null ): DeclaredReferenceExpression { @@ -554,7 +555,7 @@ fun MetadataProvider.newDesignatedInitializerExpression( @JvmOverloads fun MetadataProvider.newTypeExpression( name: CharSequence?, - type: Type = newUnknownType(), + type: Type = unknownType(), rawNode: Any? = null ): TypeExpression { val node = TypeExpression() @@ -586,6 +587,14 @@ fun MetadataProvider.newProblemExpression( return node } +fun MetadataProvider.newProblemType(code: String? = null, rawNode: Any? = null): ProblemType { + val node = ProblemType() + node.applyMetadata(this, EMPTY_NAME, rawNode, code, true) + + log(node) + return node +} + fun Literal.duplicate(implicit: Boolean): Literal { val duplicate = Literal() duplicate.ctx = this.ctx diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/NodeBuilder.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/NodeBuilder.kt index 6d05808cf4..cb80bb22d2 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/NodeBuilder.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/NodeBuilder.kt @@ -26,14 +26,15 @@ package de.fraunhofer.aisec.cpg.graph import de.fraunhofer.aisec.cpg.TranslationContext +import de.fraunhofer.aisec.cpg.TypeManager import de.fraunhofer.aisec.cpg.frontends.* import de.fraunhofer.aisec.cpg.graph.Node.Companion.EMPTY_NAME import de.fraunhofer.aisec.cpg.graph.NodeBuilder.log import de.fraunhofer.aisec.cpg.graph.scopes.Scope import de.fraunhofer.aisec.cpg.graph.statements.expressions.* -import de.fraunhofer.aisec.cpg.graph.types.Type -import de.fraunhofer.aisec.cpg.graph.types.TypeParser -import de.fraunhofer.aisec.cpg.graph.types.UnknownType +import de.fraunhofer.aisec.cpg.graph.types.* +import de.fraunhofer.aisec.cpg.graph.types.PointerType.PointerOrigin.ARRAY +import de.fraunhofer.aisec.cpg.graph.types.PointerType.PointerOrigin.POINTER import de.fraunhofer.aisec.cpg.passes.inference.IsInferredProvider import org.slf4j.LoggerFactory @@ -223,7 +224,7 @@ fun MetadataProvider.newAnnotationMember( * Creates a new [UnknownType] and sets the appropriate language, if this [MetadataProvider] * includes a [LanguageProvider]. */ -fun MetadataProvider?.newUnknownType(): UnknownType { +fun MetadataProvider?.unknownType(): Type { return if (this is LanguageProvider) { UnknownType.getUnknownType(language) } else { @@ -231,17 +232,93 @@ fun MetadataProvider?.newUnknownType(): UnknownType { } } +fun LanguageProvider.incompleteType(): Type { + return IncompleteType() +} + +/** Returns a [PointerType] that describes an array reference to the current type. */ +context(ContextProvider) + +fun Type.array(): Type { + val c = + (this@ContextProvider).ctx + ?: throw TranslationException( + "Could not create type: translation context not available" + ) + val type = this.reference(ARRAY) + + return c.typeManager.registerType(type) +} + +/** Returns a [PointerType] that describes a pointer reference to the current type. */ +context(ContextProvider) + +fun Type.pointer(): Type { + val c = + (this@ContextProvider).ctx + ?: throw TranslationException( + "Could not create type: translation context not available" + ) + val type = this.reference(POINTER) + + return c.typeManager.registerType(type) +} + +context(ContextProvider) + +fun Type.ref(): Type { + val c = + (this@ContextProvider).ctx + ?: throw TranslationException( + "Could not create type: translation context not available" + ) + val type = ReferenceType(this) + + return c.typeManager.registerType(type) +} + /** - * Provides a nice alias to [TypeParser.createFrom]. In the future, this should not be used anymore - * since we are moving away from the [TypeParser] altogether. + * This function creates a new [Type] with the given [name]. In order to avoid unnecessary + * allocation of simple types, we do a pre-check within this function, whether a built-in type exist + * with the particular name. If it not exists, a new [ObjectType] is created and registered with the + * [TypeManager]. */ @JvmOverloads -fun LanguageProvider.parseType(name: CharSequence, resolveAlias: Boolean = false): Type { - return if (this is ContextProvider) { - TypeParser.createFrom(name.toString(), language, resolveAlias, this.ctx) - } else { - throw TranslationException("Cannot parse type without translation context") +fun LanguageProvider.objectType(name: CharSequence, generics: List = listOf()): Type { + // First, we check, whether this is a built-in type, to avoid necessary allocations of simple + // types + val builtIn = language?.getSimpleTypeOf(name.toString()) + if (builtIn != null) { + return builtIn } + + // Otherwise, we need to create a new type and register it at the type manager + val c = + (this as? ContextProvider)?.ctx + ?: throw TranslationException( + "Could not create type: translation context not available" + ) + val type = ObjectType(name, generics, false, language) + + return c.typeManager.registerType(type) +} + +/** + * This function constructs a new primitive [Type]. Primitive or built-in types are defined in + * [Language.builtInTypes]. This function will look up the type by its name, if it fails to find an + * appropriate build-in type, a [TranslationException] is thrown. Therefore, this function should + * primarily be called by language frontends if they are sure that this type is a built-in type, + * e.g., when constructing literals. It can be useful, if frontends want to check, whether all + * literal types are correctly registered as built-in types. + * + * If the frontend is not sure, what kind of type it is, it should call [objectType], which also + * does a check, whether it is a known built-in type. + */ +fun LanguageProvider.primitiveType(name: CharSequence): Type { + return language?.getSimpleTypeOf(name.toString()) + ?: throw TranslationException( + "Cannot find primitive type $name in language ${language?.name}. This is either an error in the language frontend or the language definition is missing a type definition." + ) } /** Returns a new [Name] based on the [localName] and the current namespace as parent. */ diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/builder/Fluent.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/builder/Fluent.kt index f3464f42bb..a65c38375d 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/builder/Fluent.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/builder/Fluent.kt @@ -123,7 +123,7 @@ context(DeclarationHolder) fun LanguageFrontend<*, *>.field( name: CharSequence, - type: Type = newUnknownType(), + type: Type = unknownType(), init: FieldDeclaration.() -> Unit ): FieldDeclaration { val node = newFieldDeclaration(name) @@ -145,7 +145,7 @@ context(DeclarationHolder) fun LanguageFrontend<*, *>.function( name: CharSequence, - returnType: Type = newUnknownType(), + returnType: Type = unknownType(), returnTypes: List? = null, init: (FunctionDeclaration.() -> Unit)? = null ): FunctionDeclaration { @@ -175,7 +175,7 @@ context(RecordDeclaration) fun LanguageFrontend<*, *>.method( name: CharSequence, - returnType: Type = newUnknownType(), + returnType: Type = unknownType(), init: MethodDeclaration.() -> Unit ): MethodDeclaration { val node = newMethodDeclaration(name) @@ -242,7 +242,7 @@ context(FunctionDeclaration) fun LanguageFrontend<*, *>.param( name: CharSequence, - type: Type = newUnknownType(), + type: Type = unknownType(), init: (ParamVariableDeclaration.() -> Unit)? = null ): ParamVariableDeclaration { val node = @@ -298,7 +298,7 @@ context(DeclarationStatement) fun LanguageFrontend<*, *>.variable( name: String, - type: Type = newUnknownType(), + type: Type = unknownType(), init: (VariableDeclaration.() -> Unit)? = null ): VariableDeclaration { val node = newVariableDeclaration(name, type) @@ -435,12 +435,12 @@ fun LanguageFrontend<*, *>.new(init: (NewExpression.() -> Unit)? = null): NewExp return node } -fun LanguageFrontend<*, *>.memberOrRef(name: Name, type: Type = newUnknownType()): Expression { +fun LanguageFrontend<*, *>.memberOrRef(name: Name, type: Type = unknownType()): Expression { val node = if (name.parent != null) { newMemberExpression(name.localName, memberOrRef(name.parent)) } else { - newDeclaredReferenceExpression(name.localName, parseType(name.localName)) + newDeclaredReferenceExpression(name.localName, objectType(name.localName)) } node.type = type @@ -717,7 +717,7 @@ fun LanguageFrontend<*, *>.default(): DefaultStatement { */ context(Holder) -fun LanguageFrontend<*, *>.literal(value: N, type: Type = newUnknownType()): Literal { +fun LanguageFrontend<*, *>.literal(value: N, type: Type = unknownType()): Literal { val node = newLiteral(value, type) // Only add this to an argument holder if the nearest holder is an argument holder @@ -738,7 +738,7 @@ context(Holder) fun LanguageFrontend<*, *>.ref( name: CharSequence, - type: Type = newUnknownType(), + type: Type = unknownType(), init: (DeclaredReferenceExpression.() -> Unit)? = null ): DeclaredReferenceExpression { val node = newDeclaredReferenceExpression(name) @@ -772,13 +772,13 @@ fun LanguageFrontend<*, *>.member( val parsedName = parseName(name) val type = if (parsedName.parent != null) { - newUnknownType() + unknownType() } else { var scope = ((this@Holder) as? ScopeProvider)?.scope while (scope != null && scope !is RecordScope) { scope = scope.parent } - val scopeType = scope?.name?.let { t(it) } ?: newUnknownType() + val scopeType = scope?.name?.let { t(it) } ?: unknownType() scopeType } val memberBase = base ?: memberOrRef(parsedName.parent ?: parseName("this"), type) @@ -1041,7 +1041,7 @@ infix fun Expression.assign(rhs: Expression): BinaryOperator { /** Creates a new [Type] with the given [name] in the Fluent Node DSL. */ fun LanguageFrontend<*, *>.t(name: CharSequence, init: (Type.() -> Unit)? = null): Type { - val type = parseType(name) + val type = objectType(name) if (init != null) { init(type) } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/ConstructorDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/ConstructorDeclaration.kt index 98ce6fe469..e870063f57 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/ConstructorDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/ConstructorDeclaration.kt @@ -25,8 +25,6 @@ */ package de.fraunhofer.aisec.cpg.graph.declarations -import de.fraunhofer.aisec.cpg.graph.parseType - /** * The declaration of a constructor within a [RecordDeclaration]. Is it essentially a special case * of a [MethodDeclaration]. @@ -39,7 +37,7 @@ class ConstructorDeclaration : MethodDeclaration() { super.recordDeclaration = recordDeclaration if (recordDeclaration != null) { // constructors always have implicitly the return type of their class - returnTypes = listOf(parseType(recordDeclaration.name)) + returnTypes = listOf(recordDeclaration.toType()) } } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/FunctionDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/FunctionDeclaration.kt index fa5ab715b2..b2adfcd3c4 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/FunctionDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/FunctionDeclaration.kt @@ -110,7 +110,7 @@ open class FunctionDeclaration : ValueDeclaration(), DeclarationHolder { targetFunctionDeclaration.signatureTypes == signatureTypes } - fun hasSignature(targetSignature: List): Boolean { + fun hasSignature(targetSignature: List): Boolean { val signature = parameters .stream() @@ -195,7 +195,7 @@ open class FunctionDeclaration : ValueDeclaration(), DeclarationHolder { if (paramVariableDeclaration.default != null) { signature.add(paramVariableDeclaration.type) } else { - signature.add(newUnknownType()) + signature.add(unknownType()) } } return signature diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/RecordDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/RecordDeclaration.kt index 839b75cd91..f9bf682090 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/RecordDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/RecordDeclaration.kt @@ -25,13 +25,10 @@ */ package de.fraunhofer.aisec.cpg.graph.declarations -import de.fraunhofer.aisec.cpg.graph.AST -import de.fraunhofer.aisec.cpg.graph.DeclarationHolder -import de.fraunhofer.aisec.cpg.graph.StatementHolder +import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge.Companion.propertyEqualsList import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeDelegate -import de.fraunhofer.aisec.cpg.graph.parseType import de.fraunhofer.aisec.cpg.graph.statements.Statement import de.fraunhofer.aisec.cpg.graph.types.ObjectType import de.fraunhofer.aisec.cpg.graph.types.Type @@ -216,7 +213,7 @@ class RecordDeclaration : Declaration(), DeclarationHolder, StatementHolder { * @return the type */ fun toType(): Type { - val type = parseType(name) + val type = objectType(name) if (type is ObjectType) { // as a shortcut, directly set the record declaration. This will be otherwise done // later by a pass, but for some frontends we need this immediately, so we set diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TypedefDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TypedefDeclaration.kt index a591f7810d..f9e0f711ee 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TypedefDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TypedefDeclaration.kt @@ -47,6 +47,9 @@ class TypedefDeclaration : Declaration() { override fun hashCode() = Objects.hash(super.hashCode(), type, alias) override fun toString(): String { - return ToStringBuilder(this).append("type", type).append("alias", alias).toString() + return ToStringBuilder(this, TO_STRING_STYLE) + .append("type", type) + .append("alias", alias) + .toString() } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/ValueDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/ValueDeclaration.kt index ab7b1106f6..558f7e9a5f 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/ValueDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/ValueDeclaration.kt @@ -64,7 +64,7 @@ abstract class ValueDeclaration : Declaration(), HasType { ?.typeCache ?.computeIfAbsent(this) { mutableListOf() } ?.firstOrNull() - ?: newUnknownType() + ?: unknownType() } return result } @@ -127,7 +127,7 @@ abstract class ValueDeclaration : Declaration(), HasType { override val propagationType: Type get() { return if (type is ReferenceType) { - (type as ReferenceType?)?.elementType ?: newUnknownType() + (type as ReferenceType?)?.elementType ?: unknownType() } else type } @@ -226,17 +226,6 @@ abstract class ValueDeclaration : Declaration(), HasType { .forEach { l: HasType.TypeListener -> l.possibleSubTypesChanged(this, root) } } - override fun registerTypeListener(listener: HasType.TypeListener) { - val root = mutableListOf(this) - typeListeners.add(listener) - listener.typeChanged(this, root, type) - listener.possibleSubTypesChanged(this, root) - } - - override fun unregisterTypeListener(listener: HasType.TypeListener) { - typeListeners.remove(listener) - } - override fun refreshType() { val root = mutableListOf(this) for (l in typeListeners) { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/BinaryOperator.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/BinaryOperator.kt index c3185f0954..ab716bcbb7 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/BinaryOperator.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/BinaryOperator.kt @@ -152,7 +152,7 @@ class BinaryOperator : // function pointer call. setType(src.propagationType, root) } else { - val resultingType = language?.propagateTypeOfBinaryOperation(this) ?: newUnknownType() + val resultingType = language?.propagateTypeOfBinaryOperation(this) ?: unknownType() if (resultingType !is UnknownType) { setType(resultingType, root) } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.kt index f480bd8d4b..83d77fab03 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.kt @@ -286,7 +286,7 @@ open class CallExpression : Expression(), HasType.TypeListener, SecondaryTypeEdg } null } - val alternative = if (types.isNotEmpty()) types[0] else newUnknownType() + val alternative = if (types.isNotEmpty()) types[0] else unknownType() val commonType = getCommonType(types).orElse(alternative) val subTypes: MutableList = ArrayList(possibleSubTypes) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CastExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CastExpression.kt index 30024d3bd3..e2073c202d 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CastExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CastExpression.kt @@ -34,7 +34,7 @@ import org.slf4j.LoggerFactory class CastExpression : Expression(), HasType.TypeListener { @AST var expression: Expression = ProblemExpression("could not parse inner expression") - var castType: Type = newUnknownType() + var castType: Type = unknownType() set(value) { field = value type = value diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConditionalExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConditionalExpression.kt index e53e5539aa..5dd72da5e6 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConditionalExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConditionalExpression.kt @@ -67,7 +67,7 @@ class ConditionalExpression : Expression(), HasType.TypeListener, ArgumentHolder val subTypes: MutableList = ArrayList(possibleSubTypes) subTypes.remove(oldType) subTypes.addAll(types) - val alternative = if (types.isNotEmpty()) types[0] else newUnknownType() + val alternative = if (types.isNotEmpty()) types[0] else unknownType() setType(getCommonType(types).orElse(alternative), root) setPossibleSubTypes(subTypes, root) if (previous != type) { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConstructExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConstructExpression.kt index 421b0730da..47c9884711 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConstructExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConstructExpression.kt @@ -26,11 +26,8 @@ package de.fraunhofer.aisec.cpg.graph.statements.expressions import de.fraunhofer.aisec.cpg.PopulatedByPass -import de.fraunhofer.aisec.cpg.graph.AST -import de.fraunhofer.aisec.cpg.graph.HasType +import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.* -import de.fraunhofer.aisec.cpg.graph.isTypeSystemActive -import de.fraunhofer.aisec.cpg.graph.parseType import de.fraunhofer.aisec.cpg.graph.types.FunctionType import de.fraunhofer.aisec.cpg.graph.types.Type import de.fraunhofer.aisec.cpg.graph.types.UnknownType @@ -80,7 +77,7 @@ class ConstructExpression : CallExpression(), HasType.TypeListener { set(value) { field = value if (value != null && this.type is UnknownType) { - type = parseType(value.name) + type = objectType(value.name) } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/Expression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/Expression.kt index 28f7c18f49..a690bf68fc 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/Expression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/Expression.kt @@ -49,7 +49,7 @@ import org.neo4j.ogm.annotation.Transient */ abstract class Expression : Statement(), HasType { - @Relationship("TYPE") private var _type: Type = newUnknownType() + @Relationship("TYPE") private var _type: Type = unknownType() /** The type of the value after evaluation. */ override var type: Type @@ -62,7 +62,7 @@ abstract class Expression : Statement(), HasType { ?.typeCache ?.computeIfAbsent(this) { mutableListOf() } ?.firstOrNull() - ?: newUnknownType() + ?: unknownType() } return result } @@ -88,7 +88,7 @@ abstract class Expression : Statement(), HasType { override val propagationType: Type get() { return if (type is ReferenceType) { - (type as ReferenceType?)?.elementType ?: newUnknownType() + (type as ReferenceType?)?.elementType ?: unknownType() } else type } @@ -212,17 +212,6 @@ abstract class Expression : Statement(), HasType { .forEach { l: HasType.TypeListener -> l.possibleSubTypesChanged(this, root) } } - override fun registerTypeListener(listener: HasType.TypeListener) { - val root = mutableListOf(this) - typeListeners.add(listener) - listener.typeChanged(this, root, type) - listener.possibleSubTypesChanged(this, root) - } - - override fun unregisterTypeListener(listener: HasType.TypeListener) { - typeListeners.remove(listener) - } - override fun refreshType() { val root = mutableListOf(this) for (l in typeListeners) { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/InitializerListExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/InitializerListExpression.kt index c7c02ddd28..69f7858c1c 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/InitializerListExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/InitializerListExpression.kt @@ -70,7 +70,7 @@ class InitializerListExpression : Expression(), HasType.TypeListener { if (initializers.contains(src)) { val types = initializers.map { registerType(it.type.reference(PointerOrigin.ARRAY)) }.toSet() - val alternative = if (types.isNotEmpty()) types.iterator().next() else newUnknownType() + val alternative = if (types.isNotEmpty()) types.iterator().next() else unknownType() newType = getCommonType(types).orElse(alternative) subTypes = ArrayList(possibleSubTypes) subTypes.remove(oldType) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/FunctionType.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/FunctionType.kt index 7f10a8172c..0dda94a841 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/FunctionType.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/FunctionType.kt @@ -27,8 +27,8 @@ package de.fraunhofer.aisec.cpg.graph.types import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration -import de.fraunhofer.aisec.cpg.graph.newUnknownType import de.fraunhofer.aisec.cpg.graph.registerType +import de.fraunhofer.aisec.cpg.graph.unknownType /** * A type representing a function. It contains a list of parameters and one or more return types. @@ -51,12 +51,12 @@ constructor( return FunctionPointerType( parameters.toList(), language, - returnTypes.firstOrNull() ?: newUnknownType(), + returnTypes.firstOrNull() ?: unknownType(), ) } override fun dereference(): Type { - return newUnknownType() + return unknownType() } override fun duplicate(): Type { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/HasType.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/HasType.kt index b8c3081e9c..edf680fc7c 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/HasType.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/HasType.kt @@ -25,6 +25,7 @@ */ package de.fraunhofer.aisec.cpg.graph +import de.fraunhofer.aisec.cpg.TypeManager import de.fraunhofer.aisec.cpg.frontends.TranslationException import de.fraunhofer.aisec.cpg.graph.types.Type import java.util.Optional @@ -69,11 +70,18 @@ interface HasType : ContextProvider { */ fun setPossibleSubTypes(possibleSubTypes: List, root: MutableList) - fun registerTypeListener(listener: TypeListener) + val typeListeners: MutableSet - fun unregisterTypeListener(listener: TypeListener) + fun registerTypeListener(listener: TypeListener) { + val root = mutableListOf(this) + typeListeners.add(listener) + listener.typeChanged(this, root, type) + listener.possibleSubTypesChanged(this, root) + } - val typeListeners: Set + fun unregisterTypeListener(listener: TypeListener) { + typeListeners.remove(listener) + } fun refreshType() @@ -105,10 +113,10 @@ interface HasType : ContextProvider { val Node.isTypeSystemActive: Boolean get() { - return TypeManager.isTypeSystemActive() + return TypeManager.isTypeSystemActive } -fun Node.isSupertypeOf(superType: Type, subType: Type?): Boolean { +fun Node.isSupertypeOf(superType: Type, subType: Type): Boolean { val c = ctx ?: throw TranslationException("context not available") return c.typeManager.isSupertypeOf(superType, subType, this) } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/ProblemType.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/ProblemType.kt new file mode 100644 index 0000000000..7ca10808cc --- /dev/null +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/ProblemType.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +package de.fraunhofer.aisec.cpg.graph.types + +class ProblemType : Type() { + override fun reference(pointer: PointerType.PointerOrigin?): Type { + return this + } + + override fun dereference(): Type { + return this + } + + override fun duplicate(): Type { + return this + } +} diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/ReferenceType.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/ReferenceType.kt index d424518375..7c46b9ffbe 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/ReferenceType.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/ReferenceType.kt @@ -25,8 +25,8 @@ */ package de.fraunhofer.aisec.cpg.graph.types -import de.fraunhofer.aisec.cpg.graph.newUnknownType import de.fraunhofer.aisec.cpg.graph.types.PointerType.PointerOrigin +import de.fraunhofer.aisec.cpg.graph.unknownType import java.util.* import org.apache.commons.lang3.builder.ToStringBuilder @@ -37,7 +37,7 @@ import org.apache.commons.lang3.builder.ToStringBuilder * called. */ class ReferenceType : Type, SecondOrderType { - override var elementType: Type = newUnknownType() + override var elementType: Type = unknownType() constructor() : super() diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/Type.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/Type.kt index f293c99218..a43599c030 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/Type.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/Type.kt @@ -26,6 +26,7 @@ package de.fraunhofer.aisec.cpg.graph.types import de.fraunhofer.aisec.cpg.PopulatedByPass +import de.fraunhofer.aisec.cpg.TypeManager import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.graph.Name import de.fraunhofer.aisec.cpg.graph.Node @@ -93,9 +94,17 @@ abstract class Type : Node { } /** + * Creates a new [Type] based on a reference of this type. The main usage is to create pointer + * and array types. This function does NOT invoke [TypeManager.registerType] and should only be + * used internally. For the public API, the extension functions, such as [Type.array] should be + * used instead. + * * @param pointer Reason for the reference (array of pointer) * @return Returns a reference to the current Type. E.g. when creating a pointer to an existing * ObjectType + * + * TODO(oxisto) Ideally, we would make this function "internal", but there is a bug in the Go + * frontend, so that we still need this function :( */ abstract fun reference(pointer: PointerOrigin?): Type diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/UnknownType.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/UnknownType.kt index 84a5947453..ea8925e75f 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/UnknownType.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/UnknownType.kt @@ -36,20 +36,11 @@ import java.util.concurrent.ConcurrentHashMap * used. E.g.: This occurs when the type is inferred by the compiler automatically when using * keywords such as auto in cpp */ -class UnknownType : Type { - private constructor() : super() { +class UnknownType private constructor() : Type() { + init { name = Name(UNKNOWN_TYPE_STRING, null, language) } - /** - * This is only intended to be used by [TypeParser] for edge cases like distinct unknown types, - * such as "UNKNOWN1", thus the package-private visibility. Other users should see - * [getUnknownType] instead - * - * @param typeName The name of this unknown type, usually a variation of UNKNOWN - */ - internal constructor(typeName: String?) : super(typeName) - /** * @return Same UnknownType, as it is makes no sense to obtain a pointer/reference to an * UnknownType diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/WrapState.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/WrapState.kt index 70dd64dad3..c84ee87448 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/WrapState.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/WrapState.kt @@ -31,14 +31,14 @@ import de.fraunhofer.aisec.cpg.graph.types.PointerType.PointerOrigin class WrapState { @JvmField var depth = 0 var isReference = false - @JvmField var pointerOrigins: Array + @JvmField var pointerOrigins: Array @JvmField var referenceType: ReferenceType? = null init { pointerOrigins = arrayOf(PointerOrigin.ARRAY) } - fun setPointerOrigin(pointerOrigin: Array) { + fun setPointerOrigin(pointerOrigin: Array) { pointerOrigins = pointerOrigin } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CXXCallResolverHelper.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CXXCallResolverHelper.kt index e58db03f34..24ba098441 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CXXCallResolverHelper.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CXXCallResolverHelper.kt @@ -26,6 +26,7 @@ package de.fraunhofer.aisec.cpg.passes import de.fraunhofer.aisec.cpg.TranslationContext +import de.fraunhofer.aisec.cpg.TypeManager import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.* @@ -43,14 +44,14 @@ import java.util.regex.Pattern * signature by means of casting */ fun compatibleSignatures( - callSignature: List, + callSignature: List, functionSignature: List, ctx: TranslationContext ): Boolean { return if (callSignature.size == functionSignature.size) { for (i in callSignature.indices) { if ( - callSignature[i]?.isPrimitive != functionSignature[i].isPrimitive && + callSignature[i].isPrimitive != functionSignature[i].isPrimitive && !ctx.typeManager.isSupertypeOf( functionSignature[i], callSignature[i], @@ -75,7 +76,7 @@ fun compatibleSignatures( fun getCallSignatureWithDefaults( call: CallExpression, functionDeclaration: FunctionDeclaration -): List { +): List { val callSignature = mutableListOf(*call.signature.toTypedArray()) if (call.signature.size < functionDeclaration.parameters.size) { callSignature.addAll( diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CallResolver.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CallResolver.kt index ae2f943134..70b17a789b 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CallResolver.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CallResolver.kt @@ -98,7 +98,7 @@ open class CallResolver(ctx: TranslationContext) : SymbolResolverPass(ctx) { protected fun registerMethods(currentClass: RecordDeclaration?, currentNode: Node?) { if (currentNode is MethodDeclaration && currentClass != null) { - containingType[currentNode] = currentNode.parseType(currentClass.name) + containingType[currentNode] = currentNode.objectType(currentClass.name) } } @@ -559,7 +559,7 @@ open class CallResolver(ctx: TranslationContext) : SymbolResolverPass(ctx) { } protected fun getOverridingCandidates( - possibleSubTypes: Set, + possibleSubTypes: Set, declaration: FunctionDeclaration ): Set { return declaration.overriddenBy @@ -573,7 +573,7 @@ open class CallResolver(ctx: TranslationContext) : SymbolResolverPass(ctx) { * @return ConstructorDeclaration that matches the provided signature */ protected fun getConstructorDeclarationDirectMatch( - signature: List, + signature: List, recordDeclaration: RecordDeclaration ): ConstructorDeclaration? { for (constructor in recordDeclaration.constructors) { @@ -595,7 +595,7 @@ open class CallResolver(ctx: TranslationContext) : SymbolResolverPass(ctx) { constructExpression: ConstructExpression, recordDeclaration: RecordDeclaration ): ConstructorDeclaration { - val signature: List = constructExpression.signature + val signature = constructExpression.signature var constructorCandidate = getConstructorDeclarationDirectMatch(signature, recordDeclaration) if (constructorCandidate == null && constructExpression.language is HasDefaultArguments) { @@ -617,7 +617,7 @@ open class CallResolver(ctx: TranslationContext) : SymbolResolverPass(ctx) { } protected fun getConstructorDeclarationForExplicitInvocation( - signature: List, + signature: List, recordDeclaration: RecordDeclaration ): ConstructorDeclaration { return recordDeclaration.constructors.firstOrNull { it.hasSignature(signature) } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/Pass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/Pass.kt index 2ad6a0ac13..2384b3b182 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/Pass.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/Pass.kt @@ -25,10 +25,7 @@ */ package de.fraunhofer.aisec.cpg.passes -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration -import de.fraunhofer.aisec.cpg.TranslationContext -import de.fraunhofer.aisec.cpg.TranslationResult +import de.fraunhofer.aisec.cpg.* import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend import de.fraunhofer.aisec.cpg.frontends.TranslationException diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolverPass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolverPass.kt index 4ca2deb0ee..fbd440fab5 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolverPass.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolverPass.kt @@ -53,7 +53,7 @@ abstract class SymbolResolverPass(ctx: TranslationContext) : ComponentPass(ctx) protected fun findEnums(node: Node?) { if (node is EnumDeclaration) { // TODO: Use the name instead of the type. - val type = node.parseType(node.name) + val type = node.objectType(node.name) enumMap.putIfAbsent(type, node) } } @@ -69,7 +69,7 @@ abstract class SymbolResolverPass(ctx: TranslationContext) : ComponentPass(ctx) protected fun FunctionDeclaration.matches( name: Name, returnType: Type, - signature: List + signature: List ): Boolean { val thisReturnType = if (this.returnTypes.isEmpty()) { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/VariableUsageResolver.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/VariableUsageResolver.kt index 20249d3e8b..f59a594076 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/VariableUsageResolver.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/VariableUsageResolver.kt @@ -142,7 +142,7 @@ open class VariableUsageResolver(ctx: TranslationContext) : SymbolResolverPass(c var recordDeclType: Type? = null if (currentClass != null) { - recordDeclType = currentClass.parseType(currentClass.name) + recordDeclType = currentClass.toType() } if (current.type is FunctionPointerType && refersTo == null) { refersTo = resolveFunctionPtr(current) @@ -198,9 +198,9 @@ open class VariableUsageResolver(ctx: TranslationContext) : SymbolResolverPass(c return if (language != null && language.namespaceDelimiter.isNotEmpty()) { val parentName = (current.name.parent ?: current.name).toString() - current.parseType(parentName) + current.objectType(parentName) } else { - current.newUnknownType() + current.unknownType() } } @@ -234,7 +234,7 @@ open class VariableUsageResolver(ctx: TranslationContext) : SymbolResolverPass(c "Could not find referring super type ${superType.typeName} for ${curClass.name} in the record map. Will set the super type to java.lang.Object" ) // TODO: Should be more generic! - base.type = current.parseType(Any::class.java.name) + base.type = current.objectType(Any::class.java.name) } else { // We need to connect this super reference to the receiver of this // method @@ -254,7 +254,7 @@ open class VariableUsageResolver(ctx: TranslationContext) : SymbolResolverPass(c } else { // no explicit super type -> java.lang.Object // TODO: Should be more generic - val objectType = current.parseType(Any::class.java.name) + val objectType = current.objectType(Any::class.java.name) base.type = objectType } } else { @@ -269,13 +269,13 @@ open class VariableUsageResolver(ctx: TranslationContext) : SymbolResolverPass(c return } } else if (baseTarget is RecordDeclaration) { - var baseType = baseTarget.parseType(baseTarget.name) + var baseType = baseTarget.toType() if (baseType.name !in recordMap) { val containingT = baseType val fqnResolvedType = recordMap.keys.firstOrNull { it.lastPartsMatch(containingT.name) } if (fqnResolvedType != null) { - baseType = baseTarget.parseType(fqnResolvedType) + baseType = baseTarget.objectType(fqnResolvedType) } } current.refersTo = resolveMember(baseType, current) @@ -286,7 +286,7 @@ open class VariableUsageResolver(ctx: TranslationContext) : SymbolResolverPass(c if (baseType.name !in recordMap) { val fqnResolvedType = recordMap.keys.firstOrNull { it.lastPartsMatch(baseType.name) } if (fqnResolvedType != null) { - baseType = current.base.parseType(fqnResolvedType) + baseType = current.base.objectType(fqnResolvedType) } } current.refersTo = resolveMember(baseType, current) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/Inference.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/Inference.kt index d532673e72..76bcd8c09a 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/Inference.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/Inference.kt @@ -234,7 +234,7 @@ class Inference(val start: Node, override val ctx: TranslationContext) : name: String, ): TypeParamDeclaration { val parameterizedType = ParameterizedType(name, language) - typeManager.addTypeParameter(start as? FunctionTemplateDeclaration, parameterizedType) + typeManager.addTypeParameter(start as FunctionTemplateDeclaration, parameterizedType) val decl = newTypeParamDeclaration(name, name) decl.type = parameterizedType diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/GraphExamples.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/GraphExamples.kt index d1eb129f32..ee669d9c42 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/GraphExamples.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/GraphExamples.kt @@ -28,9 +28,9 @@ package de.fraunhofer.aisec.cpg import de.fraunhofer.aisec.cpg.frontends.StructTestLanguage import de.fraunhofer.aisec.cpg.frontends.TestLanguage import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend -import de.fraunhofer.aisec.cpg.graph.TypeManager import de.fraunhofer.aisec.cpg.graph.builder.* import de.fraunhofer.aisec.cpg.graph.newVariableDeclaration +import de.fraunhofer.aisec.cpg.graph.types.PointerType.PointerOrigin.POINTER import de.fraunhofer.aisec.cpg.sarif.PhysicalLocation import de.fraunhofer.aisec.cpg.sarif.Region import java.net.URI @@ -60,7 +60,7 @@ class GraphExamples { // The main method function("main", t("int")) { body { - declare { variable("node", t("T*")) } + declare { variable("node", t("T").reference(POINTER)) } member("value", ref("node"), "->") assign literal(42, t("int")) member("next", ref("node"), "->") assign ref("node") memberCall( diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/TypeTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/TypeTest.kt new file mode 100644 index 0000000000..fcc512f89e --- /dev/null +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/TypeTest.kt @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2023, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +package de.fraunhofer.aisec.cpg.graph + +import de.fraunhofer.aisec.cpg.assertLocalName +import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend +import de.fraunhofer.aisec.cpg.frontends.TranslationException +import kotlin.test.Test +import org.junit.jupiter.api.assertThrows + +class TypeTest { + @Test + fun testType() { + with(TestLanguageFrontend()) { + val tu = newTranslationUnitDeclaration("file.extension") + this.scopeManager.resetToGlobal(tu) + + val func = newFunctionDeclaration("main") + assertLocalName("main", func) + + val simpleType = objectType("SomeObject") + assertLocalName("SomeObject", simpleType) + } + } + + @Test + fun testPrimitive() { + with(TestLanguageFrontend()) { + val boolean = primitiveType("boolean") + assertLocalName("boolean", boolean) + + assertThrows { primitiveType("BOOLEAN") } + } + } +} diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/AssignExpressionTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/AssignExpressionTest.kt index c9d96d6df7..9d59e76ad2 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/AssignExpressionTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/AssignExpressionTest.kt @@ -50,7 +50,7 @@ class AssignExpressionTest { assertContains(refB.typeListeners, stmt) // Suddenly, we now we know the type of b. - refB.type = parseType("MyClass") + refB.type = objectType("MyClass") // It should now propagate to a assertLocalName("MyClass", refA.type) @@ -68,7 +68,7 @@ class AssignExpressionTest { val func = function( "func", - returnTypes = listOf(parseType("MyClass"), parseType("error")) + returnTypes = listOf(objectType("MyClass"), objectType("error")) ) function("main") { val refA = newDeclaredReferenceExpression("a") diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/scopes/ScopeManagerTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/scopes/ScopeManagerTest.kt index 2926bfe247..a3674d7583 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/scopes/ScopeManagerTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/scopes/ScopeManagerTest.kt @@ -25,10 +25,7 @@ */ package de.fraunhofer.aisec.cpg.passes.scopes -import de.fraunhofer.aisec.cpg.BaseTest -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration -import de.fraunhofer.aisec.cpg.TranslationContext +import de.fraunhofer.aisec.cpg.* import de.fraunhofer.aisec.cpg.frontends.TestLanguage import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend import de.fraunhofer.aisec.cpg.graph.* diff --git a/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/frontends/TestLanguage.kt b/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/frontends/TestLanguage.kt index be707d5b73..7ccd49347d 100644 --- a/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/frontends/TestLanguage.kt +++ b/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/frontends/TestLanguage.kt @@ -26,15 +26,12 @@ package de.fraunhofer.aisec.cpg.frontends import de.fraunhofer.aisec.cpg.* +import de.fraunhofer.aisec.cpg.TypeManager import de.fraunhofer.aisec.cpg.graph.Node -import de.fraunhofer.aisec.cpg.graph.TypeManager import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration -import de.fraunhofer.aisec.cpg.graph.newUnknownType import de.fraunhofer.aisec.cpg.graph.statements.expressions.ProblemExpression -import de.fraunhofer.aisec.cpg.graph.types.FloatingPointType -import de.fraunhofer.aisec.cpg.graph.types.IntegerType -import de.fraunhofer.aisec.cpg.graph.types.NumericType -import de.fraunhofer.aisec.cpg.graph.types.Type +import de.fraunhofer.aisec.cpg.graph.types.* +import de.fraunhofer.aisec.cpg.graph.unknownType import de.fraunhofer.aisec.cpg.sarif.PhysicalLocation import java.io.File import java.util.function.Supplier @@ -61,6 +58,7 @@ open class TestLanguage(namespaceDelimiter: String = "::") : Language().configureEach { - kotlinOptions { - freeCompilerArgs = listOf("-Xcontext-receivers") - } -} - dependencies { api(libs.javaparser) diff --git a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CLanguage.kt b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CLanguage.kt index 79c8538a23..c9960715ed 100644 --- a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CLanguage.kt +++ b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CLanguage.kt @@ -32,10 +32,7 @@ import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression -import de.fraunhofer.aisec.cpg.graph.types.FloatingPointType -import de.fraunhofer.aisec.cpg.graph.types.IntegerType -import de.fraunhofer.aisec.cpg.graph.types.NumericType -import de.fraunhofer.aisec.cpg.graph.types.Type +import de.fraunhofer.aisec.cpg.graph.types.* import de.fraunhofer.aisec.cpg.passes.CallResolver import de.fraunhofer.aisec.cpg.passes.resolveWithImplicitCast import java.util.regex.Pattern @@ -66,50 +63,38 @@ open class CLanguage : override val compoundAssignmentOperators = setOf("+=", "-=", "*=", "/=", "%=", "<<=", ">>=", "&=", "|=", "^=") + /** + * The list of built-in types. See https://en.cppreference.com/w/c/language/arithmetic_types for + * a reference. We only list equivalent types here and use the canonical form of integer values. + */ @Transient @JsonIgnore override val builtInTypes: Map = mapOf( - "boolean" to IntegerType("boolean", 1, this, NumericType.Modifier.SIGNED), + // Integer types "char" to IntegerType("char", 8, this, NumericType.Modifier.NOT_APPLICABLE), - "byte" to IntegerType("byte", 8, this, NumericType.Modifier.SIGNED), - "short" to IntegerType("short", 16, this, NumericType.Modifier.SIGNED), - "short int" to IntegerType("short", 16, this, NumericType.Modifier.SIGNED), - "int" to IntegerType("int", 32, this, NumericType.Modifier.SIGNED), - "long" to IntegerType("long", 64, this, NumericType.Modifier.SIGNED), - "long int" to IntegerType("long", 64, this, NumericType.Modifier.SIGNED), - "long long" to IntegerType("long long int", 64, this, NumericType.Modifier.SIGNED), - "long long int" to IntegerType("long long int", 64, this, NumericType.Modifier.SIGNED), "signed char" to IntegerType("signed char", 8, this, NumericType.Modifier.SIGNED), - "signed byte" to IntegerType("byte", 8, this, NumericType.Modifier.SIGNED), - "signed short" to IntegerType("short", 16, this, NumericType.Modifier.SIGNED), - "signed short int" to IntegerType("short", 16, this, NumericType.Modifier.SIGNED), - "signed" to IntegerType("int", 32, this, NumericType.Modifier.SIGNED), - "signed int" to IntegerType("int", 32, this, NumericType.Modifier.SIGNED), - "signed long" to IntegerType("long", 64, this, NumericType.Modifier.SIGNED), - "signed long int" to IntegerType("long", 64, this, NumericType.Modifier.SIGNED), - "signed long long" to - IntegerType("long long int", 64, this, NumericType.Modifier.SIGNED), - "signed long long int" to - IntegerType("long long int", 64, this, NumericType.Modifier.SIGNED), - "float" to FloatingPointType("float", 32, this, NumericType.Modifier.SIGNED), - "double" to FloatingPointType("double", 64, this, NumericType.Modifier.SIGNED), "unsigned char" to IntegerType("unsigned char", 8, this, NumericType.Modifier.UNSIGNED), - "unsigned byte" to IntegerType("unsigned byte", 8, this, NumericType.Modifier.UNSIGNED), - "unsigned short" to - IntegerType("unsigned short", 16, this, NumericType.Modifier.UNSIGNED), + "short int" to IntegerType("short int", 16, this, NumericType.Modifier.SIGNED), "unsigned short int" to - IntegerType("unsigned short", 16, this, NumericType.Modifier.UNSIGNED), - "unsigned" to IntegerType("unsigned int", 32, this, NumericType.Modifier.UNSIGNED), + IntegerType("unsigned short int", 16, this, NumericType.Modifier.UNSIGNED), + "int" to IntegerType("int", 32, this, NumericType.Modifier.SIGNED), "unsigned int" to IntegerType("unsigned int", 32, this, NumericType.Modifier.UNSIGNED), - "unsigned long" to - IntegerType("unsigned long", 64, this, NumericType.Modifier.UNSIGNED), + "long int" to IntegerType("long int", 64, this, NumericType.Modifier.SIGNED), "unsigned long int" to - IntegerType("unsigned long", 64, this, NumericType.Modifier.UNSIGNED), - "unsigned long long" to - IntegerType("unsigned long long int", 64, this, NumericType.Modifier.UNSIGNED), + IntegerType("unsigned long int", 64, this, NumericType.Modifier.UNSIGNED), + "long long int" to IntegerType("long long int", 64, this, NumericType.Modifier.SIGNED), "unsigned long long int" to - IntegerType("unsigned long long int", 64, this, NumericType.Modifier.UNSIGNED) + IntegerType("unsigned long long int", 64, this, NumericType.Modifier.UNSIGNED), + + // Floating-point types + "float" to FloatingPointType("float", 32, this, NumericType.Modifier.SIGNED), + "double" to FloatingPointType("double", 64, this, NumericType.Modifier.SIGNED), + "long double" to + FloatingPointType("long double", 128, this, NumericType.Modifier.SIGNED), + + // Convenience types, defined in + "bool" to IntegerType("bool", 1, this, NumericType.Modifier.SIGNED), ) override fun refineNormalCallResolution( diff --git a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CPPLanguage.kt b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CPPLanguage.kt index 3e1333927a..8c22732914 100644 --- a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CPPLanguage.kt +++ b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CPPLanguage.kt @@ -51,36 +51,44 @@ class CPPLanguage : override val elaboratedTypeSpecifier = listOf("class", "struct", "union", "enum") override val unknownTypeString = listOf("auto") + /** + * The list of built-in types. See https://en.cppreference.com/w/cpp/language/types for a + * reference. We only list equivalent types here and use the canonical form of integer values. + */ @Transient override val builtInTypes = mapOf( - "bool" to BooleanType("bool", language = this), - "char" to IntegerType("char", 8, this, NumericType.Modifier.NOT_APPLICABLE), - "byte" to IntegerType("byte", 8, this, NumericType.Modifier.SIGNED), - "short" to IntegerType("short", 16, this, NumericType.Modifier.SIGNED), + // Integer types + "short int" to IntegerType("short int", 16, this, NumericType.Modifier.SIGNED), + "unsigned short int" to + IntegerType("unsigned short int", 16, this, NumericType.Modifier.UNSIGNED), "int" to IntegerType("int", 32, this, NumericType.Modifier.SIGNED), - "long" to IntegerType("long", 64, this, NumericType.Modifier.SIGNED), + "unsigned int" to IntegerType("unsigned int", 32, this, NumericType.Modifier.UNSIGNED), + "long int" to IntegerType("long int", 64, this, NumericType.Modifier.SIGNED), + "unsigned long int" to + IntegerType("unsigned long int", 64, this, NumericType.Modifier.UNSIGNED), "long long int" to IntegerType("long long int", 64, this, NumericType.Modifier.SIGNED), + "unsigned long long int" to + IntegerType("unsigned long long int", 64, this, NumericType.Modifier.UNSIGNED), + + // Boolean type + "bool" to BooleanType("bool"), + + // Character types "signed char" to IntegerType("signed char", 8, this, NumericType.Modifier.SIGNED), - "signed byte" to IntegerType("byte", 8, this, NumericType.Modifier.SIGNED), - "signed short" to IntegerType("short", 16, this, NumericType.Modifier.SIGNED), - "signed int" to IntegerType("int", 32, this, NumericType.Modifier.SIGNED), - "signed long" to IntegerType("long", 64, this, NumericType.Modifier.SIGNED), - "signed long long int" to - IntegerType("long long int", 64, this, NumericType.Modifier.SIGNED), + "unsigned char" to IntegerType("unsigned char", 8, this, NumericType.Modifier.UNSIGNED), + "char" to IntegerType("char", 8, this, NumericType.Modifier.NOT_APPLICABLE), + "char8_t" to IntegerType("char8_t", 8, this, NumericType.Modifier.NOT_APPLICABLE), + "char16_t" to IntegerType("char16_t", 16, this, NumericType.Modifier.NOT_APPLICABLE), + "char32_t" to IntegerType("char32_t", 32, this, NumericType.Modifier.NOT_APPLICABLE), + + // Floating-point types "float" to FloatingPointType("float", 32, this, NumericType.Modifier.SIGNED), "double" to FloatingPointType("double", 64, this, NumericType.Modifier.SIGNED), - "unsigned char" to IntegerType("unsigned char", 8, this, NumericType.Modifier.UNSIGNED), - "unsigned byte" to IntegerType("unsigned byte", 8, this, NumericType.Modifier.UNSIGNED), - "unsigned short" to - IntegerType("unsigned short", 16, this, NumericType.Modifier.UNSIGNED), - "unsigned int" to IntegerType("unsigned int", 32, this, NumericType.Modifier.UNSIGNED), - "unsigned long" to - IntegerType("unsigned long", 64, this, NumericType.Modifier.UNSIGNED), - "unsigned long long" to - IntegerType("unsigned long long", 64, this, NumericType.Modifier.UNSIGNED), - "unsigned long long int" to - IntegerType("unsigned long long int", 64, this, NumericType.Modifier.UNSIGNED), + "long double" to + FloatingPointType("long double", 128, this, NumericType.Modifier.SIGNED), + + // Some convenience types "std::string" to StringType("std::string", this), ) 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 891382a383..c7c6a93a2f 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 @@ -38,8 +38,8 @@ import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression import de.fraunhofer.aisec.cpg.graph.types.* -import de.fraunhofer.aisec.cpg.graph.types.FunctionType import de.fraunhofer.aisec.cpg.helpers.Benchmark +import de.fraunhofer.aisec.cpg.helpers.Util import de.fraunhofer.aisec.cpg.passes.FunctionPointerCallResolver import de.fraunhofer.aisec.cpg.passes.order.RegisterExtraPass import de.fraunhofer.aisec.cpg.sarif.PhysicalLocation @@ -61,7 +61,10 @@ import org.eclipse.cdt.core.parser.IncludeFileContentProvider import org.eclipse.cdt.core.parser.ScannerInfo import org.eclipse.cdt.internal.core.dom.parser.ASTNode import org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit +import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTLiteralExpression import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTQualifiedName +import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTTemplateId +import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTTypeId import org.eclipse.cdt.internal.core.model.ASTStringUtil import org.eclipse.cdt.internal.core.parser.IMacroDictionary import org.eclipse.cdt.internal.core.parser.scanner.InternalFileContent @@ -385,27 +388,16 @@ class CXXLanguageFrontend(language: Language, ctx: Translat val expression: Expression = when (token.tokenType) { 1 -> // a variable - newDeclaredReferenceExpression(code, newUnknownType(), code) + newDeclaredReferenceExpression(code, unknownType(), code) 2 -> // an integer - newLiteral( - code.toInt(), - (language.getSimpleTypeOf("int") as? ObjectType) ?: parseType("int"), - code - ) + newLiteral(code.toInt(), primitiveType("int"), code) 130 -> // a string newLiteral( if (code.length >= 2) code.substring(1, code.length - 1) else "", - (language.getSimpleTypeOf("char") as? ObjectType)?.reference() - ?: parseType("char"), - code - ) - else -> - newLiteral( - code, - (language.getSimpleTypeOf("char") as? ObjectType)?.reference() - ?: parseType("char"), + primitiveType("char").pointer(), code ) + else -> newLiteral(code, primitiveType("char").pointer(), code) } return newAnnotationMember("", expression, code) } @@ -446,7 +438,7 @@ class CXXLanguageFrontend(language: Language, ctx: Translat val loc: Pair = Pair(location.artifactLocation.uri.path, location.region.endLine) comments[loc]?.let { - // only exact match for now} + // only exact match for now node.comment = it } // TODO: handle orphanComments? i.e. comments which do not correspond to one line @@ -482,20 +474,13 @@ class CXXLanguageFrontend(language: Language, ctx: Translat hint: Declaration? = null ): Type { // Retrieve the "name" of this type, including qualifiers. - // TODO: In the future, we should parse the qualifiers, such as const here, instead of in - // the TypeParser val name = ASTStringUtil.getSignatureString(specifier, null) + var resolveAlias = false + var type = when (specifier) { - is IASTSimpleDeclSpecifier -> { - if (hint is ConstructorDeclaration && hint.name.parent != null) { - parseType(hint.name.parent!!) - } else { - // A primitive type - parseType(name) - } - } + is IASTSimpleDeclSpecifier -> typeOf(specifier, hint) is IASTNamedTypeSpecifier -> { // A reference to an object type. We need to differentiate between two cases: // a) the type name is already an FQN. In this case, we can just parse it as @@ -504,16 +489,17 @@ class CXXLanguageFrontend(language: Language, ctx: Translat // refers to a symbol in our current namespace. This means that we are doing // some resolving in the frontend, which we actually want to avoid since it // has limited view. - // - // Note: we cannot use parseType here, because of typedefs (and templates?) the - // TypeParser still needs to have access directly to the language frontend - // (meh!) - if (specifier.name is CPPASTQualifiedName) { - // Case a: FQN - TypeParser.createFrom(name, true, this) + if ( + specifier.name is CPPASTQualifiedName || specifier.name is CPPASTTemplateId + ) { + // Case a: FQN or template + resolveAlias = true + typeOf(specifier.name) } else { // Case b: Peek into our symbols. This is most likely limited to our current // translation unit + resolveAlias = true + val decl = scopeManager.currentScope?.let { scopeManager.getRecordForName(it, Name(name)) @@ -521,33 +507,134 @@ class CXXLanguageFrontend(language: Language, ctx: Translat // We found a symbol, so we can use its name if (decl != null) { - TypeParser.createFrom(decl.name.toString(), true, this) + objectType(decl.name) } else { + // It could be, that this is a parameterized type + val paramType = + typeManager.searchTemplateScopeForDefinedParameterizedTypes( + scopeManager.currentScope, + specifier.name.toString() + ) // Otherwise, we keep it as a local name and hope for the best - TypeParser.createFrom(name, true, this) + paramType ?: typeOf(specifier.name) } } } is IASTCompositeTypeSpecifier -> { // A class. This actually also declares the class. At the moment, we handle this // in handleSimpleDeclaration, but we might want to move it here - TypeParser.createFrom(name, true, this) + resolveAlias = true + + objectType(specifier.name.toString()) } is IASTElaboratedTypeSpecifier -> { + resolveAlias = true + // A class or struct - TypeParser.createFrom(name, true, this) + objectType(specifier.name.toString()) } else -> { - newUnknownType() + unknownType() } } - type = typeManager.registerType(type) + type = + if (resolveAlias) { + typeManager.registerType(typeManager.resolvePossibleTypedef(type, scopeManager)) + } else { + typeManager.registerType(type) + } type = this.adjustType(declarator, type) return type } + private fun typeOf( + specifier: IASTSimpleDeclSpecifier, + hint: Declaration? = null, + ): Type { + val name = specifier.rawSignature + + return when { + // auto type; we model this as an unknown type. Maybe in the future, we will + // differentiate between unknown types and "auto-deduced" types + specifier.type == IASTSimpleDeclSpecifier.t_auto -> { + unknownType() + } + // void type + specifier.type == IASTSimpleDeclSpecifier.t_void -> { + IncompleteType() + } + // The type of constructor declaration is always the declaration itself + specifier.type == IASTSimpleDeclSpecifier.t_unspecified && + hint is ConstructorDeclaration -> { + hint.name.parent?.let { objectType(it) } ?: unknownType() + } + // C (not C++) allows unspecified types in function declarations, they + // default to int and usually produce a warning + name == "" && language !is CPPLanguage -> { + Util.warnWithFileLocation( + this, + specifier, + log, + "Type specifier missing, defaulting to 'int'" + ) + primitiveType("int") + } + name == "" && language is CPPLanguage -> { + Util.errorWithFileLocation( + this, + specifier, + log, + "C++ does not allow unspecified type specifiers" + ) + newProblemType() + } + // In all other cases, this must be a primitive type, otherwise it's an error + else -> { + // We need to remove qualifiers such as "const" from the name here, because + // we model them as part of the variable declaration and not the type. The tricky + // part is that they can be specified in any order, so we need to split the + // individual pieces, remove qualifiers and put them back together. + var pieces = specifier.rawSignature.split(" ") + pieces = pieces.filter { it != "const" && it != "static" && it != "typedef" } + primitiveType(specifier.canonicalName) + } + } + } + + fun typeOf(name: IASTName, prefix: String? = null): Type { + if (name is CPPASTQualifiedName) { + val last = name.lastName + if (last is CPPASTTemplateId) { + return typeOf(last, name.qualifier.joinToString("::", postfix = "::")) + } + } else if (name is CPPASTTemplateId) { + // Build fqn + val fqn = + if (prefix != null) { + prefix + name.templateName.toString() + } else { + name.templateName.toString() + } + val generics = mutableListOf() + + // Loop through template arguments + for (arg in name.templateArguments) { + if (arg is CPPASTTypeId) { + generics += typeOf(arg) + } else if (arg is CPPASTLiteralExpression) { + // This is most likely a constant in a template class definition, but we need to + // model this somehow, but it seems the old code just ignored this, so we do as + // well! + } + } + + return objectType(fqn, generics) + } + return objectType(name.toString()) + } + /** * This is a little helper function, primarily used by [typeOf]. It's primary purpose is to * "adjust" the [incoming] type based on the [declarator]. This is needed because the type @@ -562,7 +649,7 @@ class CXXLanguageFrontend(language: Language, ctx: Translat type = when (op) { is IASTPointer -> { - type.reference(PointerType.PointerOrigin.POINTER) + type.pointer() } is ICPPASTReferenceOperator -> { ReferenceType(type) @@ -576,7 +663,7 @@ class CXXLanguageFrontend(language: Language, ctx: Translat // Check, if we are an array type if (declarator is IASTArrayDeclarator) { for (mod in declarator.arrayModifiers) { - type = type.reference(PointerType.PointerOrigin.ARRAY) + type = type.array() } } else if (declarator is IASTStandardFunctionDeclarator) { // Loop through the parameters @@ -622,8 +709,22 @@ class CXXLanguageFrontend(language: Language, ctx: Translat type = adjustType(declarator.nestedDeclarator, type) } + type = typeManager.registerType(type) + + // Check for parameterized types + if (type is SecondOrderType) { + val templateType = + typeManager.searchTemplateScopeForDefinedParameterizedTypes( + scopeManager.currentScope, + type.root.name.toString() + ) + if (templateType != null) { + type.root = templateType + } + } + // Make sure, the type manager knows about this type - return typeManager.registerType(type) + return type } companion object { @@ -649,3 +750,50 @@ class CXXLanguageFrontend(language: Language, ctx: Translat } } } + +private val IASTSimpleDeclSpecifier.canonicalName: CharSequence + get() { + var type = this.type + var parts = mutableListOf() + // First, we specify whether it is signed or unsigned. We only need "signed" for chars + if (this.isUnsigned) { + parts += "unsigned" + } else if (this.isSigned && this.type == IASTSimpleDeclSpecifier.t_char) { + parts += "signed" + } + + // Next, we analyze the size (long, long long, ...) + if (this.isShort || this.isLong || this.isLongLong) { + parts += + if (this.isShort) { + "short" + } else if (this.isLong) { + "long" + } else { + "long long" + } + + // Also make this an int, if it is omitted + if (type == IASTSimpleDeclSpecifier.t_unspecified) { + type = IASTSimpleDeclSpecifier.t_int + } + } + + // Last part is the actual type (int, float, ...) + when (type) { + IASTSimpleDeclSpecifier.t_char -> parts += "char" + IASTSimpleDeclSpecifier.t_char16_t -> parts += "char16_t" + IASTSimpleDeclSpecifier.t_char32_t -> parts += "chat32_t" + IASTSimpleDeclSpecifier.t_int -> parts += "int" + IASTSimpleDeclSpecifier.t_float -> parts += "float" + IASTSimpleDeclSpecifier.t_double -> parts += "double" + IASTSimpleDeclSpecifier.t_bool -> parts += "bool" + IASTSimpleDeclSpecifier.t_unspecified -> {} + IASTSimpleDeclSpecifier.t_auto -> parts = mutableListOf("auto") + else -> { + LanguageFrontend.Companion.log.error("Unknown C/C++ simple type: {}", type) + } + } + + return parts.joinToString(" ") + } 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 4f70039e99..88a1faafe9 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 @@ -132,7 +132,7 @@ class DeclarationHandler(lang: CXXLanguageFrontend) : // Retrieve the type. This should parse as a function type, otherwise it is unknown. val type = frontend.typeOf(ctx.declarator, ctx.declSpecifier, declaration) as? FunctionType - declaration.type = type ?: newUnknownType() + declaration.type = type ?: unknownType() declaration.isDefinition = true // We also need to set the return type, based on the function type. @@ -318,12 +318,7 @@ class DeclarationHandler(lang: CXXLanguageFrontend) : ) typeParamDeclaration.type = parameterizedType if (templateParameter.defaultType != null) { - val defaultType = - TypeParser.createFrom( - templateParameter.defaultType.declSpecifier.rawSignature, - false, - frontend - ) + val defaultType = frontend.typeOf(templateParameter.defaultType) typeParamDeclaration.default = defaultType } templateDeclaration.addParameter(typeParamDeclaration) @@ -487,27 +482,31 @@ class DeclarationHandler(lang: CXXLanguageFrontend) : declSpecifier } - // It is important, that we parse the type first, so that the type is known before - // parsing the declaration. - // This allows us to guess cast vs. call expression in - // ExpressionHandler.handleUnaryExpression. - var type = frontend.typeOf(declarator, declSpecifierToUse) + var type: Type + + // If this is a variable declaration with initializer, it is important, that we + // parse the type first, so that the type is known before parsing the declaration. + // This allows us to guess cast vs. call expression in the initializer. + if (declarator !is IASTFunctionDeclarator && declarator.initializer != null) { + // We only need to parse it, but we are not really storing the result. That is + // not ideal, but probably the "best" way. + frontend.typeOf(declarator, declSpecifierToUse) + } if (ctx.isTypedef) { + type = frontend.typeOf(declarator, declSpecifierToUse) + // Handle typedefs. val declaration = handleTypedef(declarator, ctx, type) sequence.addDeclaration(declaration) } else { + // Parse the declaration first, so we can supply the declaration as a hint to + // the typeOf function. val declaration = frontend.declaratorHandler.handle(declarator) as? ValueDeclaration - // We need to reparse the type, if this is a constructor declaration, so that we - // can supply this as a hint to - // the typeOf - if (declaration is ConstructorDeclaration) { - type = frontend.typeOf(declarator, declSpecifierToUse, declaration) - } + 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 @@ -518,7 +517,7 @@ class DeclarationHandler(lang: CXXLanguageFrontend) : } if (declaration != null) { - // We also need to set the return type, based on the declrator type. + // We also need to set the return type, based on the declarator type. declaration.type = type // process attributes @@ -544,7 +543,7 @@ class DeclarationHandler(lang: CXXLanguageFrontend) : frontend, frontend.codeOf(ctx), type, - nameDecl.name.toString() + frontend.typeOf(nameDecl.name) ) // Add the declaration to the current scope @@ -574,7 +573,7 @@ class DeclarationHandler(lang: CXXLanguageFrontend) : ) // In C/C++, default enums are of type int - enumConst.type = parseType("int") + enumConst.type = primitiveType("int") // 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 @@ -615,21 +614,11 @@ class DeclarationHandler(lang: CXXLanguageFrontend) : sequence: DeclarationSequence ) { val templateId = typeSpecifier.name as CPPASTTemplateId - val type = parseType(ctx.rawSignature) val templateParams = mutableListOf() - if (type.root !is ObjectType) { - // we cannot continue in this case - return - } - - val objectType = type.root as ObjectType - objectType.generics = emptyList() - for (templateArgument in templateId.templateArguments) { if (templateArgument is CPPASTTypeId) { - val genericInstantiation = parseType(templateArgument.getRawSignature()) - objectType.addGeneric(genericInstantiation) + val genericInstantiation = frontend.typeOf(templateArgument) templateParams.add( newTypeExpression( genericInstantiation.name.toString(), @@ -641,11 +630,12 @@ class DeclarationHandler(lang: CXXLanguageFrontend) : expression?.let { templateParams.add(it) } } } + for (declarator in ctx.declarators) { val declaration = frontend.declaratorHandler.handle(declarator) as ValueDeclaration // Update Type - declaration.type = type + declaration.type = frontend.typeOf(declarator, typeSpecifier) // Set TemplateParameters into VariableDeclaration if (declaration is VariableDeclaration) { 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 38241c6138..a0cbc1a71e 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 @@ -36,10 +36,8 @@ import de.fraunhofer.aisec.cpg.helpers.Util import java.util.* import java.util.function.Supplier import java.util.regex.Pattern -import java.util.stream.Collectors import org.eclipse.cdt.core.dom.ast.* import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier -import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier.ICPPASTBaseSpecifier import org.eclipse.cdt.core.dom.ast.gnu.cpp.GPPLanguage import org.eclipse.cdt.internal.core.dom.parser.cpp.* @@ -118,7 +116,7 @@ class DeclaratorHandler(lang: CXXLanguageFrontend) : val declaration = newVariableDeclaration( ctx.name.toString(), - newUnknownType(), // Type will be filled out later by + unknownType(), // Type will be filled out later by // handleSimpleDeclaration ctx.rawSignature, implicitInitializerAllowed, @@ -152,7 +150,7 @@ class DeclaratorHandler(lang: CXXLanguageFrontend) : val fieldName = rr[rr.size - 1] newFieldDeclaration( fieldName, - newUnknownType(), + unknownType(), emptyList(), ctx.rawSignature, frontend.locationOf(ctx), @@ -162,7 +160,7 @@ class DeclaratorHandler(lang: CXXLanguageFrontend) : } else { newFieldDeclaration( name, - newUnknownType(), + unknownType(), emptyList(), ctx.rawSignature, frontend.locationOf(ctx), @@ -190,17 +188,29 @@ class DeclaratorHandler(lang: CXXLanguageFrontend) : // Retrieve the AST node for the scope we need to put the function in val holder = scope?.astNode - // Check, if it's a constructor. This is the case if the local names of the function and the - // record declaration match val func = - if (holder is RecordDeclaration && name.localName == holder.name.localName) { - newConstructorDeclaration(name, null, holder, ctx) - } else if (scope?.astNode is NamespaceDeclaration) { + when { + // Check, if it's a constructor. This is the case if the local names of the function + // and the + // record declaration match + holder is RecordDeclaration && name.localName == holder.name.localName -> { + newConstructorDeclaration(name, null, holder, ctx) + } + // It's also a constructor, if the name is in the form A::A, and it has no type + // specifier + name.localName == name.parent.toString() && + ((ctx as? IASTFunctionDefinition)?.declSpecifier as? IASTSimpleDeclSpecifier) + ?.type == IASTSimpleDeclSpecifier.t_unspecified -> { + newConstructorDeclaration(name, null, null, ctx) + } // It could also be a scoped function declaration. - newFunctionDeclaration(name, null, ctx) - } else { + scope?.astNode is NamespaceDeclaration -> { + newFunctionDeclaration(name, null, ctx) + } // Otherwise, it's a method to a known or unknown record - newMethodDeclaration(name, null, false, holder as? RecordDeclaration, ctx) + else -> { + newMethodDeclaration(name, null, false, holder as? RecordDeclaration, ctx) + } } // Also make sure to correctly set the scope of the function, regardless where we are in the @@ -345,7 +355,7 @@ class DeclaratorHandler(lang: CXXLanguageFrontend) : // is appended to the original ones. For coherent graph behaviour, we introduce an implicit // declaration that wraps this list if (ctx.takesVarArgs()) { - val varargs = newParamVariableDeclaration("va_args", newUnknownType(), true, "") + val varargs = newParamVariableDeclaration("va_args", unknownType(), true, "") varargs.isImplicit = true varargs.argumentIndex = i frontend.scopeManager.addDeclaration(varargs) @@ -388,9 +398,7 @@ class DeclaratorHandler(lang: CXXLanguageFrontend) : val recordDeclaration = declaration.recordDeclaration // Create a pointer to the class type (if we know it) - val type = - recordDeclaration?.toType()?.reference(PointerType.PointerOrigin.POINTER) - ?: newUnknownType() + val type = recordDeclaration?.toType()?.pointer() ?: unknownType() // Create the receiver. implicitInitializerAllowed must be false, otherwise fixInitializers // will create another implicit constructexpression for this variable, and we don't want @@ -417,7 +425,7 @@ class DeclaratorHandler(lang: CXXLanguageFrontend) : val recordDeclaration = frontend.scopeManager.currentRecord if (recordDeclaration == null) { // variable - result = newVariableDeclaration(name, newUnknownType(), ctx.rawSignature, true) + result = newVariableDeclaration(name, unknownType(), ctx.rawSignature, true) result.initializer = initializer } else { // field @@ -431,7 +439,7 @@ class DeclaratorHandler(lang: CXXLanguageFrontend) : result = newFieldDeclaration( fieldName, - newUnknownType(), + unknownType(), emptyList(), code, frontend.locationOf(ctx), @@ -461,14 +469,10 @@ class DeclaratorHandler(lang: CXXLanguageFrontend) : ctx.rawSignature, ) - // Handle c++ classes + // Handle C++ classes if (ctx is CPPASTCompositeTypeSpecifier) { recordDeclaration.superClasses = - Arrays.stream(ctx.baseSpecifiers) - .map { b: ICPPASTBaseSpecifier -> - TypeParser.createFrom(b.nameSpecifier.toString(), true, frontend) - } - .collect(Collectors.toList()) + ctx.baseSpecifiers.map { objectType(it.nameSpecifier.toString()) }.toMutableList() } frontend.scopeManager.addDeclaration(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 1a8443622c..8b64171acb 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 @@ -25,6 +25,7 @@ */ package de.fraunhofer.aisec.cpg.frontends.cxx +import de.fraunhofer.aisec.cpg.TypeManager import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.MethodDeclaration @@ -32,7 +33,6 @@ import de.fraunhofer.aisec.cpg.graph.edge.Properties import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge import de.fraunhofer.aisec.cpg.graph.statements.expressions.* import de.fraunhofer.aisec.cpg.graph.types.* -import de.fraunhofer.aisec.cpg.graph.types.PointerType.PointerOrigin import de.fraunhofer.aisec.cpg.helpers.Util import de.fraunhofer.aisec.cpg.passes.CallResolver import java.math.BigInteger @@ -141,19 +141,19 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : // there are a lot of other constants defined for type traits, but they are not really // parsed as type id expressions var operatorCode = "" - var type: Type = newUnknownType() + var type: Type = unknownType() when (ctx.operator) { IASTTypeIdExpression.op_sizeof -> { operatorCode = "sizeof" - type = parseType("std::size_t") + type = objectType("std::size_t") } IASTTypeIdExpression.op_typeid -> { operatorCode = "typeid" - type = parseType("const std::type_info&") + type = objectType("std::type_info").ref() } IASTTypeIdExpression.op_alignof -> { operatorCode = "alignof" - type = parseType("std::size_t") + type = objectType("std::size_t") } IASTTypeIdExpression .op_typeof -> // typeof is not an official c++ keyword - not sure why eclipse @@ -174,14 +174,13 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : } private fun handleNewExpression(ctx: CPPASTNewExpression): Expression { - val name = ctx.typeId.declSpecifier.toString() val code = ctx.rawSignature - val t = TypeParser.createFrom(name, true, frontend) + val t = frontend.typeOf(ctx.typeId) val init = ctx.initializer // we need to check, whether this is an array initialization or a single new expression return if (ctx.isArrayAllocation) { - t.reference(PointerOrigin.ARRAY) + t.array() val arrayMods = (ctx.typeId.abstractDeclarator as IASTArrayDeclarator).arrayModifiers val arrayCreate = newArrayCreationExpression(code) arrayCreate.type = t @@ -200,13 +199,13 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : if (declSpecifier?.name is CPPASTTemplateId) { templateParameters = getTemplateArguments(declSpecifier.name as CPPASTTemplateId) assert(t.root is ObjectType) - val objectType = t.root as? ObjectType + // val objectType = t.root as? ObjectType val generics = templateParameters.filterIsInstance().map { it.type } - objectType?.generics = generics + // objectType?.generics = generics } // new returns a pointer, so we need to reference the type by pointer - val newExpression = newNewExpression(code, t.reference(PointerOrigin.POINTER), ctx) + val newExpression = newNewExpression(code, t.pointer(), ctx) newExpression.templateParameters = templateParameters val initializer: Expression? if (init != null) { @@ -324,7 +323,7 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : return newMemberExpression( name, base, - newUnknownType(), + unknownType(), if (ctx.isPointerDereference) "->" else ".", ctx.rawSignature ) @@ -357,8 +356,9 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : val typeName = (ctx.operand as IASTIdExpression).name.toString() if (frontend.typeManager.typeExists(typeName)) { val cast = newCastExpression(frontend.codeOf(ctx)) - cast.castType = parseType(typeName) - cast.expression = input ?: newProblemExpression("could not parse input") + cast.castType = frontend.typeOf((ctx.operand as IASTIdExpression).name) + // The expression member can only be filled by the parent call + // (handleFunctionCallExpression) cast.location = frontend.locationOf(ctx) return cast } @@ -442,6 +442,9 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : } reference is CastExpression -> { // this really is a cast expression in disguise + reference.expression = + handle(ctx.arguments.first()) + ?: ProblemExpression("could not parse argument for cast") return reference } else -> { @@ -468,11 +471,7 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : // this expression could actually be a field / member expression, but somehow CDT only // recognizes them as a member expression if it has an explicit 'this' // TODO: handle this? convert the declared reference expression into a member expression? - return newDeclaredReferenceExpression( - ctx.name.toString(), - newUnknownType(), - ctx.rawSignature - ) + return newDeclaredReferenceExpression(ctx.name.toString(), unknownType(), ctx.rawSignature) } private fun handleExpressionList(exprList: IASTExpressionList): ExpressionList { @@ -542,18 +541,18 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : return when (ctx.kind) { lk_integer_constant -> handleIntegerLiteral(ctx) lk_float_constant -> handleFloatLiteral(ctx) - lk_char_constant -> newLiteral(ctx.value[1], parseType("char"), ctx.rawSignature) + lk_char_constant -> newLiteral(ctx.value[1], primitiveType("char"), ctx.rawSignature) lk_string_literal -> newLiteral( String(ctx.value.slice(IntRange(1, ctx.value.size - 2)).toCharArray()), - parseType("const char[]"), + primitiveType("char").array(), ctx.rawSignature ) lk_this -> handleThisLiteral(ctx) - lk_true -> newLiteral(true, parseType("bool"), ctx.rawSignature) - lk_false -> newLiteral(false, parseType("bool"), ctx.rawSignature) - lk_nullptr -> newLiteral(null, parseType("nullptr_t"), ctx.rawSignature) - else -> newLiteral(String(ctx.value), newUnknownType(), ctx.rawSignature) + lk_true -> newLiteral(true, primitiveType("bool"), ctx.rawSignature) + lk_false -> newLiteral(false, primitiveType("bool"), ctx.rawSignature) + lk_nullptr -> newLiteral(null, objectType("nullptr_t"), ctx.rawSignature) + else -> newLiteral(String(ctx.value), unknownType(), ctx.rawSignature) } } @@ -590,7 +589,7 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : oneLhs = newDeclaredReferenceExpression( des.name.toString(), - newUnknownType(), + unknownType(), des.getRawSignature() ) } @@ -644,7 +643,7 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : oneLhs = newDeclaredReferenceExpression( des.name.toString(), - newUnknownType(), + unknownType(), des.getRawSignature() ) } @@ -758,15 +757,15 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : // retrieve type based on stored Java number val type = - parseType( + primitiveType( when { // we follow the way clang/llvm handles this and this seems to always // be an unsigned long long, except if it is explicitly specified as ul // differentiate between long and long long - numberValue is BigInteger && "ul" == suffix -> "unsigned long" - numberValue is BigInteger -> "unsigned long long" - numberValue is Long && "ll" == suffix -> "long long" - numberValue is Long -> "long" + numberValue is BigInteger && "ul" == suffix -> "unsigned long int" + numberValue is BigInteger -> "unsigned long long int" + numberValue is Long && "ll" == suffix -> "long long int" + numberValue is Long -> "long int" else -> "int" } ) @@ -783,14 +782,15 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : return try { when (suffix) { - "f" -> newLiteral(strippedValue.toFloat(), parseType("float"), ctx.rawSignature) + "f" -> newLiteral(strippedValue.toFloat(), primitiveType("float"), ctx.rawSignature) "l" -> newLiteral( strippedValue.toBigDecimal(), - parseType("long double"), + primitiveType("long double"), ctx.rawSignature ) - else -> newLiteral(strippedValue.toDouble(), parseType("double"), ctx.rawSignature) + else -> + newLiteral(strippedValue.toDouble(), primitiveType("double"), ctx.rawSignature) } } catch (ex: NumberFormatException) { // It could be that we cannot parse the literal, in this case we return an error @@ -806,9 +806,9 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : private fun handleThisLiteral(ctx: IASTLiteralExpression): DeclaredReferenceExpression { // We should be in a record here. However since we are a fuzzy parser, maybe things went // wrong, so we might have an unknown type. - val recordType = frontend.scopeManager.currentRecord?.toType() ?: newUnknownType() + val recordType = frontend.scopeManager.currentRecord?.toType() ?: unknownType() // We do want to make sure that the type of the expression is at least a pointer. - val pointerType = recordType.reference(PointerOrigin.POINTER) + val pointerType = recordType.pointer() return newDeclaredReferenceExpression("this", pointerType, ctx.rawSignature, ctx) } 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 8d4d8cc561..87d5ce18e9 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 @@ -233,7 +233,7 @@ class StatementHandler(lang: CXXLanguageFrontend) : // 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, parseType("bool"), "true") + val literal: Literal<*> = newLiteral(true, primitiveType("bool"), "true") statement.condition = literal } diff --git a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/FunctionPointerCallResolver.kt b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/FunctionPointerCallResolver.kt index 225840df5f..6f5279192e 100644 --- a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/FunctionPointerCallResolver.kt +++ b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/FunctionPointerCallResolver.kt @@ -32,9 +32,9 @@ import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration +import de.fraunhofer.aisec.cpg.graph.pointer import de.fraunhofer.aisec.cpg.graph.statements.expressions.* import de.fraunhofer.aisec.cpg.graph.types.FunctionPointerType -import de.fraunhofer.aisec.cpg.graph.types.PointerType import de.fraunhofer.aisec.cpg.helpers.IdentitySet import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker.ScopedWalker import de.fraunhofer.aisec.cpg.passes.order.DependsOn @@ -127,8 +127,7 @@ class FunctionPointerCallResolver(ctx: TranslationContext) : ComponentPass(ctx) // Even if it is a function declaration, the dataflow might just come from a // situation where the target of a fptr is passed through via a return value. Keep // searching if return type or signature don't match - val functionPointerType = - currentFunction.type.reference(PointerType.PointerOrigin.POINTER) + val functionPointerType = currentFunction.type.pointer() if ( isLambda && currentFunction.returnTypes.isEmpty() && diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/ScopeManagerCXXTest.kt b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/ScopeManagerCXXTest.kt index d040f58a10..a4155abe96 100644 --- a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/ScopeManagerCXXTest.kt +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/ScopeManagerCXXTest.kt @@ -29,7 +29,6 @@ import de.fraunhofer.aisec.cpg.frontends.TranslationException import de.fraunhofer.aisec.cpg.frontends.cxx.CPPLanguage import de.fraunhofer.aisec.cpg.frontends.cxx.CXXLanguageFrontend import de.fraunhofer.aisec.cpg.graph.* -import de.fraunhofer.aisec.cpg.graph.TypeManager import de.fraunhofer.aisec.cpg.graph.declarations.ConstructorDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.MethodDeclaration import java.io.File diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/types/TypeTests.kt b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/types/TypeTests.kt index 736e749bb9..57efa21b86 100644 --- a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/types/TypeTests.kt +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/types/TypeTests.kt @@ -106,214 +106,6 @@ internal class TypeTests : BaseTest() { assertEquals(functionPointerType, functionPointerType.dereference()) } - @Test - fun createFromCPP() { - var result: Type - - with( - CXXLanguageFrontend( - CPPLanguage(), - TranslationContext( - TranslationConfiguration.builder().build(), - ScopeManager(), - TypeManager() - ) - ) - ) { - // Test 1: Function pointer - var typeString = "void (*single_param)(int)" - result = parseType(typeString) - val parameterList = - listOf(IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.SIGNED)) - var expected: Type = FunctionPointerType(parameterList, CPPLanguage(), IncompleteType()) - assertEquals(expected, result) - - // Test 1.1: interleaved brackets in function pointer - typeString = "void ((*single_param)(int))" - result = parseType(typeString) - assertEquals(result, expected) - - // Test 2: Stronger binding of brackets and pointer - typeString = "char (* const a)[]" - result = parseType(typeString) - expected = - PointerType( - PointerType( - IntegerType("char", 8, CPPLanguage(), NumericType.Modifier.NOT_APPLICABLE), - PointerType.PointerOrigin.ARRAY - ), - PointerType.PointerOrigin.POINTER - ) - assertEquals(expected, result) - - // Test 3: Mutable pointer to a mutable char - typeString = "char *p" - result = parseType(typeString) - expected = - PointerType( - IntegerType("char", 8, CPPLanguage(), NumericType.Modifier.NOT_APPLICABLE), - PointerType.PointerOrigin.POINTER - ) - assertEquals(expected, result) - - // Test 3.1: Different Whitespaces - typeString = "char* p" - result = parseType(typeString) - assertEquals(expected, result) - - // Test 3.2: Different Whitespaces - typeString = "char * p" - result = parseType(typeString) - assertEquals(expected, result) - - // Test 4: Mutable pointer to a constant char - typeString = "const char *p;" - result = parseType(typeString) - expected = - PointerType( - IntegerType("char", 8, CPPLanguage(), NumericType.Modifier.NOT_APPLICABLE), - PointerType.PointerOrigin.POINTER - ) - assertEquals(expected, result) - - // Test 5: Constant pointer to a mutable char - typeString = "char * const p;" - result = parseType(typeString) - expected = - PointerType( - IntegerType("char", 8, CPPLanguage(), NumericType.Modifier.NOT_APPLICABLE), - PointerType.PointerOrigin.POINTER - ) - assertEquals(expected, result) - - // Test 6: Constant pointer to a constant char - typeString = "const char * const p;" - result = parseType(typeString) - expected = - PointerType( - IntegerType("char", 8, CPPLanguage(), NumericType.Modifier.NOT_APPLICABLE), - PointerType.PointerOrigin.POINTER - ) - assertEquals(expected, result) - - // Test 7: Array of const pointer to static const char - typeString = "static const char * const somearray []" - result = parseType(typeString) - expected = - PointerType( - PointerType( - IntegerType("char", 8, CPPLanguage(), NumericType.Modifier.NOT_APPLICABLE), - PointerType.PointerOrigin.POINTER - ), - PointerType.PointerOrigin.ARRAY - ) - assertEquals(expected, result) - - // Test 7.1: Array of array of pointer to static const char - typeString = "static const char * somearray[][]" - result = parseType(typeString) - expected = - PointerType( - PointerType( - PointerType( - IntegerType( - "char", - 8, - CPPLanguage(), - NumericType.Modifier.NOT_APPLICABLE - ), - PointerType.PointerOrigin.POINTER - ), - PointerType.PointerOrigin.ARRAY - ), - PointerType.PointerOrigin.ARRAY - ) - assertEquals(expected, result) - - // Test 8: Generics - typeString = "Array array" - result = parseType(typeString) - var generics = mutableListOf() - generics.add(IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.SIGNED)) - expected = ObjectType("Array", generics, false, CPPLanguage()) - assertEquals(expected, result) - - // Test 9: Compound Primitive Types - typeString = "long long int" - result = parseType(typeString) - expected = IntegerType("long long int", 64, CPPLanguage(), NumericType.Modifier.SIGNED) - assertEquals(expected, result) - - // Test 10: Unsigned/Signed Types - typeString = "unsigned int" - result = parseType(typeString) - expected = IntegerType("unsigned int", 32, CPPLanguage(), NumericType.Modifier.UNSIGNED) - assertEquals(expected, result) - typeString = "signed int" - result = parseType(typeString) - expected = IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.SIGNED) - assertEquals(expected, result) - typeString = "A a" - result = parseType(typeString) - expected = ObjectType("A", ArrayList(), false, CPPLanguage()) - assertEquals(expected, result) - - // Test 11: Unsigned + const + compound primitive Types - expected = - IntegerType( - "unsigned long long int", - 64, - CPPLanguage(), - NumericType.Modifier.UNSIGNED - ) - typeString = "const unsigned long long int a = 1" - result = parseType(typeString) - assertEquals(expected, result) - - typeString = "unsigned const long long int b = 1" - result = parseType(typeString) - assertEquals(expected, result) - - typeString = "unsigned long const long int c = 1" - result = parseType(typeString) - assertEquals(expected, result) - - typeString = "unsigned long long const int d = 1" - result = parseType(typeString) - assertEquals(expected, result) - - typeString = "unsigned long long int const e = 1" - result = parseType(typeString) - assertEquals(expected, result) - - // Test 12: C++ Reference Types - typeString = "const int& ref = a" - result = parseType(typeString) - expected = - ReferenceType(IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.SIGNED)) - assertEquals(expected, result) - - typeString = "int const &ref2 = a" - result = parseType(typeString) - assertEquals(expected, result) - - // Test 13: Elaborated Type in Generics - result = parseType("Array") - generics = ArrayList() - var generic = ObjectType("Node", ArrayList(), false, CPPLanguage()) - generics.add(generic) - expected = ObjectType("Array", generics, false, CPPLanguage()) - assertEquals(expected, result) - - result = parseType("Array") - generics = ArrayList() - generic = ObjectType("myclass", ArrayList(), false, CPPLanguage()) - generics.add(generic) - expected = ObjectType("Array", generics, false, CPPLanguage()) - assertEquals(expected, result) - } - } - /** * Test for usage of getTypeStringFromDeclarator to determine function pointer raw type string * @@ -328,16 +120,13 @@ internal class TypeTests : BaseTest() { val noParamType = FunctionPointerType(emptyList(), CPPLanguage(), IncompleteType()) val oneParamType = FunctionPointerType( - listOf(IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.SIGNED)), + listOf(tu.primitiveType("int")), CPPLanguage(), IncompleteType() ) val twoParamType = FunctionPointerType( - listOf( - IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.SIGNED), - IntegerType("unsigned long", 64, CPPLanguage(), NumericType.Modifier.UNSIGNED) - ), + listOf(tu.primitiveType("int"), tu.primitiveType("unsigned long int")), CPPLanguage(), IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.SIGNED) ) @@ -383,12 +172,12 @@ internal class TypeTests : BaseTest() { val topLevel = Path.of("src", "test", "resources", "compiling", "hierarchy", "multistep") val result = analyze("simple_inheritance.cpp", topLevel, true) - val root = parseType("Root") - val level0 = parseType("Level0") - val level1 = parseType("Level1") - val level1b = parseType("Level1B") - val level2 = parseType("Level2") - val unrelated = parseType("Unrelated") + val root = objectType("Root") + val level0 = objectType("Level0") + val level1 = objectType("Level1") + val level1b = objectType("Level1B") + val level2 = objectType("Level2") + val unrelated = objectType("Unrelated") getCommonTypeTestGeneral(root, level0, level1, level1b, level2, unrelated, result) } } @@ -411,14 +200,14 @@ internal class TypeTests : BaseTest() { Path.of("src", "test", "resources", "compiling", "hierarchy", "multistep") val result = analyze("multi_inheritance.cpp", topLevel, true) - val root = parseType("Root") - val level0 = parseType("Level0") - val level0b = parseType("Level0B") - val level1 = parseType("Level1") - val level1b = parseType("Level1B") - val level1c = parseType("Level1C") - val level2 = parseType("Level2") - val level2b = parseType("Level2B") + val root = objectType("Root") + val level0 = objectType("Level0") + val level0b = objectType("Level0B") + val level1 = objectType("Level1") + val level1b = objectType("Level1B") + val level1c = objectType("Level1C") + val level2 = objectType("Level2") + val level2b = objectType("Level2B") val typeManager = result.finalCtx.typeManager /* diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXIncludeTest.kt b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXIncludeTest.kt index 11b649f78c..0e4a987c74 100644 --- a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXIncludeTest.kt +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXIncludeTest.kt @@ -31,6 +31,7 @@ import de.fraunhofer.aisec.cpg.TestUtils.analyzeWithBuilder import de.fraunhofer.aisec.cpg.TranslationConfiguration import de.fraunhofer.aisec.cpg.graph.declarations.* import de.fraunhofer.aisec.cpg.graph.get +import de.fraunhofer.aisec.cpg.graph.methods import de.fraunhofer.aisec.cpg.graph.records import de.fraunhofer.aisec.cpg.graph.statements.ReturnStatement import de.fraunhofer.aisec.cpg.graph.statements.expressions.DeclaredReferenceExpression @@ -240,7 +241,7 @@ internal class CXXIncludeTest : BaseTest() { @Test @Throws(Exception::class) - fun testLoadIncludes() { + fun testLoadIncludesDisabled() { val file = File("src/test/resources/include.cpp") val tus = analyzeWithBuilder( @@ -254,8 +255,15 @@ internal class CXXIncludeTest : BaseTest() { ) assertNotNull(tus) + val tu = tus.firstOrNull() + assertNotNull(tu) + // the tu should not contain any classes, since they are defined in the header (which are - // not loaded) - assertTrue(tus.firstOrNull().records.isEmpty()) + // not loaded) and inference is off. + assertTrue(tu.records.isEmpty()) + + // however, we should still have two methods (one of which is a constructor declaration) + assertEquals(2, tu.methods.size) + assertEquals(1, tu.methods.filterIsInstance().size) } } diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontendTest.kt b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontendTest.kt index 088206af73..d5fd09b57b 100644 --- a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontendTest.kt +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontendTest.kt @@ -25,21 +25,20 @@ */ package de.fraunhofer.aisec.cpg.frontends.cxx -import de.fraunhofer.aisec.cpg.BaseTest +import de.fraunhofer.aisec.cpg.* import de.fraunhofer.aisec.cpg.InferenceConfiguration.Companion.builder import de.fraunhofer.aisec.cpg.TestUtils.analyze import de.fraunhofer.aisec.cpg.TestUtils.analyzeAndGetFirstTU import de.fraunhofer.aisec.cpg.TestUtils.analyzeWithBuilder import de.fraunhofer.aisec.cpg.TestUtils.assertInvokes import de.fraunhofer.aisec.cpg.TestUtils.assertRefersTo -import de.fraunhofer.aisec.cpg.TranslationConfiguration -import de.fraunhofer.aisec.cpg.assertFullName -import de.fraunhofer.aisec.cpg.assertLocalName import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.* import de.fraunhofer.aisec.cpg.graph.statements.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.* import de.fraunhofer.aisec.cpg.graph.types.* +import de.fraunhofer.aisec.cpg.graph.types.PointerType.PointerOrigin.ARRAY +import de.fraunhofer.aisec.cpg.graph.types.PointerType.PointerOrigin.POINTER import de.fraunhofer.aisec.cpg.passes.* import de.fraunhofer.aisec.cpg.processing.IVisitor import de.fraunhofer.aisec.cpg.processing.strategy.Strategy @@ -63,7 +62,7 @@ internal class CXXLanguageFrontendTest : BaseTest() { val decl = main.iterator().next() val ls = decl.variables["ls"] assertNotNull(ls) - assertEquals(tu.parseType("std::vector", true), ls.type) + assertEquals(tu.objectType("std::vector", listOf(tu.objectType("int"))), ls.type) assertLocalName("ls", ls) val forEachStatement = decl.getBodyStatementAs(1, ForEachStatement::class.java) @@ -123,33 +122,36 @@ internal class CXXLanguageFrontendTest : BaseTest() { val file = File("src/test/resources/typeidexpr.cpp") val tu = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) val main = tu.getDeclarationsByName("main", FunctionDeclaration::class.java) - assertNotNull(main) + with(tu) { + assertNotNull(main) - val funcDecl = main.iterator().next() - val i = funcDecl.variables["i"] - assertNotNull(i) + val funcDecl = main.iterator().next() + val i = funcDecl.variables["i"] + assertNotNull(i) - val sizeof = i.initializer as? TypeIdExpression - assertNotNull(sizeof) - assertLocalName("sizeof", sizeof) - assertEquals(tu.parseType("std::size_t", true), sizeof.type) + val sizeof = i.initializer as? TypeIdExpression + assertNotNull(sizeof) + assertLocalName("sizeof", sizeof) + assertEquals(tu.objectType("std::size_t"), sizeof.type) - val typeInfo = funcDecl.variables["typeInfo"] - assertNotNull(typeInfo) + val typeInfo = funcDecl.variables["typeInfo"] + assertNotNull(typeInfo) - val typeid = typeInfo.initializer as? TypeIdExpression - assertNotNull(typeid) - assertLocalName("typeid", typeid) - assertEquals(tu.parseType("const std::type_info&", true), typeid.type) + val typeid = typeInfo.initializer as? TypeIdExpression + assertNotNull(typeid) + assertLocalName("typeid", typeid) - val j = funcDecl.variables["j"] - assertNotNull(j) + assertEquals(objectType("std::type_info").ref(), typeid.type) - val alignOf = j.initializer as? TypeIdExpression - assertNotNull(sizeof) - assertNotNull(alignOf) - assertLocalName("alignof", alignOf) - assertEquals(tu.parseType("std::size_t", true), alignOf.type) + val j = funcDecl.variables["j"] + assertNotNull(j) + + val alignOf = j.initializer as? TypeIdExpression + assertNotNull(sizeof) + assertNotNull(alignOf) + assertLocalName("alignof", alignOf) + assertEquals(tu.objectType("std::size_t"), alignOf.type) + } } @Test @@ -157,44 +159,48 @@ internal class CXXLanguageFrontendTest : BaseTest() { fun testCast() { val file = File("src/test/resources/components/castexpr.cpp") val tu = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) - val main = tu.getDeclarationAs(0, FunctionDeclaration::class.java) - val e = - Objects.requireNonNull(main!!.getBodyStatementAs(0, DeclarationStatement::class.java)) - ?.singleDeclaration as VariableDeclaration - assertNotNull(e) - assertEquals(tu.parseType("ExtendedClass*", true), e.type) - - val b = - Objects.requireNonNull(main.getBodyStatementAs(1, DeclarationStatement::class.java)) - ?.singleDeclaration as VariableDeclaration - assertNotNull(b) - assertEquals(tu.parseType("BaseClass*", true), b.type) - - // initializer - var cast = b.initializer as? CastExpression - assertNotNull(cast) - assertEquals(tu.parseType("BaseClass*", true), cast.castType) - - val staticCast = main.getBodyStatementAs(2, BinaryOperator::class.java) - assertNotNull(staticCast) - cast = staticCast.rhs as CastExpression - assertNotNull(cast) - assertLocalName("static_cast", cast) - - val reinterpretCast = main.getBodyStatementAs(3, BinaryOperator::class.java) - assertNotNull(reinterpretCast) - cast = reinterpretCast.rhs as CastExpression - assertNotNull(cast) - assertLocalName("reinterpret_cast", cast) - - val d = - Objects.requireNonNull(main.getBodyStatementAs(4, DeclarationStatement::class.java)) - ?.singleDeclaration as VariableDeclaration - assertNotNull(d) - - cast = d.initializer as? CastExpression - assertNotNull(cast) - assertEquals(tu.parseType("int", true), cast.castType) + with(tu) { + val main = tu.getDeclarationAs(0, FunctionDeclaration::class.java) + val e = + Objects.requireNonNull( + main!!.getBodyStatementAs(0, DeclarationStatement::class.java) + ) + ?.singleDeclaration as VariableDeclaration + assertNotNull(e) + assertEquals(objectType("ExtendedClass").pointer(), e.type) + + val b = + Objects.requireNonNull(main.getBodyStatementAs(1, DeclarationStatement::class.java)) + ?.singleDeclaration as VariableDeclaration + assertNotNull(b) + assertEquals(objectType("BaseClass").pointer(), b.type) + + // initializer + var cast = b.initializer as? CastExpression + assertNotNull(cast) + assertEquals(objectType("BaseClass").pointer(), cast.castType) + + val staticCast = main.getBodyStatementAs(2, BinaryOperator::class.java) + assertNotNull(staticCast) + cast = staticCast.rhs as CastExpression + assertNotNull(cast) + assertLocalName("static_cast", cast) + + val reinterpretCast = main.getBodyStatementAs(3, BinaryOperator::class.java) + assertNotNull(reinterpretCast) + cast = reinterpretCast.rhs as CastExpression + assertNotNull(cast) + assertLocalName("reinterpret_cast", cast) + + val d = + Objects.requireNonNull(main.getBodyStatementAs(4, DeclarationStatement::class.java)) + ?.singleDeclaration as VariableDeclaration + assertNotNull(d) + + cast = d.initializer as? CastExpression + assertNotNull(cast) + assertEquals(primitiveType("int"), cast.castType) + } } @Test @@ -203,47 +209,47 @@ internal class CXXLanguageFrontendTest : BaseTest() { val file = File("src/test/resources/cxx/arrays.cpp") val tu = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) val main = tu.byNameOrNull("main") - assertNotNull(main) - assertNotNull(main) - - val statement = main.body as CompoundStatement - - // first statement is the variable declaration - val x = - (statement.statements[0] as DeclarationStatement).singleDeclaration - as VariableDeclaration - assertNotNull(x) - assertEquals(tu.parseType("int[]", true), x.type) - - // initializer is an initializer list expression - val ile = x.initializer as? InitializerListExpression - assertNotNull(ile) - - val initializers = ile.initializers - assertNotNull(initializers) - assertEquals(3, initializers.size) - - // second statement is an expression directly - val ase = statement.statements[1] as ArraySubscriptionExpression - assertNotNull(ase) - assertEquals(x, (ase.arrayExpression as DeclaredReferenceExpression).refersTo) - assertEquals(0, (ase.subscriptExpression as Literal<*>).value) - - // third statement declares a pointer to an array - val a = - (statement.statements[2] as? DeclarationStatement)?.singleDeclaration - as? VariableDeclaration - assertNotNull(a) - - val type = a.type - assertTrue(type is PointerType && type.pointerOrigin == PointerType.PointerOrigin.POINTER) + with(tu) { + assertNotNull(main) + + val statement = main.body as CompoundStatement + + // first statement is the variable declaration + val x = + (statement.statements[0] as DeclarationStatement).singleDeclaration + as VariableDeclaration + assertNotNull(x) + assertEquals(primitiveType("int").array(), x.type) + + // initializer is an initializer list expression + val ile = x.initializer as? InitializerListExpression + assertNotNull(ile) + + val initializers = ile.initializers + assertNotNull(initializers) + assertEquals(3, initializers.size) + + // second statement is an expression directly + val ase = statement.statements[1] as ArraySubscriptionExpression + assertNotNull(ase) + assertEquals(x, (ase.arrayExpression as DeclaredReferenceExpression).refersTo) + assertEquals(0, (ase.subscriptExpression as Literal<*>).value) + + // third statement declares a pointer to an array + val a = + (statement.statements[2] as? DeclarationStatement)?.singleDeclaration + as? VariableDeclaration + assertNotNull(a) + + val type = a.type + assertTrue( + type is PointerType && type.pointerOrigin == PointerType.PointerOrigin.POINTER + ) - val elementType = (a.type as? PointerType)?.elementType - assertNotNull(elementType) - assertTrue( - elementType is PointerType && - elementType.pointerOrigin == PointerType.PointerOrigin.ARRAY - ) + val elementType = (a.type as? PointerType)?.elementType + assertNotNull(elementType) + assertTrue(elementType is PointerType && elementType.pointerOrigin == ARRAY) + } } @Test @@ -415,85 +421,88 @@ internal class CXXLanguageFrontendTest : BaseTest() { fun testDeclarationStatement() { val file = File("src/test/resources/cxx/declstmt.cpp") val tu = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) - val function = tu.getDeclarationAs(0, FunctionDeclaration::class.java) - val statements = function?.statements - assertNotNull(statements) - statements.forEach( - Consumer { node: Statement -> - log.debug("{}", node) - assertTrue( - node is DeclarationStatement || - statements.indexOf(node) == statements.size - 1 && node is ReturnStatement - ) - } - ) - - val declFromMultiplicateExpression = - (statements[0] as DeclarationStatement).getSingleDeclarationAs( - VariableDeclaration::class.java + with(tu) { + val function = tu.getDeclarationAs(0, FunctionDeclaration::class.java) + val statements = function?.statements + assertNotNull(statements) + statements.forEach( + Consumer { node: Statement -> + log.debug("{}", node) + assertTrue( + node is DeclarationStatement || + statements.indexOf(node) == statements.size - 1 && + node is ReturnStatement + ) + } ) - assertEquals(tu.parseType("SSL_CTX*", true), declFromMultiplicateExpression.type) - assertLocalName("ptr", declFromMultiplicateExpression) - val withInitializer = - (statements[1] as DeclarationStatement).getSingleDeclarationAs( - VariableDeclaration::class.java - ) - var initializer = withInitializer.initializer - assertNotNull(initializer) - assertTrue(initializer is Literal<*>) - assertEquals(1, initializer.value) - - val twoDeclarations = statements[2].declarations - assertEquals(2, twoDeclarations.size) - - val b = twoDeclarations[0] as VariableDeclaration - assertNotNull(b) - assertLocalName("b", b) - assertEquals(tu.parseType("int*", false), b.type) - - val c = twoDeclarations[1] as VariableDeclaration - assertNotNull(c) - assertLocalName("c", c) - assertEquals(tu.parseType("int", false), c.type) + val declFromMultiplicateExpression = + (statements[0] as DeclarationStatement).getSingleDeclarationAs( + VariableDeclaration::class.java + ) + assertEquals(objectType("SSL_CTX").pointer(), declFromMultiplicateExpression.type) + assertLocalName("ptr", declFromMultiplicateExpression) - val withoutInitializer = - (statements[3] as DeclarationStatement).getSingleDeclarationAs( - VariableDeclaration::class.java - ) - initializer = withoutInitializer.initializer - assertEquals(tu.parseType("int*", true), withoutInitializer.type) - assertLocalName("d", withoutInitializer) - assertNull(initializer) - - val qualifiedType = - (statements[4] as DeclarationStatement).getSingleDeclarationAs( - VariableDeclaration::class.java - ) - assertEquals(tu.parseType("std::string", true), qualifiedType.type) - assertLocalName("text", qualifiedType) - assertTrue(qualifiedType.initializer is Literal<*>) - assertEquals("some text", (qualifiedType.initializer as? Literal<*>)?.value) - - val pointerWithAssign = - (statements[5] as DeclarationStatement).getSingleDeclarationAs( - VariableDeclaration::class.java - ) - assertEquals(tu.parseType("void*", true), pointerWithAssign.type) - assertLocalName("ptr2", pointerWithAssign) - assertLocalName("NULL", pointerWithAssign.initializer) + val withInitializer = + (statements[1] as DeclarationStatement).getSingleDeclarationAs( + VariableDeclaration::class.java + ) + var initializer = withInitializer.initializer + assertNotNull(initializer) + assertTrue(initializer is Literal<*>) + assertEquals(1, initializer.value) + + val twoDeclarations = statements[2].declarations + assertEquals(2, twoDeclarations.size) + + val b = twoDeclarations[0] as VariableDeclaration + assertNotNull(b) + assertLocalName("b", b) + assertEquals(primitiveType("int").reference(POINTER), b.type) + + val c = twoDeclarations[1] as VariableDeclaration + assertNotNull(c) + assertLocalName("c", c) + assertEquals(primitiveType("int"), c.type) + + val withoutInitializer = + (statements[3] as DeclarationStatement).getSingleDeclarationAs( + VariableDeclaration::class.java + ) + initializer = withoutInitializer.initializer + assertEquals(primitiveType("int").reference(POINTER), withoutInitializer.type) + assertLocalName("d", withoutInitializer) + assertNull(initializer) + + val qualifiedType = + (statements[4] as DeclarationStatement).getSingleDeclarationAs( + VariableDeclaration::class.java + ) + assertEquals(objectType("std::string"), qualifiedType.type) + assertLocalName("text", qualifiedType) + assertTrue(qualifiedType.initializer is Literal<*>) + assertEquals("some text", (qualifiedType.initializer as? Literal<*>)?.value) + + val pointerWithAssign = + (statements[5] as DeclarationStatement).getSingleDeclarationAs( + VariableDeclaration::class.java + ) + assertEquals(incompleteType().reference(POINTER), pointerWithAssign.type) + assertLocalName("ptr2", pointerWithAssign) + assertLocalName("NULL", pointerWithAssign.initializer) - val classWithVariable = statements[6].declarations - assertEquals(2, classWithVariable.size) + val classWithVariable = statements[6].declarations + assertEquals(2, classWithVariable.size) - val classA = classWithVariable[0] as RecordDeclaration - assertNotNull(classA) - assertLocalName("A", classA) + val classA = classWithVariable[0] as RecordDeclaration + assertNotNull(classA) + assertLocalName("A", classA) - val myA = classWithVariable[1] as VariableDeclaration - assertNotNull(myA) - assertLocalName("myA", myA) - assertEquals(classA, (myA.type as ObjectType).recordDeclaration) + val myA = classWithVariable[1] as VariableDeclaration + assertNotNull(myA) + assertLocalName("myA", myA) + assertEquals(classA, (myA.type as ObjectType).recordDeclaration) + } } @Test @@ -657,7 +666,7 @@ internal class CXXLanguageFrontendTest : BaseTest() { // syntactically no different than the previous ones val stmt = statements[4] as DeclarationStatement val decl = stmt.singleDeclaration as VariableDeclaration - assertEquals(tu.parseType("std::string*", true), decl.type) + with(tu) { assertEquals(objectType("std::string").pointer(), decl.type) } assertLocalName("notMultiplication", decl) assertTrue(decl.initializer is BinaryOperator) @@ -686,7 +695,7 @@ internal class CXXLanguageFrontendTest : BaseTest() { val constant = recordDeclaration.fields["CONSTANT"] assertNotNull(constant) - assertEquals(tu.parseType("void*", true), field.type) + assertEquals(tu.incompleteType().reference(POINTER), field.type) assertEquals(3, recordDeclaration.methods.size) val method = recordDeclaration.methods[0] @@ -704,12 +713,12 @@ internal class CXXLanguageFrontendTest : BaseTest() { val methodWithParam = recordDeclaration.methods[1] assertLocalName("method", methodWithParam) assertEquals(1, methodWithParam.parameters.size) - assertEquals(tu.parseType("int", true), methodWithParam.parameters[0].type) + assertEquals(tu.primitiveType("int"), methodWithParam.parameters[0].type) assertEquals( FunctionType( "(int)void*", - listOf(tu.parseType("int", true)), - listOf(tu.parseType("void*", true)), + listOf(tu.primitiveType("int")), + listOf(tu.incompleteType().reference(POINTER)), CPPLanguage() ), methodWithParam.type @@ -725,7 +734,12 @@ internal class CXXLanguageFrontendTest : BaseTest() { val inlineMethod = recordDeclaration.methods[2] assertLocalName("inlineMethod", inlineMethod) assertEquals( - FunctionType("()void*", listOf(), listOf(tu.parseType("void*", true)), CPPLanguage()), + FunctionType( + "()void*", + listOf(), + listOf(tu.incompleteType().reference(POINTER)), + CPPLanguage() + ), inlineMethod.type ) assertTrue(inlineMethod.hasBody()) @@ -736,7 +750,7 @@ internal class CXXLanguageFrontendTest : BaseTest() { FunctionType( "()SomeClass", listOf(), - listOf(tu.parseType("SomeClass", true)), + listOf(tu.objectType("SomeClass")), CPPLanguage() ), inlineConstructor.type @@ -746,12 +760,12 @@ internal class CXXLanguageFrontendTest : BaseTest() { val constructorDefinition = tu.getDeclarationAs(3, ConstructorDeclaration::class.java) assertNotNull(constructorDefinition) assertEquals(1, constructorDefinition.parameters.size) - assertEquals(tu.parseType("int", true), constructorDefinition.parameters[0].type) + assertEquals(tu.primitiveType("int"), constructorDefinition.parameters[0].type) assertEquals( FunctionType( "(int)SomeClass", - listOf(tu.parseType("int", false)), - listOf(tu.parseType("SomeClass", true)), + listOf(tu.primitiveType("int")), + listOf(tu.objectType("SomeClass")), CPPLanguage() ), constructorDefinition.type @@ -841,7 +855,7 @@ internal class CXXLanguageFrontendTest : BaseTest() { val hex = tu.variables["hex"] assertNotNull(hex) assertIs(hex.type) - assertLocalName("unsigned long long", hex.type) + assertLocalName("unsigned long long int", hex.type) val duration_ms = tu.variables["duration_ms"] assertNotNull(duration_ms) @@ -877,7 +891,8 @@ internal class CXXLanguageFrontendTest : BaseTest() { // int z[] = { 2, 3, 4 }; val z = tu.getDeclarationAs(2, VariableDeclaration::class.java) - assertEquals(tu.parseType("int[]", true), z!!.type) + assertNotNull(z) + with(tu) { assertEquals(primitiveType("int").array(), z.type) } initializer = z.initializer assertNotNull(initializer) @@ -893,53 +908,54 @@ internal class CXXLanguageFrontendTest : BaseTest() { val file = File("src/test/resources/cxx/objcreation.cpp") val tu = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) assertNotNull(tu) - - // get the main method - val main = tu.getDeclarationAs(3, FunctionDeclaration::class.java) - val statement = main!!.body as CompoundStatement - - // Integer i - val i = - (statement.statements[0] as DeclarationStatement).singleDeclaration - as VariableDeclaration - // type should be Integer - assertEquals(tu.parseType("Integer", true), i.type) - - // initializer should be a construct expression - var constructExpression = i.initializer as? ConstructExpression - assertNotNull(constructExpression) - // type of the construct expression should also be Integer - assertEquals(tu.parseType("Integer", true), constructExpression.type) - - // auto (Integer) m - val m = - (statement.statements[6] as DeclarationStatement).singleDeclaration - as VariableDeclaration - // type should be Integer* - assertEquals(tu.parseType("Integer*", true), m.type) - - val constructor = constructExpression.constructor - assertNotNull(constructor) - assertLocalName("Integer", constructor) - assertFalse(constructor.isImplicit) - - // initializer should be a new expression - val newExpression = m.initializer as? NewExpression - assertNotNull(newExpression) - // type of the new expression should also be Integer* - assertEquals(tu.parseType("Integer*", true), newExpression.type) - - // initializer should be a construct expression - constructExpression = newExpression.initializer as? ConstructExpression - assertNotNull(constructExpression) - // type of the construct expression should be Integer - assertEquals(tu.parseType("Integer", true), constructExpression.type) - - // argument should be named k and of type m - val k = constructExpression.arguments[0] as DeclaredReferenceExpression - assertLocalName("k", k) - // type of the construct expression should also be Integer - assertEquals(tu.parseType("int", true), k.type) + with(tu) { + // get the main method + val main = tu.getDeclarationAs(3, FunctionDeclaration::class.java) + val statement = main!!.body as CompoundStatement + + // Integer i + val i = + (statement.statements[0] as DeclarationStatement).singleDeclaration + as VariableDeclaration + // type should be Integer + assertEquals(tu.objectType("Integer"), i.type) + + // initializer should be a construct expression + var constructExpression = i.initializer as? ConstructExpression + assertNotNull(constructExpression) + // type of the construct expression should also be Integer + assertEquals(tu.objectType("Integer"), constructExpression.type) + + // auto (Integer) m + val m = + (statement.statements[6] as DeclarationStatement).singleDeclaration + as VariableDeclaration + // type should be Integer* + assertEquals(objectType("Integer").pointer(), m.type) + + val constructor = constructExpression.constructor + assertNotNull(constructor) + assertLocalName("Integer", constructor) + assertFalse(constructor.isImplicit) + + // initializer should be a new expression + val newExpression = m.initializer as? NewExpression + assertNotNull(newExpression) + // type of the new expression should also be Integer* + assertEquals(objectType("Integer").pointer(), newExpression.type) + + // initializer should be a construct expression + constructExpression = newExpression.initializer as? ConstructExpression + assertNotNull(constructExpression) + // type of the construct expression should be Integer + assertEquals(objectType("Integer"), constructExpression.type) + + // argument should be named k and of type m + val k = constructExpression.arguments[0] as DeclaredReferenceExpression + assertLocalName("k", k) + // type of the construct expression should also be Integer + assertEquals(primitiveType("int"), k.type) + } } private val FunctionDeclaration.statements: List? @@ -1241,6 +1257,7 @@ internal class CXXLanguageFrontendTest : BaseTest() { assertNotNull(initializer) assertTrue(initializer is CastExpression) assertLocalName("size_t", initializer.castType) + assertLiteralValue(42, initializer.expression) } @Test @@ -1547,4 +1564,14 @@ internal class CXXLanguageFrontendTest : BaseTest() { val tu = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) assertNotNull(tu) } + + @Test + @Throws(Exception::class) + fun testCFunctionReturnType() { + val file = File("src/test/resources/c/types.c") + val tu = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) + assertNotNull(tu) + + assertLocalName("int", tu.functions["main"]?.returnTypes?.firstOrNull()) + } } diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLiteralTest.kt b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLiteralTest.kt index 3f4634989d..d3761416d7 100644 --- a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLiteralTest.kt +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLiteralTest.kt @@ -53,12 +53,12 @@ internal class CXXLiteralTest : BaseTest() { val funcDecl = zero.iterator().next() assertLocalName("zero", funcDecl) - assertLiteral(0, tu.parseType("int"), funcDecl, "i") - assertLiteral(0L, tu.parseType("long"), funcDecl, "l_with_suffix") - assertLiteral(0L, tu.parseType("long long"), funcDecl, "l_long_long_with_suffix") + assertLiteral(0, tu.primitiveType("int"), funcDecl, "i") + assertLiteral(0L, tu.primitiveType("long int"), funcDecl, "l_with_suffix") + assertLiteral(0L, tu.primitiveType("long long int"), funcDecl, "l_long_long_with_suffix") assertLiteral( BigInteger.valueOf(0), - tu.parseType("unsigned long long"), + tu.primitiveType("unsigned long long int"), funcDecl, "l_unsigned_long_long_with_suffix" ) @@ -73,31 +73,31 @@ internal class CXXLiteralTest : BaseTest() { assertFalse(decimal.isEmpty()) val funcDecl = decimal.iterator().next() assertLocalName("decimal", funcDecl) - assertLiteral(42, tu.parseType("int"), funcDecl, "i") - assertLiteral(1000, tu.parseType("int"), funcDecl, "i_with_literal") - assertLiteral(9223372036854775807L, tu.parseType("long"), funcDecl, "l") - assertLiteral(9223372036854775807L, tu.parseType("long"), funcDecl, "l_with_suffix") + assertLiteral(42, tu.primitiveType("int"), funcDecl, "i") + assertLiteral(1000, tu.primitiveType("int"), funcDecl, "i_with_literal") + assertLiteral(9223372036854775807L, tu.primitiveType("long int"), funcDecl, "l") + assertLiteral(9223372036854775807L, tu.primitiveType("long int"), funcDecl, "l_with_suffix") assertLiteral( 9223372036854775807L, - tu.parseType("long long"), + tu.primitiveType("long long int"), funcDecl, "l_long_long_with_suffix" ) assertLiteral( BigInteger("9223372036854775809"), - tu.parseType("unsigned long"), + tu.primitiveType("unsigned long int"), funcDecl, "l_unsigned_long_with_suffix" ) assertLiteral( BigInteger("9223372036854775808"), - tu.parseType("unsigned long long"), + tu.primitiveType("unsigned long long int"), funcDecl, "l_long_long_implicit" ) assertLiteral( BigInteger("9223372036854775809"), - tu.parseType("unsigned long long"), + tu.primitiveType("unsigned long long int"), funcDecl, "l_unsigned_long_long_with_suffix" ) @@ -112,11 +112,11 @@ internal class CXXLiteralTest : BaseTest() { assertFalse(octal.isEmpty()) val funcDecl = octal.iterator().next() assertLocalName("octal", funcDecl) - assertLiteral(42, tu.parseType("int"), funcDecl, "i") - assertLiteral(42L, tu.parseType("long"), funcDecl, "l_with_suffix") + assertLiteral(42, tu.primitiveType("int"), funcDecl, "i") + assertLiteral(42L, tu.primitiveType("long int"), funcDecl, "l_with_suffix") assertLiteral( BigInteger.valueOf(42), - tu.parseType("unsigned long long"), + tu.primitiveType("unsigned long long int"), funcDecl, "l_unsigned_long_long_with_suffix" ) @@ -132,11 +132,11 @@ internal class CXXLiteralTest : BaseTest() { assertFalse(hex.isEmpty()) val funcDecl = hex.iterator().next() assertLocalName("hex", funcDecl) - assertLiteral(42, tu.parseType("int"), funcDecl, "i") - assertLiteral(42L, tu.parseType("long"), funcDecl, "l_with_suffix") + assertLiteral(42, tu.primitiveType("int"), funcDecl, "i") + assertLiteral(42L, tu.primitiveType("long int"), funcDecl, "l_with_suffix") assertLiteral( BigInteger.valueOf(42), - tu.parseType("unsigned long long"), + tu.primitiveType("unsigned long long int"), funcDecl, "l_unsigned_long_long_with_suffix" ) diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXSymbolConfigurationTest.kt b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXSymbolConfigurationTest.kt index 1270cac616..8c9b894742 100644 --- a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXSymbolConfigurationTest.kt +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXSymbolConfigurationTest.kt @@ -26,8 +26,8 @@ package de.fraunhofer.aisec.cpg.frontends.cxx import de.fraunhofer.aisec.cpg.* +import de.fraunhofer.aisec.cpg.TypeManager import de.fraunhofer.aisec.cpg.frontends.TranslationException -import de.fraunhofer.aisec.cpg.graph.TypeManager import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.statements.expressions.BinaryOperator import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/CallResolverTest.kt b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/CallResolverTest.kt index 51056da5b5..21b9b6eaac 100644 --- a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/CallResolverTest.kt +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/CallResolverTest.kt @@ -42,6 +42,7 @@ import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.CastExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.Literal +import de.fraunhofer.aisec.cpg.graph.types.PointerType import de.fraunhofer.aisec.cpg.graph.types.Type import de.fraunhofer.aisec.cpg.graph.types.UnknownType import java.io.File @@ -158,8 +159,8 @@ class CallResolverTest : BaseTest() { val records = result.records - val intType = tu.parseType("int") - val stringType = tu.parseType("char*") + val intType = tu.primitiveType("int") + val stringType = tu.primitiveType("char").reference(PointerType.PointerOrigin.POINTER) testMethods(records, intType, stringType) testOverriding(records) diff --git a/cpg-language-cxx/src/test/resources/c/types.c b/cpg-language-cxx/src/test/resources/c/types.c new file mode 100644 index 0000000000..910affb82c --- /dev/null +++ b/cpg-language-cxx/src/test/resources/c/types.c @@ -0,0 +1,5 @@ +// C allows to omit the type specifier, which then defaults to int. +// However, usually a compiler will at least emit a warning here. +main() { + return 1; +} \ No newline at end of file diff --git a/cpg-language-go/src/main/golang/basic_types.go b/cpg-language-go/src/main/golang/basic_types.go index 3325260572..b302ff2134 100644 --- a/cpg-language-go/src/main/golang/basic_types.go +++ b/cpg-language-go/src/main/golang/basic_types.go @@ -1,28 +1,29 @@ -/* - * Copyright (c) 2021, Fraunhofer AISEC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * $$$$$$\ $$$$$$$\ $$$$$$\ - * $$ __$$\ $$ __$$\ $$ __$$\ - * $$ / \__|$$ | $$ |$$ / \__| - * $$ | $$$$$$$ |$$ |$$$$\ - * $$ | $$ ____/ $$ |\_$$ | - * $$ | $$\ $$ | $$ | $$ | - * \$$$$$ |$$ | \$$$$$ | - * \______/ \__| \______/ - * - */ +// +// Copyright (c) 2021, Fraunhofer AISEC. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// $$$$$$\ $$$$$$$\ $$$$$$\ +// $$ __$$\ $$ __$$\ $$ __$$\ +// $$ / \__|$$ | $$ |$$ / \__| +// $$ | $$$$$$$ |$$ |$$$$\ +// $$ | $$ ____/ $$ |\_$$ | +// $$ | $$\ $$ | $$ | $$ | +// \$$$$$ |$$ | \$$$$$ | +// \______/ \__| \______/ +// +// + package cpg import ( diff --git a/cpg-language-go/src/main/golang/declarations.go b/cpg-language-go/src/main/golang/declarations.go index caadcced29..13d64c2efe 100644 --- a/cpg-language-go/src/main/golang/declarations.go +++ b/cpg-language-go/src/main/golang/declarations.go @@ -1,28 +1,29 @@ -/* - * Copyright (c) 2021, Fraunhofer AISEC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * $$$$$$\ $$$$$$$\ $$$$$$\ - * $$ __$$\ $$ __$$\ $$ __$$\ - * $$ / \__|$$ | $$ |$$ / \__| - * $$ | $$$$$$$ |$$ |$$$$\ - * $$ | $$ ____/ $$ |\_$$ | - * $$ | $$\ $$ | $$ | $$ | - * \$$$$$ |$$ | \$$$$$ | - * \______/ \__| \______/ - * - */ +// +// Copyright (c) 2021, Fraunhofer AISEC. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// $$$$$$\ $$$$$$$\ $$$$$$\ +// $$ __$$\ $$ __$$\ $$ __$$\ +// $$ / \__|$$ | $$ |$$ / \__| +// $$ | $$$$$$$ |$$ |$$$$\ +// $$ | $$ ____/ $$ |\_$$ | +// $$ | $$\ $$ | $$ | $$ | +// \$$$$$ |$$ | \$$$$$ | +// \______/ \__| \______/ +// +// + package cpg import ( diff --git a/cpg-language-go/src/main/golang/expressions.go b/cpg-language-go/src/main/golang/expressions.go index d1716ea336..0486da9036 100644 --- a/cpg-language-go/src/main/golang/expressions.go +++ b/cpg-language-go/src/main/golang/expressions.go @@ -1,28 +1,29 @@ -/* - * Copyright (c) 2021, Fraunhofer AISEC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * $$$$$$\ $$$$$$$\ $$$$$$\ - * $$ __$$\ $$ __$$\ $$ __$$\ - * $$ / \__|$$ | $$ |$$ / \__| - * $$ | $$$$$$$ |$$ |$$$$\ - * $$ | $$ ____/ $$ |\_$$ | - * $$ | $$\ $$ | $$ | $$ | - * \$$$$$ |$$ | \$$$$$ | - * \______/ \__| \______/ - * - */ +// +// Copyright (c) 2021, Fraunhofer AISEC. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// $$$$$$\ $$$$$$$\ $$$$$$\ +// $$ __$$\ $$ __$$\ $$ __$$\ +// $$ / \__|$$ | $$ |$$ / \__| +// $$ | $$$$$$$ |$$ |$$$$\ +// $$ | $$ ____/ $$ |\_$$ | +// $$ | $$\ $$ | $$ | $$ | +// \$$$$$ |$$ | \$$$$$ | +// \______/ \__| \______/ +// +// + package cpg import ( @@ -74,7 +75,9 @@ type LambdaExpression Expression type ProblemExpression Expression func (e *Expression) SetType(t *Type) { - (*HasType)(e).SetType(t) + if t != nil && !t.IsNil() { + (*HasType)(e).SetType(t) + } } func (c *CallExpression) SetFqn(s string) { @@ -178,7 +181,9 @@ func (u *UnaryOperator) SetOperatorCode(s string) (err error) { } func (l *Literal) SetType(t *Type) { - (*Expression)(l).SetType(t) + if t != nil && !t.IsNil() { + (*Expression)(l).SetType(t) + } } func (l *Literal) SetValue(value interface{}) { diff --git a/cpg-language-go/src/main/golang/frontend/declaration_builder.go b/cpg-language-go/src/main/golang/frontend/declaration_builder.go index 817ce7faf9..a3d3a6697d 100644 --- a/cpg-language-go/src/main/golang/frontend/declaration_builder.go +++ b/cpg-language-go/src/main/golang/frontend/declaration_builder.go @@ -1,28 +1,29 @@ -/* - * Copyright (c) 2022, Fraunhofer AISEC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * $$$$$$\ $$$$$$$\ $$$$$$\ - * $$ __$$\ $$ __$$\ $$ __$$\ - * $$ / \__|$$ | $$ |$$ / \__| - * $$ | $$$$$$$ |$$ |$$$$\ - * $$ | $$ ____/ $$ |\_$$ | - * $$ | $$\ $$ | $$ | $$ | - * \$$$$$ |$$ | \$$$$$ | - * \______/ \__| \______/ - * - */ +// +// Copyright (c) 2022, Fraunhofer AISEC. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// $$$$$$\ $$$$$$$\ $$$$$$\ +// $$ __$$\ $$ __$$\ $$ __$$\ +// $$ / \__|$$ | $$ |$$ / \__| +// $$ | $$$$$$$ |$$ |$$$$\ +// $$ | $$ ____/ $$ |\_$$ | +// $$ | $$\ $$ | $$ | $$ | +// \$$$$$ |$$ | \$$$$$ | +// \______/ \__| \______/ +// +// + package frontend import ( diff --git a/cpg-language-go/src/main/golang/frontend/expression_builder.go b/cpg-language-go/src/main/golang/frontend/expression_builder.go index 9b248e90e1..4a457293b7 100644 --- a/cpg-language-go/src/main/golang/frontend/expression_builder.go +++ b/cpg-language-go/src/main/golang/frontend/expression_builder.go @@ -1,28 +1,29 @@ -/* - * Copyright (c) 2022, Fraunhofer AISEC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * $$$$$$\ $$$$$$$\ $$$$$$\ - * $$ __$$\ $$ __$$\ $$ __$$\ - * $$ / \__|$$ | $$ |$$ / \__| - * $$ | $$$$$$$ |$$ |$$$$\ - * $$ | $$ ____/ $$ |\_$$ | - * $$ | $$\ $$ | $$ | $$ | - * \$$$$$ |$$ | \$$$$$ | - * \______/ \__| \______/ - * - */ +// +// Copyright (c) 2022, Fraunhofer AISEC. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// $$$$$$\ $$$$$$$\ $$$$$$\ +// $$ __$$\ $$ __$$\ $$ __$$\ +// $$ / \__|$$ | $$ |$$ / \__| +// $$ | $$$$$$$ |$$ |$$$$\ +// $$ | $$ ____/ $$ |\_$$ | +// $$ | $$\ $$ | $$ | $$ | +// \$$$$$ |$$ | \$$$$$ | +// \______/ \__| \______/ +// +// + package frontend import ( diff --git a/cpg-language-go/src/main/golang/frontend/frontend.go b/cpg-language-go/src/main/golang/frontend/frontend.go index f6d788f34a..f724f62b6a 100644 --- a/cpg-language-go/src/main/golang/frontend/frontend.go +++ b/cpg-language-go/src/main/golang/frontend/frontend.go @@ -1,28 +1,29 @@ -/* - * Copyright (c) 2021, Fraunhofer AISEC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * $$$$$$\ $$$$$$$\ $$$$$$\ - * $$ __$$\ $$ __$$\ $$ __$$\ - * $$ / \__|$$ | $$ |$$ / \__| - * $$ | $$$$$$$ |$$ |$$$$\ - * $$ | $$ ____/ $$ |\_$$ | - * $$ | $$\ $$ | $$ | $$ | - * \$$$$$ |$$ | \$$$$$ | - * \______/ \__| \______/ - * - */ +// +// Copyright (c) 2021, Fraunhofer AISEC. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// $$$$$$\ $$$$$$$\ $$$$$$\ +// $$ __$$\ $$ __$$\ $$ __$$\ +// $$ / \__|$$ | $$ |$$ / \__| +// $$ | $$$$$$$ |$$ |$$$$\ +// $$ | $$ ____/ $$ |\_$$ | +// $$ | $$\ $$ | $$ | $$ | +// \$$$$$ |$$ | \$$$$$ | +// \______/ \__| \______/ +// +// + package frontend import ( diff --git a/cpg-language-go/src/main/golang/frontend/handler.go b/cpg-language-go/src/main/golang/frontend/handler.go index 163cea3897..5f03a0593b 100644 --- a/cpg-language-go/src/main/golang/frontend/handler.go +++ b/cpg-language-go/src/main/golang/frontend/handler.go @@ -1,28 +1,29 @@ -/* - * Copyright (c) 2021, Fraunhofer AISEC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * $$$$$$\ $$$$$$$\ $$$$$$\ - * $$ __$$\ $$ __$$\ $$ __$$\ - * $$ / \__|$$ | $$ |$$ / \__| - * $$ | $$$$$$$ |$$ |$$$$\ - * $$ | $$ ____/ $$ |\_$$ | - * $$ | $$\ $$ | $$ | $$ | - * \$$$$$ |$$ | \$$$$$ | - * \______/ \__| \______/ - * - */ +// +// Copyright (c) 2021, Fraunhofer AISEC. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// $$$$$$\ $$$$$$$\ $$$$$$\ +// $$ __$$\ $$ __$$\ $$ __$$\ +// $$ / \__|$$ | $$ |$$ / \__| +// $$ | $$$$$$$ |$$ |$$$$\ +// $$ | $$ ____/ $$ |\_$$ | +// $$ | $$\ $$ | $$ | $$ | +// \$$$$$ |$$ | \$$$$$ | +// \______/ \__| \______/ +// +// + package frontend import ( @@ -349,13 +350,7 @@ func (this *GoLanguageFrontend) handleFuncDecl(fset *token.FileSet, funcDecl *as p.SetVariadic(true) var t = this.handleType(fset, ell.Elt) - var i = jnigi.NewObjectRef(cpg.PointerOriginClass) - err := env.GetStaticField(cpg.PointerOriginClass, "ARRAY", i) - if err != nil { - log.Fatal(err) - } - - p.SetType(t.Reference(i)) + p.SetType(this.ref(t, "pointer")) } else { p.SetType(this.handleType(fset, param.Type)) } @@ -446,7 +441,7 @@ func (this *GoLanguageFrontend) handleValueSpec(fset *token.FileSet, valueDecl * return decls } -// handleTypeSpec handles an [ast.TypeSec], which defines either a struct or an +// handleTypeSpec handles an [ast.TypeSpec], which defines either a struct or an // interface. It returns a single [cpg.Declaration]. func (this *GoLanguageFrontend) handleTypeSpec(fset *token.FileSet, typeDecl *ast.TypeSpec) *cpg.Declaration { err := this.LogInfo("Type specifier with name %s and type (%T, %+v)", typeDecl.Name.Name, typeDecl.Type, typeDecl.Type) @@ -1058,6 +1053,8 @@ func (this *GoLanguageFrontend) handleNewExpr(fset *token.FileSet, callExpr *ast t := this.handleType(fset, callExpr.Args[0]) // new is a pointer, so need to reference the type with a pointer + // TODO(oxisto): There is a very weird bug here: If we would use this.Pointer() here, we will get a + // null pointer exception and I have no idea why var pointer = jnigi.NewObjectRef(cpg.PointerOriginClass) err := env.GetStaticField(cpg.PointerOriginClass, "POINTER", pointer) if err != nil { @@ -1226,31 +1223,26 @@ func (this *GoLanguageFrontend) handleBasicLit(fset *token.FileSet, lit *ast.Bas var value cpg.Castable var t *cpg.Type - lang, err := this.GetLanguage() - if err != nil { - panic(err) - } - switch lit.Kind { case token.STRING: // strip the " value = cpg.NewString(lit.Value[1 : len(lit.Value)-1]) - t = cpg.TypeParser_createFrom("string", lang, this.GetCtx()) + t = this.primitiveType("string") case token.INT: i, _ := strconv.ParseInt(lit.Value, 10, 64) value = cpg.NewInteger(int(i)) - t = cpg.TypeParser_createFrom("int", lang, this.GetCtx()) + t = this.primitiveType("int") case token.FLOAT: // default seems to be float64 f, _ := strconv.ParseFloat(lit.Value, 64) value = cpg.NewDouble(f) - t = cpg.TypeParser_createFrom("float64", lang, this.GetCtx()) + t = this.primitiveType("float64") case token.IMAG: // TODO - t = &cpg.UnknownType_getUnknown(lang).Type + t = this.unknownType() case token.CHAR: value = cpg.NewString(lit.Value) - t = cpg.TypeParser_createFrom("rune", lang, this.GetCtx()) + t = this.primitiveType("rune") break } @@ -1316,14 +1308,9 @@ func (this *GoLanguageFrontend) handleFuncLit(fset *token.FileSet, lit *ast.Func } func (this *GoLanguageFrontend) handleIdent(fset *token.FileSet, ident *ast.Ident) *cpg.Expression { - lang, err := this.GetLanguage() - if err != nil { - panic(err) - } - // Check, if this is 'nil', because then we handle it as a literal in the graph if ident.Name == "nil" { - lit := this.NewLiteral(fset, ident, nil, &cpg.UnknownType_getUnknown(lang).Type) + lit := this.NewLiteral(fset, ident, nil, this.unknownType()) (*cpg.Node)(lit).SetName(this.ParseName(ident.Name)) @@ -1384,40 +1371,28 @@ func (this *GoLanguageFrontend) handleType(fset *token.FileSet, typeExpr ast.Exp this.LogTrace("fqn type: %s", name) } - return cpg.TypeParser_createFrom(name, lang, this.GetCtx()) + return this.objectType(name) case *ast.SelectorExpr: // small shortcut fqn := fmt.Sprintf("%s.%s", v.X.(*ast.Ident).Name, v.Sel.Name) this.LogTrace("FQN type: %s", fqn) - return cpg.TypeParser_createFrom(fqn, lang, this.GetCtx()) + return this.objectType(fqn) case *ast.StarExpr: t := this.handleType(fset, v.X) - var i = jnigi.NewObjectRef(cpg.PointerOriginClass) - err = env.GetStaticField(cpg.PointerOriginClass, "POINTER", i) - if err != nil { - log.Fatal(err) - } - this.LogTrace("Pointer to %s", t.GetName()) - return t.Reference(i) + return this.ref(t, "pointer") case *ast.ArrayType: t := this.handleType(fset, v.Elt) - var i = jnigi.NewObjectRef(cpg.PointerOriginClass) - err = env.GetStaticField(cpg.PointerOriginClass, "ARRAY", i) - if err != nil { - log.Fatal(err) - } - this.LogTrace("Array of %s", t.GetName()) - return t.Reference(i) + return this.ref(t, "array") case *ast.MapType: // we cannot properly represent Golangs built-in map types, yet so we have // to make a shortcut here and represent it as a Java-like map type. - t := cpg.TypeParser_createFrom("map", lang, this.GetCtx()) + t := this.objectType("map") keyType := this.handleType(fset, v.Key) valueType := this.handleType(fset, v.Value) @@ -1428,7 +1403,7 @@ func (this *GoLanguageFrontend) handleType(fset *token.FileSet, typeExpr ast.Exp return t case *ast.ChanType: // handle them similar to maps - t := cpg.TypeParser_createFrom("chan", lang, this.GetCtx()) + t := this.objectType("chan") chanType := this.handleType(fset, v.Value) (*cpg.ObjectType)(t).AddGeneric(chanType) @@ -1436,8 +1411,8 @@ func (this *GoLanguageFrontend) handleType(fset *token.FileSet, typeExpr ast.Exp return t case *ast.FuncType: var parametersTypesList, returnTypesList, name *jnigi.ObjectRef - var parameterTypes = []*cpg.Type{} - var returnTypes = []*cpg.Type{} + var parameterTypes []*cpg.Type + var returnTypes []*cpg.Type for _, param := range v.Params.List { parameterTypes = append(parameterTypes, this.handleType(fset, param.Type)) @@ -1484,7 +1459,7 @@ func (this *GoLanguageFrontend) handleType(fset *token.FileSet, typeExpr ast.Exp name += "}" - return cpg.TypeParser_createFrom(name, lang, this.GetCtx()) + return this.objectType(name) case *ast.IndexExpr: // This is a type with one type parameter. First we need to parse the "X" expression as a type var t = this.handleType(fset, v.X) @@ -1511,7 +1486,7 @@ func (this *GoLanguageFrontend) handleType(fset *token.FileSet, typeExpr ast.Exp this.LogError("Not parsing type of type %T yet. Defaulting to unknown type", v) } - return &cpg.UnknownType_getUnknown(lang).Type + return this.unknownType() } func (this *GoLanguageFrontend) isBuiltinType(s string) bool { diff --git a/cpg-language-go/src/main/golang/frontend/statement_builder.go b/cpg-language-go/src/main/golang/frontend/statement_builder.go index f951d41392..93db27d63f 100644 --- a/cpg-language-go/src/main/golang/frontend/statement_builder.go +++ b/cpg-language-go/src/main/golang/frontend/statement_builder.go @@ -1,28 +1,29 @@ -/* - * Copyright (c) 2022, Fraunhofer AISEC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * $$$$$$\ $$$$$$$\ $$$$$$\ - * $$ __$$\ $$ __$$\ $$ __$$\ - * $$ / \__|$$ | $$ |$$ / \__| - * $$ | $$$$$$$ |$$ |$$$$\ - * $$ | $$ ____/ $$ |\_$$ | - * $$ | $$\ $$ | $$ | $$ | - * \$$$$$ |$$ | \$$$$$ | - * \______/ \__| \______/ - * - */ +// +// Copyright (c) 2022, Fraunhofer AISEC. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// $$$$$$\ $$$$$$$\ $$$$$$\ +// $$ __$$\ $$ __$$\ $$ __$$\ +// $$ / \__|$$ | $$ |$$ / \__| +// $$ | $$$$$$$ |$$ |$$$$\ +// $$ | $$ ____/ $$ |\_$$ | +// $$ | $$\ $$ | $$ | $$ | +// \$$$$$ |$$ | \$$$$$ | +// \______/ \__| \______/ +// +// + package frontend import ( diff --git a/cpg-language-go/src/main/golang/frontend/type_builder.go b/cpg-language-go/src/main/golang/frontend/type_builder.go new file mode 100644 index 0000000000..13e9c7589f --- /dev/null +++ b/cpg-language-go/src/main/golang/frontend/type_builder.go @@ -0,0 +1,91 @@ +// +// Copyright (c) 2023, Fraunhofer AISEC. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// $$$$$$\ $$$$$$$\ $$$$$$\ +// $$ __$$\ $$ __$$\ $$ __$$\ +// $$ / \__|$$ | $$ |$$ / \__| +// $$ | $$$$$$$ |$$ |$$$$\ +// $$ | $$ ____/ $$ |\_$$ | +// $$ | $$\ $$ | $$ | $$ | +// \$$$$$ |$$ | \$$$$$ | +// \______/ \__| \______/ +// +// + +package frontend + +import ( + "cpg" + "fmt" + "log" + "strings" + "tekao.net/jnigi" +) + +func (frontend *GoLanguageFrontend) unknownType() *cpg.Type { + return frontend.newType("MetadataProvider", "UnknownType") +} + +func (frontend *GoLanguageFrontend) primitiveType(typeName string) *cpg.Type { + return frontend.newType("LanguageProvider", "PrimitiveType", cpg.NewCharSequence(typeName)) +} + +func (frontend *GoLanguageFrontend) objectType(typeName string) *cpg.Type { + return frontend.newType("LanguageProvider", "ObjectType", cpg.NewCharSequence(typeName)) +} + +func (frontend *GoLanguageFrontend) newType(providerClass string, typ string, args ...any) *cpg.Type { + var node cpg.Type + + // Prepend the frontend as the receiver + args = append([]any{frontend.Cast(cpg.GraphPackage + fmt.Sprintf("/%s", providerClass))}, args...) + + err := env.CallStaticMethod( + cpg.GraphPackage+"/NodeBuilderKt", + fmt.Sprintf("%s%s", strings.ToLower(string(typ[0])), typ[1:]), &node, + args..., + ) + if err != nil { + log.Fatal(err) + } + + return &node +} + +func (frontend *GoLanguageFrontend) ref(t *cpg.Type, typ string) *cpg.Type { + var ( + refType *jnigi.ObjectRef + args []any + ) + + refType = jnigi.NewObjectRef(cpg.TypeClass) + + args = []any{ + frontend.Cast(cpg.GraphPackage + fmt.Sprintf("/%s", "ContextProvider")), + t, + } + + err := env.CallStaticMethod( + cpg.GraphPackage+"/NodeBuilderKt", + typ, + refType, + args..., + ) + if err != nil { + panic(err) + } + + return &cpg.Type{ObjectRef: refType} +} diff --git a/cpg-language-go/src/main/golang/helper.go b/cpg-language-go/src/main/golang/helper.go index 4f1513421b..52a31156d1 100644 --- a/cpg-language-go/src/main/golang/helper.go +++ b/cpg-language-go/src/main/golang/helper.go @@ -1,28 +1,29 @@ -/* - * Copyright (c) 2022, Fraunhofer AISEC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * $$$$$$\ $$$$$$$\ $$$$$$\ - * $$ __$$\ $$ __$$\ $$ __$$\ - * $$ / \__|$$ | $$ |$$ / \__| - * $$ | $$$$$$$ |$$ |$$$$\ - * $$ | $$ ____/ $$ |\_$$ | - * $$ | $$\ $$ | $$ | $$ | - * \$$$$$ |$$ | \$$$$$ | - * \______/ \__| \______/ - * - */ +// +// Copyright (c) 2022, Fraunhofer AISEC. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// $$$$$$\ $$$$$$$\ $$$$$$\ +// $$ __$$\ $$ __$$\ $$ __$$\ +// $$ / \__|$$ | $$ |$$ / \__| +// $$ | $$$$$$$ |$$ |$$$$\ +// $$ | $$ ____/ $$ |\_$$ | +// $$ | $$\ $$ | $$ | $$ | +// \$$$$$ |$$ | \$$$$$ | +// \______/ \__| \______/ +// +// + package cpg import "tekao.net/jnigi" diff --git a/cpg-language-go/src/main/golang/language.go b/cpg-language-go/src/main/golang/language.go index 36d0d504a5..8e92564fd1 100644 --- a/cpg-language-go/src/main/golang/language.go +++ b/cpg-language-go/src/main/golang/language.go @@ -1,28 +1,29 @@ -/* - * Copyright (c) 2021, Fraunhofer AISEC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * $$$$$$\ $$$$$$$\ $$$$$$\ - * $$ __$$\ $$ __$$\ $$ __$$\ - * $$ / \__|$$ | $$ |$$ / \__| - * $$ | $$$$$$$ |$$ |$$$$\ - * $$ | $$ ____/ $$ |\_$$ | - * $$ | $$\ $$ | $$ | $$ | - * \$$$$$ |$$ | \$$$$$ | - * \______/ \__| \______/ - * - */ +// +// Copyright (c) 2021, Fraunhofer AISEC. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// $$$$$$\ $$$$$$$\ $$$$$$\ +// $$ __$$\ $$ __$$\ $$ __$$\ +// $$ / \__|$$ | $$ |$$ / \__| +// $$ | $$$$$$$ |$$ |$$$$\ +// $$ | $$ ____/ $$ |\_$$ | +// $$ | $$\ $$ | $$ | $$ | +// \$$$$$ |$$ | \$$$$$ | +// \______/ \__| \______/ +// +// + package cpg import ( diff --git a/cpg-language-go/src/main/golang/lib/cpg/main.go b/cpg-language-go/src/main/golang/lib/cpg/main.go index eb28bdee51..273c321112 100644 --- a/cpg-language-go/src/main/golang/lib/cpg/main.go +++ b/cpg-language-go/src/main/golang/lib/cpg/main.go @@ -1,28 +1,29 @@ -/* - * Copyright (c) 2021, Fraunhofer AISEC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * $$$$$$\ $$$$$$$\ $$$$$$\ - * $$ __$$\ $$ __$$\ $$ __$$\ - * $$ / \__|$$ | $$ |$$ / \__| - * $$ | $$$$$$$ |$$ |$$$$\ - * $$ | $$ ____/ $$ |\_$$ | - * $$ | $$\ $$ | $$ | $$ | - * \$$$$$ |$$ | \$$$$$ | - * \______/ \__| \______/ - * - */ +// +// Copyright (c) 2021, Fraunhofer AISEC. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// $$$$$$\ $$$$$$$\ $$$$$$\ +// $$ __$$\ $$ __$$\ $$ __$$\ +// $$ / \__|$$ | $$ |$$ / \__| +// $$ | $$$$$$$ |$$ |$$$$\ +// $$ | $$ ____/ $$ |\_$$ | +// $$ | $$\ $$ | $$ | $$ | +// \$$$$$ |$$ | \$$$$$ | +// \______/ \__| \______/ +// +// + package main import ( diff --git a/cpg-language-go/src/main/golang/location.go b/cpg-language-go/src/main/golang/location.go index e5a1fa4e1a..c4b82e984e 100644 --- a/cpg-language-go/src/main/golang/location.go +++ b/cpg-language-go/src/main/golang/location.go @@ -1,28 +1,29 @@ -/* - * Copyright (c) 2021, Fraunhofer AISEC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * $$$$$$\ $$$$$$$\ $$$$$$\ - * $$ __$$\ $$ __$$\ $$ __$$\ - * $$ / \__|$$ | $$ |$$ / \__| - * $$ | $$$$$$$ |$$ |$$$$\ - * $$ | $$ ____/ $$ |\_$$ | - * $$ | $$\ $$ | $$ | $$ | - * \$$$$$ |$$ | \$$$$$ | - * \______/ \__| \______/ - * - */ +// +// Copyright (c) 2021, Fraunhofer AISEC. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// $$$$$$\ $$$$$$$\ $$$$$$\ +// $$ __$$\ $$ __$$\ $$ __$$\ +// $$ / \__|$$ | $$ |$$ / \__| +// $$ | $$$$$$$ |$$ |$$$$\ +// $$ | $$ ____/ $$ |\_$$ | +// $$ | $$\ $$ | $$ | $$ | +// \$$$$$ |$$ | \$$$$$ | +// \______/ \__| \______/ +// +// + package cpg import ( diff --git a/cpg-language-go/src/main/golang/node.go b/cpg-language-go/src/main/golang/node.go index 03c86dc51a..11f7f260e6 100644 --- a/cpg-language-go/src/main/golang/node.go +++ b/cpg-language-go/src/main/golang/node.go @@ -1,28 +1,29 @@ -/* - * Copyright (c) 2021, Fraunhofer AISEC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * $$$$$$\ $$$$$$$\ $$$$$$\ - * $$ __$$\ $$ __$$\ $$ __$$\ - * $$ / \__|$$ | $$ |$$ / \__| - * $$ | $$$$$$$ |$$ |$$$$\ - * $$ | $$ ____/ $$ |\_$$ | - * $$ | $$\ $$ | $$ | $$ | - * \$$$$$ |$$ | \$$$$$ | - * \______/ \__| \______/ - * - */ +// +// Copyright (c) 2021, Fraunhofer AISEC. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// $$$$$$\ $$$$$$$\ $$$$$$\ +// $$ __$$\ $$ __$$\ $$ __$$\ +// $$ / \__|$$ | $$ |$$ / \__| +// $$ | $$$$$$$ |$$ |$$$$\ +// $$ | $$ ____/ $$ |\_$$ | +// $$ | $$\ $$ | $$ | $$ | +// \$$$$$ |$$ | \$$$$$ | +// \______/ \__| \______/ +// +// + package cpg import ( diff --git a/cpg-language-go/src/main/golang/scope.go b/cpg-language-go/src/main/golang/scope.go index 95b9312fd4..4915f127dd 100644 --- a/cpg-language-go/src/main/golang/scope.go +++ b/cpg-language-go/src/main/golang/scope.go @@ -1,28 +1,29 @@ -/* - * Copyright (c) 2021, Fraunhofer AISEC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * $$$$$$\ $$$$$$$\ $$$$$$\ - * $$ __$$\ $$ __$$\ $$ __$$\ - * $$ / \__|$$ | $$ |$$ / \__| - * $$ | $$$$$$$ |$$ |$$$$\ - * $$ | $$ ____/ $$ |\_$$ | - * $$ | $$\ $$ | $$ | $$ | - * \$$$$$ |$$ | \$$$$$ | - * \______/ \__| \______/ - * - */ +// +// Copyright (c) 2021, Fraunhofer AISEC. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// $$$$$$\ $$$$$$$\ $$$$$$\ +// $$ __$$\ $$ __$$\ $$ __$$\ +// $$ / \__|$$ | $$ |$$ / \__| +// $$ | $$$$$$$ |$$ |$$$$\ +// $$ | $$ ____/ $$ |\_$$ | +// $$ | $$\ $$ | $$ | $$ | +// \$$$$$ |$$ | \$$$$$ | +// \______/ \__| \______/ +// +// + package cpg import "tekao.net/jnigi" diff --git a/cpg-language-go/src/main/golang/statements.go b/cpg-language-go/src/main/golang/statements.go index 35bfcb001a..85bf95dbb2 100644 --- a/cpg-language-go/src/main/golang/statements.go +++ b/cpg-language-go/src/main/golang/statements.go @@ -1,28 +1,29 @@ -/* - * Copyright (c) 2021, Fraunhofer AISEC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * $$$$$$\ $$$$$$$\ $$$$$$\ - * $$ __$$\ $$ __$$\ $$ __$$\ - * $$ / \__|$$ | $$ |$$ / \__| - * $$ | $$$$$$$ |$$ |$$$$\ - * $$ | $$ ____/ $$ |\_$$ | - * $$ | $$\ $$ | $$ | $$ | - * \$$$$$ |$$ | \$$$$$ | - * \______/ \__| \______/ - * - */ +// +// Copyright (c) 2021, Fraunhofer AISEC. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// $$$$$$\ $$$$$$$\ $$$$$$\ +// $$ __$$\ $$ __$$\ $$ __$$\ +// $$ / \__|$$ | $$ |$$ / \__| +// $$ | $$$$$$$ |$$ |$$$$\ +// $$ | $$ ____/ $$ |\_$$ | +// $$ | $$\ $$ | $$ | $$ | +// \$$$$$ |$$ | \$$$$$ | +// \______/ \__| \______/ +// +// + package cpg import ( diff --git a/cpg-language-go/src/main/golang/types.go b/cpg-language-go/src/main/golang/types.go index fe1485c284..f7f037dc67 100644 --- a/cpg-language-go/src/main/golang/types.go +++ b/cpg-language-go/src/main/golang/types.go @@ -1,33 +1,33 @@ -/* - * Copyright (c) 2021, Fraunhofer AISEC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * $$$$$$\ $$$$$$$\ $$$$$$\ - * $$ __$$\ $$ __$$\ $$ __$$\ - * $$ / \__|$$ | $$ |$$ / \__| - * $$ | $$$$$$$ |$$ |$$$$\ - * $$ | $$ ____/ $$ |\_$$ | - * $$ | $$\ $$ | $$ | $$ | - * \$$$$$ |$$ | \$$$$$ | - * \______/ \__| \______/ - * - */ +// +// Copyright (c) 2021, Fraunhofer AISEC. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// $$$$$$\ $$$$$$$\ $$$$$$\ +// $$ __$$\ $$ __$$\ $$ __$$\ +// $$ / \__|$$ | $$ |$$ / \__| +// $$ | $$$$$$$ |$$ |$$$$\ +// $$ | $$ ____/ $$ |\_$$ | +// $$ | $$\ $$ | $$ | $$ | +// \$$$$$ |$$ | \$$$$$ | +// \______/ \__| \______/ +// +// + package cpg import ( "C" - "tekao.net/jnigi" ) import ( @@ -85,36 +85,34 @@ func InitEnv(e *jnigi.Env) { env = e } -func TypeParser_createFrom(s string, l *Language, ctx *TranslationContext) *Type { - var t Type - err := env.CallStaticMethod(TypeParserClass, "createFrom", &t, NewString(s), l, false, ctx) +func (t *Type) GetRoot() *Type { + var root Type + err := t.CallMethod(env, "getRoot", &root) if err != nil { log.Fatal(err) - } - return &t + return &root } -func UnknownType_getUnknown(l *Language) *UnknownType { - var t UnknownType - err := env.CallStaticMethod(UnknownTypeClass, "getUnknownType", &t, l) - if err != nil { - log.Fatal(err) - +func (h *HasType) SetType(t *Type) { + if t != nil || !t.IsNil() { + err := (*jnigi.ObjectRef)(h).CallMethod(env, "setType", nil, t.Cast(TypeClass)) + if err != nil { + panic(err) + } } - return &t } -func (t *Type) GetRoot() *Type { - var root Type - err := t.CallMethod(env, "getRoot", &root) +func (h *HasType) GetType() *Type { + var t Type + err := (*jnigi.ObjectRef)(h).CallMethod(env, "getType", &t) if err != nil { log.Fatal(err) } - return &root + return &t } func (t *Type) Reference(o *jnigi.ObjectRef) *Type { @@ -128,31 +126,12 @@ func (t *Type) Reference(o *jnigi.ObjectRef) *Type { return &refType } -func (h *HasType) SetType(t *Type) { - if t != nil { - (*jnigi.ObjectRef)(h).CallMethod(env, "setType", nil, t.Cast(TypeClass)) - } -} - -func (h *HasType) GetType() *Type { - var t Type - err := (*jnigi.ObjectRef)(h).CallMethod(env, "getType", &t) - if err != nil { - log.Fatal(err) - } - - return &t -} - func (t *Type) GetName() (fn *Name) { return (*Node)(t.ObjectRef).GetName() } func (t *ObjectType) AddGeneric(g *Type) { - // Stupid workaround, since casting does not work. See - // https://github.com/timob/jnigi/issues/60 - var objType = jnigi.WrapJObject(uintptr(t.JObject()), ObjectTypeClass, false) - err := objType.CallMethod(env, "addGeneric", nil, g.Cast(TypeClass)) + err := t.Cast(ObjectTypeClass).CallMethod(env, "addGeneric", nil, g.Cast(TypeClass)) if err != nil { log.Fatal(err) } diff --git a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontend.kt b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontend.kt index e14db7c339..778793e9bd 100644 --- a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontend.kt +++ b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontend.kt @@ -32,8 +32,8 @@ import de.fraunhofer.aisec.cpg.frontends.SupportsParallelParsing import de.fraunhofer.aisec.cpg.frontends.TranslationException import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration -import de.fraunhofer.aisec.cpg.graph.newUnknownType import de.fraunhofer.aisec.cpg.graph.types.Type +import de.fraunhofer.aisec.cpg.graph.unknownType import de.fraunhofer.aisec.cpg.passes.GoExtraPass import de.fraunhofer.aisec.cpg.passes.order.RegisterExtraPass import de.fraunhofer.aisec.cpg.sarif.PhysicalLocation @@ -93,7 +93,7 @@ class GoLanguageFrontend(language: Language, ctx: Translatio override fun typeOf(type: Any): Type { // this is handled by native code - return newUnknownType() + return unknownType() } override fun codeOf(astNode: Any): String? { diff --git a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoExtraPass.kt b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoExtraPass.kt index c422085675..a0c7bf15de 100644 --- a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoExtraPass.kt +++ b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoExtraPass.kt @@ -143,7 +143,7 @@ class GoExtraPass(ctx: TranslationContext) : ComponentPass(ctx), ScopeProvider { // The key is the first variable. It is always an int val keyVariable = variable.declarations.firstOrNull() as? VariableDeclaration - keyVariable?.type = forEach.parseType("int") + keyVariable?.type = forEach.primitiveType("int") // The value is the second one. Its type depends on the array type val valueVariable = @@ -254,7 +254,7 @@ class GoExtraPass(ctx: TranslationContext) : ComponentPass(ctx), ScopeProvider { ) { val cast = parent.newCastExpression(call.code) cast.location = call.location - cast.castType = call.parseType(typeName.toString()) + cast.castType = call.objectType(typeName) cast.expression = call.arguments.single() if (parent !is ArgumentHolder) { diff --git a/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt b/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt index c2dab38819..dca2e02f44 100644 --- a/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt +++ b/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt @@ -128,7 +128,7 @@ class GoLanguageFrontendTest : BaseTest() { assertNotNull(decl) val new = assertIs(decl.firstAssignment) - assertEquals(tu.parseType("p.MyStruct*"), new.type) + with(tu) { assertEquals(objectType("p.MyStruct").pointer(), new.type) } val construct = new.initializer as? ConstructExpression assertNotNull(construct) @@ -141,7 +141,7 @@ class GoLanguageFrontendTest : BaseTest() { var make = assertIs(decl.firstAssignment) assertNotNull(make) - assertEquals(tu.parseType("int[]"), make.type) + with(tu) { assertEquals(tu.primitiveType("int").array(), make.type) } assertTrue(make is ArrayCreationExpression) @@ -159,7 +159,10 @@ class GoLanguageFrontendTest : BaseTest() { assertTrue(make is ConstructExpression) // TODO: Maps can have dedicated types and parsing them as a generic here is only a // temporary solution. This should be fixed in the future. - assertEquals(tu.parseType("map[string,string]"), make.type) + assertEquals( + tu.objectType("map", listOf(tu.primitiveType("string"), tu.primitiveType("string"))), + make.type + ) // make channel @@ -169,7 +172,7 @@ class GoLanguageFrontendTest : BaseTest() { make = assertIs(decl.firstAssignment) assertNotNull(make) assertTrue(make is ConstructExpression) - assertEquals(tu.parseType("chan[int]"), make.type) + assertEquals(tu.objectType("chan", listOf(tu.primitiveType("int"))), make.type) } @Test @@ -190,26 +193,26 @@ class GoLanguageFrontendTest : BaseTest() { assertNotNull(a.location) assertLocalName("a", a) - assertEquals(tu.parseType("int"), a.type) + assertEquals(tu.primitiveType("int"), a.type) val s = p.variables["s"] assertNotNull(s) assertLocalName("s", s) - assertEquals(tu.parseType("string"), s.type) + assertEquals(tu.primitiveType("string"), s.type) val f = p.variables["f"] assertNotNull(f) assertLocalName("f", f) - assertEquals(tu.parseType("float64"), f.type) + assertEquals(tu.primitiveType("float64"), f.type) val f32 = p.variables["f32"] assertNotNull(f32) assertLocalName("f32", f32) - assertEquals(tu.parseType("float32"), f32.type) + assertEquals(tu.primitiveType("float32"), f32.type) val n = p.variables["n"] assertNotNull(n) - assertEquals(tu.parseType("int*"), n.type) + with(tu) { assertEquals(tu.primitiveType("int").pointer(), n.type) } val nil = n.initializer as? Literal<*> assertNotNull(nil) @@ -275,7 +278,7 @@ class GoLanguageFrontendTest : BaseTest() { val s = myTest.parameters.first() assertNotNull(s) assertLocalName("s", s) - assertEquals(tu.parseType("string"), s.type) + assertEquals(tu.primitiveType("string"), s.type) assertLocalName("myTest", myTest) @@ -292,7 +295,7 @@ class GoLanguageFrontendTest : BaseTest() { assertNotNull(literal) assertEquals("%s", literal.value) - assertEquals(tu.parseType("string"), literal.type) + assertEquals(tu.primitiveType("string"), literal.type) val ref = callExpression.arguments[1] as? DeclaredReferenceExpression assertNotNull(ref) @@ -359,7 +362,7 @@ class GoLanguageFrontendTest : BaseTest() { val myField = fields.first() assertLocalName("MyField", myField) - assertEquals(tu.parseType("int"), myField.type) + assertEquals(tu.primitiveType("int"), myField.type) val myInterface = p.getDeclarationsByName("p.MyInterface", RecordDeclaration::class.java) @@ -460,7 +463,7 @@ class GoLanguageFrontendTest : BaseTest() { assertNotNull(lhs) assertEquals(myFunc.receiver, (lhs.base as? DeclaredReferenceExpression)?.refersTo) assertLocalName("Field", lhs) - assertEquals(tu.parseType("int"), lhs.type) + assertEquals(tu.primitiveType("int"), lhs.type) val rhs = assign.rhs.firstOrNull() as? DeclaredReferenceExpression assertNotNull(rhs) @@ -488,7 +491,7 @@ class GoLanguageFrontendTest : BaseTest() { assertNotNull(b) assertLocalName("b", b) - assertEquals(tu.parseType("bool"), b.type) + assertEquals(tu.primitiveType("bool"), b.type) // true, false are builtin variables, NOT literals in Golang // we might need to parse this special case differently @@ -586,8 +589,10 @@ class GoLanguageFrontendTest : BaseTest() { val c = body.variables["c"] assertNotNull(c) - // type will be inferred from the function declaration - assertEquals(tu.parseType("p.MyStruct*"), c.type) + with(tu) { + // type will be inferred from the function declaration + assertEquals(objectType("p.MyStruct").pointer(), c.type) + } val newMyStruct = assertIs(c.firstAssignment) diff --git a/cpg-language-go/src/test/resources/log4j2.xml b/cpg-language-go/src/test/resources/log4j2.xml index 359d8071bf..747860628a 100644 --- a/cpg-language-go/src/test/resources/log4j2.xml +++ b/cpg-language-go/src/test/resources/log4j2.xml @@ -2,7 +2,7 @@ - + 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 0ef6768d38..2e6ab669df 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 @@ -46,9 +46,7 @@ import de.fraunhofer.aisec.cpg.graph.scopes.RecordScope import de.fraunhofer.aisec.cpg.graph.types.FunctionType.Companion.computeType import de.fraunhofer.aisec.cpg.graph.types.ParameterizedType import de.fraunhofer.aisec.cpg.graph.types.Type -import de.fraunhofer.aisec.cpg.graph.types.UnknownType import java.util.function.Supplier -import java.util.stream.Collectors import kotlin.collections.set open class DeclarationHandler(lang: JavaLanguageFrontend) : @@ -71,10 +69,7 @@ open class DeclarationHandler(lang: JavaLanguageFrontend) : frontend.scopeManager.enterScope(declaration) createMethodReceiver(currentRecordDecl, declaration) declaration.addThrowTypes( - constructorDecl.thrownExceptions - .stream() - .map { type: ReferenceType -> this.parseType(type.asString()) } - .collect(Collectors.toList()) + constructorDecl.thrownExceptions.map { type: ReferenceType -> frontend.typeOf(type) } ) for (parameter in constructorDecl.parameters) { val param = @@ -88,9 +83,11 @@ open class DeclarationHandler(lang: JavaLanguageFrontend) : frontend.scopeManager.addDeclaration(param) } - val name = frontend.scopeManager.firstScopeOrNull { it is RecordScope }?.astNode?.name - if (name != null) { - val type = this.parseType(name) + val record = + frontend.scopeManager.firstScopeOrNull { it is RecordScope }?.astNode + as? RecordDeclaration + if (record != null) { + val type = record.toType() declaration.type = type } @@ -118,10 +115,7 @@ open class DeclarationHandler(lang: JavaLanguageFrontend) : frontend.scopeManager.enterScope(functionDeclaration) createMethodReceiver(currentRecordDecl, functionDeclaration) functionDeclaration.addThrowTypes( - methodDecl.thrownExceptions - .stream() - .map { type: ReferenceType -> this.parseType(type.asString()) } - .collect(Collectors.toList()) + methodDecl.thrownExceptions.map { type: ReferenceType -> frontend.typeOf(type) } ) for (parameter in methodDecl.parameters) { var resolvedType: Type? = @@ -170,7 +164,7 @@ open class DeclarationHandler(lang: JavaLanguageFrontend) : val receiver = this.newVariableDeclaration( "this", - if (recordDecl != null) this.parseType(recordDecl.name) else newUnknownType(), + recordDecl?.toType() ?: unknownType(), "this", false ) @@ -289,12 +283,10 @@ open class DeclarationHandler(lang: JavaLanguageFrontend) : // is stored as an implicit field. if (frontend.scopeManager.currentScope is RecordScope) { // Get all the information of the outer class (its name and the respective type). We - // need this - // to generate the field. + // need this to generate the field. val scope = frontend.scopeManager.currentScope as RecordScope? if (scope?.name != null) { - val fieldType = - scope.name?.let { this.parseType(it) } ?: UnknownType.getUnknownType(language) + val fieldType = scope.name?.let { this.objectType(it) } ?: unknownType() // Enter the scope of the inner class because the new field belongs there. frontend.scopeManager.enterScope(recordDeclaration) @@ -322,7 +314,6 @@ open class DeclarationHandler(lang: JavaLanguageFrontend) : // TODO: can field have more than one variable? val variable = fieldDecl.getVariable(0) val modifiers = fieldDecl.modifiers.map { modifier -> modifier.keyword.asString() } - val joinedModifiers = java.lang.String.join(" ", modifiers) + " " val location = frontend.locationOf(fieldDecl) val initializer = variable.initializer @@ -336,23 +327,23 @@ open class DeclarationHandler(lang: JavaLanguageFrontend) : frontend.scopeManager.currentRecord, variable.resolve().type.describe() ) - ?: this.parseType(joinedModifiers + variable.resolve().type.describe()) + ?: frontend.typeOf(variable.resolve().type) } catch (e: UnsolvedSymbolException) { val t = frontend.recoverTypeFromUnsolvedException(e) if (t == null) { log.warn("Could not resolve type for {}", variable) - type = this.parseType(joinedModifiers + variable.type.asString()) + type = frontend.typeOf(variable.type) } else { - type = this.parseType(joinedModifiers + t) + type = this.objectType(t) type.typeOrigin = Type.Origin.GUESSED } } catch (e: UnsupportedOperationException) { val t = frontend.recoverTypeFromUnsolvedException(e) if (t == null) { log.warn("Could not resolve type for {}", variable) - type = this.parseType(joinedModifiers + variable.type.asString()) + type = frontend.typeOf(variable.type) } else { - type = this.parseType(joinedModifiers + t) + type = this.objectType(t) type.typeOrigin = Type.Origin.GUESSED } } @@ -378,7 +369,7 @@ open class DeclarationHandler(lang: JavaLanguageFrontend) : val enumDeclaration = this.newEnumDeclaration(name, enumDecl.toString(), location) val entries = enumDecl.entries.mapNotNull { handle(it) as EnumConstantDeclaration? } - entries.forEach { it.type = this.parseType(enumDeclaration.name) } + entries.forEach { it.type = this.objectType(enumDeclaration.name) } enumDeclaration.entries = entries val superTypes = enumDecl.implementedTypes.map { frontend.getTypeAsGoodAsPossible(it) } enumDeclaration.superTypes = superTypes 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 1428eae0d4..2b0978b562 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 @@ -92,7 +92,7 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : if (castExpr.type.isPrimitiveType) { // Set Type based on the Casting type as it will result in a conversion for primitive // types - castExpression.type = this.parseType(castExpr.type.resolve().asPrimitive().describe()) + castExpression.type = frontend.typeOf(castExpr.type.resolve().asPrimitive()) } else { // Get Runtime type from cast expression for complex types; castExpression.expression.registerTypeListener(castExpression) @@ -167,20 +167,20 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : val conditionalExpr = expr.asConditionalExpr() val superType: Type = try { - this.parseType(conditionalExpr.calculateResolvedType().describe()) + frontend.typeOf(conditionalExpr.calculateResolvedType()) } catch (e: RuntimeException) { val s = frontend.recoverTypeFromUnsolvedException(e) if (s != null) { - this.parseType(s) + this.objectType(s) } else { - newUnknownType() + unknownType() } } catch (e: NoClassDefFoundError) { val s = frontend.recoverTypeFromUnsolvedException(e) if (s != null) { - this.parseType(s) + this.objectType(s) } else { - newUnknownType() + unknownType() } } val condition = @@ -270,12 +270,12 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : if (resolve.asField().isStatic) { isStaticAccess = true } - baseType = this.parseType(resolve.asField().declaringType().qualifiedName) + baseType = this.objectType(resolve.asField().declaringType().qualifiedName) } catch (ex: RuntimeException) { isStaticAccess = true val typeString = frontend.recoverTypeFromUnsolvedException(ex) if (typeString != null) { - baseType = this.parseType(typeString) + baseType = this.objectType(typeString) } else { // try to get the name val name: String @@ -289,17 +289,17 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : val qualifiedNameFromImports = frontend.getQualifiedNameFromImports(name) baseType = if (qualifiedNameFromImports != null) { - this.parseType(qualifiedNameFromImports) + this.objectType(qualifiedNameFromImports) } else { log.info("Unknown base type 1 for {}", fieldAccessExpr) - newUnknownType() + unknownType() } } } catch (ex: NoClassDefFoundError) { isStaticAccess = true val typeString = frontend.recoverTypeFromUnsolvedException(ex) if (typeString != null) { - baseType = this.parseType(typeString) + baseType = this.objectType(typeString) } else { val name: String val tokenRange = scope.asNameExpr().tokenRange @@ -312,10 +312,10 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : val qualifiedNameFromImports = frontend.getQualifiedNameFromImports(name) baseType = if (qualifiedNameFromImports != null) { - this.parseType(qualifiedNameFromImports) + this.objectType(qualifiedNameFromImports) } else { log.info("Unknown base type 1 for {}", fieldAccessExpr) - newUnknownType() + unknownType() } } } @@ -351,10 +351,10 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : val qualifiedNameFromImports = frontend.getQualifiedNameFromImports(name) val baseType = if (qualifiedNameFromImports != null) { - this.parseType(qualifiedNameFromImports) + this.objectType(qualifiedNameFromImports) } else { log.info("Unknown base type 2 for {}", fieldAccessExpr) - newUnknownType() + unknownType() } base = this.newDeclaredReferenceExpression( @@ -379,18 +379,18 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : symbol.asField().type.describe() ) if (fieldType == null) { - fieldType = this.parseType(symbol.asField().type.describe()) + fieldType = frontend.typeOf(symbol.asField().type) } } catch (ex: RuntimeException) { val typeString = frontend.recoverTypeFromUnsolvedException(ex) fieldType = if (typeString != null) { - this.parseType(typeString) + this.objectType(typeString) } else if (fieldAccessExpr.toString().endsWith(".length")) { - this.parseType("int") + this.primitiveType("int") } else { log.info("Unknown field type for {}", fieldAccessExpr) - newUnknownType() + unknownType() } val memberExpression = this.newMemberExpression( @@ -405,12 +405,12 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : val typeString = frontend.recoverTypeFromUnsolvedException(ex) fieldType = if (typeString != null) { - this.parseType(typeString) + this.objectType(typeString) } else if (fieldAccessExpr.toString().endsWith(".length")) { - this.parseType("int") + this.primitiveType("int") } else { log.info("Unknown field type for {}", fieldAccessExpr) - newUnknownType() + unknownType() } val memberExpression = this.newMemberExpression(fieldAccessExpr.name.identifier, base, fieldType, ".") @@ -430,43 +430,47 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : is IntegerLiteralExpr -> newLiteral( literalExpr.asIntegerLiteralExpr().asNumber(), - this.parseType("int"), + this.primitiveType("int"), value ) is StringLiteralExpr -> newLiteral( literalExpr.asStringLiteralExpr().asString(), - this.parseType("java.lang.String"), + this.primitiveType("java.lang.String"), value ) is BooleanLiteralExpr -> newLiteral( literalExpr.asBooleanLiteralExpr().value, - this.parseType("boolean"), + this.primitiveType("boolean"), value ) is CharLiteralExpr -> - newLiteral(literalExpr.asCharLiteralExpr().asChar(), this.parseType("char"), value) + newLiteral( + literalExpr.asCharLiteralExpr().asChar(), + this.primitiveType("char"), + value + ) is DoubleLiteralExpr -> newLiteral( literalExpr.asDoubleLiteralExpr().asDouble(), - this.parseType("double"), + this.primitiveType("double"), value ) is LongLiteralExpr -> newLiteral( literalExpr.asLongLiteralExpr().asNumber(), - this.parseType("long"), + this.primitiveType("long"), value ) - is NullLiteralExpr -> newLiteral(null, this.parseType("null"), value) + is NullLiteralExpr -> newLiteral(null, this.objectType("null"), value) else -> null } } private fun handleClassExpression(expr: Expression): DeclaredReferenceExpression { val classExpr = expr.asClassExpr() - val type = this.parseType(classExpr.type.asString()) + val type = frontend.typeOf(classExpr.type) val thisExpression = this.newDeclaredReferenceExpression( classExpr.toString().substring(classExpr.toString().lastIndexOf('.') + 1), @@ -481,7 +485,7 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : private fun handleThisExpression(expr: Expression): DeclaredReferenceExpression { val thisExpr = expr.asThisExpr() val resolvedValueDeclaration = thisExpr.resolve() - val type = this.parseType(resolvedValueDeclaration.qualifiedName) + val type = this.objectType(resolvedValueDeclaration.qualifiedName) var name = thisExpr.toString() // If the typeName is specified, then this a "qualified this" and we need to handle it @@ -503,7 +507,7 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : // about the inheritance structure. Thus, we delay the resolving to the variable resolving // process val superExpression = - this.newDeclaredReferenceExpression(expr.toString(), newUnknownType(), expr.toString()) + this.newDeclaredReferenceExpression(expr.toString(), unknownType(), expr.toString()) frontend.setCodeAndLocation(superExpression, expr) return superExpression } @@ -582,7 +586,7 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : symbol.type.describe() ) if (type == null) { - type = this.parseType(symbol.type.describe()) + type = frontend.typeOf(symbol.type) } this.newDeclaredReferenceExpression(symbol.name, type, nameExpr.toString()) } @@ -599,9 +603,9 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : } val t: Type if (typeString == null) { - t = newUnknownType() + t = unknownType() } else { - t = this.parseType(typeString) + t = this.objectType(typeString) t.typeOrigin = Type.Origin.GUESSED } val declaredReferenceExpression = @@ -612,11 +616,11 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : } declaredReferenceExpression } catch (ex: RuntimeException) { - val t = this.parseType("UNKNOWN4") // TODO: What's this? UNKNOWN4?? + val t = unknownType() log.info("Unresolved symbol: {}", nameExpr.nameAsString) this.newDeclaredReferenceExpression(nameExpr.nameAsString, t, nameExpr.toString()) } catch (ex: NoClassDefFoundError) { - val t = this.parseType("UNKNOWN4") + val t = unknownType() log.info("Unresolved symbol: {}", nameExpr.nameAsString) this.newDeclaredReferenceExpression(nameExpr.nameAsString, t, nameExpr.toString()) } @@ -637,7 +641,7 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : val rhs: de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression = this.newLiteral( typeAsGoodAsPossible.typeName, - this.parseType("class"), + this.objectType("class"), binaryExpr.typeAsString ) val binaryOperator = this.newBinaryOperator("instanceof", binaryExpr.toString()) @@ -747,7 +751,7 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : } else { this.parseName(qualifiedName).parent } - baseType = this.parseType(baseName ?: Type.UNKNOWN_TYPE_STRING) + baseType = this.objectType(baseName ?: Type.UNKNOWN_TYPE_STRING) base = this.newDeclaredReferenceExpression(baseName, baseType) } else { // Since it is possible to omit the "this" keyword, some methods in java do not have @@ -758,14 +762,14 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : base = createImplicitThis() } } - val member = this.newMemberExpression(name, base, newUnknownType(), ".") + val member = this.newMemberExpression(name, base, unknownType(), ".") frontend.setCodeAndLocation( member, methodCallExpr.name ) // This will also overwrite the code set to the empty string set above callExpression = this.newMemberCallExpression(member, isStatic, methodCallExpr.toString(), expr) - callExpression.type = typeString?.let { this.parseType(it) } ?: newUnknownType() + callExpression.type = typeString?.let { this.objectType(it) } ?: unknownType() val arguments = methodCallExpr.arguments // handle the arguments @@ -791,7 +795,7 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : val base: de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression val thisType = (frontend.scopeManager.currentFunction as MethodDeclaration?)?.receiver?.type - ?: newUnknownType() + ?: unknownType() base = this.newDeclaredReferenceExpression("this", thisType, "this") base.isImplicit = true return base @@ -843,7 +847,7 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : frontend.scopeManager.enterScope(anonymousRecord) - anonymousRecord.addSuperClass(parseType(constructorName)) + 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 diff --git a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguage.kt b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguage.kt index e932fdbc63..c1666370da 100644 --- a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguage.kt +++ b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguage.kt @@ -84,6 +84,8 @@ open class JavaLanguage : "char" to IntegerType("char", 16, this, NumericType.Modifier.UNSIGNED), "short" to IntegerType("short", 16, this, NumericType.Modifier.SIGNED), "int" to IntegerType("int", 32, this, NumericType.Modifier.SIGNED), + "java.lang.Integer" to + IntegerType("java.lang.Integer", 32, this, NumericType.Modifier.SIGNED), "long" to IntegerType("long", 64, this, NumericType.Modifier.SIGNED), // Floating-Point Types: 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 0d26100047..14354fb397 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 @@ -36,10 +36,15 @@ import com.github.javaparser.ast.expr.MethodCallExpr import com.github.javaparser.ast.expr.NameExpr import com.github.javaparser.ast.nodeTypes.NodeWithAnnotations import com.github.javaparser.ast.nodeTypes.NodeWithType -import com.github.javaparser.ast.type.Type +import com.github.javaparser.ast.type.* import com.github.javaparser.resolution.UnsolvedSymbolException import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration +import com.github.javaparser.resolution.types.ResolvedArrayType +import com.github.javaparser.resolution.types.ResolvedPrimitiveType +import com.github.javaparser.resolution.types.ResolvedReferenceType +import com.github.javaparser.resolution.types.ResolvedType +import com.github.javaparser.resolution.types.ResolvedVoidType import com.github.javaparser.symbolsolver.JavaSymbolSolver import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver @@ -65,6 +70,7 @@ import java.io.File import java.io.FileNotFoundException import java.io.IOException import java.util.function.Consumer +import kotlin.jvm.optionals.getOrNull /** Main parser for ONE Java file. */ @RegisterExtraPass( @@ -144,11 +150,6 @@ open class JavaLanguageFrontend(language: Language, ctx: T } } - override fun typeOf(type: Type): de.fraunhofer.aisec.cpg.graph.types.Type { - // reserved for future use - return newUnknownType() - } - @Throws(TranslationException::class, FileNotFoundException::class) fun parse(file: File?, parser: JavaParser): CompilationUnit { val result = parser.parse(file) @@ -211,8 +212,8 @@ open class JavaLanguageFrontend(language: Language, ctx: T return try { val type = nodeWithType.typeAsString if (type == "var") { - newUnknownType() - } else parseType(resolved.type.describe()) + unknownType() + } else typeOf(resolved.type) } catch (ex: RuntimeException) { getTypeFromImportIfPossible(nodeWithType.type) } catch (ex: NoClassDefFoundError) { @@ -223,8 +224,8 @@ open class JavaLanguageFrontend(language: Language, ctx: T fun getTypeAsGoodAsPossible(type: Type): de.fraunhofer.aisec.cpg.graph.types.Type { return try { if (type.toString() == "var") { - newUnknownType() - } else parseType(type.resolve().describe()) + unknownType() + } else typeOf(type.resolve()) } catch (ex: RuntimeException) { getTypeFromImportIfPossible(type) } catch (ex: NoClassDefFoundError) { @@ -350,7 +351,7 @@ open class JavaLanguageFrontend(language: Language, ctx: T resolved.returnType.describe() ) if (type == null) { - type = parseType(resolved.returnType.describe()) + type = typeOf(resolved.returnType) } type } catch (ex: RuntimeException) { @@ -384,7 +385,7 @@ open class JavaLanguageFrontend(language: Language, ctx: T // if this is not a ClassOrInterfaceType, just return if (!searchType.isClassOrInterfaceType || context == null) { log.warn("Unable to resolve type for {}", type.asString()) - val returnType = parseType(type.asString()) + val returnType = this.typeOf(type) returnType.typeOrigin = de.fraunhofer.aisec.cpg.graph.types.Type.Origin.GUESSED return returnType } @@ -394,23 +395,24 @@ open class JavaLanguageFrontend(language: Language, ctx: T for (importDeclaration in context?.imports ?: listOf()) { if (importDeclaration.name.identifier.endsWith(clazz.name.identifier)) { // TODO: handle type parameters - return parseType(importDeclaration.nameAsString) + return objectType(importDeclaration.nameAsString) } } - var name = clazz.asString() + val returnType = this.typeOf(clazz) // no import found, so our last guess is that the type is in the same package - // as our current translation unit + // as our current translation unit, so we can "adjust" the name to an FQN val o = context?.packageDeclaration if (o?.isPresent == true) { - name = o.get().nameAsString + language.namespaceDelimiter + name + returnType.name = + parseName(o.get().nameAsString + language.namespaceDelimiter + returnType.name) } - val returnType = parseType(name) + returnType.typeOrigin = de.fraunhofer.aisec.cpg.graph.types.Type.Origin.GUESSED return returnType } log.warn("Unable to resolve type for {}", type.asString()) - val returnType = parseType(type.asString()) + val returnType = this.typeOf(type) returnType.typeOrigin = de.fraunhofer.aisec.cpg.graph.types.Type.Origin.GUESSED return returnType } @@ -477,6 +479,32 @@ open class JavaLanguageFrontend(language: Language, ctx: T return list } + override fun typeOf(type: Type): de.fraunhofer.aisec.cpg.graph.types.Type { + return when (type) { + is ArrayType -> this.typeOf(type.elementType).array() + is VoidType -> incompleteType() + is PrimitiveType -> primitiveType(type.asString()) + is ClassOrInterfaceType -> + objectType( + type.nameAsString, + type.typeArguments.getOrNull()?.map { this.typeOf(it) } ?: listOf() + ) + is ReferenceType -> objectType(type.asString()) + else -> objectType(type.asString()) + } + } + + fun typeOf(type: ResolvedType): de.fraunhofer.aisec.cpg.graph.types.Type { + return when (type) { + is ResolvedArrayType -> typeOf(type.componentType).array() + is ResolvedVoidType -> incompleteType() + is ResolvedPrimitiveType -> primitiveType(type.describe()) + is ResolvedReferenceType -> + objectType(type.describe(), type.typeParametersValues().map { typeOf(it) }) + else -> objectType(type.describe()) + } + } + companion object { const val THIS = "this" const val ANNOTATION_MEMBER_VALUE = "value" 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 e340b8d935..1ef4739e85 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 @@ -234,7 +234,7 @@ class StatementHandler(lang: JavaLanguageFrontend?) : // and in // cpp StatementHandler if (statement.condition == null) { - val literal: Literal<*> = this.newLiteral(true, this.parseType("boolean"), "true") + val literal: Literal<*> = this.newLiteral(true, this.primitiveType("boolean"), "true") statement.condition = literal } if (forStmt.update.size > 1) { @@ -579,7 +579,7 @@ class StatementHandler(lang: JavaLanguageFrontend?) : } // we do not know which of the exceptions was actually thrown, so we assume this might // be any - concreteType = this.parseType("java.lang.Throwable") + concreteType = this.objectType("java.lang.Throwable") concreteType.typeOrigin = Type.Origin.GUESSED } else { concreteType = frontend.getTypeAsGoodAsPossible(catchCls.parameter.type) diff --git a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/JavaCallResolverHelper.kt b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/JavaCallResolverHelper.kt index 13a334c34c..d2940fa7dd 100644 --- a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/JavaCallResolverHelper.kt +++ b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/JavaCallResolverHelper.kt @@ -30,7 +30,7 @@ import de.fraunhofer.aisec.cpg.frontends.java.JavaLanguage import de.fraunhofer.aisec.cpg.graph.Name import de.fraunhofer.aisec.cpg.graph.declarations.MethodDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration -import de.fraunhofer.aisec.cpg.graph.parseType +import de.fraunhofer.aisec.cpg.graph.objectType import de.fraunhofer.aisec.cpg.graph.statements.expressions.DeclaredReferenceExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.MemberCallExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.MemberExpression @@ -111,7 +111,7 @@ class JavaCallResolverHelper { ): RecordDeclaration? { val baseName = callee.base.name.parent ?: return null - if (curClass.parseType(baseName) in curClass.implementedInterfaces) { + if (curClass.objectType(baseName) in curClass.implementedInterfaces) { // Basename is an interface -> BaseName.super refers to BaseName itself return recordMap[baseName] } else { diff --git a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/JavaExternalTypeHierarchyResolver.kt b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/JavaExternalTypeHierarchyResolver.kt index 55834f49d1..b6aeaa2525 100644 --- a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/JavaExternalTypeHierarchyResolver.kt +++ b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/JavaExternalTypeHierarchyResolver.kt @@ -76,7 +76,7 @@ class JavaExternalTypeHierarchyResolver(ctx: TranslationContext) : ComponentPass val resolvedSuperTypes = symbol.correspondingDeclaration.getAncestors(true) for (anc in resolvedSuperTypes) { // Add all resolved supertypes to the type. - val superType = provider.parseType(anc.qualifiedName) + val superType = provider.objectType(anc.qualifiedName) superType.typeOrigin = Type.Origin.RESOLVED t.superTypes.add(superType) } diff --git a/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontendTest.kt b/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontendTest.kt index 5f9a5b5810..f538719246 100644 --- a/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontendTest.kt +++ b/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontendTest.kt @@ -142,7 +142,7 @@ internal class JavaLanguageFrontendTest : BaseTest() { val sDecl = s.singleDeclaration as? VariableDeclaration assertNotNull(sDecl) assertLocalName("s", sDecl) - assertEquals(tu.parseType("java.lang.String"), sDecl.type) + assertEquals(tu.primitiveType("java.lang.String"), sDecl.type) // should contain a single statement val sce = forEachStatement.statement as? MemberCallExpression @@ -189,11 +189,11 @@ internal class JavaLanguageFrontendTest : BaseTest() { assertNotNull(scope) // first exception type was? resolved, so we can expect a FQN - assertEquals(tu.parseType("java.lang.NumberFormatException"), firstCatch.parameter?.type) + assertEquals(tu.objectType("java.lang.NumberFormatException"), firstCatch.parameter?.type) // second one could not be resolved so we do not have an FQN - assertEquals(tu.parseType("NotResolvableTypeException"), catchClauses[1].parameter?.type) + assertEquals(tu.objectType("NotResolvableTypeException"), catchClauses[1].parameter?.type) // third type should have been resolved through the import - assertEquals(tu.parseType("some.ImportedException"), (catchClauses[2].parameter)?.type) + assertEquals(tu.objectType("some.ImportedException"), (catchClauses[2].parameter)?.type) // and 1 finally val finallyBlock = tryStatement.finallyBlock @@ -303,7 +303,7 @@ internal class JavaLanguageFrontendTest : BaseTest() { assertNotNull(method) assertEquals(recordDeclaration, method.recordDeclaration) assertLocalName("method", method) - assertEquals(tu.parseType("java.lang.Integer"), method.returnTypes.firstOrNull()) + assertEquals(tu.primitiveType("java.lang.Integer"), method.returnTypes.firstOrNull()) val functionType = method.type as? FunctionType assertNotNull(functionType) @@ -424,8 +424,10 @@ internal class JavaLanguageFrontendTest : BaseTest() { val a = (statements[0] as? DeclarationStatement)?.singleDeclaration as? VariableDeclaration assertNotNull(a) - // type should be Integer[] - assertEquals(tu.parseType("int[]"), a.type) + with(tu) { + // type should be Integer[] + assertEquals(primitiveType("int").array(), a.type) + } // it has an array creation initializer val ace = a.initializer as? ArrayCreationExpression @@ -633,7 +635,7 @@ internal class JavaLanguageFrontendTest : BaseTest() { (lhs?.base as? DeclaredReferenceExpression)?.refersTo as? VariableDeclaration? assertNotNull(receiver) assertLocalName("this", receiver) - assertEquals(tu.parseType("my.Animal"), receiver.type) + assertEquals(tu.objectType("my.Animal"), receiver.type) } @Test diff --git a/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypeTests.kt b/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypeTests.kt index 65ebf1fbf3..5302853f54 100644 --- a/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypeTests.kt +++ b/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypeTests.kt @@ -38,96 +38,6 @@ import kotlin.test.* internal class TypeTests : BaseTest() { - @Test - fun createFromJava() { - var result: Type - var expected: Type - - with( - JavaLanguageFrontend( - JavaLanguage(), - TranslationContext( - TranslationConfiguration.builder().build(), - ScopeManager(), - TypeManager() - ) - ) - ) { - // Test 1: Ignore Access Modifier Keyword (public, private, protected) - var typeString = "private int a" - result = parseType(typeString) - expected = IntegerType("int", 32, JavaLanguage(), NumericType.Modifier.SIGNED) - assertEquals(expected, result) - - // Test 2: constant type using final - typeString = "final int a" - result = parseType(typeString) - expected = IntegerType("int", 32, JavaLanguage(), NumericType.Modifier.SIGNED) - assertEquals(expected, result) - - // Test 3: static type - typeString = "static int a" - result = parseType(typeString) - expected = IntegerType("int", 32, JavaLanguage(), NumericType.Modifier.SIGNED) - assertEquals(expected, result) - - // Test 4: volatile type - typeString = "public volatile int a" - result = parseType(typeString) - expected = IntegerType("int", 32, JavaLanguage(), NumericType.Modifier.SIGNED) - assertEquals(expected, result) - - // Test 5: combining a storage type and a qualifier - typeString = "private static final String a" - result = parseType(typeString) - expected = StringType("java.lang.String", JavaLanguage()) - assertEquals(expected, result) - - // Test 6: using two different qualifiers - typeString = "public final volatile int a" - result = parseType(typeString) - expected = IntegerType("int", 32, JavaLanguage(), NumericType.Modifier.SIGNED) - assertEquals(expected, result) - - // Test 7: Reference level using arrays - typeString = "int[] a" - result = parseType(typeString) - expected = - PointerType( - IntegerType("int", 32, JavaLanguage(), NumericType.Modifier.SIGNED), - PointerType.PointerOrigin.ARRAY - ) - assertEquals(expected, result) - - // Test 8: generics - typeString = "List list" - result = parseType(typeString) - var generics = mutableListOf() - generics.add(StringType("java.lang.String", JavaLanguage())) - expected = ObjectType("List", generics, false, JavaLanguage()) - assertEquals(expected, result) - - // Test 9: more generics - typeString = "List>, List> data" - result = parseType(typeString) - val genericStringType = StringType("java.lang.String", JavaLanguage()) - val generics3: MutableList = ArrayList() - generics3.add(genericStringType) - val genericElement3 = ObjectType("List", generics3, false, JavaLanguage()) - val generics2a: MutableList = ArrayList() - generics2a.add(genericElement3) - val generics2b: MutableList = ArrayList() - generics2b.add(genericStringType) - val genericElement1 = ObjectType("List", generics2a, false, JavaLanguage()) - val genericElement2 = ObjectType("List", generics2b, false, JavaLanguage()) - generics = ArrayList() - generics.add(genericElement1) - generics.add(genericElement2) - expected = ObjectType("List", generics, false, JavaLanguage()) - assertEquals(expected, result) - } - } - // Tests on the resulting graph @Test @Throws(Exception::class) @@ -224,12 +134,12 @@ internal class TypeTests : BaseTest() { ) { val topLevel = Path.of("src", "test", "resources", "compiling", "hierarchy") val result = analyze("java", topLevel, true) { it.registerLanguage(JavaLanguage()) } - val root = parseType("multistep.Root") - val level0 = parseType("multistep.Level0") - val level1 = parseType("multistep.Level1") - val level1b = parseType("multistep.Level1B") - val level2 = parseType("multistep.Level2") - val unrelated = parseType("multistep.Unrelated") + val root = objectType("multistep.Root") + val level0 = objectType("multistep.Level0") + val level1 = objectType("multistep.Level1") + val level1b = objectType("multistep.Level1B") + val level2 = objectType("multistep.Level2") + val unrelated = objectType("multistep.Unrelated") getCommonTypeTestGeneral( root, level0, diff --git a/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/CallResolverTest.kt b/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/CallResolverTest.kt index 8bd719e38c..dc47080ec8 100644 --- a/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/CallResolverTest.kt +++ b/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/CallResolverTest.kt @@ -124,8 +124,8 @@ class CallResolverTest : BaseTest() { assertNotNull(tu) val records = result.records - val intType = tu.parseType("int") - val stringType = tu.parseType(("java.lang.String")) + val intType = tu.primitiveType("int") + val stringType = tu.primitiveType(("java.lang.String")) testMethods(records, intType, stringType) testOverriding(records) ensureNoUnknownClassDummies(records) diff --git a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/DeclarationHandler.kt b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/DeclarationHandler.kt index e547148e18..69754747d4 100644 --- a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/DeclarationHandler.kt +++ b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/DeclarationHandler.kt @@ -106,7 +106,7 @@ class DeclarationHandler(lang: LLVMIRLanguageFrontend) : val funcType = LLVMGetElementType(funcPtrType) val returnType = LLVMGetReturnType(funcType) - functionDeclaration.type = frontend.typeFrom(returnType) + functionDeclaration.type = frontend.typeOf(returnType) frontend.scopeManager.enterScope(functionDeclaration) @@ -215,7 +215,7 @@ class DeclarationHandler(lang: LLVMIRLanguageFrontend) : for (i in 0 until size) { val a = LLVMStructGetTypeAtIndex(typeRef, i) - val fieldType = frontend.typeFrom(a, alreadyVisited) + val fieldType = frontend.typeOf(a, alreadyVisited) // there are no names, so we need to invent some dummy ones for easier reading val fieldName = "field_$i" @@ -254,7 +254,7 @@ class DeclarationHandler(lang: LLVMIRLanguageFrontend) : for (i in 0 until size) { val field = LLVMStructGetTypeAtIndex(typeRef, i) - val fieldType = frontend.typeFrom(field, alreadyVisited) + val fieldType = frontend.typeOf(field, alreadyVisited) name += "_${fieldType.typeName}" } diff --git a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/ExpressionHandler.kt b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/ExpressionHandler.kt index c37c551af4..72be04e5b3 100644 --- a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/ExpressionHandler.kt +++ b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/ExpressionHandler.kt @@ -64,7 +64,7 @@ class ExpressionHandler(lang: LLVMIRLanguageFrontend) : newDeclaredReferenceExpression("poison", frontend.typeOf(value), "poison") } LLVMConstantTokenNoneValueKind -> - newLiteral(null, newUnknownType(), frontend.codeOf(value)) + newLiteral(null, unknownType(), frontend.codeOf(value)) LLVMUndefValueValueKind -> initializeAsUndef(frontend.typeOf(value), frontend.codeOf(value)) LLVMConstantAggregateZeroValueKind -> @@ -515,11 +515,10 @@ class ExpressionHandler(lang: LLVMIRLanguageFrontend) : } // our new base-type is the type of the field - baseType = field?.type ?: newUnknownType() + baseType = field?.type ?: unknownType() // construct our member expression - expr = - newMemberExpression(fieldName, base, field?.type ?: newUnknownType(), ".", "") + expr = newMemberExpression(fieldName, base, field?.type ?: unknownType(), ".", "") log.info("{}", expr) // the current expression is the new base diff --git a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontend.kt b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontend.kt index ed44dba0e2..65642a8d5d 100644 --- a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontend.kt +++ b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontend.kt @@ -29,13 +29,10 @@ import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend import de.fraunhofer.aisec.cpg.frontends.TranslationException -import de.fraunhofer.aisec.cpg.graph.Node +import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.Declaration import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration -import de.fraunhofer.aisec.cpg.graph.newTranslationUnitDeclaration -import de.fraunhofer.aisec.cpg.graph.newUnknownType -import de.fraunhofer.aisec.cpg.graph.parseType import de.fraunhofer.aisec.cpg.graph.statements.expressions.DeclaredReferenceExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression import de.fraunhofer.aisec.cpg.graph.types.* @@ -163,7 +160,7 @@ class LLVMIRLanguageFrontend(language: Language, ctx: Tr } override fun typeOf(type: LLVMTypeRef): Type { - return typeFrom(type, mutableMapOf()) + return typeOf(type, mutableMapOf()) } /** Returns a pair of the name and symbol name of [valueRef]. */ @@ -183,10 +180,10 @@ class LLVMIRLanguageFrontend(language: Language, ctx: Tr fun typeOf(valueRef: LLVMValueRef): Type { val typeRef = LLVMTypeOf(valueRef) - return typeFrom(typeRef) + return typeOf(typeRef) } - internal fun typeFrom( + internal fun typeOf( typeRef: LLVMTypeRef, alreadyVisited: MutableMap = mutableMapOf() ): Type { @@ -196,7 +193,7 @@ class LLVMIRLanguageFrontend(language: Language, ctx: Tr if (result != null) return result } if (typeRef in alreadyVisited) { - return alreadyVisited[typeRef] ?: newUnknownType() + return alreadyVisited[typeRef] ?: unknownType() } alreadyVisited[typeRef] = null val res: Type = @@ -204,19 +201,19 @@ class LLVMIRLanguageFrontend(language: Language, ctx: Tr LLVMVectorTypeKind, LLVMArrayTypeKind -> { // var length = LLVMGetArrayLength(typeRef) - val elementType = typeFrom(LLVMGetElementType(typeRef), alreadyVisited) - elementType.reference(PointerType.PointerOrigin.ARRAY) + val elementType = typeOf(LLVMGetElementType(typeRef), alreadyVisited) + elementType.array() } LLVMPointerTypeKind -> { - val elementType = typeFrom(LLVMGetElementType(typeRef), alreadyVisited) - elementType.reference(PointerType.PointerOrigin.POINTER) + val elementType = typeOf(LLVMGetElementType(typeRef), alreadyVisited) + elementType.pointer() } LLVMStructTypeKind -> { val record = declarationHandler.handleStructureType(typeRef, alreadyVisited) record.toType() } else -> { - parseType(typeStr) + objectType(typeStr) } } alreadyVisited[typeRef] = res diff --git a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/StatementHandler.kt b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/StatementHandler.kt index 2c027c2d8d..c5fe499ad1 100644 --- a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/StatementHandler.kt +++ b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/StatementHandler.kt @@ -573,7 +573,7 @@ class StatementHandler(lang: LLVMIRLanguageFrontend) : val cmpPred = when (LLVMGetFCmpPredicate(instr)) { LLVMRealPredicateFalse -> { - return newLiteral(false, parseType("i1"), "false") + return newLiteral(false, primitiveType("i1"), "false") } LLVMRealOEQ -> "==" LLVMRealOGT -> ">" @@ -608,7 +608,7 @@ class StatementHandler(lang: LLVMIRLanguageFrontend) : "!=" } LLVMRealPredicateTrue -> { - return newLiteral(true, parseType("i1"), "true") + return newLiteral(true, primitiveType("i1"), "true") } else -> "unknown" } @@ -645,7 +645,7 @@ class StatementHandler(lang: LLVMIRLanguageFrontend) : base = newDeclaredReferenceExpression( copy.singleDeclaration?.name?.localName, - (copy.singleDeclaration as? VariableDeclaration)?.type ?: newUnknownType(), + (copy.singleDeclaration as? VariableDeclaration)?.type ?: unknownType(), frontend.codeOf(instr) ) } @@ -695,7 +695,7 @@ class StatementHandler(lang: LLVMIRLanguageFrontend) : val field = record.fields["field_$index"] // our new base-type is the type of the field - baseType = field?.type ?: newUnknownType() + baseType = field?.type ?: unknownType() // construct our member expression expr = newMemberExpression(field?.name?.localName, base, baseType, ".", "") @@ -778,12 +778,13 @@ class StatementHandler(lang: LLVMIRLanguageFrontend) : val instrString = frontend.codeOf(instr) val callExpression = newCallExpression(llvmInternalRef("llvm.fence"), "llvm.fence", instrString, false) - val ordering = newLiteral(LLVMGetOrdering(instr), parseType("i32"), frontend.codeOf(instr)) + val ordering = + newLiteral(LLVMGetOrdering(instr), primitiveType("i32"), frontend.codeOf(instr)) callExpression.addArgument(ordering, "ordering") if (instrString?.contains("syncscope") == true) { val syncscope = instrString.split("\"")[1] callExpression.addArgument( - newLiteral(syncscope, parseType("String"), instrString), + newLiteral(syncscope, objectType("String"), instrString), "syncscope" ) } @@ -958,12 +959,12 @@ class StatementHandler(lang: LLVMIRLanguageFrontend) : } val condition = newBinaryOperator(operatorCode, instrStr) val castExprLhs = newCastExpression(frontend.codeOf(instr)) - castExprLhs.castType = parseType("u${ty.name}") + castExprLhs.castType = objectType("u${ty.name}") castExprLhs.expression = ptrDeref condition.lhs = castExprLhs val castExprRhs = newCastExpression(frontend.codeOf(instr)) - castExprRhs.castType = parseType("u${ty.name}") + castExprRhs.castType = objectType("u${ty.name}") castExprRhs.expression = value condition.rhs = castExprRhs @@ -1023,7 +1024,7 @@ class StatementHandler(lang: LLVMIRLanguageFrontend) : // The case statement is derived from the address of the label which we can jump to val caseBBAddress = LLVMValueAsBasicBlock(LLVMGetOperand(instr, idx)).address() val caseStatement = newCaseStatement(nodeCode) - caseStatement.caseExpression = newLiteral(caseBBAddress, parseType("i64"), nodeCode) + caseStatement.caseExpression = newLiteral(caseBBAddress, primitiveType("i64"), nodeCode) caseStatements.addStatement(caseStatement) // Get the label of the goto statement. @@ -1172,7 +1173,7 @@ class StatementHandler(lang: LLVMIRLanguageFrontend) : catchClause.parameter = newVariableDeclaration( "e_${gotoCatch.labelName}", - newUnknownType(), + unknownType(), instrStr, true, instr @@ -1225,7 +1226,7 @@ class StatementHandler(lang: LLVMIRLanguageFrontend) : val except = newVariableDeclaration( exceptionName, - parseType(catchType), // TODO: This doesn't work for multiple types to catch + objectType(catchType), // TODO: This doesn't work for multiple types to catch frontend.codeOf(instr), false, instr @@ -1254,7 +1255,7 @@ class StatementHandler(lang: LLVMIRLanguageFrontend) : arrayExpr.arrayExpression = newDeclaredReferenceExpression( decl?.name?.toString() ?: Node.EMPTY_NAME, - decl?.type ?: newUnknownType(), + decl?.type ?: unknownType(), instrStr ) arrayExpr.subscriptExpression = frontend.getOperandValueAtIndex(instr, 2) @@ -1331,7 +1332,8 @@ class StatementHandler(lang: LLVMIRLanguageFrontend) : } else { val arrayExpr = newArraySubscriptionExpression(instrStr) arrayExpr.arrayExpression = frontend.getOperandValueAtIndex(instr, 0) - arrayExpr.subscriptExpression = newLiteral(idxInt, parseType("i32"), instrStr) + arrayExpr.subscriptExpression = + newLiteral(idxInt, primitiveType("i32"), instrStr) initializers += arrayExpr } } else if (idxInt < array1Length + array2Length) { @@ -1343,7 +1345,7 @@ class StatementHandler(lang: LLVMIRLanguageFrontend) : val arrayExpr = newArraySubscriptionExpression(instrStr) arrayExpr.arrayExpression = frontend.getOperandValueAtIndex(instr, 1) arrayExpr.subscriptExpression = - newLiteral(idxInt - array1Length, parseType("i32"), instrStr) + newLiteral(idxInt - array1Length, primitiveType("i32"), instrStr) initializers += arrayExpr } } else { @@ -1582,13 +1584,13 @@ class StatementHandler(lang: LLVMIRLanguageFrontend) : if (unsigned) { val op1Type = "u${op1.type.name}" val castExprLhs = newCastExpression(frontend.codeOf(instr)) - castExprLhs.castType = parseType(op1Type) + castExprLhs.castType = objectType(op1Type) castExprLhs.expression = op1 binaryOperator.lhs = castExprLhs val op2Type = "u${op2.type.name}" val castExprRhs = newCastExpression(frontend.codeOf(instr)) - castExprRhs.castType = parseType(op2Type) + castExprRhs.castType = objectType(op2Type) castExprRhs.expression = op2 binaryOperator.rhs = castExprRhs } else { diff --git a/cpg-language-llvm/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontendTest.kt b/cpg-language-llvm/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontendTest.kt index 8f2c0448ab..0d98a6852b 100644 --- a/cpg-language-llvm/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontendTest.kt +++ b/cpg-language-llvm/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontendTest.kt @@ -26,13 +26,10 @@ package de.fraunhofer.aisec.cpg.frontends.llvm import de.fraunhofer.aisec.cpg.* -import de.fraunhofer.aisec.cpg.graph.TypeManager -import de.fraunhofer.aisec.cpg.graph.bodyOrNull -import de.fraunhofer.aisec.cpg.graph.byNameOrNull +import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration -import de.fraunhofer.aisec.cpg.graph.parseType import de.fraunhofer.aisec.cpg.graph.statements.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.* import de.fraunhofer.aisec.cpg.graph.types.ObjectType @@ -312,10 +309,10 @@ class LLVMIRLanguageFrontendTest { val rhs = (comparison.rhs as Literal<*>) val lhs = (comparison.lhs as DeclaredReferenceExpression).refersTo as VariableDeclaration assertEquals(10L, (rhs.value as Long)) - assertEquals(tu.parseType("i32"), rhs.type) + assertEquals(tu.primitiveType("i32"), rhs.type) assertLocalName("x", comparison.lhs as DeclaredReferenceExpression) assertLocalName("x", lhs) - assertEquals(tu.parseType("i32"), lhs.type) + assertEquals(tu.primitiveType("i32"), lhs.type) // Check that the jump targets are set correctly val ifStatement = main.bodyOrNull(0) @@ -346,11 +343,11 @@ class LLVMIRLanguageFrontendTest { assertTrue(ifBranchComp.lhs is CastExpression) val ifBranchCompRhs = ifBranchComp.rhs as CastExpression - assertEquals(tu.parseType("ui32"), ifBranchCompRhs.castType) - assertEquals(tu.parseType("ui32"), ifBranchCompRhs.type) + assertEquals(tu.objectType("ui32"), ifBranchCompRhs.castType) + assertEquals(tu.objectType("ui32"), ifBranchCompRhs.type) val ifBranchCompLhs = ifBranchComp.lhs as CastExpression - assertEquals(tu.parseType("ui32"), ifBranchCompLhs.castType) - assertEquals(tu.parseType("ui32"), ifBranchCompLhs.type) + assertEquals(tu.objectType("ui32"), ifBranchCompLhs.castType) + assertEquals(tu.objectType("ui32"), ifBranchCompLhs.type) val declRefExpr = ifBranchCompLhs.expression as DeclaredReferenceExpression assertEquals(-3, ((ifBranchCompRhs.expression as Literal<*>).value as Long)) diff --git a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguageFrontend.kt b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguageFrontend.kt index 11f1116b10..6e381748da 100644 --- a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguageFrontend.kt +++ b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguageFrontend.kt @@ -31,8 +31,8 @@ import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend import de.fraunhofer.aisec.cpg.frontends.TranslationException import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration -import de.fraunhofer.aisec.cpg.graph.newUnknownType import de.fraunhofer.aisec.cpg.graph.types.Type +import de.fraunhofer.aisec.cpg.graph.unknownType import de.fraunhofer.aisec.cpg.sarif.PhysicalLocation import java.io.File import java.nio.file.Paths @@ -50,7 +50,7 @@ class PythonLanguageFrontend(language: Language, ctx: Tr override fun typeOf(type: Any): Type { // will be invoked by native function - return newUnknownType() + return unknownType() } override fun codeOf(astNode: Any): String? { diff --git a/cpg-language-python/src/main/python/CPGPython/_expressions.py b/cpg-language-python/src/main/python/CPGPython/_expressions.py index dd642a561b..b0bbbc127d 100644 --- a/cpg-language-python/src/main/python/CPGPython/_expressions.py +++ b/cpg-language-python/src/main/python/CPGPython/_expressions.py @@ -67,7 +67,8 @@ def handle_expression_impl(self, expr): self.get_src_code(expr), expr) # we got a complex number - complextype = NodeBuilderKt.parseType(self.frontend, "complex") + complextype = NodeBuilderKt.primitiveType(self.frontend, + "complex") # TODO: fix this once the CPG supports complex numbers realpart = complex(lhs.getValue()) @@ -242,7 +243,7 @@ def handle_expression_impl(self, expr): cast = ExpressionBuilderKt.newCastExpression( self.frontend, self.get_src_code(expr)) cast.setCastType( - NodeBuilderKt.parseType(self.frontend, "str")) + NodeBuilderKt.primitiveType(self.frontend, "str")) cast.setExpression( self.handle_expression(expr.args[0])) return cast @@ -275,27 +276,31 @@ def handle_expression_impl(self, expr): elif isinstance(expr, ast.Constant): resultvalue = expr.value if isinstance(expr.value, type(None)): - tpe = NodeBuilderKt.parseType(self.frontend, "None") + tpe = NodeBuilderKt.objectType(self.frontend, "None") elif isinstance(expr.value, bool): - tpe = NodeBuilderKt.parseType(self.frontend, "bool") + tpe = NodeBuilderKt.primitiveType(self.frontend, "bool") elif isinstance(expr.value, int): - tpe = NodeBuilderKt.parseType(self.frontend, "int") + tpe = NodeBuilderKt.primitiveType(self.frontend, "int") elif isinstance(expr.value, float): - tpe = NodeBuilderKt.parseType(self.frontend, "float") + tpe = NodeBuilderKt.primitiveType(self.frontend, "float") elif isinstance(expr.value, complex): - tpe = NodeBuilderKt.parseType(self.frontend, "complex") + tpe = NodeBuilderKt.primitiveType(self.frontend, "complex") # TODO: fix this once the CPG supports complex numbers resultvalue = str(resultvalue) elif isinstance(expr.value, str): - tpe = NodeBuilderKt.parseType(self.frontend, "str") + tpe = NodeBuilderKt.primitiveType(self.frontend, "str") elif isinstance(expr.value, bytes): - tpe = NodeBuilderKt.parseType(self.frontend, "byte[]") + tpe = NodeBuilderKt.array( + self.frontend, + NodeBuilderKt.primitiveType( + self.frontend, + "byte")) else: self.log_with_loc( "Found unexpected type - using a dummy: %s" % (type(expr.value)), loglevel="ERROR") - tpe = UnknownType.getUnknownType(self.frontend.getLanguage()) + tpe = NodeBuilderKt.unknownType(self.frontend) lit = ExpressionBuilderKt.newLiteral( self.frontend, resultvalue, tpe, self.get_src_code(expr)) @@ -314,7 +319,7 @@ def handle_expression_impl(self, expr): value.getName(), value.getType(), value.getCode()) mem = ExpressionBuilderKt.newMemberExpression( self.frontend, expr.attr, value, - UnknownType.getUnknownType(self.frontend.getLanguage()), + NodeBuilderKt.unknownType(self.frontend), ".", self.get_src_code(expr)) return mem @@ -333,7 +338,7 @@ def handle_expression_impl(self, expr): elif isinstance(expr, ast.Name): r = ExpressionBuilderKt.newDeclaredReferenceExpression( self.frontend, expr.id, - UnknownType.getUnknownType(self.frontend.getLanguage()), + NodeBuilderKt.unknownType(self.frontend), self.get_src_code(expr)) # Take a little shortcut and set refersTo, in case this is a method diff --git a/cpg-language-python/src/main/python/CPGPython/_statements.py b/cpg-language-python/src/main/python/CPGPython/_statements.py index 2a10a329fe..6ae8eddc1a 100644 --- a/cpg-language-python/src/main/python/CPGPython/_statements.py +++ b/cpg-language-python/src/main/python/CPGPython/_statements.py @@ -63,7 +63,7 @@ def handle_statement_impl(self, stmt): tname = "%s.%s" % (namespace.toString(), base.id) self.log_with_loc("Building super type using current " "namespace: %s" % tname) - t = NodeBuilderKt.parseType(self.frontend, tname) + t = NodeBuilderKt.objectType(self.frontend, tname) bases.append(t) cls.setSuperClasses(bases) @@ -170,7 +170,7 @@ def handle_statement_impl(self, stmt): else: name = s.name src = name - tpe = UnknownType.getUnknownType(self.frontend.getLanguage()) + tpe = NodeBuilderKt.unknownType(self.frontend) v = DeclarationBuilderKt.newVariableDeclaration(self.frontend, name, tpe, src, False) @@ -200,7 +200,7 @@ def handle_statement_impl(self, stmt): else: name = s.name src = name - tpe = UnknownType.getUnknownType(self.frontend.getLanguage()) + tpe = NodeBuilderKt.unknownType(self.frontend) v = DeclarationBuilderKt.newVariableDeclaration( self.frontend, name, tpe, src, False) # inaccurate but ast.alias does not hold location information @@ -301,8 +301,9 @@ def handle_function_or_method(self, node, record=None): if record is not None: if len(node.args.args) > 0: recv_node = node.args.args[0] - tpe = NodeBuilderKt.parseType(self.frontend, - record.getName()) + tpe = NodeBuilderKt.objectType( + self.frontend, + record.getName()) recv = DeclarationBuilderKt.newVariableDeclaration( self.frontend, recv_node.arg, tpe, self.get_src_code(recv_node), @@ -406,9 +407,10 @@ def handle_function_or_method(self, node, record=None): def handle_argument(self, arg: ast.arg): self.log_with_loc("Handling an argument: %s" % (ast.dump(arg))) if arg.annotation is not None: - tpe = NodeBuilderKt.parseType(self.frontend, arg.annotation.id) + # TODO: parse non-scalar types + tpe = NodeBuilderKt.objectType(self.frontend, arg.annotation.id) else: - tpe = UnknownType.getUnknownType(self.frontend.getLanguage()) + tpe = NodeBuilderKt.unknownType(self.frontend) # TODO variadic pvd = DeclarationBuilderKt.newParamVariableDeclaration( self.frontend, arg.arg, tpe, False, self.get_src_code(arg)) @@ -579,7 +581,7 @@ class Foo: else: v = DeclarationBuilderKt.newFieldDeclaration( self.frontend, name, - UnknownType.getUnknownType(self.frontend.getLanguage()), + NodeBuilderKt.unknownType(self.frontend), None, self.get_src_code(stmt), None, None, False) # TODO None -> add infos self.scopemanager.addDeclaration(v) @@ -604,8 +606,7 @@ def bar(self): else: v = DeclarationBuilderKt.newVariableDeclaration( self.frontend, lhs.getName(), - UnknownType.getUnknownType( - self.frontend.getLanguage()), + NodeBuilderKt.unknownType(self.frontend), self.get_src_code(stmt), False) if rhs is not None: @@ -643,8 +644,7 @@ def bar(self): else: v = DeclarationBuilderKt.newFieldDeclaration( self.frontend, lhs.getName(), - UnknownType.getUnknownType( - self.frontend.getLanguage()), + NodeBuilderKt.unknownType(self.frontend), None, self.get_src_code(stmt), None, None, False) self.scopemanager.addDeclaration(v) @@ -666,7 +666,7 @@ def bar(self): else: v = DeclarationBuilderKt.newVariableDeclaration( self.frontend, lhs.getName(), - UnknownType.getUnknownType(self.frontend.getLanguage()), + NodeBuilderKt.unknownType(self.frontend), self.get_src_code(stmt), False) if rhs is not None: diff --git a/cpg-language-python/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonFrontendTest.kt b/cpg-language-python/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonFrontendTest.kt index b5f808b10f..39fd3815a6 100644 --- a/cpg-language-python/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonFrontendTest.kt +++ b/cpg-language-python/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonFrontendTest.kt @@ -69,19 +69,19 @@ class PythonFrontendTest : BaseTest() { val b = p.variables["b"] assertNotNull(b) assertLocalName("b", b) - assertEquals(tu.parseType("bool"), b.type) + assertEquals(tu.primitiveType("bool"), b.type) assertEquals(true, (b.initializer as? Literal<*>)?.value) val i = p.variables["i"] assertNotNull(i) assertLocalName("i", i) - assertEquals(tu.parseType("int"), i.type) + assertEquals(tu.primitiveType("int"), i.type) assertEquals(42L, (i.initializer as? Literal<*>)?.value) val f = p.variables["f"] assertNotNull(f) assertLocalName("f", f) - assertEquals(tu.parseType("float"), f.type) + assertEquals(tu.primitiveType("float"), f.type) assertEquals(1.0, (f.initializer as? Literal<*>)?.value) val c = p.variables["c"] @@ -96,13 +96,13 @@ class PythonFrontendTest : BaseTest() { val t = p.variables["t"] assertNotNull(t) assertLocalName("t", t) - assertEquals(tu.parseType("str"), t.type) + assertEquals(tu.primitiveType("str"), t.type) assertEquals("Hello", (t.initializer as? Literal<*>)?.value) val n = p.variables["n"] assertNotNull(n) assertLocalName("n", n) - assertEquals(tu.parseType("None"), n.type) + assertEquals(tu.objectType("None"), n.type) assertEquals(null, (n.initializer as? Literal<*>)?.value) } @@ -142,7 +142,7 @@ class PythonFrontendTest : BaseTest() { val s = bar.parameters.first() assertNotNull(s) assertLocalName("s", s) - assertEquals(tu.parseType("str"), s.type) + assertEquals(tu.primitiveType("str"), s.type) assertLocalName("bar", bar) @@ -159,7 +159,7 @@ class PythonFrontendTest : BaseTest() { assertNotNull(literal) assertEquals("bar(s) here: ", literal.value) - assertEquals(tu.parseType("str"), literal.type) + assertEquals(tu.primitiveType("str"), literal.type) val ref = callExpression.arguments[1] as? DeclaredReferenceExpression assertNotNull(ref) @@ -219,11 +219,11 @@ class PythonFrontendTest : BaseTest() { as? VariableDeclaration assertNotNull(sel) assertLocalName("sel", sel) - assertEquals(tu.parseType("bool"), sel.type) + assertEquals(tu.primitiveType("bool"), sel.type) val initializer = sel.initializer as? Literal<*> assertNotNull(initializer) - assertEquals(tu.parseType("bool"), initializer.type) + assertEquals(tu.primitiveType("bool"), initializer.type) assertEquals("True", initializer.code) val `if` = body.statements[1] as? IfStatement @@ -308,11 +308,11 @@ class PythonFrontendTest : BaseTest() { val foo = body.singleDeclaration as? VariableDeclaration assertNotNull(foo) assertLocalName("foo", foo) - assertEquals(tu.parseType("int"), foo.type) + assertEquals(tu.primitiveType("int"), foo.type) val initializer = foo.initializer as? ConditionalExpression assertNotNull(initializer) - assertEquals(tu.parseType("int"), initializer.type) + assertEquals(tu.primitiveType("int"), initializer.type) val ifCond = initializer.condition as? Literal<*> assertNotNull(ifCond) @@ -321,13 +321,13 @@ class PythonFrontendTest : BaseTest() { val elseExpr = initializer.elseExpr as? Literal<*> assertNotNull(elseExpr) - assertEquals(tu.parseType("bool"), ifCond.type) + assertEquals(tu.primitiveType("bool"), ifCond.type) assertEquals(false, ifCond.value) - assertEquals(tu.parseType("int"), thenExpr.type) + assertEquals(tu.primitiveType("int"), thenExpr.type) assertEquals(21, (thenExpr.value as? Long)?.toInt()) - assertEquals(tu.parseType("int"), elseExpr.type) + assertEquals(tu.primitiveType("int"), elseExpr.type) assertEquals(42, (elseExpr.value as? Long)?.toInt()) } @@ -433,7 +433,7 @@ class PythonFrontendTest : BaseTest() { assertNotNull(i) assertLocalName("i", i) - assertEquals(tu.parseType("int"), i.type) + assertEquals(tu.primitiveType("int"), i.type) // self.somevar = i val someVarDeclaration = diff --git a/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/DeclarationHandler.kt b/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/DeclarationHandler.kt index 03e2153820..9911ccb2e3 100644 --- a/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/DeclarationHandler.kt +++ b/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/DeclarationHandler.kt @@ -56,8 +56,7 @@ class DeclarationHandler(lang: TypeScriptLanguageFrontend) : private fun handlePropertySignature(node: TypeScriptNode): FieldDeclaration { val name = this.frontend.getIdentifierName(node) - val type = - node.typeChildNode?.let { this.frontend.typeHandler.handle(it) } ?: newUnknownType() + val type = node.typeChildNode?.let { this.frontend.typeOf(it) } ?: unknownType() val field = newFieldDeclaration( @@ -110,8 +109,7 @@ class DeclarationHandler(lang: TypeScriptLanguageFrontend) : private fun handleParameter(node: TypeScriptNode): Declaration { val name = this.frontend.getIdentifierName(node) - val type = - node.typeChildNode?.let { this.frontend.typeHandler.handle(it) } ?: newUnknownType() + val type = node.typeChildNode?.let { this.frontend.typeOf(it) } ?: unknownType() return newParamVariableDeclaration(name, type, false, this.frontend.codeOf(node)) } @@ -160,9 +158,7 @@ class DeclarationHandler(lang: TypeScriptLanguageFrontend) : else -> newFunctionDeclaration(name, this.frontend.codeOf(node)) } - node.typeChildNode?.let { - func.type = this.frontend.typeHandler.handle(it) ?: newUnknownType() - } + node.typeChildNode?.let { func.type = this.frontend.typeOf(it) } this.frontend.scopeManager.enterScope(func) @@ -201,8 +197,7 @@ class DeclarationHandler(lang: TypeScriptLanguageFrontend) : // TODO: support ObjectBindingPattern (whatever it is). seems to be multiple assignment - val `var` = - newVariableDeclaration(name, newUnknownType(), this.frontend.codeOf(node), false) + val `var` = newVariableDeclaration(name, unknownType(), this.frontend.codeOf(node), false) `var`.location = this.frontend.locationOf(node) // the last node that is not an identifier or an object binding pattern is an initializer diff --git a/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/ExpressionHandler.kt b/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/ExpressionHandler.kt index 224e388e56..9069f4910a 100644 --- a/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/ExpressionHandler.kt +++ b/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/ExpressionHandler.kt @@ -113,7 +113,7 @@ class ExpressionHandler(lang: TypeScriptLanguageFrontend) : // the function will (probably) not have a defined return type, so we try to deduce this // from a return statement - if (func?.type == newUnknownType()) { + if (func?.type == unknownType()) { val returnValue = func.bodyOrNull()?.returnValue /*if (returnValue == null) { @@ -121,7 +121,7 @@ class ExpressionHandler(lang: TypeScriptLanguageFrontend) : func.type = TypeParser.createFrom("void", false) } else {*/ - val returnType = returnValue?.type ?: newUnknownType() + val returnType = returnValue?.type ?: unknownType() func.type = returnType // } @@ -163,13 +163,13 @@ class ExpressionHandler(lang: TypeScriptLanguageFrontend) : ?.replace("'", "") ?: "" - return newLiteral(value, parseType("String"), frontend.codeOf(node)) + return newLiteral(value, primitiveType("string"), frontend.codeOf(node)) } private fun handleIdentifier(node: TypeScriptNode): Expression { val name = this.frontend.codeOf(node)?.trim() ?: "" - return newDeclaredReferenceExpression(name, newUnknownType(), this.frontend.codeOf(node)) + return newDeclaredReferenceExpression(name, unknownType(), this.frontend.codeOf(node)) } private fun handlePropertyAccessExpression(node: TypeScriptNode): Expression { @@ -179,7 +179,7 @@ class ExpressionHandler(lang: TypeScriptLanguageFrontend) : val name = node.children?.last()?.let { this.frontend.codeOf(it) } ?: "" - return newMemberExpression(name, base, newUnknownType(), ".", this.frontend.codeOf(node)) + return newMemberExpression(name, base, unknownType(), ".", this.frontend.codeOf(node)) } private fun handleCallExpression(node: TypeScriptNode): Expression { diff --git a/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypeHandler.kt b/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypeHandler.kt index e1f83be9ec..b1dd08906d 100644 --- a/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypeHandler.kt +++ b/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypeHandler.kt @@ -26,11 +26,12 @@ package de.fraunhofer.aisec.cpg.frontends.typescript import de.fraunhofer.aisec.cpg.frontends.Handler -import de.fraunhofer.aisec.cpg.graph.newUnknownType -import de.fraunhofer.aisec.cpg.graph.parseType -import de.fraunhofer.aisec.cpg.graph.types.PointerType +import de.fraunhofer.aisec.cpg.graph.array +import de.fraunhofer.aisec.cpg.graph.objectType +import de.fraunhofer.aisec.cpg.graph.primitiveType import de.fraunhofer.aisec.cpg.graph.types.Type import de.fraunhofer.aisec.cpg.graph.types.UnknownType +import de.fraunhofer.aisec.cpg.graph.unknownType class TypeHandler(frontend: TypeScriptLanguageFrontend) : Handler( @@ -51,32 +52,32 @@ class TypeHandler(frontend: TypeScriptLanguageFrontend) : "ArrayType" -> return handleArrayType(node) } - return newUnknownType() + return unknownType() } private fun handleArrayType(node: TypeScriptNode): Type { - val type = node.firstChild("TypeReference")?.let { this.handle(it) } ?: newUnknownType() + val type = node.firstChild("TypeReference")?.let { this.handle(it) } ?: unknownType() - return type.reference(PointerType.PointerOrigin.ARRAY) + return type.array() } private fun handleStringKeyword(): Type { - return parseType("string") + return primitiveType("string") } private fun handleNumberKeyword(): Type { - return parseType("number") + return primitiveType("number") } private fun handleAnyKeyword(): Type { - return parseType("any") + return objectType("any") } private fun handleTypeReference(node: TypeScriptNode): Type { node.firstChild("Identifier")?.let { - return parseType(this.frontend.getIdentifierName(node)) + return objectType(this.frontend.getIdentifierName(node)) } - return newUnknownType() + return unknownType() } } diff --git a/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypeScriptLanguageFrontend.kt b/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypeScriptLanguageFrontend.kt index 8eb7e3eeda..c85ac52ee3 100644 --- a/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypeScriptLanguageFrontend.kt +++ b/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypeScriptLanguageFrontend.kt @@ -109,8 +109,7 @@ class TypeScriptLanguageFrontend( } override fun typeOf(type: TypeScriptNode): Type { - // reserved for future use - return newUnknownType() + return typeHandler.handleNode(type) } /** diff --git a/cpg-language-typescript/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypescriptLanguageFrontendTest.kt b/cpg-language-typescript/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypescriptLanguageFrontendTest.kt index 65d8bf2726..5e465efe3f 100644 --- a/cpg-language-typescript/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypescriptLanguageFrontendTest.kt +++ b/cpg-language-typescript/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypescriptLanguageFrontendTest.kt @@ -27,21 +27,17 @@ package de.fraunhofer.aisec.cpg.frontends.typescript import de.fraunhofer.aisec.cpg.TestUtils import de.fraunhofer.aisec.cpg.assertLocalName -import de.fraunhofer.aisec.cpg.graph.byNameOrNull +import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration -import de.fraunhofer.aisec.cpg.graph.get -import de.fraunhofer.aisec.cpg.graph.parseType import de.fraunhofer.aisec.cpg.graph.statements.DeclarationStatement import de.fraunhofer.aisec.cpg.graph.statements.ReturnStatement import de.fraunhofer.aisec.cpg.graph.statements.expressions.* +import de.fraunhofer.aisec.cpg.graph.types.PointerType import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker import java.nio.file.Path -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertNotNull -import kotlin.test.assertSame +import kotlin.test.* class TypeScriptLanguageFrontendTest { @@ -66,11 +62,11 @@ class TypeScriptLanguageFrontendTest { val someFunction = functions.first() assertLocalName("someFunction", someFunction) - assertEquals(tu.parseType("Number"), someFunction.type) + assertEquals(tu.primitiveType("number"), someFunction.type) val someOtherFunction = functions.last() assertLocalName("someOtherFunction", someOtherFunction) - assertEquals(tu.parseType("Number"), someOtherFunction.type) + assertEquals(tu.primitiveType("number"), someOtherFunction.type) val parameters = someOtherFunction.parameters assertNotNull(parameters) @@ -79,7 +75,7 @@ class TypeScriptLanguageFrontendTest { val parameter = parameters.first() assertLocalName("s", parameter) - assertEquals(tu.parseType("String"), parameter.type) + assertEquals(tu.primitiveType("string"), parameter.type) } @Test @@ -278,7 +274,7 @@ class TypeScriptLanguageFrontendTest { val lastName = user.fields.lastOrNull() assertNotNull(lastName) assertLocalName("lastName", lastName) - assertEquals(tu.parseType("string"), lastName.type) + assertEquals(tu.primitiveType("string"), lastName.type) val usersState = tu.getDeclarationsByName("UsersState", RecordDeclaration::class.java).iterator().next() @@ -291,7 +287,8 @@ class TypeScriptLanguageFrontendTest { val users = usersState.fields.firstOrNull() assertNotNull(users) assertLocalName("users", users) - assertEquals(tu.parseType("User[]"), users.type) + assertIs(users.type) + assertLocalName("User[]", users.type) val usersComponent = tu.getDeclarationsByName("Users", RecordDeclaration::class.java).iterator().next() diff --git a/cpg-language-typescript/src/test/resources/log4j2.xml b/cpg-language-typescript/src/test/resources/log4j2.xml new file mode 100644 index 0000000000..ac6e67063f --- /dev/null +++ b/cpg-language-typescript/src/test/resources/log4j2.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/cpg-language-typescript/src/test/resources/typescript/function.ts b/cpg-language-typescript/src/test/resources/typescript/function.ts index 3620d429bf..ec260d52b8 100644 --- a/cpg-language-typescript/src/test/resources/typescript/function.ts +++ b/cpg-language-typescript/src/test/resources/typescript/function.ts @@ -1,7 +1,7 @@ /* Block comment on a function */ -function someFunction(): Number { +function someFunction(): number { // Comment on a variable const i = someOtherFunction("hello"); @@ -9,6 +9,6 @@ function someFunction(): Number { } // Comment on a Function -function someOtherFunction(s: String): Number { +function someOtherFunction(s: string): number { return s.length; } \ No newline at end of file