From ddee368309fbbb7a7d716af9e13ce295ac0ddd43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 2 Feb 2026 13:10:43 +0100 Subject: [PATCH 01/21] Tigten IR checking of NewLambda. --- .../main/scala/org/scalajs/linker/checker/IRChecker.scala | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/linker/shared/src/main/scala/org/scalajs/linker/checker/IRChecker.scala b/linker/shared/src/main/scala/org/scalajs/linker/checker/IRChecker.scala index 441759130a..5758ee515d 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/checker/IRChecker.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/checker/IRChecker.scala @@ -515,6 +515,14 @@ private final class IRChecker(linkTimeProperties: LinkTimeProperties, val closureType = ClosureType(descriptor.paramTypes, descriptor.resultType, nullable = false) typecheckExpect(fun, env, closureType) + val possibleTypes = (descriptor.superClass :: descriptor.interfaces).map { parent => + ClassType(parent, nullable = false, exact = false) + } + if (!possibleTypes.exists(isSubtype(_, tree.tpe))) { + val possibleTypesStr = possibleTypes.map(_.show()).mkString(", ") + reportError(i"illegal type for NewLambda: expected a supertype of one of $possibleTypesStr; got ${tree.tpe}") + } + case UnaryOp(UnaryOp.CheckNotNull, lhs) => // CheckNotNull accepts any closure type in addition to `AnyType` lhs.tpe match { From 4cb8c94d415d783c10d45084026cbeb898210c7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 16 Feb 2026 10:57:23 +0100 Subject: [PATCH 02/21] Fix #5145: Add {ClassDef,IR}Checker tests for NewLambda. --- .../org/scalajs/linker/IRCheckerTest.scala | 54 +++++++++++++++++++ .../linker/checker/ClassDefCheckerTest.scala | 34 ++++++++++++ 2 files changed, 88 insertions(+) diff --git a/linker/shared/src/test/scala/org/scalajs/linker/IRCheckerTest.scala b/linker/shared/src/test/scala/org/scalajs/linker/IRCheckerTest.scala index 58ae17e255..aed20585c6 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/IRCheckerTest.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/IRCheckerTest.scala @@ -389,6 +389,60 @@ class IRCheckerTest { testLinkNoIRError(classDefs, MainTestModuleInitializers, postOptimizer = true) } + @Test + def newLambda(): AsyncResult = await { + val ComparableClass = ClassName("java.lang.Comparable") + val ComparableType = ClassType(ComparableClass, nullable = false, exact = false) + + val descriptor = NewLambda.Descriptor( + ObjectClass, List(ComparableClass), m("compareTo", List(O), I), List(AnyType), IntType) + val closure = + Closure(ClosureFlags.typed, Nil, List(paramDef("that", AnyType)), None, IntType, int(0), Nil) + + val allowedTypeMsg = "a supertype of one of java.lang.Object!, java.lang.Comparable!" + + val result1 = for { + log <- testLinkIRErrors( + Seq(mainTestClassDef( + NewLambda(descriptor, closure.copy(resultType = AnyType))(ComparableType) + )), + MainTestModuleInitializers) + } yield { + log.assertContainsError("((any) => int)! expected but ((any) => any)! found") + } + + val result2 = for { + log <- testLinkIRErrors( + Seq(mainTestClassDef( + NewLambda(descriptor, closure)(ClassType(CloneableClass, nullable = false, exact = false)) + )), + MainTestModuleInitializers) + } yield { + log.assertContainsError( + s"illegal type for NewLambda: expected $allowedTypeMsg; got java.lang.Cloneable!") + } + + val result3 = for { + log <- testLinkIRErrors( + Seq(mainTestClassDef( + NewLambda(descriptor, closure)(ComparableType.copy(exact = true)) + )), + MainTestModuleInitializers) + } yield { + log.assertContainsError( + s"illegal type for NewLambda: expected $allowedTypeMsg; got =java.lang.Comparable!") + } + + // Using any! as super type is fine + val result4 = testLinkNoIRError( + Seq(mainTestClassDef( + NewLambda(descriptor, closure)(AnyNotNullType) + )), + MainTestModuleInitializers) + + Future.sequence(Seq(result1, result2, result3)) + } + @Test def badRecordSelect(): AsyncResult = await { val recordValue = RecordValue( diff --git a/linker/shared/src/test/scala/org/scalajs/linker/checker/ClassDefCheckerTest.scala b/linker/shared/src/test/scala/org/scalajs/linker/checker/ClassDefCheckerTest.scala index ea59be84aa..257cb1a41f 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/checker/ClassDefCheckerTest.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/checker/ClassDefCheckerTest.scala @@ -870,6 +870,40 @@ class ClassDefCheckerTest { previousPhase = CheckingPhase.Optimizer) } + @Test + def newLambdaTest(): Unit = { + val ComparableClass = ClassName("java.lang.Comparable") + val ComparableType = ClassType(ComparableClass, nullable = false, exact = false) + + val descriptor = NewLambda.Descriptor( + ObjectClass, List(ComparableClass), m("compareTo", List(O), I), List(AnyType), IntType) + val closure = + Closure(ClosureFlags.typed, Nil, List(paramDef("that", AnyType)), None, IntType, int(0), Nil) + + // Wrong Closure flags + assertError( + mainTestClassDef(NewLambda(descriptor, + closure.copy(flags = ClosureFlags.arrow, resultType = AnyType))(ComparableType)), + "The argument to a NewLambda must be a typed closure") + + // Not a closure + assertError( + mainTestClassDef(NewLambda(descriptor, int(0))(ComparableType)), + "The argument to a NewLambda must be a typed closure") + + // Something wrong in the Closure itself (smoke test to check that we check the Closure as a whole) + assertError( + mainTestClassDef(NewLambda(descriptor, + closure.copy(captureValues = List(int(0))))(ComparableType)), + "Mismatched size for captures: 0 params vs 1 values") + + // NewLambda is rejected after desugaring + assertError( + mainTestClassDef(NewLambda(descriptor, closure)(ComparableType)), + "Illegal NewLambda after desugaring", + previousPhase = CheckingPhase.Optimizer) + } + @Test def linkTimePropertyTest(): Unit = { // Test that some illegal types are rejected From 41e6063d22024502e2dfc2b8ffada79d22a34104 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 16 Feb 2026 11:43:39 +0100 Subject: [PATCH 03/21] Close #5311: Deprecate support for JDK < 17. We display a warning in two places: * when loading an sbt build with sbt-scalajs, and * when calling the `link` method of a `StandardLinkerImpl`. --- .../linker/standard/StandardLinkerImpl.scala | 26 +++++++++++++++++++ .../scala/tools/nsc/MainGenericRunner.scala | 2 +- .../org/scalajs/sbtplugin/ScalaJSPlugin.scala | 15 +++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/linker/shared/src/main/scala/org/scalajs/linker/standard/StandardLinkerImpl.scala b/linker/shared/src/main/scala/org/scalajs/linker/standard/StandardLinkerImpl.scala index 97771f1df0..7664d1c58f 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/standard/StandardLinkerImpl.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/standard/StandardLinkerImpl.scala @@ -26,6 +26,8 @@ private final class StandardLinkerImpl private ( frontend: LinkerFrontend, backend: LinkerBackend) extends LinkerImpl { + import StandardLinkerImpl._ + require(frontend.coreSpec == backend.coreSpec, "Frontend and backend must implement the same core specification") @@ -40,6 +42,8 @@ private final class StandardLinkerImpl private ( throw new IllegalStateException("Linker used concurrently") } + checkJDKVersion(logger) + checkValid() .flatMap { _ => frontend.link( @@ -64,4 +68,26 @@ private final class StandardLinkerImpl private ( object StandardLinkerImpl { def apply(frontend: LinkerFrontend, backend: LinkerBackend): Linker = new StandardLinkerImpl(frontend, backend) + + private lazy val deprecatedJDKVersion: Option[Int] = { + if (1.0.toString() == "1") { + // We're running on JS + None + } else { + val fullVersion = System.getProperty("java.version") + val v = fullVersion.stripPrefix("1.").takeWhile(_.isDigit).toInt + if (v < 17) + Some(v) + else + None + } + } + + private def checkJDKVersion(logger: Logger): Unit = { + for (v <- deprecatedJDKVersion) { + logger.warn( + s"Using the Scala.js Linker API on JDK $v is deprecated. " + + "A future minor version will require at least JDK 17.") + } + } } diff --git a/partest/src/main/scala/scala/tools/nsc/MainGenericRunner.scala b/partest/src/main/scala/scala/tools/nsc/MainGenericRunner.scala index 69e2ab3f69..492c992fe4 100644 --- a/partest/src/main/scala/scala/tools/nsc/MainGenericRunner.scala +++ b/partest/src/main/scala/scala/tools/nsc/MainGenericRunner.scala @@ -86,7 +86,7 @@ class MainGenericRunner { if (command.howToRun != AsObject) return errorFn("Scala.js runner can only run an object") - val logger = new ScalaConsoleLogger(Level.Warn) + val logger = new ScalaConsoleLogger(Level.Error) val semantics0 = readSemantics() val semantics = if (optMode == FullOpt) semantics0.optimized else semantics0 diff --git a/sbt-plugin/src/main/scala/org/scalajs/sbtplugin/ScalaJSPlugin.scala b/sbt-plugin/src/main/scala/org/scalajs/sbtplugin/ScalaJSPlugin.scala index 0f6fa25c00..0a6ff26882 100644 --- a/sbt-plugin/src/main/scala/org/scalajs/sbtplugin/ScalaJSPlugin.scala +++ b/sbt-plugin/src/main/scala/org/scalajs/sbtplugin/ScalaJSPlugin.scala @@ -372,6 +372,21 @@ object ScalaJSPlugin extends AutoPlugin { scalaJSLoggerFactory := Loggers.sbtLogger2ToolsLogger _, + // Show a deprecation message if we load the build on JDK < 17 + onLoad := { + onLoad.value.andThen { state => + val fullVersion = System.getProperty("java.version") + val v = fullVersion.stripPrefix("1.").takeWhile(_.isDigit).toInt + if (v < 17) { + sLog.value.warn( + s"Using sbt-scalajs on JDK $v is deprecated. " + + "A future minor version will require at least JDK 17." + ) + } + state + } + }, + // Clear the IR cache stats every time a sequence of tasks ends onComplete := { val prev = onComplete.value From 21a2387ef7424bcedc0aefe04c64df00f6a27d1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Thu, 19 Feb 2026 16:20:59 +0100 Subject: [PATCH 04/21] Add LinkingInfo.moduleKind as a link-time property. This can be used for code that must adapt to the module kind in a way that would not link otherwise. --- .../src/main/scala/org/scalajs/ir/Trees.scala | 1 + .../scala/scala/scalajs/LinkingInfo.scala | 31 +++++++++++++++++++ .../linker/frontend/LinkTimeProperties.scala | 29 ++++++++++------- .../testsuite/library/LinkTimeIfTest.scala | 5 +++ .../testsuite/library/LinkingInfoTest.scala | 18 ++++++++++- 5 files changed, 71 insertions(+), 13 deletions(-) diff --git a/ir/shared/src/main/scala/org/scalajs/ir/Trees.scala b/ir/shared/src/main/scala/org/scalajs/ir/Trees.scala index 81b43b57cc..dd24231856 100644 --- a/ir/shared/src/main/scala/org/scalajs/ir/Trees.scala +++ b/ir/shared/src/main/scala/org/scalajs/ir/Trees.scala @@ -1319,6 +1319,7 @@ object Trees { final val ProductionMode = "core/productionMode" final val ESVersion = "core/esVersion" final val UseECMAScript2015Semantics = "core/useECMAScript2015Semantics" + final val ModuleKind = "core/moduleKind" final val IsWebAssembly = "core/isWebAssembly" final val LinkerVersion = "core/linkerVersion" } diff --git a/library/src/main/scala/scala/scalajs/LinkingInfo.scala b/library/src/main/scala/scala/scalajs/LinkingInfo.scala index 94c075ac98..6b72515f15 100644 --- a/library/src/main/scala/scala/scalajs/LinkingInfo.scala +++ b/library/src/main/scala/scala/scalajs/LinkingInfo.scala @@ -224,6 +224,11 @@ object LinkingInfo { def useECMAScript2015Semantics: Boolean = linkTimePropertyBoolean("core/useECMAScript2015Semantics") + /** Kind of module structure emitted for the Scala.js output. */ + @inline @linkTimeProperty("core/moduleKind") + def moduleKind: Int = + linkTimePropertyInt("core/moduleKind") + /** Whether we are linking to WebAssembly. * * This property can be used to delegate to different code paths optimized @@ -368,6 +373,32 @@ object LinkingInfo { final val ES2021 = 12 } + /** Constants for the value of `moduleKind`. */ + object ModuleKind { + + /** No module structure. + * + * With this module kind, exports are stored on the global object. + * + * Imports are not supported. + */ + final val NoModule = 1 + + /** An ECMAScript 2015 module. + * + * Scala.js imports and exports directly map to `import` and `export` + * clauses in the ES module. + */ + final val ESModule = 2 + + /** A CommonJS module (notably used by Node.js). + * + * Imported modules are fetched with `require`. Exports go to the `exports` + * module-global variable. + */ + final val CommonJSModule = 3 + } + private[scalajs] def linkTimePropertyInt(name: String): Int = throw new java.lang.Error("stub") diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/LinkTimeProperties.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/LinkTimeProperties.scala index a6b44fc707..02fbdd76c4 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/LinkTimeProperties.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/LinkTimeProperties.scala @@ -16,27 +16,24 @@ import org.scalajs.ir.Trees.LinkTimeProperty._ import org.scalajs.ir.Types._ import org.scalajs.ir.ScalaJSVersions -import org.scalajs.linker.interface.{ESVersion => _, _} +import org.scalajs.linker.interface.{ESVersion => _, ModuleKind => _, _} import org.scalajs.linker.standard.CoreSpec final class LinkTimeProperties private ( semantics: Semantics, esFeatures: ESFeatures, + moduleKindInt: Int, targetIsWebAssembly: Boolean ) { import LinkTimeProperties._ private val linkTimeProperties: Map[String, LinkTimeValue] = Map( - ESVersion -> - LinkTimeInt(esFeatures.esVersion.edition), - UseECMAScript2015Semantics -> - LinkTimeBoolean(esFeatures.useECMAScript2015Semantics), - IsWebAssembly -> - LinkTimeBoolean(targetIsWebAssembly), - ProductionMode -> - LinkTimeBoolean(semantics.productionMode), - LinkerVersion -> - LinkTimeString(ScalaJSVersions.current) + ESVersion -> LinkTimeInt(esFeatures.esVersion.edition), + UseECMAScript2015Semantics -> LinkTimeBoolean(esFeatures.useECMAScript2015Semantics), + ModuleKind -> LinkTimeInt(moduleKindInt), + IsWebAssembly -> LinkTimeBoolean(targetIsWebAssembly), + ProductionMode -> LinkTimeBoolean(semantics.productionMode), + LinkerVersion -> LinkTimeString(ScalaJSVersions.current) ) def get(name: String): Option[LinkTimeValue] = @@ -56,7 +53,15 @@ object LinkTimeProperties { } def fromCoreSpec(coreSpec: CoreSpec): LinkTimeProperties = { + // These magic constants are mandated by the values in `scala.scalajs.LinkingInfo.ModuleKind`. + import org.scalajs.linker.interface.ModuleKind._ + val moduleKindInt = coreSpec.moduleKind match { + case NoModule => 1 + case ESModule => 2 + case CommonJSModule => 3 + } + new LinkTimeProperties(coreSpec.semantics, coreSpec.esFeatures, - coreSpec.targetIsWebAssembly) + moduleKindInt, coreSpec.targetIsWebAssembly) } } diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/library/LinkTimeIfTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/library/LinkTimeIfTest.scala index d7e1771765..ab54554a32 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/library/LinkTimeIfTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/library/LinkTimeIfTest.scala @@ -50,6 +50,11 @@ class LinkTimeIfTest { val cond = !(Platform.assumedESVersion < ESVersion.ES2015) assertEquals(cond, linkTimeIf(!(esVersion < ESVersion.ES2015))(true)(false)) } + + locally { + val hasModules = !Platform.isNoModule + assertEquals(hasModules, linkTimeIf(moduleKind == ModuleKind.NoModule)(false)(true)) + } } @Test def linkTimeIfNested(): Unit = { diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/library/LinkingInfoTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/library/LinkingInfoTest.scala index 50fc1fe8af..d724f5eaa3 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/library/LinkingInfoTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/library/LinkingInfoTest.scala @@ -13,7 +13,7 @@ package org.scalajs.testsuite.library import scala.scalajs.LinkingInfo -import scala.scalajs.LinkingInfo.ESVersion +import scala.scalajs.LinkingInfo.{ESVersion, ModuleKind} import org.junit.Assert._ import org.junit.Assume._ @@ -37,6 +37,15 @@ class LinkingInfoTest { @Test def useECMAScript2015Semantics(): Unit = assertEquals(Platform.useECMAScript2015Semantics, LinkingInfo.useECMAScript2015Semantics) + @Test def moduleKind(): Unit = { + if (Platform.isNoModule) + assertEquals(ModuleKind.NoModule, LinkingInfo.moduleKind) + else if (Platform.isESModule) + assertEquals(ModuleKind.ESModule, LinkingInfo.moduleKind) + else + assertEquals(ModuleKind.CommonJSModule, LinkingInfo.moduleKind) + } + @Test def isWebAssembly(): Unit = assertEquals(Platform.executingInWebAssembly, LinkingInfo.isWebAssembly) @@ -52,6 +61,13 @@ class LinkingInfoTest { assertEquals(12, ESVersion.ES2021) } + @Test def moduleKindConstants(): Unit = { + // The numeric values behind the constants should stay stable forever, so we test them. + assertEquals(1, ModuleKind.NoModule) + assertEquals(2, ModuleKind.ESModule) + assertEquals(3, ModuleKind.CommonJSModule) + } + @Test def isolatedJSLinkingInfo(): Unit = { val linkingInfo = scala.scalajs.runtime.linkingInfo assertEquals(Platform.isInProductionMode, linkingInfo.productionMode) From e133016f020bddab23f0279ebef663b3d35a5b57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Thu, 19 Feb 2026 16:38:09 +0100 Subject: [PATCH 05/21] Use `LinkingInfo.moduleKind` instead of config-dependent `ExportLoopback`. --- project/Build.scala | 6 +---- .../require-commonjs/ExportLoopback.scala | 25 ------------------- .../testsuite/jsinterop/ExportLoopback.scala | 22 ---------------- .../testsuite/jsinterop/ExportLoopback.scala | 22 ---------------- .../jsinterop/TopLevelExportsTest.scala | 21 ++++++++++++++-- 5 files changed, 20 insertions(+), 76 deletions(-) delete mode 100644 test-suite/js/src/test/require-commonjs/ExportLoopback.scala delete mode 100644 test-suite/js/src/test/require-esmodule/org/scalajs/testsuite/jsinterop/ExportLoopback.scala delete mode 100644 test-suite/js/src/test/require-no-modules/org/scalajs/testsuite/jsinterop/ExportLoopback.scala diff --git a/project/Build.scala b/project/Build.scala index 7c8cf70ab0..5d9fbf97b6 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -2287,16 +2287,12 @@ object Build { esVersion >= ESVersion.ES2017 && isWebAssembly) ::: includeIf(testDir / "require-modules", hasModules) ::: - includeIf(testDir / "require-no-modules", - !hasModules) ::: includeIf(testDir / "require-multi-modules", hasModules && !linkerConfig.closureCompiler && !isWebAssembly) ::: includeIf(testDir / "require-dynamic-import", moduleKind == ModuleKind.ESModule) ::: includeIf(testDir / "require-esmodule", - moduleKind == ModuleKind.ESModule) ::: - includeIf(testDir / "require-commonjs", - moduleKind == ModuleKind.CommonJSModule) + moduleKind == ModuleKind.ESModule) }, Test / unmanagedResourceDirectories ++= { diff --git a/test-suite/js/src/test/require-commonjs/ExportLoopback.scala b/test-suite/js/src/test/require-commonjs/ExportLoopback.scala deleted file mode 100644 index aeca2e8864..0000000000 --- a/test-suite/js/src/test/require-commonjs/ExportLoopback.scala +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Scala.js (https://www.scala-js.org/) - * - * Copyright EPFL. - * - * Licensed under Apache License 2.0 - * (https://www.apache.org/licenses/LICENSE-2.0). - * - * See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - */ - -package org.scalajs.testsuite.jsinterop - -import scala.scalajs.js - -import scala.concurrent.Future - -object ExportLoopback { - val exportsNamespace: Future[js.Dynamic] = { - js.Promise.resolve[Unit](()) - .`then`[js.Dynamic](_ => js.Dynamic.global.require("./main.js")) - .toFuture - } -} diff --git a/test-suite/js/src/test/require-esmodule/org/scalajs/testsuite/jsinterop/ExportLoopback.scala b/test-suite/js/src/test/require-esmodule/org/scalajs/testsuite/jsinterop/ExportLoopback.scala deleted file mode 100644 index b91a1bdbf8..0000000000 --- a/test-suite/js/src/test/require-esmodule/org/scalajs/testsuite/jsinterop/ExportLoopback.scala +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Scala.js (https://www.scala-js.org/) - * - * Copyright EPFL. - * - * Licensed under Apache License 2.0 - * (https://www.apache.org/licenses/LICENSE-2.0). - * - * See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - */ - -package org.scalajs.testsuite.jsinterop - -import scala.scalajs.js - -import scala.concurrent.Future - -object ExportLoopback { - val exportsNamespace: Future[js.Dynamic] = - js.`import`("./main.js").toFuture -} diff --git a/test-suite/js/src/test/require-no-modules/org/scalajs/testsuite/jsinterop/ExportLoopback.scala b/test-suite/js/src/test/require-no-modules/org/scalajs/testsuite/jsinterop/ExportLoopback.scala deleted file mode 100644 index 7a76610937..0000000000 --- a/test-suite/js/src/test/require-no-modules/org/scalajs/testsuite/jsinterop/ExportLoopback.scala +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Scala.js (https://www.scala-js.org/) - * - * Copyright EPFL. - * - * Licensed under Apache License 2.0 - * (https://www.apache.org/licenses/LICENSE-2.0). - * - * See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - */ - -package org.scalajs.testsuite.jsinterop - -import scala.scalajs.js - -import scala.concurrent.Future - -object ExportLoopback { - def exportsNamespace: Future[js.Dynamic] = - throw new AssertionError("attempted to get exportsNamsepace in NoModule mode") -} diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/TopLevelExportsTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/TopLevelExportsTest.scala index 66ae05a678..41c9179d81 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/TopLevelExportsTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/TopLevelExportsTest.scala @@ -35,8 +35,25 @@ import org.scalajs.junit.async._ class TopLevelExportsTest { /** The namespace in which top-level exports are stored. */ - private lazy val exportsNamespace: Future[js.Dynamic] = - ExportLoopback.exportsNamespace + private lazy val exportsNamespace: Future[js.Dynamic] = { + import scala.scalajs.LinkingInfo._ + + linkTimeIf[Future[js.Dynamic]](moduleKind == ModuleKind.NoModule) { + throw new AssertionError("attempted to get exportsNamsepace in NoModule mode") + } { + linkTimeIf[Future[js.Dynamic]](moduleKind == ModuleKind.ESModule) { + js.`import`("./main.js").toFuture + } { + linkTimeIf[Future[js.Dynamic]](moduleKind == ModuleKind.CommonJSModule) { + js.Promise.resolve[Unit](()) + .`then`[js.Dynamic](_ => js.Dynamic.global.require("./main.js")) + .toFuture + } { + throw new AssertionError(s"unknown module kind: ${moduleKind}") + } + } + } + } def witnessOf(obj: Any): Any = { assertTrue("" + obj.getClass(), obj.isInstanceOf[WitnessInterface]) From 186650bc4d96e51123001802647a656111fdf55e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Mar 2026 21:48:25 +0000 Subject: [PATCH 06/21] Bump @tootallnate/once and jsdom Removes [@tootallnate/once](https://github.com/TooTallNate/once). It's no longer used after updating ancestor dependency [jsdom](https://github.com/jsdom/jsdom). These dependencies need to be updated together. Removes `@tootallnate/once` Updates `jsdom` from 16.7.0 to 28.1.0 - [Release notes](https://github.com/jsdom/jsdom/releases) - [Changelog](https://github.com/jsdom/jsdom/blob/main/Changelog.md) - [Commits](https://github.com/jsdom/jsdom/compare/16.7.0...28.1.0) --- updated-dependencies: - dependency-name: "@tootallnate/once" dependency-version: dependency-type: indirect - dependency-name: jsdom dependency-version: 28.1.0 dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- package-lock.json | 1578 +++++++++++++++++++-------------------------- package.json | 2 +- 2 files changed, 656 insertions(+), 924 deletions(-) diff --git a/package-lock.json b/package-lock.json index c2f0693ef2..c1ab759c5f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,116 +6,228 @@ "": { "devDependencies": { "express": "4.22.1", - "jsdom": "16.7.0", + "jsdom": "28.1.0", "jszip": "3.8.0", "source-map-support": "0.5.19" } }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "node_modules/@acemir/cssom": { + "version": "0.9.31", + "resolved": "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.31.tgz", + "integrity": "sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==", + "dev": true + }, + "node_modules/@asamuzakjp/css-color": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.0.1.tgz", + "integrity": "sha512-2SZFvqMyvboVV1d15lMf7XiI3m7SDqXUuKaTymJYLN6dSGadqp+fVojqJlVoMlbZnlTmu3S0TLwLTJpvBMO1Aw==", "dev": true, + "dependencies": { + "@csstools/css-calc": "^3.1.1", + "@csstools/css-color-parser": "^4.0.2", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0", + "lru-cache": "^11.2.6" + }, "engines": { - "node": ">= 6" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, - "node_modules/abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "node_modules/@asamuzakjp/dom-selector": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.8.1.tgz", + "integrity": "sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==", + "dev": true, + "dependencies": { + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.1.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.2.6" + } + }, + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", "dev": true }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "node_modules/@bramus/specificity": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", + "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==", "dev": true, "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" + "css-tree": "^3.0.0" }, - "engines": { - "node": ">= 0.6" + "bin": { + "specificity": "bin/cli.js" } }, - "node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "node_modules/@csstools/color-helpers": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", + "integrity": "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==", "dev": true, - "bin": { - "acorn": "bin/acorn" - }, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], "engines": { - "node": ">=0.4.0" + "node": ">=20.19.0" } }, - "node_modules/acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "node_modules/@csstools/css-calc": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.1.1.tgz", + "integrity": "sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==", "dev": true, - "dependencies": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" } }, - "node_modules/acorn-globals/node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "node_modules/@csstools/css-color-parser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.0.2.tgz", + "integrity": "sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw==", "dev": true, - "bin": { - "acorn": "bin/acorn" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/color-helpers": "^6.0.2", + "@csstools/css-calc": "^3.1.1" }, "engines": { - "node": ">=0.4.0" + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" } }, - "node_modules/acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "node_modules/@csstools/css-parser-algorithms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", + "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], "engines": { - "node": ">=0.4.0" + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^4.0.0" } }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.0.29", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.29.tgz", + "integrity": "sha512-jx9GjkkP5YHuTmko2eWAvpPnb0mB4mGRr2U7XwVNwevm8nlpobZEVk+GNmiYMk2VuA75v+plfXWyroWKmICZXg==", "dev": true, - "dependencies": { - "debug": "4" - }, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ] + }, + "node_modules/@csstools/css-tokenizer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", + "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], "engines": { - "node": ">= 6.0.0" + "node": ">=20.19.0" } }, - "node_modules/agent-base/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/@exodus/bytes": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.0.tgz", + "integrity": "sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==", "dev": true, - "dependencies": { - "ms": "2.1.2" - }, "engines": { - "node": ">=6.0" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" }, "peerDependenciesMeta": { - "supports-color": { + "@noble/hashes": { "optional": true } } }, - "node_modules/agent-base/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "engines": { + "node": ">= 14" + } }, "node_modules/array-flatten": { "version": "1.1.1", @@ -123,11 +235,14 @@ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "dev": true }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dev": true, + "dependencies": { + "require-from-string": "^2.0.2" + } }, "node_modules/body-parser": { "version": "1.20.3", @@ -153,12 +268,6 @@ "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", - "dev": true - }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -203,18 +312,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -277,42 +374,45 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, - "node_modules/cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", - "dev": true + "node_modules/css-tree": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz", + "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==", + "dev": true, + "dependencies": { + "mdn-data": "2.12.2", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } }, "node_modules/cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-6.2.0.tgz", + "integrity": "sha512-Fm5NvhYathRnXNVndkUsCCuR63DCLVVwGOOwQw782coXFi5HhkXdu289l59HlXZBawsyNccXfWRYvLzcDCdDig==", "dev": true, "dependencies": { - "cssom": "~0.3.6" + "@asamuzakjp/css-color": "^5.0.1", + "@csstools/css-syntax-patches-for-csstree": "^1.0.28", + "css-tree": "^3.1.0", + "lru-cache": "^11.2.6" }, "engines": { - "node": ">=8" + "node": ">=20" } }, - "node_modules/cssstyle/node_modules/cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true - }, "node_modules/data-urls": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", - "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", + "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", "dev": true, "dependencies": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.0" }, "engines": { - "node": ">=10" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, "node_modules/debug": { @@ -325,26 +425,11 @@ } }, "node_modules/decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", "dev": true }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -364,27 +449,6 @@ "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", - "dev": true, - "dependencies": { - "webidl-conversions": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/domexception/node_modules/webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -414,6 +478,18 @@ "node": ">= 0.8" } }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -444,80 +520,12 @@ "node": ">= 0.4" } }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "dev": true }, - "node_modules/escodegen": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", - "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", - "dev": true, - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -608,12 +616,6 @@ } ] }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, "node_modules/finalhandler": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", @@ -632,22 +634,6 @@ "node": ">= 0.8" } }, - "node_modules/form-data": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.4.tgz", - "integrity": "sha512-f0cRzm6dkyVYV3nPoooP8XlccPQukegwhAnpoLcXy+X+A8KfpGOoXwDr9FLZd3wzgLaBGQBE3lY93Zm/i1JvIQ==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.35" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -736,21 +722,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -764,15 +735,15 @@ } }, "node_modules/html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", + "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", "dev": true, "dependencies": { - "whatwg-encoding": "^1.0.5" + "@exodus/bytes": "^1.6.0" }, "engines": { - "node": ">=10" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, "node_modules/http-errors": { @@ -792,26 +763,25 @@ } }, "node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dev": true, "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" + "agent-base": "^7.1.0", + "debug": "^4.3.4" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, "node_modules/http-proxy-agent/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -823,31 +793,31 @@ } }, "node_modules/http-proxy-agent/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "dev": true, "dependencies": { - "agent-base": "6", + "agent-base": "^7.1.2", "debug": "4" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, "node_modules/https-proxy-agent/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -859,9 +829,9 @@ } }, "node_modules/https-proxy-agent/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "node_modules/iconv-lite": { @@ -910,44 +880,38 @@ "dev": true }, "node_modules/jsdom": { - "version": "16.7.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", - "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", - "dev": true, - "dependencies": { - "abab": "^2.0.5", - "acorn": "^8.2.4", - "acorn-globals": "^6.0.0", - "cssom": "^0.4.4", - "cssstyle": "^2.3.0", - "data-urls": "^2.0.0", - "decimal.js": "^10.2.1", - "domexception": "^2.0.1", - "escodegen": "^2.0.0", - "form-data": "^3.0.0", - "html-encoding-sniffer": "^2.0.1", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", + "version": "28.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-28.1.0.tgz", + "integrity": "sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==", + "dev": true, + "dependencies": { + "@acemir/cssom": "^0.9.31", + "@asamuzakjp/dom-selector": "^6.8.1", + "@bramus/specificity": "^2.4.2", + "@exodus/bytes": "^1.11.0", + "cssstyle": "^6.0.1", + "data-urls": "^7.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^6.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.0", - "parse5": "6.0.1", - "saxes": "^5.0.1", + "parse5": "^8.0.0", + "saxes": "^6.0.0", "symbol-tree": "^3.2.4", - "tough-cookie": "^4.0.0", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.1.0", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.5.0", - "ws": "^7.4.6", - "xml-name-validator": "^3.0.0" + "tough-cookie": "^6.0.0", + "undici": "^7.21.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.1", + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.0", + "xml-name-validator": "^5.0.0" }, "engines": { - "node": ">=10" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "canvas": "^2.5.0" + "canvas": "^3.0.0" }, "peerDependenciesMeta": { "canvas": { @@ -967,19 +931,6 @@ "set-immediate-shim": "~1.0.1" } }, - "node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", - "dev": true, - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/lie": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", @@ -989,11 +940,14 @@ "immediate": "~3.0.5" } }, - "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", - "dev": true + "node_modules/lru-cache": { + "version": "11.2.6", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", + "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", + "dev": true, + "engines": { + "node": "20 || >=22" + } }, "node_modules/math-intrinsics": { "version": "1.1.0", @@ -1004,6 +958,12 @@ "node": ">= 0.4" } }, + "node_modules/mdn-data": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", + "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", + "dev": true + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -1079,12 +1039,6 @@ "node": ">= 0.6" } }, - "node_modules/nwsapi": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz", - "integrity": "sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==", - "dev": true - }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -1109,23 +1063,6 @@ "node": ">= 0.8" } }, - "node_modules/optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", @@ -1133,10 +1070,16 @@ "dev": true }, "node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", + "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==", + "dev": true, + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } }, "node_modules/parseurl": { "version": "1.3.3", @@ -1153,15 +1096,6 @@ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "dev": true }, - "node_modules/prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -1181,16 +1115,10 @@ "node": ">= 0.10" } }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dev": true - }, "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "engines": { "node": ">=6" @@ -1211,12 +1139,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true - }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -1256,11 +1178,14 @@ "util-deprecate": "~1.0.1" } }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, "node_modules/safe-buffer": { "version": "5.1.2", @@ -1275,15 +1200,15 @@ "dev": true }, "node_modules/saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", "dev": true, "dependencies": { "xmlchars": "^2.2.0" }, "engines": { - "node": ">=10" + "node": ">=v12.22.7" } }, "node_modules/send": { @@ -1436,6 +1361,15 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-support": { "version": "0.5.19", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", @@ -1461,13 +1395,31 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "dependencies": { - "safe-buffer": "~5.1.0" + "safe-buffer": "~5.1.0" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "node_modules/tldts": { + "version": "7.0.24", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.24.tgz", + "integrity": "sha512-1r6vQTTt1rUiJkI5vX7KG8PR342Ru/5Oh13kEQP2SMbRSZpOey9SrBe27IDxkoWulx8ShWu4K6C0BkctP8Z1bQ==", + "dev": true, + "dependencies": { + "tldts-core": "^7.0.24" + }, + "bin": { + "tldts": "bin/cli.js" } }, - "node_modules/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "node_modules/tldts-core": { + "version": "7.0.24", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.24.tgz", + "integrity": "sha512-pj7yygNMoMRqG7ML2SDQ0xNIOfN3IBDUcPVM2Sg6hP96oFNN2nqnzHreT3z9xLq85IWJyNTvD38O002DdOrPMw==", "dev": true }, "node_modules/toidentifier": { @@ -1480,42 +1432,27 @@ } }, "node_modules/tough-cookie": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", - "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.0.tgz", + "integrity": "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==", "dev": true, "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" + "tldts": "^7.0.5" }, "engines": { - "node": ">=6" + "node": ">=16" } }, "node_modules/tr46": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", - "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", - "dev": true, - "dependencies": { - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", "dev": true, "dependencies": { - "prelude-ls": "~1.1.2" + "punycode": "^2.3.1" }, "engines": { - "node": ">= 0.8.0" + "node": ">=20" } }, "node_modules/type-is": { @@ -1531,13 +1468,13 @@ "node": ">= 0.6" } }, - "node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "node_modules/undici": { + "version": "7.22.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.22.0.tgz", + "integrity": "sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==", "dev": true, "engines": { - "node": ">= 4.0.0" + "node": ">=20.18.1" } }, "node_modules/unpipe": { @@ -1549,16 +1486,6 @@ "node": ">= 0.8" } }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -1583,102 +1510,59 @@ "node": ">= 0.8" } }, - "node_modules/w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "deprecated": "Use your platform's native performance.now() and performance.timeOrigin.", - "dev": true, - "dependencies": { - "browser-process-hrtime": "^1.0.0" - } - }, "node_modules/w3c-xmlserializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", - "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", "dev": true, "dependencies": { - "xml-name-validator": "^3.0.0" + "xml-name-validator": "^5.0.0" }, "engines": { - "node": ">=10" + "node": ">=18" } }, "node_modules/webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", + "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", "dev": true, "engines": { - "node": ">=10.4" + "node": ">=20" } }, - "node_modules/whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "node_modules/whatwg-mimetype": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", + "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", "dev": true, - "dependencies": { - "iconv-lite": "0.4.24" + "engines": { + "node": ">=20" } }, - "node_modules/whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", - "dev": true - }, "node_modules/whatwg-url": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", - "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", + "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", "dev": true, "dependencies": { - "lodash": "^4.7.0", - "tr46": "^2.1.0", - "webidl-conversions": "^6.1.0" + "@exodus/bytes": "^1.11.0", + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.1" }, "engines": { - "node": ">=10" - } - }, - "node_modules/word-wrap": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", - "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, - "node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", "dev": true, "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } + "node": ">=18" } }, - "node_modules/xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", - "dev": true - }, "node_modules/xmlchars": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", @@ -1687,95 +1571,132 @@ } }, "dependencies": { - "@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "@acemir/cssom": { + "version": "0.9.31", + "resolved": "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.31.tgz", + "integrity": "sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==", "dev": true }, - "abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "@asamuzakjp/css-color": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.0.1.tgz", + "integrity": "sha512-2SZFvqMyvboVV1d15lMf7XiI3m7SDqXUuKaTymJYLN6dSGadqp+fVojqJlVoMlbZnlTmu3S0TLwLTJpvBMO1Aw==", + "dev": true, + "requires": { + "@csstools/css-calc": "^3.1.1", + "@csstools/css-color-parser": "^4.0.2", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0", + "lru-cache": "^11.2.6" + } + }, + "@asamuzakjp/dom-selector": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.8.1.tgz", + "integrity": "sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==", + "dev": true, + "requires": { + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.1.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.2.6" + } + }, + "@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", "dev": true }, - "accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "@bramus/specificity": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", + "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==", "dev": true, "requires": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" + "css-tree": "^3.0.0" } }, - "acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "@csstools/color-helpers": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", + "integrity": "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==", "dev": true }, - "acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "@csstools/css-calc": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.1.1.tgz", + "integrity": "sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==", + "dev": true, + "requires": {} + }, + "@csstools/css-color-parser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.0.2.tgz", + "integrity": "sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw==", "dev": true, "requires": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" - }, - "dependencies": { - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - } + "@csstools/color-helpers": "^6.0.2", + "@csstools/css-calc": "^3.1.1" } }, - "acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "@csstools/css-parser-algorithms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", + "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", + "dev": true, + "requires": {} + }, + "@csstools/css-syntax-patches-for-csstree": { + "version": "1.0.29", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.29.tgz", + "integrity": "sha512-jx9GjkkP5YHuTmko2eWAvpPnb0mB4mGRr2U7XwVNwevm8nlpobZEVk+GNmiYMk2VuA75v+plfXWyroWKmICZXg==", "dev": true }, - "agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "@csstools/css-tokenizer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", + "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", + "dev": true + }, + "@exodus/bytes": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.0.tgz", + "integrity": "sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==", + "dev": true, + "requires": {} + }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "dev": true, "requires": { - "debug": "4" - }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } + "mime-types": "~2.1.34", + "negotiator": "0.6.3" } }, + "agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true + }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "dev": true }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dev": true, + "requires": { + "require-from-string": "^2.0.2" + } }, "body-parser": { "version": "1.20.3", @@ -1797,12 +1718,6 @@ "unpipe": "1.0.0" } }, - "browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", - "dev": true - }, "buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -1835,15 +1750,6 @@ "get-intrinsic": "^1.3.0" } }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, "content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -1885,38 +1791,36 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, - "cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", - "dev": true + "css-tree": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz", + "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==", + "dev": true, + "requires": { + "mdn-data": "2.12.2", + "source-map-js": "^1.0.1" + } }, "cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-6.2.0.tgz", + "integrity": "sha512-Fm5NvhYathRnXNVndkUsCCuR63DCLVVwGOOwQw782coXFi5HhkXdu289l59HlXZBawsyNccXfWRYvLzcDCdDig==", "dev": true, "requires": { - "cssom": "~0.3.6" - }, - "dependencies": { - "cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true - } + "@asamuzakjp/css-color": "^5.0.1", + "@csstools/css-syntax-patches-for-csstree": "^1.0.28", + "css-tree": "^3.1.0", + "lru-cache": "^11.2.6" } }, "data-urls": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", - "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", + "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", "dev": true, "requires": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.0" } }, "debug": { @@ -1929,21 +1833,9 @@ } }, "decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", - "dev": true - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", "dev": true }, "depd": { @@ -1958,23 +1850,6 @@ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", "dev": true }, - "domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", - "dev": true, - "requires": { - "webidl-conversions": "^5.0.0" - }, - "dependencies": { - "webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "dev": true - } - } - }, "dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -1998,6 +1873,12 @@ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "dev": true }, + "entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true + }, "es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -2019,55 +1900,12 @@ "es-errors": "^1.3.0" } }, - "es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, - "requires": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - } - }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "dev": true }, - "escodegen": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", - "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", - "dev": true, - "requires": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -2130,12 +1968,6 @@ } } }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, "finalhandler": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", @@ -2151,19 +1983,6 @@ "unpipe": "~1.0.0" } }, - "form-data": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.4.tgz", - "integrity": "sha512-f0cRzm6dkyVYV3nPoooP8XlccPQukegwhAnpoLcXy+X+A8KfpGOoXwDr9FLZd3wzgLaBGQBE3lY93Zm/i1JvIQ==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.35" - } - }, "forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -2222,15 +2041,6 @@ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true }, - "has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, - "requires": { - "has-symbols": "^1.0.3" - } - }, "hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -2241,12 +2051,12 @@ } }, "html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", + "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", "dev": true, "requires": { - "whatwg-encoding": "^1.0.5" + "@exodus/bytes": "^1.6.0" } }, "http-errors": { @@ -2263,56 +2073,55 @@ } }, "http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dev": true, "requires": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" + "agent-base": "^7.1.0", + "debug": "^4.3.4" }, "dependencies": { "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "requires": { - "ms": "2.1.2" + "ms": "^2.1.3" } }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true } } }, "https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "dev": true, "requires": { - "agent-base": "6", + "agent-base": "^7.1.2", "debug": "4" }, "dependencies": { "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "requires": { - "ms": "2.1.2" + "ms": "^2.1.3" } }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true } } @@ -2357,38 +2166,32 @@ "dev": true }, "jsdom": { - "version": "16.7.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", - "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", + "version": "28.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-28.1.0.tgz", + "integrity": "sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==", "dev": true, "requires": { - "abab": "^2.0.5", - "acorn": "^8.2.4", - "acorn-globals": "^6.0.0", - "cssom": "^0.4.4", - "cssstyle": "^2.3.0", - "data-urls": "^2.0.0", - "decimal.js": "^10.2.1", - "domexception": "^2.0.1", - "escodegen": "^2.0.0", - "form-data": "^3.0.0", - "html-encoding-sniffer": "^2.0.1", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", + "@acemir/cssom": "^0.9.31", + "@asamuzakjp/dom-selector": "^6.8.1", + "@bramus/specificity": "^2.4.2", + "@exodus/bytes": "^1.11.0", + "cssstyle": "^6.0.1", + "data-urls": "^7.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^6.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.0", - "parse5": "6.0.1", - "saxes": "^5.0.1", + "parse5": "^8.0.0", + "saxes": "^6.0.0", "symbol-tree": "^3.2.4", - "tough-cookie": "^4.0.0", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.1.0", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.5.0", - "ws": "^7.4.6", - "xml-name-validator": "^3.0.0" + "tough-cookie": "^6.0.0", + "undici": "^7.21.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.1", + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.0", + "xml-name-validator": "^5.0.0" } }, "jszip": { @@ -2403,16 +2206,6 @@ "set-immediate-shim": "~1.0.1" } }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, "lie": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", @@ -2422,10 +2215,10 @@ "immediate": "~3.0.5" } }, - "lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "lru-cache": { + "version": "11.2.6", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", + "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", "dev": true }, "math-intrinsics": { @@ -2434,6 +2227,12 @@ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "dev": true }, + "mdn-data": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", + "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", + "dev": true + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -2485,12 +2284,6 @@ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "dev": true }, - "nwsapi": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz", - "integrity": "sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==", - "dev": true - }, "object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -2506,20 +2299,6 @@ "ee-first": "1.1.1" } }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - } - }, "pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", @@ -2527,10 +2306,13 @@ "dev": true }, "parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", + "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==", + "dev": true, + "requires": { + "entities": "^6.0.0" + } }, "parseurl": { "version": "1.3.3", @@ -2544,12 +2326,6 @@ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "dev": true }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", - "dev": true - }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -2566,16 +2342,10 @@ "ipaddr.js": "1.9.1" } }, - "psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dev": true - }, "punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true }, "qs": { @@ -2587,12 +2357,6 @@ "side-channel": "^1.0.6" } }, - "querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true - }, "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -2626,10 +2390,10 @@ "util-deprecate": "~1.0.1" } }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true }, "safe-buffer": { @@ -2645,9 +2409,9 @@ "dev": true }, "saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", "dev": true, "requires": { "xmlchars": "^2.2.0" @@ -2766,6 +2530,12 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, + "source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true + }, "source-map-support": { "version": "0.5.19", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", @@ -2797,6 +2567,21 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, + "tldts": { + "version": "7.0.24", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.24.tgz", + "integrity": "sha512-1r6vQTTt1rUiJkI5vX7KG8PR342Ru/5Oh13kEQP2SMbRSZpOey9SrBe27IDxkoWulx8ShWu4K6C0BkctP8Z1bQ==", + "dev": true, + "requires": { + "tldts-core": "^7.0.24" + } + }, + "tldts-core": { + "version": "7.0.24", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.24.tgz", + "integrity": "sha512-pj7yygNMoMRqG7ML2SDQ0xNIOfN3IBDUcPVM2Sg6hP96oFNN2nqnzHreT3z9xLq85IWJyNTvD38O002DdOrPMw==", + "dev": true + }, "toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -2804,33 +2589,21 @@ "dev": true }, "tough-cookie": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", - "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.0.tgz", + "integrity": "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==", "dev": true, "requires": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" + "tldts": "^7.0.5" } }, "tr46": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", - "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", - "dev": true, - "requires": { - "punycode": "^2.1.1" - } - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", "dev": true, "requires": { - "prelude-ls": "~1.1.2" + "punycode": "^2.3.1" } }, "type-is": { @@ -2843,10 +2616,10 @@ "mime-types": "~2.1.24" } }, - "universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "undici": { + "version": "7.22.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.22.0.tgz", + "integrity": "sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==", "dev": true }, "unpipe": { @@ -2855,16 +2628,6 @@ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "dev": true }, - "url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, - "requires": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -2883,73 +2646,42 @@ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "dev": true }, - "w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "dev": true, - "requires": { - "browser-process-hrtime": "^1.0.0" - } - }, "w3c-xmlserializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", - "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", "dev": true, "requires": { - "xml-name-validator": "^3.0.0" + "xml-name-validator": "^5.0.0" } }, "webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", + "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", "dev": true }, - "whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", - "dev": true, - "requires": { - "iconv-lite": "0.4.24" - } - }, "whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", + "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", "dev": true }, "whatwg-url": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", - "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", + "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", "dev": true, "requires": { - "lodash": "^4.7.0", - "tr46": "^2.1.0", - "webidl-conversions": "^6.1.0" + "@exodus/bytes": "^1.11.0", + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.1" } }, - "word-wrap": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", - "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", - "dev": true - }, - "ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", - "dev": true, - "requires": {} - }, "xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", "dev": true }, "xmlchars": { diff --git a/package.json b/package.json index 09c1bf4ac4..d3d1dc7b6a 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "private": true, "devDependencies": { "express": "4.22.1", - "jsdom": "16.7.0", + "jsdom": "28.1.0", "jszip": "3.8.0", "source-map-support": "0.5.19" } From 3ff16444469894817278a35b90e05cabeec1a7b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Sun, 8 Mar 2026 12:49:31 +0100 Subject: [PATCH 07/21] Fix #5331: Transform the body of a void typed closure as a statement. --- .../linker/frontend/optimizer/OptimizerCore.scala | 9 +++++---- .../scalajs/testsuite/compiler/RegressionTest.scala | 13 +++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/optimizer/OptimizerCore.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/optimizer/OptimizerCore.scala index 055988ff18..428d5b685b 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/optimizer/OptimizerCore.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/optimizer/OptimizerCore.scala @@ -834,7 +834,7 @@ private[optimizer] abstract class OptimizerCore( .withLocalDefs(paramLocalDefs) .withLocalDefs(restParamLocalDef.toList) - transformCapturingBody(captureParams, tcaptureValues, body, innerEnv) { + transformCapturingBody(captureParams, tcaptureValues, resultType, body, innerEnv) { (newCaptureParams, newCaptureValues, newBody) => val newClosure = { Closure(flags, newCaptureParams, newParams, newRestParam, resultType, @@ -845,7 +845,7 @@ private[optimizer] abstract class OptimizerCore( } private def transformCapturingBody(captureParams: List[ParamDef], - tcaptureValues: List[PreTransform], body: Tree, innerEnv: OptEnv)( + tcaptureValues: List[PreTransform], resultType: Type, body: Tree, innerEnv: OptEnv)( inner: (List[ParamDef], List[Tree], Tree) => PreTransTree)(cont: PreTransCont)( implicit scope: Scope, pos: Position): TailRec[Tree] = { /* Process captures. @@ -913,7 +913,7 @@ private[optimizer] abstract class OptimizerCore( val innerScope = scope.withEnv(innerEnv.withLocalDefs(captureParamLocalDefs.result())) - val newBody = transformExpr(body)(innerScope) + val newBody = transform(body, isStat = resultType == VoidType)(innerScope) withNewLocalDefs(captureValueBindings.result()) { (localDefs, cont1) => val (finalCaptureParams, finalCaptureValues) = (for { @@ -2540,7 +2540,8 @@ private[optimizer] abstract class OptimizerCore( val methodDef = getMethodBody(targetMethod) - transformCapturingBody(methodDef.args, targs, methodDef.body.get, OptEnv.Empty) { + transformCapturingBody(methodDef.args, targs, AnyType, + methodDef.body.get, OptEnv.Empty) { (newCaptureParams, newCaptureValues, newBody) => if (!importReplacement.used.value.isUsed) cancelFun() diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/RegressionTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/RegressionTest.scala index defc7ba819..57a7314c48 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/RegressionTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/RegressionTest.scala @@ -982,6 +982,19 @@ class RegressionTest { assertEquals("g", hide(b.bar)) } + @Test + def typedClosureWithStatementBodyMustBeTransformedAsStat_Issue5331(): Unit = { + final class Box(var x: Int) + + @noinline + def escapeAndCall(body: Runnable): Unit = body.run() + + val box = new Box(5) + val task: Runnable = () => box.x = 6 // SAM for Runnable + escapeAndCall(task) + assertEquals(6, box.x) + } + } object RegressionTest { From 218e64908e64566cf7d3029bf0f28f9a92955b30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 16 Feb 2026 14:22:30 +0100 Subject: [PATCH 08/21] Fix #5144: More direct hashing of method names for lambda class names. --- .../linker/frontend/LambdaSynthesizer.scala | 53 ++++++++--- .../frontend/LambdaSynthesizerTest.scala | 93 +++++++++++++++++++ 2 files changed, 135 insertions(+), 11 deletions(-) create mode 100644 linker/shared/src/test/scala/org/scalajs/linker/frontend/LambdaSynthesizerTest.scala diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/LambdaSynthesizer.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/LambdaSynthesizer.scala index 92ff3d20a5..8029f00cfb 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/LambdaSynthesizer.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/LambdaSynthesizer.scala @@ -43,17 +43,7 @@ private[linker] object LambdaSynthesizer { descriptor.superClass } - val digestBuilder = new SHA1.DigestBuilder() - digestBuilder.updateUTF8String(descriptor.superClass.encoded) - for (intf <- descriptor.interfaces) - digestBuilder.updateUTF8String(intf.encoded) - - // FIXME This is not efficient - digestBuilder.updateUTF8String(UTF8String(descriptor.methodName.nameString)) - - // No need the hash the paramTypes and resultType because they derive from the method name - - val digest = digestBuilder.finalizeDigest() + val digest = hashDescriptor(descriptor) /* The "$$Lambda" segment is meant to match the way LambdaMetaFactory * names generated classes. This is mostly for test compatibility @@ -69,6 +59,47 @@ private[linker] object LambdaSynthesizer { ClassName(baseClassName.encoded ++ UTF8String(suffixBuilder.toString())) } + private def hashDescriptor(descriptor: NewLambda.Descriptor): Array[Byte] = { + val digestBuilder = new SHA1.DigestBuilder() + + def updateInt(x: Int): Unit = { + digestBuilder.update((x >>> 24).toByte) + digestBuilder.update((x >>> 16).toByte) + digestBuilder.update((x >>> 8).toByte) + digestBuilder.update(x.toByte) + } + + def updateTypeRef(typeRef: TypeRef): Unit = typeRef match { + case typeRef: PrimRef => + digestBuilder.update(typeRef.charCode.toByte) + case ClassRef(className) => + digestBuilder.update('L'.toByte) + digestBuilder.updateUTF8String(className.encoded) + case ArrayTypeRef(base, dimensions) => + digestBuilder.update('['.toByte) + updateTypeRef(base) + updateInt(dimensions) + case TransientTypeRef(name) => + digestBuilder.update('t'.toByte) + digestBuilder.updateUTF8String(name.encoded) + } + + digestBuilder.updateUTF8String(descriptor.superClass.encoded) + updateInt(descriptor.interfaces.size) + for (intf <- descriptor.interfaces) + digestBuilder.updateUTF8String(intf.encoded) + + val methodName = descriptor.methodName + digestBuilder.updateUTF8String(methodName.simpleName.encoded) + updateInt(methodName.paramTypeRefs.size) + methodName.paramTypeRefs.foreach(updateTypeRef(_)) + updateTypeRef(methodName.resultTypeRef) + + // No need to hash the paramTypes and resultType because they derive from the method name + + digestBuilder.finalizeDigest() + } + /** Computes the constructor name for the lambda class of a descriptor. */ def makeConstructorName(descriptor: NewLambda.Descriptor): MethodName = { val closureTypeNonNull = diff --git a/linker/shared/src/test/scala/org/scalajs/linker/frontend/LambdaSynthesizerTest.scala b/linker/shared/src/test/scala/org/scalajs/linker/frontend/LambdaSynthesizerTest.scala new file mode 100644 index 0000000000..6719eeb30f --- /dev/null +++ b/linker/shared/src/test/scala/org/scalajs/linker/frontend/LambdaSynthesizerTest.scala @@ -0,0 +1,93 @@ +/* + * Scala.js (https://www.scala-js.org/) + * + * Copyright EPFL. + * + * Licensed under Apache License 2.0 + * (https://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package org.scalajs.linker.frontend + +import org.junit.Test +import org.junit.Assert._ + +import org.scalajs.ir.Names._ +import org.scalajs.ir.Trees.NewLambda.Descriptor +import org.scalajs.ir.Types._ +import org.scalajs.ir.WellKnownNames._ + +import org.scalajs.linker.testutils.TestIRBuilder._ + +class LambdaSynthesizerTest { + private def makeDesc(superClass: ClassName, interfaces: List[ClassName], + methodName: String, paramTypeRefs: List[TypeRef], resultTypeRef: TypeRef): Descriptor = { + + // Only for tests; would not work for JS class types + def typeRefToType(typeRef: TypeRef): Type = typeRef match { + case typeRef: PrimRef => typeRef.tpe + case ClassRef(className) => ClassType(className, nullable = true, exact = false) + case typeRef: ArrayTypeRef => ArrayType(typeRef, nullable = true, exact = false) + case typeRef: TransientTypeRef => typeRef.tpe + } + + Descriptor(superClass, interfaces, + MethodName(SimpleMethodName(methodName), paramTypeRefs, resultTypeRef), + paramTypeRefs.map(typeRefToType(_)), typeRefToType(resultTypeRef)) + } + + private def makeClassName(superClass: ClassName, interfaces: List[ClassName], + methodName: String, paramTypeRefs: List[TypeRef], resultTypeRef: TypeRef): String = { + val desc = makeDesc(superClass, interfaces, methodName, paramTypeRefs, resultTypeRef) + LambdaSynthesizer.makeClassName(desc).nameString + } + + @Test def testMakeClassNameBasicShape(): Unit = { + assertEquals( + "java.lang.Comparable.$$Lambda$fa13d0f5607243329b6dbf6698569d230ec3ead0", + makeClassName(ObjectClass, List("java.lang.Comparable"), "compareTo", List(O), I)) + + assertEquals( + "scala.runtime.AbstractFunction1.$$Lambda$7afc3dd0acc1681fb022ef921c83979087aaa919", + makeClassName("scala.runtime.AbstractFunction1", Nil, "apply", List(O), O)) + } + + @Test def testMakeClassNameEveryBitMatters(): Unit = { + val IClass = ClassRef("I") + val CClass = ClassRef("C") + + val descs: Vector[Descriptor] = Vector( + makeDesc(ObjectClass, List("I"), "foo", List(IClass), I), + makeDesc("A", List("I"), "foo", List(IClass), I), + makeDesc(ObjectClass, List("I"), "foo", List(IClass, CharRef), I), + makeDesc(ObjectClass, List("J"), "foo", List(IClass), I), + makeDesc(ObjectClass, List("I"), "bar", List(IClass), I), + makeDesc(ObjectClass, List("I"), "foo", List(CClass), I), + makeDesc(ObjectClass, List("I"), "foo", List(IClass), Z), + makeDesc(ObjectClass, List("I"), "foo", List(IClass), IClass), + makeDesc(ObjectClass, List("I"), "foo", List(CClass), IClass), + makeDesc(ObjectClass, List("I"), "foo", List(IClass), V), + makeDesc(ObjectClass, List("I"), "foo", List(IClass), ArrayTypeRef(I, 1)), + makeDesc(ObjectClass, List("I"), "foo", List(IClass), ArrayTypeRef(IClass, 1)), + makeDesc(ObjectClass, List("I"), "foo", List(IClass), ArrayTypeRef(I, 3)), + makeDesc(ObjectClass, List("I"), "foo", List(IClass), ArrayTypeRef(IClass, 3)), + makeDesc(ObjectClass, List("I"), "foo", List(IClass), TransientTypeRef(LabelName("I"))(IntType)) + ) + + val classNames = descs.map(LambdaSynthesizer.makeClassName(_)) + + for { + i <- 0 until descs.size + j <- i + 1 until descs.size + } { + if (classNames(i) == classNames(j)) { + fail( + "Two descriptors hashed to the same class name:\n" + + s"${descs(i)}\n${descs(j)}\n${classNames(i).nameString}") + } + } + } +} From 00b2d3f7a8e54fb501cef076bda9849585336e23 Mon Sep 17 00:00:00 2001 From: Rikito Taniguchi Date: Mon, 2 Mar 2026 23:51:14 +0900 Subject: [PATCH 09/21] Grow linear memory in malloc if required --- .../backend/wasmemitter/CoreWasmLib.scala | 36 ++++++++++++++++++- .../backend/webassembly/BinaryWriter.scala | 4 +++ .../backend/webassembly/Instructions.scala | 2 ++ .../backend/webassembly/TextWriter.scala | 4 +++ 4 files changed, 45 insertions(+), 1 deletion(-) diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/CoreWasmLib.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/CoreWasmLib.scala index 9e946dad1b..faec5326a1 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/CoreWasmLib.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/CoreWasmLib.scala @@ -973,6 +973,9 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { val alignment = 8 // max alignment? val base = fb.addLocal("base", Int32) + val requiredEnd = fb.addLocal("requiredEnd", Int32) + val currentMemEnd = fb.addLocal("currentMemEnd", Int32) + val pagesToGrow = fb.addLocal("pagesToGrow", Int32) fb += GlobalGet(genGlobalID.stackPointer) @@ -988,9 +991,40 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { fb += I32Mul fb += LocalTee(base) - fb += LocalGet(nbytes) // newPtr = stackPointer + nbytes + fb += LocalGet(nbytes) fb += I32Add + fb += LocalSet(requiredEnd) + // Grow linear memory on demand. + fb += MemorySize(genMemoryID.memory) + fb += I32Const(16) + fb += I32Shl // pages -> bytes + fb += LocalSet(currentMemEnd) + + fb += LocalGet(requiredEnd) + fb += LocalGet(currentMemEnd) + fb += I32GtU + fb.ifThen() { + // ceil((requiredEnd - currentMemEnd) / 65536) + fb += LocalGet(requiredEnd) + fb += LocalGet(currentMemEnd) + fb += I32Sub + fb += I32Const(65535) + fb += I32Add + fb += I32Const(65536) + fb += I32DivU + fb += LocalSet(pagesToGrow) + + fb += LocalGet(pagesToGrow) + fb += MemoryGrow(genMemoryID.memory) + fb += I32Const(-1) + fb += I32Eq + fb.ifThen() { + fb += Unreachable + } + } + + fb += LocalGet(requiredEnd) fb += GlobalSet(genGlobalID.stackPointer) fb += LocalGet(base) diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/webassembly/BinaryWriter.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/webassembly/BinaryWriter.scala index 23caf512cd..c60ee185b0 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/webassembly/BinaryWriter.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/webassembly/BinaryWriter.scala @@ -579,6 +579,10 @@ private sealed class BinaryWriter(module: Module, emitDebugInfo: Boolean) { case MemoryCopy(src, dst) => writeMemoryIdx(src) writeMemoryIdx(dst) + case MemorySize(memory) => + writeMemoryIdx(memory) + case MemoryGrow(memory) => + writeMemoryIdx(memory) case PositionMark(pos) => throw new AssertionError(s"Unexpected $instr") diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/webassembly/Instructions.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/webassembly/Instructions.scala index a8aa657774..f92ab32b24 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/webassembly/Instructions.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/webassembly/Instructions.scala @@ -228,6 +228,8 @@ object Instructions { case class I64tore8(arg: MemoryArg = MemoryArg()) extends LoadStoreInstr("i64.store8", 0x3c, arg) case class I64tore16(arg: MemoryArg = MemoryArg()) extends LoadStoreInstr("i64.store16", 0x3d, arg) case class I64tore32(arg: MemoryArg = MemoryArg()) extends LoadStoreInstr("i64.store32", 0x3e, arg) + final case class MemorySize(i: MemoryID) extends Instr("memory.size", 0x3f) + final case class MemoryGrow(i: MemoryID) extends Instr("memory.grow", 0x40) // Literals of primitive numeric types diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/webassembly/TextWriter.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/webassembly/TextWriter.scala index 4e96a9f72c..475631c51a 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/webassembly/TextWriter.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/webassembly/TextWriter.scala @@ -537,6 +537,10 @@ private class TextWriter(module: Module) { case MemoryCopy(src, dst) => appendName(src) appendName(dst) + case MemorySize(memory) => + appendName(memory) + case MemoryGrow(memory) => + appendName(memory) case PositionMark(_) => throw new AssertionError(s"Unexpected $instr") From 9dd12daa0b32ebba742d88b66a981e927df33d83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Thu, 12 Mar 2026 14:47:15 +0100 Subject: [PATCH 10/21] Fix the buffer growing logic in InputStream.readNBytes. The comparison was the wrong way around. On the first resize, we jumped straight to a buffer of size `min(Int.MaxValue, len)`. That was not too bad for `readNBytes` per se, but devastating for `readAllBytes`, which calls `readNBytes` with `len = Int.MaxValue`. --- javalib/src/main/scala/java/io/InputStream.scala | 2 +- .../scalajs/testsuite/javalib/io/InputStreamTestOnJDK11.scala | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/javalib/src/main/scala/java/io/InputStream.scala b/javalib/src/main/scala/java/io/InputStream.scala index 1d62ddb5f5..e494bf967e 100644 --- a/javalib/src/main/scala/java/io/InputStream.scala +++ b/javalib/src/main/scala/java/io/InputStream.scala @@ -75,7 +75,7 @@ abstract class InputStream extends Closeable { * - len <= Integer.MAX_VALUE (because of its type) */ val newLen = - if (Integer.MAX_VALUE / 2 > buf.length) Integer.MAX_VALUE + if (buf.length > Integer.MAX_VALUE / 2) Integer.MAX_VALUE else buf.length * 2 buf = Arrays.copyOf(buf, Math.min(len, newLen)) } diff --git a/test-suite/shared/src/test/require-jdk11/org/scalajs/testsuite/javalib/io/InputStreamTestOnJDK11.scala b/test-suite/shared/src/test/require-jdk11/org/scalajs/testsuite/javalib/io/InputStreamTestOnJDK11.scala index 3a588e79d2..94fb666f14 100644 --- a/test-suite/shared/src/test/require-jdk11/org/scalajs/testsuite/javalib/io/InputStreamTestOnJDK11.scala +++ b/test-suite/shared/src/test/require-jdk11/org/scalajs/testsuite/javalib/io/InputStreamTestOnJDK11.scala @@ -42,6 +42,7 @@ class InputStreamTestOnJDK11 { @Test def readAllBytes(): Unit = { assertBytesEqual(0 until 100, chunkedStream(10, 0 until 100).readAllBytes()) + assertBytesEqual(0 until 4000, chunkedStream(100, 0 until 4000).readAllBytes()) assertBytesEqual(Nil, emptyStream().readAllBytes()) } @@ -53,6 +54,7 @@ class InputStreamTestOnJDK11 { // test buffer growing assertBytesEqual(0 until 2000, chunkedStream(200, 0 until 2000).readNBytes(2000)) + assertBytesEqual(0 until 20000, chunkedStream(2000, 0 until 20000).readNBytes(20000)) assertThrows(classOf[IllegalArgumentException], emptyStream().readNBytes(-1)) } From 7c7d6d30ef2d02431b7524e36092e7d980c9d8ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Fri, 13 Mar 2026 16:19:42 +0100 Subject: [PATCH 11/21] Use dedicated ModuleKind's for Wasm without a JS environment. Instead of boolean configs in `WasmFeatures`. It makes more sense to use a `ModuleKind`, because it affects how the produced artifacts look like to the external world, which is exactly what a `ModuleKind` specifies. --- .github/workflows/ci.yml | 2 +- README.md | 2 +- .../scala/org/scalajs/ir/Serializers.scala | 8 +- .../src/main/scala/org/scalajs/ir/Trees.scala | 1 - .../src/main/scala/java/lang/Character.scala | 9 +- javalib/src/main/scala/java/lang/Double.scala | 9 +- javalib/src/main/scala/java/lang/Float.scala | 6 +- .../src/main/scala/java/lang/Integer.scala | 5 +- javalib/src/main/scala/java/lang/Math.scala | 39 +-- javalib/src/main/scala/java/lang/System.scala | 11 +- .../src/main/scala/java/lang/Throwables.scala | 14 +- .../src/main/scala/java/lang/_String.scala | 38 +-- .../src/main/scala/java/nio/ByteOrder.scala | 4 +- .../scala/java/nio/charset/CoderResult.scala | 16 +- .../src/main/scala/java/util/Formatter.scala | 4 +- .../main/scala/java/util/regex/Engine.scala | 4 +- .../main/scala/java/util/regex/Pattern.scala | 4 +- .../java/util/regex/PatternCompiler.scala | 13 +- .../scala/java/util/regex/RegExpImpl.scala | 7 +- .../src/main/scala/org/junit/Assert.scala | 15 +- .../scala/scala/scalajs/LinkingInfo.scala | 10 +- .../concurrent/QueueExecutionContext.scala | 5 +- .../scalajs/linker/interface/ModuleKind.scala | 23 +- .../org/scalajs/linker/interface/Report.scala | 10 +- .../linker/interface/WasmFeatures.scala | 26 +- .../scalajs/linker/analyzer/Analysis.scala | 2 +- .../scalajs/linker/analyzer/Analyzer.scala | 12 +- .../scalajs/linker/analyzer/InfoLoader.scala | 4 +- .../org/scalajs/linker/analyzer/Infos.scala | 18 +- .../backend/WebAssemblyLinkerBackend.scala | 7 +- .../backend/wasmemitter/ClassEmitter.scala | 50 ++-- .../backend/wasmemitter/CoreWasmLib.scala | 277 ++++++++++-------- .../backend/wasmemitter/DerivedClasses.scala | 4 +- .../linker/backend/wasmemitter/Emitter.scala | 13 +- .../backend/wasmemitter/FunctionEmitter.scala | 170 +++++------ .../backend/wasmemitter/LoaderContent.scala | 6 +- .../linker/backend/wasmemitter/SWasmGen.scala | 27 +- .../backend/wasmemitter/SpecialNames.scala | 1 - .../backend/wasmemitter/TypeTransformer.scala | 4 +- .../linker/backend/wasmemitter/VarGen.scala | 15 +- .../backend/wasmemitter/WasmContext.scala | 9 +- .../linker/frontend/LinkTimeProperties.scala | 11 +- .../frontend/optimizer/OptimizerCore.scala | 13 +- project/Build.scala | 69 +++-- project/JavalibIRCleaner.scala | 4 - .../sbtplugin/ScalaJSPluginInternal.scala | 6 +- .../scala/collection/mutable/Buffer.scala | 2 +- scalalib/overrides-2.13/scala/Symbol.scala | 9 +- .../scala/collection/mutable/Buffer.scala | 4 +- scalalib/overrides/scala/Symbol.scala | 9 +- .../scala/runtime/BoxesRunTime.scala | 5 +- .../org/scalajs/testing/bridge/Bridge.scala | 19 +- .../org/scalajs/testing/bridge/JSRPC.scala | 8 +- .../scalajs/testsuite/utils/BuildInfo.scala | 2 + .../scalajs/testsuite/utils/Platform.scala | 11 +- .../testsuite/library/LinkTimeIfTest.scala | 11 +- .../scala/scala/scalajs/LinkingInfo.scala | 7 +- .../testsuite/compiler/RegressionTest.scala | 4 +- .../testsuite/javalib/lang/LongTest.scala | 4 +- .../testsuite/javalib/lang/MathTest.scala | 10 +- .../testsuite/javalib/lang/ThreadTest.scala | 4 +- .../testsuite/javalib/util/BitSetTest.scala | 4 +- 62 files changed, 604 insertions(+), 516 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a0b4eb9bcb..9636d8bef6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -81,7 +81,7 @@ jobs: esVersion: [ES2015, ES2021] # Some javalib features depend on the target ES version eh-support: [true, false] env: - SBT_SET_COMMAND: 'set Seq(Global/enableWasmEverywhere := true, ThisBuild/scalaJSLinkerConfig ~= (_.withESFeatures(_.withESVersion(ESVersion.${{ matrix.esVersion }})).withWasmFeatures(_.withTargetPureWasm(true).withExceptionHandling(${{ matrix.eh-support }}))))' + SBT_SET_COMMAND: 'set Seq(Global/enableWasmEverywhere := true, ThisBuild/scalaJSLinkerConfig ~= (_.withESFeatures(_.withESVersion(ESVersion.${{ matrix.esVersion }})).withModuleKind(ModuleKind.MinimalWasmModule).withWasmFeatures(_.withExceptionHandling(${{ matrix.eh-support }}))))' steps: - uses: actions/checkout@v4 - uses: actions/setup-java@v4 diff --git a/README.md b/README.md index d0ed6fd58a..1fb3836265 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ This is a friendly fork of Scala.js, targeting stand-alone Wasm runtimes such as ### `test-suite` ```sh sbt:Scala.js> set Global/enableWasmEverywhere := true -sbt:Scala.js> set scalaJSLinkerConfig in testSuite.v2_12 ~= (_.withWasmFeatures(_.withTargetPureWasm(true))) +sbt:Scala.js> set scalaJSLinkerConfig in testSuite.v2_12 ~= (_.withModuleKind(ModuleKind.MinimalWasmModule)) sbt:Scala.js> testSuite2_12/test ``` diff --git a/ir/shared/src/main/scala/org/scalajs/ir/Serializers.scala b/ir/shared/src/main/scala/org/scalajs/ir/Serializers.scala index d632f06f95..b78f8c366e 100644 --- a/ir/shared/src/main/scala/org/scalajs/ir/Serializers.scala +++ b/ir/shared/src/main/scala/org/scalajs/ir/Serializers.scala @@ -30,8 +30,7 @@ import LinkTimeProperty.{ ESVersion, UseECMAScript2015Semantics, IsWebAssembly, - LinkerVersion, - TargetPureWasm + LinkerVersion } import Types._ import Tags._ @@ -1590,8 +1589,6 @@ object Serializers { LinkTimeProperty(LinkerVersion)(StringType) case StringLiteral("fileLevelThis") => JSGlobalRef(JSGlobalRef.FileLevelThis) - case StringLiteral("targetPureWasm") => - LinkTimeProperty(TargetPureWasm)(BooleanType) case otherItem => JSSelect(jsLinkingInfo, otherItem) } @@ -1628,8 +1625,7 @@ object Serializers { LinkTimeProperty(UseECMAScript2015Semantics)(BooleanType)), (StringLiteral("isWebAssembly"), LinkTimeProperty(IsWebAssembly)(BooleanType)), (StringLiteral("linkerVersion"), LinkTimeProperty(LinkerVersion)(StringType)), - (StringLiteral("fileLevelThis"), JSGlobalRef(JSGlobalRef.FileLevelThis)), - (StringLiteral("targetPureWasm"), LinkTimeProperty(TargetPureWasm)(BooleanType)) + (StringLiteral("fileLevelThis"), JSGlobalRef(JSGlobalRef.FileLevelThis)) )) } else { throw new IOException( diff --git a/ir/shared/src/main/scala/org/scalajs/ir/Trees.scala b/ir/shared/src/main/scala/org/scalajs/ir/Trees.scala index 7f9e45fbb7..440d8ec4b9 100644 --- a/ir/shared/src/main/scala/org/scalajs/ir/Trees.scala +++ b/ir/shared/src/main/scala/org/scalajs/ir/Trees.scala @@ -1327,7 +1327,6 @@ object Trees { final val ModuleKind = "core/moduleKind" final val IsWebAssembly = "core/isWebAssembly" final val LinkerVersion = "core/linkerVersion" - final val TargetPureWasm = "core/targetPureWasm" } // Atomic expressions diff --git a/javalib/src/main/scala/java/lang/Character.scala b/javalib/src/main/scala/java/lang/Character.scala index 4d040ac9dc..639a5f98ef 100644 --- a/javalib/src/main/scala/java/lang/Character.scala +++ b/javalib/src/main/scala/java/lang/Character.scala @@ -21,7 +21,8 @@ import scala.annotation.{tailrec, switch} import scala.scalajs.js import scala.scalajs.LinkingInfo -import scala.scalajs.LinkingInfo.ESVersion +import scala.scalajs.LinkingInfo.{ESVersion, moduleKind} +import scala.scalajs.LinkingInfo.ModuleKind.{MinimalWasmModule, WasmComponent} import java.lang.constant.Constable import java.util.{ArrayList, Arrays, HashMap} @@ -132,7 +133,7 @@ object Character { if (!isValidCodePoint(codePoint)) throw new IllegalArgumentException() - LinkingInfo.linkTimeIf(LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { if (isBmpCodePoint(codePoint)) { Character.toString(codePoint.toChar) } else { @@ -723,7 +724,7 @@ object Character { case _ => // In WASI implementation, we cannot use String#toUpperCase // since it uses Character#toUpperCase. - LinkingInfo.linkTimeIf(LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { import CaseUtil._ toCase(codePoint, a, z, lowerBeta, lowerRanges, lowerDeltas, lowerSteps) } { @@ -752,7 +753,7 @@ object Character { case 0x0130 => 0x0069 // İ => i case _ => - LinkingInfo.linkTimeIf(LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { // in pure Wasm implementation, we cannot use String#toLowerCase // since it uses Character$toLowerCase import CaseUtil._ diff --git a/javalib/src/main/scala/java/lang/Double.scala b/javalib/src/main/scala/java/lang/Double.scala index b3713a667d..9483076671 100644 --- a/javalib/src/main/scala/java/lang/Double.scala +++ b/javalib/src/main/scala/java/lang/Double.scala @@ -17,7 +17,8 @@ import java.util.regex.RegExpImpl import scala.scalajs.js import scala.scalajs.LinkingInfo -import scala.scalajs.LinkingInfo.linkTimeIf +import scala.scalajs.LinkingInfo.{linkTimeIf, moduleKind} +import scala.scalajs.LinkingInfo.ModuleKind.{MinimalWasmModule, WasmComponent} import Utils._ @@ -127,7 +128,7 @@ object Double { import RegExpImpl.impl val groups = impl.exec(doubleStrPat, s) if (impl.matches(groups)) { - linkTimeIf(LinkingInfo.targetPureWasm) { + linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { parseDoubleWasm(s, groups) } { js.Dynamic.global.parseFloat(impl.get(groups, 1)).asInstanceOf[scala.Double] @@ -289,7 +290,7 @@ object Double { @inline def nativeParseInt(s: String, radix: Int): scala.Double = js.Dynamic.global.parseInt(s, radix).asInstanceOf[scala.Double] - val mantissa = linkTimeIf(LinkingInfo.targetPureWasm) { + val mantissa = linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { val mantissaLong = Long.parseUnsignedLong(truncatedMantissaStr, 16) // convert unsigned long to double (mantissaLong >>> 32).toDouble * (1L << 32) + (mantissaLong & 0xffffffffL).toDouble @@ -298,7 +299,7 @@ object Double { } // Assert: mantissa != 0.0 && mantissa != scala.Double.PositiveInfinity - val binaryExp = linkTimeIf(LinkingInfo.targetPureWasm) { + val binaryExp = linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { if (binaryExpStr.length() > 11) { if (binaryExpStr.charAt(0) == '-') Int.MinValue else Int.MaxValue diff --git a/javalib/src/main/scala/java/lang/Float.scala b/javalib/src/main/scala/java/lang/Float.scala index 33cb1be8f1..ae0ee5281c 100644 --- a/javalib/src/main/scala/java/lang/Float.scala +++ b/javalib/src/main/scala/java/lang/Float.scala @@ -16,7 +16,9 @@ import java.lang.constant.{Constable, ConstantDesc} import java.util.regex.{Matcher, Pattern, RegExpImpl} import scala.scalajs.js -import scala.scalajs.LinkingInfo._ +import scala.scalajs.LinkingInfo +import scala.scalajs.LinkingInfo.{ESVersion, esVersion, linkTimeIf, moduleKind} +import scala.scalajs.LinkingInfo.ModuleKind.{MinimalWasmModule, WasmComponent} /* This is a hijacked class. Its instances are primitive numbers. * Constructors are not emitted. @@ -150,7 +152,7 @@ object Float { private def parseFloatDecimal(fullNumberStr: String, integralPartStr: String, fractionalPartStr: String, exponentStr: String): scala.Float = { - linkTimeIf[scala.Float](targetPureWasm) { + linkTimeIf[scala.Float](moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { parseFloatDecimalWasm(fullNumberStr, integralPartStr, fractionalPartStr, exponentStr) } { parseFloatDecimalJS(fullNumberStr, integralPartStr, fractionalPartStr, exponentStr) diff --git a/javalib/src/main/scala/java/lang/Integer.scala b/javalib/src/main/scala/java/lang/Integer.scala index 791548f230..1742bb65a6 100644 --- a/javalib/src/main/scala/java/lang/Integer.scala +++ b/javalib/src/main/scala/java/lang/Integer.scala @@ -17,7 +17,8 @@ import java.util.function._ import scala.scalajs.js import scala.scalajs.LinkingInfo -import scala.scalajs.LinkingInfo.ESVersion +import scala.scalajs.LinkingInfo.{ESVersion, moduleKind} +import scala.scalajs.LinkingInfo.ModuleKind.{MinimalWasmModule, WasmComponent} /* This is a hijacked class. Its instances are primitive numbers. * Constructors are not emitted. @@ -339,7 +340,7 @@ object Integer { @inline def min(a: Int, b: Int): Int = Math.min(a, b) @inline private[this] def toStringBase(i: scala.Int, base: scala.Int): String = { - LinkingInfo.linkTimeIf(LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { toStringWasmGenericImpl(i, base, false) } { import js.JSNumberOps.enableJSNumberOps diff --git a/javalib/src/main/scala/java/lang/Math.scala b/javalib/src/main/scala/java/lang/Math.scala index 15e80307e2..5be460731b 100644 --- a/javalib/src/main/scala/java/lang/Math.scala +++ b/javalib/src/main/scala/java/lang/Math.scala @@ -29,7 +29,8 @@ import scala.scalajs.js import js.Dynamic.{global => g} import scala.scalajs.LinkingInfo -import scala.scalajs.LinkingInfo.{ESVersion, linkTimeIf} +import scala.scalajs.LinkingInfo.{ESVersion, linkTimeIf, moduleKind} +import scala.scalajs.LinkingInfo.ModuleKind.{MinimalWasmModule, WasmComponent} object Math { final val E = 2.718281828459045 @@ -53,7 +54,7 @@ object Math { // Wasm intrinsics @inline def abs(a: scala.Float): scala.Float = { - linkTimeIf(LinkingInfo.targetPureWasm) { + linkTimeIf(LinkingInfo.isWebAssembly) { Float.intBitsToFloat(Float.floatToIntBits(a) & ~Int.MinValue) } { js.Math.abs(a).toFloat @@ -61,7 +62,7 @@ object Math { } @inline def abs(a: scala.Double): scala.Double = { - linkTimeIf(LinkingInfo.targetPureWasm) { + linkTimeIf(LinkingInfo.isWebAssembly) { Double.longBitsToDouble(Double.doubleToLongBits(a) & ~scala.Long.MinValue) } { js.Math.abs(a) @@ -73,7 +74,7 @@ object Math { // Wasm intrinsics @inline def max(a: scala.Float, b: scala.Float): scala.Float = { - linkTimeIf(LinkingInfo.targetPureWasm) { + linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { if (a != a || b != b) { Float.NaN } else if (a == 0.0f && b == 0.0f) { @@ -90,7 +91,7 @@ object Math { } @inline def max(a: scala.Double, b: scala.Double): scala.Double = { - linkTimeIf(LinkingInfo.targetPureWasm) { + linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { if (a != a || b != b) { Double.NaN } else if (a == 0.0 && b == 0.0) { @@ -111,7 +112,7 @@ object Math { // Wasm intrinsics @inline def min(a: scala.Float, b: scala.Float): scala.Float = { - linkTimeIf(LinkingInfo.targetPureWasm) { + linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { if (a != a || b != b) { Float.NaN } else if (a == 0.0f && b == 0.0f) { @@ -128,7 +129,7 @@ object Math { } @inline def min(a: scala.Double, b: scala.Double): scala.Double = { - linkTimeIf(LinkingInfo.targetPureWasm) { + linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { if (a != a || b != b) { Double.NaN } else if (a == 0.0 && b == 0.0) { @@ -189,7 +190,7 @@ object Math { // Wasm intrinsics @inline def ceil(a: scala.Double): scala.Double = { - linkTimeIf(LinkingInfo.targetPureWasm) { + linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { -floor(-a) } { js.Math.ceil(a) @@ -197,7 +198,7 @@ object Math { } @inline def floor(a: scala.Double): scala.Double = { - linkTimeIf(LinkingInfo.targetPureWasm) { + linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { floorWasm(a) } { js.Math.floor(a) @@ -278,7 +279,7 @@ object Math { } @inline def round(a: scala.Float): scala.Int = { - linkTimeIf(LinkingInfo.targetPureWasm) { + linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { if (Float.isNaN(a)) { 0 } else if (a <= Int.MinValue.toFloat) { @@ -294,7 +295,7 @@ object Math { } @inline def round(a: scala.Double): scala.Long = { - linkTimeIf(LinkingInfo.targetPureWasm) { + linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { if (Double.isNaN(a)) { 0L } else if (a <= scala.Long.MinValue.toDouble) { @@ -311,7 +312,7 @@ object Math { // Wasm intrinsic @inline def sqrt(a: scala.Double): scala.Double = { - linkTimeIf(LinkingInfo.targetPureWasm) { + linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { StrictMath.sqrt(a) } { js.Math.sqrt(a) @@ -319,7 +320,7 @@ object Math { } @inline def pow(a: scala.Double, b: scala.Double): scala.Double = { - linkTimeIf(LinkingInfo.targetPureWasm) { + linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { StrictMath.pow(a, b) } { js.Math.pow(a, b) @@ -329,7 +330,7 @@ object Math { @inline def exp(a: scala.Double): scala.Double = js.Math.exp(a) @inline def log(a: scala.Double): scala.Double = { - linkTimeIf(LinkingInfo.targetPureWasm) { + linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { StrictMath.log(a) } { js.Math.log(a) @@ -337,7 +338,7 @@ object Math { } @inline def log10(a: scala.Double): scala.Double = { - linkTimeIf(LinkingInfo.targetPureWasm) { + linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { StrictMath.log10(a) } { if (assumingES6 || !Utils.isUndefined(g.Math.log10)) @@ -348,7 +349,7 @@ object Math { } @inline def log1p(a: scala.Double): scala.Double = { - linkTimeIf(LinkingInfo.targetPureWasm) { + linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { StrictMath.log1p(a) } { if (assumingES6 || !Utils.isUndefined(g.Math.log1p)) @@ -367,7 +368,7 @@ object Math { @inline def atan2(y: scala.Double, x: scala.Double): scala.Double = js.Math.atan2(y, x) @inline def random(): scala.Double = { - linkTimeIf(LinkingInfo.targetPureWasm) { + linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { WasmSystem.random() } { js.Math.random() @@ -405,7 +406,7 @@ object Math { } def cbrt(a: scala.Double): scala.Double = { - linkTimeIf(LinkingInfo.targetPureWasm) { + linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { cbrtImpl(a) } { if (assumingES6 || !Utils.isUndefined(g.Math.cbrt)) { @@ -676,7 +677,7 @@ object Math { } def hypot(a: scala.Double, b: scala.Double): scala.Double = { - linkTimeIf(LinkingInfo.targetPureWasm) { + linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { hypotImpl(a, b) } { if (assumingES6 || !Utils.isUndefined(g.Math.hypot)) { diff --git a/javalib/src/main/scala/java/lang/System.scala b/javalib/src/main/scala/java/lang/System.scala index 4498e25cbb..a8627c0d83 100644 --- a/javalib/src/main/scala/java/lang/System.scala +++ b/javalib/src/main/scala/java/lang/System.scala @@ -17,6 +17,8 @@ import java.io._ import scala.scalajs.js import scala.scalajs.js.Dynamic.global import scala.scalajs.LinkingInfo +import scala.scalajs.LinkingInfo.{ESVersion, moduleKind} +import scala.scalajs.LinkingInfo.ModuleKind.{MinimalWasmModule, WasmComponent} import java.{util => ju} import java.util.function._ @@ -68,7 +70,7 @@ object System { @inline def currentTimeMillis(): scala.Long = { - LinkingInfo.linkTimeIf(LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { WasmSystem.currentTimeMillis() } { js.Date.now().toLong @@ -86,7 +88,7 @@ object System { @inline def nanoTime(): scala.Long = { - LinkingInfo.linkTimeIf(LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { WasmSystem.nanoTime() } { (NanoTime.highPrecisionTimer.now().asInstanceOf[scala.Double] * 1000000).toLong @@ -192,7 +194,8 @@ object System { private object SystemProperties { private val storageImpl: StorageImpl = { - LinkingInfo.linkTimeIf[StorageImpl](LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf[StorageImpl]( + moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { StorageImpl.HashMapStorageImpl } { StorageImpl.DictStorageImpl @@ -460,7 +463,7 @@ private final class JSConsoleBasedPrintStream(isErr: scala.Boolean) override def close(): Unit = () private def doWriteLine(line: String): Unit = { - LinkingInfo.linkTimeIf(LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { WasmSystem.print(line) } { import js.DynamicImplicits.truthValue diff --git a/javalib/src/main/scala/java/lang/Throwables.scala b/javalib/src/main/scala/java/lang/Throwables.scala index 316c4dcb1d..e10612d431 100644 --- a/javalib/src/main/scala/java/lang/Throwables.scala +++ b/javalib/src/main/scala/java/lang/Throwables.scala @@ -16,6 +16,8 @@ import java.util.function._ import scala.scalajs.js.annotation.JSExport import scala.scalajs.LinkingInfo +import scala.scalajs.LinkingInfo.moduleKind +import scala.scalajs.LinkingInfo.ModuleKind.{MinimalWasmModule, WasmComponent} class Throwable protected (s: String, private var e: Throwable, enableSuppression: scala.Boolean, writableStackTrace: scala.Boolean) @@ -47,17 +49,17 @@ class Throwable protected (s: String, private var e: Throwable, def getLocalizedMessage(): String = getMessage() def fillInStackTrace(): Throwable = { - LinkingInfo.linkTimeIf(!LinkingInfo.targetPureWasm) { - jsErrorForStackTrace = StackTrace.captureJSError(this) + LinkingInfo.linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { this } { + jsErrorForStackTrace = StackTrace.captureJSError(this) this } } def getStackTrace(): Array[StackTraceElement] = { if (stackTrace eq null) { - LinkingInfo.linkTimeIf(LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { stackTrace = new Array[StackTraceElement](0) } { if (writableStackTrace) @@ -70,7 +72,9 @@ class Throwable protected (s: String, private var e: Throwable, } def setStackTrace(stackTrace: Array[StackTraceElement]): Unit = { - LinkingInfo.linkTimeIf(!LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { + // do nothing + } { if (writableStackTrace) { var i = 0 while (i < stackTrace.length) { @@ -81,7 +85,7 @@ class Throwable protected (s: String, private var e: Throwable, this.stackTrace = stackTrace.clone() } - } {} + } } def printStackTrace(): Unit = printStackTrace(System.err) diff --git a/javalib/src/main/scala/java/lang/_String.scala b/javalib/src/main/scala/java/lang/_String.scala index 932fc68228..170c706a9f 100644 --- a/javalib/src/main/scala/java/lang/_String.scala +++ b/javalib/src/main/scala/java/lang/_String.scala @@ -20,7 +20,8 @@ import scala.scalajs.js import scala.scalajs.js.annotation._ import scala.scalajs.js.JSStringOps.enableJSStringOps import scala.scalajs.LinkingInfo -import scala.scalajs.LinkingInfo.ESVersion +import scala.scalajs.LinkingInfo.{ESVersion, moduleKind} +import scala.scalajs.LinkingInfo.ModuleKind.{MinimalWasmModule, WasmComponent} import java.lang.constant.{Constable, ConstantDesc} import java.nio.ByteBuffer @@ -59,7 +60,7 @@ final class _String private () // scalastyle:ignore // Wasm intrinsic def codePointAt(index: Int): Int = { - LinkingInfo.linkTimeIf(LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { Character.codePointAtImpl(this, index) } { if (LinkingInfo.esVersion >= ESVersion.ES2015) { @@ -201,7 +202,7 @@ final class _String private () // scalastyle:ignore @inline def endsWith(suffix: String): scala.Boolean = { - LinkingInfo.linkTimeIf(LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { regionMatches(thisString.length() - suffix.length, suffix, 0, suffix.length) } { if (LinkingInfo.esVersion >= ESVersion.ES2015) { @@ -246,7 +247,7 @@ final class _String private () // scalastyle:ignore indexOf(Character.toString(ch), fromIndex) def indexOf(str: String): Int = { - LinkingInfo.linkTimeIf(LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { indexOf(str, 0) } { thisString.jsIndexOf(str) @@ -254,7 +255,7 @@ final class _String private () // scalastyle:ignore } def indexOf(str: String, fromIndex: Int): Int = { - LinkingInfo.linkTimeIf(LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { val thisLen = thisString.length() val strLen = str.length() @@ -303,7 +304,7 @@ final class _String private () // scalastyle:ignore @inline def lastIndexOf(str: String): Int = { - LinkingInfo.linkTimeIf(LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { val thisLen = thisString.length() lastIndexOf(str, thisLen) } { @@ -316,7 +317,7 @@ final class _String private () // scalastyle:ignore def lastIndexOf(str: String, fromIndex: Int): Int = { if (fromIndex < 0) -1 else { - LinkingInfo.linkTimeIf(LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { val thisLen = thisString.length() val strLen = str.length() @@ -390,7 +391,8 @@ final class _String private () // scalastyle:ignore throw new IllegalArgumentException } else { LinkingInfo.linkTimeIf( - LinkingInfo.esVersion >= ESVersion.ES2015 && !LinkingInfo.targetPureWasm) { + LinkingInfo.esVersion >= ESVersion.ES2015 && + moduleKind != MinimalWasmModule && moduleKind != WasmComponent) { /* This will throw a `js.RangeError` if `count` is too large, instead of * an `OutOfMemoryError`. That's fine because the behavior of `repeat` is * not specified for `count` too large. @@ -409,7 +411,7 @@ final class _String private () // scalastyle:ignore str += str remainingIters -= 1 } - LinkingInfo.linkTimeIf(LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { str += str.substring(0, resultLength - str.length) } { str += str.jsSubstring(0, resultLength - str.length) @@ -426,10 +428,10 @@ final class _String private () // scalastyle:ignore @inline def replace(target: CharSequence, replacement: CharSequence): String = { - LinkingInfo.linkTimeIf(!LinkingInfo.targetPureWasm) { - thisString.jsSplit(target.toString).join(replacement.toString) - } { + LinkingInfo.linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { replaceInternal(target, replacement) + } { + thisString.jsSplit(target.toString).join(replacement.toString) } } @@ -476,7 +478,7 @@ final class _String private () // scalastyle:ignore @inline def startsWith(prefix: String): scala.Boolean = { - LinkingInfo.linkTimeIf(LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { regionMatches(0, prefix, 0, prefix.length()) } { if (LinkingInfo.esVersion >= ESVersion.ES2015) { @@ -490,7 +492,7 @@ final class _String private () // scalastyle:ignore @inline def startsWith(prefix: String, toffset: Int): scala.Boolean = { - LinkingInfo.linkTimeIf(LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { regionMatches(toffset, prefix, 0, prefix.length()) } { if (LinkingInfo.esVersion >= ESVersion.ES2015) { @@ -515,7 +517,7 @@ final class _String private () // scalastyle:ignore if (beginIndex < 0 || beginIndex > length()) charAt(beginIndex) - LinkingInfo.linkTimeIf(LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { this.substring(beginIndex, thisString.length) } { thisString.jsSubstring(beginIndex) @@ -533,7 +535,7 @@ final class _String private () // scalastyle:ignore if (endIndex < beginIndex) charAt(-1) - LinkingInfo.linkTimeIf(LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { val length = thisString.length val builder = new StringBuilder(endIndex - beginIndex) var i = beginIndex @@ -725,7 +727,7 @@ final class _String private () // scalastyle:ignore @inline def toLowerCase(): String = { - LinkingInfo.linkTimeIf(LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { this.asInstanceOf[_String].toLowerCaseImpl() } { this.asInstanceOf[js.Dynamic].toLowerCase().asInstanceOf[String] @@ -825,7 +827,7 @@ for (cp <- 0 to Character.MAX_CODE_POINT) { @inline def toUpperCase(): String = { - LinkingInfo.linkTimeIf(LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { replaceCharsAtIndex { i => val c = this.charAt(i) if (c < 0x80) null // fast-forward ASCII characters diff --git a/javalib/src/main/scala/java/nio/ByteOrder.scala b/javalib/src/main/scala/java/nio/ByteOrder.scala index 296978ae13..611c036333 100644 --- a/javalib/src/main/scala/java/nio/ByteOrder.scala +++ b/javalib/src/main/scala/java/nio/ByteOrder.scala @@ -16,6 +16,8 @@ import scala.scalajs.js import scala.scalajs.js.typedarray._ import scala.scalajs.LinkingInfo +import scala.scalajs.LinkingInfo.moduleKind +import scala.scalajs.LinkingInfo.ModuleKind.{MinimalWasmModule, WasmComponent} final class ByteOrder private (name: String) { override def toString(): String = name @@ -26,7 +28,7 @@ object ByteOrder { val LITTLE_ENDIAN: ByteOrder = new ByteOrder("LITTLE_ENDIAN") private[nio] val areTypedArraysBigEndian = { - LinkingInfo.linkTimeIf(LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { true } { if (js.typeOf(js.Dynamic.global.Int32Array) != "undefined") { diff --git a/javalib/src/main/scala/java/nio/charset/CoderResult.scala b/javalib/src/main/scala/java/nio/charset/CoderResult.scala index cd150bcdc7..868af138aa 100644 --- a/javalib/src/main/scala/java/nio/charset/CoderResult.scala +++ b/javalib/src/main/scala/java/nio/charset/CoderResult.scala @@ -18,7 +18,9 @@ import java.lang.Utils._ import java.nio._ import scala.scalajs.js -import scala.scalajs.LinkingInfo.{targetPureWasm, linkTimeIf} +import scala.scalajs.LinkingInfo +import scala.scalajs.LinkingInfo.{ESVersion, linkTimeIf, moduleKind} +import scala.scalajs.LinkingInfo.ModuleKind.{MinimalWasmModule, WasmComponent} class CoderResult private (kind: Int, _length: Int) { import CoderResult._ @@ -61,7 +63,7 @@ object CoderResult { // This is a sparse array private val uniqueMalformedJS = { - linkTimeIf(!targetPureWasm) { + linkTimeIf(moduleKind != MinimalWasmModule && moduleKind != WasmComponent) { js.Array[js.UndefOr[CoderResult]]() } { null @@ -69,7 +71,7 @@ object CoderResult { } private val uniqueMalformedWasm = { - linkTimeIf(targetPureWasm) { + linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { new java.util.HashMap[Int, CoderResult]() } { null @@ -83,7 +85,7 @@ object CoderResult { // This is a sparse array private val uniqueUnmappableJS = { - linkTimeIf(!targetPureWasm) { + linkTimeIf(moduleKind != MinimalWasmModule && moduleKind != WasmComponent) { js.Array[js.UndefOr[CoderResult]]() } { null @@ -91,7 +93,7 @@ object CoderResult { } private val uniqueUnmappableWasm = { - linkTimeIf(targetPureWasm) { + linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { new java.util.HashMap[Int, CoderResult]() } { null @@ -107,7 +109,7 @@ object CoderResult { } private def malformedForLengthImpl(length: Int): CoderResult = { - linkTimeIf(targetPureWasm) { + linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { uniqueMalformedWasm.computeIfAbsent(length, _ => new CoderResult(Malformed, length)) } { undefOrFold(uniqueMalformedJS(length)) { () => @@ -129,7 +131,7 @@ object CoderResult { } private def unmappableForLengthImpl(length: Int): CoderResult = { - linkTimeIf(targetPureWasm) { + linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { uniqueUnmappableWasm.computeIfAbsent(length, _ => new CoderResult(Unmappable, length)) } { undefOrFold(uniqueUnmappableJS(length)) { () => diff --git a/javalib/src/main/scala/java/util/Formatter.scala b/javalib/src/main/scala/java/util/Formatter.scala index 1a1d1bd0c7..8dcfefd0e2 100644 --- a/javalib/src/main/scala/java/util/Formatter.scala +++ b/javalib/src/main/scala/java/util/Formatter.scala @@ -15,6 +15,8 @@ package java.util import scala.annotation.switch import scala.scalajs.js import scala.scalajs.LinkingInfo +import scala.scalajs.LinkingInfo.moduleKind +import scala.scalajs.LinkingInfo.ModuleKind.{MinimalWasmModule, WasmComponent} import java.lang.{Double => JDouble} import java.lang.Utils._ @@ -1107,7 +1109,7 @@ object Formatter { if (ePos < 0) { 0 } else { - LinkingInfo.linkTimeIf(LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { java.lang.Integer.parseInt(s.substring(ePos + 1)) } { js.Dynamic.global.parseInt(s.substring(ePos + 1)).asInstanceOf[Int] diff --git a/javalib/src/main/scala/java/util/regex/Engine.scala b/javalib/src/main/scala/java/util/regex/Engine.scala index 39a3b85347..1a6b6c266d 100644 --- a/javalib/src/main/scala/java/util/regex/Engine.scala +++ b/javalib/src/main/scala/java/util/regex/Engine.scala @@ -17,6 +17,8 @@ import scala.language.higherKinds import java.util.function.Supplier import scala.scalajs.LinkingInfo +import scala.scalajs.LinkingInfo.moduleKind +import scala.scalajs.LinkingInfo.ModuleKind.{MinimalWasmModule, WasmComponent} /** Underlying engine used by `Pattern`. * @@ -109,7 +111,7 @@ private[regex] abstract class Engine { private[regex] object Engine { val engine: Engine = { - LinkingInfo.linkTimeIf[Engine](LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf[Engine](moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { WasmEngine } { JSEngine diff --git a/javalib/src/main/scala/java/util/regex/Pattern.scala b/javalib/src/main/scala/java/util/regex/Pattern.scala index 0397bb48b5..53e6591a85 100644 --- a/javalib/src/main/scala/java/util/regex/Pattern.scala +++ b/javalib/src/main/scala/java/util/regex/Pattern.scala @@ -17,6 +17,8 @@ import scala.annotation.tailrec import java.util.ScalaOps._ import scala.scalajs.LinkingInfo +import scala.scalajs.LinkingInfo.moduleKind +import scala.scalajs.LinkingInfo.ModuleKind.{MinimalWasmModule, WasmComponent} import PatternCompiler.Support._ @@ -140,7 +142,7 @@ final class Pattern private[regex] ( private[regex] def getIndices(lastMatch: engine.ExecResult, forMatches: Boolean): engine.IndicesArray = { - LinkingInfo.linkTimeIf(LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { val indices = engine.getIndices(lastMatch) if (indices == null) throw new AssertionError("Unreachable; WasmEngine always supports and produces indices") diff --git a/javalib/src/main/scala/java/util/regex/PatternCompiler.scala b/javalib/src/main/scala/java/util/regex/PatternCompiler.scala index f6a2f605fb..c91209491c 100644 --- a/javalib/src/main/scala/java/util/regex/PatternCompiler.scala +++ b/javalib/src/main/scala/java/util/regex/PatternCompiler.scala @@ -30,7 +30,8 @@ import java.util.ScalaOps._ import scala.scalajs.js import scala.scalajs.LinkingInfo -import scala.scalajs.LinkingInfo.ESVersion +import scala.scalajs.LinkingInfo.{ESVersion, moduleKind} +import scala.scalajs.LinkingInfo.ModuleKind.{MinimalWasmModule, WasmComponent} /** Compiler from Java regular expressions to JavaScript regular expressions. * @@ -50,7 +51,7 @@ private[regex] object PatternCompiler { /** RegExp to renumber backreferences (used for possessive quantifiers). * - * Lazy because we cannot link it under targetPureWasm. + * Lazy because we cannot link it under Wasm without JS interop. */ private lazy val renumberingRegExp = new js.RegExp("(\\\\+)(\\d+)", "g") @@ -1209,7 +1210,7 @@ private final class PatternCompiler(private val pattern: String, private var fla private def buildPossessiveQuantifier(compiledGroupCountBeforeThisToken: Int, compiledToken: String, baseRepeater: String): String = { - LinkingInfo.linkTimeIf(LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { /* Our WasmEngine supports possessive quantifiers. * We use them so that we don't need renumbering, which we otherwise * use a js.RegExp for. @@ -1910,7 +1911,7 @@ private final class PatternCompiler(private val pattern: String, private var fla } else if (c1 == '>') { // Atomic group pIndex = start + 3 - LinkingInfo.linkTimeIf(LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { // Our WasmEngine directly supports atomic groups, so use them. s"(?>${compileInsideGroup()})" } { @@ -1945,7 +1946,7 @@ private final class PatternCompiler(private val pattern: String, private var fla @inline def ucSubstring(s: String, start: Int): String = { LinkingInfo.linkTimeIf(LinkingInfo.isWebAssembly) { - // use s.substring anyway, for the intrinsic or the targetPureWasm implementation + // use s.substring anyway, for the intrinsic or the Wasm-only implementation s.substring(start) } { // avoid range checks @@ -1958,7 +1959,7 @@ private final class PatternCompiler(private val pattern: String, private var fla @inline def ucSubstring(s: String, start: Int, end: Int): String = { LinkingInfo.linkTimeIf(LinkingInfo.isWebAssembly) { - // use s.substring anyway, for the intrinsic or the targetPureWasm implementation + // use s.substring anyway, for the intrinsic or the Wasm-only implementation s.substring(start, end) } { // avoid range checks diff --git a/javalib/src/main/scala/java/util/regex/RegExpImpl.scala b/javalib/src/main/scala/java/util/regex/RegExpImpl.scala index 505dab722c..75bc392dba 100644 --- a/javalib/src/main/scala/java/util/regex/RegExpImpl.scala +++ b/javalib/src/main/scala/java/util/regex/RegExpImpl.scala @@ -12,7 +12,9 @@ package java.util.regex -import scala.scalajs.LinkingInfo._ +import scala.scalajs.LinkingInfo +import scala.scalajs.LinkingInfo.moduleKind +import scala.scalajs.LinkingInfo.ModuleKind.{MinimalWasmModule, WasmComponent} /** A wrapper class to select regex implementation across different platforms. * @@ -38,7 +40,8 @@ private[java] sealed abstract class RegExpImpl { } private[java] object RegExpImpl { - val impl = linkTimeIf[RegExpImpl](targetPureWasm) { + val impl = LinkingInfo.linkTimeIf[RegExpImpl]( + moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { JavaRegExpImpl } { JSRegExpImpl diff --git a/junit-runtime/src/main/scala/org/junit/Assert.scala b/junit-runtime/src/main/scala/org/junit/Assert.scala index 2278965622..7dd7b8a6ed 100644 --- a/junit-runtime/src/main/scala/org/junit/Assert.scala +++ b/junit-runtime/src/main/scala/org/junit/Assert.scala @@ -10,7 +10,6 @@ import org.junit.internal.InexactComparisonCriteria import org.junit.internal.ExactComparisonCriteria import org.hamcrest.Matcher import org.hamcrest.MatcherAssert -import scala.scalajs.LinkingInfo object Assert { @noinline @@ -411,17 +410,9 @@ object Assert { actualThrown) } - LinkingInfo.linkTimeIf(LinkingInfo.targetPureWasm) { - throw new AssertionError( - buildPrefix + - "expecte " + formatClass(expectedThrowable) + " to be thrown, but nothing was thrown" - ) - } { - throw new AssertionError( - buildPrefix + - String.format( - "expected %s to be thrown, but nothing was thrown", formatClass(expectedThrowable))) - } + throw new AssertionError( + s"${buildPrefix}expected ${formatClass(expectedThrowable)} " + + "to be thrown, but nothing was thrown") // scalastyle:on return } diff --git a/library/src/main/scala/scala/scalajs/LinkingInfo.scala b/library/src/main/scala/scala/scalajs/LinkingInfo.scala index 550f0232cf..7558fda2ef 100644 --- a/library/src/main/scala/scala/scalajs/LinkingInfo.scala +++ b/library/src/main/scala/scala/scalajs/LinkingInfo.scala @@ -263,10 +263,6 @@ object LinkingInfo { def isWebAssembly: Boolean = linkTimePropertyBoolean("core/isWebAssembly") - @inline @linkTimeProperty("core/targetPureWasm") - def targetPureWasm: Boolean = - linkTimePropertyBoolean("core/targetPureWasm") - /** Version of the linker. */ @inline @linkTimeProperty("core/linkerVersion") def linkerVersion: String = @@ -401,6 +397,12 @@ object LinkingInfo { * module-global variable. */ final val CommonJSModule = 3 + + /** A minimal Wasm module. */ + final val MinimalWasmModule = 4 + + /** A Wasm Component in the Component Model. */ + final val WasmComponent = 5 } private[scalajs] def linkTimePropertyInt(name: String): Int = diff --git a/library/src/main/scala/scala/scalajs/concurrent/QueueExecutionContext.scala b/library/src/main/scala/scala/scalajs/concurrent/QueueExecutionContext.scala index 3a462174fd..b98e4b199b 100644 --- a/library/src/main/scala/scala/scalajs/concurrent/QueueExecutionContext.scala +++ b/library/src/main/scala/scala/scalajs/concurrent/QueueExecutionContext.scala @@ -19,7 +19,8 @@ import java.util.concurrent.Executor import scala.scalajs.js import scala.scalajs.js.| import scala.scalajs.LinkingInfo -import scala.scalajs.LinkingInfo.linkTimeIf +import scala.scalajs.LinkingInfo.{linkTimeIf, moduleKind} +import scala.scalajs.LinkingInfo.ModuleKind.{MinimalWasmModule, WasmComponent} object QueueExecutionContext { def timeouts(): ExecutionContextExecutor = @@ -32,7 +33,7 @@ object QueueExecutionContext { new SingleThreadedExecutionContext def apply(): ExecutionContextExecutor = { - linkTimeIf(LinkingInfo.targetPureWasm) { + linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { single() } { if (js.typeOf(js.Dynamic.global.Promise) == "undefined") timeouts() diff --git a/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/ModuleKind.scala b/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/ModuleKind.scala index bc708445ac..45a4999131 100644 --- a/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/ModuleKind.scala +++ b/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/ModuleKind.scala @@ -23,9 +23,12 @@ object ModuleKind { * whoever maintains the back-ends. */ val All: List[ModuleKind] = List( - NoModule, - ESModule, - CommonJSModule) + NoModule, + ESModule, + MinimalWasmModule, + WasmComponent, + CommonJSModule + ) /** No module structure. * @@ -42,6 +45,12 @@ object ModuleKind { */ case object ESModule extends ModuleKind + /** A minimal Wasm module. */ + case object MinimalWasmModule extends ModuleKind + + /** A Wasm Component in the Component Model. */ + case object WasmComponent extends ModuleKind + /** A CommonJS module (notably used by Node.js). * * Imported modules are fetched with `require`. Exports go to the `exports` @@ -53,9 +62,11 @@ object ModuleKind { override def fingerprint(moduleKind: ModuleKind): String = { moduleKind match { - case NoModule => "NoModule" - case ESModule => "ESModule" - case CommonJSModule => "CommonJSModule" + case NoModule => "NoModule" + case ESModule => "ESModule" + case MinimalWasmModule => "MinimalWasmModule" + case WasmComponent => "WasmComponent" + case CommonJSModule => "CommonJSModule" } } } diff --git a/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/Report.scala b/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/Report.scala index 23ce4c3583..476538aaba 100644 --- a/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/Report.scala +++ b/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/Report.scala @@ -114,9 +114,11 @@ object Report { private def writeModuleKind(kind: ModuleKind): Unit = { val i = kind match { - case ModuleKind.NoModule => 0 - case ModuleKind.ESModule => 1 - case ModuleKind.CommonJSModule => 2 + case ModuleKind.NoModule => 0 + case ModuleKind.ESModule => 1 + case ModuleKind.CommonJSModule => 2 + case ModuleKind.MinimalWasmModule => 3 + case ModuleKind.WasmComponent => 4 } writeByte(i) } @@ -153,6 +155,8 @@ object Report { case 0 => ModuleKind.NoModule case 1 => ModuleKind.ESModule case 2 => ModuleKind.CommonJSModule + case 3 => ModuleKind.MinimalWasmModule + case 4 => ModuleKind.WasmComponent case v => throw new IllegalArgumentException(s"unknown module byte: $v") } } diff --git a/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/WasmFeatures.scala b/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/WasmFeatures.scala index 4bc39d775d..bcdb9dc7b1 100644 --- a/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/WasmFeatures.scala +++ b/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/WasmFeatures.scala @@ -4,8 +4,6 @@ import Fingerprint.FingerprintBuilder final class WasmFeatures private ( _exceptionHandling: Boolean, - _targetPureWasm: Boolean, - _componentModel: Boolean, _witDirectory: Option[String], _witWorld: Option[String], _autoIncludeWasiImports: Boolean @@ -15,8 +13,6 @@ final class WasmFeatures private ( private def this() = { this( _exceptionHandling = true, - _targetPureWasm = false, - _componentModel = false, _witDirectory = None, _witWorld = None, _autoIncludeWasiImports = true @@ -24,8 +20,6 @@ final class WasmFeatures private ( } val exceptionHandling = _exceptionHandling - val targetPureWasm = _targetPureWasm - val componentModel = _componentModel val witDirectory = _witDirectory val witWorld = _witWorld val autoIncludeWasiImports = _autoIncludeWasiImports @@ -33,12 +27,6 @@ final class WasmFeatures private ( def withExceptionHandling(exceptionHandling: Boolean): WasmFeatures = copy(exceptionHandling = exceptionHandling) - def withTargetPureWasm(targetPureWasm: Boolean): WasmFeatures = - copy(targetPureWasm = targetPureWasm) - - def withComponentModel(componentModel: Boolean): WasmFeatures = - copy(componentModel = componentModel) - def withWitDirectory(witDirectory: Option[String]): WasmFeatures = copy(witDirectory = witDirectory) @@ -51,8 +39,6 @@ final class WasmFeatures private ( override def equals(that: Any): Boolean = that match { case that: WasmFeatures => this.exceptionHandling == that.exceptionHandling && - this.targetPureWasm == that.targetPureWasm && - this.componentModel == that.componentModel && this.witDirectory == that.witDirectory && this.witWorld == that.witWorld && this.autoIncludeWasiImports == that.autoIncludeWasiImports @@ -64,19 +50,15 @@ final class WasmFeatures private ( import scala.util.hashing.MurmurHash3._ var acc = HashSeed acc = mix(acc, exceptionHandling.##) - acc = mix(acc, targetPureWasm.##) - acc = mix(acc, componentModel.##) acc = mix(acc, witDirectory.##) acc = mix(acc, witWorld.##) acc = mixLast(acc, autoIncludeWasiImports.##) - finalizeHash(acc, 6) + finalizeHash(acc, 4) } override def toString(): String = { s"""WasmFeatures( | exceptionHandling = $exceptionHandling, - | targetPureWasm = $targetPureWasm, - | componentModel = $componentModel, | witDirectory = $witDirectory, | witWorld = $witWorld, | autoIncludeWasiImports = $autoIncludeWasiImports @@ -85,16 +67,12 @@ final class WasmFeatures private ( private def copy( exceptionHandling: Boolean = this.exceptionHandling, - targetPureWasm: Boolean = this.targetPureWasm, - componentModel: Boolean = this.componentModel, witDirectory: Option[String] = this.witDirectory, witWorld: Option[String] = this.witWorld, autoIncludeWasiImports: Boolean = this.autoIncludeWasiImports ): WasmFeatures = { new WasmFeatures( _exceptionHandling = exceptionHandling, - _targetPureWasm = targetPureWasm, - _componentModel = componentModel, _witDirectory = witDirectory, _witWorld = witWorld, _autoIncludeWasiImports = autoIncludeWasiImports @@ -113,8 +91,6 @@ object WasmFeatures { override def fingerprint(wasmFeatures: WasmFeatures): String = { new FingerprintBuilder("WasmFeatures") .addField("exceptionHandling", wasmFeatures.exceptionHandling) - .addField("targetPureWasm", wasmFeatures.targetPureWasm) - .addField("componentModel", wasmFeatures.componentModel) .addField("witDirectory", wasmFeatures.witDirectory) .addField("witWorld", wasmFeatures.witWorld) .addField("autoIncludeWasiImports", wasmFeatures.autoIncludeWasiImports) diff --git a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analysis.scala b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analysis.scala index 5fa8a96224..d0222db65a 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analysis.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analysis.scala @@ -312,7 +312,7 @@ object Analysis { val usages = jsInteropUsages.map { case (pos, irStr) => s" at ${pos.source}:${pos.line + 1}:${pos.column + 1}: $irStr" }.mkString("\n") - s"Uses JS interop with targetPureWasm = true:\n$usages" + s"Uses JS interop with with a Wasm-only module kind:\n$usages" } logger.log(level, headMsg) diff --git a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala index 628117856b..5f20ea3839 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala @@ -56,8 +56,16 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean, private val linkTimeProperties = LinkTimeProperties.fromCoreSpec(config.coreSpec) private val infoLoader: InfoLoader = { - new InfoLoader(irLoader, checkIRFor, linkTimeProperties, - config.coreSpec.wasmFeatures.targetPureWasm) + /* We only ask the InfoLoader to register JS interop if we actually need to + * report errors for them. Otherwise, it is too expensive. + */ + val registerJSInterop = config.coreSpec.moduleKind match { + case ModuleKind.MinimalWasmModule => true + case ModuleKind.WasmComponent => true + case _ => false + } + + new InfoLoader(irLoader, checkIRFor, linkTimeProperties, registerJSInterop) } def computeReachability(moduleInitializers: Seq[ModuleInitializer], diff --git a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/InfoLoader.scala b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/InfoLoader.scala index a8c21799fd..ba9b2ad5e8 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/InfoLoader.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/InfoLoader.scala @@ -31,9 +31,9 @@ import Platform.emptyThreadSafeMap private[analyzer] final class InfoLoader(irLoader: IRLoader, checkIRFor: Option[CheckingPhase], linkTimeProperties: LinkTimeProperties, - targetPureWasm: Boolean) { + registerJSInterop: Boolean) { - private val generator = new Infos.InfoGenerator(linkTimeProperties, targetPureWasm) + private val generator = new Infos.InfoGenerator(linkTimeProperties, registerJSInterop) private var logger: Logger = _ private val cache = emptyThreadSafeMap[ClassName, InfoLoader.ClassInfoCache] diff --git a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Infos.scala b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Infos.scala index 4767e7b0cf..4481bd17df 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Infos.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Infos.scala @@ -569,7 +569,7 @@ object Infos { } final class InfoGenerator(linkTimeProperties: LinkTimeProperties, - targetPureWasm: Boolean) { + registerJSInterop: Boolean) { def genReferencedFieldClasses(fields: List[AnyFieldDef]): Map[FieldName, ClassName] = { val builder = Map.newBuilder[FieldName, ClassName] @@ -595,7 +595,7 @@ object Infos { * [[org.scalajs.ir.Trees.MethodDef Trees.MethodDef]]. */ def generateMethodInfo(methodDef: MethodDef): MethodInfo = { - new GenInfoTraverser(methodDef.version, linkTimeProperties, targetPureWasm) + new GenInfoTraverser(methodDef.version, linkTimeProperties, registerJSInterop) .generateMethodInfo(methodDef) } @@ -603,7 +603,7 @@ object Infos { * [[org.scalajs.ir.Trees.JSConstructorDef Trees.JSConstructorDef]]. */ def generateJSConstructorInfo(ctorDef: JSConstructorDef): ReachabilityInfo = { - new GenInfoTraverser(ctorDef.version, linkTimeProperties, targetPureWasm) + new GenInfoTraverser(ctorDef.version, linkTimeProperties, registerJSInterop) .generateJSConstructorInfo(ctorDef) } @@ -611,7 +611,7 @@ object Infos { * [[org.scalajs.ir.Trees.JSMethodDef Trees.JSMethodDef]]. */ def generateJSMethodInfo(methodDef: JSMethodDef): ReachabilityInfo = { - new GenInfoTraverser(methodDef.version, linkTimeProperties, targetPureWasm) + new GenInfoTraverser(methodDef.version, linkTimeProperties, registerJSInterop) .generateJSMethodInfo(methodDef) } @@ -619,7 +619,7 @@ object Infos { * [[org.scalajs.ir.Trees.JSPropertyDef Trees.JSPropertyDef]]. */ def generateJSPropertyInfo(propertyDef: JSPropertyDef): ReachabilityInfo = { - new GenInfoTraverser(propertyDef.version, linkTimeProperties, targetPureWasm) + new GenInfoTraverser(propertyDef.version, linkTimeProperties, registerJSInterop) .generateJSPropertyInfo(propertyDef) } @@ -631,7 +631,7 @@ object Infos { /** Generates the [[MethodInfo]] for the top-level exports. */ def generateTopLevelExportInfo(enclosingClass: ClassName, topLevelExportDef: TopLevelExportDef): TopLevelExportInfo = { - val info = new GenInfoTraverser(Version.Unversioned, linkTimeProperties, targetPureWasm) + val info = new GenInfoTraverser(Version.Unversioned, linkTimeProperties, registerJSInterop) .generateTopLevelExportInfo(enclosingClass, topLevelExportDef) new TopLevelExportInfo(info, ModuleID(topLevelExportDef.moduleID), @@ -639,13 +639,13 @@ object Infos { } def generateWitNativeMember(member: WitNativeMemberDef): MethodInfo = { - new GenInfoTraverser(Version.Unversioned, linkTimeProperties, targetPureWasm) + new GenInfoTraverser(Version.Unversioned, linkTimeProperties, registerJSInterop) .generateWitNativeMember(member) } } private final class GenInfoTraverser(version: Version, - linkTimeProperties: LinkTimeProperties, targetPureWasm: Boolean) + linkTimeProperties: LinkTimeProperties, registerJSInterop: Boolean) extends Traverser { private val builder = new ReachabilityInfoBuilder(version) @@ -835,7 +835,7 @@ object Infos { override def traverse(tree: Tree): Unit = { builder.maybeAddReferencedClass(tree.tpe) - if (targetPureWasm) + if (registerJSInterop) checkJSInterop(tree) tree match { diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/WebAssemblyLinkerBackend.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/WebAssemblyLinkerBackend.scala index 75c54d3841..8fa162bb01 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/WebAssemblyLinkerBackend.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/WebAssemblyLinkerBackend.scala @@ -33,8 +33,9 @@ final class WebAssemblyLinkerBackend(config: LinkerBackendImpl.Config) extends LinkerBackendImpl(config) { require( - coreSpec.moduleKind == ModuleKind.ESModule, - s"The WebAssembly backend only supports ES modules; was ${coreSpec.moduleKind}." + Set[ModuleKind](ModuleKind.ESModule, ModuleKind.MinimalWasmModule, ModuleKind.WasmComponent) + .contains(coreSpec.moduleKind), + s"The WebAssembly backend does not support the module kind ${coreSpec.moduleKind}." ) require( coreSpec.esFeatures.useECMAScript2015Semantics, @@ -139,7 +140,7 @@ final class WebAssemblyLinkerBackend(config: LinkerBackendImpl.Config) val binaryOutput = BinaryWriter.write(wasmModule, emitDebugInfo) outputImpl.writeFull(wasmFileName, binaryOutput) }).flatMap { _ => - if (!coreSpec.wasmFeatures.componentModel) { + if (coreSpec.moduleKind != ModuleKind.WasmComponent) { Future.unit } else { processComponentModel() diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/ClassEmitter.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/ClassEmitter.scala index c0d7d49d5f..f5e532b5db 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/ClassEmitter.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/ClassEmitter.scala @@ -41,11 +41,11 @@ import VarGen._ import TypeTransformer._ import WasmContext._ import _root_.org.scalajs.linker.backend.wasmemitter.canonicalabi.CABIToScalaJS +import org.scalajs.linker.interface.ModuleKind class ClassEmitter(coreSpec: CoreSpec) { import ClassEmitter._ import coreSpec.semantics - import coreSpec.wasmFeatures.targetPureWasm def genClassDef(clazz: LinkedClass)(implicit ctx: WasmContext): Unit = { val classInfo = ctx.getClassInfo(clazz.className) @@ -78,8 +78,7 @@ class ClassEmitter(coreSpec: CoreSpec) { genMethod(clazz, method) } - if (coreSpec.wasmFeatures.targetPureWasm && - coreSpec.wasmFeatures.componentModel) { + if (coreSpec.moduleKind == ModuleKind.WasmComponent) { for (member <- clazz.witNativeMembers) { canonicalabi.InteropEmitter.genComponentNativeInterop(clazz, member) } @@ -149,7 +148,7 @@ class ClassEmitter(coreSpec: CoreSpec) { def genTopLevelExport(topLevelExport: LinkedTopLevelExport)( implicit ctx: WasmContext): Unit = { topLevelExport.tree match { - case d: WitExportDef if ctx.coreSpec.wasmFeatures.targetPureWasm => + case d: WitExportDef if !ctx.hasJSInterop => canonicalabi.InteropEmitter.genWitExportDef(topLevelExport.owningClass, d) case d: TopLevelMethodExportDef => genTopLevelExportSetter(topLevelExport.exportName) @@ -221,11 +220,11 @@ class ClassEmitter(coreSpec: CoreSpec) { val classInfo = ctx.getClassInfo(className) val nameStr = runtimeClassNameOf(className) - val nameValue = { - if (targetPureWasm) { - ctx.stringPool.getConstantStringDataInstr(nameStr) :+ - wa.RefNull(watpe.HeapType(genTypeID.wasmString)) - } else ctx.stringPool.getConstantStringDataInstr(nameStr) + val nameValue = if (ctx.hasJSInterop) { + ctx.stringPool.getConstantStringDataInstr(nameStr) + } else { + ctx.stringPool.getConstantStringDataInstr(nameStr) :+ + wa.RefNull(watpe.HeapType(genTypeID.wasmString)) } val kind = className match { @@ -400,14 +399,16 @@ class ClassEmitter(coreSpec: CoreSpec) { watpe.RefType(vtableTypeID), isMutable = false ) - val idHashCodeField = if (targetPureWasm) { + val idHashCodeField = if (ctx.hasJSInterop) { + None + } else { Some(watpe.StructField( genFieldID.objStruct.idHashCode, OriginalName(genFieldID.objStruct.idHashCode.toString()), watpe.Int32, isMutable = true )) - } else None + } val fields = idHashCodeField.toList ::: classInfo.allFieldDefs.map { field => watpe.StructField( @@ -505,7 +506,7 @@ class ClassEmitter(coreSpec: CoreSpec) { isMutable = false ) - val idHashCodeFieldOpt = if (targetPureWasm) { + val idHashCodeFieldOpt = if (!ctx.hasJSInterop) { Some(watpe.StructField( genFieldID.objStruct.idHashCode, OriginalName(genFieldID.objStruct.idHashCode.toString()), @@ -578,11 +579,11 @@ class ClassEmitter(coreSpec: CoreSpec) { case ClassRef(className) => "[L" + runtimeClassNameOf(className) + ";" case WitResourceTypeRef(className) => "[W" + runtimeClassNameOf(className) + ";" } - val nameValue = { - if (targetPureWasm) { - ctx.stringPool.getConstantStringDataInstr(nameStr) :+ - wa.RefNull(watpe.HeapType(genTypeID.wasmString)) - } else ctx.stringPool.getConstantStringDataInstr(nameStr) + val nameValue = if (ctx.hasJSInterop) { + ctx.stringPool.getConstantStringDataInstr(nameStr) + } else { + ctx.stringPool.getConstantStringDataInstr(nameStr) :+ + wa.RefNull(watpe.HeapType(genTypeID.wasmString)) } val vtableInit: List[wa.Instr] = nameValue ::: List( // name @@ -729,8 +730,10 @@ class ClassEmitter(coreSpec: CoreSpec) { // Load 1 << jsValueType(expr) fb += wa.I32Const(1) fb += wa.LocalGet(exprNonNullLocal) - if (targetPureWasm) fb += wa.Call(genFunctionID.scalaValueType) - else fb += wa.Call(genFunctionID.jsValueType) + if (ctx.hasJSInterop) + fb += wa.Call(genFunctionID.jsValueType) + else + fb += wa.Call(genFunctionID.scalaValueType) fb += wa.I32Shl // return (... & specialInstanceTypes) != 0 @@ -815,7 +818,8 @@ class ClassEmitter(coreSpec: CoreSpec) { fb += wa.GlobalGet(genGlobalID.forVTable(className)) // idHashCode - if (targetPureWasm) fb += wa.I32Const(0) + if (!ctx.hasJSInterop) + fb += wa.I32Const(0) classInfo.allFieldDefs.foreach { f => fb ++= genZeroOf(f.ftpe) @@ -861,7 +865,9 @@ class ClassEmitter(coreSpec: CoreSpec) { // Push the vtable on the stack fb += wa.GlobalGet(genGlobalID.forVTable(className)) - if (targetPureWasm) fb += wa.I32Const(0) + // idHashCode + if (!ctx.hasJSInterop) + fb += wa.I32Const(0) // Push every field of `fromTyped` on the stack info.allFieldDefs.foreach { field => @@ -1540,7 +1546,7 @@ class ClassEmitter(coreSpec: CoreSpec) { val body = method.body.getOrElse(throw new Exception("abstract method cannot be transformed")) // Emit the function - if (!ctx.coreSpec.wasmFeatures.componentModel && + if (coreSpec.moduleKind == ModuleKind.MinimalWasmModule && (className == SpecialNames.WasmSystemClass || className == SpecialNames.WasmScalajsComClass) && namespace == MemberNamespace.Public && !methodName.isReflectiveProxy) { emitSpecialMethod( diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/CoreWasmLib.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/CoreWasmLib.scala index faec5326a1..4312b21f2e 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/CoreWasmLib.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/CoreWasmLib.scala @@ -19,7 +19,7 @@ import org.scalajs.ir.Types.{Type => _, ArrayType => _, _} import org.scalajs.ir.{OriginalName, Position, Types => irtpe} import org.scalajs.ir.WellKnownNames._ -import org.scalajs.linker.interface.CheckedBehavior +import org.scalajs.linker.interface.{CheckedBehavior, ModuleKind} import org.scalajs.linker.standard.{CoreSpec, LinkedGlobalInfo} import org.scalajs.linker.backend.webassembly._ @@ -51,13 +51,14 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { import CoreWasmLib._ import RefType.anyref import coreSpec.semantics - import coreSpec.wasmFeatures.targetPureWasm - val stringType = if (targetPureWasm) RefType(genTypeID.wasmString) else RefType.extern + private val hasJSInterop = coreSpec.moduleKind == ModuleKind.ESModule - val nullableStringType = - if (targetPureWasm) RefType.nullable(genTypeID.wasmString) - else RefType.externref + private val stringType: RefType = + if (hasJSInterop) RefType.extern + else RefType(genTypeID.wasmString) + + private val nullableStringType: RefType = stringType.toNullable private implicit val noPos: Position = Position.NoPosition @@ -94,15 +95,15 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { def make(id: FieldID, tpe: Type, isMutable: Boolean): StructField = StructField(id, OriginalName(id.toString()), tpe, isMutable) - val nameFields = { - if (targetPureWasm) { - List( - make(nameOffset, Int32, isMutable = false), - make(nameSize, Int32, isMutable = false), - make(nameStringIndex, Int32, isMutable = false), - make(name, RefType.nullable(genTypeID.wasmString), isMutable = true) - ) - } else List(make(name, RefType.externref, isMutable = true)) + val nameFields = if (hasJSInterop) { + List(make(name, RefType.externref, isMutable = true)) + } else { + List( + make(nameOffset, Int32, isMutable = false), + make(nameSize, Int32, isMutable = false), + make(nameStringIndex, Int32, isMutable = false), + make(name, RefType.nullable(genTypeID.wasmString), isMutable = true) + ) } nameFields ::: @@ -136,7 +137,7 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { * with the enclosing function's result type. */ private def genThrow(fb: FunctionBuilder, fakeResult: List[Instr]): Unit = { - if (!targetPureWasm) + if (hasJSInterop) fb += ExternConvertAny if (coreSpec.wasmFeatures.exceptionHandling) { @@ -178,9 +179,10 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { ctx.moduleBuilder.addRecTypeBuilder(ctx.mainRecType) genCoreTypesInRecType() - if (!targetPureWasm) genImports() - else { - if (!coreSpec.wasmFeatures.componentModel) + if (hasJSInterop) { + genImports() + } else { + if (coreSpec.moduleKind == ModuleKind.MinimalWasmModule) genWasmEssentialsImports() if (coreSpec.wasmFeatures.exceptionHandling) { @@ -231,7 +233,7 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { def genPostClasses()(implicit ctx: WasmContext): Unit = { genBoxedZeroGlobals() - if (targetPureWasm) { + if (!hasJSInterop) { genUndefinedAndIsUndef() genNaiveFmod() genLtoa() @@ -316,7 +318,7 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { ) ) - if (targetPureWasm) { + if (!hasJSInterop) { genCoreType( genTypeID.wasmString, StructType( @@ -348,7 +350,7 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { // --- Imports --- private def genImports()(implicit ctx: WasmContext): Unit = { - assert(!targetPureWasm) + assert(hasJSInterop) if (ctx.coreSpec.wasmFeatures.exceptionHandling) genTagImports() genGlobalImports() genStringBuiltinImports() @@ -720,11 +722,11 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { ) for ((primRef, kind) <- primRefsWithKinds) { - val nameValue = { - if (targetPureWasm) { - ctx.stringPool.getConstantStringDataInstr(primRef.displayName) :+ - RefNull(HeapType(genTypeID.wasmString)) - } else ctx.stringPool.getConstantStringDataInstr(primRef.displayName) + val nameValue = if (hasJSInterop) { + ctx.stringPool.getConstantStringDataInstr(primRef.displayName) + } else { + ctx.stringPool.getConstantStringDataInstr(primRef.displayName) :+ + RefNull(HeapType(genTypeID.wasmString)) } val instrs: List[Instr] = { @@ -748,19 +750,24 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { val primTypesWithBoxClasses: List[(GlobalID, ClassName, Instr)] = List( (genGlobalID.bZeroChar, SpecialNames.CharBoxClass, I32Const(0)), (genGlobalID.bZeroLong, SpecialNames.LongBoxClass, I64Const(0)) - ) ++ (if (targetPureWasm) List( - (genGlobalID.bZeroInteger, SpecialNames.IntegerBoxClass, I32Const(0)), - (genGlobalID.bZeroFloat, SpecialNames.DoubleBoxClass, F64Const(0)), - (genGlobalID.bZeroDouble, SpecialNames.DoubleBoxClass, F64Const(0)) - ) - else Nil) + ) ++ { + if (hasJSInterop) { + Nil + } else { + List( + (genGlobalID.bZeroInteger, SpecialNames.IntegerBoxClass, I32Const(0)), + (genGlobalID.bZeroFloat, SpecialNames.DoubleBoxClass, F64Const(0)), + (genGlobalID.bZeroDouble, SpecialNames.DoubleBoxClass, F64Const(0)) + ) + } + } for ((globalID, boxClassName, zeroValueInstr) <- primTypesWithBoxClasses) { val boxStruct = genTypeID.forClass(boxClassName) val instrs: List[Instr] = List( GlobalGet(genGlobalID.forVTable(boxClassName)) ) ::: - (if (targetPureWasm) List(I32Const(0)) else Nil) ::: + (if (hasJSInterop) Nil else List(I32Const(0))) ::: List( zeroValueInstr, StructNew(boxStruct) @@ -789,7 +796,8 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { genUnboxByteOrShort(ShortRef) genTestByteOrShort(ByteRef, I32Extend8S) genTestByteOrShort(ShortRef, I32Extend16S) - if (targetPureWasm) genStringLiteral() + if (!hasJSInterop) + genStringLiteral() genTypeDataName() genCreateClassOf() genGetClassOf() @@ -822,19 +830,17 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { } if (semantics.stringIndexOutOfBounds != CheckedBehavior.Unchecked) { - genCheckedStringCharAtOrCodePointAt( - genFunctionID.checkedStringCharAt, - if (targetPureWasm) { - genFunctionID.wasmString.charCodeAt - } else { - genFunctionID.stringBuiltins.charCodeAt - } - ) - if (!targetPureWasm) { // In WASI, Optimizer won't transform substring method + if (hasJSInterop) { + genCheckedStringCharAtOrCodePointAt( + genFunctionID.checkedStringCharAt, genFunctionID.stringBuiltins.charCodeAt) genCheckedStringCharAtOrCodePointAt( genFunctionID.checkedStringCodePointAt, genFunctionID.stringBuiltins.codePointAt) genCheckedSubstringStart() genCheckedSubstringStartEnd() + } else { + // With Wasm-defined strings, the optimizer won't transform the substring method + genCheckedStringCharAtOrCodePointAt( + genFunctionID.checkedStringCharAt, genFunctionID.wasmString.charCodeAt) } } @@ -862,8 +868,8 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { genArrayCloneFunctions() genArrayCopyFunctions() - // WASI - if (targetPureWasm) { + // Wasm-defined strings + if (!hasJSInterop) { genStringConcat() genStringEquals() genGetWholeChars() @@ -1183,7 +1189,7 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { } private def genBoxBoolean()(implicit ctx: WasmContext): Unit = { - if (targetPureWasm) { + if (!hasJSInterop) { genBox(genFunctionID.box(BooleanRef), BooleanType) } else { val fb = newFunctionBuilder(genFunctionID.box(BooleanRef)) @@ -1252,7 +1258,7 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { private def genTypeTest(functionID: FunctionID, targetTpe: PrimType)( implicit ctx: WasmContext): Unit = { - assert(targetPureWasm) + assert(!hasJSInterop) val fb = newFunctionBuilder(functionID) val xParam = fb.addParam("x", RefType.anyref) @@ -1268,7 +1274,7 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { } private def genBox(functionID: FunctionID, targetTpe: PrimType)(implicit ctx: WasmContext): Unit = { - assert(targetPureWasm) + assert(!hasJSInterop) val wasmType = transformPrimType(targetTpe) val fb = newFunctionBuilder(functionID) @@ -1286,7 +1292,7 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { } fb += GlobalGet(genGlobalID.forVTable(boxClass)) - if (targetPureWasm) fb += I32Const(0) + fb += I32Const(0) // idHashCode fb += LocalGet(xParam) if (targetTpe == FloatType) fb += F64PromoteF32 fb += StructNew(genTypeID.forClass(boxClass)) @@ -1295,7 +1301,8 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { private def genUnbox(functionID: FunctionID, targetTpe: PrimType)( implicit ctx: WasmContext): Unit = { - assert(targetPureWasm) + assert(!hasJSInterop) + val fb = newFunctionBuilder(functionID) val xParam = fb.addParam("x", RefType.anyref) val resultType = transformPrimType(targetTpe) @@ -1329,7 +1336,8 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { } private def genUnboxFloat()(implicit ctx: WasmContext): Unit = { - assert(targetPureWasm) + assert(!hasJSInterop) + val fb = newFunctionBuilder(genFunctionID.unbox(FloatRef)) val xParam = fb.addParam("x", RefType.anyref) val resultType = transformPrimType(FloatType) @@ -1366,7 +1374,8 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { } private def genUnboxDouble()(implicit ctx: WasmContext): Unit = { - assert(targetPureWasm) + assert(!hasJSInterop) + val fb = newFunctionBuilder(genFunctionID.unbox(DoubleRef)) val xParam = fb.addParam("x", RefType.anyref) val resultType = transformPrimType(DoubleType) @@ -1470,7 +1479,8 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { } private def genTestFloat()(implicit ctx: WasmContext): Unit = { - assert(targetPureWasm) + assert(!hasJSInterop) + val fb = newFunctionBuilder(genFunctionID.typeTest(FloatRef)) val xParam = fb.addParam("x", RefType.anyref) fb.setResultType(Int32) @@ -1504,7 +1514,7 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { } private def genTestInteger()(implicit ctx: WasmContext): Unit = { - assert(targetPureWasm) + assert(!hasJSInterop) val fb = newFunctionBuilder(genFunctionID.typeTest(IntRef)) val xParam = fb.addParam("x", RefType.anyref) @@ -1559,7 +1569,7 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { } private def genTestDouble()(implicit ctx: WasmContext): Unit = { - assert(targetPureWasm) + assert(!hasJSInterop) val fb = newFunctionBuilder(genFunctionID.typeTest(DoubleRef)) val xParam = fb.addParam("x", RefType.anyref) @@ -1588,7 +1598,8 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { } private def genStringLiteral()(implicit ctx: WasmContext): Unit = { - assert(targetPureWasm, "genStringLiteral should be generated only for Wasm only target") + assert(!hasJSInterop, "genStringLiteral should be generated only for Wasm only target") + val fb = newFunctionBuilder(genFunctionID.stringLiteral) val offsetParam = fb.addParam("offset", Int32) val sizeParam = fb.addParam("size", Int32) @@ -1696,7 +1707,7 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { // for the StructSet typeData.name near the end fb += LocalGet(typeDataParam) - if (!targetPureWasm) { + if (hasJSInterop) { /* if it was null, the typeData must represent an array type, and its * component type cannot be a primitive type; * compute its name from the component type name. @@ -1816,7 +1827,7 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { val doubleValueLocal = fb.addLocal("doubleValue", Float64) fb.setResultType(stringType) - if (targetPureWasm) { + if (!hasJSInterop) { val doubleBoxType = RefType(genTypeID.forClass(SpecialNames.DoubleBoxClass)) fb.block(anyref) { notOurObjectLabel => fb.block(objectType) { isCharLabel => @@ -1901,7 +1912,7 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { } } - if (targetPureWasm) { + if (!hasJSInterop) { // shouldn't reach here fb += Drop fb ++= ctx.stringPool.getConstantStringInstr("TODO") @@ -2067,13 +2078,13 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { case UndefType => fb += Call(genFunctionID.isUndef) case StringType => - if (targetPureWasm) { + if (!hasJSInterop) { fb += RefTest(RefType(genTypeID.wasmString)) } else { fb += ExternConvertAny fb += Call(genFunctionID.stringBuiltins.test) } - case ByteType | ShortType | IntType | FloatType | DoubleType if targetPureWasm => + case ByteType | ShortType | IntType | FloatType | DoubleType if !hasJSInterop => fb += Call(genFunctionID.typeTest(DoubleRef)) case primType: PrimTypeWithRef => fb += Call(genFunctionID.typeTest(primType.primRef)) @@ -2086,18 +2097,18 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { fb += GlobalGet(genGlobalID.undef) case StringType => fb += LocalGet(objParam) - if (targetPureWasm) { + if (!hasJSInterop) { fb += RefCast(RefType(genTypeID.wasmString)) } else { fb += ExternConvertAny fb += RefAsNonNull } - case ByteType | ShortType | IntType if targetPureWasm => + case ByteType | ShortType | IntType if !hasJSInterop => fb += LocalGet(objParam) fb += Call(genFunctionID.unbox(DoubleRef)) fb += I32TruncF64S - case FloatType if targetPureWasm => + case FloatType if !hasJSInterop => fb += LocalGet(objParam) fb += Call(genFunctionID.unbox(DoubleRef)) fb += F32DemoteF64 @@ -2110,7 +2121,7 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { fb += LocalGet(objParam) primType match { case StringType => - if (targetPureWasm) { + if (!hasJSInterop) { fb += RefCast(RefType.nullable(genTypeID.wasmString)) } else { fb += ExternConvertAny @@ -2118,19 +2129,20 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { } // subtype of DoubleType (Byte, Short, Int, Float, and Double) - case ByteType | ShortType | IntType if targetPureWasm => + case ByteType | ShortType | IntType if !hasJSInterop => fb += Call(genFunctionID.unbox(DoubleRef)) fb += I32TruncF64S fb += Call(genFunctionID.box(primType.asInstanceOf[PrimTypeWithRef].primRef)) - case FloatType if targetPureWasm => + case FloatType if !hasJSInterop => fb += Call(genFunctionID.unbox(DoubleRef)) fb += F32DemoteF64 fb += Call(genFunctionID.box(FloatRef)) - case p: PrimTypeWithRef if targetPureWasm => + case p: PrimTypeWithRef if !hasJSInterop => fb += Call(genFunctionID.unbox(p.primRef)) fb += Call(genFunctionID.box(p.primRef)) + case _ => } @@ -2266,13 +2278,11 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { maybeWrapInUBE(fb, semantics.arrayIndexOutOfBounds) { genNewScalaClass(fb, ArrayIndexOutOfBoundsExceptionClass, SpecialNames.StringArgConstructorName) { - if (targetPureWasm) { - fb += LocalGet(indexParam) - fb += Call(genFunctionID.itoa) - } else { - fb += LocalGet(indexParam) + fb += LocalGet(indexParam) + if (hasJSInterop) fb += Call(genFunctionID.intToString) - } + else + fb += Call(genFunctionID.itoa) } } genThrow(fb, fakeResult = Nil) @@ -2294,13 +2304,11 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { maybeWrapInUBE(fb, semantics.negativeArraySizes) { genNewScalaClass(fb, NegativeArraySizeExceptionClass, SpecialNames.StringArgConstructorName) { - if (targetPureWasm) { - fb += LocalGet(sizeParam) - fb += Call(genFunctionID.itoa) - } else { - fb += LocalGet(sizeParam) + fb += LocalGet(sizeParam) + if (hasJSInterop) fb += Call(genFunctionID.intToString) - } + else + fb += Call(genFunctionID.itoa) } } genThrow(fb, fakeResult = Nil) @@ -2529,7 +2537,7 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { fb += LocalGet(typeDataParam) // typeData := new typeData(...) - if (targetPureWasm) { + if (!hasJSInterop) { fb += I32Const(0) // nameOffset fb += I32Const(0) // nameSize fb += I32Const(0) // nameStringIndex @@ -2619,11 +2627,10 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { // if index unsigned_>= str.length fb += LocalGet(indexParam) fb += LocalGet(strParam) - if (targetPureWasm) { - fb += StructGet(genTypeID.wasmString, genFieldID.wasmString.length) - } else { + if (hasJSInterop) fb += Call(genFunctionID.stringBuiltins.length) - } + else + fb += StructGet(genTypeID.wasmString, genFieldID.wasmString.length) fb += I32GeU // unsigned comparison makes negative values of index larger than the length fb.ifThen() { // then, throw a StringIndexOutOfBoundsException @@ -2834,7 +2841,7 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { }, List(KindBoxedString) -> { () => fb += LocalGet(valueParam) - if (targetPureWasm) { + if (!hasJSInterop) { fb += RefTest(RefType(genTypeID.wasmString)) } else { fb += ExternConvertAny @@ -2843,7 +2850,7 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { }, // case KindJSType | KindJSTypeWithSuperClass => call typeData.isJSClassInstance(value) or throw if it is null List(KindJSType, KindJSTypeWithSuperClass) -> { () => - if (targetPureWasm) { + if (!hasJSInterop) { fb += Unreachable // shouldn't reach here } else { fb.block(RefType.anyref) { isJSClassInstanceIsNull => @@ -2911,8 +2918,10 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { // Load (1 << jsValueType(valueNonNull)) fb += I32Const(1) fb += LocalGet(valueNonNullLocal) - if (targetPureWasm) fb += Call(genFunctionID.scalaValueType) - else fb += Call(genFunctionID.jsValueType) + if (hasJSInterop) + fb += Call(genFunctionID.jsValueType) + else + fb += Call(genFunctionID.scalaValueType) fb += I32Shl // if ((... & specialInstanceTypes) != 0) @@ -3283,7 +3292,7 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { } else { val arrayTypeRef = ArrayTypeRef(baseRef, 1) fb += GlobalGet(genGlobalID.forArrayVTable(baseRef)) - if (targetPureWasm) { + if (!hasJSInterop) { fb += I32Const(0) // idHashCode } fb += LocalGet(lengthParam) @@ -3301,7 +3310,8 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { fb += LocalGet(componentTypeDataLocal) fb += I32Const(1) fb += Call(genFunctionID.specificArrayTypeData) - if (targetPureWasm) fb += I32Const(0) // idHashCode + if (!hasJSInterop) + fb += I32Const(0) // idHashCode fb += LocalGet(lengthParam) fb += ArrayNewDefault(genTypeID.underlyingOf(arrayTypeRef)) @@ -3395,8 +3405,10 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { fb.switch() { () => // scrutinee fb += LocalGet(valueParam) - if (targetPureWasm) fb += Call(genFunctionID.scalaValueType) - else fb += Call(genFunctionID.jsValueType) + if (hasJSInterop) + fb += Call(genFunctionID.jsValueType) + else + fb += Call(genFunctionID.scalaValueType) }( // case JSValueTypeFalse, JSValueTypeTrue => typeDataOf[jl.Boolean] List(JSValueTypeFalse, JSValueTypeTrue) -> { () => @@ -3511,7 +3523,7 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { fb.ifThenElse(typeDataType) { fb += getHijackedClassTypeDataInstr(BoxedLongClass) } { - if (targetPureWasm) { + if (!hasJSInterop) { fb += LocalGet(ourObjectLocal) fb += RefTest(RefType(genTypeID.forClass(SpecialNames.IntegerBoxClass))) fb.ifThenElse(typeDataType) { @@ -3583,7 +3595,7 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { fb += Return } - if (targetPureWasm) { + if (!hasJSInterop) { // In pure Wasm, the boxed primitives are our objects, and // the scalaValueType tests are needed. fb += LocalSet(objNonNullLocal) @@ -3597,8 +3609,10 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { fb.ifThen() { fb.switch() { () => fb += LocalGet(objNonNullLocal) - if (targetPureWasm) fb += Call(genFunctionID.scalaValueType) - else fb += Call(genFunctionID.jsValueType) + if (hasJSInterop) + fb += Call(genFunctionID.jsValueType) + else + fb += Call(genFunctionID.scalaValueType) }( Seq( List(JSValueTypeFalse) -> { () => @@ -3611,11 +3625,11 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { }, List(JSValueTypeString) -> { () => fb += LocalGet(objNonNullLocal) - if (!targetPureWasm) fb += ExternConvertAny - else fb += RefCast(RefType(genTypeID.wasmString)) - fb += Call( - genFunctionID.forMethod(Public, BoxedStringClass, hashCodeMethodName) - ) + if (hasJSInterop) + fb += ExternConvertAny + else + fb += RefCast(RefType(genTypeID.wasmString)) + fb += Call(genFunctionID.forMethod(Public, BoxedStringClass, hashCodeMethodName)) fb += Return }, List(JSValueTypeNumber) -> { () => @@ -3630,7 +3644,7 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { fb += I32Const(0) // specified by jl.Void.hashCode(), Scala.js only fb += Return } - ) ++ (if (!targetPureWasm) { + ) ++ (if (hasJSInterop) { List( List(JSValueTypeBigInt) -> { () => fb += LocalGet(objNonNullLocal) @@ -3660,7 +3674,7 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { // If we get here, use the idHashCodeMap - if (!targetPureWasm) { + if (hasJSInterop) { // Read the existing idHashCode, if one exists fb += GlobalGet(genGlobalID.idHashCodeMap) fb += LocalGet(objNonNullLocal) @@ -3822,7 +3836,7 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { // If we get here, we did not find the method; throw. - if (targetPureWasm) { + if (!hasJSInterop) { genNewScalaClass(fb, ArrayIndexOutOfBoundsExceptionClass, SpecialNames.StringArgConstructorName) { fb ++= ctx.stringPool.getConstantStringInstr("Method not found") @@ -3891,7 +3905,8 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { // Build the result arrayStruct fb += LocalGet(fromLocal) fb += StructGet(arrayStructTypeID, genFieldID.objStruct.vtable) // vtable - if (targetPureWasm) fb += I32Const(0) // idHashCode + if (!hasJSInterop) + fb += I32Const(0) // idHashCode fb += LocalGet(resultUnderlyingLocal) fb += StructNew(arrayStructTypeID) @@ -3961,10 +3976,10 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { maybeWrapInUBE(fb, semantics.arrayIndexOutOfBounds) { genNewScalaClass(fb, ArrayIndexOutOfBoundsExceptionClass, SpecialNames.StringArgConstructorName) { - if (targetPureWasm) - fb += RefNull(HeapType(genTypeID.wasmString)) - else + if (hasJSInterop) fb += RefNull(HeapType.NoExtern) + else + fb += RefNull(HeapType(genTypeID.wasmString)) } } genThrow(fb, fakeResult = Nil) @@ -4147,10 +4162,10 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { maybeWrapInUBE(fb, semantics.arrayStores) { genNewScalaClass(fb, ArrayStoreExceptionClass, SpecialNames.StringArgConstructorName) { - if (targetPureWasm) - fb += RefNull(HeapType(genTypeID.wasmString)) - else + if (hasJSInterop) fb += RefNull(HeapType.NoExtern) + else + fb += RefNull(HeapType(genTypeID.wasmString)) } } genThrow(fb, fakeResult = Nil) @@ -4160,7 +4175,8 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { } private def genStringConcat()(implicit ctx: WasmContext): Unit = { - assert(targetPureWasm, "stringConcat should be generated only for Wasm only target.") + assert(!hasJSInterop, "stringConcat should be generated only for Wasm only target.") + val fb = newFunctionBuilder(genFunctionID.wasmString.stringConcat) val str1 = fb.addParam("str1", stringType) @@ -4184,7 +4200,8 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { } private def genStringEquals()(implicit ctx: WasmContext): Unit = { - assert(targetPureWasm, "stringEquals should be generated only for Wasm only target.") + assert(!hasJSInterop, "stringEquals should be generated only for Wasm only target.") + val fb = newFunctionBuilder(genFunctionID.wasmString.stringEquals) val str1 = fb.addParam("str1", nullableStringType) val str2 = fb.addParam("str2", nullableStringType) @@ -4277,7 +4294,8 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { } private def genCharCodeAt()(implicit ctx: WasmContext): Unit = { - assert(targetPureWasm, "charCodeAt should be generated only for Wasm only target.") + assert(!hasJSInterop, "charCodeAt should be generated only for Wasm only target.") + val fb = newFunctionBuilder(genFunctionID.wasmString.charCodeAt) val strParam = fb.addParam("str", RefType(genTypeID.wasmString)) @@ -4294,7 +4312,8 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { } private def genGetWholeChars()(implicit ctx: WasmContext): Unit = { - assert(targetPureWasm, "getWholeChars should be generated only for Wasm only target.") + assert(!hasJSInterop, "getWholeChars should be generated only for Wasm only target.") + val fb = newFunctionBuilder(genFunctionID.wasmString.getWholeChars) val strParam = fb.addParam("str", RefType(genTypeID.wasmString)) @@ -4316,7 +4335,8 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { } private def genCollapseString()(implicit ctx: WasmContext): Unit = { - assert(targetPureWasm, "collapseString should be generated only for Wasm only target.") + assert(!hasJSInterop, "collapseString should be generated only for Wasm only target.") + val fb = newFunctionBuilder(genFunctionID.wasmString.collapseString) val strParam = fb.addParam("str", RefType(genTypeID.wasmString)) @@ -4383,7 +4403,7 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { } private def genUndefinedAndIsUndef()(implicit ctx: WasmContext): Unit = { - assert(targetPureWasm) + assert(!hasJSInterop) ctx.mainRecType.addSubType( genTypeID.undefined, @@ -4410,7 +4430,8 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { // TODO: https://262.ecma-international.org/#sec-numeric-types-number-remainder private def genNaiveFmod()(implicit ctx: WasmContext): Unit = { - assert(targetPureWasm) + assert(!hasJSInterop) + // f32.fmod locally { val fb = newFunctionBuilder(genFunctionID.f32Fmod) @@ -4465,11 +4486,10 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { fb += I64Eqz fb.ifThen() { fb += I32Const('0'.toInt) - if (targetPureWasm) { - SWasmGen.genWasmStringFromCharCode(fb) - } else { + if (hasJSInterop) fb += Call(genFunctionID.stringBuiltins.fromCharCode) - } + else + SWasmGen.genWasmStringFromCharCode(fb) fb += Return } @@ -4616,7 +4636,8 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { * the return value should be compatible with jsValueType. */ private def genScalaValueType()(implicit ctx: WasmContext): Unit = { - assert(targetPureWasm) + assert(!hasJSInterop) + val fb = newFunctionBuilder(genFunctionID.scalaValueType) val xParam = fb.addParam("x", RefType.any) fb.setResultType(Int32) @@ -4759,8 +4780,10 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) { } private def genStringConcat(fb: FunctionBuilder): Unit = { - if (targetPureWasm) fb += Call(genFunctionID.wasmString.stringConcat) - else fb += Call(genFunctionID.stringBuiltins.concat) + if (hasJSInterop) + fb += Call(genFunctionID.stringBuiltins.concat) + else + fb += Call(genFunctionID.wasmString.stringConcat) } } diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/DerivedClasses.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/DerivedClasses.scala index c4b86e0d5c..5de8da302e 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/DerivedClasses.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/DerivedClasses.scala @@ -24,7 +24,7 @@ import org.scalajs.ir.Types._ import org.scalajs.ir.WellKnownNames._ import org.scalajs.ir.{EntryPointsInfo, Version} -import org.scalajs.linker.interface.IRFile +import org.scalajs.linker.interface.{IRFile, ModuleKind} import org.scalajs.linker.interface.unstable.IRFileImpl import org.scalajs.linker.standard.{LinkedClass, CoreSpec} @@ -34,7 +34,7 @@ import SpecialNames._ /** Derives `CharacterBox` and `LongBox` from `jl.Character` and `jl.Long`. */ object DerivedClasses { def deriveClasses(classes: List[LinkedClass], coreSpec: CoreSpec): List[LinkedClass] = { - if (coreSpec.wasmFeatures.targetPureWasm) { + if (coreSpec.moduleKind != ModuleKind.ESModule) { classes.collect { case clazz if clazz.className == BoxedCharacterClass || diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/Emitter.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/Emitter.scala index 02593ceebf..8c31b6f1c1 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/Emitter.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/Emitter.scala @@ -62,8 +62,8 @@ final class Emitter(config: Emitter.Config) { def emit(module: ModuleSet.Module, globalInfo: LinkedGlobalInfo, logger: Logger): Result = { val (wasmModule, jsFileContentInfo) = emitWasmModule(module, globalInfo) val loaderContent = - if (coreSpec.wasmFeatures.targetPureWasm) LoaderContent.pureWasmBytesContent - else LoaderContent.bytesContent + if (coreSpec.moduleKind == ModuleKind.ESModule) LoaderContent.bytesContent + else LoaderContent.noJSInteropBytesContent val jsFileContent = buildJSFileContent(module, jsFileContentInfo) new Result(wasmModule, loaderContent, jsFileContent) @@ -201,11 +201,10 @@ final class Emitter(config: Emitter.Config) { ModuleInitializerImpl.fromInitializer(init) match { case ModuleInitializerImpl.MainMethodWithArgs(className, encodedMainMethodName, args) => val stringArrayTypeRef = ArrayTypeRef(ClassRef(BoxedStringClass), 1) - SWasmGen.genArrayValue( - fb, stringArrayTypeRef, args.size, coreSpec.wasmFeatures.targetPureWasm) { + SWasmGen.genArrayValue(fb, stringArrayTypeRef, args.size) { for (arg <- args) { fb ++= ctx.stringPool.getConstantStringInstr(arg) - if (!coreSpec.wasmFeatures.targetPureWasm) + if (ctx.hasJSInterop) fb += wa.AnyConvertExtern } } @@ -228,7 +227,7 @@ final class Emitter(config: Emitter.Config) { fb += wa.I32Const(c.toInt) fb += wa.ArrayNewFixed(genTypeID.i16Array, message.length()) - if (coreSpec.wasmFeatures.componentModel) + if (coreSpec.moduleKind == ModuleKind.WasmComponent) fb += wa.Drop // TODO else fb += wa.Call(genFunctionID.wasmEssentials.print) @@ -594,7 +593,7 @@ object Emitter { callStaticMethod(WasmRuntimeClass, fmodfMethodName), callStaticMethod(WasmRuntimeClass, fmoddMethodName), - cond(coreSpec.wasmFeatures.targetPureWasm) { + cond(coreSpec.moduleKind != ModuleKind.ESModule) { callStaticMethod(RyuDoubleClass, doubleToStringMethodName) } ) diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/FunctionEmitter.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/FunctionEmitter.scala index 99f58b24f9..ec808ce83a 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/FunctionEmitter.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/FunctionEmitter.scala @@ -328,7 +328,6 @@ private class FunctionEmitter private ( private val coreSpec = ctx.coreSpec import coreSpec.semantics - import coreSpec.wasmFeatures.targetPureWasm private var currentExceptionHandler: Option[wanme.LabelID] = null private var currentNPELabel: Option[wanme.LabelID] = null @@ -691,24 +690,23 @@ private class FunctionEmitter private ( case t: Skip => VoidType // JavaScript expressions - case t: JSNew if !targetPureWasm => genJSNew(t) - case t: JSSelect if !targetPureWasm => genJSSelect(t, castTo = expectedType) - case t: JSFunctionApply if !targetPureWasm => genJSFunctionApply(t, castTo = expectedType) - case t: JSMethodApply if !targetPureWasm => genJSMethodApply(t, castTo = expectedType) - case t: JSImportCall if !targetPureWasm => genJSImportCall(t) - case t: JSImportMeta if !targetPureWasm => genJSImportMeta(t) - case t: LoadJSConstructor if !targetPureWasm => genLoadJSConstructor(t) - case t: LoadJSModule if !targetPureWasm => genLoadJSModule(t) - case t: SelectJSNativeMember if !targetPureWasm => - genSelectJSNativeMember(t, castTo = expectedType) - case t: JSDelete if !targetPureWasm => genJSDelete(t) - case t: JSUnaryOp if !targetPureWasm => genJSUnaryOp(t, castTo = expectedType) - case t: JSBinaryOp if !targetPureWasm => genJSBinaryOp(t, castTo = expectedType) - case t: JSArrayConstr if !targetPureWasm => genJSArrayConstr(t) - case t: JSObjectConstr if !targetPureWasm => genJSObjectConstr(t) - case t: JSGlobalRef if !targetPureWasm => genJSGlobalRef(t, castTo = expectedType) - case t: JSTypeOfGlobalRef if !targetPureWasm => genJSTypeOfGlobalRef(t, castTo = expectedType) - case t: Closure => genClosure(t) + case t: JSNew => genJSNew(t) + case t: JSSelect => genJSSelect(t, castTo = expectedType) + case t: JSFunctionApply => genJSFunctionApply(t, castTo = expectedType) + case t: JSMethodApply => genJSMethodApply(t, castTo = expectedType) + case t: JSImportCall => genJSImportCall(t) + case t: JSImportMeta => genJSImportMeta(t) + case t: LoadJSConstructor => genLoadJSConstructor(t) + case t: LoadJSModule => genLoadJSModule(t) + case t: SelectJSNativeMember => genSelectJSNativeMember(t, castTo = expectedType) + case t: JSDelete => genJSDelete(t) + case t: JSUnaryOp => genJSUnaryOp(t, castTo = expectedType) + case t: JSBinaryOp => genJSBinaryOp(t, castTo = expectedType) + case t: JSArrayConstr => genJSArrayConstr(t) + case t: JSObjectConstr => genJSObjectConstr(t) + case t: JSGlobalRef => genJSGlobalRef(t, castTo = expectedType) + case t: JSTypeOfGlobalRef => genJSTypeOfGlobalRef(t, castTo = expectedType) + case t: Closure => genClosure(t) // array case t: NewArray => genNewArray(t) @@ -716,11 +714,11 @@ private class FunctionEmitter private ( case t: ArrayValue => genArrayValue(t) // Non-native JS classes - case t: CreateJSClass if !targetPureWasm => genCreateJSClass(t) - case t: JSPrivateSelect if !targetPureWasm => genJSPrivateSelect(t) - case t: JSSuperSelect if !targetPureWasm => genJSSuperSelect(t) - case t: JSSuperMethodCall if !targetPureWasm => genJSSuperMethodCall(t, castTo = expectedType) - case t: JSNewTarget if !targetPureWasm => genJSNewTarget(t) + case t: CreateJSClass => genCreateJSClass(t) + case t: JSPrivateSelect => genJSPrivateSelect(t) + case t: JSSuperSelect => genJSSuperSelect(t) + case t: JSSuperMethodCall => genJSSuperMethodCall(t, castTo = expectedType) + case t: JSNewTarget => genJSNewTarget(t) // Records (only generated by the optimizer) case t: RecordSelect => genRecordSelect(t) @@ -761,7 +759,7 @@ private class FunctionEmitter private ( primType match { case NullType => expectedType match { - case ClassType(BoxedStringClass, true, _) if !targetPureWasm => + case ClassType(BoxedStringClass, true, _) if ctx.hasJSInterop => fb += wa.ExternConvertAny case _ => () } @@ -789,7 +787,7 @@ private class FunctionEmitter private ( } case (StringType | ClassType(BoxedStringClass, _, _), _) => - if (!targetPureWasm) { + if (ctx.hasJSInterop) { expectedType match { case ClassType(BoxedStringClass, _, _) => () case _ => fb += wa.AnyConvertExtern @@ -872,7 +870,7 @@ private class FunctionEmitter private ( def genRhs(): Unit = { genTree(rhs, lhs.tpe) lhs.tpe match { - case ClassType(BoxedStringClass, _, _) if !targetPureWasm => + case ClassType(BoxedStringClass, _, _) if ctx.hasJSInterop => fb += wa.AnyConvertExtern case _ => () } @@ -914,19 +912,19 @@ private class FunctionEmitter private ( s"ArraySelect.array must be an array type, but has type ${array.tpe}") } - case JSPrivateSelect(qualifier, field) if !targetPureWasm => + case JSPrivateSelect(qualifier, field) => genTree(qualifier, AnyType) genTree(rhs, AnyType) markPosition(tree) fb += wa.Call(genFunctionID.forPrivateJSFieldSetter(field.name)) - case JSSelect(qualifier, item) if !targetPureWasm => + case JSSelect(qualifier, item) => genThroughCustomJSHelper(List(qualifier, item, rhs), VoidType) { allJSArgs => val List(jsQualifier, jsItem, jsRhs) = allJSArgs js.Assign(js.BracketSelect.makeOptimized(jsQualifier, jsItem), jsRhs) } - case JSSuperSelect(superClass, receiver, item) if !targetPureWasm => + case JSSuperSelect(superClass, receiver, item) => genTree(superClass, AnyType) genTree(receiver, AnyType) genTree(item, AnyType) @@ -934,7 +932,7 @@ private class FunctionEmitter private ( markPosition(tree) fb += wa.Call(genFunctionID.jsSuperSelectSet) - case lhs @ JSGlobalRef(name) if !targetPureWasm => + case lhs @ JSGlobalRef(name) => val builder = new CustomJSHelperBuilderWithTreeSupport() val rhsRef = builder.addInput(rhs) val helperID = builder.build(VoidType) { @@ -1248,7 +1246,7 @@ private class FunctionEmitter private ( if (methodName == toStringMethodName) { // By spec, toString() is special assert(argsLocals.isEmpty) - if (targetPureWasm) + if (!ctx.hasJSInterop) fb += wa.Call(genFunctionID.hijackedValueToString) else fb += wa.Call(genFunctionID.jsValueToString) @@ -1259,8 +1257,10 @@ private class FunctionEmitter private ( genHijackedClassCall(BoxedDoubleClass) } else if (receiverClassName == CharSequenceClass) { // the value must be a `string` - if (!targetPureWasm) fb += wa.ExternConvertAny - else fb += wa.RefCast(watpe.RefType(genTypeID.wasmString)) + if (ctx.hasJSInterop) + fb += wa.ExternConvertAny + else + fb += wa.RefCast(watpe.RefType(genTypeID.wasmString)) pushArgs(argsLocals) genHijackedClassCall(BoxedStringClass) } else if (methodName == compareToMethodName) { @@ -1273,7 +1273,7 @@ private class FunctionEmitter private ( val receiverLocal = addSyntheticLocal(watpe.RefType.any) fb += wa.LocalTee(receiverLocal) - if (!targetPureWasm) { + if (ctx.hasJSInterop) { val jsValueTypeLocal = addSyntheticLocal(watpe.Int32) fb += wa.Call(genFunctionID.jsValueType) fb += wa.LocalTee(jsValueTypeLocal) @@ -1440,7 +1440,7 @@ private class FunctionEmitter private ( case Some(primReceiverType) => if (receiver.tpe == primReceiverType) { genTreeAuto(receiver) - } else if (targetPureWasm && + } else if (!ctx.hasJSInterop && receiver.tpe == ClassType(BoxedStringClass, false, false) && primReceiverType == StringType) { genTreeAuto(receiver) @@ -1535,14 +1535,13 @@ private class FunctionEmitter private ( * type in the IR but they get a `void` expected type. */ expectedType - } else if (tree.isInstanceOf[Null] && + } else if (tree.isInstanceOf[Null] && ctx.hasJSInterop && expectedType == ClassType(BoxedStringClass, true, false)) { /* Directly emit a `ref.null noextern` instead of requiring an * `extern.convert_from_any` in `genAdapt`. */ markPosition(tree) - if (targetPureWasm) fb += wa.RefNull(watpe.HeapType(genTypeID.wasmString)) - else fb += wa.RefNull(watpe.HeapType.NoExtern) + fb += wa.RefNull(watpe.HeapType.NoExtern) expectedType } else { markPosition(tree) @@ -1699,8 +1698,10 @@ private class FunctionEmitter private ( // String.length case String_length => - if (targetPureWasm) fb += wa.StructGet(genTypeID.wasmString, genFieldID.wasmString.length) - else fb += wa.Call(genFunctionID.stringBuiltins.length) + if (ctx.hasJSInterop) + fb += wa.Call(genFunctionID.stringBuiltins.length) + else + fb += wa.StructGet(genTypeID.wasmString, genFieldID.wasmString.length) // Null check case CheckNotNull => @@ -1860,7 +1861,7 @@ private class FunctionEmitter private ( */ private def genThrow(): Unit = { if (ctx.coreSpec.wasmFeatures.exceptionHandling) { - if (!targetPureWasm) + if (ctx.hasJSInterop) fb += wa.ExternConvertAny fb += wa.Throw(genTagID.exception) } else { @@ -1957,11 +1958,10 @@ private class FunctionEmitter private ( genTree(rhs, IntType) markPosition(tree) if (semantics.stringIndexOutOfBounds == CheckedBehavior.Unchecked) { - if (targetPureWasm) { - fb += wa.Call(genFunctionID.wasmString.charCodeAt) - } else { + if (ctx.hasJSInterop) fb += wa.Call(genFunctionID.stringBuiltins.charCodeAt) - } + else + fb += wa.Call(genFunctionID.wasmString.charCodeAt) } else { fb += wa.Call(genFunctionID.checkedStringCharAt) genForwardThrow() @@ -2071,7 +2071,7 @@ private class FunctionEmitter private ( fb += wa.RefIsNull maybeGenInvert() BooleanType - } else if (targetPureWasm && isStringType(lhsType) && isStringType(rhsType)) { + } else if (isStringType(lhsType) && isStringType(rhsType)) { genTreeAuto(lhs) genTreeAuto(rhs) markPosition(tree) @@ -2191,8 +2191,10 @@ private class FunctionEmitter private ( genToStringForConcat(lhs) genToStringForConcat(rhs) markPosition(tree) - if (targetPureWasm) fb += wa.Call(genFunctionID.wasmString.stringConcat) - else fb += wa.Call(genFunctionID.stringBuiltins.concat) + if (ctx.hasJSInterop) + fb += wa.Call(genFunctionID.stringBuiltins.concat) + else + fb += wa.Call(genFunctionID.wasmString.stringConcat) } StringType @@ -2200,7 +2202,9 @@ private class FunctionEmitter private ( private def genToStringForConcat(tree: Tree): Unit = { val stringType = - if (targetPureWasm) watpe.RefType(genTypeID.wasmString) else watpe.RefType.extern + if (ctx.hasJSInterop) watpe.RefType.extern + else watpe.RefType(genTypeID.wasmString) + def genWithDispatch(needHijackedClassDispatch: Boolean): Unit = { // TODO Better codegen when non-nullable @@ -2283,10 +2287,10 @@ private class FunctionEmitter private ( } // end block labelNotOurObject // Now we have a value that is not one of our objects; the anyref is still on the stack - if (targetPureWasm) - fb += wa.Call(genFunctionID.hijackedValueToString) - else + if (ctx.hasJSInterop) fb += wa.Call(genFunctionID.jsValueToStringForConcat) + else + fb += wa.Call(genFunctionID.hijackedValueToString) } // end block labelDone } } @@ -2301,7 +2305,7 @@ private class FunctionEmitter private ( case StringType => () // no-op case BooleanType => - if (targetPureWasm) { + if (!ctx.hasJSInterop) { fb += wa.I32Eqz fb.ifThenElse(stringType) { fb ++= ctx.stringPool.getConstantStringInstr("false") @@ -2312,7 +2316,7 @@ private class FunctionEmitter private ( fb += wa.Call(genFunctionID.booleanToString) } case CharType => - if (targetPureWasm) { + if (!ctx.hasJSInterop) { fb += wa.ArrayNewFixed(genTypeID.i16Array, 1) fb += wa.I32Const(1) fb += wa.RefNull(watpe.HeapType(genTypeID.wasmString)) @@ -2321,20 +2325,20 @@ private class FunctionEmitter private ( fb += wa.Call(genFunctionID.stringBuiltins.fromCharCode) } case ByteType | ShortType | IntType => - if (targetPureWasm) { + if (!ctx.hasJSInterop) { fb += wa.Call(genFunctionID.itoa) } else { fb += wa.Call(genFunctionID.intToString) } case LongType => - if (targetPureWasm) { + if (!ctx.hasJSInterop) { fb += wa.Call(genFunctionID.ltoa) } else { fb += wa.Call(genFunctionID.longToString) } case FloatType => fb += wa.F64PromoteF32 - if (targetPureWasm) { + if (!ctx.hasJSInterop) { fb += wa.Call(genFunctionID.forMethod(MemberNamespace.PublicStatic, SpecialNames.RyuDoubleClass, SpecialNames.doubleToStringMethodName)) fb += wa.RefAsNonNull @@ -2342,22 +2346,19 @@ private class FunctionEmitter private ( fb += wa.Call(genFunctionID.doubleToString) } case DoubleType => - if (targetPureWasm) { + if (!ctx.hasJSInterop) { fb += wa.Call(genFunctionID.forMethod(MemberNamespace.PublicStatic, SpecialNames.RyuDoubleClass, SpecialNames.doubleToStringMethodName)) fb += wa.RefAsNonNull } else { fb += wa.Call(genFunctionID.doubleToString) } - case NullType | UndefType => - if (targetPureWasm) { - fb += wa.Drop - fb ++= - (if (primType == NullType) ctx.stringPool.getConstantStringInstr("null") - else ctx.stringPool.getConstantStringInstr("undefined")) - } else { - fb += wa.Call(genFunctionID.jsValueToStringForConcat) - } + case NullType => + fb += wa.Drop + fb ++= ctx.stringPool.getConstantStringInstr("null") + case UndefType => + fb += wa.Drop + fb ++= ctx.stringPool.getConstantStringInstr("undefined") case NothingType => () // unreachable case VoidType => @@ -2509,7 +2510,7 @@ private class FunctionEmitter private ( case UndefType => fb += wa.Call(genFunctionID.isUndef) case StringType => - if (targetPureWasm) { + if (!ctx.hasJSInterop) { fb += wa.RefTest(watpe.RefType(genTypeID.wasmString)) } else { fb += wa.ExternConvertAny @@ -2759,7 +2760,7 @@ private class FunctionEmitter private ( fb += wa.GlobalGet(genGlobalID.undef) case StringType => - if (targetPureWasm) { + if (!ctx.hasJSInterop) { fb += wa.RefCast(watpe.RefType.nullable(genTypeID.wasmString)) val sig = watpe.FunctionType(List(watpe.RefType.nullable(genTypeID.wasmString)), List(watpe.RefType(genTypeID.wasmString))) @@ -2929,8 +2930,8 @@ private class FunctionEmitter private ( markPosition(tree) val exceptionType = - if (targetPureWasm) watpe.RefType.anyref - else watpe.RefType.externref + if (ctx.hasJSInterop) watpe.RefType.externref + else watpe.RefType.anyref fb.block(resultType) { doneLabel => fb.block(exceptionType) { catchLabel => @@ -2951,7 +2952,8 @@ private class FunctionEmitter private ( } } // end block $catch withNewLocal(errVarName, errVarOrigName, watpe.RefType.anyref) { exceptionLocal => - if (!targetPureWasm) fb += wa.AnyConvertExtern + if (ctx.hasJSInterop) + fb += wa.AnyConvertExtern fb += wa.LocalSet(exceptionLocal) genTree(handler, expectedType) } @@ -3093,7 +3095,8 @@ private class FunctionEmitter private ( fb += wa.LocalSet(primLocal) fb += wa.GlobalGet(genGlobalID.forVTable(boxClassName)) - if (targetPureWasm) fb += wa.I32Const(0) // idHashCode + if (!ctx.hasJSInterop) + fb += wa.I32Const(0) // idHashCode fb += wa.LocalGet(primLocal) fb += wa.StructNew(genTypeID.forClass(boxClassName)) } @@ -3361,7 +3364,8 @@ private class FunctionEmitter private ( markPosition(tree) genLoadArrayTypeData(fb, arrayTypeRef) // vtable - if (targetPureWasm) fb += wa.I32Const(0) + if (!ctx.hasJSInterop) + fb += wa.I32Const(0) // idHashCode // Create the underlying array genTree(length, IntType) @@ -3521,7 +3525,7 @@ private class FunctionEmitter private ( throw new AssertionError(s"Invalid array type $arrayTypeRef at ${tree.pos}") } - SWasmGen.genArrayValueFromUnderlying(fb, arrayTypeRef, targetPureWasm) { + SWasmGen.genArrayValueFromUnderlying(fb, arrayTypeRef) { fb += wa.I32Const(offset) fb += wa.I32Const(length) fb += wa.ArrayNewData(genTypeID.underlyingOf(arrayTypeRef), dataID) @@ -3533,7 +3537,7 @@ private class FunctionEmitter private ( case _ => AnyType } - SWasmGen.genArrayValue(fb, arrayTypeRef, elems.size, targetPureWasm) { + SWasmGen.genArrayValue(fb, arrayTypeRef, elems.size) { // Create the underlying array elems.foreach(genTree(_, expectedElemType)) @@ -3548,10 +3552,8 @@ private class FunctionEmitter private ( private def genClosure(tree: Closure): Type = { if (tree.flags.typed) genTypedClosure(tree) - else if (!targetPureWasm) - genJSClosure(tree) else - throw new AssertionError(s"JavaScript closure isn't supported when targetPureWasm = true.") + genJSClosure(tree) } private def genTypedClosure(tree: Closure): Type = { @@ -3594,6 +3596,8 @@ private class FunctionEmitter private ( implicit val pos = tree.pos + assert(ctx.hasJSInterop, s"Unexpected JS Closure at $pos") + val dataStructTypeID = ctx.getClosureDataStructType(captureParams.map(_.ptpe)) // Define the function where captures are reified as a `__captureData` argument. @@ -4016,8 +4020,10 @@ private class FunctionEmitter private ( } private def genStringEquals(): Unit = { - if (targetPureWasm) fb += wa.Call(genFunctionID.wasmString.stringEquals) - else fb += wa.Call(genFunctionID.stringBuiltins.equals) + if (ctx.hasJSInterop) + fb += wa.Call(genFunctionID.stringBuiltins.equals) + else + fb += wa.Call(genFunctionID.wasmString.stringEquals) } /** Generates code with a custom JS helper with direct access to the builder. */ diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/LoaderContent.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/LoaderContent.scala index ea4253f56c..df65f1f2c7 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/LoaderContent.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/LoaderContent.scala @@ -23,8 +23,8 @@ object LoaderContent { val bytesContent: Array[Byte] = stringContent.getBytes(StandardCharsets.UTF_8) - val pureWasmBytesContent: Array[Byte] = - pureWasmStringContent.getBytes(StandardCharsets.UTF_8) + val noJSInteropBytesContent: Array[Byte] = + noJSInteropStringContent.getBytes(StandardCharsets.UTF_8) private def stringContent: String = { raw""" @@ -225,7 +225,7 @@ export async function load(wasmFileURL, exportSetters, privateJSFieldGetters, """ } - private def pureWasmStringContent: String = { + private def noJSInteropStringContent: String = { import org.scalajs.ir.OriginalName.NoOriginalName import org.scalajs.ir.OriginalName import org.scalajs.ir.Position.{NoPosition => NoPos} diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/SWasmGen.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/SWasmGen.scala index 7b9f242ee7..0ef02034a9 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/SWasmGen.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/SWasmGen.scala @@ -46,15 +46,11 @@ object SWasmGen { case LongType => I64Const(0L) case FloatType => F32Const(0.0f) case DoubleType => F64Const(0.0) - case StringType => - if (ctx.coreSpec.wasmFeatures.targetPureWasm) - GlobalGet(genGlobalID.emptyStringArray) - else - ctx.stringPool.getEmptyStringInstr() - case UndefType => GlobalGet(genGlobalID.undef) + case StringType => ctx.stringPool.getEmptyStringInstr() + case UndefType => GlobalGet(genGlobalID.undef) case ClassType(BoxedStringClass, true, _) => - if (ctx.coreSpec.wasmFeatures.targetPureWasm) + if (!ctx.hasJSInterop) RefNull(HeapType(genTypeID.wasmString)) else RefNull(Types.HeapType.NoExtern) @@ -109,21 +105,22 @@ object SWasmGen { } } - def genArrayValue(fb: FunctionBuilder, arrayTypeRef: ArrayTypeRef, - length: Int, targetPureWasm: Boolean)( - genElems: => Unit): Unit = { - genArrayValueFromUnderlying(fb, arrayTypeRef, targetPureWasm) { + def genArrayValue(fb: FunctionBuilder, arrayTypeRef: ArrayTypeRef, length: Int)( + genElems: => Unit)( + implicit ctx: WasmContext): Unit = { + genArrayValueFromUnderlying(fb, arrayTypeRef) { // Create the underlying array genElems fb += ArrayNewFixed(genTypeID.underlyingOf(arrayTypeRef), length) } } - def genArrayValueFromUnderlying(fb: FunctionBuilder, arrayTypeRef: ArrayTypeRef, - targetPureWasm: Boolean)( - genUnderlying: => Unit): Unit = { + def genArrayValueFromUnderlying(fb: FunctionBuilder, arrayTypeRef: ArrayTypeRef)( + genUnderlying: => Unit)( + implicit ctx: WasmContext): Unit = { genLoadArrayTypeData(fb, arrayTypeRef) // vtable - if (targetPureWasm) fb += I32Const(0) + if (!ctx.hasJSInterop) + fb += I32Const(0) // idHashCode genUnderlying fb += StructNew(genTypeID.forArrayClass(arrayTypeRef)) } diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/SpecialNames.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/SpecialNames.scala index c56da80124..5ac6dc2176 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/SpecialNames.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/SpecialNames.scala @@ -26,7 +26,6 @@ object SpecialNames { val CharBoxClass = BoxedCharacterClass.withSuffix("Box") val LongBoxClass = BoxedLongClass.withSuffix("Box") - // targetPureWasm val BooleanBoxClass = BoxedBooleanClass.withSuffix("Box") val IntegerBoxClass = BoxedIntegerClass.withSuffix("Box") val DoubleBoxClass = BoxedDoubleClass.withSuffix("Box") diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/TypeTransformer.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/TypeTransformer.scala index 092ef1d803..b4ec3c30c6 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/TypeTransformer.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/TypeTransformer.scala @@ -110,7 +110,7 @@ object TypeTransformer { val heapType: watpe.HeapType = ctx.getClassInfoOption(className) match { case Some(info) => if (className == BoxedStringClass) { - if (ctx.coreSpec.wasmFeatures.targetPureWasm) + if (!ctx.hasJSInterop) watpe.HeapType(genTypeID.wasmString) else watpe.HeapType.Extern // for all the JS string builtin functions @@ -153,7 +153,7 @@ object TypeTransformer { case FloatType => watpe.Float32 case DoubleType => watpe.Float64 case StringType => - if (ctx.coreSpec.wasmFeatures.targetPureWasm) + if (!ctx.hasJSInterop) watpe.RefType(genTypeID.wasmString) else watpe.RefType.extern diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/VarGen.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/VarGen.scala index 0e130966e7..cb389fe7e7 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/VarGen.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/VarGen.scala @@ -57,7 +57,7 @@ object VarGen { case object bZeroLong extends GlobalID case object lastIDHashCode extends GlobalID - // targetPureWasm + // Wasm-only, without JS interop case object stringLiteralCache extends GlobalID case object bZeroBoolean extends GlobalID case object bZeroInteger extends GlobalID @@ -264,7 +264,8 @@ object VarGen { case object equals extends JSHelperFunctionID } - object wasmString { // targetPureWasm + // Wasm-only, without JS interop + object wasmString { // case object stringFromCharCode extends FunctionID case object stringConcat extends FunctionID case object stringEquals extends FunctionID @@ -426,7 +427,7 @@ object VarGen { */ case object reflectiveProxies extends FieldID - /** The name data as the 3 arguments to `stringLiteral`. (targetPureWasm only) + /** The name data as the 3 arguments to `stringLiteral` (only without JS interop). * * It is only meaningful for primitives and for classes. For array types, they are all 0, as * array types compute their `name` from the `name` of their component type. @@ -457,7 +458,9 @@ object VarGen { case object fun extends FieldID } - object wasmString { // targetPureWasm + // Wasm-only, without JS interop + object wasmString { + /** Internal i16Array storage for the characters of the string. * * For concatenation optimizations, this array may initially contain @@ -542,8 +545,8 @@ object VarGen { case object typeDataArray extends TypeID case object reflectiveProxies extends TypeID - case object undefined extends TypeID // targetPureWasm - case object wasmString extends TypeID // targetPureWasm + case object undefined extends TypeID // Wasm-only, without JS interop + case object wasmString extends TypeID // Wasm-only, without JS interop // primitive array types, underlying the Array[T] classes case object i8Array extends TypeID diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/WasmContext.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/WasmContext.scala index 69438b45a8..ac9667394f 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/WasmContext.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/WasmContext.scala @@ -25,8 +25,7 @@ import org.scalajs.ir.Trees.{FieldDef, ParamDef, JSNativeLoadSpec} import org.scalajs.ir.Types._ import org.scalajs.ir.WellKnownNames._ -import org.scalajs.linker.interface.ModuleInitializer -import org.scalajs.linker.interface.unstable.ModuleInitializerImpl +import org.scalajs.linker.interface.ModuleKind import org.scalajs.linker.standard.{CoreSpec, LinkedClass, LinkedTopLevelExport} import org.scalajs.linker.backend.emitter.{NameGen => JSNameGen} @@ -52,6 +51,8 @@ final class WasmContext( ) { import WasmContext._ + val hasJSInterop = coreSpec.moduleKind == ModuleKind.ESModule + private val functionTypes = LinkedHashMap.empty[watpe.FunctionType, wanme.TypeID] private val tableFunctionTypes = mutable.HashMap.empty[MethodName, wanme.TypeID] private val closureDataTypes = LinkedHashMap.empty[List[Type], wanme.TypeID] @@ -99,8 +100,8 @@ final class WasmContext( new mutable.LinkedHashSet() val stringPool: StringPool = - if (coreSpec.wasmFeatures.targetPureWasm) new DataStringPool - else new JSStringPool + if (hasJSInterop) new JSStringPool + else new DataStringPool val constantArrayPool: ConstantArrayPool = new ConstantArrayPool diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/LinkTimeProperties.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/LinkTimeProperties.scala index 04e30ecb0e..1510169c30 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/LinkTimeProperties.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/LinkTimeProperties.scala @@ -34,8 +34,7 @@ final class LinkTimeProperties private ( ModuleKind -> LinkTimeInt(moduleKindInt), IsWebAssembly -> LinkTimeBoolean(targetIsWebAssembly), ProductionMode -> LinkTimeBoolean(semantics.productionMode), - LinkerVersion -> LinkTimeString(ScalaJSVersions.current), - TargetPureWasm -> LinkTimeBoolean(wasmFeatures.targetPureWasm) + LinkerVersion -> LinkTimeString(ScalaJSVersions.current) ) def get(name: String): Option[LinkTimeValue] = @@ -58,9 +57,11 @@ object LinkTimeProperties { // These magic constants are mandated by the values in `scala.scalajs.LinkingInfo.ModuleKind`. import org.scalajs.linker.interface.ModuleKind._ val moduleKindInt = coreSpec.moduleKind match { - case NoModule => 1 - case ESModule => 2 - case CommonJSModule => 3 + case NoModule => 1 + case ESModule => 2 + case CommonJSModule => 3 + case MinimalWasmModule => 4 + case WasmComponent => 5 } new LinkTimeProperties(coreSpec.semantics, coreSpec.esFeatures, diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/optimizer/OptimizerCore.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/optimizer/OptimizerCore.scala index 02d56533b9..7e1bafb9dc 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/optimizer/OptimizerCore.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/optimizer/OptimizerCore.scala @@ -54,8 +54,6 @@ private[optimizer] abstract class OptimizerCore( private val isWasm: Boolean = config.coreSpec.targetIsWebAssembly - private val targetPureWasm: Boolean = config.coreSpec.wasmFeatures.targetPureWasm - // Uncomment and adapt to print debug messages only during one method // lazy val debugThisMethod: Boolean = // debugID == "java.lang.FloatingPointBits$.numberHashCode;D;I" @@ -146,7 +144,7 @@ private[optimizer] abstract class OptimizerCore( !config.coreSpec.esFeatures.allowBigIntsForLongs && !isWasm private val intrinsics = - Intrinsics.buildIntrinsics(config.coreSpec.esFeatures, isWasm, targetPureWasm) + Intrinsics.buildIntrinsics(config.coreSpec) private val integerDivisions = new IntegerDivisions(useRuntimeLong) @@ -7499,14 +7497,13 @@ private[optimizer] object OptimizerCore { ) // scalafmt: {} - def buildIntrinsics(esFeatures: ESFeatures, isWasm: Boolean, - targetPureWasm: Boolean): Intrinsics = { - val allIntrinsics = if (isWasm) { + def buildIntrinsics(coreSpec: CoreSpec): Intrinsics = { + val allIntrinsics = if (coreSpec.targetIsWebAssembly) { commonIntrinsics ::: wasmIntrinsics ::: - (if (targetPureWasm) Nil else wasmJSStringIntrinsics) + (if (coreSpec.moduleKind == ModuleKind.ESModule) wasmJSStringIntrinsics else Nil) } else { val baseIntrinsics = commonIntrinsics ::: baseJSIntrinsics - if (esFeatures.allowBigIntsForLongs) baseIntrinsics + if (coreSpec.esFeatures.allowBigIntsForLongs) baseIntrinsics else baseIntrinsics ++ runtimeLongIntrinsics } diff --git a/project/Build.scala b/project/Build.scala index a7751a3b16..2eb45b82fa 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -242,7 +242,7 @@ object MyScalaJSPlugin extends AutoPlugin { val baseConfig = NodeJSEnv.Config().withSourceMap(wantSourceMaps.value) val config = if (enableWasmEverywhere.value) { val linkerConfig = scalaJSLinkerConfig.value - val additionWasmArgs = if (!linkerConfig.wasmFeatures.targetPureWasm) { + val additionWasmArgs = if (linkerConfig.moduleKind == ModuleKind.ESModule) { List( "--experimental-wasm-exnref", "--experimental-wasm-imported-strings", // for JS string builtins @@ -275,6 +275,10 @@ object MyScalaJSPlugin extends AutoPlugin { case ModuleKind.NoModule => "commonjs" case ModuleKind.CommonJSModule => "commonjs" case ModuleKind.ESModule => "module" + + case ModuleKind.MinimalWasmModule | ModuleKind.WasmComponent => + // Nonsensical, but we need to emit something + "module" } val path = target.value / "package.json" @@ -2085,11 +2089,9 @@ object Build { scalaJSLinkerConfig.value .withPrettyPrint(true) .withExperimentalUseWebAssembly(true) - .withModuleKind(ModuleKind.ESModule) + .withModuleKind(ModuleKind.WasmComponent) .withWasmFeatures { prevFeatures => prevFeatures - .withTargetPureWasm(true) - .withComponentModel(true) .withWitDirectory(Some(witDir.getAbsolutePath)) .withWitWorld(witWorld) } @@ -2108,8 +2110,7 @@ object Build { scalaJSLinkerConfig ~= { _.withPrettyPrint(true) .withExperimentalUseWebAssembly(true) - .withModuleKind(ModuleKind.ESModule) - .withWasmFeatures(_.withTargetPureWasm(true)) + .withModuleKind(ModuleKind.MinimalWasmModule) }, jsEnv := { val config = NodeJSEnv.Config().withArgs(List( @@ -2138,11 +2139,9 @@ object Build { scalaJSLinkerConfig.value .withPrettyPrint(true) .withExperimentalUseWebAssembly(true) - .withModuleKind(ModuleKind.ESModule) + .withModuleKind(ModuleKind.WasmComponent) .withWasmFeatures { prevFeatures => prevFeatures - .withTargetPureWasm(true) - .withComponentModel(true) .withWitDirectory(Some(witDir.getAbsolutePath)) .withWitWorld(witWorld) } @@ -2166,11 +2165,9 @@ object Build { scalaJSLinkerConfig.value .withPrettyPrint(true) .withExperimentalUseWebAssembly(true) - .withModuleKind(ModuleKind.ESModule) + .withModuleKind(ModuleKind.WasmComponent) .withWasmFeatures { prevFeatures => prevFeatures - .withTargetPureWasm(true) - .withComponentModel(true) .withWitDirectory(Some(witDir.getAbsolutePath)) .withWitWorld(witWorld) } @@ -2193,12 +2190,10 @@ object Build { val witWorld = scalaJSWitWorld.value scalaJSLinkerConfig.value .withPrettyPrint(true) - .withModuleKind(ModuleKind.ESModule) + .withModuleKind(ModuleKind.WasmComponent) .withExperimentalUseWebAssembly(true) .withWasmFeatures { prevFeatures => prevFeatures - .withTargetPureWasm(true) - .withComponentModel(true) .withWitDirectory(Some(witDir.getAbsolutePath)) .withWitWorld(witWorld) } @@ -2309,7 +2304,11 @@ object Build { Test / unmanagedSourceDirectories ++= { val config = (Test / scalaJSLinkerConfig).value - val targetPureWasm = config.wasmFeatures.targetPureWasm + + val isWasmNoJS = config.moduleKind match { + case ModuleKind.MinimalWasmModule | ModuleKind.WasmComponent => true + case _ => false + } val testDir = (Test / sourceDirectory).value val sharedTestDir = @@ -2320,7 +2319,7 @@ object Build { val javaV = javaVersion.value val scalaV = scalaVersion.value - if (targetPureWasm) { + if (isWasmNoJS) { List( sharedTestDir / "scala", jsTestDir, // run only a few tests (filtered out in sources) @@ -2339,7 +2338,11 @@ object Build { Test / sources := { val config = (Test / scalaJSLinkerConfig).value - val targetPureWasm = config.wasmFeatures.targetPureWasm + + val isWasmNoJS = config.moduleKind match { + case ModuleKind.MinimalWasmModule | ModuleKind.WasmComponent => true + case _ => false + } def endsWith(f: File, suffix: String): Boolean = f.getPath().replace('\\', '/').endsWith(suffix) @@ -2348,8 +2351,9 @@ object Build { f.getPath().replace('\\', '/').contains(substr) val originalSources = (Test / sources).value - if (!targetPureWasm) originalSources - else { + if (!isWasmNoJS) { + originalSources + } else { originalSources .filter(f => contains(f, "/shared/src/test/scala-old-collections/") || @@ -2489,9 +2493,13 @@ object Build { val moduleKind = linkerConfig.moduleKind val hasModules = moduleKind != ModuleKind.NoModule val isWebAssembly = linkerConfig.experimentalUseWebAssembly - val targetPureWasm = linkerConfig.wasmFeatures.targetPureWasm - if (targetPureWasm) Nil + val isWasmNoJS = linkerConfig.moduleKind match { + case ModuleKind.MinimalWasmModule | ModuleKind.WasmComponent => true + case _ => false + } + + if (isWasmNoJS) Nil else { collectionsEraDependentDirectory(scalaV, testDir) :: includeIf(testDir / "require-new-target", @@ -2515,15 +2523,13 @@ object Build { Test / unmanagedResourceDirectories ++= { val testDir = (Test / sourceDirectory).value - val targetPureWasm = scalaJSLinkerConfig.value.wasmFeatures.targetPureWasm - if (targetPureWasm) Nil - else { - scalaJSLinkerConfig.value.moduleKind match { - case ModuleKind.NoModule => Nil - case ModuleKind.CommonJSModule => Seq(testDir / "resources-commonjs") - case ModuleKind.ESModule => Seq(testDir / "resources-esmodule") - } + scalaJSLinkerConfig.value.moduleKind match { + case ModuleKind.NoModule => Nil + case ModuleKind.CommonJSModule => Seq(testDir / "resources-commonjs") + case ModuleKind.ESModule => Seq(testDir / "resources-esmodule") + case ModuleKind.MinimalWasmModule => Nil + case ModuleKind.WasmComponent => Nil } }, @@ -2566,6 +2572,8 @@ object Build { "isNoModule" -> (moduleKind == ModuleKind.NoModule), "isESModule" -> (moduleKind == ModuleKind.ESModule), "isCommonJSModule" -> (moduleKind == ModuleKind.CommonJSModule), + "isMinimalWasmModule" -> (moduleKind == ModuleKind.MinimalWasmModule), + "isWasmComponent" -> (moduleKind == ModuleKind.WasmComponent), "usesClosureCompiler" -> linkerConfig.closureCompiler, "hasMinifiedNames" -> (linkerConfig.closureCompiler || linkerConfig.minify), "compliantAsInstanceOfs" -> (sems.asInstanceOfs == CheckedBehavior.Compliant), @@ -2579,7 +2587,6 @@ object Build { "esVersion" -> linkerConfig.esFeatures.esVersion.edition, "useECMAScript2015Semantics" -> linkerConfig.esFeatures.useECMAScript2015Semantics, "isWebAssembly" -> linkerConfig.experimentalUseWebAssembly, - "targetPureWasm" -> linkerConfig.wasmFeatures.targetPureWasm, ) }, diff --git a/project/JavalibIRCleaner.scala b/project/JavalibIRCleaner.scala index 131e161891..e24bd72b76 100644 --- a/project/JavalibIRCleaner.scala +++ b/project/JavalibIRCleaner.scala @@ -407,9 +407,6 @@ final class JavalibIRCleaner(baseDirectoryURI: URI) { case IntrinsicCall(LinkingInfoClass, `linkerVersionMethodName`, Nil) => LinkTimeProperty(LinkTimeProperty.LinkerVersion)(StringType) - case IntrinsicCall(LinkingInfoClass, `targetPureWasmMethodName`, Nil) => - LinkTimeProperty(LinkTimeProperty.TargetPureWasm)(BooleanType) - case _ => tree } @@ -723,7 +720,6 @@ object JavalibIRCleaner { private val esVersionMethodName = MethodName("esVersion", Nil, IntRef) private val isWebAssemblyMethodName = MethodName("isWebAssembly", Nil, BooleanRef) private val linkerVersionMethodName = MethodName("linkerVersion", Nil, ClassRef(BoxedStringClass)) - private val targetPureWasmMethodName = MethodName("targetPureWasm", Nil, BooleanRef) private val ClassNameSubstitutions: Map[ClassName, ClassName] = { val refBaseNames = diff --git a/sbt-plugin/src/main/scala/org/scalajs/sbtplugin/ScalaJSPluginInternal.scala b/sbt-plugin/src/main/scala/org/scalajs/sbtplugin/ScalaJSPluginInternal.scala index 8e51b8105b..cda08bcc9b 100644 --- a/sbt-plugin/src/main/scala/org/scalajs/sbtplugin/ScalaJSPluginInternal.scala +++ b/sbt-plugin/src/main/scala/org/scalajs/sbtplugin/ScalaJSPluginInternal.scala @@ -563,6 +563,10 @@ private[sbtplugin] object ScalaJSPluginInternal { case ModuleKind.NoModule => Input.Script(path) case ModuleKind.ESModule => Input.ESModule(path) case ModuleKind.CommonJSModule => Input.CommonJSModule(path) + + case ModuleKind.MinimalWasmModule | ModuleKind.WasmComponent => + // Pretend that we are an ES module for now + Input.ESModule(path) } }, @@ -686,7 +690,7 @@ private[sbtplugin] object ScalaJSPluginInternal { val log = s.log // Only run when component model is enabled - if (!linkerConfig.wasmFeatures.componentModel) { + if (linkerConfig.moduleKind != ModuleKind.WasmComponent) { Seq.empty[File] } else if (!witDir.exists()) { log.debug(s"WIT directory $witDir does not exist, skipping wit-bindgen") diff --git a/scalalib/overrides-2.12/scala/collection/mutable/Buffer.scala b/scalalib/overrides-2.12/scala/collection/mutable/Buffer.scala index 4c7e44a475..530a66b6ef 100644 --- a/scalalib/overrides-2.12/scala/collection/mutable/Buffer.scala +++ b/scalalib/overrides-2.12/scala/collection/mutable/Buffer.scala @@ -47,7 +47,7 @@ trait Buffer[A] extends Seq[A] object Buffer extends SeqFactory[Buffer] { implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, Buffer[A]] = ReusableCBF.asInstanceOf[GenericCanBuildFrom[A]] def newBuilder[A]: Builder[A, Buffer[A]] = - linkTimeIf[Builder[A, Buffer[A]]](LinkingInfo.targetPureWasm) { + linkTimeIf[Builder[A, Buffer[A]]](LinkingInfo.isWebAssembly) { ArrayBuffer.newBuilder[A] } { new js.WrappedArray[A] diff --git a/scalalib/overrides-2.13/scala/Symbol.scala b/scalalib/overrides-2.13/scala/Symbol.scala index 64f3371e34..bc70f427c8 100644 --- a/scalalib/overrides-2.13/scala/Symbol.scala +++ b/scalalib/overrides-2.13/scala/Symbol.scala @@ -13,7 +13,8 @@ package scala import scala.scalajs.js -import scala.scalajs.LinkingInfo.{linkTimeIf, targetPureWasm} +import scala.scalajs.LinkingInfo.{linkTimeIf, moduleKind} +import scala.scalajs.LinkingInfo.ModuleKind.{MinimalWasmModule, WasmComponent} /** This class provides a simple way to get unique objects for equal strings. * Since symbols are interned, they can be compared using reference equality. @@ -48,7 +49,7 @@ object Symbol extends JSUniquenessCache[Symbol] { private[scala] abstract class JSUniquenessCache[V] { private val cacheJS = { - linkTimeIf(!targetPureWasm) { + linkTimeIf(moduleKind != MinimalWasmModule && moduleKind != WasmComponent) { js.Dictionary.empty[V] } { null @@ -56,7 +57,7 @@ private[scala] abstract class JSUniquenessCache[V] } private val cacheWasm = { - linkTimeIf(targetPureWasm) { + linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { new java.util.HashMap[String, V]() } { null @@ -67,7 +68,7 @@ private[scala] abstract class JSUniquenessCache[V] protected def keyFromValue(v: V): Option[String] def apply(name: String): V = { - linkTimeIf(targetPureWasm) { + linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { cacheWasm.computeIfAbsent(name, _ => valueFromKey(name)) } { cacheJS.getOrElseUpdate(name, valueFromKey(name)) diff --git a/scalalib/overrides-2.13/scala/collection/mutable/Buffer.scala b/scalalib/overrides-2.13/scala/collection/mutable/Buffer.scala index 9d02bcde99..79b83dd776 100644 --- a/scalalib/overrides-2.13/scala/collection/mutable/Buffer.scala +++ b/scalalib/overrides-2.13/scala/collection/mutable/Buffer.scala @@ -226,7 +226,7 @@ trait IndexedBuffer[A] extends IndexedSeq[A] @SerialVersionUID(3L) object Buffer extends SeqFactory.Delegate[Buffer]( - linkTimeIf[SeqFactory[Buffer]](LinkingInfo.targetPureWasm) { + linkTimeIf[SeqFactory[Buffer]](LinkingInfo.isWebAssembly) { ArrayBuffer } { js.WrappedArray @@ -235,7 +235,7 @@ object Buffer extends SeqFactory.Delegate[Buffer]( @SerialVersionUID(3L) object IndexedBuffer extends SeqFactory.Delegate[IndexedBuffer]( - linkTimeIf[SeqFactory[IndexedBuffer]](LinkingInfo.targetPureWasm) { + linkTimeIf[SeqFactory[IndexedBuffer]](LinkingInfo.isWebAssembly) { ArrayBuffer } { js.WrappedArray diff --git a/scalalib/overrides/scala/Symbol.scala b/scalalib/overrides/scala/Symbol.scala index deab491add..55a0df31e7 100644 --- a/scalalib/overrides/scala/Symbol.scala +++ b/scalalib/overrides/scala/Symbol.scala @@ -13,7 +13,8 @@ package scala import scala.scalajs.js -import scala.scalajs.LinkingInfo.{linkTimeIf, targetPureWasm} +import scala.scalajs.LinkingInfo.{linkTimeIf, moduleKind} +import scala.scalajs.LinkingInfo.ModuleKind.{MinimalWasmModule, WasmComponent} /** This class provides a simple way to get unique objects for equal strings. * Since symbols are interned, they can be compared using reference equality. @@ -48,7 +49,7 @@ object Symbol extends JSUniquenessCache[Symbol] { private[scala] abstract class JSUniquenessCache[V] { private val cacheJS = { - linkTimeIf(!targetPureWasm) { + linkTimeIf(moduleKind != MinimalWasmModule && moduleKind != WasmComponent) { js.Dictionary.empty[V] } { null @@ -56,7 +57,7 @@ private[scala] abstract class JSUniquenessCache[V] } private val cacheWasm = { - linkTimeIf(targetPureWasm) { + linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { new java.util.HashMap[String, V]() } { null @@ -67,7 +68,7 @@ private[scala] abstract class JSUniquenessCache[V] protected def keyFromValue(v: V): Option[String] def apply(name: String): V = { - linkTimeIf(targetPureWasm) { + linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { cacheWasm.computeIfAbsent(name, _ => valueFromKey(name)) } { cacheJS.getOrElseUpdate(name, valueFromKey(name)) diff --git a/scalalib/overrides/scala/runtime/BoxesRunTime.scala b/scalalib/overrides/scala/runtime/BoxesRunTime.scala index 9e92186249..18fa33a89b 100644 --- a/scalalib/overrides/scala/runtime/BoxesRunTime.scala +++ b/scalalib/overrides/scala/runtime/BoxesRunTime.scala @@ -3,7 +3,8 @@ package scala.runtime import scala.math.ScalaNumber import scala.scalajs.LinkingInfo -import scala.scalajs.LinkingInfo.linkTimeIf +import scala.scalajs.LinkingInfo.{linkTimeIf, moduleKind} +import scala.scalajs.LinkingInfo.ModuleKind.{MinimalWasmModule, WasmComponent} /* The declaration of the class is only to make the JVM back-end happy when * compiling the scalalib. @@ -52,7 +53,7 @@ object BoxesRunTime { def unboxToDouble(d: Any): Double = d.asInstanceOf[Double] def equals(x: Object, y: Object): Boolean = - linkTimeIf(LinkingInfo.targetPureWasm) { + linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { if (x eq y) { x match { case x: java.lang.Double => x == x // rejects NaN diff --git a/test-bridge/src/main/scala/org/scalajs/testing/bridge/Bridge.scala b/test-bridge/src/main/scala/org/scalajs/testing/bridge/Bridge.scala index 84524ff7c1..4a30738129 100644 --- a/test-bridge/src/main/scala/org/scalajs/testing/bridge/Bridge.scala +++ b/test-bridge/src/main/scala/org/scalajs/testing/bridge/Bridge.scala @@ -14,25 +14,30 @@ package org.scalajs.testing.bridge import scala.scalajs.js import scala.scalajs.js.annotation.{JSGlobalScope, JSName} -import scala.scalajs.LinkingInfo.linkTimeIf -import scala.scalajs.LinkingInfo +import scala.scalajs.LinkingInfo.{linkTimeIf, moduleKind} +import scala.scalajs.LinkingInfo.ModuleKind.{MinimalWasmModule, WasmComponent} import org.scalajs.testing.common._ private[bridge] object Bridge { // Called via org.scalajs.testing.adapter.testAdapterInitializer def start(): Unit = mode match { - case TestBridgeMode.FullBridge => TestAdapterBridge.start() + case TestBridgeMode.FullBridge => + TestAdapterBridge.start() + case TestBridgeMode.HTMLRunner(tests) => - linkTimeIf(LinkingInfo.targetPureWasm) { // shouldn't reach here + linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { + // shouldn't reach here throw new AssertionError("The HTML runner is not supported in pure Wasm.") } { HTMLRunner.start(tests) } } - private def mode = { - linkTimeIf(!LinkingInfo.targetPureWasm) { + private def mode: TestBridgeMode = { + linkTimeIf[TestBridgeMode](moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { + TestBridgeMode.FullBridge + } { if (js.typeOf(js.Dynamic.global.__ScalaJSTestBridgeMode) == "undefined") { TestBridgeMode.FullBridge } else { @@ -40,8 +45,6 @@ private[bridge] object Bridge { js.Dynamic.global.__ScalaJSTestBridgeMode.asInstanceOf[String] Serializer.deserialize[TestBridgeMode](modeStr) } - } { - TestBridgeMode.FullBridge } } } diff --git a/test-bridge/src/main/scala/org/scalajs/testing/bridge/JSRPC.scala b/test-bridge/src/main/scala/org/scalajs/testing/bridge/JSRPC.scala index 58ce8e1c0b..fd641de077 100644 --- a/test-bridge/src/main/scala/org/scalajs/testing/bridge/JSRPC.scala +++ b/test-bridge/src/main/scala/org/scalajs/testing/bridge/JSRPC.scala @@ -14,8 +14,8 @@ package org.scalajs.testing.bridge import scala.scalajs.js import scala.scalajs.js.annotation._ -import scala.scalajs.LinkingInfo -import scala.scalajs.LinkingInfo.linkTimeIf +import scala.scalajs.LinkingInfo.{linkTimeIf, moduleKind} +import scala.scalajs.LinkingInfo.ModuleKind.{MinimalWasmModule, WasmComponent} /* Use the queue execution context (based on JS promises) explicitly: * We do not have anything better at our disposal and it is accceptable in @@ -31,7 +31,7 @@ import org.scalajs.testing.common.RPCCore /** JS RPC Core. Uses `scalajsCom`. */ private[bridge] final object JSRPC extends RPCCore { - linkTimeIf(LinkingInfo.targetPureWasm) { + linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { ScalajsCom.init() } { Com.init(handleMessage _) @@ -46,7 +46,7 @@ private[bridge] final object JSRPC extends RPCCore { handleMessage(msg) override protected def send(msg: String): Unit = { - linkTimeIf(LinkingInfo.targetPureWasm) { + linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { ScalajsCom.send(msg) } { Com.send(msg) diff --git a/test-suite/js/src/main/scala-ide-stubs/org/scalajs/testsuite/utils/BuildInfo.scala b/test-suite/js/src/main/scala-ide-stubs/org/scalajs/testsuite/utils/BuildInfo.scala index 8356a0da7c..7ce0e5e6be 100644 --- a/test-suite/js/src/main/scala-ide-stubs/org/scalajs/testsuite/utils/BuildInfo.scala +++ b/test-suite/js/src/main/scala-ide-stubs/org/scalajs/testsuite/utils/BuildInfo.scala @@ -22,6 +22,8 @@ private[utils] object BuildInfo { final val isNoModule = false final val isESModule = false final val isCommonJSModule = false + final val isMinimalWasmModule = false + final val isWasmComponent = false final val usesClosureCompiler = false final val hasMinifiedNames = false final val compliantAsInstanceOfs = false diff --git a/test-suite/js/src/main/scala/org/scalajs/testsuite/utils/Platform.scala b/test-suite/js/src/main/scala/org/scalajs/testsuite/utils/Platform.scala index efc8f45b73..e9361f3f06 100644 --- a/test-suite/js/src/main/scala/org/scalajs/testsuite/utils/Platform.scala +++ b/test-suite/js/src/main/scala/org/scalajs/testsuite/utils/Platform.scala @@ -13,7 +13,8 @@ package org.scalajs.testsuite.utils import scala.scalajs.js -import scala.scalajs.LinkingInfo.{ESVersion, targetPureWasm, linkTimeIf} +import scala.scalajs.LinkingInfo.{ESVersion, linkTimeIf, moduleKind} +import scala.scalajs.LinkingInfo.ModuleKind.{MinimalWasmModule, WasmComponent} import org.scalajs.testsuite.utils.{BuildInfo => ScalaJSBuildInfo} @@ -34,8 +35,6 @@ object Platform { def executingInWebAssembly: Boolean = BuildInfo.isWebAssembly - def executingInPureWebAssembly: Boolean = BuildInfo.targetPureWasm - def executingInNodeJS: Boolean = { js.typeOf(js.Dynamic.global.process) != "undefined" && !js.isUndefined(js.Dynamic.global.process.release) && @@ -93,7 +92,7 @@ object Platform { def hasCompliantModuleInit: Boolean = BuildInfo.compliantModuleInit def hasDirectBuffers: Boolean = - linkTimeIf(targetPureWasm)(false)(typedArrays) + linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent)(false)(typedArrays) def regexSupportsUnicodeCase: Boolean = assumedESVersion >= ESVersion.ES2015 @@ -107,6 +106,10 @@ object Platform { def isNoModule: Boolean = BuildInfo.isNoModule def isESModule: Boolean = BuildInfo.isESModule def isCommonJSModule: Boolean = BuildInfo.isCommonJSModule + def isMinimalWasmModule: Boolean = BuildInfo.isMinimalWasmModule + def isWasmComponent: Boolean = BuildInfo.isWasmComponent + + def executingInPureWebAssembly: Boolean = isMinimalWasmModule || isWasmComponent /** Runs the specified piece of code in the global context. * diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/library/LinkTimeIfTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/library/LinkTimeIfTest.scala index c82eb10cfc..55e1d48129 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/library/LinkTimeIfTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/library/LinkTimeIfTest.scala @@ -14,6 +14,7 @@ package org.scalajs.testsuite.library import scala.scalajs.js import scala.scalajs.LinkingInfo._ +import scala.scalajs.LinkingInfo.ModuleKind.{MinimalWasmModule, WasmComponent} import org.junit.Test import org.junit.Assert._ @@ -85,13 +86,17 @@ class LinkTimeIfTest { @Test def exponentOp(): Unit = { def pow(x: Double, y: Double): Double = { - linkTimeIf(esVersion >= ESVersion.ES2016 && !targetPureWasm) { + linkTimeIf( + esVersion >= ESVersion.ES2016 && + moduleKind != MinimalWasmModule && moduleKind != WasmComponent) { assertTrue("Took the wrong branch of linkTimeIf when linking for ES 2016+", - esVersion >= ESVersion.ES2016 && !targetPureWasm) + esVersion >= ESVersion.ES2016 && + moduleKind != MinimalWasmModule && moduleKind != WasmComponent) (x.asInstanceOf[js.Dynamic] ** y.asInstanceOf[js.Dynamic]).asInstanceOf[Double] } { assertFalse("Took the wrong branch of linkTimeIf when linking for ES 2015-", - esVersion >= ESVersion.ES2016 && !targetPureWasm) + esVersion >= ESVersion.ES2016 && + moduleKind != MinimalWasmModule && moduleKind != WasmComponent) Math.pow(x, y) } } diff --git a/test-suite/jvm/src/main/scala/scala/scalajs/LinkingInfo.scala b/test-suite/jvm/src/main/scala/scala/scalajs/LinkingInfo.scala index 5dda47fd00..418809c8b2 100644 --- a/test-suite/jvm/src/main/scala/scala/scalajs/LinkingInfo.scala +++ b/test-suite/jvm/src/main/scala/scala/scalajs/LinkingInfo.scala @@ -19,8 +19,13 @@ package scala.scalajs * Once all tests link to pure Wasm and linkTimeIf removed, this shim will no longer be needed. */ object LinkingInfo { - final val targetPureWasm = false + val moduleKind = -1 def linkTimeIf[T](cond: Boolean)(thenp: T)(elsep: T): T = if (cond) thenp else elsep + + object ModuleKind { + final val MinimalWasmModule = 4 + final val WasmComponent = 5 + } } diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/RegressionTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/RegressionTest.scala index 1a03e1df41..4171e26097 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/RegressionTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/RegressionTest.scala @@ -23,6 +23,8 @@ import org.scalajs.testsuite.utils.AssertThrows.{assertThrows, _} import org.scalajs.testsuite.utils.Platform._ import scala.scalajs.LinkingInfo +import scala.scalajs.LinkingInfo.moduleKind +import scala.scalajs.LinkingInfo.ModuleKind.{MinimalWasmModule, WasmComponent} class RegressionTest { import RegressionTest._ @@ -110,7 +112,7 @@ class RegressionTest { assumeFalse("TODO: mutable.Buffer doesn't link in pure Wasm, it uses typedarray", executingInPureWebAssembly) - LinkingInfo.linkTimeIf(!LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind != MinimalWasmModule && moduleKind != WasmComponent) { val a = scala.collection.mutable.Buffer.empty[Int] a.insert(0, 0) a.remove(0) diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/LongTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/LongTest.scala index 351a7af110..4f3f793f20 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/LongTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/LongTest.scala @@ -22,6 +22,8 @@ import org.scalajs.testsuite.utils.AssertThrows.assertThrows import org.scalajs.testsuite.utils.Platform.executingInPureWebAssembly import scala.scalajs.LinkingInfo +import scala.scalajs.LinkingInfo.moduleKind +import scala.scalajs.LinkingInfo.ModuleKind.{MinimalWasmModule, WasmComponent} /** Tests the implementation of the java standard library Long * requires jsinterop/LongTest to work to make sense @@ -192,7 +194,7 @@ class LongTest { @Test def parseStringBase2To36(): Unit = { assumeFalse("Doesn't link StringRadixInfos", executingInPureWebAssembly) - LinkingInfo.linkTimeIf(!LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind != MinimalWasmModule && moduleKind != WasmComponent) { def test(radix: Int, s: String, v: Long): Unit = { assertEquals(v, JLong.parseLong(s, radix)) assertEquals(v, JLong.valueOf(s, radix).longValue()) diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/MathTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/MathTest.scala index be4dd36654..d01bd641a3 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/MathTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/MathTest.scala @@ -27,6 +27,8 @@ import org.scalajs.testsuite.utils.AssertThrows.assertThrows import org.scalajs.testsuite.utils.Platform._ import scala.scalajs.LinkingInfo +import scala.scalajs.LinkingInfo.moduleKind +import scala.scalajs.LinkingInfo.ModuleKind.{MinimalWasmModule, WasmComponent} class MathTest { @@ -532,7 +534,7 @@ class MathTest { @Test def expm1(): Unit = { assumeFalse(executingInPureWebAssembly) - LinkingInfo.linkTimeIf(!LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind != MinimalWasmModule && moduleKind != WasmComponent) { assertTrue(1 / Math.expm1(-0.0) < 0) assertTrue(1 / Math.expm1(0.0) > 0) assertSameDouble(-0.0, Math.expm1(-0.0)) @@ -549,7 +551,7 @@ class MathTest { @Test def sinh(): Unit = { assumeFalse(executingInPureWebAssembly) - LinkingInfo.linkTimeIf(!LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind != MinimalWasmModule && moduleKind != WasmComponent) { assertEquals(Double.NegativeInfinity, Math.sinh(-1234.56), 0.0) assertEquals(Double.PositiveInfinity, Math.sinh(1234.56), 0.0) assertSameDouble(0.0, Math.sinh(0.0)) @@ -562,7 +564,7 @@ class MathTest { @Test def cosh(): Unit = { assumeFalse(executingInPureWebAssembly) - LinkingInfo.linkTimeIf(!LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind != MinimalWasmModule && moduleKind != WasmComponent) { assertEquals(Double.PositiveInfinity, Math.cosh(-1234.56), 0.0) assertEquals(Double.PositiveInfinity, Math.cosh(1234.56), 0.0) assertEquals(1.0, Math.cosh(-0.0), 0.01) @@ -575,7 +577,7 @@ class MathTest { @Test def tanh(): Unit = { assumeFalse(executingInPureWebAssembly) - LinkingInfo.linkTimeIf(!LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind != MinimalWasmModule && moduleKind != WasmComponent) { assertEquals(-1.0, Math.tanh(-1234.56), 0.01) assertEquals(-1.0, Math.tanh(-120.56), 0.01) assertEquals(1.0, Math.tanh(1234.56), 0.01) diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/ThreadTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/ThreadTest.scala index 211d29a702..eb83713ce5 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/ThreadTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/ThreadTest.scala @@ -19,6 +19,8 @@ import org.junit.Assume._ import org.scalajs.testsuite.utils.Platform.{executingInJVM, executingInPureWebAssembly} import scala.scalajs.LinkingInfo +import scala.scalajs.LinkingInfo.moduleKind +import scala.scalajs.LinkingInfo.ModuleKind.{MinimalWasmModule, WasmComponent} class ThreadTest { @@ -38,7 +40,7 @@ class ThreadTest { @Test def currentThreadGetStackTrace(): Unit = { assumeFalse("Doesn't link in pure Wasm", executingInPureWebAssembly) - LinkingInfo.linkTimeIf(!LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind != MinimalWasmModule && moduleKind != WasmComponent) { val _ = Thread.currentThread().getStackTrace() } {} } diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/BitSetTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/BitSetTest.scala index 6ad19b7a0f..4d51355d08 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/BitSetTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/BitSetTest.scala @@ -21,6 +21,8 @@ import org.scalajs.testsuite.utils.AssertThrows._ import org.scalajs.testsuite.utils.Platform._ import scala.scalajs.LinkingInfo +import scala.scalajs.LinkingInfo.moduleKind +import scala.scalajs.LinkingInfo.ModuleKind.{MinimalWasmModule, WasmComponent} class BitSetTest { @Test def test_Constructor_empty(): Unit = { @@ -1499,7 +1501,7 @@ class BitSetTest { @Test def valueOf_ByteBuffer_typedArrays(): Unit = { assumeFalse("requires support for direct Buffers, which isn't available in pure Wasm", executingInPureWebAssembly) - LinkingInfo.linkTimeIf(!LinkingInfo.targetPureWasm) { + LinkingInfo.linkTimeIf(moduleKind != MinimalWasmModule && moduleKind != WasmComponent) { assumeTrue("requires support for direct Buffers", hasDirectBuffers) val eightBS = makeEightBS() From 22d48c03c882566aa824b65bc9d1e9f43a2fe157 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Fri, 13 Mar 2026 10:29:29 +0100 Subject: [PATCH 12/21] Fix #5335: Throw a user-friendly exception on inconsistent config. --- .../linker/frontend/LinkerFrontendImpl.scala | 5 ++ .../linker/IncompatibleConfigTest.scala | 55 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 linker/shared/src/test/scala/org/scalajs/linker/IncompatibleConfigTest.scala diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/LinkerFrontendImpl.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/LinkerFrontendImpl.scala index 1dc406b45a..7f6adf9575 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/LinkerFrontendImpl.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/LinkerFrontendImpl.scala @@ -36,6 +36,11 @@ final class LinkerFrontendImpl private (config: LinkerFrontendImpl.Config) exten /** Core specification that this linker frontend implements. */ val coreSpec = config.commonConfig.coreSpec + require( + coreSpec.moduleKind != ModuleKind.NoModule || + config.moduleSplitStyle == ModuleSplitStyle.FewestModules, + s"NoModule requires ModuleSplitStyle.FewestModules; was ${config.moduleSplitStyle}.") + private[this] val linker: BaseLinker = new BaseLinker(config.commonConfig, config.checkIR) diff --git a/linker/shared/src/test/scala/org/scalajs/linker/IncompatibleConfigTest.scala b/linker/shared/src/test/scala/org/scalajs/linker/IncompatibleConfigTest.scala new file mode 100644 index 0000000000..9ac2786fb6 --- /dev/null +++ b/linker/shared/src/test/scala/org/scalajs/linker/IncompatibleConfigTest.scala @@ -0,0 +1,55 @@ +/* + * Scala.js (https://www.scala-js.org/) + * + * Copyright EPFL. + * + * Licensed under Apache License 2.0 + * (https://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package org.scalajs.linker + +import org.junit.Test +import org.junit.Assert._ + +import org.scalajs.linker.interface._ + +/** Tests for the error messages that we get when trying to use invalid configurations. */ +class IncompatibleConfigTest { + private val baseC = StandardConfig() + + private def test(expectedMessage: String, config: StandardConfig): Unit = { + val e = assertThrows(classOf[IllegalArgumentException], + () => StandardImpl.linker(config)) + assertEquals(expectedMessage, e.getMessage().stripPrefix("requirement failed: ")) + } + + @Test def inconsistentConfigTest(): Unit = { + // #5335 NoModule requires ModuleSplitStyle.FewestModules + test("NoModule requires ModuleSplitStyle.FewestModules; was SmallestModules.", + baseC.withModuleSplitStyle(ModuleSplitStyle.SmallestModules)) + test("NoModule requires ModuleSplitStyle.FewestModules; was SmallModulesFor(List(foo)).", + baseC.withModuleSplitStyle(ModuleSplitStyle.SmallModulesFor(List("foo")))) + } + + @deprecated("tests deprecated APIs", since = "forever") + @Test def configNotSupportedByWasmBackend(): Unit = { + val wasmC = baseC + .withExperimentalUseWebAssembly(true) + .withModuleKind(ModuleKind.ESModule) + + // Unsupported ModuleKind + val supportedModuleKinds = Set[ModuleKind](ModuleKind.ESModule) + for (moduleKind <- ModuleKind.All if !supportedModuleKinds.contains(moduleKind)) { + test(s"The WebAssembly backend only supports ES modules; was $moduleKind.", + wasmC.withModuleKind(moduleKind)) + } + + // ES 5.1 + test("The WebAssembly backend only supports the ECMAScript 2015 semantics.", + wasmC.withESFeatures(_.withESVersion(ESVersion.ES5_1))) + } +} From 20f9f18645e55f4585a2c03bc0b0ab810c1d1bdf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 13 Mar 2026 23:13:55 +0000 Subject: [PATCH 13/21] Bump undici from 7.22.0 to 7.24.1 Bumps [undici](https://github.com/nodejs/undici) from 7.22.0 to 7.24.1. - [Release notes](https://github.com/nodejs/undici/releases) - [Commits](https://github.com/nodejs/undici/compare/v7.22.0...v7.24.1) --- updated-dependencies: - dependency-name: undici dependency-version: 7.24.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index c1ab759c5f..1a93c3c75a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1469,9 +1469,9 @@ } }, "node_modules/undici": { - "version": "7.22.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.22.0.tgz", - "integrity": "sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.1.tgz", + "integrity": "sha512-5xoBibbmnjlcR3jdqtY2Lnx7WbrD/tHlT01TmvqZUFVc9Q1w4+j5hbnapTqbcXITMH1ovjq/W7BkqBilHiVAaA==", "dev": true, "engines": { "node": ">=20.18.1" @@ -2617,9 +2617,9 @@ } }, "undici": { - "version": "7.22.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.22.0.tgz", - "integrity": "sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.1.tgz", + "integrity": "sha512-5xoBibbmnjlcR3jdqtY2Lnx7WbrD/tHlT01TmvqZUFVc9Q1w4+j5hbnapTqbcXITMH1ovjq/W7BkqBilHiVAaA==", "dev": true }, "unpipe": { From 0cce77cce4e334837e68241af5d7f80ff99eb22d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Wed, 4 Mar 2026 15:38:49 +0100 Subject: [PATCH 14/21] Fix: Return 0 in CharArrayReader(_, _, 0) when past the end. Previously, we were returning -1. --- javalib/src/main/scala/java/io/CharArrayReader.scala | 4 +++- .../testsuite/javalib/io/CharArrayReaderTest.scala | 11 +++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/javalib/src/main/scala/java/io/CharArrayReader.scala b/javalib/src/main/scala/java/io/CharArrayReader.scala index 627f0613dd..3fe5723158 100644 --- a/javalib/src/main/scala/java/io/CharArrayReader.scala +++ b/javalib/src/main/scala/java/io/CharArrayReader.scala @@ -55,7 +55,9 @@ class CharArrayReader(protected var buf: Array[Char], offset: Int, length: Int) ensureOpen() - if (this.pos < this.count) { + if (len == 0) { + 0 + } else if (this.pos < this.count) { val bytesRead = Math.min(len, this.count - this.pos) System.arraycopy(this.buf, this.pos, buffer, offset, bytesRead) this.pos += bytesRead diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/io/CharArrayReaderTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/io/CharArrayReaderTest.scala index 871e425ffa..0a7c0ea775 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/io/CharArrayReaderTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/io/CharArrayReaderTest.scala @@ -87,8 +87,15 @@ class CharArrayReaderTest { @Test def readCharArray(): Unit = { withClose(new CharArrayReader(hw)) { cr => val c = new Array[Char](11) - cr.read(c, 1, 10) - assertArrayEquals(Array(0.toChar) ++ hw, c) + assertEquals(4, cr.read(c, 1, 4)) + assertEquals("\u0000Hell" + "\u0000" * 6, String.valueOf(c)) + assertEquals(0, cr.read(c, 3, 0)) + assertThrows(classOf[IndexOutOfBoundsException], cr.read(c, 3, 10)) + assertEquals("\u0000Hell" + "\u0000" * 6, String.valueOf(c)) + assertEquals(6, cr.read(c, 3, 8)) + assertEquals("\u0000HeoWorld" + "\u0000" * 2, String.valueOf(c)) + assertEquals(-1, cr.read(c, 3, 1)) + assertEquals(0, cr.read(c, 3, 0)) } } From dbf2ef584c8df5f07b97b4604b3d898c145f1e7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Wed, 4 Mar 2026 12:05:52 +0100 Subject: [PATCH 15/21] In StringBuilder, rely on the underlying string's StringIOOBEs. Strengthen the tests when in compliant mode. --- .../main/scala/java/lang/StringBuilder.scala | 90 ++++++++---- .../javalib/lang/StringBufferTest.scala | 131 ++++++----------- .../javalib/lang/StringBuilderTest.scala | 134 ++++++------------ .../testsuite/utils/AssertThrows.scala | 5 + 4 files changed, 157 insertions(+), 203 deletions(-) diff --git a/javalib/src/main/scala/java/lang/StringBuilder.scala b/javalib/src/main/scala/java/lang/StringBuilder.scala index a27e93c8fc..d3fb54138f 100644 --- a/javalib/src/main/scala/java/lang/StringBuilder.scala +++ b/javalib/src/main/scala/java/lang/StringBuilder.scala @@ -48,14 +48,23 @@ class StringBuilder extends AnyRef with CharSequence with Appendable with java.i def append(s: CharSequence): StringBuilder = append(s: AnyRef) - def append(s: CharSequence, start: Int, end: Int): StringBuilder = - append((if (s == null) "null" else s).subSequence(start, end)) + def append(s: CharSequence, start: Int, end: Int): StringBuilder = { + val s2 = if (s == null) "null" else s + checkStartEnd(start, end, s2.length()) + append(s2.subSequence(start, end).toString()) + } def append(str: Array[scala.Char]): StringBuilder = append(String.valueOf(str)) - def append(str: Array[scala.Char], offset: Int, len: Int): StringBuilder = + def append(str: Array[scala.Char], offset: Int, len: Int): StringBuilder = { + /* Since we check offset >= 0, if offset + len can only overflow from the + * positives into the negatives, in which case offset + len < offset, which + * is reported as well. + */ + checkStartEnd(offset, offset + len, str.length) append(String.valueOf(str, offset, len)) + } def append(b: scala.Boolean): StringBuilder = append(b.toString()) def append(c: scala.Char): StringBuilder = append(c.toString()) @@ -73,10 +82,10 @@ class StringBuilder extends AnyRef with CharSequence with Appendable with java.i def deleteCharAt(index: Int): StringBuilder = { /* This is not equivalent to `delete(index, index + 1)` when * `index == length`. + * + * The two calls to substring imply the required bounds checks. */ val oldContent = content - if (index < 0 || index >= oldContent.length) - throw new StringIndexOutOfBoundsException(index) content = oldContent.substring(0, index) + oldContent.substring(index + 1) this } @@ -84,9 +93,12 @@ class StringBuilder extends AnyRef with CharSequence with Appendable with java.i def replace(start: Int, end: Int, str: String): StringBuilder = { val oldContent = content val length = oldContent.length - if (start < 0 || start > length || start > end) - throw new StringIndexOutOfBoundsException(start) + + // The call to substring implies the bounds checks for 0 <= start <= length val firstPart = oldContent.substring(0, start) + str + if (end < start) + oldContent.charAt(-1) + content = if (end >= length) firstPart else firstPart + oldContent.substring(end) @@ -95,6 +107,7 @@ class StringBuilder extends AnyRef with CharSequence with Appendable with java.i def insert(index: Int, str: Array[scala.Char], offset: Int, len: Int): StringBuilder = { + // Surprisingly, this overload specifies a StringIOOBE. insert(index, String.valueOf(str, offset, len)) } @@ -102,9 +115,8 @@ class StringBuilder extends AnyRef with CharSequence with Appendable with java.i insert(offset, String.valueOf(obj)) def insert(offset: Int, str: String): StringBuilder = { + // The first call to substring implies the required bounds checks val oldContent = content - if (offset < 0 || offset > oldContent.length) - throw new StringIndexOutOfBoundsException(offset) content = oldContent.substring(0, offset) + str + oldContent.substring(offset) this @@ -113,19 +125,26 @@ class StringBuilder extends AnyRef with CharSequence with Appendable with java.i def insert(offset: Int, str: Array[scala.Char]): StringBuilder = insert(offset, String.valueOf(str)) - def insert(dstOffset: Int, s: CharSequence): StringBuilder = - insert(dstOffset, s: AnyRef) + def insert(dstOffset: Int, s: CharSequence): StringBuilder = { + checkInsertOffset(dstOffset) + insert(dstOffset, String.valueOf(s)) + } def insert(dstOffset: Int, s: CharSequence, start: Int, end: Int): StringBuilder = { - insert(dstOffset, (if (s == null) "null" else s).subSequence(start, end)) + checkInsertOffset(dstOffset) + val s2 = if (s == null) "null" else s + checkStartEnd(start, end, s2.length()) + insert(dstOffset, s2.subSequence(start, end).toString()) } def insert(offset: Int, b: scala.Boolean): StringBuilder = insert(offset, b.toString) - def insert(offset: Int, c: scala.Char): StringBuilder = + def insert(offset: Int, c: scala.Char): StringBuilder = { + checkInsertOffset(offset) insert(offset, c.toString) + } def insert(offset: Int, i: scala.Int): StringBuilder = insert(offset, i.toString) @@ -139,6 +158,28 @@ class StringBuilder extends AnyRef with CharSequence with Appendable with java.i def insert(offset: Int, d: scala.Double): StringBuilder = insert(offset, d.toString) + /** Explicitly checks an insertion offset. + * + * Used by the overloads of insert() that specify IOOBE, instead of UB + * StringIOOBE. + */ + @inline + private def checkInsertOffset(offset: Int): Unit = { + if (offset < 0 || offset > content.length) + throw new IndexOutOfBoundsException(offset) + } + + /** Explicitly checks start and end indices. + * + * Used by the overloads of append() and insert() that specify IOOBE, + * instead of UB StringIOOBE. + */ + @inline + private def checkStartEnd(start: Int, end: Int, length: Int): Unit = { + if (start < 0 || start > end || end > length) + throw new IndexOutOfBoundsException() + } + def indexOf(str: String): Int = content.indexOf(str) def indexOf(str: String, fromIndex: Int): Int = @@ -186,20 +227,20 @@ class StringBuilder extends AnyRef with CharSequence with Appendable with java.i def trimToSize(): Unit = () def setLength(newLength: Int): Unit = { - if (newLength < 0) - throw new StringIndexOutOfBoundsException(newLength) var newContent = content - val additional = newLength - newContent.length // cannot overflow - if (additional < 0) { - newContent = newContent.substring(0, newLength) - } else { - var i = 0 - while (i != additional) { + var currentLength = newContent.length() + + if (newLength >= currentLength) { + // Implies newLength >= 0, so bounds are OK + while (currentLength != newLength) { newContent += "\u0000" - i += 1 + currentLength += 1 } + content = newContent + } else { + // The call to substring implies the required bounds check + content = newContent.substring(0, newLength) } - content = newContent } def charAt(index: Int): Char = content.charAt(index) @@ -220,9 +261,8 @@ class StringBuilder extends AnyRef with CharSequence with Appendable with java.i } def setCharAt(index: Int, ch: scala.Char): Unit = { + // The two calls to substring imply the required bounds checks. val oldContent = content - if (index < 0 || index >= oldContent.length) - throw new StringIndexOutOfBoundsException(index) content = oldContent.substring(0, index) + ch + oldContent.substring(index + 1) } diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/StringBufferTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/StringBufferTest.scala index dee8b07824..bb4f1146e1 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/StringBufferTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/StringBufferTest.scala @@ -15,7 +15,7 @@ package org.scalajs.testsuite.javalib.lang import org.junit.Test import org.junit.Assert._ -import org.scalajs.testsuite.utils.AssertThrows.assertThrows +import org.scalajs.testsuite.utils.AssertThrows.{assertThrows, _} import org.scalajs.testsuite.utils.Platform.executingInJVM import WrappedStringCharSequence.charSequence @@ -167,10 +167,8 @@ class StringBufferTest { assertEquals("hello", resultFor("hello", 5, 5)) assertEquals("hel", resultFor("hello", 3, 8)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("hello", -1, 2)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("hello", 3, 2)) + assertThrowsStringIIOBEIfCompliant(resultFor("hello", -1, 2)) + assertThrowsStringIIOBEIfCompliant(resultFor("hello", 3, 2)) } @Test def deleteCharAt(): Unit = { @@ -181,10 +179,8 @@ class StringBufferTest { assertEquals("123", resultFor("0123", 0)) assertEquals("012", resultFor("0123", 3)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("0123", -1)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("0123", 4)) + assertThrowsStringIIOBEIfCompliant(resultFor("0123", -1)) + assertThrowsStringIIOBEIfCompliant(resultFor("0123", 4)) } @Test def replace(): Unit = { @@ -199,12 +195,9 @@ class StringBufferTest { assertEquals("0xxxx123", resultFor("0123", 1, 1, "xxxx")) assertEquals("0123x", resultFor("0123", 4, 5, "x")) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("0123", -1, 3, "x")) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("0123", 4, 3, "x")) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("0123", 5, 8, "x")) + assertThrowsStringIIOBEIfCompliant(resultFor("0123", -1, 3, "x")) + assertThrowsStringIIOBEIfCompliant(resultFor("0123", 4, 3, "x")) + assertThrowsStringIIOBEIfCompliant(resultFor("0123", 5, 8, "x")) if (executingInJVM) assertThrows(classOf[NullPointerException], resultFor("0123", 1, 3, null)) @@ -221,16 +214,12 @@ class StringBufferTest { assertEquals("0bc12", resultFor("012", 1, arr, 1, 2)) assertEquals("abcdef", resultFor("abef", 2, arr, 2, 2)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("1234", -1, arr, 1, 2)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("1234", 6, arr, 1, 2)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("1234", 1, arr, -1, 2)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("1234", 1, arr, 1, -2)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("1234", 1, arr, 4, 3)) + // Surprisingly, this overload specifies a StringIOOBE. + assertThrowsStringIIOBEIfCompliant(resultFor("1234", -1, arr, 1, 2)) + assertThrowsStringIIOBEIfCompliant(resultFor("1234", 6, arr, 1, 2)) + assertThrowsStringIIOBEIfCompliant(resultFor("1234", 1, arr, -1, 2)) + assertThrowsStringIIOBEIfCompliant(resultFor("1234", 1, arr, 1, -2)) + assertThrowsStringIIOBEIfCompliant(resultFor("1234", 1, arr, 4, 3)) if (executingInJVM) { assertThrows(classOf[NullPointerException], @@ -247,10 +236,8 @@ class StringBufferTest { assertEquals("01hello234", resultFor("01234", 2, "hello")) assertEquals("01foobar234", resultFor("01234", 2, charSequence("foobar"))) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("1234", -1, "foo")) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("1234", 6, "foo")) + assertThrowsStringIIOBEIfCompliant(resultFor("1234", -1, "foo")) + assertThrowsStringIIOBEIfCompliant(resultFor("1234", 6, "foo")) } @Test def insertString(): Unit = { @@ -260,10 +247,8 @@ class StringBufferTest { assertEquals("01null234", resultFor("01234", 2, null)) assertEquals("01hello234", resultFor("01234", 2, "hello")) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("1234", -1, "foo")) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("1234", 6, "foo")) + assertThrowsStringIIOBEIfCompliant(resultFor("1234", -1, "foo")) + assertThrowsStringIIOBEIfCompliant(resultFor("1234", 6, "foo")) } @Test def insertCharArray(): Unit = { @@ -275,10 +260,8 @@ class StringBufferTest { assertEquals("0abcde12", resultFor("012", 1, arr)) assertEquals("ababcdeef", resultFor("abef", 2, arr)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("1234", -1, arr)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("1234", 6, arr)) + assertThrowsStringIIOBEIfCompliant(resultFor("1234", -1, arr)) + assertThrowsStringIIOBEIfCompliant(resultFor("1234", 6, arr)) if (executingInJVM) assertThrows(classOf[NullPointerException], resultFor("1234", 1, null)) @@ -337,10 +320,8 @@ class StringBufferTest { assertEquals("a4bcd", initBuffer("abcd").insert(1, 4.toByte).toString) assertEquals("a304bcd", initBuffer("abcd").insert(1, 304.toShort).toString) - assertThrows(classOf[StringIndexOutOfBoundsException], - initBuffer("abcd").insert(5, 56)) - assertThrows(classOf[StringIndexOutOfBoundsException], - initBuffer("abcd").insert(-1, 56)) + assertThrowsStringIIOBEIfCompliant(initBuffer("abcd").insert(5, 56)) + assertThrowsStringIIOBEIfCompliant(initBuffer("abcd").insert(-1, 56)) } @Test def indexOfString(): Unit = { @@ -419,7 +400,7 @@ class StringBufferTest { @Test def setLength(): Unit = { val b = initBuffer("foobar") - assertThrows(classOf[StringIndexOutOfBoundsException], b.setLength(-3)) + assertThrowsStringIIOBEIfCompliant(b.setLength(-3)) b.setLength(3) assertEquals("foo", b.toString) @@ -435,11 +416,9 @@ class StringBufferTest { assertEquals('\ud801', resultFor("ab\ud801\udc02cd", 2)) assertEquals('\udc02', resultFor("ab\ud801\udc02cd", 3)) - if (executingInJVM) { - assertThrows(classOf[IndexOutOfBoundsException], resultFor("hello", -1)) - assertThrows(classOf[IndexOutOfBoundsException], resultFor("hello", 5)) - assertThrows(classOf[IndexOutOfBoundsException], resultFor("hello", 6)) - } + assertThrowsStringIIOBEIfCompliant(resultFor("hello", -1)) + assertThrowsStringIIOBEIfCompliant(resultFor("hello", 5)) + assertThrowsStringIIOBEIfCompliant(resultFor("hello", 6)) } @Test def codePointAt(): Unit = { @@ -456,12 +435,8 @@ class StringBufferTest { assertEquals(0xdf06, resultFor("\udf06abc", 0)) assertEquals(0xd834, resultFor("abc\ud834", 3)) - if (executingInJVM) { - assertThrows(classOf[IndexOutOfBoundsException], - resultFor("abc\ud834\udf06def", -1)) - assertThrows(classOf[IndexOutOfBoundsException], - resultFor("abc\ud834\udf06def", 15)) - } + assertThrowsStringIIOBEIfCompliant(resultFor("abc\ud834\udf06def", -1)) + assertThrowsStringIIOBEIfCompliant(resultFor("abc\ud834\udf06def", 15)) } @Test def codePointBefore(): Unit = { @@ -477,12 +452,8 @@ class StringBufferTest { assertEquals(0xd834, resultFor("\ud834abc", 1)) assertEquals(0xdf06, resultFor("\udf06abc", 1)) - if (executingInJVM) { - assertThrows(classOf[IndexOutOfBoundsException], - resultFor("abc\ud834\udf06def", 0)) - assertThrows(classOf[IndexOutOfBoundsException], - resultFor("abc\ud834\udf06def", 15)) - } + assertThrowsStringIIOBEIfCompliant(resultFor("abc\ud834\udf06def", 0)) + assertThrowsStringIIOBEIfCompliant(resultFor("abc\ud834\udf06def", 15)) } @Test def codePointCount(): Unit = { @@ -568,10 +539,8 @@ class StringBufferTest { assertEquals("foxbar", resultFor("foobar", 2, 'x')) assertEquals("foobah", resultFor("foobar", 5, 'h')) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("foobar", -1, 'h')) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("foobar", 6, 'h')) + assertThrowsStringIIOBEIfCompliant(resultFor("foobar", -1, 'h')) + assertThrowsStringIIOBEIfCompliant(resultFor("foobar", 6, 'h')) } @Test def substringStart(): Unit = { @@ -581,12 +550,8 @@ class StringBufferTest { assertEquals("llo", resultFor("hello", 2)) assertEquals("", resultFor("hello", 5)) - if (executingInJVM) { - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("hello", -1)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("hello", 8)) - } + assertThrowsStringIIOBEIfCompliant(resultFor("hello", -1)) + assertThrowsStringIIOBEIfCompliant(resultFor("hello", 8)) } @Test def subSequence(): Unit = { @@ -601,16 +566,10 @@ class StringBufferTest { assertEquals("", resultFor("hello", 5, 5)) assertEquals("hel", resultFor("hello", 0, 3)) - if (executingInJVM) { - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("hello", -1, 3)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("hello", 8, 8)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("hello", 3, 2)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("hello", 3, 8)) - } + assertThrowsStringIIOBEIfCompliant(resultFor("hello", -1, 3)) + assertThrowsStringIIOBEIfCompliant(resultFor("hello", 8, 8)) + assertThrowsStringIIOBEIfCompliant(resultFor("hello", 3, 2)) + assertThrowsStringIIOBEIfCompliant(resultFor("hello", 3, 8)) } @Test def substringStartEnd(): Unit = { @@ -621,15 +580,9 @@ class StringBufferTest { assertEquals("", resultFor("hello", 5, 5)) assertEquals("hel", resultFor("hello", 0, 3)) - if (executingInJVM) { - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("hello", -1, 3)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("hello", 8, 8)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("hello", 3, 2)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("hello", 3, 8)) - } + assertThrowsStringIIOBEIfCompliant(resultFor("hello", -1, 3)) + assertThrowsStringIIOBEIfCompliant(resultFor("hello", 8, 8)) + assertThrowsStringIIOBEIfCompliant(resultFor("hello", 3, 2)) + assertThrowsStringIIOBEIfCompliant(resultFor("hello", 3, 8)) } } diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/StringBuilderTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/StringBuilderTest.scala index 0a703cd535..dccff37c9f 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/StringBuilderTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/StringBuilderTest.scala @@ -17,7 +17,7 @@ import java.lang.StringBuilder import org.junit.Test import org.junit.Assert._ -import org.scalajs.testsuite.utils.AssertThrows.assertThrows +import org.scalajs.testsuite.utils.AssertThrows.{assertThrows, _} import org.scalajs.testsuite.utils.Platform.executingInJVM import WrappedStringCharSequence.charSequence @@ -169,10 +169,8 @@ class StringBuilderTest { assertEquals("hello", resultFor("hello", 5, 5)) assertEquals("hel", resultFor("hello", 3, 8)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("hello", -1, 2)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("hello", 3, 2)) + assertThrowsStringIIOBEIfCompliant(resultFor("hello", -1, 2)) + assertThrowsStringIIOBEIfCompliant(resultFor("hello", 3, 2)) } @Test def deleteCharAt(): Unit = { @@ -183,10 +181,8 @@ class StringBuilderTest { assertEquals("123", resultFor("0123", 0)) assertEquals("012", resultFor("0123", 3)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("0123", -1)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("0123", 4)) + assertThrowsStringIIOBEIfCompliant(resultFor("0123", -1)) + assertThrowsStringIIOBEIfCompliant(resultFor("0123", 4)) } @Test def replace(): Unit = { @@ -201,12 +197,9 @@ class StringBuilderTest { assertEquals("0xxxx123", resultFor("0123", 1, 1, "xxxx")) assertEquals("0123x", resultFor("0123", 4, 5, "x")) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("0123", -1, 3, "x")) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("0123", 4, 3, "x")) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("0123", 5, 8, "x")) + assertThrowsStringIIOBEIfCompliant(resultFor("0123", -1, 3, "x")) + assertThrowsStringIIOBEIfCompliant(resultFor("0123", 4, 3, "x")) + assertThrowsStringIIOBEIfCompliant(resultFor("0123", 5, 8, "x")) if (executingInJVM) assertThrows(classOf[NullPointerException], resultFor("0123", 1, 3, null)) @@ -223,16 +216,12 @@ class StringBuilderTest { assertEquals("0bc12", resultFor("012", 1, arr, 1, 2)) assertEquals("abcdef", resultFor("abef", 2, arr, 2, 2)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("1234", -1, arr, 1, 2)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("1234", 6, arr, 1, 2)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("1234", 1, arr, -1, 2)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("1234", 1, arr, 1, -2)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("1234", 1, arr, 4, 3)) + // Surprisingly, this overload specifies a StringIOOBE. + assertThrowsStringIIOBEIfCompliant(resultFor("1234", -1, arr, 1, 2)) + assertThrowsStringIIOBEIfCompliant(resultFor("1234", 6, arr, 1, 2)) + assertThrowsStringIIOBEIfCompliant(resultFor("1234", 1, arr, -1, 2)) + assertThrowsStringIIOBEIfCompliant(resultFor("1234", 1, arr, 1, -2)) + assertThrowsStringIIOBEIfCompliant(resultFor("1234", 1, arr, 4, 3)) if (executingInJVM) { assertThrows(classOf[NullPointerException], @@ -249,10 +238,8 @@ class StringBuilderTest { assertEquals("01hello234", resultFor("01234", 2, "hello")) assertEquals("01foobar234", resultFor("01234", 2, charSequence("foobar"))) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("1234", -1, "foo")) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("1234", 6, "foo")) + assertThrowsStringIIOBEIfCompliant(resultFor("1234", -1, "foo")) + assertThrowsStringIIOBEIfCompliant(resultFor("1234", 6, "foo")) } @Test def insertString(): Unit = { @@ -262,10 +249,8 @@ class StringBuilderTest { assertEquals("01null234", resultFor("01234", 2, null)) assertEquals("01hello234", resultFor("01234", 2, "hello")) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("1234", -1, "foo")) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("1234", 6, "foo")) + assertThrowsStringIIOBEIfCompliant(resultFor("1234", -1, "foo")) + assertThrowsStringIIOBEIfCompliant(resultFor("1234", 6, "foo")) } @Test def insertCharArray(): Unit = { @@ -277,10 +262,8 @@ class StringBuilderTest { assertEquals("0abcde12", resultFor("012", 1, arr)) assertEquals("ababcdeef", resultFor("abef", 2, arr)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("1234", -1, arr)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("1234", 6, arr)) + assertThrowsStringIIOBEIfCompliant(resultFor("1234", -1, arr)) + assertThrowsStringIIOBEIfCompliant(resultFor("1234", 6, arr)) if (executingInJVM) assertThrows(classOf[NullPointerException], resultFor("1234", 1, null)) @@ -339,10 +322,8 @@ class StringBuilderTest { assertEquals("a4bcd", initBuilder("abcd").insert(1, 4.toByte).toString) assertEquals("a304bcd", initBuilder("abcd").insert(1, 304.toShort).toString) - assertThrows(classOf[StringIndexOutOfBoundsException], - initBuilder("abcd").insert(5, 56)) - assertThrows(classOf[StringIndexOutOfBoundsException], - initBuilder("abcd").insert(-1, 56)) + assertThrowsStringIIOBEIfCompliant(initBuilder("abcd").insert(5, 56)) + assertThrowsStringIIOBEIfCompliant(initBuilder("abcd").insert(-1, 56)) } @Test def indexOfString(): Unit = { @@ -421,7 +402,7 @@ class StringBuilderTest { @Test def setLength(): Unit = { val b = initBuilder("foobar") - assertThrows(classOf[StringIndexOutOfBoundsException], b.setLength(-3)) + assertThrowsStringIIOBEIfCompliant(b.setLength(-3)) b.setLength(3) assertEquals("foo", b.toString) @@ -437,11 +418,9 @@ class StringBuilderTest { assertEquals('\ud801', resultFor("ab\ud801\udc02cd", 2)) assertEquals('\udc02', resultFor("ab\ud801\udc02cd", 3)) - if (executingInJVM) { - assertThrows(classOf[IndexOutOfBoundsException], resultFor("hello", -1)) - assertThrows(classOf[IndexOutOfBoundsException], resultFor("hello", 5)) - assertThrows(classOf[IndexOutOfBoundsException], resultFor("hello", 6)) - } + assertThrowsStringIIOBEIfCompliant(resultFor("hello", -1)) + assertThrowsStringIIOBEIfCompliant(resultFor("hello", 5)) + assertThrowsStringIIOBEIfCompliant(resultFor("hello", 6)) } @Test def codePointAt(): Unit = { @@ -458,12 +437,8 @@ class StringBuilderTest { assertEquals(0xdf06, resultFor("\udf06abc", 0)) assertEquals(0xd834, resultFor("abc\ud834", 3)) - if (executingInJVM) { - assertThrows(classOf[IndexOutOfBoundsException], - resultFor("abc\ud834\udf06def", -1)) - assertThrows(classOf[IndexOutOfBoundsException], - resultFor("abc\ud834\udf06def", 15)) - } + assertThrowsStringIIOBEIfCompliant(resultFor("abc\ud834\udf06def", -1)) + assertThrowsStringIIOBEIfCompliant(resultFor("abc\ud834\udf06def", 15)) } @Test def codePointBefore(): Unit = { @@ -479,12 +454,8 @@ class StringBuilderTest { assertEquals(0xd834, resultFor("\ud834abc", 1)) assertEquals(0xdf06, resultFor("\udf06abc", 1)) - if (executingInJVM) { - assertThrows(classOf[IndexOutOfBoundsException], - resultFor("abc\ud834\udf06def", 0)) - assertThrows(classOf[IndexOutOfBoundsException], - resultFor("abc\ud834\udf06def", 15)) - } + assertThrowsStringIIOBEIfCompliant(resultFor("abc\ud834\udf06def", 0)) + assertThrowsStringIIOBEIfCompliant(resultFor("abc\ud834\udf06def", 15)) } @Test def codePointCount(): Unit = { @@ -505,6 +476,7 @@ class StringBuilderTest { assertEquals(0, sb.codePointCount(sb.length - 1, sb.length - 1)) assertEquals(0, sb.codePointCount(sb.length, sb.length)) + // Actually mandated IOOBE, not StringIIOBE assertThrows(classOf[IndexOutOfBoundsException], sb.codePointCount(-3, 4)) assertThrows(classOf[IndexOutOfBoundsException], sb.codePointCount(6, 2)) assertThrows(classOf[IndexOutOfBoundsException], sb.codePointCount(10, 30)) @@ -527,6 +499,7 @@ class StringBuilderTest { assertEquals(sb.length - 1, sb.offsetByCodePoints(sb.length - 1, 0)) assertEquals(sb.length, sb.offsetByCodePoints(sb.length, 0)) + // Actually mandated IOOBE, not StringIIOBE assertThrows(classOf[IndexOutOfBoundsException], sb.offsetByCodePoints(-3, 4)) assertThrows(classOf[IndexOutOfBoundsException], sb.offsetByCodePoints(6, 18)) assertThrows(classOf[IndexOutOfBoundsException], sb.offsetByCodePoints(30, 2)) @@ -549,6 +522,7 @@ class StringBuilderTest { assertEquals(sb.length - 1, sb.offsetByCodePoints(sb.length - 1, -0)) assertEquals(sb.length, sb.offsetByCodePoints(sb.length, -0)) + // Actually mandated IOOBE, not StringIIOBE assertThrows(classOf[IndexOutOfBoundsException], sb.offsetByCodePoints(-3, 4)) assertThrows(classOf[IndexOutOfBoundsException], sb.offsetByCodePoints(6, 18)) assertThrows(classOf[IndexOutOfBoundsException], sb.offsetByCodePoints(30, 2)) @@ -570,10 +544,8 @@ class StringBuilderTest { assertEquals("foxbar", resultFor("foobar", 2, 'x')) assertEquals("foobah", resultFor("foobar", 5, 'h')) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("foobar", -1, 'h')) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("foobar", 6, 'h')) + assertThrowsStringIIOBEIfCompliant(resultFor("foobar", -1, 'h')) + assertThrowsStringIIOBEIfCompliant(resultFor("foobar", 6, 'h')) } @Test def substringStart(): Unit = { @@ -583,12 +555,8 @@ class StringBuilderTest { assertEquals("llo", resultFor("hello", 2)) assertEquals("", resultFor("hello", 5)) - if (executingInJVM) { - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("hello", -1)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("hello", 8)) - } + assertThrowsStringIIOBEIfCompliant(resultFor("hello", -1)) + assertThrowsStringIIOBEIfCompliant(resultFor("hello", 8)) } @Test def subSequence(): Unit = { @@ -603,16 +571,10 @@ class StringBuilderTest { assertEquals("", resultFor("hello", 5, 5)) assertEquals("hel", resultFor("hello", 0, 3)) - if (executingInJVM) { - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("hello", -1, 3)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("hello", 8, 8)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("hello", 3, 2)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("hello", 3, 8)) - } + assertThrowsStringIIOBEIfCompliant(resultFor("hello", -1, 3)) + assertThrowsStringIIOBEIfCompliant(resultFor("hello", 8, 8)) + assertThrowsStringIIOBEIfCompliant(resultFor("hello", 3, 2)) + assertThrowsStringIIOBEIfCompliant(resultFor("hello", 3, 8)) } @Test def substringStartEnd(): Unit = { @@ -623,16 +585,10 @@ class StringBuilderTest { assertEquals("", resultFor("hello", 5, 5)) assertEquals("hel", resultFor("hello", 0, 3)) - if (executingInJVM) { - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("hello", -1, 3)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("hello", 8, 8)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("hello", 3, 2)) - assertThrows(classOf[StringIndexOutOfBoundsException], - resultFor("hello", 3, 8)) - } + assertThrowsStringIIOBEIfCompliant(resultFor("hello", -1, 3)) + assertThrowsStringIIOBEIfCompliant(resultFor("hello", 8, 8)) + assertThrowsStringIIOBEIfCompliant(resultFor("hello", 3, 2)) + assertThrowsStringIIOBEIfCompliant(resultFor("hello", 3, 8)) } @Test def stringInterpolationToSurviveNullAndUndefined(): Unit = { diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/utils/AssertThrows.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/utils/AssertThrows.scala index df343e91d8..87fefaf548 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/utils/AssertThrows.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/utils/AssertThrows.scala @@ -26,4 +26,9 @@ object AssertThrows { if (Platform.hasCompliantNullPointers) assertThrows(classOf[NullPointerException], code) } + + def assertThrowsStringIIOBEIfCompliant(code: => Unit): Unit = { + if (Platform.hasCompliantStringIndexOutOfBounds) + assertThrows(classOf[StringIndexOutOfBoundsException], code) + } } From bfed364036fe4a4662f1691fb39698e34d848350 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Fri, 6 Mar 2026 11:31:46 +0100 Subject: [PATCH 16/21] Throw a specified IndexOutOfBoundsException in CharArrayWriter. Instead of a StringIOOBE, which is subject to UB. --- javalib/src/main/scala/java/io/CharArrayWriter.scala | 2 +- .../scalajs/testsuite/javalib/io/CharArrayWriterTest.scala | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/javalib/src/main/scala/java/io/CharArrayWriter.scala b/javalib/src/main/scala/java/io/CharArrayWriter.scala index 3c9dfb3490..4367acc92a 100644 --- a/javalib/src/main/scala/java/io/CharArrayWriter.scala +++ b/javalib/src/main/scala/java/io/CharArrayWriter.scala @@ -58,7 +58,7 @@ class CharArrayWriter(initialSize: Int) extends Writer { override def write(str: String, offset: Int, len: Int): Unit = { if (offset < 0 || offset > str.length || len < 0 || len > str.length - offset) - throw new StringIndexOutOfBoundsException + throw new IndexOutOfBoundsException ensureCapacity(len) str.getChars(offset, offset + len, this.buf, this.count) diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/io/CharArrayWriterTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/io/CharArrayWriterTest.scala index 4bc7fb0391..a4b3fcc48e 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/io/CharArrayWriterTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/io/CharArrayWriterTest.scala @@ -116,6 +116,11 @@ class CharArrayWriterTest { cw.write("HelloWorld", 5, 5) assertEquals("World", cw.toString) assertArrayEquals("World".toCharArray, cw.toCharArray) + + assertThrows(classOf[IndexOutOfBoundsException], cw.write("foo", 1, 3)) + assertThrows(classOf[IndexOutOfBoundsException], cw.write("foo", -1, 3)) + assertThrows(classOf[IndexOutOfBoundsException], cw.write("foo", 2, -1)) + assertArrayEquals("World".toCharArray, cw.toCharArray) } } From 7b06e2c7a9563ea73f22a55c0acf2fc31f1b47c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Fri, 6 Mar 2026 11:32:36 +0100 Subject: [PATCH 17/21] Trigger UB for StringIOOBEs in all jl.String methods. --- .../src/main/scala/java/lang/_String.scala | 47 +++++++++++++++---- project/Build.scala | 4 +- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/javalib/src/main/scala/java/lang/_String.scala b/javalib/src/main/scala/java/lang/_String.scala index 19d78774e6..16f1b6a56d 100644 --- a/javalib/src/main/scala/java/lang/_String.scala +++ b/javalib/src/main/scala/java/lang/_String.scala @@ -216,10 +216,26 @@ final class _String private () // scalastyle:ignore res } - def getChars(srcBegin: Int, srcEnd: Int, dst: Array[Char], - dstBegin: Int): Unit = { - if (srcEnd > length() || srcBegin < 0 || srcEnd < 0 || srcBegin > srcEnd) - throw new StringIndexOutOfBoundsException("Index out of Bound") + def getChars(srcBegin: Int, srcEnd: Int, dst: Array[Char], dstBegin: Int): Unit = { + val dstLength = dst.length // implies null check + + // Bounds checks on the source + if (srcEnd > length() || srcBegin < 0 || srcEnd < 0 || srcBegin > srcEnd) { + if (srcBegin < 0) + charAt(srcBegin) + if (srcEnd > length()) + charAt(srcEnd) + charAt(-1) + } + + /* Bounds checks on the destination. + * Intuitively, they should throw ArrayIOOBE. However, in practice, the JVM + * throws throws a StringIOOBE. We follow that behavior. + */ + if (dstBegin < 0) + "".charAt(dstBegin) + if (dstBegin > dstLength - (srcEnd - srcBegin)) + "".charAt(dstBegin + (srcEnd - srcBegin)) val offset = dstBegin - srcBegin var i = srcBegin @@ -967,10 +983,8 @@ object _String { // scalastyle:ignore `new`(value, 0, value.length) def `new`(value: Array[Char], offset: Int, count: Int): String = { + checkBoundsForNewFromArray(offset, count, value.length) val end = offset + count - if (offset < 0 || end < offset || end > value.length) - throw new StringIndexOutOfBoundsException - var result = "" var i = offset while (i != end) { @@ -1003,10 +1017,8 @@ object _String { // scalastyle:ignore } def `new`(codePoints: Array[Int], offset: Int, count: Int): String = { + checkBoundsForNewFromArray(offset, count, codePoints.length) val end = offset + count - if (offset < 0 || end < offset || end > codePoints.length) - throw new StringIndexOutOfBoundsException - var result = "" var i = offset while (i != end) { @@ -1028,6 +1040,21 @@ object _String { // scalastyle:ignore def `new`(builder: java.lang.StringBuilder): String = builder.toString + @inline + private def checkBoundsForNewFromArray(offset: Int, count: Int, arrayLength: Int): Unit = { + /* Publicly specified as throwing an IndexOutOfBoundsException. + * Intuitively, should throw an ArrayIOOBE. However, in practice, the JVM + * throws a StringIOOBE. We replicate that behavior. + */ + if (offset < 0 || count < 0 || offset > arrayLength - count) { + if (offset < 0 || offset >= arrayLength) + "".charAt(offset) + if (count < 0) + "".charAt(count) + "".charAt(offset + count - 1) + } + } + // Static methods (aka methods on the companion object) def valueOf(b: scala.Boolean): String = b.toString() diff --git a/project/Build.scala b/project/Build.scala index 5d9fbf97b6..7afdb5849f 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -2092,14 +2092,14 @@ object Build { if (!useMinifySizes) { Some(ExpectedSizes( fastLink = 438000 to 439000, - fullLink = 263000 to 264000, + fullLink = 262000 to 263000, fastLinkGz = 57000 to 58000, fullLinkGz = 43000 to 44000, )) } else { Some(ExpectedSizes( fastLink = 304000 to 305000, - fullLink = 263000 to 264000, + fullLink = 262000 to 263000, fastLinkGz = 48000 to 49000, fullLinkGz = 43000 to 44000, )) From 23bf595559c80031e92f6dade2b73ec7eae33836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Wed, 4 Mar 2026 15:40:06 +0100 Subject: [PATCH 18/21] Throw IOOBE instead of ArrayIOOBE in CharArrayReader.read. It is specified as the more general exception, and in practice that is what the JVM throws. This was the only place where we explicitly threw an ArrayIOOBE not subject to UB. --- javalib/src/main/scala/java/io/CharArrayReader.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/javalib/src/main/scala/java/io/CharArrayReader.scala b/javalib/src/main/scala/java/io/CharArrayReader.scala index 3fe5723158..c4ac09dcbe 100644 --- a/javalib/src/main/scala/java/io/CharArrayReader.scala +++ b/javalib/src/main/scala/java/io/CharArrayReader.scala @@ -48,10 +48,10 @@ class CharArrayReader(protected var buf: Array[Char], offset: Int, length: Int) override def read(buffer: Array[Char], offset: Int, len: Int): Int = { if (offset < 0 || offset > buffer.length) - throw new ArrayIndexOutOfBoundsException("Offset out of bounds : " + offset) + throw new IndexOutOfBoundsException("Offset out of bounds : " + offset) if (len < 0 || len > buffer.length - offset) - throw new ArrayIndexOutOfBoundsException("Length out of bounds : " + len) + throw new IndexOutOfBoundsException("Length out of bounds : " + len) ensureOpen() From edd74b6f38b888f91004c522c5df210e19a45882 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Wed, 4 Mar 2026 15:50:32 +0100 Subject: [PATCH 19/21] Trigger a UB `ArrayStoreException` in `System.arraycopy`. This was the only place where we explicitly threw an `ArrayStoreException`, as opposed to a UBE. --- javalib/src/main/scala/java/lang/System.scala | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/javalib/src/main/scala/java/lang/System.scala b/javalib/src/main/scala/java/lang/System.scala index 4ca1a42342..fb0f7baaba 100644 --- a/javalib/src/main/scala/java/lang/System.scala +++ b/javalib/src/main/scala/java/lang/System.scala @@ -91,8 +91,10 @@ object System { import scala.{Boolean, Char, Byte, Short, Int, Long, Float, Double} - def mismatch(): Nothing = - throw new ArrayStoreException("Incompatible array types") + def mismatch(): Unit = { + // Trigger an ArrayStoreException subject to UB. + new Array[String](1).asInstanceOf[Array[Object]](0) = Integer.valueOf(0) + } def impl(srcLen: Int, destLen: Int, f: BiConsumer[Int, Int]): Unit = { /* Perform dummy swaps to trigger an ArrayIndexOutOfBoundsException or From dd7be5a0741a5e428901805e44c38cc673148bb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Wed, 4 Mar 2026 17:11:59 +0100 Subject: [PATCH 20/21] Replace explicit NegativeArraySizeException's by UB ones. The trick we use is to allocate an array of the requested length and discard it in statement position. We enhance the optimizer to recognize that pattern, so that we do not actually allocate an array. This is similar to what we do with `x.getClass()` to perform a null pointer check. --- .../main/scala/java/lang/StringBuilder.scala | 3 +-- javalib/src/main/scala/java/util/Arrays.scala | 6 ----- javalib/src/main/scala/java/util/BitSet.scala | 5 +--- .../backend/emitter/FunctionEmitter.scala | 15 ++++++++++- .../linker/backend/emitter/Transients.scala | 25 +++++++++++++++++++ .../backend/wasmemitter/FunctionEmitter.scala | 20 +++++++++++++++ .../frontend/optimizer/OptimizerCore.scala | 11 +++++--- project/Build.scala | 8 +++--- .../javalib/lang/StringBufferTest.scala | 5 +++- .../javalib/lang/StringBuilderTest.scala | 5 +++- .../testsuite/javalib/util/ArraysTest.scala | 14 +++++++++++ .../testsuite/javalib/util/BitSetTest.scala | 3 +-- .../testsuite/utils/AssertThrows.scala | 5 ++++ 13 files changed, 100 insertions(+), 25 deletions(-) diff --git a/javalib/src/main/scala/java/lang/StringBuilder.scala b/javalib/src/main/scala/java/lang/StringBuilder.scala index d3fb54138f..1f560f1d54 100644 --- a/javalib/src/main/scala/java/lang/StringBuilder.scala +++ b/javalib/src/main/scala/java/lang/StringBuilder.scala @@ -25,8 +25,7 @@ class StringBuilder extends AnyRef with CharSequence with Appendable with java.i def this(initialCapacity: Int) = { this() - if (initialCapacity < 0) - throw new NegativeArraySizeException() + new Array[Char](initialCapacity) // NegativeArraySize check } def this(seq: CharSequence) = this(seq.toString) diff --git a/javalib/src/main/scala/java/util/Arrays.scala b/javalib/src/main/scala/java/util/Arrays.scala index 886a43ff80..49a0de9ed1 100644 --- a/javalib/src/main/scala/java/util/Arrays.scala +++ b/javalib/src/main/scala/java/util/Arrays.scala @@ -456,7 +456,6 @@ object Arrays { @inline private def copyOfImpl[U, T](original: Array[U], newLength: Int)( implicit uops: ArrayOps[U], tops: ArrayCreateOps[T]): Array[T] = { - checkArrayLength(newLength) val copyLength = Math.min(newLength, uops.length(original)) val ret = tops.create(newLength) System.arraycopy(original, 0, ret, 0, copyLength) @@ -512,11 +511,6 @@ object Arrays { ret } - @inline private def checkArrayLength(len: Int): Unit = { - if (len < 0) - throw new NegativeArraySizeException - } - @noinline def asList[T <: AnyRef](a: Array[T]): List[T] = { new AbstractList[T] with RandomAccess { def size(): Int = diff --git a/javalib/src/main/scala/java/util/BitSet.scala b/javalib/src/main/scala/java/util/BitSet.scala index e43966defa..0b35758ff8 100644 --- a/javalib/src/main/scala/java/util/BitSet.scala +++ b/javalib/src/main/scala/java/util/BitSet.scala @@ -70,11 +70,8 @@ class BitSet private (private var bits: Array[Int]) extends Serializable with Cl def this(nbits: Int) = { this( bits = { - if (nbits < 0) - throw new NegativeArraySizeException - + new Array[Int](nbits) // NegativeArraySize check val length = (nbits + BitSet.RightBits) >> BitSet.AddressBitsPerWord - new Array[Int](length) } ) diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/FunctionEmitter.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/FunctionEmitter.scala index c6946460c5..8dad9d3940 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/FunctionEmitter.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/FunctionEmitter.scala @@ -938,6 +938,19 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { case Return(expr, label) => pushLhsInto(Lhs.Return(label), expr, tailPosLabels) + case Transient(CheckArrayLength(length)) => + unnest(length) { (newLength, env0) => + implicit val env = env0 + val jsLength = transformExprNoChar(newLength) + + if (semantics.negativeArraySizes != CheckedBehavior.Unchecked) { + js.If(jsLength < js.IntLiteral(0), + genCallHelper(VarField.throwNegativeArraySizeException)) + } else { + jsLength + } + } + case Transient(SystemArrayCopy(src, srcPos, dest, destPos, length)) => unnest(List(src, srcPos, dest, destPos, length)) { (newArgs, env0) => implicit val env = env0 @@ -2377,7 +2390,7 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { case _:Skip | _:VarDef | _:Assign | _:While | _:Debugger | _:JSSuperConstructorCall | _:JSDelete | _:StoreModule | - Transient(_: SystemArrayCopy) => + Transient(_:CheckArrayLength | _:SystemArrayCopy) => /* Go "back" to transformStat() after having dived into * expression statements. This can only happen for Lhs.Discard and * for Lhs.Return's whose target is a statement. diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Transients.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Transients.scala index df1356271d..b649e5b668 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Transients.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Transients.scala @@ -96,6 +96,31 @@ object Transients { } } + /** Check an array length as a statement. + * + * This node is produced by the optimizer when a NewArray ends up in + * statement position. We then only need to check the array length, but not + * actually allocate the array. + * + * This is important because we intentionally use `new Array[x](n)` in + * statement position to trigger NegativeArraySizeException's subject to UB. + */ + final case class CheckArrayLength(length: Tree) extends Transient.Value { + val tpe: Type = VoidType + + def traverse(traverser: Traverser): Unit = + traverser.traverse(length) + + def transform(t: Transformer)(implicit pos: Position): Tree = + Transient(CheckArrayLength(t.transform(length))) + + def printIR(out: IRTreePrinter): Unit = { + out.print("$checkArrayLength(") + out.print(length) + out.print(")") + } + } + /** Intrinsic for `System.arraycopy`. * * This node *assumes* that `src` and `dest` are non-null. It is the diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/FunctionEmitter.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/FunctionEmitter.scala index c6429253ba..81f81b43af 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/FunctionEmitter.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/FunctionEmitter.scala @@ -3571,6 +3571,26 @@ private class FunctionEmitter private ( case Transients.Cast(expr, tpe) => genCast(expr, tpe, tree.pos) + case Transients.CheckArrayLength(length) => + if (semantics.negativeArraySizes == CheckedBehavior.Unchecked) { + genTree(length, VoidType) + } else { + // if length < 0 + genTree(length, IntType) + markPosition(tree) + val lengthLocal = addSyntheticLocal(watpe.Int32) + fb += wa.LocalTee(lengthLocal) + fb += wa.I32Const(0) + fb += wa.I32LtS + fb.ifThen() { + // then throw NegativeArraySizeException + fb += wa.LocalGet(lengthLocal) + fb += wa.Call(genFunctionID.throwNegativeArraySizeException) + fb += wa.Unreachable + } + } + VoidType + case value: Transients.SystemArrayCopy => genSystemArrayCopy(tree, value) diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/optimizer/OptimizerCore.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/optimizer/OptimizerCore.scala index 428d5b685b..e52c4afc67 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/optimizer/OptimizerCore.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/optimizer/OptimizerCore.scala @@ -1844,10 +1844,13 @@ private[optimizer] abstract class OptimizerCore( case LoadModule(moduleClassName) => if (hasElidableConstructors(moduleClassName)) Skip()(stat.pos) else stat - case NewArray(_, length) if isNonNegativeIntLiteral(length) => - Skip()(stat.pos) - case NewArray(_, length) if semantics.negativeArraySizes == CheckedBehavior.Unchecked => - keepOnlySideEffects(length) + case NewArray(_, length) => + if (isNonNegativeIntLiteral(length)) + Skip()(stat.pos) + else if (semantics.negativeArraySizes == CheckedBehavior.Unchecked) + keepOnlySideEffects(length) + else + Transient(CheckArrayLength(length))(stat.pos) case ArrayValue(_, elems) => Block(elems.map(keepOnlySideEffects(_)))(stat.pos) case ArraySelect(array, index) diff --git a/project/Build.scala b/project/Build.scala index 7afdb5849f..9b2398beb4 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -2074,15 +2074,15 @@ object Build { case `default212Version` => if (!useMinifySizes) { Some(ExpectedSizes( - fastLink = 620000 to 621000, - fullLink = 284000 to 285000, - fastLinkGz = 75000 to 79000, + fastLink = 619000 to 620000, + fullLink = 283000 to 284000, + fastLinkGz = 75000 to 76000, fullLinkGz = 43000 to 44000, )) } else { Some(ExpectedSizes( fastLink = 425000 to 426000, - fullLink = 284000 to 285000, + fullLink = 283000 to 284000, fastLinkGz = 61000 to 62000, fullLinkGz = 43000 to 44000, )) diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/StringBufferTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/StringBufferTest.scala index bb4f1146e1..565be213ac 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/StringBufferTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/StringBufferTest.scala @@ -34,9 +34,12 @@ class StringBufferTest { @Test def init(): Unit = assertEquals("", new StringBuffer().toString()) - @Test def initInt(): Unit = + @Test def initInt(): Unit = { assertEquals("", new StringBuffer(5).toString()) + assertThrowsNegArraySizeIfCompliant(new StringBuffer(-3)) + } + @Test def initString(): Unit = { assertEquals("hello", new StringBuffer("hello").toString()) diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/StringBuilderTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/StringBuilderTest.scala index dccff37c9f..b3b08d38f0 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/StringBuilderTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/StringBuilderTest.scala @@ -36,9 +36,12 @@ class StringBuilderTest { @Test def init(): Unit = assertEquals("", new StringBuilder().toString()) - @Test def initInt(): Unit = + @Test def initInt(): Unit = { assertEquals("", new StringBuilder(5).toString()) + assertThrowsNegArraySizeIfCompliant(new StringBuilder(-3)) + } + @Test def initString(): Unit = { assertEquals("hello", new StringBuilder("hello").toString()) diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/ArraysTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/ArraysTest.scala index 15e0e8941b..12c5dc66d5 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/ArraysTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/ArraysTest.scala @@ -685,6 +685,20 @@ class ArraysTest { assertArrayEquals(Array[A](B(1), B(2), B(3), null, null), bscopyAsA) } + @Test def copyOfNegativeNewSize(): Unit = { + assumeTrue("requires compliant NegativeArraySizeException's", hasCompliantNegativeArraySizes) + + assertThrows(classOf[NegativeArraySizeException], Arrays.copyOf(new Array[Boolean](3), -3)) + assertThrows(classOf[NegativeArraySizeException], Arrays.copyOf(new Array[Char](3), -3)) + assertThrows(classOf[NegativeArraySizeException], Arrays.copyOf(new Array[Byte](3), -3)) + assertThrows(classOf[NegativeArraySizeException], Arrays.copyOf(new Array[Short](3), -3)) + assertThrows(classOf[NegativeArraySizeException], Arrays.copyOf(new Array[Int](3), -3)) + assertThrows(classOf[NegativeArraySizeException], Arrays.copyOf(new Array[Long](3), -3)) + assertThrows(classOf[NegativeArraySizeException], Arrays.copyOf(new Array[Float](3), -3)) + assertThrows(classOf[NegativeArraySizeException], Arrays.copyOf(new Array[Double](3), -3)) + assertThrows(classOf[NegativeArraySizeException], Arrays.copyOf(new Array[Object](3), -3)) + } + @Test def copyOfRangeAnyRef(): Unit = { val anyrefs: Array[AnyRef] = Array("a", "b", "c", "d", "e") val anyrefscopy = Arrays.copyOfRange(anyrefs, 2, 4) diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/BitSetTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/BitSetTest.scala index f5f05898fa..d32ef61fed 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/BitSetTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/BitSetTest.scala @@ -43,8 +43,7 @@ class BitSetTest { assertEquals("Failed to round BitSet element size", 96, bs.size()) } - // "Failed to throw exception when creating a new BitSet with negative element value" - assertThrows(classOf[NegativeArraySizeException], new BitSet(-9)) + assertThrowsNegArraySizeIfCompliant(new BitSet(-9)) } @Test def test_clone(): Unit = { diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/utils/AssertThrows.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/utils/AssertThrows.scala index 87fefaf548..de5947ede5 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/utils/AssertThrows.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/utils/AssertThrows.scala @@ -31,4 +31,9 @@ object AssertThrows { if (Platform.hasCompliantStringIndexOutOfBounds) assertThrows(classOf[StringIndexOutOfBoundsException], code) } + + def assertThrowsNegArraySizeIfCompliant(code: => Unit): Unit = { + if (Platform.hasCompliantNegativeArraySizes) + assertThrows(classOf[NegativeArraySizeException], code) + } } From cb6b2955d413b83f93ff7094a7df90dee01d94cc Mon Sep 17 00:00:00 2001 From: "v.karamyshev" Date: Mon, 9 Mar 2026 13:01:31 +0500 Subject: [PATCH 21/21] Don't use js.Map in ClassValue when targeting pure Wasm --- .../src/main/scala/java/lang/ClassValue.scala | 82 ++++++++++++------- project/Build.scala | 3 - 2 files changed, 53 insertions(+), 32 deletions(-) diff --git a/javalib/src/main/scala/java/lang/ClassValue.scala b/javalib/src/main/scala/java/lang/ClassValue.scala index 0ab92d37cb..0b06ed675d 100644 --- a/javalib/src/main/scala/java/lang/ClassValue.scala +++ b/javalib/src/main/scala/java/lang/ClassValue.scala @@ -17,16 +17,24 @@ import java.util.HashMap import scala.scalajs.js import scala.scalajs.js.annotation._ import scala.scalajs.LinkingInfo -import scala.scalajs.LinkingInfo.ESVersion +import scala.scalajs.LinkingInfo.{ESVersion, moduleKind} +import scala.scalajs.LinkingInfo.ModuleKind.{MinimalWasmModule, WasmComponent} import Utils._ abstract class ClassValue[T] protected () { private val jsMap: js.Map[Class[_], T] = { - if (LinkingInfo.esVersion >= ESVersion.ES2015 || js.typeOf(js.Dynamic.global.Map) != "undefined") - new js.Map() - else + LinkingInfo.linkTimeIf[js.Map[Class[_], T]]( + moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { null + } { + if (LinkingInfo.esVersion >= ESVersion.ES2015 || + js.typeOf(js.Dynamic.global.Map) != "undefined") { + new js.Map() + } else { + null + } + } } @inline @@ -35,7 +43,11 @@ abstract class ClassValue[T] protected () { * emitting ES 2015 code, which allows to dead-code-eliminate the branches * using `HashMap`s, and therefore `HashMap` itself. */ - LinkingInfo.esVersion >= ESVersion.ES2015 || jsMap != null + LinkingInfo.linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { + false + } { + LinkingInfo.esVersion >= ESVersion.ES2015 || jsMap != null + } } /* We use a HashMap instead of an IdentityHashMap because the latter is @@ -49,35 +61,47 @@ abstract class ClassValue[T] protected () { protected def computeValue(`type`: Class[_]): T def get(`type`: Class[_]): T = { - if (useJSMap) { - mapGetOrElseUpdate(jsMap, `type`)(() => computeValue(`type`)) - } else { - /* We first perform `get`, and if the result is null, we use - * `containsKey` to disambiguate a present null from an absent key. - * Since the purpose of ClassValue is to be used a cache indexed by Class - * values, the expected use case will have more hits than misses, and so - * this ordering should be faster on average than first performing `has` - * then `get`. - */ - javaMap.get(`type`) match { - case null => - if (javaMap.containsKey(`type`)) { - null.asInstanceOf[T] - } else { - val newValue = computeValue(`type`) - javaMap.put(`type`, newValue) - newValue - } - case value => - value + LinkingInfo.linkTimeIf(moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { + getJavaMap(`type`) + } { + if (useJSMap) { + mapGetOrElseUpdate(jsMap, `type`)(() => computeValue(`type`)) + } else { + getJavaMap(`type`) } } } + private def getJavaMap(`type`: Class[_]): T = { + /* We first perform `get`, and if the result is null, we use + * `containsKey` to disambiguate a present null from an absent key. + * Since the purpose of ClassValue is to be used a cache indexed by Class + * values, the expected use case will have more hits than misses, and so + * this ordering should be faster on average than first performing `has` + * then `get`. + */ + javaMap.get(`type`) match { + case null => + if (javaMap.containsKey(`type`)) { + null.asInstanceOf[T] + } else { + val newValue = computeValue(`type`) + javaMap.put(`type`, newValue) + newValue + } + case value => + value + } + } + def remove(`type`: Class[_]): Unit = { - if (useJSMap) - jsMap.delete(`type`) - else + LinkingInfo.linkTimeIf[Unit](moduleKind == MinimalWasmModule || moduleKind == WasmComponent) { javaMap.remove(`type`) + } { + if (useJSMap) + jsMap.delete(`type`) + else + javaMap.remove(`type`) + } } } diff --git a/project/Build.scala b/project/Build.scala index e33c99838f..7ec885d02b 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -2359,9 +2359,6 @@ object Build { contains(f, "/shared/src/test/scala-old-collections/") || contains(f, "/shared/src/test/require-scala2/") || contains(f, "/shared/src/test/scala/org/scalajs/testsuite/") && ( - // javalib/lang - !endsWith(f, "/lang/ClassValueTest.scala") && // js.Map in ClassValue - // javalib/util !endsWith(f, "/DateTest.scala") && // js.Date !endsWith(f, "/PropertiesTest.scala") && // Date.toString