Skip to content

Commit ddd32cc

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. We only enable the deserialization hack when reading old IR.
1 parent e4f5cdc commit ddd32cc

File tree

5 files changed

+78
-72
lines changed

5 files changed

+78
-72
lines changed

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

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2188,17 +2188,36 @@ 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+
21912196
if (currentClassSym.get == HackedStringClass) {
21922197
/* Hijack the bodies of String.length and String.charAt and replace
21932198
* them with String_length and String_charAt operations, respectively.
21942199
*/
21952200
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)
2200-
case _ =>
2201-
genBody()
2201+
case `lengthMethodName` => genAsUnaryOp(js.UnaryOp.String_length)
2202+
case `charAtMethodName` => genAsBinaryOp(js.BinaryOp.String_charAt)
2203+
case _ => genBody()
2204+
}
2205+
} else if (currentClassSym.get == ClassClass) {
2206+
// Similar, for the Class_x operations
2207+
methodName.name match {
2208+
case `getNameMethodName` => genAsUnaryOp(js.UnaryOp.Class_name)
2209+
case `isPrimitiveMethodName` => genAsUnaryOp(js.UnaryOp.Class_isPrimitive)
2210+
case `isInterfaceMethodName` => genAsUnaryOp(js.UnaryOp.Class_isInterface)
2211+
case `isArrayMethodName` => genAsUnaryOp(js.UnaryOp.Class_isArray)
2212+
case `getComponentTypeMethodName` => genAsUnaryOp(js.UnaryOp.Class_componentType)
2213+
case `getSuperclassMethodName` => genAsUnaryOp(js.UnaryOp.Class_superClass)
2214+
2215+
case `isInstanceMethodName` => genAsBinaryOp(js.BinaryOp.Class_isInstance)
2216+
case `isAssignableFromMethodName` => genAsBinaryOp(js.BinaryOp.Class_isAssignableFrom)
2217+
case `castMethodName` => genAsBinaryOp(js.BinaryOp.Class_cast)
2218+
case `newArrayOfThisClassMethodName` => genAsBinaryOp(js.BinaryOp.Class_newArray)
2219+
2220+
case _ => genBody()
22022221
}
22032222
} else {
22042223
genBody()
@@ -7084,6 +7103,28 @@ private object GenJSCode {
70847103
private val charAtMethodName =
70857104
MethodName("charAt", List(jstpe.IntRef), jstpe.CharRef)
70867105

7106+
private val getNameMethodName =
7107+
MethodName("getName", Nil, jstpe.ClassRef(ir.Names.BoxedStringClass))
7108+
private val isPrimitiveMethodName =
7109+
MethodName("isPrimitive", Nil, jstpe.BooleanRef)
7110+
private val isInterfaceMethodName =
7111+
MethodName("isInterface", Nil, jstpe.BooleanRef)
7112+
private val isArrayMethodName =
7113+
MethodName("isArray", Nil, jstpe.BooleanRef)
7114+
private val getComponentTypeMethodName =
7115+
MethodName("getComponentType", Nil, jstpe.ClassRef(ir.Names.ClassClass))
7116+
private val getSuperclassMethodName =
7117+
MethodName("getSuperclass", Nil, jstpe.ClassRef(ir.Names.ClassClass))
7118+
7119+
private val isInstanceMethodName =
7120+
MethodName("isInstance", List(jstpe.ClassRef(ir.Names.ObjectClass)), jstpe.BooleanRef)
7121+
private val isAssignableFromMethodName =
7122+
MethodName("isAssignableFrom", List(jstpe.ClassRef(ir.Names.ClassClass)), jstpe.BooleanRef)
7123+
private val castMethodName =
7124+
MethodName("cast", List(jstpe.ClassRef(ir.Names.ObjectClass)), jstpe.ClassRef(ir.Names.ObjectClass))
7125+
private val newArrayOfThisClassMethodName =
7126+
MethodName("newArrayOfThisClass", List(jstpe.ArrayTypeRef(jstpe.IntRef, 1)), jstpe.ClassRef(ir.Names.ObjectClass))
7127+
70877128
private val thisOriginalName = OriginalName("this")
70887129

70897130
private object BlockOrAlone {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1475,9 +1475,9 @@ object Serializers {
14751475
if (hacks.use4 && kind.isJSClass) {
14761476
// #4409: Filter out abstract methods in non-native JS classes for version < 1.5
14771477
methods0.filter(_.body.isDefined)
1478-
} else if (true /*hacks.use16*/ && cls == ClassClass) { // scalastyle:ignore
1478+
} else if (hacks.use16 && cls == ClassClass) {
14791479
jlClassMethodsHack16(methods0)
1480-
} else if (true /*hacks.use16*/ && cls == ReflectArrayModClass) { // scalastyle:ignore
1480+
} else if (hacks.use16 && cls == ReflectArrayModClass) {
14811481
jlReflectArrayMethodsHack16(methods0)
14821482
} else {
14831483
methods0

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

Lines changed: 23 additions & 51 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,21 @@ 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-
}
122+
def cast(obj: Any): A =
123+
throw new Error("Stub filled in by the compiler")
153124

154125
// java.lang.reflect.Array support
155126

156-
private[lang] def newArrayOfThisClass(dimensions: js.Array[Int]): AnyRef =
157-
data.newArrayOfThisClass(dimensions)
127+
@inline
128+
private[lang] def newArrayOfThisClass(dimensions: Array[Int]): AnyRef =
129+
throw new Error("Stub filled in by the compiler")
158130
}

javalib/src/main/scala/java/lang/reflect/Array.scala

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,12 @@ import scala.scalajs.js
1717
import java.lang.Class
1818

1919
object Array {
20+
@inline
2021
def newInstance(componentType: Class[_], length: Int): AnyRef =
21-
componentType.newArrayOfThisClass(js.Array(length))
22-
23-
def newInstance(componentType: Class[_], dimensions: scala.Array[Int]): AnyRef = {
24-
val jsDims = js.Array[Int]()
25-
val len = dimensions.length
26-
var i = 0
27-
while (i != len) {
28-
jsDims.push(dimensions(i))
29-
i += 1
30-
}
31-
componentType.newArrayOfThisClass(jsDims)
32-
}
22+
componentType.newArrayOfThisClass(scala.Array(length))
23+
24+
def newInstance(componentType: Class[_], dimensions: scala.Array[Int]): AnyRef =
25+
componentType.newArrayOfThisClass(dimensions)
3326

3427
def getLength(array: AnyRef): Int = array match {
3528
// yes, this is kind of stupid, but that's how it is

project/Build.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2055,7 +2055,7 @@ object Build {
20552055
} else {
20562056
Some(ExpectedSizes(
20572057
fastLink = 424000 to 425000,
2058-
fullLink = 282000 to 283000,
2058+
fullLink = 281000 to 282000,
20592059
fastLinkGz = 60000 to 61000,
20602060
fullLinkGz = 43000 to 44000,
20612061
))

0 commit comments

Comments
 (0)