Skip to content

Fix #8215 Add ability to detect attempted Tasty inspection of a Java class #8220

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

Merged
merged 7 commits into from
Feb 11, 2020
Merged
Show file tree
Hide file tree
Changes from 4 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
13 changes: 13 additions & 0 deletions compiler/src/dotty/tools/dotc/fromtasty/JavaCompilationUnit.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package dotty.tools.dotc.fromtasty

import dotty.tools.dotc.CompilationUnit
import dotty.tools.dotc.util.NoSource

/**
* A marker CompilationUnit to return up the call stack from ReadTasty. This will tell us that we've
* encountered, and attempted to inspect, a Java class file. We can't TASTy-inspect a Java class obviously,
* but we want to return the fact we found it so that higher-up we can take appropriate action if desired.
*/
class JavaCompilationUnit(val className: String) extends CompilationUnit(NoSource) {
override def toString: String = s"Java class file $className"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package dotty.tools.dotc.fromtasty

import dotty.tools.dotc.CompilationUnit
import dotty.tools.dotc.util.NoSource

/**
* A marker CompilationUnit to return up the call stack from ReadTasty. This will tell us that we've
* encountered, and attempted to inspect, a non-Tasty Scala class file (for example a legacy class pre-Scala 3).
* In this case we still want to return the fact we found it so that higher-up we can take appropriate
* action if desired.
*/
class NonTastyScalaCompilationUnit(val className: String) extends CompilationUnit(NoSource) {
override def toString: String = s"Non-Tasty Scala class file $className"
}
6 changes: 5 additions & 1 deletion compiler/src/dotty/tools/dotc/fromtasty/ReadTasty.scala
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@ class ReadTasty extends Phase {
Some(unit)
}
case tree: Tree[?] =>
alreadyLoaded()
cls.denot.infoOrCompleter match {
case _: NoLoader => Some(NonTastyScalaCompilationUnit(cls.fullName.toString))
case _ if cls.flags.is(Flags.JavaDefined) => Some(JavaCompilationUnit(cls.fullName.toString))
case _ => alreadyLoaded()
}
case _ =>
cannotUnpickle(s"its class file does not have a TASTY attribute")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,16 @@ class ReflectionCompilerInterface(val rootContext: core.Contexts.Context) extend
def Context_requiredClass(self: Context)(path: String): Symbol = self.requiredClass(path)
def Context_requiredModule(self: Context)(path: String): Symbol = self.requiredModule(path)
def Context_requiredMethod(self: Context)(path: String): Symbol = self.requiredMethod(path)
def Context_javaCompilationUnitClassname(self: Context): Option[String] =
self.compilationUnit match {
case j: fromtasty.JavaCompilationUnit => Some(j.className)
case _ => None
}
def Context_nonTastyScalaCompilationUnitClassname(self: Context): Option[String] =
self.compilationUnit match {
case s: fromtasty.NonTastyScalaCompilationUnit => Some(s.className)
case _ => None
}


///////////////
Expand Down
5 changes: 5 additions & 0 deletions library/src/scala/tasty/Reflection.scala
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,11 @@ class Reflection(private[scala] val internal: CompilerInterface) { self =>
/** Get method symbol if method is either defined in current compilation run or present on classpath. Throws if the method has an overload. */
def requiredMethod(path: String): Symbol = internal.Context_requiredMethod(self)(path)

/** Get Java class name if we've accidentally tried to reflect on a Java class. None returned if TASTy class. */
def javaCompilationUnitClassname(): Option[String] = internal.Context_javaCompilationUnitClassname(self)

/** Get Scala class name if attempted reflection is performed on an older Scala file w/o Tasty information present. */
def nonTastyScalaCompilationUnitClassname(): Option[String] = internal.Context_nonTastyScalaCompilationUnitClassname(self)
}


Expand Down
6 changes: 6 additions & 0 deletions library/src/scala/tasty/reflect/CompilerInterface.scala
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,12 @@ trait CompilerInterface {
/** Get method symbol if method is either defined in current compilation run or present on classpath. Throws if the method has an overload. */
def Context_requiredMethod(self: Context)(path: String): Symbol

/** Get Java class name if we've accidentally tried to reflect on a Java class. None returned if TASTy class. */
def Context_javaCompilationUnitClassname(self: Context): Option[String]

/** Get Scala class name if attempted reflection is performed on an older Scala file w/o Tasty information present. */
def Context_nonTastyScalaCompilationUnitClassname(self: Context): Option[String]


///////////////
// REPORTING //
Expand Down
37 changes: 37 additions & 0 deletions tests/run-custom-args/tasty-inspector/i8215.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import scala.tasty.Reflection
import scala.tasty.inspector._

case class I8215(id: String)

object Test {
def main(args: Array[String]): Unit = {

// Tasty Scala Class
val inspect1 = new TestInspector()
inspect1.inspect("", List("I8215"))
assert(inspect1.gotJava == None)
assert(inspect1.gotNonTastyScala == None)

// Java Class
val inspect2 = new TestInspector()
inspect2.inspect("", List("java.util.UUID"))
assert(inspect2.gotJava == Some("java.util.UUID"))
assert(inspect2.gotNonTastyScala == None)

// Legacy non-Tasty Scala class
val inspect3 = new TestInspector()
inspect3.inspect("", List("scala.collection.immutable.RedBlackTree"))
assert(inspect3.gotJava == None)
assert(inspect3.gotNonTastyScala == Some("scala.collection.immutable.RedBlackTree"))
}
}

class TestInspector() extends TastyInspector

var gotJava: Option[String] = None
var gotNonTastyScala: Option[String] = None

protected def processCompilationUnit(reflect: Reflection)(root: reflect.Tree): Unit =
import reflect.{given,_}
gotJava = reflect.rootContext.javaCompilationUnitClassname()
gotNonTastyScala = reflect.rootContext.nonTastyScalaCompilationUnitClassname()