All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.joern.rubysrc2cpg.datastructures.RubyProgramSummary.scala Maven / Gradle / Ivy
package io.joern.rubysrc2cpg.datastructures
import better.files.File
import io.joern.x2cpg.Defines as XDefines
import io.joern.x2cpg.datastructures.{FieldLike, MethodLike, ProgramSummary, StubbedType, TypeLike}
import io.joern.x2cpg.typestub.{TypeStubMetaData, TypeStubUtil}
import org.slf4j.LoggerFactory
import io.joern.rubysrc2cpg.passes.Defines
import upickle.default.*
import java.io.{ByteArrayInputStream, InputStream}
import java.util.zip.ZipInputStream
import scala.annotation.targetName
import scala.collection.mutable
import scala.collection.mutable.ListBuffer
import scala.util.{Failure, Success, Try}
type NamespaceToTypeMap = mutable.Map[String, mutable.Set[RubyType]]
class RubyProgramSummary(
initialNamespaceMap: NamespaceToTypeMap = mutable.Map.empty,
initialPathMap: NamespaceToTypeMap = mutable.Map.empty
) extends ProgramSummary[RubyType, RubyMethod, RubyField] {
override val namespaceToType: NamespaceToTypeMap = initialNamespaceMap
val pathToType: NamespaceToTypeMap = initialPathMap
@targetName("appendAll")
def ++=(other: RubyProgramSummary): RubyProgramSummary = {
RubyProgramSummary(
ProgramSummary.merge(this.namespaceToType, other.namespaceToType),
ProgramSummary.merge(this.pathToType, other.pathToType)
)
}
}
object RubyProgramSummary {
private val logger = LoggerFactory.getLogger(getClass)
def BuiltinTypes(implicit typeStubMetaData: TypeStubMetaData): NamespaceToTypeMap = {
val typeStubDir = File(typeStubMetaData.packagePath)
if (!typeStubDir.exists || !typeStubDir.isDirectory) {
logger.warn("No builtin type stubs provided, continuing with types provided by the project")
mutable.Map.empty
} else if (typeStubMetaData.useTypeStubs) {
mpkZipToInitialMapping(mergeBuiltinMpkZip) match {
case Failure(exception) => logger.warn("Unable to parse builtin types", exception); mutable.Map.empty
case Success(mapping) => mapping
}
} else {
mutable.Map.empty
}
}
private def mpkZipToInitialMapping(inputStream: InputStream): Try[NamespaceToTypeMap] = {
Try(readBinary[NamespaceToTypeMap](inputStream.readAllBytes()))
}
private def mergeBuiltinMpkZip(implicit typeStubMetaData: TypeStubMetaData): InputStream = {
val classLoader = getClass.getClassLoader
val typeStubDir = TypeStubUtil.typeStubDir
val typeStubFiles: Seq[File] =
typeStubDir
.walk()
.filter(f => f.isRegularFile && f.name.startsWith("rubysrc") && f.`extension`.contains(".zip"))
.toSeq
if (typeStubFiles.isEmpty) {
logger.warn("No ZIP files found.")
InputStream.nullInputStream()
} else {
val mergedMpksObj = ListBuffer[collection.mutable.Map[String, Set[RubyStubbedType]]]()
typeStubFiles.foreach { f =>
f.fileInputStream { fis =>
val zis = new ZipInputStream(fis)
LazyList.continually(zis.getNextEntry).takeWhile(_ != null).foreach { file =>
val mpkObj =
upickle.default.readBinary[collection.mutable.Map[String, Set[RubyStubbedType]]](zis.readAllBytes())
mergedMpksObj.addOne(mpkObj)
}
}
}
val mergedMpks = mergedMpksObj
.reduceOption((prev, curr) => {
curr.keys.foreach(key => {
prev.updateWith(key) {
case Some(x) =>
Option(x ++ curr(key))
case None =>
Option(curr(key))
}
})
prev
})
.getOrElse(collection.mutable.Map[String, Set[RubyStubbedType]]())
new ByteArrayInputStream(upickle.default.writeBinary(mergedMpks))
}
}
}
case class RubyMethod(
name: String,
parameterTypes: List[(String, String)],
returnType: String,
baseTypeFullName: Option[String]
) extends MethodLike
object RubyMethod {
implicit val rubyMethodRwJson: ReadWriter[RubyMethod] = readwriter[ujson.Value].bimap[RubyMethod](
x => ujson.Obj("name" -> x.name),
json =>
RubyMethod(
name = json("name").str,
parameterTypes = List.empty,
returnType = XDefines.Any,
baseTypeFullName = Option(json("name").str.split("\\.").dropRight(1).mkString("."))
)
)
}
case class RubyField(name: String, typeName: String) extends FieldLike derives ReadWriter
class RubyStubbedType(name: String, methods: List[RubyMethod], fields: List[RubyField])
extends RubyType(name, methods, fields)
with StubbedType[RubyMethod, RubyField]
object RubyStubbedType {
implicit val rubyTypeRw: ReadWriter[RubyStubbedType] = readwriter[ujson.Value].bimap[RubyStubbedType](
x =>
ujson.Obj(
"name" -> x.name,
"methods" -> x.methods.map { method =>
ujson.Obj("name" -> method.name)
},
"fields" -> x.fields.map { field => write[RubyField](field) }
),
json =>
RubyStubbedType(
name = json("name").str,
methods = json.obj.get("methods") match {
case Some(jsonMethods) =>
val methodsList = read[List[RubyMethod]](jsonMethods)
methodsList.map { func =>
val baseTypeFullName = json("name").str
func.copy(name = func.name, baseTypeFullName = Option(baseTypeFullName))
}
case None => Nil
},
fields = json.obj.get("fields").map(read[List[RubyField]](_)).getOrElse(Nil)
)
)
}
case class RubyType(name: String, methods: List[RubyMethod], fields: List[RubyField])
extends TypeLike[RubyMethod, RubyField] {
@targetName("add")
override def +(o: TypeLike[RubyMethod, RubyField]): TypeLike[RubyMethod, RubyField] = {
this.copy(methods = mergeMethods(o), fields = mergeFields(o))
}
def hasConstructor: Boolean = {
methods.exists(_.name == Defines.Initialize)
}
}
object RubyType {
implicit val rubyTypeRw: ReadWriter[RubyType] = readwriter[ujson.Value].bimap[RubyType](
x =>
ujson.Obj(
"name" -> x.name,
"methods" -> x.methods.map { method =>
ujson.Obj("name" -> method.name)
},
"fields" -> x.fields.map { field => write[RubyField](field) }
),
json =>
RubyType(
name = json("name").str,
methods = json.obj.get("methods") match {
case Some(jsonMethods) =>
val methodsList = read[List[RubyMethod]](jsonMethods)
methodsList.map { func =>
val splitName = func.name.split("\\.")
val baseTypeFullName = splitName.dropRight(1).mkString(".")
func.copy(name = func.name, baseTypeFullName = Option(baseTypeFullName))
}
case None => Nil
},
fields = json.obj.get("fields").map(read[List[RubyField]](_)).getOrElse(Nil)
)
)
}