8000 -Yopen-packages opens packages in jrt under --release · scala/scala@bee5534 · GitHub
[go: up one dir, main page]

Skip to content

Commit bee5534

Browse files
committed
-Yopen-packages opens packages in jrt under --release
1 parent c62ace5 commit bee5534

File tree

8 files changed

+96
-35
lines changed

8 files changed

+96
-35
lines changed

src/compiler/scala/tools/nsc/Global.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,10 @@ class Global(var currentSettings: Settings, reporter0: Reporter)
146146
def optimizerClassPath(base: ClassPath): ClassPath =
147147
base match {
148148
case AggregateClassPath(entries) if entries.head.isInstanceOf[CtSymClassPath] =>
149-
JrtClassPath(release = None, closeableRegistry)
150-
.map(jrt => AggregateClassPath(entries.drop(1).prepended(jrt)))
151-
.getOrElse(base)
149+
JrtClassPath(release = None, unsafe = None, closeableRegistry) match {
150+
case jrt :: Nil => AggregateClassPath(entries.drop(1).prepended(jrt))
151+
case _ => base
152+
}
152153
case _ => base
153154
}
154155

src/compiler/scala/tools/nsc/classpath/DirectoryClassPath.scala

Lines changed: 51 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,16 @@ package scala.tools.nsc.classpath
1414

1515
import java.io.{Closeable, File}
1616
import java.net.{URI, URL}
17+
import java.nio.file._
1718

18-
import scala.reflect.io.{AbstractFile, PlainFile, PlainNioFile}
19-
import scala.tools.nsc.util.{ClassPath, ClassRepresentation, EfficientClassPath}
20-
import FileUtils._
2119
import scala.jdk.CollectionConverters._
2220
import scala.reflect.internal.JDK9Reflectors
21+
import scala.reflect.io.{AbstractFile, PlainFile, PlainNioFile}
2322
import scala.tools.nsc.CloseableRegistry
2423
import scala.tools.nsc.classpath.PackageNameUtils.{packageContains, separatePkgAndClassNames}
24+
import scala.tools.nsc.util.{ClassPath, ClassRepresentation, EfficientClassPath}
25+
import scala.util.Properties.{isJavaAtLeast, javaHome}
26+
import FileUtils._
2527

