Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
254 changes: 194 additions & 60 deletions compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ final class JSDefinitions()(using Context) {
def JSGlobalScopeAnnot(using Context) = JSGlobalScopeAnnotType.symbol.asClass
@threadUnsafe lazy val JSNameAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.JSName")
def JSNameAnnot(using Context) = JSNameAnnotType.symbol.asClass
@threadUnsafe lazy val JSOperatorAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.JSOperator")
def JSOperatorAnnot(using Context) = JSOperatorAnnotType.symbol.asClass
@threadUnsafe lazy val JSFullNameAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.JSFullName")
def JSFullNameAnnot(using Context) = JSFullNameAnnotType.symbol.asClass
@threadUnsafe lazy val JSBracketAccessAnnotType: TypeRef = requiredClassRef("scala.scalajs.js.annotation.JSBracketAccess")
Expand Down Expand Up @@ -213,6 +215,14 @@ final class JSDefinitions()(using Context) {
@threadUnsafe lazy val WrappedArrayType: TypeRef = requiredClassRef("scala.scalajs.js.WrappedArray")
def WrappedArrayClass(using Context) = WrappedArrayType.symbol.asClass

@threadUnsafe lazy val LinkingInfoModuleRef = requiredModuleRef("scala.scalajs.LinkingInfo")
def LinkingInfoModule(using Context) = LinkingInfoModuleRef.symbol
@threadUnsafe lazy val LinkingInfo_linkTimeIfR = LinkingInfoModule.requiredMethodRef("linkTimeIf")
def LinkingInfo_linkTimeIf(using Context) = LinkingInfo_linkTimeIfR.symbol

@threadUnsafe lazy val LinkTimePropertyAnnotType: TypeRef = requiredClassRef("scala.scalajs.annotation.linkTimeProperty")
def LinkTimePropertyAnnot(using Context) = LinkTimePropertyAnnotType.symbol.asClass

@threadUnsafe lazy val ScalaRunTime_isArrayR = defn.ScalaRuntimeModule.requiredMethodRef("isArray", List(???, ???))
def ScalaRunTime_isArray(using Context): Symbol = ScalaRunTime_isArrayR.symbol

Expand Down
8 changes: 6 additions & 2 deletions compiler/src/dotty/tools/backend/sjs/JSPrimitives.scala
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +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 // <special-ops>.throw
inline val NEW_ARRAY = THROW + 1 // scala.runtime.Arrays.newArray
inline val LINKTIME_IF = DEBUGGER + 1 // LinkingInfo.linkTimeIf

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

inline val UNION_FROM = NEW_ARRAY + 1 // js.|.from
inline val UNION_FROM_TYPE_CONSTRUCTOR = UNION_FROM + 1 // js.|.fromTypeConstructor
Expand Down Expand Up @@ -135,6 +137,8 @@ class JSPrimitives(ictx: Context) extends DottyPrimitives(ictx) {
addPrimitive(jsdefn.Special_unwrapFromThrowable, UNWRAP_FROM_THROWABLE)
addPrimitive(jsdefn.Special_debugger, DEBUGGER)

addPrimitive(jsdefn.LinkingInfo_linkTimeIf, LINKTIME_IF)

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

Expand Down
94 changes: 64 additions & 30 deletions compiler/src/dotty/tools/dotc/transform/sjs/JSSymUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,16 @@ object JSSymUtils {
lazy val pc = sym.info.paramNamess.map(_.size).sum

sym.name match {
case nme.apply => Call
case JSUnaryOpMethodName(code) if pc == 0 => UnaryOp(code)
case JSBinaryOpMethodName(code) if pc == 1 => BinaryOp(code)
case _ => default
case nme.apply =>
Call
case JSUnaryOpMethodName(code, defaultsToOp)
if (defaultsToOp || sym.hasAnnotation(jsdefn.JSOperatorAnnot)) && pc == 0 =>
UnaryOp(code)
case JSBinaryOpMethodName(code, defaultsToOp)
if (defaultsToOp || sym.hasAnnotation(jsdefn.JSOperatorAnnot)) && pc == 1 =>
BinaryOp(code)
case _ =>
default
}
} else {
default
Expand Down Expand Up @@ -182,6 +188,7 @@ object JSSymUtils {
sym.info.paramNamess.flatten.zip(sym.info.paramInfoss.flatten)

val paramInfosAtElimRepeated = atPhase(elimRepeatedPhase) {
// See also JSCodeGen.genActualArgs
val list =
for ((name, info) <- paramNamesAndTypes) yield {
val v =
Expand Down Expand Up @@ -230,43 +237,70 @@ object JSSymUtils {
end sjsNeedsField
}

/** Extractor for a `TermName` that *may* be a JS unary operator.
*
* If it may be a JS unary operator, then a method with that name may have
* the `@JSOperator` annotation, and it will be treated as such.
*
* If a method has neither `@JSName` nor `@JSOperator`, then a default is
* chosen. If the `Boolean` value is `true`, the default is to treat the
* method as if it had `@JSOperator`. If it is `false`, the default is *not*
* to treat it as an operator.
*
* Currently, all JS unary operators default to `@JSOperator`.
*/
private object JSUnaryOpMethodName {
private val map = Map(
nme.UNARY_+ -> js.JSUnaryOp.+,
nme.UNARY_- -> js.JSUnaryOp.-,
nme.UNARY_~ -> js.JSUnaryOp.~,
nme.UNARY_! -> js.JSUnaryOp.!
nme.UNARY_+ -> (js.JSUnaryOp.+, true),
nme.UNARY_- -> (js.JSUnaryOp.-, true),
nme.UNARY_~ -> (js.JSUnaryOp.~, true),
nme.UNARY_! -> (js.JSUnaryOp.!, true),
)

def unapply(name: TermName): Option[js.JSUnaryOp.Code] =
def unapply(name: TermName): Option[(js.JSUnaryOp.Code, Boolean)] =
map.get(name)
}

/** Extractor for a `TermName` that *may* be a JS binary operator.
*
* If it may be a JS binary operator, then a method with that name may have
* the `@JSOperator` annotation, and it will be treated as such.
*
* If a method has neither `@JSName` nor `@JSOperator`, then a default is
* chosen. If the `Boolean` value is `true`, the default is to treat the
* method as if it had `@JSOperator`. If it is `false`, the default is *not*
* to treat it as an operator.
*
* Most JS binary operators default to `@JSOperator`. Currently, the only
* exception is `**`, for backward compatibility reasons.
*/
private object JSBinaryOpMethodName {
private val map = Map(
nme.ADD -> js.JSBinaryOp.+,
nme.SUB -> js.JSBinaryOp.-,
nme.MUL -> js.JSBinaryOp.*,
nme.DIV -> js.JSBinaryOp./,
nme.MOD -> js.JSBinaryOp.%,

nme.LSL -> js.JSBinaryOp.<<,
nme.ASR -> js.JSBinaryOp.>>,
nme.LSR -> js.JSBinaryOp.>>>,
nme.OR -> js.JSBinaryOp.|,
nme.AND -> js.JSBinaryOp.&,
nme.XOR -> js.JSBinaryOp.^,

nme.LT -> js.JSBinaryOp.<,
nme.LE -> js.JSBinaryOp.<=,
nme.GT -> js.JSBinaryOp.>,
nme.GE -> js.JSBinaryOp.>=,

nme.ZAND -> js.JSBinaryOp.&&,
nme.ZOR -> js.JSBinaryOp.||
nme.ADD -> (js.JSBinaryOp.+, true),
nme.SUB -> (js.JSBinaryOp.-, true),
nme.MUL -> (js.JSBinaryOp.*, true),
nme.DIV -> (js.JSBinaryOp./, true),
nme.MOD -> (js.JSBinaryOp.%, true),

nme.LSL -> (js.JSBinaryOp.<<, true),
nme.ASR -> (js.JSBinaryOp.>>, true),
nme.LSR -> (js.JSBinaryOp.>>>, true),
nme.OR -> (js.JSBinaryOp.|, true),
nme.AND -> (js.JSBinaryOp.&, true),
nme.XOR -> (js.JSBinaryOp.^, true),

nme.LT -> (js.JSBinaryOp.<, true),
nme.LE -> (js.JSBinaryOp.<=, true),
nme.GT -> (js.JSBinaryOp.>, true),
nme.GE -> (js.JSBinaryOp.>=, true),

nme.ZAND -> (js.JSBinaryOp.&&, true),
nme.ZOR -> (js.JSBinaryOp.||, true),

termName("**") -> (js.JSBinaryOp.**, false),
)

def unapply(name: TermName): Option[js.JSBinaryOp.Code] =
def unapply(name: TermName): Option[(js.JSBinaryOp.Code, Boolean)] =
map.get(name)
}
}
20 changes: 11 additions & 9 deletions compiler/test/dotty/tools/vulpix/ParallelTesting.scala
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,13 @@ trait ParallelTesting extends RunnerOrchestration:
def outDir: JFile
def flags: TestFlags
def sourceFiles: Array[JFile]
def checkFile: Option[JFile]
def checkFileBasePathCandidates: Array[String]

final def checkFile: Option[JFile] =
checkFileBasePathCandidates
.iterator
.flatMap(base => Iterator(new JFile(s"$base.$testPlatform.check"), new JFile(s"$base.check")))
.find(_.exists())

def runClassPath: String = outDir.getPath + JFile.pathSeparator + flags.runClassPath

Expand Down Expand Up @@ -186,9 +192,8 @@ trait ParallelTesting extends RunnerOrchestration:
) extends TestSource {
def sourceFiles: Array[JFile] = files.filter(isSourceFile)

def checkFile: Option[JFile] =
sourceFiles.map(f => new JFile(f.getPath.replaceFirst("\\.(scala|java)$", ".check")))
.find(_.exists())
def checkFileBasePathCandidates: Array[String] =
sourceFiles.map(f => f.getPath.replaceFirst("\\.(scala|java)$", ""))
}

/** A test source whose files will be compiled separately according to their
Expand Down Expand Up @@ -221,11 +226,8 @@ trait ParallelTesting extends RunnerOrchestration:

def sourceFiles = compilationGroups.map(_._2).flatten.toArray

def checkFile: Option[JFile] =
val platform =
if allToolArgs.getOrElse(ToolName.Target, Nil).nonEmpty then s".$testPlatform"
else ""
Some(new JFile(dir.getPath + platform + ".check")).filter(_.exists)
def checkFileBasePathCandidates: Array[String] =
Array(dir.getPath)
}

protected def shouldSkipTestSource(testSource: TestSource): Boolean = false
Expand Down
4 changes: 2 additions & 2 deletions project/ScalaLibraryPlugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import scala.jdk.CollectionConverters.*
import java.nio.file.Files
import xsbti.VirtualFileRef
import sbt.internal.inc.Stamper
import org.scalajs.sbtplugin.ScalaJSPlugin.autoImport.scalaJSVersion

object ScalaLibraryPlugin extends AutoPlugin {

override def trigger = noTrigger

private val scala2Version = "2.13.16"
private val scalaJSVersion = "1.19.0"
private val scala2Version = "2.13.16"

val fetchScala2ClassFiles = taskKey[(Set[File], File)]("Fetch the files to use that were compiled with Scala 2")
val fetchScala2SJSIR = taskKey[(Set[File], File)]("Fetch the .sjsir to use from Scala 2")
Expand Down
2 changes: 1 addition & 1 deletion project/plugins.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
libraryDependencySchemes +=
"org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always

addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.19.0")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.20.1")

addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.2.1")

Expand Down
8 changes: 8 additions & 0 deletions tests/run/i17761.scala-js.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Normal
test
WrappedVarArgs(class java.lang.String, int)
WrappedVarArgs(test, 42)
Transparent
test
ArraySeq(class java.lang.String, int)
ArraySeq(test, 42)
2 changes: 2 additions & 0 deletions tests/run/i3207.scala-js.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
WrappedVarArgs()
WrappedVarArgs(A, B)
1 change: 1 addition & 0 deletions tests/run/i768.check
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ArraySeq(a, bc)
4 changes: 1 addition & 3 deletions tests/run/i768.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ case class A(a: String*){

object Test {
def main(args: Array[String]) = {
assert(A("a", "bc").s == "ArraySeq(a, bc)")
println(A("a", "bc").s)
}
}


1 change: 1 addition & 0 deletions tests/run/i768.scala-js.check
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
WrappedVarArgs(a, bc)
1 change: 1 addition & 0 deletions tests/run/sammy_repeated.scala-js.check
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
WrappedVarArgs(1)
20 changes: 20 additions & 0 deletions tests/run/tagless.scala-js.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
5
(8 + (-(1 + 2)))
5
(7 + (-1 * 2))
35
7 * (8 + (-(1 + 2)))
tf1Tree = Node(Add,WrappedVarArgs(Node(Lit,WrappedVarArgs(Leaf(8))), Node(Neg,WrappedVarArgs(Node(Add,WrappedVarArgs(Node(Lit,WrappedVarArgs(Leaf(1))), Node(Lit,WrappedVarArgs(Leaf(2)))))))))
tfm1Tree = Node(Add,WrappedVarArgs(Node(Lit,WrappedVarArgs(Leaf(7))), Node(Neg,WrappedVarArgs(Node(Mult,WrappedVarArgs(Node(Lit,WrappedVarArgs(Leaf(1))), Node(Lit,WrappedVarArgs(Leaf(2)))))))))
2
Not a number: "X"
5
(8 + (-(1 + 2)))
Node(Add,WrappedVarArgs(Node(Lit,WrappedVarArgs(Leaf(8))), Node(Neg,WrappedVarArgs(Node(Add,WrappedVarArgs(Node(Lit,WrappedVarArgs(Leaf(1))), Node(Lit,WrappedVarArgs(Leaf(2)))))))))
5
(8 + (-(1 + 2)))
(8 + ((-1) + (-2)))
(8 + ((-1) + (-2)))
(8 + ((-1) + (-2)))
(7 + 1 * (-2))
7 * (8 + ((-1) + (-2)))
3 changes: 3 additions & 0 deletions tests/run/targetName.scala-js.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
strings: WrappedVarArgs(hello)
ints: WrappedVarArgs(1, 2)
objs: WrappedVarArgs(1, hi)
Loading