Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 37d97e3

Browse files
committed
Emit the new Class_x operations into jl.Class from the compiler.
We use the same technique as for `String_length` and `String_charAt`, for which we hijack the method body from the compiler and replace it with the appropriate operation. Likewise, we actually change the `NewArray` data type to contain a single length. We only enable the deserialization hacks of the previous commit when reading old IR.
1 parent 2dd91c1 commit 37d97e3

File tree

17 files changed

+161
-136
lines changed

17 files changed

+161
-136
lines changed

compiler/src/main/scala/org/scalajs/nscplugin/GenJSCode.scala

Lines changed: 64 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2188,15 +2188,45 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
21882188
isJSFunctionDef(currentClassSym)) {
21892189
val flags = js.MemberFlags.empty.withNamespace(namespace)
21902190
val body = {
2191+
def genAsUnaryOp(op: js.UnaryOp.Code): js.Tree =
2192+
js.UnaryOp(op, genThis())
2193+
def genAsBinaryOp(op: js.BinaryOp.Code): js.Tree =
2194+
js.BinaryOp(op, genThis(), jsParams.head.ref)
2195+
def genAsBinaryOpRhsNotNull(op: js.BinaryOp.Code): js.Tree =
2196+
js.BinaryOp(op, genThis(), js.UnaryOp(js.UnaryOp.CheckNotNull, jsParams.head.ref))
2197+
21912198
if (currentClassSym.get == HackedStringClass) {
21922199
/* Hijack the bodies of String.length and String.charAt and replace
21932200
* them with String_length and String_charAt operations, respectively.
21942201
*/
21952202
methodName.name match {
2196-
case `lengthMethodName` =>
2197-
js.UnaryOp(js.UnaryOp.String_length, genThis())
2198-
case `charAtMethodName` =>
2199-
js.BinaryOp(js.BinaryOp.String_charAt, genThis(), jsParams.head.ref)
2203+
case `lengthMethodName` => genAsUnaryOp(js.UnaryOp.String_length)
2204+
case `charAtMethodName` => genAsBinaryOp(js.BinaryOp.String_charAt)
2205+
case _ => genBody()
2206+
}
2207+
} else if (currentClassSym.get == ClassClass) {
2208+
// Similar, for the Class_x operations
2209+
methodName.name match {
2210+
case `getNameMethodName` => genAsUnaryOp(js.UnaryOp.Class_name)
2211+
case `isPrimitiveMethodName` => genAsUnaryOp(js.UnaryOp.Class_isPrimitive)
2212+
case `isInterfaceMethodName` => genAsUnaryOp(js.UnaryOp.Class_isInterface)
2213+
case `isArrayMethodName` => genAsUnaryOp(js.UnaryOp.Class_isArray)
2214+
case `getComponentTypeMethodName` => genAsUnaryOp(js.UnaryOp.Class_componentType)
2215+
case `getSuperclassMethodName` => genAsUnaryOp(js.UnaryOp.Class_superClass)
2216+
2217+
case `isInstanceMethodName` => genAsBinaryOp(js.BinaryOp.Class_isInstance)
2218+
case `isAssignableFromMethodName` => genAsBinaryOpRhsNotNull(js.BinaryOp.Class_isAssignableFrom)
2219+
case `castMethodName` => genAsBinaryOp(js.BinaryOp.Class_cast)
2220+
2221+
case _ => genBody()
2222+
}
2223+
} else if (currentClassSym.get == JavaLangReflectArrayModClass) {
2224+
methodName.name match {
2225+
case `arrayNewInstanceMethodName` =>
2226+
val List(jlClassParam, lengthParam) = jsParams
2227+
js.BinaryOp(js.BinaryOp.Class_newArray,
2228+
js.UnaryOp(js.UnaryOp.CheckNotNull, jlClassParam.ref),
2229+
lengthParam.ref)
22002230
case _ =>
22012231
genBody()
22022232
}
@@ -3644,12 +3674,11 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
36443674
*/
36453675
def genNewArray(arrayTypeRef: jstpe.ArrayTypeRef, arguments: List[js.Tree])(
36463676
implicit pos: Position): js.Tree = {
3647-
assert(arguments.length <= arrayTypeRef.dimensions,
3648-
"too many arguments for array constructor: found " + arguments.length +
3649-
" but array has only " + arrayTypeRef.dimensions +
3650-
" dimension(s)")
3677+
assert(arguments.size == 1,
3678+
"expected exactly 1 argument for array constructor: found " +
3679+
s"${arguments.length} at $pos")
36513680

3652-
js.NewArray(arrayTypeRef, arguments)
3681+
js.NewArray(arrayTypeRef, arguments.head)
36533682
}
36543683

36553684
/** Gen JS code for an array literal. */
@@ -7084,6 +7113,32 @@ private object GenJSCode {
70847113
private val charAtMethodName =
70857114
MethodName("charAt", List(jstpe.IntRef), jstpe.CharRef)
70867115

7116+
private val getNameMethodName =
7117+
MethodName("getName", Nil, jstpe.ClassRef(ir.Names.BoxedStringClass))
7118+
private val isPrimitiveMethodName =
7119+
MethodName("isPrimitive", Nil, jstpe.BooleanRef)
7120+
private val isInterfaceMethodName =
7121+
MethodName("isInterface", Nil, jstpe.BooleanRef)
7122+
private val isArrayMethodName =
7123+
MethodName("isArray", Nil, jstpe.BooleanRef)
7124+
private val getComponentTypeMethodName =
7125+
MethodName("getComponentType", Nil, jstpe.ClassRef(ir.Names.ClassClass))
7126+
private val getSuperclassMethodName =
7127+
MethodName("getSuperclass", Nil, jstpe.ClassRef(ir.Names.ClassClass))
7128+
7129+
private val isInstanceMethodName =
7130+
MethodName("isInstance", List(jstpe.ClassRef(ir.Names.ObjectClass)), jstpe.BooleanRef)
7131+
private val isAssignableFromMethodName =
7132+
MethodName("isAssignableFrom", List(jstpe.ClassRef(ir.Names.ClassClass)), jstpe.BooleanRef)
7133+
private val castMethodName =
7134+
MethodName("cast", List(jstpe.ClassRef(ir.Names.ObjectClass)), jstpe.ClassRef(ir.Names.ObjectClass))
7135+
7136+
private val arrayNewInstanceMethodName = {
7137+
MethodName("newInstance",
7138+
List(jstpe.ClassRef(ir.Names.ClassClass), jstpe.IntRef),
7139+
jstpe.ClassRef(ir.Names.ObjectClass))
7140+
}
7141+
70877142
private val thisOriginalName = OriginalName("this")
70887143

70897144
private object BlockOrAlone {

compiler/src/main/scala/org/scalajs/nscplugin/JSDefinitions.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ trait JSDefinitions {
3737

3838
lazy val JavaLangVoidClass = getRequiredClass("java.lang.Void")
3939

40+
lazy val JavaLangReflectArrayModClass = getModuleIfDefined("java.lang.reflect.Array").moduleClass
41+
4042
lazy val BoxedUnitModClass = BoxedUnitModule.moduleClass
4143

4244
lazy val ScalaJSJSPackageModule = getPackageObject("scala.scalajs.js")

ir/shared/src/main/scala/org/scalajs/ir/Hashers.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -321,10 +321,10 @@ object Hashers {
321321
mixTree(lhs)
322322
mixTree(rhs)
323323

324-
case NewArray(typeRef, lengths) =>
324+
case NewArray(typeRef, length) =>
325325
mixTag(TagNewArray)
326326
mixArrayTypeRef(typeRef)
327-
mixTrees(lengths)
327+
mixTrees(length :: Nil) // mixed as a list for historical reasons
328328

329329
case ArrayValue(typeRef, elems) =>
330330
mixTag(TagArrayValue)

ir/shared/src/main/scala/org/scalajs/ir/Printers.scala

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -511,15 +511,13 @@ object Printers {
511511
print(rhs)
512512
print(')')
513513

514-
case NewArray(typeRef, lengths) =>
514+
case NewArray(typeRef, length) =>
515515
print("new ")
516516
print(typeRef.base)
517-
for (length <- lengths) {
518-
print('[')
519-
print(length)
520-
print(']')
521-
}
522-
for (dim <- lengths.size until typeRef.dimensions)
517+
print('[')
518+
print(length)
519+
print(']')
520+
for (dim <- 1 until typeRef.dimensions)
523521
print("[]")
524522

525523
case ArrayValue(typeRef, elems) =>

ir/shared/src/main/scala/org/scalajs/ir/Serializers.scala

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -363,9 +363,10 @@ object Serializers {
363363
writeTagAndPos(TagBinaryOp)
364364
writeByte(op); writeTree(lhs); writeTree(rhs)
365365

366-
case NewArray(tpe, lengths) =>
366+
case NewArray(tpe, length) =>
367367
writeTagAndPos(TagNewArray)
368-
writeArrayTypeRef(tpe); writeTrees(lengths)
368+
writeArrayTypeRef(tpe)
369+
writeTrees(length :: Nil) // written as a list of historical reasons
369370

370371
case ArrayValue(tpe, elems) =>
371372
writeTagAndPos(TagArrayValue)
@@ -1232,10 +1233,10 @@ object Serializers {
12321233
val lengths = readTrees()
12331234
lengths match {
12341235
case length :: Nil =>
1235-
NewArray(arrayTypeRef, lengths)
1236+
NewArray(arrayTypeRef, length)
12361237

12371238
case _ =>
1238-
if (true /* hacks.use16 */) { // scalastyle:ignore
1239+
if (hacks.use16) {
12391240
// Rewrite as a call to j.l.r.Array.newInstance
12401241
val ArrayTypeRef(base, origDims) = arrayTypeRef
12411242
val newDims = origDims - lengths.size
@@ -1504,9 +1505,9 @@ object Serializers {
15041505
if (hacks.use4 && kind.isJSClass) {
15051506
// #4409: Filter out abstract methods in non-native JS classes for version < 1.5
15061507
methods0.filter(_.body.isDefined)
1507-
} else if (true /*hacks.use16*/ && cls == ClassClass) { // scalastyle:ignore
1508+
} else if (hacks.use16 && cls == ClassClass) {
15081509
jlClassMethodsHack16(methods0)
1509-
} else if (true /*hacks.use16*/ && cls == HackNames.ReflectArrayModClass) { // scalastyle:ignore
1510+
} else if (hacks.use16 && cls == HackNames.ReflectArrayModClass) {
15101511
jlReflectArrayMethodsHack16(methods0)
15111512
} else {
15121513
methods0
@@ -1697,14 +1698,7 @@ object Serializers {
16971698
OptimizerHints.empty, Version.fromInt(1))
16981699
}
16991700

1700-
/* Only for the temporary commit where we always apply the hack, because
1701-
* the hack is applied in the JavalibIRCleaner and then a second time
1702-
* for the true deserialization.
1703-
*/
1704-
val oldMethodsTemp =
1705-
methods.filterNot(_.methodName == newInstanceRecName)
1706-
1707-
val newMethods = for (method <- oldMethodsTemp) yield {
1701+
val newMethods = for (method <- methods) yield {
17081702
method.methodName match {
17091703
case `newInstanceSingleName` =>
17101704
// newInstance(jl.Class, int) --> newArray(jlClass.notNull, length)

ir/shared/src/main/scala/org/scalajs/ir/Transformers.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,8 @@ object Transformers {
114114
case BinaryOp(op, lhs, rhs) =>
115115
BinaryOp(op, transformExpr(lhs), transformExpr(rhs))
116116

117-
case NewArray(tpe, lengths) =>
118-
NewArray(tpe, lengths map transformExpr)
117+
case NewArray(tpe, length) =>
118+
NewArray(tpe, transformExpr(length))
119119

120120
case ArrayValue(tpe, elems) =>
121121
ArrayValue(tpe, elems map transformExpr)

ir/shared/src/main/scala/org/scalajs/ir/Traversers.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,8 @@ object Traversers {
101101
traverse(lhs)
102102
traverse(rhs)
103103

104-
case NewArray(tpe, lengths) =>
105-
lengths foreach traverse
104+
case NewArray(tpe, length) =>
105+
traverse(length)
106106

107107
case ArrayValue(tpe, elems) =>
108108
elems foreach traverse

ir/shared/src/main/scala/org/scalajs/ir/Trees.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,7 @@ object Trees {
519519
}
520520
}
521521

522-
sealed case class NewArray(typeRef: ArrayTypeRef, lengths: List[Tree])(
522+
sealed case class NewArray(typeRef: ArrayTypeRef, length: Tree)(
523523
implicit val pos: Position) extends Tree {
524524
val tpe = ArrayType(typeRef, nullable = false)
525525
}

ir/shared/src/test/scala/org/scalajs/ir/PrintersTest.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -557,10 +557,10 @@ class PrintersTest {
557557
}
558558

559559
@Test def printNewArray(): Unit = {
560-
assertPrintEquals("new int[3]", NewArray(ArrayTypeRef(IntRef, 1), List(i(3))))
561-
assertPrintEquals("new int[3][]", NewArray(ArrayTypeRef(IntRef, 2), List(i(3))))
562-
assertPrintEquals("new java.lang.Object[3][4][][]",
563-
NewArray(ArrayTypeRef(ObjectClass, 4), List(i(3), i(4))))
560+
assertPrintEquals("new int[3]", NewArray(ArrayTypeRef(IntRef, 1), i(3)))
561+
assertPrintEquals("new int[3][]", NewArray(ArrayTypeRef(IntRef, 2), i(3)))
562+
assertPrintEquals("new java.lang.Object[3][][][]",
563+
NewArray(ArrayTypeRef(ObjectClass, 4), i(3)))
564564
}
565565

566566
@Test def printArrayValue(): Unit = {

javalib/src/main/scala/java/lang/Class.scala

Lines changed: 20 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -15,68 +15,39 @@ package java.lang
1515
import java.lang.constant.Constable
1616
import java.io.Serializable
1717

18-
import scala.scalajs.js
19-
20-
@js.native
21-
private trait ScalaJSClassData[A] extends js.Object {
22-
val name: String = js.native
23-
val isPrimitive: scala.Boolean = js.native
24-
val isInterface: scala.Boolean = js.native
25-
val isArrayClass: scala.Boolean = js.native
26-
27-
def isInstance(obj: Any): scala.Boolean = js.native
28-
def isAssignableFrom(that: ScalaJSClassData[_]): scala.Boolean = js.native
29-
def checkCast(obj: Any): scala.Unit = js.native
30-
31-
def getSuperclass(): Class[_ >: A] = js.native
32-
def getComponentType(): Class[_] = js.native
33-
34-
def newArrayOfThisClass(dimensions: js.Array[Int]): AnyRef = js.native
35-
}
36-
37-
final class Class[A] private (data0: Object)
18+
final class Class[A] private ()
3819
extends Object with Serializable with Constable {
3920

40-
private[this] val data: ScalaJSClassData[A] =
41-
data0.asInstanceOf[ScalaJSClassData[A]]
42-
4321
private[this] var cachedSimpleName: String = _
4422

45-
/** Access to `data` for other instances or `@inline` methods.
46-
*
47-
* Directly accessing the `data` field from `@inline` methods will cause
48-
* scalac to make the field public and mangle its name. Since the Emitter
49-
* relies on the field being called exactly `data` in some of its
50-
* optimizations, we must avoid that.
51-
*
52-
* This non-`@noinline` method can be used to access the field without
53-
* triggering scalac's mangling. Since it is a trivial accessor, the
54-
* Scala.js optimizer will inline it anyway.
55-
*/
56-
private def getData(): ScalaJSClassData[A] = data
57-
5823
override def toString(): String = {
5924
(if (isInterface()) "interface " else
6025
if (isPrimitive()) "" else "class ")+getName()
6126
}
6227

28+
@inline
6329
def isInstance(obj: Any): scala.Boolean =
64-
data.isInstance(obj)
30+
throw new Error("Stub filled in by the compiler")
6531

32+
@inline
6633
def isAssignableFrom(that: Class[_]): scala.Boolean =
67-
this.data.isAssignableFrom(that.getData())
34+
throw new Error("Stub filled in by the compiler")
6835

36+
@inline
6937
def isInterface(): scala.Boolean =
70-
data.isInterface
38+
throw new Error("Stub filled in by the compiler")
7139

40+
@inline
7241
def isArray(): scala.Boolean =
73-
data.isArrayClass
42+
throw new Error("Stub filled in by the compiler")
7443

44+
@inline
7545
def isPrimitive(): scala.Boolean =
76-
data.isPrimitive
46+
throw new Error("Stub filled in by the compiler")
7747

48+
@inline
7849
def getName(): String =
79-
data.name
50+
throw new Error("Stub filled in by the compiler")
8051

8152
def getSimpleName(): String = {
8253
if (cachedSimpleName == null)
@@ -108,7 +79,7 @@ final class Class[A] private (data0: Object)
10879
if (isArray()) {
10980
getComponentType().getSimpleName() + "[]"
11081
} else {
111-
val name = data.name
82+
val name = getName()
11283
var idx = name.length - 1
11384

11485
// Include trailing '$'s for module class names
@@ -139,20 +110,15 @@ final class Class[A] private (data0: Object)
139110
}
140111
}
141112

113+
@inline
142114
def getSuperclass(): Class[_ >: A] =
143-
data.getSuperclass()
115+
throw new Error("Stub filled in by the compiler")
144116

117+
@inline
145118
def getComponentType(): Class[_] =
146-
data.getComponentType()
119+
throw new Error("Stub filled in by the compiler")
147120

148121
@inline
149-
def cast(obj: Any): A = {
150-
getData().checkCast(obj)
151-
obj.asInstanceOf[A]
152-
}
153-
154-
// java.lang.reflect.Array support
155-
156-
private[lang] def newArrayOfThisClass(dimensions: js.Array[Int]): AnyRef =
157-
data.newArrayOfThisClass(dimensions)
122+
def cast(obj: Any): A =
123+
throw new Error("Stub filled in by the compiler")
158124
}

0 commit comments

Comments
 (0)