Skip to content

Commit

Permalink
Playing around with a dynamic type
Browse files Browse the repository at this point in the history
  • Loading branch information
oxisto committed Sep 3, 2024
1 parent def570f commit 5d8dee6
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,10 @@ abstract class Language<T : LanguageFrontend<*, *>> : Node() {
open fun shouldPropagateType(hasType: HasType, srcType: Type): Boolean {
val nodeType = hasType.type

if (srcType is DynamicType) {
return false
}

// We only want to add certain types, in case we have a numeric type
if (nodeType is NumericType) {
// We do not allow to propagate non-numeric types into numeric types
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ fun MetadataProvider?.unknownType(): Type {
}
}

fun LanguageProvider.dynamicType(): Type {
return DynamicType(this.language)
}

fun LanguageProvider.autoType(): Type {
return AutoType(this.language)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import de.fraunhofer.aisec.cpg.graph.edges.unwrapping
import de.fraunhofer.aisec.cpg.graph.types.HasType
import de.fraunhofer.aisec.cpg.graph.types.TupleType
import de.fraunhofer.aisec.cpg.graph.types.Type
import de.fraunhofer.aisec.cpg.graph.types.UnknownType
import org.neo4j.ogm.annotation.Relationship
import org.slf4j.Logger
import org.slf4j.LoggerFactory
Expand Down Expand Up @@ -208,10 +209,19 @@ class AssignExpression :
val targets = findTargets(src)
if (targets.size == newType.types.size) {
// Set the corresponding type on the left-side
newType.types.forEachIndexed { idx, t -> lhs.getOrNull(idx)?.addAssignedType(t) }
newType.types.forEachIndexed { idx, t ->
var lhs = lhs.getOrNull(idx) ?: return
lhs.addAssignedType(t)
}
}
} else {
findTargets(src).forEach { it.addAssignedType(newType) }
findTargets(src).forEach {
// If the type is unknown, we can also set it
if (it.type is UnknownType) {
it.type = newType
}
it.addAssignedType(newType)
}
}

// If this is used as an expression, we also set the type accordingly
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright (c) 2024, 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

/**
* This type represents a [Type] that is dynamically determined at run-time. This is used for a
* [Language]. which has dynamic runtime typing, such as Python or Java/TypeScript.
*/
class DynamicType(override var language: Language<*>?) : Type("dynamic", language) {
override fun reference(pointer: PointerType.PointerOrigin?): Type {
TODO("Not yet implemented")
}

override fun dereference(): Type {
TODO("Not yet implemented")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ interface HasType : ContextProvider, LanguageProvider {

// If we would only propagate the unknown type, we can also skip it
val newType = this.type
if (newType !is UnknownType) {
if (newType !is UnknownType && newType !is DynamicType) {
// Immediately inform about changes
typeObserver.typeChanged(newType, this)
}
Expand Down Expand Up @@ -207,6 +207,12 @@ interface HasType : ContextProvider, LanguageProvider {
class InitializerTypePropagation(private var decl: HasType, private var tupleIdx: Int = -1) :
HasType.TypeObserver {
override fun typeChanged(newType: Type, src: HasType) {
// We do not want to change the type of a "dynamic type" declaration, since the "type" will
// always be dynamic.
if (decl.type is DynamicType) {
return
}

if (newType is TupleType && tupleIdx != -1) {
decl.type = newType.types.getOrElse(tupleIdx) { decl.unknownType() }
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ class PythonAddDeclarationsPass(ctx: TranslationContext) : ComponentPass(ctx) {
newVariableDeclaration(node.name)
}

decl.type = node.dynamicType()
decl.code = node.code
decl.location = node.location
decl.isImplicit = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1256,6 +1256,27 @@ class PythonFrontendTest : BaseTest() {
assertInvokes(call, cCompletelyDifferentFunc)
}

@Test
fun testTypePropagation() {
val topLevel = Path.of("src", "test", "resources", "python")
val tu =
analyzeAndGetFirstTU(
listOf(topLevel.resolve("type_propagation.py").toFile()),
topLevel,
true
) {
it.registerLanguage<PythonLanguage>()
}
assertNotNull(tu)

var a = tu.variables["a"]
assertNotNull(a)
println("type: {${a.type}, assigned: {${a.assignedTypes}")

var refs = tu.refs("a")
refs.forEach { println("type: {${it.type}, assigned: {${it.assignedTypes}") }
}

class PythonValueEvaluator : ValueEvaluator() {
override fun computeBinaryOpEffect(
lhsValue: Any?,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
a = "test"
a = 2

0 comments on commit 5d8dee6

Please sign in to comment.