2628
/**
2729
* A trait allowing to look for classpath entries in directories. It provides common logic for
@@ -129,12 +131,10 @@ trait JFileDirectoryLookup[FileEntryType <: ClassRepresentation] extends Directo
129131
}
130132

131133
object JrtClassPath {
132-
import java.nio.file._, java.net.URI
133134
private val jrtClassPathCache = new FileBasedCache[Unit, JrtClassPath]()
134135
private val ctSymClassPathCache = new FileBasedCache[String, CtSymClassPath]()
135-
def apply(release: Option[String], closeableRegistry: CloseableRegistry): Option[ClassPath] = {
136-
import scala.util.Properties._
137-
if (!isJavaAtLeast("9")) None
136+
def apply(release: Option[String], unsafe: Option[List[String]], closeableRegistry: CloseableRegistry): List[ClassPath] =
137+
if (!isJavaAtLeast("9")) Nil
138138
else {
139139
// TODO escalate errors once we're sure they are fatal
140140
// I'm hesitant to do this immediately, because -release will still work for multi-release JARs
@@ -145,28 +145,52 @@ object JrtClassPath {
145145

146146
val currentMajorVersion: Int = JDK9Reflectors.runtimeVersionMajor(JDK9Reflectors.runtimeVersion()).intValue()
147147
release match {
148-
case Some(v) if v.toInt < currentMajorVersion =>
149-
try {
150-
val ctSym = Paths.get(javaHome).resolve("lib").resolve("ct.sym")
151-
if (Files.notExists(ctSym)) None
152-
else {
153-
val classPath = ctSymClassPathCache.getOrCreate(v, ctSym :: Nil, () => new CtSymClassPath(ctSym, v.toInt), closeableRegistry, true)
154-
Some(classPath)
155-
}
156-
} catch {
157-
case _: Throwable => None
148+
case Some(version) if version.toInt < currentMajorVersion =>
149+
val ct = createCt(version, closeableRegistry)
150+
unsafe match {
151+
case Some(pkgs) if pkgs.nonEmpty =>
152+
createJrt(closeableRegistry) match {
153+
case Nil => ct
154+
case jrts => ct.appended(new FilteringJrtClassPath(jrts.head, pkgs: _*))
155+
}
156+
case _ => ct
158157
}
159158
case _ =>
160-
try {
161-
val fs = FileSystems.getFileSystem(URI.create("jrt:/"))
162-
val classPath = jrtClassPathCache.getOrCreate((), Nil, () => new JrtClassPath(fs), closeableRegistry, false)
163-
Some(classPath)
164-
} catch {
165-
case _: ProviderNotFoundException | _: FileSystemNotFoundException => None
166-
}
159+
createJrt(closeableRegistry)
167160
}
168161
}
169-
}
162+
private def createCt(v: String, closeableRegistry: CloseableRegistry): List[ClassPath] =
163+
try {
164+
val ctSym = Paths.get(javaHome).resolve("lib").resolve("ct.sym")
165+
if (Files.notExists(ctSym)) Nil
166+
else {
167+
val classPath = ctSymClassPathCache.getOrCreate(v, ctSym :: Nil, () => new CtSymClassPath(ctSym, v.toInt), closeableRegistry, checkStamps=true)
168+
List(classPath)
169+
}
170+
} catch {
171+
case _: Throwable => Nil
172+
}
173+
private def createJrt(closeableRegistry: CloseableRegistry): List[JrtClassPath] =
174+
try {
175+
val fs = FileSystems.getFileSystem(URI.create("jrt:/"))
176+
val classPath = jrtClassPathCache.getOrCreate((), Nil, () => new JrtClassPath(fs), closeableRegistry, checkStamps=false)
177+
List(classPath)
178+
} catch {
179+
case _: ProviderNotFoundException | _: FileSystemNotFoundException => Nil
180+
}
181+
}
182+
183+
final class FilteringJrtClassPath(delegate: JrtClassPath, allowed: String*) extends ClassPath with NoSourcePaths {
184+
private val allowedPackages = allowed
185+
private def packagePrefix(p: String, q: String) = p.startsWith(q) && (p.length == q.length || p.charAt(q.length) == '.')
186+
private def ok(pkg: PackageName) = pkg.dottedString.isEmpty || allowedPackages.exists(packagePrefix(_, pkg.dottedString))
187+
def asClassPathStrings: Seq[String] = delegate.asClassPathStrings
188+
def asURLs: Seq[java.net.URL] = delegate.asURLs
189+
private[nsc] def classes(inPackage: PackageName) = if (ok(inPackage)) delegate.classes(inPackage) else Nil
190+
def findClassFile(className: String) = if (ok(PackageName(separatePkgAndClassNames(className)._1))) delegate.findClassFile(className) else None
191+
private[nsc] def hasPackage(pkg: PackageName) = ok(pkg) && delegate.hasPackage(pkg)
192+
private[nsc] def list(inPackage: PackageName) = if (ok(inPackage)) delegate.list(inPackage) else ClassPathEntries(Nil, Nil)
193+
private[nsc] def packages(inPackage: PackageName) = if (ok(inPackage)) delegate.packages(inPackage) else Nil
170194
}
171195

172196
/**
@@ -177,8 +201,7 @@ object JrtClassPath {
177201
*
178202
* The implementation assumes that no classes exist in the empty package.
179203
*/
180-
final class JrtClassPath(fs: java.nio.file.FileSystem) extends ClassPath with NoSourcePaths {
181-
import java.nio.file.Path, java.nio.file._
204+
final class JrtClassPath(fs: FileSystem) extends ClassPath with NoSourcePaths {
182205
type F = Path
183206
private val dir: Path = fs.getPath("/packages")
184207

@@ -246,7 +269,7 @@ final class CtSymClassPath(ctSym: java.nio.file.Path, release: Int) extends Clas
246269
// e.g. "java.lang" -> Seq(/876/java/lang, /87/java/lang, /8/java/lang))
247270
private val packageIndex: scala.collection.Map[String, scala.collection.Seq[Path]] = {
248271
val index = collection.mutable.AnyRefMap[String, collection.mutable.ListBuffer[Path]]()
249-
val isJava12OrHigher = scala.util.Properties.isJavaAtLeast("12")
272+
val isJava12OrHigher = isJavaAtLeast("12")
250273
rootsForRelease.foreach(root => Files.walk(root).iterator().asScala.filter(Files.isDirectory(_)).foreach { p =>
251274
val moduleNamePathElementCount = if (isJava12OrHigher) 1 else 0
252275
if (p.getNameCount > root.getNameCount + moduleNamePathElementCount) {

src/compiler/scala/tools/nsc/settings/ScalaSettings.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ trait ScalaSettings extends StandardScalaSettings with Warnings { _: MutableSett
264264

265265
val Youtline = BooleanSetting ("-Youtline", "Don't compile method bodies. Use together with `-Ystop-after:pickler` to generate the pickled signatures for all source files.").internalOnly()
266266

267+
val unsafe = MultiStringSetting("-Yopen-packages", "packages", "Expose platform packages under --release")
267268
val exposeEmptyPackage = BooleanSetting ("-Yexpose-empty-package", "Internal only: expose the empty package.").internalOnly()
268269
val Ydelambdafy = ChoiceSetting ("-Ydelambdafy", "strategy", "Strategy used for translating lambdas into JVM code.", List("inline", "method"), "method")
269270

src/compiler/scala/tools/util/PathResolver.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -257,8 +257,9 @@ final class PathResolver(settings: Settings, closeableRegistry: CloseableRegistr
257257

258258
// Assemble the elements!
259259
def basis = List[Iterable[ClassPath]](
260-
jrt // 0. The Java 9+ classpath (backed by the ct.sym or jrt:/ virtual system, if available)
261-
.filter(_ => !settings.javabootclasspath.isSetByUser), // respect explicit `-javabootclasspath rt.jar`
260+
if (settings.javabootclasspath.isSetByUser) // respect explicit `-javabootclasspath rt.jar`
261+
Nil
262+
else jrt, // 0. The Java 9+ classpath (backed by the ct.sym or jrt:/ virtual system, if available)
262263
classesInPath(javaBootClassPath), // 1. The Java bootstrap class path.
263264
contentsOfDirsInPath(javaExtDirs), // 2. The Java extension class path.
264265
classesInExpandedPath(javaUserClassPath), // 3. The Java application class path.
@@ -269,7 +270,7 @@ final class PathResolver(settings: Settings, closeableRegistry: CloseableRegistr
269270
sourcesInPath(sourcePath) // 7. The Scala source path.
270271
)
271272

272-
private def jrt: Option[ClassPath] = JrtClassPath.apply(settings.releaseValue, closeableRegistry)
273+
private def jrt: List[ClassPath] = JrtClassPath.apply(settings.releaseValue, settings.unsafe.valueSetByUser, closeableRegistry)
273274

274275
lazy val containers = basis.flatten.distinct
275276

test/files/neg/unsafe.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
unsafe.scala:9: error: value threadId is not a member of Thread
2+
def f(t: Thread) = t.threadId
3+
^
4+
1 error

test/files/neg/unsafe.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
// scalac: --release:8 -Yopen-packages:java.lang
3+
// javaVersion: 19+
4+
5+
// -Yunsafe opens packages but does not override class definitions
6+
// because ct.sym comes first
7+
8+
class C {
9+
def f(t: Thread) = t.threadId
10+
}

test/files/pos/unsafe.scala

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
2+
// scalac: --release:8 -Yopen-packages:sun.misc
3+
4+
import sun.misc.Unsafe
5+
6+
class C {
7+
val f = classOf[Unsafe].getDeclaredField("theUnsafe")
8+
f.setAccessible(true)
9+
val unsafe = f.get(null).asInstanceOf[Unsafe]
10+
11+
val k = unsafe.allocateInstance(classOf[K]).asInstanceOf[K]
12+
assert(k.value == 0)
13+
}
14+
15+
class K {
16+
val value = 42
17+
}
18+
19+
object Test extends App {
20+
new C
21+
}

test/junit/scala/tools/nsc/classpath/JrtClassPathTest.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class JrtClassPathTest {
2727
val elements = new ClassPathFactory(settings, closeableRegistry).classesInPath(resolver.Calculated.javaBootClassPath)
2828
AggregateClassPath(elements)
2929
}
30-
else JrtClassPath(None, closeableRegistry).get
30+
else JrtClassPath(None, None, closeableRegistry).head
3131

3232
assertEquals(Nil, cp.classes(""))
3333
assertTrue(cp.packages("java").toString, cp.packages("java").exists(_.name == "java.lang"))

0 commit comments

Comments
 (0)
0