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

io.joern.rubysrc2cpg.deprecated.astcreation.RubyScope.scala Maven / Gradle / Ivy

package io.joern.rubysrc2cpg.deprecated.astcreation

import io.joern.x2cpg.datastructures.Scope
import io.shiftleft.codepropertygraph.generated.{DiffGraphBuilder, EdgeTypes}
import io.shiftleft.codepropertygraph.generated.nodes.{DeclarationNew, NewIdentifier, NewLocal, NewNode}

import scala.collection.mutable

/** Extends the Scope class to help scope variables and create locals.
  *
  * TODO: Extend this to similarly link parameter nodes (especially `this` node) for consistency.
  */
class RubyScope extends Scope[String, NewIdentifier, NewNode] {

  private type VarMap        = Map[String, VarGroup]
  private type ScopeNodeType = NewNode

  /** Groups a local node with its referencing identifiers.
    */
  private case class VarGroup(local: NewLocal, ids: List[NewIdentifier])

  /** Links a scope to its variable groupings.
    */
  private val scopeToVarMap = mutable.HashMap.empty[ScopeNodeType, VarMap]

  override def addToScope(identifier: String, variable: NewIdentifier): NewNode = {
    val scopeNode = super.addToScope(identifier, variable)
    stack.headOption.foreach(head => scopeToVarMap.appendIdentifierToVarGroup(head.scopeNode, variable))
    scopeNode
  }

  override def popScope(): Option[NewNode] = {
    stack.headOption.map(_.scopeNode).foreach(scopeToVarMap.remove)
    super.popScope()
  }

  /** Will generate local nodes for this scope's variables, excluding those that reference parameters.
    * @param paramNames
    *   the names of parameters.
    */
  def createAndLinkLocalNodes(diffGraph: DiffGraphBuilder, paramNames: Set[String] = Set.empty): List[DeclarationNew] =
    stack.headOption match
      case Some(top) => scopeToVarMap.buildVariableGroupings(top.scopeNode, paramNames ++ Set("this"), diffGraph)
      case None      => List.empty[DeclarationNew]

      /** @param identifier
        *   the identifier to count
        * @return
        *   the number of times the given identifier occurs in the immediate scope.
        */
  def numVariableReferences(identifier: String): Int = {
    stack.map(_.scopeNode).flatMap(scopeToVarMap.get).flatMap(_.get(identifier)).map(_.ids.size).headOption.getOrElse(0)
  }

  private implicit class IdentifierExt(node: NewIdentifier) {

    /** Creates a new VarGroup and corresponding NewLocal for the given identifier.
      */
    def toNewVarGroup: VarGroup = {
      val newLocal = NewLocal()
        .name(node.name)
        .code(node.name)
        .lineNumber(node.lineNumber)
        .columnNumber(node.columnNumber)
        .typeFullName(node.typeFullName)
      VarGroup(newLocal, List(node))
    }

  }

  private implicit class ScopeExt(scopeMap: mutable.Map[ScopeNodeType, VarMap]) {

    /** Registers the identifier to its corresponding variable grouping in the given scope.
      */
    def appendIdentifierToVarGroup(key: ScopeNodeType, identifier: NewIdentifier): Unit =
      scopeMap.updateWith(key) {
        case Some(varMap: VarMap) =>
          Some(varMap.updatedWith(identifier.name) {
            case Some(varGroup: VarGroup) => Some(varGroup.copy(ids = varGroup.ids :+ identifier))
            case None                     => Some(identifier.toNewVarGroup)
          })
        case None =>
          Some(Map(identifier.name -> identifier.toNewVarGroup))
      }

    /** Will persist the variable groupings that do not represent parameter nodes and link them with REF edges.
      * @return
      *   the list of persisted local nodes.
      */
    def buildVariableGroupings(
      key: ScopeNodeType,
      paramNames: Set[String],
      diffGraph: DiffGraphBuilder
    ): List[DeclarationNew] =
      scopeMap.get(key) match
        case Some(varMap) =>
          varMap.values
            .filterNot { case VarGroup(local, _) => paramNames.contains(local.name) }
            .map { case VarGroup(local, ids) =>
              ids.foreach(id => diffGraph.addEdge(id, local, EdgeTypes.REF))
              local
            }
            .toList
        case None => List.empty[DeclarationNew]
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy