Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ class AstCreator(
case switchStmt: PhpSwitchStmt => astForSwitchStmt(switchStmt) :: Nil
case tryStmt: PhpTryStmt => astForTryStmt(tryStmt) :: Nil
case returnStmt: PhpReturnStmt => astForReturnStmt(returnStmt) :: Nil
case classLikeStmt: PhpClassLikeStmt => astForClassLikeStmt(classLikeStmt)
case classLikeStmt: PhpClassLikeStmt => astForClassLikeStmt(classLikeStmt) :: Nil
case gotoStmt: PhpGotoStmt => astForGotoStmt(gotoStmt) :: Nil
case labelStmt: PhpLabelStmt => astForLabelStmt(labelStmt) :: Nil
case namespace: PhpNamespaceStmt => astForNamespaceStmt(namespace) :: Nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ trait AstCreatorHelper(disableFileContent: Boolean)(implicit withSchemaValidatio
}

protected def composeMethodFullName(methodName: String): String = {
scope.resolveIdentifier(methodName) match {
scope.resolveFunctionIdentifier(methodName) match {
case Some(importedMethod) => importedMethod.name
case None if methodName == NamespaceTraversal.globalNamespaceName => globalNamespace.fullName
case None =>
Expand All @@ -62,14 +62,12 @@ trait AstCreatorHelper(disableFileContent: Boolean)(implicit withSchemaValidatio
}
}

protected def composeMethodFullNameForCall(methodName: String, appendMetaTypeDeclExt: Boolean = false): String = {
scope.resolveIdentifier(methodName) match {
protected def composeMethodFullNameForCall(methodName: String): String = {
scope.resolveFunctionIdentifier(methodName) match {
case Some(importedMethod) => importedMethod.name
case None if methodName == NamespaceTraversal.globalNamespaceName => globalNamespace.fullName
case None =>
val className = Option
.when(appendMetaTypeDeclExt)(getTypeDeclPrefix.map(name => s"$name$MetaTypeDeclExtension").orNull)
.orElse(getTypeDeclPrefix)
val className = getTypeDeclPrefix

val nameWithClass = List(className, Some(methodName)).flatten.mkString(MethodDelimiter)
prependNamespacePrefix(nameWithClass)
Expand Down Expand Up @@ -252,13 +250,13 @@ trait AstCreatorHelper(disableFileContent: Boolean)(implicit withSchemaValidatio

protected def getMfn(call: PhpCallExpr, name: String): String = {
lazy val default = s"$UnresolvedNamespace$MethodDelimiter$name"
lazy val maybeResolvedFunction = scope.resolveIdentifier(name).filter(_.isInstanceOf[PhpFunction])
lazy val maybeResolvedFunction = scope.resolveFunctionIdentifier(name)
call.target match {
case Some(nameExpr: PhpNameExpr) if call.isStatic =>
// Static method call with a simple receiver
if (nameExpr.name == NameConstants.Self)
composeMethodFullNameForCall(name, appendMetaTypeDeclExt = !scope.isSurroundedByMetaclassTypeDecl)
else s"${nameExpr.name}$MetaTypeDeclExtension$MethodDelimiter$name"
composeMethodFullNameForCall(name)
else s"${nameExpr.name}$MethodDelimiter$name"
case Some(_) =>
// As soon as we have a dynamic component to the call, we can't truly define a method full name
default
Expand All @@ -269,7 +267,7 @@ trait AstCreatorHelper(disableFileContent: Boolean)(implicit withSchemaValidatio
name
// Assume name-space local function call
case None =>
composeMethodFullNameForCall(name, call.isStatic)
composeMethodFullNameForCall(name)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) {
val name = getCallName(call, nameAst)
val arguments = call.args.map(astForCallArg)

val inStaticScope = scope.getEnclosingTypeDeclTypeFullName.exists(_.endsWith(Domain.MetaTypeDeclExtension))
/*
* A receiver only makes sense if one can track the receiver back to some sort of runtime type information. In the
* case of a normal top-level call like foo() that is not possible. There is no corresponding foo identifier which
Expand Down Expand Up @@ -143,9 +142,9 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) {

val staticReceiver = call.target.collect {
case nameExpr: PhpNameExpr if nameExpr.name == NameConstants.Self =>
getTypeDeclPrefix.map(name => s"$name$MetaTypeDeclExtension")
getTypeDeclPrefix
case nameExpr: PhpNameExpr =>
Option(s"${nameExpr.name}$MetaTypeDeclExtension")
Option(s"${nameExpr.name}")
}.flatten

val callRoot = callNode(call, code, name, fullName, dispatchType, None, Some(Defines.Any), staticReceiver)
Expand Down Expand Up @@ -872,7 +871,7 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) {
val initArgs = expr.args.map(astForCallArg)
val initFullName = s"$className$MethodDelimiter$ConstructorMethodName"
val initCode = s"new $className(${initArgs.map(_.rootCodeOrEmpty).mkString(",")})"
val maybeTypeHint = scope.resolveIdentifier(className).map(_.name) // consider imported or defined types
val maybeTypeHint = scope.resolveClassIdentifier(className).map(_.name) // consider imported or defined types
val initCallNode = callNode(
expr,
initCode,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,12 @@ import io.shiftleft.codepropertygraph.generated.nodes.{
NewTypeDecl,
NewTypeRef
}
import io.shiftleft.codepropertygraph.generated.{
DispatchTypes,
EdgeTypes,
ModifierTypes,
NodeTypes,
Operators,
PropertyDefaults
}
import io.shiftleft.codepropertygraph.generated.{DispatchTypes, EdgeTypes, ModifierTypes, NodeTypes, Operators}
import io.shiftleft.semanticcpg.language.types.structure.NamespaceTraversal

trait AstForTypesCreator(implicit withSchemaValidation: ValidationMode) { this: AstCreator =>

protected def astForClassLikeStmt(stmt: PhpClassLikeStmt): List[Ast] = {
protected def astForClassLikeStmt(stmt: PhpClassLikeStmt): Ast = {
val (staticStmts, dynamicStmts) = stmt.stmts.partition {
case x: PhpPropertyStmt if x.modifiers.contains(ModifierTypes.STATIC) => true
case x: PhpMethodDecl if x.modifiers.contains(ModifierTypes.STATIC) => true
Expand All @@ -36,9 +29,9 @@ trait AstForTypesCreator(implicit withSchemaValidation: ValidationMode) { this:
}

stmt.name match {
case None => astForAnonymousClass(stmt, dynamicStmts, staticStmts) :: Nil
case None => astForAnonymousClass(stmt, dynamicStmts, staticStmts)
case Some(name) if name.name.contains("anon-class") =>
astForAnonymousClass(stmt, dynamicStmts, staticStmts) :: Nil
astForAnonymousClass(stmt, dynamicStmts, staticStmts)
case Some(name) => astForNamedClass(stmt, name, dynamicStmts, staticStmts)
}
}
Expand Down Expand Up @@ -69,18 +62,13 @@ trait AstForTypesCreator(implicit withSchemaValidation: ValidationMode) { this:
): Ast = {
val useTraitNames = dynamicStmts.collect { case x: PhpTraitUseStmt => x }.flatMap(_.traits)
val inheritsFrom = (stmt.extendsNames ++ useTraitNames ++ stmt.implementedInterfaces).map(_.name)
val inheritsFromMeta =
(stmt.extendsNames ++ useTraitNames ++ stmt.implementedInterfaces).map(name =>
s"${name.name}$MetaTypeDeclExtension"
)

val className = stmt.name match {
case Some(name) => name.name
case None => this.scope.getNewClassTmp
}

val classFullName = prependNamespacePrefix(className)
val metaTypeDeclClassFullName = s"${classFullName}$MetaTypeDeclExtension"
val classFullName = prependNamespacePrefix(className)

val code = codeForClassStmt(stmt, PhpNameExpr(className, stmt.attributes))

Expand All @@ -94,16 +82,6 @@ trait AstForTypesCreator(implicit withSchemaValidation: ValidationMode) { this:
alias = None
)

val metaTypeDecl = typeDeclNode(
node = stmt,
name = s"${className}$MetaTypeDeclExtension",
fullName = metaTypeDeclClassFullName,
filename = relativeFileName,
code = code,
inherits = inheritsFromMeta,
alias = None
)

scope.surroundingAstLabel.foreach(typeDeclTemp.astParentType(_))
scope.surroundingScopeFullName.foreach(typeDeclTemp.astParentFullName(_))
scope.pushNewScope(TypeScope(typeDeclTemp, classFullName))
Expand All @@ -112,20 +90,12 @@ trait AstForTypesCreator(implicit withSchemaValidation: ValidationMode) { this:
val modifiers = stmt.modifiers.map(modifierNode(stmt, _)).map(Ast(_))
val annotationAsts = stmt.attributeGroups.flatMap(astForAttributeGroup)

scope.popScope()
val staticClassConsts = staticStmts.collect { case cs: PhpConstStmt => cs }.flatMap(astsForConstStmt)
val staticProperties = staticStmts.collect { case cp: PhpPropertyStmt => cp }.flatMap(astsForPropertyStmt)
val staticMethodStmts = staticStmts.collect { case mp: PhpMethodDecl => mp }.map(astForMethodDecl(_))

(scope.surroundingAstLabel, scope.surroundingScopeFullName) match {
case (Some(astLabel), Some(sfn)) =>
metaTypeDecl.astParentType(astLabel)
metaTypeDecl.astParentFullName(sfn)
case _ =>
logger.warn(
s"Expected values for `surroundingAstLabel` and `surroundingScopeFullName` in `astForAnonymousClass`"
)
}
val clinitAst = astForStaticAndConstInits(stmt)

scope.pushNewScope(TypeScope(metaTypeDecl, metaTypeDeclClassFullName))
val metaTypeDeclAst = astForMetaTypeDecl(stmt, staticStmts, metaTypeDecl)
scope.popScope()

if scope.surroundingAstLabel.contains(NodeTypes.TYPE_DECL) then {
Expand All @@ -139,29 +109,23 @@ trait AstForTypesCreator(implicit withSchemaValidation: ValidationMode) { this:
typeDeclMember.astParentType(NodeTypes.TYPE_DECL)
}

val metaTypeDeclMember = NewMember()
.name(s"${className}$MetaTypeDeclExtension")
.code(s"${className}$MetaTypeDeclExtension")
.dynamicTypeHintFullName(Seq(s"${classFullName}${MetaTypeDeclExtension}"))

scope.getEnclosingTypeDeclTypeFullName.foreach { tfn =>
metaTypeDeclMember.astParentFullName(s"$tfn$MetaTypeDeclExtension")
metaTypeDeclMember.astParentType(NodeTypes.TYPE_DECL)
}

diffGraph.addNode(typeDeclMember)
diffGraph.addNode(metaTypeDeclMember)
}

val prefixAst = createTypeRefPointer(typeDeclTemp)
val allChildren = List(
modifiers,
bodyStmts,
annotationAsts,
staticClassConsts,
staticProperties,
staticMethodStmts,
clinitAst
).flatten
val typeDeclAst = Ast(typeDeclTemp)
.withChildren(modifiers)
.withChildren(bodyStmts)
.withChildren(annotationAsts)
.withChildren(allChildren)

Ast.storeInDiffGraph(typeDeclAst, diffGraph)
Ast.storeInDiffGraph(metaTypeDeclAst, diffGraph)

prefixAst
}

Expand Down Expand Up @@ -211,7 +175,7 @@ trait AstForTypesCreator(implicit withSchemaValidation: ValidationMode) { this:
name: PhpNameExpr,
dynamicStmts: List[PhpStmt],
staticStmts: List[PhpStmt]
): List[Ast] = {
): Ast = {
/*
the `use <trait>` can be used anywhere in a class definition, but the method is available to be called in any function in the class so we find all the traits before processing the rest of the class.
In the below sample, if we execute `(Foo.new())->foo()` it executes the `traitFunc` even though the `use <trait>` is after the `foo` method
Expand All @@ -229,11 +193,7 @@ trait AstForTypesCreator(implicit withSchemaValidation: ValidationMode) { this:
*/
val useTraitNames = dynamicStmts.collect { case x: PhpTraitUseStmt => x }.flatMap(_.traits)
val inheritsFrom = (stmt.extendsNames ++ useTraitNames ++ stmt.implementedInterfaces).map(_.name)
val inheritsFromMeta =
(stmt.extendsNames ++ useTraitNames ++ stmt.implementedInterfaces).map(name =>
s"${name.name}$MetaTypeDeclExtension"
)
val code = codeForClassStmt(stmt, name)
val code = codeForClassStmt(stmt, name)

val (dedupedName, fullName) =
if (name.name == NamespaceTraversal.globalNamespaceName)
Expand All @@ -243,17 +203,7 @@ trait AstForTypesCreator(implicit withSchemaValidation: ValidationMode) { this:
(name.name, prependNamespacePrefix(dedupClassName))
}

val metaTypeDeclFullName = s"$fullName$MetaTypeDeclExtension"

val typeDecl = typeDeclNode(stmt, dedupedName, fullName, relativeFileName, code, inherits = inheritsFrom)
val metaTypeDeclNode = typeDeclNode(
stmt,
s"$dedupedName$MetaTypeDeclExtension",
metaTypeDeclFullName,
relativeFileName,
code,
inherits = inheritsFromMeta
)

// Add this to scope for symbol resolution
scope.useTypeDecl(dedupedName, fullName)
Expand All @@ -262,9 +212,10 @@ trait AstForTypesCreator(implicit withSchemaValidation: ValidationMode) { this:

scope.pushNewScope(TypeScope(typeDecl, fullName))

val bodyStmts = astsForClassLikeBody(stmt, dynamicStmts, createDefaultConstructor, Option(typeDecl))
val modifiers = stmt.modifiers.map(modifierNode(stmt, _)).map(Ast(_))
val bodyStmts = astsForClassLikeBody(stmt, staticStmts ++ dynamicStmts, createDefaultConstructor, Option(typeDecl))
val modifiers = stmt.modifiers.map(modifierNode(stmt, _)).map(Ast(_))
val annotationAsts = stmt.attributeGroups.flatMap(astForAttributeGroup)
val clinitAst = astForStaticAndConstInits(stmt).toList

// This needs to be carried over for enums
val staticConsts = if (stmt.classLikeType == ClassLikeTypes.Enum) {
Expand All @@ -273,33 +224,14 @@ trait AstForTypesCreator(implicit withSchemaValidation: ValidationMode) { this:
List.empty
}

scope.popScope()

val classTypeDeclAst = Ast(typeDecl).withChildren(modifiers).withChildren(bodyStmts).withChildren(annotationAsts)

scope.pushNewScope(TypeScope(metaTypeDeclNode, metaTypeDeclFullName))

staticConsts.foreach(init => scope.addConstOrStaticInitToScope(init.originNode, init.memberNode, init.value))
val metaTypeDeclAst = astForMetaTypeDecl(stmt, staticStmts, metaTypeDeclNode)
scope.popScope()

List(classTypeDeclAst, metaTypeDeclAst)
}

private def astForMetaTypeDecl(
stmt: PhpClassLikeStmt,
staticStmts: List[PhpStmt],
metaTypeDeclNode: NewTypeDecl
): Ast = {
val classConsts = staticStmts.collect { case cs: PhpConstStmt => cs }.flatMap(astsForConstStmt)
val properties = staticStmts.collect { case cp: PhpPropertyStmt => cp }.flatMap(astsForPropertyStmt)
val methodStmts = staticStmts.collect { case mp: PhpMethodDecl => mp }.map(astForMethodDecl(_))

val clinitAst = astForStaticAndConstInits(stmt)
scope.popScope()

val metaTypeDeclBodyStmts = List(classConsts, properties, methodStmts, clinitAst).flatten
val classTypeDeclAst =
Ast(typeDecl).withChildren(modifiers).withChildren(bodyStmts).withChildren(annotationAsts).withChildren(clinitAst)

Ast(metaTypeDeclNode).withChildren(metaTypeDeclBodyStmts)
classTypeDeclAst
}

protected def astsForClassLikeBody(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ object Domain {
val MethodDelimiter = "."
// Used for creating the default constructor.
val ConstructorMethodName = "__construct"
val MetaTypeDeclExtension = "<metaclass>"
val GlobalName = "<global>"

final case class PhpAttributes(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package io.joern.php2cpg.utils

import io.joern.php2cpg.astcreation.AstCreator.NameConstants
import io.joern.php2cpg.parser.Domain.{MetaTypeDeclExtension, PhpExpr, PhpNode}
import io.joern.php2cpg.parser.Domain.{PhpExpr, PhpNode}
import io.joern.php2cpg.passes.SymbolSummaryPass.*
import io.joern.x2cpg.Ast
import io.joern.x2cpg.datastructures.{NamespaceLikeScope, ScopeElement, TypedScopeElement, Scope as X2CpgScope}
Expand Down Expand Up @@ -158,12 +158,6 @@ class Scope(summary: Map[String, Seq[SymbolSummary]] = Map.empty)
methods
}

def isSurroundedByMetaclassTypeDecl: Boolean =
stack
.map(_.scopeNode)
.collectFirst { case TypeScope(td, _) => td }
.exists(_.name.endsWith(MetaTypeDeclExtension))

def isSurroundedByArrowClosure: Boolean =
stack.map(_.scopeNode).collectFirst { case nm: MethodScope if nm.isArrowFunc => nm }.isDefined

Expand Down Expand Up @@ -342,4 +336,16 @@ class Scope(summary: Map[String, Seq[SymbolSummary]] = Map.empty)
def resolveIdentifier(symbol: String): Option[SymbolSummary] = {
importedSymbols.get(symbol)
}

def resolveClassIdentifier(symbol: String): Option[SymbolSummary] = {
resolveIdentifier(symbol).collect { case sym: PhpClass =>
sym
}
}

def resolveFunctionIdentifier(symbol: String): Option[SymbolSummary] = {
resolveIdentifier(symbol).collect { case sym: PhpFunction =>
sym
}
}
}
Loading