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 961accc6e0..440d8ec4b9 100644 --- a/ir/shared/src/main/scala/org/scalajs/ir/Trees.scala +++ b/ir/shared/src/main/scala/org/scalajs/ir/Trees.scala @@ -1324,9 +1324,9 @@ 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" - final val TargetPureWasm = "core/targetPureWasm" } // Atomic expressions diff --git a/javalib/src/main/scala/java/io/CharArrayReader.scala b/javalib/src/main/scala/java/io/CharArrayReader.scala index 627f0613dd..c4ac09dcbe 100644 --- a/javalib/src/main/scala/java/io/CharArrayReader.scala +++ b/javalib/src/main/scala/java/io/CharArrayReader.scala @@ -48,14 +48,16 @@ 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() - 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/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/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/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/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/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/StringBuilder.scala b/javalib/src/main/scala/java/lang/StringBuilder.scala index a27e93c8fc..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) @@ -48,14 +47,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 +81,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 +92,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 +106,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 +114,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 +124,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 +157,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 +226,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 +260,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/javalib/src/main/scala/java/lang/System.scala b/javalib/src/main/scala/java/lang/System.scala index 4498e25cbb..d0f03d673c 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 @@ -101,8 +103,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 @@ -192,7 +196,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 +465,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..1358c73516 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) { @@ -226,10 +227,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 @@ -246,7 +263,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 +271,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 +320,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 +333,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 +407,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 +427,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 +444,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 +494,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 +508,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 +533,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 +551,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 +743,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 +843,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 @@ -1168,10 +1186,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) { @@ -1204,10 +1220,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) { @@ -1229,6 +1243,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/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/Arrays.scala b/javalib/src/main/scala/java/util/Arrays.scala index ac6cffda77..a9d7f905dd 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/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 559d88e7ee..7558fda2ef 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 @@ -258,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 = @@ -372,6 +373,38 @@ 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 + + /** 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 = throw new java.lang.Error("stub") 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/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/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 9e946dad1b..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() @@ -973,6 +979,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 +997,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) @@ -1149,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)) @@ -1218,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) @@ -1234,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) @@ -1252,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)) @@ -1261,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) @@ -1295,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) @@ -1332,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) @@ -1436,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) @@ -1470,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) @@ -1525,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) @@ -1554,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) @@ -1662,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. @@ -1782,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 => @@ -1867,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") @@ -2033,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)) @@ -2052,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 @@ -2076,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 @@ -2084,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 _ => } @@ -2232,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) @@ -2260,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) @@ -2495,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 @@ -2585,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 @@ -2800,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 @@ -2809,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 => @@ -2877,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) @@ -3249,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) @@ -3267,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)) @@ -3361,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) -> { () => @@ -3477,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) { @@ -3549,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) @@ -3563,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) -> { () => @@ -3577,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) -> { () => @@ -3596,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) @@ -3626,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) @@ -3788,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") @@ -3857,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) @@ -3927,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) @@ -4113,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) @@ -4126,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) @@ -4150,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) @@ -4243,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)) @@ -4260,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)) @@ -4282,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)) @@ -4349,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, @@ -4376,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) @@ -4431,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 } @@ -4582,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) @@ -4725,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..77eb30296f 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. @@ -3858,6 +3862,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) @@ -4016,8 +4040,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/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") 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 f7e47059d1..4385d9c25a 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 @@ -518,6 +518,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 { 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 e893eb41e1..82ff75bfc0 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,50 @@ 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 WitResourceTypeRef(className) => + digestBuilder.update('W'.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/main/scala/org/scalajs/linker/frontend/LinkTimeProperties.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/LinkTimeProperties.scala index 8e7ac23df0..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 @@ -16,30 +16,25 @@ 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, wasmFeatures: WasmFeatures, + 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), - TargetPureWasm -> - LinkTimeBoolean(wasmFeatures.targetPureWasm) + 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] = @@ -59,7 +54,17 @@ 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 + case MinimalWasmModule => 4 + case WasmComponent => 5 + } + new LinkTimeProperties(coreSpec.semantics, coreSpec.esFeatures, - coreSpec.wasmFeatures, coreSpec.targetIsWebAssembly) + coreSpec.wasmFeatures, moduleKindInt, coreSpec.targetIsWebAssembly) } } 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/main/scala/org/scalajs/linker/frontend/optimizer/OptimizerCore.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/optimizer/OptimizerCore.scala index 3cd81da783..1e67faae2c 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) @@ -843,7 +841,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, @@ -854,7 +852,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. @@ -922,7 +920,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 { @@ -1856,10 +1854,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) @@ -2553,7 +2554,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() @@ -7498,14 +7500,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/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/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/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))) + } +} 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 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..4b4432b1b3 --- /dev/null +++ b/linker/shared/src/test/scala/org/scalajs/linker/frontend/LambdaSynthesizerTest.scala @@ -0,0 +1,95 @@ +/* + * 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 WitResourceTypeRef(className) => WitResourceType(className) + 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), WitResourceTypeRef("R")), + 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}") + } + } + } +} diff --git a/package-lock.json b/package-lock.json index 850c20ab1c..33ba714084 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ }, "devDependencies": { "express": "4.22.1", - "jsdom": "16.7.0", + "jsdom": "28.1.0", "jszip": "3.8.0", "source-map-support": "0.5.19" } @@ -20,111 +20,223 @@ "integrity": "sha512-mNm/lblgES8UkVle8rGImXOz4TtL3eU3inHay/7TVchkKrb/lgcVvTK0+VAw8p5zQ0rgQsXm1j5dOlAAd+MeoA==", "license": "(Apache-2.0 WITH LLVM-exception)" }, - "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", @@ -132,11 +244,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", @@ -162,12 +277,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", @@ -212,18 +321,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", @@ -286,42 +383,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": { @@ -334,26 +434,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", @@ -373,27 +458,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", @@ -423,6 +487,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", @@ -453,80 +529,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", @@ -617,12 +625,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", @@ -641,22 +643,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", @@ -745,21 +731,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", @@ -773,15 +744,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": { @@ -801,26 +772,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" @@ -832,31 +802,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" @@ -868,9 +838,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": { @@ -919,44 +889,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": { @@ -976,19 +940,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", @@ -998,11 +949,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", @@ -1013,6 +967,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", @@ -1088,12 +1048,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", @@ -1118,23 +1072,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", @@ -1142,10 +1079,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", @@ -1162,15 +1105,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", @@ -1190,16 +1124,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" @@ -1220,12 +1148,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", @@ -1265,11 +1187,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", @@ -1284,15 +1209,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": { @@ -1445,6 +1370,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", @@ -1470,13 +1404,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": { @@ -1489,42 +1441,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": { @@ -1540,13 +1477,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.24.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.1.tgz", + "integrity": "sha512-5xoBibbmnjlcR3jdqtY2Lnx7WbrD/tHlT01TmvqZUFVc9Q1w4+j5hbnapTqbcXITMH1ovjq/W7BkqBilHiVAaA==", "dev": true, "engines": { - "node": ">= 4.0.0" + "node": ">=20.18.1" } }, "node_modules/unpipe": { @@ -1558,16 +1495,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", @@ -1592,102 +1519,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", @@ -1701,95 +1585,132 @@ "resolved": "https://registry.npmjs.org/@bytecodealliance/preview2-shim/-/preview2-shim-0.17.2.tgz", "integrity": "sha512-mNm/lblgES8UkVle8rGImXOz4TtL3eU3inHay/7TVchkKrb/lgcVvTK0+VAw8p5zQ0rgQsXm1j5dOlAAd+MeoA==" }, - "@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", @@ -1811,12 +1732,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", @@ -1849,15 +1764,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", @@ -1899,38 +1805,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": { @@ -1943,21 +1847,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": { @@ -1972,23 +1864,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", @@ -2012,6 +1887,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", @@ -2033,55 +1914,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", @@ -2144,12 +1982,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", @@ -2165,19 +1997,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", @@ -2236,15 +2055,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", @@ -2255,12 +2065,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": { @@ -2277,56 +2087,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 } } @@ -2371,38 +2180,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": { @@ -2417,16 +2220,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", @@ -2436,10 +2229,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": { @@ -2448,6 +2241,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", @@ -2499,12 +2298,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", @@ -2520,20 +2313,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", @@ -2541,10 +2320,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", @@ -2558,12 +2340,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", @@ -2580,16 +2356,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": { @@ -2601,12 +2371,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", @@ -2640,10 +2404,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": { @@ -2659,9 +2423,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" @@ -2780,6 +2544,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", @@ -2811,6 +2581,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", @@ -2818,33 +2603,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": { @@ -2857,10 +2630,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.24.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.1.tgz", + "integrity": "sha512-5xoBibbmnjlcR3jdqtY2Lnx7WbrD/tHlT01TmvqZUFVc9Q1w4+j5hbnapTqbcXITMH1ovjq/W7BkqBilHiVAaA==", "dev": true }, "unpipe": { @@ -2869,16 +2642,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", @@ -2897,73 +2660,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 d3f4e8aad0..3762de3fe3 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" }, 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/project/Build.scala b/project/Build.scala index fd259198fd..7ec885d02b 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) } @@ -2233,15 +2228,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, )) @@ -2251,14 +2246,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, )) @@ -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,16 +2351,14 @@ 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/") || 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 @@ -2489,9 +2490,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", @@ -2504,30 +2509,24 @@ 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 ++= { 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 } }, @@ -2570,6 +2569,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), @@ -2583,7 +2584,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/ScalaJSPlugin.scala b/sbt-plugin/src/main/scala/org/scalajs/sbtplugin/ScalaJSPlugin.scala index 687dcf3099..51ccc5e654 100644 --- a/sbt-plugin/src/main/scala/org/scalajs/sbtplugin/ScalaJSPlugin.scala +++ b/sbt-plugin/src/main/scala/org/scalajs/sbtplugin/ScalaJSPlugin.scala @@ -410,6 +410,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 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/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]) 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 dc5594a32f..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._ @@ -50,6 +51,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 = { @@ -80,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/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) 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/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)) } 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 1030aef054..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) @@ -999,6 +1001,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 { 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)) } } 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) } } 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/StringBufferTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/StringBufferTest.scala index e44a2c1f6a..2282d124ee 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 @@ -16,7 +16,7 @@ import org.junit.Test import org.junit.Assert._ import org.junit.Assume._ -import org.scalajs.testsuite.utils.AssertThrows.assertThrows +import org.scalajs.testsuite.utils.AssertThrows.{assertThrows, _} import org.scalajs.testsuite.utils.Platform.executingInJVM import WrappedStringCharSequence.charSequence @@ -35,9 +35,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()) @@ -168,10 +171,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 = { @@ -182,10 +183,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 = { @@ -200,12 +199,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)) @@ -222,16 +218,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], @@ -248,10 +240,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 = { @@ -261,10 +251,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 = { @@ -276,10 +264,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)) @@ -338,10 +324,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 = { @@ -420,7 +404,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) @@ -436,11 +420,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 = { @@ -457,12 +439,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 = { @@ -478,12 +456,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 = { @@ -569,10 +543,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 = { @@ -582,12 +554,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 = { @@ -602,16 +570,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 = { @@ -622,15 +584,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 5fae122d13..95a371e0db 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 @@ -18,7 +18,7 @@ import org.junit.Test import org.junit.Assert._ import org.junit.Assume._ -import org.scalajs.testsuite.utils.AssertThrows.assertThrows +import org.scalajs.testsuite.utils.AssertThrows.{assertThrows, _} import org.scalajs.testsuite.utils.Platform.executingInJVM import WrappedStringCharSequence.charSequence @@ -37,9 +37,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()) @@ -170,10 +173,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 = { @@ -184,10 +185,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 = { @@ -202,12 +201,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)) @@ -224,16 +220,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], @@ -250,10 +242,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 = { @@ -263,10 +253,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 = { @@ -278,10 +266,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)) @@ -340,10 +326,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 = { @@ -422,7 +406,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) @@ -438,11 +422,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 = { @@ -459,12 +441,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 = { @@ -480,12 +458,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 = { @@ -506,6 +480,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)) @@ -528,6 +503,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)) @@ -550,6 +526,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)) @@ -571,10 +548,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 = { @@ -584,12 +559,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 = { @@ -604,16 +575,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 = { @@ -624,16 +589,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/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/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 6ad19b7a0f..8381cb74ca 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 = { @@ -45,8 +47,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 = { @@ -1499,7 +1500,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() 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..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 @@ -26,4 +26,14 @@ object AssertThrows { if (Platform.hasCompliantNullPointers) assertThrows(classOf[NullPointerException], code) } + + def assertThrowsStringIIOBEIfCompliant(code: => Unit): Unit = { + if (Platform.hasCompliantStringIndexOutOfBounds) + assertThrows(classOf[StringIndexOutOfBoundsException], code) + } + + def assertThrowsNegArraySizeIfCompliant(code: => Unit): Unit = { + if (Platform.hasCompliantNegativeArraySizes) + assertThrows(classOf[NegativeArraySizeException], code) + } }