All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.joern.swiftsrc2cpg.datastructures.Scope.scala Maven / Gradle / Ivy

There is a newer version: 4.0.78
Show newest version
package io.joern.swiftsrc2cpg.datastructures

import io.shiftleft.codepropertygraph.generated.nodes.NewNode

import scala.collection.mutable

/** Handles the scope stack for tracking identifier to variable relation.
  */
class Scope {

  private val pendingReferences: mutable.Buffer[PendingReference] =
    mutable.ListBuffer.empty[PendingReference]

  private var stack = Option.empty[ScopeElement]

  def getScopeHead: Option[ScopeElement] = stack

  def isEmpty: Boolean = stack.isEmpty

  def pushNewMethodScope(
    methodFullName: String,
    name: String,
    scopeNode: NewNode,
    capturingRefId: Option[NewNode]
  ): Unit =
    stack = Option(new MethodScopeElement(methodFullName, capturingRefId, name, scopeNode, surroundingScope = stack))

  def pushNewBlockScope(scopeNode: NewNode): Unit = {
    peek match {
      case Some(stackTop) =>
        stack = Option(new BlockScopeElement(stackTop.subScopeCounter.toString, scopeNode, surroundingScope = stack))
        stackTop.subScopeCounter += 1
      case None =>
        stack = Option(new BlockScopeElement("0", scopeNode, surroundingScope = stack))
    }
  }

  private def peek: Option[ScopeElement] = {
    stack
  }

  def popScope(): Unit = {
    stack = stack.get.surroundingScope
  }

  def addVariable(variableName: String, variableNode: NewNode, scopeType: ScopeType): Unit = {
    addVariable(stack, variableName, variableNode, scopeType)
  }

  def addVariableReference(variableName: String, referenceNode: NewNode): Unit = {
    pendingReferences prepend PendingReference(variableName, referenceNode, stack)
  }

  def resolve(unresolvedHandler: (NewNode, String) => (NewNode, ScopeType)): Iterator[ResolvedReference] = {
    pendingReferences.iterator.map { pendingReference =>
      val resolvedReferenceOption = pendingReference.tryResolve()

      resolvedReferenceOption.getOrElse {
        val methodScopeNode = Scope.getEnclosingMethodScopeNode(pendingReference.stack)
        val (newVariableNode, scopeType) =
          unresolvedHandler(methodScopeNode, pendingReference.variableName)
        addVariable(pendingReference.stack, pendingReference.variableName, newVariableNode, scopeType)
        pendingReference.tryResolve().get
      }
    }

  }

  private def addVariable(
    stack: Option[ScopeElement],
    variableName: String,
    variableNode: NewNode,
    scopeType: ScopeType
  ): Unit = {
    val scopeToAddTo = scopeType match {
      case MethodScope => Scope.getEnclosingMethodScopeElement(stack)
      case _           => stack.get
    }
    scopeToAddTo.addVariable(variableName, variableNode)
  }

}

object Scope {
  private def getEnclosingMethodScopeNode(scopeHead: Option[ScopeElement]): NewNode =
    getEnclosingMethodScopeElement(scopeHead).scopeNode

  def getEnclosingMethodScopeElement(scopeHead: Option[ScopeElement]): MethodScopeElement = {
    // There are no references outside of methods. Meaning we always find a MethodScope here.
    new ScopeElementIterator(scopeHead)
      .collectFirst { case methodScopeElement: MethodScopeElement => methodScopeElement }
      .getOrElse(throw new RuntimeException("Cannot find method scope."))
  }
}

class ScopeElementIterator(start: Option[ScopeElement]) extends Iterator[ScopeElement] {
  private var currentScopeElement = start

  override def hasNext: Boolean = {
    currentScopeElement.isDefined
  }

  override def next(): ScopeElement = {
    val result = currentScopeElement.get
    currentScopeElement = result.surroundingScope
    result
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy