Skip to content

Commit 19b37be

Browse files
committed
Fix #4616: Proper static dependency tracking for jl.Class
1 parent f7bfb17 commit 19b37be

File tree

4 files changed

+104
-7
lines changed

4 files changed

+104
-7
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Scala.js (https://www.scala-js.org/)
3+
*
4+
* Copyright EPFL.
5+
*
6+
* Licensed under Apache License 2.0
7+
* (https://www.apache.org/licenses/LICENSE-2.0).
8+
*
9+
* See the NOTICE file distributed with this work for
10+
* additional information regarding copyright ownership.
11+
*/
12+
13+
package org.scalajs.linker
14+
15+
import scala.concurrent._
16+
import scala.concurrent.duration._
17+
18+
import java.nio.file.Path
19+
20+
import org.junit.{Rule, Test}
21+
import org.junit.Assert._
22+
import org.junit.rules.TemporaryFolder
23+
24+
import org.scalajs.ir.Trees._
25+
26+
import org.scalajs.junit.async._
27+
28+
import org.scalajs.jsenv.nodejs.NodeJSEnv
29+
import org.scalajs.jsenv.test.kit.TestKit
30+
31+
import org.scalajs.linker.interface._
32+
import org.scalajs.linker.testutils.LinkingUtils._
33+
import org.scalajs.linker.testutils.TestIRBuilder._
34+
35+
class RunTest {
36+
import scala.concurrent.ExecutionContext.Implicits.global
37+
38+
@(Rule @scala.annotation.meta.getter)
39+
val tempFolder = new TemporaryFolder()
40+
41+
@Test
42+
def jlClassIsCore_Issue4616(): AsyncResult = await {
43+
// Check that if jl.Class is instantiated, it must be depended upon by jl.Object.
44+
45+
val classDefs = Seq(
46+
mainTestClassDef({
47+
consoleLog(ClassOf(T))
48+
})
49+
)
50+
51+
val linkerConfig = StandardConfig()
52+
.withModuleKind(ModuleKind.ESModule)
53+
.withModuleSplitStyle(ModuleSplitStyle.SmallestModules)
54+
.withSourceMap(false)
55+
.withOutputPatterns(OutputPatterns.fromJSFile("%s.mjs"))
56+
57+
testLinkAndRun(classDefs, MainTestModuleInitializers, linkerConfig,
58+
TestKit.InputKind.ESModule)
59+
}
60+
61+
private def testLinkAndRun(classDefs: Seq[ClassDef],
62+
moduleInitializers: List[ModuleInitializer],
63+
linkerConfig: StandardConfig, inputKind: TestKit.InputKind): Future[Unit] = {
64+
val output = tempFolder.newFolder().toPath
65+
66+
val kit = new TestKit(new NodeJSEnv, timeout = 2.seconds, inputKind)
67+
68+
for {
69+
report <- testLink(classDefs, moduleInitializers, linkerConfig, PathOutputDirectory(output))
70+
} yield {
71+
assertEquals(report.publicModules.size, 1)
72+
73+
val path = output.resolve(report.publicModules.head.jsFileName)
74+
75+
kit.withRun(Seq(kit.pathToInput(path))) {
76+
_.succeeds()
77+
}
78+
}
79+
}
80+
}

Diff for: linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala

+11
Original file line numberDiff line numberDiff line change
@@ -1270,6 +1270,17 @@ private final class Analyzer(config: CommonPhaseConfig,
12701270
lookupClass(className)(_.accessData())
12711271
}
12721272

1273+
if (data.accessedClassClass) {
1274+
/* java.lang.Class is only ever instantiated in the CoreJSLib.
1275+
* Therefore, make java.lang.Object depend on it instead of the caller itself.
1276+
*/
1277+
objectClassInfo.staticDependencies += ClassClass
1278+
lookupClass(ClassClass) { clazz =>
1279+
clazz.instantiated()
1280+
clazz.callMethodStatically(MemberNamespace.Constructor, ObjectArgConstructorName)
1281+
}
1282+
}
1283+
12731284
for (className <- data.referencedClasses) {
12741285
/* No need to add to staticDependencies: The classes will not be
12751286
* referenced in the final JS code.

Diff for: linker/shared/src/main/scala/org/scalajs/linker/analyzer/Infos.scala

+11-6
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ object Infos {
9090
val usedInstanceTests: List[ClassName],
9191
val accessedClassData: List[ClassName],
9292
val referencedClasses: List[ClassName],
93+
val accessedClassClass: Boolean,
9394
val accessedNewTarget: Boolean,
9495
val accessedImportMeta: Boolean
9596
)
@@ -98,7 +99,7 @@ object Infos {
9899
val Empty: ReachabilityInfo = {
99100
new ReachabilityInfo(Map.empty, Map.empty, Map.empty, Map.empty,
100101
Map.empty, Map.empty, Map.empty, Nil, Nil, Nil, Nil, Nil, false,
101-
false)
102+
false, false)
102103
}
103104
}
104105

@@ -161,6 +162,7 @@ object Infos {
161162
private val usedInstanceTests = mutable.Set.empty[ClassName]
162163
private val accessedClassData = mutable.Set.empty[ClassName]
163164
private val referencedClasses = mutable.Set.empty[ClassName]
165+
private var accessedClassClass = false
164166
private var accessedNewTarget = false
165167
private var accessedImportMeta = false
166168

@@ -311,6 +313,11 @@ object Infos {
311313
this
312314
}
313315

316+
def addAccessedClassClass(): this.type = {
317+
accessedClassClass = true
318+
this
319+
}
320+
314321
def addAccessNewTarget(): this.type = {
315322
accessedNewTarget = true
316323
this
@@ -338,6 +345,7 @@ object Infos {
338345
usedInstanceTests = usedInstanceTests.toList,
339346
accessedClassData = accessedClassData.toList,
340347
referencedClasses = referencedClasses.toList,
348+
accessedClassClass = accessedClassClass,
341349
accessedNewTarget = accessedNewTarget,
342350
accessedImportMeta = accessedImportMeta
343351
)
@@ -570,13 +578,10 @@ object Infos {
570578

571579
case ClassOf(cls) =>
572580
builder.maybeAddAccessedClassData(cls)
573-
574-
// `ClassOf` instantiates a java.lang.Class.
575-
builder.addInstantiatedClass(ClassClass, ObjectArgConstructorName)
581+
builder.addAccessedClassClass()
576582

577583
case GetClass(_) =>
578-
// `GetClass` instantiates a java.lang.Class.
579-
builder.addInstantiatedClass(ClassClass, ObjectArgConstructorName)
584+
builder.addAccessedClassClass()
580585

581586
case JSPrivateSelect(qualifier, className, field) =>
582587
builder.addPrivateJSFieldUsed(className, field.name)

Diff for: project/Build.scala

+2-1
Original file line numberDiff line numberDiff line change
@@ -979,7 +979,8 @@ object Build {
979979
libraryDependencies ++= Seq(
980980
"com.google.javascript" % "closure-compiler" % "v20211201",
981981
"com.google.jimfs" % "jimfs" % "1.1" % "test",
982-
"org.scala-js" %% "scalajs-env-nodejs" % "1.3.0" % "test"
982+
"org.scala-js" %% "scalajs-env-nodejs" % "1.3.0" % "test",
983+
"org.scala-js" %% "scalajs-js-envs-test-kit" % "1.3.0" % "test"
983984
) ++ (
984985
parallelCollectionsDependencies(scalaVersion.value)
985986
),

0 commit comments

Comments
 (0)