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

Skip to content

Commit ab45adc

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 9062c9c commit ab45adc

File tree

4 files changed

+96
-74
lines changed

4 files changed

+96
-74
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
@@ -2176,17 +2176,36 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
21762176
isJSFunctionDef(currentClassSym)) {
21772177
val flags = js.MemberFlags.empty.withNamespace(namespace)
21782178
val body = {
2179+
def genAsUnaryOp(op: js.UnaryOp.Code): js.Tree =
2180+
js.UnaryOp(op, genThis())
2181+
def genAsBinaryOp(op: js.BinaryOp.Code): js.Tree =
2182+
js.BinaryOp(op, genThis(), jsParams.head.ref)
2183+
21792184
if (currentClassSym.get == HackedStringClass) {
21802185
/* Hijack the bodies of String.length and String.charAt and replace
21812186
* them with String_length and String_charAt operations, respectively.
21822187
*/
21832188
methodName.name match {
2184-
case `lengthMethodName` =>
2185-
js.UnaryOp(js.UnaryOp.String_length, genThis())
2186-
case `charAtMethodName` =>
2187-
js.BinaryOp(js.BinaryOp.String_charAt, genThis(), jsParams.head.ref)
2188-
case _ =>
2189-
genBody()
2189+
case `lengthMethodName` => genAsUnaryOp(js.UnaryOp.String_length)
2190+
case `charAtMethodName` => genAsBinaryOp(js.BinaryOp.String_charAt)
2191+
case _ => genBody()
2192+
}
2193+
} else if (currentClassSym.get == ClassClass) {
2194+
// Similar, for the Class_x operations
2195+
methodName.name match {
2196+
case `getNameMethodName` => genAsUnaryOp(js.UnaryOp.Class_name)
2197+
case `isPrimitiveMethodName` => genAsUnaryOp(js.UnaryOp.Class_isPrimitive)
2198+
case `isInterfaceMethodName` => genAsUnaryOp(js.UnaryOp.Class_isInterface)
2199+
case `isArrayMethodName` => genAsUnaryOp(js.UnaryOp.Class_isArray)
2200+
case `getComponentTypeMethodName` => genAsUnaryOp(js.UnaryOp.Class_componentType)
2201+
case `getSuperclassMethodName` => genAsUnaryOp(js.UnaryOp.Class_superClass)
2202+
2203+
case `isInstanceMethodName` => genAsBinaryOp(js.BinaryOp.Class_isInstance)
2204+
case `isAssignableFromMethodName` => genAsBinaryOp(js.BinaryOp.Class_isAssignableFrom)
2205+
case `castMethodName` => genAsBinaryOp(js.BinaryOp.Class_cast)
2206+
case `newArrayOfThisClassMethodName` => genAsBinaryOp(js.BinaryOp.Class_newArray)
2207+
2208+
case _ => genBody()
21902209
}
21912210
} else {
21922211
genBody()
@@ -7072,6 +7091,28 @@ private object GenJSCode {
70727091
private val charAtMethodName =
70737092
MethodName("charAt", List(jstpe.IntRef), jstpe.CharRef)
70747093

7094+
private val getNameMethodName =
7095+
MethodName("getName", Nil, jstpe.ClassRef(ir.Names.BoxedStringClass))
7096+
private val isPrimitiveMethodName =
7097+
MethodName("isPrimitive", Nil, jstpe.BooleanRef)
7098+
private val isInterfaceMethodName =
7099+
MethodName("isInterface", Nil, jstpe.BooleanRef)
7100+
private val isArrayMethodName =
7101+
MethodName("isArray", Nil, jstpe.BooleanRef)
7102+
private val getComponentTypeMethodName =
7103+
MethodName("getComponentType", Nil, jstpe.ClassRef(ir.Names.ClassClass))
7104+
private val getSuperclassMethodName =
7105+
MethodName("getSuperclass", Nil, jstpe.ClassRef(ir.Names.ClassClass))
7106+
7107+
private val isInstanceMethodName =
7108+
MethodName("isInstance", List(jstpe.ClassRef(ir.Names.ObjectClass)), jstpe.BooleanRef)
7109+
private val isAssignableFromMethodName =
7110+
MethodName("isAssignableFrom", List(jstpe.ClassRef(ir.Names.ClassClass)), jstpe.BooleanRef)
7111+
private val castMethodName =
7112+
MethodName("cast", List(jstpe.ClassRef(ir.Names.ObjectClass)), jstpe.ClassRef(ir.Names.ObjectClass))
7113+
private val newArrayOfThisClassMethodName =
7114+
MethodName("newArrayOfThisClass", List(jstpe.IntRef), jstpe.ClassRef(ir.Names.ObjectClass))
7115+
70757116
private val thisOriginalName = OriginalName("this")
70767117

70777118
private object BlockOrAlone {

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

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1460,9 +1460,9 @@ object Serializers {
14601460
if (hacks.use4 && kind.isJSClass) {
14611461
// #4409: Filter out abstract methods in non-native JS classes for version < 1.5
14621462
methods0.filter(_.body.isDefined)
1463-
} else if (true /*hacks.use16*/ && cls == ClassClass) { // scalastyle:ignore
1463+
} else if (hacks.use16 && cls == ClassClass) {
14641464
jlClassMethodsHack16(methods0)
1465-
} else if (true /*hacks.use16*/ && cls == ReflectArrayModClass) { // scalastyle:ignore
1465+
} else if (hacks.use16 && cls == ReflectArrayModClass) {
14661466
jlReflectArrayMethodsHack16(methods0)
14671467
} else {
14681468
methods0
@@ -1651,14 +1651,7 @@ object Serializers {
16511651
OptimizerHints.empty, Version.fromInt(1))
16521652
}
16531653

1654-
/* Only for the temporary commit where we always apply the hack, because
1655-
* the hack is applied in the JavalibIRCleaner and then a second time
1656-
* for the true deserialization.
1657-
*/
1658-
val oldMethodsTemp =
1659-
methods.filterNot(_.methodName == newInstanceRecMethod.methodName)
1660-
1661-
val newMethods = for (method <- oldMethodsTemp) yield {
1654+
val newMethods = for (method <- methods) yield {
16621655
if (method.methodName.simpleName.nameString != "newInstance") {
16631656
method
16641657
} else {

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(length: Int): AnyRef =
129+
throw new Error("Stub filled in by the compiler")
158130
}

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

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,34 @@ 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+
componentType.newArrayOfThisClass(length)
2223

2324
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))
25+
def rec(componentType: Class[_], offset: Int): AnyRef = {
26+
val length = dimensions(offset)
27+
val result = newInstance(componentType, length)
28+
val innerOffset = offset + 1
29+
if (innerOffset < dimensions.length) {
30+
val result2 = result.asInstanceOf[Array[AnyRef]]
31+
val innerComponentType = componentType.getComponentType()
32+
var i = 0
33+
while (i != length) {
34+
result2(i) = rec(innerComponentType, innerOffset)
35+
i += 1
36+
}
37+
}
38+
result
39+
}
40+
41+
var componentType2 = componentType
42+
var i = 1
43+
while (i != dimensions.length) {
44+
componentType2 = newInstance(componentType2, 0).getClass()
2945
i += 1
3046
}
31-
componentType.newArrayOfThisClass(jsDims)
47+
rec(componentType2, 0)
3248
}
3349

3450
def getLength(array: AnyRef): Int = array match {

0 commit comments

Comments
 (0)