diff --git a/CODINGSTYLE.md b/CODINGSTYLE.md index da517b020e..47cb6a448a 100644 --- a/CODINGSTYLE.md +++ b/CODINGSTYLE.md @@ -63,13 +63,13 @@ f( arg1, arg2, arg3, - arg4 + arg4, ) ``` Notes about the list style: * The parentheses must be on individual lines. -* A trailing comma will become mandatory if/once we drop 2.11. +* The trailing comma is mandatory. * This style is relatively new, so a lot of code does not comply to it; apply the boy scout rule where this does not cause unnecessary diffs. ### Blank lines diff --git a/Jenkinsfile b/Jenkinsfile index 9346cf8139..7f87dfdae4 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -156,6 +156,13 @@ def Tasks = [ ++$scala testingExample$v/testHtmlJSDom && sbtretry ++$scala testSuiteJVM$v/test testSuiteExJVM$v/test && sbtretry ++$scala testSuite$v/test && + sbtretry ++$scala \ + testSuite$v/saveForStabilityTest \ + testSuite$v/checkStability \ + testSuite$v/forceRelinkForStabilityTest \ + testSuite$v/checkStability \ + testSuite$v/clean \ + testSuite$v/checkStability && sbtretry ++$scala testSuiteEx$v/test && sbtretry 'set scalaJSStage in Global := FullOptStage' \ ++$scala testSuiteEx$v/test && @@ -230,11 +237,6 @@ def Tasks = [ 'set scalaJSLinkerConfig in $testSuite.v$v ~= (_.withESFeatures(_.withAvoidLetsAndConsts(false).withAvoidClasses(false)))' \ 'set scalaJSStage in Global := FullOptStage' \ ++$scala $testSuite$v/test && - sbtretry 'set scalacOptions in $testSuite.v$v += "-Xexperimental"' \ - ++$scala $testSuite$v/test && - sbtretry 'set scalacOptions in $testSuite.v$v += "-Xexperimental"' \ - 'set scalaJSStage in Global := FullOptStage' \ - ++$scala $testSuite$v/test && sbtretry 'set scalaJSLinkerConfig in $testSuite.v$v ~= (_.withModuleKind(ModuleKind.CommonJSModule))' \ ++$scala $testSuite$v/test && sbtretry \ @@ -406,21 +408,14 @@ def Tasks = [ testAdapter$v/compile:doc ''', - "tools-sbtplugin": ''' + // These are agnostic to the Scala version + "sbt-plugin-and-scalastyle": ''' setJavaVersion $java npm install && - sbtnoretry ++$scala ir$v/test linkerInterface$v/compile \ - linker$v/compile testAdapter$v/test \ - sbtPlugin/package \ - ir$v/mimaReportBinaryIssues \ - linkerInterface$v/mimaReportBinaryIssues linker$v/mimaReportBinaryIssues \ - testAdapter$v/mimaReportBinaryIssues \ - sbtPlugin/mimaReportBinaryIssues && - sbtnoretry ++$scala scalastyleCheck && - sbtnoretry ++$scala ir$v/compile:doc \ - linkerInterface$v/compile:doc linker$v/compile:doc \ - testAdapter$v/compile:doc \ - sbtPlugin/compile:doc && + sbtnoretry \ + sbtPlugin/compile:doc \ + sbtPlugin/mimaReportBinaryIssues \ + scalastyleCheck && sbtnoretry sbtPlugin/scripted ''', @@ -458,10 +453,8 @@ def allJavaVersions = otherJavaVersions.clone() allJavaVersions << mainJavaVersion def mainScalaVersion = "2.12.17" -def mainScalaVersions = ["2.11.12", "2.12.17", "2.13.10"] +def mainScalaVersions = ["2.12.17", "2.13.10"] def otherScalaVersions = [ - "2.11.12", - "2.12.1", "2.12.2", "2.12.3", "2.12.4", @@ -502,21 +495,12 @@ def allESVersions = [ "ES2021" // We do not use anything specifically from ES2021, but always test the latest to avoid #4675 ] -// Scala 2.11 does not support newer Java versions -def isExcludedForScala211(javaVersion) { - return javaVersion != "1.8" && javaVersion != "11" -} - -def isExcludedScalaJavaCombo(scalaVersion, javaVersion) { - return scalaVersion.startsWith("2.11.") && isExcludedForScala211(javaVersion) -} - // The 'quick' matrix def quickMatrix = [] mainScalaVersions.each { scalaVersion -> allJavaVersions.each { javaVersion -> - if (!isExcludedScalaJavaCombo(scalaVersion, javaVersion)) - quickMatrix.add([task: "main", scala: scalaVersion, java: javaVersion]) + quickMatrix.add([task: "main", scala: scalaVersion, java: javaVersion]) + quickMatrix.add([task: "tools", scala: scalaVersion, java: javaVersion]) } quickMatrix.add([task: "test-suite-default-esversion", scala: scalaVersion, java: mainJavaVersion, testSuite: "testSuite"]) quickMatrix.add([task: "test-suite-custom-esversion", scala: scalaVersion, java: mainJavaVersion, esVersion: "ES5_1", testSuite: "testSuite"]) @@ -529,12 +513,8 @@ allESVersions.each { esVersion -> quickMatrix.add([task: "test-suite-custom-esversion-force-polyfills", scala: mainScalaVersion, java: mainJavaVersion, esVersion: esVersion, testSuite: "testSuite"]) } allJavaVersions.each { javaVersion -> - if (!isExcludedForScala211(javaVersion)) { - // the sbt plugin tests want to compile everything for 2.11, 2.12 and 2.13 - quickMatrix.add([task: "tools-sbtplugin", scala: "2.12.17", java: javaVersion]) - quickMatrix.add([task: "tools", scala: "2.11.12", java: javaVersion]) - } - quickMatrix.add([task: "tools", scala: "2.13.10", java: javaVersion]) + // the `scala` version is irrelevant here + quickMatrix.add([task: "sbt-plugin-and-scalastyle", scala: mainScalaVersion, java: javaVersion]) } quickMatrix.add([task: "scala3-compat", scala: scala3Version, java: mainJavaVersion]) @@ -545,8 +525,7 @@ otherScalaVersions.each { scalaVersion -> } mainScalaVersions.each { scalaVersion -> otherJavaVersions.each { javaVersion -> - if (!isExcludedScalaJavaCombo(scalaVersion, javaVersion)) - quickMatrix.add([task: "test-suite-default-esversion", scala: scalaVersion, java: javaVersion, testSuite: "testSuite"]) + quickMatrix.add([task: "test-suite-default-esversion", scala: scalaVersion, java: javaVersion, testSuite: "testSuite"]) } fullMatrix.add([task: "partest-noopt", scala: scalaVersion, java: mainJavaVersion]) fullMatrix.add([task: "partest-fullopt", scala: scalaVersion, java: mainJavaVersion]) diff --git a/TESTING.md b/TESTING.md index f013e257f5..d88cbda79c 100644 --- a/TESTING.md +++ b/TESTING.md @@ -4,8 +4,8 @@ This file contains test cases that should be manually executed. The following HTML-runners must be manually tested: - examples/helloworld/helloworld-{2.11|2.12}{|-fastopt}.html - examples/reversi/reversi-{2.11|2.12}{|-fastopt}.html + examples/helloworld/helloworld-2.12{|-fastopt}.html + examples/reversi/reversi-2.12{|-fastopt}.html ## HTML-Test Runner with Modules @@ -29,7 +29,7 @@ $ python3 -m http.server To test source maps, do the following on: - examples/reversi/reversi-{2.11|2.12}{|-fastopt}.html + examples/reversi/reversi-2.12{|-fastopt}.html 1. Open the respective file in Google Chrome 2. Set a break-point in the HTML launcher on the `new Reversi` statement diff --git a/compiler/src/main/scala/org/scalajs/nscplugin/CompatComponent.scala b/compiler/src/main/scala/org/scalajs/nscplugin/CompatComponent.scala index ec59a07718..af59c2b4d0 100644 --- a/compiler/src/main/scala/org/scalajs/nscplugin/CompatComponent.scala +++ b/compiler/src/main/scala/org/scalajs/nscplugin/CompatComponent.scala @@ -25,30 +25,19 @@ import scala.tools.nsc._ * @author Sébastien Doeraene */ trait CompatComponent { - import CompatComponent.{infiniteLoop, noImplClasses} + import CompatComponent.infiniteLoop val global: Global import global._ implicit final class SymbolCompat(self: Symbol) { - def originalOwner: Symbol = - global.originalOwner.getOrElse(self, self.rawowner) - - def implClass: Symbol = NoSymbol - - def isTraitOrInterface: Boolean = self.isTrait || self.isInterface - def isScala3Defined: Boolean = false } implicit final class GlobalCompat( self: CompatComponent.this.global.type) { - object originalOwner { - def getOrElse(sym: Symbol, orElse: => Symbol): Symbol = infiniteLoop() - } - // Added in Scala 2.13.2 for configurable warnings object runReporting { def warning(pos: Position, msg: String, cat: Any, site: Symbol): Unit = @@ -65,38 +54,8 @@ trait CompatComponent { infiniteLoop() } - private implicit final class FlagsCompat(self: Flags.type) { - def IMPLCLASS: Long = infiniteLoop() - } - - lazy val scalaUsesImplClasses: Boolean = - definitions.SeqClass.implClass != NoSymbol // a trait we know has an impl class - - def isImplClass(sym: Symbol): Boolean = - scalaUsesImplClasses && sym.hasFlag(Flags.IMPLCLASS) - - lazy val isScala211: Boolean = scalaUsesImplClasses - - implicit final class StdTermNamesCompat(self: global.nme.type) { - def IMPL_CLASS_SUFFIX: String = noImplClasses() - - def isImplClassName(name: Name): Boolean = false - } - - implicit final class StdTypeNamesCompat(self: global.tpnme.type) { - def IMPL_CLASS_SUFFIX: String = noImplClasses() - - def interfaceName(implname: Name): TypeName = noImplClasses() - } - - /* SAMFunction was introduced in 2.12 for LMF-capable SAM types. - * DottyEnumSingleton was introduced in 2.13.6 to identify Scala 3 `enum` singleton cases. - */ - + // DottyEnumSingleton was introduced in 2.13.6 to identify Scala 3 `enum` singleton cases. object AttachmentsCompatDef { - case class SAMFunction(samTp: Type, sam: Symbol, synthCls: Symbol) - extends PlainAttachment - object DottyEnumSingleton extends PlainAttachment } @@ -106,19 +65,13 @@ trait CompatComponent { object Inner { import global._ - type SAMFunctionAlias = SAMFunction - val SAMFunctionAlias = SAMFunction - val DottyEnumSingletonAlias = DottyEnumSingleton } } - type SAMFunctionCompat = AttachmentsCompat.Inner.SAMFunctionAlias - lazy val SAMFunctionCompat = AttachmentsCompat.Inner.SAMFunctionAlias - lazy val DottyEnumSingletonCompat = AttachmentsCompat.Inner.DottyEnumSingletonAlias - implicit final class SAMFunctionCompatOps(self: SAMFunctionCompat) { + implicit final class SAMFunctionCompatOps(self: SAMFunction) { // Introduced in 2.12.5 to synthesize bridges in LMF classes def synthCls: Symbol = NoSymbol } @@ -157,7 +110,4 @@ trait CompatComponent { object CompatComponent { private def infiniteLoop(): Nothing = throw new AssertionError("Infinite loop in Compat") - - private def noImplClasses(): Nothing = - throw new AssertionError("No impl classes in this version") } diff --git a/compiler/src/main/scala/org/scalajs/nscplugin/ExplicitInnerJS.scala b/compiler/src/main/scala/org/scalajs/nscplugin/ExplicitInnerJS.scala index 4e0e4855c8..01ac4b8a85 100644 --- a/compiler/src/main/scala/org/scalajs/nscplugin/ExplicitInnerJS.scala +++ b/compiler/src/main/scala/org/scalajs/nscplugin/ExplicitInnerJS.scala @@ -113,13 +113,6 @@ abstract class ExplicitInnerJS[G <: Global with Singleton](val global: G) /** This class does not change linearization. */ override protected def changesBaseClasses: Boolean = false - /** Whether vals in traits are represented by their getter. - * This is true in 2.12+, since the addition of the `fields` phase. - * @see https://github.com/scala/scala/pull/5141 - */ - private lazy val traitValsHoldTheirGetterSymbol = - !scala.util.Properties.versionNumberString.startsWith("2.11.") - protected def newTransformer(unit: CompilationUnit): Transformer = new ExplicitInnerJSTransformer(unit) @@ -198,7 +191,7 @@ abstract class ExplicitInnerJS[G <: Global with Singleton](val global: G) addAnnotsIfInJSClass(accessor) decls1.enter(accessor) - if (!clazz.isTrait || !traitValsHoldTheirGetterSymbol) { + if (!clazz.isTrait) { val fieldName = accessorName.append(nme.LOCAL_SUFFIX_STRING) val fieldFlags = Flags.SYNTHETIC | Flags.ARTIFACT | Flags.PrivateLocal @@ -275,7 +268,7 @@ abstract class ExplicitInnerJS[G <: Global with Singleton](val global: G) } } - if (!currentOwner.isTrait || !traitValsHoldTheirGetterSymbol) { + if (!currentOwner.isTrait) { val jsclassField = jsclassAccessor.accessed assert(jsclassField != NoSymbol, jsclassAccessor.fullName) newDecls += localTyper.typedValDef(ValDef(jsclassField, rhs)) @@ -288,14 +281,11 @@ abstract class ExplicitInnerJS[G <: Global with Singleton](val global: G) } } else if (currentOwner.isStaticOwner) { // #4086 - val maybeModuleSym = - if (declSym.isModuleClass) declSym.module // Necessary for Scala 2.11 - else declSym - if (isExposedModule(maybeModuleSym)) { + if (isExposedModule(declSym)) { val getter = - currentOwner.info.member(jsobjectGetterNameFor(maybeModuleSym)) + currentOwner.info.member(jsobjectGetterNameFor(declSym)) newDecls += localTyper.typedDefDef { - DefDef(getter, gen.mkAttributedRef(maybeModuleSym)) + DefDef(getter, gen.mkAttributedRef(declSym)) } } } diff --git a/compiler/src/main/scala/org/scalajs/nscplugin/GenJSCode.scala b/compiler/src/main/scala/org/scalajs/nscplugin/GenJSCode.scala index cd367f7d63..019ab2cc24 100644 --- a/compiler/src/main/scala/org/scalajs/nscplugin/GenJSCode.scala +++ b/compiler/src/main/scala/org/scalajs/nscplugin/GenJSCode.scala @@ -30,6 +30,7 @@ import org.scalajs.ir.{Trees => js, Types => jstpe, ClassKind, Hashers, Original import org.scalajs.ir.Names.{LocalName, FieldName, SimpleMethodName, MethodName, ClassName} import org.scalajs.ir.OriginalName.NoOriginalName import org.scalajs.ir.Trees.OptimizerHints +import org.scalajs.ir.Version.Unversioned import org.scalajs.nscplugin.util.{ScopedVar, VarBox} import ScopedVar.withScopedVars @@ -163,7 +164,6 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) // Per method body private val currentMethodSym = new ScopedVar[Symbol] private val thisLocalVarIdent = new ScopedVar[Option[js.LocalIdent]] - private val fakeTailJumpParamRepl = new ScopedVar[(Symbol, Symbol)] private val enclosingLabelDefInfos = new ScopedVar[Map[Symbol, EnclosingLabelDefInfo]] private val isModuleInitialized = new ScopedVar[VarBox[Boolean]] private val undefinedDefaultParams = new ScopedVar[mutable.Set[Symbol]] @@ -174,7 +174,6 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) withScopedVars( currentMethodSym := methodSym, thisLocalVarIdent := None, - fakeTailJumpParamRepl := (NoSymbol, NoSymbol), enclosingLabelDefInfos := Map.empty, isModuleInitialized := new VarBox(false), undefinedDefaultParams := mutable.Set.empty, @@ -224,7 +223,6 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) generatedSAMWrapperCount := new VarBox(0), currentMethodSym := null, thisLocalVarIdent := null, - fakeTailJumpParamRepl := null, enclosingLabelDefInfos := null, isModuleInitialized := null, undefinedDefaultParams := null, @@ -318,23 +316,13 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) * Since for all these, we don't know how they inter-depend, we just * store them in a map at this point. */ - val (lazyAnons, fullClassDefs0) = allClassDefs.partition { cd => + val (lazyAnons, fullClassDefs) = allClassDefs.partition { cd => val sym = cd.symbol isAnonymousJSClass(sym) || isJSFunctionDef(sym) || sym.isAnonymousFunction } lazilyGeneratedAnonClasses ++= lazyAnons.map(cd => cd.symbol -> cd) - /* Under Scala 2.11 with -Xexperimental, anonymous JS function classes - * can be referred to in private method signatures, which means they - * must exist at the IR level, as `AbstractJSType`s. - */ - val fullClassDefs = if (isScala211WithXexperimental) { - lazyAnons.filter(cd => isJSFunctionDef(cd.symbol)) ::: fullClassDefs0 - } else { - fullClassDefs0 - } - /* Finally, we emit true code for the remaining class defs. */ for (cd <- fullClassDefs) { val sym = cd.symbol @@ -344,7 +332,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) val isPrimitive = isPrimitiveValueClass(sym) || (sym == ArrayClass) - if (!isPrimitive && !isJSImplClass(sym)) { + if (!isPrimitive) { withScopedVars( currentClassSym := sym, fieldsMutatedInCurrentClass := mutable.Set.empty, @@ -503,7 +491,8 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) // Generate members (constructor + methods) - val generatedNonFieldMembers = new ListBuffer[js.MemberDef] + val methodsBuilder = List.newBuilder[js.MethodDef] + val jsNativeMembersBuilder = List.newBuilder[js.JSNativeMemberDef] def gen(tree: Tree): Unit = { tree match { @@ -515,9 +504,9 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) case dd: DefDef => if (dd.symbol.hasAnnotation(JSNativeAnnotation)) - generatedNonFieldMembers += genJSNativeMemberDef(dd) + jsNativeMembersBuilder += genJSNativeMemberDef(dd) else - generatedNonFieldMembers ++= genMethod(dd) + methodsBuilder ++= genMethod(dd) case _ => abort("Illegal tree in gen of genClass(): " + tree) } @@ -525,15 +514,13 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) gen(impl) - // Generate fields if necessary (and add to methods + ctors) - val generatedMembers = - if (!isHijacked) genClassFields(cd) ++ generatedNonFieldMembers.toList - else generatedNonFieldMembers.toList // No fields needed + val fields = if (!isHijacked) genClassFields(cd) else Nil + + val jsNativeMembers = jsNativeMembersBuilder.result() + val generatedMethods = methodsBuilder.result() - // Generate member exports val memberExports = genMemberExports(sym) - // Generate the exported members, constructors and accessors val topLevelExportDefs = genTopLevelExports(sym) // Static initializer @@ -573,12 +560,12 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) if (isDynamicImportThunk) List(genDynamicImportForwarder(sym)) else Nil - val allMemberDefsExceptStaticForwarders = - generatedMembers ::: memberExports ::: optStaticInitializer ::: optDynamicImportForwarder + val allMethodsExceptStaticForwarders: List[js.MethodDef] = + generatedMethods ::: optStaticInitializer ::: optDynamicImportForwarder // Add static forwarders - val allMemberDefs = if (!isCandidateForForwarders(sym)) { - allMemberDefsExceptStaticForwarders + val allMethods = if (!isCandidateForForwarders(sym)) { + allMethodsExceptStaticForwarders } else { if (sym.isModuleClass) { /* If the module class has no linked class, we must create one to @@ -597,23 +584,24 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) Nil, None, None, - forwarders, - Nil + fields = Nil, + methods = forwarders, + jsConstructor = None, + jsMethodProps = Nil, + jsNativeMembers = Nil, + topLevelExportDefs = Nil )(js.OptimizerHints.empty) generatedStaticForwarderClasses += sym -> forwardersClassDef } } - allMemberDefsExceptStaticForwarders + allMethodsExceptStaticForwarders } else { val forwarders = genStaticForwardersForClassOrInterface( - allMemberDefsExceptStaticForwarders, sym) - allMemberDefsExceptStaticForwarders ::: forwarders + allMethodsExceptStaticForwarders, sym) + allMethodsExceptStaticForwarders ::: forwarders } } - // Hashed definitions of the class - val hashedMemberDefs = Hashers.hashMemberDefs(allMemberDefs) - // The complete class definition val kind = if (isStaticModule(sym)) ClassKind.ModuleClass @@ -629,11 +617,15 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) genClassInterfaces(sym, forJSClass = false), None, None, - hashedMemberDefs, + fields, + allMethods, + jsConstructor = None, + memberExports, + jsNativeMembers, topLevelExportDefs)( optimizerHints) - classDefinition + Hashers.hashClassDef(classDefinition) } /** Gen the IR ClassDef for a non-native JS class. */ @@ -700,7 +692,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) gen(cd.impl) // Static members (exported from the companion object) - val staticMembers = { + val (staticFields, staticExports) = { /* Phase travel is necessary for non-top-level classes, because flatten * breaks their companionModule. This is tracked upstream at * https://github.com/scala/scala-dev/issues/403 @@ -708,19 +700,20 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) val companionModuleClass = exitingPhase(currentRun.picklerPhase)(sym.linkedClassOfClass) if (companionModuleClass == NoSymbol) { - Nil + (Nil, Nil) } else { - val exports = withScopedVars(currentClassSym := companionModuleClass) { - genStaticExports(companionModuleClass) + val (staticFields, staticExports) = { + withScopedVars(currentClassSym := companionModuleClass) { + genStaticExports(companionModuleClass) + } } - if (exports.exists(_.isInstanceOf[js.JSFieldDef])) { - val classInitializer = genStaticConstructorWithStats( - ir.Names.ClassInitializerName, - genLoadModule(companionModuleClass)) - exports :+ classInitializer - } else { - exports + + if (staticFields.nonEmpty) { + generatedMethods += genStaticConstructorWithStats( + ir.Names.ClassInitializerName, genLoadModule(companionModuleClass)) } + + (staticFields, staticExports) } } @@ -753,17 +746,10 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) } // Generate fields (and add to methods + ctors) - val generatedMembers = { - genClassFields(cd) ::: - generatedCtor :: - genJSClassDispatchers(sym, dispatchMethodNames.result().distinct) ::: - generatedMethods.toList ::: - staticMembers - } + val fields = genClassFields(cd) - // Hashed definitions of the class - val hashedMemberDefs = - Hashers.hashMemberDefs(generatedMembers) + val jsMethodProps = + genJSClassDispatchers(sym, dispatchMethodNames.result().distinct) ::: staticExports // The complete class definition val kind = @@ -779,11 +765,15 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) genClassInterfaces(sym, forJSClass = true), jsSuperClass = jsClassCaptures.map(_.head.ref), None, - hashedMemberDefs, + fields ::: staticFields, + generatedMethods.toList, + Some(generatedCtor), + jsMethodProps, + jsNativeMembers = Nil, topLevelExports)( OptimizerHints.empty) - classDefinition + Hashers.hashClassDef(classDefinition) } /** Generate an instance of an anonymous (non-lambda) JS class inline @@ -808,37 +798,19 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) // Partition class members. val privateFieldDefs = ListBuffer.empty[js.FieldDef] - val classDefMembers = ListBuffer.empty[js.MemberDef] - val instanceMembers = ListBuffer.empty[js.MemberDef] - var constructor: Option[js.JSConstructorDef] = None + val jsFieldDefs = ListBuffer.empty[js.JSFieldDef] - origJsClass.memberDefs.foreach { + origJsClass.fields.foreach { case fdef: js.FieldDef => privateFieldDefs += fdef case fdef: js.JSFieldDef => - instanceMembers += fdef - - case mdef: js.MethodDef => - assert(mdef.flags.namespace.isStatic, - "Non-static, unexported method in non-native JS class") - classDefMembers += mdef - - case cdef: js.JSConstructorDef => - assert(constructor.isEmpty, "two ctors in class") - constructor = Some(cdef) - - case mdef: js.JSMethodDef => - assert(!mdef.flags.namespace.isStatic, "Exported static method") - instanceMembers += mdef - - case property: js.JSPropertyDef => - instanceMembers += property - - case nativeMemberDef: js.JSNativeMemberDef => - abort("illegal native JS member in JS class at " + nativeMemberDef.pos) + jsFieldDefs += fdef } + assert(origJsClass.jsNativeMembers.isEmpty, + "Found JS native members in anonymous JS class at " + pos) + assert(origJsClass.topLevelExportDefs.isEmpty, "Found top-level exports in anonymous JS class at " + pos) @@ -848,8 +820,9 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) val parent = js.ClassIdent(ir.Names.ObjectClass) js.ClassDef(origJsClass.name, origJsClass.originalName, ClassKind.AbstractJSType, None, Some(parent), interfaces = Nil, - jsSuperClass = None, jsNativeLoadSpec = None, - classDefMembers.toList, Nil)( + jsSuperClass = None, jsNativeLoadSpec = None, fields = Nil, + methods = origJsClass.methods, jsConstructor = None, jsMethodProps = Nil, + jsNativeMembers = Nil, topLevelExportDefs = Nil)( origJsClass.optimizerHints) } @@ -861,7 +834,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) throw new AssertionError( s"no class captures for anonymous JS class at $pos") } - val js.JSConstructorDef(_, ctorParams, ctorRestParam, ctorBody) = constructor.getOrElse { + val js.JSConstructorDef(_, ctorParams, ctorRestParam, ctorBody) = origJsClass.jsConstructor.getOrElse { throw new AssertionError("No ctor found") } assert(ctorParams.isEmpty && ctorRestParam.isEmpty, @@ -887,20 +860,12 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) captureValues = Nil) } - val memberDefinitions0 = instanceMembers.toList.map { - case fdef: js.FieldDef => - throw new AssertionError("unexpected FieldDef") - - case fdef: js.JSFieldDef => - implicit val pos = fdef.pos - js.Assign(js.JSSelect(selfRef, fdef.name), jstpe.zeroOf(fdef.ftpe)) - - case mdef: js.MethodDef => - throw new AssertionError("unexpected MethodDef") - - case cdef: js.JSConstructorDef => - throw new AssertionError("unexpected JSConstructorDef") + val fieldDefinitions = jsFieldDefs.toList.map { fdef => + implicit val pos = fdef.pos + js.Assign(js.JSSelect(selfRef, fdef.name), jstpe.zeroOf(fdef.ftpe)) + } + val memberDefinitions0 = origJsClass.jsMethodProps.toList.map { case mdef: js.JSMethodDef => implicit val pos = mdef.pos val impl = memberLambda(mdef.args, mdef.restParam, mdef.body) @@ -922,13 +887,12 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) js.JSMethodApply(js.JSGlobalRef("Object"), js.StringLiteral("defineProperty"), List(selfRef, pdef.name, descriptor)) - - case nativeMemberDef: js.JSNativeMemberDef => - abort("illegal native JS member in JS class at " + nativeMemberDef.pos) } + val memberDefinitions1 = fieldDefinitions ::: memberDefinitions0 + val memberDefinitions = if (privateFieldDefs.isEmpty) { - memberDefinitions0 + memberDefinitions1 } else { /* Private fields, declared in FieldDefs, are stored in a separate * object, itself stored as a non-enumerable field of the `selfRef`. @@ -968,7 +932,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) ) ) } - definePrivateFieldsObj :: memberDefinitions0 + definePrivateFieldsObj :: memberDefinitions1 } // Transform the constructor body. @@ -1038,7 +1002,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) js.ClassDef(classIdent, originalNameOfClass(sym), kind, None, superClass, genClassInterfaces(sym, forJSClass = true), None, jsNativeLoadSpec, - Nil, Nil)( + Nil, Nil, None, Nil, Nil, Nil)( OptimizerHints.empty) } @@ -1072,13 +1036,12 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) if (!isCandidateForForwarders(sym)) generatedMethods else generatedMethods ::: genStaticForwardersForClassOrInterface(generatedMethods, sym) - // Hashed definitions of the interface - val hashedMemberDefs = - Hashers.hashMemberDefs(allMemberDefs) - - js.ClassDef(classIdent, originalNameOfClass(sym), ClassKind.Interface, - None, None, interfaces, None, None, hashedMemberDefs, Nil)( + val classDef = js.ClassDef(classIdent, originalNameOfClass(sym), ClassKind.Interface, + None, None, interfaces, None, None, fields = Nil, methods = allMemberDefs, + None, Nil, Nil, Nil)( OptimizerHints.empty) + + Hashers.hashClassDef(classDef) } private lazy val jsTypeInterfacesBlacklist: Set[Symbol] = @@ -1108,9 +1071,8 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) * the companion class. This is because in the IR, only methods with the * same `MethodName` (including signature) and that are also * `PublicStatic` would collide. Since we never emit any `PublicStatic` - * method otherwise (except in 2.11 impl classes, which have no companion), - * there can be no collision. If that assumption is broken, an error - * message is emitted asking the user to report a bug. + * method otherwise, there can be no collision. If that assumption is broken, + * an error message is emitted asking the user to report a bug. * * It is important that we always emit forwarders, because some Java APIs * actually have a public static method and a public instance method with @@ -1129,7 +1091,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) * static forwarders? */ def isCandidateForForwarders(sym: Symbol): Boolean = { - !settings.noForwarders.value && sym.isStatic && !isImplClass(sym) && { + !settings.noForwarders.value && sym.isStatic && { // Reject non-top-level objects unless opted in via the appropriate option scalaJSOpts.genStaticForwardersForNonTopLevelObjects || !sym.name.containsChar('$') // this is the same test that scalac performs @@ -1145,8 +1107,8 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) * Precondition: `isCandidateForForwarders(sym)` is true */ def genStaticForwardersForClassOrInterface( - existingMembers: List[js.MemberDef], sym: Symbol)( - implicit pos: Position): List[js.MemberDef] = { + existingMethods: List[js.MethodDef], sym: Symbol)( + implicit pos: Position): List[js.MethodDef] = { /* Phase travel is necessary for non-top-level classes, because flatten * breaks their companionModule. This is tracked upstream at * https://github.com/scala/scala-dev/issues/403 @@ -1157,31 +1119,24 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) } else { val moduleClass = module.moduleClass if (!isJSType(moduleClass)) - genStaticForwardersFromModuleClass(existingMembers, moduleClass) + genStaticForwardersFromModuleClass(existingMethods, moduleClass) else Nil } } - private lazy val dontUseExitingUncurryForForwarders = - scala.util.Properties.versionNumberString.startsWith("2.11.") - /** Gen the static forwarders for the methods of a module class. * * Precondition: `isCandidateForForwarders(moduleClass)` is true */ - def genStaticForwardersFromModuleClass(existingMembers: List[js.MemberDef], + def genStaticForwardersFromModuleClass(existingMethods: List[js.MethodDef], moduleClass: Symbol)( - implicit pos: Position): List[js.MemberDef] = { + implicit pos: Position): List[js.MethodDef] = { assert(moduleClass.isModuleClass, moduleClass) - val hasAnyExistingPublicStaticMethod = existingMembers.exists { - case js.MethodDef(flags, _, _, _, _, _) => - flags.namespace == js.MemberNamespace.PublicStatic - case _ => - false - } + val hasAnyExistingPublicStaticMethod = + existingMethods.exists(_.flags.namespace == js.MemberNamespace.PublicStatic) if (hasAnyExistingPublicStaticMethod) { reporter.error(pos, "Unexpected situation: found existing public static methods in " + @@ -1191,7 +1146,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) } def listMembersBasedOnFlags = { - // Copy-pasted from BCodeHelpers (it's somewhere else in 2.11.x) + // Copy-pasted from BCodeHelpers. val ExcludedForwarderFlags: Long = { import scala.tools.nsc.symtab.Flags._ SPECIALIZED | LIFTED | PROTECTED | STATIC | EXPANDEDNAME | PRIVATE | MACRO @@ -1200,21 +1155,8 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) moduleClass.info.membersBasedOnFlags(ExcludedForwarderFlags, symtab.Flags.METHOD) } - /* See BCodeHelprs.addForwarders in 2.12+ for why we normally use - * exitingUncurry. In 2.11.x we do not use it, because Scala/JVM did not - * use it back then, and using it on that version causes mixed in methods - * not to be found (this notably breaks `extends App` as the `main` - * method that it defines is not found). - * - * This means that in 2.11.x we suffer from - * https://github.com/scala/bug/issues/10812, like upstream Scala/JVM, - * but it does not really affect Scala.js because the IR methods are not - * used for compilation, only for linking, and for linking it is fine to - * have additional, unexpected bridges. - */ - val members = - if (dontUseExitingUncurryForForwarders) listMembersBasedOnFlags - else exitingUncurry(listMembersBasedOnFlags) + // See BCodeHelprs.addForwarders in 2.12+ for why we use exitingUncurry. + val members = exitingUncurry(listMembersBasedOnFlags) def isExcluded(m: Symbol): Boolean = { def isOfJLObject: Boolean = { @@ -1247,7 +1189,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) js.MethodDef(flags, methodIdent, originalName, jsParams, resultType, Some { genApplyMethod(genLoadModule(moduleClass), m, jsParams.map(_.ref)) - })(OptimizerHints.empty, None) + })(OptimizerHints.empty, Unversioned) } } @@ -1344,7 +1286,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) Nil, jstpe.NoType, Some(stats))( - OptimizerHints.empty, None) + OptimizerHints.empty, Unversioned) } private def genRegisterReflectiveInstantiation(sym: Symbol)( @@ -1526,7 +1468,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) val constructorDef = js.JSConstructorDef( js.MemberFlags.empty.withNamespace(js.MemberNamespace.Constructor), - formalArgs, restParam, constructorBody)(OptimizerHints.empty, None) + formalArgs, restParam, constructorBody)(OptimizerHints.empty, Unversioned) (jsClassCaptures, constructorDef) } @@ -1816,7 +1758,6 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) * - Abstract methods in non-native JS classes * - Default accessor of a native JS constructor * - Constructors of hijacked classes - * - Methods with the {{{@JavaDefaultMethod}}} annotation mixed in classes. */ def genMethod(dd: DefDef): Option[js.MethodDef] = { val sym = dd.symbol @@ -1878,10 +1819,6 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) None } else if (sym.isClassConstructor && isHijackedClass(sym.owner)) { None - } else if (scalaUsesImplClasses && !isImplClass(sym.owner) && - !isAbstract && sym.hasAnnotation(JavaDefaultMethodAnnotation)) { - // Do not emit trait impl forwarders with @JavaDefaultMethod - None } else { withNewLocalNameScope { Some(genMethodWithCurrentLocalNameScope(dd)) @@ -1896,9 +1833,6 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) * * Constructors are emitted by generating their body as a statement. * - * Interface methods with the {{{@JavaDefaultMethod}}} annotation produce - * default methods forwarding to the trait impl class method. - * * Other (normal) methods are emitted with `genMethodDef()`. */ def genMethodWithCurrentLocalNameScope(dd: DefDef): js.MethodDef = { @@ -1918,35 +1852,10 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) } val jsMethodDef = if (isAbstractMethod(dd)) { - val body = if (scalaUsesImplClasses && - sym.hasAnnotation(JavaDefaultMethodAnnotation)) { - /* For an interface method with @JavaDefaultMethod, make it a - * default method calling the impl class method. - */ - val implClassSym = sym.owner.implClass - val implMethodSym = implClassSym.info.member(sym.name).suchThat { s => - s.isMethod && - s.tpe.params.size == sym.tpe.params.size + 1 && - s.tpe.params.head.tpe =:= sym.owner.toTypeConstructor && - s.tpe.params.tail.zip(sym.tpe.params).forall { - case (sParam, symParam) => - sParam.tpe =:= symParam.tpe - } - } - Some(genApplyStatic(implMethodSym, - js.This()(currentThisType) :: jsParams.map(_.ref))) - } else { - None - } js.MethodDef(js.MemberFlags.empty, methodName, originalName, - jsParams, toIRType(sym.tpe.resultType), body)( - OptimizerHints.empty, None) + jsParams, toIRType(sym.tpe.resultType), None)( + OptimizerHints.empty, Unversioned) } else { - def isTraitImplForwarder = dd.rhs match { - case app: Apply => isImplClass(app.symbol.owner) - case _ => false - } - val shouldMarkInline = { sym.hasAnnotation(InlineAnnotationClass) || sym.name.startsWith(nme.ANON_FUN_NAME) || @@ -1955,7 +1864,6 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) val shouldMarkNoinline = { sym.hasAnnotation(NoinlineAnnotationClass) && - !isTraitImplForwarder && !ignoreNoinlineAnnotation(sym) } @@ -1970,7 +1878,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) js.MethodDef( js.MemberFlags.empty.withNamespace(namespace), methodName, originalName, jsParams, jstpe.NoType, Some(genStat(dd.rhs)))( - optimizerHints, None) + optimizerHints, Unversioned) } else { val resultIRType = toIRType(sym.tpe.resultType) val namespace = { @@ -2028,18 +1936,8 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) } } - def isAbstractMethod(dd: DefDef): Boolean = { - /* When scalac uses impl classes, we cannot trust `rhs` to be - * `EmptyTree` for deferred methods (probably due to an internal bug - * of scalac), as can be seen in run/t6443.scala. - * However, when it does not use impl class anymore, we have to use - * `rhs == EmptyTree` as predicate, just like the JVM back-end does. - */ - if (scalaUsesImplClasses) - dd.symbol.isDeferred || dd.symbol.owner.isInterface - else - dd.rhs == EmptyTree - } + def isAbstractMethod(dd: DefDef): Boolean = + dd.rhs == EmptyTree private val adHocInlineMethods = Set( "scala.collection.mutable.ArrayOps$ofRef.newBuilder$extension", @@ -2081,7 +1979,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) val newBody = body.map( b => transformer.transform(b, isStat = resultType == jstpe.NoType)) js.MethodDef(flags, methodName, originalName, newParams, resultType, - newBody)(methodDef.optimizerHints, None)(methodDef.pos) + newBody)(methodDef.optimizerHints, Unversioned)(methodDef.pos) } /** Patches the type of selected param defs in a [[js.MethodDef]]. @@ -2117,7 +2015,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) val newBody = body.map( b => transformer.transform(b, isStat = resultType == jstpe.NoType)) js.MethodDef(flags, methodName, originalName, newParams, resultType, - newBody)(methodDef.optimizerHints, None)(methodDef.pos) + newBody)(methodDef.optimizerHints, Unversioned)(methodDef.pos) } /** Generates the JSNativeMemberDef of a JS native method. */ @@ -2155,72 +2053,47 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) rhs) => // This method has tail jumps - // To be called from within withScopedVars - def genInnerBody() = { - js.Block(otherStats.map(genStat) :+ ( - if (bodyIsStat) genStat(rhs) - else genExpr(rhs))) + val thisSym = thisDef.symbol + if (thisSym.isMutable) + mutableLocalVars += thisSym + + val thisLocalIdent = encodeLocalSym(thisSym) + val thisLocalType = currentThisType + + val genRhs = { + /* #3267 In default methods, scalac will type its _$this + * pseudo-variable as the *self-type* of the enclosing class, + * instead of the enclosing class type itself. However, it then + * considers *usages* of _$this as if its type were the + * enclosing class type. The latter makes sense, since it is + * compiled as `this` in the bytecode, which necessarily needs + * to be the enclosing class type. Only the declared type of + * _$this is wrong. + * + * In our case, since we generate an actual local variable for + * _$this, we must make sure to type it correctly, as the + * enclosing class type. However, this means the rhs of the + * ValDef does not match, which is why we have to adapt it + * here. + */ + forceAdapt(genExpr(initialThis), thisLocalType) } - initialThis match { - case Ident(_) => - /* This case happens in trait implementation classes, until - * Scala 2.11. In the method, all usages of `this` have been - * replaced by the method's formal parameter `$this`. However, - * there is still a declaration of the pseudo local variable - * `_$this`, which is used in the param list of the label. We - * need to remember it now, so that when we build the JS version - * of the formal params for the label, we can redirect the - * assignment to `$this` instead of the otherwise unused - * `_$this`. - */ - withScopedVars( - fakeTailJumpParamRepl := (thisDef.symbol, initialThis.symbol) - ) { - genInnerBody() - } - - case _ => - val thisSym = thisDef.symbol - if (thisSym.isMutable) - mutableLocalVars += thisSym - - val thisLocalIdent = encodeLocalSym(thisSym) - val thisLocalType = currentThisType - - val genRhs = { - /* #3267 In default methods, scalac will type its _$this - * pseudo-variable as the *self-type* of the enclosing class, - * instead of the enclosing class type itself. However, it then - * considers *usages* of _$this as if its type were the - * enclosing class type. The latter makes sense, since it is - * compiled as `this` in the bytecode, which necessarily needs - * to be the enclosing class type. Only the declared type of - * _$this is wrong. - * - * In our case, since we generate an actual local variable for - * _$this, we must make sure to type it correctly, as the - * enclosing class type. However, this means the rhs of the - * ValDef does not match, which is why we have to adapt it - * here. - */ - forceAdapt(genExpr(initialThis), thisLocalType) - } + val thisLocalVarDef = js.VarDef(thisLocalIdent, thisOriginalName, + thisLocalType, thisSym.isMutable, genRhs) - val thisLocalVarDef = js.VarDef(thisLocalIdent, thisOriginalName, - thisLocalType, thisSym.isMutable, genRhs) - - val innerBody = { - withScopedVars( - thisLocalVarIdent := Some(thisLocalIdent) - ) { - genInnerBody() - } - } - - js.Block(thisLocalVarDef, innerBody) + val innerBody = { + withScopedVars( + thisLocalVarIdent := Some(thisLocalIdent) + ) { + js.Block(otherStats.map(genStat) :+ ( + if (bodyIsStat) genStat(rhs) + else genExpr(rhs))) + } } + js.Block(thisLocalVarDef, innerBody) + case _ => if (bodyIsStat) genStat(tree) else genExpr(tree) @@ -2248,20 +2121,13 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) case _ => genBody() } - } else if (isImplClass(currentClassSym)) { - val thisParam = jsParams.head - withScopedVars( - thisLocalVarIdent := Some(thisParam.name) - ) { - genBody() - } } else { genBody() } } js.MethodDef(flags, methodName, originalName, jsParams, resultIRType, Some(body))( - optimizerHints, None) + optimizerHints, Unversioned) } else { assert(!namespace.isStatic, tree.pos) @@ -2279,7 +2145,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) js.MethodDef(flags, methodName, originalName, thisParamDef :: jsParams, resultIRType, Some(genBody()))( - optimizerHints, None) + optimizerHints, Unversioned) } } } @@ -2656,7 +2522,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) case mtch: Match => genMatch(mtch, isStat) - /** Anonymous function (in 2.12, or with -Ydelambdafy:method in 2.11) */ + /** Anonymous function */ case fun: Function => genAnonFunction(fun) @@ -2683,13 +2549,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) js.This()(currentThisType) } { thisLocalIdent => // .copy() to get the correct position - val tpe = { - if (isImplClass(currentClassSym)) - encodeClassType(traitOfImplClass(currentClassSym)) - else - currentThisType - } - js.VarRef(thisLocalIdent.copy())(tpe) + js.VarRef(thisLocalIdent.copy())(currentThisType) } } @@ -2736,9 +2596,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) implicit val pos = tree.pos val sym = tree.symbol - val labelParamSyms = tree.params.map(_.symbol).map { s => - if (s == fakeTailJumpParamRepl._1) fakeTailJumpParamRepl._2 else s - } + val labelParamSyms = tree.params.map(_.symbol) val info = new EnclosingLabelDefInfoWithResultAsAssigns(labelParamSyms) val labelIdent = encodeLabelSym(sym) @@ -3419,7 +3277,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) } else if (sym.hasAnnotation(JSNativeAnnotation)) { genJSNativeMemberCall(tree, isStat) } else if (sym.isStaticMember) { - if (sym.isMixinConstructor && isJSImplClass(sym.owner)) { + if (sym.isMixinConstructor) { /* Do not emit a call to the $init$ method of JS traits. * This exception is necessary because optional JS fields cause the * creation of a $init$ method, which we must not call. @@ -4593,12 +4451,6 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) } } - /** See comment in `genEqEqPrimitive()` about `mustUseAnyComparator`. */ - private lazy val shouldPreserveEqEqBugWithJLFloatDouble = { - val v = scala.util.Properties.versionNumberString - v.startsWith("2.11.") || v == "2.12.1" - } - /** Gen JS code for a call to Any.== */ def genEqEqPrimitive(ltpe: Type, rtpe: Type, lsrc: js.Tree, rsrc: js.Tree)( implicit pos: Position): js.Tree = { @@ -4614,9 +4466,6 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) * own equals method will do ok), except for java.lang.Float and * java.lang.Double: their `equals` have different behavior around `NaN` * and `-0.0`, see Javadoc (scala-dev#329, #2799). - * - * The latter case is only avoided in 2.12.2+, to remain bug-compatible - * with the Scala/JVM compiler. */ val mustUseAnyComparator: Boolean = { val lsym = ltpe.typeSymbol @@ -4625,12 +4474,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) isMaybeBoxed(lsym) && isMaybeBoxed(rsym) && { val areSameFinals = ltpe.isFinalType && rtpe.isFinalType && lsym == rsym - !areSameFinals || { - (lsym == BoxedFloatClass || lsym == BoxedDoubleClass) && { - // Bug-compatibility for Scala < 2.12.2 - !shouldPreserveEqEqBugWithJLFloatDouble - } - } + !areSameFinals || (lsym == BoxedFloatClass || lsym == BoxedDoubleClass) } } } @@ -4761,12 +4605,8 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) // common case for which there is no side-effect nor NPE newArg case _ => - val NPECtor = getMemberMethod(NullPointerExceptionClass, - nme.CONSTRUCTOR).suchThat(_.tpe.params.isEmpty) js.Block( - js.If(js.BinaryOp(js.BinaryOp.===, newReceiver, js.Null()), - js.Throw(genNew(NullPointerExceptionClass, NPECtor, Nil)), - js.Skip())(jstpe.NoType), + js.GetClass(newReceiver), // null check newArg) } } @@ -6226,8 +6066,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) /** Generate JS code for an anonymous function * - * Anonymous functions survive until the backend in 2.11 under - * -Ydelambdafy:method (for Scala function types) and in 2.12 for any + * Anonymous functions survive until the backend for any * LambdaMetaFactory-capable type. * * When they do, their body is always of the form @@ -6333,7 +6172,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) * We have to synthesize a class like LambdaMetaFactory would do on * the JVM. */ - val sam = originalFunction.attachments.get[SAMFunctionCompat].getOrElse { + val sam = originalFunction.attachments.get[SAMFunction].getOrElse { abort(s"Cannot find the SAMFunction attachment on $originalFunction at $pos") } @@ -6343,7 +6182,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) } } - private def synthesizeSAMWrapper(funSym: Symbol, samInfo: SAMFunctionCompat)( + private def synthesizeSAMWrapper(funSym: Symbol, samInfo: SAMFunction)( implicit pos: Position): ClassName = { val intfName = encodeClassName(funSym) @@ -6381,7 +6220,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) ir.Names.ObjectClass, js.MethodIdent(ir.Names.NoArgConstructorName), Nil)(jstpe.NoType)))))( - js.OptimizerHints.empty, None) + js.OptimizerHints.empty, Unversioned) } // Compute the set of method symbols that we need to implement @@ -6435,7 +6274,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) js.MethodDef(js.MemberFlags.empty, encodeMethodSym(sam), originalNameOfMethod(sam), jsParams, resultType, Some(body))( - js.OptimizerHints.empty, None) + js.OptimizerHints.empty, Unversioned) } // The class definition @@ -6448,7 +6287,11 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) List(js.ClassIdent(intfName)), None, None, - fFieldDef :: ctorDef :: samMethodDefs, + fields = fFieldDef :: Nil, + methods = ctorDef :: samMethodDefs, + jsConstructor = None, + Nil, + Nil, Nil)( js.OptimizerHints.empty.withInline(true)) @@ -6530,7 +6373,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) NoOriginalName, paramDefs, jstpe.AnyType, - Some(body))(OptimizerHints.empty, None) + Some(body))(OptimizerHints.empty, Unversioned) } } @@ -6823,16 +6666,8 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) } } - private lazy val isScala211WithXexperimental = { - scala.util.Properties.versionNumberString.startsWith("2.11.") && - settings.Xexperimental.value - } - - private lazy val hasNewCollections = { - val v = scala.util.Properties.versionNumberString - !v.startsWith("2.11.") && - !v.startsWith("2.12.") - } + private lazy val hasNewCollections = + !scala.util.Properties.versionNumberString.startsWith("2.12.") /** Tests whether the given type represents a JavaScript type, * i.e., whether it extends scala.scalajs.js.Any. @@ -6857,13 +6692,6 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) private def isJSNativeClass(sym: Symbol): Boolean = sym.hasAnnotation(JSNativeAnnotation) - /** Tests whether the given class is the impl class of a JS trait. */ - private def isJSImplClass(sym: Symbol): Boolean = - isImplClass(sym) && isJSType(traitOfImplClass(sym)) - - private def traitOfImplClass(sym: Symbol): Symbol = - sym.owner.info.decl(sym.name.dropRight(nme.IMPL_CLASS_SUFFIX.length)) - /** Tests whether the given member is exposed, i.e., whether it was * originally a public or protected member of a non-native JS class. */ @@ -7031,7 +6859,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) JavaScriptExceptionClass isSubClass tpe.typeSymbol def isStaticModule(sym: Symbol): Boolean = - sym.isModuleClass && !isImplClass(sym) && !sym.isLifted + sym.isModuleClass && !sym.isLifted def isAnonymousJSClass(sym: Symbol): Boolean = { /* sym.isAnonymousClass simply checks if diff --git a/compiler/src/main/scala/org/scalajs/nscplugin/GenJSExports.scala b/compiler/src/main/scala/org/scalajs/nscplugin/GenJSExports.scala index 4239c9c11a..ce36da4cab 100644 --- a/compiler/src/main/scala/org/scalajs/nscplugin/GenJSExports.scala +++ b/compiler/src/main/scala/org/scalajs/nscplugin/GenJSExports.scala @@ -24,6 +24,7 @@ import org.scalajs.ir.{Trees => js, Types => jstpe} import org.scalajs.ir.Names.LocalName import org.scalajs.ir.OriginalName.NoOriginalName import org.scalajs.ir.Trees.OptimizerHints +import org.scalajs.ir.Version.Unversioned import org.scalajs.nscplugin.util.ScopedVar import ScopedVar.withScopedVars @@ -47,7 +48,7 @@ trait GenJSExports[G <: Global with Singleton] extends SubComponent { * * @param classSym symbol of the class we export for */ - def genMemberExports(classSym: Symbol): List[js.MemberDef] = { + def genMemberExports(classSym: Symbol): List[js.JSMethodPropDef] = { val allExports = classSym.info.members.filter(jsInterop.isExport(_)) val newlyDecldExports = if (classSym.superClass == NoSymbol) { @@ -66,7 +67,7 @@ trait GenJSExports[G <: Global with Singleton] extends SubComponent { } def genJSClassDispatchers(classSym: Symbol, - dispatchMethodsNames: List[JSName]): List[js.MemberDef] = { + dispatchMethodsNames: List[JSName]): List[js.JSMethodPropDef] = { dispatchMethodsNames .map(genJSClassDispatcher(classSym, _)) } @@ -133,8 +134,8 @@ trait GenJSExports[G <: Global with Singleton] extends SubComponent { (info, sym) } - (for { - (info, tups) <- exports.groupBy(_._1) + for { + (info, tups) <- stableGroupByWithoutHashCode(exports)(_._1) kind <- checkSameKind(tups) } yield { import ExportKind._ @@ -163,10 +164,10 @@ trait GenJSExports[G <: Global with Singleton] extends SubComponent { val sym = checkSingleField(tups) js.TopLevelFieldExportDef(info.moduleID, info.jsName, encodeFieldSym(sym)) } - }).toList + } } - def genStaticExports(classSym: Symbol): List[js.MemberDef] = { + def genStaticExports(classSym: Symbol): (List[js.JSFieldDef], List[js.JSMethodPropDef]) = { val exports = (for { sym <- classSym.info.members info <- jsInterop.staticExportsOf(sym) @@ -174,10 +175,13 @@ trait GenJSExports[G <: Global with Singleton] extends SubComponent { (info, sym) }).toList - (for { - (info, tups) <- exports.groupBy(_._1) + val fields = List.newBuilder[js.JSFieldDef] + val methodProps = List.newBuilder[js.JSMethodPropDef] + + for { + (info, tups) <- stableGroupByWithoutHashCode(exports)(_._1) kind <- checkSameKind(tups) - } yield { + } { def alts = tups.map(_._2) implicit val pos = info.pos @@ -186,11 +190,11 @@ trait GenJSExports[G <: Global with Singleton] extends SubComponent { kind match { case Method => - genMemberExportOrDispatcher( + methodProps += genMemberExportOrDispatcher( JSName.Literal(info.jsName), isProp = false, alts, static = true) case Property => - genMemberExportOrDispatcher( + methodProps += genMemberExportOrDispatcher( JSName.Literal(info.jsName), isProp = true, alts, static = true) case Field => @@ -202,15 +206,17 @@ trait GenJSExports[G <: Global with Singleton] extends SubComponent { .withMutable(true) val name = js.StringLiteral(info.jsName) val irTpe = genExposedFieldIRType(sym) - js.JSFieldDef(flags, name, irTpe) + fields += js.JSFieldDef(flags, name, irTpe) case kind => throw new AssertionError(s"unexpected static export kind: $kind") } - }).toList + } + + (fields.result(), methodProps.result()) } - private def genMemberExport(classSym: Symbol, name: TermName): js.MemberDef = { + private def genMemberExport(classSym: Symbol, name: TermName): js.JSMethodPropDef = { /* This used to be `.member(name)`, but it caused #3538, since we were * sometimes selecting mixin forwarders, whose type history does not go * far enough back in time to see varargs. We now explicitly exclude @@ -241,7 +247,7 @@ trait GenJSExports[G <: Global with Singleton] extends SubComponent { genMemberExportOrDispatcher(JSName.Literal(jsName), isProp, alts, static = false) } - private def genJSClassDispatcher(classSym: Symbol, name: JSName): js.MemberDef = { + private def genJSClassDispatcher(classSym: Symbol, name: JSName): js.JSMethodPropDef = { val alts = classSym.info.members.toList.filter { sym => sym.isMethod && !sym.isBridge && @@ -264,14 +270,14 @@ trait GenJSExports[G <: Global with Singleton] extends SubComponent { reporter.error(alts.head.pos, s"Conflicting properties and methods for ${classSym.fullName}::$name.") implicit val pos = alts.head.pos - js.JSPropertyDef(js.MemberFlags.empty, genExpr(name), None, None) + js.JSPropertyDef(js.MemberFlags.empty, genExpr(name), None, None)(Unversioned) } else { genMemberExportOrDispatcher(name, isProp, alts, static = false) } } def genMemberExportOrDispatcher(jsName: JSName, isProp: Boolean, - alts: List[Symbol], static: Boolean): js.MemberDef = { + alts: List[Symbol], static: Boolean): js.JSMethodPropDef = { withNewLocalNameScope { if (isProp) genExportProperty(alts, jsName, static) @@ -317,7 +323,7 @@ trait GenJSExports[G <: Global with Singleton] extends SubComponent { } } - js.JSPropertyDef(flags, genExpr(jsName), getterBody, setterArgAndBody) + js.JSPropertyDef(flags, genExpr(jsName), getterBody, setterArgAndBody)(Unversioned) } /** generates the exporter function (i.e. exporter for non-properties) for @@ -352,7 +358,7 @@ trait GenJSExports[G <: Global with Singleton] extends SubComponent { genOverloadDispatch(jsName, overloads, jstpe.AnyType) js.JSMethodDef(flags, genExpr(jsName), formalArgs, restParam, body)( - OptimizerHints.empty, None) + OptimizerHints.empty, Unversioned) } def genOverloadDispatch(jsName: JSName, alts: List[Exported], tpe: jstpe.Type)( @@ -393,12 +399,28 @@ trait GenJSExports[G <: Global with Singleton] extends SubComponent { } yield (argc, method) } - // Create a map: argCount -> methods (methods may appear multiple times) - val methodByArgCount = - methodArgCounts.groupBy(_._1).map(kv => kv._1 -> kv._2.map(_._2).toSet) + /** Like groupBy, but returns a sorted List instead of an unordered Map. */ + def sortedGroupBy[A, K, O](xs: List[A])(grouper: A => K)( + sorter: ((K, List[A])) => O)(implicit ord: Ordering[O]): List[(K, List[A])] = { + xs.groupBy(grouper).toList.sortBy(sorter) + } + + /* Create tuples: (argCount, methods). + * Methods may appear multiple times. + * + * The method lists preserve the order out of `methodArgCounts`, so if + * two such lists contain the same set of methods, they are equal. + * + * The resulting list is sorted by argCount. This is both for stability + * and because we then rely on the fact that the head is the minimum. + */ + val methodByArgCount: List[(Int, List[Exported])] = { + sortedGroupBy(methodArgCounts)(_._1)(_._1) // sort by the Int + .map(kv => kv._1 -> kv._2.map(_._2)) // preserves the relative order of the Exported's + } // Create the formal args registry - val minArgc = methodByArgCount.keys.min + val minArgc = methodByArgCount.head._1 // it is sorted by argCount, so the head is the minimum val hasVarArg = varArgMeths.nonEmpty val needsRestParam = maxArgc != minArgc || hasVarArg val formalArgsRegistry = new FormalArgsRegistry(minArgc, needsRestParam) @@ -406,32 +428,48 @@ trait GenJSExports[G <: Global with Singleton] extends SubComponent { // List of formal parameters val (formalArgs, restParam) = formalArgsRegistry.genFormalArgs() - // Create tuples: (methods, argCounts). This will be the cases we generate - val caseDefinitions = - methodByArgCount.groupBy(_._2).map(kv => kv._1 -> kv._2.keySet) + /* Create tuples: (methods, argCounts). These will be the cases we generate. + * + * Within each element, the argCounts are sorted. (This sort order is + * inherited from the sort order of `methodByArgCount`.) + * + * For stability, the list as a whole is sorted by the minimum (head) of + * argCounts. + */ + val caseDefinitions: List[(List[Exported], List[Int])] = { + sortedGroupBy(methodByArgCount)(_._2)(_._2.head._1) // sort by the minimum of the Ints + .map(kv => kv._1 -> kv._2.map(_._1)) // the Ints are still sorted from `methodByArgCount` + } // Verify stuff about caseDefinitions assert({ - val argcs = caseDefinitions.values.flatten.toList + val argcs = caseDefinitions.map(_._2).flatten argcs == argcs.distinct && argcs.forall(_ <= maxArgc) }, "every argc should appear only once and be lower than max") + /* We will avoid generating cases where the set of methods is exactly the + * the set of varargs methods. Since all the `Exported` in `alts`, and + * hence in `varArgMeths` and `methods`, are distinct, we can do + * something faster than converting both sides to sets. + */ + def isSameAsVarArgMethods(methods: List[Exported]): Boolean = + methods.size == varArgMeths.size && methods.forall(varArgMeths.contains(_)) + // Generate a case block for each (methods, argCounts) tuple - val cases = for { + val cases: List[(List[js.IntLiteral], js.Tree)] = for { (methods, argcs) <- caseDefinitions - if methods.nonEmpty && argcs.nonEmpty - - // exclude default case we're generating anyways for varargs - if methods != varArgMeths.toSet + if methods.nonEmpty && argcs.nonEmpty && !isSameAsVarArgMethods(methods) + } yield { + val argcAlternatives = argcs.map(argc => js.IntLiteral(argc - minArgc)) - // body of case to disambiguates methods with current count - caseBody = genOverloadDispatchSameArgc(jsName, formalArgsRegistry, - methods.toList, tpe, paramIndex = 0, Some(argcs.min)) + // body of case to disambiguate methods with current count + val maxUsableArgc = argcs.head // i.e., the *minimum* of the argcs here + val caseBody = genOverloadDispatchSameArgc(jsName, formalArgsRegistry, + methods, tpe, paramIndex = 0, Some(maxUsableArgc)) - // argc in reverse order - argcList = argcs.toList.sortBy(- _) - } yield (argcList.map(argc => js.IntLiteral(argc - minArgc)), caseBody) + (argcAlternatives, caseBody) + } def defaultCase = { if (!hasVarArg) { @@ -453,7 +491,7 @@ trait GenJSExports[G <: Global with Singleton] extends SubComponent { val restArgRef = formalArgsRegistry.genRestArgRef() js.Match( js.AsInstanceOf(js.JSSelect(restArgRef, js.StringLiteral("length")), jstpe.IntType), - cases.toList, defaultCase)(tpe) + cases, defaultCase)(tpe) } } @@ -486,7 +524,7 @@ trait GenJSExports[G <: Global with Singleton] extends SubComponent { reportCannotDisambiguateError(jsName, alts.map(_.sym)) js.Undefined() } else { - val altsByTypeTest = groupByWithoutHashCode(alts) { exported => + val altsByTypeTest = stableGroupByWithoutHashCode(alts) { exported => typeTestForTpe(exported.exportArgTypeAt(paramIndex)) } @@ -927,22 +965,30 @@ trait GenJSExports[G <: Global with Singleton] extends SubComponent { } } - // Group-by that does not rely on hashCode(), only equals() - O(n²) - private def groupByWithoutHashCode[A, B]( + /** Stable group-by that does not rely on hashCode(), only equals() - O(n²). + * + * In addition to preserving the relative order of elements in the value + * lists (like `groupBy`), this stable group-by also preserves the relative + * order of they keys, by their first appearance in the collection. + */ + private def stableGroupByWithoutHashCode[A, B]( coll: List[A])(f: A => B): List[(B, List[A])] = { - import scala.collection.mutable.ArrayBuffer - val m = new ArrayBuffer[(B, List[A])] + import scala.collection.mutable.{ArrayBuffer, Builder} + + val m = new ArrayBuffer[(B, Builder[A, List[A]])] m.sizeHint(coll.length) for (elem <- coll) { val key = f(elem) val index = m.indexWhere(_._1 == key) - if (index < 0) m += ((key, List(elem))) - else m(index) = (key, elem :: m(index)._2) + if (index < 0) + m += ((key, List.newBuilder[A] += elem)) + else + m(index)._2 += elem } - m.toList + m.toList.map(kv => kv._1 -> kv._2.result()) } private def genThrowTypeError(msg: String = "No matching overload")( diff --git a/compiler/src/main/scala/org/scalajs/nscplugin/JSDefinitions.scala b/compiler/src/main/scala/org/scalajs/nscplugin/JSDefinitions.scala index 389c76d5c6..43fa33aedd 100644 --- a/compiler/src/main/scala/org/scalajs/nscplugin/JSDefinitions.scala +++ b/compiler/src/main/scala/org/scalajs/nscplugin/JSDefinitions.scala @@ -72,8 +72,6 @@ trait JSDefinitions { lazy val JSGlobalScopeAnnotation = getRequiredClass("scala.scalajs.js.annotation.JSGlobalScope") lazy val JSOperatorAnnotation = getRequiredClass("scala.scalajs.js.annotation.JSOperator") - lazy val JavaDefaultMethodAnnotation = getRequiredClass("scala.scalajs.js.annotation.JavaDefaultMethod") - lazy val JSImportNamespaceObject = getRequiredModule("scala.scalajs.js.annotation.JSImport.Namespace") lazy val ExposedJSMemberAnnot = getRequiredClass("scala.scalajs.js.annotation.internal.ExposedJSMember") diff --git a/compiler/src/main/scala/org/scalajs/nscplugin/JSEncoding.scala b/compiler/src/main/scala/org/scalajs/nscplugin/JSEncoding.scala index 2d7105cd91..432abaaa7a 100644 --- a/compiler/src/main/scala/org/scalajs/nscplugin/JSEncoding.scala +++ b/compiler/src/main/scala/org/scalajs/nscplugin/JSEncoding.scala @@ -300,7 +300,7 @@ trait JSEncoding[G <: Global with Singleton] extends SubComponent { } def needsModuleClassSuffix(sym: Symbol): Boolean = - sym.isModuleClass && !sym.isJavaDefined && !isImplClass(sym) + sym.isModuleClass && !sym.isJavaDefined def originalNameOfLocal(sym: Symbol): OriginalName = { val irName = localSymbolName(sym) diff --git a/compiler/src/main/scala/org/scalajs/nscplugin/PrepJSExports.scala b/compiler/src/main/scala/org/scalajs/nscplugin/PrepJSExports.scala index e2ac9a6a88..9c2b0b5c62 100644 --- a/compiler/src/main/scala/org/scalajs/nscplugin/PrepJSExports.scala +++ b/compiler/src/main/scala/org/scalajs/nscplugin/PrepJSExports.scala @@ -54,90 +54,54 @@ trait PrepJSExports[G <: Global with Singleton] { this: PrepJSInterop[G] => private case class ExportInfo(jsName: String, destination: ExportDestination)(val pos: Position) - /** Generate the exporter for the given DefDef - * or ValDef (abstract val in class, val in trait or lazy val; - * these don't get DefDefs until the fields phase) + /** Generate exports for the given Symbol. * - * If this DefDef is a constructor, it is registered to be exported by - * GenJSCode instead and no trees are returned. + * * Registers top-level and static exports. + * * Returns (non-static) exporters for this symbol. */ - def genExportMember(baseSym: Symbol): List[Tree] = { - val clsSym = baseSym.owner + def genExport(sym: Symbol): List[Tree] = { + // Scala classes are never exported: Their constructors are. + val isScalaClass = sym.isClass && !sym.isTrait && !sym.isModuleClass && !isJSAny(sym) - val exports = exportsOf(baseSym) - - // Helper function for errors - def err(msg: String) = { - reporter.error(exports.head.pos, msg) - Nil - } + /* Filter case class apply (and unapply) to work around + * https://github.com/scala/bug/issues/8826 + */ + val isCaseApplyOrUnapplyParam = sym.isLocalToBlock && sym.owner.isCaseApplyOrUnapply - def memType = if (baseSym.isConstructor) "constructor" else "method" + /* Filter constructors of module classes: The module classes themselves will + * be exported. + */ + val isModuleClassCtor = sym.isConstructor && sym.owner.isModuleClass - if (exports.isEmpty) { - Nil - } else if (!hasLegalExportVisibility(baseSym)) { - err(s"You may only export public and protected ${memType}s") - } else if (baseSym.isMacro) { - err("You may not export a macro") - } else if (isJSAny(clsSym)) { - err(s"You may not export a $memType of a subclass of js.Any") - } else if (scalaPrimitives.isPrimitive(baseSym)) { - err("You may not export a primitive") - } else if (baseSym.isLocalToBlock) { - // We exclude case class apply (and unapply) to work around SI-8826 - if (clsSym.isCaseApplyOrUnapply) { - Nil - } else { - err("You may not export a local definition") - } - } else if (hasIllegalRepeatedParam(baseSym)) { - err(s"In an exported $memType, a *-parameter must come last " + - "(through all parameter lists)") - } else if (hasIllegalDefaultParam(baseSym)) { - err(s"In an exported $memType, all parameters with defaults " + - "must be at the end") - } else if (baseSym.isConstructor) { - // we can generate constructors entirely in the backend, since they - // do not need inheritance and such. But we want to check their sanity - // here by previous tests and the following ones. - if (checkClassOrModuleExports(clsSym, exports.head.pos)) - registerStaticAndTopLevelExports(baseSym, exports) - - Nil - } else { - assert(!baseSym.isBridge, - s"genExportMember called for bridge symbol $baseSym") + val exports = + if (isScalaClass || isCaseApplyOrUnapplyParam || isModuleClassCtor) Nil + else exportsOf(sym) - // Reset interface flag: Any trait will contain non-empty methods - clsSym.resetFlag(Flags.INTERFACE) + assert(exports.isEmpty || !sym.isBridge, + s"found exports for bridge symbol $sym. exports: $exports") - val (normalExports, topLevelAndStaticExports) = - exports.partition(_.destination == ExportDestination.Normal) + val (normalExports, topLevelAndStaticExports) = + exports.partition(_.destination == ExportDestination.Normal) - /* We can handle top level exports and static exports entirely in the - * backend. So just register them here. + /* We can handle top level exports and static exports entirely in the + * backend. So just register them here. + * + * For accessors, we need to apply some special logic to static and + * top-level exports: They actually apply to the *fields*, not to the + * accessors. + */ + if (sym.isAccessor && sym.accessed != NoSymbol) { + /* Only forward registration from the getter (not the setter) to avoid + * duplicate registration. */ - registerStaticAndTopLevelExports(baseSym, topLevelAndStaticExports) - - // Actually generate exporter methods - normalExports.flatMap(exp => genExportDefs(baseSym, exp.jsName, exp.pos)) + if (sym.isGetter) + registerStaticAndTopLevelExports(sym.accessed, topLevelAndStaticExports) + } else { + registerStaticAndTopLevelExports(sym, topLevelAndStaticExports) } - } - /** Check and (potentially) register a class or module for export. - * - * Note that Scala classes are never registered for export, their - * constructors are. - */ - def registerClassOrModuleExports(sym: Symbol): Unit = { - val exports = exportsOf(sym) - def isScalaClass = !sym.isModuleClass && !isJSAny(sym) - - if (exports.nonEmpty && checkClassOrModuleExports(sym, exports.head.pos) && - !isScalaClass) { - registerStaticAndTopLevelExports(sym, exports) - } + // For normal exports, generate exporter methods. + normalExports.flatMap(exp => genExportDefs(sym, exp.jsName, exp.pos)) } private def registerStaticAndTopLevelExports(sym: Symbol, @@ -159,56 +123,6 @@ trait PrepJSExports[G <: Global with Singleton] { this: PrepJSInterop[G] => jsInterop.registerStaticExports(sym, static) } - /** Check a class or module for export. - * - * There are 2 ways that this method can be reached: - * - via `registerClassOrModuleExports` - * - via `genExportMember` (constructor of Scala class) - */ - private def checkClassOrModuleExports(sym: Symbol, errPos: Position): Boolean = { - val isMod = sym.isModuleClass - - def err(msg: String) = { - reporter.error(errPos, msg) - false - } - - def hasAnyNonPrivateCtor: Boolean = - sym.info.member(nme.CONSTRUCTOR).filter(!isPrivateMaybeWithin(_)).exists - - def isJSNative = sym.hasAnnotation(JSNativeAnnotation) - - if (sym.isTrait) { - err("You may not export a trait") - } else if (isJSNative) { - err("You may not export a native JS " + (if (isMod) "object" else "class")) - } else if (!hasLegalExportVisibility(sym)) { - err("You may only export public and protected " + - (if (isMod) "objects" else "classes")) - } else if (sym.isLocalToBlock) { - err("You may not export a local " + - (if (isMod) "object" else "class")) - } else if (!sym.isStatic) { - err("You may not export a nested " + - (if (isMod) "object" else s"class. $createFactoryInOuterClassHint")) - } else if (sym.isAbstractClass && !isJSAny(sym)) { - err("You may not export an abstract class") - } else if (!isMod && !hasAnyNonPrivateCtor) { - /* This test is only relevant for JS classes but doesn't hurt for Scala - * classes as we could not reach it if there were only private - * constructors. - */ - err("You may not export a class that has only private constructors") - } else { - true - } - } - - private def createFactoryInOuterClassHint = { - "Create an exported factory method in the outer class to work " + - "around this limitation." - } - /** retrieves the names a sym should be exported to from its annotations * * Note that for accessor symbols, the annotations of the accessed symbol @@ -223,23 +137,47 @@ trait PrepJSExports[G <: Global with Singleton] { this: PrepJSInterop[G] => else sym } + val symOwner = + if (sym.isConstructor) sym.owner.owner + else sym.owner + // Annotations that are directly on the member val directAnnots = trgSym.annotations.filter( annot => isDirectMemberAnnot(annot.symbol)) - // Is this a member export (i.e. not a class or module export)? - val isMember = !sym.isClass && !sym.isConstructor - - // Annotations for this member on the whole unit + /* Annotations for this member on the whole unit + * + * Note that for top-level classes / modules this is always empty, because + * packages cannot have annotations. + */ val unitAnnots = { - if (isMember && sym.isPublic && !sym.isSynthetic) - sym.owner.annotations.filter(_.symbol == JSExportAllAnnotation) + val useExportAll = { + sym.isPublic && + !sym.isSynthetic && + !sym.isConstructor && + !sym.isTrait && + (!sym.isClass || sym.isModuleClass) + } + + if (useExportAll) + symOwner.annotations.filter(_.symbol == JSExportAllAnnotation) else Nil } + val allAnnots = { + val allAnnots0 = directAnnots ++ unitAnnots + + if (allAnnots0.nonEmpty) { + if (checkExportTarget(sym, allAnnots0.head.pos)) allAnnots0 + else Nil // prevent code generation from running to avoid crashes. + } else { + Nil + } + } + val allExportInfos = for { - annot <- directAnnots ++ unitAnnots + annot <- allAnnots } yield { val isExportAll = annot.symbol == JSExportAllAnnotation val isTopLevelExport = annot.symbol == JSExportTopLevelAnnotation @@ -256,12 +194,16 @@ trait PrepJSExports[G <: Global with Singleton] { this: PrepJSInterop[G] => s"The argument to ${annot.symbol.name} must be a literal string") "dummy" } - } else if (sym.isConstructor) { - decodedFullName(sym.owner) - } else if (sym.isClass) { - decodedFullName(sym) } else { - sym.unexpandedName.decoded.stripSuffix("_=") + val nameBase = + (if (sym.isConstructor) sym.owner else sym).unexpandedName + + if (nme.isSetterName(nameBase) && !jsInterop.isJSSetter(sym)) { + reporter.error(annot.pos, "You must set an explicit name when " + + "exporting a non-setter with a name ending in _=") + } + + nameBase.decoded.stripSuffix("_=") } } @@ -285,10 +227,6 @@ trait PrepJSExports[G <: Global with Singleton] { this: PrepJSInterop[G] => } } - // Enforce proper setter signature - if (jsInterop.isJSSetter(sym)) - checkSetterSignature(sym, annot.pos, exported = true) - // Enforce no __ in name if (!isTopLevelExport && name.contains("__")) { // Get position for error message @@ -298,12 +236,30 @@ trait PrepJSExports[G <: Global with Singleton] { this: PrepJSInterop[G] => "An exported name may not contain a double underscore (`__`)") } - /* Illegal function application exports, i.e., method named 'apply' - * without an explicit export name. - */ - if (isMember && !hasExplicitName && sym.name == nme.apply) { - destination match { - case ExportDestination.Normal => + // Destination-specific restrictions + destination match { + case ExportDestination.Normal => + if (symOwner.hasPackageFlag) { + // Disallow @JSExport on top-level definitions. + reporter.error(annot.pos, + "@JSExport is forbidden on top-level objects and classes. " + + "Use @JSExportTopLevel instead.") + } + + // Make sure we do not override the default export of toString + def isIllegalToString = { + name == "toString" && sym.name != nme.toString_ && + sym.tpe.params.isEmpty && !jsInterop.isJSGetter(sym) + } + if (isIllegalToString) { + reporter.error(annot.pos, "You may not export a zero-argument " + + "method named other than 'toString' under the name 'toString'") + } + + /* Illegal function application exports, i.e., method named 'apply' + * without an explicit export name. + */ + if (!hasExplicitName && sym.name == nme.apply) { def shouldBeTolerated = { isExportAll && directAnnots.exists { annot => annot.symbol == JSExportAnnotation && @@ -321,42 +277,6 @@ trait PrepJSExports[G <: Global with Singleton] { this: PrepJSInterop[G] => "application. Add @JSExport(\"apply\") to export under the " + "name apply.") } - - case _: ExportDestination.TopLevel => - throw new AssertionError( - "Found a top-level export without an explicit name at " + - annot.pos) - - case ExportDestination.Static => - reporter.error(annot.pos, - "A member cannot be exported to function application as " + - "static. Use @JSExportStatic(\"apply\") to export it under " + - "the name 'apply'.") - } - } - - val symOwner = - if (sym.isConstructor) sym.owner.owner - else sym.owner - - // Destination-specific restrictions - destination match { - case ExportDestination.Normal => - // Make sure we do not override the default export of toString - def isIllegalToString = { - isMember && name == "toString" && sym.name != nme.toString_ && - sym.tpe.params.isEmpty && !jsInterop.isJSGetter(sym) - } - if (isIllegalToString) { - reporter.error(annot.pos, "You may not export a zero-argument " + - "method named other than 'toString' under the name 'toString'") - } - - // Disallow @JSExport on non-members. - if (!isMember && !sym.isTrait) { - reporter.error(annot.pos, - "@JSExport is forbidden on objects and classes. " + - "Use @JSExportTopLevel instead.") } case _: ExportDestination.TopLevel => @@ -368,11 +288,8 @@ trait PrepJSExports[G <: Global with Singleton] { this: PrepJSInterop[G] => "You may not export a getter or a setter to the top level") } - /* Disallow non-static methods. - * Note: Non-static classes have more specific error messages in - * checkClassOrModuleExports - */ - if (sym.isMethod && (!symOwner.isStatic || !symOwner.isModuleClass)) { + // Disallow non-static definitions. + if (!symOwner.isStatic || !symOwner.isModuleClass) { reporter.error(annot.pos, "Only static objects may export their members to the top level") } @@ -400,20 +317,23 @@ trait PrepJSExports[G <: Global with Singleton] { this: PrepJSInterop[G] => "non-native JS class may export its members as static.") } - if (isMember) { - if (sym.isLazy) { - reporter.error(annot.pos, - "You may not export a lazy val as static") - } - } else { - if (sym.isTrait) { - reporter.error(annot.pos, - "You may not export a trait as static.") - } else { - reporter.error(annot.pos, - "Implementation restriction: cannot export a class or " + - "object as static") - } + if (sym.isLazy) { + reporter.error(annot.pos, + "You may not export a lazy val as static") + } + + // Illegal function application export + if (!hasExplicitName && sym.name == nme.apply) { + reporter.error(annot.pos, + "A member cannot be exported to function application as " + + "static. Use @JSExportStatic(\"apply\") to export it under " + + "the name 'apply'.") + } + + if (sym.isClass || sym.isConstructor) { + reporter.error(annot.pos, + "Implementation restriction: cannot export a class or " + + "object as static") } } @@ -431,110 +351,140 @@ trait PrepJSExports[G <: Global with Singleton] { this: PrepJSInterop[G] => } .foreach(_ => reporter.warning(sym.pos, s"Found duplicate @JSExport")) - /* Filter out static exports of accessors (as they are not actually - * exported, their fields are). The above is only used to uniformly perform - * checks. + /* Check that no field is exported *twice* as static, nor both as static + * and as top-level (it is possible to export a field several times as + * top-level, though). */ - val filteredExports = if (!sym.isAccessor || sym.accessed == NoSymbol) { - allExportInfos - } else { - /* For accessors, we need to apply some special logic to static exports. - * When tested on accessors, they actually apply on *fields*, not on the - * accessors. We use the same code paths hereabove to uniformly perform - * relevant checks, but at the end of the day, we have to throw away the - * ExportInfo. - * However, we must make sure that no field is exported *twice* as static, - * nor both as static and as top-level (it is possible to export a field - * several times as top-level, though). - */ - val (topLevelAndStaticExportInfos, actualExportInfos) = - allExportInfos.partition(_.destination != ExportDestination.Normal) - - if (sym.isGetter) { - topLevelAndStaticExportInfos.find { - _.destination == ExportDestination.Static - }.foreach { firstStatic => - for { - duplicate <- topLevelAndStaticExportInfos - if duplicate ne firstStatic - } { - if (duplicate.destination == ExportDestination.Static) { - reporter.error(duplicate.pos, - "Fields (val or var) cannot be exported as static more " + - "than once") - } else { - reporter.error(duplicate.pos, - "Fields (val or var) cannot be exported both as static " + - "and at the top-level") - } - } - } + if (sym.isGetter) { + for { + firstStatic <- allExportInfos.find(_.destination == ExportDestination.Static).toList + duplicate <- allExportInfos + if duplicate ne firstStatic + } { + duplicate.destination match { + case ExportDestination.Normal => // OK - registerStaticAndTopLevelExports(sym.accessed, topLevelAndStaticExportInfos) - } + case ExportDestination.Static => + reporter.error(duplicate.pos, + "Fields (val or var) cannot be exported as static more " + + "than once") - actualExportInfos + case _: ExportDestination.TopLevel => + reporter.error(duplicate.pos, + "Fields (val or var) cannot be exported both as static " + + "and at the top-level") + } + } } - filteredExports.distinct + allExportInfos.distinct } - /** Just like sym.fullName, but does not encode components */ - private def decodedFullName(sym: Symbol): String = { - if (sym.isRoot || sym.isRootPackage || sym == NoSymbol) sym.name.decoded - else if (sym.owner.isEffectiveRoot) sym.name.decoded - else decodedFullName(sym.effectiveOwner.enclClass) + '.' + sym.name.decoded + /** Checks whether the given target is suitable for export and exporting + * should be performed. + * + * Reports any errors for unsuitable targets. + * @returns a boolean indicating whether exporting should be performed. Note: + * a result of true is not a guarantee that no error was emitted. But it is + * a guarantee that the target is not "too broken" to run the rest of + * the generation. This approximation is done to avoid having to complicate + * shared code verifying conditions. + */ + private def checkExportTarget(sym: Symbol, errPos: Position): Boolean = { + def err(msg: String) = { + reporter.error(errPos, msg) + false + } + + def hasLegalExportVisibility(sym: Symbol) = + sym.isPublic || sym.isProtected && !sym.isProtectedLocal + + lazy val params = sym.paramss.flatten + + def hasIllegalDefaultParam = { + val isDefParam = (_: Symbol).hasFlag(Flags.DEFAULTPARAM) + params.reverse.dropWhile(isDefParam).exists(isDefParam) + } + + def hasAnyNonPrivateCtor: Boolean = + sym.info.member(nme.CONSTRUCTOR).filter(!isPrivateMaybeWithin(_)).exists + + if (sym.isTrait) { + err("You may not export a trait") + } else if (sym.hasAnnotation(JSNativeAnnotation)) { + err("You may not export a native JS definition") + } else if (!hasLegalExportVisibility(sym)) { + err("You may only export public and protected definitions") + } else if (sym.isConstructor && !hasLegalExportVisibility(sym.owner)) { + err("You may only export constructors of public and protected classes") + } else if (sym.isMacro) { + err("You may not export a macro") + } else if (isJSAny(sym.owner)) { + err("You may not export a member of a subclass of js.Any") + } else if (scalaPrimitives.isPrimitive(sym)) { + err("You may not export a primitive") + } else if (sym.isLocalToBlock) { + err("You may not export a local definition") + } else if (sym.isConstructor && sym.owner.isLocalToBlock) { + err("You may not export constructors of local classes") + } else if (params.nonEmpty && params.init.exists(isRepeated _)) { + err("In an exported method or constructor, a *-parameter must come last " + + "(through all parameter lists)") + } else if (hasIllegalDefaultParam) { + err("In an exported method or constructor, all parameters with " + + "defaults must be at the end") + } else if (sym.isConstructor && sym.owner.isAbstractClass && !isJSAny(sym)) { + err("You may not export an abstract class") + } else if (sym.isClass && !sym.isModuleClass && isJSAny(sym) && !hasAnyNonPrivateCtor) { + /* This test is only relevant for JS classes: We'll complain on the + * individual exported constructors in case of a Scala class. + */ + err("You may not export a class that has only private constructors") + } else { + if (jsInterop.isJSSetter(sym)) + checkSetterSignature(sym, errPos, exported = true) + + true // ok even if a setter has the wrong signature. + } } - /** generate an exporter for a DefDef including default parameter methods */ - private def genExportDefs(defSym: Symbol, jsName: String, pos: Position) = { - val clsSym = defSym.owner - val scalaName = - jsInterop.scalaExportName(jsName, jsInterop.isJSProperty(defSym)) + /** generate an exporter for a target including default parameter methods */ + private def genExportDefs(sym: Symbol, jsName: String, pos: Position) = { + val siblingSym = + if (sym.isConstructor) sym.owner + else sym + + val clsSym = siblingSym.owner + + val isProperty = sym.isModuleClass || isJSAny(sym) || jsInterop.isJSProperty(sym) + val scalaName = jsInterop.scalaExportName(jsName, isProperty) + + val copiedFlags = siblingSym.flags & (Flags.PROTECTED | Flags.FINAL) // Create symbol for new method - val expSym = defSym.cloneSymbol - - // Set position of symbol - expSym.pos = pos - - // Alter type for new method (lift return type to Any) - // The return type is lifted, in order to avoid bridge - // construction and to detect methods whose signature only differs - // in the return type. - // Attention: This will cause boxes for primitive value types and value - // classes. However, since we have restricted the return types, we can - // always safely remove these boxes again in the back-end. - if (!defSym.isConstructor) - expSym.setInfo(retToAny(expSym.tpe)) - - // Change name for new method - expSym.name = scalaName - - // Update flags - expSym.setFlag(Flags.SYNTHETIC) - expSym.resetFlag( - Flags.DEFERRED | // We always have a body - Flags.ACCESSOR | // We are never a "direct" accessor - Flags.CASEACCESSOR | // And a fortiori not a case accessor - Flags.LAZY | // We are not a lazy val (even if we export one) - Flags.OVERRIDE // Synthetic methods need not bother with this - ) - - // Remove export annotations - expSym.removeAnnotation(JSExportAnnotation) - - // Add symbol to class - clsSym.info.decls.enter(expSym) + val expSym = clsSym.newMethod(scalaName, pos, Flags.SYNTHETIC | copiedFlags) + expSym.privateWithin = siblingSym.privateWithin + + val expSymTpe = { + /* Alter type for new method (lift return type to Any) + * The return type is lifted, in order to avoid bridge + * construction and to detect methods whose signature only differs + * in the return type. + */ + if (sym.isClass) NullaryMethodType(AnyClass.tpe) + else retToAny(sym.tpe.cloneInfo(expSym)) + } + + expSym.setInfoAndEnter(expSymTpe) // Construct exporter DefDef tree - val exporter = genProxyDefDef(clsSym, defSym, expSym, pos) + val exporter = genProxyDefDef(sym, expSym, pos) // Construct exporters for default getters val defaultGetters = for { (param, i) <- expSym.paramss.flatten.zipWithIndex if param.hasFlag(Flags.DEFAULTPARAM) - } yield genExportDefaultGetter(clsSym, defSym, expSym, i + 1, pos) + } yield genExportDefaultGetter(clsSym, sym, expSym, i + 1, pos) exporter :: defaultGetters } @@ -559,27 +509,46 @@ trait PrepJSExports[G <: Global with Singleton] { this: PrepJSInterop[G] => clsSym.info.decls.enter(expGetter) - genProxyDefDef(clsSym, trgGetter, expGetter, pos) + genProxyDefDef(trgGetter, expGetter, pos) } else EmptyTree } /** generate a DefDef tree (from [[proxySym]]) that calls [[trgSym]] */ - private def genProxyDefDef(clsSym: Symbol, trgSym: Symbol, - proxySym: Symbol, pos: Position) = atPos(pos) { + private def genProxyDefDef(trgSym: Symbol, proxySym: Symbol, pos: Position) = atPos(pos) { + val tpeParams = proxySym.typeParams.map(gen.mkAttributedIdent(_)) - // Helper to ascribe repeated argument lists when calling - def spliceParam(sym: Symbol) = { - if (isRepeated(sym)) - Typed(Ident(sym), Ident(tpnme.WILDCARD_STAR)) - else - Ident(sym) + // Construct proxied function call + val nonPolyFun = { + if (trgSym.isConstructor) { + val clsTpe = trgSym.owner.tpe + val tpe = gen.mkTypeApply(TypeTree(clsTpe), tpeParams) + Select(New(tpe), trgSym) + } else if (trgSym.isModuleClass) { + assert(proxySym.paramss.isEmpty, + s"got a module export with non-empty paramss. target: $trgSym, proxy: $proxySym at $pos") + gen.mkAttributedRef(trgSym.sourceModule) + } else if (trgSym.isClass) { + assert(isJSAny(trgSym), s"got a class export for a non-JS class ($trgSym) at $pos") + assert(proxySym.paramss.isEmpty, + s"got a class export with non-empty paramss. target: $trgSym, proxy: $proxySym at $pos") + val tpe = gen.mkTypeApply(TypeTree(trgSym.tpe), tpeParams) + gen.mkTypeApply(gen.mkAttributedRef(JSPackage_constructorOf), List(tpe)) + } else { + val fun = gen.mkAttributedRef(trgSym) + gen.mkTypeApply(fun, tpeParams) + } } - // Construct proxied function call - val sel = Select(This(clsSym), trgSym) - val rhs = proxySym.paramss.foldLeft[Tree](sel) { - (fun,params) => Apply(fun, params map spliceParam) + val rhs = proxySym.paramss.foldLeft(nonPolyFun) { (fun, params) => + val args = params.map { param => + val ident = gen.mkAttributedIdent(param) + + if (isRepeated(param)) Typed(ident, Ident(tpnme.WILDCARD_STAR)) + else ident + } + + Apply(fun, args) } typer.typedDefDef(DefDef(proxySym, rhs)) @@ -593,26 +562,6 @@ trait PrepJSExports[G <: Global with Singleton] { this: PrepJSInterop[G] => case _ => AnyClass.tpe } - /** Whether the given symbol has a visibility that allows exporting */ - private def hasLegalExportVisibility(sym: Symbol): Boolean = - sym.isPublic || sym.isProtected && !sym.isProtectedLocal - - /** checks whether this type has a repeated parameter elsewhere than at the end - * of all the params - */ - private def hasIllegalRepeatedParam(sym: Symbol): Boolean = { - val params = sym.paramss.flatten - params.nonEmpty && params.init.exists(isRepeated _) - } - - /** checks whether there are default parameters not at the end of - * the flattened parameter list - */ - private def hasIllegalDefaultParam(sym: Symbol): Boolean = { - val isDefParam = (_: Symbol).hasFlag(Flags.DEFAULTPARAM) - sym.paramss.flatten.reverse.dropWhile(isDefParam).exists(isDefParam) - } - /** Whether a symbol is an annotation that goes directly on a member */ private lazy val isDirectMemberAnnot = Set[Symbol]( JSExportAnnotation, diff --git a/compiler/src/main/scala/org/scalajs/nscplugin/PrepJSInterop.scala b/compiler/src/main/scala/org/scalajs/nscplugin/PrepJSInterop.scala index f9e6144641..2a59a931bc 100644 --- a/compiler/src/main/scala/org/scalajs/nscplugin/PrepJSInterop.scala +++ b/compiler/src/main/scala/org/scalajs/nscplugin/PrepJSInterop.scala @@ -53,6 +53,8 @@ abstract class PrepJSInterop[G <: Global with Singleton](val global: G) import jsDefinitions._ import jsInterop.{JSCallingConvention, JSName} + import scala.reflect.internal.Flags + val phaseName: String = "jsinterop" override def description: String = "prepare ASTs for JavaScript interop" @@ -169,8 +171,11 @@ abstract class PrepJSInterop[G <: Global with Singleton](val global: G) // @unchecked needed because MemberDef is not marked `sealed` val transformedTree: Tree = (tree: @unchecked) match { case tree: ImplDef => - if (shouldPrepareExports) - registerClassOrModuleExports(sym) + if (shouldPrepareExports) { + val exports = genExport(sym) + if (exports.nonEmpty) + exporters.getOrElseUpdate(sym.owner, mutable.ListBuffer.empty) ++= exports + } if ((enclosingOwner is OwnerKind.JSNonNative) && sym.owner.isTrait && !sym.isTrait) { reporter.error(tree.pos, @@ -190,8 +195,14 @@ abstract class PrepJSInterop[G <: Global with Singleton](val global: G) * messages for that are handled by genExportMember). */ if (shouldPrepareExports && (sym.isMethod || sym.isLocalToBlock)) { - exporters.getOrElseUpdate(sym.owner, mutable.ListBuffer.empty) ++= - genExportMember(sym) + val exports = genExport(sym) + if (exports.nonEmpty) { + val target = + if (sym.isConstructor) sym.owner.owner + else sym.owner + + exporters.getOrElseUpdate(target, mutable.ListBuffer.empty) ++= exports + } } if (sym.isLocalToBlock) { @@ -307,6 +318,11 @@ abstract class PrepJSInterop[G <: Global with Singleton](val global: G) val transformedBodyWithExports = exporters.get(clsSym).fold { transformedBody } { exports => + assert(exports.nonEmpty, s"found empty exporters for $clsSym" ) + + // Reset interface flag: We're adding non-empty methods. + clsSym.resetFlag(Flags.INTERFACE) + transformedBody ::: exports.toList } @@ -452,6 +468,11 @@ abstract class PrepJSInterop[G <: Global with Singleton](val global: G) typer.typed { atPos(tree.pos) { + /* gen.mkSuperInitCall would be nicer, but that doesn't get past the typer: + * + * scala.reflect.internal.Types$TypeError: + * stable identifier required, but $anon.super. found. + */ val superCtorCall = gen.mkMethodCall( Super(clsSym, tpnme.EMPTY), ObjectClass.primaryConstructor, Nil, Nil) @@ -459,9 +480,7 @@ abstract class PrepJSInterop[G <: Global with Singleton](val global: G) // class $anon extends DynamicImportThunk val clsDef = ClassDef(clsSym, List( // def () = { super.(); () } - DefDef(ctorSym, - // `gen.mkUnitBlock(gen.mkSuperInitCall)` would be better but that fails on 2.11. - Block(superCtorCall, Literal(Constant(())))), + DefDef(ctorSym, gen.mkUnitBlock(superCtorCall)), // def apply(): Any = body DefDef(applySym, newBody))) @@ -600,9 +619,6 @@ abstract class PrepJSInterop[G <: Global with Singleton](val global: G) val isJSNative = sym.hasAnnotation(JSNativeAnnotation) - val isJSFunctionSAMInScala211 = - isScala211 && sym.name == tpnme.ANON_FUN_NAME && sym.superClass == JSFunctionClass - // Forbid @EnableReflectiveInstantiation on JS types sym.getAnnotation(EnableReflectiveInstantiationAnnotation).foreach { annot => @@ -646,11 +662,6 @@ abstract class PrepJSInterop[G <: Global with Singleton](val global: G) * This causes the unsoundness filed as #1385. */ () - case SerializableClass if isJSFunctionSAMInScala211 => - /* Ignore the scala.Serializable trait that Scala 2.11 adds on all - * SAM classes when on a JS function SAM. - */ - () case parentSym => /* This is a Scala class or trait other than AnyRef and Dynamic, * which is never valid. @@ -661,23 +672,6 @@ abstract class PrepJSInterop[G <: Global with Singleton](val global: G) } } - // Require that the SAM of a JS function def be `apply` (2.11-only here) - if (isJSFunctionSAMInScala211) { - if (!sym.info.decl(nme.apply).filter(JSCallingConvention.isCall(_)).exists) { - val samType = sym.parentSymbols.find(_ != JSFunctionClass).getOrElse { - /* This shouldn't happen, but fall back on this symbol (which has a - * compiler-generated name) not to crash. - */ - sym - } - reporter.error(implDef.pos, - "Using an anonymous function as a SAM for the JavaScript type " + - s"${samType.fullNameString} is not allowed because its single " + - "abstract method is not named `apply`. " + - "Use an anonymous class instead.") - } - } - // Checks for non-native JS stuff if (!isJSNative) { // It cannot be in a native JS class or trait @@ -1050,15 +1044,8 @@ abstract class PrepJSInterop[G <: Global with Singleton](val global: G) * synthetic `apply` method if it is in a SAM for a JS function * type. */ - val isJSFunctionSAM = { - /* Under 2.11, sym.owner.isAnonymousFunction does not properly - * recognize anonymous functions here (because they seem to not - * be marked as synthetic). - */ - sym.isSynthetic && - sym.owner.name == tpnme.ANON_FUN_NAME && - sym.owner.superClass == JSFunctionClass - } + val isJSFunctionSAM = + sym.isSynthetic && sym.owner.isAnonymousFunction if (!isJSFunctionSAM) { reporter.error(sym.pos, "A non-native JS class cannot declare a concrete method " + @@ -1221,16 +1208,8 @@ abstract class PrepJSInterop[G <: Global with Singleton](val global: G) } } - /* Check that the right-hand-side is `js.undefined`. - * - * On 2.12+, fields are created later than this phase, and getters - * still hold the right-hand-side that we need to check (we - * identify this case with `sym.accessed == NoSymbol`). - * On 2.11 and before, however, the getter has already been - * rewritten to read the field, so we must not check it. - * In either case, setters must not be checked. - */ - if (!sym.isAccessor || (sym.isGetter && sym.accessed == NoSymbol)) { + // Check that the right-hand-side is `js.undefined`. + if (!sym.isSetter) { // Check that the tree's body is `js.undefined` tree.rhs match { case sel: Select if sel.symbol == JSPackage_undefined => diff --git a/compiler/src/main/scala/org/scalajs/nscplugin/ScalaJSPlugin.scala b/compiler/src/main/scala/org/scalajs/nscplugin/ScalaJSPlugin.scala index 5e7ca6faba..e7f68f6554 100644 --- a/compiler/src/main/scala/org/scalajs/nscplugin/ScalaJSPlugin.scala +++ b/compiler/src/main/scala/org/scalajs/nscplugin/ScalaJSPlugin.scala @@ -68,17 +68,6 @@ class ScalaJSPlugin(val global: Global) extends NscPlugin { var absSourceMap: Option[URI] = None } - /** Checks and registers module exports on the symbol. - * - * This bridge allows other plugins to register new modules for export - * between jsinterop and jscode phases. It is meant to be accessed using - * reflection. The calling code still must insert the `@JSExport` annotation - * to the module. - */ - @deprecated("Might be removed at any time, use at your own risk.", "0.6.24") - def registerModuleExports(sym: Symbol): Unit = - PrepInteropComponent.registerClassOrModuleExports(sym) - object PreTyperComponentComponent extends PreTyperComponent(global) { val runsAfter = List("parser") override val runsBefore = List("namer") diff --git a/compiler/src/main/scala/org/scalajs/nscplugin/TypeConversions.scala b/compiler/src/main/scala/org/scalajs/nscplugin/TypeConversions.scala index c063a2a02a..93a4a3a5db 100644 --- a/compiler/src/main/scala/org/scalajs/nscplugin/TypeConversions.scala +++ b/compiler/src/main/scala/org/scalajs/nscplugin/TypeConversions.scala @@ -93,12 +93,12 @@ trait TypeConversions[G <: Global with Singleton] extends SubComponent { */ private def convert(t: Type): (Symbol, Int) = t.normalize match { case ThisType(ArrayClass) => (ObjectClass, 0) - case ThisType(sym) => (convertBase(sym), 0) - case SingleType(_, sym) => (convertBase(sym), 0) + case ThisType(sym) => (sym, 0) + case SingleType(_, sym) => (sym, 0) case ConstantType(_) => convert(t.underlying) case TypeRef(_, sym, args) => convertMaybeArray(sym, args) case ClassInfoType(_, _, ArrayClass) => abort("ClassInfoType to ArrayClass!") - case ClassInfoType(_, _, sym) => (convertBase(sym), 0) + case ClassInfoType(_, _, sym) => (sym, 0) // !!! Iulian says types which make no sense after erasure should not reach here, // which includes the ExistentialType, AnnotatedType, RefinedType. I don't know @@ -114,7 +114,7 @@ trait TypeConversions[G <: Global with Singleton] extends SubComponent { * run/valueclasses-classtag-existential. I have no idea how icode does * not fail this test: we do everything the same as icode up to here. */ - case tpe: ErasedValueType => (convertBase(tpe.valueClazz), 0) + case tpe: ErasedValueType => (tpe.valueClazz, 0) // For sure WildcardTypes shouldn't reach here either, but when // debugging such situations this may come in handy. @@ -133,23 +133,9 @@ trait TypeConversions[G <: Global with Singleton] extends SubComponent { val convertedArg = convert(targs.head) (convertedArg._1, convertedArg._2 + 1) case _ if sym.isClass => - (convertBase(sym), 0) + (sym, 0) case _ => assert(sym.isType, sym) // it must be compiling Array[a] (ObjectClass, 0) } - - /** Convert a class ref, definitely not an array type. */ - private def convertBase(sym: Symbol): Symbol = { - if (isImplClass(sym)) { - // pos/spec-List.scala is the sole failure if we don't check for NoSymbol - val traitSym = sym.owner.info.decl(tpnme.interfaceName(sym.name)) - if (traitSym != NoSymbol) - traitSym - else - sym - } else { - sym - } - } } diff --git a/compiler/src/test/scala/org/scalajs/nscplugin/test/DiverseErrorsTest.scala b/compiler/src/test/scala/org/scalajs/nscplugin/test/DiverseErrorsTest.scala index ef625a67c0..da1360b581 100644 --- a/compiler/src/test/scala/org/scalajs/nscplugin/test/DiverseErrorsTest.scala +++ b/compiler/src/test/scala/org/scalajs/nscplugin/test/DiverseErrorsTest.scala @@ -27,7 +27,6 @@ class DiverseErrorsTest extends DirectTest with TestHelpers { private def version = scala.util.Properties.versionNumberString private val allowsSingletonClassOf = ( - !version.startsWith("2.11.") && !version.startsWith("2.12.") && version != "2.13.0" && version != "2.13.1" && diff --git a/compiler/src/test/scala/org/scalajs/nscplugin/test/JSExportTest.scala b/compiler/src/test/scala/org/scalajs/nscplugin/test/JSExportTest.scala index 790edb15b6..234d3a1bb6 100644 --- a/compiler/src/test/scala/org/scalajs/nscplugin/test/JSExportTest.scala +++ b/compiler/src/test/scala/org/scalajs/nscplugin/test/JSExportTest.scala @@ -121,10 +121,10 @@ class JSExportTest extends DirectTest with TestHelpers { class B """ hasErrors """ - |newSource1.scala:3: error: @JSExport is forbidden on objects and classes. Use @JSExportTopLevel instead. + |newSource1.scala:3: error: @JSExport is forbidden on top-level objects and classes. Use @JSExportTopLevel instead. | @JSExport | ^ - |newSource1.scala:6: error: @JSExport is forbidden on objects and classes. Use @JSExportTopLevel instead. + |newSource1.scala:6: error: @JSExport is forbidden on top-level objects and classes. Use @JSExportTopLevel instead. | @JSExport("Foo") | ^ """ @@ -140,10 +140,10 @@ class JSExportTest extends DirectTest with TestHelpers { object B """ hasErrors """ - |newSource1.scala:3: error: @JSExport is forbidden on objects and classes. Use @JSExportTopLevel instead. + |newSource1.scala:3: error: @JSExport is forbidden on top-level objects and classes. Use @JSExportTopLevel instead. | @JSExport | ^ - |newSource1.scala:6: error: @JSExport is forbidden on objects and classes. Use @JSExportTopLevel instead. + |newSource1.scala:6: error: @JSExport is forbidden on top-level objects and classes. Use @JSExportTopLevel instead. | @JSExport("Foo") | ^ """ @@ -328,10 +328,10 @@ class JSExportTest extends DirectTest with TestHelpers { } """ hasErrors """ - |newSource1.scala:5: error: @JSExport is forbidden on objects and classes. Use @JSExportTopLevel instead. + |newSource1.scala:5: error: You may not export constructors of local classes | @JSExport | ^ - |newSource1.scala:8: error: @JSExport is forbidden on objects and classes. Use @JSExportTopLevel instead. + |newSource1.scala:8: error: You may not export a local definition | @JSExport | ^ """ @@ -349,10 +349,10 @@ class JSExportTest extends DirectTest with TestHelpers { } """ hasErrors """ - |newSource1.scala:5: error: @JSExport is forbidden on objects and classes. Use @JSExportTopLevel instead. + |newSource1.scala:5: error: You may not export a local definition | @JSExport | ^ - |newSource1.scala:8: error: @JSExport is forbidden on objects and classes. Use @JSExportTopLevel instead. + |newSource1.scala:8: error: You may not export a local definition | @JSExport | ^ """ @@ -414,7 +414,7 @@ class JSExportTest extends DirectTest with TestHelpers { } """ hasErrors """ - |newSource1.scala:4: error: In an exported method, a *-parameter must come last (through all parameter lists) + |newSource1.scala:4: error: In an exported method or constructor, a *-parameter must come last (through all parameter lists) | @JSExport | ^ """ @@ -431,7 +431,7 @@ class JSExportTest extends DirectTest with TestHelpers { } """ hasErrors """ - |newSource1.scala:4: error: In an exported method, all parameters with defaults must be at the end + |newSource1.scala:4: error: In an exported method or constructor, all parameters with defaults must be at the end | @JSExport | ^ """ @@ -441,6 +441,29 @@ class JSExportTest extends DirectTest with TestHelpers { @Test def noExportAbstractClass(): Unit = { + """ + object Foo { + @JSExport + abstract class A + + abstract class B(x: Int) { + @JSExport + def this() = this(5) + } + + @JSExport // ok! + abstract class C extends js.Object + } + """ hasErrors + """ + |newSource1.scala:4: error: You may not export an abstract class + | @JSExport + | ^ + |newSource1.scala:8: error: You may not export an abstract class + | @JSExport + | ^ + """ + """ @JSExportTopLevel("A") abstract class A @@ -487,6 +510,24 @@ class JSExportTest extends DirectTest with TestHelpers { | ^ """ + """ + object A { + @JSExport + trait Test + + @JSExport + trait Test2 extends js.Object + } + """ hasErrors + """ + |newSource1.scala:4: error: You may not export a trait + | @JSExport + | ^ + |newSource1.scala:7: error: You may not export a trait + | @JSExport + | ^ + """ + } @Test @@ -504,20 +545,28 @@ class JSExportTest extends DirectTest with TestHelpers { @JSExportTopLevel("D") protected[this] class D extends js.Object + + private class E(x: Int) { + @JSExportTopLevel("E") + def this() = this(1) + } """ hasErrors """ - |newSource1.scala:3: error: You may only export public and protected classes + |newSource1.scala:3: error: You may only export constructors of public and protected classes | @JSExportTopLevel("A") | ^ - |newSource1.scala:6: error: You may only export public and protected classes + |newSource1.scala:6: error: You may only export constructors of public and protected classes | @JSExportTopLevel("B") | ^ - |newSource1.scala:9: error: You may only export public and protected classes + |newSource1.scala:9: error: You may only export public and protected definitions | @JSExportTopLevel("C") | ^ - |newSource1.scala:12: error: You may only export public and protected classes + |newSource1.scala:12: error: You may only export public and protected definitions | @JSExportTopLevel("D") | ^ + |newSource1.scala:16: error: You may only export constructors of public and protected classes + | @JSExportTopLevel("E") + | ^ """ """ @@ -534,16 +583,16 @@ class JSExportTest extends DirectTest with TestHelpers { protected[this] object D extends js.Object """ hasErrors """ - |newSource1.scala:3: error: You may only export public and protected objects + |newSource1.scala:3: error: You may only export public and protected definitions | @JSExportTopLevel("A") | ^ - |newSource1.scala:6: error: You may only export public and protected objects + |newSource1.scala:6: error: You may only export public and protected definitions | @JSExportTopLevel("B") | ^ - |newSource1.scala:9: error: You may only export public and protected objects + |newSource1.scala:9: error: You may only export public and protected definitions | @JSExportTopLevel("C") | ^ - |newSource1.scala:12: error: You may only export public and protected objects + |newSource1.scala:12: error: You may only export public and protected definitions | @JSExportTopLevel("D") | ^ """ @@ -563,92 +612,10 @@ class JSExportTest extends DirectTest with TestHelpers { } """ hasErrors """ - |newSource1.scala:4: error: You may only export public and protected methods - | @JSExport - | ^ - |newSource1.scala:7: error: You may only export public and protected methods - | @JSExport - | ^ - """ - - } - - @Test - def noExportNestedClass(): Unit = { - - """ - class A { - @JSExport - class Nested { - @JSExport - def this(x: Int) = this() - } - - @JSExport - class Nested2 extends js.Object - } - """ hasErrors - """ - |newSource1.scala:4: error: @JSExport is forbidden on objects and classes. Use @JSExportTopLevel instead. - | @JSExport - | ^ - |newSource1.scala:6: error: @JSExport is forbidden on objects and classes. Use @JSExportTopLevel instead. - | @JSExport - | ^ - |newSource1.scala:10: error: @JSExport is forbidden on objects and classes. Use @JSExportTopLevel instead. - | @JSExport - | ^ - """ - - } - - @Test - def noNestedExportClass: Unit = { - - """ - object A { - @JSExport - class Nested { - @JSExport - def this(x: Int) = this - } - - @JSExport - class Nested2 extends js.Object - } - """ hasErrors - """ - - |newSource1.scala:4: error: @JSExport is forbidden on objects and classes. Use @JSExportTopLevel instead. - | @JSExport - | ^ - |newSource1.scala:6: error: @JSExport is forbidden on objects and classes. Use @JSExportTopLevel instead. - | @JSExport - | ^ - |newSource1.scala:10: error: @JSExport is forbidden on objects and classes. Use @JSExportTopLevel instead. - | @JSExport - | ^ - """ - - } - - @Test - def noNestedExportObject(): Unit = { - - """ - object A { - @JSExport - object Nested - - @JSExport - object Nested2 extends js.Object - } - """ hasErrors - """ - |newSource1.scala:4: error: @JSExport is forbidden on objects and classes. Use @JSExportTopLevel instead. + |newSource1.scala:4: error: You may only export public and protected definitions | @JSExport | ^ - |newSource1.scala:7: error: @JSExport is forbidden on objects and classes. Use @JSExportTopLevel instead. + |newSource1.scala:7: error: You may only export public and protected definitions | @JSExport | ^ """ @@ -668,10 +635,10 @@ class JSExportTest extends DirectTest with TestHelpers { } """ hasErrors """ - |newSource1.scala:4: error: You may not export a nested object + |newSource1.scala:4: error: Only static objects may export their members to the top level | @JSExportTopLevel("Nested") | ^ - |newSource1.scala:7: error: You may not export a nested object + |newSource1.scala:7: error: Only static objects may export their members to the top level | @JSExportTopLevel("Nested2") | ^ """ @@ -690,7 +657,7 @@ class JSExportTest extends DirectTest with TestHelpers { object A extends js.Object """ hasErrors """ - |newSource1.scala:5: error: You may not export a native JS object + |newSource1.scala:5: error: You may not export a native JS definition | @JSExportTopLevel("A") | ^ """ @@ -720,14 +687,29 @@ class JSExportTest extends DirectTest with TestHelpers { } """ hasErrors """ - |newSource1.scala:5: error: You may not export a native JS class + |newSource1.scala:5: error: You may not export a native JS definition | @JSExportTopLevel("A") | ^ - |newSource1.scala:9: error: You may not export a constructor of a subclass of js.Any + |newSource1.scala:9: error: You may not export a member of a subclass of js.Any | @JSExportTopLevel("A") | ^ """ + """ + import scala.scalajs.js + + object A { + @JSExport("A") + @js.native + @JSGlobal("a") + def a(x: Int): Int = js.native + } + """ hasErrors + """ + |newSource1.scala:6: error: You may not export a native JS definition + | @JSExport("A") + | ^ + """ } @Test @@ -744,7 +726,7 @@ class JSExportTest extends DirectTest with TestHelpers { } """ hasErrors """ - |newSource1.scala:8: error: You may not export a method of a subclass of js.Any + |newSource1.scala:8: error: You may not export a member of a subclass of js.Any | @JSExport | ^ """ @@ -758,7 +740,7 @@ class JSExportTest extends DirectTest with TestHelpers { } """ hasErrors """ - |newSource1.scala:6: error: You may not export a method of a subclass of js.Any + |newSource1.scala:6: error: You may not export a member of a subclass of js.Any | @JSExport | ^ """ @@ -822,6 +804,43 @@ class JSExportTest extends DirectTest with TestHelpers { } + @Test + def noNonSetterNameForNonSetter(): Unit = { + + """ + class A { + @JSExport + class A_= + } + """ hasErrors + """ + |newSource1.scala:4: error: You must set an explicit name when exporting a non-setter with a name ending in _= + | @JSExport + | ^ + """ + + """ + class A { + @JSExport + object A_= + } + """ hasErrors + """ + |newSource1.scala:4: error: You must set an explicit name when exporting a non-setter with a name ending in _= + | @JSExport + | ^ + """ + + // Not a Scala.js error, but we check it anyways to complete the test suite. + """ + class A { + @JSExport + val A_= = 1 + } + """.fails() // error is different on 2.12 / 2.13 + + } + @Test def noBadToStringExport(): Unit = { @@ -1021,6 +1040,28 @@ class JSExportTest extends DirectTest with TestHelpers { } """.hasNoWarns() + """ + class StaticContainer extends js.Object + + object StaticContainer { + @JSExportStatic + def apply(): Int = 1 + } + """ hasErrors + """ + |newSource1.scala:6: error: A member cannot be exported to function application as static. Use @JSExportStatic("apply") to export it under the name 'apply'. + | @JSExportStatic + | ^ + """ + + """ + class StaticContainer extends js.Object + + object StaticContainer { + @JSExportStatic("apply") + def apply(): Int = 1 + } + """.hasNoWarns() } @Test @@ -1281,7 +1322,7 @@ class JSExportTest extends DirectTest with TestHelpers { } """ hasErrors """ - |newSource1.scala:4: error: You may not export a nested object + |newSource1.scala:4: error: Only static objects may export their members to the top level | @JSExportTopLevel("Foo") | ^ """ @@ -1293,7 +1334,7 @@ class JSExportTest extends DirectTest with TestHelpers { } """ hasErrors """ - |newSource1.scala:4: error: You may not export a nested object + |newSource1.scala:4: error: Only static objects may export their members to the top level | @JSExportTopLevel("Foo") | ^ """ @@ -1305,7 +1346,7 @@ class JSExportTest extends DirectTest with TestHelpers { } """ hasErrors """ - |newSource1.scala:4: error: You may not export a nested class. Create an exported factory method in the outer class to work around this limitation. + |newSource1.scala:4: error: Only static objects may export their members to the top level | @JSExportTopLevel("Foo") | ^ """ @@ -1317,7 +1358,7 @@ class JSExportTest extends DirectTest with TestHelpers { } """ hasErrors """ - |newSource1.scala:4: error: You may not export a nested class. Create an exported factory method in the outer class to work around this limitation. + |newSource1.scala:4: error: Only static objects may export their members to the top level | @JSExportTopLevel("Foo") | ^ """ @@ -1338,10 +1379,10 @@ class JSExportTest extends DirectTest with TestHelpers { } """ hasErrors """ - |newSource1.scala:5: error: You may not export a local class + |newSource1.scala:5: error: You may not export constructors of local classes | @JSExportTopLevel("A") | ^ - |newSource1.scala:8: error: You may not export a local class + |newSource1.scala:8: error: You may not export a local definition | @JSExportTopLevel("B") | ^ """ @@ -1359,10 +1400,10 @@ class JSExportTest extends DirectTest with TestHelpers { } """ hasErrors """ - |newSource1.scala:5: error: You may not export a local object + |newSource1.scala:5: error: You may not export a local definition | @JSExportTopLevel("A") | ^ - |newSource1.scala:8: error: You may not export a local object + |newSource1.scala:8: error: You may not export a local definition | @JSExportTopLevel("B") | ^ """ @@ -1377,7 +1418,7 @@ class JSExportTest extends DirectTest with TestHelpers { } """ hasErrors """ - |newSource1.scala:4: error: You may not export a method of a subclass of js.Any + |newSource1.scala:4: error: You may not export a member of a subclass of js.Any | @JSExportTopLevel("foo") | ^ """ @@ -1411,7 +1452,7 @@ class JSExportTest extends DirectTest with TestHelpers { } """ hasErrors """ - |newSource1.scala:6: error: You may not export a trait as static. + |newSource1.scala:6: error: You may not export a trait | @JSExportStatic | ^ """ @@ -1783,7 +1824,7 @@ class JSExportTest extends DirectTest with TestHelpers { } """ hasErrors """ - |newSource1.scala:6: error: You may not export a method of a subclass of js.Any + |newSource1.scala:6: error: You may not export a member of a subclass of js.Any | @JSExportStatic | ^ """ @@ -1799,7 +1840,7 @@ class JSExportTest extends DirectTest with TestHelpers { } """ hasErrors """ - |newSource1.scala:8: error: You may not export a method of a subclass of js.Any + |newSource1.scala:8: error: You may not export a member of a subclass of js.Any | @JSExportStatic | ^ """ diff --git a/compiler/src/test/scala/org/scalajs/nscplugin/test/JSGlobalScopeTest.scala b/compiler/src/test/scala/org/scalajs/nscplugin/test/JSGlobalScopeTest.scala index a00d6a2909..6d5628abf2 100644 --- a/compiler/src/test/scala/org/scalajs/nscplugin/test/JSGlobalScopeTest.scala +++ b/compiler/src/test/scala/org/scalajs/nscplugin/test/JSGlobalScopeTest.scala @@ -249,7 +249,7 @@ class JSGlobalScopeTest extends DirectTest with TestHelpers { } """ hasErrors s""" - |newSource1.scala:41: warning: method apply in object global is deprecated${since("forever")}: The global scope cannot be called as function. + |newSource1.scala:41: warning: method apply in object global is deprecated (since forever): The global scope cannot be called as function. | val a = js.Dynamic.global(3) | ^ |newSource1.scala:41: error: Loading the global scope as a value (anywhere but as the left-hand-side of a `.`-selection) is not allowed. diff --git a/compiler/src/test/scala/org/scalajs/nscplugin/test/JSInteropTest.scala b/compiler/src/test/scala/org/scalajs/nscplugin/test/JSInteropTest.scala index 1c3f0e4330..582aba2f40 100644 --- a/compiler/src/test/scala/org/scalajs/nscplugin/test/JSInteropTest.scala +++ b/compiler/src/test/scala/org/scalajs/nscplugin/test/JSInteropTest.scala @@ -38,8 +38,7 @@ class JSInteropTest extends DirectTest with TestHelpers { private def version = scala.util.Properties.versionNumberString private def ifHasNewRefChecks(msg: String): String = { - if (version.startsWith("2.11.") || - version.startsWith("2.12.")) { + if (version.startsWith("2.12.")) { "" } else { msg.stripMargin.trim() @@ -4420,8 +4419,6 @@ class JSInteropTest extends DirectTest with TestHelpers { val postUnarySpace = { val hasNoSpace = { - version.startsWith("2.11.") || - version == "2.12.1" || version == "2.12.2" || version == "2.12.3" || version == "2.12.4" || diff --git a/compiler/src/test/scala/org/scalajs/nscplugin/test/JSOptionalTest.scala b/compiler/src/test/scala/org/scalajs/nscplugin/test/JSOptionalTest.scala index 2fa4d0b6e9..c275ea0d59 100644 --- a/compiler/src/test/scala/org/scalajs/nscplugin/test/JSOptionalTest.scala +++ b/compiler/src/test/scala/org/scalajs/nscplugin/test/JSOptionalTest.scala @@ -82,12 +82,12 @@ class JSOptionalTest extends DirectTest with TestHelpers { | ^ """ - // Also for custom JS function types (2.11 has more errors than expected here) + // Also for custom JS function types s""" trait A extends js.Function { def apply(x: js.UndefOr[Int] = 1): Int } - """ containsErrors + """ hasErrors """ |newSource1.scala:6: error: Members of non-native JS traits may not have default parameters unless their default is `js.undefined`. | def apply(x: js.UndefOr[Int] = 1): Int diff --git a/compiler/src/test/scala/org/scalajs/nscplugin/test/JSSAMTest.scala b/compiler/src/test/scala/org/scalajs/nscplugin/test/JSSAMTest.scala index 157a38156a..3513fff2e3 100644 --- a/compiler/src/test/scala/org/scalajs/nscplugin/test/JSSAMTest.scala +++ b/compiler/src/test/scala/org/scalajs/nscplugin/test/JSSAMTest.scala @@ -22,13 +22,6 @@ import org.junit.Test class JSSAMTest extends DirectTest with TestHelpers { - override def extraArgs: List[String] = { - if (scalaVersion.startsWith("2.11.")) - super.extraArgs :+ "-Xexperimental" - else - super.extraArgs - } - override def preamble: String = """ import scala.scalajs.js @@ -36,46 +29,7 @@ class JSSAMTest extends DirectTest with TestHelpers { """ @Test - def noSAMAsJSType211: Unit = { - assumeTrue(scalaVersion.startsWith("2.11.")) - - """ - @js.native - trait Foo extends js.Object { - def foo(x: Int): Int - } - - trait Bar extends js.Object { - def bar(x: Int): Int - } - - class Foobar extends js.Function { - def foobar(x: Int): Int - } - - class A { - val foo: Foo = x => x + 1 - val bar: Bar = x => x + 1 - val foobar: Foobar = x => x + 1 - } - """ hasErrors - """ - |newSource1.scala:19: error: Non-native JS types cannot directly extend native JS traits. - | val foo: Foo = x => x + 1 - | ^ - |newSource1.scala:20: error: $anonfun extends scala.Serializable which does not extend js.Any. - | val bar: Bar = x => x + 1 - | ^ - |newSource1.scala:21: error: $anonfun extends scala.Serializable which does not extend js.Any. - | val foobar: Foobar = x => x + 1 - | ^ - """ - } - - @Test - def noSAMAsJSType212Plus: Unit = { - assumeTrue(!scalaVersion.startsWith("2.11.")) - + def noSAMAsJSType: Unit = { """ @js.native trait Foo extends js.Object { @@ -110,39 +64,7 @@ class JSSAMTest extends DirectTest with TestHelpers { } @Test - def noSAMOfNativeJSFunctionType211: Unit = { - assumeTrue(scalaVersion.startsWith("2.11.")) - - """ - @js.native - trait Foo extends js.Function { - def apply(x: Int): Int - } - - @js.native - trait Bar extends js.Function { - def bar(x: Int = 5): Int - } - - class A { - val foo: Foo = x => x + 1 - val bar: Bar = x => x + 1 - } - """ hasErrors - """ - |newSource1.scala:16: error: Non-native JS types cannot directly extend native JS traits. - | val foo: Foo = x => x + 1 - | ^ - |newSource1.scala:17: error: Non-native JS types cannot directly extend native JS traits. - | val bar: Bar = x => x + 1 - | ^ - """ - } - - @Test - def noSAMOfNativeJSFunctionType212Plus: Unit = { - assumeTrue(!scalaVersion.startsWith("2.11.")) - + def noSAMOfNativeJSFunctionType: Unit = { """ @js.native trait Foo extends js.Function { diff --git a/compiler/src/test/scala/org/scalajs/nscplugin/test/OptimizationTest.scala b/compiler/src/test/scala/org/scalajs/nscplugin/test/OptimizationTest.scala index 227eafde4d..1e014eb873 100644 --- a/compiler/src/test/scala/org/scalajs/nscplugin/test/OptimizationTest.scala +++ b/compiler/src/test/scala/org/scalajs/nscplugin/test/OptimizationTest.scala @@ -546,12 +546,8 @@ object OptimizationTest { private val applySimpleMethodName = SimpleMethodName("apply") - private val hasOldCollections = { - val version = scala.util.Properties.versionNumberString - - version.startsWith("2.11.") || - version.startsWith("2.12.") - } + private val hasOldCollections = + scala.util.Properties.versionNumberString.startsWith("2.12.") private object WrapArrayCall { private val WrappedArrayTypeRef = { diff --git a/compiler/src/test/scala/org/scalajs/nscplugin/test/StaticForwardersASTTest.scala b/compiler/src/test/scala/org/scalajs/nscplugin/test/StaticForwardersASTTest.scala index e718a73ff4..3505a06eaf 100644 --- a/compiler/src/test/scala/org/scalajs/nscplugin/test/StaticForwardersASTTest.scala +++ b/compiler/src/test/scala/org/scalajs/nscplugin/test/StaticForwardersASTTest.scala @@ -43,10 +43,10 @@ class StaticForwardersASTTest extends JSASTTest { case cd: ClassDef if cd.name.name == ClassName("Foo") => cd } - val staticMethodNames = classDef.memberDefs.collect { - case MethodDef(flags, MethodIdent(name), _, _, _, _) if flags.namespace.isStatic => - name - }.sortBy(_.simpleName) + val staticMethodNames = classDef.methods + .withFilter(_.flags.namespace.isStatic) + .map(_.name.name) + .sortBy(_.simpleName) assertEquals( List( @@ -74,10 +74,10 @@ class StaticForwardersASTTest extends JSASTTest { case cd: ClassDef if cd.name.name == ClassName("Foo") => cd } - val staticMethodNames = classDef.memberDefs.collect { - case MethodDef(flags, MethodIdent(name), _, _, _, _) if flags.namespace.isStatic => - name - }.sortBy(_.simpleName) + val staticMethodNames = classDef.methods + .withFilter(_.flags.namespace.isStatic) + .map(_.name.name) + .sortBy(_.simpleName) assertEquals( List( diff --git a/compiler/src/test/scala/org/scalajs/nscplugin/test/StaticForwardersWarningsTopLevelOnlyTest.scala b/compiler/src/test/scala/org/scalajs/nscplugin/test/StaticForwardersWarningsTopLevelOnlyTest.scala index e3e9cf39ea..e0781dcf1b 100644 --- a/compiler/src/test/scala/org/scalajs/nscplugin/test/StaticForwardersWarningsTopLevelOnlyTest.scala +++ b/compiler/src/test/scala/org/scalajs/nscplugin/test/StaticForwardersWarningsTopLevelOnlyTest.scala @@ -25,8 +25,6 @@ class StaticForwardersWarningsTopLevelOnlyTest extends DirectTest with TestHelpe @Test def warnWhenAvoidingStaticForwardersForTopLevelObject: Unit = { val jvmBackendIssuesWarningOfItsOwn = { - !scalaVersion.startsWith("2.11.") && - scalaVersion != "2.12.1" && scalaVersion != "2.12.2" && scalaVersion != "2.12.3" && scalaVersion != "2.12.4" diff --git a/compiler/src/test/scala/org/scalajs/nscplugin/test/util/JSASTTest.scala b/compiler/src/test/scala/org/scalajs/nscplugin/test/util/JSASTTest.scala index 7b1e52ca44..76b7be968a 100644 --- a/compiler/src/test/scala/org/scalajs/nscplugin/test/util/JSASTTest.scala +++ b/compiler/src/test/scala/org/scalajs/nscplugin/test/util/JSASTTest.scala @@ -62,9 +62,24 @@ abstract class JSASTTest extends DirectTest { super.traverseClassDef(classDef) } - override def traverseMemberDef(memberDef: js.MemberDef): Unit = { - handle(memberDef) - super.traverseMemberDef(memberDef) + override def traverseAnyFieldDef(fieldDef: js.AnyFieldDef): Unit = { + handle(fieldDef) + super.traverseAnyFieldDef(fieldDef) + } + + override def traverseMethodDef(methodDef: js.MethodDef): Unit = { + handle(methodDef) + super.traverseMethodDef(methodDef) + } + + override def traverseJSConstructorDef(jsConstructor: js.JSConstructorDef): Unit = { + handle(jsConstructor) + super.traverseJSConstructorDef(jsConstructor) + } + + override def traverseJSMethodPropDef(jsMethodPropDef: js.JSMethodPropDef): Unit = { + handle(jsMethodPropDef) + super.traverseJSMethodPropDef(jsMethodPropDef) } override def traverseTopLevelExportDef( diff --git a/compiler/src/test/scala/org/scalajs/nscplugin/test/util/TestHelpers.scala b/compiler/src/test/scala/org/scalajs/nscplugin/test/util/TestHelpers.scala index 8336f2e472..112b9aad99 100644 --- a/compiler/src/test/scala/org/scalajs/nscplugin/test/util/TestHelpers.scala +++ b/compiler/src/test/scala/org/scalajs/nscplugin/test/util/TestHelpers.scala @@ -32,12 +32,6 @@ trait TestHelpers extends DirectTest { /** will be prefixed to every code that is compiled. use for imports */ def preamble: String = "" - protected def since(v: String): String = { - val version = scala.util.Properties.versionNumberString - if (version.startsWith("2.11.")) "" - else s" (since $v)" - } - /** pimps a string to compile it and apply the specified test */ implicit class CompileTests(val code: String) { private lazy val (success, output) = { diff --git a/compiler/src/test/scala/org/scalajs/nscplugin/test/util/VersionDependentUtils.scala b/compiler/src/test/scala/org/scalajs/nscplugin/test/util/VersionDependentUtils.scala index fe976ebe9e..5b54f73a1b 100644 --- a/compiler/src/test/scala/org/scalajs/nscplugin/test/util/VersionDependentUtils.scala +++ b/compiler/src/test/scala/org/scalajs/nscplugin/test/util/VersionDependentUtils.scala @@ -17,7 +17,6 @@ object VersionDependentUtils { /** Does the current Scala version support the `@nowarn` annotation? */ val scalaSupportsNoWarn = { - !scalaVersion.startsWith("2.11.") && !scalaVersion.startsWith("2.12.") && scalaVersion != "2.13.0" && scalaVersion != "2.13.1" @@ -27,7 +26,6 @@ object VersionDependentUtils { /* Yes, this is the same test as in scalaSupportsNoWarn, but that's * completely coincidental, so we have a copy. */ - !scalaVersion.startsWith("2.11.") && !scalaVersion.startsWith("2.12.") && scalaVersion != "2.13.0" && scalaVersion != "2.13.1" diff --git a/examples/helloworld/helloworld-2.11-fastopt.html b/examples/helloworld/helloworld-2.11-fastopt.html deleted file mode 100644 index 8f7eceddd9..0000000000 --- a/examples/helloworld/helloworld-2.11-fastopt.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - Hello world - Scala.js example - - - - -
-
- - - - - - - diff --git a/examples/helloworld/helloworld-2.11.html b/examples/helloworld/helloworld-2.11.html deleted file mode 100644 index ad39b7a9fe..0000000000 --- a/examples/helloworld/helloworld-2.11.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - Hello world - Scala.js example - - - - -
-
- - - - - - - diff --git a/examples/reversi/reversi-2.11-fastopt.html b/examples/reversi/reversi-2.11-fastopt.html deleted file mode 100644 index 0f60bc72c3..0000000000 --- a/examples/reversi/reversi-2.11-fastopt.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - Reversi - Scala.js example - - - - -

Reversi - Scala.js example

- -

Somewhat inspired by -http://davidbau.com/reversi/

- -
-
- - - - - - - - - diff --git a/examples/reversi/reversi-2.11.html b/examples/reversi/reversi-2.11.html deleted file mode 100644 index fe7b7a8dce..0000000000 --- a/examples/reversi/reversi-2.11.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - Reversi - Scala.js example - - - - -

Reversi - Scala.js example

- -

Somewhat inspired by -http://davidbau.com/reversi/

- -
-
- - - - - - - - - diff --git a/ir/shared/src/main/scala/org/scalajs/ir/EntryPointsInfo.scala b/ir/shared/src/main/scala/org/scalajs/ir/EntryPointsInfo.scala index e0f6f239b8..e64f831d69 100644 --- a/ir/shared/src/main/scala/org/scalajs/ir/EntryPointsInfo.scala +++ b/ir/shared/src/main/scala/org/scalajs/ir/EntryPointsInfo.scala @@ -24,13 +24,9 @@ object EntryPointsInfo { def forClassDef(classDef: ClassDef): EntryPointsInfo = { val hasEntryPoint = { classDef.topLevelExportDefs.nonEmpty || - classDef.memberDefs.exists { - case m: MethodDef => + classDef.methods.exists(m => m.flags.namespace == MemberNamespace.StaticConstructor && - m.methodName.isStaticInitializer - case _ => - false - } + m.methodName.isStaticInitializer) } new EntryPointsInfo(classDef.name.name, hasEntryPoint) } diff --git a/ir/shared/src/main/scala/org/scalajs/ir/Hashers.scala b/ir/shared/src/main/scala/org/scalajs/ir/Hashers.scala index cba76e8b64..9246cc6874 100644 --- a/ir/shared/src/main/scala/org/scalajs/ir/Hashers.scala +++ b/ir/shared/src/main/scala/org/scalajs/ir/Hashers.scala @@ -13,7 +13,6 @@ package org.scalajs.ir import java.io.{DataOutputStream, OutputStream} -import java.util.Arrays import Names._ import Trees._ @@ -23,8 +22,9 @@ import Tags._ object Hashers { def hashMethodDef(methodDef: MethodDef): MethodDef = { - if (methodDef.hash.isDefined) methodDef - else { + if (methodDef.version.isHash) { + methodDef + } else { val hasher = new TreeHasher() val MethodDef(flags, name, originalName, args, resultType, body) = methodDef @@ -40,12 +40,12 @@ object Hashers { val hash = hasher.finalizeHash() MethodDef(flags, name, originalName, args, resultType, body)( - methodDef.optimizerHints, Some(hash))(methodDef.pos) + methodDef.optimizerHints, hash)(methodDef.pos) } } def hashJSConstructorDef(ctorDef: JSConstructorDef): JSConstructorDef = { - if (ctorDef.hash.isDefined) { + if (ctorDef.version.isHash) { ctorDef } else { val hasher = new TreeHasher() @@ -62,13 +62,14 @@ object Hashers { val hash = hasher.finalizeHash() JSConstructorDef(flags, params, restParam, body)( - ctorDef.optimizerHints, Some(hash))(ctorDef.pos) + ctorDef.optimizerHints, hash)(ctorDef.pos) } } def hashJSMethodDef(methodDef: JSMethodDef): JSMethodDef = { - if (methodDef.hash.isDefined) methodDef - else { + if (methodDef.version.isHash) { + methodDef + } else { val hasher = new TreeHasher() val JSMethodDef(flags, name, params, restParam, body) = methodDef @@ -83,43 +84,57 @@ object Hashers { val hash = hasher.finalizeHash() JSMethodDef(flags, name, params, restParam, body)( - methodDef.optimizerHints, Some(hash))(methodDef.pos) + methodDef.optimizerHints, hash)(methodDef.pos) } } - /** Hash definitions from a ClassDef where applicable */ - def hashMemberDefs(memberDefs: List[MemberDef]): List[MemberDef] = memberDefs.map { - case methodDef: MethodDef => hashMethodDef(methodDef) - case ctorDef: JSConstructorDef => hashJSConstructorDef(ctorDef) - case methodDef: JSMethodDef => hashJSMethodDef(methodDef) - case otherDef => otherDef + def hashJSPropertyDef(propDef: JSPropertyDef): JSPropertyDef = { + if (propDef.version.isHash) { + propDef + } else { + val hasher = new TreeHasher() + val JSPropertyDef(flags, name, getterBody, setterArgAndBody) = propDef + + hasher.mixPos(propDef.pos) + hasher.mixInt(MemberFlags.toBits(flags)) + hasher.mixTree(name) + getterBody.foreach(hasher.mixTree(_)) + setterArgAndBody.foreach { case (param, body) => + hasher.mixParamDef(param) + hasher.mixTree(body) + } + + val hash = hasher.finalizeHash() + + JSPropertyDef(flags, name, getterBody, setterArgAndBody)(hash)(propDef.pos) + } + } + + def hashTopLevelExportDef(tle: TopLevelExportDef): TopLevelExportDef = tle match { + case TopLevelMethodExportDef(moduleID, methodDef) => + TopLevelMethodExportDef(moduleID, hashJSMethodDef(methodDef))(tle.pos) + + case _:TopLevelFieldExportDef | _:TopLevelModuleExportDef | + _:TopLevelJSClassExportDef => + tle } /** Hash the definitions in a ClassDef (where applicable) */ def hashClassDef(classDef: ClassDef): ClassDef = { import classDef._ - val newMemberDefs = hashMemberDefs(memberDefs) + val newMethods = methods.map(hashMethodDef(_)) + val newJSConstructorDef = jsConstructor.map(hashJSConstructorDef(_)) + val newExportedMembers = jsMethodProps.map { + case methodDef: JSMethodDef => hashJSMethodDef(methodDef) + case propDef: JSPropertyDef => hashJSPropertyDef(propDef) + } + val newTopLevelExportDefs = topLevelExportDefs.map(hashTopLevelExportDef(_)) ClassDef(name, originalName, kind, jsClassCaptures, superClass, interfaces, - jsSuperClass, jsNativeLoadSpec, newMemberDefs, topLevelExportDefs)( + jsSuperClass, jsNativeLoadSpec, fields, newMethods, newJSConstructorDef, + newExportedMembers, jsNativeMembers, newTopLevelExportDefs)( optimizerHints) } - def hashesEqual(x: TreeHash, y: TreeHash): Boolean = - Arrays.equals(x.hash, y.hash) - - def hashAsVersion(hash: TreeHash): String = { - // 2 chars per byte, 20 bytes in a hash - val size = 2 * 20 - val builder = new StringBuilder(size) - - def hexDigit(digit: Int): Char = Character.forDigit(digit, 16) - - for (b <- hash.hash) - builder.append(hexDigit((b >> 4) & 0x0f)).append(hexDigit(b & 0x0f)) - - builder.toString - } - private final class TreeHasher { private[this] val digestBuilder = new SHA1.DigestBuilder @@ -136,8 +151,8 @@ object Hashers { }) } - def finalizeHash(): TreeHash = - new TreeHash(digestBuilder.finalizeDigest()) + def finalizeHash(): Version = + Version.fromHash(digestBuilder.finalizeDigest()) def mixParamDef(paramDef: ParamDef): Unit = { mixPos(paramDef.pos) @@ -196,11 +211,6 @@ object Hashers { mixTree(cond) mixTree(body) - case DoWhile(body, cond) => - mixTag(TagDoWhile) - mixTree(body) - mixTree(cond) - case ForIn(obj, keyVar, keyVarOriginalName, body) => mixTag(TagForIn) mixTree(obj) diff --git a/ir/shared/src/main/scala/org/scalajs/ir/Names.scala b/ir/shared/src/main/scala/org/scalajs/ir/Names.scala index a3233877c3..aca9f38461 100644 --- a/ir/shared/src/main/scala/org/scalajs/ir/Names.scala +++ b/ir/shared/src/main/scala/org/scalajs/ir/Names.scala @@ -12,7 +12,7 @@ package org.scalajs.ir -import scala.annotation.switch +import scala.annotation.{switch, tailrec} import Types._ @@ -271,7 +271,8 @@ object Names { */ final class MethodName private (val simpleName: SimpleMethodName, val paramTypeRefs: List[TypeRef], val resultTypeRef: TypeRef, - val isReflectiveProxy: Boolean) { + val isReflectiveProxy: Boolean) + extends Comparable[MethodName] { import MethodName._ @@ -300,6 +301,35 @@ object Names { override def hashCode(): Int = _hashCode + def compareTo(that: MethodName): Int = { + @tailrec + def compareParamTypeRefs(xs: List[TypeRef], ys: List[TypeRef]): Int = (xs, ys) match { + case (x :: xr, y :: yr) => + val cmp = x.compareTo(y) + if (cmp != 0) cmp + else compareParamTypeRefs(xr, yr) + case _ => + java.lang.Boolean.compare(xs.isEmpty, ys.isEmpty) + } + + val simpleCmp = this.simpleName.compareTo(that.simpleName) + if (simpleCmp != 0) { + simpleCmp + } else { + val paramsCmp = compareParamTypeRefs(this.paramTypeRefs, that.paramTypeRefs) + if (paramsCmp != 0) { + paramsCmp + } else { + val reflProxyCmp = java.lang.Boolean.compare( + this.isReflectiveProxy, that.isReflectiveProxy) + if (reflProxyCmp != 0) + reflProxyCmp + else + this.resultTypeRef.compareTo(that.resultTypeRef) + } + } + } + protected def stringPrefix: String = "MethodName" def nameString: String = { @@ -509,6 +539,16 @@ object Names { val NegativeArraySizeExceptionClass: ClassName = ClassName("java.lang.NegativeArraySizeException") + /** The exception thrown by a variety of nodes for `null` arguments. + * + * - `Apply` and `ApplyStatically` for the receiver, + * - `Select` for the qualifier, + * - `ArrayLength` and `ArraySelect` for the array, + * - `GetClass`, `Clone` and `UnwrapFromException` for their respective only arguments. + */ + val NullPointerExceptionClass: ClassName = + ClassName("java.lang.NullPointerException") + /** The exception thrown by a `BinaryOp.String_charAt` that is out of bounds. */ val StringIndexOutOfBoundsExceptionClass: ClassName = ClassName("java.lang.StringIndexOutOfBoundsException") diff --git a/ir/shared/src/main/scala/org/scalajs/ir/Printers.scala b/ir/shared/src/main/scala/org/scalajs/ir/Printers.scala index 11dfdc81ee..f3efeb3d52 100644 --- a/ir/shared/src/main/scala/org/scalajs/ir/Printers.scala +++ b/ir/shared/src/main/scala/org/scalajs/ir/Printers.scala @@ -223,13 +223,6 @@ object Printers { print(") ") printBlock(body) - case DoWhile(body, cond) => - print("do ") - printBlock(body) - print(" while (") - print(cond) - print(')') - case ForIn(obj, keyVar, keyVarOriginalName, body) => print("for (val ") print(keyVar) @@ -953,7 +946,8 @@ object Printers { print(spec) } print(" ") - printColumn(memberDefs ::: topLevelExportDefs, "{", "", "}") + printColumn(fields ::: methods ::: jsConstructor.toList ::: + jsMethodProps ::: jsNativeMembers ::: topLevelExportDefs, "{", "", "}") } def print(memberDef: MemberDef): Unit = { diff --git a/ir/shared/src/main/scala/org/scalajs/ir/ScalaJSVersions.scala b/ir/shared/src/main/scala/org/scalajs/ir/ScalaJSVersions.scala index afb84b6a77..89ca31d304 100644 --- a/ir/shared/src/main/scala/org/scalajs/ir/ScalaJSVersions.scala +++ b/ir/shared/src/main/scala/org/scalajs/ir/ScalaJSVersions.scala @@ -17,8 +17,8 @@ import java.util.concurrent.ConcurrentHashMap import scala.util.matching.Regex object ScalaJSVersions extends VersionChecks( - current = "1.12.0", - binaryEmitted = "1.12" + current = "1.13.0", + binaryEmitted = "1.13" ) /** Helper class to allow for testing of logic. */ diff --git a/ir/shared/src/main/scala/org/scalajs/ir/Serializers.scala b/ir/shared/src/main/scala/org/scalajs/ir/Serializers.scala index 43c00f988a..a2eb58cd91 100644 --- a/ir/shared/src/main/scala/org/scalajs/ir/Serializers.scala +++ b/ir/shared/src/main/scala/org/scalajs/ir/Serializers.scala @@ -26,6 +26,7 @@ import Position._ import Trees._ import Types._ import Tags._ +import Version.Unversioned import Utils.JumpBackByteArrayOutputStream @@ -277,10 +278,6 @@ object Serializers { writeTagAndPos(TagWhile) writeTree(cond); writeTree(body) - case DoWhile(body, cond) => - writeTagAndPos(TagDoWhile) - writeTree(body); writeTree(cond) - case ForIn(obj, keyVar, keyVarOriginalName, body) => writeTagAndPos(TagForIn) writeTree(obj); writeLocalIdent(keyVar) @@ -624,7 +621,7 @@ object Serializers { writeClassIdents(interfaces) writeOptTree(jsSuperClass) writeJSNativeLoadSpec(jsNativeLoadSpec) - writeMemberDefs(memberDefs) + writeMemberDefs(fields ::: methods ::: jsConstructor.toList ::: jsMethodProps ::: jsNativeMembers) writeTopLevelExportDefs(topLevelExportDefs) writeInt(OptimizerHints.toBits(optimizerHints)) } @@ -650,7 +647,7 @@ object Serializers { val MethodDef(flags, name, originalName, args, resultType, body) = methodDef writeByte(TagMethodDef) - writeOptHash(methodDef.hash) + writeOptHash(methodDef.version) // Prepare for back-jump and write dummy length bufferUnderlying.markJump() @@ -671,7 +668,7 @@ object Serializers { val JSConstructorDef(flags, args, restParam, body) = ctorDef writeByte(TagJSConstructorDef) - writeOptHash(ctorDef.hash) + writeOptHash(ctorDef.version) // Prepare for back-jump and write dummy length bufferUnderlying.markJump() @@ -695,7 +692,7 @@ object Serializers { val JSMethodDef(flags, name, args, restParam, body) = methodDef writeByte(TagJSMethodDef) - writeOptHash(methodDef.hash) + writeOptHash(methodDef.version) // Prepare for back-jump and write dummy length bufferUnderlying.markJump() @@ -711,8 +708,17 @@ object Serializers { writeInt(length) bufferUnderlying.continue() - case JSPropertyDef(flags, name, getter, setterArgAndBody) => + case propDef: JSPropertyDef => + val JSPropertyDef(flags, name, getter, setterArgAndBody) = propDef + writeByte(TagJSPropertyDef) + writeOptHash(propDef.version) + + // Prepare for back-jump and write dummy length + bufferUnderlying.markJump() + writeInt(-1) + + // Write out prop def writeInt(MemberFlags.toBits(flags)) writeTree(name) writeOptTree(getter) @@ -721,6 +727,11 @@ object Serializers { writeParamDef(arg); writeTree(body) } + // Jump back and write true length + val length = bufferUnderlying.jumpBack() + writeInt(length) + bufferUnderlying.continue() + case JSNativeMemberDef(flags, name, jsNativeLoadSpec) => writeByte(TagJSNativeMemberDef) writeInt(MemberFlags.toBits(flags)) @@ -969,10 +980,11 @@ object Serializers { } } - def writeOptHash(optHash: Option[TreeHash]): Unit = { - buffer.writeBoolean(optHash.isDefined) - for (hash <- optHash) - buffer.write(hash.hash) + def writeOptHash(version: Version): Unit = { + val isHash = version.isHash + buffer.writeBoolean(isHash) + if (isHash) + version.writeHash(buffer) } def writeString(s: String): Unit = @@ -1112,7 +1124,15 @@ object Serializers { case TagReturn => Return(readTree(), readLabelIdent()) case TagIf => If(readTree(), readTree(), readTree())(readType()) case TagWhile => While(readTree(), readTree()) - case TagDoWhile => DoWhile(readTree(), readTree()) + + case TagDoWhile => + if (!hacks.use12) + throw new IOException(s"Found invalid pre-1.13 DoWhile loop at $pos") + // Rewrite `do { body } while (cond)` to `while ({ body; cond }) {}` + val body = readTree() + val cond = readTree() + While(Block(body, cond), Skip()) + case TagForIn => ForIn(readTree(), readLocalIdent(), readOriginalName(), readTree()) case TagTryCatch => @@ -1369,21 +1389,65 @@ object Serializers { val jsSuperClass = readOptTree() val jsNativeLoadSpec = readJSNativeLoadSpec() - val memberDefs0 = readMemberDefs(cls, kind) - val topLevelExportDefs = readTopLevelExportDefs(cls, kind) + + // Read member defs + val fieldsBuilder = List.newBuilder[AnyFieldDef] + val methodsBuilder = List.newBuilder[MethodDef] + val jsConstructorBuilder = new OptionBuilder[JSConstructorDef] + val jsMethodPropsBuilder = List.newBuilder[JSMethodPropDef] + val jsNativeMembersBuilder = List.newBuilder[JSNativeMemberDef] + + for (_ <- 0 until readInt()) { + implicit val pos = readPosition() + readByte() match { + case TagFieldDef => fieldsBuilder += readFieldDef() + case TagJSFieldDef => fieldsBuilder += readJSFieldDef() + case TagMethodDef => methodsBuilder += readMethodDef(cls, kind) + case TagJSConstructorDef => jsConstructorBuilder += readJSConstructorDef() + case TagJSMethodDef => jsMethodPropsBuilder += readJSMethodDef() + case TagJSPropertyDef => jsMethodPropsBuilder += readJSPropertyDef() + case TagJSNativeMemberDef => jsNativeMembersBuilder += readJSNativeMemberDef() + } + } + + val topLevelExportDefs = readTopLevelExportDefs() val optimizerHints = OptimizerHints.fromBits(readInt()) - val memberDefs = - if (hacks.use8 && kind.isJSClass) memberDefs0.map(jsConstructorDefHack(_)) - else memberDefs0 + val fields = fieldsBuilder.result() + + val methods = { + val methods0 = methodsBuilder.result() + if (hacks.use4 && kind.isJSClass) { + // #4409: Filter out abstract methods in non-native JS classes for version < 1.5 + methods0.filter(_.body.isDefined) + } else { + methods0 + } + } + + val (jsConstructor, jsMethodProps) = { + if (hacks.use8 && kind.isJSClass) { + assert(jsConstructorBuilder.result().isEmpty, "found JSConstructorDef in pre 1.8 IR") + jsConstructorHack(jsMethodPropsBuilder.result()) + } else { + (jsConstructorBuilder.result(), jsMethodPropsBuilder.result()) + } + } + + val jsNativeMembers = jsNativeMembersBuilder.result() ClassDef(name, originalName, kind, jsClassCaptures, superClass, parents, - jsSuperClass, jsNativeLoadSpec, memberDefs, topLevelExportDefs)( + jsSuperClass, jsNativeLoadSpec, fields, methods, jsConstructor, + jsMethodProps, jsNativeMembers, topLevelExportDefs)( optimizerHints) } - private def jsConstructorDefHack(memberDef: MemberDef): MemberDef = { - memberDef match { + private def jsConstructorHack( + jsMethodProps: List[JSMethodPropDef]): (Option[JSConstructorDef], List[JSMethodPropDef]) = { + val jsConstructorBuilder = new OptionBuilder[JSConstructorDef] + val jsMethodPropsBuilder = List.newBuilder[JSMethodPropDef] + + jsMethodProps.foreach { case methodDef @ JSMethodDef(flags, StringLiteral("constructor"), args, restParam, body) if flags.namespace == MemberNamespace.Public => val bodyStats = body match { @@ -1396,8 +1460,8 @@ object Serializers { val newFlags = flags.withNamespace(MemberNamespace.Constructor) val newBody = JSConstructorBody(beforeSuper, superCall, afterSuper)(body.pos) val ctorDef = JSConstructorDef(newFlags, args, restParam, newBody)( - methodDef.optimizerHints, None)(methodDef.pos) - Hashers.hashJSConstructorDef(ctorDef) + methodDef.optimizerHints, Unversioned)(methodDef.pos) + jsConstructorBuilder += Hashers.hashJSConstructorDef(ctorDef) case _ => /* This is awkward: we have an old-style JS constructor that is @@ -1408,249 +1472,232 @@ object Serializers { s"Found invalid pre-1.11 JS constructor def at ${methodDef.pos}:\n${methodDef.show}") } - case _ => - memberDef + case exportedMember => + jsMethodPropsBuilder += exportedMember } + + (jsConstructorBuilder.result(), jsMethodPropsBuilder.result()) } - def readMemberDef(owner: ClassName, ownerKind: ClassKind): MemberDef = { - implicit val pos = readPosition() - val tag = readByte() + private def readFieldDef()(implicit pos: Position): FieldDef = { + val flags = MemberFlags.fromBits(readInt()) + val name = readFieldIdent() + val originalName = readOriginalName() + + val ftpe0 = readType() + val ftpe = if (hacks.use4 && ftpe0 == NothingType) { + /* Note [Nothing FieldDef rewrite] + * val field: nothing --> val field: null + */ + NullType + } else { + ftpe0 + } - def bodyHack5(body: Tree, isStat: Boolean): Tree = { - if (!hacks.use5) { - body + FieldDef(flags, name, originalName, ftpe) + } + + private def readJSFieldDef()(implicit pos: Position): JSFieldDef = + JSFieldDef(MemberFlags.fromBits(readInt()), readTree(), readType()) + + private def readMethodDef(owner: ClassName, ownerKind: ClassKind)( + implicit pos: Position): MethodDef = { + val optHash = readOptHash() + // read and discard the length + val len = readInt() + assert(len >= 0) + + val flags = MemberFlags.fromBits(readInt()) + + val name = { + /* In versions 1.0 and 1.1 of the IR, static initializers and + * class initializers were conflated into one concept, which was + * handled differently in the linker based on whether the owner + * was a JS type or not. They were serialized as ``. + * Starting with 1.2, `` is only for class initializers. + * If we read a definition for a `` in a non-JS type, we + * rewrite it as a static initializers instead (``). + */ + val name0 = readMethodIdent() + if (hacks.use1 && + name0.name == ClassInitializerName && + !ownerKind.isJSType) { + MethodIdent(StaticInitializerName)(name0.pos) } else { - /* #4442 and #4601: Patch Labeled, If, Match and TryCatch nodes in - * statement position to have type NoType. These 4 nodes are the - * control structures whose result type is explicitly specified (and - * not derived from their children like Block or TryFinally, or - * constant like While). - */ - new Transformers.Transformer { - override def transform(tree: Tree, isStat: Boolean): Tree = { - val newTree = super.transform(tree, isStat) - if (isStat && newTree.tpe != NoType) { - newTree match { - case Labeled(label, _, body) => - Labeled(label, NoType, body)(newTree.pos) - case If(cond, thenp, elsep) => - If(cond, thenp, elsep)(NoType)(newTree.pos) - case Match(selector, cases, default) => - Match(selector, cases, default)(NoType)(newTree.pos) - case TryCatch(block, errVar, errVarOriginalName, handler) => - TryCatch(block, errVar, errVarOriginalName, handler)(NoType)(newTree.pos) - case _ => - newTree - } - } else { - newTree - } - } - }.transform(body, isStat) + name0 } } - def bodyHack5Expr(body: Tree): Tree = bodyHack5(body, isStat = false) - - (tag: @switch) match { - case TagFieldDef => - val flags = MemberFlags.fromBits(readInt()) - val name = readFieldIdent() - val originalName = readOriginalName() - - val ftpe0 = readType() - val ftpe = if (hacks.use4 && ftpe0 == NothingType) { - /* Note [Nothing FieldDef rewrite] - * val field: nothing --> val field: null - */ - NullType - } else { - ftpe0 - } - - FieldDef(flags, name, originalName, ftpe) - - case TagJSFieldDef => - JSFieldDef(MemberFlags.fromBits(readInt()), readTree(), readType()) - - case TagMethodDef => - val optHash = readOptHash() - // read and discard the length - val len = readInt() - assert(len >= 0) - - val flags = MemberFlags.fromBits(readInt()) - - val name = { - /* In versions 1.0 and 1.1 of the IR, static initializers and - * class initializers were conflated into one concept, which was - * handled differently in the linker based on whether the owner - * was a JS type or not. They were serialized as ``. - * Starting with 1.2, `` is only for class initializers. - * If we read a definition for a `` in a non-JS type, we - * rewrite it as a static initializers instead (``). - */ - val name0 = readMethodIdent() - if (hacks.use1 && - name0.name == ClassInitializerName && - !ownerKind.isJSType) { - MethodIdent(StaticInitializerName)(name0.pos) - } else { - name0 - } - } + val originalName = readOriginalName() + val args = readParamDefs() + val resultType = readType() + val body = readOptTree() + val optimizerHints = OptimizerHints.fromBits(readInt()) - val originalName = readOriginalName() - val args = readParamDefs() - val resultType = readType() - val body = readOptTree() - val optimizerHints = OptimizerHints.fromBits(readInt()) - - if (hacks.use0 && - flags.namespace == MemberNamespace.Public && - owner == HackNames.SystemModule && - name.name == HackNames.identityHashCodeName) { - /* #3976: 1.0 javalib relied on wrong linker dispatch. - * We simply replace it with a correct implementation. - */ - assert(args.size == 1) - - val patchedBody = Some(IdentityHashCode(args(0).ref)) - val patchedOptimizerHints = OptimizerHints.empty.withInline(true) - - MethodDef(flags, name, originalName, args, resultType, patchedBody)( - patchedOptimizerHints, optHash) - } else if (hacks.use4 && - flags.namespace == MemberNamespace.Public && - owner == ObjectClass && - name.name == HackNames.cloneName) { - /* #4391: In version 1.5, we introduced a dedicated IR node for the - * primitive operation behind `Object.clone()`. This allowed to - * simplify the linker by removing several special-cases that - * treated it specially (for example, preventing it from being - * inlined if the receiver could be an array). The simplifications - * mean that the old implementation is not valid anymore, and so we - * must force using the new implementation if we read IR from an - * older version. - */ - assert(args.isEmpty) - - val thisValue = This()(ClassType(ObjectClass)) - val cloneableClassType = ClassType(CloneableClass) - - val patchedBody = Some { - If(IsInstanceOf(thisValue, cloneableClassType), - Clone(AsInstanceOf(thisValue, cloneableClassType)), - Throw(New( - HackNames.CloneNotSupportedExceptionClass, - MethodIdent(NoArgConstructorName), - Nil)))(cloneableClassType) - } - val patchedOptimizerHints = OptimizerHints.empty.withInline(true) + if (hacks.use0 && + flags.namespace == MemberNamespace.Public && + owner == HackNames.SystemModule && + name.name == HackNames.identityHashCodeName) { + /* #3976: 1.0 javalib relied on wrong linker dispatch. + * We simply replace it with a correct implementation. + */ + assert(args.size == 1) + + val patchedBody = Some(IdentityHashCode(args(0).ref)) + val patchedOptimizerHints = OptimizerHints.empty.withInline(true) + + MethodDef(flags, name, originalName, args, resultType, patchedBody)( + patchedOptimizerHints, optHash) + } else if (hacks.use4 && + flags.namespace == MemberNamespace.Public && + owner == ObjectClass && + name.name == HackNames.cloneName) { + /* #4391: In version 1.5, we introduced a dedicated IR node for the + * primitive operation behind `Object.clone()`. This allowed to + * simplify the linker by removing several special-cases that + * treated it specially (for example, preventing it from being + * inlined if the receiver could be an array). The simplifications + * mean that the old implementation is not valid anymore, and so we + * must force using the new implementation if we read IR from an + * older version. + */ + assert(args.isEmpty) + + val thisValue = This()(ClassType(ObjectClass)) + val cloneableClassType = ClassType(CloneableClass) + + val patchedBody = Some { + If(IsInstanceOf(thisValue, cloneableClassType), + Clone(AsInstanceOf(thisValue, cloneableClassType)), + Throw(New( + HackNames.CloneNotSupportedExceptionClass, + MethodIdent(NoArgConstructorName), + Nil)))(cloneableClassType) + } + val patchedOptimizerHints = OptimizerHints.empty.withInline(true) - MethodDef(flags, name, originalName, args, resultType, patchedBody)( - patchedOptimizerHints, optHash) - } else { - val patchedBody = body.map(bodyHack5(_, isStat = resultType == NoType)) - MethodDef(flags, name, originalName, args, resultType, patchedBody)( - optimizerHints, optHash) - } + MethodDef(flags, name, originalName, args, resultType, patchedBody)( + patchedOptimizerHints, optHash) + } else { + val patchedBody = body.map(bodyHack5(_, isStat = resultType == NoType)) + MethodDef(flags, name, originalName, args, resultType, patchedBody)( + optimizerHints, optHash) + } + } - case TagJSConstructorDef => - val optHash = readOptHash() - // read and discard the length - val len = readInt() - assert(len >= 0) + private def readJSConstructorDef()(implicit pos: Position): JSConstructorDef = { + val optHash = readOptHash() + // read and discard the length + val len = readInt() + assert(len >= 0) - /* JSConstructorDef was introduced in 1.11. Therefore, by - * construction, they never need the body hack of 1.5. - */ + /* JSConstructorDef was introduced in 1.11. Therefore, by + * construction, they never need the body hack of 1.5. + */ - val flags = MemberFlags.fromBits(readInt()) - val (params, restParam) = readParamDefsWithRest() - val bodyPos = readPosition() - val beforeSuper = readTrees() - val superCall = readTree().asInstanceOf[JSSuperConstructorCall] - val afterSuper = readTrees() - val body = JSConstructorBody(beforeSuper, superCall, afterSuper)(bodyPos) - JSConstructorDef(flags, params, restParam, body)( - OptimizerHints.fromBits(readInt()), optHash) - - case TagJSMethodDef => + val flags = MemberFlags.fromBits(readInt()) + val (params, restParam) = readParamDefsWithRest() + val bodyPos = readPosition() + val beforeSuper = readTrees() + val superCall = readTree().asInstanceOf[JSSuperConstructorCall] + val afterSuper = readTrees() + val body = JSConstructorBody(beforeSuper, superCall, afterSuper)(bodyPos) + JSConstructorDef(flags, params, restParam, body)( + OptimizerHints.fromBits(readInt()), optHash) + } + + private def readJSMethodDef()(implicit pos: Position): JSMethodDef = { + val optHash = readOptHash() + // read and discard the length + val len = readInt() + assert(len >= 0) + + val flags = MemberFlags.fromBits(readInt()) + val name = bodyHack5Expr(readTree()) + val (params, restParam) = readParamDefsWithRest() + val body = bodyHack5Expr(readTree()) + JSMethodDef(flags, name, params, restParam, body)( + OptimizerHints.fromBits(readInt()), optHash) + } + + private def readJSPropertyDef()(implicit pos: Position): JSPropertyDef = { + val optHash: Version = { + if (hacks.use12) { + Unversioned + } else { val optHash = readOptHash() // read and discard the length val len = readInt() assert(len >= 0) + optHash + } + } - val flags = MemberFlags.fromBits(readInt()) - val name = bodyHack5Expr(readTree()) - val (params, restParam) = readParamDefsWithRest() - val body = bodyHack5Expr(readTree()) - JSMethodDef(flags, name, params, restParam, body)( - OptimizerHints.fromBits(readInt()), optHash) - - case TagJSPropertyDef => - val flags = MemberFlags.fromBits(readInt()) - val name = bodyHack5Expr(readTree()) - val getterBody = readOptTree().map(bodyHack5Expr(_)) - val setterArgAndBody = { - if (readBoolean()) - Some((readParamDef(), bodyHack5Expr(readTree()))) - else - None - } - JSPropertyDef(flags, name, getterBody, setterArgAndBody) - - case TagJSNativeMemberDef => - val flags = MemberFlags.fromBits(readInt()) - val name = readMethodIdent() - val jsNativeLoadSpec = readJSNativeLoadSpec().get - JSNativeMemberDef(flags, name, jsNativeLoadSpec) + val flags = MemberFlags.fromBits(readInt()) + val name = bodyHack5Expr(readTree()) + val getterBody = readOptTree().map(bodyHack5Expr(_)) + val setterArgAndBody = { + if (readBoolean()) + Some((readParamDef(), bodyHack5Expr(readTree()))) + else + None } + JSPropertyDef(flags, name, getterBody, setterArgAndBody)(optHash) } - def readMemberDefs(owner: ClassName, ownerKind: ClassKind): List[MemberDef] = { - val memberDefs = List.fill(readInt())(readMemberDef(owner, ownerKind)) + private def readJSNativeMemberDef()(implicit pos: Position): JSNativeMemberDef = { + val flags = MemberFlags.fromBits(readInt()) + val name = readMethodIdent() + val jsNativeLoadSpec = readJSNativeLoadSpec().get + JSNativeMemberDef(flags, name, jsNativeLoadSpec) + } - // #4409: Filter out abstract methods in non-native JS classes for version < 1.5 - if (ownerKind.isJSClass) { - if (hacks.use4) { - memberDefs.filter { m => - m match { - case MethodDef(_, _, _, _, _, None) => false - case _ => true - } - } - } else { - /* #4388 This check should be moved to a link-time check dependent on - * `checkIR`, but currently we only have the post-BaseLinker IR - * checker, at which points those methods have already been - * eliminated. - */ - for (m <- memberDefs) { - m match { - case MethodDef(_, _, _, _, _, None) => - throw new InvalidIRException(m, - "Invalid abstract method in non-native JS class") - case _ => - // ok + private def bodyHack5(body: Tree, isStat: Boolean): Tree = { + if (!hacks.use5) { + body + } else { + /* #4442 and #4601: Patch Labeled, If, Match and TryCatch nodes in + * statement position to have type NoType. These 4 nodes are the + * control structures whose result type is explicitly specified (and + * not derived from their children like Block or TryFinally, or + * constant like While). + */ + new Transformers.Transformer { + override def transform(tree: Tree, isStat: Boolean): Tree = { + val newTree = super.transform(tree, isStat) + if (isStat && newTree.tpe != NoType) { + newTree match { + case Labeled(label, _, body) => + Labeled(label, NoType, body)(newTree.pos) + case If(cond, thenp, elsep) => + If(cond, thenp, elsep)(NoType)(newTree.pos) + case Match(selector, cases, default) => + Match(selector, cases, default)(NoType)(newTree.pos) + case TryCatch(block, errVar, errVarOriginalName, handler) => + TryCatch(block, errVar, errVarOriginalName, handler)(NoType)(newTree.pos) + case _ => + newTree + } + } else { + newTree } } - memberDefs - } - } else { - memberDefs + }.transform(body, isStat) } } - def readTopLevelExportDef(owner: ClassName, - ownerKind: ClassKind): TopLevelExportDef = { + private def bodyHack5Expr(body: Tree): Tree = bodyHack5(body, isStat = false) + + def readTopLevelExportDef(): TopLevelExportDef = { implicit val pos = readPosition() val tag = readByte() - def readJSMethodDef(): JSMethodDef = - readMemberDef(owner, ownerKind).asInstanceOf[JSMethodDef] + def readJSMethodDef(): JSMethodDef = { + implicit val pos = readPosition() + val tag = readByte() + assert(tag == TagJSMethodDef, s"unexpected tag $tag") + this.readJSMethodDef() + } def readModuleID(): String = if (hacks.use2) DefaultModuleID @@ -1664,10 +1711,8 @@ object Serializers { } } - def readTopLevelExportDefs(owner: ClassName, - ownerKind: ClassKind): List[TopLevelExportDef] = { - List.fill(readInt())(readTopLevelExportDef(owner, ownerKind)) - } + def readTopLevelExportDefs(): List[TopLevelExportDef] = + List.fill(readInt())(readTopLevelExportDef()) def readLocalIdent(): LocalIdent = { implicit val pos = readPosition() @@ -1859,13 +1904,13 @@ object Serializers { } } - def readOptHash(): Option[TreeHash] = { + def readOptHash(): Version = { if (readBoolean()) { val hash = new Array[Byte](20) buf.get(hash) - Some(new TreeHash(hash)) + Version.fromHash(hash) } else { - None + Unversioned } } @@ -2021,6 +2066,13 @@ object Serializers { private val use7: Boolean = use6 || sourceVersion == "1.7" val use8: Boolean = use7 || sourceVersion == "1.8" + + assert(sourceVersion != "1.9", "source version 1.9 does not exist") + assert(sourceVersion != "1.10", "source version 1.10 does not exist") + + private val use11: Boolean = use8 || sourceVersion == "1.11" + + val use12: Boolean = use11 || sourceVersion == "1.12" } /** Names needed for hacks. */ @@ -2036,6 +2088,17 @@ object Serializers { MethodName("identityHashCode", List(ClassRef(ObjectClass)), IntRef) } + private class OptionBuilder[T] { + private[this] var value: Option[T] = None + + def +=(x: T): Unit = { + require(value.isEmpty) + value = Some(x) + } + + def result(): Option[T] = value + } + /* Note [Nothing FieldDef rewrite] * * Prior to Scala.js 1.5.0, the compiler back-end emitted `FieldDef`s with diff --git a/ir/shared/src/main/scala/org/scalajs/ir/Tags.scala b/ir/shared/src/main/scala/org/scalajs/ir/Tags.scala index a084304571..80b0774f31 100644 --- a/ir/shared/src/main/scala/org/scalajs/ir/Tags.scala +++ b/ir/shared/src/main/scala/org/scalajs/ir/Tags.scala @@ -31,7 +31,7 @@ private[ir] object Tags { final val TagReturn = TagAssign + 1 final val TagIf = TagReturn + 1 final val TagWhile = TagIf + 1 - final val TagDoWhile = TagWhile + 1 + final val TagDoWhile = TagWhile + 1 // removed in 1.13 final val TagForIn = TagDoWhile + 1 final val TagTryCatch = TagForIn + 1 final val TagTryFinally = TagTryCatch + 1 diff --git a/ir/shared/src/main/scala/org/scalajs/ir/Transformers.scala b/ir/shared/src/main/scala/org/scalajs/ir/Transformers.scala index 6f4820aab8..99a72aedb9 100644 --- a/ir/shared/src/main/scala/org/scalajs/ir/Transformers.scala +++ b/ir/shared/src/main/scala/org/scalajs/ir/Transformers.scala @@ -14,6 +14,7 @@ package org.scalajs.ir import Trees._ import Types._ +import Version.Unversioned object Transformers { @@ -66,9 +67,6 @@ object Transformers { case While(cond, body) => While(transformExpr(cond), transformStat(body)) - case DoWhile(body, cond) => - DoWhile(transformStat(body), transformExpr(cond)) - case ForIn(obj, keyVar, keyVarOriginalName, body) => ForIn(transformExpr(obj), keyVar, keyVarOriginalName, transformStat(body)) @@ -238,33 +236,33 @@ object Transformers { import tree._ ClassDef(name, originalName, kind, jsClassCaptures, superClass, interfaces, jsSuperClass.map(transformExpr), jsNativeLoadSpec, - memberDefs.map(transformMemberDef), + fields.map(transformAnyFieldDef(_)), + methods.map(transformMethodDef), jsConstructor.map(transformJSConstructorDef), + jsMethodProps.map(transformJSMethodPropDef), jsNativeMembers, topLevelExportDefs.map(transformTopLevelExportDef))( tree.optimizerHints)(tree.pos) } - def transformMemberDef(memberDef: MemberDef): MemberDef = { - implicit val pos = memberDef.pos - - memberDef match { - case _:AnyFieldDef | _:JSNativeMemberDef => - memberDef + def transformAnyFieldDef(fieldDef: AnyFieldDef): AnyFieldDef = + fieldDef - case memberDef: MethodDef => - val MethodDef(flags, name, originalName, args, resultType, body) = memberDef - val newBody = body.map(transform(_, isStat = resultType == NoType)) - MethodDef(flags, name, originalName, args, resultType, newBody)( - memberDef.optimizerHints, None) + def transformMethodDef(methodDef: MethodDef): MethodDef = { + val MethodDef(flags, name, originalName, args, resultType, body) = methodDef + val newBody = body.map(transform(_, isStat = resultType == NoType)) + MethodDef(flags, name, originalName, args, resultType, newBody)( + methodDef.optimizerHints, Unversioned)(methodDef.pos) + } - case memberDef: JSConstructorDef => - val JSConstructorDef(flags, args, restParam, body) = memberDef - JSConstructorDef(flags, args, restParam, transformJSConstructorBody(body))( - memberDef.optimizerHints, None) + def transformJSConstructorDef(jsConstructor: JSConstructorDef): JSConstructorDef = { + val JSConstructorDef(flags, args, restParam, body) = jsConstructor + JSConstructorDef(flags, args, restParam, transformJSConstructorBody(body))( + jsConstructor.optimizerHints, Unversioned)(jsConstructor.pos) + } - case memberDef: JSMethodDef => - val JSMethodDef(flags, name, args, restParam, body) = memberDef - JSMethodDef(flags, name, args, restParam, transformExpr(body))( - memberDef.optimizerHints, None) + def transformJSMethodPropDef(jsMethodPropDef: JSMethodPropDef): JSMethodPropDef = { + jsMethodPropDef match { + case jsMethodDef: JSMethodDef => + transformJSMethodDef(jsMethodDef) case JSPropertyDef(flags, name, getterBody, setterArgAndBody) => JSPropertyDef( @@ -273,10 +271,16 @@ object Transformers { getterBody.map(transformStat), setterArgAndBody map { case (arg, body) => (arg, transformStat(body)) - }) + })(Unversioned)(jsMethodPropDef.pos) } } + def transformJSMethodDef(jsMethodDef: JSMethodDef): JSMethodDef = { + val JSMethodDef(flags, name, args, restParam, body) = jsMethodDef + JSMethodDef(flags, name, args, restParam, transformExpr(body))( + jsMethodDef.optimizerHints, Unversioned)(jsMethodDef.pos) + } + def transformJSConstructorBody(body: JSConstructorBody): JSConstructorBody = { implicit val pos = body.pos @@ -301,8 +305,7 @@ object Transformers { exportDef case TopLevelMethodExportDef(moduleID, methodDef) => - TopLevelMethodExportDef(moduleID, - transformMemberDef(methodDef).asInstanceOf[JSMethodDef]) + TopLevelMethodExportDef(moduleID, transformJSMethodDef(methodDef)) } } } diff --git a/ir/shared/src/main/scala/org/scalajs/ir/Traversers.scala b/ir/shared/src/main/scala/org/scalajs/ir/Traversers.scala index d202f6a4f2..7a4f5d9756 100644 --- a/ir/shared/src/main/scala/org/scalajs/ir/Traversers.scala +++ b/ir/shared/src/main/scala/org/scalajs/ir/Traversers.scala @@ -52,10 +52,6 @@ object Traversers { traverse(cond) traverse(body) - case DoWhile(body, cond) => - traverse(body) - traverse(cond) - case ForIn(obj, _, _, body) => traverse(obj) traverse(body) @@ -233,20 +229,23 @@ object Traversers { def traverseClassDef(tree: ClassDef): Unit = { tree.jsSuperClass.foreach(traverse) - tree.memberDefs.foreach(traverseMemberDef) + tree.fields.foreach(traverseAnyFieldDef) + tree.methods.foreach(traverseMethodDef) + tree.jsConstructor.foreach(traverseJSConstructorDef) + tree.jsMethodProps.foreach(traverseJSMethodPropDef) tree.topLevelExportDefs.foreach(traverseTopLevelExportDef) } - def traverseMemberDef(memberDef: MemberDef): Unit = { - memberDef match { - case _:AnyFieldDef | _:JSNativeMemberDef => + def traverseAnyFieldDef(fieldDef: AnyFieldDef): Unit = () - case MethodDef(_, _, _, _, _, body) => - body.foreach(traverse) + def traverseMethodDef(methodDef: MethodDef): Unit = + methodDef.body.foreach(traverse) - case JSConstructorDef(_, _, _, body) => - body.allStats.foreach(traverse) + def traverseJSConstructorDef(jsConstructor: JSConstructorDef): Unit = + jsConstructor.body.allStats.foreach(traverse) + def traverseJSMethodPropDef(jsMethodPropDef: JSMethodPropDef): Unit = { + jsMethodPropDef match { case JSMethodDef(_, _, _, _, body) => traverse(body) @@ -262,7 +261,7 @@ object Traversers { _:TopLevelFieldExportDef => case TopLevelMethodExportDef(_, methodDef) => - traverseMemberDef(methodDef) + traverseJSMethodPropDef(methodDef) } } } 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 e454fdb196..0a680d0737 100644 --- a/ir/shared/src/main/scala/org/scalajs/ir/Trees.scala +++ b/ir/shared/src/main/scala/org/scalajs/ir/Trees.scala @@ -21,7 +21,7 @@ import Types._ object Trees { /* The case classes for IR Nodes are sealed instead of final because making - * them final triggers bugs with Scala 2.11.x and 2.12.{1-4}, in combination + * them final triggers bugs with Scala 2.12.{1-4}, in combination * with their `implicit val pos`. */ @@ -174,11 +174,6 @@ object Trees { } } - sealed case class DoWhile(body: Tree, cond: Tree)( - implicit val pos: Position) extends Tree { - val tpe = NoType // cannot be in expression position - } - sealed case class ForIn(obj: Tree, keyVar: LocalIdent, keyVarOriginalName: OriginalName, body: Tree)( implicit val pos: Position) extends Tree { @@ -1110,7 +1105,11 @@ object Trees { */ val jsSuperClass: Option[Tree], val jsNativeLoadSpec: Option[JSNativeLoadSpec], - val memberDefs: List[MemberDef], + val fields: List[AnyFieldDef], + val methods: List[MethodDef], + val jsConstructor: Option[JSConstructorDef], + val jsMethodProps: List[JSMethodPropDef], + val jsNativeMembers: List[JSNativeMemberDef], val topLevelExportDefs: List[TopLevelExportDef] )( val optimizerHints: OptimizerHints @@ -1128,13 +1127,17 @@ object Trees { interfaces: List[ClassIdent], jsSuperClass: Option[Tree], jsNativeLoadSpec: Option[JSNativeLoadSpec], - memberDefs: List[MemberDef], + fields: List[AnyFieldDef], + methods: List[MethodDef], + jsConstructor: Option[JSConstructorDef], + jsMethodProps: List[JSMethodPropDef], + jsNativeMembers: List[JSNativeMemberDef], topLevelExportDefs: List[TopLevelExportDef])( optimizerHints: OptimizerHints)( implicit pos: Position): ClassDef = { new ClassDef(name, originalName, kind, jsClassCaptures, superClass, - interfaces, jsSuperClass, jsNativeLoadSpec, memberDefs, - topLevelExportDefs)( + interfaces, jsSuperClass, jsNativeLoadSpec, fields, methods, + jsConstructor, jsMethodProps, jsNativeMembers, topLevelExportDefs)( optimizerHints) } } @@ -1150,6 +1153,10 @@ object Trees { val flags: MemberFlags } + sealed trait VersionedMemberDef extends MemberDef { + val version: Version + } + sealed abstract class AnyFieldDef extends MemberDef { // val name: Ident | Tree val ftpe: Type @@ -1165,16 +1172,16 @@ object Trees { sealed case class MethodDef(flags: MemberFlags, name: MethodIdent, originalName: OriginalName, args: List[ParamDef], resultType: Type, body: Option[Tree])( - val optimizerHints: OptimizerHints, val hash: Option[TreeHash])( - implicit val pos: Position) extends MemberDef { + val optimizerHints: OptimizerHints, val version: Version)( + implicit val pos: Position) extends VersionedMemberDef { def methodName: MethodName = name.name } sealed case class JSConstructorDef(flags: MemberFlags, args: List[ParamDef], restParam: Option[ParamDef], body: JSConstructorBody)( - val optimizerHints: OptimizerHints, val hash: Option[TreeHash])( + val optimizerHints: OptimizerHints, val version: Version)( implicit val pos: Position) - extends MemberDef + extends VersionedMemberDef sealed case class JSConstructorBody( beforeSuper: List[Tree], superCall: JSSuperConstructorCall, afterSuper: List[Tree])( @@ -1183,16 +1190,17 @@ object Trees { val allStats: List[Tree] = beforeSuper ::: superCall :: afterSuper } - sealed abstract class JSMethodPropDef extends MemberDef + sealed abstract class JSMethodPropDef extends VersionedMemberDef sealed case class JSMethodDef(flags: MemberFlags, name: Tree, args: List[ParamDef], restParam: Option[ParamDef], body: Tree)( - val optimizerHints: OptimizerHints, val hash: Option[TreeHash])( + val optimizerHints: OptimizerHints, val version: Version)( implicit val pos: Position) extends JSMethodPropDef sealed case class JSPropertyDef(flags: MemberFlags, name: Tree, getterBody: Option[Tree], setterArgAndBody: Option[(ParamDef, Tree)])( + val version: Version)( implicit val pos: Position) extends JSMethodPropDef @@ -1498,12 +1506,4 @@ object Trees { extends JSNativeLoadSpec } - - /** A hash of a tree (usually a MethodDef). - * - * Contains a SHA-1 hash. - */ - final class TreeHash(val hash: Array[Byte]) { - assert(hash.length == 20) - } } diff --git a/ir/shared/src/main/scala/org/scalajs/ir/Types.scala b/ir/shared/src/main/scala/org/scalajs/ir/Types.scala index a82570a840..459f42f457 100644 --- a/ir/shared/src/main/scala/org/scalajs/ir/Types.scala +++ b/ir/shared/src/main/scala/org/scalajs/ir/Types.scala @@ -170,7 +170,29 @@ object Types { * type refs that are used in method signatures, and which therefore dictate * JVM/IR overloading. */ - sealed abstract class TypeRef { + sealed abstract class TypeRef extends Comparable[TypeRef] { + final def compareTo(that: TypeRef): Int = this match { + case thiz: PrimRef => + that match { + case that: PrimRef => Character.compare(thiz.charCode, that.charCode) + case _ => -1 + } + case thiz: ClassRef => + that match { + case that: ClassRef => thiz.className.compareTo(that.className) + case that: PrimRef => 1 + case that: ArrayTypeRef => -1 + } + case thiz: ArrayTypeRef => + that match { + case that: ArrayTypeRef => + if (thiz.dimensions != that.dimensions) thiz.dimensions - that.dimensions + else thiz.base.compareTo(that.base) + case _ => + 1 + } + } + def show(): String = { val writer = new java.io.StringWriter val printer = new Printers.IRTreePrinter(writer) diff --git a/ir/shared/src/main/scala/org/scalajs/ir/Version.scala b/ir/shared/src/main/scala/org/scalajs/ir/Version.scala new file mode 100644 index 0000000000..6531b8c2a4 --- /dev/null +++ b/ir/shared/src/main/scala/org/scalajs/ir/Version.scala @@ -0,0 +1,146 @@ +/* + * 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.ir + +import java.util.Arrays +import java.io.OutputStream +import java.nio.ByteBuffer + +/** A version of a thing + * + * Versions are always optional, [[Version.Unversioned]] being the sentinel. + * + * The remaining versions come in two fundamentally different flavors: + * - Hashes: They are stable in serialized form, [[Serializers]] will write + * them to IR files. The only way to create these versions is via + * [[Hashers]]. + * - Non hashes: Not guaranteed to be stable / collision free across different + * programs. Never written to IR files. + */ +final class Version private (private val v: Array[Byte]) extends AnyVal { + import Version.Type + + /** Checks whether two versions are known to be the same. + * + * Returns false if either of the versions is [[Version.Unversioned]] + */ + def sameVersion(that: Version): Boolean = { + if (!this.isVersioned || !that.isVersioned) false + else Arrays.equals(this.v, that.v) + } + + private[ir] def isHash: Boolean = isVersioned && v(0) == Type.Hash + + private[ir] def writeHash(out: OutputStream): Unit = { + require(isHash) + out.write(v, 1, 20) + } + + @inline + private def isVersioned: Boolean = v != null +} + +object Version { + private object Type { + val Hash: Byte = 0x00 + val Ephemeral: Byte = 0x02 + val Combined: Byte = 0x03 + } + + val Unversioned: Version = new Version(null) + + /** Create a non-hash version from the given bytes. + * + * Guaranteed to differ from: + * - all hash versions. + * - versions returned from [[combine]]. + * - versions with different bytes. + */ + def fromBytes(bytes: Array[Byte]): Version = + make(Type.Ephemeral, bytes) + + /** Create a non-hash version from an Int. + * + * Strictly equivalent to (but potentially more efficient): + * {{{ + * fromBytes(ByteBuffer.allocate(4).putInt(i).array()) + * }}} + */ + def fromInt(i: Int): Version = { + val buf = ByteBuffer.allocate(5) + buf.put(Type.Ephemeral) + buf.putInt(i) + new Version(buf.array()) + } + + /** Create a non-hash version from a Long. + * + * Strictly equivalent to (but potentially more efficient): + * {{{ + * fromBytes(ByteBuffer.allocate(8).putLong(i).array()) + * }}} + */ + def fromLong(l: Long): Version = { + val buf = ByteBuffer.allocate(9) + buf.put(Type.Ephemeral) + buf.putLong(l) + new Version(buf.array()) + } + + /** Create a combined, non-hash version from the given bytes. + * + * Returns [[Unversioned]] if at least one of versions is [[Unversioned]]. + * + * The returned version is to differ from: + * - all hash versions. + * - all non-hash versions created with `from` methods. + * - combined versions created with different (ordered) version lists + * (including the empty list). + * + * @note This can be used to create tagged versions (for alternatives): + * {{{ + * Versions.combine(Versions.fromInt(0), underlying) + * }}} + */ + def combine(versions: Version*): Version = { + if (versions.forall(_.isVersioned)) { + val buf = ByteBuffer.allocate(1 + 4 + versions.map(_.v.length + 4).sum) + + buf.put(Type.Combined) + buf.putInt(versions.length) + + for (version <- versions) { + buf.putInt(version.v.length) + buf.put(version.v) + } + + new Version(buf.array()) + } else { + Unversioned + } + } + + private[ir] def fromHash(hash: Array[Byte]): Version = { + require(hash.length == 20) + make(Type.Hash, hash) + } + + private def make(tpe: Byte, bytes: Array[Byte]): Version = { + val len = bytes.length + val v = new Array[Byte](len + 1) + v(0) = tpe + + System.arraycopy(bytes, 0, v, 1, len) + new Version(v) + } +} diff --git a/ir/shared/src/test/scala/org/scalajs/ir/HashersTest.scala b/ir/shared/src/test/scala/org/scalajs/ir/HashersTest.scala index 3a95a15b55..4115c19c81 100644 --- a/ir/shared/src/test/scala/org/scalajs/ir/HashersTest.scala +++ b/ir/shared/src/test/scala/org/scalajs/ir/HashersTest.scala @@ -14,6 +14,8 @@ package org.scalajs.ir import scala.language.implicitConversions +import java.io.ByteArrayOutputStream + import org.junit.Test import org.junit.Assert._ @@ -27,18 +29,20 @@ import Types._ import TestIRBuilder._ class HashersTest { - private def assertHashEquals(expected: String, actual: Option[TreeHash]): Unit = { - assertTrue(actual.isDefined) - assertEquals(expected, hashAsVersion(actual.get)) - } + private def assertHashEquals(expected: String, actual: Version): Unit = { + assertTrue(actual.isHash) + + val actualBytes = { + val out = new ByteArrayOutputStream + actual.writeHash(out) + out.close() + out.toByteArray() + } - @Test def testHashAsVersion(): Unit = { - val hash: TreeHash = new TreeHash(Array( - 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe, 0xc3, 0x7f, - 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xbb, 0x34 - ).map(_.toByte)) + val expectedBytes = expected.grouped(2) + .map(Integer.parseInt(_, 16).toByte).toArray - assertEquals("1032547698badcfec37f0123456789abcdefbb34", hashAsVersion(hash)) + assertArrayEquals(expectedBytes, actualBytes) } private val bodyWithInterestingStuff = Block( @@ -75,7 +79,7 @@ class HashersTest { @Test def testHashMethodDef(): Unit = { def test(expected: String, methodDef: MethodDef): Unit = { val hashedMethodDef = hashMethodDef(methodDef) - assertHashEquals(expected, hashedMethodDef.hash) + assertHashEquals(expected, hashedMethodDef.version) } val mIIMethodName = MethodName("m", List(I), I) @@ -85,7 +89,7 @@ class HashersTest { MethodDef(MemberFlags.empty, mIIMethodName, NON, List(ParamDef("x", NON, IntType, mutable = false)), IntType, None)( - NoOptHints, None) + NoOptHints, UNV) ) test( @@ -93,14 +97,14 @@ class HashersTest { MethodDef(MemberFlags.empty, mIIMethodName, NON, List(ParamDef("x", NON, IntType, mutable = false)), IntType, Some(bodyWithInterestingStuff))( - NoOptHints, None) + NoOptHints, UNV) ) } @Test def testHashJSMethodDef(): Unit = { def test(expected: String, methodDef: JSMethodDef): Unit = { val hashedMethodDef = hashJSMethodDef(methodDef) - assertHashEquals(expected, hashedMethodDef.hash) + assertHashEquals(expected, hashedMethodDef.version) } test( @@ -108,7 +112,7 @@ class HashersTest { JSMethodDef(MemberFlags.empty, s("m"), List(ParamDef("x", NON, AnyType, mutable = false)), None, bodyWithInterestingStuff)( - NoOptHints, None) + NoOptHints, UNV) ) } diff --git a/ir/shared/src/test/scala/org/scalajs/ir/PrintersTest.scala b/ir/shared/src/test/scala/org/scalajs/ir/PrintersTest.scala index 4684db9a93..b8af4c7fe0 100644 --- a/ir/shared/src/test/scala/org/scalajs/ir/PrintersTest.scala +++ b/ir/shared/src/test/scala/org/scalajs/ir/PrintersTest.scala @@ -199,16 +199,6 @@ class PrintersTest { While(b(true), i(5))) } - @Test def printDoWhile(): Unit = { - assertPrintEquals( - """ - |do { - | 5 - |} while (true) - """, - DoWhile(i(5), b(true))) - } - @Test def printForIn(): Unit = { assertPrintEquals( """ @@ -949,7 +939,7 @@ class PrintersTest { def makeForKind(kind: ClassKind): ClassDef = { ClassDef("Test", NON, kind, None, Some(ObjectClass), Nil, None, None, Nil, - Nil)( + Nil, None, Nil, Nil, Nil)( NoOptHints) } @@ -1021,7 +1011,7 @@ class PrintersTest { def makeForParents(superClass: Option[ClassIdent], interfaces: List[ClassIdent]): ClassDef = { ClassDef("Test", NON, ClassKind.Class, None, superClass, interfaces, None, - None, Nil, Nil)( + None, Nil, Nil, None, Nil, Nil, Nil)( NoOptHints) } @@ -1054,7 +1044,8 @@ class PrintersTest { |} """, ClassDef("Test", NON, ClassKind.NativeJSClass, None, Some(ObjectClass), Nil, - None, Some(JSNativeLoadSpec.Global("Foo", List("Bar"))), Nil, Nil)( + None, Some(JSNativeLoadSpec.Global("Foo", List("Bar"))), Nil, Nil, None, + Nil, Nil, Nil)( NoOptHints)) assertPrintEquals( @@ -1063,7 +1054,8 @@ class PrintersTest { |} """, ClassDef("Test", NON, ClassKind.NativeJSClass, None, Some(ObjectClass), Nil, - None, Some(JSNativeLoadSpec.Import("foo", List("Bar"))), Nil, Nil)( + None, Some(JSNativeLoadSpec.Import("foo", List("Bar"))), Nil, Nil, None, + Nil, Nil, Nil)( NoOptHints)) assertPrintEquals( @@ -1075,7 +1067,8 @@ class PrintersTest { None, Some(JSNativeLoadSpec.ImportWithGlobalFallback( JSNativeLoadSpec.Import("foo", List("Bar")), - JSNativeLoadSpec.Global("Baz", List("Foobar")))), Nil, Nil)( + JSNativeLoadSpec.Global("Baz", List("Foobar")))), Nil, Nil, None, + Nil, Nil, Nil)( NoOptHints)) } @@ -1087,7 +1080,7 @@ class PrintersTest { |} """, ClassDef("Test", NON, ClassKind.JSClass, Some(Nil), Some(ObjectClass), Nil, - None, None, Nil, Nil)( + None, None, Nil, Nil, None, Nil, Nil, Nil)( NoOptHints)) assertPrintEquals( @@ -1101,7 +1094,7 @@ class PrintersTest { ParamDef("x", NON, IntType, mutable = false), ParamDef("y", TestON, StringType, mutable = false) )), - Some(ObjectClass), Nil, None, None, Nil, Nil)( + Some(ObjectClass), Nil, None, None, Nil, Nil, None, Nil, Nil, Nil)( NoOptHints)) } @@ -1114,7 +1107,8 @@ class PrintersTest { """, ClassDef("Test", NON, ClassKind.JSClass, Some(List(ParamDef("sup", NON, AnyType, mutable = false))), - Some("Bar"), Nil, Some(ref("sup", AnyType)), None, Nil, Nil)( + Some("Bar"), Nil, Some(ref("sup", AnyType)), None, Nil, Nil, None, + Nil, Nil, Nil)( NoOptHints)) } @@ -1125,7 +1119,7 @@ class PrintersTest { |} """, ClassDef("Test", NON, ClassKind.Class, None, Some(ObjectClass), Nil, - None, None, Nil, Nil)( + None, None, Nil, Nil, None, Nil, Nil, Nil)( NoOptHints.withInline(true))) } @@ -1136,7 +1130,7 @@ class PrintersTest { |} """, ClassDef("Test", TestON, ClassKind.ModuleClass, None, Some(ObjectClass), - Nil, None, None, Nil, Nil)( + Nil, None, None, Nil, Nil, None, Nil, Nil, Nil)( NoOptHints)) } @@ -1145,17 +1139,27 @@ class PrintersTest { """ |module class Test extends java.lang.Object { | val x: int - | var y: int + | def m;I(): int = + | constructor def constructor(): any = { + | super() + | } + | def "o"(): any = { + | 5 + | } + | static native p;Ljava.lang.Object loadfrom global:foo | export top[moduleID="main"] module "Foo" |} """, ClassDef("Test", NON, ClassKind.ModuleClass, None, Some(ObjectClass), Nil, None, None, - List( - FieldDef(MemberFlags.empty, "x", NON, IntType), - FieldDef(MemberFlags.empty.withMutable(true), "y", NON, IntType)), - List( - TopLevelModuleExportDef("main", "Foo")))( + List(FieldDef(MemberFlags.empty, "x", NON, IntType)), + List(MethodDef(MemberFlags.empty, MethodName("m", Nil, I), NON, Nil, IntType, None)(NoOptHints, UNV)), + Some(JSConstructorDef(MemberFlags.empty.withNamespace(Constructor), Nil, None, + JSConstructorBody(Nil, JSSuperConstructorCall(Nil), Nil))(NoOptHints, UNV)), + List(JSMethodDef(MemberFlags.empty, StringLiteral("o"), Nil, None, i(5))(NoOptHints, UNV)), + List(JSNativeMemberDef(MemberFlags.empty.withNamespace(Static), MethodName("p", Nil, O), + JSNativeLoadSpec.Global("foo", Nil))), + List(TopLevelModuleExportDef("main", "Foo")))( NoOptHints)) } @@ -1190,7 +1194,7 @@ class PrintersTest { """, MethodDef(MemberFlags.empty, mIIMethodName, NON, List(ParamDef("x", NON, IntType, mutable = false)), - IntType, None)(NoOptHints, None)) + IntType, None)(NoOptHints, UNV)) assertPrintEquals( """ @@ -1200,7 +1204,7 @@ class PrintersTest { """, MethodDef(MemberFlags.empty, mIIMethodName, NON, List(ParamDef("x", NON, IntType, mutable = false)), - IntType, Some(i(5)))(NoOptHints, None)) + IntType, Some(i(5)))(NoOptHints, UNV)) assertPrintEquals( """ @@ -1210,7 +1214,7 @@ class PrintersTest { """, MethodDef(MemberFlags.empty, mIIMethodName, NON, List(ParamDef("x", NON, IntType, mutable = false)), - IntType, Some(i(5)))(NoOptHints.withInline(true), None)) + IntType, Some(i(5)))(NoOptHints.withInline(true), UNV)) assertPrintEquals( """ @@ -1220,7 +1224,7 @@ class PrintersTest { """, MethodDef(MemberFlags.empty, mIVMethodName, NON, List(ParamDef("x", NON, IntType, mutable = false)), - NoType, Some(i(5)))(NoOptHints, None)) + NoType, Some(i(5)))(NoOptHints, UNV)) assertPrintEquals( """ @@ -1230,7 +1234,7 @@ class PrintersTest { """, MethodDef(MemberFlags.empty.withNamespace(Static), mIIMethodName, NON, List(ParamDef("x", NON, IntType, mutable = false)), - IntType, Some(i(5)))(NoOptHints, None)) + IntType, Some(i(5)))(NoOptHints, UNV)) assertPrintEquals( """ @@ -1240,7 +1244,7 @@ class PrintersTest { """, MethodDef(MemberFlags.empty.withNamespace(Private), mIIMethodName, NON, List(ParamDef("x", NON, IntType, mutable = false)), - IntType, Some(i(5)))(NoOptHints, None)) + IntType, Some(i(5)))(NoOptHints, UNV)) assertPrintEquals( """ @@ -1250,7 +1254,7 @@ class PrintersTest { """, MethodDef(MemberFlags.empty.withNamespace(PrivateStatic), mIIMethodName, NON, List(ParamDef("x", NON, IntType, mutable = false)), - IntType, Some(i(5)))(NoOptHints, None)) + IntType, Some(i(5)))(NoOptHints, UNV)) assertPrintEquals( """ @@ -1258,7 +1262,7 @@ class PrintersTest { """, MethodDef(MemberFlags.empty, mIIMethodName, TestON, List(ParamDef("x", TestON, IntType, mutable = false)), - IntType, None)(NoOptHints, None)) + IntType, None)(NoOptHints, UNV)) } @Test def printJSConstructorDef(): Unit = { @@ -1273,7 +1277,7 @@ class PrintersTest { JSConstructorDef(MemberFlags.empty.withNamespace(Constructor), List(ParamDef("x", NON, AnyType, mutable = false)), None, JSConstructorBody(List(i(5)), JSSuperConstructorCall(List(i(6))), List(Undefined())))( - NoOptHints, None)) + NoOptHints, UNV)) assertPrintEquals( """ @@ -1286,7 +1290,7 @@ class PrintersTest { List(ParamDef("x", NON, AnyType, mutable = false)), Some(ParamDef("y", NON, AnyType, mutable = false)), JSConstructorBody(Nil, JSSuperConstructorCall(List(i(6))), List(i(7))))( - NoOptHints, None)) + NoOptHints, UNV)) // This example is an invalid constructor, but it should be printed anyway assertPrintEquals( @@ -1299,7 +1303,7 @@ class PrintersTest { JSConstructorDef(MemberFlags.empty, List(ParamDef("x", TestON, AnyType, mutable = false)), None, JSConstructorBody(List(i(5)), JSSuperConstructorCall(List(i(6))), Nil))( - NoOptHints, None)) + NoOptHints, UNV)) } @Test def printJSMethodDef(): Unit = { @@ -1311,7 +1315,7 @@ class PrintersTest { """, JSMethodDef(MemberFlags.empty, StringLiteral("m"), List(ParamDef("x", NON, AnyType, mutable = false)), None, - i(5))(NoOptHints, None)) + i(5))(NoOptHints, UNV)) assertPrintEquals( """ @@ -1322,7 +1326,7 @@ class PrintersTest { JSMethodDef(MemberFlags.empty, StringLiteral("m"), List(ParamDef("x", NON, AnyType, mutable = false)), Some(ParamDef("y", NON, AnyType, mutable = false)), - i(5))(NoOptHints, None)) + i(5))(NoOptHints, UNV)) assertPrintEquals( """ @@ -1332,7 +1336,7 @@ class PrintersTest { """, JSMethodDef(MemberFlags.empty.withNamespace(Static), StringLiteral("m"), List(ParamDef("x", NON, AnyType, mutable = false)), None, - i(5))(NoOptHints, None)) + i(5))(NoOptHints, UNV)) assertPrintEquals( """ @@ -1342,7 +1346,7 @@ class PrintersTest { """, JSMethodDef(MemberFlags.empty, StringLiteral("m"), List(ParamDef("x", TestON, AnyType, mutable = false)), None, - i(5))(NoOptHints, None)) + i(5))(NoOptHints, UNV)) } @Test def printJSPropertyDef(): Unit = { @@ -1360,7 +1364,7 @@ class PrintersTest { | 5 |} """, - JSPropertyDef(flags, StringLiteral("prop"), Some(i(5)), None)) + JSPropertyDef(flags, StringLiteral("prop"), Some(i(5)), None)(UNV)) assertPrintEquals( s""" @@ -1370,7 +1374,7 @@ class PrintersTest { """, JSPropertyDef(flags, StringLiteral("prop"), None, - Some((ParamDef("x", NON, AnyType, mutable = false), i(7))))) + Some((ParamDef("x", NON, AnyType, mutable = false), i(7))))(UNV)) assertPrintEquals( s""" @@ -1380,7 +1384,7 @@ class PrintersTest { """, JSPropertyDef(flags, StringLiteral("prop"), None, - Some((ParamDef("x", TestON, AnyType, mutable = false), i(7))))) + Some((ParamDef("x", TestON, AnyType, mutable = false), i(7))))(UNV)) assertPrintEquals( s""" @@ -1394,7 +1398,7 @@ class PrintersTest { JSPropertyDef(flags, StringLiteral("prop"), Some(i(5)), Some((ParamDef("x", NON, AnyType, mutable = false), - i(7))))) + i(7))))(UNV)) } } @@ -1419,7 +1423,7 @@ class PrintersTest { TopLevelMethodExportDef("main", JSMethodDef( MemberFlags.empty.withNamespace(Static), StringLiteral("foo"), List(ParamDef("x", NON, AnyType, mutable = false)), None, - i(5))(NoOptHints, None))) + i(5))(NoOptHints, UNV))) } @Test def printTopLevelFieldExportDef(): Unit = { diff --git a/ir/shared/src/test/scala/org/scalajs/ir/TestIRBuilder.scala b/ir/shared/src/test/scala/org/scalajs/ir/TestIRBuilder.scala index 7fa9de34c7..83255c1ca2 100644 --- a/ir/shared/src/test/scala/org/scalajs/ir/TestIRBuilder.scala +++ b/ir/shared/src/test/scala/org/scalajs/ir/TestIRBuilder.scala @@ -30,6 +30,9 @@ object TestIRBuilder { /** No original name, for short. */ val NON = NoOriginalName + /** Unversioned, for short */ + val UNV = Version.Unversioned + /** No optimizer hints, for short. */ val NoOptHints = OptimizerHints.empty diff --git a/ir/shared/src/test/scala/org/scalajs/ir/VersionTest.scala b/ir/shared/src/test/scala/org/scalajs/ir/VersionTest.scala new file mode 100644 index 0000000000..a934b5f34d --- /dev/null +++ b/ir/shared/src/test/scala/org/scalajs/ir/VersionTest.scala @@ -0,0 +1,132 @@ +/* + * 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.ir + +import java.io.ByteArrayOutputStream + +import org.junit.Test +import org.junit.Assert._ + +class VersionTest { + import Version._ + + private def testEq(x: Version, y: Version) = { + assertTrue(x.sameVersion(y)) + assertTrue(y.sameVersion(x)) + } + + private def testNe(x: Version, y: Version) = { + assertFalse(x.sameVersion(y)) + assertFalse(y.sameVersion(x)) + } + + @Test + def testUnversioned(): Unit = { + testNe(Unversioned, Unversioned) + testNe(Unversioned, fromInt(1)) + testNe(Unversioned, fromLong(1L)) + testNe(Unversioned, fromBytes(new Array(2))) + testNe(Unversioned, fromHash(new Array(20))) + testNe(Unversioned, combine(fromInt(1), fromInt(2))) + } + + @Test + def testFromHash(): Unit = { + val v = fromHash(Array.fill(20)(0)) + + testEq(v, fromHash(Array.fill(20)(0))) + testNe(v, fromHash(Array.fill(20)(1))) + } + + @Test + def testFromBytes(): Unit = { + val v = fromBytes(Array(1)) + + testEq(v, fromBytes(Array(1))) + testNe(v, fromBytes(Array(2))) + testNe(v, fromBytes(Array(1, 2))) + testNe(v, fromBytes(Array())) + } + + @Test + def testFromInt(): Unit = { + val v = fromInt(2) + + testEq(v, fromInt(2)) + testEq(v, fromBytes(Array(0, 0, 0, 2))) + testNe(v, fromInt(3)) + testNe(v, fromBytes(Array(0))) + } + + @Test + def testFromLong(): Unit = { + val v = fromLong(2L) + + testEq(v, fromLong(2L)) + testEq(v, fromBytes(Array[Byte](0, 0, 0, 0, 0, 0, 0, 2))) + testNe(v, fromLong(3L)) + testNe(v, fromInt(2)) + testNe(v, fromBytes(Array[Byte](0))) + } + + @Test + def testCombine(): Unit = { + val v = combine(fromBytes(Array(1)), fromBytes(Array(2))) + + testEq(v, combine(fromBytes(Array(1)), fromBytes(Array(2)))) + testNe(v, fromBytes(Array(1, 2))) + testNe(v, combine()) + testNe(v, combine(fromBytes(Array(1)))) + + testEq(combine(), combine()) + } + + @Test + def testKinds(): Unit = { + // Hash doesn't equal ephemeral. + testNe(fromHash(Array.fill(20)(1)), fromBytes(Array.fill(20)(1))) + + // Combined doesn't equal hash or ephemeral + val v = combine(fromBytes(Array.fill(11)(0))) + + // Internal representation of combined of the above. + // (length 20, so it could be a hash). + val a = Array[Byte]( + 0, 0, 0, 1, // number of versions + 0, 0, 0, 12, // length of the version + 0x02, // type of the version (ephemeral) + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // payload of the version + ) + + testNe(v, fromHash(a)) + testNe(v, fromBytes(a)) + } + + @Test + def testIsHash(): Unit = { + assertFalse(Unversioned.isHash) + assertFalse(fromBytes(Array()).isHash) + assertFalse(combine().isHash) + assertTrue(fromHash(Array.fill(20)(0)).isHash) + assertFalse(combine(fromHash(Array.fill(20)(0))).isHash) + } + + @Test + def testWriteHash(): Unit = { + val out = new ByteArrayOutputStream + + fromHash(Array.fill(20)(1)).writeHash(out) + + assertArrayEquals(Array.fill[Byte](20)(1), out.toByteArray()) + } +} diff --git a/javalib/src/main/scala/java/io/BufferedReader.scala b/javalib/src/main/scala/java/io/BufferedReader.scala index 4cb0afdd32..4c31435afa 100644 --- a/javalib/src/main/scala/java/io/BufferedReader.scala +++ b/javalib/src/main/scala/java/io/BufferedReader.scala @@ -16,9 +16,7 @@ class BufferedReader(in: Reader, sz: Int) extends Reader { def this(in: Reader) = this(in, 4096) - // Workaround 2.11 with no-specialization ; buf should be initialized on the same line - private[this] var buf: Array[Char] = null - buf = new Array[Char](sz) + private[this] var buf: Array[Char] = new Array[Char](sz) /** Last valid value in the buffer (exclusive) */ private[this] var end = 0 diff --git a/javalib/src/main/scala/java/io/InputStream.scala b/javalib/src/main/scala/java/io/InputStream.scala index 4259dc95a8..c6b706b7a5 100644 --- a/javalib/src/main/scala/java/io/InputStream.scala +++ b/javalib/src/main/scala/java/io/InputStream.scala @@ -12,6 +12,8 @@ package java.io +import java.util.Arrays + abstract class InputStream extends Closeable { def read(): Int @@ -44,6 +46,67 @@ abstract class InputStream extends Closeable { } } + def readAllBytes(): Array[Byte] = + readNBytes(Integer.MAX_VALUE) + + def readNBytes(len: Int): Array[Byte] = { + if (len < 0) { + throw new IllegalArgumentException + } else if (len == 0) { + new Array[Byte](0) + } else { + var bytesRead = 0 + + /* Allocate a buffer. + * + * Note that the implementation is required to grow memory proportional to + * the amount read, not the amount requested. Therefore, we cannot simply + * allocate an array of length len. + */ + var buf = new Array[Byte](Math.min(len, 1024)) + + var lastRead = 0 + + while (bytesRead < len && lastRead != -1) { + if (buf.length == bytesRead) { + /* Note that buf.length < Integer.MAX_VALUE, because: + * - bytesRead < len (loop condition) + * - len <= Integer.MAX_VALUE (because of its type) + */ + val newLen = + if (Integer.MAX_VALUE / 2 > buf.length) Integer.MAX_VALUE + else buf.length * 2 + buf = Arrays.copyOf(buf, Math.min(len, newLen)) + } + + lastRead = read(buf, bytesRead, buf.length - bytesRead) + if (lastRead > 0) + bytesRead += lastRead + } + + if (buf.length > bytesRead) + Arrays.copyOf(buf, bytesRead) + else + buf + } + } + + def readNBytes(b: Array[Byte], off: Int, len: Int): Int = { + if (off < 0 || len < 0 || len > b.length - off) + throw new IndexOutOfBoundsException + + var bytesRead = 0 + var lastRead = 0 + while (bytesRead < len && lastRead != -1) { + lastRead = read(b, off + bytesRead, len - bytesRead) + if (lastRead > 0) { + bytesRead += lastRead + } + } + + bytesRead + } + def skip(n: Long): Long = { var skipped = 0 while (skipped < n && read() != -1) @@ -51,6 +114,22 @@ abstract class InputStream extends Closeable { skipped } + def skipNBytes(n: Long): Unit = { + var remaining = n + while (remaining > 0) { + val skipped = skip(remaining) + if (skipped < 0 || skipped > remaining) { + throw new IOException + } else if (skipped == 0) { + if (read() == -1) + throw new EOFException + remaining -= 1 + } else { + remaining -= skipped + } + } + } + def available(): Int = 0 def close(): Unit = () @@ -62,4 +141,66 @@ abstract class InputStream extends Closeable { def markSupported(): Boolean = false + def transferTo(out: OutputStream): Long = { + out.getClass() // Trigger NPE (if enabled). + + var transferred = 0L + val buf = new Array[Byte](4096) + var bytesRead = 0 + + while (bytesRead != -1) { + bytesRead = read(buf) + if (bytesRead != -1) { + out.write(buf, 0, bytesRead) + transferred += bytesRead + } + } + + transferred + } +} + +object InputStream { + def nullInputStream(): InputStream = new InputStream { + private[this] var closed = false + + @inline + private def ensureOpen(): Unit = { + if (closed) + throw new IOException + } + + override def available(): Int = { + ensureOpen() + 0 + } + + def read(): Int = { + ensureOpen() + -1 + } + + override def readNBytes(n: Int): Array[Byte] = { + ensureOpen() + super.readNBytes(n) + } + + override def readNBytes(b: Array[Byte], off: Int, len: Int): Int = { + ensureOpen() + super.readNBytes(b, off, len) + } + + override def skip(n: Long): Long = { + ensureOpen() + 0L + } + + override def skipNBytes(n: Long): Unit = { + ensureOpen() + super.skipNBytes(n) + } + + override def close(): Unit = + closed = true + } } diff --git a/javalib/src/main/scala/java/io/OutputStream.scala b/javalib/src/main/scala/java/io/OutputStream.scala index af6c6b370c..a88173ebfd 100644 --- a/javalib/src/main/scala/java/io/OutputStream.scala +++ b/javalib/src/main/scala/java/io/OutputStream.scala @@ -35,3 +35,32 @@ abstract class OutputStream extends Object with Closeable with Flushable { def close(): Unit = () } + +object OutputStream { + def nullOutputStream(): OutputStream = new OutputStream { + private[this] var closed = false + + private def ensureOpen(): Unit = { + if (closed) + throw new IOException + } + + def write(b: Int): Unit = ensureOpen() + + override def write(b: Array[Byte]): Unit = { + ensureOpen() + + b.length // Null check + } + + override def write(b: Array[Byte], off: Int, len: Int): Unit = { + ensureOpen() + + if (off < 0 || len < 0 || len > b.length - off) + throw new IndexOutOfBoundsException() + } + + override def close(): Unit = + closed = true + } +} diff --git a/javalib/src/main/scala/java/lang/Iterable.scala b/javalib/src/main/scala/java/lang/Iterable.scala index 478284343b..78416d2a99 100644 --- a/javalib/src/main/scala/java/lang/Iterable.scala +++ b/javalib/src/main/scala/java/lang/Iterable.scala @@ -15,12 +15,9 @@ package java.lang import java.util.Iterator import java.util.function.Consumer -import scala.scalajs.js.annotation.JavaDefaultMethod - trait Iterable[T] { def iterator(): Iterator[T] - @JavaDefaultMethod def forEach(action: Consumer[_ >: T]): Unit = { val iter = iterator() while (iter.hasNext()) diff --git a/javalib/src/main/scala/java/lang/reflect/Array.scala b/javalib/src/main/scala/java/lang/reflect/Array.scala index 688d97b5d1..b2f94b8906 100644 --- a/javalib/src/main/scala/java/lang/reflect/Array.scala +++ b/javalib/src/main/scala/java/lang/reflect/Array.scala @@ -42,7 +42,7 @@ object Array { case array: Array[Long] => array.length case array: Array[Float] => array.length case array: Array[Double] => array.length - case _ => throw new IllegalArgumentException("argument type mismatch") + case _ => mismatch(array) } def get(array: AnyRef, index: Int): AnyRef = array match { @@ -55,28 +55,28 @@ object Array { case array: Array[Long] => java.lang.Long.valueOf(array(index)) case array: Array[Float] => java.lang.Float.valueOf(array(index)) case array: Array[Double] => java.lang.Double.valueOf(array(index)) - case _ => throw new IllegalArgumentException("argument type mismatch") + case _ => mismatch(array) } def getBoolean(array: AnyRef, index: Int): Boolean = array match { case array: Array[Boolean] => array(index) - case _ => throw new IllegalArgumentException("argument type mismatch") + case _ => mismatch(array) } def getChar(array: AnyRef, index: Int): Char = array match { - case array: Array[Char] => array(index) - case _ => throw new IllegalArgumentException("argument type mismatch") + case array: Array[Char] => array(index) + case _ => mismatch(array) } def getByte(array: AnyRef, index: Int): Byte = array match { case array: Array[Byte] => array(index) - case _ => throw new IllegalArgumentException("argument type mismatch") + case _ => mismatch(array) } def getShort(array: AnyRef, index: Int): Short = array match { case array: Array[Short] => array(index) case array: Array[Byte] => array(index) - case _ => throw new IllegalArgumentException("argument type mismatch") + case _ => mismatch(array) } def getInt(array: AnyRef, index: Int): Int = array match { @@ -84,7 +84,7 @@ object Array { case array: Array[Char] => array(index) case array: Array[Byte] => array(index) case array: Array[Short] => array(index) - case _ => throw new IllegalArgumentException("argument type mismatch") + case _ => mismatch(array) } def getLong(array: AnyRef, index: Int): Long = array match { @@ -93,7 +93,7 @@ object Array { case array: Array[Byte] => array(index) case array: Array[Short] => array(index) case array: Array[Int] => array(index) - case _ => throw new IllegalArgumentException("argument type mismatch") + case _ => mismatch(array) } def getFloat(array: AnyRef, index: Int): Float = array match { @@ -103,7 +103,7 @@ object Array { case array: Array[Short] => array(index) case array: Array[Int] => array(index).toFloat case array: Array[Long] => array(index).toFloat - case _ => throw new IllegalArgumentException("argument type mismatch") + case _ => mismatch(array) } def getDouble(array: AnyRef, index: Int): Double = array match { @@ -114,7 +114,7 @@ object Array { case array: Array[Int] => array(index) case array: Array[Long] => array(index).toDouble case array: Array[Float] => array(index) - case _ => throw new IllegalArgumentException("argument type mismatch") + case _ => mismatch(array) } def set(array: AnyRef, index: Int, value: AnyRef): Unit = array match { @@ -129,13 +129,13 @@ object Array { case value: Long => setLong(array, index, value) case value: Float => setFloat(array, index, value) case value: Double => setDouble(array, index, value) - case _ => throw new IllegalArgumentException("argument type mismatch") + case _ => mismatch(array) } } def setBoolean(array: AnyRef, index: Int, value: Boolean): Unit = array match { case array: Array[Boolean] => array(index) = value - case _ => throw new IllegalArgumentException("argument type mismatch") + case _ => mismatch(array) } def setChar(array: AnyRef, index: Int, value: Char): Unit = array match { @@ -144,7 +144,7 @@ object Array { case array: Array[Long] => array(index) = value case array: Array[Float] => array(index) = value case array: Array[Double] => array(index) = value - case _ => throw new IllegalArgumentException("argument type mismatch") + case _ => mismatch(array) } def setByte(array: AnyRef, index: Int, value: Byte): Unit = array match { @@ -154,7 +154,7 @@ object Array { case array: Array[Long] => array(index) = value case array: Array[Float] => array(index) = value case array: Array[Double] => array(index) = value - case _ => throw new IllegalArgumentException("argument type mismatch") + case _ => mismatch(array) } def setShort(array: AnyRef, index: Int, value: Short): Unit = array match { @@ -163,7 +163,7 @@ object Array { case array: Array[Long] => array(index) = value case array: Array[Float] => array(index) = value case array: Array[Double] => array(index) = value - case _ => throw new IllegalArgumentException("argument type mismatch") + case _ => mismatch(array) } def setInt(array: AnyRef, index: Int, value: Int): Unit = array match { @@ -171,24 +171,29 @@ object Array { case array: Array[Long] => array(index) = value case array: Array[Float] => array(index) = value.toFloat case array: Array[Double] => array(index) = value - case _ => throw new IllegalArgumentException("argument type mismatch") + case _ => mismatch(array) } def setLong(array: AnyRef, index: Int, value: Long): Unit = array match { case array: Array[Long] => array(index) = value case array: Array[Float] => array(index) = value.toFloat case array: Array[Double] => array(index) = value.toDouble - case _ => throw new IllegalArgumentException("argument type mismatch") + case _ => mismatch(array) } def setFloat(array: AnyRef, index: Int, value: Float): Unit = array match { case array: Array[Float] => array(index) = value case array: Array[Double] => array(index) = value - case _ => throw new IllegalArgumentException("argument type mismatch") + case _ => mismatch(array) } def setDouble(array: AnyRef, index: Int, value: Double): Unit = array match { case array: Array[Double] => array(index) = value - case _ => throw new IllegalArgumentException("argument type mismatch") + case _ => mismatch(array) + } + + private def mismatch(array: AnyRef): Nothing = { + array.getClass() // null check + throw new IllegalArgumentException("argument type mismatch") } } diff --git a/javalib/src/main/scala/java/math/BigInteger.scala b/javalib/src/main/scala/java/math/BigInteger.scala index 59ce0d1c49..9a9e328742 100644 --- a/javalib/src/main/scala/java/math/BigInteger.scala +++ b/javalib/src/main/scala/java/math/BigInteger.scala @@ -501,6 +501,13 @@ class BigInteger extends Number with Comparable[BigInteger] { override def intValue(): Int = sign * digits(0) + def intValueExact(): Int = { + if (numberLength <= 1 && bitLength() < Integer.SIZE) + intValue() + else + throw new ArithmeticException("BigInteger out of int range") + } + def isProbablePrime(certainty: Int): Boolean = Primality.isProbablePrime(abs(), certainty) @@ -511,6 +518,13 @@ class BigInteger extends Number with Comparable[BigInteger] { sign * value } + def longValueExact(): Long = { + if (numberLength <= 2 && bitLength() < java.lang.Long.SIZE) + longValue() + else + throw new ArithmeticException("BigInteger out of long range") + } + def max(bi: BigInteger): BigInteger = { if (this.compareTo(bi) == GREATER) this else bi diff --git a/javalib/src/main/scala/java/math/Division.scala b/javalib/src/main/scala/java/math/Division.scala index a69382135b..f895fc5fe1 100644 --- a/javalib/src/main/scala/java/math/Division.scala +++ b/javalib/src/main/scala/java/math/Division.scala @@ -884,14 +884,11 @@ private[math] object Division { for (i <- 0 until modulusLen) { var innnerCarry: Int = 0 // unsigned val m = Multiplication.unsignedMultAddAdd(res(i), n2, 0, 0).toInt - // Work around Scala 2.11 limitation with the IR cleaner ; should be for (j <- 0 until modulusLen) - var j = 0 - while (j < modulusLen) { + for (j <- 0 until modulusLen) { val nextInnnerCarry = unsignedMultAddAdd(m, modulusDigits(j), res(i + j), innnerCarry) res(i + j) = nextInnnerCarry.toInt innnerCarry = (nextInnnerCarry >> 32).toInt - j += 1 } val nextOuterCarry = (outerCarry & UINT_MAX) + (res(i + modulusLen) & UINT_MAX) + (innnerCarry & UINT_MAX) diff --git a/javalib/src/main/scala/java/math/Multiplication.scala b/javalib/src/main/scala/java/math/Multiplication.scala index 10ecb738cc..859d9f926f 100644 --- a/javalib/src/main/scala/java/math/Multiplication.scala +++ b/javalib/src/main/scala/java/math/Multiplication.scala @@ -124,13 +124,10 @@ private[math] object Multiplication { for (i <- 0 until aLen) { carry = 0 - // Work around Scala 2.11 limitation with the IR cleaner ; should be for (j <- i + 1 until aLen) - var j = i + 1 - while (j < aLen) { + for (j <- i + 1 until aLen) { val t = unsignedMultAddAdd(a(i), a(j), res(i + j), carry) res(i + j) = t.toInt carry = (t >>> 32).toInt - j += 1 } res(i + aLen) = carry } @@ -442,13 +439,10 @@ private[math] object Multiplication { for (i <- 0 until aLen) { var carry = 0 val aI = a(i) - // Work around Scala 2.11 limitation with the IR cleaner ; should be for (j <- 0 until bLen) - var j = 0 - while (j < bLen) { + for (j <- 0 until bLen) { val added = unsignedMultAddAdd(aI, b(j), t(i + j), carry) t(i + j) = added.toInt carry = (added >>> 32).toInt - j += 1 } t(i + bLen) = carry } diff --git a/javalib/src/main/scala/java/nio/charset/Charset.scala b/javalib/src/main/scala/java/nio/charset/Charset.scala index 2c5ac9e42c..752600eb8c 100644 --- a/javalib/src/main/scala/java/nio/charset/Charset.scala +++ b/javalib/src/main/scala/java/nio/charset/Charset.scala @@ -88,9 +88,20 @@ object Charset { def isSupported(charsetName: String): Boolean = dictContains(CharsetMap, charsetName.toLowerCase()) + def availableCharsets(): java.util.SortedMap[String, Charset] = + availableCharsetsResult + + private lazy val availableCharsetsResult = { + val m = new java.util.TreeMap[String, Charset](String.CASE_INSENSITIVE_ORDER) + forArrayElems(allSJSCharsets) { c => + m.put(c.name(), c) + } + Collections.unmodifiableSortedMap(m) + } + private lazy val CharsetMap = { val m = dictEmpty[Charset]() - forArrayElems(js.Array(US_ASCII, ISO_8859_1, UTF_8, UTF_16BE, UTF_16LE, UTF_16)) { c => + forArrayElems(allSJSCharsets) { c => dictSet(m, c.name().toLowerCase(), c) val aliases = c._aliases for (i <- 0 until aliases.length) @@ -98,4 +109,7 @@ object Charset { } m } + + private def allSJSCharsets = + js.Array(US_ASCII, ISO_8859_1, UTF_8, UTF_16BE, UTF_16LE, UTF_16) } diff --git a/javalib/src/main/scala/java/util/BitSet.scala b/javalib/src/main/scala/java/util/BitSet.scala index 171ed1a629..5e2c4bd61f 100644 --- a/javalib/src/main/scala/java/util/BitSet.scala +++ b/javalib/src/main/scala/java/util/BitSet.scala @@ -637,20 +637,16 @@ class BitSet private (private var bits: Array[Int]) extends Serializable with Cl var result: String = "{" var comma: Boolean = false - // Work around Scala 2.11 limitation with the IR cleaner ; should be double-for over i and j for { i <- 0 until getActualArrayLength() + j <- 0 until ElementSize } { - var j = 0 - while (j < ElementSize) { - if ((bits(i) & (1 << j)) != 0) { - if (comma) - result += ", " - else - comma = true - result += (i << AddressBitsPerWord) + j - } - j += 1 + if ((bits(i) & (1 << j)) != 0) { + if (comma) + result += ", " + else + comma = true + result += (i << AddressBitsPerWord) + j } } diff --git a/javalib/src/main/scala/java/util/Collection.scala b/javalib/src/main/scala/java/util/Collection.scala index 34af7828ea..d2c1956313 100644 --- a/javalib/src/main/scala/java/util/Collection.scala +++ b/javalib/src/main/scala/java/util/Collection.scala @@ -14,8 +14,6 @@ package java.util import java.util.function.Predicate -import scala.scalajs.js.annotation.JavaDefaultMethod - trait Collection[E] extends java.lang.Iterable[E] { def size(): Int def isEmpty(): Boolean @@ -29,7 +27,6 @@ trait Collection[E] extends java.lang.Iterable[E] { def addAll(c: Collection[_ <: E]): Boolean def removeAll(c: Collection[_]): Boolean - @JavaDefaultMethod def removeIf(filter: Predicate[_ >: E]): Boolean = { var result = false val iter = iterator() diff --git a/javalib/src/main/scala/java/util/Collections.scala b/javalib/src/main/scala/java/util/Collections.scala index e27b526a86..687ba74b5e 100644 --- a/javalib/src/main/scala/java/util/Collections.scala +++ b/javalib/src/main/scala/java/util/Collections.scala @@ -260,14 +260,14 @@ object Collections { // Differs from original type definition, original: [T <: jl.Comparable[_ >: T]], returning T def min[T <: AnyRef with jl.Comparable[T]](coll: Collection[_ <: T]): AnyRef = - min(coll, naturalComparator[T]) + min(coll, Comparator.naturalOrder[T]) def min[T](coll: Collection[_ <: T], comp: Comparator[_ >: T]): T = coll.scalaOps.reduceLeft((a, b) => if (comp.compare(a, b) <= 0) a else b) // Differs from original type definition, original: [T <: jl.Comparable[_ >: T]], returning T def max[T <: AnyRef with jl.Comparable[T]](coll: Collection[_ <: T]): AnyRef = - max(coll, naturalComparator[T]) + max(coll, Comparator.naturalOrder[T]) def max[T](coll: Collection[_ <: T], comp: Comparator[_ >: T]): T = coll.scalaOps.reduceLeft((a, b) => if (comp.compare(a, b) >= 0) a else b) @@ -604,13 +604,6 @@ object Collections { @inline private def modulo(a: Int, b: Int): Int = ((a % b) + b) % b - @inline - private def naturalComparator[T <: jl.Comparable[T]]: Comparator[T] = { - new Comparator[T] with Serializable { - final def compare(o1: T, o2: T): Int = o1.compareTo(o2) - } - } - private trait WrappedEquals { protected def inner: AnyRef diff --git a/javalib/src/main/scala/java/util/Comparator.scala b/javalib/src/main/scala/java/util/Comparator.scala index 6edd9b50a3..7cbf1ec521 100644 --- a/javalib/src/main/scala/java/util/Comparator.scala +++ b/javalib/src/main/scala/java/util/Comparator.scala @@ -12,15 +12,161 @@ package java.util -import scala.scalajs.js.annotation.JavaDefaultMethod +import java.util.function._ // scalastyle:off equals.hash.code -trait Comparator[A] { +/* A note about serializability: + * + * The JDK documentation states that returned comparators are serializable if + * their respective elements (Comparators / Functions) are serializable. + * + * Experimentation on `nullsFirst` has shown that the returned comparator always + * implements `Serializable` (and supposedly relies on the serialization + * mechanism itself to fail when it is unable to serialize a field). + * + * Our implementation mimics this behavior. + */ + +trait Comparator[A] { self => + import Comparator._ + def compare(o1: A, o2: A): Int def equals(obj: Any): Boolean - @JavaDefaultMethod def reversed(): Comparator[A] = Collections.reverseOrder(this) + + @inline + def thenComparing(other: Comparator[_ >: A]): Comparator[A] = { + other.getClass() // null check + new Comparator[A] with Serializable { + def compare(o1: A, o2: A) = { + val cmp = self.compare(o1, o2) + if (cmp != 0) cmp + else other.compare(o1, o2) + } + } + } + + def thenComparing[U](keyExtractor: Function[_ >: A, _ <: U], + keyComparator: Comparator[_ >: U]): Comparator[A] = { + thenComparing(comparing[A, U](keyExtractor, keyComparator)) + } + + /* Should be U <: Comparable[_ >: U] but scalac fails with + * > illegal cyclic reference involving type U + */ + def thenComparing[U <: Comparable[U]]( + keyExtractor: Function[_ >: A, _ <: U]): Comparator[A] = { + thenComparing(comparing[A, U](keyExtractor)) + } + + def thenComparingInt(keyExtractor: ToIntFunction[_ >: A]): Comparator[A] = + thenComparing(comparingInt(keyExtractor)) + + def thenComparingLong(keyExtractor: ToLongFunction[_ >: A]): Comparator[A] = + thenComparing(comparingLong(keyExtractor)) + + def thenComparingDouble(keyExtractor: ToDoubleFunction[_ >: A]): Comparator[A] = + thenComparing(comparingDouble(keyExtractor)) + +} + +object Comparator { + + /* Should be T <: Comparable[_ >: T] but scalac fails with + * > illegal cyclic reference involving type U + */ + def reverseOrder[T <: Comparable[T]](): Comparator[T] = + naturalOrder[T]().reversed() + + /* Should be T <: Comparable[_ >: T] but scalac fails with + * > illegal cyclic reference involving type U + */ + @inline + def naturalOrder[T <: Comparable[T]](): Comparator[T] = + ReusableNaturalComparator.asInstanceOf[Comparator[T]] + + /* Not the same object as NaturalComparator. + * + * Otherwise we'll get null back from TreeSet#comparator() (see #4796). + */ + private object ReusableNaturalComparator extends Comparator[Any] { + def compare(o1: Any, o2: Any): Int = + o1.asInstanceOf[Comparable[Any]].compareTo(o2) + } + + @inline + def nullsFirst[T](comparator: Comparator[_ >: T]): Comparator[T] = new Comparator[T] with Serializable { + def compare(o1: T, o2: T): Int = { + if (o1 == null && o2 == null) 0 + else if (o1 == null) -1 + else if (o2 == null) 1 + else if (comparator == null) 0 + else comparator.compare(o1, o2) + } + } + + @inline + def nullsLast[T](comparator: Comparator[_ >: T]): Comparator[T] = new Comparator[T] with Serializable { + def compare(o1: T, o2: T): Int = { + if (o1 == null && o2 == null) 0 + else if (o1 == null) 1 + else if (o2 == null) -1 + else if (comparator == null) 0 + else comparator.compare(o1, o2) + } + } + + @inline + def comparing[T, U](keyExtractor: Function[_ >: T, _ <: U], + keyComparator: Comparator[_ >: U]): Comparator[T] = { + keyExtractor.getClass() // null check + keyComparator.getClass() // null check + new Comparator[T] with Serializable { + def compare(o1: T, o2: T): Int = + keyComparator.compare(keyExtractor(o1), keyExtractor(o2)) + } + } + + /* Should be U <: Comparable[_ >: U] but scalac fails with + * > illegal cyclic reference involving type U + */ + @inline + def comparing[T, U <: Comparable[U]]( + keyExtractor: Function[_ >: T, _ <: U]): Comparator[T] = { + keyExtractor.getClass() // null check + new Comparator[T] with Serializable { + def compare(o1: T, o2: T): Int = + keyExtractor(o1).compareTo(keyExtractor(o2)) + } + } + + @inline + def comparingInt[T](keyExtractor: ToIntFunction[_ >: T]): Comparator[T] = { + keyExtractor.getClass() // null check + new Comparator[T] with Serializable { + def compare(o1: T, o2: T): Int = + Integer.compare(keyExtractor.applyAsInt(o1), keyExtractor.applyAsInt(o2)) + } + } + + @inline + def comparingLong[T](keyExtractor: ToLongFunction[_ >: T]): Comparator[T] = { + keyExtractor.getClass() // null check + new Comparator[T] with Serializable { + def compare(o1: T, o2: T): Int = + java.lang.Long.compare(keyExtractor.applyAsLong(o1), keyExtractor.applyAsLong(o2)) + } + } + + @inline + def comparingDouble[T](keyExtractor: ToDoubleFunction[_ >: T]): Comparator[T] = { + keyExtractor.getClass() // null check + new Comparator[T] with Serializable { + def compare(o1: T, o2: T): Int = + java.lang.Double.compare(keyExtractor.applyAsDouble(o1), keyExtractor.applyAsDouble(o2)) + } + } } diff --git a/javalib/src/main/scala/java/util/Formatter.scala b/javalib/src/main/scala/java/util/Formatter.scala index b70237c115..750bebf4c4 100644 --- a/javalib/src/main/scala/java/util/Formatter.scala +++ b/javalib/src/main/scala/java/util/Formatter.scala @@ -83,12 +83,8 @@ final class Formatter private (private[this] var dest: Appendable, @noinline private def sendToDestSlowPath(ss: js.Array[String]): Unit = { - // Workaround Scala 2.11 limitation: cannot nest anonymous functions for the IR cleaner - @inline def body(): Unit = - forArrayElems(ss)(dest.append(_)) - trapIOExceptions { - body() + forArrayElems(ss)(dest.append(_)) } } diff --git a/javalib/src/main/scala/java/util/Iterator.scala b/javalib/src/main/scala/java/util/Iterator.scala index f6f2943a44..de610cc7a5 100644 --- a/javalib/src/main/scala/java/util/Iterator.scala +++ b/javalib/src/main/scala/java/util/Iterator.scala @@ -12,19 +12,15 @@ package java.util -import scala.scalajs.js.annotation.JavaDefaultMethod - import java.util.function.Consumer trait Iterator[E] { def hasNext(): Boolean def next(): E - @JavaDefaultMethod def remove(): Unit = throw new UnsupportedOperationException("remove") - @JavaDefaultMethod def forEachRemaining(action: Consumer[_ >: E]): Unit = { while (hasNext()) action.accept(next()) diff --git a/javalib/src/main/scala/java/util/List.scala b/javalib/src/main/scala/java/util/List.scala index 33d219303a..66465dde50 100644 --- a/javalib/src/main/scala/java/util/List.scala +++ b/javalib/src/main/scala/java/util/List.scala @@ -14,17 +14,13 @@ package java.util import java.util.function.UnaryOperator -import scala.scalajs.js.annotation.JavaDefaultMethod - trait List[E] extends Collection[E] { - @JavaDefaultMethod def replaceAll(operator: UnaryOperator[E]): Unit = { val iter = listIterator() while (iter.hasNext()) iter.set(operator.apply(iter.next())) } - @JavaDefaultMethod def sort(c: Comparator[_ >: E]): Unit = { val arrayBuf = toArray() Arrays.sort[AnyRef with E](arrayBuf.asInstanceOf[Array[AnyRef with E]], c) diff --git a/javalib/src/main/scala/java/util/Map.scala b/javalib/src/main/scala/java/util/Map.scala index 260bd05a92..c2250b2143 100644 --- a/javalib/src/main/scala/java/util/Map.scala +++ b/javalib/src/main/scala/java/util/Map.scala @@ -14,8 +14,6 @@ package java.util import java.util.function.{BiConsumer, BiFunction, Function} -import scala.scalajs.js.annotation.JavaDefaultMethod - import ScalaOps._ trait Map[K, V] { @@ -34,24 +32,20 @@ trait Map[K, V] { def equals(o: Any): Boolean def hashCode(): Int - @JavaDefaultMethod def getOrDefault(key: Any, defaultValue: V): V = if (containsKey(key)) get(key) else defaultValue - @JavaDefaultMethod def forEach(action: BiConsumer[_ >: K, _ >: V]): Unit = { for (entry <- entrySet().scalaOps) action.accept(entry.getKey(), entry.getValue()) } - @JavaDefaultMethod def replaceAll(function: BiFunction[_ >: K, _ >: V, _ <: V]): Unit = { for (entry <- entrySet().scalaOps) entry.setValue(function.apply(entry.getKey(), entry.getValue())) } - @JavaDefaultMethod def putIfAbsent(key: K, value: V): V = { val prevValue = get(key) if (prevValue == null) @@ -60,7 +54,6 @@ trait Map[K, V] { prevValue } - @JavaDefaultMethod def remove(key: Any, value: Any): Boolean = { if (containsKey(key) && Objects.equals(get(key), value)) { remove(key) @@ -70,7 +63,6 @@ trait Map[K, V] { } } - @JavaDefaultMethod def replace(key: K, oldValue: V, newValue: V): Boolean = { if (containsKey(key) && Objects.equals(get(key), oldValue)) { put(key, newValue) @@ -80,12 +72,10 @@ trait Map[K, V] { } } - @JavaDefaultMethod def replace(key: K, value: V): V = if (containsKey(key)) put(key, value) else null.asInstanceOf[V] - @JavaDefaultMethod def computeIfAbsent(key: K, mappingFunction: Function[_ >: K, _ <: V]): V = { val oldValue = get(key) if (oldValue != null) { @@ -98,7 +88,6 @@ trait Map[K, V] { } } - @JavaDefaultMethod def computeIfPresent(key: K, remappingFunction: BiFunction[_ >: K, _ >: V, _ <: V]): V = { val oldValue = get(key) if (oldValue == null) { @@ -110,7 +99,6 @@ trait Map[K, V] { } } - @JavaDefaultMethod def compute(key: K, remappingFunction: BiFunction[_ >: K, _ >: V, _ <: V]): V = { val oldValue = get(key) val newValue = remappingFunction.apply(key, oldValue) @@ -131,7 +119,6 @@ trait Map[K, V] { newValue } - @JavaDefaultMethod def merge(key: K, value: V, remappingFunction: BiFunction[_ >: V, _ >: V, _ <: V]): V = { Objects.requireNonNull(value) diff --git a/javalib/src/main/scala/java/util/NaturalComparator.scala b/javalib/src/main/scala/java/util/NaturalComparator.scala index b0b72d3dc1..3775df2e75 100644 --- a/javalib/src/main/scala/java/util/NaturalComparator.scala +++ b/javalib/src/main/scala/java/util/NaturalComparator.scala @@ -24,7 +24,7 @@ package java.util * Scala.js is configured with compliant `asInstanceOf`s. The behavior is * otherwise undefined. */ -private[util] object NaturalComparator extends Comparator[Any] { +private[util] object NaturalComparator extends Comparator[Any] with Serializable { def compare(o1: Any, o2: Any): Int = o1.asInstanceOf[Comparable[Any]].compareTo(o2) diff --git a/javalib/src/main/scala/java/util/RedBlackTree.scala b/javalib/src/main/scala/java/util/RedBlackTree.scala index 336df6d2c4..3e91b22e66 100644 --- a/javalib/src/main/scala/java/util/RedBlackTree.scala +++ b/javalib/src/main/scala/java/util/RedBlackTree.scala @@ -16,9 +16,7 @@ import scala.annotation.tailrec import scala.scalajs.js -/** The red-black tree implementation used by `TreeSet`s. - * - * It could also be used by `TreeMap`s in the future. +/** The red-black tree implementation used by `TreeSet`s and `TreeMap`s. * * This implementation was copied and adapted from * `scala.collection.mutable.RedBlackTree` as found in Scala 2.13.0. @@ -223,7 +221,12 @@ private[util] object RedBlackTree { def get[A, B](tree: Tree[A, B], key: Any)( implicit comp: Comparator[_ >: A]): B = { - nullableNodeFlatMap(getNode(tree.root, key))(_.value) + nullableNodeFlatMap(getNode(tree, key))(_.value) + } + + def getNode[A, B](tree: Tree[A, B], key: Any)( + implicit comp: Comparator[_ >: A]): Node[A, B] = { + getNode(tree.root, key) } @tailrec @@ -471,10 +474,10 @@ private[util] object RedBlackTree { // ---- deletion ---- def delete[A, B](tree: Tree[A, B], key: Any)( - implicit comp: Comparator[_ >: A]): B = { + implicit comp: Comparator[_ >: A]): Node[A, B] = { nullableNodeFlatMap(getNode(tree.root, key)) { node => deleteNode(tree, node) - node.value + node } } @@ -612,10 +615,16 @@ private[util] object RedBlackTree { /** Returns `null.asInstanceOf[A]` if `node eq null`, otherwise `node.key`. */ @inline - private def nullableNodeKey[A, B](node: Node[A, B]): A = + def nullableNodeKey[A, B](node: Node[A, B]): A = if (node eq null) null.asInstanceOf[A] else node.key + /** Returns `null.asInstanceOf[B]` if `node eq null`, otherwise `node.value`. */ + @inline + def nullableNodeValue[A, B](node: Node[A, B]): B = + if (node eq null) null.asInstanceOf[B] + else node.value + /** Returns the node that follows `node` in an in-order tree traversal. * * If `node` has the maximum key (and is, therefore, the last node), this diff --git a/javalib/src/main/scala/java/util/TreeMap.scala b/javalib/src/main/scala/java/util/TreeMap.scala new file mode 100644 index 0000000000..ca0789f9c4 --- /dev/null +++ b/javalib/src/main/scala/java/util/TreeMap.scala @@ -0,0 +1,606 @@ +/* + * 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 java.util + +import java.lang.Cloneable +import java.util.{RedBlackTree => RB} +import java.util.function.{Function, BiFunction} + +class TreeMap[K, V] private (tree: RB.Tree[K, V])( + implicit comp: Comparator[_ >: K]) + extends AbstractMap[K, V] with NavigableMap[K, V] with Cloneable with Serializable { + + def this() = this(RB.Tree.empty[K, V])(NaturalComparator) + + def this(comparator: Comparator[_ >: K]) = + this(RB.Tree.empty[K, V])(NaturalComparator.select(comparator)) + + def this(m: Map[K, V]) = { + this() + putAll(m) + } + + def this(m: SortedMap[K, V]) = { + this(RB.fromOrderedEntries(m.entrySet().iterator(), m.size()))( + NaturalComparator.select(m.comparator())) + } + + override def size(): Int = RB.size(tree) + + override def containsKey(key: Any): Boolean = RB.contains(tree, key) + + override def containsValue(value: Any): Boolean = { + // scalastyle:off return + val iter = RB.valuesIterator(tree) + while (iter.hasNext()) { + if (Objects.equals(value, iter.next())) + return true + } + false + // scalastyle:on return + } + + override def get(key: Any): V = RB.get(tree, key) + + def comparator(): Comparator[_ >: K] = + NaturalComparator.unselect(comp) + + def firstKey(): K = { + if (isEmpty()) + throw new NoSuchElementException() + RB.minKey(tree) + } + + def lastKey(): K = { + if (isEmpty()) + throw new NoSuchElementException() + RB.maxKey(tree) + } + + override def putAll(map: Map[_ <: K, _ <: V]): Unit = + map.forEach((k, v) => put(k, v)) + + override def put(key: K, value: V): V = + RB.insert(tree, key, value) + + override def computeIfAbsent(key: K, mappingFunction: Function[_ >: K, _ <: V]): V = { + val node = RB.getNode(tree, key) + + if (node eq null) { + val newValue = mappingFunction(key) + if (newValue != null) + put(key, newValue) + newValue + } else if (node.getValue() == null) { + val newValue = mappingFunction(key) + if (newValue != null) + updateNodeValue(node, newValue) + newValue + } else { + node.getValue() + } + } + + override def computeIfPresent(key: K, remappingFunction: BiFunction[_ >: K, _ >: V, _ <: V]): V = { + val node = RB.getNode(tree, key) + if ((node ne null) && node.getValue() != null) + updateNodeValue(node, remappingFunction(key, node.getValue())) + else + null.asInstanceOf[V] + } + + override def compute(key: K, remappingFunction: BiFunction[_ >: K, _ >: V, _ <: V]): V = { + val node = RB.getNode(tree, key) + if (node eq null) { + val newValue = remappingFunction(key, null.asInstanceOf[V]) + if (newValue != null) + put(key, newValue) + newValue + } else { + updateNodeValue(node, remappingFunction(key, node.getValue())) + } + } + + override def merge(key: K, value: V, remappingFunction: BiFunction[_ >: V, _ >: V, _ <: V]): V = { + value.getClass() // null check + + val node = RB.getNode(tree, key) + if (node eq null) { + put(key, value) + value + } else { + val oldValue = node.getValue() + val newValue = + if (oldValue == null) value + else remappingFunction(oldValue, value) + + updateNodeValue(node, newValue) + } + } + + /** Common code for functions above. + * + * - Sets value to newValue if it is non-null + * - deletes the node if newValue is null. + * + * @returns newValue + */ + private def updateNodeValue(node: RB.Node[K, V], newValue: V): V = { + if (newValue == null) + RB.deleteNode(tree, node) + else + node.setValue(newValue) + newValue + } + + override def remove(key: Any): V = + RB.nullableNodeValue(RB.delete(tree, key)) + + override def clear(): Unit = RB.clear(tree) + + override def clone(): Object = new TreeMap(tree.treeCopy())(comp) + + def firstEntry(): Map.Entry[K, V] = RB.minNode(tree) + + def lastEntry(): Map.Entry[K, V] = RB.maxNode(tree) + + def pollFirstEntry(): Map.Entry[K, V] = { + val node = RB.minNode(tree) + if (node ne null) + RB.deleteNode(tree, node) + node + } + + def pollLastEntry(): Map.Entry[K, V] = { + val node = RB.maxNode(tree) + if (node ne null) + RB.deleteNode(tree, node) + node + } + + def lowerEntry(key: K): Map.Entry[K, V] = + RB.maxNodeBefore(tree, key, RB.ExclusiveBound) + + def lowerKey(key: K): K = + RB.maxKeyBefore(tree, key, RB.ExclusiveBound) + + def floorEntry(key: K): Map.Entry[K, V] = + RB.maxNodeBefore(tree, key, RB.InclusiveBound) + + def floorKey(key: K): K = + RB.maxKeyBefore(tree, key, RB.InclusiveBound) + + def ceilingEntry(key: K): Map.Entry[K, V] = + RB.minNodeAfter(tree, key, RB.InclusiveBound) + + def ceilingKey(key: K): K = + RB.minKeyAfter(tree, key, RB.InclusiveBound) + + def higherEntry(key: K): Map.Entry[K, V] = + RB.minNodeAfter(tree, key, RB.ExclusiveBound) + + def higherKey(key: K): K = + RB.minKeyAfter(tree, key, RB.ExclusiveBound) + + override def keySet(): Set[K] = navigableKeySet() + + def navigableKeySet(): NavigableSet[K] = { + new TreeSet.Projection(tree, null.asInstanceOf[K], RB.NoBound, + null.asInstanceOf[K], RB.NoBound, null.asInstanceOf[V]) + } + + def descendingKeySet(): NavigableSet[K] = { + new TreeSet.DescendingProjection(tree, null.asInstanceOf[K], RB.NoBound, + null.asInstanceOf[K], RB.NoBound, null.asInstanceOf[V]) + } + + override def values(): Collection[V] = new AbstractCollection[V] { + def iterator(): Iterator[V] = RB.valuesIterator(tree) + + def size(): Int = RB.size(tree) + + override def contains(o: Any): Boolean = containsValue(o) + + override def clear(): Unit = RB.clear(tree) + } + + def entrySet(): Set[Map.Entry[K, V]] = { + new TreeMap.ProjectedEntrySet(tree, null.asInstanceOf[K], RB.NoBound, + null.asInstanceOf[K], RB.NoBound) + } + + def descendingMap(): NavigableMap[K, V] = { + new TreeMap.DescendingProjection(tree, null.asInstanceOf[K], RB.NoBound, + null.asInstanceOf[K], RB.NoBound) + } + + def subMap(fromKey: K, fromInclusive: Boolean, toKey: K, toInclusive: Boolean): NavigableMap[K, V] = { + new TreeMap.Projection(tree, + fromKey, RB.boundKindFromIsInclusive(fromInclusive), + toKey, RB.boundKindFromIsInclusive(toInclusive)) + } + + def headMap(toKey: K, inclusive: Boolean): NavigableMap[K, V] = { + new TreeMap.Projection(tree, + null.asInstanceOf[K], RB.NoBound, + toKey, RB.boundKindFromIsInclusive(inclusive)) + } + + def tailMap(fromKey: K, inclusive: Boolean): NavigableMap[K, V] = { + new TreeMap.Projection(tree, + fromKey, RB.boundKindFromIsInclusive(inclusive), + null.asInstanceOf[K], RB.NoBound) + } + + def subMap(fromKey: K, toKey: K): SortedMap[K, V] = + subMap(fromKey, true, toKey, false) + + def headMap(toKey: K): SortedMap[K, V] = + headMap(toKey, false) + + def tailMap(fromKey: K): SortedMap[K, V] = + tailMap(fromKey, true) +} + +private object TreeMap { + private class ProjectedEntrySet[K, V](tree: RB.Tree[K, V], + lowerBound: K, lowerKind: RB.BoundKind, upperBound: K, upperKind: RB.BoundKind)( + implicit protected val comp: Comparator[_ >: K]) + extends AbstractSet[Map.Entry[K, V]] { + + def iterator(): Iterator[Map.Entry[K, V]] = + RB.projectionIterator(tree, lowerBound, lowerKind, upperBound, upperKind) + + def size(): Int = + RB.projectionSize(tree, lowerBound, lowerKind, upperBound, upperKind) + + override def contains(o: Any): Boolean = o match { + case o: Map.Entry[_, _] if isWithinBounds(o.getKey()) => + val node = RB.getNode(tree, o.getKey()) + (node ne null) && Objects.equals(node.getValue(), o.getValue()) + case _ => + false + } + + override def remove(o: Any): Boolean = o match { + case o: Map.Entry[_, _] if isWithinBounds(o.getKey()) => + val node = RB.getNode(tree, o.getKey()) + if ((node ne null) && Objects.equals(node.getValue(), o.getValue())) { + RB.deleteNode(tree, node) + true + } else { + false + } + case _ => + false + } + + private def isWithinBounds(key: Any): Boolean = + RB.isWithinLowerBound(key, lowerBound, lowerKind) && RB.isWithinUpperBound(key, upperBound, upperKind) + } + + private abstract class AbstractProjection[K, V]( + protected val tree: RB.Tree[K, V], + protected val lowerBound: K, protected val lowerKind: RB.BoundKind, + protected val upperBound: K, protected val upperKind: RB.BoundKind + )( + implicit protected val comp: Comparator[_ >: K]) + extends AbstractMap[K, V] with NavigableMap[K, V] { + + // To be implemented by the two concrete subclasses, depending on the order + + protected def nextNode(key: K, boundKind: RB.BoundKind): RB.Node[K, V] + protected def previousNode(key: K, boundKind: RB.BoundKind): RB.Node[K, V] + + protected def subMapGeneric(newFromKey: K = null.asInstanceOf[K], + newFromBoundKind: RB.BoundKind = RB.NoBound, + newToKey: K = null.asInstanceOf[K], + newToBoundKind: RB.BoundKind = RB.NoBound): NavigableMap[K, V] + + // Implementation of most of the NavigableMap API + + override def size(): Int = + RB.projectionSize(tree, lowerBound, lowerKind, upperBound, upperKind) + + override def isEmpty(): Boolean = + RB.projectionIsEmpty(tree, lowerBound, lowerKind, upperBound, upperKind) + + override def containsKey(key: Any): Boolean = + isWithinBounds(key) && RB.contains(tree, key) + + override def get(key: Any): V = { + if (!isWithinBounds(key)) + null.asInstanceOf[V] + else + RB.get(tree, key) + } + + override def put(key: K, value: V): V = { + if (!isWithinBounds(key)) + throw new IllegalArgumentException + RB.insert(tree, key, value) + } + + override def remove(key: Any): V = { + val oldNode = + if (isWithinBounds(key)) RB.delete(tree, key) + else null + RB.nullableNodeValue(oldNode) + } + + def entrySet(): Set[Map.Entry[K, V]] = + new ProjectedEntrySet(tree, lowerBound, lowerKind, upperBound, upperKind) + + def lowerEntry(key: K): Map.Entry[K, V] = + previousNode(key, RB.ExclusiveBound) + + def lowerKey(key: K): K = + RB.nullableNodeKey(previousNode(key, RB.ExclusiveBound)) + + def floorEntry(key: K): Map.Entry[K, V] = + previousNode(key, RB.InclusiveBound) + + def floorKey(key: K): K = + RB.nullableNodeKey(previousNode(key, RB.InclusiveBound)) + + def ceilingEntry(key: K): Map.Entry[K, V] = + nextNode(key, RB.InclusiveBound) + + def ceilingKey(key: K): K = + RB.nullableNodeKey(nextNode(key, RB.InclusiveBound)) + + def higherEntry(key: K): Map.Entry[K, V] = + nextNode(key, RB.ExclusiveBound) + + def higherKey(key: K): K = + RB.nullableNodeKey(nextNode(key, RB.ExclusiveBound)) + + def firstKey(): K = { + val e = firstEntry() + if (e eq null) + throw new NoSuchElementException + e.getKey() + } + + def lastKey(): K = { + val e = lastEntry() + if (e eq null) + throw new NoSuchElementException + e.getKey() + } + + def subMap(fromKey: K, fromInclusive: Boolean, toKey: K, + toInclusive: Boolean): NavigableMap[K, V] = { + subMapGeneric( + fromKey, RB.boundKindFromIsInclusive(fromInclusive), + toKey, RB.boundKindFromIsInclusive(toInclusive)) + } + + def headMap(toKey: K, inclusive: Boolean): NavigableMap[K, V] = { + subMapGeneric(newToKey = toKey, + newToBoundKind = RB.boundKindFromIsInclusive(inclusive)) + } + + def tailMap(fromKey: K, inclusive: Boolean): NavigableMap[K, V] = { + subMapGeneric(newFromKey = fromKey, + newFromBoundKind = RB.boundKindFromIsInclusive(inclusive)) + } + + def subMap(fromKey: K, toKey: K): SortedMap[K, V] = + subMap(fromKey, true, toKey, false) + + def headMap(toKey: K): SortedMap[K, V] = + headMap(toKey, false) + + def tailMap(fromKey: K): SortedMap[K, V] = + tailMap(fromKey, true) + + // Common implementation of pollFirstEntry() and pollLastEntry() + + @inline + protected final def pollLowerEntry(): Map.Entry[K, V] = { + val node = RB.minNodeAfter(tree, lowerBound, lowerKind) + if (node ne null) { + if (isWithinUpperBound(node.key)) { + RB.deleteNode(tree, node) + node + } else { + null + } + } else { + null + } + } + + @inline + protected final def pollUpperEntry(): Map.Entry[K, V] = { + val node = RB.maxNodeBefore(tree, upperBound, upperKind) + if (node ne null) { + if (isWithinLowerBound(node.key)) { + RB.deleteNode(tree, node) + node + } else { + null + } + } else { + null + } + } + + // Helpers + + protected final def isWithinBounds(key: Any): Boolean = + isWithinLowerBound(key) && isWithinUpperBound(key) + + protected final def isWithinLowerBound(key: Any): Boolean = + RB.isWithinLowerBound(key, lowerBound, lowerKind) + + protected final def isWithinUpperBound(key: Any): Boolean = + RB.isWithinUpperBound(key, upperBound, upperKind) + + protected final def ifWithinLowerBound(node: RB.Node[K, V]): RB.Node[K, V] = + if (node != null && isWithinLowerBound(node.key)) node + else null + + protected final def ifWithinUpperBound(node: RB.Node[K, V]): RB.Node[K, V] = + if (node != null && isWithinUpperBound(node.key)) node + else null + } + + private final class Projection[K, V]( + tree0: RB.Tree[K, V], fromKey0: K, fromBoundKind0: RB.BoundKind, + toKey0: K, toBoundKind0: RB.BoundKind)( + implicit comp: Comparator[_ >: K]) + extends AbstractProjection[K, V](tree0, fromKey0, fromBoundKind0, + toKey0, toBoundKind0) { + + // Access fields under a different name, more appropriate for some uses + + @inline private def fromKey: K = lowerBound + @inline private def fromBoundKind: RB.BoundKind = lowerKind + @inline private def toKey: K = upperBound + @inline private def toBoundKind: RB.BoundKind = upperKind + + /* Implementation of the abstract methods from AbstractProjection + * Some are marked `@inline` for the likely case where + * `DescendingProjection` is not reachable at all and hence + * dead-code-eliminated. + */ + + @inline + protected def nextNode(key: K, boundKind: RB.BoundKind): RB.Node[K, V] = + ifWithinUpperBound(RB.minNodeAfter(tree, key, boundKind)) + + @inline + protected def previousNode(key: K, boundKind: RB.BoundKind): RB.Node[K, V] = + ifWithinLowerBound(RB.maxNodeBefore(tree, key, boundKind)) + + protected def subMapGeneric( + newFromKey: K, newFromBoundKind: RB.BoundKind, + newToKey: K, newToBoundKind: RB.BoundKind): NavigableMap[K, V] = { + val intersectedFromBound = RB.intersectLowerBounds( + new RB.Bound(fromKey, fromBoundKind), + new RB.Bound(newFromKey, newFromBoundKind)) + val intersectedToBound = RB.intersectUpperBounds( + new RB.Bound(toKey, toBoundKind), + new RB.Bound(newToKey, newToBoundKind)) + new Projection(tree, + intersectedFromBound.bound, intersectedFromBound.kind, + intersectedToBound.bound, intersectedToBound.kind) + } + + // Methods of the NavigableMap API that are not implemented in AbstractProjection + + def comparator(): Comparator[_ >: K] = + NaturalComparator.unselect(comp) + + def firstEntry(): Map.Entry[K, V] = + nextNode(fromKey, fromBoundKind) + + def lastEntry(): Map.Entry[K, V] = + previousNode(toKey, toBoundKind) + + @noinline + def pollFirstEntry(): Map.Entry[K, V] = + pollLowerEntry() + + @noinline + def pollLastEntry(): Map.Entry[K, V] = + pollUpperEntry() + + def navigableKeySet(): NavigableSet[K] = { + new TreeSet.Projection(tree, fromKey, fromBoundKind, + toKey, toBoundKind, null.asInstanceOf[V]) + } + + def descendingKeySet(): NavigableSet[K] = { + new TreeSet.DescendingProjection(tree, toKey, toBoundKind, + fromKey, fromBoundKind, null.asInstanceOf[V]) + } + + def descendingMap(): NavigableMap[K, V] = { + new DescendingProjection(tree, toKey, toBoundKind, + fromKey, fromBoundKind) + } + } + + private final class DescendingProjection[K, V]( + tree0: RB.Tree[K, V], fromKey0: K, fromBoundKind0: RB.BoundKind, + toKey0: K, toBoundKind0: RB.BoundKind)( + implicit comp: Comparator[_ >: K]) + extends AbstractProjection[K, V](tree0, toKey0, toBoundKind0, + fromKey0, fromBoundKind0) { + + // Access fields under a different name, more appropriate for some uses + + @inline private def fromKey: K = upperBound + @inline private def fromBoundKind: RB.BoundKind = upperKind + @inline private def toKey: K = lowerBound + @inline private def toBoundKind: RB.BoundKind = lowerKind + + // Implementation of the abstract methods from AbstractProjection + + protected def nextNode(key: K, boundKind: RB.BoundKind): RB.Node[K, V] = + ifWithinLowerBound(RB.maxNodeBefore(tree, key, boundKind)) + + protected def previousNode(key: K, boundKind: RB.BoundKind): RB.Node[K, V] = + ifWithinUpperBound(RB.minNodeAfter(tree, key, boundKind)) + + protected def subMapGeneric( + newFromKey: K, newFromBoundKind: RB.BoundKind, + newToKey: K, newToBoundKind: RB.BoundKind): NavigableMap[K, V] = { + val intersectedFromBound = RB.intersectUpperBounds( + new RB.Bound(fromKey, fromBoundKind), + new RB.Bound(newFromKey, newFromBoundKind)) + val intersectedToBound = RB.intersectLowerBounds( + new RB.Bound(toKey, toBoundKind), + new RB.Bound(newToKey, newToBoundKind)) + new Projection(tree, + intersectedFromBound.bound, intersectedFromBound.kind, + intersectedToBound.bound, intersectedToBound.kind) + } + + // Methods of the NavigableMap API that are not implemented in AbstractProjection + + def comparator(): Comparator[_ >: K] = + Collections.reverseOrder(NaturalComparator.unselect(comp)) + + def firstEntry(): Map.Entry[K, V] = + nextNode(fromKey, fromBoundKind) + + def lastEntry(): Map.Entry[K, V] = + previousNode(toKey, toBoundKind) + + @noinline + def pollFirstEntry(): Map.Entry[K, V] = + pollUpperEntry() + + @noinline + def pollLastEntry(): Map.Entry[K, V] = + pollLowerEntry() + + def navigableKeySet(): NavigableSet[K] = { + new TreeSet.DescendingProjection(tree, fromKey, fromBoundKind, + toKey, toBoundKind, null.asInstanceOf[V]) + } + + def descendingKeySet(): NavigableSet[K] = { + new TreeSet.Projection(tree, toKey, toBoundKind, + fromKey, fromBoundKind, null.asInstanceOf[V]) + } + + def descendingMap(): NavigableMap[K, V] = { + new Projection(tree, toKey, toBoundKind, fromKey, fromBoundKind) + } + } +} diff --git a/javalib/src/main/scala/java/util/TreeSet.scala b/javalib/src/main/scala/java/util/TreeSet.scala index 5a5924dbf1..eec1f08aad 100644 --- a/javalib/src/main/scala/java/util/TreeSet.scala +++ b/javalib/src/main/scala/java/util/TreeSet.scala @@ -50,7 +50,7 @@ class TreeSet[E] private (tree: RB.Tree[E, Any])( def descendingSet(): NavigableSet[E] = { new DescendingProjection(tree, null.asInstanceOf[E], RB.NoBound, - null.asInstanceOf[E], RB.NoBound) + null.asInstanceOf[E], RB.NoBound, ()) } def size(): Int = @@ -75,19 +75,19 @@ class TreeSet[E] private (tree: RB.Tree[E, Any])( toInclusive: Boolean): NavigableSet[E] = { new Projection(tree, fromElement, RB.boundKindFromIsInclusive(fromInclusive), - toElement, RB.boundKindFromIsInclusive(toInclusive)) + toElement, RB.boundKindFromIsInclusive(toInclusive), ()) } def headSet(toElement: E, inclusive: Boolean): NavigableSet[E] = { new Projection(tree, null.asInstanceOf[E], RB.NoBound, - toElement, RB.boundKindFromIsInclusive(inclusive)) + toElement, RB.boundKindFromIsInclusive(inclusive), ()) } def tailSet(fromElement: E, inclusive: Boolean): NavigableSet[E] = { new Projection(tree, fromElement, RB.boundKindFromIsInclusive(inclusive), - null.asInstanceOf[E], RB.NoBound) + null.asInstanceOf[E], RB.NoBound, ()) } def subSet(fromElement: E, toElement: E): SortedSet[E] = @@ -150,11 +150,13 @@ class TreeSet[E] private (tree: RB.Tree[E, Any])( new TreeSet(tree.treeCopy())(comp) } -private object TreeSet { - private abstract class AbstractProjection[E]( - protected val tree: RB.Tree[E, Any], +private[util] object TreeSet { + private[util] abstract class AbstractProjection[E, V]( + protected val tree: RB.Tree[E, V], protected val lowerBound: E, protected val lowerKind: RB.BoundKind, - protected val upperBound: E, protected val upperKind: RB.BoundKind)( + protected val upperBound: E, protected val upperKind: RB.BoundKind, + private val valueForAdd: V + )( implicit protected val comp: Comparator[_ >: E]) extends AbstractSet[E] with NavigableSet[E] { @@ -180,9 +182,11 @@ private object TreeSet { isWithinBounds(o) && RB.contains(tree, o) override def add(e: E): Boolean = { + if (valueForAdd == null) + throw new UnsupportedOperationException if (!isWithinBounds(e)) throw new IllegalArgumentException - RB.insert(tree, e, ()) == null + RB.insert(tree, e, valueForAdd) == null } override def remove(o: Any): Boolean = @@ -280,12 +284,12 @@ private object TreeSet { else null.asInstanceOf[E] } - private final class Projection[E]( - tree0: RB.Tree[E, Any], fromElement0: E, fromBoundKind0: RB.BoundKind, - toElement0: E, toBoundKind0: RB.BoundKind)( + private[util] final class Projection[E, V]( + tree0: RB.Tree[E, V], fromElement0: E, fromBoundKind0: RB.BoundKind, + toElement0: E, toBoundKind0: RB.BoundKind, valueForAdd: V)( implicit comp: Comparator[_ >: E]) - extends AbstractProjection[E](tree0, fromElement0, fromBoundKind0, - toElement0, toBoundKind0) { + extends AbstractProjection[E, V](tree0, fromElement0, fromBoundKind0, + toElement0, toBoundKind0, valueForAdd) { // Access fields under a different name, more appropriate for some uses @@ -319,7 +323,7 @@ private object TreeSet { new RB.Bound(newToElement, newToBoundKind)) new Projection(tree, intersectedFromBound.bound, intersectedFromBound.kind, - intersectedToBound.bound, intersectedToBound.kind) + intersectedToBound.bound, intersectedToBound.kind, valueForAdd) } // Methods of the NavigableSet API that are not implemented in AbstractProjection @@ -353,18 +357,18 @@ private object TreeSet { pollUpper() def descendingSet(): NavigableSet[E] = - new DescendingProjection(tree, toElement, toBoundKind, fromElement, fromBoundKind) + new DescendingProjection(tree, toElement, toBoundKind, fromElement, fromBoundKind, valueForAdd) def descendingIterator(): Iterator[E] = RB.descendingKeysIterator(tree, toElement, toBoundKind, fromElement, fromBoundKind) } - private final class DescendingProjection[E]( - tree0: RB.Tree[E, Any], fromElement0: E, fromBoundKind0: RB.BoundKind, - toElement0: E, toBoundKind0: RB.BoundKind)( + private[util] final class DescendingProjection[E, V]( + tree0: RB.Tree[E, V], fromElement0: E, fromBoundKind0: RB.BoundKind, + toElement0: E, toBoundKind0: RB.BoundKind, valueForAdd: V)( implicit comp: Comparator[_ >: E]) - extends AbstractProjection[E](tree0, toElement0, toBoundKind0, - fromElement0, fromBoundKind0) { + extends AbstractProjection[E, V](tree0, toElement0, toBoundKind0, + fromElement0, fromBoundKind0, valueForAdd) { // Access fields under a different name, more appropriate for some uses @@ -392,7 +396,7 @@ private object TreeSet { new RB.Bound(newToElement, newToBoundKind)) new Projection(tree, intersectedFromBound.bound, intersectedFromBound.kind, - intersectedToBound.bound, intersectedToBound.kind) + intersectedToBound.bound, intersectedToBound.kind, valueForAdd) } // Methods of the NavigableSet API that are not implemented in AbstractProjection @@ -426,7 +430,7 @@ private object TreeSet { pollLower() def descendingSet(): NavigableSet[E] = - new Projection(tree, toElement, toBoundKind, fromElement, fromBoundKind) + new Projection(tree, toElement, toBoundKind, fromElement, fromBoundKind, valueForAdd) def descendingIterator(): Iterator[E] = RB.projectionKeysIterator(tree, toElement, toBoundKind, fromElement, fromBoundKind) diff --git a/javalib/src/main/scala/java/util/concurrent/Flow.scala b/javalib/src/main/scala/java/util/concurrent/Flow.scala new file mode 100644 index 0000000000..77bca32d80 --- /dev/null +++ b/javalib/src/main/scala/java/util/concurrent/Flow.scala @@ -0,0 +1,38 @@ +/* + * 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 java.util.concurrent + +object Flow { + + @inline def defaultBufferSize(): Int = 256 + + trait Processor[T, R] extends Subscriber[T] with Publisher[R] + + @FunctionalInterface + trait Publisher[T] { + def subscribe(subscriber: Subscriber[_ >: T]): Unit + } + + trait Subscriber[T] { + def onSubscribe(subscription: Subscription): Unit + def onNext(item: T): Unit + def onError(throwable: Throwable): Unit + def onComplete(): Unit + } + + trait Subscription { + def request(n: Long): Unit + def cancel(): Unit + } + +} diff --git a/javalib/src/main/scala/java/util/function/BiConsumer.scala b/javalib/src/main/scala/java/util/function/BiConsumer.scala index d39a9a3222..ce30ef7046 100644 --- a/javalib/src/main/scala/java/util/function/BiConsumer.scala +++ b/javalib/src/main/scala/java/util/function/BiConsumer.scala @@ -12,12 +12,9 @@ package java.util.function -import scala.scalajs.js.annotation.JavaDefaultMethod - trait BiConsumer[T, U] { def accept(t: T, u: U): Unit - @JavaDefaultMethod def andThen(after: BiConsumer[T, U]): BiConsumer[T, U] = { (t: T, u: U) => accept(t, u) after.accept(t, u) diff --git a/javalib/src/main/scala/java/util/function/BiFunction.scala b/javalib/src/main/scala/java/util/function/BiFunction.scala index c6f1f75a8a..95dcda75bf 100644 --- a/javalib/src/main/scala/java/util/function/BiFunction.scala +++ b/javalib/src/main/scala/java/util/function/BiFunction.scala @@ -12,12 +12,9 @@ package java.util.function -import scala.scalajs.js.annotation.JavaDefaultMethod - trait BiFunction[T, U, R] { def apply(t: T, u: U): R - @JavaDefaultMethod def andThen[V](after: Function[_ >: R, _ <: V]): BiFunction[T, U, V] = { (t: T, u: U) => after.apply(this.apply(t, u)) } diff --git a/javalib/src/main/scala/java/util/function/BiPredicate.scala b/javalib/src/main/scala/java/util/function/BiPredicate.scala index 24e9610343..2e34d6617d 100644 --- a/javalib/src/main/scala/java/util/function/BiPredicate.scala +++ b/javalib/src/main/scala/java/util/function/BiPredicate.scala @@ -12,20 +12,15 @@ package java.util.function -import scala.scalajs.js.annotation.JavaDefaultMethod - trait BiPredicate[T, U] { def test(t: T, u: U): Boolean - @JavaDefaultMethod def and(other: BiPredicate[_ >: T, _ >: U]): BiPredicate[T, U] = { (t: T, u: U) => test(t, u) && other.test(t, u) } - @JavaDefaultMethod def negate(): BiPredicate[T, U] = (t: T, u: U) => !test(t, u) - @JavaDefaultMethod def or(other: BiPredicate[_ >: T, _ >: U]): BiPredicate[T, U] = { (t: T, u: U) => test(t, u) || other.test(t, u) } diff --git a/javalib/src/main/scala/java/util/function/Consumer.scala b/javalib/src/main/scala/java/util/function/Consumer.scala index e978992b91..2df930c639 100644 --- a/javalib/src/main/scala/java/util/function/Consumer.scala +++ b/javalib/src/main/scala/java/util/function/Consumer.scala @@ -12,13 +12,10 @@ package java.util.function -import scala.scalajs.js.annotation.JavaDefaultMethod - @FunctionalInterface trait Consumer[T] { self => def accept(t: T): Unit - @JavaDefaultMethod def andThen(after: Consumer[_ >: T]): Consumer[T] = { new Consumer[T] { def accept(t: T): Unit = { diff --git a/javalib/src/main/scala/java/util/function/DoubleConsumer.scala b/javalib/src/main/scala/java/util/function/DoubleConsumer.scala index 32efd4d086..8184c13119 100644 --- a/javalib/src/main/scala/java/util/function/DoubleConsumer.scala +++ b/javalib/src/main/scala/java/util/function/DoubleConsumer.scala @@ -12,13 +12,10 @@ package java.util.function -import scala.scalajs.js.annotation.JavaDefaultMethod - @FunctionalInterface trait DoubleConsumer { def accept(value: Double): Unit - @JavaDefaultMethod def andThen(after: DoubleConsumer): DoubleConsumer = { (value: Double) => this.accept(value) after.accept(value) diff --git a/javalib/src/main/scala/java/util/function/DoublePredicate.scala b/javalib/src/main/scala/java/util/function/DoublePredicate.scala index b327f1ce2d..fb4c986cf9 100755 --- a/javalib/src/main/scala/java/util/function/DoublePredicate.scala +++ b/javalib/src/main/scala/java/util/function/DoublePredicate.scala @@ -12,13 +12,10 @@ package java.util.function -import scala.scalajs.js.annotation.JavaDefaultMethod - @FunctionalInterface trait DoublePredicate { self => def test(t: Double): Boolean - @JavaDefaultMethod def and(other: DoublePredicate): DoublePredicate = { new DoublePredicate { def test(value: Double): Boolean = @@ -26,7 +23,6 @@ trait DoublePredicate { self => } } - @JavaDefaultMethod def negate(): DoublePredicate = { new DoublePredicate { def test(value: Double): Boolean = @@ -34,7 +30,6 @@ trait DoublePredicate { self => } } - @JavaDefaultMethod def or(other: DoublePredicate): DoublePredicate = { new DoublePredicate { def test(value: Double): Boolean = diff --git a/javalib/src/main/scala/java/util/function/DoubleUnaryOperator.scala b/javalib/src/main/scala/java/util/function/DoubleUnaryOperator.scala index 53efb491f4..038c40a1e3 100644 --- a/javalib/src/main/scala/java/util/function/DoubleUnaryOperator.scala +++ b/javalib/src/main/scala/java/util/function/DoubleUnaryOperator.scala @@ -12,18 +12,14 @@ package java.util.function -import scala.scalajs.js.annotation.JavaDefaultMethod - @FunctionalInterface trait DoubleUnaryOperator { def applyAsDouble(operand: Double): Double - @JavaDefaultMethod def andThen(after: DoubleUnaryOperator): DoubleUnaryOperator = { (d: Double) => after.applyAsDouble(applyAsDouble(d)) } - @JavaDefaultMethod def compose(before: DoubleUnaryOperator): DoubleUnaryOperator = { (d: Double) => applyAsDouble(before.applyAsDouble(d)) } diff --git a/javalib/src/main/scala/java/util/function/Function.scala b/javalib/src/main/scala/java/util/function/Function.scala index f01c853527..6058a971dc 100644 --- a/javalib/src/main/scala/java/util/function/Function.scala +++ b/javalib/src/main/scala/java/util/function/Function.scala @@ -12,17 +12,13 @@ package java.util.function -import scala.scalajs.js.annotation.JavaDefaultMethod - trait Function[T, R] { def apply(t: T): R - @JavaDefaultMethod def andThen[V](after: Function[_ >: R, _ <: V]): Function[T, V] = { (t: T) => after.apply(apply(t)) } - @JavaDefaultMethod def compose[V](before: Function[_ >: V, _ <: T]): Function[V, R] = { (v: V) => apply(before.apply(v)) } diff --git a/javalib/src/main/scala/java/util/function/IntConsumer.scala b/javalib/src/main/scala/java/util/function/IntConsumer.scala index 5e54e7a101..023c191f6b 100644 --- a/javalib/src/main/scala/java/util/function/IntConsumer.scala +++ b/javalib/src/main/scala/java/util/function/IntConsumer.scala @@ -12,13 +12,10 @@ package java.util.function -import scala.scalajs.js.annotation.JavaDefaultMethod - @FunctionalInterface trait IntConsumer { def accept(value: Int): Unit - @JavaDefaultMethod def andThen(after: IntConsumer): IntConsumer = { (value: Int) => this.accept(value) after.accept(value) diff --git a/javalib/src/main/scala/java/util/function/IntPredicate.scala b/javalib/src/main/scala/java/util/function/IntPredicate.scala index c6cfc6c7fc..ed29e78459 100755 --- a/javalib/src/main/scala/java/util/function/IntPredicate.scala +++ b/javalib/src/main/scala/java/util/function/IntPredicate.scala @@ -12,13 +12,10 @@ package java.util.function -import scala.scalajs.js.annotation.JavaDefaultMethod - @FunctionalInterface trait IntPredicate { self => def test(t: Int): Boolean - @JavaDefaultMethod def and(other: IntPredicate): IntPredicate = { new IntPredicate { def test(value: Int): Boolean = @@ -26,7 +23,6 @@ trait IntPredicate { self => } } - @JavaDefaultMethod def negate(): IntPredicate = { new IntPredicate { def test(value: Int): Boolean = @@ -34,7 +30,6 @@ trait IntPredicate { self => } } - @JavaDefaultMethod def or(other: IntPredicate): IntPredicate = { new IntPredicate { def test(value: Int): Boolean = diff --git a/javalib/src/main/scala/java/util/function/IntUnaryOperator.scala b/javalib/src/main/scala/java/util/function/IntUnaryOperator.scala index 826ab9fc37..89297429c7 100644 --- a/javalib/src/main/scala/java/util/function/IntUnaryOperator.scala +++ b/javalib/src/main/scala/java/util/function/IntUnaryOperator.scala @@ -12,18 +12,14 @@ package java.util.function -import scala.scalajs.js.annotation.JavaDefaultMethod - @FunctionalInterface trait IntUnaryOperator { def applyAsInt(operand: Int): Int - @JavaDefaultMethod def andThen(after: IntUnaryOperator): IntUnaryOperator = { (i: Int) => after.applyAsInt(applyAsInt(i)) } - @JavaDefaultMethod def compose(before: IntUnaryOperator): IntUnaryOperator = { (i: Int) => applyAsInt(before.applyAsInt(i)) } diff --git a/javalib/src/main/scala/java/util/function/LongConsumer.scala b/javalib/src/main/scala/java/util/function/LongConsumer.scala index 4603d80376..a8a904246b 100644 --- a/javalib/src/main/scala/java/util/function/LongConsumer.scala +++ b/javalib/src/main/scala/java/util/function/LongConsumer.scala @@ -12,13 +12,10 @@ package java.util.function -import scala.scalajs.js.annotation.JavaDefaultMethod - @FunctionalInterface trait LongConsumer { def accept(value: Long): Unit - @JavaDefaultMethod def andThen(after: LongConsumer): LongConsumer = { (value: Long) => this.accept(value) after.accept(value) diff --git a/javalib/src/main/scala/java/util/function/LongPredicate.scala b/javalib/src/main/scala/java/util/function/LongPredicate.scala index 0b3693e6fc..a2de7a58ba 100755 --- a/javalib/src/main/scala/java/util/function/LongPredicate.scala +++ b/javalib/src/main/scala/java/util/function/LongPredicate.scala @@ -12,13 +12,10 @@ package java.util.function -import scala.scalajs.js.annotation.JavaDefaultMethod - @FunctionalInterface trait LongPredicate { self => def test(t: Long): Boolean - @JavaDefaultMethod def and(other: LongPredicate): LongPredicate = { new LongPredicate { def test(value: Long): Boolean = @@ -26,7 +23,6 @@ trait LongPredicate { self => } } - @JavaDefaultMethod def negate(): LongPredicate = { new LongPredicate { def test(value: Long): Boolean = @@ -34,7 +30,6 @@ trait LongPredicate { self => } } - @JavaDefaultMethod def or(other: LongPredicate): LongPredicate = { new LongPredicate { def test(value: Long): Boolean = diff --git a/javalib/src/main/scala/java/util/function/LongUnaryOperator.scala b/javalib/src/main/scala/java/util/function/LongUnaryOperator.scala index 0b84f242d9..c326b872a8 100644 --- a/javalib/src/main/scala/java/util/function/LongUnaryOperator.scala +++ b/javalib/src/main/scala/java/util/function/LongUnaryOperator.scala @@ -12,18 +12,14 @@ package java.util.function -import scala.scalajs.js.annotation.JavaDefaultMethod - @FunctionalInterface trait LongUnaryOperator { def applyAsLong(operand: Long): Long - @JavaDefaultMethod def andThen(after: LongUnaryOperator): LongUnaryOperator = { (l: Long) => after.applyAsLong(applyAsLong(l)) } - @JavaDefaultMethod def compose(before: LongUnaryOperator): LongUnaryOperator = { (l: Long) => applyAsLong(before.applyAsLong(l)) } diff --git a/javalib/src/main/scala/java/util/function/Predicate.scala b/javalib/src/main/scala/java/util/function/Predicate.scala index 4862e87927..70a2c9404f 100644 --- a/javalib/src/main/scala/java/util/function/Predicate.scala +++ b/javalib/src/main/scala/java/util/function/Predicate.scala @@ -14,13 +14,10 @@ package java.util.function import java.{util => ju} -import scala.scalajs.js.annotation.JavaDefaultMethod - @FunctionalInterface trait Predicate[T] { self => def test(t: T): Boolean - @JavaDefaultMethod def and(other: Predicate[_ >: T]): Predicate[T] = { new Predicate[T] { def test(t: T): Boolean = @@ -28,7 +25,6 @@ trait Predicate[T] { self => } } - @JavaDefaultMethod def negate(): Predicate[T] = { new Predicate[T] { def test(t: T): Boolean = @@ -36,7 +32,6 @@ trait Predicate[T] { self => } } - @JavaDefaultMethod def or(other: Predicate[_ >: T]): Predicate[T] = { new Predicate[T] { def test(t: T): Boolean = diff --git a/javalib/src/main/scala/java/util/regex/PatternCompiler.scala b/javalib/src/main/scala/java/util/regex/PatternCompiler.scala index cb9974f382..b2f001407f 100644 --- a/javalib/src/main/scala/java/util/regex/PatternCompiler.scala +++ b/javalib/src/main/scala/java/util/regex/PatternCompiler.scala @@ -1083,6 +1083,33 @@ private final class PatternCompiler(private val pattern: String, private var fla if (hasRepeater) { // There is a repeater + + /* #4784 Wrap tokens that are Assertions in ES' pattern syntax, since + * it is not syntactically valid to directly quantify them. It is valid + * to quantify a group containing an Assertion, however. + * + * There is no index-out-of-bounds in the following code because + * `compiledToken` is known to be a syntactically valid, non-empty regex. + */ + val isTokenAnAssertion = (compiledToken.charAt(0): @switch) match { + case '^' | '$' => + true + case '(' => + /* This expression would also match named capturing groups, but we + * never emit those. Anyway, even if we did, we would uselessly wrap + * a group that does not need to be, but it would still be correct. + */ + compiledToken.charAt(1) == '?' && compiledToken.charAt(2) != ':' + case '\\' => + val c = compiledToken.charAt(1) + c == 'b' || c == 'B' + case _ => + false + } + val wrappedToken = + if (isTokenAnAssertion) "(?:" + compiledToken + ")" + else compiledToken + val baseRepeater = parseBaseRepeater(repeaterDispatchChar) if (pIndex != len) { @@ -1090,18 +1117,18 @@ private final class PatternCompiler(private val pattern: String, private var fla case '+' => // Possessive quantifier pIndex += 1 - buildPossessiveQuantifier(compiledGroupCountBeforeThisToken, compiledToken, baseRepeater) + buildPossessiveQuantifier(compiledGroupCountBeforeThisToken, wrappedToken, baseRepeater) case '?' => // Lazy quantifier pIndex += 1 - compiledToken + baseRepeater + "?" + wrappedToken + baseRepeater + "?" case _ => // Greedy quantifier - compiledToken + baseRepeater + wrappedToken + baseRepeater } } else { // Greedy quantifier - compiledToken + baseRepeater + wrappedToken + baseRepeater } } else { // No repeater @@ -1192,13 +1219,7 @@ private final class PatternCompiler(private val pattern: String, private var fla else "(?<=^|\r(?!\n)|[\n\u0085\u2028\u2029])" } else { - /* Wrap as (?:^) in case it ends up being repeated, for example `^+` - * becomes `(?:^)+`. This is necessary because `^+` is not syntactically - * valid in JS, although it is valid once wrapped in a group. - * (Not that repeating ^ has any useful purpose, but the spec does not - * prevent it.) - */ - "(?:^)" + "^" } } @@ -1214,8 +1235,7 @@ private final class PatternCompiler(private val pattern: String, private var fla else "(?=$|(? // We can always use ^ for start-of-text because we never use the 'm' flag in the JS RegExp pIndex += 1 - "(?:^)" // wrap in case it is quantified (see compilation of '^') + "^" case 'G' => parseError("\\G in the middle of a pattern is not supported") case 'Z' => @@ -1316,7 +1336,7 @@ private final class PatternCompiler(private val pattern: String, private var fla case 'z' => // We can always use $ for end-of-text because we never use the 'm' flag in the JS RegExp pIndex += 1 - "(?:$)" // wrap in case it is quantified (see compilation of '$') + "$" // Linebreak matcher diff --git a/library-aux/src/main/scala/scala/runtime/Statics.scala b/library-aux/src/main/scala/scala/runtime/Statics.scala index a33e9d2a08..69f549c073 100644 --- a/library-aux/src/main/scala/scala/runtime/Statics.scala +++ b/library-aux/src/main/scala/scala/runtime/Statics.scala @@ -53,16 +53,6 @@ object Statics { } def doubleHash(dv: Double): Int = { - /* This implementation is based on what 2.12.0-M5+ does on the JVM. - * The 2.11 implementation on the JVM was not consistent with that of - * BoxesRunTime, and most importantly was not consistent with the hash of - * Long values. - * - * In Scala.js, we always use the version consistent with BoxesRunTime. - * Note that, for values that happen to be valid floats but not valid - * longs, this implementation is *not* consistent with the JVM (just like - * that of BoxesRunTime). - */ val iv = dv.toInt if (iv == dv) { iv diff --git a/library/src/main/scala/scala/scalajs/js/Tuple.nodoc.scala b/library/src/main/scala/scala/scalajs/js/Tuple.nodoc.scala index 962be5531c..e772e0bb26 100644 --- a/library/src/main/scala/scala/scalajs/js/Tuple.nodoc.scala +++ b/library/src/main/scala/scala/scalajs/js/Tuple.nodoc.scala @@ -36,7 +36,11 @@ object Tuple4 { @inline def apply[T1, T2, T3, T4](_1: T1, _2: T2, _3: T3, _4: T4): js.Tuple4[T1, T2, T3, T4] = js.Array(_1, _2, _3, _4).asInstanceOf[js.Tuple4[T1, T2, T3, T4]] - @inline def unapply[T1, T2, T3, T4](t: js.Tuple4[T1, T2, T3, T4]): Option[(T1, T2, T3, T4)] = + @inline def unapply[T1, T2, T3, T4](t: js.Tuple4[T1, T2, T3, T4]): Some[(T1, T2, T3, T4)] = + Some(t) + + // For binary compatibility + @inline protected def unapply[T1, T2, T3, T4, Dummy](t: js.Tuple4[T1, T2, T3, T4]): Option[(T1, T2, T3, T4)] = Some(t) @inline implicit def fromScalaTuple4[T1, T2, T3, T4](t: (T1, T2, T3, T4)): js.Tuple4[T1, T2, T3, T4] = @@ -63,7 +67,11 @@ object Tuple5 { @inline def apply[T1, T2, T3, T4, T5](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5): js.Tuple5[T1, T2, T3, T4, T5] = js.Array(_1, _2, _3, _4, _5).asInstanceOf[js.Tuple5[T1, T2, T3, T4, T5]] - @inline def unapply[T1, T2, T3, T4, T5](t: js.Tuple5[T1, T2, T3, T4, T5]): Option[(T1, T2, T3, T4, T5)] = + @inline def unapply[T1, T2, T3, T4, T5](t: js.Tuple5[T1, T2, T3, T4, T5]): Some[(T1, T2, T3, T4, T5)] = + Some(t) + + // For binary compatibility + @inline protected def unapply[T1, T2, T3, T4, T5, Dummy](t: js.Tuple5[T1, T2, T3, T4, T5]): Option[(T1, T2, T3, T4, T5)] = Some(t) @inline implicit def fromScalaTuple5[T1, T2, T3, T4, T5](t: (T1, T2, T3, T4, T5)): js.Tuple5[T1, T2, T3, T4, T5] = @@ -91,7 +99,11 @@ object Tuple6 { @inline def apply[T1, T2, T3, T4, T5, T6](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6): js.Tuple6[T1, T2, T3, T4, T5, T6] = js.Array(_1, _2, _3, _4, _5, _6).asInstanceOf[js.Tuple6[T1, T2, T3, T4, T5, T6]] - @inline def unapply[T1, T2, T3, T4, T5, T6](t: js.Tuple6[T1, T2, T3, T4, T5, T6]): Option[(T1, T2, T3, T4, T5, T6)] = + @inline def unapply[T1, T2, T3, T4, T5, T6](t: js.Tuple6[T1, T2, T3, T4, T5, T6]): Some[(T1, T2, T3, T4, T5, T6)] = + Some(t) + + // For binary compatibility + @inline protected def unapply[T1, T2, T3, T4, T5, T6, Dummy](t: js.Tuple6[T1, T2, T3, T4, T5, T6]): Option[(T1, T2, T3, T4, T5, T6)] = Some(t) @inline implicit def fromScalaTuple6[T1, T2, T3, T4, T5, T6](t: (T1, T2, T3, T4, T5, T6)): js.Tuple6[T1, T2, T3, T4, T5, T6] = @@ -120,7 +132,11 @@ object Tuple7 { @inline def apply[T1, T2, T3, T4, T5, T6, T7](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7): js.Tuple7[T1, T2, T3, T4, T5, T6, T7] = js.Array(_1, _2, _3, _4, _5, _6, _7).asInstanceOf[js.Tuple7[T1, T2, T3, T4, T5, T6, T7]] - @inline def unapply[T1, T2, T3, T4, T5, T6, T7](t: js.Tuple7[T1, T2, T3, T4, T5, T6, T7]): Option[(T1, T2, T3, T4, T5, T6, T7)] = + @inline def unapply[T1, T2, T3, T4, T5, T6, T7](t: js.Tuple7[T1, T2, T3, T4, T5, T6, T7]): Some[(T1, T2, T3, T4, T5, T6, T7)] = + Some(t) + + // For binary compatibility + @inline protected def unapply[T1, T2, T3, T4, T5, T6, T7, Dummy](t: js.Tuple7[T1, T2, T3, T4, T5, T6, T7]): Option[(T1, T2, T3, T4, T5, T6, T7)] = Some(t) @inline implicit def fromScalaTuple7[T1, T2, T3, T4, T5, T6, T7](t: (T1, T2, T3, T4, T5, T6, T7)): js.Tuple7[T1, T2, T3, T4, T5, T6, T7] = @@ -150,7 +166,11 @@ object Tuple8 { @inline def apply[T1, T2, T3, T4, T5, T6, T7, T8](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8): js.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8] = js.Array(_1, _2, _3, _4, _5, _6, _7, _8).asInstanceOf[js.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] - @inline def unapply[T1, T2, T3, T4, T5, T6, T7, T8](t: js.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]): Option[(T1, T2, T3, T4, T5, T6, T7, T8)] = + @inline def unapply[T1, T2, T3, T4, T5, T6, T7, T8](t: js.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]): Some[(T1, T2, T3, T4, T5, T6, T7, T8)] = + Some(t) + + // For binary compatibility + @inline protected def unapply[T1, T2, T3, T4, T5, T6, T7, T8, Dummy](t: js.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]): Option[(T1, T2, T3, T4, T5, T6, T7, T8)] = Some(t) @inline implicit def fromScalaTuple8[T1, T2, T3, T4, T5, T6, T7, T8](t: (T1, T2, T3, T4, T5, T6, T7, T8)): js.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8] = @@ -181,7 +201,11 @@ object Tuple9 { @inline def apply[T1, T2, T3, T4, T5, T6, T7, T8, T9](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9): js.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9] = js.Array(_1, _2, _3, _4, _5, _6, _7, _8, _9).asInstanceOf[js.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] - @inline def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9](t: js.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]): Option[(T1, T2, T3, T4, T5, T6, T7, T8, T9)] = + @inline def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9](t: js.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]): Some[(T1, T2, T3, T4, T5, T6, T7, T8, T9)] = + Some(t) + + // For binary compatibility + @inline protected def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, Dummy](t: js.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]): Option[(T1, T2, T3, T4, T5, T6, T7, T8, T9)] = Some(t) @inline implicit def fromScalaTuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9](t: (T1, T2, T3, T4, T5, T6, T7, T8, T9)): js.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9] = @@ -213,7 +237,11 @@ object Tuple10 { @inline def apply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10): js.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10] = js.Array(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10).asInstanceOf[js.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] - @inline def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10](t: js.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]): Option[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10)] = + @inline def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10](t: js.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]): Some[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10)] = + Some(t) + + // For binary compatibility + @inline protected def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, Dummy](t: js.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]): Option[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10)] = Some(t) @inline implicit def fromScalaTuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10](t: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10)): js.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10] = @@ -246,7 +274,11 @@ object Tuple11 { @inline def apply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11): js.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11] = js.Array(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11).asInstanceOf[js.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] - @inline def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11](t: js.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]): Option[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11)] = + @inline def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11](t: js.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]): Some[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11)] = + Some(t) + + // For binary compatibility + @inline protected def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, Dummy](t: js.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]): Option[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11)] = Some(t) @inline implicit def fromScalaTuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11](t: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11)): js.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11] = @@ -280,7 +312,11 @@ object Tuple12 { @inline def apply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11, _12: T12): js.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12] = js.Array(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12).asInstanceOf[js.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] - @inline def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12](t: js.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]): Option[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12)] = + @inline def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12](t: js.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]): Some[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12)] = + Some(t) + + // For binary compatibility + @inline protected def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, Dummy](t: js.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]): Option[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12)] = Some(t) @inline implicit def fromScalaTuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12](t: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12)): js.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12] = @@ -315,7 +351,11 @@ object Tuple13 { @inline def apply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11, _12: T12, _13: T13): js.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13] = js.Array(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13).asInstanceOf[js.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] - @inline def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13](t: js.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]): Option[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13)] = + @inline def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13](t: js.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]): Some[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13)] = + Some(t) + + // For binary compatibility + @inline protected def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, Dummy](t: js.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]): Option[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13)] = Some(t) @inline implicit def fromScalaTuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13](t: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13)): js.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13] = @@ -351,7 +391,11 @@ object Tuple14 { @inline def apply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11, _12: T12, _13: T13, _14: T14): js.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14] = js.Array(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14).asInstanceOf[js.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] - @inline def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14](t: js.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]): Option[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14)] = + @inline def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14](t: js.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]): Some[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14)] = + Some(t) + + // For binary compatibility + @inline protected def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, Dummy](t: js.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]): Option[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14)] = Some(t) @inline implicit def fromScalaTuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14](t: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14)): js.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14] = @@ -388,7 +432,11 @@ object Tuple15 { @inline def apply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11, _12: T12, _13: T13, _14: T14, _15: T15): js.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15] = js.Array(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15).asInstanceOf[js.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] - @inline def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15](t: js.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]): Option[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15)] = + @inline def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15](t: js.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]): Some[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15)] = + Some(t) + + // For binary compatibility + @inline protected def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, Dummy](t: js.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]): Option[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15)] = Some(t) @inline implicit def fromScalaTuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15](t: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15)): js.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15] = @@ -426,7 +474,11 @@ object Tuple16 { @inline def apply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11, _12: T12, _13: T13, _14: T14, _15: T15, _16: T16): js.Tuple16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16] = js.Array(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16).asInstanceOf[js.Tuple16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16]] - @inline def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16](t: js.Tuple16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16]): Option[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16)] = + @inline def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16](t: js.Tuple16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16]): Some[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16)] = + Some(t) + + // For binary compatibility + @inline protected def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, Dummy](t: js.Tuple16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16]): Option[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16)] = Some(t) @inline implicit def fromScalaTuple16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16](t: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16)): js.Tuple16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16] = @@ -465,7 +517,11 @@ object Tuple17 { @inline def apply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11, _12: T12, _13: T13, _14: T14, _15: T15, _16: T16, _17: T17): js.Tuple17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17] = js.Array(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17).asInstanceOf[js.Tuple17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17]] - @inline def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17](t: js.Tuple17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17]): Option[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17)] = + @inline def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17](t: js.Tuple17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17]): Some[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17)] = + Some(t) + + // For binary compatibility + @inline protected def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, Dummy](t: js.Tuple17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17]): Option[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17)] = Some(t) @inline implicit def fromScalaTuple17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17](t: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17)): js.Tuple17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17] = @@ -505,7 +561,11 @@ object Tuple18 { @inline def apply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11, _12: T12, _13: T13, _14: T14, _15: T15, _16: T16, _17: T17, _18: T18): js.Tuple18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18] = js.Array(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18).asInstanceOf[js.Tuple18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18]] - @inline def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18](t: js.Tuple18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18]): Option[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18)] = + @inline def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18](t: js.Tuple18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18]): Some[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18)] = + Some(t) + + // For binary compatibility + @inline protected def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, Dummy](t: js.Tuple18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18]): Option[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18)] = Some(t) @inline implicit def fromScalaTuple18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18](t: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18)): js.Tuple18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18] = @@ -546,7 +606,11 @@ object Tuple19 { @inline def apply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11, _12: T12, _13: T13, _14: T14, _15: T15, _16: T16, _17: T17, _18: T18, _19: T19): js.Tuple19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19] = js.Array(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19).asInstanceOf[js.Tuple19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19]] - @inline def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19](t: js.Tuple19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19]): Option[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19)] = + @inline def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19](t: js.Tuple19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19]): Some[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19)] = + Some(t) + + // For binary compatibility + @inline protected def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, Dummy](t: js.Tuple19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19]): Option[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19)] = Some(t) @inline implicit def fromScalaTuple19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19](t: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19)): js.Tuple19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19] = @@ -588,7 +652,11 @@ object Tuple20 { @inline def apply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11, _12: T12, _13: T13, _14: T14, _15: T15, _16: T16, _17: T17, _18: T18, _19: T19, _20: T20): js.Tuple20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20] = js.Array(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20).asInstanceOf[js.Tuple20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20]] - @inline def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20](t: js.Tuple20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20]): Option[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20)] = + @inline def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20](t: js.Tuple20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20]): Some[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20)] = + Some(t) + + // For binary compatibility + @inline protected def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, Dummy](t: js.Tuple20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20]): Option[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20)] = Some(t) @inline implicit def fromScalaTuple20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20](t: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20)): js.Tuple20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20] = @@ -631,7 +699,11 @@ object Tuple21 { @inline def apply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11, _12: T12, _13: T13, _14: T14, _15: T15, _16: T16, _17: T17, _18: T18, _19: T19, _20: T20, _21: T21): js.Tuple21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21] = js.Array(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21).asInstanceOf[js.Tuple21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21]] - @inline def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21](t: js.Tuple21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21]): Option[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21)] = + @inline def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21](t: js.Tuple21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21]): Some[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21)] = + Some(t) + + // For binary compatibility + @inline protected def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, Dummy](t: js.Tuple21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21]): Option[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21)] = Some(t) @inline implicit def fromScalaTuple21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21](t: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21)): js.Tuple21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21] = @@ -675,7 +747,11 @@ object Tuple22 { @inline def apply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11, _12: T12, _13: T13, _14: T14, _15: T15, _16: T16, _17: T17, _18: T18, _19: T19, _20: T20, _21: T21, _22: T22): js.Tuple22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22] = js.Array(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22).asInstanceOf[js.Tuple22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22]] - @inline def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22](t: js.Tuple22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22]): Option[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22)] = + @inline def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22](t: js.Tuple22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22]): Some[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22)] = + Some(t) + + // For binary compatibility + @inline protected def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, Dummy](t: js.Tuple22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22]): Option[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22)] = Some(t) @inline implicit def fromScalaTuple22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22](t: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22)): js.Tuple22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22] = diff --git a/library/src/main/scala/scala/scalajs/js/Tuple.scala b/library/src/main/scala/scala/scalajs/js/Tuple.scala index 9222e93652..9be0ba6cc2 100644 --- a/library/src/main/scala/scala/scalajs/js/Tuple.scala +++ b/library/src/main/scala/scala/scalajs/js/Tuple.scala @@ -46,7 +46,11 @@ object Tuple2 { @inline def apply[T1, T2](_1: T1, _2: T2): js.Tuple2[T1, T2] = js.Array(_1, _2).asInstanceOf[js.Tuple2[T1, T2]] - @inline def unapply[T1, T2](t: js.Tuple2[T1, T2]): Option[(T1, T2)] = + @inline def unapply[T1, T2](t: js.Tuple2[T1, T2]): Some[(T1, T2)] = + Some(t) + + // For binary compatibility + @inline protected def unapply[T1, T2, Dummy](t: js.Tuple2[T1, T2]): Option[(T1, T2)] = Some(t) @inline implicit def fromScalaTuple2[T1, T2](t: (T1, T2)): js.Tuple2[T1, T2] = @@ -71,7 +75,11 @@ object Tuple3 { @inline def apply[T1, T2, T3](_1: T1, _2: T2, _3: T3): js.Tuple3[T1, T2, T3] = js.Array(_1, _2, _3).asInstanceOf[js.Tuple3[T1, T2, T3]] - @inline def unapply[T1, T2, T3](t: js.Tuple3[T1, T2, T3]): Option[(T1, T2, T3)] = + @inline def unapply[T1, T2, T3](t: js.Tuple3[T1, T2, T3]): Some[(T1, T2, T3)] = + Some(t) + + // For binary compatibility + @inline protected def unapply[T1, T2, T3, Dummy](t: js.Tuple3[T1, T2, T3]): Option[(T1, T2, T3)] = Some(t) @inline implicit def fromScalaTuple3[T1, T2, T3](t: (T1, T2, T3)): js.Tuple3[T1, T2, T3] = diff --git a/library/src/main/scala/scala/scalajs/js/annotation/JSExportAll.scala b/library/src/main/scala/scala/scalajs/js/annotation/JSExportAll.scala index 1e0ec32910..5d70f7f4e5 100644 --- a/library/src/main/scala/scala/scalajs/js/annotation/JSExportAll.scala +++ b/library/src/main/scala/scala/scalajs/js/annotation/JSExportAll.scala @@ -12,11 +12,13 @@ package scala.scalajs.js.annotation -/** Exports all public members directly defined in a class / object. +/** Exports all public term members directly defined in a class / object. * - * Strictly equivalent to putting [[JSExport]] on every public member. - * Note: You are allowed to export protected members, but you'll have to do - * this explicitly on each member. + * Strictly equivalent to putting [[JSExport]] on every public term + * member (def, val, var, lazy val, object). + * + * Note: You are allowed to export protected members and classes. However, + * you'll have to do this explicitly on each member. * * @see [[http://www.scala-js.org/doc/export-to-javascript.html Export Scala.js APIs to JavaScript]] */ diff --git a/library/src/main/scala/scala/scalajs/js/annotation/JavaDefaultMethod.scala b/library/src/main/scala/scala/scalajs/js/annotation/JavaDefaultMethod.scala index db1474fd4e..14d387a486 100644 --- a/library/src/main/scala/scala/scalajs/js/annotation/JavaDefaultMethod.scala +++ b/library/src/main/scala/scala/scalajs/js/annotation/JavaDefaultMethod.scala @@ -20,4 +20,5 @@ package scala.scalajs.js.annotation * * Otherwise using this annotation is unspecified. */ +@deprecated("Has no effect in Scala 2.12+ (default methods are the default). Remove", "1.13.0") class JavaDefaultMethod extends scala.annotation.StaticAnnotation diff --git a/library/src/main/scala/scala/scalajs/js/timers/RawTimers.scala b/library/src/main/scala/scala/scalajs/js/timers/RawTimers.scala index 4ba4b3713d..a359542381 100644 --- a/library/src/main/scala/scala/scalajs/js/timers/RawTimers.scala +++ b/library/src/main/scala/scala/scalajs/js/timers/RawTimers.scala @@ -22,6 +22,11 @@ import js.annotation.JSGlobalScope * The methods on this object expose the raw JavaScript methods for timers. In * general it is more advisable to use the methods directly defined on * [[timers]] as they are more Scala-like. + * + * The methods exposed by this object are not standard in ECMAScript. + * Different JavaScript environments support all, some or none of them. + * + * Browsers support all those methods as part the of the DOM standard. */ @js.native @JSGlobalScope diff --git a/library/src/main/scala/scala/scalajs/js/timers/package.scala b/library/src/main/scala/scala/scalajs/js/timers/package.scala index bdbf9333a9..f255d6e2eb 100644 --- a/library/src/main/scala/scala/scalajs/js/timers/package.scala +++ b/library/src/main/scala/scala/scalajs/js/timers/package.scala @@ -16,11 +16,17 @@ import scala.concurrent.duration.FiniteDuration /** * Non-Standard - * Non-standard, but in general well supported methods to schedule asynchronous + * Non-standard, although often supported methods to schedule asynchronous * execution. * - * The methods in this package work in all JavaScript virtual machines - * supporting `setTimeout` and `setInterval`. + * The methods in this package are not supported in all JavaScript + * environments. Each relies on the global JavaScript function with the same + * name. + * + * The corresponding methods are not standard in ECMAScript. + * Different JavaScript environments support all, some or none of them. + * + * Browsers support all those methods as part the of the DOM standard. */ package object timers { diff --git a/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/Semantics.scala b/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/Semantics.scala index 1cfe1db3f0..5795b1c5db 100644 --- a/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/Semantics.scala +++ b/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/Semantics.scala @@ -20,6 +20,7 @@ final class Semantics private ( val arrayIndexOutOfBounds: CheckedBehavior, val arrayStores: CheckedBehavior, val negativeArraySizes: CheckedBehavior, + val nullPointers: CheckedBehavior, val stringIndexOutOfBounds: CheckedBehavior, val moduleInit: CheckedBehavior, val strictFloats: Boolean, @@ -40,6 +41,9 @@ final class Semantics private ( def withNegativeArraySizes(behavior: CheckedBehavior): Semantics = copy(negativeArraySizes = behavior) + def withNullPointers(behavior: CheckedBehavior): Semantics = + copy(nullPointers = behavior) + def withStringIndexOutOfBounds(behavior: CheckedBehavior): Semantics = copy(stringIndexOutOfBounds = behavior) @@ -67,6 +71,7 @@ final class Semantics private ( arrayIndexOutOfBounds = this.arrayIndexOutOfBounds.optimized, arrayStores = this.arrayStores.optimized, negativeArraySizes = this.negativeArraySizes.optimized, + nullPointers = this.nullPointers.optimized, stringIndexOutOfBounds = this.stringIndexOutOfBounds.optimized, moduleInit = this.moduleInit.optimized, productionMode = true) @@ -78,6 +83,7 @@ final class Semantics private ( this.arrayIndexOutOfBounds == that.arrayIndexOutOfBounds && this.arrayStores == that.arrayStores && this.negativeArraySizes == that.negativeArraySizes && + this.nullPointers == that.nullPointers && this.stringIndexOutOfBounds == that.stringIndexOutOfBounds && this.moduleInit == that.moduleInit && this.strictFloats == that.strictFloats && @@ -94,12 +100,13 @@ final class Semantics private ( acc = mix(acc, arrayIndexOutOfBounds.##) acc = mix(acc, arrayStores.##) acc = mix(acc, negativeArraySizes.##) + acc = mix(acc, nullPointers.##) acc = mix(acc, stringIndexOutOfBounds.##) acc = mix(acc, moduleInit.##) acc = mix(acc, strictFloats.##) acc = mix(acc, productionMode.##) acc = mixLast(acc, runtimeClassNameMapper.##) - finalizeHash(acc, 9) + finalizeHash(acc, 10) } override def toString(): String = { @@ -108,6 +115,7 @@ final class Semantics private ( | arrayIndexOutOfBounds = $arrayIndexOutOfBounds, | arrayStores = $arrayStores, | negativeArraySizes = $negativeArraySizes, + | nullPointers = $nullPointers, | stringIndexOutOfBounds = $stringIndexOutOfBounds, | moduleInit = $moduleInit, | strictFloats = $strictFloats, @@ -120,6 +128,7 @@ final class Semantics private ( arrayIndexOutOfBounds: CheckedBehavior = this.arrayIndexOutOfBounds, arrayStores: CheckedBehavior = this.arrayStores, negativeArraySizes: CheckedBehavior = this.negativeArraySizes, + nullPointers: CheckedBehavior = this.nullPointers, stringIndexOutOfBounds: CheckedBehavior = this.stringIndexOutOfBounds, moduleInit: CheckedBehavior = this.moduleInit, strictFloats: Boolean = this.strictFloats, @@ -131,6 +140,7 @@ final class Semantics private ( arrayIndexOutOfBounds = arrayIndexOutOfBounds, arrayStores = arrayStores, negativeArraySizes = negativeArraySizes, + nullPointers = nullPointers, stringIndexOutOfBounds = stringIndexOutOfBounds, moduleInit = moduleInit, strictFloats = strictFloats, @@ -249,6 +259,7 @@ object Semantics { .addField("arrayIndexOutOfBounds", semantics.arrayIndexOutOfBounds) .addField("arrayStores", semantics.arrayStores) .addField("negativeArraySizes", semantics.negativeArraySizes) + .addField("nullPointers", semantics.nullPointers) .addField("stringIndexOutOfBounds", semantics.stringIndexOutOfBounds) .addField("moduleInit", semantics.moduleInit) .addField("strictFloats", semantics.strictFloats) @@ -263,6 +274,7 @@ object Semantics { arrayIndexOutOfBounds = Fatal, arrayStores = Fatal, negativeArraySizes = Fatal, + nullPointers = Fatal, stringIndexOutOfBounds = Fatal, moduleInit = Unchecked, strictFloats = true, diff --git a/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/unstable/IRContainerImpl.scala b/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/unstable/IRContainerImpl.scala index 4393102b0b..195ec91d02 100644 --- a/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/unstable/IRContainerImpl.scala +++ b/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/unstable/IRContainerImpl.scala @@ -42,7 +42,7 @@ abstract class IRContainerImpl( * Such a token can be used by caches: the file need not be read and * processed again if its version has not changed. */ - val version: Option[String] + val version: ir.Version ) extends IRContainer { private[interface] final def impl: IRContainerImpl = this diff --git a/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/unstable/IRFileImpl.scala b/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/unstable/IRFileImpl.scala index 3f0ee18049..90126e6ccd 100644 --- a/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/unstable/IRFileImpl.scala +++ b/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/unstable/IRFileImpl.scala @@ -39,7 +39,7 @@ abstract class IRFileImpl( * Such a token can be used by caches: the file need not be read and * processed again if its version has not changed. */ - val version: Option[String] + val version: ir.Version ) extends IRFile { private[interface] final def impl: IRFileImpl = this diff --git a/linker/js/src/main/scala/org/scalajs/linker/NodeIRContainer.scala b/linker/js/src/main/scala/org/scalajs/linker/NodeIRContainer.scala index ee25267859..5a938b9223 100644 --- a/linker/js/src/main/scala/org/scalajs/linker/NodeIRContainer.scala +++ b/linker/js/src/main/scala/org/scalajs/linker/NodeIRContainer.scala @@ -79,7 +79,7 @@ object NodeIRContainer { (e.asInstanceOf[js.Dynamic].code: Any) == "ENOENT" private final class NodeJarIRContainer(path: String, version: Option[js.Date]) - extends IRContainerImpl(path, version.map(_.getTime().toString)) { + extends IRContainerImpl(path, NodeIRFile.dateToVersion(version)) { import NodeFS._ def sjsirFiles(implicit ec: ExecutionContext): Future[List[IRFile]] = { diff --git a/linker/js/src/main/scala/org/scalajs/linker/NodeIRFile.scala b/linker/js/src/main/scala/org/scalajs/linker/NodeIRFile.scala index 719595ad55..a21cf0b77f 100644 --- a/linker/js/src/main/scala/org/scalajs/linker/NodeIRFile.scala +++ b/linker/js/src/main/scala/org/scalajs/linker/NodeIRFile.scala @@ -34,8 +34,17 @@ object NodeIRFile { new NodeIRFileImpl(path, stats.mtime.toOption)) } + private[linker] def dateToVersion(optDate: Option[js.Date]): ir.Version = { + optDate + .map(_.getTime()) + // filter invalid dates and over / underflows. + .filter(d => !d.isNaN && !d.isInfinity) + .map(_.toLong) + .fold(ir.Version.Unversioned)(ir.Version.fromLong(_)) + } + private final class NodeIRFileImpl(path: String, version: Option[js.Date]) - extends IRFileImpl(path, version.map(_.getTime().toString)) { + extends IRFileImpl(path, dateToVersion(version)) { def entryPointsInfo(implicit ec: ExecutionContext): Future[ir.EntryPointsInfo] = { def loop(fd: Int, buf: ByteBuffer): Future[ir.EntryPointsInfo] = { diff --git a/linker/js/src/main/scala/org/scalajs/linker/backend/emitter/PrivateLibHolder.scala b/linker/js/src/main/scala/org/scalajs/linker/backend/emitter/PrivateLibHolder.scala index b84b790498..84b5b93d13 100644 --- a/linker/js/src/main/scala/org/scalajs/linker/backend/emitter/PrivateLibHolder.scala +++ b/linker/js/src/main/scala/org/scalajs/linker/backend/emitter/PrivateLibHolder.scala @@ -12,6 +12,8 @@ package org.scalajs.linker.backend.emitter +import org.scalajs.ir + import org.scalajs.linker.interface.IRFile import org.scalajs.linker.standard.MemIRFileImpl @@ -20,7 +22,7 @@ object PrivateLibHolder { for ((name, contentBase64) <- PrivateLibData.pathsAndContents) yield { new MemIRFileImpl( path = "org/scalajs/linker/runtime/" + name, - version = Some(""), // this indicates that the file never changes + version = ir.Version.fromInt(0), // never changes content = java.util.Base64.getDecoder().decode(contentBase64) ) } diff --git a/linker/jvm/src/main/scala/org/scalajs/linker/PathIRContainer.scala b/linker/jvm/src/main/scala/org/scalajs/linker/PathIRContainer.scala index 70c6a13470..799b61b586 100644 --- a/linker/jvm/src/main/scala/org/scalajs/linker/PathIRContainer.scala +++ b/linker/jvm/src/main/scala/org/scalajs/linker/PathIRContainer.scala @@ -23,6 +23,8 @@ import org.scalajs.linker.interface.{IRContainer, IRFile} import org.scalajs.linker.interface.unstable.IRContainerImpl import org.scalajs.linker.standard.MemIRFileImpl +import PathIRFile.fileTimeToVersion + object PathIRContainer { def fromClasspath(classpath: Seq[Path])( implicit ec: ExecutionContext): Future[(Seq[IRContainer], Seq[Path])] = Future { @@ -52,7 +54,7 @@ object PathIRContainer { } private final class JarIRContainer(path: Path, lastModified: FileTime) - extends IRContainerImpl(path.toString, Some(lastModified.toString)) { + extends IRContainerImpl(path.toString, fileTimeToVersion(lastModified)) { def sjsirFiles(implicit ec: ExecutionContext): Future[List[IRFile]] = Future { val files = List.newBuilder[IRFile] diff --git a/linker/jvm/src/main/scala/org/scalajs/linker/PathIRFile.scala b/linker/jvm/src/main/scala/org/scalajs/linker/PathIRFile.scala index b6d6aa111f..041aca85f0 100644 --- a/linker/jvm/src/main/scala/org/scalajs/linker/PathIRFile.scala +++ b/linker/jvm/src/main/scala/org/scalajs/linker/PathIRFile.scala @@ -17,6 +17,7 @@ import scala.concurrent._ import java.io._ import java.nio._ import java.nio.channels._ +import java.nio.charset.StandardCharsets import java.nio.file._ import java.nio.file.attribute._ @@ -30,8 +31,13 @@ object PathIRFile { .map(new PathIRFileImpl(path, _)) } + private[linker] def fileTimeToVersion(time: FileTime): ir.Version = { + // FileTime.toString seems to be the only lossless way to get a byte string. + ir.Version.fromBytes(time.toString().getBytes(StandardCharsets.US_ASCII)) + } + private[linker] final class PathIRFileImpl(path: Path, lastModified: FileTime) - extends IRFileImpl(path.toString, Some(lastModified.toString)) { + extends IRFileImpl(path.toString, fileTimeToVersion(lastModified)) { def entryPointsInfo(implicit ec: ExecutionContext): Future[ir.EntryPointsInfo] = { def loop(chan: AsynchronousFileChannel, buf: ByteBuffer): Future[ir.EntryPointsInfo] = { readAsync(chan, buf).map { _ => 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 118724602e..276cec4f71 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 @@ -177,7 +177,7 @@ final class ClosureLinkerBackend(config: LinkerBackendImpl.Config) val exportedPropertyNames = for { classDef <- sjsModule.classDefs member <- classDef.exportedMembers - name <- exportName(member.value) + name <- exportName(member) if isValidJSIdentifierName(name) } yield { name diff --git a/linker/jvm/src/main/scala/org/scalajs/linker/backend/emitter/PrivateLibHolder.scala b/linker/jvm/src/main/scala/org/scalajs/linker/backend/emitter/PrivateLibHolder.scala index c39e9c566e..96b78cbd89 100644 --- a/linker/jvm/src/main/scala/org/scalajs/linker/backend/emitter/PrivateLibHolder.scala +++ b/linker/jvm/src/main/scala/org/scalajs/linker/backend/emitter/PrivateLibHolder.scala @@ -14,6 +14,8 @@ package org.scalajs.linker.backend.emitter import java.io._ +import org.scalajs.ir + import org.scalajs.linker.interface.IRFile import org.scalajs.linker.standard.MemIRFileImpl @@ -30,7 +32,7 @@ object PrivateLibHolder { val name = path.substring(path.lastIndexOf('/') + 1) new MemIRFileImpl( path = path, - version = Some(""), // this indicates that the file never changes + version = ir.Version.fromInt(0), // never changes content = readResource(name) ) } diff --git a/linker/shared/src/main/scala/org/scalajs/linker/CollectionsCompat.scala b/linker/shared/src/main/scala/org/scalajs/linker/CollectionsCompat.scala index b517e23fa2..4a05e97eb1 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/CollectionsCompat.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/CollectionsCompat.scala @@ -29,4 +29,11 @@ private[linker] object CollectionsCompat { } } } + + implicit class ArrayBufferCompatOps[V](private val self: mutable.ArrayBuffer[V]) + extends AnyVal { + + def dropRightInPlace(n: Int): Unit = + self.remove(self.length - n, n) + } } 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 611e92e668..82239836ef 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 @@ -35,7 +35,7 @@ trait Analysis { import Analysis._ def classInfos: scala.collection.Map[ClassName, ClassInfo] - def topLevelExportInfos: Map[(ModuleID, String), TopLevelExportInfo] + def topLevelExportInfos: scala.collection.Map[(ModuleID, String), TopLevelExportInfo] def errors: scala.collection.Seq[Error] } 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 03920312b7..0339d72ee9 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 @@ -64,7 +64,10 @@ private final class Analyzer(config: CommonPhaseConfig, def classInfos: scala.collection.Map[ClassName, Analysis.ClassInfo] = _loadedClassInfos - var topLevelExportInfos: Map[(ModuleID, String), Analysis.TopLevelExportInfo] = _ + private[this] val _topLevelExportInfos = mutable.Map.empty[(ModuleID, String), TopLevelExportInfo] + + def topLevelExportInfos: scala.collection.Map[(ModuleID, String), Analysis.TopLevelExportInfo] = + _topLevelExportInfos def errors: scala.collection.Seq[Error] = _errors @@ -124,18 +127,27 @@ private final class Analyzer(config: CommonPhaseConfig, // External symbol requirements. reachSymbolRequirement(symbolRequirements) - // Reach static initializers. + // Reach entry points for (className <- inputProvider.classesWithEntryPoints()) - lookupClass(className)(_.reachStaticInitializer()) + lookupClass(className)(_.reachEntryPoints()) // Reach module initializers. reachInitializers(moduleInitializers) - - // Reach top level exports - reachTopLevelExports() } private def postLoad(): Unit = { + if (isNoModule) { + // Check there is only a single module. + val publicModuleIDs = ( + topLevelExportInfos.keys.map(_._1).toList ++ + moduleInitializers.map(i => ModuleID(i.moduleID)) + ).distinct + + if (publicModuleIDs.size > 1) + _errors += MultiplePublicModulesWithoutModuleSupport(publicModuleIDs) + } + + // Assemble loaded infos. val infos = _classInfos.collect { case (k, i: ClassInfo) => (k, i) } assert(_errors.nonEmpty || infos.size == _classInfos.size, @@ -255,36 +267,6 @@ private final class Analyzer(config: CommonPhaseConfig, } } - private def reachTopLevelExports(): Unit = { - workQueue.enqueue(inputProvider.loadTopLevelExportInfos()(ec)) { data => - // Assemble individual infos. - val infos = data.map(new TopLevelExportInfo(_)) - - infos.foreach(_.reach()) - - if (isNoModule) { - // Check there is only a single module. - val publicModuleIDs = ( - infos.map(_.moduleID) ++ - moduleInitializers.map(i => ModuleID(i.moduleID)) - ).distinct - - if (publicModuleIDs.size > 1) - _errors += MultiplePublicModulesWithoutModuleSupport(publicModuleIDs) - } - - // Check conflicts, record infos - topLevelExportInfos = for { - (id @ (moduleID, exportName), infos) <- infos.groupBy(i => (i.moduleID, i.exportName)) - } yield { - if (infos.size > 1) - _errors += ConflictingTopLevelExport(moduleID, exportName, infos) - - id -> infos.head - } - } - } - /** Reach additional class data based on reflection methods being used. */ private def reachDataThroughReflection( classInfos: scala.collection.Map[ClassName, ClassInfo]): Unit = { @@ -914,13 +896,27 @@ private final class Analyzer(config: CommonPhaseConfig, override def toString(): String = className.nameString - def reachStaticInitializer(): Unit = { + def reachEntryPoints(): Unit = { implicit val from = FromExports + // Static initializer tryLookupStaticLikeMethod(MemberNamespace.StaticConstructor, StaticInitializerName).foreach { _.reachStatic()(fromAnalyzer) } + + // Top Level Exports + for (tle <- data.topLevelExports) { + val key = (tle.moduleID, tle.exportName) + val info = new TopLevelExportInfo(className, tle) + info.reach() + + _topLevelExportInfos.get(key).fold[Unit] { + _topLevelExportInfos.put(key, info) + } { other => + _errors += ConflictingTopLevelExport(tle.moduleID, tle.exportName, List(info, other)) + } + } } def accessModule()(implicit from: From): Unit = { @@ -981,7 +977,7 @@ private final class Analyzer(config: CommonPhaseConfig, validateLoadSpec(jsNativeLoadSpec, jsNativeMember = None) } - for (reachabilityInfo <- data.exportedMembers) + for (reachabilityInfo <- data.jsMethodProps) followReachabilityInfo(reachabilityInfo, staticDependencies, externalDependencies, dynamicDependencies)(FromExports) } @@ -1004,7 +1000,7 @@ private final class Analyzer(config: CommonPhaseConfig, // Reach exported members if (!isJSClass) { - for (reachabilityInfo <- data.exportedMembers) + for (reachabilityInfo <- data.jsMethodProps) followReachabilityInfo(reachabilityInfo, staticDependencies, externalDependencies, dynamicDependencies)(FromExports) } @@ -1218,8 +1214,8 @@ private final class Analyzer(config: CommonPhaseConfig, } } - private class TopLevelExportInfo(data: Infos.TopLevelExportInfo) extends Analysis.TopLevelExportInfo { - val owningClass: ClassName = data.owningClass + private class TopLevelExportInfo(val owningClass: ClassName, data: Infos.TopLevelExportInfo) + extends Analysis.TopLevelExportInfo { val moduleID: ModuleID = data.moduleID val exportName: String = data.exportName @@ -1442,9 +1438,6 @@ object Analyzer { trait InputProvider { def classesWithEntryPoints(): Iterable[ClassName] - def loadTopLevelExportInfos()( - implicit ec: ExecutionContext): Future[List[Infos.TopLevelExportInfo]] - def loadInfo(className: ClassName)( implicit ec: ExecutionContext): Option[Future[Infos.ClassInfo]] } 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 babb616f64..37367c4091 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 @@ -61,7 +61,8 @@ object Infos { val referencedFieldClasses: Map[FieldName, ClassName], val methods: List[MethodInfo], val jsNativeMembers: Map[MethodName, JSNativeLoadSpec], - val exportedMembers: List[ReachabilityInfo] + val jsMethodProps: List[ReachabilityInfo], + val topLevelExports: List[TopLevelExportInfo] ) { override def toString(): String = className.nameString } @@ -86,7 +87,6 @@ object Infos { } final class TopLevelExportInfo private[Infos] ( - val owningClass: ClassName, val reachability: ReachabilityInfo, val moduleID: ModuleID, val exportName: String @@ -134,7 +134,8 @@ object Infos { private val referencedFieldClasses = mutable.Map.empty[FieldName, ClassName] private val methods = mutable.ListBuffer.empty[MethodInfo] private val jsNativeMembers = mutable.Map.empty[MethodName, JSNativeLoadSpec] - private val exportedMembers = mutable.ListBuffer.empty[ReachabilityInfo] + private val jsMethodProps = mutable.ListBuffer.empty[ReachabilityInfo] + private val topLevelExports = mutable.ListBuffer.empty[TopLevelExportInfo] def maybeAddReferencedFieldClass(name: FieldName, tpe: Type): this.type = { tpe match { @@ -159,14 +160,20 @@ object Infos { } def addExportedMember(reachabilityInfo: ReachabilityInfo): this.type = { - exportedMembers += reachabilityInfo + jsMethodProps += reachabilityInfo + this + } + + def addTopLevelExport(topLevelExportInfo: TopLevelExportInfo): this.type = { + topLevelExports += topLevelExportInfo this } def result(): ClassInfo = { new ClassInfo(className, kind, superClass, interfaces, jsNativeLoadSpec, referencedFieldClasses.toMap, - methods.toList, jsNativeMembers.toMap, exportedMembers.toList) + methods.toList, jsNativeMembers.toMap, jsMethodProps.toList, + topLevelExports.toList) } } @@ -402,7 +409,7 @@ object Infos { classDef.superClass.map(_.name), classDef.interfaces.map(_.name), classDef.jsNativeLoadSpec) - classDef.memberDefs foreach { + classDef.fields foreach { case FieldDef(flags, FieldIdent(name), _, ftpe) => if (!flags.namespace.isStatic) { builder.maybeAddReferencedFieldClass(name, ftpe) @@ -410,39 +417,31 @@ object Infos { case _: JSFieldDef => // Nothing to do. + } - case methodDef: MethodDef => - builder.addMethod(generateMethodInfo(methodDef)) + classDef.methods.foreach { methodDef => + builder.addMethod(generateMethodInfo(methodDef)) + } - case ctorDef: JSConstructorDef => - builder.addExportedMember(generateJSConstructorInfo(ctorDef)) + classDef.jsConstructor.foreach { ctorDef => + builder.addExportedMember(generateJSConstructorInfo(ctorDef)) + } + classDef.jsMethodProps.foreach { case methodDef: JSMethodDef => builder.addExportedMember(generateJSMethodInfo(methodDef)) case propertyDef: JSPropertyDef => builder.addExportedMember(generateJSPropertyInfo(propertyDef)) - - case nativeMemberDef: JSNativeMemberDef => - builder.addJSNativeMember(nativeMemberDef) } - builder.result() - } + classDef.jsNativeMembers.foreach(builder.addJSNativeMember(_)) - def generateTopLevelExportInfos(classDef: ClassDef): List[TopLevelExportInfo] = { - classDef.topLevelExportDefs.map { topLevelExportDef => - generateTopLevelExportInfo(classDef.name.name, topLevelExportDef) + classDef.topLevelExportDefs.foreach { topLevelExportDef => + builder.addTopLevelExport(generateTopLevelExportInfo(classDef.name.name, topLevelExportDef)) } - } - def generateTopLevelExportInfos( - topLevelExports: List[LinkedTopLevelExport]): List[TopLevelExportInfo] = { - for { - topLevelExport <- topLevelExports - } yield { - Infos.generateTopLevelExportInfo(topLevelExport.owningClass, topLevelExport.tree) - } + builder.result() } /** Generates the [[MethodInfo]] of a @@ -474,7 +473,7 @@ object Infos { topLevelExportDef: TopLevelExportDef): TopLevelExportInfo = { val info = new GenInfoTraverser().generateTopLevelExportInfo(enclosingClass, topLevelExportDef) - new TopLevelExportInfo(enclosingClass, info, + new TopLevelExportInfo(info, ModuleID(topLevelExportDef.moduleID), topLevelExportDef.topLevelExportName) } 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 40cb4f2460..07d5044001 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 @@ -44,7 +44,7 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { import varGen._ def buildClass(tree: LinkedClass, useESClass: Boolean, ctor: js.Tree, - memberDefs: List[js.MethodDef], exportedDefs: js.Tree)( + memberDefs: List[js.MethodDef], exportedDefs: List[js.Tree])( implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge): WithGlobals[js.Tree] = { @@ -52,7 +52,7 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { val className = tree.name.name def allES6Defs = { - js.Block(ctor +: memberDefs :+ exportedDefs)(tree.pos) match { + js.Block(ctor +: (memberDefs ++ exportedDefs))(tree.pos) match { case js.Block(allDefs) => allDefs case js.Skip() => Nil case oneDef => List(oneDef) @@ -61,7 +61,7 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { def allES5Defs(classVar: js.Tree) = { WithGlobals(js.Block( - ctor, assignES5ClassMembers(classVar, memberDefs), exportedDefs)) + ctor, assignES5ClassMembers(classVar, memberDefs), js.Block(exportedDefs: _*))) } if (!tree.kind.isJSClass) { @@ -155,11 +155,11 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { /** Extracts the inlineable init method, if there is one. */ def extractInlineableInit(tree: LinkedClass)( - implicit globalKnowledge: GlobalKnowledge): (Option[Versioned[MethodDef]], List[Versioned[MethodDef]]) = { + implicit globalKnowledge: GlobalKnowledge): (Option[MethodDef], List[MethodDef]) = { if (globalKnowledge.hasInlineableInit(tree.className)) { val (constructors, otherMethods) = tree.methods.partition { m => - m.value.flags.namespace == MemberNamespace.Constructor + m.flags.namespace == MemberNamespace.Constructor } assert(constructors.size == 1, s"Found ${constructors.size} constructors in class " + @@ -385,7 +385,7 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { val JSConstructorDef(_, params, restParam, body) = tree.jsConstructorDef.getOrElse { throw new IllegalArgumentException( s"${tree.className} does not have an exported constructor") - }.value + } desugarToFunction(tree.className, params, restParam, body) } @@ -486,8 +486,8 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { globalKnowledge: GlobalKnowledge): List[js.Tree] = { implicit val pos = tree.pos val hasStaticInit = tree.methods.exists { m => - m.value.flags.namespace == MemberNamespace.StaticConstructor && - m.value.methodName.isStaticInitializer + m.flags.namespace == MemberNamespace.StaticConstructor && + m.methodName.isStaticInitializer } if (hasStaticInit) { val field = globalVar("sct", (tree.className, StaticInitializerName), @@ -514,8 +514,8 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { private def hasClassInitializer(tree: LinkedClass): Boolean = { tree.methods.exists { m => - m.value.flags.namespace == MemberNamespace.StaticConstructor && - m.value.methodName.isClassInitializer + m.flags.namespace == MemberNamespace.StaticConstructor && + m.methodName.isClassInitializer } } @@ -1066,20 +1066,13 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { createAccessor.map(js.Block(createModuleInstanceField, _)) } - def genExportedMembers(tree: LinkedClass, useESClass: Boolean)( + def genExportedMember(tree: LinkedClass, useESClass: Boolean, member: JSMethodPropDef)( implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge): WithGlobals[js.Tree] = { - val exportsWithGlobals = tree.exportedMembers map { member => - member.value match { - case m: JSMethodDef => - genJSMethod(tree, useESClass, m) - case p: JSPropertyDef => - genJSProperty(tree, useESClass, p) - } + member match { + case m: JSMethodDef => genJSMethod(tree, useESClass, m) + case p: JSPropertyDef => genJSProperty(tree, useESClass, p) } - - for (exports <- WithGlobals.list(exportsWithGlobals)) - yield js.Block(exports)(tree.pos) } def genTopLevelExports(topLevelExports: List[LinkedTopLevelExport])( 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 dbf601ad3a..7abc4b2396 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 @@ -661,6 +661,22 @@ private[emitter] object CoreJSLib { } ), + condTree(nullPointers != CheckedBehavior.Unchecked)(Block( + defineFunction0("throwNullPointerException") { + Throw(maybeWrapInUBE(nullPointers, { + genScalaClassNew(NullPointerExceptionClass, NoArgConstructorName) + })) + }, + + // "checkNotNull", but with a very short name + defineFunction1("n") { x => + Block( + If(x === Null(), genCallHelper("throwNullPointerException")), + Return(x) + ) + } + )), + defineFunction1("noIsInstance") { instance => Throw(New(TypeErrorRef, str("Cannot call isInstance() on a Class representing a JS trait/object") :: Nil)) @@ -754,7 +770,10 @@ private[emitter] object CoreJSLib { } ), { If(instance === Null(), { - Return(Apply(instance DOT genName(getClassMethodName), Nil)) + if (nullPointers == CheckedBehavior.Unchecked) + Return(Apply(instance DOT genName(getClassMethodName), Nil)) + else + genCallHelper("throwNullPointerException") }, { If(genIsInstanceOfHijackedClass(instance, BoxedLongClass), { Return(constantClassResult(BoxedLongClass)) @@ -798,7 +817,12 @@ private[emitter] object CoreJSLib { semantics.runtimeClassNameMapper, className.nameString)) }, instance => genIdentBracketSelect(instance DOT classData, "name"), - Apply(Null() DOT genName(getNameMethodName), Nil) + { + if (nullPointers == CheckedBehavior.Unchecked) + Apply(Null() DOT genName(getNameMethodName), Nil) + else + genCallHelper("throwNullPointerException") + } ) ) } @@ -1119,6 +1143,11 @@ private[emitter] object CoreJSLib { genCallHelper("arraycopyGeneric", src.u, srcPos, dest.u, destPos, length) } ), + condTree(esVersion >= ESVersion.ES2015 && nullPointers != CheckedBehavior.Unchecked)( + defineFunction5("systemArraycopy") { (src, srcPos, dest, destPos, length) => + Apply(src DOT "copyTo", List(srcPos, dest, destPos, length)) + } + ), condTree(arrayStores != CheckedBehavior.Unchecked)(Block( defineFunction5("systemArraycopyRefs") { (src, srcPos, dest, destPos, length) => 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 e07f8300f5..b535edd5de 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 @@ -16,7 +16,7 @@ import scala.annotation.tailrec import scala.collection.mutable -import org.scalajs.ir.{ClassKind, Position} +import org.scalajs.ir.{ClassKind, Position, Version} import org.scalajs.ir.Names._ import org.scalajs.ir.OriginalName.NoOriginalName import org.scalajs.ir.Trees.{JSNativeLoadSpec, MemberNamespace} @@ -377,8 +377,7 @@ final class Emitter(config: Emitter.Config) { } // Static-like methods - for (m <- linkedMethods) { - val methodDef = m.value + for (methodDef <- linkedMethods) { val namespace = methodDef.flags.namespace val emitAsStaticLike = { @@ -391,8 +390,8 @@ final class Emitter(config: Emitter.Config) { val methodCache = classCache.getStaticLikeMethodCache(namespace, methodDef.methodName) - addToMain(methodCache.getOrElseUpdate(m.version, - classEmitter.genStaticLikeMethod(className, m.value)(moduleContext, methodCache))) + addToMain(methodCache.getOrElseUpdate(methodDef.version, + classEmitter.genStaticLikeMethod(className, methodDef)(moduleContext, methodCache))) } } @@ -427,16 +426,24 @@ final class Emitter(config: Emitter.Config) { val ctorWithGlobals = { /* The constructor depends both on the class version, and the version * of the inlineable init, if there is one. + * + * If it is a JS class, it depends on the jsConstructorDef. */ val ctorCache = classCache.getConstructorCache() - val ctorVersion = linkedInlineableInit.fold[Option[String]] { - linkedClass.version.map("1-" + _) - } { linkedInit => - mergeVersions(linkedClass.version, linkedInit.version).map("2-" + _) + val ctorVersion = { + if (linkedClass.kind.isJSClass) { + assert(linkedInlineableInit.isEmpty) + Version.combine(linkedClass.version, linkedClass.jsConstructorDef.get.version) + } else { + linkedInlineableInit.fold { + Version.combine(linkedClass.version) + } { linkedInit => + Version.combine(linkedClass.version, linkedInit.version) + } + } } - val initToInline = linkedInlineableInit.map(_.value) ctorCache.getOrElseUpdate(ctorVersion, - classEmitter.genConstructor(linkedClass, useESClass, initToInline)(moduleContext, ctorCache)) + classEmitter.genConstructor(linkedClass, useESClass, linkedInlineableInit)(moduleContext, ctorCache)) } /* Bridges from Throwable to methods of Object, which are necessary @@ -445,19 +452,17 @@ final class Emitter(config: Emitter.Config) { */ val linkedMethodsAndBridges = if (ClassEmitter.shouldExtendJSError(linkedClass)) { val existingMethods = linkedMethods - .withFilter(_.value.flags.namespace == MemberNamespace.Public) - .map(_.value.methodName) + .withFilter(_.flags.namespace == MemberNamespace.Public) + .map(_.methodName) .toSet val bridges = for { - m <- uncachedKnowledge.methodsInObject() - methodName = m.value.methodName - if !existingMethods.contains(methodName) + methodDef <- uncachedKnowledge.methodsInObject() + if !existingMethods.contains(methodDef.methodName) } yield { import org.scalajs.ir.Trees._ import org.scalajs.ir.Types._ - val methodDef = m.value implicit val pos = methodDef.pos val methodName = methodDef.name @@ -465,11 +470,10 @@ final class Emitter(config: Emitter.Config) { This()(ClassType(className)), ObjectClass, methodName, methodDef.args.map(_.ref))( methodDef.resultType) - val newMethodDef = MethodDef(MemberFlags.empty, methodName, + MethodDef(MemberFlags.empty, methodName, methodDef.originalName, methodDef.args, methodDef.resultType, Some(newBody))( - OptimizerHints.empty, None) - new Versioned(newMethodDef, m.version) + OptimizerHints.empty, methodDef.version) } linkedMethods ++ bridges @@ -479,24 +483,30 @@ final class Emitter(config: Emitter.Config) { // Normal methods val memberMethodsWithGlobals = for { - m <- linkedMethodsAndBridges - if m.value.flags.namespace == MemberNamespace.Public + method <- linkedMethodsAndBridges + if method.flags.namespace == MemberNamespace.Public } yield { val methodCache = - classCache.getMemberMethodCache(m.value.methodName) + classCache.getMemberMethodCache(method.methodName) - methodCache.getOrElseUpdate(m.version, - classEmitter.genMemberMethod(className, m.value)(moduleContext, methodCache)) + methodCache.getOrElseUpdate(method.version, + classEmitter.genMemberMethod(className, method)(moduleContext, methodCache)) } // Exported Members - val exportedMembersWithGlobals = classTreeCache.exportedMembers.getOrElseUpdate( - classEmitter.genExportedMembers(linkedClass, useESClass)(moduleContext, classCache)) + val exportedMembersWithGlobals = for { + (member, idx) <- linkedClass.exportedMembers.zipWithIndex + } yield { + val memberCache = classCache.getExportedMemberCache(idx) + val version = Version.combine(linkedClass.version, member.version) + memberCache.getOrElseUpdate(version, + classEmitter.genExportedMember(linkedClass, useESClass, member)(moduleContext, memberCache)) + } val fullClass = for { ctor <- ctorWithGlobals memberMethods <- WithGlobals.list(memberMethodsWithGlobals) - exportedMembers <- exportedMembersWithGlobals + exportedMembers <- WithGlobals.list(exportedMembersWithGlobals) clazz <- classEmitter.buildClass(linkedClass, useESClass, ctor, memberMethods, exportedMembers)(moduleContext, classCache) } yield { @@ -565,18 +575,11 @@ final class Emitter(config: Emitter.Config) { ) } - // Helpers - - private def mergeVersions(v1: Option[String], - v2: Option[String]): Option[String] = { - v1.flatMap(s1 => v2.map(s2 => "" + s1.length + "-" + s1 + s2)) - } - // Caching private final class ClassCache extends knowledgeGuardian.KnowledgeAccessor { private[this] var _cache: DesugaredClassCache = null - private[this] var _lastVersion: Option[String] = None + private[this] var _lastVersion: Version = Version.Unversioned private[this] var _cacheUsed = false private[this] val _methodCaches = @@ -587,13 +590,16 @@ final class Emitter(config: Emitter.Config) { private[this] var _constructorCache: Option[MethodCache[js.Tree]] = None + private[this] val _exportedMembersCache = + mutable.Map.empty[Int, MethodCache[js.Tree]] + override def invalidate(): Unit = { /* Do not invalidate contained methods, as they have their own * invalidation logic. */ super.invalidate() _cache = null - _lastVersion = None + _lastVersion = Version.Unversioned } def startRun(): Unit = { @@ -603,8 +609,8 @@ final class Emitter(config: Emitter.Config) { _constructorCache.foreach(_.startRun()) } - def getCache(version: Option[String]): DesugaredClassCache = { - if (_cache == null || _lastVersion.isEmpty || _lastVersion != version) { + def getCache(version: Version): DesugaredClassCache = { + if (_cache == null || !_lastVersion.sameVersion(version)) { invalidate() statsClassesInvalidated += 1 _lastVersion = version @@ -635,6 +641,9 @@ final class Emitter(config: Emitter.Config) { } } + def getExportedMemberCache(idx: Int): MethodCache[js.Tree] = + _exportedMembersCache.getOrElseUpdate(idx, new MethodCache) + def cleanAfterRun(): Boolean = { _methodCaches.foreach(_.filterInPlace((_, c) => c.cleanAfterRun())) _memberMethodCache.filterInPlace((_, c) => c.cleanAfterRun()) @@ -642,6 +651,8 @@ final class Emitter(config: Emitter.Config) { if (_constructorCache.exists(!_.cleanAfterRun())) _constructorCache = None + _exportedMembersCache.filterInPlace((_, c) => c.cleanAfterRun()) + if (!_cacheUsed) invalidate() @@ -651,20 +662,20 @@ final class Emitter(config: Emitter.Config) { private final class MethodCache[T <: js.Tree] extends knowledgeGuardian.KnowledgeAccessor { private[this] var _tree: WithGlobals[T] = null - private[this] var _lastVersion: Option[String] = None + private[this] var _lastVersion: Version = Version.Unversioned private[this] var _cacheUsed = false override def invalidate(): Unit = { super.invalidate() _tree = null - _lastVersion = None + _lastVersion = Version.Unversioned } def startRun(): Unit = _cacheUsed = false - def getOrElseUpdate(version: Option[String], + def getOrElseUpdate(version: Version, v: => WithGlobals[T]): WithGlobals[T] = { - if (_tree == null || _lastVersion.isEmpty || _lastVersion != version) { + if (_tree == null || !_lastVersion.sameVersion(version)) { invalidate() statsMethodsInvalidated += 1 _tree = v @@ -780,7 +791,6 @@ object Emitter { private final class DesugaredClassCache { val privateJSFields = new OneTimeCache[WithGlobals[List[js.Tree]]] - val exportedMembers = new OneTimeCache[WithGlobals[js.Tree]] val instanceTests = new OneTimeCache[WithGlobals[js.Tree]] val typeData = new OneTimeCache[WithGlobals[js.Tree]] val setTypeData = new OneTimeCache[js.Tree] @@ -841,13 +851,17 @@ object Emitter { NoArgConstructorName) }, + cond(nullPointers != Unchecked) { + instantiateClass(NullPointerExceptionClass, NoArgConstructorName) + }, + cond(stringIndexOutOfBounds != Unchecked) { instantiateClass(StringIndexOutOfBoundsExceptionClass, IntArgConstructorName) }, cond(isAnyFatal(asInstanceOfs, arrayIndexOutOfBounds, arrayStores, - negativeArraySizes, stringIndexOutOfBounds)) { + negativeArraySizes, nullPointers, stringIndexOutOfBounds)) { instantiateClass(UndefinedBehaviorErrorClass, ThrowableArgConsructorName) }, 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 0f6d9b9beb..145eb89285 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 @@ -604,7 +604,7 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { case Assign(lhs, rhs) => lhs match { case Select(qualifier, className, field) => - unnest(qualifier, rhs) { (newQualifier, newRhs, env0) => + unnest(checkNotNull(qualifier), rhs) { (newQualifier, newRhs, env0) => implicit val env = env0 js.Assign( genSelect(transformExprNoChar(newQualifier), className, field)(lhs.pos), @@ -612,7 +612,7 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { } case ArraySelect(array, index) => - unnest(array, index, rhs) { (newArray, newIndex, newRhs, env0) => + unnest(checkNotNull(array), index, rhs) { (newArray, newIndex, newRhs, env0) => implicit val env = env0 val genArray = transformExprNoChar(newArray) val genIndex = transformExprNoChar(newIndex) @@ -741,37 +741,6 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { }, optLabel) } - case DoWhile(body, cond) => - val loopEnv = env.withInLoopForVarCapture(true) - - /* We cannot simply unnest(cond) here, because that would eject the - * evaluation of the condition out of the loop. - */ - val bodyEnv = loopEnv - .withDefaultBreakTargets(tailPosLabels) - .withDefaultContinueTargets(Set.empty) - val newBody = transformStat(body, Set.empty)(bodyEnv) - if (isExpression(cond)) { - /* Here, we could do the same optimization with `continue` as in - * `While` loops (see above), but no Scala source code produces - * patterns where this happens. Therefore, we do not bother. - */ - js.DoWhile(newBody, transformExprNoChar(cond)(loopEnv)) - } else { - /* Since in this rewriting, the old body is not in tail position of - * the emitted do..while body, we cannot optimize an inner Labeled - * block into using `continue` statements. - */ - js.While(js.BooleanLiteral(true), { - js.Block( - newBody, - unnest(cond) { (newCond, env0) => - implicit val env = env0 - js.If(transformExprNoChar(newCond), js.Skip(), js.Break()) - } (loopEnv)) - }) - } - case ForIn(obj, keyVar, keyVarOriginalName, body) => unnest(obj) { (newObj, env0) => implicit val env = env0 @@ -1112,11 +1081,15 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { case RecordSelect(record, field) if noExtractYet => RecordSelect(rec(record), field)(arg.tpe) + case Transient(AssumeNotNull(obj)) => + Transient(AssumeNotNull(rec(obj))) case Transient(ZeroOf(runtimeClass)) => Transient(ZeroOf(rec(runtimeClass))) case Transient(ObjectClassName(obj)) => Transient(ObjectClassName(rec(obj))) + case Transient(CheckNotNull(obj)) if noExtractYet => + Transient(CheckNotNull(rec(obj))) case Transient(NativeArrayWrapper(elemClass, nativeArray)) if noExtractYet => val newNativeArray = rec(nativeArray) val newElemClass = rec(elemClass) @@ -1256,6 +1229,11 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { case tree: Tree => test(tree) } + def testNPE(tree: Tree): Boolean = { + val npeOK = allowBehavior(semantics.nullPointers) || isNotNull(tree) + npeOK && test(tree) + } + def test(tree: Tree): Boolean = tree match { // Atomic expressions case _: Literal => true @@ -1285,36 +1263,38 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { case BinaryOp(BinaryOp.String_charAt, lhs, rhs) => allowBehavior(semantics.stringIndexOutOfBounds) && test(lhs) && test(rhs) - // Expressions preserving pureness + // Expressions preserving pureness (modulo NPE) case Block(trees) => trees forall test case If(cond, thenp, elsep) => test(cond) && test(thenp) && test(elsep) case BinaryOp(_, lhs, rhs) => test(lhs) && test(rhs) case UnaryOp(_, lhs) => test(lhs) - case ArrayLength(array) => test(array) + case ArrayLength(array) => testNPE(array) case RecordSelect(record, _) => test(record) case IsInstanceOf(expr, _) => test(expr) case IdentityHashCode(expr) => test(expr) - case GetClass(arg) => test(arg) // may NPE but that is UB. + case GetClass(arg) => testNPE(arg) - // Expressions preserving pureness but requiring that expr be a var + // Expressions preserving pureness (modulo NPE) but requiring that expr be a var case WrapAsThrowable(expr @ (VarRef(_) | Transient(JSVarRef(_, _)))) => test(expr) - case UnwrapFromThrowable(expr @ (VarRef(_) | Transient(JSVarRef(_, _)))) => test(expr) + case UnwrapFromThrowable(expr @ (VarRef(_) | Transient(JSVarRef(_, _)))) => testNPE(expr) - // Transients preserving pureness + // Transients preserving pureness (modulo NPE) + case Transient(AssumeNotNull(obj)) => + test(obj) case Transient(ZeroOf(runtimeClass)) => - test(runtimeClass) // may NPE but that is UB. + test(runtimeClass) // ZeroOf *assumes* that `runtimeClass ne null` case Transient(ObjectClassName(obj)) => - test(obj) // may NPE but that is UB. + testNPE(obj) - // Expressions preserving side-effect freedom + // Expressions preserving side-effect freedom (modulo NPE) case Select(qualifier, _, _) => - allowUnpure && test(qualifier) // may NPE but that is UB. + allowUnpure && testNPE(qualifier) case SelectStatic(_, _) => allowUnpure case ArrayValue(tpe, elems) => allowUnpure && (elems forall test) case Clone(arg) => - allowUnpure && test(arg) // may NPE but that is UB. + allowUnpure && testNPE(arg) case JSArrayConstr(items) => allowUnpure && (items.forall(testJSArg)) case tree @ JSObjectConstr(items) => @@ -1326,11 +1306,11 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { case Closure(arrow, captureParams, params, restParam, body, captureValues) => allowUnpure && (captureValues forall test) - // Transients preserving side-effect freedom + // Transients preserving side-effect freedom (modulo NPE) case Transient(NativeArrayWrapper(elemClass, nativeArray)) => - allowUnpure && test(elemClass) && test(nativeArray) // may NPE but that is UB. + allowUnpure && testNPE(elemClass) && test(nativeArray) case Transient(ArrayToTypedArray(expr, primRef)) => - allowUnpure && test(expr) // may NPE but that is UB. + allowUnpure && testNPE(expr) // Scala expressions that can always have side-effects case New(className, constr, args) => @@ -1347,6 +1327,8 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { allowSideEffects && args.forall(test) // Transients with side effects. + case Transient(CheckNotNull(obj)) => + allowSideEffects && test(obj) case Transient(TypedArrayToArray(expr, primRef)) => allowSideEffects && test(expr) // may TypeError @@ -1354,7 +1336,7 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { case NewArray(tpe, lengths) => allowBehavior(semantics.negativeArraySizes) && allowUnpure && lengths.forall(test) case ArraySelect(array, index) => - allowBehavior(semantics.arrayIndexOutOfBounds) && allowUnpure && test(array) && test(index) + allowBehavior(semantics.arrayIndexOutOfBounds) && allowUnpure && testNPE(array) && test(index) // Casts case AsInstanceOf(expr, _) => @@ -1735,12 +1717,12 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { } case Apply(flags, receiver, method, args) => - unnest(receiver, args) { (newReceiver, newArgs, env) => + unnest(checkNotNull(receiver), args) { (newReceiver, newArgs, env) => redo(Apply(flags, newReceiver, method, newArgs)(rhs.tpe))(env) } case ApplyStatically(flags, receiver, className, method, args) => - unnest(receiver, args) { (newReceiver, newArgs, env) => + unnest(checkNotNull(receiver), args) { (newReceiver, newArgs, env) => redo(ApplyStatically(flags, newReceiver, className, method, newArgs)(rhs.tpe))(env) } @@ -1781,7 +1763,7 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { } case ArraySelect(array, index) => - unnest(array, index) { (newArray, newIndex, env) => + unnest(checkNotNull(array), index) { (newArray, newIndex, env) => redo(ArraySelect(newArray, newIndex)(rhs.tpe))(env) } @@ -1831,6 +1813,16 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { } } + case Transient(CheckNotNull(obj)) => + unnest(obj) { (newObj, env) => + redo(Transient(CheckNotNull(newObj)))(env) + } + + case Transient(AssumeNotNull(obj)) => + unnest(obj) { (newObj, env) => + redo(Transient(AssumeNotNull(newObj)))(env) + } + case Transient(ZeroOf(runtimeClass)) => unnest(runtimeClass) { (newRuntimeClass, env) => redo(Transient(ZeroOf(newRuntimeClass)))(env) @@ -2025,7 +2017,7 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { * we use to "add" all the code of pushLhsInto() to transformStat(). */ rhs match { - case _:Skip | _:VarDef | _:Assign | _:While | _:DoWhile | + case _:Skip | _:VarDef | _:Assign | _:While | _:Debugger | _:JSSuperConstructorCall | _:JSDelete | _:StoreModule | Transient(_:SystemArrayCopy) => transformStat(rhs, tailPosLabels) @@ -2214,7 +2206,7 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { genLoadModule(className) case Select(qualifier, className, field) => - genSelect(transformExprNoChar(qualifier), className, field) + genSelect(transformExprNoChar(checkNotNull(qualifier)), className, field) case SelectStatic(className, item) => globalVar("t", (className, item.name)) @@ -2238,14 +2230,14 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { if (receiver.tpe == CharType) transformExpr(receiver, preserveChar = true) else - transformExpr(AsInstanceOf(receiver, CharType), preserveChar = true) + transformExpr(AsInstanceOf(checkNotNull(receiver), CharType), preserveChar = true) } else { /* For other primitive types, unboxes/casts are not necessary, * because they would only convert `null` to the zero value of - * the type. However, calling a method on `null` is UB, so we - * need not do anything about it. + * the type. However, `null` is ruled out by `checkNotNull` (or + * because it is UB). */ - transformExprNoChar(receiver) + transformExprNoChar(checkNotNull(receiver)) } } @@ -2303,7 +2295,7 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { } case ApplyStatically(flags, receiver, className, method, args) => - val newReceiver = transformExprNoChar(receiver) + val newReceiver = transformExprNoChar(checkNotNull(receiver)) val newArgs = transformTypedArgs(method.name, args) val transformedArgs = newReceiver :: newArgs @@ -2657,11 +2649,11 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { genArrayValue(typeRef, elems.map(transformExpr(_, preserveChar)))) case ArrayLength(array) => - genIdentBracketSelect(js.DotSelect(transformExprNoChar(array), + genIdentBracketSelect(js.DotSelect(transformExprNoChar(checkNotNull(array)), js.Ident("u")), "length") case ArraySelect(array, index) => - val newArray = transformExprNoChar(array) + val newArray = transformExprNoChar(checkNotNull(array)) val newIndex = transformExprNoChar(index) semantics.arrayIndexOutOfBounds match { case CheckedBehavior.Compliant | CheckedBehavior.Fatal => @@ -2683,7 +2675,7 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { genCallHelper("objectGetClass", transformExprNoChar(expr)) case Clone(expr) => - val newExpr = transformExprNoChar(expr) + val newExpr = transformExprNoChar(checkNotNull(expr)) expr.tpe match { /* If the argument is known to be an array, directly call its * `clone__O` method. @@ -2730,13 +2722,18 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { js.If( genIsInstanceOfClass(newExpr, JavaScriptExceptionClass), genSelect(newExpr, JavaScriptExceptionClass, FieldIdent(exceptionFieldName)), - newExpr) + genCheckNotNull(newExpr)) // Transients + case Transient(CheckNotNull(obj)) => + genCallHelper("n", transformExpr(obj, preserveChar = true)) + case Transient(AssumeNotNull(obj)) => + transformExpr(obj, preserveChar = true) + case Transient(ZeroOf(runtimeClass)) => js.DotSelect( - js.DotSelect(transformExprNoChar(runtimeClass), js.Ident("jl_Class__f_data")), + js.DotSelect(transformExprNoChar(checkNotNull(runtimeClass)), js.Ident("jl_Class__f_data")), js.Ident("zero")) case Transient(NativeArrayWrapper(elemClass, nativeArray)) => @@ -2748,7 +2745,7 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { genNativeArrayWrapper(arrayTypeRef, newNativeArray)) case _ => val elemClassData = js.DotSelect( - transformExprNoChar(elemClass), + transformExprNoChar(checkNotNull(elemClass)), js.Ident("jl_Class__f_data")) val arrayClassData = js.Apply( js.DotSelect(elemClassData, js.Ident("getArrayOf")), Nil) @@ -2759,7 +2756,7 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { genCallHelper("objectClassName", transformExprNoChar(obj)) case Transient(ArrayToTypedArray(expr, primRef)) => - val value = transformExprNoChar(expr) + val value = transformExprNoChar(checkNotNull(expr)) if (es2015) { js.Apply(genIdentBracketSelect(value.u, "slice"), Nil) @@ -3166,6 +3163,36 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { MethodName("notifyAll", Nil, VoidRef) ) + private def checkNotNull(tree: Tree)(implicit pos: Position): Tree = { + if (semantics.nullPointers == CheckedBehavior.Unchecked || isNotNull(tree)) + tree + else + Transient(CheckNotNull(tree)) + } + + private def isNotNull(tree: Tree): Boolean = { + // !!! Duplicate code with OptimizerCore.isNotNull + + def isNullableType(tpe: Type): Boolean = tpe match { + case NullType => true + case _: PrimType => false + case _ => true + } + + def isShapeNotNull(tree: Tree): Boolean = tree match { + case Transient(CheckNotNull(_) | AssumeNotNull(_)) => + true + case _: This => + tree.tpe != AnyType + case _:New | _:LoadModule | _:NewArray | _:ArrayValue | _:Clone | _:ClassOf => + true + case _ => + false + } + + !isNullableType(tree.tpe) || isShapeNotNull(tree) + } + private def transformParamDef(paramDef: ParamDef): js.ParamDef = js.ParamDef(transformLocalVarIdent(paramDef.name, paramDef.originalName))(paramDef.pos) diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/GlobalKnowledge.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/GlobalKnowledge.scala index 768be75675..a8e211c986 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/GlobalKnowledge.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/GlobalKnowledge.scala @@ -16,7 +16,6 @@ import org.scalajs.ir.Names._ import org.scalajs.ir.Trees.{AnyFieldDef, MethodDef, JSNativeLoadSpec} import org.scalajs.ir.Types.Type -import org.scalajs.linker.standard.Versioned import org.scalajs.linker.standard.ModuleSet.ModuleID private[emitter] trait GlobalKnowledge { @@ -105,7 +104,7 @@ private[emitter] trait GlobalKnowledge { def methodsInRepresentativeClasses(): List[(MethodName, Set[ClassName])] /** The public (non-static) methods of java.lang.Object. */ - def methodsInObject(): List[Versioned[MethodDef]] + def methodsInObject(): List[MethodDef] /** Hijacked classes that are strict descendants of `className`. */ def hijackedDescendants(className: ClassName): Set[ClassName] diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/KnowledgeGuardian.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/KnowledgeGuardian.scala index b5236d3735..18bfea85b3 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/KnowledgeGuardian.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/KnowledgeGuardian.scala @@ -127,7 +127,7 @@ private[emitter] final class KnowledgeGuardian(config: Emitter.Config) { */ !classesWithInstantiatedSubclasses(classDef.className) && { classDef.methods.count( - x => x.value.flags.namespace == MemberNamespace.Constructor) == 1 + x => x.flags.namespace == MemberNamespace.Constructor) == 1 } } @@ -213,7 +213,7 @@ private[emitter] final class KnowledgeGuardian(config: Emitter.Config) { def methodsInRepresentativeClasses(): List[(MethodName, Set[ClassName])] = specialInfo.askMethodsInRepresentativeClasses(this) - def methodsInObject(): List[Versioned[MethodDef]] = + def methodsInObject(): List[MethodDef] = specialInfo.askMethodsInObject(this) def hijackedDescendants(className: ClassName): Set[ClassName] = @@ -541,8 +541,8 @@ private[emitter] final class KnowledgeGuardian(config: Emitter.Config) { private def computeIsParentDataAccessed(classClass: Option[LinkedClass]): Boolean = { def methodExists(linkedClass: LinkedClass, methodName: MethodName): Boolean = { linkedClass.methods.exists { m => - m.value.flags.namespace == MemberNamespace.Public && - m.value.methodName == methodName + m.flags.namespace == MemberNamespace.Public && + m.methodName == methodName } } @@ -559,18 +559,18 @@ private[emitter] final class KnowledgeGuardian(config: Emitter.Config) { for { representativeClass <- representativeClasses method <- representativeClass.methods - if method.value.flags.namespace == MemberNamespace.Public + if method.flags.namespace == MemberNamespace.Public } { - result.getOrElseUpdate(method.value.methodName, mutable.Set.empty) += + result.getOrElseUpdate(method.methodName, mutable.Set.empty) += representativeClass.className } result.toList.sortBy(_._1.nameString).map(kv => (kv._1, kv._2.toSet)) } - private def computeMethodsInObject(objectClass: Option[LinkedClass]): List[Versioned[MethodDef]] = { + private def computeMethodsInObject(objectClass: Option[LinkedClass]): List[MethodDef] = { objectClass.toList.flatMap( - _.methods.filter(_.value.flags.namespace == MemberNamespace.Public)) + _.methods.filter(_.flags.namespace == MemberNamespace.Public)) } private def computeHijackedDescendants( @@ -606,7 +606,7 @@ private[emitter] final class KnowledgeGuardian(config: Emitter.Config) { methodsInRepresentativeClasses } - def askMethodsInObject(invalidatable: Invalidatable): List[Versioned[MethodDef]] = { + def askMethodsInObject(invalidatable: Invalidatable): List[MethodDef] = { invalidatable.registeredTo(this) methodsInObjectAskers += invalidatable methodsInObject 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 c2417f09f5..e36ee63582 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 @@ -150,7 +150,7 @@ private[emitter] final class SJSGen( assert(args.lengthCompare(5) == 0, s"wrong number of args for genUncheckedArrayCopy: $args") - if (esFeatures.esVersion >= ESVersion.ES2015) + if (esFeatures.esVersion >= ESVersion.ES2015 && semantics.nullPointers == CheckedBehavior.Unchecked) Apply(args.head DOT "copyTo", args.tail) else genCallHelper("systemArraycopy", args: _*) @@ -591,4 +591,13 @@ private[emitter] final class SJSGen( pos: Position): Tree = { genClassDataOf(ClassRef(className)) } + + def genCheckNotNull(obj: Tree)( + implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge, + pos: Position): Tree = { + if (semantics.nullPointers == CheckedBehavior.Unchecked) + obj + else + genCallHelper("n", obj) + } } diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Transients.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Transients.scala index 18d697b394..c5ee557807 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Transients.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Transients.scala @@ -21,6 +21,61 @@ import org.scalajs.ir.Types._ object Transients { + /** Checks that `obj ne null`, then returns `obj`. + * + * If `obj eq null`, throw a `NullPointerException`, or a corresponding + * `UndefinedBehaviorError`. + * + * This node must not be used when NPEs are Unchecked. + */ + final case class CheckNotNull(obj: Tree) extends Transient.Value { + val tpe: Type = if (obj.tpe == NullType) NothingType else obj.tpe + + def traverse(traverser: Traverser): Unit = + traverser.traverse(obj) + + def transform(transformer: Transformer, isStat: Boolean)( + implicit pos: Position): Tree = { + Transient(CheckNotNull(transformer.transformExpr(obj))) + } + + def printIR(out: IRTreePrinter): Unit = { + out.print("$n") + out.printArgs(List(obj)) + } + } + + /** Assumes that `obj ne null`, and always returns `obj`. + * + * This is used by the optimizer to communicate to the emitter that an + * expression is known not to be `null`, so that it doesn't insert useless + * `null` checks. + * + * This node should not be used when NPEs are Unchecked. + */ + final case class AssumeNotNull(obj: Tree) extends Transient.Value { + val tpe: Type = obj.tpe + + def traverse(traverser: Traverser): Unit = + traverser.traverse(obj) + + def transform(transformer: Transformer, isStat: Boolean)( + implicit pos: Position): Tree = { + Transient(CheckNotNull(transformer.transformExpr(obj))) + } + + def printIR(out: IRTreePrinter): Unit = { + out.print(obj) + out.print("!") + } + } + + /** Intrinsic for `System.arraycopy`. + * + * This node *assumes* that `src` and `dest` are non-null. It is the + * responsibility of whoever creates a `SystemArrayCopy` to wrap those + * parameters with `CheckNotNull`s if necessary. + */ final case class SystemArrayCopy(src: Tree, srcPos: Tree, dest: Tree, destPos: Tree, length: Tree) extends Transient.Value { @@ -48,6 +103,12 @@ object Transients { } } + /** Intrinsic for the private method `ArrayBuilder.generic.zeroOf`. + * + * This node *assumes* that `runtimeClass` is non-null. It is the + * responsibility of whoever creates a `ZeroOf` to wrap that parameter + * with `CheckNotNull`s if necessary. + */ final case class ZeroOf(runtimeClass: Tree) extends Transient.Value { /* The concrete value of ZeroOf will of course have a more concrete type. * However, if we knew this type, we could simply emit a plain literal. @@ -68,6 +129,12 @@ object Transients { } } + /** Intrinsic for the private method `ArrayBuilder.generic.genericArrayBuilderResult`. + * + * This node *assumes* that `elemClass` is non-null. It is the + * responsibility of whoever creates a `NativeArrayWrapper` to wrap that + * parameter with `CheckNotNull`s if necessary. + */ final case class NativeArrayWrapper(elemClass: Tree, nativeArray: Tree)(val tpe: Type) extends Transient.Value { @@ -88,6 +155,12 @@ object Transients { } } + /** Intrinsic for `obj.getClass().getName()`. + * + * This node accepts any value for `obj`, including `null`. Its + * implementation takes care of throwing `NullPointerException`s as + * required. + */ final case class ObjectClassName(obj: Tree) extends Transient.Value { val tpe: Type = StringType @@ -105,6 +178,11 @@ object Transients { } } + /** Copies a primitive `Array` into a new appropriate `TypedArray`. + * + * This node accepts `null` values for `expr`. Its implementation takes care + * of throwing `NullPointerException`s as required. + */ final case class ArrayToTypedArray(expr: Tree, primRef: PrimRef) extends Transient.Value { val tpe: Type = AnyType @@ -124,7 +202,13 @@ object Transients { } } - + /** Copies a `TypedArray` into a new `Array` of the specified type. + * + * Invalid values of `expr` will cause `TypeError`s or other JavaScript + * exceptions, in an implementation-dependent way. It does not protect + * itself against values forged to look like typed arrays without being + * actual typed arrays. + */ final case class TypedArrayToArray(expr: Tree, primRef: PrimRef) extends Transient.Value { val tpe: Type = ArrayType(ArrayTypeRef.of(primRef)) diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/javascript/Trees.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/javascript/Trees.scala index ee7a6df2a6..dfc28ccbd0 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/javascript/Trees.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/javascript/Trees.scala @@ -21,7 +21,7 @@ import org.scalajs.ir.Position.NoPosition object Trees { /* The case classes for JS Trees are sealed instead of final because making - * them final triggers bugs with Scala 2.11.x and 2.12.{1-4}, in combination + * them final triggers bugs with 2.12.{1-4}, in combination * with their `implicit val pos`. */ diff --git a/linker/shared/src/main/scala/org/scalajs/linker/checker/ClassDefChecker.scala b/linker/shared/src/main/scala/org/scalajs/linker/checker/ClassDefChecker.scala index 3ebfd5f8e1..fb5ebc1a61 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/checker/ClassDefChecker.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/checker/ClassDefChecker.scala @@ -87,14 +87,14 @@ private final class ClassDefChecker(classDef: ClassDef, reporter: ErrorReporter) /* These checks also populate the lookup maps on the instance * (fields, methods, jsNativeMembers). */ - classDef.memberDefs.foreach { - case fieldDef: AnyFieldDef => checkFieldDef(fieldDef) - case methodDef: MethodDef => checkMethodDef(methodDef) - case jsCtorDef: JSConstructorDef => checkJSConstructorDef(jsCtorDef) + classDef.fields.foreach(checkFieldDef(_)) + classDef.methods.foreach(checkMethodDef(_)) + classDef.jsConstructor.foreach(checkJSConstructorDef(_)) + classDef.jsMethodProps.foreach { case jsMethodDef: JSMethodDef => checkJSMethodDef(jsMethodDef) case jsPropertyDef: JSPropertyDef => checkJSPropertyDef(jsPropertyDef) - case jsNativeMemberDef: JSNativeMemberDef => checkJSNativeMemberDef(jsNativeMemberDef) } + classDef.jsNativeMembers.foreach(checkJSNativeMemberDef(_)) // top level exports need the lookup maps to be populated. classDef.topLevelExportDefs.foreach(checkTopLevelExportDef(_)) @@ -105,8 +105,8 @@ private final class ClassDefChecker(classDef: ClassDef, reporter: ErrorReporter) methods(MemberNamespace.Constructor.ordinal).size != 1) reportError("Module class must have exactly 1 constructor") - if (classDef.kind.isJSClass && classDef.memberDefs.count(_.isInstanceOf[JSConstructorDef]) != 1) - reportError("JS classes and module classes must have exactly 1 constructor") + if (classDef.kind.isJSClass && classDef.jsConstructor.isEmpty) + reportError("JS classes and module classes must have a constructor") } private def checkKind()(implicit ctx: ErrorContext): Unit = { @@ -231,6 +231,9 @@ private final class ClassDefChecker(classDef: ClassDef, reporter: ErrorReporter) if (!methods(namespace.ordinal).add(name)) reportError(i"duplicate ${namespace.prefixString}method '$name'") + if (body.isEmpty && namespace != MemberNamespace.Public) + reportError("Abstract methods may only be in the public namespace") + // ClassInitializer if (name.isClassInitializer) { if (!classDef.kind.isJSClass) { @@ -551,10 +554,6 @@ private final class ClassDefChecker(classDef: ClassDef, reporter: ErrorReporter) checkTree(cond, env) checkTree(body, env) - case DoWhile(body, cond) => - checkTree(body, env) - checkTree(cond, env) - case ForIn(obj, keyVar, _, body) => checkTree(obj, env) checkDeclareLocalVar(keyVar) diff --git a/linker/shared/src/main/scala/org/scalajs/linker/checker/IRChecker.scala b/linker/shared/src/main/scala/org/scalajs/linker/checker/IRChecker.scala index e2a1f8a88f..2801ec93d4 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/checker/IRChecker.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/checker/IRChecker.scala @@ -53,22 +53,15 @@ private final class IRChecker(unit: LinkingUnit, reporter: ErrorReporter) { case JSFieldDef(_, name, _) => typecheckExpr(name, Env.empty) } - classDef.methods.foreach { versioned => - checkMethodDef(versioned.value, classDef) - } - - classDef.jsConstructorDef.foreach { versioned => - checkJSConstructorDef(versioned.value, classDef) - } + classDef.methods.foreach(checkMethodDef(_, classDef)) + classDef.jsConstructorDef.foreach(checkJSConstructorDef(_, classDef)) - classDef.exportedMembers.foreach { versioned => - versioned.value match { - case jsMethodDef: JSMethodDef => - checkJSMethodDef(jsMethodDef, classDef) + classDef.exportedMembers.foreach { + case jsMethodDef: JSMethodDef => + checkJSMethodDef(jsMethodDef, classDef) - case jsPropertyDef: JSPropertyDef => - checkJSPropertyDef(jsPropertyDef, classDef) - } + case jsPropertyDef: JSPropertyDef => + checkJSPropertyDef(jsPropertyDef, classDef) } } @@ -280,10 +273,6 @@ private final class IRChecker(unit: LinkingUnit, reporter: ErrorReporter) { typecheckExpect(cond, env, BooleanType) typecheck(body, env) - case DoWhile(body, cond) => - typecheck(body, env) - typecheckExpect(cond, env, BooleanType) - case ForIn(obj, keyVar, _, body) => typecheckExpr(obj, env) typecheck(body, env) diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/BaseLinker.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/BaseLinker.scala index 4d260cb18e..c18c0ac2e9 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/BaseLinker.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/BaseLinker.scala @@ -26,7 +26,7 @@ import org.scalajs.linker.analyzer._ import org.scalajs.ir import org.scalajs.ir.Names.ClassName import org.scalajs.ir.Trees.{ClassDef, MethodDef} -import org.scalajs.ir.Hashers +import org.scalajs.ir.Version import Analysis._ @@ -107,25 +107,14 @@ final class BaseLinker(config: CommonPhaseConfig, checkIR: Boolean) { private def assemble(moduleInitializers: Seq[ModuleInitializer], analysis: Analysis)(implicit ec: ExecutionContext): Future[LinkingUnit] = { def assembleClass(info: ClassInfo) = { - val className = info.className - val classAndVersion = irLoader.loadClassDefAndVersion(className) + val classAndVersion = irLoader.loadClassDefAndVersion(info.className) val syntheticMethods = methodSynthesizer.synthesizeMembers(info, analysis) for { (classDef, version) <- classAndVersion syntheticMethods <- syntheticMethods } yield { - val linkedClass = linkedClassDef(classDef, version, syntheticMethods, info) - val linkedTopLevelExports = for { - topLevelExport <- classDef.topLevelExportDefs - } yield { - val infos = analysis.topLevelExportInfos( - (ModuleID(topLevelExport.moduleID), topLevelExport.topLevelExportName)) - new LinkedTopLevelExport(className, topLevelExport, - infos.staticDependencies.toSet, infos.externalDependencies.toSet) - } - - (linkedClass, linkedTopLevelExports) + BaseLinker.linkClassDef(classDef, version, syntheticMethods, analysis) } } @@ -139,73 +128,64 @@ final class BaseLinker(config: CommonPhaseConfig, checkIR: Boolean) { moduleInitializers.toList) } } +} + +private[frontend] object BaseLinker { /** Takes a ClassDef and DCE infos to construct a stripped down LinkedClass. */ - private def linkedClassDef(classDef: ClassDef, version: Option[String], - syntheticMethodDefs: Iterator[MethodDef], - analyzerInfo: ClassInfo): LinkedClass = { + private[frontend] def linkClassDef(classDef: ClassDef, version: Version, + syntheticMethodDefs: List[MethodDef], + analysis: Analysis): (LinkedClass, List[LinkedTopLevelExport]) = { import ir.Trees._ - val fields = List.newBuilder[AnyFieldDef] - val methods = List.newBuilder[Versioned[MethodDef]] - val jsNativeMembers = List.newBuilder[JSNativeMemberDef] - var jsConstructorDef: Option[Versioned[JSConstructorDef]] = None - val exportedMembers = List.newBuilder[Versioned[JSMethodPropDef]] + val classInfo = analysis.classInfos(classDef.className) - def linkedMethod(m: MethodDef) = { - val version = m.hash.map(Hashers.hashAsVersion(_)) - new Versioned(m, version) - } + val fields = classDef.fields.filter { + case field: FieldDef => + if (field.flags.namespace.isStatic) + classInfo.staticFieldsRead(field.name.name) || classInfo.staticFieldsWritten(field.name.name) + else if (classInfo.kind.isJSClass || classInfo.isAnySubclassInstantiated) + classInfo.fieldsRead(field.name.name) || classInfo.fieldsWritten(field.name.name) + else + false - classDef.memberDefs.foreach { - case field: AnyFieldDef => - if (isFieldDefNeeded(analyzerInfo, field)) - fields += field + case field: JSFieldDef => + classInfo.isAnySubclassInstantiated + } - case m: MethodDef => - val methodInfo = - analyzerInfo.methodInfos(m.flags.namespace)(m.methodName) + val methods = classDef.methods.filter { m => + val methodInfo = + classInfo.methodInfos(m.flags.namespace)(m.methodName) - if (methodInfo.isReachable) { - assert(m.body.isDefined, - s"The abstract method ${classDef.name.name}.${m.methodName} " + - "is reachable.") - methods += linkedMethod(m) - } + val reachable = methodInfo.isReachable + assert(m.body.isDefined || !reachable, + s"The abstract method ${classDef.name.name}.${m.methodName} " + + "is reachable.") - case m: JSConstructorDef => - if (analyzerInfo.isAnySubclassInstantiated) { - assert(jsConstructorDef.isEmpty, - s"Duplicate JS constructor in ${classDef.name.name} at ${m.pos}") - val version = m.hash.map(Hashers.hashAsVersion(_)) - jsConstructorDef = Some(new Versioned(m, version)) - } + reachable + } - case m: JSMethodDef => - if (analyzerInfo.isAnySubclassInstantiated) { - val version = m.hash.map(Hashers.hashAsVersion(_)) - exportedMembers += new Versioned(m, version) - } + val jsConstructor = + if (classInfo.isAnySubclassInstantiated) classDef.jsConstructor + else None - case m: JSPropertyDef => - if (analyzerInfo.isAnySubclassInstantiated) - exportedMembers += new Versioned(m, None) + val jsMethodProps = + if (classInfo.isAnySubclassInstantiated) classDef.jsMethodProps + else Nil - case m: JSNativeMemberDef => - if (analyzerInfo.jsNativeMembersUsed.contains(m.name.name)) - jsNativeMembers += m - } + val jsNativeMembers = classDef.jsNativeMembers + .filter(m => classInfo.jsNativeMembersUsed.contains(m.name.name)) - methods ++= syntheticMethodDefs.map(linkedMethod) + val allMethods = methods ++ syntheticMethodDefs val kind = - if (analyzerInfo.isModuleAccessed) classDef.kind + if (classInfo.isModuleAccessed) classDef.kind else classDef.kind.withoutModuleAccessor - val ancestors = analyzerInfo.ancestors.map(_.className) + val ancestors = classInfo.ancestors.map(_.className) - new LinkedClass( + val linkedClass = new LinkedClass( classDef.name, kind, classDef.jsClassCaptures, @@ -213,42 +193,33 @@ final class BaseLinker(config: CommonPhaseConfig, checkIR: Boolean) { classDef.interfaces, classDef.jsSuperClass, classDef.jsNativeLoadSpec, - fields.result(), - methods.result(), - jsConstructorDef, - exportedMembers.result(), - jsNativeMembers.result(), + fields, + allMethods, + jsConstructor, + jsMethodProps, + jsNativeMembers, classDef.optimizerHints, classDef.pos, ancestors.toList, - hasInstances = analyzerInfo.isAnySubclassInstantiated, - hasInstanceTests = analyzerInfo.areInstanceTestsUsed, - hasRuntimeTypeInfo = analyzerInfo.isDataAccessed, - fieldsRead = analyzerInfo.fieldsRead.toSet, - staticFieldsRead = analyzerInfo.staticFieldsRead.toSet, - staticDependencies = analyzerInfo.staticDependencies.toSet, - externalDependencies = analyzerInfo.externalDependencies.toSet, - dynamicDependencies = analyzerInfo.dynamicDependencies.toSet, + hasInstances = classInfo.isAnySubclassInstantiated, + hasInstanceTests = classInfo.areInstanceTestsUsed, + hasRuntimeTypeInfo = classInfo.isDataAccessed, + fieldsRead = classInfo.fieldsRead.toSet, + staticFieldsRead = classInfo.staticFieldsRead.toSet, + staticDependencies = classInfo.staticDependencies.toSet, + externalDependencies = classInfo.externalDependencies.toSet, + dynamicDependencies = classInfo.dynamicDependencies.toSet, version) - } -} - -private[frontend] object BaseLinker { - private[frontend] def isFieldDefNeeded(classInfo: ClassInfo, - field: ir.Trees.AnyFieldDef): Boolean = { - import ir.Trees._ - field match { - case field: FieldDef => - if (field.flags.namespace.isStatic) - classInfo.staticFieldsRead(field.name.name) || classInfo.staticFieldsWritten(field.name.name) - else if (classInfo.kind.isJSClass || classInfo.isAnySubclassInstantiated) - classInfo.fieldsRead(field.name.name) || classInfo.fieldsWritten(field.name.name) - else - false - - case field: JSFieldDef => - classInfo.isAnySubclassInstantiated + val linkedTopLevelExports = for { + topLevelExport <- classDef.topLevelExportDefs + } yield { + val infos = analysis.topLevelExportInfos( + (ModuleID(topLevelExport.moduleID), topLevelExport.topLevelExportName)) + new LinkedTopLevelExport(classDef.className, topLevelExport, + infos.staticDependencies.toSet, infos.externalDependencies.toSet) } + + (linkedClass, linkedTopLevelExports) } } diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/IRLoader.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/IRLoader.scala index cd2372b5ee..9643bec90a 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/IRLoader.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/IRLoader.scala @@ -23,7 +23,7 @@ import org.scalajs.linker.CollectionsCompat.MutableMapCompatOps import org.scalajs.logging._ -import org.scalajs.ir +import org.scalajs.ir.Version import org.scalajs.ir.Names.ClassName import org.scalajs.ir.Trees.ClassDef @@ -60,18 +60,13 @@ final class IRLoader(checkIR: Boolean) extends Analyzer.InputProvider def classesWithEntryPoints(): Iterable[ClassName] = entryPoints - def loadTopLevelExportInfos()(implicit ec: ExecutionContext): Future[List[Infos.TopLevelExportInfo]] = { - Future.traverse(entryPoints)(get(_, _.topLevelExportInfos)) - .map(_.flatten.toList) - } - def loadInfo(className: ClassName)( implicit ec: ExecutionContext): Option[Future[Infos.ClassInfo]] = { maybeGet(className, _.classInfo) } def loadClassDefAndVersion(className: ClassName)( - implicit ec: ExecutionContext): Future[(ClassDef, Option[String])] = { + implicit ec: ExecutionContext): Future[(ClassDef, Version)] = { get(className, u => (u.classDef, u.version)) } @@ -107,15 +102,14 @@ private object ClassDefAndInfoCache { final class Update( val classDef: ClassDef, val classInfo: Infos.ClassInfo, - val topLevelExportInfos: List[Infos.TopLevelExportInfo], - val version: Option[String]) + val version: Version) } private final class ClassDefAndInfoCache { import ClassDefAndInfoCache.Update private var cacheUsed: Boolean = false - private var version: Option[String] = None + private var version: Version = Version.Unversioned private var cacheUpdate: Future[Update] = _ def update(irFile: IRFileImpl, logger: Logger, checkIR: Boolean)( @@ -127,8 +121,7 @@ private final class ClassDefAndInfoCache { cacheUsed = true val newVersion = irFile.version - if (version.isEmpty || newVersion.isEmpty || - version.get != newVersion.get) { + if (!version.sameVersion(newVersion)) { version = newVersion cacheUpdate = irFile.tree.map { tree => if (checkIR) { @@ -138,8 +131,7 @@ private final class ClassDefAndInfoCache { s"There were $errorCount ClassDef checking errors.") } } - new Update(tree, Infos.generateClassInfo(tree), - Infos.generateTopLevelExportInfos(tree), version) + new Update(tree, Infos.generateClassInfo(tree), version) } } } diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/LinkerFrontendImpl.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/LinkerFrontendImpl.scala index 3aec774f91..9498836186 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/LinkerFrontendImpl.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/LinkerFrontendImpl.scala @@ -87,7 +87,7 @@ final class LinkerFrontendImpl private (config: LinkerFrontendImpl.Config) } logger.timeFuture("Refiner") { - refiner.refine(optimized, symbolRequirements, logger) + refiner.refine(optimized, unit.moduleInitializers, symbolRequirements, logger) } } } diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/MethodSynthesizer.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/MethodSynthesizer.scala index 7a9cbc0243..68ad2893fb 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/MethodSynthesizer.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/MethodSynthesizer.scala @@ -29,7 +29,7 @@ private[frontend] final class MethodSynthesizer( inputProvider: MethodSynthesizer.InputProvider) { def synthesizeMembers(classInfo: ClassInfo, analysis: Analysis)( - implicit ec: ExecutionContext): Future[Iterator[MethodDef]] = { + implicit ec: ExecutionContext): Future[List[MethodDef]] = { val publicMethodInfos = classInfo.methodInfos(MemberNamespace.Public) val futures = publicMethodInfos.valuesIterator.filter(_.isReachable).flatMap { m => m.syntheticKind match { @@ -44,7 +44,12 @@ private[frontend] final class MethodSynthesizer( } } - Future.sequence(futures) + /* Sort for stability. + * All synthetic members are in the Public namespace, so their `methodName` + * uniquely identifies them. + */ + Future.sequence(futures.toList) + .map(_.sortBy(_.methodName)) } private def synthesizeReflectiveProxy( @@ -75,7 +80,7 @@ private[frontend] final class MethodSynthesizer( MethodDef(MemberFlags.empty, proxyIdent, targetMDef.originalName, params, AnyType, Some(body))( - OptimizerHints.empty, targetMDef.hash) + OptimizerHints.empty, targetMDef.version) } } @@ -103,7 +108,7 @@ private[frontend] final class MethodSynthesizer( MethodDef(MemberFlags.empty, bridgeIdent, targetMDef.originalName, params, targetMDef.resultType, Some(body))( - OptimizerHints.empty, targetMDef.hash) + OptimizerHints.empty, targetMDef.version) } } @@ -146,11 +151,8 @@ private[frontend] final class MethodSynthesizer( for { classDef <- inputProvider.loadClassDef(classInfo.className) } yield { - classDef.memberDefs.collectFirst { - case mDef: MethodDef - if mDef.flags.namespace == MemberNamespace.Public && - mDef.methodName == methodName => - mDef + classDef.methods.find { mDef => + mDef.flags.namespace == MemberNamespace.Public && mDef.methodName == methodName }.getOrElse { throw new AssertionError( s"Cannot find ${methodName.nameString} in ${classInfo.className.nameString}") diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/Refiner.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/Refiner.scala index 31d318d395..06cfe85df4 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/Refiner.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/Refiner.scala @@ -16,6 +16,7 @@ import scala.concurrent._ import scala.collection.mutable +import org.scalajs.ir.{EntryPointsInfo, Version} import org.scalajs.ir.Names._ import org.scalajs.ir.Trees._ @@ -24,6 +25,7 @@ import org.scalajs.logging._ import org.scalajs.linker._ import org.scalajs.linker.interface.ModuleInitializer import org.scalajs.linker.standard._ +import org.scalajs.linker.standard.ModuleSet.ModuleID import org.scalajs.linker.analyzer._ import org.scalajs.linker.CollectionsCompat.MutableMapCompatOps @@ -33,29 +35,34 @@ final class Refiner(config: CommonPhaseConfig) { private val inputProvider = new InputProvider - def refine(unit: LinkingUnit, symbolRequirements: SymbolRequirement, - logger: Logger)(implicit ec: ExecutionContext): Future[LinkingUnit] = { + def refine(classDefs: Seq[(ClassDef, Version)], + moduleInitializers: List[ModuleInitializer], + symbolRequirements: SymbolRequirement, logger: Logger)( + implicit ec: ExecutionContext): Future[LinkingUnit] = { - val linkedClassesByName = - Map(unit.classDefs.map(c => c.className -> c): _*) - inputProvider.update(linkedClassesByName, unit.topLevelExports) + val linkedClassesByName = classDefs.map(c => c._1.className -> c._1).toMap + inputProvider.update(linkedClassesByName) val analysis = logger.timeFuture("Refiner: Compute reachability") { - analyze(unit.moduleInitializers, symbolRequirements, logger) + analyze(moduleInitializers, symbolRequirements, logger) } for { analysis <- analysis } yield { val result = logger.time("Refiner: Assemble LinkedClasses") { - val linkedClassDefs = for { - info <- analysis.classInfos.values + val assembled = for { + (classDef, version) <- classDefs + if analysis.classInfos.contains(classDef.className) } yield { - refineClassDef(linkedClassesByName(info.className), info) + BaseLinker.linkClassDef(classDef, version, + syntheticMethodDefs = Nil, analysis) } - new LinkingUnit(unit.coreSpec, linkedClassDefs.toList, unit.topLevelExports, - unit.moduleInitializers) + val (linkedClassDefs, linkedTopLevelExports) = assembled.unzip + + new LinkingUnit(config.coreSpec, linkedClassDefs.toList, + linkedTopLevelExports.flatten.toList, moduleInitializers) } inputProvider.cleanAfterRun() @@ -88,79 +95,30 @@ final class Refiner(config: CommonPhaseConfig) { } } } - - private def refineClassDef(classDef: LinkedClass, - info: Analysis.ClassInfo): LinkedClass = { - - val fields = classDef.fields.filter { f => - BaseLinker.isFieldDefNeeded(info, f) - } - - val methods = classDef.methods.filter { m => - val methodDef = m.value - info.methodInfos(methodDef.flags.namespace)(methodDef.methodName) - .isReachable - } - - val jsNativeMembers = classDef.jsNativeMembers.filter { m => - info.jsNativeMembersUsed.contains(m.name.name) - } - - val kind = - if (info.isModuleAccessed) classDef.kind - else classDef.kind.withoutModuleAccessor - - classDef.refined( - kind = kind, - fields = fields, - methods = methods, - jsNativeMembers = jsNativeMembers, - hasInstances = info.isAnySubclassInstantiated, - hasInstanceTests = info.areInstanceTestsUsed, - hasRuntimeTypeInfo = info.isDataAccessed, - fieldsRead = info.fieldsRead.toSet, - staticFieldsRead = info.staticFieldsRead.toSet, - staticDependencies = info.staticDependencies.toSet, - externalDependencies = info.externalDependencies.toSet, - dynamicDependencies = info.dynamicDependencies.toSet - ) - } - } private object Refiner { private class InputProvider extends Analyzer.InputProvider { - private var linkedClassesByName: Map[ClassName, LinkedClass] = _ - private var topLevelExports: List[LinkedTopLevelExport] = _ - private val cache = mutable.Map.empty[ClassName, LinkedClassInfoCache] - - def update(linkedClassesByName: Map[ClassName, LinkedClass], - topLevelExports: List[LinkedTopLevelExport]): Unit = { - this.linkedClassesByName = linkedClassesByName - this.topLevelExports = topLevelExports + private var classesByName: Map[ClassName, ClassDef] = _ + private val cache = mutable.Map.empty[ClassName, ClassInfoCache] + + def update(classesByName: Map[ClassName, ClassDef]): Unit = { + this.classesByName = classesByName } def classesWithEntryPoints(): Iterable[ClassName] = { - linkedClassesByName.values - .filter(_.hasStaticInitializer) + classesByName.values + .withFilter(EntryPointsInfo.forClassDef(_).hasEntryPoint) .map(_.className) } - def loadTopLevelExportInfos()( - implicit ec: ExecutionContext): Future[List[Infos.TopLevelExportInfo]] = Future { - /* We do not cache top-level exports, because they're quite rare, - * and usually quite small when they exist. - */ - Infos.generateTopLevelExportInfos(topLevelExports) - } - def loadInfo(className: ClassName)(implicit ec: ExecutionContext): Option[Future[Infos.ClassInfo]] = - getCache(className).map(_.loadInfo(linkedClassesByName(className))) + getCache(className).map(_.loadInfo(classesByName(className))) - private def getCache(className: ClassName): Option[LinkedClassInfoCache] = { + private def getCache(className: ClassName): Option[ClassInfoCache] = { cache.get(className).orElse { - if (linkedClassesByName.contains(className)) { - val fileCache = new LinkedClassInfoCache + if (classesByName.contains(className)) { + val fileCache = new ClassInfoCache cache += className -> fileCache Some(fileCache) } else { @@ -170,47 +128,60 @@ private object Refiner { } def cleanAfterRun(): Unit = { - linkedClassesByName = null + classesByName = null cache.filterInPlace((_, linkedClassCache) => linkedClassCache.cleanAfterRun()) } } - private class LinkedClassInfoCache { + private class ClassInfoCache { private var cacheUsed: Boolean = false - private val methodsInfoCaches = LinkedMethodDefsInfosCache() - private val jsConstructorInfoCache = new LinkedJSConstructorDefInfoCache() - private val exportedMembersInfoCaches = LinkedJSMethodPropDefsInfosCache() + private val methodsInfoCaches = MethodDefsInfosCache() + private val jsConstructorInfoCache = new JSConstructorDefInfoCache() + private val exportedMembersInfoCaches = JSMethodPropDefsInfosCache() private var info: Infos.ClassInfo = _ - def loadInfo(linkedClass: LinkedClass)(implicit ec: ExecutionContext): Future[Infos.ClassInfo] = Future { - update(linkedClass) + def loadInfo(classDef: ClassDef)(implicit ec: ExecutionContext): Future[Infos.ClassInfo] = Future { + update(classDef) info } - private def update(linkedClass: LinkedClass): Unit = synchronized { + private def update(classDef: ClassDef): Unit = synchronized { if (!cacheUsed) { cacheUsed = true - val builder = new Infos.ClassInfoBuilder(linkedClass.className, - linkedClass.kind, linkedClass.superClass.map(_.name), - linkedClass.interfaces.map(_.name), linkedClass.jsNativeLoadSpec) + val builder = new Infos.ClassInfoBuilder(classDef.className, + classDef.kind, classDef.superClass.map(_.name), + classDef.interfaces.map(_.name), classDef.jsNativeLoadSpec) + + classDef.fields.foreach { + case FieldDef(flags, FieldIdent(name), _, ftpe) => + if (!flags.namespace.isStatic) + builder.maybeAddReferencedFieldClass(name, ftpe) + + case _: JSFieldDef => + // Nothing to do. + } + + classDef.methods.foreach { method => + builder.addMethod(methodsInfoCaches.getInfo(method)) + } - for { - FieldDef(flags, FieldIdent(name), _, ftpe) <- linkedClass.fields - if !flags.namespace.isStatic - } { - builder.maybeAddReferencedFieldClass(name, ftpe) + classDef.jsConstructor.foreach { jsConstructor => + builder.addExportedMember(jsConstructorInfoCache.getInfo(jsConstructor)) } - for (linkedMethod <- linkedClass.methods) - builder.addMethod(methodsInfoCaches.getInfo(linkedMethod)) - for (jsNativeMember <- linkedClass.jsNativeMembers) - builder.addJSNativeMember(jsNativeMember) - for (jsConstructorDef <- linkedClass.jsConstructorDef) - builder.addExportedMember(jsConstructorInfoCache.getInfo(jsConstructorDef)) - for (info <- exportedMembersInfoCaches.getInfos(linkedClass.exportedMembers)) + for (info <- exportedMembersInfoCaches.getInfos(classDef.jsMethodProps)) builder.addExportedMember(info) + /* We do not cache top-level exports, because they're quite rare, + * and usually quite small when they exist. + */ + classDef.topLevelExportDefs.foreach { topLevelExportDef => + builder.addTopLevelExport(Infos.generateTopLevelExportInfo(classDef.name.name, topLevelExportDef)) + } + + classDef.jsNativeMembers.foreach(builder.addJSNativeMember(_)) + info = builder.result() } } @@ -229,15 +200,14 @@ private object Refiner { } } - private final class LinkedMethodDefsInfosCache private ( - val caches: Array[mutable.Map[MethodName, LinkedMethodDefInfoCache]]) + private final class MethodDefsInfosCache private ( + val caches: Array[mutable.Map[MethodName, MethodDefInfoCache]]) extends AnyVal { - def getInfo(method: Versioned[MethodDef]): Infos.MethodInfo = { - val methodDef = method.value + def getInfo(methodDef: MethodDef): Infos.MethodInfo = { val cache = caches(methodDef.flags.namespace.ordinal) - .getOrElseUpdate(methodDef.methodName, new LinkedMethodDefInfoCache) - cache.getInfo(method) + .getOrElseUpdate(methodDef.methodName, new MethodDefInfoCache) + cache.getInfo(methodDef) } def cleanAfterRun(): Unit = { @@ -245,9 +215,9 @@ private object Refiner { } } - private object LinkedMethodDefsInfosCache { - def apply(): LinkedMethodDefsInfosCache = { - new LinkedMethodDefsInfosCache( + private object MethodDefsInfosCache { + def apply(): MethodDefsInfosCache = { + new MethodDefsInfosCache( Array.fill(MemberNamespace.Count)(mutable.Map.empty)) } } @@ -262,17 +232,17 @@ private object Refiner { * only missing opportunities for incrementality in the case where JS members * are added or removed in the original .sjsir, which is not a big deal. */ - private final class LinkedJSMethodPropDefsInfosCache private ( - private var caches: Array[LinkedJSMethodPropDefInfoCache]) { + private final class JSMethodPropDefsInfosCache private ( + private var caches: Array[JSMethodPropDefInfoCache]) { - def getInfos(members: List[Versioned[JSMethodPropDef]]): List[Infos.ReachabilityInfo] = { + def getInfos(members: List[JSMethodPropDef]): List[Infos.ReachabilityInfo] = { if (members.isEmpty) { caches = null Nil } else { val membersSize = members.size if (caches == null || membersSize != caches.size) - caches = Array.fill(membersSize)(new LinkedJSMethodPropDefInfoCache) + caches = Array.fill(membersSize)(new JSMethodPropDefInfoCache) for ((member, i) <- members.zipWithIndex) yield { caches(i).getInfo(member) @@ -286,27 +256,27 @@ private object Refiner { } } - private object LinkedJSMethodPropDefsInfosCache { - def apply(): LinkedJSMethodPropDefsInfosCache = - new LinkedJSMethodPropDefsInfosCache(null) + private object JSMethodPropDefsInfosCache { + def apply(): JSMethodPropDefsInfosCache = + new JSMethodPropDefsInfosCache(null) } - private abstract class AbstractLinkedMemberInfoCache[Def <: MemberDef, Info] { + private abstract class AbstractMemberInfoCache[Def <: VersionedMemberDef, Info] { private var cacheUsed: Boolean = false - private var lastVersion: Option[String] = None + private var lastVersion: Version = Version.Unversioned private var info: Info = _ - final def getInfo(member: Versioned[Def]): Info = { + final def getInfo(member: Def): Info = { update(member) info } - private final def update(member: Versioned[Def]): Unit = { + private final def update(member: Def): Unit = { if (!cacheUsed) { cacheUsed = true val newVersion = member.version - if (!versionsMatch(newVersion, lastVersion)) { - info = computeInfo(member.value) + if (!lastVersion.sameVersion(newVersion)) { + info = computeInfo(member) lastVersion = newVersion } } @@ -322,22 +292,22 @@ private object Refiner { } } - private final class LinkedMethodDefInfoCache - extends AbstractLinkedMemberInfoCache[MethodDef, Infos.MethodInfo] { + private final class MethodDefInfoCache + extends AbstractMemberInfoCache[MethodDef, Infos.MethodInfo] { protected def computeInfo(member: MethodDef): Infos.MethodInfo = Infos.generateMethodInfo(member) } - private final class LinkedJSConstructorDefInfoCache - extends AbstractLinkedMemberInfoCache[JSConstructorDef, Infos.ReachabilityInfo] { + private final class JSConstructorDefInfoCache + extends AbstractMemberInfoCache[JSConstructorDef, Infos.ReachabilityInfo] { protected def computeInfo(member: JSConstructorDef): Infos.ReachabilityInfo = Infos.generateJSConstructorInfo(member) } - private final class LinkedJSMethodPropDefInfoCache - extends AbstractLinkedMemberInfoCache[JSMethodPropDef, Infos.ReachabilityInfo] { + private final class JSMethodPropDefInfoCache + extends AbstractMemberInfoCache[JSMethodPropDef, Infos.ReachabilityInfo] { protected def computeInfo(member: JSMethodPropDef): Infos.ReachabilityInfo = { member match { @@ -348,7 +318,4 @@ private object Refiner { } } } - - private def versionsMatch(a: Option[String], b: Option[String]): Boolean = - a.isDefined && b.isDefined && a.get == b.get } diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/optimizer/IncOptimizer.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/optimizer/IncOptimizer.scala index f4345a3b78..8982107805 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/optimizer/IncOptimizer.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/optimizer/IncOptimizer.scala @@ -22,6 +22,7 @@ import org.scalajs.ir._ import org.scalajs.ir.Names._ import org.scalajs.ir.Trees._ import org.scalajs.ir.Types._ +import org.scalajs.ir.Position.NoPosition import org.scalajs.logging._ @@ -30,7 +31,7 @@ import org.scalajs.linker.backend.emitter.LongImpl import org.scalajs.linker.frontend.LinkingUnit import org.scalajs.linker.interface.ModuleKind import org.scalajs.linker.standard._ -import org.scalajs.linker.CollectionsCompat.MutableMapCompatOps +import org.scalajs.linker.CollectionsCompat._ /** Incremental optimizer. * @@ -64,21 +65,22 @@ final class IncOptimizer private[optimizer] (config: CommonPhaseConfig, collOps: private var objectClass: Class = _ private val classes = collOps.emptyMap[ClassName, Class] private val interfaces = collOps.emptyParMap[ClassName, InterfaceType] + private val topLevelExports = new JSTopLevelMethodContainer - private var methodsToProcess = collOps.emptyAddable[MethodImpl] + private var methodsToProcess = collOps.emptyAddable[Processable] @inline private def getInterface(className: ClassName): InterfaceType = collOps.forceGet(interfaces, className) /** Update the incremental analyzer with a new run. */ - def update(unit: LinkingUnit, logger: Logger): LinkingUnit = { + def update(unit: LinkingUnit, logger: Logger): List[(ClassDef, Version)] = { batchMode = objectClass == null logger.debug(s"Optimizer: Batch mode: $batchMode") logger.time("Optimizer: Incremental part") { /* UPDATE PASS */ - updateAndTagEverything(unit.classDefs) + updateAndTagEverything(unit) } logger.time("Optimizer: Optimizer part") { @@ -86,41 +88,80 @@ final class IncOptimizer private[optimizer] (config: CommonPhaseConfig, collOps: processAllTaggedMethods(logger) } - val newLinkedClasses = for (linkedClass <- unit.classDefs) yield { - val className = linkedClass.className - val interface = getInterface(className) + val groupedTopLevelExports = unit.topLevelExports.groupBy(_.owningClass) - val publicContainer = classes.get(className).getOrElse { - /* For interfaces, we need to look at default methods. - * For other kinds of classes, the public namespace is necessarily - * empty. - */ - val container = interface.staticLike(MemberNamespace.Public) - assert( - linkedClass.kind == ClassKind.Interface || container.methods.isEmpty, - linkedClass.className -> linkedClass.kind) - container - } + for { + linkedClass <- unit.classDefs + } yield { + val topLevelExports = groupedTopLevelExports.getOrElse(linkedClass.className, Nil) + optimizedClass(linkedClass, topLevelExports) + } + } - val newMethods = for (m <- linkedClass.methods) yield { - val namespace = m.value.flags.namespace - val container = - if (namespace == MemberNamespace.Public) publicContainer - else interface.staticLike(namespace) - container.methods(m.value.methodName).optimizedMethodDef - } + private def optimizedClass(linkedClass: LinkedClass, + tles: List[LinkedTopLevelExport]): (ClassDef, Version) = { + val className = linkedClass.className + val interface = getInterface(className) + + val publicContainer = classes.get(className).getOrElse { + /* For interfaces, we need to look at default methods. + * For other kinds of classes, the public namespace is necessarily + * empty. + */ + val container = interface.staticLike(MemberNamespace.Public) + assert( + linkedClass.kind == ClassKind.Interface || container.methods.isEmpty, + linkedClass.className -> linkedClass.kind) + container + } - linkedClass.optimized(methods = newMethods) + val newMethods = for (m <- linkedClass.methods) yield { + val namespace = m.flags.namespace + val container = + if (namespace == MemberNamespace.Public) publicContainer + else interface.staticLike(namespace) + container.methods(m.methodName).optimizedDef } - new LinkingUnit(unit.coreSpec, newLinkedClasses, unit.topLevelExports, - unit.moduleInitializers) + val newTopLevelExports = tles.map { tle => + tle.tree match { + case method: TopLevelMethodExportDef => + topLevelExports.optimizedMethod(method.moduleID, method.topLevelExportName) + + case tree => + tree + } + } + + val classDef = ClassDef( + linkedClass.name, + OriginalName.NoOriginalName, + linkedClass.kind, + linkedClass.jsClassCaptures, + linkedClass.superClass, + linkedClass.interfaces, + linkedClass.jsSuperClass, + linkedClass.jsNativeLoadSpec, + linkedClass.fields, + newMethods, + interface.optimizedJSConstructorDef(), + interface.optimizedExportedMembers(), + linkedClass.jsNativeMembers, + newTopLevelExports + )(linkedClass.optimizerHints)(linkedClass.pos) + + (classDef, linkedClass.version) } /** Incremental part: update state and detect what needs to be re-optimized. * UPDATE PASS ONLY. (This IS the update pass). - */ - private def updateAndTagEverything(linkedClasses: List[LinkedClass]): Unit = { + */ + private def updateAndTagEverything(unit: LinkingUnit): Unit = { + updateAndTagClasses(unit.classDefs) + topLevelExports.updateWith(unit.topLevelExports) + } + + private def updateAndTagClasses(linkedClasses: List[LinkedClass]): Unit = { val neededInterfaces = collOps.emptyParMap[ClassName, LinkedClass] val neededClasses = collOps.emptyParMap[ClassName, LinkedClass] for (linkedClass <- linkedClasses) { @@ -234,28 +275,18 @@ final class IncOptimizer private[optimizer] (config: CommonPhaseConfig, collOps: * [[IncOptimizer.StaticLikeNamespace]]. */ private abstract class MethodContainer(linkedClass: LinkedClass, - val namespace: MemberNamespace) { + val myInterface: InterfaceType, val namespace: MemberNamespace) { val className: ClassName = linkedClass.className - def thisType: Type = + def untrackedThisType: Type = if (namespace.isStatic) NoType - else if (linkedClass.kind == ClassKind.HijackedClass) BoxedClassToPrimType(className) - else ClassType(className) + else myInterface.untrackedInstanceThisType val methods = mutable.Map.empty[MethodName, MethodImpl] updateWith(linkedClass) - def optimizedDefs: List[Versioned[MethodDef]] = { - (for { - method <- methods.values - if !method.deleted - } yield { - method.optimizedMethodDef - }).toList - } - /** UPDATE PASS ONLY. Global concurrency safe but not on same instance */ def updateWith(linkedClass: LinkedClass): (Set[MethodName], Set[MethodName], Set[MethodName]) = { @@ -277,10 +308,10 @@ final class IncOptimizer private[optimizer] (config: CommonPhaseConfig, collOps: namespace.ordinal } val linkedMethodDefs = linkedClass.methods.withFilter { - _.value.flags.namespace.ordinal == applicableNamespaceOrdinal + _.flags.namespace.ordinal == applicableNamespaceOrdinal } - val newMethodNames = linkedMethodDefs.map(_.value.methodName).toSet + val newMethodNames = linkedMethodDefs.map(_.methodName).toSet val methodSetChanged = methods.keySet != newMethodNames if (methodSetChanged) { // Remove deleted methods @@ -296,7 +327,7 @@ final class IncOptimizer private[optimizer] (config: CommonPhaseConfig, collOps: } for (linkedMethodDef <- linkedMethodDefs) { - val methodName = linkedMethodDef.value.methodName + val methodName = linkedMethodDef.methodName methods.get(methodName).fold { addedMethods += methodName @@ -327,9 +358,8 @@ final class IncOptimizer private[optimizer] (config: CommonPhaseConfig, collOps: * [[Class]] form a tree of the class hierarchy. */ private final class Class(val superClass: Option[Class], linkedClass: LinkedClass) - extends MethodContainer(linkedClass, MemberNamespace.Public) with Unregisterable { - - val myInterface = getInterface(className) + extends MethodContainer(linkedClass, getInterface(linkedClass.className), MemberNamespace.Public) + with Unregisterable { if (className == ObjectClass) { assert(superClass.isEmpty) @@ -351,7 +381,7 @@ final class IncOptimizer private[optimizer] (config: CommonPhaseConfig, collOps: var isInstantiated: Boolean = linkedClass.hasInstances private var hasElidableModuleAccessor: Boolean = computeHasElidableModuleAccessor(linkedClass) - private val hasElidableModuleAccessorAskers = collOps.emptyMap[MethodImpl, Unit] + private val hasElidableModuleAccessorAskers = collOps.emptyMap[Processable, Unit] var fields: List[AnyFieldDef] = linkedClass.fields var fieldsRead: Set[FieldName] = linkedClass.fieldsRead @@ -513,7 +543,7 @@ final class IncOptimizer private[optimizer] (config: CommonPhaseConfig, collOps: subclasses = collOps.finishAdd(subclassAcc) } - def askHasElidableModuleAccessor(asker: MethodImpl): Boolean = { + def askHasElidableModuleAccessor(asker: Processable): Boolean = { hasElidableModuleAccessorAskers.put(asker, ()) asker.registerTo(this) hasElidableModuleAccessor @@ -603,17 +633,7 @@ final class IncOptimizer private[optimizer] (config: CommonPhaseConfig, collOps: case Block(stats) => stats.forall(isElidableStat) case Assign(Select(This(), _, _), rhs) => isTriviallySideEffectFree(rhs) - // Mixin constructor, 2.11 - case ApplyStatic(flags, className, methodName, List(This())) - if !flags.isPrivate => - val container = - getInterface(className).staticLike(MemberNamespace.PublicStatic) - container.methods(methodName.name).originalDef.body.exists { - case Skip() => true - case _ => false - } - - // Mixin constructor, 2.12+ + // Mixin constructor case ApplyStatically(flags, This(), className, methodName, Nil) if !flags.isPrivate && !classes.contains(className) => // Since className is not in classes, it must be a default method call. @@ -671,25 +691,134 @@ final class IncOptimizer private[optimizer] (config: CommonPhaseConfig, collOps: } } - def unregisterDependee(dependee: MethodImpl): Unit = { + def unregisterDependee(dependee: Processable): Unit = { hasElidableModuleAccessorAskers.remove(dependee) } } /** Namespace for static members of a class. */ private final class StaticLikeNamespace(linkedClass: LinkedClass, - namespace: MemberNamespace) - extends MethodContainer(linkedClass, namespace) { + myInterface: InterfaceType, namespace: MemberNamespace) + extends MethodContainer(linkedClass, myInterface, namespace) { /** BOTH PASSES. */ final def lookupMethod(methodName: MethodName): Option[MethodImpl] = methods.get(methodName) } + private sealed abstract class JSMethodContainer { + def untrackedJSClassCaptures: List[ParamDef] + def untrackedThisType(namespace: MemberNamespace): Type + } + + private final class JSClassMethodContainer(linkedClass: LinkedClass, + val myInterface: InterfaceType) extends JSMethodContainer { + + val className: ClassName = linkedClass.className + + private[this] val exportedMembers = mutable.ArrayBuffer.empty[JSMethodImpl] + private[this] var jsConstructorDef: Option[JSCtorImpl] = None + private[this] var _jsClassCaptures: List[ParamDef] = Nil + + updateWith(linkedClass) + + /** JS class captures + * + * A similar argument applies here than for + * [[InterfaceType#untrackedThisType]]: The captures are merely a + * convenience for the optimizer's environment: Any real change of usage + * also necessarily changes the body of the method. + */ + def untrackedJSClassCaptures: List[ParamDef] = _jsClassCaptures + + def untrackedThisType(namespace: MemberNamespace): Type = + if (namespace.isStatic) NoType + else myInterface.untrackedInstanceThisType + + def updateWith(linkedClass: LinkedClass): Unit = { + _jsClassCaptures = linkedClass.jsClassCaptures.getOrElse(Nil) + updateExportedMembers(linkedClass.exportedMembers) + updateJSConstructorDef(linkedClass.jsConstructorDef) + } + + private def updateExportedMembers( + newExportedMembers: List[JSMethodPropDef]): Unit = { + val newLen = newExportedMembers.length + val oldLen = exportedMembers.length + + if (newLen > oldLen) { + exportedMembers.sizeHint(newLen) + for (i <- oldLen until newLen) + exportedMembers += new JSMethodImpl(this, i) + } else if (newLen < oldLen) { + for (i <- newLen until oldLen) + exportedMembers(i).delete() + exportedMembers.dropRightInPlace(oldLen - newLen) + } + + for { + (method, methodIdx) <- newExportedMembers.zipWithIndex + } { + exportedMembers(methodIdx).updateWith(method) + } + } + + private def updateJSConstructorDef( + newJSConstructorDef: Option[JSConstructorDef]): Unit = { + + newJSConstructorDef.fold { + jsConstructorDef.foreach(_.delete()) + jsConstructorDef = None + } { newJSConstructorDef => + if (jsConstructorDef.isEmpty) { + jsConstructorDef = Some(new JSCtorImpl(this)) + } + + jsConstructorDef.get.updateWith(newJSConstructorDef) + } + } + + def optimizedExportedMembers(): List[JSMethodPropDef] = + exportedMembers.map(_.optimizedDef).toList + + def optimizedJSConstructorDef(): Option[JSConstructorDef] = + jsConstructorDef.map(_.optimizedDef) + } + + private final class JSTopLevelMethodContainer extends JSMethodContainer { + + private[this] var methods = Map.empty[(String, String), (JSMethodImpl, Position)] + + val untrackedJSClassCaptures: List[ParamDef] = Nil + def untrackedThisType(namespace: MemberNamespace): Type = NoType + + def updateWith(topLevelExports: List[LinkedTopLevelExport]): Unit = { + val newMethods = topLevelExports.map(_.tree).collect { + case m: TopLevelMethodExportDef => + val key = (m.moduleID, m.topLevelExportName) + val impl = methods.get(key).fold(new JSMethodImpl(this, key))(_._1) + impl.updateWith(m.methodDef) + key -> (impl, m.pos) + }.toMap + + methods + .withFilter(e => !newMethods.contains(e._1)) + .foreach(_._2._1.delete()) + + methods = newMethods + } + + def optimizedMethod(moduleID: String, name: String): TopLevelMethodExportDef = { + val (impl, pos) = methods((moduleID, name)) + val newMethod = impl.optimizedDef.asInstanceOf[JSMethodDef] + TopLevelMethodExportDef(moduleID, newMethod)(pos) + } + } + /** Thing from which a [[MethodImpl]] can unregister itself from. */ private trait Unregisterable { /** UPDATE PASS ONLY. */ - def unregisterDependee(dependee: MethodImpl): Unit + def unregisterDependee(dependee: Processable): Unit } /** Type of a class or interface. @@ -702,17 +831,17 @@ final class IncOptimizer private[optimizer] (config: CommonPhaseConfig, collOps: val className: ClassName = linkedClass.className - private type MethodCallers = collOps.Map[MethodName, collOps.Map[MethodImpl, Unit]] + private type MethodCallers = collOps.Map[MethodName, collOps.Map[Processable, Unit]] - private val ancestorsAskers = collOps.emptyMap[MethodImpl, Unit] + private val ancestorsAskers = collOps.emptyMap[Processable, Unit] private val dynamicCallers: MethodCallers = collOps.emptyMap // ArrayBuffer to avoid need for ClassTag[collOps.Map[_, _]] private val staticCallers = mutable.ArrayBuffer.fill[MethodCallers](MemberNamespace.Count)(collOps.emptyMap) - private val jsNativeImportsAskers = collOps.emptyMap[MethodImpl, Unit] - private val fieldsReadAskers = collOps.emptyMap[MethodImpl, Unit] + private val jsNativeImportsAskers = collOps.emptyMap[Processable, Unit] + private val fieldsReadAskers = collOps.emptyMap[Processable, Unit] private var _ancestors: List[ClassName] = linkedClass.ancestors @@ -720,10 +849,12 @@ final class IncOptimizer private[optimizer] (config: CommonPhaseConfig, collOps: private val staticLikes: Array[StaticLikeNamespace] = { Array.tabulate(MemberNamespace.Count) { ord => - new StaticLikeNamespace(linkedClass, MemberNamespace.fromOrdinal(ord)) + new StaticLikeNamespace(linkedClass, this, MemberNamespace.fromOrdinal(ord)) } } + private val jsMethodContainer = new JSClassMethodContainer(linkedClass, this) + /* For now, we track all JS native imports together (the class itself and native members). * * This is more to avoid unnecessary tracking than due to an intrinsic reason. @@ -742,12 +873,27 @@ final class IncOptimizer private[optimizer] (config: CommonPhaseConfig, collOps: private var fieldsRead: Set[FieldName] = linkedClass.fieldsRead private var staticFieldsRead: Set[FieldName] = linkedClass.staticFieldsRead + /** The type of instances of this interface. + * + * Offered via untracked accessor since its only usage is in the + * environment of the Optimizer. + * + * However, this is merely a convenience: If the this type changes + * and a method body relies on it, the method body itself must change, + * because the type of the This() tree must change. + * + * Therefore, any tracking would be unnecessarily duplicate. + */ + def untrackedInstanceThisType: Type = _instanceThisType + + private var _instanceThisType = computeInstanceThisType(linkedClass) + override def toString(): String = s"intf ${className.nameString}" /** PROCESS PASS ONLY. */ def askDynamicCallTargets(methodName: MethodName, - asker: MethodImpl): List[MethodImpl] = { + asker: Processable): List[MethodImpl] = { dynamicCallers .getOrElseUpdate(methodName, collOps.emptyMap) .put(asker, ()) @@ -757,7 +903,7 @@ final class IncOptimizer private[optimizer] (config: CommonPhaseConfig, collOps: /** PROCESS PASS ONLY. */ def askStaticCallTarget(namespace: MemberNamespace, methodName: MethodName, - asker: MethodImpl): MethodImpl = { + asker: Processable): MethodImpl = { staticCallers(namespace.ordinal) .getOrElseUpdate(methodName, collOps.emptyMap) .put(asker, ()) @@ -784,14 +930,14 @@ final class IncOptimizer private[optimizer] (config: CommonPhaseConfig, collOps: _instantiatedSubclasses -= x /** PROCESS PASS ONLY. */ - def askAncestors(asker: MethodImpl): List[ClassName] = { + def askAncestors(asker: Processable): List[ClassName] = { ancestorsAskers.put(asker, ()) asker.registerTo(this) _ancestors } /** PROCESS PASS ONLY. Concurrency safe except with [[updateWith]]. */ - def askJSNativeImport(asker: MethodImpl): Option[JSNativeLoadSpec.Import] = { + def askJSNativeImport(asker: Processable): Option[JSNativeLoadSpec.Import] = { jsNativeImportsAskers.put(asker, ()) asker.registerTo(this) jsNativeImports._1 @@ -799,19 +945,19 @@ final class IncOptimizer private[optimizer] (config: CommonPhaseConfig, collOps: /** PROCESS PASS ONLY. Concurrency safe except with [[updateWith]]. */ def askJSNativeImport(methodName: MethodName, - asker: MethodImpl): Option[JSNativeLoadSpec.Import] = { + asker: Processable): Option[JSNativeLoadSpec.Import] = { jsNativeImportsAskers.put(asker, ()) asker.registerTo(this) jsNativeImports._2.get(methodName) } - def askFieldRead(name: FieldName, asker: MethodImpl): Boolean = { + def askFieldRead(name: FieldName, asker: Processable): Boolean = { fieldsReadAskers.put(asker, ()) asker.registerTo(this) fieldsRead.contains(name) } - def askStaticFieldRead(name: FieldName, asker: MethodImpl): Boolean = { + def askStaticFieldRead(name: FieldName, asker: Processable): Boolean = { fieldsReadAskers.put(asker, ()) asker.registerTo(this) staticFieldsRead.contains(name) @@ -821,6 +967,12 @@ final class IncOptimizer private[optimizer] (config: CommonPhaseConfig, collOps: def staticLike(namespace: MemberNamespace): StaticLikeNamespace = staticLikes(namespace.ordinal) + def optimizedExportedMembers(): List[JSMethodPropDef] = + jsMethodContainer.optimizedExportedMembers() + + def optimizedJSConstructorDef(): Option[JSConstructorDef] = + jsMethodContainer.optimizedJSConstructorDef() + /** UPDATE PASS ONLY. Not concurrency safe. */ def updateWith(linkedClass: LinkedClass): Unit = { // Update ancestors @@ -854,6 +1006,10 @@ final class IncOptimizer private[optimizer] (config: CommonPhaseConfig, collOps: this.tagStaticCallersOf(staticLike.namespace, method) } } + + _instanceThisType = computeInstanceThisType(linkedClass) + + jsMethodContainer.updateWith(linkedClass) } /** UPDATE PASS ONLY. */ @@ -880,7 +1036,7 @@ final class IncOptimizer private[optimizer] (config: CommonPhaseConfig, collOps: } /** UPDATE PASS ONLY. */ - def unregisterDependee(dependee: MethodImpl): Unit = { + def unregisterDependee(dependee: Processable): Unit = { ancestorsAskers.remove(dependee) dynamicCallers.valuesIterator.foreach(_.remove(dependee)) staticCallers.foreach(_.valuesIterator.foreach(_.remove(dependee))) @@ -910,6 +1066,94 @@ final class IncOptimizer private[optimizer] (config: CommonPhaseConfig, collOps: (clazz, nativeMembers.toMap) } + + private def computeInstanceThisType(linkedClass: LinkedClass): Type = { + if (linkedClass.kind.isJSType) AnyType + else if (linkedClass.kind == ClassKind.HijackedClass) BoxedClassToPrimType(className) + else ClassType(className) + } + } + + /** A thing that can be tagged for reprocessing and then reprocessed. */ + private abstract class Processable { + type Def >: scala.Null <: VersionedMemberDef + + private[this] val registeredTo = collOps.emptyMap[Unregisterable, Unit] + private[this] val tagged = new AtomicBoolean(false) + private[this] var _deleted: Boolean = false + + private[this] var lastInVersion: Version = Version.Unversioned + private[this] var lastOutVersion: Int = 0 + + private[this] var _originalDef: Def = _ + private[this] var _optimizedDef: Def = _ + + protected def doProcess(newVersion: Version): Def + + final def deleted: Boolean = _deleted + + final def originalDef: Def = _originalDef + final def optimizedDef: Def = _optimizedDef + + /** PROCESS PASS ONLY. */ + final def process(): Unit = { + if (!_deleted) { + lastOutVersion += 1 + _optimizedDef = doProcess(Version.fromInt(lastOutVersion)) + tagged.set(false) + } + } + + /** Returns true if the method changed */ + protected def updateDef(methodDef: Def): Boolean = { + assert(!deleted, "updateDef() called on a deleted method") + + if (lastInVersion.sameVersion(methodDef.version)) { + false + } else { + lastInVersion = methodDef.version + _originalDef = methodDef + _optimizedDef = null + tag() + true + } + } + + private def unregisterFromEverywhere(): Unit = { + registeredTo.keysIterator.foreach(_.unregisterDependee(this)) + registeredTo.clear() + } + + /** UPDATE PASS ONLY. Not concurrency safe on same instance. */ + final def delete(): Unit = { + assert(!_deleted, "delete() called twice") + _deleted = true + if (protectTag()) + unregisterFromEverywhere() + } + + /** Concurrency safe with itself and [[delete]] on the same instance + * + * [[tag]] can be called concurrently with [[delete]] when methods in + * traits/classes are updated. + * + * UPDATE PASS ONLY. + */ + final def tag(): Unit = { + if (protectTag()) { + collOps.add(methodsToProcess, this) + unregisterFromEverywhere() + } + } + + /** PROCESS PASS ONLY. */ + final def registerTo(unregisterable: Unregisterable): Unit = + registeredTo.put(unregisterable, ()) + + /** Tag this method and return true iff it wasn't tagged before. + * UPDATE PASS ONLY. + */ + private def protectTag(): Boolean = !tagged.getAndSet(true) } /** A method implementation. @@ -922,34 +1166,21 @@ final class IncOptimizer private[optimizer] (config: CommonPhaseConfig, collOps: */ private final class MethodImpl(owner: MethodContainer, val methodName: MethodName) - extends OptimizerCore.MethodImpl with OptimizerCore.AbstractMethodID - with Unregisterable { - - private[this] var _deleted: Boolean = false + extends Processable with OptimizerCore.AbstractMethodID with Unregisterable { - private val bodyAskers = collOps.emptyMap[MethodImpl, Unit] - private val registeredTo = collOps.emptyMap[Unregisterable, Unit] - private val tagged = new AtomicBoolean(false) + type Def = MethodDef - var lastInVersion: Option[String] = None - var lastOutVersion: Int = 0 - - var optimizerHints: OptimizerHints = OptimizerHints.empty - var originalDef: MethodDef = _ - var optimizedMethodDef: Versioned[MethodDef] = _ + private val bodyAskers = collOps.emptyMap[Processable, Unit] var attributes: OptimizerCore.MethodAttributes = _ def enclosingClassName: ClassName = owner.className - def thisType: Type = owner.thisType - def deleted: Boolean = _deleted - override def toString(): String = s"$owner.${methodName.nameString}" /** PROCESS PASS ONLY. */ - def askBody(asker: MethodImpl): MethodDef = { + def askBody(asker: Processable): MethodDef = { bodyAskers.put(asker, ()) asker.registerTo(this) originalDef @@ -962,23 +1193,9 @@ final class IncOptimizer private[optimizer] (config: CommonPhaseConfig, collOps: } /** UPDATE PASS ONLY. */ - def unregisterDependee(dependee: MethodImpl): Unit = + def unregisterDependee(dependee: Processable): Unit = bodyAskers.remove(dependee) - def registerTo(unregisterable: Unregisterable): Unit = - registeredTo.put(unregisterable, ()) - - /** UPDATE PASS ONLY. */ - private def unregisterFromEverywhere(): Unit = { - registeredTo.keysIterator.foreach(_.unregisterDependee(this)) - registeredTo.clear() - } - - /** Tag this method and return true iff it wasn't tagged before. - * UPDATE PASS ONLY. - */ - private def protectTag(): Boolean = !tagged.getAndSet(true) - /** Returns true if the method's attributes changed. * Attributes are whether it is inlineable, and whether it is a trait * impl forwarder. Basically this is what is declared in @@ -986,120 +1203,175 @@ final class IncOptimizer private[optimizer] (config: CommonPhaseConfig, collOps: * In the process, tags all the body askers if the body changes. * UPDATE PASS ONLY. Not concurrency safe on same instance. */ - def updateWith(linkedMethod: Versioned[MethodDef]): Boolean = { - assert(!_deleted, "updateWith() called on a deleted method") - - if (lastInVersion.isDefined && lastInVersion == linkedMethod.version) { - false + def updateWith(methodDef: MethodDef): Boolean = { + val changed = updateDef(methodDef) + if (changed) { + tagBodyAskers() + + val oldAttributes = attributes + attributes = OptimizerCore.MethodAttributes.compute(enclosingClassName, methodDef) + attributes != oldAttributes } else { - lastInVersion = linkedMethod.version + false + } + } - val methodDef = linkedMethod.value + /** PROCESS PASS ONLY. */ + protected def doProcess(newVersion: Version): MethodDef = { + val MethodDef(static, name, originalName, params, resultType, optBody) = + originalDef + val body = optBody.getOrElse { + throw new AssertionError("Methods to optimize must be concrete") + } - val changed = { - originalDef == null || - (methodDef.hash zip originalDef.hash).forall { - case (h1, h2) => !Hashers.hashesEqual(h1, h2) - } - } + val (newParams, newBody) = new Optimizer(this, this.toString()).optimize( + Some(this), owner.untrackedThisType, params, jsClassCaptures = Nil, + resultType, body, isNoArgCtor = name.name == NoArgConstructorName) - if (changed) { - tagBodyAskers() + MethodDef(static, name, originalName, + newParams, resultType, Some(newBody))( + originalDef.optimizerHints, newVersion)(originalDef.pos) + } + } - val oldAttributes = attributes + private final class JSMethodImpl(owner: JSMethodContainer, id: Any) extends Processable { - optimizerHints = methodDef.optimizerHints - originalDef = methodDef - optimizedMethodDef = null - attributes = computeNewAttributes() - tag() + type Def = JSMethodPropDef - attributes != oldAttributes - } else { - false - } + override def toString(): String = + s"$owner[$id]" + + def updateWith(linkedMethod: JSMethodPropDef): Unit = + updateDef(linkedMethod) + + protected def doProcess(newVersion: Version): JSMethodPropDef = { + originalDef match { + case originalDef @ JSMethodDef(flags, name, params, restParam, body) => + val thisType = owner.untrackedThisType(flags.namespace) + + val (newParamsAndRest, newBody) = new Optimizer(this, this.toString()).optimize( + None, thisType, params ++ restParam.toList, owner.untrackedJSClassCaptures, + AnyType, body, isNoArgCtor = false) + + val (newParams, newRestParam) = + if (restParam.isDefined) (newParamsAndRest.init, Some(newParamsAndRest.last)) + else (newParamsAndRest, None) + + JSMethodDef(flags, name, newParams, newRestParam, newBody)( + originalDef.optimizerHints, newVersion)(originalDef.pos) + + case originalDef @ JSPropertyDef(flags, name, getterBody, setterArgAndBody) => + val thisType = owner.untrackedThisType(flags.namespace) + val jsClassCaptures = owner.untrackedJSClassCaptures + + val newGetterBody = getterBody.map { body => + val (_, newBody) = new Optimizer(this, "get " + this.toString()).optimize( + None, thisType, Nil, jsClassCaptures, AnyType, body, isNoArgCtor = false) + newBody + } + + val newSetterArgAndBody = setterArgAndBody.map { case (param, body) => + val (List(newParam), newBody) = new Optimizer(this, "set " + this.toString()).optimize( + None, thisType, List(param), jsClassCaptures, AnyType, body, + isNoArgCtor = false) + (newParam, newBody) + } + + JSPropertyDef(flags, name, newGetterBody, newSetterArgAndBody)(newVersion)(originalDef.pos) } } + } - /** UPDATE PASS ONLY. Not concurrency safe on same instance. */ - def delete(): Unit = { - assert(!_deleted, "delete() called twice") - _deleted = true - if (protectTag()) - unregisterFromEverywhere() - } + private final class JSCtorImpl(owner: JSMethodContainer) extends Processable { - /** Concurrency safe with itself and [[delete]] on the same instance - * - * [[tag]] can be called concurrently with [[delete]] when methods in - * traits/classes are updated. - * - * UPDATE PASS ONLY. - */ - def tag(): Unit = if (protectTag()) { - collOps.add(methodsToProcess, this) - unregisterFromEverywhere() - } + type Def = JSConstructorDef - /** PROCESS PASS ONLY. */ - def process(): Unit = if (!_deleted) { - val optimizedDef = new Optimizer().optimize(thisType, originalDef) - lastOutVersion += 1 - optimizedMethodDef = - new Versioned(optimizedDef, Some(lastOutVersion.toString)) - tagged.set(false) - } + override def toString(): String = + s"$owner ctor" - /** All methods are PROCESS PASS ONLY */ - private class Optimizer extends OptimizerCore(config) { - import OptimizerCore.ImportTarget + def updateWith(linkedMethod: JSConstructorDef): Unit = + updateDef(linkedMethod) - type MethodID = MethodImpl + protected def doProcess(newVersion: Version): JSConstructorDef = { + val JSConstructorDef(flags, params, restParam, body) = originalDef - val myself: MethodImpl.this.type = MethodImpl.this + val thisType = owner.untrackedThisType(flags.namespace) - protected def getMethodBody(method: MethodID): MethodDef = - method.askBody(myself) + val (newParamsAndRest, newRawBody) = new Optimizer(this, this.toString()).optimize( + None, thisType, params ++ restParam.toList, owner.untrackedJSClassCaptures, AnyType, + Block(body.allStats)(body.pos), isNoArgCtor = false) - /** Look up the targets of a dynamic call to an instance method. */ - protected def dynamicCall(intfName: ClassName, - methodName: MethodName): List[MethodID] = { - getInterface(intfName).askDynamicCallTargets(methodName, myself) - } + val (newParams, newRestParam) = + if (restParam.isDefined) (newParamsAndRest.init, Some(newParamsAndRest.last)) + else (newParamsAndRest, None) - /** Look up the target of a static call to an instance method. */ - protected def staticCall(className: ClassName, namespace: MemberNamespace, - methodName: MethodName): MethodID = { - getInterface(className).askStaticCallTarget(namespace, methodName, myself) + val bodyStats = newRawBody match { + case Block(stats) => stats + case stat => List(stat) } - protected def getAncestorsOf(intfName: ClassName): List[ClassName] = - getInterface(intfName).askAncestors(myself) + val (beforeSuper, superCall :: afterSuper) = + bodyStats.span(!_.isInstanceOf[JSSuperConstructorCall]) - protected def hasElidableModuleAccessor(moduleClassName: ClassName): Boolean = - classes(moduleClassName).askHasElidableModuleAccessor(myself) + val newBody = JSConstructorBody(beforeSuper, + superCall.asInstanceOf[JSSuperConstructorCall], afterSuper)(body.pos) - protected def tryNewInlineableClass( - className: ClassName): Option[OptimizerCore.InlineableClassStructure] = { - classes(className).tryNewInlineable - } + JSConstructorDef(flags, newParams, newRestParam, newBody)( + originalDef.optimizerHints, newVersion)(originalDef.pos) + } + } - protected def getJSNativeImportOf( - target: ImportTarget): Option[JSNativeLoadSpec.Import] = { - target match { - case ImportTarget.Class(className) => - getInterface(className).askJSNativeImport(myself) - case ImportTarget.Member(className, methodName) => - getInterface(className).askJSNativeImport(methodName, myself) - } - } + /** Concrete optimizer bound to types we use. + * + * All methods are PROCESS PASS ONLY + */ + private final class Optimizer(asker: Processable, debugID: String) + extends OptimizerCore(config, debugID) { + import OptimizerCore.ImportTarget + + type MethodID = MethodImpl - protected def isFieldRead(className: ClassName, fieldName: FieldName): Boolean = - getInterface(className).askFieldRead(fieldName, myself) + protected def getMethodBody(method: MethodID): MethodDef = + method.askBody(asker) + + /** Look up the targets of a dynamic call to an instance method. */ + protected def dynamicCall(intfName: ClassName, + methodName: MethodName): List[MethodID] = { + getInterface(intfName).askDynamicCallTargets(methodName, asker) + } - protected def isStaticFieldRead(className: ClassName, fieldName: FieldName): Boolean = - getInterface(className).askStaticFieldRead(fieldName, myself) + /** Look up the target of a static call to an instance method. */ + protected def staticCall(className: ClassName, namespace: MemberNamespace, + methodName: MethodName): MethodID = { + getInterface(className).askStaticCallTarget(namespace, methodName, asker) } + + protected def getAncestorsOf(intfName: ClassName): List[ClassName] = + getInterface(intfName).askAncestors(asker) + + protected def hasElidableModuleAccessor(moduleClassName: ClassName): Boolean = + classes(moduleClassName).askHasElidableModuleAccessor(asker) + + protected def tryNewInlineableClass( + className: ClassName): Option[OptimizerCore.InlineableClassStructure] = { + classes(className).tryNewInlineable + } + + protected def getJSNativeImportOf( + target: ImportTarget): Option[JSNativeLoadSpec.Import] = { + target match { + case ImportTarget.Class(className) => + getInterface(className).askJSNativeImport(asker) + case ImportTarget.Member(className, methodName) => + getInterface(className).askJSNativeImport(methodName, asker) + } + } + + protected def isFieldRead(className: ClassName, fieldName: FieldName): Boolean = + getInterface(className).askFieldRead(fieldName, asker) + + protected def isStaticFieldRead(className: ClassName, fieldName: FieldName): Boolean = + getInterface(className).askStaticFieldRead(fieldName, asker) } } diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/optimizer/OptimizerCore.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/optimizer/OptimizerCore.scala index 62d3f514ac..264ac04ee1 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/optimizer/OptimizerCore.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/optimizer/OptimizerCore.scala @@ -40,18 +40,17 @@ import org.scalajs.linker.backend.emitter.Transients._ * optimizer does. To perform inlining, it relies on abstract protected * methods to identify the target of calls. */ -private[optimizer] abstract class OptimizerCore(config: CommonPhaseConfig) { +private[optimizer] abstract class OptimizerCore( + config: CommonPhaseConfig, debugID: String) { import OptimizerCore._ type MethodID <: AbstractMethodID - val myself: MethodID - private def semantics: Semantics = config.coreSpec.semantics // Uncomment and adapt to print debug messages only during one method //lazy val debugThisMethod: Boolean = - // myself.toString() == "java.lang.FloatingPointBits$.numberHashCode;D;I" + // debugID == "java.lang.FloatingPointBits$.numberHashCode;D;I" /** Returns the body of a method. */ protected def getMethodBody(method: MethodID): MethodDef @@ -142,16 +141,12 @@ private[optimizer] abstract class OptimizerCore(config: CommonPhaseConfig) { private val intrinsics = Intrinsics.buildIntrinsics(config.coreSpec.esFeatures) - def optimize(thisType: Type, originalDef: MethodDef): MethodDef = { + def optimize(myself: Option[MethodID], thisType: Type, params: List[ParamDef], + jsClassCaptures: List[ParamDef], resultType: Type, body: Tree, + isNoArgCtor: Boolean): (List[ParamDef], Tree) = { try { - val MethodDef(static, name, originalName, params, resultType, optBody) = - originalDef - val body = optBody getOrElse { - throw new AssertionError("Methods to optimize must be concrete") - } - - val (newParams, newBody1) = try { - transformMethodDefBody(myself, thisType, params, resultType, body) + try { + transformMethodDefBody(myself, thisType, params, jsClassCaptures, resultType, body, isNoArgCtor) } catch { case _: TooManyRollbacksException => localNameAllocator.clear() @@ -159,20 +154,15 @@ private[optimizer] abstract class OptimizerCore(config: CommonPhaseConfig) { labelNameAllocator.clear() stateBackupChain = Nil disableOptimisticOptimizations = true - transformMethodDefBody(myself, thisType, params, resultType, body) + transformMethodDefBody(myself, thisType, params, jsClassCaptures, resultType, body, isNoArgCtor) } - val newBody = - if (originalDef.methodName == NoArgConstructorName) tryElimStoreModule(newBody1) - else newBody1 - MethodDef(static, name, originalName, newParams, resultType, - Some(newBody))(originalDef.optimizerHints, None)(originalDef.pos) } catch { case NonFatal(cause) => - throw new OptimizeException(myself, attemptedInlining.distinct.toList, cause) + throw new OptimizeException(debugID, attemptedInlining.distinct.toList, cause) case e: Throwable => // This is a fatal exception. Don't wrap, just output debug info error Console.err.println(exceptionMsg( - myself, attemptedInlining.distinct.toList, e)) + debugID, attemptedInlining.distinct.toList, e)) throw e } } @@ -413,14 +403,6 @@ private[optimizer] abstract class OptimizerCore(config: CommonPhaseConfig) { case _ => While(newCond, transformStat(body)) } - case DoWhile(body, cond) => - val newBody = transformStat(body) - val newCond = transformExpr(cond) - newCond match { - case BooleanLiteral(false) => newBody - case _ => DoWhile(newBody, newCond) - } - case ForIn(obj, keyVar @ LocalIdent(name), originalName, body) => val newObj = transformExpr(obj) val (newName, newOriginalName) = @@ -564,7 +546,7 @@ private[optimizer] abstract class OptimizerCore(config: CommonPhaseConfig) { trampoline { pretransformExpr(expr) { texpr => def constant(typeRef: TypeRef): TailRec[Tree] = - TailCalls.done(Block(finishTransformStat(texpr), ClassOf(typeRef))) + TailCalls.done(Block(checkNotNullStatement(texpr), ClassOf(typeRef))) texpr.tpe match { case RefinedType(ClassType(LongImpl.RuntimeLongClass), true, false) => @@ -574,7 +556,7 @@ private[optimizer] abstract class OptimizerCore(config: CommonPhaseConfig) { case RefinedType(ArrayType(arrayTypeRef), true, false) => constant(arrayTypeRef) case _ => - TailCalls.done(GetClass(finishTransformExpr(texpr))) + TailCalls.done(GetClass(finishTransformExprMaybeAssumeNotNull(texpr))) } } } @@ -888,7 +870,7 @@ private[optimizer] abstract class OptimizerCore(config: CommonPhaseConfig) { val localDef = scope.env.localDefs.getOrElse(name, { throw new AssertionError( s"Cannot find local def '$name' at $pos\n" + - s"While optimizing $myself\n" + + s"While optimizing $debugID\n" + s"Env is ${scope.env}\n" + s"Inlining ${scope.implsBeingInlined}") }) @@ -898,7 +880,7 @@ private[optimizer] abstract class OptimizerCore(config: CommonPhaseConfig) { val localDef = scope.env.thisLocalDef.getOrElse { throw new AssertionError( s"Found invalid 'this' at $pos\n" + - s"While optimizing $myself\n" + + s"While optimizing $debugID\n" + s"Env is ${scope.env}\n" + s"Inlining ${scope.implsBeingInlined}") } @@ -986,18 +968,13 @@ private[optimizer] abstract class OptimizerCore(config: CommonPhaseConfig) { if (baseTpe == NothingType) { cont(texpr) } else if (baseTpe == NullType) { - // Undefined behavior for NPE - cont(texpr) + cont(checkNotNull(texpr)) } else if (isSubtype(baseTpe, JavaScriptExceptionClassType)) { - if (texpr.tpe.isNullable) { - default - } else { - pretransformSelectCommon(AnyType, texpr, JavaScriptExceptionClass, - FieldIdent(exceptionFieldName), isLhsOfAssign = false)(cont) - } + pretransformSelectCommon(AnyType, texpr, JavaScriptExceptionClass, + FieldIdent(exceptionFieldName), isLhsOfAssign = false)(cont) } else { if (texpr.tpe.isExact || !isSubtype(JavaScriptExceptionClassType, baseTpe)) - cont(texpr) + cont(checkNotNull(texpr)) else default } @@ -1259,8 +1236,9 @@ private[optimizer] abstract class OptimizerCore(config: CommonPhaseConfig) { cont(PreTransTree(sel, RefinedType(sel.tpe))) } - case PreTransTree(newQual, _) => - cont(PreTransTree(Select(newQual, className, field)(expectedType), + case PreTransTree(newQual, newQualType) => + val newQual1 = maybeAssumeNotNull(newQual, newQualType) + cont(PreTransTree(Select(newQual1, className, field)(expectedType), RefinedType(expectedType))) } } @@ -1343,7 +1321,7 @@ private[optimizer] abstract class OptimizerCore(config: CommonPhaseConfig) { } case _:PreTransUnaryOp | _:PreTransBinaryOp | _:PreTransJSBinaryOp => - PreTransTree(finishTransformExpr(preTrans)) + PreTransTree(finishTransformExpr(preTrans), preTrans.tpe) case PreTransLocalDef(localDef @ LocalDef(tpe, _, replacement)) => replacement match { @@ -1425,6 +1403,12 @@ private[optimizer] abstract class OptimizerCore(config: CommonPhaseConfig) { } } + /** Finishes an expression pretransform to get a normal [[Tree]], recording + * whether the pretransform was known to be not-null. + */ + private def finishTransformExprMaybeAssumeNotNull(preTrans: PreTransform): Tree = + maybeAssumeNotNull(finishTransformExpr(preTrans), preTrans.tpe) + /** Finishes an expression pretransform to get a normal [[Tree]]. * This method (together with finishTransformStat) must not be called more * than once per pretransform and per translation. @@ -1601,8 +1585,14 @@ private[optimizer] abstract class OptimizerCore(config: CommonPhaseConfig) { Skip()(stat.pos) case NewArray(_, lengths) if semantics.negativeArraySizes == CheckedBehavior.Unchecked => Block(lengths.map(keepOnlySideEffects))(stat.pos) + case ArrayValue(_, elems) => + Block(elems.map(keepOnlySideEffects(_)))(stat.pos) + case ArrayLength(array) => + checkNotNullStatement(array)(stat.pos) + case ArraySelect(array, index) if semantics.arrayIndexOutOfBounds == CheckedBehavior.Unchecked => + Block(checkNotNullStatement(array)(stat.pos), keepOnlySideEffects(index))(stat.pos) case Select(qualifier, _, _) => - keepOnlySideEffects(qualifier) + checkNotNullStatement(qualifier)(stat.pos) case Closure(_, _, _, _, _, captureValues) => Block(captureValues.map(keepOnlySideEffects))(stat.pos) case UnaryOp(_, arg) => @@ -1649,10 +1639,14 @@ private[optimizer] abstract class OptimizerCore(config: CommonPhaseConfig) { Block(elems.map(keepOnlySideEffects))(stat.pos) case RecordSelect(record, _) => keepOnlySideEffects(record) + case GetClass(expr) => + checkNotNullStatement(expr)(stat.pos) + case Clone(expr) => + checkNotNullStatement(expr)(stat.pos) case WrapAsThrowable(expr) => keepOnlySideEffects(expr) case UnwrapFromThrowable(expr) => - keepOnlySideEffects(expr) + checkNotNullStatement(expr)(stat.pos) case _ => stat } @@ -1684,16 +1678,25 @@ private[optimizer] abstract class OptimizerCore(config: CommonPhaseConfig) { val methodName = methodIdent.name def treeNotInlined = { - cont(PreTransTree(Apply(flags, finishTransformExpr(treceiver), methodIdent, + cont(PreTransTree(Apply(flags, + finishTransformExprMaybeAssumeNotNull(treceiver), methodIdent, targs.map(finishTransformExpr))(resultType), RefinedType(resultType))) } treceiver.tpe.base match { case NothingType => - cont(treceiver) + cont(treceiver) // throws case NullType => - // Apply on null is UB, just create a well-typed tree. - cont(Block(finishTransformStat(treceiver), Throw(Null())).toPreTransform) + val checked = checkNotNull(treceiver) + /* When NPEs are Unchecked, checkNotNull directly returns `treceiver`, + * whose `tpe` is still `Null`. If the call is used in a context that + * expects a non-nullable type (such as a primitive), this causes + * ill-typed IR. In that case, we explicitly insert a `throw null`. + */ + val checkedAndWellTyped = + if (checked.tpe.isNothingType) checked + else PreTransTree(Block(finishTransformStat(checked), Throw(Null()))) + cont(checkedAndWellTyped) case _ => if (methodName.isReflectiveProxy) { // Never inline reflective proxies @@ -1738,9 +1741,10 @@ private[optimizer] abstract class OptimizerCore(config: CommonPhaseConfig) { * targets belong to. Therefore, we have to keep the receiver's * static type as a declared type, which is our only safe choice. */ + val representative = impls.minBy(_.enclosingClassName) // for stability val receiverType = treceiver.tpe.base inline(allocationSites, Some((receiverType, treceiver)), targs, - impls.head, isStat, usePreTransform)(cont) + representative, isStat, usePreTransform)(cont) } else { treeNotInlined } @@ -1831,7 +1835,8 @@ private[optimizer] abstract class OptimizerCore(config: CommonPhaseConfig) { methodName) pretransformExprs(receiver, args) { (treceiver, targs) => pretransformSingleDispatch(flags, target, Some(treceiver), targs, isStat, usePreTransform)(cont) { - treeNotInlined0(finishTransformExpr(treceiver), targs.map(finishTransformExpr)) + treeNotInlined0(finishTransformExprMaybeAssumeNotNull(treceiver), + targs.map(finishTransformExpr)) } } } @@ -2194,7 +2199,7 @@ private[optimizer] abstract class OptimizerCore(config: CommonPhaseConfig) { def finishTransformArgsAsStat(): Tree = { val newOptReceiver = - optReceiver.fold[Tree](Skip())(r => finishTransformStat(r._2)) + optReceiver.fold[Tree](Skip())(r => checkNotNullStatement(r._2)) val newArgs = args.map(finishTransformStat(_)) Block(newOptReceiver :: newArgs) } @@ -2214,7 +2219,7 @@ private[optimizer] abstract class OptimizerCore(config: CommonPhaseConfig) { case This() if args.isEmpty => assert(optReceiver.isDefined, "There was a This(), there should be a receiver") - cont(optReceiver.get._2) + cont(checkNotNull(optReceiver.get._2)) case Select(This(), className, field) if formals.isEmpty => assert(optReceiver.isDefined, @@ -2233,8 +2238,7 @@ private[optimizer] abstract class OptimizerCore(config: CommonPhaseConfig) { if (!isFieldRead(className, field.name)) { // Field is never read, discard assign, keep side effects only. - cont(PreTransTree(Block(finishTransformStat(treceiver), - finishTransformStat(trhs)))) + cont(PreTransTree(finishTransformArgsAsStat(), RefinedType.NoRefinedType)) } else { pretransformSelectCommon(lhs.tpe, treceiver, className, field, isLhsOfAssign = true) { tlhs => @@ -2261,12 +2265,13 @@ private[optimizer] abstract class OptimizerCore(config: CommonPhaseConfig) { * must communicate to the emitter that it has to unbox the value. * For other primitive types, unboxes/casts are not necessary, because * they would only convert `null` to the zero value of the type. However, - * calling a method on `null` is UB, so we need not do anything about it. + * `null` is ruled out by `checkNotNull` (or because it is UB). */ val (declaredType, value0) = receiver + val value1 = checkNotNull(value0) val value = - if (declaredType == CharType) foldAsInstanceOf(value0, declaredType) - else value0 + if (declaredType == CharType) foldAsInstanceOf(value1, declaredType) + else value1 Binding(Binding.This, declaredType, false, value) } @@ -2340,8 +2345,17 @@ private[optimizer] abstract class OptimizerCore(config: CommonPhaseConfig) { case ArrayCopy => assert(isStat, "System.arraycopy must be used in statement position") - val List(src, srcPos, dest, destPos, length) = targs.map(finishTransformExpr(_)) - contTree(Transient(SystemArrayCopy(src, srcPos, dest, destPos, length))) + val List(tsrc, tsrcPos, tdest, tdestPos, tlength) = targs + withNewTempLocalDefs(targs) { (localDefs, cont1) => + val List(srcDef, srcPosDef, destDef, destPosDef, lengthDef) = localDefs + cont1(PreTransTree(Transient(SystemArrayCopy( + finishTransformExpr(checkNotNull(srcDef.toPreTransform)), + srcPosDef.newReplacement, + finishTransformExpr(checkNotNull(destDef.toPreTransform)), + destPosDef.newReplacement, + lengthDef.newReplacement + )))) + } (cont) // scala.runtime.ScalaRunTime object @@ -2363,7 +2377,7 @@ private[optimizer] abstract class OptimizerCore(config: CommonPhaseConfig) { */ val elemType = cursoryArrayElemType(arrayTpe) if (!tarray.tpe.isNullable) { - val array = finishTransformExpr(tarray) + val array = finishTransformExprMaybeAssumeNotNull(tarray) val index = finishTransformExpr(tindex) val select = ArraySelect(array, index)(elemType) contTree(select) @@ -2387,7 +2401,7 @@ private[optimizer] abstract class OptimizerCore(config: CommonPhaseConfig) { */ val elemType = cursoryArrayElemType(arrayTpe) if (!tarray.tpe.isNullable) { - val array = finishTransformExpr(tarray) + val array = finishTransformExprMaybeAssumeNotNull(tarray) val index = finishTransformExpr(tindex) val select = ArraySelect(array, index)(elemType) val tunboxedValue = foldAsInstanceOf(tvalue, elemType) @@ -2410,7 +2424,8 @@ private[optimizer] abstract class OptimizerCore(config: CommonPhaseConfig) { val tarray = targs.head tarray.tpe.base match { case _: ArrayType => - contTree(Trees.ArrayLength(finishTransformExpr(tarray))) + val array = finishTransformExprMaybeAssumeNotNull(tarray) + contTree(Trees.ArrayLength(array)) case _ => default } @@ -2452,6 +2467,7 @@ private[optimizer] abstract class OptimizerCore(config: CommonPhaseConfig) { // scala.collection.mutable.ArrayBuilder case GenericArrayBuilderResult => + // This is a private API: `runtimeClass` is known not to be `null` val List(runtimeClass, array) = targs.map(finishTransformExpr(_)) val (resultType, isExact) = runtimeClass match { case ClassOf(elemTypeRef) => (ArrayType(ArrayTypeRef.of(elemTypeRef)), true) @@ -2462,6 +2478,7 @@ private[optimizer] abstract class OptimizerCore(config: CommonPhaseConfig) { RefinedType(resultType, isExact = isExact, isNullable = false))) case ArrayBuilderZeroOf => + // This is a private API: `runtimeClass` is known not to be `null` contTree(finishTransformExpr(targs.head) match { case ClassOf(PrimRef(tpe)) => /* Note that for CharType we produce a literal int instead of char. @@ -2594,17 +2611,17 @@ private[optimizer] abstract class OptimizerCore(config: CommonPhaseConfig) { // TypedArray conversions case ByteArrayToInt8Array => - contTree(Transient(ArrayToTypedArray(finishTransformExpr(targs.head), ByteRef))) + contTree(Transient(ArrayToTypedArray(finishTransformExprMaybeAssumeNotNull(targs.head), ByteRef))) case ShortArrayToInt16Array => - contTree(Transient(ArrayToTypedArray(finishTransformExpr(targs.head), ShortRef))) + contTree(Transient(ArrayToTypedArray(finishTransformExprMaybeAssumeNotNull(targs.head), ShortRef))) case CharArrayToUint16Array => - contTree(Transient(ArrayToTypedArray(finishTransformExpr(targs.head), CharRef))) + contTree(Transient(ArrayToTypedArray(finishTransformExprMaybeAssumeNotNull(targs.head), CharRef))) case IntArrayToInt32Array => - contTree(Transient(ArrayToTypedArray(finishTransformExpr(targs.head), IntRef))) + contTree(Transient(ArrayToTypedArray(finishTransformExprMaybeAssumeNotNull(targs.head), IntRef))) case FloatArrayToFloat32Array => - contTree(Transient(ArrayToTypedArray(finishTransformExpr(targs.head), FloatRef))) + contTree(Transient(ArrayToTypedArray(finishTransformExprMaybeAssumeNotNull(targs.head), FloatRef))) case DoubleArrayToFloat64Array => - contTree(Transient(ArrayToTypedArray(finishTransformExpr(targs.head), DoubleRef))) + contTree(Transient(ArrayToTypedArray(finishTransformExprMaybeAssumeNotNull(targs.head), DoubleRef))) case Int8ArrayToByteArray => contTree(Transient(TypedArrayToArray(finishTransformExpr(targs.head), ByteRef))) @@ -4426,8 +4443,22 @@ private[optimizer] abstract class OptimizerCore(config: CommonPhaseConfig) { } } - private def transformMethodDefBody(target: MethodID, thisType: Type, - params: List[ParamDef], resultType: Type, body: Tree): (List[ParamDef], Tree) = { + private def transformMethodDefBody(optTarget: Option[MethodID], thisType: Type, + params: List[ParamDef], jsClassCaptures: List[ParamDef], resultType: Type, + body: Tree, isNoArgCtor: Boolean): (List[ParamDef], Tree) = { + + val jsClassCaptureLocalDefs = for { + ParamDef(LocalIdent(name), _, ptpe, mutable) <- jsClassCaptures + } yield { + /* Reserve capture name: They have the same name for the whole class + * definition, so we cannot rename them. + */ + localNameAllocator.reserve(name) + + val replacement = ReplaceWithVarRef(name, newSimpleState(Unused), None) + val localDef = LocalDef(RefinedType(ptpe), mutable, replacement) + name -> localDef + } val (paramLocalDefs, newParamDefs) = params.map(newParamReplacement(_)).unzip @@ -4435,21 +4466,29 @@ private[optimizer] abstract class OptimizerCore(config: CommonPhaseConfig) { if (thisType == NoType) None else Some(newThisLocalDef(thisType)) - val inlining = { - val allocationSiteCount = - paramLocalDefs.size + (if (thisLocalDef.isDefined) 1 else 0) - val allocationSites = - List.fill(allocationSiteCount)(AllocationSite.Anonymous) - allocationSites -> target - } - val env = OptEnv.Empty .withThisLocalDef(thisLocalDef) .withLocalDefs(paramLocalDefs) + .withLocalDefs(jsClassCaptureLocalDefs) - val scope = Scope.Empty.inlining(inlining).withEnv(env) + val scope = { + val scope0 = Scope.Empty.withEnv(env) - val newBody = transform(body, resultType == NoType)(scope) + optTarget.fold(scope0) { target => + val allocationSiteCount = + paramLocalDefs.size + (if (thisLocalDef.isDefined) 1 else 0) + val allocationSites = + List.fill(allocationSiteCount)(AllocationSite.Anonymous) + + scope0.inlining(allocationSites -> target) + } + } + + val newBody0 = transform(body, resultType == NoType)(scope) + + val newBody = + if (isNoArgCtor) tryElimStoreModule(newBody0) + else newBody0 (newParamDefs, newBody) } @@ -4600,6 +4639,80 @@ private[optimizer] abstract class OptimizerCore(config: CommonPhaseConfig) { } } + private def checkNotNull(texpr: PreTransform)(implicit pos: Position): PreTransform = { + val tpe = texpr.tpe + + if (!tpe.isNullable || semantics.nullPointers == CheckedBehavior.Unchecked) { + texpr + } else { + val refinedType: RefinedType = tpe.base match { + case NullType => RefinedType.Nothing + case baseType => RefinedType(baseType, isExact = tpe.isExact, isNullable = false) + } + PreTransTree(Transient(CheckNotNull(finishTransformExpr(texpr))), refinedType) + } + } + + private def checkNotNull(expr: Tree)(implicit pos: Position): Tree = { + if (semantics.nullPointers == CheckedBehavior.Unchecked || isNotNull(expr)) + expr + else + Transient(CheckNotNull(expr)) + } + + private def checkNotNullStatement(texpr: PreTransform)(implicit pos: Position): Tree = { + if (!texpr.tpe.isNullable || semantics.nullPointers == CheckedBehavior.Unchecked) + finishTransformStat(texpr) + else + Transient(CheckNotNull(finishTransformExpr(texpr))) + } + + private def checkNotNullStatement(expr: Tree)(implicit pos: Position): Tree = { + if (semantics.nullPointers == CheckedBehavior.Unchecked || isNotNull(expr)) + keepOnlySideEffects(expr) + else + Transient(CheckNotNull(expr)) + } + + private def maybeAssumeNotNull(tree: Tree, tpe: RefinedType): Tree = { + if (tpe.isNullable || semantics.nullPointers == CheckedBehavior.Unchecked) { + tree + } else { + /* Do not introduce AssumeNotNull for some tree shapes that the function + * emitter will trivially recognize as non-null. This is particularly + * important not to hide `This` nodes in a way that prevents elimination + * of `StoreModule`s. + */ + if (isNotNull(tree)) + tree + else + Transient(AssumeNotNull(tree))(tree.pos) + } + } + + private def isNotNull(tree: Tree): Boolean = { + // !!! Duplicate code with FunctionEmitter.isNotNull + + def isNullableType(tpe: Type): Boolean = tpe match { + case NullType => true + case _: PrimType => false + case _ => true + } + + def isShapeNotNull(tree: Tree): Boolean = tree match { + case Transient(CheckNotNull(_) | AssumeNotNull(_)) => + true + case _: This => + tree.tpe != AnyType + case _:New | _:LoadModule | _:NewArray | _:ArrayValue | _:Clone | _:ClassOf => + true + case _ => + false + } + + !isNullableType(tree.tpe) || isShapeNotNull(tree) + } + private def newParamReplacement(paramDef: ParamDef): ((LocalName, LocalDef), ParamDef) = { val ParamDef(ident @ LocalIdent(name), originalName, ptpe, mutable) = paramDef @@ -4967,7 +5080,6 @@ private[optimizer] object OptimizerCore { private val AnyArgConstructorName = MethodName.constructor(List(ClassRef(ObjectClass))) - private val ObjectCloneName = MethodName("clone", Nil, ClassRef(ObjectClass)) private val TupleFirstMethodName = MethodName("_1", Nil, ClassRef(ObjectClass)) private val TupleSecondMethodName = MethodName("_2", Nil, ClassRef(ObjectClass)) private val ClassTagApplyMethodName = @@ -5866,20 +5978,30 @@ private[optimizer] object OptimizerCore { this.enclosingClassName == className && this.methodName == methodName } - /** Parts of [[GenIncOptimizer#MethodImpl]] with decisions about optimizations. */ - abstract class MethodImpl { - def enclosingClassName: ClassName - def methodName: MethodName - def optimizerHints: OptimizerHints - def originalDef: MethodDef - def thisType: Type + /* This is a "broken" case class so we get equals (and hashCode) for free. + * + * This hack is somewhat acceptable, because: + * - it is only part of the OptimizerCore / IncOptimizer interface. + * - the risk of getting equals wrong is high: it only affects the incremental + * behavior of the optimizer, which we have few tests for. + */ + final case class MethodAttributes private[OptimizerCore] ( + private[OptimizerCore] val inlineable: Boolean, + private[OptimizerCore] val shouldInline: Boolean, + private[OptimizerCore] val isForwarder: Boolean, + private[OptimizerCore] val jsDynImportInlineTarget: Option[ImportTarget], + private[OptimizerCore] val jsDynImportThunkFor: Option[MethodName] + ) - protected def computeNewAttributes(): MethodAttributes = { - val MethodDef(_, MethodIdent(methodName), _, params, _, optBody) = originalDef + object MethodAttributes { + def compute(enclosingClassName: ClassName, methodDef: MethodDef): MethodAttributes = { + val MethodDef(_, MethodIdent(methodName), _, params, _, optBody) = methodDef val body = optBody getOrElse { throw new AssertionError("Methods in optimizer must be concrete") } + val optimizerHints = methodDef.optimizerHints + val isForwarder = body match { // Shape of forwarders to trait impls case ApplyStatic(_, impl, method, args) => @@ -5980,21 +6102,6 @@ private[optimizer] object OptimizerCore { } } - /* This is a "broken" case class so we get equals (and hashCode) for free. - * - * This hack is somewhat acceptable, because: - * - it is only part of the OptimizerCore / IncOptimizer interface. - * - the risk of getting equals wrong is high: it only affects the incremental - * behavior of the optimizer, which we have few tests for. - */ - final case class MethodAttributes private[OptimizerCore] ( - private[OptimizerCore] val inlineable: Boolean, - private[OptimizerCore] val shouldInline: Boolean, - private[OptimizerCore] val isForwarder: Boolean, - private[OptimizerCore] val jsDynImportInlineTarget: Option[ImportTarget], - private[OptimizerCore] val jsDynImportThunkFor: Option[MethodName] - ) - sealed trait ImportTarget object ImportTarget { @@ -6097,13 +6204,11 @@ private[optimizer] object OptimizerCore { } } - private def exceptionMsg(myself: AbstractMethodID, + private def exceptionMsg(debugID: String, attemptedInlining: List[AbstractMethodID], cause: Throwable) = { val buf = new StringBuilder() - buf.append("The Scala.js optimizer crashed while optimizing " + myself + - ": " + cause.toString) - + buf.append(s"The Scala.js optimizer crashed while optimizing $debugID: $cause") buf.append("\nMethods attempted to inline:\n") for (m <- attemptedInlining) { @@ -6122,9 +6227,9 @@ private[optimizer] object OptimizerCore { val savedStateBackupChain: List[StateBackup], val cont: () => TailRec[Tree]) extends ControlThrowable - class OptimizeException(val myself: AbstractMethodID, + class OptimizeException(val debugID: String, val attemptedInlining: List[AbstractMethodID], cause: Throwable - ) extends Exception(exceptionMsg(myself, attemptedInlining, cause), cause) + ) extends Exception(exceptionMsg(debugID, attemptedInlining, cause), cause) private abstract class FreshNameAllocator[N <: Name] private ( initialMap: Map[N, Int]) { @@ -6154,6 +6259,14 @@ private[optimizer] object OptimizerCore { protected def nameWithSuffix(name: N, suffix: String): N + /** Reserves the provided name to not be allocated. + * + * May only be called on a "cleared" instance (i.e. [[freshName]] has not + * been called yet or clear has just been called). + */ + def reserve(name: N): Unit = + usedNamesToNextCounter = usedNamesToNextCounter.updated(name, 1) + def snapshot(): Snapshot[N] = new Snapshot(usedNamesToNextCounter) def restore(snapshot: Snapshot[N]): Unit = diff --git a/linker/shared/src/main/scala/org/scalajs/linker/standard/LinkedClass.scala b/linker/shared/src/main/scala/org/scalajs/linker/standard/LinkedClass.scala index fb34793699..3f796633ae 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/standard/LinkedClass.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/standard/LinkedClass.scala @@ -13,7 +13,7 @@ package org.scalajs.linker.standard import org.scalajs.ir.Trees._ -import org.scalajs.ir.{ClassKind, Position} +import org.scalajs.ir.{ClassKind, Position, Version} import org.scalajs.ir.Names.{ClassName, FieldName} /** A ClassDef after linking. @@ -40,9 +40,9 @@ final class LinkedClass( val jsSuperClass: Option[Tree], val jsNativeLoadSpec: Option[JSNativeLoadSpec], val fields: List[AnyFieldDef], - val methods: List[Versioned[MethodDef]], - val jsConstructorDef: Option[Versioned[JSConstructorDef]], - val exportedMembers: List[Versioned[JSMethodPropDef]], + val methods: List[MethodDef], + val jsConstructorDef: Option[JSConstructorDef], + val exportedMembers: List[JSMethodPropDef], val jsNativeMembers: List[JSNativeMemberDef], val optimizerHints: OptimizerHints, val pos: Position, @@ -59,13 +59,12 @@ final class LinkedClass( val externalDependencies: Set[String], val dynamicDependencies: Set[ClassName], - val version: Option[String]) { + val version: Version) { def className: ClassName = name.name val hasStaticInitializer: Boolean = { - methods.exists { m => - val methodDef = m.value + methods.exists { methodDef => methodDef.flags.namespace == MemberNamespace.StaticConstructor && methodDef.methodName.isStaticInitializer } @@ -80,92 +79,4 @@ final class LinkedClass( } def fullName: String = className.nameString - - private[linker] def refined( - kind: ClassKind, - fields: List[AnyFieldDef], - methods: List[Versioned[MethodDef]], - jsNativeMembers: List[JSNativeMemberDef], - hasInstances: Boolean, - hasInstanceTests: Boolean, - hasRuntimeTypeInfo: Boolean, - fieldsRead: Set[FieldName], - staticFieldsRead: Set[FieldName], - staticDependencies: Set[ClassName], - externalDependencies: Set[String], - dynamicDependencies: Set[ClassName] - ): LinkedClass = { - copy( - kind = kind, - fields = fields, - methods = methods, - jsNativeMembers = jsNativeMembers, - hasInstances = hasInstances, - hasInstanceTests = hasInstanceTests, - hasRuntimeTypeInfo = hasRuntimeTypeInfo, - fieldsRead = fieldsRead, - staticFieldsRead = staticFieldsRead, - staticDependencies = staticDependencies, - externalDependencies = externalDependencies, - dynamicDependencies = dynamicDependencies - ) - } - - private[linker] def optimized( - methods: List[Versioned[MethodDef]] - ): LinkedClass = { - copy(methods = methods) - } - - private def copy( - name: ClassIdent = this.name, - kind: ClassKind = this.kind, - jsClassCaptures: Option[List[ParamDef]] = this.jsClassCaptures, - superClass: Option[ClassIdent] = this.superClass, - interfaces: List[ClassIdent] = this.interfaces, - jsSuperClass: Option[Tree] = this.jsSuperClass, - jsNativeLoadSpec: Option[JSNativeLoadSpec] = this.jsNativeLoadSpec, - fields: List[AnyFieldDef] = this.fields, - methods: List[Versioned[MethodDef]] = this.methods, - jsConstructorDef: Option[Versioned[JSConstructorDef]] = this.jsConstructorDef, - exportedMembers: List[Versioned[JSMethodPropDef]] = this.exportedMembers, - jsNativeMembers: List[JSNativeMemberDef] = this.jsNativeMembers, - optimizerHints: OptimizerHints = this.optimizerHints, - pos: Position = this.pos, - ancestors: List[ClassName] = this.ancestors, - hasInstances: Boolean = this.hasInstances, - hasInstanceTests: Boolean = this.hasInstanceTests, - hasRuntimeTypeInfo: Boolean = this.hasRuntimeTypeInfo, - fieldsRead: Set[FieldName] = this.fieldsRead, - staticFieldsRead: Set[FieldName] = this.staticFieldsRead, - staticDependencies: Set[ClassName] = this.staticDependencies, - externalDependencies: Set[String] = this.externalDependencies, - dynamicDependencies: Set[ClassName] = this.dynamicDependencies, - version: Option[String] = this.version): LinkedClass = { - new LinkedClass( - name, - kind, - jsClassCaptures, - superClass, - interfaces, - jsSuperClass, - jsNativeLoadSpec, - fields, - methods, - jsConstructorDef, - exportedMembers, - jsNativeMembers, - optimizerHints, - pos, - ancestors, - hasInstances, - hasInstanceTests, - hasRuntimeTypeInfo, - fieldsRead, - staticFieldsRead, - staticDependencies, - externalDependencies, - dynamicDependencies, - version) - } } diff --git a/linker/shared/src/main/scala/org/scalajs/linker/standard/MemIRFile.scala b/linker/shared/src/main/scala/org/scalajs/linker/standard/MemIRFile.scala index 6315f2dc3d..fc2e39e690 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/standard/MemIRFile.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/standard/MemIRFile.scala @@ -24,7 +24,7 @@ import org.scalajs.linker.interface.unstable.IRFileImpl /** A simple in-memory virtual serialized Scala.js IR file. */ final class MemIRFileImpl( path: String, - version: Option[String], + version: ir.Version, content: Array[Byte] ) extends IRFileImpl(path, version) { def entryPointsInfo(implicit ec: ExecutionContext): Future[ir.EntryPointsInfo] = diff --git a/linker/shared/src/main/scala/org/scalajs/linker/standard/StandardIRFileCache.scala b/linker/shared/src/main/scala/org/scalajs/linker/standard/StandardIRFileCache.scala index 61d1d9f480..21408c3526 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/standard/StandardIRFileCache.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/standard/StandardIRFileCache.scala @@ -21,7 +21,7 @@ import java.net.URI import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.atomic.AtomicInteger -import org.scalajs.ir.EntryPointsInfo +import org.scalajs.ir.{EntryPointsInfo, Version} import org.scalajs.ir.Trees.ClassDef import org.scalajs.linker.interface._ @@ -121,7 +121,7 @@ final class StandardIRFileCache(config: IRFileCacheConfig) extends IRFileCacheIm * May only be written under synchronization, except if this is a tombstone */ @volatile - private[this] var _version: Option[String] = None + private[this] var _version: Version = Version.Unversioned /** Files in this [[PersistedFiles]] being calculated. * May only be written under synchronization, except if this is a tombstone @@ -173,7 +173,7 @@ final class StandardIRFileCache(config: IRFileCacheConfig) extends IRFileCacheIm */ globalCache.remove(path, this) // aggressively free stuff for GC - _version = null + _version = Version.Unversioned _files = null } @@ -185,17 +185,13 @@ final class StandardIRFileCache(config: IRFileCacheConfig) extends IRFileCacheIm assert(_references.get > 0, "Updating an unreferenced file") assert(file.path == path, s"Path mismatch: $path, ${file.path}") - // Helper to ensure v is stable during check - @inline - def upToDate(v: Option[String]) = v.isDefined && v == file.version - - if (upToDate(_version)) { + if (_version.sameVersion(file.version)) { // yeepeeh, nothing to do statsReused.incrementAndGet() } else { // We need to update this. We synchronize synchronized { - if (upToDate(_version)) { + if (_version.sameVersion(file.version)) { // someone else had the same idea and did our work statsReused.incrementAndGet() } else { diff --git a/linker/shared/src/main/scala/org/scalajs/linker/standard/Versioned.scala b/linker/shared/src/main/scala/org/scalajs/linker/standard/Versioned.scala deleted file mode 100644 index f8d5e00ab3..0000000000 --- a/linker/shared/src/main/scala/org/scalajs/linker/standard/Versioned.scala +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Scala.js (https://www.scala-js.org/) - * - * Copyright EPFL. - * - * Licensed under Apache License 2.0 - * (https://www.apache.org/licenses/LICENSE-2.0). - * - * See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - */ - -package org.scalajs.linker.standard - -/** A versioned thing, accompanied by its version. - * - * Note that, as used in `LinkingUnit`, the [[version]] is relative to the - * identity of the versioned thing. The definition of identity varies as - * items progress through the linking pipeline, but it only gets stronger, - * i.e., if two items are id-different at phase P, then they must also be - * id-different at phase P+1. The converse is not true. This guarantees that - * versions can be reliably used to determine at phase P+1 whether the given - * item coming from phase P must be reprocessed. - */ -final class Versioned[+T](val value: T, val version: Option[String]) 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 0f05ba1c79..01b834bdde 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/AnalyzerTest.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/AnalyzerTest.scala @@ -157,10 +157,12 @@ class AnalyzerTest { val classDefs = Seq( classDef("A", kind = kindSub, superClass = Some("B"), - memberDefs = requiredMemberDefs("A", kindSub)), + methods = requiredMethods("A", kindSub), + jsConstructor = requiredJSConstructor(kindSub)), classDef("B", kind = kindBase, superClass = validParentForKind(kindBase), - memberDefs = requiredMemberDefs("B", kindBase)) + methods = requiredMethods("B", kindBase), + jsConstructor = requiredJSConstructor(kindBase)) ) val analysis = computeAnalysis(classDefs, @@ -208,10 +210,12 @@ class AnalyzerTest { classDef("A", kind = kindCls, superClass = validParentForKind(kindCls), interfaces = List("B"), - memberDefs = requiredMemberDefs("A", kindCls)), + methods = requiredMethods("A", kindCls), + jsConstructor = requiredJSConstructor(kindCls)), classDef("B", kind = kindIntf, superClass = validParentForKind(kindIntf), - memberDefs = requiredMemberDefs("B", kindIntf)) + methods = requiredMethods("B", kindIntf), + jsConstructor = requiredJSConstructor(kindIntf)) ) val analysis = computeAnalysis(classDefs, @@ -230,7 +234,7 @@ class AnalyzerTest { def notAModule(): AsyncResult = await { val classDefs = Seq( classDef("A", superClass = Some(ObjectClass), - memberDefs = List(trivialCtor("A"))) + methods = List(trivialCtor("A"))) ) val analysis = computeAnalysis(classDefs, reqsFactory.accessModule("A")) @@ -244,7 +248,7 @@ class AnalyzerTest { def missingMethod(): AsyncResult = await { val classDefs = Seq( classDef("A", superClass = Some(ObjectClass), - memberDefs = List(trivialCtor("A"))) + methods = List(trivialCtor("A"))) ) val analysis = computeAnalysis(classDefs, @@ -262,11 +266,11 @@ class AnalyzerTest { val classDefs = Seq( classDef("A", superClass = Some(ObjectClass), - memberDefs = List(trivialCtor("A"))), + methods = List(trivialCtor("A"))), classDef("B", superClass = Some("A"), - memberDefs = List( + methods = List( trivialCtor("B"), - MethodDef(EMF, fooMethodName, NON, Nil, IntType, Some(int(5)))(EOH, None) + MethodDef(EMF, fooMethodName, NON, Nil, IntType, Some(int(5)))(EOH, UNV) )) ) @@ -286,11 +290,11 @@ class AnalyzerTest { val method = MethodDef( EMF.withNamespace(MemberNamespace.PublicStatic), mainName, NON, Nil, NoType, - Some(SelectJSNativeMember("A", testName)))(EOH, None) + Some(SelectJSNativeMember("A", testName)))(EOH, UNV) val classDefs = Seq( classDef("A", superClass = Some(ObjectClass), - memberDefs = List(method)) + methods = List(method)) ) val analysis = computeAnalysis(classDefs, @@ -305,15 +309,15 @@ class AnalyzerTest { @Test def conflictingDefaultMethods(): AsyncResult = await { val defaultMethodDef = MethodDef(EMF, m("foo", Nil, V), NON, Nil, - NoType, Some(Skip()))(EOH, None) + NoType, Some(Skip()))(EOH, UNV) val classDefs = Seq( classDef("I1", kind = ClassKind.Interface, - memberDefs = List(defaultMethodDef)), + methods = List(defaultMethodDef)), classDef("I2", kind = ClassKind.Interface, - memberDefs = List(defaultMethodDef)), + methods = List(defaultMethodDef)), classDef("A", superClass = Some(ObjectClass), interfaces = List("I1", "I2"), - memberDefs = List(trivialCtor("A"))) + methods = List(trivialCtor("A"))) ) val analysis = computeAnalysis(classDefs, @@ -339,12 +343,12 @@ class AnalyzerTest { "A", kind = ClassKind.ModuleClass, superClass = Some(ObjectClass), - memberDefs = List(trivialCtor("A")), + methods = List(trivialCtor("A")), topLevelExportDefs = List( TopLevelMethodExportDef("main", JSMethodDef( EMF.withNamespace(MemberNamespace.PublicStatic), str("default"), Nil, None, Undefined())( - EOH, None)) + EOH, UNV)) ) ) ) @@ -364,7 +368,7 @@ class AnalyzerTest { def singleDef(name: String) = { classDef(name, kind = ClassKind.ModuleClass, superClass = Some(ObjectClass), - memberDefs = List(trivialCtor(name)), + methods = List(trivialCtor(name)), topLevelExportDefs = List(TopLevelModuleExportDef(name, "foo"))) } @@ -387,7 +391,7 @@ class AnalyzerTest { def singleDef(name: String) = { classDef(name, kind = ClassKind.ModuleClass, superClass = Some(ObjectClass), - memberDefs = List(trivialCtor(name)), + methods = List(trivialCtor(name)), topLevelExportDefs = List(TopLevelModuleExportDef("main", "foo"))) } @@ -408,7 +412,7 @@ class AnalyzerTest { def degenerateConflictingTopLevelExports(): AsyncResult = await { val classDefs = Seq(classDef("A", kind = ClassKind.ModuleClass, superClass = Some(ObjectClass), - memberDefs = List(trivialCtor("A")), + methods = List(trivialCtor("A")), topLevelExportDefs = List( TopLevelModuleExportDef("main", "foo"), TopLevelModuleExportDef("main", "foo")))) @@ -423,7 +427,7 @@ class AnalyzerTest { def multipleModulesTopLevelExportAndModuleInitializer(): AsyncResult = await { val classDefs = Seq(classDef("A", kind = ClassKind.ModuleClass, superClass = Some(ObjectClass), - memberDefs = List( + methods = List( trivialCtor("A"), mainMethodDef(Skip()) ), @@ -476,14 +480,15 @@ class AnalyzerTest { val mainMethod = MethodDef( EMF.withNamespace(MemberNamespace.PublicStatic), mainName, NON, Nil, NoType, - Some(SelectJSNativeMember("A", testName)))(EOH, None) + Some(SelectJSNativeMember("A", testName)))(EOH, UNV) val nativeMember = JSNativeMemberDef( EMF.withNamespace(MemberNamespace.PublicStatic), testName, JSNativeLoadSpec.Import("my-module", List("test"))) val classDefs = Seq( classDef("A", superClass = Some(ObjectClass), - memberDefs = List(mainMethod, nativeMember)) + methods = List(mainMethod), + jsNativeMembers = List(nativeMember)) ) val analysis = computeAnalysis(classDefs, @@ -502,16 +507,16 @@ class AnalyzerTest { val classDefs = Seq( classDef("A", kind = ClassKind.ModuleClass, superClass = Some(ObjectClass), - memberDefs = List( + methods = List( trivialCtor("A"), mainMethodDef(ApplyDynamicImport(EAF, "B", dynName, Nil))) ), classDef("B", kind = ClassKind.Class, superClass = Some(ObjectClass), - memberDefs = List( + methods = List( MethodDef(EMF.withNamespace(MemberNamespace.PublicStatic), dynName, NON, Nil, AnyType, - Some(consoleLog(str("hello world"))))(EOH, None))) + Some(consoleLog(str("hello world"))))(EOH, UNV))) ) val moduleInitializer = ModuleInitializer.mainMethodWithArgs("A", "main") @@ -533,12 +538,12 @@ class AnalyzerTest { classDef("A", kind = ClassKind.JSClass, superClass = Some(JSObjectLikeClass), - memberDefs = List( + jsConstructor = Some( JSConstructorDef(JSCtorFlags, Nil, None, JSConstructorBody( Nil, JSSuperConstructorCall(Nil), JSNewTarget() :: Nil - ))(EOH, None) + ))(EOH, UNV) ) ), JSObjectLikeClassDef @@ -560,7 +565,7 @@ class AnalyzerTest { val classDefs = Seq( classDef("A", kind = ClassKind.ModuleClass, superClass = Some(ObjectClass), - memberDefs = List( + methods = List( trivialCtor("A"), mainMethodDef(JSImportMeta()) ) @@ -594,12 +599,12 @@ class AnalyzerTest { classDef("A", superClass = Some(ObjectClass)), classDef("B", superClass = Some("A")), classDef("X", superClass = Some(ObjectClass), - memberDefs = List( + methods = List( trivialCtor("X"), MethodDef(EMF, fooAMethodName, NON, Nil, ClassType("A"), - Some(Null()))(EOH, None), + Some(Null()))(EOH, UNV), MethodDef(EMF, fooBMethodName, NON, Nil, ClassType("B"), - Some(Null()))(EOH, None) + Some(Null()))(EOH, UNV) ) ) ) @@ -628,27 +633,27 @@ class AnalyzerTest { val classDefs = Seq( classDef("I1", kind = ClassKind.Interface, - memberDefs = List( - MethodDef(EMF, barMethodName, NON, Nil, IntType, None)(EOH, None) + methods = List( + MethodDef(EMF, barMethodName, NON, Nil, IntType, None)(EOH, UNV) )), classDef("I2", kind = ClassKind.Interface, - memberDefs = List( - MethodDef(EMF, barMethodName, NON, Nil, IntType, None)(EOH, None) + methods = List( + MethodDef(EMF, barMethodName, NON, Nil, IntType, None)(EOH, UNV) )), classDef("A", superClass = Some(ObjectClass), interfaces = List("I1"), - memberDefs = List( + methods = List( trivialCtor("A"), - MethodDef(EMF, fooMethodName, NON, Nil, IntType, None)(EOH, None) + MethodDef(EMF, fooMethodName, NON, Nil, IntType, None)(EOH, UNV) )), classDef("B", superClass = Some("A"), interfaces = List("I2"), - memberDefs = List( + methods = List( trivialCtor("B"), - MethodDef(EMF, fooMethodName, NON, Nil, IntType, Some(int(5)))(EOH, None) + MethodDef(EMF, fooMethodName, NON, Nil, IntType, Some(int(5)))(EOH, UNV) )), classDef("C", superClass = Some("B"), - memberDefs = List( + methods = List( trivialCtor("C"), - MethodDef(EMF, barMethodName, NON, Nil, IntType, Some(int(5)))(EOH, None) + MethodDef(EMF, barMethodName, NON, Nil, IntType, Some(int(5)))(EOH, UNV) )) ) diff --git a/linker/shared/src/test/scala/org/scalajs/linker/BackwardsCompatTest.scala b/linker/shared/src/test/scala/org/scalajs/linker/BackwardsCompatTest.scala index 43b6805e55..ca12a09277 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/BackwardsCompatTest.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/BackwardsCompatTest.scala @@ -65,7 +65,7 @@ class BackwardsCompatTest { classDef("A", superClass = Some(ObjectClass), interfaces = List(CloneableClass), - memberDefs = List(trivialCtor("A"))), + methods = List(trivialCtor("A"))), mainTestClassDef( systemOutPrintln(Apply(EAF, New("A", NoArgConstructorName, Nil), diff --git a/linker/shared/src/test/scala/org/scalajs/linker/FewestModulesSplittingTest.scala b/linker/shared/src/test/scala/org/scalajs/linker/FewestModulesSplittingTest.scala index b2b9693abe..74770e1505 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/FewestModulesSplittingTest.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/FewestModulesSplittingTest.scala @@ -95,12 +95,12 @@ class FewestModulesSplittingTest { def dynClass(i: Int, body: Tree): ClassDef = { val dynMethod = MethodDef( MemberFlags.empty.withNamespace(MemberNamespace.PublicStatic), - dynTargetName, NON, Nil, AnyType, Some(body))(EOH, None) + dynTargetName, NON, Nil, AnyType, Some(body))(EOH, UNV) classDef( className = "Dyn" + i, kind = ClassKind.Interface, - memberDefs = List(dynMethod) + methods = List(dynMethod) ) } diff --git a/linker/shared/src/test/scala/org/scalajs/linker/IRCheckerTest.scala b/linker/shared/src/test/scala/org/scalajs/linker/IRCheckerTest.scala index 872644f9f0..f53e421b43 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/IRCheckerTest.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/IRCheckerTest.scala @@ -56,7 +56,7 @@ class IRCheckerTest { classDef("Bar", superClass = Some(ObjectClass), - memberDefs = List( + methods = List( trivialCtor("Bar"), /* This method is called, but unreachable because there are no @@ -65,18 +65,18 @@ class IRCheckerTest { MethodDef(EMF, methMethodName, NON, List(paramDef("foo", ClassType("Foo"))), NoType, Some(Skip()))( - EOH, None) + EOH, UNV) ) ), classDef(MainTestClassName, superClass = Some(ObjectClass), - memberDefs = List( + methods = List( trivialCtor(MainTestClassName), MethodDef(EMF.withNamespace(MemberNamespace.PublicStatic), nullBarMethodName, NON, Nil, ClassType("Bar"), Some(Null()))( - EOH, None), + EOH, UNV), mainMethodDef(Block( callMethOn(ApplyStatic(EAF, MainTestClassName, nullBarMethodName, Nil)(ClassType("Bar"))), @@ -97,7 +97,7 @@ class IRCheckerTest { classDef("B", kind = ClassKind.NativeJSClass, superClass = Some(ObjectClass)), classDef("C", kind = ClassKind.NativeJSModuleClass, superClass = Some(ObjectClass)), - classDef("D", kind = ClassKind.JSClass, superClass = Some("A"), memberDefs = List(trivialJSCtor)), + classDef("D", kind = ClassKind.JSClass, superClass = Some("A"), jsConstructor = Some(trivialJSCtor)), mainTestClassDef(Block( LoadJSConstructor("B"), @@ -125,12 +125,12 @@ class IRCheckerTest { "Foo", kind = ClassKind.JSClass, superClass = Some(JSObjectLikeClass), - memberDefs = List( + jsConstructor = Some( JSConstructorDef(JSCtorFlags, Nil, None, JSConstructorBody( Nil, JSSuperConstructorCall(Nil), Nil - ))(EOH, None) + ))(EOH, UNV) ) ), @@ -154,12 +154,12 @@ class IRCheckerTest { "Foo", kind = ClassKind.JSClass, superClass = Some(JSObjectLikeClass), - memberDefs = List( + jsConstructor = Some( JSConstructorDef(JSCtorFlags, Nil, None, JSConstructorBody( Nil, JSSuperConstructorCall(Nil), VarDef("x", NON, IntType, mutable = false, int(5)) :: Nil - ))(EOH, None) + ))(EOH, UNV) ) ), @@ -189,12 +189,9 @@ object IRCheckerTest { val logger = new CapturingLogger - // We cannot use `transform` because of 2.11. - link(classDefs, moduleInitializers, logger).failed.recoverWith { - case _: NoSuchElementException => - Future.failed(new AssertionError("IR checking did not fail")) - }.map { _ => - logger.allLogLines + link(classDefs, moduleInitializers, logger).transform { + case Success(_) => Failure(new AssertionError("IR checking did not fail")) + case Failure(_) => Success(logger.allLogLines) } } diff --git a/linker/shared/src/test/scala/org/scalajs/linker/IncrementalTest.scala b/linker/shared/src/test/scala/org/scalajs/linker/IncrementalTest.scala index 09efc21eea..42ea84755a 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/IncrementalTest.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/IncrementalTest.scala @@ -23,6 +23,7 @@ import org.scalajs.ir.ClassKind import org.scalajs.ir.Names._ import org.scalajs.ir.Trees._ import org.scalajs.ir.Types._ +import org.scalajs.ir.Version import org.scalajs.logging._ @@ -48,22 +49,24 @@ class IncrementalTest { val staticMethodName = m("value", Nil, IntRef) def classDefs(pre: Boolean) = Seq( - mainTestClassDef( + v0 -> mainTestClassDef( consoleLog(JSMethodApply(LoadModule(FooClass), jsMethodName, Nil)) ), - classDef( + v(pre) -> classDef( FooClass, kind = ClassKind.ModuleClass, superClass = Some(ObjectClass), - memberDefs = List( - trivialCtor(FooClass), - JSMethodDef( - EMF, jsMethodName, Nil, None, - if (pre) int(5) - else ApplyStatic(EAF, FooClass, staticMethodName, Nil)(IntType))( - EOH, None), - MethodDef(EMF.withNamespace(MemberNamespace.PublicStatic), - staticMethodName, NON, Nil, IntType, Some(int(6)))(EOH, None) + methods = List( + trivialCtor(FooClass), + MethodDef(EMF.withNamespace(MemberNamespace.PublicStatic), + staticMethodName, NON, Nil, IntType, Some(int(6)))(EOH, UNV) + ), + jsMethodProps = List( + JSMethodDef( + EMF, jsMethodName, Nil, None, + if (pre) int(5) + else ApplyStatic(EAF, FooClass, staticMethodName, Nil)(IntType))( + EOH, UNV) ) ) ) @@ -79,18 +82,18 @@ class IncrementalTest { val x = LocalName("x") - def classDefs(pre: Boolean): Seq[ClassDef] = Seq( - mainTestClassDef({ + def classDefs(pre: Boolean) = Seq( + v0 -> mainTestClassDef({ consoleLog(Apply(EAF, New(FooClass, NoArgConstructorName, Nil), foo, List(int(5)))(IntType)) }), - classDef( + v(pre) -> classDef( FooClass, superClass = Some(ObjectClass), - memberDefs = List( + methods = List( trivialCtor(FooClass), MethodDef(EMF, foo, NON, List(paramDef(x, IntType)), IntType, Some(VarRef(x)(IntType)))( - EOH.withNoinline(pre), None) + EOH.withNoinline(pre), UNV) ) ) ) @@ -106,21 +109,21 @@ class IncrementalTest { val x = LocalName("x") - def classDefs(pre: Boolean): Seq[ClassDef] = Seq( - mainTestClassDef({ + def classDefs(pre: Boolean) = Seq( + v0 -> mainTestClassDef({ consoleLog(Apply(EAF, New(FooClass, NoArgConstructorName, Nil), foo, List(int(5)))(IntType)) }), - classDef( + v(pre) -> classDef( FooClass, superClass = Some(ObjectClass), - memberDefs = List( + methods = List( trivialCtor(FooClass), MethodDef(EMF, foo, NON, List(paramDef(x, IntType)), IntType, Some(Block( consoleLog(VarRef(x)(IntType)), VarRef(x)(IntType) )))( - EOH.withInline(pre), None) + EOH.withInline(pre), UNV) ) ) ) @@ -150,9 +153,9 @@ class IncrementalTest { val methParamDefs = List(paramDef(foo1, Foo1Type), paramDef(x, IntType)) - def classDefs(pre: Boolean): List[ClassDef] = List( + def classDefs(pre: Boolean) = List( // Main - mainTestClassDef(Block( + v0 -> mainTestClassDef(Block( VarDef(foo1, NON, Foo1Type, mutable = false, New(Foo1Class, NoArgConstructorName, Nil)), VarDef(bar, NON, BarType, mutable = false, If(AsInstanceOf(JSGlobalRef("randomBool"), BooleanType), @@ -163,27 +166,32 @@ class IncrementalTest { )), // Bar - classDef(BarInterface, kind = ClassKind.Interface, memberDefs = List( + v0 -> classDef(BarInterface, kind = ClassKind.Interface, methods = List( MethodDef(EMF, meth, NON, methParamDefs, IntType, Some({ BinaryOp(BinaryOp.Int_+, int(5), BinaryOp(BinaryOp.Int_*, xRef, int(2))) - }))(EOH, None) + }))(EOH, UNV) )), // Foo1 - classDef(Foo1Class, superClass = Some(ObjectClass), interfaces = List(BarInterface), memberDefs = List( - trivialCtor(Foo1Class), - MethodDef(EMF, meth, NON, methParamDefs, IntType, Some({ - ApplyStatically(EAF, if (pre) This()(Foo1Type) else foo1Ref, - BarInterface, meth, List(foo1Ref, xRef))(IntType) - }))(EOH, None) - )), + v(pre) -> classDef( + Foo1Class, + superClass = Some(ObjectClass), + interfaces = List(BarInterface), + methods = List( + trivialCtor(Foo1Class), + MethodDef(EMF, meth, NON, methParamDefs, IntType, Some({ + ApplyStatically(EAF, if (pre) This()(Foo1Type) else foo1Ref, + BarInterface, meth, List(foo1Ref, xRef))(IntType) + }))(EOH, UNV) + ) + ), // Foo2 - classDef(Foo2Class, superClass = Some(ObjectClass), interfaces = List(BarInterface), memberDefs = List( + v0 -> classDef(Foo2Class, superClass = Some(ObjectClass), interfaces = List(BarInterface), methods = List( trivialCtor(Foo2Class), MethodDef(EMF, meth, NON, methParamDefs, IntType, Some({ ApplyStatically(EAF, This()(Foo2Type), BarInterface, meth, List(foo1Ref, xRef))(IntType) - }))(EOH, None) + }))(EOH, UNV) )) ) @@ -198,12 +206,12 @@ class IncrementalTest { val meth2 = m("meth2", Nil, VoidRef) def methDef(name: MethodName, body: Tree): MethodDef = - MethodDef(EMF, name, NON, Nil, NoType, Some(body))(EOH.withNoinline(true), None) + MethodDef(EMF, name, NON, Nil, NoType, Some(body))(EOH.withNoinline(true), UNV) def callMeth(targetMeth: MethodName): Tree = Apply(EAF, LoadModule(FooClass), targetMeth, Nil)(NoType) - def classDefs(step: Int): List[ClassDef] = { + def classDefs(step: Int) = { val stepDependentMembers = step match { case 0 => List( @@ -227,11 +235,13 @@ class IncrementalTest { case 2 => List(callMeth(meth1), callMeth(meth2)) } + val v = Version.fromInt(step) + List( - classDef(FooClass, kind = ClassKind.ModuleClass, superClass = Some(ObjectClass), - memberDefs = trivialCtor(FooClass) :: stepDependentMembers), + v -> classDef(FooClass, kind = ClassKind.ModuleClass, superClass = Some(ObjectClass), + methods = trivialCtor(FooClass) :: stepDependentMembers), - mainTestClassDef(Block(stepDependentMainStats)) + v -> mainTestClassDef(Block(stepDependentMainStats)) ) } @@ -255,13 +265,13 @@ class IncrementalTest { def methDef(name: MethodName, body: Tree): MethodDef = { MethodDef(EMF.withNamespace(MemberNamespace.PublicStatic), name, NON, Nil, NoType, Some(body))( - EOH.withNoinline(true), None) + EOH.withNoinline(true), UNV) } def callMeth(targetMeth: MethodName): Tree = ApplyStatic(EAF, FooClass, targetMeth, Nil)(NoType) - def classDefs(step: Int): List[ClassDef] = { + def classDefs(step: Int) = { val stepDependentMembers = step match { case 0 => List( @@ -285,11 +295,13 @@ class IncrementalTest { case 2 => List(callMeth(meth1), callMeth(meth2)) } + val v = Version.fromInt(step) + List( - classDef(FooClass, superClass = Some(ObjectClass), - memberDefs = trivialCtor(FooClass) :: stepDependentMembers), + v -> classDef(FooClass, superClass = Some(ObjectClass), + methods = trivialCtor(FooClass) :: stepDependentMembers), - mainTestClassDef(Block(stepDependentMainStats)) + v -> mainTestClassDef(Block(stepDependentMainStats)) ) } @@ -315,31 +327,146 @@ class IncrementalTest { MethodDef(MemberFlags.empty.withNamespace(MemberNamespace.Constructor), MethodIdent(NoArgConstructorName), NON, Nil, NoType, - Some(body))(EOH, None) + Some(body))(EOH, UNV) } - def classDefs(pre: Boolean): Seq[ClassDef] = Seq( - mainTestClassDef(Block( + def classDefs(pre: Boolean) = Seq( + v0 -> mainTestClassDef(Block( consoleLog(str("foo")), LoadModule(FooModule) )), - classDef( + v(pre) -> classDef( FooModule, kind = ClassKind.ModuleClass, superClass = Some(ObjectClass), - memberDefs = List(fooCtor(pre)) + methods = List(fooCtor(pre)) ) ) testIncrementalBidirectional(classDefs(_), _ => MainTestModuleInitializers) } + @Test + def testInvalidateExportedMethods_Issue4774(): AsyncResult = await { + val AModule = ClassName("A") + val BModule = ClassName("B") + + val jsMethodName = str("foo") + val targetMethodName = m("value", Nil, IntRef) + + def classDefs(pre: Boolean) = Seq( + v0 -> mainTestClassDef( + consoleLog(JSMethodApply(LoadModule(AModule), jsMethodName, Nil)) + ), + v0 -> classDef( + AModule, + kind = ClassKind.ModuleClass, + superClass = Some(ObjectClass), + methods = List( + trivialCtor(AModule) + ), + jsMethodProps = List( + JSMethodDef(EMF, str("foo"), Nil, None, + Apply(EAF, LoadModule(BModule), targetMethodName, Nil)(IntType))(EOH, UNV) + ) + ), + v(pre) -> classDef( + BModule, + kind = ClassKind.ModuleClass, + superClass = Some(ObjectClass), + methods = List( + trivialCtor(BModule), + MethodDef(EMF, targetMethodName, NON, Nil, IntType, + Some(int(if (pre) 1 else 2)))(EOH.withInline(true), UNV) + ) + ) + ) + + testIncrementalBidirectional(classDefs(_), _ => MainTestModuleInitializers) + } + + @Test + def testInvalidateJSCtor_Issue4774(): AsyncResult = await { + val AClass = ClassName("A") + val BModule = ClassName("B") + val JSObject = ClassName("jso") + + val jsMethodName = str("foo") + val targetMethodName = m("value", Nil, IntRef) + + def classDefs(pre: Boolean) = Seq( + v0 -> mainTestClassDef( + consoleLog(JSNew(LoadJSConstructor(AClass), Nil)) + ), + v0 -> classDef( + JSObject, + kind = ClassKind.NativeJSClass, + jsNativeLoadSpec = Some(JSNativeLoadSpec.Global("Object", Nil)), + superClass = Some(ObjectClass) + ), + v0 -> classDef( + AClass, + kind = ClassKind.JSClass, + superClass = Some(JSObject), + jsConstructor = Some( + JSConstructorDef(EMF.withNamespace(MemberNamespace.Constructor), Nil, None, + JSConstructorBody(Nil, JSSuperConstructorCall(Nil), List({ + consoleLog(Apply(EAF, LoadModule(BModule), targetMethodName, Nil)(IntType)) + })))(EOH, UNV) + ) + ), + v(pre) -> classDef( + BModule, + kind = ClassKind.ModuleClass, + superClass = Some(ObjectClass), + methods = List( + trivialCtor(BModule), + MethodDef(EMF, targetMethodName, NON, Nil, IntType, + Some(int(if (pre) 1 else 2)))(EOH.withInline(true), UNV) + ) + ) + ) + + testIncrementalBidirectional(classDefs(_), _ => MainTestModuleInitializers) + } + + @Test + def testInvalidateTopLevelExportDependency(): AsyncResult = await { + val AModule = ClassName("A") + val BModule = ClassName("B") + + val targetMethodName = m("value", Nil, IntRef) + + def classDefs(pre: Boolean) = Seq( + v0 -> classDef( + AModule, + kind = ClassKind.Interface, + topLevelExportDefs = List( + TopLevelMethodExportDef("main", JSMethodDef(EMF.withNamespace(MemberNamespace.PublicStatic), + str("foo"), Nil, None, + Apply(EAF, LoadModule(BModule), targetMethodName, Nil)(IntType))(EOH, UNV)) + ) + ), + v(pre) -> classDef( + BModule, + kind = ClassKind.ModuleClass, + superClass = Some(ObjectClass), + methods = List( + trivialCtor(BModule), + MethodDef(EMF, targetMethodName, NON, Nil, IntType, + Some(int(if (pre) 1 else 2)))(EOH.withInline(true), UNV) + ) + ) + ) + + testIncrementalBidirectional(classDefs(_), _ => Nil) + } } object IncrementalTest { def testIncrementalBidirectional( - classDefs: Boolean => Seq[ClassDef], + classDefs: Boolean => Seq[(Version, ClassDef)], moduleInitializers: Boolean => List[ModuleInitializer])( implicit ec: ExecutionContext): Future[Unit] = { @@ -360,7 +487,7 @@ object IncrementalTest { def testIncrementalSteps( contextMessage: String, steps: Int, - stepToClassDefs: Int => Seq[ClassDef], + stepToClassDefs: Int => Seq[(Version, ClassDef)], stepToModuleInitializers: Int => List[ModuleInitializer])( implicit ec: ExecutionContext): Future[Unit] = { @@ -380,7 +507,7 @@ object IncrementalTest { val outputBatch = MemOutputDirectory() val linkerBatch = StandardImpl.linker(config) - val irFiles = minilib ++ stepToClassDefs(step).map(MemClassDefIRFile(_)) + val irFiles = minilib ++ stepToClassDefs(step).map(x => MemClassDefIRFile(x._2, x._1)) val moduleInitializers = stepToModuleInitializers(step) val thisStepResult = for { @@ -402,6 +529,10 @@ object IncrementalTest { } } + private val v0 = Version.fromInt(0) + private def v(pre: Boolean) = + Version.fromInt(if (pre) 0 else 1) + private def assertModulesEqual(msg: String, expected: Iterable[Report.Module], actual: Iterable[Report.Module]): Unit = { // Poor man's equality based on toString() diff --git a/linker/shared/src/test/scala/org/scalajs/linker/LibraryReachabilityTest.scala b/linker/shared/src/test/scala/org/scalajs/linker/LibraryReachabilityTest.scala index 8b8981f4cb..29166c621b 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/LibraryReachabilityTest.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/LibraryReachabilityTest.scala @@ -41,14 +41,14 @@ class LibraryReachabilityTest { val StringType = ClassType(BoxedStringClass) val classDefs = Seq( - classDef("A", superClass = Some(ObjectClass), memberDefs = List( + classDef("A", superClass = Some(ObjectClass), methods = List( trivialCtor("A"), MethodDef(EMF, m("test", Nil, V), NON, Nil, NoType, Some(Block( Apply(EAF, systemMod, m("getProperty", List(T), T), List(emptyStr))(StringType), Apply(EAF, systemMod, m("getProperty", List(T, T), T), List(emptyStr, emptyStr))(StringType), Apply(EAF, systemMod, m("setProperty", List(T, T), T), List(emptyStr, emptyStr))(StringType), Apply(EAF, systemMod, m("clearProperty", List(T), T), List(emptyStr))(StringType) - )))(EOH, None) + )))(EOH, UNV) )) ) @@ -70,11 +70,11 @@ class LibraryReachabilityTest { val formatMethod = m("format", List(T, ArrayTypeRef(O, 1)), T) val classDefs = Seq( - classDef("A", superClass = Some(ObjectClass), memberDefs = List( + classDef("A", superClass = Some(ObjectClass), methods = List( trivialCtor("A"), MethodDef(EMF, m("test", Nil, V), NON, Nil, NoType, Some(Block( ApplyStatic(EAF, BoxedStringClass, formatMethod, List(str("hello %d"), int(42)))(StringType) - )))(EOH, None) + )))(EOH, UNV) )) ) 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 013054f7f5..a64f546d68 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 = 146276, - expectedFullLinkSizeWithoutClosure = 129956, - expectedFullLinkSizeWithClosure = 21210, + expectedFastLinkSize = 150031, + expectedFullLinkSizeWithoutClosure = 130655, + expectedFullLinkSizeWithClosure = 21394, classDefs, moduleInitializers = MainTestModuleInitializers ) diff --git a/linker/shared/src/test/scala/org/scalajs/linker/OptimizerTest.scala b/linker/shared/src/test/scala/org/scalajs/linker/OptimizerTest.scala index 30bfd2da25..046a3ea860 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/OptimizerTest.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/OptimizerTest.scala @@ -56,7 +56,7 @@ class OptimizerTest { * `j.l.Object.clone()` is not otherwise reachable). */ private def testCloneOnArrayInliningGeneric(inlinedWhenOnObject: Boolean, - customMemberDefs: List[MemberDef]): Future[Unit] = { + customMethodDefs: List[MethodDef]): Future[Unit] = { val thisFoo = This()(ClassType("Foo")) val intArrayTypeRef = ArrayTypeRef(IntRef, 1) @@ -69,37 +69,37 @@ class OptimizerTest { val anObjectMethodName = m("anObject", Nil, O) def callCloneOn(receiver: Tree): Tree = - Apply(EAF, receiver, cloneMethodName, Nil)(AnyType) + consoleLog(Apply(EAF, receiver, cloneMethodName, Nil)(AnyType)) - val fooMemberDefs = List( + val fooMethodDefs = List( trivialCtor("Foo"), // @noinline def witness(): AnyRef = throw null MethodDef(EMF, witnessMethodName, NON, Nil, AnyType, Some { Throw(Null()) - })(EOH.withNoinline(true), None), + })(EOH.withNoinline(true), UNV), // @noinline def reachClone(): Object = clone() MethodDef(EMF, reachCloneMethodName, NON, Nil, AnyType, Some { Apply(EAF, thisFoo, cloneMethodName, Nil)(AnyType) - })(EOH.withNoinline(true), None), + })(EOH.withNoinline(true), UNV), // @noinline def anArray(): Array[Int] = Array(1) MethodDef(EMF, anArrayMethodName, NON, Nil, intArrayType, Some { anArrayOfInts - })(EOH.withNoinline(true), None), + })(EOH.withNoinline(true), UNV), // @noinline def anObject(): AnyRef = Array(1) MethodDef(EMF, anObjectMethodName, NON, Nil, AnyType, Some { anArrayOfInts - })(EOH.withNoinline(true), None) - ) ::: customMemberDefs + })(EOH.withNoinline(true), UNV) + ) ::: customMethodDefs val classDefs = Seq( classDef("Foo", superClass = Some(ObjectClass), interfaces = List("java.lang.Cloneable"), - memberDefs = fooMemberDefs + methods = fooMethodDefs ), mainTestClassDef(Block( // new Foo().reachClone() -- make Foo.clone() reachable for sure @@ -140,7 +140,7 @@ class OptimizerTest { // @inline override def clone(): AnyRef = witness() MethodDef(EMF, cloneMethodName, NON, Nil, AnyType, Some { Apply(EAF, This()(ClassType("Foo")), witnessMethodName, Nil)(AnyType) - })(EOH.withInline(true), None) + })(EOH.withInline(true), UNV) )) } @@ -168,7 +168,7 @@ class OptimizerTest { ApplyStatically(EAF, This()(ClassType("Foo")), ObjectClass, cloneMethodName, Nil)(AnyType) ) - })(EOH.withInline(true), None) + })(EOH.withInline(true), UNV) )) } @@ -200,16 +200,18 @@ class OptimizerTest { MainTestClassName, kind = ClassKind.Class, superClass = Some(ObjectClass), - memberDefs = List( + fields = List( + // static var foo: java.lang.String + FieldDef(EMF.withNamespace(PublicStatic).withMutable(true), + "foo", NON, StringType) + ), + methods = List( trivialCtor(MainTestClassName), - // static var foo: java.lang.String - FieldDef(EMF.withNamespace(PublicStatic).withMutable(true), - "foo", NON, StringType), // static def foo(): java.lang.String = Test::foo MethodDef(EMF.withNamespace(MemberNamespace.PublicStatic), fooGetter, NON, Nil, StringType, Some({ SelectStatic(MainTestClassName, "foo")(StringType) - }))(EOH, None), + }))(EOH, UNV), // static def main(args: String[]) { println(Test::foo()) } mainMethodDef({ consoleLog(ApplyStatic(EAF, MainTestClassName, fooGetter, Nil)(StringType)) @@ -304,12 +306,12 @@ class OptimizerTest { MainTestClassName, kind = ClassKind.Class, superClass = Some(ObjectClass), - memberDefs = List( + methods = List( trivialCtor(MainTestClassName), // @noinline static def sideEffect(x: Int): Int = x MethodDef(EMF.withNamespace(PublicStatic), sideEffect, NON, List(paramDef(x, IntType)), IntType, Some(VarRef(x)(IntType)))( - EOH.withNoinline(true), None), + EOH.withNoinline(true), UNV), /* static def main(args: String[]) { * console.log(arrow-lambda< * x1 = sideEffect(1), @@ -372,19 +374,19 @@ class OptimizerTest { classDef("Thunk", superClass = Some(ObjectClass), optimizerHints = EOH.withInline(true), - memberDefs = List( + methods = List( trivialCtor("Thunk"), MethodDef(EMF, implMethodName, NON, Nil, AnyType, Some { SelectJSNativeMember("Holder", memberMethodName) - })(EOH, None), + })(EOH, UNV), MethodDef(SMF, thunkMethodName, NON, Nil, AnyType, Some { val inst = New("Thunk", NoArgConstructorName, Nil) Apply(EAF, inst, implMethodName, Nil)(AnyType) - })(EOH, None) + })(EOH, UNV) ) ), classDef("Holder", kind = ClassKind.Interface, - memberDefs = List( + jsNativeMembers = List( JSNativeMemberDef(SMF, memberMethodName, JSNativeLoadSpec.Import("foo", List("bar"))) ) ) @@ -409,7 +411,7 @@ class OptimizerTest { } } - main.methods.foreach(v => traverser.traverseMemberDef(v.value)) + main.methods.foreach(traverser.traverseMethodDef(_)) assertTrue(foundJSImport) } @@ -446,10 +448,10 @@ class OptimizerTest { MainTestClassName, kind = ClassKind.Class, superClass = Some(ObjectClass), - memberDefs = List( + methods = List( // @noinline static def calc(): Int = 1 MethodDef(EMF.withNamespace(PublicStatic), calc, NON, Nil, - IntType, Some(int(1)))(EOH.withNoinline(true), None), + IntType, Some(int(1)))(EOH.withNoinline(true), UNV), mainMethodDef(Block( VarDef("x", NON, IntType, mutable = false, ApplyStatic(EAF, MainTestClassName, calc, Nil)(IntType)), @@ -484,32 +486,33 @@ class OptimizerTest { val witnessType = ClassType("Witness") - val fooMemberDefs = List( - // x: Witness - FieldDef(EMF.withMutable(witnessMutable), "x", NON, witnessType), - - // y: Int - FieldDef(EMF, "y", NON, IntType), - - // def this() = { - // this.x = null - // this.y = 5 - // } - MethodDef(EMF.withNamespace(Constructor), NoArgConstructorName, NON, Nil, NoType, Some(Block( - Assign(Select(This()(ClassType("Foo")), "Foo", "x")(witnessType), Null()), - Assign(Select(This()(ClassType("Foo")), "Foo", "y")(IntType), int(5)) - )))(EOH, None), - - // def method(): Int = this.y - MethodDef(EMF, methodName, NON, Nil, IntType, Some { - Select(This()(ClassType("Foo")), "Foo", "y")(IntType) - })(EOH, None) - ) - Seq( classDef("Witness", kind = ClassKind.Interface), classDef("Foo", kind = ClassKind.Class, superClass = Some(ObjectClass), - memberDefs = fooMemberDefs, optimizerHints = EOH.withInline(classInline)), + fields = List( + // x: Witness + FieldDef(EMF.withMutable(witnessMutable), "x", NON, witnessType), + + // y: Int + FieldDef(EMF, "y", NON, IntType) + ), + methods = List( + // def this() = { + // this.x = null + // this.y = 5 + // } + MethodDef(EMF.withNamespace(Constructor), NoArgConstructorName, NON, Nil, NoType, Some(Block( + Assign(Select(This()(ClassType("Foo")), "Foo", "x")(witnessType), Null()), + Assign(Select(This()(ClassType("Foo")), "Foo", "y")(IntType), int(5)) + )))(EOH, UNV), + + // def method(): Int = this.y + MethodDef(EMF, methodName, NON, Nil, IntType, Some { + Select(This()(ClassType("Foo")), "Foo", "y")(IntType) + })(EOH, UNV) + ), + optimizerHints = EOH.withInline(classInline) + ), mainTestClassDef({ consoleLog(Apply(EAF, New("Foo", NoArgConstructorName, Nil), methodName, Nil)(IntType)) }) @@ -569,7 +572,7 @@ object OptimizerTest { private def traverseMainMethod(moduleSet: ModuleSet)(f: Tree => Unit) = { val mainClassDef = findClass(moduleSet, MainTestClassName).get - val mainMethodDef = mainClassDef.methods.map(_.value) + val mainMethodDef = mainClassDef.methods .find(m => m.name.name == MainMethodName && m.flags.namespace == MemberNamespace.PublicStatic).get new Traverser { @@ -577,6 +580,6 @@ object OptimizerTest { f(tree) super.traverse(tree) } - }.traverseMemberDef(mainMethodDef) + }.traverseMethodDef(mainMethodDef) } } diff --git a/linker/shared/src/test/scala/org/scalajs/linker/SmallModulesForSplittingTest.scala b/linker/shared/src/test/scala/org/scalajs/linker/SmallModulesForSplittingTest.scala index aff889e2e1..2a8df79153 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/SmallModulesForSplittingTest.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/SmallModulesForSplittingTest.scala @@ -45,9 +45,9 @@ class SmallModulesForSplittingTest { def methodHolder(name: ClassName, body: Tree) = { classDef(name, kind = ClassKind.Interface, - memberDefs = List( + methods = List( MethodDef(SMF, methodName, NON, Nil, strClsType, Some(body))( - EOH.withNoinline(true), None) + EOH.withNoinline(true), UNV) )) } diff --git a/linker/shared/src/test/scala/org/scalajs/linker/SmallestModulesSplittingTest.scala b/linker/shared/src/test/scala/org/scalajs/linker/SmallestModulesSplittingTest.scala index 3ff90b20b9..f385497320 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/SmallestModulesSplittingTest.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/SmallestModulesSplittingTest.scala @@ -37,19 +37,19 @@ class SmallestModulesSplittingTest { val greetMethodName = m("greet", Nil, T) - val greeterMemberDefs = List( + val greeterMethods = List( trivialCtor("lib.Greeter"), // @noinline def greet(): String = "Hello world!" MethodDef(EMF, greetMethodName, NON, Nil, strClsType, Some { str("Hello world!") - })(EOH.withNoinline(true), None) + })(EOH.withNoinline(true), UNV) ) val classDefs = Seq( classDef("lib.Greeter", superClass = Some(ObjectClass), - memberDefs = greeterMemberDefs + methods = greeterMethods ), mainTestClassDef({ diff --git a/linker/shared/src/test/scala/org/scalajs/linker/checker/ClassDefCheckerTest.scala b/linker/shared/src/test/scala/org/scalajs/linker/checker/ClassDefCheckerTest.scala index ab357f2455..7337cc82fe 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/checker/ClassDefCheckerTest.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/checker/ClassDefCheckerTest.scala @@ -73,7 +73,9 @@ class ClassDefCheckerTest { for (kind <- kinds) { val name = if (kind == ClassKind.HijackedClass) BoxedIntegerClass else ClassName("A") assertError( - classDef(name, kind = kind, memberDefs = requiredMemberDefs(name, kind)), + classDef(name, kind = kind, + methods = requiredMethods(name, kind), + jsConstructor = requiredJSConstructor(kind)), "missing superClass") } } @@ -89,7 +91,7 @@ class ClassDefCheckerTest { def noDuplicateFields(): Unit = { assertError( classDef("A", superClass = Some(ObjectClass), - memberDefs = List( + fields = List( FieldDef(EMF, "foobar", NON, IntType), FieldDef(EMF, "foobar", NON, BooleanType) )), @@ -102,11 +104,11 @@ class ClassDefCheckerTest { assertError( classDef("A", superClass = Some(ObjectClass), - memberDefs = List( + methods = List( MethodDef(EMF, babarMethodName, NON, List(paramDef("x", IntType)), - IntType, None)(EOH, None), + IntType, None)(EOH, UNV), MethodDef(EMF, babarMethodName, NON, List(paramDef("y", IntType)), - IntType, None)(EOH, None) + IntType, None)(EOH, UNV) )), "duplicate method 'babar(int)int'") } @@ -126,20 +128,36 @@ class ClassDefCheckerTest { assertError( classDef(FooClass, superClass = Some(ObjectClass), - memberDefs = List( + methods = List( trivialCtor(FooClass), MethodDef(EMF.withNamespace(MemberNamespace.Constructor), stringCtorName, NON, List(paramDef("x", BoxedStringType)), NoType, Some(callPrimaryCtorBody))( - EOH, None), + EOH, UNV), MethodDef(EMF.withNamespace(MemberNamespace.Constructor), stringCtorName, NON, List(paramDef("y", BoxedStringType)), NoType, Some(callPrimaryCtorBody))( - EOH, None) + EOH, UNV) )), "duplicate constructor method '(java.lang.String)void'") } + @Test + def noStaticAbstractMethods(): Unit = { + val fooMethodName = MethodName("foo", Nil, IntRef) + + assertError( + classDef("A", + kind = ClassKind.Interface, + methods = List( + MethodDef(EMF.withNamespace(MemberNamespace.PublicStatic), + fooMethodName, NON, Nil, IntType, None)(EOH, UNV), + MethodDef(EMF, fooMethodName, NON, Nil, IntType, None)(EOH, UNV) // OK + ) + ), + "Abstract methods may only be in the public namespace") + } + @Test def noDuplicateVarDef(): Unit = { val body = Block( @@ -150,7 +168,7 @@ class ClassDefCheckerTest { ) assertError( - classDef("A", kind = ClassKind.Interface, memberDefs = List(mainMethodDef(body))), + classDef("A", kind = ClassKind.Interface, methods = List(mainMethodDef(body))), "Duplicate local variable name x." ) } @@ -163,7 +181,7 @@ class ClassDefCheckerTest { ) assertError( - classDef("A", kind = ClassKind.Interface, memberDefs = List(mainMethodDef(body))), + classDef("A", kind = ClassKind.Interface, methods = List(mainMethodDef(body))), "Duplicate local variable name x." ) } @@ -176,7 +194,7 @@ class ClassDefCheckerTest { ) assertError( - classDef("A", kind = ClassKind.Interface, memberDefs = List(mainMethodDef(body))), + classDef("A", kind = ClassKind.Interface, methods = List(mainMethodDef(body))), "Duplicate local variable name x." ) } @@ -191,10 +209,10 @@ class ClassDefCheckerTest { assertError( classDef( "Foo", superClass = Some(ObjectClass), - memberDefs = List( + methods = List( MethodDef(methodFlags, m("bar", Nil, V), NON, Nil, NoType, Some({ consoleLog(expr) - }))(EOH, None) + }))(EOH, UNV) ) ), expectedMsg) diff --git a/linker/shared/src/test/scala/org/scalajs/linker/standard/StandardIRFileCacheTest.scala b/linker/shared/src/test/scala/org/scalajs/linker/standard/StandardIRFileCacheTest.scala index 0dc47f1784..fa4b3adbf2 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/standard/StandardIRFileCacheTest.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/standard/StandardIRFileCacheTest.scala @@ -22,6 +22,7 @@ import org.scalajs.junit.async._ import org.scalajs.ir.EntryPointsInfo import org.scalajs.ir.Trees.ClassDef import org.scalajs.ir.Names.{ClassName, ObjectClass} +import org.scalajs.ir.Version.Unversioned import org.scalajs.linker.interface._ import org.scalajs.linker.interface.unstable._ @@ -80,7 +81,7 @@ class StandardIRFileCacheTest { object StandardIRFileCacheTest { final class MockIRContainer(path: String) - extends IRContainerImpl(path, version = None) { + extends IRContainerImpl(path, Unversioned) { private val files = List.tabulate(10)(i => new MockIRFile(f"$path.F$i")) private val _sjsirFiles = new MockOperation(files) @@ -91,7 +92,7 @@ object StandardIRFileCacheTest { _sjsirFiles.run() } - final class MockIRFile(path: String) extends IRFileImpl(path, version = None) { + final class MockIRFile(path: String) extends IRFileImpl(path, Unversioned) { private val className: ClassName = path private val _entryPointsInfo = diff --git a/linker/shared/src/test/scala/org/scalajs/linker/testutils/IRAssertions.scala b/linker/shared/src/test/scala/org/scalajs/linker/testutils/IRAssertions.scala index 6ece4b84d5..7443935882 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/testutils/IRAssertions.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/testutils/IRAssertions.scala @@ -34,109 +34,54 @@ object IRAssertions { implicit def linkedClassAssertions(linkedClass: LinkedClass): LinkedClassAssertions = new LinkedClassAssertions(linkedClass) - type Pat = PartialFunction[IRNode, Boolean] - - private def patToTotal(pat: Pat): IRNode => Boolean = - node => pat.applyOrElse(node, (_: IRNode) => false) - - abstract class AbstractIRNodeAssertions[T](selfNode: T) { - protected def newTraverser(f: IRNode => Unit): TestTraverser[T] + type Pat = PartialFunction[Tree, Boolean] + + abstract class AbstractIRNodeAssertions { + protected def startTraverse(traverser: Traverser): Unit + + private def countTrees(pf: Pat): Int = { + var count = 0 + val traverser = new Traverser { + override def traverse(tree: Tree): Unit = { + if (pf.applyOrElse(tree, (_: Tree) => false)) + count += 1 + super.traverse(tree) + } + } - private def find(pf: Pat): Boolean = - new TestFinder[T](patToTotal(pf))(newTraverser(_)).find(selfNode) + startTraverse(traverser) + count + } def has(trgName: String)(pf: Pat): this.type = { - assertTrue(s"AST should have $trgName", find(pf)) + assertTrue(s"AST should have $trgName", countTrees(pf) > 0) this } def hasNot(trgName: String)(pf: Pat): this.type = { - assertFalse(s"AST should not have $trgName", find(pf)) + assertTrue(s"AST should not have $trgName", countTrees(pf) == 0) this } def hasExactly(count: Int, trgName: String)(pf: Pat): this.type = { - var actualCount = 0 - val traverser = newTraverser(patToTotal(pf).andThen { matches => - if (matches) - actualCount += 1 - }) - traverser.traverse(selfNode) + val actualCount = countTrees(pf) assertEquals(s"AST has the wrong number of $trgName", count, actualCount) this } } - class ClassDefAssertions(classDef: ClassDef) - extends AbstractIRNodeAssertions(classDef) { - - protected def newTraverser(f: IRNode => Unit): TestTraverser[ClassDef] = { - new TestTraverser[ClassDef](f) { - def baseTraverse(node: ClassDef): Unit = traverseClassDef(node) - } - } - } - - class LinkedClassAssertions(linkedClass: LinkedClass) - extends AbstractIRNodeAssertions(linkedClass) { - - protected def newTraverser(f: IRNode => Unit): TestTraverser[LinkedClass] = { - new TestTraverser[LinkedClass](f) { - def baseTraverse(node: LinkedClass): Unit = { - for (memberDef <- node.fields) - traverseMemberDef(memberDef) - for (memberDef <- node.methods) - traverseMemberDef(memberDef.value) - for (memberDef <- node.exportedMembers) - traverseMemberDef(memberDef.value) - } - } - } + class ClassDefAssertions(classDef: ClassDef) extends AbstractIRNodeAssertions { + protected def startTraverse(traverser: Traverser): Unit = + traverser.traverseClassDef(classDef) } - abstract class TestTraverser[T](f: IRNode => Unit) extends Traverser { - protected def baseTraverse(node: T): Unit - - def traverse(node: T): Unit = - baseTraverse(node) - - override def traverse(tree: Tree): Unit = { - f(tree) - super.traverse(tree) - } - - override def traverseClassDef(classDef: ClassDef): Unit = { - f(classDef) - super.traverseClassDef(classDef) - } - - override def traverseMemberDef(memberDef: MemberDef): Unit = { - f(memberDef) - super.traverseMemberDef(memberDef) - } - - override def traverseTopLevelExportDef( - exportDef: TopLevelExportDef): Unit = { - f(exportDef) - super.traverseTopLevelExportDef(exportDef) - } - } - - final class TestFinder[T](f: IRNode => Boolean)( - newTraverser: (IRNode => Unit) => TestTraverser[T]) { - - private case object Found extends ControlThrowable - - def find(node: T): Boolean = { - try { - newTraverser { innerNode => - if (f(innerNode)) - throw Found - }.traverse(node) - false - } catch { - case Found => true - } + class LinkedClassAssertions(linkedClass: LinkedClass) extends AbstractIRNodeAssertions { + protected def startTraverse(traverser: Traverser): Unit = { + linkedClass.jsSuperClass.foreach(traverser.traverse(_)) + linkedClass.fields.foreach(traverser.traverseAnyFieldDef(_)) + linkedClass.methods.foreach(traverser.traverseMethodDef(_)) + linkedClass.jsConstructorDef.foreach(traverser.traverseJSConstructorDef(_)) + linkedClass.exportedMembers.foreach(traverser.traverseJSMethodPropDef(_)) } } } diff --git a/linker/shared/src/test/scala/org/scalajs/linker/testutils/MemClassDefIRFile.scala b/linker/shared/src/test/scala/org/scalajs/linker/testutils/MemClassDefIRFile.scala index 26f9ebd04e..48d1c13907 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/testutils/MemClassDefIRFile.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/testutils/MemClassDefIRFile.scala @@ -16,12 +16,13 @@ import scala.concurrent._ import org.scalajs.ir.EntryPointsInfo import org.scalajs.ir.Trees.ClassDef +import org.scalajs.ir.Version import org.scalajs.linker.interface.IRFile import org.scalajs.linker.interface.unstable.IRFileImpl -private final class MemClassDefIRFile(classDef: ClassDef) - extends IRFileImpl("mem://" + classDef.name.name + ".sjsir", None) { +private final class MemClassDefIRFile(classDef: ClassDef, version: Version) + extends IRFileImpl("mem://" + classDef.name.name + ".sjsir", version) { def tree(implicit ec: ExecutionContext): Future[ClassDef] = Future(classDef) @@ -32,5 +33,8 @@ private final class MemClassDefIRFile(classDef: ClassDef) object MemClassDefIRFile { def apply(classDef: ClassDef): IRFile = - new MemClassDefIRFile(classDef) + apply(classDef, Version.Unversioned) + + def apply(classDef: ClassDef, version: Version): IRFile = + new MemClassDefIRFile(classDef, version) } diff --git a/linker/shared/src/test/scala/org/scalajs/linker/testutils/TestIRBuilder.scala b/linker/shared/src/test/scala/org/scalajs/linker/testutils/TestIRBuilder.scala index 5f8f1b67e3..e95f27b8c9 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/testutils/TestIRBuilder.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/testutils/TestIRBuilder.scala @@ -32,6 +32,7 @@ object TestIRBuilder { val EMF = MemberFlags.empty val EOH = OptimizerHints.empty val NON = NoOriginalName + val UNV = ir.Version.Unversioned val JSCtorFlags = EMF.withNamespace(MemberNamespace.Constructor) @@ -53,13 +54,18 @@ object TestIRBuilder { interfaces: List[ClassName] = Nil, jsSuperClass: Option[Tree] = None, jsNativeLoadSpec: Option[JSNativeLoadSpec] = None, - memberDefs: List[MemberDef] = Nil, + fields: List[AnyFieldDef] = Nil, + methods: List[MethodDef] = Nil, + jsConstructor: Option[JSConstructorDef] = None, + jsMethodProps: List[JSMethodPropDef] = Nil, + jsNativeMembers: List[JSNativeMemberDef] = Nil, topLevelExportDefs: List[TopLevelExportDef] = Nil, optimizerHints: OptimizerHints = EOH ): ClassDef = { val notHashed = ClassDef(ClassIdent(className), NON, kind, jsClassCaptures, superClass.map(ClassIdent(_)), interfaces.map(ClassIdent(_)), - jsSuperClass, jsNativeLoadSpec, memberDefs, topLevelExportDefs)( + jsSuperClass, jsNativeLoadSpec, fields, methods, jsConstructor, + jsMethodProps, jsNativeMembers, topLevelExportDefs)( optimizerHints) Hashers.hashClassDef(notHashed) } @@ -73,7 +79,7 @@ object TestIRBuilder { MainTestClassName, kind = ClassKind.ModuleClass, superClass = Some(ObjectClass), - memberDefs = List( + methods = List( trivialCtor(MainTestClassName), mainMethodDef(mainBody) ) @@ -87,13 +93,13 @@ object TestIRBuilder { This()(ClassType(enclosingClassName)), ObjectClass, MethodIdent(NoArgConstructorName), Nil)(NoType)))( - EOH, None) + EOH, UNV) } def trivialJSCtor: JSConstructorDef = { JSConstructorDef(JSCtorFlags, Nil, None, JSConstructorBody(Nil, JSSuperConstructorCall(Nil), Undefined() :: Nil))( - EOH, None) + EOH, UNV) } val MainMethodName: MethodName = m("main", List(AT), VoidRef) @@ -102,7 +108,7 @@ object TestIRBuilder { val argsParamDef = paramDef("args", ArrayType(AT)) MethodDef(MemberFlags.empty.withNamespace(MemberNamespace.PublicStatic), MainMethodName, NON, List(argsParamDef), NoType, Some(body))( - EOH, None) + EOH, UNV) } def consoleLog(expr: Tree): Tree = @@ -134,13 +140,17 @@ object TestIRBuilder { ) } - def requiredMemberDefs(className: ClassName, - classKind: ClassKind): List[MemberDef] = { + def requiredMethods(className: ClassName, + classKind: ClassKind): List[MethodDef] = { if (classKind == ClassKind.ModuleClass) List(trivialCtor(className)) - else if (classKind.isJSClass) List(trivialJSCtor) else Nil } + def requiredJSConstructor(classKind: ClassKind): Option[JSConstructorDef] = { + if (classKind.isJSClass) Some(trivialJSCtor) + else None + } + implicit def string2LocalName(name: String): LocalName = LocalName(name) implicit def string2LabelName(name: String): LabelName = diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/BlacklistedTests.txt b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/BlacklistedTests.txt deleted file mode 100644 index 0884c72b2c..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/BlacklistedTests.txt +++ /dev/null @@ -1,1002 +0,0 @@ -# -# POS -# - -# Using Jsoup, what's that? -pos/cycle-jsoup.scala - -# Using scala.actors -pos/t533.scala -pos/functions.scala -pos/MailBox.scala - -# Spuriously fails too often, and causes other subsequent tests to fail too -# Note that this test, by design, stress-tests type checking -pos/t6367.scala - -# Kills our IR serializer, it's an artificially super-deep if/else if -pos/t9181.scala - -# -# NEG -# - -# Screws up, but not really our problem (error: None.get instead of -# phase ordering error) -neg/t7494-multi-right-after -neg/t7494-right-after-before -neg/t7622-multi-followers -neg/t7622-cyclic-dependency - -# Uses some strange macro cross compile mechanism. -neg/macro-incompatible-macro-engine-c.scala - -# Spurious failures -neg/inlineMaxSize.scala -neg/patmatexhaust-huge.scala - -# Uses .java files -neg/t6289 - -# -# RUN -# - -# Relies on the exact toString() representation of Floats/Doubles -run/t2378.scala - -# Uses ClassTags on existentials which are broken in Scala (see #251) -run/valueclasses-classtag-existential.scala - -# Relies on a particular execution speed -run/t5857.scala - -# Using parts of the javalib we don't plan to support - -run/t5018.scala -run/t2417.scala -run/t4813.scala -run/lazy-concurrent.scala -run/t3667.scala -run/t3038d.scala -run/shutdownhooks.scala -run/t5590.scala -run/t3895b.scala -run/t5974.scala -run/hashset.scala -run/t5262.scala -run/serialize-stream.scala -run/sysprops.scala -run/lambda-serialization-gc.scala - -run/t2849.scala -run/t1360.scala -run/t6114.scala -run/t3199b.scala -run/t8690.scala - -run/various-flat-classpath-types.scala - -# Uses java.util.Collections -run/t2250.scala - -# Used java.io.ObjectInputStream -run/t9365.scala -run/t9375.scala - -# Uses java.math.BigDecimal / BigInteger : but failures not due to them -run/hashhash.scala -run/is-valid-num.scala -run/stringinterpolation_macro-run.scala - -# Using Threads -run/t6969.scala -run/inner-obj-auto.scala -run/predef-cycle.scala -run/synchronized.scala - -# Uses java.security -run/t2318.scala - -# Tries to catch java.lang.StackOverflowError -run/t6154.scala -run/t9841.scala - -# Tries to catch java.lang.OutOfMemoryError -run/t7880.scala - -# Taking too much time, because JavaScript is not as fast as the JVM - -run/t3822.scala -run/collections.scala -run/t3989.scala -run/adding-growing-set.scala -run/t3242.scala -run/hashCodeDistribution.scala -run/t408.scala -run/t6584.scala -run/t6853.scala -run/UnrolledBuffer.scala -run/t6253a.scala -run/t6253b.scala -run/t6253c.scala -run/numbereq.scala -run/t4658.scala - -# Crashes Rhino - -run/bridges.scala -run/patmat-exprs.scala - -# Using partest properties - -run/tailcalls.scala -run/t4294.scala -run/t6331b.scala - -# Using IO - -run/t6488.scala -run/t6988.scala - -# Object{Output|Input}Streams -run/t6935.scala -run/t8188.scala - -# Using System.getProperties - -run/t4426.scala - -# Using sys.exit / System.exit - -run/verify-ctor.scala - -# Using Await - -run/t7336.scala -run/t7775.scala -run/future-flatmap-exec-count.scala - -# Using detailed stack trace - -run/t6308.scala - -# Using reflection - -run/t720.scala -run/t6063 - -run/mixin-bridge-methods.scala -run/t5125.scala -run/outertest.scala -run/t6223.scala -run/t5652b -run/elidable-opt.scala -run/nullable-lazyvals.scala -run/t4794.scala -run/t5652 -run/t5652c -run/getClassTest-old.scala -run/t8960.scala -run/t7965.scala -run/t8087.scala -run/t8931.scala -run/delambdafyLambdaClassNames -run/t8445.scala -run/lambda-serialization.scala - -run/reflection-repl-classes.scala -run/t5256e.scala -run/typetags_core.scala -run/reflection-constructormirror-toplevel-badpath.scala -run/t5276_1b.scala -run/reflection-sorted-decls.scala -run/toolbox_typecheck_implicitsdisabled.scala -run/t5418b.scala -run/toolbox_typecheck_macrosdisabled2.scala -run/abstypetags_serialize.scala -run/all-overridden.scala -run/showraw_tree_kinds.scala -run/showraw_tree_types_ids.scala -run/showraw_tree_types_typed.scala -run/showraw_tree_ids.scala -run/showraw_tree_ultimate.scala -run/t5266_2.scala -run/t5274_1.scala -run/t5224.scala -run/reflection-sanitychecks.scala -run/t6086-vanilla.scala -run/t5277_2.scala -run/reflection-methodsymbol-params.scala -run/reflection-valueclasses-standard.scala -run/t5274_2.scala -run/t5423.scala -run/reflection-modulemirror-toplevel-good.scala -run/t5419.scala -run/t5271_3.scala -run/reflection-enclosed-nested-basic.scala -run/reflection-enclosed-nested-nested-basic.scala -run/fail-non-value-types.scala -run/exprs_serialize.scala -run/t5258a.scala -run/typetags_without_scala_reflect_manifest_lookup.scala -run/t4110-new.scala -run/t5273_2b_newpatmat.scala -run/t6277.scala -run/t5335.scala -run/toolbox_typecheck_macrosdisabled.scala -run/reflection-modulemirror-inner-good.scala -run/t5229_2.scala -run/typetags_multi.scala -run/typetags_without_scala_reflect_typetag_manifest_interop.scala -run/reflection-constructormirror-toplevel-good.scala -run/reflection-magicsymbols-invoke.scala -run/t6392b.scala -run/t5229_1.scala -run/reflection-magicsymbols-vanilla.scala -run/t5225_2.scala -run/origins.scala -run/runtimeEval1.scala -run/reflection-implClass.scala -run/reflection-enclosed-nested-inner-basic.scala -run/reflection-fieldmirror-ctorparam.scala -run/t6181.scala -run/reflection-magicsymbols-repl.scala -run/t5272_2_newpatmat.scala -run/t5270.scala -run/t5418a.scala -run/t5276_2b.scala -run/t5256f.scala -run/reflection-enclosed-basic.scala -run/reflection-constructormirror-inner-badpath.scala -run/interop_typetags_are_manifests.scala -run/newTags.scala -run/t5273_1_newpatmat.scala -run/reflection-constructormirror-nested-good.scala -run/t2236-new.scala -run/existentials3-new.scala -run/t6323b.scala -run/t5943a1.scala -run/reflection-fieldmirror-getsetval.scala -run/t5272_1_oldpatmat.scala -run/t5256h.scala -run/t1195-new.scala -run/t5840.scala -run/reflection-methodsymbol-returntype.scala -run/reflection-fieldmirror-accessorsareokay.scala -run/reflection-sorted-members.scala -run/reflection-allmirrors-tostring.scala -run/valueclasses-typetag-existential.scala -run/toolbox_console_reporter.scala -run/reflection-enclosed-inner-inner-basic.scala -run/t5256b.scala -run/bytecodecs.scala -run/elidable.scala -run/freetypes_false_alarm1.scala -run/freetypes_false_alarm2.scala -run/getClassTest-new.scala -run/idempotency-extractors.scala -run/idempotency-case-classes.scala -run/idempotency-this.scala -run/idempotency-labels.scala -run/idempotency-lazy-vals.scala -run/interop_manifests_are_abstypetags.scala -run/interop_manifests_are_typetags.scala -run/abstypetags_core.scala -run/macro-reify-abstypetag-notypeparams -run/macro-reify-abstypetag-typeparams-tags -run/macro-reify-abstypetag-typeparams-notags -run/macro-reify-abstypetag-usetypetag -run/macro-reify-freevars -run/macro-reify-splice-outside-reify -run/macro-reify-tagless-a -run/macro-reify-type -run/macro-reify-typetag-typeparams-tags -run/macro-reify-typetag-notypeparams -run/macro-undetparams-implicitval -run/manifests-new.scala -run/manifests-old.scala -run/no-pickle-skolems -run/position-val-def.scala -run/reflect-priv-ctor.scala -run/primitive-sigs-2-new.scala -run/primitive-sigs-2-old.scala -run/reflection-enclosed-inner-basic.scala -run/reflection-enclosed-inner-nested-basic.scala -run/reflection-constructormirror-inner-good.scala -run/reflection-constructormirror-nested-badpath.scala -run/reflection-fancy-java-classes -run/reflection-fieldsymbol-navigation.scala -run/reflection-fieldmirror-nmelocalsuffixstring.scala -run/reflection-fieldmirror-getsetvar.scala -run/reflection-fieldmirror-privatethis.scala -run/reflection-implicit.scala -run/reflection-mem-glbs.scala -run/reflection-mem-tags.scala -run/reflection-java-annotations -run/reflection-java-crtp -run/reflection-methodsymbol-typeparams.scala -run/reflection-modulemirror-nested-badpath.scala -run/reflection-modulemirror-inner-badpath.scala -run/reflection-modulemirror-nested-good.scala -run/reflection-modulemirror-toplevel-badpath.scala -run/reflection-sync-subtypes.scala -run/reflinit.scala -run/reflection-valueclasses-derived.scala -run/reflection-valueclasses-magic.scala -run/resetattrs-this.scala -run/runtimeEval2.scala -run/showraw_aliases.scala -run/showraw_mods.scala -run/shortClass.scala -run/showraw_nosymbol.scala -run/showraw_tree.scala -run/showraw_tree_types_untyped.scala -run/t1167.scala -run/t2577.scala -run/t2873.scala -run/t2886.scala -run/t2251b.scala -run/t3346j.scala -run/t3507-new.scala -run/t3569.scala -run/t5125b.scala -run/t5225_1.scala -run/t3425b -run/t5256a.scala -run/t5230.scala -run/t5256c.scala -run/t5256g.scala -run/t5266_1.scala -run/t5269.scala -run/t5271_1.scala -run/t5271_2.scala -run/t5271_4.scala -run/t5272_1_newpatmat.scala -run/t5272_2_oldpatmat.scala -run/t5273_1_oldpatmat.scala -run/t5273_2a_newpatmat.scala -run/t5273_2a_oldpatmat.scala -run/t5275.scala -run/t5276_1a.scala -run/t5276_2a.scala -run/t5277_1.scala -run/t5279.scala -run/t5334_1.scala -run/t5334_2.scala -run/t5415.scala -run/t5418.scala -run/t5676.scala -run/t5704.scala -run/t5710-1.scala -run/t5710-2.scala -run/t5770.scala -run/t5894.scala -run/t5816.scala -run/t5824.scala -run/t5912.scala -run/t5942.scala -run/t5943a2.scala -run/t6023.scala -run/t6113.scala -run/t6175.scala -run/t6178.scala -run/t6199-mirror.scala -run/t6199-toolbox.scala -run/t6240-universe-code-gen.scala -run/t6221 -run/t6260b.scala -run/t6259.scala -run/t6287.scala -run/t6344.scala -run/t6392a.scala -run/t6591_1.scala -run/t6591_2.scala -run/t6591_3.scala -run/t6591_5.scala -run/t6591_6.scala -run/t6591_7.scala -run/t6608.scala -run/t6677.scala -run/t6687.scala -run/t6715.scala -run/t6719.scala -run/t6793.scala -run/t6860.scala -run/t6793b.scala -run/t6793c.scala -run/t7045.scala -run/t7046.scala -run/t7008-scala-defined -run/t7120b.scala -run/t7151.scala -run/t7214.scala -run/t7235.scala -run/t7331a.scala -run/t7331b.scala -run/t7331c.scala -run/t7558.scala -run/t7556 -run/t7779.scala -run/t7868b.scala -run/toolbox_current_run_compiles.scala -run/toolbox_default_reporter_is_silent.scala -run/toolbox_expand_macro.scala -run/toolbox_parse_package.scala -run/toolbox_silent_reporter.scala -run/toolbox_typecheck_inferimplicitvalue.scala -run/trait-renaming -run/typetags_serialize.scala -run/valueclasses-typetag-basic.scala -run/WeakHashSetTest.scala -run/valueclasses-typetag-generic.scala -run/t4023.scala -run/t4024.scala -run/t6380.scala -run/t5273_2b_oldpatmat.scala -run/t8104 -run/t8047.scala -run/t6992 -run/var-arity-class-symbol.scala -run/typetags_symbolof_x.scala -run/typecheck -run/t8190.scala -run/t8192 -run/t8177f.scala -run/t8199.scala -run/t7932.scala -run/t7700.scala -run/t7570c.scala -run/t7570b.scala -run/t7533.scala -run/t7570a.scala -run/t7044 -run/t7328.scala -run/t6733.scala -run/t6554.scala -run/t6732.scala -run/t6379 -run/t6411b.scala -run/t6411a.scala -run/t6260c.scala -run/t6260-delambdafy.scala -run/showdecl -run/reflection-sync-potpourri.scala -run/reflection-tags.scala -run/reflection-companiontype.scala -run/reflection-scala-annotations.scala -run/reflection-idtc.scala -run/macro-reify-nested-b2 -run/mixin-signatures.scala -run/reflection-companion.scala -run/macro-reify-nested-b1 -run/macro-reify-nested-a2 -run/macro-reify-nested-a1 -run/macro-reify-chained2 -run/macro-reify-chained1 -run/inferred-type-constructors.scala -run/mirror_symbolof_x.scala -run/t8196.scala -run/t8549b.scala -run/t8574.scala -run/t8549.scala -run/t8637.scala -run/t8253.scala -run/t9027.scala -run/t6622.scala -run/toolbox-varargs -run/t9252.scala -run/t9182.scala -run/t9102.scala -run/t9388-bin-compat.scala - -run/reify_newimpl_29.scala -run/reify_magicsymbols.scala -run/reify_inheritance.scala -run/reify_newimpl_12.scala -run/reify_typerefs_2b.scala -run/reify_csv.scala -run/reify_inner2.scala -run/reify_maps_oldpatmat.scala -run/reify_newimpl_43.scala -run/reify_nested_inner_refers_to_local.scala -run/reify_closure7.scala -run/reify_closure8b.scala -run/reify_typerefs_3b.scala -run/reify_newimpl_44.scala -run/reify_newimpl_06.scala -run/reify_newimpl_05.scala -run/reify_newimpl_20.scala -run/reify_newimpl_23.scala -run/reify_metalevel_breach_-1_refers_to_1.scala -run/reify_newimpl_41.scala -run/reify-repl-fail-gracefully.scala -run/reify_fors_oldpatmat.scala -run/reify_inner3.scala -run/reify_closure8a.scala -run/reify_closures10.scala -run/reify_ann2a.scala -run/reify_newimpl_51.scala -run/reify_newimpl_47.scala -run/reify_extendbuiltins.scala -run/reify_newimpl_30.scala -run/reify_newimpl_38.scala -run/reify_closure2a.scala -run/reify_newimpl_45.scala -run/reify_closure1.scala -run/reify_generic2.scala -run/reify_printf.scala -run/reify_closure6.scala -run/reify_newimpl_37.scala -run/reify_newimpl_35.scala -run/reify_typerefs_3a.scala -run/reify_newimpl_25.scala -run/reify_ann4.scala -run/reify_typerefs_1b.scala -run/reify_newimpl_22.scala -run/reify_this.scala -run/reify_typerefs_2a.scala -run/reify_newimpl_03.scala -run/reify_newimpl_48.scala -run/reify_varargs.scala -run/reify_newimpl_42.scala -run/reify_newimpl_15.scala -run/reify_nested_inner_refers_to_global.scala -run/reify_newimpl_02.scala -run/reify_newimpl_01.scala -run/reify_fors_newpatmat.scala -run/reify_classfileann_a.scala -run/reify_nested_outer_refers_to_local.scala -run/reify_newimpl_13.scala -run/reify_closure5a.scala -run/reify_inner4.scala -run/reify_sort.scala -run/reify_ann1a.scala -run/reify_classfileann_b.scala -run/reify_closure4a.scala -run/reify_newimpl_33.scala -run/reify_sort1.scala -run/reify_properties.scala -run/reify_generic.scala -run/reify_newimpl_27.scala -run/reify-aliases.scala -run/reify_ann3.scala -run/reify-staticXXX.scala -run/reify_ann1b.scala -run/reify_ann5.scala -run/reify_anonymous.scala -run/reify-each-node-type.scala -run/reify_copypaste2.scala -run/reify_closure3a.scala -run/reify_copypaste1.scala -run/reify_complex.scala -run/reify_for1.scala -run/reify_getter.scala -run/reify_implicits-new.scala -run/reify_inner1.scala -run/reify_implicits-old.scala -run/reify_lazyunit.scala -run/reify_lazyevaluation.scala -run/reify_maps_newpatmat.scala -run/reify_metalevel_breach_+0_refers_to_1.scala -run/reify_metalevel_breach_-1_refers_to_0_a.scala -run/reify_metalevel_breach_-1_refers_to_0_b.scala -run/reify_nested_outer_refers_to_global.scala -run/reify_newimpl_04.scala -run/reify_newimpl_14.scala -run/reify_newimpl_11.scala -run/reify_newimpl_18.scala -run/reify_newimpl_19.scala -run/reify_newimpl_31.scala -run/reify_newimpl_21.scala -run/reify_newimpl_36.scala -run/reify_newimpl_39.scala -run/reify_newimpl_40.scala -run/reify_newimpl_49.scala -run/reify_newimpl_50.scala -run/reify_newimpl_52.scala -run/reify_renamed_term_basic.scala -run/reify_renamed_term_local_to_reifee.scala -run/reify_renamed_term_overloaded_method.scala -run/reify_renamed_type_basic.scala -run/reify_renamed_type_local_to_reifee.scala -run/reify_renamed_type_spliceable.scala -run/reify_typerefs_1a.scala -run/reify_timeofday.scala -run/reify_renamed_term_t5841.scala - -run/inferred-type-constructors-hou.scala - -# Uses refletction indirectly through -# scala.runtime.ScalaRunTime.replStringOf -run/t6634.scala - -# Using reflection to invoke macros. These tests actually don't require -# or test reflection, but use it to separate compilation units nicely. -# It's a pity we cannot use them - -run/macro-abort-fresh -run/macro-expand-varargs-explicit-over-nonvarargs-bad -run/macro-invalidret-doesnt-conform-to-def-rettype -run/macro-invalidret-nontypeable -run/macro-invalidusage-badret -run/macro-invalidusage-partialapplication -run/macro-invalidusage-partialapplication-with-tparams -run/macro-reflective-ma-normal-mdmi -run/macro-reflective-mamd-normal-mi - -# Using macros, but indirectly creating calls to reflection -run/macro-reify-unreify - -# Using Enumeration in a way we cannot fix - -run/enums.scala -run/t3719.scala -run/t8611b.scala - -# Exceptions that become JavaScriptException - -run/pf-catch.scala -run/exceptions-2.scala -run/exceptions-nest.scala -run/t8601c.scala -run/t8601b.scala - -# Expecting unsupported exceptions (e.g. ArrayIndexOutOfBounds) -run/optimizer-array-load.scala -run/t8601.scala - -# Playing with classfile format - -run/classfile-format-51.scala -run/classfile-format-52.scala - -# Concurrent collections (TrieMap) -# has too much stuff implemented in *.java, so no support -run/triemap-hash.scala - -# Using parallel collections - -run/t5375.scala -run/t4894.scala -run/ctries-new -run/collection-conversions.scala -run/concurrent-map-conversions.scala -run/t4761.scala -run/concurrent-stream.scala -run/t7498.scala -run/t6448.scala -run/ctries-old -run/map_java_conversions.scala -run/parmap-ops.scala -run/pc-conversions.scala -run/t4459.scala -run/t4608.scala -run/t4723.scala -run/t4895.scala -run/t6052.scala -run/t6410.scala -run/t6467.scala -run/t6908.scala - -# Using scala.xml - -run/t4124.scala - -# Using Swing - -run/t3613.scala - -# Using the REPL - -run/t4285.scala -run/constant-type.scala -run/repl-bare-expr.scala -run/repl-parens.scala -run/repl-assign.scala -run/t5583.scala -run/treePrint.scala -run/constrained-types.scala -run/repl-power.scala -run/t4710.scala -run/repl-paste.scala -run/repl-reset.scala -run/repl-paste-3.scala -run/t6329_repl.scala -run/t6273.scala -run/repl-paste-2.scala -run/t5655.scala -run/t5072.scala -run/repl-colon-type.scala -run/kind-repl-command.scala -run/repl-trim-stack-trace.scala -run/t4594-repl-settings.scala -run/repl-save.scala -run/repl-paste-raw.scala -run/repl-paste-4.scala -run/repl-paste-5.scala -run/t7801.scala -run/repl-backticks.scala -run/t6633.scala - -# Using the Repl (scala.tools.partest.ReplTest) -run/class-symbol-contravariant.scala -run/lub-visibility.scala -run/macro-bundle-repl.scala -run/macro-repl-basic.scala -run/macro-repl-dontexpand.scala -run/macro-system-properties.scala -run/reflection-equality.scala -run/reflection-repl-elementary.scala -run/reify_newimpl_26.scala -run/repl-javap-app.scala -run/repl-out-dir.scala -run/repl-term-macros.scala -run/repl-transcript.scala -run/repl-type-verbose.scala -run/t3376.scala -run/t4025.scala -run/t4172.scala -run/t4216.scala -run/t4542.scala -run/t4671.scala -run/t5256d.scala -run/t5535.scala -run/t5537.scala -run/t5789.scala -run/t6086-repl.scala -run/t6146b.scala -run/t6187.scala -run/t6320.scala -run/t6381.scala -run/t6434.scala -run/t6439.scala -run/t6507.scala -run/t6549.scala -run/t6937.scala -run/t7185.scala -run/t7319.scala -run/t7482a.scala -run/t7634.scala -run/t7747-repl.scala -run/t7805-repl-i.scala -run/tpeCache-tyconCache.scala -run/repl-empty-package -run/repl-javap-def.scala -run/repl-javap-fun.scala -run/repl-javap-mem.scala -run/repl-javap-memfun.scala -run/repl-javap-more-fun.scala -run/repl-javap-outdir -run/repl-javap.scala -run/repl-javap-outdir-funs -run/t6329_repl_bug.scala -run/t4950.scala -run/xMigration.scala -run/t6541-option.scala -run/repl-serialization.scala -run/repl-paste-6.scala -run/repl-no-uescape.scala -run/repl-classbased.scala -run/repl-paste-parse.scala - -# Using Scala Script (partest.ScriptTest) - -run/t7711-script-args.scala -run/t4625.scala -run/t4625b.scala -run/t4625c.scala - -# Using the compiler API - -run/t2512.scala -run/analyzerPlugins.scala -run/test-cpp.scala -run/compiler-asSeenFrom.scala -run/t5603.scala -run/t6440.scala -run/t5545.scala -run/existentials-in-compiler.scala -run/global-showdef.scala -run/inline-ex-handlers.scala -run/stream_length.scala -run/annotatedRetyping.scala -run/imain.scala -run/existential-rangepos.scala -run/delambdafy_uncurry_byname_inline.scala -run/delambdafy_uncurry_byname_method.scala -run/delambdafy_uncurry_inline.scala -run/delambdafy_t6555.scala -run/delambdafy_uncurry_method.scala -run/delambdafy_t6028.scala -run/memberpos.scala -run/programmatic-main.scala -run/reflection-names.scala -run/settings-parse.scala -run/sm-interpolator.scala -run/t1501.scala -run/t1500.scala -run/sammy_java8.scala -run/t1618.scala -run/t2464 -run/t4072.scala -run/t5064.scala -run/t5313.scala -run/t5385.scala -run/t5699.scala -run/t5717.scala -run/t5940.scala -run/t6028.scala -run/t6194.scala -run/t6288b-jump-position.scala -run/t6669.scala -run/t6745-2.scala -run/t6955.scala -run/t6956.scala -run/t7096.scala -run/t7271.scala -run/t7337.scala -run/t7398.scala -run/t7569.scala -run/t7852.scala -run/t7817-tree-gen.scala -run/t7825.scala - -# partest.ParserTest -run/t3368.scala -run/t3368-b.scala -run/t3368-c.scala -run/t3368-d.scala - -# partest.DirectTest -run/t6288.scala -run/t6331.scala -run/t6440b.scala -run/t6555.scala -run/t7876.scala -run/typetags_without_scala_reflect_typetag_lookup.scala -run/dynamic-updateDynamic.scala -run/dynamic-selectDynamic.scala -run/dynamic-applyDynamic.scala -run/dynamic-applyDynamicNamed.scala -run/t4841-isolate-plugins -run/large_code.scala -run/macroPlugins-namerHooks.scala -run/t4287inferredMethodTypes.scala -run/t4841-no-plugin.scala -run/t4332.scala -run/t8029.scala -run/t8046 -run/t5905-features.scala -run/t5905b-features.scala -run/large_class.scala -run/t8708_b -run/icode-reader-dead-code.scala -run/t5938.scala -run/t8502.scala -run/t6502.scala -run/t8907.scala -run/t9097.scala -run/macroPlugins-enterStats.scala -run/sbt-icode-interface.scala - -# Using partest.StoreReporterDirectTest -run/t8502b.scala - -# partest.StubErrorMessageTest -run/StubErrorBInheritsFromA.scala -run/StubErrorComplexInnerClass.scala -run/StubErrorHK.scala -run/StubErrorReturnTypeFunction.scala -run/StubErrorReturnTypeFunction2.scala -run/StubErrorReturnTypePolyFunction.scala -run/StubErrorSubclasses.scala -run/StubErrorTypeclass.scala -run/StubErrorTypeDef.scala - -# partest.CompilerTest -run/t8852a.scala - -# partest.BytecodeTest -run/t6546 -run/t7106 -run/t7974 -run/t8601-closure-elim.scala -run/t4788 -run/t4788-separate-compilation -run/t9403 - -# partest.SessionTest -run/t1931.scala -run/t8843-repl-xlat.scala -run/t9206.scala -run/t9170.scala - -# partest.JavapTest -run/t8608-no-format.scala -run/repl-javap-lambdas.scala - -# Using .java source files - -run/t4317 -run/t4238 -run/t2296c -run/t4119 -run/t4283 -run/t4891 -run/t6168 -run/t6168b -run/t6240a -run/t6240b -run/t6548 -run/t6989 -run/t7008 -run/t7246 -run/t7246b -run/t7359 -run/t7439 -run/t7455 -run/t7510 -run/t7582-private-within -run/t7582 -run/t7582b -run/t3897 -run/t7374 -run/t3452e -run/t3452g -run/t3452d -run/t3452b-bcode -run/t3452b -run/t3452a -run/t1430 -run/t4729 -run/t8442 -run/t8601e -run/t9298 -run/t9298b -run/t9359 -run/t7741a -run/t7741b -run/bcodeInlinerMixed -run/t9268 -run/t1459 -run/t1459generic -run/t3236 -run/t9013 -run/sd304 - -# Using scalap -run/scalapInvokedynamic.scala - -# Using scala-script -run/t7791-script-linenums.scala - -# Suffers from bug in Node.js (https://github.com/joyent/node/issues/7528) -run/range-unit.scala - -# Using Manifests (which use Class.getInterfaces) -run/valueclasses-manifest-existential.scala -run/existentials3-old.scala -run/t2236-old.scala -run/interop_manifests_are_classtags.scala -run/valueclasses-manifest-generic.scala -run/valueclasses-manifest-basic.scala -run/t1195-old.scala -run/t3758-old.scala -run/t4110-old.scala -run/t6246.scala - -# Using ScalaRunTime.stringOf -run/value-class-extractor-seq.scala -run/t3493.scala - -# Using Class.forName -run/private-inline.scala - -# Suffers from #3180 -run/t3877.scala -run/t6272.scala - -### Incorrect partests ### -# Badly uses constract of Console.print (no flush) -run/t429.scala diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/neg/choices.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/neg/choices.check deleted file mode 100644 index ca484583a3..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/neg/choices.check +++ /dev/null @@ -1,6 +0,0 @@ -error: Usage: -Yresolve-term-conflict: - where choices are package, object, error (default: error) -error: bad option: '-Yresolve-term-conflict' -error: bad options: -P:scalajs:nowarnGlobalExecutionContext -Yresolve-term-conflict -error: flags file may only contain compiler options, found: -Yresolve-term-conflict -four errors found diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/neg/partestInvalidFlag.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/neg/partestInvalidFlag.check deleted file mode 100644 index b0a60aaae2..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/neg/partestInvalidFlag.check +++ /dev/null @@ -1,4 +0,0 @@ -error: bad option: '-badCompilerFlag' -error: bad options: -P:scalajs:nowarnGlobalExecutionContext -badCompilerFlag notAFlag -Yopt:badChoice -error: flags file may only contain compiler options, found: -badCompilerFlag notAFlag -Yopt:badChoice -three errors found diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/neg/t6446-additional.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/neg/t6446-additional.check deleted file mode 100644 index dd8738b40e..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/neg/t6446-additional.check +++ /dev/null @@ -1,33 +0,0 @@ - phase name id description - ---------- -- ----------- - parser 1 parse source into ASTs, perform simple desugaring - jspretyper 2 capture pre-typer only tree info (for Scala.js) - namer 3 resolve names, attach symbols to named trees -packageobjects 4 load package objects - typer 5 the meat and potatoes: type the trees - jsinterop 6 prepare ASTs for JavaScript interop - patmat 7 translate match expressions -superaccessors 8 add super accessors in traits and nested classes - extmethods 9 add extension methods for inline classes - pickler 10 serialize symbol tables - refchecks 11 reference/override checking, translate nested objects -xplicitinnerjs 12 make references to inner JS classes explicit - uncurry 13 uncurry, translate function values to anonymous classes - tailcalls 14 replace tail calls by jumps - specialize 15 @specialized-driven class and method specialization -xplicitlocaljs 16 make references to local JS classes explicit - explicitouter 17 this refs to outer pointers - erasure 18 erase types, add interfaces for traits - posterasure 19 clean up erased inline classes - lazyvals 20 allocate bitmaps, translate lazy vals into lazified defs - lambdalift 21 move nested functions to top level - constructors 22 move field definitions into constructors - flatten 23 eliminate inner classes - mixin 24 mixin composition - jscode 25 generate JavaScript code from ASTs - cleanup 26 platform-specific cleanups, generate reflective calls - delambdafy 27 remove lambdas - icode 28 generate portable intermediate code - jvm 29 generate JVM bytecode - ploogin 30 A sample phase that does so many things it's kind of hard... - terminal 31 the last phase during a compilation run diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/neg/t6446-list.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/neg/t6446-list.check deleted file mode 100644 index 95883c8c81..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/neg/t6446-list.check +++ /dev/null @@ -1,2 +0,0 @@ -ploogin - A sample plugin for testing. -scalajs - Compile to JavaScript diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/neg/t6446-missing.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/neg/t6446-missing.check deleted file mode 100644 index 0283ab3d47..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/neg/t6446-missing.check +++ /dev/null @@ -1,33 +0,0 @@ -Error: unable to load class: t6446.Ploogin - phase name id description - ---------- -- ----------- - parser 1 parse source into ASTs, perform simple desugaring - jspretyper 2 capture pre-typer only tree info (for Scala.js) - namer 3 resolve names, attach symbols to named trees -packageobjects 4 load package objects - typer 5 the meat and potatoes: type the trees - jsinterop 6 prepare ASTs for JavaScript interop - patmat 7 translate match expressions -superaccessors 8 add super accessors in traits and nested classes - extmethods 9 add extension methods for inline classes - pickler 10 serialize symbol tables - refchecks 11 reference/override checking, translate nested objects -xplicitinnerjs 12 make references to inner JS classes explicit - uncurry 13 uncurry, translate function values to anonymous classes - tailcalls 14 replace tail calls by jumps - specialize 15 @specialized-driven class and method specialization -xplicitlocaljs 16 make references to local JS classes explicit - explicitouter 17 this refs to outer pointers - erasure 18 erase types, add interfaces for traits - posterasure 19 clean up erased inline classes - lazyvals 20 allocate bitmaps, translate lazy vals into lazified defs - lambdalift 21 move nested functions to top level - constructors 22 move field definitions into constructors - flatten 23 eliminate inner classes - mixin 24 mixin composition - jscode 25 generate JavaScript code from ASTs - cleanup 26 platform-specific cleanups, generate reflective calls - delambdafy 27 remove lambdas - icode 28 generate portable intermediate code - jvm 29 generate JVM bytecode - terminal 30 the last phase during a compilation run diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/neg/t6446-show-phases.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/neg/t6446-show-phases.check deleted file mode 100644 index 568fd80c8d..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/neg/t6446-show-phases.check +++ /dev/null @@ -1,32 +0,0 @@ - phase name id description - ---------- -- ----------- - parser 1 parse source into ASTs, perform simple desugaring - jspretyper 2 capture pre-typer only tree info (for Scala.js) - namer 3 resolve names, attach symbols to named trees -packageobjects 4 load package objects - typer 5 the meat and potatoes: type the trees - jsinterop 6 prepare ASTs for JavaScript interop - patmat 7 translate match expressions -superaccessors 8 add super accessors in traits and nested classes - extmethods 9 add extension methods for inline classes - pickler 10 serialize symbol tables - refchecks 11 reference/override checking, translate nested objects -xplicitinnerjs 12 make references to inner JS classes explicit - uncurry 13 uncurry, translate function values to anonymous classes - tailcalls 14 replace tail calls by jumps - specialize 15 @specialized-driven class and method specialization -xplicitlocaljs 16 make references to local JS classes explicit - explicitouter 17 this refs to outer pointers - erasure 18 erase types, add interfaces for traits - posterasure 19 clean up erased inline classes - lazyvals 20 allocate bitmaps, translate lazy vals into lazified defs - lambdalift 21 move nested functions to top level - constructors 22 move field definitions into constructors - flatten 23 eliminate inner classes - mixin 24 mixin composition - jscode 25 generate JavaScript code from ASTs - cleanup 26 platform-specific cleanups, generate reflective calls - delambdafy 27 remove lambdas - icode 28 generate portable intermediate code - jvm 29 generate JVM bytecode - terminal 30 the last phase during a compilation run diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/neg/t7494-no-options.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/neg/t7494-no-options.check deleted file mode 100644 index 7b0d01750a..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/neg/t7494-no-options.check +++ /dev/null @@ -1,34 +0,0 @@ -error: Error: ploogin takes no options - phase name id description - ---------- -- ----------- - parser 1 parse source into ASTs, perform simple desugaring - jspretyper 2 capture pre-typer only tree info (for Scala.js) - namer 3 resolve names, attach symbols to named trees -packageobjects 4 load package objects - typer 5 the meat and potatoes: type the trees - jsinterop 6 prepare ASTs for JavaScript interop - patmat 7 translate match expressions -superaccessors 8 add super accessors in traits and nested classes - extmethods 9 add extension methods for inline classes - pickler 10 serialize symbol tables - refchecks 11 reference/override checking, translate nested objects -xplicitinnerjs 12 make references to inner JS classes explicit - uncurry 13 uncurry, translate function values to anonymous classes - tailcalls 14 replace tail calls by jumps - specialize 15 @specialized-driven class and method specialization -xplicitlocaljs 16 make references to local JS classes explicit - explicitouter 17 this refs to outer pointers - erasure 18 erase types, add interfaces for traits - posterasure 19 clean up erased inline classes - lazyvals 20 allocate bitmaps, translate lazy vals into lazified defs - lambdalift 21 move nested functions to top level - constructors 22 move field definitions into constructors - flatten 23 eliminate inner classes - mixin 24 mixin composition - jscode 25 generate JavaScript code from ASTs - cleanup 26 platform-specific cleanups, generate reflective calls - delambdafy 27 remove lambdas - icode 28 generate portable intermediate code - jvm 29 generate JVM bytecode - ploogin 30 A sample phase that does so many things it's kind of hard... - terminal 31 the last phase during a compilation run diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/Course-2002-01.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/Course-2002-01.check deleted file mode 100644 index fcda9433de..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/Course-2002-01.check +++ /dev/null @@ -1,37 +0,0 @@ -Course-2002-01.scala:41: warning: method loop in object M0 does nothing other than call itself recursively - def loop: Int = loop; - ^ -232 -667 -11 -10 -62.8318 -62.8318 -62.8318 -4 -81 -256 -25 -1 -737 -1 -0 -1 -76 -1.4142156862745097 -1.7321428571428572 -2.0000000929222947 -1.4142156862745097 -1.7321428571428572 -2.0000000929222947 -1.4142156862745097 -1.7321428571428572 -2.0000000929222947 -sqrt(2) = 1.4142135623746899 -sqrt(2) = 1.4142135623746899 -cbrt(2) = 1.2599210500177698 -1 -1 1 -1 2 1 -1 3 3 1 -1 4 6 4 1 diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/Course-2002-02.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/Course-2002-02.check deleted file mode 100644 index ab75cfdb61..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/Course-2002-02.check +++ /dev/null @@ -1,187 +0,0 @@ -7 -120 - -10 -100 -2.083333333333333 -3025.7687714031754 -pi = 3.1659792728432152 - -10 -100 -2.083333333333333 -3025.7687714031754 -pi = 3.1659792728432152 - -10 -100 -2.083333333333333 -3025.7687714031754 -pi = 3.1659792728432152 - -10 -100 -2.083333333333333 -3025.7687714031754 -pi = 3.1659792728432152 - -10 -100 -2.083333333333333 -3025.7687714031754 -pi = 3.1659792728432152 - -10 -100 -2.083333333333333 -3025.7687714031754 -pi = 3.1659792728432152 - -10 -100 -2.083333333333333 -3025.7687714031754 -pi = 3.1659792728432152 - -pi = 3.181104885577714 -pi = 3.181104885577714 - -10 -100 -2.083333333333333 -3025.7687714031754 -pi = 3.1659792728432152 -pi = 3.181104885577714 -pi = 3.181104885577714 - -1.5 -1.4166666666666665 -1.4142156862745097 -1.4142135623746899 -sqrt(2) = 1.4142135623746899 - -1.5 -1.4166666666666665 -1.4142156862745097 -1.4142135623746899 -sqrt(2) = 1.4142135623746899 - -1 + 2 + .. + 5 = 15 -1 * 2 * .. * 5 = 120 - -1^2 + 2^2 + .. + 5^2 = 55 -1^2 * 2^2 * .. * 5^2 = 14400 - -factorial(0) = 1 -factorial(1) = 1 -factorial(2) = 2 -factorial(3) = 6 -factorial(4) = 24 -factorial(5) = 120 - -1 + 2 + .. + 5 = 15 -1 * 2 * .. * 5 = 120 - -1^2 + 2^2 + .. + 5^2 = 55 -1^2 * 2^2 * .. * 5^2 = 14400 - -factorial(0) = 1 -factorial(1) = 1 -factorial(2) = 2 -factorial(3) = 6 -factorial(4) = 24 -factorial(5) = 120 - -1 + 2 + .. + 5 = 15 -1 * 2 * .. * 5 = 120 - -1^2 + 2^2 + .. + 5^2 = 55 -1^2 * 2^2 * .. * 5^2 = 14400 - -factorial(0) = 1 -factorial(1) = 1 -factorial(2) = 2 -factorial(3) = 6 -factorial(4) = 24 -factorial(5) = 120 - -fib(0) = 0 -fib(1) = 1 -fib(2) = 1 -fib(3) = 2 -fib(4) = 3 -fib(5) = 5 -fib(6) = 8 -fib(7) = 13 -fib(8) = 21 -fib(9) = 34 -fib(0) = 0 -fib(1) = 1 -fib(2) = 1 -fib(3) = 2 -fib(4) = 3 -fib(5) = 5 -fib(6) = 8 -fib(7) = 13 -fib(8) = 21 -fib(9) = 34 -power(0,0) = 1 -power(0,1) = 0 -power(0,2) = 0 -power(0,3) = 0 -power(0,4) = 0 -power(0,5) = 0 -power(0,6) = 0 -power(0,7) = 0 -power(0,8) = 0 - -power(1,0) = 1 -power(1,1) = 1 -power(1,2) = 1 -power(1,3) = 1 -power(1,4) = 1 -power(1,5) = 1 -power(1,6) = 1 -power(1,7) = 1 -power(1,8) = 1 - -power(2,0) = 1 -power(2,1) = 2 -power(2,2) = 4 -power(2,3) = 8 -power(2,4) = 16 -power(2,5) = 32 -power(2,6) = 64 -power(2,7) = 128 -power(2,8) = 256 - -power(3,0) = 1 -power(3,1) = 3 -power(3,2) = 9 -power(3,3) = 27 -power(3,4) = 81 -power(3,5) = 243 -power(3,6) = 729 -power(3,7) = 2187 -power(3,8) = 6561 - -power(4,0) = 1 -power(4,1) = 4 -power(4,2) = 16 -power(4,3) = 64 -power(4,4) = 256 -power(4,5) = 1024 -power(4,6) = 4096 -power(4,7) = 16384 -power(4,8) = 65536 - -power(5,0) = 1 -power(5,1) = 5 -power(5,2) = 25 -power(5,3) = 125 -power(5,4) = 625 -power(5,5) = 3125 -power(5,6) = 15625 -power(5,7) = 78125 -power(5,8) = 390625 - diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/Course-2002-04.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/Course-2002-04.check deleted file mode 100644 index fc6ad96eed..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/Course-2002-04.check +++ /dev/null @@ -1,64 +0,0 @@ -list0 = List(6, 3, 1, 8, 7, 1, 2, 5, 8, 4, 3, 4, 8) -list1 = List(1, 1, 2, 3, 3, 4, 4, 5, 6, 7, 8, 8, 8) -list2 = List(1, 1, 2, 3, 3, 4, 4, 5, 6, 7, 8, 8, 8) -list3 = List(1, 1, 2, 3, 3, 4, 4, 5, 6, 7, 8, 8, 8) -list4 = List(1, 1, 2, 3, 3, 4, 4, 5, 6, 7, 8, 8, 8) -list5 = List(8, 8, 8, 7, 6, 5, 4, 4, 3, 3, 2, 1, 1) -list6 = List(8, 8, 8, 7, 6, 5, 4, 4, 3, 3, 2, 1, 1) - -list0: List() -> List() -list1: List(0) -> List(0) -list2: List(0, 1) -> List(0, 1) -list3: List(1, 0) -> List(0, 1) -list4: List(0, 1, 2) -> List(0, 1, 2) -list5: List(1, 0, 2) -> List(0, 1, 2) -list6: List(0, 1, 2) -> List(0, 1, 2) -list7: List(1, 0, 2) -> List(0, 1, 2) -list8: List(2, 0, 1) -> List(0, 1, 2) -list9: List(2, 1, 0) -> List(0, 1, 2) -listA: List(6, 3, 1, 8, 7, 1, 2, 5, 8, 4) -> List(1, 1, 2, 3, 4, 5, 6, 7, 8, 8) - -f(x) = 5x^3+7x^2+5x+9 -f(0) = 9 -f(1) = 26 -f(2) = 87 -f(3) = 222 - -v1 = List(2, 3, 4) -v2 = List(6, 7, 8) - -id = List(List(1, 0, 0), List(0, 1, 0), List(0, 0, 1)) -m1 = List(List(2, 0, 0), List(0, 2, 0), List(0, 0, 2)) -m2 = List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9)) - -v1 * v1 = 29 -v1 * v2 = 65 -v2 * v1 = 65 -v1 * v2 = 65 - -id * v1 = List(2, 3, 4) -m1 * v1 = List(4, 6, 8) -m2 * v1 = List(20, 47, 74) - -trn(id) = List(List(1, 0, 0), List(0, 1, 0), List(0, 0, 1)) -trn(m1) = List(List(2, 0, 0), List(0, 2, 0), List(0, 0, 2)) -trn(m2) = List(List(1, 4, 7), List(2, 5, 8), List(3, 6, 9)) - -List(v1) * id = List(List(2, 3, 4)) -List(v1) * m1 = List(List(4, 6, 8)) -List(v1) * m2 = List(List(42, 51, 60)) - -id * List(v1) = List(List(2, 3, 4), List(0, 0, 0), List(0, 0, 0)) -m1 * List(v1) = List(List(4, 6, 8), List(0, 0, 0), List(0, 0, 0)) -m2 * List(v1) = List(List(2, 3, 4), List(8, 12, 16), List(14, 21, 28)) - -id * id = List(List(1, 0, 0), List(0, 1, 0), List(0, 0, 1)) -id * m1 = List(List(2, 0, 0), List(0, 2, 0), List(0, 0, 2)) -m1 * id = List(List(2, 0, 0), List(0, 2, 0), List(0, 0, 2)) -m1 * m1 = List(List(4, 0, 0), List(0, 4, 0), List(0, 0, 4)) -id * m2 = List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9)) -m2 * id = List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9)) -m1 * m2 = List(List(2, 4, 6), List(8, 10, 12), List(14, 16, 18)) -m2 * m1 = List(List(2, 4, 6), List(8, 10, 12), List(14, 16, 18)) -m2 * m2 = List(List(30, 36, 42), List(66, 81, 96), List(102, 126, 150)) - diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/Course-2002-08.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/Course-2002-08.check deleted file mode 100644 index 0585d5b44f..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/Course-2002-08.check +++ /dev/null @@ -1,171 +0,0 @@ -x = abc -count = 111 -x = hello -count = 112 - -account deposit 50 -> undefined -account withdraw 20 -> 30 -account withdraw 20 -> 10 -account withdraw 15 -> - -x deposit 30 -> undefined -y withdraw 20 -> - -x deposit 30 -> undefined -x withdraw 20 -> 10 - -x deposit 30 -> undefined -y withdraw 20 -> 10 - -2^0 = 1 -2^1 = 2 -2^2 = 4 -2^3 = 8 - -2^0 = 1 -2^1 = 2 -2^2 = 4 -2^3 = 8 - -1 2 3 -List(1, 2, 3) - -out 0 new-value = false -*** simulation started *** -out 1 new-value = true -!0 = 1 - -*** simulation started *** -out 2 new-value = false -!1 = 0 - -out 2 new-value = false - -*** simulation started *** -0 & 0 = 0 - -*** simulation started *** -0 & 1 = 0 - -*** simulation started *** -out 11 new-value = true -out 11 new-value = false -1 & 0 = 0 - -*** simulation started *** -out 14 new-value = true -1 & 1 = 1 - -out 14 new-value = false - -*** simulation started *** -0 | 0 = 0 - -*** simulation started *** -out 24 new-value = true -0 | 1 = 1 - -*** simulation started *** -1 | 0 = 1 - -*** simulation started *** -1 | 1 = 1 - -sum 34 new-value = false -carry 34 new-value = false - -*** simulation started *** -0 + 0 = 0 - -*** simulation started *** -sum 47 new-value = true -0 + 1 = 1 - -*** simulation started *** -carry 50 new-value = true -carry 50 new-value = false -sum 54 new-value = false -sum 54 new-value = true -1 + 0 = 1 - -*** simulation started *** -carry 57 new-value = true -sum 61 new-value = false -1 + 1 = 2 - -sum 61 new-value = false -carry 61 new-value = false - -*** simulation started *** -0 + 0 + 0 = 0 - -*** simulation started *** -sum 82 new-value = true -0 + 0 + 1 = 1 - -*** simulation started *** -sum 89 new-value = false -carry 90 new-value = true -sum 97 new-value = true -carry 98 new-value = false -0 + 1 + 0 = 1 - -*** simulation started *** -sum 113 new-value = false -carry 114 new-value = true -0 + 1 + 1 = 2 - -*** simulation started *** -sum 121 new-value = true -carry 122 new-value = false -sum 129 new-value = false -sum 129 new-value = true -1 + 0 + 0 = 1 - -*** simulation started *** -carry 137 new-value = true -sum 144 new-value = false -1 + 0 + 1 = 2 - -*** simulation started *** -carry 152 new-value = false -sum 152 new-value = true -sum 158 new-value = false -carry 159 new-value = true -1 + 1 + 0 = 2 - -*** simulation started *** -sum 173 new-value = true -1 + 1 + 1 = 3 - -in 0 new-value = false -ctrl0 0 new-value = false -ctrl1 0 new-value = false -ctrl2 0 new-value = false -out0 0 new-value = false -out1 0 new-value = false -out2 0 new-value = false -out3 0 new-value = false -out4 0 new-value = false -out5 0 new-value = false -out6 0 new-value = false -out7 0 new-value = false -in 0 new-value = true -*** simulation started *** -out0 10 new-value = true -ctrl0 10 new-value = true -*** simulation started *** -out1 13 new-value = true -out0 14 new-value = false -ctrl1 14 new-value = true -*** simulation started *** -out3 20 new-value = true -out1 21 new-value = false -ctrl2 21 new-value = true -*** simulation started *** -out7 30 new-value = true -out3 31 new-value = false -ctrl0 31 new-value = false -*** simulation started *** -out7 34 new-value = false -out6 35 new-value = true diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/Course-2002-09.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/Course-2002-09.check deleted file mode 100644 index c921361db7..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/Course-2002-09.check +++ /dev/null @@ -1,50 +0,0 @@ -Probe: f = 32 -Probe: c = 0 -Probe: f = ? -Probe: c = ? - -Probe: f = 212 -Probe: c = 100 -Probe: f = ? -Probe: c = ? - -Probe: c = 0 -Probe: f = 32 -Probe: c = ? -Probe: f = ? - -Probe: c = 100 -Probe: f = 212 -Probe: c = ? -Probe: f = ? - -0 Celsius -> 32 Fahrenheits -100 Celsius -> 212 Fahrenheits -32 Fahrenheits -> 0 Celsius -212 Fahrenheits -> 100 Celsius - -a = ?, b = ?, c = ? => ? * ? = ? -a = 2, b = ?, c = ? => 2 * ? = ? -a = ?, b = 3, c = ? => ? * 3 = ? -a = ?, b = ?, c = 6 => ? * ? = 6 -a = 2, b = 3, c = ? => 2 * 3 = 6 -a = 2, b = ?, c = 6 => 2 * 3 = 6 -a = ?, b = 3, c = 6 => 2 * 3 = 6 -a = 2, b = 3, c = 6 => 2 * 3 = 6 - -a = 0, b = ?, c = ? => 0 * ? = 0 -a = ?, b = 0, c = ? => ? * 0 = 0 -a = ?, b = ?, c = 0 => ? * ? = 0 -a = 0, b = 7, c = ? => 0 * 7 = 0 -a = 7, b = 0, c = ? => 7 * 0 = 0 -a = 0, b = 0, c = ? => 0 * 0 = 0 -a = 0, b = ?, c = 0 => 0 * ? = 0 -a = ?, b = 0, c = 0 => ? * 0 = 0 -a = 0, b = 7, c = 0 => 0 * 7 = 0 -a = 7, b = 0, c = 0 => 7 * 0 = 0 -a = 0, b = 0, c = 0 => 0 * 0 = 0 - -a = 3, b = 4 => c = 5 -a = 3, c = 5 => b = 4 -b = 4, c = 5 => a = 3 - diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/Course-2002-10.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/Course-2002-10.check deleted file mode 100644 index 847f0fa703..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/Course-2002-10.check +++ /dev/null @@ -1,46 +0,0 @@ -fib(0) = 0 -fib(1) = 1 -fib(2) = 1 -fib(3) = 2 -fib(4) = 3 -fib(5) = 5 -fib(6) = 8 -fib(7) = 13 -fib(8) = 21 -fib(9) = 34 -fib(10) = 55 -fib(11) = 89 -fib(12) = 144 -fib(13) = 233 -fib(14) = 377 -fib(15) = 610 -fib(16) = 987 -fib(17) = 1597 -fib(18) = 2584 -fib(19) = 4181 - -pi(0) = 4 , 3.166666666666667 , 4 -pi(1) = 2.666666666666667 , 3.1333333333333337, 3.166666666666667 -pi(2) = 3.466666666666667 , 3.1452380952380956, 3.142105263157895 -pi(3) = 2.8952380952380956, 3.1396825396825396, 3.1415993573190044 -pi(4) = 3.33968253968254 , 3.142712842712843 , 3.141592714033778 -pi(5) = 2.976046176046176 , 3.140881340881341 , 3.1415926539752923 -pi(6) = 3.283738483738484 , 3.142071817071817 , 3.141592653591176 -pi(7) = 3.017071817071817 , 3.1412548236077646, 3.141592653589777 -pi(8) = 3.252365934718876 , 3.1418396189294024, 3.141592653589794 -pi(9) = 3.0418396189294024, 3.141406718496502 , 3.1415926535897936 -pi = 3.141592653589793 , 3.141592653589793 , 3.141592653589793 - -ln(0) = 1 , 0.7 , 1 -ln(1) = 0.5 , 0.6904761904761905, 0.7 -ln(2) = 0.8333333333333333, 0.6944444444444444, 0.6932773109243697 -ln(3) = 0.5833333333333333, 0.6924242424242424, 0.6931488693329254 -ln(4) = 0.7833333333333333, 0.6935897435897436, 0.6931471960735491 -ln(5) = 0.6166666666666667, 0.6928571428571428, 0.6931471806635636 -ln(6) = 0.7595238095238095, 0.6933473389355742, 0.6931471805604038 -ln(7) = 0.6345238095238095, 0.6930033416875522, 0.6931471805599444 -ln(8) = 0.7456349206349207, 0.6932539682539682, 0.6931471805599426 -ln(9) = 0.6456349206349206, 0.6930657506744463, 0.6931471805599453 -ln = 0.6931471805599453, 0.6931471805599453, 0.6931471805599453 - -prime numbers: 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/Meter.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/Meter.check deleted file mode 100644 index f46fd557c8..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/Meter.check +++ /dev/null @@ -1,16 +0,0 @@ -Meter.scala:72: warning: a.Meter and Int are unrelated: they will never compare equal - println("x == 1: "+(x == 1)) - ^ -2 -4m -false -x.isInstanceOf[Meter]: true -x.hashCode: 1 -x == 1: false -x == y: true -a == b: true -testing native arrays -Array(1m, 2m) -1m ->>>1m<<< 1m ->>>2m<<< 2m diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/MeterCaseClass.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/MeterCaseClass.check deleted file mode 100644 index ac538d240f..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/MeterCaseClass.check +++ /dev/null @@ -1,16 +0,0 @@ -MeterCaseClass.scala:69: warning: comparing values of types a.Meter and Int using `==' will always yield false - println("x == 1: "+(x == 1)) - ^ -2 -Meter(4) -false -x.isInstanceOf[Meter]: true -x.hashCode: 1 -x == 1: false -x == y: true -a == b: true -testing native arrays -Array(Meter(1), Meter(2)) -Meter(1) ->>>Meter(1)<<< Meter(1) ->>>Meter(2)<<< Meter(2) diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/bugs.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/bugs.sem deleted file mode 100644 index d36898b932..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/bugs.sem +++ /dev/null @@ -1 +0,0 @@ -asInstanceOfs diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/caseClassHash.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/caseClassHash.check deleted file mode 100644 index f975151e9c..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/caseClassHash.check +++ /dev/null @@ -1,9 +0,0 @@ -Foo(true,-1,-1,d,-5,-10,500,500,List(),5) -Foo(true,-1,-1,d,-5,-10,500,500,List(),5) -1383698062 -1383698062 -true -## method 1: 1383698062 -## method 2: 1383698062 - Murmur 1: 1383698062 - Murmur 2: 1383698062 diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/classof.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/classof.check deleted file mode 100644 index 590b4621d8..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/classof.check +++ /dev/null @@ -1,22 +0,0 @@ -Value types: -void -boolean -byte -short -char -int -long -float -double -Class types -class SomeClass -class scala.collection.immutable.List -class scala.Tuple2 -Arrays: -class [Ljava.lang.Void; -class [I -class [D -class [Lscala.collection.immutable.List; -Functions: -interface scala.Function2 -interface scala.Function1 diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/deeps.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/deeps.check deleted file mode 100644 index e533b87dc5..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/deeps.check +++ /dev/null @@ -1,87 +0,0 @@ -testEquals1 -false -false -true - -testEquals2 -false -false -true - -testEquals3 -x=Array(1) -y=Array(1) -false -false -true - -x=Array(Array(1), Array(1)) -y=Array(Array(1), Array(1)) -false -false -true - -x=Array(Array(Array(1), Array(1)), Array(Array(1), Array(1))) -y=Array(Array(Array(1), Array(1)), Array(Array(1), Array(1))) -false -false -true - -testEquals4 -false -false -true -false -false -true -Array(true, false) -Array(true, false) -[true;false] -true;false - -Array(Array(true, false), Array(true, false)) -Array(Array(true, false), Array(true, false)) -[Array(true, false);Array(true, false)] -Array(true, false);Array(true, false) - -Array(Array(Array(true, false), Array(true, false)), Array(Array(true, false), Array(true, false))) -Array(Array(Array(true, false), Array(true, false)), Array(Array(true, false), Array(true, false))) -[Array(Array(true, false), Array(true, false));Array(Array(true, false), Array(true, false))] -Array(Array(true, false), Array(true, false));Array(Array(true, false), Array(true, false)) - -Array(1, 0) -Array(1, 0) -[1;0] -1;0 - -Array(Array(1, 0), Array(1, 0)) -Array(Array(1, 0), Array(1, 0)) -[Array(1, 0);Array(1, 0)] -Array(1, 0);Array(1, 0) - -Array(Array(Array(1, 0), Array(1, 0)), Array(Array(1, 0), Array(1, 0))) -Array(Array(Array(1, 0), Array(1, 0)), Array(Array(1, 0), Array(1, 0))) -[Array(Array(1, 0), Array(1, 0));Array(Array(1, 0), Array(1, 0))] -Array(Array(1, 0), Array(1, 0));Array(Array(1, 0), Array(1, 0)) - -Array(a, b) -Array(a, b) -[a;b] -a;b - -Array(Array(a, b), Array(a, b)) -Array(Array(a, b), Array(a, b)) -[Array(a, b);Array(a, b)] -Array(a, b);Array(a, b) - -Array(Array(Array(a, b), Array(a, b)), Array(Array(a, b), Array(a, b))) -Array(Array(Array(a, b), Array(a, b)), Array(Array(a, b), Array(a, b))) -[Array(Array(a, b), Array(a, b));Array(Array(a, b), Array(a, b))] -Array(Array(a, b), Array(a, b));Array(Array(a, b), Array(a, b)) - -[Array(true, false); Array(false)] -[Array(1, 2); Array(3)] -[Array(1, 2); Array(3)] - -Array(boo, and, foo) -Array(a) diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/delambdafy-specialized.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/delambdafy-specialized.check deleted file mode 100644 index a20ad22303..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/delambdafy-specialized.check +++ /dev/null @@ -1 +0,0 @@ -scala.runtime.AbstractFunction1 diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/dynamic-anyval.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/dynamic-anyval.check deleted file mode 100644 index c125372c9a..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/dynamic-anyval.check +++ /dev/null @@ -1,4 +0,0 @@ -undefined.dingo(bippy, 5) -List(1, 2, 3).dingo(bippy, 5) -undefined.dingo(bippy, 5) -List(1, 2, 3).dingo(bippy, 5) diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/impconvtimes.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/impconvtimes.check deleted file mode 100644 index 082377e474..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/impconvtimes.check +++ /dev/null @@ -1 +0,0 @@ -3.0 * Hour = Measure(3,Hour) diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/imports.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/imports.check deleted file mode 100644 index 1aad598062..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/imports.check +++ /dev/null @@ -1,21 +0,0 @@ -In C_ico, v_ico .toString() returns ↩ -↪C_ico -> ok -In C_ico, field .toString() returns ↩ -↪C_ico -> ok -In C_ico, method.toString() returns ↩ -↪C_ico -> ok - -In C_ioc, v_ioc .toString() returns ↩ -↪C_ioc -> ok -In C_ioc, field .toString() returns ↩ -↪C_ioc -> ok -In C_ioc, method.toString() returns ↩ -↪C_ioc -> ok - -In C_oic, v_oic .toString() returns ↩ -↪C_oic -> ok -In C_oic, field .toString() returns ↩ -↪C_oic -> ok -In C_oic, method.toString() returns ↩ -↪C_oic -> ok - diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/interpolation.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/interpolation.check deleted file mode 100644 index 9c4a77715b..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/interpolation.check +++ /dev/null @@ -1,32 +0,0 @@ -Bob is 1 years old -Bob is 1 years old -Bob will be 2 years old -Bob will be 2 years old -1+1 = 2 -1+1 = 2 -Bob is 12 years old -Bob is 12 years old -Bob will be 13 years old -Bob will be 13 years old -12+1 = 13 -12+1 = 13 -Bob is 123 years old -Bob is 123 years old -Bob will be 124 years old -Bob will be 124 years old -123+1 = 124 -123+1 = 124 -Best price: 10 -Best price: 10.00 -10% discount included -10.00% discount included -Best price: 13.345000267028809 -Best price: 13.35 -13.345000267028809% discount included -13.35% discount included - -0 -00 - -0 -00 diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/interpolationMultiline1.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/interpolationMultiline1.check deleted file mode 100644 index 1b6e140c13..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/interpolationMultiline1.check +++ /dev/null @@ -1,26 +0,0 @@ -Bob is 1 years old -Bob is 1 years old -Bob will be 2 years old -Bob will be 2 years old -1+1 = 2 -1+1 = 2 -Bob is 12 years old -Bob is 12 years old -Bob will be 13 years old -Bob will be 13 years old -12+1 = 13 -12+1 = 13 -Bob is 123 years old -Bob is 123 years old -Bob will be 124 years old -Bob will be 124 years old -123+1 = 124 -123+1 = 124 -Best price: 10 -Best price: 10.00 -10% discount included -10.00% discount included -Best price: 13.345000267028809 -Best price: 13.35 -13.345000267028809% discount included -13.35% discount included diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/macro-bundle-static.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/macro-bundle-static.check deleted file mode 100644 index e2e7628a0d..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/macro-bundle-static.check +++ /dev/null @@ -1,6 +0,0 @@ -undefined -Int -undefined -true -IntInt -true diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/macro-bundle-toplevel.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/macro-bundle-toplevel.check deleted file mode 100644 index e2e7628a0d..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/macro-bundle-toplevel.check +++ /dev/null @@ -1,6 +0,0 @@ -undefined -Int -undefined -true -IntInt -true diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/macro-bundle-whitebox-decl.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/macro-bundle-whitebox-decl.check deleted file mode 100644 index e2e7628a0d..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/macro-bundle-whitebox-decl.check +++ /dev/null @@ -1,6 +0,0 @@ -undefined -Int -undefined -true -IntInt -true diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/misc.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/misc.check deleted file mode 100644 index 6043817dbc..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/misc.check +++ /dev/null @@ -1,62 +0,0 @@ -misc.scala:46: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses - 42; - ^ -misc.scala:47: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses - 42l; - ^ -misc.scala:48: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses - 23.5f; - ^ -misc.scala:49: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses - 23.5; - ^ -misc.scala:50: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses - "Hello"; - ^ -misc.scala:51: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses - 32 + 45; - ^ -misc.scala:62: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses - x; - ^ -misc.scala:74: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses - 1 < 2; - ^ -### Hello -### 17 -### Bye - -### fib(0) = ↩ -↪1 -### fib(1) = ↩ -↪1 -### fib(2) = ↩ -↪2 -### fib(3) = ↩ -↪3 -### fib(4) = ↩ -↪5 -=== MyClass::toString === -=== MySubclass::toString === -=== MyClass::test === - -identity - -A.a = 1 -B.a = 5 -B.b = 2 - -X.a = 4 -Y.a = 11 -Y.b = 5 -Y.b = 5 - -X::foo - -Y::foo -X::foo - -3 -3 - -true diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/promotion.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/promotion.check deleted file mode 100644 index 41e36c369d..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/promotion.check +++ /dev/null @@ -1,4 +0,0 @@ -2 -6 -20 -30 diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/runtime.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/runtime.check deleted file mode 100644 index 0450b9498a..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/runtime.check +++ /dev/null @@ -1,70 +0,0 @@ -runtime.scala:141: warning: comparing values of types Null and Null using `eq' will always yield true - check(true , null eq null, null ne null); - ^ -runtime.scala:141: warning: comparing values of types Null and Null using `ne' will always yield false - check(true , null eq null, null ne null); - ^ -<<< Test0 -[false,true] -[0,1,2] -[3,4,5] -[a,b,c] -[6,7,8] -[9,10,11] -[12,13] -[14,15] -[string] ->>> Test0 - -<<< Test1 -10 -14 -15 -16 -20 -23 -24 -25 -26 ->>> Test1 - -<<< Test2 -A -M0 -N0 - -A -N0 -M0 - -A -M0 -M1 -N0 - -A -N0 -N1 -M0 - ->>> Test2 - -<<< Test3 -Ok -Ok -Ok -Ok -Ok -Ok -Ok -Ok -Ok -Ok -Ok -Ok -Ok -Ok -Ok -Ok ->>> Test3 - diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/spec-self.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/spec-self.check deleted file mode 100644 index fd3c81a4d7..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/spec-self.check +++ /dev/null @@ -1,2 +0,0 @@ -5 -5 diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/structural.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/structural.check deleted file mode 100644 index 2fec112a87..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/structural.check +++ /dev/null @@ -1,37 +0,0 @@ - 1. hey - 2. 11 - 3. dee - 4. iei - 5. 6 - 6. 51 - 7. 2 - 8. 11 -10. 12 -11. eitch -12. 1 -13. ohone -14. 1 -15. undefined -16. one -17. tieone -18. 2 -19. true -20. 1 -21. undefined -22. one -23. oy -24. 1 -25. null -26. iei -31. 4 -32. undefined -33. iei -33. tieone -1 -2 -3 -4 -5 -caught -3 -2 diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t0421-new.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t0421-new.check deleted file mode 100644 index 00d29b7e5b..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t0421-new.check +++ /dev/null @@ -1,3 +0,0 @@ -[Array(0, 1),Array(2, 3),Array(4, 5)] -[Array(31)] -[Array(24, 32)] diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t0421-old.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t0421-old.check deleted file mode 100644 index 00d29b7e5b..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t0421-old.check +++ /dev/null @@ -1,3 +0,0 @@ -[Array(0, 1),Array(2, 3),Array(4, 5)] -[Array(31)] -[Array(24, 32)] diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t1503.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t1503.sem deleted file mode 100644 index d36898b932..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t1503.sem +++ /dev/null @@ -1 +0,0 @@ -asInstanceOfs diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t3702.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t3702.check deleted file mode 100644 index 3fce98715c..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t3702.check +++ /dev/null @@ -1,2 +0,0 @@ -undefined -6 diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t4148.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t4148.sem deleted file mode 100644 index d36898b932..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t4148.sem +++ /dev/null @@ -1 +0,0 @@ -asInstanceOfs diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t4617.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t4617.check deleted file mode 100644 index a6790f16f7..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t4617.check +++ /dev/null @@ -1 +0,0 @@ -Str 8 diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t5356.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t5356.check deleted file mode 100644 index 870c901131..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t5356.check +++ /dev/null @@ -1,6 +0,0 @@ -1 java.lang.Byte -1 java.lang.Byte -1 scala.math.BigInt -1 java.lang.Byte -1 java.lang.Byte -1 diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t5552.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t5552.check deleted file mode 100644 index 4704611116..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t5552.check +++ /dev/null @@ -1,2 +0,0 @@ -(3,3) -(3,3) diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t5568.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t5568.check deleted file mode 100644 index 1c8e0882d6..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t5568.check +++ /dev/null @@ -1,9 +0,0 @@ -void -int -class java.lang.Void -class java.lang.Void -class java.lang.Byte -class java.lang.Byte -5 -5 -5 diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t5629b.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t5629b.check deleted file mode 100644 index c65298a6ce..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t5629b.check +++ /dev/null @@ -1,10 +0,0 @@ -=== pf(1): -MySmartPF.apply entered... -newPF.applyOrElse entered... -default -scala.MatchError: 1 (of class java.lang.Byte) -=== pf(42): -MySmartPF.apply entered... -newPF.applyOrElse entered... -ok -=== done diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t5680.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t5680.check deleted file mode 100644 index 962df2f4f3..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t5680.check +++ /dev/null @@ -1,3 +0,0 @@ -[Ljava.lang.Void -undefined -undefined diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t5866.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t5866.check deleted file mode 100644 index 64df1cec7f..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t5866.check +++ /dev/null @@ -1,2 +0,0 @@ -0 -Foo(0) diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t6102.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t6102.check deleted file mode 100644 index aa342511d0..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t6102.check +++ /dev/null @@ -1,30 +0,0 @@ -[running phase parser on t6102.scala] -[running phase jspretyper on t6102.scala] -[running phase namer on t6102.scala] -[running phase packageobjects on t6102.scala] -[running phase typer on t6102.scala] -[running phase jsinterop on t6102.scala] -[running phase patmat on t6102.scala] -[running phase superaccessors on t6102.scala] -[running phase extmethods on t6102.scala] -[running phase pickler on t6102.scala] -[running phase refchecks on t6102.scala] -[running phase xplicitinnerjs on t6102.scala] -[running phase uncurry on t6102.scala] -[running phase tailcalls on t6102.scala] -[running phase specialize on t6102.scala] -[running phase xplicitlocaljs on t6102.scala] -[running phase explicitouter on t6102.scala] -[running phase erasure on t6102.scala] -[running phase posterasure on t6102.scala] -[running phase lazyvals on t6102.scala] -[running phase lambdalift on t6102.scala] -[running phase constructors on t6102.scala] -[running phase flatten on t6102.scala] -[running phase mixin on t6102.scala] -[running phase jscode on t6102.scala] -[running phase cleanup on t6102.scala] -[running phase delambdafy on t6102.scala] -[running phase icode on t6102.scala] -[running phase dce on t6102.scala] -[running phase jvm on icode] diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t6318_primitives.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t6318_primitives.check deleted file mode 100644 index b86fc66f7b..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t6318_primitives.check +++ /dev/null @@ -1,54 +0,0 @@ -Checking if byte matches byte -Some(1) -Checking if byte matches short -Some(1) -Checking if class java.lang.Byte matches byte -Some(1) -Checking if short matches short -Some(1) -Checking if short matches char -None -Checking if class java.lang.Byte matches short -Some(1) -Checking if char matches char -Some() -Checking if char matches int -None -Checking if class java.lang.Character matches char -Some() -Checking if int matches int -Some(1) -Checking if int matches long -None -Checking if class java.lang.Byte matches int -Some(1) -Checking if long matches long -Some(1) -Checking if long matches float -None -Checking if class java.lang.Long matches long -Some(1) -Checking if float matches float -Some(1) -Checking if float matches double -Some(1) -Checking if class java.lang.Byte matches float -Some(1) -Checking if double matches double -Some(1) -Checking if double matches boolean -None -Checking if class java.lang.Byte matches double -Some(1) -Checking if boolean matches boolean -Some(true) -Checking if boolean matches void -None -Checking if class java.lang.Boolean matches boolean -Some(true) -Checking if void matches void -Some(undefined) -Checking if void matches byte -None -Checking if class java.lang.Void matches void -Some(undefined) diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t6662.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t6662.check deleted file mode 100644 index 417b7b5370..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t6662.check +++ /dev/null @@ -1 +0,0 @@ -undefined diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t7657.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t7657.check deleted file mode 100644 index 1a87c1e866..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t7657.check +++ /dev/null @@ -1,3 +0,0 @@ -undefined -undefined -undefined diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t7763.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t7763.sem deleted file mode 100644 index d36898b932..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t7763.sem +++ /dev/null @@ -1 +0,0 @@ -asInstanceOfs diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t8570a.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t8570a.check deleted file mode 100644 index 417b7b5370..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t8570a.check +++ /dev/null @@ -1 +0,0 @@ -undefined diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t8764.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t8764.check deleted file mode 100644 index 121120217e..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t8764.check +++ /dev/null @@ -1,5 +0,0 @@ -IntOnly: should return an unboxed int -Int: int -IntAndDouble: should just box and return Anyval -Double: class java.lang.Byte -Int: class java.lang.Byte diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t9387b.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t9387b.check deleted file mode 100644 index 417b7b5370..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/t9387b.check +++ /dev/null @@ -1 +0,0 @@ -undefined diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/try-catch-unify.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/try-catch-unify.check deleted file mode 100644 index 813f01166d..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/try-catch-unify.check +++ /dev/null @@ -1,4 +0,0 @@ -Failure(java.lang.NumberFormatException: For input string: "Hi") -Success(5) -O NOES -Failure(java.lang.NumberFormatException: For input string: "Hi") diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/virtpatmat_switch.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/virtpatmat_switch.check deleted file mode 100644 index 0900a9ca32..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/virtpatmat_switch.check +++ /dev/null @@ -1,7 +0,0 @@ -zero -one -many -got a -got b -got some letter -scala.MatchError: 5 (of class java.lang.Byte) \ No newline at end of file diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/virtpatmat_typetag.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/virtpatmat_typetag.check deleted file mode 100644 index 048c3aeed0..0000000000 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.12/run/virtpatmat_typetag.check +++ /dev/null @@ -1,10 +0,0 @@ -1 is a Int -1 is a java.lang.Integer -1 is not a java.lang.String; it's a class java.lang.Byte -true is a Any -woele is a java.lang.String -1 is a Int -1 is a java.lang.Integer -1 is not a java.lang.String; it's a class java.lang.Byte -true is a Any -woele is a java.lang.String diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/BlacklistedTests.txt b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/BlacklistedTests.txt index b8f1d01084..4a6f7b8ec6 100644 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/BlacklistedTests.txt +++ b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/BlacklistedTests.txt @@ -670,20 +670,6 @@ run/enums.scala run/t3719.scala run/t8611b.scala -# Exceptions that become JavaScriptException - -run/pf-catch.scala -run/exceptions-2.scala -run/exceptions-nest.scala -run/t8601c.scala -run/t8601b.scala -run/inlineHandlers.scala - -# Expecting unsupported exceptions (e.g. ArrayIndexOutOfBounds) -run/optimizer-array-load.scala -run/t6827.scala -run/t8601.scala - # Expecting exceptions that are linking errors in Scala.js (e.g. NoSuchMethodException) run/t10334.scala diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/exceptions-2.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/exceptions-2.sem new file mode 100644 index 0000000000..faf2309802 --- /dev/null +++ b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/exceptions-2.sem @@ -0,0 +1 @@ +nullPointers diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/exceptions-nest.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/exceptions-nest.check new file mode 100644 index 0000000000..f4f003cf62 --- /dev/null +++ b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/exceptions-nest.check @@ -0,0 +1,13 @@ +2 +23 +2 +5 +2 +4 +OK +4 +OK +10 +1 +undefined +10 diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/exceptions-nest.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/exceptions-nest.sem new file mode 100644 index 0000000000..faf2309802 --- /dev/null +++ b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/exceptions-nest.sem @@ -0,0 +1 @@ +nullPointers diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/inlineHandlers.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/inlineHandlers.sem new file mode 100644 index 0000000000..faf2309802 --- /dev/null +++ b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/inlineHandlers.sem @@ -0,0 +1 @@ +nullPointers diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/optimizer-array-load.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/optimizer-array-load.sem new file mode 100644 index 0000000000..6e7bf4e75c --- /dev/null +++ b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/optimizer-array-load.sem @@ -0,0 +1 @@ +arrayIndexOutOfBounds diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/pf-catch.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/pf-catch.sem new file mode 100644 index 0000000000..faf2309802 --- /dev/null +++ b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/pf-catch.sem @@ -0,0 +1 @@ +nullPointers diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t6827.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t6827.sem new file mode 100644 index 0000000000..6e7bf4e75c --- /dev/null +++ b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t6827.sem @@ -0,0 +1 @@ +arrayIndexOutOfBounds diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t8601b.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t8601b.sem new file mode 100644 index 0000000000..4de3a97396 --- /dev/null +++ b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t8601b.sem @@ -0,0 +1,2 @@ +negativeArraySizes +nullPointers diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t8601c.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t8601c.sem new file mode 100644 index 0000000000..faf2309802 --- /dev/null +++ b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t8601c.sem @@ -0,0 +1 @@ +nullPointers diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t8601d.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t8601d.sem new file mode 100644 index 0000000000..faf2309802 --- /dev/null +++ b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.12.17/run/t8601d.sem @@ -0,0 +1 @@ +nullPointers diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/BlacklistedTests.txt b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/BlacklistedTests.txt index 1aad0d6248..cd03f2194a 100644 --- a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/BlacklistedTests.txt +++ b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/BlacklistedTests.txt @@ -662,20 +662,7 @@ run/enums.scala run/t3719.scala run/t8611b.scala -# Exceptions that become JavaScriptException - -run/pf-catch.scala -run/exceptions-2.scala -run/exceptions-nest.scala -run/t8601c.scala -run/t8601b.scala -run/inlineHandlers.scala - -# Expecting unsupported exceptions (e.g. ArrayIndexOutOfBounds) -run/optimizer-array-load.scala -run/t6827.scala -run/t8601.scala -run/t5887.scala +# Expecting exceptions that become JS `TypeError`s in Scala.js due to reflective calls run/t11534c.scala # Expecting exceptions that are linking errors in Scala.js (e.g. NoSuchMethodException) @@ -1033,6 +1020,7 @@ run/small-seq-apply.scala # scala.tools.testkit.AssertUtil run/productElementName.scala +run/t5887.scala # Using .java source files diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/exceptions-2.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/exceptions-2.sem new file mode 100644 index 0000000000..faf2309802 --- /dev/null +++ b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/exceptions-2.sem @@ -0,0 +1 @@ +nullPointers diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/exceptions-nest.check b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/exceptions-nest.check new file mode 100644 index 0000000000..f4f003cf62 --- /dev/null +++ b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/exceptions-nest.check @@ -0,0 +1,13 @@ +2 +23 +2 +5 +2 +4 +OK +4 +OK +10 +1 +undefined +10 diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/exceptions-nest.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/exceptions-nest.sem new file mode 100644 index 0000000000..faf2309802 --- /dev/null +++ b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/exceptions-nest.sem @@ -0,0 +1 @@ +nullPointers diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/inlineHandlers.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/inlineHandlers.sem new file mode 100644 index 0000000000..faf2309802 --- /dev/null +++ b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/inlineHandlers.sem @@ -0,0 +1 @@ +nullPointers diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/optimizer-array-load.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/optimizer-array-load.sem new file mode 100644 index 0000000000..6e7bf4e75c --- /dev/null +++ b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/optimizer-array-load.sem @@ -0,0 +1 @@ +arrayIndexOutOfBounds diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/pf-catch.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/pf-catch.sem new file mode 100644 index 0000000000..faf2309802 --- /dev/null +++ b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/pf-catch.sem @@ -0,0 +1 @@ +nullPointers diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t6827.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t6827.sem new file mode 100644 index 0000000000..6e7bf4e75c --- /dev/null +++ b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t6827.sem @@ -0,0 +1 @@ +arrayIndexOutOfBounds diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t8601b.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t8601b.sem new file mode 100644 index 0000000000..4de3a97396 --- /dev/null +++ b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t8601b.sem @@ -0,0 +1,2 @@ +negativeArraySizes +nullPointers diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t8601c.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t8601c.sem new file mode 100644 index 0000000000..faf2309802 --- /dev/null +++ b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t8601c.sem @@ -0,0 +1 @@ +nullPointers diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t8601d.sem b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t8601d.sem new file mode 100644 index 0000000000..faf2309802 --- /dev/null +++ b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.13.10/run/t8601d.sem @@ -0,0 +1 @@ +nullPointers diff --git a/partest/src/main/scala/scala/tools/nsc/MainGenericRunner.scala b/partest/src/main/scala/scala/tools/nsc/MainGenericRunner.scala index 1425dec72e..184b0afea9 100644 --- a/partest/src/main/scala/scala/tools/nsc/MainGenericRunner.scala +++ b/partest/src/main/scala/scala/tools/nsc/MainGenericRunner.scala @@ -64,6 +64,7 @@ class MainGenericRunner { case "arrayIndexOutOfBounds" => prev.withArrayIndexOutOfBounds(Compliant) case "arrayStores" => prev.withArrayStores(Compliant) case "negativeArraySizes" => prev.withNegativeArraySizes(Compliant) + case "nullPointers" => prev.withNullPointers(Compliant) case "stringIndexOutOfBounds" => prev.withStringIndexOutOfBounds(Compliant) case "moduleInit" => prev.withModuleInit(Compliant) } diff --git a/project/BinaryIncompatibilities.scala b/project/BinaryIncompatibilities.scala index 951b6c5cec..653d0c0200 100644 --- a/project/BinaryIncompatibilities.scala +++ b/project/BinaryIncompatibilities.scala @@ -5,12 +5,19 @@ import com.typesafe.tools.mima.core.ProblemFilters._ object BinaryIncompatibilities { val IR = Seq( + // Breaking, but in minor verison, so OK. + exclude[Problem]("org.scalajs.ir.*"), ) val Linker = Seq( + // Breaking, but in minor version, so OK. + exclude[Problem]("org.scalajs.linker.standard.*"), ) val LinkerInterface = Seq( + // Breaking, but in minor version, so OK. + exclude[Problem]("org.scalajs.linker.interface.unstable.*"), + // private, not an issue ProblemFilters.exclude[DirectMissingMethodProblem]("org.scalajs.linker.interface.Semantics.this"), ) @@ -21,9 +28,47 @@ object BinaryIncompatibilities { val TestAdapter = Seq( ) + private val JSTupleUnapplyExclusion: ProblemFilter = { + /* !!! Very delicate + * + * We changed the result type of `js.TupleN.unapply` from `Option` to + * `Some`, to make them irrefutable from Scala 3's point of view. This + * breaks binary compat, so we added a `protected` overload with the old + * binary signature. + * + * Unfortunately, those do not get a *static forwarder* in the class file, + * and hence MiMa still complains about them. Although the error message is + * clearly about "static method"s, the *filter* to apply is + * indistinguishable between the instance and static methods! + * + * Therefore, we implement here our own filter that only matches the + * *static* `unapply` method. + * + * Note that even though MiMa reports potential issues with static methods, + * these are ghost proplems. They do not exist in the .sjsir files to begin + * with, because the companion trait is a JS trait. We only generate static + * forwarders in Scala classes and traits. So filtering out the static + * method incompatibilities is legit. + */ + + val JSTupleUnapplyFullNameRegex = raw"""scala\.scalajs\.js\.Tuple\d+\.unapply""".r + + { (problem: Problem) => + val isStaticJSTupleUnapply = problem match { + case problem: IncompatibleResultTypeProblem => + problem.ref.isStatic && (problem.ref.fullName match { + case JSTupleUnapplyFullNameRegex() => true + case _ => false + }) + case _ => + false + } + !isStaticJSTupleUnapply // true to keep; false to filter out the problem + } + } + val Library = Seq( - // new methods in sealed trait, not an issue - ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.scalajs.js.Dynamic.**"), + JSTupleUnapplyExclusion, ) val TestInterface = Seq( diff --git a/project/Build.scala b/project/Build.scala index fe238e92fd..44a901737f 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -47,6 +47,7 @@ object ExposedValues extends AutoPlugin { .withArrayIndexOutOfBounds(CheckedBehavior.Compliant) .withArrayStores(CheckedBehavior.Compliant) .withNegativeArraySizes(CheckedBehavior.Compliant) + .withNullPointers(CheckedBehavior.Compliant) .withStringIndexOutOfBounds(CheckedBehavior.Compliant) .withModuleInit(CheckedBehavior.Compliant) } @@ -230,7 +231,6 @@ object Build { } import MultiScalaProject.{ - Default2_11ScalaVersion, Default2_12ScalaVersion, Default2_13ScalaVersion, DefaultScalaVersion @@ -245,22 +245,27 @@ object Build { val packageMinilib = taskKey[File]("Produces the minilib jar.") + val saveForStabilityTest = taskKey[Unit]( + "Saves the output of fastLinkJS for a later stability test") + val checkStability = taskKey[Unit]( + "Checks that the output of fastLinkJS corresponds to the saved stability test") + val forceRelinkForStabilityTest = taskKey[Unit]( + "Deletes the output directory of fastLinkJS to force it to rerun") + val previousVersions = List("1.0.0", "1.0.1", "1.1.0", "1.1.1", "1.2.0", "1.3.0", "1.3.1", "1.4.0", "1.5.0", "1.5.1", "1.6.0", "1.7.0", "1.7.1", - "1.8.0", "1.9.0", "1.10.0", "1.10.1", "1.11.0") + "1.8.0", "1.9.0", "1.10.0", "1.10.1", "1.11.0", "1.12.0") val previousVersion = previousVersions.last val previousBinaryCrossVersion = CrossVersion.binaryWith("sjs1_", "") val scalaVersionsUsedForPublishing: Set[String] = - Set(Default2_11ScalaVersion, Default2_12ScalaVersion, Default2_13ScalaVersion) + Set(Default2_12ScalaVersion, Default2_13ScalaVersion) val newScalaBinaryVersionsInThisRelease: Set[String] = Set() - def hasNewCollections(version: String): Boolean = { - !version.startsWith("2.11.") && + def hasNewCollections(version: String): Boolean = !version.startsWith("2.12.") - } /** Returns the appropriate subdirectory of `sourceDir` depending on the * collection "era" used by the `scalaV`. @@ -563,7 +568,6 @@ object Build { val prev = (scalacOptions in (Compile, doc)).value val scalaV = scalaVersion.value def scaladocFullySupportsJDKgreaterThan8 = { - !scalaV.startsWith("2.11.") && !scalaV.startsWith("2.12.") && scalaV != "2.13.0" && scalaV != "2.13.1" && scalaV != "2.13.2" } @@ -1015,7 +1019,6 @@ object Build { publishSettings(None), fatalWarningsSettings, name := "Scala.js linker", - ensureSAMSupportSetting, unmanagedSourceDirectories in Compile += baseDirectory.value.getParentFile.getParentFile / "shared/src/main/scala", @@ -1215,9 +1218,6 @@ object Build { scriptedDependencies := { scriptedDependencies.dependsOn( // Compiler Plugins - publishLocal in compiler.v2_11, - publishLocal in jUnitPlugin.v2_11, - publishLocal in compiler.v2_12, publishLocal in jUnitPlugin.v2_12, @@ -1227,12 +1227,6 @@ object Build { // JS libs publishLocal in javalib, - publishLocal in library.v2_11, - publishLocal in testInterface.v2_11, - publishLocal in testBridge.v2_11, - publishLocal in jUnitRuntime.v2_11, - publishLocal in irProjectJS.v2_11, - publishLocal in library.v2_12, publishLocal in testInterface.v2_12, publishLocal in testBridge.v2_12, @@ -1275,13 +1269,6 @@ object Build { else Seq("-Ydelambdafy:method")) } - lazy val ensureSAMSupportSetting: Setting[_] = { - scalacOptions ++= { - if (scalaBinaryVersion.value == "2.11") Seq("-Xexperimental") - else Nil - } - } - lazy val javalibintf: Project = Project( id = "javalibintf", base = file("javalibintf") ).settings( @@ -1564,7 +1551,6 @@ object Build { fatalWarningsSettings, name := "Scala.js library", delambdafySetting, - ensureSAMSupportSetting, exportJars := !isGeneratingForIDE, previousArtifactSetting, mimaBinaryIssueFilters ++= BinaryIncompatibilities.Library, @@ -1587,42 +1573,22 @@ object Build { // Filter doc sources to remove implementation details from doc. sources in doc := { val prev = (sources in doc).value - val javaV = javaVersion.value - val scalaV = scalaVersion.value - - /* On Java 9+, Scaladoc will crash with "bad constant pool tag 20" - * until version 2.12.1 included. The problem seems to have been - * fixed in 2.12.2, perhaps through - * https://github.com/scala/scala/pull/5711. - * See also #3152. - */ - val mustAvoidJavaDoc = { - javaV >= 9 && { - scalaV.startsWith("2.11.") || - scalaV == "2.12.0" || - scalaV == "2.12.1" - } - } - if (!mustAvoidJavaDoc) { - def containsFileFilter(s: String): FileFilter = new FileFilter { - override def accept(f: File): Boolean = { - val path = f.getAbsolutePath.replace('\\', '/') - path.contains(s) - } + def containsFileFilter(s: String): FileFilter = new FileFilter { + override def accept(f: File): Boolean = { + val path = f.getAbsolutePath.replace('\\', '/') + path.contains(s) } + } - val filter: FileFilter = ( - AllPassFilter - -- containsFileFilter("/scala/scalajs/runtime/") - -- containsFileFilter("/scala/scalajs/js/annotation/internal/") - -- "*.nodoc.scala" - ) + val filter: FileFilter = ( + AllPassFilter + -- containsFileFilter("/scala/scalajs/runtime/") + -- containsFileFilter("/scala/scalajs/js/annotation/internal/") + -- "*.nodoc.scala" + ) - prev.filter(filter.accept) - } else { - Nil - } + prev.filter(filter.accept) }, /* Add compiled .class files to doc dependencyClasspath, so we can @@ -1821,27 +1787,19 @@ object Build { MyScalaJSPlugin.expectedSizes := { scalaVersion.value match { - case Default2_11ScalaVersion => - Some(ExpectedSizes( - fastLink = 383000 to 384000, - fullLink = 79000 to 80000, - fastLinkGz = 49000 to 50000, - fullLinkGz = 21000 to 22000, - )) - case Default2_12ScalaVersion => Some(ExpectedSizes( - fastLink = 760000 to 761000, + fastLink = 772000 to 773000, fullLink = 145000 to 146000, - fastLinkGz = 89000 to 90000, + fastLinkGz = 91000 to 92000, fullLinkGz = 35000 to 36000, )) case Default2_13ScalaVersion => Some(ExpectedSizes( - fastLink = 449000 to 450000, + fastLink = 456000 to 457000, fullLink = 97000 to 98000, - fastLinkGz = 58000 to 59000, + fastLinkGz = 59000 to 60000, fullLinkGz = 26000 to 27000, )) @@ -1875,19 +1833,6 @@ object Build { publishArtifact in Compile := false, scalacOptions ~= (_.filter(_ != "-deprecation")), - // To support calls to static methods in interfaces - scalacOptions in Test ++= { - /* Starting from 2.11.12, scalac refuses to emit calls to static methods - * in interfaces unless the -target:jvm-1.8 flag is given. - * scalac 2.12+ emits JVM 8 bytecode by default, of course, so it is not - * needed for later versions. - */ - if (scalaVersion.value.startsWith("2.11.")) - Seq("-target:jvm-1.8") - else - Nil - }, - // Need reflect for typechecking macros libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value % "provided", @@ -1901,49 +1846,25 @@ object Build { val javaV = javaVersion.value val scalaV = scalaVersion.value - val isScalaAtLeast212 = !scalaV.startsWith("2.11.") List(sharedTestDir / "scala", sharedTestDir / "require-scala2") ::: collectionsEraDependentDirectory(scalaV, sharedTestDir) :: includeIf(sharedTestDir / "require-jdk11", javaV >= 11) ::: includeIf(sharedTestDir / "require-jdk15", javaV >= 15) ::: - includeIf(testDir / "require-2.12", isJSTest && isScalaAtLeast212) ::: includeIf(testDir / "require-scala2", isJSTest) }, - sources in Test ++= { - val supportsSAM = scalaBinaryVersion.value match { - case "2.11" => scalacOptions.value.contains("-Xexperimental") - case _ => true - } - + sources in Test := { + val allSources = (sources in Test).value val scalaV = scalaVersion.value - /* Can't add require-sam as unmanagedSourceDirectories because of the - * use of scalacOptions. Hence sources are added individually. - * Note that a testSuite/test will not trigger a compile when sources - * are modified in require-sam - */ - if (supportsSAM) { - val testDir = (sourceDirectory in Test).value - val sharedTestDir = - testDir.getParentFile.getParentFile.getParentFile / "shared/src/test" - - val allSAMSources = { - ((sharedTestDir / "require-sam") ** "*.scala").get ++ - (if (isJSTest) ((testDir / "require-sam") ** "*.scala").get else Nil) - } + val hasBugWithOverriddenMethods = + Set("2.12.2", "2.12.3", "2.12.4").contains(scalaV) - val hasBugWithOverriddenMethods = - Set("2.12.0", "2.12.1", "2.12.2", "2.12.3", "2.12.4").contains(scalaV) - - if (hasBugWithOverriddenMethods) - allSAMSources.filter(_.getName != "SAMWithOverridingBridgesTest.scala") - else - allSAMSources - } else { - Nil - } + if (hasBugWithOverriddenMethods) + allSources.filter(_.getName != "SAMWithOverridingBridgesTest.scala") + else + allSources } ) @@ -2126,6 +2047,7 @@ object Build { "compliantArrayIndexOutOfBounds" -> (sems.arrayIndexOutOfBounds == CheckedBehavior.Compliant), "compliantArrayStores" -> (sems.arrayStores == CheckedBehavior.Compliant), "compliantNegativeArraySizes" -> (sems.negativeArraySizes == CheckedBehavior.Compliant), + "compliantNullPointers" -> (sems.nullPointers == CheckedBehavior.Compliant), "compliantStringIndexOutOfBounds" -> (sems.stringIndexOutOfBounds == CheckedBehavior.Compliant), "compliantModuleInit" -> (sems.moduleInit == CheckedBehavior.Compliant), "strictFloats" -> sems.strictFloats, @@ -2212,6 +2134,38 @@ object Build { actual } }, + + // Infrastructure for stability test + inConfig(Test)(Def.settings( + saveForStabilityTest / artifactPath := { + // this path intentionally survives a `clean` + (LocalRootProject / baseDirectory).value / "test-suite/target/test-suite-stability.js", + }, + saveForStabilityTest := { + val output = fastLinkJSOutput.value / "main.js" + val targetFile = (saveForStabilityTest / artifactPath).value + IO.copyFile(output, targetFile) + }, + checkStability := { + val log = streams.value.log + val rootDir = (LocalRootProject / baseDirectory).value + val reference = (saveForStabilityTest / artifactPath).value + val output = fastLinkJSOutput.value / "main.js" + if (java.util.Arrays.equals(IO.readBytes(reference), IO.readBytes(output))) { + log.info("Stability check passed") + } else { + def rel(f: File): String = + f.relativeTo(rootDir).getOrElse(f).toString().replace('\\', '/') + throw new MessageOnlyException( + "Stability check failed; show diff with\n" + + s"diff -u ${rel(reference)} ${rel(output)}") + } + }, + forceRelinkForStabilityTest := { + val outputDir = (fastLinkJS / scalaJSLinkerOutputDirectory).value + IO.delete(outputDir) + }, + )), ).zippedSettings(testSuiteLinker)( l => inConfig(Bootstrap)(testSuiteBootstrapSetting(l)) ).withScalaJSCompiler.withScalaJSJUnitPlugin.dependsOnLibrary.dependsOn( @@ -2262,7 +2216,6 @@ object Build { name := "Java Ext Dummies library for Scala.js", publishArtifact in Compile := false, delambdafySetting, - ensureSAMSupportSetting, // Ensure that .class files are not used in downstream projects exportJars := true, @@ -2361,10 +2314,8 @@ object Build { }, ) - private def useOldPartest(scalaV: String): Boolean = { - scalaV.startsWith("2.11.") || + private def useOldPartest(scalaV: String): Boolean = (scalaV.startsWith("2.12.") && scalaV.substring(5).takeWhile(_.isDigit).toInt < 13) - } lazy val partest: MultiScalaProject = MultiScalaProject( id = "partest", base = file("partest") @@ -2417,10 +2368,7 @@ object Build { { val scalaV = scalaVersion.value if (useOldPartest(scalaV)) { - if (scalaV.startsWith("2.11.")) - "org.scala-lang.modules" %% "scala-partest" % "1.0.16" - else - "org.scala-lang.modules" %% "scala-partest" % "1.1.4" + "org.scala-lang.modules" %% "scala-partest" % "1.1.4" } else { "org.scala-lang" % "scala-partest" % scalaV } @@ -2513,8 +2461,7 @@ object Build { val scalaV = scalaVersion.value val upstreamSrcDir = (fetchScalaSource in partest).value - if (scalaV.startsWith("2.11.") || - scalaV.startsWith("2.12.")) { + if (scalaV.startsWith("2.12.")) { Nil } else { List(upstreamSrcDir / "src/testkit/scala/tools/testkit/AssertUtil.scala") diff --git a/project/JavaLangObject.scala b/project/JavaLangObject.scala index 2487e451a9..aa2c4d1931 100644 --- a/project/JavaLangObject.scala +++ b/project/JavaLangObject.scala @@ -13,6 +13,7 @@ import org.scalajs.ir.OriginalName.NoOriginalName import org.scalajs.ir.Trees._ import org.scalajs.ir.Types._ import org.scalajs.ir.Position.NoPosition +import org.scalajs.ir.Version.Unversioned /** Hard-coded IR for java.lang.Object. * We cannot so much as begin to fake a compilation of java.lang.Object, @@ -42,6 +43,7 @@ object JavaLangObject { Nil, None, None, + fields = Nil, List( /* def this() = () */ MethodDef( @@ -50,7 +52,7 @@ object JavaLangObject { NoOriginalName, Nil, NoType, - Some(Skip()))(OptimizerHints.empty, None), + Some(Skip()))(OptimizerHints.empty, Unversioned), /* def getClass(): java.lang.Class[_] = (this) */ MethodDef( @@ -61,7 +63,7 @@ object JavaLangObject { ClassType(ClassClass), Some { GetClass(This()(ThisType)) - })(OptimizerHints.empty.withInline(true), None), + })(OptimizerHints.empty.withInline(true), Unversioned), /* def hashCode(): Int = (this) */ MethodDef( @@ -72,7 +74,7 @@ object JavaLangObject { IntType, Some { IdentityHashCode(This()(ThisType)) - })(OptimizerHints.empty.withInline(true), None), + })(OptimizerHints.empty.withInline(true), Unversioned), /* def equals(that: Object): Boolean = this eq that */ MethodDef( @@ -86,7 +88,7 @@ object JavaLangObject { BinaryOp(BinaryOp.===, This()(ThisType), VarRef(LocalIdent(LocalName("that")))(AnyType)) - })(OptimizerHints.empty.withInline(true), None), + })(OptimizerHints.empty.withInline(true), Unversioned), /* protected def clone(): Object = * if (this.isInstanceOf[Cloneable]) (this.asInstanceOf[Cloneable]) @@ -105,7 +107,7 @@ object JavaLangObject { Throw(New(ClassName("java.lang.CloneNotSupportedException"), MethodIdent(NoArgConstructorName), Nil)) })(AnyType) - })(OptimizerHints.empty.withInline(true), None), + })(OptimizerHints.empty.withInline(true), Unversioned), /* def toString(): String = * getClass().getName() + "@" + Integer.toHexString(hashCode()) @@ -133,7 +135,7 @@ object JavaLangObject { MethodIdent(MethodName("toHexString", List(IntRef), StringClassRef)), List(Apply(EAF, This()(ThisType), MethodIdent(MethodName("hashCode", Nil, IntRef)), Nil)(IntType)))( ClassType(BoxedStringClass))) - })(OptimizerHints.empty, None), + })(OptimizerHints.empty, Unversioned), /* Since wait() is not supported in any way, a correct implementation * of notify() and notifyAll() is to do nothing. @@ -146,7 +148,7 @@ object JavaLangObject { NoOriginalName, Nil, NoType, - Some(Skip()))(OptimizerHints.empty, None), + Some(Skip()))(OptimizerHints.empty, Unversioned), /* def notifyAll(): Unit = () */ MethodDef( @@ -155,7 +157,7 @@ object JavaLangObject { NoOriginalName, Nil, NoType, - Some(Skip()))(OptimizerHints.empty, None), + Some(Skip()))(OptimizerHints.empty, Unversioned), /* def finalize(): Unit = () */ MethodDef( @@ -164,10 +166,10 @@ object JavaLangObject { NoOriginalName, Nil, NoType, - Some(Skip()))(OptimizerHints.empty, None), - - // Exports - + Some(Skip()))(OptimizerHints.empty, Unversioned), + ), + jsConstructor = None, + jsMethodProps = List( /* JSExport for toString(). */ JSMethodDef( MemberFlags.empty, @@ -177,9 +179,10 @@ object JavaLangObject { Apply(EAF, This()(ThisType), MethodIdent(MethodName("toString", Nil, StringClassRef)), Nil)(ClassType(BoxedStringClass)) - })(OptimizerHints.empty, None) + })(OptimizerHints.empty, Unversioned) ), - Nil)(OptimizerHints.empty) + jsNativeMembers = Nil, + topLevelExportDefs = Nil)(OptimizerHints.empty) Hashers.hashClassDef(classDef) } diff --git a/project/JavalibIRCleaner.scala b/project/JavalibIRCleaner.scala index 8214b6ee71..58fdc14a8a 100644 --- a/project/JavalibIRCleaner.scala +++ b/project/JavalibIRCleaner.scala @@ -5,6 +5,7 @@ import org.scalajs.ir.ClassKind import org.scalajs.ir.Names._ import org.scalajs.ir.Trees._ import org.scalajs.ir.Types._ +import org.scalajs.ir.Version.Unversioned import java.io._ import java.net.URI @@ -148,16 +149,12 @@ final class JavalibIRCleaner(baseDirectoryURI: URI) { /* Remove the `private def writeReplace__O` generated by scalac 2.13+ * in the companion of serializable classes. */ - val newMemberDefs = memberDefs.filter { - case MethodDef(_, MethodIdent(`writeReplaceMethodName`), _, _, _, _) => - false - case _ => - true - } + val newMethods = methods.filter(_.name.name != writeReplaceMethodName) val preprocessedTree = ClassDef(name, originalName, kind, jsClassCaptures, - superClass, newInterfaces, jsSuperClass, jsNativeLoadSpec, - newMemberDefs, topLevelExportDefs)( + superClass, newInterfaces, jsSuperClass, jsNativeLoadSpec, fields, + newMethods, jsConstructor, jsMethodProps, jsNativeMembers, + topLevelExportDefs)( optimizerHints)(pos) // Only validate the hierarchy; do not transform @@ -196,20 +193,24 @@ final class JavalibIRCleaner(baseDirectoryURI: URI) { } } - override def transformMemberDef(memberDef: MemberDef): MemberDef = { - super.transformMemberDef(memberDef) match { + override def transformAnyFieldDef(fieldDef: AnyFieldDef): AnyFieldDef = { + super.transformAnyFieldDef(fieldDef) match { case m @ FieldDef(flags, name, originalName, ftpe) => implicit val pos = m.pos FieldDef(flags, name, originalName, transformType(ftpe)) - case m @ MethodDef(flags, name, originalName, args, resultType, body) => - implicit val pos = m.pos - MethodDef(flags, transformMethodIdent(name), originalName, transformParamDefs(args), - transformType(resultType), body)(m.optimizerHints, m.hash) - case m => - m + + case f: JSFieldDef => f } } + override def transformMethodDef(methodDef: MethodDef): MethodDef = { + val m = super.transformMethodDef(methodDef) + val MethodDef(flags, name, originalName, args, resultType, body) = m + implicit val pos = m.pos + MethodDef(flags, transformMethodIdent(name), originalName, transformParamDefs(args), + transformType(resultType), body)(m.optimizerHints, Unversioned) + } + /** Eliminate bridges that have become redundant because of our additional erasure. */ private def eliminateRedundantBridges(classDef: ClassDef): ClassDef = { import MemberNamespace._ @@ -223,33 +224,33 @@ final class JavalibIRCleaner(baseDirectoryURI: URI) { } } - val memberDefs = classDef.memberDefs - // Instance bridges, which call "themselves" (another version of themselves with the same name) - def isRedundantBridge(memberDef: MemberDef): Boolean = memberDef match { - case MethodDef(flags, MethodIdent(name), _, paramDefs, _, Some(body)) if flags.namespace == Public => + def isRedundantBridge(method: MethodDef): Boolean = { + val MethodDef(flags, MethodIdent(name), _, paramDefs, _, body) = method + + flags.namespace == Public && { body match { - case Apply(ApplyFlags.empty, This(), MethodIdent(`name`), args) => + case Some(Apply(ApplyFlags.empty, This(), MethodIdent(`name`), args)) => argsCorrespond(args, paramDefs) case _ => false } - case _ => - false + } } - val newMemberDefs1 = memberDefs.filterNot(isRedundantBridge(_)) + val newMethods1 = classDef.methods.filterNot(isRedundantBridge(_)) // Make sure that we did not remove *all* overloads for any method name - def publicMethodNames(memberDefs: List[MemberDef]): Set[MethodName] = { - memberDefs.collect { - case MethodDef(flags, name, _, _, _, _) if flags.namespace == Public => name.name - }.toSet + def publicMethodNames(methods: List[MethodDef]): Set[MethodName] = { + methods + .withFilter(_.flags.namespace == Public) + .map(_.name.name) + .toSet } - val lostMethodNames = publicMethodNames(memberDefs) -- publicMethodNames(newMemberDefs1) + val lostMethodNames = publicMethodNames(classDef.methods) -- publicMethodNames(newMethods1) if (lostMethodNames.nonEmpty) { for (lostMethodName <- lostMethodNames) reportError(s"eliminateRedundantBridges removed all overloads of ${lostMethodName.nameString}")(classDef.pos) @@ -270,13 +271,11 @@ final class JavalibIRCleaner(baseDirectoryURI: URI) { } val seenStaticForwarderNames = mutable.Set.empty[MethodName] - val newMemberDefs2 = newMemberDefs1.filter { memberDef => - memberDef match { - case m: MethodDef if isStaticForwarder(m) => - seenStaticForwarderNames.add(m.name.name) // keep if it is the first one - case _ => - true // always keep - } + val newMethods2 = newMethods1.filter { m => + if (isStaticForwarder(m)) + seenStaticForwarderNames.add(m.name.name) // keep if it is the first one + else + true // always keep } new ClassDef( @@ -288,7 +287,11 @@ final class JavalibIRCleaner(baseDirectoryURI: URI) { classDef.interfaces, classDef.jsSuperClass, classDef.jsNativeLoadSpec, - newMemberDefs2, + classDef.fields, + newMethods2, + classDef.jsConstructor, + classDef.jsMethodProps, + classDef.jsNativeMembers, classDef.topLevelExportDefs )(classDef.optimizerHints)(classDef.pos) } @@ -364,7 +367,7 @@ final class JavalibIRCleaner(baseDirectoryURI: URI) { JSUnaryOp(JSUnaryOp.!, JSUnaryOp(JSUnaryOp.!, arg)), BooleanType) - // 2.11 s"..." interpolator + // s"..." interpolator in 2.12.2 up to 2.12.4 case Apply( ApplyFlags.empty, New(StringContextClass, MethodIdent(`stringContextCtorMethodName`), @@ -552,15 +555,11 @@ final class JavalibIRCleaner(baseDirectoryURI: URI) { private def postTransformChecks(classDef: ClassDef): Unit = { // Check that no two methods have been erased to the same name val seenMethodNames = mutable.Set.empty[(MemberNamespace, MethodName)] - for (m <- classDef.memberDefs) { - m match { - case MethodDef(flags, name, _, _, _, _) => - if (!seenMethodNames.add((flags.namespace, name.name))) { - reportError( - s"duplicate method name ${name.name.nameString} after erasure")( - m.pos) - } - case _ => + classDef.methods.foreach { m => + if (!seenMethodNames.add((m.flags.namespace, m.name.name))) { + reportError( + s"duplicate method name ${m.name.name.nameString} after erasure")( + m.pos) } } } diff --git a/project/MiniLib.scala b/project/MiniLib.scala index 449141944b..0d447e4e30 100644 --- a/project/MiniLib.scala +++ b/project/MiniLib.scala @@ -37,6 +37,7 @@ object MiniLib { "CloneNotSupportedException", "IndexOutOfBoundsException", "NegativeArraySizeException", + "NullPointerException", "StringIndexOutOfBoundsException" ).map("java/lang/" + _) diff --git a/project/MultiScalaProject.scala b/project/MultiScalaProject.scala index 48450ff508..deb5f0c163 100644 --- a/project/MultiScalaProject.scala +++ b/project/MultiScalaProject.scala @@ -8,7 +8,6 @@ final class MultiScalaProject private (private val projects: Map[String, Project extends CompositeProject { import MultiScalaProject._ - val v2_11: Project = projects("2.11") val v2_12: Project = projects("2.12") val v2_13: Project = projects("2.13") @@ -81,11 +80,7 @@ object MultiScalaProject { v.map(v => (v._1, f(v._2))) private final val versions = Map[String, Seq[String]]( - "2.11" -> Seq( - "2.11.12", - ), "2.12" -> Seq( - "2.12.1", "2.12.2", "2.12.3", "2.12.4", @@ -118,7 +113,6 @@ object MultiScalaProject { ), ) - val Default2_11ScalaVersion = versions("2.11").last val Default2_12ScalaVersion = versions("2.12").last val Default2_13ScalaVersion = versions("2.13").last diff --git a/sbt-plugin/src/sbt-test/cross-version/2.11/build.sbt b/sbt-plugin/src/sbt-test/cross-version/2.11/build.sbt deleted file mode 100644 index 28e1b13511..0000000000 --- a/sbt-plugin/src/sbt-test/cross-version/2.11/build.sbt +++ /dev/null @@ -1,7 +0,0 @@ -enablePlugins(ScalaJSPlugin) -enablePlugins(ScalaJSJUnitPlugin) - -version := scalaJSVersion -scalaVersion := "2.11.12" - -scalaJSUseMainModuleInitializer := true diff --git a/sbt-plugin/src/sbt-test/cross-version/2.11/project/build.properties b/sbt-plugin/src/sbt-test/cross-version/2.11/project/build.properties deleted file mode 100644 index c0bab04941..0000000000 --- a/sbt-plugin/src/sbt-test/cross-version/2.11/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.2.8 diff --git a/sbt-plugin/src/sbt-test/cross-version/2.11/project/build.sbt b/sbt-plugin/src/sbt-test/cross-version/2.11/project/build.sbt deleted file mode 100644 index 7de678c575..0000000000 --- a/sbt-plugin/src/sbt-test/cross-version/2.11/project/build.sbt +++ /dev/null @@ -1 +0,0 @@ -addSbtPlugin("org.scala-js" % "sbt-scalajs" % sys.props("plugin.version")) diff --git a/sbt-plugin/src/sbt-test/cross-version/2.11/src/main/scala/sbttest/Lib.scala b/sbt-plugin/src/sbt-test/cross-version/2.11/src/main/scala/sbttest/Lib.scala deleted file mode 100644 index 95dcc364a5..0000000000 --- a/sbt-plugin/src/sbt-test/cross-version/2.11/src/main/scala/sbttest/Lib.scala +++ /dev/null @@ -1,11 +0,0 @@ -package sbttest - -object Lib { - - /** appends `_foo` to a string */ - def foo(x: String): String = x + "foo" - - /** squares a number */ - def sq(x: Int): Int = x * x - -} diff --git a/sbt-plugin/src/sbt-test/cross-version/2.11/src/main/scala/sbttest/TestApp.scala b/sbt-plugin/src/sbt-test/cross-version/2.11/src/main/scala/sbttest/TestApp.scala deleted file mode 100644 index 403faa8112..0000000000 --- a/sbt-plugin/src/sbt-test/cross-version/2.11/src/main/scala/sbttest/TestApp.scala +++ /dev/null @@ -1,8 +0,0 @@ -package sbttest - -object TestApp { - def main(args: Array[String]): Unit = { - println(Lib.foo("Hello World")) - println(Lib.sq(10)) - } -} diff --git a/sbt-plugin/src/sbt-test/cross-version/2.11/src/test/scala/sbttest/LibTest.scala b/sbt-plugin/src/sbt-test/cross-version/2.11/src/test/scala/sbttest/LibTest.scala deleted file mode 100644 index 2634701972..0000000000 --- a/sbt-plugin/src/sbt-test/cross-version/2.11/src/test/scala/sbttest/LibTest.scala +++ /dev/null @@ -1,16 +0,0 @@ -package sbttest - -import org.junit.Test -import org.junit.Assert._ - -class LibTest { - @Test def dummyLibraryHasFoo(): Unit = { - assertEquals("foo", Lib.foo("")) - assertEquals("afoo", Lib.foo("a")) - } - - @Test def dummyLibraryHasSq(): Unit = { - assertEquals(0, Lib.sq(0)) - assertEquals(100, Lib.sq(10)) - } -} diff --git a/sbt-plugin/src/sbt-test/cross-version/2.11/test b/sbt-plugin/src/sbt-test/cross-version/2.11/test deleted file mode 100644 index e57d7c9b72..0000000000 --- a/sbt-plugin/src/sbt-test/cross-version/2.11/test +++ /dev/null @@ -1,3 +0,0 @@ -> ; run ; test ; testHtml -> clean -> ; set scalaJSStage in Global := FullOptStage ; run ; test ; testHtml diff --git a/sbt-plugin/src/sbt-test/cross-version/2.13/project/build.properties b/sbt-plugin/src/sbt-test/cross-version/2.13/project/build.properties index c0bab04941..6adcdc753f 100644 --- a/sbt-plugin/src/sbt-test/cross-version/2.13/project/build.properties +++ b/sbt-plugin/src/sbt-test/cross-version/2.13/project/build.properties @@ -1 +1 @@ -sbt.version=1.2.8 +sbt.version=1.3.3 diff --git a/sbt-plugin/src/sbt-test/incremental/change-config-and-source/project/build.properties b/sbt-plugin/src/sbt-test/incremental/change-config-and-source/project/build.properties index c0bab04941..6adcdc753f 100644 --- a/sbt-plugin/src/sbt-test/incremental/change-config-and-source/project/build.properties +++ b/sbt-plugin/src/sbt-test/incremental/change-config-and-source/project/build.properties @@ -1 +1 @@ -sbt.version=1.2.8 +sbt.version=1.3.3 diff --git a/sbt-plugin/src/sbt-test/incremental/change-config/project/build.properties b/sbt-plugin/src/sbt-test/incremental/change-config/project/build.properties index c0bab04941..6adcdc753f 100644 --- a/sbt-plugin/src/sbt-test/incremental/change-config/project/build.properties +++ b/sbt-plugin/src/sbt-test/incremental/change-config/project/build.properties @@ -1 +1 @@ -sbt.version=1.2.8 +sbt.version=1.3.3 diff --git a/sbt-plugin/src/sbt-test/incremental/fix-compile-error/project/build.properties b/sbt-plugin/src/sbt-test/incremental/fix-compile-error/project/build.properties index c0bab04941..6adcdc753f 100644 --- a/sbt-plugin/src/sbt-test/incremental/fix-compile-error/project/build.properties +++ b/sbt-plugin/src/sbt-test/incremental/fix-compile-error/project/build.properties @@ -1 +1 @@ -sbt.version=1.2.8 +sbt.version=1.3.3 diff --git a/sbt-plugin/src/sbt-test/linker/concurrent-linker-use/project/build.properties b/sbt-plugin/src/sbt-test/linker/concurrent-linker-use/project/build.properties index c0bab04941..6adcdc753f 100644 --- a/sbt-plugin/src/sbt-test/linker/concurrent-linker-use/project/build.properties +++ b/sbt-plugin/src/sbt-test/linker/concurrent-linker-use/project/build.properties @@ -1 +1 @@ -sbt.version=1.2.8 +sbt.version=1.3.3 diff --git a/sbt-plugin/src/sbt-test/linker/custom-linker/project/build.properties b/sbt-plugin/src/sbt-test/linker/custom-linker/project/build.properties index c0bab04941..6adcdc753f 100644 --- a/sbt-plugin/src/sbt-test/linker/custom-linker/project/build.properties +++ b/sbt-plugin/src/sbt-test/linker/custom-linker/project/build.properties @@ -1 +1 @@ -sbt.version=1.2.8 +sbt.version=1.3.3 diff --git a/sbt-plugin/src/sbt-test/linker/no-root-dependency-resolution/project/build.properties b/sbt-plugin/src/sbt-test/linker/no-root-dependency-resolution/project/build.properties index c0bab04941..6adcdc753f 100644 --- a/sbt-plugin/src/sbt-test/linker/no-root-dependency-resolution/project/build.properties +++ b/sbt-plugin/src/sbt-test/linker/no-root-dependency-resolution/project/build.properties @@ -1 +1 @@ -sbt.version=1.2.8 +sbt.version=1.3.3 diff --git a/sbt-plugin/src/sbt-test/linker/non-existent-classpath/project/build.properties b/sbt-plugin/src/sbt-test/linker/non-existent-classpath/project/build.properties index c0bab04941..6adcdc753f 100644 --- a/sbt-plugin/src/sbt-test/linker/non-existent-classpath/project/build.properties +++ b/sbt-plugin/src/sbt-test/linker/non-existent-classpath/project/build.properties @@ -1 +1 @@ -sbt.version=1.2.8 +sbt.version=1.3.3 diff --git a/sbt-plugin/src/sbt-test/settings/cross-version/project/build.properties b/sbt-plugin/src/sbt-test/settings/cross-version/project/build.properties index c0bab04941..6adcdc753f 100644 --- a/sbt-plugin/src/sbt-test/settings/cross-version/project/build.properties +++ b/sbt-plugin/src/sbt-test/settings/cross-version/project/build.properties @@ -1 +1 @@ -sbt.version=1.2.8 +sbt.version=1.3.3 diff --git a/sbt-plugin/src/sbt-test/settings/env-vars/project/build.properties b/sbt-plugin/src/sbt-test/settings/env-vars/project/build.properties index c0bab04941..6adcdc753f 100644 --- a/sbt-plugin/src/sbt-test/settings/env-vars/project/build.properties +++ b/sbt-plugin/src/sbt-test/settings/env-vars/project/build.properties @@ -1 +1 @@ -sbt.version=1.2.8 +sbt.version=1.3.3 diff --git a/sbt-plugin/src/sbt-test/settings/legacy-link-empty/project/build.properties b/sbt-plugin/src/sbt-test/settings/legacy-link-empty/project/build.properties index c0bab04941..6adcdc753f 100644 --- a/sbt-plugin/src/sbt-test/settings/legacy-link-empty/project/build.properties +++ b/sbt-plugin/src/sbt-test/settings/legacy-link-empty/project/build.properties @@ -1 +1 @@ -sbt.version=1.2.8 +sbt.version=1.3.3 diff --git a/sbt-plugin/src/sbt-test/settings/legacy-link-tasks/project/build.properties b/sbt-plugin/src/sbt-test/settings/legacy-link-tasks/project/build.properties index c0bab04941..6adcdc753f 100644 --- a/sbt-plugin/src/sbt-test/settings/legacy-link-tasks/project/build.properties +++ b/sbt-plugin/src/sbt-test/settings/legacy-link-tasks/project/build.properties @@ -1 +1 @@ -sbt.version=1.2.8 +sbt.version=1.3.3 diff --git a/sbt-plugin/src/sbt-test/settings/module-init/project/build.properties b/sbt-plugin/src/sbt-test/settings/module-init/project/build.properties index c0bab04941..6adcdc753f 100644 --- a/sbt-plugin/src/sbt-test/settings/module-init/project/build.properties +++ b/sbt-plugin/src/sbt-test/settings/module-init/project/build.properties @@ -1 +1 @@ -sbt.version=1.2.8 +sbt.version=1.3.3 diff --git a/sbt-plugin/src/sbt-test/settings/source-map/project/build.properties b/sbt-plugin/src/sbt-test/settings/source-map/project/build.properties index c0bab04941..6adcdc753f 100644 --- a/sbt-plugin/src/sbt-test/settings/source-map/project/build.properties +++ b/sbt-plugin/src/sbt-test/settings/source-map/project/build.properties @@ -1 +1 @@ -sbt.version=1.2.8 +sbt.version=1.3.3 diff --git a/sbt-plugin/src/sbt-test/testing/multi-framework/project/build.properties b/sbt-plugin/src/sbt-test/testing/multi-framework/project/build.properties index c0bab04941..6adcdc753f 100644 --- a/sbt-plugin/src/sbt-test/testing/multi-framework/project/build.properties +++ b/sbt-plugin/src/sbt-test/testing/multi-framework/project/build.properties @@ -1 +1 @@ -sbt.version=1.2.8 +sbt.version=1.3.3 diff --git a/scala-test-suite/src/test/resources/2.11.12/BlacklistedTests.txt b/scala-test-suite/src/test/resources/2.11.12/BlacklistedTests.txt deleted file mode 100644 index 016de57f31..0000000000 --- a/scala-test-suite/src/test/resources/2.11.12/BlacklistedTests.txt +++ /dev/null @@ -1,90 +0,0 @@ -# Do not compile -scala/issues/BytecodeTests.scala -scala/reflect/QTest.scala -scala/reflect/io/ZipArchiveTest.scala -scala/reflect/internal/util/AbstractFileClassLoaderTest.scala -scala/reflect/internal/util/SourceFileTest.scala -scala/reflect/internal/util/StringOpsTest.scala -scala/reflect/internal/PrintersTest.scala -scala/reflect/internal/ScopeTest.scala -scala/reflect/internal/TypesTest.scala -scala/reflect/internal/util/WeakHashSetTest.scala -scala/reflect/internal/MirrorsTest.scala -scala/reflect/internal/NamesTest.scala -scala/tools/nsc/ScriptRunnerTest.scala -scala/tools/nsc/backend/jvm/BTypesTest.scala -scala/tools/nsc/backend/jvm/CodeGenTools.scala -scala/tools/nsc/backend/jvm/DirectCompileTest.scala -scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzerTest.scala -scala/tools/nsc/backend/jvm/analysis/ProdConsAnalyzerTest.scala -scala/tools/nsc/backend/jvm/opt/BTypesFromClassfileTest.scala -scala/tools/nsc/backend/jvm/opt/CallGraphTest.scala -scala/tools/nsc/backend/jvm/opt/CompactLocalVariablesTest.scala -scala/tools/nsc/backend/jvm/opt/EmptyExceptionHandlersTest.scala -scala/tools/nsc/backend/jvm/opt/EmptyLabelsAndLineNumbersTest.scala -scala/tools/nsc/backend/jvm/opt/InlineInfoTest.scala -scala/tools/nsc/backend/jvm/opt/InlinerIllegalAccessTest.scala -scala/tools/nsc/backend/jvm/opt/InlinerSeparateCompilationTest.scala -scala/tools/nsc/backend/jvm/opt/InlinerTest.scala -scala/tools/nsc/backend/jvm/opt/InlineWarningTest.scala -scala/tools/nsc/backend/jvm/opt/MethodLevelOpts.scala -scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala -scala/tools/nsc/backend/jvm/opt/SimplifyJumpsTest.scala -scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala -scala/tools/nsc/backend/jvm/opt/UnusedLocalVariablesTest.scala -scala/tools/nsc/classpath/AggregateFlatClassPathTest.scala -scala/tools/nsc/classpath/FlatClassPathResolverTest.scala -scala/tools/nsc/doc/html/HtmlDocletTest.scala -scala/tools/nsc/interpreter/CompletionTest.scala -scala/tools/nsc/interpreter/TabulatorTest.scala -scala/tools/nsc/settings/ScalaVersionTest.scala -scala/tools/nsc/settings/SettingsTest.scala -scala/tools/nsc/symtab/CannotHaveAttrsTest.scala -scala/tools/nsc/symtab/FlagsTest.scala -scala/tools/nsc/symtab/FreshNameExtractorTest.scala -scala/tools/nsc/symtab/StdNamesTest.scala -scala/tools/nsc/symtab/SymbolTableForUnitTesting.scala -scala/tools/nsc/symtab/SymbolTableTest.scala -scala/tools/nsc/transform/delambdafy/DelambdafyTest.scala -scala/tools/nsc/transform/patmat/SolvingTest.scala -scala/tools/nsc/util/ClassPathImplComparator.scala -scala/tools/nsc/util/StackTraceTest.scala -scala/util/SpecVersionTest.scala - -## Do not link -scala/StringContextTest.scala -scala/collection/IteratorTest.scala -scala/collection/ParallelConsistencyTest.scala -scala/collection/immutable/ListTest.scala -scala/collection/immutable/StringLikeTest.scala -scala/collection/mutable/ArrayBufferTest.scala -scala/collection/mutable/MutableListTest.scala -scala/collection/mutable/OpenHashMapTest.scala -scala/collection/mutable/PriorityQueueTest.scala -scala/concurrent/duration/SerializationTest.scala -scala/concurrent/impl/DefaultPromiseTest.scala -scala/io/SourceTest.scala -scala/runtime/ScalaRunTimeTest.scala -scala/tools/testing/AssertUtilTest.scala - -## Tests fail - -# Reflection -scala/reflect/ClassTag.scala -scala/tools/testing/AssertThrowsTest.scala - -# Require strict-floats -scala/math/BigDecimalTest.scala - -# Tests passed but are too slow (timeouts) -scala/collection/immutable/RangeConsistencyTest.scala -scala/collection/SetMapConsistencyTest.scala -scala/util/SortingTest.scala - -# Bugs -scala/collection/convert/MapWrapperTest.scala - -# Test fails only some times with -# 'set scalaJSOptimizerOptions in scalaTestSuite ~= (_.withDisableOptimizer(true))' -# and' 'set scalaJSUseRhino in Global := false' -scala/collection/immutable/PagedSeqTest.scala diff --git a/scalalib/overrides-2.11/scala/Console.scala b/scalalib/overrides-2.11/scala/Console.scala deleted file mode 100644 index b85f8dc3d0..0000000000 --- a/scalalib/overrides-2.11/scala/Console.scala +++ /dev/null @@ -1,222 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala - -import java.io.{ BufferedReader, InputStream, InputStreamReader, OutputStream, PrintStream, Reader } -import scala.io.{ AnsiColor, StdIn } -import scala.util.DynamicVariable - -/** Implements functionality for - * printing Scala values on the terminal as well as reading specific values. - * Also defines constants for marking up text on ANSI terminals. - * - * @author Matthias Zenger - * @version 1.0, 03/09/2003 - */ -object Console extends DeprecatedConsole with AnsiColor { - private val outVar = new DynamicVariable[PrintStream](java.lang.System.out) - private val errVar = new DynamicVariable[PrintStream](java.lang.System.err) - private val inVar = new DynamicVariable[BufferedReader](null) - //new BufferedReader(new InputStreamReader(java.lang.System.in))) - - protected def setOutDirect(out: PrintStream): Unit = outVar.value = out - protected def setErrDirect(err: PrintStream): Unit = errVar.value = err - protected def setInDirect(in: BufferedReader): Unit = inVar.value = in - - /** The default output, can be overridden by `setOut` */ - def out = outVar.value - /** The default error, can be overridden by `setErr` */ - def err = errVar.value - /** The default input, can be overridden by `setIn` */ - def in = inVar.value - - /** Sets the default output stream for the duration - * of execution of one thunk. - * - * @example {{{ - * withOut(Console.err) { println("This goes to default _error_") } - * }}} - * - * @param out the new output stream. - * @param thunk the code to execute with - * the new output stream active - * @return the results of `thunk` - * @see `withOut[T](out:OutputStream)(thunk: => T)` - */ - def withOut[T](out: PrintStream)(thunk: =>T): T = - outVar.withValue(out)(thunk) - - /** Sets the default output stream for the duration - * of execution of one thunk. - * - * @param out the new output stream. - * @param thunk the code to execute with - * the new output stream active - * @return the results of `thunk` - * @see `withOut[T](out:PrintStream)(thunk: => T)` - */ - def withOut[T](out: OutputStream)(thunk: =>T): T = - withOut(new PrintStream(out))(thunk) - - /** Set the default error stream for the duration - * of execution of one thunk. - * @example {{{ - * withErr(Console.out) { println("This goes to default _out_") } - * }}} - * - * @param err the new error stream. - * @param thunk the code to execute with - * the new error stream active - * @return the results of `thunk` - * @see `withErr[T](err:OutputStream)(thunk: =>T)` - */ - def withErr[T](err: PrintStream)(thunk: =>T): T = - errVar.withValue(err)(thunk) - - /** Sets the default error stream for the duration - * of execution of one thunk. - * - * @param err the new error stream. - * @param thunk the code to execute with - * the new error stream active - * @return the results of `thunk` - * @see `withErr[T](err:PrintStream)(thunk: =>T)` - */ - def withErr[T](err: OutputStream)(thunk: =>T): T = - withErr(new PrintStream(err))(thunk) - - /** Sets the default input stream for the duration - * of execution of one thunk. - * - * @example {{{ - * val someFile:Reader = openFile("file.txt") - * withIn(someFile) { - * // Reads a line from file.txt instead of default input - * println(readLine) - * } - * }}} - * - * @param thunk the code to execute with - * the new input stream active - * - * @return the results of `thunk` - * @see `withIn[T](in:InputStream)(thunk: =>T)` - */ - def withIn[T](reader: Reader)(thunk: =>T): T = - inVar.withValue(new BufferedReader(reader))(thunk) - - /** Sets the default input stream for the duration - * of execution of one thunk. - * - * @param in the new input stream. - * @param thunk the code to execute with - * the new input stream active - * @return the results of `thunk` - * @see `withIn[T](reader:Reader)(thunk: =>T)` - */ - def withIn[T](in: InputStream)(thunk: =>T): T = - withIn(new InputStreamReader(in))(thunk) - - /** Prints an object to `out` using its `toString` method. - * - * @param obj the object to print; may be null. - */ - def print(obj: Any) { - out.print(if (null == obj) "null" else obj.toString()) - } - - /** Flushes the output stream. This function is required when partial - * output (i.e. output not terminated by a newline character) has - * to be made visible on the terminal. - */ - def flush() { out.flush() } - - /** Prints a newline character on the default output. - */ - def println() { out.println() } - - /** Prints out an object to the default output, followed by a newline character. - * - * @param x the object to print. - */ - def println(x: Any) { out.println(x) } - - /** Prints its arguments as a formatted string to the default output, - * based on a string pattern (in a fashion similar to printf in C). - * - * The interpretation of the formatting patterns is described in - * - * `java.util.Formatter`. - * - * @param text the pattern for formatting the arguments. - * @param args the arguments used to instantiating the pattern. - * @throws java.lang.IllegalArgumentException if there was a problem with the format string or arguments - */ - def printf(text: String, args: Any*) { out.print(text format (args : _*)) } -} - -private[scala] abstract class DeprecatedConsole { - self: Console.type => - - /** Internal usage only. */ - protected def setOutDirect(out: PrintStream): Unit - protected def setErrDirect(err: PrintStream): Unit - protected def setInDirect(in: BufferedReader): Unit - - @deprecated("Use the method in scala.io.StdIn", "2.11.0") def readBoolean(): Boolean = StdIn.readBoolean() - @deprecated("Use the method in scala.io.StdIn", "2.11.0") def readByte(): Byte = StdIn.readByte() - @deprecated("Use the method in scala.io.StdIn", "2.11.0") def readChar(): Char = StdIn.readChar() - @deprecated("Use the method in scala.io.StdIn", "2.11.0") def readDouble(): Double = StdIn.readDouble() - @deprecated("Use the method in scala.io.StdIn", "2.11.0") def readFloat(): Float = StdIn.readFloat() - @deprecated("Use the method in scala.io.StdIn", "2.11.0") def readInt(): Int = StdIn.readInt() - @deprecated("Use the method in scala.io.StdIn", "2.11.0") def readLine(): String = StdIn.readLine() - @deprecated("Use the method in scala.io.StdIn", "2.11.0") def readLine(text: String, args: Any*): String = StdIn.readLine(text, args: _*) - @deprecated("Use the method in scala.io.StdIn", "2.11.0") def readLong(): Long = StdIn.readLong() - @deprecated("Use the method in scala.io.StdIn", "2.11.0") def readShort(): Short = StdIn.readShort() - @deprecated("Use the method in scala.io.StdIn", "2.11.0") def readf(format: String): List[Any] = StdIn.readf(format) - @deprecated("Use the method in scala.io.StdIn", "2.11.0") def readf1(format: String): Any = StdIn.readf1(format) - @deprecated("Use the method in scala.io.StdIn", "2.11.0") def readf2(format: String): (Any, Any) = StdIn.readf2(format) - @deprecated("Use the method in scala.io.StdIn", "2.11.0") def readf3(format: String): (Any, Any, Any) = StdIn.readf3(format) - - /** Sets the default output stream. - * - * @param out the new output stream. - */ - @deprecated("Use withOut", "2.11.0") def setOut(out: PrintStream): Unit = setOutDirect(out) - - /** Sets the default output stream. - * - * @param out the new output stream. - */ - @deprecated("Use withOut", "2.11.0") def setOut(out: OutputStream): Unit = setOutDirect(new PrintStream(out)) - - /** Sets the default error stream. - * - * @param err the new error stream. - */ - @deprecated("Use withErr", "2.11.0") def setErr(err: PrintStream): Unit = setErrDirect(err) - - /** Sets the default error stream. - * - * @param err the new error stream. - */ - @deprecated("Use withErr", "2.11.0") def setErr(err: OutputStream): Unit = setErrDirect(new PrintStream(err)) - - /** Sets the default input stream. - * - * @param reader specifies the new input stream. - */ - @deprecated("Use withIn", "2.11.0") def setIn(reader: Reader): Unit = setInDirect(new BufferedReader(reader)) - - /** Sets the default input stream. - * - * @param in the new input stream. - */ - @deprecated("Use withIn", "2.11.0") def setIn(in: InputStream): Unit = setInDirect(new BufferedReader(new InputStreamReader(in))) -} diff --git a/scalalib/overrides-2.11/scala/collection/immutable/NumericRange.scala b/scalalib/overrides-2.11/scala/collection/immutable/NumericRange.scala deleted file mode 100644 index c3fcbd9f46..0000000000 --- a/scalalib/overrides-2.11/scala/collection/immutable/NumericRange.scala +++ /dev/null @@ -1,367 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package collection -package immutable - -// TODO: Now the specialization exists there is no clear reason to have -// separate classes for Range/NumericRange. Investigate and consolidate. - -/** `NumericRange` is a more generic version of the - * `Range` class which works with arbitrary types. - * It must be supplied with an `Integral` implementation of the - * range type. - * - * Factories for likely types include `Range.BigInt`, `Range.Long`, - * and `Range.BigDecimal`. `Range.Int` exists for completeness, but - * the `Int`-based `scala.Range` should be more performant. - * - * {{{ - * val r1 = new Range(0, 100, 1) - * val veryBig = Int.MaxValue.toLong + 1 - * val r2 = Range.Long(veryBig, veryBig + 100, 1) - * assert(r1 sameElements r2.map(_ - veryBig)) - * }}} - * - * @author Paul Phillips - * @version 2.8 - * @define Coll `NumericRange` - * @define coll numeric range - * @define mayNotTerminateInf - * @define willNotTerminateInf - */ -abstract class NumericRange[T] - (val start: T, val end: T, val step: T, val isInclusive: Boolean) - (implicit num: Integral[T]) -extends AbstractSeq[T] with IndexedSeq[T] with Serializable { - /** Note that NumericRange must be invariant so that constructs - * such as "1L to 10 by 5" do not infer the range type as AnyVal. - */ - import num._ - - // See comment in Range for why this must be lazy. - private lazy val numRangeElements: Int = - NumericRange.count(start, end, step, isInclusive) - - override def length = numRangeElements - override def isEmpty = length == 0 - override lazy val last: T = - if (length == 0) Nil.last - else locationAfterN(length - 1) - - /** Create a new range with the start and end values of this range and - * a new `step`. - */ - def by(newStep: T): NumericRange[T] = copy(start, end, newStep) - - /** Create a copy of this range. - */ - def copy(start: T, end: T, step: T): NumericRange[T] - - override def foreach[U](f: T => U) { - var count = 0 - var current = start - while (count < length) { - f(current) - current += step - count += 1 - } - } - - // TODO: these private methods are straight copies from Range, duplicated - // to guard against any (most likely illusory) performance drop. They should - // be eliminated one way or another. - - // Tests whether a number is within the endpoints, without testing - // whether it is a member of the sequence (i.e. when step > 1.) - private def isWithinBoundaries(elem: T) = !isEmpty && ( - (step > zero && start <= elem && elem <= last ) || - (step < zero && last <= elem && elem <= start) - ) - // Methods like apply throw exceptions on invalid n, but methods like take/drop - // are forgiving: therefore the checks are with the methods. - private def locationAfterN(n: Int): T = start + (step * fromInt(n)) - - // When one drops everything. Can't ever have unchecked operations - // like "end + 1" or "end - 1" because ranges involving Int.{ MinValue, MaxValue } - // will overflow. This creates an exclusive range where start == end - // based on the given value. - private def newEmptyRange(value: T) = NumericRange(value, value, step) - - final override def take(n: Int): NumericRange[T] = ( - if (n <= 0 || length == 0) newEmptyRange(start) - else if (n >= length) this - else new NumericRange.Inclusive(start, locationAfterN(n - 1), step) - ) - - final override def drop(n: Int): NumericRange[T] = ( - if (n <= 0 || length == 0) this - else if (n >= length) newEmptyRange(end) - else copy(locationAfterN(n), end, step) - ) - - def apply(idx: Int): T = { - if (idx < 0 || idx >= length) throw new IndexOutOfBoundsException(idx.toString) - else locationAfterN(idx) - } - - import NumericRange.defaultOrdering - - override def min[T1 >: T](implicit ord: Ordering[T1]): T = - // We can take the fast path: - // - If the Integral of this NumericRange is also the requested Ordering - // (Integral <: Ordering). This can happen for custom Integral types. - // - The Ordering is the default Ordering of a well-known Integral type. - if ((ord eq num) || defaultOrdering.get(num).exists(ord eq _)) { - if (num.signum(step) > 0) start - else last - } else super.min(ord) - - override def max[T1 >: T](implicit ord: Ordering[T1]): T = - // See comment for fast path in min(). - if ((ord eq num) || defaultOrdering.get(num).exists(ord eq _)) { - if (num.signum(step) > 0) last - else start - } else super.max(ord) - - // Motivated by the desire for Double ranges with BigDecimal precision, - // we need some way to map a Range and get another Range. This can't be - // done in any fully general way because Ranges are not arbitrary - // sequences but step-valued, so we have a custom method only we can call - // which we promise to use responsibly. - // - // The point of it all is that - // - // 0.0 to 1.0 by 0.1 - // - // should result in - // - // NumericRange[Double](0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0) - // - // and not - // - // NumericRange[Double](0.0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7000000000000001, 0.8, 0.9) - // - // or perhaps more importantly, - // - // (0.1 to 0.3 by 0.1 contains 0.3) == true - // - private[immutable] def mapRange[A](fm: T => A)(implicit unum: Integral[A]): NumericRange[A] = { - val self = this - - // XXX This may be incomplete. - new NumericRange[A](fm(start), fm(end), fm(step), isInclusive) { - def copy(start: A, end: A, step: A): NumericRange[A] = - if (isInclusive) NumericRange.inclusive(start, end, step) - else NumericRange(start, end, step) - - private lazy val underlyingRange: NumericRange[T] = self - override def foreach[U](f: A => U) { underlyingRange foreach (x => f(fm(x))) } - override def isEmpty = underlyingRange.isEmpty - override def apply(idx: Int): A = fm(underlyingRange(idx)) - override def containsTyped(el: A) = underlyingRange exists (x => fm(x) == el) - } - } - - // a well-typed contains method. - def containsTyped(x: T): Boolean = - isWithinBoundaries(x) && (((x - start) % step) == zero) - - override def contains[A1 >: T](x: A1): Boolean = - try containsTyped(x.asInstanceOf[T]) - catch { case _: ClassCastException => false } - - final override def sum[B >: T](implicit num: Numeric[B]): B = { - if (isEmpty) num.zero - else if (numRangeElements == 1) head - else { - // If there is no overflow, use arithmetic series formula - // a + ... (n terms total) ... + b = n*(a+b)/2 - if ((num eq scala.math.Numeric.IntIsIntegral)|| - (num eq scala.math.Numeric.ShortIsIntegral)|| - (num eq scala.math.Numeric.ByteIsIntegral)|| - (num eq scala.math.Numeric.CharIsIntegral)) { - // We can do math with no overflow in a Long--easy - val exact = (numRangeElements * ((num toLong head) + (num toInt last))) / 2 - num fromInt exact.toInt - } - else if (num eq scala.math.Numeric.LongIsIntegral) { - // Uh-oh, might be overflow, so we have to divide before we overflow. - // Either numRangeElements or (head + last) must be even, so divide the even one before multiplying - val a = head.toLong - val b = last.toLong - val ans = - if ((numRangeElements & 1) == 0) (numRangeElements / 2) * (a + b) - else numRangeElements * { - // Sum is even, but we might overflow it, so divide in pieces and add back remainder - val ha = a/2 - val hb = b/2 - ha + hb + ((a - 2*ha) + (b - 2*hb)) / 2 - } - ans.asInstanceOf[B] - } - else { - // User provided custom Numeric, so we cannot rely on arithmetic series formula (e.g. won't work on something like Z_6) - if (isEmpty) num.zero - else { - var acc = num.zero - var i = head - var idx = 0 - while(idx < length) { - acc = num.plus(acc, i) - i = i + step - idx = idx + 1 - } - acc - } - } - } - } - - override lazy val hashCode = super.hashCode() - override def equals(other: Any) = other match { - case x: NumericRange[_] => - (x canEqual this) && (length == x.length) && ( - (length == 0) || // all empty sequences are equal - (start == x.start && last == x.last) // same length and same endpoints implies equality - ) - case _ => - super.equals(other) - } - - override def toString() = { - val endStr = if (length > Range.MAX_PRINT) ", ... )" else ")" - take(Range.MAX_PRINT).mkString("NumericRange(", ", ", endStr) - } -} - -/** A companion object for numeric ranges. - */ -object NumericRange { - - /** Calculates the number of elements in a range given start, end, step, and - * whether or not it is inclusive. Throws an exception if step == 0 or - * the number of elements exceeds the maximum Int. - */ - def count[T](start: T, end: T, step: T, isInclusive: Boolean)(implicit num: Integral[T]): Int = { - val zero = num.zero - val upward = num.lt(start, end) - val posStep = num.gt(step, zero) - - if (step == zero) throw new IllegalArgumentException("step cannot be 0.") - else if (start == end) if (isInclusive) 1 else 0 - else if (upward != posStep) 0 - else { - /* We have to be frightfully paranoid about running out of range. - * We also can't assume that the numbers will fit in a Long. - * We will assume that if a > 0, -a can be represented, and if - * a < 0, -a+1 can be represented. We also assume that if we - * can't fit in Int, we can represent 2*Int.MaxValue+3 (at least). - * And we assume that numbers wrap rather than cap when they overflow. - */ - // Check whether we can short-circuit by deferring to Int range. - val startint = num.toInt(start) - if (start == num.fromInt(startint)) { - val endint = num.toInt(end) - if (end == num.fromInt(endint)) { - val stepint = num.toInt(step) - if (step == num.fromInt(stepint)) { - return { - if (isInclusive) Range.inclusive(startint, endint, stepint).length - else Range (startint, endint, stepint).length - } - } - } - } - // If we reach this point, deferring to Int failed. - // Numbers may be big. - val one = num.one - val limit = num.fromInt(Int.MaxValue) - def check(t: T): T = - if (num.gt(t, limit)) throw new IllegalArgumentException("More than Int.MaxValue elements.") - else t - // If the range crosses zero, it might overflow when subtracted - val startside = num.signum(start) - val endside = num.signum(end) - num.toInt{ - if (startside*endside >= 0) { - // We're sure we can subtract these numbers. - // Note that we do not use .rem because of different conventions for Long and BigInt - val diff = num.minus(end, start) - val quotient = check(num.quot(diff, step)) - val remainder = num.minus(diff, num.times(quotient, step)) - if (!isInclusive && zero == remainder) quotient else check(num.plus(quotient, one)) - } - else { - // We might not even be able to subtract these numbers. - // Jump in three pieces: - // * start to -1 or 1, whichever is closer (waypointA) - // * one step, which will take us at least to 0 (ends at waypointB) - // * there to the end - val negone = num.fromInt(-1) - val startlim = if (posStep) negone else one - val startdiff = num.minus(startlim, start) - val startq = check(num.quot(startdiff, step)) - val waypointA = if (startq == zero) start else num.plus(start, num.times(startq, step)) - val waypointB = num.plus(waypointA, step) - check { - if (num.lt(waypointB, end) != upward) { - // No last piece - if (isInclusive && waypointB == end) num.plus(startq, num.fromInt(2)) - else num.plus(startq, one) - } - else { - // There is a last piece - val enddiff = num.minus(end,waypointB) - val endq = check(num.quot(enddiff, step)) - val last = if (endq == zero) waypointB else num.plus(waypointB, num.times(endq, step)) - // Now we have to tally up all the pieces - // 1 for the initial value - // startq steps to waypointA - // 1 step to waypointB - // endq steps to the end (one less if !isInclusive and last==end) - num.plus(startq, num.plus(endq, if (!isInclusive && last==end) one else num.fromInt(2))) - } - } - } - } - } - } - - class Inclusive[T](start: T, end: T, step: T)(implicit num: Integral[T]) - extends NumericRange(start, end, step, true) { - def copy(start: T, end: T, step: T): Inclusive[T] = - NumericRange.inclusive(start, end, step) - - def exclusive: Exclusive[T] = NumericRange(start, end, step) - } - - class Exclusive[T](start: T, end: T, step: T)(implicit num: Integral[T]) - extends NumericRange(start, end, step, false) { - def copy(start: T, end: T, step: T): Exclusive[T] = - NumericRange(start, end, step) - - def inclusive: Inclusive[T] = NumericRange.inclusive(start, end, step) - } - - def apply[T](start: T, end: T, step: T)(implicit num: Integral[T]): Exclusive[T] = - new Exclusive(start, end, step) - def inclusive[T](start: T, end: T, step: T)(implicit num: Integral[T]): Inclusive[T] = - new Inclusive(start, end, step) - - private[collection] val defaultOrdering = Map[Numeric[_], Ordering[_]]( - Numeric.IntIsIntegral -> Ordering.Int, - Numeric.ShortIsIntegral -> Ordering.Short, - Numeric.ByteIsIntegral -> Ordering.Byte, - Numeric.CharIsIntegral -> Ordering.Char, - Numeric.LongIsIntegral -> Ordering.Long - ) - -} - diff --git a/scalalib/overrides-2.11/scala/collection/immutable/Range.scala b/scalalib/overrides-2.11/scala/collection/immutable/Range.scala deleted file mode 100644 index e3bd573ab7..0000000000 --- a/scalalib/overrides-2.11/scala/collection/immutable/Range.scala +++ /dev/null @@ -1,519 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - - -package scala -package collection.immutable - -import scala.collection.parallel.immutable.ParRange - -/** The `Range` class represents integer values in range - * ''[start;end)'' with non-zero step value `step`. - * It's a special case of an indexed sequence. - * For example: - * - * {{{ - * val r1 = 0 until 10 - * val r2 = r1.start until r1.end by r1.step + 1 - * println(r2.length) // = 5 - * }}} - * - * Ranges that contain more than `Int.MaxValue` elements can be created, but - * these overfull ranges have only limited capabilities. Any method that - * could require a collection of over `Int.MaxValue` length to be created, or - * could be asked to index beyond `Int.MaxValue` elements will throw an - * exception. Overfull ranges can safely be reduced in size by changing - * the step size (e.g. `by 3`) or taking/dropping elements. `contains`, - * `equals`, and access to the ends of the range (`head`, `last`, `tail`, - * `init`) are also permitted on overfull ranges. - * - * @param start the start of this range. - * @param end the end of the range. For exclusive ranges, e.g. - * `Range(0,3)` or `(0 until 3)`, this is one - * step past the last one in the range. For inclusive - * ranges, e.g. `Range.inclusive(0,3)` or `(0 to 3)`, - * it may be in the range if it is not skipped by the step size. - * To find the last element inside a non-empty range, - use `last` instead. - * @param step the step for the range. - * - * @author Martin Odersky - * @author Paul Phillips - * @version 2.8 - * @since 2.5 - * @see [[http://docs.scala-lang.org/overviews/collections/concrete-immutable-collection-classes.html#ranges "Scala's Collection Library overview"]] - * section on `Ranges` for more information. - * - * @define coll range - * @define mayNotTerminateInf - * @define willNotTerminateInf - * @define doesNotUseBuilders - * '''Note:''' this method does not use builders to construct a new range, - * and its complexity is O(1). - */ -@SerialVersionUID(7618862778670199309L) -@inline -@deprecatedInheritance("The implementation details of Range makes inheriting from it unwise.", "2.11.0") -class Range(val start: Int, val end: Int, val step: Int) -extends scala.collection.AbstractSeq[Int] - with IndexedSeq[Int] - with scala.collection.CustomParallelizable[Int, ParRange] - with Serializable -{ - override def par = new ParRange(this) - - private def gap = end.toLong - start.toLong - private def isExact = gap % step == 0 - private def hasStub = isInclusive || !isExact - private def longLength = gap / step + ( if (hasStub) 1 else 0 ) - - // Check cannot be evaluated eagerly because we have a pattern where - // ranges are constructed like: "x to y by z" The "x to y" piece - // should not trigger an exception. So the calculation is delayed, - // which means it will not fail fast for those cases where failing was - // correct. - override final val isEmpty = ( - (start > end && step > 0) - || (start < end && step < 0) - || (start == end && !isInclusive) - ) - @deprecated("This method will be made private, use `length` instead.", "2.11") - final val numRangeElements: Int = { - if (step == 0) throw new IllegalArgumentException("step cannot be 0.") - else if (isEmpty) 0 - else { - val len = longLength - if (len > scala.Int.MaxValue) -1 - else len.toInt - } - } - @deprecated("This method will be made private, use `last` instead.", "2.11") - final val lastElement = - if (isEmpty) start - step - else step match { - case 1 => if (isInclusive) end else end-1 - case -1 => if (isInclusive) end else end+1 - case _ => - val remainder = (gap % step).toInt - if (remainder != 0) end - remainder - else if (isInclusive) end - else end - step - } - - @deprecated("This method will be made private.", "2.11") - final val terminalElement = lastElement + step - - /** The last element of this range. This method will return the correct value - * even if there are too many elements to iterate over. - */ - override def last = if (isEmpty) Nil.last else lastElement - override def head = if (isEmpty) Nil.head else start - - override def min[A1 >: Int](implicit ord: Ordering[A1]): Int = - if (ord eq Ordering.Int) { - if (step > 0) head - else last - } else super.min(ord) - - override def max[A1 >: Int](implicit ord: Ordering[A1]): Int = - if (ord eq Ordering.Int) { - if (step > 0) last - else head - } else super.max(ord) - - protected def copy(start: Int, end: Int, step: Int): Range = new Range(start, end, step) - - /** Create a new range with the `start` and `end` values of this range and - * a new `step`. - * - * @return a new range with a different step - */ - def by(step: Int): Range = copy(start, end, step) - - def isInclusive = false - - override def size = length - override def length = if (numRangeElements < 0) fail() else numRangeElements - - private def fail() = Range.fail(start, end, step, isInclusive) - private def validateMaxLength() { - if (numRangeElements < 0) - fail() - } - - final def apply(idx: Int): Int = { - validateMaxLength() - if (idx < 0 || idx >= numRangeElements) throw new IndexOutOfBoundsException(idx.toString) - else start + (step * idx) - } - - @inline final override def foreach[@specialized(Unit) U](f: Int => U) { - // Implementation chosen on the basis of favorable microbenchmarks - // Note--initialization catches step == 0 so we don't need to here - if (!isEmpty) { - var i = start - while (true) { - f(i) - if (i == lastElement) return - i += step - } - } - } - - /** Creates a new range containing the first `n` elements of this range. - * - * $doesNotUseBuilders - * - * @param n the number of elements to take. - * @return a new range consisting of `n` first elements. - */ - final override def take(n: Int): Range = ( - if (n <= 0 || isEmpty) newEmptyRange(start) - else if (n >= numRangeElements && numRangeElements >= 0) this - else { - // May have more than Int.MaxValue elements in range (numRangeElements < 0) - // but the logic is the same either way: take the first n - new Range.Inclusive(start, locationAfterN(n - 1), step) - } - ) - - /** Creates a new range containing all the elements of this range except the first `n` elements. - * - * $doesNotUseBuilders - * - * @param n the number of elements to drop. - * @return a new range consisting of all the elements of this range except `n` first elements. - */ - final override def drop(n: Int): Range = ( - if (n <= 0 || isEmpty) this - else if (n >= numRangeElements && numRangeElements >= 0) newEmptyRange(end) - else { - // May have more than Int.MaxValue elements (numRangeElements < 0) - // but the logic is the same either way: go forwards n steps, keep the rest - copy(locationAfterN(n), end, step) - } - ) - - /** Creates a new range containing all the elements of this range except the last one. - * - * $doesNotUseBuilders - * - * @return a new range consisting of all the elements of this range except the last one. - */ - final override def init: Range = { - if (isEmpty) - Nil.init - - dropRight(1) - } - - /** Creates a new range containing all the elements of this range except the first one. - * - * $doesNotUseBuilders - * - * @return a new range consisting of all the elements of this range except the first one. - */ - final override def tail: Range = { - if (isEmpty) - Nil.tail - - drop(1) - } - - // Advance from the start while we meet the given test - private def argTakeWhile(p: Int => Boolean): Long = { - if (isEmpty) start - else { - var current = start - val stop = last - while (current != stop && p(current)) current += step - if (current != stop || !p(current)) current - else current.toLong + step - } - } - // Methods like apply throw exceptions on invalid n, but methods like take/drop - // are forgiving: therefore the checks are with the methods. - private def locationAfterN(n: Int) = start + (step * n) - - // When one drops everything. Can't ever have unchecked operations - // like "end + 1" or "end - 1" because ranges involving Int.{ MinValue, MaxValue } - // will overflow. This creates an exclusive range where start == end - // based on the given value. - private def newEmptyRange(value: Int) = new Range(value, value, step) - - final override def takeWhile(p: Int => Boolean): Range = { - val stop = argTakeWhile(p) - if (stop==start) newEmptyRange(start) - else { - val x = (stop - step).toInt - if (x == last) this - else new Range.Inclusive(start, x, step) - } - } - final override def dropWhile(p: Int => Boolean): Range = { - val stop = argTakeWhile(p) - if (stop == start) this - else { - val x = (stop - step).toInt - if (x == last) newEmptyRange(last) - else new Range.Inclusive(x + step, last, step) - } - } - final override def span(p: Int => Boolean): (Range, Range) = { - val border = argTakeWhile(p) - if (border == start) (newEmptyRange(start), this) - else { - val x = (border - step).toInt - if (x == last) (this, newEmptyRange(last)) - else (new Range.Inclusive(start, x, step), new Range.Inclusive(x+step, last, step)) - } - } - - /** Creates a pair of new ranges, first consisting of elements before `n`, and the second - * of elements after `n`. - * - * $doesNotUseBuilders - */ - final override def splitAt(n: Int) = (take(n), drop(n)) - - /** Creates a new range consisting of the last `n` elements of the range. - * - * $doesNotUseBuilders - */ - final override def takeRight(n: Int): Range = { - if (n <= 0) newEmptyRange(start) - else if (numRangeElements >= 0) drop(numRangeElements - n) - else { - // Need to handle over-full range separately - val y = last - val x = y - step.toLong*(n-1) - if ((step > 0 && x < start) || (step < 0 && x > start)) this - else new Range.Inclusive(x.toInt, y, step) - } - } - - /** Creates a new range consisting of the initial `length - n` elements of the range. - * - * $doesNotUseBuilders - */ - final override def dropRight(n: Int): Range = { - if (n <= 0) this - else if (numRangeElements >= 0) take(numRangeElements - n) - else { - // Need to handle over-full range separately - val y = last - step.toInt*n - if ((step > 0 && y < start) || (step < 0 && y > start)) newEmptyRange(start) - else new Range.Inclusive(start, y.toInt, step) - } - } - - /** Returns the reverse of this range. - * - * $doesNotUseBuilders - */ - final override def reverse: Range = - if (isEmpty) this - else new Range.Inclusive(last, start, -step) - - /** Make range inclusive. - */ - def inclusive = - if (isInclusive) this - else new Range.Inclusive(start, end, step) - - final def contains(x: Int) = { - if (x==end && !isInclusive) false - else if (step > 0) { - if (x < start || x > end) false - else (step == 1) || (((x - start) % step) == 0) - } - else { - if (x < end || x > start) false - else (step == -1) || (((x - start) % step) == 0) - } - } - - final override def sum[B >: Int](implicit num: Numeric[B]): Int = { - if (num eq scala.math.Numeric.IntIsIntegral) { - // this is normal integer range with usual addition. arithmetic series formula can be used - if (isEmpty) 0 - else if (numRangeElements == 1) head - else ((numRangeElements * (head.toLong + last)) / 2).toInt - } else { - // user provided custom Numeric, we cannot rely on arithmetic series formula - if (isEmpty) num.toInt(num.zero) - else { - var acc = num.zero - var i = head - while (true) { - acc = num.plus(acc, i) - if (i == lastElement) return num.toInt(acc) - i = i + step - } - 0 // Never hit this--just to satisfy compiler since it doesn't know while(true) has type Nothing - } - } - } - - override def toIterable = this - - override def toSeq = this - - override def equals(other: Any) = other match { - case x: Range => - // Note: this must succeed for overfull ranges (length > Int.MaxValue) - (x canEqual this) && { - if (isEmpty) x.isEmpty // empty sequences are equal - else // this is non-empty... - x.nonEmpty && start == x.start && { // ...so other must contain something and have same start - val l0 = last - (l0 == x.last && ( // And same end - start == l0 || step == x.step // And either the same step, or not take any steps - )) - } - } - case _ => - super.equals(other) - } - /** Note: hashCode can't be overridden without breaking Seq's - * equals contract. - */ - - override def toString() = { - val endStr = - if (numRangeElements > Range.MAX_PRINT || (!isEmpty && numRangeElements < 0)) ", ... )" else ")" - take(Range.MAX_PRINT).mkString("Range(", ", ", endStr) - } -} - -/** A companion object for the `Range` class. - */ -object Range { - private[immutable] val MAX_PRINT = 512 // some arbitrary value - - private def description(start: Int, end: Int, step: Int, isInclusive: Boolean) = - start + (if (isInclusive) " to " else " until ") + end + " by " + step - - private def fail(start: Int, end: Int, step: Int, isInclusive: Boolean) = - throw new IllegalArgumentException(description(start, end, step, isInclusive) + - ": seqs cannot contain more than Int.MaxValue elements.") - - /** Counts the number of range elements. - * @pre step != 0 - * If the size of the range exceeds Int.MaxValue, the - * result will be negative. - */ - def count(start: Int, end: Int, step: Int, isInclusive: Boolean): Int = { - if (step == 0) - throw new IllegalArgumentException("step cannot be 0.") - - val isEmpty = ( - if (start == end) !isInclusive - else if (start < end) step < 0 - else step > 0 - ) - if (isEmpty) 0 - else { - // Counts with Longs so we can recognize too-large ranges. - val gap: Long = end.toLong - start.toLong - val jumps: Long = gap / step - // Whether the size of this range is one larger than the - // number of full-sized jumps. - val hasStub = isInclusive || (gap % step != 0) - val result: Long = jumps + ( if (hasStub) 1 else 0 ) - - if (result > scala.Int.MaxValue) -1 - else result.toInt - } - } - def count(start: Int, end: Int, step: Int): Int = - count(start, end, step, isInclusive = false) - - @inline - class Inclusive(start: Int, end: Int, step: Int) extends Range(start, end, step) { -// override def par = new ParRange(this) - override def isInclusive = true - override protected def copy(start: Int, end: Int, step: Int): Range = new Inclusive(start, end, step) - } - - /** Make a range from `start` until `end` (exclusive) with given step value. - * @note step != 0 - */ - def apply(start: Int, end: Int, step: Int): Range = new Range(start, end, step) - - /** Make a range from `start` until `end` (exclusive) with step value 1. - */ - def apply(start: Int, end: Int): Range = new Range(start, end, 1) - - /** Make an inclusive range from `start` to `end` with given step value. - * @note step != 0 - */ - def inclusive(start: Int, end: Int, step: Int): Range.Inclusive = new Inclusive(start, end, step) - - /** Make an inclusive range from `start` to `end` with step value 1. - */ - def inclusive(start: Int, end: Int): Range.Inclusive = new Inclusive(start, end, 1) - - // BigInt and Long are straightforward generic ranges. - object BigInt { - def apply(start: BigInt, end: BigInt, step: BigInt) = NumericRange(start, end, step) - def inclusive(start: BigInt, end: BigInt, step: BigInt) = NumericRange.inclusive(start, end, step) - } - - object Long { - def apply(start: Long, end: Long, step: Long) = NumericRange(start, end, step) - def inclusive(start: Long, end: Long, step: Long) = NumericRange.inclusive(start, end, step) - } - - // BigDecimal uses an alternative implementation of Numeric in which - // it pretends to be Integral[T] instead of Fractional[T]. See Numeric for - // details. The intention is for it to throw an exception anytime - // imprecision or surprises might result from anything, although this may - // not yet be fully implemented. - object BigDecimal { - implicit val bigDecAsIntegral = scala.math.Numeric.BigDecimalAsIfIntegral - - def apply(start: BigDecimal, end: BigDecimal, step: BigDecimal) = - NumericRange(start, end, step) - def inclusive(start: BigDecimal, end: BigDecimal, step: BigDecimal) = - NumericRange.inclusive(start, end, step) - } - - // Double works by using a BigDecimal under the hood for precise - // stepping, but mapping the sequence values back to doubles with - // .doubleValue. This constructs the BigDecimals by way of the - // String constructor (valueOf) instead of the Double one, which - // is necessary to keep 0.3d at 0.3 as opposed to - // 0.299999999999999988897769753748434595763683319091796875 or so. - object Double { - implicit val bigDecAsIntegral = scala.math.Numeric.BigDecimalAsIfIntegral - implicit val doubleAsIntegral = scala.math.Numeric.DoubleAsIfIntegral - def toBD(x: Double): BigDecimal = scala.math.BigDecimal valueOf x - - def apply(start: Double, end: Double, step: Double) = - BigDecimal(toBD(start), toBD(end), toBD(step)) mapRange (_.doubleValue) - - def inclusive(start: Double, end: Double, step: Double) = - BigDecimal.inclusive(toBD(start), toBD(end), toBD(step)) mapRange (_.doubleValue) - } - - // As there is no appealing default step size for not-really-integral ranges, - // we offer a partially constructed object. - class Partial[T, U](f: T => U) { - def by(x: T): U = f(x) - } - - // Illustrating genericity with Int Range, which should have the same behavior - // as the original Range class. However we leave the original Range - // indefinitely, for performance and because the compiler seems to bootstrap - // off it and won't do so with our parameterized version without modifications. - object Int { - def apply(start: Int, end: Int, step: Int) = NumericRange(start, end, step) - def inclusive(start: Int, end: Int, step: Int) = NumericRange.inclusive(start, end, step) - } -} diff --git a/scalalib/overrides-2.11/scala/collection/immutable/RedBlackTree.scala b/scalalib/overrides-2.11/scala/collection/immutable/RedBlackTree.scala deleted file mode 100644 index 0e678d703e..0000000000 --- a/scalalib/overrides-2.11/scala/collection/immutable/RedBlackTree.scala +++ /dev/null @@ -1,562 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2005-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - - - -package scala -package collection -package immutable - -import scala.annotation.tailrec -import scala.annotation.meta.getter - -/** An object containing the RedBlack tree implementation used by for `TreeMaps` and `TreeSets`. - * - * Implementation note: since efficiency is important for data structures this implementation - * uses `null` to represent empty trees. This also means pattern matching cannot - * easily be used. The API represented by the RedBlackTree object tries to hide these - * optimizations behind a reasonably clean API. - * - * @since 2.10 - */ -private[collection] -object RedBlackTree { - - def isEmpty(tree: Tree[_, _]): Boolean = tree eq null - - def contains[A: Ordering](tree: Tree[A, _], x: A): Boolean = lookup(tree, x) ne null - def get[A: Ordering, B](tree: Tree[A, B], x: A): Option[B] = lookup(tree, x) match { - case null => None - case tree => Some(tree.value) - } - - @tailrec - def lookup[A, B](tree: Tree[A, B], x: A)(implicit ordering: Ordering[A]): Tree[A, B] = if (tree eq null) null else { - val cmp = ordering.compare(x, tree.key) - if (cmp < 0) lookup(tree.left, x) - else if (cmp > 0) lookup(tree.right, x) - else tree - } - - def count(tree: Tree[_, _]) = if (tree eq null) 0 else tree.count - /** - * Count all the nodes with keys greater than or equal to the lower bound and less than the upper bound. - * The two bounds are optional. - */ - def countInRange[A](tree: Tree[A, _], from: Option[A], to:Option[A])(implicit ordering: Ordering[A]) : Int = - if (tree eq null) 0 else - (from, to) match { - // with no bounds use this node's count - case (None, None) => tree.count - // if node is less than the lower bound, try the tree on the right, it might be in range - case (Some(lb), _) if ordering.lt(tree.key, lb) => countInRange(tree.right, from, to) - // if node is greater than or equal to the upper bound, try the tree on the left, it might be in range - case (_, Some(ub)) if ordering.gteq(tree.key, ub) => countInRange(tree.left, from, to) - // node is in range so the tree on the left will all be less than the upper bound and the tree on the - // right will all be greater than or equal to the lower bound. So 1 for this node plus - // count the subtrees by stripping off the bounds that we don't need any more - case _ => 1 + countInRange(tree.left, from, None) + countInRange(tree.right, None, to) - - } - def update[A: Ordering, B, B1 >: B](tree: Tree[A, B], k: A, v: B1, overwrite: Boolean): Tree[A, B1] = blacken(upd(tree, k, v, overwrite)) - def delete[A: Ordering, B](tree: Tree[A, B], k: A): Tree[A, B] = blacken(del(tree, k)) - def rangeImpl[A: Ordering, B](tree: Tree[A, B], from: Option[A], until: Option[A]): Tree[A, B] = (from, until) match { - case (Some(from), Some(until)) => this.range(tree, from, until) - case (Some(from), None) => this.from(tree, from) - case (None, Some(until)) => this.until(tree, until) - case (None, None) => tree - } - def range[A: Ordering, B](tree: Tree[A, B], from: A, until: A): Tree[A, B] = blacken(doRange(tree, from, until)) - def from[A: Ordering, B](tree: Tree[A, B], from: A): Tree[A, B] = blacken(doFrom(tree, from)) - def to[A: Ordering, B](tree: Tree[A, B], to: A): Tree[A, B] = blacken(doTo(tree, to)) - def until[A: Ordering, B](tree: Tree[A, B], key: A): Tree[A, B] = blacken(doUntil(tree, key)) - - def drop[A: Ordering, B](tree: Tree[A, B], n: Int): Tree[A, B] = blacken(doDrop(tree, n)) - def take[A: Ordering, B](tree: Tree[A, B], n: Int): Tree[A, B] = blacken(doTake(tree, n)) - def slice[A: Ordering, B](tree: Tree[A, B], from: Int, until: Int): Tree[A, B] = blacken(doSlice(tree, from, until)) - - def smallest[A, B](tree: Tree[A, B]): Tree[A, B] = { - if (tree eq null) throw new NoSuchElementException("empty map") - var result = tree - while (result.left ne null) result = result.left - result - } - def greatest[A, B](tree: Tree[A, B]): Tree[A, B] = { - if (tree eq null) throw new NoSuchElementException("empty map") - var result = tree - while (result.right ne null) result = result.right - result - } - - - def foreach[A,B,U](tree:Tree[A,B], f:((A,B)) => U):Unit = if (tree ne null) _foreach(tree,f) - - private[this] def _foreach[A, B, U](tree: Tree[A, B], f: ((A, B)) => U) { - if (tree.left ne null) _foreach(tree.left, f) - f((tree.key, tree.value)) - if (tree.right ne null) _foreach(tree.right, f) - } - - def foreachKey[A, U](tree:Tree[A,_], f: A => U):Unit = if (tree ne null) _foreachKey(tree,f) - - private[this] def _foreachKey[A, U](tree: Tree[A, _], f: A => U) { - if (tree.left ne null) _foreachKey(tree.left, f) - f((tree.key)) - if (tree.right ne null) _foreachKey(tree.right, f) - } - - def iterator[A: Ordering, B](tree: Tree[A, B], start: Option[A] = None): Iterator[(A, B)] = new EntriesIterator(tree, start) - def keysIterator[A: Ordering](tree: Tree[A, _], start: Option[A] = None): Iterator[A] = new KeysIterator(tree, start) - def valuesIterator[A: Ordering, B](tree: Tree[A, B], start: Option[A] = None): Iterator[B] = new ValuesIterator(tree, start) - - @tailrec - def nth[A, B](tree: Tree[A, B], n: Int): Tree[A, B] = { - val count = this.count(tree.left) - if (n < count) nth(tree.left, n) - else if (n > count) nth(tree.right, n - count - 1) - else tree - } - - def isBlack(tree: Tree[_, _]) = (tree eq null) || isBlackTree(tree) - - private[this] def isRedTree(tree: Tree[_, _]) = tree.isInstanceOf[RedTree[_, _]] - private[this] def isBlackTree(tree: Tree[_, _]) = tree.isInstanceOf[BlackTree[_, _]] - - private[this] def blacken[A, B](t: Tree[A, B]): Tree[A, B] = if (t eq null) null else t.black - - private[this] def mkTree[A, B](isBlack: Boolean, k: A, v: B, l: Tree[A, B], r: Tree[A, B]) = - if (isBlack) BlackTree(k, v, l, r) else RedTree(k, v, l, r) - - private[this] def balanceLeft[A, B, B1 >: B](isBlack: Boolean, z: A, zv: B, l: Tree[A, B1], d: Tree[A, B1]): Tree[A, B1] = { - if (isRedTree(l) && isRedTree(l.left)) - RedTree(l.key, l.value, BlackTree(l.left.key, l.left.value, l.left.left, l.left.right), BlackTree(z, zv, l.right, d)) - else if (isRedTree(l) && isRedTree(l.right)) - RedTree(l.right.key, l.right.value, BlackTree(l.key, l.value, l.left, l.right.left), BlackTree(z, zv, l.right.right, d)) - else - mkTree(isBlack, z, zv, l, d) - } - private[this] def balanceRight[A, B, B1 >: B](isBlack: Boolean, x: A, xv: B, a: Tree[A, B1], r: Tree[A, B1]): Tree[A, B1] = { - if (isRedTree(r) && isRedTree(r.left)) - RedTree(r.left.key, r.left.value, BlackTree(x, xv, a, r.left.left), BlackTree(r.key, r.value, r.left.right, r.right)) - else if (isRedTree(r) && isRedTree(r.right)) - RedTree(r.key, r.value, BlackTree(x, xv, a, r.left), BlackTree(r.right.key, r.right.value, r.right.left, r.right.right)) - else - mkTree(isBlack, x, xv, a, r) - } - private[this] def upd[A, B, B1 >: B](tree: Tree[A, B], k: A, v: B1, overwrite: Boolean)(implicit ordering: Ordering[A]): Tree[A, B1] = if (tree eq null) { - RedTree(k, v, null, null) - } else { - val cmp = ordering.compare(k, tree.key) - if (cmp < 0) balanceLeft(isBlackTree(tree), tree.key, tree.value, upd(tree.left, k, v, overwrite), tree.right) - else if (cmp > 0) balanceRight(isBlackTree(tree), tree.key, tree.value, tree.left, upd(tree.right, k, v, overwrite)) - else if (overwrite || k != tree.key) mkTree(isBlackTree(tree), k, v, tree.left, tree.right) - else tree - } - private[this] def updNth[A, B, B1 >: B](tree: Tree[A, B], idx: Int, k: A, v: B1, overwrite: Boolean): Tree[A, B1] = if (tree eq null) { - RedTree(k, v, null, null) - } else { - val rank = count(tree.left) + 1 - if (idx < rank) balanceLeft(isBlackTree(tree), tree.key, tree.value, updNth(tree.left, idx, k, v, overwrite), tree.right) - else if (idx > rank) balanceRight(isBlackTree(tree), tree.key, tree.value, tree.left, updNth(tree.right, idx - rank, k, v, overwrite)) - else if (overwrite) mkTree(isBlackTree(tree), k, v, tree.left, tree.right) - else tree - } - - /* Based on Stefan Kahrs' Haskell version of Okasaki's Red&Black Trees - * Constructing Red-Black Trees, Ralf Hinze: http://www.cs.ox.ac.uk/ralf.hinze/publications/WAAAPL99b.ps.gz - * Red-Black Trees in a Functional Setting, Chris Okasaki: https://wiki.rice.edu/confluence/download/attachments/2761212/Okasaki-Red-Black.pdf */ - private[this] def del[A, B](tree: Tree[A, B], k: A)(implicit ordering: Ordering[A]): Tree[A, B] = if (tree eq null) null else { - def balance(x: A, xv: B, tl: Tree[A, B], tr: Tree[A, B]) = if (isRedTree(tl)) { - if (isRedTree(tr)) { - RedTree(x, xv, tl.black, tr.black) - } else if (isRedTree(tl.left)) { - RedTree(tl.key, tl.value, tl.left.black, BlackTree(x, xv, tl.right, tr)) - } else if (isRedTree(tl.right)) { - RedTree(tl.right.key, tl.right.value, BlackTree(tl.key, tl.value, tl.left, tl.right.left), BlackTree(x, xv, tl.right.right, tr)) - } else { - BlackTree(x, xv, tl, tr) - } - } else if (isRedTree(tr)) { - if (isRedTree(tr.right)) { - RedTree(tr.key, tr.value, BlackTree(x, xv, tl, tr.left), tr.right.black) - } else if (isRedTree(tr.left)) { - RedTree(tr.left.key, tr.left.value, BlackTree(x, xv, tl, tr.left.left), BlackTree(tr.key, tr.value, tr.left.right, tr.right)) - } else { - BlackTree(x, xv, tl, tr) - } - } else { - BlackTree(x, xv, tl, tr) - } - def subl(t: Tree[A, B]) = - if (t.isInstanceOf[BlackTree[_, _]]) t.red - else throw new IllegalStateException("Defect: invariance violation; expected black, got "+t) - - def balLeft(x: A, xv: B, tl: Tree[A, B], tr: Tree[A, B]) = if (isRedTree(tl)) { - RedTree(x, xv, tl.black, tr) - } else if (isBlackTree(tr)) { - balance(x, xv, tl, tr.red) - } else if (isRedTree(tr) && isBlackTree(tr.left)) { - RedTree(tr.left.key, tr.left.value, BlackTree(x, xv, tl, tr.left.left), balance(tr.key, tr.value, tr.left.right, subl(tr.right))) - } else { - throw new IllegalStateException("Defect: invariance violation") - } - def balRight(x: A, xv: B, tl: Tree[A, B], tr: Tree[A, B]) = if (isRedTree(tr)) { - RedTree(x, xv, tl, tr.black) - } else if (isBlackTree(tl)) { - balance(x, xv, tl.red, tr) - } else if (isRedTree(tl) && isBlackTree(tl.right)) { - RedTree(tl.right.key, tl.right.value, balance(tl.key, tl.value, subl(tl.left), tl.right.left), BlackTree(x, xv, tl.right.right, tr)) - } else { - throw new IllegalStateException("Defect: invariance violation") - } - def delLeft = if (isBlackTree(tree.left)) balLeft(tree.key, tree.value, del(tree.left, k), tree.right) else RedTree(tree.key, tree.value, del(tree.left, k), tree.right) - def delRight = if (isBlackTree(tree.right)) balRight(tree.key, tree.value, tree.left, del(tree.right, k)) else RedTree(tree.key, tree.value, tree.left, del(tree.right, k)) - def append(tl: Tree[A, B], tr: Tree[A, B]): Tree[A, B] = if (tl eq null) { - tr - } else if (tr eq null) { - tl - } else if (isRedTree(tl) && isRedTree(tr)) { - val bc = append(tl.right, tr.left) - if (isRedTree(bc)) { - RedTree(bc.key, bc.value, RedTree(tl.key, tl.value, tl.left, bc.left), RedTree(tr.key, tr.value, bc.right, tr.right)) - } else { - RedTree(tl.key, tl.value, tl.left, RedTree(tr.key, tr.value, bc, tr.right)) - } - } else if (isBlackTree(tl) && isBlackTree(tr)) { - val bc = append(tl.right, tr.left) - if (isRedTree(bc)) { - RedTree(bc.key, bc.value, BlackTree(tl.key, tl.value, tl.left, bc.left), BlackTree(tr.key, tr.value, bc.right, tr.right)) - } else { - balLeft(tl.key, tl.value, tl.left, BlackTree(tr.key, tr.value, bc, tr.right)) - } - } else if (isRedTree(tr)) { - RedTree(tr.key, tr.value, append(tl, tr.left), tr.right) - } else if (isRedTree(tl)) { - RedTree(tl.key, tl.value, tl.left, append(tl.right, tr)) - } else { - throw new IllegalStateException("unmatched tree on append: " + tl + ", " + tr) - } - - val cmp = ordering.compare(k, tree.key) - if (cmp < 0) delLeft - else if (cmp > 0) delRight - else append(tree.left, tree.right) - } - - private[this] def doFrom[A, B](tree: Tree[A, B], from: A)(implicit ordering: Ordering[A]): Tree[A, B] = { - if (tree eq null) return null - if (ordering.lt(tree.key, from)) return doFrom(tree.right, from) - val newLeft = doFrom(tree.left, from) - if (newLeft eq tree.left) tree - else if (newLeft eq null) upd(tree.right, tree.key, tree.value, overwrite = false) - else rebalance(tree, newLeft, tree.right) - } - private[this] def doTo[A, B](tree: Tree[A, B], to: A)(implicit ordering: Ordering[A]): Tree[A, B] = { - if (tree eq null) return null - if (ordering.lt(to, tree.key)) return doTo(tree.left, to) - val newRight = doTo(tree.right, to) - if (newRight eq tree.right) tree - else if (newRight eq null) upd(tree.left, tree.key, tree.value, overwrite = false) - else rebalance(tree, tree.left, newRight) - } - private[this] def doUntil[A, B](tree: Tree[A, B], until: A)(implicit ordering: Ordering[A]): Tree[A, B] = { - if (tree eq null) return null - if (ordering.lteq(until, tree.key)) return doUntil(tree.left, until) - val newRight = doUntil(tree.right, until) - if (newRight eq tree.right) tree - else if (newRight eq null) upd(tree.left, tree.key, tree.value, overwrite = false) - else rebalance(tree, tree.left, newRight) - } - private[this] def doRange[A, B](tree: Tree[A, B], from: A, until: A)(implicit ordering: Ordering[A]): Tree[A, B] = { - if (tree eq null) return null - if (ordering.lt(tree.key, from)) return doRange(tree.right, from, until) - if (ordering.lteq(until, tree.key)) return doRange(tree.left, from, until) - val newLeft = doFrom(tree.left, from) - val newRight = doUntil(tree.right, until) - if ((newLeft eq tree.left) && (newRight eq tree.right)) tree - else if (newLeft eq null) upd(newRight, tree.key, tree.value, overwrite = false) - else if (newRight eq null) upd(newLeft, tree.key, tree.value, overwrite = false) - else rebalance(tree, newLeft, newRight) - } - - private[this] def doDrop[A, B](tree: Tree[A, B], n: Int): Tree[A, B] = { - if (n <= 0) return tree - if (n >= this.count(tree)) return null - val count = this.count(tree.left) - if (n > count) return doDrop(tree.right, n - count - 1) - val newLeft = doDrop(tree.left, n) - if (newLeft eq tree.left) tree - else if (newLeft eq null) updNth(tree.right, n - count - 1, tree.key, tree.value, overwrite = false) - else rebalance(tree, newLeft, tree.right) - } - private[this] def doTake[A, B](tree: Tree[A, B], n: Int): Tree[A, B] = { - if (n <= 0) return null - if (n >= this.count(tree)) return tree - val count = this.count(tree.left) - if (n <= count) return doTake(tree.left, n) - val newRight = doTake(tree.right, n - count - 1) - if (newRight eq tree.right) tree - else if (newRight eq null) updNth(tree.left, n, tree.key, tree.value, overwrite = false) - else rebalance(tree, tree.left, newRight) - } - private[this] def doSlice[A, B](tree: Tree[A, B], from: Int, until: Int): Tree[A, B] = { - if (tree eq null) return null - val count = this.count(tree.left) - if (from > count) return doSlice(tree.right, from - count - 1, until - count - 1) - if (until <= count) return doSlice(tree.left, from, until) - val newLeft = doDrop(tree.left, from) - val newRight = doTake(tree.right, until - count - 1) - if ((newLeft eq tree.left) && (newRight eq tree.right)) tree - else if (newLeft eq null) updNth(newRight, from - count - 1, tree.key, tree.value, overwrite = false) - else if (newRight eq null) updNth(newLeft, until, tree.key, tree.value, overwrite = false) - else rebalance(tree, newLeft, newRight) - } - - // The zipper returned might have been traversed left-most (always the left child) - // or right-most (always the right child). Left trees are traversed right-most, - // and right trees are traversed leftmost. - - // Returns the zipper for the side with deepest black nodes depth, a flag - // indicating whether the trees were unbalanced at all, and a flag indicating - // whether the zipper was traversed left-most or right-most. - - // If the trees were balanced, returns an empty zipper - private[this] def compareDepth[A, B](left: Tree[A, B], right: Tree[A, B]): (NList[Tree[A, B]], Boolean, Boolean, Int) = { - import NList.cons - // Once a side is found to be deeper, unzip it to the bottom - def unzip(zipper: NList[Tree[A, B]], leftMost: Boolean): NList[Tree[A, B]] = { - val next = if (leftMost) zipper.head.left else zipper.head.right - if (next eq null) zipper - else unzip(cons(next, zipper), leftMost) - } - - // Unzip left tree on the rightmost side and right tree on the leftmost side until one is - // found to be deeper, or the bottom is reached - def unzipBoth(left: Tree[A, B], - right: Tree[A, B], - leftZipper: NList[Tree[A, B]], - rightZipper: NList[Tree[A, B]], - smallerDepth: Int): (NList[Tree[A, B]], Boolean, Boolean, Int) = { - if (isBlackTree(left) && isBlackTree(right)) { - unzipBoth(left.right, right.left, cons(left, leftZipper), cons(right, rightZipper), smallerDepth + 1) - } else if (isRedTree(left) && isRedTree(right)) { - unzipBoth(left.right, right.left, cons(left, leftZipper), cons(right, rightZipper), smallerDepth) - } else if (isRedTree(right)) { - unzipBoth(left, right.left, leftZipper, cons(right, rightZipper), smallerDepth) - } else if (isRedTree(left)) { - unzipBoth(left.right, right, cons(left, leftZipper), rightZipper, smallerDepth) - } else if ((left eq null) && (right eq null)) { - (null, true, false, smallerDepth) - } else if ((left eq null) && isBlackTree(right)) { - val leftMost = true - (unzip(cons(right, rightZipper), leftMost), false, leftMost, smallerDepth) - } else if (isBlackTree(left) && (right eq null)) { - val leftMost = false - (unzip(cons(left, leftZipper), leftMost), false, leftMost, smallerDepth) - } else { - throw new IllegalStateException("unmatched trees in unzip: " + left + ", " + right) - } - } - unzipBoth(left, right, null, null, 0) - } - - private[this] def rebalance[A, B](tree: Tree[A, B], newLeft: Tree[A, B], newRight: Tree[A, B]) = { - // This is like drop(n-1), but only counting black nodes - @tailrec - def findDepth(zipper: NList[Tree[A, B]], depth: Int): NList[Tree[A, B]] = - if (zipper eq null) { - throw new IllegalStateException("Defect: unexpected empty zipper while computing range") - } else if (isBlackTree(zipper.head)) { - if (depth == 1) zipper else findDepth(zipper.tail, depth - 1) - } else { - findDepth(zipper.tail, depth) - } - - // Blackening the smaller tree avoids balancing problems on union; - // this can't be done later, though, or it would change the result of compareDepth - val blkNewLeft = blacken(newLeft) - val blkNewRight = blacken(newRight) - val (zipper, levelled, leftMost, smallerDepth) = compareDepth(blkNewLeft, blkNewRight) - - if (levelled) { - BlackTree(tree.key, tree.value, blkNewLeft, blkNewRight) - } else { - val zipFrom = findDepth(zipper, smallerDepth) - val union = if (leftMost) { - RedTree(tree.key, tree.value, blkNewLeft, zipFrom.head) - } else { - RedTree(tree.key, tree.value, zipFrom.head, blkNewRight) - } - val zippedTree = NList.foldLeft(zipFrom.tail, union: Tree[A, B]) { (tree, node) => - if (leftMost) - balanceLeft(isBlackTree(node), node.key, node.value, tree, node.right) - else - balanceRight(isBlackTree(node), node.key, node.value, node.left, tree) - } - zippedTree - } - } - - // Null optimized list implementation for tree rebalancing. null presents Nil. - private[this] final class NList[A](val head: A, val tail: NList[A]) - - private[this] final object NList { - - def cons[B](x: B, xs: NList[B]): NList[B] = new NList(x, xs) - - def foldLeft[A, B](xs: NList[A], z: B)(op: (B, A) => B): B = { - var acc = z - var these = xs - while (these ne null) { - acc = op(acc, these.head) - these = these.tail - } - acc - } - - } - - /* - * Forcing direct fields access using the @inline annotation helps speed up - * various operations (especially smallest/greatest and update/delete). - * - * Unfortunately the direct field access is not guaranteed to work (but - * works on the current implementation of the Scala compiler). - * - * An alternative is to implement the these classes using plain old Java code... - */ - sealed abstract class Tree[A, +B]( - @(inline @getter) final val key: A, - @(inline @getter) final val value: B, - @(inline @getter) final val left: Tree[A, B], - @(inline @getter) final val right: Tree[A, B]) - extends Serializable { - @(inline @getter) final val count: Int = 1 + RedBlackTree.count(left) + RedBlackTree.count(right) - def black: Tree[A, B] - def red: Tree[A, B] - } - final class RedTree[A, +B](key: A, - value: B, - left: Tree[A, B], - right: Tree[A, B]) extends Tree[A, B](key, value, left, right) { - override def black: Tree[A, B] = BlackTree(key, value, left, right) - override def red: Tree[A, B] = this - override def toString: String = "RedTree(" + key + ", " + value + ", " + left + ", " + right + ")" - } - final class BlackTree[A, +B](key: A, - value: B, - left: Tree[A, B], - right: Tree[A, B]) extends Tree[A, B](key, value, left, right) { - override def black: Tree[A, B] = this - override def red: Tree[A, B] = RedTree(key, value, left, right) - override def toString: String = "BlackTree(" + key + ", " + value + ", " + left + ", " + right + ")" - } - - object RedTree { - @inline def apply[A, B](key: A, value: B, left: Tree[A, B], right: Tree[A, B]) = new RedTree(key, value, left, right) - def unapply[A, B](t: RedTree[A, B]) = Some((t.key, t.value, t.left, t.right)) - } - object BlackTree { - @inline def apply[A, B](key: A, value: B, left: Tree[A, B], right: Tree[A, B]) = new BlackTree(key, value, left, right) - def unapply[A, B](t: BlackTree[A, B]) = Some((t.key, t.value, t.left, t.right)) - } - - private[this] abstract class TreeIterator[A, B, R](root: Tree[A, B], start: Option[A])(implicit ordering: Ordering[A]) extends Iterator[R] { - protected[this] def nextResult(tree: Tree[A, B]): R - - override def hasNext: Boolean = lookahead ne null - - override def next: R = lookahead match { - case null => - throw new NoSuchElementException("next on empty iterator") - case tree => - lookahead = findLeftMostOrPopOnEmpty(goRight(tree)) - nextResult(tree) - } - - @tailrec - private[this] def findLeftMostOrPopOnEmpty(tree: Tree[A, B]): Tree[A, B] = - if (tree eq null) popNext() - else if (tree.left eq null) tree - else findLeftMostOrPopOnEmpty(goLeft(tree)) - - private[this] def pushNext(tree: Tree[A, B]) { - try { - stackOfNexts(index) = tree - index += 1 - } catch { - case _: ArrayIndexOutOfBoundsException => - /* - * Either the tree became unbalanced or we calculated the maximum height incorrectly. - * To avoid crashing the iterator we expand the path array. Obviously this should never - * happen... - * - * An exception handler is used instead of an if-condition to optimize the normal path. - * This makes a large difference in iteration speed! - */ - assert(index >= stackOfNexts.length) - stackOfNexts :+= null - pushNext(tree) - } - } - private[this] def popNext(): Tree[A, B] = if (index == 0) null else { - index -= 1 - stackOfNexts(index) - } - - private[this] var stackOfNexts = if (root eq null) null else { - /* - * According to "Ralf Hinze. Constructing red-black trees" [http://www.cs.ox.ac.uk/ralf.hinze/publications/#P5] - * the maximum height of a red-black tree is 2*log_2(n + 2) - 2. - * - * According to {@see Integer#numberOfLeadingZeros} ceil(log_2(n)) = (32 - Integer.numberOfLeadingZeros(n - 1)) - * - * We also don't store the deepest nodes in the path so the maximum path length is further reduced by one. - */ - val maximumHeight = 2 * (32 - Integer.numberOfLeadingZeros(root.count + 2 - 1)) - 2 - new Array[Tree[A, B]](maximumHeight) - } - private[this] var index = 0 - private[this] var lookahead: Tree[A, B] = start map startFrom getOrElse findLeftMostOrPopOnEmpty(root) - - /** - * Find the leftmost subtree whose key is equal to the given key, or if no such thing, - * the leftmost subtree with the key that would be "next" after it according - * to the ordering. Along the way build up the iterator's path stack so that "next" - * functionality works. - */ - private[this] def startFrom(key: A) : Tree[A,B] = if (root eq null) null else { - @tailrec def find(tree: Tree[A, B]): Tree[A, B] = - if (tree eq null) popNext() - else find( - if (ordering.lteq(key, tree.key)) goLeft(tree) - else goRight(tree) - ) - find(root) - } - - private[this] def goLeft(tree: Tree[A, B]) = { - pushNext(tree) - tree.left - } - - private[this] def goRight(tree: Tree[A, B]) = tree.right - } - - private[this] class EntriesIterator[A: Ordering, B](tree: Tree[A, B], focus: Option[A]) extends TreeIterator[A, B, (A, B)](tree, focus) { - override def nextResult(tree: Tree[A, B]) = (tree.key, tree.value) - } - - private[this] class KeysIterator[A: Ordering, B](tree: Tree[A, B], focus: Option[A]) extends TreeIterator[A, B, A](tree, focus) { - override def nextResult(tree: Tree[A, B]) = tree.key - } - - private[this] class ValuesIterator[A: Ordering, B](tree: Tree[A, B], focus: Option[A]) extends TreeIterator[A, B, B](tree, focus) { - override def nextResult(tree: Tree[A, B]) = tree.value - } -} diff --git a/scalalib/overrides-2.11/scala/collection/mutable/ArrayBuilder.scala b/scalalib/overrides-2.11/scala/collection/mutable/ArrayBuilder.scala deleted file mode 100644 index 31f67a3931..0000000000 --- a/scalalib/overrides-2.11/scala/collection/mutable/ArrayBuilder.scala +++ /dev/null @@ -1,795 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package collection -package mutable - -import scala.reflect.ClassTag -import scala.runtime.BoxedUnit - -import scala.scalajs.js - -/** A builder class for arrays. - * - * @since 2.8 - * - * @tparam T the type of the elements for the builder. - */ -abstract class ArrayBuilder[T] extends Builder[T, Array[T]] with Serializable - -/** A companion object for array builders. - * - * @since 2.8 - */ -object ArrayBuilder { - - /** Creates a new arraybuilder of type `T`. - * - * @tparam T type of the elements for the array builder, with a `ClassTag` context bound. - * @return a new empty array builder. - */ - @inline - def make[T: ClassTag](): ArrayBuilder[T] = - new ArrayBuilder.generic[T](implicitly[ClassTag[T]].runtimeClass) - - /** A generic ArrayBuilder optimized for Scala.js. - * - * @tparam T type of elements for the array builder. - * @param elementClass runtime class of the elements in the array. - */ - @inline - private final class generic[T](elementClass: Class[_]) extends ArrayBuilder[T] { - - private val isCharArrayBuilder = classOf[Char] == elementClass - private var elems: js.Array[Any] = js.Array() - - def +=(elem: T): this.type = { - val unboxedElem = - if (isCharArrayBuilder) elem.asInstanceOf[Char].toInt - else if (elem == null) zeroOf(elementClass) - else elem - elems.push(unboxedElem) - this - } - - def clear(): Unit = - elems = js.Array() - - def result(): Array[T] = { - val elemRuntimeClass = - if (classOf[Unit] == elementClass) classOf[BoxedUnit] - else if (classOf[Null] == elementClass || classOf[Nothing] == elementClass) classOf[Object] - else elementClass - genericArrayBuilderResult(elemRuntimeClass, elems) - } - - override def toString(): String = "ArrayBuilder.generic" - } - - // Intrinsic - private def zeroOf(runtimeClass: Class[_]): Any = runtimeClass match { - case java.lang.Byte.TYPE => 0.toByte - case java.lang.Short.TYPE => 0.toShort - case java.lang.Character.TYPE => 0 // yes, as an Int - case java.lang.Integer.TYPE => 0 - case java.lang.Long.TYPE => 0L - case java.lang.Float.TYPE => 0.0f - case java.lang.Double.TYPE => 0.0 - case java.lang.Boolean.TYPE => false - case java.lang.Void.TYPE => () - case _ => null - } - - // Intrinsic - private def genericArrayBuilderResult[T](runtimeClass: Class[_], - a: js.Array[Any]): Array[T] = { - val len = a.length - - if (classOf[Char] == runtimeClass) { - val result = new Array[Char](len) - var i = 0 - while (i != len) { - result(i) = a(i).asInstanceOf[Int].toChar - i += 1 - } - result.asInstanceOf[Array[T]] - } else { - val result: Array[T] = java.lang.reflect.Array.newInstance( - runtimeClass, len).asInstanceOf[Array[T]] - var i = 0 - while (i != len) { - result(i) = a(i).asInstanceOf[T] - i += 1 - } - result - } - } - - /** A class for array builders for arrays of reference types. - * - * @tparam T type of elements for the array builder, subtype of `AnyRef` with a `ClassTag` context bound. - */ - @deprecatedInheritance("ArrayBuilder.ofRef is an internal implementation not intended for subclassing.", "2.11.0") - class ofRef[T <: AnyRef : ClassTag] extends ArrayBuilder[T] { - - private var elems: Array[T] = _ - private var capacity: Int = 0 - private var size: Int = 0 - - private def mkArray(size: Int): Array[T] = { - val newelems = new Array[T](size) - if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) - newelems - } - - private def resize(size: Int) { - elems = mkArray(size) - capacity = size - } - - override def sizeHint(size: Int) { - if (capacity < size) resize(size) - } - - private def ensureSize(size: Int) { - if (capacity < size || capacity == 0) { - var newsize = if (capacity == 0) 16 else capacity * 2 - while (newsize < size) newsize *= 2 - resize(newsize) - } - } - - def +=(elem: T): this.type = { - ensureSize(size + 1) - elems(size) = elem - size += 1 - this - } - - override def ++=(xs: TraversableOnce[T]): this.type = (xs.asInstanceOf[AnyRef]) match { - case xs: WrappedArray.ofRef[_] => - ensureSize(this.size + xs.length) - Array.copy(xs.array, 0, elems, this.size, xs.length) - size += xs.length - this - case _ => - super.++=(xs) - } - - def clear() { - size = 0 - } - - def result() = { - if (capacity != 0 && capacity == size) { - capacity = 0 - elems - } - else mkArray(size) - } - - override def equals(other: Any): Boolean = other match { - case x: ofRef[_] => (size == x.size) && (elems == x.elems) - case _ => false - } - - override def toString = "ArrayBuilder.ofRef" - } - - /** A class for array builders for arrays of `byte`s. */ - @deprecatedInheritance("ArrayBuilder.ofByte is an internal implementation not intended for subclassing.", "2.11.0") - class ofByte extends ArrayBuilder[Byte] { - - private var elems: Array[Byte] = _ - private var capacity: Int = 0 - private var size: Int = 0 - - private def mkArray(size: Int): Array[Byte] = { - val newelems = new Array[Byte](size) - if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) - newelems - } - - private def resize(size: Int) { - elems = mkArray(size) - capacity = size - } - - override def sizeHint(size: Int) { - if (capacity < size) resize(size) - } - - private def ensureSize(size: Int) { - if (capacity < size || capacity == 0) { - var newsize = if (capacity == 0) 16 else capacity * 2 - while (newsize < size) newsize *= 2 - resize(newsize) - } - } - - def +=(elem: Byte): this.type = { - ensureSize(size + 1) - elems(size) = elem - size += 1 - this - } - - override def ++=(xs: TraversableOnce[Byte]): this.type = xs match { - case xs: WrappedArray.ofByte => - ensureSize(this.size + xs.length) - Array.copy(xs.array, 0, elems, this.size, xs.length) - size += xs.length - this - case _ => - super.++=(xs) - } - - def clear() { - size = 0 - } - - def result() = { - if (capacity != 0 && capacity == size) { - capacity = 0 - elems - } - else mkArray(size) - } - - override def equals(other: Any): Boolean = other match { - case x: ofByte => (size == x.size) && (elems == x.elems) - case _ => false - } - - override def toString = "ArrayBuilder.ofByte" - } - - /** A class for array builders for arrays of `short`s. */ - @deprecatedInheritance("ArrayBuilder.ofShort is an internal implementation not intended for subclassing.", "2.11.0") - class ofShort extends ArrayBuilder[Short] { - - private var elems: Array[Short] = _ - private var capacity: Int = 0 - private var size: Int = 0 - - private def mkArray(size: Int): Array[Short] = { - val newelems = new Array[Short](size) - if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) - newelems - } - - private def resize(size: Int) { - elems = mkArray(size) - capacity = size - } - - override def sizeHint(size: Int) { - if (capacity < size) resize(size) - } - - private def ensureSize(size: Int) { - if (capacity < size || capacity == 0) { - var newsize = if (capacity == 0) 16 else capacity * 2 - while (newsize < size) newsize *= 2 - resize(newsize) - } - } - - def +=(elem: Short): this.type = { - ensureSize(size + 1) - elems(size) = elem - size += 1 - this - } - - override def ++=(xs: TraversableOnce[Short]): this.type = xs match { - case xs: WrappedArray.ofShort => - ensureSize(this.size + xs.length) - Array.copy(xs.array, 0, elems, this.size, xs.length) - size += xs.length - this - case _ => - super.++=(xs) - } - - def clear() { - size = 0 - } - - def result() = { - if (capacity != 0 && capacity == size) { - capacity = 0 - elems - } - else mkArray(size) - } - - override def equals(other: Any): Boolean = other match { - case x: ofShort => (size == x.size) && (elems == x.elems) - case _ => false - } - - override def toString = "ArrayBuilder.ofShort" - } - - /** A class for array builders for arrays of `char`s. */ - @deprecatedInheritance("ArrayBuilder.ofChar is an internal implementation not intended for subclassing.", "2.11.0") - class ofChar extends ArrayBuilder[Char] { - - private var elems: Array[Char] = _ - private var capacity: Int = 0 - private var size: Int = 0 - - private def mkArray(size: Int): Array[Char] = { - val newelems = new Array[Char](size) - if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) - newelems - } - - private def resize(size: Int) { - elems = mkArray(size) - capacity = size - } - - override def sizeHint(size: Int) { - if (capacity < size) resize(size) - } - - private def ensureSize(size: Int) { - if (capacity < size || capacity == 0) { - var newsize = if (capacity == 0) 16 else capacity * 2 - while (newsize < size) newsize *= 2 - resize(newsize) - } - } - - def +=(elem: Char): this.type = { - ensureSize(size + 1) - elems(size) = elem - size += 1 - this - } - - override def ++=(xs: TraversableOnce[Char]): this.type = xs match { - case xs: WrappedArray.ofChar => - ensureSize(this.size + xs.length) - Array.copy(xs.array, 0, elems, this.size, xs.length) - size += xs.length - this - case _ => - super.++=(xs) - } - - def clear() { - size = 0 - } - - def result() = { - if (capacity != 0 && capacity == size) { - capacity = 0 - elems - } - else mkArray(size) - } - - override def equals(other: Any): Boolean = other match { - case x: ofChar => (size == x.size) && (elems == x.elems) - case _ => false - } - - override def toString = "ArrayBuilder.ofChar" - } - - /** A class for array builders for arrays of `int`s. */ - @deprecatedInheritance("ArrayBuilder.ofInt is an internal implementation not intended for subclassing.", "2.11.0") - class ofInt extends ArrayBuilder[Int] { - - private var elems: Array[Int] = _ - private var capacity: Int = 0 - private var size: Int = 0 - - private def mkArray(size: Int): Array[Int] = { - val newelems = new Array[Int](size) - if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) - newelems - } - - private def resize(size: Int) { - elems = mkArray(size) - capacity = size - } - - override def sizeHint(size: Int) { - if (capacity < size) resize(size) - } - - private def ensureSize(size: Int) { - if (capacity < size || capacity == 0) { - var newsize = if (capacity == 0) 16 else capacity * 2 - while (newsize < size) newsize *= 2 - resize(newsize) - } - } - - def +=(elem: Int): this.type = { - ensureSize(size + 1) - elems(size) = elem - size += 1 - this - } - - override def ++=(xs: TraversableOnce[Int]): this.type = xs match { - case xs: WrappedArray.ofInt => - ensureSize(this.size + xs.length) - Array.copy(xs.array, 0, elems, this.size, xs.length) - size += xs.length - this - case _ => - super.++=(xs) - } - - def clear() { - size = 0 - } - - def result() = { - if (capacity != 0 && capacity == size) { - capacity = 0 - elems - } - else mkArray(size) - } - - override def equals(other: Any): Boolean = other match { - case x: ofInt => (size == x.size) && (elems == x.elems) - case _ => false - } - - override def toString = "ArrayBuilder.ofInt" - } - - /** A class for array builders for arrays of `long`s. */ - @deprecatedInheritance("ArrayBuilder.ofLong is an internal implementation not intended for subclassing.", "2.11.0") - class ofLong extends ArrayBuilder[Long] { - - private var elems: Array[Long] = _ - private var capacity: Int = 0 - private var size: Int = 0 - - private def mkArray(size: Int): Array[Long] = { - val newelems = new Array[Long](size) - if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) - newelems - } - - private def resize(size: Int) { - elems = mkArray(size) - capacity = size - } - - override def sizeHint(size: Int) { - if (capacity < size) resize(size) - } - - private def ensureSize(size: Int) { - if (capacity < size || capacity == 0) { - var newsize = if (capacity == 0) 16 else capacity * 2 - while (newsize < size) newsize *= 2 - resize(newsize) - } - } - - def +=(elem: Long): this.type = { - ensureSize(size + 1) - elems(size) = elem - size += 1 - this - } - - override def ++=(xs: TraversableOnce[Long]): this.type = xs match { - case xs: WrappedArray.ofLong => - ensureSize(this.size + xs.length) - Array.copy(xs.array, 0, elems, this.size, xs.length) - size += xs.length - this - case _ => - super.++=(xs) - } - - def clear() { - size = 0 - } - - def result() = { - if (capacity != 0 && capacity == size) { - capacity = 0 - elems - } - else mkArray(size) - } - - override def equals(other: Any): Boolean = other match { - case x: ofLong => (size == x.size) && (elems == x.elems) - case _ => false - } - - override def toString = "ArrayBuilder.ofLong" - } - - /** A class for array builders for arrays of `float`s. */ - @deprecatedInheritance("ArrayBuilder.ofFloat is an internal implementation not intended for subclassing.", "2.11.0") - class ofFloat extends ArrayBuilder[Float] { - - private var elems: Array[Float] = _ - private var capacity: Int = 0 - private var size: Int = 0 - - private def mkArray(size: Int): Array[Float] = { - val newelems = new Array[Float](size) - if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) - newelems - } - - private def resize(size: Int) { - elems = mkArray(size) - capacity = size - } - - override def sizeHint(size: Int) { - if (capacity < size) resize(size) - } - - private def ensureSize(size: Int) { - if (capacity < size || capacity == 0) { - var newsize = if (capacity == 0) 16 else capacity * 2 - while (newsize < size) newsize *= 2 - resize(newsize) - } - } - - def +=(elem: Float): this.type = { - ensureSize(size + 1) - elems(size) = elem - size += 1 - this - } - - override def ++=(xs: TraversableOnce[Float]): this.type = xs match { - case xs: WrappedArray.ofFloat => - ensureSize(this.size + xs.length) - Array.copy(xs.array, 0, elems, this.size, xs.length) - size += xs.length - this - case _ => - super.++=(xs) - } - - def clear() { - size = 0 - } - - def result() = { - if (capacity != 0 && capacity == size) { - capacity = 0 - elems - } - else mkArray(size) - } - - override def equals(other: Any): Boolean = other match { - case x: ofFloat => (size == x.size) && (elems == x.elems) - case _ => false - } - - override def toString = "ArrayBuilder.ofFloat" - } - - /** A class for array builders for arrays of `double`s. */ - @deprecatedInheritance("ArrayBuilder.ofDouble is an internal implementation not intended for subclassing.", "2.11.0") - class ofDouble extends ArrayBuilder[Double] { - - private var elems: Array[Double] = _ - private var capacity: Int = 0 - private var size: Int = 0 - - private def mkArray(size: Int): Array[Double] = { - val newelems = new Array[Double](size) - if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) - newelems - } - - private def resize(size: Int) { - elems = mkArray(size) - capacity = size - } - - override def sizeHint(size: Int) { - if (capacity < size) resize(size) - } - - private def ensureSize(size: Int) { - if (capacity < size || capacity == 0) { - var newsize = if (capacity == 0) 16 else capacity * 2 - while (newsize < size) newsize *= 2 - resize(newsize) - } - } - - def +=(elem: Double): this.type = { - ensureSize(size + 1) - elems(size) = elem - size += 1 - this - } - - override def ++=(xs: TraversableOnce[Double]): this.type = xs match { - case xs: WrappedArray.ofDouble => - ensureSize(this.size + xs.length) - Array.copy(xs.array, 0, elems, this.size, xs.length) - size += xs.length - this - case _ => - super.++=(xs) - } - - def clear() { - size = 0 - } - - def result() = { - if (capacity != 0 && capacity == size) { - capacity = 0 - elems - } - else mkArray(size) - } - - override def equals(other: Any): Boolean = other match { - case x: ofDouble => (size == x.size) && (elems == x.elems) - case _ => false - } - - override def toString = "ArrayBuilder.ofDouble" - } - - /** A class for array builders for arrays of `boolean`s. */ - class ofBoolean extends ArrayBuilder[Boolean] { - - private var elems: Array[Boolean] = _ - private var capacity: Int = 0 - private var size: Int = 0 - - private def mkArray(size: Int): Array[Boolean] = { - val newelems = new Array[Boolean](size) - if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) - newelems - } - - private def resize(size: Int) { - elems = mkArray(size) - capacity = size - } - - override def sizeHint(size: Int) { - if (capacity < size) resize(size) - } - - private def ensureSize(size: Int) { - if (capacity < size || capacity == 0) { - var newsize = if (capacity == 0) 16 else capacity * 2 - while (newsize < size) newsize *= 2 - resize(newsize) - } - } - - def +=(elem: Boolean): this.type = { - ensureSize(size + 1) - elems(size) = elem - size += 1 - this - } - - override def ++=(xs: TraversableOnce[Boolean]): this.type = xs match { - case xs: WrappedArray.ofBoolean => - ensureSize(this.size + xs.length) - Array.copy(xs.array, 0, elems, this.size, xs.length) - size += xs.length - this - case _ => - super.++=(xs) - } - - def clear() { - size = 0 - } - - def result() = { - if (capacity != 0 && capacity == size) { - capacity = 0 - elems - } - else mkArray(size) - } - - override def equals(other: Any): Boolean = other match { - case x: ofBoolean => (size == x.size) && (elems == x.elems) - case _ => false - } - - override def toString = "ArrayBuilder.ofBoolean" - } - - /** A class for array builders for arrays of `Unit` type. */ - @deprecatedInheritance("ArrayBuilder.ofUnit is an internal implementation not intended for subclassing.", "2.11.0") - class ofUnit extends ArrayBuilder[Unit] { - - private var elems: Array[Unit] = _ - private var capacity: Int = 0 - private var size: Int = 0 - - private def mkArray(size: Int): Array[Unit] = { - val newelems = new Array[Unit](size) - if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) - newelems - } - - private def resize(size: Int) { - elems = mkArray(size) - capacity = size - } - - override def sizeHint(size: Int) { - if (capacity < size) resize(size) - } - - private def ensureSize(size: Int) { - if (capacity < size || capacity == 0) { - var newsize = if (capacity == 0) 16 else capacity * 2 - while (newsize < size) newsize *= 2 - resize(newsize) - } - } - - def +=(elem: Unit): this.type = { - ensureSize(size + 1) - elems(size) = elem - size += 1 - this - } - - override def ++=(xs: TraversableOnce[Unit]): this.type = xs match { - case xs: WrappedArray.ofUnit => - ensureSize(this.size + xs.length) - Array.copy(xs.array, 0, elems, this.size, xs.length) - size += xs.length - this - case _ => - super.++=(xs) - } - - def clear() { - size = 0 - } - - def result() = { - if (capacity != 0 && capacity == size) { - capacity = 0 - elems - } - else mkArray(size) - } - - override def equals(other: Any): Boolean = other match { - case x: ofUnit => (size == x.size) && (elems == x.elems) - case _ => false - } - - override def toString = "ArrayBuilder.ofUnit" - } -} diff --git a/scalalib/overrides-2.11/scala/collection/mutable/Buffer.scala b/scalalib/overrides-2.11/scala/collection/mutable/Buffer.scala deleted file mode 100644 index 2171cb9dea..0000000000 --- a/scalalib/overrides-2.11/scala/collection/mutable/Buffer.scala +++ /dev/null @@ -1,51 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - - - -package scala -package collection -package mutable - -import generic._ - -import scala.scalajs.js - -/** Buffers are used to create sequences of elements incrementally by - * appending, prepending, or inserting new elements. It is also - * possible to access and modify elements in a random access fashion - * via the index of the element in the current sequence. - * - * @author Matthias Zenger - * @author Martin Odersky - * @version 2.8 - * @since 1 - * - * @tparam A type of the elements contained in this buffer. - * - * @define Coll `Buffer` - * @define coll buffer - */ -trait Buffer[A] extends Seq[A] - with GenericTraversableTemplate[A, Buffer] - with BufferLike[A, Buffer[A]] - with scala.Cloneable { - override def companion: GenericCompanion[Buffer] = Buffer -} - -/** $factoryInfo - * @define coll buffer - * @define Coll `Buffer` - */ -object Buffer extends SeqFactory[Buffer] { - implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, Buffer[A]] = ReusableCBF.asInstanceOf[GenericCanBuildFrom[A]] - def newBuilder[A]: Builder[A, Buffer[A]] = new js.WrappedArray -} - -/** Explicit instantiation of the `Buffer` trait to reduce class file size in subclasses. */ -abstract class AbstractBuffer[A] extends AbstractSeq[A] with Buffer[A] diff --git a/scalalib/overrides-2.11/scala/compat/Platform.scala b/scalalib/overrides-2.11/scala/compat/Platform.scala deleted file mode 100644 index cdb69167ad..0000000000 --- a/scalalib/overrides-2.11/scala/compat/Platform.scala +++ /dev/null @@ -1,132 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package compat - -import java.lang.System - -object Platform { - - /** Thrown when a stack overflow occurs because a method or function recurses too deeply. - * - * On the JVM, this is a type alias for `java.lang.StackOverflowError`, which itself extends `java.lang.Error`. - * The same rules apply to catching a `java.lang.Error` as for Java, that it indicates a serious problem that a reasonable application should not try and catch. - */ - type StackOverflowError = java.lang.StackOverflowError - - /** This is a type alias for `java.util.ConcurrentModificationException`, - * which may be thrown by methods that detect an invalid modification of an object. - * For example, many common collection types do not allow modifying a collection - * while it is being iterated over. - */ - type ConcurrentModificationException = java.util.ConcurrentModificationException - - /** Copies `length` elements of array `src` starting at position `srcPos` to the - * array `dest` starting at position `destPos`. If `src`==`dest`, the copying will - * behave as if the elements copied from `src` were first copied to a temporary - * array before being copied back into the array at the destination positions. - * - * @param src A non-null array as source for the copy. - * @param srcPos The starting index in the source array. - * @param dest A non-null array as destination for the copy. - * @param destPos The starting index in the destination array. - * @param length The number of elements to be copied. - * @throws java.lang.NullPointerException If either `src` or `dest` are `null`. - * @throws java.lang.ArrayStoreException If either `src` or `dest` are not of type - * [java.lang.Array]; or if the element type of `src` is not - * compatible with that of `dest`. - * @throws java.lang.IndexOutOfBoundsException If either srcPos` or `destPos` are - * outside of the bounds of their respective arrays; or if `length` - * is negative; or if there are less than `length` elements available - * after `srcPos` or `destPos` in `src` and `dest` respectively. - */ - @inline - def arraycopy(src: AnyRef, srcPos: Int, dest: AnyRef, destPos: Int, length: Int) { - System.arraycopy(src, srcPos, dest, destPos, length) - } - - /** Creates a new array of the specified type and given length. - * - * Note that if `elemClass` is a subclass of [[scala.AnyVal]] then the returned value is an Array of the corresponding java primitive type. - * For example, the following code `scala.compat.Platform.createArray(classOf[Int], 4)` returns an array of the java primitive type `int`. - * - * For a [[scala.AnyVal]] array, the values of the array are set to 0 for ''numeric value types'' ([[scala.Double]], [[scala.Float]], [[scala.Long]], [[scala.Int]], [[scala.Char]], - * [[scala.Short]], and [[scala.Byte]]), and `false` for [[scala.Boolean]]. Creation of an array of type [[scala.Unit]] is not possible. - * - * For subclasses of [[scala.AnyRef]], the values of the array are set to `null`. - * - * The caller must cast the returned value to the correct type. - * - * @example {{{ - * val a = scala.compat.Platform.createArray(classOf[Int], 4).asInstanceOf[Array[Int]] // returns Array[Int](0, 0, 0, 0) - * }}} - * - * @param elemClass the `Class` object of the component type of the array - * @param length the length of the new array. - * @return an array of the given component type as an `AnyRef`. - * @throws `java.lang.NullPointerException` If `elemClass` is `null`. - * @throws `java.lang.IllegalArgumentException` if componentType is [[scala.Unit]] or `java.lang.Void.TYPE` - * @throws `java.lang.NegativeArraySizeException` if the specified length is negative - */ - @inline - def createArray(elemClass: Class[_], length: Int): AnyRef = - java.lang.reflect.Array.newInstance(elemClass, length) - - /** Assigns the value of 0 to each element in the array. - * @param arr A non-null Array[Int]. - * @throws `java.lang.NullPointerException` If `arr` is `null`. - */ - @inline - def arrayclear(arr: Array[Int]) { java.util.Arrays.fill(arr, 0) } - - /** Returns the `Class` object associated with the class or interface with the given string name using the current `ClassLoader`. - * On the JVM, invoking this method is equivalent to: `java.lang.Class.forName(name)` - * - * For more information, please see the Java documentation for [[java.lang.Class]]. - * - * @param name the fully qualified name of the desired class. - * @return the `Class` object for the class with the specified name. - * @throws `java.lang.LinkageError` if the linkage fails - * @throws `java.lang.ExceptionInInitializerError` if the initialization provoked by this method fails - * @throws `java.lang.ClassNotFoundException` if the class cannot be located - * @example {{{ - * val a = scala.compat.Platform.getClassForName("java.lang.Integer") // returns the Class[_] for java.lang.Integer - * }}} - */ - @inline - def getClassForName(name: String): Class[_] = java.lang.Class.forName(name) - - /** The default line separator. - * - * On the JavaScript backend, this is always "\n". - */ - val EOL = "\n" - - /** The current time in milliseconds. The time is counted since 1 January 1970 - * UTC. - * - * Note that the operating system timer used to obtain this value may be less - * precise than a millisecond. - */ - @inline - def currentTime: Long = System.currentTimeMillis() - - /** Runs the garbage collector. - * - * This is a request that the underlying JVM runs the garbage collector. - * The results of this call depends heavily on the JVM used. - * The underlying JVM is free to ignore this request. - */ - @inline - def collectGarbage(): Unit = System.gc() - - /** The name of the default character set encoding as a string */ - @inline - def defaultCharsetName: String = java.nio.charset.Charset.defaultCharset.name -} diff --git a/scalalib/overrides-2.11/scala/concurrent/impl/AbstractPromise.scala b/scalalib/overrides-2.11/scala/concurrent/impl/AbstractPromise.scala deleted file mode 100644 index 8ea135e4d7..0000000000 --- a/scalalib/overrides-2.11/scala/concurrent/impl/AbstractPromise.scala +++ /dev/null @@ -1,29 +0,0 @@ -package scala.concurrent.impl - -/** - * JavaScript specific implementation of AbstractPromise - * - * This basically implements a "CAS" in Scala for JavaScript. Its - * implementation is trivial because there is no multi-threading. - * - * @author Tobias Schlatter - */ -abstract class AbstractPromise { - - private var state: AnyRef = _ - - protected final - def updateState(oldState: AnyRef, newState: AnyRef): Boolean = { - if (state eq oldState) { - state = newState - true - } else false - } - - protected final def getState: AnyRef = state - -} - -object AbstractPromise { - protected def updater = ??? -} diff --git a/scalalib/overrides-2.11/scala/package.scala b/scalalib/overrides-2.11/scala/package.scala deleted file mode 100644 index 21051d473f..0000000000 --- a/scalalib/overrides-2.11/scala/package.scala +++ /dev/null @@ -1,133 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - - -/** - * Core Scala types. They are always available without an explicit import. - * @contentDiagram hideNodes "scala.Serializable" - */ -package object scala { - type Throwable = java.lang.Throwable - type Exception = java.lang.Exception - type Error = java.lang.Error - - type RuntimeException = java.lang.RuntimeException - type NullPointerException = java.lang.NullPointerException - type ClassCastException = java.lang.ClassCastException - type IndexOutOfBoundsException = java.lang.IndexOutOfBoundsException - type ArrayIndexOutOfBoundsException = java.lang.ArrayIndexOutOfBoundsException - type StringIndexOutOfBoundsException = java.lang.StringIndexOutOfBoundsException - type UnsupportedOperationException = java.lang.UnsupportedOperationException - type IllegalArgumentException = java.lang.IllegalArgumentException - type NoSuchElementException = java.util.NoSuchElementException - type NumberFormatException = java.lang.NumberFormatException - type AbstractMethodError = java.lang.AbstractMethodError - type InterruptedException = java.lang.InterruptedException - - // A dummy used by the specialization annotation. - val AnyRef = new Specializable { - override def toString = "object AnyRef" - } - - type TraversableOnce[+A] = scala.collection.TraversableOnce[A] - - type Traversable[+A] = scala.collection.Traversable[A] - val Traversable = scala.collection.Traversable - - type Iterable[+A] = scala.collection.Iterable[A] - val Iterable = scala.collection.Iterable - - type Seq[+A] = scala.collection.Seq[A] - val Seq = scala.collection.Seq - - type IndexedSeq[+A] = scala.collection.IndexedSeq[A] - val IndexedSeq = scala.collection.IndexedSeq - - type Iterator[+A] = scala.collection.Iterator[A] - val Iterator = scala.collection.Iterator - - type BufferedIterator[+A] = scala.collection.BufferedIterator[A] - - type List[+A] = scala.collection.immutable.List[A] - val List = scala.collection.immutable.List - - val Nil = scala.collection.immutable.Nil - - type ::[A] = scala.collection.immutable.::[A] - val :: = scala.collection.immutable.:: - - val +: = scala.collection.+: - val :+ = scala.collection.:+ - - type Stream[+A] = scala.collection.immutable.Stream[A] - val Stream = scala.collection.immutable.Stream - val #:: = scala.collection.immutable.Stream.#:: - - type Vector[+A] = scala.collection.immutable.Vector[A] - val Vector = scala.collection.immutable.Vector - - type StringBuilder = scala.collection.mutable.StringBuilder - val StringBuilder = scala.collection.mutable.StringBuilder - - type Range = scala.collection.immutable.Range - val Range = scala.collection.immutable.Range - - // Numeric types which were moved into scala.math.* - - type BigDecimal = scala.math.BigDecimal - lazy val BigDecimal = scala.math.BigDecimal - - type BigInt = scala.math.BigInt - lazy val BigInt = scala.math.BigInt - - type Equiv[T] = scala.math.Equiv[T] - val Equiv = scala.math.Equiv - - type Fractional[T] = scala.math.Fractional[T] - val Fractional = scala.math.Fractional - - type Integral[T] = scala.math.Integral[T] - val Integral = scala.math.Integral - - type Numeric[T] = scala.math.Numeric[T] - val Numeric = scala.math.Numeric - - type Ordered[T] = scala.math.Ordered[T] - val Ordered = scala.math.Ordered - - type Ordering[T] = scala.math.Ordering[T] - val Ordering = scala.math.Ordering - - type PartialOrdering[T] = scala.math.PartialOrdering[T] - type PartiallyOrdered[T] = scala.math.PartiallyOrdered[T] - - type Either[+A, +B] = scala.util.Either[A, B] - val Either = scala.util.Either - - type Left[+A, +B] = scala.util.Left[A, B] - val Left = scala.util.Left - - type Right[+A, +B] = scala.util.Right[A, B] - val Right = scala.util.Right - - // Annotations which we might move to annotation.* -/* - type SerialVersionUID = annotation.SerialVersionUID - type deprecated = annotation.deprecated - type deprecatedName = annotation.deprecatedName - type inline = annotation.inline - type native = annotation.native - type noinline = annotation.noinline - type remote = annotation.remote - type specialized = annotation.specialized - type transient = annotation.transient - type throws = annotation.throws - type unchecked = annotation.unchecked.unchecked - type volatile = annotation.volatile - */ -} diff --git a/scalalib/overrides-2.11/scala/reflect/ClassTag.scala b/scalalib/overrides-2.11/scala/reflect/ClassTag.scala deleted file mode 100644 index 1b79bf395e..0000000000 --- a/scalalib/overrides-2.11/scala/reflect/ClassTag.scala +++ /dev/null @@ -1,156 +0,0 @@ -package scala -package reflect - -import java.lang.{ Class => jClass } - -/** - * - * A `ClassTag[T]` stores the erased class of a given type `T`, accessible via the `runtimeClass` - * field. This is particularly useful for instantiating `Array`s whose element types are unknown - * at compile time. - * - * `ClassTag`s are a weaker special case of [[scala.reflect.api.TypeTags#TypeTag]]s, in that they - * wrap only the runtime class of a given type, whereas a `TypeTag` contains all static type - * information. That is, `ClassTag`s are constructed from knowing only the top-level class of a - * type, without necessarily knowing all of its argument types. This runtime information is enough - * for runtime `Array` creation. - * - * For example: - * {{{ - * scala> def mkArray[T : ClassTag](elems: T*) = Array[T](elems: _*) - * mkArray: [T](elems: T*)(implicit evidence$1: scala.reflect.ClassTag[T])Array[T] - * - * scala> mkArray(42, 13) - * res0: Array[Int] = Array(42, 13) - * - * scala> mkArray("Japan","Brazil","Germany") - * res1: Array[String] = Array(Japan, Brazil, Germany) - * }}} - * - * See [[scala.reflect.api.TypeTags]] for more examples, or the - * [[http://docs.scala-lang.org/overviews/reflection/typetags-manifests.html Reflection Guide: TypeTags]] - * for more details. - * - */ -@scala.annotation.implicitNotFound(msg = "No ClassTag available for ${T}") -trait ClassTag[T] extends ClassManifestDeprecatedApis[T] with Equals with Serializable { - // please, don't add any APIs here, like it was with `newWrappedArray` and `newArrayBuilder` - // class tags, and all tags in general, should be as minimalistic as possible - - /** A class representing the type `U` to which `T` would be erased. - * Note that there is no subtyping relationship between `T` and `U`. - */ - def runtimeClass: jClass[_] - - /** Produces a `ClassTag` that knows how to instantiate an `Array[Array[T]]` */ - def wrap: ClassTag[Array[T]] = ClassTag[Array[T]](arrayClass(runtimeClass)) - - /** Produces a new array with element type `T` and length `len` */ - override def newArray(len: Int): Array[T] = - runtimeClass match { - case java.lang.Byte.TYPE => new Array[Byte](len).asInstanceOf[Array[T]] - case java.lang.Short.TYPE => new Array[Short](len).asInstanceOf[Array[T]] - case java.lang.Character.TYPE => new Array[Char](len).asInstanceOf[Array[T]] - case java.lang.Integer.TYPE => new Array[Int](len).asInstanceOf[Array[T]] - case java.lang.Long.TYPE => new Array[Long](len).asInstanceOf[Array[T]] - case java.lang.Float.TYPE => new Array[Float](len).asInstanceOf[Array[T]] - case java.lang.Double.TYPE => new Array[Double](len).asInstanceOf[Array[T]] - case java.lang.Boolean.TYPE => new Array[Boolean](len).asInstanceOf[Array[T]] - case java.lang.Void.TYPE => new Array[Unit](len).asInstanceOf[Array[T]] - case _ => java.lang.reflect.Array.newInstance(runtimeClass, len).asInstanceOf[Array[T]] - } - - /** A ClassTag[T] can serve as an extractor that matches only objects of type T. - * - * The compiler tries to turn unchecked type tests in pattern matches into checked ones - * by wrapping a `(_: T)` type pattern as `ct(_: T)`, where `ct` is the `ClassTag[T]` instance. - * Type tests necessary before calling other extractors are treated similarly. - * `SomeExtractor(...)` is turned into `ct(SomeExtractor(...))` if `T` in `SomeExtractor.unapply(x: T)` - * is uncheckable, but we have an instance of `ClassTag[T]`. - */ - def unapply(x: Any): Option[T] = - if (null != x && ( - (runtimeClass.isInstance(x)) - || (x.isInstanceOf[Byte] && runtimeClass.isAssignableFrom(classOf[Byte])) - || (x.isInstanceOf[Short] && runtimeClass.isAssignableFrom(classOf[Short])) - || (x.isInstanceOf[Char] && runtimeClass.isAssignableFrom(classOf[Char])) - || (x.isInstanceOf[Int] && runtimeClass.isAssignableFrom(classOf[Int])) - || (x.isInstanceOf[Long] && runtimeClass.isAssignableFrom(classOf[Long])) - || (x.isInstanceOf[Float] && runtimeClass.isAssignableFrom(classOf[Float])) - || (x.isInstanceOf[Double] && runtimeClass.isAssignableFrom(classOf[Double])) - || (x.isInstanceOf[Boolean] && runtimeClass.isAssignableFrom(classOf[Boolean])) - || (x.isInstanceOf[Unit] && runtimeClass.isAssignableFrom(classOf[Unit]))) - ) Some(x.asInstanceOf[T]) - else None - - // TODO: deprecate overloads in 2.12.0, remove in 2.13.0 - def unapply(x: Byte) : Option[T] = unapply(x: Any) - def unapply(x: Short) : Option[T] = unapply(x: Any) - def unapply(x: Char) : Option[T] = unapply(x: Any) - def unapply(x: Int) : Option[T] = unapply(x: Any) - def unapply(x: Long) : Option[T] = unapply(x: Any) - def unapply(x: Float) : Option[T] = unapply(x: Any) - def unapply(x: Double) : Option[T] = unapply(x: Any) - def unapply(x: Boolean) : Option[T] = unapply(x: Any) - def unapply(x: Unit) : Option[T] = unapply(x: Any) - - // case class accessories - override def canEqual(x: Any) = x.isInstanceOf[ClassTag[_]] - override def equals(x: Any) = x.isInstanceOf[ClassTag[_]] && this.runtimeClass == x.asInstanceOf[ClassTag[_]].runtimeClass - override def hashCode = scala.runtime.ScalaRunTime.hash(runtimeClass) - override def toString = { - def prettyprint(clazz: jClass[_]): String = - if (clazz.isArray) s"Array[${prettyprint(clazz.getComponentType)}]" else - clazz.getName - prettyprint(runtimeClass) - } -} - -/** - * Class tags corresponding to primitive types and constructor/extractor for ClassTags. - */ -object ClassTag { - def Byte : ClassTag[scala.Byte] = ManifestFactory.Byte - def Short : ClassTag[scala.Short] = ManifestFactory.Short - def Char : ClassTag[scala.Char] = ManifestFactory.Char - def Int : ClassTag[scala.Int] = ManifestFactory.Int - def Long : ClassTag[scala.Long] = ManifestFactory.Long - def Float : ClassTag[scala.Float] = ManifestFactory.Float - def Double : ClassTag[scala.Double] = ManifestFactory.Double - def Boolean : ClassTag[scala.Boolean] = ManifestFactory.Boolean - def Unit : ClassTag[scala.Unit] = ManifestFactory.Unit - def Any : ClassTag[scala.Any] = ManifestFactory.Any - def Object : ClassTag[java.lang.Object] = ManifestFactory.Object - def AnyVal : ClassTag[scala.AnyVal] = ManifestFactory.AnyVal - def AnyRef : ClassTag[scala.AnyRef] = ManifestFactory.AnyRef - def Nothing : ClassTag[scala.Nothing] = ManifestFactory.Nothing - def Null : ClassTag[scala.Null] = ManifestFactory.Null - - def apply[T](runtimeClass1: jClass[_]): ClassTag[T] = - runtimeClass1 match { - case java.lang.Byte.TYPE => ClassTag.Byte.asInstanceOf[ClassTag[T]] - case java.lang.Short.TYPE => ClassTag.Short.asInstanceOf[ClassTag[T]] - case java.lang.Character.TYPE => ClassTag.Char.asInstanceOf[ClassTag[T]] - case java.lang.Integer.TYPE => ClassTag.Int.asInstanceOf[ClassTag[T]] - case java.lang.Long.TYPE => ClassTag.Long.asInstanceOf[ClassTag[T]] - case java.lang.Float.TYPE => ClassTag.Float.asInstanceOf[ClassTag[T]] - case java.lang.Double.TYPE => ClassTag.Double.asInstanceOf[ClassTag[T]] - case java.lang.Boolean.TYPE => ClassTag.Boolean.asInstanceOf[ClassTag[T]] - case java.lang.Void.TYPE => ClassTag.Unit.asInstanceOf[ClassTag[T]] - case _ => - if (classOf[java.lang.Object] == runtimeClass1) - ClassTag.Object.asInstanceOf[ClassTag[T]] - else if (classOf[scala.runtime.Nothing$] == runtimeClass1) - ClassTag.Nothing.asInstanceOf[ClassTag[T]] - else if (classOf[scala.runtime.Null$] == runtimeClass1) - ClassTag.Null.asInstanceOf[ClassTag[T]] - else - new ClassClassTag[T](runtimeClass1) - } - - @inline - private final class ClassClassTag[T]( - val runtimeClass: Class[_]) extends ClassTag[T] - - def unapply[T](ctag: ClassTag[T]): Option[Class[_]] = Some(ctag.runtimeClass) -} diff --git a/scalalib/overrides-2.11/scala/reflect/Manifest.scala b/scalalib/overrides-2.11/scala/reflect/Manifest.scala deleted file mode 100644 index 071e44b457..0000000000 --- a/scalalib/overrides-2.11/scala/reflect/Manifest.scala +++ /dev/null @@ -1,292 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2007-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package reflect - -import scala.collection.mutable.{ ArrayBuilder, WrappedArray } - -/** A `Manifest[T]` is an opaque descriptor for type T. Its supported use - * is to give access to the erasure of the type as a `Class` instance, as - * is necessary for the creation of native `Arrays` if the class is not - * known at compile time. - * - * The type-relation operators `<:<` and `=:=` should be considered - * approximations only, as there are numerous aspects of type conformance - * which are not yet adequately represented in manifests. - * - * Example usages: -{{{ - def arr[T] = new Array[T](0) // does not compile - def arr[T](implicit m: Manifest[T]) = new Array[T](0) // compiles - def arr[T: Manifest] = new Array[T](0) // shorthand for the preceding - - // Methods manifest, classManifest, and optManifest are in [[scala.Predef]]. - def isApproxSubType[T: Manifest, U: Manifest] = manifest[T] <:< manifest[U] - isApproxSubType[List[String], List[AnyRef]] // true - isApproxSubType[List[String], List[Int]] // false - - def methods[T: ClassManifest] = classManifest[T].erasure.getMethods - def retType[T: ClassManifest](name: String) = - methods[T] find (_.getName == name) map (_.getGenericReturnType) - - retType[Map[_, _]]("values") // Some(scala.collection.Iterable) -}}} - * - */ -@scala.annotation.implicitNotFound(msg = "No Manifest available for ${T}.") -// TODO undeprecated until Scala reflection becomes non-experimental -// @deprecated("Use scala.reflect.ClassTag (to capture erasures) or scala.reflect.runtime.universe.TypeTag (to capture types) or both instead", "2.10.0") -trait Manifest[T] extends ClassManifest[T] with Equals { - override def typeArguments: List[Manifest[_]] = Nil - - override def arrayManifest: Manifest[Array[T]] = - Manifest.classType[Array[T]](arrayClass[T](runtimeClass), this) - - override def canEqual(that: Any): Boolean = that match { - case _: Manifest[_] => true - case _ => false - } - /** Note: testing for erasure here is important, as it is many times - * faster than <:< and rules out most comparisons. - */ - override def equals(that: Any): Boolean = that match { - case m: Manifest[_] => (m canEqual this) && (this.runtimeClass == m.runtimeClass) && (this <:< m) && (m <:< this) - case _ => false - } - override def hashCode = this.runtimeClass.## -} - -// TODO undeprecated until Scala reflection becomes non-experimental -// @deprecated("Use type tags and manually check the corresponding class or type instead", "2.10.0") -@SerialVersionUID(1L) -abstract class AnyValManifest[T <: AnyVal](override val toString: String) extends Manifest[T] with Equals { - override def <:<(that: ClassManifest[_]): Boolean = - (that eq this) || (that eq Manifest.Any) || (that eq Manifest.AnyVal) - override def canEqual(other: Any) = other match { - case _: AnyValManifest[_] => true - case _ => false - } - override def equals(that: Any): Boolean = this eq that.asInstanceOf[AnyRef] - override def hashCode = System.identityHashCode(this) -} - -/** `ManifestFactory` defines factory methods for manifests. - * It is intended for use by the compiler and should not be used in client code. - * - * Unlike `Manifest`, this factory isn't annotated with a deprecation warning. - * This is done to prevent avalanches of deprecation warnings in the code that calls methods with manifests. - * Why so complicated? Read up the comments for `ClassManifestFactory`. - */ -object ManifestFactory { - def valueManifests: List[AnyValManifest[_]] = - List(Byte, Short, Char, Int, Long, Float, Double, Boolean, Unit) - - def Byte: AnyValManifest[Byte] = ByteManifest - def Short: AnyValManifest[Short] = ShortManifest - def Char: AnyValManifest[Char] = CharManifest - def Int: AnyValManifest[Int] = IntManifest - def Long: AnyValManifest[Long] = LongManifest - def Float: AnyValManifest[Float] = FloatManifest - def Double: AnyValManifest[Double] = DoubleManifest - def Boolean: AnyValManifest[Boolean] = BooleanManifest - def Unit: AnyValManifest[Unit] = UnitManifest - def Any: Manifest[scala.Any] = AnyManifest - def Object: Manifest[java.lang.Object] = ObjectManifest - def AnyRef: Manifest[scala.AnyRef] = Object.asInstanceOf[Manifest[scala.AnyRef]] - def AnyVal: Manifest[scala.AnyVal] = AnyValManifest - def Null: Manifest[scala.Null] = NullManifest - def Nothing: Manifest[scala.Nothing] = NothingManifest - - private object ByteManifest extends AnyValManifest[scala.Byte]("Byte") { - def runtimeClass = java.lang.Byte.TYPE - override def newArray(len: Int): Array[Byte] = new Array[Byte](len) - override def newWrappedArray(len: Int): WrappedArray[Byte] = new WrappedArray.ofByte(new Array[Byte](len)) - override def newArrayBuilder(): ArrayBuilder[Byte] = new ArrayBuilder.ofByte() - private def readResolve(): Any = Manifest.Byte - } - - private object ShortManifest extends AnyValManifest[scala.Short]("Short") { - def runtimeClass = java.lang.Short.TYPE - override def newArray(len: Int): Array[Short] = new Array[Short](len) - override def newWrappedArray(len: Int): WrappedArray[Short] = new WrappedArray.ofShort(new Array[Short](len)) - override def newArrayBuilder(): ArrayBuilder[Short] = new ArrayBuilder.ofShort() - private def readResolve(): Any = Manifest.Short - } - - private object CharManifest extends AnyValManifest[scala.Char]("Char") { - def runtimeClass = java.lang.Character.TYPE - override def newArray(len: Int): Array[Char] = new Array[Char](len) - override def newWrappedArray(len: Int): WrappedArray[Char] = new WrappedArray.ofChar(new Array[Char](len)) - override def newArrayBuilder(): ArrayBuilder[Char] = new ArrayBuilder.ofChar() - private def readResolve(): Any = Manifest.Char - } - - private object IntManifest extends AnyValManifest[scala.Int]("Int") { - def runtimeClass = java.lang.Integer.TYPE - override def newArray(len: Int): Array[Int] = new Array[Int](len) - override def newWrappedArray(len: Int): WrappedArray[Int] = new WrappedArray.ofInt(new Array[Int](len)) - override def newArrayBuilder(): ArrayBuilder[Int] = new ArrayBuilder.ofInt() - private def readResolve(): Any = Manifest.Int - } - - private object LongManifest extends AnyValManifest[scala.Long]("Long") { - def runtimeClass = java.lang.Long.TYPE - override def newArray(len: Int): Array[Long] = new Array[Long](len) - override def newWrappedArray(len: Int): WrappedArray[Long] = new WrappedArray.ofLong(new Array[Long](len)) - override def newArrayBuilder(): ArrayBuilder[Long] = new ArrayBuilder.ofLong() - private def readResolve(): Any = Manifest.Long - } - - private object FloatManifest extends AnyValManifest[scala.Float]("Float") { - def runtimeClass = java.lang.Float.TYPE - override def newArray(len: Int): Array[Float] = new Array[Float](len) - override def newWrappedArray(len: Int): WrappedArray[Float] = new WrappedArray.ofFloat(new Array[Float](len)) - override def newArrayBuilder(): ArrayBuilder[Float] = new ArrayBuilder.ofFloat() - private def readResolve(): Any = Manifest.Float - } - - private object DoubleManifest extends AnyValManifest[scala.Double]("Double") { - def runtimeClass = java.lang.Double.TYPE - override def newArray(len: Int): Array[Double] = new Array[Double](len) - override def newWrappedArray(len: Int): WrappedArray[Double] = new WrappedArray.ofDouble(new Array[Double](len)) - override def newArrayBuilder(): ArrayBuilder[Double] = new ArrayBuilder.ofDouble() - private def readResolve(): Any = Manifest.Double - } - - private object BooleanManifest extends AnyValManifest[scala.Boolean]("Boolean") { - def runtimeClass = java.lang.Boolean.TYPE - override def newArray(len: Int): Array[Boolean] = new Array[Boolean](len) - override def newWrappedArray(len: Int): WrappedArray[Boolean] = new WrappedArray.ofBoolean(new Array[Boolean](len)) - override def newArrayBuilder(): ArrayBuilder[Boolean] = new ArrayBuilder.ofBoolean() - private def readResolve(): Any = Manifest.Boolean - } - - private object UnitManifest extends AnyValManifest[scala.Unit]("Unit") { - def runtimeClass = java.lang.Void.TYPE - override def newArray(len: Int): Array[Unit] = new Array[Unit](len) - override def newWrappedArray(len: Int): WrappedArray[Unit] = new WrappedArray.ofUnit(new Array[Unit](len)) - override def newArrayBuilder(): ArrayBuilder[Unit] = new ArrayBuilder.ofUnit() - private def readResolve(): Any = Manifest.Unit - } - - private object AnyManifest extends PhantomManifest[scala.Any](classOf[java.lang.Object], "Any") { - override def runtimeClass = classOf[java.lang.Object] - override def newArray(len: Int) = new Array[scala.Any](len) - override def <:<(that: ClassManifest[_]): Boolean = (that eq this) - private def readResolve(): Any = Manifest.Any - } - - private object ObjectManifest extends PhantomManifest[java.lang.Object](classOf[java.lang.Object], "Object") { - override def runtimeClass = classOf[java.lang.Object] - override def newArray(len: Int) = new Array[java.lang.Object](len) - override def <:<(that: ClassManifest[_]): Boolean = (that eq this) || (that eq Any) - private def readResolve(): Any = Manifest.Object - } - - private object AnyValManifest extends PhantomManifest[scala.AnyVal](classOf[java.lang.Object], "AnyVal") { - override def runtimeClass = classOf[java.lang.Object] - override def newArray(len: Int) = new Array[scala.AnyVal](len) - override def <:<(that: ClassManifest[_]): Boolean = (that eq this) || (that eq Any) - private def readResolve(): Any = Manifest.AnyVal - } - - private object NullManifest extends PhantomManifest[scala.Null](classOf[scala.runtime.Null$], "Null") { - override def runtimeClass = classOf[scala.runtime.Null$] - override def newArray(len: Int) = new Array[scala.Null](len) - override def <:<(that: ClassManifest[_]): Boolean = - (that ne null) && (that ne Nothing) && !(that <:< AnyVal) - private def readResolve(): Any = Manifest.Null - } - - private object NothingManifest extends PhantomManifest[scala.Nothing](classOf[scala.runtime.Nothing$], "Nothing") { - override def runtimeClass = classOf[scala.runtime.Nothing$] - override def newArray(len: Int) = new Array[scala.Nothing](len) - override def <:<(that: ClassManifest[_]): Boolean = (that ne null) - private def readResolve(): Any = Manifest.Nothing - } - - private class SingletonTypeManifest[T <: AnyRef](value: AnyRef) extends Manifest[T] { - lazy val runtimeClass = value.getClass - override lazy val toString = value.toString + ".type" - } - - /** Manifest for the singleton type `value.type`. */ - def singleType[T <: AnyRef](value: AnyRef): Manifest[T] = - new SingletonTypeManifest[T](value) - - /** Manifest for the class type `clazz[args]`, where `clazz` is - * a top-level or static class. - * @note This no-prefix, no-arguments case is separate because we - * it's called from ScalaRunTime.boxArray itself. If we - * pass varargs as arrays into this, we get an infinitely recursive call - * to boxArray. (Besides, having a separate case is more efficient) - */ - def classType[T](clazz: Predef.Class[_]): Manifest[T] = - new ClassTypeManifest[T](None, clazz, Nil) - - /** Manifest for the class type `clazz`, where `clazz` is - * a top-level or static class and args are its type arguments. */ - def classType[T](clazz: Predef.Class[T], arg1: Manifest[_], args: Manifest[_]*): Manifest[T] = - new ClassTypeManifest[T](None, clazz, arg1 :: args.toList) - - /** Manifest for the class type `clazz[args]`, where `clazz` is - * a class with non-package prefix type `prefix` and type arguments `args`. - */ - def classType[T](prefix: Manifest[_], clazz: Predef.Class[_], args: Manifest[_]*): Manifest[T] = - new ClassTypeManifest[T](Some(prefix), clazz, args.toList) - - private abstract class PhantomManifest[T](_runtimeClass: Predef.Class[_], - override val toString: String) extends ClassTypeManifest[T](None, _runtimeClass, Nil) { - override def equals(that: Any): Boolean = this eq that.asInstanceOf[AnyRef] - override def hashCode = System.identityHashCode(this) - } - - /** Manifest for the class type `clazz[args]`, where `clazz` is - * a top-level or static class. */ - private class ClassTypeManifest[T](prefix: Option[Manifest[_]], - runtimeClass1: Predef.Class[_], - override val typeArguments: List[Manifest[_]]) extends Manifest[T] { - def runtimeClass: Predef.Class[_] = runtimeClass1 - override def toString = - (if (prefix.isEmpty) "" else prefix.get.toString+"#") + - (if (runtimeClass.isArray) "Array" else runtimeClass.getName) + - argString - } - - def arrayType[T](arg: Manifest[_]): Manifest[Array[T]] = - arg.asInstanceOf[Manifest[T]].arrayManifest - - /** Manifest for the abstract type `prefix # name'. `upperBound` is not - * strictly necessary as it could be obtained by reflection. It was - * added so that erasure can be calculated without reflection. */ - def abstractType[T](prefix: Manifest[_], name: String, upperBound: Predef.Class[_], args: Manifest[_]*): Manifest[T] = - new Manifest[T] { - def runtimeClass = upperBound - override val typeArguments = args.toList - override def toString = prefix.toString+"#"+name+argString - } - - /** Manifest for the unknown type `_ >: L <: U` in an existential. - */ - def wildcardType[T](lowerBound: Manifest[_], upperBound: Manifest[_]): Manifest[T] = - new Manifest[T] { - def runtimeClass = upperBound.runtimeClass - override def toString = - "_" + - (if (lowerBound eq Nothing) "" else " >: "+lowerBound) + - (if (upperBound eq Nothing) "" else " <: "+upperBound) - } - - /** Manifest for the intersection type `parents_0 with ... with parents_n'. */ - def intersectionType[T](parents: Manifest[_]*): Manifest[T] = - new Manifest[T] { - def runtimeClass = parents.head.runtimeClass - override def toString = parents.mkString(" with ") - } -} diff --git a/scalalib/overrides-2.11/scala/reflect/NameTransformer.scala b/scalalib/overrides-2.11/scala/reflect/NameTransformer.scala deleted file mode 100644 index fcb8bdd1b8..0000000000 --- a/scalalib/overrides-2.11/scala/reflect/NameTransformer.scala +++ /dev/null @@ -1,161 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package reflect - -/** Provides functions to encode and decode Scala symbolic names. - * Also provides some constants. - */ -object NameTransformer { - // XXX Short term: providing a way to alter these without having to recompile - // the compiler before recompiling the compiler. - val MODULE_SUFFIX_STRING = "$" - val NAME_JOIN_STRING = "$" - val MODULE_INSTANCE_NAME = "MODULE$" - val LOCAL_SUFFIX_STRING = " " - val SETTER_SUFFIX_STRING = "_$eq" - val TRAIT_SETTER_SEPARATOR_STRING = "$_setter_$" - - private val nops = 128 - private val ncodes = 26 * 26 - - private class OpCodes(val op: Char, val code: String, val next: OpCodes) - - private val op2code = new Array[String](nops) - private val code2op = new Array[OpCodes](ncodes) - private def enterOp(op: Char, code: String) = { - op2code(op.toInt) = code - val c = (code.charAt(1) - 'a') * 26 + code.charAt(2) - 'a' - code2op(c.toInt) = new OpCodes(op, code, code2op(c)) - } - - /* Note: decoding assumes opcodes are only ever lowercase. */ - enterOp('~', "$tilde") - enterOp('=', "$eq") - enterOp('<', "$less") - enterOp('>', "$greater") - enterOp('!', "$bang") - enterOp('#', "$hash") - enterOp('%', "$percent") - enterOp('^', "$up") - enterOp('&', "$amp") - enterOp('|', "$bar") - enterOp('*', "$times") - enterOp('/', "$div") - enterOp('+', "$plus") - enterOp('-', "$minus") - enterOp(':', "$colon") - enterOp('\\', "$bslash") - enterOp('?', "$qmark") - enterOp('@', "$at") - - /** Replace operator symbols by corresponding `\$opname`. - * - * @param name the string to encode - * @return the string with all recognized opchars replaced with their encoding - */ - def encode(name: String): String = { - var buf: StringBuilder = null - val len = name.length() - var i = 0 - while (i < len) { - val c = name charAt i - if (c < nops && (op2code(c.toInt) ne null)) { - if (buf eq null) { - buf = new StringBuilder() - buf.append(name.substring(0, i)) - } - buf.append(op2code(c.toInt)) - /* Handle glyphs that are not valid Java/JVM identifiers */ - } - else if (!Character.isJavaIdentifierPart(c)) { - if (buf eq null) { - buf = new StringBuilder() - buf.append(name.substring(0, i)) - } - buf.append("$u%04X".format(c.toInt)) - } - else if (buf ne null) { - buf.append(c) - } - i += 1 - } - if (buf eq null) name else buf.toString() - } - - /** Replace `\$opname` by corresponding operator symbol. - * - * @param name0 the string to decode - * @return the string with all recognized operator symbol encodings replaced with their name - */ - def decode(name0: String): String = { - //System.out.println("decode: " + name);//DEBUG - val name = if (name0.endsWith("")) name0.stripSuffix("") + "this" - else name0 - var buf: StringBuilder = null - val len = name.length() - var i = 0 - while (i < len) { - var ops: OpCodes = null - var unicode = false - val c = name charAt i - if (c == '$' && i + 2 < len) { - val ch1 = name.charAt(i+1) - if ('a' <= ch1 && ch1 <= 'z') { - val ch2 = name.charAt(i+2) - if ('a' <= ch2 && ch2 <= 'z') { - ops = code2op((ch1 - 'a') * 26 + ch2 - 'a') - while ((ops ne null) && !name.startsWith(ops.code, i)) ops = ops.next - if (ops ne null) { - if (buf eq null) { - buf = new StringBuilder() - buf.append(name.substring(0, i)) - } - buf.append(ops.op) - i += ops.code.length() - } - /* Handle the decoding of Unicode glyphs that are - * not valid Java/JVM identifiers */ - } else if ((len - i) >= 6 && // Check that there are enough characters left - ch1 == 'u' && - ((Character.isDigit(ch2)) || - ('A' <= ch2 && ch2 <= 'F'))) { - /* Skip past "$u", next four should be hexadecimal */ - val hex = name.substring(i+2, i+6) - try { - val str = Integer.parseInt(hex, 16).toChar - if (buf eq null) { - buf = new StringBuilder() - buf.append(name.substring(0, i)) - } - buf.append(str) - /* 2 for "$u", 4 for hexadecimal number */ - i += 6 - unicode = true - } catch { - case _:NumberFormatException => - /* `hex` did not decode to a hexadecimal number, so - * do nothing. */ - } - } - } - } - /* If we didn't see an opcode or encoded Unicode glyph, and the - buffer is non-empty, write the current character and advance - one */ - if ((ops eq null) && !unicode) { - if (buf ne null) - buf.append(c) - i += 1 - } - } - //System.out.println("= " + (if (buf == null) name else buf.toString()));//DEBUG - if (buf eq null) name else buf.toString() - } -} diff --git a/scalalib/overrides-2.11/scala/runtime/ScalaRunTime.scala b/scalalib/overrides-2.11/scala/runtime/ScalaRunTime.scala deleted file mode 100644 index baefecf3de..0000000000 --- a/scalalib/overrides-2.11/scala/runtime/ScalaRunTime.scala +++ /dev/null @@ -1,359 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package runtime - -import scala.collection.{ Seq, IndexedSeq, TraversableView, AbstractIterator, GenIterable } -import scala.collection.mutable.WrappedArray -import scala.collection.immutable.{ StringLike, NumericRange, List, Stream, Nil, :: } -import scala.collection.generic.{ Sorted, IsTraversableLike } -import scala.reflect.{ ClassTag, classTag } -import scala.util.control.ControlThrowable -import java.lang.{ Class => jClass } - -import java.lang.Double.doubleToLongBits -import java.lang.reflect.{ Modifier, Method => JMethod } - -/** The object ScalaRunTime provides support methods required by - * the scala runtime. All these methods should be considered - * outside the API and subject to change or removal without notice. - */ -object ScalaRunTime { - def isArray(x: Any, atLevel: Int = 1): Boolean = - x != null && isArrayClass(x.getClass, atLevel) - - private def isArrayClass(clazz: jClass[_], atLevel: Int): Boolean = - clazz != null && clazz.isArray && (atLevel == 1 || isArrayClass(clazz.getComponentType, atLevel - 1)) - - def isValueClass(clazz: jClass[_]) = clazz.isPrimitive() - - // includes specialized subclasses and future proofed against hypothetical TupleN (for N > 22) - def isTuple(x: Any) = x != null && x.getClass.getName.startsWith("scala.Tuple") - def isAnyVal(x: Any) = x match { - case _: Byte | _: Short | _: Char | _: Int | _: Long | _: Float | _: Double | _: Boolean | _: Unit => true - case _ => false - } - - // A helper method to make my life in the pattern matcher a lot easier. - def drop[Repr](coll: Repr, num: Int)(implicit traversable: IsTraversableLike[Repr]): Repr = - traversable conversion coll drop num - - /** Return the class object representing an array with element class `clazz`. - */ - def arrayClass(clazz: jClass[_]): jClass[_] = { - // newInstance throws an exception if the erasure is Void.TYPE. see SI-5680 - if (clazz == java.lang.Void.TYPE) classOf[Array[Unit]] - else java.lang.reflect.Array.newInstance(clazz, 0).getClass - } - - /** Return the class object representing elements in arrays described by a given schematic. - */ - def arrayElementClass(schematic: Any): jClass[_] = schematic match { - case cls: jClass[_] => cls.getComponentType - case tag: ClassTag[_] => tag.runtimeClass - case _ => - throw new UnsupportedOperationException(s"unsupported schematic $schematic (${schematic.getClass})") - } - - /** Return the class object representing an unboxed value type, - * e.g., classOf[int], not classOf[java.lang.Integer]. The compiler - * rewrites expressions like 5.getClass to come here. - */ - def anyValClass[T <: AnyVal : ClassTag](value: T): jClass[T] = - classTag[T].runtimeClass.asInstanceOf[jClass[T]] - - /** Retrieve generic array element */ - def array_apply(xs: AnyRef, idx: Int): Any = { - xs match { - case x: Array[AnyRef] => x(idx).asInstanceOf[Any] - case x: Array[Int] => x(idx).asInstanceOf[Any] - case x: Array[Double] => x(idx).asInstanceOf[Any] - case x: Array[Long] => x(idx).asInstanceOf[Any] - case x: Array[Float] => x(idx).asInstanceOf[Any] - case x: Array[Char] => x(idx).asInstanceOf[Any] - case x: Array[Byte] => x(idx).asInstanceOf[Any] - case x: Array[Short] => x(idx).asInstanceOf[Any] - case x: Array[Boolean] => x(idx).asInstanceOf[Any] - case null => throw new NullPointerException - } - } - - /** update generic array element */ - def array_update(xs: AnyRef, idx: Int, value: Any): Unit = { - xs match { - case x: Array[AnyRef] => x(idx) = value.asInstanceOf[AnyRef] - case x: Array[Int] => x(idx) = value.asInstanceOf[Int] - case x: Array[Double] => x(idx) = value.asInstanceOf[Double] - case x: Array[Long] => x(idx) = value.asInstanceOf[Long] - case x: Array[Float] => x(idx) = value.asInstanceOf[Float] - case x: Array[Char] => x(idx) = value.asInstanceOf[Char] - case x: Array[Byte] => x(idx) = value.asInstanceOf[Byte] - case x: Array[Short] => x(idx) = value.asInstanceOf[Short] - case x: Array[Boolean] => x(idx) = value.asInstanceOf[Boolean] - case null => throw new NullPointerException - } - } - - /** Get generic array length */ - def array_length(xs: AnyRef): Int = xs match { - case x: Array[AnyRef] => x.length - case x: Array[Int] => x.length - case x: Array[Double] => x.length - case x: Array[Long] => x.length - case x: Array[Float] => x.length - case x: Array[Char] => x.length - case x: Array[Byte] => x.length - case x: Array[Short] => x.length - case x: Array[Boolean] => x.length - case null => throw new NullPointerException - } - - def array_clone(xs: AnyRef): AnyRef = xs match { - case x: Array[AnyRef] => ArrayRuntime.cloneArray(x) - case x: Array[Int] => ArrayRuntime.cloneArray(x) - case x: Array[Double] => ArrayRuntime.cloneArray(x) - case x: Array[Long] => ArrayRuntime.cloneArray(x) - case x: Array[Float] => ArrayRuntime.cloneArray(x) - case x: Array[Char] => ArrayRuntime.cloneArray(x) - case x: Array[Byte] => ArrayRuntime.cloneArray(x) - case x: Array[Short] => ArrayRuntime.cloneArray(x) - case x: Array[Boolean] => ArrayRuntime.cloneArray(x) - case null => throw new NullPointerException - } - - /** Convert an array to an object array. - * Needed to deal with vararg arguments of primitive types that are passed - * to a generic Java vararg parameter T ... - */ - def toObjectArray(src: AnyRef): Array[Object] = src match { - case x: Array[AnyRef] => x - case _ => - val length = array_length(src) - val dest = new Array[Object](length) - for (i <- 0 until length) - array_update(dest, i, array_apply(src, i)) - dest - } - - def toArray[T](xs: scala.collection.Seq[T]) = { - val arr = new Array[AnyRef](xs.length) - var i = 0 - for (x <- xs) { - arr(i) = x.asInstanceOf[AnyRef] - i += 1 - } - arr - } - - // Java bug: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4071957 - // More background at ticket #2318. - def ensureAccessible(m: JMethod): JMethod = scala.reflect.ensureAccessible(m) - - def checkInitialized[T <: AnyRef](x: T): T = - if (x == null) throw new UninitializedError else x - - def _toString(x: Product): String = - x.productIterator.mkString(x.productPrefix + "(", ",", ")") - - def _hashCode(x: Product): Int = scala.util.hashing.MurmurHash3.productHash(x) - - /** A helper for case classes. */ - def typedProductIterator[T](x: Product): Iterator[T] = { - new AbstractIterator[T] { - private var c: Int = 0 - private val cmax = x.productArity - def hasNext = c < cmax - def next() = { - val result = x.productElement(c) - c += 1 - result.asInstanceOf[T] - } - } - } - - /** Fast path equality method for inlining; used when -optimise is set. - */ - @inline def inlinedEquals(x: Object, y: Object): Boolean = - if (x eq y) true - else if (x eq null) false - else if (x.isInstanceOf[java.lang.Number]) BoxesRunTime.equalsNumObject(x.asInstanceOf[java.lang.Number], y) - else if (x.isInstanceOf[java.lang.Character]) BoxesRunTime.equalsCharObject(x.asInstanceOf[java.lang.Character], y) - else x.equals(y) - - def _equals(x: Product, y: Any): Boolean = y match { - case y: Product if x.productArity == y.productArity => x.productIterator sameElements y.productIterator - case _ => false - } - - // hashcode ----------------------------------------------------------- - // - // Note that these are the implementations called by ##, so they - // must not call ## themselves. - - def hash(x: Any): Int = - if (x == null) 0 - else if (x.isInstanceOf[java.lang.Number]) BoxesRunTime.hashFromNumber(x.asInstanceOf[java.lang.Number]) - else x.hashCode - - def hash(dv: Double): Int = { - val iv = dv.toInt - if (iv == dv) return iv - - val lv = dv.toLong - if (lv == dv) return lv.hashCode - - val fv = dv.toFloat - if (fv == dv) fv.hashCode else dv.hashCode - } - def hash(fv: Float): Int = { - val iv = fv.toInt - if (iv == fv) return iv - - val lv = fv.toLong - if (lv == fv) hash(lv) - else fv.hashCode - } - def hash(lv: Long): Int = { - val low = lv.toInt - val lowSign = low >>> 31 - val high = (lv >>> 32).toInt - low ^ (high + lowSign) - } - def hash(x: Number): Int = runtime.BoxesRunTime.hashFromNumber(x) - - // The remaining overloads are here for completeness, but the compiler - // inlines these definitions directly so they're not generally used. - def hash(x: Int): Int = x - def hash(x: Short): Int = x.toInt - def hash(x: Byte): Int = x.toInt - def hash(x: Char): Int = x.toInt - def hash(x: Boolean): Int = if (x) true.hashCode else false.hashCode - def hash(x: Unit): Int = 0 - - /** A helper method for constructing case class equality methods, - * because existential types get in the way of a clean outcome and - * it's performing a series of Any/Any equals comparisons anyway. - * See ticket #2867 for specifics. - */ - def sameElements(xs1: scala.collection.Seq[Any], xs2: scala.collection.Seq[Any]) = xs1 sameElements xs2 - - /** Given any Scala value, convert it to a String. - * - * The primary motivation for this method is to provide a means for - * correctly obtaining a String representation of a value, while - * avoiding the pitfalls of naively calling toString on said value. - * In particular, it addresses the fact that (a) toString cannot be - * called on null and (b) depending on the apparent type of an - * array, toString may or may not print it in a human-readable form. - * - * @param arg the value to stringify - * @return a string representation of arg. - */ - def stringOf(arg: Any): String = stringOf(arg, scala.Int.MaxValue) - def stringOf(arg: Any, maxElements: Int): String = { - def packageOf(x: AnyRef) = x.getClass.getPackage match { - case null => "" - case p => p.getName - } - def isScalaClass(x: AnyRef) = packageOf(x) startsWith "scala." - def isScalaCompilerClass(x: AnyRef) = packageOf(x) startsWith "scala.tools.nsc." - - // We use reflection because the scala.xml package might not be available - def isSubClassOf(potentialSubClass: Class[_], ofClass: String) = - try { - val classLoader = potentialSubClass.getClassLoader - val clazz = Class.forName(ofClass, /*initialize =*/ false, classLoader) - clazz.isAssignableFrom(potentialSubClass) - } catch { - case cnfe: ClassNotFoundException => false - } - def isXmlNode(potentialSubClass: Class[_]) = isSubClassOf(potentialSubClass, "scala.xml.Node") - def isXmlMetaData(potentialSubClass: Class[_]) = isSubClassOf(potentialSubClass, "scala.xml.MetaData") - - // When doing our own iteration is dangerous - def useOwnToString(x: Any) = x match { - // Range/NumericRange have a custom toString to avoid walking a gazillion elements - case _: Range | _: NumericRange[_] => true - // Sorted collections to the wrong thing (for us) on iteration - ticket #3493 - case _: Sorted[_, _] => true - // StringBuilder(a, b, c) and similar not so attractive - case _: StringLike[_] => true - // Don't want to evaluate any elements in a view - case _: TraversableView[_, _] => true - // Node extends NodeSeq extends Seq[Node] and MetaData extends Iterable[MetaData] - // -> catch those by isXmlNode and isXmlMetaData. - // Don't want to a) traverse infinity or b) be overly helpful with peoples' custom - // collections which may have useful toString methods - ticket #3710 - // or c) print AbstractFiles which are somehow also Iterable[AbstractFile]s. - case x: Traversable[_] => !x.hasDefiniteSize || !isScalaClass(x) || isScalaCompilerClass(x) || isXmlNode(x.getClass) || isXmlMetaData(x.getClass) - // Otherwise, nothing could possibly go wrong - case _ => false - } - - // A variation on inner for maps so they print -> instead of bare tuples - def mapInner(arg: Any): String = arg match { - case (k, v) => inner(k) + " -> " + inner(v) - case _ => inner(arg) - } - - // Special casing Unit arrays, the value class which uses a reference array type. - def arrayToString(x: AnyRef) = { - if (x.getClass.getComponentType == classOf[BoxedUnit]) - 0 until (array_length(x) min maxElements) map (_ => "()") mkString ("Array(", ", ", ")") - else - WrappedArray make x take maxElements map inner mkString ("Array(", ", ", ")") - } - - // The recursively applied attempt to prettify Array printing. - // Note that iterator is used if possible and foreach is used as a - // last resort, because the parallel collections "foreach" in a - // random order even on sequences. - def inner(arg: Any): String = arg match { - case null => "null" - case "" => "\"\"" - case x: String => if (x.head.isWhitespace || x.last.isWhitespace) "\"" + x + "\"" else x - case x if useOwnToString(x) => x.toString - case x: AnyRef if isArray(x) => arrayToString(x) - case x: scala.collection.Map[_, _] => x.iterator take maxElements map mapInner mkString (x.stringPrefix + "(", ", ", ")") - case x: GenIterable[_] => x.iterator take maxElements map inner mkString (x.stringPrefix + "(", ", ", ")") - case x: Traversable[_] => x take maxElements map inner mkString (x.stringPrefix + "(", ", ", ")") - case x: Product1[_] if isTuple(x) => "(" + inner(x._1) + ",)" // that special trailing comma - case x: Product if isTuple(x) => x.productIterator map inner mkString ("(", ",", ")") - case x => x.toString - } - - // The try/catch is defense against iterables which aren't actually designed - // to be iterated, such as some scala.tools.nsc.io.AbstractFile derived classes. - try inner(arg) - catch { - case _: UnsupportedOperationException | _: AssertionError => "" + arg - } - } - - /** stringOf formatted for use in a repl result. */ - def replStringOf(arg: Any, maxElements: Int): String = { - val s = stringOf(arg, maxElements) - val nl = if (s contains "\n") "\n" else "" - - nl + s + "\n" - } - - def box[T](clazz: jClass[T]): jClass[_] = clazz match { - case java.lang.Byte.TYPE => classOf[java.lang.Byte] - case java.lang.Short.TYPE => classOf[java.lang.Short] - case java.lang.Character.TYPE => classOf[java.lang.Character] - case java.lang.Integer.TYPE => classOf[java.lang.Integer] - case java.lang.Long.TYPE => classOf[java.lang.Long] - case java.lang.Float.TYPE => classOf[java.lang.Float] - case java.lang.Double.TYPE => classOf[java.lang.Double] - case java.lang.Void.TYPE => classOf[scala.runtime.BoxedUnit] - case java.lang.Boolean.TYPE => classOf[java.lang.Boolean] - case _ => clazz - } -} diff --git a/scalalib/overrides-2.12.1/scala/collection/immutable/RedBlackTree.scala b/scalalib/overrides-2.12.1/scala/collection/immutable/RedBlackTree.scala deleted file mode 100644 index c4a58f7ed4..0000000000 --- a/scalalib/overrides-2.12.1/scala/collection/immutable/RedBlackTree.scala +++ /dev/null @@ -1,569 +0,0 @@ -/* - * Scala (https://www.scala-lang.org) - * - * Copyright EPFL and Lightbend, Inc. - * - * Licensed under Apache License 2.0 - * (http://www.apache.org/licenses/LICENSE-2.0). - * - * See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - */ - -package scala -package collection -package immutable - -import scala.annotation.tailrec -import scala.annotation.meta.getter - -/** An object containing the RedBlack tree implementation used by for `TreeMaps` and `TreeSets`. - * - * Implementation note: since efficiency is important for data structures this implementation - * uses `null` to represent empty trees. This also means pattern matching cannot - * easily be used. The API represented by the RedBlackTree object tries to hide these - * optimizations behind a reasonably clean API. - * - * @since 2.10 - */ -private[collection] -object RedBlackTree { - - def isEmpty(tree: Tree[_, _]): Boolean = tree eq null - - def contains[A: Ordering](tree: Tree[A, _], x: A): Boolean = lookup(tree, x) ne null - def get[A: Ordering, B](tree: Tree[A, B], x: A): Option[B] = lookup(tree, x) match { - case null => None - case tree => Some(tree.value) - } - - @tailrec - def lookup[A, B](tree: Tree[A, B], x: A)(implicit ordering: Ordering[A]): Tree[A, B] = if (tree eq null) null else { - val cmp = ordering.compare(x, tree.key) - if (cmp < 0) lookup(tree.left, x) - else if (cmp > 0) lookup(tree.right, x) - else tree - } - - def count(tree: Tree[_, _]) = if (tree eq null) 0 else tree.count - /** - * Count all the nodes with keys greater than or equal to the lower bound and less than the upper bound. - * The two bounds are optional. - */ - def countInRange[A](tree: Tree[A, _], from: Option[A], to:Option[A])(implicit ordering: Ordering[A]) : Int = - if (tree eq null) 0 else - (from, to) match { - // with no bounds use this node's count - case (None, None) => tree.count - // if node is less than the lower bound, try the tree on the right, it might be in range - case (Some(lb), _) if ordering.lt(tree.key, lb) => countInRange(tree.right, from, to) - // if node is greater than or equal to the upper bound, try the tree on the left, it might be in range - case (_, Some(ub)) if ordering.gteq(tree.key, ub) => countInRange(tree.left, from, to) - // node is in range so the tree on the left will all be less than the upper bound and the tree on the - // right will all be greater than or equal to the lower bound. So 1 for this node plus - // count the subtrees by stripping off the bounds that we don't need any more - case _ => 1 + countInRange(tree.left, from, None) + countInRange(tree.right, None, to) - - } - def update[A: Ordering, B, B1 >: B](tree: Tree[A, B], k: A, v: B1, overwrite: Boolean): Tree[A, B1] = blacken(upd(tree, k, v, overwrite)) - def delete[A: Ordering, B](tree: Tree[A, B], k: A): Tree[A, B] = blacken(del(tree, k)) - def rangeImpl[A: Ordering, B](tree: Tree[A, B], from: Option[A], until: Option[A]): Tree[A, B] = (from, until) match { - case (Some(from), Some(until)) => this.range(tree, from, until) - case (Some(from), None) => this.from(tree, from) - case (None, Some(until)) => this.until(tree, until) - case (None, None) => tree - } - def range[A: Ordering, B](tree: Tree[A, B], from: A, until: A): Tree[A, B] = blacken(doRange(tree, from, until)) - def from[A: Ordering, B](tree: Tree[A, B], from: A): Tree[A, B] = blacken(doFrom(tree, from)) - def to[A: Ordering, B](tree: Tree[A, B], to: A): Tree[A, B] = blacken(doTo(tree, to)) - def until[A: Ordering, B](tree: Tree[A, B], key: A): Tree[A, B] = blacken(doUntil(tree, key)) - - def drop[A: Ordering, B](tree: Tree[A, B], n: Int): Tree[A, B] = blacken(doDrop(tree, n)) - def take[A: Ordering, B](tree: Tree[A, B], n: Int): Tree[A, B] = blacken(doTake(tree, n)) - def slice[A: Ordering, B](tree: Tree[A, B], from: Int, until: Int): Tree[A, B] = blacken(doSlice(tree, from, until)) - - def smallest[A, B](tree: Tree[A, B]): Tree[A, B] = { - if (tree eq null) throw new NoSuchElementException("empty map") - var result = tree - while (result.left ne null) result = result.left - result - } - def greatest[A, B](tree: Tree[A, B]): Tree[A, B] = { - if (tree eq null) throw new NoSuchElementException("empty map") - var result = tree - while (result.right ne null) result = result.right - result - } - - def foreach[A,B,U](tree:Tree[A,B], f:((A,B)) => U):Unit = if (tree ne null) _foreach(tree,f) - def foreachEntry[A,B,U](tree:Tree[A,B], f:(A,B) => U):Unit = if (tree ne null) _foreachEntry(tree,f) - - private[this] def _foreach[A, B, U](tree: Tree[A, B], f: ((A, B)) => U) { - if (tree.left ne null) _foreach(tree.left, f) - f((tree.key, tree.value)) - if (tree.right ne null) _foreach(tree.right, f) - } - private[this] def _foreachEntry[A, B, U](tree: Tree[A, B], f: (A, B) => U): Unit = { - if (tree.left ne null) _foreachEntry(tree.left, f) - f(tree.key, tree.value) - if (tree.right ne null) _foreachEntry(tree.right, f) - } - - def foreachKey[A, U](tree:Tree[A,_], f: A => U):Unit = if (tree ne null) _foreachKey(tree,f) - - private[this] def _foreachKey[A, U](tree: Tree[A, _], f: A => U) { - if (tree.left ne null) _foreachKey(tree.left, f) - f((tree.key)) - if (tree.right ne null) _foreachKey(tree.right, f) - } - - def iterator[A: Ordering, B](tree: Tree[A, B], start: Option[A] = None): Iterator[(A, B)] = new EntriesIterator(tree, start) - def keysIterator[A: Ordering](tree: Tree[A, _], start: Option[A] = None): Iterator[A] = new KeysIterator(tree, start) - def valuesIterator[A: Ordering, B](tree: Tree[A, B], start: Option[A] = None): Iterator[B] = new ValuesIterator(tree, start) - - @tailrec - def nth[A, B](tree: Tree[A, B], n: Int): Tree[A, B] = { - val count = this.count(tree.left) - if (n < count) nth(tree.left, n) - else if (n > count) nth(tree.right, n - count - 1) - else tree - } - - def isBlack(tree: Tree[_, _]) = (tree eq null) || isBlackTree(tree) - - private[this] def isRedTree(tree: Tree[_, _]) = tree.isInstanceOf[RedTree[_, _]] - private[this] def isBlackTree(tree: Tree[_, _]) = tree.isInstanceOf[BlackTree[_, _]] - - private[this] def blacken[A, B](t: Tree[A, B]): Tree[A, B] = if (t eq null) null else t.black - - private[this] def mkTree[A, B](isBlack: Boolean, k: A, v: B, l: Tree[A, B], r: Tree[A, B]) = - if (isBlack) BlackTree(k, v, l, r) else RedTree(k, v, l, r) - - private[this] def balanceLeft[A, B, B1 >: B](isBlack: Boolean, z: A, zv: B, l: Tree[A, B1], d: Tree[A, B1]): Tree[A, B1] = { - if (isRedTree(l) && isRedTree(l.left)) - RedTree(l.key, l.value, BlackTree(l.left.key, l.left.value, l.left.left, l.left.right), BlackTree(z, zv, l.right, d)) - else if (isRedTree(l) && isRedTree(l.right)) - RedTree(l.right.key, l.right.value, BlackTree(l.key, l.value, l.left, l.right.left), BlackTree(z, zv, l.right.right, d)) - else - mkTree(isBlack, z, zv, l, d) - } - private[this] def balanceRight[A, B, B1 >: B](isBlack: Boolean, x: A, xv: B, a: Tree[A, B1], r: Tree[A, B1]): Tree[A, B1] = { - if (isRedTree(r) && isRedTree(r.left)) - RedTree(r.left.key, r.left.value, BlackTree(x, xv, a, r.left.left), BlackTree(r.key, r.value, r.left.right, r.right)) - else if (isRedTree(r) && isRedTree(r.right)) - RedTree(r.key, r.value, BlackTree(x, xv, a, r.left), BlackTree(r.right.key, r.right.value, r.right.left, r.right.right)) - else - mkTree(isBlack, x, xv, a, r) - } - private[this] def upd[A, B, B1 >: B](tree: Tree[A, B], k: A, v: B1, overwrite: Boolean)(implicit ordering: Ordering[A]): Tree[A, B1] = if (tree eq null) { - RedTree(k, v, null, null) - } else { - val cmp = ordering.compare(k, tree.key) - if (cmp < 0) balanceLeft(isBlackTree(tree), tree.key, tree.value, upd(tree.left, k, v, overwrite), tree.right) - else if (cmp > 0) balanceRight(isBlackTree(tree), tree.key, tree.value, tree.left, upd(tree.right, k, v, overwrite)) - else if (overwrite || k != tree.key) mkTree(isBlackTree(tree), k, v, tree.left, tree.right) - else tree - } - private[this] def updNth[A, B, B1 >: B](tree: Tree[A, B], idx: Int, k: A, v: B1, overwrite: Boolean): Tree[A, B1] = if (tree eq null) { - RedTree(k, v, null, null) - } else { - val rank = count(tree.left) + 1 - if (idx < rank) balanceLeft(isBlackTree(tree), tree.key, tree.value, updNth(tree.left, idx, k, v, overwrite), tree.right) - else if (idx > rank) balanceRight(isBlackTree(tree), tree.key, tree.value, tree.left, updNth(tree.right, idx - rank, k, v, overwrite)) - else if (overwrite) mkTree(isBlackTree(tree), k, v, tree.left, tree.right) - else tree - } - - /* Based on Stefan Kahrs' Haskell version of Okasaki's Red&Black Trees - * Constructing Red-Black Trees, Ralf Hinze: [[http://www.cs.ox.ac.uk/ralf.hinze/publications/WAAAPL99b.ps.gz]] - * Red-Black Trees in a Functional Setting, Chris Okasaki: [[https://wiki.rice.edu/confluence/download/attachments/2761212/Okasaki-Red-Black.pdf]] */ - private[this] def del[A, B](tree: Tree[A, B], k: A)(implicit ordering: Ordering[A]): Tree[A, B] = if (tree eq null) null else { - def balance(x: A, xv: B, tl: Tree[A, B], tr: Tree[A, B]) = if (isRedTree(tl)) { - if (isRedTree(tr)) { - RedTree(x, xv, tl.black, tr.black) - } else if (isRedTree(tl.left)) { - RedTree(tl.key, tl.value, tl.left.black, BlackTree(x, xv, tl.right, tr)) - } else if (isRedTree(tl.right)) { - RedTree(tl.right.key, tl.right.value, BlackTree(tl.key, tl.value, tl.left, tl.right.left), BlackTree(x, xv, tl.right.right, tr)) - } else { - BlackTree(x, xv, tl, tr) - } - } else if (isRedTree(tr)) { - if (isRedTree(tr.right)) { - RedTree(tr.key, tr.value, BlackTree(x, xv, tl, tr.left), tr.right.black) - } else if (isRedTree(tr.left)) { - RedTree(tr.left.key, tr.left.value, BlackTree(x, xv, tl, tr.left.left), BlackTree(tr.key, tr.value, tr.left.right, tr.right)) - } else { - BlackTree(x, xv, tl, tr) - } - } else { - BlackTree(x, xv, tl, tr) - } - def subl(t: Tree[A, B]) = - if (t.isInstanceOf[BlackTree[_, _]]) t.red - else throw new IllegalStateException("Defect: invariance violation; expected black, got "+t) - - def balLeft(x: A, xv: B, tl: Tree[A, B], tr: Tree[A, B]) = if (isRedTree(tl)) { - RedTree(x, xv, tl.black, tr) - } else if (isBlackTree(tr)) { - balance(x, xv, tl, tr.red) - } else if (isRedTree(tr) && isBlackTree(tr.left)) { - RedTree(tr.left.key, tr.left.value, BlackTree(x, xv, tl, tr.left.left), balance(tr.key, tr.value, tr.left.right, subl(tr.right))) - } else { - throw new IllegalStateException("Defect: invariance violation") - } - def balRight(x: A, xv: B, tl: Tree[A, B], tr: Tree[A, B]) = if (isRedTree(tr)) { - RedTree(x, xv, tl, tr.black) - } else if (isBlackTree(tl)) { - balance(x, xv, tl.red, tr) - } else if (isRedTree(tl) && isBlackTree(tl.right)) { - RedTree(tl.right.key, tl.right.value, balance(tl.key, tl.value, subl(tl.left), tl.right.left), BlackTree(x, xv, tl.right.right, tr)) - } else { - throw new IllegalStateException("Defect: invariance violation") - } - def delLeft = if (isBlackTree(tree.left)) balLeft(tree.key, tree.value, del(tree.left, k), tree.right) else RedTree(tree.key, tree.value, del(tree.left, k), tree.right) - def delRight = if (isBlackTree(tree.right)) balRight(tree.key, tree.value, tree.left, del(tree.right, k)) else RedTree(tree.key, tree.value, tree.left, del(tree.right, k)) - def append(tl: Tree[A, B], tr: Tree[A, B]): Tree[A, B] = if (tl eq null) { - tr - } else if (tr eq null) { - tl - } else if (isRedTree(tl) && isRedTree(tr)) { - val bc = append(tl.right, tr.left) - if (isRedTree(bc)) { - RedTree(bc.key, bc.value, RedTree(tl.key, tl.value, tl.left, bc.left), RedTree(tr.key, tr.value, bc.right, tr.right)) - } else { - RedTree(tl.key, tl.value, tl.left, RedTree(tr.key, tr.value, bc, tr.right)) - } - } else if (isBlackTree(tl) && isBlackTree(tr)) { - val bc = append(tl.right, tr.left) - if (isRedTree(bc)) { - RedTree(bc.key, bc.value, BlackTree(tl.key, tl.value, tl.left, bc.left), BlackTree(tr.key, tr.value, bc.right, tr.right)) - } else { - balLeft(tl.key, tl.value, tl.left, BlackTree(tr.key, tr.value, bc, tr.right)) - } - } else if (isRedTree(tr)) { - RedTree(tr.key, tr.value, append(tl, tr.left), tr.right) - } else if (isRedTree(tl)) { - RedTree(tl.key, tl.value, tl.left, append(tl.right, tr)) - } else { - throw new IllegalStateException("unmatched tree on append: " + tl + ", " + tr) - } - - val cmp = ordering.compare(k, tree.key) - if (cmp < 0) delLeft - else if (cmp > 0) delRight - else append(tree.left, tree.right) - } - - private[this] def doFrom[A, B](tree: Tree[A, B], from: A)(implicit ordering: Ordering[A]): Tree[A, B] = { - if (tree eq null) return null - if (ordering.lt(tree.key, from)) return doFrom(tree.right, from) - val newLeft = doFrom(tree.left, from) - if (newLeft eq tree.left) tree - else if (newLeft eq null) upd(tree.right, tree.key, tree.value, overwrite = false) - else rebalance(tree, newLeft, tree.right) - } - private[this] def doTo[A, B](tree: Tree[A, B], to: A)(implicit ordering: Ordering[A]): Tree[A, B] = { - if (tree eq null) return null - if (ordering.lt(to, tree.key)) return doTo(tree.left, to) - val newRight = doTo(tree.right, to) - if (newRight eq tree.right) tree - else if (newRight eq null) upd(tree.left, tree.key, tree.value, overwrite = false) - else rebalance(tree, tree.left, newRight) - } - private[this] def doUntil[A, B](tree: Tree[A, B], until: A)(implicit ordering: Ordering[A]): Tree[A, B] = { - if (tree eq null) return null - if (ordering.lteq(until, tree.key)) return doUntil(tree.left, until) - val newRight = doUntil(tree.right, until) - if (newRight eq tree.right) tree - else if (newRight eq null) upd(tree.left, tree.key, tree.value, overwrite = false) - else rebalance(tree, tree.left, newRight) - } - private[this] def doRange[A, B](tree: Tree[A, B], from: A, until: A)(implicit ordering: Ordering[A]): Tree[A, B] = { - if (tree eq null) return null - if (ordering.lt(tree.key, from)) return doRange(tree.right, from, until) - if (ordering.lteq(until, tree.key)) return doRange(tree.left, from, until) - val newLeft = doFrom(tree.left, from) - val newRight = doUntil(tree.right, until) - if ((newLeft eq tree.left) && (newRight eq tree.right)) tree - else if (newLeft eq null) upd(newRight, tree.key, tree.value, overwrite = false) - else if (newRight eq null) upd(newLeft, tree.key, tree.value, overwrite = false) - else rebalance(tree, newLeft, newRight) - } - - private[this] def doDrop[A, B](tree: Tree[A, B], n: Int): Tree[A, B] = { - if (n <= 0) return tree - if (n >= this.count(tree)) return null - val count = this.count(tree.left) - if (n > count) return doDrop(tree.right, n - count - 1) - val newLeft = doDrop(tree.left, n) - if (newLeft eq tree.left) tree - else if (newLeft eq null) updNth(tree.right, n - count - 1, tree.key, tree.value, overwrite = false) - else rebalance(tree, newLeft, tree.right) - } - private[this] def doTake[A, B](tree: Tree[A, B], n: Int): Tree[A, B] = { - if (n <= 0) return null - if (n >= this.count(tree)) return tree - val count = this.count(tree.left) - if (n <= count) return doTake(tree.left, n) - val newRight = doTake(tree.right, n - count - 1) - if (newRight eq tree.right) tree - else if (newRight eq null) updNth(tree.left, n, tree.key, tree.value, overwrite = false) - else rebalance(tree, tree.left, newRight) - } - private[this] def doSlice[A, B](tree: Tree[A, B], from: Int, until: Int): Tree[A, B] = { - if (tree eq null) return null - val count = this.count(tree.left) - if (from > count) return doSlice(tree.right, from - count - 1, until - count - 1) - if (until <= count) return doSlice(tree.left, from, until) - val newLeft = doDrop(tree.left, from) - val newRight = doTake(tree.right, until - count - 1) - if ((newLeft eq tree.left) && (newRight eq tree.right)) tree - else if (newLeft eq null) updNth(newRight, from - count - 1, tree.key, tree.value, overwrite = false) - else if (newRight eq null) updNth(newLeft, until, tree.key, tree.value, overwrite = false) - else rebalance(tree, newLeft, newRight) - } - - // The zipper returned might have been traversed left-most (always the left child) - // or right-most (always the right child). Left trees are traversed right-most, - // and right trees are traversed leftmost. - - // Returns the zipper for the side with deepest black nodes depth, a flag - // indicating whether the trees were unbalanced at all, and a flag indicating - // whether the zipper was traversed left-most or right-most. - - // If the trees were balanced, returns an empty zipper - private[this] def compareDepth[A, B](left: Tree[A, B], right: Tree[A, B]): (NList[Tree[A, B]], Boolean, Boolean, Int) = { - import NList.cons - // Once a side is found to be deeper, unzip it to the bottom - def unzip(zipper: NList[Tree[A, B]], leftMost: Boolean): NList[Tree[A, B]] = { - val next = if (leftMost) zipper.head.left else zipper.head.right - if (next eq null) zipper - else unzip(cons(next, zipper), leftMost) - } - - // Unzip left tree on the rightmost side and right tree on the leftmost side until one is - // found to be deeper, or the bottom is reached - def unzipBoth(left: Tree[A, B], - right: Tree[A, B], - leftZipper: NList[Tree[A, B]], - rightZipper: NList[Tree[A, B]], - smallerDepth: Int): (NList[Tree[A, B]], Boolean, Boolean, Int) = { - if (isBlackTree(left) && isBlackTree(right)) { - unzipBoth(left.right, right.left, cons(left, leftZipper), cons(right, rightZipper), smallerDepth + 1) - } else if (isRedTree(left) && isRedTree(right)) { - unzipBoth(left.right, right.left, cons(left, leftZipper), cons(right, rightZipper), smallerDepth) - } else if (isRedTree(right)) { - unzipBoth(left, right.left, leftZipper, cons(right, rightZipper), smallerDepth) - } else if (isRedTree(left)) { - unzipBoth(left.right, right, cons(left, leftZipper), rightZipper, smallerDepth) - } else if ((left eq null) && (right eq null)) { - (null, true, false, smallerDepth) - } else if ((left eq null) && isBlackTree(right)) { - val leftMost = true - (unzip(cons(right, rightZipper), leftMost), false, leftMost, smallerDepth) - } else if (isBlackTree(left) && (right eq null)) { - val leftMost = false - (unzip(cons(left, leftZipper), leftMost), false, leftMost, smallerDepth) - } else { - throw new IllegalStateException("unmatched trees in unzip: " + left + ", " + right) - } - } - unzipBoth(left, right, null, null, 0) - } - - private[this] def rebalance[A, B](tree: Tree[A, B], newLeft: Tree[A, B], newRight: Tree[A, B]) = { - // This is like drop(n-1), but only counting black nodes - @tailrec - def findDepth(zipper: NList[Tree[A, B]], depth: Int): NList[Tree[A, B]] = - if (zipper eq null) { - throw new IllegalStateException("Defect: unexpected empty zipper while computing range") - } else if (isBlackTree(zipper.head)) { - if (depth == 1) zipper else findDepth(zipper.tail, depth - 1) - } else { - findDepth(zipper.tail, depth) - } - - // Blackening the smaller tree avoids balancing problems on union; - // this can't be done later, though, or it would change the result of compareDepth - val blkNewLeft = blacken(newLeft) - val blkNewRight = blacken(newRight) - val (zipper, levelled, leftMost, smallerDepth) = compareDepth(blkNewLeft, blkNewRight) - - if (levelled) { - BlackTree(tree.key, tree.value, blkNewLeft, blkNewRight) - } else { - val zipFrom = findDepth(zipper, smallerDepth) - val union = if (leftMost) { - RedTree(tree.key, tree.value, blkNewLeft, zipFrom.head) - } else { - RedTree(tree.key, tree.value, zipFrom.head, blkNewRight) - } - val zippedTree = NList.foldLeft(zipFrom.tail, union: Tree[A, B]) { (tree, node) => - if (leftMost) - balanceLeft(isBlackTree(node), node.key, node.value, tree, node.right) - else - balanceRight(isBlackTree(node), node.key, node.value, node.left, tree) - } - zippedTree - } - } - - // Null optimized list implementation for tree rebalancing. null presents Nil. - private[this] final class NList[A](val head: A, val tail: NList[A]) - - private[this] final object NList { - - def cons[B](x: B, xs: NList[B]): NList[B] = new NList(x, xs) - - def foldLeft[A, B](xs: NList[A], z: B)(op: (B, A) => B): B = { - var acc = z - var these = xs - while (these ne null) { - acc = op(acc, these.head) - these = these.tail - } - acc - } - - } - - /* - * Forcing direct fields access using the @inline annotation helps speed up - * various operations (especially smallest/greatest and update/delete). - * - * Unfortunately the direct field access is not guaranteed to work (but - * works on the current implementation of the Scala compiler). - * - * An alternative is to implement the these classes using plain old Java code... - */ - sealed abstract class Tree[A, +B]( - @(inline @getter) final val key: A, - @(inline @getter) final val value: B, - @(inline @getter) final val left: Tree[A, B], - @(inline @getter) final val right: Tree[A, B]) - extends Serializable { - @(inline @getter) final val count: Int = 1 + RedBlackTree.count(left) + RedBlackTree.count(right) - def black: Tree[A, B] - def red: Tree[A, B] - } - final class RedTree[A, +B](key: A, - value: B, - left: Tree[A, B], - right: Tree[A, B]) extends Tree[A, B](key, value, left, right) { - override def black: Tree[A, B] = BlackTree(key, value, left, right) - override def red: Tree[A, B] = this - override def toString: String = "RedTree(" + key + ", " + value + ", " + left + ", " + right + ")" - } - final class BlackTree[A, +B](key: A, - value: B, - left: Tree[A, B], - right: Tree[A, B]) extends Tree[A, B](key, value, left, right) { - override def black: Tree[A, B] = this - override def red: Tree[A, B] = RedTree(key, value, left, right) - override def toString: String = "BlackTree(" + key + ", " + value + ", " + left + ", " + right + ")" - } - - object RedTree { - @inline def apply[A, B](key: A, value: B, left: Tree[A, B], right: Tree[A, B]) = new RedTree(key, value, left, right) - def unapply[A, B](t: RedTree[A, B]) = Some((t.key, t.value, t.left, t.right)) - } - object BlackTree { - @inline def apply[A, B](key: A, value: B, left: Tree[A, B], right: Tree[A, B]) = new BlackTree(key, value, left, right) - def unapply[A, B](t: BlackTree[A, B]) = Some((t.key, t.value, t.left, t.right)) - } - - private[this] abstract class TreeIterator[A, B, R](root: Tree[A, B], start: Option[A])(implicit ordering: Ordering[A]) extends Iterator[R] { - protected[this] def nextResult(tree: Tree[A, B]): R - - override def hasNext: Boolean = lookahead ne null - - override def next: R = lookahead match { - case null => - throw new NoSuchElementException("next on empty iterator") - case tree => - lookahead = findLeftMostOrPopOnEmpty(goRight(tree)) - nextResult(tree) - } - - @tailrec - private[this] def findLeftMostOrPopOnEmpty(tree: Tree[A, B]): Tree[A, B] = - if (tree eq null) popNext() - else if (tree.left eq null) tree - else findLeftMostOrPopOnEmpty(goLeft(tree)) - - private[this] def pushNext(tree: Tree[A, B]) { - try { - stackOfNexts(index) = tree - index += 1 - } catch { - case _: ArrayIndexOutOfBoundsException => - /* - * Either the tree became unbalanced or we calculated the maximum height incorrectly. - * To avoid crashing the iterator we expand the path array. Obviously this should never - * happen... - * - * An exception handler is used instead of an if-condition to optimize the normal path. - * This makes a large difference in iteration speed! - */ - assert(index >= stackOfNexts.length) - stackOfNexts :+= null - pushNext(tree) - } - } - private[this] def popNext(): Tree[A, B] = if (index == 0) null else { - index -= 1 - stackOfNexts(index) - } - - private[this] var stackOfNexts = if (root eq null) null else { - /* - * According to "Ralf Hinze. Constructing red-black trees" [http://www.cs.ox.ac.uk/ralf.hinze/publications/#P5] - * the maximum height of a red-black tree is 2*log_2(n + 2) - 2. - * - * According to {@see Integer#numberOfLeadingZeros} ceil(log_2(n)) = (32 - Integer.numberOfLeadingZeros(n - 1)) - * - * We also don't store the deepest nodes in the path so the maximum path length is further reduced by one. - */ - val maximumHeight = 2 * (32 - Integer.numberOfLeadingZeros(root.count + 2 - 1)) - 2 - new Array[Tree[A, B]](maximumHeight) - } - private[this] var index = 0 - private[this] var lookahead: Tree[A, B] = start map startFrom getOrElse findLeftMostOrPopOnEmpty(root) - - /** - * Find the leftmost subtree whose key is equal to the given key, or if no such thing, - * the leftmost subtree with the key that would be "next" after it according - * to the ordering. Along the way build up the iterator's path stack so that "next" - * functionality works. - */ - private[this] def startFrom(key: A) : Tree[A,B] = if (root eq null) null else { - @tailrec def find(tree: Tree[A, B]): Tree[A, B] = - if (tree eq null) popNext() - else find( - if (ordering.lteq(key, tree.key)) goLeft(tree) - else goRight(tree) - ) - find(root) - } - - private[this] def goLeft(tree: Tree[A, B]) = { - pushNext(tree) - tree.left - } - - private[this] def goRight(tree: Tree[A, B]) = tree.right - } - - private[this] class EntriesIterator[A: Ordering, B](tree: Tree[A, B], focus: Option[A]) extends TreeIterator[A, B, (A, B)](tree, focus) { - override def nextResult(tree: Tree[A, B]) = (tree.key, tree.value) - } - - private[this] class KeysIterator[A: Ordering, B](tree: Tree[A, B], focus: Option[A]) extends TreeIterator[A, B, A](tree, focus) { - override def nextResult(tree: Tree[A, B]) = tree.key - } - - private[this] class ValuesIterator[A: Ordering, B](tree: Tree[A, B], focus: Option[A]) extends TreeIterator[A, B, B](tree, focus) { - override def nextResult(tree: Tree[A, B]) = tree.value - } -} diff --git a/scripts/publish.sh b/scripts/publish.sh index 03b7e83c08..6a6ac44d00 100755 --- a/scripts/publish.sh +++ b/scripts/publish.sh @@ -7,7 +7,7 @@ else CMD="echo sbt" fi -SUFFIXES="2_11 2_12 2_13" +SUFFIXES="2_12 2_13" JAVA_LIBS="javalibintf javalib" COMPILER="compiler jUnitPlugin" 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 43af54be1a..cbd1a4f46d 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 @@ -27,6 +27,7 @@ private[utils] object BuildInfo { final val compliantArrayIndexOutOfBounds = false final val compliantArrayStores = false final val compliantNegativeArraySizes = false + final val compliantNullPointers = false final val compliantStringIndexOutOfBounds = false final val compliantModuleInit = false final val strictFloats = 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 34dc7063c8..cbf49e2d92 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 @@ -34,6 +34,8 @@ object Platform { final val executingInJVMOnLowerThanJDK16 = false + final val executingInJVMOnLowerThanJDK17 = false + def executingInNodeJS: Boolean = { js.typeOf(js.Dynamic.global.process) != "undefined" && !js.isUndefined(js.Dynamic.global.process.release) && @@ -80,6 +82,8 @@ object Platform { def hasCompliantNegativeArraySizes: Boolean = BuildInfo.compliantNegativeArraySizes + def hasCompliantNullPointers: Boolean = BuildInfo.compliantNullPointers + def hasCompliantStringIndexOutOfBounds: Boolean = BuildInfo.compliantStringIndexOutOfBounds 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 b00a22cedc..f1ea0425b7 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 @@ -219,8 +219,7 @@ class SJSDynamicImportTest { Future.sequence(List(a, b.toFuture)) } - // Future#flatten, but that's not available on 2.11. - for (i <- promise.toFuture; _ <- i) yield { + promise.toFuture.flatten.map { _ => assertEquals(3, x) } } diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/DefaultMethodsJSTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/DefaultMethodsJSTest.scala index 52b1d05093..9e9c3d65df 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/DefaultMethodsJSTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/DefaultMethodsJSTest.scala @@ -15,8 +15,6 @@ package org.scalajs.testsuite.compiler import org.junit.Test import org.junit.Assert._ -import scala.scalajs.js.annotation.JavaDefaultMethod - class DefaultMethodsJSTest { import DefaultMethodsJSTest._ @@ -35,7 +33,6 @@ object DefaultMethodsJSTest { trait SimpleInterfaceWithDefault { def value: Int - @JavaDefaultMethod def foo(x: Int): Int = value + x } } diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/javalib/lang/ClassJSTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/javalib/lang/ClassJSTest.scala index 53dca82540..b9af6df1b5 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/javalib/lang/ClassJSTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/javalib/lang/ClassJSTest.scala @@ -14,11 +14,27 @@ package org.scalajs.testsuite.javalib.lang import org.junit.Test import org.junit.Assert._ +import org.junit.Assume._ import scala.scalajs.js +import org.scalajs.testsuite.utils.AssertThrows.assertThrows +import org.scalajs.testsuite.utils.Platform._ + class ClassJSTest { + @Test def getClassGetNameForJSObjects(): Unit = { + // getClass().getName() is subject to optimizations + + assumeTrue("Assuming compliant null pointers", hasCompliantNullPointers) + + assertThrows(classOf[NullPointerException], new js.Object().getClass().getName()) + + @noinline def newJSObject(): Any = new js.Object() + + assertThrows(classOf[NullPointerException], newJSObject().getClass().getName()) + } + @Test def isAssignableFrom(): Unit = { /* isAssignableFrom should respect the JVM rules even for JS types, * although isInstance doesn't. The reason is that it provides reflection diff --git a/test-suite/js/src/test/require-sam/org/scalajs/testsuite/jsinterop/ArraySAMTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/ArraySAMTest.scala similarity index 75% rename from test-suite/js/src/test/require-sam/org/scalajs/testsuite/jsinterop/ArraySAMTest.scala rename to test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/ArraySAMTest.scala index 822e901c45..d3c292dd69 100644 --- a/test-suite/js/src/test/require-sam/org/scalajs/testsuite/jsinterop/ArraySAMTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/ArraySAMTest.scala @@ -1,10 +1,15 @@ -/* __ *\ -** ________ ___ / / ___ __ ____ Scala.js Test Suite ** -** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** -** /____/\___/_/ |_/____/_/ | |__/ /____/ ** -** |/____/ ** -\* */ +/* + * Scala.js (https://www.scala-js.org/) + * + * Copyright EPFL. + * + * Licensed under Apache License 2.0 + * (https://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + package org.scalajs.testsuite.jsinterop import scala.language.implicitConversions diff --git a/test-suite/js/src/test/require-sam/org/scalajs/testsuite/jsinterop/CustomJSFunctionTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/CustomJSFunctionTest.scala similarity index 88% rename from test-suite/js/src/test/require-sam/org/scalajs/testsuite/jsinterop/CustomJSFunctionTest.scala rename to test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/CustomJSFunctionTest.scala index d4a2cb6328..e7afcdb337 100644 --- a/test-suite/js/src/test/require-sam/org/scalajs/testsuite/jsinterop/CustomJSFunctionTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/CustomJSFunctionTest.scala @@ -1,10 +1,14 @@ -/* __ *\ -** ________ ___ / / ___ __ ____ Scala.js Test Suite ** -** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2018, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** -** /____/\___/_/ |_/____/_/ | |__/ /____/ ** -** |/____/ ** -\* */ +/* + * Scala.js (https://www.scala-js.org/) + * + * Copyright EPFL. + * + * Licensed under Apache License 2.0 + * (https://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ package org.scalajs.testsuite.jsinterop 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 3baa2575c6..2d2ce36b22 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 @@ -12,6 +12,8 @@ package org.scalajs.testsuite.jsinterop +import scala.language.higherKinds + import scala.scalajs.js import scala.scalajs.js.annotation._ import scala.scalajs.js.Dynamic.global @@ -120,6 +122,313 @@ class ExportsTest { assertEquals(100, foo.foo()) } + @Test def exportsForNestedClassesInClass(): Unit = { + class A(x: Int) { + @JSExport + class Nested(y: Int) { + @JSExport + def this() = this(2) + + @JSExport + def witness = x + y + } + + @JSExport + class Nested2(y: Int) extends js.Object { + def witness = x + y + } + } + + val scalaA = new A(2) + val jsA = scalaA.asInstanceOf[js.Dynamic] + + val n0 = jsA.Nested(3) + assertTrue(n0.isInstanceOf[scalaA.Nested]) + assertEquals(5, n0.witness) + + val n1 = jsA.Nested() + assertTrue(n1.isInstanceOf[scalaA.Nested]) + assertEquals(4, n1.witness) + + val n2 = js.Dynamic.newInstance(jsA.Nested2)(4) + assertTrue(n2.isInstanceOf[scalaA.Nested2]) + assertEquals(6, n2.witness) + } + + @Test def exportsForNestedClassesInTrait(): Unit = { + trait A { + val x: Int + + @JSExport + class Nested(y: Int) { + @JSExport + def this() = this(2) + + @JSExport + def witness = x + y + } + + @JSExport + class Nested2(y: Int) extends js.Object { + def witness = x + y + } + } + + val scalaA = new A { val x = 2 } + val jsA = scalaA.asInstanceOf[js.Dynamic] + + val n0 = jsA.Nested(3) + assertTrue(n0.isInstanceOf[scalaA.Nested]) + assertEquals(5, n0.witness) + + val n1 = jsA.Nested() + assertTrue(n1.isInstanceOf[scalaA.Nested]) + assertEquals(4, n1.witness) + + val n2 = js.Dynamic.newInstance(jsA.Nested2)(4) + assertTrue(n2.isInstanceOf[scalaA.Nested2]) + assertEquals(6, n2.witness) + } + + @Test def exportsForNestedClassesInObject(): Unit = { + object A { + val x = 2 + + @JSExport + class Nested(y: Int) { + @JSExport + def this() = this(2) + + @JSExport + def witness = x + y + } + + @JSExport + class Nested2(y: Int) extends js.Object { + def witness = x + y + } + } + + val jsA = A.asInstanceOf[js.Dynamic] + + val n0 = jsA.Nested(3) + assertTrue(n0.isInstanceOf[A.Nested]) + assertEquals(5, n0.witness) + + val n1 = jsA.Nested() + assertTrue(n1.isInstanceOf[A.Nested]) + assertEquals(4, n1.witness) + + val n2 = js.Dynamic.newInstance(jsA.Nested2)(4) + assertTrue(n2.isInstanceOf[A.Nested2]) + assertEquals(6, n2.witness) + } + + @Test def exportsForNestedClassesInStaticObject(): Unit = { + val jsObj = StaticObjectWithNestedClasses.asInstanceOf[js.Dynamic] + + val n0 = jsObj.Nested(3) + assertTrue(n0.isInstanceOf[StaticObjectWithNestedClasses.Nested]) + assertEquals(5, n0.witness) + + val n1 = jsObj.Nested() + assertTrue(n1.isInstanceOf[StaticObjectWithNestedClasses.Nested]) + assertEquals(4, n1.witness) + + val n2 = js.Dynamic.newInstance(jsObj.Nested2)(4) + assertTrue(n2.isInstanceOf[StaticObjectWithNestedClasses.Nested2]) + assertEquals(6, n2.witness) + } + + @Test def exportsForNestedGenericClasses(): Unit = { + class A[A](x: A) { + @JSExport + class Nested[B](y: B) { + @JSExport + def witness: (A, B) = (x, y) + } + } + + val scalaA = new A("foo") + val jsA = scalaA.asInstanceOf[js.Dynamic] + + val n0 = jsA.Nested(3) + assertTrue(n0.isInstanceOf[scalaA.Nested[_]]) + assertEquals(("foo", 3), n0.witness) + + val n1 = jsA.Nested("bar") + assertTrue(n1.isInstanceOf[scalaA.Nested[_]]) + assertEquals(("foo", "bar"), n1.witness) + } + + @Test def exportsForNestedGenericJSClasses(): Unit = { + class A[A](x: A) { + @JSExport + class Nested[B](y: B) extends js.Object { + def witness: (A, B) = (x, y) + } + } + + val scalaA = new A("foo") + val jsA = scalaA.asInstanceOf[js.Dynamic] + + val n0 = js.Dynamic.newInstance(jsA.Nested)(3) + assertTrue(n0.isInstanceOf[scalaA.Nested[_]]) + assertEquals(("foo", 3), n0.witness) + + val n1 = js.Dynamic.newInstance(jsA.Nested)("bar") + assertTrue(n1.isInstanceOf[scalaA.Nested[_]]) + assertEquals(("foo", "bar"), n1.witness) + } + + @Test def exportsForNestedAbstractJSClasses(): Unit = { + class A(x: String) { + @JSExport + abstract class Nested(y: String) extends js.Object { + def foo(): String + def witness: String = s"$x | $y | ${foo()}" + } + } + + val scalaA = new A("outer") + val jsA = scalaA.asInstanceOf[js.Dynamic] + + val body = if (useECMAScript2015Semantics) { + """ + class SubClass extends constr { + constructor(x) { + super(x + " from super"); + } + foo() { + return "foo result"; + } + } + 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) + .asInstanceOf[js.Function1[js.Dynamic, js.Dynamic]] + val subclass = subclassFun(jsA.Nested) + val obj = js.Dynamic.newInstance(subclass)("inner") + + assertEquals("outer | inner from super | foo result", obj.witness) + } + + @Test def exportsForNestedObjectsInClass(): Unit = { + class Foo(x: Int) { + @JSExport + object obj { + @JSExport + def witness = x + 1 + } + + @JSExport + object jsObj extends js.Object { + def witness = x + 1 + } + } + + val scalaFoo0 = new Foo(0) + val scalaFoo1 = new Foo(1) + + val foo0 = scalaFoo0.asInstanceOf[js.Dynamic] + val foo1 = scalaFoo1.asInstanceOf[js.Dynamic] + + assertSame(scalaFoo0.obj, foo0.obj) + assertSame(scalaFoo1.obj, foo1.obj) + assertNotSame(foo0.obj, foo1.obj) + assertEquals(1, foo0.obj.witness) + assertEquals(2, foo1.obj.witness) + + assertSame(scalaFoo0.jsObj, foo0.jsObj) + assertSame(scalaFoo1.jsObj, foo1.jsObj) + assertNotSame(foo0.jsObj, foo1.jsObj) + assertEquals(1, foo0.jsObj.witness) + assertEquals(2, foo1.jsObj.witness) + } + + @Test def exportsForNestedObjectsInTrait(): Unit = { + trait Foo { + val x: Int + + @JSExport + object obj { + @JSExport + def witness = x + 1 + } + + @JSExport + object jsObj extends js.Object { + def witness = x + 1 + } + } + + val scalaFoo0 = new Foo { val x = 0 } + val scalaFoo1 = new Foo { val x = 1 } + + val foo0 = scalaFoo0.asInstanceOf[js.Dynamic] + val foo1 = scalaFoo1.asInstanceOf[js.Dynamic] + + assertSame(scalaFoo0.obj, foo0.obj) + assertSame(scalaFoo1.obj, foo1.obj) + assertNotSame(foo0.obj, foo1.obj) + assertEquals(1, foo0.obj.witness) + assertEquals(2, foo1.obj.witness) + + assertSame(scalaFoo0.jsObj, foo0.jsObj) + assertSame(scalaFoo1.jsObj, foo1.jsObj) + assertNotSame(foo0.jsObj, foo1.jsObj) + assertEquals(1, foo0.jsObj.witness) + assertEquals(2, foo1.jsObj.witness) + } + + @Test def exportsForNestedObjectsInObject(): Unit = { + object Foo { + val x: Int = 1 + + @JSExport + object obj { + @JSExport + def witness = x + 1 + } + + @JSExport + object jsObj extends js.Object { + def witness = x + 1 + } + } + + val foo = Foo.asInstanceOf[js.Dynamic] + + assertSame(Foo.obj, foo.obj) + assertEquals(2, foo.obj.witness) + + assertSame(Foo.jsObj, foo.jsObj) + assertEquals(2, foo.jsObj.witness) + } + + @Test def exportsForNestedObjectsInStaticObject(): Unit = { + val foo = StaticObjectWithNestedObjects.asInstanceOf[js.Dynamic] + + assertSame(StaticObjectWithNestedObjects.obj, foo.obj) + assertEquals(2, foo.obj.witness) + + assertSame(StaticObjectWithNestedObjects.jsObj, foo.jsObj) + assertEquals(2, foo.jsObj.witness) + } + @Test def exportsForPropertiesWithImplicitName(): Unit = { class Foo { private[this] var myY: String = "hello" @@ -135,7 +444,8 @@ class ExportsTest { def y_=(v: String): Unit = myY = v + " set" } - val foo = (new Foo).asInstanceOf[js.Dynamic] + val scalaFoo = new Foo + val foo = scalaFoo.asInstanceOf[js.Dynamic] assertEquals("number", js.typeOf(foo.answer)) assertEquals(42, foo.answer) assertEquals(3, foo.x) @@ -254,6 +564,48 @@ class ExportsTest { assertEquals(7, bar.y) } + @Test def exportsForAbstractClassPropertiesImplementedWithObject(): Unit = { + abstract class Foo { + @JSExport + def x: js.Object + } + + class Bar extends Foo { + object x extends js.Object { + val y = 1 + } + } + + val bar = (new Bar).asInstanceOf[js.Dynamic] + assertEquals(1, bar.x.y) + } + + @Test def exportsForTraitPropertiesImplementedWithObject(): Unit = { + trait Foo { + @JSExport + def x: js.Object + } + + class Bar extends Foo { + object x extends js.Object { + val y = 1 + } + } + + val bar = (new Bar).asInstanceOf[js.Dynamic] + assertEquals(1, bar.x.y) + } + + @Test def exportsForAbstractClassPropertiesImplementedWithStaticObject(): Unit = { + val bar = StaticObjectWithObjectForExportFromAbstractClass.asInstanceOf[js.Dynamic] + assertEquals(1, bar.x.y) + } + + @Test def exportsForTraitPropertiesImplementedWithStaticObject(): Unit = { + val bar = StaticObjectWithObjectForExportFromTrait.asInstanceOf[js.Dynamic] + assertEquals(1, bar.x.y) + } + @Test def readonlyProperties(): Unit = { class Foo { @JSExport @@ -442,6 +794,19 @@ class ExportsTest { assertEquals(5, foo.defArg(5)) } + @Test def exportsForHigherKinds(): Unit = { + class HK { + /* Probably there's no real use case for this + * but make sure it doesn't crash the compiler. + */ + @JSExport + def ahem[F[T] <: Seq[T]](x: F[Int]): F[String] = ??? + } + + val x = (new HK).asInstanceOf[js.Dynamic] + assertEquals("function", js.typeOf(x.ahem)) + } + @Test def exportsForWeirdStuff(): Unit = { class UhOh { // Something no one should export @@ -1023,14 +1388,28 @@ class ExportsTest { lazy val c = 3 - class Bar // not exported, but should not fail + object d + + // Classes should not be exported automatically. + class Bar + class JSBar extends js.Object + abstract class AbstractBar + abstract class JSAbstractBar extends js.Object + trait Baz } - val foo = (new Foo).asInstanceOf[js.Dynamic] + val scalaFoo = new Foo + val jsFoo = scalaFoo.asInstanceOf[js.Dynamic] - assertEquals(1, foo.a) - assertEquals(2, foo.b) - assertEquals(3, foo.c) + assertEquals(1, jsFoo.a) + assertEquals(2, jsFoo.b) + assertEquals(3, jsFoo.c) + assertSame(scalaFoo.d, jsFoo.d) + assertJSUndefined(jsFoo.Bar) + assertJSUndefined(jsFoo.JSBar) + assertJSUndefined(jsFoo.AbstractBar) + assertJSUndefined(jsFoo.JSAbstractBar) + assertJSUndefined(jsFoo.Baz) } @Test def noExportOfSyntheticMembersWithJSExportAll_Issue1195(): Unit = { @@ -1700,3 +2079,58 @@ object TopLevelFieldExportsReachability { @JSExportTopLevel("TopLevelExport_fieldreachability") val greeting = "Hello " + name } + +abstract class AbstractClasstWithPropertyForExport { + @JSExport + def x: js.Object +} + +object StaticObjectWithObjectForExportFromAbstractClass extends AbstractClasstWithPropertyForExport { + object x extends js.Object { + val y = 1 + } +} + +trait TraitWithPropertyForExport { + @JSExport + def x: js.Object +} + +object StaticObjectWithObjectForExportFromTrait extends TraitWithPropertyForExport { + object x extends js.Object { + val y = 1 + } +} + +object StaticObjectWithNestedClasses { + val x = 2 + + @JSExport + class Nested(y: Int) { + @JSExport + def this() = this(2) + + @JSExport + def witness: Int = x + y + } + + @JSExport + class Nested2(y: Int) extends js.Object { + def witness: Int = x + y + } +} + +object StaticObjectWithNestedObjects { + val x: Int = 1 + + @JSExport + object obj { + @JSExport + def witness: Int = x + 1 + } + + @JSExport + object jsObj extends js.Object { + def witness: Int = x + 1 + } +} diff --git a/test-suite/js/src/test/require-2.12/org/scalajs/testsuite/jsinterop/JSOptionalTest212.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/JSOptionalTest212.scala similarity index 100% rename from test-suite/js/src/test/require-2.12/org/scalajs/testsuite/jsinterop/JSOptionalTest212.scala rename to test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/JSOptionalTest212.scala diff --git a/test-suite/js/src/test/require-2.12/org/scalajs/testsuite/jsinterop/JSOptionalTest212FunParamInference.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/JSOptionalTest212FunParamInference.scala similarity index 100% rename from test-suite/js/src/test/require-2.12/org/scalajs/testsuite/jsinterop/JSOptionalTest212FunParamInference.scala rename to test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/JSOptionalTest212FunParamInference.scala diff --git a/test-suite/js/src/test/require-sam/org/scalajs/testsuite/jsinterop/SAMJSTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/SAMJSTest.scala similarity index 55% rename from test-suite/js/src/test/require-sam/org/scalajs/testsuite/jsinterop/SAMJSTest.scala rename to test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/SAMJSTest.scala index 9303659135..9d977d28f3 100644 --- a/test-suite/js/src/test/require-sam/org/scalajs/testsuite/jsinterop/SAMJSTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/SAMJSTest.scala @@ -1,10 +1,14 @@ -/* __ *\ -** ________ ___ / / ___ __ ____ Scala.js Test Suite ** -** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2018, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** -** /____/\___/_/ |_/____/_/ | |__/ /____/ ** -** |/____/ ** -\* */ +/* + * Scala.js (https://www.scala-js.org/) + * + * Copyright EPFL. + * + * Licensed under Apache License 2.0 + * (https://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ package org.scalajs.testsuite.jsinterop diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/SpecialTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/SpecialTest.scala index e96e4df314..a33f3cbdb6 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/SpecialTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/SpecialTest.scala @@ -224,8 +224,13 @@ class SpecialTest { // Does not unwrap a Throwable val th = new IllegalArgumentException assertSame(th, js.special.unwrapFromThrowable(th)) + } + + @Test def unwrapFromThrowableNull(): Unit = { + assumeTrue("assumed compliant NPEs", Platform.hasCompliantNullPointers) - // unwrapFromThrowable(null) is UB (as NullPointerException) and is therefore not tested + // Unwrapping null throws + assertThrows(classOf[NullPointerException], js.special.unwrapFromThrowable(null)) } // js.special.fileLevelThis diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/library/ArrayOpsTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/library/ArrayOpsTest.scala index 8d1be08d13..d6944cbeb8 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/library/ArrayOpsTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/library/ArrayOpsTest.scala @@ -114,7 +114,6 @@ class ArrayOpsTest { @Test def sizeCompare(): Unit = { assumeFalse("sizeCompare was added in 2.13", - scalaVersion.startsWith("2.11.") || scalaVersion.startsWith("2.12.")) import FallbackImplicits._ @@ -135,7 +134,6 @@ class ArrayOpsTest { @Test def sizeIs(): Unit = { assumeFalse("sizeIs was added in 2.13", - scalaVersion.startsWith("2.11.") || scalaVersion.startsWith("2.12.")) import FallbackImplicits._ @@ -149,7 +147,6 @@ class ArrayOpsTest { @Test def lengthIs(): Unit = { assumeFalse("lengthIs was added in 2.13", - scalaVersion.startsWith("2.11.") || scalaVersion.startsWith("2.12.")) import FallbackImplicits._ @@ -302,7 +299,6 @@ class ArrayOpsTest { @Test def partitionMap(): Unit = { assumeFalse("partitionMap was added in 2.13", - scalaVersion.startsWith("2.11.") || scalaVersion.startsWith("2.12.")) import FallbackImplicits._ @@ -664,10 +660,7 @@ class ArrayOpsTest { @Test def startsWith(): Unit = { val array = js.Array(1, 5, 7, 2, 54, 2, 78, 0, 3) - val supportsNegativeStart = { - !scalaVersion.startsWith("2.11.") && - !scalaVersion.startsWith("2.12.") - } + val supportsNegativeStart = !scalaVersion.startsWith("2.12.") // js.Array @@ -915,7 +908,6 @@ class ArrayOpsTest { array.trimStart(4) assumeFalse("the safe behavior was introduced in 2.13", - scalaVersion.startsWith("2.11.") || scalaVersion.startsWith("2.12.")) assertJSArrayEquals(js.Array(42, 53, 5, 54, 23, 44, 78), array) array.trimStart(-3) @@ -929,7 +921,6 @@ class ArrayOpsTest { array.trimEnd(4) assumeFalse("the safe behavior was introduced in 2.13", - scalaVersion.startsWith("2.11.") || scalaVersion.startsWith("2.12.")) assertJSArrayEquals(js.Array(33, 11, 2, 3, 42, 53, 5), array) array.trimEnd(-3) diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/library/StackTraceTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/library/StackTraceTest.scala index eb0b7b2f94..d931b4bb0d 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/library/StackTraceTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/library/StackTraceTest.scala @@ -156,6 +156,7 @@ object StackTraceTest { class SJS extends js.Object { @JSName("n") + @noinline def m(): Int = new Foo().f(20) } } 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 2473184938..2488245ef2 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 @@ -12,8 +12,12 @@ package org.scalajs.testsuite.typedarray -import org.junit.Assert._ import org.junit.Test +import org.junit.Assert._ +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 @@ -193,4 +197,20 @@ class TypedArrayConversionTest { x(0) = 0 assertEquals(1.0, y(0), 0.0) } + + @Test def convertScalaArrayToTypedArrayNulls(): Unit = { + assumeTrue("Assuming compliant nullPointers", hasCompliantNullPointers) + + @noinline def assertNPE[U](body: => U): Unit = + assertThrows(classOf[NullPointerException], body) + + @noinline def nullOf[T >: Null]: T = null + + assertNPE(nullOf[Array[Byte]].toTypedArray) + assertNPE(nullOf[Array[Short]].toTypedArray) + assertNPE(nullOf[Array[Char]].toTypedArray) + assertNPE(nullOf[Array[Int]].toTypedArray) + assertNPE(nullOf[Array[Float]].toTypedArray) + assertNPE(nullOf[Array[Double]].toTypedArray) + } } 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 76ef4e6a17..a3d908bf8e 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 @@ -32,6 +32,8 @@ object Platform { def executingInJVMOnLowerThanJDK16: Boolean = jdkVersion < 16 + def executingInJVMOnLowerThanJDK17: Boolean = jdkVersion < 17 + private lazy val jdkVersion = { val v = System.getProperty("java.version") if (v.startsWith("1.")) Integer.parseInt(v.drop(2).takeWhile(_.isDigit)) @@ -44,6 +46,7 @@ object Platform { def hasCompliantArrayIndexOutOfBounds: Boolean = true def hasCompliantArrayStores: Boolean = true def hasCompliantNegativeArraySizes: Boolean = true + def hasCompliantNullPointers: Boolean = true def hasCompliantStringIndexOutOfBounds: Boolean = true def hasCompliantModule: Boolean = true def hasDirectBuffers: Boolean = true diff --git a/test-suite/shared/src/test/require-jdk11/org/scalajs/testsuite/javalib/io/InputStreamTestOnJDK11.scala b/test-suite/shared/src/test/require-jdk11/org/scalajs/testsuite/javalib/io/InputStreamTestOnJDK11.scala new file mode 100644 index 0000000000..cf12e7ae55 --- /dev/null +++ b/test-suite/shared/src/test/require-jdk11/org/scalajs/testsuite/javalib/io/InputStreamTestOnJDK11.scala @@ -0,0 +1,102 @@ +/* + * 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.javalib.io + +import java.io._ + +import org.junit.Test +import org.junit.Assert._ +import org.junit.Assume._ + +import org.scalajs.testsuite.utils.AssertThrows.assertThrows +import org.scalajs.testsuite.utils.Platform + +class InputStreamTestOnJDK11 { + /** InputStream that only ever reads max bytes at once */ + def chunkedStream(max: Int, seq: Seq[Int]): InputStream = new SeqInputStreamForTest(seq) { + require(max > 0) + + override def read(b: Array[Byte], off: Int, len: Int): Int = { + val newLen = Math.min(max, len) + super.read(b, off, newLen) + } + } + + def emptyStream(): InputStream = new InputStream { + def read(): Int = -1 + } + + private def assertBytesEqual(expect: Seq[Int], got: Array[Byte]) = + assertArrayEquals(expect.toArray.map(_.toByte), got) + + @Test def readAllBytes(): Unit = { + assertBytesEqual(0 until 100, chunkedStream(10, 0 until 100).readAllBytes()) + assertBytesEqual(Nil, emptyStream().readAllBytes()) + } + + @Test def readNBytes(): Unit = { + assertBytesEqual(0 until 20, chunkedStream(10, 0 until 100).readNBytes(20)) + assertBytesEqual(0 until 100, chunkedStream(10, 0 until 100).readNBytes(200)) + assertBytesEqual(Nil, chunkedStream(10, 0 until 100).readNBytes(0)) + assertBytesEqual(Nil, emptyStream().readNBytes(1000)) + + // test buffer growing + assertBytesEqual(0 until 2000, chunkedStream(200, 0 until 2000).readNBytes(2000)) + + assertThrows(classOf[IllegalArgumentException], emptyStream().readNBytes(-1)) + } + + @Test def readNBytesBuf(): Unit = { + val buf = new Array[Byte](30) + + chunkedStream(10, 0 until 100).readNBytes(buf, 2, 22) + + assertBytesEqual(Seq.fill(2)(0) ++ (0 until 22) ++ Seq.fill(6)(0), buf) + } + + @Test def transferTo(): Unit = { + val stream = chunkedStream(10, 0 until 100) + val out = new ByteArrayOutputStream() + stream.transferTo(out) + + assertBytesEqual(0 until 100, out.toByteArray()) + } + + @Test def transferToThrowsNPE(): Unit = { + assumeTrue("assumed compliant NPEs", Platform.hasCompliantNullPointers) + // nothing to write, should still throw. + assertThrows(classOf[NullPointerException], emptyStream().transferTo(null)) + } + + @Test def nullInputStream(): Unit = { + val stream = InputStream.nullInputStream() + + assertEquals(-1, stream.read()) + assertEquals(0, stream.skip(10)) + assertBytesEqual(Nil, stream.readAllBytes()) + + stream.close() + stream.close() // shouldn't throw + + assertThrows(classOf[IOException], stream.available()) + assertThrows(classOf[IOException], stream.read()) + assertThrows(classOf[IOException], stream.read(new Array[Byte](1))) // JDK doesn't throw if len == 0 + assertThrows(classOf[IOException], stream.read(new Array[Byte](1), 0, 1)) // JDK doesn't throw if len == 0 + assertThrows(classOf[IOException], stream.readAllBytes()) + assertThrows(classOf[IOException], stream.readNBytes(new Array[Byte](1), 0, 0)) + assertThrows(classOf[IOException], stream.readNBytes(0)) + assertThrows(classOf[IOException], stream.skip(1)) + assertThrows(classOf[IOException], stream.skip(0)) + assertThrows(classOf[IOException], stream.transferTo(new ByteArrayOutputStream)) + } +} diff --git a/test-suite/shared/src/test/require-jdk11/org/scalajs/testsuite/javalib/io/OutputStreamTestOnJDK11.scala b/test-suite/shared/src/test/require-jdk11/org/scalajs/testsuite/javalib/io/OutputStreamTestOnJDK11.scala new file mode 100644 index 0000000000..1b572c20a9 --- /dev/null +++ b/test-suite/shared/src/test/require-jdk11/org/scalajs/testsuite/javalib/io/OutputStreamTestOnJDK11.scala @@ -0,0 +1,41 @@ +/* + * 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.javalib.io + +import java.io._ + +import org.junit.Test +import org.junit.Assert._ +import org.junit.Assume._ + +import org.scalajs.testsuite.utils.AssertThrows.assertThrows +import org.scalajs.testsuite.utils.Platform + +class OutputStreamTestOnJDK11 { + @Test def nullOutputStream(): Unit = { + val stream = OutputStream.nullOutputStream() + + stream.write(1) + stream.write(new Array[Byte](2)) + stream.write(new Array[Byte](2), 0, 1) + + stream.close() + stream.close() // shouldn't throw + + assertThrows(classOf[IOException], stream.write(1)) + assertThrows(classOf[IOException], stream.write(new Array[Byte](0))) + assertThrows(classOf[IOException], stream.write(new Array[Byte](0), 0, 0)) + + stream.flush() // shouldn't throw + } +} diff --git a/test-suite/shared/src/test/require-jdk11/org/scalajs/testsuite/javalib/util/concurrent/FlowTest.scala b/test-suite/shared/src/test/require-jdk11/org/scalajs/testsuite/javalib/util/concurrent/FlowTest.scala new file mode 100644 index 0000000000..d2bc91c7fe --- /dev/null +++ b/test-suite/shared/src/test/require-jdk11/org/scalajs/testsuite/javalib/util/concurrent/FlowTest.scala @@ -0,0 +1,90 @@ +/* + * 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.javalib.util.concurrent + +import java.util.concurrent.Flow + +import org.junit.Test +import org.junit.Assert._ + +class FlowTest { + import FlowTest._ + + @Test def testDefaultBufferSize(): Unit = + assertEquals(256, Flow.defaultBufferSize()) + + @Test def testProcessor(): Unit = { + val processor = makeProcessor[Int, String]() + processor.subscribe(makeSubscriber[String]()) + processor.onSubscribe(makeSubscription()) + processor.onNext(42) + processor.onError(new Exception) + processor.onComplete() + } + + @Test def testPublisher(): Unit = { + val publisher = makePublisher[Int]() + publisher.subscribe(makeSubscriber[Int]()) + } + + @Test def testSubscriber(): Unit = { + val subscriber = makeSubscriber[Int]() + subscriber.onSubscribe(makeSubscription()) + subscriber.onNext(42) + subscriber.onError(new Exception) + subscriber.onComplete() + } + + @Test def testSubscription(): Unit = { + val subscription = makeSubscription() + subscription.request(42) + subscription.cancel() + } + +} + +object FlowTest { + + def makeProcessor[T, R](): Flow.Processor[T, R] = { + new Flow.Processor[T, R] { + def subscribe(subscriber: Flow.Subscriber[_ >: R]): Unit = () + def onSubscribe(subscription: Flow.Subscription): Unit = () + def onNext(item: T): Unit = () + def onError(throwable: Throwable): Unit = () + def onComplete(): Unit = () + } + } + + def makePublisher[T](): Flow.Publisher[T] = { + new Flow.Publisher[T] { + def subscribe(subscriber: Flow.Subscriber[_ >: T]): Unit = () + } + } + + def makeSubscriber[T](): Flow.Subscriber[T] = { + new Flow.Subscriber[T] { + def onSubscribe(subscription: Flow.Subscription): Unit = () + def onNext(item: T): Unit = () + def onError(throwable: Throwable): Unit = () + def onComplete(): Unit = () + } + } + + def makeSubscription(): Flow.Subscription = { + new Flow.Subscription { + def request(n: Long): Unit = () + def cancel(): Unit = () + } + } + +} diff --git a/test-suite/shared/src/test/require-jdk15/org/scalajs/testsuite/javalib/io/InputStreamTestOnJDK15.scala b/test-suite/shared/src/test/require-jdk15/org/scalajs/testsuite/javalib/io/InputStreamTestOnJDK15.scala new file mode 100644 index 0000000000..5bda94aa7a --- /dev/null +++ b/test-suite/shared/src/test/require-jdk15/org/scalajs/testsuite/javalib/io/InputStreamTestOnJDK15.scala @@ -0,0 +1,75 @@ +/* + * 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.javalib.io + +import java.io._ + +import org.junit.Test +import org.junit.Assert._ +import org.junit.Assume._ + +import org.scalajs.testsuite.utils.AssertThrows.assertThrows +import org.scalajs.testsuite.utils.Platform + +class InputStreamTestOnJDK15 { + /** InputStream that only ever skips max bytes at once */ + def lowSkipStream(max: Int, seq: Seq[Int]): InputStream = new SeqInputStreamForTest(seq) { + require(max > 0) + + override def skip(n: Long): Long = + super.skip(Math.min(max.toLong, n).toInt) + } + + private def assertBytesEqual(expect: Seq[Int], got: Array[Byte]) = + assertArrayEquals(expect.toArray.map(_.toByte), got) + + @Test def skipNBytes(): Unit = { + val stream = lowSkipStream(10, 0 until 100) + + assertBytesEqual(0 until 15, stream.readNBytes(15)) + + stream.skipNBytes(25) + + assertBytesEqual(40 until 55, stream.readNBytes(15)) + + stream.skipNBytes(45) + + assertBytesEqual(Nil, stream.readNBytes(20)) + } + + @Test def skipNBytesThrowsOnEOF(): Unit = { + assertThrows(classOf[EOFException], lowSkipStream(10, 0 until 11).skipNBytes(20)) + } + + @Test def skipNBytesThrowsIfBadSkip(): Unit = { + class BadSkipStream(skipResult: Long) extends InputStream { + def read(): Int = 0 + override def skip(n: Long): Long = skipResult + } + + assertThrows(classOf[IOException], new BadSkipStream(-1).skipNBytes(1)) + assertThrows(classOf[IOException], new BadSkipStream(2).skipNBytes(1)) + + // Must not invoke skip if non-positive count + new BadSkipStream(2).skipNBytes(0) + new BadSkipStream(2).skipNBytes(-1) + } + + @Test def nullInputStream(): Unit = { + val stream = InputStream.nullInputStream() + + stream.close() + + assertThrows(classOf[IOException], stream.skipNBytes(0)) + } +} diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/DefaultMethodsTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/DefaultMethodsTest.scala index 93b3885ee3..bd41867292 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/DefaultMethodsTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/DefaultMethodsTest.scala @@ -23,9 +23,6 @@ import org.scalajs.testsuite.utils.Platform._ class DefaultMethodsTest { @Test def canOverrideDefaultMethod(): Unit = { - assumeFalse("Affected by https://github.com/scala/bug/issues/10609", - executingInJVM && scalaVersion == "2.11.12") - var counter = 0 class SpecialIntComparator extends ju.Comparator[Int] { diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/IntTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/IntTest.scala index 454c872da2..612c1edf0b 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/IntTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/IntTest.scala @@ -382,22 +382,13 @@ class IntTest { test(MaxVal, 1, MaxVal >>> 1) } - private def scalacCorrectlyHandlesIntShiftLong: Boolean = - !Platform.scalaVersion.startsWith("2.11.") - @Test def intShiftLeftLongConstantFolded(): Unit = { - assumeTrue("scalac must correctly handle int shift long", - scalacCorrectlyHandlesIntShiftLong) - assert(0x01030507 << 36L == 271601776) val r = 0x01030507 << 36L assert(r == 271601776) } @Test def intShiftLeftLongAtRuntime(): Unit = { - assumeTrue("On the JVM, scalac must correctly handle int shift long", - !Platform.executingInJVM || scalacCorrectlyHandlesIntShiftLong) - var x: Int = 0x01030507 var y: Long = 36L assert(x << y == 271601776) @@ -406,18 +397,12 @@ class IntTest { } @Test def intShiftLogicalRightLongConstantFolded(): Unit = { - assumeTrue("scalac must correctly handle int shift long", - scalacCorrectlyHandlesIntShiftLong) - assert(0x90503010 >>> 36L == 151323393) val r = 0x90503010 >>> 36L assert(r == 151323393) } @Test def intShiftLogicalRightLongAtRuntime(): Unit = { - assumeTrue("On the JVM, scalac must correctly handle int shift long", - !Platform.executingInJVM || scalacCorrectlyHandlesIntShiftLong) - var x: Int = 0x90503010 var y: Long = 36L assert(x >>> y == 151323393) @@ -426,18 +411,12 @@ class IntTest { } @Test def intShiftArithmeticRightLongConstantFolded(): Unit = { - assumeTrue("scalac must correctly handle int shift long", - scalacCorrectlyHandlesIntShiftLong) - assert(0x90503010 >> 36L == -117112063) val r = 0x90503010 >> 36L assert(r == -117112063) } @Test def intShiftArithmeticRightLongAtRuntime(): Unit = { - assumeTrue("On the JVM, scalac must correctly handle int shift long", - !Platform.executingInJVM || scalacCorrectlyHandlesIntShiftLong) - var x: Int = 0x90503010 var y: Long = 36L assert(x >> y == -117112063) diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/LongTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/LongTest.scala index 89aaed6d47..a21f8ff802 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/LongTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/LongTest.scala @@ -182,8 +182,7 @@ class LongTest { } @Test def hashHashInCaseClasses(): Unit = { - if (scalaVersion.startsWith("2.11.") || - scalaVersion.startsWith("2.12.")) { + if (scalaVersion.startsWith("2.12.")) { assertEquals(-1669410282, HashTestBox(0L).##) assertEquals(-1561146018, HashTestBox(55L).##) assertEquals(-1266055417, HashTestBox(-12L).##) diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/NullPointersTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/NullPointersTest.scala new file mode 100644 index 0000000000..d76225c466 --- /dev/null +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/NullPointersTest.scala @@ -0,0 +1,281 @@ +/* + * 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.compiler + +import org.junit.{BeforeClass, Test} +import org.junit.Assert._ +import org.junit.Assume._ + +import org.scalajs.testsuite.utils.AssertThrows.assertThrows +import org.scalajs.testsuite.utils.Platform._ + +class NullPointersTest { + import NullPointersTest._ + + // Instantiate Tester somewhere, otherwise plenty of tests are moot + new Tester(0) + + @noinline + private def nullOf[T >: Null]: T = null + + @inline + private def inlineNullOf[T >: Null]: T = null + + @noinline + private def assertNPE[U](body: => U): Unit = + assertThrows(classOf[NullPointerException], body) + + @noinline + private def throwIllegalArgAsInt(): Int = + throw new IllegalArgumentException + + @inline + private def throwIllegalArgAsIntInline(): Int = + throw new IllegalArgumentException + + @noinline + private def throwIllegalArgAsString(): String = + throw new IllegalArgumentException + + @inline + private def throwIllegalArgAsStringInline(): String = + throw new IllegalArgumentException + + @Test def methodCallsWithRegularClasses(): Unit = { + assertNPE(nullOf[Tester].x) + assertNPE(inlineNullOf[Tester].x) + + assertNPE(nullOf[Tester].noInlineMethod(1)) + assertNPE(inlineNullOf[Tester].noInlineMethod(1)) + + assertNPE(nullOf[Tester].inlineMethod(1)) + assertNPE(inlineNullOf[Tester].inlineMethod(1)) + + assertNPE(nullOf[Tester].inlineMethodWithField(1)) + assertNPE(inlineNullOf[Tester].inlineMethodWithField(1)) + + assertNPE(nullOf[Tester].toString()) + assertNPE(inlineNullOf[Tester].toString()) + assertNPE(nullOf[AnyRef].toString()) + assertNPE(inlineNullOf[AnyRef].toString()) + + assertNPE(nullOf[Tester].synchronized("foo")) + assertNPE(inlineNullOf[Tester].synchronized("foo")) + assertNPE(nullOf[AnyRef].synchronized("foo")) + assertNPE(inlineNullOf[AnyRef].synchronized("foo")) + + assertNPE(nullOf[Tester].getClass()) + assertNPE(inlineNullOf[Tester].getClass()) + assertNPE(nullOf[AnyRef].getClass()) + assertNPE(inlineNullOf[AnyRef].getClass()) + + assertNPE(nullOf[Tester].clone()) + assertNPE(inlineNullOf[Tester].clone()) + + // NPE takes precedence over evaluating arguments, unlike on the JVM + + if (executingInJVM) { + assertThrows(classOf[IllegalArgumentException], + nullOf[Tester].noInlineMethod(throwIllegalArgAsInt())) + } else { + assertNPE(nullOf[Tester].noInlineMethod(throwIllegalArgAsInt())) + assertNPE(nullOf[Tester].noInlineMethod(throwIllegalArgAsIntInline())) + assertNPE(inlineNullOf[Tester].noInlineMethod(throwIllegalArgAsInt())) + assertNPE(inlineNullOf[Tester].noInlineMethod(throwIllegalArgAsIntInline())) + + assertNPE(nullOf[Tester].inlineMethod(throwIllegalArgAsInt())) + assertNPE(nullOf[Tester].inlineMethod(throwIllegalArgAsIntInline())) + assertNPE(inlineNullOf[Tester].inlineMethod(throwIllegalArgAsInt())) + assertNPE(inlineNullOf[Tester].inlineMethod(throwIllegalArgAsIntInline())) + } + } + + @Test def methodCallsWithHijackedClasses(): Unit = { + assertNPE(nullOf[Integer].intValue()) + assertNPE(inlineNullOf[Integer].intValue()) + assertNPE(nullOf[Integer].toString()) + assertNPE(inlineNullOf[Integer].toString()) + assertNPE(nullOf[Integer].synchronized("foo")) + assertNPE(inlineNullOf[Integer].synchronized("foo")) + assertNPE(nullOf[Integer].getClass()) + assertNPE(inlineNullOf[Integer].getClass()) + + assertNPE(nullOf[Character].charValue()) + assertNPE(inlineNullOf[Character].charValue()) + assertNPE(nullOf[Character].toString()) + assertNPE(inlineNullOf[Character].toString()) + + assertNPE(nullOf[String].length()) + assertNPE(inlineNullOf[String].length()) + assertNPE(nullOf[String].charAt(3)) + assertNPE(inlineNullOf[String].charAt(3)) + assertNPE(nullOf[String].toString()) + assertNPE(inlineNullOf[String].toString()) + assertNPE(nullOf[String].concat("foo")) + assertNPE(inlineNullOf[String].concat("foo")) + + // NPE takes precedence over evaluating arguments, unlike on the JVM + + if (executingInJVM) { + assertThrows(classOf[IllegalArgumentException], + nullOf[String].substring(throwIllegalArgAsInt())) + } else { + // The implementation of charAt in Scala.js is a bit special, so it deserves its own test + assertNPE(nullOf[String].charAt(throwIllegalArgAsInt())) + assertNPE(nullOf[String].charAt(throwIllegalArgAsIntInline())) + assertNPE(inlineNullOf[String].charAt(throwIllegalArgAsInt())) + assertNPE(inlineNullOf[String].charAt(throwIllegalArgAsIntInline())) + + // regular no-inline method + assertNPE(nullOf[String].compareTo(throwIllegalArgAsString())) + assertNPE(nullOf[String].compareTo(throwIllegalArgAsStringInline())) + assertNPE(inlineNullOf[String].compareTo(throwIllegalArgAsString())) + assertNPE(inlineNullOf[String].compareTo(throwIllegalArgAsStringInline())) + + // regular inline method + assertNPE(nullOf[String].substring(throwIllegalArgAsInt())) + assertNPE(nullOf[String].substring(throwIllegalArgAsIntInline())) + assertNPE(inlineNullOf[String].substring(throwIllegalArgAsInt())) + assertNPE(inlineNullOf[String].substring(throwIllegalArgAsIntInline())) + } + } + + @Test def arrays(): Unit = { + assertNPE(nullOf[Array[Int]].length) + assertNPE(nullOf[Array[Char]].length) + assertNPE(nullOf[Array[AnyRef]].length) + assertNPE(nullOf[Array[String]].length) + assertNPE(nullOf[Array[List[Any]]].length) + + assertNPE(inlineNullOf[Array[Int]].length) + assertNPE(inlineNullOf[Array[Char]].length) + assertNPE(inlineNullOf[Array[AnyRef]].length) + assertNPE(inlineNullOf[Array[String]].length) + assertNPE(inlineNullOf[Array[List[Any]]].length) + + assertNPE(nullOf[Array[Int]](5)) + assertNPE(nullOf[Array[Char]](5)) + assertNPE(nullOf[Array[AnyRef]](5)) + assertNPE(nullOf[Array[String]](5)) + assertNPE(nullOf[Array[List[Any]]](5)) + + assertNPE(inlineNullOf[Array[Int]](5)) + assertNPE(inlineNullOf[Array[Char]](5)) + assertNPE(inlineNullOf[Array[AnyRef]](5)) + assertNPE(inlineNullOf[Array[String]](5)) + assertNPE(inlineNullOf[Array[List[Any]]](5)) + + assertNPE(nullOf[Array[Int]](-5)) + assertNPE(nullOf[Array[Char]](-5)) + assertNPE(nullOf[Array[AnyRef]](-5)) + assertNPE(nullOf[Array[String]](-5)) + assertNPE(nullOf[Array[List[Any]]](-5)) + + assertNPE(inlineNullOf[Array[Int]](-5)) + assertNPE(inlineNullOf[Array[Char]](-5)) + assertNPE(inlineNullOf[Array[AnyRef]](-5)) + assertNPE(inlineNullOf[Array[String]](-5)) + assertNPE(inlineNullOf[Array[List[Any]]](-5)) + + assertNPE(nullOf[Array[Int]](5) = 1) + assertNPE(nullOf[Array[Char]](5) = 'A') + assertNPE(nullOf[Array[AnyRef]](5) = None) + assertNPE(nullOf[Array[String]](5) = "foo") + assertNPE(nullOf[Array[List[Any]]](5) = List(1)) + + assertNPE(inlineNullOf[Array[Int]](5) = 1) + assertNPE(inlineNullOf[Array[Char]](5) = 'A') + assertNPE(inlineNullOf[Array[AnyRef]](5) = None) + assertNPE(inlineNullOf[Array[String]](5) = "foo") + assertNPE(inlineNullOf[Array[List[Any]]](5) = List(1)) + + assertNPE(nullOf[Array[Int]].clone()) + assertNPE(nullOf[Array[Char]].clone()) + assertNPE(nullOf[Array[AnyRef]].clone()) + assertNPE(nullOf[Array[String]].clone()) + assertNPE(nullOf[Array[List[Any]]].clone()) + + assertNPE(inlineNullOf[Array[Int]].clone()) + assertNPE(inlineNullOf[Array[Char]].clone()) + assertNPE(inlineNullOf[Array[AnyRef]].clone()) + assertNPE(inlineNullOf[Array[String]].clone()) + assertNPE(inlineNullOf[Array[List[Any]]].clone()) + } + + @Test def genericArrays(): Unit = { + // Tests for the intrinsics for ScalaRunTime.array_{apply,update,select}. + + @inline def testGeneric[T](array: Array[T], value: T): Unit = { + assertNPE(array.length) + assertNPE(array(1)) + assertNPE(array(1) = value) + assertNPE(array(-1)) + assertNPE(array(-1) = value) + + assertThrows(classOf[IllegalArgumentException], array(throwIllegalArgAsInt())) + assertThrows(classOf[IllegalArgumentException], array(throwIllegalArgAsIntInline())) + + assertThrows(classOf[IllegalArgumentException], array(throwIllegalArgAsInt()) = value) + assertThrows(classOf[IllegalArgumentException], array(throwIllegalArgAsIntInline()) = value) + + assertThrows(classOf[IllegalArgumentException], array(1) = (throw new IllegalArgumentException())) + } + + @noinline def testNoInline[T](array: Array[T], value: T): Unit = { + testGeneric(array, value) + } + + @inline def test[T](array: Array[T], value: T): Unit = { + testNoInline(array, value) + testGeneric(array, value) + } + + val nullArrayRef = nullOf[Array[AnyRef]] + test(nullArrayRef, List(1)) + test(inlineNullOf[Array[AnyRef]], List(1)) + + val nullArrayInt = nullOf[Array[Int]] + test(nullArrayInt, 1) + test(inlineNullOf[Array[Int]], 1) + } + + @Test def throwNull(): Unit = { + assertNPE(throw null) + assertNPE(throw (null: Throwable)) + assertNPE(throw (null: IllegalArgumentException)) + + @noinline def nullThrowable(): Throwable = null + @noinline def nullIllegalArg(): IllegalArgumentException = null + + assertNPE(throw nullThrowable()) + assertNPE(throw nullIllegalArg()) + } +} + +object NullPointersTest { + @BeforeClass + def beforeClass(): Unit = { + assumeTrue("assuming compliant null pointer checks", hasCompliantNullPointers) + } + + class Tester(val x: Int) extends java.lang.Cloneable { + @noinline def noInlineMethod(a: Int): Int = a + + @inline def inlineMethod(a: Int): Int = a + + @inline def inlineMethodWithField(a: Int): Int = a + x + + @inline override def clone(): Tester = + super.clone().asInstanceOf[Tester] + } +} diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/RegressionTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/RegressionTest.scala index 2886f742f0..219358cecc 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/RegressionTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/RegressionTest.scala @@ -21,6 +21,7 @@ import org.junit.Assume._ import org.scalajs.testsuite.utils.AssertThrows.assertThrows import org.scalajs.testsuite.utils.Platform +import org.scalajs.testsuite.utils.Platform._ class RegressionTest { import RegressionTest._ @@ -105,8 +106,6 @@ class RegressionTest { assumeFalse("Affected by https://github.com/scala/bug/issues/10551", Platform.executingInJVM && { - scalaVersion.startsWith("2.11.") || - scalaVersion == "2.12.0" || scalaVersion == "2.12.1" || scalaVersion == "2.12.2" || scalaVersion == "2.12.3" || scalaVersion == "2.12.4" }) @@ -199,10 +198,6 @@ class RegressionTest { foo(2, 4) } - @Test def nullSynchronizedThrows_Issue874(): Unit = { - assertThrows(classOf[NullPointerException], null.synchronized(5)) - } - @Test def synchronizedXPreservesSideEffectsOfX(): Unit = { var c = 0 def x: RegressionTest.this.type = { c += 1; this } @@ -210,14 +205,21 @@ class RegressionTest { assertEquals(1, c) } - @Test def irCheckerAllowsApplySelectOnNullTypeAndNothingType_Issue1123(): Unit = { + @Test def irCheckerAllowsApplySelectOnNullType_Issue1123(): Unit = { + /* The IR checker checks this code whether or not the assumption holds. + * The assumption only applies to the run-time behavior. + */ + assumeTrue("assuming compliant null pointer checks", hasCompliantNullPointers) + def giveMeANull(): Null = null - assertThrows(classOf[Exception], (giveMeANull(): StringBuilder).append(5)) - assertThrows(classOf[Exception], (giveMeANull(): scala.runtime.IntRef).elem) + assertThrows(classOf[NullPointerException], (giveMeANull(): StringBuilder).append(5)) + assertThrows(classOf[NullPointerException], (giveMeANull(): scala.runtime.IntRef).elem) + } - def giveMeANothing(): Nothing = throw new Exception("boom") - assertThrows(classOf[Exception], (giveMeANothing(): StringBuilder).append(5)) - assertThrows(classOf[Exception], (giveMeANothing(): scala.runtime.IntRef).elem) + @Test def irCheckerAllowsApplySelectOnNothingType_Issue1123(): Unit = { + def giveMeANothing(): Nothing = throw new IllegalStateException("boom") + assertThrows(classOf[IllegalStateException], (giveMeANothing(): StringBuilder).append(5)) + assertThrows(classOf[IllegalStateException], (giveMeANothing(): scala.runtime.IntRef).elem) } @Test def irCheckerDoesNotCheckFieldExistenceOnNonExistentClasses(): Unit = { @@ -554,12 +556,7 @@ class RegressionTest { def getNull(): Any = null val x = getNull().asInstanceOf[Unit]: Any - if (Platform.scalaVersion.startsWith("2.11.")) { - assertNull(x.asInstanceOf[AnyRef]) - } else { - // As of Scala 2.12.0-M5, null.asInstanceOf[Unit] (correctly) returns () - assertEquals((), x) - } + assertEquals((), x) } @Test def lambdaParameterWithDash_Issue1790(): Unit = { @@ -595,18 +592,6 @@ class RegressionTest { assertEquals((Nil, 10), result) } - private val hasEqEqJLFloatDoubleBug: Boolean = { - val v = Platform.scalaVersion - v.startsWith("2.11.") || v == "2.12.1" - } - - def assertTrueUnlessEqEqJLFloatDoubleBug(actual: Boolean): Unit = { - if (hasEqEqJLFloatDoubleBug) - assertFalse(actual) - else - assertTrue(actual) - } - @Test def eqEqJLDouble(): Unit = { // Taken from run/sd329.scala in scala/scala @@ -621,10 +606,10 @@ class RegressionTest { def d2B: java.lang.Double = d2 def d3B: java.lang.Double = d3 def d4B: java.lang.Double = d4 - assertTrueUnlessEqEqJLFloatDoubleBug(d1B == d2B) + assertTrue(d1B == d2B) assertTrue(d1 == d1B) assertTrue(d1B == d1) - assertTrueUnlessEqEqJLFloatDoubleBug(d3B != d4B) + assertTrue(d3B != d4B) assertTrue(d3 != d4B) assertTrue(d3B != d4) @@ -662,10 +647,10 @@ class RegressionTest { def f2B: java.lang.Float = f2 def f3B: java.lang.Float = f3 def f4B: java.lang.Float = f4 - assertTrueUnlessEqEqJLFloatDoubleBug(f1B == f2B) + assertTrue(f1B == f2B) assertTrue(f1 == f1B) assertTrue(f1B == f1) - assertTrueUnlessEqEqJLFloatDoubleBug(f3B != f4B) + assertTrue(f3B != f4B) assertTrue(f3 != f4B) assertTrue(f3B != f4) @@ -714,8 +699,7 @@ class RegressionTest { @Test def superMixinCallIn212_Issue3013(): Unit = { assumeTrue( "Super mixin calls are broken in Scala/JVM 2.12.{0-2}", - !Platform.executingInJVM || - !Set("2.12.1", "2.12.2").contains(Platform.scalaVersion)) + !Platform.executingInJVM || Platform.scalaVersion != "2.12.2") import Bug3013._ @@ -871,9 +855,6 @@ class RegressionTest { } @Test def paramDefWithWrongTypeWithHKTAndTypeAliases_Issue3953(): Unit = { - assumeFalse("Scala/JVM 2.11.x produces wrong bytecode for this test", - Platform.executingInJVM && Platform.scalaVersion.startsWith("2.11.")) - import scala.language.higherKinds sealed class StreamT[M[_]](val step: M[Step[StreamT[M]]]) diff --git a/test-suite/shared/src/test/require-sam/org/scalajs/testsuite/compiler/SAMTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/SAMTest.scala similarity index 83% rename from test-suite/shared/src/test/require-sam/org/scalajs/testsuite/compiler/SAMTest.scala rename to test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/SAMTest.scala index abfd0b88af..4fb4aba1c5 100644 --- a/test-suite/shared/src/test/require-sam/org/scalajs/testsuite/compiler/SAMTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/SAMTest.scala @@ -1,10 +1,15 @@ -/* __ *\ -** ________ ___ / / ___ __ ____ Scala.js Test Suite ** -** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2016, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** -** /____/\___/_/ |_/____/_/ | |__/ /____/ ** -** |/____/ ** -\* */ +/* + * 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.compiler import java.util.Comparator diff --git a/test-suite/shared/src/test/require-sam/org/scalajs/testsuite/compiler/SAMWithOverridingBridgesTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/SAMWithOverridingBridgesTest.scala similarity index 82% rename from test-suite/shared/src/test/require-sam/org/scalajs/testsuite/compiler/SAMWithOverridingBridgesTest.scala rename to test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/SAMWithOverridingBridgesTest.scala index 51f654cead..53bda11da3 100644 --- a/test-suite/shared/src/test/require-sam/org/scalajs/testsuite/compiler/SAMWithOverridingBridgesTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/SAMWithOverridingBridgesTest.scala @@ -1,10 +1,14 @@ -/* __ *\ -** ________ ___ / / ___ __ ____ Scala.js Test Suite ** -** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2018, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** -** /____/\___/_/ |_/____/_/ | |__/ /____/ ** -** |/____/ ** -\* */ +/* + * 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.compiler diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/io/DataInputStreamTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/io/DataInputStreamTest.scala index 3c381c2d7a..8bcbc94a8c 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/io/DataInputStreamTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/io/DataInputStreamTest.scala @@ -15,7 +15,7 @@ package org.scalajs.testsuite.javalib.io import java.io._ import org.scalajs.testsuite.utils.AssertThrows.assertThrows -import org.scalajs.testsuite.utils.Platform.executingInJVM +import org.scalajs.testsuite.utils.Platform._ import org.junit._ import org.junit.Assert._ @@ -230,8 +230,6 @@ trait DataInputStreamTest { stream.readFully(buf) assertArrayEquals(toByteArray(-100 to -51), buf) - assertThrows(classOf[Exception], stream.readFully(null)) - stream.readFully(buf, 40, 10) assertArrayEquals(toByteArray((-100 to -61) ++ (-50 to -41)), buf) @@ -249,6 +247,22 @@ trait DataInputStreamTest { assertThrows(classOf[Exception], stream.readFully(buf)) } + @Test def readFullyOneArgThreeArgNull(): Unit = { + assumeTrue("assuming compliant null pointer checks", hasCompliantNullPointers) + + val stream = newStream(-100 to 99: _*) + val buf = new Array[Byte](50) + + stream.readFully(buf) + assertArrayEquals(toByteArray(-100 to -51), buf) + + assertThrows(classOf[NullPointerException], stream.readFully(null)) + assertThrows(classOf[NullPointerException], stream.readFully(null, 70, 1)) + + stream.readFully(buf, 40, 10) + assertArrayEquals(toByteArray((-100 to -61) ++ (-50 to -41)), buf) + } + @Test def readFullyForBurstyStreams(): Unit = { class BurstyStream(length: Int, burst: Int) extends InputStream { private var i: Int = 0 diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/io/InputStreamTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/io/InputStreamTest.scala index 5e513ddbc5..70f433273b 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/io/InputStreamTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/io/InputStreamTest.scala @@ -21,21 +21,8 @@ import org.scalajs.testsuite.utils.AssertThrows.assertThrows class InputStreamTest extends CommonStreamsTests { - def mkStream(seq: Seq[Int]): InputStream = new InputStream { - private var i: Int = 0 - private var m: Int = 0 - - override def read(b: Array[Byte], off: Int, len: Int): Int = super.read(b, off, len) - - def read(): Int = if (i < seq.length) { val e = seq(i); i += 1; e & 0xFF } else -1 - override def available(): Int = seq.length - i - - override def mark(readlimit: Int): Unit = m = i - - override def reset(): Unit = i = m - - override def markSupported(): Boolean = true - } + def mkStream(seq: Seq[Int]): InputStream = + new SeqInputStreamForTest(seq) @Test def readArrayByte(): Unit = { val stream = mkStream(1 to 200) diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/io/SeqInputStreamForTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/io/SeqInputStreamForTest.scala new file mode 100644 index 0000000000..0a0f2be02b --- /dev/null +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/io/SeqInputStreamForTest.scala @@ -0,0 +1,38 @@ +/* + * 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.javalib.io + +import java.io._ + +class SeqInputStreamForTest(seq: Seq[Int]) extends InputStream { + private var i: Int = 0 + private var m: Int = 0 + + def read(): Int = { + if (i < seq.length) { + val e = seq(i) + i += 1 + e & 0xFF + } else { + -1 + } + } + + override def available(): Int = seq.length - i + + override def mark(readlimit: Int): Unit = m = i + + override def reset(): Unit = i = m + + override def markSupported(): Boolean = true +} diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/BooleanTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/BooleanTest.scala index 44738f160f..b04a24b0ce 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/BooleanTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/BooleanTest.scala @@ -16,8 +16,10 @@ import java.lang.{Boolean => JBoolean} import org.junit.Test import org.junit.Assert._ +import org.junit.Assume._ import org.scalajs.testsuite.utils.AssertThrows.assertThrows +import org.scalajs.testsuite.utils.Platform._ /** Tests the implementation of the java standard library Boolean */ @@ -26,7 +28,12 @@ class BooleanTest { @Test def booleanValue(): Unit = { assertEquals(true, JBoolean.TRUE.booleanValue()) assertEquals(false, JBoolean.FALSE.booleanValue()) - assertThrows(classOf[Exception], (null: JBoolean).booleanValue()) + } + + @Test def booleanValueNull(): Unit = { + assumeTrue("assuming compliant null pointer checks", hasCompliantNullPointers) + + assertThrows(classOf[NullPointerException], (null: JBoolean).booleanValue()) } @Test def compareTo(): Unit = { diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/ClassTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/ClassTest.scala index 8afecbd5f9..ce1980840b 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/ClassTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/ClassTest.scala @@ -14,9 +14,11 @@ package org.scalajs.testsuite.javalib.lang import org.junit.Test import org.junit.Assert._ +import org.junit.Assume._ import scala.runtime.BoxedUnit +import org.scalajs.testsuite.utils.AssertThrows.assertThrows import org.scalajs.testsuite.utils.Platform._ class ClassTest { @@ -107,6 +109,17 @@ class ClassTest { test("[[[Ljava.lang.String;", new Array[Array[Array[String]]](1)) } + @Test def getClassGetNameNull(): Unit = { + // x.getClass().getName() is subject to optimizations + + assumeTrue("Assuming compliant null pointers", hasCompliantNullPointers) + + @noinline def getNull(): Any = null + + assertThrows(classOf[NullPointerException], (null: Any).getClass().getName()) + assertThrows(classOf[NullPointerException], getNull().getClass().getName()) + } + @Test def wellKnownClasses(): Unit = { assertSame(classOf[Unit], scala.runtime.BoxedUnit.TYPE) assertSame(classOf[Unit], java.lang.Void.TYPE) diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/IterableTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/IterableTest.scala index b2e12a42f5..74115aaf1f 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/IterableTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/IterableTest.scala @@ -22,6 +22,7 @@ import org.junit.Test import org.junit.Assert._ import org.scalajs.testsuite.utils.AssertThrows.assertThrows +import org.scalajs.testsuite.utils.Platform._ /** Tests the implementation of the java standard library Iterable */ diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/SystemArraycopyTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/SystemArraycopyTest.scala index 8c6bf1e963..97586bd058 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/SystemArraycopyTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/SystemArraycopyTest.scala @@ -185,6 +185,107 @@ class SystemArraycopyTest { assertArrayRefEquals(Array(null, "1", "2", null, "1", "1", null, "2", "1", null), array) } + @Test def arraycopyNulls(): Unit = { + assumeTrue("Assuming compliant NullPointers", + hasCompliantNullPointers) + + @noinline def assertThrowsNPE[U](body: => U): Unit = + assertThrows(classOf[NullPointerException], body) + + @noinline def getNull(): Any = null + val nul = getNull() + + val nullArrayRef = nul.asInstanceOf[Array[AnyRef]] + val nullArrayInt = nul.asInstanceOf[Array[Int]] + + val arrayRef = new Array[AnyRef](10) + val arrayInt = new Array[Int](10) + + val otherValues = List[Any]( + null, + arrayRef, + arrayInt, + new Array[String](10), + "foo", + (), + List(1) + ) + + for (otherValue <- otherValues) { + assertThrowsNPE(arraycopy(nul, 0, otherValue, 0, 0)) + assertThrowsNPE(arraycopy(otherValue, 0, nul, 0, 0)) + + assertThrowsNPE(arraycopy(nul, 0, otherValue, 0, 1)) + assertThrowsNPE(arraycopy(otherValue, 0, nul, 0, 1)) + + assertThrowsNPE(arraycopy(nul, -1, otherValue, 0, 1)) + assertThrowsNPE(arraycopy(otherValue, -1, nul, 0, 1)) + + assertThrowsNPE(arraycopy(nul, 5, otherValue, 0, 1)) + assertThrowsNPE(arraycopy(otherValue, 5, nul, 0, 1)) + + assertThrowsNPE(arraycopy(nul, 15, otherValue, 0, 1)) + assertThrowsNPE(arraycopy(otherValue, 15, nul, 0, 1)) + + assertThrowsNPE(arraycopy(nul, 0, otherValue, -1, 1)) + assertThrowsNPE(arraycopy(otherValue, 0, nul, -1, 1)) + + assertThrowsNPE(arraycopy(nul, 0, otherValue, 5, 1)) + assertThrowsNPE(arraycopy(otherValue, 0, nul, 5, 1)) + + assertThrowsNPE(arraycopy(nul, 0, otherValue, 15, 1)) + assertThrowsNPE(arraycopy(otherValue, 0, nul, 15, 1)) + + assertThrowsNPE(arraycopy(nullArrayRef, 0, otherValue, 0, 1)) + assertThrowsNPE(arraycopy(otherValue, 0, nullArrayRef, 0, 1)) + } + + assertThrowsNPE(arraycopy(nullArrayRef, 0, arrayRef, 5, 1)) + assertThrowsNPE(arraycopy(arrayRef, 0, nullArrayRef, 5, 1)) + + assertThrowsNPE(arraycopy(nullArrayInt, 0, arrayInt, 5, 1)) + assertThrowsNPE(arraycopy(arrayInt, 0, nullArrayInt, 5, 1)) + } + + @Test def arraycopyNullsShortcircuited(): Unit = { + @noinline def getNull(): Any = null + val nul = getNull() + + val nullArrayRef = nul.asInstanceOf[Array[AnyRef]] + val nullArrayInt = nul.asInstanceOf[Array[Int]] + + val arrayRef = new Array[AnyRef](10) + val arrayInt = new Array[Int](10) + + def throwIllegalArgNothing(): Nothing = + throw new IllegalArgumentException + + @noinline def assertThrowsIllegalArg[U](body: => U): Unit = + assertThrows(classOf[IllegalArgumentException], body) + + @noinline def throwIllegalArgArrayRef(): Array[AnyRef] = + throwIllegalArgNothing() + + @noinline def throwIllegalArgArrayInt(): Array[Int] = + throwIllegalArgNothing() + + @noinline def throwIllegalArgInt(): Int = + throwIllegalArgNothing() + + assertThrowsIllegalArg(arraycopy(nul, throwIllegalArgInt(), nul, 0, 0)) + assertThrowsIllegalArg(arraycopy(nul, 0, nul, 0, throwIllegalArgInt())) + + assertThrowsIllegalArg(arraycopy(nul, 0, throwIllegalArgArrayRef(), 0, 0)) + assertThrowsIllegalArg(arraycopy(nullArrayRef, 0, throwIllegalArgArrayRef(), 0, 0)) + assertThrowsIllegalArg(arraycopy(throwIllegalArgArrayRef(), 0, nul, 0, 0)) + assertThrowsIllegalArg(arraycopy(throwIllegalArgArrayRef(), 0, nullArrayRef, 0, 0)) + + assertThrowsIllegalArg(arraycopy(nul, 0, throwIllegalArgArrayInt(), 0, 0)) + assertThrowsIllegalArg(arraycopy(nullArrayInt, 0, throwIllegalArgArrayInt(), 0, 0)) + assertThrowsIllegalArg(arraycopy(throwIllegalArgArrayInt(), 0, nul, 0, 0)) + assertThrowsIllegalArg(arraycopy(throwIllegalArgArrayInt(), 0, nullArrayInt, 0, 0)) + } + @Test def arraycopyIndexOutOfBoundsInt(): Unit = { assumeTrue("Assuming compliant ArrayIndexOutOfBounds", hasCompliantArrayIndexOutOfBounds) diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/math/BigIntegerConvertTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/math/BigIntegerConvertTest.scala index 16f5073dd6..c383d455f4 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/math/BigIntegerConvertTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/math/BigIntegerConvertTest.scala @@ -25,6 +25,7 @@ import org.junit.Test import org.junit.Assert._ import org.junit.Assume._ +import org.scalajs.testsuite.utils.AssertThrows.assertThrows import org.scalajs.testsuite.utils.Platform._ class BigIntegerConvertTest { @@ -393,6 +394,42 @@ class BigIntegerConvertTest { assertEquals(result, aNumber, 0.0f) } + @Test def testIntValueExact1(): Unit = { + val aBytes = Array[Byte](12, 56, 100) + val resInt = 800868 + val aNumber = new BigInteger(aBytes).intValueExact() + assertEquals(resInt, aNumber) + } + + @Test def testIntValueExact2(): Unit = { + val aBytes = Array[Byte](12, 56, 100, -2, -76, 89, 45, 91, 3) + assertThrows(classOf[ArithmeticException], new BigInteger(aBytes).intValueExact()) + } + + @Test def testIntValueExact3(): Unit = { + val aBytes = Array[Byte](-128, 0, 0, 0) // Int.MinValue + val resInt = Int.MinValue + val aNumber = new BigInteger(aBytes).intValueExact() + assertEquals(resInt, aNumber) + } + + @Test def testIntValueExact4(): Unit = { + val aBytes = Array[Byte](-1, 127, -1, -1, -1) // Int.MinValue - 1 + assertThrows(classOf[ArithmeticException], new BigInteger(aBytes).intValueExact()) + } + + @Test def testIntValueExact5(): Unit = { + val aBytes = Array[Byte](127, -1, -1, -1) // Int.MaxValue + val resInt = Int.MaxValue + val aNumber = new BigInteger(aBytes).intValueExact() + assertEquals(resInt, aNumber) + } + + @Test def testIntValueExact6(): Unit = { + val aBytes = Array[Byte](0, -128, 0, 0, 0) // Int.MaxValue + 1 + assertThrows(classOf[ArithmeticException], new BigInteger(aBytes).intValueExact()) + } + @Test def testIntValueNegative1(): Unit = { val aBytes = Array[Byte](12, 56, 100, -2, -76, -128, 45, 91, 3) val sign = -1 @@ -438,6 +475,42 @@ class BigIntegerConvertTest { assertEquals(resInt, aNumber) } + @Test def testLongValueExact1(): Unit = { + val aBytes = Array[Byte](12, 56, 100, 18, -105, 34, -18, 45) + val result = 880563758158769709L + val aNumber = new BigInteger(aBytes).longValueExact() + assertEquals(result, aNumber) + } + + @Test def testIntLongExact2(): Unit = { + val aBytes = Array[Byte](12, 56, 100, -2, -76, 89, 45, 91, 3, 120, -34, -12, 45, 98) + assertThrows(classOf[ArithmeticException], new BigInteger(aBytes).longValueExact()) + } + + @Test def testLongValueExact3(): Unit = { + val aBytes = Array[Byte](-128, 0, 0, 0, 0, 0, 0, 0) // Long.MinValue + val result = Long.MinValue + val aNumber = new BigInteger(aBytes).longValueExact() + assertEquals(result, aNumber) + } + + @Test def testLongValueExact4(): Unit = { + val aBytes = Array[Byte](-1, 127, -1, -1, -1, -1, -1, -1, -1) // Long.MinValue - 1 + assertThrows(classOf[ArithmeticException], new BigInteger(aBytes).longValueExact()) + } + + @Test def testLongValueExact5(): Unit = { + val aBytes = Array[Byte](127, -1, -1, -1, -1, -1, -1, -1) // Long.MaxValue + val result = Long.MaxValue + val aNumber = new BigInteger(aBytes).longValueExact() + assertEquals(result, aNumber) + } + + @Test def testLongValueExact6(): Unit = { + val aBytes = Array[Byte](0, -128, 0, 0, 0, 0, 0, 0, 0) // Long.MaxValue + 1 + assertThrows(classOf[ArithmeticException], new BigInteger(aBytes).longValueExact()) + } + @Test def testLongValueNegative1(): Unit = { val aBytes = Array[Byte](12, -1, 100, -2, -76, -128, 45, 91, 3) val result = -43630045168837885L diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/ArraysTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/ArraysTest.scala index 43eebcfe21..c6c0579e13 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/ArraysTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/ArraysTest.scala @@ -45,10 +45,6 @@ class ArraysTest { /** Overridden by typedarray tests */ def Array[T: ClassTag](v: T*): scala.Array[T] = scala.Array(v: _*) - val stringComparator = new Comparator[String]() { - def compare(s1: String, s2: String): Int = s1.compareTo(s2) - } - @Test def sortInt(): Unit = testSort[Int](_.toInt, new Array(_), Arrays.sort(_), Arrays.sort(_, _, _)) @@ -124,15 +120,13 @@ class ArraysTest { val scalajs: Array[String] = Array("S", "c", "a", "l", "a", ".", "j", "s") val sorted = Array[String](".", "S", "a", "a", "c", "j", "l", "s") - Arrays.sort(scalajs, stringComparator) + Arrays.sort(scalajs, Comparator.naturalOrder[String]) assertArrayEquals(sorted, scalajs) } @Test def sortIsStable(): Unit = { case class A(n: Int) - val cmp = new Comparator[A]() { - def compare(a1: A, a2: A): Int = a1.n.compareTo(a2.n) - } + val cmp = Comparator.comparingInt((_: A).n) val scalajs: Array[A] = Array(A(1), A(2), A(2), A(3), A(1), A(2), A(3)) val sorted = Array[A](scalajs(0), scalajs(4), scalajs(1), scalajs(2), scalajs(5), scalajs(3), scalajs(6)) diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/CollectionTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/CollectionTest.scala index e43ed35ba3..787d88a4c3 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/CollectionTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/CollectionTest.scala @@ -19,7 +19,7 @@ import org.junit.Assert._ import org.scalajs.testsuite.javalib.lang.IterableFactory import org.scalajs.testsuite.javalib.lang.IterableTest -import org.scalajs.testsuite.utils.AssertThrows.assertThrows +import org.scalajs.testsuite.utils.AssertThrows.{assertThrows, _} import scala.reflect.ClassTag import Utils._ @@ -181,7 +181,7 @@ trait CollectionTest extends IterableTest { if (factory.allowsNullElementQuery) { assertFalse(coll.contains(null)) } else { - assertThrows(classOf[Exception], coll.contains(null)) + assertThrowsNPEIfCompliant(coll.contains(null)) } } diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/CollectionsOnCollectionsTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/CollectionsOnCollectionsTest.scala index c17416ecbc..e142c09556 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/CollectionsOnCollectionsTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/CollectionsOnCollectionsTest.scala @@ -80,15 +80,12 @@ trait CollectionsOnCollectionsTest extends CollectionsTestBase { } @Test def minWithComparator(): Unit = { - def test[T: ClassTag](toElem: Int => T, cmpFun: (T, T) => Int): Unit = { - testMinMax2(factory, toElem, true, new Comparator[T] { - override def compare(o1: T, o2: T): Int = cmpFun(o1, o2) - }) - } + def test[T: ClassTag](toElem: Int => T, cmp: Comparator[T]): Unit = + testMinMax2(factory, toElem, true, cmp) - test[jl.Integer](_.toInt, (x: jl.Integer, y: jl.Integer) => x.compareTo(y)) - test[jl.Long](_.toLong, (x: jl.Long, y: jl.Long) => x.compareTo(y)) - test[jl.Double](_.toDouble, (x: jl.Double, y: jl.Double) => x.compareTo(y)) + test[jl.Integer](_.toInt, Comparator.naturalOrder[jl.Integer]) + test[jl.Long](_.toLong, Comparator.naturalOrder[jl.Long]) + test[jl.Double](_.toDouble, Comparator.naturalOrder[jl.Double]) } @Test def maxOnComparables(): Unit = { @@ -101,15 +98,12 @@ trait CollectionsOnCollectionsTest extends CollectionsTestBase { } @Test def maxWithComparator(): Unit = { - def test[T: ClassTag](toElem: Int => T, cmpFun: (T, T) => Int): Unit = { - testMinMax2(factory, toElem, false, new Comparator[T] { - override def compare(o1: T, o2: T): Int = cmpFun(o1, o2) - }) - } + def test[T: ClassTag](toElem: Int => T, cmp: Comparator[T]): Unit = + testMinMax2(factory, toElem, false, cmp) - test[jl.Integer](_.toInt, (x: jl.Integer, y: jl.Integer) => x.compareTo(y)) - test[jl.Long](_.toLong, (x: jl.Long, y: jl.Long) => x.compareTo(y)) - test[jl.Double](_.toDouble, (x: jl.Double, y: jl.Double) => x.compareTo(y)) + test[jl.Integer](_.toInt, Comparator.naturalOrder[jl.Integer]) + test[jl.Long](_.toLong, Comparator.naturalOrder[jl.Long]) + test[jl.Double](_.toDouble, Comparator.naturalOrder[jl.Double]) } @Test def frequency(): Unit = { diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/ComparatorTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/ComparatorTest.scala index af8b3a3fde..1e9d1eb4e5 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/ComparatorTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/ComparatorTest.scala @@ -12,10 +12,15 @@ package org.scalajs.testsuite.javalib.util +import java.{util => ju} +import java.util.{function => juf} + import org.junit.Test import org.junit.Assert._ +import org.junit.Assume._ -import java.{util => ju} +import org.scalajs.testsuite.utils.AssertThrows._ +import org.scalajs.testsuite.utils.Platform class ComparatorTest { @@ -40,4 +45,191 @@ class ComparatorTest { assertTrue(reversed.compare(6, 8) > 0) } + @Test def reverseOrder(): Unit = { + val cmp = ju.Comparator.reverseOrder[String] + + assertEquals(0, cmp.compare("a", "a")) + assertTrue(cmp.compare("b", "a") < 0) + assertTrue(cmp.compare("a", "b") > 0) + } + + @Test def naturalOrder(): Unit = { + val cmp = ju.Comparator.naturalOrder[String] + + assertEquals(0, cmp.compare("a", "a")) + assertTrue(cmp.compare("b", "a") > 0) + assertTrue(cmp.compare("a", "b") < 0) + } + + @Test def nullsFirst(): Unit = { + val cmp = ju.Comparator.nullsFirst(ju.Comparator.naturalOrder[String]) + + assertEquals(0, cmp.compare("a", "a")) + assertEquals(0, cmp.compare(null, null)) + assertTrue(cmp.compare(null, "a") < 0) + assertTrue(cmp.compare("a", null) > 0) + } + + @Test def nullsFirstNull(): Unit = { + val cmp = ju.Comparator.nullsFirst(null) + + assertEquals(0, cmp.compare("a", "b")) + assertEquals(0, cmp.compare(null, null)) + assertTrue(cmp.compare(null, "a") < 0) + assertTrue(cmp.compare("a", null) > 0) + } + + @Test def nullsLast(): Unit = { + val cmp = ju.Comparator.nullsLast(ju.Comparator.naturalOrder[String]) + assertEquals(0, cmp.compare("a", "a")) + assertEquals(0, cmp.compare(null, null)) + assertTrue(cmp.compare(null, "a") > 0) + assertTrue(cmp.compare("a", null) < 0) + } + + @Test def nullsLastNull(): Unit = { + val cmp = ju.Comparator.nullsLast(null) + assertEquals(0, cmp.compare("a", "b")) + assertEquals(0, cmp.compare(null, null)) + assertTrue(cmp.compare(null, "a") > 0) + assertTrue(cmp.compare("a", null) < 0) + } + + @Test def comparing(): Unit = { + val cmp = ju.Comparator.comparing[String, String]( + (_.substring(1)): juf.Function[String, String], + ju.Comparator.reverseOrder[String]) + assertEquals(0, cmp.compare("ac", "bc")) + assertTrue(cmp.compare("ba", "ab") > 0) + assertTrue(cmp.compare("ab", "ba") < 0) + + assertThrowsNPEIfCompliant(ju.Comparator.comparing[String, String]( + null, ju.Comparator.reverseOrder[String])) + assertThrowsNPEIfCompliant(ju.Comparator.comparing( + (_.substring(1)): juf.Function[String, String], null)) + } + + @Test def comparingComparable(): Unit = { + val cmp = ju.Comparator.comparing[String, String]( + (_.substring(1)): juf.Function[String, String]) + assertEquals(0, cmp.compare("ac", "bc")) + assertTrue(cmp.compare("ba", "ab") < 0) + assertTrue(cmp.compare("ab", "ba") > 0) + + assertThrowsNPEIfCompliant( + ju.Comparator.comparing[String, String](null)) + } + + @Test def comparingInt(): Unit = { + val cmp = ju.Comparator.comparingInt((_: String).length) + assertEquals(0, cmp.compare("a", "b")) + assertTrue(cmp.compare("", "a") < 0) + assertTrue(cmp.compare("ab", "") > 0) + + assertThrowsNPEIfCompliant(ju.Comparator.comparingInt(null)) + } + + @Test def comparingLong(): Unit = { + val cmp = ju.Comparator.comparingLong((_: String).length.toLong) + assertEquals(0, cmp.compare("a", "b")) + assertTrue(cmp.compare("", "a") < 0) + assertTrue(cmp.compare("ab", "") > 0) + + assertThrowsNPEIfCompliant(ju.Comparator.comparingLong(null)) + } + + @Test def comparingDouble(): Unit = { + val cmp = ju.Comparator.comparingDouble((_: String).length.toDouble / 2) + assertEquals(0, cmp.compare("a", "b")) + assertTrue(cmp.compare("", "a") < 0) + assertTrue(cmp.compare("ab", "") > 0) + + assertThrowsNPEIfCompliant(ju.Comparator.comparingDouble(null)) + } + + @Test def thenComparingComparator(): Unit = { + val base = ju.Comparator.comparingInt((x: (Int, Int)) => x._1) + + val cmp = base.thenComparing( + ju.Comparator.comparingInt((x: (Int, Int)) => x._2)) + assertEquals(0, cmp.compare((1, 2), (1, 2))) + assertTrue(cmp.compare((1, 1), (1, 2)) < 0) + assertTrue(cmp.compare((1, 2), (1, 1)) > 0) + assertTrue(cmp.compare((1, 2), (2, 1)) < 0) + assertTrue(cmp.compare((2, 1), (1, 2)) > 0) + + assertThrowsNPEIfCompliant(base.thenComparing(null: ju.Comparator[(Int, Int)])) + } + + @Test def thenComparingExtractorComparator(): Unit = { + val base = ju.Comparator.comparingInt((x: (Int, String)) => x._1) + + val cmp = base.thenComparing[String]( + ((x: (Int, String)) => x._2): juf.Function[(Int, String), String], + ju.Comparator.reverseOrder[String]) + assertEquals(0, cmp.compare((1, "a"), (1, "a"))) + assertTrue(cmp.compare((1, "a"), (1, "b")) > 0) + assertTrue(cmp.compare((1, "b"), (1, "a")) < 0) + assertTrue(cmp.compare((1, "b"), (2, "a")) < 0) + assertTrue(cmp.compare((2, "a"), (1, "b")) > 0) + + assertThrowsNPEIfCompliant(base.thenComparing[String]( + null, ju.Comparator.reverseOrder[String])) + assertThrowsNPEIfCompliant(base.thenComparing[String]( + ((_: (Int, String))._2): juf.Function[(Int, String), String], null)) + } + + @Test def thenComparingExtractor(): Unit = { + val base = ju.Comparator.comparingInt((x: (Int, String)) => x._1) + + val cmp = base.thenComparing[String]( + ((x: (Int, String)) => x._2): juf.Function[(Int, String), String]) + assertEquals(0, cmp.compare((1, "a"), (1, "a"))) + assertTrue(cmp.compare((1, "a"), (1, "b")) < 0) + assertTrue(cmp.compare((1, "b"), (1, "a")) > 0) + assertTrue(cmp.compare((1, "b"), (2, "a")) < 0) + assertTrue(cmp.compare((2, "a"), (1, "b")) > 0) + + assertThrowsNPEIfCompliant(base.thenComparing[String]( + null: juf.Function[(Int, String), String])) + } + + @Test def thenComparingInt(): Unit = { + val base = ju.Comparator.comparingInt((x: (Int, Int)) => x._1) + + val cmp = base.thenComparingInt((x: (Int, Int)) => x._2) + assertEquals(0, cmp.compare((1, 2), (1, 2))) + assertTrue(cmp.compare((1, 1), (1, 2)) < 0) + assertTrue(cmp.compare((1, 2), (1, 1)) > 0) + assertTrue(cmp.compare((1, 2), (2, 1)) < 0) + assertTrue(cmp.compare((2, 1), (1, 2)) > 0) + + assertThrowsNPEIfCompliant(base.thenComparingInt(null)) + } + + @Test def thenComparingLong(): Unit = { + val base = ju.Comparator.comparingInt((x: (Int, Int)) => x._1) + + val cmp = base.thenComparingLong((x: (Int, Int)) => x._2.toLong) + assertEquals(0, cmp.compare((1, 2), (1, 2))) + assertTrue(cmp.compare((1, 1), (1, 2)) < 0) + assertTrue(cmp.compare((1, 2), (1, 1)) > 0) + assertTrue(cmp.compare((1, 2), (2, 1)) < 0) + assertTrue(cmp.compare((2, 1), (1, 2)) > 0) + + assertThrowsNPEIfCompliant(base.thenComparingLong(null)) + } + + @Test def thenComparingDouble(): Unit = { + val base = ju.Comparator.comparingInt((x: (Int, Int)) => x._1) + + val cmp = base.thenComparingDouble((x: (Int, Int)) => x._2.toDouble / 2) + assertEquals(0, cmp.compare((1, 2), (1, 2))) + assertTrue(cmp.compare((1, 1), (1, 2)) < 0) + assertTrue(cmp.compare((1, 2), (1, 1)) > 0) + assertTrue(cmp.compare((1, 2), (2, 1)) < 0) + assertTrue(cmp.compare((2, 1), (1, 2)) > 0) + + assertThrowsNPEIfCompliant(base.thenComparingDouble(null)) + } } diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/MapTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/MapTest.scala index 854ad02dab..6a401ad58c 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/MapTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/MapTest.scala @@ -20,7 +20,7 @@ import org.junit.Assert._ import org.junit.Assume._ import org.scalajs.testsuite.javalib.util.concurrent.ConcurrentMapFactory -import org.scalajs.testsuite.utils.AssertThrows.assertThrows +import org.scalajs.testsuite.utils.AssertThrows.{assertThrows, _} import org.scalajs.testsuite.utils.Platform._ import scala.reflect.ClassTag @@ -54,12 +54,14 @@ trait MapTest { assertEquals("three", mp.get("ONE")) assertEquals(null, mp.get("THREE")) - assertEquals(null, mp.get(42)) - assertEquals(null, mp.get(testObj(42))) + if (factory.allowsSupertypeKeyQueries) { + assertEquals(null, mp.get(42)) + assertEquals(null, mp.get(testObj(42))) + } if (factory.allowsNullKeysQueries) assertEquals(null, mp.get(null)) else - assertThrows(classOf[NullPointerException], mp.get(null)) + assertThrowsNPEIfCompliant(mp.get(null)) } @Test def testSizeGetPutWithStringsLargeMap(): Unit = { @@ -75,8 +77,10 @@ trait MapTest { assertNull(largeMap.get("1000")) assertEquals(null, largeMap.get("THREE")) - assertEquals(null, largeMap.get(42)) - assertEquals(null, largeMap.get(testObj(42))) + if (factory.allowsSupertypeKeyQueries) { + assertEquals(null, largeMap.get(42)) + assertEquals(null, largeMap.get(testObj(42))) + } if (factory.allowsNullKeysQueries) assertEquals(null, largeMap.get(null)) } @@ -97,8 +101,10 @@ trait MapTest { assertEquals(3, mp.get(100)) assertEquals(null, mp.get(42)) - assertEquals(null, mp.get("THREE")) - assertEquals(null, mp.get(testObj(42))) + if (factory.allowsSupertypeKeyQueries) { + assertEquals(null, mp.get("THREE")) + assertEquals(null, mp.get(testObj(42))) + } if (factory.allowsNullKeysQueries) assertEquals(null, mp.get(null)) } @@ -116,8 +122,10 @@ trait MapTest { assertNull(largeMap.get(1000)) assertEquals(null, largeMap.get(-42)) - assertEquals(null, largeMap.get("THREE")) - assertEquals(null, largeMap.get(testObj(42))) + if (factory.allowsSupertypeKeyQueries) { + assertEquals(null, largeMap.get("THREE")) + assertEquals(null, largeMap.get(testObj(42))) + } if (factory.allowsNullKeysQueries) assertEquals(null, largeMap.get(null)) } @@ -135,9 +143,11 @@ trait MapTest { assertEquals(2, mp.size()) assertEquals(3, mp.get(testObj(100)).num) - assertEquals(null, mp.get("THREE")) - assertEquals(null, mp.get(42)) assertEquals(null, mp.get(testObj(42))) + if (factory.allowsSupertypeKeyQueries) { + assertEquals(null, mp.get("THREE")) + assertEquals(null, mp.get(42)) + } if (factory.allowsNullKeysQueries) assertEquals(null, mp.get(null)) } @@ -150,11 +160,13 @@ trait MapTest { assertEquals(expectedSize, largeMap.size()) for (i <- (1000 - expectedSize) until 1000) assertEquals(i * 2, largeMap.get(testObj(i))) - assertNull(largeMap.get(1000)) + assertNull(largeMap.get(testObj(1000))) assertEquals(null, largeMap.get(testObj(-42))) - assertEquals(null, largeMap.get("THREE")) - assertEquals(null, largeMap.get(42)) + if (factory.allowsSupertypeKeyQueries) { + assertEquals(null, largeMap.get("THREE")) + assertEquals(null, largeMap.get(42)) + } if (factory.allowsNullKeysQueries) assertEquals(null, largeMap.get(null)) } @@ -197,12 +209,14 @@ trait MapTest { assertNull(mp.remove("ONE")) assertNull(mp.remove("foobar")) - assertNull(mp.remove(42)) - assertNull(mp.remove(testObj(42))) + if (factory.allowsSupertypeKeyQueries) { + assertNull(mp.remove(42)) + assertNull(mp.remove(testObj(42))) + } if (factory.allowsNullKeys) assertNull(mp.remove(null)) else - assertThrows(classOf[NullPointerException], mp.remove(null)) + assertThrowsNPEIfCompliant(mp.remove(null)) } @Test def testRemoveWithInts(): Unit = { @@ -218,9 +232,11 @@ trait MapTest { assertNull(mp.get(543)) assertNull(mp.remove(543)) - assertNull(mp.remove("foobar")) assertNull(mp.remove(42)) - assertNull(mp.remove(testObj(42))) + if (factory.allowsSupertypeKeyQueries) { + assertNull(mp.remove("foobar")) + assertNull(mp.remove(testObj(42))) + } if (factory.allowsNullKeys) assertNull(mp.remove(null)) } @@ -237,8 +253,10 @@ trait MapTest { assertNull(mp.remove(testObj(543))) assertNull(mp.remove(testObj(42))) - assertNull(mp.remove("foobar")) - assertNull(mp.remove(42)) + if (factory.allowsSupertypeKeyQueries) { + assertNull(mp.remove("foobar")) + assertNull(mp.remove(42)) + } if (factory.allowsNullKeys) assertNull(mp.remove(null)) } @@ -291,7 +309,7 @@ trait MapTest { assertNull(mp.get(null)) assertNull(mp.remove(null)) } else { - assertThrows(classOf[NullPointerException], mp.put(null, "one")) + assertThrowsNPEIfCompliant(mp.put(null, "one")) } } @@ -308,7 +326,7 @@ trait MapTest { assertEquals(30, mp.size()) assertNull(mp.get("one")) } else { - assertThrows(classOf[NullPointerException], mp.put("one", null)) + assertThrowsNPEIfCompliant(mp.put("one", null)) } } @@ -348,7 +366,7 @@ trait MapTest { if (factory.allowsNullKeysQueries) assertFalse(mp.containsKey(null)) else - assertThrows(classOf[NullPointerException], mp.containsKey(null)) + assertThrowsNPEIfCompliant(mp.containsKey(null)) } @Test def testContainsValue(): Unit = { @@ -360,7 +378,7 @@ trait MapTest { if (factory.allowsNullValuesQueries) assertFalse(mp.containsValue(null)) else - assertThrows(classOf[NullPointerException], mp.containsValue(null)) + assertThrowsNPEIfCompliant(mp.containsValue(null)) } @Test def testPutAll(): Unit = { @@ -382,7 +400,7 @@ trait MapTest { assertEquals("one", mp.get("ONE")) assertEquals("b", mp.get("A")) } else { - assertThrows(classOf[NullPointerException], mp.putAll(nullMap)) + assertThrowsNPEIfCompliant(mp.putAll(nullMap)) } } @@ -453,7 +471,7 @@ trait MapTest { if (factory.allowsNullValuesQueries) assertFalse(values.contains(null)) else - assertThrows(classOf[NullPointerException], values.contains(null)) + assertThrowsNPEIfCompliant(values.contains(null)) mp.put("THREE", "three") @@ -483,7 +501,7 @@ trait MapTest { if (factory.allowsNullValuesQueries) assertFalse(values.contains(null)) else - assertThrows(classOf[NullPointerException], values.contains(null)) + assertThrowsNPEIfCompliant(values.contains(null)) mp.put(testObj(3), testObj(33)) @@ -653,7 +671,7 @@ trait MapTest { if (factory.allowsNullKeysQueries) assertFalse(keySet.contains(null)) else - assertThrows(classOf[NullPointerException], keySet.contains(null)) + assertThrowsNPEIfCompliant(keySet.contains(null)) mp.put("THREE", "three") @@ -683,7 +701,7 @@ trait MapTest { if (factory.allowsNullKeysQueries) assertFalse(keySet.contains(null)) else - assertThrows(classOf[NullPointerException], keySet.contains(null)) + assertThrowsNPEIfCompliant(keySet.contains(null)) mp.put(testObj(3), TestObj(33)) @@ -889,9 +907,12 @@ trait MapTest { assertFalse(entrySet.contains(SIE("THREE", "three"))) assertFalse(entrySet.contains(SIE("ONE", "two"))) assertFalse(entrySet.contains(SIE("THREE", "one"))) + + if (factory.allowsNullKeysQueries) + assertTrue(entrySet.contains(SIE(null, "NULL"))) + if (factory.allowsNullValuesQueries) { assertTrue(entrySet.contains(SIE("NULL", null))) - assertTrue(entrySet.contains(SIE(null, "NULL"))) assertFalse(entrySet.contains(SIE("NOTFOUND", null))) } @@ -1183,7 +1204,7 @@ trait MapTest { assertNull(mp.get("ONE")) assertEquals("it was null", mp.get("nullable")) } else { - assertThrows(classOf[NullPointerException], mp.replaceAll(new BiFunction[String, String, String] { + assertThrowsNPEIfCompliant(mp.replaceAll(new BiFunction[String, String, String] { def apply(key: String, value: String): String = null })) } @@ -1205,12 +1226,12 @@ trait MapTest { assertNull(mp.putIfAbsent("nullable", "non null")) assertEquals("non null", mp.get("nullable")) } else { - assertThrows(classOf[NullPointerException], mp.putIfAbsent("abc", null)) - assertThrows(classOf[NullPointerException], mp.putIfAbsent("new key", null)) + assertThrowsNPEIfCompliant(mp.putIfAbsent("abc", null)) + assertThrowsNPEIfCompliant(mp.putIfAbsent("new key", null)) } if (!factory.allowsNullKeys) { - assertThrows(classOf[NullPointerException], mp.putIfAbsent(null, "def")) + assertThrowsNPEIfCompliant(mp.putIfAbsent(null, "def")) } } @@ -1235,7 +1256,7 @@ trait MapTest { assertTrue(mp.remove(null, "one")) assertFalse(mp.containsKey(null)) } else { - assertThrows(classOf[NullPointerException], mp.remove(null, "old value")) + assertThrowsNPEIfCompliant(mp.remove(null, "old value")) } if (factory.allowsNullValues) { @@ -1265,7 +1286,7 @@ trait MapTest { assertEquals("one", mp.remove(null)) assertFalse(mp.containsKey(null)) } else { - assertThrows(classOf[NullPointerException], mp.remove(null)) + assertThrowsNPEIfCompliant(mp.remove(null)) } } @@ -1292,8 +1313,8 @@ trait MapTest { assertTrue(mp.containsKey("nullable")) assertNull(mp.get("nullable")) } else { - assertThrows(classOf[NullPointerException], mp.replace("ONE", null, "one")) - assertThrows(classOf[NullPointerException], mp.replace("ONE", "four", null)) + assertThrowsNPEIfCompliant(mp.replace("ONE", null, "one")) + assertThrowsNPEIfCompliant(mp.replace("ONE", "four", null)) } if (factory.allowsNullKeys) { @@ -1304,7 +1325,7 @@ trait MapTest { assertTrue(mp.replace(null, "null value", "new value")) assertEquals("new value", mp.get(null)) } else { - assertThrows(classOf[NullPointerException], mp.replace(null, "one", "two")) + assertThrowsNPEIfCompliant(mp.replace(null, "one", "two")) } } @@ -1326,7 +1347,7 @@ trait MapTest { assertNull(mp.replace("ONE", "new one")) assertEquals("new one", mp.get("ONE")) } else { - assertThrows(classOf[NullPointerException], mp.replace("ONE", null)) + assertThrowsNPEIfCompliant(mp.replace("ONE", null)) assertEquals("four", mp.get("ONE")) } @@ -1338,7 +1359,7 @@ trait MapTest { assertEquals("null value", mp.replace(null, "new value")) assertEquals("new value", mp.get(null)) } else { - assertThrows(classOf[NullPointerException], mp.replace(null, "one")) + assertThrowsNPEIfCompliant(mp.replace(null, "one")) } } @@ -1366,6 +1387,13 @@ trait MapTest { assertFalse(mp.containsKey("non existing")) if (factory.allowsNullValues) { + /* JDK 15 & 16 are affected by + * https://bugs.openjdk.org/browse/JDK-8259622 + */ + assumeFalse("affected by JDK-8259622", + executingInJVMOnLowerThanJDK17 && !executingInJVMOnLowerThanJDK15 && + mp.isInstanceOf[ju.TreeMap[_, _]]) + mp.put("nullable", null) assertEquals("8", mp.computeIfAbsent("nullable", lengthAsString)) assertEquals("8", mp.get("nullable")) @@ -1461,8 +1489,8 @@ trait MapTest { assertEquals("def", mp.merge("SEVEN", "def", notCalled)) assertEquals("def", mp.get("SEVEN")) - assertThrows(classOf[NullPointerException], mp.merge("non existing", null, notCalled)) - assertThrows(classOf[NullPointerException], mp.merge("ONE", null, notCalled)) + assertThrowsNPEIfCompliant(mp.merge("non existing", null, notCalled)) + assertThrowsNPEIfCompliant(mp.merge("ONE", null, notCalled)) assertNull(mp.merge("ONE", "def", returnsNull)) assertFalse(mp.containsKey("ONE")) @@ -1485,7 +1513,9 @@ trait MapTest { } object MapTest { - final case class TestObj(num: Int) + final case class TestObj(num: Int) extends Comparable[TestObj] { + def compareTo(that: TestObj): Int = this.num - that.num + } } trait MapFactory { @@ -1508,6 +1538,8 @@ trait MapFactory { def allowsNullValuesQueries: Boolean = true + def allowsSupertypeKeyQueries: Boolean = false + def withSizeLimit: Option[Int] = None def isIdentityBased: Boolean = false diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/NavigableMapTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/NavigableMapTest.scala new file mode 100644 index 0000000000..cd1c61e6a0 --- /dev/null +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/NavigableMapTest.scala @@ -0,0 +1,271 @@ +/* + * 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.javalib.util + +import java.{util => ju} + +import org.junit.Test +import org.junit.Assert._ + +import scala.reflect.ClassTag + +import org.scalajs.testsuite.utils.AssertThrows.{assertThrows, _} + +trait NavigableMapTest extends SortedMapTest { + + def factory: NavigableMapFactory + + private def newMapForTest() = { + val m = factory.empty[Int, String] + m.putAll(TrivialImmutableMap(1 -> "a", 5 -> "b", 2 -> "c", 3 -> "d")) + m + } + + @Test + def lowerEntry(): Unit = { + val m = newMapForTest() + + assertNull(m.lowerEntry(1)) + + val e = m.lowerEntry(2) + + assertEquals(1, e.getKey()) + assertEquals("a", e.getValue()) + + assertEquals(3, m.lowerEntry(4).getKey()) + } + + @Test + def lowerKey(): Unit = { + val m = newMapForTest() + + assertNull(m.lowerKey(1)) + assertEquals(1, m.lowerKey(2)) + assertEquals(3, m.lowerKey(4)) + } + + @Test + def floorEntry(): Unit = { + val m = newMapForTest() + + assertNull(m.floorEntry(0)) + + val e = m.floorEntry(2) + + assertEquals(2, e.getKey()) + assertEquals("c", e.getValue()) + + assertEquals(3, m.floorEntry(4).getKey()) + } + + @Test + def floorKey(): Unit = { + val m = newMapForTest() + + assertNull(m.floorKey(0)) + assertEquals(2, m.floorKey(2)) + assertEquals(3, m.floorKey(4)) + } + + @Test + def ceilingEntry(): Unit = { + val m = newMapForTest() + + assertNull(m.ceilingEntry(6)) + + val e = m.ceilingEntry(2) + + assertEquals(2, e.getKey()) + assertEquals("c", e.getValue()) + + assertEquals(5, m.ceilingEntry(4).getKey()) + } + + @Test + def ceilingKey(): Unit = { + val m = newMapForTest() + + assertNull(m.ceilingKey(6)) + assertEquals(2, m.ceilingKey(2)) + assertEquals(5, m.ceilingKey(4)) + } + + @Test + def higherEntry(): Unit = { + val m = newMapForTest() + + assertNull(m.higherEntry(6)) + + val e = m.higherEntry(2) + + assertEquals(3, e.getKey()) + assertEquals("d", e.getValue()) + + assertEquals(5, m.higherEntry(4).getKey()) + } + + @Test + def higherKey(): Unit = { + val m = newMapForTest() + + assertNull(m.higherKey(6)) + assertEquals(3, m.higherKey(2)) + assertEquals(5, m.higherKey(4)) + } + + @Test + def firstEntry(): Unit = { + assertNull(factory.empty[String, String].firstEntry()) + + val e = newMapForTest().firstEntry() + assertEquals(1, e.getKey()) + assertEquals("a", e.getValue()) + } + + @Test + def lastEntry(): Unit = { + assertNull(factory.empty[String, String].lastEntry()) + + val e = newMapForTest().lastEntry() + assertEquals(5, e.getKey()) + assertEquals("b", e.getValue()) + } + + @Test + def pollFirstEntry(): Unit = { + val em = factory.empty[String, String] + assertNull(em.pollFirstEntry()) + assertTrue(em.isEmpty()) + + val m = newMapForTest() + val e = m.pollFirstEntry() + assertEquals(1, e.getKey()) + assertEquals("a", e.getValue()) + assertEquals(3, m.size()) + } + + @Test + def pollLastEntry(): Unit = { + val em = factory.empty[String, String] + assertNull(em.pollLastEntry()) + assertTrue(em.isEmpty()) + + val m = newMapForTest() + val e = m.pollLastEntry() + assertEquals(5, e.getKey()) + assertEquals("b", e.getValue()) + assertEquals(3, m.size()) + } + + @Test + def descendingMap(): Unit = { + val m = newMapForTest() + val r = m.descendingMap() + + assertEquals(1, r.pollLastEntry().getKey()) + assertEquals(2, m.firstKey()) + } + + @Test + def navigableKeySet(): Unit = { + val m = newMapForTest() + val s = m.navigableKeySet() + + assertEquals(5, s.pollLast()) + assertEquals(3, m.lastKey()) + } + + @Test + def descendingKeySet(): Unit = { + val m = newMapForTest() + val s = m.descendingKeySet() + + assertEquals(1, s.pollLast()) + assertEquals(2, m.firstKey()) + } + + @Test def navigableSubMap(): Unit = { + val m = newMapForTest() + + val sm = m.subMap(2, true, 4, false) + assertEquals(2, sm.size()) + assertTrue(sm.containsKey(2)) + assertTrue(sm.containsKey(3)) + + assertThrows(classOf[IllegalArgumentException], sm.put(4, "a")) + assertThrows(classOf[IllegalArgumentException], sm.put(1, "a")) + + assertEquals("c", sm.remove(2)) + assertFalse(m.containsKey(2)) + + assertEquals("d", sm.remove(3)) + assertFalse(m.containsKey(3)) + + assertTrue(sm.isEmpty()) + assertEquals(2, m.size()) + + assertEquals(1, newMapForTest().subMap(2, false, 4, false).size()) + assertEquals(1, newMapForTest().subMap(2, false, 4, true).size()) + assertEquals(2, newMapForTest().subMap(2, true, 4, true).size()) + } + + @Test def navigableHeadMap(): Unit = { + val m = newMapForTest() + + val sm = m.headMap(4, false) + assertEquals(3, sm.size()) + assertTrue(sm.containsKey(1)) + assertTrue(sm.containsKey(2)) + assertTrue(sm.containsKey(3)) + + assertThrows(classOf[IllegalArgumentException], sm.put(4, "a")) + + assertEquals("c", sm.remove(2)) + assertFalse(m.containsKey(2)) + + assertEquals("d", sm.remove(3)) + assertFalse(m.containsKey(3)) + + assertEquals(1, sm.size()) + assertEquals(2, m.size) + + assertEquals(2, newMapForTest().headMap(3, false).size()) + assertEquals(3, newMapForTest().headMap(3, true).size()) + } + + @Test def navigableTailMap(): Unit = { + val m = newMapForTest() + + val sm = m.tailMap(2, false) + assertEquals(2, sm.size()) + assertTrue(sm.containsKey(3)) + assertTrue(sm.containsKey(5)) + + assertThrows(classOf[IllegalArgumentException], sm.put(2, "a")) + + assertEquals("d", sm.remove(3)) + assertFalse(m.containsKey(3)) + + assertEquals("b", sm.remove(5)) + assertFalse(m.containsKey(5)) + + assertEquals(0, sm.size()) + assertEquals(2, m.size) + + assertEquals(3, newMapForTest().tailMap(2, true).size()) + } +} + +trait NavigableMapFactory extends SortedMapFactory { + def empty[K: ClassTag, V: ClassTag]: ju.NavigableMap[K, V] +} diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/PriorityQueueTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/PriorityQueueTest.scala index 26cca2047f..da64a857cb 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/PriorityQueueTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/PriorityQueueTest.scala @@ -66,10 +66,7 @@ class PriorityQueueTest extends CollectionTest { @Test def addAndRemoveObjectWithCustomComparator(): Unit = { case class Rect(x: Int, y: Int) - val areaComp = new Comparator[Rect] { - def compare(a: Rect, b: Rect): Int = (a.x*a.y) - (b.x*b.y) - } - + val areaComp = Comparator.comparingInt((r: Rect) => r.x * r.y) val pq = new PriorityQueue[Rect](11, areaComp) assertTrue(pq.add(Rect(1,2))) diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/SetTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/SetTest.scala index 631545db99..1df9f5ce45 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/SetTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/SetTest.scala @@ -15,7 +15,7 @@ package org.scalajs.testsuite.javalib.util import org.junit.Test import org.junit.Assert._ -import org.scalajs.testsuite.utils.AssertThrows.assertThrows +import org.scalajs.testsuite.utils.AssertThrows.{assertThrows, _} import java.{util => ju, lang => jl} @@ -164,7 +164,7 @@ trait SetTest extends CollectionTest { assertTrue(hs.add(null)) assertTrue(hs.contains(null)) } else { - assertThrows(classOf[Exception], hs.add(null)) + assertThrowsNPEIfCompliant(hs.add(null)) } } @@ -180,7 +180,7 @@ trait SetTest extends CollectionTest { assertTrue(hs.contains("TWO")) assertTrue(hs.contains(null)) } else { - assertThrows(classOf[Exception], hs.addAll(l)) + assertThrowsNPEIfCompliant(hs.addAll(l)) } } diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/SortedMapTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/SortedMapTest.scala index 5addb1e67b..081f49f18b 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/SortedMapTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/SortedMapTest.scala @@ -23,30 +23,87 @@ trait SortedMapTest extends MapTest { def factory: SortedMapFactory - // TODO: implement tests (when we port the first SortedMap) - - @Test def sort(): Unit = { + @Test def sorted(): Unit = { + val m = factory.empty[Int, String] + m.putAll(TrivialImmutableMap(1 -> "a", 5 -> "b", 2 -> "c", 3 -> "d", 4 -> "e")) + assertArrayEquals(Array[AnyRef]("a", "c", "d", "e", "b"), m.values().toArray) } @Test def firstKey(): Unit = { - + val m = factory.empty[Int, String] + m.put(1000, "a") + m.put(10, "b") + assertEquals(10, m.firstKey()) } @Test def lastKey(): Unit = { - + val m = factory.empty[Int, String] + m.put(1000, "a") + m.put(10, "b") + assertEquals(1000, m.lastKey()) } + val elems = TrivialImmutableMap(1 -> "a", 5 -> "e", 2 -> "b", 3 -> "c", 4 -> "d") + @Test def headMap(): Unit = { + val m = factory.empty[Int, String] + + m.putAll(elems) + + val sm = m.headMap(3) + assertEquals(2, sm.size()) + assertTrue(sm.containsKey(1)) + assertTrue(sm.containsKey(2)) + + assertEquals("a", sm.remove(1)) + assertFalse(m.containsKey(1)) + + assertEquals("b", sm.remove(2)) + assertFalse(m.containsKey(2)) + assertTrue(sm.isEmpty()) + assertEquals(3, m.size) } @Test def tailMap(): Unit = { + val m = factory.empty[Int, String] + m.putAll(elems) + + val sm = m.tailMap(4) + assertEquals(2, sm.size()) + assertTrue(sm.containsKey(4)) + assertTrue(sm.containsKey(5)) + + assertEquals("d", sm.remove(4)) + assertFalse(m.containsKey(4)) + + assertEquals("e", sm.remove(5)) + assertFalse(m.containsKey(5)) + + assertTrue(sm.isEmpty()) + assertEquals(3, m.size) } @Test def subMap(): Unit = { + val m = factory.empty[Int, String] + + m.putAll(elems) + + val sm = m.subMap(2, 4) + assertEquals(2, sm.size()) + assertTrue(sm.containsKey(2)) + assertTrue(sm.containsKey(3)) + + assertEquals("b", sm.remove(2)) + assertFalse(m.containsKey(2)) + + assertEquals("c", sm.remove(3)) + assertFalse(m.containsKey(3)) + assertTrue(sm.isEmpty()) + assertEquals(3, m.size) } } diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/TreeMapTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/TreeMapTest.scala new file mode 100644 index 0000000000..bcdcfaaed2 --- /dev/null +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/TreeMapTest.scala @@ -0,0 +1,76 @@ +/* + * 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.javalib.util + +import java.{util => ju} +import java.util.function.{BiConsumer, BiFunction, Function} + +import org.junit.Test +import org.junit.Assert._ +import org.junit.Assume._ + +import org.scalajs.testsuite.javalib.util.concurrent.ConcurrentMapFactory +import org.scalajs.testsuite.utils.AssertThrows.assertThrows +import org.scalajs.testsuite.utils.Platform._ + +import scala.reflect.ClassTag + +import Utils._ + +abstract class TreeMapTest(val factory: TreeMapFactory) + extends AbstractMapTest + with NavigableMapTest { + + @Test + def comparator(): Unit = { + assertNull(new ju.TreeMap[String, String]().comparator()) + + val cmp = ju.Comparator.naturalOrder[String]() + + assertSame(cmp, new ju.TreeMap[String, String](cmp).comparator()) + } +} + +class TreeMapWithoutNullTest extends TreeMapTest(new TreeMapFactory) + +class TreeMapWithNullTest extends TreeMapTest(new TreeMapWithNullFactory) + +class TreeMapFactory extends AbstractMapFactory with NavigableMapFactory { + def implementationName: String = "java.util.TreeMap" + + def empty[K: ClassTag, V: ClassTag]: ju.TreeMap[K, V] = + new ju.TreeMap[K, V] + + def allowsNullKeys: Boolean = false + + def allowsNullValues: Boolean = true + + override def allowsNullKeysQueries: Boolean = false + + override def allowsSupertypeKeyQueries: Boolean = false +} + +class TreeMapWithNullFactory extends TreeMapFactory { + override def implementationName: String = + super.implementationName + " (allows nulls)" + + override def empty[K: ClassTag, V: ClassTag]: ju.TreeMap[K, V] = { + val natural = ju.Comparator.comparing[K, Comparable[Any]]( + ((_: K).asInstanceOf[Comparable[Any]]): Function[K, Comparable[Any]]) + new ju.TreeMap[K, V](ju.Comparator.nullsFirst(natural)) + } + + override def allowsNullKeys: Boolean = true + + override def allowsNullKeysQueries: Boolean = true +} diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/TreeSetTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/TreeSetTest.scala index 483ad099a6..133890c559 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/TreeSetTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/TreeSetTest.scala @@ -18,7 +18,7 @@ import org.junit.Test import org.junit.Assert._ import org.junit.Assume._ -import org.scalajs.testsuite.utils.AssertThrows.assertThrows +import org.scalajs.testsuite.utils.AssertThrows.{assertThrows, _} import org.scalajs.testsuite.utils.Platform._ import java.{util => ju} @@ -27,6 +27,16 @@ import ju.Comparator import scala.reflect.ClassTag +class TreeSetComparatorTest { + + @Test def naturalComparator_issue4796(): Unit = { + val cmp = ju.Comparator.naturalOrder[String]() + + assertSame(cmp, new TreeSet[String](cmp).comparator()) + } + +} + class TreeSetWithoutNullTest extends TreeSetTest(new TreeSetFactory) { @Test def comparatorNull(): Unit = { @@ -254,7 +264,7 @@ abstract class TreeSetTest(val factory: TreeSetFactory) assertTrue(hs.add(null)) assertTrue(hs.contains(null)) } else { - assertThrows(classOf[Exception], hs.add(null)) + assertThrowsNPEIfCompliant(hs.add(null)) } } @@ -268,7 +278,7 @@ abstract class TreeSetTest(val factory: TreeSetFactory) assertTrue(ts1.contains("ONE")) assertFalse(ts1.contains("THREE")) } else { - assertThrows(classOf[Exception], ts1.addAll(l)) + assertThrowsNPEIfCompliant(ts1.addAll(l)) } } @@ -360,18 +370,11 @@ class TreeSetWithNullFactory extends TreeSetFactory { override def implementationName: String = super.implementationName + " {allows null}" - case class EvenNullComp[E]() extends Comparator[E] { - def compare(a: E, b: E): Int = - (Option(a), Option(b)) match { - case (Some(e1), Some(e2)) => e1.asInstanceOf[Comparable[E]].compareTo(e2) - case (Some(e1), None) => -1 - case (None, Some(e2)) => 1 - case (None, None) => 0 - } - } - - override def empty[E: ClassTag]: ju.TreeSet[E] = - new TreeSet[E](EvenNullComp[E]()) + override def empty[E: ClassTag]: ju.TreeSet[E] = { + val natural = Comparator.comparing[E, Comparable[Any]]( + ((_: E).asInstanceOf[Comparable[Any]]): ju.function.Function[E, Comparable[Any]]) + new TreeSet[E](Comparator.nullsFirst(natural)) + } override def allowsNullElement: Boolean = true diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/UUIDTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/UUIDTest.scala index dbc078b682..4657b5741a 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/UUIDTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/UUIDTest.scala @@ -13,11 +13,13 @@ package org.scalajs.testsuite.javalib.util import org.junit.Assert._ +import org.junit.Assume._ import org.junit.Test import java.util.UUID import org.scalajs.testsuite.utils.AssertThrows.assertThrows +import org.scalajs.testsuite.utils.Platform._ class UUIDTest { @@ -163,18 +165,23 @@ class UUIDTest { assertEquals(0, uuid2.clockSequence()) assertEquals(0L, uuid2.node()) - assertThrows(classOf[Exception], UUID.fromString(null)) - assertThrows(classOf[Exception], UUID.fromString("")) - assertThrows(classOf[Exception], UUID.fromString("f81d4fae_7dec-11d0-a765-00a0c91e6bf6")) - assertThrows(classOf[Exception], UUID.fromString("f81d4fae-7dec_11d0-a765-00a0c91e6bf6")) - assertThrows(classOf[Exception], UUID.fromString("f81d4fae-7dec-11d0_a765-00a0c91e6bf6")) - assertThrows(classOf[Exception], UUID.fromString("f81d4fae-7dec-11d0-a765_00a0c91e6bf6")) - assertThrows(classOf[Exception], UUID.fromString("-7dec-11d0-a765-00a0c91e6bf6")) - assertThrows(classOf[Exception], UUID.fromString("f81d4fae--11d0-a765-00a0c91e6bf6")) - assertThrows(classOf[Exception], UUID.fromString("f81d4fae-7dec--a765-00a0c91e6bf6")) - assertThrows(classOf[Exception], UUID.fromString("f81d4fae-7dec-11d0--00a0c91e6bf6")) - assertThrows(classOf[Exception], UUID.fromString("f81d4fae-7dec-11d0-a765-")) - assertThrows(classOf[Exception], UUID.fromString("f81d4fae-7dec-11d0-a765")) - assertThrows(classOf[Exception], UUID.fromString("f81d4fae-7dZc-11d0-a765-00a0c91e6bf6")) + assertThrows(classOf[IllegalArgumentException], UUID.fromString("")) + assertThrows(classOf[IllegalArgumentException], UUID.fromString("f81d4fae_7dec-11d0-a765-00a0c91e6bf6")) + assertThrows(classOf[IllegalArgumentException], UUID.fromString("f81d4fae-7dec_11d0-a765-00a0c91e6bf6")) + assertThrows(classOf[IllegalArgumentException], UUID.fromString("f81d4fae-7dec-11d0_a765-00a0c91e6bf6")) + assertThrows(classOf[IllegalArgumentException], UUID.fromString("f81d4fae-7dec-11d0-a765_00a0c91e6bf6")) + assertThrows(classOf[IllegalArgumentException], UUID.fromString("-7dec-11d0-a765-00a0c91e6bf6")) + assertThrows(classOf[IllegalArgumentException], UUID.fromString("f81d4fae--11d0-a765-00a0c91e6bf6")) + assertThrows(classOf[IllegalArgumentException], UUID.fromString("f81d4fae-7dec--a765-00a0c91e6bf6")) + assertThrows(classOf[IllegalArgumentException], UUID.fromString("f81d4fae-7dec-11d0--00a0c91e6bf6")) + assertThrows(classOf[IllegalArgumentException], UUID.fromString("f81d4fae-7dec-11d0-a765-")) + assertThrows(classOf[IllegalArgumentException], UUID.fromString("f81d4fae-7dec-11d0-a765")) + assertThrows(classOf[IllegalArgumentException], UUID.fromString("f81d4fae-7dZc-11d0-a765-00a0c91e6bf6")) + } + + @Test def fromStringNull(): Unit = { + assumeTrue("assuming compliant null pointer checks", hasCompliantNullPointers) + + assertThrows(classOf[NullPointerException], UUID.fromString(null)) } } 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 ba50551fe0..99beb524fe 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 @@ -535,6 +535,96 @@ class RegexEngineTest { assertEquals("c", m.group("D")) } + @Test def quantifiedAssertions_Issue4784(): Unit = { + /* In ES RegExp's, Assertions cannot be quantified. We test here that our + * compiler emits them in a way that satisfies the ES regex syntax. + */ + + val questionStart = compile("^?a") + assertFind(questionStart, "ab", 0, 1) + assertFind(questionStart, "ba", 1, 2) + assertNotFind(questionStart, "bc") + + val plusStart = compile("^+a") + assertFind(plusStart, "ab", 0, 1) + assertNotFind(plusStart, "ba") + assertNotFind(plusStart, "bc") + + val questionEnd = compile("a$?") + assertFind(questionEnd, "ab", 0, 1) + assertFind(questionEnd, "ba", 1, 2) + assertNotFind(questionEnd, "bc") + + val plusEnd = compile("a$+") + assertNotFind(plusEnd, "ab") + assertFind(plusEnd, "ba", 1, 2) + assertNotFind(plusEnd, "bc") + + val questionWordBoundary = compile("a\\b?") + assertFind(questionWordBoundary, "ab", 0, 1) + assertFind(questionWordBoundary, "ba", 1, 2) + assertNotFind(questionWordBoundary, "bc") + + val plusWordBoundary = compile("a\\b+") + assertNotFind(plusWordBoundary, "ab") + assertFind(plusWordBoundary, "ba", 1, 2) + assertNotFind(plusWordBoundary, "bc") + + val questionNotWordBoundary = compile("a\\B?") + assertFind(questionNotWordBoundary, "ab", 0, 1) + assertFind(questionNotWordBoundary, "ba", 1, 2) + assertNotFind(questionNotWordBoundary, "bc") + + val plusNotWordBoundary = compile("a\\B+") + assertFind(plusNotWordBoundary, "ab", 0, 1) + assertNotFind(plusNotWordBoundary, "ba") + assertNotFind(plusNotWordBoundary, "bc") + + val questionPosLookahead = compile("a(?=b)?") + assertFind(questionPosLookahead, "ab", 0, 1) + assertFind(questionPosLookahead, "ba", 1, 2) + assertNotFind(questionPosLookahead, "bc") + + val plusPosLookahead = compile("a(?=b)+") + assertFind(plusPosLookahead, "ab", 0, 1) + assertNotFind(plusPosLookahead, "ba") + assertNotFind(plusPosLookahead, "bc") + + val questionNegLookahead = compile("a(?!b)?") + assertFind(questionNegLookahead, "ab", 0, 1) + assertFind(questionNegLookahead, "ba", 1, 2) + assertNotFind(questionNegLookahead, "bc") + + val plusNegLookahead = compile("a(?!b)+") + assertNotFind(plusNegLookahead, "ab") + assertFind(plusNegLookahead, "ba", 1, 2) + assertNotFind(plusNegLookahead, "bc") + } + + @Test def quantifiedLookBehindAssertions_Issue4784(): Unit = { + assumeTrue("requires look-behinds", regexSupportsLookBehinds) + + val questionPosLookbehind = compile("(?<=b)?a") + assertFind(questionPosLookbehind, "ab", 0, 1) + assertFind(questionPosLookbehind, "ba", 1, 2) + assertNotFind(questionPosLookbehind, "bc") + + val plusPosLookbehind = compile("(?<=b)+a") + assertNotFind(plusPosLookbehind, "ab") + assertFind(plusPosLookbehind, "ba", 1, 2) + assertNotFind(plusPosLookbehind, "bc") + + val questionNegLookbehind = compile("(? true case _ => false diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/scalalib/RangesTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/scalalib/RangesTest.scala index 6df9f8d246..0d7a17d747 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/scalalib/RangesTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/scalalib/RangesTest.scala @@ -47,39 +47,18 @@ class RangesTest { } @Test def rangeToString_Issue2412(): Unit = { - if (scalaVersion.startsWith("2.11.")) { - assertEquals("Range(1, 3, 5, 7, 9)", (1 to 10 by 2).toString) - assertEquals("Range()", (1 until 1 by 2).toString) - assertTrue( - (BigDecimal(0.0) to BigDecimal(1.0)).toString.startsWith("scala.collection.immutable.Range$Partial")) - assertEquals("Range(0, 1)", (0 to 1).toString) - } else { - assertEquals("inexact Range 1 to 10 by 2", (1 to 10 by 2).toString) - assertEquals("empty Range 1 until 1 by 2", (1 until 1 by 2).toString) - assertEquals("Range requires step", (BigDecimal(0.0) to BigDecimal(1.0)).toString) - assertEquals("Range 0 to 1", (0 to 1).toString) - } + assertEquals("inexact Range 1 to 10 by 2", (1 to 10 by 2).toString) + assertEquals("empty Range 1 until 1 by 2", (1 until 1 by 2).toString) + assertEquals("Range requires step", (BigDecimal(0.0) to BigDecimal(1.0)).toString) + assertEquals("Range 0 to 1", (0 to 1).toString) } @Test def numericRangeToString_Issue2412(): Unit = { - if (scalaVersion.startsWith("2.11.")) { - assertEquals("NumericRange(0, 2, 4, 6, 8, 10)", - NumericRange.inclusive(0, 10, 2).toString()) - assertEquals("NumericRange(0, 2, 4, 6, 8)", - NumericRange(0, 10, 2).toString) - } else { - assertEquals("NumericRange 0 to 10 by 2", - NumericRange.inclusive(0, 10, 2).toString()) - assertEquals("NumericRange 0 until 10 by 2", - NumericRange(0, 10, 2).toString) - } + assertEquals("NumericRange 0 to 10 by 2", NumericRange.inclusive(0, 10, 2).toString()) + assertEquals("NumericRange 0 until 10 by 2", NumericRange(0, 10, 2).toString) } @Test def numericRangeWithArbitraryIntegral(): Unit = { - // This is broken in Scala JVM up to (including) 2.11.8, 2.12.1 (SI-10086). - assumeFalse("Assumed not on JVM for 2.12.1", - executingInJVM && scalaVersion == "2.12.1") - // Our custom integral type. case class A(v: Int) diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/scalalib/SymbolTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/scalalib/SymbolTest.scala index 446bcd9f4f..15ecdef4b0 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/scalalib/SymbolTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/scalalib/SymbolTest.scala @@ -57,8 +57,6 @@ class SymbolTest { val scalajs = Symbol("ScalaJS") val toStringUsesQuoteSyntax = { - scalaVersion.startsWith("2.10.") || - scalaVersion.startsWith("2.11.") || scalaVersion.startsWith("2.12.") || scalaVersion == "2.13.0" || scalaVersion == "2.13.1" || diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/utils/AssertThrows.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/utils/AssertThrows.scala index 4df546e0d8..df343e91d8 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/utils/AssertThrows.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/utils/AssertThrows.scala @@ -21,4 +21,9 @@ object AssertThrows { def run(): Unit = code }) } + + def assertThrowsNPEIfCompliant(code: => Unit): Unit = { + if (Platform.hasCompliantNullPointers) + assertThrows(classOf[NullPointerException], code) + } }