8000 Refactoring: Isolate handling of javalib methods with special bodies. · scala-js/scala-js@46962c5 · GitHub
[go: up one dir, main page]

Skip to content

Commit 46962c5

Browse files
committed
Refactoring: Isolate handling of javalib methods with special bodies.
A number of methods from the javalib are special-cased by the compiler, which replaces their body with a dedicated `UnaryOp` or `BinaryOp`. This commit refactors that handling to isolate it better from the handling of regular methods. We also make it a bit more flexible, so that we can more easily add further such methods in the future.
1 parent 493130e commit 46962c5

File tree

1 file changed

+96
-73
lines changed

1 file changed

+96
-73
lines changed

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

Lines changed: 96 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -2298,50 +2298,17 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
22982298
isJSFunctionDef(currentClassSym)) {
22992299
val flags = js.MemberFlags.empty.withNamespace(namespace)
23002300
val body = {
2301-
def genAsUnaryOp(op: js.UnaryOp.Code): js.Tree =
2302-
js.UnaryOp(op, genThis())
2303-
def genAsBinaryOp(op: js.BinaryOp.Code): js.Tree =
2304-
js.BinaryOp(op, genThis(), jsParams.head.ref)
2305-
def genAsBinaryOpRhsNotNull(op: js.BinaryOp.Code): js.Tree =
2306-
js.BinaryOp(op, genThis(), js.UnaryOp(js.UnaryOp.CheckNotNull, jsParams.head.ref))
2307-
2308-
if (currentClassSym.get == HackedStringClass) {
2309-
/* Hijack the bodies of String.length and String.charAt and replace
2310-
* them with String_length and String_charAt operations, respectively.
2311-
*/
2312-
methodName.name match {
2313-
case `lengthMethodName` => genAsUnaryOp(js.UnaryOp.String_length)
2314-
case `charAtMethodName` => genAsBinaryOp(js.BinaryOp.String_charAt)
2315-
case _ => genBody()
2316-
}
2317-
} else if (currentClassSym.get == ClassClass) {
2318-
// Similar, for the Class_x operations
2319-
methodName.name match {
2320-
case `getNameMethodName` => genAsUnaryOp(js.UnaryOp.Class_name)
2321-
case `isPrimitiveMethodName` => genAsUnaryOp(js.UnaryOp.Class_isPrimitive)
2322-
case `isInterfaceMethodName` => genAsUnaryOp(js.UnaryOp.Class_isInterface)
2323-
case `isArrayMethodName` => genAsUnaryOp(js.UnaryOp.Class_isArray)
2324-
case `getComponentTypeMethodName` => genAsUnaryOp(js.UnaryOp.Class_componentType)
2325-
case `getSuperclassMethodName` => genAsUnaryOp(js.UnaryOp.Class_superClass)
2326-
2327-
case `isInstanceMethodName` => genAsBinaryOp(js.BinaryOp.Class_isInstance)
2328-
case `isAssignableFromMethodName` => genAsBinaryOpRhsNotNull(js.BinaryOp.Class_isAssignableFrom)
2329-
case `castMethodName` => genAsBinaryOp(js.BinaryOp.Class_cast)
2330-
2331-
case _ => genBody()
2332-
}
2333-
} else if (currentClassSym.get == JavaLangReflectArrayModClass) {
2334-
methodName.name match {
2335-
case `arrayNewInstanceMethodName` =>
2336-
val List(jlClassParam, lengthParam) = jsParams
2337-
js.BinaryOp(js.BinaryOp.Class_newArray,
2338-
js.UnaryOp(js.UnaryOp.CheckNotNull, jlClassParam.ref),
2339-
lengthParam.ref)
2340-
case _ =>
2301+
val classOwner = currentClassSym.owner
2302+
if (classOwner != JavaLangPackageClass && classOwner.owner != JavaLangPackageClass) {
2303+
// Fast path; it cannot be any of the special methods of the javalib
2304+
genBody()
2305+
} else {
2306+
JavalibMethodsWithOpBody.get((encodeClassName(currentClassSym), methodName.name)) match {
2307+
case None =>
23412308
genBody()
2309+
case Some(javalibOpBody) =>
2310+
javalibOpBody.generate(genThis(), jsParams.map(_.ref))
23422311
}
2343-
} else {
2344-
genBody()
23452312
}
23462313
}
23472314
js.MethodDef(flags, methodName, originalName, jsParams, resultIRType,
@@ -7379,37 +7346,6 @@ private object GenJSCode {
73797346
private val ObjectArgConstructorName =
73807347
MethodName.constructor(List(jswkn.ObjectRef))
73817348

7382-
private val lengthMethodName =
7383-
MethodName("length", Nil, jstpe.IntRef)
7384-
private val charAtMethodName =
7385-
MethodName("charAt", List(jstpe.IntRef), jstpe.CharRef)
7386-
7387-
private val getNameMethodName =
7388-
MethodName("getName", Nil, jstpe.ClassRef(jswkn.BoxedStringClass))
7389-
private val isPrimitiveMethodName =
7390-
MethodName("isPrimitive", Nil, jstpe.BooleanRef)
7391-
private val isInterfaceMethodName =
7392-
MethodName("isInterface", Nil, jstpe.BooleanRef)
7393-
private val isArrayMethodName =
7394-
MethodName("isArray", Nil, jstpe.BooleanRef)
7395-
private val getComponentTypeMethodName =
7396-
MethodName("getComponentType", Nil, jstpe.ClassRef(jswkn.ClassClass))
7397-
private val getSuperclassMethodName =
7398-
MethodName("getSuperclass", Nil, jstpe.ClassRef(jswkn.ClassClass))
7399-
7400-
private val isInstanceMethodName =
7401-
MethodName("isInstance", List(jstpe.ClassRef(jswkn.ObjectClass)), jstpe.BooleanRef)
7402-
private val isAssignableFromMethodName =
7403-
MethodName("isAssignableFrom", List(jstpe.ClassRef(jswkn.ClassClass)), jstpe.BooleanRef)
7404-
private val castMethodName =
7405-
MethodName("cast", List(jstpe.ClassRef(jswkn.ObjectClass)), jstpe.ClassRef(jswkn.ObjectClass))
7406-
7407-
private val arrayNewInstanceMethodName = {
7408-
MethodName("newInstance",
7409-
List(jstpe.ClassRef(jswkn.ClassClass), jstpe.IntRef),
7410-
jstpe.ClassRef(jswkn.ObjectClass))
7411-
}
7412-
74137349
private val thisOriginalName = OriginalName("this")
74147350

74157351
private object BlockOrAlone {
@@ -7425,4 +7361,91 @@ private object GenJSCode {
74257361
case _ => Some((tree, Nil))
74267362
}
74277363
}
7364+
7365+
private sealed abstract class JavalibOpBody {
7366+
import JavalibOpBody._
7367+
7368+
def generate(receiver: js.Tree, args: List[js.Tree])(implicit pos: ir.Position): js.Tree = {
7369+
def checkNotNullIf(arg: js.Tree, checkNulls: Boolean): js.Tree =
7370+
if (checkNulls && arg.tpe.isNullable) js.UnaryOp(js.UnaryOp.CheckNotNull, arg)
7371+
else arg
7372+
7373+
7374+
this match {
7375+
case ThisUnaryOp(op) =>
7376+
assert(args.isEmpty)
7377+
js.UnaryOp(op, receiver)
7378+
7379+
case ThisBinaryOp(op, checkNulls) =>
7380+
val List(rhs) = args: @unchecked
7381+
js.BinaryOp(op, receiver, checkNotNullIf(rhs, checkNulls))
7382+
7383+
case ArgUnaryOp(op, checkNulls) =>
7384+
val List(arg) = args: @unchecked
7385+
js.UnaryOp(op, checkNotNullIf(arg, checkNulls))
7386+
7387+
case ArgBinaryOp(op, checkNulls) =>
7388+
val List(lhs, rhs) = args: @unchecked
7389+
js.BinaryOp(op, checkNotNullIf(lhs, checkNulls), checkNotNullIf(rhs, checkNulls))
7390+
}
7391+
}
7392+
}
7393+
7394+
private object JavalibOpBody {
7395+
/** UnaryOp applying to the `this` parameter. */
7396+
final case class ThisUnaryOp(op: js.UnaryOp.Code) extends JavalibOpBody
7397+
7398+
/** BinaryOp applying to the `this` parameter and the regular parameter. */
7399+
final case class ThisBinaryOp(op: js.BinaryOp.Code, checkNulls: Boolean = false) extends JavalibOpBody
7400+
7401+
/** UnaryOp applying to the only regular parameter (`this` is ignored). */
7402+
final case class ArgUnaryOp(op: js.UnaryOp.Code, checkNulls: Boolean = false) extends JavalibOpBody
7403+
7404+
/** BinaryOp applying to the two regular paramters (`this` is ignored). */
7405+
final case class ArgBinaryOp(op: js.BinaryOp.Code, checkNulls: Boolean = false) extends JavalibOpBody
7406+
}
7407+
7408+
/** Methods of the javalib whose body must be replaced by a dedicated
7409+
* UnaryOp or BinaryOp.
7410+
*/
7411+
private lazy val JavalibMethodsWithOpBody: Map[(ClassName, MethodName), JavalibOpBody] = {
7412+
import JavalibOpBody._
7413+
import js.{UnaryOp => unop, BinaryOp => binop}
7414+
import jstpe.{BooleanRef => Z, CharRef => C, IntRef => I}
7415+
import MethodName.{apply => m}
7416+
7417+
val O = jswkn.ObjectRef
7418+
val CC = jstpe.ClassRef(jswkn.ClassClass)
7419+
val T = jstpe.ClassRef(jswkn.BoxedStringClass)
7420+
7421+
val byClass: Map[ClassName, Map[MethodName, JavalibOpBody]] = Map(
7422+
jswkn.BoxedStringClass -> Map(
7423+
m("length", Nil, I) -> ThisUnaryOp(unop.String_length),
7424+
m("charAt", List(I), C) -> ThisBinaryOp(binop.String_charAt)
7425+
),
7426+
jswkn.ClassClass -> Map(
7427+
// Unary operators
7428+
m("getName", Nil, T) -> ThisUnaryOp(unop.Class_name),
7429+
m("isPrimitive", Nil, Z) -> ThisUnaryOp(unop.Class_isPrimitive),
7430+
m("isInterface", Nil, Z) -> ThisUnaryOp(unop.Class_isInterface),
7431+
m("isArray", Nil, Z) -> ThisUnaryOp(unop.Class_isArray),
7432+
m("getComponentType", Nil, CC) -> ThisUnaryOp(unop.Class_componentType),
7433+
m("getSuperclass", Nil, CC) -> ThisUnaryOp(unop.Class_superClass),
7434+
// Binary operators
7435+
m("isInstance", List(O), Z) -> ThisBinaryOp(binop.Class_isInstance),
7436+
m("isAssignableFrom", List(CC), Z) -> ThisBinaryOp(binop.Class_isAssignableFrom, checkNulls = true),
7437+
m("cast", List(O), O) -> ThisBinaryOp(binop.Class_cast)
7438+
),
7439+
ClassName("java.lang.reflect.Array$") -> Map(
7440+
m("newInstance", List(CC, I), O) -> ArgBinaryOp(binop.Class_newArray, checkNulls = true)
7441+
)
7442+
)
7443+
7444+
for {
7445+
(cls, methods) <- byClass
7446+
(methodName, body) <- methods
7447+
} yield {
7448+
(cls, methodName) -> body
7449+
}
7450+
}
74287451
}

0 commit comments

Comments
 (0)
0