Skip to content

Commit

Permalink
a lot of symbol merge logic improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
matanox committed Dec 3, 2015
1 parent cf9369d commit d7244d8
Show file tree
Hide file tree
Showing 10 changed files with 193 additions and 102 deletions.
5 changes: 5 additions & 0 deletions buildAndRunOnProject.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
sbt +publishLocal
cd sbt-plugin-integration-test/target/scala-2.11/classes/integration-test-projects/pipeline
rc=$?; if [[ $rc != 0 ]]; then exit $rc; fi
echo "Running for target project..."
sbt canve > out.txt && subl out.txt
Original file line number Diff line number Diff line change
@@ -1,29 +1,41 @@
package org.canve.compilerPlugin
import tools.nsc.Global

/*
* types for whether a symbol is defined in
* the current project, or is an external one
*/

abstract class DefiningProject
object ProjectDefined extends DefiningProject
object ExternallyDefined extends DefiningProject

case class ExtractedSymbol
(symbolCompilerId: SymbolCompilerId,
name: String,
kind: String,
notSynthetic: Boolean,
qualifiedId: QualifiedID,

signatureString: Option[String] = None,

notSynthetic: Boolean,
definingProject: DefiningProject) extends SymbolSerialization {

var ownersTraversed = false
val qualifiedIdAndSignature: FurtherQualifiedID =
qualifiedId.pickle + (signatureString match {
case Some(s) => " * " + s
case None => ""
})

def definitionCode(implicit extractedModel: ExtractedModel): Option[Code] =
extractedModel.codes.get.get(symbolCompilerId)

/* more inclusive serialization for this class - for logging */
override def toString =
List(symbolCompilerId,
name,
kind,
qualifiedId,
signatureString,
notSynthetic,
definingProject,
qualifiedIdAndSignature).map(_.toString).mkString(",")

def toJoinedString(implicit extractedModel: ExtractedModel) = this.toString + definitionCode.toString
/* symbol and its code info joined into a string - for logging */
def toJoinedString(implicit extractedModel: ExtractedModel) = toString + ",code: " + definitionCode.toString

var ownersTraversed = false
}

/*
Expand All @@ -46,8 +58,18 @@ case class Span(start: Int, end: Int) extends Position
case class Point(init: Int) extends Position { def apply = init }

/*
* Simply a join of a symbol and its extracted code, wherever helpful
* types for whether a symbol is defined in
* the current project, or is an external one
*/

abstract class DefiningProject
object ProjectDefined extends DefiningProject
object ExternallyDefined extends DefiningProject

/*
* a lame join type of a symbol and its extracted code, wherever helpful
*/

case class SymbolCodeJoin(
extractedModel: ExtractedModel,
symbol: ExtractedSymbol) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,22 @@ class ExtractedModel(global: Global) extends ContainsExtractedGraph {
case _ => ProjectDefined
}


/*
* add the symbol to the extracted model
*/
val extractedSymbol = ExtractedSymbol(s.id, s.nameString, s.kindString, !(s.isSynthetic), qualifiedId, definingProject)
val extractedSymbol =
ExtractedSymbol(
symbolCompilerId = s.id,
name = s.nameString,
kind = s.kindString,
qualifiedId = qualifiedId,
notSynthetic = !(s.isSynthetic),
definingProject = definingProject,
signatureString = s.signatureString match {
case("<_>") => None
case signatureString@_ => Some(signatureString)
})

