Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refix compact names w/o breaking local names #1259

Merged
merged 1 commit into from
Sep 26, 2023
Merged
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
@@ -0,0 +1,21 @@
package scala.reflect

import scala.tools.nsc.Global

// Gives access to `private[reflect] def compactifyName`
object ReflectAccess {
def flattenedOwner(g: Global)(sym: g.Symbol): g.Symbol = {
val chain = sym.owner.ownerChain.dropWhile(o => o.isClass && !o.hasPackageFlag)
if (chain.isEmpty) g.NoSymbol else chain.head
}

def flattenedName(g: Global)(sym: g.Symbol): g.Name = {
val owner = sym.owner
val prefix =
if (owner.isRoot || owner == g.NoSymbol || owner.hasPackageFlag) ""
else "" + flattenedName(g)(owner) + NameTransformer.NAME_JOIN_STRING
val flat = prefix + sym.rawname
val nameString = if (owner.isJava) flat else g.compactifyName(flat)
if (sym.isType) g.newTypeNameCached(nameString) else g.newTermNameCached(nameString)
}
}
44 changes: 21 additions & 23 deletions internal/compiler-bridge/src/main/scala/xsbt/API.scala
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ final class API(val global: CallbackGlobal) extends Compat with GlobalHelpers wi
* @param allClassSymbols The class symbols found in all the compilation units.
*/
def registerGeneratedClasses(classSymbols: Iterator[Symbol]): Unit = {
classSymbols.foreach { symbol =>
// Guard against a local class in case it surreptitiously leaks here
classSymbols.filter(!_.isLocalClass).foreach { symbol =>
val sourceFile = symbol.sourceFile
val sourceVF0 =
if (sourceFile == null) symbol.enclosingTopLevelClass.sourceFile
Expand All @@ -138,29 +139,26 @@ final class API(val global: CallbackGlobal) extends Compat with GlobalHelpers wi
}

def registerProductNames(names: FlattenedNames): Unit = {
// Guard against a local class in case it surreptitiously leaks here
if (!symbol.isLocalClass) {
val pathToClassFile = s"${names.binaryName}.class"
val classFile = {
JarUtils.outputJar match {
case Some(outputJar) =>
new java.io.File(JarUtils.classNameInJar(outputJar, pathToClassFile))
case None =>
val outputDir = global.settings.outputDirs.outputDirFor(sourceFile).file
new java.io.File(outputDir, pathToClassFile)
}
val pathToClassFile = s"${names.binaryName}.class"
val classFile = {
JarUtils.outputJar match {
case Some(outputJar) =>
new java.io.File(JarUtils.classNameInJar(outputJar, pathToClassFile))
case None =>
val outputDir = global.settings.outputDirs.outputDirFor(sourceFile).file
new java.io.File(outputDir, pathToClassFile)
}
val zincClassName = names.className
val srcClassName = classNameAsString(symbol)
sourceVF foreach { source =>
callback.generatedNonLocalClass(
source,
classFile.toPath,
zincClassName,
srcClassName
)
}
} else ()
}
val zincClassName = names.className
val srcClassName = classNameAsString(symbol)
sourceVF foreach { source =>
callback.generatedNonLocalClass(
source,
classFile.toPath,
zincClassName,
srcClassName
)
}
}

val names = FlattenedNames(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import io.AbstractFile
import java.nio.file.{ Files, Path }

import scala.reflect.io.PlainFile
import scala.reflect.ReflectAccess._

/** Defines the interface of the incremental compiler hiding implementation details. */
sealed abstract class CallbackGlobal(
Expand Down Expand Up @@ -225,19 +226,19 @@ sealed class ZincCompiler(settings: Settings, dreporter: DelegatingReporter, out
): String = {
var b: java.lang.StringBuffer = null
def loop(size: Int, sym: Symbol): Unit = {
val symName = sym.name
val symName = flattenedName(this)(sym)
// Use of encoded to produce correct paths for names that have symbols
val encodedName = symName.encode
val nSize = encodedName.length - (if (symName.endsWith(nme.LOCAL_SUFFIX_STRING)) 1 else 0)
if (sym.isRoot || sym.isRootPackage || sym == NoSymbol || sym.owner.isEffectiveRoot) {
val owner = flattenedOwner(this)(sym)
if (sym.isRoot || sym.isRootPackage || sym == NoSymbol || owner.isEffectiveRoot) {
val capacity = size + nSize
b = new java.lang.StringBuffer(capacity)
b.append(chrs, encodedName.start, nSize)
} else {
val next = if (sym.owner.isPackageObjectClass) sym.owner else sym.effectiveOwner.enclClass
val next = if (owner.isPackageObjectClass) owner else owner.skipPackageObject
loop(size + nSize + 1, next)
// Addition to normal `fullName` to produce correct names for nested non-local classes
if (sym.isNestedClass) b.append(nme.MODULE_SUFFIX_STRING) else b.append(separator)
if (owner.isPackageObjectClass) b.append(nme.MODULE_SUFFIX_STRING) else b.append(separator)
b.append(chrs, encodedName.start, nSize)
}
()
Expand Down
26 changes: 26 additions & 0 deletions internal/compiler-bridge/src/main/scala/xsbt/trace.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package xsbt

object trace extends TraceSyntax {
object disable extends TraceSyntax {
override def apply[T](q: String)(op: => T): T = op
}
object enable extends TraceSyntax
}

object TraceSyntax {
private var indent = 0
}
trait TraceSyntax {
import TraceSyntax._
def apply[T](q: String)(op: => T): T = {
val margin = " " * indent
println(s"$margin$q?")
val res =
try {
indent += 1
op
} finally indent -= 1
println(s"$margin$q = $res")
res
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,9 @@ class IncHandler(directory: Path, cacheDir: Path, scriptedLog: ManagedLogger, co
onArgs("checkProducts") {
case (p, src :: products, i) => p.checkProducts(i, dropRightColon(src), products)
},
onArgs("checkProductsExists") {
case (p, src :: Nil, i) => p.checkProductsExist(i, src)
},
onArgs("checkDependencies") {
case (p, cls :: dependencies, i) => p.checkDependencies(i, dropRightColon(cls), dependencies)
},
Expand Down Expand Up @@ -441,28 +444,32 @@ case class ProjectStructure(
()
}

def getProducts(analysis: Analysis)(srcFile: String): Set[VirtualFileRef] =
analysis.relations.products(converter.toVirtualFile(baseDirectory / srcFile))

def checkProducts(i: IncState, src: String, expected: List[String]): Future[Unit] =
compile(i).map { analysis =>
// def isWindows: Boolean = sys.props("os.name").toLowerCase.startsWith("win")
// def relativeClassDir(f: File): File = f.relativeTo(classesDir) getOrElse f
// def normalizePath(path: String): String = {
// if (isWindows) path.replace('\\', '/') else path
// }
def products(srcFile: String): Set[String] = {
val productFiles = analysis.relations.products(converter.toVirtualFile(baseDirectory / src))
productFiles.map { file: VirtualFileRef =>
// if (JarUtils.isClassInJar(file)) {
// JarUtils.ClassInJar.fromPath(output.toPath).toClassFilePath
// } else {
// normalizePath(relativeClassDir(file).getPath)
// }
file.id
}
}
def assertClasses(expected: Set[String], actual: Set[String]) =
assert(expected == actual, s"Expected $expected products, got $actual")
assert(
expected == actual,
s"""Mismatch:
|Expected:${expected.toList.sorted.map("\n " + _).mkString}
|Obtained:${actual.toList.sorted.map("\n " + _).mkString}""".stripMargin
)

assertClasses(expected.toSet, getProducts(analysis)(src).map(_.id))
()
}

assertClasses(expected.toSet, products(src))
def checkProductsExist(i: IncState, src: String): Future[Unit] =
compile(i).map { analysis =>
val missing =
for (p <- getProducts(analysis)(src).toList if !Files.exists(converter.toPath(p))) yield p
assert(
missing.isEmpty,
s"""Missing ${missing.size} products:${missing.map("\n " + _).mkString}
|Generated:${generatedClassFiles.get.toList.map("\n " + _).mkString}""".stripMargin
)
()
}

Expand Down Expand Up @@ -829,6 +836,14 @@ case class ProjectStructure(
}
()
}

def assertShort(assertion: Boolean, message: => Any) = {
dwijnand marked this conversation as resolved.
Show resolved Hide resolved
if (!assertion) {
val err = new AssertionError("assertion failed: " + message)
err.setStackTrace(Array())
throw err
}
}
}

object IncHandler {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package p1.p2

abstract class B {
def m(): L
abstract class L {
def i: I
abstract class I
}
}
class D extends B { override def m() = new L { override case object i extends I } }
class G extends B { override def m() = new L { override case object i extends I } }
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
> checkProducts main.scala: ${BASE}/target/classes/p1/p2/B$L$I.class ${BASE}/target/classes/p1/p2/B$L.class ${BASE}/target/classes/p1/p2/B.class ${BASE}/target/classes/p1/p2/D$$anon$1$i$.class ${BASE}/target/classes/p1/p2/D$$anon$1.class ${BASE}/target/classes/p1/p2/D.class ${BASE}/target/classes/p1/p2/G$$anon$2$i$.class ${BASE}/target/classes/p1/p2/G$$anon$2.class ${BASE}/target/classes/p1/p2/G.class
> checkProductsExists main.scala
15 changes: 15 additions & 0 deletions zinc/src/sbt-test/source-dependencies/compactify-nested/main.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package p1.p2

class OuterLevelWithVeryVeryVeryLongClassName1 {
class OuterLevelWithVeryVeryVeryLongClassName2 {
class OuterLevelWithVeryVeryVeryLongClassName3 {
class OuterLevelWithVeryVeryVeryLongClassName4 {
class OuterLevelWithVeryVeryVeryLongClassName5 {
class OuterLevelWithVeryVeryVeryLongClassName6 {
class OuterLevelWithVeryVeryVeryLongClassName7
}
}
}
}
}
}
2 changes: 2 additions & 0 deletions zinc/src/sbt-test/source-dependencies/compactify-nested/test
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
> checkProducts main.scala: ${BASE}/target/classes/p1/p2/OuterLevelWithVeryVeryVeryLongClassName1.class ${BASE}/target/classes/p1/p2/OuterLevelWithVeryVeryVeryLongClassName1$OuterLevelWithVeryVeryVeryLongClassName2.class ${BASE}/target/classes/p1/p2/OuterLevelWithVeryVeryVeryLongClassName1$OuterLevelWithVeryVeryVeryLongClassName2$OuterLevelWithVeryVeryVeryLongClassName3.class ${BASE}/target/classes/p1/p2/OuterLevelWithVeryVeryVeryLongClassName1$OuterLevelWithVeryVeryVeryLongClassName2$OuterLevelWithVeryVeryVeryLongClassName3$OuterLevelWithVeryVeryVeryLongClassName4.class ${BASE}/target/classes/p1/p2/OuterLevelWithVeryVeryVeryLongClassName1$OuterLevelWithVeryVeryVeryLongClassName2$OuterLevelWithVeryVeryVeryLongClassName3$OuterLevelWithVeryVeryVeryLongClassName4$OuterLevelWithVeryVeryVeryLongClassName5.class ${BASE}/target/classes/p1/p2/OuterLevelWithVeryVeryVeryLongClassName1$OuterLevelWithVeryVeryVeryLongClassName2$OuterLevelWithVeryVeryVeryLongClassName3$OuterLevelWithVeryVeryVeryLongClassName4$OuterLevelWithVeryVeryVeryLongClassName5$OuterLevelWithVeryVeryVeryLongClassName6.class ${BASE}/target/classes/p1/p2/OuterLevelWithVeryVeryVeryLongClassName1$OuterLevelWithVeryVe$$$$6facba931fe42f8a8c3cee88c4087$$$$ryVeryLongClassName6$OuterLevelWithVeryVeryVeryLongClassName7.class
> checkProductsExists main.scala