Skip to content

Commit

Permalink
Scala.js: Emit js.NewArray IR nodes when possible. (#22446)
Browse files Browse the repository at this point in the history
Although there is a *correct* implementation of `sr.Arrays.newArray`, it
is not efficient when creating 1-dimensional arrays. The JVM backend
special-cases it to emit `newarray` bytecode instructions.

We now also special-case it in the JS backend. In the Scala.js IR
however, `js.NewArray` only accepts a single dimension. For multiple
dimensions, the right thing to do is to emit a direct call to
`jlr.Array.newInstance`.
  • Loading branch information
smarter authored Jan 27, 2025
2 parents 555aedd + 2852168 commit 89c20f8
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 3 deletions.
25 changes: 24 additions & 1 deletion compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2569,6 +2569,8 @@ class JSCodeGen()(using genCtx: Context) {
genCoercion(tree, receiver, code)
else if (code == JSPrimitives.THROW)
genThrow(tree, args)
else if (code == JSPrimitives.NEW_ARRAY)
genNewArray(tree, args)
else if (JSPrimitives.isJSPrimitive(code))
genJSPrimitive(tree, args, code, isStat)
else
Expand Down Expand Up @@ -3038,6 +3040,24 @@ class JSCodeGen()(using genCtx: Context) {
}
}

/** Gen a call to the special `newArray` method. */
private def genNewArray(tree: Apply, args: List[Tree]): js.Tree = {
implicit val pos: SourcePosition = tree.sourcePos

val List(elemClazz, Literal(arrayClassConstant), dimsArray: JavaSeqLiteral) = args: @unchecked

dimsArray.elems match {
case singleDim :: Nil =>
// Use a js.NewArray
val arrayTypeRef = toTypeRef(arrayClassConstant.typeValue).asInstanceOf[jstpe.ArrayTypeRef]
js.NewArray(arrayTypeRef, genExpr(singleDim))
case _ =>
// Delegate to jlr.Array.newInstance
js.ApplyStatic(js.ApplyFlags.empty, JLRArrayClassName, js.MethodIdent(JLRArrayNewInstanceMethodName),
List(genExpr(elemClazz), genJavaSeqLiteral(dimsArray)))(jstpe.AnyType)
}
}

/** Gen a "normal" apply (to a true method).
*
* But even these are further refined into:
Expand Down Expand Up @@ -4846,7 +4866,7 @@ class JSCodeGen()(using genCtx: Context) {

object JSCodeGen {

private val NullPointerExceptionClass = ClassName("java.lang.NullPointerException")
private val JLRArrayClassName = ClassName("java.lang.reflect.Array")
private val JSObjectClassName = ClassName("scala.scalajs.js.Object")
private val JavaScriptExceptionClassName = ClassName("scala.scalajs.js.JavaScriptException")

Expand All @@ -4856,6 +4876,9 @@ object JSCodeGen {

private val selectedValueMethodName = MethodName("selectedValue", Nil, ObjectClassRef)

private val JLRArrayNewInstanceMethodName =
MethodName("newInstance", List(jstpe.ClassRef(jsNames.ClassClass), jstpe.ArrayTypeRef(jstpe.IntRef, 1)), ObjectClassRef)

private val ObjectArgConstructorName = MethodName.constructor(List(ObjectClassRef))

private val thisOriginalName = OriginalName("this")
Expand Down
6 changes: 4 additions & 2 deletions compiler/src/dotty/tools/backend/sjs/JSPrimitives.scala
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,10 @@ object JSPrimitives {
inline val UNWRAP_FROM_THROWABLE = WRAP_AS_THROWABLE + 1 // js.special.unwrapFromThrowable
inline val DEBUGGER = UNWRAP_FROM_THROWABLE + 1 // js.special.debugger

inline val THROW = DEBUGGER + 1
inline val THROW = DEBUGGER + 1 // <special-ops>.throw
inline val NEW_ARRAY = THROW + 1 // scala.runtime.Arrays.newArray

inline val UNION_FROM = THROW + 1 // js.|.from
inline val UNION_FROM = NEW_ARRAY + 1 // js.|.from
inline val UNION_FROM_TYPE_CONSTRUCTOR = UNION_FROM + 1 // js.|.fromTypeConstructor

inline val REFLECT_SELECTABLE_SELECTDYN = UNION_FROM_TYPE_CONSTRUCTOR + 1 // scala.reflect.Selectable.selectDynamic
Expand Down Expand Up @@ -135,6 +136,7 @@ class JSPrimitives(ictx: Context) extends DottyPrimitives(ictx) {
addPrimitive(jsdefn.Special_debugger, DEBUGGER)

addPrimitive(defn.throwMethod, THROW)
addPrimitive(defn.newArrayMethod, NEW_ARRAY)

addPrimitive(jsdefn.PseudoUnion_from, UNION_FROM)
addPrimitive(jsdefn.PseudoUnion_fromTypeConstructor, UNION_FROM_TYPE_CONSTRUCTOR)
Expand Down

0 comments on commit 89c20f8

Please sign in to comment.