Skip to content

Commit

Permalink
Add name mangling uses for name expressions (#5319)
Browse files Browse the repository at this point in the history
  • Loading branch information
johannescoetzee authored Feb 20, 2025
1 parent de1b767 commit 3102b73
Show file tree
Hide file tree
Showing 10 changed files with 637 additions and 179 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -280,13 +280,13 @@ private[expressions] trait AstForLambdasCreator { this: AstCreator =>
val scopeVariable = variables.head
val capturedLocal = localNode(
lambdaNode,
scopeVariable.name,
scopeVariable.name,
scopeVariable.mangledName,
scopeVariable.mangledName,
scopeVariable.typeFullName,
Option(closureBindingId),
Option(scopeVariable.genericSignature)
)
scope.enclosingBlock.foreach(_.addLocal(capturedLocal))
scope.enclosingBlock.foreach(_.addLocal(capturedLocal, scopeVariable.name))

ClosureBindingEntry(scopeVariable, closureBindingNode) -> capturedLocal
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,16 @@ import com.github.javaparser.ast.Node
import com.github.javaparser.ast.expr.NameExpr
import com.github.javaparser.resolution.declarations.ResolvedFieldDeclaration
import io.joern.javasrc2cpg.astcreation.{AstCreator, ExpectedType}
import io.joern.javasrc2cpg.scope.JavaScopeElement.TypeDeclScope
import io.joern.javasrc2cpg.typesolvers.TypeInfoCalculator.TypeConstants
import io.joern.javasrc2cpg.util.NameConstants
import io.joern.x2cpg.{Ast, Defines}
import io.shiftleft.codepropertygraph.generated.nodes.{
NewLocal,
NewMethodParameterIn,
NewTypeDecl,
NewTypeRef,
NewUnknown
}
import io.joern.x2cpg.Ast
import io.shiftleft.codepropertygraph.generated.nodes.{NewLocal, NewMethodParameterIn, NewTypeRef}

import scala.util.Success
import io.joern.javasrc2cpg.scope.Scope.{
CapturedVariable,
NotInScope,
ScopeMember,
ScopeParameter,
ScopeVariable,
SimpleVariable
}
import io.joern.javasrc2cpg.scope.Scope.{CapturedVariable, NotInScope, ScopeMember, SimpleVariable}
import org.slf4j.LoggerFactory
import io.joern.x2cpg.utils.AstPropertiesUtil.*
import io.shiftleft.codepropertygraph.generated.Operators
import io.joern.x2cpg.utils.NodeBuilders.{newIdentifierNode, newOperatorCallNode}
import io.joern.javasrc2cpg.scope.PatternVariableInfo

trait AstForNameExpressionsCreator { this: AstCreator =>

Expand All @@ -56,7 +40,9 @@ trait AstForNameExpressionsCreator { this: AstCreator =>
)

case SimpleVariable(variable) =>
val identifier = identifierNode(nameExpr, name, name, typeFullName.getOrElse(defaultTypeFallback()))
val mangledName = variable.mangledName
val identifier =
identifierNode(nameExpr, mangledName, mangledName, typeFullName.getOrElse(defaultTypeFallback()))
val captured = variable.node match {
case param: NewMethodParameterIn => Some(param)
case local: NewLocal => Some(local)
Expand Down Expand Up @@ -130,11 +116,11 @@ trait AstForNameExpressionsCreator { this: AstCreator =>
logger.warn(
s"Attempted to create AST for captured variable ${variable.name}, but could not find `this` param in direct scope."
)
Ast(identifierNode(nameExpr, variable.name, variable.name, variable.typeFullName))
Ast(identifierNode(nameExpr, variable.mangledName, variable.mangledName, variable.typeFullName))

case SimpleVariable(scopeVariable) =>
val thisIdentifier =
identifierNode(nameExpr, scopeVariable.name, scopeVariable.name, scopeVariable.typeFullName)
identifierNode(nameExpr, scopeVariable.mangledName, scopeVariable.mangledName, scopeVariable.typeFullName)
val thisAst = Ast(thisIdentifier).withRefEdge(thisIdentifier, scopeVariable.node)

val lineNumber = line(nameExpr)
Expand All @@ -154,13 +140,13 @@ trait AstForNameExpressionsCreator { this: AstCreator =>

val finalFieldAccess = newOperatorCallNode(
Operators.fieldAccess,
s"${outerClassChain.rootCodeOrEmpty}.${variable.name}",
s"${outerClassChain.rootCodeOrEmpty}.${variable.mangledName}",
Some(variable.typeFullName),
lineNumber,
columnNumber
)

val captureFieldIdentifier = fieldIdentifierNode(nameExpr, variable.name, variable.name)
val captureFieldIdentifier = fieldIdentifierNode(nameExpr, variable.mangledName, variable.name)
callAst(finalFieldAccess, List(outerClassChain, Ast(captureFieldIdentifier)))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,86 +45,6 @@ trait AstForPatternExpressionsCreator { this: AstCreator =>

private val logger = LoggerFactory.getLogger(this.getClass)

trait PatternInitTreeNode(val patternExpr: PatternExpr) {
def getAst: Ast

def typeFullName: Option[String]
}

class PatternInitRoot(patternExpr: PatternExpr, ast: PatternInitAndRefAsts) extends PatternInitTreeNode(patternExpr) {
override def getAst: Ast = ast.get

override def typeFullName: Option[String] = ast.rootType
}

class PatternInitNode(
parentNode: PatternInitTreeNode,
patternExpr: PatternExpr,
fieldName: String,
fieldTypeFullName: Option[String],
requiresTemporaryVariable: Boolean
) extends PatternInitTreeNode(patternExpr) {
private var cachedResult: Option[PatternInitAndRefAsts] = None

override def typeFullName: Option[String] = fieldTypeFullName

override def getAst: Ast = {
cachedResult.map(_.get).getOrElse {
val parentAst = parentNode.getAst
val patternTypeFullName = tryWithSafeStackOverflow(patternExpr.getType).toOption
.map { typ =>
scope
.lookupScopeType(typ.asString())
.map(_.typeFullName)
.orElse(typeInfoCalc.fullName(typ))
.getOrElse(defaultTypeFallback(typ))
}
.getOrElse(defaultTypeFallback())

val parentPatternType = getPatternTypeFullName(parentNode.patternExpr)
val lhsAst = castAstIfNecessary(parentNode.patternExpr, parentPatternType, parentAst)

val signature = composeSignature(fieldTypeFullName, Option(Nil), 0)
val typeDeclFullName =
if (isResolvedTypeFullName(parentPatternType))
parentPatternType
else
s"${Defines.UnresolvedNamespace}.${code(parentNode.patternExpr.getType)}"
val methodFullName = Util.composeMethodFullName(typeDeclFullName, fieldName, signature)
val methodCodePrefix = lhsAst.root match {
case Some(call: NewCall) if call.name.startsWith("<operator") => s"(${call.code})"
case Some(root: AstNodeNew) => root.code
case _ => ""

}
val methodCode = s"$methodCodePrefix.$fieldName()"

val fieldAccessorCall = callNode(
patternExpr,
methodCode,
fieldName,
methodFullName,
DispatchTypes.DYNAMIC_DISPATCH,
Option(signature),
fieldTypeFullName.orElse(Option(defaultTypeFallback()))
)

val fieldAccessorAst = callAst(fieldAccessorCall, Nil, Option(lhsAst))

val patternInitWithRef = if (requiresTemporaryVariable) {
val patternInitWithRef = initAndRefAstsForPatternInitializer(patternExpr, fieldAccessorAst)
patternInitWithRef
} else {
PatternInitAndRefAsts(fieldAccessorAst)
}

cachedResult = Option(patternInitWithRef)

patternInitWithRef.get
}
}
}

/** In the lowering for instanceof expressions with patterns like `X instanceof Foo f`, the first argument to
* `instanceof` (in this case `X`) appears in the CPG at least 2 times:
* - once for the `X instanceof Foo` check
Expand Down Expand Up @@ -174,7 +94,7 @@ trait AstForPatternExpressionsCreator { this: AstCreator =>

// Don't need to add the local to the block scope since the only identifiers referencing it are created here
// (so a lookup for the local will never be done)
scope.enclosingMethod.foreach(_.putPatternLocals(tmpLocal, None))
scope.enclosingMethod.foreach(_.registerLocalToAddToCpg(tmpLocal))

val initAst =
callAst(tmpAssignmentNode, Ast(tmpIdentifier) :: patternInitAst :: Nil).withRefEdge(tmpIdentifier, tmpLocal)
Expand Down Expand Up @@ -212,6 +132,7 @@ trait AstForPatternExpressionsCreator { this: AstCreator =>

case typePatternExpr: TypePatternExpr =>
val variableName = typePatternExpr.getNameAsString

val variableType = {
tryWithSafeStackOverflow(typePatternExpr.getType).toOption
.map(typ =>
Expand All @@ -225,15 +146,28 @@ trait AstForPatternExpressionsCreator { this: AstCreator =>
}
val variableTypeCode = tryWithSafeStackOverflow(code(typePatternExpr.getType)).getOrElse(variableType)
val genericSignature = binarySignatureCalculator.variableBinarySignature(typePatternExpr.getType)
val patternLocal = localNode(
typePatternExpr,
variableName,
code(typePatternExpr),
variableType,
genericSignature = Option(genericSignature)
)
scope.enclosingMethod.get.putPatternLocals(patternLocal, Option(typePatternExpr))
val patternIdentifier = identifierNode(typePatternExpr, variableName, variableName, variableType)
val patternLocal = scope.getHoistedPatternLocals.find(local =>
local.name == variableName && local.typeFullName == variableType
) match {
case Some(local) =>
scope.enclosingMethod.get.registerPatternLocal(typePatternExpr, local)
local
case None =>
val mangledName = scope.getMangledName(variableName)
val patternLocal = localNode(
typePatternExpr,
mangledName,
code(typePatternExpr),
variableType,
genericSignature = Option(genericSignature)
)
scope.enclosingBlock.get.addHoistedPatternLocal(patternLocal)
scope.enclosingMethod.get.registerLocalToAddToCpg(patternLocal)
scope.enclosingMethod.get.registerPatternLocal(typePatternExpr, patternLocal)
patternLocal
}

val patternIdentifier = identifierNode(typePatternExpr, patternLocal.name, patternLocal.name, variableType)

val initializerAst = castAstIfNecessary(typePatternExpr, variableType, patternNode.getAst)

Expand Down Expand Up @@ -405,4 +339,84 @@ trait AstForPatternExpressionsCreator { this: AstCreator =>
)
.getOrElse(defaultTypeFallback())
}

trait PatternInitTreeNode(val patternExpr: PatternExpr) {
def getAst: Ast

def typeFullName: Option[String]
}

class PatternInitRoot(patternExpr: PatternExpr, ast: PatternInitAndRefAsts) extends PatternInitTreeNode(patternExpr) {
override def getAst: Ast = ast.get

override def typeFullName: Option[String] = ast.rootType
}

class PatternInitNode(
parentNode: PatternInitTreeNode,
patternExpr: PatternExpr,
fieldName: String,
fieldTypeFullName: Option[String],
requiresTemporaryVariable: Boolean
) extends PatternInitTreeNode(patternExpr) {
private var cachedResult: Option[PatternInitAndRefAsts] = None

override def typeFullName: Option[String] = fieldTypeFullName

override def getAst: Ast = {
cachedResult.map(_.get).getOrElse {
val parentAst = parentNode.getAst
val patternTypeFullName = tryWithSafeStackOverflow(patternExpr.getType).toOption
.map { typ =>
scope
.lookupScopeType(typ.asString())
.map(_.typeFullName)
.orElse(typeInfoCalc.fullName(typ))
.getOrElse(defaultTypeFallback(typ))
}
.getOrElse(defaultTypeFallback())

val parentPatternType = getPatternTypeFullName(parentNode.patternExpr)
val lhsAst = castAstIfNecessary(parentNode.patternExpr, parentPatternType, parentAst)

val signature = composeSignature(fieldTypeFullName, Option(Nil), 0)
val typeDeclFullName =
if (isResolvedTypeFullName(parentPatternType))
parentPatternType
else
s"${Defines.UnresolvedNamespace}.${code(parentNode.patternExpr.getType)}"
val methodFullName = Util.composeMethodFullName(typeDeclFullName, fieldName, signature)
val methodCodePrefix = lhsAst.root match {
case Some(call: NewCall) if call.name.startsWith("<operator") => s"(${call.code})"
case Some(root: AstNodeNew) => root.code
case _ => ""

}
val methodCode = s"$methodCodePrefix.$fieldName()"

val fieldAccessorCall = callNode(
patternExpr,
methodCode,
fieldName,
methodFullName,
DispatchTypes.DYNAMIC_DISPATCH,
Option(signature),
fieldTypeFullName.orElse(Option(defaultTypeFallback()))
)

val fieldAccessorAst = callAst(fieldAccessorCall, Nil, Option(lhsAst))

val patternInitWithRef = if (requiresTemporaryVariable) {
val patternInitWithRef = initAndRefAstsForPatternInitializer(patternExpr, fieldAccessorAst)
patternInitWithRef
} else {
PatternInitAndRefAsts(fieldAccessorAst)
}

cachedResult = Option(patternInitWithRef)

patternInitWithRef.get
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -166,18 +166,12 @@ trait AstForSimpleExpressionsCreator { this: AstCreator =>
scope.pushBlockScope()
val context = new BinaryExprContext(expr, new CombinedTypeSolver())

context
.typePatternExprsExposedToChild(expr.getRight)
.asScala
.flatMap(pattern => scope.enclosingMethod.flatMap(_.getLocalForPattern(pattern)))
.foreach { local =>
scope.enclosingBlock.foreach(_.addLocal(local))
}
scope.addLocalsForPatternsToEnclosingBlock(context.typePatternExprsExposedToChild(expr.getRight).asScala.toList)

val rhsArgs = astsForExpression(expr.getRight, expectedType)
// TODO Fix code
// val rhsCode = rhsArgs.headOption.flatMap(_.rootCode).getOrElse("")
scope.popBlockScope()
scope.popBlockAndHoistPatternVariables()

val args = lhsArgs ++ rhsArgs

Expand Down Expand Up @@ -266,12 +260,12 @@ trait AstForSimpleExpressionsCreator { this: AstCreator =>
scope.pushBlockScope()
scope.addLocalsForPatternsToEnclosingBlock(patternsExposedToThen)
val thenAst = astsForExpression(expr.getThenExpr, expectedType)
scope.popBlockScope()
scope.popBlockAndHoistPatternVariables()

scope.pushBlockScope()
scope.addLocalsForPatternsToEnclosingBlock(patternsExposedToElse)
val elseAst = astsForExpression(expr.getElseExpr, expectedType)
scope.popBlockScope()
scope.popBlockAndHoistPatternVariables()

val typeFullName =
expressionReturnTypeFullName(expr)
Expand Down
Loading

0 comments on commit 3102b73

Please sign in to comment.