Skip to content

Commit

Permalink
Merge pull request #10694 from scala/tasty/test-classpath-standalone-obj
Browse files Browse the repository at this point in the history
Align tasty file lookup with dotty, add test case
  • Loading branch information
SethTisue committed Feb 19, 2024
2 parents 232521f + 5936c32 commit ec6078f
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 3 deletions.
Expand Up @@ -334,7 +334,7 @@ case class DirectoryClassPath(dir: File) extends JFileDirectoryLookup[ClassFileE

protected def createFileEntry(file: AbstractFile): ClassFileEntryImpl = ClassFileEntryImpl(file)
protected def isMatchingFile(f: File, siblingExists: String => Boolean): Boolean =
f.isClass && !(f.getName.endsWith(".class") && siblingExists(f.getName.dropRight(6) + ".tasty"))
f.isClass && !(f.getName.endsWith(".class") && siblingExists(classNameToTasty(f.getName)))

private[nsc] def classes(inPackage: PackageName): Seq[ClassFileEntry] = files(inPackage)
}
Expand Down
25 changes: 25 additions & 0 deletions src/compiler/scala/tools/nsc/classpath/FileUtils.scala
Expand Up @@ -58,6 +58,31 @@ object FileUtils {

@inline private def ends (filename:String, suffix:String) = filename.endsWith(suffix) && filename.length > suffix.length

def classNameToTasty(fileName: String): String = {
// TODO [tasty]: Dotty really wants to special-case standalone objects
// i.e. their classfile will end with `$`, but the tasty file will not.
// however then it needs to escape `Null$`, `Nothing$`, and `$`
// because these are "legitimate" classes with `$` in their name.
// It seems its not actually necessary to drop these files,
// as the classfile parser will not complain about them,
// however, it could increase efficiency to follow dotty
// and drop them anyway.
// Scala 3 also prevents compilation of `object Foo` and `class Foo$` in the same package
// See test/tasty/run/src-2/tastytest/TestRuntimeSpecialClasses.scala for a test case
val isStandaloneObjectHeuristic = (
fileName.lastIndexOf('$') == fileName.length - 7
&& fileName != "Null$.class"
&& fileName != "Nothing$.class"
&& fileName != "$.class"
)
val className =
if (isStandaloneObjectHeuristic)
fileName.stripSuffix("$.class")
else
fileName.stripSuffix(".class")
className + SUFFIX_TASTY
}

def endsClass(fileName: String): Boolean =
ends (fileName, SUFFIX_CLASS) || fileName.endsWith(SUFFIX_SIG) || fileName.endsWith(SUFFIX_TASTY)

Expand Down
Expand Up @@ -50,5 +50,5 @@ case class VirtualDirectoryClassPath(dir: VirtualDirectory) extends ClassPath wi

protected def createFileEntry(file: AbstractFile): ClassFileEntryImpl = ClassFileEntryImpl(file)
protected def isMatchingFile(f: AbstractFile, siblingExists: String => Boolean): Boolean =
f.isClass && !(f.hasExtension("class") && siblingExists(f.name.dropRight(6) + ".tasty"))
f.isClass && !(f.hasExtension("class") && siblingExists(classNameToTasty(f.name)))
}
Expand Up @@ -74,7 +74,7 @@ object ZipAndJarClassPathFactory extends ZipAndJarFileLookupFactory {

override protected def createFileEntry(file: FileZipArchive#Entry): ClassFileEntryImpl = ClassFileEntryImpl(file)
override protected def isRequiredFileType(file: AbstractFile, siblingExists: String => Boolean): Boolean = {
file.isClass && !(file.hasExtension("class") && siblingExists(file.name.dropRight(6) + ".tasty"))
file.isClass && !(file.hasExtension("class") && siblingExists(classNameToTasty(file.name)))
}
}

Expand Down
10 changes: 10 additions & 0 deletions test/tasty/run/src-2/tastytest/TestRuntimeSpecialClasses.scala
@@ -0,0 +1,10 @@
package tastytest

object TestRuntimeSpecialClasses extends Suite("TestRuntimeSpecialClasses") {
test("consume standalone top-level object") {
assert(new tastytest.Null$().res.head === 23)
assert(new tastytest.Nothing$().res.head === 23)
assert($.res.head === 23)
assert(StandaloneObject.res.head === 23)
}
}
22 changes: 22 additions & 0 deletions test/tasty/run/src-3/tastytest/RuntimeSpecialClasses.scala
@@ -0,0 +1,22 @@
package tastytest

// makes Null$.class, and Null$.tasty
class Null$ {
def res: List[Int] = List(23)
}

// makes Nothing$.class, and Nothing$.tasty
class Nothing$ {
def res: List[Int] = List(23)

}

// makes $$.class, $.class, and $.tasty
object $ {
def res: List[Int] = List(23)
}

// makes StandaloneObject$.class, StandaloneObject.class, and StandaloneObject.tasty
object StandaloneObject {
def res: List[Int] = List(23)
}

0 comments on commit ec6078f

Please sign in to comment.