Skip to content

Commit 680be40

Browse files
committed
Add :javap to the REPL
Adapted from the Scala 2 implementation, which was written by Paul Phillips and Som Snytt / A. P. Marki
1 parent fb6b453 commit 680be40

File tree

8 files changed

+1280
-2
lines changed

8 files changed

+1280
-2
lines changed

compiler/src/dotty/tools/dotc/config/PathResolver.scala

+41
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,47 @@ object PathResolver {
128128
)
129129
}
130130

131+
/** Locations discovered by supplemental heuristics.
132+
*/
133+
object SupplementalLocations {
134+
135+
/** The platform-specific support jar.
136+
*
137+
* Usually this is `tools.jar` in the jdk/lib directory of the platform distribution.
138+
*
139+
* The file location is determined by probing the lib directory under JDK_HOME or JAVA_HOME,
140+
* if one of those environment variables is set, then the lib directory under java.home,
141+
* and finally the lib directory under the parent of java.home. Or, as a last resort,
142+
* search deeply under those locations (except for the parent of java.home, on the notion
143+
* that if this is not a canonical installation, then that search would have little
144+
* chance of succeeding).
145+
*/
146+
def platformTools: Option[File] = {
147+
val jarName = "tools.jar"
148+
def jarPath(path: Path) = (path / "lib" / jarName).toFile
149+
def jarAt(path: Path) = {
150+
val f = jarPath(path)
151+
if (f.isFile) Some(f) else None
152+
}
153+
val jdkDir = {
154+
val d = Directory(jdkHome)
155+
if (d.isDirectory) Some(d) else None
156+
}
157+
def deeply(dir: Directory) = dir.deepFiles find (_.name == jarName)
158+
159+
val home = envOrSome("JDK_HOME", envOrNone("JAVA_HOME")) map (p => Path(p))
160+
val install = Some(Path(javaHome))
161+
162+
(home flatMap jarAt) orElse (install flatMap jarAt) orElse (install map (_.parent) flatMap jarAt) orElse
163+
(jdkDir flatMap deeply)
164+
}
165+
166+
override def toString = s"""
167+
|object SupplementalLocations {
168+
| platformTools = $platformTools
169+
|}""" //.asLines // TODO implement asLines or equivalent
170+
}
171+
131172
def fromPathString(path: String)(using Context): ClassPath = {
132173
val settings = ctx.settings.classpath.update(path)
133174
new PathResolver()(using ctx.fresh.setSettings(settings)).result

compiler/src/dotty/tools/dotc/config/Properties.scala

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ trait PropertiesTrait {
5353

5454
def envOrElse(name: String, alt: String): String = Option(System getenv name) getOrElse alt
5555
def envOrNone(name: String): Option[String] = Option(System getenv name)
56+
def envOrSome(name: String, alt: => Option[String]) = envOrNone(name) orElse alt
5657

5758
// for values based on propFilename
5859
def scalaPropOrElse(name: String, alt: String): String = scalaProps.getProperty(name, alt)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package dotty.tools.repl
2+
3+
import scala.reflect.{ ClassTag, classTag }
4+
import scala.util.control.Exception.catching
5+
6+
object ClassLoaderOps:
7+
extension (cl: ClassLoader)
8+
/** Create an instance with ctor args, or invoke errorFn before throwing. */
9+
def createInstance[T <: AnyRef : ClassTag](path: String, errorFn: String => Unit)(args: AnyRef*): T =
10+
def fail(msg: String) = error(msg, new IllegalArgumentException(msg))
11+
def error(msg: String, e: Throwable) = { errorFn(msg) ; throw e }
12+
try
13+
val clazz = Class.forName(path, /*initialize =*/ true, /*loader =*/ cl)
14+
if classTag[T].runtimeClass.isAssignableFrom(clazz) then
15+
val ctor =
16+
val maybes = clazz.getConstructors.filter(c =>
17+
c.getParameterCount == args.size
18+
&& (c.getParameterTypes zip args).forall { case (k, a) => k.isAssignableFrom(a.getClass) })
19+
if maybes.size == 1 then maybes.head
20+
else fail(s"Constructor must accept arg list (${args.map(_.getClass.getName).mkString(", ")}): ${path}")
21+
(ctor.newInstance(args*)).asInstanceOf[T]
22+
else
23+
// TODO show is undefined; in the original code, it is imported from
24+
// import scala.reflect.runtime.ReflectionUtils.show
25+
//errorFn(s"""Loader for ${classTag[T]}: [${show(classTag[T].runtimeClass.getClassLoader)}]
26+
// |Loader for ${clazz.getName}: [${show(clazz.getClassLoader)}]""".stripMargin)
27+
fail(s"Not a ${classTag[T]}: ${path}")
28+
catch
29+
case e: ClassNotFoundException =>
30+
error(s"Class not found: ${path}", e)
31+
case e @ (_: LinkageError | _: ReflectiveOperationException) =>
32+
error(s"Unable to create instance: ${path}: ${e.toString}", e)
33+
34+
/** Load and link a class with this classloader */
35+
def tryToLoadClass[T <: AnyRef](path: String): Option[Class[T]] =
36+
tryClass(path, initialize = false)
37+
38+
/** Load, link and initialize a class with this classloader */
39+
def tryToInitializeClass[T <: AnyRef](path: String): Option[Class[T]] =
40+
tryClass(path, initialize = true)
41+
42+
private def tryClass[T <: AnyRef](path: String, initialize: Boolean): Option[Class[T]] =
43+
catching(classOf[ClassNotFoundException], classOf[SecurityException]) opt
44+
Class.forName(path, initialize, cl).asInstanceOf[Class[T]]
45+
46+
/** The actual bytes for a class file, or an empty array if it can't be found. */
47+
def classBytes(className: String): Array[Byte] = classAsStream(className) match
48+
case null => Array()
49+
case stream => dotty.tools.io.Streamable.bytes(stream)
50+
51+
private inline def classAsStream(className: String) = cl.getResourceAsStream {
52+
if className.endsWith(".class") then className
53+
else s"${className.replace('.', '/')}.class" // classNameToPath
54+
}
55+
end ClassLoaderOps

0 commit comments

Comments
 (0)