graph ++ graph.Vertex(extractedSymbol.symbolCompilerId, extractedSymbol)

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@ object Output {

def quote(int: Int) = "\"" + int.toString() + "\""

def write(model: ExtractedModel) = {
def write(implicit extractedModel: ExtractedModel) = {
Log(s"writing extracted type relations and call graph for project ${PluginArgs.projectName}...")

writeOutputFile(PluginArgs.projectName, "symbols",
"definition,notSynthetic,id,name,kind,qualifiedId\n" +
(model.graph.vertexIterator.map(_.data.toCsvRow)).mkString("\n"))
"definition,notSynthetic,id,name,kind,qualifiedId,signature\n" +
(extractedModel.graph.vertexIterator.map(_.data.toCsvRow)).mkString("\n"))

println("before output")
writeOutputFile(PluginArgs.projectName, "relations",
"id1,relation,id2\n" +
model.graph.edgeIterator.map ( e =>
extractedModel.graph.edgeIterator.map ( e =>
List(e.v1, e.data, e.v2).mkString(","))
.mkString("\n"))

model.codes.get.foreach { keyVal =>
extractedModel.codes.get.foreach { keyVal =>
val extractedCode = keyVal._2
if (extractedCode.code.isDefined)
writeOutputFile(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,21 @@ object QualifiedID {

assert(kindNameList.head.kind == "package")
assert(kindNameList.head.name == "<root>")


/*
* TODO: remove this commented out code
*
(s.kindString == "method") match {
case true =>
val paramListList: List[List[global.Symbol]] = s.paramss
QualifiedID(kindNameList.drop(1) ++ s.paramss.flatten.map(p => KindAndName(global)(p)))
case false =>
QualifiedID(kindNameList.drop(1))
}
*/

QualifiedID(kindNameList.drop(1))

}

def unpickle(s: String) =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package org.canve.compilerPlugin

/*
* helper for writing node data to csv
* Serialize extracted symbol into CSV data row
*/
trait SymbolSerialization {
self: ExtractedSymbol =>

def toCsvRow: String = {
def toCsvRow(implicit extractedModel: ExtractedModel): String = {
// Note: escaping for csv is for now handled here by hand (likely a tad faster)
List(
definingProject match {
case ProjectDefined => "project"
Expand All @@ -15,7 +16,8 @@ trait SymbolSerialization {
symbolCompilerId,
name,
kind,
qualifiedId.pickle
qualifiedId.pickle,
"\"" + signatureString + "\"" // escaped as it contains, typically, commas
).mkString(",")
}
}
Expand All @@ -24,28 +26,26 @@ trait SymbolSerialization {
* Symbol constructor from CANVE CSV data row
*/
object SymbolFromCsvRow {
import Util._
def apply(projectName: String, rowMap: Map[String, String]): ExtractedSymbol = {
ExtractedSymbol(symbolCompilerId = rowMap("id").toInt,
name = rowMap("name"),
kind = rowMap("kind"),
notSynthetic = rowMap("notSynthetic").toBoolean,
qualifiedId = QualifiedID.unpickle(rowMap("qualifiedId")),
signatureString = deSerializeOption(rowMap("signature")),
definingProject = rowMap("definition") match {
case "project" => ProjectDefined
case "external" => ExternallyDefined
})
}
}

/*
/*
* Symbol relation from CANVE CSV data row
*/
object SymbolRelationFromCsvRow {
def apply(rowMap: Map[String, String]) =
ExtractedSymbolRelation(
rowMap("id1").toInt,
rowMap("id2").toInt,
rowMap("relation"))
}
*/
object Util {
def deSerializeOption[T](s: String): Option[T] = {
s match {
case "None" => None
case s@_ => Some(s.drop("Some(".length).dropRight(1).asInstanceOf[T])
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,14 @@ object TraversalExtraction {

extractedModel.add(global)(select.symbol)

if (defParent.isDefined) extractedModel.add(defParent.get.id, "uses", select.symbol.id)

if (defParent.isDefined) {
extractedModel.add(defParent.get.id, "uses", select.symbol.id)
println
println(s"Method call. Symbol owner chain: ${select.symbol.ownerChain.reverse}, \nParams: ${select.symbol.paramss}")
println("signatureString: " + select.symbol.signatureString)
println
}

// record the source code location where the symbol is being used by the user
// this is a proof of concept, that only doesn't propagate its information
// to the UI in any way yet.
Expand Down Expand Up @@ -90,7 +96,11 @@ object TraversalExtraction {

extractedModel.add(global)(symbol)
extractedModel.addIfUnique(defParent.get.id, "declares member", symbol.id)

println
println(s"Method definition. Symbol owner chain: ${symbol.ownerChain.reverse}, \nParams: ${symbol.paramss}")
println("signatureString: " + symbol.signatureString)
println

val traverser = new ExtractionTraversal(Some(tree.symbol))
if (symbol.nameString == "get") {
//val tracer = new TraceTree
Expand All @@ -115,8 +125,15 @@ object TraversalExtraction {
if (defParent.get.id != typeSymbol.owner.id)
Warning.logParentNotOwner(global)(defParent.get, typeSymbol.owner)

parentTypeSymbols.foreach(s =>
extractedModel.add(typeSymbol.id, "extends", s.id))
parentTypeSymbols.foreach(s => {
extractedModel.add(typeSymbol.id, "extends", s.id)
println
println(s"Symbol ${typeSymbol.ownerChain.reverse}")
println(s"Performs Type extension of ${s.ownerChain.reverse}. TypeParams: ${s.typeParams}")
println("signatureString: " + s.signatureString)
println("typeSignature: " + s.typeSignature)
println
})

val traverser = new ExtractionTraversal(Some(tree.tpe.typeSymbol))
body foreach { tree => traverser.traverse(tree) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import org.canve.simpleGraph._
trait DataReader {

type LoadedGraph = SimpleGraph[SymbolCompilerId, ExtractedSymbol, ExtractedSymbolRelation]
type ReIndexedGraph = SimpleGraph[QualifiedID, ExtractedSymbol, ExtractedSymbolRelation]
type ReIndexedGraph = SimpleGraph[FurtherQualifiedID, ExtractedSymbol, ExtractedSymbolRelation]

/*
* builds canve nodes and edges from a canve data directory
* builds graph from a canve data directory
*/
def readCanveDirData(dir: File): ManagedExtractedGraph = {

Expand Down Expand Up @@ -62,9 +62,9 @@ object CrossProjectNormalizer extends DataReader {

projectGraphs.foreach { graph =>
graph.vertexIterator.foreach { v => val symbol = v.data
aggregateGraph.vertex(symbol.qualifiedId) match {
aggregateGraph.vertex(symbol.qualifiedIdAndSignature) match {

case None => aggregateGraph ++ aggregateGraph.Vertex(key = symbol.qualifiedId, data = symbol)
case None => aggregateGraph ++ aggregateGraph.Vertex(key = symbol.qualifiedIdAndSignature, data = symbol)

case Some(v) => maybeMerge(aggregateGraph, aggregateGraphSymbol = v.data, sameKeyedSymbol = symbol)

Expand All @@ -88,15 +88,15 @@ object CrossProjectNormalizer extends DataReader {
case (ExternallyDefined, ExternallyDefined) => // do nothing

case (ExternallyDefined, ProjectDefined) =>
aggregateGraph -- aggregateGraphSymbol.qualifiedId
aggregateGraph ++ aggregateGraph.Vertex(key = sameKeyedSymbol.qualifiedId, data = sameKeyedSymbol)
aggregateGraph -- aggregateGraphSymbol.qualifiedIdAndSignature
aggregateGraph ++ aggregateGraph.Vertex(key = sameKeyedSymbol.qualifiedIdAndSignature, data = sameKeyedSymbol)
println("deduplicated one node")

case (ProjectDefined, ProjectDefined) =>
case (ProjectDefined, ProjectDefined) =>
// TODO: This exception message doesn't help zoom in on the project names without logs excavation.
// Might be solved by adding the project name to each symbol being compared, somewhere before.
println(DataNormalizationException(
"Two subprojects seem to define the same qualified name:" +
"Two symbols seem to define the same qualified name:" +
s"\n$aggregateGraphSymbol and" +
s"\n$sameKeyedSymbol"))
}
Expand All @@ -110,9 +110,9 @@ object CrossProjectNormalizer extends DataReader {
return

println(DataNormalizationException(
s"""Two symbols having the same Qualified ID are unexpectedly different:
|$s1" +
|$s2" +
s"""Two symbols expected to be the same are unexpectedly different:
|$s1
|$s2
|This may indicate the normalization model is relying on a false assumption.""".stripMargin))
}
}
Loading

0 comments on commit d7244d8

Please sign in to comment.