From 4b42414874955b46e28e12530746af9f4eb088db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 3 Feb 2025 13:03:31 +0100 Subject: [PATCH 1/8] Deprecate support for ECMAScript 5.1. That support is the biggest source of alternative code paths and polyfills in our codebase. The polyfills and other alternative implementations we need to support ES 5.1 include: * `Math.fround` to support `Float` operations (in `CoreJSLib`). * Other bit manipulation magic for floating-point numbers (in `FloatingPointBits`). * Using dynamic JS `Array`s instead of typed arrays for Scala `Array`s of primitive types. * Using custom Java `Map`s instead of `js.Map`. * Various implementations of math methods in `jl.Math` (although these may come back for a Wasm-only target, along with more of them). * Using run-time generated random property names instead of `Symbol`s. * Very complex code to handle surrogate pairs in `ju.regex.*`. * (Imperfect) desugaring of `...rest` parameters and `...spread` operators. Moreover, the ES 5.1 output does not even have exactly the same semantics as later versions: * JS classes are not true `class`es. Notably, that means they cannot extend native ES classes, and they do not inherit static members. * Top-level exports are declared as `var`s instead of `let`s. 10 years after the instruction of ECMAScript 2015, I believe it is time to deprecate the support for Es 5.1, so that we can eventually remove it. --- .../main/scala/org/scalajs/linker/interface/ESVersion.scala | 5 ++++- .../linker/interface/StandardConfigFingerprintTest.scala | 2 +- .../linker/backend/closure/ClosureLinkerBackend.scala | 5 ++++- .../src/test/scala/org/scalajs/linker/AnalyzerTest.scala | 1 + 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/ESVersion.scala b/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/ESVersion.scala index 6c55b76b8f..e8025020fc 100644 --- a/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/ESVersion.scala +++ b/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/ESVersion.scala @@ -30,7 +30,10 @@ final class ESVersion private (val edition: Int, val name: String) } object ESVersion { - /** ECMAScrîpt 5.1. */ + /** ECMAScript 5.1. */ + @deprecated( + "Support for ECMAScript 5.1 is deprecated and will eventually be removed.", + since = "1.19.0") val ES5_1: ESVersion = new ESVersion(5, "ECMAScript 5.1") /** ECMAScript 2015 (6th edition). */ diff --git a/linker-interface/shared/src/test/scala/org/scalajs/linker/interface/StandardConfigFingerprintTest.scala b/linker-interface/shared/src/test/scala/org/scalajs/linker/interface/StandardConfigFingerprintTest.scala index 193015cc07..0b495a5d3c 100644 --- a/linker-interface/shared/src/test/scala/org/scalajs/linker/interface/StandardConfigFingerprintTest.scala +++ b/linker-interface/shared/src/test/scala/org/scalajs/linker/interface/StandardConfigFingerprintTest.scala @@ -42,7 +42,7 @@ class StandardConfigFingerprintTest { @Test def noFingerprintCollisionESFeatures(): Unit = { val sc1 = StandardConfig().withESFeatures(_.withESVersion(ESVersion.ES2015)) - val sc2 = StandardConfig().withESFeatures(_.withESVersion(ESVersion.ES5_1)) + val sc2 = StandardConfig().withESFeatures(_.withESVersion(ESVersion.ES2016)) assertFingerprintsNotEquals(sc1, sc2) } diff --git a/linker/jvm/src/main/scala/org/scalajs/linker/backend/closure/ClosureLinkerBackend.scala b/linker/jvm/src/main/scala/org/scalajs/linker/backend/closure/ClosureLinkerBackend.scala index de9fdefa68..05ad09fe10 100644 --- a/linker/jvm/src/main/scala/org/scalajs/linker/backend/closure/ClosureLinkerBackend.scala +++ b/linker/jvm/src/main/scala/org/scalajs/linker/backend/closure/ClosureLinkerBackend.scala @@ -78,7 +78,6 @@ final class ClosureLinkerBackend(config: LinkerBackendImpl.Config) import ClosureOptions.LanguageMode._ esFeatures.esVersion match { - case ESVersion.ES5_1 => ECMASCRIPT5_STRICT case ESVersion.ES2015 => ECMASCRIPT_2015 case ESVersion.ES2016 => ECMASCRIPT_2016 case ESVersion.ES2017 => ECMASCRIPT_2017 @@ -87,6 +86,10 @@ final class ClosureLinkerBackend(config: LinkerBackendImpl.Config) case ESVersion.ES2020 => ECMASCRIPT_2020 case ESVersion.ES2021 => ECMASCRIPT_2021 + // Test for ES5_1 without triggering the deprecation warning + case esVersion if esVersion.edition == 5 => + ECMASCRIPT5_STRICT + case _ => throw new AssertionError(s"Unknown ES version ${esFeatures.esVersion}") } diff --git a/linker/shared/src/test/scala/org/scalajs/linker/AnalyzerTest.scala b/linker/shared/src/test/scala/org/scalajs/linker/AnalyzerTest.scala index ad0aeb6655..572998563a 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/AnalyzerTest.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/AnalyzerTest.scala @@ -654,6 +654,7 @@ class AnalyzerTest { } } + @deprecated("test deprecated features", "forever") @Test def newTargetWithoutES2015(): AsyncResult = await { val classDefs = Seq( From cd2c89701ae8e04b1406bc1cf0927605f49b3085 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 3 Feb 2025 13:24:08 +0100 Subject: [PATCH 2/8] Remove support for ECMAScript 5.1. In this commit, we do the minimum changes to remove `ESVersion.ES5_1`. All the supporting code is still present elsewhere. We will add separate commits for the various areas that can be removed. --- Jenkinsfile | 15 +++----- .../scala/scala/scalajs/LinkingInfo.scala | 5 ++- .../scalajs/linker/interface/ESFeatures.scala | 38 ++++--------------- .../scalajs/linker/interface/ESVersion.scala | 12 +++--- .../linker/interface/ESFeaturesTest.scala | 37 ------------------ .../closure/ClosureLinkerBackend.scala | 4 -- .../org/scalajs/linker/AnalyzerTest.scala | 30 --------------- 7 files changed, 24 insertions(+), 117 deletions(-) delete mode 100644 linker-interface/shared/src/test/scala/org/scalajs/linker/interface/ESFeaturesTest.scala diff --git a/Jenkinsfile b/Jenkinsfile index 5cbf59ce7f..176f2104a2 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -278,27 +278,27 @@ def Tasks = [ setJavaVersion $java npm install && sbtretry 'set scalaJSLinkerConfig in $testSuite.v$v ~= (_.withESFeatures(_.withESVersion(ESVersion.$esVersion)))' \ - 'set Seq(jsEnv in $testSuite.v$v := new NodeJSEnvForcePolyfills(ESVersion.$esVersion), MyScalaJSPlugin.wantSourceMaps in $testSuite.v$v := ("$esVersion" != "ES5_1"))' \ + 'set jsEnv in $testSuite.v$v := new NodeJSEnvForcePolyfills(ESVersion.$esVersion)' \ ++$scala $testSuite$v/test && sbtretry 'set scalaJSLinkerConfig in $testSuite.v$v ~= (_.withESFeatures(_.withESVersion(ESVersion.$esVersion)))' \ - 'set Seq(jsEnv in $testSuite.v$v := new NodeJSEnvForcePolyfills(ESVersion.$esVersion), MyScalaJSPlugin.wantSourceMaps in $testSuite.v$v := ("$esVersion" != "ES5_1"))' \ + 'set jsEnv in $testSuite.v$v := new NodeJSEnvForcePolyfills(ESVersion.$esVersion)' \ 'set scalaJSStage in Global := FullOptStage' \ ++$scala $testSuite$v/test && sbtretry 'set scalaJSLinkerConfig in $testSuite.v$v ~= (_.withESFeatures(_.withESVersion(ESVersion.$esVersion)))' \ - 'set Seq(jsEnv in $testSuite.v$v := new NodeJSEnvForcePolyfills(ESVersion.$esVersion), MyScalaJSPlugin.wantSourceMaps in $testSuite.v$v := ("$esVersion" != "ES5_1"))' \ + 'set jsEnv in $testSuite.v$v := new NodeJSEnvForcePolyfills(ESVersion.$esVersion)' \ 'set scalaJSLinkerConfig in $testSuite.v$v ~= (_.withOptimizer(false))' \ ++$scala $testSuite$v/test && sbtretry 'set scalaJSLinkerConfig in $testSuite.v$v ~= (_.withESFeatures(_.withESVersion(ESVersion.$esVersion)))' \ - 'set Seq(jsEnv in $testSuite.v$v := new NodeJSEnvForcePolyfills(ESVersion.$esVersion), MyScalaJSPlugin.wantSourceMaps in $testSuite.v$v := ("$esVersion" != "ES5_1"))' \ + 'set jsEnv in $testSuite.v$v := new NodeJSEnvForcePolyfills(ESVersion.$esVersion)' \ 'set scalaJSLinkerConfig in $testSuite.v$v ~= makeCompliant' \ ++$scala $testSuite$v/test && sbtretry 'set scalaJSLinkerConfig in $testSuite.v$v ~= (_.withESFeatures(_.withESVersion(ESVersion.$esVersion)))' \ - 'set Seq(jsEnv in $testSuite.v$v := new NodeJSEnvForcePolyfills(ESVersion.$esVersion), MyScalaJSPlugin.wantSourceMaps in $testSuite.v$v := ("$esVersion" != "ES5_1"))' \ + 'set jsEnv in $testSuite.v$v := new NodeJSEnvForcePolyfills(ESVersion.$esVersion)' \ 'set scalaJSLinkerConfig in $testSuite.v$v ~= makeCompliant' \ 'set scalaJSStage in Global := FullOptStage' \ ++$scala $testSuite$v/test && sbtretry 'set scalaJSLinkerConfig in $testSuite.v$v ~= (_.withESFeatures(_.withESVersion(ESVersion.$esVersion)))' \ - 'set Seq(jsEnv in $testSuite.v$v := new NodeJSEnvForcePolyfills(ESVersion.$esVersion), MyScalaJSPlugin.wantSourceMaps in $testSuite.v$v := ("$esVersion" != "ES5_1"))' \ + 'set jsEnv in $testSuite.v$v := new NodeJSEnvForcePolyfills(ESVersion.$esVersion)' \ 'set scalaJSLinkerConfig in $testSuite.v$v ~= makeCompliant' \ 'set scalaJSLinkerConfig in $testSuite.v$v ~= (_.withOptimizer(false))' \ ++$scala $testSuite$v/test @@ -542,7 +542,6 @@ def otherScalaVersions = [ def scala3Version = "3.6.3" def allESVersions = [ - "ES5_1", "ES2015", // "ES2016", // Technically we have the '**' operator dependent on ES2016, but it's not enough to justify testing this version "ES2017", @@ -561,11 +560,9 @@ mainScalaVersions.each { scalaVersion -> } quickMatrix.add([task: "test-suite-default-esversion", scala: scalaVersion, java: mainJavaVersion, testMinify: "false", testSuite: "testSuite"]) quickMatrix.add([task: "test-suite-default-esversion", scala: scalaVersion, java: mainJavaVersion, testMinify: "true", testSuite: "testSuite"]) - quickMatrix.add([task: "test-suite-custom-esversion", scala: scalaVersion, java: mainJavaVersion, esVersion: "ES5_1", testSuite: "testSuite"]) quickMatrix.add([task: "test-suite-webassembly", scala: scalaVersion, java: mainJavaVersion, testMinify: "false", testSuite: "testSuite"]) quickMatrix.add([task: "test-suite-webassembly", scala: scalaVersion, java: mainJavaVersion, testMinify: "false", testSuite: "testSuiteEx"]) quickMatrix.add([task: "test-suite-default-esversion", scala: scalaVersion, java: mainJavaVersion, testMinify: "false", testSuite: "scalaTestSuite"]) - quickMatrix.add([task: "test-suite-custom-esversion", scala: scalaVersion, java: mainJavaVersion, esVersion: "ES5_1", testSuite: "scalaTestSuite"]) quickMatrix.add([task: "test-suite-webassembly", scala: scalaVersion, java: mainJavaVersion, testMinify: "false", testSuite: "scalaTestSuite"]) quickMatrix.add([task: "bootstrap", scala: scalaVersion, java: mainJavaVersion]) quickMatrix.add([task: "partest-fastopt", scala: scalaVersion, java: mainJavaVersion, partestopts: ""]) diff --git a/library/src/main/scala/scala/scalajs/LinkingInfo.scala b/library/src/main/scala/scala/scalajs/LinkingInfo.scala index ea9d6c1a2f..18ade9955d 100644 --- a/library/src/main/scala/scala/scalajs/LinkingInfo.scala +++ b/library/src/main/scala/scala/scalajs/LinkingInfo.scala @@ -263,7 +263,10 @@ object LinkingInfo { /** Constants for the value of `esVersion`. */ object ESVersion { - /** ECMAScrîpt 5.1. */ + /** ECMAScript 5.1. */ + @deprecated( + "ECMAScript 2015 is not supported anymore; LinkingInfo.esVersion will never be equal to ES5_1.", + since = "future") final val ES5_1 = 5 /** ECMAScript 2015 (6th edition). */ diff --git a/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/ESFeatures.scala b/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/ESFeatures.scala index df68b6a501..7834ae082e 100644 --- a/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/ESFeatures.scala +++ b/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/ESFeatures.scala @@ -12,6 +12,8 @@ package org.scalajs.linker.interface +import scala.annotation.compileTimeOnly + import Fingerprint.FingerprintBuilder /** ECMAScript features to use when linking to JavaScript. @@ -75,13 +77,8 @@ final class ESFeatures private ( /** Use the ECMAScript 2015 semantics of Scala.js language features. * - * Default: `true` - * - * As of Scala.js 1.6.0, this is `true` if and only if - * `esVersion >= ESVersion.ES2015`. In the future, it might become - * independently configurable. - * - * When `true`, the following behaviors are guaranteed: + * Since "the future", this is always true. That means that the following + * behaviors are always guaranteed: * * - JavaScript classes are true `class`'es, therefore a) they can extend * native JavaScript `class`'es and b) they inherit static members from @@ -92,22 +89,8 @@ final class ESFeatures private ( * - Throwable classes are proper JavaScript error classes, recognized as * such by debuggers (only with the JavaScript backend; not in Wasm). * - In Script (`NoModule`) mode, top-level exports are defined as `let`s. - * - * When `false`, the following behaviors apply instead: - * - * - All classes defined in Scala.js are `function`s instead of `class`'es. - * Non-native JS classes cannot extend native JS `class`'es and they do - * not inherit static members from their parent class. - * - All lambdas for `js.Function`s are `function`s. - * - Throwable classes have JavaScript's `Error.prototype` in their - * prototype chain, but they are not considered proper error classes. - * - In Script (`NoModule`) mode, top-level exports are defined as `var`s. - * - * Prefer reading this value instead of `esVersion` to determine which - * semantics apply. Doing so will be future-proof if and when this setting - * becomes configurable independently from `esVersion`. */ - val useECMAScript2015Semantics = esVersion >= ESVersion.ES2015 + val useECMAScript2015Semantics: Boolean = true /** Use ECMAScript 2015 features. * @@ -191,14 +174,9 @@ final class ESFeatures private ( * Otherwise, if `esVersion` was below `ES2015`, it sets it to `ES2015`. * Otherwise, it returns the same configuration of `ESFeatures`. */ - @deprecated( - "use withESVersion(ESVersion.ES5_1) or withESVersion(ESVersion.ES2015) instead", - "1.6.0") - def withUseECMAScript2015(useECMAScript2015: Boolean): ESFeatures = { - if (!useECMAScript2015) withESVersion(ESVersion.ES5_1) - else if (esVersion == ESVersion.ES5_1) withESVersion(ESVersion.ES2015) - else this - } + @compileTimeOnly("ECMAScript 5.1 is not supported anymore.") + def withUseECMAScript2015(useECMAScript2015: Boolean): ESFeatures = + this def withAllowBigIntsForLongs(allowBigIntsForLongs: Boolean): ESFeatures = copy(allowBigIntsForLongs = allowBigIntsForLongs) diff --git a/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/ESVersion.scala b/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/ESVersion.scala index e8025020fc..82e0bd396a 100644 --- a/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/ESVersion.scala +++ b/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/ESVersion.scala @@ -12,6 +12,8 @@ package org.scalajs.linker.interface +import scala.annotation.compileTimeOnly + import Fingerprint.FingerprintBuilder final class ESVersion private (val edition: Int, val name: String) @@ -30,15 +32,13 @@ final class ESVersion private (val edition: Int, val name: String) } object ESVersion { - /** ECMAScript 5.1. */ - @deprecated( - "Support for ECMAScript 5.1 is deprecated and will eventually be removed.", - since = "1.19.0") - val ES5_1: ESVersion = new ESVersion(5, "ECMAScript 5.1") - /** ECMAScript 2015 (6th edition). */ val ES2015: ESVersion = new ESVersion(6, "ECMAScript 2015") + /** ECMAScript 5.1. */ + @compileTimeOnly("ECMAScript 5.1 is not supported anymore.") + val ES5_1: ESVersion = ES2015 + /** ECMAScript 2016 (7th edition). * * Contains the following notable features: diff --git a/linker-interface/shared/src/test/scala/org/scalajs/linker/interface/ESFeaturesTest.scala b/linker-interface/shared/src/test/scala/org/scalajs/linker/interface/ESFeaturesTest.scala deleted file mode 100644 index 311be69c78..0000000000 --- a/linker-interface/shared/src/test/scala/org/scalajs/linker/interface/ESFeaturesTest.scala +++ /dev/null @@ -1,37 +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.linker.interface - -import org.junit.Test -import org.junit.Assert._ - -class ESFeaturesTest { - @deprecated("test deprecated features", "forever") - @Test def esVersionAndUseECMAScript2015AreLinked(): Unit = { - import ESFeatures.Defaults - - assertEquals(ESVersion.ES2015, Defaults.esVersion) - assertTrue(Defaults.useECMAScript2015) - - assertFalse(Defaults.withESVersion(ESVersion.ES5_1).useECMAScript2015) - assertEquals(ESVersion.ES5_1, Defaults.withUseECMAScript2015(false).esVersion) - - val esFeaturesWithES2018 = Defaults.withESVersion(ESVersion.ES2018) - assertTrue(esFeaturesWithES2018.useECMAScript2015) - - assertEquals(ESVersion.ES2018, esFeaturesWithES2018.withUseECMAScript2015(true).esVersion) - assertEquals(ESVersion.ES5_1, esFeaturesWithES2018.withUseECMAScript2015(false).esVersion) - - assertFalse(esFeaturesWithES2018.withESVersion(ESVersion.ES5_1).useECMAScript2015) - } -} diff --git a/linker/jvm/src/main/scala/org/scalajs/linker/backend/closure/ClosureLinkerBackend.scala b/linker/jvm/src/main/scala/org/scalajs/linker/backend/closure/ClosureLinkerBackend.scala index 05ad09fe10..2d1de4d02b 100644 --- a/linker/jvm/src/main/scala/org/scalajs/linker/backend/closure/ClosureLinkerBackend.scala +++ b/linker/jvm/src/main/scala/org/scalajs/linker/backend/closure/ClosureLinkerBackend.scala @@ -86,10 +86,6 @@ final class ClosureLinkerBackend(config: LinkerBackendImpl.Config) case ESVersion.ES2020 => ECMASCRIPT_2020 case ESVersion.ES2021 => ECMASCRIPT_2021 - // Test for ES5_1 without triggering the deprecation warning - case esVersion if esVersion.edition == 5 => - ECMASCRIPT5_STRICT - case _ => throw new AssertionError(s"Unknown ES version ${esFeatures.esVersion}") } diff --git a/linker/shared/src/test/scala/org/scalajs/linker/AnalyzerTest.scala b/linker/shared/src/test/scala/org/scalajs/linker/AnalyzerTest.scala index 572998563a..c147524674 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/AnalyzerTest.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/AnalyzerTest.scala @@ -654,36 +654,6 @@ class AnalyzerTest { } } - @deprecated("test deprecated features", "forever") - @Test - def newTargetWithoutES2015(): AsyncResult = await { - val classDefs = Seq( - mainTestClassDef(LoadJSConstructor("A")), - classDef("A", - kind = ClassKind.JSClass, - superClass = Some(JSObjectLikeClass), - jsConstructor = Some( - JSConstructorDef(JSCtorFlags, Nil, None, JSConstructorBody( - Nil, - JSSuperConstructorCall(Nil), - JSNewTarget() :: Nil - ))(EOH, UNV) - ) - ), - JSObjectLikeClassDef - ) - - val moduleInitializer = MainTestModuleInitializers - - val analysis = computeAnalysis(classDefs, - moduleInitializers = MainTestModuleInitializers, - config = StandardConfig().withESFeatures(_.withESVersion(ESVersion.ES5_1))) - - assertContainsError("NewTargetWithoutES2015Support", analysis) { - case NewTargetWithoutES2015Support(_) => true - } - } - @Test def importMetaWithoutESModule(): AsyncResult = await { val classDefs = Seq( From cf13e15b0313c23b3a9e8da9dd1b433daeca4c59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 3 Feb 2025 13:48:33 +0100 Subject: [PATCH 3/8] Deprecate useECMAScript2015Semantics as always true. And remove support for `!useECMAScript2015Semantics` in the linker. --- .../src/main/scala/org/scalajs/ir/Trees.scala | 2 +- .../scala/scala/scalajs/LinkingInfo.scala | 47 ++----------------- .../scalajs/linker/interface/ESFeatures.scala | 6 +-- .../linker/interface/StandardConfig.scala | 1 - .../backend/WebAssemblyLinkerBackend.scala | 4 -- .../linker/backend/emitter/Emitter.scala | 22 +++------ .../backend/emitter/FunctionEmitter.scala | 35 ++------------ .../linker/backend/emitter/JSGen.scala | 22 +-------- .../linker/standard/LinkTimeProperties.scala | 2 +- project/Build.scala | 1 - .../scalajs/testsuite/utils/BuildInfo.scala | 1 - .../scalajs/testsuite/utils/Platform.scala | 3 -- .../javalib/lang/ThrowableJSTest.scala | 1 - .../testsuite/jsinterop/ExportsTest.scala | 13 +---- .../testsuite/jsinterop/FunctionTest.scala | 5 -- .../jsinterop/JSExportStaticTest.scala | 4 +- .../jsinterop/NonNativeJSTypeTest.scala | 3 +- .../jsinterop/TopLevelExportsTest.scala | 34 ++++---------- .../testsuite/library/LinkingInfoTest.scala | 2 +- 19 files changed, 33 insertions(+), 175 deletions(-) diff --git a/ir/shared/src/main/scala/org/scalajs/ir/Trees.scala b/ir/shared/src/main/scala/org/scalajs/ir/Trees.scala index e75ab17925..959c90051f 100644 --- a/ir/shared/src/main/scala/org/scalajs/ir/Trees.scala +++ b/ir/shared/src/main/scala/org/scalajs/ir/Trees.scala @@ -1073,7 +1073,7 @@ object Trees { object LinkTimeProperty { final val ProductionMode = "core/productionMode" final val ESVersion = "core/esVersion" - final val UseECMAScript2015Semantics = "core/useECMAScript2015Semantics" + final val UseECMAScript2015Semantics = "core/useECMAScript2015Semantics" // legacy, always true final val IsWebAssembly = "core/isWebAssembly" final val LinkerVersion = "core/linkerVersion" } diff --git a/library/src/main/scala/scala/scalajs/LinkingInfo.scala b/library/src/main/scala/scala/scalajs/LinkingInfo.scala index 18ade9955d..0188784318 100644 --- a/library/src/main/scala/scala/scalajs/LinkingInfo.scala +++ b/library/src/main/scala/scala/scalajs/LinkingInfo.scala @@ -163,9 +163,10 @@ object LinkingInfo { esVersion >= ESVersion.ES2015 /** Whether Scala.js language features use ECMAScript 2015 (edition 6) - * semantics or not. + * semantics or not (always true). * - * When `true`, the following semantics apply: + * Since "future", this is always constant-folded to `true`. The following + * semantics always apply: * * - JavaScript classes are true `class`'es, therefore a) they can extend * native JavaScript `class`'es and b) they inherit static members from @@ -176,49 +177,9 @@ object LinkingInfo { * - Throwable classes are proper JavaScript error classes, recognized as * such by debuggers. * - In Script (`NoModule`) mode, top-level exports are defined as `let`s. - * - * When `false`, the following semantics apply: - * - * - All classes defined in Scala.js are `function`s instead of `class`'es. - * Non-native JS classes cannot extend native JS `class`'es and they do - * not inherit static members from their parent class. - * - All lambdas for `js.Function`s are `function`s. - * - Throwable classes have JavaScript's `Error.prototype` in their - * prototype chain, but they are not considered proper error classes. - * - In Script (`NoModule`) mode, top-level exports are defined as `var`s. - * - * Prefer reading this value instead of `esVersion` to determine which - * semantics apply. - * - * For example, it can be used in tests whose results depend on which - * semantics are used. - * - * --- - * - * This ends up being constant-folded to a constant at link-time. So - * constant-folding, inlining, and other local optimizations can be - * leveraged with this "constant" to write alternatives that can be - * dead-code-eliminated. - * - * A typical usage of this method is: - * {{{ - * if (useECMAScript2015Semantics) - * implementationWithES2015Semantics() - * else - * implementationWithoutES2015Semantics() - * }}} - * - * At link-time, `useECMAScript2015Semantics` will either be a constant - * true, in which case the above snippet folds into - * {{{ - * implementationWithES2015Semantics() - * }}} - * or a constant false, in which case it folds into - * {{{ - * implementationWithoutES2015Semantics() - * }}} */ @inline + @deprecated("always true", since = "future") def useECMAScript2015Semantics: Boolean = linkTimePropertyBoolean("core/useECMAScript2015Semantics") diff --git a/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/ESFeatures.scala b/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/ESFeatures.scala index 7834ae082e..a28749310c 100644 --- a/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/ESFeatures.scala +++ b/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/ESFeatures.scala @@ -69,9 +69,6 @@ final class ESFeatures private ( * * - provide more features that rely on recent ECMAScript language features, and/or * - dead-code-eliminate away polyfills. - * - * Prefer reading this value over `useECMAScript2015Semantics` to perform - * feature tests. */ val esVersion: ESVersion = _esVersion @@ -90,6 +87,7 @@ final class ESFeatures private ( * such by debuggers (only with the JavaScript backend; not in Wasm). * - In Script (`NoModule`) mode, top-level exports are defined as `let`s. */ + @deprecated("ES 5.1 is not supported anymore. This is always true", since = "future") val useECMAScript2015Semantics: Boolean = true /** Use ECMAScript 2015 features. @@ -210,7 +208,6 @@ final class ESFeatures private ( override def toString(): String = { s"""ESFeatures( | esVersion = $esVersion, - | useECMAScript2015Semantics = $useECMAScript2015Semantics, | allowBigIntsForLongs = $allowBigIntsForLongs, | avoidClasses = $avoidClasses, | avoidLetsAndConsts = $avoidLetsAndConsts @@ -239,7 +236,6 @@ object ESFeatures { /** Default configuration of ECMAScript features. * * - `esVersion`: `ESVersion.ES2015` - * - `useECMAScript2015Semantics`: true * - `allowBigIntsForLongs`: false * - `avoidClasses`: true * - `avoidLetsAndConsts`: true diff --git a/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/StandardConfig.scala b/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/StandardConfig.scala index 2f3a2afe7b..f860f9c888 100644 --- a/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/StandardConfig.scala +++ b/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/StandardConfig.scala @@ -191,7 +191,6 @@ final class StandardConfig private ( * When using this setting, the following properties must also hold: * * - `moduleKind == ModuleKind.ESModule` - * - `esFeatures.useECMAScript2015Semantics == true` (true by default) * * We may lift these restrictions in the future, although we do not expect * to do so. 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 a167a7bf9f..d663e4b8f9 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 @@ -36,10 +36,6 @@ final class WebAssemblyLinkerBackend(config: LinkerBackendImpl.Config) coreSpec.moduleKind == ModuleKind.ESModule, s"The WebAssembly backend only supports ES modules; was ${coreSpec.moduleKind}." ) - require( - coreSpec.esFeatures.useECMAScript2015Semantics, - s"The WebAssembly backend only supports the ECMAScript 2015 semantics." - ) require(coreSpec.targetIsWebAssembly, s"A WebAssembly backend cannot be used with CoreSpec targeting JavaScript") diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Emitter.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Emitter.scala index c6576f4f5d..4a91dd2662 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Emitter.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Emitter.scala @@ -106,12 +106,9 @@ final class Emitter(config: Emitter.Config, prePrinter: Emitter.PrePrinter) { .map(_.exportName) val header = { - val maybeTopLevelVarDecls = if (topLevelVars.nonEmpty) { - val kw = if (esFeatures.useECMAScript2015Semantics) "let " else "var " - topLevelVars.mkString(kw, ",", ";\n") - } else { - "" - } + val maybeTopLevelVarDecls = + if (topLevelVars.isEmpty) "" + else topLevelVars.mkString("let ", ",", ";\n") config.jsHeader + maybeTopLevelVarDecls + "(function(){\n" } @@ -489,9 +486,6 @@ final class Emitter(config: Emitter.Config, prePrinter: Emitter.PrePrinter) { * Accessing `ancestors` without cache invalidation is fine because it * is part of the identity of a class for its cache in the first place. * - * Note that `useClassesForRegularClasses` implies - * `useClassesForJSClassesAndThrowables`, so the short-cut is valid. - * * Compared to `ClassEmitter.shouldExtendJSError`, which is used below, * we do not check here that `Throwable` directly extends `Object`. If * that is not the case (for some obscure reason), then we are going to @@ -504,12 +498,10 @@ final class Emitter(config: Emitter.Config, prePrinter: Emitter.PrePrinter) { * it is the only "dynamic" value it depends on. The rest is configuration * or part of the cache key (ancestors). */ - val useESClass = if (jsGen.useClassesForRegularClasses) { - assert(jsGen.useClassesForJSClassesAndThrowables) - true - } else { - jsGen.useClassesForJSClassesAndThrowables && - (isJSClass || linkedClass.ancestors.contains(ThrowableClass)) + val useESClass = { + jsGen.useClassesForRegularClasses || + isJSClass || + linkedClass.ancestors.contains(ThrowableClass) } val memberIndent = { 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 2252d71cd4..e36033eb66 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 @@ -532,12 +532,11 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { case other => other } - val actualArrowFun = arrow && esFeatures.useECMAScript2015Semantics val jsParams = params.map(transformParamDef(_)) if (es2015) { val jsRestParam = restParam.map(transformParamDef(_)) - js.Function(actualArrowFun, jsParams, jsRestParam, cleanedNewBody) + js.Function(arrow, jsParams, jsRestParam, cleanedNewBody) } else { val patchedBody = restParam.fold { cleanedNewBody @@ -545,7 +544,7 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { js.Block(makeExtractRestParam(restParam, jsParams.size), cleanedNewBody) } - js.Function(actualArrowFun, jsParams, None, patchedBody) + js.Function(arrow, jsParams, None, patchedBody) } } @@ -771,30 +770,7 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { "Need enclosing class for super constructor call.") } - val superCtorCall = if (useClassesForJSClassesAndThrowables) { - js.Apply(js.Super(), newArgs.map(transformJSArg)) - } else { - val superCtor = { - if (globalKnowledge.hasStoredSuperClass(enclosingClassName)) { - fileLevelVar(VarField.superClass) - } else { - val superClass = - globalKnowledge.getSuperClassOfJSClass(enclosingClassName) - extractWithGlobals(genJSClassConstructor(superClass)) - } - } - - if (needsToTranslateAnySpread(newArgs)) { - val argArray = spreadToArgArray(newArgs) - js.Apply( - genIdentBracketSelect(superCtor, "apply"), - List(js.This(), transformExprNoChar(argArray))) - } else { - js.Apply( - genIdentBracketSelect(superCtor, "call"), - js.This() :: newArgs.map(transformJSArg)) - } - } + val superCtorCall = js.Apply(js.Super(), newArgs.map(transformJSArg)) val enclosingClassFieldDefs = globalKnowledge.getFieldDefs(enclosingClassName) @@ -3124,9 +3100,6 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { private def prepareCapture(value: Tree, forceName: Option[LocalName], arrow: Boolean)( explicitCapture: () => Unit)(implicit env: Env): VarKind = { - def permitImplicitJSThisCapture = - esFeatures.useECMAScript2015Semantics && arrow - value match { case VarRef(name) => /* forceName is needed when capturing for Closure trees: @@ -3159,7 +3132,7 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { * not the same. */ - case VarKind.ThisAlias if permitImplicitJSThisCapture => + case VarKind.ThisAlias if arrow => VarKind.ThisAlias case VarKind.ExplicitThisAlias => diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/JSGen.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/JSGen.scala index ea9be90e76..f14e088eac 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/JSGen.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/JSGen.scala @@ -27,34 +27,14 @@ private[emitter] final class JSGen(val config: Emitter.Config) { import config._ import coreSpec._ - /** Should we use ECMAScript classes for JavaScript classes and Throwable - * classes? - * - * This is true iff `useECMAScript2015Semantics` is true, independently of - * [[org.scalajs.linker.interface.ESFeatures.avoidClasses ESFeatures.avoidClasses]]. - * - * We must emit classes for JavaScript classes for semantics reasons: - * inheritance of static properties and ability to extend native JavaScript - * ES classes. - * - * We must emit classes Throwable classes so that they are recognized as - * proper JavaScript error classes, which gives them better support in - * debuggers. - */ - val useClassesForJSClassesAndThrowables = esFeatures.useECMAScript2015Semantics - /** Should we use ECMAScript classes for non-Throwable Scala classes? * * If [[org.scalajs.linker.interface.ESFeatures.avoidClasses ESFeatures.avoidClasses]] * is true, we do not use classes for non-Throwable classes. We can do that * because whether regular classes are compiled as classes or functions and * prototypes has no impact on observable semantics. - * - * `useClassesForRegularClasses` is always false when - * `useClassesForJSClassesAndThrowables` is false. */ - val useClassesForRegularClasses = - useClassesForJSClassesAndThrowables && !esFeatures.avoidClasses + val useClassesForRegularClasses = !esFeatures.avoidClasses /** Should we emit `let`s and `const`s for all internal variables? * diff --git a/linker/shared/src/main/scala/org/scalajs/linker/standard/LinkTimeProperties.scala b/linker/shared/src/main/scala/org/scalajs/linker/standard/LinkTimeProperties.scala index 875196c736..d944a41547 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/standard/LinkTimeProperties.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/standard/LinkTimeProperties.scala @@ -29,7 +29,7 @@ private[linker] final class LinkTimeProperties ( ESVersion -> LinkTimeInt(esFeatures.esVersion.edition), UseECMAScript2015Semantics -> - LinkTimeBoolean(esFeatures.useECMAScript2015Semantics), + LinkTimeBoolean(true), // historical; always true IsWebAssembly -> LinkTimeBoolean(targetIsWebAssembly), ProductionMode -> diff --git a/project/Build.scala b/project/Build.scala index d08f6224c9..0d0e1f4408 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -2328,7 +2328,6 @@ object Build { "compliantModuleInit" -> (sems.moduleInit == CheckedBehavior.Compliant), "productionMode" -> sems.productionMode, "esVersion" -> linkerConfig.esFeatures.esVersion.edition, - "useECMAScript2015Semantics" -> linkerConfig.esFeatures.useECMAScript2015Semantics, "isWebAssembly" -> linkerConfig.experimentalUseWebAssembly, ) }, 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..6e1bbbf58e 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 @@ -33,5 +33,4 @@ private[utils] object BuildInfo { final val compliantModuleInit = false final val productionMode = false final val esVersion = 0 - final val useECMAScript2015Semantics = 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 65ab362539..f809bbeaf7 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 @@ -42,9 +42,6 @@ object Platform { /** Convenience for `assumedESVersion >= ESVersion.ES2015`. */ def assumeES2015: Boolean = assumedESVersion >= ESVersion.ES2015 - /** Whether Scala.js language features use ECMAScript 2015 semantics. */ - def useECMAScript2015Semantics: Boolean = BuildInfo.useECMAScript2015Semantics - def jsSymbols: Boolean = assumeES2015 || js.typeOf(js.Dynamic.global.Symbol) != "undefined" diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/javalib/lang/ThrowableJSTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/javalib/lang/ThrowableJSTest.scala index 74a9180361..45cb96b207 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/javalib/lang/ThrowableJSTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/javalib/lang/ThrowableJSTest.scala @@ -31,7 +31,6 @@ class ThrowableJSTest { @Test def throwablesAreTrueErrors(): Unit = { assumeFalse("Not supported on WebAssembly", executingInWebAssembly) - assumeTrue("Requires ECMAScript 2015 semantics", useECMAScript2015Semantics) def coreToString(x: Any): String = { js.constructorOf[js.Object].prototype diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/ExportsTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/ExportsTest.scala index 9bb851510a..fd6b904f82 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/ExportsTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/ExportsTest.scala @@ -278,7 +278,7 @@ class ExportsTest { val scalaA = new A("outer") val jsA = scalaA.asInstanceOf[js.Dynamic] - val body = if (useECMAScript2015Semantics) { + val body = { """ class SubClass extends constr { constructor(x) { @@ -290,17 +290,6 @@ class ExportsTest { } return SubClass; """ - } else { - """ - function SubClass(x) { - constr.call(this, x + " from super"); - } - SubClass.prototype = Object.create(constr.prototype); - SubClass.prototype.foo = function(y) { - return "foo result"; - }; - return SubClass; - """ } val subclassFun = new js.Function("constr", body) diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/FunctionTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/FunctionTest.scala index 701e6bcd3a..b5879c3016 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/FunctionTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/FunctionTest.scala @@ -19,7 +19,6 @@ import org.junit.Assume._ import org.junit.Test import org.scalajs.testsuite.utils.AssertThrows.assertThrows -import org.scalajs.testsuite.utils.Platform.useECMAScript2015Semantics class FunctionTest { @@ -47,8 +46,6 @@ class FunctionTest { } @Test def functionWithConversionIsAnArrowFunction(): Unit = { - assumeTrue("Requires ECMAScript 2015 semantics", useECMAScript2015Semantics) - val ctor: js.Function = (x: js.Any) => x val ctorDyn = ctor.asInstanceOf[js.Dynamic] @@ -59,8 +56,6 @@ class FunctionTest { } @Test def functionWithSAMIsAnArrowFunction(): Unit = { - assumeTrue("Requires ECMAScript 2015 semantics", useECMAScript2015Semantics) - val ctor: js.Function1[js.Any, Any] = (x: js.Any) => x val ctorDyn = ctor.asInstanceOf[js.Dynamic] diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/JSExportStaticTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/JSExportStaticTest.scala index 9b38d3e856..bb20b094bf 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/JSExportStaticTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/JSExportStaticTest.scala @@ -278,9 +278,7 @@ class JSExportStaticTest { // Inherited members - @Test def testInheritedMembersInECMAScript2015(): Unit = { - assumeTrue("Requires ECMAScript 2015 semantics", useECMAScript2015Semantics) - + @Test def testInheritedMembers(): Unit = { val parent = js.constructorOf[JSExportStaticTest.StaticExportsParent] val child = js.constructorOf[JSExportStaticTest.StaticExportsChild] diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/NonNativeJSTypeTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/NonNativeJSTypeTest.scala index 0417489023..76f8ce69b0 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/NonNativeJSTypeTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/NonNativeJSTypeTest.scala @@ -219,8 +219,7 @@ class NonNativeJSTypeTest { val obj3 = new SubclassOfMethodNamedConstructorNoRedefine(42) assertSame(js.constructorOf[SubclassOfMethodNamedConstructorNoRedefine], obj3.asInstanceOf[js.Dynamic].constructor) - if (Platform.useECMAScript2015Semantics) - assertThrows(classOf[Exception], obj3.constructor(1)) + assertThrows(classOf[Exception], obj3.constructor(1)) } @Test def defaultValuesForFields(): Unit = { 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..daaa6fb2a7 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 @@ -137,7 +137,7 @@ class TopLevelExportsTest { for (constr <- constrFuture) yield { assertEquals("function", js.typeOf(constr)) - val body = if (useECMAScript2015Semantics) { + val body = { """ class SubClass extends constr { constructor(x) { @@ -149,17 +149,6 @@ class TopLevelExportsTest { } return SubClass; """ - } else { - """ - function SubClass(x) { - constr.call(this, x); - } - SubClass.prototype = Object.create(constr.prototype); - SubClass.prototype.foo = function(y) { - return y + this.x; - }; - return SubClass; - """ } val subclassFun = new js.Function("constr", body) @@ -486,23 +475,20 @@ class TopLevelExportsTest { TopLevelFieldExports.inlineVar = "hello" } - // @JSExportTopLevel in Script's are `let`s in ES 2015, `var`s in ES 5.1 + // @JSExportTopLevel in Script's are `let`s - @Test def topLevelExportsNoModuleAreOfCorrectKind(): Unit = { + @Test def topLevelExportsNoModuleAreLets(): Unit = { assumeTrue("relevant only for NoModule", isNoModule) val g = JSUtils.globalObject - // Do we expect to get undefined when looking up the exports in the global object? - val undefinedExpected = useECMAScript2015Semantics - - assertEquals(undefinedExpected, js.isUndefined(g.TopLevelExportedObject)) - assertEquals(undefinedExpected, js.isUndefined(g.SJSDefinedTopLevelExportedObject)) - assertEquals(undefinedExpected, js.isUndefined(g.TopLevelExportedClass)) - assertEquals(undefinedExpected, js.isUndefined(g.SJSDefinedTopLevelExportedClass)) - assertEquals(undefinedExpected, js.isUndefined(g.TopLevelExport_basic)) - assertEquals(undefinedExpected, js.isUndefined(g.TopLevelExport_basicVal)) - assertEquals(undefinedExpected, js.isUndefined(g.TopLevelExport_basicVar)) + assertEquals(true, js.isUndefined(g.TopLevelExportedObject)) + assertEquals(true, js.isUndefined(g.SJSDefinedTopLevelExportedObject)) + assertEquals(true, js.isUndefined(g.TopLevelExportedClass)) + assertEquals(true, js.isUndefined(g.SJSDefinedTopLevelExportedClass)) + assertEquals(true, js.isUndefined(g.TopLevelExport_basic)) + assertEquals(true, js.isUndefined(g.TopLevelExport_basicVal)) + assertEquals(true, js.isUndefined(g.TopLevelExport_basicVar)) } } 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..ce13936fa3 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 @@ -35,7 +35,7 @@ class LinkingInfoTest { assertEquals(Platform.assumedESVersion >= ESVersion.ES2015, LinkingInfo.assumingES6) @Test def useECMAScript2015Semantics(): Unit = - assertEquals(Platform.useECMAScript2015Semantics, LinkingInfo.useECMAScript2015Semantics) + assertEquals(true, LinkingInfo.useECMAScript2015Semantics) @Test def isWebAssembly(): Unit = assertEquals(Platform.executingInWebAssembly, LinkingInfo.isWebAssembly) From f882bd081122ec20dfd1a4d46c53e7b350da0034 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 3 Feb 2025 14:13:39 +0100 Subject: [PATCH 4/8] Globally assume ES2015 support in the test suite. Remove all the `assumeTrue(esVersion >= ES2015)` tests, as it is now always true by construction. --- .../scalajs/testsuite/utils/Platform.scala | 17 ---------- .../jsinterop/SJSDynamicImportTest.scala | 8 ----- .../testsuite/compiler/FloatJSTest.scala | 2 -- .../compiler/InteroperabilityTest.scala | 2 -- .../testsuite/javalib/lang/ObjectJSTest.scala | 2 -- .../testsuite/javalib/lang/SystemJSTest.scala | 33 +++++++------------ .../testsuite/jsinterop/DynamicTest.scala | 2 -- .../testsuite/jsinterop/IterableTest.scala | 5 --- .../testsuite/jsinterop/JSSymbolTest.scala | 6 ---- .../scalajs/testsuite/jsinterop/MapTest.scala | 7 ---- .../ModulesWithGlobalFallbackTest.scala | 2 -- .../scalajs/testsuite/jsinterop/SetTest.scala | 8 ----- .../testsuite/jsinterop/SymbolTest.scala | 7 ---- .../testsuite/library/LinkingInfoTest.scala | 4 +-- .../testsuite/library/ObjectTest.scala | 8 ----- .../testsuite/library/WrappedMapTest.scala | 7 ---- .../testsuite/library/WrappedSetTest.scala | 8 ----- .../niobuffer/ByteBufferJSTest.scala | 6 ---- .../niobuffer/CharBufferJSTest.scala | 26 --------------- .../niobuffer/DoubleBufferJSTest.scala | 26 --------------- .../niobuffer/FloatBufferJSTest.scala | 26 --------------- .../testsuite/niobuffer/IntBufferJSTest.scala | 26 --------------- .../niobuffer/LongBufferJSTest.scala | 24 -------------- .../niobuffer/ShortBufferJSTest.scala | 26 --------------- .../niobuffer/SupportsTypedArrays.scala | 24 -------------- .../typedarray/ArrayBufferTest.scala | 4 --- .../testsuite/typedarray/ArraysTest.scala | 3 -- .../testsuite/typedarray/DataViewTest.scala | 4 --- .../typedarray/TypedArrayConversionTest.scala | 3 -- .../testsuite/typedarray/TypedArrayTest.scala | 29 ---------------- .../scalajs/testsuite/utils/Requires.scala | 26 --------------- .../scalajs/testsuite/utils/Platform.scala | 2 -- .../testsuite/javalib/util/BitSetTest.scala | 2 -- .../javalib/util/regex/RegexEngineTest.scala | 21 ++---------- 34 files changed, 16 insertions(+), 390 deletions(-) delete mode 100644 test-suite/js/src/test/scala/org/scalajs/testsuite/niobuffer/SupportsTypedArrays.scala delete mode 100644 test-suite/js/src/test/scala/org/scalajs/testsuite/utils/Requires.scala 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 f809bbeaf7..eb8486b61f 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 @@ -39,18 +39,6 @@ object Platform { /** The assumed ECMAScript version. */ def assumedESVersion: Int = BuildInfo.esVersion - /** Convenience for `assumedESVersion >= ESVersion.ES2015`. */ - def assumeES2015: Boolean = assumedESVersion >= ESVersion.ES2015 - - def jsSymbols: Boolean = - assumeES2015 || js.typeOf(js.Dynamic.global.Symbol) != "undefined" - - def typedArrays: Boolean = - assumeES2015 || js.typeOf(js.Dynamic.global.Int32Array) != "undefined" - - def jsMaps: Boolean = - assumeES2015 || js.typeOf(js.Dynamic.global.Map) != "undefined" - def jsBigInts: Boolean = assumedESVersion >= ESVersion.ES2020 || js.typeOf(js.Dynamic.global.BigInt) != "undefined" @@ -83,11 +71,6 @@ object Platform { def hasCompliantModuleInit: Boolean = BuildInfo.compliantModuleInit - def hasDirectBuffers: Boolean = typedArrays - - def regexSupportsUnicodeCase: Boolean = - assumedESVersion >= ESVersion.ES2015 - def regexSupportsUnicodeCharacterClasses: Boolean = assumedESVersion >= ESVersion.ES2018 diff --git a/test-suite/js/src/test/require-multi-modules/org/scalajs/testsuite/jsinterop/SJSDynamicImportTest.scala b/test-suite/js/src/test/require-multi-modules/org/scalajs/testsuite/jsinterop/SJSDynamicImportTest.scala index f1ea0425b7..88530739ef 100644 --- a/test-suite/js/src/test/require-multi-modules/org/scalajs/testsuite/jsinterop/SJSDynamicImportTest.scala +++ b/test-suite/js/src/test/require-multi-modules/org/scalajs/testsuite/jsinterop/SJSDynamicImportTest.scala @@ -25,14 +25,6 @@ import org.scalajs.junit.async._ import org.scalajs.testsuite.utils.Platform._ -object SJSDynamicImportTest { - @BeforeClass - def assumeRuntimeSupportsPromise(): Unit = { - assumeTrue("Requires Promises", - assumeES2015 || js.typeOf(js.Dynamic.global.Promise) != "undefined") - } -} - class SJSDynamicImportTest { import scala.concurrent.ExecutionContext.Implicits.global diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/FloatJSTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/FloatJSTest.scala index 054f2bac59..27df279bb6 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/FloatJSTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/FloatJSTest.scala @@ -15,8 +15,6 @@ package org.scalajs.testsuite.compiler import org.junit.Test import org.junit.Assert._ -import org.scalajs.testsuite.utils.Requires - class FloatJSTest { @noinline def froundNotInlined(x: Double): Float = diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/InteroperabilityTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/InteroperabilityTest.scala index d69dd148f0..e263cd8272 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/InteroperabilityTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/InteroperabilityTest.scala @@ -355,8 +355,6 @@ class InteroperabilityTest { } @Test def callJSConstructorsWithVarArgsWhenConstructIsRequired_Issue4362(): Unit = { - assumeTrue("requires the spread operator", assumeES2015) - @noinline def args(): Seq[Any] = Seq(1234) val dateObj = new InteroperabilityTestJSDateWithVarArgsConstructor(args(): _*) diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/javalib/lang/ObjectJSTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/javalib/lang/ObjectJSTest.scala index f9ecc1dc01..6d6a36e073 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/javalib/lang/ObjectJSTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/javalib/lang/ObjectJSTest.scala @@ -56,8 +56,6 @@ class ObjectJSTest { * `SystemJSTest.identityHashCodeOfSymbols()`. */ - assumeTrue("requires JS symbols", Platform.jsSymbols) - @noinline def test(hash: Int, x: js.Symbol): Unit = assertEquals(hash, x.hashCode()) diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/javalib/lang/SystemJSTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/javalib/lang/SystemJSTest.scala index b57f6f25ce..40ae72cefa 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/javalib/lang/SystemJSTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/javalib/lang/SystemJSTest.scala @@ -24,25 +24,18 @@ import org.junit.Assume._ class SystemJSTest { @Test def identityHashCodeForJSObjects(): Unit = { - if (Platform.assumeES2015 || js.typeOf(js.Dynamic.global.WeakMap) != "undefined") { - /* This test is more restrictive than the spec, but we know our - * implementation will always pass the test. - */ - val x1 = new js.Object - val x2 = new js.Object - val x1FirstHash = x1.hashCode() - assertEquals(x1FirstHash, x1.hashCode()) - assertNotEquals(x1.hashCode(), x2.hashCode()) - assertEquals(x1FirstHash, x1.hashCode()) - - assertEquals(x1FirstHash, System.identityHashCode(x1)) - assertEquals(x2.hashCode(), System.identityHashCode(x2)) - } else { - val x1 = new js.Object - val x1FirstHash = x1.hashCode() - assertEquals(x1FirstHash, x1.hashCode()) - assertEquals(x1FirstHash, System.identityHashCode(x1)) - } + /* This test is more restrictive than the spec, but we know our + * implementation will always pass the test. + */ + val x1 = new js.Object + val x2 = new js.Object + val x1FirstHash = x1.hashCode() + assertEquals(x1FirstHash, x1.hashCode()) + assertNotEquals(x1.hashCode(), x2.hashCode()) + assertEquals(x1FirstHash, x1.hashCode()) + + assertEquals(x1FirstHash, System.identityHashCode(x1)) + assertEquals(x2.hashCode(), System.identityHashCode(x2)) } @Test def identityHashCodeOfValuesImplementedAsJSPrimitives(): Unit = { @@ -111,8 +104,6 @@ class SystemJSTest { * `ObjectJSTest.hashCodeOfSymbols()`. */ - assumeTrue("requires JS symbols", Platform.jsSymbols) - @noinline def test(hash: Int, x: js.Symbol): Unit = assertEquals(hash, System.identityHashCode(x)) diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/DynamicTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/DynamicTest.scala index bd23946f2e..0300d8f3c2 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/DynamicTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/DynamicTest.scala @@ -80,8 +80,6 @@ class DynamicTest { } @Test def newInstanceOfWithVarargsWhenConstructIsRequired_Issue4362(): Unit = { - assumeTrue("requires the spread operator", assumeES2015) - @noinline def args(): Seq[js.Any] = Seq(1234) val dateObj = js.Dynamic.newInstance(js.constructorOf[js.Date])(args(): _*) diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/IterableTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/IterableTest.scala index bc3db06d38..e70b09c3f6 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/IterableTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/IterableTest.scala @@ -20,11 +20,6 @@ import org.junit.Assume._ import org.junit.{BeforeClass, Test} object IterableTest { - @BeforeClass def assumeSymbolsAreSupported(): Unit = { - assumeTrue("Assuming JavaScript symbols are supported", - org.scalajs.testsuite.utils.Platform.jsSymbols) - } - private class CounterIterable(val max: Int) extends js.Iterable[Int] { @JSName(js.Symbol.iterator) def jsIterator(): js.Iterator[Int] = new CounterIterator(max) diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/JSSymbolTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/JSSymbolTest.scala index 4d092be096..3fc59f299e 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/JSSymbolTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/JSSymbolTest.scala @@ -273,12 +273,6 @@ class JSSymbolTest { object JSSymbolTest { - @BeforeClass - def beforeClass(): Unit = { - assumeTrue("Assuming JavaScript symbols are supported", - org.scalajs.testsuite.utils.Platform.jsSymbols) - } - /* These need to be lazy vals, so that they do not blow up if there is no * symbol support at all. */ diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/MapTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/MapTest.scala index e4fedf17c4..d1414b285c 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/MapTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/MapTest.scala @@ -21,13 +21,6 @@ import scala.scalajs.js import org.scalajs.testsuite.utils.AssertThrows.assertThrows import org.scalajs.testsuite.utils.Platform._ -object MapTest { - @BeforeClass - def assumeRuntimeSupportsMap(): Unit = { - assumeTrue("Requires js.Map support", jsMaps) - } -} - class MapTest { // scala.scalajs.js.Map diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/ModulesWithGlobalFallbackTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/ModulesWithGlobalFallbackTest.scala index f6d7c69089..0eabad9f8f 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/ModulesWithGlobalFallbackTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/ModulesWithGlobalFallbackTest.scala @@ -125,8 +125,6 @@ object ModulesWithGlobalFallbackTest { @BeforeClass def beforeClass(): Unit = { - assumeTrue("Assuming that Typed Arrays are supported", typedArrays) - if (isNoModule) { val global = org.scalajs.testsuite.utils.JSUtils.globalObject global.ModulesWithGlobalFallbackTest_Module = ModuleFallbackImpl diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/SetTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/SetTest.scala index 174a4cfe09..b864e7606f 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/SetTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/SetTest.scala @@ -20,14 +20,6 @@ import scala.scalajs.js import org.scalajs.testsuite.utils.Platform._ -object SetTest { - @BeforeClass - def assumeRuntimeSupportsSet(): Unit = { - assumeTrue("Requires js.Set support", - assumeES2015 || js.typeOf(js.Dynamic.global.Set) != "undefined") - } -} - class SetTest { // scala.scalajs.js.Set diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/SymbolTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/SymbolTest.scala index 4c754d543d..11ec6e0b8c 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/SymbolTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/SymbolTest.scala @@ -18,13 +18,6 @@ import org.junit.Assert._ import org.junit.Assume._ import org.junit.{BeforeClass, Test} -object SymbolTest { - @BeforeClass def assumeSymbolsAreSupported(): Unit = { - assumeTrue("Assuming JavaScript symbols are supported", - org.scalajs.testsuite.utils.Platform.jsSymbols) - } -} - class SymbolTest { val namedSymbol = js.Symbol.forKey("namedsym") 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 ce13936fa3..5f800d4537 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 @@ -32,7 +32,7 @@ class LinkingInfoTest { assertEquals(Platform.assumedESVersion, LinkingInfo.esVersion) @Test def assumingES6(): Unit = - assertEquals(Platform.assumedESVersion >= ESVersion.ES2015, LinkingInfo.assumingES6) + assertEquals(true, LinkingInfo.assumingES6) @Test def useECMAScript2015Semantics(): Unit = assertEquals(true, LinkingInfo.useECMAScript2015Semantics) @@ -56,7 +56,7 @@ class LinkingInfoTest { val linkingInfo = scala.scalajs.runtime.linkingInfo assertEquals(Platform.isInProductionMode, linkingInfo.productionMode) assertEquals(Platform.assumedESVersion, linkingInfo.esVersion) - assertEquals(Platform.assumedESVersion >= ESVersion.ES2015, linkingInfo.assumingES6) + assertEquals(true, linkingInfo.assumingES6) assertEquals(Platform.executingInWebAssembly, linkingInfo.isWebAssembly) assertEquals(Platform.assumedESVersion, linkingInfo.esVersion) } diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/library/ObjectTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/library/ObjectTest.scala index 6e86add99f..1906fcb923 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/library/ObjectTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/library/ObjectTest.scala @@ -29,8 +29,6 @@ class ObjectTest { private lazy val symB = js.Symbol.forKey("b") @Test def getOwnPropertySymbols(): Unit = { - assumeTrue("requires Symbols", jsSymbols) - val obj = (new js.Object()).asInstanceOf[ObjectCreator] obj(symA) = "localSymbol" obj(symB) = "globalSymbol" @@ -39,10 +37,6 @@ class ObjectTest { } @Test def is(): Unit = { - assumeTrue("requires Object.is", - assumedESVersion >= ESVersion.ES2015 || - js.typeOf(js.Dynamic.global.Object.is) != "undefined") - val a = new js.Object() assertTrue(js.Object.is(a, a)) assertTrue(js.Object.is(Double.NaN, Double.NaN)) @@ -113,8 +107,6 @@ class ObjectTest { assumedESVersion >= ESVersion.ES2020 || js.typeOf(js.Dynamic.global.Object.fromEntries) != "undefined") - assumeTrue("requires js.Map", jsMaps) - val map = js.Map("a" -> 42, "b" -> "foo") val obj = js.Object.fromEntries(map) assertEquals(42, obj("a")) diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/library/WrappedMapTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/library/WrappedMapTest.scala index 898c5b15da..89c2f604dc 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/library/WrappedMapTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/library/WrappedMapTest.scala @@ -22,13 +22,6 @@ import scala.scalajs.js import org.scalajs.testsuite.utils.Platform._ -object WrappedMapTest { - @BeforeClass - def assumeRuntimeSupportsMap(): Unit = { - assumeTrue("Requires js.Map support", jsMaps) - } -} - class WrappedMapTest { // Methods we actually implement diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/library/WrappedSetTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/library/WrappedSetTest.scala index 14b0213466..39d90bba06 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/library/WrappedSetTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/library/WrappedSetTest.scala @@ -22,14 +22,6 @@ import scala.scalajs.js import org.scalajs.testsuite.utils.Platform._ -object WrappedSetTest { - @BeforeClass - def assumeRuntimeSupportsSet(): Unit = { - assumeTrue("Requires js.Set support", - assumeES2015 || js.typeOf(js.Dynamic.global.Set) != "undefined") - } -} - class WrappedSetTest { // Methods we actually implement diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/niobuffer/ByteBufferJSTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/niobuffer/ByteBufferJSTest.scala index a3001e4a13..74d960282c 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/niobuffer/ByteBufferJSTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/niobuffer/ByteBufferJSTest.scala @@ -14,22 +14,16 @@ package org.scalajs.testsuite.niobuffer import org.scalajs.testsuite.niobuffer.BufferFactory.ByteBufferFactory -object AllocDirectByteBufferJSTest extends SupportsTypedArrays - class AllocDirectByteBufferJSTest extends ByteBufferTest { val factory: ByteBufferFactory = new ByteBufferFactories.AllocDirectByteBufferFactory } -object SlicedAllocDirectByteBufferJSTest extends SupportsTypedArrays - class SlicedAllocDirectByteBufferJSTest extends ByteBufferTest { val factory: ByteBufferFactory = new ByteBufferFactories.SlicedAllocDirectByteBufferFactory } -object WrappedTypedArrayByteBufferJSTest extends SupportsTypedArrays - class WrappedTypedArrayByteBufferJSTest extends ByteBufferTest { val factory: ByteBufferFactory = new ByteBufferJSFactories.WrappedTypedArrayByteBufferFactory diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/niobuffer/CharBufferJSTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/niobuffer/CharBufferJSTest.scala index 7248b7a0e9..5f98a85ad7 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/niobuffer/CharBufferJSTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/niobuffer/CharBufferJSTest.scala @@ -23,8 +23,6 @@ import js.JSConverters._ import org.scalajs.testsuite.niobuffer.ByteBufferFactories._ import org.scalajs.testsuite.niobuffer.ByteBufferJSFactories._ -object WrappedTypedArrayCharBufferJSTest extends SupportsTypedArrays - class WrappedTypedArrayCharBufferJSTest extends CharBufferTest { val factory: CharBufferFactory = new WrappedTypedArrayCharBufferJSFactory @@ -38,64 +36,40 @@ class WrappedTypedArrayCharBufferJSTest extends CharBufferTest { // Char views of byte buffers -object CharViewOfAllocDirectByteBufferBigEndianJSTest extends SupportsTypedArrays - class CharViewOfAllocDirectByteBufferBigEndianJSTest extends CharViewOfByteBufferTest(new AllocDirectByteBufferFactory, ByteOrder.BIG_ENDIAN) -object CharViewOfSlicedAllocDirectByteBufferBigEndianJSTest extends SupportsTypedArrays - class CharViewOfSlicedAllocDirectByteBufferBigEndianJSTest extends CharViewOfByteBufferTest(new SlicedAllocDirectByteBufferFactory, ByteOrder.BIG_ENDIAN) -object CharViewOfWrappedTypedArrayByteBufferBigEndianJSTest extends SupportsTypedArrays - class CharViewOfWrappedTypedArrayByteBufferBigEndianJSTest extends CharViewOfByteBufferTest(new WrappedTypedArrayByteBufferFactory, ByteOrder.BIG_ENDIAN) -object CharViewOfAllocDirectByteBufferLittleEndianJSTest extends SupportsTypedArrays - class CharViewOfAllocDirectByteBufferLittleEndianJSTest extends CharViewOfByteBufferTest(new AllocDirectByteBufferFactory, ByteOrder.LITTLE_ENDIAN) -object CharViewOfSlicedAllocDirectByteBufferLittleEndianJSTest extends SupportsTypedArrays - class CharViewOfSlicedAllocDirectByteBufferLittleEndianJSTest extends CharViewOfByteBufferTest(new SlicedAllocDirectByteBufferFactory, ByteOrder.LITTLE_ENDIAN) -object CharViewOfWrappedTypedArrayByteBufferLittleEndianJSTest extends SupportsTypedArrays - class CharViewOfWrappedTypedArrayByteBufferLittleEndianJSTest extends CharViewOfByteBufferTest(new WrappedTypedArrayByteBufferFactory, ByteOrder.LITTLE_ENDIAN) // Read only Char views of byte buffers -object ReadOnlyCharViewOfAllocDirectByteBufferBigEndianJSTest extends SupportsTypedArrays - class ReadOnlyCharViewOfAllocDirectByteBufferBigEndianJSTest extends ReadOnlyCharViewOfByteBufferTest(new AllocDirectByteBufferFactory, ByteOrder.BIG_ENDIAN) -object ReadOnlyCharViewOfSlicedAllocDirectByteBufferBigEndianJSTest extends SupportsTypedArrays - class ReadOnlyCharViewOfSlicedAllocDirectByteBufferBigEndianJSTest extends ReadOnlyCharViewOfByteBufferTest(new SlicedAllocDirectByteBufferFactory, ByteOrder.BIG_ENDIAN) -object ReadOnlyCharViewOfWrappedTypedArrayByteBufferBigEndianJSTest extends SupportsTypedArrays - class ReadOnlyCharViewOfWrappedTypedArrayByteBufferBigEndianJSTest extends ReadOnlyCharViewOfByteBufferTest(new WrappedTypedArrayByteBufferFactory, ByteOrder.BIG_ENDIAN) -object ReadOnlyCharViewOfAllocDirectByteBufferLittleEndianJSTest extends SupportsTypedArrays - class ReadOnlyCharViewOfAllocDirectByteBufferLittleEndianJSTest extends ReadOnlyCharViewOfByteBufferTest(new AllocDirectByteBufferFactory, ByteOrder.LITTLE_ENDIAN) -object ReadOnlyCharViewOfSlicedAllocDirectByteBufferLittleEndianJSTest extends SupportsTypedArrays - class ReadOnlyCharViewOfSlicedAllocDirectByteBufferLittleEndianJSTest extends ReadOnlyCharViewOfByteBufferTest(new SlicedAllocDirectByteBufferFactory, ByteOrder.LITTLE_ENDIAN) -object ReadOnlyCharViewOfWrappedTypedArrayByteBufferLittleEndianJSTest extends SupportsTypedArrays - class ReadOnlyCharViewOfWrappedTypedArrayByteBufferLittleEndianJSTest extends ReadOnlyCharViewOfByteBufferTest(new WrappedTypedArrayByteBufferFactory, ByteOrder.LITTLE_ENDIAN) diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/niobuffer/DoubleBufferJSTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/niobuffer/DoubleBufferJSTest.scala index 489e4f36c8..8e9b489788 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/niobuffer/DoubleBufferJSTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/niobuffer/DoubleBufferJSTest.scala @@ -23,8 +23,6 @@ import js.JSConverters._ import org.scalajs.testsuite.niobuffer.ByteBufferFactories._ import org.scalajs.testsuite.niobuffer.ByteBufferJSFactories._ -object WrappedTypedArrayDoubleBufferJSTest extends SupportsTypedArrays - class WrappedTypedArrayDoubleBufferJSTest extends DoubleBufferTest { val factory: DoubleBufferFactory = new WrappedTypedArrayDoubleBufferJSFactory @@ -38,64 +36,40 @@ class WrappedTypedArrayDoubleBufferJSTest extends DoubleBufferTest { // Double views of byte buffers -object DoubleViewOfAllocDirectByteBufferBigEndianJSTest extends SupportsTypedArrays - class DoubleViewOfAllocDirectByteBufferBigEndianJSTest extends DoubleViewOfByteBufferTest(new AllocDirectByteBufferFactory, ByteOrder.BIG_ENDIAN) -object DoubleViewOfSlicedAllocDirectByteBufferBigEndianJSTest extends SupportsTypedArrays - class DoubleViewOfSlicedAllocDirectByteBufferBigEndianJSTest extends DoubleViewOfByteBufferTest(new SlicedAllocDirectByteBufferFactory, ByteOrder.BIG_ENDIAN) -object DoubleViewOfWrappedTypedArrayByteBufferBigEndianJSTest extends SupportsTypedArrays - class DoubleViewOfWrappedTypedArrayByteBufferBigEndianJSTest extends DoubleViewOfByteBufferTest(new WrappedTypedArrayByteBufferFactory, ByteOrder.BIG_ENDIAN) -object DoubleViewOfAllocDirectByteBufferLittleEndianJSTest extends SupportsTypedArrays - class DoubleViewOfAllocDirectByteBufferLittleEndianJSTest extends DoubleViewOfByteBufferTest(new AllocDirectByteBufferFactory, ByteOrder.LITTLE_ENDIAN) -object DoubleViewOfSlicedAllocDirectByteBufferLittleEndianJSTest extends SupportsTypedArrays - class DoubleViewOfSlicedAllocDirectByteBufferLittleEndianJSTest extends DoubleViewOfByteBufferTest(new SlicedAllocDirectByteBufferFactory, ByteOrder.LITTLE_ENDIAN) -object DoubleViewOfWrappedTypedArrayByteBufferLittleEndianJSTest extends SupportsTypedArrays - class DoubleViewOfWrappedTypedArrayByteBufferLittleEndianJSTest extends DoubleViewOfByteBufferTest(new WrappedTypedArrayByteBufferFactory, ByteOrder.LITTLE_ENDIAN) // Read only Double views of byte buffers -object ReadOnlyDoubleViewOfAllocDirectByteBufferBigEndianJSTest extends SupportsTypedArrays - class ReadOnlyDoubleViewOfAllocDirectByteBufferBigEndianJSTest extends ReadOnlyDoubleViewOfByteBufferTest(new AllocDirectByteBufferFactory, ByteOrder.BIG_ENDIAN) -object ReadOnlyDoubleViewOfSlicedAllocDirectByteBufferBigEndianJSTest extends SupportsTypedArrays - class ReadOnlyDoubleViewOfSlicedAllocDirectByteBufferBigEndianJSTest extends ReadOnlyDoubleViewOfByteBufferTest(new SlicedAllocDirectByteBufferFactory, ByteOrder.BIG_ENDIAN) -object ReadOnlyDoubleViewOfWrappedTypedArrayByteBufferBigEndianJSTest extends SupportsTypedArrays - class ReadOnlyDoubleViewOfWrappedTypedArrayByteBufferBigEndianJSTest extends ReadOnlyDoubleViewOfByteBufferTest(new WrappedTypedArrayByteBufferFactory, ByteOrder.BIG_ENDIAN) -object ReadOnlyDoubleViewOfAllocDirectByteBufferLittleEndianJSTest extends SupportsTypedArrays - class ReadOnlyDoubleViewOfAllocDirectByteBufferLittleEndianJSTest extends ReadOnlyDoubleViewOfByteBufferTest(new AllocDirectByteBufferFactory, ByteOrder.LITTLE_ENDIAN) -object ReadOnlyDoubleViewOfSlicedAllocDirectByteBufferLittleEndianJSTest extends SupportsTypedArrays - class ReadOnlyDoubleViewOfSlicedAllocDirectByteBufferLittleEndianJSTest extends ReadOnlyDoubleViewOfByteBufferTest(new SlicedAllocDirectByteBufferFactory, ByteOrder.LITTLE_ENDIAN) -object ReadOnlyDoubleViewOfWrappedTypedArrayByteBufferLittleEndianJSTest extends SupportsTypedArrays - class ReadOnlyDoubleViewOfWrappedTypedArrayByteBufferLittleEndianJSTest extends ReadOnlyDoubleViewOfByteBufferTest(new WrappedTypedArrayByteBufferFactory, ByteOrder.LITTLE_ENDIAN) diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/niobuffer/FloatBufferJSTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/niobuffer/FloatBufferJSTest.scala index 7c41e00ab5..b3fa59381b 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/niobuffer/FloatBufferJSTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/niobuffer/FloatBufferJSTest.scala @@ -23,8 +23,6 @@ import js.JSConverters._ import org.scalajs.testsuite.niobuffer.ByteBufferFactories._ import org.scalajs.testsuite.niobuffer.ByteBufferJSFactories._ -object WrappedTypedArrayFloatBufferJSTest extends SupportsTypedArrays - class WrappedTypedArrayFloatBufferJSTest extends FloatBufferTest { val factory: FloatBufferFactory = new WrappedTypedArrayFloatBufferJSFactory @@ -38,64 +36,40 @@ class WrappedTypedArrayFloatBufferJSTest extends FloatBufferTest { // Float views of byte buffers -object FloatViewOfAllocDirectByteBufferBigEndianJSTest extends SupportsTypedArrays - class FloatViewOfAllocDirectByteBufferBigEndianJSTest extends FloatViewOfByteBufferTest(new AllocDirectByteBufferFactory, ByteOrder.BIG_ENDIAN) -object FloatViewOfSlicedAllocDirectByteBufferBigEndianJSTest extends SupportsTypedArrays - class FloatViewOfSlicedAllocDirectByteBufferBigEndianJSTest extends FloatViewOfByteBufferTest(new SlicedAllocDirectByteBufferFactory, ByteOrder.BIG_ENDIAN) -object FloatViewOfWrappedTypedArrayByteBufferBigEndianJSTest extends SupportsTypedArrays - class FloatViewOfWrappedTypedArrayByteBufferBigEndianJSTest extends FloatViewOfByteBufferTest(new WrappedTypedArrayByteBufferFactory, ByteOrder.BIG_ENDIAN) -object FloatViewOfAllocDirectByteBufferLittleEndianJSTest extends SupportsTypedArrays - class FloatViewOfAllocDirectByteBufferLittleEndianJSTest extends FloatViewOfByteBufferTest(new AllocDirectByteBufferFactory, ByteOrder.LITTLE_ENDIAN) -object FloatViewOfSlicedAllocDirectByteBufferLittleEndianJSTest extends SupportsTypedArrays - class FloatViewOfSlicedAllocDirectByteBufferLittleEndianJSTest extends FloatViewOfByteBufferTest(new SlicedAllocDirectByteBufferFactory, ByteOrder.LITTLE_ENDIAN) -object FloatViewOfWrappedTypedArrayByteBufferLittleEndianJSTest extends SupportsTypedArrays - class FloatViewOfWrappedTypedArrayByteBufferLittleEndianJSTest extends FloatViewOfByteBufferTest(new WrappedTypedArrayByteBufferFactory, ByteOrder.LITTLE_ENDIAN) // Read only Float views of byte buffers -object ReadOnlyFloatViewOfAllocDirectByteBufferBigEndianJSTest extends SupportsTypedArrays - class ReadOnlyFloatViewOfAllocDirectByteBufferBigEndianJSTest extends ReadOnlyFloatViewOfByteBufferTest(new AllocDirectByteBufferFactory, ByteOrder.BIG_ENDIAN) -object ReadOnlyFloatViewOfSlicedAllocDirectByteBufferBigEndianJSTest extends SupportsTypedArrays - class ReadOnlyFloatViewOfSlicedAllocDirectByteBufferBigEndianJSTest extends ReadOnlyFloatViewOfByteBufferTest(new SlicedAllocDirectByteBufferFactory, ByteOrder.BIG_ENDIAN) -object ReadOnlyFloatViewOfWrappedTypedArrayByteBufferBigEndianJSTest extends SupportsTypedArrays - class ReadOnlyFloatViewOfWrappedTypedArrayByteBufferBigEndianJSTest extends ReadOnlyFloatViewOfByteBufferTest(new WrappedTypedArrayByteBufferFactory, ByteOrder.BIG_ENDIAN) -object ReadOnlyFloatViewOfAllocDirectByteBufferLittleEndianJSTest extends SupportsTypedArrays - class ReadOnlyFloatViewOfAllocDirectByteBufferLittleEndianJSTest extends ReadOnlyFloatViewOfByteBufferTest(new AllocDirectByteBufferFactory, ByteOrder.LITTLE_ENDIAN) -object ReadOnlyFloatViewOfSlicedAllocDirectByteBufferLittleEndianJSTest extends SupportsTypedArrays - class ReadOnlyFloatViewOfSlicedAllocDirectByteBufferLittleEndianJSTest extends ReadOnlyFloatViewOfByteBufferTest(new SlicedAllocDirectByteBufferFactory, ByteOrder.LITTLE_ENDIAN) -object ReadOnlyFloatViewOfWrappedTypedArrayByteBufferLittleEndianJSTest extends SupportsTypedArrays - class ReadOnlyFloatViewOfWrappedTypedArrayByteBufferLittleEndianJSTest extends ReadOnlyFloatViewOfByteBufferTest(new WrappedTypedArrayByteBufferFactory, ByteOrder.LITTLE_ENDIAN) diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/niobuffer/IntBufferJSTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/niobuffer/IntBufferJSTest.scala index 656902a75f..e53bdc5a6a 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/niobuffer/IntBufferJSTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/niobuffer/IntBufferJSTest.scala @@ -23,8 +23,6 @@ import js.JSConverters._ import org.scalajs.testsuite.niobuffer.ByteBufferFactories._ import org.scalajs.testsuite.niobuffer.ByteBufferJSFactories._ -object WrappedTypedArrayIntBufferJSTest extends SupportsTypedArrays - class WrappedTypedArrayIntBufferJSTest extends IntBufferTest { val factory: IntBufferFactory = new WrappedTypedArrayIntBufferJSFactory @@ -38,64 +36,40 @@ class WrappedTypedArrayIntBufferJSTest extends IntBufferTest { // Int views of byte buffers -object IntViewOfAllocDirectByteBufferBigEndianJSTest extends SupportsTypedArrays - class IntViewOfAllocDirectByteBufferBigEndianJSTest extends IntViewOfByteBufferTest(new AllocDirectByteBufferFactory, ByteOrder.BIG_ENDIAN) -object IntViewOfSlicedAllocDirectByteBufferBigEndianJSTest extends SupportsTypedArrays - class IntViewOfSlicedAllocDirectByteBufferBigEndianJSTest extends IntViewOfByteBufferTest(new SlicedAllocDirectByteBufferFactory, ByteOrder.BIG_ENDIAN) -object IntViewOfWrappedTypedArrayByteBufferBigEndianJSTest extends SupportsTypedArrays - class IntViewOfWrappedTypedArrayByteBufferBigEndianJSTest extends IntViewOfByteBufferTest(new WrappedTypedArrayByteBufferFactory, ByteOrder.BIG_ENDIAN) -object IntViewOfAllocDirectByteBufferLittleEndianJSTest extends SupportsTypedArrays - class IntViewOfAllocDirectByteBufferLittleEndianJSTest extends IntViewOfByteBufferTest(new AllocDirectByteBufferFactory, ByteOrder.LITTLE_ENDIAN) -object IntViewOfSlicedAllocDirectByteBufferLittleEndianJSTest extends SupportsTypedArrays - class IntViewOfSlicedAllocDirectByteBufferLittleEndianJSTest extends IntViewOfByteBufferTest(new SlicedAllocDirectByteBufferFactory, ByteOrder.LITTLE_ENDIAN) -object IntViewOfWrappedTypedArrayByteBufferLittleEndianJSTest extends SupportsTypedArrays - class IntViewOfWrappedTypedArrayByteBufferLittleEndianJSTest extends IntViewOfByteBufferTest(new WrappedTypedArrayByteBufferFactory, ByteOrder.LITTLE_ENDIAN) // Read only Int views of byte buffers -object ReadOnlyIntViewOfAllocDirectByteBufferBigEndianJSTest extends SupportsTypedArrays - class ReadOnlyIntViewOfAllocDirectByteBufferBigEndianJSTest extends ReadOnlyIntViewOfByteBufferTest(new AllocDirectByteBufferFactory, ByteOrder.BIG_ENDIAN) -object ReadOnlyIntViewOfSlicedAllocDirectByteBufferBigEndianJSTest extends SupportsTypedArrays - class ReadOnlyIntViewOfSlicedAllocDirectByteBufferBigEndianJSTest extends ReadOnlyIntViewOfByteBufferTest(new SlicedAllocDirectByteBufferFactory, ByteOrder.BIG_ENDIAN) -object ReadOnlyIntViewOfWrappedTypedArrayByteBufferBigEndianJSTest extends SupportsTypedArrays - class ReadOnlyIntViewOfWrappedTypedArrayByteBufferBigEndianJSTest extends ReadOnlyIntViewOfByteBufferTest(new WrappedTypedArrayByteBufferFactory, ByteOrder.BIG_ENDIAN) -object ReadOnlyIntViewOfAllocDirectByteBufferLittleEndianJSTest extends SupportsTypedArrays - class ReadOnlyIntViewOfAllocDirectByteBufferLittleEndianJSTest extends ReadOnlyIntViewOfByteBufferTest(new AllocDirectByteBufferFactory, ByteOrder.LITTLE_ENDIAN) -object ReadOnlyIntViewOfSlicedAllocDirectByteBufferLittleEndianJSTest extends SupportsTypedArrays - class ReadOnlyIntViewOfSlicedAllocDirectByteBufferLittleEndianJSTest extends ReadOnlyIntViewOfByteBufferTest(new SlicedAllocDirectByteBufferFactory, ByteOrder.LITTLE_ENDIAN) -object ReadOnlyIntViewOfWrappedTypedArrayByteBufferLittleEndianJSTest extends SupportsTypedArrays - class ReadOnlyIntViewOfWrappedTypedArrayByteBufferLittleEndianJSTest extends ReadOnlyIntViewOfByteBufferTest(new WrappedTypedArrayByteBufferFactory, ByteOrder.LITTLE_ENDIAN) diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/niobuffer/LongBufferJSTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/niobuffer/LongBufferJSTest.scala index 05a5ce0e1c..c78fc9c566 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/niobuffer/LongBufferJSTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/niobuffer/LongBufferJSTest.scala @@ -19,64 +19,40 @@ import org.scalajs.testsuite.niobuffer.ByteBufferJSFactories._ // Long views of byte buffers -object LongViewOfAllocDirectByteBufferBigEndianJSTest extends SupportsTypedArrays - class LongViewOfAllocDirectByteBufferBigEndianJSTest extends LongViewOfByteBufferTest(new AllocDirectByteBufferFactory, ByteOrder.BIG_ENDIAN) -object LongViewOfSlicedAllocDirectByteBufferBigEndianJSTest extends SupportsTypedArrays - class LongViewOfSlicedAllocDirectByteBufferBigEndianJSTest extends LongViewOfByteBufferTest(new SlicedAllocDirectByteBufferFactory, ByteOrder.BIG_ENDIAN) -object LongViewOfWrappedTypedArrayByteBufferBigEndianJSTest extends SupportsTypedArrays - class LongViewOfWrappedTypedArrayByteBufferBigEndianJSTest extends LongViewOfByteBufferTest(new WrappedTypedArrayByteBufferFactory, ByteOrder.BIG_ENDIAN) -object LongViewOfAllocDirectByteBufferLittleEndianJSTest extends SupportsTypedArrays - class LongViewOfAllocDirectByteBufferLittleEndianJSTest extends LongViewOfByteBufferTest(new AllocDirectByteBufferFactory, ByteOrder.LITTLE_ENDIAN) -object LongViewOfSlicedAllocDirectByteBufferLittleEndianJSTest extends SupportsTypedArrays - class LongViewOfSlicedAllocDirectByteBufferLittleEndianJSTest extends LongViewOfByteBufferTest(new SlicedAllocDirectByteBufferFactory, ByteOrder.LITTLE_ENDIAN) -object LongViewOfWrappedTypedArrayByteBufferLittleEndianJSTest extends SupportsTypedArrays - class LongViewOfWrappedTypedArrayByteBufferLittleEndianJSTest extends LongViewOfByteBufferTest(new WrappedTypedArrayByteBufferFactory, ByteOrder.LITTLE_ENDIAN) // Read only Long views of byte buffers -object ReadOnlyLongViewOfAllocDirectByteBufferBigEndianJSTest extends SupportsTypedArrays - class ReadOnlyLongViewOfAllocDirectByteBufferBigEndianJSTest extends ReadOnlyLongViewOfByteBufferTest(new AllocDirectByteBufferFactory, ByteOrder.BIG_ENDIAN) -object ReadOnlyLongViewOfSlicedAllocDirectByteBufferBigEndianJSTest extends SupportsTypedArrays - class ReadOnlyLongViewOfSlicedAllocDirectByteBufferBigEndianJSTest extends ReadOnlyLongViewOfByteBufferTest(new SlicedAllocDirectByteBufferFactory, ByteOrder.BIG_ENDIAN) -object ReadOnlyLongViewOfWrappedTypedArrayByteBufferBigEndianJSTest extends SupportsTypedArrays - class ReadOnlyLongViewOfWrappedTypedArrayByteBufferBigEndianJSTest extends ReadOnlyLongViewOfByteBufferTest(new WrappedTypedArrayByteBufferFactory, ByteOrder.BIG_ENDIAN) -object ReadOnlyLongViewOfAllocDirectByteBufferLittleEndianJSTest extends SupportsTypedArrays - class ReadOnlyLongViewOfAllocDirectByteBufferLittleEndianJSTest extends ReadOnlyLongViewOfByteBufferTest(new AllocDirectByteBufferFactory, ByteOrder.LITTLE_ENDIAN) -object ReadOnlyLongViewOfSlicedAllocDirectByteBufferLittleEndianJSTest extends SupportsTypedArrays - class ReadOnlyLongViewOfSlicedAllocDirectByteBufferLittleEndianJSTest extends ReadOnlyLongViewOfByteBufferTest(new SlicedAllocDirectByteBufferFactory, ByteOrder.LITTLE_ENDIAN) -object ReadOnlyLongViewOfWrappedTypedArrayByteBufferLittleEndianJSTest extends SupportsTypedArrays - class ReadOnlyLongViewOfWrappedTypedArrayByteBufferLittleEndianJSTest extends ReadOnlyLongViewOfByteBufferTest(new WrappedTypedArrayByteBufferFactory, ByteOrder.LITTLE_ENDIAN) diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/niobuffer/ShortBufferJSTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/niobuffer/ShortBufferJSTest.scala index e89c3b5648..b3de70a428 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/niobuffer/ShortBufferJSTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/niobuffer/ShortBufferJSTest.scala @@ -23,8 +23,6 @@ import js.JSConverters._ import org.scalajs.testsuite.niobuffer.ByteBufferFactories._ import org.scalajs.testsuite.niobuffer.ByteBufferJSFactories._ -object WrappedTypedArrayShortBufferJSTest extends SupportsTypedArrays - class WrappedTypedArrayShortBufferJSTest extends ShortBufferTest { val factory: ShortBufferFactory = new WrappedTypedArrayShortBufferJSFactory @@ -38,64 +36,40 @@ class WrappedTypedArrayShortBufferJSTest extends ShortBufferTest { // Short views of byte buffers -object ShortViewOfAllocDirectByteBufferBigEndianJSTest extends SupportsTypedArrays - class ShortViewOfAllocDirectByteBufferBigEndianJSTest extends ShortViewOfByteBufferTest(new AllocDirectByteBufferFactory, ByteOrder.BIG_ENDIAN) -object ShortViewOfSlicedAllocDirectByteBufferBigEndianJSTest extends SupportsTypedArrays - class ShortViewOfSlicedAllocDirectByteBufferBigEndianJSTest extends ShortViewOfByteBufferTest(new SlicedAllocDirectByteBufferFactory, ByteOrder.BIG_ENDIAN) -object ShortViewOfWrappedTypedArrayByteBufferBigEndianJSTest extends SupportsTypedArrays - class ShortViewOfWrappedTypedArrayByteBufferBigEndianJSTest extends ShortViewOfByteBufferTest(new WrappedTypedArrayByteBufferFactory, ByteOrder.BIG_ENDIAN) -object ShortViewOfAllocDirectByteBufferLittleEndianJSTest extends SupportsTypedArrays - class ShortViewOfAllocDirectByteBufferLittleEndianJSTest extends ShortViewOfByteBufferTest(new AllocDirectByteBufferFactory, ByteOrder.LITTLE_ENDIAN) -object ShortViewOfSlicedAllocDirectByteBufferLittleEndianJSTest extends SupportsTypedArrays - class ShortViewOfSlicedAllocDirectByteBufferLittleEndianJSTest extends ShortViewOfByteBufferTest(new SlicedAllocDirectByteBufferFactory, ByteOrder.LITTLE_ENDIAN) -object ShortViewOfWrappedTypedArrayByteBufferLittleEndianJSTest extends SupportsTypedArrays - class ShortViewOfWrappedTypedArrayByteBufferLittleEndianJSTest extends ShortViewOfByteBufferTest(new WrappedTypedArrayByteBufferFactory, ByteOrder.LITTLE_ENDIAN) // Read only Short views of byte buffers -object ReadOnlyShortViewOfAllocDirectByteBufferBigEndianJSTest extends SupportsTypedArrays - class ReadOnlyShortViewOfAllocDirectByteBufferBigEndianJSTest extends ReadOnlyShortViewOfByteBufferTest(new AllocDirectByteBufferFactory, ByteOrder.BIG_ENDIAN) -object ReadOnlyShortViewOfSlicedAllocDirectByteBufferBigEndianJSTest extends SupportsTypedArrays - class ReadOnlyShortViewOfSlicedAllocDirectByteBufferBigEndianJSTest extends ReadOnlyShortViewOfByteBufferTest(new SlicedAllocDirectByteBufferFactory, ByteOrder.BIG_ENDIAN) -object ReadOnlyShortViewOfWrappedTypedArrayByteBufferBigEndianJSTest extends SupportsTypedArrays - class ReadOnlyShortViewOfWrappedTypedArrayByteBufferBigEndianJSTest extends ReadOnlyShortViewOfByteBufferTest(new WrappedTypedArrayByteBufferFactory, ByteOrder.BIG_ENDIAN) -object ReadOnlyShortViewOfAllocDirectByteBufferLittleEndianJSTest extends SupportsTypedArrays - class ReadOnlyShortViewOfAllocDirectByteBufferLittleEndianJSTest extends ReadOnlyShortViewOfByteBufferTest(new AllocDirectByteBufferFactory, ByteOrder.LITTLE_ENDIAN) -object ReadOnlyShortViewOfSlicedAllocDirectByteBufferLittleEndianJSTest extends SupportsTypedArrays - class ReadOnlyShortViewOfSlicedAllocDirectByteBufferLittleEndianJSTest extends ReadOnlyShortViewOfByteBufferTest(new SlicedAllocDirectByteBufferFactory, ByteOrder.LITTLE_ENDIAN) -object ReadOnlyShortViewOfWrappedTypedArrayByteBufferLittleEndianJSTest extends SupportsTypedArrays - class ReadOnlyShortViewOfWrappedTypedArrayByteBufferLittleEndianJSTest extends ReadOnlyShortViewOfByteBufferTest(new WrappedTypedArrayByteBufferFactory, ByteOrder.LITTLE_ENDIAN) diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/niobuffer/SupportsTypedArrays.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/niobuffer/SupportsTypedArrays.scala deleted file mode 100644 index 1534d8897e..0000000000 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/niobuffer/SupportsTypedArrays.scala +++ /dev/null @@ -1,24 +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.niobuffer - -import org.junit.BeforeClass -import org.junit.Assume._ - -import org.scalajs.testsuite.utils.Platform - -trait SupportsTypedArrays { - @BeforeClass def assumeThatContextSupportsTypedByteArrays(): Unit = { - assumeTrue("Assumed typed arrays are supported", Platform.typedArrays) - } -} diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/typedarray/ArrayBufferTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/typedarray/ArrayBufferTest.scala index 0068e1b4ea..3953e76433 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/typedarray/ArrayBufferTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/typedarray/ArrayBufferTest.scala @@ -15,12 +15,8 @@ package org.scalajs.testsuite.typedarray import org.junit.Assert._ import org.junit.Test -import org.scalajs.testsuite.utils.Requires - import scala.scalajs.js.typedarray._ -object ArrayBufferTest extends Requires.TypedArray - class ArrayBufferTest { @Test def lengthConstructor(): Unit = { diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/typedarray/ArraysTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/typedarray/ArraysTest.scala index 2f8abd068a..ef5790d55d 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/typedarray/ArraysTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/typedarray/ArraysTest.scala @@ -18,9 +18,6 @@ import scala.scalajs.js.JSConverters._ import scala.reflect._ import org.scalajs.testsuite.javalib -import org.scalajs.testsuite.utils.Requires - -object ArraysTest extends Requires.TypedArray class ArraysTest extends javalib.util.ArraysTest { diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/typedarray/DataViewTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/typedarray/DataViewTest.scala index 68691c429f..77d8c268a5 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/typedarray/DataViewTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/typedarray/DataViewTest.scala @@ -15,13 +15,9 @@ package org.scalajs.testsuite.typedarray import org.junit.Assert._ import org.junit.Test -import org.scalajs.testsuite.utils.Requires - import scala.scalajs.js.typedarray._ import DataViewExt._ -object DataViewTest extends Requires.TypedArray - class DataViewTest { @Test def arrayBufferOnlyConstructor(): Unit = { diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/typedarray/TypedArrayConversionTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/typedarray/TypedArrayConversionTest.scala index 2488245ef2..0e0c986887 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/typedarray/TypedArrayConversionTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/typedarray/TypedArrayConversionTest.scala @@ -18,13 +18,10 @@ import org.junit.Assume._ import org.scalajs.testsuite.utils.AssertThrows.assertThrows import org.scalajs.testsuite.utils.Platform._ -import org.scalajs.testsuite.utils.Requires import scala.scalajs.js import js.typedarray._ -object TypedArrayConversionTest extends Requires.TypedArray - class TypedArrayConversionTest { val data = js.Array[Int](-1, 1, 2, 3, 4, 5, 6, 7, 8) diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/typedarray/TypedArrayTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/typedarray/TypedArrayTest.scala index 5478416db3..b5548577a1 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/typedarray/TypedArrayTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/typedarray/TypedArrayTest.scala @@ -15,7 +15,6 @@ package org.scalajs.testsuite.typedarray import org.junit.Assert._ import org.junit.Assume._ import org.junit.Test -import org.scalajs.testsuite.utils.Requires import scala.scalajs.js import js.typedarray._ @@ -250,9 +249,6 @@ trait TypedArrayTest[V, T <: TypedArray[V, T]] { } @Test def isIterable(): Unit = { - assumeTrue("Assuming JavaScript symbols are supported", - org.scalajs.testsuite.utils.Platform.jsSymbols) - import js.JSConverters._ val testData: List[Int] = (1 to 10).toList @@ -264,9 +260,6 @@ trait TypedArrayTest[V, T <: TypedArray[V, T]] { } @Test def fromIterable(): Unit = { - assumeTrue("Assuming JavaScript symbols are supported", - org.scalajs.testsuite.utils.Platform.jsSymbols) - import js.JSConverters._ val x = itCtor(Iterable(1, 2, 3, 4).map(intToV).toJSIterable) @@ -279,8 +272,6 @@ trait TypedArrayTest[V, T <: TypedArray[V, T]] { } } -object Int8ArrayTest extends Requires.TypedArray - class Int8ArrayTest extends TypedArrayTest[Byte, Int8Array] { def ofFn(items: Byte*): Int8Array = Int8Array.of(items: _*) def fromFn(iterable: js.Iterable[Byte]): Int8Array = Int8Array.from(iterable) @@ -299,8 +290,6 @@ class Int8ArrayTest extends TypedArrayTest[Byte, Int8Array] { def intToV(n: Int): Byte = n.toByte } -object Uint8ArrayTest extends Requires.TypedArray - class Uint8ArrayTest extends TypedArrayTest[Short, Uint8Array] { def ofFn(items: Short*): Uint8Array = Uint8Array.of(items: _*) def fromFn(iterable: js.Iterable[Short]): Uint8Array = Uint8Array.from(iterable) @@ -319,8 +308,6 @@ class Uint8ArrayTest extends TypedArrayTest[Short, Uint8Array] { def intToV(n: Int): Short = n.toShort } -object Uint8ClampedArrayTest extends Requires.TypedArray - class Uint8ClampedArrayTest extends TypedArrayTest[Int, Uint8ClampedArray] { def ofFn(items: Int*): Uint8ClampedArray = Uint8ClampedArray.of(items: _*) def fromFn(iterable: js.Iterable[Int]): Uint8ClampedArray = Uint8ClampedArray.from(iterable) @@ -340,8 +327,6 @@ class Uint8ClampedArrayTest extends TypedArrayTest[Int, Uint8ClampedArray] { def intToV(n: Int): Int = n } -object Int16ArrayTest extends Requires.TypedArray - class Int16ArrayTest extends TypedArrayTest[Short, Int16Array] { def ofFn(items: Short*): Int16Array = Int16Array.of(items: _*) def fromFn(iterable: js.Iterable[Short]): Int16Array = Int16Array.from(iterable) @@ -360,8 +345,6 @@ class Int16ArrayTest extends TypedArrayTest[Short, Int16Array] { def intToV(n: Int): Short = n.toShort } -object Uint16ArrayTest extends Requires.TypedArray - class Uint16ArrayTest extends TypedArrayTest[Int, Uint16Array] { def ofFn(items: Int*): Uint16Array = Uint16Array.of(items: _*) def fromFn(iterable: js.Iterable[Int]): Uint16Array = Uint16Array.from(iterable) @@ -380,8 +363,6 @@ class Uint16ArrayTest extends TypedArrayTest[Int, Uint16Array] { def intToV(n: Int): Int = n } -object Int32ArrayTest extends Requires.TypedArray - class Int32ArrayTest extends TypedArrayTest[Int, Int32Array] { def ofFn(items: Int*): Int32Array = Int32Array.of(items: _*) def fromFn(iterable: js.Iterable[Int]): Int32Array = Int32Array.from(iterable) @@ -400,8 +381,6 @@ class Int32ArrayTest extends TypedArrayTest[Int, Int32Array] { def intToV(n: Int): Int = n } -object Uint32ArrayTest extends Requires.TypedArray - class Uint32ArrayTest extends TypedArrayTest[Double, Uint32Array] { def ofFn(items: Double*): Uint32Array = Uint32Array.of(items: _*) def fromFn(iterable: js.Iterable[Double]): Uint32Array = Uint32Array.from(iterable) @@ -420,8 +399,6 @@ class Uint32ArrayTest extends TypedArrayTest[Double, Uint32Array] { def intToV(n: Int): Double = n.toDouble } -object Float32ArrayTest extends Requires.TypedArray - class Float32ArrayTest extends TypedArrayTest[Float, Float32Array] { def ofFn(items: Float*): Float32Array = Float32Array.of(items: _*) def fromFn(iterable: js.Iterable[Float]): Float32Array = Float32Array.from(iterable) @@ -440,8 +417,6 @@ class Float32ArrayTest extends TypedArrayTest[Float, Float32Array] { def intToV(n: Int): Float = n.toFloat } -object Float64ArrayTest extends Requires.TypedArray - class Float64ArrayTest extends TypedArrayTest[Double, Float64Array] { def ofFn(items: Double*): Float64Array = Float64Array.of(items: _*) def fromFn(iterable: js.Iterable[Double]): Float64Array = Float64Array.from(iterable) @@ -460,8 +435,6 @@ class Float64ArrayTest extends TypedArrayTest[Double, Float64Array] { def intToV(n: Int): Double = n.toDouble } -object BigInt64ArrayTest extends Requires.TypedArray - class BigInt64ArrayTest extends TypedArrayTest[js.BigInt, BigInt64Array] { def ofFn(items: js.BigInt*): BigInt64Array = BigInt64Array.of(items: _*) def fromFn(iterable: js.Iterable[js.BigInt]): BigInt64Array = BigInt64Array.from(iterable) @@ -481,8 +454,6 @@ class BigInt64ArrayTest extends TypedArrayTest[js.BigInt, BigInt64Array] { def intToV(n: Int): js.BigInt = js.BigInt(n) } -object BigUint64ArrayTest extends Requires.TypedArray - class BigUint64ArrayTest extends TypedArrayTest[js.BigInt, BigUint64Array] { def ofFn(items: js.BigInt*): BigUint64Array = BigUint64Array.of(items: _*) def fromFn(iterable: js.Iterable[js.BigInt]): BigUint64Array = BigUint64Array.from(iterable) diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/utils/Requires.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/utils/Requires.scala deleted file mode 100644 index 90e539e020..0000000000 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/utils/Requires.scala +++ /dev/null @@ -1,26 +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.utils - -import org.junit.Assume._ -import org.junit.BeforeClass -import org.scalajs.testsuite.utils.Platform._ - -object Requires { - - trait TypedArray { - @BeforeClass def needsTypedArrays(): Unit = - assumeTrue("Assumed typed arrays are supported", typedArrays) - } - -} diff --git a/test-suite/jvm/src/main/scala/org/scalajs/testsuite/utils/Platform.scala b/test-suite/jvm/src/main/scala/org/scalajs/testsuite/utils/Platform.scala index 38e5007356..5e2f53de79 100644 --- a/test-suite/jvm/src/main/scala/org/scalajs/testsuite/utils/Platform.scala +++ b/test-suite/jvm/src/main/scala/org/scalajs/testsuite/utils/Platform.scala @@ -45,9 +45,7 @@ object Platform { def hasCompliantNullPointers: Boolean = true def hasCompliantStringIndexOutOfBounds: Boolean = true def hasCompliantModule: Boolean = true - def hasDirectBuffers: Boolean = true - def regexSupportsUnicodeCase: Boolean = true def regexSupportsUnicodeCharacterClasses: Boolean = true def regexSupportsLookBehinds: Boolean = true } 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 c49a68d766..60e4629e39 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 @@ -1481,8 +1481,6 @@ class BitSetTest { } @Test def valueOf_ByteBuffer_typedArrays(): Unit = { - assumeTrue("requires support for direct Buffers", hasDirectBuffers) - val eightBS = makeEightBS() val eightBytes = eightBS.toByteArray() diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/regex/RegexEngineTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/regex/RegexEngineTest.scala index 4cc2a720b4..718af492a0 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/regex/RegexEngineTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/regex/RegexEngineTest.scala @@ -1971,8 +1971,6 @@ class RegexEngineTest { } @Test def unicodeCaseInsensitive(): Unit = { - assumeTrue("requires 'u' flag support", regexSupportsUnicodeCase) - val s = compile("s", CaseInsensitive | UnicodeCase) assertMatches(s, "s") assertMatches(s, "S") @@ -2594,23 +2592,8 @@ class RegexEngineTest { assertSyntaxErrorInJS("a", UnicodeCharacterClass, s"UNICODE_CHARACTER_CLASS is not supported $reason", 0) assertSyntaxErrorInJS(".\\p{L}", s"Unicode character family is not supported $reason", 5) - if (regexSupportsUnicodeCase) { - assertSyntaxErrorInJS("a\\b.", UnicodeCase, s"\\b with UNICODE_CASE is not supported $reason", 2) - assertSyntaxErrorInJS("a\\B.", UnicodeCase, s"\\B with UNICODE_CASE is not supported $reason", 2) - } - } - - // Not supported below ES2015 - - if (!regexSupportsUnicodeCase) { - val reason = """ - |because it requires RegExp features of ECMAScript 2015. - |If you only target environments with ES2015+, you can enable ES2015 features with - | scalaJSLinkerConfig ~= { _.withESFeatures(_.withESVersion(ESVersion.ES2015)) } - |or an equivalent configuration depending on your build tool. - """.stripMargin.trim() - - assertSyntaxErrorInJS("a", CaseInsensitive | UnicodeCase, s"UNICODE_CASE is not supported $reason", 0) + assertSyntaxErrorInJS("a\\b.", UnicodeCase, s"\\b with UNICODE_CASE is not supported $reason", 2) + assertSyntaxErrorInJS("a\\B.", UnicodeCase, s"\\B with UNICODE_CASE is not supported $reason", 2) } } } From a9876417de1f52a04ecc3df3616427d6c15fe98b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 3 Feb 2025 14:16:18 +0100 Subject: [PATCH 5/8] Remove all the polyfills for ES2015 features in the library and javalib. Except in `ju.regex`, for which we will have a separate commit. --- .../src/main/scala/java/lang/Character.scala | 14 +- .../src/main/scala/java/lang/ClassValue.scala | 59 +--- .../scala/java/lang/FloatingPointBits.scala | 277 ++---------------- .../src/main/scala/java/lang/Integer.scala | 25 +- javalib/src/main/scala/java/lang/Math.scala | 133 +-------- .../src/main/scala/java/lang/_String.scala | 66 ++--- .../runtime/PrivateFieldsSymbolHolder.scala | 15 +- .../org/scalajs/linker/LibrarySizeTest.scala | 6 +- project/Build.scala | 4 +- 9 files changed, 61 insertions(+), 538 deletions(-) diff --git a/javalib/src/main/scala/java/lang/Character.scala b/javalib/src/main/scala/java/lang/Character.scala index a085f427d7..be3d85550a 100644 --- a/javalib/src/main/scala/java/lang/Character.scala +++ b/javalib/src/main/scala/java/lang/Character.scala @@ -128,19 +128,7 @@ object Character { if (!isValidCodePoint(codePoint)) throw new IllegalArgumentException() - if (LinkingInfo.esVersion >= ESVersion.ES2015) { - js.Dynamic.global.String.fromCodePoint(codePoint).asInstanceOf[String] - } else { - if (codePoint < MIN_SUPPLEMENTARY_CODE_POINT) { - js.Dynamic.global.String - .fromCharCode(codePoint) - .asInstanceOf[String] - } else { - js.Dynamic.global.String - .fromCharCode(highSurrogate(codePoint).toInt, lowSurrogate(codePoint).toInt) - .asInstanceOf[String] - } - } + js.Dynamic.global.String.fromCodePoint(codePoint).asInstanceOf[String] } // Low-level code point and code unit manipulations ------------------------- diff --git a/javalib/src/main/scala/java/lang/ClassValue.scala b/javalib/src/main/scala/java/lang/ClassValue.scala index 0ab92d37cb..69e6aa7861 100644 --- a/javalib/src/main/scala/java/lang/ClassValue.scala +++ b/javalib/src/main/scala/java/lang/ClassValue.scala @@ -22,62 +22,13 @@ import scala.scalajs.LinkingInfo.ESVersion 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 - null - } - - @inline - private def useJSMap: scala.Boolean = { - /* The linking-info test allows to constant-fold this method as `true` when - * 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 - } - - /* We use a HashMap instead of an IdentityHashMap because the latter is - * implemented in terms of the former anyway, to a HashMap is leaner and - * faster. - */ - private val javaMap: HashMap[Class[_], T] = - if (useJSMap) null - else new HashMap() + private val jsMap: js.Map[Class[_], T] = new js.Map() 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 - } - } - } + def get(`type`: Class[_]): T = + mapGetOrElseUpdate(jsMap, `type`)(() => computeValue(`type`)) - def remove(`type`: Class[_]): Unit = { - if (useJSMap) - jsMap.delete(`type`) - else - javaMap.remove(`type`) - } + def remove(`type`: Class[_]): Unit = + jsMap.delete(`type`) } diff --git a/javalib/src/main/scala/java/lang/FloatingPointBits.scala b/javalib/src/main/scala/java/lang/FloatingPointBits.scala index 96e1c8f64c..620be9a5b4 100644 --- a/javalib/src/main/scala/java/lang/FloatingPointBits.scala +++ b/javalib/src/main/scala/java/lang/FloatingPointBits.scala @@ -15,86 +15,22 @@ package java.lang import scala.scalajs.js import scala.scalajs.js.Dynamic.global import scala.scalajs.js.typedarray -import scala.scalajs.LinkingInfo.ESVersion /** Manipulating the bits of floating point numbers. */ private[lang] object FloatingPointBits { - - import scala.scalajs.LinkingInfo - - private[this] val _areTypedArraysSupported = { - // Here we use the `esVersion` test to dce the 4 subsequent tests - LinkingInfo.esVersion >= ESVersion.ES2015 || { - js.typeOf(global.ArrayBuffer) != "undefined" && - js.typeOf(global.Int32Array) != "undefined" && - js.typeOf(global.Float32Array) != "undefined" && - js.typeOf(global.Float64Array) != "undefined" - } - } - - @inline - private def areTypedArraysSupported: scala.Boolean = { - /* We have a forwarder to the internal `val _areTypedArraysSupported` to - * be able to inline it. This achieves the following: - * * If we emit ES2015+, dce `|| _areTypedArraysSupported` and replace - * `areTypedArraysSupported` by `true` in the calling code, allowing - * polyfills in the calling code to be dce'ed in turn. - * * If we emit ES5, replace `areTypedArraysSupported` by - * `_areTypedArraysSupported` so we do not calculate it multiple times. - */ - LinkingInfo.esVersion >= ESVersion.ES2015 || _areTypedArraysSupported - } - - private val arrayBuffer = - if (areTypedArraysSupported) new typedarray.ArrayBuffer(8) - else null - - private val int32Array = - if (areTypedArraysSupported) new typedarray.Int32Array(arrayBuffer, 0, 2) - else null - - private val float32Array = - if (areTypedArraysSupported) new typedarray.Float32Array(arrayBuffer, 0, 2) - else null - - private val float64Array = - if (areTypedArraysSupported) new typedarray.Float64Array(arrayBuffer, 0, 1) - else null + private val arrayBuffer = new typedarray.ArrayBuffer(8) + private val int32Array = new typedarray.Int32Array(arrayBuffer, 0, 2) + private val float32Array = new typedarray.Float32Array(arrayBuffer, 0, 2) + private val float64Array = new typedarray.Float64Array(arrayBuffer, 0, 1) private val areTypedArraysBigEndian = { - if (areTypedArraysSupported) { - int32Array(0) = 0x01020304 - (new typedarray.Int8Array(arrayBuffer, 0, 8))(0) == 0x01 - } else { - true // as good a value as any - } + int32Array(0) = 0x01020304 + (new typedarray.Int8Array(arrayBuffer, 0, 8))(0) == 0x01 } private val highOffset = if (areTypedArraysBigEndian) 0 else 1 private val lowOffset = if (areTypedArraysBigEndian) 1 else 0 - private val floatPowsOf2: js.Array[scala.Double] = - if (areTypedArraysSupported) null - else makePowsOf2(len = 1 << 8, java.lang.Float.MIN_NORMAL.toDouble) - - private val doublePowsOf2: js.Array[scala.Double] = - if (areTypedArraysSupported) null - else makePowsOf2(len = 1 << 11, java.lang.Double.MIN_NORMAL) - - private def makePowsOf2(len: Int, minNormal: scala.Double): js.Array[scala.Double] = { - val r = new js.Array[scala.Double](len) - r(0) = 0.0 - var i = 1 - var next = minNormal - while (i != len - 1) { - r(i) = next - i += 1 - next *= 2 - } - r(len - 1) = scala.Double.PositiveInfinity - r - } - /** Hash code of a number (excluding Longs). * * Because of the common encoding for integer and floating point values, @@ -117,205 +53,36 @@ private[lang] object FloatingPointBits { * so that we never allocate a RuntimeLong instance (or anything, for * that matter). * - * In addition, in the happy path where typed arrays are supported, since - * we xor together the two Ints, it doesn't matter which one comes first - * or second, and hence we can use constants 0 and 1 instead of having an - * indirection through `highOffset` and `lowOffset`. + * In addition, since we xor together the two Ints, it doesn't matter + * which one comes first or second, and hence we can use constants + * 0 and 1 instead of having an indirection through `highOffset` and + * `lowOffset`. */ - if (areTypedArraysSupported) { - float64Array(0) = value - int32Array(0) ^ int32Array(1) - } else { - doubleHashCodePolyfill(value) - } + float64Array(0) = value + int32Array(0) ^ int32Array(1) } } - @noinline - private def doubleHashCodePolyfill(value: scala.Double): Int = - Long.hashCode(doubleToLongBitsPolyfillInline(value)) - def intBitsToFloat(bits: Int): scala.Float = { - if (areTypedArraysSupported) { - int32Array(0) = bits - float32Array(0) - } else { - intBitsToFloatPolyfill(bits).toFloat - } + int32Array(0) = bits + float32Array(0) } def floatToIntBits(value: scala.Float): Int = { - if (areTypedArraysSupported) { - float32Array(0) = value - int32Array(0) - } else { - floatToIntBitsPolyfill(value) - } + float32Array(0) = value + int32Array(0) } def longBitsToDouble(bits: scala.Long): scala.Double = { - if (areTypedArraysSupported) { - int32Array(highOffset) = (bits >>> 32).toInt - int32Array(lowOffset) = bits.toInt - float64Array(0) - } else { - longBitsToDoublePolyfill(bits) - } + int32Array(highOffset) = (bits >>> 32).toInt + int32Array(lowOffset) = bits.toInt + float64Array(0) } def doubleToLongBits(value: scala.Double): scala.Long = { - if (areTypedArraysSupported) { - float64Array(0) = value - ((int32Array(highOffset).toLong << 32) | - (int32Array(lowOffset).toLong & 0xffffffffL)) - } else { - doubleToLongBitsPolyfill(value) - } - } - - /* --- Polyfills for floating point bit manipulations --- - * - * Originally inspired by - * https://github.com/inexorabletash/polyfill/blob/3447582628b6e3ea81959c4d5987aa332c22d1ca/typedarray.js#L150-L264 - * - * Note that if typed arrays are not supported, it is almost certain that - * fround is not supported natively, so Float operations are extremely slow. - * - * We therefore do all computations in Doubles here. - */ - - private def intBitsToFloatPolyfill(bits: Int): scala.Double = { - val ebits = 8 - val fbits = 23 - val sign = (bits >> 31) | 1 // -1 or 1 - val e = (bits >> fbits) & ((1 << ebits) - 1) - val f = bits & ((1 << fbits) - 1) - decodeIEEE754(ebits, fbits, floatPowsOf2, scala.Float.MinPositiveValue, sign, e, f) - } - - private def floatToIntBitsPolyfill(floatValue: scala.Float): Int = { - // Some constants - val ebits = 8 - val fbits = 23 - - // Force computations to be on Doubles - val value = floatValue.toDouble - - // Determine sign bit and compute the absolute value av - val sign = if (value < 0.0 || (value == 0.0 && 1.0 / value < 0.0)) -1 else 1 - val s = sign & scala.Int.MinValue - val av = sign * value - - // Compute e and f - val powsOf2 = this.floatPowsOf2 // local cache - val e = encodeIEEE754Exponent(ebits, powsOf2, av) - val f = encodeIEEE754MantissaBits(ebits, fbits, powsOf2, scala.Float.MinPositiveValue.toDouble, av, e) - - // Encode - s | (e << fbits) | rawToInt(f) - } - - private def longBitsToDoublePolyfill(bits: scala.Long): scala.Double = { - val ebits = 11 - val fbits = 52 - val hifbits = fbits - 32 - val hi = (bits >>> 32).toInt - val lo = Utils.toUint(bits.toInt) - val sign = (hi >> 31) | 1 // -1 or 1 - val e = (hi >> hifbits) & ((1 << ebits) - 1) - val f = (hi & ((1 << hifbits) - 1)).toDouble * 0x100000000L.toDouble + lo - decodeIEEE754(ebits, fbits, doublePowsOf2, scala.Double.MinPositiveValue, sign, e, f) - } - - @noinline - private def doubleToLongBitsPolyfill(value: scala.Double): scala.Long = - doubleToLongBitsPolyfillInline(value) - - @inline - private def doubleToLongBitsPolyfillInline(value: scala.Double): scala.Long = { - // Some constants - val ebits = 11 - val fbits = 52 - val hifbits = fbits - 32 - - // Determine sign bit and compute the absolute value av - val sign = if (value < 0.0 || (value == 0.0 && 1.0 / value < 0.0)) -1 else 1 - val s = sign & scala.Int.MinValue - val av = sign * value - - // Compute e and f - val powsOf2 = this.doublePowsOf2 // local cache - val e = encodeIEEE754Exponent(ebits, powsOf2, av) - val f = encodeIEEE754MantissaBits(ebits, fbits, powsOf2, scala.Double.MinPositiveValue, av, e) - - // Encode - val hi = s | (e << hifbits) | rawToInt(f / 0x100000000L.toDouble) - val lo = rawToInt(f) - (hi.toLong << 32) | (lo.toLong & 0xffffffffL) - } - - @inline - private def decodeIEEE754(ebits: Int, fbits: Int, - powsOf2: js.Array[scala.Double], minPositiveValue: scala.Double, - sign: scala.Int, e: Int, f: scala.Double): scala.Double = { - - // Some constants - val specialExponent = (1 << ebits) - 1 - val twoPowFbits = (1L << fbits).toDouble - - if (e == specialExponent) { - // Special - if (f == 0.0) - sign * scala.Double.PositiveInfinity - else - scala.Double.NaN - } else if (e > 0) { - // Normalized - sign * powsOf2(e) * (1 + f / twoPowFbits) - } else { - // Subnormal - sign * f * minPositiveValue - } - } - - private def encodeIEEE754Exponent(ebits: Int, - powsOf2: js.Array[scala.Double], av: scala.Double): Int = { - - /* Binary search of `av` inside `powsOf2`. - * There are exactly `ebits` iterations of this loop (11 for Double, 8 for Float). - */ - var eMin = 0 - var eMax = 1 << ebits - while (eMin + 1 < eMax) { - val e = (eMin + eMax) >> 1 - if (av < powsOf2(e)) // false when av is NaN - eMax = e - else - eMin = e - } - eMin - } - - @inline - private def encodeIEEE754MantissaBits(ebits: Int, fbits: Int, - powsOf2: js.Array[scala.Double], minPositiveValue: scala.Double, - av: scala.Double, e: Int): scala.Double = { - - // Some constants - val specialExponent = (1 << ebits) - 1 - val twoPowFbits = (1L << fbits).toDouble - - if (e == specialExponent) { - if (av != av) - (1L << (fbits - 1)).toDouble // NaN - else - 0.0 // Infinity - } else { - if (e == 0) - av / minPositiveValue // Subnormal - else - ((av / powsOf2(e)) - 1.0) * twoPowFbits // Normal - } + float64Array(0) = value + ((int32Array(highOffset).toLong << 32) | + (int32Array(lowOffset).toLong & 0xffffffffL)) } @inline private def rawToInt(x: scala.Double): Int = { diff --git a/javalib/src/main/scala/java/lang/Integer.scala b/javalib/src/main/scala/java/lang/Integer.scala index a4c2694365..4a730ecdfc 100644 --- a/javalib/src/main/scala/java/lang/Integer.scala +++ b/javalib/src/main/scala/java/lang/Integer.scala @@ -279,29 +279,8 @@ object Integer { if (i == 0) 0 else if (i < 0) -1 else 1 // Intrinsic, fallback on actual code for non-literal in JS - @inline def numberOfLeadingZeros(i: scala.Int): scala.Int = { - if (LinkingInfo.esVersion >= ESVersion.ES2015) js.Math.clz32(i) - else clz32Dynamic(i) - } - - private def clz32Dynamic(i: scala.Int) = { - if (js.typeOf(js.Dynamic.global.Math.clz32) == "function") { - js.Math.clz32(i) - } else { - // See Hacker's Delight, Section 5-3 - var x = i - if (x == 0) { - 32 - } else { - var r = 1 - if ((x & 0xffff0000) == 0) { x <<= 16; r += 16 } - if ((x & 0xff000000) == 0) { x <<= 8; r += 8 } - if ((x & 0xf0000000) == 0) { x <<= 4; r += 4 } - if ((x & 0xc0000000) == 0) { x <<= 2; r += 2 } - r + (x >> 31) - } - } - } + @inline def numberOfLeadingZeros(i: scala.Int): scala.Int = + js.Math.clz32(i) // Wasm intrinsic @inline def numberOfTrailingZeros(i: scala.Int): scala.Int = diff --git a/javalib/src/main/scala/java/lang/Math.scala b/javalib/src/main/scala/java/lang/Math.scala index cb965bb56b..431ed961e7 100644 --- a/javalib/src/main/scala/java/lang/Math.scala +++ b/javalib/src/main/scala/java/lang/Math.scala @@ -23,9 +23,6 @@ object Math { final val E = 2.718281828459045 final val PI = 3.141592653589793 - @inline private def assumingES6: scala.Boolean = - LinkingInfo.esVersion >= ESVersion.ES2015 - @inline def abs(a: scala.Int): scala.Int = if (a < 0) -a else a @inline def abs(a: scala.Long): scala.Long = if (a < 0) -a else a @@ -75,20 +72,8 @@ object Math { @inline def exp(a: scala.Double): scala.Double = js.Math.exp(a) @inline def log(a: scala.Double): scala.Double = js.Math.log(a) - - @inline def log10(a: scala.Double): scala.Double = { - if (assumingES6 || !Utils.isUndefined(g.Math.log10)) - js.Math.log10(a) - else - log(a) / 2.302585092994046 - } - - @inline def log1p(a: scala.Double): scala.Double = { - if (assumingES6 || !Utils.isUndefined(g.Math.log1p)) - js.Math.log1p(a) - else if (a == 0.0) a - else log(a + 1) - } + @inline def log10(a: scala.Double): scala.Double = js.Math.log10(a) + @inline def log1p(a: scala.Double): scala.Double = js.Math.log1p(a) @inline def sin(a: scala.Double): scala.Double = js.Math.sin(a) @inline def cos(a: scala.Double): scala.Double = js.Math.cos(a) @@ -115,31 +100,7 @@ object Math { else a } - def cbrt(a: scala.Double): scala.Double = { - if (assumingES6 || !Utils.isUndefined(g.Math.cbrt)) { - js.Math.cbrt(a) - } else { - if (a == 0 || Double.isNaN(a) || Double.isInfinite(a)) { - a - } else { - val sign = if (a < 0.0) -1.0 else 1.0 - val value = sign * a - - //Initial Approximation - var x = 0.0 - var xi = pow(value, 0.3333333333333333) - - //Halley's Method (http://metamerist.com/cbrt/cbrt.htm) - while (abs(x - xi) >= 1E-16) { - x = xi - val x3 = js.Math.pow(x, 3) - val x3Plusa = x3 + value - xi = x * (x3Plusa + value) / (x3Plusa + x3) - } - sign * xi - } - } - } + def cbrt(a: scala.Double): scala.Double = js.Math.cbrt(a) def nextUp(a: scala.Double): scala.Double = { if (a != a || a == scala.Double.PositiveInfinity) { @@ -231,91 +192,13 @@ object Math { nextUp(absa) - absa // this case handles NaN as well } - def hypot(a: scala.Double, b: scala.Double): scala.Double = { - if (assumingES6 || !Utils.isUndefined(g.Math.hypot)) { - js.Math.hypot(a, b) - } else { - // http://en.wikipedia.org/wiki/Hypot#Implementation - if (abs(a) == scala.Double.PositiveInfinity || abs(b) == scala.Double.PositiveInfinity) - scala.Double.PositiveInfinity - else if (Double.isNaN(a) || Double.isNaN(b)) - scala.Double.NaN - else if (a == 0 && b == 0) - 0.0 - else { - //To Avoid Overflow and UnderFlow - // calculate |x| * sqrt(1 - (y/x)^2) instead of sqrt(x^2 + y^2) - val x = abs(a) - val y = abs(b) - val m = max(x, y) - val t = min(x, y) / m - m * sqrt(1 + t * t) - } - } - } - - def expm1(a: scala.Double): scala.Double = { - if (assumingES6 || !Utils.isUndefined(g.Math.expm1)) { - js.Math.expm1(a) - } else { - // https://github.com/ghewgill/picomath/blob/master/javascript/expm1.js - if (a == 0 || Double.isNaN(a)) - a - // Power Series http://en.wikipedia.org/wiki/Power_series - // for small values of a, exp(a) = 1 + a + (a*a)/2 - else if (abs(a) < 1E-5) - a + 0.5 * a * a - else - exp(a) - 1.0 - } - } - - def sinh(a: scala.Double): scala.Double = { - if (assumingES6 || !Utils.isUndefined(g.Math.sinh)) { - js.Math.sinh(a) - } else { - if (Double.isNaN(a) || a == 0.0 || abs(a) == scala.Double.PositiveInfinity) a - else (exp(a) - exp(-a)) / 2.0 - } - } + def hypot(a: scala.Double, b: scala.Double): scala.Double = js.Math.hypot(a, b) - def cosh(a: scala.Double): scala.Double = { - if (assumingES6 || !Utils.isUndefined(g.Math.cosh)) { - js.Math.cosh(a) - } else { - if (Double.isNaN(a)) - a - else if (a == 0.0) - 1.0 - else if (abs(a) == scala.Double.PositiveInfinity) - scala.Double.PositiveInfinity - else - (exp(a) + exp(-a)) / 2.0 - } - } + def expm1(a: scala.Double): scala.Double = js.Math.expm1(a) - def tanh(a: scala.Double): scala.Double = { - if (assumingES6 || !Utils.isUndefined(g.Math.tanh)) { - js.Math.tanh(a) - } else { - if (Double.isNaN(a) || a == 0.0) - a - else if (abs(a) == scala.Double.PositiveInfinity) - signum(a) - else { - // sinh(a) / cosh(a) = - // 1 - 2 * (exp(-a)/ (exp(-a) + exp (a))) - val expma = exp(-a) - if (expma == scala.Double.PositiveInfinity) //Infinity / Infinity - -1.0 - else { - val expa = exp(a) - val ret = expma / (expa + expma) - 1.0 - (2.0 * ret) - } - } - } - } + def sinh(a: scala.Double): scala.Double = js.Math.sinh(a) + def cosh(a: scala.Double): scala.Double = js.Math.cosh(a) + def tanh(a: scala.Double): scala.Double = js.Math.tanh(a) def addExact(a: scala.Int, b: scala.Int): scala.Int = { val res = a + b diff --git a/javalib/src/main/scala/java/lang/_String.scala b/javalib/src/main/scala/java/lang/_String.scala index ea29540e37..e98265acb8 100644 --- a/javalib/src/main/scala/java/lang/_String.scala +++ b/javalib/src/main/scala/java/lang/_String.scala @@ -57,12 +57,8 @@ final class _String private () // scalastyle:ignore // Wasm intrinsic def codePointAt(index: Int): Int = { - if (LinkingInfo.esVersion >= ESVersion.ES2015) { - charAt(index) // bounds check - this.asInstanceOf[js.Dynamic].codePointAt(index).asInstanceOf[Int] - } else { - Character.codePointAtImpl(this, index) - } + charAt(index) // bounds check + this.asInstanceOf[js.Dynamic].codePointAt(index).asInstanceOf[Int] } @noinline @@ -165,12 +161,8 @@ final class _String private () // scalastyle:ignore @inline def endsWith(suffix: String): scala.Boolean = { - if (LinkingInfo.esVersion >= ESVersion.ES2015) { - suffix.getClass() // null check - thisString.asInstanceOf[js.Dynamic].endsWith(suffix).asInstanceOf[scala.Boolean] - } else { - thisString.jsSubstring(this.length() - suffix.length()) == suffix - } + suffix.getClass() // null check + thisString.asInstanceOf[js.Dynamic].endsWith(suffix).asInstanceOf[scala.Boolean] } def getBytes(): Array[scala.Byte] = @@ -269,29 +261,14 @@ final class _String private () // scalastyle:ignore } def repeat(count: Int): String = { - if (count < 0) { - throw new IllegalArgumentException - } else if (LinkingInfo.esVersion >= ESVersion.ES2015) { - /* 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. - */ - this.asInstanceOf[js.Dynamic].repeat(count).asInstanceOf[String] - } else if (thisString == "" || count == 0) { - "" - } else if (thisString.length > (Int.MaxValue / count)) { - throw new OutOfMemoryError - } else { - var str = thisString - val resultLength = thisString.length * count - var remainingIters = 31 - Integer.numberOfLeadingZeros(count) - while (remainingIters > 0) { - str += str - remainingIters -= 1 - } - str += str.jsSubstring(0, resultLength - str.length) - str - } + if (count < 0) + throw new IllegalArgumentException() + + /* 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. + */ + this.asInstanceOf[js.Dynamic].repeat(count).asInstanceOf[String] } @inline @@ -317,24 +294,15 @@ final class _String private () // scalastyle:ignore @inline def startsWith(prefix: String): scala.Boolean = { - if (LinkingInfo.esVersion >= ESVersion.ES2015) { - prefix.getClass() // null check - thisString.asInstanceOf[js.Dynamic].startsWith(prefix).asInstanceOf[scala.Boolean] - } else { - thisString.jsSubstring(0, prefix.length()) == prefix - } + prefix.getClass() // null check + thisString.asInstanceOf[js.Dynamic].startsWith(prefix).asInstanceOf[scala.Boolean] } @inline def startsWith(prefix: String, toffset: Int): scala.Boolean = { - if (LinkingInfo.esVersion >= ESVersion.ES2015) { - prefix.getClass() // null check - (toffset <= length() && toffset >= 0 && - thisString.asInstanceOf[js.Dynamic].startsWith(prefix, toffset).asInstanceOf[scala.Boolean]) - } else { - (toffset <= length() && toffset >= 0 && - thisString.jsSubstring(toffset, toffset + prefix.length()) == prefix) - } + prefix.getClass() // null check + (toffset <= length() && toffset >= 0 && + thisString.asInstanceOf[js.Dynamic].startsWith(prefix, toffset).asInstanceOf[scala.Boolean]) } @inline diff --git a/library/src/main/scala/scala/scalajs/runtime/PrivateFieldsSymbolHolder.scala b/library/src/main/scala/scala/scalajs/runtime/PrivateFieldsSymbolHolder.scala index a880135f4d..1f723398c2 100644 --- a/library/src/main/scala/scala/scalajs/runtime/PrivateFieldsSymbolHolder.scala +++ b/library/src/main/scala/scala/scalajs/runtime/PrivateFieldsSymbolHolder.scala @@ -17,18 +17,5 @@ import scala.scalajs.js.JSStringOps._ import scala.scalajs.LinkingInfo.ESVersion private[runtime] object PrivateFieldsSymbolHolder { - val privateFieldsSymbol: Any = { - // Cannot import scala.scalajs.LinkingInfo because it is shadowed by runtime.LinkingInfo - if (scala.scalajs.LinkingInfo.esVersion >= ESVersion.ES2015 || - js.typeOf(js.Symbol) != "undefined") { - js.Symbol("privateFields") - } else { - def rand32(): String = { - val s = ((js.Math.random() * 4294967296.0).asInstanceOf[js.Dynamic] >>> 0.asInstanceOf[js.Dynamic]) - .applyDynamic("toString")(16).asInstanceOf[String] - "00000000".jsSubstring(s.length) + s - } - rand32() + rand32() + rand32() + rand32() - } - } + val privateFieldsSymbol: Any = js.Symbol("privateFields") } diff --git a/linker/shared/src/test/scala/org/scalajs/linker/LibrarySizeTest.scala b/linker/shared/src/test/scala/org/scalajs/linker/LibrarySizeTest.scala index 137d8e7400..9c013f8ad6 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/LibrarySizeTest.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/LibrarySizeTest.scala @@ -70,9 +70,9 @@ class LibrarySizeTest { ) testLinkedSizes( - expectedFastLinkSize = 147046, - expectedFullLinkSizeWithoutClosure = 85355, - expectedFullLinkSizeWithClosure = 21492, + expectedFastLinkSize = 146573, + expectedFullLinkSizeWithoutClosure = 85204, + expectedFullLinkSizeWithClosure = 21476, classDefs, moduleInitializers = MainTestModuleInitializers ) diff --git a/project/Build.scala b/project/Build.scala index 0d0e1f4408..e832dbef5d 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -2049,7 +2049,7 @@ object Build { case `default212Version` => if (!useMinifySizes) { Some(ExpectedSizes( - fastLink = 622000 to 623000, + fastLink = 621000 to 622000, fullLink = 96000 to 97000, fastLinkGz = 75000 to 79000, fullLinkGz = 25000 to 26000, @@ -2074,7 +2074,7 @@ object Build { } else { Some(ExpectedSizes( fastLink = 298000 to 299000, - fullLink = 256000 to 257000, + fullLink = 255000 to 256000, fastLinkGz = 47000 to 48000, fullLinkGz = 42000 to 43000, )) From 29f5f4ee7ef5fb5680f6dc693e391fd492d5140a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 3 Feb 2025 17:04:35 +0100 Subject: [PATCH 6/8] Remove the polyfills for ES2015 features in `ju.regex.*`. --- .../java/util/regex/IndicesBuilder.scala | 8 +- .../main/scala/java/util/regex/Pattern.scala | 59 +---- .../java/util/regex/PatternCompiler.scala | 217 ++++-------------- .../src/main/scala/java/util/regex/README.md | 92 +------- .../org/scalajs/linker/LibrarySizeTest.scala | 4 +- 5 files changed, 57 insertions(+), 323 deletions(-) diff --git a/javalib/src/main/scala/java/util/regex/IndicesBuilder.scala b/javalib/src/main/scala/java/util/regex/IndicesBuilder.scala index 3d2b480a94..c11b77b4e0 100644 --- a/javalib/src/main/scala/java/util/regex/IndicesBuilder.scala +++ b/javalib/src/main/scala/java/util/regex/IndicesBuilder.scala @@ -392,13 +392,7 @@ private[regex] object IndicesBuilder { } while (true) { - /* Parse the pattern by code points if RegExp supports the 'u' flag, - * in which case PatternCompiler always uses it, or by chars if it - * doesn't. This distinction is important for repeated surrogate pairs. - */ - val dispatchCP = - if (PatternCompiler.Support.supportsUnicode) pattern.codePointAt(pIndex) - else pattern.charAt(pIndex).toInt + val dispatchCP = pattern.codePointAt(pIndex) val baseNode = (dispatchCP: @switch) match { case '|' => diff --git a/javalib/src/main/scala/java/util/regex/Pattern.scala b/javalib/src/main/scala/java/util/regex/Pattern.scala index 52dcc3e8f0..439f56e918 100644 --- a/javalib/src/main/scala/java/util/regex/Pattern.scala +++ b/javalib/src/main/scala/java/util/regex/Pattern.scala @@ -35,7 +35,7 @@ final class Pattern private[regex] ( import Pattern._ @inline private def jsFlagsForFind: String = - jsFlags + (if (sticky && supportsSticky) "gy" else "g") + jsFlags + (if (sticky) "gy" else "g") /** Whether we already added the 'd' flag to the native RegExp's. */ private var enabledNativeIndices: Boolean = false @@ -44,9 +44,7 @@ final class Pattern private[regex] ( * * It receives the 'g' flag so that `lastIndex` is taken into acount. * - * It also receives the 'y' flag if this pattern is sticky and it is - * supported. If it is not supported, its behavior is polyfilled in - * `execFind()`. + * It also receives the 'y' flag if this pattern is sticky. * * Since that RegExp is only used locally within `execFind()`, we can * always reuse the same instance. @@ -74,56 +72,11 @@ final class Pattern private[regex] ( @inline // to stack-allocate the tuple private[regex] def execFind(input: String, start: Int): (js.RegExp.ExecResult, Int) = { - val mtch = execFindInternal(input, start) - val end = jsRegExpForFind.lastIndex - (mtch, end) - } - - private def execFindInternal(input: String, start: Int): js.RegExp.ExecResult = { val regexp = jsRegExpForFind - - if (!supportsSticky && sticky) { - regexp.lastIndex = start - val mtch = regexp.exec(input) - if (mtch == null || mtch.index > start) - null - else - mtch - } else if (supportsUnicode) { - regexp.lastIndex = start - regexp.exec(input) - } else { - /* When the native RegExp does not support the 'u' flag (introduced in - * ECMAScript 2015), it can find a match starting in the middle of a - * surrogate pair. This can happen if the pattern can match a substring - * starting with a lone low surrogate. However, that is not valid, - * because surrogate pairs must always stick together. - * - * In all the other situations, the `PatternCompiler` makes sure that - * surrogate pairs are always matched together or not at all, but it - * cannot avoid this specific situation because there is no look-behind - * support in that case either. So we take care of it now by skipping - * matches that start in the middle of a surrogate pair. - */ - @tailrec - def loop(start: Int): js.RegExp.ExecResult = { - regexp.lastIndex = start - val mtch = regexp.exec(input) - if (mtch == null) { - null - } else { - val index = mtch.index - if (index > start && index < input.length() && - Character.isLowSurrogate(input.charAt(index)) && - Character.isHighSurrogate(input.charAt(index - 1))) { - loop(index + 1) - } else { - mtch - } - } - } - loop(start) - } + regexp.lastIndex = start + val mtch = regexp.exec(input) + val end = regexp.lastIndex + (mtch, end) } private[regex] def numberedGroup(group: Int): Int = { diff --git a/javalib/src/main/scala/java/util/regex/PatternCompiler.scala b/javalib/src/main/scala/java/util/regex/PatternCompiler.scala index 751f2e8f78..06ae5f0b20 100644 --- a/javalib/src/main/scala/java/util/regex/PatternCompiler.scala +++ b/javalib/src/main/scala/java/util/regex/PatternCompiler.scala @@ -81,14 +81,6 @@ private[regex] object PatternCompiler { } } - /** Cache for `Support.supportsUnicode`. */ - private val _supportsUnicode = - (LinkingInfo.esVersion >= ESVersion.ES2015) || featureTest("u") - - /** Cache for `Support.supportsSticky`. */ - private val _supportsSticky = - (LinkingInfo.esVersion >= ESVersion.ES2015) || featureTest("y") - /** Cache for `Support.supportsDotAll`. */ private val _supportsDotAll = (LinkingInfo.esVersion >= ESVersion.ES2018) || featureTest("us") @@ -104,16 +96,6 @@ private[regex] object PatternCompiler { * enclosing object behind, depending on the target ES version. */ private[regex] object Support { - /** Tests whether the underlying JS RegExp supports the 'u' flag. */ - @inline - def supportsUnicode: Boolean = - (LinkingInfo.esVersion >= ESVersion.ES2015) || _supportsUnicode - - /** Tests whether the underlying JS RegExp supports the 'y' flag. */ - @inline - def supportsSticky: Boolean = - (LinkingInfo.esVersion >= ESVersion.ES2015) || _supportsSticky - /** Tests whether the underlying JS RegExp supports the 's' flag. */ @inline def supportsDotAll: Boolean = @@ -124,15 +106,6 @@ private[regex] object PatternCompiler { def supportsIndices: Boolean = _supportsIndices - /** Tests whether features requiring support for the 'u' flag are enabled. - * - * They are enabled if and only if the project is configured to rely on - * ECMAScript 2015 features. - */ - @inline - def enableUnicodeCaseInsensitive: Boolean = - LinkingInfo.esVersion >= ESVersion.ES2015 - /** Tests whether features requiring \p{} and/or look-behind assertions are enabled. * * They are enabled if and only if the project is configured to rely on @@ -148,19 +121,12 @@ private[regex] object PatternCompiler { // Helpers to deal with surrogate pairs when the 'u' flag is not supported private def codePointNotAmong(characters: String): String = { - if (supportsUnicode) { - if (characters != "") - "[^" + characters + "]" - else if (supportsDotAll) - "." // we always add the 's' flag when it is supported, so we can use "." here - else - "[\\d\\D]" // In theory, "[^]" works, but XRegExp does not trust JS engines on that, so we don't either - } else { - val highCharRange = s"$MIN_HIGH_SURROGATE-$MAX_HIGH_SURROGATE" - val lowCharRange = s"$MIN_LOW_SURROGATE-$MAX_LOW_SURROGATE" - val highCPOrSupplementaryCP = s"[$highCharRange](?:[$lowCharRange]|(?![$lowCharRange]))" - s"(?:[^$characters$highCharRange]|$highCPOrSupplementaryCP)" - } + if (characters != "") + "[^" + characters + "]" + else if (supportsDotAll) + "." // we always add the 's' flag when it is supported, so we can use "." here + else + "[\\d\\D]" // In theory, "[^]" works, but XRegExp does not trust JS engines on that, so we don't either } // Other helpers @@ -214,19 +180,8 @@ private[regex] object PatternCompiler { import InlinedHelpers._ - private def codePointToString(codePoint: Int): String = { - if (LinkingInfo.esVersion >= ESVersion.ES2015) { - js.Dynamic.global.String.fromCodePoint(codePoint).asInstanceOf[String] - } else { - if (isBmpCodePoint(codePoint)) { - js.Dynamic.global.String.fromCharCode(codePoint).asInstanceOf[String] - } else { - js.Dynamic.global.String - .fromCharCode(highSurrogate(codePoint).toInt, lowSurrogate(codePoint).toInt) - .asInstanceOf[String] - } - } - } + private def codePointToString(codePoint: Int): String = + js.Dynamic.global.String.fromCodePoint(codePoint).asInstanceOf[String] // Everything for compiling character classes @@ -504,22 +459,6 @@ private[regex] object PatternCompiler { @inline def apply(start: Int, end: Int): CodePointRange = new CodePointRange(start, end) - - @inline - def BmpBelowHighSurrogates: CodePointRange = - CodePointRange(0, Character.MIN_HIGH_SURROGATE - 1) - - @inline - def HighSurrogates: CodePointRange = - CodePointRange(Character.MIN_HIGH_SURROGATE, Character.MAX_HIGH_SURROGATE) - - @inline - def BmpAboveHighSurrogates: CodePointRange = - CodePointRange(Character.MAX_HIGH_SURROGATE + 1, Character.MAX_VALUE) - - @inline - def Supplementaries: CodePointRange = - CodePointRange(Character.MIN_SUPPLEMENTARY_CODE_POINT, Character.MAX_CODE_POINT) } private final class CharacterClassBuilder(asciiCaseInsensitive: Boolean, isNegated: Boolean) { @@ -602,21 +541,11 @@ private[regex] object PatternCompiler { def addSingleCodePoint(codePoint: Int): Unit = { val s = literalCodePoint(codePoint) - if (supportsUnicode || (isBmpCodePoint(codePoint) && !isHighSurrogateCP(codePoint))) { - if (isLowSurrogateCP(codePoint)) { - // Put low surrogates at the beginning so that they do not merge with high surrogates - thisSegment = s + thisSegment - } else { - thisSegment += s - } + if (isLowSurrogateCP(codePoint)) { + // Put low surrogates at the beginning so that they do not merge with high surrogates + thisSegment = s + thisSegment } else { - if (isBmpCodePoint(codePoint)) { - // It is a high surrogate - addAlternative(s"(?:$s(?![$MIN_LOW_SURROGATE-$MAX_LOW_SURROGATE]))") - } else { - // It is a supplementary code point - addAlternative(s) - } + thisSegment += s } if (asciiCaseInsensitive) { @@ -633,65 +562,19 @@ private[regex] object PatternCompiler { val range = CodePointRange(startCodePoint, endCodePoint) - if (supportsUnicode || range.end < MIN_HIGH_SURROGATE) { - val s = literalRange(range) + val s = literalRange(range) - if (isLowSurrogateCP(range.start)) { - /* Put ranges whose start code point is a low surrogate at the - * beginning, so that they cannot merge with a high surrogate. Since - * the numeric values of high surrogates is *less than* that of low - * surrogates, the `range.end` cannot be a high surrogate here, and - * so there is no danger of it merging with a low surrogate already - * present at the beginning of `thisSegment`. - */ - thisSegment = s + thisSegment - } else { - thisSegment += s - } - } else { - /* Here be dragons. We need to split the range into several ranges that - * we can separately compile. - * - * Since the 'u' flag is not used when we get here, the RegExp engine - * treats surrogate chars as individual chars in all cases. Therefore, - * we do not need to protect low surrogates. + if (isLowSurrogateCP(range.start)) { + /* Put ranges whose start code point is a low surrogate at the + * beginning, so that they cannot merge with a high surrogate. Since + * the numeric values of high surrogates is *less than* that of low + * surrogates, the `range.end` cannot be a high surrogate here, and + * so there is no danger of it merging with a low surrogate already + * present at the beginning of `thisSegment`. */ - - val bmpBelowHighSurrogates = range.intersect(CodePointRange.BmpBelowHighSurrogates) - if (bmpBelowHighSurrogates.nonEmpty) - thisSegment += literalRange(bmpBelowHighSurrogates) - - val highSurrogates = range.intersect(CodePointRange.HighSurrogates) - if (highSurrogates.nonEmpty) - addAlternative("[" + literalRange(highSurrogates) + "]" + s"(?![$MIN_LOW_SURROGATE-$MAX_LOW_SURROGATE])") - - val bmpAboveHighSurrogates = range.intersect(CodePointRange.BmpAboveHighSurrogates) - if (bmpAboveHighSurrogates.nonEmpty) - thisSegment += literalRange(bmpAboveHighSurrogates) - - val supplementaries = range.intersect(CodePointRange.Supplementaries) - if (supplementaries.nonEmpty) { - val startHigh = highSurrogate(supplementaries.start) - val startLow = lowSurrogate(supplementaries.start) - - val endHigh = highSurrogate(supplementaries.end) - val endLow = lowSurrogate(supplementaries.end) - - if (startHigh == endHigh) { - addAlternative( - codePointToString(startHigh) + "[" + literalRange(CodePointRange(startLow, endLow)) + "]") - } else { - addAlternative( - codePointToString(startHigh) + "[" + literalRange(CodePointRange(startLow, MAX_LOW_SURROGATE)) + "]") - - val middleHighs = CodePointRange(startHigh + 1, endHigh - 1) - if (middleHighs.nonEmpty) - addAlternative(s"[${literalRange(middleHighs)}][$MIN_LOW_SURROGATE-$MAX_LOW_SURROGATE]") - - addAlternative( - codePointToString(endHigh) + "[" + literalRange(CodePointRange(MIN_LOW_SURROGATE, endLow)) + "]") - } - } + thisSegment = s + thisSegment + } else { + thisSegment += s } if (asciiCaseInsensitive) { @@ -757,16 +640,12 @@ private final class PatternCompiler(private val pattern: String, private var fla (flags & (CASE_INSENSITIVE | UNICODE_CASE)) == CASE_INSENSITIVE @inline - private def unicodeCaseInsensitive: Boolean = { - enableUnicodeCaseInsensitive && // for dead code elimination + private def unicodeCaseInsensitive: Boolean = (flags & (CASE_INSENSITIVE | UNICODE_CASE)) == (CASE_INSENSITIVE | UNICODE_CASE) - } @inline - private def unicodeCaseOrUnicodeCharacterClass: Boolean = { - enableUnicodeCaseInsensitive && // for dead code elimination + private def unicodeCaseOrUnicodeCharacterClass: Boolean = (flags & (UNICODE_CASE | UNICODE_CHARACTER_CLASS)) != 0 - } @inline private def multiline: Boolean = { @@ -800,11 +679,6 @@ private final class PatternCompiler(private val pattern: String, private var fla parseErrorRequireESVersion("UNICODE_CHARACTER_CLASS", "2018") } - if (!enableUnicodeCaseInsensitive) { - if (hasFlag(UNICODE_CASE)) - parseErrorRequireESVersion("UNICODE_CASE", "2015") - } - val jsPattern = if (isLiteral) { literal(pattern) } else { @@ -816,12 +690,10 @@ private final class PatternCompiler(private val pattern: String, private var fla } val jsFlags = { - // We always use the 'u' and 's' flags when they are supported. - val baseJSFlags = { + // We always use the 'u' flag, as well as the 's' flag when it is supported. + val baseJSFlags = if (supportsDotAll) "us" - else if (supportsUnicode) "u" - else "" - } + else "u" // We add the 'i' flag when using Unicode-aware case insensitive matching. if (unicodeCaseInsensitive) baseJSFlags + "i" @@ -921,27 +793,18 @@ private final class PatternCompiler(private val pattern: String, private var fla s } } else { - if (supportsUnicode) { - /* We wrap low surrogates with `(?:x)` to ensure that we do not - * artificially create a surrogate pair in the compiled pattern where - * none existed in the source pattern. - * Consider the source pattern `\x{D834}\x{DD1E}`, for example. - * If low surrogates were not wrapped, it would be compiled to a - * surrogate pair, which would match the input string `"𝄞"` although it - * is not supposed to. - */ - if (isLowSurrogateCP(cp)) - s"(?:$s)" - else - s - } else { - if (isHighSurrogateCP(cp)) - s"(?:$s(?![$MIN_LOW_SURROGATE-$MAX_LOW_SURROGATE]))" - else if (isBmpCodePoint(cp)) - s - else - s"(?:$s)" // group a surrogate pair so that it is repeated as a whole - } + /* We wrap low surrogates with `(?:x)` to ensure that we do not + * artificially create a surrogate pair in the compiled pattern where + * none existed in the source pattern. + * Consider the source pattern `\x{D834}\x{DD1E}`, for example. + * If low surrogates were not wrapped, it would be compiled to a + * surrogate pair, which would match the input string `"𝄞"` although it + * is not supposed to. + */ + if (isLowSurrogateCP(cp)) + s"(?:$s)" + else + s } } diff --git a/javalib/src/main/scala/java/util/regex/README.md b/javalib/src/main/scala/java/util/regex/README.md index e14742347f..461ff7bc50 100644 --- a/javalib/src/main/scala/java/util/regex/README.md +++ b/javalib/src/main/scala/java/util/regex/README.md @@ -27,10 +27,6 @@ The following features are never supported: * embedded flag expressions without inner groups, i.e., constructs of the form `(?idmsuxU-idmsuxU)`, *except* if they appear at the very beginning of the regex (e.g., `(?i)abc` is accepted, but `ab(?i)c` is not), and * numeric "back" references to groups that are defined later in the pattern (note that even Java does not support *named* back references like that). -The following features require `esVersion >= ESVersion.ES2015`: - -* the `UNICODE_CASE` flag. - The following features require `esVersion >= ESVersion.ES2018`: * the `MULTILINE` and `UNICODE_CHARACTER_CLASS` flags, @@ -40,8 +36,6 @@ The following features require `esVersion >= ESVersion.ES2018`: It is worth noting that, among others, the following features *are* supported in all cases, even when no equivalent feature exists in ECMAScript at all, or in the target version of ECMAScript: -* correct handling of surrogate pairs (natively supported in ES 2015+), -* the `\G` boundary matcher when it is at the beginning of the pattern (corresponding to the 'y' flag, natively supported in ES 2015+), * named groups and named back references (natively supported in ES 2018+), * the `DOTALL` flag (natively supported in ES 2018+), * ASCII case-insensitive matching (`CASE_INSENSITIVE` on but `UNICODE_CASE` off), @@ -50,6 +44,7 @@ It is worth noting that, among others, the following features *are* supported in * complex character classes with unions and intersections (e.g., `[a-z&&[^g-p]]`), * atomic groups `(?>𝑋)`, * possessive quantifiers `𝑋*+`, `𝑋++` and `𝑋?+`, +* the `\G` boundary matcher when it is at the beginning of the pattern (corresponding to the 'y' flag), * the `\A`, `\Z` and `\z` boundary matchers, * the `\R` expression, * embedded quotations with `\Q` and `\E`, both outside and inside character classes. @@ -94,21 +89,17 @@ There is a state variable `pIndex` which indicates the position inside the origi Compilation methods parse a subexpression at `pIndex`, advance `pIndex` past what they parsed, and return the result of the compilation. Parsing is always done at the code point level, and not at the individual `Char` level, using the [WTF-16 encoding](https://simonsapin.github.io/wtf-8/#wtf-16). -See [Handling surrogate pairs without support for the 'u' flag](#handling-surrogate-pairs-without-support-for-the-u-flag) for details about the behavior of lone surrogates. - -We first describe the compilation with the assumption that the underlying `RegExp`s support the `u` flag. -This is always true in ES 2015+, and dynamically determined at run-time in ES 5.1. -We will cover later what happens when it is not supported. +See [Meaning of lone surrogates](#meaning-of-lone-surrogates) for details about the behavior of lone surrogates. ### JS RegExp flags and case sensitivity Irrespective of the Java flags, we always use the following JS flags when they are supported (including through dynamic detection): -- 'u' for correct handling of surrogate pairs and Unicode rules for case folding (introduced in ES2015), +- 'u' for correct handling of surrogate pairs and Unicode rules for case folding (introduced in ES2015, hence always supported), - 's' for the dotAll behavior, i.e., `.` matches any code point (introduced in ES2018). In addition, we use the 'i' JS flag when both `CASE_INSENSITIVE` and `UNICODE_CASE` are on. -Since `UNICODE_CASE` is only supported in ES 2015+, this implies that 'u' is supported, and hence that we can leave all the handling of case insensitivity to the native RegExp. +We then leave all the handling of case insensitivity to the native RegExp, which does the right thing when combined with 'u'. When `CASE_INSENSITIVE` is on but `UNICODE_CASE` is off, we must apply ASCII case insensitivity. This is not supported by JS RegExps, so we implement it ourselves during compilation. @@ -119,7 +110,7 @@ When it is true: * any character range in a character class that intersects with the range `A-Z` and/or `a-z` is compiled with additional range(s) to cover the uppercase and lowercase variants. `PatternCompiler` never uses any other JS RegExp flag. -`Pattern` adds the 'g' flag for its general-purpose instance of `RegExp` (the one used for everything except `Matcher.matches()`), as well as the 'y' flag if the regex is sticky and it is supported. +`Pattern` adds the 'g' flag for its general-purpose instance of `RegExp` (the one used for everything except `Matcher.matches()`), as well as the 'y' flag if the regex is sticky. ### Capturing groups @@ -233,13 +224,9 @@ An alternative design would have been to resolve all the operations at compile-t This would require to expand `\p{}` and `\P{}` Unicode property names into equivalent sets, which would need a significant chunk of the Unicode database to be available. That strategy would have a huge cost in terms of code size, and likely in terms of execution time as well (for compilation and/or matching). -### Handling surrogate pairs without support for the 'u' flag +### Meaning of lone surrogates -So far, we have assumed that the underlying RegExp supports the 'u' flag, which we test with `supportsUnicode`. -In this section, we cover how the compilation is affected when it is not supported. -This can only happen when we target ES 5.1. - -The ECMAScript specification is very precise about how patterns and strings are interpreted when the 'u' flag is enabled. +The ECMAScript specification is very precise about how lone surrogates in patterns and strings are interpreted. It boils down to: * First, the pattern and the input, which are strings of 16-bit UTF-16 code units, are decoded into a *list of code points*, using the WTF-16 encoding. @@ -247,70 +234,7 @@ It boils down to: * Then, all the regular expressions operators work on these lists of code points, never taking individual code units into account. The documentation for Java regexes does not really say anything about what it considers "characters" to be. -However, experimentation and tests show that they behave exactly like ECMAScript with the 'u' flag. - -Without support for the 'u' flag, the JavaScript RegExp engine will parse the pattern and process the input with individual Chars rather than code points. -In other words, it will consider surrogate pairs as two separate (and therefore separable) code units. -If we do nothing against it, it can jeopardize the semantics of regexes in several ways: - -* a `.` will match only the high surrogate of a surrogate pair instead of the whole codepoint, -* same issue with any negative character class like `[^a]`, -* an unpaired high surrogate in the pattern may match the high surrogate of a surrogate pair in the input, although it must not, -* a surrogate pair in a character class will be interpreted as *either* the high surrogate or the low surrogate, instead of both together, -* etc. - -Even patterns that contain only ASCII characters (escaped or not) and use no flags can behave incorrectly on inputs that contain surrogate characters (paired or unpaired). -A possible design would have been to restrict the *inputs* to strings without surrogate code units when targeting ES 5.1. -However, that could lead to patterns that fail at matching-time, rather than at compile-time (during `Pattern.compile()`), unlike all the other features that are conditioned on the ES version. - -Therefore, we go to great lengths to implement the right semantics despite the lack of support for 'u'. - -#### Overall idea of the solution - -When `supportsUnicode` is false, we apply the following changes to the compilation scheme. -In general, we make sure that: - -* something that can match a high surrogate does not match one followed by a low surrogate, -* something that can match a supplementary code point or a high surrogate never selects the high surrogate if it could match the whole code point. - -We do nothing special for low surrogates, as all possible patterns go from left to right (we don't have look-behinds in this context) and we otherwise make sure that all code points from the input are either fully matched or not at all. -Therefore, the "cursor" of the engine can never stop in the middle of a code point, and so low surrogates are only visible if they were unpaired to begin with. -The only exception to this is when the cursor is at the beginning of the pattern, when using `find`. -In that case we cannot a priori prevent the JS engine from trying to find a match starting in the middle of a code point. -To address that, we have special a posteriori handling in `Pattern.execFind()`. - -#### Concretely - -A single code point that is a high surrogate `𝑥` is compiled to `(?:𝑥(?![ℒ]))`, where `ℒ` is `\uDC00-\uDFFF`, the range of all low surrogates. -The negative look-ahead group prevents a match from separating the high surrogate from a following low surrogate. - -A dot-all (in `codePointNotAmong("")`) is compiled to `(?:[^ℋ]|[ℋ](?:[ℒ]|(?![ℒ])))`, where `ℋ` is `\uD800-\uDBFF`, the range of all high surrogates. -This means either - -* any code unit that is not a high surrogate, or -* a high surrogate and a following low surrogate (taking a full code point is allowed), or -* a high surrogate that is not followed by a low surrogate (separating a surrogate pair is not allowed). - -We restrict the internal contract of `codePointNotAmong(𝑠)` to only take BMP code points that are not high surrogates, and compile it to the same as the dot-all but with the characters in `𝑠` excluded like the high surrogates: `(?:[^𝑠ℋ]|[ℋ](?:[ℒ]|(?![ℒ])))`. - -Since `UNICODE_CHARACTER_CLASS` is not supported, all but one call site of `codePointNotAmong` already respect that stricter contract. -The only one that does not is the call `codePointNotAmong(thisSegment)` inside `CharacterClassBuilder.conjunctResult()`. -To make that one compliant, we make sure not to add illegal code points in `thisSegment`. -To do that, we exploit the equivalences `[𝐴𝐵] = [𝐴]|[𝐵]` and `[^𝐴𝐵] = (?![𝐴])[𝐵]` where `𝐴` is an illegal code point to isolate it in a separate alternative, that we can compile as a single code point above. -For example, the character class `[k\uD834f]`, containing a high surrogate code point, is equivalent to `[\uD834]|[kf]`, which can be compiled as `(?:\uD834(?![ℒ]))|[kf])`. -That logic is implemented in `CharacterClassBuilder.addSingleCodePoint()`. - -Code point ranges that contain illegal code points are decomposed into the union of 4 (possibly empty) ranges: - -* one with only BMP code points below high surrogates, compiled as is -* one with high surrogates `𝑥-𝑦`, compiled to `(?:[𝑥-𝑦](?![ℒ]))` -* one with BMP code points above high surrogates, compiled as is -* one with supplementary code points `𝑥-𝑦`, where `𝑥` is the surrogate pair `𝑝𝑞` and `𝑦` is the pair `𝑠𝑡`, which is further decomposed into: - * the range `𝑝𝑞-𝑝\uDFFF`, compiled as `(?:𝑝[𝑞-\uDFFF])` - * the range `𝑝′\uDC00-𝑠′\uDFFF` where 𝑝′ = 𝑝+1 and 𝑠′ = 𝑠−1, compiled to `(?:[𝑝′-𝑠′][\uDC00-\uDFFF])` - * the range `𝑠\uDC00-𝑠𝑡`, compiled to `(?:𝑠[\uDC00-𝑡])` - -That logic is implemented in `CharacterClassBuilder.addCodePointRange()`. +However, experimentation and tests show that they behave exactly like ECMAScript. ## About code size diff --git a/linker/shared/src/test/scala/org/scalajs/linker/LibrarySizeTest.scala b/linker/shared/src/test/scala/org/scalajs/linker/LibrarySizeTest.scala index 9c013f8ad6..744dc5bcaf 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/LibrarySizeTest.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/LibrarySizeTest.scala @@ -70,8 +70,8 @@ class LibrarySizeTest { ) testLinkedSizes( - expectedFastLinkSize = 146573, - expectedFullLinkSizeWithoutClosure = 85204, + expectedFastLinkSize = 146195, + expectedFullLinkSizeWithoutClosure = 85130, expectedFullLinkSizeWithClosure = 21476, classDefs, moduleInitializers = MainTestModuleInitializers From 7f6835cb49fbc8a5e569e142a1da4effd80e9a3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 3 Feb 2025 22:33:56 +0100 Subject: [PATCH 7/8] Remove polyfills and implementations for ES 5.1 from the linker. --- .../scalajs/linker/analyzer/Analysis.scala | 4 - .../scalajs/linker/analyzer/Analyzer.scala | 5 - .../org/scalajs/linker/analyzer/Infos.scala | 15 +- .../linker/backend/emitter/ClassEmitter.scala | 2 +- .../linker/backend/emitter/CoreJSLib.scala | 343 ++---------------- .../backend/emitter/FunctionEmitter.scala | 203 +++-------- .../linker/backend/emitter/JSGen.scala | 34 +- .../backend/emitter/PolyfillableBuiltin.scala | 9 - .../linker/backend/emitter/SJSGen.scala | 15 +- 9 files changed, 108 insertions(+), 522 deletions(-) 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 781fc30c48..e2acd6da91 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 @@ -206,8 +206,6 @@ object Analysis { final case class DynamicImportWithoutModuleSupport(from: From) extends Error - final case class NewTargetWithoutES2015Support(from: From) extends Error - final case class ImportMetaWithoutESModule(from: From) extends Error final case class ExponentOperatorWithoutES2016Support(from: From) extends Error @@ -268,8 +266,6 @@ object Analysis { moduleIDs.map(_.id).mkString("[", ", ", "]") case DynamicImportWithoutModuleSupport(_) => "Uses dynamic import but module support is disabled" - case NewTargetWithoutES2015Support(_) => - "Uses new.target with an ECMAScript version older than ES 2015" case ImportMetaWithoutESModule(_) => "Uses import.meta with a module kind other than ESModule" case ExponentOperatorWithoutES2016Support(_) => 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 e1faf2fa1a..02e9e89794 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 @@ -1464,11 +1464,6 @@ private class AnalyzerRun(config: CommonPhaseConfig, initial: Boolean, } } - if ((globalFlags & ReachabilityInfo.FlagAccessedNewTarget) != 0 && - config.coreSpec.esFeatures.esVersion < ESVersion.ES2015) { - _errors ::= NewTargetWithoutES2015Support(from) - } - if ((globalFlags & ReachabilityInfo.FlagAccessedImportMeta) != 0 && config.coreSpec.moduleKind != ModuleKind.ESModule) { _errors ::= ImportMetaWithoutESModule(from) 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 c713d5799e..47e521d6cf 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 @@ -111,11 +111,10 @@ object Infos { type Flags = Int final val FlagAccessedClassClass = 1 << 0 - final val FlagAccessedNewTarget = 1 << 1 - final val FlagAccessedImportMeta = 1 << 2 - final val FlagUsedExponentOperator = 1 << 3 - final val FlagUsedClassSuperClass = 1 << 4 - final val FlagNeedsDesugaring = 1 << 5 + final val FlagAccessedImportMeta = 1 << 1 + final val FlagUsedExponentOperator = 1 << 2 + final val FlagUsedClassSuperClass = 1 << 3 + final val FlagNeedsDesugaring = 1 << 4 } /** Things from a given class that are reached by one method. */ @@ -377,9 +376,6 @@ object Infos { def addAccessedClassClass(): this.type = setFlag(ReachabilityInfo.FlagAccessedClassClass) - def addAccessNewTarget(): this.type = - setFlag(ReachabilityInfo.FlagAccessedNewTarget) - def addAccessImportMeta(): this.type = setFlag(ReachabilityInfo.FlagAccessedImportMeta) @@ -740,9 +736,6 @@ object Infos { builder.addStaticallyReferencedClass(field.name.className) // for the private name of the field builder.addFieldRead(field.name) - case JSNewTarget() => - builder.addAccessNewTarget() - case JSImportMeta() => builder.addAccessImportMeta() diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/ClassEmitter.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/ClassEmitter.scala index a27cb2dd1e..93ef040df2 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/ClassEmitter.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/ClassEmitter.scala @@ -412,7 +412,7 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { val args = if (semantics.productionMode) Nil else js.StringLiteral(description) :: Nil - genCallPolyfillableBuiltin(PolyfillableBuiltin.PrivateSymbolBuiltin, args: _*) + genCallGlobalBuiltin("Symbol", args: _*) } symbolValueWithGlobals.flatMap { symbolValue => diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/CoreJSLib.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/CoreJSLib.scala index 8b30a408f5..087e2bb2af 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/CoreJSLib.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/CoreJSLib.scala @@ -161,226 +161,6 @@ private[emitter] object CoreJSLib { private def defineJSBuiltinsSnapshotsAndPolyfills(): List[Tree] = { def genPolyfillFor(builtin: PolyfillableBuiltin): Tree = builtin match { - case ObjectIsBuiltin => - val x = varRef("x") - val y = varRef("y") - genArrowFunction(paramList(x, y), Return { - If(x === y, { - // +0.0 must be different from -0.0 - (x !== 0) || ((int(1) / x) === (int(1) / y)) - }, { - // NaN must be equal to NaN - (x !== x) && (y !== y) - }) - }) - - case ImulBuiltin => - val a = varRef("a") - val b = varRef("b") - val ah = varRef("ah") - val al = varRef("al") - val bh = varRef("bh") - val bl = varRef("bl") - genArrowFunction(paramList(a, b), Block( - const(ah, a >>> 16), - const(al, a & 0xffff), - const(bh, b >>> 16), - const(bl, b & 0xffff), - Return((al * bl) + (((ah * bl + al * bh) << 16) >>> 0) | 0) - )) - - case FroundBuiltin => - val v = varRef("v") - val Float32ArrayRef = globalRef("Float32Array") - - /* (function(array) { - * return function(v) { - * array[0] = v; - * return array[0]; - * } - * })(new Float32Array(1)) - * - * Allocating the Float32Array once and for all, and capturing it - * in an IIFE, is *much* faster than recreating it in every call of - * the polyfill (about an order of magnitude). - */ - val array = varRef("array") - val typedArrayPolyfillInner = genArrowFunction(paramList(v), { - Block( - BracketSelect(array, 0) := v, - Return(BracketSelect(array, 0)) - ) - }) - val typedArrayPolyfill = Apply( - genArrowFunction(paramList(array), Return(typedArrayPolyfillInner)), - New(Float32ArrayRef, 1 :: Nil) :: Nil) - - // scalastyle:off line.size.limit - /* Originally inspired by the Typed Array polyfills written by - * Joshua Bell: - * https://github.com/inexorabletash/polyfill/blob/a682f42c1092280bb01907c245979fb07219513d/typedarray.js#L150-L255 - * Then simplified quite a lot because - * 1) we do not need to produce the actual bit string that serves - * as storage of the floats, and - * 2) we are only interested in the float32 case. - * - * Eventually, the last bits of the above were replaced by an - * application of Veltkamp's splitting (see below). The inspiration - * for that use case came from core-js' implementation at - * https://github.com/zloirock/core-js/blob/a3f591658e063a6e2c2594ec3c80eff16340a98d/packages/core-js/internals/math-fround.js - * The code does not mention Veltkamp's splitting, but the PR - * discussion that led to it does, although with a question mark, - * and without any explanation of how/why it works: - * https://github.com/paulmillr/es6-shim/pull/140#issuecomment-91787165 - * We tracked down the descriptions and proofs relative to - * Veltkamp's splitting and re-derived an implementation from there. - * - * The direct tests for this polyfill are the tests for `toFloat` - * in org.scalajs.testsuite.compiler.DoubleTest. - */ - // scalastyle:on line.size.limit - val sign = varRef("sign") - val av = varRef("av") - val p = varRef("p") - - val Inf = double(Double.PositiveInfinity) - val overflowThreshold = double(3.4028235677973366e38) - val normalThreshold = double(1.1754943508222875e-38) - - val noTypedArrayPolyfill = genArrowFunction(paramList(v), Block( - v := +v, // turns `null` into +0, making sure not to deoptimize what follows - const(sign, If(v < 0, -1, 1)), // 1 for NaN, +0 and -0 - const(av, sign * v), // abs(v), or -0 if v is -0 - If(av >= overflowThreshold, { // also handles the case av === Infinity - Return(sign * Inf) - }, If(av >= normalThreshold, Block( - /* Here, we know that both the input and output are expressed - * in a Double normal form, so standard floating point - * algorithms from papers can be used. - * - * We use Veltkamp's splitting, as described and studied in - * Sylvie Boldo. - * Pitfalls of a Full Floating-Point Proof: Example on the - * Formal Proof of the Veltkamp/Dekker Algorithms - * https://dx.doi.org/10.1007/11814771_6 - * Section 3, with β = 2, t = 53, s = 53 - 24 = 29, x = av. - * 53 is the number of effective mantissa bits in a Double; - * 24 in a Float. - * - * ◦ is the round-to-nearest operation with a tie-breaking - * rule (in our case, break-to-even). - * - * Let C = βˢ + 1 = 536870913 - * p = ◦(x × C) - * q = ◦(x − p) - * x₁ = ◦(p + q) - * - * Boldo proves that x₁ is the (t-s)-bit float closest to x, - * using the same tie-breaking rule as ◦. Since (t-s) = 24, - * this is the closest float32 (with 24 mantissa bits), and - * therefore the correct result of `fround`. - * - * Boldo also proves that if the computation of x × C does not - * cause overflow, then none of the following operations will - * cause overflow. We know that x (av) is less than the - * overflowThreshold, and overflowThreshold × C does not - * overflow, so that computation can never cause an overflow. - * - * If the reader does not have access to Boldo's paper, they - * may refer instead to - * Claude-Pierre Jeannerod, Jean-Michel Muller, Paul Zimmermann. - * On various ways to split a floating-point number. - * ARITH 2018 - 25th IEEE Symposium on Computer Arithmetic, - * Jun 2018, Amherst (MA), United States. - * pp.53-60, 10.1109/ARITH.2018.8464793. hal-01774587v2 - * available at - * https://hal.inria.fr/hal-01774587v2/document - * Section III, although that paper defers some theorems and - * proofs to Boldo's. - */ - const(p, av * 536870913), - Return(sign * (p + (av - p))) - ), { - /* Here, the result is represented as a subnormal form in a - * float32 representation. - * - * We round `av` to the nearest multiple of the smallest - * positive Float value (i.e., `Float.MinPositiveValue`), - * breaking ties to an even multiple. - * - * We do this by leveraging the inherent loss of precision near - * the minimum positive *double* value: conceptually, we divide - * the value by - * Float.MinPositiveValue / Double.MinPositiveValue - * which will drop the excess precision, applying exactly the - * rounding strategy that we want. Then we multiply the value - * back by the same constant. - * - * However, `Float.MinPositiveValue / Double.MinPositiveValue` - * is not representable as a finite Double. Therefore, we - * instead use the *inverse* constant - * Double.MinPositiveValue / Float.MinPositiveValue - * and we first multiply by that constant, then divide by it. - * - * --- - * - * As an additional "hack", the input values NaN, +0 and -0 - * also fall in this code path. For them, this computation - * happens to be an identity, and is therefore correct as well. - */ - val roundingFactor = double(Double.MinPositiveValue / Float.MinPositiveValue.toDouble) - Return(sign * ((av * roundingFactor) / roundingFactor)) - })) - )) - - If(typeof(Float32ArrayRef) !== str("undefined"), - typedArrayPolyfill, noTypedArrayPolyfill) - - case PrivateSymbolBuiltin => - /* function privateJSFieldSymbol(description) { - * function rand32() { - * const s = ((Math.random() * 4294967296.0) >>> 0).toString(16); - * return "00000000".substring(s.length) + s; - * } - * return description + rand32() + rand32() + rand32() + rand32(); - * } - * - * In production mode, we remove the `description` parameter. - */ - val description = varRef("description") - val rand32 = varRef("rand32") - val s = varRef("s") - - val theParamList = - if (semantics.productionMode) Nil - else paramList(description) - - genArrowFunction(theParamList, Block( - FunctionDef(rand32.ident, Nil, None, Block( - genLet(s.ident, mutable = false, { - val randomDouble = - Apply(genIdentBracketSelect(MathRef, "random"), Nil) - val randomUint = - (randomDouble * double(4294967296.0)) >>> 0 - Apply(genIdentBracketSelect(randomUint, "toString"), 16 :: Nil) - }), - { - val padding = Apply( - genIdentBracketSelect(str("00000000"), "substring"), - genIdentBracketSelect(s, "length") :: Nil) - Return(padding + s) - } - )), - { - val callRand32 = Apply(rand32, Nil) - val rand128 = callRand32 + callRand32 + callRand32 + callRand32 - val result = - if (semantics.productionMode) rand128 - else description + rand128 - Return(result) - } - )) - case GetOwnPropertyDescriptorsBuiltin => /* getOwnPropertyDescriptors = (() => { * // Fetch or polyfill Reflect.ownKeys @@ -952,30 +732,13 @@ private[emitter] object CoreJSLib { (abs & bigInt(~0xffffL)) | bigInt(0x8000L) })), const(absR, Apply(NumberRef, y :: Nil)), - Return(genCallPolyfillableBuiltin(FroundBuiltin, If(x < bigInt(0L), -absR, absR))) + Return(genFround(If(x < bigInt(0L), -absR, absR))) ) } ) } private def defineES2015LikeHelpers(): List[Tree] = ( - condDefs(esVersion < ESVersion.ES2015)( - defineFunction2(VarField.newJSObjectWithVarargs) { (ctor, args) => - val instance = varRef("instance") - val result = varRef("result") - - // This basically emulates the ECMAScript specification for 'new'. - Block( - const(instance, Apply(genIdentBracketSelect(ObjectRef, "create"), ctor.prototype :: Nil)), - const(result, Apply(genIdentBracketSelect(ctor, "apply"), instance :: args :: Nil)), - Switch(typeof(result), - List("string", "number", "boolean", "undefined").map(str(_) -> Skip()) :+ - str("symbol") -> Return(instance), - Return(If(result === Null(), instance, result))) - ) - } - ) ::: - defineFunction2(VarField.resolveSuperRef) { (superClass, propName) => val getPrototypeOf = varRef("getPrototypeOf") val getOwnPropertyDescriptor = varRef("getOwnPropertyDescriptor") @@ -1071,12 +834,7 @@ private[emitter] object CoreJSLib { ) } ::: - condDefs(esVersion < ESVersion.ES2015)( - defineFunction5(VarField.systemArraycopy) { (src, srcPos, dest, destPos, length) => - genCallHelper(VarField.arraycopyGeneric, src.u, srcPos, dest.u, destPos, length) - } - ) ::: - condDefs(esVersion >= ESVersion.ES2015 && nullPointers != CheckedBehavior.Unchecked)( + condDefs(nullPointers != CheckedBehavior.Unchecked)( defineFunction5(VarField.systemArraycopy) { (src, srcPos, dest, destPos, length) => genSyntheticPropApply(src, SyntheticProperty.copyTo, srcPos, dest, destPos, length) } @@ -1119,7 +877,7 @@ private[emitter] object CoreJSLib { // Both values have the same "data" (could also be falsy values) If(srcData && (srcData DOT cpn.isArrayClass), { // Fast path: the values are array of the same type - if (esVersion >= ESVersion.ES2015 && nullPointers == CheckedBehavior.Unchecked) + if (nullPointers == CheckedBehavior.Unchecked) genSyntheticPropApply(src, SyntheticProperty.copyTo, srcPos, dest, destPos, length) else genCallHelper(VarField.systemArraycopy, src, srcPos, dest, destPos, length) @@ -1152,7 +910,7 @@ private[emitter] object CoreJSLib { val description = varRef("description") val hash = varRef("hash") - def functionSkeleton(defaultImpl: Tree): Function = { + val implFunction: Function = { def genHijackedMethodApply(className: ClassName, arg: Tree): Tree = Apply(globalVar(VarField.f, (className, hashCodeMethodName)), arg :: Nil) @@ -1219,71 +977,33 @@ private[emitter] object CoreJSLib { str("boolean") -> Return(If(obj, 1231, 1237)), str("undefined") -> Return(0), str("symbol") -> genReturnSymbolHashCode() - ), defaultImpl) - }) - } - - def weakMapBasedFunction: Function = { - functionSkeleton { - If(obj === Null(), { - Return(0) - }, { - Block( - let(hash, Apply(genIdentBracketSelect(idHashCodeMap, "get"), obj :: Nil)), - If(hash === Undefined(), { - Block( - hash := ((lastIDHash + 1) | 0), - lastIDHash := hash, - Apply(genIdentBracketSelect(idHashCodeMap, "set"), obj :: hash :: Nil) - ) - }, { - Skip() - }), - Return(hash) - ) - }) - } - } - - def fieldBasedFunction: Function = { - functionSkeleton { - If(genIsScalaJSObject(obj), { - Block( - let(hash, genIdentBracketSelect(obj, "$idHashCode$0")), - If(hash !== Undefined(), { - Return(hash) - }, { - If(!Apply(genIdentBracketSelect(ObjectRef, "isSealed"), obj :: Nil), { + ), { + If(obj === Null(), { + Return(0) + }, { + Block( + let(hash, Apply(genIdentBracketSelect(idHashCodeMap, "get"), obj :: Nil)), + If(hash === Undefined(), { Block( hash := ((lastIDHash + 1) | 0), lastIDHash := hash, - genIdentBracketSelect(obj, "$idHashCode$0") := hash, - Return(hash) + Apply(genIdentBracketSelect(idHashCodeMap, "set"), obj :: hash :: Nil) ) }, { - Return(42) - }) - }) - ) - }, { - If(obj === Null(), 0, 42) + Skip() + }), + Return(hash) + ) + }) }) - } + }) } List( let(lastIDHash, 0), - const(idHashCodeMap, - if (esVersion >= ESVersion.ES2015) New(WeakMapRef, Nil) - else If(typeof(WeakMapRef) !== str("undefined"), New(WeakMapRef, Nil), Null())) + const(idHashCodeMap, New(WeakMapRef, Nil)) ) ::: ( - if (esVersion >= ESVersion.ES2015) { - val f = weakMapBasedFunction - defineFunction(VarField.systemIdentityHashCode, f.args, f.body) - } else { - extractWithGlobals(globalVarDef(VarField.systemIdentityHashCode, CoreVar, - If(idHashCodeMap !== Null(), weakMapBasedFunction, fieldBasedFunction))) - } + defineFunction(VarField.systemIdentityHashCode, implFunction.args, implFunction.body) ) } ) @@ -1307,7 +1027,7 @@ private[emitter] object CoreJSLib { ) ::: defineFunction1(VarField.isFloat) { v => Return((typeof(v) === str("number")) && - ((v !== v) || (genCallPolyfillableBuiltin(FroundBuiltin, v) === v))) + ((v !== v) || (genFround(v) === v))) } } @@ -1425,7 +1145,7 @@ private[emitter] object CoreJSLib { Nil } - val copyTo = if (esVersion >= ESVersion.ES2015) { + val copyTo = { val srcPos = varRef("srcPos") val dest = varRef("dest") val destPos = varRef("destPos") @@ -1433,7 +1153,7 @@ private[emitter] object CoreJSLib { val copyToName = genSyntheticPropertyForDef(SyntheticProperty.copyTo) - val methodDef = MethodDef(static = false, copyToName, + MethodDef(static = false, copyToName, paramList(srcPos, dest, destPos, length), None, { if (isTypedArray) { Block( @@ -1453,9 +1173,6 @@ private[emitter] object CoreJSLib { dest.u, destPos, length) } }) - methodDef :: Nil - } else { - Nil } val cloneMethodIdent = genMethodIdentForDef(cloneMethodName, NoOriginalName) @@ -1464,7 +1181,7 @@ private[emitter] object CoreJSLib { Apply(genIdentBracketSelect(This().u, "slice"), Nil) :: Nil)) }) - val members = getAndSet ::: copyTo ::: clone :: Nil + val members = getAndSet ::: copyTo :: clone :: Nil if (useClassesForRegularClasses) { extractWithGlobals(globalClassDef(VarField.ac, componentTypeRef, @@ -1762,7 +1479,7 @@ private[emitter] object CoreJSLib { Nil } - val copyTo = if (esVersion >= ESVersion.ES2015) { + val copyTo = { val srcPos = varRef("srcPos") val dest = varRef("dest") val destPos = varRef("destPos") @@ -1770,14 +1487,11 @@ private[emitter] object CoreJSLib { val copyToName = genSyntheticPropertyForDef(SyntheticProperty.copyTo) - val methodDef = MethodDef(static = false, copyToName, + MethodDef(static = false, copyToName, paramList(srcPos, dest, destPos, length), None, { genCallHelper(VarField.arraycopyGeneric, This().u, srcPos, dest.u, destPos, length) }) - methodDef :: Nil - } else { - Nil } val cloneMethodIdent = genMethodIdentForDef(cloneMethodName, NoOriginalName) @@ -1786,7 +1500,7 @@ private[emitter] object CoreJSLib { Apply(genIdentBracketSelect(This().u, "slice"), Nil) :: Nil)) }) - val members = set ::: copyTo ::: clone :: Nil + val members = set ::: copyTo :: clone :: Nil if (useClassesForRegularClasses) { Block( @@ -2171,6 +1885,9 @@ private[emitter] object CoreJSLib { extractWithGlobals(sjsGen.genCallPolyfillableBuiltin(builtin, args: _*)) } + private def genFround(arg: Tree): Tree = + extractWithGlobals(jsGen.genFround(arg)) + private def maybeWrapInUBE(behavior: CheckedBehavior, exception: Tree): Tree = { if (behavior == CheckedBehavior.Fatal) { genScalaClassNew(UndefinedBehaviorErrorClass, 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 e36033eb66..98a88e6229 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 @@ -335,9 +335,6 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { private implicit val globalRefTracking: GlobalRefTracking = GlobalRefTracking.All - // For convenience - private val es2015 = esFeatures.esVersion >= ESVersion.ES2015 - // Name management /** Whether we are running in the "optimistic naming" run. @@ -534,18 +531,8 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { val jsParams = params.map(transformParamDef(_)) - if (es2015) { - val jsRestParam = restParam.map(transformParamDef(_)) - js.Function(arrow, jsParams, jsRestParam, cleanedNewBody) - } else { - val patchedBody = restParam.fold { - cleanedNewBody - } { restParam => - js.Block(makeExtractRestParam(restParam, jsParams.size), cleanedNewBody) - } - - js.Function(arrow, jsParams, None, patchedBody) - } + val jsRestParam = restParam.map(transformParamDef(_)) + js.Function(arrow, jsParams, jsRestParam, cleanedNewBody) } private def makeExtractRestParam(restParamDef: ParamDef, offset: Int)( @@ -852,7 +839,7 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { val jsArgs = newArgs.map(transformExprNoChar(_)) def genUnchecked(): js.Tree = { - if (esFeatures.esVersion >= ESVersion.ES2015 && semantics.nullPointers == CheckedBehavior.Unchecked) + if (semantics.nullPointers == CheckedBehavior.Unchecked) genSyntheticPropApply(jsArgs.head, SyntheticProperty.copyTo, jsArgs.tail) else genCallHelper(VarField.systemArraycopy, jsArgs: _*) @@ -1030,7 +1017,7 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { NewArray(tpe, rec(length)) case ArrayValue(tpe, elems) => ArrayValue(tpe, recs(elems)) - case JSArrayConstr(items) if !needsToTranslateAnySpread(items) => + case JSArrayConstr(items) => JSArrayConstr(recsOrSpread(items)) case arg @ JSObjectConstr(items) @@ -1225,7 +1212,7 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { allowSideEffects || behavior == Unchecked def testJSArg(tree: TreeOrJSSpread): Boolean = tree match { - case JSSpread(items) => es2015 && test(items) + case JSSpread(items) => test(items) case tree: Tree => test(tree) } @@ -1349,8 +1336,6 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { allowSideEffects case JSNew(fun, args) => allowSideEffects && test(fun) && (args.forall(testJSArg)) - case Transient(JSNewVararg(ctor, argArray)) => - allowSideEffects && test(ctor) && test(argArray) case JSPrivateSelect(qualifier, _) => allowSideEffects && test(qualifier) case JSSelect(qualifier, item) => @@ -1830,51 +1815,22 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { // JavaScript expressions (if we reach here their arguments are not expressions) case JSNew(ctor, args) => - if (needsToTranslateAnySpread(args)) { - redo { - Transient(JSNewVararg(ctor, spreadToArgArray(args))) - } - } else { - unnestOrSpread(ctor :: Nil, args) { (newCtor0, newArgs, env) => - val newCtor :: Nil = newCtor0 - redo(JSNew(newCtor, newArgs))(env) - } - } - - case Transient(JSNewVararg(ctor, argArray)) => - unnest(ctor, argArray) { (newCtor, newArgArray, env) => - redo(Transient(JSNewVararg(newCtor, newArgArray)))(env) + unnestOrSpread(ctor :: Nil, args) { (newCtor0, newArgs, env) => + val newCtor :: Nil = newCtor0 + redo(JSNew(newCtor, newArgs))(env) } case JSFunctionApply(fun, args) => - if (needsToTranslateAnySpread(args)) { - redo { - JSMethodApply(fun, StringLiteral("apply"), - List(Undefined(), spreadToArgArray(args))) - } - } else { - unnestOrSpread(fun :: Nil, args) { (newFun0, newArgs, env) => - val newFun :: Nil = newFun0 - redo(JSFunctionApply(newFun, newArgs))(env) - } + unnestOrSpread(fun :: Nil, args) { (newFun0, newArgs, env) => + val newFun :: Nil = newFun0 + redo(JSFunctionApply(newFun, newArgs))(env) } case JSMethodApply(receiver, method, args) => - if (needsToTranslateAnySpread(args)) { - withTempVar(receiver) { newReceiver => - redo { - JSMethodApply( - JSSelect(newReceiver, method), - StringLiteral("apply"), - List(newReceiver, spreadToArgArray(args))) - } - } - } else { - unnestOrSpread(receiver :: method :: Nil, args) { - (newReceiverAndMethod, newArgs, env) => - val newReceiver :: newMethod :: Nil = newReceiverAndMethod - redo(JSMethodApply(newReceiver, newMethod, newArgs))(env) - } + unnestOrSpread(receiver :: method :: Nil, args) { + (newReceiverAndMethod, newArgs, env) => + val newReceiver :: newMethod :: Nil = newReceiverAndMethod + redo(JSMethodApply(newReceiver, newMethod, newArgs))(env) } case JSSuperSelect(superClass, qualifier, item) => @@ -1937,14 +1893,8 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { } case JSArrayConstr(items) => - if (needsToTranslateAnySpread(items)) { - redo { - spreadToArgArray(items) - } - } else { - unnestOrSpread(items) { (newItems, env) => - redo(JSArrayConstr(newItems))(env) - } + unnestOrSpread(items) { (newItems, env) => + redo(JSArrayConstr(newItems))(env) } case rhs @ JSObjectConstr(fields) => @@ -2044,9 +1994,6 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { js.Block(varDef, body) } - private def needsToTranslateAnySpread(args: List[TreeOrJSSpread]): Boolean = - !es2015 && args.exists(_.isInstanceOf[JSSpread]) - private def spreadToArgArray(args: List[TreeOrJSSpread])( implicit env: Env, pos: Position): Tree = { var reversedParts: List[Tree] = Nil @@ -2080,23 +2027,17 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { } } - /** Tests whether a [[JSObjectConstr]] must be desugared. */ + /** Tests whether a [[JSObjectConstr]] must be desugared. + * + * This is true iff there is at least one duplicate string literal + * property name. + */ private def doesObjectConstrRequireDesugaring( tree: JSObjectConstr): Boolean = { - def computedNamesAllowed: Boolean = - es2015 - - def hasComputedName: Boolean = - tree.fields.exists(!_._1.isInstanceOf[StringLiteral]) - - def hasDuplicateNonComputedProp: Boolean = { - val names = tree.fields.collect { - case (StringLiteral(name), _) => name - } - names.toSet.size != names.size + val names = tree.fields.collect { + case (StringLiteral(name), _) => name } - - (!computedNamesAllowed && hasComputedName) || hasDuplicateNonComputedProp + names.toSet.size != names.size } /** Evaluates `expr` and stores the result in a temp, then evaluates the @@ -2122,7 +2063,6 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { def transformJSArg(tree: TreeOrJSSpread)(implicit env: Env): js.Tree = { tree match { case JSSpread(items) => - assert(es2015) js.Spread(transformExprNoChar(items))(tree.pos) case tree: Tree => transformExprNoChar(tree) @@ -2527,8 +2467,7 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { js.BinaryOp(if (op == ===) JSBinaryOp.=== else JSBinaryOp.!==, newLhs, newRhs) } else { - val objectIsCall = - genCallPolyfillableBuiltin(ObjectIsBuiltin, newLhs, newRhs) + val objectIsCall = genCallNamespacedBuiltin("Object", "is", newLhs, newRhs) if (op == ===) objectIsCall else js.UnaryOp(JSUnaryOp.!, objectIsCall) } @@ -2558,7 +2497,7 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { case _ => or0(js.BinaryOp(JSBinaryOp.-, newLhs, newRhs)) } case Int_* => - genCallPolyfillableBuiltin(ImulBuiltin, newLhs, newRhs) + genCallNamespacedBuiltin("Math", "imul", newLhs, newRhs) case Int_/ => rhs match { case IntLiteral(r) if r != 0 => @@ -2821,40 +2760,11 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { case Transient(ArrayToTypedArray(expr, primRef)) => val value = transformExprNoChar(checkNotNull(expr)) val valueUnderlying = genSyntheticPropSelect(value, SyntheticProperty.u) - - if (es2015) { - js.Apply(genIdentBracketSelect(valueUnderlying, "slice"), Nil) - } else { - val typedArrayClass = extractWithGlobals(typedArrayRef(primRef).get) - js.New(typedArrayClass, valueUnderlying :: Nil) - } + js.Apply(genIdentBracketSelect(valueUnderlying, "slice"), Nil) case Transient(TypedArrayToArray(expr, primRef)) => val value = transformExprNoChar(expr) - - val arrayValue = if (es2015) { - js.Apply(genIdentBracketSelect(value, "slice"), Nil) - } else { - /* Array.prototype.slice.call(value) - * - * This works because: - * - If the `this` value of `slice` is not a proper `Array`, the - * result will be created through `ArrayCreate` without explicit - * prototype, which creates a new proper `Array`. - * - To know what elements to copy, `slice` does not check that its - * `this` value is a proper `Array`. Instead, it simply assumes - * that it is an "Array-like", and reads the `"length"` property - * as well as indexed properties. Both of those work on a typed - * array. - * - * Reference: - * http://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.slice - * (also follow the link for `ArraySpeciesCreate`) - */ - js.Apply(genIdentBracketSelect( - genIdentBracketSelect(genGlobalVarRef("Array").prototype, "slice"), "call"), - value :: Nil) - } + val arrayValue = js.Apply(genIdentBracketSelect(value, "slice"), Nil) js.New(genArrayConstrOf(ArrayTypeRef(primRef, 1)), arrayValue :: Nil) // JavaScript expressions @@ -2862,11 +2772,6 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { case JSNew(constr, args) => js.New(transformExprNoChar(constr), args.map(transformJSArg)) - case Transient(JSNewVararg(constr, argsArray)) => - assert(!es2015, s"generated a JSNewVargs with ES 2015+ at ${tree.pos}") - genCallHelper(VarField.newJSObjectWithVarargs, - transformExprNoChar(constr), transformExprNoChar(argsArray)) - case JSPrivateSelect(qualifier, field) => genJSPrivateSelect(transformExprNoChar(qualifier), field) @@ -3242,6 +3147,17 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { private def genGetDataOf(jlClassValue: js.Tree)(implicit pos: Position): js.Tree = genSyntheticPropSelect(jlClassValue, SyntheticProperty.data) + private def genCallGlobalBuiltin(globalVar: String, args: js.Tree*)( + implicit pos: Position): js.Tree = { + extractWithGlobals(jsGen.genCallGlobalBuiltin(globalVar, args: _*)) + } + + private def genCallNamespacedBuiltin(namespaceGlobalVar: String, builtinName: String, args: js.Tree*)( + implicit pos: Position): js.Tree = { + extractWithGlobals( + jsGen.genCallNamespacedBuiltin(namespaceGlobalVar, builtinName, args: _*)) + } + private def genCallPolyfillableBuiltin( builtin: PolyfillableBuiltin, args: js.Tree*)( implicit pos: Position): js.Tree = { @@ -3249,7 +3165,7 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { } private def genFround(arg: js.Tree)(implicit pos: Position): js.Tree = - genCallPolyfillableBuiltin(FroundBuiltin, arg) + extractWithGlobals(jsGen.genFround(arg)) private def wrapBigInt32(tree: js.Tree)(implicit pos: Position): js.Tree = wrapBigIntN(32, tree) @@ -3257,20 +3173,14 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { private def wrapBigInt64(tree: js.Tree)(implicit pos: Position): js.Tree = wrapBigIntN(64, tree) - private def wrapBigIntN(n: Int, tree: js.Tree)( - implicit pos: Position): js.Tree = { - js.Apply(genIdentBracketSelect(genGlobalVarRef("BigInt"), "asIntN"), - List(js.IntLiteral(n), tree)) - } + private def wrapBigIntN(n: Int, tree: js.Tree)(implicit pos: Position): js.Tree = + genCallNamespacedBuiltin("BigInt", "asIntN", js.IntLiteral(n), tree) private def wrapBigIntU64(tree: js.Tree)(implicit pos: Position): js.Tree = wrapBigIntUN(64, tree) - private def wrapBigIntUN(n: Int, tree: js.Tree)( - implicit pos: Position): js.Tree = { - js.Apply(genIdentBracketSelect(genGlobalVarRef("BigInt"), "asUintN"), - List(js.IntLiteral(n), tree)) - } + private def wrapBigIntUN(n: Int, tree: js.Tree)(implicit pos: Position): js.Tree = + genCallNamespacedBuiltin("BigInt", "asUintN", js.IntLiteral(n), tree) } } @@ -3311,29 +3221,6 @@ private object FunctionEmitter { out.print(ident.name) } - private final case class JSNewVararg(ctor: Tree, argArray: Tree) - extends Transient.Value { - val tpe: Type = AnyType - - def traverse(traverser: Traverser): Unit = { - traverser.traverse(ctor) - traverser.traverse(argArray) - } - - def transform(transformer: Transformer)(implicit pos: Position): Tree = { - Transient(JSNewVararg(transformer.transform(ctor), - transformer.transform(argArray))) - } - - def printIR(out: IRTreePrinter): Unit = { - out.print("new (") - out.print(ctor) - out.print(")(...") - out.print(argArray) - out.print(')') - } - } - /** A left hand side that can be pushed into a right hand side tree. */ sealed abstract class Lhs { def hasNothingType: Boolean = false diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/JSGen.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/JSGen.scala index f14e088eac..46b5b50834 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/JSGen.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/JSGen.scala @@ -42,10 +42,9 @@ private[emitter] final class JSGen(val config: Emitter.Config) { * for a rationale. * * Note: top-level exports in Script (`NoModule`) mode are always - * emitted as `let`s under ECMAScript 2015 semantics, irrespective of this - * value. + * emitted as `let`s, irrespective of this value. */ - val useLets = esFeatures.esVersion >= ESVersion.ES2015 && !esFeatures.avoidLetsAndConsts + val useLets = !esFeatures.avoidLetsAndConsts def genConst(name: MaybeDelayedIdent, rhs: Tree)(implicit pos: Position): LocalDef = genLet(name, mutable = false, rhs) @@ -88,16 +87,29 @@ private[emitter] final class JSGen(val config: Emitter.Config) { BracketSelect(qual, StringLiteral(item)) } - /** Generates an arrow function if supported by the ES version. - * - * This is independent of the ECMAScript 2015 *semantics*. This method must - * not be used for closures that are *specified* to be arrow functions in - * ES 2015 but `function`s in ES 5.1 semantics. In other words, it must not - * be used to compile `ir.Trees.Closure`s. - */ + def genCallGlobalBuiltin(globalVar: String, args: Tree*)( + implicit tracking: GlobalRefTracking, pos: Position): WithGlobals[Tree] = { + globalRef(globalVar).map { global => + Apply(global, args.toList) + } + } + + def genCallNamespacedBuiltin(namespaceGlobalVar: String, builtinName: String, args: Tree*)( + implicit tracking: GlobalRefTracking, pos: Position): WithGlobals[Tree] = { + globalRef(namespaceGlobalVar).map { namespace => + Apply(genIdentBracketSelect(namespace, builtinName), args.toList) + } + } + + def genFround(arg: Tree)( + implicit tracking: GlobalRefTracking, pos: Position): WithGlobals[Tree] = { + genCallNamespacedBuiltin("Math", "fround", arg) + } + + /** Generates an arrow function. */ def genArrowFunction(args: List[ParamDef], restParam: Option[ParamDef], body: Tree)( implicit pos: Position): Function = { - Function(esFeatures.esVersion >= ESVersion.ES2015, args, restParam, body) + Function(arrow = true, args, restParam, body) } def genDefineProperty(obj: Tree, prop: Tree, descriptor: List[(String, Tree)])( diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/PolyfillableBuiltin.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/PolyfillableBuiltin.scala index 908d264a9f..82d6d3ce06 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/PolyfillableBuiltin.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/PolyfillableBuiltin.scala @@ -19,10 +19,6 @@ private[emitter] sealed abstract class PolyfillableBuiltin( private[emitter] object PolyfillableBuiltin { lazy val All: List[PolyfillableBuiltin] = List( - ObjectIsBuiltin, - ImulBuiltin, - FroundBuiltin, - PrivateSymbolBuiltin, GetOwnPropertyDescriptorsBuiltin ) @@ -34,11 +30,6 @@ private[emitter] object PolyfillableBuiltin { val builtinName: String, polyfillField: VarField, availableInESVersion: ESVersion) extends PolyfillableBuiltin(polyfillField, availableInESVersion) - case object ObjectIsBuiltin extends NamespacedBuiltin("Object", "is", VarField.is, ESVersion.ES2015) - case object ImulBuiltin extends NamespacedBuiltin("Math", "imul", VarField.imul, ESVersion.ES2015) - case object FroundBuiltin extends NamespacedBuiltin("Math", "fround", VarField.fround, ESVersion.ES2015) - case object PrivateSymbolBuiltin - extends GlobalVarBuiltin("Symbol", VarField.privateJSFieldSymbol, ESVersion.ES2015) case object GetOwnPropertyDescriptorsBuiltin extends NamespacedBuiltin("Object", "getOwnPropertyDescriptors", VarField.getOwnPropertyDescriptors, ESVersion.ES2017) } diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/SJSGen.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/SJSGen.scala index 2ff301a7bf..a57d2372f2 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/SJSGen.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/SJSGen.scala @@ -262,9 +262,8 @@ private[emitter] final class SJSGen( def getArrayUnderlyingTypedArrayClassRef(elemTypeRef: NonArrayTypeRef)( implicit tracking: GlobalRefTracking, pos: Position): Option[WithGlobals[VarRef]] = { elemTypeRef match { - case _ if esFeatures.esVersion < ESVersion.ES2015 => None - case primRef: PrimRef => typedArrayRef(primRef) - case _ => None + case primRef: PrimRef => typedArrayRef(primRef) + case _ => None } } @@ -511,12 +510,10 @@ private[emitter] final class SJSGen( case CharType => wg(genCallHelper(VarField.uC, expr)) case ByteType | ShortType| IntType => wg(expr | 0) case LongType => wg(genCallHelper(VarField.uJ, expr)) + case FloatType => genFround(expr) case DoubleType => wg(UnaryOp(irt.JSUnaryOp.+, expr)) case StringType => wg(expr || StringLiteral("")) - case FloatType => - genCallPolyfillableBuiltin(FroundBuiltin, expr) - case VoidType | NullType | NothingType | AnyNotNullType | ClassType(_, false) | ArrayType(_, false) | _:RecordType => throw new AssertionError(s"Unexpected type $tpe in genAsInstanceOf") @@ -612,11 +609,9 @@ private[emitter] final class SJSGen( if (esFeatures.esVersion >= builtin.availableInESVersion) { builtin match { case builtin: GlobalVarBuiltin => - for (global <- globalRef(builtin.globalVar)) yield - Apply(global, args.toList) + genCallGlobalBuiltin(builtin.globalVar, args: _*) case builtin: NamespacedBuiltin => - for (namespace <- globalRef(builtin.namespaceGlobalVar)) yield - Apply(genIdentBracketSelect(namespace, builtin.builtinName), args.toList) + genCallNamespacedBuiltin(builtin.namespaceGlobalVar, builtin.builtinName, args: _*) } } else { WithGlobals(genCallHelper(builtin.polyfillField, args: _*)) From aabf8276cada89d62152333b37d4f644289a8986 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 3 Feb 2025 22:38:49 +0100 Subject: [PATCH 8/8] Remove code paths for ES 5.1 from the build. --- project/Build.scala | 2 - project/NodeJSEnvForcePolyfills.scala | 50 +------------------ .../testsuite/jsinterop/NewTargetTest.scala | 0 3 files changed, 1 insertion(+), 51 deletions(-) rename test-suite/js/src/test/{require-new-target => scala}/org/scalajs/testsuite/jsinterop/NewTargetTest.scala (100%) diff --git a/project/Build.scala b/project/Build.scala index e832dbef5d..43d4861b57 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -2252,8 +2252,6 @@ object Build { val isWebAssembly = linkerConfig.experimentalUseWebAssembly collectionsEraDependentDirectory(scalaV, testDir) :: - includeIf(testDir / "require-new-target", - esVersion >= ESVersion.ES2015) ::: includeIf(testDir / "require-exponent-op", esVersion >= ESVersion.ES2016) ::: includeIf(testDir / "require-modules", diff --git a/project/NodeJSEnvForcePolyfills.scala b/project/NodeJSEnvForcePolyfills.scala index 6859a3aa7b..271f9ded27 100644 --- a/project/NodeJSEnvForcePolyfills.scala +++ b/project/NodeJSEnvForcePolyfills.scala @@ -15,13 +15,7 @@ final class NodeJSEnvForcePolyfills(esVersion: ESVersion, config: NodeJSEnv.Conf val name: String = s"Node.js forcing polyfills for $esVersion" - // Deactivate source maps if esVersion < ES2015 because source-map-support requires `Map` - private val nodeJSEnv = { - val config1 = - if (esVersion >= ESVersion.ES2015) config - else config.withSourceMap(false) - new NodeJSEnv(config1) - } + private val nodeJSEnv = new NodeJSEnv(config) def start(input: Seq[Input], runConfig: RunConfig): JSRun = nodeJSEnv.start(forcePolyfills +: input, runConfig) @@ -43,42 +37,6 @@ final class NodeJSEnvForcePolyfills(esVersion: ESVersion, config: NodeJSEnv.Conf var script = "" - if (esVersion < ES2015) { - script += """ - |delete Object.is; - | - |delete Reflect.ownKeys; - | - |delete Math.fround; - |delete Math.imul; - |delete Math.clz32; - |delete Math.log10; - |delete Math.log1p; - |delete Math.cbrt; - |delete Math.hypot; - |delete Math.expm1; - |delete Math.sinh; - |delete Math.cosh; - |delete Math.tanh; - | - |delete global.Map; - |delete global.Promise; - |delete global.Set; - |delete global.Symbol; - | - |delete global.Int8Array; - |delete global.Int16Array; - |delete global.Int32Array; - |delete global.Uint8Array; - |delete global.Uint16Array; - |delete global.Uint32Array; - |delete global.Float32Array; - |delete global.Float64Array; - | - |delete String.prototype.repeat; - """.stripMargin - } - if (esVersion < ES2017) { script += """ |delete Object.getOwnPropertyDescriptors; @@ -90,12 +48,6 @@ final class NodeJSEnvForcePolyfills(esVersion: ESVersion, config: NodeJSEnv.Conf |global.RegExp = (function(OrigRegExp) { | return function RegExp(pattern, flags) { | if (typeof flags === 'string') { - |${cond(ES2015, """ - | if (flags.indexOf('u') >= 0) - | throw new SyntaxError("unsupported flag 'u'"); - | if (flags.indexOf('y') >= 0) - | throw new SyntaxError("unsupported flag 'y'"); - |""".stripMargin)} |${cond(ES2018, """ | if (flags.indexOf('s') >= 0) | throw new SyntaxError("unsupported flag 's'"); diff --git a/test-suite/js/src/test/require-new-target/org/scalajs/testsuite/jsinterop/NewTargetTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/NewTargetTest.scala similarity index 100% rename from test-suite/js/src/test/require-new-target/org/scalajs/testsuite/jsinterop/NewTargetTest.scala rename to test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/NewTargetTest.scala