From 36b2d18245529dd460b305d464c70fca67955332 Mon Sep 17 00:00:00 2001 From: Maximilian Kaul Date: Wed, 8 Feb 2023 13:34:54 +0100 Subject: [PATCH 1/2] Python paths OS independent (resolves #1032) (#1053) This fixes several Unix / Windows path issues and should allow the Python frontend to run on either OS. --- README.md | 2 +- .../cpg/frontends/python/JepSingleton.kt | 73 ++++++++++++++----- .../python/PythonLanguageFrontend.kt | 7 +- .../src/main/python/CPGPython/__init__.py | 5 +- .../src/main/python/CPGPython/_misc.py | 4 +- 5 files changed, 67 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 16a1eab690..f60c905831 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ In the case of Golang, the necessary native code can be found in the `src/main/g You need to install [jep](https://github.com/ninia/jep/). This can either be system-wide or in a virtual environment. Your jep version has to match the version used by the CPG (see [version catalog](./gradle/libs.versions.toml)). -Currently, only Python 3.10 is supported. +Currently, only Python 3.{9,10,11,12} is supported. ##### System Wide diff --git a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/JepSingleton.kt b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/JepSingleton.kt index 7131932725..b59664d3d8 100644 --- a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/JepSingleton.kt +++ b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/JepSingleton.kt @@ -29,10 +29,12 @@ import de.fraunhofer.aisec.cpg.frontends.TranslationException import java.io.File import java.net.JarURLConnection import java.nio.file.Path +import java.nio.file.Paths import jep.JepConfig import jep.JepException import jep.MainInterpreter import jep.SubInterpreter +import kotlin.io.path.exists import org.slf4j.LoggerFactory /** @@ -58,12 +60,11 @@ object JepSingleton { // we can point JEP to the folder and get better debug messages with python source code // locations - // we want to have the parent folder of "CPGPython" so that we can do "import CPGPython" - // in python - var pyFolder = pyInitFile.file.dropLastWhile { it != File.separatorChar } - pyFolder = pyFolder.dropLast(1) - pyFolder = pyFolder.dropLastWhile { it != File.separatorChar } - config.addIncludePaths(pyFolder) + // We want to have the parent folder of "CPGPython" so that we can do "import CPGPython" + // in python. The layout looks like `.../main/CPGPython/__init__.py` -> we have to go + // two levels up to get the path of `main`. + var pyFolder = Paths.get(pyInitFile.toURI()).parent.parent + config.addIncludePaths(pyFolder.toString()) } else { val targetFolder = tempFileHolder.pyFolder config.addIncludePaths(tempFileHolder.pyFolder.toString()) @@ -112,6 +113,9 @@ object JepSingleton { if (library.exists()) { MainInterpreter.setJepLibraryPath(library.path) config.addIncludePaths( + // We want to have the parent folder of "CPGPython" so that we can do "import + // CPGPython" in python. The layout looks like `.../main/CPGPython/__init__.py` + // -> we have to go two levels up to get the path of `main`. library.toPath().parent.parent.toString() ) // this assumes that the python code is also at the library's location } @@ -122,28 +126,63 @@ object JepSingleton { virtualEnv = System.getenv("CPG_PYTHON_VIRTUALENV") } - val virtualEnvPath = "${System.getProperty("user.home")}/.virtualenvs/${virtualEnv}/" + val virtualEnvPath = + Paths.get(System.getProperty("user.home"), ".virtualenvs", "${virtualEnv}/") val pythonVersions = listOf("3.9", "3.10", "3.11", "3.12") - val wellKnownPaths = mutableListOf() + val wellKnownPaths = mutableListOf() pythonVersions.forEach { version -> + // Linux wellKnownPaths.add( - File("${virtualEnvPath}/lib/python${version}/site-packages/jep/libjep.so") + Paths.get( + "$virtualEnvPath", + "lib", + "python${version}", + "site-packages", + "jep", + "libjep.so" + ) ) + // Mac OS wellKnownPaths.add( - File("${virtualEnvPath}/lib/python${version}/site-packages/jep/libjep.jnilib") + Paths.get( + "$virtualEnvPath", + "lib", + "python${version}", + "site-packages", + "jep", + "libjep.jnilib" + ) ) + wellKnownPaths.add( + Paths.get( + "$virtualEnvPath", + "lib", + "python${version}", + "site-packages", + "jep", + "libjep.dll" + ) + ) + } + try { + wellKnownPaths.add(Paths.get("/", "usr", "lib", "libjep.so")) + wellKnownPaths.add(Paths.get("/", "Library", "Java", "Extensions", "libjep.jnilib")) + } catch (e: Exception) { + // noop } - wellKnownPaths.add(File("/usr/lib/libjep.so")) - wellKnownPaths.add(File("/Library/Java/Extensions/libjep.jnilib")) wellKnownPaths.forEach { if (it.exists()) { // Jep's configuration must be set before the first instance is created. Later // calls // to setJepLibraryPath and co result in failures. - MainInterpreter.setJepLibraryPath(it.path) + MainInterpreter.setJepLibraryPath(it.toString()) config.addIncludePaths( - it.toPath().parent.parent.toString() + // We want to have the parent folder of "CPGPython" so that we can do + // "import CPGPython" in python. The layout looks like + // `.../main/CPGPython/__init__.py` -> we have to go two levels up to get + // the path of `main`. + it.parent.parent.toString() ) // this assumes that the python code is also at the library's location } } @@ -161,9 +200,9 @@ object JepSingleton { val possibleLocations = listOf( - Path.of(".").resolve(modulePath), - Path.of("src/main/python").resolve(modulePath), - Path.of("cpg-library/src/main/python").resolve(modulePath) + Paths.get(".").resolve(modulePath), + Paths.get("src", "main", "python").resolve(modulePath), + Paths.get("cpg-library", "src", "main", "python").resolve(modulePath) ) var entryScript: Path? = null 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 67b2dd7e29..d5aed59707 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 @@ -33,7 +33,9 @@ import de.fraunhofer.aisec.cpg.frontends.TranslationException import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration import de.fraunhofer.aisec.cpg.sarif.PhysicalLocation import java.io.File +import java.nio.file.Paths import jep.JepException +import kotlin.io.path.absolutePathString class PythonLanguageFrontend( language: Language, @@ -64,15 +66,16 @@ class PythonLanguageFrontend( private fun parseInternal(code: String, path: String): TranslationUnitDeclaration { val pythonInterpreter = jep.getInterp() val tu: TranslationUnitDeclaration + val absolutePath = Paths.get(path).absolutePathString() try { // run python function parse_code() tu = - pythonInterpreter.invoke("parse_code", this, code, path) + pythonInterpreter.invoke("parse_code", this, code, absolutePath) as TranslationUnitDeclaration if (config.matchCommentsToNodes) { // Parse comments and attach to nodes - pythonInterpreter.invoke("parse_comments", this, code, path, tu) + pythonInterpreter.invoke("parse_comments", this, code, absolutePath, tu) } } catch (e: JepException) { e.printStackTrace() diff --git a/cpg-language-python/src/main/python/CPGPython/__init__.py b/cpg-language-python/src/main/python/CPGPython/__init__.py index bb5a8eb4fd..49d84d2bf8 100644 --- a/cpg-language-python/src/main/python/CPGPython/__init__.py +++ b/cpg-language-python/src/main/python/CPGPython/__init__.py @@ -25,12 +25,13 @@ from ._code_extractor import CodeExtractor from de.fraunhofer.aisec.cpg.graph import DeclarationBuilderKt import ast +import os class PythonASTToCPG(ast.NodeVisitor): def __init__(self, fname, frontend, code): self.sourcecode = CodeExtractor(fname) - self.frontend = frontend + self.frontend = frontend # absolute path self.tud = DeclarationBuilderKt.newTranslationUnitDeclaration( self.frontend, fname, code) self.fname = fname @@ -73,7 +74,7 @@ def execute(self): # Module(stmt* body, type_ignore* type_ignores) # TODO how to name the namespace? # TODO improve readability - nsd_name = ".".join(self.fname.split("/")[-1].split(".")[:-1]) + nsd_name = ".".join(os.path.basename(self.fname).split(".")[:-1]) nsd = DeclarationBuilderKt.newNamespaceDeclaration(self.frontend, nsd_name, "") self.tud.addDeclaration(nsd) diff --git a/cpg-language-python/src/main/python/CPGPython/_misc.py b/cpg-language-python/src/main/python/CPGPython/_misc.py index 7c3ede8a75..23d6407ea2 100644 --- a/cpg-language-python/src/main/python/CPGPython/_misc.py +++ b/cpg-language-python/src/main/python/CPGPython/_misc.py @@ -28,7 +28,7 @@ from de.fraunhofer.aisec.cpg.graph import StatementBuilderKt from de.fraunhofer.aisec.cpg.sarif import PhysicalLocation from de.fraunhofer.aisec.cpg.sarif import Region -from java.net import URI +from java.io import File NOT_IMPLEMENTED_MSG = "This has not been implemented, yet. Using a dummy." CPG_JAVA = "de.fraunhofer.aisec.cpg" @@ -84,7 +84,7 @@ def add_mul_loc_infos(self, start_node, end_node, obj): (type(start_node)), (type(end_node)), loglevel="ERROR") return - uri = URI("file://" + self.fname) + uri = File(self.fname).toURI() obj.setLocation(PhysicalLocation(uri, Region(start_node.lineno, start_node.col_offset + 1, From 0bb383ae951a052bf2781d0e5265ca5a4363146a Mon Sep 17 00:00:00 2001 From: KuechA <31155350+KuechA@users.noreply.github.com> Date: Thu, 9 Feb 2023 11:49:27 +0100 Subject: [PATCH 2/2] More fine-grained and language specific interpretation of primitive types (#1033) * start of new type system * Better type creation * First attempt to move primitive types to languages * Add default constructor for neo4j * Revert change of gradle properties * Remove some logic from TypeParser * Trying with map instead of method * Moving some code * Fix json endless recursion * Formatting * String type * Formatting * Fix tests * Another fix * Adding type cache, the future of type management * ++ * Simplify TypeParser.fixGenerics * Less java regex magic * Fix problem with NamespaceProvider * Replace hardcoded generic characters in TypeParser * Fix go generic and handle its [] notation * Note on go map generic * Make sonar happy * Get rid of storage property in Type * Remove qualifier property in Type * Python fix * Fix sonar bug * Completely remove old qualifier class * Simplify TypeParser * Move modifier from ObjectType to NumericType * Fix errors in subprojects * One more fix * Generate the type in the failing test * Duplicate NumericType * Move TypeCache to Component * Move code incpg-neo4j * Make the relation of simple types transient * Removing typecache again * Fix ogm loop again * Fix VariableUsageResolver scope issue * Add go types * Remove constructors * Fix python * Reverted renaming of TypeManager * Remove more constructors --------- Co-authored-by: Christian Banse Co-authored-by: Christian Banse --- .../aisec/cpg/graph/TypeManager.java | 19 +- ...ierConverter.java => FloatingPointType.kt} | 35 +- .../cpg/graph/types/FunctionPointerType.java | 15 +- .../aisec/cpg/graph/types/FunctionType.kt | 21 +- .../aisec/cpg/graph/types/IncompleteType.java | 6 +- .../aisec/cpg/graph/types/IntegerType.kt | 42 ++ .../aisec/cpg/graph/types/NumericType.kt | 59 ++ .../aisec/cpg/graph/types/ObjectType.java | 42 +- .../aisec/cpg/graph/types/PointerType.java | 3 +- .../aisec/cpg/graph/types/ReferenceType.java | 25 +- .../aisec/cpg/graph/types/StringType.kt | 40 ++ .../aisec/cpg/graph/types/Type.java | 198 +------ .../aisec/cpg/graph/types/TypeParser.java | 438 +++++---------- .../aisec/cpg/graph/types/UnknownType.java | 10 - .../aisec/cpg/TranslationManager.kt | 5 +- .../fraunhofer/aisec/cpg/TranslationResult.kt | 5 +- .../aisec/cpg/frontends/Language.kt | 45 +- .../aisec/cpg/frontends/LanguageFrontend.kt | 2 +- .../aisec/cpg/frontends/LanguageTraits.kt | 10 +- .../aisec/cpg/frontends/cpp/CLanguage.kt | 35 +- .../aisec/cpg/frontends/cpp/CPPLanguage.kt | 46 +- .../cpg/frontends/cpp/CXXLanguageFrontend.kt | 16 +- .../cpg/frontends/java/ExpressionHandler.kt | 8 - .../aisec/cpg/frontends/java/JavaLanguage.kt | 25 +- .../frontends/java/JavaLanguageFrontend.kt | 2 +- .../fraunhofer/aisec/cpg/graph/Component.kt | 2 +- .../fraunhofer/aisec/cpg/graph/NodeBuilder.kt | 71 ++- .../fraunhofer/aisec/cpg/graph/TypeBuilder.kt | 34 +- .../graph/declarations/FunctionDeclaration.kt | 8 +- .../graph/declarations/ValueDeclaration.kt | 13 +- .../ArraySubscriptionExpression.kt | 5 +- .../statements/expressions/Expression.kt | 14 +- .../expressions/InitializerListExpression.kt | 11 +- .../expressions/LambdaExpression.kt | 11 +- .../aisec/cpg/passes/VariableUsageResolver.kt | 5 +- .../aisec/cpg/PerformanceRegressionTest.kt | 14 +- .../templates/FunctionTemplateTest.kt | 44 +- .../frontends/cpp/CXXLanguageFrontendTest.kt | 2 - .../cpp/CXXSymbolConfigurationTest.kt | 4 +- .../java/JavaLanguageFrontendTest.kt | 4 +- .../aisec/cpg/graph/types/TypeTests.kt | 515 ++---------------- .../aisec/cpg/graph/types/TypedefTest.kt | 4 +- .../cpg/passes/scopes/ScopeManagerTest.kt | 24 +- .../aisec/cpg/frontends/TestLanguage.kt | 4 +- .../aisec/cpg/frontends/golang/GoLanguage.kt | 32 +- .../frontends/golang/GoLanguageFrontend.kt | 3 +- .../golang/GoLanguageFrontendTest.kt | 7 +- .../cpg/frontends/llvm/LLVMIRLanguage.kt | 46 +- .../frontends/llvm/LLVMIRLanguageFrontend.kt | 2 +- .../llvm/LLVMIRLanguageFrontendTest.kt | 2 +- .../cpg/frontends/python/PythonLanguage.kt | 31 +- .../python/PythonLanguageFrontend.kt | 2 +- .../frontends/python/PythonFrontendTest.kt | 6 +- .../typescript/JavaScriptLanguage.kt | 2 +- .../typescript/TypeScriptLanguageFrontend.kt | 2 +- .../aisec/cpg_vis_neo4j/Application.kt | 0 .../aisec/cpg_vis_neo4j/ApplicationTest.kt | 0 57 files changed, 759 insertions(+), 1317 deletions(-) rename cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/{QualifierConverter.java => FloatingPointType.kt} (51%) create mode 100644 cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/IntegerType.kt create mode 100644 cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/NumericType.kt create mode 100644 cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/StringType.kt rename cpg-neo4j/src/main/{java => kotlin}/de/fraunhofer/aisec/cpg_vis_neo4j/Application.kt (100%) rename cpg-neo4j/src/test/{java => kotlin}/de/fraunhofer/aisec/cpg_vis_neo4j/ApplicationTest.kt (100%) diff --git a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/TypeManager.java b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/TypeManager.java index 43b9f1f444..8bc1fe5bc7 100644 --- a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/TypeManager.java +++ b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/TypeManager.java @@ -179,9 +179,8 @@ public ParameterizedType searchTemplateScopeForDefinedParameterizedTypes( // 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 template = (TemplateDeclaration) node; - ParameterizedType parameterizedType = getTypeParameter(template, name); + if (node instanceof TemplateDeclaration templateDeclaration) { + ParameterizedType parameterizedType = getTypeParameter(templateDeclaration, name); if (parameterizedType != null) { return parameterizedType; } @@ -318,13 +317,13 @@ public boolean containsParameterizedType(List generics) { * with parameterizedTypes as generics false otherwise */ public boolean stopPropagation(Type type, Type newType) { - if (type instanceof ObjectType - && newType instanceof ObjectType - && ((ObjectType) type).getGenerics() != null - && ((ObjectType) newType).getGenerics() != null + if (type instanceof ObjectType typeObjectType + && newType instanceof ObjectType newObjectType + && typeObjectType.getGenerics() != null + && newObjectType.getGenerics() != null && type.getName().equals(newType.getName())) { - return containsParameterizedType(((ObjectType) newType).getGenerics()) - && !(containsParameterizedType(((ObjectType) type).getGenerics())); + return containsParameterizedType(newObjectType.getGenerics()) + && !(containsParameterizedType(typeObjectType.getGenerics())); } return false; } @@ -482,7 +481,7 @@ public Optional getCommonType(@NotNull Collection types, ScopeProvid .map(t -> typeToRecord.getOrDefault(t, null)) .filter(Objects::nonNull) .map(r -> getAncestors(r, 0)) - .collect(Collectors.toList()); + .toList(); // normalize/reverse depth: roots start at 0, increasing on each level for (Set ancestors : allAncestors) { diff --git a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/QualifierConverter.java b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/FloatingPointType.kt similarity index 51% rename from cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/QualifierConverter.java rename to cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/FloatingPointType.kt index 38ad4fdb74..e34c4a01c6 100644 --- a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/QualifierConverter.java +++ b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/FloatingPointType.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Fraunhofer AISEC. All rights reserved. + * 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. @@ -23,27 +23,20 @@ * \______/ \__| \______/ * */ -package de.fraunhofer.aisec.cpg.graph.types; +package de.fraunhofer.aisec.cpg.graph.types -import java.util.HashMap; -import java.util.Map; -import org.neo4j.ogm.typeconversion.CompositeAttributeConverter; +import de.fraunhofer.aisec.cpg.frontends.Language +import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend -public class QualifierConverter implements CompositeAttributeConverter { - @Override - public Map toGraphProperties(Type.Qualifier value) { - Map properties = new HashMap<>(); - properties.put("isConst", value.isConst()); - properties.put("isVolatile", value.isVolatile()); - properties.put("isRestrict", value.isRestrict()); - properties.put("isAtomic", value.isAtomic()); - return properties; - } +/** Instances of this class represent floating point types. */ +class FloatingPointType( + typeName: CharSequence = "", + bitWidth: Int? = null, + language: Language? = null, + modifier: Modifier = Modifier.SIGNED +) : NumericType(typeName, bitWidth, language, modifier) { - @Override - public Type.Qualifier toEntityAttribute(Map value) { - Map val = (Map) value; - return new Type.Qualifier( - val.get("isConst"), val.get("isVolatile"), val.get("isRestrict"), val.get("isAtomic")); - } + override fun duplicate(): Type { + return FloatingPointType(name, bitWidth, language, modifier) + } } diff --git a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/FunctionPointerType.java b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/FunctionPointerType.java index 2e31b68ef2..9dc6360c1c 100644 --- a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/FunctionPointerType.java +++ b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/FunctionPointerType.java @@ -57,12 +57,8 @@ public void setReturnType(Type returnType) { private FunctionPointerType() {} public FunctionPointerType( - Type.Qualifier qualifier, - Type.Storage storage, - List parameters, - Type returnType, - Language language) { - super("", storage, qualifier, language); + List parameters, Type returnType, Language language) { + super("", language); this.parameters = PropertyEdge.transformIntoOutgoingPropertyEdgeList(parameters, this); this.returnType = returnType; } @@ -124,9 +120,8 @@ public boolean isSimilar(Type t) { @Override public boolean equals(Object o) { if (this == o) return true; - if (!(o instanceof FunctionPointerType)) return false; + if (!(o instanceof FunctionPointerType that)) return false; if (!super.equals(o)) return false; - FunctionPointerType that = (FunctionPointerType) o; return Objects.equals(this.getParameters(), that.getParameters()) && PropertyEdge.propertyEqualsList(parameters, that.parameters) && Objects.equals(returnType, that.returnType); @@ -148,10 +143,6 @@ public String toString() { + ", typeName='" + getName() + '\'' - + ", storage=" - + this.getStorage() - + ", qualifier=" - + this.getQualifier() + ", origin=" + this.getTypeOrigin() + '}'; diff --git a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/FunctionType.kt b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/FunctionType.kt index 854c699c2a..7cc18683fa 100644 --- a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/FunctionType.kt +++ b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/FunctionType.kt @@ -44,9 +44,7 @@ class FunctionType : Type { parameters: List, returnTypes: List, language: Language?, - qualifier: Qualifier = Qualifier(), - storage: Storage = Storage.AUTO - ) : super(typeName, storage, qualifier, language) { + ) : super(typeName, language) { this.parameters = parameters this.returnTypes = returnTypes } @@ -59,13 +57,7 @@ class FunctionType : Type { override fun reference(pointer: PointerType.PointerOrigin?): Type { // TODO(oxisto): In the future, we actually could just remove the FunctionPointerType // and just have a regular PointerType here - return FunctionPointerType( - qualifier, - storage, - parameters.toList(), - returnTypes.first(), - language - ) + return FunctionPointerType(parameters.toList(), returnTypes.first(), language) } override fun dereference(): Type { @@ -73,14 +65,7 @@ class FunctionType : Type { } override fun duplicate(): Type { - return FunctionType( - typeName, - parameters.toList(), - returnTypes.toList(), - language, - qualifier, - storage - ) + return FunctionType(typeName, parameters.toList(), returnTypes.toList(), language) } companion object { diff --git a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/IncompleteType.java b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/IncompleteType.java index 42c14a56a5..84cf5ad84e 100644 --- a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/IncompleteType.java +++ b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/IncompleteType.java @@ -32,13 +32,13 @@ * length, forward declarated classes in C++ * *

Right now we are only dealing with void for objects with unknown size, therefore the name is - * fixed to void. However, this can be changed in future, in order to support other objects with - * unknown size apart from void. Therefore this Type is not called VoidType + * fixed to void. However, this can be changed in the future, in order to support other objects with + * unknown size apart from void. Therefore, this Type is not called VoidType */ public class IncompleteType extends Type { public IncompleteType() { - super("void", Storage.AUTO, new Qualifier(false, false, false, false), null); + super("void", null); } public IncompleteType(Type type) { diff --git a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/IntegerType.kt b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/IntegerType.kt new file mode 100644 index 0000000000..353ac3e599 --- /dev/null +++ b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/IntegerType.kt @@ -0,0 +1,42 @@ +/* + * 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 + +import de.fraunhofer.aisec.cpg.frontends.Language +import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend + +/** Instances of this class represent integer types. */ +class IntegerType( + typeName: CharSequence = "", + bitWidth: Int? = null, + language: Language? = null, + modifier: Modifier = Modifier.SIGNED +) : NumericType(typeName, bitWidth, language, modifier) { + + override fun duplicate(): Type { + return IntegerType(this.name, bitWidth, language, modifier) + } +} diff --git a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/NumericType.kt b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/NumericType.kt new file mode 100644 index 0000000000..a9a3f82871 --- /dev/null +++ b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/NumericType.kt @@ -0,0 +1,59 @@ +/* + * 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 + +import de.fraunhofer.aisec.cpg.frontends.Language +import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend +import java.util.* + +/** This type collects all kind of numeric types. */ +open class NumericType( + typeName: CharSequence = "", + val bitWidth: Int? = null, + language: Language? = null, + val modifier: Modifier = Modifier.SIGNED +) : ObjectType(typeName, listOf(), true, language) { + + override fun duplicate(): Type { + return NumericType(this.name, bitWidth, language, modifier) + } + + /** + * NumericTypes can have a modifier. The default is signed. Some types (e.g. char in C) may be + * neither of the signed/unsigned option. TODO: maybe replace with a flag "signed" or + * "unsigned"? + */ + enum class Modifier { + SIGNED, + UNSIGNED, + NOT_APPLICABLE + } + + override fun equals(other: Any?) = + super.equals(other) && this.modifier == (other as? NumericType)?.modifier + + override fun hashCode() = Objects.hash(super.hashCode(), generics, modifier, primitive) +} diff --git a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/ObjectType.java b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/ObjectType.java index 96fdbced49..9ba4e1b0a2 100644 --- a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/ObjectType.java +++ b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/ObjectType.java @@ -67,18 +67,6 @@ public void replaceGenerics(Type oldType, Type newType) { } } - /** - * ObjectTypes can have a modifier if they are primitive datatypes. The default is signed for - * primitive data types if there is no more information provided. In case of non primitive - * datatypes the modifier is NOT_APPLICABLE - */ - public enum Modifier { - SIGNED, - UNSIGNED, - NOT_APPLICABLE, - } - - private final Modifier modifier; // Reference from the ObjectType to its class (RecordDeclaration) only if the class is available private RecordDeclaration recordDeclaration = null; @@ -86,16 +74,12 @@ public enum Modifier { private List> generics; public ObjectType( - String typeName, - Storage storage, - Qualifier qualifier, + CharSequence typeName, List generics, - Modifier modifier, boolean primitive, Language language) { - super(typeName, storage, qualifier, language); + super(typeName, language); this.generics = PropertyEdge.transformIntoOutgoingPropertyEdgeList(generics, this); - this.modifier = modifier; this.primitive = primitive; this.setLanguage(language); } @@ -103,13 +87,11 @@ public ObjectType( public ObjectType( Type type, List generics, - Modifier modifier, boolean primitive, Language language) { super(type); this.setLanguage(language); this.generics = PropertyEdge.transformIntoOutgoingPropertyEdgeList(generics, this); - this.modifier = modifier; this.primitive = primitive; } @@ -117,7 +99,6 @@ public ObjectType( public ObjectType() { super(); this.generics = new ArrayList<>(); - this.modifier = Modifier.NOT_APPLICABLE; this.primitive = false; } @@ -165,8 +146,7 @@ public Type dereference() { @Override public Type duplicate() { ObjectType newObject = - new ObjectType(this, this.getGenerics(), this.modifier, this.primitive, this.getLanguage()); - newObject.setLanguage(this.getLanguage()); + new ObjectType(this, this.getGenerics(), this.primitive, this.getLanguage()); return newObject; } @@ -186,31 +166,25 @@ public void addGenerics(List generics) { } } - public Modifier getModifier() { - return modifier; - } - @Override public boolean isSimilar(Type t) { - return t instanceof ObjectType - && this.getGenerics().equals(((ObjectType) t).getGenerics()) + return t instanceof ObjectType that + && this.getGenerics().equals(that.getGenerics()) && super.isSimilar(t); } @Override public boolean equals(Object o) { if (this == o) return true; - if (!(o instanceof ObjectType)) return false; + if (!(o instanceof ObjectType that)) return false; if (!super.equals(o)) return false; - ObjectType that = (ObjectType) o; return Objects.equals(this.getGenerics(), that.getGenerics()) && PropertyEdge.propertyEqualsList(generics, that.generics) - && this.primitive == that.primitive - && this.modifier.equals(that.modifier); + && this.primitive == that.primitive; } @Override public int hashCode() { - return Objects.hash(super.hashCode(), generics, modifier, primitive); + return Objects.hash(super.hashCode(), generics, primitive); } } diff --git a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/PointerType.java b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/PointerType.java index fc8d546ac9..457823f3b6 100644 --- a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/PointerType.java +++ b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/PointerType.java @@ -165,9 +165,8 @@ public void setElementType(Type elementType) { @Override public boolean equals(Object o) { if (this == o) return true; - if (!(o instanceof PointerType)) return false; + if (!(o instanceof PointerType that)) return false; if (!super.equals(o)) return false; - PointerType that = (PointerType) o; return Objects.equals(elementType, that.elementType) && Objects.equals(pointerOrigin, that.pointerOrigin); } diff --git a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/ReferenceType.java b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/ReferenceType.java index 8efb2e78b0..81dc6e631c 100644 --- a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/ReferenceType.java +++ b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/ReferenceType.java @@ -26,9 +26,10 @@ package de.fraunhofer.aisec.cpg.graph.types; import java.util.Objects; +import org.jetbrains.annotations.NotNull; /** - * ReferenceTypes describe CPP References (int&), which are represent an alternative name for a + * ReferenceTypes describe CPP References (int&), which represent an alternative name for a * variable. It is necessary to make this distinction, and not just rely on the original type as it * is required for matching parameters in function arguments to discover which implementation is * called. @@ -53,15 +54,6 @@ public ReferenceType(Type type, Type reference) { this.reference = reference; } - public ReferenceType(Storage storage, Qualifier qualifier, Type reference) { - this.setName(reference.getName().append("&")); - this.storage = storage != null ? storage : Storage.AUTO; - this.qualifier = qualifier; - this.origin = Origin.UNRESOLVED; - this.reference = reference; - this.setLanguage(reference.getLanguage()); - } - /** * @return Referencing a ReferenceType results in a PointerType to the original ReferenceType */ @@ -93,8 +85,8 @@ public void setElementType(Type reference) { @Override public boolean isSimilar(Type t) { - return t instanceof ReferenceType - && ((ReferenceType) t).getElementType().equals(this) + return t instanceof ReferenceType referenceType + && referenceType.getElementType().equals(this) && super.isSimilar(t); } @@ -105,9 +97,8 @@ public void refreshName() { @Override public boolean equals(Object o) { if (this == o) return true; - if (!(o instanceof ReferenceType)) return false; + if (!(o instanceof ReferenceType that)) return false; if (!super.equals(o)) return false; - ReferenceType that = (ReferenceType) o; return Objects.equals(reference, that.reference); } @@ -117,17 +108,13 @@ public int hashCode() { } @Override - public String toString() { + public @NotNull String toString() { return "ReferenceType{" + "reference=" + reference + ", typeName='" + this.getName() + '\'' - + ", storage=" - + this.getStorage() - + ", qualifier=" - + this.getQualifier() + ", origin=" + this.getTypeOrigin() + '}'; diff --git a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/StringType.kt b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/StringType.kt new file mode 100644 index 0000000000..633a1dbf8d --- /dev/null +++ b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/StringType.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 + +import de.fraunhofer.aisec.cpg.frontends.Language +import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend + +class StringType( + typeName: CharSequence = "", + language: Language? = null, + generics: List = listOf() +) : ObjectType(typeName, generics, false, language) { + + override fun duplicate(): Type { + return StringType(this.name, language, generics) + } +} diff --git a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/Type.java b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/Type.java index e0852ec0e5..ebabb3664f 100644 --- a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/Type.java +++ b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/Type.java @@ -33,9 +33,7 @@ import java.util.*; import org.apache.commons.lang3.builder.ToStringBuilder; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.neo4j.ogm.annotation.Relationship; -import org.neo4j.ogm.annotation.typeconversion.Convert; /** * Abstract Type, describing all possible SubTypes, i.e. all different Subtypes are compliant with @@ -49,106 +47,46 @@ public abstract class Type extends Node { @NotNull protected Set superTypes = new HashSet<>(); - /** - * auto, extern, static, register: consider "auto" as modifier or auto to automatically infer the - * value. - */ - @NotNull protected Storage storage = Storage.AUTO; - protected boolean primitive = false; - @Convert(QualifierConverter.class) - protected Qualifier qualifier; - protected Origin origin; public Type() { this.setName(new Name(EMPTY_NAME, null, this.getLanguage())); - this.storage = Storage.AUTO; - this.qualifier = new Qualifier(false, false, false, false); } public Type(String typeName) { this.setName(NameKt.parseName(this.getLanguage(), typeName)); - this.storage = Storage.AUTO; - this.qualifier = new Qualifier(); this.origin = Origin.UNRESOLVED; } public Type(Type type) { - this.storage = type.storage; this.setName(type.getName().clone()); - this.qualifier = - new Qualifier( - type.qualifier.isConst, - type.qualifier.isVolatile, - type.qualifier.isRestrict, - type.qualifier.isAtomic); this.origin = type.origin; } - public Type( - String typeName, - @Nullable Storage storage, - Qualifier qualifier, - Language language) { + public Type(CharSequence typeName, Language language) { if (this instanceof FunctionType) { - this.setName(new Name(typeName, null, language)); + this.setName(new Name(typeName.toString(), null, language)); } else { this.setName(NameKt.parseName(language, typeName)); } this.setLanguage(language); - this.storage = storage != null ? storage : Storage.AUTO; - this.qualifier = qualifier; this.origin = Origin.UNRESOLVED; } - public Type(Name fullTypeName, @Nullable Storage storage, Qualifier qualifier) { + public Type(Name fullTypeName, Language language) { this.setName(fullTypeName.clone()); - this.storage = storage != null ? storage : Storage.AUTO; - this.qualifier = qualifier; this.origin = Origin.UNRESOLVED; + this.setLanguage(language); } - /** - * All direct supertypes of this type. - * - * @return - */ + /** All direct supertypes of this type. */ @NotNull public Set getSuperTypes() { return superTypes; } - /** - * Describes Storage specifier of variables. Depending on the storage specifier, variables are - * stored in different parts e.g. in C/CPP AUTO stores the variable on the stack whereas static in - * the bss section - */ - public enum Storage { - AUTO, - EXTERN, - STATIC, - REGISTER - } - - @NotNull - public Storage getStorage() { - return storage; - } - - public void setStorage(@NotNull Storage storage) { - this.storage = storage; - } - - public Qualifier getQualifier() { - return qualifier; - } - - public void setQualifier(Qualifier qualifier) { - this.qualifier = qualifier; - } - public Origin getTypeOrigin() { return origin; } @@ -169,101 +107,6 @@ public enum Origin { UNRESOLVED } - /** - * Describes possible qualifiers that can be added to the type in order to modify its behavior. - * Supported: const (final), volatile, restrict, atomic - */ - public static class Qualifier { - private boolean isConst; // C, C++, Java - private boolean isVolatile; // C, C++, Java - private boolean isRestrict; // C - private boolean isAtomic; // C - - public Qualifier(boolean isConst, boolean isVolatile, boolean isRestrict, boolean isAtomic) { - this.isConst = isConst; - this.isVolatile = isVolatile; - this.isRestrict = isRestrict; - this.isAtomic = isAtomic; - } - - public Qualifier() { - this.isConst = false; - this.isVolatile = false; - this.isRestrict = false; - this.isAtomic = false; - } - - public boolean isConst() { - return isConst; - } - - public void setConst(boolean aConst) { - isConst = aConst; - } - - public boolean isVolatile() { - return isVolatile; - } - - public void setVolatile(boolean aVolatile) { - isVolatile = aVolatile; - } - - public boolean isRestrict() { - return isRestrict; - } - - public void setRestrict(boolean restrict) { - isRestrict = restrict; - } - - public boolean isAtomic() { - return isAtomic; - } - - public void setAtomic(boolean atomic) { - isAtomic = atomic; - } - - public Qualifier merge(Qualifier other) { - return new Qualifier( - this.isConst || other.isConst, - this.isVolatile || other.isVolatile, - this.isRestrict || other.isRestrict, - this.isAtomic || other.isAtomic); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof Qualifier)) return false; - Qualifier qualifier = (Qualifier) o; - return isConst == qualifier.isConst - && isVolatile == qualifier.isVolatile - && isRestrict == qualifier.isRestrict - && isAtomic == qualifier.isAtomic; - } - - @Override - public int hashCode() { - return Objects.hash(isConst, isVolatile, isRestrict, isAtomic); - } - - @Override - public String toString() { - return "Qualifier{" - + "isConst=" - + isConst - + ", isVolatile=" - + isVolatile - + ", isRestrict=" - + isRestrict - + ", isAtomic=" - + isAtomic - + '}'; - } - } - /** * @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 @@ -272,7 +115,7 @@ public String toString() { public abstract Type reference(PointerType.PointerOrigin pointer); /** - * @return Dereferences the current Type by resolving the reference. E.g. when dereferencing an + * @return Dereferences the current Type by resolving the reference. E.g. when dereferencing a * pointer type we obtain the type the pointer is pointing towards */ public abstract Type dereference(); @@ -281,7 +124,7 @@ public void refreshNames() {} /** * Obtain the root Type Element for a Type Chain (follows Pointer and ReferenceTypes until a - * Object-, Incomplete-, or FunctionPtrType is reached. + * Object-, Incomplete-, or FunctionPtrType is reached). * * @return root Type */ @@ -320,26 +163,9 @@ public int getReferenceDepth() { return 0; } - public void setAdditionalTypeKeywords(String keywords) { - List separatedKeywords = TypeParser.separate(keywords); - for (String keyword : separatedKeywords) { - if (TypeParser.isKnownSpecifier(keyword, getLanguage())) { - if (TypeParser.isStorageSpecifier(keyword, getLanguage())) { - List specifiers = new ArrayList<>(); - specifiers.add(keyword); - this.setStorage(TypeParser.calcStorage(specifiers)); - } else if (TypeParser.isQualifierSpecifier(keyword, getLanguage())) { - List qualifiers = new ArrayList<>(); - qualifiers.add(keyword); - this.setQualifier(TypeParser.calcQualifier(qualifiers, this.getQualifier())); - } - } - } - } - /** * @return True if the Type parameter t is a FirstOrderType (Root of a chain) and not a Pointer or - * RefrenceType + * ReferenceType */ public boolean isFirstOrderType() { return this instanceof ObjectType @@ -368,16 +194,14 @@ public boolean isSimilar(Type t) { @Override public boolean equals(Object o) { if (this == o) return true; - if (!(o instanceof Type)) return false; - Type type = (Type) o; + if (!(o instanceof Type type)) return false; return Objects.equals(getName(), type.getName()) - && storage == type.storage - && Objects.equals(qualifier, type.qualifier); + && Objects.equals(getLanguage(), type.getLanguage()); } @Override public int hashCode() { - return Objects.hash(getName(), storage, qualifier); + return Objects.hash(getName(), getLanguage()); } @NotNull 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 index 5b9e7907aa..8576f42e3b 100644 --- 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 @@ -27,8 +27,6 @@ import de.fraunhofer.aisec.cpg.ScopeManager; import de.fraunhofer.aisec.cpg.frontends.*; -import de.fraunhofer.aisec.cpg.frontends.cpp.CLanguage; -import de.fraunhofer.aisec.cpg.frontends.cpp.CPPLanguage; import de.fraunhofer.aisec.cpg.graph.TypeManager; import java.util.ArrayList; import java.util.List; @@ -47,107 +45,26 @@ public class TypeParser { private static final Logger log = LoggerFactory.getLogger(TypeParser.class); - public static final String UNKNOWN_TYPE_STRING = "UNKNOWN"; - // 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 String VOLATILE_QUALIFIER = "volatile"; - private static final String FINAL_QUALIFIER = "final"; - private static final String CONST_QUALIFIER = "const"; - private static final String RESTRICT_QUALIFIER = "restrict"; - private static final String ATOMIC_QUALIFIER = "atomic"; + 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"); } - /** - * Infers corresponding qualifier information for the type depending on the keywords. - * - * @param typeString list of found keywords - * @param old previous qualifier information which is completed with newer qualifier information - * @return Type.Qualifier - */ - public static Type.Qualifier calcQualifier(List typeString, Type.Qualifier old) { - boolean constantFlag = false; - boolean volatileFlag = false; - boolean restrictFlag = false; - boolean atomicFlag = false; - - if (old != null) { - constantFlag = old.isConst(); - volatileFlag = old.isVolatile(); - restrictFlag = old.isRestrict(); - atomicFlag = old.isAtomic(); - } - - for (String part : typeString) { - switch (part) { - case FINAL_QUALIFIER: - case CONST_QUALIFIER: - constantFlag = true; - break; - - case VOLATILE_QUALIFIER: - volatileFlag = true; - break; - - case RESTRICT_QUALIFIER: - restrictFlag = true; - break; - - case ATOMIC_QUALIFIER: - atomicFlag = true; - break; - default: - // do nothing - } - } - - return new Type.Qualifier(constantFlag, volatileFlag, restrictFlag, atomicFlag); - } - - /** - * Infers the corresponding storage type depending on the present storage keyword. Default AUTO - * - * @param typeString List of storage keywords - * @return Storage - */ - public static Type.Storage calcStorage(List typeString) { - for (String part : typeString) { - try { - return Type.Storage.valueOf(part.toUpperCase()); - } catch (IllegalArgumentException ignored) { - // continue in case of illegalArgumentException - } - } - return Type.Storage.AUTO; - } - - public static boolean isStorageSpecifier( - String specifier, Language language) { - // TODO: Convert to language trait - if (language instanceof CLanguage || language instanceof CPPLanguage) { - return specifier.equalsIgnoreCase("STATIC"); - } else { - try { - Type.Storage.valueOf(specifier.toUpperCase()); - return true; - } catch (IllegalArgumentException e) { - return false; - } - } - } - - protected static boolean isQualifierSpecifier( - String qualifier, Language language) { - return language instanceof HasQualifier - && ((HasQualifier) language).getQualifiers().contains(qualifier); - } - /** * 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 @@ -158,13 +75,8 @@ protected static boolean isQualifierSpecifier( */ public static boolean isElaboratedTypeSpecifier( String specifier, Language language) { - return language instanceof HasElaboratedTypeSpecifier - && ((HasElaboratedTypeSpecifier) language).getElaboratedTypeSpecifier().contains(specifier); - } - - public static boolean isKnownSpecifier( - String specifier, Language language) { - return isQualifierSpecifier(specifier, language) || isStorageSpecifier(specifier, language); + return language instanceof HasElaboratedTypeSpecifier hasElaboratedTypeSpecifier + && hasElaboratedTypeSpecifier.getElaboratedTypeSpecifier().contains(specifier); } /** @@ -201,7 +113,7 @@ private static int findMatching(char openBracket, char closeBracket, String stri * Matches the type blocks and checks if the typeString has the structure of a function pointer * * @param type separated type string - * @return true if function pointer structure is found in typeString, false if not + * @return the Matcher of the functionPointer or null */ @Nullable private static Matcher getFunctionPtrMatcher(@NotNull List type) { @@ -232,9 +144,9 @@ private static boolean isIncompleteType(String typeName) { private static boolean isUnknownType( String typeName, @NotNull Language language) { - return typeName.equals(UNKNOWN_TYPE_STRING) - || (language instanceof HasUnknownType - && ((HasUnknownType) language).getUnknownTypeString().contains(typeName)); + return typeName.equals(Type.UNKNOWN_TYPE_STRING) + || (language instanceof HasUnknownType hasUnknownType + && hasUnknownType.getUnknownTypeString().contains(typeName)); } /** @@ -247,68 +159,40 @@ private static boolean isUnknownType( @NotNull private static String fixGenerics( @NotNull String type, @NotNull Language language) { - if (type.contains("<") - && type.contains(">") - && language instanceof HasTemplates) { // TODO: Change to Trait - String generics = type.substring(type.indexOf('<') + 1, type.lastIndexOf('>')); - - /* Explanation from @vfsrfs: - * We fist extract the generic string (the substring between < and >). Then, the elaborate - * string can either start directly with the elaborate type specifier e.g. struct Node or it - * must be preceded by <, \\h (horizontal whitespace), or ,. If any other character precedes - * the elaborate type specifier then it is not considered to be a type specifier e.g. - * mystruct. Then there can be an arbitrary amount of horizontal whitespaces. This is followed - * by the elaborate type specifier and at least one more horizontal whitespace, which marks - * that it is indeed an elaborate type and not something like structMy. - */ - for (String elaborate : - ((HasElaboratedTypeSpecifier) language).getElaboratedTypeSpecifier()) { - generics = generics.replaceAll("(^|(?<=[\\h,<]))\\h*(?

" + elaborate + "\\h+)", ""); + 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('<') + 1) - + generics.trim() - + type.substring(type.lastIndexOf('>')); - } - - StringBuilder out = new StringBuilder(); - int bracketCount = 0; - int iterator = 0; - while (iterator < type.length()) { - switch (type.charAt(iterator)) { - case '<': - bracketCount++; - out.append(type.charAt(iterator)); - break; - - case '>': - out.append('>'); - bracketCount--; - break; - - case ' ': - if (bracketCount == 0) { - out.append(type.charAt(iterator)); - } - break; - - default: - out.append(type.charAt(iterator)); - break; - } - iterator++; - } - - String[] splitted = out.toString().split("\\<"); - StringBuilder out2 = new StringBuilder(); - for (String s : splitted) { - if (out2.length() > 0) { - out2.append('<'); - } - out2.append(s.trim()); + 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 out2.toString(); + return type; } private static void processBlockUntilLastSplit( @@ -326,7 +210,8 @@ private static void processBlockUntilLastSplit( * @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) { + public static List separate( + @NotNull String type, Language language) { type = type.split("=")[0]; // Guarantee that there is no arbitrary number of whitespaces @@ -337,63 +222,60 @@ public static List separate(@NotNull String type) { // Splits TypeString into relevant information blocks int lastSplit = 0; - int finishPosition = 0; - String substr = ""; - int i = 0; - while (i < type.length()) { + for (int i = 0; i < type.length(); i++) { char ch = type.charAt(i); switch (ch) { - case ' ': + case ' ' -> { // handle space create element processBlockUntilLastSplit(type, lastSplit, i, typeBlocks); lastSplit = i + 1; - break; - - case '(': + } + case '(' -> { // handle ( find matching closing ignore content (not relevant type information) processBlockUntilLastSplit(type, lastSplit, i, typeBlocks); - finishPosition = findMatching('(', ')', type.substring(i + 1)); + int finishPosition = findMatching('(', ')', type.substring(i + 1)); typeBlocks.add(type.substring(i, i + finishPosition + 1)); i = finishPosition + i; lastSplit = i + 1; - break; - - case '[': + } + case '[' -> { // handle [ find matching closing ignore content (not relevant type information) - processBlockUntilLastSplit(type, lastSplit, i, typeBlocks); - - finishPosition = findMatching('[', ']', type.substring(i + 1)); - typeBlocks.add("[]"); // type.substring(i, i+finishPosition+1) - i = finishPosition + i; - lastSplit = i + 1; - break; - - case '*': + 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; - break; - - case '&': + } + case '&' -> { // handle & operator processBlockUntilLastSplit(type, lastSplit, i, typeBlocks); - typeBlocks.add("&"); lastSplit = i + 1; - break; - - default: + } + default -> { // everything else - substr = type.substring(lastSplit); + String substr = type.substring(lastSplit); if (substr.length() != 0 && i == type.length() - 1) { typeBlocks.add(substr); } - break; + } } - i++; } return typeBlocks; @@ -407,7 +289,7 @@ private static List getParameterList( List parameters = new ArrayList<>(); String[] parametersSplit = parameterList.split(","); for (String parameter : parametersSplit) { - // ignore void parameters + // ignore void parameters // TODO: WHY?? if (parameter.length() > 0 && !parameter.trim().equals("void")) { parameters.add(createFrom(parameter.trim(), language)); } @@ -418,17 +300,21 @@ private static List getParameterList( private static List getGenerics( String typeName, Language language) { - if (typeName.contains("<") && typeName.contains(">")) { - String generics = typeName.substring(typeName.indexOf('<') + 1, typeName.lastIndexOf('>')); - List genericList = new ArrayList<>(); + 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)); } - - return genericList; } - return new ArrayList<>(); + return genericList; } private static Type performBracketContentAction( @@ -444,24 +330,9 @@ private static Type performBracketContentAction( if (part.startsWith("[") && part.endsWith("]")) { return finalType.reference(PointerType.PointerOrigin.ARRAY); } - if (part.startsWith("(") && part.endsWith(")")) { - List subBracketExpression = new ArrayList<>(); - subBracketExpression.add(part); - return resolveBracketExpression(finalType, subBracketExpression, language); - } - if (isStorageSpecifier(part, language)) { - List specifiers = new ArrayList<>(); - specifiers.add(part); - finalType.setStorage(calcStorage(specifiers)); - return finalType; - } - - if (isQualifierSpecifier(part, language)) { - List qualifiers = new ArrayList<>(); - qualifiers.add(part); - finalType.setQualifier(calcQualifier(qualifiers, finalType.getQualifier())); - return finalType; + if (part.startsWith("(") && part.endsWith(")")) { + return resolveBracketExpression(finalType, List.of(part), language); } return finalType; @@ -482,7 +353,7 @@ private static Type resolveBracketExpression( @NotNull Language language) { for (String bracketExpression : bracketExpressions) { List splitExpression = - separate(bracketExpression.substring(1, bracketExpression.length() - 1)); + separate(bracketExpression.substring(1, bracketExpression.length() - 1), language); for (String part : splitExpression) { finalType = performBracketContentAction(finalType, part, language); } @@ -497,8 +368,9 @@ private static Type resolveBracketExpression( * @param type provided typeString * @return typeString without access modifier */ - private static String removeAccessModifier(@NotNull String type) { - return type.replaceAll("public|private|protected", "").trim(); + private static String removeAccessModifier( + @NotNull String type, @NotNull Language language) { + return type.replaceAll(String.join("|", language.getAccessModifiers()), "").trim(); } /** @@ -510,12 +382,7 @@ private static String removeAccessModifier(@NotNull String type) { */ private static boolean isPrimitiveType( @NotNull List stringList, @NotNull Language language) { - for (String s : stringList) { - if (language.getPrimitiveTypes().contains(s)) { - return true; - } - } - return false; + return stringList.stream().anyMatch(s -> language.getPrimitiveTypes().contains(s)); } /** @@ -530,26 +397,23 @@ private static List joinPrimitive( @NotNull List typeBlocks, @NotNull Language language) { List joinedTypeBlocks = new ArrayList<>(); StringBuilder primitiveType = new StringBuilder(); - boolean foundPrimitive = false; + int index = 0; for (String s : typeBlocks) { if (language.getPrimitiveTypes().contains(s)) { if (primitiveType.length() > 0) { primitiveType.append(" "); + } else { + index = joinedTypeBlocks.size(); } primitiveType.append(s); + } else { + joinedTypeBlocks.add(s); } } - for (String s : typeBlocks) { - if (language.getPrimitiveTypes().contains(s) && !foundPrimitive) { - joinedTypeBlocks.add(primitiveType.toString()); - foundPrimitive = true; - } else { - if (!language.getPrimitiveTypes().contains(s)) { - joinedTypeBlocks.add(s); - } - } + if (!primitiveType.isEmpty()) { + joinedTypeBlocks.add(index, primitiveType.toString()); } return joinedTypeBlocks; @@ -576,8 +440,8 @@ public static Type reWrapType(@NotNull Type oldChain, @NotNull Type newRoot) { if (oldChain instanceof ObjectType && newRoot instanceof ObjectType) { ((ObjectType) newRoot.getRoot()).setGenerics(((ObjectType) oldChain).getGenerics()); return newRoot; - } else if (oldChain instanceof ReferenceType) { - Type reference = reWrapType(((ReferenceType) oldChain).getElementType(), newRoot); + } else if (oldChain instanceof ReferenceType referenceType) { + Type reference = reWrapType(referenceType.getElementType(), newRoot); ReferenceType newChain = (ReferenceType) oldChain.duplicate(); newChain.setElementType(reference); newChain.refreshName(); @@ -609,8 +473,7 @@ public static Type createIgnoringAlias( private static Type postTypeParsing( @NotNull List subPart, @NotNull Type finalType, - @NotNull List bracketExpressions, - @NotNull Language language) { + @NotNull List bracketExpressions) { for (String part : subPart) { if (part.equals("*")) { // Creates a Pointer to the finalType @@ -620,13 +483,7 @@ private static Type postTypeParsing( 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 - Type.Qualifier oldQualifier = finalType.getQualifier(); - Type.Storage oldStorage = finalType.getStorage(); - finalType.setQualifier(new Type.Qualifier()); - finalType.setStorage(Type.Storage.AUTO); finalType = new ReferenceType(finalType); - finalType.setStorage(oldStorage); - finalType.setQualifier(oldQualifier); } if (part.startsWith("[") && part.endsWith("]")) { @@ -639,41 +496,30 @@ private static Type postTypeParsing( // processed afterwards bracketExpressions.add(part); } - - // Check storage and qualifiers specifierd that are defined after the typeName e.g. int const - if (isStorageSpecifier(part, language)) { - List specifiers = new ArrayList<>(); - specifiers.add(part); - finalType.setStorage(calcStorage(specifiers)); - } - - if (isQualifierSpecifier(part, language)) { - List qualifiers = new ArrayList<>(); - qualifiers.add(part); - finalType.setQualifier(calcQualifier(qualifiers, finalType.getQualifier())); - } } return finalType; } - private static String removeGenerics(String typeName) { - if (typeName.contains("<") && typeName.contains(">")) { - typeName = typeName.substring(0, typeName.indexOf('<')); + 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 ObjectType.Modifier determineModifier( - List typeBlocks, boolean primitiveType) { + 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 - ObjectType.Modifier modifier = ObjectType.Modifier.NOT_APPLICABLE; + String modifier = ""; if (primitiveType) { if (typeBlocks.contains("unsigned")) { - modifier = ObjectType.Modifier.UNSIGNED; + modifier = "unsigned "; typeBlocks.remove("unsigned"); - } else { - modifier = ObjectType.Modifier.SIGNED; + } else if (typeBlocks.contains("signed")) { + modifier = "signed "; typeBlocks.remove("signed"); } } @@ -708,14 +554,14 @@ private static Type createFromUnsafe( } // Preprocessing of the typeString - type = removeAccessModifier(type); + 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); + List typeBlocks = separate(type, language); // Depending on if the Type is primitive or not signed/unsigned must be set differently (only // relevant for ObjectTypes) @@ -723,35 +569,25 @@ private static Type createFromUnsafe( // Default is signed, unless unsigned keyword is specified. For other classes that are not // primitive this is NOT_APPLICABLE - ObjectType.Modifier modifier = determineModifier(typeBlocks, primitiveType); + 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); - List qualifierList = new ArrayList<>(); - List storageList = new ArrayList<>(); - // Handle preceding qualifier or storage specifier to the type name e.g. static const int int counter = 0; + for (String part : typeBlocks) { - if (isQualifierSpecifier(part, language)) { - qualifierList.add(part); - counter++; - } else if (isStorageSpecifier(part, language)) { - storageList.add(part); - counter++; - } else if (isElaboratedTypeSpecifier(part, language)) { - // ignore elaborated types for now + if (potentialKeywords.contains(part.toUpperCase()) + || isElaboratedTypeSpecifier(part, language)) { + // We only want to get rid of these parts for the remaining method. counter++; } else { break; } } - Type.Storage storageValue = calcStorage(storageList); - Type.Qualifier qualifier = calcQualifier(qualifierList, null); - // 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. @@ -766,34 +602,26 @@ private static Type createFromUnsafe( // Check if type is FunctionPointer Matcher funcptr = getFunctionPtrMatcher(typeBlocks.subList(counter, typeBlocks.size())); - if (funcptr != null) { + finalType = language.getSimpleTypeOf(modifier + typeName); + if (finalType != null) { + // Nothing to do here + } else if (funcptr != null) { Type returnType = createFrom(typeName, language); List parameterList = getParameterList(funcptr.group("args"), language); - return typeManager.registerType( - new FunctionPointerType(qualifier, storageValue, parameterList, returnType, language)); + return typeManager.registerType(new FunctionPointerType(parameterList, returnType, language)); } 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(UNKNOWN_TYPE_STRING); + finalType = new UnknownType(Type.UNKNOWN_TYPE_STRING); } else { // ObjectType // Obtain possible generic List from TypeString List generics = getGenerics(typeName, language); - typeName = removeGenerics(typeName); - finalType = - new ObjectType( - typeName, storageValue, qualifier, generics, modifier, primitiveType, language); - } - - if (finalType.getName().getLocalName().equals("auto") - || (type.contains("auto") && !primitiveType)) { - // In C++17 if auto keyword is used the compiler infers the type automatically, hence we - // are not able to find out, which type this should be, it will be resolved due to - // dataflow - return UnknownType.getUnknownType(language); + typeName = removeGenerics(typeName, language); + finalType = new ObjectType(typeName, generics, primitiveType, language); } // Process Keywords / Operators (*, &) after typeName @@ -801,7 +629,7 @@ private static Type createFromUnsafe( List bracketExpressions = new ArrayList<>(); - finalType = postTypeParsing(subPart, finalType, bracketExpressions, language); + finalType = postTypeParsing(subPart, finalType, bracketExpressions); // Resolve BracketExpressions that were identified previously finalType = resolveBracketExpression(finalType, bracketExpressions, language); diff --git a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/UnknownType.java b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/UnknownType.java index c6af1192a9..39e24ca075 100644 --- a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/UnknownType.java +++ b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/types/UnknownType.java @@ -112,16 +112,6 @@ public String toString() { return "UNKNOWN"; } - @Override - public void setStorage(@NotNull Storage storage) { - // Only one instance of UnknownType, use default values - } - - @Override - public void setQualifier(Qualifier qualifier) { - // Only one instance of UnknownType, use default values - } - @Override public void setTypeOrigin(Origin origin) { // Only one instance of UnknownType, use default values 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 7945cac3fc..54b770ab2c 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 @@ -365,7 +365,10 @@ private constructor( return Optional.ofNullable(frontend) } - private fun getFrontend(file: File, scopeManager: ScopeManager): LanguageFrontend? { + private fun getFrontend( + file: File, + scopeManager: ScopeManager, + ): LanguageFrontend? { val language = file.language return if (language != null) { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationResult.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationResult.kt index 530a582018..249e0522eb 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationResult.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationResult.kt @@ -25,10 +25,7 @@ */ package de.fraunhofer.aisec.cpg -import de.fraunhofer.aisec.cpg.graph.Component -import de.fraunhofer.aisec.cpg.graph.Name -import de.fraunhofer.aisec.cpg.graph.Node -import de.fraunhofer.aisec.cpg.graph.SubGraph +import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration import de.fraunhofer.aisec.cpg.helpers.MeasurementHolder import de.fraunhofer.aisec.cpg.helpers.StatisticsHolder 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 45a29bf4e7..7f1f00ef09 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 @@ -25,6 +25,7 @@ */ package de.fraunhofer.aisec.cpg.frontends +import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.core.JsonGenerator import com.fasterxml.jackson.databind.SerializerProvider import com.fasterxml.jackson.databind.annotation.JsonSerialize @@ -32,8 +33,10 @@ import com.fasterxml.jackson.databind.ser.std.StdSerializer import de.fraunhofer.aisec.cpg.ScopeManager import de.fraunhofer.aisec.cpg.TranslationConfiguration import de.fraunhofer.aisec.cpg.graph.Node +import de.fraunhofer.aisec.cpg.graph.types.* import java.io.File import kotlin.reflect.KClass +import org.neo4j.ogm.annotation.Transient /** * Represents a programming language. When creating new languages in the CPG, one must derive custom @@ -55,20 +58,56 @@ abstract class Language : Node() { abstract val frontend: KClass /** The primitive types of this language. */ - open val primitiveTypes: Set - get() = setOf("byte", "short", "int", "long", "float", "double", "boolean", "char") + @get:JsonIgnore + val primitiveTypes: Set + get() = simpleTypes.keys + + // TODO: Maybe make this abstract? + @get:JsonIgnore + @Transient + open val simpleTypes: Map = + mapOf( + "boolean" to IntegerType("boolean", 1, this, NumericType.Modifier.SIGNED), + "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), + "int" to IntegerType("int", 32, this, NumericType.Modifier.SIGNED), + "long" to IntegerType("long", 64, this, NumericType.Modifier.SIGNED), + "float" to FloatingPointType("float", 32, this, NumericType.Modifier.SIGNED), + "double" to FloatingPointType("double", 64, this, NumericType.Modifier.SIGNED), + ) + + /** The access modifiers of this programming language */ + open val accessModifiers: Set + get() = setOf("public", "protected", "private") /** Creates a new [LanguageFrontend] object to parse the language. */ abstract fun newFrontend( config: TranslationConfiguration, - scopeManager: ScopeManager = ScopeManager() + scopeManager: ScopeManager = ScopeManager(), ): T + fun getSimpleTypeOf(typeString: String) = simpleTypes[typeString] + /** Returns true if the [file] can be handled by the frontend of this language. */ fun handlesFile(file: File): Boolean { return file.extension in fileExtensions } + override fun equals(other: Any?): Boolean { + return other?.javaClass == this.javaClass + } + + override fun hashCode(): Int { + var result = super.hashCode() + result = 31 * result + fileExtensions.hashCode() + result = 31 * result + namespaceDelimiter.hashCode() + result = 31 * result + frontend.hashCode() + result = 31 * result + primitiveTypes.hashCode() + result = 31 * result + accessModifiers.hashCode() + return result + } + init { this.also { this.language = it } } 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 d66b079dad..a97a156165 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 @@ -48,7 +48,7 @@ import org.slf4j.LoggerFactory abstract class LanguageFrontend( override val language: Language, val config: TranslationConfiguration, - scopeManager: ScopeManager + scopeManager: ScopeManager, ) : ProcessedListener(), CodeAndLocationProvider, diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageTraits.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageTraits.kt index 69f710f81b..9b26c0e5cd 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageTraits.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageTraits.kt @@ -46,8 +46,16 @@ import java.util.regex.Pattern interface LanguageTrait /** A language trait, that specifies that this language has support for templates or generics. */ -interface HasTemplates : LanguageTrait { +interface HasGenerics : LanguageTrait { + /** The char starting the template specific code (e.g. '<') */ + val startCharacter: Char + /** The char ending the template specific code (e.g. '>') */ + val endCharacter: Char +} + +/** A language trait, that specifies that this language has support for templates or generics. */ +interface HasTemplates : HasGenerics { /** * This function can be used to fine-tune the resolution of template function calls. * diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CLanguage.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CLanguage.kt index 330298aa3c..7084e6b639 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CLanguage.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CLanguage.kt @@ -25,9 +25,11 @@ */ package de.fraunhofer.aisec.cpg.frontends.cpp +import com.fasterxml.jackson.annotation.JsonIgnore import de.fraunhofer.aisec.cpg.ScopeManager import de.fraunhofer.aisec.cpg.TranslationConfiguration import de.fraunhofer.aisec.cpg.frontends.* +import de.fraunhofer.aisec.cpg.graph.types.* import kotlin.reflect.KClass /** The C language. */ @@ -46,9 +48,40 @@ open class CLanguage : override val conjunctiveOperators = listOf("&&") override val disjunctiveOperators = listOf("||") + @Transient + @JsonIgnore + override val simpleTypes: Map = + mapOf( + "boolean" to IntegerType("boolean", 1, this, NumericType.Modifier.SIGNED), + "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), + "int" to IntegerType("int", 32, this, NumericType.Modifier.SIGNED), + "long" to IntegerType("long", 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 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), + "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 int" to + IntegerType("unsigned long long int", 64, this, NumericType.Modifier.UNSIGNED) + ) + override fun newFrontend( config: TranslationConfiguration, - scopeManager: ScopeManager + scopeManager: ScopeManager, ): CXXLanguageFrontend { return CXXLanguageFrontend(this, config, scopeManager) } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CPPLanguage.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CPPLanguage.kt index 56f33da91a..8a7a15362c 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CPPLanguage.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CPPLanguage.kt @@ -26,6 +26,7 @@ package de.fraunhofer.aisec.cpg.frontends.cpp import de.fraunhofer.aisec.cpg.ScopeManager +import de.fraunhofer.aisec.cpg.frontends.* import de.fraunhofer.aisec.cpg.frontends.HasClasses import de.fraunhofer.aisec.cpg.frontends.HasComplexCallResolution import de.fraunhofer.aisec.cpg.frontends.HasDefaultArguments @@ -35,17 +36,53 @@ import de.fraunhofer.aisec.cpg.graph.declarations.* import de.fraunhofer.aisec.cpg.graph.edge.Properties import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.MemberCallExpression -import de.fraunhofer.aisec.cpg.graph.types.ParameterizedType -import de.fraunhofer.aisec.cpg.graph.types.Type +import de.fraunhofer.aisec.cpg.graph.types.* import de.fraunhofer.aisec.cpg.passes.* import de.fraunhofer.aisec.cpg.passes.inference.startInference import java.util.regex.Pattern /** The C++ language. */ class CPPLanguage : - CLanguage(), HasDefaultArguments, HasTemplates, HasComplexCallResolution, HasClasses { + CLanguage(), + HasDefaultArguments, + HasTemplates, + HasComplexCallResolution, + HasClasses, + HasUnknownType { override val fileExtensions = listOf("cpp", "cc", "cxx", "hpp", "hh") override val elaboratedTypeSpecifier = listOf("class", "struct", "union", "enum") + override val unknownTypeString = listOf("auto") + + @Transient + override val simpleTypes = + mapOf( + "boolean" to IntegerType("boolean", 1, this, NumericType.Modifier.SIGNED), + "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), + "int" to IntegerType("int", 32, this, NumericType.Modifier.SIGNED), + "long" to IntegerType("long", 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 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), + "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 int" to + IntegerType("unsigned long long int", 64, this, NumericType.Modifier.UNSIGNED), + "std::string" to StringType("std::string", this), + ) /** * @param call @@ -185,6 +222,9 @@ class CPPLanguage : return resolveWithDefaultArgs(call, invocationCandidates) } + override val startCharacter = '<' + override val endCharacter = '>' + /** * @param curClass class the invoked method must be part of. * @param templateCall call to instantiate and invoke a function template diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXLanguageFrontend.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXLanguageFrontend.kt index 99f1bfd184..1ef8501048 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXLanguageFrontend.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXLanguageFrontend.kt @@ -79,7 +79,7 @@ import org.slf4j.LoggerFactory class CXXLanguageFrontend( language: Language, config: TranslationConfiguration, - scopeManager: ScopeManager + scopeManager: ScopeManager, ) : LanguageFrontend(language, config, scopeManager) { /** @@ -402,10 +402,15 @@ class CXXLanguageFrontend( 130 -> // a string newLiteral( if (code.length >= 2) code.substring(1, code.length - 1) else "", - newPrimitiveType("char").const().reference(), + newPrimitiveType("char", NumericType.Modifier.NOT_APPLICABLE).reference(), + code + ) + else -> + newLiteral( + code, + newPrimitiveType("char", NumericType.Modifier.NOT_APPLICABLE).reference(), code ) - else -> newLiteral(code, newPrimitiveType("char").const().reference(), code) } return newAnnotationMember("", expression, code) } @@ -568,7 +573,7 @@ class CXXLanguageFrontend( type.reference(PointerType.PointerOrigin.POINTER) } is ICPPASTReferenceOperator -> { - ReferenceType(type.storage, type.qualifier, type) + ReferenceType(type) } else -> { type @@ -614,8 +619,7 @@ class CXXLanguageFrontend( ) { it.typeName } + type.typeName - type = - FunctionType(name, paramTypes, listOf(type), language, type.qualifier, type.storage) + type = FunctionType(name, paramTypes, listOf(type), language) } // Lastly, there might be further nested declarators that adjust the type further. diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/ExpressionHandler.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/ExpressionHandler.kt index be678e29b8..8ba9bde075 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/ExpressionHandler.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/ExpressionHandler.kt @@ -27,7 +27,6 @@ package de.fraunhofer.aisec.cpg.frontends.java import com.github.javaparser.Range import com.github.javaparser.TokenRange -import com.github.javaparser.ast.Modifier import com.github.javaparser.ast.Node import com.github.javaparser.ast.expr.* import com.github.javaparser.ast.expr.Expression @@ -45,7 +44,6 @@ 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.util.function.Supplier -import java.util.stream.Collectors import kotlin.collections.set import org.slf4j.LoggerFactory @@ -196,12 +194,6 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : for (variable in variableDeclarationExpr.variables) { val resolved = variable.resolve() val declarationType = frontend.getTypeAsGoodAsPossible(variable, resolved) - declarationType.setAdditionalTypeKeywords( - variableDeclarationExpr.modifiers - .stream() - .map { m: Modifier -> m.keyword.asString() } - .collect(Collectors.joining(" ")) - ) val declaration = this.newVariableDeclaration( resolved.name, diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguage.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguage.kt index 38bf38b51f..c1437aff7e 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguage.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguage.kt @@ -25,9 +25,11 @@ */ package de.fraunhofer.aisec.cpg.frontends.java +import com.fasterxml.jackson.annotation.JsonIgnore import de.fraunhofer.aisec.cpg.ScopeManager import de.fraunhofer.aisec.cpg.TranslationConfiguration import de.fraunhofer.aisec.cpg.frontends.* +import de.fraunhofer.aisec.cpg.graph.types.* import kotlin.reflect.KClass /** The Java language. */ @@ -36,7 +38,7 @@ open class JavaLanguage : // HasComplexCallResolution, HasClasses, HasSuperClasses, - // HasTemplates, + HasGenerics, HasQualifier, HasUnknownType, HasShortCircuitOperators { @@ -49,10 +51,29 @@ open class JavaLanguage : override val conjunctiveOperators = listOf("&&") override val disjunctiveOperators = listOf("||") + @Transient + @JsonIgnore + override val simpleTypes = + mapOf( + "boolean" to IntegerType("boolean", 1, this, NumericType.Modifier.SIGNED), + "byte" to IntegerType("byte", 8, this, NumericType.Modifier.SIGNED), + "char" to IntegerType("char", 16, this, NumericType.Modifier.SIGNED), + "short" 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), + "float" to FloatingPointType("float", 32, this, NumericType.Modifier.SIGNED), + "double" to FloatingPointType("double", 64, this, NumericType.Modifier.SIGNED), + "String" to StringType("java.lang.String", this), + "java.lang.String" to StringType("java.lang.String", this) + ) + override fun newFrontend( config: TranslationConfiguration, - scopeManager: ScopeManager + scopeManager: ScopeManager, ): JavaLanguageFrontend { return JavaLanguageFrontend(this, config, scopeManager) } + + override val startCharacter = '<' + override val endCharacter = '>' } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontend.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontend.kt index 562b7b7877..ca4283bcba 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontend.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontend.kt @@ -70,7 +70,7 @@ import java.util.function.Consumer open class JavaLanguageFrontend( language: Language, config: TranslationConfiguration, - scopeManager: ScopeManager + scopeManager: ScopeManager, ) : LanguageFrontend(language, config, scopeManager) { var context: CompilationUnit? = null diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Component.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Component.kt index 855f33aeb0..d7fb26312b 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Component.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Component.kt @@ -34,7 +34,7 @@ import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration * This node holds all translation units belonging to this software component as well as (potential) * entry points or interactions with other software. */ -open class Component : Node() { +open class Component() : Node() { /** All translation units belonging to this application. */ @field:SubGraph("AST") val translationUnits: MutableList = mutableListOf() 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 5d7c492d36..9afcfee3d7 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 @@ -116,37 +116,14 @@ fun Node.applyMetadata( this.scope = provider.scope } - val namespace = - if (provider is NamespaceProvider) { - provider.namespace ?: defaultNamespace - } else { - defaultNamespace - } - if (name != null) { - val language = this.language - - // The name could already be a real "name" (of our Name class). In this case we can just set - // the name (if it is qualified). This is preferred over passing an FQN as - // CharSequence/String. - if (name is Name && name.isQualified()) { - this.name = name - } else if (language != null && name.contains(language.namespaceDelimiter)) { - // Let's check, if this is an FQN as string / char sequence by any chance. Then we need - // to parse the name. In the future, we might drop compatibility for this - this.name = language.parseName(name) - } else { - // Otherwise, a local name is supplied. Some nodes only want a local name. In this case, - // we create a new name with the supplied (local) name and set the parent to null. - val parent = - if (localNameOnly) { - null - } else { - namespace - } - - this.name = Name(name.toString(), parent, language?.namespaceDelimiter ?: ".") - } + val namespace = + if (provider is NamespaceProvider) { + provider.namespace ?: defaultNamespace + } else { + defaultNamespace + } + this.name = this.newName(name, localNameOnly, namespace) } if (codeOverride != null) { @@ -154,6 +131,40 @@ fun Node.applyMetadata( } } +/** + * Generates a [Name] object from the given [name]. If [localNameOnly] is set, only the localName is + * used, otherwise the [namespace] is added to generate a fqn if the [name] is not a fqn anyway. + */ +fun LanguageProvider.newName( + name: CharSequence, + localNameOnly: Boolean = false, + namespace: Name? = null +): Name { + val language = this.language + + // The name could already be a real "name" (of our Name class). In this case we can just set + // the name (if it is qualified). This is preferred over passing an FQN as + // CharSequence/String. + return if (name is Name && name.isQualified()) { + name + } else if (language != null && name.contains(language.namespaceDelimiter)) { + // Let's check, if this is an FQN as string / char sequence by any chance. Then we need + // to parse the name. In the future, we might drop compatibility for this + language.parseName(name) + } else { + // Otherwise, a local name is supplied. Some nodes only want a local name. In this case, + // we create a new name with the supplied (local) name and set the parent to null. + val parent = + if (localNameOnly) { + null + } else { + namespace + } + + Name(name.toString(), parent, language?.namespaceDelimiter ?: ".") + } +} + /** * Creates a new [Annotation]. The [MetadataProvider] receiver will be used to fill different * meta-data using [Node.applyMetadata]. Calling this extension function outside of Kotlin requires diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/TypeBuilder.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/TypeBuilder.kt index 9ebef1d601..454ca3a452 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/TypeBuilder.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/TypeBuilder.kt @@ -25,32 +25,28 @@ */ package de.fraunhofer.aisec.cpg.graph +import de.fraunhofer.aisec.cpg.graph.types.NumericType +import de.fraunhofer.aisec.cpg.graph.types.NumericType.Modifier import de.fraunhofer.aisec.cpg.graph.types.ObjectType -import de.fraunhofer.aisec.cpg.graph.types.ObjectType.Modifier -import de.fraunhofer.aisec.cpg.graph.types.Type -fun MetadataProvider.newPrimitiveType( +fun LanguageProvider.newPrimitiveType( name: String, modifier: Modifier = Modifier.SIGNED, - qualifier: Type.Qualifier = Type.Qualifier() ): ObjectType { - val type = - ObjectType( - name, - Type.Storage.AUTO, - qualifier, - listOf(), - modifier, - true, - (this as? LanguageProvider)?.language!! - ) - + val type = language?.getSimpleTypeOf(name) + if ((type as? NumericType)?.modifier != modifier) { + // Try again but explicitly state "signed" or "unsigned" as our best guess. + val modifierStr = + when (modifier) { + Modifier.SIGNED -> "signed " + Modifier.UNSIGNED -> "unsigned " + else -> "" + } + return language?.getSimpleTypeOf(modifierStr + name) as ObjectType + } return type } fun ObjectType.const(): ObjectType { - val constType = ObjectType(this, listOf(), this.modifier, this.isPrimitive, this.language) - constType.qualifier.isConst = true - - return constType + return ObjectType(this, listOf(), this.isPrimitive, this.language) } 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 bc0e575cd8..de31537993 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 @@ -127,11 +127,9 @@ open class FunctionDeclaration : ValueDeclaration(), DeclarationHolder { val declared = signature[i] if (declared.isVariadic && targetSignature.size >= signature.size) { // Everything that follows is collected by this param, so the signature is - // fulfilled no - // matter what comes now (potential FIXME: in Java, we could have overloading - // with - // different vararg types, in C++ we can't, as vararg types are not defined here - // anyways) + // fulfilled no matter what comes now (potential FIXME: in Java, we could have + // overloading with different vararg types, in C++ we can't, as vararg types are + // not defined here anyways) return true } val provided = targetSignature[i] 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 c064f11ac2..4dcfb155cb 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 @@ -35,8 +35,6 @@ import de.fraunhofer.aisec.cpg.graph.types.FunctionPointerType import de.fraunhofer.aisec.cpg.graph.types.ReferenceType import de.fraunhofer.aisec.cpg.graph.types.Type import de.fraunhofer.aisec.cpg.graph.types.UnknownType -import java.util.function.Consumer -import java.util.function.Predicate import java.util.stream.Collectors import org.apache.commons.lang3.builder.ToStringBuilder import org.neo4j.ogm.annotation.Relationship @@ -152,7 +150,6 @@ abstract class ValueDeclaration : Declaration(), HasType { } val oldType = this.type type = type.duplicate() - type.qualifier = this.type.qualifier.merge(type.qualifier) val subTypes: MutableSet = HashSet() for (t in possibleSubTypes) { if (!t.isSimilar(type)) { @@ -187,14 +184,12 @@ abstract class ValueDeclaration : Declaration(), HasType { var possibleSubTypes = possibleSubTypes possibleSubTypes = possibleSubTypes - .stream() - .filter(Predicate.not { type: Type? -> TypeManager.getInstance().isUnknown(type) }) + .filterNot { type -> TypeManager.getInstance().isUnknown(type) } .distinct() - .collect(Collectors.toList()) + .toMutableList() if (!TypeManager.isTypeSystemActive()) { - possibleSubTypes.forEach( - Consumer { t: Type? -> TypeManager.getInstance().cacheType(this, t) } - ) + possibleSubTypes.forEach { t -> TypeManager.getInstance().cacheType(this, t) } + return } if (root.contains(this)) { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ArraySubscriptionExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ArraySubscriptionExpression.kt index e01ffa64e2..0c3783fdef 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ArraySubscriptionExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ArraySubscriptionExpression.kt @@ -25,10 +25,7 @@ */ package de.fraunhofer.aisec.cpg.graph.statements.expressions -import de.fraunhofer.aisec.cpg.graph.HasBase -import de.fraunhofer.aisec.cpg.graph.HasType -import de.fraunhofer.aisec.cpg.graph.SubGraph -import de.fraunhofer.aisec.cpg.graph.TypeManager +import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.types.Type import java.util.* import java.util.stream.Collectors 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 9cf2c9cac6..720f291cf2 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 @@ -32,9 +32,6 @@ import de.fraunhofer.aisec.cpg.graph.types.ReferenceType import de.fraunhofer.aisec.cpg.graph.types.Type import de.fraunhofer.aisec.cpg.graph.types.UnknownType import java.util.* -import java.util.function.Consumer -import java.util.function.Predicate -import java.util.stream.Collectors import org.apache.commons.lang3.builder.ToStringBuilder import org.neo4j.ogm.annotation.Transient @@ -127,8 +124,6 @@ abstract class Expression : Statement(), HasType { // Backup to check if something changed type = type.duplicate() - type.qualifier = this.type.qualifier.merge(type.qualifier) - val subTypes = mutableSetOf() // Check all current subtypes and consider only those which are "different enough" to type. @@ -176,14 +171,11 @@ abstract class Expression : Statement(), HasType { var possibleSubTypes = possibleSubTypes possibleSubTypes = possibleSubTypes - .stream() - .filter(Predicate.not { type: Type? -> TypeManager.getInstance().isUnknown(type) }) + .filterNot { type -> TypeManager.getInstance().isUnknown(type) } .distinct() - .collect(Collectors.toList()) + .toMutableList() if (!TypeManager.isTypeSystemActive()) { - possibleSubTypes.forEach( - Consumer { t: Type? -> TypeManager.getInstance().cacheType(this, t) } - ) + possibleSubTypes.forEach { t -> TypeManager.getInstance().cacheType(this, t) } return } if (root.contains(this)) { 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 934ffcbc04..b405caba25 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 @@ -36,7 +36,6 @@ import de.fraunhofer.aisec.cpg.graph.types.PointerType.PointerOrigin import de.fraunhofer.aisec.cpg.graph.types.Type import de.fraunhofer.aisec.cpg.graph.types.UnknownType import java.util.* -import java.util.stream.Collectors import org.apache.commons.lang3.builder.ToStringBuilder import org.neo4j.ogm.annotation.Relationship @@ -82,13 +81,11 @@ class InitializerListExpression : Expression(), HasType.TypeListener { if (initializers.contains(src)) { val types = initializers - .parallelStream() - .map { obj: Expression -> obj.type } - .filter { obj: Type? -> Objects.nonNull(obj) } - .map { t: Type -> - TypeManager.getInstance().registerType(t.reference(PointerOrigin.ARRAY)) + .map { + TypeManager.getInstance() + .registerType(it.type.reference(PointerOrigin.ARRAY)) } - .collect(Collectors.toSet()) + .toSet() val alternative = if (types.isNotEmpty()) types.iterator().next() else UnknownType.getUnknownType() newType = TypeManager.getInstance().getCommonType(types, this).orElse(alternative) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/LambdaExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/LambdaExpression.kt index e8ed41bfc0..7d3a24f4f0 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/LambdaExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/LambdaExpression.kt @@ -56,7 +56,7 @@ class LambdaExpression : Expression(), HasType.TypeListener { return } - if (!TypeManager.getInstance().isUnknown(type) && src.propagationType == oldType) { + if (!TypeManager.getInstance().isUnknown(type) && src!!.propagationType == oldType) { return } @@ -72,14 +72,7 @@ class LambdaExpression : Expression(), HasType.TypeListener { // the incoming "type" is associated to the function and it is only its return type (if it // is known). what we really want is to construct a function type, or rather a function // pointer type, since this is the closest to what we have - val functionType = - FunctionPointerType( - Type.Qualifier(false, false, false, false), - Type.Storage.AUTO, - parameterTypes, - returnType, - this.language - ) + val functionType = FunctionPointerType(parameterTypes, returnType, this.language) setType(functionType, root) if (previous != type) { 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 e625071cfa..14fbb1e859 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 @@ -124,9 +124,8 @@ open class VariableUsageResolver : SymbolResolverPass() { } // only consider resolving, if the language frontend did not specify a resolution - var refersTo = current.refersTo ?: scopeManager.resolveReference(current) - // if (current.refersTo == null) scopeManager?.resolveReference(current) - // else current.refersTo!! + var refersTo = current.refersTo ?: scopeManager.resolveReference(current, current.scope) + var recordDeclType: Type? = null if (currentClass != null) { recordDeclType = TypeParser.createFrom(currentClass.name, currentClass.language) diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/PerformanceRegressionTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/PerformanceRegressionTest.kt index f72699c2cf..77ad7f7764 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/PerformanceRegressionTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/PerformanceRegressionTest.kt @@ -35,8 +35,8 @@ import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge import de.fraunhofer.aisec.cpg.graph.newLiteral import de.fraunhofer.aisec.cpg.graph.statements.expressions.InitializerListExpression -import de.fraunhofer.aisec.cpg.graph.types.ObjectType -import de.fraunhofer.aisec.cpg.graph.types.Type +import de.fraunhofer.aisec.cpg.graph.types.IntegerType +import de.fraunhofer.aisec.cpg.graph.types.NumericType import de.fraunhofer.aisec.cpg.helpers.Benchmark import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker import java.time.Duration @@ -92,15 +92,7 @@ class PerformanceRegressionTest { list, newLiteral( i, - ObjectType( - "int", - Type.Storage.AUTO, - Type.Qualifier(), - listOf(), - ObjectType.Modifier.UNSIGNED, - true, - CPPLanguage() - ), + IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.UNSIGNED), null ) ) diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/templates/FunctionTemplateTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/templates/FunctionTemplateTest.kt index dd12237848..2a195f3255 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/templates/FunctionTemplateTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/templates/FunctionTemplateTest.kt @@ -109,26 +109,8 @@ internal class FunctionTemplateTest : BaseTest() { assertEquals(typeParamDeclaration, functionTemplateDeclaration.parameters[0]) val typeT = ParameterizedType("T", CPPLanguage()) - val intType = - ObjectType( - "int", - Type.Storage.AUTO, - Type.Qualifier(), - ArrayList(), - ObjectType.Modifier.SIGNED, - true, - CPPLanguage() - ) - val floatType = - ObjectType( - "float", - Type.Storage.AUTO, - Type.Qualifier(), - ArrayList(), - ObjectType.Modifier.SIGNED, - true, - CPPLanguage() - ) + val intType = IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.SIGNED) + val floatType = FloatingPointType("float", 32, CPPLanguage(), NumericType.Modifier.SIGNED) assertEquals(typeT, typeParamDeclaration.type) assertEquals(intType, typeParamDeclaration.default) @@ -236,16 +218,7 @@ internal class FunctionTemplateTest : BaseTest() { assertEquals(fixedMultiply, call.invokes[0]) // Check template parameters - val doubleType = - ObjectType( - "double", - Type.Storage.AUTO, - Type.Qualifier(), - ArrayList(), - ObjectType.Modifier.SIGNED, - true, - CPPLanguage() - ) + val doubleType = FloatingPointType("double", 64, CPPLanguage(), NumericType.Modifier.SIGNED) val literal5 = findByUniquePredicate(result.literals) { l: Literal<*> -> l.value == 5 } assertEquals(2, call.templateParameters.size) assertEquals(doubleType, (call.templateParameters[0] as TypeExpression).type) @@ -376,16 +349,7 @@ internal class FunctionTemplateTest : BaseTest() { assertEquals(fixedMultiply, call.invokes[0]) // Check template parameters - val doubleType = - ObjectType( - "double", - Type.Storage.AUTO, - Type.Qualifier(), - ArrayList(), - ObjectType.Modifier.SIGNED, - true, - CPPLanguage() - ) + val doubleType = FloatingPointType("double", 64, CPPLanguage(), NumericType.Modifier.SIGNED) val literal5 = findByUniquePredicate(result.literals) { l: Literal<*> -> l.value == 5 } assertEquals(2, call.templateParameters.size) assertEquals(doubleType, (call.templateParameters[0] as TypeExpression).type) diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXLanguageFrontendTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXLanguageFrontendTest.kt index 6b1a48c4ca..723722b473 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXLanguageFrontendTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXLanguageFrontendTest.kt @@ -103,7 +103,6 @@ internal class CXXLanguageFrontendTest : BaseTest() { assertNotNull(parameter) assertLocalName("e", parameter) assertEquals("std::exception&", parameter.type.typeName) - assertTrue(parameter.type.qualifier.isConst) // anonymous variable (this is not 100% handled correctly but will do for now) parameter = catchClauses[1].parameter @@ -111,7 +110,6 @@ internal class CXXLanguageFrontendTest : BaseTest() { // this is currently our 'unnamed' parameter assertLocalName("", parameter) assertEquals("std::exception&", parameter.type.typeName) - assertTrue(parameter.type.qualifier.isConst) // catch all parameter = catchClauses[2].parameter diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXSymbolConfigurationTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXSymbolConfigurationTest.kt index 5acd747819..15b3afff40 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXSymbolConfigurationTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cpp/CXXSymbolConfigurationTest.kt @@ -50,7 +50,7 @@ internal class CXXSymbolConfigurationTest : BaseTest() { CXXLanguageFrontend( CPPLanguage(), TranslationConfiguration.builder().defaultPasses().build(), - ScopeManager() + ScopeManager(), ) .parse(File("src/test/resources/symbols.cpp")) val main = tu.getDeclarationsByName("main", FunctionDeclaration::class.java) @@ -92,7 +92,7 @@ internal class CXXSymbolConfigurationTest : BaseTest() { ) .defaultPasses() .build(), - ScopeManager() + ScopeManager(), ) .parse(File("src/test/resources/symbols.cpp")) val main = tu.getDeclarationsByName("main", FunctionDeclaration::class.java) diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontendTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontendTest.kt index 62d0fe396f..bdec73ca94 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontendTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontendTest.kt @@ -592,7 +592,7 @@ internal class JavaLanguageFrontendTest : BaseTest() { class MyJavaLanguageFrontend( language: JavaLanguage, config: TranslationConfiguration, - scopeManager: ScopeManager + scopeManager: ScopeManager, ) : JavaLanguageFrontend(language, config, scopeManager) { init { this.declarationHandler = @@ -623,7 +623,7 @@ internal class JavaLanguageFrontendTest : BaseTest() { override val frontend = MyJavaLanguageFrontend::class override fun newFrontend( config: TranslationConfiguration, - scopeManager: ScopeManager + scopeManager: ScopeManager, ): MyJavaLanguageFrontend { return MyJavaLanguageFrontend(this, config, scopeManager) } diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypeTests.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypeTests.kt index 70b69c39c1..bc65f66f35 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypeTests.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypeTests.kt @@ -43,39 +43,14 @@ import kotlin.test.* internal class TypeTests : BaseTest() { @Test fun reference() { - val objectType: Type = - ObjectType( - "int", - Type.Storage.AUTO, - Type.Qualifier(), - ArrayList(), - ObjectType.Modifier.SIGNED, - true, - CPPLanguage() - ) + val objectType: Type = IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.SIGNED) val pointerType: Type = PointerType(objectType, PointerType.PointerOrigin.POINTER) val unknownType: Type = UnknownType.getUnknownType(CPPLanguage()) val incompleteType: Type = IncompleteType() val parameterList = - listOf( - ObjectType( - "int", - Type.Storage.AUTO, - Type.Qualifier(), - emptyList(), - ObjectType.Modifier.SIGNED, - true, - CPPLanguage() - ) - ) + listOf(IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.SIGNED)) val functionPointerType: Type = - FunctionPointerType( - Type.Qualifier(), - Type.Storage.AUTO, - parameterList, - IncompleteType(), - CPPLanguage() - ) + FunctionPointerType(parameterList, IncompleteType(), CPPLanguage()) // Test 1: ObjectType becomes PointerType containing the original ObjectType as ElementType assertEquals( @@ -107,39 +82,14 @@ internal class TypeTests : BaseTest() { @Test fun dereference() { - val objectType: Type = - ObjectType( - "int", - Type.Storage.AUTO, - Type.Qualifier(), - ArrayList(), - ObjectType.Modifier.SIGNED, - true, - CPPLanguage() - ) + val objectType: Type = IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.SIGNED) val pointerType: Type = PointerType(objectType, PointerType.PointerOrigin.POINTER) val unknownType: Type = UnknownType.getUnknownType(CPPLanguage()) val incompleteType: Type = IncompleteType() val parameterList = - listOf( - ObjectType( - "int", - Type.Storage.AUTO, - Type.Qualifier(), - emptyList(), - ObjectType.Modifier.SIGNED, - true, - CPPLanguage() - ) - ) + listOf(IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.SIGNED)) val functionPointerType: Type = - FunctionPointerType( - Type.Qualifier(), - Type.Storage.AUTO, - parameterList, - IncompleteType(), - CPPLanguage() - ) + FunctionPointerType(parameterList, IncompleteType(), CPPLanguage()) // Test 1: Dereferencing an ObjectType results in an UnknownType, since we cannot track the // type @@ -167,91 +117,37 @@ internal class TypeTests : BaseTest() { // Test 1: Ignore Access Modifier Keyword (public, private, protected) var typeString = "private int a" result = TypeParser.createFrom(typeString, JavaLanguage()) - expected = - ObjectType( - "int", - Type.Storage.AUTO, - Type.Qualifier(), - ArrayList(), - ObjectType.Modifier.SIGNED, - true, - JavaLanguage() - ) + expected = IntegerType("int", 32, JavaLanguage(), NumericType.Modifier.SIGNED) assertEquals(expected, result) // Test 2: constant type using final typeString = "final int a" result = TypeParser.createFrom(typeString, JavaLanguage()) - expected = - ObjectType( - "int", - Type.Storage.AUTO, - Type.Qualifier(true, false, false, false), - ArrayList(), - ObjectType.Modifier.SIGNED, - true, - JavaLanguage() - ) + expected = IntegerType("int", 32, JavaLanguage(), NumericType.Modifier.SIGNED) assertEquals(expected, result) // Test 3: static type typeString = "static int a" result = TypeParser.createFrom(typeString, JavaLanguage()) - expected = - ObjectType( - "int", - Type.Storage.STATIC, - Type.Qualifier(), - ArrayList(), - ObjectType.Modifier.SIGNED, - true, - JavaLanguage() - ) + expected = IntegerType("int", 32, JavaLanguage(), NumericType.Modifier.SIGNED) assertEquals(expected, result) // Test 4: volatile type typeString = "public volatile int a" result = TypeParser.createFrom(typeString, JavaLanguage()) - expected = - ObjectType( - "int", - Type.Storage.AUTO, - Type.Qualifier(false, true, false, false), - ArrayList(), - ObjectType.Modifier.SIGNED, - true, - JavaLanguage() - ) + expected = IntegerType("int", 32, JavaLanguage(), NumericType.Modifier.SIGNED) assertEquals(expected, result) // Test 5: combining a storage type and a qualifier typeString = "private static final String a" result = TypeParser.createFrom(typeString, JavaLanguage()) - expected = - ObjectType( - "String", - Type.Storage.STATIC, - Type.Qualifier(true, false, false, false), - ArrayList(), - ObjectType.Modifier.NOT_APPLICABLE, - false, - JavaLanguage() - ) + expected = StringType("java.lang.String", JavaLanguage()) assertEquals(expected, result) // Test 6: using two different qualifiers typeString = "public final volatile int a" result = TypeParser.createFrom(typeString, JavaLanguage()) - expected = - ObjectType( - "int", - Type.Storage.AUTO, - Type.Qualifier(true, true, false, false), - ArrayList(), - ObjectType.Modifier.SIGNED, - true, - JavaLanguage() - ) + expected = IntegerType("int", 32, JavaLanguage(), NumericType.Modifier.SIGNED) assertEquals(expected, result) // Test 7: Reference level using arrays @@ -259,15 +155,7 @@ internal class TypeTests : BaseTest() { result = TypeParser.createFrom(typeString, JavaLanguage()) expected = PointerType( - ObjectType( - "int", - Type.Storage.AUTO, - Type.Qualifier(), - ArrayList(), - ObjectType.Modifier.SIGNED, - true, - JavaLanguage() - ), + IntegerType("int", 32, JavaLanguage(), NumericType.Modifier.SIGNED), PointerType.PointerOrigin.ARRAY ) assertEquals(expected, result) @@ -276,91 +164,27 @@ internal class TypeTests : BaseTest() { typeString = "List list" result = TypeParser.createFrom(typeString, JavaLanguage()) var generics: MutableList = ArrayList() - generics.add( - ObjectType( - "String", - Type.Storage.AUTO, - Type.Qualifier(), - ArrayList(), - ObjectType.Modifier.NOT_APPLICABLE, - false, - JavaLanguage() - ) - ) - expected = - ObjectType( - "List", - Type.Storage.AUTO, - Type.Qualifier(), - generics, - ObjectType.Modifier.NOT_APPLICABLE, - false, - JavaLanguage() - ) + generics.add(StringType("java.lang.String", JavaLanguage())) + expected = ObjectType("List", generics, false, JavaLanguage()) assertEquals(expected, result) // Test 9: more generics typeString = "List>, List> data" result = TypeParser.createFrom(typeString, JavaLanguage()) - val genericStringType = - ObjectType( - "String", - Type.Storage.AUTO, - Type.Qualifier(), - ArrayList(), - ObjectType.Modifier.NOT_APPLICABLE, - false, - JavaLanguage() - ) + val genericStringType = StringType("java.lang.String", JavaLanguage()) val generics3: MutableList = ArrayList() generics3.add(genericStringType) - val genericElement3 = - ObjectType( - "List", - Type.Storage.AUTO, - Type.Qualifier(), - generics3, - ObjectType.Modifier.NOT_APPLICABLE, - false, - JavaLanguage() - ) + 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", - Type.Storage.AUTO, - Type.Qualifier(), - generics2a, - ObjectType.Modifier.NOT_APPLICABLE, - false, - JavaLanguage() - ) - val genericElement2 = - ObjectType( - "List", - Type.Storage.AUTO, - Type.Qualifier(), - generics2b, - ObjectType.Modifier.NOT_APPLICABLE, - false, - JavaLanguage() - ) + 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", - Type.Storage.AUTO, - Type.Qualifier(), - generics, - ObjectType.Modifier.NOT_APPLICABLE, - false, - JavaLanguage() - ) + expected = ObjectType("List", generics, false, JavaLanguage()) assertEquals(expected, result) } @@ -372,25 +196,8 @@ internal class TypeTests : BaseTest() { var typeString = "void (*single_param)(int)" result = TypeParser.createFrom(typeString, CPPLanguage()) val parameterList = - listOf( - ObjectType( - "int", - Type.Storage.AUTO, - Type.Qualifier(), - emptyList(), - ObjectType.Modifier.SIGNED, - true, - CPPLanguage() - ) - ) - var expected: Type = - FunctionPointerType( - Type.Qualifier(), - Type.Storage.AUTO, - parameterList, - IncompleteType(), - CPPLanguage() - ) + listOf(IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.SIGNED)) + var expected: Type = FunctionPointerType(parameterList, IncompleteType(), CPPLanguage()) assertEquals(expected, result) // Test 1.1: interleaved brackets in function pointer @@ -404,20 +211,11 @@ internal class TypeTests : BaseTest() { expected = PointerType( PointerType( - ObjectType( - "char", - Type.Storage.AUTO, - Type.Qualifier(), - emptyList(), - ObjectType.Modifier.SIGNED, - true, - CPPLanguage() - ), + IntegerType("char", 8, CPPLanguage(), NumericType.Modifier.NOT_APPLICABLE), PointerType.PointerOrigin.ARRAY ), PointerType.PointerOrigin.POINTER ) - expected.setQualifier(Type.Qualifier(true, false, false, false)) assertEquals(expected, result) // Test 3: Mutable pointer to a mutable char @@ -425,15 +223,7 @@ internal class TypeTests : BaseTest() { result = TypeParser.createFrom(typeString, CPPLanguage()) expected = PointerType( - ObjectType( - "char", - Type.Storage.AUTO, - Type.Qualifier(), - emptyList(), - ObjectType.Modifier.SIGNED, - true, - CPPLanguage() - ), + IntegerType("char", 8, CPPLanguage(), NumericType.Modifier.NOT_APPLICABLE), PointerType.PointerOrigin.POINTER ) assertEquals(expected, result) @@ -453,15 +243,7 @@ internal class TypeTests : BaseTest() { result = TypeParser.createFrom(typeString, CPPLanguage()) expected = PointerType( - ObjectType( - "char", - Type.Storage.AUTO, - Type.Qualifier(true, false, false, false), - emptyList(), - ObjectType.Modifier.SIGNED, - true, - CPPLanguage() - ), + IntegerType("char", 8, CPPLanguage(), NumericType.Modifier.NOT_APPLICABLE), PointerType.PointerOrigin.POINTER ) assertEquals(expected, result) @@ -471,18 +253,9 @@ internal class TypeTests : BaseTest() { result = TypeParser.createFrom(typeString, CPPLanguage()) expected = PointerType( - ObjectType( - "char", - Type.Storage.AUTO, - Type.Qualifier(false, false, false, false), - emptyList(), - ObjectType.Modifier.SIGNED, - true, - CPPLanguage() - ), + IntegerType("char", 8, CPPLanguage(), NumericType.Modifier.NOT_APPLICABLE), PointerType.PointerOrigin.POINTER ) - expected.setQualifier(Type.Qualifier(true, false, false, false)) assertEquals(expected, result) // Test 6: Constant pointer to a constant char @@ -490,18 +263,9 @@ internal class TypeTests : BaseTest() { result = TypeParser.createFrom(typeString, CPPLanguage()) expected = PointerType( - ObjectType( - "char", - Type.Storage.AUTO, - Type.Qualifier(true, false, false, false), - emptyList(), - ObjectType.Modifier.SIGNED, - true, - CPPLanguage() - ), + IntegerType("char", 8, CPPLanguage(), NumericType.Modifier.NOT_APPLICABLE), PointerType.PointerOrigin.POINTER ) - expected.setQualifier(Type.Qualifier(true, false, false, false)) assertEquals(expected, result) // Test 7: Array of const pointer to static const char @@ -510,20 +274,11 @@ internal class TypeTests : BaseTest() { expected = PointerType( PointerType( - ObjectType( - "char", - Type.Storage.STATIC, - Type.Qualifier(true, false, false, false), - emptyList(), - ObjectType.Modifier.SIGNED, - true, - CPPLanguage() - ), + IntegerType("char", 8, CPPLanguage(), NumericType.Modifier.NOT_APPLICABLE), PointerType.PointerOrigin.POINTER ), PointerType.PointerOrigin.ARRAY ) - expected.elementType.qualifier = Type.Qualifier(true, false, false, false) assertEquals(expected, result) // Test 7.1: Array of array of pointer to static const char @@ -533,15 +288,7 @@ internal class TypeTests : BaseTest() { PointerType( PointerType( PointerType( - ObjectType( - "char", - Type.Storage.STATIC, - Type.Qualifier(true, false, false, false), - emptyList(), - ObjectType.Modifier.SIGNED, - true, - CPPLanguage() - ), + IntegerType("char", 8, CPPLanguage(), NumericType.Modifier.NOT_APPLICABLE), PointerType.PointerOrigin.POINTER ), PointerType.PointerOrigin.ARRAY @@ -554,96 +301,33 @@ internal class TypeTests : BaseTest() { typeString = "Array array" result = TypeParser.createFrom(typeString, CPPLanguage()) var generics: MutableList = ArrayList() - generics.add( - ObjectType( - "int", - Type.Storage.AUTO, - Type.Qualifier(), - emptyList(), - ObjectType.Modifier.SIGNED, - true, - CPPLanguage() - ) - ) - expected = - ObjectType( - "Array", - Type.Storage.AUTO, - Type.Qualifier(), - generics, - ObjectType.Modifier.NOT_APPLICABLE, - false, - CPPLanguage() - ) + generics.add(IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.SIGNED)) + expected = ObjectType("Array", generics, false, CPPLanguage()) assertEquals(expected, result) // Test 9: Compound Primitive Types typeString = "long long int" result = TypeParser.createFrom(typeString, CPPLanguage()) - expected = - ObjectType( - "long long int", - Type.Storage.AUTO, - Type.Qualifier(), - ArrayList(), - ObjectType.Modifier.SIGNED, - true, - CPPLanguage() - ) + expected = IntegerType("long long int", 64, CPPLanguage(), NumericType.Modifier.SIGNED) assertEquals(expected, result) // Test 10: Unsigned/Signed Types typeString = "unsigned int" result = TypeParser.createFrom(typeString, CPPLanguage()) - expected = - ObjectType( - "int", - Type.Storage.AUTO, - Type.Qualifier(), - ArrayList(), - ObjectType.Modifier.UNSIGNED, - true, - CPPLanguage() - ) + expected = IntegerType("unsigned int", 32, CPPLanguage(), NumericType.Modifier.UNSIGNED) assertEquals(expected, result) typeString = "signed int" result = TypeParser.createFrom(typeString, CPPLanguage()) - expected = - ObjectType( - "int", - Type.Storage.AUTO, - Type.Qualifier(), - ArrayList(), - ObjectType.Modifier.SIGNED, - true, - CPPLanguage() - ) + expected = IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.SIGNED) assertEquals(expected, result) typeString = "A a" result = TypeParser.createFrom(typeString, CPPLanguage()) - expected = - ObjectType( - "A", - Type.Storage.AUTO, - Type.Qualifier(), - ArrayList(), - ObjectType.Modifier.NOT_APPLICABLE, - false, - CPPLanguage() - ) + expected = ObjectType("A", ArrayList(), false, CPPLanguage()) assertEquals(expected, result) // Test 11: Unsigned + const + compound primitive Types expected = - ObjectType( - "long long int", - Type.Storage.AUTO, - Type.Qualifier(true, false, false, false), - ArrayList(), - ObjectType.Modifier.UNSIGNED, - true, - CPPLanguage() - ) + IntegerType("unsigned long long int", 64, CPPLanguage(), NumericType.Modifier.UNSIGNED) typeString = "const unsigned long long int a = 1" result = TypeParser.createFrom(typeString, CPPLanguage()) assertEquals(expected, result) @@ -667,20 +351,7 @@ internal class TypeTests : BaseTest() { // Test 12: C++ Reference Types typeString = "const int& ref = a" result = TypeParser.createFrom(typeString, CPPLanguage()) - expected = - ReferenceType( - Type.Storage.AUTO, - Type.Qualifier(true, false, false, false), - ObjectType( - "int", - Type.Storage.AUTO, - Type.Qualifier(), - ArrayList(), - ObjectType.Modifier.SIGNED, - true, - CPPLanguage() - ) - ) + expected = ReferenceType(IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.SIGNED)) assertEquals(expected, result) typeString = "int const &ref2 = a" @@ -690,52 +361,16 @@ internal class TypeTests : BaseTest() { // Test 13: Elaborated Type in Generics result = TypeParser.createFrom("Array", CPPLanguage()) generics = ArrayList() - var generic = - ObjectType( - "Node", - Type.Storage.AUTO, - Type.Qualifier(), - ArrayList(), - ObjectType.Modifier.NOT_APPLICABLE, - false, - CPPLanguage() - ) + var generic = ObjectType("Node", ArrayList(), false, CPPLanguage()) generics.add(generic) - expected = - ObjectType( - "Array", - Type.Storage.AUTO, - Type.Qualifier(), - generics, - ObjectType.Modifier.NOT_APPLICABLE, - false, - CPPLanguage() - ) + expected = ObjectType("Array", generics, false, CPPLanguage()) assertEquals(expected, result) result = TypeParser.createFrom("Array", CPPLanguage()) generics = ArrayList() - generic = - ObjectType( - "myclass", - Type.Storage.AUTO, - Type.Qualifier(), - ArrayList(), - ObjectType.Modifier.NOT_APPLICABLE, - false, - CPPLanguage() - ) + generic = ObjectType("myclass", ArrayList(), false, CPPLanguage()) generics.add(generic) - expected = - ObjectType( - "Array", - Type.Storage.AUTO, - Type.Qualifier(), - generics, - ObjectType.Modifier.NOT_APPLICABLE, - false, - CPPLanguage() - ) + expected = ObjectType("Array", generics, false, CPPLanguage()) assertEquals(expected, result) } @@ -797,16 +432,13 @@ internal class TypeTests : BaseTest() { // Test propagation of specifiers in primitive fields (final int y) val y = findByUniqueName(fieldDeclarations, "y") - assertTrue(y.type.qualifier.isConst) // Test propagation of specifiers in non-primitive fields (final A a) var variableDeclarations = result.variables val aA = findByUniqueName(variableDeclarations, "a") - assertTrue(aA.type.qualifier.isConst) // Test propagation of specifiers in variables (final String s) val sString = findByUniqueName(variableDeclarations, "s") - assertTrue(sString.type.qualifier.isConst) // Test PointerType chain with array val array = findByUniqueName(variableDeclarations, "array") @@ -851,65 +483,20 @@ internal class TypeTests : BaseTest() { val topLevel = Path.of("src", "test", "resources", "types") val tu = analyzeAndGetFirstTU(listOf(topLevel.resolve("fptr_type.cpp").toFile()), topLevel, true) - val noParamType = - FunctionPointerType( - Type.Qualifier(), - Type.Storage.AUTO, - emptyList(), - IncompleteType(), - CPPLanguage() - ) + val noParamType = FunctionPointerType(emptyList(), IncompleteType(), CPPLanguage()) val oneParamType = FunctionPointerType( - Type.Qualifier(), - Type.Storage.AUTO, - listOf( - ObjectType( - "int", - Type.Storage.AUTO, - Type.Qualifier(), - ArrayList(), - ObjectType.Modifier.SIGNED, - true, - CPPLanguage() - ) - ), + listOf(IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.SIGNED)), IncompleteType(), CPPLanguage() ) val twoParamType = FunctionPointerType( - Type.Qualifier(), - Type.Storage.AUTO, listOf( - ObjectType( - "int", - Type.Storage.AUTO, - Type.Qualifier(), - ArrayList(), - ObjectType.Modifier.SIGNED, - true, - CPPLanguage() - ), - ObjectType( - "long", - Type.Storage.AUTO, - Type.Qualifier(), - ArrayList(), - ObjectType.Modifier.UNSIGNED, - true, - CPPLanguage() - ) - ), - ObjectType( - "int", - Type.Storage.AUTO, - Type.Qualifier(), - ArrayList(), - ObjectType.Modifier.SIGNED, - true, - CPPLanguage() + IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.SIGNED), + IntegerType("unsigned long", 64, CPPLanguage(), NumericType.Modifier.UNSIGNED) ), + IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.SIGNED), CPPLanguage() ) val variables = tu.variables @@ -943,7 +530,7 @@ internal class TypeTests : BaseTest() { fun testCommonTypeTestJava() { disableTypeManagerCleanup() val topLevel = Path.of("src", "test", "resources", "compiling", "hierarchy") - var result = analyze("java", topLevel, true) + val result = analyze("java", topLevel, true) val root = TypeParser.createFrom("multistep.Root", JavaLanguage()) val level0 = TypeParser.createFrom("multistep.Level0", JavaLanguage()) val level1 = TypeParser.createFrom("multistep.Level1", JavaLanguage()) @@ -958,7 +545,7 @@ internal class TypeTests : BaseTest() { fun testCommonTypeTestCpp() { disableTypeManagerCleanup() val topLevel = Path.of("src", "test", "resources", "compiling", "hierarchy", "multistep") - var result = analyze("simple_inheritance.cpp", topLevel, true) + val result = analyze("simple_inheritance.cpp", topLevel, true) val root = TypeParser.createFrom("Root", CPPLanguage()) val level0 = TypeParser.createFrom("Level0", CPPLanguage()) val level1 = TypeParser.createFrom("Level1", CPPLanguage()) @@ -974,7 +561,7 @@ internal class TypeTests : BaseTest() { fun testCommonTypeTestCppMultiInheritance() { disableTypeManagerCleanup() val topLevel = Path.of("src", "test", "resources", "compiling", "hierarchy", "multistep") - var result = analyze("multi_inheritance.cpp", topLevel, true) + val result = analyze("multi_inheritance.cpp", topLevel, true) val root = TypeParser.createFrom("Root", CPPLanguage()) val level0 = TypeParser.createFrom("Level0", CPPLanguage()) @@ -985,7 +572,7 @@ internal class TypeTests : BaseTest() { val level2 = TypeParser.createFrom("Level2", CPPLanguage()) val level2b = TypeParser.createFrom("Level2B", CPPLanguage()) - var provider = result.scopeManager + val provider = result.scopeManager /* Type hierarchy: Root------------ @@ -1053,7 +640,7 @@ internal class TypeTests : BaseTest() { | Level2 */ - var provider = result.scopeManager + val provider = result.scopeManager // A single type is its own least common ancestor for (t in listOf(root, level0, level1, level1b, level2)) { diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypedefTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypedefTest.kt index e2d5ac3bd7..4b1acdd0de 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypedefTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypedefTest.kt @@ -67,9 +67,9 @@ internal class TypedefTest : BaseTest() { val fpType = uintfp1.type as? FunctionPointerType assertNotNull(fpType) - val returnType = fpType.returnType as? ObjectType + val returnType = fpType.returnType as? NumericType assertNotNull(returnType) - assertEquals(ObjectType.Modifier.UNSIGNED, returnType.modifier) + assertEquals(NumericType.Modifier.UNSIGNED, returnType.modifier) assertEquals(uintfp1.type, uintfp2.type) val typedefs = result.scopeManager.currentTypedefs 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 2aba01374d..7bd5110f2d 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 @@ -91,7 +91,11 @@ internal class ScopeManagerTest : BaseTest() { fun testMerge() { val s1 = ScopeManager() val frontend1 = - CXXLanguageFrontend(CPPLanguage(), TranslationConfiguration.builder().build(), s1) + CXXLanguageFrontend( + CPPLanguage(), + TranslationConfiguration.builder().build(), + s1, + ) s1.resetToGlobal(frontend1.newTranslationUnitDeclaration("f1.cpp", null)) // build a namespace declaration in f1.cpp with the namespace A @@ -103,7 +107,11 @@ internal class ScopeManagerTest : BaseTest() { val s2 = ScopeManager() val frontend2 = - CXXLanguageFrontend(CPPLanguage(), TranslationConfiguration.builder().build(), s2) + CXXLanguageFrontend( + CPPLanguage(), + TranslationConfiguration.builder().build(), + s2, + ) s2.resetToGlobal(frontend2.newTranslationUnitDeclaration("f1.cpp", null)) // and do the same in the other file @@ -116,7 +124,11 @@ internal class ScopeManagerTest : BaseTest() { // merge the two scopes. this replicates the behaviour of parseParallel val final = ScopeManager() val frontend = - CXXLanguageFrontend(CPPLanguage(), TranslationConfiguration.builder().build(), final) + CXXLanguageFrontend( + CPPLanguage(), + TranslationConfiguration.builder().build(), + final, + ) final.mergeFrom(listOf(s1, s2)) // in the final scope manager, there should only be one NameScope "A" @@ -154,7 +166,11 @@ internal class ScopeManagerTest : BaseTest() { fun testScopeFQN() { val s = ScopeManager() val frontend = - CXXLanguageFrontend(CPPLanguage(), TranslationConfiguration.builder().build(), s) + CXXLanguageFrontend( + CPPLanguage(), + TranslationConfiguration.builder().build(), + s, + ) s.resetToGlobal(frontend.newTranslationUnitDeclaration("file.cpp", null)) assertNull(s.currentNamespace) 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 2a4f80eb07..f9fa872b14 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 @@ -46,7 +46,7 @@ class TestLanguage : Language() { override fun newFrontend( config: TranslationConfiguration, - scopeManager: ScopeManager + scopeManager: ScopeManager, ): TestLanguageFrontend { return TestLanguageFrontend() } @@ -56,7 +56,7 @@ class TestLanguageFrontend(scopeManager: ScopeManager = ScopeManager()) : LanguageFrontend( TestLanguage(), TranslationConfiguration.builder().build(), - scopeManager, + ScopeManager(), ) { override fun parse(file: File): TranslationUnitDeclaration { TODO("Not yet implemented") diff --git a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguage.kt b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguage.kt index aac6cb90f1..17b2389d14 100644 --- a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguage.kt +++ b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguage.kt @@ -27,21 +27,45 @@ package de.fraunhofer.aisec.cpg.frontends.golang import de.fraunhofer.aisec.cpg.ScopeManager import de.fraunhofer.aisec.cpg.TranslationConfiguration +import de.fraunhofer.aisec.cpg.frontends.HasGenerics import de.fraunhofer.aisec.cpg.frontends.HasShortCircuitOperators import de.fraunhofer.aisec.cpg.frontends.Language -import kotlin.reflect.KClass +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.StringType /** The Go language. */ -open class GoLanguage : Language(), HasShortCircuitOperators { +open class GoLanguage : Language(), HasShortCircuitOperators, HasGenerics { override val fileExtensions = listOf("go") override val namespaceDelimiter = "." - override val frontend: KClass = GoLanguageFrontend::class + override val frontend = GoLanguageFrontend::class override val conjunctiveOperators = listOf("&&") override val disjunctiveOperators = listOf("||") + override val startCharacter = '[' + override val endCharacter = ']' + + @Transient + override val simpleTypes = + mapOf( + "int8" to IntegerType("int8", 8, this, NumericType.Modifier.SIGNED), + "int16" to IntegerType("int16", 16, this, NumericType.Modifier.SIGNED), + "int32" to IntegerType("int32", 32, this, NumericType.Modifier.SIGNED), + "int64" to IntegerType("int64", 64, this, NumericType.Modifier.SIGNED), + "uint8" to IntegerType("uint8", 8, this, NumericType.Modifier.UNSIGNED), + "uint16" to IntegerType("uint16", 16, this, NumericType.Modifier.UNSIGNED), + "uint32" to IntegerType("uint32", 32, this, NumericType.Modifier.UNSIGNED), + "uint64" to IntegerType("uint64", 64, this, NumericType.Modifier.UNSIGNED), + "float32" to FloatingPointType("float32", 32, this, NumericType.Modifier.SIGNED), + "float64" to FloatingPointType("float64", 64, this, NumericType.Modifier.SIGNED), + "complex32" to NumericType("complex32", 32, this, NumericType.Modifier.NOT_APPLICABLE), + "complex64" to NumericType("complex54", 64, this, NumericType.Modifier.NOT_APPLICABLE), + "string" to StringType("string", this) + ) override fun newFrontend( config: TranslationConfiguration, - scopeManager: ScopeManager + scopeManager: ScopeManager, ): GoLanguageFrontend { return GoLanguageFrontend(this, config, scopeManager) } diff --git a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontend.kt b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontend.kt index d26fa301c0..1c10b07a19 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 @@ -40,10 +40,9 @@ import java.io.FileOutputStream class GoLanguageFrontend( language: Language, config: TranslationConfiguration, - scopeManager: ScopeManager + scopeManager: ScopeManager, ) : LanguageFrontend(language, config, scopeManager) { companion object { - @JvmField var GOLANG_EXTENSIONS: List = listOf(".go") init { try { 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 2360f89647..2ffdb45db9 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 @@ -167,7 +167,10 @@ class GoLanguageFrontendTest : BaseTest() { make = decl.initializer assertNotNull(make) assertTrue(make is ConstructExpression) - assertEquals(TypeParser.createFrom("map", GoLanguage()), make.type) + // TODO: Maps can have dedicated types and parsing them as a generic here is only a + // temporary solution. + // This should be fixed in the future. + assertEquals(TypeParser.createFrom("map[string,string]", GoLanguage()), make.type) // make channel @@ -180,7 +183,7 @@ class GoLanguageFrontendTest : BaseTest() { make = decl.initializer assertNotNull(make) assertTrue(make is ConstructExpression) - assertEquals(TypeParser.createFrom("chan", GoLanguage()), make.type) + assertEquals(TypeParser.createFrom("chan[int]", GoLanguage()), make.type) } @Test diff --git a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguage.kt b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguage.kt index 220118e9aa..19be811940 100644 --- a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguage.kt +++ b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguage.kt @@ -28,6 +28,9 @@ package de.fraunhofer.aisec.cpg.frontends.llvm import de.fraunhofer.aisec.cpg.ScopeManager import de.fraunhofer.aisec.cpg.TranslationConfiguration import de.fraunhofer.aisec.cpg.frontends.Language +import de.fraunhofer.aisec.cpg.graph.types.FloatingPointType +import de.fraunhofer.aisec.cpg.graph.types.IntegerType +import de.fraunhofer.aisec.cpg.graph.types.NumericType import kotlin.reflect.KClass /** The LLVM IR language. */ @@ -35,32 +38,29 @@ class LLVMIRLanguage : Language() { override val fileExtensions = listOf("ll") override val namespaceDelimiter = "::" override val frontend: KClass = LLVMIRLanguageFrontend::class - override val primitiveTypes: Set - get() = - setOf( - "byte", - "short", - "int", - "long", - "float", - "double", - "boolean", - "char", - "i1", - "i8", - "i32", - "i64", - "i128", - "half", - "bfloat", - "fp128", - "x86_fp80", - "ppc_fp128" - ) + + // TODO: In theory, the integers can have any bitwidth from 1 to 1^32 bits. It's not known if + // they are interpreted as signed or unsigned. + @Transient + override val simpleTypes = + mapOf( + "i1" to IntegerType("i1", 1, this, NumericType.Modifier.NOT_APPLICABLE), + "i8" to IntegerType("i8", 8, this, NumericType.Modifier.NOT_APPLICABLE), + "i32" to IntegerType("i32", 32, this, NumericType.Modifier.NOT_APPLICABLE), + "i64" to IntegerType("i64", 64, this, NumericType.Modifier.NOT_APPLICABLE), + "i128" to IntegerType("i128", 128, this, NumericType.Modifier.NOT_APPLICABLE), + "half" to FloatingPointType("half", 16, this, NumericType.Modifier.SIGNED), + "bfloat" to FloatingPointType("bfloat", 16, this, NumericType.Modifier.SIGNED), + "float" to FloatingPointType("float", 32, this, NumericType.Modifier.SIGNED), + "double" to FloatingPointType("double", 64, this, NumericType.Modifier.SIGNED), + "fp128" to FloatingPointType("fp128", 128, this, NumericType.Modifier.SIGNED), + "x86_fp80" to FloatingPointType("x86_fp80", 80, this, NumericType.Modifier.SIGNED), + "ppc_fp128" to FloatingPointType("ppc_fp128", 128, this, NumericType.Modifier.SIGNED), + ) override fun newFrontend( config: TranslationConfiguration, - scopeManager: ScopeManager + scopeManager: ScopeManager, ): LLVMIRLanguageFrontend { return LLVMIRLanguageFrontend(this, config, scopeManager) } diff --git a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontend.kt b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontend.kt index 7c1fd1bf65..1f5ba20567 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 @@ -53,7 +53,7 @@ import org.bytedeco.llvm.global.LLVM.* class LLVMIRLanguageFrontend( language: Language, config: TranslationConfiguration, - scopeManager: ScopeManager + scopeManager: ScopeManager, ) : LanguageFrontend(language, config, scopeManager) { val statementHandler = StatementHandler(this) 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 1f1f25381b..ae05556baf 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 @@ -52,7 +52,7 @@ class LLVMIRLanguageFrontendTest { LLVMIRLanguageFrontend( LLVMIRLanguage(), TranslationConfiguration.builder().build(), - ScopeManager() + ScopeManager(), ) frontend.parse(topLevel.resolve("main.ll").toFile()) } diff --git a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguage.kt b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguage.kt index 3664b27cef..a4f4d470a0 100644 --- a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguage.kt +++ b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguage.kt @@ -29,6 +29,7 @@ import de.fraunhofer.aisec.cpg.ScopeManager import de.fraunhofer.aisec.cpg.TranslationConfiguration import de.fraunhofer.aisec.cpg.frontends.HasShortCircuitOperators import de.fraunhofer.aisec.cpg.frontends.Language +import de.fraunhofer.aisec.cpg.graph.types.* import kotlin.reflect.KClass /** The Python language. */ @@ -39,9 +40,37 @@ class PythonLanguage : Language(), HasShortCircuitOperat override val conjunctiveOperators = listOf("and") override val disjunctiveOperators = listOf("or") + @Transient + override val simpleTypes = + mapOf( + "bool" to IntegerType("bool", 1, this, NumericType.Modifier.NOT_APPLICABLE), + "int" to + IntegerType( + "int", + Integer.MAX_VALUE, + this, + NumericType.Modifier.NOT_APPLICABLE + ), // Unlimited precision + "float" to + FloatingPointType( + "float", + 32, + this, + NumericType.Modifier.NOT_APPLICABLE + ), // This depends on the implementation + "complex" to + NumericType( + "complex", + null, + this, + NumericType.Modifier.NOT_APPLICABLE + ), // It's two floats + "str" to StringType("str", this, listOf()) + ) + override fun newFrontend( config: TranslationConfiguration, - scopeManager: ScopeManager + scopeManager: ScopeManager, ): PythonLanguageFrontend { return PythonLanguageFrontend(this, config, scopeManager) } 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 d5aed59707..9392392fa3 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 @@ -40,7 +40,7 @@ import kotlin.io.path.absolutePathString class PythonLanguageFrontend( language: Language, config: TranslationConfiguration, - scopeManager: ScopeManager + scopeManager: ScopeManager, ) : LanguageFrontend(language, config, scopeManager) { private val jep = JepSingleton // configure Jep 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 90e193c2ad..d465cf684e 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 @@ -35,6 +35,7 @@ import de.fraunhofer.aisec.cpg.graph.declarations.* import de.fraunhofer.aisec.cpg.graph.edge.Properties import de.fraunhofer.aisec.cpg.graph.statements.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.* +import de.fraunhofer.aisec.cpg.graph.types.NumericType import de.fraunhofer.aisec.cpg.graph.types.ObjectType import de.fraunhofer.aisec.cpg.graph.types.TypeParser import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker @@ -90,7 +91,10 @@ class PythonFrontendTest : BaseTest() { val c = p.variables["c"] assertNotNull(c) assertLocalName("c", c) - assertEquals(TypeParser.createFrom("complex", PythonLanguage()), c.type) + assertEquals( + NumericType("complex", null, PythonLanguage(), NumericType.Modifier.NOT_APPLICABLE), + c.type + ) assertEquals("(3+5j)", (c.initializer as? Literal<*>)?.value) val t = p.variables["t"] diff --git a/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/JavaScriptLanguage.kt b/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/JavaScriptLanguage.kt index 91f48cdcb6..8e85f6207e 100644 --- a/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/JavaScriptLanguage.kt +++ b/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/JavaScriptLanguage.kt @@ -42,7 +42,7 @@ open class JavaScriptLanguage : Language(), HasShort override fun newFrontend( config: TranslationConfiguration, - scopeManager: ScopeManager + scopeManager: ScopeManager, ): TypeScriptLanguageFrontend { return TypeScriptLanguageFrontend(this, config, scopeManager) } diff --git a/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypeScriptLanguageFrontend.kt b/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypeScriptLanguageFrontend.kt index a0d52395bb..25458bb421 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 @@ -59,7 +59,7 @@ import java.nio.file.StandardCopyOption class TypeScriptLanguageFrontend( language: Language, config: TranslationConfiguration, - scopeManager: ScopeManager + scopeManager: ScopeManager, ) : LanguageFrontend(language, config, scopeManager) { val declarationHandler = DeclarationHandler(this) diff --git a/cpg-neo4j/src/main/java/de/fraunhofer/aisec/cpg_vis_neo4j/Application.kt b/cpg-neo4j/src/main/kotlin/de/fraunhofer/aisec/cpg_vis_neo4j/Application.kt similarity index 100% rename from cpg-neo4j/src/main/java/de/fraunhofer/aisec/cpg_vis_neo4j/Application.kt rename to cpg-neo4j/src/main/kotlin/de/fraunhofer/aisec/cpg_vis_neo4j/Application.kt diff --git a/cpg-neo4j/src/test/java/de/fraunhofer/aisec/cpg_vis_neo4j/ApplicationTest.kt b/cpg-neo4j/src/test/kotlin/de/fraunhofer/aisec/cpg_vis_neo4j/ApplicationTest.kt similarity index 100% rename from cpg-neo4j/src/test/java/de/fraunhofer/aisec/cpg_vis_neo4j/ApplicationTest.kt rename to cpg-neo4j/src/test/kotlin/de/fraunhofer/aisec/cpg_vis_neo4j/ApplicationTest.kt