summaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorHaoyi Li <haoyi@haoyi-mbp.corp.dropbox.com>2014-11-26 00:45:31 -0800
committerHaoyi Li <haoyi@haoyi-mbp.corp.dropbox.com>2014-11-26 00:45:31 -0800
commit24f31e120f9537faede7a174bb09ee35f64e1ce4 (patch)
tree06ffc3ecc7847789008352b7e2b7c040dad48907 /examples
parentb89ce9cbf79363f8cab09186a5d7ba94bc0af02a (diff)
parent2c4b142503bd2d871e6818b5cab8c38627d9e4a0 (diff)
downloadhands-on-scala-js-24f31e120f9537faede7a174bb09ee35f64e1ce4.tar.gz
hands-on-scala-js-24f31e120f9537faede7a174bb09ee35f64e1ce4.tar.bz2
hands-on-scala-js-24f31e120f9537faede7a174bb09ee35f64e1ce4.zip
Merge commit '2c4b142503bd2d871e6818b5cab8c38627d9e4a0' as 'examples/scala-js'
Diffstat (limited to 'examples')
-rw-r--r--examples/scala-js/.gitignore10
-rw-r--r--examples/scala-js/LICENSE27
-rw-r--r--examples/scala-js/README.md116
-rw-r--r--examples/scala-js/TESTING67
-rwxr-xr-xexamples/scala-js/ci/check-partest-coverage.sh58
-rwxr-xr-xexamples/scala-js/ci/checksizes.sh65
-rw-r--r--examples/scala-js/ci/matrix.xml360
-rw-r--r--examples/scala-js/ci/scalajs-matrix-build.groovy57
-rwxr-xr-xexamples/scala-js/cli/src/main/resources/scalajsc16
-rw-r--r--examples/scala-js/cli/src/main/resources/scalajsc.bat14
-rwxr-xr-xexamples/scala-js/cli/src/main/resources/scalajsld10
-rw-r--r--examples/scala-js/cli/src/main/resources/scalajsld.bat8
-rwxr-xr-xexamples/scala-js/cli/src/main/resources/scalajsp9
-rw-r--r--examples/scala-js/cli/src/main/resources/scalajsp.bat7
-rw-r--r--examples/scala-js/cli/src/main/scala/scala/scalajs/cli/Scalajsld.scala180
-rw-r--r--examples/scala-js/cli/src/main/scala/scala/scalajs/cli/Scalajsp.scala158
-rw-r--r--examples/scala-js/compiler/src/main/resources/scalac-plugin.xml4
-rw-r--r--examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/ClassInfos.scala143
-rw-r--r--examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/Compat210Component.scala108
-rw-r--r--examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/GenJSCode.scala3911
-rw-r--r--examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/GenJSExports.scala751
-rw-r--r--examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/GenJSFiles.scala51
-rw-r--r--examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSDefinitions.scala128
-rw-r--r--examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSEncoding.scala261
-rw-r--r--examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSGlobalAddons.scala244
-rw-r--r--examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSPrimitives.scala119
-rw-r--r--examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSTreeExtractors.scala66
-rw-r--r--examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/PrepJSExports.scala251
-rw-r--r--examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/PrepJSInterop.scala621
-rw-r--r--examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/ScalaJSOptions.scala30
-rw-r--r--examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/ScalaJSPlugin.scala143
-rw-r--r--examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/TypeKinds.scala252
-rw-r--r--examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/util/ScopedVar.scala38
-rw-r--r--examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/DiverseErrorsTest.scala31
-rw-r--r--examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/EnumerationInteropTest.scala135
-rw-r--r--examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/JSDynamicLiteralTest.scala102
-rw-r--r--examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/JSExportASTTest.scala38
-rw-r--r--examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/JSExportTest.scala745
-rw-r--r--examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/JSInteropTest.scala350
-rw-r--r--examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/OptimizationTest.scala109
-rw-r--r--examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/PositionTest.scala37
-rw-r--r--examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/util/DirectTest.scala70
-rw-r--r--examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/util/JSASTTest.scala100
-rw-r--r--examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/util/TestHelpers.scala68
-rw-r--r--examples/scala-js/examples/helloworld/HelloWorld.scala86
-rw-r--r--examples/scala-js/examples/helloworld/helloworld-2.10-fastopt.html19
-rw-r--r--examples/scala-js/examples/helloworld/helloworld-2.10.html19
-rw-r--r--examples/scala-js/examples/helloworld/helloworld-2.11-fastopt.html19
-rw-r--r--examples/scala-js/examples/helloworld/helloworld-2.11.html19
-rw-r--r--examples/scala-js/examples/helloworld/startup.js8
-rw-r--r--examples/scala-js/examples/reversi/JSTypes.scala87
-rw-r--r--examples/scala-js/examples/reversi/Reversi.scala266
-rw-r--r--examples/scala-js/examples/reversi/reversi-2.10-fastopt.html30
-rw-r--r--examples/scala-js/examples/reversi/reversi-2.10.html30
-rw-r--r--examples/scala-js/examples/reversi/reversi-2.11-fastopt.html30
-rw-r--r--examples/scala-js/examples/reversi/reversi-2.11.html30
-rw-r--r--examples/scala-js/examples/testing/src/main/scala/ElementCreator.scala7
-rw-r--r--examples/scala-js/examples/testing/src/test/scala/CollectionTest.scala17
-rw-r--r--examples/scala-js/examples/testing/src/test/scala/ElementCreatorTest.scala24
-rw-r--r--examples/scala-js/examples/testing/testing-2.10-fastopt.html32
-rw-r--r--examples/scala-js/examples/testing/testing-2.10.html32
-rw-r--r--examples/scala-js/examples/testing/testing-2.11-fastopt.html32
-rw-r--r--examples/scala-js/examples/testing/testing-2.11.html32
-rw-r--r--examples/scala-js/ir/src/main/scala/scala/scalajs/ir/ClassKind.scala53
-rw-r--r--examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Definitions.scala144
-rw-r--r--examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Hashers.scala459
-rw-r--r--examples/scala-js/ir/src/main/scala/scala/scalajs/ir/InfoSerializers.scala180
-rw-r--r--examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Infos.scala118
-rw-r--r--examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Position.scala42
-rw-r--r--examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Printers.scala709
-rw-r--r--examples/scala-js/ir/src/main/scala/scala/scalajs/ir/ScalaJSVersions.scala25
-rw-r--r--examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Serializers.scala790
-rw-r--r--examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Tags.scala107
-rw-r--r--examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Transformers.scala218
-rw-r--r--examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Traversers.scala197
-rw-r--r--examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Trees.scala536
-rw-r--r--examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Types.scala182
-rw-r--r--examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Utils.scala110
-rw-r--r--examples/scala-js/jasmine-test-framework/src/main/resources/jasmine-polyfills.js9
-rw-r--r--examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Jasmine.scala17
-rw-r--r--examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/JasmineEnv.scala14
-rw-r--r--examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/JasmineExpectation.scala21
-rw-r--r--examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Results.scala13
-rw-r--r--examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Spec.scala9
-rw-r--r--examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/SpecResults.scala8
-rw-r--r--examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Suite.scala8
-rw-r--r--examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/SuiteResults.scala9
-rw-r--r--examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTest.scala34
-rw-r--r--examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTestFramework.scala93
-rw-r--r--examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTestReporter.scala130
-rw-r--r--examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/TestSuiteContext.scala55
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/Appendable.scala7
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/AutoCloseable.scala5
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/Boolean.scala61
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/Byte.scala71
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/CharSequence.scala8
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/Character.scala289
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/Class.scala83
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/Cloneable.scala3
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/Comparable.scala5
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/Double.scala110
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/Float.scala96
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/InheritableThreadLocal.scala5
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/Integer.scala129
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/Long.scala196
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/Math.scala211
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/Number.scala12
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/Readable.scala7
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/Runnable.scala5
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/Runtime.scala47
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/Short.scala73
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/StackTraceElement.scala55
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/StringBuffer.scala159
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/StringBuilder.scala178
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/System.scala275
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/Thread.scala16
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/ThreadLocal.scala24
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/Throwables.scala363
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/Void.scala7
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/ref/PhantomReference.scala7
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/ref/Reference.scala8
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/ref/ReferenceQueue.scala3
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/ref/SoftReference.scala9
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/ref/WeakReference.scala7
-rw-r--r--examples/scala-js/javalanglib/src/main/scala/java/lang/reflect/Array.scala176
-rw-r--r--examples/scala-js/javalib-ex-test-suite/src/test/scala/scala/scalajs/testsuite/javalibex/DataInputStreamTest.scala338
-rw-r--r--examples/scala-js/javalib-ex-test-suite/src/test/scala/scala/scalajs/testsuite/javalibex/ZipInputStreamTest.scala136
-rw-r--r--examples/scala-js/javalib-ex/src/main/scala/java/io/DataInputStream.scala274
-rw-r--r--examples/scala-js/javalib-ex/src/main/scala/java/util/zip/InflaterInputStream.scala18
-rw-r--r--examples/scala-js/javalib-ex/src/main/scala/java/util/zip/ZipEntry.scala67
-rw-r--r--examples/scala-js/javalib-ex/src/main/scala/java/util/zip/ZipInputStream.scala87
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/io/BufferedReader.scala145
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/io/ByteArrayInputStream.scala58
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/io/ByteArrayOutputStream.scala62
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/io/Closeable.scala6
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/io/DataInput.scala19
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/io/FilterInputStream.scala24
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/io/FilterOutputStream.scala16
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/io/Flushable.scala5
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/io/InputStream.scala53
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/io/InputStreamReader.scala216
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/io/OutputStream.scala25
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/io/OutputStreamWriter.scala160
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/io/PrintStream.scala218
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/io/PrintWriter.scala150
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/io/Reader.scala60
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/io/Serializable.scala3
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/io/StringReader.scala69
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/io/StringWriter.scala44
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/io/Throwables.scala19
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/io/Writer.scala45
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/net/URI.scala706
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/net/URISyntaxException.scala15
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/nio/Buffer.scala83
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/nio/BufferOverflowException.scala3
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/nio/BufferUnderflowException.scala3
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/nio/ByteBuffer.scala227
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/nio/ByteOrder.scala15
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/nio/CharBuffer.scala228
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/nio/HeapByteBuffer.scala129
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/nio/HeapCharBuffer.scala138
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/nio/InvalidMarkException.scala3
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/nio/ReadOnlyBufferException.scala3
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/nio/StringCharBuffer.scala102
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/nio/charset/CharacterCodingException.scala3
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/nio/charset/Charset.scala103
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/nio/charset/CharsetDecoder.scala217
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/nio/charset/CharsetEncoder.scala235
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/nio/charset/CoderMalfunctionError.scala3
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/nio/charset/CoderResult.scala78
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/nio/charset/CodingErrorAction.scala11
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/nio/charset/MalformedInputException.scala9
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/nio/charset/StandardCharsets.scala14
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/nio/charset/UnmappableCharacterException.scala9
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/nio/charset/UnsupportedCharsetException.scala6
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/util/Arrays.scala401
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/util/Comparator.scala6
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/util/Date.scala147
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/util/Formattable.scala5
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/util/FormattableFlags.scala7
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/util/Formatter.scala273
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/util/Random.scala119
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/util/Throwables.scala166
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/util/UUID.scala163
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/util/concurrent/ExecutionException.scala9
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/util/concurrent/Executor.scala5
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/util/concurrent/TimeUnit.scala133
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/util/concurrent/atomic/AtomicBoolean.scala33
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/util/concurrent/atomic/AtomicInteger.scala63
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/util/concurrent/atomic/AtomicLong.scala61
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/util/concurrent/atomic/AtomicReference.scala34
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/util/regex/MatchResult.scala13
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/util/regex/Matcher.scala274
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/util/regex/Pattern.scala154
-rw-r--r--examples/scala-js/library-aux/src/main/scala/scala/runtime/ArrayRuntime.scala16
-rw-r--r--examples/scala-js/library-aux/src/main/scala/scala/runtime/BoxedUnit.scala18
-rw-r--r--examples/scala-js/library-aux/src/main/scala/scala/runtime/RefTypes.scala165
-rw-r--r--examples/scala-js/library-aux/src/main/scala/scala/runtime/Statics.scala89
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/concurrent/JSExecutionContext.scala24
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/concurrent/QueueExecutionContext.scala17
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/concurrent/RunNowExcecutionContext.scala14
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/Array.scala173
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/ArrayOps.scala119
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/Date.scala225
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/Dictionary.scala92
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/Error.scala114
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/Function.scala194
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/GlobalScope.scala21
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/JSApp.scala20
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/JSArrayOps.scala264
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/JSConverters.scala54
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/JSON.scala51
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/JavaScriptException.scala20
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/Math.scala277
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/Primitives.scala872
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/PropertyDescriptor.scala20
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/RegExp.scala108
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/ThisFunction.scala160
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/UndefOr.scala254
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/WrappedArray.scala92
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/WrappedDictionary.scala89
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/JSBracketAccess.scala17
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/JSExport.scala19
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/JSExportAll.scala21
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/JSExportDescendentClasses.scala20
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/JSExportDescendentObjects.scala20
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/JSExportNamed.scala38
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/JSName.scala17
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/README.md3
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/RawJSType.scala23
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/package.scala161
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/ArrayBuffer.scala17
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/ArrayBufferInputStream.scala88
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/ArrayBufferView.scala14
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/DataView.scala26
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Float32Array.scala24
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Float64Array.scala24
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Int16Array.scala24
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Int32Array.scala24
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Int8Array.scala24
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/TypedArray.scala46
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Uint16Array.scala24
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Uint32Array.scala24
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Uint8Array.scala24
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Uint8ClampedArray.scala24
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/package.scala145
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/ISO_8859_1.scala19
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/ISO_8859_1_And_US_ASCII_Common.scala197
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/StandardCharsets.scala42
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/US_ASCII.scala19
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/UTF_16.scala17
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/UTF_16BE.scala17
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/UTF_16LE.scala17
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/UTF_16_Common.scala205
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/UTF_8.scala455
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/runtime/AnonFunctions.scala119
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/runtime/Bits.scala240
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/runtime/BooleanReflectiveCall.scala31
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/runtime/IntegerReflectiveCall.scala87
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/runtime/NumberReflectiveCall.scala162
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/runtime/RuntimeLong.scala686
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/runtime/RuntimeString.scala338
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/runtime/StackTrace.scala507
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/runtime/UndefinedBehaviorError.scala23
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/runtime/package.scala176
-rw-r--r--examples/scala-js/no-ir-check-test/src/test/scala/scala/scalajs/testsuite/noircheck/DummyParentsTest.scala34
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/BlacklistedTests.txt891
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/BuglistedTests.txt4
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/NoDCEWarn.txt8
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/WhitelistedTests.txt2929
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/neg/t7494-no-options.check42
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-01.check37
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-02.check187
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-04.check64
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-08.check171
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-09.check50
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-10.check46
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/bugs.sem1
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/dynamic-anyval.check4
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/impconvtimes.check1
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/imports.check21
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/issue192.sem1
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/macro-bundle-static.check6
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/macro-bundle-toplevel.check6
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/macro-bundle-whitebox-decl.check6
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/misc.check62
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/promotion.check4
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/runtime.check70
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/spec-self.check2
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/structural.check37
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t0421-new.check3
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t0421-old.check3
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t1503.sem1
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t3702.check2
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t4148.sem1
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t4617.check1
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t5552.check2
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t5568.check9
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t5629b.check10
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t5680.check3
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t5866.check2
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t6102.check27
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t6318_primitives.check36
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t6662.check1
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t7657.check3
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t7763.sem1
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/try-catch-unify.check4
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/virtpatmat_switch.check7
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/virtpatmat_typetag.check10
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/BlacklistedTests.txt899
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/BuglistedTests.txt4
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/NoDCEWarn.txt8
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/WhitelistedTests.txt2949
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/neg/t7494-no-options.check42
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-01.check37
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-02.check187
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-04.check64
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-08.check171
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-09.check50
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-10.check46
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/bugs.sem1
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/dynamic-anyval.check4
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/impconvtimes.check1
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/imports.check21
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/issue192.sem1
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/macro-bundle-static.check6
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/macro-bundle-toplevel.check6
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/macro-bundle-whitebox-decl.check6
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/misc.check62
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/promotion.check4
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/runtime.check70
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/spec-self.check2
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/structural.check37
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t0421-new.check3
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t0421-old.check3
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t1503.sem1
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t3702.check2
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t4148.sem1
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t4617.check1
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t5552.check2
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t5568.check9
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t5629b.check10
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t5680.check3
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t5866.check2
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t6102.check27
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t6318_primitives.check36
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t6662.check1
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t7657.check3
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t7763.sem1
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t8570a.check1
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/try-catch-unify.check4
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/virtpatmat_switch.check7
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/virtpatmat_typetag.check10
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/BlacklistedTests.txt914
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/BuglistedTests.txt4
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/NoDCEWarn.txt8
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/WhitelistedTests.txt2976
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/neg/t7494-no-options.check42
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-01.check37
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-02.check187
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-04.check64
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-08.check171
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-09.check50
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-10.check46
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/bugs.sem1
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/dynamic-anyval.check4
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/impconvtimes.check1
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/imports.check21
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/issue192.sem1
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/macro-bundle-static.check6
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/macro-bundle-toplevel.check6
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/macro-bundle-whitebox-decl.check6
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/misc.check62
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/promotion.check4
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/runtime.check70
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/spec-self.check2
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/structural.check37
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t0421-new.check3
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t0421-old.check3
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t1503.sem1
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t3702.check2
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t4148.sem1
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t4617.check1
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t5552.check2
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t5568.check9
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t5629b.check10
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t5680.check3
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t5866.check2
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t6102.check27
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t6318_primitives.check54
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t6662.check1
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t7657.check3
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t7763.sem1
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t8570a.check1
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/try-catch-unify.check4
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/virtpatmat_switch.check7
-rw-r--r--examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/virtpatmat_typetag.check10
-rw-r--r--examples/scala-js/partest/src/main/scala/scala/tools/nsc/MainGenericRunner.scala220
-rw-r--r--examples/scala-js/partest/src/main/scala/scala/tools/partest/scalajs/PartestInterface.scala119
-rw-r--r--examples/scala-js/partest/src/main/scala/scala/tools/partest/scalajs/ScalaJSPartest.scala203
-rw-r--r--examples/scala-js/partest/src/main/scala/scala/tools/partest/scalajs/ScalaJSPartestOptions.scala109
-rw-r--r--examples/scala-js/project/ExternalCompile.scala116
-rw-r--r--examples/scala-js/project/JavaLangObject.scala243
-rw-r--r--examples/scala-js/project/JavaLangString.scala215
-rw-r--r--examples/scala-js/project/ScalaJSBuild.scala891
-rw-r--r--examples/scala-js/project/build.properties1
-rw-r--r--examples/scala-js/project/build.sbt48
-rw-r--r--examples/scala-js/project/project/ScalaJSEnvGenerator.scala31
-rw-r--r--examples/scala-js/sbt-plugin-test/README.md7
-rw-r--r--examples/scala-js/sbt-plugin-test/build.sbt44
-rw-r--r--examples/scala-js/sbt-plugin-test/jetty9/src/main/resources/test.txt1
-rw-r--r--examples/scala-js/sbt-plugin-test/noDOM/src/main/scala/sbttest/noDOM/Lib.scala11
-rw-r--r--examples/scala-js/sbt-plugin-test/noDOM/src/main/scala/sbttest/noDOM/TestApp.scala12
-rw-r--r--examples/scala-js/sbt-plugin-test/noDOM/src/test/scala/sbttest/noDOM/LibTest.scala19
-rw-r--r--examples/scala-js/sbt-plugin-test/project/Jetty9Test.scala83
-rw-r--r--examples/scala-js/sbt-plugin-test/project/build.properties1
-rw-r--r--examples/scala-js/sbt-plugin-test/project/build.sbt4
-rw-r--r--examples/scala-js/sbt-plugin-test/project/project/build.sbt1
-rw-r--r--examples/scala-js/sbt-plugin-test/withDOM/src/main/scala/sbttest/withDOM/Lib.scala28
-rw-r--r--examples/scala-js/sbt-plugin-test/withDOM/src/main/scala/sbttest/withDOM/TestApp.scala14
-rw-r--r--examples/scala-js/sbt-plugin-test/withDOM/src/test/scala/sbttest/withDOM/LibTest.scala24
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/AbstractJSDeps.scala81
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/Implicits.scala34
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/JSUtils.scala35
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/LoggerJSConsole.scala18
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/OptimizerOptions.scala74
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/ScalaJSCrossVersion.scala48
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/ScalaJSPlugin.scala179
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/ScalaJSPluginInternal.scala598
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/Stage.scala18
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/ExternalJSEnv.scala200
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/VirtualFileMaterializer.scala67
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/nodejs/NodeJSEnv.scala306
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/JettyWebsocketManager.scala126
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/PhantomJSEnv.scala466
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/PhantomJettyClassLoader.scala63
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/WebsocketListener.scala10
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/WebsocketManager.scala10
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/LazyScalaJSScope.scala96
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/RhinoJSEnv.scala303
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/ScalaJSCoreLib.scala173
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/package.scala42
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/impl/DependencyBuilders.scala99
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/Events.scala35
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/JSClasspathLoader.scala15
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/SbtTestLoggerAccWrapper.scala22
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestException.scala9
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestFramework.scala52
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestOutputConsole.scala190
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestRunner.scala37
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestTask.scala110
-rw-r--r--examples/scala-js/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/AsyncTests.scala37
-rw-r--r--examples/scala-js/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/ComTests.scala206
-rw-r--r--examples/scala-js/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/JSEnvTest.scala44
-rw-r--r--examples/scala-js/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/NodeJSTest.scala54
-rw-r--r--examples/scala-js/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/PhantomJSTest.scala21
-rw-r--r--examples/scala-js/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/RhinoJSEnvTest.scala9
-rw-r--r--examples/scala-js/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/StoreJSConsole.scala14
-rw-r--r--examples/scala-js/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/StoreLogger.scala29
-rw-r--r--examples/scala-js/scalalib/overrides-2.10/scala/Console.scala468
-rw-r--r--examples/scala-js/scalalib/overrides-2.10/scala/collection/immutable/NumericRange.scala285
-rw-r--r--examples/scala-js/scalalib/overrides-2.10/scala/collection/immutable/Range.scala424
-rw-r--r--examples/scala-js/scalalib/overrides-2.10/scala/collection/mutable/Buffer.scala50
-rw-r--r--examples/scala-js/scalalib/overrides-2.10/scala/compat/Platform.scala133
-rw-r--r--examples/scala-js/scalalib/overrides-2.10/scala/package.scala138
-rw-r--r--examples/scala-js/scalalib/overrides-2.11/scala/Console.scala222
-rw-r--r--examples/scala-js/scalalib/overrides-2.11/scala/collection/immutable/NumericRange.scala346
-rw-r--r--examples/scala-js/scalalib/overrides-2.11/scala/collection/immutable/Range.scala516
-rw-r--r--examples/scala-js/scalalib/overrides-2.11/scala/collection/mutable/Buffer.scala51
-rw-r--r--examples/scala-js/scalalib/overrides-2.11/scala/compat/Platform.scala132
-rw-r--r--examples/scala-js/scalalib/overrides-2.11/scala/package.scala133
-rw-r--r--examples/scala-js/scalalib/overrides/scala/App.scala83
-rw-r--r--examples/scala-js/scalalib/overrides/scala/Enumeration.scala284
-rw-r--r--examples/scala-js/scalalib/overrides/scala/Symbol.scala117
-rw-r--r--examples/scala-js/scalalib/overrides/scala/concurrent/impl/AbstractPromise.scala29
-rw-r--r--examples/scala-js/scalalib/overrides/scala/math/ScalaNumber.scala21
-rw-r--r--examples/scala-js/scalalib/overrides/scala/runtime/BoxesRunTime.scala124
-rw-r--r--examples/scala-js/scalalib/overrides/scala/util/control/NoStackTrace.scala33
-rwxr-xr-xexamples/scala-js/scripts/assemble-cli.sh96
-rwxr-xr-xexamples/scala-js/scripts/build-all-js.sh15
-rwxr-xr-xexamples/scala-js/scripts/publish-to-bintray.sh35
-rw-r--r--examples/scala-js/stubs/src/main/scala/scala/scalajs/js/annotation/ExportAnnotations.scala24
-rw-r--r--examples/scala-js/test-bridge/src/main/scala/scala/scalajs/testbridge/Test.scala17
-rw-r--r--examples/scala-js/test-bridge/src/main/scala/scala/scalajs/testbridge/TestFramework.scala36
-rw-r--r--examples/scala-js/test-bridge/src/main/scala/scala/scalajs/testbridge/TestOutput.scala34
-rw-r--r--examples/scala-js/test-bridge/src/main/scala/scala/scalajs/testbridge/TestOutputLog.scala16
-rw-r--r--examples/scala-js/test-bridge/src/main/scala/scala/scalajs/testbridge/internal/ConsoleTestOutput.scala118
-rw-r--r--examples/scala-js/test-suite/run-jasmine-tests.js18
-rw-r--r--examples/scala-js/test-suite/scalajs-test-suite-2.10-fastopt.html15
-rw-r--r--examples/scala-js/test-suite/scalajs-test-suite-2.10.html15
-rw-r--r--examples/scala-js/test-suite/scalajs-test-suite-2.11-fastopt.html15
-rw-r--r--examples/scala-js/test-suite/scalajs-test-suite-2.11.html15
-rw-r--r--examples/scala-js/test-suite/src/test/require-sam/scala/scalajs/testsuite/jsinterop/ArraySAMTest.scala32
-rw-r--r--examples/scala-js/test-suite/src/test/resources/SourceMapTestTemplate.scala655
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/BooleanTest.scala41
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/ByteTest.scala40
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/CharTest.scala40
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/FloatTest.scala73
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/InstanceTestsHijackedBoxedClassesTest.scala98
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/IntTest.scala206
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/InteroperabilityTest.scala528
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/LongTest.scala159
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/OptimizerTest.scala43
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/ReflectionTest.scala69
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/ReflectiveCallTest.scala330
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/RegressionTest.scala287
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/RuntimeTypesTest.scala77
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/ShortTest.scala38
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/UnitTest.scala47
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ArraysTest.scala749
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/AtomicTest.scala119
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/BooleanTest.scala62
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ByteTest.scala64
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/CharacterTest.scala672
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ClassTest.scala30
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/DateTest.scala87
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/DoubleTest.scala217
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/FloatTest.scala221
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/FormatterTest.scala241
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/IntegerTest.scala161
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/LongTest.scala178
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/MathTest.scala142
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/MockByteArrayOutputStream.scala47
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ObjectTest.scala72
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/OutputStreamWriterTest.scala134
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/PrintStreamTest.scala296
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/PrintWriterTest.scala287
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/RandomTest.scala225
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ReadersTest.scala244
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ReferenceTest.scala28
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/RegexTest.scala397
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ShortTest.scala66
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/StackTraceElementTest.scala29
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/StreamsTest.scala308
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/StringBufferTest.scala219
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/StringTest.scala237
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/SystemTest.scala118
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ThrowablesTest.scala90
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/TimeUnitTest.scala135
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/URITest.scala312
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/UUIDTest.scala180
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/ArrayTest.scala94
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/AsyncTest.scala121
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/DictionaryTest.scala79
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/DynamicTest.scala187
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/ExportsTest.scala1075
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/FunctionTest.scala41
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/MiscInteropTest.scala89
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/RuntimeLongTest.scala140
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/StrangeNamedTests.scala28
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/ThisFunctionTest.scala70
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/UndefOrTest.scala191
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/library/ArrayOpsTest.scala117
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/library/WrappedArrayTest.scala118
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/library/WrappedDictionaryTest.scala106
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niobuffer/BaseBufferTest.scala178
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niobuffer/ByteBufferTest.scala330
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niobuffer/CharBufferTest.scala388
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niocharset/BaseCharsetTest.scala234
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niocharset/CharsetTest.scala74
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niocharset/Latin1Test.scala91
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niocharset/USASCIITest.scala93
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niocharset/UTF16Test.scala155
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niocharset/UTF8Test.scala240
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/scalalib/EnumerationTest.scala95
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/scalalib/RangesTest.scala27
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/scalalib/SymbolTest.scala63
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/typedarray/ArrayBufferInputStreamTest.scala36
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/typedarray/ArrayBufferTest.scala39
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/typedarray/ArraysTest.scala45
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/typedarray/DataViewTest.scala275
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/typedarray/TypedArrayConversionTest.scala197
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/typedarray/TypedArrayTest.scala332
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/utils/JSUtils.scala26
-rw-r--r--examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/utils/TestDetector.scala33
-rw-r--r--examples/scala-js/tools/js/src/main/scala/scala/scalajs/tools/classpath/builder/NodeFileSystem.scala67
-rw-r--r--examples/scala-js/tools/js/src/main/scala/scala/scalajs/tools/io/NodeVirtualFiles.scala62
-rw-r--r--examples/scala-js/tools/js/src/main/scala/scala/scalajs/tools/json/Impl.scala36
-rw-r--r--examples/scala-js/tools/js/src/test/scala/scala/scalajs/tools/js/test/JasmineReporter.scala71
-rw-r--r--examples/scala-js/tools/js/src/test/scala/scala/scalajs/tools/js/test/QuickLinker.scala37
-rw-r--r--examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/classpath/builder/JarLibClasspathBuilder.scala13
-rw-r--r--examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/classpath/builder/PartialClasspathBuilder.scala38
-rw-r--r--examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/classpath/builder/PhysicalFileSystem.scala41
-rw-r--r--examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/io/FileVirtualFiles.scala157
-rw-r--r--examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/json/Impl.scala38
-rw-r--r--examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/optimizer/ClosureAstBuilder.scala47
-rw-r--r--examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/optimizer/ClosureAstTransformer.scala397
-rw-r--r--examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/optimizer/ConcurrencyUtils.scala74
-rw-r--r--examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/optimizer/LoggerErrorManager.scala38
-rw-r--r--examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/optimizer/ParIncOptimizer.scala188
-rw-r--r--examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/optimizer/ScalaJSClosureOptimizer.scala216
-rw-r--r--examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/sourcemap/SourceMapper.scala88
-rw-r--r--examples/scala-js/tools/jvm/src/test/resources/test.jar0
-rw-r--r--examples/scala-js/tools/jvm/src/test/scala/scala/scalajs/tools/classpath/builder/test/ClasspathElementsTraverserTest.scala42
-rw-r--r--examples/scala-js/tools/jvm/src/test/scala/scala/scalajs/tools/classpath/builder/test/JarBuilderTest.scala74
-rw-r--r--examples/scala-js/tools/scalajsenv.js772
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/CompleteClasspath.scala35
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/ComplianceRequirement.scala7
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/Exceptions.scala42
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/IRClasspath.scala69
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/LinkedClasspath.scala26
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/PartialClasspath.scala99
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/ResolvedJSDependency.scala10
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/AbstractJarLibClasspathBuilder.scala53
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/AbstractPartialClasspathBuilder.scala47
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/ClasspathContentHandler.scala25
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/ClasspathElementsTraverser.scala38
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/DirTraverser.scala60
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/FileSystem.scala57
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/JarTraverser.scala85
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/corelib/CoreJSLibs.scala113
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/AsyncJSEnv.scala19
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/AsyncJSRunner.scala19
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/ComJSEnv.scala38
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/ComJSRunner.scala22
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/ConsoleJSConsole.scala17
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/JSConsole.scala15
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/JSEnv.scala20
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/JSRunner.scala15
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/NullJSConsole.scala5
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/io/CacheUtils.scala52
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/io/IO.scala116
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/io/MemFiles.scala105
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/io/VirtualFiles.scala169
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/JSDesugaring.scala1525
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/LongImpl.scala116
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/Printers.scala420
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/ScalaJSClassEmitter.scala569
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/TreeDSL.scala50
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/Trees.scala194
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/Exceptions.scala59
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/FlatJSDependency.scala17
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/JSDependency.scala66
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/JSDependencyManifest.scala130
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/Origin.scala28
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/ResolutionInfo.scala21
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/AbstractJSONImpl.scala32
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/JSONDeserializer.scala30
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/JSONObjBuilder.scala20
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/JSONObjExtractor.scala13
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/JSONSerializer.scala32
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/package.scala26
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/logging/Level.scala24
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/logging/Logger.scala25
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/logging/NullLogger.scala7
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/logging/ScalaConsoleLogger.scala15
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/Analyzer.scala587
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/GenIncOptimizer.scala921
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/IRChecker.scala854
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/IncOptimizer.scala158
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/JSTreeBuilder.scala16
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/OptimizerCore.scala3572
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/ScalaJSOptimizer.scala552
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/sem/CheckedBehavior.scala24
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/sem/Semantics.scala97
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/sourcemap/JSFileBuilder.scala144
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/sourcemap/SourceMapWriter.scala213
657 files changed, 86304 insertions, 0 deletions
diff --git a/examples/scala-js/.gitignore b/examples/scala-js/.gitignore
new file mode 100644
index 0000000..90977dc
--- /dev/null
+++ b/examples/scala-js/.gitignore
@@ -0,0 +1,10 @@
+target/
+.cache
+.classpath
+.project
+.settings/
+/scalalib/fetchedSources/
+/partest/fetchedSources/
+/cli/pack/
+/.idea/
+/.idea_modules/
diff --git a/examples/scala-js/LICENSE b/examples/scala-js/LICENSE
new file mode 100644
index 0000000..79ec7ac
--- /dev/null
+++ b/examples/scala-js/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2013-2014 EPFL
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * Neither the name of the EPFL nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/examples/scala-js/README.md b/examples/scala-js/README.md
new file mode 100644
index 0000000..51e65c9
--- /dev/null
+++ b/examples/scala-js/README.md
@@ -0,0 +1,116 @@
+# Scala.js, a Scala to JavaScript compiler
+
+Scala.js compiles Scala code to JavaScript, allowing you to write your
+Web application entirely in Scala!
+
+Noteworthy features are:
+
+* Support all of Scala (including macros!),
+ modulo [a few semantic differences](http://www.scala-js.org/doc/semantics.html)
+* Very good [interoperability with JavaScript code](http://www.scala-js.org/doc/js-interoperability.html).
+ For example, use jQuery and HTML5 from your Scala.js code, either in a
+ typed or untyped way. Or create Scala.js objects and call their methods
+ from JavaScript.
+* Integrated with [sbt](http://www.scala-sbt.org/)
+ (including support for dependency management and incremental compilation)
+* Can be used with your favorite IDE for Scala
+* Generates [Source Maps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/)
+ for a smooth debugging experience (step through your Scala code from within
+ your browser supporting source maps)
+* Integrates [Google Closure Compiler](https://developers.google.com/closure/compiler/)
+ for producing minimal code for production.
+
+## Resources
+
+* [Website](http://www.scala-js.org/)
+* [Mailing list](https://groups.google.com/forum/?fromgroups#!forum/scala-js)
+
+## Get started
+
+We provide a
+[bootstrapping application](https://github.com/sjrd/scala-js-example-app)
+which you can fork to kick off your own project. Its readme provides further
+explanations on how to do so.
+
+## Contribute
+
+### Compile
+
+Scala.js uses [sbt](http://www.scala-sbt.org/) for its build process.
+To compile your fork, simply run:
+
+ sbt> package
+
+By default the sbt environment uses Scala 2.11.2. You can switch to any of the
+supported versions with, e.g.,
+
+ sbt> ++2.10.4
+
+### Run the test suite
+
+Compile and run the Scala.js-specific test suite with
+
+ sbt> testSuite/test
+
+(you must have run `package` before running the test suite)
+
+To run the Scala test suite (aka partest), you have to use a 2.11 version, e.g.,
+2.11.0 or 2.11.1, and run:
+
+ sbt> partestSuite/test
+
+Beware, this takes a very long time. You may use the `--fastOpt` and
+`--fullOpt` switches to run Scala.js DCE or the full Google Closure
+Compiler:
+
+ sbt> partestSuite/testOnly -- --fastOpt
+
+A complete test session from scratch on 2.11.1 would then be
+
+ sbt> ++2.11.1
+ sbt> package
+ sbt> testSuite/test
+ sbt> partestSuite/test
+
+### Test the examples
+
+After having compiled Scala.js, you can compile the example applications with:
+
+ sbt> examples/fullOptJS
+
+Then, you can "execute" them by opening their respective HTML files in your
+favorite browser. Since fully optimizing the JavaScript takes time
+(up to ten seconds, depending on your hardware), it is also possible
+to only partially optimize JS by doing instead:
+
+ sbt> examples/fastOptJS
+
+In this case, you have to open the `-fastopt` version of the HTML
+files.
+
+Currently, two examples are provided:
+
+* `examples/helloworld/helloworld.html`, saying Hello World in four different
+ ways (using DOM or jQuery, and using the untyped or typed interface to
+ JavaScript).
+* `examples/reversi/reversi.html`, an implementation of a
+ [Reversi](http://en.wikipedia.org/wiki/Reversi) game. Note that it uses the
+ HTML5 Canvas element, so it won't work with Internet Explorer 8 or
+ below.
+
+If both `fastOptJS` and `fullOptJS` break, you can try and use
+`packageJS` which doesn't perform any optimizations (use the `-pack`
+version of the HTML files).
+
+### Use your fork with your own projects
+
+Simply publish it locally with:
+
+ sbt> publishLocal
+ sbt> tools/publishLocal
+ sbt> sbtPlugin/publishLocal
+
+## License
+
+Scala.js is distributed under the
+[Scala License](http://www.scala-lang.org/license.html).
diff --git a/examples/scala-js/TESTING b/examples/scala-js/TESTING
new file mode 100644
index 0000000..7d393a4
--- /dev/null
+++ b/examples/scala-js/TESTING
@@ -0,0 +1,67 @@
+This file contains test cases that should be manually executed.
+
+## CLI Distribution
+
+For each major Scala version on a *NIX distro and a Windows distro:
+
+1. Download packaged Scala from scala-lang.org
+2. Build Scala.js CLI distribution (e.g. `./assemble-cli.sh 2.10`)
+3. Unpack Scala and Scala.js distro
+4. Add `bin/` directories of both distributions to path (`export PATH=$PATH:<scala path>/bin:<scala.js path>/bin`)
+5. Create a temporary directory and do:
+
+ mkdir bin
+ echo 'import scala.scalajs.js.JSApp
+ object Foo extends JSApp {
+
+ def main() = {
+ println(s"asdf ${1 + 1}")
+ new A
+ }
+
+ class A
+ }' > foo.scala
+ scalajsc -d bin foo.scala
+
+ scalajsp bin/Foo$.sjsir
+ # Verify output
+ scalajsp bin/Foo\$A.sjsir
+ # Verify output
+
+ scalajsld -o test.js bin
+ # Verify output
+
+ echo "Foo().main()" >> test.js
+ node test.js # Or your favorite thing to run JS
+
+ # Expect "asdf 2"
+
+## HTML-Runners
+
+The following HTML-runners/testers must be manually tested:
+
+ examples/helloworld/helloworld-{2.10|2.11}{|-fastopt}.html
+ examples/reversi/reversi-{2.10|2.11}{|-fastopt}.html
+ examples/testing/testing-{2.10|2.11}{|-fastopt}.html
+ test-suite/scalajs-test-suite-{2.10|2.11}{|-fastopt}.html
+
+## Sourcemaps
+
+To test source maps, do the following on:
+
+ examples/reversi/reversi-{2.10|2.11}{|-fastopt}.html
+
+1. Open the respective file in Google Chrome
+2. Set a break-point in the HTML launcher on the `new Reversi` statement
+3. Step over calls to jQuery into constructor
+4. Step into the call to `Array.tabulate` and verify that source maps
+ to Scala standard library sources work (should point to GitHub)
+5. Single step through constructor, until you reach `buildUI()`
+6. Step into `buildUI()`
+
+
+## When releasing only
+
+Once all tests pass, tag the revision and verify that source maps to
+Scala.js sources work correctly (should point to GitHub), following
+the steps described in the section Sourcemaps.
diff --git a/examples/scala-js/ci/check-partest-coverage.sh b/examples/scala-js/ci/check-partest-coverage.sh
new file mode 100755
index 0000000..ca35f37
--- /dev/null
+++ b/examples/scala-js/ci/check-partest-coverage.sh
@@ -0,0 +1,58 @@
+#! /bin/sh
+
+# This script tests if all Scala partests are classified. Since
+# Scala.js does not provide all the Scala functionality (see [1]), we
+# have to exclude some partests from testing. Therefore, every partest
+# in $TESTDIR has to be in exactly one of the following files located
+# in $KNOWDIR:
+# - WhitelistedTests.txt: Tests that succeed
+# - BlacklistedTests.txt: Tests that fail since they test for behavior
+# which is not supported in Scala.js
+# - BuglistedTests.txt: Tests that fail due to a bug in Scala.js
+#
+# [1] http://www.scala-js.org/doc/semantics.html
+
+# Arguments
+if [ $# -le 0 ]; then
+ echo "Please give full scala version as argument" >&2
+ exit 42
+fi
+
+FULLVER="$1"
+
+# Config
+BASEDIR="`dirname $0`/.."
+TESTDIR="$BASEDIR/partest/fetchedSources/$1/test/files"
+KNOWDIR="$BASEDIR/partest-suite/src/test/resources/scala/tools/partest/scalajs/$1/"
+
+# If the classification directory does not exist, this means (by
+# definition) that we do not want to or cannot partest this scala
+# version. Therefore, everything is OK.
+if [ ! -d $KNOWDIR ]; then
+ exit 0
+fi
+
+# Temp files
+TMP_PREF=`basename $0`
+TMP_HAVE_FILE=`mktemp /tmp/${TMP_PREF}_have_XXXXX` || exit 2
+TMP_KNOW_FILE=`mktemp /tmp/${TMP_PREF}_know_XXXXX` || exit 2
+
+# Trap removal of tmp files on exit
+trap "rm \"$TMP_HAVE_FILE\" \"$TMP_KNOW_FILE\"" EXIT
+
+# Find all partests
+( # Subshell to protect cwd
+cd "$TESTDIR"
+find "run" "neg" "pos" \
+ -mindepth 1 -maxdepth 1 \( -type d -or -name '*.scala' \) \
+ | sort >> $TMP_HAVE_FILE
+)
+
+# Find classified partests
+( # Subshell to protect cwd
+cd "$KNOWDIR"
+cat BlacklistedTests.txt BuglistedTests.txt WhitelistedTests.txt \
+ | grep -E -v '^#|^\s*$' | sort >> $TMP_KNOW_FILE
+)
+
+diff -U 0 --label 'Classified Tests' $TMP_KNOW_FILE --label 'Existing Tests' $TMP_HAVE_FILE
diff --git a/examples/scala-js/ci/checksizes.sh b/examples/scala-js/ci/checksizes.sh
new file mode 100755
index 0000000..8e7a508
--- /dev/null
+++ b/examples/scala-js/ci/checksizes.sh
@@ -0,0 +1,65 @@
+#!/bin/sh
+
+BASEDIR="`dirname $0`/.."
+
+FULLVER="$1"
+
+case $FULLVER in
+ 2.10.2)
+ VER=2.10
+ ;;
+ 2.11.2)
+ VER=2.11
+ ;;
+ 2.11.4)
+ VER=2.11
+ ;;
+ 2.10.3|2.10.4|2.11.0|2.11.1)
+ echo "Ignoring checksizes for Scala $FULLVER"
+ exit 0
+ ;;
+esac
+
+REVERSI_PREOPT="$BASEDIR/examples/reversi/target/scala-$VER/reversi-fastopt.js"
+REVERSI_OPT="$BASEDIR/examples/reversi/target/scala-$VER/reversi-opt.js"
+
+REVERSI_PREOPT_SIZE=$(stat '-c%s' "$REVERSI_PREOPT")
+REVERSI_OPT_SIZE=$(stat '-c%s' "$REVERSI_OPT")
+
+gzip "$REVERSI_PREOPT"
+gzip "$REVERSI_OPT"
+
+REVERSI_PREOPT_GZ_SIZE=$(stat '-c%s' "$REVERSI_PREOPT.gz")
+REVERSI_OPT_GZ_SIZE=$(stat '-c%s' "$REVERSI_OPT.gz")
+
+case $FULLVER in
+ 2.10.2)
+ REVERSI_PREOPT_EXPECTEDSIZE=1008000
+ REVERSI_OPT_EXPECTEDSIZE=161000
+ REVERSI_PREOPT_GZ_EXPECTEDSIZE=109000
+ REVERSI_OPT_GZ_EXPECTEDSIZE=43000
+ ;;
+ 2.11.2)
+ REVERSI_PREOPT_EXPECTEDSIZE=943000
+ REVERSI_OPT_EXPECTEDSIZE=151000
+ REVERSI_PREOPT_GZ_EXPECTEDSIZE=104000
+ REVERSI_OPT_GZ_EXPECTEDSIZE=39000
+ ;;
+ 2.11.4)
+ REVERSI_PREOPT_EXPECTEDSIZE=946000
+ REVERSI_OPT_EXPECTEDSIZE=152000
+ REVERSI_PREOPT_GZ_EXPECTEDSIZE=104000
+ REVERSI_OPT_GZ_EXPECTEDSIZE=40000
+ ;;
+esac
+
+echo "Checksizes: Scala version: $FULLVER"
+echo "Reversi preopt size = $REVERSI_PREOPT_SIZE (expected $REVERSI_PREOPT_EXPECTEDSIZE)"
+echo "Reversi opt size = $REVERSI_OPT_SIZE (expected $REVERSI_OPT_EXPECTEDSIZE)"
+echo "Reversi preopt gzip size = $REVERSI_PREOPT_GZ_SIZE (expected $REVERSI_PREOPT_GZ_EXPECTEDSIZE)"
+echo "Reversi opt gzip size = $REVERSI_OPT_GZ_SIZE (expected $REVERSI_OPT_GZ_EXPECTEDSIZE)"
+
+[ "$REVERSI_PREOPT_SIZE" -le "$REVERSI_PREOPT_EXPECTEDSIZE" ] && \
+ [ "$REVERSI_OPT_SIZE" -le "$REVERSI_OPT_EXPECTEDSIZE" ] && \
+ [ "$REVERSI_PREOPT_GZ_SIZE" -le "$REVERSI_PREOPT_GZ_EXPECTEDSIZE" ] && \
+ [ "$REVERSI_OPT_GZ_SIZE" -le "$REVERSI_OPT_GZ_EXPECTEDSIZE" ]
diff --git a/examples/scala-js/ci/matrix.xml b/examples/scala-js/ci/matrix.xml
new file mode 100644
index 0000000..5c02146
--- /dev/null
+++ b/examples/scala-js/ci/matrix.xml
@@ -0,0 +1,360 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE ci [
+ <!ELEMENT ci (task*,matrix*)>
+ <!ELEMENT task (#PCDATA)>
+ <!ATTLIST task id ID #REQUIRED>
+ <!ELEMENT matrix (run*)>
+ <!ATTLIST matrix id ID #REQUIRED>
+ <!ELEMENT run (v*)>
+ <!ATTLIST run matrix IDREF #IMPLIED>
+ <!ATTLIST run task IDREF #IMPLIED>
+ <!ELEMENT v (#PCDATA)>
+ <!ATTLIST v n CDATA #REQUIRED>
+]>
+<ci>
+
+ <task id="main"><![CDATA[
+ export JAVA_HOME=$HOME/apps/java-$java
+ sbt ++$scala package packageDoc &&
+ sbt ++$scala helloworld/run \
+ 'set scalaJSStage in Global := FastOptStage' \
+ ++$scala helloworld/run \
+ 'set scalaJSStage in Global := FullOptStage' \
+ ++$scala helloworld/run \
+ helloworld/clean &&
+ sbt 'set scalaJSOptimizerOptions in ScalaJSBuild.helloworld ~= (_.withDisableOptimizer(true))' \
+ 'set scalaJSStage in Global := FastOptStage' \
+ ++$scala helloworld/run \
+ helloworld/clean &&
+ sbt 'set inScope(ThisScope in ScalaJSBuild.helloworld)(postLinkJSEnv := PhantomJSEnv().value)' \
+ 'set scalaJSStage in Global := FastOptStage' \
+ ++$scala helloworld/run \
+ 'set scalaJSStage in Global := FullOptStage' \
+ ++$scala helloworld/run &&
+ sbt ++$scala testingExample/test \
+ 'set scalaJSStage in Global := FastOptStage' \
+ ++$scala testingExample/test \
+ 'set scalaJSStage in Global := FullOptStage' \
+ ++$scala testingExample/test \
+ testingExample/clean &&
+ sbt 'set scalaJSOptimizerOptions in ScalaJSBuild.testingExample ~= (_.withDisableOptimizer(true))' \
+ 'set scalaJSStage in Global := FastOptStage' \
+ ++$scala testingExample/test &&
+ sbt ++$scala "testSuite/testOnly -- -- -trhino -tsource-maps" \
+ "noIrCheckTest/testOnly -- -- -trhino -tsource-maps" \
+ 'set scalaJSStage in Global := FastOptStage' \
+ ++$scala "testSuite/testOnly -- -- -ttypedarray -tnodejs -tsource-maps" \
+ "noIrCheckTest/testOnly -- -- -ttypedarray -tnodejs -tsource-maps" \
+ 'set scalaJSStage in Global := FullOptStage' \
+ ++$scala "testSuite/testOnly -- -- -ttypedarray -tnodejs -tsource-maps" \
+ "noIrCheckTest/testOnly -- -- -ttypedarray -tnodejs -tsource-maps" \
+ testSuite/clean &&
+ sbt 'set scalaJSOptimizerOptions in ScalaJSBuild.testSuite ~= (_.withDisableOptimizer(true))' \
+ 'set scalaJSStage in Global := FastOptStage' \
+ ++$scala "testSuite/testOnly -- -- -ttypedarray -tnodejs -tsource-maps" \
+ testSuite/clean &&
+ sbt 'set scalaJSSemantics in ScalaJSBuild.testSuite ~= { _.withAsInstanceOfs(scala.scalajs.tools.sem.CheckedBehavior.Compliant).withStrictFloats(true) }' \
+ ++$scala "testSuite/testOnly -- -- -trhino -tsource-maps -tcompliant-asinstanceofs -tstrict-floats" \
+ 'set scalaJSStage in Global := FastOptStage' \
+ ++$scala "testSuite/testOnly -- -- -ttypedarray -tnodejs -tsource-maps -tcompliant-asinstanceofs -tstrict-floats" \
+ 'set scalaJSStage in Global := FullOptStage' \
+ ++$scala "testSuite/testOnly -- -- -ttypedarray -tnodejs -tsource-maps -tcompliant-asinstanceofs -tstrict-floats" \
+ testSuite/clean &&
+ sbt 'set scalaJSSemantics in ScalaJSBuild.testSuite ~= { _.withAsInstanceOfs(scala.scalajs.tools.sem.CheckedBehavior.Compliant).withStrictFloats(true) }' \
+ 'set scalaJSOptimizerOptions in ScalaJSBuild.testSuite ~= (_.withDisableOptimizer(true))' \
+ 'set scalaJSStage in Global := FastOptStage' \
+ ++$scala "testSuite/testOnly -- -- -ttypedarray -tnodejs -tsource-maps -tcompliant-asinstanceofs -tstrict-floats" \
+ testSuite/clean &&
+ sbt 'set inScope(ThisScope in ScalaJSBuild.helloworld)(postLinkJSEnv := PhantomJSEnv().value)' \
+ 'set scalaJSStage in Global := FastOptStage' \
+ ++$scala "testSuite/testOnly -- -- -tphantomjs" \
+ 'set scalaJSStage in Global := FullOptStage' \
+ ++$scala "testSuite/testOnly -- -- -tphantomjs" \
+ testSuite/clean &&
+ sbt 'set scalacOptions in ScalaJSBuild.testSuite += "-Xexperimental"' \
+ ++$scala "testSuite/testOnly -- -- -trhino -tsource-maps" \
+ 'set scalaJSStage in Global := FastOptStage' \
+ ++$scala "testSuite/testOnly -- -- -ttypedarray -tnodejs -tsource-maps" \
+ 'set scalaJSStage in Global := FullOptStage' \
+ ++$scala "testSuite/testOnly -- -- -ttypedarray -tnodejs -tsource-maps" &&
+ sbt 'set scalaJSStage in Global := FastOptStage' \
+ ++$scala "javalibExTestSuite/testOnly -- -- -ttypedarray -tnodejs -tsource-maps" \
+ 'set scalaJSStage in Global := FullOptStage' \
+ ++$scala "javalibExTestSuite/testOnly -- -- -ttypedarray -tnodejs -tsource-maps" &&
+ sbt ++$scala compiler/test reversi/fastOptJS reversi/fullOptJS &&
+ sbt ++$scala partest/fetchScalaSource &&
+ sh ci/checksizes.sh $scala &&
+ sh ci/check-partest-coverage.sh $scala
+ ]]></task>
+
+ <task id="bootstrap"><![CDATA[
+ export JAVA_HOME=$HOME/apps/java-$java
+ sbt 'set scalaJSStage in Global := FastOptStage' \
+ ++$scala toolsJS/test \
+ 'set scalaJSStage in Global := FullOptStage' \
+ ++$scala toolsJS/test
+ ]]></task>
+
+ <task id="tools-cli-stubs"><![CDATA[
+ export JAVA_HOME=$HOME/apps/java-$java
+ sbt ++$scala tools/package tools/test cli/assembly stubs/package
+ ]]></task>
+
+ <task id="tools-cli-stubs-sbtplugin"><![CDATA[
+ export JAVA_HOME=$HOME/apps/java-$java
+ sbt ++$scala tools/package tools/test cli/assembly stubs/package \
+ sbtPlugin/package sbtPlugin/test
+ ]]></task>
+
+ <task id="partestc"><![CDATA[
+ export JAVA_HOME=$HOME/apps/java-$java
+ sbt ++$scala partest/compile
+ ]]></task>
+
+ <task id="sbtplugin-test"><![CDATA[
+ # Publish Scala.js artifacts locally
+ sbt ++2.11.2 publishLocal ++2.10.4 tools/publishLocal sbtPlugin/publishLocal
+ # Go into standalone project and test
+ cd sbt-plugin-test
+ sbt noDOM/run withDOM/run test \
+ 'set scalaJSStage in Global := FastOptStage' \
+ jetty9/run test
+ ]]></task>
+
+ <task id="partest-noopt"><![CDATA[
+ export JAVA_HOME=$HOME/apps/java-$java
+ sbt ++$scala package "partestSuite/testOnly -- --showDiff"
+ ]]></task>
+
+ <task id="partest-fastopt"><![CDATA[
+ export JAVA_HOME=$HOME/apps/java-$java
+ sbt ++$scala package "partestSuite/testOnly -- --fastOpt --showDiff"
+ ]]></task>
+
+ <task id="partest-fullopt"><![CDATA[
+ export JAVA_HOME=$HOME/apps/java-$java
+ sbt ++$scala package "partestSuite/testOnly -- --fullOpt --showDiff"
+ ]]></task>
+
+ <matrix id="pr">
+ <!-- Main test tasks -->
+ <run task="main">
+ <v n="scala">2.10.2</v>
+ <v n="java">1.6</v>
+ </run>
+ <run task="main">
+ <v n="scala">2.10.2</v>
+ <v n="java">1.7</v>
+ </run>
+ <run task="main">
+ <v n="scala">2.10.2</v>
+ <v n="java">1.8</v>
+ </run>
+ <run task="main">
+ <v n="scala">2.11.2</v>
+ <v n="java">1.6</v>
+ </run>
+ <run task="main">
+ <v n="scala">2.11.2</v>
+ <v n="java">1.7</v>
+ </run>
+ <run task="main">
+ <v n="scala">2.11.2</v>
+ <v n="java">1.8</v>
+ </run>
+ <run task="main">
+ <v n="scala">2.11.4</v>
+ <v n="java">1.7</v>
+ </run>
+
+ <!-- Bootstrap test tasks -->
+ <run task="bootstrap">
+ <v n="scala">2.10.2</v>
+ <v n="java">1.6</v>
+ </run>
+ <run task="bootstrap">
+ <v n="scala">2.10.2</v>
+ <v n="java">1.7</v>
+ </run>
+ <run task="bootstrap">
+ <v n="scala">2.10.2</v>
+ <v n="java">1.8</v>
+ </run>
+ <!-- Tools do not compile on JDK6, Scala 2.11.x (see #1235) -->
+ <run task="bootstrap">
+ <v n="scala">2.11.2</v>
+ <v n="java">1.7</v>
+ </run>
+ <run task="bootstrap">
+ <v n="scala">2.11.2</v>
+ <v n="java">1.8</v>
+ </run>
+ <!-- Tools do not compile on Scala 2.11.4 (see #1215). -->
+
+ <!-- Tools / CLI / Stubs / sbtPlugin test tasks -->
+ <run task="tools-cli-stubs-sbtplugin">
+ <v n="scala">2.10.4</v>
+ <v n="java">1.6</v>
+ </run>
+ <run task="tools-cli-stubs-sbtplugin">
+ <v n="scala">2.10.4</v>
+ <v n="java">1.7</v>
+ </run>
+ <run task="tools-cli-stubs-sbtplugin">
+ <v n="scala">2.10.4</v>
+ <v n="java">1.8</v>
+ </run>
+ <!-- Tools do not compile on JDK6, Scala 2.11.x (see #1235) -->
+ <run task="tools-cli-stubs">
+ <v n="scala">2.11.2</v>
+ <v n="java">1.7</v>
+ </run>
+ <run task="tools-cli-stubs">
+ <v n="scala">2.11.2</v>
+ <v n="java">1.8</v>
+ </run>
+ <!-- Tools do not compile on Scala 2.11.4 (see #1215). -->
+
+ <!-- Partest compilation test tasks -->
+ <run task="partestc">
+ <v n="scala">2.11.0</v>
+ <v n="java">1.7</v>
+ </run>
+ <run task="partestc">
+ <v n="scala">2.11.1</v>
+ <v n="java">1.7</v>
+ </run>
+ <run task="partestc">
+ <v n="scala">2.11.2</v>
+ <v n="java">1.7</v>
+ </run>
+ <!-- Partest does not compile on Scala 2.11.4 (see #1215). -->
+
+ <run task="sbtplugin-test" />
+ </matrix>
+
+ <matrix id="nightly">
+ <run matrix="pr" />
+
+ <!-- Main test tasks (all remaining Scala versions) -->
+ <run task="main">
+ <v n="scala">2.10.3</v>
+ <v n="java">1.7</v>
+ </run>
+ <run task="main">
+ <v n="scala">2.10.4</v>
+ <v n="java">1.6</v>
+ </run>
+ <run task="main">
+ <v n="scala">2.10.4</v>
+ <v n="java">1.7</v>
+ </run>
+ <run task="main">
+ <v n="scala">2.10.4</v>
+ <v n="java">1.8</v>
+ </run>
+ <run task="main">
+ <v n="scala">2.11.0</v>
+ <v n="java">1.7</v>
+ </run>
+ <run task="main">
+ <v n="scala">2.11.1</v>
+ <v n="java">1.7</v>
+ </run>
+ <run task="main">
+ <v n="scala">2.11.4</v>
+ <v n="java">1.6</v>
+ </run>
+ <run task="main">
+ <v n="scala">2.11.4</v>
+ <v n="java">1.8</v>
+ </run>
+
+ <!-- Bootstrap test tasks (all remaining Scala versions) -->
+ <run task="bootstrap">
+ <v n="scala">2.10.3</v>
+ <v n="java">1.7</v>
+ </run>
+ <run task="bootstrap">
+ <v n="scala">2.10.4</v>
+ <v n="java">1.6</v>
+ </run>
+ <run task="bootstrap">
+ <v n="scala">2.10.4</v>
+ <v n="java">1.7</v>
+ </run>
+ <run task="bootstrap">
+ <v n="scala">2.10.4</v>
+ <v n="java">1.8</v>
+ </run>
+ <run task="bootstrap">
+ <v n="scala">2.11.0</v>
+ <v n="java">1.7</v>
+ </run>
+ <run task="bootstrap">
+ <v n="scala">2.11.1</v>
+ <v n="java">1.7</v>
+ </run>
+
+ <run task="partest-noopt">
+ <v n="scala">2.11.2</v>
+ <v n="java">1.7</v>
+ </run>
+ <run task="partest-fastopt">
+ <v n="scala">2.11.2</v>
+ <v n="java">1.7</v>
+ </run>
+ <run task="partest-fullopt">
+ <v n="scala">2.11.2</v>
+ <v n="java">1.7</v>
+ </run>
+ </matrix>
+
+ <matrix id="weekly">
+ <!-- weekly does not have to run nightly, since they will run at the same time -->
+
+ <run task="partest-noopt">
+ <v n="scala">2.11.0</v>
+ <v n="java">1.7</v>
+ </run>
+ <run task="partest-fastopt">
+ <v n="scala">2.11.0</v>
+ <v n="java">1.7</v>
+ </run>
+ <run task="partest-fullopt">
+ <v n="scala">2.11.0</v>
+ <v n="java">1.7</v>
+ </run>
+ <run task="partest-noopt">
+ <v n="scala">2.11.1</v>
+ <v n="java">1.7</v>
+ </run>
+ <run task="partest-fastopt">
+ <v n="scala">2.11.1</v>
+ <v n="java">1.7</v>
+ </run>
+ <run task="partest-fullopt">
+ <v n="scala">2.11.1</v>
+ <v n="java">1.7</v>
+ </run>
+ <run task="partest-noopt">
+ <v n="scala">2.11.2</v>
+ <v n="java">1.8</v>
+ </run>
+ <run task="partest-fastopt">
+ <v n="scala">2.11.2</v>
+ <v n="java">1.8</v>
+ </run>
+ <run task="partest-fullopt">
+ <v n="scala">2.11.2</v>
+ <v n="java">1.8</v>
+ </run>
+ <!--
+ Partest does sometimes not compile on JDK6 (see #1227) we
+ therefore do not run any JDK6 partests.
+
+ Partest does not compile on Scala 2.11.4 (see #1215).
+ -->
+ </matrix>
+
+</ci>
diff --git a/examples/scala-js/ci/scalajs-matrix-build.groovy b/examples/scala-js/ci/scalajs-matrix-build.groovy
new file mode 100644
index 0000000..f2a661a
--- /dev/null
+++ b/examples/scala-js/ci/scalajs-matrix-build.groovy
@@ -0,0 +1,57 @@
+out.println("Trying to run matrix ${params.matrix}")
+
+out.println("Loading ci/matrix.xml")
+
+def matrixFile = build.properties.moduleRoot.child("ci/matrix.xml")
+def fact = javax.xml.parsers.DocumentBuilderFactory.newInstance()
+def builder = fact.newDocumentBuilder()
+
+ciMatrix = builder.parse(matrixFile.read())
+
+out.println("Loading relevant definitions")
+
+buildDefs = []
+
+fetchMatrix(params.matrix)
+
+def fetchMatrix(matrixName) {
+ def matrix = ciMatrix.getElementById(matrixName)
+ def list = matrix.getElementsByTagName("run")
+ for (int i = 0; i < list.getLength(); ++i) {
+ handleRun(list.item(i))
+ }
+}
+
+def handleRun(run) {
+ def attrs = run.getAttributes()
+ def matrixAttr = attrs.getNamedItem("matrix")
+ def taskAttr = attrs.getNamedItem("task")
+ if (matrixAttr != null)
+ fetchMatrix(matrixAttr.getValue())
+ else if (taskAttr != null)
+ fetchTask(taskAttr.getValue(), run)
+}
+
+def fetchTask(taskName, runElem) {
+ def taskElem = ciMatrix.getElementById(taskName)
+ def taskStr = taskElem.getTextContent()
+ def fullTaskName = taskName
+
+ runElem.getElementsByTagName("v").each { v ->
+ def name = v.getAttribute("n")
+ def value = v.getTextContent()
+ taskStr = taskStr.replace('$' + name, value)
+ fullTaskName += " $name=$value"
+ }
+
+ out.println("Found task: $fullTaskName")
+
+ buildDefs.add({
+ build("scalajs-task-worker",
+ refspec: params.refspec,
+ sha1: params.sha1,
+ taskCommand: taskStr)
+ })
+}
+
+parallel(buildDefs)
diff --git a/examples/scala-js/cli/src/main/resources/scalajsc b/examples/scala-js/cli/src/main/resources/scalajsc
new file mode 100755
index 0000000..7fd1100
--- /dev/null
+++ b/examples/scala-js/cli/src/main/resources/scalajsc
@@ -0,0 +1,16 @@
+#! /bin/sh
+
+SCALA_BIN_VER="@SCALA_BIN_VER@"
+SCALAJS_VER="@SCALAJS_VER@"
+SCALA_VER=$(scalac -version 2>&1 | grep -o '[0-9]\.[0-9][0-9]\.[0-9]')
+
+if [ "$(echo $SCALA_VER | cut -b 1-4)" != "$SCALA_BIN_VER" ]; then
+ echo "This bundle of Scala.js CLI is for $SCALA_BIN_VER. Your scala version is $SCALA_VER!" >&2
+ exit 1
+fi
+
+BASE="$(dirname $0)/.."
+PLUGIN="$BASE/lib/scalajs-compiler_$SCALA_VER-$SCALAJS_VER.jar"
+JSLIB="$BASE/lib/scalajs-library_$SCALA_BIN_VER-$SCALAJS_VER.jar"
+
+scalac -classpath "$JSLIB" "-Xplugin:$PLUGIN" "$@"
diff --git a/examples/scala-js/cli/src/main/resources/scalajsc.bat b/examples/scala-js/cli/src/main/resources/scalajsc.bat
new file mode 100644
index 0000000..767c5df
--- /dev/null
+++ b/examples/scala-js/cli/src/main/resources/scalajsc.bat
@@ -0,0 +1,14 @@
+@ECHO OFF
+set SCALA_BIN_VER=@SCALA_BIN_VER@
+set SCALAJS_VER=@SCALAJS_VER@
+
+for /F "tokens=5" %%i in (' scala -version 2^>^&1 1^>nul ') do set SCALA_VER=%%i
+
+if NOT "%SCALA_VER:~0,4%" == "%SCALA_BIN_VER%" (
+ echo "This bundle of Scala.js CLI is for %SCALA_BIN_VER%. Your scala version is %SCALA_VER%!" 1>&2
+) else (
+ set PLUGIN=%~dp0\..\lib\scalajs-compiler_%SCALA_VER%-%SCALAJS_VER%.jar
+ set JSLIB=%~dp0\..\lib\scalajs-library_%SCALA_BIN_VER%-%SCALAJS_VER%.jar
+
+ scalac -classpath "%JSLIB%" "-Xplugin:%PLUGIN%" %*
+)
diff --git a/examples/scala-js/cli/src/main/resources/scalajsld b/examples/scala-js/cli/src/main/resources/scalajsld
new file mode 100755
index 0000000..7732e2a
--- /dev/null
+++ b/examples/scala-js/cli/src/main/resources/scalajsld
@@ -0,0 +1,10 @@
+#! /bin/sh
+
+SCALA_BIN_VER="@SCALA_BIN_VER@"
+SCALAJS_VER="@SCALAJS_VER@"
+
+BASE="$(dirname $0)/.."
+CLILIB="$BASE/lib/scalajs-cli-assembly_$SCALA_BIN_VER-$SCALAJS_VER.jar"
+JSLIB="$BASE/lib/scalajs-library_$SCALA_BIN_VER-$SCALAJS_VER.jar"
+
+scala -classpath "$CLILIB" scala.scalajs.cli.Scalajsld --stdlib "$JSLIB" "$@"
diff --git a/examples/scala-js/cli/src/main/resources/scalajsld.bat b/examples/scala-js/cli/src/main/resources/scalajsld.bat
new file mode 100644
index 0000000..e915237
--- /dev/null
+++ b/examples/scala-js/cli/src/main/resources/scalajsld.bat
@@ -0,0 +1,8 @@
+@ECHO OFF
+set SCALA_BIN_VER=@SCALA_BIN_VER@
+set SCALAJS_VER=@SCALAJS_VER@
+
+set CLILIB="%~dp0\..\lib\scalajs-cli-assembly_%SCALA_BIN_VER%-%SCALAJS_VER%.jar"
+set JSLIB="%~dp0\..\lib\scalajs-library_%SCALA_BIN_VER%-%SCALAJS_VER%.jar"
+
+scala -classpath %CLILIB% scala.scalajs.cli.Scalajsld --stdlib %JSLIB% %*
diff --git a/examples/scala-js/cli/src/main/resources/scalajsp b/examples/scala-js/cli/src/main/resources/scalajsp
new file mode 100755
index 0000000..e7a6e58
--- /dev/null
+++ b/examples/scala-js/cli/src/main/resources/scalajsp
@@ -0,0 +1,9 @@
+#! /bin/sh
+
+SCALA_BIN_VER="@SCALA_BIN_VER@"
+SCALAJS_VER="@SCALAJS_VER@"
+
+BASE="$(dirname $0)/.."
+CLILIB="$BASE/lib/scalajs-cli-assembly_$SCALA_BIN_VER-$SCALAJS_VER.jar"
+
+scala -classpath "$CLILIB" scala.scalajs.cli.Scalajsp "$@"
diff --git a/examples/scala-js/cli/src/main/resources/scalajsp.bat b/examples/scala-js/cli/src/main/resources/scalajsp.bat
new file mode 100644
index 0000000..dd9745c
--- /dev/null
+++ b/examples/scala-js/cli/src/main/resources/scalajsp.bat
@@ -0,0 +1,7 @@
+@ECHO OFF
+set SCALA_BIN_VER=@SCALA_BIN_VER@
+set SCALAJS_VER=@SCALAJS_VER@
+
+set CLILIB="%~dp0\..\lib\scalajs-cli-assembly_%SCALA_BIN_VER%-%SCALAJS_VER%.jar"
+
+scala -classpath %CLILIB% scala.scalajs.cli.Scalajsp %*
diff --git a/examples/scala-js/cli/src/main/scala/scala/scalajs/cli/Scalajsld.scala b/examples/scala-js/cli/src/main/scala/scala/scalajs/cli/Scalajsld.scala
new file mode 100644
index 0000000..55e61af
--- /dev/null
+++ b/examples/scala-js/cli/src/main/scala/scala/scalajs/cli/Scalajsld.scala
@@ -0,0 +1,180 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js CLI **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.cli
+
+import scala.scalajs.ir.ScalaJSVersions
+
+import scala.scalajs.tools.sem._
+import scala.scalajs.tools.io._
+import scala.scalajs.tools.logging._
+import scala.scalajs.tools.classpath._
+import scala.scalajs.tools.classpath.builder._
+
+import CheckedBehavior.Compliant
+
+import scala.scalajs.tools.optimizer.{
+ ScalaJSOptimizer,
+ ScalaJSClosureOptimizer,
+ ParIncOptimizer
+}
+
+import scala.collection.immutable.Seq
+
+import java.io.File
+import java.net.URI
+
+object Scalajsld {
+
+ case class Options(
+ cp: Seq[File] = Seq.empty,
+ output: File = null,
+ jsoutput: Option[File] = None,
+ semantics: Semantics = Semantics.Defaults,
+ noOpt: Boolean = false,
+ fullOpt: Boolean = false,
+ prettyPrint: Boolean = false,
+ sourceMap: Boolean = false,
+ relativizeSourceMap: Option[URI] = None,
+ checkIR: Boolean = false,
+ stdLib: Option[File] = None,
+ logLevel: Level = Level.Info)
+
+ def main(args: Array[String]): Unit = {
+ val parser = new scopt.OptionParser[Options]("scalajsld") {
+ head("scalajsld", ScalaJSVersions.current)
+ arg[File]("<value> ...")
+ .unbounded()
+ .action { (x, c) => c.copy(cp = c.cp :+ x) }
+ .text("Entries of Scala.js classpath to link")
+ opt[File]('o', "output")
+ .valueName("<file>")
+ .required()
+ .action { (x, c) => c.copy(output = x) }
+ .text("Output file of linker (required)")
+ opt[File]("jsoutput")
+ .valueName("<file>")
+ .abbr("jo")
+ .action { (x, c) => c.copy(jsoutput = Some(x)) }
+ .text("Concatenate all JavaScript libary dependencies to this file")
+ opt[Unit]('f', "fastOpt")
+ .action { (_, c) => c.copy(noOpt = false, fullOpt = false) }
+ .text("Optimize code (this is the default)")
+ opt[Unit]('n', "noOpt")
+ .action { (_, c) => c.copy(noOpt = true, fullOpt = false) }
+ .text("Don't optimize code")
+ opt[Unit]('u', "fullOpt")
+ .action { (_, c) => c.copy(noOpt = false, fullOpt = true) }
+ .text("Fully optimize code (uses Google Closure Compiler)")
+ opt[Unit]('p', "prettyPrint")
+ .action { (_, c) => c.copy(prettyPrint = true) }
+ .text("Pretty print full opted code (meaningful with -u)")
+ opt[Unit]('s', "sourceMap")
+ .action { (_, c) => c.copy(sourceMap = true) }
+ .text("Produce a source map for the produced code")
+ opt[Unit]("compliantAsInstanceOfs")
+ .action { (_, c) => c.copy(semantics =
+ c.semantics.withAsInstanceOfs(Compliant))
+ }
+ .text("Use compliant asInstanceOfs")
+ opt[Unit]('c', "checkIR")
+ .action { (_, c) => c.copy(checkIR = true) }
+ .text("Check IR before optimizing")
+ opt[File]('r', "relativizeSourceMap")
+ .valueName("<path>")
+ .action { (x, c) => c.copy(relativizeSourceMap = Some(x.toURI)) }
+ .text("Relativize source map with respect to given path (meaningful with -s)")
+ opt[Unit]("noStdlib")
+ .action { (_, c) => c.copy(stdLib = None) }
+ .text("Don't automatcially include Scala.js standard library")
+ opt[File]("stdlib")
+ .valueName("<scala.js stdlib jar>")
+ .hidden()
+ .action { (x, c) => c.copy(stdLib = Some(x)) }
+ .text("Location of Scala.js standard libarary. This is set by the " +
+ "runner script and automatically prepended to the classpath. " +
+ "Use -n to not include it.")
+ opt[Unit]('d', "debug")
+ .action { (_, c) => c.copy(logLevel = Level.Debug) }
+ .text("Debug mode: Show full log")
+ opt[Unit]('q', "quiet")
+ .action { (_, c) => c.copy(logLevel = Level.Warn) }
+ .text("Only show warnings & errors")
+ opt[Unit]("really-quiet")
+ .abbr("qq")
+ .action { (_, c) => c.copy(logLevel = Level.Error) }
+ .text("Only show errors")
+ version("version")
+ .abbr("v")
+ .text("Show scalajsld version")
+ help("help")
+ .abbr("h")
+ .text("prints this usage text")
+
+ override def showUsageOnError = true
+ }
+
+ for (options <- parser.parse(args, Options())) {
+ val cpFiles = options.stdLib.toList ++ options.cp
+ // Load and resolve classpath
+ val cp = PartialClasspathBuilder.build(cpFiles).resolve()
+
+ // Write JS dependencies if requested
+ for (jsout <- options.jsoutput)
+ IO.concatFiles(WritableFileVirtualJSFile(jsout), cp.jsLibs.map(_.lib))
+
+ // Link Scala.js code
+ val outFile = WritableFileVirtualJSFile(options.output)
+ if (options.fullOpt)
+ fullOpt(cp, outFile, options)
+ else
+ fastOpt(cp, outFile, options)
+ }
+ }
+
+ private def fullOpt(cp: IRClasspath,
+ output: WritableVirtualJSFile, options: Options) = {
+ import ScalaJSClosureOptimizer._
+
+ val semantics = options.semantics.optimized
+
+ new ScalaJSClosureOptimizer(semantics).optimizeCP(
+ newScalaJSOptimizer(semantics),
+ Inputs(ScalaJSOptimizer.Inputs(cp)),
+ OutputConfig(
+ output = output,
+ wantSourceMap = options.sourceMap,
+ relativizeSourceMapBase = options.relativizeSourceMap,
+ checkIR = options.checkIR,
+ prettyPrint = options.prettyPrint),
+ newLogger(options))
+ }
+
+ private def fastOpt(cp: IRClasspath,
+ output: WritableVirtualJSFile, options: Options) = {
+ import ScalaJSOptimizer._
+
+ newScalaJSOptimizer(options.semantics).optimizeCP(
+ Inputs(cp),
+ OutputConfig(
+ output = output,
+ wantSourceMap = options.sourceMap,
+ checkIR = options.checkIR,
+ disableOptimizer = options.noOpt,
+ relativizeSourceMapBase = options.relativizeSourceMap),
+ newLogger(options))
+ }
+
+ private def newLogger(options: Options) =
+ new ScalaConsoleLogger(options.logLevel)
+
+ private def newScalaJSOptimizer(semantics: Semantics) =
+ new ScalaJSOptimizer(semantics, new ParIncOptimizer(_))
+
+}
diff --git a/examples/scala-js/cli/src/main/scala/scala/scalajs/cli/Scalajsp.scala b/examples/scala-js/cli/src/main/scala/scala/scalajs/cli/Scalajsp.scala
new file mode 100644
index 0000000..0d64b93
--- /dev/null
+++ b/examples/scala-js/cli/src/main/scala/scala/scalajs/cli/Scalajsp.scala
@@ -0,0 +1,158 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js CLI **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.cli
+
+import scala.scalajs.ir
+import ir.ScalaJSVersions
+import ir.Trees.{Tree, ClassDef}
+import ir.Printers.{InfoPrinter, IRTreePrinter}
+
+import scala.scalajs.tools.sem.Semantics
+import scala.scalajs.tools.javascript
+import javascript.ScalaJSClassEmitter
+import javascript.Printers.JSTreePrinter
+
+import scala.scalajs.tools.io._
+import scala.collection.immutable.Seq
+
+import java.io.{Console => _, _}
+import java.util.zip.{ZipFile, ZipEntry}
+
+object Scalajsp {
+
+ case class Options(
+ infos: Boolean = false,
+ desugar: Boolean = false,
+ showReflProxy: Boolean = false,
+ jar: Option[File] = None,
+ fileNames: Seq[String] = Seq.empty)
+
+ def main(args: Array[String]): Unit = {
+ val parser = new scopt.OptionParser[Options]("scalajsp") {
+ head("scalajsp", ScalaJSVersions.current)
+ arg[String]("<file> ...")
+ .unbounded()
+ .action { (x, c) => c.copy(fileNames = c.fileNames :+ x) }
+ .text("*.sjsir file to display content of")
+ opt[File]('j', "jar")
+ .valueName("<jar>")
+ .action { (x, c) => c.copy(jar = Some(x)) }
+ .text("Read *.sjsir file(s) from the given JAR.")
+ opt[Unit]('d', "desugar")
+ .action { (_, c) => c.copy(desugar = true) }
+ .text("Desugar JS trees. This yields runnable JavaScript")
+ opt[Unit]('i', "infos")
+ .action { (_, c) => c.copy(infos = true) }
+ .text("Show DCE infos instead of trees")
+ opt[Unit]('p', "reflProxies")
+ .action { (_, c) => c.copy(showReflProxy = true) }
+ .text("Show reflective call proxies")
+ opt[Unit]('s', "supported")
+ .action { (_,_) => printSupported(); sys.exit() }
+ .text("Show supported Scala.js IR versions")
+ version("version")
+ .abbr("v")
+ .text("Show scalajsp version")
+ help("help")
+ .abbr("h")
+ .text("prints this usage text")
+
+ override def showUsageOnError = true
+ }
+
+ for {
+ options <- parser.parse(args, Options())
+ fileName <- options.fileNames
+ } {
+ val vfile = options.jar map { jar =>
+ readFromJar(jar, fileName)
+ } getOrElse {
+ readFromFile(fileName)
+ }
+
+ displayFileContent(vfile, options)
+ }
+ }
+
+ def printSupported(): Unit = {
+ import ScalaJSVersions._
+ println(s"Emitted Scala.js IR version is: $binaryEmitted")
+ println("Supported Scala.js IR versions are")
+ binarySupported.foreach(v => println(s"* $v"))
+ }
+
+ def displayFileContent(vfile: VirtualScalaJSIRFile, opts: Options): Unit = {
+ if (opts.infos)
+ new InfoPrinter(stdout).printClassInfo(vfile.info)
+ else {
+ val outTree = {
+ if (opts.showReflProxy) vfile.tree
+ else filterOutReflProxies(vfile.tree)
+ }
+
+ if (opts.desugar)
+ new JSTreePrinter(stdout).printTopLevelTree(
+ new ScalaJSClassEmitter(Semantics.Defaults).genClassDef(outTree))
+ else
+ new IRTreePrinter(stdout).printTopLevelTree(outTree)
+ }
+
+ stdout.flush()
+ }
+
+ private def fail(msg: String) = {
+ Console.err.println(msg)
+ sys.exit(1)
+ }
+
+ private def readFromFile(fileName: String) = {
+ val file = new File(fileName)
+
+ if (!file.exists)
+ fail(s"No such file: $fileName")
+ else if (!file.canRead)
+ fail(s"Unable to read file: $fileName")
+ else
+ FileVirtualScalaJSIRFile(file)
+ }
+
+ private def readFromJar(jar: File, name: String) = {
+ val jarFile =
+ try { new ZipFile(jar) }
+ catch { case _: FileNotFoundException => fail(s"No such JAR: $jar") }
+ try {
+ val entry = jarFile.getEntry(name)
+ if (entry == null)
+ fail(s"No such file in jar: $name")
+ else {
+ val name = jarFile.getName + "#" + entry.getName
+ val content =
+ IO.readInputStreamToByteArray(jarFile.getInputStream(entry))
+ new MemVirtualSerializedScalaJSIRFile(name).withContent(content)
+ }
+ } finally {
+ jarFile.close()
+ }
+ }
+
+ private val stdout =
+ new BufferedWriter(new OutputStreamWriter(Console.out, "UTF-8"))
+
+ private def filterOutReflProxies(tree: ClassDef): ClassDef = {
+ import ir.Trees._
+ import ir.Definitions.isReflProxyName
+ val newDefs = tree.defs.filter {
+ case MethodDef(Ident(name, _), _, _, _) => !isReflProxyName(name)
+ case _ => true
+ }
+ tree.copy(defs = newDefs)(tree.pos)
+ }
+
+}
diff --git a/examples/scala-js/compiler/src/main/resources/scalac-plugin.xml b/examples/scala-js/compiler/src/main/resources/scalac-plugin.xml
new file mode 100644
index 0000000..76ff1b7
--- /dev/null
+++ b/examples/scala-js/compiler/src/main/resources/scalac-plugin.xml
@@ -0,0 +1,4 @@
+<plugin>
+ <name>scalajs</name>
+ <classname>scala.scalajs.compiler.ScalaJSPlugin</classname>
+</plugin>
diff --git a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/ClassInfos.scala b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/ClassInfos.scala
new file mode 100644
index 0000000..026d664
--- /dev/null
+++ b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/ClassInfos.scala
@@ -0,0 +1,143 @@
+/* Scala.js compiler
+ * Copyright 2013 LAMP/EPFL
+ * @author Sébastien Doeraene
+ */
+
+package scala.scalajs.compiler
+
+import scala.language.implicitConversions
+
+import scala.collection.mutable
+import scala.tools.nsc._
+
+import java.io.{ File, PrintWriter, BufferedOutputStream, FileOutputStream }
+
+import scala.scalajs.ir
+import ir.{Trees => js, Types => jstpe, ClassKind}
+import ir.Infos._
+
+trait ClassInfos extends SubComponent { self: GenJSCode =>
+ import global._
+ import jsAddons._
+
+ /** Class data that are never eliminated by dce, so we don't need to
+ * record them.
+ */
+ private val AlwaysPresentClassData = {
+ import ir.Definitions._
+ Set("V", "Z", "C", "B", "S", "I", "J", "F", "D",
+ ObjectClass, StringClass)
+ }
+
+ class ClassInfoBuilder(val symbol: ClassSymbol) {
+ val name = classNameOf(symbol)
+ val encodedName = encodeClassFullName(symbol)
+ var isExported: Boolean = false
+ val ancestorCount = symbol.ancestors.count(!_.isInterface)
+ val kind = {
+ if (isStaticModule(symbol)) ClassKind.ModuleClass
+ else if (symbol.isInterface) ClassKind.Interface
+ else if (isRawJSType(symbol.tpe)) ClassKind.RawJSType
+ else if (isHijackedBoxedClass(symbol)) ClassKind.HijackedClass
+ else if (symbol.isImplClass) ClassKind.TraitImpl
+ else ClassKind.Class
+ }
+ val superClass =
+ if (kind.isClass || kind == ClassKind.HijackedClass)
+ encodeClassFullName(symbol.superClass)
+ else
+ ""
+ val ancestors = (symbol :: symbol.ancestors) map encodeClassFullName
+
+ var optimizerHints: OptimizerHints = OptimizerHints.empty
+
+ val methodInfos = mutable.ListBuffer.empty[MethodInfoBuilder]
+
+ def addMethod(encodedName: String, isAbstract: Boolean = false,
+ isExported: Boolean = false): MethodInfoBuilder = {
+ val b = new MethodInfoBuilder(encodedName, isAbstract, isExported)
+ methodInfos += b
+ b
+ }
+
+ def result(): ClassInfo = {
+ ClassInfo(name, encodedName, isExported, ancestorCount, kind,
+ superClass, ancestors, optimizerHints,
+ methodInfos.map(_.result()).result())
+ }
+ }
+
+ class MethodInfoBuilder(val encodedName: String,
+ val isAbstract: Boolean = false,
+ val isExported: Boolean = false) {
+
+ val calledMethods = mutable.Set.empty[(String, String)] // (tpe, method)
+ val calledMethodsStatic = mutable.Set.empty[(String, String)] // (class, method)
+ val instantiatedClasses = mutable.Set.empty[String]
+ val accessedModules = mutable.Set.empty[String]
+ val accessedClassData = mutable.Set.empty[String]
+ var optimizerHints: OptimizerHints = OptimizerHints.empty
+
+ def callsMethod(ownerIdent: js.Ident, method: js.Ident): Unit =
+ calledMethods += ((patchClassName(ownerIdent.name), method.name))
+
+ def callsMethod(owner: Symbol, method: js.Ident): Unit =
+ calledMethods += ((patchClassName(encodeClassFullName(owner)), method.name))
+
+ def callsMethodStatic(ownerIdent: js.Ident, method: js.Ident): Unit =
+ calledMethodsStatic += ((patchClassName(ownerIdent.name), method.name))
+
+ def instantiatesClass(classSym: Symbol): Unit =
+ instantiatedClasses += patchClassName(encodeClassFullName(classSym))
+
+ def accessesModule(moduleClassSym: Symbol): Unit =
+ accessedModules += patchModuleName(encodeModuleFullName(moduleClassSym))
+
+ def accessesClassData(refType: jstpe.ReferenceType): Unit = {
+ val className = refType match {
+ case jstpe.ClassType(name) => name
+ case jstpe.ArrayType(base, _) => base
+ }
+ if (!AlwaysPresentClassData.contains(className))
+ accessedClassData += className
+ }
+
+ def createsAnonFunction(funInfo: ClassInfoBuilder): Unit = {
+ for (methodInfo <- funInfo.methodInfos) {
+ calledMethods ++= methodInfo.calledMethods
+ calledMethodsStatic ++= methodInfo.calledMethodsStatic
+ instantiatedClasses ++= methodInfo.instantiatedClasses
+ accessedModules ++= methodInfo.accessedModules
+ accessedClassData ++= methodInfo.accessedClassData
+ }
+ }
+
+ private def patchClassName(name: String): String = name match {
+ case "jl_String$" => "sjsr_RuntimeString$"
+ case _ => name
+ }
+
+ private def patchModuleName(name: String): String = name match {
+ case "jl_String" => "sjsr_RuntimeString"
+ case _ => name
+ }
+
+ def result(): MethodInfo = {
+ MethodInfo(
+ encodedName,
+ isAbstract,
+ isExported,
+ calledMethods.toList.groupBy(_._1).mapValues(_.map(_._2)),
+ calledMethodsStatic.toList.groupBy(_._1).mapValues(_.map(_._2)),
+ instantiatedClasses.toList,
+ accessedModules.result.toList,
+ accessedClassData.result.toList,
+ optimizerHints
+ )
+ }
+ }
+
+ private def classNameOf(sym: Symbol): String =
+ if (needsModuleClassSuffix(sym)) sym.fullName + nme.MODULE_SUFFIX_STRING
+ else sym.fullName
+}
diff --git a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/Compat210Component.scala b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/Compat210Component.scala
new file mode 100644
index 0000000..f357337
--- /dev/null
+++ b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/Compat210Component.scala
@@ -0,0 +1,108 @@
+/* Scala.js compiler
+ * Copyright 2013 LAMP/EPFL
+ * @author Sébastien Doeraene
+ */
+
+package scala.scalajs.compiler
+
+import scala.tools.nsc._
+
+/** Hacks to have our source code compatible with 2.10 and 2.11.
+ * It exposes 2.11 API in a 2.10 compiler.
+ *
+ * @author Sébastien Doeraene
+ */
+trait Compat210Component {
+
+ val global: Global
+
+ import global._
+
+ // unexpandedName replaces originalName
+
+ implicit final class SymbolCompat(self: Symbol) {
+ def unexpandedName: Name = self.originalName
+ def originalName: Name = sys.error("infinite loop in Compat")
+
+ def isLocalToBlock: Boolean = self.isLocal
+ }
+
+ // enteringPhase/exitingPhase replace beforePhase/afterPhase
+
+ @inline final def enteringPhase[T](ph: Phase)(op: => T): T = {
+ global.enteringPhase(ph)(op)
+ }
+
+ @inline final def exitingPhase[T](ph: Phase)(op: => T): T = {
+ global.exitingPhase(ph)(op)
+ }
+
+ private implicit final class GlobalCompat(
+ self: Compat210Component.this.global.type) {
+
+ def enteringPhase[T](ph: Phase)(op: => T): T = self.beforePhase(ph)(op)
+ def beforePhase[T](ph: Phase)(op: => T): T = sys.error("infinite loop in Compat")
+
+ def exitingPhase[T](ph: Phase)(op: => T): T = self.afterPhase(ph)(op)
+ def afterPhase[T](ph: Phase)(op: => T): T = sys.error("infinite loop in Compat")
+ }
+
+ // ErasedValueType has a different encoding
+
+ implicit final class ErasedValueTypeCompat(self: global.ErasedValueType) {
+ def valueClazz: Symbol = self.original.typeSymbol
+ def erasedUnderlying: Type =
+ enteringPhase(currentRun.erasurePhase)(
+ erasure.erasedValueClassArg(self.original))
+ def original: TypeRef = sys.error("infinite loop in Compat")
+ }
+
+ // repeatedToSingle
+
+ @inline final def repeatedToSingle(t: Type) =
+ global.definitions.repeatedToSingle(t)
+
+ private implicit final class DefinitionsCompat(
+ self: Compat210Component.this.global.definitions.type) {
+
+ def repeatedToSingle(t: Type) = t match {
+ case TypeRef(_, self.RepeatedParamClass, arg :: Nil) => arg
+ case _ => t
+ }
+
+ }
+
+ // run.runDefinitions bundles methods and state related to the run
+ // that were previously in definitions itself
+
+ implicit final class RunCompat(self: Run) {
+ val runDefinitions: Compat210Component.this.global.definitions.type =
+ global.definitions
+ }
+
+ // Mode.FUNmode replaces analyzer.FUNmode
+
+ object Mode {
+ import Compat210Component.AnalyzerCompat
+ // No type ascription! Type is different in 2.10 / 2.11
+ val FUNmode = analyzer.FUNmode
+ }
+}
+
+object Compat210Component {
+ private object LowPriorityMode {
+ object Mode {
+ def FUNmode = sys.error("infinite loop in Compat")
+ }
+ }
+
+ private implicit final class AnalyzerCompat(self: scala.tools.nsc.typechecker.Analyzer) {
+ def FUNmode = {
+ import Compat210Component.LowPriorityMode._
+ {
+ import scala.reflect.internal._
+ Mode.FUNmode
+ }
+ }
+ }
+}
diff --git a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/GenJSCode.scala b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/GenJSCode.scala
new file mode 100644
index 0000000..f9885a0
--- /dev/null
+++ b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/GenJSCode.scala
@@ -0,0 +1,3911 @@
+/* Scala.js compiler
+ * Copyright 2013 LAMP/EPFL
+ * @author Sébastien Doeraene
+ */
+
+package scala.scalajs.compiler
+
+import scala.language.implicitConversions
+
+import scala.annotation.switch
+
+import scala.collection.mutable
+import scala.collection.mutable.ListBuffer
+
+import scala.tools.nsc._
+
+import scala.annotation.tailrec
+
+import scala.scalajs.ir
+import ir.{Trees => js, Types => jstpe, ClassKind, Hashers}
+
+import util.ScopedVar
+import ScopedVar.withScopedVars
+
+/** Generate JavaScript code and output it to disk
+ *
+ * @author Sébastien Doeraene
+ */
+abstract class GenJSCode extends plugins.PluginComponent
+ with TypeKinds
+ with JSEncoding
+ with GenJSExports
+ with ClassInfos
+ with GenJSFiles
+ with Compat210Component {
+
+ val jsAddons: JSGlobalAddons {
+ val global: GenJSCode.this.global.type
+ }
+
+ val scalaJSOpts: ScalaJSOptions
+
+ import global._
+ import jsAddons._
+ import rootMirror._
+ import definitions._
+ import jsDefinitions._
+ import JSTreeExtractors._
+
+ import treeInfo.hasSynthCaseSymbol
+
+ import platform.isMaybeBoxed
+
+ val phaseName = "jscode"
+
+ /** testing: this will be called when ASTs are generated */
+ def generatedJSAST(clDefs: List[js.Tree]): Unit
+
+ /** Implicit conversion from nsc Position to ir.Position. */
+ implicit def pos2irPos(pos: Position): ir.Position = {
+ if (pos == NoPosition) ir.Position.NoPosition
+ else {
+ val source = pos2irPosCache.toIRSource(pos.source)
+ // nsc positions are 1-based but IR positions are 0-based
+ ir.Position(source, pos.line-1, pos.column-1)
+ }
+ }
+
+ private[this] object pos2irPosCache {
+ import scala.reflect.internal.util._
+
+ private[this] var lastNscSource: SourceFile = null
+ private[this] var lastIRSource: ir.Position.SourceFile = null
+
+ def toIRSource(nscSource: SourceFile): ir.Position.SourceFile = {
+ if (nscSource != lastNscSource) {
+ lastIRSource = convert(nscSource)
+ lastNscSource = nscSource
+ }
+ lastIRSource
+ }
+
+ private[this] def convert(nscSource: SourceFile): ir.Position.SourceFile = {
+ nscSource.file.file match {
+ case null =>
+ new java.net.URI(
+ "virtualfile", // Pseudo-Scheme
+ nscSource.file.path, // Scheme specific part
+ null // Fragment
+ )
+ case file =>
+ val srcURI = file.toURI
+ def matches(pat: java.net.URI) = pat.relativize(srcURI) != srcURI
+
+ scalaJSOpts.sourceURIMaps.collectFirst {
+ case ScalaJSOptions.URIMap(from, to) if matches(from) =>
+ val relURI = from.relativize(srcURI)
+ to.fold(relURI)(_.resolve(relURI))
+ } getOrElse srcURI
+ }
+ }
+
+ def clear(): Unit = {
+ lastNscSource = null
+ lastIRSource = null
+ }
+ }
+
+ /** Materialize implicitly an ir.Position from an implicit nsc Position. */
+ implicit def implicitPos2irPos(implicit pos: Position): ir.Position = pos
+
+ override def newPhase(p: Phase) = new JSCodePhase(p)
+
+ private object jsnme {
+ val arg_outer = newTermName("arg$outer")
+ val newString = newTermName("newString")
+ }
+
+ class JSCodePhase(prev: Phase) extends StdPhase(prev) with JSExportsPhase {
+
+ override def name = phaseName
+ override def description = "Generate JavaScript code from ASTs"
+ override def erasedTypes = true
+
+ // Some state --------------------------------------------------------------
+
+ val currentClassSym = new ScopedVar[Symbol]
+ val currentClassInfoBuilder = new ScopedVar[ClassInfoBuilder]
+ val currentMethodSym = new ScopedVar[Symbol]
+ val currentMethodInfoBuilder = new ScopedVar[MethodInfoBuilder]
+ val methodTailJumpThisSym = new ScopedVar[Symbol](NoSymbol)
+ val fakeTailJumpParamRepl = new ScopedVar[(Symbol, Symbol)]((NoSymbol, NoSymbol))
+ val enclosingLabelDefParams = new ScopedVar(Map.empty[Symbol, List[Symbol]])
+ val mutableLocalVars = new ScopedVar[mutable.Set[Symbol]]
+ val mutatedLocalVars = new ScopedVar[mutable.Set[Symbol]]
+ val paramAccessorLocals = new ScopedVar(Map.empty[Symbol, js.ParamDef])
+
+ var isModuleInitialized: Boolean = false // see genApply for super calls
+
+ def currentClassType = encodeClassType(currentClassSym)
+
+ val tryingToGenMethodAsJSFunction = new ScopedVar[Boolean](false)
+ class CancelGenMethodAsJSFunction(message: String)
+ extends Throwable(message) with scala.util.control.ControlThrowable
+
+ // Rewriting of anonymous function classes ---------------------------------
+
+ private val translatedAnonFunctions =
+ mutable.Map.empty[Symbol,
+ (/*ctor args:*/ List[js.Tree] => /*instance:*/ js.Tree, ClassInfoBuilder)]
+ private val instantiatedAnonFunctions =
+ mutable.Set.empty[Symbol]
+ private val undefinedDefaultParams =
+ mutable.Set.empty[Symbol]
+
+ // Top-level apply ---------------------------------------------------------
+
+ override def run() {
+ scalaPrimitives.init()
+ jsPrimitives.init()
+ super.run()
+ }
+
+ /** Generates the Scala.js IR for a compilation unit
+ * This method iterates over all the class and interface definitions
+ * found in the compilation unit and emits their IR (.sjsir).
+ *
+ * Some classes are never actually emitted:
+ * - Classes representing primitive types
+ * - The scala.Array class
+ * - Implementation classes for raw JS traits
+ *
+ * Some classes representing anonymous functions are not actually emitted.
+ * Instead, a temporary representation of their `apply` method is built
+ * and recorded, so that it can be inlined as a JavaScript anonymous
+ * function in the method that instantiates it.
+ *
+ * Other ClassDefs are emitted according to their nature:
+ * * Raw JS type (<: js.Any) -> `genRawJSClassData()`
+ * * Interface -> `genInterface()`
+ * * Implementation class -> `genImplClass()`
+ * * Normal class -> `genClass()`
+ */
+ override def apply(cunit: CompilationUnit) {
+ try {
+ val generatedClasses = ListBuffer.empty[(Symbol, js.ClassDef, ClassInfoBuilder)]
+
+ def collectClassDefs(tree: Tree): List[ClassDef] = {
+ tree match {
+ case EmptyTree => Nil
+ case PackageDef(_, stats) => stats flatMap collectClassDefs
+ case cd: ClassDef => cd :: Nil
+ }
+ }
+ val allClassDefs = collectClassDefs(cunit.body)
+
+ /* First gen and record lambdas for js.FunctionN and js.ThisFunctionN.
+ * Since they are SAMs, there cannot be dependencies within this set,
+ * and hence we are sure we can record them before they are used,
+ * which is critical for these.
+ */
+ val nonRawJSFunctionDefs = allClassDefs filterNot { cd =>
+ if (isRawJSFunctionDef(cd.symbol)) {
+ genAndRecordRawJSFunctionClass(cd)
+ true
+ } else {
+ false
+ }
+ }
+
+ /* Then try to gen and record lambdas for scala.FunctionN.
+ * These may fail, and sometimes because of dependencies. Since there
+ * appears to be more forward dependencies than backward dependencies
+ * (at least for non-nested lambdas, which we cannot translate anyway),
+ * we process class defs in reverse order here.
+ */
+ val fullClassDefs = (nonRawJSFunctionDefs.reverse filterNot { cd =>
+ cd.symbol.isAnonymousFunction && tryGenAndRecordAnonFunctionClass(cd)
+ }).reverse
+
+ /* Finally, we emit true code for the remaining class defs. */
+ for (cd <- fullClassDefs) {
+ val sym = cd.symbol
+ implicit val pos = sym.pos
+
+ /* Do not actually emit code for primitive types nor scala.Array. */
+ val isPrimitive =
+ isPrimitiveValueClass(sym) || (sym == ArrayClass)
+
+ /* Similarly, do not emit code for impl classes of raw JS traits. */
+ val isRawJSImplClass =
+ sym.isImplClass && isRawJSType(
+ sym.owner.info.decl(sym.name.dropRight(nme.IMPL_CLASS_SUFFIX.length)).tpe)
+
+ if (!isPrimitive && !isRawJSImplClass) {
+ withScopedVars(
+ currentClassInfoBuilder := new ClassInfoBuilder(sym.asClass),
+ currentClassSym := sym
+ ) {
+ val tree = if (isRawJSType(sym.tpe)) {
+ assert(!isRawJSFunctionDef(sym),
+ s"Raw JS function def should have been recorded: $cd")
+ genRawJSClassData(cd)
+ } else if (sym.isInterface) {
+ genInterface(cd)
+ } else if (sym.isImplClass) {
+ genImplClass(cd)
+ } else {
+ genClass(cd)
+ }
+ generatedClasses += ((sym, tree, currentClassInfoBuilder.get))
+ }
+ }
+ }
+
+ val clDefs = generatedClasses.map(_._2).toList
+ generatedJSAST(clDefs)
+
+ for ((sym, tree, infoBuilder) <- generatedClasses) {
+ genIRFile(cunit, sym, tree, infoBuilder.result())
+ }
+ } finally {
+ translatedAnonFunctions.clear()
+ instantiatedAnonFunctions.clear()
+ undefinedDefaultParams.clear()
+ pos2irPosCache.clear()
+ }
+ }
+
+ // Generate a class --------------------------------------------------------
+
+ /** Gen the IR ClassDef for a class definition (maybe a module class).
+ */
+ def genClass(cd: ClassDef): js.ClassDef = {
+ val ClassDef(mods, name, _, impl) = cd
+ val sym = cd.symbol
+ implicit val pos = sym.pos
+
+ assert(!sym.isInterface && !sym.isImplClass,
+ "genClass() must be called only for normal classes: "+sym)
+ assert(sym.superClass != NoSymbol, sym)
+
+ val classIdent = encodeClassFullNameIdent(sym)
+ val isHijacked = isHijackedBoxedClass(sym)
+
+ // Optimizer hints
+
+ def isStdLibClassWithAdHocInlineAnnot(sym: Symbol): Boolean = {
+ val fullName = sym.fullName
+ (fullName.startsWith("scala.Tuple") && !fullName.endsWith("$")) ||
+ (fullName.startsWith("scala.collection.mutable.ArrayOps$of"))
+ }
+
+ if (sym.hasAnnotation(InlineAnnotationClass) ||
+ (sym.isAnonymousFunction && !sym.isSubClass(PartialFunctionClass)) ||
+ isStdLibClassWithAdHocInlineAnnot(sym))
+ currentClassInfoBuilder.optimizerHints =
+ currentClassInfoBuilder.optimizerHints.copy(hasInlineAnnot = true)
+
+ // Generate members (constructor + methods)
+
+ val generatedMembers = new ListBuffer[js.Tree]
+ val exportedSymbols = new ListBuffer[Symbol]
+
+ if (!isHijacked)
+ generatedMembers ++= genClassFields(cd)
+
+ def gen(tree: Tree): Unit = {
+ tree match {
+ case EmptyTree => ()
+ case Template(_, _, body) => body foreach gen
+
+ case ValDef(mods, name, tpt, rhs) =>
+ () // fields are added via genClassFields()
+
+ case dd: DefDef =>
+ val sym = dd.symbol
+
+ val isExport = jsInterop.isExport(sym)
+ val isNamedExport = isExport && sym.annotations.exists(
+ _.symbol == JSExportNamedAnnotation)
+
+ if (isNamedExport)
+ generatedMembers += genNamedExporterDef(dd)
+ else
+ generatedMembers ++= genMethod(dd)
+
+ if (isExport) {
+ // We add symbols that we have to export here. This way we also
+ // get inherited stuff that is implemented in this class.
+ exportedSymbols += sym
+ }
+
+ case _ => abort("Illegal tree in gen of genClass(): " + tree)
+ }
+ }
+
+ gen(impl)
+
+ // Create method info builder for exported stuff
+ val exports = withScopedVars(
+ currentMethodInfoBuilder := currentClassInfoBuilder.addMethod(
+ dceExportName + classIdent.name, isExported = true)
+ ) {
+ // Generate the exported members
+ val memberExports = genMemberExports(sym, exportedSymbols.toList)
+
+ // Generate exported constructors or accessors
+ val exportedConstructorsOrAccessors =
+ if (isStaticModule(sym)) genModuleAccessorExports(sym)
+ else genConstructorExports(sym)
+ if (exportedConstructorsOrAccessors.nonEmpty)
+ currentClassInfoBuilder.isExported = true
+
+ memberExports ++ exportedConstructorsOrAccessors
+ }
+
+ // Generate the reflective call proxies (where required)
+ val reflProxies =
+ if (isHijacked) Nil
+ else genReflCallProxies(sym)
+
+ // Hashed definitions of the class
+ val hashedDefs =
+ Hashers.hashDefs(generatedMembers.toList ++ exports ++ reflProxies)
+
+ // The complete class definition
+ val kind =
+ if (sym.isModuleClass) ClassKind.ModuleClass
+ else if (isHijacked) ClassKind.HijackedClass
+ else ClassKind.Class
+
+ val classDefinition = js.ClassDef(
+ classIdent,
+ kind,
+ Some(encodeClassFullNameIdent(sym.superClass)),
+ sym.ancestors.map(encodeClassFullNameIdent),
+ hashedDefs)
+
+ classDefinition
+ }
+
+ // Generate the class data of a raw JS class -------------------------------
+
+ /** Gen the IR ClassDef for a raw JS class or trait.
+ */
+ def genRawJSClassData(cd: ClassDef): js.ClassDef = {
+ val sym = cd.symbol
+ implicit val pos = sym.pos
+
+ // Check that RawJS type is not exported
+ for (exp <- jsInterop.exportsOf(sym))
+ reporter.error(exp.pos, "You may not export a class extending js.Any")
+
+ val classIdent = encodeClassFullNameIdent(sym)
+ js.ClassDef(classIdent, ClassKind.RawJSType, None, Nil, Nil)
+ }
+
+ // Generate an interface ---------------------------------------------------
+
+ /** Gen the IR ClassDef for an interface definition.
+ */
+ def genInterface(cd: ClassDef): js.ClassDef = {
+ val sym = cd.symbol
+ implicit val pos = sym.pos
+
+ val classIdent = encodeClassFullNameIdent(sym)
+
+ // fill in class info builder
+ def gen(tree: Tree) {
+ tree match {
+ case EmptyTree => ()
+ case Template(_, _, body) => body foreach gen
+ case dd: DefDef =>
+ currentClassInfoBuilder.addMethod(
+ encodeMethodName(dd.symbol), isAbstract = true)
+ case _ => abort("Illegal tree in gen of genInterface(): " + tree)
+ }
+ }
+ gen(cd.impl)
+
+ // Check that interface/trait is not exported
+ for (exp <- jsInterop.exportsOf(sym))
+ reporter.error(exp.pos, "You may not export a trait")
+
+ js.ClassDef(classIdent, ClassKind.Interface, None,
+ sym.ancestors.map(encodeClassFullNameIdent), Nil)
+ }
+
+ // Generate an implementation class of a trait -----------------------------
+
+ /** Gen the IR ClassDef for an implementation class (of a trait).
+ */
+ def genImplClass(cd: ClassDef): js.ClassDef = {
+ val ClassDef(mods, name, _, impl) = cd
+ val sym = cd.symbol
+ implicit val pos = sym.pos
+
+ def gen(tree: Tree): List[js.MethodDef] = {
+ tree match {
+ case EmptyTree => Nil
+ case Template(_, _, body) => body.flatMap(gen)
+
+ case dd: DefDef =>
+ val m = genMethod(dd)
+ m.toList
+
+ case _ => abort("Illegal tree in gen of genImplClass(): " + tree)
+ }
+ }
+ val generatedMethods = gen(impl)
+
+ js.ClassDef(encodeClassFullNameIdent(sym), ClassKind.TraitImpl,
+ None, Nil, generatedMethods)
+ }
+
+ // Generate the fields of a class ------------------------------------------
+
+ /** Gen definitions for the fields of a class.
+ * The fields are initialized with the zero of their types.
+ */
+ def genClassFields(cd: ClassDef): List[js.VarDef] = withScopedVars(
+ currentMethodInfoBuilder :=
+ currentClassInfoBuilder.addMethod("__init__")
+ ) {
+ // Non-method term members are fields, except for module members.
+ (for {
+ f <- currentClassSym.info.decls
+ if !f.isMethod && f.isTerm && !f.isModule
+ } yield {
+ implicit val pos = f.pos
+ js.VarDef(encodeFieldSym(f), toIRType(f.tpe),
+ mutable = f.isMutable, genZeroOf(f.tpe))
+ }).toList
+ }
+
+ // Generate a method -------------------------------------------------------
+
+ def genMethod(dd: DefDef): Option[js.MethodDef] = withNewLocalNameScope {
+ genMethodWithInfoBuilder(dd).map(_._1)
+ }
+
+ /** Gen JS code for a method definition in a class or in an impl class.
+ * On the JS side, method names are mangled to encode the full signature
+ * of the Scala method, as described in `JSEncoding`, to support
+ * overloading.
+ *
+ * Some methods are not emitted at all:
+ * * Primitives, since they are never actually called
+ * * Abstract methods
+ * * Constructors of hijacked classes
+ * * Trivial constructors, which only call their super constructor, with
+ * the same signature, and the same arguments. The JVM needs these
+ * constructors, but not JavaScript. Since there are lots of them, we
+ * take the trouble of recognizing and removing them.
+ *
+ * Constructors are emitted by generating their body as a statement, then
+ * return `this`.
+ *
+ * Other (normal) methods are emitted with `genMethodBody()`.
+ */
+ def genMethodWithInfoBuilder(
+ dd: DefDef): Option[(js.MethodDef, MethodInfoBuilder)] = {
+
+ implicit val pos = dd.pos
+ val DefDef(mods, name, _, vparamss, _, rhs) = dd
+ val sym = dd.symbol
+
+ isModuleInitialized = false
+
+ val result = withScopedVars(
+ currentMethodSym := sym,
+ methodTailJumpThisSym := NoSymbol,
+ fakeTailJumpParamRepl := (NoSymbol, NoSymbol),
+ enclosingLabelDefParams := Map.empty
+ ) {
+ assert(vparamss.isEmpty || vparamss.tail.isEmpty,
+ "Malformed parameter list: " + vparamss)
+ val params = if (vparamss.isEmpty) Nil else vparamss.head map (_.symbol)
+
+ assert(!sym.owner.isInterface,
+ "genMethod() must not be called for methods in interfaces: "+sym)
+
+ val methodIdent = encodeMethodSym(sym)
+
+ def createInfoBuilder(isAbstract: Boolean = false) = {
+ currentClassInfoBuilder.addMethod(methodIdent.name,
+ isAbstract = isAbstract,
+ isExported = sym.isClassConstructor &&
+ jsInterop.exportsOf(sym).nonEmpty)
+ }
+
+ if (scalaPrimitives.isPrimitive(sym)) {
+ None
+ } else if (sym.isDeferred) {
+ createInfoBuilder(isAbstract = true)
+ None
+ } else if (isRawJSCtorDefaultParam(sym)) {
+ None
+ } else if (isTrivialConstructor(sym, params, rhs)) {
+ createInfoBuilder().callsMethod(sym.owner.superClass, methodIdent)
+ None
+ } else if (sym.isClassConstructor && isHijackedBoxedClass(sym.owner)) {
+ None
+ } else {
+ withScopedVars(
+ currentMethodInfoBuilder := createInfoBuilder(),
+ mutableLocalVars := mutable.Set.empty,
+ mutatedLocalVars := mutable.Set.empty
+ ) {
+ def shouldMarkInline = {
+ sym.hasAnnotation(InlineAnnotationClass) ||
+ sym.name.startsWith(nme.ANON_FUN_NAME)
+ }
+ currentMethodInfoBuilder.optimizerHints =
+ currentMethodInfoBuilder.optimizerHints.copy(
+ isAccessor = sym.isAccessor,
+ hasInlineAnnot = shouldMarkInline)
+
+ val methodDef = {
+ if (sym.isClassConstructor) {
+ val jsParams = for (param <- params) yield {
+ implicit val pos = param.pos
+ js.ParamDef(encodeLocalSym(param), toIRType(param.tpe),
+ mutable = false)
+ }
+ js.MethodDef(methodIdent, jsParams, currentClassType,
+ js.Block(genStat(rhs), genThis()))(None)
+ } else {
+ val resultIRType = toIRType(sym.tpe.resultType)
+ genMethodDef(methodIdent, params, resultIRType, rhs)
+ }
+ }
+
+ val methodDefWithoutUselessVars = {
+ val unmutatedMutableLocalVars =
+ (mutableLocalVars -- mutatedLocalVars).toList
+ val mutatedImmutableLocalVals =
+ (mutatedLocalVars -- mutableLocalVars).toList
+ if (unmutatedMutableLocalVars.isEmpty &&
+ mutatedImmutableLocalVals.isEmpty) {
+ // OK, we're good (common case)
+ methodDef
+ } else {
+ val patches = (
+ unmutatedMutableLocalVars.map(encodeLocalSym(_).name -> false) :::
+ mutatedImmutableLocalVals.map(encodeLocalSym(_).name -> true)
+ ).toMap
+ patchMutableFlagOfLocals(methodDef, patches)
+ }
+ }
+
+ Some((methodDefWithoutUselessVars, currentMethodInfoBuilder.get))
+ }
+ }
+ }
+
+ result
+ }
+
+ private def isTrivialConstructor(sym: Symbol, params: List[Symbol],
+ rhs: Tree): Boolean = {
+ if (!sym.isClassConstructor) {
+ false
+ } else {
+ rhs match {
+ // Shape of a constructor that only calls super
+ case Block(List(Apply(fun @ Select(_:Super, _), args)), Literal(_)) =>
+ val callee = fun.symbol
+ implicit val dummyPos = NoPosition
+
+ // Does the callee have the same signature as sym
+ if (encodeMethodSym(sym) == encodeMethodSym(callee)) {
+ // Test whether args are trivial forwarders
+ assert(args.size == params.size, "Argument count mismatch")
+ params.zip(args) forall { case (param, arg) =>
+ arg.symbol == param
+ }
+ } else {
+ false
+ }
+
+ case _ => false
+ }
+ }
+ }
+
+ /** Patches the mutable flags of selected locals in a [[js.MethodDef]].
+ *
+ * @param patches Map from local name to new value of the mutable flags.
+ * For locals not in the map, the flag is untouched.
+ */
+ private def patchMutableFlagOfLocals(methodDef: js.MethodDef,
+ patches: Map[String, Boolean]): js.MethodDef = {
+
+ def newMutable(name: String, oldMutable: Boolean): Boolean =
+ patches.getOrElse(name, oldMutable)
+
+ val js.MethodDef(methodName, params, resultType, body) = methodDef
+ val newParams = for {
+ p @ js.ParamDef(name, ptpe, mutable) <- params
+ } yield {
+ js.ParamDef(name, ptpe, newMutable(name.name, mutable))(p.pos)
+ }
+ val transformer = new ir.Transformers.Transformer {
+ override def transform(tree: js.Tree, isStat: Boolean): js.Tree = tree match {
+ case js.VarDef(name, vtpe, mutable, rhs) =>
+ assert(isStat)
+ super.transform(js.VarDef(
+ name, vtpe, newMutable(name.name, mutable), rhs)(tree.pos), isStat)
+ case js.VarRef(name, mutable) =>
+ js.VarRef(name, newMutable(name.name, mutable))(tree.tpe)(tree.pos)
+ case js.Closure(captureParams, params, body, captureValues) =>
+ js.Closure(captureParams, params, body,
+ captureValues.map(transformExpr))(tree.pos)
+ case _ =>
+ super.transform(tree, isStat)
+ }
+ }
+ val newBody =
+ transformer.transform(body, isStat = resultType == jstpe.NoType)
+ js.MethodDef(methodName, newParams, resultType, newBody)(None)(methodDef.pos)
+ }
+
+ /**
+ * Generates reflective proxy methods for methods in sym
+ *
+ * Reflective calls don't depend on the return type, so it's hard to
+ * generate calls without using runtime reflection to list the methods. We
+ * generate a method to be used for reflective calls (without return
+ * type in the name).
+ *
+ * There are cases where non-trivial overloads cause ambiguous situations:
+ *
+ * {{{
+ * object A {
+ * def foo(x: Option[Int]): String
+ * def foo(x: Option[String]): Int
+ * }
+ * }}}
+ *
+ * This is completely legal code, but due to the same erased parameter
+ * type of the {{{foo}}} overloads, they cannot be disambiguated in a
+ * reflective call, as the exact return type is unknown at the call site.
+ *
+ * Cases like the upper currently fail on the JVM backend at runtime. The
+ * Scala.js backend uses the following rules for selection (which will
+ * also cause runtime failures):
+ *
+ * - If a proxy with the same signature (method name and parameters)
+ * exists in the superclass, no proxy is generated (proxy is inherited)
+ * - If no proxy exists in the superclass, a proxy is generated for the
+ * first method with matching signatures.
+ */
+ def genReflCallProxies(sym: Symbol): List[js.MethodDef] = {
+ import scala.reflect.internal.Flags
+
+ // Flags of members we do not want to consider for reflective call proxys
+ val excludedFlags = (
+ Flags.BRIDGE |
+ Flags.PRIVATE |
+ Flags.MACRO
+ )
+
+ /** Check if two method symbols conform in name and parameter types */
+ def weakMatch(s1: Symbol)(s2: Symbol) = {
+ val p1 = s1.tpe.params
+ val p2 = s2.tpe.params
+ s1 == s2 || // Shortcut
+ s1.name == s2.name &&
+ p1.size == p2.size &&
+ (p1 zip p2).forall { case (s1,s2) =>
+ s1.tpe =:= s2.tpe
+ }
+ }
+
+ /** Check if the symbol's owner's superclass has a matching member (and
+ * therefore an existing proxy).
+ */
+ def superHasProxy(s: Symbol) = {
+ val alts = sym.superClass.tpe.findMember(
+ name = s.name,
+ excludedFlags = excludedFlags,
+ requiredFlags = Flags.METHOD,
+ stableOnly = false).alternatives
+ alts.exists(weakMatch(s) _)
+ }
+
+ // Query candidate methods
+ val methods = sym.tpe.findMembers(
+ excludedFlags = excludedFlags,
+ requiredFlags = Flags.METHOD)
+
+ val candidates = methods filterNot { s =>
+ s.isConstructor ||
+ superHasProxy(s) ||
+ jsInterop.isExport(s)
+ }
+
+ val proxies = candidates filter {
+ c => candidates.find(weakMatch(c) _).get == c
+ }
+
+ proxies.map(genReflCallProxy _).toList
+ }
+
+ /** actually generates reflective call proxy for the given method symbol */
+ private def genReflCallProxy(sym: Symbol): js.MethodDef = {
+ implicit val pos = sym.pos
+
+ val proxyIdent = encodeMethodSym(sym, reflProxy = true)
+
+ withNewLocalNameScope {
+ withScopedVars(
+ currentMethodInfoBuilder :=
+ currentClassInfoBuilder.addMethod(proxyIdent.name)
+ ) {
+ val jsParams = for (param <- sym.tpe.params) yield {
+ implicit val pos = param.pos
+ js.ParamDef(encodeLocalSym(param), toIRType(param.tpe),
+ mutable = false)
+ }
+
+ val call = genApplyMethod(genThis(), sym.owner, sym,
+ jsParams.map(_.ref))
+ val body = ensureBoxed(call,
+ enteringPhase(currentRun.posterasurePhase)(sym.tpe.resultType))
+
+ js.MethodDef(proxyIdent, jsParams, jstpe.AnyType, body)(None)
+ }
+ }
+ }
+
+ /** Generates the MethodDef of a (non-constructor) method
+ *
+ * Most normal methods are emitted straightforwardly. If the result
+ * type is Unit, then the body is emitted as a statement. Otherwise, it is
+ * emitted as an expression.
+ *
+ * The additional complexity of this method handles the transformation of
+ * a peculiarity of recursive tail calls: the local ValDef that replaces
+ * `this`.
+ */
+ def genMethodDef(methodIdent: js.Ident, paramsSyms: List[Symbol],
+ resultIRType: jstpe.Type, tree: Tree): js.MethodDef = {
+ implicit val pos = tree.pos
+
+ val jsParams = for (param <- paramsSyms) yield {
+ implicit val pos = param.pos
+ js.ParamDef(encodeLocalSym(param), toIRType(param.tpe), mutable = false)
+ }
+
+ val bodyIsStat = resultIRType == jstpe.NoType
+
+ val body = tree match {
+ case Block(
+ (thisDef @ ValDef(_, nme.THIS, _, initialThis)) :: otherStats,
+ rhs) =>
+ // This method has tail jumps
+ withScopedVars(
+ (initialThis match {
+ case This(_) =>
+ Seq(methodTailJumpThisSym := thisDef.symbol,
+ fakeTailJumpParamRepl := (NoSymbol, NoSymbol))
+ case Ident(_) =>
+ Seq(methodTailJumpThisSym := NoSymbol,
+ fakeTailJumpParamRepl := (thisDef.symbol, initialThis.symbol))
+ }): _*
+ ) {
+ val innerBody = js.Block(otherStats.map(genStat) :+ (
+ if (bodyIsStat) genStat(rhs)
+ else genExpr(rhs)))
+
+ if (methodTailJumpThisSym.get == NoSymbol) {
+ innerBody
+ } else {
+ if (methodTailJumpThisSym.isMutable)
+ mutableLocalVars += methodTailJumpThisSym
+ js.Block(
+ js.VarDef(encodeLocalSym(methodTailJumpThisSym),
+ currentClassType, methodTailJumpThisSym.isMutable,
+ js.This()(currentClassType)),
+ innerBody)
+ }
+ }
+
+ case _ =>
+ if (bodyIsStat) genStat(tree)
+ else genExpr(tree)
+ }
+
+ js.MethodDef(methodIdent, jsParams, resultIRType, body)(None)
+ }
+
+ /** Gen JS code for a tree in statement position (in the IR).
+ */
+ def genStat(tree: Tree): js.Tree = {
+ exprToStat(genStatOrExpr(tree, isStat = true))
+ }
+
+ /** Turn a JavaScript expression of type Unit into a statement */
+ def exprToStat(tree: js.Tree): js.Tree = {
+ /* Any JavaScript expression is also a statement, but at least we get rid
+ * of some pure expressions that come from our own codegen.
+ */
+ implicit val pos = tree.pos
+ tree match {
+ case js.Block(stats :+ expr) => js.Block(stats :+ exprToStat(expr))
+ case _:js.Literal | js.This() => js.Skip()
+ case _ => tree
+ }
+ }
+
+ /** Gen JS code for a tree in expression position (in the IR).
+ */
+ def genExpr(tree: Tree): js.Tree = {
+ val result = genStatOrExpr(tree, isStat = false)
+ assert(result.tpe != jstpe.NoType,
+ s"genExpr($tree) returned a tree with type NoType at pos ${tree.pos}")
+ result
+ }
+
+ /** Gen JS code for a tree in statement or expression position (in the IR).
+ *
+ * This is the main transformation method. Each node of the Scala AST
+ * is transformed into an equivalent portion of the JS AST.
+ */
+ def genStatOrExpr(tree: Tree, isStat: Boolean): js.Tree = {
+ implicit val pos = tree.pos
+
+ tree match {
+ /** LabelDefs (for while and do..while loops) */
+ case lblDf: LabelDef =>
+ genLabelDef(lblDf)
+
+ /** Local val or var declaration */
+ case ValDef(_, name, _, rhs) =>
+ /* Must have been eliminated by the tail call transform performed
+ * by genMethodBody(). */
+ assert(name != nme.THIS,
+ s"ValDef(_, nme.THIS, _, _) found at ${tree.pos}")
+
+ val sym = tree.symbol
+ val rhsTree =
+ if (rhs == EmptyTree) genZeroOf(sym.tpe)
+ else genExpr(rhs)
+
+ rhsTree match {
+ case js.UndefinedParam() =>
+ // This is an intermediate assignment for default params on a
+ // js.Any. Add the symbol to the corresponding set to inform
+ // the Ident resolver how to replace it and don't emit the symbol
+ undefinedDefaultParams += sym
+ js.Skip()
+ case _ =>
+ if (sym.isMutable)
+ mutableLocalVars += sym
+ js.VarDef(encodeLocalSym(sym),
+ toIRType(sym.tpe), sym.isMutable, rhsTree)
+ }
+
+ case If(cond, thenp, elsep) =>
+ js.If(genExpr(cond), genStatOrExpr(thenp, isStat),
+ genStatOrExpr(elsep, isStat))(toIRType(tree.tpe))
+
+ case Return(expr) =>
+ js.Return(toIRType(expr.tpe) match {
+ case jstpe.NoType => js.Block(genStat(expr), js.Undefined())
+ case _ => genExpr(expr)
+ })
+
+ case t: Try =>
+ genTry(t, isStat)
+
+ case Throw(expr) =>
+ val ex = genExpr(expr)
+ js.Throw {
+ if (isMaybeJavaScriptException(expr.tpe)) {
+ genApplyMethod(
+ genLoadModule(RuntimePackageModule),
+ RuntimePackageModule.moduleClass,
+ Runtime_unwrapJavaScriptException,
+ List(ex))
+ } else {
+ ex
+ }
+ }
+
+ case app: Apply =>
+ genApply(app, isStat)
+
+ case app: ApplyDynamic =>
+ genApplyDynamic(app)
+
+ case This(qual) =>
+ if (tree.symbol == currentClassSym.get) {
+ genThis()
+ } else {
+ assert(tree.symbol.isModuleClass,
+ "Trying to access the this of another class: " +
+ "tree.symbol = " + tree.symbol +
+ ", class symbol = " + currentClassSym.get +
+ " compilation unit:" + currentUnit)
+ genLoadModule(tree.symbol)
+ }
+
+ case Select(qualifier, selector) =>
+ val sym = tree.symbol
+ if (sym.isModule) {
+ assert(!sym.isPackageClass, "Cannot use package as value: " + tree)
+ genLoadModule(sym)
+ } else if (sym.isStaticMember) {
+ genStaticMember(sym)
+ } else if (paramAccessorLocals contains sym) {
+ paramAccessorLocals(sym).ref
+ } else {
+ js.Select(genExpr(qualifier), encodeFieldSym(sym),
+ mutable = sym.isMutable)(toIRType(sym.tpe))
+ }
+
+ case Ident(name) =>
+ val sym = tree.symbol
+ if (!sym.hasPackageFlag) {
+ if (sym.isModule) {
+ assert(!sym.isPackageClass, "Cannot use package as value: " + tree)
+ genLoadModule(sym)
+ } else if (undefinedDefaultParams contains sym) {
+ // This is a default parameter whose assignment was moved to
+ // a local variable. Put a literal undefined param again
+ js.UndefinedParam()(toIRType(sym.tpe))
+ } else {
+ js.VarRef(encodeLocalSym(sym), sym.isMutable)(toIRType(sym.tpe))
+ }
+ } else {
+ sys.error("Cannot use package as value: " + tree)
+ }
+
+ case Literal(value) =>
+ value.tag match {
+ case UnitTag =>
+ js.Skip()
+ case BooleanTag =>
+ js.BooleanLiteral(value.booleanValue)
+ case ByteTag | ShortTag | CharTag | IntTag =>
+ js.IntLiteral(value.intValue)
+ case LongTag =>
+ js.LongLiteral(value.longValue)
+ case FloatTag =>
+ js.FloatLiteral(value.floatValue)
+ case DoubleTag =>
+ js.DoubleLiteral(value.doubleValue)
+ case StringTag =>
+ js.StringLiteral(value.stringValue)
+ case NullTag =>
+ js.Null()
+ case ClazzTag =>
+ genClassConstant(value.typeValue)
+ case EnumTag =>
+ genStaticMember(value.symbolValue)
+ }
+
+ case tree: Block =>
+ genBlock(tree, isStat)
+
+ case Typed(Super(_, _), _) =>
+ genThis()
+
+ case Typed(expr, _) =>
+ genExpr(expr)
+
+ case Assign(lhs, rhs) =>
+ val sym = lhs.symbol
+ if (sym.isStaticMember)
+ abort(s"Assignment to static member ${sym.fullName} not supported")
+ val genLhs = lhs match {
+ case Select(qualifier, _) =>
+ js.Select(genExpr(qualifier), encodeFieldSym(sym),
+ mutable = sym.isMutable)(toIRType(sym.tpe))
+ case _ =>
+ mutatedLocalVars += sym
+ js.VarRef(encodeLocalSym(sym), sym.isMutable)(toIRType(sym.tpe))
+ }
+ js.Assign(genLhs, genExpr(rhs))
+
+ /** Array constructor */
+ case av: ArrayValue =>
+ genArrayValue(av)
+
+ /** A Match reaching the backend is supposed to be optimized as a switch */
+ case mtch: Match =>
+ genMatch(mtch, isStat)
+
+ /** Anonymous function (only with -Ydelambdafy:method) */
+ case fun: Function =>
+ genAnonFunction(fun)
+
+ case EmptyTree =>
+ js.Skip()
+
+ case _ =>
+ abort("Unexpected tree in genExpr: " +
+ tree + "/" + tree.getClass + " at: " + tree.pos)
+ }
+ } // end of GenJSCode.genExpr()
+
+ /** Gen JS this of the current class.
+ * Normally encoded straightforwardly as a JS this.
+ * But must be replaced by the tail-jump-this local variable if there
+ * is one.
+ */
+ private def genThis()(implicit pos: Position): js.Tree = {
+ if (methodTailJumpThisSym.get != NoSymbol) {
+ js.VarRef(
+ encodeLocalSym(methodTailJumpThisSym),
+ methodTailJumpThisSym.isMutable)(currentClassType)
+ } else {
+ if (tryingToGenMethodAsJSFunction)
+ throw new CancelGenMethodAsJSFunction(
+ "Trying to generate `this` inside the body")
+ js.This()(currentClassType)
+ }
+ }
+
+ /** Gen JS code for LabelDef
+ * The only LabelDefs that can reach here are the desugaring of
+ * while and do..while loops. All other LabelDefs (for tail calls or
+ * matches) are caught upstream and transformed in ad hoc ways.
+ *
+ * So here we recognize all the possible forms of trees that can result
+ * of while or do..while loops, and we reconstruct the loop for emission
+ * to JS.
+ */
+ def genLabelDef(tree: LabelDef): js.Tree = {
+ implicit val pos = tree.pos
+ val sym = tree.symbol
+
+ tree match {
+ // while (cond) { body }
+ case LabelDef(lname, Nil,
+ If(cond,
+ Block(bodyStats, Apply(target @ Ident(lname2), Nil)),
+ Literal(_))) if (target.symbol == sym) =>
+ js.While(genExpr(cond), js.Block(bodyStats map genStat))
+
+ // while (cond) { body }; result
+ case LabelDef(lname, Nil,
+ Block(List(
+ If(cond,
+ Block(bodyStats, Apply(target @ Ident(lname2), Nil)),
+ Literal(_))),
+ result)) if (target.symbol == sym) =>
+ js.Block(
+ js.While(genExpr(cond), js.Block(bodyStats map genStat)),
+ genExpr(result))
+
+ // while (true) { body }
+ case LabelDef(lname, Nil,
+ Block(bodyStats,
+ Apply(target @ Ident(lname2), Nil))) if (target.symbol == sym) =>
+ js.While(js.BooleanLiteral(true), js.Block(bodyStats map genStat))
+
+ // while (false) { body }
+ case LabelDef(lname, Nil, Literal(Constant(()))) =>
+ js.Skip()
+
+ // do { body } while (cond)
+ case LabelDef(lname, Nil,
+ Block(bodyStats,
+ If(cond,
+ Apply(target @ Ident(lname2), Nil),
+ Literal(_)))) if (target.symbol == sym) =>
+ js.DoWhile(js.Block(bodyStats map genStat), genExpr(cond))
+
+ // do { body } while (cond); result
+ case LabelDef(lname, Nil,
+ Block(
+ bodyStats :+
+ If(cond,
+ Apply(target @ Ident(lname2), Nil),
+ Literal(_)),
+ result)) if (target.symbol == sym) =>
+ js.Block(
+ js.DoWhile(js.Block(bodyStats map genStat), genExpr(cond)),
+ genExpr(result))
+
+ /* Arbitrary other label - we can jump to it from inside it.
+ * This is typically for the label-defs implementing tail-calls.
+ * It can also handle other weird LabelDefs generated by some compiler
+ * plugins (see for example #1148).
+ */
+ case LabelDef(labelName, labelParams, rhs) =>
+ val labelParamSyms = labelParams.map(_.symbol) map {
+ s => if (s == fakeTailJumpParamRepl._1) fakeTailJumpParamRepl._2 else s
+ }
+
+ withScopedVars(
+ enclosingLabelDefParams :=
+ enclosingLabelDefParams.get + (tree.symbol -> labelParamSyms)
+ ) {
+ val bodyType = toIRType(tree.tpe)
+ val labelIdent = encodeLabelSym(tree.symbol)
+ val blockLabelIdent = freshLocalIdent()
+
+ js.Labeled(blockLabelIdent, bodyType, {
+ js.While(js.BooleanLiteral(true), {
+ if (bodyType == jstpe.NoType)
+ js.Block(genStat(rhs), js.Return(js.Undefined(), Some(blockLabelIdent)))
+ else
+ js.Return(genExpr(rhs), Some(blockLabelIdent))
+ }, Some(labelIdent))
+ })
+ }
+ }
+ }
+
+ /** Gen JS code for a try..catch or try..finally block
+ *
+ * try..finally blocks are compiled straightforwardly to try..finally
+ * blocks of JS.
+ *
+ * try..catch blocks are a bit more subtle, as JS does not have
+ * type-based selection of exceptions to catch. We thus encode explicitly
+ * the type tests, like in:
+ *
+ * try { ... }
+ * catch (e) {
+ * if (e.isInstanceOf[IOException]) { ... }
+ * else if (e.isInstanceOf[Exception]) { ... }
+ * else {
+ * throw e; // default, re-throw
+ * }
+ * }
+ */
+ def genTry(tree: Try, isStat: Boolean): js.Tree = {
+ implicit val pos = tree.pos
+ val Try(block, catches, finalizer) = tree
+
+ val blockAST = genStatOrExpr(block, isStat)
+
+ val exceptIdent = freshLocalIdent("e")
+ val origExceptVar = js.VarRef(exceptIdent, mutable = false)(jstpe.AnyType)
+
+ val resultType = toIRType(tree.tpe)
+
+ val handlerAST = {
+ if (catches.isEmpty) {
+ js.EmptyTree
+ } else {
+ val mightCatchJavaScriptException = catches.exists { caseDef =>
+ caseDef.pat match {
+ case Typed(Ident(nme.WILDCARD), tpt) =>
+ isMaybeJavaScriptException(tpt.tpe)
+ case Ident(nme.WILDCARD) =>
+ true
+ case pat @ Bind(_, _) =>
+ isMaybeJavaScriptException(pat.symbol.tpe)
+ }
+ }
+
+ val (exceptValDef, exceptVar) = if (mightCatchJavaScriptException) {
+ val valDef = js.VarDef(freshLocalIdent("e"),
+ encodeClassType(ThrowableClass), mutable = false, {
+ genApplyMethod(
+ genLoadModule(RuntimePackageModule),
+ RuntimePackageModule.moduleClass,
+ Runtime_wrapJavaScriptException,
+ List(origExceptVar))
+ })
+ (valDef, valDef.ref)
+ } else {
+ (js.Skip(), origExceptVar)
+ }
+
+ val elseHandler: js.Tree = js.Throw(origExceptVar)
+
+ val handler0 = catches.foldRight(elseHandler) { (caseDef, elsep) =>
+ implicit val pos = caseDef.pos
+ val CaseDef(pat, _, body) = caseDef
+
+ // Extract exception type and variable
+ val (tpe, boundVar) = (pat match {
+ case Typed(Ident(nme.WILDCARD), tpt) =>
+ (tpt.tpe, None)
+ case Ident(nme.WILDCARD) =>
+ (ThrowableClass.tpe, None)
+ case Bind(_, _) =>
+ (pat.symbol.tpe, Some(encodeLocalSym(pat.symbol)))
+ })
+
+ // Generate the body that must be executed if the exception matches
+ val bodyWithBoundVar = (boundVar match {
+ case None =>
+ genStatOrExpr(body, isStat)
+ case Some(bv) =>
+ val castException = genAsInstanceOf(exceptVar, tpe)
+ js.Block(
+ js.VarDef(bv, toIRType(tpe), mutable = false, castException),
+ genStatOrExpr(body, isStat))
+ })
+
+ // Generate the test
+ if (tpe == ThrowableClass.tpe) {
+ bodyWithBoundVar
+ } else {
+ val cond = genIsInstanceOf(exceptVar, tpe)
+ js.If(cond, bodyWithBoundVar, elsep)(resultType)
+ }
+ }
+
+ js.Block(
+ exceptValDef,
+ handler0)
+ }
+ }
+
+ val finalizerAST = genStat(finalizer) match {
+ case js.Skip() => js.EmptyTree
+ case ast => ast
+ }
+
+ if (handlerAST == js.EmptyTree && finalizerAST == js.EmptyTree) blockAST
+ else js.Try(blockAST, exceptIdent, handlerAST, finalizerAST)(resultType)
+ }
+
+ /** Gen JS code for an Apply node (method call)
+ *
+ * There's a whole bunch of varieties of Apply nodes: regular method
+ * calls, super calls, constructor calls, isInstanceOf/asInstanceOf,
+ * primitives, JS calls, etc. They are further dispatched in here.
+ */
+ def genApply(tree: Apply, isStat: Boolean): js.Tree = {
+ implicit val pos = tree.pos
+ val Apply(fun, args) = tree
+
+ fun match {
+ case TypeApply(_, _) =>
+ genApplyTypeApply(tree)
+
+ case Select(Super(_, _), _) =>
+ genSuperCall(tree)
+
+ case Select(New(_), nme.CONSTRUCTOR) =>
+ genApplyNew(tree)
+
+ case _ =>
+ val sym = fun.symbol
+
+ if (sym.isLabel) {
+ genLabelApply(tree)
+ } else if (scalaPrimitives.isPrimitive(sym)) {
+ genPrimitiveOp(tree, isStat)
+ } else if (currentRun.runDefinitions.isBox(sym)) {
+ // Box a primitive value (cannot be Unit)
+ val arg = args.head
+ makePrimitiveBox(genExpr(arg), arg.tpe)
+ } else if (currentRun.runDefinitions.isUnbox(sym)) {
+ // Unbox a primitive value (cannot be Unit)
+ val arg = args.head
+ makePrimitiveUnbox(genExpr(arg), tree.tpe)
+ } else {
+ genNormalApply(tree, isStat)
+ }
+ }
+ }
+
+ /** Gen an Apply with a TypeApply method.
+ * Only isInstanceOf and asInstanceOf keep their type argument until the
+ * backend.
+ */
+ private def genApplyTypeApply(tree: Apply): js.Tree = {
+ implicit val pos = tree.pos
+ val Apply(TypeApply(fun @ Select(obj, _), targs), _) = tree
+ val sym = fun.symbol
+
+ val cast = sym match {
+ case Object_isInstanceOf => false
+ case Object_asInstanceOf => true
+ case _ =>
+ abort("Unexpected type application " + fun +
+ "[sym: " + sym.fullName + "]" + " in: " + tree)
+ }
+
+ val to = targs.head.tpe
+ val l = toTypeKind(obj.tpe)
+ val r = toTypeKind(to)
+ val source = genExpr(obj)
+
+ if (l.isValueType && r.isValueType) {
+ if (cast)
+ genConversion(l, r, source)
+ else
+ js.BooleanLiteral(l == r)
+ } else if (l.isValueType) {
+ val result = if (cast) {
+ val ctor = ClassCastExceptionClass.info.member(
+ nme.CONSTRUCTOR).suchThat(_.tpe.params.isEmpty)
+ js.Throw(genNew(ClassCastExceptionClass, ctor, Nil))
+ } else {
+ js.BooleanLiteral(false)
+ }
+ js.Block(source, result) // eval and discard source
+ } else if (r.isValueType) {
+ assert(!cast, s"Unexpected asInstanceOf from ref type to value type")
+ genIsInstanceOf(source, boxedClass(to.typeSymbol).tpe)
+ } else {
+ if (cast)
+ genAsInstanceOf(source, to)
+ else
+ genIsInstanceOf(source, to)
+ }
+ }
+
+ /** Gen JS code for a super call, of the form Class.super[mix].fun(args).
+ *
+ * This does not include calls defined in mixin traits, as these are
+ * already desugared by the 'mixin' phase. Only calls to super classes
+ * remain.
+ * Since a class has exactly one direct superclass, and calling a method
+ * two classes above the current one is invalid, the `mix` item is
+ * irrelevant.
+ */
+ private def genSuperCall(tree: Apply): js.Tree = {
+ implicit val pos = tree.pos
+ val Apply(fun @ Select(sup @ Super(_, mix), _), args) = tree
+ val sym = fun.symbol
+
+ if (sym == Object_getClass) {
+ // The only primitive that is also callable as super call
+ js.GetClass(genThis())
+ } else {
+ val superCall = genStaticApplyMethod(
+ genThis()(sup.pos), sym, genActualArgs(sym, args))
+
+ // Initialize the module instance just after the super constructor call.
+ if (isStaticModule(currentClassSym) && !isModuleInitialized &&
+ currentMethodSym.isClassConstructor) {
+ isModuleInitialized = true
+ val thisType = jstpe.ClassType(encodeClassFullName(currentClassSym))
+ val initModule = js.StoreModule(thisType, js.This()(thisType))
+ js.Block(superCall, initModule, js.This()(thisType))
+ } else {
+ superCall
+ }
+ }
+ }
+
+ /** Gen JS code for a constructor call (new).
+ * Further refined into:
+ * * new String(...)
+ * * new of a hijacked boxed class
+ * * new of an anonymous function class that was recorded as JS function
+ * * new of a raw JS class
+ * * new Array
+ * * regular new
+ */
+ private def genApplyNew(tree: Apply): js.Tree = {
+ implicit val pos = tree.pos
+ val Apply(fun @ Select(New(tpt), nme.CONSTRUCTOR), args) = tree
+ val ctor = fun.symbol
+ val tpe = tpt.tpe
+
+ assert(ctor.isClassConstructor,
+ "'new' call to non-constructor: " + ctor.name)
+
+ if (isStringType(tpe)) {
+ genNewString(tree)
+ } else if (isHijackedBoxedClass(tpe.typeSymbol)) {
+ genNewHijackedBoxedClass(tpe.typeSymbol, ctor, args map genExpr)
+ } else if (translatedAnonFunctions contains tpe.typeSymbol) {
+ val (functionMaker, funInfo) = translatedAnonFunctions(tpe.typeSymbol)
+ currentMethodInfoBuilder.createsAnonFunction(funInfo)
+ functionMaker(args map genExpr)
+ } else if (isRawJSType(tpe)) {
+ genPrimitiveJSNew(tree)
+ } else {
+ toTypeKind(tpe) match {
+ case arr @ ARRAY(elem) =>
+ genNewArray(arr.toIRType, args map genExpr)
+ case rt @ REFERENCE(cls) =>
+ genNew(cls, ctor, genActualArgs(ctor, args))
+ case generatedType =>
+ abort(s"Non reference type cannot be instantiated: $generatedType")
+ }
+ }
+ }
+
+ /** Gen jump to a label.
+ * Most label-applys are caught upstream (while and do..while loops,
+ * jumps to next case of a pattern match), but some are still handled here:
+ * * Jumps to enclosing label-defs, including tail-recursive calls
+ * * Jump to the end of a pattern match
+ */
+ private def genLabelApply(tree: Apply): js.Tree = {
+ implicit val pos = tree.pos
+ val Apply(fun, args) = tree
+ val sym = fun.symbol
+
+ if (enclosingLabelDefParams.contains(sym)) {
+ genEnclosingLabelApply(tree)
+ } else if (sym.name.toString() startsWith "matchEnd") {
+ /* Jump the to the end-label of a pattern match
+ * Such labels have exactly one argument, which is the result of
+ * the pattern match (of type BoxedUnit if the match is in statement
+ * position). We simply `return` the argument as the result of the
+ * labeled block surrounding the match.
+ */
+ js.Return(genExpr(args.head), Some(encodeLabelSym(sym)))
+ } else {
+ /* No other label apply should ever happen. If it does, then we
+ * have missed a pattern of LabelDef/LabelApply and some new
+ * translation must be found for it.
+ */
+ abort("Found unknown label apply at "+tree.pos+": "+tree)
+ }
+ }
+
+ /** Gen a label-apply to an enclosing label def.
+ *
+ * This is typically used for tail-recursive calls.
+ *
+ * Basically this is compiled into
+ * continue labelDefIdent;
+ * but arguments need to be updated beforehand.
+ *
+ * Since the rhs for the new value of an argument can depend on the value
+ * of another argument (and since deciding if it is indeed the case is
+ * impossible in general), new values are computed in temporary variables
+ * first, then copied to the actual variables representing the argument.
+ *
+ * Trivial assignments (arg1 = arg1) are eliminated.
+ *
+ * If, after elimination of trivial assignments, only one assignment
+ * remains, then we do not use a temporary variable for this one.
+ */
+ private def genEnclosingLabelApply(tree: Apply): js.Tree = {
+ implicit val pos = tree.pos
+ val Apply(fun, args) = tree
+ val sym = fun.symbol
+
+ // Prepare quadruplets of (formalArg, irType, tempVar, actualArg)
+ // Do not include trivial assignments (when actualArg == formalArg)
+ val formalArgs = enclosingLabelDefParams(sym)
+ val actualArgs = args map genExpr
+ val quadruplets = {
+ for {
+ (formalArgSym, actualArg) <- formalArgs zip actualArgs
+ formalArg = encodeLocalSym(formalArgSym)
+ if (actualArg match {
+ case js.VarRef(`formalArg`, _) => false
+ case _ => true
+ })
+ } yield {
+ mutatedLocalVars += formalArgSym
+ val tpe = toIRType(formalArgSym.tpe)
+ (js.VarRef(formalArg, formalArgSym.isMutable)(tpe), tpe,
+ freshLocalIdent("temp$" + formalArg.name),
+ actualArg)
+ }
+ }
+
+ // The actual jump (continue labelDefIdent;)
+ val jump = js.Continue(Some(encodeLabelSym(sym)))
+
+ quadruplets match {
+ case Nil => jump
+
+ case (formalArg, argType, _, actualArg) :: Nil =>
+ js.Block(
+ js.Assign(formalArg, actualArg),
+ jump)
+
+ case _ =>
+ val tempAssignments =
+ for ((_, argType, tempArg, actualArg) <- quadruplets)
+ yield js.VarDef(tempArg, argType, mutable = false, actualArg)
+ val trueAssignments =
+ for ((formalArg, argType, tempArg, _) <- quadruplets)
+ yield js.Assign(
+ formalArg,
+ js.VarRef(tempArg, mutable = false)(argType))
+ js.Block(tempAssignments ++ trueAssignments :+ jump)
+ }
+ }
+
+ /** Gen a "normal" apply (to a true method).
+ *
+ * But even these are further refined into:
+ * * Methods of java.lang.String, which are redirected to the
+ * RuntimeString trait implementation.
+ * * Calls to methods of raw JS types (Scala.js -> JS bridge)
+ * * Calls to methods in impl classes of traits.
+ * * Regular method call
+ */
+ private def genNormalApply(tree: Apply, isStat: Boolean): js.Tree = {
+ implicit val pos = tree.pos
+ val Apply(fun @ Select(receiver, _), args) = tree
+ val sym = fun.symbol
+
+ def isStringMethodFromObject: Boolean = sym.name match {
+ case nme.toString_ | nme.equals_ | nme.hashCode_ => true
+ case _ => false
+ }
+
+ if (sym.owner == StringClass && !isStringMethodFromObject) {
+ genStringCall(tree)
+ } else if (isRawJSType(receiver.tpe) && sym.owner != ObjectClass) {
+ genPrimitiveJSCall(tree, isStat)
+ } else if (foreignIsImplClass(sym.owner)) {
+ genTraitImplApply(sym, args map genExpr)
+ } else if (isRawJSCtorDefaultParam(sym)) {
+ js.UndefinedParam()(toIRType(sym.tpe.resultType))
+ } else if (sym.isClassConstructor) {
+ /* See #66: we have to emit a static call to avoid calling a
+ * constructor with the same signature in a subclass */
+ genStaticApplyMethod(genExpr(receiver), sym, genActualArgs(sym, args))
+ } else {
+ genApplyMethod(genExpr(receiver), receiver.tpe, sym, genActualArgs(sym, args))
+ }
+ }
+
+ def genStaticApplyMethod(receiver: js.Tree, method: Symbol,
+ arguments: List[js.Tree])(implicit pos: Position): js.Tree = {
+ val classIdent = encodeClassFullNameIdent(method.owner)
+ val methodIdent = encodeMethodSym(method)
+ currentMethodInfoBuilder.callsMethodStatic(classIdent, methodIdent)
+ js.StaticApply(receiver, jstpe.ClassType(classIdent.name), methodIdent,
+ arguments)(toIRType(method.tpe.resultType))
+ }
+
+ def genTraitImplApply(method: Symbol, arguments: List[js.Tree])(
+ implicit pos: Position): js.Tree = {
+ val implIdent = encodeClassFullNameIdent(method.owner)
+ val methodIdent = encodeMethodSym(method)
+ genTraitImplApply(implIdent, methodIdent, arguments,
+ toIRType(method.tpe.resultType))
+ }
+
+ def genTraitImplApply(implIdent: js.Ident, methodIdent: js.Ident,
+ arguments: List[js.Tree], resultType: jstpe.Type)(
+ implicit pos: Position): js.Tree = {
+ currentMethodInfoBuilder.callsMethod(implIdent, methodIdent)
+ js.TraitImplApply(jstpe.ClassType(implIdent.name), methodIdent,
+ arguments)(resultType)
+ }
+
+ /** Gen JS code for a conversion between primitive value types */
+ def genConversion(from: TypeKind, to: TypeKind, value: js.Tree)(
+ implicit pos: Position): js.Tree = {
+ def int0 = js.IntLiteral(0)
+ def int1 = js.IntLiteral(1)
+ def long0 = js.LongLiteral(0L)
+ def long1 = js.LongLiteral(1L)
+ def float0 = js.FloatLiteral(0.0f)
+ def float1 = js.FloatLiteral(1.0f)
+
+ (from, to) match {
+ case (INT(_), BOOL) => js.BinaryOp(js.BinaryOp.Num_!=, value, int0)
+ case (LONG, BOOL) => js.BinaryOp(js.BinaryOp.Long_!=, value, long0)
+ case (FLOAT(_), BOOL) => js.BinaryOp(js.BinaryOp.Num_!=, value, float0)
+
+ case (BOOL, INT(_)) => js.If(value, int1, int0 )(jstpe.IntType)
+ case (BOOL, LONG) => js.If(value, long1, long0 )(jstpe.LongType)
+ case (BOOL, FLOAT(_)) => js.If(value, float1, float0)(jstpe.FloatType)
+
+ case _ => value
+ }
+ }
+
+ /** Gen JS code for an isInstanceOf test (for reference types only) */
+ def genIsInstanceOf(value: js.Tree, to: Type)(
+ implicit pos: Position): js.Tree = {
+
+ def genTypeOfTest(typeString: String) = {
+ js.BinaryOp(js.BinaryOp.===,
+ js.UnaryOp(js.UnaryOp.typeof, value),
+ js.StringLiteral(typeString))
+ }
+
+ if (isRawJSType(to)) {
+ to.typeSymbol match {
+ case JSNumberClass => genTypeOfTest("number")
+ case JSStringClass => genTypeOfTest("string")
+ case JSBooleanClass => genTypeOfTest("boolean")
+ case JSUndefinedClass => genTypeOfTest("undefined")
+ case sym if sym.isTrait =>
+ reporter.error(pos,
+ s"isInstanceOf[${sym.fullName}] not supported because it is a raw JS trait")
+ js.BooleanLiteral(true)
+ case sym =>
+ js.BinaryOp(js.BinaryOp.instanceof, value, genGlobalJSObject(sym))
+ }
+ } else {
+ val refType = toReferenceType(to)
+ currentMethodInfoBuilder.accessesClassData(refType)
+ js.IsInstanceOf(value, refType)
+ }
+ }
+
+ /** Gen JS code for an asInstanceOf cast (for reference types only) */
+ def genAsInstanceOf(value: js.Tree, to: Type)(
+ implicit pos: Position): js.Tree = {
+
+ def default: js.Tree = {
+ val refType = toReferenceType(to)
+ currentMethodInfoBuilder.accessesClassData(refType)
+ js.AsInstanceOf(value, refType)
+ }
+
+ if (isRawJSType(to)) {
+ // asInstanceOf on JavaScript is completely erased
+ value
+ } else if (FunctionClass.seq contains to.typeSymbol) {
+ /* Don't hide a JSFunctionToScala inside a useless cast, otherwise
+ * the optimization avoiding double-wrapping in genApply() will not
+ * be able to kick in.
+ */
+ value match {
+ case JSFunctionToScala(fun, _) => value
+ case _ => default
+ }
+ } else {
+ default
+ }
+ }
+
+ /** Gen JS code for a call to a Scala method.
+ * This also registers that the given method is called by the current
+ * method in the method info builder.
+ */
+ def genApplyMethod(receiver: js.Tree, receiverType: Type,
+ methodSym: Symbol, arguments: List[js.Tree])(
+ implicit pos: Position): js.Tree = {
+ genApplyMethod(receiver, receiverType.typeSymbol, methodSym, arguments)
+ }
+
+ /** Gen JS code for a call to a Scala method.
+ * This also registers that the given method is called by the current
+ * method in the method info builder.
+ */
+ def genApplyMethod(receiver: js.Tree, receiverTypeSym: Symbol,
+ methodSym: Symbol, arguments: List[js.Tree])(
+ implicit pos: Position): js.Tree = {
+ genApplyMethod(receiver, receiverTypeSym,
+ encodeMethodSym(methodSym), arguments,
+ toIRType(methodSym.tpe.resultType))
+ }
+
+ /** Gen JS code for a call to a Scala method.
+ * This also registers that the given method is called by the current
+ * method in the method info builder.
+ */
+ def genApplyMethod(receiver: js.Tree, receiverType: Type,
+ methodIdent: js.Ident, arguments: List[js.Tree], resultType: jstpe.Type)(
+ implicit pos: Position): js.Tree = {
+ genApplyMethod(receiver, receiverType.typeSymbol, methodIdent,
+ arguments, resultType)
+ }
+
+ /** Gen JS code for a call to a Scala method.
+ * This also registers that the given method is called by the current
+ * method in the method info builder.
+ */
+ def genApplyMethod(receiver: js.Tree, receiverTypeSym: Symbol,
+ methodIdent: js.Ident, arguments: List[js.Tree], resultType: jstpe.Type)(
+ implicit pos: Position): js.Tree = {
+ currentMethodInfoBuilder.callsMethod(receiverTypeSym, methodIdent)
+ js.Apply(receiver, methodIdent, arguments)(resultType)
+ }
+
+ /** Gen JS code for a call to a Scala class constructor.
+ *
+ * This also registers that the given class is instantiated by the current
+ * method, and that the given constructor is called, in the method info
+ * builder.
+ */
+ def genNew(clazz: Symbol, ctor: Symbol, arguments: List[js.Tree])(
+ implicit pos: Position): js.Tree = {
+ if (clazz.isAnonymousFunction)
+ instantiatedAnonFunctions += clazz
+ assert(!isRawJSFunctionDef(clazz),
+ s"Trying to instantiate a raw JS function def $clazz")
+ val ctorIdent = encodeMethodSym(ctor)
+ currentMethodInfoBuilder.instantiatesClass(clazz)
+ currentMethodInfoBuilder.callsMethod(clazz, ctorIdent)
+ js.New(jstpe.ClassType(encodeClassFullName(clazz)),
+ ctorIdent, arguments)
+ }
+
+ /** Gen JS code for a call to a constructor of a hijacked boxed class.
+ * All of these have 2 constructors: one with the primitive
+ * value, which is erased, and one with a String, which is
+ * equivalent to BoxedClass.valueOf(arg).
+ */
+ private def genNewHijackedBoxedClass(clazz: Symbol, ctor: Symbol,
+ arguments: List[js.Tree])(implicit pos: Position): js.Tree = {
+ assert(arguments.size == 1)
+ if (isStringType(ctor.tpe.params.head.tpe)) {
+ // BoxedClass.valueOf(arg)
+ val companion = clazz.companionModule.moduleClass
+ val valueOf = getMemberMethod(companion, nme.valueOf) suchThat { s =>
+ s.tpe.params.size == 1 && isStringType(s.tpe.params.head.tpe)
+ }
+ genApplyMethod(genLoadModule(companion), companion, valueOf, arguments)
+ } else {
+ // erased
+ arguments.head
+ }
+ }
+
+ /** Gen JS code for creating a new Array: new Array[T](length)
+ * For multidimensional arrays (dimensions > 1), the arguments can
+ * specify up to `dimensions` lengths for the first dimensions of the
+ * array.
+ */
+ def genNewArray(arrayType: jstpe.ArrayType, arguments: List[js.Tree])(
+ implicit pos: Position): js.Tree = {
+ assert(arguments.length <= arrayType.dimensions,
+ "too many arguments for array constructor: found " + arguments.length +
+ " but array has only " + arrayType.dimensions + " dimension(s)")
+
+ currentMethodInfoBuilder.accessesClassData(arrayType)
+ js.NewArray(arrayType, arguments)
+ }
+
+ /** Gen JS code for an array literal.
+ */
+ def genArrayValue(tree: Tree): js.Tree = {
+ implicit val pos = tree.pos
+ val ArrayValue(tpt @ TypeTree(), elems) = tree
+
+ val arrType = toReferenceType(tree.tpe).asInstanceOf[jstpe.ArrayType]
+ currentMethodInfoBuilder.accessesClassData(arrType)
+ js.ArrayValue(arrType, elems map genExpr)
+ }
+
+ /** Gen JS code for a Match, i.e., a switch-able pattern match
+ * Eventually, this is compiled into a JS switch construct. But because
+ * we can be in expression position, and a JS switch cannot be given a
+ * meaning in expression position, we emit a JS "match" construct (which
+ * does not need the `break`s in each case. `JSDesugaring` will transform
+ * that in a switch.
+ *
+ * Some caveat here. It may happen that there is a guard in here, despite
+ * the fact that switches cannot have guards (in the JVM nor in JS).
+ * The JVM backend emits a jump to the default clause when a guard is not
+ * fulfilled. We cannot do that. Instead, currently we duplicate the body
+ * of the default case in the else branch of the guard test.
+ */
+ def genMatch(tree: Tree, isStat: Boolean): js.Tree = {
+ implicit val pos = tree.pos
+ val Match(selector, cases) = tree
+
+ val expr = genExpr(selector)
+ val resultType = toIRType(tree.tpe)
+
+ val List(defaultBody0) = for {
+ CaseDef(Ident(nme.WILDCARD), EmptyTree, body) <- cases
+ } yield body
+
+ val (defaultBody, defaultLabelSym) = defaultBody0 match {
+ case LabelDef(_, Nil, rhs) if hasSynthCaseSymbol(defaultBody0) =>
+ (rhs, defaultBody0.symbol)
+ case _ =>
+ (defaultBody0, NoSymbol)
+ }
+
+ val genDefaultBody = genStatOrExpr(defaultBody, isStat)
+
+ var clauses: List[(List[js.Literal], js.Tree)] = Nil
+ var elseClause: js.Tree = js.EmptyTree
+
+ for (caze @ CaseDef(pat, guard, body) <- cases) {
+ assert(guard == EmptyTree)
+
+ def genBody() = body match {
+ // Yes, this will duplicate the default body in the output
+ case If(cond, thenp, app @ Apply(_, Nil))
+ if app.symbol == defaultLabelSym =>
+ js.If(genExpr(cond), genStatOrExpr(thenp, isStat), genDefaultBody)(
+ resultType)(body.pos)
+ case If(cond, thenp, Block(List(app @ Apply(_, Nil)), _))
+ if app.symbol == defaultLabelSym =>
+ js.If(genExpr(cond), genStatOrExpr(thenp, isStat), genDefaultBody)(
+ resultType)(body.pos)
+
+ case _ =>
+ genStatOrExpr(body, isStat)
+ }
+
+ def genLiteral(lit: Literal): js.Literal =
+ genExpr(lit).asInstanceOf[js.Literal]
+
+ pat match {
+ case lit: Literal =>
+ clauses = (List(genLiteral(lit)), genBody()) :: clauses
+ case Ident(nme.WILDCARD) =>
+ elseClause = genDefaultBody
+ case Alternative(alts) =>
+ val genAlts = {
+ alts map {
+ case lit: Literal => genLiteral(lit)
+ case _ =>
+ abort("Invalid case in alternative in switch-like pattern match: " +
+ tree + " at: " + tree.pos)
+ }
+ }
+ clauses = (genAlts, genBody()) :: clauses
+ case _ =>
+ abort("Invalid case statement in switch-like pattern match: " +
+ tree + " at: " + (tree.pos))
+ }
+ }
+
+ js.Match(expr, clauses.reverse, elseClause)(resultType)
+ }
+
+ private def genBlock(tree: Block, isStat: Boolean): js.Tree = {
+ implicit val pos = tree.pos
+ val Block(stats, expr) = tree
+
+ /** Predicate satisfied by LabelDefs produced by the pattern matcher */
+ def isCaseLabelDef(tree: Tree) =
+ tree.isInstanceOf[LabelDef] && hasSynthCaseSymbol(tree)
+
+ def translateMatch(expr: LabelDef) = {
+ /* Block that appeared as the result of a translated match
+ * Such blocks are recognized by having at least one element that is
+ * a so-called case-label-def.
+ * The method `genTranslatedMatch()` takes care of compiling the
+ * actual match.
+ *
+ * The assumption is once we encounter a case, the remainder of the
+ * block will consist of cases.
+ * The prologue may be empty, usually it is the valdef that stores
+ * the scrut.
+ */
+ val (prologue, cases) = stats.span(s => !isCaseLabelDef(s))
+ assert(cases.forall(isCaseLabelDef),
+ "Assumption on the form of translated matches broken: " + tree)
+
+ val genPrologue = prologue map genStat
+ val translatedMatch =
+ genTranslatedMatch(cases.map(_.asInstanceOf[LabelDef]), expr)
+
+ js.Block(genPrologue :+ translatedMatch)
+ }
+
+ expr match {
+ case expr: LabelDef if isCaseLabelDef(expr) =>
+ translateMatch(expr)
+
+ // Sometimes the pattern matcher casts its final result
+ case Apply(TypeApply(Select(expr: LabelDef, nme.asInstanceOf_Ob), _), _)
+ if isCaseLabelDef(expr) =>
+ translateMatch(expr)
+
+ case _ =>
+ assert(!stats.exists(isCaseLabelDef), "Found stats with case label " +
+ s"def in non-match block at ${tree.pos}: $tree")
+
+ /* Normal block */
+ val statements = stats map genStat
+ val expression = genStatOrExpr(expr, isStat)
+ js.Block(statements :+ expression)
+ }
+ }
+
+ /** Gen JS code for a translated match
+ *
+ * This implementation relies heavily on the patterns of trees emitted
+ * by the current pattern match phase (as of Scala 2.10).
+ *
+ * The trees output by the pattern matcher are assumed to follow these
+ * rules:
+ * * Each case LabelDef (in `cases`) must not take any argument.
+ * * The last one must be a catch-all (case _ =>) that never falls through.
+ * * Jumps to the `matchEnd` are allowed anywhere in the body of the
+ * corresponding case label-defs, but not outside.
+ * * Jumps to case label-defs are restricted to jumping to the very next
+ * case, and only in positions denoted by <jump> in:
+ * <case-body> ::=
+ * If(_, <case-body>, <case-body>)
+ * | Block(_, <case-body>)
+ * | <jump>
+ * | _
+ * These restrictions, together with the fact that we are in statement
+ * position (thanks to the above transformation), mean that they can be
+ * simply replaced by `skip`.
+ *
+ * To implement jumps to `matchEnd`, which have one argument which is the
+ * result of the match, we enclose all the cases in one big labeled block.
+ * Jumps are then compiled as `return`s out of the block.
+ */
+ def genTranslatedMatch(cases: List[LabelDef],
+ matchEnd: LabelDef)(implicit pos: Position): js.Tree = {
+
+ val nextCaseSyms = (cases.tail map (_.symbol)) :+ NoSymbol
+
+ val translatedCases = for {
+ (LabelDef(_, Nil, rhs), nextCaseSym) <- cases zip nextCaseSyms
+ } yield {
+ def genCaseBody(tree: Tree): js.Tree = {
+ implicit val pos = tree.pos
+ tree match {
+ case If(cond, thenp, elsep) =>
+ js.If(genExpr(cond), genCaseBody(thenp), genCaseBody(elsep))(
+ jstpe.NoType)
+
+ case Block(stats, expr) =>
+ js.Block((stats map genStat) :+ genCaseBody(expr))
+
+ case Apply(_, Nil) if tree.symbol == nextCaseSym =>
+ js.Skip()
+
+ case _ =>
+ genStat(tree)
+ }
+ }
+
+ genCaseBody(rhs)
+ }
+
+ js.Labeled(encodeLabelSym(matchEnd.symbol), toIRType(matchEnd.tpe),
+ js.Block(translatedCases))
+ }
+
+ /** Gen JS code for a primitive method call */
+ private def genPrimitiveOp(tree: Apply, isStat: Boolean): js.Tree = {
+ import scalaPrimitives._
+
+ implicit val pos = tree.pos
+
+ val sym = tree.symbol
+ val Apply(fun @ Select(receiver, _), args) = tree
+
+ val code = scalaPrimitives.getPrimitive(sym, receiver.tpe)
+
+ if (isArithmeticOp(code) || isLogicalOp(code) || isComparisonOp(code))
+ genSimpleOp(tree, receiver :: args, code)
+ else if (code == scalaPrimitives.CONCAT)
+ genStringConcat(tree, receiver, args)
+ else if (code == HASH)
+ genScalaHash(tree, receiver)
+ else if (isArrayOp(code))
+ genArrayOp(tree, code)
+ else if (code == SYNCHRONIZED)
+ genSynchronized(tree, isStat)
+ else if (isCoercion(code))
+ genCoercion(tree, receiver, code)
+ else if (jsPrimitives.isJavaScriptPrimitive(code))
+ genJSPrimitive(tree, receiver, args, code)
+ else
+ abort("Unknown primitive operation: " + sym.fullName + "(" +
+ fun.symbol.simpleName + ") " + " at: " + (tree.pos))
+ }
+
+ /** Gen JS code for a simple operation (arithmetic, logical, or comparison) */
+ private def genSimpleOp(tree: Apply, args: List[Tree], code: Int): js.Tree = {
+ import scalaPrimitives._
+
+ implicit val pos = tree.pos
+
+ def isLongOp(ltpe: Type, rtpe: Type) =
+ (isLongType(ltpe) || isLongType(rtpe)) &&
+ !(toTypeKind(ltpe).isInstanceOf[FLOAT] ||
+ toTypeKind(rtpe).isInstanceOf[FLOAT] ||
+ isStringType(ltpe) || isStringType(rtpe))
+
+ val sources = args map genExpr
+
+ val resultType = toIRType(tree.tpe)
+
+ sources match {
+ // Unary operation
+ case List(source) =>
+ (code match {
+ case POS =>
+ source
+ case NEG =>
+ (resultType: @unchecked) match {
+ case jstpe.IntType =>
+ js.BinaryOp(js.BinaryOp.Int_-, js.IntLiteral(0), source)
+ case jstpe.LongType =>
+ js.BinaryOp(js.BinaryOp.Long_-, js.LongLiteral(0), source)
+ case jstpe.FloatType =>
+ js.BinaryOp(js.BinaryOp.Float_-, js.FloatLiteral(0.0f), source)
+ case jstpe.DoubleType =>
+ js.BinaryOp(js.BinaryOp.Double_-, js.DoubleLiteral(0), source)
+ }
+ case NOT =>
+ (resultType: @unchecked) match {
+ case jstpe.IntType =>
+ js.BinaryOp(js.BinaryOp.Int_^, js.IntLiteral(-1), source)
+ case jstpe.LongType =>
+ js.BinaryOp(js.BinaryOp.Long_^, js.LongLiteral(-1), source)
+ }
+ case ZNOT =>
+ js.UnaryOp(js.UnaryOp.Boolean_!, source)
+ case _ =>
+ abort("Unknown unary operation code: " + code)
+ })
+
+ // Binary operation on Longs
+ case List(lsrc, rsrc) if isLongOp(args(0).tpe, args(1).tpe) =>
+ def toLong(tree: js.Tree, tpe: Type) =
+ if (isLongType(tpe)) tree
+ else js.UnaryOp(js.UnaryOp.IntToLong, tree)
+
+ def toInt(tree: js.Tree, tpe: Type) =
+ if (isLongType(tpe)) js.UnaryOp(js.UnaryOp.LongToInt, rsrc)
+ else tree
+
+ val ltree = toLong(lsrc, args(0).tpe)
+ def rtree = toLong(rsrc, args(1).tpe)
+ def rtreeInt = toInt(rsrc, args(1).tpe)
+
+ import js.BinaryOp._
+ (code: @switch) match {
+ case ADD => js.BinaryOp(Long_+, ltree, rtree)
+ case SUB => js.BinaryOp(Long_-, ltree, rtree)
+ case MUL => js.BinaryOp(Long_*, ltree, rtree)
+ case DIV => js.BinaryOp(Long_/, ltree, rtree)
+ case MOD => js.BinaryOp(Long_%, ltree, rtree)
+ case OR => js.BinaryOp(Long_|, ltree, rtree)
+ case XOR => js.BinaryOp(Long_^, ltree, rtree)
+ case AND => js.BinaryOp(Long_&, ltree, rtree)
+ case LSL => js.BinaryOp(Long_<<, ltree, rtreeInt)
+ case LSR => js.BinaryOp(Long_>>>, ltree, rtreeInt)
+ case ASR => js.BinaryOp(Long_>>, ltree, rtreeInt)
+ case EQ => js.BinaryOp(Long_==, ltree, rtree)
+ case NE => js.BinaryOp(Long_!=, ltree, rtree)
+ case LT => js.BinaryOp(Long_<, ltree, rtree)
+ case LE => js.BinaryOp(Long_<=, ltree, rtree)
+ case GT => js.BinaryOp(Long_>, ltree, rtree)
+ case GE => js.BinaryOp(Long_>=, ltree, rtree)
+ case _ =>
+ abort("Unknown binary operation code: " + code)
+ }
+
+ // Binary operation
+ case List(lsrc_in, rsrc_in) =>
+ def convertArg(tree: js.Tree, tpe: Type) = {
+ val kind = toTypeKind(tpe)
+
+ // If we end up with a long, target must be float or double
+ val fromLong =
+ if (kind == LongKind) js.UnaryOp(js.UnaryOp.LongToDouble, tree)
+ else tree
+
+ if (resultType != jstpe.FloatType) fromLong
+ else if (kind == FloatKind) fromLong
+ else js.UnaryOp(js.UnaryOp.DoubleToFloat, fromLong)
+ }
+
+ val lsrc = convertArg(lsrc_in, args(0).tpe)
+ val rsrc = convertArg(rsrc_in, args(1).tpe)
+
+ def genEquality(eqeq: Boolean, not: Boolean) = {
+ val typeKind = toTypeKind(args(0).tpe)
+ typeKind match {
+ case INT(_) | LONG | FLOAT(_) =>
+ /* Note that LONG happens when a fromLong() had to do something,
+ * which means we're effectively in the FLOAT case. */
+ js.BinaryOp(if (not) js.BinaryOp.Num_!= else js.BinaryOp.Num_==, lsrc, rsrc)
+ case BOOL =>
+ js.BinaryOp(if (not) js.BinaryOp.Boolean_!= else js.BinaryOp.Boolean_==, lsrc, rsrc)
+ case REFERENCE(_) =>
+ if (eqeq &&
+ // don't call equals if we have a literal null at either side
+ !lsrc.isInstanceOf[js.Null] &&
+ !rsrc.isInstanceOf[js.Null]) {
+ val body = genEqEqPrimitive(args(0).tpe, args(1).tpe, lsrc, rsrc)
+ if (not) js.UnaryOp(js.UnaryOp.Boolean_!, body) else body
+ } else {
+ js.BinaryOp(if (not) js.BinaryOp.!== else js.BinaryOp.===, lsrc, rsrc)
+ }
+ case _ =>
+ // Arrays, Null, Nothing do not have an equals() method.
+ js.BinaryOp(if (not) js.BinaryOp.!== else js.BinaryOp.===, lsrc, rsrc)
+ }
+ }
+
+ (code: @switch) match {
+ case EQ => genEquality(eqeq = true, not = false)
+ case NE => genEquality(eqeq = true, not = true)
+ case ID => genEquality(eqeq = false, not = false)
+ case NI => genEquality(eqeq = false, not = true)
+
+ case ZOR => js.If(lsrc, js.BooleanLiteral(true), rsrc)(jstpe.BooleanType)
+ case ZAND => js.If(lsrc, rsrc, js.BooleanLiteral(false))(jstpe.BooleanType)
+
+ case _ =>
+ import js.BinaryOp._
+ val op = (resultType: @unchecked) match {
+ case jstpe.IntType =>
+ (code: @switch) match {
+ case ADD => Int_+
+ case SUB => Int_-
+ case MUL => Int_*
+ case DIV => Int_/
+ case MOD => Int_%
+ case OR => Int_|
+ case AND => Int_&
+ case XOR => Int_^
+ case LSL => Int_<<
+ case LSR => Int_>>>
+ case ASR => Int_>>
+ }
+ case jstpe.FloatType =>
+ (code: @switch) match {
+ case ADD => Float_+
+ case SUB => Float_-
+ case MUL => Float_*
+ case DIV => Float_/
+ case MOD => Float_%
+ }
+ case jstpe.DoubleType =>
+ (code: @switch) match {
+ case ADD => Double_+
+ case SUB => Double_-
+ case MUL => Double_*
+ case DIV => Double_/
+ case MOD => Double_%
+ }
+ case jstpe.BooleanType =>
+ (code: @switch) match {
+ case LT => Num_<
+ case LE => Num_<=
+ case GT => Num_>
+ case GE => Num_>=
+ case OR => Boolean_|
+ case AND => Boolean_&
+ case XOR => Boolean_!=
+ }
+ }
+ js.BinaryOp(op, lsrc, rsrc)
+ }
+
+ case _ =>
+ abort("Too many arguments for primitive function: " + tree)
+ }
+ }
+
+ /** 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 = {
+ /* True if the equality comparison is between values that require the
+ * use of the rich equality comparator
+ * (scala.runtime.BoxesRunTime.equals).
+ * This is the case when either side of the comparison might have a
+ * run-time type subtype of java.lang.Number or java.lang.Character,
+ * **which includes when either is a raw JS type**.
+ * When it is statically known that both sides are equal and subtypes of
+ * Number or Character, not using the rich equality is possible (their
+ * own equals method will do ok.)
+ */
+ val mustUseAnyComparator: Boolean = isRawJSType(ltpe) || isRawJSType(rtpe) || {
+ val areSameFinals = ltpe.isFinalType && rtpe.isFinalType && (ltpe =:= rtpe)
+ !areSameFinals && isMaybeBoxed(ltpe.typeSymbol) && isMaybeBoxed(rtpe.typeSymbol)
+ }
+
+ if (mustUseAnyComparator) {
+ val equalsMethod: Symbol = {
+ val ptfm = platform.asInstanceOf[backend.JavaPlatform with ThisPlatform] // 2.10 compat
+ if (ltpe <:< BoxedNumberClass.tpe) {
+ if (rtpe <:< BoxedNumberClass.tpe) ptfm.externalEqualsNumNum
+ else if (rtpe <:< BoxedCharacterClass.tpe) ptfm.externalEqualsNumChar
+ else ptfm.externalEqualsNumObject
+ } else ptfm.externalEquals
+ }
+ val moduleClass = equalsMethod.owner
+ val instance = genLoadModule(moduleClass)
+ genApplyMethod(instance, moduleClass, equalsMethod, List(lsrc, rsrc))
+ } else {
+ // if (lsrc eq null) rsrc eq null else lsrc.equals(rsrc)
+ if (isStringType(ltpe)) {
+ // String.equals(that) === (this eq that)
+ js.BinaryOp(js.BinaryOp.===, lsrc, rsrc)
+ } else {
+ /* This requires to evaluate both operands in local values first.
+ * The optimizer will eliminate them if possible.
+ */
+ val ltemp = js.VarDef(freshLocalIdent(), lsrc.tpe, mutable = false, lsrc)
+ val rtemp = js.VarDef(freshLocalIdent(), rsrc.tpe, mutable = false, rsrc)
+ js.Block(
+ ltemp,
+ rtemp,
+ js.If(js.BinaryOp(js.BinaryOp.===, ltemp.ref, js.Null()),
+ js.BinaryOp(js.BinaryOp.===, rtemp.ref, js.Null()),
+ genApplyMethod(ltemp.ref, ltpe, Object_equals, List(rtemp.ref)))(
+ jstpe.BooleanType))
+ }
+ }
+ }
+
+ /** Gen JS code for string concatenation.
+ */
+ private def genStringConcat(tree: Apply, receiver: Tree,
+ args: List[Tree]): js.Tree = {
+ implicit val pos = tree.pos
+
+ /* Primitive number types such as scala.Int have a
+ * def +(s: String): String
+ * method, which is why we have to box the lhs sometimes.
+ * Otherwise, both lhs and rhs are already reference types (Any of String)
+ * so boxing is not necessary (in particular, rhs is never a primitive).
+ */
+ assert(!isPrimitiveValueType(receiver.tpe) || isStringType(args.head.tpe))
+ assert(!isPrimitiveValueType(args.head.tpe))
+
+ val rhs = genExpr(args.head)
+
+ val lhs = {
+ val lhs0 = genExpr(receiver)
+ // Box the receiver if it is a primitive value
+ if (!isPrimitiveValueType(receiver.tpe)) lhs0
+ else makePrimitiveBox(lhs0, receiver.tpe)
+ }
+
+ js.BinaryOp(js.BinaryOp.String_+, lhs, rhs)
+ }
+
+ /** Gen JS code for a call to Any.## */
+ private def genScalaHash(tree: Apply, receiver: Tree): js.Tree = {
+ implicit val pos = tree.pos
+
+ val instance = genLoadModule(ScalaRunTimeModule)
+ val arguments = List(genExpr(receiver))
+ val sym = getMember(ScalaRunTimeModule, nme.hash_)
+
+ genApplyMethod(instance, ScalaRunTimeModule.moduleClass, sym, arguments)
+ }
+
+ /** Gen JS code for an array operation (get, set or length) */
+ private def genArrayOp(tree: Tree, code: Int): js.Tree = {
+ import scalaPrimitives._
+
+ implicit val pos = tree.pos
+
+ val Apply(Select(arrayObj, _), args) = tree
+ val arrayValue = genExpr(arrayObj)
+ val arguments = args map genExpr
+
+ def genSelect() = {
+ val elemIRType =
+ toTypeKind(arrayObj.tpe).asInstanceOf[ARRAY].elem.toIRType
+ js.ArraySelect(arrayValue, arguments(0))(elemIRType)
+ }
+
+ if (scalaPrimitives.isArrayGet(code)) {
+ // get an item of the array
+ assert(args.length == 1,
+ s"Array get requires 1 argument, found ${args.length} in $tree")
+ genSelect()
+ } else if (scalaPrimitives.isArraySet(code)) {
+ // set an item of the array
+ assert(args.length == 2,
+ s"Array set requires 2 arguments, found ${args.length} in $tree")
+ js.Assign(genSelect(), arguments(1))
+ } else {
+ // length of the array
+ js.ArrayLength(arrayValue)
+ }
+ }
+
+ /** Gen JS code for a call to AnyRef.synchronized */
+ private def genSynchronized(tree: Apply, isStat: Boolean): js.Tree = {
+ /* JavaScript is single-threaded, so we can drop the
+ * synchronization altogether.
+ */
+ val Apply(Select(receiver, _), List(arg)) = tree
+ val newReceiver = genExpr(receiver)
+ val newArg = genStatOrExpr(arg, isStat)
+ newReceiver match {
+ case js.This() =>
+ // common case for which there is no side-effect nor NPE
+ newArg
+ case _ =>
+ implicit val pos = tree.pos
+ 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),
+ newArg)
+ }
+ }
+
+ /** Gen JS code for a coercion */
+ private def genCoercion(tree: Apply, receiver: Tree, code: Int): js.Tree = {
+ import scalaPrimitives._
+
+ implicit val pos = tree.pos
+
+ val source = genExpr(receiver)
+
+ def source2int = (code: @switch) match {
+ case F2C | D2C | F2B | D2B | F2S | D2S | F2I | D2I =>
+ js.UnaryOp(js.UnaryOp.DoubleToInt, source)
+ case L2C | L2B | L2S | L2I =>
+ js.UnaryOp(js.UnaryOp.LongToInt, source)
+ case _ =>
+ source
+ }
+
+ (code: @switch) match {
+ // To Char, need to crop at unsigned 16-bit
+ case B2C | S2C | I2C | L2C | F2C | D2C =>
+ js.BinaryOp(js.BinaryOp.Int_&, source2int, js.IntLiteral(0xffff))
+
+ // To Byte, need to crop at signed 8-bit
+ case C2B | S2B | I2B | L2B | F2B | D2B =>
+ // note: & 0xff would not work because of negative values
+ js.BinaryOp(js.BinaryOp.Int_>>,
+ js.BinaryOp(js.BinaryOp.Int_<<, source2int, js.IntLiteral(24)),
+ js.IntLiteral(24))
+
+ // To Short, need to crop at signed 16-bit
+ case C2S | I2S | L2S | F2S | D2S =>
+ // note: & 0xffff would not work because of negative values
+ js.BinaryOp(js.BinaryOp.Int_>>,
+ js.BinaryOp(js.BinaryOp.Int_<<, source2int, js.IntLiteral(16)),
+ js.IntLiteral(16))
+
+ // To Int, need to crop at signed 32-bit
+ case L2I | F2I | D2I =>
+ source2int
+
+ // Any int to Long
+ case C2L | B2L | S2L | I2L =>
+ js.UnaryOp(js.UnaryOp.IntToLong, source)
+
+ // Any double to Long
+ case F2L | D2L =>
+ js.UnaryOp(js.UnaryOp.DoubleToLong, source)
+
+ // Long to Double
+ case L2D =>
+ js.UnaryOp(js.UnaryOp.LongToDouble, source)
+
+ // Any int, or Double, to Float
+ case C2F | B2F | S2F | I2F | D2F =>
+ js.UnaryOp(js.UnaryOp.DoubleToFloat, source)
+
+ // Long to Float === Long to Double to Float
+ case L2F =>
+ js.UnaryOp(js.UnaryOp.DoubleToFloat,
+ js.UnaryOp(js.UnaryOp.LongToDouble, source))
+
+ // Identities and IR upcasts
+ case C2C | B2B | S2S | I2I | L2L | F2F | D2D |
+ C2I | C2D |
+ B2S | B2I | B2D |
+ S2I | S2D |
+ I2D |
+ F2D =>
+ source
+ }
+ }
+
+ /** Gen JS code for an ApplyDynamic
+ * ApplyDynamic nodes appear as the result of calls to methods of a
+ * structural type.
+ *
+ * Most unfortunately, earlier phases of the compiler assume too much
+ * about the backend, namely, they believe arguments and the result must
+ * be boxed, and do the boxing themselves. This decision should be left
+ * to the backend, but it's not, so we have to undo these boxes.
+ * Note that this applies to parameter types only. The return type is boxed
+ * anyway since we do not know it's exact type.
+ *
+ * This then generates a call to the reflective call proxy for the given
+ * arguments.
+ */
+ private def genApplyDynamic(tree: ApplyDynamic): js.Tree = {
+ implicit val pos = tree.pos
+
+ val sym = tree.symbol
+ val params = sym.tpe.params
+
+ /** check if the method we are invoking is eq or ne. they cannot be
+ * overridden since they are final. If this is true, we only emit a
+ * `===` or `!==`.
+ */
+ val isEqOrNeq = (sym.name == nme.eq || sym.name == nme.ne) &&
+ params.size == 1 && params.head.tpe.typeSymbol == ObjectClass
+
+ /** check if the method we are invoking conforms to a method on
+ * scala.Array. If this is the case, we check that case specially at
+ * runtime to avoid having reflective call proxies on scala.Array.
+ * (Also, note that the element type of Array#update is not erased and
+ * therefore the method name mangling would turn out wrong)
+ *
+ * Note that we cannot check if the expected return type is correct,
+ * since this type information is already erased.
+ */
+ def isArrayLikeOp = {
+ sym.name == nme.update &&
+ params.size == 2 && params.head.tpe.typeSymbol == IntClass ||
+ sym.name == nme.apply &&
+ params.size == 1 && params.head.tpe.typeSymbol == IntClass ||
+ sym.name == nme.length &&
+ params.size == 0 ||
+ sym.name == nme.clone_ &&
+ params.size == 0
+ }
+
+ /**
+ * Tests whether one of our reflective "boxes" for primitive types
+ * implements the particular method. If this is the case
+ * (result != NoSymbol), we generate a runtime instance check if we are
+ * dealing with the appropriate primitive type.
+ */
+ def matchingSymIn(clazz: Symbol) = clazz.tpe.member(sym.name).suchThat { s =>
+ val sParams = s.tpe.params
+ !s.isBridge &&
+ params.size == sParams.size &&
+ (params zip sParams).forall { case (s1,s2) =>
+ s1.tpe =:= s2.tpe
+ }
+ }
+
+ val ApplyDynamic(receiver, args) = tree
+
+ if (isEqOrNeq) {
+ // Just emit a boxed equality check
+ val jsThis = genExpr(receiver)
+ val jsThat = genExpr(args.head)
+ val op = if (sym.name == nme.eq) js.BinaryOp.=== else js.BinaryOp.!==
+ ensureBoxed(js.BinaryOp(op, jsThis, jsThat), BooleanClass.tpe)
+ } else {
+ // Create a fully-fledged reflective call
+ val receiverType = toIRType(receiver.tpe)
+ val callTrgIdent = freshLocalIdent()
+ val callTrgVarDef =
+ js.VarDef(callTrgIdent, receiverType, mutable = false, genExpr(receiver))
+ val callTrg = js.VarRef(callTrgIdent, mutable = false)(receiverType)
+
+ val arguments = args zip sym.tpe.params map { case (arg, param) =>
+ /* No need for enteringPosterasure, because value classes are not
+ * supported as parameters of methods in structural types.
+ * We could do it for safety and future-proofing anyway, except that
+ * I am weary of calling enteringPosterasure for a reflective method
+ * symbol.
+ *
+ * Note also that this will typically unbox a primitive value that
+ * has just been boxed, or will .asInstanceOf[T] an expression which
+ * is already of type T. But the optimizer will get rid of that, and
+ * reflective calls are not numerous, so we don't complicate the
+ * compiler to eliminate them early.
+ */
+ fromAny(genExpr(arg), param.tpe)
+ }
+
+ val proxyIdent = encodeMethodSym(sym, reflProxy = true)
+ var callStatement: js.Tree =
+ genApplyMethod(callTrg, receiver.tpe, proxyIdent, arguments,
+ jstpe.AnyType)
+
+ if (isArrayLikeOp) {
+ def genRTCall(method: Symbol, args: js.Tree*) =
+ genApplyMethod(genLoadModule(ScalaRunTimeModule),
+ ScalaRunTimeModule.moduleClass, method, args.toList)
+ val isArrayTree =
+ genRTCall(ScalaRunTime_isArray, callTrg, js.IntLiteral(1))
+ callStatement = js.If(isArrayTree, {
+ sym.name match {
+ case nme.update =>
+ js.Block(
+ genRTCall(currentRun.runDefinitions.arrayUpdateMethod,
+ callTrg, arguments(0), arguments(1)),
+ js.Undefined()) // Boxed Unit
+ case nme.apply =>
+ genRTCall(currentRun.runDefinitions.arrayApplyMethod, callTrg,
+ arguments(0))
+ case nme.length =>
+ genRTCall(currentRun.runDefinitions.arrayLengthMethod, callTrg)
+ case nme.clone_ =>
+ genApplyMethod(callTrg, receiver.tpe, Object_clone, arguments)
+ }
+ }, {
+ callStatement
+ })(jstpe.AnyType)
+ }
+
+ for {
+ (primTypeOf, reflBoxClass) <- Seq(
+ ("string", StringClass),
+ ("number", NumberReflectiveCallClass),
+ ("boolean", BooleanReflectiveCallClass)
+ )
+ implMethodSym = matchingSymIn(reflBoxClass)
+ if implMethodSym != NoSymbol && implMethodSym.isPublic
+ } {
+ callStatement = js.If(
+ js.BinaryOp(js.BinaryOp.===,
+ js.UnaryOp(js.UnaryOp.typeof, callTrg),
+ js.StringLiteral(primTypeOf)), {
+ if (implMethodSym.owner == ObjectClass) {
+ // If the method is defined on Object, we can call it normally.
+ genApplyMethod(callTrg, receiver.tpe, implMethodSym, arguments)
+ } else {
+ if (primTypeOf == "string") {
+ val (rtModuleClass, methodIdent) =
+ encodeRTStringMethodSym(implMethodSym)
+ val retTpe = implMethodSym.tpe.resultType
+ val castCallTrg = fromAny(callTrg, StringClass.toTypeConstructor)
+ val rawApply = genApplyMethod(
+ genLoadModule(rtModuleClass),
+ rtModuleClass,
+ methodIdent,
+ castCallTrg :: arguments,
+ toIRType(retTpe))
+ // Box the result of the implementing method if required
+ if (isPrimitiveValueType(retTpe))
+ makePrimitiveBox(rawApply, retTpe)
+ else
+ rawApply
+ } else {
+ val (reflBoxClassPatched, callTrg1) = {
+ def isIntOrLongKind(kind: TypeKind) = kind match {
+ case _:INT | LONG => true
+ case _ => false
+ }
+ if (primTypeOf == "number" &&
+ toTypeKind(implMethodSym.tpe.resultType) == DoubleKind &&
+ isIntOrLongKind(toTypeKind(sym.tpe.resultType))) {
+ // This must be an Int, and not a Double
+ (IntegerReflectiveCallClass,
+ js.AsInstanceOf(callTrg,
+ toReferenceType(BoxedIntClass.toTypeConstructor)))
+ } else {
+ (reflBoxClass, callTrg)
+ }
+ }
+ val castCallTrg =
+ fromAny(callTrg1,
+ reflBoxClassPatched.primaryConstructor.tpe.params.head.tpe)
+ val reflBox = genNew(reflBoxClassPatched,
+ reflBoxClassPatched.primaryConstructor, List(castCallTrg))
+ genApplyMethod(
+ reflBox,
+ reflBoxClassPatched,
+ proxyIdent,
+ arguments,
+ jstpe.AnyType)
+ }
+ }
+ }, { // else
+ callStatement
+ })(jstpe.AnyType)
+ }
+
+ js.Block(callTrgVarDef, callStatement)
+ }
+ }
+
+ /** Ensures that the value of the given tree is boxed.
+ * @param expr Tree to be boxed if needed.
+ * @param tpeEnteringPosterasure The type of `expr` as it was entering
+ * the posterasure phase.
+ */
+ def ensureBoxed(expr: js.Tree, tpeEnteringPosterasure: Type)(
+ implicit pos: Position): js.Tree = {
+
+ tpeEnteringPosterasure match {
+ case tpe if isPrimitiveValueType(tpe) =>
+ makePrimitiveBox(expr, tpe)
+
+ case tpe: ErasedValueType =>
+ val boxedClass = tpe.valueClazz
+ val ctor = boxedClass.primaryConstructor
+ genNew(boxedClass, ctor, List(expr))
+
+ case _ =>
+ expr
+ }
+ }
+
+ /** Extracts a value typed as Any to the given type after posterasure.
+ * @param expr Tree to be extracted.
+ * @param tpeEnteringPosterasure The type of `expr` as it was entering
+ * the posterasure phase.
+ */
+ def fromAny(expr: js.Tree, tpeEnteringPosterasure: Type)(
+ implicit pos: Position): js.Tree = {
+
+ tpeEnteringPosterasure match {
+ case tpe if isPrimitiveValueType(tpe) =>
+ makePrimitiveUnbox(expr, tpe)
+
+ case tpe: ErasedValueType =>
+ val boxedClass = tpe.valueClazz
+ val unboxMethod = boxedClass.derivedValueClassUnbox
+ val content = genApplyMethod(
+ genAsInstanceOf(expr, tpe),
+ boxedClass, unboxMethod, Nil)
+ if (unboxMethod.tpe.resultType <:< tpe.erasedUnderlying)
+ content
+ else
+ fromAny(content, tpe.erasedUnderlying)
+
+ case tpe =>
+ genAsInstanceOf(expr, tpe)
+ }
+ }
+
+ /** Gen a boxing operation (tpe is the primitive type) */
+ def makePrimitiveBox(expr: js.Tree, tpe: Type)(
+ implicit pos: Position): js.Tree = {
+ toTypeKind(tpe) match {
+ case VOID => // must be handled at least for JS interop
+ js.Block(expr, js.Undefined())
+ case kind: ValueTypeKind =>
+ if (kind == CharKind) {
+ genApplyMethod(
+ genLoadModule(BoxesRunTimeClass),
+ BoxesRunTimeClass,
+ BoxesRunTime_boxToCharacter,
+ List(expr))
+ } else {
+ expr // box is identity for all non-Char types
+ }
+ case _ =>
+ abort(s"makePrimitiveBox requires a primitive type, found $tpe at $pos")
+ }
+ }
+
+ /** Gen an unboxing operation (tpe is the primitive type) */
+ def makePrimitiveUnbox(expr: js.Tree, tpe: Type)(
+ implicit pos: Position): js.Tree = {
+ toTypeKind(tpe) match {
+ case VOID => // must be handled at least for JS interop
+ expr
+ case kind: ValueTypeKind =>
+ if (kind == CharKind) {
+ genApplyMethod(
+ genLoadModule(BoxesRunTimeClass),
+ BoxesRunTimeClass,
+ BoxesRunTime_unboxToChar,
+ List(expr))
+ } else {
+ js.Unbox(expr, kind.primitiveCharCode)
+ }
+ case _ =>
+ abort(s"makePrimitiveUnbox requires a primitive type, found $tpe at $pos")
+ }
+ }
+
+ private def lookupModuleClass(name: String) = {
+ val module = getModuleIfDefined(name)
+ if (module == NoSymbol) NoSymbol
+ else module.moduleClass
+ }
+
+ lazy val ReflectArrayModuleClass = lookupModuleClass("java.lang.reflect.Array")
+ lazy val UtilArraysModuleClass = lookupModuleClass("java.util.Arrays")
+
+ /** Gen JS code for a Scala.js-specific primitive method */
+ private def genJSPrimitive(tree: Apply, receiver0: Tree,
+ args: List[Tree], code: Int): js.Tree = {
+ import jsPrimitives._
+
+ implicit val pos = tree.pos
+
+ def receiver = genExpr(receiver0)
+ val genArgArray = genPrimitiveJSArgs(tree.symbol, args)
+
+ lazy val js.JSArrayConstr(genArgs) = genArgArray
+
+ def extractFirstArg() = {
+ (genArgArray: @unchecked) match {
+ case js.JSArrayConstr(firstArg :: otherArgs) =>
+ (firstArg, js.JSArrayConstr(otherArgs))
+ case js.JSBracketMethodApply(
+ js.JSArrayConstr(firstArg :: firstPart), concat, otherParts) =>
+ (firstArg, js.JSBracketMethodApply(
+ js.JSArrayConstr(firstPart), concat, otherParts))
+ }
+ }
+
+ if (code == DYNNEW) {
+ // js.Dynamic.newInstance(clazz)(actualArgs:_*)
+ val (jsClass, actualArgArray) = extractFirstArg()
+ actualArgArray match {
+ case js.JSArrayConstr(actualArgs) =>
+ js.JSNew(jsClass, actualArgs)
+ case _ =>
+ genNewJSWithVarargs(jsClass, actualArgArray)
+ }
+ } else if (code == DYNAPPLY) {
+ // js.Dynamic.applyDynamic(methodName)(actualArgs:_*)
+ val (methodName, actualArgArray) = extractFirstArg()
+ actualArgArray match {
+ case js.JSArrayConstr(actualArgs) =>
+ js.JSBracketMethodApply(receiver, methodName, actualArgs)
+ case _ =>
+ genApplyJSMethodWithVarargs(receiver, methodName, actualArgArray)
+ }
+ } else if (code == DYNLITN) {
+ // We have a call of the form:
+ // js.Dynamic.literal(name1 = ..., name2 = ...)
+ // Translate to:
+ // {"name1": ..., "name2": ... }
+ extractFirstArg() match {
+ case (js.StringLiteral("apply"),
+ js.JSArrayConstr(jse.LitNamed(pairs))) =>
+ js.JSObjectConstr(pairs)
+ case (js.StringLiteral(name), _) if name != "apply" =>
+ reporter.error(pos,
+ s"js.Dynamic.literal does not have a method named $name")
+ js.Undefined()
+ case _ =>
+ reporter.error(pos,
+ "js.Dynamic.literal.applyDynamicNamed may not be called directly")
+ js.Undefined()
+ }
+ } else if (code == DYNLIT) {
+ // We have a call of some other form
+ // js.Dynamic.literal(...)
+ // Translate to:
+ // var obj = {};
+ // obj[...] = ...;
+ // obj
+
+ // Extract first arg to future proof against varargs
+ extractFirstArg() match {
+ // case js.Dynamic.literal("name1" -> ..., "name2" -> ...)
+ case (js.StringLiteral("apply"),
+ js.JSArrayConstr(jse.LitNamed(pairs))) =>
+ js.JSObjectConstr(pairs)
+
+ // case js.Dynamic.literal(x, y)
+ case (js.StringLiteral("apply"), js.JSArrayConstr(tups)) =>
+ // Create tmp variable
+ val resIdent = freshLocalIdent("obj")
+ val resVarDef = js.VarDef(resIdent, jstpe.AnyType, mutable = false,
+ js.JSObjectConstr(Nil))
+ val res = resVarDef.ref
+
+ // Assign fields
+ val tuple2Type = encodeClassType(TupleClass(2))
+ val assigns = tups flatMap {
+ // special case for literals
+ case jse.Tuple2(name, value) =>
+ js.Assign(js.JSBracketSelect(res, name), value) :: Nil
+ case tupExpr =>
+ val tupIdent = freshLocalIdent("tup")
+ val tup = js.VarRef(tupIdent, mutable = false)(tuple2Type)
+ js.VarDef(tupIdent, tuple2Type, mutable = false, tupExpr) ::
+ js.Assign(js.JSBracketSelect(res,
+ genApplyMethod(tup, TupleClass(2), js.Ident("$$und1__O"), Nil, jstpe.AnyType)),
+ genApplyMethod(tup, TupleClass(2), js.Ident("$$und2__O"), Nil, jstpe.AnyType)) :: Nil
+ }
+
+ js.Block(resVarDef +: assigns :+ res: _*)
+
+ /* Here we would need the case where the varargs are passed in
+ * as non-literal list:
+ * js.Dynamic.literal(x: _*)
+ * However, Scala does not currently support this
+ */
+
+ // case where another method is called
+ case (js.StringLiteral(name), _) if name != "apply" =>
+ reporter.error(pos,
+ s"js.Dynamic.literal does not have a method named $name")
+ js.Undefined()
+ case _ =>
+ reporter.error(pos,
+ "js.Dynamic.literal.applyDynamic may not be called directly")
+ js.Undefined()
+ }
+ } else if (code == ARR_CREATE) {
+ // js.Array.create(elements: _*)
+ genArgArray
+ } else (genArgs match {
+ case Nil =>
+ code match {
+ case GETCLASS => js.GetClass(receiver)
+ case ENV_INFO => js.JSEnvInfo()
+ case DEBUGGER => js.Debugger()
+ case UNDEFVAL => js.Undefined()
+ case UNITVAL => js.Undefined()
+ case UNITTYPE => genClassConstant(UnitTpe)
+ case JS_NATIVE =>
+ reporter.error(pos, "js.native may only be used as stub implementation in facade types")
+ js.Undefined()
+ }
+
+ case List(arg) =>
+
+ /** Factorization of F2JS and F2JSTHIS. */
+ def genFunctionToJSFunction(isThisFunction: Boolean): js.Tree = {
+ val arity = {
+ val funName = tree.fun.symbol.name.encoded
+ assert(funName.startsWith("fromFunction"))
+ funName.stripPrefix("fromFunction").toInt
+ }
+ val inputClass = FunctionClass(arity)
+ val inputIRType = encodeClassType(inputClass)
+ val applyMeth = getMemberMethod(inputClass, nme.apply) suchThat { s =>
+ val ps = s.paramss
+ ps.size == 1 &&
+ ps.head.size == arity &&
+ ps.head.forall(_.tpe.typeSymbol == ObjectClass)
+ }
+ val fCaptureParam = js.ParamDef(js.Ident("f"), inputIRType,
+ mutable = false)
+ val jsArity =
+ if (isThisFunction) arity - 1
+ else arity
+ val jsParams = (1 to jsArity).toList map {
+ x => js.ParamDef(js.Ident("arg"+x), jstpe.AnyType,
+ mutable = false)
+ }
+ js.Closure(
+ List(fCaptureParam),
+ jsParams,
+ genApplyMethod(
+ fCaptureParam.ref,
+ inputClass, applyMeth,
+ if (isThisFunction)
+ js.This()(jstpe.AnyType) :: jsParams.map(_.ref)
+ else
+ jsParams.map(_.ref)),
+ List(arg))
+ }
+
+ code match {
+ /** Convert a scala.FunctionN f to a js.FunctionN. */
+ case F2JS =>
+ arg match {
+ /* This case will happen every time we have a Scala lambda
+ * in js.FunctionN position. We remove the JS function to
+ * Scala function wrapper, instead of adding a Scala function
+ * to JS function wrapper.
+ */
+ case JSFunctionToScala(fun, arity) =>
+ fun
+ case _ =>
+ genFunctionToJSFunction(isThisFunction = false)
+ }
+
+ /** Convert a scala.FunctionN f to a js.ThisFunction{N-1}. */
+ case F2JSTHIS =>
+ genFunctionToJSFunction(isThisFunction = true)
+
+ case DYNSELECT =>
+ // js.Dynamic.selectDynamic(arg)
+ js.JSBracketSelect(receiver, arg)
+
+ case DICT_DEL =>
+ // js.Dictionary.delete(arg)
+ js.JSDelete(js.JSBracketSelect(receiver, arg))
+
+ case ISUNDEF =>
+ // js.isUndefined(arg)
+ js.BinaryOp(js.BinaryOp.===, arg, js.Undefined())
+ case TYPEOF =>
+ // js.typeOf(arg)
+ js.UnaryOp(js.UnaryOp.typeof, arg)
+
+ case OBJPROPS =>
+ // js.Object.properties(arg)
+ genApplyMethod(
+ genLoadModule(RuntimePackageModule),
+ RuntimePackageModule.moduleClass,
+ Runtime_propertiesOf,
+ List(arg))
+ }
+
+ case List(arg1, arg2) =>
+ code match {
+ case DYNUPDATE =>
+ // js.Dynamic.updateDynamic(arg1)(arg2)
+ js.Assign(js.JSBracketSelect(receiver, arg1), arg2)
+
+ case HASPROP =>
+ // js.Object.hasProperty(arg1, arg2)
+ /* Here we have an issue with evaluation order of arg1 and arg2,
+ * since the obvious translation is `arg2 in arg1`, but then
+ * arg2 is evaluated before arg1. Since this is not a commonly
+ * used operator, we don't try to avoid unnessary temp vars, and
+ * simply always evaluate arg1 in a temp before doing the `in`.
+ */
+ val temp = freshLocalIdent()
+ js.Block(
+ js.VarDef(temp, jstpe.AnyType, mutable = false, arg1),
+ js.BinaryOp(js.BinaryOp.in, arg2,
+ js.VarRef(temp, mutable = false)(jstpe.AnyType)))
+ }
+ })
+ }
+
+ /** Gen JS code for a primitive JS call (to a method of a subclass of js.Any)
+ * This is the typed Scala.js to JS bridge feature. Basically it boils
+ * down to calling the method without name mangling. But other aspects
+ * come into play:
+ * * Operator methods are translated to JS operators (not method calls)
+ * * apply is translated as a function call, i.e. o() instead of o.apply()
+ * * Scala varargs are turned into JS varargs (see genPrimitiveJSArgs())
+ * * Getters and parameterless methods are translated as Selects
+ * * Setters are translated to Assigns of Selects
+ */
+ private def genPrimitiveJSCall(tree: Apply, isStat: Boolean): js.Tree = {
+ implicit val pos = tree.pos
+
+ val sym = tree.symbol
+ val Apply(fun @ Select(receiver0, _), args0) = tree
+
+ val funName = sym.unexpandedName.decoded
+ val receiver = genExpr(receiver0)
+ val argArray = genPrimitiveJSArgs(sym, args0)
+
+ // valid only for methods that don't have any varargs
+ lazy val js.JSArrayConstr(args) = argArray
+ lazy val argc = args.length
+
+ def hasExplicitJSEncoding =
+ sym.hasAnnotation(JSNameAnnotation) ||
+ sym.hasAnnotation(JSBracketAccessAnnotation)
+
+ val boxedResult = funName match {
+ case "unary_+" | "unary_-" | "unary_~" | "unary_!" =>
+ assert(argc == 0)
+ js.JSUnaryOp(funName.substring(funName.length-1), receiver)
+
+ case "+" | "-" | "*" | "/" | "%" | "<<" | ">>" | ">>>" |
+ "&" | "|" | "^" | "&&" | "||" | "<" | ">" | "<=" | ">=" =>
+ assert(argc == 1)
+ js.JSBinaryOp(funName, receiver, args.head)
+
+ case "apply" if receiver0.tpe.typeSymbol.isSubClass(JSThisFunctionClass) =>
+ js.JSBracketMethodApply(receiver, js.StringLiteral("call"), args)
+
+ case "apply" if !hasExplicitJSEncoding =>
+ argArray match {
+ case js.JSArrayConstr(args) =>
+ js.JSFunctionApply(receiver, args)
+ case _ =>
+ js.JSBracketMethodApply(
+ receiver, js.StringLiteral("apply"), List(js.Null(), argArray))
+ }
+
+ case _ =>
+ def jsFunName = jsNameOf(sym)
+
+ if (sym.hasFlag(reflect.internal.Flags.DEFAULTPARAM)) {
+ js.UndefinedParam()(toIRType(sym.tpe.resultType))
+ } else if (jsInterop.isJSGetter(sym)) {
+ assert(argc == 0)
+ js.JSBracketSelect(receiver, js.StringLiteral(jsFunName))
+ } else if (jsInterop.isJSSetter(sym)) {
+ assert(argc == 1)
+ js.Assign(
+ js.JSBracketSelect(receiver,
+ js.StringLiteral(jsFunName.stripSuffix("_="))),
+ args.head)
+ } else if (jsInterop.isJSBracketAccess(sym)) {
+ assert(argArray.isInstanceOf[js.JSArrayConstr] && (argc == 1 || argc == 2),
+ s"@JSBracketAccess methods should have 1 or 2 non-varargs arguments")
+ args match {
+ case List(keyArg) =>
+ js.JSBracketSelect(receiver, keyArg)
+ case List(keyArg, valueArg) =>
+ js.Assign(
+ js.JSBracketSelect(receiver, keyArg),
+ valueArg)
+ }
+ } else {
+ argArray match {
+ case js.JSArrayConstr(args) =>
+ js.JSBracketMethodApply(
+ receiver, js.StringLiteral(jsFunName), args)
+ case _ =>
+ genApplyJSMethodWithVarargs(receiver,
+ js.StringLiteral(jsFunName), argArray)
+ }
+ }
+ }
+
+ boxedResult match {
+ case js.UndefinedParam() | js.Assign(_, _) =>
+ boxedResult
+ case _ if isStat =>
+ boxedResult
+ case _ =>
+ fromAny(boxedResult,
+ enteringPhase(currentRun.posterasurePhase)(sym.tpe.resultType))
+ }
+ }
+
+ /** Gen JS code to call a primitive JS method with variadic parameters. */
+ private def genApplyJSMethodWithVarargs(receiver: js.Tree,
+ methodName: js.Tree, argArray: js.Tree)(
+ implicit pos: Position): js.Tree = {
+ // We need to evaluate `receiver` only once
+ val receiverValDef =
+ js.VarDef(freshLocalIdent(), receiver.tpe, mutable = false, receiver)
+ js.Block(
+ receiverValDef,
+ js.JSBracketMethodApply(
+ js.JSBracketSelect(receiverValDef.ref, methodName),
+ js.StringLiteral("apply"),
+ List(receiverValDef.ref, argArray)))
+ }
+
+ /** Gen JS code to instantiate a JS class with variadic parameters. */
+ private def genNewJSWithVarargs(jsClass: js.Tree, argArray: js.Tree)(
+ implicit pos: Position): js.Tree = {
+ genApplyMethod(
+ genLoadModule(RuntimePackageModule),
+ RuntimePackageModule.moduleClass,
+ Runtime_newJSObjectWithVarargs,
+ List(jsClass, argArray))
+ }
+
+ /** Gen JS code for new java.lang.String(...)
+ * Proxies calls to method newString on object
+ * scala.scalajs.runtime.RuntimeString with proper arguments
+ */
+ private def genNewString(tree: Apply): js.Tree = {
+ implicit val pos = tree.pos
+ val Apply(fun @ Select(_, _), args0) = tree
+
+ val ctor = fun.symbol
+ val args = args0 map genExpr
+
+ // Filter members of target module for matching member
+ val compMembers = for {
+ mem <- RuntimeStringModule.tpe.members
+ if mem.name == jsnme.newString && ctor.tpe.matches(mem.tpe)
+ } yield mem
+
+ if (compMembers.isEmpty) {
+ reporter.error(pos,
+ s"""Could not find implementation for constructor of java.lang.String
+ |with type ${ctor.tpe}. Constructors on java.lang.String
+ |are forwarded to the companion object of
+ |scala.scalajs.runtime.RuntimeString""".stripMargin)
+ js.Undefined()
+ } else {
+ assert(compMembers.size == 1,
+ s"""For constructor with type ${ctor.tpe} on java.lang.String,
+ |found multiple companion module members.""".stripMargin)
+
+ // Emit call to companion object
+ genApplyMethod(
+ genLoadModule(RuntimeStringModule),
+ RuntimeStringModule.moduleClass,
+ compMembers.head,
+ args)
+ }
+ }
+
+ /** Gen JS code for calling a method on java.lang.String.
+ *
+ * Forwards call on java.lang.String to the module
+ * scala.scalajs.runtime.RuntimeString.
+ */
+ private def genStringCall(tree: Apply): js.Tree = {
+ implicit val pos = tree.pos
+
+ val sym = tree.symbol
+
+ // Deconstruct tree and create receiver and argument JS expressions
+ val Apply(Select(receiver0, _), args0) = tree
+ val receiver = genExpr(receiver0)
+ val args = args0 map genExpr
+
+ // Emit call to the RuntimeString module
+ val (rtModuleClass, methodIdent) = encodeRTStringMethodSym(sym)
+ genApplyMethod(
+ genLoadModule(rtModuleClass),
+ rtModuleClass,
+ methodIdent,
+ receiver :: args,
+ toIRType(tree.tpe))
+ }
+
+ /** Gen JS code for a new of a raw JS class (subclass of js.Any) */
+ private def genPrimitiveJSNew(tree: Apply): js.Tree = {
+ implicit val pos = tree.pos
+
+ val Apply(fun @ Select(New(tpt), _), args0) = tree
+ val cls = tpt.tpe.typeSymbol
+ val ctor = fun.symbol
+
+ genPrimitiveJSArgs(ctor, args0) match {
+ case js.JSArrayConstr(args) =>
+ if (cls == JSObjectClass && args.isEmpty) js.JSObjectConstr(Nil)
+ else if (cls == JSArrayClass && args.isEmpty) js.JSArrayConstr(Nil)
+ else js.JSNew(genPrimitiveJSClass(cls), args)
+ case argArray =>
+ genNewJSWithVarargs(genPrimitiveJSClass(cls), argArray)
+ }
+ }
+
+ /** Gen JS code representing a JS class (subclass of js.Any) */
+ private def genPrimitiveJSClass(sym: Symbol)(
+ implicit pos: Position): js.Tree = {
+ genGlobalJSObject(sym)
+ }
+
+ /** Gen JS code representing a JS module (var of the global scope) */
+ private def genPrimitiveJSModule(sym: Symbol)(
+ implicit pos: Position): js.Tree = {
+ genGlobalJSObject(sym)
+ }
+
+ /** Gen JS code representing a JS object (class or module) in global scope
+ */
+ private def genGlobalJSObject(sym: Symbol)(
+ implicit pos: Position): js.Tree = {
+ jsNameOf(sym).split('.').foldLeft(genLoadGlobal()) { (memo, chunk) =>
+ js.JSBracketSelect(memo, js.StringLiteral(chunk))
+ }
+ }
+
+ /** Gen actual actual arguments to Scala method call.
+ * Returns a list of the transformed arguments.
+ *
+ * This tries to optimized repeated arguments (varargs) by turning them
+ * into js.WrappedArray instead of Scala wrapped arrays.
+ */
+ private def genActualArgs(sym: Symbol, args: List[Tree])(
+ implicit pos: Position): List[js.Tree] = {
+ val wereRepeated = exitingPhase(currentRun.typerPhase) {
+ sym.tpe.params.map(p => isScalaRepeatedParamType(p.tpe))
+ }
+
+ if (wereRepeated.size > args.size) {
+ // Should not happen, but let's not crash
+ args.map(genExpr)
+ } else {
+ /* Arguments that are in excess compared to the type signature after
+ * erasure are lambda-lifted arguments. They cannot be repeated, hence
+ * the extension to `false`.
+ */
+ for ((arg, wasRepeated) <- args.zipAll(wereRepeated, EmptyTree, false)) yield {
+ if (wasRepeated) {
+ tryGenRepeatedParamAsJSArray(arg, handleNil = false).fold {
+ genExpr(arg)
+ } { argArray =>
+ genNew(WrappedArrayClass, WrappedArray_ctor, List(argArray))
+ }
+ } else {
+ genExpr(arg)
+ }
+ }
+ }
+ }
+
+ /** Gen actual actual arguments to a primitive JS call
+ * This handles repeated arguments (varargs) by turning them into
+ * JS varargs, i.e., by expanding them into normal arguments.
+ *
+ * Returns an only tree which is a JS array of the arguments. In most
+ * cases, it will be a js.JSArrayConstr with the expanded arguments. It will
+ * not if a Seq is passed to a varargs argument with the syntax seq: _*.
+ */
+ private def genPrimitiveJSArgs(sym: Symbol, args: List[Tree])(
+ implicit pos: Position): js.Tree = {
+ val wereRepeated = exitingPhase(currentRun.typerPhase) {
+ for {
+ params <- sym.tpe.paramss
+ param <- params
+ } yield isScalaRepeatedParamType(param.tpe)
+ }
+
+ var reversedParts: List[js.Tree] = Nil
+ var reversedPartUnderConstruction: List[js.Tree] = Nil
+
+ def closeReversedPartUnderConstruction() = {
+ if (!reversedPartUnderConstruction.isEmpty) {
+ val part = reversedPartUnderConstruction.reverse
+ reversedParts ::= js.JSArrayConstr(part)
+ reversedPartUnderConstruction = Nil
+ }
+ }
+
+ val paramTpes = enteringPhase(currentRun.posterasurePhase) {
+ for (param <- sym.tpe.params)
+ yield param.tpe
+ }
+
+ for (((arg, wasRepeated), tpe) <- (args zip wereRepeated) zip paramTpes) {
+ if (wasRepeated) {
+ genPrimitiveJSRepeatedParam(arg) match {
+ case js.JSArrayConstr(jsArgs) =>
+ reversedPartUnderConstruction =
+ jsArgs reverse_::: reversedPartUnderConstruction
+ case jsArgArray =>
+ closeReversedPartUnderConstruction()
+ reversedParts ::= jsArgArray
+ }
+ } else {
+ val unboxedArg = genExpr(arg)
+ val boxedArg = unboxedArg match {
+ case js.UndefinedParam() => unboxedArg
+ case _ => ensureBoxed(unboxedArg, tpe)
+ }
+ reversedPartUnderConstruction ::= boxedArg
+ }
+ }
+ closeReversedPartUnderConstruction()
+
+ // Find js.UndefinedParam at the end of the argument list. No check is
+ // performed whether they may be there, since they will only be placed
+ // where default arguments can be anyway
+ reversedParts = reversedParts match {
+ case Nil => Nil
+ case js.JSArrayConstr(params) :: others =>
+ val nparams =
+ params.reverse.dropWhile(_.isInstanceOf[js.UndefinedParam]).reverse
+ js.JSArrayConstr(nparams) :: others
+ case parts => parts
+ }
+
+ // Find remaining js.UndefinedParam and replace by js.Undefined. This can
+ // happen with named arguments or when multiple argument lists are present
+ reversedParts = reversedParts map {
+ case js.JSArrayConstr(params) =>
+ val nparams = params map {
+ case js.UndefinedParam() => js.Undefined()
+ case param => param
+ }
+ js.JSArrayConstr(nparams)
+ case part => part
+ }
+
+ reversedParts match {
+ case Nil => js.JSArrayConstr(Nil)
+ case List(part) => part
+ case _ =>
+ val partHead :: partTail = reversedParts.reverse
+ js.JSBracketMethodApply(
+ partHead, js.StringLiteral("concat"), partTail)
+ }
+ }
+
+ /** Gen JS code for a repeated param of a primitive JS method
+ * In this case `arg` has type Seq[T] for some T, but the result should
+ * have type js.Array[T]. So this method takes care of the conversion.
+ * It is specialized for the shapes of tree generated by the desugaring
+ * of repeated params in Scala, so that these produce a js.JSArrayConstr.
+ */
+ private def genPrimitiveJSRepeatedParam(arg: Tree): js.Tree = {
+ tryGenRepeatedParamAsJSArray(arg, handleNil = true) getOrElse {
+ /* Fall back to calling runtime.genTraversableOnce2jsArray
+ * to perform the conversion.
+ */
+ implicit val pos = arg.pos
+ genApplyMethod(
+ genLoadModule(RuntimePackageModule),
+ RuntimePackageModule.moduleClass,
+ Runtime_genTraversableOnce2jsArray,
+ List(genExpr(arg)))
+ }
+ }
+
+ /** Try and gen a js.Array for a repeated param (xs: T*).
+ * It is specialized for the shapes of tree generated by the desugaring
+ * of repeated params in Scala, so that these produce a js.JSArrayConstr.
+ * If `arg` does not have the shape of a generated repeated param, this
+ * method returns `None`.
+ */
+ private def tryGenRepeatedParamAsJSArray(arg: Tree,
+ handleNil: Boolean): Option[js.Tree] = {
+ implicit val pos = arg.pos
+
+ // Given a method `def foo(args: T*)`
+ arg match {
+ // foo(arg1, arg2, ..., argN) where N > 0
+ case MaybeAsInstanceOf(WrapArray(
+ MaybeAsInstanceOf(ArrayValue(tpt, elems)))) =>
+ /* Value classes in arrays are already boxed, so no need to use
+ * the type before erasure.
+ */
+ val elemTpe = tpt.tpe
+ Some(js.JSArrayConstr(elems.map(e => ensureBoxed(genExpr(e), elemTpe))))
+
+ // foo()
+ case Select(_, _) if handleNil && arg.symbol == NilModule =>
+ Some(js.JSArrayConstr(Nil))
+
+ // foo(argSeq:_*) - cannot be optimized
+ case _ =>
+ None
+ }
+ }
+
+ object MaybeAsInstanceOf {
+ def unapply(tree: Tree): Some[Tree] = tree match {
+ case Apply(TypeApply(asInstanceOf_? @ Select(base, _), _), _)
+ if asInstanceOf_?.symbol == Object_asInstanceOf =>
+ Some(base)
+ case _ =>
+ Some(tree)
+ }
+ }
+
+ object WrapArray {
+ lazy val isWrapArray: Set[Symbol] = Seq(
+ nme.wrapRefArray,
+ nme.wrapByteArray,
+ nme.wrapShortArray,
+ nme.wrapCharArray,
+ nme.wrapIntArray,
+ nme.wrapLongArray,
+ nme.wrapFloatArray,
+ nme.wrapDoubleArray,
+ nme.wrapBooleanArray,
+ nme.wrapUnitArray,
+ nme.genericWrapArray).map(getMemberMethod(PredefModule, _)).toSet
+
+ def unapply(tree: Apply): Option[Tree] = tree match {
+ case Apply(wrapArray_?, List(wrapped))
+ if isWrapArray(wrapArray_?.symbol) =>
+ Some(wrapped)
+ case _ =>
+ None
+ }
+ }
+
+ // Synthesizers for raw JS functions ---------------------------------------
+
+ /** Try and gen and record JS code for an anonymous function class.
+ *
+ * Returns true if the class could be rewritten that way, false otherwise.
+ *
+ * We make the following assumptions on the form of such classes:
+ * - It is an anonymous function
+ * - Includes being anonymous, final, and having exactly one constructor
+ * - It is not a PartialFunction
+ * - It has no field other than param accessors
+ * - It has exactly one constructor
+ * - It has exactly one non-bridge method apply if it is not specialized,
+ * or a method apply$...$sp and a forwarder apply if it is specialized.
+ * - As a precaution: it is synthetic
+ *
+ * From a class looking like this:
+ *
+ * final class <anon>(outer, capture1, ..., captureM) extends AbstractionFunctionN[...] {
+ * def apply(param1, ..., paramN) = {
+ * <body>
+ * }
+ * }
+ * new <anon>(o, c1, ..., cM)
+ *
+ * we generate a function maker that emits:
+ *
+ * lambda<o, c1, ..., cM>[notype](
+ * outer, capture1, ..., captureM, param1, ..., paramN) {
+ * <body>
+ * }
+ *
+ * so that, at instantiation point, we can write:
+ *
+ * new AnonFunctionN(functionMaker(this, captured1, ..., capturedM))
+ *
+ * Trickier things apply when the function is specialized.
+ */
+ private def tryGenAndRecordAnonFunctionClass(cd: ClassDef): Boolean = {
+ implicit val pos = cd.pos
+ val sym = cd.symbol
+ assert(sym.isAnonymousFunction,
+ s"tryGenAndRecordAnonFunctionClass called with non-anonymous function $cd")
+
+ withScopedVars(
+ currentClassInfoBuilder := new ClassInfoBuilder(sym.asClass),
+ currentClassSym := sym
+ ) {
+ val (functionMakerBase, arity) =
+ tryGenAndRecordAnonFunctionClassGeneric(cd) { msg =>
+ return false
+ }
+ val functionMaker = { capturedArgs: List[js.Tree] =>
+ JSFunctionToScala(functionMakerBase(capturedArgs), arity)
+ }
+
+ translatedAnonFunctions +=
+ sym -> (functionMaker, currentClassInfoBuilder.get)
+ }
+ true
+ }
+
+ /** Constructor and extractor object for a tree that converts a JavaScript
+ * function into a Scala function.
+ */
+ private object JSFunctionToScala {
+ private val AnonFunPrefScala =
+ "scala.scalajs.runtime.AnonFunction"
+ private val AnonFunPrefJS =
+ "sjsr_AnonFunction"
+
+ def apply(jsFunction: js.Tree, arity: Int)(
+ implicit pos: Position): js.Tree = {
+ val clsSym = getRequiredClass(AnonFunPrefScala + arity)
+ val ctor = clsSym.tpe.member(nme.CONSTRUCTOR)
+ genNew(clsSym, ctor, List(jsFunction))
+ }
+
+ def unapply(tree: js.New): Option[(js.Tree, Int)] = tree match {
+ case js.New(jstpe.ClassType(wrapperName), _, List(fun))
+ if wrapperName.startsWith(AnonFunPrefJS) =>
+ val arityStr = wrapperName.substring(AnonFunPrefJS.length)
+ try {
+ Some((fun, arityStr.toInt))
+ } catch {
+ case e: NumberFormatException => None
+ }
+
+ case _ =>
+ None
+ }
+ }
+
+ /** Gen and record JS code for a raw JS function class.
+ *
+ * This is called when emitting a ClassDef that represents an anonymous
+ * class extending `js.FunctionN`. These are generated by the SAM
+ * synthesizer when the target type is a `js.FunctionN`. Since JS
+ * functions are not classes, we deconstruct the ClassDef, then
+ * reconstruct it to be a genuine Closure.
+ *
+ * Compared to `tryGenAndRecordAnonFunctionClass()`, this function must
+ * always succeed, because we really cannot afford keeping them as
+ * anonymous classes. The good news is that it can do so, because the
+ * body of SAM lambdas is hoisted in the enclosing class. Hence, the
+ * apply() method is just a forwarder to calling that hoisted method.
+ *
+ * From a class looking like this:
+ *
+ * final class <anon>(outer, capture1, ..., captureM) extends js.FunctionN[...] {
+ * def apply(param1, ..., paramN) = {
+ * outer.lambdaImpl(param1, ..., paramN, capture1, ..., captureM)
+ * }
+ * }
+ * new <anon>(o, c1, ..., cM)
+ *
+ * we generate a function maker that emits:
+ *
+ * lambda<o, c1, ..., cM>[notype](
+ * outer, capture1, ..., captureM, param1, ..., paramN) {
+ * outer.lambdaImpl(param1, ..., paramN, capture1, ..., captureM)
+ * }
+ *
+ * The function maker is recorded in `translatedAnonFunctions` to be
+ * fetched later by the translation for New.
+ */
+ def genAndRecordRawJSFunctionClass(cd: ClassDef): Unit = {
+ val sym = cd.symbol
+ assert(isRawJSFunctionDef(sym),
+ s"genAndRecordRawJSFunctionClass called with non-JS function $cd")
+
+ withScopedVars(
+ currentClassInfoBuilder := new ClassInfoBuilder(sym.asClass),
+ currentClassSym := sym
+ ) {
+ val (functionMaker, _) =
+ tryGenAndRecordAnonFunctionClassGeneric(cd) { msg =>
+ abort(s"Could not generate raw function maker for JS function: $msg")
+ }
+
+ translatedAnonFunctions +=
+ sym -> (functionMaker, currentClassInfoBuilder.get)
+ }
+ }
+
+ /** Code common to tryGenAndRecordAnonFunctionClass and
+ * genAndRecordRawJSFunctionClass.
+ */
+ private def tryGenAndRecordAnonFunctionClassGeneric(cd: ClassDef)(
+ fail: (=> String) => Nothing): (List[js.Tree] => js.Tree, Int) = {
+ implicit val pos = cd.pos
+ val sym = cd.symbol
+
+ // First checks
+
+ if (sym.isSubClass(PartialFunctionClass))
+ fail(s"Cannot rewrite PartialFunction $cd")
+ if (instantiatedAnonFunctions contains sym) {
+ // when the ordering we're given is evil (it happens!)
+ fail(s"Abort function rewrite because it was already instantiated: $cd")
+ }
+
+ // First step: find the apply method def, and collect param accessors
+
+ var paramAccessors: List[Symbol] = Nil
+ var applyDef: DefDef = null
+
+ def gen(tree: Tree): Unit = {
+ tree match {
+ case EmptyTree => ()
+ case Template(_, _, body) => body foreach gen
+ case vd @ ValDef(mods, name, tpt, rhs) =>
+ val fsym = vd.symbol
+ if (!fsym.isParamAccessor)
+ fail(s"Found field $fsym which is not a param accessor in anon function $cd")
+
+ if (fsym.isPrivate) {
+ paramAccessors ::= fsym
+ } else {
+ // Uh oh ... an inner something will try to access my fields
+ fail(s"Found a non-private field $fsym in $cd")
+ }
+ case dd: DefDef =>
+ val ddsym = dd.symbol
+ if (ddsym.isClassConstructor) {
+ if (!ddsym.isPrimaryConstructor)
+ fail(s"Non-primary constructor $ddsym in anon function $cd")
+ } else {
+ val name = dd.name.toString
+ if (name == "apply" || (ddsym.isSpecialized && name.startsWith("apply$"))) {
+ if ((applyDef eq null) || ddsym.isSpecialized)
+ applyDef = dd
+ } else {
+ // Found a method we cannot encode in the rewriting
+ fail(s"Found a non-apply method $ddsym in $cd")
+ }
+ }
+ case _ =>
+ fail("Illegal tree in gen of genAndRecordAnonFunctionClass(): " + tree)
+ }
+ }
+ gen(cd.impl)
+ paramAccessors = paramAccessors.reverse // preserve definition order
+
+ if (applyDef eq null)
+ fail(s"Did not find any apply method in anon function $cd")
+
+ withNewLocalNameScope {
+ // Second step: build the list of useful constructor parameters
+
+ val ctorParams = sym.primaryConstructor.tpe.params
+
+ if (paramAccessors.size != ctorParams.size &&
+ !(paramAccessors.size == ctorParams.size-1 &&
+ ctorParams.head.unexpandedName == jsnme.arg_outer)) {
+ fail(
+ s"Have param accessors $paramAccessors but "+
+ s"ctor params $ctorParams in anon function $cd")
+ }
+
+ val hasUnusedOuterCtorParam = paramAccessors.size != ctorParams.size
+ val usedCtorParams =
+ if (hasUnusedOuterCtorParam) ctorParams.tail
+ else ctorParams
+ val ctorParamDefs = usedCtorParams map { p =>
+ // in the apply method's context
+ js.ParamDef(encodeLocalSym(p)(p.pos), toIRType(p.tpe),
+ mutable = false)(p.pos)
+ }
+
+ // Third step: emit the body of the apply method def
+
+ val (applyMethod, methodInfoBuilder) = withScopedVars(
+ paramAccessorLocals := (paramAccessors zip ctorParamDefs).toMap,
+ tryingToGenMethodAsJSFunction := true
+ ) {
+ try {
+ genMethodWithInfoBuilder(applyDef).getOrElse(
+ abort(s"Oops, $applyDef did not produce a method"))
+ } catch {
+ case e: CancelGenMethodAsJSFunction =>
+ fail(e.getMessage)
+ }
+ }
+
+ withScopedVars(
+ currentMethodInfoBuilder := methodInfoBuilder
+ ) {
+ // Fourth step: patch the body to unbox parameters and box result
+
+ val js.MethodDef(_, params, _, body) = applyMethod
+ val (patchedParams, patchedBody) =
+ patchFunBodyWithBoxes(applyDef.symbol, params, body)
+
+ // Fifth step: build the function maker
+
+ val isThisFunction = JSThisFunctionClasses.exists(sym isSubClass _)
+ assert(!isThisFunction || patchedParams.nonEmpty,
+ s"Empty param list in ThisFunction: $cd")
+
+ val functionMaker = { capturedArgs0: List[js.Tree] =>
+ val capturedArgs =
+ if (hasUnusedOuterCtorParam) capturedArgs0.tail
+ else capturedArgs0
+ assert(capturedArgs.size == ctorParamDefs.size)
+
+ if (isThisFunction) {
+ val thisParam :: actualParams = patchedParams
+ js.Closure(
+ ctorParamDefs,
+ actualParams,
+ js.Block(
+ js.VarDef(thisParam.name, thisParam.ptpe, mutable = false,
+ js.This()(thisParam.ptpe)(thisParam.pos))(thisParam.pos),
+ patchedBody),
+ capturedArgs)
+ } else {
+ js.Closure(ctorParamDefs, patchedParams, patchedBody, capturedArgs)
+ }
+ }
+
+ val arity = params.size
+
+ (functionMaker, arity)
+ }
+ }
+ }
+
+ /** Generate JS code for an anonymous function
+ *
+ * Anonymous functions survive until the backend only under
+ * -Ydelambdafy:method
+ * and when they do, their body is always of the form
+ * EnclosingClass.this.someMethod(arg1, ..., argN, capture1, ..., captureM)
+ * where argI are the formal arguments of the lambda, and captureI are
+ * local variables or the enclosing def.
+ *
+ * We translate them by instantiating scala.scalajs.runtime.AnonFunctionN
+ * with a JS closure:
+ *
+ * new ScalaJS.c.sjsr_AnonFunctionN().init___xyz(
+ * lambda<this, capture1, ..., captureM>(
+ * _this, capture1, ..., captureM, arg1, ..., argN) {
+ * _this.someMethod(arg1, ..., argN, capture1, ..., captureM)
+ * }
+ * )
+ *
+ * In addition, input params are unboxed before use, and the result of
+ * someMethod() is boxed back.
+ */
+ private def genAnonFunction(originalFunction: Function): js.Tree = {
+ implicit val pos = originalFunction.pos
+ val Function(paramTrees, Apply(
+ targetTree @ Select(receiver, _), allArgs0)) = originalFunction
+
+ val target = targetTree.symbol
+ val params = paramTrees.map(_.symbol)
+
+ val allArgs = allArgs0 map genExpr
+
+ val formalArgs = params map { p =>
+ js.ParamDef(encodeLocalSym(p)(p.pos), toIRType(p.tpe),
+ mutable = false)(p.pos)
+ }
+
+ val isInImplClass = target.owner.isImplClass
+
+ def makeCaptures(actualCaptures: List[js.Tree]) = {
+ (actualCaptures map { c => (c: @unchecked) match {
+ case js.VarRef(ident, _) =>
+ (js.ParamDef(ident, c.tpe, mutable = false)(c.pos),
+ js.VarRef(ident, false)(c.tpe)(c.pos))
+ }}).unzip
+ }
+
+ val (allFormalCaptures, body, allActualCaptures) = if (!isInImplClass) {
+ val thisActualCapture = genExpr(receiver)
+ val thisFormalCapture = js.ParamDef(
+ freshLocalIdent("this")(receiver.pos),
+ thisActualCapture.tpe, mutable = false)(receiver.pos)
+ val thisCaptureArg = thisFormalCapture.ref
+ val (actualArgs, actualCaptures) = allArgs.splitAt(formalArgs.size)
+ val (formalCaptures, captureArgs) = makeCaptures(actualCaptures)
+ val body = genApplyMethod(thisCaptureArg, receiver.tpe, target,
+ actualArgs ::: captureArgs)
+
+ (thisFormalCapture :: formalCaptures,
+ body, thisActualCapture :: actualCaptures)
+ } else {
+ val (thisActualCapture :: actualArgs, actualCaptures) =
+ allArgs.splitAt(formalArgs.size+1)
+ val (thisFormalCapture :: formalCaptures, thisCaptureArg :: captureArgs) =
+ makeCaptures(thisActualCapture :: actualCaptures)
+ val body = genTraitImplApply(target,
+ thisCaptureArg :: actualArgs ::: captureArgs)
+
+ (thisFormalCapture :: formalCaptures,
+ body, thisActualCapture :: actualCaptures)
+ }
+
+ val (patchedFormalArgs, patchedBody) =
+ patchFunBodyWithBoxes(target, formalArgs, body)
+ val closure = js.Closure(
+ allFormalCaptures,
+ patchedFormalArgs,
+ patchedBody,
+ allActualCaptures)
+
+ JSFunctionToScala(closure, params.size)
+ }
+
+ private def patchFunBodyWithBoxes(methodSym: Symbol,
+ params: List[js.ParamDef], body: js.Tree)(
+ implicit pos: Position): (List[js.ParamDef], js.Tree) = {
+ val methodType = enteringPhase(currentRun.posterasurePhase)(methodSym.tpe)
+
+ val (patchedParams, paramsLocal) = (for {
+ (param, paramSym) <- params zip methodType.params
+ } yield {
+ val paramTpe = enteringPhase(currentRun.posterasurePhase)(paramSym.tpe)
+ val paramName = param.name
+ val js.Ident(name, origName) = paramName
+ val newOrigName = origName.getOrElse(name)
+ val newNameIdent = freshLocalIdent(newOrigName)(paramName.pos)
+ val patchedParam = js.ParamDef(newNameIdent, jstpe.AnyType,
+ mutable = false)(param.pos)
+ val paramLocal = js.VarDef(paramName, param.ptpe, mutable = false,
+ fromAny(patchedParam.ref, paramTpe))
+ (patchedParam, paramLocal)
+ }).unzip
+
+ val patchedBody = js.Block(
+ paramsLocal :+ ensureBoxed(body, methodType.resultType))
+
+ (patchedParams, patchedBody)
+ }
+
+ // Utilities ---------------------------------------------------------------
+
+ /** Generate a literal "zero" for the requested type */
+ def genZeroOf(tpe: Type)(implicit pos: Position): js.Tree = toTypeKind(tpe) match {
+ case VOID => abort("Cannot call genZeroOf(VOID)")
+ case BOOL => js.BooleanLiteral(false)
+ case LONG => js.LongLiteral(0L)
+ case INT(_) => js.IntLiteral(0)
+ case FloatKind => js.FloatLiteral(0.0f)
+ case DoubleKind => js.DoubleLiteral(0.0)
+ case _ => js.Null()
+ }
+
+ /** Generate loading of a module value
+ * Can be given either the module symbol, or its module class symbol.
+ */
+ def genLoadModule(sym0: Symbol)(implicit pos: Position): js.Tree = {
+ require(sym0.isModuleOrModuleClass,
+ "genLoadModule called with non-module symbol: " + sym0)
+ val sym1 = if (sym0.isModule) sym0.moduleClass else sym0
+ val sym = // redirect all static methods of String to RuntimeString
+ if (sym1 == StringModule) RuntimeStringModule.moduleClass
+ else sym1
+
+ val isGlobalScope = sym.tpe.typeSymbol isSubClass JSGlobalScopeClass
+
+ if (isGlobalScope) genLoadGlobal()
+ else if (isRawJSType(sym.tpe)) genPrimitiveJSModule(sym)
+ else {
+ if (!foreignIsImplClass(sym))
+ currentMethodInfoBuilder.accessesModule(sym)
+ js.LoadModule(jstpe.ClassType(encodeClassFullName(sym)))
+ }
+ }
+
+ /** Gen JS code to load the global scope. */
+ private def genLoadGlobal()(implicit pos: Position): js.Tree =
+ js.JSBracketSelect(js.JSEnvInfo(), js.StringLiteral("global"))
+
+ /** Generate access to a static member */
+ private def genStaticMember(sym: Symbol)(implicit pos: Position) = {
+ /* Actually, there is no static member in Scala.js. If we come here, that
+ * is because we found the symbol in a Java-emitted .class in the
+ * classpath. But the corresponding implementation in Scala.js will
+ * actually be a val in the companion module.
+ * We cannot use the .class files produced by our reimplementations of
+ * these classes (in which the symbol would be a Scala accessor) because
+ * that crashes the rest of scalac (at least for some choice symbols).
+ * Hence we cheat here.
+ */
+ import scalaPrimitives._
+ import jsPrimitives._
+ if (isPrimitive(sym)) {
+ getPrimitive(sym) match {
+ case UNITVAL => js.Undefined()
+ case UNITTYPE => genClassConstant(UnitTpe)
+ }
+ } else {
+ val instance = genLoadModule(sym.owner)
+ val method = encodeStaticMemberSym(sym)
+ currentMethodInfoBuilder.callsMethod(sym.owner, method)
+ js.Apply(instance, method, Nil)(toIRType(sym.tpe))
+ }
+ }
+
+ /** Generate a Class[_] value (e.g. coming from classOf[T]) */
+ private def genClassConstant(tpe: Type)(implicit pos: Position): js.Tree = {
+ val refType = toReferenceType(tpe)
+ currentMethodInfoBuilder.accessesClassData(refType)
+ js.ClassOf(refType)
+ }
+ }
+
+ /** Tests whether the given type represents a raw JavaScript type,
+ * i.e., whether it extends scala.scalajs.js.Any.
+ */
+ def isRawJSType(tpe: Type): Boolean =
+ tpe.typeSymbol.annotations.find(_.tpe =:= RawJSTypeAnnot.tpe).isDefined
+
+ /** Test whether `sym` is the symbol of a raw JS function definition */
+ private def isRawJSFunctionDef(sym: Symbol): Boolean =
+ sym.isAnonymousClass && AllJSFunctionClasses.exists(sym isSubClass _)
+
+ private def isRawJSCtorDefaultParam(sym: Symbol) = {
+ sym.hasFlag(reflect.internal.Flags.DEFAULTPARAM) &&
+ sym.owner.isModuleClass &&
+ isRawJSType(patchedLinkedClassOfClass(sym.owner).tpe) &&
+ nme.defaultGetterToMethod(sym.name) == nme.CONSTRUCTOR
+ }
+
+ private def patchedLinkedClassOfClass(sym: Symbol): Symbol = {
+ /* Work around a bug of scalac with linkedClassOfClass where package
+ * objects are involved (the companion class would somehow exist twice
+ * in the scope, making an assertion fail in Symbol.suchThat).
+ * Basically this inlines linkedClassOfClass up to companionClass,
+ * then replaces the `suchThat` by a `filter` and `head`.
+ */
+ val flatOwnerInfo = {
+ // inline Symbol.flatOwnerInfo because it is protected
+ if (sym.needsFlatClasses)
+ sym.info
+ sym.owner.rawInfo
+ }
+ val result = flatOwnerInfo.decl(sym.name).filter(_ isCoDefinedWith sym)
+ if (!result.isOverloaded) result
+ else result.alternatives.head
+ }
+
+ private def isStringType(tpe: Type): Boolean =
+ tpe.typeSymbol == StringClass
+
+ private def isLongType(tpe: Type): Boolean =
+ tpe.typeSymbol == LongClass
+
+ private lazy val BoxedBooleanClass = boxedClass(BooleanClass)
+ private lazy val BoxedByteClass = boxedClass(ByteClass)
+ private lazy val BoxedShortClass = boxedClass(ShortClass)
+ private lazy val BoxedIntClass = boxedClass(IntClass)
+ private lazy val BoxedLongClass = boxedClass(LongClass)
+ private lazy val BoxedFloatClass = boxedClass(FloatClass)
+ private lazy val BoxedDoubleClass = boxedClass(DoubleClass)
+
+ private lazy val NumberClass = requiredClass[java.lang.Number]
+
+ private lazy val HijackedNumberClasses =
+ Seq(BoxedByteClass, BoxedShortClass, BoxedIntClass, BoxedLongClass,
+ BoxedFloatClass, BoxedDoubleClass)
+ private lazy val HijackedBoxedClasses =
+ Seq(BoxedUnitClass, BoxedBooleanClass) ++ HijackedNumberClasses
+
+ protected lazy val isHijackedBoxedClass: Set[Symbol] =
+ HijackedBoxedClasses.toSet
+
+ private lazy val InlineAnnotationClass = requiredClass[scala.inline]
+
+ private def isMaybeJavaScriptException(tpe: Type) =
+ JavaScriptExceptionClass isSubClass tpe.typeSymbol
+
+ /** Get JS name of Symbol if it was specified with JSName annotation, or
+ * infers a default from the Scala name. */
+ def jsNameOf(sym: Symbol): String =
+ sym.getAnnotation(JSNameAnnotation).flatMap(_.stringArg(0)).getOrElse(
+ sym.unexpandedName.decoded)
+
+ def isStaticModule(sym: Symbol): Boolean =
+ sym.isModuleClass && !sym.isImplClass && !sym.isLifted
+}
diff --git a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/GenJSExports.scala b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/GenJSExports.scala
new file mode 100644
index 0000000..92dc26b
--- /dev/null
+++ b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/GenJSExports.scala
@@ -0,0 +1,751 @@
+/* Scala.js compiler
+ * Copyright 2013 LAMP/EPFL
+ * @author Sébastien Doeraene
+ */
+
+package scala.scalajs.compiler
+
+import scala.collection.mutable
+
+import scala.tools.nsc._
+import scala.math.PartialOrdering
+import scala.reflect.internal.Flags
+
+import scala.scalajs.ir
+import ir.{Trees => js, Types => jstpe}
+
+import util.ScopedVar
+import ScopedVar.withScopedVars
+
+/** Generation of exports for JavaScript
+ *
+ * @author Sébastien Doeraene
+ */
+trait GenJSExports extends SubComponent { self: GenJSCode =>
+ import global._
+ import jsAddons._
+ import definitions._
+ import jsDefinitions._
+
+ trait JSExportsPhase { this: JSCodePhase =>
+
+ /**
+ * Generate exporter methods for a class
+ * @param classSym symbol of class we export for
+ * @param decldExports symbols exporter methods that have been encountered in
+ * the class' tree. This is not the same as classSym.info.delcs since
+ * inherited concrete methods from traits should be in this param, too
+ */
+ def genMemberExports(
+ classSym: Symbol,
+ decldExports: List[Symbol]): List[js.Tree] = {
+
+ val newlyDecldExports = decldExports.filterNot { isOverridingExport _ }
+ val newlyDecldExportNames =
+ newlyDecldExports.map(_.name.toTermName).toList.distinct
+
+ newlyDecldExportNames map { genMemberExport(classSym, _) }
+ }
+
+ def genConstructorExports(classSym: Symbol): List[js.ConstructorExportDef] = {
+ val constructors = classSym.tpe.member(nme.CONSTRUCTOR).alternatives
+
+ // Generate exports from constructors and their annotations
+ val ctorExports = for {
+ ctor <- constructors
+ exp <- jsInterop.exportsOf(ctor)
+ } yield (exp, ctor)
+
+ val exports = for {
+ (jsName, specs) <- ctorExports.groupBy(_._1.jsName) // group by exported name
+ } yield {
+ val (namedExports, normalExports) = specs.partition(_._1.isNamed)
+
+ val normalCtors = normalExports.map(s => ExportedSymbol(s._2))
+ val namedCtors = for {
+ (exp, ctor) <- namedExports
+ } yield {
+ implicit val pos = exp.pos
+ ExportedBody(List(JSAnyTpe),
+ genNamedExporterBody(ctor, genFormalArg(1).ref),
+ nme.CONSTRUCTOR.toString, pos)
+ }
+
+ val ctors = normalCtors ++ namedCtors
+
+ implicit val pos = ctors.head.pos
+
+ val js.MethodDef(_, args, _, body) =
+ withNewLocalNameScope(genExportMethod(ctors, jsName))
+
+ js.ConstructorExportDef(jsName, args, body)
+ }
+
+ exports.toList
+ }
+
+ def genModuleAccessorExports(classSym: Symbol): List[js.ModuleExportDef] = {
+ for {
+ exp <- jsInterop.exportsOf(classSym)
+ } yield {
+ implicit val pos = exp.pos
+
+ if (exp.isNamed)
+ reporter.error(pos, "You may not use @JSNamedExport on an object")
+
+ js.ModuleExportDef(exp.jsName)
+ }
+ }
+
+ /** Generate the exporter proxy for a named export */
+ def genNamedExporterDef(dd: DefDef): js.MethodDef = {
+ implicit val pos = dd.pos
+
+ val sym = dd.symbol
+
+ val Block(Apply(fun, _) :: Nil, _) = dd.rhs
+ val trgSym = fun.symbol
+
+ val inArg =
+ js.ParamDef(js.Ident("namedParams"), jstpe.AnyType, mutable = false)
+ val inArgRef = inArg.ref
+
+ val methodIdent = encodeMethodSym(sym)
+
+ withScopedVars(
+ currentMethodInfoBuilder :=
+ currentClassInfoBuilder.addMethod(methodIdent.name)
+ ) {
+ js.MethodDef(methodIdent, List(inArg), toIRType(sym.tpe.resultType),
+ genNamedExporterBody(trgSym, inArg.ref))(None)
+ }
+ }
+
+ private def genNamedExporterBody(trgSym: Symbol, inArg: js.Tree)(
+ implicit pos: Position) = {
+
+ if (hasRepeatedParam(trgSym)) {
+ reporter.error(pos,
+ "You may not name-export a method with a *-parameter")
+ }
+
+ val jsArgs = for {
+ (pSym, index) <- trgSym.info.params.zipWithIndex
+ } yield {
+ val rhs = js.JSBracketSelect(inArg,
+ js.StringLiteral(pSym.name.decoded))
+ js.VarDef(js.Ident("namedArg$" + index), jstpe.AnyType,
+ mutable = false, rhs = rhs)
+ }
+
+ val jsArgRefs = jsArgs.map(_.ref)
+
+ // Generate JS code to prepare arguments (default getters and unboxes)
+ val jsArgPrep = genPrepareArgs(jsArgRefs, trgSym)
+ val jsResult = genResult(trgSym, jsArgPrep.map(_.ref))
+
+ js.Block(jsArgs ++ jsArgPrep :+ jsResult)
+ }
+
+ private def genMemberExport(classSym: Symbol, name: TermName): js.Tree = {
+ val alts = classSym.info.member(name).alternatives
+
+ assert(!alts.isEmpty,
+ s"Ended up with no alternatives for ${classSym.fullName}::$name. " +
+ s"Original set was ${alts} with types ${alts.map(_.tpe)}")
+
+ val (jsName, isProp) = jsInterop.jsExportInfo(name)
+
+ // Check if we have a conflicting export of the other kind
+ val conflicting =
+ classSym.info.member(jsInterop.scalaExportName(jsName, !isProp))
+
+ if (conflicting != NoSymbol) {
+ val kind = if (isProp) "property" else "method"
+ val alts = conflicting.alternatives
+
+ reporter.error(alts.head.pos,
+ s"Exported $kind $jsName conflicts with ${alts.head.fullName}")
+ }
+
+ withNewLocalNameScope {
+ if (isProp)
+ genExportProperty(alts, jsName)
+ else
+ genExportMethod(alts.map(ExportedSymbol), jsName)
+ }
+ }
+
+ private def genExportProperty(alts: List[Symbol], jsName: String) = {
+ assert(!alts.isEmpty)
+ implicit val pos = alts.head.pos
+
+ // Separate getters and setters. Somehow isJSGetter doesn't work here. Hence
+ // we just check the parameter list length.
+ val (getter, setters) = alts.partition(_.tpe.params.isEmpty)
+
+ // if we have more than one getter, something went horribly wrong
+ assert(getter.size <= 1,
+ s"Found more than one getter to export for name ${jsName}.")
+
+ val getTree =
+ if (getter.isEmpty) js.EmptyTree
+ else genApplyForSym(getter.head)
+
+ val setTree =
+ if (setters.isEmpty) js.EmptyTree
+ else genExportSameArgc(setters.map(ExportedSymbol), 0) // we only have 1 argument
+
+ js.PropertyDef(js.StringLiteral(jsName), getTree, genFormalArg(1), setTree)
+ }
+
+ /** generates the exporter function (i.e. exporter for non-properties) for
+ * a given name */
+ private def genExportMethod(alts0: List[Exported], jsName: String) = {
+ assert(alts0.nonEmpty,
+ "need at least one alternative to generate exporter method")
+
+ implicit val pos = alts0.head.pos
+
+ val alts = {
+ // toString() is always exported. We might need to add it here
+ // to get correct overloading.
+ if (jsName == "toString" && alts0.forall(_.params.nonEmpty))
+ ExportedSymbol(Object_toString) :: alts0
+ else
+ alts0
+ }
+
+ // Factor out methods with variable argument lists. Note that they can
+ // only be at the end of the lists as enforced by PrepJSExports
+ val (varArgMeths, normalMeths) = alts.partition(_.hasRepeatedParam)
+
+ // Highest non-repeated argument count
+ val maxArgc = (
+ // We have argc - 1, since a repeated parameter list may also be empty
+ // (unlike a normal parameter)
+ varArgMeths.map(_.params.size - 1) ++
+ normalMeths.map(_.params.size)
+ ).max
+
+ val formalArgs = genFormalArgs(maxArgc)
+
+ // Calculates possible arg counts for normal method
+ def argCounts(ex: Exported) = ex match {
+ case ExportedSymbol(sym) =>
+ val params = sym.tpe.params
+ // Find default param
+ val dParam = params.indexWhere { _.hasFlag(Flags.DEFAULTPARAM) }
+ if (dParam == -1) Seq(params.size)
+ else dParam to params.size
+ case ex: ExportedBody =>
+ List(ex.params.size)
+ }
+
+ // Generate tuples (argc, method)
+ val methodArgCounts = {
+ // Normal methods
+ for {
+ method <- normalMeths
+ argc <- argCounts(method)
+ } yield (argc, method)
+ } ++ {
+ // Repeated parameter methods
+ for {
+ method <- varArgMeths
+ argc <- method.params.size - 1 to maxArgc
+ } yield (argc, method)
+ }
+
+ // Create a map: argCount -> methods (methods may appear multiple times)
+ val methodByArgCount =
+ methodArgCounts.groupBy(_._1).mapValues(_.map(_._2).toSet)
+
+ // Create tuples: (methods, argCounts). This will be the cases we generate
+ val caseDefinitions =
+ methodByArgCount.groupBy(_._2).mapValues(_.keySet)
+
+ // Verify stuff about caseDefinitions
+ assert({
+ val argcs = caseDefinitions.values.flatten.toList
+ argcs == argcs.distinct &&
+ argcs.forall(_ <= maxArgc)
+ }, "every argc should appear only once and be lower than max")
+
+ // Generate a case block for each (methods, argCounts) tuple
+ val cases = for {
+ (methods, argcs) <- caseDefinitions
+ if methods.nonEmpty && argcs.nonEmpty
+
+ // exclude default case we're generating anyways for varargs
+ if methods != varArgMeths.toSet
+
+ // body of case to disambiguates methods with current count
+ caseBody =
+ genExportSameArgc(methods.toList, 0, Some(argcs.min))
+
+ // argc in reverse order
+ argcList = argcs.toList.sortBy(- _)
+ } yield (argcList.map(js.IntLiteral(_)), caseBody)
+
+ val hasVarArg = varArgMeths.nonEmpty
+
+ def defaultCase = {
+ if (!hasVarArg)
+ genThrowTypeError()
+ else
+ genExportSameArgc(varArgMeths, 0)
+ }
+
+ val body = {
+ if (cases.isEmpty)
+ defaultCase
+ else if (cases.size == 1 && !hasVarArg)
+ cases.head._2
+ else {
+ js.Match(
+ js.Unbox(js.JSBracketSelect(
+ js.VarRef(js.Ident("arguments"), false)(jstpe.AnyType),
+ js.StringLiteral("length")),
+ 'I'),
+ cases.toList, defaultCase)(jstpe.AnyType)
+ }
+ }
+
+ js.MethodDef(js.StringLiteral(jsName), formalArgs, jstpe.AnyType, body)(None)
+ }
+
+ /**
+ * Resolve method calls to [[alts]] while assuming they have the same
+ * parameter count.
+ * @param alts Alternative methods
+ * @param paramIndex Index where to start disambiguation
+ * @param maxArgc only use that many arguments
+ */
+ private def genExportSameArgc(alts: List[Exported],
+ paramIndex: Int, maxArgc: Option[Int] = None): js.Tree = {
+
+ implicit val pos = alts.head.pos
+
+ if (alts.size == 1)
+ alts.head.body
+ else if (maxArgc.exists(_ <= paramIndex) ||
+ !alts.exists(_.params.size > paramIndex)) {
+ // We reach here in three cases:
+ // 1. The parameter list has been exhausted
+ // 2. The optional argument count restriction has triggered
+ // 3. We only have (more than once) repeated parameters left
+ // Therefore, we should fail
+ reporter.error(pos,
+ s"""Cannot disambiguate overloads for exported method ${alts.head.name} with types
+ | ${alts.map(_.typeInfo).mkString("\n ")}""".stripMargin)
+ js.Undefined()
+ } else {
+
+ val altsByTypeTest = groupByWithoutHashCode(alts) {
+ case ExportedSymbol(alt) =>
+ // get parameter type while resolving repeated params
+ val tpe = enteringPhase(currentRun.uncurryPhase) {
+ val ps = alt.paramss.flatten
+ if (ps.size <= paramIndex || isRepeated(ps(paramIndex))) {
+ assert(isRepeated(ps.last))
+ repeatedToSingle(ps.last.tpe)
+ } else {
+ enteringPhase(currentRun.posterasurePhase) {
+ ps(paramIndex).tpe
+ }
+ }
+ }
+
+ typeTestForTpe(tpe)
+
+ case ex: ExportedBody =>
+ typeTestForTpe(ex.params(paramIndex))
+ }
+
+ if (altsByTypeTest.size == 1) {
+ // Testing this parameter is not doing any us good
+ genExportSameArgc(alts, paramIndex+1, maxArgc)
+ } else {
+ // Sort them so that, e.g., isInstanceOf[String]
+ // comes before isInstanceOf[Object]
+ val sortedAltsByTypeTest = topoSortDistinctsBy(
+ altsByTypeTest)(_._1)(RTTypeTest.Ordering)
+
+ val defaultCase = genThrowTypeError()
+
+ sortedAltsByTypeTest.foldRight[js.Tree](defaultCase) { (elem, elsep) =>
+ val (typeTest, subAlts) = elem
+ implicit val pos = subAlts.head.pos
+
+ val param = genFormalArg(paramIndex+1)
+ val genSubAlts = genExportSameArgc(subAlts, paramIndex+1, maxArgc)
+
+ def hasDefaultParam = subAlts.exists {
+ case ExportedSymbol(p) =>
+ val params = p.tpe.params
+ params.size > paramIndex &&
+ params(paramIndex).hasFlag(Flags.DEFAULTPARAM)
+ case _: ExportedBody => false
+ }
+
+ val optCond = typeTest match {
+ case HijackedTypeTest(boxedClassName, _) =>
+ Some(js.IsInstanceOf(param.ref, jstpe.ClassType(boxedClassName)))
+
+ case InstanceOfTypeTest(tpe) =>
+ Some(genIsInstanceOf(param.ref, tpe))
+
+ case NoTypeTest =>
+ None
+ }
+
+ optCond.fold[js.Tree] {
+ genSubAlts // note: elsep is discarded, obviously
+ } { cond =>
+ val condOrUndef = if (!hasDefaultParam) cond else {
+ js.If(cond, js.BooleanLiteral(true),
+ js.BinaryOp(js.BinaryOp.===, param.ref, js.Undefined()))(
+ jstpe.BooleanType)
+ }
+ js.If(condOrUndef, genSubAlts, elsep)(jstpe.AnyType)
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Generate a call to the method [[sym]] while using the formalArguments
+ * and potentially the argument array. Also inserts default parameters if
+ * required.
+ */
+ private def genApplyForSym(sym: Symbol): js.Tree = {
+ implicit val pos = sym.pos
+
+ // the (single) type of the repeated parameter if any
+ val repeatedTpe = enteringPhase(currentRun.uncurryPhase) {
+ for {
+ param <- sym.paramss.flatten.lastOption
+ if isRepeated(param)
+ } yield repeatedToSingle(param.tpe)
+ }
+
+ val normalArgc = sym.tpe.params.size -
+ (if (repeatedTpe.isDefined) 1 else 0)
+
+ // optional repeated parameter list
+ val jsVarArg = repeatedTpe map { tpe =>
+ // Copy arguments that go to vararg into an array, put it in a wrapper
+
+ val countIdent = freshLocalIdent("count")
+ val count = js.VarRef(countIdent, mutable = false)(jstpe.IntType)
+
+ val counterIdent = freshLocalIdent("i")
+ val counter = js.VarRef(counterIdent, mutable = true)(jstpe.IntType)
+
+ val arrayIdent = freshLocalIdent("varargs")
+ val array = js.VarRef(arrayIdent, mutable = false)(jstpe.AnyType)
+
+ val arguments = js.VarRef(js.Ident("arguments"),
+ mutable = false)(jstpe.AnyType)
+ val argLen = js.Unbox(
+ js.JSBracketSelect(arguments, js.StringLiteral("length")), 'I')
+ val argOffset = js.IntLiteral(normalArgc)
+
+ val jsArrayCtor =
+ js.JSBracketSelect(
+ js.JSBracketSelect(js.JSEnvInfo(), js.StringLiteral("global")),
+ js.StringLiteral("Array"))
+
+ js.Block(
+ // var i = 0
+ js.VarDef(counterIdent, jstpe.IntType, mutable = true,
+ rhs = js.IntLiteral(0)),
+ // val count = arguments.length - <normalArgc>
+ js.VarDef(countIdent, jstpe.IntType, mutable = false,
+ rhs = js.BinaryOp(js.BinaryOp.Int_-, argLen, argOffset)),
+ // val varargs = new Array(count)
+ js.VarDef(arrayIdent, jstpe.AnyType, mutable = false,
+ rhs = js.JSNew(jsArrayCtor, List(count))),
+ // while (i < count)
+ js.While(js.BinaryOp(js.BinaryOp.Num_<, counter, count), js.Block(
+ // varargs[i] = arguments[<normalArgc> + i];
+ js.Assign(
+ js.JSBracketSelect(array, counter),
+ js.JSBracketSelect(arguments,
+ js.BinaryOp(js.BinaryOp.Int_+, argOffset, counter))),
+ // i = i + 1 (++i won't work, desugar eliminates it)
+ js.Assign(counter, js.BinaryOp(js.BinaryOp.Int_+,
+ counter, js.IntLiteral(1)))
+ )),
+ // new WrappedArray(varargs)
+ genNew(WrappedArrayClass, WrappedArray_ctor, List(array))
+ )
+ }
+
+ // normal arguments
+ val jsArgs = genFormalArgs(normalArgc)
+ val jsArgRefs = jsArgs.map(_.ref)
+
+ // Generate JS code to prepare arguments (default getters and unboxes)
+ val jsArgPrep = genPrepareArgs(jsArgRefs, sym)
+ val jsResult = genResult(sym, jsArgPrep.map(_.ref) ++ jsVarArg)
+
+ js.Block(jsArgPrep :+ jsResult)
+ }
+
+ /** Generate the necessary JavaScript code to prepare the arguments of an
+ * exported method (unboxing and default parameter handling)
+ */
+ private def genPrepareArgs(jsArgs: List[js.VarRef], sym: Symbol)(
+ implicit pos: Position): List[js.VarDef] = {
+
+ val result = new mutable.ListBuffer[js.VarDef]
+
+ val funTpe = enteringPhase(currentRun.posterasurePhase)(sym.tpe)
+ for {
+ (jsArg, (param, i)) <- jsArgs zip funTpe.params.zipWithIndex
+ } yield {
+ // Code to verify the type of the argument (if it is defined)
+ val verifiedArg = {
+ val tpePosterasure =
+ enteringPhase(currentRun.posterasurePhase)(param.tpe)
+ tpePosterasure match {
+ case tpe if isPrimitiveValueType(tpe) =>
+ val unboxed = makePrimitiveUnbox(jsArg, tpe)
+ // Ensure we don't convert null to a primitive value type
+ js.If(js.BinaryOp(js.BinaryOp.===, jsArg, js.Null()),
+ genThrowTypeError(s"Found null, expected $tpe"),
+ unboxed)(unboxed.tpe)
+ case tpe: ErasedValueType =>
+ val boxedClass = tpe.valueClazz
+ val unboxMethod = boxedClass.derivedValueClassUnbox
+ genApplyMethod(
+ genAsInstanceOf(jsArg, tpe),
+ boxedClass, unboxMethod, Nil)
+ case tpe =>
+ genAsInstanceOf(jsArg, tpe)
+ }
+ }
+
+ // If argument is undefined and there is a default getter, call it
+ val verifiedOrDefault = if (param.hasFlag(Flags.DEFAULTPARAM)) {
+ js.If(js.BinaryOp(js.BinaryOp.===, jsArg, js.Undefined()), {
+ val trgSym = {
+ if (sym.isClassConstructor) sym.owner.companionModule.moduleClass
+ else sym.owner
+ }
+ val defaultGetter = trgSym.tpe.member(
+ nme.defaultGetterName(sym.name, i+1))
+
+ assert(defaultGetter.exists,
+ s"need default getter for method ${sym.fullName}")
+ assert(!defaultGetter.isOverloaded)
+
+ val trgTree = {
+ if (sym.isClassConstructor) genLoadModule(trgSym)
+ else js.This()(encodeClassType(trgSym))
+ }
+
+ // Pass previous arguments to defaultGetter
+ genApplyMethod(trgTree, trgSym, defaultGetter,
+ result.take(defaultGetter.tpe.params.size).toList.map(_.ref))
+ }, {
+ // Otherwise, unbox the argument
+ verifiedArg
+ })(verifiedArg.tpe)
+ } else {
+ // Otherwise, it is always the unboxed argument
+ verifiedArg
+ }
+
+ result +=
+ js.VarDef(js.Ident("prep"+jsArg.ident.name, jsArg.ident.originalName),
+ verifiedOrDefault.tpe, mutable = false, verifiedOrDefault)
+ }
+
+ result.toList
+ }
+
+ /** Generate the final forwarding call to the exported method.
+ * Attention: This method casts the arguments to the right type. The IR
+ * checker will not detect if you pass in a wrongly typed argument.
+ */
+ private def genResult(sym: Symbol,
+ args: List[js.Tree])(implicit pos: Position) = {
+ val thisType =
+ if (sym.owner == ObjectClass) jstpe.ClassType(ir.Definitions.ObjectClass)
+ else encodeClassType(sym.owner)
+ val call = genApplyMethod(js.This()(thisType), sym.owner, sym, args)
+ ensureBoxed(call,
+ enteringPhase(currentRun.posterasurePhase)(sym.tpe.resultType))
+ }
+
+ private sealed abstract class Exported {
+ def pos: Position
+ def params: List[Type]
+ def body: js.Tree
+ def name: String
+ def typeInfo: String
+ def hasRepeatedParam: Boolean
+ }
+
+ private case class ExportedSymbol(sym: Symbol) extends Exported {
+ def pos: Position = sym.pos
+ def params: List[Type] = sym.tpe.params.map(_.tpe)
+ def body: js.Tree = genApplyForSym(sym)
+ def name: String = sym.name.toString
+ def typeInfo: String = sym.tpe.toString
+ def hasRepeatedParam: Boolean = GenJSExports.this.hasRepeatedParam(sym)
+ }
+
+ private case class ExportedBody(params: List[Type], body: js.Tree,
+ name: String, pos: Position) extends Exported {
+ def typeInfo: String = params.mkString("(", ", ", ")")
+ val hasRepeatedParam: Boolean = false
+ }
+ }
+
+ private def isOverridingExport(sym: Symbol): Boolean = {
+ lazy val osym = sym.nextOverriddenSymbol
+ sym.isOverridingSymbol && !osym.owner.isInterface
+ }
+
+ private sealed abstract class RTTypeTest
+
+ private final case class HijackedTypeTest(
+ boxedClassName: String, rank: Int) extends RTTypeTest
+
+ private final case class InstanceOfTypeTest(tpe: Type) extends RTTypeTest {
+ override def equals(that: Any): Boolean = {
+ that match {
+ case InstanceOfTypeTest(thatTpe) => tpe =:= thatTpe
+ case _ => false
+ }
+ }
+ }
+
+ private case object NoTypeTest extends RTTypeTest
+
+ private object RTTypeTest {
+ implicit object Ordering extends PartialOrdering[RTTypeTest] {
+ override def tryCompare(lhs: RTTypeTest, rhs: RTTypeTest): Option[Int] = {
+ if (lteq(lhs, rhs)) if (lteq(rhs, lhs)) Some(0) else Some(-1)
+ else if (lteq(rhs, lhs)) Some(1) else None
+ }
+
+ override def lteq(lhs: RTTypeTest, rhs: RTTypeTest): Boolean = {
+ (lhs, rhs) match {
+ // NoTypeTest is always last
+ case (_, NoTypeTest) => true
+ case (NoTypeTest, _) => false
+
+ case (HijackedTypeTest(_, rank1), HijackedTypeTest(_, rank2)) =>
+ rank1 <= rank2
+
+ case (InstanceOfTypeTest(t1), InstanceOfTypeTest(t2)) =>
+ t1 <:< t2
+
+ case (_:HijackedTypeTest, _:InstanceOfTypeTest) => true
+ case (_:InstanceOfTypeTest, _:HijackedTypeTest) => false
+ }
+ }
+
+ override def equiv(lhs: RTTypeTest, rhs: RTTypeTest): Boolean = {
+ lhs == rhs
+ }
+ }
+ }
+
+ // Very simple O(n²) topological sort for elements assumed to be distinct
+ private def topoSortDistinctsBy[A <: AnyRef, B](coll: List[A])(f: A => B)(
+ implicit ord: PartialOrdering[B]): List[A] = {
+
+ @scala.annotation.tailrec
+ def loop(coll: List[A], acc: List[A]): List[A] = {
+ if (coll.isEmpty) acc
+ else if (coll.tail.isEmpty) coll.head :: acc
+ else {
+ val (lhs, rhs) = coll.span(x => !coll.forall(
+ y => (x eq y) || !ord.lteq(f(x), f(y))))
+ assert(!rhs.isEmpty, s"cycle while ordering $coll")
+ loop(lhs ::: rhs.tail, rhs.head :: acc)
+ }
+ }
+
+ loop(coll, Nil)
+ }
+
+ private def typeTestForTpe(tpe: Type): RTTypeTest = {
+ tpe match {
+ case tpe: ErasedValueType =>
+ InstanceOfTypeTest(tpe.valueClazz.typeConstructor)
+
+ case _ =>
+ import ir.{Definitions => Defs}
+ (toTypeKind(tpe): @unchecked) match {
+ case VoidKind => HijackedTypeTest(Defs.BoxedUnitClass, 0)
+ case BooleanKind => HijackedTypeTest(Defs.BoxedBooleanClass, 1)
+ case ByteKind => HijackedTypeTest(Defs.BoxedByteClass, 2)
+ case ShortKind => HijackedTypeTest(Defs.BoxedShortClass, 3)
+ case IntKind => HijackedTypeTest(Defs.BoxedIntegerClass, 4)
+ case FloatKind => HijackedTypeTest(Defs.BoxedFloatClass, 5)
+ case DoubleKind => HijackedTypeTest(Defs.BoxedDoubleClass, 6)
+
+ case CharKind => InstanceOfTypeTest(boxedClass(CharClass).tpe)
+ case LongKind => InstanceOfTypeTest(boxedClass(LongClass).tpe)
+
+ case REFERENCE(cls) =>
+ if (cls == StringClass) HijackedTypeTest(Defs.StringClass, 7)
+ else if (cls == ObjectClass) NoTypeTest
+ else if (isRawJSType(tpe)) {
+ cls match {
+ case JSUndefinedClass => HijackedTypeTest(Defs.BoxedUnitClass, 0)
+ case JSBooleanClass => HijackedTypeTest(Defs.BoxedBooleanClass, 1)
+ case JSNumberClass => HijackedTypeTest(Defs.BoxedDoubleClass, 6)
+ case JSStringClass => HijackedTypeTest(Defs.StringClass, 7)
+ case _ => NoTypeTest
+ }
+ } else InstanceOfTypeTest(tpe)
+
+ case ARRAY(_) => InstanceOfTypeTest(tpe)
+ }
+ }
+ }
+
+ // Group-by that does not rely on hashCode(), only equals() - O(n²)
+ private def groupByWithoutHashCode[A, B](
+ coll: List[A])(f: A => B): List[(B, List[A])] = {
+
+ import scala.collection.mutable.ArrayBuffer
+ val m = new ArrayBuffer[(B, 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)
+ }
+
+ m.toList
+ }
+
+ private def genThrowTypeError(msg: String = "No matching overload")(
+ implicit pos: Position): js.Tree = {
+ js.Throw(js.StringLiteral(msg))
+ }
+
+ private def genFormalArgs(count: Int)(implicit pos: Position): List[js.ParamDef] =
+ (1 to count map genFormalArg).toList
+
+ private def genFormalArg(index: Int)(implicit pos: Position): js.ParamDef =
+ js.ParamDef(js.Ident("arg$" + index), jstpe.AnyType, mutable = false)
+
+ private def hasRepeatedParam(sym: Symbol) =
+ enteringPhase(currentRun.uncurryPhase) {
+ sym.paramss.flatten.lastOption.exists(isRepeated _)
+ }
+
+}
diff --git a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/GenJSFiles.scala b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/GenJSFiles.scala
new file mode 100644
index 0000000..f754e70
--- /dev/null
+++ b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/GenJSFiles.scala
@@ -0,0 +1,51 @@
+/* Scala.js compiler
+ * Copyright 2013 LAMP/EPFL
+ * @author Sébastien Doeraene
+ */
+
+package scala.scalajs.compiler
+
+import scala.tools.nsc._
+import scala.tools.nsc.io.AbstractFile
+import scala.reflect.internal.pickling.PickleBuffer
+
+import java.io._
+
+import scala.scalajs.ir
+import ir.Infos._
+
+/** Send JS ASTs to files
+ *
+ * @author Sébastien Doeraene
+ */
+trait GenJSFiles extends SubComponent { self: GenJSCode =>
+ import global._
+ import jsAddons._
+
+ def genIRFile(cunit: CompilationUnit, sym: Symbol, tree: ir.Trees.ClassDef,
+ classInfo: ClassInfo): Unit = {
+ val outfile = getFileFor(cunit, sym, ".sjsir")
+ val output = outfile.bufferedOutput
+ try {
+ ir.InfoSerializers.serialize(output, classInfo)
+ ir.Serializers.serialize(output, tree)
+ } finally {
+ output.close()
+ }
+ }
+
+ private def getFileFor(cunit: CompilationUnit, sym: Symbol,
+ suffix: String) = {
+ val baseDir: AbstractFile =
+ settings.outputDirs.outputDirFor(cunit.source.file)
+
+ val pathParts = sym.fullName.split("[./]")
+ val dir = (baseDir /: pathParts.init)(_.subdirectoryNamed(_))
+
+ var filename = pathParts.last
+ if (sym.isModuleClass && !sym.isImplClass)
+ filename = filename + nme.MODULE_SUFFIX_STRING
+
+ dir fileNamed (filename + suffix)
+ }
+}
diff --git a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSDefinitions.scala b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSDefinitions.scala
new file mode 100644
index 0000000..b8a483a
--- /dev/null
+++ b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSDefinitions.scala
@@ -0,0 +1,128 @@
+/* Scala.js compiler
+ * Copyright 2013 LAMP/EPFL
+ * @author Sébastien Doeraene
+ */
+
+package scala.scalajs.compiler
+
+import scala.tools.nsc._
+
+/** Core definitions for Scala.js
+ *
+ * @author Sébastien Doeraene
+ */
+trait JSDefinitions { self: JSGlobalAddons =>
+ import global._
+
+ object jsDefinitions extends JSDefinitionsClass
+
+ import definitions._
+ import rootMirror._
+
+ class JSDefinitionsClass {
+
+ lazy val ScalaJSJSPackage = getPackage(newTermNameCached("scala.scalajs.js")) // compat 2.10/2.11
+ lazy val JSPackage_undefined = getMemberMethod(ScalaJSJSPackage, newTermName("undefined"))
+ lazy val JSPackage_isUndefined = getMemberMethod(ScalaJSJSPackage, newTermName("isUndefined"))
+ lazy val JSPackage_typeOf = getMemberMethod(ScalaJSJSPackage, newTermName("typeOf"))
+ lazy val JSPackage_debugger = getMemberMethod(ScalaJSJSPackage, newTermName("debugger"))
+ lazy val JSPackage_native = getMemberMethod(ScalaJSJSPackage, newTermName("native"))
+
+ lazy val ScalaJSJSPrimPackage = getPackage(newTermNameCached("scala.scalajs.js.prim")) // compat 2.10/2.11
+
+ lazy val JSAnyClass = getRequiredClass("scala.scalajs.js.Any")
+ lazy val JSDynamicClass = getRequiredClass("scala.scalajs.js.Dynamic")
+ lazy val JSDynamic_selectDynamic = getMemberMethod(JSDynamicClass, newTermName("selectDynamic"))
+ lazy val JSDynamic_updateDynamic = getMemberMethod(JSDynamicClass, newTermName("updateDynamic"))
+ lazy val JSDynamic_applyDynamic = getMemberMethod(JSDynamicClass, newTermName("applyDynamic"))
+ lazy val JSDictionaryClass = getRequiredClass("scala.scalajs.js.Dictionary")
+ lazy val JSDictionary_delete = getMemberMethod(JSDictionaryClass, newTermName("delete"))
+ lazy val JSNumberClass = getRequiredClass("scala.scalajs.js.prim.Number")
+ lazy val JSBooleanClass = getRequiredClass("scala.scalajs.js.prim.Boolean")
+ lazy val JSStringClass = getRequiredClass("scala.scalajs.js.prim.String")
+ lazy val JSUndefinedClass = getRequiredClass("scala.scalajs.js.prim.Undefined")
+ lazy val JSObjectClass = getRequiredClass("scala.scalajs.js.Object")
+ lazy val JSThisFunctionClass = getRequiredClass("scala.scalajs.js.ThisFunction")
+
+ lazy val JSGlobalScopeClass = getRequiredClass("scala.scalajs.js.GlobalScope")
+
+ lazy val UndefOrClass = getRequiredClass("scala.scalajs.js.UndefOr")
+
+ lazy val JSArrayClass = getRequiredClass("scala.scalajs.js.Array")
+ lazy val JSArray_apply = getMemberMethod(JSArrayClass, newTermName("apply"))
+ lazy val JSArray_update = getMemberMethod(JSArrayClass, newTermName("update"))
+
+ lazy val JSFunctionClasses = (0 to 22) map (n => getRequiredClass("scala.scalajs.js.Function"+n))
+ lazy val JSThisFunctionClasses = (0 to 21) map (n => getRequiredClass("scala.scalajs.js.ThisFunction"+n))
+ lazy val AllJSFunctionClasses = JSFunctionClasses ++ JSThisFunctionClasses
+
+ lazy val RuntimeExceptionClass = requiredClass[RuntimeException]
+ lazy val JavaScriptExceptionClass = getClassIfDefined("scala.scalajs.js.JavaScriptException")
+
+ lazy val JSNameAnnotation = getRequiredClass("scala.scalajs.js.annotation.JSName")
+ lazy val JSBracketAccessAnnotation = getRequiredClass("scala.scalajs.js.annotation.JSBracketAccess")
+ lazy val JSExportAnnotation = getRequiredClass("scala.scalajs.js.annotation.JSExport")
+ lazy val JSExportDescendentObjectsAnnotation = getRequiredClass("scala.scalajs.js.annotation.JSExportDescendentObjects")
+ lazy val JSExportDescendentClassesAnnotation = getRequiredClass("scala.scalajs.js.annotation.JSExportDescendentClasses")
+ lazy val JSExportAllAnnotation = getRequiredClass("scala.scalajs.js.annotation.JSExportAll")
+ lazy val JSExportNamedAnnotation = getRequiredClass("scala.scalajs.js.annotation.JSExportNamed")
+
+ lazy val JSAnyTpe = JSAnyClass.toTypeConstructor
+ lazy val JSDynamicTpe = JSDynamicClass.toTypeConstructor
+ lazy val JSNumberTpe = JSNumberClass.toTypeConstructor
+ lazy val JSBooleanTpe = JSBooleanClass.toTypeConstructor
+ lazy val JSStringTpe = JSStringClass.toTypeConstructor
+ lazy val JSUndefinedTpe = JSUndefinedClass.toTypeConstructor
+ lazy val JSObjectTpe = JSObjectClass.toTypeConstructor
+
+ lazy val JSGlobalScopeTpe = JSGlobalScopeClass.toTypeConstructor
+
+ lazy val JSFunctionTpes = JSFunctionClasses.map(_.toTypeConstructor)
+
+ lazy val JSAnyModule = JSAnyClass.companionModule
+ def JSAny_fromFunction(arity: Int) = getMemberMethod(JSAnyModule, newTermName("fromFunction"+arity))
+
+ lazy val JSDynamicModule = JSDynamicClass.companionModule
+ lazy val JSDynamic_newInstance = getMemberMethod(JSDynamicModule, newTermName("newInstance"))
+ lazy val JSDynamicLiteral = getMemberModule(JSDynamicModule, newTermName("literal"))
+ lazy val JSDynamicLiteral_applyDynamicNamed = getMemberMethod(JSDynamicLiteral, newTermName("applyDynamicNamed"))
+ lazy val JSDynamicLiteral_applyDynamic = getMemberMethod(JSDynamicLiteral, newTermName("applyDynamic"))
+
+ lazy val JSObjectModule = JSObjectClass.companionModule
+ lazy val JSObject_hasProperty = getMemberMethod(JSObjectModule, newTermName("hasProperty"))
+ lazy val JSObject_properties = getMemberMethod(JSObjectModule, newTermName("properties"))
+
+ lazy val JSArrayModule = JSArrayClass.companionModule
+ lazy val JSArray_create = getMemberMethod(JSArrayModule, newTermName("apply"))
+
+ lazy val JSThisFunctionModule = JSThisFunctionClass.companionModule
+ def JSThisFunction_fromFunction(arity: Int) = getMemberMethod(JSThisFunctionModule, newTermName("fromFunction"+arity))
+
+ lazy val RawJSTypeAnnot = getClassIfDefined("scala.scalajs.js.annotation.RawJSType")
+
+ lazy val RuntimeStringModule = getRequiredModule("scala.scalajs.runtime.RuntimeString")
+ lazy val RuntimeStringModuleClass = RuntimeStringModule.moduleClass
+
+ lazy val BooleanReflectiveCallClass = getRequiredClass("scala.scalajs.runtime.BooleanReflectiveCall")
+ lazy val NumberReflectiveCallClass = getRequiredClass("scala.scalajs.runtime.NumberReflectiveCall")
+ lazy val IntegerReflectiveCallClass = getRequiredClass("scala.scalajs.runtime.IntegerReflectiveCall")
+
+ lazy val RuntimePackageModule = getPackageObject("scala.scalajs.runtime")
+ lazy val Runtime_wrapJavaScriptException = getMemberMethod(RuntimePackageModule, newTermName("wrapJavaScriptException"))
+ lazy val Runtime_unwrapJavaScriptException = getMemberMethod(RuntimePackageModule, newTermName("unwrapJavaScriptException"))
+ lazy val Runtime_genTraversableOnce2jsArray = getMemberMethod(RuntimePackageModule, newTermName("genTraversableOnce2jsArray"))
+ lazy val Runtime_newJSObjectWithVarargs = getMemberMethod(RuntimePackageModule, newTermName("newJSObjectWithVarargs"))
+ lazy val Runtime_propertiesOf = getMemberMethod(RuntimePackageModule, newTermName("propertiesOf"))
+
+ lazy val WrappedArrayClass = getRequiredClass("scala.scalajs.js.WrappedArray")
+ lazy val WrappedArray_ctor = WrappedArrayClass.primaryConstructor
+
+ // This is a def, since similar symbols (arrayUpdateMethod, etc.) are in runDefinitions
+ // (rather than definitions) and we weren't sure if it is safe to make this a lazy val
+ def ScalaRunTime_isArray = getMemberMethod(ScalaRunTimeModule, newTermName("isArray")).suchThat(_.tpe.params.size == 2)
+
+ lazy val BoxesRunTime_boxToCharacter = getMemberMethod(BoxesRunTimeModule, newTermName("boxToCharacter"))
+ lazy val BoxesRunTime_unboxToChar = getMemberMethod(BoxesRunTimeModule, newTermName("unboxToChar"))
+
+ }
+}
diff --git a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSEncoding.scala b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSEncoding.scala
new file mode 100644
index 0000000..bc7f8be
--- /dev/null
+++ b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSEncoding.scala
@@ -0,0 +1,261 @@
+/* Scala.js compiler
+ * Copyright 2013 LAMP/EPFL
+ * @author Sébastien Doeraene
+ */
+
+package scala.scalajs.compiler
+
+import scala.collection.mutable
+
+import scala.tools.nsc._
+
+import scala.scalajs.ir
+import ir.{Trees => js, Types => jstpe}
+
+import util.ScopedVar
+import ScopedVar.withScopedVars
+
+/** Encoding of symbol names for JavaScript
+ *
+ * Some issues that this encoding solves:
+ * * Overloading: encode the full signature in the JS name
+ * * Same scope for fields and methods of a class
+ * * Global access to classes and modules (by their full name)
+ *
+ * @author Sébastien Doeraene
+ */
+trait JSEncoding extends SubComponent { self: GenJSCode =>
+ import global._
+ import jsAddons._
+
+ /** Outer separator string (between parameter types) */
+ final val OuterSep = "__"
+
+ /** Inner separator character (replace dots in full names) */
+ final val InnerSep = "_"
+
+ /** Name given to the local Scala.js environment variable */
+ final val ScalaJSEnvironmentName = "ScalaJS"
+
+ /** Name given to all exported stuff of a class for DCE */
+ final val dceExportName = "<exported>"
+
+ // Fresh local name generator ----------------------------------------------
+
+ private val usedLocalNames = new ScopedVar[mutable.Set[String]]
+ private val localSymbolNames = new ScopedVar[mutable.Map[Symbol, String]]
+ private val isKeywordOrReserved =
+ js.isKeyword ++ Seq("arguments", "eval", ScalaJSEnvironmentName)
+
+ def withNewLocalNameScope[A](body: => A): A =
+ withScopedVars(
+ usedLocalNames := mutable.Set.empty,
+ localSymbolNames := mutable.Map.empty
+ )(body)
+
+ private def freshName(base: String = "x"): String = {
+ var suffix = 1
+ var longName = base
+ while (usedLocalNames(longName) || isKeywordOrReserved(longName)) {
+ suffix += 1
+ longName = base+"$"+suffix
+ }
+ usedLocalNames += longName
+ longName
+ }
+
+ def freshLocalIdent()(implicit pos: ir.Position): js.Ident =
+ js.Ident(freshName(), None)
+
+ def freshLocalIdent(base: String)(implicit pos: ir.Position): js.Ident =
+ js.Ident(freshName(base), Some(base))
+
+ private def localSymbolName(sym: Symbol): String =
+ localSymbolNames.getOrElseUpdate(sym, freshName(sym.name.toString))
+
+ // Encoding methods ----------------------------------------------------------
+
+ def encodeLabelSym(sym: Symbol)(implicit pos: Position): js.Ident = {
+ require(sym.isLabel, "encodeLabelSym called with non-label symbol: " + sym)
+ js.Ident(localSymbolName(sym), Some(sym.unexpandedName.decoded))
+ }
+
+ private lazy val allRefClasses: Set[Symbol] = {
+ import definitions._
+ (Set(ObjectRefClass, VolatileObjectRefClass) ++
+ refClass.values ++ volatileRefClass.values)
+ }
+
+ def encodeFieldSym(sym: Symbol)(implicit pos: Position): js.Ident = {
+ require(sym.owner.isClass && sym.isTerm && !sym.isMethod && !sym.isModule,
+ "encodeFieldSym called with non-field symbol: " + sym)
+
+ val name0 = encodeMemberNameInternal(sym)
+ val name =
+ if (name0.charAt(name0.length()-1) != ' ') name0
+ else name0.substring(0, name0.length()-1)
+
+ /* We have to special-case fields of Ref types (IntRef, ObjectRef, etc.)
+ * because they are emitted as private by our .scala source files, but
+ * they are considered public at use site since their symbols come from
+ * Java-emitted .class files.
+ */
+ val idSuffix =
+ if (sym.isPrivate || allRefClasses.contains(sym.owner))
+ sym.owner.ancestors.count(!_.isInterface).toString
+ else
+ "f"
+
+ val encodedName = name + "$" + idSuffix
+ js.Ident(mangleJSName(encodedName), Some(sym.unexpandedName.decoded))
+ }
+
+ def encodeMethodSym(sym: Symbol, reflProxy: Boolean = false)
+ (implicit pos: Position): js.Ident = {
+ val (encodedName, paramsString) = encodeMethodNameInternal(sym, reflProxy)
+ js.Ident(encodedName + paramsString,
+ Some(sym.unexpandedName.decoded + paramsString))
+ }
+
+ def encodeMethodName(sym: Symbol, reflProxy: Boolean = false): String = {
+ val (encodedName, paramsString) = encodeMethodNameInternal(sym, reflProxy)
+ encodedName + paramsString
+ }
+
+ /** Encodes a method symbol of java.lang.String for use in RuntimeString.
+ *
+ * This basically means adding an initial parameter of type
+ * java.lang.String, which is the `this` parameter.
+ */
+ def encodeRTStringMethodSym(sym: Symbol)(
+ implicit pos: Position): (Symbol, js.Ident) = {
+ require(sym.isMethod, "encodeMethodSym called with non-method symbol: " + sym)
+ require(sym.owner == definitions.StringClass)
+ require(!sym.isClassConstructor && !sym.isPrivate)
+
+ val (encodedName, paramsString) =
+ encodeMethodNameInternal(sym, inRTClass = true)
+ val methodIdent = js.Ident(encodedName + paramsString,
+ Some(sym.unexpandedName.decoded + paramsString))
+
+ (jsDefinitions.RuntimeStringModuleClass, methodIdent)
+ }
+
+ private def encodeMethodNameInternal(sym: Symbol,
+ reflProxy: Boolean = false,
+ inRTClass: Boolean = false): (String, String) = {
+ require(sym.isMethod, "encodeMethodSym called with non-method symbol: " + sym)
+
+ def name = encodeMemberNameInternal(sym)
+
+ val encodedName = {
+ if (sym.isClassConstructor)
+ "init" + InnerSep
+ else if (foreignIsImplClass(sym.owner))
+ encodeClassFullName(sym.owner) + OuterSep + name
+ else if (sym.isPrivate)
+ mangleJSName(name) + OuterSep + "p" +
+ sym.owner.ancestors.count(!_.isInterface).toString
+ else
+ mangleJSName(name)
+ }
+
+ val paramsString = makeParamsString(sym, reflProxy, inRTClass)
+
+ (encodedName, paramsString)
+ }
+
+ def encodeStaticMemberSym(sym: Symbol)(implicit pos: Position): js.Ident = {
+ require(sym.isStaticMember,
+ "encodeStaticMemberSym called with non-static symbol: " + sym)
+ js.Ident(
+ mangleJSName(encodeMemberNameInternal(sym)) +
+ makeParamsString(List(internalName(sym.tpe))),
+ Some(sym.unexpandedName.decoded))
+ }
+
+ def encodeLocalSym(sym: Symbol)(implicit pos: Position): js.Ident = {
+ require(!sym.owner.isClass && sym.isTerm && !sym.isMethod && !sym.isModule,
+ "encodeLocalSym called with non-local symbol: " + sym)
+ js.Ident(mangleJSName(localSymbolName(sym)), Some(sym.unexpandedName.decoded))
+ }
+
+ def foreignIsImplClass(sym: Symbol): Boolean =
+ sym.isModuleClass && nme.isImplClassName(sym.name)
+
+ def encodeClassType(sym: Symbol): jstpe.Type = {
+ if (sym == definitions.ObjectClass) jstpe.AnyType
+ else if (isRawJSType(sym.toTypeConstructor)) jstpe.AnyType
+ else {
+ assert(sym != definitions.ArrayClass,
+ "encodeClassType() cannot be called with ArrayClass")
+ jstpe.ClassType(encodeClassFullName(sym))
+ }
+ }
+
+ def encodeClassFullNameIdent(sym: Symbol)(implicit pos: Position): js.Ident = {
+ js.Ident(encodeClassFullName(sym), Some(sym.fullName))
+ }
+
+ def encodeModuleFullNameIdent(sym: Symbol)(implicit pos: Position): js.Ident = {
+ js.Ident(encodeModuleFullName(sym), Some(sym.fullName))
+ }
+
+ def encodeClassFullName(sym: Symbol): String = {
+ ir.Definitions.encodeClassName(
+ sym.fullName + (if (needsModuleClassSuffix(sym)) "$" else ""))
+ }
+
+ def needsModuleClassSuffix(sym: Symbol): Boolean =
+ sym.isModuleClass && !foreignIsImplClass(sym)
+
+ def encodeModuleFullName(sym: Symbol): String =
+ ir.Definitions.encodeClassName(sym.fullName + "$").dropRight(1)
+
+ private def encodeMemberNameInternal(sym: Symbol): String =
+ sym.name.toString.replace("_", "$und")
+
+ // Encoding of method signatures
+
+ private def makeParamsString(sym: Symbol, reflProxy: Boolean,
+ inRTClass: Boolean): String = {
+ val tpe = sym.tpe
+ val paramTypeNames = tpe.params map (p => internalName(p.tpe))
+ val paramAndResultTypeNames = {
+ if (sym.isClassConstructor)
+ paramTypeNames
+ else if (reflProxy)
+ paramTypeNames :+ ""
+ else {
+ val paramAndResultTypeNames0 =
+ paramTypeNames :+ internalName(tpe.resultType)
+ if (!inRTClass) paramAndResultTypeNames0
+ else internalName(sym.owner.toTypeConstructor) +: paramAndResultTypeNames0
+ }
+ }
+ makeParamsString(paramAndResultTypeNames)
+ }
+
+ private def makeParamsString(paramAndResultTypeNames: List[String]) =
+ paramAndResultTypeNames.mkString(OuterSep, OuterSep, "")
+
+ /** Computes the internal name for a type. */
+ private def internalName(tpe: Type): String = internalName(toTypeKind(tpe))
+
+ private def internalName(kind: TypeKind): String = kind match {
+ case VOID => "V"
+ case kind: ValueTypeKind => kind.primitiveCharCode.toString()
+ case NOTHING => ir.Definitions.RuntimeNothingClass
+ case NULL => ir.Definitions.RuntimeNullClass
+ case REFERENCE(cls) => encodeClassFullName(cls)
+ case ARRAY(elem) => "A"+internalName(elem)
+ }
+
+ /** mangles names that are illegal in JavaScript by prepending a $
+ * also mangles names that would collide with these mangled names
+ */
+ private def mangleJSName(name: String) =
+ if (js.isKeyword(name) || name(0).isDigit || name(0) == '$')
+ "$" + name
+ else name
+}
diff --git a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSGlobalAddons.scala b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSGlobalAddons.scala
new file mode 100644
index 0000000..3621050
--- /dev/null
+++ b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSGlobalAddons.scala
@@ -0,0 +1,244 @@
+/* Scala.js compiler
+ * Copyright 2013 LAMP/EPFL
+ * @author Sébastien Doeraene
+ */
+
+package scala.scalajs.compiler
+
+import scala.tools.nsc._
+
+import scala.collection.mutable
+
+/** Additions to Global meaningful for the JavaScript backend
+ *
+ * @author Sébastien Doeraene
+ */
+trait JSGlobalAddons extends JSDefinitions
+ with Compat210Component {
+ val global: Global
+
+ import global._
+ import jsDefinitions._
+ import definitions._
+
+ /** JavaScript primitives, used in jscode */
+ object jsPrimitives extends JSPrimitives {
+ val global: JSGlobalAddons.this.global.type = JSGlobalAddons.this.global
+ val jsAddons: ThisJSGlobalAddons =
+ JSGlobalAddons.this.asInstanceOf[ThisJSGlobalAddons]
+ }
+
+ /** global javascript interop related helpers */
+ object jsInterop {
+ import scala.reflect.NameTransformer
+ import scala.reflect.internal.Flags
+
+ private val exportPrefix = "$js$exported$"
+ private val methodExportPrefix = exportPrefix + "meth$"
+ private val propExportPrefix = exportPrefix + "prop$"
+
+ case class ExportInfo(jsName: String, pos: Position, isNamed: Boolean)
+
+ /** retrieves the names a sym should be exported to from its annotations
+ *
+ * Note that for accessor symbols, the annotations of the accessed symbol
+ * are used, rather than the annotations of the accessor itself.
+ */
+ def exportsOf(sym: Symbol): List[ExportInfo] = {
+ val exports = directExportsOf(sym) ++ inheritedExportsOf(sym)
+
+ // Calculate the distinct exports for this symbol (eliminate double
+ // occurrences of (name, isNamed) pairs).
+ val buf = new mutable.ListBuffer[ExportInfo]
+ val seen = new mutable.HashSet[(String, Boolean)]
+ for (exp <- exports) {
+ if (!seen.contains((exp.jsName, exp.isNamed))) {
+ buf += exp
+ seen += ((exp.jsName, exp.isNamed))
+ }
+ }
+
+ buf.toList
+ }
+
+ private def directExportsOf(sym: Symbol): List[ExportInfo] = {
+ val trgSym = {
+ // For accessors, look on the val/var def
+ if (sym.isAccessor) sym.accessed
+ // For primary class constructors, look on the class itself
+ else if (sym.isPrimaryConstructor && !sym.owner.isModuleClass) sym.owner
+ else sym
+ }
+
+ // Annotations that are directly on the member
+ val directAnnots = for {
+ annot <- trgSym.annotations
+ if annot.symbol == JSExportAnnotation ||
+ annot.symbol == JSExportNamedAnnotation
+ } yield annot
+
+ // Annotations for this member on the whole unit
+ val unitAnnots = {
+ if (sym.isMethod && sym.isPublic &&
+ !sym.isConstructor && !sym.isSynthetic)
+ sym.owner.annotations.filter(_.symbol == JSExportAllAnnotation)
+ else
+ Nil
+ }
+
+ for {
+ annot <- directAnnots ++ unitAnnots
+ } yield {
+ // Is this a named export or a normal one?
+ val named = annot.symbol == JSExportNamedAnnotation
+
+ def explicitName = annot.stringArg(0).getOrElse {
+ reporter.error(annot.pos,
+ s"The argument to ${annot.symbol.name} must be a literal string")
+ "dummy"
+ }
+
+ val name =
+ if (annot.args.nonEmpty) explicitName
+ else if (sym.isConstructor) decodedFullName(sym.owner)
+ else if (sym.isModuleClass) decodedFullName(sym)
+ else sym.unexpandedName.decoded.stripSuffix("_=")
+
+ // Enforce that methods ending with _= are exported as setters
+ if (sym.isMethod && !sym.isConstructor &&
+ sym.name.decoded.endsWith("_=") && !isJSSetter(sym)) {
+ reporter.error(annot.pos, "A method ending in _= will be exported " +
+ s"as setter. But ${sym.name.decoded} does not have the right " +
+ "signature to do so (single argument, unit return type).")
+ }
+
+ // Enforce no __ in name
+ if (name.contains("__")) {
+ // Get position for error message
+ val pos = if (annot.stringArg(0).isDefined)
+ annot.args.head.pos
+ else trgSym.pos
+
+ reporter.error(pos,
+ "An exported name may not contain a double underscore (`__`)")
+ }
+
+ // Make sure we do not override the default export of toString
+ if (!sym.isConstructor && name == "toString" && !named &&
+ sym.name != nme.toString_ && sym.tpe.params.isEmpty &&
+ !isJSGetter(sym)) {
+ reporter.error(annot.pos, "You may not export a zero-argument " +
+ "method named other than 'toString' under the name 'toString'")
+ }
+
+ if (named && isJSProperty(sym)) {
+ reporter.error(annot.pos,
+ "You may not export a getter or a setter as a named export")
+ }
+
+ ExportInfo(name, annot.pos, named)
+ }
+ }
+
+ private def inheritedExportsOf(sym: Symbol): List[ExportInfo] = {
+ // The symbol from which we (potentially) inherit exports. It also
+ // gives the exports their name
+ val trgSym = {
+ if (sym.isModuleClass)
+ sym
+ else if (sym.isConstructor && sym.isPublic &&
+ sym.owner.isConcreteClass && !sym.owner.isModuleClass)
+ sym.owner
+ else NoSymbol
+ }
+
+ if (trgSym == NoSymbol) {
+ Nil
+ } else {
+ val trgAnnot =
+ if (sym.isModuleClass) JSExportDescendentObjectsAnnotation
+ else JSExportDescendentClassesAnnotation
+
+ val forcingSym =
+ trgSym.ancestors.find(_.annotations.exists(_.symbol == trgAnnot))
+
+ val name = decodedFullName(trgSym)
+
+ forcingSym.map { fs =>
+ // Enfore no __ in name
+ if (name.contains("__")) {
+ // Get all annotation positions for error message
+ reporter.error(sym.pos,
+ s"""${trgSym.name} may not have a double underscore (`__`) in its fully qualified
+ |name, since it is forced to be exported by a @${trgAnnot.name} on ${fs}""".stripMargin)
+ }
+
+ ExportInfo(name, sym.pos, false)
+ }.toList
+ }
+ }
+
+ /** 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
+ }
+
+ /** creates a name for an export specification */
+ def scalaExportName(jsName: String, isProp: Boolean): TermName = {
+ val pref = if (isProp) propExportPrefix else methodExportPrefix
+ val encname = NameTransformer.encode(jsName)
+ newTermName(pref + encname)
+ }
+
+ /** checks if the given symbol is a JSExport */
+ def isExport(sym: Symbol): Boolean =
+ sym.unexpandedName.startsWith(exportPrefix) &&
+ !sym.hasFlag(Flags.DEFAULTPARAM)
+
+ /** retrieves the originally assigned jsName of this export and whether it
+ * is a property
+ */
+ def jsExportInfo(name: Name): (String, Boolean) = {
+ def dropPrefix(prefix: String) ={
+ if (name.startsWith(prefix)) {
+ // We can't decode right away due to $ separators
+ val enc = name.encoded.substring(prefix.length)
+ Some(NameTransformer.decode(enc))
+ } else None
+ }
+
+ dropPrefix(methodExportPrefix).map((_,false)) orElse
+ dropPrefix(propExportPrefix).map((_,true)) getOrElse
+ sys.error("non-exported name passed to jsInfoSpec")
+ }
+
+ def isJSProperty(sym: Symbol): Boolean = isJSGetter(sym) || isJSSetter(sym)
+
+ /** has this symbol to be translated into a JS getter (both directions)? */
+ def isJSGetter(sym: Symbol): Boolean = {
+ sym.tpe.params.isEmpty && enteringPhase(currentRun.uncurryPhase) {
+ sym.tpe.isInstanceOf[NullaryMethodType]
+ }
+ }
+
+ /** has this symbol to be translated into a JS setter (both directions)? */
+ def isJSSetter(sym: Symbol) = {
+ sym.unexpandedName.decoded.endsWith("_=") &&
+ sym.tpe.resultType.typeSymbol == UnitClass &&
+ enteringPhase(currentRun.uncurryPhase) {
+ sym.tpe.paramss match {
+ case List(List(arg)) => !isScalaRepeatedParamType(arg.tpe)
+ case _ => false
+ }
+ }
+ }
+
+ /** has this symbol to be translated into a JS bracket access (JS to Scala) */
+ def isJSBracketAccess(sym: Symbol) =
+ sym.hasAnnotation(JSBracketAccessAnnotation)
+
+ }
+
+}
diff --git a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSPrimitives.scala b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSPrimitives.scala
new file mode 100644
index 0000000..b8c20e6
--- /dev/null
+++ b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSPrimitives.scala
@@ -0,0 +1,119 @@
+/* Scala.js compiler
+ * Copyright 2013 LAMP/EPFL
+ * @author Sébastien Doeraene
+ */
+
+package scala.scalajs.compiler
+
+import scala.tools.nsc._
+
+import scala.collection.mutable
+
+/** Extension of ScalaPrimitives for primitives only relevant to the JS backend
+ *
+ * @author Sébastie Doeraene
+ */
+abstract class JSPrimitives {
+ val global: Global
+
+ type ThisJSGlobalAddons = JSGlobalAddons {
+ val global: JSPrimitives.this.global.type
+ }
+
+ val jsAddons: ThisJSGlobalAddons
+
+ import global._
+ import jsAddons._
+ import definitions._
+ import rootMirror._
+ import jsDefinitions._
+ import scalaPrimitives._
+
+ val GETCLASS = 301 // Object.getClass()
+
+ val F2JS = 305 // FunctionN to js.FunctionN
+ val F2JSTHIS = 306 // FunctionN to js.ThisFunction{N-1}
+
+ val DYNNEW = 321 // Instantiate a new JavaScript object
+
+ val DYNSELECT = 330 // js.Dynamic.selectDynamic
+ val DYNUPDATE = 331 // js.Dynamic.updateDynamic
+ val DYNAPPLY = 332 // js.Dynamic.applyDynamic
+ val DYNLITN = 333 // js.Dynamic.literal.applyDynamicNamed
+ val DYNLIT = 334 // js.Dynamic.literal.applyDynamic
+
+ val DICT_DEL = 335 // js.Dictionary.delete
+
+ val ARR_CREATE = 337 // js.Array.apply (array literal syntax)
+
+ val UNDEFVAL = 342 // js.undefined
+ val ISUNDEF = 343 // js.isUndefined
+ val TYPEOF = 344 // typeof x
+ val DEBUGGER = 345 // js.debugger()
+ val HASPROP = 346 // js.Object.hasProperty(o, p), equiv to `p in o` in JS
+ val OBJPROPS = 347 // js.Object.properties(o), equiv to `for (p in o)` in JS
+ val JS_NATIVE = 348 // js.native. Marker method. Fails if tried to be emitted.
+
+ val UNITVAL = 349 // () value, which is undefined
+ val UNITTYPE = 350 // BoxedUnit.TYPE (== classOf[Unit])
+
+ val ENV_INFO = 353 // __ScalaJSEnv via helper
+
+ /** Initialize the map of primitive methods (for GenJSCode) */
+ def init(): Unit = initWithPrimitives(addPrimitive)
+
+ /** Init the map of primitive methods for Scala.js (for PrepJSInterop) */
+ def initPrepJSPrimitives(): Unit = {
+ scalaJSPrimitives.clear()
+ initWithPrimitives(scalaJSPrimitives.put)
+ }
+
+ /** Only call from PrepJSInterop. In GenJSCode, use
+ * scalaPrimitives.isPrimitive instead
+ */
+ def isJavaScriptPrimitive(sym: Symbol): Boolean =
+ scalaJSPrimitives.contains(sym)
+
+ private val scalaJSPrimitives = mutable.Map.empty[Symbol, Int]
+
+ private def initWithPrimitives(addPrimitive: (Symbol, Int) => Unit): Unit = {
+ addPrimitive(Object_getClass, GETCLASS)
+
+ for (i <- 0 to 22)
+ addPrimitive(JSAny_fromFunction(i), F2JS)
+ for (i <- 1 to 22)
+ addPrimitive(JSThisFunction_fromFunction(i), F2JSTHIS)
+
+ addPrimitive(JSDynamic_newInstance, DYNNEW)
+
+ addPrimitive(JSDynamic_selectDynamic, DYNSELECT)
+ addPrimitive(JSDynamic_updateDynamic, DYNUPDATE)
+ addPrimitive(JSDynamic_applyDynamic, DYNAPPLY)
+ addPrimitive(JSDynamicLiteral_applyDynamicNamed, DYNLITN)
+ addPrimitive(JSDynamicLiteral_applyDynamic, DYNLIT)
+
+ addPrimitive(JSDictionary_delete, DICT_DEL)
+
+ addPrimitive(JSArray_create, ARR_CREATE)
+
+ val ntModule = getRequiredModule("scala.reflect.NameTransformer")
+
+ addPrimitive(JSPackage_typeOf, TYPEOF)
+ addPrimitive(JSPackage_debugger, DEBUGGER)
+ addPrimitive(JSPackage_undefined, UNDEFVAL)
+ addPrimitive(JSPackage_isUndefined, ISUNDEF)
+ addPrimitive(JSPackage_native, JS_NATIVE)
+
+ addPrimitive(JSObject_hasProperty, HASPROP)
+ addPrimitive(JSObject_properties, OBJPROPS)
+
+ addPrimitive(BoxedUnit_UNIT, UNITVAL)
+ addPrimitive(BoxedUnit_TYPE, UNITTYPE)
+
+ addPrimitive(getMember(RuntimePackageModule,
+ newTermName("environmentInfo")), ENV_INFO)
+ }
+
+ def isJavaScriptPrimitive(code: Int) =
+ code >= 300 && code < 360
+}
diff --git a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSTreeExtractors.scala b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSTreeExtractors.scala
new file mode 100644
index 0000000..a18ad88
--- /dev/null
+++ b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSTreeExtractors.scala
@@ -0,0 +1,66 @@
+/* Scala.js compiler
+ * Copyright 2013 LAMP/EPFL
+ * @author Tobias Schlatter
+ */
+
+package scala.scalajs.compiler
+
+import scala.annotation.tailrec
+
+import scala.scalajs.ir.Trees._
+import scala.scalajs.ir.Types._
+
+/** Useful extractors for JavaScript trees */
+object JSTreeExtractors {
+
+ object jse {
+ /**
+ * A literally named sequence (like in a call to applyDynamicNamed)
+ *
+ * Example (Scala): method(("name1", x), ("name2", y))
+ */
+ object LitNamed {
+ def unapply(exprs: List[Tree]) = unapply0(exprs, Nil)
+
+ @tailrec
+ private def unapply0(
+ exprs: List[Tree],
+ acc: List[(StringLiteral, Tree)]
+ ): Option[List[(StringLiteral, Tree)]] = exprs match {
+ case Tuple2(name: StringLiteral, value) :: xs =>
+ unapply0(xs, (name, value) :: acc)
+ case Nil => Some(acc.reverse)
+ case _ => None
+ }
+ }
+
+ /**
+ * A literal Tuple2
+ *
+ * Example (Scala): (x, y)
+ * But also (Scala): x -> y
+ */
+ object Tuple2 {
+ def unapply(tree: Tree): Option[(Tree, Tree)] = tree match {
+ // case (x, y)
+ case New(ClassType("T2"), Ident("init___O__O", _),
+ List(_1, _2)) =>
+ Some((_1, _2))
+ // case x -> y
+ case Apply(
+ LoadModule(ClassType("s_Predef$ArrowAssoc$")),
+ Ident("$$minus$greater$extension__O__O__T2", _),
+ List(
+ Apply(
+ LoadModule(ClassType("s_Predef$")),
+ Ident("any2ArrowAssoc__O__O", _),
+ List(_1)),
+ _2)) =>
+ Some((_1, _2))
+ case _ =>
+ None
+ }
+ }
+ }
+
+}
diff --git a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/PrepJSExports.scala b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/PrepJSExports.scala
new file mode 100644
index 0000000..9223061
--- /dev/null
+++ b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/PrepJSExports.scala
@@ -0,0 +1,251 @@
+/* Scala.js compiler
+ * Copyright 2013 LAMP/EPFL
+ * @author Tobias Schlatter
+ */
+
+package scala.scalajs.compiler
+
+import scala.annotation.tailrec
+
+import scala.tools.nsc.NoPhase
+
+/**
+ * Prepare export generation
+ *
+ * Helpers for transformation of @JSExport annotations
+ */
+trait PrepJSExports { this: PrepJSInterop =>
+
+ import global._
+ import jsAddons._
+ import definitions._
+ import jsDefinitions._
+
+ import scala.reflect.internal.Flags
+
+ /** Whether the given symbol has a visibility that allows exporting */
+ def hasLegalExportVisibility(sym: Symbol): Boolean =
+ sym.isPublic || sym.isProtected && !sym.isProtectedLocal
+
+ def genExportMember(ddef: DefDef): List[Tree] = {
+ val baseSym = ddef.symbol
+ val clsSym = baseSym.owner
+
+ val exports = jsInterop.exportsOf(baseSym)
+
+ // Helper function for errors
+ def err(msg: String) = { reporter.error(exports.head.pos, msg); Nil }
+ def memType = if (baseSym.isConstructor) "constructor" else "method"
+
+ 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 (scalaPrimitives.isPrimitive(baseSym))
+ err("You may not export a primitive")
+ 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 (currentRun.uncurryPhase == NoPhase) {
+ /* When using scaladoc, the uncurry phase does not exist. This makes
+ * our code down below blow up (see bug #323). So we do not do anything
+ * more here if the phase does not exist. It's no big deal because we do
+ * not need exports for scaladoc.
+ */
+ Nil
+ } 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 (!hasLegalExportVisibility(clsSym))
+ err("You may only export public and protected classes")
+ else if (clsSym.isLocalToBlock)
+ err("You may not export a local class")
+ else if (clsSym.isNestedClass)
+ err("You may not export a nested class. Create an exported factory " +
+ "method in the outer class to work around this limitation.")
+ else Nil
+
+ } else {
+ assert(!baseSym.isBridge)
+
+ // Reset interface flag: Any trait will contain non-empty methods
+ clsSym.resetFlag(Flags.INTERFACE)
+
+ // Actually generate exporter methods
+ exports.flatMap { exp =>
+ if (exp.isNamed)
+ genNamedExport(baseSym, exp.jsName, exp.pos) :: Nil
+ else
+ genExportDefs(baseSym, exp.jsName, exp.pos)
+ }
+ }
+ }
+
+ /** 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))
+
+ // 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)
+ expSym.removeAnnotation(JSExportNamedAnnotation)
+
+ // Add symbol to class
+ clsSym.info.decls.enter(expSym)
+
+ // Construct exporter DefDef tree
+ val exporter = genProxyDefDef(clsSym, defSym, 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)
+
+ exporter :: defaultGetters
+ }
+
+ /** Generate a dummy DefDef tree for a named export. This tree is captured
+ * by GenJSCode again to generate the required JavaScript logic.
+ */
+ private def genNamedExport(defSym: Symbol, jsName: String, pos: Position) = {
+ val clsSym = defSym.owner
+ val scalaName = jsInterop.scalaExportName(jsName, false)
+
+ // Create symbol for the new exporter method
+ val expSym = clsSym.newMethodSymbol(scalaName, pos,
+ Flags.SYNTHETIC | Flags.FINAL)
+
+ // Mark the symbol to be a named export
+ expSym.addAnnotation(JSExportNamedAnnotation)
+
+ // Create a single parameter of type Any
+ val param = expSym.newValueParameter(newTermName("namedArgs"), pos)
+ param.setInfo(AnyTpe)
+
+ // Set method type
+ expSym.setInfo(MethodType(param :: Nil, AnyClass.tpe))
+
+ // Register method to parent
+ clsSym.info.decls.enter(expSym)
+
+ // Placeholder tree
+ def ph = Ident(Predef_???)
+
+ // Create a call to the forwarded method with ??? as args
+ val sel: Tree = Select(This(clsSym), defSym)
+ val call = (sel /: defSym.paramss) {
+ (fun, params) => Apply(fun, List.fill(params.size)(ph))
+ }
+
+ // rhs is a block to prevent boxing of result
+ typer.typedDefDef(DefDef(expSym, Block(call, ph)))
+ }
+
+ private def genExportDefaultGetter(clsSym: Symbol, trgMethod: Symbol,
+ exporter: Symbol, paramPos: Int, pos: Position) = {
+
+ // Get default getter method we'll copy
+ val trgGetter =
+ clsSym.tpe.member(nme.defaultGetterName(trgMethod.name, paramPos))
+
+ assert(trgGetter.exists)
+
+ // Although the following must be true in a correct program, we cannot
+ // assert, since a graceful failure message is only generated later
+ if (!trgGetter.isOverloaded) {
+ val expGetter = trgGetter.cloneSymbol
+
+ expGetter.name = nme.defaultGetterName(exporter.name, paramPos)
+ expGetter.pos = pos
+
+ clsSym.info.decls.enter(expGetter)
+
+ genProxyDefDef(clsSym, 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) {
+
+ // 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 sel: Tree = Select(This(clsSym), trgSym)
+ val rhs = (sel /: proxySym.paramss) {
+ (fun,params) => Apply(fun, params map spliceParam)
+ }
+
+ typer.typedDefDef(DefDef(proxySym, rhs))
+ }
+
+ /** changes the return type of the method type tpe to Any. returns new type */
+ private def retToAny(tpe: Type): Type = tpe match {
+ case MethodType(params, result) => MethodType(params, retToAny(result))
+ case NullaryMethodType(result) => NullaryMethodType(AnyClass.tpe)
+ case PolyType(tparams, result) => PolyType(tparams, retToAny(result))
+ case _ => AnyClass.tpe
+ }
+
+ /** 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)
+ }
+
+}
diff --git a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/PrepJSInterop.scala b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/PrepJSInterop.scala
new file mode 100644
index 0000000..437576a
--- /dev/null
+++ b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/PrepJSInterop.scala
@@ -0,0 +1,621 @@
+/* Scala.js compiler
+ * Copyright 2013 LAMP/EPFL
+ * @author Tobias Schlatter
+ */
+
+package scala.scalajs.compiler
+
+import scala.tools.nsc
+import nsc._
+
+import scala.collection.immutable.ListMap
+import scala.collection.mutable
+
+/** Prepares classes extending js.Any for JavaScript interop
+ *
+ * This phase does:
+ * - Sanity checks for js.Any hierarchy
+ * - Annotate subclasses of js.Any to be treated specially
+ * - Rewrite calls to scala.Enumeration.Value (include name string)
+ * - Create JSExport methods: Dummy methods that are propagated
+ * through the whole compiler chain to mark exports. This allows
+ * exports to have the same semantics than methods.
+ *
+ * @author Tobias Schlatter
+ */
+abstract class PrepJSInterop extends plugins.PluginComponent
+ with PrepJSExports
+ with transform.Transform
+ with Compat210Component {
+ val jsAddons: JSGlobalAddons {
+ val global: PrepJSInterop.this.global.type
+ }
+
+ val scalaJSOpts: ScalaJSOptions
+
+ import global._
+ import jsAddons._
+ import definitions._
+ import rootMirror._
+ import jsDefinitions._
+
+ val phaseName = "jsinterop"
+
+ override def newPhase(p: nsc.Phase) = new JSInteropPhase(p)
+ class JSInteropPhase(prev: nsc.Phase) extends Phase(prev) {
+ override def name = phaseName
+ override def description = "Prepare ASTs for JavaScript interop"
+ override def run(): Unit = {
+ jsPrimitives.initPrepJSPrimitives()
+ super.run()
+ }
+ }
+
+ override protected def newTransformer(unit: CompilationUnit) =
+ new JSInteropTransformer(unit)
+
+ private object jsnme {
+ val hasNext = newTermName("hasNext")
+ val next = newTermName("next")
+ val nextName = newTermName("nextName")
+ val x = newTermName("x")
+ val Value = newTermName("Value")
+ val Val = newTermName("Val")
+ }
+
+ private object jstpnme {
+ val scala_ = newTypeName("scala") // not defined in 2.10's tpnme
+ }
+
+ class JSInteropTransformer(unit: CompilationUnit) extends Transformer {
+
+ // Force evaluation of JSDynamicLiteral: Strangely, we are unable to find
+ // nested objects in the JSCode phase (probably after flatten).
+ // Therefore we force the symbol of js.Dynamic.literal here in order to
+ // have access to it in JSCode.
+ JSDynamicLiteral
+
+ var inJSAnyMod = false
+ var inJSAnyCls = false
+ var inScalaCls = false
+ /** are we inside a subclass of scala.Enumeration */
+ var inScalaEnum = false
+ /** are we inside the implementation of scala.Enumeration? */
+ var inEnumImpl = false
+
+ def jsAnyClassOnly = !inJSAnyCls && allowJSAny
+ def allowImplDef = !inJSAnyCls && !inJSAnyMod
+ def allowJSAny = !inScalaCls
+ def inJSAny = inJSAnyMod || inJSAnyCls
+
+ /** DefDefs in class templates that export methods to JavaScript */
+ val exporters = mutable.Map.empty[Symbol, mutable.ListBuffer[Tree]]
+
+ override def transform(tree: Tree): Tree = postTransform { tree match {
+ // Catch special case of ClassDef in ModuleDef
+ case cldef: ClassDef if jsAnyClassOnly && isJSAny(cldef) =>
+ transformJSAny(cldef)
+
+ // Catch forbidden implDefs
+ case idef: ImplDef if !allowImplDef =>
+ reporter.error(idef.pos, "Traits, classes and objects extending " +
+ "js.Any may not have inner traits, classes or objects")
+ super.transform(tree)
+
+ // Handle js.Anys
+ case idef: ImplDef if isJSAny(idef) =>
+ transformJSAny(idef)
+
+ // Catch the definition of scala.Enumeration itself
+ case cldef: ClassDef if cldef.symbol == ScalaEnumClass =>
+ enterEnumImpl { super.transform(cldef) }
+
+ // Catch Scala Enumerations to transform calls to scala.Enumeration.Value
+ case cldef: ClassDef if isScalaEnum(cldef) =>
+ enterScalaCls {
+ enterScalaEnum {
+ super.transform(cldef)
+ }
+ }
+ case idef: ImplDef if isScalaEnum(idef) =>
+ enterScalaEnum { super.transform(idef) }
+
+ // Catch (Scala) ClassDefs to forbid js.Anys
+ case cldef: ClassDef =>
+ enterScalaCls { super.transform(cldef) }
+
+ // Catch ValorDefDef in js.Any
+ case vddef: ValOrDefDef if inJSAny =>
+ transformValOrDefDefInRawJSType(vddef)
+
+ // Catch ValDefs in enumerations with simple calls to Value
+ case ValDef(mods, name, tpt, ScalaEnumValue.NoName(optPar)) if inScalaEnum =>
+ val nrhs = ScalaEnumValName(tree.symbol.owner, tree.symbol, optPar)
+ treeCopy.ValDef(tree, mods, name, transform(tpt), nrhs)
+
+ // Catch Select on Enumeration.Value we couldn't transform but need to
+ // we ignore the implementation of scala.Enumeration itself
+ case ScalaEnumValue.NoName(_) if !inEnumImpl =>
+ reporter.warning(tree.pos,
+ """Couldn't transform call to Enumeration.Value.
+ |The resulting program is unlikely to function properly as this
+ |operation requires reflection.""".stripMargin)
+ super.transform(tree)
+
+ case ScalaEnumValue.NullName() if !inEnumImpl =>
+ reporter.warning(tree.pos,
+ """Passing null as name to Enumeration.Value
+ |requires reflection at runtime. The resulting
+ |program is unlikely to function properly.""".stripMargin)
+ super.transform(tree)
+
+ case ScalaEnumVal.NoName(_) if !inEnumImpl =>
+ reporter.warning(tree.pos,
+ """Calls to the non-string constructors of Enumeration.Val
+ |require reflection at runtime. The resulting
+ |program is unlikely to function properly.""".stripMargin)
+ super.transform(tree)
+
+ case ScalaEnumVal.NullName() if !inEnumImpl =>
+ reporter.warning(tree.pos,
+ """Passing null as name to a constructor of Enumeration.Val
+ |requires reflection at runtime. The resulting
+ |program is unlikely to function properly.""".stripMargin)
+ super.transform(tree)
+
+ // Catch calls to Predef.classOf[T]. These should NEVER reach this phase
+ // but unfortunately do. In normal cases, the typer phase replaces these
+ // calls by a literal constant of the given type. However, when we compile
+ // the scala library itself and Predef.scala is in the sources, this does
+ // not happen.
+ //
+ // The trees reach this phase under the form:
+ //
+ // scala.this.Predef.classOf[T]
+ //
+ // If we encounter such a tree, depending on the plugin options, we fail
+ // here or silently fix those calls.
+ case TypeApply(
+ classOfTree @ Select(Select(This(jstpnme.scala_), nme.Predef), nme.classOf),
+ List(tpeArg)) =>
+ if (scalaJSOpts.fixClassOf) {
+ // Replace call by literal constant containing type
+ if (typer.checkClassType(tpeArg)) {
+ typer.typed { Literal(Constant(tpeArg.tpe.dealias.widen)) }
+ } else {
+ reporter.error(tpeArg.pos, s"Type ${tpeArg} is not a class type")
+ EmptyTree
+ }
+ } else {
+ reporter.error(classOfTree.pos,
+ """This classOf resulted in an unresolved classOf in the jscode
+ |phase. This is most likely a bug in the Scala compiler. ScalaJS
+ |is probably able to work around this bug. Enable the workaround
+ |by passing the fixClassOf option to the plugin.""".stripMargin)
+ EmptyTree
+ }
+
+ // Exporter generation
+ case ddef: DefDef =>
+ // Generate exporters for this ddef if required
+ exporters.getOrElseUpdate(ddef.symbol.owner,
+ mutable.ListBuffer.empty) ++= genExportMember(ddef)
+
+ super.transform(tree)
+
+ // Module export sanity check (export generated in JSCode phase)
+ case modDef: ModuleDef =>
+ val sym = modDef.symbol
+
+ def condErr(msg: String) = {
+ for (exp <- jsInterop.exportsOf(sym)) {
+ reporter.error(exp.pos, msg)
+ }
+ }
+
+ if (!hasLegalExportVisibility(sym))
+ condErr("You may only export public and protected objects")
+ else if (sym.isLocalToBlock)
+ condErr("You may not export a local object")
+ else if (!sym.owner.hasPackageFlag)
+ condErr("You may not export a nested object")
+
+ super.transform(modDef)
+
+ // Fix for issue with calls to js.Dynamic.x()
+ // Rewrite (obj: js.Dynamic).x(...) to obj.applyDynamic("x")(...)
+ case Select(Select(trg, jsnme.x), nme.apply) if isJSDynamic(trg) =>
+ val newTree = atPos(tree.pos) {
+ Apply(
+ Select(super.transform(trg), newTermName("applyDynamic")),
+ List(Literal(Constant("x")))
+ )
+ }
+ typer.typed(newTree, Mode.FUNmode, tree.tpe)
+
+
+ // Fix for issue with calls to js.Dynamic.x()
+ // Rewrite (obj: js.Dynamic).x to obj.selectDynamic("x")
+ case Select(trg, jsnme.x) if isJSDynamic(trg) =>
+ val newTree = atPos(tree.pos) {
+ Apply(
+ Select(super.transform(trg), newTermName("selectDynamic")),
+ List(Literal(Constant("x")))
+ )
+ }
+ typer.typed(newTree, Mode.FUNmode, tree.tpe)
+
+ case _ => super.transform(tree)
+ } }
+
+ private def postTransform(tree: Tree) = tree match {
+ case Template(parents, self, body) =>
+ val clsSym = tree.symbol.owner
+ val exports = exporters.get(clsSym).toIterable.flatten
+ // Add exports to the template
+ treeCopy.Template(tree, parents, self, body ++ exports)
+
+ case memDef: MemberDef =>
+ val sym = memDef.symbol
+ if (sym.isLocalToBlock && !sym.owner.isCaseApplyOrUnapply) {
+ // We exclude case class apply (and unapply) to work around SI-8826
+ for (exp <- jsInterop.exportsOf(sym)) {
+ val msg = {
+ val base = "You may not export a local definition"
+ if (sym.owner.isPrimaryConstructor)
+ base + ". To export a (case) class field, use the " +
+ "meta-annotation scala.annotation.meta.field like this: " +
+ "@(JSExport @field)."
+ else
+ base
+ }
+ reporter.error(exp.pos, msg)
+ }
+ }
+ memDef
+
+ case _ => tree
+ }
+
+ /**
+ * Performs checks and rewrites specific to classes / objects extending
+ * js.Any
+ */
+ private def transformJSAny(implDef: ImplDef) = {
+ val sym = implDef.symbol
+
+ lazy val badParent = sym.info.parents.find(t => !(t <:< JSAnyClass.tpe))
+ val inScalaJSJSPackage =
+ sym.enclosingPackage == ScalaJSJSPackage ||
+ sym.enclosingPackage == ScalaJSJSPrimPackage
+
+ implDef match {
+ // Check that we do not have a case modifier
+ case _ if implDef.mods.hasFlag(Flag.CASE) =>
+ reporter.error(implDef.pos, "Classes and objects extending " +
+ "js.Any may not have a case modifier")
+
+ // Check that we do not extends a trait that does not extends js.Any
+ case _ if !inScalaJSJSPackage && !badParent.isEmpty &&
+ !isJSLambda(sym) =>
+ val badName = badParent.get.typeSymbol.fullName
+ reporter.error(implDef.pos, s"${sym.nameString} extends ${badName} " +
+ "which does not extend js.Any.")
+
+ // Check that we are not an anonymous class
+ case cldef: ClassDef
+ if cldef.symbol.isAnonymousClass && !isJSLambda(sym) =>
+ reporter.error(implDef.pos, "Anonymous classes may not " +
+ "extend js.Any")
+
+ // Check if we may have a js.Any here
+ case cldef: ClassDef if !allowJSAny && !jsAnyClassOnly &&
+ !isJSLambda(sym) =>
+ reporter.error(implDef.pos, "Classes extending js.Any may not be " +
+ "defined inside a class or trait")
+
+ case _: ModuleDef if !allowJSAny =>
+ reporter.error(implDef.pos, "Objects extending js.Any may not be " +
+ "defined inside a class or trait")
+
+ case _ if sym.isLocalToBlock && !isJSLambda(sym) =>
+ reporter.error(implDef.pos, "Local classes and objects may not " +
+ "extend js.Any")
+
+ // Check that this is not a class extending js.GlobalScope
+ case _: ClassDef if isJSGlobalScope(implDef) &&
+ implDef.symbol != JSGlobalScopeClass =>
+ reporter.error(implDef.pos, "Only objects may extend js.GlobalScope")
+
+ case _ =>
+ // We cannot use sym directly, since the symbol
+ // of a module is not its type's symbol but the value it declares
+ val tSym = sym.tpe.typeSymbol
+
+ tSym.setAnnotations(rawJSAnnot :: sym.annotations)
+
+ }
+
+ if (implDef.isInstanceOf[ModuleDef])
+ enterJSAnyMod { super.transform(implDef) }
+ else
+ enterJSAnyCls { super.transform(implDef) }
+ }
+
+ /** Verify a ValOrDefDef inside a js.Any */
+ private def transformValOrDefDefInRawJSType(tree: ValOrDefDef) = {
+ val sym = tree.symbol
+
+ val exports = jsInterop.exportsOf(sym)
+
+ if (exports.nonEmpty) {
+ val memType = if (sym.isConstructor) "constructor" else "method"
+ reporter.error(exports.head.pos,
+ s"You may not export a $memType of a subclass of js.Any")
+ }
+
+ if (isNonJSScalaSetter(sym)) {
+ // Forbid setters with non-unit return type
+ reporter.error(tree.pos, "Setters that do not return Unit are " +
+ "not allowed in types extending js.Any")
+ }
+
+ if (sym.hasAnnotation(NativeAttr)) {
+ // Native methods are not allowed
+ reporter.error(tree.pos, "Methods in a js.Any may not be @native")
+ }
+
+ for {
+ annot <- sym.getAnnotation(JSNameAnnotation)
+ if annot.stringArg(0).isEmpty
+ } {
+ reporter.error(annot.pos,
+ "The argument to JSName must be a literal string")
+ }
+
+ if (sym.isPrimaryConstructor || sym.isValueParameter ||
+ sym.isParamWithDefault || sym.isAccessor && !sym.isDeferred ||
+ sym.isParamAccessor || sym.isSynthetic ||
+ AllJSFunctionClasses.contains(sym.owner)) {
+ /* Ignore (i.e. allow) primary ctor, parameters, default parameter
+ * getters, accessors, param accessors, synthetic methods (to avoid
+ * double errors with case classes, e.g. generated copy method) and
+ * js.Functions and js.ThisFunctions (they need abstract methods for SAM
+ * treatment.
+ */
+ } else if (jsPrimitives.isJavaScriptPrimitive(sym)) {
+ // Force rhs of a primitive to be `sys.error("stub")` except for the
+ // js.native primitive which displays an elaborate error message
+ if (sym != JSPackage_native) {
+ tree.rhs match {
+ case Apply(trg, Literal(Constant("stub")) :: Nil)
+ if trg.symbol == definitions.Sys_error =>
+ case _ =>
+ reporter.error(tree.pos,
+ "The body of a primitive must be `sys.error(\"stub\")`.")
+ }
+ }
+ } else if (sym.isConstructor) {
+ // Force secondary ctor to have only a call to the primary ctor inside
+ tree.rhs match {
+ case Block(List(Apply(trg, _)), Literal(Constant(())))
+ if trg.symbol.isPrimaryConstructor &&
+ trg.symbol.owner == sym.owner =>
+ // everything is fine here
+ case _ =>
+ reporter.error(tree.pos, "A secondary constructor of a class " +
+ "extending js.Any may only call the primary constructor")
+ }
+ } else {
+ // Check that the tree's body is either empty or calls js.native
+ tree.rhs match {
+ case sel: Select if sel.symbol == JSPackage_native =>
+ case _ =>
+ val pos = if (tree.rhs != EmptyTree) tree.rhs.pos else tree.pos
+ reporter.warning(pos, "Members of traits, classes and objects " +
+ "extending js.Any may only contain members that call js.native. " +
+ "This will be enforced in 1.0.")
+ }
+
+ if (sym.tpe.resultType.typeSymbol == NothingClass &&
+ tree.tpt.asInstanceOf[TypeTree].original == null) {
+ // Warn if resultType is Nothing and not ascribed
+ val name = sym.name.decoded.trim
+ reporter.warning(tree.pos, s"The type of $name got inferred " +
+ "as Nothing. To suppress this warning, explicitly ascribe " +
+ "the type.")
+ }
+ }
+
+ super.transform(tree)
+ }
+
+ private def enterJSAnyCls[T](body: =>T) = {
+ val old = inJSAnyCls
+ inJSAnyCls = true
+ val res = body
+ inJSAnyCls = old
+ res
+ }
+
+ private def enterJSAnyMod[T](body: =>T) = {
+ val old = inJSAnyMod
+ inJSAnyMod = true
+ val res = body
+ inJSAnyMod = old
+ res
+ }
+
+ private def enterScalaCls[T](body: =>T) = {
+ val old = inScalaCls
+ inScalaCls = true
+ val res = body
+ inScalaCls = old
+ res
+ }
+
+ private def enterScalaEnum[T](body: =>T) = {
+ val old = inScalaEnum
+ inScalaEnum = true
+ val res = body
+ inScalaEnum = old
+ res
+ }
+
+ private def enterEnumImpl[T](body: =>T) = {
+ val old = inEnumImpl
+ inEnumImpl = true
+ val res = body
+ inEnumImpl = old
+ res
+ }
+
+ }
+
+ def isJSAny(sym: Symbol): Boolean =
+ sym.tpe.typeSymbol isSubClass JSAnyClass
+
+ private def isJSAny(implDef: ImplDef): Boolean = isJSAny(implDef.symbol)
+
+ private def isJSGlobalScope(implDef: ImplDef) =
+ implDef.symbol.tpe.typeSymbol isSubClass JSGlobalScopeClass
+
+ private def isJSLambda(sym: Symbol) = sym.isAnonymousClass &&
+ AllJSFunctionClasses.exists(sym.tpe.typeSymbol isSubClass _)
+
+ private def isScalaEnum(implDef: ImplDef) =
+ implDef.symbol.tpe.typeSymbol isSubClass ScalaEnumClass
+
+ private def isJSDynamic(tree: Tree) = tree.tpe.typeSymbol == JSDynamicClass
+
+ /**
+ * is this symbol a setter that has a non-unit return type
+ *
+ * these setters don't make sense in JS (in JS, assignment returns
+ * the assigned value) and are therefore not allowed in facade types
+ */
+ private def isNonJSScalaSetter(sym: Symbol) = nme.isSetterName(sym.name) && {
+ sym.tpe.paramss match {
+ case List(List(arg)) =>
+ !isScalaRepeatedParamType(arg.tpe) &&
+ sym.tpe.resultType.typeSymbol != UnitClass
+ case _ => false
+ }
+ }
+
+ trait ScalaEnumFctExtractors {
+ protected val methSym: Symbol
+
+ protected def resolve(ptpes: Symbol*) = {
+ val res = methSym suchThat {
+ _.tpe.params.map(_.tpe.typeSymbol) == ptpes.toList
+ }
+ assert(res != NoSymbol)
+ res
+ }
+
+ protected val noArg = resolve()
+ protected val nameArg = resolve(StringClass)
+ protected val intArg = resolve(IntClass)
+ protected val fullMeth = resolve(IntClass, StringClass)
+
+ /**
+ * Extractor object for calls to the targeted symbol that do not have an
+ * explicit name in the parameters
+ *
+ * Extracts:
+ * - `sel: Select` where sel.symbol is targeted symbol (no arg)
+ * - Apply(meth, List(param)) where meth.symbol is targeted symbol (i: Int)
+ */
+ object NoName {
+ def unapply(t: Tree) = t match {
+ case sel: Select if sel.symbol == noArg =>
+ Some(None)
+ case Apply(meth, List(param)) if meth.symbol == intArg =>
+ Some(Some(param))
+ case _ =>
+ None
+ }
+ }
+
+ object NullName {
+ def unapply(tree: Tree) = tree match {
+ case Apply(meth, List(Literal(Constant(null)))) =>
+ meth.symbol == nameArg
+ case Apply(meth, List(_, Literal(Constant(null)))) =>
+ meth.symbol == fullMeth
+ case _ => false
+ }
+ }
+
+ }
+
+ private object ScalaEnumValue extends {
+ protected val methSym = getMemberMethod(ScalaEnumClass, jsnme.Value)
+ } with ScalaEnumFctExtractors
+
+ private object ScalaEnumVal extends {
+ protected val methSym = {
+ val valSym = getMemberClass(ScalaEnumClass, jsnme.Val)
+ valSym.tpe.member(nme.CONSTRUCTOR)
+ }
+ } with ScalaEnumFctExtractors
+
+ /**
+ * Construct a call to Enumeration.Value
+ * @param thisSym ClassSymbol of enclosing class
+ * @param nameOrig Symbol of ValDef where this call will be placed
+ * (determines the string passed to Value)
+ * @param intParam Optional tree with Int passed to Value
+ * @return Typed tree with appropriate call to Value
+ */
+ private def ScalaEnumValName(
+ thisSym: Symbol,
+ nameOrig: Symbol,
+ intParam: Option[Tree]) = {
+
+ val defaultName = nameOrig.asTerm.getterName.encoded
+
+
+ // Construct the following tree
+ //
+ // if (nextName != null && nextName.hasNext)
+ // nextName.next()
+ // else
+ // <defaultName>
+ //
+ val nextNameTree = Select(This(thisSym), jsnme.nextName)
+ val nullCompTree =
+ Apply(Select(nextNameTree, nme.NE), Literal(Constant(null)) :: Nil)
+ val hasNextTree = Select(nextNameTree, jsnme.hasNext)
+ val condTree = Apply(Select(nullCompTree, nme.ZAND), hasNextTree :: Nil)
+ val nameTree = If(condTree,
+ Apply(Select(nextNameTree, jsnme.next), Nil),
+ Literal(Constant(defaultName)))
+ val params = intParam.toList :+ nameTree
+
+ typer.typed {
+ Apply(Select(This(thisSym), jsnme.Value), params)
+ }
+ }
+
+ private def rawJSAnnot =
+ Annotation(RawJSTypeAnnot.tpe, List.empty, ListMap.empty)
+
+ private lazy val ScalaEnumClass = getRequiredClass("scala.Enumeration")
+
+ /** checks if the primary constructor of the ClassDef `cldef` does not
+ * take any arguments
+ */
+ private def primCtorNoArg(cldef: ClassDef) =
+ getPrimCtor(cldef.symbol.tpe).map(_.paramss == List(List())).getOrElse(true)
+
+ /** return the MethodSymbol of the primary constructor of the given type
+ * if it exists
+ */
+ private def getPrimCtor(tpe: Type) =
+ tpe.declaration(nme.CONSTRUCTOR).alternatives.collectFirst {
+ case ctor: MethodSymbol if ctor.isPrimaryConstructor => ctor
+ }
+
+}
diff --git a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/ScalaJSOptions.scala b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/ScalaJSOptions.scala
new file mode 100644
index 0000000..72912bf
--- /dev/null
+++ b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/ScalaJSOptions.scala
@@ -0,0 +1,30 @@
+/* Scala.js compiler
+ * Copyright 2013 LAMP/EPFL
+ * @author Tobias Schlatter
+ */
+
+package scala.scalajs.compiler
+
+import java.net.URI
+
+/** This trait allows to query all options to the ScalaJS plugin
+ *
+ * Also see the help text in ScalaJSPlugin for information about particular
+ * options.
+ */
+trait ScalaJSOptions {
+ import ScalaJSOptions.URIMap
+
+ /** should calls to Predef.classOf[T] be fixed in the jsinterop phase.
+ * If false, bad calls to classOf will cause an error. */
+ def fixClassOf: Boolean
+
+ /** which source locations in source maps should be relativized (or where
+ * should they be mapped to)? */
+ def sourceURIMaps: List[URIMap]
+
+}
+
+object ScalaJSOptions {
+ case class URIMap(from: URI, to: Option[URI])
+}
diff --git a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/ScalaJSPlugin.scala b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/ScalaJSPlugin.scala
new file mode 100644
index 0000000..c3916ab
--- /dev/null
+++ b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/ScalaJSPlugin.scala
@@ -0,0 +1,143 @@
+/* Scala.js compiler
+ * Copyright 2013 LAMP/EPFL
+ * @author Sébastien Doeraene
+ */
+
+package scala.scalajs.compiler
+
+import scala.tools.nsc._
+import scala.tools.nsc.plugins.{
+ Plugin => NscPlugin, PluginComponent => NscPluginComponent
+}
+import scala.collection.{ mutable, immutable }
+
+import java.net.{ URI, URISyntaxException }
+
+import scala.scalajs.ir.Trees
+
+/** Main entry point for the Scala.js compiler plugin
+ *
+ * @author Sébastien Doeraene
+ */
+class ScalaJSPlugin(val global: Global) extends NscPlugin {
+ import global._
+
+ val name = "scalajs"
+ val description = "Compile to JavaScript"
+ val components = {
+ if (global.forScaladoc)
+ List[NscPluginComponent](PrepInteropComponent)
+ else
+ List[NscPluginComponent](PrepInteropComponent, GenCodeComponent)
+ }
+
+ /** Called when the JS ASTs are generated. Override for testing */
+ def generatedJSAST(clDefs: List[Trees.Tree]): Unit = {}
+
+ /** Addons for JavaScript platform */
+ object jsAddons extends {
+ val global: ScalaJSPlugin.this.global.type = ScalaJSPlugin.this.global
+ } with JSGlobalAddons with Compat210Component
+
+ object scalaJSOpts extends ScalaJSOptions {
+ import ScalaJSOptions.URIMap
+ var fixClassOf: Boolean = false
+ lazy val sourceURIMaps: List[URIMap] = {
+ if (_sourceURIMaps.nonEmpty)
+ _sourceURIMaps.reverse
+ else
+ relSourceMap.toList.map(URIMap(_, absSourceMap))
+ }
+ var _sourceURIMaps: List[URIMap] = Nil
+ var relSourceMap: Option[URI] = None
+ var absSourceMap: Option[URI] = None
+ }
+
+ object PrepInteropComponent extends {
+ val global: ScalaJSPlugin.this.global.type = ScalaJSPlugin.this.global
+ val jsAddons: ScalaJSPlugin.this.jsAddons.type = ScalaJSPlugin.this.jsAddons
+ val scalaJSOpts = ScalaJSPlugin.this.scalaJSOpts
+ override val runsAfter = List("typer")
+ override val runsBefore = List("pickle")
+ } with PrepJSInterop
+
+ object GenCodeComponent extends {
+ val global: ScalaJSPlugin.this.global.type = ScalaJSPlugin.this.global
+ val jsAddons: ScalaJSPlugin.this.jsAddons.type = ScalaJSPlugin.this.jsAddons
+ val scalaJSOpts = ScalaJSPlugin.this.scalaJSOpts
+ override val runsAfter = List("mixin")
+ override val runsBefore = List("delambdafy", "cleanup", "terminal")
+ } with GenJSCode {
+ def generatedJSAST(clDefs: List[Trees.Tree]) =
+ ScalaJSPlugin.this.generatedJSAST(clDefs)
+ }
+
+ override def processOptions(options: List[String],
+ error: String => Unit): Unit = {
+ import ScalaJSOptions.URIMap
+ import scalaJSOpts._
+
+ for (option <- options) {
+ if (option == "fixClassOf") {
+ fixClassOf = true
+
+ } else if (option.startsWith("mapSourceURI:")) {
+ val uris = option.stripPrefix("mapSourceURI:").split("->")
+
+ if (uris.length != 1 && uris.length != 2) {
+ error("relocateSourceMap needs one or two URIs as argument.")
+ } else {
+ try {
+ val from = new URI(uris.head)
+ val to = uris.lift(1).map(str => new URI(str))
+ _sourceURIMaps ::= URIMap(from, to)
+ } catch {
+ case e: URISyntaxException =>
+ error(s"${e.getInput} is not a valid URI")
+ }
+ }
+
+ // The following options are deprecated (how do we show this to the user?)
+ } else if (option.startsWith("relSourceMap:")) {
+ val uriStr = option.stripPrefix("relSourceMap:")
+ try { relSourceMap = Some(new URI(uriStr)) }
+ catch {
+ case e: URISyntaxException => error(s"$uriStr is not a valid URI")
+ }
+ } else if (option.startsWith("absSourceMap:")) {
+ val uriStr = option.stripPrefix("absSourceMap:")
+ try { absSourceMap = Some(new URI(uriStr)) }
+ catch {
+ case e: URISyntaxException => error(s"$uriStr is not a valid URI")
+ }
+ } else {
+ error("Option not understood: " + option)
+ }
+ }
+
+ // Verify constraints
+ if (_sourceURIMaps.nonEmpty && relSourceMap.isDefined)
+ error("You may not use mapSourceURI and relSourceMap together. " +
+ "Use another mapSourceURI option without second URI.")
+ else if (_sourceURIMaps.nonEmpty && absSourceMap.isDefined)
+ error("You may not use mapSourceURI and absSourceMap together. " +
+ "Use another mapSourceURI option.")
+ else if (absSourceMap.isDefined && relSourceMap.isEmpty)
+ error("absSourceMap requires the use of relSourceMap")
+ }
+
+ override val optionsHelp: Option[String] = Some(s"""
+ | -P:$name:mapSourceURI:FROM_URI[->TO_URI]
+ | change the location the source URIs in the emitted IR point to
+ | - strips away the prefix FROM_URI (if it matches)
+ | - optionally prefixes the TO_URI, where stripping has been performed
+ | - any number of occurences are allowed. Processing is done on a first match basis.
+ | -P:$name:fixClassOf repair calls to Predef.classOf that reach ScalaJS
+ | WARNING: This is a tremendous hack! Expect ugly errors if you use this option.
+ |Deprecated options
+ | -P:$name:relSourceMap:<URI> relativize emitted source maps with <URI>
+ | -P:$name:absSourceMap:<URI> absolutize emitted source maps with <URI>
+ | This option requires the use of relSourceMap
+ """.stripMargin)
+
+}
diff --git a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/TypeKinds.scala b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/TypeKinds.scala
new file mode 100644
index 0000000..774be68
--- /dev/null
+++ b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/TypeKinds.scala
@@ -0,0 +1,252 @@
+/* Scala.js compiler
+ * Copyright 2013 LAMP/EPFL
+ * @author Sébastien Doeraene
+ */
+
+package scala.scalajs.compiler
+
+import scala.tools.nsc._
+
+import scala.scalajs.ir
+import ir.{Definitions, Types}
+
+/** Glue representation of types as seen from the IR but still with a
+ * reference to the Symbols.
+ *
+ * @author Sébastien Doeraene
+ */
+trait TypeKinds extends SubComponent { this: GenJSCode =>
+ import global._
+ import jsAddons._
+ import definitions._
+
+ lazy val ObjectReference = REFERENCE(definitions.ObjectClass)
+
+ lazy val VoidKind = VOID
+ lazy val BooleanKind = BOOL
+ lazy val CharKind = INT(CharClass)
+ lazy val ByteKind = INT(ByteClass)
+ lazy val ShortKind = INT(ShortClass)
+ lazy val IntKind = INT(IntClass)
+ lazy val LongKind = LONG
+ lazy val FloatKind = FLOAT(FloatClass)
+ lazy val DoubleKind = FLOAT(DoubleClass)
+
+ /** TypeKinds for Scala primitive types. */
+ lazy val primitiveTypeMap: Map[Symbol, TypeKind] = {
+ import definitions._
+ Map(
+ UnitClass -> VoidKind,
+ BooleanClass -> BooleanKind,
+ CharClass -> CharKind,
+ ByteClass -> ByteKind,
+ ShortClass -> ShortKind,
+ IntClass -> IntKind,
+ LongClass -> LongKind,
+ FloatClass -> FloatKind,
+ DoubleClass -> DoubleKind
+ )
+ }
+
+ /** Glue representation of types as seen from the IR but still with a
+ * reference to the Symbols.
+ */
+ sealed abstract class TypeKind {
+ def isReferenceType = false
+ def isArrayType = false
+ def isValueType = false
+
+ def toIRType: Types.Type
+ def toReferenceType: Types.ReferenceType
+ }
+
+ sealed abstract class TypeKindButArray extends TypeKind {
+ protected def typeSymbol: Symbol
+
+ override def toReferenceType: Types.ClassType =
+ Types.ClassType(encodeClassFullName(typeSymbol))
+ }
+
+ /** The void, for trees that can only appear in statement position. */
+ case object VOID extends TypeKindButArray {
+ protected def typeSymbol = UnitClass
+ def toIRType: Types.NoType.type = Types.NoType
+ }
+
+ sealed abstract class ValueTypeKind extends TypeKindButArray {
+ override def isValueType = true
+
+ val primitiveCharCode: Char = typeSymbol match {
+ case BooleanClass => 'Z'
+ case CharClass => 'C'
+ case ByteClass => 'B'
+ case ShortClass => 'S'
+ case IntClass => 'I'
+ case LongClass => 'J'
+ case FloatClass => 'F'
+ case DoubleClass => 'D'
+ case x => abort("Unknown primitive type: " + x.fullName)
+ }
+ }
+
+ /** Integer number (Byte, Short, Char or Int). */
+ case class INT private[TypeKinds] (typeSymbol: Symbol) extends ValueTypeKind {
+ def toIRType: Types.IntType.type = Types.IntType
+ }
+
+ /** Long */
+ case object LONG extends ValueTypeKind {
+ protected def typeSymbol = definitions.LongClass
+ def toIRType: Types.LongType.type = Types.LongType
+ }
+
+ /** Floating-point number (Float or Double). */
+ case class FLOAT private[TypeKinds] (typeSymbol: Symbol) extends ValueTypeKind {
+ def toIRType: Types.Type =
+ if (typeSymbol == FloatClass) Types.FloatType
+ else Types.DoubleType
+ }
+
+ /** Boolean */
+ case object BOOL extends ValueTypeKind {
+ protected def typeSymbol = definitions.BooleanClass
+ def toIRType: Types.BooleanType.type = Types.BooleanType
+ }
+
+ /** Nothing */
+ case object NOTHING extends TypeKindButArray {
+ protected def typeSymbol = definitions.NothingClass
+ def toIRType: Types.NothingType.type = Types.NothingType
+ override def toReferenceType: Types.ClassType =
+ Types.ClassType(Definitions.RuntimeNothingClass)
+ }
+
+ /** Null */
+ case object NULL extends TypeKindButArray {
+ protected def typeSymbol = definitions.NullClass
+ def toIRType: Types.NullType.type = Types.NullType
+ override def toReferenceType: Types.ClassType =
+ Types.ClassType(Definitions.RuntimeNullClass)
+ }
+
+ /** An object */
+ case class REFERENCE private[TypeKinds] (typeSymbol: Symbol) extends TypeKindButArray {
+ override def toString(): String = "REFERENCE(" + typeSymbol.fullName + ")"
+ override def isReferenceType = true
+
+ def toIRType: Types.Type = encodeClassType(typeSymbol)
+ }
+
+ /** An array */
+ case class ARRAY private[TypeKinds] (elem: TypeKind) extends TypeKind {
+ override def toString = "ARRAY[" + elem + "]"
+ override def isArrayType = true
+
+ def dimensions: Int = elem match {
+ case a: ARRAY => a.dimensions + 1
+ case _ => 1
+ }
+
+ override def toIRType: Types.ArrayType = toReferenceType
+
+ override def toReferenceType: Types.ArrayType = {
+ Types.ArrayType(
+ elementKind.toReferenceType.className,
+ dimensions)
+ }
+
+ /** The ultimate element type of this array. */
+ def elementKind: TypeKindButArray = elem match {
+ case a: ARRAY => a.elementKind
+ case k: TypeKindButArray => k
+ }
+ }
+
+ ////////////////// Conversions //////////////////////////////
+
+ def toIRType(t: Type): Types.Type =
+ toTypeKind(t).toIRType
+
+ def toReferenceType(t: Type): Types.ReferenceType =
+ toTypeKind(t).toReferenceType
+
+ // The following code is a hard copy-and-paste from backend.icode.TypeKinds
+
+ /** Return the TypeKind of the given type
+ *
+ * Call to .normalize fixes #3003 (follow type aliases). Otherwise,
+ * arrayOrClassType below would return ObjectReference.
+ */
+ def toTypeKind(t: Type): TypeKind = t.normalize match {
+ case ThisType(ArrayClass) => ObjectReference
+ case ThisType(sym) => newReference(sym)
+ case SingleType(_, sym) => primitiveOrRefType(sym)
+ case ConstantType(_) => toTypeKind(t.underlying)
+ case TypeRef(_, sym, args) => primitiveOrClassType(sym, args)
+ case ClassInfoType(_, _, ArrayClass) => abort("ClassInfoType to ArrayClass!")
+ case ClassInfoType(_, _, sym) => primitiveOrRefType(sym)
+
+ // !!! Iulian says types which make no sense after erasure should not reach here,
+ // which includes the ExistentialType, AnnotatedType, RefinedType. I don't know
+ // if the first two cases exist because they do or as a defensive measure, but
+ // at the time I added it, RefinedTypes were indeed reaching here.
+ // !!! Removed in JavaScript backend because I do not know what to do with lub
+ //case ExistentialType(_, t) => toTypeKind(t)
+ // Apparently, this case does occur (see pos/CustomGlobal.scala)
+ case t: AnnotatedType => toTypeKind(t.underlying)
+ //case RefinedType(parents, _) => parents map toTypeKind reduceLeft lub
+
+ /* This case is not in scalac. We need it for the test
+ * 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 => newReference(tpe.valueClazz)
+
+ // For sure WildcardTypes shouldn't reach here either, but when
+ // debugging such situations this may come in handy.
+ // case WildcardType => REFERENCE(ObjectClass)
+ case norm => abort(
+ "Unknown type: %s, %s [%s, %s] TypeRef? %s".format(
+ t, norm, t.getClass, norm.getClass, t.isInstanceOf[TypeRef]
+ )
+ )
+ }
+
+ /** Return the type kind of a class, possibly an array type.
+ */
+ private def arrayOrClassType(sym: Symbol, targs: List[Type]) = sym match {
+ case ArrayClass => ARRAY(toTypeKind(targs.head))
+ case _ if sym.isClass => newReference(sym)
+ case _ =>
+ assert(sym.isType, sym) // it must be compiling Array[a]
+ ObjectReference
+ }
+
+ /** Interfaces have to be handled delicately to avoid introducing
+ * spurious errors, but if we treat them all as AnyRef we lose too
+ * much information.
+ */
+ private def newReference(sym: Symbol): TypeKind = sym match {
+ case NothingClass => NOTHING
+ case NullClass => NULL
+ case _ =>
+ // Can't call .toInterface (at this phase) or we trip an assertion.
+ // See PackratParser#grow for a method which fails with an apparent mismatch
+ // between "object PackratParsers$class" and "trait PackratParsers"
+ if (sym.isImplClass) {
+ // 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)
+ REFERENCE(traitSym)
+ else
+ REFERENCE(sym)
+ } else {
+ REFERENCE(sym)
+ }
+ }
+
+ private def primitiveOrRefType(sym: Symbol) =
+ primitiveTypeMap.getOrElse(sym, newReference(sym))
+ private def primitiveOrClassType(sym: Symbol, targs: List[Type]) =
+ primitiveTypeMap.getOrElse(sym, arrayOrClassType(sym, targs))
+}
diff --git a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/util/ScopedVar.scala b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/util/ScopedVar.scala
new file mode 100644
index 0000000..3924955
--- /dev/null
+++ b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/util/ScopedVar.scala
@@ -0,0 +1,38 @@
+package scala.scalajs.compiler.util
+
+import language.implicitConversions
+
+class ScopedVar[A](init: A) {
+ import ScopedVar.Assignment
+
+ private var value = init
+
+ def this()(implicit ev: Null <:< A) = this(ev(null))
+
+ def get: A = value
+ def :=(newValue: A): Assignment[A] = new Assignment(this, newValue)
+}
+
+object ScopedVar {
+ class Assignment[T](scVar: ScopedVar[T], value: T) {
+ private[ScopedVar] def push(): AssignmentStackElement[T] = {
+ val stack = new AssignmentStackElement(scVar, scVar.value)
+ scVar.value = value
+ stack
+ }
+ }
+
+ private class AssignmentStackElement[T](scVar: ScopedVar[T], oldValue: T) {
+ private[ScopedVar] def pop(): Unit = {
+ scVar.value = oldValue
+ }
+ }
+
+ implicit def toValue[T](scVar: ScopedVar[T]): T = scVar.get
+
+ def withScopedVars[T](ass: Assignment[_]*)(body: => T): T = {
+ val stack = ass.map(_.push())
+ try body
+ finally stack.reverse.foreach(_.pop())
+ }
+}
diff --git a/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/DiverseErrorsTest.scala b/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/DiverseErrorsTest.scala
new file mode 100644
index 0000000..0fe10f8
--- /dev/null
+++ b/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/DiverseErrorsTest.scala
@@ -0,0 +1,31 @@
+package scala.scalajs.compiler.test
+
+import scala.scalajs.compiler.test.util._
+import org.junit.Test
+
+class DiverseErrorsTest extends DirectTest with TestHelpers {
+
+ override def preamble =
+ """import scala.scalajs.js
+ """
+
+ @Test
+ def noIsInstanceOnJSRaw = {
+
+ """
+ trait JSRaw extends js.Object
+
+ class A {
+ val a: AnyRef = "asdf"
+ def x = a.isInstanceOf[JSRaw]
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:7: error: isInstanceOf[JSRaw] not supported because it is a raw JS trait
+ | def x = a.isInstanceOf[JSRaw]
+ | ^
+ """
+
+ }
+
+}
diff --git a/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/EnumerationInteropTest.scala b/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/EnumerationInteropTest.scala
new file mode 100644
index 0000000..e186cf4
--- /dev/null
+++ b/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/EnumerationInteropTest.scala
@@ -0,0 +1,135 @@
+package scala.scalajs.compiler.test
+
+import scala.scalajs.compiler.test.util._
+
+import org.junit.Test
+
+class EnumerationInteropTest extends DirectTest with TestHelpers {
+
+ @Test
+ def warnIfUnableToTransformValue = {
+
+ """
+ class A extends Enumeration {
+ val a = {
+ println("oh, oh!")
+ Value
+ }
+ val b = {
+ println("oh, oh!")
+ Value(4)
+ }
+ }
+ """ hasWarns
+ """
+ |newSource1.scala:5: warning: Couldn't transform call to Enumeration.Value.
+ |The resulting program is unlikely to function properly as this
+ |operation requires reflection.
+ | Value
+ | ^
+ |newSource1.scala:9: warning: Couldn't transform call to Enumeration.Value.
+ |The resulting program is unlikely to function properly as this
+ |operation requires reflection.
+ | Value(4)
+ | ^
+ """
+
+ }
+
+ @Test
+ def warnIfNoNameVal = {
+
+ """
+ class A extends Enumeration {
+ val a = new Val
+ val b = new Val(10)
+ }
+ """ hasWarns
+ """
+ |newSource1.scala:3: warning: Calls to the non-string constructors of Enumeration.Val
+ |require reflection at runtime. The resulting
+ |program is unlikely to function properly.
+ | val a = new Val
+ | ^
+ |newSource1.scala:4: warning: Calls to the non-string constructors of Enumeration.Val
+ |require reflection at runtime. The resulting
+ |program is unlikely to function properly.
+ | val b = new Val(10)
+ | ^
+ """
+
+ }
+
+ @Test
+ def warnIfNullValue = {
+
+ """
+ class A extends Enumeration {
+ val a = Value(null)
+ val b = Value(10, null)
+ }
+ """ hasWarns
+ """
+ |newSource1.scala:3: warning: Passing null as name to Enumeration.Value
+ |requires reflection at runtime. The resulting
+ |program is unlikely to function properly.
+ | val a = Value(null)
+ | ^
+ |newSource1.scala:4: warning: Passing null as name to Enumeration.Value
+ |requires reflection at runtime. The resulting
+ |program is unlikely to function properly.
+ | val b = Value(10, null)
+ | ^
+ """
+
+ }
+
+ @Test
+ def warnIfNullNewVal = {
+
+ """
+ class A extends Enumeration {
+ val a = new Val(null)
+ val b = new Val(10, null)
+ }
+ """ hasWarns
+ """
+ |newSource1.scala:3: warning: Passing null as name to a constructor of Enumeration.Val
+ |requires reflection at runtime. The resulting
+ |program is unlikely to function properly.
+ | val a = new Val(null)
+ | ^
+ |newSource1.scala:4: warning: Passing null as name to a constructor of Enumeration.Val
+ |requires reflection at runtime. The resulting
+ |program is unlikely to function properly.
+ | val b = new Val(10, null)
+ | ^
+ """
+
+ }
+
+ @Test
+ def warnIfExtNoNameVal = {
+
+ """
+ class A extends Enumeration {
+ protected class Val1 extends Val
+ protected class Val2 extends Val(1)
+ }
+ """ warns() // no message checking: position differs in 2.10 and 2.11
+
+ }
+
+ @Test
+ def warnIfExtNullNameVal = {
+
+ """
+ class A extends Enumeration {
+ protected class Val1 extends Val(null)
+ protected class Val2 extends Val(1,null)
+ }
+ """ warns() // no message checking: position differs in 2.10 and 2.11
+
+ }
+
+}
diff --git a/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/JSDynamicLiteralTest.scala b/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/JSDynamicLiteralTest.scala
new file mode 100644
index 0000000..bc1a1b4
--- /dev/null
+++ b/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/JSDynamicLiteralTest.scala
@@ -0,0 +1,102 @@
+package scala.scalajs.compiler.test
+
+import scala.scalajs.compiler.test.util._
+import org.junit.Test
+
+class JSDynamicLiteralTest extends DirectTest with TestHelpers {
+
+ override def preamble =
+ """import scala.scalajs.js.Dynamic.{ literal => lit }
+ """
+
+ @Test
+ def callApplyOnly = {
+
+ // selectDynamic (with any name)
+ expr"""
+ lit.helloWorld
+ """.fails() // Scala error, no string checking due to versions
+
+ // applyDynamicNamed with wrong method name
+ expr"""
+ lit.helloWorld(a = "a")
+ """ hasErrors
+ """
+ |newSource1.scala:3: error: js.Dynamic.literal does not have a method named helloWorld
+ | lit.helloWorld(a = "a")
+ | ^
+ """
+
+ // applyDynamic with wrong method name
+ expr"""
+ lit.helloWorld("a" -> "a")
+ """ hasErrors
+ """
+ |newSource1.scala:3: error: js.Dynamic.literal does not have a method named helloWorld
+ | lit.helloWorld("a" -> "a")
+ | ^
+ """
+
+ }
+
+ @Test
+ def goodTypesOnly = {
+
+ // Bad value type (applyDynamic)
+ """
+ class A {
+ val x = new Object()
+ def foo = lit("a" -> x)
+ }
+ """.fails()
+
+ // Bad key type (applyDynamic)
+ """
+ class A {
+ val x = Seq()
+ def foo = lit(x -> "a")
+ }
+ """.fails()
+
+ // Bad value type (applyDynamicNamed)
+ """
+ class A {
+ val x = new Object()
+ def foo = lit(a = x)
+ }
+ """.fails()
+
+ }
+
+ @Test
+ def noNonLiteralMethodName = {
+
+ // applyDynamicNamed
+ """
+ class A {
+ val x = "string"
+ def foo = lit.applyDynamicNamed(x)()
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:5: error: js.Dynamic.literal.applyDynamicNamed may not be called directly
+ | def foo = lit.applyDynamicNamed(x)()
+ | ^
+ """
+
+ // applyDynamic
+ """
+ class A {
+ val x = "string"
+ def foo = lit.applyDynamic(x)()
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:5: error: js.Dynamic.literal.applyDynamic may not be called directly
+ | def foo = lit.applyDynamic(x)()
+ | ^
+ """
+
+ }
+
+}
diff --git a/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/JSExportASTTest.scala b/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/JSExportASTTest.scala
new file mode 100644
index 0000000..4a2b1af
--- /dev/null
+++ b/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/JSExportASTTest.scala
@@ -0,0 +1,38 @@
+package scala.scalajs.compiler.test
+
+import util._
+
+import org.junit.Test
+import org.junit.Assert._
+
+import scala.scalajs.ir.{Trees => js}
+
+class JSExportASTTest extends JSASTTest {
+
+ @Test
+ def inheritExportMethods: Unit = {
+
+ var props = 0
+
+ """
+ import scala.scalajs.js.annotation.JSExport
+
+ class A {
+ @JSExport
+ def foo = 1
+ }
+
+ class B extends A {
+ @JSExport
+ override def foo = 2
+ }
+ """.traverse {
+ case js.PropertyDef(js.StringLiteral("foo"), _, _, _) =>
+ props += 1
+ }
+
+ assertEquals("Only define the property `foo` once", props, 1)
+
+ }
+
+}
diff --git a/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/JSExportTest.scala b/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/JSExportTest.scala
new file mode 100644
index 0000000..c675420
--- /dev/null
+++ b/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/JSExportTest.scala
@@ -0,0 +1,745 @@
+package scala.scalajs.compiler.test
+
+import scala.scalajs.compiler.test.util._
+import org.junit.Test
+import org.junit.Ignore
+
+class JSExportTest extends DirectTest with TestHelpers {
+
+ override def preamble =
+ """import scala.scalajs.js.annotation._
+ """
+
+ @Test
+ def noDoubleUnderscoreExport = {
+ // Normal exports
+ """
+ class A {
+ @JSExport(name = "__")
+ def foo = 1
+
+ @JSExport
+ def bar__(x: Int) = x
+ }
+
+ @JSExport
+ class B__
+ """ hasErrors
+ """
+ |newSource1.scala:4: error: An exported name may not contain a double underscore (`__`)
+ | @JSExport(name = "__")
+ | ^
+ |newSource1.scala:8: error: An exported name may not contain a double underscore (`__`)
+ | def bar__(x: Int) = x
+ | ^
+ |newSource1.scala:12: error: An exported name may not contain a double underscore (`__`)
+ | class B__
+ | ^
+ """
+
+ // Inherited exports (objects)
+ """
+ @JSExportDescendentObjects
+ trait A
+
+ package fo__o {
+ object B extends A
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:7: error: B may not have a double underscore (`__`) in its fully qualified
+ |name, since it is forced to be exported by a @JSExportDescendentObjects on trait A
+ | object B extends A
+ | ^
+ """
+
+ // Inherited exports (classes)
+ """
+ @JSExportDescendentClasses
+ trait A
+
+ package fo__o {
+ class B(x: Int) extends A {
+ def this() = this(1)
+ private def this(s: String) = this(1)
+ }
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:7: error: B may not have a double underscore (`__`) in its fully qualified
+ |name, since it is forced to be exported by a @JSExportDescendentClasses on trait A
+ | class B(x: Int) extends A {
+ | ^
+ |newSource1.scala:8: error: B may not have a double underscore (`__`) in its fully qualified
+ |name, since it is forced to be exported by a @JSExportDescendentClasses on trait A
+ | def this() = this(1)
+ | ^
+ """
+ }
+
+ @Test
+ def noConflictingExport = {
+ """
+ class Confl {
+ @JSExport("value")
+ def hello = "foo"
+
+ @JSExport("value")
+ def world = "bar"
+ }
+ """ fails() // No error test, Scala version dependent error messages
+
+ """
+ class Confl {
+ class Box[T](val x: T)
+
+ @JSExport
+ def ub(x: Box[String]): String = x.x
+ @JSExport
+ def ub(x: Box[Int]): Int = x.x
+ }
+ """ fails() // No error test, Scala version dependent error messages
+
+ """
+ class Confl {
+ @JSExport
+ def rtType(x: scala.scalajs.js.prim.Number) = x
+
+ @JSExport
+ def rtType(x: Double) = x
+ }
+ """ fails() // Error message depends on Scala version
+
+ """
+ class Confl {
+ @JSExport
+ def foo(x: Int)(ys: Int*) = x
+
+ @JSExport
+ def foo(x: Int*) = x
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:7: error: Cannot disambiguate overloads for exported method $js$exported$meth$foo with types
+ | (x: Seq)Object
+ | (x: Int, ys: Seq)Object
+ | @JSExport
+ | ^
+ """
+
+ """
+ class Confl {
+ @JSExport
+ def foo(x: Int = 1) = x
+ @JSExport
+ def foo(x: String*) = x
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:4: error: Cannot disambiguate overloads for exported method $js$exported$meth$foo with types
+ | (x: Int)Object
+ | (x: Seq)Object
+ | @JSExport
+ | ^
+ """
+
+ """
+ class Confl {
+ @JSExport
+ def foo(x: scala.scalajs.js.prim.Number, y: String)(z: Int = 1) = x
+ @JSExport
+ def foo(x: Double, y: String)(z: String*) = x
+ }
+ """ fails() // Error message depends on Scala version
+
+ """
+ class A {
+ @JSExport
+ def a(x: scala.scalajs.js.Any) = 1
+
+ @JSExport
+ def a(x: Any) = 2
+ }
+ """ fails() // Error message depends on Scala version
+
+ }
+
+ @Test
+ def noExportLocal = {
+ // Local class
+ """
+ class A {
+ def method = {
+ @JSExport
+ class A
+ }
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:5: error: You may not export a local class
+ | @JSExport
+ | ^
+ """
+
+ // Local object
+ """
+ class A {
+ def method = {
+ @JSExport
+ object A
+ }
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:5: error: You may not export a local object
+ | @JSExport
+ | ^
+ """
+
+ // Local method
+ """
+ class A {
+ def method = {
+ @JSExport
+ def foo = 1
+ }
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:5: error: You may not export a local definition
+ | @JSExport
+ | ^
+ """
+
+ // Local val
+ """
+ class A {
+ def method = {
+ @JSExport
+ val x = 1
+ }
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:5: error: You may not export a local definition
+ | @JSExport
+ | ^
+ """
+
+ // Local var
+ """
+ class A {
+ def method = {
+ @JSExport
+ var x = 1
+ }
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:5: error: You may not export a local definition
+ | @JSExport
+ | ^
+ """
+
+ }
+
+ @Test
+ def infoExportLocal = {
+
+ """
+ class A(@JSExport val x: Int)
+ """ hasErrors
+ """
+ |newSource1.scala:3: error: You may not export a local definition. To export a (case) class field, use the meta-annotation scala.annotation.meta.field like this: @(JSExport @field).
+ | class A(@JSExport val x: Int)
+ | ^
+ """
+
+ }
+
+ @Test
+ def noMiddleVarArg = {
+
+ """
+ class A {
+ @JSExport
+ def method(xs: Int*)(ys: String) = 1
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:4: error: In an exported method, a *-parameter must come last (through all parameter lists)
+ | @JSExport
+ | ^
+ """
+
+ }
+
+ @Test
+ def noMiddleDefaultParam = {
+
+ """
+ class A {
+ @JSExport
+ def method(x: Int = 1)(y: String) = 1
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:4: error: In an exported method, all parameters with defaults must be at the end
+ | @JSExport
+ | ^
+ """
+
+ }
+
+ @Test
+ def noExportTrait = {
+
+ """
+ @JSExport
+ trait Test
+ """ hasErrors
+ """
+ |newSource1.scala:3: error: You may not export a trait
+ | @JSExport
+ | ^
+ """
+
+ }
+
+ @Test
+ def noExportNonPublicClassOrObject = {
+
+ """
+ @JSExport
+ private class A
+
+ @JSExport
+ protected[this] class B
+ """ hasErrors
+ """
+ |newSource1.scala:3: error: You may only export public and protected classes
+ | @JSExport
+ | ^
+ |newSource1.scala:6: error: You may only export public and protected classes
+ | @JSExport
+ | ^
+ """
+
+ """
+ @JSExport
+ private object A
+
+ @JSExport
+ protected[this] object B
+ """ hasErrors
+ """
+ |newSource1.scala:3: error: You may only export public and protected objects
+ | @JSExport
+ | ^
+ |newSource1.scala:6: error: You may only export public and protected objects
+ | @JSExport
+ | ^
+ """
+
+ }
+
+ @Test
+ def noExportNonPublicMember = {
+
+ """
+ class A {
+ @JSExport
+ private def foo = 1
+
+ @JSExport
+ protected[this] def bar = 2
+ }
+ """ 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 = {
+
+ """
+ class A {
+ @JSExport
+ class Nested
+ }
+ """ 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.
+ | @JSExport
+ | ^
+ """
+
+ """
+ object A {
+ @JSExport
+ class Nested
+ }
+ """ 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.
+ | @JSExport
+ | ^
+ """
+
+ }
+
+ @Test
+ def noExportNestedObject = {
+
+ """
+ class A {
+ @JSExport
+ object Nested
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:4: error: You may not export a nested object
+ | @JSExport
+ | ^
+ """
+
+ """
+ object A {
+ @JSExport
+ object Nested
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:4: error: You may not export a nested object
+ | @JSExport
+ | ^
+ """
+
+ }
+
+ @Test
+ def noExportJSRaw = {
+
+ """
+ import scala.scalajs.js
+
+ @JSExport
+ object A extends js.Object
+ """ hasErrors
+ """
+ |newSource1.scala:5: error: You may not export a class extending js.Any
+ | @JSExport
+ | ^
+ """
+
+ """
+ import scala.scalajs.js
+
+ @JSExport
+ class A extends js.Object
+ """ hasErrors
+ """
+ |newSource1.scala:5: error: You may not export a constructor of a subclass of js.Any
+ | @JSExport
+ | ^
+ """
+
+ }
+
+ @Test
+ def noExportJSRawMember = {
+
+ """
+ import scala.scalajs.js
+
+ class A extends js.Object {
+ @JSExport
+ def foo: Int = js.native
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:6: error: You may not export a method of a subclass of js.Any
+ | @JSExport
+ | ^
+ """
+
+ }
+
+ @Test
+ def noBadSetterType = {
+
+ // Bad param list
+ """
+ class A {
+ @JSExport
+ def foo_=(x: Int, y: Int) = ()
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:4: error: A method ending in _= will be exported as setter. But foo_= does not have the right signature to do so (single argument, unit return type).
+ | @JSExport
+ | ^
+ """
+
+ // Bad return type
+ """
+ class A {
+ @JSExport
+ def foo_=(x: Int) = "string"
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:4: error: A method ending in _= will be exported as setter. But foo_= does not have the right signature to do so (single argument, unit return type).
+ | @JSExport
+ | ^
+ """
+
+ }
+
+ @Test
+ def noBadToStringExport = {
+
+ """
+ class A {
+ @JSExport("toString")
+ def a(): Int = 5
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:4: error: You may not export a zero-argument method named other than 'toString' under the name 'toString'
+ | @JSExport("toString")
+ | ^
+ """
+
+ }
+
+ @Test
+ def noBadNameExportAll = {
+
+ """
+ @JSExportAll
+ class A {
+ val __f = 1
+ def a_= = 2
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:5: error: An exported name may not contain a double underscore (`__`)
+ | val __f = 1
+ | ^
+ |newSource1.scala:3: error: A method ending in _= will be exported as setter. But a_= does not have the right signature to do so (single argument, unit return type).
+ | @JSExportAll
+ | ^
+ """
+
+ }
+
+ @Test
+ def noConflictingMethodAndProperty = {
+
+ // Basic case
+ """
+ class A {
+ @JSExport("a")
+ def bar() = 2
+
+ @JSExport("a")
+ val foo = 1
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:7: error: Exported method a conflicts with A.$js$exported$prop$a
+ | @JSExport("a")
+ | ^
+ |newSource1.scala:4: error: Exported property a conflicts with A.$js$exported$meth$a
+ | @JSExport("a")
+ | ^
+ """
+
+ // Inherited case
+ """
+ class A {
+ @JSExport("a")
+ def bar() = 2
+ }
+
+ class B extends A {
+ @JSExport("a")
+ def foo_=(x: Int): Unit = ()
+
+ @JSExport("a")
+ val foo = 1
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:4: error: Exported property a conflicts with A.$js$exported$meth$a
+ | @JSExport("a")
+ | ^
+ """
+
+ }
+
+ @Test
+ def noOverrideNamedExport = {
+
+ """
+ class A {
+ @JSExportNamed
+ def foo(x: Int, y: Int) = 1
+ }
+
+ class B extends A {
+ @JSExportNamed
+ override def foo(x: Int, y: Int) = 2
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:9: error: overriding method $js$exported$meth$foo in class A of type (namedArgs: Any)Any;
+ | method $js$exported$meth$foo cannot override final member
+ | @JSExportNamed
+ | ^
+ """
+
+ }
+
+ @Test
+ def noConflictNamedExport = {
+
+ // Normal method
+ """
+ class A {
+ @JSExportNamed
+ def foo(x: Int, y: Int) = 1
+
+ @JSExport
+ def foo(x: scala.scalajs.js.Any) = 2
+ }
+ """ fails() // No error test, Scala version dependent error messages
+
+ // Ctors
+ """
+ class A {
+ @JSExportNamed
+ def this(x: Int) = this()
+
+ @JSExport
+ def this(x: scala.scalajs.js.Any) = this
+
+ @JSExportNamed
+ def this(x: Long) = this()
+ }
+ """ fails() // No error test, Scala version dependent error messages
+
+ }
+
+ @Test
+ def noNamedExportObject = {
+
+ """
+ @JSExportNamed
+ object A
+ """ hasErrors
+ """
+ |newSource1.scala:3: error: You may not use @JSNamedExport on an object
+ | @JSExportNamed
+ | ^
+ """
+
+ }
+
+ @Test
+ def noNamedExportVarArg = {
+
+ """
+ class A {
+ @JSExportNamed
+ def foo(a: Int*) = 1
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:4: error: You may not name-export a method with a *-parameter
+ | @JSExportNamed
+ | ^
+ """
+
+ }
+
+ @Test
+ def noNamedExportProperty = {
+
+ // Getter
+ """
+ class A {
+ @JSExportNamed
+ def a = 1
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:4: error: You may not export a getter or a setter as a named export
+ | @JSExportNamed
+ | ^
+ """
+
+
+ // Setter
+ """
+ class A {
+ @JSExportNamed
+ def a_=(x: Int) = ()
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:4: error: You may not export a getter or a setter as a named export
+ | @JSExportNamed
+ | ^
+ """
+
+ }
+
+ @Test
+ def gracefulDoubleDefaultFail = {
+ // This used to blow up (i.e. not just fail), because PrepJSExports asked
+ // for the symbol of the default parameter getter of [[y]], and asserted its
+ // not overloaded. Since the Scala compiler only fails later on this, the
+ // assert got triggered and made the compiler crash
+ """
+ class A {
+ @JSExport
+ def foo(x: String, y: String = "hello") = x
+ def foo(x: Int, y: String = "bar") = x
+ }
+ """ fails()
+ }
+
+ @Test
+ def noNonLiteralExportNames = {
+
+ """
+ object A {
+ val a = "Hello"
+ final val b = "World"
+ }
+
+ class B {
+ @JSExport(A.a)
+ def foo = 1
+ @JSExport(A.b)
+ def bar = 1
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:9: error: The argument to JSExport must be a literal string
+ | @JSExport(A.a)
+ | ^
+ """
+
+ }
+
+}
diff --git a/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/JSInteropTest.scala b/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/JSInteropTest.scala
new file mode 100644
index 0000000..99c274f
--- /dev/null
+++ b/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/JSInteropTest.scala
@@ -0,0 +1,350 @@
+package scala.scalajs.compiler.test
+
+import scala.scalajs.compiler.test.util._
+
+import org.junit.Test
+import org.junit.Ignore
+
+class JSInteropTest extends DirectTest with TestHelpers {
+
+ override def preamble =
+ """import scala.scalajs.js
+ """
+
+ @Test
+ def noInnerClassTraitObject: Unit = {
+
+ val objs = List("class", "trait", "object")
+
+ for {
+ outer <- objs
+ inner <- objs
+ } yield {
+ s"""
+ $outer A extends js.Object {
+ $inner A
+ }
+ """ hasErrors
+ s"""
+ |newSource1.scala:4: error: Traits, classes and objects extending js.Any may not have inner traits, classes or objects
+ | $inner A
+ | ${" " * inner.length}^
+ """
+ }
+
+ }
+
+ @Test
+ def noBadSetters = {
+
+ """
+ class A extends js.Object {
+ def foo_=(x: Int): Int = js.native
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:4: error: Setters that do not return Unit are not allowed in types extending js.Any
+ | def foo_=(x: Int): Int = js.native
+ | ^
+ """
+
+ }
+
+ @Test
+ def onlyJSRawTraits = {
+
+ """
+ trait A
+ class B extends js.Object with A
+ """ hasErrors
+ """
+ |newSource1.scala:4: error: B extends A which does not extend js.Any.
+ | class B extends js.Object with A
+ | ^
+ """
+
+ """
+ trait A
+ class B extends js.Object with Serializable
+ """ hasErrors
+ """
+ |newSource1.scala:4: error: B extends scala.Serializable which does not extend js.Any.
+ | class B extends js.Object with Serializable
+ | ^
+ """
+
+ }
+
+ @Test
+ def noAnonymousClass = {
+
+ """
+ class A {
+ val x = new js.Object {
+ def a: Int = js.native
+ }
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:4: error: Anonymous classes may not extend js.Any
+ | val x = new js.Object {
+ | ^
+ """
+
+ }
+
+ @Test
+ def noCaseClassObject = {
+
+ """
+ case class A(x: Int) extends js.Object
+ """ hasErrors
+ """
+ |newSource1.scala:3: error: Classes and objects extending js.Any may not have a case modifier
+ | case class A(x: Int) extends js.Object
+ | ^
+ """
+
+ """
+ case object B extends js.Object
+ """ hasErrors
+ """
+ |newSource1.scala:3: error: Classes and objects extending js.Any may not have a case modifier
+ | case object B extends js.Object
+ | ^
+ """
+
+ }
+
+ @Test
+ def notNested: Unit = {
+
+ val outers = List("class", "trait")
+ val inners = List("trait", "class", "object")
+
+ for {
+ outer <- outers
+ inner <- inners
+ } yield {
+
+ val errTrg = if (inner == "object") "Objects" else "Classes"
+
+ s"""
+ $outer A {
+ $inner Inner extends js.Object
+ }
+ """ hasErrors
+ s"""
+ |newSource1.scala:4: error: $errTrg extending js.Any may not be defined inside a class or trait
+ | $inner Inner extends js.Object
+ | ${" " * inner.length}^
+ """
+ }
+
+ }
+
+ @Test
+ def noGlobalScopeClass = {
+
+ """
+ class A extends js.GlobalScope
+ """ hasErrors
+ """
+ |newSource1.scala:3: error: Only objects may extend js.GlobalScope
+ | class A extends js.GlobalScope
+ | ^
+ """
+
+ """
+ trait A extends js.GlobalScope
+ """ hasErrors
+ """
+ |newSource1.scala:3: error: Only objects may extend js.GlobalScope
+ | trait A extends js.GlobalScope
+ | ^
+ """
+
+ }
+
+ @Test
+ def noLocalClass = {
+
+ """
+ object A {
+ def a = {
+ class B extends js.Object
+ }
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:5: error: Local classes and objects may not extend js.Any
+ | class B extends js.Object
+ | ^
+ """
+
+ }
+
+ @Test
+ def noLocalObject = {
+
+ """
+ object A {
+ def a = {
+ object B extends js.Object
+ }
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:5: error: Local classes and objects may not extend js.Any
+ | object B extends js.Object
+ | ^
+ """
+
+ }
+
+ @Test
+ def noExtendJSAny = {
+
+ """
+ class A extends js.Any
+ """ hasErrors
+ """
+ |newSource1.scala:3: error: illegal inheritance from sealed trait Any
+ | class A extends js.Any
+ | ^
+ """
+
+ }
+
+ @Test
+ def noNativeInJSAny = {
+
+ """
+ class A extends js.Object {
+ @native
+ def value: Int = js.native
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:5: error: Methods in a js.Any may not be @native
+ | def value: Int = js.native
+ | ^
+ """
+
+ }
+
+ @Test
+ def warnJSAnyBody = {
+
+ """
+ class A extends js.Object {
+ def value: Int = ???
+ val x: Int = ???
+ }
+ """ hasWarns
+ """
+ |newSource1.scala:4: warning: Members of traits, classes and objects extending js.Any may only contain members that call js.native. This will be enforced in 1.0.
+ | def value: Int = ???
+ | ^
+ |newSource1.scala:5: warning: Members of traits, classes and objects extending js.Any may only contain members that call js.native. This will be enforced in 1.0.
+ | val x: Int = ???
+ | ^
+ """
+
+ """
+ trait A extends js.Object {
+ def value: Int
+ val x: Int
+ }
+ """ hasWarns
+ """
+ |newSource1.scala:4: warning: Members of traits, classes and objects extending js.Any may only contain members that call js.native. This will be enforced in 1.0.
+ | def value: Int
+ | ^
+ |newSource1.scala:5: warning: Members of traits, classes and objects extending js.Any may only contain members that call js.native. This will be enforced in 1.0.
+ | val x: Int
+ | ^
+ """
+
+ }
+
+ @Test
+ def noCallSecondaryCtor = {
+
+ """
+ class A(x: Int, y: Int) extends js.Object {
+ def this(x: Int) = this(x, 5)
+ def this() = this(7)
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:5: error: A secondary constructor of a class extending js.Any may only call the primary constructor
+ | def this() = this(7)
+ | ^
+ """
+
+ }
+
+ @Test
+ def noUseJsNative = {
+
+ """
+ class A {
+ def foo = js.native
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:4: error: js.native may only be used as stub implementation in facade types
+ | def foo = js.native
+ | ^
+ """
+
+ }
+
+ @Test
+ def warnNothingRaw = {
+
+ """
+ class A extends js.Object {
+ def foo = js.native
+ val bar = js.native
+ }
+ """ hasWarns
+ """
+ |newSource1.scala:4: warning: The type of foo got inferred as Nothing. To suppress this warning, explicitly ascribe the type.
+ | def foo = js.native
+ | ^
+ |newSource1.scala:5: warning: The type of bar got inferred as Nothing. To suppress this warning, explicitly ascribe the type.
+ | val bar = js.native
+ | ^
+ """
+
+ }
+
+ @Test
+ def noNonLiteralJSName = {
+
+ """
+ import js.annotation.JSName
+
+ object A {
+ val a = "Hello"
+ final val b = "World"
+ }
+
+ class B extends js.Object {
+ @JSName(A.a)
+ def foo: Int = js.native
+ @JSName(A.b)
+ def bar: Int = js.native
+ }
+ """ hasErrors
+ """
+ |newSource1.scala:11: error: The argument to JSName must be a literal string
+ | @JSName(A.a)
+ | ^
+ """
+
+ }
+
+}
diff --git a/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/OptimizationTest.scala b/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/OptimizationTest.scala
new file mode 100644
index 0000000..7f15c7a
--- /dev/null
+++ b/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/OptimizationTest.scala
@@ -0,0 +1,109 @@
+package scala.scalajs.compiler.test
+
+import util._
+
+import org.junit.Test
+
+import scala.scalajs.ir.{Trees => js, Types => jstpe}
+
+class OptimizationTest extends JSASTTest {
+
+ @Test
+ def unwrapScalaFunWrapper: Unit = {
+
+ // Make sure we do not wrap and unwrap right away
+ """
+ import scala.scalajs.js
+
+ class A {
+ val jsFun: js.Function = (x: Int) => x * 2
+ }
+ """.
+ hasNot("runtime.AnonFunction ctor") {
+ case js.New(jstpe.ClassType("sjsr_AnonFunction1"), _, _) =>
+ }
+
+ // Make sure our wrapper matcher has the right name
+ """
+ import scala.scalajs.js
+
+ class A {
+ val scalaFun = (x: Int) => x * 2
+ val jsFun: js.Function = scalaFun
+ }
+ """.
+ has("runtime.AnonFunction ctor") {
+ case js.New(jstpe.ClassType("sjsr_AnonFunction1"), _, _) =>
+ }
+
+ /* Make sure js.Array(...) is optimized away completely for several kinds
+ * of data types.
+ */
+ """
+ import scala.scalajs.js
+
+ class VC(val x: Int) extends AnyVal
+
+ class A {
+ val a = js.Array(5, 7, 9, -3)
+ val b = js.Array("hello", "world")
+ val c = js.Array('a', 'b')
+ val d = js.Array(Nil)
+ val e = js.Array(new VC(151189))
+ }
+ """.
+ hasNot("any of the wrapArray methods") {
+ case js.Apply(_, js.Ident(name, _), _)
+ if name.startsWith("wrap") && name.endsWith("__scm_WrappedArray") =>
+ }
+
+ /* Make sure varargs are optimized to use js.WrappedArray instead of
+ * scm.WrappedArray, for various data types.
+ */
+ """
+ import scala.scalajs.js
+
+ class VC(val x: Int) extends AnyVal
+
+ class A {
+ val a = List(5, 7, 9, -3)
+ val b = List("hello", "world")
+ val c = List('a', 'b')
+ val d = List(Nil)
+ val e = List(new VC(151189))
+ }
+ """.
+ hasNot("any of the wrapArray methods") {
+ case js.Apply(_, js.Ident(name, _), _)
+ if name.startsWith("wrap") && name.endsWith("__scm_WrappedArray") =>
+ }
+
+ // Make sure our wrapper matcher has the right name
+ """
+ import scala.scalajs.js
+
+ class A {
+ val a: Seq[Int] = new Array[Int](5)
+ }
+ """.
+ has("one of the wrapArray methods") {
+ case js.Apply(_, js.Ident(name, _), _)
+ if name.startsWith("wrap") && name.endsWith("__scm_WrappedArray") =>
+ }
+
+ // Verify the optimized emitted code for 'new js.Object' and 'new js.Array'
+ """
+ import scala.scalajs.js
+
+ class A {
+ val o = new js.Object
+ val a = new js.Array
+ }
+ """.
+ hasNot("any reference to the global scope") {
+ case js.JSEnvInfo() =>
+ }
+
+ }
+
+}
diff --git a/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/PositionTest.scala b/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/PositionTest.scala
new file mode 100644
index 0000000..e25399b
--- /dev/null
+++ b/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/PositionTest.scala
@@ -0,0 +1,37 @@
+package scala.scalajs.compiler.test
+
+import util.JSASTTest
+
+import org.junit.Test
+import org.junit.Assert._
+
+import scala.reflect.internal.util.BatchSourceFile
+
+import scala.scalajs.ir.{Trees => js}
+
+class PositionTest extends JSASTTest {
+
+ @Test
+ def virtualFilePosition = {
+
+ val name = "<foo with illegal URI chars: %%>"
+ val source = new BatchSourceFile(name,
+ """class A { def x = 1 }""")
+
+ var found = false
+ sourceAST(source) traverse {
+ case lit: js.IntLiteral =>
+ found = true
+ assertEquals(
+ "Scheme of virtual file URI should be `virtualfile'",
+ "virtualfile", lit.pos.source.getScheme)
+ assertEquals(
+ "Scheme specific part of virtual file URI should be its path",
+ name, lit.pos.source.getSchemeSpecificPart)
+ }
+
+ assertTrue("Should have IntLiteral tree", found)
+
+ }
+
+}
diff --git a/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/util/DirectTest.scala b/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/util/DirectTest.scala
new file mode 100644
index 0000000..8289129
--- /dev/null
+++ b/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/util/DirectTest.scala
@@ -0,0 +1,70 @@
+package scala.scalajs.compiler.test.util
+
+import scala.tools.nsc._
+import reporters.{Reporter, ConsoleReporter}
+import scala.reflect.internal.util.{ SourceFile, BatchSourceFile }
+
+import scala.scalajs.compiler.ScalaJSPlugin
+
+import scala.collection.mutable
+
+/** This is heavily inspired by scala's partest suite's DirectTest */
+abstract class DirectTest {
+
+ /** these arguments are always added to the args passed to newSettings */
+ def extraArgs: List[String] = Nil
+
+ /** create settings objects for test from arg string */
+ def newSettings(args: List[String]) = {
+ val s = new Settings
+ s processArguments (args, true)
+ s
+ }
+
+ def newScalaJSCompiler(args: String*): Global = {
+ val settings = newSettings(
+ List(
+ "-d", testOutputPath,
+ "-bootclasspath", scalaLibPath,
+ "-classpath", scalaJSLibPath) ++
+ extraArgs ++ args.toList)
+
+ lazy val global: Global = new Global(settings, newReporter(settings)) {
+ override lazy val plugins = newScalaJSPlugin(global) :: Nil
+ }
+
+ global
+ }
+
+ def newScalaJSPlugin(global: Global): ScalaJSPlugin =
+ new ScalaJSPlugin(global)
+
+ def newReporter(settings: Settings) = new ConsoleReporter(settings)
+
+ def newSources(codes: String*) = codes.toList.zipWithIndex map {
+ case (src, idx) => new BatchSourceFile(s"newSource${idx + 1}.scala", src)
+ }
+
+ def withRun[T](global: Global)(f: global.Run => T): T = {
+ global.reporter.reset()
+ f(new global.Run)
+ }
+
+ def compileSources(global: Global)(sources: SourceFile*): Boolean = {
+ withRun(global)(_ compileSources sources.toList)
+ !global.reporter.hasErrors
+ }
+
+ def compileString(global: Global)(sourceCode: String): Boolean =
+ compileSources(global)(newSources(sourceCode): _*)
+
+ def compileString(sourceCode: String): Boolean =
+ compileString(defaultGlobal)(sourceCode)
+
+ lazy val defaultGlobal = newScalaJSCompiler()
+
+ def testOutputPath = sys.props("scala.scalajs.compiler.test.output")
+ def scalaJSLibPath = sys.props("scala.scalajs.compiler.test.scalajslib")
+ def scalaLibPath = sys.props("scala.scalajs.compiler.test.scalalib")
+
+}
diff --git a/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/util/JSASTTest.scala b/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/util/JSASTTest.scala
new file mode 100644
index 0000000..d3dfd75
--- /dev/null
+++ b/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/util/JSASTTest.scala
@@ -0,0 +1,100 @@
+package scala.scalajs.compiler.test.util
+
+import language.implicitConversions
+
+import scala.tools.nsc._
+import scala.reflect.internal.util.SourceFile
+
+import scala.util.control.ControlThrowable
+
+import org.junit.Assert._
+
+import scala.scalajs.compiler.{ScalaJSPlugin, JSTreeExtractors}
+import JSTreeExtractors.jse
+import scala.scalajs.ir
+import ir.{Trees => js}
+
+abstract class JSASTTest extends DirectTest {
+
+ private var lastAST: JSAST = _
+
+ class JSAST(val clDefs: List[js.Tree]) {
+ type Pat = PartialFunction[js.Tree, Unit]
+
+ class PFTraverser(pf: Pat) extends ir.Traversers.Traverser {
+ private case object Found extends ControlThrowable
+
+ private[this] var finding = false
+
+ def find: Boolean = {
+ finding = true
+ try {
+ clDefs.map(traverse)
+ false
+ } catch {
+ case Found => true
+ }
+ }
+
+ def traverse(): Unit = {
+ finding = false
+ clDefs.map(traverse)
+ }
+
+ override def traverse(tree: js.Tree): Unit = {
+ if (finding && pf.isDefinedAt(tree))
+ throw Found
+
+ if (!finding)
+ pf.lift(tree)
+
+ super.traverse(tree)
+ }
+ }
+
+ def has(trgName: String)(pf: Pat): this.type = {
+ val tr = new PFTraverser(pf)
+ assertTrue(s"AST should have $trgName", tr.find)
+ this
+ }
+
+ def hasNot(trgName: String)(pf: Pat): this.type = {
+ val tr = new PFTraverser(pf)
+ assertFalse(s"AST should not have $trgName", tr.find)
+ this
+ }
+
+ def traverse(pf: Pat): this.type = {
+ val tr = new PFTraverser(pf)
+ tr.traverse()
+ this
+ }
+
+ def show: this.type = {
+ clDefs foreach println _
+ this
+ }
+
+ }
+
+ implicit def string2ast(str: String): JSAST = stringAST(str)
+
+ override def newScalaJSPlugin(global: Global) = new ScalaJSPlugin(global) {
+ override def generatedJSAST(cld: List[js.Tree]): Unit = {
+ lastAST = new JSAST(cld)
+ }
+ }
+
+ def stringAST(code: String): JSAST = stringAST(defaultGlobal)(code)
+ def stringAST(global: Global)(code: String): JSAST = {
+ compileString(global)(code)
+ lastAST
+ }
+
+ def sourceAST(source: SourceFile): JSAST = sourceAST(defaultGlobal)(source)
+ def sourceAST(global: Global)(source: SourceFile): JSAST = {
+ compileSources(global)(source)
+ lastAST
+ }
+
+}
diff --git a/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/util/TestHelpers.scala b/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/util/TestHelpers.scala
new file mode 100644
index 0000000..adad89c
--- /dev/null
+++ b/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/util/TestHelpers.scala
@@ -0,0 +1,68 @@
+package scala.scalajs.compiler.test.util
+
+import java.io._
+import scala.tools.nsc._
+
+import reporters.ConsoleReporter
+
+import org.junit.Assert._
+
+import scala.util.matching.Regex
+
+trait TestHelpers extends DirectTest {
+
+ private[this] val errBuffer = new CharArrayWriter
+
+ override def newReporter(settings: Settings) = {
+ val in = new BufferedReader(new StringReader(""))
+ val out = new PrintWriter(errBuffer)
+ new ConsoleReporter(settings, in, out)
+ }
+
+ /** will be prefixed to every code that is compiled. use for imports */
+ def preamble = ""
+
+ /** pimps a string to compile it and apply the specified test */
+ implicit class CompileTests(val code: String) {
+
+ def hasErrors(expected: String) = {
+ val reps = repResult {
+ assertFalse("snippet shouldn't compile", compileString(preamble + code))
+ }
+ assertEquals("should have right errors",
+ expected.stripMargin.trim, reps.trim)
+ }
+
+ def hasWarns(expected: String) = {
+ val reps = repResult {
+ assertTrue("snippet should compile", compileString(preamble + code))
+ }
+ assertEquals("should have right warnings",
+ expected.stripMargin.trim, reps.trim)
+ }
+
+ def fails() =
+ assertFalse("snippet shouldn't compile", compileString(preamble + code))
+
+ def warns() = {
+ val reps = repResult {
+ assertTrue("snippet should compile", compileString(preamble + code))
+ }
+ assertFalse("should have warnings", reps.isEmpty)
+ }
+
+ def succeeds() =
+ assertTrue("snippet should compile", compileString(preamble + code))
+
+ private def repResult(body: => Unit) = {
+ errBuffer.reset()
+ body
+ errBuffer.toString
+ }
+ }
+
+ implicit class CodeWrappers(sc: StringContext) {
+ def expr() = new CompileTests(s"class A { ${sc.parts.mkString} }")
+ }
+
+}
diff --git a/examples/scala-js/examples/helloworld/HelloWorld.scala b/examples/scala-js/examples/helloworld/HelloWorld.scala
new file mode 100644
index 0000000..fd33060
--- /dev/null
+++ b/examples/scala-js/examples/helloworld/HelloWorld.scala
@@ -0,0 +1,86 @@
+/* Scala.js example code
+ * Public domain
+ * @author Sébastien Doeraene
+ */
+
+package helloworld
+
+import scala.scalajs.js
+import js.annotation.JSName
+
+object HelloWorld extends js.JSApp {
+ def main() {
+ if (!(!js.Dynamic.global.document) &&
+ !(!js.Dynamic.global.document.getElementById("playground"))) {
+ sayHelloFromDOM()
+ sayHelloFromTypedDOM()
+ sayHelloFromJQuery()
+ sayHelloFromTypedJQuery()
+ } else {
+ println("Hello world!")
+ }
+ }
+
+ def sayHelloFromDOM() {
+ val document = js.Dynamic.global.document
+ val playground = document.getElementById("playground")
+
+ val newP = document.createElement("p")
+ newP.innerHTML = "Hello world! <i>-- DOM</i>"
+ playground.appendChild(newP)
+ }
+
+ def sayHelloFromTypedDOM() {
+ val document = window.document
+ val playground = document.getElementById("playground")
+
+ val newP = document.createElement("p")
+ newP.innerHTML = "Hello world! <i>-- typed DOM</i>"
+ playground.appendChild(newP)
+ }
+
+ def sayHelloFromJQuery() {
+ // val $ is fine too, but not very recommended in Scala code
+ val jQuery = js.Dynamic.global.jQuery
+ val newP = jQuery("<p>").html("Hello world! <i>-- jQuery</i>")
+ newP.appendTo(jQuery("#playground"))
+ }
+
+ def sayHelloFromTypedJQuery() {
+ val jQuery = helloworld.JQuery
+ val newP = jQuery("<p>").html("Hello world! <i>-- typed jQuery</i>")
+ newP.appendTo(jQuery("#playground"))
+ }
+}
+
+object window extends js.GlobalScope {
+ val document: DOMDocument = js.native
+
+ def alert(msg: String): Unit = js.native
+}
+
+trait DOMDocument extends js.Object {
+ def getElementById(id: String): DOMElement = js.native
+ def createElement(tag: String): DOMElement = js.native
+}
+
+trait DOMElement extends js.Object {
+ var innerHTML: String = js.native
+
+ def appendChild(child: DOMElement): Unit = js.native
+}
+
+@JSName("jQuery")
+object JQuery extends js.Object {
+ def apply(selector: String): JQuery = js.native
+}
+
+trait JQuery extends js.Object {
+ def text(value: String): JQuery = js.native
+ def text(): String = js.native
+
+ def html(value: String): JQuery = js.native
+ def html(): String = js.native
+
+ def appendTo(parent: JQuery): JQuery = js.native
+}
diff --git a/examples/scala-js/examples/helloworld/helloworld-2.10-fastopt.html b/examples/scala-js/examples/helloworld/helloworld-2.10-fastopt.html
new file mode 100644
index 0000000..98b2705
--- /dev/null
+++ b/examples/scala-js/examples/helloworld/helloworld-2.10-fastopt.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Hello world - Scala.js example</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+</head>
+<body>
+
+<div id="playground">
+</div>
+
+<script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
+
+<script type="text/javascript" src="./target/scala-2.10/helloworld-fastopt.js"></script>
+
+<script type="text/javascript" src="./target/scala-2.10/helloworld-launcher.js"></script>
+
+</body>
+</html>
diff --git a/examples/scala-js/examples/helloworld/helloworld-2.10.html b/examples/scala-js/examples/helloworld/helloworld-2.10.html
new file mode 100644
index 0000000..80b00b9
--- /dev/null
+++ b/examples/scala-js/examples/helloworld/helloworld-2.10.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Hello world - Scala.js example</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+</head>
+<body>
+
+<div id="playground">
+</div>
+
+<script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
+
+<script type="text/javascript" src="./target/scala-2.10/helloworld-opt.js"></script>
+
+<script type="text/javascript" src="./target/scala-2.10/helloworld-launcher.js"></script>
+
+</body>
+</html>
diff --git a/examples/scala-js/examples/helloworld/helloworld-2.11-fastopt.html b/examples/scala-js/examples/helloworld/helloworld-2.11-fastopt.html
new file mode 100644
index 0000000..dbf5598
--- /dev/null
+++ b/examples/scala-js/examples/helloworld/helloworld-2.11-fastopt.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Hello world - Scala.js example</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+</head>
+<body>
+
+<div id="playground">
+</div>
+
+<script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
+
+<script type="text/javascript" src="./target/scala-2.11/helloworld-fastopt.js"></script>
+
+<script type="text/javascript" src="./target/scala-2.11/helloworld-launcher.js"></script>
+
+</body>
+</html>
diff --git a/examples/scala-js/examples/helloworld/helloworld-2.11.html b/examples/scala-js/examples/helloworld/helloworld-2.11.html
new file mode 100644
index 0000000..9c9a3a1
--- /dev/null
+++ b/examples/scala-js/examples/helloworld/helloworld-2.11.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Hello world - Scala.js example</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+</head>
+<body>
+
+<div id="playground">
+</div>
+
+<script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
+
+<script type="text/javascript" src="./target/scala-2.11/helloworld-opt.js"></script>
+
+<script type="text/javascript" src="./target/scala-2.11/helloworld-launcher.js"></script>
+
+</body>
+</html>
diff --git a/examples/scala-js/examples/helloworld/startup.js b/examples/scala-js/examples/helloworld/startup.js
new file mode 100644
index 0000000..f45e4cb
--- /dev/null
+++ b/examples/scala-js/examples/helloworld/startup.js
@@ -0,0 +1,8 @@
+/* Scala.js example code
+ * Public domain
+ * Author: Sébastien Doeraene
+ */
+
+$(function() {
+ ScalaJS.modules.helloworld_HelloWorld().main();
+});
diff --git a/examples/scala-js/examples/reversi/JSTypes.scala b/examples/scala-js/examples/reversi/JSTypes.scala
new file mode 100644
index 0000000..cc0e5a4
--- /dev/null
+++ b/examples/scala-js/examples/reversi/JSTypes.scala
@@ -0,0 +1,87 @@
+/* Scala.js example code
+ * Public domain
+ * @author Sébastien Doeraene
+ */
+
+package reversi
+
+import scala.scalajs.js
+
+trait Window extends js.Object {
+ val document: DOMDocument = js.native
+
+ def alert(msg: String): Unit = js.native
+}
+
+trait DOMDocument extends js.Object {
+ def getElementById(id: String): DOMElement = js.native
+ def createElement(tag: String): DOMElement = js.native
+}
+
+trait DOMElement extends js.Object {
+ var innerHTML: String = js.native
+
+ def appendChild(child: DOMElement): Unit = js.native
+}
+
+trait JQueryStatic extends js.Object {
+ def apply(arg: js.Any): JQuery = js.native
+ def apply(arg: js.Any, attributes: js.Any): JQuery = js.native
+}
+
+trait JQuery extends js.Object {
+ def get(index: Int): DOMElement = js.native
+
+ def text(value: String): JQuery = js.native
+ def text(): String = js.native
+
+ def html(value: String): JQuery = js.native
+ def html(): String = js.native
+
+ def prop(property: String): js.Any = js.native
+ def prop(property: String, value: js.Any): JQuery = js.native
+
+ def offset(): JQueryOffset = js.native
+
+ def appendTo(parent: JQuery): JQuery = js.native
+ def append(children: JQuery): JQuery = js.native
+
+ def addClass(classes: String): JQuery = js.native
+ def removeClass(classes: String): JQuery = js.native
+
+ def each[U](callback: js.Function2[Int, js.Dynamic, U]): JQuery = js.native
+
+ def click[U](handler: js.Function0[U]): JQuery = js.native
+ def click[U](handler: js.Function1[JQueryEvent, U]): JQuery = js.native
+}
+
+trait JQueryOffset extends js.Object {
+ val top: Double = js.native
+ val left: Double = js.native
+}
+
+trait JQueryEvent extends js.Object {
+ val pageX: Double = js.native
+ val pageY: Double = js.native
+}
+
+trait HTMLCanvasElement extends DOMElement {
+ def getContext(kind: String): js.Any = js.native // depends on the kind
+}
+
+trait CanvasRenderingContext2D extends js.Object {
+ val canvas: HTMLCanvasElement = js.native
+
+ var fillStyle: String = js.native
+ var lineWidth: Double = js.native
+
+ def fillRect(x: Double, y: Double, w: Double, h: Double): Unit = js.native
+ def strokeRect(x: Double, y: Double, w: Double, h: Double): Unit = js.native
+
+ def beginPath(): Unit = js.native
+ def fill(): Unit = js.native
+ def stroke(): Unit = js.native
+
+ def arc(x: Double, y: Double, radius: Double, startAngle: Double,
+ endAngle: Double, anticlockwise: Boolean): Unit = js.native
+}
diff --git a/examples/scala-js/examples/reversi/Reversi.scala b/examples/scala-js/examples/reversi/Reversi.scala
new file mode 100644
index 0000000..b4a34a4
--- /dev/null
+++ b/examples/scala-js/examples/reversi/Reversi.scala
@@ -0,0 +1,266 @@
+/* Scala.js example code
+ * Public domain
+ * @author Sébastien Doeraene
+ */
+
+package reversi
+
+import scala.annotation.tailrec
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSExport
+
+sealed abstract class OptPlayer
+
+sealed abstract class Player extends OptPlayer {
+ val opponent: Player
+}
+
+case object NoPlayer extends OptPlayer
+
+case object White extends Player {
+ val opponent = Black
+}
+
+case object Black extends Player {
+ val opponent = White
+}
+
+@JSExport("Reversi")
+class Reversi(jQuery: JQueryStatic, playground: JQuery) {
+
+ // The Model -----------------------------------------------------------------
+
+ val BoardSize = 8 // size of a Reversi board
+
+ def inBounds(index: Int): Boolean = index >= 0 && index < BoardSize
+ def inBounds(x: Int, y: Int): Boolean = inBounds(x) && inBounds(y)
+
+ class Square(val x: Int, val y: Int) {
+ private var _owner: OptPlayer = NoPlayer
+
+ var onOwnerChange: (OptPlayer, OptPlayer) => Unit = (oldP, newP) => ()
+
+ def owner = _owner
+ def owner_=(value: OptPlayer) {
+ val previous = _owner
+ if (value != previous) {
+ _owner = value
+ onOwnerChange(previous, value)
+ }
+ }
+
+ override def toString() = "Square("+x+", "+y+", "+owner+")"
+ }
+
+ val board = Array.tabulate[Square](BoardSize, BoardSize)(new Square(_, _))
+ val allSquares = board.flatten
+ var currentPlayer: Player = White // Irrelevant, set again in startGame()
+
+ // The GUI -------------------------------------------------------------------
+
+ val resetButton = createResetButton()
+ val passButton = createPassButton()
+ val status = createStatus()
+ buildUI()
+
+ def createResetButton() = {
+ jQuery("<input>", js.Dynamic.literal(
+ `type` = "button", value = "Reset"
+ )).click(reset _)
+ }
+
+ def createPassButton() = {
+ jQuery("<input>", js.Dynamic.literal(
+ `type` = "button", value = "Pass"
+ )).click(pass _)
+ }
+
+ def createStatus() = {
+ jQuery("<span>")
+ }
+
+ def buildUI() {
+ // Some dimensions
+ val SquareSizePx = 48
+ val HalfSquareSizePx = SquareSizePx/2
+ val PawnRadiusPx = HalfSquareSizePx-4
+ val BoardSizePx = BoardSize*SquareSizePx + 3
+
+ // Creat the board canvas
+ val boardCanvas = jQuery(
+ "<canvas width='"+BoardSizePx+"' height='"+BoardSizePx+"'></canvas>")
+ val domCanvas = boardCanvas.get(0).asInstanceOf[HTMLCanvasElement]
+ val context = domCanvas.getContext("2d").asInstanceOf[CanvasRenderingContext2D]
+
+ playground.append(jQuery("<div>").append(boardCanvas))
+
+ /** Draw the specified square on the board canvas */
+ def drawSquare(square: Square) {
+ val x = square.x * SquareSizePx
+ val y = square.y * SquareSizePx
+
+ // Background
+ context.fillStyle = "green"
+ context.fillRect(x, y, SquareSizePx, SquareSizePx)
+
+ // Border
+ context.fillStyle = "black"
+ context.lineWidth = 3
+ context.strokeRect(x, y, SquareSizePx, SquareSizePx)
+
+ // Pawn
+ if (square.owner != NoPlayer) {
+ context.fillStyle = if (square.owner == White) "white" else "black"
+ context.beginPath()
+ context.arc(x+HalfSquareSizePx, y+HalfSquareSizePx, PawnRadiusPx,
+ 0, 2*Math.PI, true)
+ context.fill()
+ }
+ }
+
+ // Draw squares now, and everytime they change ownership
+ for (square <- allSquares) {
+ drawSquare(square)
+ square.onOwnerChange = { (prevOwner, newOwner) =>
+ drawSquare(square)
+ }
+ }
+
+ // Configure clicks on the board
+ boardCanvas.click { (event: JQueryEvent) =>
+ val offsetX = event.pageX - boardCanvas.offset().left
+ val offsetY = event.pageY - boardCanvas.offset().top
+ val x = offsetX.toInt / SquareSizePx
+ val y = offsetY.toInt / SquareSizePx
+
+ if (inBounds(x, y))
+ clickSquare(board(x)(y))
+ }
+
+ // Build the status bar
+ val statusBar = jQuery("<p>")
+ statusBar.append(resetButton)
+ statusBar.append(status)
+ statusBar.append(passButton)
+ playground.append(statusBar)
+ }
+
+ // The Game ------------------------------------------------------------------
+
+ def reset() {
+ startGame()
+ }
+
+ @JSExport
+ def startGame() {
+ // Set up the board
+ allSquares foreach (_.owner = NoPlayer)
+ board(3)(3).owner = White
+ board(3)(4).owner = Black
+ board(4)(3).owner = Black
+ board(4)(4).owner = White
+
+ // White begins
+ currentPlayer = White
+
+ // Let's go!
+ startTurn()
+ }
+
+ def startTurn() {
+ val (scoreWhite, scoreBlack) = computeScore()
+ status.text(currentPlayer+"'s turn -- White: "+scoreWhite+
+ " -- Black: "+scoreBlack)
+
+ passButton.prop("disabled", true)
+
+ if (!existsValidMove()) {
+ // Test if the other player can do something
+ currentPlayer = currentPlayer.opponent
+ val opponentCanDoSomething = existsValidMove()
+ currentPlayer = currentPlayer.opponent
+
+ if (opponentCanDoSomething) {
+ passButton.prop("disabled", false)
+ } else {
+ // End of game
+ val winnerText =
+ if (scoreWhite > scoreBlack) "White won!"
+ else if (scoreBlack > scoreWhite) "Black won!"
+ else "Draw"
+ status.text("Game finished -- White: "+scoreWhite+
+ " -- Black: "+scoreBlack+" -- "+winnerText)
+ }
+ }
+ }
+
+ def clickSquare(square: Square) {
+ val toFlip = computeFlips(square)
+ if (!toFlip.isEmpty) {
+ (square :: toFlip) foreach (_.owner = currentPlayer)
+ nextTurn()
+ }
+ }
+
+ def pass() {
+ assert(!existsValidMove())
+ nextTurn()
+ }
+
+ def existsValidMove(): Boolean = {
+ allSquares.exists(isValidMove)
+ }
+
+ def isValidMove(square: Square): Boolean = {
+ !computeFlips(square).isEmpty
+ }
+
+ def computeFlips(square: Square): List[Square] = {
+ if (square.owner != NoPlayer) Nil
+ else {
+ for {
+ i <- (-1 to 1).toList
+ j <- -1 to 1
+ if i != 0 || j != 0
+ flip <- computeFlipsInDirection(square.x, square.y, i, j)
+ } yield flip
+ }
+ }
+
+ def computeFlipsInDirection(x: Int, y: Int,
+ dirx: Int, diry: Int): List[Square] = {
+
+ val allInDir = allSquaresInDirection(x, y, dirx, diry)
+ val (toFlip, remaining) =
+ allInDir.span(_.owner == currentPlayer.opponent)
+
+ val success = remaining.headOption.exists(_.owner == currentPlayer)
+ if (success) toFlip
+ else Nil
+ }
+
+ def allSquaresInDirection(fromx: Int, fromy: Int,
+ dirx: Int, diry: Int): List[Square] = {
+ val nextx = fromx + dirx
+ val nexty = fromy + diry
+ if (inBounds(nextx, nexty))
+ board(nextx)(nexty) :: allSquaresInDirection(nextx, nexty, dirx, diry)
+ else
+ Nil
+ }
+
+ def computeScore(): (Int, Int) = {
+ allSquares.foldLeft((0, 0)) { case ((white, black), square) =>
+ square.owner match {
+ case White => (white+1, black)
+ case Black => (white, black+1)
+ case NoPlayer => (white, black)
+ }
+ }
+ }
+
+ def nextTurn() {
+ currentPlayer = currentPlayer.opponent
+ startTurn()
+ }
+}
diff --git a/examples/scala-js/examples/reversi/reversi-2.10-fastopt.html b/examples/scala-js/examples/reversi/reversi-2.10-fastopt.html
new file mode 100644
index 0000000..46cd1c7
--- /dev/null
+++ b/examples/scala-js/examples/reversi/reversi-2.10-fastopt.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Reversi - Scala.js example</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+</head>
+<body>
+
+<h1>Reversi - Scala.js example</h1>
+
+<p>Somewhat inspired by
+<a href="http://davidbau.com/reversi/">http://davidbau.com/reversi/</a></p>
+
+<div id="playground">
+</div>
+
+<script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
+
+<script type="text/javascript" src="./target/scala-2.10/reversi-fastopt.js"></script>
+
+<script type="text/javascript">
+$(function() {
+ var mainInstance = new Reversi(
+ jQuery, jQuery("#playground"));
+ mainInstance.startGame();
+});
+</script>
+
+</body>
+</html>
diff --git a/examples/scala-js/examples/reversi/reversi-2.10.html b/examples/scala-js/examples/reversi/reversi-2.10.html
new file mode 100644
index 0000000..5f7b696
--- /dev/null
+++ b/examples/scala-js/examples/reversi/reversi-2.10.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Reversi - Scala.js example</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+</head>
+<body>
+
+<h1>Reversi - Scala.js example</h1>
+
+<p>Somewhat inspired by
+<a href="http://davidbau.com/reversi/">http://davidbau.com/reversi/</a></p>
+
+<div id="playground">
+</div>
+
+<script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
+
+<script type="text/javascript" src="./target/scala-2.10/reversi-opt.js"></script>
+
+<script type="text/javascript">
+$(function() {
+ var mainInstance = new Reversi(
+ jQuery, jQuery("#playground"));
+ mainInstance.startGame();
+});
+</script>
+
+</body>
+</html>
diff --git a/examples/scala-js/examples/reversi/reversi-2.11-fastopt.html b/examples/scala-js/examples/reversi/reversi-2.11-fastopt.html
new file mode 100644
index 0000000..524e716
--- /dev/null
+++ b/examples/scala-js/examples/reversi/reversi-2.11-fastopt.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Reversi - Scala.js example</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+</head>
+<body>
+
+<h1>Reversi - Scala.js example</h1>
+
+<p>Somewhat inspired by
+<a href="http://davidbau.com/reversi/">http://davidbau.com/reversi/</a></p>
+
+<div id="playground">
+</div>
+
+<script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
+
+<script type="text/javascript" src="./target/scala-2.11/reversi-fastopt.js"></script>
+
+<script type="text/javascript">
+$(function() {
+ var mainInstance = new Reversi(
+ jQuery, jQuery("#playground"));
+ mainInstance.startGame();
+});
+</script>
+
+</body>
+</html>
diff --git a/examples/scala-js/examples/reversi/reversi-2.11.html b/examples/scala-js/examples/reversi/reversi-2.11.html
new file mode 100644
index 0000000..b1a6d08
--- /dev/null
+++ b/examples/scala-js/examples/reversi/reversi-2.11.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Reversi - Scala.js example</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+</head>
+<body>
+
+<h1>Reversi - Scala.js example</h1>
+
+<p>Somewhat inspired by
+<a href="http://davidbau.com/reversi/">http://davidbau.com/reversi/</a></p>
+
+<div id="playground">
+</div>
+
+<script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
+
+<script type="text/javascript" src="./target/scala-2.11/reversi-opt.js"></script>
+
+<script type="text/javascript">
+$(function() {
+ var mainInstance = new Reversi(
+ jQuery, jQuery("#playground"));
+ mainInstance.startGame();
+});
+</script>
+
+</body>
+</html>
diff --git a/examples/scala-js/examples/testing/src/main/scala/ElementCreator.scala b/examples/scala-js/examples/testing/src/main/scala/ElementCreator.scala
new file mode 100644
index 0000000..ccb3600
--- /dev/null
+++ b/examples/scala-js/examples/testing/src/main/scala/ElementCreator.scala
@@ -0,0 +1,7 @@
+import scala.scalajs.js.Dynamic.global
+
+object ElementCreator {
+ val jQ = global.jQuery
+
+ def create() = jQ("body").append(jQ("<h1>Test</h1>"))
+}
diff --git a/examples/scala-js/examples/testing/src/test/scala/CollectionTest.scala b/examples/scala-js/examples/testing/src/test/scala/CollectionTest.scala
new file mode 100644
index 0000000..a586ca2
--- /dev/null
+++ b/examples/scala-js/examples/testing/src/test/scala/CollectionTest.scala
@@ -0,0 +1,17 @@
+import scala.scalajs.js
+import scala.scalajs.js.Dynamic.global
+import scala.scalajs.js.JSConverters._
+
+import org.scalajs.jasminetest.JasmineTest
+
+object CollectionTest extends JasmineTest {
+
+ describe("Array") {
+
+ it("should be able to map and filter integers") {
+ val array = Array(5, 7, 2, 6, -30, 33, 66, 76, 75, 0)
+ val result = array.filter(_.toInt % 3 != 0).map(x => x*x)
+ expect(result.toJSArray).toEqual(js.Array(25, 49, 4, 76*76))
+ }
+ }
+}
diff --git a/examples/scala-js/examples/testing/src/test/scala/ElementCreatorTest.scala b/examples/scala-js/examples/testing/src/test/scala/ElementCreatorTest.scala
new file mode 100644
index 0000000..43f6756
--- /dev/null
+++ b/examples/scala-js/examples/testing/src/test/scala/ElementCreatorTest.scala
@@ -0,0 +1,24 @@
+import scala.scalajs.js
+import scala.scalajs.js.Dynamic.global
+
+import org.scalajs.jasminetest.JasmineTest
+
+object ElementCreatorTest extends JasmineTest {
+
+ describe("ElementCreator") {
+
+ it("should be able to create an element in the body") {
+ // create the element
+ ElementCreator.create()
+
+ // jquery would make this easier, but I wanted to
+ // only use pure html in the test itself
+ val body = global.document.getElementsByTagName("body")
+ .asInstanceOf[js.Array[js.Dynamic]].head
+
+ // the Scala.js DOM API would make this easier
+ expect(body.lastChild.tagName.toString == "H1").toBeTruthy
+ expect(body.lastChild.innerHTML.toString == "Test").toBeTruthy
+ }
+ }
+}
diff --git a/examples/scala-js/examples/testing/testing-2.10-fastopt.html b/examples/scala-js/examples/testing/testing-2.10-fastopt.html
new file mode 100644
index 0000000..04ded2f
--- /dev/null
+++ b/examples/scala-js/examples/testing/testing-2.10-fastopt.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Testing - Jasmine HTML reporter example</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+ <link rel="stylesheet" type="text/css" href="http://cdn.jsdelivr.net/jasmine/1.3.1/jasmine.css"></link>
+</head>
+<body>
+<script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
+<script type="text/javascript" src="http://cdn.jsdelivr.net/jasmine/1.3.1/jasmine.js"></script>
+<script type="text/javascript" src="http://cdn.jsdelivr.net/jasmine/1.3.1/jasmine-html.js"></script>
+<script type="text/javascript" src="./target/scala-2.10/testing-test-fastopt.js"></script>
+
+<script type="text/javascript">
+$(function() {
+ // Load tests
+ CollectionTest();
+ ElementCreatorTest();
+
+ // Setup and run Jasmine
+ var jasmineEnv = jasmine.getEnv();
+ var htmlReporter = new jasmine.HtmlReporter();
+ jasmineEnv.addReporter(htmlReporter);
+ jasmineEnv.specFilter = function(spec) {
+ return htmlReporter.specFilter(spec);
+ };
+ jasmineEnv.execute();
+});
+</script>
+
+</body>
+</html>
diff --git a/examples/scala-js/examples/testing/testing-2.10.html b/examples/scala-js/examples/testing/testing-2.10.html
new file mode 100644
index 0000000..bbb0cfe
--- /dev/null
+++ b/examples/scala-js/examples/testing/testing-2.10.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Testing - Jasmine HTML reporter example</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+ <link rel="stylesheet" type="text/css" href="http://cdn.jsdelivr.net/jasmine/1.3.1/jasmine.css"></link>
+</head>
+<body>
+<script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
+<script type="text/javascript" src="http://cdn.jsdelivr.net/jasmine/1.3.1/jasmine.js"></script>
+<script type="text/javascript" src="http://cdn.jsdelivr.net/jasmine/1.3.1/jasmine-html.js"></script>
+<script type="text/javascript" src="./target/scala-2.10/testing-test-opt.js"></script>
+
+<script type="text/javascript">
+$(function() {
+ // Load tests
+ CollectionTest();
+ ElementCreatorTest();
+
+ // Setup and run Jasmine
+ var jasmineEnv = jasmine.getEnv();
+ var htmlReporter = new jasmine.HtmlReporter();
+ jasmineEnv.addReporter(htmlReporter);
+ jasmineEnv.specFilter = function(spec) {
+ return htmlReporter.specFilter(spec);
+ };
+ jasmineEnv.execute();
+});
+</script>
+
+</body>
+</html>
diff --git a/examples/scala-js/examples/testing/testing-2.11-fastopt.html b/examples/scala-js/examples/testing/testing-2.11-fastopt.html
new file mode 100644
index 0000000..a87f635
--- /dev/null
+++ b/examples/scala-js/examples/testing/testing-2.11-fastopt.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Testing - Jasmine HTML reporter example</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+ <link rel="stylesheet" type="text/css" href="http://cdn.jsdelivr.net/jasmine/1.3.1/jasmine.css"></link>
+</head>
+<body>
+<script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
+<script type="text/javascript" src="http://cdn.jsdelivr.net/jasmine/1.3.1/jasmine.js"></script>
+<script type="text/javascript" src="http://cdn.jsdelivr.net/jasmine/1.3.1/jasmine-html.js"></script>
+<script type="text/javascript" src="./target/scala-2.11/testing-test-fastopt.js"></script>
+
+<script type="text/javascript">
+$(function() {
+ // Load tests
+ CollectionTest();
+ ElementCreatorTest();
+
+ // Setup and run Jasmine
+ var jasmineEnv = jasmine.getEnv();
+ var htmlReporter = new jasmine.HtmlReporter();
+ jasmineEnv.addReporter(htmlReporter);
+ jasmineEnv.specFilter = function(spec) {
+ return htmlReporter.specFilter(spec);
+ };
+ jasmineEnv.execute();
+});
+</script>
+
+</body>
+</html>
diff --git a/examples/scala-js/examples/testing/testing-2.11.html b/examples/scala-js/examples/testing/testing-2.11.html
new file mode 100644
index 0000000..9902c3f
--- /dev/null
+++ b/examples/scala-js/examples/testing/testing-2.11.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Testing - Jasmine HTML reporter example</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+ <link rel="stylesheet" type="text/css" href="http://cdn.jsdelivr.net/jasmine/1.3.1/jasmine.css"></link>
+</head>
+<body>
+<script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
+<script type="text/javascript" src="http://cdn.jsdelivr.net/jasmine/1.3.1/jasmine.js"></script>
+<script type="text/javascript" src="http://cdn.jsdelivr.net/jasmine/1.3.1/jasmine-html.js"></script>
+<script type="text/javascript" src="./target/scala-2.11/testing-test-opt.js"></script>
+
+<script type="text/javascript">
+$(function() {
+ // Load tests
+ CollectionTest();
+ ElementCreatorTest();
+
+ // Setup and run Jasmine
+ var jasmineEnv = jasmine.getEnv();
+ var htmlReporter = new jasmine.HtmlReporter();
+ jasmineEnv.addReporter(htmlReporter);
+ jasmineEnv.specFilter = function(spec) {
+ return htmlReporter.specFilter(spec);
+ };
+ jasmineEnv.execute();
+});
+</script>
+
+</body>
+</html>
diff --git a/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/ClassKind.scala b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/ClassKind.scala
new file mode 100644
index 0000000..5092d2c
--- /dev/null
+++ b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/ClassKind.scala
@@ -0,0 +1,53 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js IR **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.ir
+
+import scala.annotation.switch
+
+sealed abstract class ClassKind {
+ import ClassKind._
+
+ def isClass = this match {
+ case Class | ModuleClass => true
+ case _ => false
+ }
+
+ def isType = this match {
+ case TraitImpl => false
+ case _ => true
+ }
+}
+
+object ClassKind {
+ case object Class extends ClassKind
+ case object ModuleClass extends ClassKind
+ case object Interface extends ClassKind
+ case object RawJSType extends ClassKind
+ case object HijackedClass extends ClassKind
+ case object TraitImpl extends ClassKind
+
+ private[ir] def toByte(kind: ClassKind): Byte = kind match {
+ case ClassKind.Class => 1
+ case ClassKind.ModuleClass => 2
+ case ClassKind.Interface => 3
+ case ClassKind.RawJSType => 4
+ case ClassKind.HijackedClass => 5
+ case ClassKind.TraitImpl => 6
+ }
+
+ private[ir] def fromByte(b: Byte): ClassKind = (b: @switch) match {
+ case 1 => ClassKind.Class
+ case 2 => ClassKind.ModuleClass
+ case 3 => ClassKind.Interface
+ case 4 => ClassKind.RawJSType
+ case 5 => ClassKind.HijackedClass
+ case 6 => ClassKind.TraitImpl
+ }
+}
diff --git a/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Definitions.scala b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Definitions.scala
new file mode 100644
index 0000000..0762602
--- /dev/null
+++ b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Definitions.scala
@@ -0,0 +1,144 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js IR **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.ir
+
+object Definitions {
+ val ObjectClass = "O"
+ val ClassClass = "jl_Class"
+
+ val StringClass = "T"
+
+ val PrimitiveClasses = Set("V", "Z", "C", "B", "S", "I", "J", "F", "D")
+
+ def isPrimitiveClass(encodedName: String): Boolean =
+ PrimitiveClasses.contains(encodedName)
+
+ val BoxedUnitClass = "sr_BoxedUnit"
+ val BoxedBooleanClass = "jl_Boolean"
+ val BoxedCharacterClass = "jl_Character"
+ val BoxedByteClass = "jl_Byte"
+ val BoxedShortClass = "jl_Short"
+ val BoxedIntegerClass = "jl_Integer"
+ val BoxedLongClass = "jl_Long"
+ val BoxedFloatClass = "jl_Float"
+ val BoxedDoubleClass = "jl_Double"
+
+ val CharSequenceClass = "jl_CharSequence"
+ val SerializableClass = "Ljava_io_Serializable"
+ val ComparableClass = "jl_Comparable"
+ val NumberClass = "jl_Number"
+
+ val HijackedBoxedClasses = Set(
+ BoxedUnitClass, BoxedBooleanClass, BoxedByteClass, BoxedShortClass,
+ BoxedIntegerClass, BoxedLongClass, BoxedFloatClass, BoxedDoubleClass)
+ val HijackedClasses =
+ HijackedBoxedClasses + StringClass
+
+ val AncestorsOfStringClass = Set(
+ CharSequenceClass, ComparableClass, SerializableClass)
+ val AncestorsOfHijackedNumberClasses = Set(
+ NumberClass, ComparableClass, SerializableClass)
+ val AncestorsOfBoxedBooleanClass = Set(
+ ComparableClass, SerializableClass)
+
+ val AncestorsOfHijackedClasses =
+ AncestorsOfStringClass ++ AncestorsOfHijackedNumberClasses ++
+ AncestorsOfBoxedBooleanClass
+
+ val RuntimeNullClass = "sr_Null$"
+ val RuntimeNothingClass = "sr_Nothing$"
+
+ val ThrowableClass = "jl_Throwable"
+
+ /** Encodes a class name. */
+ def encodeClassName(fullName: String): String = {
+ val base = fullName.replace("_", "$und").replace(".", "_")
+ val encoded = compressedClasses.getOrElse(base, {
+ compressedPrefixes collectFirst {
+ case (prefix, compressed) if base.startsWith(prefix) =>
+ compressed + base.substring(prefix.length)
+ } getOrElse {
+ "L"+base
+ }
+ })
+ if (Trees.isKeyword(encoded) || encoded.charAt(0).isDigit ||
+ encoded.charAt(0) == '$') {
+ "$" + encoded
+ } else encoded
+ }
+
+ // !!! Duplicate logic: this code must be in sync with runtime.StackTrace
+
+ /** Decodes a class name encoded with [[encodeClassName]]. */
+ def decodeClassName(encodedName: String): String = {
+ val encoded =
+ if (encodedName.charAt(0) == '$') encodedName.substring(1)
+ else encodedName
+ val base = decompressedClasses.getOrElse(encoded, {
+ decompressedPrefixes collectFirst {
+ case (prefix, decompressed) if encoded.startsWith(prefix) =>
+ decompressed + encoded.substring(prefix.length)
+ } getOrElse {
+ assert(!encoded.isEmpty && encoded.charAt(0) == 'L',
+ s"Cannot decode invalid encoded name '$encodedName'")
+ encoded.substring(1)
+ }
+ })
+ base.replace("_", ".").replace("$und", "_")
+ }
+
+ private val compressedClasses: Map[String, String] = Map(
+ "java_lang_Object" -> "O",
+ "java_lang_String" -> "T",
+ "scala_Unit" -> "V",
+ "scala_Boolean" -> "Z",
+ "scala_Char" -> "C",
+ "scala_Byte" -> "B",
+ "scala_Short" -> "S",
+ "scala_Int" -> "I",
+ "scala_Long" -> "J",
+ "scala_Float" -> "F",
+ "scala_Double" -> "D"
+ ) ++ (
+ for (index <- 2 to 22)
+ yield s"scala_Tuple$index" -> ("T"+index)
+ ) ++ (
+ for (index <- 0 to 22)
+ yield s"scala_Function$index" -> ("F"+index)
+ )
+
+ private val decompressedClasses: Map[String, String] =
+ compressedClasses map { case (a, b) => (b, a) }
+
+ private val compressedPrefixes = Seq(
+ "scala_scalajs_runtime_" -> "sjsr_",
+ "scala_scalajs_" -> "sjs_",
+ "scala_collection_immutable_" -> "sci_",
+ "scala_collection_mutable_" -> "scm_",
+ "scala_collection_generic_" -> "scg_",
+ "scala_collection_" -> "sc_",
+ "scala_runtime_" -> "sr_",
+ "scala_" -> "s_",
+ "java_lang_" -> "jl_",
+ "java_util_" -> "ju_"
+ )
+
+ private val decompressedPrefixes: Seq[(String, String)] =
+ compressedPrefixes map { case (a, b) => (b, a) }
+
+ /* Common predicates on encoded names */
+
+ def isConstructorName(name: String): Boolean =
+ name.startsWith("init___")
+
+ def isReflProxyName(name: String): Boolean =
+ name.endsWith("__") && !isConstructorName(name)
+
+}
diff --git a/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Hashers.scala b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Hashers.scala
new file mode 100644
index 0000000..168d7c1
--- /dev/null
+++ b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Hashers.scala
@@ -0,0 +1,459 @@
+package scala.scalajs.ir
+
+import java.security.{MessageDigest, DigestOutputStream}
+import java.io.{OutputStream, DataOutputStream}
+import java.util.Arrays
+
+import Trees._
+import Types._
+import Tags._
+
+object Hashers {
+
+ def hashMethodDef(methodDef: MethodDef): MethodDef = {
+ if (methodDef.hash.isDefined) methodDef
+ else {
+ val hasher = new TreeHasher()
+ val MethodDef(name, args, resultType, body) = methodDef
+
+ hasher.mixPos(methodDef.pos)
+ hasher.mixPropertyName(name)
+ hasher.mixTrees(args)
+ hasher.mixType(resultType)
+ hasher.mixTree(body)
+
+ val hash = hasher.finalizeHash()
+
+ MethodDef(name, args, resultType, body)(Some(hash))(methodDef.pos)
+ }
+ }
+
+ /** Hash definitions from a ClassDef where applicable */
+ def hashDefs(defs: List[Tree]): List[Tree] = defs map {
+ case methodDef: MethodDef => hashMethodDef(methodDef)
+ case otherDef => otherDef
+ }
+
+ /** Hash the definitions in a ClassDef (where applicable) */
+ def hashClassDef(classDef: ClassDef): ClassDef =
+ classDef.copy(defs = hashDefs(classDef.defs))(classDef.pos)
+
+ def hashesEqual(x: TreeHash, y: TreeHash, considerPos: Boolean): Boolean = {
+ Arrays.equals(x.treeHash, y.treeHash) &&
+ (!considerPos || Arrays.equals(x.posHash, y.posHash))
+ }
+
+ private final class TreeHasher {
+ private def newDigest = MessageDigest.getInstance("SHA-1")
+ private def newDigestStream(digest: MessageDigest) = {
+ val out = new OutputStream {
+ def write(b: Int): Unit = ()
+ }
+ val digOut = new DigestOutputStream(out, digest)
+ new DataOutputStream(digOut)
+ }
+
+ private[this] val treeDigest = newDigest
+ private[this] val treeStream = newDigestStream(treeDigest)
+
+ private[this] val posDigest = newDigest
+ private[this] val posStream = newDigestStream(posDigest)
+
+ def finalizeHash(): TreeHash =
+ new TreeHash(treeDigest.digest(), posDigest.digest())
+
+ def mixTree(tree: Tree): Unit = {
+ mixPos(tree.pos)
+ tree match {
+ case EmptyTree =>
+ mixTag(TagEmptyTree)
+
+ case VarDef(ident, vtpe, mutable, rhs) =>
+ mixTag(TagVarDef)
+ mixIdent(ident)
+ mixType(vtpe)
+ mixBoolean(mutable)
+ mixTree(rhs)
+
+ case ParamDef(ident, ptpe, mutable) =>
+ mixTag(TagParamDef)
+ mixIdent(ident)
+ mixType(ptpe)
+ mixBoolean(mutable)
+
+ case Skip() =>
+ mixTag(TagSkip)
+
+ case Block(stats) =>
+ mixTag(TagBlock)
+ mixTrees(stats)
+
+ case Labeled(label, tpe, body) =>
+ mixTag(TagLabeled)
+ mixIdent(label)
+ mixType(tpe)
+ mixTree(body)
+
+ case Assign(lhs, rhs) =>
+ mixTag(TagAssign)
+ mixTree(lhs)
+ mixTree(rhs)
+
+ case Return(expr, label) =>
+ mixTag(TagReturn)
+ mixTree(expr)
+ mixOptIdent(label)
+
+ case If(cond, thenp, elsep) =>
+ mixTag(TagIf)
+ mixTree(cond)
+ mixTree(thenp)
+ mixTree(elsep)
+ mixType(tree.tpe)
+
+ case While(cond, body, label) =>
+ mixTag(TagWhile)
+ mixTree(cond)
+ mixTree(body)
+ mixOptIdent(label)
+
+ case DoWhile(body, cond, label) =>
+ mixTag(TagDoWhile)
+ mixTree(body)
+ mixTree(cond)
+ mixOptIdent(label)
+
+ case Try(block, errVar, handler, finalizer) =>
+ mixTag(TagTry)
+ mixTree(block)
+ mixIdent(errVar)
+ mixTree(handler)
+ mixTree(finalizer)
+ mixType(tree.tpe)
+
+ case Throw(expr) =>
+ mixTag(TagThrow)
+ mixTree(expr)
+
+ case Continue(label) =>
+ mixTag(TagContinue)
+ mixOptIdent(label)
+
+ case Match(selector, cases, default) =>
+ mixTag(TagMatch)
+ mixTree(selector)
+ cases foreach { case (patterns, body) =>
+ mixTrees(patterns)
+ mixTree(body)
+ }
+ mixTree(default)
+ mixType(tree.tpe)
+
+ case Debugger() =>
+ mixTag(TagDebugger)
+
+ case New(cls, ctor, args) =>
+ mixTag(TagNew)
+ mixType(cls)
+ mixIdent(ctor)
+ mixTrees(args)
+
+ case LoadModule(cls) =>
+ mixTag(TagLoadModule)
+ mixType(cls)
+
+ case StoreModule(cls, value) =>
+ mixTag(TagStoreModule)
+ mixType(cls)
+ mixTree(value)
+
+ case Select(qualifier, item, mutable) =>
+ mixTag(TagSelect)
+ mixTree(qualifier)
+ mixIdent(item)
+ mixBoolean(mutable)
+ mixType(tree.tpe)
+
+ case Apply(receiver, method, args) =>
+ mixTag(TagApply)
+ mixTree(receiver)
+ mixIdent(method)
+ mixTrees(args)
+ mixType(tree.tpe)
+
+ case StaticApply(receiver, cls, method, args) =>
+ mixTag(TagStaticApply)
+ mixTree(receiver)
+ mixType(cls)
+ mixIdent(method)
+ mixTrees(args)
+ mixType(tree.tpe)
+
+ case TraitImplApply(impl, method, args) =>
+ mixTag(TagTraitImplApply)
+ mixType(impl)
+ mixIdent(method)
+ mixTrees(args)
+ mixType(tree.tpe)
+
+ case UnaryOp(op, lhs) =>
+ mixTag(TagUnaryOp)
+ mixInt(op)
+ mixTree(lhs)
+
+ case BinaryOp(op, lhs, rhs) =>
+ mixTag(TagBinaryOp)
+ mixInt(op)
+ mixTree(lhs)
+ mixTree(rhs)
+
+ case NewArray(tpe, lengths) =>
+ mixTag(TagNewArray)
+ mixType(tpe)
+ mixTrees(lengths)
+
+ case ArrayValue(tpe, elems) =>
+ mixTag(TagArrayValue)
+ mixType(tpe)
+ mixTrees(elems)
+
+ case ArrayLength(array) =>
+ mixTag(TagArrayLength)
+ mixTree(array)
+
+ case ArraySelect(array, index) =>
+ mixTag(TagArraySelect)
+ mixTree(array)
+ mixTree(index)
+ mixType(tree.tpe)
+
+ case RecordValue(tpe, elems) =>
+ mixTag(TagRecordValue)
+ mixType(tpe)
+ mixTrees(elems)
+
+ case IsInstanceOf(expr, cls) =>
+ mixTag(TagIsInstanceOf)
+ mixTree(expr)
+ mixType(cls)
+
+ case AsInstanceOf(expr, cls) =>
+ mixTag(TagAsInstanceOf)
+ mixTree(expr)
+ mixType(cls)
+
+ case Unbox(expr, charCode) =>
+ mixTag(TagUnbox)
+ mixTree(expr)
+ mixInt(charCode)
+
+ case GetClass(expr) =>
+ mixTag(TagGetClass)
+ mixTree(expr)
+
+ case CallHelper(helper, args) =>
+ mixTag(TagCallHelper)
+ mixString(helper)
+ mixTrees(args)
+ mixType(tree.tpe)
+
+ case JSNew(ctor, args) =>
+ mixTag(TagJSNew)
+ mixTree(ctor)
+ mixTrees(args)
+
+ case JSDotSelect(qualifier, item) =>
+ mixTag(TagJSDotSelect)
+ mixTree(qualifier)
+ mixIdent(item)
+
+ case JSBracketSelect(qualifier, item) =>
+ mixTag(TagJSBracketSelect)
+ mixTree(qualifier)
+ mixTree(item)
+
+ case JSFunctionApply(fun, args) =>
+ mixTag(TagJSFunctionApply)
+ mixTree(fun)
+ mixTrees(args)
+
+ case JSDotMethodApply(receiver, method, args) =>
+ mixTag(TagJSDotMethodApply)
+ mixTree(receiver)
+ mixIdent(method)
+ mixTrees(args)
+
+ case JSBracketMethodApply(receiver, method, args) =>
+ mixTag(TagJSBracketMethodApply)
+ mixTree(receiver)
+ mixTree(method)
+ mixTrees(args)
+
+ case JSDelete(prop) =>
+ mixTag(TagJSDelete)
+ mixTree(prop)
+
+ case JSUnaryOp(op, lhs) =>
+ mixTag(TagJSUnaryOp)
+ mixString(op)
+ mixTree(lhs)
+
+ case JSBinaryOp(op, lhs, rhs) =>
+ mixTag(TagJSBinaryOp)
+ mixString(op)
+ mixTree(lhs)
+ mixTree(rhs)
+
+ case JSArrayConstr(items) =>
+ mixTag(TagJSArrayConstr)
+ mixTrees(items)
+
+ case JSObjectConstr(fields) =>
+ mixTag(TagJSObjectConstr)
+ fields foreach { case (pn, value) =>
+ mixPropertyName(pn)
+ mixTree(value)
+ }
+
+ case JSEnvInfo() =>
+ mixTag(TagJSEnvInfo)
+
+ case Undefined() =>
+ mixTag(TagUndefined)
+
+ case UndefinedParam() =>
+ mixTag(TagUndefinedParam)
+ mixType(tree.tpe)
+
+ case Null() =>
+ mixTag(TagNull)
+
+ case BooleanLiteral(value) =>
+ mixTag(TagBooleanLiteral)
+ mixBoolean(value)
+
+ case IntLiteral(value) =>
+ mixTag(TagIntLiteral)
+ mixInt(value)
+
+ case LongLiteral(value) =>
+ mixTag(TagLongLiteral)
+ mixLong(value)
+
+ case FloatLiteral(value) =>
+ mixTag(TagFloatLiteral)
+ mixFloat(value)
+
+ case DoubleLiteral(value) =>
+ mixTag(TagDoubleLiteral)
+ mixDouble(value)
+
+ case StringLiteral(value) =>
+ mixTag(TagStringLiteral)
+ mixString(value)
+
+ case ClassOf(cls) =>
+ mixTag(TagClassOf)
+ mixType(cls)
+
+ case VarRef(ident, mutable) =>
+ mixTag(TagVarRef)
+ mixIdent(ident)
+ mixBoolean(mutable)
+ mixType(tree.tpe)
+
+ case This() =>
+ mixTag(TagThis)
+ mixType(tree.tpe)
+
+ case Closure(captureParams, params, body, captureValues) =>
+ mixTag(TagClosure)
+ mixTrees(captureParams)
+ mixTrees(params)
+ mixTree(body)
+ mixTrees(captureValues)
+
+ case _ =>
+ sys.error(s"Unable to hash tree of class ${tree.getClass}")
+
+ }
+ }
+
+ def mixTrees(trees: List[Tree]): Unit =
+ trees.foreach(mixTree)
+
+ def mixType(tpe: Type): Unit = tpe match {
+ case AnyType => mixTag(TagAnyType)
+ case NothingType => mixTag(TagNothingType)
+ case UndefType => mixTag(TagUndefType)
+ case BooleanType => mixTag(TagBooleanType)
+ case IntType => mixTag(TagIntType)
+ case LongType => mixTag(TagLongType)
+ case FloatType => mixTag(TagFloatType)
+ case DoubleType => mixTag(TagDoubleType)
+ case StringType => mixTag(TagStringType)
+ case NullType => mixTag(TagNullType)
+ case NoType => mixTag(TagNoType)
+
+ case tpe: ClassType =>
+ mixTag(TagClassType)
+ mixString(tpe.className)
+
+ case tpe: ArrayType =>
+ mixTag(TagArrayType)
+ mixString(tpe.baseClassName)
+ mixInt(tpe.dimensions)
+
+ case RecordType(fields) =>
+ mixTag(TagRecordType)
+ for (RecordType.Field(name, originalName, tpe, mutable) <- fields) {
+ mixString(name)
+ originalName.foreach(mixString)
+ mixType(tpe)
+ mixBoolean(mutable)
+ }
+ }
+
+ def mixIdent(ident: Ident): Unit = {
+ mixPos(ident.pos)
+ mixString(ident.name)
+ ident.originalName.foreach(mixString)
+ }
+
+ def mixOptIdent(optIdent: Option[Ident]): Unit = optIdent.foreach(mixIdent)
+
+ def mixPropertyName(name: PropertyName): Unit = name match {
+ case name: Ident => mixIdent(name)
+ case name: StringLiteral => mixTree(name)
+ }
+
+ def mixPos(pos: Position): Unit = {
+ posStream.writeUTF(pos.source.toString)
+ posStream.writeInt(pos.line)
+ posStream.writeInt(pos.column)
+ }
+
+ @inline
+ private final def mixTag(tag: Int): Unit = mixInt(tag)
+
+ @inline
+ private final def mixString(str: String): Unit = treeStream.writeUTF(str)
+
+ @inline
+ private final def mixInt(i: Int): Unit = treeStream.writeInt(i)
+
+ @inline
+ private final def mixLong(l: Long): Unit = treeStream.writeLong(l)
+
+ @inline
+ private final def mixBoolean(b: Boolean): Unit = treeStream.writeBoolean(b)
+
+ @inline
+ private final def mixFloat(f: Float): Unit = treeStream.writeFloat(f)
+
+ @inline
+ private final def mixDouble(d: Double): Unit = treeStream.writeDouble(d)
+
+ }
+
+}
diff --git a/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/InfoSerializers.scala b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/InfoSerializers.scala
new file mode 100644
index 0000000..dfb520f
--- /dev/null
+++ b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/InfoSerializers.scala
@@ -0,0 +1,180 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js IR **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.ir
+
+import java.io._
+
+import Infos._
+
+object InfoSerializers {
+
+ /** Scala.js IR File Magic Number
+ *
+ * CA FE : first part of magic number of Java class files
+ * 4A 53 : "JS" in ASCII
+ *
+ */
+ final val IRMagicNumber = 0xCAFE4A53
+
+ def serialize(stream: OutputStream, classInfo: ClassInfo): Unit = {
+ new Serializer().serialize(stream, classInfo)
+ }
+
+ def deserializeRoughInfo(stream: InputStream): RoughClassInfo = {
+ deserializeVersionRoughInfo(stream)._2
+ }
+
+ def deserializeFullInfo(stream: InputStream): ClassInfo = {
+ deserializeVersionFullInfo(stream)._2
+ }
+
+ def deserializeVersionRoughInfo(stream: InputStream): (String, RoughClassInfo) = {
+ new Deserializer(stream).deserializeRough()
+ }
+
+ def deserializeVersionFullInfo(stream: InputStream): (String, ClassInfo) = {
+ new Deserializer(stream).deserializeFull()
+ }
+
+ private final class Serializer {
+ def serialize(stream: OutputStream, classInfo: ClassInfo): Unit = {
+ val s = new DataOutputStream(stream)
+
+ def writeSeq[A](seq: Seq[A])(writeElem: A => Unit): Unit = {
+ s.writeInt(seq.size)
+ seq.foreach(writeElem)
+ }
+
+ def writeStrings(seq: Seq[String]): Unit =
+ writeSeq(seq)(s.writeUTF(_))
+
+ // Write the Scala.js IR magic number
+ s.writeInt(IRMagicNumber)
+
+ // Write the Scala.js Version
+ s.writeUTF(ScalaJSVersions.binaryEmitted)
+
+ import classInfo._
+ s.writeUTF(name)
+ s.writeUTF(encodedName)
+ s.writeBoolean(isExported)
+ s.writeInt(ancestorCount)
+ s.writeByte(ClassKind.toByte(kind))
+ s.writeUTF(superClass)
+ writeStrings(ancestors)
+ s.writeInt(optimizerHints.bits)
+
+ def writeMethodInfo(methodInfo: MethodInfo): Unit = {
+ import methodInfo._
+ s.writeUTF(encodedName)
+ s.writeBoolean(isAbstract)
+ s.writeBoolean(isExported)
+ writeSeq(calledMethods.toSeq) {
+ case (caller, callees) => s.writeUTF(caller); writeStrings(callees)
+ }
+ writeSeq(calledMethodsStatic.toSeq) {
+ case (caller, callees) => s.writeUTF(caller); writeStrings(callees)
+ }
+ writeStrings(instantiatedClasses)
+ writeStrings(accessedModules)
+ writeStrings(accessedClassData)
+ s.writeInt(optimizerHints.bits)
+ }
+
+ writeSeq(methods)(writeMethodInfo(_))
+
+ s.flush()
+ }
+ }
+
+ private final class Deserializer(stream: InputStream) {
+ private[this] val input = new DataInputStream(stream)
+
+ def readList[A](readElem: => A): List[A] =
+ List.fill(input.readInt())(readElem)
+
+ def readStrings(): List[String] =
+ readList(input.readUTF())
+
+ def deserializeRough(): (String, RoughClassInfo) = {
+ val version = readHeader()
+
+ import input._
+ val name = readUTF()
+ val encodedName = readUTF()
+ val isExported = readBoolean()
+ val ancestorCount = readInt()
+ val info = RoughClassInfo(name, encodedName, isExported, ancestorCount)
+
+ (version, info)
+ }
+
+ def deserializeFull(): (String, ClassInfo) = {
+ val version = readHeader()
+
+ import input._
+
+ val name = readUTF()
+ val encodedName = readUTF()
+ val isExported = readBoolean()
+ val ancestorCount = readInt()
+ val kind = ClassKind.fromByte(readByte())
+ val superClass = readUTF()
+ val ancestors = readList(readUTF())
+
+ val optimizerHints =
+ if (version == "0.5.0" || version == "0.5.2") OptimizerHints.empty
+ else new OptimizerHints(readInt())
+
+ def readMethod(): MethodInfo = {
+ val encodedName = readUTF()
+ val isAbstract = readBoolean()
+ val isExported = readBoolean()
+ val calledMethods = readList(readUTF() -> readStrings()).toMap
+ val calledMethodsStatic = readList(readUTF() -> readStrings()).toMap
+ val instantiatedClasses = readStrings()
+ val accessedModules = readStrings()
+ val accessedClassData = readStrings()
+ val optimizerHints = new OptimizerHints(readInt())
+ MethodInfo(encodedName, isAbstract, isExported,
+ calledMethods, calledMethodsStatic,
+ instantiatedClasses, accessedModules, accessedClassData,
+ optimizerHints)
+ }
+
+ val methods = readList(readMethod())
+
+ val info = ClassInfo(name, encodedName, isExported, ancestorCount, kind,
+ superClass, ancestors, optimizerHints, methods)
+
+ (version, info)
+ }
+
+ /** Reads the Scala.js IR header and verifies the version compatibility.
+ * Returns the emitted binary version.
+ */
+ def readHeader(): String = {
+ // Check magic number
+ if (input.readInt() != IRMagicNumber)
+ throw new IOException("Not a Scala.js IR file")
+
+ // Check that we support this version of the IR
+ val version = input.readUTF()
+ val supported = ScalaJSVersions.binarySupported
+ if (!supported.contains(version)) {
+ throw new IOException(
+ s"This version ($version) of Scala.js IR is not supported. " +
+ s"Supported versions are: ${supported.mkString(", ")}")
+ }
+
+ version
+ }
+ }
+}
diff --git a/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Infos.scala b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Infos.scala
new file mode 100644
index 0000000..66feec2
--- /dev/null
+++ b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Infos.scala
@@ -0,0 +1,118 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js IR **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.ir
+
+object Infos {
+
+ sealed class RoughClassInfo protected (
+ val name: String,
+ val encodedName: String,
+ val isExported: Boolean,
+ val ancestorCount: Int
+ )
+
+ object RoughClassInfo {
+ def apply(name: String, encodedName: String, isExported: Boolean,
+ ancestorCount: Int): RoughClassInfo = {
+ new RoughClassInfo(name, encodedName, isExported, ancestorCount)
+ }
+ }
+
+ final class ClassInfo protected (
+ name: String,
+ encodedName: String,
+ isExported: Boolean,
+ ancestorCount: Int,
+ val kind: ClassKind,
+ val superClass: String,
+ val ancestors: List[String], // includes this class
+ val optimizerHints: OptimizerHints,
+ val methods: List[MethodInfo]
+ ) extends RoughClassInfo(name, encodedName, isExported, ancestorCount)
+
+ object ClassInfo {
+ def apply(
+ name: String,
+ encodedName: String,
+ isExported: Boolean = false,
+ ancestorCount: Int = 0,
+ kind: ClassKind = ClassKind.Class,
+ superClass: String = "",
+ ancestors: List[String] = Nil,
+ optimizerHints: OptimizerHints = OptimizerHints.empty,
+ methods: List[MethodInfo] = Nil): ClassInfo = {
+ new ClassInfo(name, encodedName, isExported, ancestorCount,
+ kind, superClass, ancestors, optimizerHints, methods)
+ }
+ }
+
+ final class MethodInfo private (
+ val encodedName: String,
+ val isAbstract: Boolean,
+ val isExported: Boolean,
+ val calledMethods: Map[String, List[String]],
+ val calledMethodsStatic: Map[String, List[String]],
+ val instantiatedClasses: List[String],
+ val accessedModules: List[String],
+ val accessedClassData: List[String],
+ val optimizerHints: OptimizerHints
+ )
+
+ object MethodInfo {
+ def apply(
+ encodedName: String,
+ isAbstract: Boolean = false,
+ isExported: Boolean = false,
+ calledMethods: Map[String, List[String]] = Map.empty,
+ calledMethodsStatic: Map[String, List[String]] = Map.empty,
+ instantiatedClasses: List[String] = Nil,
+ accessedModules: List[String] = Nil,
+ accessedClassData: List[String] = Nil,
+ optimizerHints: OptimizerHints = OptimizerHints.empty): MethodInfo = {
+ new MethodInfo(encodedName, isAbstract, isExported, calledMethods,
+ calledMethodsStatic, instantiatedClasses, accessedModules,
+ accessedClassData, optimizerHints)
+ }
+ }
+
+ final class OptimizerHints(val bits: Int) extends AnyVal {
+ import OptimizerHints._
+
+ private[scalajs] def isAccessor: Boolean = (bits & AccessorMask) != 0
+ private[scalajs] def hasInlineAnnot: Boolean = (bits & InlineAnnotMask) != 0
+
+ private[scalajs] def copy(
+ isAccessor: Boolean = this.isAccessor,
+ hasInlineAnnot: Boolean = this.hasInlineAnnot
+ ): OptimizerHints = {
+ var bits: Int = 0
+ if (isAccessor)
+ bits |= AccessorMask
+ if (hasInlineAnnot)
+ bits |= InlineAnnotMask
+ new OptimizerHints(bits)
+ }
+
+ override def toString(): String =
+ s"OptimizerHints($bits)"
+ }
+
+ object OptimizerHints {
+ private final val AccessorShift = 0
+ private final val AccessorMask = 1 << AccessorShift
+
+ private final val InlineAnnotShift = 1
+ private final val InlineAnnotMask = 1 << InlineAnnotShift
+
+ final val empty: OptimizerHints =
+ new OptimizerHints(0)
+ }
+
+}
diff --git a/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Position.scala b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Position.scala
new file mode 100644
index 0000000..3b6d0a2
--- /dev/null
+++ b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Position.scala
@@ -0,0 +1,42 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js IR **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.ir
+
+final case class Position(
+ /** Source file. */
+ source: Position.SourceFile,
+ /** Zero-based line number. */
+ line: Int,
+ /** Zero-based column number. */
+ column: Int
+) {
+ def show: String = s"$line:$column"
+
+ def isEmpty: Boolean = {
+ source.getScheme == null && source.getRawAuthority == null &&
+ source.getRawPath == "" && source.getRawQuery == null &&
+ source.getRawFragment == null
+ }
+
+ def isDefined: Boolean = !isEmpty
+
+ def orElse(that: => Position): Position = if (isDefined) this else that
+}
+
+object Position {
+ type SourceFile = java.net.URI
+
+ object SourceFile {
+ def apply(f: java.io.File): SourceFile = f.toURI
+ def apply(f: String): SourceFile = new java.net.URI(f)
+ }
+
+ val NoPosition = Position(SourceFile(""), 0, 0)
+}
diff --git a/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Printers.scala b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Printers.scala
new file mode 100644
index 0000000..6208d5f
--- /dev/null
+++ b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Printers.scala
@@ -0,0 +1,709 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js IR **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.ir
+
+import scala.annotation.{switch, tailrec}
+
+import java.io.Writer
+
+import Position._
+import Trees._
+import Types._
+import Infos._
+import Utils.escapeJS
+
+object Printers {
+
+ /** Basically copied from scala.reflect.internal.Printers */
+ trait IndentationManager {
+ protected val out: Writer
+
+ protected var indentMargin = 0
+ protected val indentStep = 2
+ protected var indentString = " " // 40
+
+ protected def indent(): Unit = indentMargin += indentStep
+ protected def undent(): Unit = indentMargin -= indentStep
+
+ protected def println(): Unit = {
+ out.write('\n')
+ while (indentMargin > indentString.length())
+ indentString += indentString
+ if (indentMargin > 0)
+ out.write(indentString, 0, indentMargin)
+ }
+
+ @tailrec
+ protected final def printSeq[A](ls: List[A])(printelem: A => Unit)(
+ printsep: A => Unit): Unit = {
+ ls match {
+ case Nil =>
+ case x :: Nil =>
+ printelem(x)
+ case x :: rest =>
+ printelem(x)
+ printsep(x)
+ printSeq(rest)(printelem)(printsep)
+ }
+ }
+
+ protected def printColumn(ts: List[Any], start: String, sep: String,
+ end: String): Unit = {
+ print(start); indent(); println()
+ printSeq(ts) { x =>
+ print(x)
+ } { _ =>
+ print(sep)
+ println()
+ }
+ undent(); println(); print(end)
+ }
+
+ protected def printRow(ts: List[Any], start: String, sep: String,
+ end: String): Unit = {
+ print(start)
+ printSeq(ts) { x =>
+ print(x)
+ } { _ =>
+ print(sep)
+ }
+ print(end)
+ }
+
+ protected def printRow(ts: List[Any], sep: String): Unit =
+ printRow(ts, "", sep, "")
+
+ protected def print(args: Any*): Unit =
+ args.foreach(printOne)
+
+ protected def printOne(arg: Any): Unit
+ }
+
+ class IRTreePrinter(protected val out: Writer) extends IndentationManager {
+ def printTopLevelTree(tree: Tree) {
+ tree match {
+ case Skip() =>
+ // do not print anything
+ case Block(stats) =>
+ for (stat <- stats)
+ printTopLevelTree(stat)
+ case _ =>
+ printTree(tree)
+ println()
+ }
+ }
+
+ protected def printBlock(tree: Tree): Unit = {
+ val trees = tree match {
+ case Block(trees) => trees
+ case _ => List(tree)
+ }
+ printColumn(trees, "{", ";", "}")
+ }
+
+ protected def printSig(args: List[ParamDef], resultType: Type): Unit = {
+ printRow(args, "(", ", ", ")")
+ if (resultType != NoType)
+ print(": ", resultType, " = ")
+ else
+ print(" ")
+ }
+
+ protected def printArgs(args: List[Tree]): Unit = {
+ printRow(args, "(", ", ", ")")
+ }
+
+ def printTree(tree: Tree): Unit = {
+ tree match {
+ case EmptyTree =>
+ print("<empty>")
+
+ // Definitions
+
+ case VarDef(ident, vtpe, mutable, rhs) =>
+ if (mutable)
+ print("var ")
+ else
+ print("val ")
+ print(ident, ": ", vtpe)
+ if (rhs != EmptyTree)
+ print(" = ", rhs)
+
+ case ParamDef(ident, ptpe, mutable) =>
+ if (mutable)
+ print("var ")
+ print(ident, ": ", ptpe)
+
+ // Control flow constructs
+
+ case Skip() =>
+ print("/*<skip>*/")
+
+ case tree: Block =>
+ printBlock(tree)
+
+ case Labeled(label, tpe, body) =>
+ print(label)
+ if (tpe != NoType)
+ print("[", tpe, "]")
+ print(": ")
+ printBlock(body)
+
+ case Assign(lhs, rhs) =>
+ print(lhs, " = ", rhs)
+
+ case Return(expr, label) =>
+ if (label.isEmpty) print("return ", expr)
+ else print("return(", label.get, ") ", expr)
+
+ case If(cond, BooleanLiteral(true), elsep) =>
+ print(cond, " || ", elsep)
+ case If(cond, thenp, BooleanLiteral(false)) =>
+ print(cond, " && ", thenp)
+
+ case If(cond, thenp, elsep) =>
+ print("if (", cond, ") ")
+ printBlock(thenp)
+ elsep match {
+ case Skip() => ()
+ case If(_, _, _) =>
+ print(" else ")
+ printTree(elsep)
+ case _ =>
+ print(" else ")
+ printBlock(elsep)
+ }
+
+ case While(cond, body, label) =>
+ if (label.isDefined)
+ print(label.get, ": ")
+ print("while (", cond, ") ")
+ printBlock(body)
+
+ case DoWhile(body, cond, label) =>
+ if (label.isDefined)
+ print(label.get, ": ")
+ print("do ")
+ printBlock(body)
+ print(" while (", cond, ")")
+
+ case Try(block, errVar, handler, finalizer) =>
+ print("try ")
+ printBlock(block)
+ if (handler != EmptyTree) {
+ print(" catch (", errVar, ") ")
+ printBlock(handler)
+ }
+ if (finalizer != EmptyTree) {
+ print(" finally ")
+ printBlock(finalizer)
+ }
+
+ case Throw(expr) =>
+ print("throw ", expr)
+
+ case Continue(label) =>
+ if (label.isEmpty) print("continue")
+ else print("continue ", label.get)
+
+ case Match(selector, cases, default) =>
+ print("match (", selector, ") ")
+ print("{"); indent
+ for ((values, body) <- cases) {
+ println()
+ printRow(values, "case ", " | ", ":"); indent; println()
+ printTree(body)
+ print(";")
+ undent
+ }
+ if (default != EmptyTree) {
+ println()
+ print("default:"); indent; println()
+ printTree(default)
+ print(";")
+ undent
+ }
+ undent; println(); print("}")
+
+ case Debugger() =>
+ print("debugger")
+
+ // Scala expressions
+
+ case New(cls, ctor, args) =>
+ print("new ", cls, "().", ctor)
+ printArgs(args)
+
+ case LoadModule(cls) =>
+ print("mod:", cls)
+
+ case StoreModule(cls, value) =>
+ print("mod:", cls, "<-", value)
+
+ case Select(qualifier, item, _) =>
+ print(qualifier, ".", item)
+
+ case Apply(receiver, method, args) =>
+ print(receiver, ".", method)
+ printArgs(args)
+
+ case StaticApply(receiver, cls, method, args) =>
+ print(receiver, ".", cls, "::", method)
+ printArgs(args)
+
+ case TraitImplApply(impl, method, args) =>
+ print(impl, "::", method)
+ printArgs(args)
+
+ case UnaryOp(op, lhs) =>
+ import UnaryOp._
+ print("(", (op: @switch) match {
+ case `typeof` => "typeof"
+ case Boolean_! => "!"
+ case IntToLong | DoubleToLong => "(long)"
+ case DoubleToInt | LongToInt => "(int)"
+ case DoubleToFloat => "(float)"
+ case LongToDouble => "(double)"
+ }, lhs, ")")
+
+ case BinaryOp(BinaryOp.Int_-, IntLiteral(0), rhs) =>
+ print("(-", rhs, ")")
+ case BinaryOp(BinaryOp.Int_^, IntLiteral(-1), rhs) =>
+ print("(~", rhs, ")")
+ case BinaryOp(BinaryOp.Long_-, LongLiteral(0L), rhs) =>
+ print("(-", rhs, ")")
+ case BinaryOp(BinaryOp.Long_^, LongLiteral(-1L), rhs) =>
+ print("(~", rhs, ")")
+ case BinaryOp(BinaryOp.Float_-, FloatLiteral(0.0f), rhs) =>
+ print("(-", rhs, ")")
+ case BinaryOp(BinaryOp.Double_-,
+ IntLiteral(0) | FloatLiteral(0.0f) | DoubleLiteral(0.0), rhs) =>
+ print("(-", rhs, ")")
+
+ case BinaryOp(op, lhs, rhs) =>
+ import BinaryOp._
+ print("(", lhs, " ", (op: @switch) match {
+ case === => "==="
+ case !== => "!=="
+
+ case String_+ => "+[string]"
+
+ case `in` => "in"
+ case `instanceof` => "instanceof"
+
+ case Int_+ => "+[int]"
+ case Int_- => "-[int]"
+ case Int_* => "*[int]"
+ case Int_/ => "/[int]"
+ case Int_% => "%[int]"
+
+ case Int_| => "|"
+ case Int_& => "&"
+ case Int_^ => "^"
+ case Int_<< => "<<"
+ case Int_>>> => ">>>"
+ case Int_>> => ">>"
+
+ case Float_+ => "+[float]"
+ case Float_- => "-[float]"
+ case Float_* => "*[float]"
+ case Float_/ => "/[float]"
+ case Float_% => "%[float]"
+
+ case Double_+ => "+"
+ case Double_- => "-"
+ case Double_* => "*"
+ case Double_/ => "/"
+ case Double_% => "%"
+
+ case Num_== => "=="
+ case Num_!= => "!="
+ case Num_< => "<"
+ case Num_<= => "<="
+ case Num_> => ">"
+ case Num_>= => ">="
+
+ case Long_+ => "+[long]"
+ case Long_- => "-[long]"
+ case Long_* => "*[long]"
+ case Long_/ => "/[long]"
+ case Long_% => "%[long]"
+
+ case Long_| => "|[long]"
+ case Long_& => "&[long]"
+ case Long_^ => "^[long]"
+ case Long_<< => "<<[long]"
+ case Long_>>> => ">>>[long]"
+ case Long_>> => ">>[long]"
+
+ case Long_== => "==[long]"
+ case Long_!= => "!=[long]"
+ case Long_< => "<[long]"
+ case Long_<= => "<=[long]"
+ case Long_> => ">[long]"
+ case Long_>= => ">=[long]"
+
+ case Boolean_== => "==[bool]"
+ case Boolean_!= => "!=[bool]"
+ case Boolean_| => "|[bool]"
+ case Boolean_& => "&[bool]"
+ }, " ", rhs, ")")
+
+ case NewArray(tpe, lengths) =>
+ print("new ", tpe.baseClassName)
+ for (length <- lengths)
+ print("[", length, "]")
+ for (dim <- lengths.size until tpe.dimensions)
+ print("[]")
+
+ case ArrayValue(tpe, elems) =>
+ print(tpe)
+ printArgs(elems)
+
+ case ArrayLength(array) =>
+ print(array, ".length")
+
+ case ArraySelect(array, index) =>
+ print(array, "[", index, "]")
+
+ case RecordValue(tpe, elems) =>
+ print("(")
+ var first = true
+ for ((field, value) <- tpe.fields zip elems) {
+ if (first) first = false
+ else print(", ")
+ print(field.name, " = ", value)
+ }
+ print(")")
+
+ case IsInstanceOf(expr, cls) =>
+ print(expr, ".isInstanceOf[", cls, "]")
+
+ case AsInstanceOf(expr, cls) =>
+ print(expr, ".asInstanceOf[", cls, "]")
+
+ case Unbox(expr, charCode) =>
+ print(expr, ".asInstanceOf[", charCode, "]")
+
+ case GetClass(expr) =>
+ print(expr, ".getClass()")
+
+ case CallHelper(helper, args) =>
+ print(helper)
+ printArgs(args)
+
+ // JavaScript expressions
+
+ case JSNew(ctor, args) =>
+ def containsOnlySelectsFromAtom(tree: Tree): Boolean = tree match {
+ case JSDotSelect(qual, _) => containsOnlySelectsFromAtom(qual)
+ case JSBracketSelect(qual, _) => containsOnlySelectsFromAtom(qual)
+ case VarRef(_, _) => true
+ case This() => true
+ case _ => false // in particular, Apply
+ }
+ if (containsOnlySelectsFromAtom(ctor))
+ print("new ", ctor)
+ else
+ print("new (", ctor, ")")
+ printArgs(args)
+
+ case JSDotSelect(qualifier, item) =>
+ print(qualifier, ".", item)
+
+ case JSBracketSelect(qualifier, item) =>
+ print(qualifier, "[", item, "]")
+
+ case JSFunctionApply(fun, args) =>
+ fun match {
+ case _:JSDotSelect | _:JSBracketSelect | _:Select =>
+ print("protect(", fun, ")")
+ case _ =>
+ print(fun)
+ }
+ printArgs(args)
+
+ case JSDotMethodApply(receiver, method, args) =>
+ print(receiver, ".", method)
+ printArgs(args)
+
+ case JSBracketMethodApply(receiver, method, args) =>
+ print(receiver, "[", method, "]")
+ printArgs(args)
+
+ case JSDelete(prop) =>
+ print("delete ", prop)
+
+ case JSUnaryOp("typeof", lhs) =>
+ print("typeof(", lhs, ")")
+
+ case JSUnaryOp(op, lhs) =>
+ print("(", op, lhs, ")")
+
+ case JSBinaryOp(op, lhs, rhs) =>
+ print("(", lhs, " ", op, " ", rhs, ")")
+
+ case JSArrayConstr(items) =>
+ printRow(items, "[", ", ", "]")
+
+ case JSObjectConstr(Nil) =>
+ print("{}")
+
+ case JSObjectConstr(fields) =>
+ print("{"); indent; println()
+ printSeq(fields) {
+ case (name, value) => print(name, ": ", value)
+ } { _ =>
+ print(",")
+ println()
+ }
+ undent; println(); print("}")
+
+ case JSEnvInfo() =>
+ print("<envinfo>")
+
+ // Literals
+
+ case Undefined() =>
+ print("(void 0)")
+
+ case UndefinedParam() =>
+ print("<undefined param>")
+
+ case Null() =>
+ print("null")
+
+ case BooleanLiteral(value) =>
+ print(if (value) "true" else "false")
+
+ case IntLiteral(value) =>
+ if (value >= 0)
+ print(value)
+ else
+ print("(", value, ")")
+
+ case FloatLiteral(value) =>
+ if (value == 0.0f && 1.0f / value < 0.0f)
+ print("(-0)")
+ else if (value >= 0.0f)
+ print(value)
+ else
+ print("(", value, ")")
+
+ case DoubleLiteral(value) =>
+ if (value == 0.0 && 1.0 / value < 0.0)
+ print("(-0)")
+ else if (value >= 0.0)
+ print(value)
+ else
+ print("(", value, ")")
+
+ case StringLiteral(value) =>
+ print("\"", escapeJS(value), "\"")
+
+ case ClassOf(cls) =>
+ print("classOf[", cls, "]")
+
+ // Atomic expressions
+
+ case VarRef(ident, _) =>
+ print(ident)
+
+ case This() =>
+ print("this")
+
+ case Closure(captureParams, params, body, captureValues) =>
+ print("(lambda")
+ printRow(captureValues, "<", ", ", ">")
+ printRow(captureParams ++ params, "(", ", ", ") = ")
+ printBlock(body)
+ print(")")
+
+ // Classes
+
+ case ClassDef(name, kind, parent, ancestors, defs) =>
+ kind match {
+ case ClassKind.Class => print("class ")
+ case ClassKind.ModuleClass => print("module class ")
+ case ClassKind.Interface => print("interface ")
+ case ClassKind.RawJSType => print("jstype ")
+ case ClassKind.HijackedClass => print("hijacked class ")
+ case ClassKind.TraitImpl => print("trait impl ")
+ }
+ print(name)
+ parent.foreach(print(" extends ", _))
+ if (ancestors.nonEmpty)
+ printRow(ancestors, " ancestors ", ", ", "")
+ print(" ")
+ printColumn(defs, "{", "", "}")
+ println()
+
+ case MethodDef(name, args, resultType, body) =>
+ print(name)
+ printSig(args, resultType)
+ printBlock(body)
+
+ case PropertyDef(name, _, _, _) =>
+ // TODO
+ print(s"<property: $name>")
+
+ case ConstructorExportDef(fullName, args, body) =>
+ print("export \"", escapeJS(fullName), "\"")
+ printSig(args, NoType) // NoType as trick not to display a type
+ printBlock(body)
+
+ case ModuleExportDef(fullName) =>
+ print("export \"", escapeJS(fullName), "\"")
+
+ case _ =>
+ print(s"<error, elem of class ${tree.getClass()}>")
+ }
+ }
+
+ def printType(tpe: Type): Unit = tpe match {
+ case AnyType => print("any")
+ case NothingType => print("nothing")
+ case UndefType => print("void")
+ case BooleanType => print("boolean")
+ case IntType => print("int")
+ case LongType => print("long")
+ case FloatType => print("float")
+ case DoubleType => print("number")
+ case StringType => print("string")
+ case NullType => print("null")
+ case ClassType(className) => print(className)
+ case NoType => print("<notype>")
+
+ case ArrayType(base, dims) =>
+ print(base)
+ for (i <- 1 to dims)
+ print("[]")
+
+ case RecordType(fields) =>
+ print("(")
+ var first = false
+ for (RecordType.Field(name, _, tpe, mutable) <- fields) {
+ if (first) first = false
+ else print(", ")
+ if (mutable)
+ print("var ")
+ print(name, ": ", tpe)
+ }
+ print(")")
+ }
+
+ protected def printIdent(ident: Ident): Unit =
+ printString(escapeJS(ident.name))
+
+ protected def printOne(arg: Any): Unit = arg match {
+ case tree: Tree =>
+ printTree(tree)
+ case tpe: Type =>
+ printType(tpe)
+ case ident: Ident =>
+ printIdent(ident)
+ case arg =>
+ printString(if (arg == null) "null" else arg.toString)
+ }
+
+ protected def printString(s: String): Unit = {
+ out.write(s)
+ }
+
+ // Make it public
+ override def println(): Unit = super.println()
+
+ def complete(): Unit = ()
+ }
+
+ class InfoPrinter(protected val out: Writer) extends IndentationManager {
+ def printClassInfo(classInfo: ClassInfo): Unit = {
+ import classInfo._
+ println("name: ", escapeJS(name))
+ println("encodedName: ", escapeJS(encodedName))
+ println("isExported: ", isExported)
+ println("ancestorCount: ", ancestorCount)
+ println("kind: ", kind)
+ println("superClass: ", superClass)
+
+ if (ancestors.nonEmpty) {
+ println("ancestors: ",
+ ancestors.map(escapeJS).mkString("[", ", ", "]"))
+ }
+
+ if (optimizerHints != OptimizerHints.empty)
+ println("optimizerHints: ", optimizerHints)
+
+ print("methods:")
+ indent(); println()
+ methods.foreach(printMethodInfo)
+ undent(); println()
+ }
+
+ def printMethodInfo(methodInfo: MethodInfo): Unit = {
+ import methodInfo._
+ print(escapeJS(encodedName), ":")
+ indent(); println()
+
+ if (isAbstract)
+ println("isAbstract: ", isAbstract)
+ if (isExported)
+ println("isExported: ", isExported)
+ if (calledMethods.nonEmpty) {
+ print("calledMethods:")
+ indent(); println()
+ printSeq(calledMethods.toList) { case (caller, callees) =>
+ print(escapeJS(caller), ": ")
+ print(callees.map(escapeJS).mkString("[", ", ", "]"))
+ } { _ => println() }
+ undent(); println()
+ }
+ if (calledMethodsStatic.nonEmpty) {
+ print("calledMethodsStatic:")
+ indent(); println()
+ printSeq(calledMethodsStatic.toList) { case (caller, callees) =>
+ print(escapeJS(caller), ": ")
+ print(callees.map(escapeJS).mkString("[", ", ", "]"))
+ } { _ => println() }
+ undent(); println()
+ }
+ if (instantiatedClasses.nonEmpty) {
+ println("instantiatedClasses: ",
+ instantiatedClasses.map(escapeJS).mkString("[", ", ", "]"))
+ }
+ if (accessedModules.nonEmpty) {
+ println("accessedModules: ",
+ accessedModules.map(escapeJS).mkString("[", ", ", "]"))
+ }
+ if (accessedClassData.nonEmpty) {
+ println("accessedClassData: ",
+ accessedClassData.map(escapeJS).mkString("[", ", ", "]"))
+ }
+ if (optimizerHints != OptimizerHints.empty)
+ println("optimizerHints: ", optimizerHints)
+
+ undent(); println()
+ }
+
+ private def println(arg1: Any, args: Any*): Unit = {
+ print((arg1 +: args): _*)
+ println()
+ }
+
+ protected def printOne(arg: Any): Unit = arg match {
+ case classInfo: ClassInfo => printClassInfo(classInfo)
+ case methodInfo: MethodInfo => printMethodInfo(methodInfo)
+ case arg => out.write(arg.toString())
+ }
+
+ def complete(): Unit = ()
+ }
+
+}
diff --git a/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/ScalaJSVersions.scala b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/ScalaJSVersions.scala
new file mode 100644
index 0000000..2690939
--- /dev/null
+++ b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/ScalaJSVersions.scala
@@ -0,0 +1,25 @@
+package scala.scalajs.ir
+
+object ScalaJSVersions {
+
+ /** the Scala.js version of this build */
+ final val current = "0.6.0-SNAPSHOT"
+
+ /** true iff the Scala.js version of this build is a snapshot version. */
+ final val currentIsSnapshot = current endsWith "-SNAPSHOT"
+
+ /** Version of binary IR this Scala.js version emits
+ *
+ * This should be either of:
+ * - a prior release version (i.e. "0.5.0", *not* "0.5.0-SNAPSHOT")
+ * - `current`
+ */
+ final val binaryEmitted = current
+
+ /** Versions whose binary files we can support (used by deserializer) */
+ val binarySupported: Set[String] = Set(binaryEmitted)
+
+ // Just to be extra safe
+ assert(binarySupported contains binaryEmitted)
+
+}
diff --git a/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Serializers.scala b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Serializers.scala
new file mode 100644
index 0000000..04ec5c2
--- /dev/null
+++ b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Serializers.scala
@@ -0,0 +1,790 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js IR **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.ir
+
+import scala.annotation.switch
+
+import java.io._
+import java.net.URI
+
+import scala.collection.mutable
+
+import Position._
+import Trees._
+import Types._
+import Tags._
+
+import Utils.JumpBackByteArrayOutputStream
+
+object Serializers {
+ def serialize(stream: OutputStream, tree: Tree): Unit = {
+ new Serializer().serialize(stream, tree)
+ }
+
+ def deserialize(stream: InputStream, version: String): Tree = {
+ new Deserializer(stream, version).deserialize()
+ }
+
+ // true for easier debugging (not for "production", it adds 8 bytes per node)
+ private final val UseDebugMagic = false
+ private final val DebugMagic = 0x3fa8ef84
+ private final val PosDebugMagic = 0x65f0ec32
+
+ private object PositionFormat {
+ /* Positions are serialized incrementally as diffs wrt the last position.
+ *
+ * Formats are (the first byte is decomposed in bits):
+ *
+ * 1st byte | next bytes | description
+ * -----------------------------------------
+ * ccccccc0 | | Column diff (7-bit signed)
+ * llllll01 | CC | Line diff (6-bit signed), column (8-bit unsigned)
+ * ____0011 | LL LL CC | Line diff (16-bit signed), column (8-bit unsigned)
+ * ____0111 | 12 bytes | File index, line, column (all 32-bit signed)
+ * 11111111 | | NoPosition (is not compared/stored in last position)
+ *
+ * Underscores are irrelevant and must be set to 0.
+ */
+
+ final val Format1Mask = 0x01
+ final val Format1MaskValue = 0x00
+ final val Format1Shift = 1
+
+ final val Format2Mask = 0x03
+ final val Format2MaskValue = 0x01
+ final val Format2Shift = 2
+
+ final val Format3Mask = 0x0f
+ final val Format3MaskValue = 0x03
+
+ final val FormatFullMask = 0x0f
+ final val FormatFullMaskValue = 0x7
+
+ final val FormatNoPositionValue = -1
+ }
+
+ private final class Serializer {
+ private[this] val bufferUnderlying = new JumpBackByteArrayOutputStream
+ private[this] val buffer = new DataOutputStream(bufferUnderlying)
+
+ private[this] val files = mutable.ListBuffer.empty[URI]
+ private[this] val fileIndexMap = mutable.Map.empty[URI, Int]
+ private def fileToIndex(file: URI): Int =
+ fileIndexMap.getOrElseUpdate(file, (files += file).size - 1)
+
+ private[this] val strings = mutable.ListBuffer.empty[String]
+ private[this] val stringIndexMap = mutable.Map.empty[String, Int]
+ private def stringToIndex(str: String): Int =
+ stringIndexMap.getOrElseUpdate(str, (strings += str).size - 1)
+
+ private[this] var lastPosition: Position = Position.NoPosition
+
+ def serialize(stream: OutputStream, tree: Tree): Unit = {
+ // Write tree to buffer and record files and strings
+ writeTree(tree)
+
+ val s = new DataOutputStream(stream)
+
+ // Emit the files
+ s.writeInt(files.size)
+ files.foreach(f => s.writeUTF(f.toString))
+
+ // Emit the strings
+ s.writeInt(strings.size)
+ strings.foreach(s.writeUTF)
+
+ // Paste the buffer
+ bufferUnderlying.writeTo(s)
+
+ s.flush()
+ }
+
+ def writeTree(tree: Tree): Unit = {
+ import buffer._
+ writePosition(tree.pos)
+ tree match {
+ case EmptyTree =>
+ writeByte(TagEmptyTree)
+
+ case VarDef(ident, vtpe, mutable, rhs) =>
+ writeByte(TagVarDef)
+ writeIdent(ident); writeType(vtpe); writeBoolean(mutable); writeTree(rhs)
+
+ case ParamDef(ident, ptpe, mutable) =>
+ writeByte(TagParamDef)
+ writeIdent(ident); writeType(ptpe); writeBoolean(mutable)
+
+ case Skip() =>
+ writeByte(TagSkip)
+
+ case Block(stats) =>
+ writeByte(TagBlock)
+ writeTrees(stats)
+
+ case Labeled(label, tpe, body) =>
+ writeByte(TagLabeled)
+ writeIdent(label); writeType(tpe); writeTree(body)
+
+ case Assign(lhs, rhs) =>
+ writeByte(TagAssign)
+ writeTree(lhs); writeTree(rhs)
+
+ case Return(expr, label) =>
+ writeByte(TagReturn)
+ writeTree(expr); writeOptIdent(label)
+
+ case If(cond, thenp, elsep) =>
+ writeByte(TagIf)
+ writeTree(cond); writeTree(thenp); writeTree(elsep)
+ writeType(tree.tpe)
+
+ case While(cond, body, label) =>
+ writeByte(TagWhile)
+ writeTree(cond); writeTree(body); writeOptIdent(label)
+
+ case DoWhile(body, cond, label) =>
+ writeByte(TagDoWhile)
+ writeTree(body); writeTree(cond); writeOptIdent(label)
+
+ case Try(block, errVar, handler, finalizer) =>
+ writeByte(TagTry)
+ writeTree(block); writeIdent(errVar); writeTree(handler); writeTree(finalizer)
+ writeType(tree.tpe)
+
+ case Throw(expr) =>
+ writeByte(TagThrow)
+ writeTree(expr)
+
+ case Continue(label) =>
+ writeByte(TagContinue)
+ writeOptIdent(label)
+
+ case Match(selector, cases, default) =>
+ writeByte(TagMatch)
+ writeTree(selector)
+ writeInt(cases.size)
+ cases foreach { caze =>
+ writeTrees(caze._1); writeTree(caze._2)
+ }
+ writeTree(default)
+ writeType(tree.tpe)
+
+ case Debugger() =>
+ writeByte(TagDebugger)
+
+ case New(cls, ctor, args) =>
+ writeByte(TagNew)
+ writeClassType(cls); writeIdent(ctor); writeTrees(args)
+
+ case LoadModule(cls) =>
+ writeByte(TagLoadModule)
+ writeClassType(cls)
+
+ case StoreModule(cls, value) =>
+ writeByte(TagStoreModule)
+ writeClassType(cls); writeTree(value)
+
+ case Select(qualifier, item, mutable) =>
+ writeByte(TagSelect)
+ writeTree(qualifier); writeIdent(item); writeBoolean(mutable)
+ writeType(tree.tpe)
+
+ case Apply(receiver, method, args) =>
+ writeByte(TagApply)
+ writeTree(receiver); writeIdent(method); writeTrees(args)
+ writeType(tree.tpe)
+
+ case StaticApply(receiver, cls, method, args) =>
+ writeByte(TagStaticApply)
+ writeTree(receiver); writeClassType(cls); writeIdent(method); writeTrees(args)
+ writeType(tree.tpe)
+
+ case TraitImplApply(impl, method, args) =>
+ writeByte(TagTraitImplApply)
+ writeClassType(impl); writeIdent(method); writeTrees(args)
+ writeType(tree.tpe)
+
+ case UnaryOp(op, lhs) =>
+ writeByte(TagUnaryOp)
+ writeByte(op); writeTree(lhs)
+
+ case BinaryOp(op, lhs, rhs) =>
+ writeByte(TagBinaryOp)
+ writeByte(op); writeTree(lhs); writeTree(rhs)
+
+ case NewArray(tpe, lengths) =>
+ writeByte(TagNewArray)
+ writeArrayType(tpe); writeTrees(lengths)
+
+ case ArrayValue(tpe, elems) =>
+ writeByte(TagArrayValue)
+ writeArrayType(tpe); writeTrees(elems)
+
+ case ArrayLength(array) =>
+ writeByte(TagArrayLength)
+ writeTree(array)
+
+ case ArraySelect(array, index) =>
+ writeByte(TagArraySelect)
+ writeTree(array); writeTree(index)
+ writeType(tree.tpe)
+
+ case RecordValue(tpe, elems) =>
+ writeByte(TagRecordValue)
+ writeType(tpe); writeTrees(elems)
+
+ case IsInstanceOf(expr, cls) =>
+ writeByte(TagIsInstanceOf)
+ writeTree(expr); writeReferenceType(cls)
+
+ case AsInstanceOf(expr, cls) =>
+ writeByte(TagAsInstanceOf)
+ writeTree(expr); writeReferenceType(cls)
+
+ case Unbox(expr, charCode) =>
+ writeByte(TagUnbox)
+ writeTree(expr); writeByte(charCode.toByte)
+
+ case GetClass(expr) =>
+ writeByte(TagGetClass)
+ writeTree(expr)
+
+ case CallHelper(helper, args) =>
+ writeByte(TagCallHelper)
+ writeString(helper); writeTrees(args)
+ writeType(tree.tpe)
+
+ case JSNew(ctor, args) =>
+ writeByte(TagJSNew)
+ writeTree(ctor); writeTrees(args)
+
+ case JSDotSelect(qualifier, item) =>
+ writeByte(TagJSDotSelect)
+ writeTree(qualifier); writeIdent(item)
+
+ case JSBracketSelect(qualifier, item) =>
+ writeByte(TagJSBracketSelect)
+ writeTree(qualifier); writeTree(item)
+
+ case JSFunctionApply(fun, args) =>
+ writeByte(TagJSFunctionApply)
+ writeTree(fun); writeTrees(args)
+
+ case JSDotMethodApply(receiver, method, args) =>
+ writeByte(TagJSDotMethodApply)
+ writeTree(receiver); writeIdent(method); writeTrees(args)
+
+ case JSBracketMethodApply(receiver, method, args) =>
+ writeByte(TagJSBracketMethodApply)
+ writeTree(receiver); writeTree(method); writeTrees(args)
+
+ case JSDelete(prop) =>
+ writeByte(TagJSDelete)
+ writeTree(prop)
+
+ case JSUnaryOp(op, lhs) =>
+ writeByte(TagJSUnaryOp)
+ writeString(op); writeTree(lhs)
+
+ case JSBinaryOp(op, lhs, rhs) =>
+ writeByte(TagJSBinaryOp)
+ writeString(op); writeTree(lhs); writeTree(rhs)
+
+ case JSArrayConstr(items) =>
+ writeByte(TagJSArrayConstr)
+ writeTrees(items)
+
+ case JSObjectConstr(fields) =>
+ writeByte(TagJSObjectConstr)
+ writeInt(fields.size)
+ fields foreach { field =>
+ writePropertyName(field._1); writeTree(field._2)
+ }
+
+ case JSEnvInfo() =>
+ writeByte(TagJSEnvInfo)
+
+ // Literals
+
+ case Undefined() =>
+ writeByte(TagUndefined)
+
+ case UndefinedParam() =>
+ writeByte(TagUndefinedParam)
+ writeType(tree.tpe)
+
+ case Null() =>
+ writeByte(TagNull)
+
+ case BooleanLiteral(value) =>
+ writeByte(TagBooleanLiteral)
+ writeBoolean(value)
+
+ case IntLiteral(value) =>
+ writeByte(TagIntLiteral)
+ writeInt(value)
+
+ case LongLiteral(value) =>
+ writeByte(TagLongLiteral)
+ writeLong(value)
+
+ case FloatLiteral(value) =>
+ writeByte(TagFloatLiteral)
+ writeFloat(value)
+
+ case DoubleLiteral(value) =>
+ writeByte(TagDoubleLiteral)
+ writeDouble(value)
+
+ case StringLiteral(value) =>
+ writeByte(TagStringLiteral)
+ writeString(value)
+
+ case ClassOf(cls) =>
+ writeByte(TagClassOf)
+ writeReferenceType(cls)
+
+ case VarRef(ident, mutable) =>
+ writeByte(TagVarRef)
+ writeIdent(ident); writeBoolean(mutable)
+ writeType(tree.tpe)
+
+ case This() =>
+ writeByte(TagThis)
+ writeType(tree.tpe)
+
+ case Closure(captureParams, params, body, captureValues) =>
+ writeByte(TagClosure)
+ writeTrees(captureParams)
+ writeTrees(params)
+ writeTree(body)
+ writeTrees(captureValues)
+
+ case ClassDef(name, kind, parent, ancestors, defs) =>
+ writeByte(TagClassDef)
+ writeIdent(name)
+ writeByte(ClassKind.toByte(kind))
+ writeOptIdent(parent)
+ writeIdents(ancestors)
+ writeTrees(defs)
+
+ case methodDef: MethodDef =>
+ val MethodDef(name, args, resultType, body) = methodDef
+
+ writeByte(TagMethodDef)
+ writeOptHash(methodDef.hash)
+
+ // Prepare for back-jump and write dummy length
+ bufferUnderlying.markJump()
+ writeInt(-1)
+
+ // Write out method def
+ writePropertyName(name); writeTrees(args); writeType(resultType); writeTree(body)
+
+ // Jump back and write true length
+ val length = bufferUnderlying.jumpBack()
+ writeInt(length)
+ bufferUnderlying.continue()
+
+ case PropertyDef(name, getter, arg, setter) =>
+ writeByte(TagPropertyDef)
+ writePropertyName(name); writeTree(getter); writeTree(arg); writeTree(setter)
+
+ case ConstructorExportDef(fullName, args, body) =>
+ writeByte(TagConstructorExportDef)
+ writeString(fullName); writeTrees(args); writeTree(body)
+
+ case ModuleExportDef(fullName) =>
+ writeByte(TagModuleExportDef)
+ writeString(fullName)
+ }
+ if (UseDebugMagic)
+ writeInt(DebugMagic)
+ }
+
+ def writeTrees(trees: List[Tree]): Unit = {
+ buffer.writeInt(trees.size)
+ trees.foreach(writeTree)
+ }
+
+ def writeIdent(ident: Ident): Unit = {
+ writePosition(ident.pos)
+ writeString(ident.name); writeString(ident.originalName.getOrElse(""))
+ }
+
+ def writeIdents(idents: List[Ident]): Unit = {
+ buffer.writeInt(idents.size)
+ idents.foreach(writeIdent)
+ }
+
+ def writeOptIdent(optIdent: Option[Ident]): Unit = {
+ buffer.writeBoolean(optIdent.isDefined)
+ optIdent.foreach(writeIdent)
+ }
+
+ def writeType(tpe: Type): Unit = {
+ tpe match {
+ case AnyType => buffer.write(TagAnyType)
+ case NothingType => buffer.write(TagNothingType)
+ case UndefType => buffer.write(TagUndefType)
+ case BooleanType => buffer.write(TagBooleanType)
+ case IntType => buffer.write(TagIntType)
+ case LongType => buffer.write(TagLongType)
+ case FloatType => buffer.write(TagFloatType)
+ case DoubleType => buffer.write(TagDoubleType)
+ case StringType => buffer.write(TagStringType)
+ case NullType => buffer.write(TagNullType)
+ case NoType => buffer.write(TagNoType)
+
+ case tpe: ClassType =>
+ buffer.write(TagClassType)
+ writeClassType(tpe)
+
+ case tpe: ArrayType =>
+ buffer.write(TagArrayType)
+ writeArrayType(tpe)
+
+ case RecordType(fields) =>
+ buffer.write(TagRecordType)
+ buffer.writeInt(fields.size)
+ for (RecordType.Field(name, originalName, tpe, mutable) <- fields) {
+ writeString(name)
+ writeString(originalName.getOrElse(""))
+ writeType(tpe)
+ buffer.writeBoolean(mutable)
+ }
+ }
+ }
+
+ def writeClassType(tpe: ClassType): Unit =
+ writeString(tpe.className)
+
+ def writeArrayType(tpe: ArrayType): Unit = {
+ writeString(tpe.baseClassName)
+ buffer.writeInt(tpe.dimensions)
+ }
+
+ def writeReferenceType(tpe: ReferenceType): Unit =
+ writeType(tpe)
+
+ def writePropertyName(name: PropertyName): Unit = {
+ name match {
+ case name: Ident => buffer.writeBoolean(true); writeIdent(name)
+ case name: StringLiteral => buffer.writeBoolean(false); writeTree(name)
+ }
+ }
+
+ def writePosition(pos: Position): Unit = {
+ import buffer._
+ import PositionFormat._
+
+ def writeFull(): Unit = {
+ writeByte(FormatFullMaskValue)
+ writeInt(fileToIndex(pos.source))
+ writeInt(pos.line)
+ writeInt(pos.column)
+ }
+
+ if (pos == Position.NoPosition) {
+ writeByte(FormatNoPositionValue)
+ } else if (lastPosition == Position.NoPosition ||
+ pos.source != lastPosition.source) {
+ writeFull()
+ lastPosition = pos
+ } else {
+ val line = pos.line
+ val column = pos.column
+ val lineDiff = line - lastPosition.line
+ val columnDiff = column - lastPosition.column
+ val columnIsByte = column >= 0 && column < 256
+
+ if (lineDiff == 0 && columnDiff >= -64 && columnDiff < 64) {
+ writeByte((columnDiff << Format1Shift) | Format1MaskValue)
+ } else if (lineDiff >= -32 && lineDiff < 32 && columnIsByte) {
+ writeByte((lineDiff << Format2Shift) | Format2MaskValue)
+ writeByte(column)
+ } else if (lineDiff >= Short.MinValue && lineDiff <= Short.MaxValue && columnIsByte) {
+ writeByte(Format3MaskValue)
+ writeShort(lineDiff)
+ writeByte(column)
+ } else {
+ writeFull()
+ }
+
+ lastPosition = pos
+ }
+
+ if (UseDebugMagic)
+ writeInt(PosDebugMagic)
+ }
+
+ def writeOptHash(optHash: Option[TreeHash]): Unit = {
+ buffer.writeBoolean(optHash.isDefined)
+ for (hash <- optHash) {
+ buffer.write(hash.treeHash)
+ buffer.write(hash.posHash)
+ }
+ }
+
+ def writeString(s: String): Unit =
+ buffer.writeInt(stringToIndex(s))
+ }
+
+ private final class Deserializer(stream: InputStream, sourceVersion: String) {
+ private[this] val input = new DataInputStream(stream)
+
+ private[this] val files =
+ Array.fill(input.readInt())(new URI(input.readUTF()))
+
+ private[this] val strings =
+ Array.fill(input.readInt())(input.readUTF())
+
+ private[this] var lastPosition: Position = Position.NoPosition
+
+ def deserialize(): Tree = {
+ readTree()
+ }
+
+ def readTree(): Tree = {
+ import input._
+ implicit val pos = readPosition()
+ val tag = readByte()
+ val result = (tag: @switch) match {
+ case TagEmptyTree => EmptyTree
+
+ case TagVarDef => VarDef(readIdent(), readType(), readBoolean(), readTree())
+ case TagParamDef => ParamDef(readIdent(), readType(), readBoolean())
+
+ case TagSkip => Skip()
+ case TagBlock => Block(readTrees())
+ case TagLabeled => Labeled(readIdent(), readType(), readTree())
+ case TagAssign => Assign(readTree(), readTree())
+ case TagReturn => Return(readTree(), readOptIdent())
+ case TagIf => If(readTree(), readTree(), readTree())(readType())
+ case TagWhile => While(readTree(), readTree(), readOptIdent())
+ case TagDoWhile => DoWhile(readTree(), readTree(), readOptIdent())
+ case TagTry => Try(readTree(), readIdent(), readTree(), readTree())(readType())
+ case TagThrow => Throw(readTree())
+ case TagContinue => Continue(readOptIdent())
+ case TagMatch =>
+ Match(readTree(), List.fill(readInt()) {
+ (readTrees().map(_.asInstanceOf[Literal]), readTree())
+ }, readTree())(readType())
+ case TagDebugger => Debugger()
+
+ case TagNew => New(readClassType(), readIdent(), readTrees())
+ case TagLoadModule => LoadModule(readClassType())
+ case TagStoreModule => StoreModule(readClassType(), readTree())
+ case TagSelect => Select(readTree(), readIdent(), readBoolean())(readType())
+ case TagApply => Apply(readTree(), readIdent(), readTrees())(readType())
+ case TagStaticApply => StaticApply(readTree(), readClassType(), readIdent(), readTrees())(readType())
+ case TagTraitImplApply => TraitImplApply(readClassType(), readIdent(), readTrees())(readType())
+ case TagUnaryOp => UnaryOp(readByte(), readTree())
+ case TagBinaryOp => BinaryOp(readByte(), readTree(), readTree())
+ case TagNewArray => NewArray(readArrayType(), readTrees())
+ case TagArrayValue => ArrayValue(readArrayType(), readTrees())
+ case TagArrayLength => ArrayLength(readTree())
+ case TagArraySelect => ArraySelect(readTree(), readTree())(readType())
+ case TagRecordValue => RecordValue(readType().asInstanceOf[RecordType], readTrees())
+ case TagIsInstanceOf => IsInstanceOf(readTree(), readReferenceType())
+ case TagAsInstanceOf => AsInstanceOf(readTree(), readReferenceType())
+ case TagUnbox => Unbox(readTree(), readByte().toChar)
+ case TagGetClass => GetClass(readTree())
+ case TagCallHelper => CallHelper(readString(), readTrees())(readType())
+
+ case TagJSNew => JSNew(readTree(), readTrees())
+ case TagJSDotSelect => JSDotSelect(readTree(), readIdent())
+ case TagJSBracketSelect => JSBracketSelect(readTree(), readTree())
+ case TagJSFunctionApply => JSFunctionApply(readTree(), readTrees())
+ case TagJSDotMethodApply => JSDotMethodApply(readTree(), readIdent(), readTrees())
+ case TagJSBracketMethodApply => JSBracketMethodApply(readTree(), readTree(), readTrees())
+ case TagJSDelete => JSDelete(readTree())
+ case TagJSUnaryOp => JSUnaryOp(readString(), readTree())
+ case TagJSBinaryOp => JSBinaryOp(readString(), readTree(), readTree())
+ case TagJSArrayConstr => JSArrayConstr(readTrees())
+ case TagJSObjectConstr =>
+ JSObjectConstr(List.fill(readInt())((readPropertyName(), readTree())))
+ case TagJSEnvInfo => JSEnvInfo()
+
+ case TagUndefined => Undefined()
+ case TagUndefinedParam => UndefinedParam()(readType())
+ case TagNull => Null()
+ case TagBooleanLiteral => BooleanLiteral(readBoolean())
+ case TagIntLiteral => IntLiteral(readInt())
+ case TagLongLiteral => LongLiteral(readLong())
+ case TagFloatLiteral => FloatLiteral(readFloat())
+ case TagDoubleLiteral => DoubleLiteral(readDouble())
+ case TagStringLiteral => StringLiteral(readString())
+ case TagClassOf => ClassOf(readReferenceType())
+
+ case TagVarRef => VarRef(readIdent(), readBoolean())(readType())
+ case TagThis => This()(readType())
+ case TagClosure =>
+ Closure(readParamDefs(), readParamDefs(), readTree(), readTrees())
+
+ case TagClassDef =>
+ val name = readIdent()
+ val kind = ClassKind.fromByte(readByte())
+ val parent = readOptIdent()
+ val ancestors = readIdents()
+ val defs = readTrees()
+ ClassDef(name, kind, parent, ancestors, defs)
+
+ case TagMethodDef =>
+ val optHash = readOptHash()
+ // read and discard the length
+ val len = readInt()
+ assert(len >= 0)
+ MethodDef(readPropertyName(), readParamDefs(), readType(), readTree())(optHash)
+ case TagPropertyDef =>
+ PropertyDef(readPropertyName(), readTree(),
+ readTree().asInstanceOf[ParamDef], readTree())
+ case TagConstructorExportDef =>
+ ConstructorExportDef(readString(), readParamDefs(), readTree())
+ case TagModuleExportDef =>
+ ModuleExportDef(readString())
+ }
+ if (UseDebugMagic) {
+ val magic = readInt()
+ assert(magic == DebugMagic,
+ s"Bad magic after reading a ${result.getClass}!")
+ }
+ result
+ }
+
+ def readTrees(): List[Tree] =
+ List.fill(input.readInt())(readTree())
+
+ def readParamDefs(): List[ParamDef] =
+ readTrees().map(_.asInstanceOf[ParamDef])
+
+ def readIdent(): Ident = {
+ implicit val pos = readPosition()
+ val name = readString()
+ val originalName = readString()
+ Ident(name, if (originalName.isEmpty) None else Some(originalName))
+ }
+
+ def readIdents(): List[Ident] =
+ List.fill(input.readInt())(readIdent())
+
+ def readOptIdent(): Option[Ident] = {
+ if (input.readBoolean()) Some(readIdent())
+ else None
+ }
+
+ def readType(): Type = {
+ val tag = input.readByte()
+ (tag: @switch) match {
+ case TagAnyType => AnyType
+ case TagNothingType => NothingType
+ case TagUndefType => UndefType
+ case TagBooleanType => BooleanType
+ case TagIntType => IntType
+ case TagLongType => LongType
+ case TagFloatType => FloatType
+ case TagDoubleType => DoubleType
+ case TagStringType => StringType
+ case TagNullType => NullType
+ case TagNoType => NoType
+
+ case TagClassType => readClassType()
+ case TagArrayType => readArrayType()
+
+ case TagRecordType =>
+ RecordType(List.fill(input.readInt()) {
+ val name = readString()
+ val originalName = readString()
+ val tpe = readType()
+ val mutable = input.readBoolean()
+ RecordType.Field(name,
+ if (originalName.isEmpty) None else Some(originalName),
+ tpe, mutable)
+ })
+ }
+ }
+
+ def readClassType(): ClassType =
+ ClassType(readString())
+
+ def readArrayType(): ArrayType =
+ ArrayType(readString(), input.readInt())
+
+ def readReferenceType(): ReferenceType =
+ readType().asInstanceOf[ReferenceType]
+
+ def readPropertyName(): PropertyName = {
+ if (input.readBoolean()) readIdent()
+ else readTree().asInstanceOf[StringLiteral]
+ }
+
+ def readPosition(): Position = {
+ import input._
+ import PositionFormat._
+
+ val first = readByte()
+
+ val result = if (first == FormatNoPositionValue) {
+ Position.NoPosition
+ } else {
+ val result = if ((first & FormatFullMask) == FormatFullMaskValue) {
+ val file = files(readInt())
+ val line = readInt()
+ val column = readInt()
+ Position(file, line, column)
+ } else {
+ assert(lastPosition != NoPosition,
+ "Position format error: first position must be full")
+ if ((first & Format1Mask) == Format1MaskValue) {
+ val columnDiff = first >> Format1Shift
+ Position(lastPosition.source, lastPosition.line,
+ lastPosition.column + columnDiff)
+ } else if ((first & Format2Mask) == Format2MaskValue) {
+ val lineDiff = first >> Format2Shift
+ val column = readByte() & 0xff // unsigned
+ Position(lastPosition.source,
+ lastPosition.line + lineDiff, column)
+ } else {
+ assert((first & Format3Mask) == Format3MaskValue,
+ s"Position format error: first byte $first does not match any format")
+ val lineDiff = readShort()
+ val column = readByte() & 0xff // unsigned
+ Position(lastPosition.source,
+ lastPosition.line + lineDiff, column)
+ }
+ }
+ lastPosition = result
+ result
+ }
+
+ if (UseDebugMagic) {
+ val magic = readInt()
+ assert(magic == PosDebugMagic,
+ s"Bad magic after reading position with first byte $first")
+ }
+
+ result
+ }
+
+ def readOptHash(): Option[TreeHash] = {
+ if (input.readBoolean()) {
+ val treeHash = new Array[Byte](20)
+ val posHash = new Array[Byte](20)
+ input.readFully(treeHash)
+ input.readFully(posHash)
+ Some(new TreeHash(treeHash, posHash))
+ } else None
+ }
+
+ def readString(): String = {
+ strings(input.readInt())
+ }
+ }
+}
diff --git a/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Tags.scala b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Tags.scala
new file mode 100644
index 0000000..a03926c
--- /dev/null
+++ b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Tags.scala
@@ -0,0 +1,107 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js IR **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.ir
+
+/** Serialization and hashing tags for trees and types */
+private[ir] object Tags {
+
+ // Tags for Trees
+
+ final val TagEmptyTree = 1
+
+ final val TagVarDef = TagEmptyTree + 1
+ final val TagParamDef = TagVarDef + 1
+
+ final val TagSkip = TagParamDef + 1
+ final val TagBlock = TagSkip + 1
+ final val TagLabeled = TagBlock + 1
+ final val TagAssign = TagLabeled + 1
+ final val TagReturn = TagAssign + 1
+ final val TagIf = TagReturn + 1
+ final val TagWhile = TagIf + 1
+ final val TagDoWhile = TagWhile + 1
+ final val TagTry = TagDoWhile + 1
+ final val TagThrow = TagTry + 1
+ final val TagContinue = TagThrow + 1
+ final val TagMatch = TagContinue + 1
+ final val TagDebugger = TagMatch + 1
+
+ final val TagNew = TagDebugger + 1
+ final val TagLoadModule = TagNew + 1
+ final val TagStoreModule = TagLoadModule + 1
+ final val TagSelect = TagStoreModule + 1
+ final val TagApply = TagSelect + 1
+ final val TagStaticApply = TagApply + 1
+ final val TagTraitImplApply = TagStaticApply + 1
+ final val TagUnaryOp = TagTraitImplApply + 1
+ final val TagBinaryOp = TagUnaryOp + 1
+ final val TagNewArray = TagBinaryOp + 1
+ final val TagArrayValue = TagNewArray + 1
+ final val TagArrayLength = TagArrayValue + 1
+ final val TagArraySelect = TagArrayLength + 1
+ final val TagRecordValue = TagArraySelect + 1
+ final val TagIsInstanceOf = TagRecordValue + 1
+ final val TagAsInstanceOf = TagIsInstanceOf + 1
+ final val TagUnbox = TagAsInstanceOf + 1
+ final val TagGetClass = TagUnbox + 1
+ final val TagCallHelper = TagGetClass + 1
+
+ final val TagJSNew = TagCallHelper + 1
+ final val TagJSDotSelect = TagJSNew + 1
+ final val TagJSBracketSelect = TagJSDotSelect + 1
+ final val TagJSFunctionApply = TagJSBracketSelect + 1
+ final val TagJSDotMethodApply = TagJSFunctionApply + 1
+ final val TagJSBracketMethodApply = TagJSDotMethodApply + 1
+ final val TagJSDelete = TagJSBracketMethodApply + 1
+ final val TagJSUnaryOp = TagJSDelete + 1
+ final val TagJSBinaryOp = TagJSUnaryOp + 1
+ final val TagJSArrayConstr = TagJSBinaryOp + 1
+ final val TagJSObjectConstr = TagJSArrayConstr + 1
+ final val TagJSEnvInfo = TagJSObjectConstr + 1
+
+ final val TagUndefined = TagJSEnvInfo + 1
+ final val TagUndefinedParam = TagUndefined + 1
+ final val TagNull = TagUndefinedParam + 1
+ final val TagBooleanLiteral = TagNull + 1
+ final val TagIntLiteral = TagBooleanLiteral + 1
+ final val TagLongLiteral = TagIntLiteral + 1
+ final val TagFloatLiteral = TagLongLiteral + 1
+ final val TagDoubleLiteral = TagFloatLiteral + 1
+ final val TagStringLiteral = TagDoubleLiteral + 1
+ final val TagClassOf = TagStringLiteral + 1
+
+ final val TagVarRef = TagClassOf + 1
+ final val TagThis = TagVarRef + 1
+ final val TagClosure = TagThis + 1
+
+ final val TagClassDef = TagClosure + 1
+ final val TagMethodDef = TagClassDef + 1
+ final val TagPropertyDef = TagMethodDef + 1
+ final val TagConstructorExportDef = TagPropertyDef + 1
+ final val TagModuleExportDef = TagConstructorExportDef + 1
+
+ // Tags for Types
+
+ final val TagAnyType = 1
+ final val TagNothingType = TagAnyType + 1
+ final val TagUndefType = TagNothingType + 1
+ final val TagBooleanType = TagUndefType + 1
+ final val TagIntType = TagBooleanType + 1
+ final val TagLongType = TagIntType + 1
+ final val TagFloatType = TagLongType + 1
+ final val TagDoubleType = TagFloatType + 1
+ final val TagStringType = TagDoubleType + 1
+ final val TagNullType = TagStringType + 1
+ final val TagClassType = TagNullType + 1
+ final val TagArrayType = TagClassType + 1
+ final val TagRecordType = TagArrayType + 1
+ final val TagNoType = TagRecordType + 1
+
+}
diff --git a/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Transformers.scala b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Transformers.scala
new file mode 100644
index 0000000..5e4f40c
--- /dev/null
+++ b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Transformers.scala
@@ -0,0 +1,218 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js IR **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.ir
+
+import Trees._
+
+object Transformers {
+
+ abstract class Transformer {
+ final def transformStat(tree: Tree): Tree =
+ transform(tree, isStat = true)
+
+ final def transformExpr(tree: Tree): Tree =
+ transform(tree, isStat = false)
+
+ def transform(tree: Tree, isStat: Boolean): Tree = {
+ implicit val pos = tree.pos
+
+ tree match {
+ // Definitions
+
+ case VarDef(ident, vtpe, mutable, rhs) =>
+ VarDef(ident, vtpe, mutable, transformExpr(rhs))
+
+ // Control flow constructs
+
+ case Block(stats :+ expr) =>
+ Block(stats.map(transformStat) :+ transform(expr, isStat))
+
+ case Labeled(label, tpe, body) =>
+ Labeled(label, tpe, transform(body, isStat))
+
+ case Assign(lhs, rhs) =>
+ Assign(transformExpr(lhs), transformExpr(rhs))
+
+ case Return(expr, label) =>
+ Return(transformExpr(expr), label)
+
+ case If(cond, thenp, elsep) =>
+ If(transformExpr(cond), transform(thenp, isStat),
+ transform(elsep, isStat))(tree.tpe)
+
+ case While(cond, body, label) =>
+ While(transformExpr(cond), transformStat(body), label)
+
+ case DoWhile(body, cond, label) =>
+ DoWhile(transformStat(body), transformExpr(cond), label)
+
+ case Try(block, errVar, handler, finalizer) =>
+ Try(transform(block, isStat), errVar, transform(handler, isStat),
+ transformStat(finalizer))(tree.tpe)
+
+ case Throw(expr) =>
+ Throw(transformExpr(expr))
+
+ case Match(selector, cases, default) =>
+ Match(transformExpr(selector),
+ cases map (c => (c._1, transform(c._2, isStat))),
+ transform(default, isStat))(tree.tpe)
+
+ // Scala expressions
+
+ case New(cls, ctor, args) =>
+ New(cls, ctor, args map transformExpr)
+
+ case StoreModule(cls, value) =>
+ StoreModule(cls, transformExpr(value))
+
+ case Select(qualifier, item, mutable) =>
+ Select(transformExpr(qualifier), item, mutable)(tree.tpe)
+
+ case Apply(receiver, method, args) =>
+ Apply(transformExpr(receiver), method,
+ args map transformExpr)(tree.tpe)
+
+ case StaticApply(receiver, cls, method, args) =>
+ StaticApply(transformExpr(receiver), cls, method,
+ args map transformExpr)(tree.tpe)
+
+ case TraitImplApply(impl, method, args) =>
+ TraitImplApply(impl, method, args map transformExpr)(tree.tpe)
+
+ case UnaryOp(op, lhs) =>
+ UnaryOp(op, transformExpr(lhs))
+
+ case BinaryOp(op, lhs, rhs) =>
+ BinaryOp(op, transformExpr(lhs), transformExpr(rhs))
+
+ case NewArray(tpe, lengths) =>
+ NewArray(tpe, lengths map transformExpr)
+
+ case ArrayValue(tpe, elems) =>
+ ArrayValue(tpe, elems map transformExpr)
+
+ case ArrayLength(array) =>
+ ArrayLength(transformExpr(array))
+
+ case ArraySelect(array, index) =>
+ ArraySelect(transformExpr(array), transformExpr(index))(tree.tpe)
+
+ case RecordValue(tpe, elems) =>
+ RecordValue(tpe, elems map transformExpr)
+
+ case IsInstanceOf(expr, cls) =>
+ IsInstanceOf(transformExpr(expr), cls)
+
+ case AsInstanceOf(expr, cls) =>
+ AsInstanceOf(transformExpr(expr), cls)
+
+ case Unbox(expr, charCode) =>
+ Unbox(transformExpr(expr), charCode)
+
+ case GetClass(expr) =>
+ GetClass(transformExpr(expr))
+
+ case CallHelper(helper, args) =>
+ CallHelper(helper, args map transformExpr)(tree.tpe)
+
+ // JavaScript expressions
+
+ case JSNew(ctor, args) =>
+ JSNew(transformExpr(ctor), args map transformExpr)
+
+ case JSDotSelect(qualifier, item) =>
+ JSDotSelect(transformExpr(qualifier), item)
+
+ case JSBracketSelect(qualifier, item) =>
+ JSBracketSelect(transformExpr(qualifier), transformExpr(item))
+
+ case JSFunctionApply(fun, args) =>
+ JSFunctionApply(transformExpr(fun), args map transformExpr)
+
+ case JSDotMethodApply(receiver, method, args) =>
+ JSDotMethodApply(transformExpr(receiver), method,
+ args map transformExpr)
+
+ case JSBracketMethodApply(receiver, method, args) =>
+ JSBracketMethodApply(transformExpr(receiver), transformExpr(method),
+ args map transformExpr)
+
+ case JSDelete(prop) =>
+ JSDelete(transformExpr(prop))
+
+ case JSUnaryOp(op, lhs) =>
+ JSUnaryOp(op, transformExpr(lhs))
+
+ case JSBinaryOp(op, lhs, rhs) =>
+ JSBinaryOp(op, transformExpr(lhs), transformExpr(rhs))
+
+ case JSArrayConstr(items) =>
+ JSArrayConstr(items map transformExpr)
+
+ case JSObjectConstr(fields) =>
+ JSObjectConstr(fields map {
+ case (name, value) => (name, transformExpr(value))
+ })
+
+ // Atomic expressions
+
+ case Closure(captureParams, params, body, captureValues) =>
+ Closure(captureParams, params, transformExpr(body),
+ captureValues.map(transformExpr))
+
+ // Trees that need not be transformed
+
+ case _:Skip | _:Continue | _:LoadModule | _:JSEnvInfo |
+ _:Literal | _:VarRef | _:This | EmptyTree =>
+ tree
+
+ case _ =>
+ sys.error(s"Invalid tree in transform() of class ${tree.getClass}")
+ }
+ }
+ }
+
+ abstract class ClassTransformer extends Transformer {
+ def transformClassDef(tree: ClassDef): ClassDef = {
+ val ClassDef(name, kind, parent, ancestors, defs) = tree
+ ClassDef(name, kind, parent, ancestors, defs.map(transformDef))(tree.pos)
+ }
+
+ def transformDef(tree: Tree): Tree = {
+ implicit val pos = tree.pos
+
+ tree match {
+ case VarDef(name, vtpe, mutable, rhs) =>
+ VarDef(name, vtpe, mutable, transformExpr(rhs))
+
+ case MethodDef(name, args, resultType, body) =>
+ MethodDef(name, args, resultType, transformStat(body))(None)
+
+ case PropertyDef(name, getterBody, setterArg, setterBody) =>
+ PropertyDef(
+ name,
+ transformStat(getterBody),
+ setterArg,
+ transformStat(setterBody))
+
+ case ConstructorExportDef(fullName, args, body) =>
+ ConstructorExportDef(fullName, args, transformStat(body))
+
+ case ModuleExportDef(_) =>
+ tree
+
+ case _ =>
+ sys.error(s"Invalid tree in transformDef() of class ${tree.getClass}")
+ }
+ }
+ }
+
+}
diff --git a/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Traversers.scala b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Traversers.scala
new file mode 100644
index 0000000..1b77e5e
--- /dev/null
+++ b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Traversers.scala
@@ -0,0 +1,197 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js IR **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.ir
+
+import Trees._
+
+object Traversers {
+
+ class Traverser {
+ def traverse(tree: Tree): Unit = tree match {
+ // Definitions
+
+ case VarDef(ident, vtpe, mutable, rhs) =>
+ traverse(rhs)
+
+ // Control flow constructs
+
+ case Block(stats) =>
+ stats foreach traverse
+
+ case Labeled(label, tpe, body) =>
+ traverse(body)
+
+ case Assign(lhs, rhs) =>
+ traverse(lhs)
+ traverse(rhs)
+
+ case Return(expr, label) =>
+ traverse(expr)
+
+ case If(cond, thenp, elsep) =>
+ traverse(cond)
+ traverse(thenp)
+ traverse(elsep)
+
+ case While(cond, body, label) =>
+ traverse(cond)
+ traverse(body)
+
+ case DoWhile(body, cond, label) =>
+ traverse(body)
+ traverse(cond)
+
+ case Try(block, errVar, handler, finalizer) =>
+ traverse(block)
+ traverse(handler)
+ traverse(finalizer)
+
+ case Throw(expr) =>
+ traverse(expr)
+
+ case Match(selector, cases, default) =>
+ traverse(selector)
+ cases foreach (c => (c._1 map traverse, traverse(c._2)))
+ traverse(default)
+
+ // Scala expressions
+
+ case New(cls, ctor, args) =>
+ args foreach traverse
+
+ case StoreModule(cls, value) =>
+ traverse(value)
+
+ case Select(qualifier, item, mutable) =>
+ traverse(qualifier)
+
+ case Apply(receiver, method, args) =>
+ traverse(receiver)
+ args foreach traverse
+
+ case StaticApply(receiver, cls, method, args) =>
+ traverse(receiver)
+ args foreach traverse
+
+ case TraitImplApply(impl, method, args) =>
+ args foreach traverse
+
+ case UnaryOp(op, lhs) =>
+ traverse(lhs)
+
+ case BinaryOp(op, lhs, rhs) =>
+ traverse(lhs)
+ traverse(rhs)
+
+ case NewArray(tpe, lengths) =>
+ lengths foreach traverse
+
+ case ArrayValue(tpe, elems) =>
+ elems foreach traverse
+
+ case ArrayLength(array) =>
+ traverse(array)
+
+ case ArraySelect(array, index) =>
+ traverse(array)
+ traverse(index)
+
+ case RecordValue(tpe, elems) =>
+ elems foreach traverse
+
+ case IsInstanceOf(expr, cls) =>
+ traverse(expr)
+
+ case AsInstanceOf(expr, cls) =>
+ traverse(expr)
+
+ case Unbox(expr, charCode) =>
+ traverse(expr)
+
+ case GetClass(expr) =>
+ traverse(expr)
+
+ case CallHelper(helper, args) =>
+ args foreach traverse
+
+ // JavaScript expressions
+
+ case JSNew(ctor, args) =>
+ traverse(ctor)
+ args foreach traverse
+
+ case JSDotSelect(qualifier, item) =>
+ traverse(qualifier)
+
+ case JSBracketSelect(qualifier, item) =>
+ traverse(qualifier)
+ traverse(item)
+
+ case JSFunctionApply(fun, args) =>
+ traverse(fun)
+ args foreach traverse
+
+ case JSDotMethodApply(receiver, method, args) =>
+ traverse(receiver)
+ args foreach traverse
+
+ case JSBracketMethodApply(receiver, method, args) =>
+ traverse(receiver)
+ traverse(method)
+ args foreach traverse
+
+ case JSDelete(prop) =>
+ traverse(prop)
+
+ case JSUnaryOp(op, lhs) =>
+ traverse(lhs)
+
+ case JSBinaryOp(op, lhs, rhs) =>
+ traverse(lhs)
+ traverse(rhs)
+
+ case JSArrayConstr(items) =>
+ items foreach traverse
+
+ case JSObjectConstr(fields) =>
+ fields foreach { f => traverse(f._2) }
+
+ // Atomic expressions
+
+ case Closure(captureParams, params, body, captureValues) =>
+ traverse(body)
+ captureValues.foreach(traverse)
+
+ // Classes
+
+ case ClassDef(name, kind, parent, ancestors, defs) =>
+ defs foreach traverse
+
+ case MethodDef(name, args, resultType, body) =>
+ traverse(body)
+
+ case PropertyDef(name, getterBody, setterArg, setterBody) =>
+ traverse(getterBody)
+ traverse(setterBody)
+
+ case ConstructorExportDef(fullName, args, body) =>
+ traverse(body)
+
+ // Trees that need not be traversed
+
+ case _:Skip | _:Continue | _:LoadModule | _:JSEnvInfo |
+ _:Literal | _:VarRef | _:This | _:ModuleExportDef | EmptyTree =>
+
+ case _ =>
+ sys.error(s"Invalid tree in traverse() of class ${tree.getClass}")
+ }
+ }
+
+}
diff --git a/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Trees.scala b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Trees.scala
new file mode 100644
index 0000000..4f58ece
--- /dev/null
+++ b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Trees.scala
@@ -0,0 +1,536 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js IR **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.ir
+
+import scala.annotation.switch
+
+import Position.NoPosition
+import Types._
+
+object Trees {
+ /** AST node of the IR. */
+ abstract sealed class Tree {
+ val pos: Position
+ val tpe: Type
+
+ def show: String = {
+ val writer = new java.io.StringWriter
+ val printer = new Printers.IRTreePrinter(writer)
+ printer.printTree(this)
+ writer.toString()
+ }
+ }
+
+ case object EmptyTree extends Tree {
+ val pos = NoPosition
+ val tpe = NoType
+ }
+
+ // Identifiers and properties
+
+ sealed trait PropertyName {
+ def name: String
+ def pos: Position
+ }
+
+ case class Ident(name: String, originalName: Option[String])(
+ implicit val pos: Position) extends PropertyName {
+ requireValidIdent(name)
+ }
+
+ object Ident {
+ def apply(name: String)(implicit pos: Position): Ident =
+ new Ident(name, Some(name))
+ }
+
+ final def isValidIdentifier(name: String): Boolean = {
+ val c = name.head
+ (c == '$' || c == '_' || c.isUnicodeIdentifierStart) &&
+ name.tail.forall(c => (c == '$') || c.isUnicodeIdentifierPart) &&
+ !isKeyword(name)
+ }
+
+ @inline final def requireValidIdent(name: String) {
+ require(isValidIdentifier(name), s"${name} is not a valid identifier")
+ }
+
+ final val isKeyword: Set[String] = Set(
+ // Value keywords
+ "true", "false", "null", "undefined",
+
+ // Current JavaScript keywords
+ "break", "case", "catch", "continue", "debugger", "default", "delete",
+ "do", "else", "finally", "for", "function", "if", "in", "instanceof",
+ "new", "return", "switch", "this", "throw", "try", "typeof", "var",
+ "void", "while", "with",
+
+ // Future reserved keywords
+ "class", "const", "enum", "export", "extends", "import", "super",
+
+ // Future reserved keywords in Strict mode
+ "implements", "interface", "let", "package", "private", "protected",
+ "public", "static", "yield",
+
+ // Other reserved keywords found on the Web but not in the spec
+ "abstract", "boolean", "byte", "char", "double", "final", "float",
+ "goto", "int", "long", "native", "short", "synchronized", "throws",
+ "transient", "volatile"
+ )
+
+ // Definitions
+
+ case class VarDef(name: Ident, vtpe: Type, mutable: Boolean, rhs: Tree)(implicit val pos: Position) extends Tree {
+ val tpe = NoType // cannot be in expression position
+
+ def ref(implicit pos: Position): VarRef =
+ VarRef(name, mutable = mutable)(vtpe)
+ }
+
+ case class ParamDef(name: Ident, ptpe: Type, mutable: Boolean)(implicit val pos: Position) extends Tree {
+ val tpe = NoType
+
+ def ref(implicit pos: Position): VarRef =
+ VarRef(name, mutable = mutable)(ptpe)
+ }
+
+ // Control flow constructs
+
+ case class Skip()(implicit val pos: Position) extends Tree {
+ val tpe = NoType // cannot be in expression position
+ }
+
+ class Block private (val stats: List[Tree])(implicit val pos: Position) extends Tree {
+ val tpe = stats.last.tpe
+
+ override def toString(): String =
+ stats.mkString("Block(", ",", ")")
+ }
+
+ object Block {
+ def apply(stats: List[Tree])(implicit pos: Position): Tree = {
+ val flattenedStats = stats flatMap {
+ case Skip() => Nil
+ case Block(subStats) => subStats
+ case other => other :: Nil
+ }
+ flattenedStats match {
+ case Nil => Skip()
+ case only :: Nil => only
+ case _ => new Block(flattenedStats)
+ }
+ }
+
+ def apply(stats: Tree*)(implicit pos: Position): Tree =
+ apply(stats.toList)
+
+ def unapply(block: Block): Some[List[Tree]] = Some(block.stats)
+ }
+
+ case class Labeled(label: Ident, tpe: Type, body: Tree)(implicit val pos: Position) extends Tree
+
+ case class Assign(lhs: Tree, rhs: Tree)(implicit val pos: Position) extends Tree {
+ require(lhs match {
+ case _:VarRef | _:Select | _:ArraySelect |
+ _:JSDotSelect | _:JSBracketSelect => true
+ case _ => false
+ }, s"Invalid lhs for Assign: $lhs")
+
+ val tpe = NoType // cannot be in expression position
+ }
+
+ case class Return(expr: Tree, label: Option[Ident] = None)(implicit val pos: Position) extends Tree {
+ val tpe = NothingType
+ }
+
+ case class If(cond: Tree, thenp: Tree, elsep: Tree)(val tpe: Type)(implicit val pos: Position) extends Tree
+
+ case class While(cond: Tree, body: Tree, label: Option[Ident] = None)(implicit val pos: Position) extends Tree {
+ // cannot be in expression position, unless it is infinite
+ val tpe = cond match {
+ case BooleanLiteral(true) => NothingType
+ case _ => NoType
+ }
+ }
+
+ case class DoWhile(body: Tree, cond: Tree, label: Option[Ident] = None)(implicit val pos: Position) extends Tree {
+ val tpe = NoType // cannot be in expression position
+ }
+
+ case class Try(block: Tree, errVar: Ident, handler: Tree, finalizer: Tree)(val tpe: Type)(implicit val pos: Position) extends Tree
+
+ case class Throw(expr: Tree)(implicit val pos: Position) extends Tree {
+ val tpe = NothingType
+ }
+
+ case class Continue(label: Option[Ident] = None)(implicit val pos: Position) extends Tree {
+ val tpe = NothingType
+ }
+
+ /** A break-free switch (without fallthrough behavior).
+ * Unlike a JavaScript switch, it can be used in expression position.
+ * It supports alternatives explicitly (hence the List[Tree] in cases),
+ * whereas in a switch one would use the fallthrough behavior to
+ * implement alternatives.
+ * (This is not a pattern matching construct like in Scala.)
+ */
+ case class Match(selector: Tree, cases: List[(List[Literal], Tree)], default: Tree)(val tpe: Type)(implicit val pos: Position) extends Tree
+
+ case class Debugger()(implicit val pos: Position) extends Tree {
+ val tpe = NoType // cannot be in expression position
+ }
+
+ // Scala expressions
+
+ case class New(cls: ClassType, ctor: Ident, args: List[Tree])(implicit val pos: Position) extends Tree {
+ val tpe = cls
+ }
+
+ case class LoadModule(cls: ClassType)(implicit val pos: Position) extends Tree {
+ val tpe = cls
+ }
+
+ case class StoreModule(cls: ClassType, value: Tree)(implicit val pos: Position) extends Tree {
+ val tpe = NoType // cannot be in expression position
+ }
+
+ case class Select(qualifier: Tree, item: Ident, mutable: Boolean)(val tpe: Type)(implicit val pos: Position) extends Tree
+
+ case class Apply(receiver: Tree, method: Ident, args: List[Tree])(val tpe: Type)(implicit val pos: Position) extends Tree
+
+ case class StaticApply(receiver: Tree, cls: ClassType, method: Ident, args: List[Tree])(val tpe: Type)(implicit val pos: Position) extends Tree
+
+ case class TraitImplApply(impl: ClassType, method: Ident, args: List[Tree])(val tpe: Type)(implicit val pos: Position) extends Tree
+
+ /** Unary operation (always preserves pureness). */
+ case class UnaryOp(op: UnaryOp.Code, lhs: Tree)(implicit val pos: Position) extends Tree {
+ import UnaryOp._
+ val tpe = (op: @switch) match {
+ case `typeof` => StringType
+ case LongToInt | DoubleToInt => IntType
+ case IntToLong | DoubleToLong => LongType
+ case DoubleToFloat => FloatType
+ case LongToDouble => DoubleType
+ case Boolean_! => BooleanType
+ }
+ }
+
+ object UnaryOp {
+ /** Codes are raw Ints to be able to write switch matches on them. */
+ type Code = Int
+
+ final val typeof = 1
+
+ final val Boolean_! = 2
+
+ final val IntToLong = 3
+ final val LongToInt = 4
+ final val LongToDouble = 5
+ final val DoubleToInt = 6
+ final val DoubleToFloat = 7
+ final val DoubleToLong = 8
+ }
+
+ /** Binary operation (always preserves pureness). */
+ case class BinaryOp(op: BinaryOp.Code, lhs: Tree, rhs: Tree)(implicit val pos: Position) extends Tree {
+ import BinaryOp._
+ val tpe = (op: @switch) match {
+ case === | !== |
+ `in` | `instanceof` |
+ Num_== | Num_!= | Num_< | Num_<= | Num_> | Num_>= |
+ Long_== | Long_!= | Long_< | Long_<= | Long_> | Long_>= |
+ Boolean_== | Boolean_!= | Boolean_| | Boolean_& =>
+ BooleanType
+ case String_+ =>
+ StringType
+ case Int_+ | Int_- | Int_* | Int_/ | Int_% |
+ Int_| | Int_& | Int_^ | Int_<< | Int_>>> | Int_>> =>
+ IntType
+ case Float_+ | Float_- | Float_* | Float_/ | Float_% =>
+ FloatType
+ case Double_+ | Double_- | Double_* | Double_/ | Double_% =>
+ DoubleType
+ case Long_+ | Long_- | Long_* | Long_/ | Long_% |
+ Long_| | Long_& | Long_^ | Long_<< | Long_>>> | Long_>> =>
+ LongType
+ }
+ }
+
+ object BinaryOp {
+ /** Codes are raw Ints to be able to write switch matches on them. */
+ type Code = Int
+
+ final val === = 1
+ final val !== = 2
+
+ final val String_+ = 3
+
+ final val in = 4
+ final val instanceof = 5
+
+ final val Int_+ = 6
+ final val Int_- = 7
+ final val Int_* = 8
+ final val Int_/ = 9
+ final val Int_% = 10
+
+ final val Int_| = 11
+ final val Int_& = 12
+ final val Int_^ = 13
+ final val Int_<< = 14
+ final val Int_>>> = 15
+ final val Int_>> = 16
+
+ final val Float_+ = 17
+ final val Float_- = 18
+ final val Float_* = 19
+ final val Float_/ = 20
+ final val Float_% = 21
+
+ final val Double_+ = 22
+ final val Double_- = 23
+ final val Double_* = 24
+ final val Double_/ = 25
+ final val Double_% = 26
+
+ final val Num_== = 27
+ final val Num_!= = 28
+ final val Num_< = 29
+ final val Num_<= = 30
+ final val Num_> = 31
+ final val Num_>= = 32
+
+ final val Long_+ = 33
+ final val Long_- = 34
+ final val Long_* = 35
+ final val Long_/ = 36
+ final val Long_% = 37
+
+ final val Long_| = 38
+ final val Long_& = 39
+ final val Long_^ = 40
+ final val Long_<< = 41
+ final val Long_>>> = 42
+ final val Long_>> = 43
+
+ final val Long_== = 44
+ final val Long_!= = 45
+ final val Long_< = 46
+ final val Long_<= = 47
+ final val Long_> = 48
+ final val Long_>= = 49
+
+ final val Boolean_== = 50
+ final val Boolean_!= = 51
+ final val Boolean_| = 52
+ final val Boolean_& = 53
+ }
+
+ case class NewArray(tpe: ArrayType, lengths: List[Tree])(implicit val pos: Position) extends Tree {
+ require(lengths.nonEmpty && lengths.size <= tpe.dimensions)
+ }
+
+ case class ArrayValue(tpe: ArrayType, elems: List[Tree])(implicit val pos: Position) extends Tree
+
+ case class ArrayLength(array: Tree)(implicit val pos: Position) extends Tree {
+ val tpe = IntType
+ }
+
+ case class ArraySelect(array: Tree, index: Tree)(val tpe: Type)(implicit val pos: Position) extends Tree
+
+ case class RecordValue(tpe: RecordType, elems: List[Tree])(implicit val pos: Position) extends Tree
+
+ case class IsInstanceOf(expr: Tree, cls: ReferenceType)(implicit val pos: Position) extends Tree {
+ val tpe = BooleanType
+ }
+
+ case class AsInstanceOf(expr: Tree, cls: ReferenceType)(implicit val pos: Position) extends Tree {
+ val tpe = cls match {
+ case ClassType(Definitions.RuntimeNullClass) => NullType
+ case ClassType(Definitions.RuntimeNothingClass) => NothingType
+ case _ => cls
+ }
+ }
+
+ case class Unbox(expr: Tree, charCode: Char)(implicit val pos: Position) extends Tree {
+ val tpe = (charCode: @switch) match {
+ case 'Z' => BooleanType
+ case 'B' | 'S' | 'I' => IntType
+ case 'J' => LongType
+ case 'F' => FloatType
+ case 'D' => DoubleType
+ }
+ }
+
+ case class GetClass(expr: Tree)(implicit val pos: Position) extends Tree {
+ val tpe = ClassType(Definitions.ClassClass)
+ }
+
+ case class CallHelper(helper: String, args: List[Tree])(val tpe: Type)(implicit val pos: Position) extends Tree
+
+ object CallHelper {
+ def apply(helper: String, args: Tree*)(tpe: Type)(
+ implicit pos: Position): CallHelper = {
+ CallHelper(helper, args.toList)(tpe)
+ }
+ }
+
+ // JavaScript expressions
+
+ case class JSNew(ctor: Tree, args: List[Tree])(implicit val pos: Position) extends Tree {
+ val tpe = AnyType
+ }
+
+ case class JSDotSelect(qualifier: Tree, item: Ident)(implicit val pos: Position) extends Tree {
+ val tpe = AnyType
+ }
+
+ case class JSBracketSelect(qualifier: Tree, item: Tree)(implicit val pos: Position) extends Tree {
+ val tpe = AnyType
+ }
+
+ case class JSFunctionApply(fun: Tree, args: List[Tree])(implicit val pos: Position) extends Tree {
+ val tpe = AnyType
+ }
+
+ case class JSDotMethodApply(receiver: Tree, method: Ident, args: List[Tree])(implicit val pos: Position) extends Tree {
+ val tpe = AnyType
+ }
+
+ case class JSBracketMethodApply(receiver: Tree, method: Tree, args: List[Tree])(implicit val pos: Position) extends Tree {
+ val tpe = AnyType
+ }
+
+ case class JSDelete(prop: Tree)(implicit val pos: Position) extends Tree {
+ require(prop match {
+ case _:JSDotSelect | _:JSBracketSelect => true
+ case _ => false
+ }, s"Invalid prop for JSDelete: $prop")
+
+ val tpe = NoType // cannot be in expression position
+ }
+
+ /** Unary operation (always preserves pureness).
+ *
+ * Operations which do not preserve pureness are not allowed in this tree.
+ * These are notably ++ and --
+ */
+ case class JSUnaryOp(op: String, lhs: Tree)(implicit val pos: Position) extends Tree {
+ val tpe = AnyType
+ }
+
+ /** Binary operation (always preserves pureness).
+ *
+ * Operations which do not preserve pureness are not allowed in this tree.
+ * These are notably +=, -=, *=, /= and %=
+ */
+ case class JSBinaryOp(op: String, lhs: Tree, rhs: Tree)(implicit val pos: Position) extends Tree {
+ val tpe = AnyType
+ }
+
+ case class JSArrayConstr(items: List[Tree])(implicit val pos: Position) extends Tree {
+ val tpe = AnyType
+ }
+
+ case class JSObjectConstr(fields: List[(PropertyName, Tree)])(implicit val pos: Position) extends Tree {
+ val tpe = AnyType
+ }
+
+ case class JSEnvInfo()(implicit val pos: Position) extends Tree {
+ val tpe = AnyType
+ }
+
+ // Literals
+
+ /** Marker for literals. Literals are always pure. */
+ sealed trait Literal extends Tree
+
+ case class Undefined()(implicit val pos: Position) extends Literal {
+ val tpe = UndefType
+ }
+
+ case class UndefinedParam()(val tpe: Type)(implicit val pos: Position) extends Literal
+
+ case class Null()(implicit val pos: Position) extends Literal {
+ val tpe = NullType
+ }
+
+ case class BooleanLiteral(value: Boolean)(implicit val pos: Position) extends Literal {
+ val tpe = BooleanType
+ }
+
+ case class IntLiteral(value: Int)(implicit val pos: Position) extends Literal {
+ val tpe = IntType
+ }
+
+ case class LongLiteral(value: Long)(implicit val pos: Position) extends Literal {
+ val tpe = LongType
+ }
+
+ case class FloatLiteral(value: Float)(implicit val pos: Position) extends Literal {
+ val tpe = FloatType
+ }
+
+ case class DoubleLiteral(value: Double)(implicit val pos: Position) extends Literal {
+ val tpe = DoubleType
+ }
+
+ case class StringLiteral(value: String)(
+ implicit val pos: Position) extends Literal with PropertyName {
+ val tpe = StringType
+ override def name = value
+ }
+
+ case class ClassOf(cls: ReferenceType)(implicit val pos: Position) extends Literal {
+ val tpe = ClassType(Definitions.ClassClass)
+ }
+
+ // Atomic expressions
+
+ case class VarRef(ident: Ident, mutable: Boolean)(val tpe: Type)(implicit val pos: Position) extends Tree
+
+ case class This()(val tpe: Type)(implicit val pos: Position) extends Tree
+
+ /** Closure with explicit captures.
+ * The n captures map to the n first formal arguments.
+ */
+ case class Closure(captureParams: List[ParamDef], params: List[ParamDef],
+ body: Tree, captureValues: List[Tree])(implicit val pos: Position) extends Tree {
+ val tpe = AnyType
+ }
+
+ // Classes
+
+ case class ClassDef(name: Ident, kind: ClassKind, parent: Option[Ident], ancestors: List[Ident], defs: List[Tree])(implicit val pos: Position) extends Tree {
+ val tpe = NoType
+ }
+
+ case class MethodDef(name: PropertyName, args: List[ParamDef], resultType: Type, body: Tree)(
+ val hash: Option[TreeHash])(implicit val pos: Position) extends Tree {
+ val tpe = NoType
+ }
+
+ case class PropertyDef(name: PropertyName, getterBody: Tree, setterArg: ParamDef, setterBody: Tree)(implicit val pos: Position) extends Tree {
+ val tpe = NoType
+ }
+
+ case class ConstructorExportDef(name: String, args: List[ParamDef], body: Tree)(implicit val pos: Position) extends Tree {
+ val tpe = NoType
+ }
+
+ case class ModuleExportDef(fullName: String)(implicit val pos: Position) extends Tree {
+ val tpe = NoType
+ }
+
+ /** A hash of a tree (usually a MethodDef). Contains two SHA-1 hashes */
+ final class TreeHash(val treeHash: Array[Byte], val posHash: Array[Byte]) {
+ assert(treeHash.length == 20)
+ assert(posHash.length == 20)
+ }
+}
diff --git a/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Types.scala b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Types.scala
new file mode 100644
index 0000000..4af493a
--- /dev/null
+++ b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Types.scala
@@ -0,0 +1,182 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js IR **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.ir
+
+import scala.annotation.tailrec
+
+object Types {
+
+ /** Type of an expression in the IR. */
+ abstract sealed class Type {
+ def show(): String = {
+ val writer = new java.io.StringWriter
+ val printer = new Printers.IRTreePrinter(writer)
+ printer.printType(this)
+ writer.toString()
+ }
+ }
+
+ /** Any type (the top type of this type system).
+ * A variable of this type can contain any value, including `undefined`
+ * and `null` and any raw JS value. This type supports a very limited set
+ * of Scala operations, the ones common to all values. Basically only
+ * reference equality tests and instance tests. It also supports all
+ * JavaScript operations, since all Scala objects are also genuine
+ * JavaScript objects.
+ * The type java.lang.Object in the back-end maps to [[AnyType]] because it
+ * can hold raw JS values (not only instances of Scala.js classes).
+ */
+ case object AnyType extends Type
+
+ /** Nothing type (the bottom type of this type system).
+ * Expressions from which one can never come back are typed as [[Nothing]].
+ * For example, `throw` and `return`.
+ */
+ case object NothingType extends Type
+
+ /** The type of `undefined`. */
+ case object UndefType extends Type
+
+ /** Boolean type.
+ * It does not accept `null` nor `undefined`.
+ */
+ case object BooleanType extends Type
+
+ /** 32-bit signed integer type.
+ * It does not accept `null` nor `undefined`.
+ */
+ case object IntType extends Type
+
+ /** 64-bit signed integer type.
+ * It does not accept `null` nor `undefined`.
+ */
+ case object LongType extends Type
+
+ /** Float type (32-bit).
+ * It does not accept `null` nor `undefined`.
+ */
+ case object FloatType extends Type
+
+ /** Double type (64-bit).
+ * It does not accept `null` nor `undefined`.
+ */
+ case object DoubleType extends Type
+
+ /** String type.
+ * It does not accept `null` nor `undefined`.
+ */
+ case object StringType extends Type
+
+ /** The type of `null`.
+ * It does not accept `undefined`.
+ * The null type is a subtype of all class types and array types.
+ */
+ case object NullType extends Type
+
+ /** Reference types (allowed for classOf[], is/asInstanceOf[]). */
+ sealed abstract class ReferenceType extends Type
+
+ /** Class (or interface) type. */
+ final case class ClassType(className: String) extends ReferenceType
+
+ /** Array type. */
+ final case class ArrayType(baseClassName: String, dimensions: Int) extends ReferenceType
+
+ object ArrayType {
+ def apply(innerType: ReferenceType): ArrayType = innerType match {
+ case ClassType(className) => ArrayType(className, 1)
+ case ArrayType(className, dim) => ArrayType(className, dim + 1)
+ }
+ }
+
+ /** Record type.
+ * Used by the optimizer to inline classes as records with multiple fields.
+ * They are desugared as several local variables by JSDesugaring.
+ * Record types cannot cross method boundaries, so they cannot appear as
+ * the type of fields or parameters, nor as result types of methods.
+ * The compiler itself never generates record types.
+ */
+ final case class RecordType(fields: List[RecordType.Field]) extends Type {
+ def findField(name: String): RecordType.Field =
+ fields.find(_.name == name).get
+ }
+
+ object RecordType {
+ final case class Field(name: String, originalName: Option[String],
+ tpe: Type, mutable: Boolean)
+ }
+
+ /** No type. */
+ case object NoType extends Type
+
+ /** Tests whether a type `lhs` is a subtype of `rhs` (or equal).
+ * [[NoType]] is never a subtype or supertype of anything (including
+ * itself). All other types are subtypes of themselves.
+ * @param isSubclass A function testing whether a class/interface is a
+ * subclass of another class/interface.
+ */
+ def isSubtype(lhs: Type, rhs: Type)(
+ isSubclass: (String, String) => Boolean): Boolean = {
+ import Definitions._
+
+ (lhs != NoType && rhs != NoType) && {
+ (lhs == rhs) ||
+ ((lhs, rhs) match {
+ case (_, AnyType) => true
+ case (NothingType, _) => true
+
+ case (ClassType(lhsClass), ClassType(rhsClass)) =>
+ isSubclass(lhsClass, rhsClass)
+
+ case (NullType, ClassType(_)) => true
+ case (NullType, ArrayType(_, _)) => true
+
+ case (UndefType, ClassType(cls)) =>
+ isSubclass(BoxedUnitClass, cls)
+ case (BooleanType, ClassType(cls)) =>
+ isSubclass(BoxedBooleanClass, cls)
+ case (IntType, ClassType(cls)) =>
+ isSubclass(BoxedIntegerClass, cls) ||
+ cls == BoxedByteClass ||
+ cls == BoxedShortClass ||
+ cls == BoxedDoubleClass
+ case (LongType, ClassType(cls)) =>
+ isSubclass(BoxedLongClass, cls)
+ case (FloatType, ClassType(cls)) =>
+ isSubclass(BoxedFloatClass, cls) ||
+ cls == BoxedDoubleClass
+ case (DoubleType, ClassType(cls)) =>
+ isSubclass(BoxedDoubleClass, cls)
+ case (StringType, ClassType(cls)) =>
+ isSubclass(StringClass, cls)
+
+ case (IntType, DoubleType) => true
+ case (FloatType, DoubleType) => true
+
+ case (ArrayType(lhsBase, lhsDims), ArrayType(rhsBase, rhsDims)) =>
+ if (lhsDims < rhsDims) {
+ false // because Array[A] </: Array[Array[A]]
+ } else if (lhsDims > rhsDims) {
+ rhsBase == ObjectClass // because Array[Array[A]] <: Array[Object]
+ } else { // lhsDims == rhsDims
+ // lhsBase must be <: rhsBase
+ if (isPrimitiveClass(lhsBase) || isPrimitiveClass(rhsBase)) {
+ lhsBase == rhsBase
+ } else {
+ isSubclass(lhsBase, rhsBase)
+ }
+ }
+
+ case _ =>
+ false
+ })
+ }
+ }
+}
diff --git a/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Utils.scala b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Utils.scala
new file mode 100644
index 0000000..d4769dc
--- /dev/null
+++ b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Utils.scala
@@ -0,0 +1,110 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js IR **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.ir
+
+import java.net.URI
+
+object Utils {
+
+ /** Relativize target URI w.r.t. base URI */
+ def relativize(base0: URI, trgt0: URI): URI = {
+ val base = base0.normalize
+ val trgt = trgt0.normalize
+
+ if (base.isOpaque || !base.isAbsolute || base.getRawPath == null ||
+ trgt.isOpaque || !trgt.isAbsolute || trgt.getRawPath == null ||
+ base.getScheme != trgt.getScheme ||
+ base.getRawAuthority != trgt.getRawAuthority)
+ trgt
+ else {
+ val trgtCmps = trgt.getRawPath.split('/')
+ val baseCmps = base.getRawPath.split('/')
+
+ val prefixLen = (trgtCmps zip baseCmps).takeWhile(t => t._1 == t._2).size
+
+ val newPathCmps =
+ List.fill(baseCmps.size - prefixLen)("..") ++ trgtCmps.drop(prefixLen)
+
+ val newPath = newPathCmps.mkString("/")
+
+ // Relative URI does not have scheme or authority
+ new URI(null, null, newPath, trgt.getRawQuery, trgt.getRawFragment)
+ }
+ }
+
+ /** Adds an empty authority to URIs with the "file" scheme without authority.
+ * Some browsers don't fetch URIs without authority correctly.
+ */
+ def fixFileURI(uri: URI): URI =
+ if (uri.getScheme() != "file" || uri.getAuthority() != null) uri
+ else new URI("file", "", uri.getPath(), uri.getQuery(), uri.getFragment())
+
+ def escapeJS(str: String): String = {
+ /* Note that Java and JavaScript happen to use the same encoding for
+ * Unicode, namely UTF-16, which means that 1 char from Java always equals
+ * 1 char in JavaScript. */
+ val builder = new StringBuilder
+ str foreach {
+ case '\\' => builder.append("\\\\")
+ case '"' => builder.append("\\\"")
+ case '\u0007' => builder.append("\\a")
+ case '\u0008' => builder.append("\\b")
+ case '\u0009' => builder.append("\\t")
+ case '\u000A' => builder.append("\\n")
+ case '\u000B' => builder.append("\\v")
+ case '\u000C' => builder.append("\\f")
+ case '\u000D' => builder.append("\\r")
+ case c =>
+ if (c >= 32 && c <= 126) builder.append(c.toChar) // ASCII printable characters
+ else builder.append(f"\\u$c%04x")
+ }
+ builder.result()
+ }
+
+ /** A ByteArrayOutput stream that allows to jump back to a given
+ * position and complete some bytes. Methods must be called in the
+ * following order only:
+ * - [[markJump]]
+ * - [[jumpBack]]
+ * - [[continue]]
+ */
+ private[ir] class JumpBackByteArrayOutputStream
+ extends java.io.ByteArrayOutputStream {
+ protected var jumpBackPos: Int = -1
+ protected var headPos: Int = -1
+
+ /** Marks the current location for a jumpback */
+ def markJump(): Unit = {
+ assert(jumpBackPos == -1)
+ assert(headPos == -1)
+ jumpBackPos = count
+ }
+
+ /** Jumps back to the mark. Returns the number of bytes jumped */
+ def jumpBack(): Int = {
+ assert(jumpBackPos >= 0)
+ assert(headPos == -1)
+ val jumped = count - jumpBackPos
+ headPos = count
+ count = jumpBackPos
+ jumpBackPos = -1
+ jumped
+ }
+
+ /** Continues to write at the head. */
+ def continue(): Unit = {
+ assert(jumpBackPos == -1)
+ assert(headPos >= 0)
+ count = headPos
+ headPos = -1
+ }
+ }
+
+}
diff --git a/examples/scala-js/jasmine-test-framework/src/main/resources/jasmine-polyfills.js b/examples/scala-js/jasmine-test-framework/src/main/resources/jasmine-polyfills.js
new file mode 100644
index 0000000..b1a5d44
--- /dev/null
+++ b/examples/scala-js/jasmine-test-framework/src/main/resources/jasmine-polyfills.js
@@ -0,0 +1,9 @@
+// jasmine.js requires the following 4 to be defined.
+(function() {
+ var g = (typeof global === "object" && global && global["Object"] === Object) ? global : this;
+ var stub = function() { console.log("jasmine-polyfill.js stub called"); };
+ g.setTimeout = g.setTimeout || stub;
+ g.clearTimeout = g.clearTimeout || stub;
+ g.setInterval = g.setInterval || stub;
+ g.clearInterval = g.clearInterval || stub;
+}).call(this);
diff --git a/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Jasmine.scala b/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Jasmine.scala
new file mode 100644
index 0000000..ca0a63f
--- /dev/null
+++ b/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Jasmine.scala
@@ -0,0 +1,17 @@
+package org.scalajs.jasmine
+
+import scala.scalajs.js
+
+object Jasmine extends js.GlobalScope {
+ def jasmine: JasmineEnv = js.native
+ def describe(name: String, suite: js.Function0[_]): Unit = js.native
+ def it(title: String, test: js.Function0[_]): Unit = js.native
+ def xdescribe(name: String, suite: js.Function0[_]): Unit = js.native
+ def xit(title: String, test: js.Function0[_]): Unit = js.native
+ def beforeEach(block: js.Function0[_]): Unit = js.native
+ def afterEach(block: js.Function0[_]): Unit = js.native
+ def expect(exp: js.Any): JasmineExpectation = js.native
+ def runs(block: js.Function0[_]): Unit = js.native
+ def waits(timeout: Int): Unit = js.native
+ def waitsFor(block: js.Function0[Boolean], errorMsg: String, timeout: Int): Unit = js.native
+}
diff --git a/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/JasmineEnv.scala b/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/JasmineEnv.scala
new file mode 100644
index 0000000..b81121e
--- /dev/null
+++ b/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/JasmineEnv.scala
@@ -0,0 +1,14 @@
+package org.scalajs.jasmine
+
+import scala.scalajs.js
+
+trait JasmineEnv extends js.Object {
+ def Clock: JasmineEnv.Clock = js.native
+}
+
+object JasmineEnv {
+ trait Clock extends js.Object {
+ def tick(time: Double): Unit = js.native
+ def useMock(): Unit = js.native
+ }
+}
diff --git a/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/JasmineExpectation.scala b/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/JasmineExpectation.scala
new file mode 100644
index 0000000..9aad02e
--- /dev/null
+++ b/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/JasmineExpectation.scala
@@ -0,0 +1,21 @@
+package org.scalajs.jasmine
+
+import scala.scalajs.js
+
+trait JasmineExpectation extends js.Object {
+ def toBe(exp: js.Any): Unit = js.native
+ def toEqual(exp: js.Any): Unit = js.native
+ def toMatch(exp: js.RegExp): Unit = js.native
+ def toMatch(exp: String): Unit = js.native
+ def toBeDefined(): Unit = js.native
+ def toBeUndefined(): Unit = js.native
+ def toBeNull(): Unit = js.native
+ def toBeTruthy(): Unit = js.native
+ def toBeFalsy(): Unit = js.native
+ def toContain(exp: js.Any): Unit = js.native
+ def toBeGreaterThan(exp: Double): Unit = js.native
+ def toBeLessThan(exp: Double): Unit = js.native
+ def toBeCloseTo(exp: Double, precision: Int = 2): Unit = js.native
+ def toThrow(): Unit = js.native
+ val not: JasmineExpectation = js.native
+}
diff --git a/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Results.scala b/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Results.scala
new file mode 100644
index 0000000..aed78b9
--- /dev/null
+++ b/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Results.scala
@@ -0,0 +1,13 @@
+package org.scalajs.jasmine
+
+import scala.scalajs.js
+
+trait Result extends js.Object {
+ def `type`: String = js.native
+ val trace: js.Dynamic = js.native
+}
+
+trait ExpectationResult extends Result {
+ def passed(): Boolean = js.native
+ val message: String = js.native
+}
diff --git a/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Spec.scala b/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Spec.scala
new file mode 100644
index 0000000..afbfa13
--- /dev/null
+++ b/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Spec.scala
@@ -0,0 +1,9 @@
+package org.scalajs.jasmine
+
+import scala.scalajs.js
+
+trait Spec extends js.Object {
+ def results(): SpecResults = js.native
+ val description: String = js.native
+ val suite: Suite = js.native
+}
diff --git a/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/SpecResults.scala b/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/SpecResults.scala
new file mode 100644
index 0000000..50f073c
--- /dev/null
+++ b/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/SpecResults.scala
@@ -0,0 +1,8 @@
+package org.scalajs.jasmine
+
+import scala.scalajs.js
+
+trait SpecResults extends js.Object {
+ def passed(): Boolean = js.native
+ def getItems(): js.Array[Result] = js.native
+}
diff --git a/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Suite.scala b/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Suite.scala
new file mode 100644
index 0000000..cd4fb75
--- /dev/null
+++ b/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Suite.scala
@@ -0,0 +1,8 @@
+package org.scalajs.jasmine
+
+import scala.scalajs.js
+
+trait Suite extends js.Object {
+ def results(): SuiteResults = js.native
+ val description: String = js.native
+}
diff --git a/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/SuiteResults.scala b/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/SuiteResults.scala
new file mode 100644
index 0000000..db98acf
--- /dev/null
+++ b/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/SuiteResults.scala
@@ -0,0 +1,9 @@
+package org.scalajs.jasmine
+
+import scala.scalajs.js
+
+trait SuiteResults extends js.Object {
+ val passedCount: Int = js.native
+ val failedCount: Int = js.native
+ val totalCount: Int = js.native
+}
diff --git a/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTest.scala b/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTest.scala
new file mode 100644
index 0000000..715d39d
--- /dev/null
+++ b/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTest.scala
@@ -0,0 +1,34 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Framework **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package org.scalajs.jasminetest
+
+import scala.scalajs.js
+import scala.scalajs.testbridge._
+
+import java.util.regex.Pattern
+import org.scalajs.jasmine.Jasmine
+import org.scalajs.jasmine.JasmineExpectation
+
+class JasmineTest extends Test with TestSuiteContext {
+ def jasmine = Jasmine.jasmine
+ def describe(name: String)(suite: => Unit): Unit = Jasmine.describe(name, suite _)
+ def it(title: String)(test: => Unit): Unit = Jasmine.it(title, test _)
+ def xdescribe(name: String)(suite: => Unit): Unit = Jasmine.xdescribe(name, suite _)
+ def xit(title: String)(test: => Unit): Unit = Jasmine.xit(title, test _)
+ def beforeEach(block: => Unit): Unit = Jasmine.beforeEach(block _)
+ def afterEach(block: => Unit): Unit = Jasmine.afterEach(block _)
+ def expect(exp: CharSequence): JasmineExpectation =
+ Jasmine.expect(if (exp == null) null else exp.toString)
+ def expect(exp: js.Any): JasmineExpectation = Jasmine.expect(exp)
+ def runs(block: => Unit): Unit = Jasmine.runs(block _)
+ def waits(timeout: Int): Unit = Jasmine.waits(timeout)
+ def waitsFor(block: => Boolean, errorMsg: String, timeout: Int): Unit =
+ Jasmine.waitsFor(block _, errorMsg, timeout)
+}
diff --git a/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTestFramework.scala b/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTestFramework.scala
new file mode 100644
index 0000000..2686e31
--- /dev/null
+++ b/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTestFramework.scala
@@ -0,0 +1,93 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Framework **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package org.scalajs.jasminetest
+
+import scala.scalajs.js
+import scala.scalajs.js.Dynamic.global
+import scala.scalajs.js.JavaScriptException
+import scala.scalajs.js.annotation.JSExport
+
+import scala.scalajs.testbridge._
+
+import scala.collection.mutable
+
+object JasmineTestFramework extends TestFramework {
+ createStackPropertyOnThrowable()
+
+ private def createStackPropertyOnThrowable(): Unit = {
+ /* All Jasmine cares about when looking for stack trace data is a field
+ * `stack` on the error object. Our Throwables do not have a `stack` field
+ * because they are not subclasses of the JavaScript class Error.
+ * However, a genuine Error object with the proper (lazy) stack field is
+ * stored under the property stackdata by StackTrace.
+ * This code installs a property getter on Throwable that will redirect
+ * `throwable.stack` to `throwable.stackdata.stack` (when it exists).
+ */
+
+ val ThrowablePrototype = js.Object.getPrototypeOf(
+ (new Throwable).asInstanceOf[js.Object]).asInstanceOf[js.Object]
+
+ js.Object.defineProperty(ThrowablePrototype, "stack", js.Dynamic.literal(
+ configurable = false,
+ enumerable = false,
+ get = { (self: js.Dynamic) =>
+ self.stackdata && self.stackdata.stack
+ }: js.ThisFunction
+ ).asInstanceOf[js.PropertyDescriptor])
+ }
+
+ private val tags = mutable.Set.empty[String]
+
+ def hasTag(tag: String): Boolean = tags.contains(tag)
+
+ def runTest(testOutput: TestOutput, args: js.Array[String])(
+ test: js.Function0[Test]): Unit = {
+
+ val jasmine = global.jasmine
+ val reporter = new JasmineTestReporter(testOutput)
+
+ handleArgs(args, testOutput)
+
+ try {
+ test()
+
+ val jasmineEnv = jasmine.getEnv()
+ jasmineEnv.addReporter(reporter.asInstanceOf[js.Any])
+ jasmineEnv.updateInterval = 0
+ jasmineEnv.execute()
+ } catch {
+ case throwable@JavaScriptException(exception) =>
+ testOutput.error("Problem executing code in tests: " + exception,
+ throwable.getStackTrace())
+ } finally {
+ clearTags()
+ }
+ }
+
+ /** Set tags used in tests. Only use when invoking with HTML reporter */
+ @JSExport
+ def setTags(newTags: String*): Unit = {
+ clearTags()
+ tags ++= newTags
+ }
+
+ /** Clear tags used in tests. Only use when invoking with HTML reporter */
+ @JSExport
+ def clearTags(): Unit = tags.clear()
+
+ private def handleArgs(args: js.Array[String], testOutput: TestOutput) = {
+ for (arg <- args) {
+ if (arg.startsWith("-t"))
+ tags += arg.stripPrefix("-t")
+ else
+ testOutput.log.warn(s"Jasmine: Discarding argument: $arg")
+ }
+ }
+}
diff --git a/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTestReporter.scala b/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTestReporter.scala
new file mode 100644
index 0000000..79a7c75
--- /dev/null
+++ b/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTestReporter.scala
@@ -0,0 +1,130 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Framework **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package org.scalajs.jasminetest
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSExport
+
+import scala.scalajs.runtime.StackTrace
+
+import scala.scalajs.testbridge._
+
+import org.scalajs.jasmine.ExpectationResult
+import org.scalajs.jasmine.Result
+import org.scalajs.jasmine.Spec
+import org.scalajs.jasmine.Suite
+
+/** This class is passed to the actual jasmine framework as a reporter */
+class JasmineTestReporter(testOutput: TestOutput) {
+ private var currentSuite: Suite = _
+
+ @JSExport
+ def reportRunnerStarting(): Unit = {
+ testOutput.log.info("")
+ }
+
+ @JSExport
+ def reportSpecStarting(spec: Spec): Unit = {
+ if (currentSuite != spec.suite) {
+ currentSuite = spec.suite
+ info(currentSuite.description)
+ }
+ }
+
+ @JSExport
+ def reportSpecResults(spec: Spec): Unit = {
+ val results = spec.results()
+ val description = spec.description
+
+ if (results.passed) {
+ testOutput.succeeded(s" $success $description")
+ } else {
+ error(s" $failure $description")
+
+ results.getItems foreach displayResult
+ }
+ }
+
+ @JSExport
+ def reportSuiteResults(suite: Suite): Unit = {
+ var results = suite.results()
+
+ info("")
+ val title = "Total for suite " + suite.description
+ val message =
+ s"${results.totalCount} specs, ${results.failedCount} failure"
+
+ if (results.passedCount != results.totalCount) {
+ error(title)
+ errorWithInfoColor(message)
+ } else {
+ info(title)
+ infoWithInfoColor(message)
+ }
+ info("")
+ }
+
+ @JSExport
+ def reportRunnerResults(): Unit = {
+ // no need to report
+ }
+
+ private def info(str: String) =
+ testOutput.log.info(str)
+
+ private def infoWithInfoColor(str: String) =
+ info(withColor(testOutput.infoColor, str))
+
+ private def errorWithInfoColor(str: String) =
+ error(withColor(testOutput.infoColor, str))
+
+ private def error(msg: js.Any) =
+ testOutput.log.error(msg.toString)
+
+ private def withColor(color: testOutput.Color, message: String) =
+ testOutput.color(message, color)
+
+ private def sanitizeMessage(message: String) = {
+ val FilePattern = """^(.+?) [^ ]+\.js \(line \d+\)\.*?$""".r
+ val EvalPattern = """^(.+?) in eval.+\(eval\).+?\(line \d+\).*?$""".r
+
+ message match {
+ case FilePattern(originalMessage) => originalMessage
+ case EvalPattern(originalMessage) => originalMessage
+ case message => message
+ }
+ }
+
+ private def failure = withColor(testOutput.errorColor, "x")
+ private def success = withColor(testOutput.successColor, "+")
+
+ private def displayResult(result: Result) = {
+ (result.`type`: String) match {
+ case "log" =>
+ info(s" ${result.toString}")
+ case "expect" =>
+ val r = result.asInstanceOf[ExpectationResult]
+
+ if (!r.passed()) {
+ val message = sanitizeMessage(r.message)
+ val stack = StackTrace.extract(r.trace).takeWhile { stackElem =>
+ (stackElem.getFileName == null ||
+ !stackElem.getFileName.endsWith("jasmine.js"))
+ }
+
+ if (stack.isEmpty)
+ testOutput.failure(s" $message")
+ else
+ testOutput.error(s" $message", stack)
+ }
+ }
+ }
+
+}
diff --git a/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/TestSuiteContext.scala b/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/TestSuiteContext.scala
new file mode 100644
index 0000000..827eefd
--- /dev/null
+++ b/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/TestSuiteContext.scala
@@ -0,0 +1,55 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Framework **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package org.scalajs.jasminetest
+
+trait TestSuiteContext {
+ def describe(title: String)(test: => Unit): Unit
+ def it(title: String)(test: => Unit): Unit
+ def xdescribe(title: String)(test: => Unit): Unit
+ def xit(title: String)(test: => Unit): Unit
+
+ def when(tag: String): TestSuiteContext =
+ if (JasmineTestFramework.hasTag(tag)) this
+ else new TestSuiteContext.IgnoredContext(this)
+
+ def whenAll(tags: String*): TestSuiteContext =
+ if (tags.forall(JasmineTestFramework.hasTag)) this
+ else new TestSuiteContext.IgnoredContext(this)
+
+ def whenAny(tags: String*): TestSuiteContext =
+ if (tags.exists(JasmineTestFramework.hasTag)) this
+ else new TestSuiteContext.IgnoredContext(this)
+
+ def unless(tag: String): TestSuiteContext =
+ if (!JasmineTestFramework.hasTag(tag)) this
+ else new TestSuiteContext.IgnoredContext(this)
+
+ def unlessAll(tags: String*): TestSuiteContext =
+ if (!tags.forall(JasmineTestFramework.hasTag)) this
+ else new TestSuiteContext.IgnoredContext(this)
+
+ def unlessAny(tags: String*): TestSuiteContext =
+ if (!tags.exists(JasmineTestFramework.hasTag)) this
+ else new TestSuiteContext.IgnoredContext(this)
+}
+
+object TestSuiteContext {
+ private class IgnoredContext(
+ baseContext: TestSuiteContext) extends TestSuiteContext {
+ def describe(title: String)(test: => Unit): Unit =
+ baseContext.xdescribe(title)(test)
+ def it(title: String)(test: => Unit): Unit =
+ baseContext.xit(title)(test)
+ def xdescribe(title: String)(test: => Unit): Unit =
+ baseContext.xdescribe(title)(test)
+ def xit(title: String)(test: => Unit): Unit =
+ baseContext.xit(title)(test)
+ }
+}
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/Appendable.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/Appendable.scala
new file mode 100644
index 0000000..9cd74ad
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/Appendable.scala
@@ -0,0 +1,7 @@
+package java.lang
+
+trait Appendable {
+ def append(c: Char): Appendable
+ def append(csq: CharSequence): Appendable
+ def append(csq: CharSequence, start: Int, end: Int): Appendable
+}
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/AutoCloseable.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/AutoCloseable.scala
new file mode 100644
index 0000000..21a3d0f
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/AutoCloseable.scala
@@ -0,0 +1,5 @@
+package java.lang
+
+trait AutoCloseable {
+ def close(): Unit
+}
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/Boolean.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/Boolean.scala
new file mode 100644
index 0000000..94a9967
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/Boolean.scala
@@ -0,0 +1,61 @@
+package java.lang
+
+import scala.scalajs.js
+
+/* This is a hijacked class. Its instances are primitive booleans.
+ * Constructors are not emitted.
+ */
+final class Boolean private () extends Comparable[Boolean] {
+
+ def this(value: scala.Boolean) = this()
+ def this(v: String) = this()
+
+ @inline def booleanValue(): scala.Boolean =
+ this.asInstanceOf[scala.Boolean]
+
+ @inline override def equals(that: Any): scala.Boolean =
+ this eq that.asInstanceOf[AnyRef]
+
+ @inline override def hashCode(): Int =
+ if (booleanValue) 1231 else 1237
+
+ @inline override def compareTo(that: Boolean): Int =
+ Boolean.compare(booleanValue, that.booleanValue)
+
+ @inline override def toString(): String =
+ Boolean.toString(booleanValue)
+
+}
+
+object Boolean {
+ final val TYPE = classOf[scala.Boolean]
+
+ /* TRUE and FALSE are supposed to be vals. However, they are better
+ * optimized as defs, because they end up being just the constant true and
+ * false (since `new Boolean(x)` is a no-op).
+ * Since vals and defs are binary-compatible (although they're not strictly
+ * speaking source-compatible, because of stability), we implement them as
+ * defs. Source-compatibility is not an issue because user code is compiled
+ * against the JDK .class files anyway.
+ * Moreover, preserving the identity of TRUE and FALSE is not an issue
+ * either, since they are primitive booleans in the end.
+ */
+ def TRUE: Boolean = new Boolean(true)
+ def FALSE: Boolean = new Boolean(false)
+
+ @inline def valueOf(booleanValue: scala.Boolean): Boolean = {
+ // We don't care about identity, since they end up as primitive booleans
+ new Boolean(booleanValue)
+ }
+
+ @inline def valueOf(s: String): Boolean = valueOf(parseBoolean(s))
+
+ @inline def parseBoolean(s: String): scala.Boolean =
+ (s != null) && s.equalsIgnoreCase("true")
+
+ @inline def toString(b: scala.Boolean): String =
+ "" + b
+
+ @inline def compare(x: scala.Boolean, y: scala.Boolean): scala.Int =
+ if (x == y) 0 else if (x) 1 else -1
+}
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/Byte.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/Byte.scala
new file mode 100644
index 0000000..dc0c82f
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/Byte.scala
@@ -0,0 +1,71 @@
+package java.lang
+
+import scala.scalajs.js
+
+/* This is a hijacked class. Its instances are primitive numbers.
+ * Constructors are not emitted.
+ */
+final class Byte private () extends Number with Comparable[Byte] {
+
+ def this(value: scala.Byte) = this()
+ def this(s: String) = this()
+
+ @inline override def byteValue(): scala.Byte =
+ this.asInstanceOf[scala.Byte]
+
+ @inline override def shortValue(): scala.Short = byteValue.toShort
+ @inline def intValue(): scala.Int = byteValue.toInt
+ @inline def longValue(): scala.Long = byteValue.toLong
+ @inline def floatValue(): scala.Float = byteValue.toFloat
+ @inline def doubleValue(): scala.Double = byteValue.toDouble
+
+ @inline override def equals(that: Any): scala.Boolean =
+ this eq that.asInstanceOf[AnyRef]
+
+ @inline override def hashCode(): Int =
+ byteValue
+
+ @inline override def compareTo(that: Byte): Int =
+ Byte.compare(byteValue, that.byteValue)
+
+ @inline override def toString(): String =
+ Byte.toString(byteValue)
+}
+
+object Byte {
+ final val TYPE = classOf[scala.Byte]
+ final val SIZE = 8
+
+ /* MIN_VALUE and MAX_VALUE should be 'final val's. But it is impossible to
+ * write a proper Byte literal in Scala, that would both considered a Byte
+ * and a constant expression (optimized as final val).
+ * Since vals and defs are binary-compatible (although they're not strictly
+ * speaking source-compatible, because of stability), we implement them as
+ * defs. Source-compatibility is not an issue because user code is compiled
+ * against the JDK .class files anyway.
+ */
+ def MIN_VALUE: scala.Byte = -128
+ def MAX_VALUE: scala.Byte = 127
+
+ @inline def valueOf(byteValue: scala.Byte): Byte = new Byte(byteValue)
+ @inline def valueOf(s: String): Byte = valueOf(parseByte(s))
+
+ @inline def valueOf(s: String, radix: Int): Byte =
+ valueOf(parseByte(s, radix))
+
+ @inline def parseByte(s: String): scala.Byte = parseByte(s, 10)
+
+ def parseByte(s: String, radix: Int): scala.Byte = {
+ val r = Integer.parseInt(s, radix)
+ if (r < MIN_VALUE || r > MAX_VALUE)
+ throw new NumberFormatException(s"""For input string: "$s"""")
+ else
+ r.toByte
+ }
+
+ @inline def toString(b: scala.Byte): String =
+ "" + b
+
+ @inline def compare(x: scala.Byte, y: scala.Byte): scala.Int =
+ x - y
+}
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/CharSequence.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/CharSequence.scala
new file mode 100644
index 0000000..5875a2d
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/CharSequence.scala
@@ -0,0 +1,8 @@
+package java.lang
+
+trait CharSequence {
+ def length(): scala.Int
+ def charAt(index: scala.Int): scala.Char
+ def subSequence(start: scala.Int, end: scala.Int): CharSequence
+ def toString(): String
+}
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/Character.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/Character.scala
new file mode 100644
index 0000000..1b2b565
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/Character.scala
@@ -0,0 +1,289 @@
+package java.lang
+
+import scala.scalajs.js
+
+class Character(private val value: scala.Char) extends Comparable[Character] {
+
+ def charValue(): scala.Char = value
+
+ override def equals(that: Any) =
+ that.isInstanceOf[Character] && (value == that.asInstanceOf[Character].charValue)
+
+ override def compareTo(that: Character): Int =
+ Character.compare(charValue, that.charValue)
+
+ override def toString(): String =
+ Character.toString(value)
+
+ override def hashCode(): Int = value.##
+
+ /*
+ * Methods on scala.Char
+ * The following methods are only here to properly support reflective calls
+ * on boxed primitive values. YOU WILL NOT BE ABLE TO USE THESE METHODS, since
+ * we use the true javalib to lookup symbols, this file contains only
+ * implementations.
+ */
+ protected def toByte: scala.Byte = value.toByte
+ protected def toShort: scala.Short = value.toShort
+ protected def toChar: scala.Char = value.toChar
+ protected def toInt: scala.Int = value
+ protected def toLong: scala.Long = value.toLong
+ protected def toFloat: scala.Float = value.toFloat
+ protected def toDouble: scala.Double = value.toDouble
+
+ protected def unary_~ : scala.Int = ~value
+ protected def unary_+ : scala.Int = value
+ protected def unary_- : scala.Int = -value
+
+ protected def +(x: String): String = value + x
+
+ protected def <<(x: scala.Int): scala.Int = value << x
+ protected def <<(x: scala.Long): scala.Int = value << x
+ protected def >>>(x: scala.Int): scala.Int = value >>> x
+ protected def >>>(x: scala.Long): scala.Int = value >>> x
+ protected def >>(x: scala.Int): scala.Int = value >> x
+ protected def >>(x: scala.Long): scala.Int = value >> x
+
+ protected def ==(x: scala.Byte): scala.Boolean = value == x
+ protected def ==(x: scala.Short): scala.Boolean = value == x
+ protected def ==(x: scala.Char): scala.Boolean = value == x
+ protected def ==(x: scala.Int): scala.Boolean = value == x
+ protected def ==(x: scala.Long): scala.Boolean = value == x
+ protected def ==(x: scala.Float): scala.Boolean = value == x
+ protected def ==(x: scala.Double): scala.Boolean = value == x
+
+ protected def !=(x: scala.Byte): scala.Boolean = value != x
+ protected def !=(x: scala.Short): scala.Boolean = value != x
+ protected def !=(x: scala.Char): scala.Boolean = value != x
+ protected def !=(x: scala.Int): scala.Boolean = value != x
+ protected def !=(x: scala.Long): scala.Boolean = value != x
+ protected def !=(x: scala.Float): scala.Boolean = value != x
+ protected def !=(x: scala.Double): scala.Boolean = value != x
+
+ protected def <(x: scala.Byte): scala.Boolean = value < x
+ protected def <(x: scala.Short): scala.Boolean = value < x
+ protected def <(x: scala.Char): scala.Boolean = value < x
+ protected def <(x: scala.Int): scala.Boolean = value < x
+ protected def <(x: scala.Long): scala.Boolean = value < x
+ protected def <(x: scala.Float): scala.Boolean = value < x
+ protected def <(x: scala.Double): scala.Boolean = value < x
+
+ protected def <=(x: scala.Byte): scala.Boolean = value <= x
+ protected def <=(x: scala.Short): scala.Boolean = value <= x
+ protected def <=(x: scala.Char): scala.Boolean = value <= x
+ protected def <=(x: scala.Int): scala.Boolean = value <= x
+ protected def <=(x: scala.Long): scala.Boolean = value <= x
+ protected def <=(x: scala.Float): scala.Boolean = value <= x
+ protected def <=(x: scala.Double): scala.Boolean = value <= x
+
+ protected def >(x: scala.Byte): scala.Boolean = value > x
+ protected def >(x: scala.Short): scala.Boolean = value > x
+ protected def >(x: scala.Char): scala.Boolean = value > x
+ protected def >(x: scala.Int): scala.Boolean = value > x
+ protected def >(x: scala.Long): scala.Boolean = value > x
+ protected def >(x: scala.Float): scala.Boolean = value > x
+ protected def >(x: scala.Double): scala.Boolean = value > x
+
+ protected def >=(x: scala.Byte): scala.Boolean = value >= x
+ protected def >=(x: scala.Short): scala.Boolean = value >= x
+ protected def >=(x: scala.Char): scala.Boolean = value >= x
+ protected def >=(x: scala.Int): scala.Boolean = value >= x
+ protected def >=(x: scala.Long): scala.Boolean = value >= x
+ protected def >=(x: scala.Float): scala.Boolean = value >= x
+ protected def >=(x: scala.Double): scala.Boolean = value >= x
+
+ protected def |(x: scala.Byte): scala.Int = value | x
+ protected def |(x: scala.Short): scala.Int = value | x
+ protected def |(x: scala.Char): scala.Int = value | x
+ protected def |(x: scala.Int): scala.Int = value | x
+ protected def |(x: scala.Long): scala.Long = value | x
+
+ protected def &(x: scala.Byte): scala.Int = value & x
+ protected def &(x: scala.Short): scala.Int = value & x
+ protected def &(x: scala.Char): scala.Int = value & x
+ protected def &(x: scala.Int): scala.Int = value & x
+ protected def &(x: scala.Long): scala.Long = value & x
+
+ protected def ^(x: scala.Byte): scala.Int = value ^ x
+ protected def ^(x: scala.Short): scala.Int = value ^ x
+ protected def ^(x: scala.Char): scala.Int = value ^ x
+ protected def ^(x: scala.Int): scala.Int = value ^ x
+ protected def ^(x: scala.Long): scala.Long = value ^ x
+
+ protected def +(x: scala.Byte): scala.Int = value + x
+ protected def +(x: scala.Short): scala.Int = value + x
+ protected def +(x: scala.Char): scala.Int = value + x
+ protected def +(x: scala.Int): scala.Int = value + x
+ protected def +(x: scala.Long): scala.Long = value + x
+ protected def +(x: scala.Float): scala.Float = value + x
+ protected def +(x: scala.Double): scala.Double = value + x
+
+ protected def -(x: scala.Byte): scala.Int = value - x
+ protected def -(x: scala.Short): scala.Int = value - x
+ protected def -(x: scala.Char): scala.Int = value - x
+ protected def -(x: scala.Int): scala.Int = value - x
+ protected def -(x: scala.Long): scala.Long = value - x
+ protected def -(x: scala.Float): scala.Float = value - x
+ protected def -(x: scala.Double): scala.Double = value - x
+
+ protected def *(x: scala.Byte): scala.Int = value * x
+ protected def *(x: scala.Short): scala.Int = value * x
+ protected def *(x: scala.Char): scala.Int = value * x
+ protected def *(x: scala.Int): scala.Int = value * x
+ protected def *(x: scala.Long): scala.Long = value * x
+ protected def *(x: scala.Float): scala.Float = value * x
+ protected def *(x: scala.Double): scala.Double = value * x
+
+ protected def /(x: scala.Byte): scala.Int = value / x
+ protected def /(x: scala.Short): scala.Int = value / x
+ protected def /(x: scala.Char): scala.Int = value / x
+ protected def /(x: scala.Int): scala.Int = value / x
+ protected def /(x: scala.Long): scala.Long = value / x
+ protected def /(x: scala.Float): scala.Float = value / x
+ protected def /(x: scala.Double): scala.Double = value / x
+
+ protected def %(x: scala.Byte): scala.Int = value % x
+ protected def %(x: scala.Short): scala.Int = value % x
+ protected def %(x: scala.Char): scala.Int = value % x
+ protected def %(x: scala.Int): scala.Int = value % x
+ protected def %(x: scala.Long): scala.Long = value % x
+ protected def %(x: scala.Float): scala.Float = value % x
+ protected def %(x: scala.Double): scala.Double = value % x
+
+}
+
+object Character {
+ final val TYPE = classOf[scala.Char]
+ final val MIN_VALUE = '\u0000'
+ final val MAX_VALUE = '\uffff'
+ final val SIZE = 16
+
+ def valueOf(charValue: scala.Char) = new Character(charValue)
+
+ /* These are supposed to be final vals of type Byte, but that's not possible.
+ * So we implement them as def's, which are binary compatible with final vals.
+ */
+ def UPPERCASE_LETTER: scala.Byte = 1
+ def LOWERCASE_LETTER: scala.Byte = 2
+ def TITLECASE_LETTER: scala.Byte = 3
+ def MODIFIER_LETTER: scala.Byte = 4
+ def OTHER_LETTER: scala.Byte = 5
+ def NON_SPACING_MARK: scala.Byte = 6
+ def ENCLOSING_MARK: scala.Byte = 7
+ def COMBINING_SPACING_MARK: scala.Byte = 8
+ def DECIMAL_DIGIT_NUMBER: scala.Byte = 9
+ def LETTER_NUMBER: scala.Byte = 10
+ def SURROGATE: scala.Byte = 19
+
+ final val MIN_RADIX = 2
+ final val MAX_RADIX = 36
+
+ final val MIN_HIGH_SURROGATE = '\uD800'
+ final val MAX_HIGH_SURROGATE = '\uDBFF'
+ final val MIN_LOW_SURROGATE = '\uDC00'
+ final val MAX_LOW_SURROGATE = '\uDFFF'
+ final val MIN_SURROGATE = MIN_HIGH_SURROGATE
+ final val MAX_SURROGATE = MAX_LOW_SURROGATE
+
+ final val MIN_CODE_POINT = 0
+ final val MAX_CODE_POINT = 0x10ffff
+ final val MIN_SUPPLEMENTARY_CODE_POINT = 0x10000
+
+ // Not implemented:
+ //def getType(ch: scala.Char): scala.Int
+ //def getType(codePoint: scala.Int): scala.Int
+
+ def digit(c: scala.Char, radix: scala.Int): scala.Int = {
+ if (radix > MAX_RADIX || radix < MIN_RADIX)
+ -1
+ else if (c >= '0' && c <= '9' && c - '0' < radix)
+ c - '0'
+ else if (c >= 'A' && c <= 'Z' && c - 'A' < radix - 10)
+ c - 'A' + 10
+ else if (c >= 'a' && c <= 'z' && c - 'a' < radix - 10)
+ c - 'a' + 10
+ else if (c >= '\uFF21' && c <= '\uFF3A' &&
+ c - '\uFF21' < radix - 10)
+ c - '\uFF21' + 10
+ else if (c >= '\uFF41' && c <= '\uFF5A' &&
+ c - '\uFF41' < radix - 10)
+ c - '\uFF21' + 10
+ else -1
+ }
+
+ def isISOControl(c: scala.Char): scala.Boolean = isISOControl(c.toInt)
+ def isISOControl(codePoint: scala.Int): scala.Boolean = {
+ (0x00 <= codePoint && codePoint <= 0x1F) || (0x7F <= codePoint && codePoint <= 0x9F)
+ }
+
+ def isDigit(c: scala.Char): scala.Boolean = c >= '0' && c <= '9'
+ //def isLetter(c: scala.Char): scala.Boolean
+ //def isLetterOrDigit(c: scala.Char): scala.Boolean
+ def isWhitespace(c: scala.Char): scala.Boolean = js.RegExp("^\\s$").test(c.toString)
+ //def isSpaceChar(c: scala.Char): scala.Boolean
+
+ // --- UTF-16 surrogate pairs handling ---
+ // See http://en.wikipedia.org/wiki/UTF-16
+
+ private final val HighSurrogateMask = 0xfc00 // 111111 00 00000000
+ private final val HighSurrogateID = 0xd800 // 110110 00 00000000
+ private final val LowSurrogateMask = 0xfc00 // 111111 00 00000000
+ private final val LowSurrogateID = 0xdc00 // 110111 00 00000000
+ private final val SurrogateUsefulPartMask = 0x03ff // 000000 11 11111111
+
+ @inline def isHighSurrogate(c: scala.Char): scala.Boolean =
+ (c & HighSurrogateMask) == HighSurrogateID
+ @inline def isLowSurrogate(c: scala.Char): scala.Boolean =
+ (c & LowSurrogateMask) == LowSurrogateID
+ @inline def isSurrogatePair(high: scala.Char, low: scala.Char): scala.Boolean =
+ isHighSurrogate(high) && isLowSurrogate(low)
+
+ @inline def toCodePoint(high: scala.Char, low: scala.Char): scala.Int =
+ ((high & SurrogateUsefulPartMask) << 10) + (low & SurrogateUsefulPartMask) + 0x10000
+
+ // --- End of UTF-16 surrogate pairs handling ---
+
+ def isUnicodeIdentifierStart(c: scala.Char): scala.Boolean =
+ reUnicodeIdentStart.test(c.toString)
+
+ def isUnicodeIdentifierPart(c: scala.Char): scala.Boolean =
+ isUnicodeIdentifierStart(c) || isIdentifierIgnorable(c) ||
+ reUnicodeIdentPartExcl.test(c.toString)
+
+ def isIdentifierIgnorable(c: scala.Char): scala.Boolean =
+ reIdentIgnorable.test(c.toString)
+
+ //def isMirrored(c: scala.Char): scala.Boolean
+ def isLowerCase(c: scala.Char): scala.Boolean = toLowerCase(c) == c
+ def isUpperCase(c: scala.Char): scala.Boolean = toUpperCase(c) == c
+ //def isTitleCase(c: scala.Char): scala.Boolean
+ //def isJavaIdentifierPart(c: scala.Char): scala.Boolean
+
+ //def getDirectionality(c: scala.Char): scala.Byte
+
+ /* Conversions */
+ def toUpperCase(c: scala.Char): scala.Char = c.toString.toUpperCase()(0)
+ def toLowerCase(c: scala.Char): scala.Char = c.toString.toLowerCase()(0)
+ //def toTitleCase(c: scala.Char): scala.Char
+ //def getNumericValue(c: scala.Char): scala.Int
+
+ /* Misc */
+ //def reverseBytes(ch: scala.Char): scala.Char
+
+ @inline def toString(c: scala.Char) = js.String.fromCharCode(c.toInt)
+
+ @inline def compare(x: scala.Char, y: scala.Char): scala.Int =
+ x - y
+
+ // Based on Unicode 7.0.0
+ private[this] lazy val reUnicodeIdentStart =
+ new js.RegExp("""[A-Za-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B2\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA7AD\uA7B0\uA7B1\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB5F\uAB64\uAB65\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]""")
+
+ private[this] lazy val reUnicodeIdentPartExcl =
+ new js.RegExp("""[0-9\x5F\u0300-\u036F\u0483-\u0487\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u064B-\u0669\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u06F0-\u06F9\u0711\u0730-\u074A\u07A6-\u07B0\u07C0-\u07C9\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08E4-\u0903\u093A-\u093C\u093E-\u094F\u0951-\u0957\u0962\u0963\u0966-\u096F\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u09E6-\u09EF\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A66-\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0AE6-\u0AEF\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B62\u0B63\u0B66-\u0B6F\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0BE6-\u0BEF\u0C00-\u0C03\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C66-\u0C6F\u0C81-\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0CE6-\u0CEF\u0D01-\u0D03\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D66-\u0D6F\u0D82\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0E50-\u0E59\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0ED0-\u0ED9\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102B-\u103E\u1040-\u1049\u1056-\u1059\u105E-\u1060\u1062-\u1064\u1067-\u106D\u1071-\u1074\u1082-\u108D\u108F-\u109D\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4-\u17D3\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u18A9\u1920-\u192B\u1930-\u193B\u1946-\u194F\u19B0-\u19C0\u19C8\u19C9\u19D0-\u19D9\u1A17-\u1A1B\u1A55-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AB0-\u1ABD\u1B00-\u1B04\u1B34-\u1B44\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAD\u1BB0-\u1BB9\u1BE6-\u1BF3\u1C24-\u1C37\u1C40-\u1C49\u1C50-\u1C59\u1CD0-\u1CD2\u1CD4-\u1CE8\u1CED\u1CF2-\u1CF4\u1CF8\u1CF9\u1DC0-\u1DF5\u1DFC-\u1DFF\u203F\u2040\u2054\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302F\u3099\u309A\uA620-\uA629\uA66F\uA674-\uA67D\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA823-\uA827\uA880\uA881\uA8B4-\uA8C4\uA8D0-\uA8D9\uA8E0-\uA8F1\uA900-\uA909\uA926-\uA92D\uA947-\uA953\uA980-\uA983\uA9B3-\uA9C0\uA9D0-\uA9D9\uA9E5\uA9F0-\uA9F9\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uAA50-\uAA59\uAA7B-\uAA7D\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEB-\uAAEF\uAAF5\uAAF6\uABE3-\uABEA\uABEC\uABED\uABF0-\uABF9\uFB1E\uFE00-\uFE0F\uFE20-\uFE2D\uFE33\uFE34\uFE4D-\uFE4F\uFF10-\uFF19\uFF3F]""")
+
+ private[this] lazy val reIdentIgnorable =
+ new js.RegExp("""[\0-\x08\x0E-\x1B\x7F-\x9F\xAD\u0600-\u0605\u061C\u06DD\u070F\u180E\u200B-\u200F\u202A-\u202E\u2060-\u2064\u2066-\u206F\uFEFF\uFFF9-\uFFFB]""")
+
+}
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/Class.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/Class.scala
new file mode 100644
index 0000000..e8ff46f
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/Class.scala
@@ -0,0 +1,83 @@
+package java.lang
+
+import scala.scalajs.js
+
+private trait ScalaJSClassData[A] extends js.Object {
+ val name: String = js.native
+ val isPrimitive: scala.Boolean = js.native
+ val isInterface: scala.Boolean = js.native
+ val isArrayClass: scala.Boolean = js.native
+
+ def isInstance(obj: Object): scala.Boolean = js.native
+ def getFakeInstance(): Object = js.native
+
+ def getSuperclass(): Class[_ >: A] = js.native
+ def getComponentType(): Class[_] = js.native
+
+ def newArrayOfThisClass(dimensions: js.Array[Int]): AnyRef = js.native
+}
+
+final class Class[A] private (data: ScalaJSClassData[A]) extends Object {
+
+ override def toString(): String = {
+ (if (isInterface()) "interface " else
+ if (isPrimitive()) "" else "class ")+getName()
+ }
+
+ def isInstance(obj: Object): scala.Boolean =
+ data.isInstance(obj)
+
+ def isAssignableFrom(that: Class[_]): scala.Boolean =
+ if (this.isPrimitive || that.isPrimitive) {
+ /* This differs from the JVM specification to mimic the behavior of
+ * runtime type tests of primitive numeric types.
+ */
+ (this eq that) || {
+ if (this eq classOf[scala.Short])
+ (that eq classOf[scala.Byte])
+ else if (this eq classOf[scala.Int])
+ (that eq classOf[scala.Byte]) || (that eq classOf[scala.Short])
+ else if (this eq classOf[scala.Float])
+ (that eq classOf[scala.Byte]) || (that eq classOf[scala.Short]) ||
+ (that eq classOf[scala.Int])
+ else if (this eq classOf[scala.Double])
+ (that eq classOf[scala.Byte]) || (that eq classOf[scala.Short]) ||
+ (that eq classOf[scala.Int]) || (that eq classOf[scala.Float])
+ else
+ false
+ }
+ } else {
+ this.isInstance(that.getFakeInstance())
+ }
+
+ private def getFakeInstance(): Object =
+ data.getFakeInstance()
+
+ def isInterface(): scala.Boolean =
+ data.isInterface
+
+ def isArray(): scala.Boolean =
+ data.isArrayClass
+
+ def isPrimitive(): scala.Boolean =
+ data.isPrimitive
+
+ def getName(): String =
+ data.name
+
+ def getSimpleName(): String =
+ data.name.split('.').last.split('$').last
+
+ def getSuperclass(): Class[_ >: A] =
+ data.getSuperclass()
+
+ def getComponentType(): Class[_] =
+ data.getComponentType()
+
+ def getEnclosingClass(): Class[_] = null
+
+ // java.lang.reflect.Array support
+
+ private[lang] def newArrayOfThisClass(dimensions: js.Array[Int]): AnyRef =
+ data.newArrayOfThisClass(dimensions)
+}
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/Cloneable.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/Cloneable.scala
new file mode 100644
index 0000000..4183bf5
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/Cloneable.scala
@@ -0,0 +1,3 @@
+package java.lang
+
+trait Cloneable
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/Comparable.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/Comparable.scala
new file mode 100644
index 0000000..8d17c6f
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/Comparable.scala
@@ -0,0 +1,5 @@
+package java.lang
+
+trait Comparable[A] {
+ def compareTo(o: A): scala.Int
+}
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/Double.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/Double.scala
new file mode 100644
index 0000000..25987ac
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/Double.scala
@@ -0,0 +1,110 @@
+package java.lang
+
+import scala.scalajs.js
+
+/* This is a hijacked class. Its instances are primitive numbers.
+ * Constructors are not emitted.
+ */
+final class Double private () extends Number with Comparable[Double] {
+
+ def this(value: scala.Double) = this()
+ def this(s: String) = this()
+
+ @inline def doubleValue(): scala.Double =
+ this.asInstanceOf[scala.Double]
+
+ @inline override def byteValue(): scala.Byte = doubleValue.toByte
+ @inline override def shortValue(): scala.Short = doubleValue.toShort
+ @inline def intValue(): scala.Int = doubleValue.toInt
+ @inline def longValue(): scala.Long = doubleValue.toLong
+ @inline def floatValue(): scala.Float = doubleValue.toFloat
+
+ override def equals(that: Any): scala.Boolean = that match {
+ case that: Double =>
+ val a = doubleValue
+ val b = that.doubleValue
+ (a == b) || (Double.isNaN(a) && Double.isNaN(b))
+ case _ =>
+ false
+ }
+
+ @inline override def hashCode(): Int =
+ scala.scalajs.runtime.Bits.numberHashCode(doubleValue)
+
+ @inline override def compareTo(that: Double): Int =
+ Double.compare(doubleValue, that.doubleValue)
+
+ @inline override def toString(): String =
+ Double.toString(doubleValue)
+
+ @inline def isNaN(): scala.Boolean =
+ Double.isNaN(doubleValue)
+
+ @inline def isInfinite(): scala.Boolean =
+ Double.isInfinite(doubleValue)
+
+}
+
+object Double {
+ final val TYPE = classOf[scala.Double]
+ final val POSITIVE_INFINITY = 1.0 / 0.0
+ final val NEGATIVE_INFINITY = 1.0 / -0.0
+ final val NaN = 0.0 / 0.0
+ final val MAX_VALUE = scala.Double.MaxValue
+ final val MIN_VALUE = scala.Double.MinPositiveValue
+ final val MAX_EXPONENT = 1023
+ final val MIN_EXPONENT = -1022
+ final val SIZE = 64
+
+ @inline def valueOf(doubleValue: scala.Double): Double =
+ new Double(doubleValue)
+
+ @inline def valueOf(s: String): Double = valueOf(parseDouble(s))
+
+ private[this] lazy val doubleStrPat = new js.RegExp("^" +
+ "[\\x00-\\x20]*" + // optional whitespace
+ "[+-]?" + // optional sign
+ "(NaN|Infinity|" + // special cases
+ "(\\d+\\.?\\d*|" + // literal w/ leading digit
+ "\\.\\d+)" + // literal w/o leading digit
+ "([eE][+-]?\\d+)?"+ // optional exponent
+ ")[fFdD]?" + // optional float / double specifier (ignored)
+ "[\\x00-\\x20]*" + // optional whitespace
+ "$")
+
+ def parseDouble(s: String): scala.Double = {
+ if (doubleStrPat.test(s))
+ js.parseFloat(s)
+ else
+ throw new NumberFormatException(s"""For input string: "$s"""")
+ }
+
+ @inline def toString(d: scala.Double): String =
+ "" + d
+
+ def compare(a: scala.Double, b: scala.Double): scala.Int = {
+ // NaN must equal itself, and be greater than anything else
+ if (isNaN(a)) {
+ if (isNaN(b)) 0
+ else 1
+ } else if (isNaN(b)) {
+ -1
+ } else {
+ if (a == b) 0
+ else if (a < b) -1
+ else 1
+ }
+ }
+
+ @inline def isNaN(v: scala.Double): scala.Boolean =
+ v != v
+
+ @inline def isInfinite(v: scala.Double): scala.Boolean =
+ v == POSITIVE_INFINITY || v == NEGATIVE_INFINITY
+
+ @inline def longBitsToDouble(bits: scala.Long): scala.Double =
+ scala.scalajs.runtime.Bits.longBitsToDouble(bits)
+
+ @inline def doubleToLongBits(value: scala.Double): scala.Long =
+ scala.scalajs.runtime.Bits.doubleToLongBits(value)
+}
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/Float.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/Float.scala
new file mode 100644
index 0000000..70cb33e
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/Float.scala
@@ -0,0 +1,96 @@
+package java.lang
+
+/* This is a hijacked class. Its instances are primitive numbers.
+ * Constructors are not emitted.
+ */
+final class Float private () extends Number with Comparable[Float] {
+
+ def this(value: scala.Float) = this()
+ def this(s: String) = this()
+
+ @inline def floatValue(): scala.Float =
+ this.asInstanceOf[scala.Float]
+
+ @inline override def byteValue(): scala.Byte = floatValue.toByte
+ @inline override def shortValue(): scala.Short = floatValue.toShort
+ @inline def intValue(): scala.Int = floatValue.toInt
+ @inline def longValue(): scala.Long = floatValue.toLong
+ @inline def doubleValue(): scala.Double = floatValue.toDouble
+
+ override def equals(that: Any): scala.Boolean = that match {
+ case that: Double => // yes, Double
+ val a = doubleValue
+ val b = that.doubleValue
+ (a == b) || (Double.isNaN(a) && Double.isNaN(b))
+ case _ =>
+ false
+ }
+
+ // Uses the hashCode of Doubles. See Bits.numberHashCode for the rationale.
+ @inline override def hashCode(): Int =
+ scala.scalajs.runtime.Bits.numberHashCode(doubleValue)
+
+ @inline override def compareTo(that: Float): Int =
+ Float.compare(floatValue, that.floatValue)
+
+ @inline override def toString(): String =
+ Float.toString(floatValue)
+
+ @inline def isNaN(): scala.Boolean =
+ Float.isNaN(floatValue)
+
+ @inline def isInfinite(): scala.Boolean =
+ Float.isInfinite(floatValue)
+
+}
+
+object Float {
+ final val TYPE = classOf[scala.Float]
+ final val POSITIVE_INFINITY = 1.0f / 0.0f
+ final val NEGATIVE_INFINITY = 1.0f / -0.0f
+ final val NaN = 0.0f / 0.0f
+ final val MAX_VALUE = scala.Float.MaxValue
+ final val MIN_VALUE = scala.Float.MinPositiveValue
+ final val MAX_EXPONENT = 127
+ final val MIN_EXPONENT = -126
+ final val SIZE = 32
+
+ @inline def valueOf(floatValue: scala.Float): Float = new Float(floatValue)
+
+ @inline def valueOf(s: String): Float = valueOf(parseFloat(s))
+
+ @inline def parseFloat(s: String): scala.Float =
+ Double.parseDouble(s).toFloat
+
+ @inline def toString(f: scala.Float): String =
+ "" + f
+
+ def compare(a: scala.Float, b: scala.Float): scala.Int = {
+ // NaN must equal itself, and be greater than anything else
+ if (isNaN(a)) {
+ if (isNaN(b)) 0
+ else 1
+ } else if (isNaN(b)) {
+ -1
+ } else {
+ if (a == b) 0
+ else if (a < b) -1
+ else 1
+ }
+ }
+
+ @inline protected def equals(a: scala.Float, b: scala.Float): scala.Boolean =
+ a == b || (isNaN(a) && isNaN(b))
+
+ @inline def isNaN(v: scala.Float): scala.Boolean =
+ v != v
+
+ @inline def isInfinite(v: scala.Float): scala.Boolean =
+ v == POSITIVE_INFINITY || v == NEGATIVE_INFINITY
+
+ @inline def intBitsToFloat(bits: scala.Int): scala.Float =
+ scala.scalajs.runtime.Bits.intBitsToFloat(bits)
+
+ @inline def floatToIntBits(value: scala.Float): scala.Int =
+ scala.scalajs.runtime.Bits.floatToIntBits(value)
+}
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/InheritableThreadLocal.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/InheritableThreadLocal.scala
new file mode 100644
index 0000000..92ef07c
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/InheritableThreadLocal.scala
@@ -0,0 +1,5 @@
+package java.lang
+
+class InheritableThreadLocal[T] extends ThreadLocal[T] {
+ protected def childValue(parentValue: T): T = parentValue
+}
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/Integer.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/Integer.scala
new file mode 100644
index 0000000..a002fb7
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/Integer.scala
@@ -0,0 +1,129 @@
+package java.lang
+
+import scala.scalajs.js
+
+/* This is a hijacked class. Its instances are primitive numbers.
+ * Constructors are not emitted.
+ */
+final class Integer private () extends Number with Comparable[Integer] {
+
+ def this(value: scala.Int) = this()
+ def this(s: String) = this()
+
+ @inline def intValue(): scala.Int =
+ this.asInstanceOf[scala.Int]
+
+ @inline override def byteValue(): scala.Byte = intValue.toByte
+ @inline override def shortValue(): scala.Short = intValue.toShort
+ @inline def longValue(): scala.Long = intValue.toLong
+ @inline def floatValue(): scala.Float = intValue.toFloat
+ @inline def doubleValue(): scala.Double = intValue.toDouble
+
+ @inline override def equals(that: Any): scala.Boolean =
+ this eq that.asInstanceOf[AnyRef]
+
+ @inline override def hashCode(): Int =
+ intValue
+
+ @inline override def compareTo(that: Integer): Int =
+ Integer.compare(intValue, that.intValue)
+
+ @inline override def toString(): String =
+ Integer.toString(intValue)
+
+}
+
+object Integer {
+ final val TYPE = classOf[scala.Int]
+ final val MIN_VALUE = -2147483648
+ final val MAX_VALUE = 2147483647
+ final val SIZE = 32
+
+ @inline def valueOf(intValue: scala.Int): Integer = new Integer(intValue)
+ @inline def valueOf(s: String): Integer = valueOf(parseInt(s))
+
+ @inline def valueOf(s: String, radix: Int): Integer =
+ valueOf(parseInt(s, radix))
+
+ @inline def parseInt(s: String): scala.Int = parseInt(s, 10)
+
+ def parseInt(s: String, radix: scala.Int): scala.Int = {
+ def fail = throw new NumberFormatException(s"""For input string: "$s"""")
+
+ if (s == null || s.size == 0 ||
+ radix < Character.MIN_RADIX ||
+ radix > Character.MAX_RADIX)
+ fail
+ else {
+ var i = if (s(0) == '-' || s(0) == '+') 1 else 0
+ // JavaDoc says: We need at least one digit
+ if (s.size <= i) fail
+ else {
+ // Check each character for validity
+ while (i < s.size) {
+ if (Character.digit(s(i), radix) < 0) fail
+ i += 1
+ }
+ val res = js.parseInt(s, radix)
+
+ if (js.isNaN(res) || res > MAX_VALUE || res < MIN_VALUE)
+ fail
+ else
+ res.toInt
+ }
+ }
+ }
+
+ @inline def toString(i: scala.Int): String =
+ "" + i
+
+ @inline def compare(x: scala.Int, y: scala.Int): scala.Int =
+ if (x == y) 0 else if (x < y) -1 else 1
+
+ def bitCount(i: scala.Int): scala.Int = {
+ // See http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
+ // The implicit casts to 32-bit ints due to binary ops make this work in JS too
+ val t1 = i - ((i >> 1) & 0x55555555)
+ val t2 = (t1 & 0x33333333) + ((t1 >> 2) & 0x33333333)
+ ((t2 + (t2 >> 4) & 0xF0F0F0F) * 0x1010101) >> 24
+ }
+
+ def reverseBytes(i: scala.Int): scala.Int = {
+ val byte3 = i >>> 24
+ val byte2 = (i >>> 8) & 0xFF00
+ val byte1 = (i << 8) & 0xFF0000
+ val byte0 = (i << 24)
+ byte0 | byte1 | byte2 | byte3
+ }
+
+ def rotateLeft(i: scala.Int, distance: scala.Int): scala.Int =
+ (i << distance) | (i >>> -distance)
+
+ def rotateRight(i: scala.Int, distance: scala.Int): scala.Int =
+ (i >>> distance) | (i << -distance)
+
+ @inline def signum(i: scala.Int): scala.Int =
+ if (i == 0) 0 else if (i < 0) -1 else 1
+
+ def numberOfLeadingZeros(i: scala.Int): scala.Int = {
+ // See http://aggregate.org/MAGIC/#Leading%20Zero%20Count
+ var x = i
+ x |= (x >>> 1)
+ x |= (x >>> 2)
+ x |= (x >>> 4)
+ x |= (x >>> 8)
+ x |= (x >>> 16)
+ 32 - bitCount(x)
+ }
+
+ def numberOfTrailingZeros(i: scala.Int): scala.Int =
+ // See http://aggregate.org/MAGIC/#Trailing%20Zero%20Count
+ bitCount((i & -i) - 1)
+
+ def toBinaryString(i: scala.Int): String = toStringBase(i, 2)
+ def toHexString(i: scala.Int): String = toStringBase(i, 16)
+ def toOctalString(i: scala.Int): String = toStringBase(i, 8)
+
+ @inline private[this] def toStringBase(i: scala.Int, base: scala.Int): String =
+ ((i: js.prim.Number) >>> 0).toString(base)
+}
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/Long.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/Long.scala
new file mode 100644
index 0000000..beeef32
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/Long.scala
@@ -0,0 +1,196 @@
+package java.lang
+
+import scala.annotation.tailrec
+
+import scala.scalajs.js
+
+/* This is a hijacked class. Its instances are the representation of scala.Longs.
+ * Constructors are not emitted.
+ */
+final class Long private () extends Number with Comparable[Long] {
+ def this(value: scala.Long) = this()
+ def this(s: String) = this()
+
+ @inline def longValue(): scala.Long =
+ this.asInstanceOf[scala.Long]
+
+ @inline override def byteValue(): scala.Byte = longValue.toByte
+ @inline override def shortValue(): scala.Short = longValue.toShort
+ @inline def intValue(): scala.Int = longValue.toInt
+ @inline def floatValue(): scala.Float = longValue.toFloat
+ @inline def doubleValue(): scala.Double = longValue.toDouble
+
+ @inline override def equals(that: Any): scala.Boolean = that match {
+ case that: Long => longValue == that.longValue
+ case _ => false
+ }
+
+ @inline override def hashCode(): Int =
+ (longValue ^ (longValue >>> 32)).toInt
+
+ @inline override def compareTo(that: Long): Int =
+ Long.compare(longValue, that.longValue)
+
+ @inline override def toString(): String =
+ Long.toString(longValue)
+
+}
+
+object Long {
+ import scala.scalajs.runtime.RuntimeLong
+
+ final val TYPE = classOf[scala.Long]
+ final val MIN_VALUE = -9223372036854775808L
+ final val MAX_VALUE = 9223372036854775807L
+ final val SIZE = 64
+
+ @inline def valueOf(longValue: scala.Long): Long = new Long(longValue)
+ @inline def valueOf(s: String): Long = valueOf(parseLong(s))
+
+ @inline def valueOf(s: String, radix: Int): Long =
+ valueOf(parseLong(s, radix))
+
+ @inline def parseLong(s: String): scala.Long =
+ parseLong(s, 10)
+
+ def parseLong(s: String, radix: Int): scala.Long = {
+ def fail() = throw new NumberFormatException(s"""For input string: "$s"""")
+
+ if (s.isEmpty) {
+ fail()
+ } else if (s.charAt(0) == '-') {
+ -parseLong(s.substring(1), radix)
+ } else {
+ @inline
+ @tailrec
+ def fastPow(base: Int, exp: Int, acc: Int = 1): Int =
+ if (exp == 0) acc
+ else if (exp % 2 == 0) fastPow(base*base, exp/2, acc)
+ else fastPow(base, exp-1, acc*base)
+
+ @inline
+ @tailrec
+ def loop(str0: String, acc: scala.Long): scala.Long = if (str0.length > 0) {
+ val MaxLen = 9
+ val cur = (str0: js.prim.String).substring(0, MaxLen): String
+ val macc = acc * fastPow(radix, cur.length)
+ val ival = js.parseInt(cur, radix): scala.Double
+ if (ival.isNaN)
+ fail()
+ val cval = ival.toInt.toLong // faster than ival.toLong
+ loop((str0: js.prim.String).substring(MaxLen), macc + cval)
+ } else acc
+
+ loop(s, 0L)
+ }
+ }
+
+ def toString(l: scala.Long): String = {
+ if (l == 0L) "0"
+ // Check for MinValue, because it is not negatable
+ else if (l == MIN_VALUE) "-9223372036854775808"
+ else if (l < 0L) "-" + toString(-l)
+ else {
+ @tailrec
+ @inline
+ def toString0(v: scala.Long, acc: String): String = {
+ val quot = v / 1000000000L // 9 zeros
+ val rem = v % 1000000000L
+
+ val digits = rem.toInt.toString
+
+ if (quot == 0L) {
+ digits + acc
+ } else {
+ val padding = "000000000".substring(digits.length) // (9 - digits.length) zeros
+ toString0(quot, padding + digits + acc)
+ }
+ }
+
+ toString0(l, "")
+ }
+ }
+
+ @inline def compare(x: scala.Long, y: scala.Long): scala.Int =
+ if (x == y) 0 else if (x < y) -1 else 1
+
+ def bitCount(i: scala.Long): scala.Int = {
+ val lo = i.toInt
+ val hi = (i >>> 32).toInt
+ Integer.bitCount(lo) + Integer.bitCount(hi)
+ }
+
+ def reverseBytes(i: scala.Long): scala.Long = {
+ val hiReversed = Integer.reverseBytes((i >>> 32).toInt)
+ val loReversed = Integer.reverseBytes(i.toInt)
+ (loReversed.toLong << 32) | (hiReversed.toLong & 0xffffffffL)
+ }
+
+ def rotateLeft(i: scala.Long, distance: scala.Int): scala.Long =
+ (i << distance) | (i >>> -distance)
+
+ def rotateRight(i: scala.Long, distance: scala.Int): scala.Long =
+ (i >>> distance) | (i << -distance)
+
+ def signum(i: scala.Long): scala.Long =
+ if (i < 0L) -1L else if (i == 0L) 0L else 1L
+
+ def numberOfLeadingZeros(l: scala.Long): Int = {
+ val hi = (l >>> 32).toInt
+ if (hi != 0) Integer.numberOfLeadingZeros(hi)
+ else Integer.numberOfLeadingZeros(l.toInt) + 32
+ }
+
+ def numberOfTrailingZeros(l: scala.Long): Int = {
+ val lo = l.toInt
+ if (lo != 0) Integer.numberOfTrailingZeros(lo)
+ else Integer.numberOfTrailingZeros((l >>> 32).toInt) + 32
+ }
+
+ def toBinaryString(l: scala.Long): String = {
+ val zeros = "00000000000000000000000000000000" // 32 zeros
+ @inline def padBinary32(i: Int) = {
+ val s = Integer.toBinaryString(i)
+ zeros.substring(s.length) + s
+ }
+
+ val lo = l.toInt
+ val hi = (l >>> 32).toInt
+
+ if (hi != 0) Integer.toBinaryString(hi) + padBinary32(lo)
+ else Integer.toBinaryString(lo)
+ }
+
+ def toHexString(l: scala.Long): String = {
+ val zeros = "00000000" // 8 zeros
+ @inline def padBinary8(i: Int) = {
+ val s = Integer.toHexString(i)
+ zeros.substring(s.length) + s
+ }
+
+ val lo = l.toInt
+ val hi = (l >>> 32).toInt
+
+ if (hi != 0) Integer.toHexString(hi) + padBinary8(lo)
+ else Integer.toHexString(lo)
+ }
+
+ def toOctalString(l: scala.Long): String = {
+ val zeros = "0000000000" // 10 zeros
+ @inline def padOctal10(i: Int) = {
+ val s = Integer.toOctalString(i)
+ zeros.substring(s.length) + s
+ }
+
+ val lo = l.toInt
+ val hi = (l >>> 32).toInt
+
+ val lp = lo & 0x3fffffff
+ val mp = ((lo >>> 30) + (hi << 2)) & 0x3fffffff
+ val hp = hi >>> 28
+
+ if (hp != 0) Integer.toOctalString(hp) + padOctal10(mp) + padOctal10(lp)
+ else if (mp != 0) Integer.toOctalString(mp) + padOctal10(lp)
+ else Integer.toOctalString(lp)
+ }
+}
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/Math.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/Math.scala
new file mode 100644
index 0000000..c8cd7aa
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/Math.scala
@@ -0,0 +1,211 @@
+package java
+package lang
+
+import scala.scalajs.js
+
+object Math {
+ private lazy val internalRandom = new java.util.Random()
+
+ final val E = 2.718281828459045
+ final val PI = 3.141592653589793
+
+ @inline def abs(a: scala.Int): scala.Int = if (a < 0) -a else a
+ @inline def abs(a: scala.Long): scala.Long = if (a < 0) -a else a
+ @inline def abs(a: scala.Float): scala.Float = if (a < 0) -a else a
+ @inline def abs(a: scala.Double): scala.Double = if (a < 0) -a else a
+
+ @inline def max(a: scala.Int, b: scala.Int): scala.Int = if (a > b) a else b
+ @inline def max(a: scala.Long, b: scala.Long): scala.Long = if (a > b) a else b
+ @inline def max(a: scala.Float, b: scala.Float): scala.Float = if (a > b) a else b
+ @inline def max(a: scala.Double, b: scala.Double): scala.Double = if (a > b) a else b
+
+ @inline def min(a: scala.Int, b: scala.Int): scala.Int = if (a < b) a else b
+ @inline def min(a: scala.Long, b: scala.Long): scala.Long = if (a < b) a else b
+ @inline def min(a: scala.Float, b: scala.Float): scala.Float = if (a < b) a else b
+ @inline def min(a: scala.Double, b: scala.Double): scala.Double = if (a < b) a else b
+
+ @inline def ceil(a: scala.Double): scala.Double = js.Math.ceil(a)
+ @inline def floor(a: scala.Double): scala.Double = js.Math.floor(a)
+
+ @inline def round(a: scala.Float): scala.Int = js.Math.round(a).toInt
+ @inline def round(a: scala.Double): scala.Long = js.Math.round(a).toLong
+
+ @inline def sqrt(a: scala.Double): scala.Double = js.Math.sqrt(a)
+ @inline def pow(a: scala.Double, b: scala.Double): scala.Double = js.Math.pow(a, b)
+
+ @inline def exp(a: scala.Double): scala.Double = js.Math.exp(a)
+ @inline def log(a: scala.Double): scala.Double = js.Math.log(a)
+ @inline def log10(a: scala.Double): scala.Double = log(a) / 2.302585092994046
+ @inline def log1p(a: scala.Double): scala.Double = log(a + 1)
+
+ @inline def sin(a: scala.Double): scala.Double = js.Math.sin(a)
+ @inline def cos(a: scala.Double): scala.Double = js.Math.cos(a)
+ @inline def tan(a: scala.Double): scala.Double = js.Math.tan(a)
+ @inline def asin(a: scala.Double): scala.Double = js.Math.asin(a)
+ @inline def acos(a: scala.Double): scala.Double = js.Math.acos(a)
+ @inline def atan(a: scala.Double): scala.Double = js.Math.atan(a)
+ @inline def atan2(y: scala.Double, x: scala.Double): scala.Double = js.Math.atan2(y, x)
+
+ def random(): scala.Double = internalRandom.nextDouble()
+
+ @inline def toDegrees(a: scala.Double): scala.Double = a * 180.0 / PI
+ @inline def toRadians(a: scala.Double): scala.Double = a / 180.0 * PI
+
+ @inline def signum(a: scala.Double): scala.Double = {
+ if (a > 0) 1.0
+ else if (a < 0) -1.0
+ else a
+ }
+
+ @inline def signum(a: scala.Float): scala.Float = {
+ if (a > 0) 1.0f
+ else if (a < 0) -1.0f
+ else a
+ }
+
+ def cbrt(a: scala.Double): scala.Double = {
+ if (a == 0 || a.isNaN)
+ return a
+
+ val sign = if (a < 0.0) -1.0 else 1.0
+ val value = sign * a
+
+ //Initial Approximation
+ var x = 0.0
+ var xi = pow(value, 0.3333333333333333)
+
+ //Halley's Method (http://metamerist.com/cbrt/cbrt.htm)
+ while (abs(x - xi) >= 1E-16) {
+ x = xi
+ val x3 = js.Math.pow(x, 3)
+ val x3Plusa = x3 + value
+ xi = x * (x3Plusa + value) / (x3Plusa + x3)
+ }
+ return sign * xi
+ }
+
+ def nextUp(a: scala.Double): scala.Double = {
+ // js implementation of nextUp https://gist.github.com/Yaffle/4654250
+ import scala.Double._
+ if (a != a || a == PositiveInfinity)
+ a
+ else if (a == NegativeInfinity)
+ -MaxValue
+ else if (a == MaxValue)
+ PositiveInfinity
+ else if (a == 0)
+ MinValue
+ else {
+ def iter(x: scala.Double, xi: scala.Double, n: scala.Double): scala.Double = {
+ if (Math.abs(xi - x) >= 1E-16) {
+ val c0 = (xi + x) / 2
+ val c =
+ if (c0 == NegativeInfinity || c0 == PositiveInfinity)
+ x + (xi - x) / 2
+ else
+ c0
+ if (n == c) xi
+ else if (a < c) iter(x = x, xi = c, n = c)
+ else iter(x = c, xi = xi, n = c)
+ }
+ else xi
+ }
+ val d = Math.max(Math.abs(a) * 2E-16, MinValue)
+ val ad = a + d
+ val xi0 =
+ if (ad == PositiveInfinity) MaxValue
+ else ad
+ iter(x = a, xi = xi0, n = a)
+ }
+ }
+
+ def nextAfter(a: scala.Double, b: scala.Double): scala.Double = {
+ if (b < a)
+ -nextUp(-a)
+ else if (a < b)
+ nextUp(a)
+ else if (a != a || b != b)
+ scala.Double.NaN
+ else
+ b
+ }
+
+ def ulp(a: scala.Double): scala.Double = {
+ if (abs(a) == scala.Double.PositiveInfinity)
+ scala.Double.PositiveInfinity
+ else if (abs(a) == scala.Double.MaxValue)
+ pow(2, 971)
+ else
+ nextAfter(abs(a), scala.Double.MaxValue) - a
+ }
+
+ def hypot(a: scala.Double, b: scala.Double): scala.Double = {
+ // http://en.wikipedia.org/wiki/Hypot#Implementation
+ if (abs(a) == scala.Double.PositiveInfinity || abs(b) == scala.Double.PositiveInfinity)
+ scala.Double.PositiveInfinity
+ else if (a.isNaN || b.isNaN)
+ scala.Double.NaN
+ else if (a == 0 && b == 0)
+ 0.0
+ else {
+ //To Avoid Overflow and UnderFlow
+ // calculate |x| * sqrt(1 - (y/x)^2) instead of sqrt(x^2 + y^2)
+ val x = abs(a)
+ val y = abs(b)
+ val m = max(x, y)
+ val t = min(x, y) / m
+ m * sqrt(1 + t * t)
+ }
+ }
+
+ def expm1(a: scala.Double): scala.Double = {
+ // https://github.com/ghewgill/picomath/blob/master/javascript/expm1.js
+ if (a == 0 || a.isNaN)
+ a
+ // Power Series http://en.wikipedia.org/wiki/Power_series
+ // for small values of a, exp(a) = 1 + a + (a*a)/2
+ else if (abs(a) < 1E-5)
+ a + 0.5 * a * a
+ else
+ exp(a) - 1.0
+ }
+
+ def sinh(a: scala.Double): scala.Double = {
+ if (a.isNaN || a == 0.0 || abs(a) == scala.Double.PositiveInfinity)
+ a
+ else
+ (exp(a) - exp(-a)) / 2.0
+ }
+
+ def cosh(a: scala.Double): scala.Double = {
+ if (a.isNaN)
+ a
+ else if (a == 0.0)
+ 1.0
+ else if (abs(a) == scala.Double.PositiveInfinity)
+ scala.Double.PositiveInfinity
+ else
+ (exp(a) + exp(-a)) / 2.0
+ }
+
+ def tanh(a: scala.Double): scala.Double = {
+ if (a.isNaN || a == 0.0)
+ a
+ else if (abs(a) == scala.Double.PositiveInfinity)
+ signum(a)
+ else {
+ // sinh(a) / cosh(a) =
+ // 1 - 2 * (exp(-a)/ (exp(-a) + exp (a)))
+ val expma = exp(-a)
+ if (expma == scala.Double.PositiveInfinity) //Infinity / Infinity
+ -1.0
+ else {
+ val expa = exp(a)
+ val ret = expma / (expa + expma)
+ 1.0 - (2.0 * ret)
+ }
+ }
+ }
+
+ // TODO The methods not available in the JavaScript Math object
+}
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/Number.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/Number.scala
new file mode 100644
index 0000000..05ffc7a
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/Number.scala
@@ -0,0 +1,12 @@
+package java.lang
+
+import scala.scalajs.js
+
+abstract class Number extends Object {
+ def byteValue(): scala.Byte = intValue.toByte
+ def shortValue(): scala.Short = intValue.toShort
+ def intValue(): scala.Int
+ def longValue(): scala.Long
+ def floatValue(): scala.Float
+ def doubleValue(): scala.Double
+}
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/Readable.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/Readable.scala
new file mode 100644
index 0000000..53e5689
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/Readable.scala
@@ -0,0 +1,7 @@
+package java.lang
+
+import java.nio.CharBuffer
+
+trait Readable {
+ def read(cb: CharBuffer): Int
+}
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/Runnable.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/Runnable.scala
new file mode 100644
index 0000000..c98cb41
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/Runnable.scala
@@ -0,0 +1,5 @@
+package java.lang
+
+trait Runnable {
+ def run(): Unit
+}
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/Runtime.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/Runtime.scala
new file mode 100644
index 0000000..25aaa9f
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/Runtime.scala
@@ -0,0 +1,47 @@
+package java.lang
+
+import scala.scalajs.js
+
+class Runtime private {
+ def exit(status: Int): Unit =
+ halt(status)
+
+ //def addShutdownHook(hook: Thread): Unit
+ //def removeShutdownHook(hook: Thread): Unit
+
+ def halt(status: Int): Unit = {
+ val envInfo = scala.scalajs.runtime.environmentInfo
+
+ if (js.typeOf(envInfo.exitFunction) == "function") {
+ envInfo.exitFunction(status)
+ throw new IllegalStateException("__ScalaJSEnv.exitFunction returned")
+ } else {
+ // We don't have an exit function. Fail
+ throw new SecurityException("Cannot terminate a JavaScript program. " +
+ "Define a JavaScript function `__ScalaJSEnv.exitFunction` to " +
+ "be called on exit.")
+ }
+ }
+
+ def availableProcessors(): Int = 1
+ //def freeMemory(): scala.Long
+ //def totalMemory(): scala.Long
+ //def maxMemory(): scala.Long
+
+ def gc(): Unit = {
+ // Ignore
+ }
+
+ //def runFinalization(): Unit
+ //def traceInstructions(on: scala.Boolean): Unit
+ //def traceMethodCalls(on: scala.Boolean): Unit
+
+ //def load(filename: String): Unit
+ //def loadLibrary(filename: String): Unit
+}
+
+object Runtime {
+ private val currentRuntime = new Runtime
+
+ def getRuntime() = currentRuntime
+}
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/Short.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/Short.scala
new file mode 100644
index 0000000..135fe12
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/Short.scala
@@ -0,0 +1,73 @@
+package java.lang
+
+/* This is a hijacked class. Its instances are primitive numbers.
+ * Constructors are not emitted.
+ */
+final class Short private () extends Number with Comparable[Short] {
+
+ def this(value: scala.Short) = this()
+ def this(s: String) = this()
+
+ @inline override def shortValue(): scala.Short =
+ this.asInstanceOf[scala.Short]
+
+ @inline override def byteValue(): scala.Byte = shortValue.toByte
+ @inline def intValue(): scala.Int = shortValue.toInt
+ @inline def longValue(): scala.Long = shortValue.toLong
+ @inline def floatValue(): scala.Float = shortValue.toFloat
+ @inline def doubleValue(): scala.Double = shortValue.toDouble
+
+ @inline override def equals(that: Any): scala.Boolean =
+ this eq that.asInstanceOf[AnyRef]
+
+ @inline override def hashCode(): Int =
+ shortValue
+
+ @inline override def compareTo(that: Short): Int =
+ Short.compare(shortValue, that.shortValue)
+
+ @inline override def toString(): String =
+ Short.toString(shortValue)
+
+}
+
+object Short {
+ final val TYPE = classOf[scala.Short]
+ final val SIZE = 16
+
+ /* MIN_VALUE and MAX_VALUE should be 'final val's. But it is impossible to
+ * write a proper Short literal in Scala, that would both considered a Short
+ * and a constant expression (optimized as final val).
+ * Since vals and defs are binary-compatible (although they're not strictly
+ * speaking source-compatible, because of stability), we implement them as
+ * defs. Source-compatibility is not an issue because user code is compiled
+ * against the JDK .class files anyway.
+ */
+ def MIN_VALUE: scala.Short = -32768
+ def MAX_VALUE: scala.Short = 32767
+
+ @inline def valueOf(shortValue: scala.Short): Short = new Short(shortValue)
+ @inline def valueOf(s: String): Short = valueOf(parseShort(s))
+
+ @inline def valueOf(s: String, radix: Int): Short =
+ valueOf(parseShort(s, radix))
+
+ @inline def parseShort(s: String): scala.Short = parseShort(s, 10)
+
+ def parseShort(s: String, radix: Int): scala.Short = {
+ val r = Integer.parseInt(s, radix)
+ if (r < MIN_VALUE || r > MAX_VALUE)
+ throw new NumberFormatException(s"""For input string: "$s"""")
+ else
+ r.toShort
+ }
+
+ @inline def toString(s: scala.Short): String =
+ "" + s
+
+ @inline def compare(x: scala.Short, y: scala.Short): scala.Int =
+ x - y
+
+ def reverseBytes(i: scala.Short): scala.Short =
+ (((i >>> 8) & 0xff) + ((i & 0xff) << 8)).toShort
+}
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/StackTraceElement.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/StackTraceElement.scala
new file mode 100644
index 0000000..cc87aec
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/StackTraceElement.scala
@@ -0,0 +1,55 @@
+package java.lang
+
+import scala.scalajs.js
+
+final class StackTraceElement(declaringClass: String, methodName: String,
+ fileName: String, lineNumber: Int) extends AnyRef with java.io.Serializable {
+
+ def getFileName(): String = fileName
+ def getLineNumber(): Int = lineNumber
+ def getClassName(): String = declaringClass
+ def getMethodName(): String = methodName
+ def isNativeMethod(): scala.Boolean = false
+
+ override def equals(that: Any): scala.Boolean = that match {
+ case that: StackTraceElement =>
+ (getFileName == that.getFileName) &&
+ (getLineNumber == that.getLineNumber) &&
+ (getClassName == that.getClassName) &&
+ (getMethodName == that.getMethodName)
+ case _ =>
+ false
+ }
+
+ override def toString(): String = {
+ var result = ""
+ if (declaringClass != "<jscode>")
+ result += declaringClass + "."
+ result += methodName
+ if (fileName eq null) {
+ if (isNativeMethod)
+ result += "(Native Method)"
+ else
+ result += "(Unknown Source)"
+ } else {
+ result += s"($fileName"
+ if (lineNumber >= 0) {
+ result += s":$lineNumber"
+ if (columnNumber >= 0)
+ result += s":$columnNumber"
+ }
+ result += ")"
+ }
+ result
+ }
+
+ override def hashCode(): Int = {
+ declaringClass.hashCode() ^ methodName.hashCode()
+ }
+
+ private def columnNumber: Int = {
+ val rawNum = this.asInstanceOf[js.Dynamic].columnNumber
+ if (!(!rawNum)) rawNum.asInstanceOf[Int]
+ else -1
+ }
+}
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/StringBuffer.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/StringBuffer.scala
new file mode 100644
index 0000000..31ee89a
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/StringBuffer.scala
@@ -0,0 +1,159 @@
+package java.lang
+
+class StringBuffer(private var content: String) extends CharSequence
+ with Appendable
+ with java.io.Serializable {
+ def this() = this("")
+ def this(initialCapacity: Int) = this("")
+ def this(csq: CharSequence) = this(csq.toString)
+
+ def append(s: String): StringBuffer = {
+ content += { if (s == null) "null" else s }
+ this
+ }
+
+ def append(b: scala.Boolean): StringBuffer = append(b.toString())
+ def append(c: scala.Char): StringBuffer = append(c.toString())
+
+ def append(str: Array[scala.Char]): StringBuffer =
+ append(str, 0, str.length)
+
+ def append(str: Array[scala.Char], offset: Int, len: Int): StringBuffer = {
+ var i = 0
+ while (i < len) {
+ content += str(i + offset)
+ i += 1
+ }
+ this
+ }
+
+ def append(b: scala.Byte): StringBuffer = append(b.toString())
+ def append(s: scala.Short): StringBuffer = append(s.toString())
+ def append(i: scala.Int): StringBuffer = append(i.toString())
+ def append(lng: scala.Long): StringBuffer = append(lng.toString())
+ def append(f: scala.Float): StringBuffer = append(f.toString())
+ def append(d: scala.Double): StringBuffer = append(d.toString())
+
+ def append(obj: AnyRef): StringBuffer = {
+ if (obj == null) append(null: String)
+ else append(obj.toString())
+ }
+
+ def append(csq: CharSequence): StringBuffer = append(csq: AnyRef)
+ def append(csq: CharSequence, start: Int, end: Int): StringBuffer = {
+ if (csq == null) append("null", start, end)
+ else append(csq.subSequence(start, end).toString())
+ }
+
+ override def toString() = content
+
+ def length() = content.length()
+
+ def charAt(index: Int) = content.charAt(index)
+ def codePointAt(index: Int) = content.codePointAt(index)
+
+ def indexOf(str: String) = content.indexOf(str)
+ def indexOf(str: String, fromIndex: Int) = content.indexOf(str, fromIndex)
+
+ def lastIndexOf(str: String) = content.lastIndexOf(str)
+ def lastIndexOf(str: String, fromIndex: Int) = content.lastIndexOf(str, fromIndex)
+
+ def subSequence(start: Int, end: Int): CharSequence = substring(start, end)
+ def substring(start: Int): String = content.substring(start)
+ def substring(start: Int, end: Int): String = content.substring(start, end)
+
+ def reverse(): StringBuffer = {
+ content = new StringBuilder(content).reverse().toString()
+ this
+ }
+
+ def deleteCharAt(index: Int): StringBuffer = {
+ if (index < 0 || index >= content.length)
+ throw new StringIndexOutOfBoundsException("String index out of range: " + index)
+ content = content.substring(0, index) + content.substring(index+1)
+ this
+ }
+
+ /**
+ * @param start The beginning index, inclusive.
+ * @param end The ending index, exclusive.
+ * @param str String that will replace previous contents.
+ * @return This StringBuilder.
+ */
+ def replace(start: Int, end: Int, str: String): StringBuffer = {
+ val length = content.length
+ if (start < 0 || start > end || start >= length)
+ throw new StringIndexOutOfBoundsException(s"Illegal to replace substring at [$start - $end] in string of length $length")
+ val realEnd = if (end > length) length else end // java api convention
+ content = content.substring(0, start) + str + content.substring(realEnd)
+ this
+ }
+
+ def setCharAt(index: Int, ch: scala.Char): Unit = {
+ if (index < 0 || index >= content.length)
+ throw new IndexOutOfBoundsException("String index out of range: " + index)
+ content = content.substring(0, index) + ch + content.substring(index + 1)
+ }
+
+ def setLength(newLength: Int): Unit = {
+ if (newLength < 0)
+ throw new IndexOutOfBoundsException("String index out of range: " + newLength)
+
+ val len = length()
+ if (len == newLength) {
+ } else if (len < newLength) {
+ var index = len
+ while (index < newLength) {
+ append("\u0000")
+ index += 1
+ }
+ } else {
+ content = substring(0, newLength)
+ }
+ }
+
+ def insert(index: Int, b: scala.Boolean): StringBuffer = insert(index, b.toString)
+ def insert(index: Int, b: scala.Byte): StringBuffer = insert(index, b.toString)
+ def insert(index: Int, s: scala.Short): StringBuffer = insert(index, s.toString)
+ def insert(index: Int, i: scala.Int): StringBuffer = insert(index, i.toString)
+ def insert(index: Int, l: scala.Long): StringBuffer = insert(index, l.toString)
+ def insert(index: Int, f: scala.Float): StringBuffer = insert(index, f.toString)
+ def insert(index: Int, d: scala.Double): StringBuffer = insert(index, d.toString)
+ def insert(index: Int, c: scala.Char): StringBuffer = insert(index, c.toString)
+ def insert(index: Int, csq: CharSequence): StringBuffer = insert(index: Int, csq: AnyRef)
+ def insert(index: Int, arr: Array[scala.Char]): StringBuffer = insert(index, arr, 0, arr.length)
+
+ def insert(index: Int, ref: AnyRef): StringBuffer =
+ if (ref == null)
+ insert(index, null: String)
+ else
+ insert(index, ref.toString)
+
+ def insert(index: Int, csq: CharSequence, start: Int, end: Int): StringBuffer =
+ if (csq == null)
+ insert(index, "null", start, end)
+ else
+ insert(index, csq.subSequence(start, end).toString)
+
+
+ def insert(index: Int, arr: Array[scala.Char], offset: Int, len: Int): StringBuffer = {
+ var str = ""
+ var i = 0
+ while (i < len) {
+ str += arr(i + offset)
+ i += 1
+ }
+ insert(index, str)
+ }
+
+ def insert(index: Int, str: String): StringBuffer = {
+ val thisLength = length()
+ if (index < 0 || index > thisLength)
+ throw new StringIndexOutOfBoundsException(index)
+ else if (index == thisLength)
+ append(str)
+ else
+ content = content.substring(0, index) + Option(str).getOrElse("null") + content.substring(index)
+ this
+ }
+}
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/StringBuilder.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/StringBuilder.scala
new file mode 100644
index 0000000..e8bd2b7
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/StringBuilder.scala
@@ -0,0 +1,178 @@
+package java.lang
+
+class StringBuilder(private var content: String) extends CharSequence
+ with Appendable
+ with java.io.Serializable {
+ def this() = this("")
+ def this(initialCapacity: Int) = this("")
+ def this(csq: CharSequence) = this(csq.toString)
+
+ def append(s: String): StringBuilder = {
+ content += { if (s == null) "null" else s }
+ this
+ }
+
+ def append(b: scala.Boolean): StringBuilder = append(b.toString())
+ def append(c: scala.Char): StringBuilder = append(c.toString())
+
+ def append(str: Array[scala.Char]): StringBuilder =
+ append(str, 0, str.length)
+
+ def append(str: Array[scala.Char], offset: Int, len: Int): StringBuilder = {
+ var i = 0
+ while (i < len) {
+ content += str(i + offset)
+ i += 1
+ }
+ this
+ }
+
+ def append(b: scala.Byte): StringBuilder = append(b.toString())
+ def append(s: scala.Short): StringBuilder = append(s.toString())
+ def append(i: scala.Int): StringBuilder = append(i.toString())
+ def append(lng: scala.Long): StringBuilder = append(lng.toString())
+ def append(f: scala.Float): StringBuilder = append(f.toString())
+ def append(d: scala.Double): StringBuilder = append(d.toString())
+
+ def append(obj: AnyRef): StringBuilder = {
+ if (obj == null) append(null: String)
+ else append(obj.toString())
+ }
+
+ def append(csq: CharSequence): StringBuilder = append(csq: AnyRef)
+ def append(csq: CharSequence, start: Int, end: Int): StringBuilder = {
+ if (csq == null) append("null", start, end)
+ else append(csq.subSequence(start, end).toString())
+ }
+
+ override def toString() = content
+
+ def length() = content.length()
+
+ def charAt(index: Int) = content.charAt(index)
+ def codePointAt(index: Int) = content.codePointAt(index)
+
+ def indexOf(str: String) = content.indexOf(str)
+ def indexOf(str: String, fromIndex: Int) = content.indexOf(str, fromIndex)
+
+ def lastIndexOf(str: String) = content.lastIndexOf(str)
+ def lastIndexOf(str: String, fromIndex: Int) = content.lastIndexOf(str, fromIndex)
+
+ def subSequence(start: Int, end: Int): CharSequence = substring(start, end)
+ def substring(start: Int): String = content.substring(start)
+ def substring(start: Int, end: Int): String = content.substring(start, end)
+
+ def reverse(): StringBuilder = {
+ val original = content
+ var result = ""
+ var i = 0
+ while (i < original.length) {
+ val c = original.charAt(i)
+ if (Character.isHighSurrogate(c) && (i+1 < original.length)) {
+ val c2 = original.charAt(i+1)
+ if (Character.isLowSurrogate(c2)) {
+ result = c.toString + c2.toString + result
+ i += 2
+ } else {
+ result = c.toString + result
+ i += 1
+ }
+ } else {
+ result = c.toString + result
+ i += 1
+ }
+ }
+ content = result
+ this
+ }
+
+ def deleteCharAt(index: Int): StringBuilder = {
+ if (index < 0 || index >= content.length)
+ throw new StringIndexOutOfBoundsException("String index out of range: " + index)
+ content = content.substring(0, index) + content.substring(index+1)
+ this
+ }
+
+ /**
+ * @param start The beginning index, inclusive.
+ * @param end The ending index, exclusive.
+ * @param str String that will replace previous contents.
+ * @return This StringBuilder.
+ */
+ def replace(start: Int, end: Int, str: String): StringBuilder = {
+ val length = content.length
+ if (start < 0 || start > end || start >= length)
+ throw new StringIndexOutOfBoundsException(s"Illegal to replace substring at [$start - $end] in string of length $length")
+ val realEnd = if (end > length) length else end // java api convention
+ content = content.substring(0, start) + str + content.substring(realEnd)
+ this
+ }
+
+ def setCharAt(index: Int, ch: scala.Char): Unit = {
+ if (index < 0 || index >= content.length)
+ throw new IndexOutOfBoundsException("String index out of range: " + index)
+ content = content.substring(0, index) + ch + content.substring(index + 1)
+ }
+
+ def setLength(newLength: Int): Unit = {
+ if (newLength < 0)
+ throw new IndexOutOfBoundsException("String index out of range: " + newLength)
+
+ val len = length()
+ if (len == newLength) {
+ } else if (len < newLength) {
+ var index = len
+ while (index < newLength) {
+ append("\u0000")
+ index += 1
+ }
+ } else {
+ content = substring(0, newLength)
+ }
+ }
+
+ def insert(index: Int, b: scala.Boolean): StringBuilder = insert(index, b.toString)
+ def insert(index: Int, b: scala.Byte): StringBuilder = insert(index, b.toString)
+ def insert(index: Int, s: scala.Short): StringBuilder = insert(index, s.toString)
+ def insert(index: Int, i: scala.Int): StringBuilder = insert(index, i.toString)
+ def insert(index: Int, l: scala.Long): StringBuilder = insert(index, l.toString)
+ def insert(index: Int, f: scala.Float): StringBuilder = insert(index, f.toString)
+ def insert(index: Int, d: scala.Double): StringBuilder = insert(index, d.toString)
+ def insert(index: Int, c: scala.Char): StringBuilder = insert(index, c.toString)
+ def insert(index: Int, csq: CharSequence): StringBuilder = insert(index: Int, csq: AnyRef)
+ def insert(index: Int, arr: Array[scala.Char]): StringBuilder = insert(index, arr, 0, arr.length)
+
+ def insert(index: Int, ref: AnyRef): StringBuilder =
+ if (ref == null)
+ insert(index, null: String)
+ else
+ insert(index, ref.toString)
+
+ def insert(index: Int, csq: CharSequence, start: Int, end: Int): StringBuilder =
+ if (csq == null)
+ insert(index, "null", start, end)
+ else
+ insert(index, csq.subSequence(start, end).toString)
+
+
+ def insert(index: Int, arr: Array[scala.Char], offset: Int, len: Int): StringBuilder = {
+ var str = ""
+ var i = 0
+ while (i < len) {
+ str += arr(i + offset)
+ i += 1
+ }
+ insert(index, str)
+ }
+
+ def insert(index: Int, str: String): StringBuilder = {
+ val thisLength = length()
+ if (index < 0 || index > thisLength)
+ throw new StringIndexOutOfBoundsException(index)
+ else if (index == thisLength)
+ append(str)
+ else
+ content = content.substring(0, index) + Option(str).getOrElse("null") + content.substring(index)
+ this
+ }
+}
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/System.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/System.scala
new file mode 100644
index 0000000..6d80eaf
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/System.scala
@@ -0,0 +1,275 @@
+package java.lang
+
+import java.io._
+
+import scala.scalajs.js
+import js.Dynamic.global
+
+object System {
+ var out: PrintStream = new JSConsoleBasedPrintStream(isErr = false)
+ var err: PrintStream = new JSConsoleBasedPrintStream(isErr = true)
+ var in: InputStream = null
+
+ def currentTimeMillis(): scala.Long = {
+ (new js.Date).getTime().toLong
+ }
+
+ private[this] val getHighPrecisionTime: js.Function0[scala.Double] = {
+ if (!(!global.performance)) {
+ if (!(!global.performance.now)) {
+ () => global.performance.now().asInstanceOf[scala.Double]
+ } else if (!(!(global.performance.webkitNow))) {
+ () => global.performance.webkitNow().asInstanceOf[scala.Double]
+ } else {
+ () => new js.Date().getTime()
+ }
+ } else {
+ () => new js.Date().getTime()
+ }
+ }
+
+ def nanoTime(): scala.Long =
+ (getHighPrecisionTime() * 1000000).toLong
+
+ def arraycopy(src: Object, srcPos: scala.Int, dest: Object,
+ destPos: scala.Int, length: scala.Int): Unit = {
+
+ import scala.{Boolean, Char, Byte, Short, Int, Long, Float, Double}
+
+ @inline def checkIndices(srcLen: Int, destLen: Int): Unit = {
+ if (srcPos < 0 || destPos < 0 || length < 0 ||
+ srcPos + length > srcLen || destPos + length > destLen)
+ throw new ArrayIndexOutOfBoundsException("Array index out of bounds")
+ }
+
+ def mismatch(): Nothing =
+ throw new ArrayStoreException("Incompatible array types")
+
+ val forward = (src ne dest) || destPos < srcPos || srcPos + length < destPos
+
+ def copyPrim[@specialized T](src: Array[T], dest: Array[T]): Unit = {
+ checkIndices(src.length, dest.length)
+ if (forward) {
+ var i = 0
+ while (i < length) {
+ dest(i+destPos) = src(i+srcPos)
+ i += 1
+ }
+ } else {
+ var i = length-1
+ while (i >= 0) {
+ dest(i+destPos) = src(i+srcPos)
+ i -= 1
+ }
+ }
+ }
+
+ def copyRef(src: Array[AnyRef], dest: Array[AnyRef]): Unit = {
+ checkIndices(src.length, dest.length)
+ if (forward) {
+ var i = 0
+ while (i < length) {
+ dest(i+destPos) = src(i+srcPos)
+ i += 1
+ }
+ } else {
+ var i = length-1
+ while (i >= 0) {
+ dest(i+destPos) = src(i+srcPos)
+ i -= 1
+ }
+ }
+ }
+
+ if (src == null || dest == null) {
+ throw new NullPointerException()
+ } else (src match {
+ case src: Array[AnyRef] =>
+ dest match {
+ case dest: Array[AnyRef] => copyRef(src, dest)
+ case _ => mismatch()
+ }
+ case src: Array[Boolean] =>
+ dest match {
+ case dest: Array[Boolean] => copyPrim(src, dest)
+ case _ => mismatch()
+ }
+ case src: Array[Char] =>
+ dest match {
+ case dest: Array[Char] => copyPrim(src, dest)
+ case _ => mismatch()
+ }
+ case src: Array[Byte] =>
+ dest match {
+ case dest: Array[Byte] => copyPrim(src, dest)
+ case _ => mismatch()
+ }
+ case src: Array[Short] =>
+ dest match {
+ case dest: Array[Short] => copyPrim(src, dest)
+ case _ => mismatch()
+ }
+ case src: Array[Int] =>
+ dest match {
+ case dest: Array[Int] => copyPrim(src, dest)
+ case _ => mismatch()
+ }
+ case src: Array[Long] =>
+ dest match {
+ case dest: Array[Long] => copyPrim(src, dest)
+ case _ => mismatch()
+ }
+ case src: Array[Float] =>
+ dest match {
+ case dest: Array[Float] => copyPrim(src, dest)
+ case _ => mismatch()
+ }
+ case src: Array[Double] =>
+ dest match {
+ case dest: Array[Double] => copyPrim(src, dest)
+ case _ => mismatch()
+ }
+ case _ =>
+ mismatch()
+ })
+ }
+
+ def identityHashCode(x: Object): scala.Int = {
+ import js.prim
+ x match {
+ case null => 0
+ case _:prim.Boolean | _:prim.Number | _:prim.String | _:prim.Undefined =>
+ x.hashCode()
+ case _ =>
+ if (x.getClass == null) {
+ // This is not a Scala.js object
+ 42
+ } else {
+ val hash = x.asInstanceOf[js.Dynamic].selectDynamic("$idHashCode$0")
+ if (!js.isUndefined(hash)) {
+ hash.asInstanceOf[Int]
+ } else {
+ val newHash = IDHashCode.nextIDHashCode()
+ x.asInstanceOf[js.Dynamic].updateDynamic("$idHashCode$0")(newHash)
+ newHash
+ }
+ }
+ }
+ }
+
+ private object IDHashCode {
+ private var lastIDHashCode: Int = 0
+
+ def nextIDHashCode(): Int = {
+ val r = lastIDHashCode + 1
+ lastIDHashCode = r
+ r
+ }
+ }
+
+ //def getProperties(): java.util.Properties
+ //def getProperty(key: String): String
+ //def getProperty(key: String, default: String): String
+ //def clearProperty(key: String): String
+ //def setProperty(key: String, value: String): String
+
+ //def getenv(): java.util.Map[String,String]
+ //def getenv(name: String): String
+
+ def exit(status: scala.Int) = Runtime.getRuntime().exit(status)
+ def gc() = Runtime.getRuntime().gc()
+}
+
+private[lang] final class JSConsoleBasedPrintStream(isErr: Boolean)
+ extends PrintStream(new JSConsoleBasedPrintStream.DummyOutputStream) {
+
+ import JSConsoleBasedPrintStream._
+
+ /** Whether the buffer is flushed.
+ * This can be true even if buffer != "" because of line continuations.
+ * However, the converse is never true, i.e., !flushed => buffer != "".
+ */
+ private var flushed: scala.Boolean = true
+ private var buffer: String = ""
+
+ override def write(b: Int): Unit =
+ write(Array(b.toByte), 0, 1)
+
+ override def write(buf: Array[scala.Byte], off: Int, len: Int): Unit = {
+ /* This does *not* decode buf as a sequence of UTF-8 code units.
+ * This is not really useful, and would uselessly pull in the UTF-8 decoder
+ * in all applications that use OutputStreams (not just PrintStreams).
+ * Instead, we use a trivial ISO-8859-1 decoder in here.
+ */
+ if (off < 0 || len < 0 || len > buf.length - off)
+ throw new IndexOutOfBoundsException
+
+ var i = 0
+ while (i < len) {
+ print((buf(i + off) & 0xff).toChar)
+ i += 1
+ }
+ }
+
+ override def print(b: scala.Boolean): Unit = printString(String.valueOf(b))
+ override def print(c: scala.Char): Unit = printString(String.valueOf(c))
+ override def print(i: scala.Int): Unit = printString(String.valueOf(i))
+ override def print(l: scala.Long): Unit = printString(String.valueOf(l))
+ override def print(f: scala.Float): Unit = printString(String.valueOf(f))
+ override def print(d: scala.Double): Unit = printString(String.valueOf(d))
+ override def print(s: Array[scala.Char]): Unit = printString(String.valueOf(s))
+ override def print(s: String): Unit = printString(if (s == null) "null" else s)
+ override def print(obj: AnyRef): Unit = printString(String.valueOf(obj))
+
+ override def println(): Unit = printString("\n")
+
+ private def printString(s: String): Unit = {
+ var rest: String = s
+ while (rest != "") {
+ val nlPos = rest.indexOf("\n")
+ if (nlPos < 0) {
+ buffer += rest
+ flushed = false
+ rest = ""
+ } else {
+ doWriteLine(buffer + rest.substring(0, nlPos))
+ buffer = ""
+ flushed = true
+ rest = rest.substring(nlPos+1)
+ }
+ }
+ }
+
+ /**
+ * Since we cannot write a partial line in JavaScript, we write a whole
+ * line with continuation symbol at the end and schedule a line continuation
+ * symbol for the new line if the buffer is flushed.
+ */
+ override def flush(): Unit = if (!flushed) {
+ doWriteLine(buffer + LineContEnd)
+ buffer = LineContStart
+ flushed = true
+ }
+
+ override def close(): Unit = ()
+
+ private def doWriteLine(line: String): Unit = {
+ if (!(!global.console)) {
+ if (isErr && !(!global.console.error))
+ global.console.error(line)
+ else
+ global.console.log(line)
+ }
+ }
+}
+
+private[lang] object JSConsoleBasedPrintStream {
+ private final val LineContEnd: String = "\u21A9"
+ private final val LineContStart: String = "\u21AA"
+
+ class DummyOutputStream extends OutputStream {
+ def write(c: Int): Unit =
+ throw new AssertionError(
+ "Should not get in JSConsoleBasedPrintStream.DummyOutputStream")
+ }
+}
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/Thread.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/Thread.scala
new file mode 100644
index 0000000..e52d7f6
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/Thread.scala
@@ -0,0 +1,16 @@
+package java.lang
+
+/* We need a constructor to create SingleThread in the companion object, but
+ * we don't want user code doing a 'new Thread()' to link, because that could
+ * be confusing.
+ * So we use a binary signature that no Java source file can ever produce.
+ */
+class Thread private (dummy: Unit) extends Runnable {
+ def run(): Unit = ()
+}
+
+object Thread {
+ private[this] val SingleThread = new Thread(())
+
+ def currentThread(): Thread = SingleThread
+}
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/ThreadLocal.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/ThreadLocal.scala
new file mode 100644
index 0000000..a36a40c
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/ThreadLocal.scala
@@ -0,0 +1,24 @@
+package java.lang
+
+class ThreadLocal[T] {
+ private var hasValue: Boolean = false
+ private var v: T = _
+
+ protected def initialValue(): T = null.asInstanceOf[T]
+
+ def get(): T = {
+ if (!hasValue)
+ set(initialValue)
+ v
+ }
+
+ def set(o: T): Unit = {
+ v = o
+ hasValue = true
+ }
+
+ def remove(): Unit = {
+ hasValue = false
+ v = null.asInstanceOf[T] // for gc
+ }
+}
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/Throwables.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/Throwables.scala
new file mode 100644
index 0000000..a38fee9
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/Throwables.scala
@@ -0,0 +1,363 @@
+package java.lang
+
+import scala.scalajs.js
+
+class Throwable(s: String, private var e: Throwable) extends Object with java.io.Serializable {
+ def this() = this(null, null)
+ def this(s: String) = this(s, null)
+ def this(e: Throwable) = this(null, e)
+
+ private[this] var stackTrace: Array[StackTraceElement] = _
+
+ fillInStackTrace()
+
+ def initCause(cause: Throwable): Throwable = {
+ e = cause
+ this
+ }
+
+ def getMessage(): String = s
+ def getCause(): Throwable = e
+ def getLocalizedMessage(): String = getMessage()
+
+ def fillInStackTrace(): Throwable = {
+ scala.scalajs.runtime.StackTrace.captureState(this)
+ this
+ }
+
+ def getStackTrace(): Array[StackTraceElement] = {
+ if (stackTrace eq null)
+ stackTrace = scala.scalajs.runtime.StackTrace.extract(this)
+ stackTrace
+ }
+
+ def setStackTrace(stackTrace: Array[StackTraceElement]): Unit = {
+ var i = 0
+ while (i < stackTrace.length) {
+ if (stackTrace(i) eq null)
+ throw new NullPointerException()
+ i += 1
+ }
+
+ this.stackTrace = stackTrace.clone()
+ }
+
+ def printStackTrace(): Unit = printStackTrace(System.err)
+
+ def printStackTrace(s: java.io.PrintStream): Unit =
+ printStackTraceImpl(s.println(_))
+
+ def printStackTrace(s: java.io.PrintWriter): Unit =
+ printStackTraceImpl(s.println(_))
+
+ private[this] def printStackTraceImpl(sprintln: String => Unit): Unit = {
+ getStackTrace() // will init it if still null
+
+ // Message
+ sprintln(toString)
+
+ // Trace
+ if (stackTrace.length != 0) {
+ var i = 0
+ while (i < stackTrace.length) {
+ sprintln(" at "+stackTrace(i))
+ i += 1
+ }
+ } else {
+ sprintln(" <no stack trace available>")
+ }
+
+ // Causes
+ var wCause: Throwable = this
+ while ((wCause ne wCause.getCause) && (wCause.getCause ne null)) {
+ val parentTrace = wCause.getStackTrace
+ wCause = wCause.getCause
+ val thisTrace = wCause.getStackTrace
+
+ val thisLength = thisTrace.length
+ val parentLength = parentTrace.length
+
+ sprintln("Caused by: " + wCause.toString)
+
+ if (thisLength != 0) {
+ /* Count how many frames are shared between this stack trace and the
+ * parent stack trace, so that we can omit them when printing.
+ */
+ var sameFrameCount: Int = 0
+ while (sameFrameCount < thisLength && sameFrameCount < parentLength &&
+ thisTrace(thisLength-sameFrameCount-1) == parentTrace(parentLength-sameFrameCount-1)) {
+ sameFrameCount += 1
+ }
+
+ /* If at least one, decrement so that the first common frame is still
+ * printed. According to Harmony this is spec'ed and common practice.
+ */
+ if (sameFrameCount > 0)
+ sameFrameCount -= 1
+
+ // Print the non-common frames
+ val lengthToPrint = thisLength - sameFrameCount
+ var i = 0
+ while (i < lengthToPrint) {
+ sprintln(" at "+thisTrace(i))
+ i += 1
+ }
+
+ if (sameFrameCount > 0)
+ sprintln(" ... " + sameFrameCount + " more")
+ } else {
+ sprintln(" <no stack trace available>")
+ }
+ }
+ }
+
+ override def toString() = {
+ val className = getClass.getName
+ val message = getMessage()
+ if (message eq null) className
+ else className + ": " + message
+ }
+}
+
+class ThreadDeath() extends Error()
+
+
+/* java.lang.*Error.java */
+
+class AbstractMethodError(s: String) extends IncompatibleClassChangeError(s) {
+ def this() = this(null)
+}
+
+class AssertionError private (s: String) extends Error(s) {
+ def this() = this(null)
+ def this(o: Object) = this(o.toString)
+ def this(b: scala.Boolean) = this(b.toString)
+ def this(c: scala.Char) = this(c.toString)
+ def this(i: scala.Int) = this(i.toString)
+ def this(l: scala.Long) = this(l.toString)
+ def this(f: scala.Float) = this(f.toString)
+ def this(d: scala.Double) = this(d.toString)
+}
+
+class BootstrapMethodError(s: String, e: Throwable) extends LinkageError(s) {
+ def this(e: Throwable) = this(null, e)
+ def this(s: String) = this(s, null)
+ def this() = this(null, null)
+}
+
+class ClassCircularityError(s: String) extends LinkageError(s) {
+ def this() = this(null)
+}
+
+class ClassFormatError(s: String) extends LinkageError(s) {
+ def this() = this(null)
+}
+
+class Error(s: String, e: Throwable) extends Throwable(s, e) {
+ def this() = this(null, null)
+ def this(s: String) = this(s, null)
+ def this(e: Throwable) = this(null, e)
+}
+
+class ExceptionInInitializerError private (s: String, private val e: Throwable) extends LinkageError(s) {
+ def this(thrown: Throwable) = this(null, thrown)
+ def this(s: String) = this(s, null)
+ def this() = this(null, null)
+ def getException(): Throwable = e
+ override def getCause(): Throwable = e
+}
+
+class IllegalAccessError(s: String) extends IncompatibleClassChangeError(s) {
+ def this() = this(null)
+}
+
+class IncompatibleClassChangeError(s: String) extends LinkageError(s) {
+ def this() = this(null)
+}
+
+class InstantiationError(s: String) extends IncompatibleClassChangeError(s) {
+ def this() = this(null)
+}
+
+class InternalError(s: String) extends VirtualMachineError(s) {
+ def this() = this(null)
+}
+
+class LinkageError(s: String) extends Error(s) {
+ def this() = this(null)
+}
+
+class NoClassDefFoundError(s: String) extends LinkageError(s) {
+ def this() = this(null)
+}
+
+class NoSuchFieldError(s: String) extends IncompatibleClassChangeError(s) {
+ def this() = this(null)
+}
+
+class NoSuchMethodError(s: String) extends IncompatibleClassChangeError(s) {
+ def this() = this(null)
+}
+
+class OutOfMemoryError(s: String) extends VirtualMachineError(s) {
+ def this() = this(null)
+}
+
+class StackOverflowError(s: String) extends VirtualMachineError(s) {
+ def this() = this(null)
+}
+
+class UnknownError(s: String) extends VirtualMachineError(s) {
+ def this() = this(null)
+}
+
+class UnsatisfiedLinkError(s: String) extends LinkageError(s) {
+ def this() = this(null)
+}
+
+class UnsupportedClassVersionError(s: String) extends ClassFormatError(s) {
+ def this() = this(null)
+}
+
+class VerifyError(s: String) extends LinkageError(s) {
+ def this() = this(null)
+}
+
+abstract class VirtualMachineError(s: String) extends Error(s) {
+ def this() = this(null)
+}
+
+
+/* java.lang.*Exception.java */
+
+class ArithmeticException(s: String) extends RuntimeException(s) {
+ def this() = this(null)
+}
+
+class ArrayIndexOutOfBoundsException(s: String) extends IndexOutOfBoundsException(s) {
+ def this(index: Int) = this("Array index out of range: " + index)
+ def this() = this(null)
+}
+
+class ArrayStoreException(s: String) extends RuntimeException(s) {
+ def this() = this(null)
+}
+
+class ClassCastException(s: String) extends RuntimeException(s) {
+ def this() = this(null)
+}
+
+class ClassNotFoundException(s: String, e: Throwable) extends ReflectiveOperationException(s) {
+ def this(s: String) = this(s, null)
+ def this() = this(null, null)
+ def getException(): Throwable = e
+ override def getCause(): Throwable = e
+}
+
+class CloneNotSupportedException(s: String) extends Exception(s) {
+ def this() = this(null)
+}
+
+import scala.language.existentials
+class EnumConstantNotPresentException(
+ e: Class[_ <: Enum[T] forSome { type T <: Enum[T] }], c: String)
+ extends RuntimeException(e.getName() + "." + c) {
+ def enumType() = e
+ def constantName() = c
+}
+
+class Exception(s: String, e: Throwable) extends Throwable(s, e) {
+ def this(e: Throwable) = this(null, e)
+ def this(s: String) = this(s, null)
+ def this() = this(null, null)
+}
+
+class IllegalAccessException(s: String) extends ReflectiveOperationException(s) {
+ def this() = this(null)
+}
+
+class IllegalArgumentException(s: String, e: Throwable) extends RuntimeException(s, e) {
+ def this(e: Throwable) = this(null, e)
+ def this(s: String) = this(s, null)
+ def this() = this(null, null)
+}
+
+class IllegalMonitorStateException(s: String) extends RuntimeException(s) {
+ def this() = this(null)
+}
+
+class IllegalStateException(s: String, e: Throwable) extends RuntimeException(s, e) {
+ def this(e: Throwable) = this(null, e)
+ def this(s: String) = this(s, null)
+ def this() = this(null, null)
+}
+
+class IllegalThreadStateException(s: String) extends IllegalArgumentException(s) {
+ def this() = this(null)
+}
+
+class IndexOutOfBoundsException(s: String) extends RuntimeException(s) {
+ def this() = this(null)
+}
+
+class InstantiationException(s: String) extends ReflectiveOperationException(s) {
+ def this() = this(null)
+}
+
+class InterruptedException(s: String) extends Exception(s) {
+ def this() = this(null)
+}
+
+class NegativeArraySizeException(s: String) extends RuntimeException(s) {
+ def this() = this(null)
+}
+
+class NoSuchFieldException(s: String) extends ReflectiveOperationException(s) {
+ def this() = this(null)
+}
+
+class NoSuchMethodException(s: String) extends ReflectiveOperationException(s) {
+ def this() = this(null)
+}
+
+class NullPointerException(s: String) extends RuntimeException(s) {
+ def this() = this(null)
+}
+
+class NumberFormatException(s: String) extends IllegalArgumentException(s) {
+ def this() = this(null)
+}
+
+class ReflectiveOperationException(s: String, e: Throwable) extends Exception(s, e) {
+ def this(e: Throwable) = this(null, e)
+ def this(s: String) = this(s, null)
+ def this() = this(null, null)
+}
+
+class RuntimeException(s: String, e: Throwable) extends Exception(s, e) {
+ def this(e: Throwable) = this(null, e)
+ def this(s: String) = this(s, null)
+ def this() = this(null, null)
+}
+
+class SecurityException(s: String, e: Throwable) extends RuntimeException(s, e) {
+ def this(e: Throwable) = this(null, e)
+ def this(s: String) = this(s, null)
+ def this() = this(null, null)
+}
+
+class StringIndexOutOfBoundsException(s: String) extends IndexOutOfBoundsException(s) {
+ def this(index: Int) = this("String index out of range: " + index)
+ def this() = this(null)
+}
+
+class TypeNotPresentException(t: String, e: Throwable)
+ extends RuntimeException("Type " + t + " not present", e) {
+ def typeName(): String = t
+}
+
+class UnsupportedOperationException(s: String, e: Throwable) extends RuntimeException(s, e) {
+ def this() = this(null, null)
+ def this(s: String) = this(s, null)
+ def this(e: Throwable) = this(null, e)
+}
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/Void.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/Void.scala
new file mode 100644
index 0000000..fbe68fb
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/Void.scala
@@ -0,0 +1,7 @@
+package java.lang
+
+final class Void private {}
+
+object Void {
+ final val TYPE = classOf[scala.Unit]
+}
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/ref/PhantomReference.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/ref/PhantomReference.scala
new file mode 100644
index 0000000..ecace8a
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/ref/PhantomReference.scala
@@ -0,0 +1,7 @@
+package java.lang.ref
+
+class PhantomReference[T >: Null <: AnyRef](referent: T,
+ queue: ReferenceQueue[_ >: T]) extends Reference[T](null) {
+
+ override def get(): T = null
+}
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/ref/Reference.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/ref/Reference.scala
new file mode 100644
index 0000000..76909cf
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/ref/Reference.scala
@@ -0,0 +1,8 @@
+package java.lang.ref
+
+abstract class Reference[T >: Null <: AnyRef](private[this] var referent: T) {
+ def get(): T = referent
+ def clear(): Unit = referent = null
+ def isEnqueued(): Boolean = false
+ def enqueue(): Boolean = false
+}
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/ref/ReferenceQueue.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/ref/ReferenceQueue.scala
new file mode 100644
index 0000000..e9c5110
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/ref/ReferenceQueue.scala
@@ -0,0 +1,3 @@
+package java.lang.ref
+
+class ReferenceQueue[T >: Null <: AnyRef]
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/ref/SoftReference.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/ref/SoftReference.scala
new file mode 100644
index 0000000..eb0fdf7
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/ref/SoftReference.scala
@@ -0,0 +1,9 @@
+package java.lang.ref
+
+class SoftReference[T >: Null <: AnyRef](referent: T,
+ queue: ReferenceQueue[_ >: T]) extends Reference[T](referent) {
+
+ def this(referent: T) = this(referent, null)
+
+ override def get(): T = super.get()
+}
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/ref/WeakReference.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/ref/WeakReference.scala
new file mode 100644
index 0000000..2a74aa1
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/ref/WeakReference.scala
@@ -0,0 +1,7 @@
+package java.lang.ref
+
+class WeakReference[T >: Null <: AnyRef](referent: T,
+ queue: ReferenceQueue[_ >: T]) extends Reference[T](referent) {
+
+ def this(referent: T) = this(referent, null)
+}
diff --git a/examples/scala-js/javalanglib/src/main/scala/java/lang/reflect/Array.scala b/examples/scala-js/javalanglib/src/main/scala/java/lang/reflect/Array.scala
new file mode 100644
index 0000000..bc3696e
--- /dev/null
+++ b/examples/scala-js/javalanglib/src/main/scala/java/lang/reflect/Array.scala
@@ -0,0 +1,176 @@
+package java.lang.reflect
+
+import scala.scalajs.js
+
+import js.JSConverters._
+
+import java.lang.Class
+
+object Array {
+ def newInstance(componentType: Class[_], length: Int): AnyRef =
+ componentType.newArrayOfThisClass(js.Array(length))
+
+ def newInstance(componentType: Class[_], dimensions: scala.Array[Int]): AnyRef =
+ componentType.newArrayOfThisClass(dimensions.toJSArray)
+
+ def getLength(array: AnyRef): Int = array match {
+ // yes, this is kind of stupid, but that's how it is
+ case array: Array[Object] => array.length
+ case array: Array[Boolean] => array.length
+ case array: Array[Char] => array.length
+ case array: Array[Byte] => array.length
+ case array: Array[Short] => array.length
+ case array: Array[Int] => array.length
+ 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")
+ }
+
+ def get(array: AnyRef, index: Int): AnyRef = array match {
+ case array: Array[Object] => array(index)
+ case array: Array[Boolean] => new java.lang.Boolean(array(index))
+ case array: Array[Char] => new java.lang.Character(array(index))
+ case array: Array[Byte] => new java.lang.Byte(array(index))
+ case array: Array[Short] => new java.lang.Short(array(index))
+ case array: Array[Int] => new java.lang.Integer(array(index))
+ case array: Array[Long] => new java.lang.Long(array(index))
+ case array: Array[Float] => new java.lang.Float(array(index))
+ case array: Array[Double] => new java.lang.Double(array(index))
+ case _ => throw new IllegalArgumentException("argument type mismatch")
+ }
+
+ def getBoolean(array: AnyRef, index: Int): Boolean = array match {
+ case array: Array[Boolean] => array(index)
+ case _ => throw new IllegalArgumentException("argument type mismatch")
+ }
+
+ def getChar(array: AnyRef, index: Int): Char = array match {
+ case array: Array[Char] => array(index)
+ case _ => throw new IllegalArgumentException("argument type mismatch")
+ }
+
+ def getByte(array: AnyRef, index: Int): Byte = array match {
+ case array: Array[Byte] => array(index)
+ case _ => throw new IllegalArgumentException("argument type mismatch")
+ }
+
+ 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")
+ }
+
+ def getInt(array: AnyRef, index: Int): Int = array match {
+ case array: Array[Int] => array(index)
+ 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")
+ }
+
+ def getLong(array: AnyRef, index: Int): Long = array match {
+ case array: Array[Long] => array(index)
+ case array: Array[Char] => array(index)
+ 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")
+ }
+
+ def getFloat(array: AnyRef, index: Int): Float = array match {
+ case array: Array[Float] => array(index)
+ case array: Array[Char] => array(index)
+ case array: Array[Byte] => array(index)
+ case array: Array[Short] => array(index)
+ case array: Array[Int] => array(index)
+ case array: Array[Long] => array(index)
+ case _ => throw new IllegalArgumentException("argument type mismatch")
+ }
+
+ def getDouble(array: AnyRef, index: Int): Double = array match {
+ case array: Array[Double] => array(index)
+ case array: Array[Char] => array(index)
+ case array: Array[Byte] => array(index)
+ case array: Array[Short] => array(index)
+ case array: Array[Int] => array(index)
+ case array: Array[Long] => array(index)
+ case array: Array[Float] => array(index)
+ case _ => throw new IllegalArgumentException("argument type mismatch")
+ }
+
+ def set(array: AnyRef, index: Int, value: AnyRef): Unit = array match {
+ case array: Array[Object] => array(index) = value
+ case _ =>
+ (value: Any) match {
+ case value: Boolean => setBoolean(array, index, value)
+ case value: Char => setChar(array, index, value)
+ case value: Byte => setByte(array, index, value)
+ case value: Short => setShort(array, index, value)
+ case value: Int => setInt(array, index, value)
+ 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")
+ }
+ }
+
+ 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")
+ }
+
+ def setChar(array: AnyRef, index: Int, value: Char): Unit = array match {
+ case array: Array[Char] => array(index) = value
+ case array: Array[Int] => array(index) = value
+ 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")
+ }
+
+ def setByte(array: AnyRef, index: Int, value: Byte): Unit = array match {
+ case array: Array[Byte] => array(index) = value
+ case array: Array[Short] => array(index) = value
+ case array: Array[Int] => array(index) = value
+ 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")
+ }
+
+ def setShort(array: AnyRef, index: Int, value: Short): Unit = array match {
+ case array: Array[Short] => array(index) = value
+ case array: Array[Int] => array(index) = value
+ 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")
+ }
+
+ def setInt(array: AnyRef, index: Int, value: Int): Unit = array match {
+ case array: Array[Int] => array(index) = value
+ 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")
+ }
+
+ 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
+ case array: Array[Double] => array(index) = value
+ case _ => throw new IllegalArgumentException("argument type mismatch")
+ }
+
+ 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")
+ }
+
+ 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")
+ }
+}
diff --git a/examples/scala-js/javalib-ex-test-suite/src/test/scala/scala/scalajs/testsuite/javalibex/DataInputStreamTest.scala b/examples/scala-js/javalib-ex-test-suite/src/test/scala/scala/scalajs/testsuite/javalibex/DataInputStreamTest.scala
new file mode 100644
index 0000000..61ec560
--- /dev/null
+++ b/examples/scala-js/javalib-ex-test-suite/src/test/scala/scala/scalajs/testsuite/javalibex/DataInputStreamTest.scala
@@ -0,0 +1,338 @@
+package scala.scalajs.testsuite.javalibex
+
+import java.io._
+
+import org.scalajs.jasminetest.JasmineTest
+
+import scala.scalajs.js.typedarray._
+import scala.scalajs.js.JSConverters._
+
+object DataInputStreamTest extends JasmineTest {
+
+ def tests(name: String)(inFromBytes: Seq[Byte] => InputStream) {
+ def newStream(data: Int*) =
+ new DataInputStream(inFromBytes(data.map(_.toByte)))
+
+ when("typedarray").
+ describe(name) {
+
+ it("should provide `readBoolean`") {
+ val data = Seq(0x00, 0x01, 0xF1, 0x00, 0x01)
+ val stream = newStream(data: _*)
+
+ for (d <- data)
+ expect(stream.readBoolean()).toBe(d != 0)
+
+ expect(() => stream.readBoolean()).toThrow // EOF
+ }
+
+ it("should provide `readByte`") {
+ val data = Seq(0x00, 0x01, 0xF1, 0x7D, 0x35)
+ val stream = newStream(data: _*)
+
+ for (d <- data)
+ expect(stream.readByte()).toBe(d.toByte)
+
+ expect(() => stream.readBoolean()).toThrow // EOF
+ }
+
+ it("should provide `readChar`") {
+ val stream = newStream(
+ 0x00, 0x48, // H
+ 0x00, 0xF6, // ö
+ 0x00, 0x6C, // l
+ 0x00, 0x6C, // l
+ 0x00, 0xF6, // ö
+ 0x00, 0x20, // [space]
+ 0x00, 0x57, // W
+ 0x01, 0x03, // ă
+ 0x00, 0x72, // r
+ 0x02, 0x34, // ȴ
+ 0x01, 0x11, // đ
+ 0x56 // dangling
+ )
+ var res = ""
+
+ for (i <- 1 to 11)
+ res += stream.readChar()
+
+ expect(res).toEqual("Höllö Wărȴđ")
+
+ expect(() => stream.readChar()).toThrow // Dangling + EOF
+ }
+
+ it("should provide `readDouble`") {
+ val stream = newStream(
+ 0x3f, 0xe6, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
+ 0x41, 0x15, 0x19, 0x20, 0x45, 0x8d, 0x9b, 0x5f,
+ 0xc0, 0xab, 0x20, 0x22, 0x75, 0x25, 0x46, 0x0b,
+ 0x7f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xc0, 0x1c, 0x0d, 0xca, 0x65, 0xea, 0x3f, 0xa4,
+ 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01 // dangling
+ )
+
+ expect(stream.readDouble()).toBe(0.7)
+ expect(stream.readDouble()).toBe(345672.067923)
+ expect(stream.readDouble()).toBe(-3472.0673)
+ expect(stream.readDouble().isNaN).toBe(true)
+ expect(stream.readDouble()).toBe(Double.PositiveInfinity)
+ expect(stream.readDouble()).toBe(-7.0134674)
+ expect(stream.readDouble()).toBe(Double.NegativeInfinity)
+ expect(stream.readDouble()).toBe(0)
+ expect(() => stream.readDouble()).toThrow
+ }
+
+ it("should provide `readFloat`") {
+ val stream = newStream(
+ 0xbf, 0x80, 0x00, 0x00,
+ 0x45, 0x8e, 0x9c, 0x83,
+ 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x7f, 0xc0, 0x00, 0x00,
+ 0x7f, 0x80, 0x00, 0x00,
+ 0xbb, 0x03, 0x12, 0x6f,
+ 0xff, 0x80, 0x00, 0x00,
+ 0xff // dangling
+ )
+
+ expect(stream.readFloat()).toBeCloseTo(-1.0f, 6)
+ expect(stream.readFloat()).toBeCloseTo(4563.564f, 6)
+ expect(stream.readFloat()).toBeCloseTo(-0.0f, 6)
+ expect(stream.readFloat()).toBeCloseTo(0.0f, 6)
+ expect(stream.readFloat().isNaN).toBe(true)
+ expect(stream.readFloat()).toBe(Float.PositiveInfinity)
+ expect(stream.readFloat()).toBeCloseTo(-0.002f, 6)
+ expect(stream.readFloat()).toBe(Float.NegativeInfinity)
+ expect(() => stream.readDouble()).toThrow
+ }
+
+ it("should provide `readInt`") {
+ val stream = newStream(
+ 0x00, 0x00, 0x00, 0x00,
+ 0x7f, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xfc,
+ 0x00, 0x00, 0x00, 0x53,
+ 0x00, 0x00, 0x89, 0xa2,
+ 0xff, 0xfe, 0x82, 0xfd,
+ 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01)
+
+ expect(stream.readInt()).toBe(0)
+ expect(stream.readInt()).toBe(Int.MaxValue)
+ expect(stream.readInt()).toBe(-4)
+ expect(stream.readInt()).toBe(83)
+ expect(stream.readInt()).toBe(35234)
+ expect(stream.readInt()).toBe(-97539)
+ expect(stream.readInt()).toBe(Int.MinValue)
+ expect(stream.readInt()).toBe(1)
+ expect(() => stream.readDouble()).toThrow
+ }
+
+ it("should provide `readLong`") {
+ val stream = newStream(
+ 0x00, 0x01, 0xf0, 0xec, 0x59, 0x0c, 0x70, 0x9a,
+ 0xff, 0xff, 0xff, 0xff, 0xfe, 0x10, 0xd5, 0x5e,
+ 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x79, 0x24,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f)
+
+ expect(stream.readLong() == 546372873646234L).toBeTruthy
+ expect(stream.readLong() == -32451234L).toBeTruthy
+ expect(stream.readLong() == Long.MaxValue).toBeTruthy
+ expect(stream.readLong() == 0L).toBeTruthy
+ expect(stream.readLong() == -1L).toBeTruthy
+ expect(stream.readLong() == Long.MinValue).toBeTruthy
+ expect(stream.readLong() == -34524L).toBeTruthy
+ expect(stream.readLong() == 47L).toBeTruthy
+ expect(() => stream.readDouble()).toThrow
+ }
+
+ it("should provide `readShort`") {
+ val stream = newStream(
+ 0x01, 0xc5,
+ 0xff, 0xd5,
+ 0x7f, 0xff,
+ 0x18, 0xb0,
+ 0x00, 0x00,
+ 0x80, 0x00,
+ 0xfe, 0xa6,
+ 0x00, 0x22,
+ 0x01 // dangling
+ )
+
+ expect(stream.readShort()).toBe(453)
+ expect(stream.readShort()).toBe(-43)
+ expect(stream.readShort()).toBe(Short.MaxValue)
+ expect(stream.readShort()).toBe(6320)
+ expect(stream.readShort()).toBe(0)
+ expect(stream.readShort()).toBe(Short.MinValue)
+ expect(stream.readShort()).toBe(-346)
+ expect(stream.readShort()).toBe(34)
+ expect(() => stream.readDouble()).toThrow
+ }
+
+ it("should provide `readUnsignedByte`") {
+ val data = Seq(0x00, 0x01, 0xF1, 0x7D, 0x35)
+ val stream = newStream(data: _*)
+
+ for (d <- data)
+ expect(stream.readUnsignedByte()).toBe(d)
+
+ expect(() => stream.readBoolean()).toThrow // EOF
+ }
+
+ it("should provide `readUnsignedShort`") {
+ val stream = newStream(
+ 0xfe, 0x4c,
+ 0x00, 0x00,
+ 0x18, 0xee,
+ 0x0d, 0xed,
+ 0x00, 0x2b,
+ 0x01, 0xce,
+ 0x01, 0x56,
+ 0x64, 0x2b,
+ 0x01 // dangling
+ )
+
+ expect(stream.readUnsignedShort()).toBe(65100)
+ expect(stream.readUnsignedShort()).toBe(0)
+ expect(stream.readUnsignedShort()).toBe(6382)
+ expect(stream.readUnsignedShort()).toBe(3565)
+ expect(stream.readUnsignedShort()).toBe(43)
+ expect(stream.readUnsignedShort()).toBe(462)
+ expect(stream.readUnsignedShort()).toBe(342)
+ expect(stream.readUnsignedShort()).toBe(25643)
+ expect(() => stream.readDouble()).toThrow
+ }
+
+ it("should provide `readFully` (1 arg & 3 arg)") {
+ val stream = newStream(-100 to 99: _*)
+ val buf = new Array[Byte](50)
+
+ stream.readFully(buf)
+ expect(buf.toJSArray).toEqual((-100 to -51).toJSArray)
+
+ expect(() => stream.readFully(null)).toThrow
+
+ stream.readFully(buf, 40, 10)
+ expect(buf.toJSArray).toEqual(((-100 to -61) ++ (-50 to -41)).toJSArray)
+
+ expect(() => stream.readFully(buf, 70, 1)).toThrow
+ expect(() => stream.readFully(buf, 10, 100)).toThrow
+ expect(() => stream.readFully(buf, -1, 2)).toThrow
+ expect(() => stream.readFully(buf, 0, -1)).toThrow
+
+ stream.readFully(buf, 0, 50)
+ expect(buf.toJSArray).toEqual((-40 to 9).toJSArray)
+
+ stream.readFully(buf, 0, 50)
+ expect(buf.toJSArray).toEqual((10 to 59).toJSArray)
+
+ expect(() => stream.readFully(buf)).toThrow
+ }
+
+ it("should provide `readFully` for bursty streams") {
+ class BurstyStream(length: Int, burst: Int) extends InputStream {
+ private var i: Int = 0
+ def read(): Int = if (i < length) { i += 1; i } else -1
+ override def read(buf: Array[Byte], off: Int, reqLen: Int): Int = {
+ val len = Math.min(Math.min(reqLen, burst), length - i)
+ if (reqLen == 0) 0
+ else if (len == 0) -1
+ else {
+ var j: Int = 0
+ while (j < len) {
+ buf(off+j) = read().toByte
+ j += 1
+ }
+ len
+ }
+ }
+ }
+
+ val stream = new DataInputStream(new BurstyStream(100, 5))
+ val buf = new Array[Byte](50)
+
+ stream.readFully(buf)
+ expect(buf.toJSArray).toEqual((1 to 50).toJSArray)
+
+ stream.readFully(buf, 40, 10)
+ expect(buf.toJSArray).toEqual(((1 to 40) ++ (51 to 60)).toJSArray)
+
+ expect(() => stream.readFully(buf)).toThrow
+ }
+
+ it("should provide `readUTF`") {
+ val stream = newStream(
+ 0x00, 0x10, 0x48, 0xc3, 0xb6, 0x6c, 0x6c, 0xc3,
+ 0xb6, 0x20, 0x57, 0xc4, 0x83, 0x72, 0xc8, 0xb4,
+ 0xc4, 0x91, 0x00, 0x0d, 0x70, 0x6f, 0x6f, 0x20,
+ 0x2d, 0x3e, 0x20, 0xed, 0xa0, 0xbd, 0xed, 0xb2,
+ 0xa9, 0x00, 0x03, 0xe6, 0x84, 0x9b)
+
+ expect(stream.readUTF).toEqual("Höllö Wărȴđ")
+ expect(stream.readUTF).toEqual("poo -> 💩")
+ expect(stream.readUTF).toEqual("愛")
+
+ val badStream = newStream(0x00, 0x01, 0xC0, 0x82)
+ expect(() => badStream.readUTF).toThrow
+ }
+
+ it("should provide `readLine`") {
+ val stream = newStream(
+ "Hello World\nUNIX\nWindows\r\nMac (old)\rStuff".map(_.toInt): _*)
+
+ expect(stream.readLine()).toEqual("Hello World")
+ expect(stream.readLine()).toEqual("UNIX")
+ expect(stream.readLine()).toEqual("Windows")
+ expect(stream.readLine()).toEqual("Mac (old)")
+ expect(stream.readLine()).toEqual("Stuff")
+ expect(stream.readLine()).toBe(null)
+ }
+
+ it("should allow marking even when `readLine` has to push back") {
+ val stream = newStream(
+ "Hello World\nUNIX\nWindows\r\nMac (old)\rStuff".map(_.toInt): _*)
+
+ expect(stream.readLine()).toEqual("Hello World")
+ stream.mark(1000)
+ expect(stream.readLine()).toEqual("UNIX")
+ stream.reset()
+ expect(stream.readLine()).toEqual("UNIX")
+ expect(stream.readLine()).toEqual("Windows")
+ expect(stream.readLine()).toEqual("Mac (old)")
+ stream.mark(1000)
+ expect(stream.readLine()).toEqual("Stuff")
+ stream.reset()
+ expect(stream.readLine()).toEqual("Stuff")
+ expect(stream.readLine()).toBe(null)
+ }
+
+ }
+
+ }
+
+ tests("java.io.DataInputStream - generic case")(bytes =>
+ new ByteArrayInputStream(bytes.toArray))
+
+ tests("java.io.DataInputStream - ArrayBufferInputStream case")(bytes =>
+ new ArrayBufferInputStream(new Int8Array(bytes.toJSArray).buffer))
+
+ tests("java.io.DataInputStream - partially consumed ArrayBufferInputStream case") { bytes =>
+ val addBytes = Seq[Byte](0, 0, 0, 0)
+ val allBytes = addBytes ++ bytes
+ val in = new ArrayBufferInputStream(
+ new Int8Array(allBytes.toJSArray).buffer)
+
+ for (_ <- addBytes) in.read()
+
+ in
+ }
+
+}
diff --git a/examples/scala-js/javalib-ex-test-suite/src/test/scala/scala/scalajs/testsuite/javalibex/ZipInputStreamTest.scala b/examples/scala-js/javalib-ex-test-suite/src/test/scala/scala/scalajs/testsuite/javalibex/ZipInputStreamTest.scala
new file mode 100644
index 0000000..b2a2fe2
--- /dev/null
+++ b/examples/scala-js/javalib-ex-test-suite/src/test/scala/scala/scalajs/testsuite/javalibex/ZipInputStreamTest.scala
@@ -0,0 +1,136 @@
+package scala.scalajs.testsuite.javalibex
+
+import org.scalajs.jasminetest.JasmineTest
+
+import java.io._
+import java.util.zip._
+
+object ZipInputStreamTest extends JasmineTest {
+
+ when("typedarray").
+ describe("java.util.zip.ZipInputStream") {
+
+ it("should read zip archives") {
+ val in = new ZipInputStream(new ByteArrayInputStream(binZip))
+
+ def expectBinEntry(name: String, data: Seq[Int]): Unit = {
+ val e = in.getNextEntry()
+ expect(e.getName()).toEqual(name)
+
+ for (d <- data)
+ expect(in.read()).toBe(d)
+
+ expect(in.read()).toBe(-1)
+ }
+
+ def expectStrEntry(name: String, content: String): Unit = {
+ val e = in.getNextEntry()
+ expect(e.getName()).toEqual(name)
+
+ val r = new InputStreamReader(in)
+
+ for (c <- content)
+ expect(r.read().toChar).toBe(c)
+
+ expect(r.read()).toBe(-1)
+ }
+
+ expectBinEntry("greetings/", Seq())
+ expectStrEntry("greetings/en.txt", "Hello World, how are you doing?\n")
+ expectStrEntry("greetings/es.txt", "¿Hola mundo, cómo estás?\n")
+ expectStrEntry("greetings/fr.txt", "Bonjour, comment ça va?\n")
+ expectStrEntry("greetings/ja.txt", "こんにちは、お元気ですか。\n")
+ expectBinEntry("binary/", Seq())
+ expectBinEntry("binary/bytes_0_to_50.bin", 0 to 50)
+
+ expect(in.getNextEntry() == null).toBeTruthy
+ in.close()
+ }
+
+ }
+
+ /** A zip archive for testing:
+ *
+ * $ zipinfo test.zip
+ * Archive: test.zip 1304 bytes 7 files
+ * drwxr-xr-x 3.0 unx 0 bx stor 13-Aug-14 07:42 greetings/
+ * -rw-r--r-- 3.0 unx 32 tx stor 13-Aug-14 07:40 greetings/en.txt
+ * -rw-r--r-- 3.0 unx 28 tx stor 13-Aug-14 07:42 greetings/es.txt
+ * -rw-r--r-- 3.0 unx 25 tx stor 13-Aug-14 07:41 greetings/fr.txt
+ * -rw-r--r-- 3.0 unx 40 tx stor 13-Aug-14 07:40 greetings/ja.txt
+ * drwxr-xr-x 3.0 unx 0 bx stor 13-Aug-14 07:48 binary/
+ * -rw-r--r-- 3.0 unx 51 bx stor 13-Aug-14 07:48 binary/bytes_0_to_50.bin
+ * 7 files, 176 bytes uncompressed, 176 bytes compressed: 0.0%
+ */
+ val binZip = Array[Byte](80, 75, 3, 4, 10, 0, 0, 0, 0, 0, 89, 61, 13, 69, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 28, 0, 103, 114, 101, 101, 116,
+ 105, 110, 103, 115, 47, 85, 84, 9, 0, 3, -39, -6, -22, 83, -44, -4, -22,
+ 83, 117, 120, 11, 0, 1, 4, -4, 1, 0, 0, 4, 0, 0, 0, 0, 80, 75, 3, 4, 10,
+ 0, 2, 0, 0, 0, 13, 61, 13, 69, -125, -110, -96, -74, 32, 0, 0, 0, 32, 0,
+ 0, 0, 16, 0, 28, 0, 103, 114, 101, 101, 116, 105, 110, 103, 115, 47, 101,
+ 110, 46, 116, 120, 116, 85, 84, 9, 0, 3, 73, -6, -22, 83, -108, -4, -22,
+ 83, 117, 120, 11, 0, 1, 4, -4, 1, 0, 0, 4, 0, 0, 0, 0, 72, 101, 108, 108,
+ 111, 32, 87, 111, 114, 108, 100, 44, 32, 104, 111, 119, 32, 97, 114, 101,
+ 32, 121, 111, 117, 32, 100, 111, 105, 110, 103, 63, 10, 80, 75, 3, 4, 10,
+ 0, 2, 0, 0, 0, 89, 61, 13, 69, -27, 99, -56, -20, 28, 0, 0, 0, 28, 0, 0,
+ 0, 16, 0, 28, 0, 103, 114, 101, 101, 116, 105, 110, 103, 115, 47, 101,
+ 115, 46, 116, 120, 116, 85, 84, 9, 0, 3, -39, -6, -22, 83, -108, -4, -22,
+ 83, 117, 120, 11, 0, 1, 4, -4, 1, 0, 0, 4, 0, 0, 0, 0, -62, -65, 72, 111,
+ 108, 97, 32, 109, 117, 110, 100, 111, 44, 32, 99, -61, -77, 109, 111, 32,
+ 101, 115, 116, -61, -95, 115, 63, 10, 80, 75, 3, 4, 10, 0, 2, 0, 0, 0, 42,
+ 61, 13, 69, -2, -58, -42, 30, 25, 0, 0, 0, 25, 0, 0, 0, 16, 0, 28, 0, 103,
+ 114, 101, 101, 116, 105, 110, 103, 115, 47, 102, 114, 46, 116, 120, 116,
+ 85, 84, 9, 0, 3, -128, -6, -22, 83, -108, -4, -22, 83, 117, 120, 11, 0, 1,
+ 4, -4, 1, 0, 0, 4, 0, 0, 0, 0, 66, 111, 110, 106, 111, 117, 114, 44, 32,
+ 99, 111, 109, 109, 101, 110, 116, 32, -61, -89, 97, 32, 118, 97, 63, 10,
+ 80, 75, 3, 4, 10, 0, 2, 0, 0, 0, 24, 61, 13, 69, -26, -5, 76, 91, 40, 0,
+ 0, 0, 40, 0, 0, 0, 16, 0, 28, 0, 103, 114, 101, 101, 116, 105, 110, 103,
+ 115, 47, 106, 97, 46, 116, 120, 116, 85, 84, 9, 0, 3, 96, -6, -22, 83,
+ -108, -4, -22, 83, 117, 120, 11, 0, 1, 4, -4, 1, 0, 0, 4, 0, 0, 0, 0,
+ -29, -127, -109, -29, -126, -109, -29, -127, -85, -29, -127, -95, -29,
+ -127, -81, -29, -128, -127, -29, -127, -118, -27, -123, -125, -26, -80,
+ -105, -29, -127, -89, -29, -127, -103, -29, -127, -117, -29, -128, -126,
+ 10, 80, 75, 3, 4, 10, 0, 0, 0, 0, 0, 6, 62, 13, 69, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 7, 0, 28, 0, 98, 105, 110, 97, 114, 121, 47, 85, 84, 9, 0,
+ 3, 28, -4, -22, 83, -44, -4, -22, 83, 117, 120, 11, 0, 1, 4, -4, 1, 0, 0,
+ 4, 0, 0, 0, 0, 80, 75, 3, 4, 10, 0, 2, 0, 0, 0, 3, 62, 13, 69, -7, 93, 98,
+ 55, 51, 0, 0, 0, 51, 0, 0, 0, 24, 0, 28, 0, 98, 105, 110, 97, 114, 121,
+ 47, 98, 121, 116, 101, 115, 95, 48, 95, 116, 111, 95, 53, 48, 46, 98, 105,
+ 110, 85, 84, 9, 0, 3, 22, -4, -22, 83, -108, -4, -22, 83, 117, 120, 11, 0,
+ 1, 4, -4, 1, 0, 0, 4, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 80, 75, 1, 2, 30, 3, 10, 0, 0, 0, 0, 0, 89, 61, 13, 69, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 24, 0, 0, 0, 0, 0, 0, 0, 16, 0, -19,
+ 65, 0, 0, 0, 0, 103, 114, 101, 101, 116, 105, 110, 103, 115, 47, 85, 84,
+ 5, 0, 3, -39, -6, -22, 83, 117, 120, 11, 0, 1, 4, -4, 1, 0, 0, 4, 0, 0, 0,
+ 0, 80, 75, 1, 2, 30, 3, 10, 0, 2, 0, 0, 0, 13, 61, 13, 69, -125, -110,
+ -96, -74, 32, 0, 0, 0, 32, 0, 0, 0, 16, 0, 24, 0, 0, 0, 0, 0, 1, 0, 0, 0,
+ -92, -127, 68, 0, 0, 0, 103, 114, 101, 101, 116, 105, 110, 103, 115, 47,
+ 101, 110, 46, 116, 120, 116, 85, 84, 5, 0, 3, 73, -6, -22, 83, 117, 120,
+ 11, 0, 1, 4, -4, 1, 0, 0, 4, 0, 0, 0, 0, 80, 75, 1, 2, 30, 3, 10, 0, 2, 0,
+ 0, 0, 89, 61, 13, 69, -27, 99, -56, -20, 28, 0, 0, 0, 28, 0, 0, 0, 16, 0,
+ 24, 0, 0, 0, 0, 0, 1, 0, 0, 0, -92, -127, -82, 0, 0, 0, 103, 114, 101,
+ 101, 116, 105, 110, 103, 115, 47, 101, 115, 46, 116, 120, 116, 85, 84, 5,
+ 0, 3, -39, -6, -22, 83, 117, 120, 11, 0, 1, 4, -4, 1, 0, 0, 4, 0, 0, 0, 0,
+ 80, 75, 1, 2, 30, 3, 10, 0, 2, 0, 0, 0, 42, 61, 13, 69, -2, -58, -42, 30,
+ 25, 0, 0, 0, 25, 0, 0, 0, 16, 0, 24, 0, 0, 0, 0, 0, 1, 0, 0, 0, -92, -127,
+ 20, 1, 0, 0, 103, 114, 101, 101, 116, 105, 110, 103, 115, 47, 102, 114,
+ 46, 116, 120, 116, 85, 84, 5, 0, 3, -128, -6, -22, 83, 117, 120, 11, 0, 1,
+ 4, -4, 1, 0, 0, 4, 0, 0, 0, 0, 80, 75, 1, 2, 30, 3, 10, 0, 2, 0, 0, 0, 24,
+ 61, 13, 69, -26, -5, 76, 91, 40, 0, 0, 0, 40, 0, 0, 0, 16, 0, 24, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0, -92, -127, 119, 1, 0, 0, 103, 114, 101, 101, 116, 105,
+ 110, 103, 115, 47, 106, 97, 46, 116, 120, 116, 85, 84, 5, 0, 3, 96, -6,
+ -22, 83, 117, 120, 11, 0, 1, 4, -4, 1, 0, 0, 4, 0, 0, 0, 0, 80, 75, 1, 2,
+ 30, 3, 10, 0, 0, 0, 0, 0, 6, 62, 13, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 7, 0, 24, 0, 0, 0, 0, 0, 0, 0, 16, 0, -19, 65, -23, 1, 0, 0, 98, 105,
+ 110, 97, 114, 121, 47, 85, 84, 5, 0, 3, 28, -4, -22, 83, 117, 120, 11, 0,
+ 1, 4, -4, 1, 0, 0, 4, 0, 0, 0, 0, 80, 75, 1, 2, 30, 3, 10, 0, 2, 0, 0, 0,
+ 3, 62, 13, 69, -7, 93, 98, 55, 51, 0, 0, 0, 51, 0, 0, 0, 24, 0, 24, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, -92, -127, 42, 2, 0, 0, 98, 105, 110, 97, 114, 121,
+ 47, 98, 121, 116, 101, 115, 95, 48, 95, 116, 111, 95, 53, 48, 46, 98, 105,
+ 110, 85, 84, 5, 0, 3, 22, -4, -22, 83, 117, 120, 11, 0, 1, 4, -4, 1, 0, 0,
+ 4, 0, 0, 0, 0, 80, 75, 5, 6, 0, 0, 0, 0, 7, 0, 7, 0, 83, 2, 0, 0, -81, 2,
+ 0, 0, 0, 0)
+
+}
diff --git a/examples/scala-js/javalib-ex/src/main/scala/java/io/DataInputStream.scala b/examples/scala-js/javalib-ex/src/main/scala/java/io/DataInputStream.scala
new file mode 100644
index 0000000..bd11731
--- /dev/null
+++ b/examples/scala-js/javalib-ex/src/main/scala/java/io/DataInputStream.scala
@@ -0,0 +1,274 @@
+package java.io
+
+import scala.scalajs.js.typedarray._
+
+class DataInputStream(in: InputStream) extends FilterInputStream(in)
+ with DataInput {
+
+ private var pushedBack: Int = -1
+ private var pushedBackMark: Int = -1
+
+ // -- ArrayBufferInputStream mode helpers --
+ // These variables are used to special case on ArrayBufferInputStreams
+ // They allow directly accessing the underlying ArrayBuffer rather than
+ // creating byte arrays first
+ private val inArrayBufferStream = in match {
+ case in: ArrayBufferInputStream => in
+ case _ => null
+ }
+ private val hasArrayBuffer = inArrayBufferStream != null
+ private val bufDataView = {
+ if (hasArrayBuffer) {
+ val in = inArrayBufferStream
+ new DataView(in.buffer, in.offset, in.length)
+ } else null
+ }
+
+ private def consumePos(n: Int) = {
+ val off = if (pushedBack != -1) 1 else 0
+ val resultPos = inArrayBufferStream.pos - off
+ val toSkip = n - off
+ if (in.skip(toSkip) != toSkip) eof()
+ resultPos
+ }
+
+ // -- General InputStream mode helpers --
+ // Due to the method readLine, we need to be able to push back a byte (if we
+ // read a \r and the following byte is NOT a \n). We implement this here.
+ // We also provide a method to create an ad-hoc data view of the next n bytes
+ private val convBufLen = 8
+ private val convBuf = new ArrayBuffer(convBufLen)
+ private val convInView = new Int8Array(convBuf)
+ private val convOutView = new DataView(convBuf)
+ private def view(len: Int) = {
+ assert(len <= convBufLen)
+ var i = 0
+ while (i < len) {
+ val byte = read()
+ if (byte == -1) eof()
+ convInView(i) = byte.toByte
+ i += 1
+ }
+ convOutView
+ }
+
+ // General Helpers
+ private def eof() = throw new EOFException()
+ private def pushBack(v: Int) = { pushedBack = v }
+
+ // Methods on DataInput
+ def readBoolean(): Boolean = readByte() != 0
+
+ def readByte(): Byte = {
+ val res = read()
+ if (res == -1) eof()
+ res.toByte
+ }
+
+ def readChar(): Char = {
+ if (hasArrayBuffer)
+ bufDataView.getUint16(consumePos(2)).toChar
+ else
+ view(2).getUint16(0).toChar
+ }
+
+ def readDouble(): Double = {
+ if (hasArrayBuffer)
+ bufDataView.getFloat64(consumePos(8))
+ else
+ view(8).getFloat64(0)
+ }
+
+ def readFloat(): Float = {
+ if (hasArrayBuffer)
+ bufDataView.getFloat32(consumePos(4))
+ else
+ view(4).getFloat32(0)
+ }
+
+ def readFully(b: Array[Byte]): Unit = readFully(b, 0, b.length)
+
+ def readFully(b: Array[Byte], off: Int, len: Int): Unit = {
+ if (off < 0 || len < 0 || off + len > b.length)
+ throw new IndexOutOfBoundsException()
+
+ var remaining = len
+ var offset = off
+ while (remaining > 0) {
+ val readCount = read(b, offset, remaining)
+ if (readCount == -1) eof()
+ remaining -= readCount
+ offset += readCount
+ }
+ }
+
+ def readInt(): Int = {
+ if (hasArrayBuffer)
+ bufDataView.getInt32(consumePos(4))
+ else
+ view(4).getInt32(0)
+ }
+
+ def readLine(): String = {
+ var cur = read()
+ if (cur == -1) null
+ else {
+ var res = ""
+ while (cur != -1 && cur != '\n' && cur != '\r') {
+ res += cur.toChar
+ cur = read()
+ }
+ if (cur == '\r') {
+ // Discard a potential \n (from \r\n line endings)
+ cur = read()
+ if (cur != '\n') pushBack(cur)
+ }
+ res
+ }
+ }
+
+ def readLong(): Long = {
+ val hi = readInt().toLong
+ val lo = readInt().toLong
+ (hi << 32) | (lo & 0xFFFFFFFFL)
+ }
+
+ def readShort(): Short = {
+ if (hasArrayBuffer)
+ bufDataView.getInt16(consumePos(2))
+ else
+ view(2).getInt16(0)
+ }
+
+ def readUnsignedByte(): Int = {
+ val res = read()
+ if (res == -1) eof()
+ res
+ }
+
+ def readUnsignedShort(): Int = {
+ if (hasArrayBuffer)
+ bufDataView.getUint16(consumePos(2))
+ else
+ view(2).getUint16(0)
+ }
+
+ def readUTF(): String = {
+ val length = readShort()
+ var res = ""
+ var i = 0
+
+ def badFormat(msg: String) = throw new UTFDataFormatException(msg)
+
+ while (i < length) {
+ val a = read()
+
+ if (a == -1)
+ badFormat(s"Unexpected EOF: ${length - i} bytes to go")
+
+ i += 1
+
+ val char = {
+ if ((a & 0x80) == 0x00) { // 0xxxxxxx
+ a.toChar
+ } else if ((a & 0xE0) == 0xC0 && i < length) { // 110xxxxx
+ val b = read()
+ i += 1
+
+ if (b == -1)
+ badFormat(f"Expected 2 bytes, found: EOF (init: $a%#02x)")
+ if ((b & 0xC0) != 0x80) // 10xxxxxx
+ badFormat(f"Expected 2 bytes, found: $b%#02x (init: $a%#02x)")
+
+ (((a & 0x1F) << 6) | (b & 0x3F)).toChar
+ } else if ((a & 0xF0) == 0xE0 && i < length - 1) { // 1110xxxx
+ val b = read()
+ val c = read()
+ i += 2
+
+ if (b == -1)
+ badFormat(f"Expected 3 bytes, found: EOF (init: $a%#02x)")
+
+ if ((b & 0xC0) != 0x80) // 10xxxxxx
+ badFormat(f"Expected 3 bytes, found: $b%#02x (init: $a%#02x)")
+
+ if (c == -1)
+ badFormat(f"Expected 3 bytes, found: $b%#02x, EOF (init: $a%#02x)")
+
+ if ((c & 0xC0) != 0x80) // 10xxxxxx
+ badFormat(
+ f"Expected 3 bytes, found: $b%#02x, $c%#02x (init: $a%#02x)")
+
+ (((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F)).toChar
+ } else {
+ val rem = length - i
+ badFormat(f"Unexpected start of char: $a%#02x ($rem%d bytes to go)")
+ }
+ }
+
+ res += char
+ }
+
+ res
+ }
+
+ def skipBytes(n: Int): Int = skip(n.toLong).toInt
+
+ // Methods on FilterInputStream.
+ // Overridden to track pushedBack / pushedBackMark
+ override def available(): Int = {
+ if (pushedBack != -1) in.available + 1
+ else in.available
+ }
+
+ override def mark(readlimit: Int): Unit = {
+ in.mark(readlimit + 1) // we need one more since we might read ahead
+ pushedBackMark = pushedBack
+ }
+
+ override def markSupported(): Boolean = in.markSupported()
+
+ override def read(): Int = {
+ val res = {
+ if (pushedBack != -1)
+ pushedBack
+ else
+ in.read()
+ }
+
+ pushedBack = -1
+
+ res
+ }
+
+ override def read(b: Array[Byte], off: Int, len: Int): Int = {
+ if (len == 0)
+ 0
+ else if (pushedBack != -1) {
+ b(off) = pushedBack.toByte
+ pushedBack = -1
+ 1
+ } else {
+ val count = in.read(b, off, len)
+ count
+ }
+ }
+
+ override def reset(): Unit = {
+ in.reset()
+ pushedBack = pushedBackMark
+ }
+
+ override def skip(n: Long): Long = {
+ if (n == 0)
+ 0L
+ else if (pushedBack != -1) {
+ pushedBack = -1
+ 1L
+ } else {
+ val skipped = in.skip(n)
+ skipped
+ }
+ }
+
+}
diff --git a/examples/scala-js/javalib-ex/src/main/scala/java/util/zip/InflaterInputStream.scala b/examples/scala-js/javalib-ex/src/main/scala/java/util/zip/InflaterInputStream.scala
new file mode 100644
index 0000000..10aa04e
--- /dev/null
+++ b/examples/scala-js/javalib-ex/src/main/scala/java/util/zip/InflaterInputStream.scala
@@ -0,0 +1,18 @@
+package java.util.zip
+
+import java.io._
+
+class InflaterInputStream(in: InputStream) extends FilterInputStream(in) {
+
+ // Not implemented:
+ // def this(in: InputStream, inf: Inflater)
+ // def this(in: InputStream, inf: Inflater, size: Int)
+ // protected var buf: Array[Byte]
+ // protected var inf: Inflater
+ // protected var len: Int
+
+ override def markSupported(): Boolean = false
+ override def mark(readlimit: Int): Unit = {}
+ override def reset(): Unit = throw new IOException("Reset not supported")
+
+}
diff --git a/examples/scala-js/javalib-ex/src/main/scala/java/util/zip/ZipEntry.scala b/examples/scala-js/javalib-ex/src/main/scala/java/util/zip/ZipEntry.scala
new file mode 100644
index 0000000..89f37a3
--- /dev/null
+++ b/examples/scala-js/javalib-ex/src/main/scala/java/util/zip/ZipEntry.scala
@@ -0,0 +1,67 @@
+package java.util.zip
+
+class ZipEntry(private[this] val _name: String) extends Cloneable {
+
+ private[this] var _comment: String = null
+ private[this] var _csize: Long = -1
+ private[this] var _crc: Long = -1
+ private[this] var _extra: Array[Byte] = null
+ private[this] var _method: Int = -1
+ private[this] var _size: Long = -1
+ private[this] var _time: Long = -1
+
+ def this(e: ZipEntry) = {
+ this(e.getName())
+ setComment(e.getComment())
+ setCompressedSize(e.getCompressedSize())
+ setCrc(e.getCrc())
+ setExtra(e.getExtra())
+ setMethod(e.getMethod())
+ setSize(e.getSize())
+ setTime(e.getTime())
+ }
+
+ override def clone(): Object = {
+ val result = super.clone()
+ if (getExtra() != null)
+ setExtra(getExtra().clone().asInstanceOf[Array[Byte]])
+ result
+ }
+
+ def getComment(): String = _comment
+ def getCompressedSize(): Long = _csize
+ def getCrc(): Long = _crc
+ def getExtra(): Array[Byte] = _extra
+ def getMethod(): Int = _method
+ def getName(): String = _name
+ def getSize(): Long = _size
+ def getTime(): Long = _time
+
+ // Strangely, the Javalib defines hashCode, but not equals.
+ override def hashCode(): Int = {
+ import scala.util.hashing.MurmurHash3._
+
+ var acc = 0x45322
+ acc = mix(acc, getComment.##)
+ acc = mix(acc, getCompressedSize.##)
+ acc = mix(acc, getCrc.##)
+ acc = mix(acc, getExtra.##)
+ acc = mix(acc, getMethod.##)
+ acc = mix(acc, getName.##)
+ acc = mix(acc, getSize.##)
+ acc = mixLast(acc, getTime.##)
+ finalizeHash(acc, 8)
+ }
+
+ def isDirectory(): Boolean = _name.endsWith("/")
+
+ def setComment(comment: String): Unit = { _comment = comment }
+ def setCompressedSize(csize: Long): Unit = { _csize = csize }
+ def setCrc(crc: Long): Unit = { _crc = crc }
+ def setExtra(extra: Array[Byte]): Unit = { _extra = extra }
+ def setMethod(method: Int): Unit = { _method = method }
+ def setSize(size: Long): Unit = { _size = size }
+ def setTime(time: Long): Unit = { _time = time }
+ override def toString(): String = _name
+
+}
diff --git a/examples/scala-js/javalib-ex/src/main/scala/java/util/zip/ZipInputStream.scala b/examples/scala-js/javalib-ex/src/main/scala/java/util/zip/ZipInputStream.scala
new file mode 100644
index 0000000..082f6fc
--- /dev/null
+++ b/examples/scala-js/javalib-ex/src/main/scala/java/util/zip/ZipInputStream.scala
@@ -0,0 +1,87 @@
+package java.util.zip
+
+import java.io._
+
+import scala.scalajs.js
+import scala.scalajs.js.typedarray._
+
+class ZipInputStream(in: InputStream) extends InflaterInputStream(in) {
+
+ // Not implemented
+ // - All static constant fields (zip internals)
+ // - protected def createZipEntry(name: String): ZipEntry
+
+ private[this] val entryIter = {
+ import js.Dynamic.{global => g}
+
+ val data = in match {
+ case in: ArrayBufferInputStream =>
+ // Simulate reading all the data
+ while (in.skip(in.available()) > 0) {}
+ new Uint8Array(in.buffer, in.offset, in.length)
+ case _ =>
+ val arr = new js.Array[Int]
+ var x = in.read()
+ while (x != -1) {
+ arr.push(x)
+ x = in.read()
+ }
+ new Uint8Array(arr)
+ }
+
+ val zip = js.Dynamic.newInstance(g.JSZip)(data)
+ val entries = zip.files.asInstanceOf[js.Dictionary[js.Dynamic]]
+
+ entries.iterator
+ }
+
+ private[this] var inner: ArrayBufferInputStream = null
+
+ override def close(): Unit = {
+ closeEntry()
+ super.close()
+ }
+
+ override def available(): Int = {
+ if (inner == null || inner.available() <= 0) 0
+ else 1
+ }
+
+ def closeEntry(): Unit = {
+ if (inner != null)
+ inner.close()
+ inner = null
+ }
+
+ def getNextEntry(): ZipEntry = {
+ closeEntry()
+ if (entryIter.hasNext) {
+ val (name, jsEntry) = entryIter.next()
+ val res = new ZipEntry(name)
+ res.setTime(jsEntry.date.asInstanceOf[js.Date].getTime().toLong)
+ res.setComment(jsEntry.comment.asInstanceOf[String])
+
+ inner = new ArrayBufferInputStream(
+ jsEntry.asArrayBuffer().asInstanceOf[ArrayBuffer])
+
+ res
+ } else null
+ }
+
+ override def read(): Int = {
+ if (inner == null) -1
+ else inner.read()
+ }
+
+ override def read(buf: Array[Byte], off: Int, len: Int): Int = {
+ if (len == 0) 0
+ else if (inner == null) -1
+ else inner.read(buf, off, len)
+ }
+
+ override def skip(n: Long): Long = {
+ if (inner == null) 0
+ else inner.skip(n)
+ }
+
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/io/BufferedReader.scala b/examples/scala-js/javalib/src/main/scala/java/io/BufferedReader.scala
new file mode 100644
index 0000000..0f06523
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/io/BufferedReader.scala
@@ -0,0 +1,145 @@
+package java.io
+
+class BufferedReader(in: Reader, sz: Int) extends Reader {
+
+ def this(in: Reader) = this(in, 4096)
+
+ private[this] var buf = new Array[Char](sz)
+
+ /** Last valid value in the buffer (exclusive) */
+ private[this] var end = 0
+
+ /** Next position to read from buffer */
+ private[this] var pos = 0
+
+ private[this] var closed = false
+
+ private[this] var validMark = false
+
+ override def close(): Unit = {
+ closed = true
+ }
+
+ override def mark(readAheadLimit: Int): Unit = {
+ ensureOpen()
+
+ val srcBuf = buf
+ if (buf.size < readAheadLimit)
+ buf = new Array[Char](readAheadLimit)
+
+ // Move data to beginning of buffer
+ if (pos != 0 || (buf ne srcBuf))
+ System.arraycopy(srcBuf, pos, buf, 0, end - pos)
+
+ // Update internal state
+ end -= pos
+ pos = 0
+ validMark = true
+ }
+
+ override def markSupported(): Boolean = true
+
+ override def read(): Int = {
+ ensureOpen()
+
+ if (prepareRead()) {
+ val res = buf(pos).toInt
+ pos += 1
+ res
+ } else -1
+ }
+
+ override def read(cbuf: Array[Char], off: Int, len: Int): Int = {
+ ensureOpen()
+
+ if (off < 0 || len < 0 || len > cbuf.length - off)
+ throw new IndexOutOfBoundsException
+
+ if (len == 0) 0
+ else if (prepareRead()) {
+ val count = Math.min(len, end - pos)
+ System.arraycopy(this.buf, pos, cbuf, off, count)
+ pos += count
+ count
+ } else -1
+ }
+
+ def readLine(): String = {
+ ensureOpen()
+
+ var res = ""
+
+ while (prepareRead() && buf(pos) != '\n' && buf(pos) != '\r') {
+ res += buf(pos)
+ pos += 1
+ }
+
+ if (pos >= end) {
+ // We have reached the end of the stream (prepareRead() returned false)
+ if (res == "") null
+ else res
+ } else {
+ // Consume terminator
+ pos += 1
+
+ // Check whether we have a \r\n. This may overrun the buffer
+ // and then push a value back which may unnecessarily invalidate
+ // the mark. This mimics java behavior
+ if (buf(pos-1) == '\r' && prepareRead() && buf(pos) == '\n')
+ pos += 1 // consume '\n'
+
+ res
+ }
+ }
+
+ override def ready(): Boolean = {
+ ensureOpen()
+ pos < end || in.ready()
+ }
+
+ override def reset(): Unit = {
+ ensureOpen()
+
+ if (!validMark) throw new IOException("Mark invalid")
+ pos = 0
+ }
+
+ override def skip(n: Long): Long = {
+ if (n < 0) throw new IllegalArgumentException("n negative")
+ else if (pos < end) {
+ val count = Math.min(n, end - pos).toInt
+ pos += count
+ count.toLong
+ } else {
+ validMark = false
+ in.skip(n)
+ }
+ }
+
+ /** Prepare the buffer for reading. Returns false if EOF */
+ private def prepareRead(): Boolean =
+ pos < end || fillBuffer()
+
+ /** Tries to fill the buffer. Returns false if EOF */
+ private def fillBuffer(): Boolean = {
+ if (validMark && end < buf.length) {
+ // we may not do a full re-read, since we'll damage the mark.
+ val read = in.read(buf, end, buf.length - end)
+ if (read > 0) // protect from adding -1
+ end += read
+ read > 0
+ } else {
+ // Full re-read
+ validMark = false
+ end = in.read(buf)
+ pos = 0
+ end > 0
+ }
+ }
+
+ private def ensureOpen(): Unit = {
+ if (closed)
+ throw new IOException("Operation on closed stream")
+ }
+
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/io/ByteArrayInputStream.scala b/examples/scala-js/javalib/src/main/scala/java/io/ByteArrayInputStream.scala
new file mode 100644
index 0000000..697e07b
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/io/ByteArrayInputStream.scala
@@ -0,0 +1,58 @@
+package java.io
+
+class ByteArrayInputStream(
+ protected val buf: Array[Byte],
+ offset: Int, length: Int) extends InputStream {
+
+ protected val count: Int = offset + length
+ protected var mark: Int = offset
+ protected var pos: Int = offset
+
+ def this(buf: Array[Byte]) = this(buf, 0, buf.length)
+
+ override def read(): Int = {
+ if (pos >= count)
+ -1
+ else {
+ val res = buf(pos) & 0xFF // convert to unsigned int
+ pos += 1
+ res
+ }
+ }
+
+ override def read(b: Array[Byte], off: Int, reqLen: Int): Int = {
+ if (off < 0 || reqLen < 0 || reqLen > b.length - off)
+ throw new IndexOutOfBoundsException
+
+ val len = Math.min(reqLen, count - pos)
+
+ if (reqLen == 0)
+ 0 // 0 requested, 0 returned
+ else if (len == 0)
+ -1 // nothing to read at all
+ else {
+ System.arraycopy(buf, pos, b, off, len)
+ pos += len
+ len
+ }
+ }
+
+ override def skip(n: Long): Long = {
+ val k = Math.max(0, Math.min(n, count - pos))
+ pos += k.toInt
+ k.toLong
+ }
+
+ override def available(): Int = count - pos
+
+ override def markSupported(): Boolean = true
+
+ override def mark(readlimit: Int): Unit =
+ mark = pos
+
+ override def reset(): Unit =
+ pos = mark
+
+ override def close(): Unit = ()
+
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/io/ByteArrayOutputStream.scala b/examples/scala-js/javalib/src/main/scala/java/io/ByteArrayOutputStream.scala
new file mode 100644
index 0000000..916002d
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/io/ByteArrayOutputStream.scala
@@ -0,0 +1,62 @@
+package java.io
+
+import scala.scalajs.js
+
+import scala.annotation.tailrec
+
+class ByteArrayOutputStream(initBufSize: Int) extends OutputStream {
+
+ protected var buf: Array[Byte] = new Array(initBufSize)
+ protected var count: Int = 0
+
+ def this() = this(32)
+
+ override def write(b: Int): Unit = {
+ if (count >= buf.length)
+ growBuf(1)
+
+ buf(count) = b.toByte
+ count += 1
+ }
+
+ override def write(b: Array[Byte], off: Int, len: Int): Unit = {
+ if (off < 0 || len < 0 || len > b.length - off)
+ throw new IndexOutOfBoundsException()
+
+ if (count + len > buf.length)
+ growBuf(len)
+
+ System.arraycopy(b, off, buf, count, len)
+ count += len
+ }
+
+ def writeTo(out: OutputStream): Unit =
+ out.write(buf, 0, count)
+
+ def reset(): Unit =
+ count = 0
+
+ def toByteArray(): Array[Byte] = {
+ val res = new Array[Byte](count)
+ System.arraycopy(buf, 0, res, 0, count)
+ res
+ }
+
+ def size(): Int = count
+
+ override def toString(): String =
+ new String(buf, 0, count)
+
+ def toString(charsetName: String): String =
+ new String(buf, 0, count, charsetName)
+
+ override def close(): Unit = ()
+
+ private def growBuf(minIncrement: Int): Unit = {
+ val newSize = Math.max(count + minIncrement, buf.length * 2)
+ val newBuf = new Array[Byte](newSize)
+ System.arraycopy(buf, 0, newBuf, 0, count)
+ buf = newBuf
+ }
+
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/io/Closeable.scala b/examples/scala-js/javalib/src/main/scala/java/io/Closeable.scala
new file mode 100644
index 0000000..e572390
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/io/Closeable.scala
@@ -0,0 +1,6 @@
+package java.io
+
+/** Note that Closeable doesn't extend AutoCloseable for Java6 compat */
+trait Closeable {
+ def close(): Unit
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/io/DataInput.scala b/examples/scala-js/javalib/src/main/scala/java/io/DataInput.scala
new file mode 100644
index 0000000..37913b4
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/io/DataInput.scala
@@ -0,0 +1,19 @@
+package java.io
+
+trait DataInput {
+ def readBoolean(): Boolean
+ def readByte(): Byte
+ def readChar(): Char
+ def readDouble(): Double
+ def readFloat(): Float
+ def readFully(b: Array[Byte]): Unit
+ def readFully(b: Array[Byte], off: Int, len: Int): Unit
+ def readInt(): Int
+ def readLine(): String
+ def readLong(): Long
+ def readShort(): Short
+ def readUnsignedByte(): Int
+ def readUnsignedShort(): Int
+ def readUTF(): String
+ def skipBytes(n: Int): Int
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/io/FilterInputStream.scala b/examples/scala-js/javalib/src/main/scala/java/io/FilterInputStream.scala
new file mode 100644
index 0000000..a85b9f6
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/io/FilterInputStream.scala
@@ -0,0 +1,24 @@
+package java.io
+
+class FilterInputStream protected (
+ protected val in: InputStream) extends InputStream {
+
+ override def read(): Int =
+ in.read()
+
+ override def read(b: Array[Byte]): Int =
+ read(b, 0, b.length) // this is spec! must not do in.read(b)
+
+ override def read(b: Array[Byte], off: Int, len: Int): Int =
+ in.read(b, off, len)
+
+ override def skip(n: Long): Long = in.skip(n)
+
+ override def available(): Int = in.available()
+
+ override def close(): Unit = in.close()
+
+ override def mark(readlimit: Int): Unit = in.mark(readlimit)
+ override def markSupported(): Boolean = in.markSupported()
+ override def reset(): Unit = in.reset()
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/io/FilterOutputStream.scala b/examples/scala-js/javalib/src/main/scala/java/io/FilterOutputStream.scala
new file mode 100644
index 0000000..299b7b6
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/io/FilterOutputStream.scala
@@ -0,0 +1,16 @@
+package java.io
+
+class FilterOutputStream(protected val out: OutputStream) extends OutputStream {
+ def write(b: Int): Unit =
+ out.write(b)
+
+ override def write(b: Array[Byte]): Unit =
+ write(b, 0, b.length) // this is spec! it must not call out.write(b)
+
+ override def write(b: Array[Byte], off: Int, len: Int): Unit =
+ super.write(b, off, len) // calls this.write(Int) repeatedly
+
+ override def flush(): Unit = out.flush()
+
+ override def close(): Unit = out.close()
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/io/Flushable.scala b/examples/scala-js/javalib/src/main/scala/java/io/Flushable.scala
new file mode 100644
index 0000000..2879ad2
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/io/Flushable.scala
@@ -0,0 +1,5 @@
+package java.io
+
+trait Flushable {
+ def flush(): Unit
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/io/InputStream.scala b/examples/scala-js/javalib/src/main/scala/java/io/InputStream.scala
new file mode 100644
index 0000000..412d84b
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/io/InputStream.scala
@@ -0,0 +1,53 @@
+package java.io
+
+abstract class InputStream extends Closeable {
+ def read(): Int
+
+ def read(b: Array[Byte]): Int = read(b, 0, b.length)
+
+ def read(b: Array[Byte], off: Int, len: Int): Int = {
+ if (off < 0 || len < 0 || len > b.length - off)
+ throw new IndexOutOfBoundsException
+
+ if (len == 0) 0
+ else {
+ var bytesWritten = 0
+ var next = 0
+
+ while (bytesWritten < len && next != -1) {
+ next =
+ if (bytesWritten == 0) read()
+ else {
+ try read()
+ catch { case _: IOException => -1 }
+ }
+ if (next != -1) {
+ b(off + bytesWritten) = next.toByte
+ bytesWritten += 1
+ }
+ }
+
+ if (bytesWritten <= 0) -1
+ else bytesWritten
+ }
+ }
+
+ def skip(n: Long): Long = {
+ var skipped = 0
+ while (skipped < n && read() != -1)
+ skipped += 1
+ skipped
+ }
+
+ def available(): Int = 0
+
+ def close(): Unit = ()
+
+ def mark(readlimit: Int): Unit = ()
+
+ def reset(): Unit =
+ throw new IOException("Reset not supported")
+
+ def markSupported(): Boolean = false
+
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/io/InputStreamReader.scala b/examples/scala-js/javalib/src/main/scala/java/io/InputStreamReader.scala
new file mode 100644
index 0000000..1ef957c
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/io/InputStreamReader.scala
@@ -0,0 +1,216 @@
+package java.io
+
+import scala.annotation.tailrec
+
+import java.nio._
+import java.nio.charset._
+
+class InputStreamReader(private[this] var in: InputStream,
+ private[this] var decoder: CharsetDecoder) extends Reader {
+
+ private[this] var closed: Boolean = false
+
+ /** Buffer in which to read bytes from the underlying input stream.
+ *
+ * Class invariant: contains bytes already read from `in` but not yet
+ * decoded.
+ */
+ private[this] var inBuf: ByteBuffer = ByteBuffer.allocate(4096)
+ inBuf.limit(0)
+
+ /** Tells whether the end of the underlying input stream has been reached.
+ * Class invariant: if true, then `in.read()` has returned -1.
+ */
+ private[this] var endOfInput: Boolean = false
+
+ /** Buffer in which to decode bytes into chars.
+ * Usually, it is not used, because we try to decode directly to the
+ * destination array. So as long as we do not really need one, we share
+ * an empty buffer.
+ *
+ * Class invariant: contains chars already decoded but not yet *read* by
+ * the user of this instance.
+ */
+ private[this] var outBuf: CharBuffer = InputStreamReader.CommonEmptyCharBuffer
+
+ def this(in: InputStream, charset: Charset) =
+ this(in,
+ charset.newDecoder
+ .onMalformedInput(CodingErrorAction.REPLACE)
+ .onUnmappableCharacter(CodingErrorAction.REPLACE))
+
+ def this(in: InputStream) =
+ this(in, Charset.defaultCharset)
+
+ def this(in: InputStream, charsetName: String) =
+ this(in, Charset.forName(charsetName))
+
+ def close(): Unit = {
+ closed = true
+ in = null
+ decoder = null
+ inBuf = null
+ outBuf = null
+ }
+
+ def getEncoding(): String =
+ if (closed) null else decoder.charset.name
+
+ override def read(): Int = {
+ ensureOpen()
+
+ if (outBuf.hasRemaining) outBuf.get()
+ else super.read()
+ }
+
+ def read(cbuf: Array[Char], off: Int, len: Int): Int = {
+ ensureOpen()
+
+ if (off < 0 || len < 0 || len > cbuf.length - off)
+ throw new IndexOutOfBoundsException
+
+ if (len == 0) 0
+ else if (outBuf.hasRemaining) {
+ // Reuse chars decoded last time
+ val available = Math.min(outBuf.remaining, len)
+ outBuf.get(cbuf, off, available)
+ available
+ } else {
+ // Try and decode directly into the destination array
+ val directOut = CharBuffer.wrap(cbuf, off, len)
+ val result = readImpl(directOut)
+ if (result != InputStreamReader.Overflow) {
+ result
+ } else {
+ /* There's not enough space in the destination array to receive even
+ * a tiny bit of output from the decoder. We need to decode to the
+ * outBuf instead.
+ * This happens typically when the next code point to decode is a
+ * supplementary character, and the given `len` is 1.
+ */
+ readMoreThroughOutBuf(cbuf, off, len)
+ }
+ }
+ }
+
+ // In a separate method because this is (hopefully) not a common case
+ private def readMoreThroughOutBuf(cbuf: Array[Char], off: Int, len: Int): Int = {
+ // Return outBuf to its full capacity
+ outBuf.limit(outBuf.capacity)
+ outBuf.position(0)
+
+ @tailrec // but not inline, this is not a common path
+ def loopWithOutBuf(desiredOutBufSize: Int): Int = {
+ if (outBuf.capacity < desiredOutBufSize)
+ outBuf = CharBuffer.allocate(desiredOutBufSize)
+ val charsRead = readImpl(outBuf)
+ if (charsRead == InputStreamReader.Overflow)
+ loopWithOutBuf(desiredOutBufSize*2)
+ else
+ charsRead
+ }
+
+ val charsRead = loopWithOutBuf(2*len)
+ assert(charsRead != 0) // can be -1, though
+ outBuf.flip()
+
+ if (charsRead == -1) -1
+ else {
+ val available = Math.min(charsRead, len)
+ outBuf.get(cbuf, off, available)
+ available
+ }
+ }
+
+ @tailrec
+ private def readImpl(out: CharBuffer): Int = {
+ val initPos = out.position
+ val result = decoder.decode(inBuf, out, endOfInput)
+
+ if (out.position != initPos) {
+ /* Good, we made progress, so we can return.
+ * Note that the `result` does not matter. Whether it's an underflow,
+ * an overflow, or even an error, if we read *something*, we can return
+ * that.
+ * The next invocation of read() will cause a new invocation of decode(),
+ * which will necessarily return the same result (but without advancing
+ * at all), which will cause one of the following cases to be handled.
+ */
+ out.position - initPos
+ } else if (result.isUnderflow) {
+ if (endOfInput) {
+ assert(!inBuf.hasRemaining,
+ "CharsetDecoder.decode() should not have returned UNDERFLOW when "+
+ "both endOfInput and inBuf.hasRemaining are true. It should have "+
+ "returned a MalformedInput error instead.")
+ // Flush
+ if (decoder.flush(out).isOverflow)
+ InputStreamReader.Overflow
+ else {
+ // Done
+ if (out.position == initPos) -1
+ else out.position - initPos
+ }
+ } else {
+ // We need to read more from the underlying input stream
+ if (inBuf.limit == inBuf.capacity) {
+ inBuf.compact()
+ if (!inBuf.hasRemaining) {
+ throw new AssertionError(
+ "Scala.js implementation restriction: " +
+ inBuf.capacity + " bytes do not seem to be enough for " +
+ getEncoding + " to decode a single code point. " +
+ "Please report this as a bug.")
+ }
+ inBuf.limit(inBuf.position)
+ inBuf.position(0)
+ }
+
+ /* Note that this stores the new data after the limit of the buffer.
+ * Further, note that we may read more bytes than strictly necessary,
+ * according to the specification of InputStreamReader.
+ */
+ val bytesRead =
+ in.read(inBuf.array, inBuf.limit, inBuf.capacity - inBuf.limit)
+
+ if (bytesRead == -1)
+ endOfInput = true
+ else
+ inBuf.limit(inBuf.limit + bytesRead)
+
+ readImpl(out)
+ }
+ } else if (result.isOverflow) {
+ InputStreamReader.Overflow
+ } else {
+ result.throwException()
+ throw new AssertionError("should not get here")
+ }
+ }
+
+ /* In theory, `in.available() > 0` is incorrect. We should return true only
+ * if there are enough bytes available to read at least one code point.
+ * However, this is how the JDK behaves, and even the JavaDoc suggests this
+ * is the expected behavior.
+ */
+ override def ready(): Boolean =
+ outBuf.hasRemaining || in.available() > 0
+
+ private def ensureOpen(): Unit = {
+ if (closed)
+ throw new IOException("Stream closed")
+ }
+
+}
+
+object InputStreamReader {
+ private final val Overflow = -2
+
+ /** Empty CharBuffer shared by all InputStreamReaders as long as they do
+ * not really need one.
+ * Since we do not use `mark()`, it is fine to share them, because `mark()`
+ * is the only piece of mutable state for an empty buffer. Everything else
+ * is effectively immutable (e.g., position and limit must always be 0).
+ */
+ private val CommonEmptyCharBuffer = CharBuffer.allocate(0)
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/io/OutputStream.scala b/examples/scala-js/javalib/src/main/scala/java/io/OutputStream.scala
new file mode 100644
index 0000000..729e69b
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/io/OutputStream.scala
@@ -0,0 +1,25 @@
+package java.io
+
+abstract class OutputStream extends Object with Closeable with Flushable {
+ def write(b: Int): Unit
+
+ def write(b: Array[Byte]): Unit =
+ write(b, 0, b.length)
+
+ def write(b: Array[Byte], off: Int, len: Int): Unit = {
+ if (off < 0 || len < 0 || len > b.length - off)
+ throw new IndexOutOfBoundsException()
+
+ var n = off
+ val stop = off + len
+ while (n < stop) {
+ write(b(n))
+ n += 1
+ }
+ }
+
+ def flush(): Unit = ()
+
+ def close(): Unit = ()
+
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/io/OutputStreamWriter.scala b/examples/scala-js/javalib/src/main/scala/java/io/OutputStreamWriter.scala
new file mode 100644
index 0000000..18c1c57
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/io/OutputStreamWriter.scala
@@ -0,0 +1,160 @@
+package java.io
+
+import scala.annotation.tailrec
+
+import java.nio._
+import java.nio.charset._
+
+class OutputStreamWriter(private[this] var out: OutputStream,
+ private[this] var enc: CharsetEncoder) extends Writer {
+
+ private[this] var closed: Boolean = false
+
+ /** Incoming buffer: pending Chars that have been written to this instance
+ * of OutputStreamWriter, but not yet encoded.
+ * Normally, this should always be at most 1 Char, if it is a high surrogate
+ * which ended up alone at the end of the input of a write().
+ */
+ private[this] var inBuf: String = ""
+
+ /** Outgoing buffer: Bytes that have been decoded (from `inBuf`), but not
+ * yet written to the underlying output stream.
+ * The valid bytes are between 0 and outBuf.position.
+ */
+ private[this] var outBuf: ByteBuffer = ByteBuffer.allocate(4096)
+
+ def this(out: OutputStream, cs: Charset) =
+ this(out,
+ cs.newEncoder
+ .onMalformedInput(CodingErrorAction.REPLACE)
+ .onUnmappableCharacter(CodingErrorAction.REPLACE))
+
+ def this(out: OutputStream) =
+ this(out, Charset.defaultCharset)
+
+ def this(out: OutputStream, charsetName: String) =
+ this(out, Charset.forName(charsetName))
+
+ def getEncoding(): String =
+ if (closed) null else enc.charset.name
+
+ override def write(c: Int): Unit =
+ write(c.toChar.toString, 0, 1)
+
+ override def write(cbuf: Array[Char], off: Int, len: Int): Unit =
+ writeImpl(CharBuffer.wrap(cbuf, off, len))
+
+ override def write(str: String, off: Int, len: Int): Unit =
+ writeImpl(CharBuffer.wrap(str, off, len))
+
+ private def writeImpl(cbuf: CharBuffer): Unit = {
+ ensureOpen()
+
+ val cbuf1 = if (inBuf != "") {
+ val fullInput = CharBuffer.wrap(inBuf + cbuf.toString)
+ inBuf = ""
+ fullInput
+ } else cbuf
+
+ @inline
+ @tailrec
+ def loopEncode(): Unit = {
+ val result = enc.encode(cbuf1, outBuf, false)
+ if (result.isUnderflow) ()
+ else if (result.isOverflow) {
+ makeRoomInOutBuf()
+ loopEncode()
+ } else {
+ result.throwException()
+ throw new AssertionError("should not get here")
+ }
+ }
+
+ loopEncode()
+ if (cbuf1.hasRemaining)
+ inBuf = cbuf1.toString
+ }
+
+ override def flush(): Unit = {
+ ensureOpen()
+ flushBuffer()
+ out.flush()
+ }
+
+ override def close(): Unit = if (!closed) {
+ // Finish up the input
+ @inline
+ @tailrec
+ def loopEncode(): Unit = {
+ val cbuf = CharBuffer.wrap(inBuf)
+ val result = enc.encode(cbuf, outBuf, true)
+ if (result.isUnderflow) {
+ assert(!cbuf.hasRemaining,
+ "CharsetEncoder.encode() should not have returned UNDERFLOW when "+
+ "both endOfInput and inBuf.hasRemaining are true. It should have "+
+ "returned a MalformedInput error instead.")
+ } else if (result.isOverflow) {
+ makeRoomInOutBuf()
+ loopEncode()
+ } else {
+ result.throwException()
+ throw new AssertionError("should not get here")
+ }
+ }
+
+ @inline
+ @tailrec
+ def loopFlush(): Unit = {
+ if (enc.flush(outBuf).isOverflow) {
+ makeRoomInOutBuf()
+ loopFlush()
+ }
+ }
+
+ loopEncode()
+ loopFlush()
+
+ // Flush before closing
+ flush()
+
+ // Close the underlying stream
+ out.close()
+
+ // Clean up all the resources
+ closed = true
+ out = null
+ enc = null
+ inBuf = null
+ outBuf = null
+ }
+
+ private def ensureOpen(): Unit = {
+ if (closed)
+ throw new IOException("Closed writer.")
+ }
+
+ private def makeRoomInOutBuf(): Unit = {
+ if (outBuf.position != 0) {
+ flushBuffer()
+ } else {
+ // Very unlikely (outBuf.capacity is not enough to encode a single code point)
+ outBuf.flip()
+ val newBuf = ByteBuffer.allocate(outBuf.capacity * 2)
+ newBuf.put(outBuf)
+ outBuf = newBuf
+ }
+ }
+
+ /** Flushes the internal buffer of this writer, but not the underlying
+ * output stream.
+ */
+ private[io] def flushBuffer(): Unit = {
+ ensureOpen()
+
+ // Don't use outBuf.flip() first, in case out.write() throws
+ // Hence, use 0 instead of position, and position instead of limit
+ out.write(outBuf.array, outBuf.arrayOffset, outBuf.position)
+ outBuf.clear()
+ }
+
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/io/PrintStream.scala b/examples/scala-js/javalib/src/main/scala/java/io/PrintStream.scala
new file mode 100644
index 0000000..68fa041
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/io/PrintStream.scala
@@ -0,0 +1,218 @@
+package java.io
+
+import java.nio.charset.Charset
+import java.util.Formatter
+
+class PrintStream private (_out: OutputStream, autoFlush: Boolean,
+ charset: Charset)
+ extends FilterOutputStream(_out) with Appendable with Closeable {
+
+ /* The way we handle charsets here is a bit tricky, because we want to
+ * minimize the area of reachability for normal programs.
+ *
+ * First, if nobody uses the constructor taking an explicit encoding, we
+ * don't want to reach Charset.forName(), which pulls in all of the
+ * implemented charsets.
+ *
+ * Second, most programs will reach PrintStream only because of
+ * java.lang.System.{out,err}, which are subclasses of PrintStream that do
+ * not actually need to encode anything: they override all of PrintStream's
+ * methods to bypass the encoding altogether, and hence don't even need
+ * the default charset.
+ *
+ * This is why we have:
+ * * A private constructor taking the Charset directly, instead of its name.
+ * * Which is allowed to be `null`, which stands for the default charset.
+ * * The default charset is only loaded lazily in the initializer of the
+ * encoder field.
+ */
+
+ def this(out: OutputStream) =
+ this(out, false, null: Charset)
+
+ def this(out: OutputStream, autoFlush: Boolean) =
+ this(out, autoFlush, null: Charset)
+
+ def this(out: OutputStream, autoFlush: Boolean, encoding: String) =
+ this(out, autoFlush, Charset.forName(encoding))
+
+ /* The following constructors, although implemented, will not link, since
+ * File, FileOutputStream and BufferedOutputStream are not implemented.
+ * They're here just in case a third-party library on the classpath
+ * implements those.
+ */
+ def this(file: File) =
+ this(new BufferedOutputStream(new FileOutputStream(file)))
+ def this(file: File, csn: String) =
+ this(new BufferedOutputStream(new FileOutputStream(file)), false, csn)
+ def this(fileName: String) =
+ this(new File(fileName))
+ def this(fileName: String, csn: String) =
+ this(new File(fileName), csn)
+
+ private lazy val encoder = {
+ val c =
+ if (charset == null) Charset.defaultCharset
+ else charset
+ /* We pass `this` as the output stream for the encoding writer so that
+ * we can apply auto-flushing. Note that this will flush() more often
+ * than required by the spec. It appears to be consistent with how the
+ * JDK behaves.
+ */
+ new OutputStreamWriter(this, c)
+ }
+
+ private var closing: Boolean = false
+ private var closed: Boolean = false
+ private var errorFlag: Boolean = false
+
+ override def flush(): Unit =
+ ensureOpenAndTrapIOExceptions(out.flush())
+
+ override def close(): Unit = trapIOExceptions {
+ if (!closing) {
+ closing = true
+ encoder.close()
+ flush()
+ closed = true
+ out.close()
+ }
+ }
+
+ def checkError(): Boolean = {
+ if (closed) {
+ /* Just check the error flag.
+ * Common sense would tell us to look at the underlying writer's
+ * checkError() result too (like we do in the not closed case below).
+ * But the JDK does not behave like that. So we don't either.
+ */
+ errorFlag
+ } else {
+ flush()
+ /* If the underlying writer is also a PrintStream, we also check its
+ * checkError() result. This is not clearly specified by the JavaDoc,
+ * but, experimentally, the JDK seems to behave that way.
+ */
+ errorFlag || (out match {
+ case out: PrintStream => out.checkError()
+ case _ => false
+ })
+ }
+ }
+
+ protected[io] def setError(): Unit = errorFlag = true
+ protected[io] def clearError(): Unit = errorFlag = false
+
+ /* Note that calling directly the write() methods will happily bypass the
+ * potential lone high surrogate that is buffered in the underlying
+ * OutputStreamWriter. This means that the following sequence of operations:
+ *
+ * ps.print('\ud83d') // high surrogate of PILE OF POO
+ * ps.write('a')
+ * ps.print('\udca9') // low surrogate of PILE OF POO
+ *
+ * will result in the following bytes being emitted to the underlying stream:
+ *
+ * a\ud83d\udca9
+ *
+ * i.e., first the 'a', then the PILE OF POO.
+ *
+ * This is consistent with the behavior of the JDK.
+ */
+
+ override def write(b: Int): Unit = {
+ ensureOpenAndTrapIOExceptions {
+ out.write(b)
+ if (autoFlush && b == '\n')
+ flush()
+ }
+ }
+
+ override def write(buf: Array[Byte], off: Int, len: Int): Unit = {
+ ensureOpenAndTrapIOExceptions {
+ out.write(buf, off, len)
+ if (autoFlush)
+ flush()
+ }
+ }
+
+ def print(b: Boolean): Unit = printString(String.valueOf(b))
+ def print(c: Char): Unit = printString(String.valueOf(c))
+ def print(i: Int): Unit = printString(String.valueOf(i))
+ def print(l: Long): Unit = printString(String.valueOf(l))
+ def print(f: Float): Unit = printString(String.valueOf(f))
+ def print(d: Double): Unit = printString(String.valueOf(d))
+ def print(s: String): Unit = printString(if (s == null) "null" else s)
+ def print(obj: AnyRef): Unit = printString(String.valueOf(obj))
+
+ private def printString(s: String): Unit = ensureOpenAndTrapIOExceptions {
+ encoder.write(s)
+ encoder.flushBuffer()
+ }
+
+ def print(s: Array[Char]): Unit = ensureOpenAndTrapIOExceptions {
+ encoder.write(s)
+ encoder.flushBuffer()
+ }
+
+ def println(): Unit = ensureOpenAndTrapIOExceptions {
+ encoder.write('\n') // In Scala.js the line separator is always LF
+ encoder.flushBuffer()
+ if (autoFlush)
+ flush()
+ }
+
+ def println(b: Boolean): Unit = { print(b); println() }
+ def println(c: Char): Unit = { print(c); println() }
+ def println(i: Int): Unit = { print(i); println() }
+ def println(l: Long): Unit = { print(l); println() }
+ def println(f: Float): Unit = { print(f); println() }
+ def println(d: Double): Unit = { print(d); println() }
+ def println(s: Array[Char]): Unit = { print(s); println() }
+ def println(s: String): Unit = { print(s); println() }
+ def println(obj: AnyRef): Unit = { print(obj); println() }
+
+ def printf(fmt: String, args: Array[Object]): PrintStream =
+ format(fmt, args)
+
+ // Not implemented:
+ //def printf(l: java.util.Locale, fmt: String, args: Array[Object]): PrintStream = ???
+
+ def format(fmt: String, args: Array[Object]): PrintStream = {
+ new Formatter(this).format(fmt, args)
+ this
+ }
+
+ // Not implemented:
+ //def format(l: java.util.Locale, fmt: String, args: Array[Object]): PrintStream = ???
+
+ def append(csq: CharSequence): PrintStream = {
+ print(if (csq == null) "null" else csq.toString)
+ this
+ }
+
+ def append(csq: CharSequence, start: Int, end: Int): PrintStream = {
+ val csq1 = if (csq == null) "null" else csq
+ print(csq1.subSequence(start, end).toString)
+ this
+ }
+
+ def append(c: Char): PrintStream = {
+ print(c)
+ this
+ }
+
+ @inline private[this] def trapIOExceptions(body: => Unit): Unit = {
+ try {
+ body
+ } catch {
+ case _: IOException => setError()
+ }
+ }
+
+ @inline private[this] def ensureOpenAndTrapIOExceptions(body: => Unit): Unit = {
+ if (closed) setError()
+ else trapIOExceptions(body)
+ }
+
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/io/PrintWriter.scala b/examples/scala-js/javalib/src/main/scala/java/io/PrintWriter.scala
new file mode 100644
index 0000000..4e693e0
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/io/PrintWriter.scala
@@ -0,0 +1,150 @@
+package java.io
+
+import java.util.Formatter
+
+class PrintWriter(protected[io] var out: Writer,
+ autoFlush: Boolean) extends Writer {
+
+ def this(out: Writer) = this(out, false)
+
+ def this(out: OutputStream, autoFlush: Boolean) =
+ this(new OutputStreamWriter(out), autoFlush)
+ def this(out: OutputStream) =
+ this(out, false)
+
+ /* The following constructors, although implemented, will not link, since
+ * File, FileOutputStream and BufferedOutputStream are not implemented.
+ * They're here just in case a third-party library on the classpath
+ * implements those.
+ */
+ def this(file: File) =
+ this(new BufferedOutputStream(new FileOutputStream(file)))
+ def this(file: File, csn: String) =
+ this(new OutputStreamWriter(new BufferedOutputStream(
+ new FileOutputStream(file)), csn))
+ def this(fileName: String) = this(new File(fileName))
+ def this(fileName: String, csn: String) = this(new File(fileName), csn)
+
+ private var closed: Boolean = false
+ private var errorFlag: Boolean = false
+
+ def flush(): Unit =
+ ensureOpenAndTrapIOExceptions(out.flush())
+
+ def close(): Unit = trapIOExceptions {
+ if (!closed) {
+ flush()
+ closed = true
+ out.close()
+ }
+ }
+
+ def checkError(): Boolean = {
+ if (closed) {
+ /* Just check the error flag.
+ * Common sense would tell us to look at the underlying writer's
+ * checkError() result too (like we do in the not closed case below).
+ * But the JDK does not behave like that. So we don't either.
+ */
+ errorFlag
+ } else {
+ flush()
+ /* If the underlying writer is also a PrintWriter, we also check its
+ * checkError() result. This is not clearly specified by the JavaDoc,
+ * but, experimentally, the JDK seems to behave that way.
+ */
+ errorFlag || (out match {
+ case out: PrintWriter => out.checkError()
+ case _ => false
+ })
+ }
+ }
+
+ protected[io] def setError(): Unit = errorFlag = true
+ protected[io] def clearError(): Unit = errorFlag = false
+
+ override def write(c: Int): Unit =
+ ensureOpenAndTrapIOExceptions(out.write(c))
+
+ override def write(buf: Array[Char], off: Int, len: Int): Unit =
+ ensureOpenAndTrapIOExceptions(out.write(buf, off, len))
+
+ override def write(buf: Array[Char]): Unit =
+ ensureOpenAndTrapIOExceptions(out.write(buf))
+
+ override def write(s: String, off: Int, len: Int): Unit =
+ ensureOpenAndTrapIOExceptions(out.write(s, off, len))
+
+ override def write(s: String): Unit =
+ ensureOpenAndTrapIOExceptions(out.write(s))
+
+ def print(b: Boolean): Unit = write(String.valueOf(b))
+ def print(c: Char): Unit = write(c)
+ def print(i: Int): Unit = write(String.valueOf(i))
+ def print(l: Long): Unit = write(String.valueOf(l))
+ def print(f: Float): Unit = write(String.valueOf(f))
+ def print(d: Double): Unit = write(String.valueOf(d))
+ def print(s: Array[Char]): Unit = write(s)
+ def print(s: String): Unit = write(if (s == null) "null" else s)
+ def print(obj: AnyRef): Unit = write(String.valueOf(obj))
+
+ def println(): Unit = {
+ write('\n') // In Scala.js the line separator is always LF
+ if (autoFlush)
+ flush()
+ }
+
+ def println(b: Boolean): Unit = { print(b); println() }
+ def println(c: Char): Unit = { print(c); println() }
+ def println(i: Int): Unit = { print(i); println() }
+ def println(l: Long): Unit = { print(l); println() }
+ def println(f: Float): Unit = { print(f); println() }
+ def println(d: Double): Unit = { print(d); println() }
+ def println(s: Array[Char]): Unit = { print(s); println() }
+ def println(s: String): Unit = { print(s); println() }
+ def println(obj: AnyRef): Unit = { print(obj); println() }
+
+ def printf(fmt: String, args: Array[Object]): PrintWriter =
+ format(fmt, args)
+
+ // Not implemented:
+ //def printf(l: java.util.Locale, fmt: String, args: Array[Object]): PrintWriter = ???
+
+ def format(fmt: String, args: Array[Object]): PrintWriter = {
+ new Formatter(this).format(fmt, args)
+ if (autoFlush)
+ flush()
+ this
+ }
+
+ // Not implemented:
+ //def format(l: java.util.Locale, fmt: String, args: Array[Object]): PrintWriter = ???
+
+ override def append(csq: CharSequence): PrintWriter = {
+ super.append(csq)
+ this
+ }
+
+ override def append(csq: CharSequence, start: Int, end: Int): PrintWriter = {
+ super.append(csq, start, end)
+ this
+ }
+
+ override def append(c: Char): PrintWriter = {
+ super.append(c)
+ this
+ }
+
+ @inline private[this] def trapIOExceptions(body: => Unit): Unit = {
+ try {
+ body
+ } catch {
+ case _: IOException => setError()
+ }
+ }
+
+ @inline private[this] def ensureOpenAndTrapIOExceptions(body: => Unit): Unit = {
+ if (closed) setError()
+ else trapIOExceptions(body)
+ }
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/io/Reader.scala b/examples/scala-js/javalib/src/main/scala/java/io/Reader.scala
new file mode 100644
index 0000000..97be140
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/io/Reader.scala
@@ -0,0 +1,60 @@
+package java.io
+
+import java.nio.CharBuffer
+
+abstract class Reader private[this] (_lock: Option[Object])
+ extends Readable with Closeable {
+
+ protected val lock = _lock.getOrElse(this)
+
+ protected def this(lock: Object) = this(Some(lock))
+ protected def this() = this(None)
+
+ def read(target: CharBuffer): Int = {
+ if (!target.hasRemaining) 0
+ else if (target.hasArray) {
+ val charsRead = read(target.array,
+ target.position + target.arrayOffset, target.remaining)
+ if (charsRead != -1)
+ target.position(target.position + charsRead)
+ charsRead
+ } else {
+ val buf = new Array[Char](target.remaining)
+ val charsRead = read(buf)
+ if (charsRead != -1)
+ target.put(buf, 0, charsRead)
+ charsRead
+ }
+ }
+
+ def read(): Int = {
+ val buf = new Array[Char](1)
+ if (read(buf) == -1) -1
+ else buf(0).toInt
+ }
+
+ def read(cbuf: Array[Char]): Int =
+ read(cbuf, 0, cbuf.length)
+
+ def read(cbuf: Array[Char], off: Int, len: Int): Int
+
+ def skip(n: Long): Long = {
+ if (n < 0)
+ throw new IllegalArgumentException("Cannot skip negative amount")
+ else if (read() == -1) 0
+ else 1
+ }
+
+ def ready(): Boolean = false
+
+ def markSupported(): Boolean = false
+
+ def mark(readAheadLimit: Int): Unit =
+ throw new IOException("Mark not supported")
+
+ def reset(): Unit =
+ throw new IOException("Reset not supported")
+
+ def close(): Unit
+
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/io/Serializable.scala b/examples/scala-js/javalib/src/main/scala/java/io/Serializable.scala
new file mode 100644
index 0000000..01dd228
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/io/Serializable.scala
@@ -0,0 +1,3 @@
+package java.io
+
+trait Serializable
diff --git a/examples/scala-js/javalib/src/main/scala/java/io/StringReader.scala b/examples/scala-js/javalib/src/main/scala/java/io/StringReader.scala
new file mode 100644
index 0000000..2ca8f90
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/io/StringReader.scala
@@ -0,0 +1,69 @@
+package java.io
+
+class StringReader(s: String) extends Reader {
+
+ private[this] var closed = false
+ private[this] var pos = 0
+ private[this] var mark = 0
+
+ override def close(): Unit = {
+ closed = true
+ }
+
+ override def mark(readAheadLimit: Int): Unit = {
+ ensureOpen()
+
+ mark = pos
+ }
+
+ override def markSupported(): Boolean = true
+
+ override def read(): Int = {
+ ensureOpen()
+
+ if (pos < s.length) {
+ val res = s.charAt(pos).toInt
+ pos += 1
+ res
+ } else -1
+ }
+
+ override def read(cbuf: Array[Char], off: Int, len: Int): Int = {
+ ensureOpen()
+
+ if (off < 0 || len < 0 || len > cbuf.length - off)
+ throw new IndexOutOfBoundsException
+
+ if (len == 0) 0
+ else {
+ val count = Math.min(len, s.length - pos)
+ var i = 0
+ while (i < count) {
+ cbuf(off + i) = s.charAt(pos + i)
+ i += 1
+ }
+ pos += count
+ count
+ }
+ }
+
+ override def ready(): Boolean = pos < s.length
+
+ override def reset(): Unit = {
+ ensureOpen()
+ pos = mark
+ }
+
+ override def skip(ns: Long): Long = {
+ // Apparently, StringReader.skip allows negative skips
+ val count = Math.max(Math.min(ns, s.length - pos).toInt, -pos)
+ pos += count
+ count.toLong
+ }
+
+ private def ensureOpen(): Unit = {
+ if (closed)
+ throw new IOException("Operation on closed stream")
+ }
+
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/io/StringWriter.scala b/examples/scala-js/javalib/src/main/scala/java/io/StringWriter.scala
new file mode 100644
index 0000000..13eca00
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/io/StringWriter.scala
@@ -0,0 +1,44 @@
+package java.io
+
+class StringWriter extends Writer {
+
+ private[this] val buf = new StringBuffer
+
+ def this(initialSize: Int) = this()
+
+ override def write(c: Int): Unit =
+ buf.append(c.toChar)
+
+ def write(cbuf: Array[Char], off: Int, len: Int): Unit =
+ buf.append(cbuf, off, len)
+
+ override def write(str: String): Unit =
+ buf.append(str)
+
+ override def write(str: String, off: Int, len: Int): Unit =
+ buf.append(str, off, off + len) // Third param is 'end', not 'len'
+
+ override def append(csq: CharSequence): StringWriter = {
+ buf.append(csq)
+ this
+ }
+
+ override def append(csq: CharSequence, start: Int, end: Int): StringWriter = {
+ buf.append(csq, start, end)
+ this
+ }
+
+ override def append(c: Char): StringWriter = {
+ buf.append(c)
+ this
+ }
+
+ override def toString(): String = buf.toString
+
+ def getBuffer(): StringBuffer = buf
+
+ def flush(): Unit = ()
+
+ def close(): Unit = ()
+
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/io/Throwables.scala b/examples/scala-js/javalib/src/main/scala/java/io/Throwables.scala
new file mode 100644
index 0000000..c312c4c
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/io/Throwables.scala
@@ -0,0 +1,19 @@
+package java.io
+
+class IOException(s: String, e: Throwable) extends Exception(s, e) {
+ def this(e: Throwable) = this(null, e)
+ def this(s: String) = this(s, null)
+ def this() = this(null, null)
+}
+
+class EOFException(s: String) extends IOException(s) {
+ def this() = this(null)
+}
+
+class UTFDataFormatException(s: String) extends IOException(s) {
+ def this() = this(null)
+}
+
+class UnsupportedEncodingException(s: String) extends IOException(s) {
+ def this() = this(null)
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/io/Writer.scala b/examples/scala-js/javalib/src/main/scala/java/io/Writer.scala
new file mode 100644
index 0000000..d63b477
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/io/Writer.scala
@@ -0,0 +1,45 @@
+package java.io
+
+abstract class Writer private[this] (_lock: Option[Object]) extends
+ Appendable with Closeable with Flushable {
+
+ protected val lock = _lock.getOrElse(this)
+
+ protected def this(lock: Object) = this(Some(lock))
+ protected def this() = this(None)
+
+ def write(c: Int): Unit =
+ write(Array(c.toChar))
+
+ def write(cbuf: Array[Char]): Unit =
+ write(cbuf, 0, cbuf.length)
+
+ def write(cbuf: Array[Char], off: Int, len: Int): Unit
+
+ def write(str: String): Unit =
+ write(str.toCharArray)
+
+ def write(str: String, off: Int, len: Int): Unit =
+ write(str.toCharArray, off, len)
+
+ def append(csq: CharSequence): Writer = {
+ write(if (csq == null) "null" else csq.toString)
+ this
+ }
+
+ def append(csq: CharSequence, start: Int, end: Int): Writer = {
+ val csq1 = if (csq == null) "null" else csq
+ write(csq1.subSequence(start, end).toString)
+ this
+ }
+
+ def append(c: Char): Writer = {
+ write(c.toInt)
+ this
+ }
+
+ def flush(): Unit
+
+ def close(): Unit
+
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/net/URI.scala b/examples/scala-js/javalib/src/main/scala/java/net/URI.scala
new file mode 100644
index 0000000..c969f55
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/net/URI.scala
@@ -0,0 +1,706 @@
+package java.net
+
+import scala.scalajs.js.RegExp
+import scala.scalajs.js
+import scala.scalajs.js.{decodeURIComponent => decode}
+
+import scala.annotation.tailrec
+
+final class URI(origStr: String) extends Serializable with Comparable[URI] {
+
+ import URI.Fields._
+
+ /** The fields matched in the regular expression.
+ *
+ * This is a local val for the primary constructor. It is a val,
+ * since we'll set it to null after initializing all fields.
+ */
+ private[this] var _fld = Option(URI.uriRe.exec(origStr)).getOrElse {
+ throw new URISyntaxException(origStr, "Malformed URI")
+ }
+
+ private val _isAbsolute = fld(AbsScheme).isDefined
+ private val _isOpaque = fld(AbsOpaquePart).isDefined
+
+ @inline private def fld(idx: Int): js.UndefOr[String] = _fld(idx)
+
+ @inline private def fld(absIdx: Int, relIdx: Int): js.UndefOr[String] =
+ if (_isAbsolute) _fld(absIdx) else _fld(relIdx)
+
+ private val _scheme = fld(AbsScheme)
+
+ private val _schemeSpecificPart = {
+ if (!_isAbsolute) fld(RelSchemeSpecificPart)
+ else if (_isOpaque) fld(AbsOpaquePart)
+ else fld(AbsHierPart)
+ }.get
+
+ private val _authority = fld(AbsAuthority, RelAuthority)
+ private val _userInfo = fld(AbsUserInfo, RelUserInfo)
+ private val _host = fld(AbsHost, RelHost)
+ private val _port = fld(AbsPort, RelPort).fold(-1)(_.toInt)
+
+ private val _path = {
+ if (_isAbsolute) {
+ if (_authority.isDefined) fld(AbsNetPath)
+ else fld(AbsAbsPath)
+ } else {
+ if (_authority.isDefined) fld(RelNetPath)
+ else fld(RelAbsPath) orElse fld(RelRelPath)
+ }
+ }
+
+ private val _query = fld(AbsQuery, RelQuery)
+ private val _fragment = fld(Fragment)
+
+ // End of default ctor. Unset helper field
+ _fld = null
+
+ def this(scheme: String, ssp: String, fragment: String) =
+ this(URI.uriStr(scheme, ssp, fragment))
+
+ def this(scheme: String, userInfo: String, host: String, port: Int,
+ path: String, query: String, fragment: String) = {
+ this(URI.uriStr(scheme, userInfo, host, port, path, query, fragment))
+ parseServerAuthority()
+ }
+
+ def this(scheme: String, host: String, path: String, fragment: String) =
+ this(scheme, null, host, -1, path, null, fragment)
+
+ def this(scheme: String, authority: String, path: String, query: String,
+ fragment: String) = {
+ this(URI.uriStr(scheme, authority, path, query, fragment))
+ // JavaDoc says to invoke parseServerAuthority() here, but in practice
+ // it isn't invoked. This makes sense, since you want to be able
+ // to create URIs with registry-based authorities.
+ // parseServerAuthority()
+ }
+
+ /** Compare this URI to another URI while supplying a comparator
+ *
+ * This helper is required to account for the semantic differences
+ * between [[compareTo]] and [[equals]]. ([[equals]] does treat
+ * URI escapes specially: they are never case-sensitive).
+ */
+ @inline
+ private def internalCompare(that: URI)(cmp: (String, String) => Int): Int = {
+ @inline def cmpOpt(x: js.UndefOr[String], y: js.UndefOr[String]): Int = {
+ if (x == y) 0
+ // Undefined components are considered less than defined components
+ else x.fold(-1)(s1 => y.fold(1)(s2 => cmp(s1, s2)))
+ }
+
+ if (this._scheme != that._scheme)
+ this._scheme.fold(-1)(s1 => that._scheme.fold(1)(s1.compareToIgnoreCase))
+ else if (this._isOpaque != that._isOpaque)
+ // A hierarchical URI is less than an opaque URI
+ if (this._isOpaque) 1 else -1
+ else if (_isOpaque) {
+ val ssp = cmp(this._schemeSpecificPart, that._schemeSpecificPart)
+ if (ssp != 0) ssp
+ else cmpOpt(this._fragment, that._fragment)
+ } else if (this._authority != that._authority) {
+ if (this._host.isDefined && that._host.isDefined) {
+ val ui = cmpOpt(this._userInfo, that._userInfo)
+ if (ui != 0) ui
+ else {
+ val hst = this._host.get.compareToIgnoreCase(that._host.get)
+ if (hst != 0) hst
+ else if (this._port == that._port) 0
+ else if (this._port == -1) -1
+ else if (that._port == -1) 1
+ else this._port - that._port
+ }
+ } else
+ cmpOpt(this._authority, that._authority)
+ } else if (this._path != that._path)
+ cmpOpt(this._path, that._path)
+ else if (this._query != that._query)
+ cmpOpt(this._query, that._query)
+ else
+ cmpOpt(this._fragment, that._fragment)
+ }
+
+ def compareTo(that: URI): Int = internalCompare(that)(_.compareTo(_))
+
+ override def equals(that: Any): Boolean = that match {
+ case that: URI => internalCompare(that)(URI.escapeAwareCompare) == 0
+ case _ => false
+ }
+
+ def getAuthority(): String = _authority.map(decode).orNull
+ def getFragment(): String = _fragment.map(decode).orNull
+ def getHost(): String = _host.orNull
+ def getPath(): String = _path.map(decode).orNull
+ def getPort(): Int = _port
+ def getQuery(): String = _query.map(decode).orNull
+ def getRawAuthority(): String = _authority.orNull
+ def getRawFragment(): String = _fragment.orNull
+ def getRawPath(): String = _path.orNull
+ def getRawQuery(): String = _query.orNull
+ def getRawSchemeSpecificPart(): String = _schemeSpecificPart
+ def getRawUserInfo(): String = _userInfo.orNull
+ def getScheme(): String = _scheme.orNull
+ def getSchemeSpecificPart(): String = decode(_schemeSpecificPart)
+ def getUserInfo(): String = _userInfo.map(decode).orNull
+
+ override def hashCode(): Int = {
+ import scala.util.hashing.MurmurHash3._
+ import URI.normalizeEscapes
+
+ var acc = URI.uriSeed
+ acc = mix(acc, _scheme.##) // scheme may not contain escapes
+ acc = mix(acc, normalizeEscapes(_schemeSpecificPart).##)
+ acc = mixLast(acc, _fragment.map(normalizeEscapes).##)
+
+ finalizeHash(acc, 3)
+ }
+
+ def isAbsolute(): Boolean = _isAbsolute
+ def isOpaque(): Boolean = _isOpaque
+
+ def normalize(): URI = if (_isOpaque || _path.isEmpty) this else {
+ val origPath = _path.get
+
+ // Step 1: Remove all "." segments
+ // Step 2: Remove ".." segments preceeded by non ".." segment until no
+ // longer applicable
+
+ /** Checks whether a successive ".." may drop the head of a
+ * reversed segment list.
+ */
+ def okToDropFrom(resRev: List[String]) =
+ resRev.nonEmpty && resRev.head != ".." && resRev.head != ""
+
+ @tailrec
+ def loop(in: List[String], resRev: List[String]): List[String] = in match {
+ case "." :: Nil =>
+ // convert "." segments at end to an empty segment
+ // (consider: /a/b/. => /a/b/, not /a/b)
+ loop(Nil, "" :: resRev)
+ case ".." :: Nil if okToDropFrom(resRev) =>
+ // prevent a ".." segment at end to change a "dir" into a "file"
+ // (consider: /a/b/.. => /a/, not /a)
+ loop(Nil, "" :: resRev.tail)
+ case "." :: xs =>
+ // remove "." segments
+ loop(xs, resRev)
+ case "" :: xs if xs.nonEmpty =>
+ // remove empty segments not at end of path
+ loop(xs, resRev)
+ case ".." :: xs if okToDropFrom(resRev) =>
+ // Remove preceeding non-".." segment
+ loop(xs, resRev.tail)
+ case x :: xs =>
+ loop(xs, x :: resRev)
+ case Nil =>
+ resRev.reverse
+ }
+
+ // Split into segments. -1 since we want empty trailing ones
+ val segments0 = origPath.split("/", -1).toList
+ val isAbsPath = segments0.nonEmpty && segments0.head == ""
+ // Don't inject first empty segment into normalization loop, so we
+ // won't need to special case it.
+ val segments1 = if (isAbsPath) segments0.tail else segments0
+ val segments2 = loop(segments1, Nil)
+
+ // Step 3: If path is relative and first segment contains ":", prepend "."
+ // segment (according to JavaDoc). If it is absolute, add empty
+ // segment again to have leading "/".
+ val segments3 = {
+ if (isAbsPath)
+ "" :: segments2
+ else if (segments2.nonEmpty && segments2.head.contains(':'))
+ "." :: segments2
+ else segments2
+ }
+
+ val newPath = segments3.mkString("/")
+
+ // Only create new instance if anything changed
+ if (newPath == origPath)
+ this
+ else
+ new URI(getScheme(), getRawAuthority(), newPath, getQuery(), getFragment())
+ }
+
+ def parseServerAuthority(): URI = {
+ if (_authority.nonEmpty && _host.isEmpty)
+ throw new URISyntaxException(origStr, "No Host in URI")
+ else this
+ }
+
+ def relativize(uri: URI): URI = {
+ def authoritiesEqual = this._authority.fold(uri._authority.isEmpty) { a1 =>
+ uri._authority.fold(false)(a2 => URI.escapeAwareCompare(a1, a2) == 0)
+ }
+
+ if (this.isOpaque || uri.isOpaque ||
+ this._scheme != uri._scheme || !authoritiesEqual) uri
+ else {
+ val thisN = this.normalize()
+ val uriN = uri.normalize()
+
+ // Strangely, Java doesn't handle escapes here. So we don't
+ if (uriN.getRawPath().startsWith(thisN.getRawPath())) {
+ val newPath = uriN.getRawPath().stripPrefix(thisN.getRawPath())
+
+ new URI(scheme = null, authority = null,
+ // never produce an abs path if we relativized
+ path = newPath.stripPrefix("/"),
+ query = uri.getQuery(), fragment = uri.getFragment())
+ } else uri
+ }
+ }
+
+ def resolve(str: String): URI = resolve(URI.create(str))
+
+ def resolve(uri: URI): URI = {
+ if (uri.isAbsolute() || this.isOpaque()) uri
+ else if (uri._scheme.isEmpty && uri._authority.isEmpty &&
+ uri._path.get == "" && uri._query.isEmpty)
+ // This is a special case for URIs like: "#foo". This allows to
+ // just change the fragment in the current document.
+ new URI(
+ this.getScheme(),
+ this.getRawAuthority(),
+ this.getRawPath(),
+ this.getRawQuery(),
+ uri.getRawFragment())
+ else if (uri._authority.isDefined)
+ new URI(
+ this.getScheme(),
+ uri.getRawAuthority(),
+ uri.getRawPath(),
+ uri.getRawQuery(),
+ uri.getRawFragment())
+ else if (uri._path.get.startsWith("/"))
+ new URI(
+ this.getScheme(),
+ this.getRawAuthority(),
+ uri.getRawPath(),
+ uri.getRawQuery(),
+ uri.getRawFragment())
+ else {
+ val basePath = this._path.get
+ val relPath = uri._path.get
+ val endIdx = basePath.lastIndexOf('/')
+ val path =
+ if (endIdx == -1) relPath
+ else basePath.substring(0, endIdx+1) + relPath
+ new URI(
+ this.getScheme(),
+ this.getAuthority(),
+ path,
+ uri.getRawQuery(),
+ uri.getRawFragment()).normalize()
+ }
+ }
+
+ def toASCIIString(): String = origStr // We allow only ASCII in URIs.
+ override def toString(): String = origStr
+
+ // Not implemented:
+ // def toURL(): URL
+
+}
+
+object URI {
+
+ def create(str: String): URI = {
+ try new URI(str)
+ catch {
+ case e: URISyntaxException => throw new IllegalArgumentException(e)
+ }
+ }
+
+ // IPv4address = 1*digit "." 1*digit "." 1*digit "." 1*digit
+ private final val ipv4address = "[0-9]{1,3}(?:\\.[0-9]{1,3}){3}"
+
+ private final val ipv6address = {
+ // http://stackoverflow.com/a/17871737/1149944
+ val block = "[0-9a-f]{1,4}"
+ val lelem = "(?:"+block+":)"
+ val relem = "(?::"+block+")"
+ val ipv4 = ipv4address
+
+ "(?:" +
+ lelem+"{7}"+block+"|"+ // 1:2:3:4:5:6:7:8
+ lelem+"{1,7}:|"+ // 1:: 1:2:3:4:5:6:7::
+ lelem+"{1,6}"+relem+"|"+ // 1::8 1:2:3:4:5:6::8 1:2:3:4:5:6::8
+ lelem+"{1,5}"+relem+"{1,2}|"+ // 1::7:8 1:2:3:4:5::7:8 1:2:3:4:5::8
+ lelem+"{1,4}"+relem+"{1,3}|"+ // 1::6:7:8 1:2:3:4::6:7:8 1:2:3:4::8
+ lelem+"{1,3}"+relem+"{1,4}|"+ // 1::5:6:7:8 1:2:3::5:6:7:8 1:2:3::8
+ lelem+"{1,2}"+relem+"{1,5}|"+ // 1::4:5:6:7:8 1:2::4:5:6:7:8 1:2::8
+ lelem +relem+"{1,6}|"+ // 1::3:4:5:6:7:8 1::3:4:5:6:7:8 1::8
+ ":(?:"+relem+"{1,7}|:)|" + // ::2:3:4:5:6:7:8 ::2:3:4:5:6:7:8 ::8 ::
+ lelem+"{6}"+ipv4+"|"+ // 1:2:3:4:5:6:10.0.0.1
+ lelem+"{1,5}:"+ipv4+"|"+ // 1::10.0.0.1 1:2:3:4:5::10.0.0.1
+ lelem+"{1,4}"+relem+":"+ipv4+"|"+ // 1::6:10.0.0.1 1:2:3:4::6:10.0.0.1
+ lelem+"{1,3}"+relem+"{1,2}:"+ipv4+"|"+ // 1::5:6:10.0.0.1 1:2:3::5:6:10.0.0.1 1:2:3::6:10.0.0.1
+ lelem+"{1,2}"+relem+"{1,3}:"+ipv4+"|"+ // 1::4:5:6:10.0.0.1 1:2::4:5:6:10.0.0.1 1:2::6:10.0.0.1
+ lelem +relem+"{1,4}:"+ipv4+"|"+ // 1::3:4:5:6:10.0.0.1 1::3:4:5:6:10.0.0.1 1::6:10.0.0.1
+ "::"+lelem+"{1,5}"+ipv4+ // ::2:3:4:5:10.0.0.1 ::5:10.0.0.1 ::10.0.0.1
+ ")(?:%[0-9a-z]+)?"
+
+ // This was part of the original regex, but is too specific to
+ // IPv6 details.
+ // fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}| # fe80::7:8%eth0 fe80::7:8%1 (link-local IPv6 addresses with zone index)
+ // ::(ffff(:0{1,4}){0,1}:){0,1}
+ // ((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}
+ // (25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])| # ::255.255.255.255 ::ffff:255.255.255.255 ::ffff:0:255.255.255.255 (IPv4-mapped IPv6 addresses and IPv4-translated addresses)
+ // ([0-9a-fA-F]{1,4}:){1,4}:
+ // ((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}
+ // (25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]) # 2001:db8:3:4::192.0.2.33 64:ff9b::192.0.2.33 (IPv4-Embedded IPv6 Address)
+ }
+
+ private val ipv6Re = new RegExp("^"+ipv6address+"$", "i")
+
+ // URI syntax parser. Based on RFC2396, RFC2732 and adaptations according to
+ // JavaDoc.
+ // - http://www.ietf.org/rfc/rfc2396.txt (see Appendix A for complete syntax)
+ // - http://www.ietf.org/rfc/rfc2732.txt
+
+ private val uriRe = {
+ // We don't use any interpolators here to allow for constant folding
+
+ ///////////////////
+ //// Helpers ////
+ ///////////////////
+
+ // Inlined definitions
+ // reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" |
+ // "$" | "," | "[" | "]" ; last two added by RFC2732
+ // unreserved = alphanum | mark
+ // mark = "-" | "_" | "." | "!" | "~" | "*" | "'" |
+ // "(" | ")"
+
+ // escaped = "%" hex hex
+ val escaped = "%[a-f0-9]{2}"
+
+ // uric = reserved | unreserved | escaped
+ val uric = "(?:[;/?:@&=+$,\\[\\]a-z0-9-_.!~*'()]|"+escaped+")"
+
+ // pchar = unreserved | escaped |
+ // ":" | "@" | "&" | "=" | "+" | "$" | ","
+ val pchar = "(?:[a-z0-9-_.!~*'():@&=+$,]|"+escaped+")"
+
+ ///////////////////
+ //// Server ////
+ ///////////////////
+
+ // domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
+ val domainlabel = "(?:[a-z0-9]|[a-z0-9][a-z0-9-]*[a-z0-9])"
+
+ // toplabel = alpha | alpha *( alphanum | "-" ) alphanum
+ val toplabel = "(?:[a-z]|[a-z][a-z0-9-]*[a-z0-9])"
+
+ // hostname = *( domainlabel "." ) toplabel [ "." ]
+ val hostname = "(?:"+domainlabel+"\\.)*"+toplabel+"\\.?"
+
+ // IPv6reference = "[" IPv6address "]"
+ val ipv6reference = "\\[(?:"+ipv6address+")\\]"
+
+ // host = hostname | IPv4address | IPv6reference
+ // ; IPv6reference added by RFC2732
+ val host = "("+hostname+"|"+ipv4address+"|"+ipv6reference+")" /*CAPT*/
+
+ // Inlined definition
+ // port = *digit
+
+ // hostport = host [ ":" port ]
+ val hostport = host+"(?::([0-9]*))?" /*CAPT*/
+
+ // userinfo = *( unreserved | escaped |
+ // ";" | ":" | "&" | "=" | "+" | "$" | "," )
+ val userinfo = "(?:[a-z0-9-_.!~*'();:&=+$,]|"+escaped+")*"
+
+ // server = [ [ userinfo "@" ] hostport ]
+ val server = "(?:(?:("+userinfo+")@)?"+hostport+")?" /*CAPT*/
+
+ ///////////////////
+ //// Authority ////
+ ///////////////////
+
+ // reg_name = 1*( unreserved | escaped | "$" | "," |
+ // ";" | ":" | "@" | "&" | "=" | "+" )
+ val reg_name = "(?:[a-z0-9-_.!~*'()$,;:@&=+]|"+escaped+")+"
+
+ // authority = server | reg_name
+ val authority = server+"|"+reg_name
+
+ ///////////////////
+ //// Paths ////
+ ///////////////////
+
+ // Inlined definitions
+ // param = *pchar
+
+ // segment = *pchar *( ";" param )
+ val segment = pchar+"*(?:;"+pchar+"*)*"
+
+ // path_segments = segment *( "/" segment )
+ val path_segments = segment+"(?:/"+segment+")*"
+
+ // abs_path = "/" path_segments
+ val abs_path = "/"+path_segments
+
+ // net_path = "//" authority [ abs_path ]
+ val net_path = "//("+authority+")("+abs_path+")?" /*2CAPT*/
+
+ // Inlined definition
+ // Deviation from RCF2396 according to JavaDoc: Allow empty rel_segment
+ // and hence empty rel_path
+ // rel_segment = 1*( unreserved | escaped |
+ // ";" | "@" | "&" | "=" | "+" | "$" | "," )
+
+ // rel_path = rel_segment [ abs_path ]
+ val rel_path = "(?:[a-z0-9-_.!~*'();@&=+$,]|"+escaped+")*(?:"+abs_path+")?"
+
+ ///////////////////
+ /// Query/Frag ///
+ ///////////////////
+
+ // query = *uric
+ val query = "("+uric+"*)" /*CAPT*/
+ // fragment = *uric
+ val fragment = "("+uric+"*)" /*CAPT*/
+
+ ///////////////////
+ /// Parts ///
+ ///////////////////
+
+ // hier_part = ( net_path | abs_path ) [ "?" query ]
+ val hier_part = "(?:"+net_path+"|("+abs_path+"))(?:\\?"+query+")?" /*CAPT*/
+
+ // Inlined definition
+ // uric_no_slash = unreserved | escaped | ";" | "?" | ":" | "@" |
+ // "&" | "=" | "+" | "$" | ","
+
+ // opaque_part = uric_no_slash *uric
+ val opaque_part = "(?:[a-z0-9-_.!~*'();?:@&=+$,]|"+escaped+")"+uric+"*"
+
+ ///////////////////
+ /// URIs ///
+ ///////////////////
+
+ // scheme = alpha *( alpha | digit | "+" | "-" | "." )
+ val scheme = "([a-z][a-z0-9+-.]*)" /*CAPT*/
+
+ // absoluteURI = scheme ":" ( hier_part | opaque_part )
+ val absoluteURI = scheme+":(?:("+hier_part+")|("+opaque_part+"))" /*2CAPT*/
+
+ // relativeURI = ( net_path | abs_path | rel_path ) [ "?" query ]
+ val relativeURI = /*2CAPT*/
+ "(?:"+net_path+"|("+abs_path+")|("+rel_path+"))(?:\\?"+query+")?"
+
+ // URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ]
+ val uriRef = "^(?:"+absoluteURI+"|"+relativeURI+")(?:#"+fragment+")?$"
+
+ new RegExp(uriRef, "i")
+ }
+
+ private object Fields {
+ final val AbsScheme = 1
+ final val AbsHierPart = AbsScheme+1
+ final val AbsAuthority = AbsHierPart+1
+ final val AbsUserInfo = AbsAuthority+1
+ final val AbsHost = AbsUserInfo+1
+ final val AbsPort = AbsHost+1
+ final val AbsNetPath = AbsPort+1 // abs_path part only
+ final val AbsAbsPath = AbsNetPath+1
+ final val AbsQuery = AbsAbsPath+1
+ final val AbsOpaquePart = AbsQuery+1
+ final val RelSchemeSpecificPart = 0 // It's the whole string
+ final val RelAuthority = AbsOpaquePart+1
+ final val RelUserInfo = RelAuthority+1
+ final val RelHost = RelUserInfo+1
+ final val RelPort = RelHost+1
+ final val RelNetPath = RelPort+1 // abs_path part only
+ final val RelAbsPath = RelNetPath+1
+ final val RelRelPath = RelAbsPath+1
+ final val RelQuery = RelRelPath+1
+ final val Fragment = RelQuery+1
+ }
+
+ // Helpers for constructors
+
+ private def uriStr(scheme: String, ssp: String, fragment: String): String = {
+ var resStr = ""
+
+ if (scheme != null)
+ resStr += scheme + ":"
+
+ if (ssp != null)
+ resStr += quoteIllegal(ssp)
+
+ if (fragment != null)
+ resStr += "#" + quoteIllegal(fragment)
+
+ resStr
+ }
+
+ private def uriStr(scheme: String, userInfo: String, host: String, port: Int,
+ path: String, query: String, fragment: String): String = {
+ var resStr = ""
+
+ if (scheme != null)
+ resStr += scheme + ":"
+
+ if (userInfo != null || host != null || port != -1)
+ resStr += "//"
+
+ if (userInfo != null)
+ resStr += quoteUserInfo(userInfo) + "@"
+
+ if (host != null) {
+ if (URI.ipv6Re.test(host))
+ resStr += "[" + host + "]"
+ else
+ resStr += host
+ }
+
+ if (port != -1)
+ resStr += ":" + port
+
+ if (path != null)
+ resStr += quotePath(path)
+
+ if (query != null)
+ resStr += "?" + quoteIllegal(query)
+
+ if (fragment != null)
+ resStr += "#" + quoteIllegal(fragment)
+
+ resStr
+ }
+
+ private def uriStr(scheme: String, authority: String, path: String,
+ query: String, fragment: String) = {
+ var resStr = ""
+
+ if (scheme != null)
+ resStr += scheme + ":"
+
+ if (authority != null)
+ resStr += "//" + quoteAuthority(authority)
+
+ if (path != null)
+ resStr += quotePath(path)
+
+ if (query != null)
+ resStr += "?" + quoteIllegal(query)
+
+ if (fragment != null)
+ resStr += "#" + quoteIllegal(fragment)
+
+ resStr
+ }
+
+ // Quote helpers
+
+ private val quoteChar: js.Function1[String, String] = { (str: String) =>
+ require(str.length == 1)
+
+ val c = str.head.toInt
+
+ if (c > 127)
+ throw new URISyntaxException(null, "Only ASCII allowed in URIs")
+ else
+ f"%%$c%02x"
+ }
+
+ /** matches any character not in unreserved, punct, escaped or other */
+ private val userInfoQuoteRe =
+ new RegExp("[^a-z0-9-_.!~*'(),;:$&+=%\\s]|%(?![0-9a-f]{2})", "ig")
+
+ /** Quote any character not in unreserved, punct, escaped or other */
+ private def quoteUserInfo(str: String) =
+ (str: js.prim.String).replace(userInfoQuoteRe, quoteChar)
+
+ /** matches any character not in unreserved, punct, escaped, other or equal
+ * to '/' or '@'
+ */
+ private val pathQuoteRe =
+ new RegExp("[^a-z0-9-_.!~*'(),;:$&+=%\\s@/]|%(?![0-9a-f]{2})", "ig")
+
+ /** Quote any character not in unreserved, punct, escaped, other or equal
+ * to '/' or '@'
+ */
+ private def quotePath(str: String) =
+ (str: js.prim.String).replace(pathQuoteRe, quoteChar)
+
+ /** matches any character not in unreserved, punct, escaped, other or equal
+ * to '@', '[' or ']'
+ * The last two are different to how JavaDoc specifies, but hopefully yield
+ * the same behavior. (We shouldn't escape [], since they may occur
+ * in IPv6 addresses, but technically speaking they are in reserved
+ * due to RFC2732).
+ */
+ private val authorityQuoteRe =
+ new RegExp("[^a-z0-9-_.!~*'(),;:$&+=%\\s@\\[\\]]|%(?![0-9a-f]{2})", "ig")
+
+ /** Quote any character not in unreserved, punct, escaped, other or equal
+ * to '@'
+ */
+ private def quoteAuthority(str: String) =
+ (str: js.prim.String).replace(authorityQuoteRe, quoteChar)
+
+ /** matches any character not in unreserved, reserved, escaped or other */
+ private val illegalQuoteRe =
+ new RegExp("[^a-z0-9-_.!~*'(),;:$&+=?/\\[\\]%\\s]|%(?![0-9a-f]{2})", "ig")
+
+ /** Quote any character not in unreserved, reserved, escaped or other */
+ private def quoteIllegal(str: String) =
+ (str: js.prim.String).replace(illegalQuoteRe, quoteChar)
+
+ /** Case-sensitive comparison that is case-insensitive inside URI
+ * escapes. Will compare `a%A0` and `a%a0` as equal, but `a%A0` and
+ * `A%A0` as different.
+ */
+ private def escapeAwareCompare(x: String, y: String): Int = {
+ @tailrec
+ def loop(i: Int): Int = {
+ if (i >= x.length || i >= y.length)
+ x.length - y.length
+ else {
+ val diff = x.charAt(i) - y.charAt(i)
+ if (diff != 0) diff
+ else if (x.charAt(i) == '%') {
+ // we need to do a CI compare for the next two characters
+ assert(x.length > i + 2, "Invalid escape in URI")
+ assert(y.length > i + 2, "Invalid escape in URI")
+ val cmp =
+ x.substring(i+1, i+3).compareToIgnoreCase(y.substring(i+1, i+3))
+ if (cmp != 0) cmp
+ else loop(i+3)
+ } else loop(i+1)
+ }
+ }
+
+ loop(0)
+ }
+
+ /** Upper-cases all URI escape sequences in `str`. Used for hashing */
+ private def normalizeEscapes(str: String): String = {
+ var i = 0
+ var res = ""
+ while (i < str.length) {
+ if (str.charAt(i) == '%') {
+ assert(str.length > i + 2, "Invalid escape in URI")
+ res += str.substring(i, i+3).toUpperCase()
+ i += 3
+ } else {
+ res += str.substring(i, i+1)
+ i += 1
+ }
+ }
+
+ res
+ }
+
+ private final val uriSeed = 53722356
+
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/net/URISyntaxException.scala b/examples/scala-js/javalib/src/main/scala/java/net/URISyntaxException.scala
new file mode 100644
index 0000000..85e0879
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/net/URISyntaxException.scala
@@ -0,0 +1,15 @@
+package java.net
+
+class URISyntaxException(
+ private val input: String,
+ private val reason: String,
+ private val index: Int) extends Exception(
+ s"$reason in $input at $index") {
+
+ def this(input: String, reason: String) = this(input, reason, -1)
+
+ def getIndex(): Int = index
+ def getInput(): String = input
+ def getReason(): String = reason
+
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/nio/Buffer.scala b/examples/scala-js/javalib/src/main/scala/java/nio/Buffer.scala
new file mode 100644
index 0000000..be7ab7f
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/nio/Buffer.scala
@@ -0,0 +1,83 @@
+package java.nio
+
+abstract class Buffer private[nio] (val _capacity: Int) {
+ private var _limit: Int = capacity
+ private var _position: Int = 0
+ private[nio] var _mark: Int = -1
+
+ final def capacity(): Int = _capacity
+
+ final def position(): Int = _position
+
+ final def position(newPosition: Int): Buffer = {
+ if (newPosition < 0 || newPosition > limit())
+ throw new IllegalArgumentException
+ _position = newPosition
+ if (_mark > newPosition)
+ _mark = -1
+ this
+ }
+
+ final def limit(): Int = _limit
+
+ final def limit(newLimit: Int): Buffer = {
+ if (newLimit < 0 || newLimit > capacity())
+ throw new IllegalArgumentException
+ _limit = newLimit
+ if (_position > newLimit) {
+ _position = newLimit
+ if (_mark > newLimit)
+ _mark = -1
+ }
+ this
+ }
+
+ final def mark(): Buffer = {
+ _mark = _position
+ this
+ }
+
+ final def reset(): Buffer = {
+ if (_mark == -1)
+ throw new InvalidMarkException
+ _position = _mark
+ this
+ }
+
+ final def clear(): Buffer = {
+ _mark = -1
+ _position = 0
+ _limit = capacity
+ this
+ }
+
+ final def flip(): Buffer = {
+ _mark = -1
+ _limit = _position
+ _position = 0
+ this
+ }
+
+ final def rewind(): Buffer = {
+ _mark = -1
+ _position = 0
+ this
+ }
+
+ @inline final def remaining(): Int = limit - position
+
+ @inline final def hasRemaining(): Boolean = position != limit
+
+ def isReadOnly(): Boolean
+
+ def hasArray(): Boolean
+
+ def array(): Object
+
+ def arrayOffset(): Int
+
+ def isDirect(): Boolean
+
+ override def toString(): String =
+ s"${getClass.getName}[pos=$position lim=$limit cap=$capacity]"
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/nio/BufferOverflowException.scala b/examples/scala-js/javalib/src/main/scala/java/nio/BufferOverflowException.scala
new file mode 100644
index 0000000..03f359e
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/nio/BufferOverflowException.scala
@@ -0,0 +1,3 @@
+package java.nio
+
+class BufferOverflowException extends RuntimeException
diff --git a/examples/scala-js/javalib/src/main/scala/java/nio/BufferUnderflowException.scala b/examples/scala-js/javalib/src/main/scala/java/nio/BufferUnderflowException.scala
new file mode 100644
index 0000000..e286975
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/nio/BufferUnderflowException.scala
@@ -0,0 +1,3 @@
+package java.nio
+
+class BufferUnderflowException extends RuntimeException
diff --git a/examples/scala-js/javalib/src/main/scala/java/nio/ByteBuffer.scala b/examples/scala-js/javalib/src/main/scala/java/nio/ByteBuffer.scala
new file mode 100644
index 0000000..b743b39
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/nio/ByteBuffer.scala
@@ -0,0 +1,227 @@
+package java.nio
+
+object ByteBuffer {
+ private final val HashSeed = -547316498 // "java.nio.ByteBuffer".##
+
+ def allocate(capacity: Int): ByteBuffer =
+ wrap(new Array[Byte](capacity))
+
+ def allocateDirect(capacity: Int): ByteBuffer =
+ allocate(capacity)
+
+ def wrap(array: Array[Byte], offset: Int, length: Int): ByteBuffer =
+ HeapByteBuffer.wrap(array, 0, array.length, offset, length, false)
+
+ def wrap(array: Array[Byte]): ByteBuffer =
+ wrap(array, 0, array.length)
+}
+
+abstract class ByteBuffer private[nio] (
+ _capacity: Int, private[nio] val _array: Array[Byte],
+ private[nio] val _arrayOffset: Int)
+ extends Buffer(_capacity) with Comparable[ByteBuffer] {
+
+ def this(_capacity: Int) = this(_capacity, null, -1)
+
+ private var _order: ByteOrder = ByteOrder.BIG_ENDIAN
+
+ def slice(): ByteBuffer
+
+ def duplicate(): ByteBuffer
+
+ def asReadOnlyBuffer(): ByteBuffer
+
+ def get(): Byte
+
+ def put(b: Byte): ByteBuffer
+
+ def get(index: Int): Byte
+
+ def put(index: Int, b: Byte): ByteBuffer
+
+ def get(dst: Array[Byte], offset: Int, length: Int): ByteBuffer = {
+ val end = offset + length
+
+ if (offset < 0 || length < 0 || end > dst.length)
+ throw new IndexOutOfBoundsException
+ if (remaining < length)
+ throw new BufferUnderflowException
+
+ var i = offset
+ while (i != end) {
+ dst(i) = get()
+ i += 1
+ }
+
+ this
+ }
+
+ def get(dst: Array[Byte]): ByteBuffer =
+ get(dst, 0, dst.length)
+
+ def put(src: ByteBuffer): ByteBuffer = {
+ if (src eq this)
+ throw new IllegalArgumentException
+ if (isReadOnly)
+ throw new ReadOnlyBufferException
+ if (src.remaining > remaining)
+ throw new BufferOverflowException
+
+ var n = src.remaining
+ if (src._array != null) { // even if read-only
+ val pos = src.position
+ put(src._array, src._arrayOffset + pos, n)
+ src.position(pos + n)
+ } else {
+ while (n != 0) {
+ put(src.get())
+ n -= 1
+ }
+ }
+
+ this
+ }
+
+ def put(src: Array[Byte], offset: Int, length: Int): ByteBuffer = {
+ val end = offset + length
+ if (offset < 0 || length < 0 || end > src.length)
+ throw new IndexOutOfBoundsException
+ if (isReadOnly)
+ throw new ReadOnlyBufferException
+ if (remaining < length)
+ throw new BufferOverflowException
+
+ var i = offset
+ while (i != end) {
+ put(src(i))
+ i += 1
+ }
+
+ this
+ }
+
+ final def put(src: Array[Byte]): ByteBuffer =
+ put(src, 0, src.length)
+
+ @inline final def hasArray(): Boolean = _array != null && !isReadOnly
+
+ @inline final def array(): Array[Byte] = {
+ val a = _array
+ if (a == null)
+ throw new UnsupportedOperationException
+ if (isReadOnly)
+ throw new ReadOnlyBufferException
+ a
+ }
+
+ @inline final def arrayOffset(): Int = {
+ val o = _arrayOffset
+ if (o == -1)
+ throw new UnsupportedOperationException
+ if (isReadOnly)
+ throw new ReadOnlyBufferException
+ o
+ }
+
+ def compact(): ByteBuffer
+
+ // Not implemented:
+ //def isDirect(): Boolean
+
+ // toString(): String inherited from Buffer
+
+ override def hashCode(): Int = {
+ import scala.util.hashing.MurmurHash3._
+ val start = position
+ val end = limit
+ var h = ByteBuffer.HashSeed
+ var i = start
+ while (i != end) {
+ h = mix(h, get().##)
+ i += 1
+ }
+ position(start)
+ finalizeHash(h, end-start)
+ }
+
+ override def equals(that: Any): Boolean = that match {
+ case that: ByteBuffer => compareTo(that) == 0
+ case _ => false
+ }
+
+ def compareTo(that: ByteBuffer): Int = {
+ if (this eq that) {
+ 0
+ } else {
+ val thisStart = this.position
+ val thisRemaining = this.remaining
+ val thatStart = that.position
+ val thatRemaining = that.remaining
+ val shortestLength = Math.min(thisRemaining, thatRemaining)
+
+ var i = 0
+ while (i != shortestLength) {
+ val cmp = this.get().compareTo(that.get())
+ if (cmp != 0) {
+ this.position(thisStart)
+ that.position(thatStart)
+ return cmp
+ }
+ i += 1
+ }
+
+ this.position(thisStart)
+ that.position(thatStart)
+ thisRemaining.compareTo(thatRemaining)
+ }
+ }
+
+ final def order(): ByteOrder = _order
+
+ final def order(bo: ByteOrder): ByteBuffer = {
+ if (bo == null)
+ throw new NullPointerException
+ _order = bo
+ this
+ }
+
+ /* Not implemented:
+
+ def getChar(): Char
+ def putChar(value: Char): ByteBuffer
+ def getChar(index: Int): Char
+ def putChar(index: Int, value: Char): ByteBuffer
+ def asCharBuffer(): CharBuffer
+
+ def getShort(): Short
+ def putShort(value: Short): ByteBuffer
+ def getShort(index: Int): Short
+ def putShort(index: Int, value: Short): ByteBuffer
+ def asShortBuffer(): ShortBuffer
+
+ def getInt(): Int
+ def putInt(value: Int): ByteBuffer
+ def getInt(index: Int): Int
+ def putInt(index: Int, value: Int): ByteBuffer
+ def asIntBuffer(): IntBuffer
+
+ def getLong(): Long
+ def putLong(value: Long): ByteBuffer
+ def getLong(index: Int): Long
+ def putLong(index: Int, value: Long): ByteBuffer
+ def asLongBuffer(): LongBuffer
+
+ def getFloat(): Float
+ def putFloat(value: Float): ByteBuffer
+ def getFloat(index: Int): Float
+ def putFloat(index: Int, value: Float): ByteBuffer
+ def asFloatBuffer(): FloatBuffer
+
+ def getDouble(): Double
+ def putDouble(value: Double): ByteBuffer
+ def getDouble(index: Int): Double
+ def putDouble(index: Int, value: Double): ByteBuffer
+ def asDoubleBuffer(): DoubleBuffer
+
+ */
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/nio/ByteOrder.scala b/examples/scala-js/javalib/src/main/scala/java/nio/ByteOrder.scala
new file mode 100644
index 0000000..20bac6a
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/nio/ByteOrder.scala
@@ -0,0 +1,15 @@
+package java.nio
+
+final class ByteOrder private (name: String) {
+ override def toString(): String = name
+}
+
+object ByteOrder {
+ val BIG_ENDIAN: ByteOrder = new ByteOrder("BIG_ENDIAN")
+ val LITTLE_ENDIAN: ByteOrder = new ByteOrder("LITTLE_ENDIAN")
+
+ def nativeOrder(): ByteOrder = {
+ if (scala.scalajs.runtime.Bits.areTypedArraysBigEndian) BIG_ENDIAN
+ else LITTLE_ENDIAN
+ }
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/nio/CharBuffer.scala b/examples/scala-js/javalib/src/main/scala/java/nio/CharBuffer.scala
new file mode 100644
index 0000000..5e74953
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/nio/CharBuffer.scala
@@ -0,0 +1,228 @@
+package java.nio
+
+object CharBuffer {
+ private final val HashSeed = -182887236 // "java.nio.CharBuffer".##
+
+ def allocate(capacity: Int): CharBuffer =
+ wrap(new Array[Char](capacity))
+
+ def wrap(array: Array[Char], offset: Int, length: Int): CharBuffer =
+ HeapCharBuffer.wrap(array, 0, array.length, offset, length, false)
+
+ def wrap(array: Array[Char]): CharBuffer =
+ wrap(array, 0, array.length)
+
+ def wrap(csq: CharSequence, start: Int, end: Int): CharBuffer =
+ StringCharBuffer.wrap(csq, 0, csq.length, start, end)
+
+ def wrap(csq: CharSequence): CharBuffer =
+ wrap(csq, 0, csq.length)
+}
+
+abstract class CharBuffer private[nio] (
+ _capacity: Int, private[nio] val _array: Array[Char],
+ private[nio] val _arrayOffset: Int)
+ extends Buffer(_capacity) with Comparable[CharBuffer]
+ with CharSequence with Appendable with Readable {
+
+ def this(_capacity: Int) = this(_capacity, null, -1)
+
+ def read(target: CharBuffer): Int = {
+ // Attention: this method must not change this buffer's position
+ val n = remaining
+ if (n == 0) -1
+ else if (_array != null) { // even if read-only
+ target.put(_array, _arrayOffset, n)
+ n
+ } else {
+ val savedPos = position
+ target.put(this)
+ position(savedPos)
+ n
+ }
+ }
+
+ def slice(): CharBuffer
+
+ def duplicate(): CharBuffer
+
+ def asReadOnlyBuffer(): CharBuffer
+
+ def get(): Char
+
+ def put(c: Char): CharBuffer
+
+ def get(index: Int): Char
+
+ def put(index: Int, c: Char): CharBuffer
+
+ def get(dst: Array[Char], offset: Int, length: Int): CharBuffer = {
+ val end = offset + length
+
+ if (offset < 0 || length < 0 || end > dst.length)
+ throw new IndexOutOfBoundsException
+ if (remaining < length)
+ throw new BufferUnderflowException
+
+ var i = offset
+ while (i != end) {
+ dst(i) = get()
+ i += 1
+ }
+
+ this
+ }
+
+ def get(dst: Array[Char]): CharBuffer =
+ get(dst, 0, dst.length)
+
+ def put(src: CharBuffer): CharBuffer = {
+ if (src eq this)
+ throw new IllegalArgumentException
+ if (isReadOnly)
+ throw new ReadOnlyBufferException
+ if (src.remaining > remaining)
+ throw new BufferOverflowException
+
+ var n = src.remaining
+ if (src._array != null) { // even if read-only
+ val pos = src.position
+ put(src._array, src._arrayOffset + pos, n)
+ src.position(pos + n)
+ } else {
+ while (n != 0) {
+ put(src.get())
+ n -= 1
+ }
+ }
+
+ this
+ }
+
+ def put(src: Array[Char], offset: Int, length: Int): CharBuffer = {
+ val end = offset + length
+ if (offset < 0 || length < 0 || end > src.length)
+ throw new IndexOutOfBoundsException
+ if (isReadOnly)
+ throw new ReadOnlyBufferException
+ if (remaining < length)
+ throw new BufferOverflowException
+
+ var i = offset
+ while (i != end) {
+ put(src(i))
+ i += 1
+ }
+
+ this
+ }
+
+ final def put(src: Array[Char]): CharBuffer =
+ put(src, 0, src.length)
+
+ def put(src: String, start: Int, end: Int): CharBuffer =
+ put(CharBuffer.wrap(src, start, end))
+
+ final def put(src: String): CharBuffer =
+ put(src, 0, src.length)
+
+ @inline final def hasArray(): Boolean = _array != null && !isReadOnly
+
+ @inline final def array(): Array[Char] = {
+ val a = _array
+ if (a == null)
+ throw new UnsupportedOperationException
+ if (isReadOnly)
+ throw new ReadOnlyBufferException
+ a
+ }
+
+ @inline final def arrayOffset(): Int = {
+ val o = _arrayOffset
+ if (o == -1)
+ throw new UnsupportedOperationException
+ if (isReadOnly)
+ throw new ReadOnlyBufferException
+ o
+ }
+
+ def compact(): CharBuffer
+
+ // Not implemented:
+ //def isDirect(): Boolean
+
+ override def hashCode(): Int = {
+ import scala.util.hashing.MurmurHash3._
+ val start = position
+ val end = limit
+ var h = CharBuffer.HashSeed
+ var i = start
+ while (i != end) {
+ h = mix(h, get().##)
+ i += 1
+ }
+ position(start)
+ finalizeHash(h, end-start)
+ }
+
+ override def equals(that: Any): Boolean = that match {
+ case that: CharBuffer => compareTo(that) == 0
+ case _ => false
+ }
+
+ def compareTo(that: CharBuffer): Int = {
+ if (this eq that) {
+ 0
+ } else {
+ val thisStart = this.position
+ val thisRemaining = this.remaining
+ val thatStart = that.position
+ val thatRemaining = that.remaining
+ val shortestLength = Math.min(thisRemaining, thatRemaining)
+
+ var i = 0
+ while (i != shortestLength) {
+ val cmp = this.get().compareTo(that.get())
+ if (cmp != 0) {
+ this.position(thisStart)
+ that.position(thatStart)
+ return cmp
+ }
+ i += 1
+ }
+
+ this.position(thisStart)
+ that.position(thatStart)
+ thisRemaining.compareTo(thatRemaining)
+ }
+ }
+
+ override def toString(): String = {
+ if (_array != null) { // even if read-only
+ new String(_array, position + _arrayOffset, remaining)
+ } else {
+ val chars = new Array[Char](remaining)
+ val savedPos = position
+ get(chars)
+ position(savedPos)
+ new String(chars)
+ }
+ }
+
+ final def length(): Int = remaining
+
+ final def charAt(index: Int): Char = get(position + index)
+
+ def subSequence(start: Int, end: Int): CharSequence
+
+ def append(csq: CharSequence): CharBuffer =
+ put(csq.toString())
+
+ def append(csq: CharSequence, start: Int, end: Int): CharBuffer =
+ put(csq.subSequence(start, end).toString())
+
+ def append(c: Char): CharBuffer =
+ put(c)
+
+ def order(): ByteOrder
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/nio/HeapByteBuffer.scala b/examples/scala-js/javalib/src/main/scala/java/nio/HeapByteBuffer.scala
new file mode 100644
index 0000000..ed3fd29
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/nio/HeapByteBuffer.scala
@@ -0,0 +1,129 @@
+package java.nio
+
+private[nio] final class HeapByteBuffer private (
+ _capacity: Int, _array0: Array[Byte], _arrayOffset0: Int,
+ _initialPosition: Int, _initialLimit: Int, _readOnly: Boolean)
+ extends ByteBuffer(_capacity, _array0, _arrayOffset0) {
+
+ position(_initialPosition)
+ limit(_initialLimit)
+
+ def isReadOnly(): Boolean = _readOnly
+
+ def isDirect(): Boolean = false
+
+ def slice(): ByteBuffer = {
+ val cap = remaining
+ new HeapByteBuffer(cap, _array, _arrayOffset+position, 0, cap, isReadOnly)
+ }
+
+ def duplicate(): ByteBuffer = {
+ val result = new HeapByteBuffer(capacity, _array, _arrayOffset,
+ position, limit, isReadOnly)
+ result._mark = this._mark
+ result
+ }
+
+ def asReadOnlyBuffer(): ByteBuffer = {
+ val result = new HeapByteBuffer(capacity, _array, _arrayOffset,
+ position, limit, true)
+ result._mark = this._mark
+ result
+ }
+
+ def get(): Byte = {
+ if (!hasRemaining)
+ throw new BufferUnderflowException
+ val p = position
+ position(p + 1)
+ _array(_arrayOffset + p)
+ }
+
+ def put(b: Byte): ByteBuffer = {
+ if (isReadOnly)
+ throw new ReadOnlyBufferException
+ if (!hasRemaining)
+ throw new BufferOverflowException
+ val p = position
+ _array(_arrayOffset + p) = b
+ position(p + 1)
+ this
+ }
+
+ def get(index: Int): Byte = {
+ if (index < 0 || index >= limit)
+ throw new IndexOutOfBoundsException
+ _array(_arrayOffset + index)
+ }
+
+ def put(index: Int, b: Byte): ByteBuffer = {
+ if (isReadOnly)
+ throw new ReadOnlyBufferException
+ if (index < 0 || index >= limit)
+ throw new IndexOutOfBoundsException
+ _array(_arrayOffset + index) = b
+ this
+ }
+
+ override def get(dst: Array[Byte], offset: Int, length: Int): ByteBuffer = {
+ val end = offset + length
+
+ if (offset < 0 || length < 0 || end > dst.length)
+ throw new IndexOutOfBoundsException
+
+ val startPos = position
+ val endPos = startPos + length
+ if (endPos > limit)
+ throw new BufferUnderflowException
+
+ System.arraycopy(_array, startPos + _arrayOffset, dst, offset, length)
+ position(endPos)
+
+ this
+ }
+
+ override def put(src: Array[Byte], offset: Int, length: Int): ByteBuffer = {
+ val end = offset + length
+ if (offset < 0 || length < 0 || end > src.length)
+ throw new IndexOutOfBoundsException
+ if (isReadOnly)
+ throw new ReadOnlyBufferException
+
+ val startPos = position
+ val endPos = startPos + length
+ if (endPos > limit)
+ throw new BufferOverflowException
+
+ System.arraycopy(src, offset, _array, startPos + _arrayOffset, length)
+ position(endPos)
+
+ this
+ }
+
+ def compact(): ByteBuffer = {
+ if (isReadOnly)
+ throw new ReadOnlyBufferException
+
+ val offset = _arrayOffset
+ val len = remaining
+ System.arraycopy(_array, offset + position, _array, offset, len)
+ _mark = -1
+ limit(capacity)
+ position(len)
+ this
+ }
+}
+
+private[nio] object HeapByteBuffer {
+ private[nio] def wrap(array: Array[Byte], arrayOffset: Int, capacity: Int,
+ initialPosition: Int, initialLength: Int,
+ isReadOnly: Boolean): ByteBuffer = {
+ if (arrayOffset < 0 || capacity < 0 || arrayOffset+capacity > array.length)
+ throw new IndexOutOfBoundsException
+ val initialLimit = initialPosition + initialLength
+ if (initialPosition < 0 || initialLength < 0 || initialLimit > capacity)
+ throw new IndexOutOfBoundsException
+ new HeapByteBuffer(capacity, array, arrayOffset,
+ initialPosition, initialLimit, isReadOnly)
+ }
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/nio/HeapCharBuffer.scala b/examples/scala-js/javalib/src/main/scala/java/nio/HeapCharBuffer.scala
new file mode 100644
index 0000000..546c55d
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/nio/HeapCharBuffer.scala
@@ -0,0 +1,138 @@
+package java.nio
+
+private[nio] final class HeapCharBuffer private (
+ _capacity: Int, _array0: Array[Char], _arrayOffset0: Int,
+ _initialPosition: Int, _initialLimit: Int, _readOnly: Boolean)
+ extends CharBuffer(_capacity, _array0, _arrayOffset0) {
+
+ position(_initialPosition)
+ limit(_initialLimit)
+
+ def isReadOnly(): Boolean = _readOnly
+
+ def isDirect(): Boolean = false
+
+ def slice(): CharBuffer = {
+ val cap = remaining
+ new HeapCharBuffer(cap, _array, _arrayOffset + position, 0, cap, isReadOnly)
+ }
+
+ def duplicate(): CharBuffer = {
+ val result = new HeapCharBuffer(capacity, _array, _arrayOffset,
+ position, limit, isReadOnly)
+ result._mark = this._mark
+ result
+ }
+
+ def asReadOnlyBuffer(): CharBuffer = {
+ val result = new HeapCharBuffer(capacity, _array, _arrayOffset,
+ position, limit, true)
+ result._mark = this._mark
+ result
+ }
+
+ def subSequence(start: Int, end: Int): CharBuffer = {
+ if (start < 0 || end < start || end > remaining)
+ throw new IndexOutOfBoundsException
+ new HeapCharBuffer(capacity, _array, _arrayOffset,
+ position + start, position + end, isReadOnly)
+ }
+
+ def get(): Char = {
+ if (!hasRemaining)
+ throw new BufferUnderflowException
+ val p = position
+ position(p + 1)
+ _array(_arrayOffset + p)
+ }
+
+ def put(c: Char): CharBuffer = {
+ if (isReadOnly)
+ throw new ReadOnlyBufferException
+ if (!hasRemaining)
+ throw new BufferOverflowException
+ val p = position
+ _array(_arrayOffset + p) = c
+ position(p + 1)
+ this
+ }
+
+ def get(index: Int): Char = {
+ if (index < 0 || index >= limit)
+ throw new IndexOutOfBoundsException
+ _array(_arrayOffset + index)
+ }
+
+ def put(index: Int, b: Char): CharBuffer = {
+ if (isReadOnly)
+ throw new ReadOnlyBufferException
+ if (index < 0 || index >= limit)
+ throw new IndexOutOfBoundsException
+ _array(_arrayOffset + index) = b
+ this
+ }
+
+ override def get(dst: Array[Char], offset: Int, length: Int): CharBuffer = {
+ val end = offset + length
+
+ if (offset < 0 || length < 0 || end > dst.length)
+ throw new IndexOutOfBoundsException
+
+ val startPos = position
+ val endPos = startPos + length
+ if (endPos > limit)
+ throw new BufferUnderflowException
+
+ System.arraycopy(_array, startPos + _arrayOffset, dst, offset, length)
+ position(endPos)
+
+ this
+ }
+
+ override def put(src: Array[Char], offset: Int, length: Int): CharBuffer = {
+ val end = offset + length
+ if (offset < 0 || length < 0 || end > src.length)
+ throw new IndexOutOfBoundsException
+ if (isReadOnly)
+ throw new ReadOnlyBufferException
+
+ val startPos = position
+ val endPos = startPos + length
+ if (endPos > limit)
+ throw new BufferOverflowException
+
+ System.arraycopy(src, offset, _array, startPos + _arrayOffset, length)
+ position(endPos)
+
+ this
+ }
+
+ def compact(): CharBuffer = {
+ if (isReadOnly)
+ throw new ReadOnlyBufferException
+
+ val offset = _arrayOffset
+ val len = remaining
+ System.arraycopy(_array, offset + position, _array, offset, len)
+ _mark = -1
+ limit(capacity)
+ position(len)
+ this
+ }
+
+ def order(): ByteOrder = ByteOrder.nativeOrder()
+}
+
+private[nio] object HeapCharBuffer {
+ private[nio] def wrap(array: Array[Char], arrayOffset: Int, capacity: Int,
+ initialPosition: Int, initialLength: Int,
+ isReadOnly: Boolean): CharBuffer = {
+ if (arrayOffset < 0 || capacity < 0 || arrayOffset+capacity > array.length)
+ throw new IndexOutOfBoundsException
+ val initialLimit = initialPosition + initialLength
+ if (initialPosition < 0 || initialLength < 0 || initialLimit > capacity)
+ throw new IndexOutOfBoundsException
+ new HeapCharBuffer(capacity, array, arrayOffset,
+ initialPosition, initialLimit, isReadOnly)
+ }
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/nio/InvalidMarkException.scala b/examples/scala-js/javalib/src/main/scala/java/nio/InvalidMarkException.scala
new file mode 100644
index 0000000..c2d3714
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/nio/InvalidMarkException.scala
@@ -0,0 +1,3 @@
+package java.nio
+
+class InvalidMarkException extends IllegalStateException
diff --git a/examples/scala-js/javalib/src/main/scala/java/nio/ReadOnlyBufferException.scala b/examples/scala-js/javalib/src/main/scala/java/nio/ReadOnlyBufferException.scala
new file mode 100644
index 0000000..ee0868b
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/nio/ReadOnlyBufferException.scala
@@ -0,0 +1,3 @@
+package java.nio
+
+class ReadOnlyBufferException extends UnsupportedOperationException
diff --git a/examples/scala-js/javalib/src/main/scala/java/nio/StringCharBuffer.scala b/examples/scala-js/javalib/src/main/scala/java/nio/StringCharBuffer.scala
new file mode 100644
index 0000000..25bc594
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/nio/StringCharBuffer.scala
@@ -0,0 +1,102 @@
+package java.nio
+
+private[nio] final class StringCharBuffer private (
+ _capacity: Int, private[this] var _csq: CharSequence,
+ private[this] var _csqOffset: Int,
+ _initialPosition: Int, _initialLimit: Int)
+ extends CharBuffer(_capacity) {
+
+ position(_initialPosition)
+ limit(_initialLimit)
+
+ def isReadOnly(): Boolean = true
+
+ def isDirect(): Boolean = false
+
+ def slice(): CharBuffer = {
+ val cap = remaining
+ new StringCharBuffer(cap, _csq, _csqOffset + position, 0, cap)
+ }
+
+ def duplicate(): CharBuffer = {
+ val result = new StringCharBuffer(capacity, _csq, _csqOffset,
+ position, limit)
+ result._mark = this._mark
+ result
+ }
+
+ def asReadOnlyBuffer(): CharBuffer = duplicate()
+
+ def subSequence(start: Int, end: Int): CharBuffer = {
+ if (start < 0 || end < start || end > remaining)
+ throw new IndexOutOfBoundsException
+ new StringCharBuffer(capacity, _csq, _csqOffset,
+ position + start, position + end)
+ }
+
+ def get(): Char = {
+ if (!hasRemaining)
+ throw new BufferUnderflowException
+ val p = position
+ position(p + 1)
+ _csq.charAt(_csqOffset + p)
+ }
+
+ def put(c: Char): CharBuffer =
+ throw new ReadOnlyBufferException
+
+ def get(index: Int): Char = {
+ if (index < 0 || index >= limit)
+ throw new IndexOutOfBoundsException
+ _csq.charAt(_csqOffset + index)
+ }
+
+ def put(index: Int, b: Char): CharBuffer =
+ throw new ReadOnlyBufferException
+
+ override def get(dst: Array[Char], offset: Int, length: Int): CharBuffer = {
+ val end = offset + length
+
+ if (offset < 0 || length < 0 || end > dst.length)
+ throw new IndexOutOfBoundsException
+
+ val startPos = position
+ val endPos = startPos + length
+ if (endPos > limit)
+ throw new BufferUnderflowException
+
+ var i = offset
+ var j = startPos + _csqOffset
+ while (i != end) {
+ dst(i) = _csq.charAt(j)
+ i += 1
+ j += 1
+ }
+ position(endPos)
+
+ this
+ }
+
+ def compact(): CharBuffer =
+ throw new ReadOnlyBufferException
+
+ override def toString(): String = {
+ val offset = _csqOffset
+ _csq.subSequence(position + offset, limit + offset).toString()
+ }
+
+ def order(): ByteOrder = ByteOrder.nativeOrder()
+}
+
+private[nio] object StringCharBuffer {
+ private[nio] def wrap(csq: CharSequence, csqOffset: Int, capacity: Int,
+ initialPosition: Int, initialLength: Int): CharBuffer = {
+ if (csqOffset < 0 || capacity < 0 || csqOffset+capacity > csq.length)
+ throw new IndexOutOfBoundsException
+ val initialLimit = initialPosition + initialLength
+ if (initialPosition < 0 || initialLength < 0 || initialLimit > capacity)
+ throw new IndexOutOfBoundsException
+ new StringCharBuffer(capacity, csq, csqOffset,
+ initialPosition, initialLimit)
+ }
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/nio/charset/CharacterCodingException.scala b/examples/scala-js/javalib/src/main/scala/java/nio/charset/CharacterCodingException.scala
new file mode 100644
index 0000000..8017348
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/nio/charset/CharacterCodingException.scala
@@ -0,0 +1,3 @@
+package java.nio.charset
+
+class CharacterCodingException extends java.io.IOException
diff --git a/examples/scala-js/javalib/src/main/scala/java/nio/charset/Charset.scala b/examples/scala-js/javalib/src/main/scala/java/nio/charset/Charset.scala
new file mode 100644
index 0000000..6d1af47
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/nio/charset/Charset.scala
@@ -0,0 +1,103 @@
+package java.nio.charset
+
+import java.nio.{ByteBuffer, CharBuffer}
+
+import scala.scalajs.js
+
+abstract class Charset protected (canonicalName: String,
+ aliases: Array[String]) extends AnyRef with Comparable[Charset] {
+ final def name(): String = canonicalName
+
+ override final def equals(that: Any): Boolean = that match {
+ case that: Charset => this.name == that.name
+ case _ => false
+ }
+
+ override final def toString(): String = name()
+
+ override final def hashCode(): Int = name.##
+
+ override final def compareTo(that: Charset): Int =
+ name.compareToIgnoreCase(that.name)
+
+ def contains(cs: Charset): Boolean
+
+ def newDecoder(): CharsetDecoder
+ def newEncoder(): CharsetEncoder
+
+ def canEncode(): Boolean = true
+
+ private lazy val cachedDecoder = {
+ this.newDecoder()
+ .onMalformedInput(CodingErrorAction.REPLACE)
+ .onUnmappableCharacter(CodingErrorAction.REPLACE)
+ }
+
+ private lazy val cachedEncoder = {
+ this.newEncoder()
+ .onMalformedInput(CodingErrorAction.REPLACE)
+ .onUnmappableCharacter(CodingErrorAction.REPLACE)
+ }
+
+ final def decode(bb: ByteBuffer): CharBuffer =
+ cachedDecoder.decode(bb)
+
+ final def encode(cb: CharBuffer): ByteBuffer =
+ cachedEncoder.encode(cb)
+
+ final def encode(str: String): ByteBuffer =
+ encode(CharBuffer.wrap(str))
+
+ def displayName(): String = name
+}
+
+object Charset {
+ import StandardCharsets._
+
+ def defaultCharset(): Charset =
+ UTF_8
+
+ def forName(charsetName: String): Charset =
+ CharsetMap.getOrElse(charsetName.toLowerCase,
+ throw new UnsupportedCharsetException(charsetName))
+
+ def isSupported(charsetName: String): Boolean =
+ CharsetMap.contains(charsetName.toLowerCase)
+
+ private lazy val CharsetMap = {
+ val m = js.Dictionary.empty[Charset]
+
+ // All these lists where obtained by experimentation on the JDK
+
+ for (s <- Seq("iso-8859-1", "iso8859-1", "iso_8859_1", "iso8859_1",
+ "iso_8859-1", "8859_1", "iso_8859-1:1987",
+ "latin1", "csisolatin1", "l1",
+ "ibm-819", "ibm819", "cp819", "819",
+ "iso-ir-100"))
+ m(s) = ISO_8859_1
+
+ for (s <- Seq("us-ascii", "ascii7", "ascii", "csascii",
+ "default",
+ "cp367", "ibm367",
+ "iso646-us", "646", "iso_646.irv:1983", "iso_646.irv:1991",
+ "ansi_x3.4-1986", "ansi_x3.4-1968",
+ "iso-ir-6"))
+ m(s) = US_ASCII
+
+ for (s <- Seq("utf-8", "utf_8", "utf8", "unicode-1-1-utf-8"))
+ m(s) = UTF_8
+
+ for (s <- Seq("utf-16be", "utf_16be", "x-utf-16be",
+ "iso-10646-ucs-2", "unicodebigunmarked"))
+ m(s) = UTF_16BE
+
+ for (s <- Seq("utf-16le", "utf_16le", "x-utf-16le",
+ "unicodelittleunmarked"))
+ m(s) = UTF_16LE
+
+ for (s <- Seq("utf-16", "utf_16", "unicode", "unicodebig"))
+ m(s) = UTF_16
+
+ m
+ }
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/nio/charset/CharsetDecoder.scala b/examples/scala-js/javalib/src/main/scala/java/nio/charset/CharsetDecoder.scala
new file mode 100644
index 0000000..a3532ba
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/nio/charset/CharsetDecoder.scala
@@ -0,0 +1,217 @@
+package java.nio.charset
+
+import scala.annotation.{switch, tailrec}
+
+import java.nio._
+
+abstract class CharsetDecoder protected (cs: Charset,
+ _averageCharsPerByte: Float, _maxCharsPerByte: Float) {
+
+ import CharsetDecoder._
+
+ // Config
+
+ private[this] var _replacement: String = "\uFFFD"
+ private[this] var _malformedInputAction: CodingErrorAction =
+ CodingErrorAction.REPORT
+ private[this] var _unmappableCharacterAction: CodingErrorAction =
+ CodingErrorAction.REPORT
+
+ // Status
+
+ private[this] var status: Int = INIT
+
+ // Methods
+
+ final def charset(): Charset = cs
+
+ final def replacement(): String = _replacement
+
+ final def replaceWith(newReplacement: String): CharsetDecoder = {
+ if (newReplacement == null || newReplacement == "")
+ throw new IllegalArgumentException("Invalid replacement: "+newReplacement)
+ if (newReplacement.length > maxCharsPerByte)
+ throw new IllegalArgumentException(
+ "Replacement string cannot be longer than maxCharsPerByte")
+ _replacement = newReplacement
+ implReplaceWith(newReplacement)
+ this
+ }
+
+ protected def implReplaceWith(newReplacement: String): Unit = ()
+
+ def malformedInputAction(): CodingErrorAction = _malformedInputAction
+
+ final def onMalformedInput(newAction: CodingErrorAction): CharsetDecoder = {
+ if (newAction == null)
+ throw new IllegalArgumentException("null CodingErrorAction")
+ _malformedInputAction = newAction
+ implOnMalformedInput(newAction)
+ this
+ }
+
+ protected def implOnMalformedInput(newAction: CodingErrorAction): Unit = ()
+
+ def unmappableCharacterAction(): CodingErrorAction = _unmappableCharacterAction
+
+ final def onUnmappableCharacter(newAction: CodingErrorAction): CharsetDecoder = {
+ if (newAction == null)
+ throw new IllegalArgumentException("null CodingErrorAction")
+ _unmappableCharacterAction = newAction
+ implOnUnmappableCharacter(newAction)
+ this
+ }
+
+ protected def implOnUnmappableCharacter(newAction: CodingErrorAction): Unit = ()
+
+ final def averageCharsPerByte(): Float = _averageCharsPerByte
+ final def maxCharsPerByte(): Float = _maxCharsPerByte
+
+ final def decode(in: ByteBuffer, out: CharBuffer,
+ endOfInput: Boolean): CoderResult = {
+
+ if (status == FLUSHED || (!endOfInput && status == END))
+ throw new IllegalStateException
+
+ status = if (endOfInput) END else ONGOING
+
+ @inline
+ @tailrec
+ def loop(): CoderResult = {
+ val result1 = try {
+ decodeLoop(in, out)
+ } catch {
+ case ex: BufferOverflowException =>
+ throw new CoderMalfunctionError(ex)
+ case ex: BufferUnderflowException =>
+ throw new CoderMalfunctionError(ex)
+ }
+
+ val result2 = if (result1.isUnderflow) {
+ val remaining = in.remaining
+ if (endOfInput && remaining > 0)
+ CoderResult.malformedForLength(remaining)
+ else
+ result1
+ } else {
+ result1
+ }
+
+ if (result2.isUnderflow || result2.isOverflow) {
+ result2
+ } else {
+ val action =
+ if (result2.isUnmappable) unmappableCharacterAction
+ else malformedInputAction
+
+ action match {
+ case CodingErrorAction.REPLACE =>
+ if (out.remaining < replacement.length) {
+ CoderResult.OVERFLOW
+ } else {
+ out.put(replacement)
+ in.position(in.position + result2.length)
+ loop()
+ }
+ case CodingErrorAction.REPORT =>
+ result2
+ case CodingErrorAction.IGNORE =>
+ in.position(in.position + result2.length)
+ loop()
+ }
+ }
+ }
+
+ loop()
+ }
+
+ final def flush(out: CharBuffer): CoderResult = {
+ (status: @switch) match {
+ case END =>
+ val result = implFlush(out)
+ if (result.isUnderflow)
+ status = FLUSHED
+ result
+ case FLUSHED =>
+ CoderResult.UNDERFLOW
+ case _ =>
+ throw new IllegalStateException
+ }
+ }
+
+ protected def implFlush(out: CharBuffer): CoderResult =
+ CoderResult.UNDERFLOW
+
+ final def reset(): CharsetDecoder = {
+ status = INIT
+ implReset()
+ this
+ }
+
+ protected def implReset(): Unit = ()
+
+ protected def decodeLoop(in: ByteBuffer, out: CharBuffer): CoderResult
+
+ final def decode(in: ByteBuffer): CharBuffer = {
+ def grow(out: CharBuffer): CharBuffer = {
+ if (out.capacity == 0) {
+ CharBuffer.allocate(1)
+ } else {
+ val result = CharBuffer.allocate(out.capacity*2)
+ out.flip()
+ result.put(out)
+ result
+ }
+ }
+
+ @inline
+ @tailrec
+ def loopDecode(out: CharBuffer): CharBuffer = {
+ val result = decode(in, out, endOfInput = true)
+ if (result.isUnderflow) {
+ assert(!in.hasRemaining)
+ out
+ } else if (result.isOverflow) {
+ loopDecode(grow(out))
+ } else {
+ result.throwException()
+ throw new AssertionError("should not get here")
+ }
+ }
+
+ @inline
+ @tailrec
+ def loopFlush(out: CharBuffer): CharBuffer = {
+ val result = flush(out)
+ if (result.isUnderflow) {
+ out
+ } else if (result.isOverflow) {
+ loopFlush(grow(out))
+ } else {
+ result.throwException()
+ throw new AssertionError("should not get here")
+ }
+ }
+
+ reset()
+ val initLength = (in.remaining.toDouble * averageCharsPerByte).toInt
+ val out = loopFlush(loopDecode(CharBuffer.allocate(initLength)))
+ out.flip()
+ out
+ }
+
+ def isAutoDetecting(): Boolean = false
+
+ def isCharsetDetected(): Boolean =
+ throw new UnsupportedOperationException
+
+ def detectedCharset(): Charset =
+ throw new UnsupportedOperationException
+}
+
+object CharsetDecoder {
+ private final val INIT = 1
+ private final val ONGOING = 2
+ private final val END = 3
+ private final val FLUSHED = 4
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/nio/charset/CharsetEncoder.scala b/examples/scala-js/javalib/src/main/scala/java/nio/charset/CharsetEncoder.scala
new file mode 100644
index 0000000..37d2296
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/nio/charset/CharsetEncoder.scala
@@ -0,0 +1,235 @@
+package java.nio.charset
+
+import scala.annotation.{switch, tailrec}
+
+import java.nio._
+
+abstract class CharsetEncoder protected (cs: Charset,
+ _averageBytesPerChar: Float, _maxBytesPerChar: Float,
+ private[this] var _replacement: Array[Byte]) {
+
+ import CharsetEncoder._
+
+ protected def this(cs: Charset, _averageBytesPerChar: Float,
+ _maxBytesPerChar: Float) =
+ this(cs, _averageBytesPerChar, _averageBytesPerChar, Array('?'.toByte))
+
+ // Config
+
+ private[this] var _malformedInputAction: CodingErrorAction =
+ CodingErrorAction.REPORT
+ private[this] var _unmappableCharacterAction: CodingErrorAction =
+ CodingErrorAction.REPORT
+
+ // Status
+
+ private[this] var status: Int = INIT
+
+ // Methods
+
+ final def charset(): Charset = cs
+
+ final def replacement(): Array[Byte] = _replacement
+
+ final def replaceWith(newReplacement: Array[Byte]): CharsetEncoder = {
+ if (newReplacement == null || newReplacement.length == 0 ||
+ newReplacement.length > maxBytesPerChar ||
+ !isLegalReplacement(newReplacement))
+ throw new IllegalArgumentException
+
+ _replacement = newReplacement
+ implReplaceWith(newReplacement)
+ this
+ }
+
+ protected def implReplaceWith(newReplacement: Array[Byte]): Unit = ()
+
+ def isLegalReplacement(repl: Array[Byte]): Boolean = {
+ val decoder = charset.newDecoder
+ val replBuf = ByteBuffer.wrap(repl)
+
+ @inline
+ @tailrec
+ def loop(outBufSize: Int): Boolean = {
+ val result = decoder.decode(replBuf, CharBuffer.allocate(outBufSize), true)
+ if (result.isOverflow) {
+ loop(outBufSize * 2)
+ } else {
+ !replBuf.hasRemaining
+ }
+ }
+
+ loop(2)
+ }
+
+ def malformedInputAction(): CodingErrorAction = _malformedInputAction
+
+ final def onMalformedInput(newAction: CodingErrorAction): CharsetEncoder = {
+ if (newAction == null)
+ throw new IllegalArgumentException("null CodingErrorAction")
+ _malformedInputAction = newAction
+ implOnMalformedInput(newAction)
+ this
+ }
+
+ protected def implOnMalformedInput(newAction: CodingErrorAction): Unit = ()
+
+ def unmappableCharacterAction(): CodingErrorAction = _unmappableCharacterAction
+
+ final def onUnmappableCharacter(newAction: CodingErrorAction): CharsetEncoder = {
+ if (newAction == null)
+ throw new IllegalArgumentException("null CodingErrorAction")
+ _unmappableCharacterAction = newAction
+ implOnUnmappableCharacter(newAction)
+ this
+ }
+
+ protected def implOnUnmappableCharacter(newAction: CodingErrorAction): Unit = ()
+
+ final def averageBytesPerChar(): Float = _averageBytesPerChar
+ final def maxBytesPerChar(): Float = _maxBytesPerChar
+
+ final def encode(in: CharBuffer, out: ByteBuffer,
+ endOfInput: Boolean): CoderResult = {
+
+ if (status == FLUSHED || (!endOfInput && status == END))
+ throw new IllegalStateException
+
+ status = if (endOfInput) END else ONGOING
+
+ @inline
+ @tailrec
+ def loop(): CoderResult = {
+ val result1 = try {
+ encodeLoop(in, out)
+ } catch {
+ case ex: BufferOverflowException =>
+ throw new CoderMalfunctionError(ex)
+ case ex: BufferUnderflowException =>
+ throw new CoderMalfunctionError(ex)
+ }
+
+ val result2 = if (result1.isUnderflow) {
+ val remaining = in.remaining
+ if (endOfInput && remaining > 0)
+ CoderResult.malformedForLength(remaining)
+ else
+ result1
+ } else {
+ result1
+ }
+
+ if (result2.isUnderflow || result2.isOverflow) {
+ result2
+ } else {
+ val action =
+ if (result2.isUnmappable) unmappableCharacterAction
+ else malformedInputAction
+
+ action match {
+ case CodingErrorAction.REPLACE =>
+ if (out.remaining < replacement.length) {
+ CoderResult.OVERFLOW
+ } else {
+ out.put(replacement)
+ in.position(in.position + result2.length)
+ loop()
+ }
+ case CodingErrorAction.REPORT =>
+ result2
+ case CodingErrorAction.IGNORE =>
+ in.position(in.position + result2.length)
+ loop()
+ }
+ }
+ }
+
+ loop()
+ }
+
+ final def flush(out: ByteBuffer): CoderResult = {
+ (status: @switch) match {
+ case END =>
+ val result = implFlush(out)
+ if (result.isUnderflow)
+ status = FLUSHED
+ result
+ case FLUSHED =>
+ CoderResult.UNDERFLOW
+ case _ =>
+ throw new IllegalStateException
+ }
+ }
+
+ protected def implFlush(out: ByteBuffer): CoderResult =
+ CoderResult.UNDERFLOW
+
+ final def reset(): CharsetEncoder = {
+ status = INIT
+ implReset()
+ this
+ }
+
+ protected def implReset(): Unit = ()
+
+ protected def encodeLoop(arg1: CharBuffer, arg2: ByteBuffer): CoderResult
+
+ final def encode(in: CharBuffer): ByteBuffer = {
+ def grow(out: ByteBuffer): ByteBuffer = {
+ if (out.capacity == 0) {
+ ByteBuffer.allocate(1)
+ } else {
+ val result = ByteBuffer.allocate(out.capacity*2)
+ out.flip()
+ result.put(out)
+ result
+ }
+ }
+
+ if (in.remaining == 0) {
+ ByteBuffer.allocate(0)
+ } else {
+ @inline
+ @tailrec
+ def loopEncode(out: ByteBuffer): ByteBuffer = {
+ val result = encode(in, out, endOfInput = true)
+ if (result.isUnderflow) {
+ assert(!in.hasRemaining)
+ out
+ } else if (result.isOverflow) {
+ loopEncode(grow(out))
+ } else {
+ result.throwException()
+ throw new AssertionError("should not get here")
+ }
+ }
+
+ @inline
+ @tailrec
+ def loopFlush(out: ByteBuffer): ByteBuffer = {
+ val result = flush(out)
+ if (result.isUnderflow) {
+ out
+ } else if (result.isOverflow) {
+ loopFlush(grow(out))
+ } else {
+ result.throwException()
+ throw new AssertionError("should not get here")
+ }
+ }
+
+ reset()
+ val initLength = (in.remaining * averageBytesPerChar).toInt
+ val out = loopFlush(loopEncode(ByteBuffer.allocate(initLength)))
+ out.flip()
+ out
+ }
+ }
+}
+
+object CharsetEncoder {
+ private final val INIT = 0
+ private final val ONGOING = 1
+ private final val END = 2
+ private final val FLUSHED = 3
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/nio/charset/CoderMalfunctionError.scala b/examples/scala-js/javalib/src/main/scala/java/nio/charset/CoderMalfunctionError.scala
new file mode 100644
index 0000000..33174f3
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/nio/charset/CoderMalfunctionError.scala
@@ -0,0 +1,3 @@
+package java.nio.charset
+
+class CoderMalfunctionError(cause: Exception) extends Error(cause)
diff --git a/examples/scala-js/javalib/src/main/scala/java/nio/charset/CoderResult.scala b/examples/scala-js/javalib/src/main/scala/java/nio/charset/CoderResult.scala
new file mode 100644
index 0000000..fdc63cc
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/nio/charset/CoderResult.scala
@@ -0,0 +1,78 @@
+package java.nio.charset
+
+import scala.annotation.switch
+
+import scala.collection.mutable
+
+import java.nio._
+
+class CoderResult private (kind: Int, _length: Int) {
+ import CoderResult._
+
+ @inline def isUnderflow(): Boolean = kind == Underflow
+ @inline def isOverflow(): Boolean = kind == Overflow
+ @inline def isMalformed(): Boolean = kind == Malformed
+ @inline def isUnmappable(): Boolean = kind == Unmappable
+
+ @inline def isError(): Boolean = isMalformed || isUnmappable
+
+ @inline def length(): Int = {
+ val l = _length
+ if (l < 0)
+ throw new UnsupportedOperationException
+ l
+ }
+
+ def throwException(): Unit = (kind: @switch) match {
+ case Overflow => throw new BufferOverflowException
+ case Underflow => throw new BufferUnderflowException
+ case Malformed => throw new MalformedInputException(_length)
+ case Unmappable => throw new UnmappableCharacterException(_length)
+ }
+}
+
+object CoderResult {
+ private final val Underflow = 0
+ private final val Overflow = 1
+ private final val Malformed = 2
+ private final val Unmappable = 3
+
+ val OVERFLOW: CoderResult = new CoderResult(Overflow, -1)
+ val UNDERFLOW: CoderResult = new CoderResult(Underflow, -1)
+
+ private val Malformed1 = new CoderResult(Malformed, 1)
+ private val Malformed2 = new CoderResult(Malformed, 2)
+ private val Malformed3 = new CoderResult(Malformed, 3)
+ private val Malformed4 = new CoderResult(Malformed, 4)
+
+ private val uniqueMalformed = mutable.Map.empty[Int, CoderResult]
+
+ private val Unmappable1 = new CoderResult(Unmappable, 1)
+ private val Unmappable2 = new CoderResult(Unmappable, 2)
+ private val Unmappable3 = new CoderResult(Unmappable, 3)
+ private val Unmappable4 = new CoderResult(Unmappable, 4)
+
+ private val uniqueUnmappable = mutable.Map.empty[Int, CoderResult]
+
+ @inline def malformedForLength(length: Int): CoderResult = (length: @switch) match {
+ case 1 => Malformed1
+ case 2 => Malformed2
+ case 3 => Malformed3
+ case 4 => Malformed4
+ case _ => malformedForLengthImpl(length)
+ }
+
+ private def malformedForLengthImpl(length: Int): CoderResult =
+ uniqueMalformed.getOrElseUpdate(length, new CoderResult(Malformed, length))
+
+ @inline def unmappableForLength(length: Int): CoderResult = (length: @switch) match {
+ case 1 => Unmappable1
+ case 2 => Unmappable2
+ case 3 => Unmappable3
+ case 4 => Unmappable4
+ case _ => unmappableForLengthImpl(length)
+ }
+
+ private def unmappableForLengthImpl(length: Int): CoderResult =
+ uniqueUnmappable.getOrElseUpdate(length, new CoderResult(Unmappable, length))
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/nio/charset/CodingErrorAction.scala b/examples/scala-js/javalib/src/main/scala/java/nio/charset/CodingErrorAction.scala
new file mode 100644
index 0000000..63b48bb
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/nio/charset/CodingErrorAction.scala
@@ -0,0 +1,11 @@
+package java.nio.charset
+
+class CodingErrorAction private (name: String) {
+ override def toString(): String = name
+}
+
+object CodingErrorAction {
+ val IGNORE = new CodingErrorAction("IGNORE")
+ val REPLACE = new CodingErrorAction("REPLACE")
+ val REPORT = new CodingErrorAction("REPORT")
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/nio/charset/MalformedInputException.scala b/examples/scala-js/javalib/src/main/scala/java/nio/charset/MalformedInputException.scala
new file mode 100644
index 0000000..4c91c1b
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/nio/charset/MalformedInputException.scala
@@ -0,0 +1,9 @@
+package java.nio.charset
+
+class MalformedInputException(
+ inputLength: Int) extends CharacterCodingException {
+ def getInputLength(): Int = inputLength
+
+ override def getMessage(): String =
+ "Input length = " + inputLength
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/nio/charset/StandardCharsets.scala b/examples/scala-js/javalib/src/main/scala/java/nio/charset/StandardCharsets.scala
new file mode 100644
index 0000000..38f3f98
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/nio/charset/StandardCharsets.scala
@@ -0,0 +1,14 @@
+package java.nio.charset
+
+final class StandardCharsets private {}
+
+object StandardCharsets {
+ import scala.scalajs.niocharset.{StandardCharsets => SC}
+
+ def ISO_8859_1: Charset = SC.ISO_8859_1
+ def US_ASCII: Charset = SC.US_ASCII
+ def UTF_8: Charset = SC.UTF_8
+ def UTF_16BE: Charset = SC.UTF_16BE
+ def UTF_16LE: Charset = SC.UTF_16LE
+ def UTF_16: Charset = SC.UTF_16
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/nio/charset/UnmappableCharacterException.scala b/examples/scala-js/javalib/src/main/scala/java/nio/charset/UnmappableCharacterException.scala
new file mode 100644
index 0000000..5748f70
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/nio/charset/UnmappableCharacterException.scala
@@ -0,0 +1,9 @@
+package java.nio.charset
+
+class UnmappableCharacterException(
+ inputLength: Int) extends CharacterCodingException {
+ def getInputLength(): Int = inputLength
+
+ override def getMessage(): String =
+ "Input length = " + inputLength
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/nio/charset/UnsupportedCharsetException.scala b/examples/scala-js/javalib/src/main/scala/java/nio/charset/UnsupportedCharsetException.scala
new file mode 100644
index 0000000..97a7a4e
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/nio/charset/UnsupportedCharsetException.scala
@@ -0,0 +1,6 @@
+package java.nio.charset
+
+class UnsupportedCharsetException(
+ charsetName: String) extends IllegalArgumentException(charsetName) {
+ def getCharsetName(): String = charsetName
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/util/Arrays.scala b/examples/scala-js/javalib/src/main/scala/java/util/Arrays.scala
new file mode 100644
index 0000000..ed9afd1
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/util/Arrays.scala
@@ -0,0 +1,401 @@
+package java.util
+
+import scala.annotation.tailrec
+
+object Arrays {
+ def sort[T <: Object](array: Array[Object], comparator: Comparator[T]): Unit = {
+ scala.util.Sorting.stableSort[Object](array,
+ (a: Object, b: Object) =>
+ comparator.compare(a.asInstanceOf[T], b.asInstanceOf[T]) < 0)
+ }
+
+ def fill(a: Array[Boolean], value: Boolean): Unit =
+ fillImpl(a, value)
+
+ def fill(a: Array[Boolean], fromIndex: Int, toIndex: Int, value: Boolean): Unit =
+ fillImpl(a, fromIndex, toIndex, value)
+
+ def fill(a: Array[Byte], value: Byte): Unit =
+ fillImpl(a, value)
+
+ def fill(a: Array[Byte], fromIndex: Int, toIndex: Int, value: Byte): Unit =
+ fillImpl(a, fromIndex, toIndex, value)
+
+ def fill(a: Array[Char], value: Char): Unit =
+ fillImpl(a, value)
+
+ def fill(a: Array[Char], fromIndex: Int, toIndex: Int, value: Char): Unit =
+ fillImpl(a, fromIndex, toIndex, value)
+
+ def fill(a: Array[Short], value: Short): Unit =
+ fillImpl(a, value)
+
+ def fill(a: Array[Short], fromIndex: Int, toIndex: Int, value: Short): Unit =
+ fillImpl(a, fromIndex, toIndex, value)
+
+ def fill(a: Array[Int], value: Int): Unit =
+ fillImpl(a, value)
+
+ def fill(a: Array[Int], fromIndex: Int, toIndex: Int, value: Int): Unit =
+ fillImpl(a, fromIndex, toIndex, value)
+
+ def fill(a: Array[Long], value: Long): Unit =
+ fillImpl(a, value)
+
+ def fill(a: Array[Long], fromIndex: Int, toIndex: Int, value: Long): Unit =
+ fillImpl(a, fromIndex, toIndex, value)
+
+ def fill(a: Array[Float], value: Float): Unit =
+ fillImpl(a, value)
+
+ def fill(a: Array[Float], fromIndex: Int, toIndex: Int, value: Float): Unit =
+ fillImpl(a, fromIndex, toIndex, value)
+
+ def fill(a: Array[Double], value: Double): Unit =
+ fillImpl(a, value)
+
+ def fill(a: Array[Double], fromIndex: Int, toIndex: Int, value: Double): Unit =
+ fillImpl(a, fromIndex, toIndex, value)
+
+ private def fillImpl[@specialized T](a: Array[T], value: T): Unit = {
+ var i = 0
+ while (i != a.length) {
+ a(i) = value
+ i += 1
+ }
+ }
+
+ private def fillImpl[@specialized T](a: Array[T],
+ fromIndex: Int, toIndex: Int, value: T): Unit = {
+ if (fromIndex > toIndex)
+ throw new IllegalArgumentException
+ if (fromIndex < 0 || toIndex > a.length)
+ throw new ArrayIndexOutOfBoundsException
+
+ var i = fromIndex
+ while (i != toIndex) {
+ a(i) = value
+ i += 1
+ }
+ }
+
+ def fill(a: Array[AnyRef], value: AnyRef): Unit = {
+ var i = 0
+ while (i < a.length) {
+ a(i) = value
+ i += 1
+ }
+ }
+
+ def fill(a: Array[AnyRef],
+ fromIndex: Int, toIndex: Int, value: AnyRef): Unit = {
+ if (fromIndex > toIndex)
+ throw new IllegalArgumentException
+ if (fromIndex < 0 || toIndex > a.length)
+ throw new ArrayIndexOutOfBoundsException
+
+ var i = fromIndex
+ while (i < toIndex) {
+ a(i) = value
+ i += 1
+ }
+ }
+
+ @inline private def checkIndexForBinarySearch(
+ length: Int, start: Int, end: Int): Unit = {
+ if (start > end)
+ throw new IllegalArgumentException("fromIndex(" + start + ") > toIndex(" + end + ")")
+ if (start < 0)
+ throw new ArrayIndexOutOfBoundsException("Array index out of range: " + start)
+ if (end > length)
+ throw new ArrayIndexOutOfBoundsException("Array index out of range: " + end)
+ }
+
+ def binarySearch(a: Array[Char], key: Char): Int =
+ binarySearchImpl[Char](a, 0, a.length, key, _ < _)
+
+ def binarySearch(a: Array[Char],
+ startIndex: Int, endIndex: Int, key: Char): Int = {
+ checkIndexForBinarySearch(a.length, startIndex, endIndex)
+ binarySearchImpl[Char](a, startIndex, endIndex, key, _ < _)
+ }
+
+ def binarySearch(a: Array[Short], key: Short): Int =
+ binarySearchImpl[Short](a, 0, a.length, key, _ < _)
+
+ def binarySearch(a: Array[Short],
+ startIndex: Int, endIndex: Int, key: Short): Int = {
+ checkIndexForBinarySearch(a.length, startIndex, endIndex)
+ binarySearchImpl[Short](a, startIndex, endIndex, key, _ < _)
+ }
+
+ def binarySearch(a: Array[Int], key: Int): Int =
+ binarySearchImpl[Int](a, 0, a.length, key, _ < _)
+
+ def binarySearch(a: Array[Int],
+ startIndex: Int, endIndex: Int, key: Int): Int = {
+ checkIndexForBinarySearch(a.length, startIndex, endIndex)
+ binarySearchImpl[Int](a, startIndex, endIndex, key, _ < _)
+ }
+
+ def binarySearch(a: Array[Long], key: Long): Int =
+ binarySearchImpl[Long](a, 0, a.length, key, _ < _)
+
+ def binarySearch(a: Array[Long],
+ startIndex: Int, endIndex: Int, key: Long): Int = {
+ checkIndexForBinarySearch(a.length, startIndex, endIndex)
+ binarySearchImpl[Long](a, startIndex, endIndex, key, _ < _)
+ }
+
+ def binarySearch(a: Array[Float], key: Float): Int =
+ binarySearchImpl[Float](a, 0, a.length, key, _ < _)
+
+ def binarySearch(a: Array[Float],
+ startIndex: Int, endIndex: Int, key: Float): Int = {
+ checkIndexForBinarySearch(a.length, startIndex, endIndex)
+ binarySearchImpl[Float](a, startIndex, endIndex, key, _ < _)
+ }
+
+ def binarySearch(a: Array[Double], key: Double): Int =
+ binarySearchImpl[Double](a, 0, a.length, key, _ < _)
+
+ def binarySearch(a: Array[Double],
+ startIndex: Int, endIndex: Int, key: Double): Int = {
+ checkIndexForBinarySearch(a.length, startIndex, endIndex)
+ binarySearchImpl[Double](a, startIndex, endIndex, key, _ < _)
+ }
+
+ @inline
+ @tailrec
+ private def binarySearchImpl[@specialized T](a: Array[T],
+ startIndex: Int, endIndex: Int, key: T, lt: (T, T) => Boolean): Int = {
+ if (startIndex == endIndex) {
+ // Not found
+ -startIndex - 1
+ } else {
+ // Indices are unsigned 31-bit integer, so this does not overflow
+ val mid = (startIndex + endIndex) >>> 1
+ val elem = a(mid)
+ if (lt(key, elem)) {
+ binarySearchImpl(a, startIndex, mid, key, lt)
+ } else if (key == elem) {
+ // Found
+ mid
+ } else {
+ binarySearchImpl(a, mid + 1, endIndex, key, lt)
+ }
+ }
+ }
+
+ def binarySearch(a: Array[AnyRef], key: AnyRef): Int =
+ binarySearchImplRef(a, 0, a.length, key)
+
+ def binarySearch(a: Array[AnyRef],
+ startIndex: Int, endIndex: Int, key: AnyRef): Int = {
+ checkIndexForBinarySearch(a.length, startIndex, endIndex)
+ binarySearchImplRef(a, startIndex, endIndex, key)
+ }
+
+ @inline
+ @tailrec
+ def binarySearchImplRef(a: Array[AnyRef],
+ startIndex: Int, endIndex: Int, key: AnyRef): Int = {
+ if (startIndex == endIndex) {
+ // Not found
+ -startIndex - 1
+ } else {
+ // Indices are unsigned 31-bit integer, so this does not overflow
+ val mid = (startIndex + endIndex) >>> 1
+ val cmp = key.asInstanceOf[Comparable[AnyRef]].compareTo(a(mid))
+ if (cmp < 0) {
+ binarySearchImplRef(a, startIndex, mid, key)
+ } else if (cmp == 0) {
+ // Found
+ mid
+ } else {
+ binarySearchImplRef(a, mid + 1, endIndex, key)
+ }
+ }
+ }
+
+ def copyOf(original: Array[Boolean], newLength: Int): Array[Boolean] =
+ copyOfImpl(original, newLength, new Array(_))
+
+ def copyOfRange(original: Array[Boolean], start: Int, end: Int): Array[Boolean] =
+ copyOfRangeImpl(original, start, end, new Array(_))
+
+ def copyOf(original: Array[Char], newLength: Int): Array[Char] =
+ copyOfImpl(original, newLength, new Array(_))
+
+ def copyOfRange(original: Array[Char], start: Int, end: Int): Array[Char] =
+ copyOfRangeImpl(original, start, end, new Array(_))
+
+ def copyOf(original: Array[Byte], newLength: Int): Array[Byte] =
+ copyOfImpl(original, newLength, new Array(_))
+
+ def copyOfRange(original: Array[Byte], start: Int, end: Int): Array[Byte] =
+ copyOfRangeImpl(original, start, end, new Array(_))
+
+ def copyOf(original: Array[Short], newLength: Int): Array[Short] =
+ copyOfImpl(original, newLength, new Array(_))
+
+ def copyOfRange(original: Array[Short], start: Int, end: Int): Array[Short] =
+ copyOfRangeImpl(original, start, end, new Array(_))
+
+ def copyOf(original: Array[Int], newLength: Int): Array[Int] =
+ copyOfImpl(original, newLength, new Array(_))
+
+ def copyOfRange(original: Array[Int], start: Int, end: Int): Array[Int] =
+ copyOfRangeImpl(original, start, end, new Array(_))
+
+ def copyOf(original: Array[Long], newLength: Int): Array[Long] =
+ copyOfImpl(original, newLength, new Array(_))
+
+ def copyOfRange(original: Array[Long], start: Int, end: Int): Array[Long] =
+ copyOfRangeImpl(original, start, end, new Array(_))
+
+ def copyOf(original: Array[Float], newLength: Int): Array[Float] =
+ copyOfImpl(original, newLength, new Array(_))
+
+ def copyOfRange(original: Array[Float], start: Int, end: Int): Array[Float] =
+ copyOfRangeImpl(original, start, end, new Array(_))
+
+ def copyOf(original: Array[Double], newLength: Int): Array[Double] =
+ copyOfImpl(original, newLength, new Array(_))
+
+ def copyOfRange(original: Array[Double], start: Int, end: Int): Array[Double] =
+ copyOfRangeImpl(original, start, end, new Array(_))
+
+ @inline private def copyOfImpl[@specialized T](original: Array[T],
+ newLength: Int, newArray: Int => Array[T]): Array[T] = {
+ checkArrayLength(newLength)
+ val copyLength = Math.min(newLength, original.length)
+ val ret = newArray(newLength)
+ System.arraycopy(original, 0, ret, 0, copyLength)
+ ret
+ }
+
+ @inline private def copyOfRangeImpl[@specialized T](original: Array[T],
+ start: Int, end: Int, newArray: Int => Array[T]): Array[T] = {
+ checkIndicesForCopyOfRange(original.length, start, end)
+ val retLength = end - start
+ val copyLength = Math.min(retLength, original.length - start)
+ val ret = newArray(retLength)
+ System.arraycopy(original, start, ret, 0, copyLength)
+ ret
+ }
+
+ def copyOf(original: Array[AnyRef], newLength: Int): Array[AnyRef] = {
+ checkArrayLength(newLength)
+ val copyLength = Math.min(newLength, original.length)
+ val ret = java.lang.reflect.Array.newInstance(
+ original.getClass.getComponentType, newLength).asInstanceOf[Array[AnyRef]]
+ System.arraycopy(original, 0, ret, 0, copyLength)
+ ret
+ }
+
+ def copyOfRange(original: Array[AnyRef], start: Int, end: Int): Array[AnyRef] = {
+ checkIndicesForCopyOfRange(original.length, start, end)
+ val retLength = end - start
+ val copyLength = Math.min(retLength, original.length - start)
+ val ret = java.lang.reflect.Array.newInstance(
+ original.getClass.getComponentType, retLength).asInstanceOf[Array[AnyRef]]
+ System.arraycopy(original, start, ret, 0, copyLength)
+ ret
+ }
+
+ @inline private def checkArrayLength(len: Int): Unit = {
+ if (len < 0)
+ throw new NegativeArraySizeException
+ }
+
+ @inline private def checkIndicesForCopyOfRange(
+ len: Int, start: Int, end: Int): Unit = {
+ if (start > end)
+ throw new IllegalArgumentException(start + " > " + end)
+ if (start < 0 || start > len)
+ throw new ArrayIndexOutOfBoundsException
+ }
+
+ def hashCode(a: Array[Boolean]): Int = {
+ if (a == null) 0
+ else a.foldLeft(1)((acc, x) => 31*acc + new java.lang.Boolean(x).hashCode)
+ }
+
+ def hashCode(a: Array[Char]): Int = {
+ if (a == null) 0
+ else a.foldLeft(1)((acc, x) => 31*acc + new java.lang.Character(x).hashCode)
+ }
+
+ def hashCode(a: Array[Byte]): Int = {
+ if (a == null) 0
+ else a.foldLeft(1)((acc, x) => 31*acc + new java.lang.Byte(x).hashCode)
+ }
+
+ def hashCode(a: Array[Short]): Int = {
+ if (a == null) 0
+ else a.foldLeft(1)((acc, x) => 31*acc + new java.lang.Short(x).hashCode)
+ }
+
+ def hashCode(a: Array[Int]): Int = {
+ if (a == null) 0
+ else a.foldLeft(1)((acc, x) => 31*acc + new java.lang.Integer(x).hashCode)
+ }
+
+ def hashCode(a: Array[Long]): Int = {
+ if (a == null) 0
+ else a.foldLeft(1)((acc, x) => 31*acc + new java.lang.Long(x).hashCode)
+ }
+
+ def hashCode(a: Array[Float]): Int = {
+ if (a == null) 0
+ else a.foldLeft(1)((acc, x) => 31*acc + new java.lang.Float(x).hashCode)
+ }
+
+ def hashCode(a: Array[Double]): Int = {
+ if (a == null) 0
+ else a.foldLeft(1)((acc, x) => 31*acc + new java.lang.Double(x).hashCode)
+ }
+
+ def hashCode(a: Array[AnyRef]): Int = {
+ if (a == null) 0
+ else a.foldLeft(1)((acc, x) => 31*acc + (if (x == null) 0 else x.hashCode))
+ }
+
+ def equals(a: Array[Boolean], b: Array[Boolean]): Boolean =
+ (a eq b) || (a != null && b != null && a.length == b.length &&
+ (0 until a.size).forall(i => a(i) == b(i)))
+
+ def equals(a: Array[Char], b: Array[Char]): Boolean =
+ (a eq b) || (a != null && b != null && a.length == b.length &&
+ (0 until a.size).forall(i => a(i) == b(i)))
+
+ def equals(a: Array[Byte], b: Array[Byte]): Boolean =
+ (a eq b) || (a != null && b != null && a.length == b.length &&
+ (0 until a.size).forall(i => a(i) == b(i)))
+
+ def equals(a: Array[Short], b: Array[Short]): Boolean =
+ (a eq b) || (a != null && b != null && a.length == b.length &&
+ (0 until a.size).forall(i => a(i) == b(i)))
+
+ def equals(a: Array[Int], b: Array[Int]): Boolean =
+ (a eq b) || (a != null && b != null && a.length == b.length &&
+ (0 until a.size).forall(i => a(i) == b(i)))
+
+ def equals(a: Array[Long], b: Array[Long]): Boolean =
+ (a eq b) || (a != null && b != null && a.length == b.length &&
+ (0 until a.size).forall(i => a(i) == b(i)))
+
+ def equals(a: Array[Float], b: Array[Float]): Boolean =
+ (a eq b) || (a != null && b != null && a.length == b.length &&
+ (0 until a.size).forall(i => a(i) == b(i)))
+
+ def equals(a: Array[Double], b: Array[Double]): Boolean =
+ (a eq b) || (a != null && b != null && a.length == b.length &&
+ (0 until a.size).forall(i => a(i) == b(i)))
+
+ def equals(a: Array[AnyRef], b: Array[AnyRef]): Boolean =
+ (a eq b) || (a != null && b != null && a.length == b.length &&
+ (0 until a.size).forall(i => a(i) == b(i)))
+
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/util/Comparator.scala b/examples/scala-js/javalib/src/main/scala/java/util/Comparator.scala
new file mode 100644
index 0000000..d63c48e
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/util/Comparator.scala
@@ -0,0 +1,6 @@
+package java.util
+
+trait Comparator[A] {
+ def compare(o1: A, o2: A): Int
+ def equals(obj: Any): Boolean
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/util/Date.scala b/examples/scala-js/javalib/src/main/scala/java/util/Date.scala
new file mode 100644
index 0000000..8f4e85f
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/util/Date.scala
@@ -0,0 +1,147 @@
+/**
+ * 2014 Matt Seddon
+ * This code is donated in full to the scala-js project.
+ */
+
+package java.util
+
+import scalajs.js
+
+class Date private (private val date: js.Date) extends Object
+ with Serializable with Cloneable with Comparable[Date] {
+
+ import Date._
+
+ def this() = this(new js.Date())
+
+ @Deprecated
+ def this(year: Int, month: Int, date: Int, hrs: Int, min: Int, sec: Int) = {
+ this(new js.Date())
+ this.date.setFullYear(1900 + year, month, date)
+ this.date.setHours(hrs, min, sec, 0)
+ }
+
+ @Deprecated
+ def this(year: Int, month: Int, date: Int, hrs: Int, min: Int) =
+ this(year, month, date, hrs, min, 0)
+
+ @Deprecated
+ def this(year: Int, month: Int, date: Int) =
+ this(year, month, date, 0, 0, 0)
+
+ def this(date: Long) = this(new js.Date(date))
+
+ @Deprecated
+ def this(date: String) = this(new js.Date(date))
+
+ def after(when: Date): Boolean = date.getTime() > when.date.getTime()
+
+ def before(when: Date): Boolean = date.getTime() < when.date.getTime()
+
+ override def clone(): Object = new Date(new js.Date(date.getTime()))
+
+ override def compareTo(anotherDate: Date): Int =
+ date.getTime().compareTo(anotherDate.date.getTime())
+
+ override def equals(obj: Any): Boolean = obj match {
+ case d: Date => d.date.getTime() == date.getTime()
+ case _ => false
+ }
+
+ override def hashCode(): Int = date.getTime().hashCode()
+
+ @Deprecated
+ def getDate(): Int = date.getDate()
+
+ @Deprecated
+ def getDay(): Int = date.getDay()
+
+ @Deprecated
+ def getHours(): Int = date.getHours()
+
+ @Deprecated
+ def getMinutes(): Int = date.getMinutes()
+
+ @Deprecated
+ def getMonth(): Int = date.getMonth()
+
+ @Deprecated
+ def getSeconds(): Int = date.getSeconds()
+
+ def getTime(): Long = date.getTime().toLong
+
+ @Deprecated
+ def getTimeZoneOffset(): Int = date.getTimezoneOffset()
+
+ @Deprecated
+ def getYear(): Int = date.getFullYear() - 1900
+
+ @Deprecated
+ def setDate(date: Int): Unit = this.date.setDate(date)
+
+ @Deprecated
+ def setHours(hours: Int): Unit = date.setHours(hours)
+
+ @Deprecated
+ def setMinutes(minutes: Int): Unit = date.setMinutes(minutes)
+
+ @Deprecated
+ def setMonth(month: Int): Unit = date.setMonth(month)
+
+ @Deprecated
+ def setSeconds(seconds: Int): Unit = date.setSeconds(seconds)
+
+ def setTime(time: Long): Unit = date.setTime(time)
+
+ @Deprecated
+ def setYear(year: Int): Unit = date.setFullYear(1900 + year)
+
+ @Deprecated
+ def toGMTString(): String = {
+ date.getUTCDate() + " " + Months(date.getUTCMonth()) + " " +
+ date.getUTCFullYear() + " " + pad0(date.getUTCHours()) + ":" +
+ pad0(date.getUTCMinutes()) + ":" +
+ pad0(date.getUTCSeconds()) +" GMT"
+ }
+
+ @Deprecated
+ def toLocaleString(): String = {
+ date.getDate() + "-" + Months(date.getMonth()) + "-" +
+ date.getFullYear() + "-" + pad0(date.getHours()) + ":" +
+ pad0(date.getMinutes()) + ":" + pad0(date.getSeconds())
+ }
+
+ override def toString(): String = {
+ val offset = -date.getTimezoneOffset()
+ val sign = if(offset < 0) "-" else "+"
+ val hours = pad0(Math.abs(offset) / 60)
+ val mins = pad0(Math.abs(offset) % 60)
+ Days(date.getDay()) + " "+ Months(date.getMonth()) + " " +
+ pad0(date.getHours()) + ":" + pad0(date.getMinutes()) + ":" +
+ pad0(date.getSeconds()) + " GMT" + sign + hours + mins + " " +
+ date.getFullYear()
+ }
+}
+
+object Date {
+ private val Days = Array(
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat")
+
+ private val Months = Array(
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec")
+
+ private def pad0(i: Int): String = {
+ val str = "" + i
+ if (str.length < 2) "0" + str else str
+ }
+
+ @Deprecated
+ def UTC(year: Int, month: Int, date: Int,
+ hrs: Int, min: Int, sec: Int): Long =
+ js.Date.UTC(year + 1900, month, date, hrs, min, sec).toLong
+
+ @Deprecated
+ def parse(string: String): Long =
+ new Date(new js.Date(string)).getTime.toLong
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/util/Formattable.scala b/examples/scala-js/javalib/src/main/scala/java/util/Formattable.scala
new file mode 100644
index 0000000..e651fbb
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/util/Formattable.scala
@@ -0,0 +1,5 @@
+package java.util
+
+trait Formattable {
+ def formatTo(formatter: Formatter, flags: Int, width: Int, precision: Int): Unit
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/util/FormattableFlags.scala b/examples/scala-js/javalib/src/main/scala/java/util/FormattableFlags.scala
new file mode 100644
index 0000000..02f5bce
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/util/FormattableFlags.scala
@@ -0,0 +1,7 @@
+package java.util
+
+object FormattableFlags {
+ final val ALTERNATE = 4
+ final val LEFT_JUSTIFY = 1
+ final val UPPERCASE = 2
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/util/Formatter.scala b/examples/scala-js/javalib/src/main/scala/java/util/Formatter.scala
new file mode 100644
index 0000000..5e0ab22
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/util/Formatter.scala
@@ -0,0 +1,273 @@
+package java.util
+
+import scala.annotation.switch
+import scala.scalajs.js
+
+import java.io._
+import java.lang._
+
+final class Formatter(private val dest: Appendable) extends Closeable with Flushable {
+ import Formatter._
+
+ var closed = false
+
+ def this() = this(new StringBuilder())
+
+ def close(): Unit = {
+ if (!closed) {
+ dest match {
+ case cl: Closeable => cl.close()
+ case _ =>
+ }
+ }
+ closed = true
+ }
+
+ def flush(): Unit = ifNotClosed {
+ dest match {
+ case fl: Flushable => fl.flush()
+ case _ =>
+ }
+ }
+
+ // Begin implem of format()
+
+ def format(format_in: String, args: Array[AnyRef]): Formatter = ifNotClosed {
+ import js.Any.fromDouble // to have .toFixed and .toExponential on Doubles
+
+ var fmt: String = format_in
+ var lastImplicitIndex: Int = 0
+ var lastIndex: Int = 0 // required for < flag
+
+ while (!fmt.isEmpty) {
+ fmt match {
+ case RegularChunk(matchResult) =>
+ fmt = fmt.substring(matchResult(0).get.length)
+ dest.append(matchResult(0).get)
+
+ case DoublePercent(_) =>
+ fmt = fmt.substring(2)
+ dest.append('%')
+
+ case EOLChunk(_) =>
+ fmt = fmt.substring(2)
+ dest.append('\n')
+
+ case FormattedChunk(matchResult) =>
+ fmt = fmt.substring(matchResult(0).get.length)
+
+ val flags = matchResult(2).get
+ def hasFlag(flag: String) = flags.indexOf(flag) >= 0
+
+ val indexStr = matchResult(1).getOrElse("")
+ val index = if (!indexStr.isEmpty) {
+ Integer.parseInt(indexStr)
+ } else if (hasFlag("<")) {
+ lastIndex
+ } else {
+ lastImplicitIndex += 1
+ lastImplicitIndex
+ }
+ lastIndex = index
+ if (index <= 0 || index > args.length)
+ throw new MissingFormatArgumentException(matchResult(5).get)
+ val arg = args(index-1)
+
+ val widthStr = matchResult(3).getOrElse("")
+ val hasWidth = !widthStr.isEmpty
+ val width =
+ if (hasWidth) Integer.parseInt(widthStr)
+ else 0
+
+ val precisionStr = matchResult(4).getOrElse("")
+ val hasPrecision = !precisionStr.isEmpty
+ val precision =
+ if (hasPrecision) Integer.parseInt(precisionStr)
+ else 0
+
+ val conversion = matchResult(5).get.charAt(0)
+
+ def intArg: Int = (arg: Any) match {
+ case arg: Int => arg
+ case arg: Char => arg.toInt
+ }
+ def numberArg: scala.Double = (arg: Any) match {
+ case arg: Number => arg.doubleValue()
+ case arg: Char => arg.toDouble
+ }
+
+ def padCaptureSign(argStr: String, prefix: String) = {
+ val firstChar = argStr.charAt(0)
+ if (firstChar == '+' || firstChar == '-')
+ pad(argStr.substring(1), firstChar+prefix)
+ else
+ pad(argStr, prefix)
+ }
+
+ def strRepeat(s: String, times: Int) = {
+ var result: String = ""
+ var i = times
+ while (i > 0) {
+ result += s
+ i -= 1
+ }
+ result
+ }
+
+ def with_+(s: String, preventZero: scala.Boolean = false) = {
+ if (s.charAt(0) != '-') {
+ if (hasFlag("+"))
+ pad(s, "+", preventZero)
+ else if (hasFlag(" "))
+ pad(s, " ", preventZero)
+ else
+ pad(s, "", preventZero)
+ } else {
+ if (hasFlag("("))
+ pad(s.substring(1) + ")", "(", preventZero)
+ else
+ pad(s.substring(1), "-", preventZero)
+ }
+ }
+
+ def pad(argStr: String, prefix: String = "",
+ preventZero: Boolean = false) = {
+ val prePadLen = argStr.length + prefix.length
+
+ val padStr = {
+ if (width <= prePadLen) {
+ prefix + argStr
+ } else {
+ val padRight = hasFlag("-")
+ val padZero = hasFlag("0") && !preventZero
+ val padLength = width - prePadLen
+ val padChar: String = if (padZero) "0" else " "
+ val padding = strRepeat(padChar, padLength)
+
+ if (padZero && padRight)
+ throw new java.util.IllegalFormatFlagsException(flags)
+ else if (padRight) prefix + argStr + padding
+ else if (padZero) prefix + padding + argStr
+ else padding + prefix + argStr
+ }
+ }
+
+ val casedStr =
+ if (conversion.isUpper) padStr.toUpperCase()
+ else padStr
+ dest.append(casedStr)
+ }
+
+ (conversion: @switch) match {
+ case 'b' | 'B' => pad { arg match {
+ case null => "false"
+ case b: Boolean => String.valueOf(b)
+ case _ => "true"
+ } }
+ case 'h' | 'H' => pad {
+ if (arg eq null) "null"
+ else Integer.toHexString(arg.hashCode)
+ }
+ case 's' | 'S' => arg match {
+ case null if !hasFlag("#") => pad("null")
+ case formattable: Formattable =>
+ val flags = (
+ (if (hasFlag("-")) FormattableFlags.LEFT_JUSTIFY else 0) |
+ (if (hasFlag("#")) FormattableFlags.ALTERNATE else 0) |
+ (if (conversion.isUpper) FormattableFlags.UPPERCASE else 0)
+ )
+
+ formattable.formatTo(this, flags,
+ if (hasWidth) width.toInt else -1,
+ if (hasPrecision) precision.toInt else -1)
+ None // no further processing
+ case t: AnyRef if !hasFlag("#") => pad(t.toString)
+ case _ =>
+ throw new FormatFlagsConversionMismatchException("#", 's')
+ }
+ case 'c' | 'C' =>
+ pad(js.String.fromCharCode(intArg))
+ case 'd' =>
+ with_+(numberArg.toString())
+ case 'o' =>
+ val str = (arg: Any) match {
+ case arg: scala.Int => Integer.toOctalString(arg)
+ case arg: scala.Long => Long.toOctalString(arg)
+ }
+ padCaptureSign(str, if (hasFlag("#")) "0" else "")
+ case 'x' | 'X' =>
+ val str = (arg: Any) match {
+ case arg: scala.Int => Integer.toHexString(arg)
+ case arg: scala.Long => Long.toHexString(arg)
+ }
+ padCaptureSign(str, if (hasFlag("#")) "0x" else "")
+ case 'e' | 'E' =>
+ sciNotation(if (hasPrecision) precision else 6)
+ case 'g' | 'G' =>
+ val m = Math.abs(numberArg)
+ // precision handling according to JavaDoc
+ // precision here means number of significant digits
+ // not digits after decimal point
+ val p =
+ if (!hasPrecision) 6
+ else if (precision == 0) 1
+ else precision
+ // between 1e-4 and 10e(p): display as fixed
+ if (m >= 1e-4 && m < Math.pow(10, p)) {
+ val sig = Math.ceil(Math.log10(m))
+ with_+(numberArg.toFixed(Math.max(p - sig, 0)))
+ } else sciNotation(p - 1)
+ case 'f' =>
+ with_+({
+ // JavaDoc: 6 is default precision
+ numberArg.toFixed(if (hasPrecision) precision else 6)
+ }, numberArg.isNaN || numberArg.isInfinite)
+ }
+
+ def sciNotation(precision: Int) = {
+ val exp = numberArg.toExponential(precision)
+ with_+({
+ // check if we need additional 0 padding in exponent
+ // JavaDoc: at least 2 digits
+ if ("e" == exp.charAt(exp.length - 3)) {
+ exp.substring(0, exp.length - 1) + "0" +
+ exp.charAt(exp.length - 1)
+ } else exp
+ }, numberArg.isNaN || numberArg.isInfinite)
+ }
+ }
+ }
+
+ this
+ }
+
+ def ioException(): IOException = null
+ def locale(): Locale = ifNotClosed { null }
+ def out(): Appendable = ifNotClosed { dest }
+
+ override def toString(): String = out().toString()
+
+ @inline private def ifNotClosed[T](body: => T): T =
+ if (closed) throwClosedException()
+ else body
+
+ private def throwClosedException(): Nothing =
+ throw new FormatterClosedException()
+
+}
+
+object Formatter {
+
+ private class RegExpExtractor(val regexp: js.RegExp) {
+ def unapply(str: String): Option[js.RegExp.ExecResult] = {
+ Option(regexp.exec(str))
+ }
+ }
+
+ private val RegularChunk = new RegExpExtractor(new js.RegExp("""^[^\x25]+"""))
+ private val DoublePercent = new RegExpExtractor(new js.RegExp("""^\x25{2}"""))
+ private val EOLChunk = new RegExpExtractor(new js.RegExp("""^\x25n"""))
+ private val FormattedChunk = new RegExpExtractor(new js.RegExp(
+ """^\x25(?:([1-9]\d*)\$)?([-#+ 0,\(<]*)(\d*)(?:\.(\d+))?([A-Za-z])"""))
+
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/util/Random.scala b/examples/scala-js/javalib/src/main/scala/java/util/Random.scala
new file mode 100644
index 0000000..80428fb
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/util/Random.scala
@@ -0,0 +1,119 @@
+package java.util
+
+import scala.annotation.tailrec
+
+import scala.scalajs.js
+
+class Random(seed_in: Long) extends AnyRef with java.io.Serializable {
+
+ private var seed: Long = _
+
+ // see nextGaussian()
+ private var nextNextGaussian: Double = _
+ private var haveNextNextGaussian: Boolean = false
+
+ setSeed(seed_in)
+
+ def this() = this(Random.randomSeed())
+
+ def setSeed(seed_in: Long): Unit = {
+ seed = (seed_in ^ 0x5DEECE66DL) & ((1L << 48) - 1)
+ haveNextNextGaussian = false
+ }
+
+ protected def next(bits: Int): Int = {
+ seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1)
+ (seed >>> (48 - bits)).toInt
+ }
+
+ def nextDouble(): Double =
+ ((next(26).toLong << 27) + next(27)) / (1L << 53).toDouble
+
+ def nextBoolean(): Boolean = next(1) != 0
+
+ def nextInt(): Int = next(32)
+
+ def nextInt(n: Int): Int = {
+ if (n <= 0)
+ throw new IllegalArgumentException("n must be positive");
+
+ if ((n & -n) == n) // i.e., n is a power of 2
+ ((n * next(31).toLong) >> 31).toInt
+ else {
+ @tailrec
+ def loop(): Int = {
+ val bits = next(31)
+ val value = bits % n
+ if (bits - value + (n-1) < 0) loop()
+ else value
+ }
+
+ loop()
+ }
+ }
+
+ def nextLong(): Long = (next(32).toLong << 32) + next(32)
+
+ def nextFloat(): Float = next(24) / (1 << 24).toFloat
+
+ def nextBytes(bytes: Array[Byte]): Unit = {
+ var i = 0
+ while (i < bytes.length) {
+ var rnd = nextInt()
+ var n = Math.min(bytes.length - i, 4)
+ while (n > 0) {
+ bytes(i) = rnd.toByte
+ rnd >>= 8
+ n -= 1
+ i += 1
+ }
+ }
+ }
+
+ def nextGaussian(): Double = {
+ // See http://www.protonfish.com/jslib/boxmuller.shtml
+
+ /* The Box-Muller algorithm produces two random numbers at once. We save
+ * the second one in `nextNextGaussian` to be used by the next call to
+ * nextGaussian().
+ */
+
+ if (haveNextNextGaussian) {
+ haveNextNextGaussian = false
+ return nextNextGaussian
+ }
+
+ var x, y, rds: Double = 0
+
+ /* Get two random numbers from -1 to 1.
+ * If the radius is zero or greater than 1, throw them out and pick two new
+ * ones.
+ * Rejection sampling throws away about 20% of the pairs.
+ */
+ do {
+ x = nextDouble()*2-1
+ y = nextDouble()*2-1
+ rds = x*x + y*y
+ } while (rds == 0 || rds > 1)
+
+ val c = Math.sqrt(-2 * Math.log(rds) / rds)
+
+ // Save y*c for next time
+ nextNextGaussian = y*c
+ haveNextNextGaussian = true
+
+ // And return x*c
+ x*c
+ }
+}
+
+object Random {
+
+ /** Generate a random long from JS RNG to seed a new Random */
+ private def randomSeed(): Long =
+ (randomInt().toLong << 32) | (randomInt().toLong & 0xffffffffL)
+
+ private def randomInt(): Int =
+ (Math.floor(js.Math.random() * 4294967296.0) - 2147483648.0).toInt
+
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/util/Throwables.scala b/examples/scala-js/javalib/src/main/scala/java/util/Throwables.scala
new file mode 100644
index 0000000..c4bb3d6
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/util/Throwables.scala
@@ -0,0 +1,166 @@
+package java.util
+
+class ServiceConfigurationError(s: String, e: Throwable) extends Error(s, e) {
+ def this(s: String) = this(s, null)
+}
+
+class ConcurrentModificationException(s: String) extends RuntimeException(s) {
+ def this() = this(null)
+}
+
+class DuplicateFormatFlagsException private() extends IllegalFormatException {
+ private var flags: String = null
+ def this(f: String) {
+ this()
+ if (f == null)
+ throw new NullPointerException()
+ flags = f
+ }
+ def getFlags(): String = flags
+ override def getMessage(): String = s"Flags = '$flags'"
+}
+
+class EmptyStackException extends RuntimeException
+
+class FormatFlagsConversionMismatchException private(private val c: Char) extends IllegalFormatException {
+ private var f: String = null
+ def this(f: String, c: Char) {
+ this(c)
+ if (f == null)
+ throw new NullPointerException()
+ this.f = f
+ }
+ def getFlags(): String = f
+ def getConversion(): Char = c
+ override def getMessage(): String = "Conversion = " + c + ", Flags = " + f
+}
+
+class FormatterClosedException extends IllegalStateException
+
+class IllegalFormatCodePointException(private val c: Int) extends IllegalFormatException {
+ def getCodePoint(): Int = c
+ override def getMessage(): String = s"Code point = $c"
+}
+
+class IllegalFormatConversionException private(private val c: Char) extends IllegalFormatException {
+ private var arg: Class[_] = null
+ def this(c: Char, arg: Class[_]) {
+ this(c)
+ if (arg == null)
+ throw new NullPointerException()
+ this.arg = arg
+ }
+ def getConversion(): Char = c
+ def getArgumentClass(): Class[_] = arg
+ override def getMessage(): String = s"$c != ${arg.getName()}"
+}
+
+class IllegalFormatException private[util] () extends IllegalArgumentException
+
+class IllegalFormatFlagsException private() extends IllegalFormatException {
+ private var flags: String = null
+ def this(f: String) {
+ this()
+ if (f == null)
+ throw new NullPointerException()
+ this.flags = f
+ }
+ def getFlags(): String = flags
+ override def getMessage(): String = "Flags = '" + flags + "'"
+}
+
+class IllegalFormatPrecisionException(private val p: Int) extends IllegalFormatException {
+ def getPrecision(): Int = p
+ override def getMessage(): String = Integer.toString(p)
+}
+
+class IllegalFormatWidthException(private val w: Int) extends IllegalFormatException {
+ def getWidth(): Int = w
+ override def getMessage(): String = Integer.toString(w)
+}
+
+class IllformedLocaleException(s: String, errorIndex: Int)
+ extends RuntimeException(s + (if(errorIndex < 0) "" else " [at index " + errorIndex + "]")) {
+ def this() = this(null, -1)
+ def this(s: String) = this(s, -1)
+ def getErrorIndex(): Int = errorIndex
+}
+
+class InputMismatchException(s: String) extends NoSuchElementException(s) {
+ def this() = this(null)
+}
+
+class InvalidPropertiesFormatException(s: String) extends java.io.IOException(s) {
+ def this(e: Throwable) {
+ this(if(e == null) null.asInstanceOf[String] else e.toString())
+ this.initCause(e)
+ }
+ // private def writeObject(out: java.io.ObjectOutputStream) =
+ // throw new java.io.NotSerializableException("Not serializable.")
+ // private def readObject(in: java.io.ObjectInputStream) =
+ // throw new java.io.NotSerializableException("Not serializable.")
+}
+
+class MissingFormatArgumentException private() extends IllegalFormatException {
+ private var s: String = null
+ def this(s: String) {
+ this()
+ if (s == null)
+ throw new NullPointerException()
+ this.s = s
+ }
+ def getFormatSpecifier(): String = s
+ override def getMessage(): String = "Format specifier '" + s + "'"
+}
+
+class MissingFormatWidthException private() extends IllegalFormatException {
+ private var s: String = null
+ def this(s: String) {
+ this()
+ if (s == null)
+ throw new NullPointerException()
+ this.s = s
+ }
+ def getFormatSpecifier(): String = s
+ override def getMessage(): String = s
+}
+
+class MissingResourceException private[util](
+ s: String, private var className: String, private var key: String, e: Throwable)
+ extends RuntimeException(s, e) {
+ def this(s: String, className: String, key: String) = this(s, className, key, null)
+ def getClassName(): String = className
+ def getKey(): String = key
+}
+
+class NoSuchElementException(s: String) extends RuntimeException(s) {
+ def this() = this(null)
+}
+
+class TooManyListenersException(s: String) extends Exception(s) {
+ def this() = this(null)
+}
+
+class UnknownFormatConversionException private () extends IllegalFormatException {
+ private var s: String = null
+ def this(s: String) {
+ this()
+ if (s == null)
+ throw new NullPointerException()
+ this.s = s
+ }
+ def getConversion(): String = s
+ override def getMessage(): String = s"Conversion = '$s'"
+}
+
+class UnknownFormatFlagsException private() extends IllegalFormatException {
+ private var flags: String = null
+ def this(f: String) {
+ this()
+ if (f == null)
+ throw new NullPointerException()
+ this.flags = f
+ }
+ def getFlags(): String = flags
+ override def getMessage(): String = "Flags = " + flags
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/util/UUID.scala b/examples/scala-js/javalib/src/main/scala/java/util/UUID.scala
new file mode 100644
index 0000000..9e6c1d4
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/util/UUID.scala
@@ -0,0 +1,163 @@
+package java.util
+
+import java.lang.{Long => JLong}
+
+import scala.scalajs.js
+
+final class UUID private (
+ private val i1: Int, private val i2: Int,
+ private val i3: Int, private val i4: Int,
+ private[this] var l1: JLong, private[this] var l2: JLong)
+ extends AnyRef with java.io.Serializable with Comparable[UUID] {
+
+ import UUID._
+
+ /* Most significant long:
+ *
+ * 0xFFFFFFFF00000000 time_low
+ * 0x00000000FFFF0000 time_mid
+ * 0x000000000000F000 version
+ * 0x0000000000000FFF time_hi
+ *
+ * Least significant long:
+ *
+ * 0xC000000000000000 variant
+ * 0x3FFF000000000000 clock_seq
+ * 0x0000FFFFFFFFFFFF node
+ */
+
+ def this(mostSigBits: Long, leastSigBits: Long) = {
+ this((mostSigBits >>> 32).toInt, mostSigBits.toInt,
+ (leastSigBits >>> 32).toInt, leastSigBits.toInt,
+ mostSigBits, leastSigBits)
+ }
+
+ def getLeastSignificantBits(): Long = {
+ if (l2 eq null)
+ l2 = (i3.toLong << 32) | (i4.toLong & 0xffffffffL)
+ l2.longValue
+ }
+
+ def getMostSignificantBits(): Long = {
+ if (l1 eq null)
+ l1 = (i1.toLong << 32) | (i2.toLong & 0xffffffffL)
+ l1.longValue
+ }
+
+ def version(): Int =
+ (i2 & 0xf000) >> 12
+
+ def variant(): Int = {
+ if ((i3 & 0x80000000) == 0) {
+ // MSB0 not set: NCS backwards compatibility variant
+ 0
+ } else if ((i3 & 0x40000000) != 0) {
+ // MSB1 set: either MS reserved or future reserved
+ (i3 & 0xe0000000) >>> 29
+ } else {
+ // MSB1 not set: RFC 4122 variant
+ 2
+ }
+ }
+
+ def timestamp(): Long = {
+ if (version() != TimeBased)
+ throw new UnsupportedOperationException("Not a time-based UUID")
+ (((i2 >>> 16) | ((i2 & 0x0fff) << 16)).toLong << 32) | (i1.toLong & 0xffffffffL)
+ }
+
+ def clockSequence(): Int = {
+ if (version() != TimeBased)
+ throw new UnsupportedOperationException("Not a time-based UUID")
+ (i3 & 0x3fff0000) >> 16
+ }
+
+ def node(): Long = {
+ if (version() != TimeBased)
+ throw new UnsupportedOperationException("Not a time-based UUID")
+ ((i3 & 0xffff).toLong << 32) | (i4.toLong & 0xffffffffL)
+ }
+
+ override def toString(): String = {
+ @inline def paddedHex8(i: Int): String = {
+ val s = Integer.toHexString(i)
+ "00000000".substring(s.length) + s
+ }
+
+ @inline def paddedHex4(i: Int): String = {
+ val s = Integer.toHexString(i)
+ "0000".substring(s.length) + s
+ }
+
+ paddedHex8(i1) + "-" + paddedHex4(i2 >>> 16) + "-" + paddedHex4(i2 & 0xffff) + "-" +
+ paddedHex4(i3 >>> 16) + "-" + paddedHex4(i3 & 0xffff) + paddedHex8(i4)
+ }
+
+ override def hashCode(): Int =
+ i1 ^ i2 ^ i3 ^ i4
+
+ override def equals(that: Any): Boolean = that match {
+ case that: UUID =>
+ i1 == that.i1 && i2 == that.i2 && i3 == that.i3 && i4 == that.i4
+ case _ =>
+ false
+ }
+
+ def compareTo(that: UUID): Int = {
+ if (this.i1 != that.i1) {
+ if (this.i1 > that.i1) 1 else -1
+ } else if (this.i2 != that.i2) {
+ if (this.i2 > that.i2) 1 else -1
+ } else if (this.i3 != that.i3) {
+ if (this.i3 > that.i3) 1 else -1
+ } else if (this.i4 != that.i4) {
+ if (this.i4 > that.i4) 1 else -1
+ } else {
+ 0
+ }
+ }
+}
+
+object UUID {
+ private final val TimeBased = 1
+ private final val DCESecurity = 2
+ private final val NameBased = 3
+ private final val Random = 4
+
+ private lazy val rng = new Random() // TODO Use java.security.SecureRandom
+
+ def randomUUID(): UUID = {
+ val i1 = rng.nextInt()
+ val i2 = (rng.nextInt() & ~0x0000f000) | 0x00004000
+ val i3 = (rng.nextInt() & ~0xc0000000) | 0x80000000
+ val i4 = rng.nextInt()
+ new UUID(i1, i2, i3, i4, null, null)
+ }
+
+ // Not implemented (requires messing with MD5 or SHA-1):
+ //def nameUUIDFromBytes(name: Array[Byte]): UUID = ???
+
+ def fromString(name: String): UUID = {
+ import Integer.parseInt
+
+ def fail(): Nothing =
+ throw new IllegalArgumentException("Illegal UUID string: "+name)
+
+ @inline def parseHex8(his: String, los: String): Int =
+ (parseInt(his, 16) << 16) | parseInt(los, 16)
+
+ if (name.length != 36 || name.charAt(8) != '-' ||
+ name.charAt(13) != '-' || name.charAt(18) != '-' || name.charAt(23) != '-')
+ fail()
+
+ try {
+ val i1 = parseHex8(name.substring(0, 4), name.substring(4, 8))
+ val i2 = parseHex8(name.substring(9, 13), name.substring(14, 18))
+ val i3 = parseHex8(name.substring(19, 23), name.substring(24, 28))
+ val i4 = parseHex8(name.substring(28, 32), name.substring(32, 36))
+ new UUID(i1, i2, i3, i4, null, null)
+ } catch {
+ case _: NumberFormatException => fail()
+ }
+ }
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/util/concurrent/ExecutionException.scala b/examples/scala-js/javalib/src/main/scala/java/util/concurrent/ExecutionException.scala
new file mode 100644
index 0000000..6d04889
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/util/concurrent/ExecutionException.scala
@@ -0,0 +1,9 @@
+package java.util.concurrent
+
+class ExecutionException(message: String, cause: Throwable)
+ extends Exception(message, cause) {
+
+ protected def this() = this(null, null)
+ protected def this(message: String) = this(message, null)
+ def this(cause: Throwable) = this(null, cause)
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/util/concurrent/Executor.scala b/examples/scala-js/javalib/src/main/scala/java/util/concurrent/Executor.scala
new file mode 100644
index 0000000..d030551
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/util/concurrent/Executor.scala
@@ -0,0 +1,5 @@
+package java.util.concurrent
+
+trait Executor {
+ def execute(command: Runnable): Unit
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/util/concurrent/TimeUnit.scala b/examples/scala-js/javalib/src/main/scala/java/util/concurrent/TimeUnit.scala
new file mode 100644
index 0000000..a77dbfc
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/util/concurrent/TimeUnit.scala
@@ -0,0 +1,133 @@
+package java.util.concurrent
+
+abstract class TimeUnit private (_index: Int,
+ _name: String) extends java.io.Serializable {
+
+ def convert(a: Long, u: TimeUnit): Long
+
+ def toNanos(a: Long): Long
+ def toMicros(a: Long): Long
+ def toMillis(a: Long): Long
+ def toSeconds(a: Long): Long
+ def toMinutes(a: Long): Long
+ def toHours(a: Long): Long
+ def toDays(a: Long): Long
+
+ // not used
+ //private[concurrent] def excessNanos(a: Long, b: Long): Int
+
+ def name(): String = _name
+ def ordinal(): Int = _index
+
+ // methods that cannot be implemented
+ //def timedWait(arg1: AnyRef, arg2: Long): Unit
+ //def timedJoin(arg1: Thread, arg2: Long): Unit
+ //def sleep(arg1: Long): Unit
+
+ override def toString() = name()
+}
+
+object TimeUnit {
+ final val NANOSECONDS: TimeUnit = new TimeUnit(0, "NANOSECONDS") {
+ def convert(a: Long, u: TimeUnit): Long = u.toNanos(a)
+ def toNanos(a: Long): Long = a
+ def toMicros(a: Long): Long = a / (C1/C0)
+ def toMillis(a: Long): Long = a / (C2/C0)
+ def toSeconds(a: Long): Long = a / (C3/C0)
+ def toMinutes(a: Long): Long = a / (C4/C0)
+ def toHours(a: Long): Long = a / (C5/C0)
+ def toDays(a: Long): Long = a / (C6/C0)
+ }
+
+ final val MICROSECONDS: TimeUnit = new TimeUnit(1, "MICROSECONDS") {
+ def convert(a: Long, u: TimeUnit): Long = u.toMicros(a)
+ def toNanos(a: Long): Long = x(a, C1/C0, MAX/(C1/C0))
+ def toMicros(a: Long): Long = a
+ def toMillis(a: Long): Long = a / (C2/C1)
+ def toSeconds(a: Long): Long = a / (C3/C1)
+ def toMinutes(a: Long): Long = a / (C4/C1)
+ def toHours(a: Long): Long = a / (C5/C1)
+ def toDays(a: Long): Long = a / (C6/C1)
+ }
+
+ final val MILLISECONDS: TimeUnit = new TimeUnit(2, "MILLISECONDS") {
+ def convert(a: Long, u: TimeUnit): Long = u.toMillis(a)
+ def toNanos(a: Long): Long = x(a, C2/C0, MAX/(C2/C0))
+ def toMicros(a: Long): Long = x(a, C2/C1, MAX/(C2/C1))
+ def toMillis(a: Long): Long = a
+ def toSeconds(a: Long): Long = a / (C3/C2)
+ def toMinutes(a: Long): Long = a / (C4/C2)
+ def toHours(a: Long): Long = a / (C5/C2)
+ def toDays(a: Long): Long = a / (C6/C2)
+ }
+
+ final val SECONDS: TimeUnit = new TimeUnit(3, "SECONDS") {
+ def convert(a: Long, u: TimeUnit): Long = u.toSeconds(a)
+ def toNanos(a: Long): Long = x(a, C3/C0, MAX/(C3/C0))
+ def toMicros(a: Long): Long = x(a, C3/C1, MAX/(C3/C1))
+ def toMillis(a: Long): Long = x(a, C3/C2, MAX/(C3/C2))
+ def toSeconds(a: Long): Long = a
+ def toMinutes(a: Long): Long = a / (C4/C3)
+ def toHours(a: Long): Long = a / (C5/C3)
+ def toDays(a: Long): Long = a / (C6/C3)
+ }
+
+ final val MINUTES: TimeUnit = new TimeUnit(4, "MINUTES") {
+ def convert(a: Long, u: TimeUnit): Long = u.toMinutes(a)
+ def toNanos(a: Long): Long = x(a, C4/C0, MAX/(C4/C0))
+ def toMicros(a: Long): Long = x(a, C4/C1, MAX/(C4/C1))
+ def toMillis(a: Long): Long = x(a, C4/C2, MAX/(C4/C2))
+ def toSeconds(a: Long): Long = x(a, C4/C3, MAX/(C4/C3))
+ def toMinutes(a: Long): Long = a
+ def toHours(a: Long): Long = a / (C5/C4)
+ def toDays(a: Long): Long = a / (C6/C4)
+ }
+
+ final val HOURS: TimeUnit = new TimeUnit(5, "HOURS") {
+ def convert(a: Long, u: TimeUnit): Long = u.toHours(a)
+ def toNanos(a: Long): Long = x(a, C5/C0, MAX/(C5/C0))
+ def toMicros(a: Long): Long = x(a, C5/C1, MAX/(C5/C1))
+ def toMillis(a: Long): Long = x(a, C5/C2, MAX/(C5/C2))
+ def toSeconds(a: Long): Long = x(a, C5/C3, MAX/(C5/C3))
+ def toMinutes(a: Long): Long = x(a, C5/C4, MAX/(C5/C4))
+ def toHours(a: Long): Long = a
+ def toDays(a: Long): Long = a / (C6/C5)
+ }
+
+ final val DAYS: TimeUnit = new TimeUnit(6, "DAYS") {
+ def convert(a: Long, u: TimeUnit): Long = u.toDays(a)
+ def toNanos(a: Long): Long = x(a, C6/C0, MAX/(C6/C0))
+ def toMicros(a: Long): Long = x(a, C6/C1, MAX/(C6/C1))
+ def toMillis(a: Long): Long = x(a, C6/C2, MAX/(C6/C2))
+ def toSeconds(a: Long): Long = x(a, C6/C3, MAX/(C6/C3))
+ def toMinutes(a: Long): Long = x(a, C6/C4, MAX/(C6/C4))
+ def toHours(a: Long): Long = x(a, C6/C5, MAX/(C6/C5))
+ def toDays(a: Long): Long = a
+ }
+
+ private[this] val _values: Array[TimeUnit] =
+ Array(NANOSECONDS, MICROSECONDS, MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS)
+
+ // deliberately without type ascription to make them compile-time constants
+ private final val C0 = 1L
+ private final val C1 = C0 * 1000L
+ private final val C2 = C1 * 1000L
+ private final val C3 = C2 * 1000L
+ private final val C4 = C3 * 60L
+ private final val C5 = C4 * 60L
+ private final val C6 = C5 * 24L
+ private final val MAX = Long.MaxValue
+
+ def values(): Array[TimeUnit] = _values.clone()
+
+ def valueOf(name: String): TimeUnit = {
+ _values.find(_.name == name).getOrElse(
+ throw new IllegalArgumentException("No enum const TimeUnit." + name))
+ }
+
+ private def x(a: Long, b: Long, max: Long): Long = {
+ if (a > max) MAX
+ else if (a < -max) -MAX
+ else a * b
+ }
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/util/concurrent/atomic/AtomicBoolean.scala b/examples/scala-js/javalib/src/main/scala/java/util/concurrent/atomic/AtomicBoolean.scala
new file mode 100644
index 0000000..5675c31
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/util/concurrent/atomic/AtomicBoolean.scala
@@ -0,0 +1,33 @@
+package java.util.concurrent.atomic
+
+class AtomicBoolean(private[this] var value: Boolean) extends Serializable {
+ def this() = this(false)
+
+ final def get(): Boolean = value
+
+ final def compareAndSet(expect: Boolean, update: Boolean): Boolean = {
+ if (expect != value) false else {
+ value = update
+ true
+ }
+ }
+
+ // For some reason, this method is not final
+ def weakCompareAndSet(expect: Boolean, update: Boolean): Boolean =
+ compareAndSet(expect, update)
+
+ final def set(newValue: Boolean): Unit =
+ value = newValue
+
+ final def lazySet(newValue: Boolean): Unit =
+ set(newValue)
+
+ final def getAndSet(newValue: Boolean): Boolean = {
+ val old = value
+ value = newValue
+ old
+ }
+
+ override def toString(): String =
+ value.toString()
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/util/concurrent/atomic/AtomicInteger.scala b/examples/scala-js/javalib/src/main/scala/java/util/concurrent/atomic/AtomicInteger.scala
new file mode 100644
index 0000000..1f24b7b
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/util/concurrent/atomic/AtomicInteger.scala
@@ -0,0 +1,63 @@
+package java.util.concurrent.atomic
+
+class AtomicInteger(private[this] var value: Int)
+ extends Number with Serializable {
+
+ def this() = this(0)
+
+ final def get(): Int = value
+
+ final def set(newValue: Int): Unit =
+ value = newValue
+
+ final def lazySet(newValue: Int): Unit =
+ set(newValue)
+
+ final def getAndSet(newValue: Int): Int = {
+ val old = value
+ value = newValue
+ old
+ }
+
+ final def compareAndSet(expect: Int, update: Int): Boolean = {
+ if (expect != value) false else {
+ value = update
+ true
+ }
+ }
+
+ final def weakCompareAndSet(expect: Int, update: Int): Boolean =
+ compareAndSet(expect, update)
+
+ final def getAndIncrement(): Int =
+ getAndAdd(1)
+
+ final def getAndDecrement(): Int =
+ getAndAdd(-1)
+
+ @inline final def getAndAdd(delta: Int): Int = {
+ val old = value
+ value = old + delta
+ old
+ }
+
+ final def incrementAndGet(): Int =
+ addAndGet(1)
+
+ final def decrementAndGet(): Int =
+ addAndGet(-1)
+
+ @inline final def addAndGet(delta: Int): Int = {
+ val newValue = value + delta
+ value = newValue
+ newValue
+ }
+
+ override def toString(): String =
+ value.toString()
+
+ def intValue(): Int = value
+ def longValue(): Long = value.toLong
+ def floatValue(): Float = value.toFloat
+ def doubleValue(): Double = value.toDouble
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/util/concurrent/atomic/AtomicLong.scala b/examples/scala-js/javalib/src/main/scala/java/util/concurrent/atomic/AtomicLong.scala
new file mode 100644
index 0000000..5bfecf2
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/util/concurrent/atomic/AtomicLong.scala
@@ -0,0 +1,61 @@
+package java.util.concurrent.atomic
+
+class AtomicLong(private[this] var value: Long) extends Number with Serializable {
+ def this() = this(0L)
+
+ final def get(): Long = value
+
+ final def set(newValue: Long): Unit =
+ value = newValue
+
+ final def lazySet(newValue: Long): Unit =
+ set(newValue)
+
+ final def getAndSet(newValue: Long): Long = {
+ val old = value
+ value = newValue
+ old
+ }
+
+ final def compareAndSet(expect: Long, update: Long): Boolean = {
+ if (expect != value) false else {
+ value = update
+ true
+ }
+ }
+
+ final def weakCompareAndSet(expect: Long, update: Long): Boolean =
+ compareAndSet(expect, update)
+
+ final def getAndIncrement(): Long =
+ getAndAdd(1L)
+
+ final def getAndDecrement(): Long =
+ getAndAdd(-1L)
+
+ @inline final def getAndAdd(delta: Long): Long = {
+ val old = value
+ value = old + delta
+ old
+ }
+
+ final def incrementAndGet(): Long =
+ addAndGet(1L)
+
+ final def decrementAndGet(): Long =
+ addAndGet(-1L)
+
+ @inline final def addAndGet(delta: Long): Long = {
+ val newValue = value + delta
+ value = newValue
+ newValue
+ }
+
+ override def toString(): String =
+ value.toString()
+
+ def intValue(): Int = value.toInt
+ def longValue(): Long = value
+ def floatValue(): Float = value.toFloat
+ def doubleValue(): Double = value.toDouble
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/util/concurrent/atomic/AtomicReference.scala b/examples/scala-js/javalib/src/main/scala/java/util/concurrent/atomic/AtomicReference.scala
new file mode 100644
index 0000000..650b1e0
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/util/concurrent/atomic/AtomicReference.scala
@@ -0,0 +1,34 @@
+package java.util.concurrent.atomic
+
+class AtomicReference[T <: AnyRef](
+ private[this] var value: T) extends Serializable {
+
+ def this() = this(null.asInstanceOf[T])
+
+ final def get(): T = value
+
+ final def set(newValue: T): Unit =
+ value = newValue
+
+ final def lazySet(newValue: T): Unit =
+ set(newValue)
+
+ final def compareAndSet(expect: T, update: T): Boolean = {
+ if (expect ne value) false else {
+ value = update
+ true
+ }
+ }
+
+ final def weakCompareAndSet(expect: T, update: T): Boolean =
+ compareAndSet(expect, update)
+
+ final def getAndSet(newValue: T): T = {
+ val old = value
+ value = newValue
+ old
+ }
+
+ override def toString(): String =
+ String.valueOf(value)
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/util/regex/MatchResult.scala b/examples/scala-js/javalib/src/main/scala/java/util/regex/MatchResult.scala
new file mode 100644
index 0000000..f321c60
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/util/regex/MatchResult.scala
@@ -0,0 +1,13 @@
+package java.util.regex
+
+trait MatchResult {
+ def groupCount(): Int
+
+ def start(): Int
+ def end(): Int
+ def group(): String
+
+ def start(group: Int): Int
+ def end(group: Int): Int
+ def group(group: Int): String
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/util/regex/Matcher.scala b/examples/scala-js/javalib/src/main/scala/java/util/regex/Matcher.scala
new file mode 100644
index 0000000..331f56b
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/util/regex/Matcher.scala
@@ -0,0 +1,274 @@
+package java.util.regex
+
+import scala.language.implicitConversions
+
+import scala.annotation.switch
+
+import scala.scalajs.js
+
+final class Matcher private[regex] (
+ private var pattern0: Pattern, private var input0: CharSequence,
+ private var regionStart0: Int, private var regionEnd0: Int)
+ extends AnyRef with MatchResult {
+
+ import Matcher._
+
+ def pattern(): Pattern = pattern0
+
+ // Configuration (updated manually)
+ private var regexp = new js.RegExp(pattern0.jspattern, pattern0.jsflags)
+ private var inputstr = input0.subSequence(regionStart0, regionEnd0).toString
+
+ // Match result (updated by successful matches)
+ private var lastMatch: js.RegExp.ExecResult = null
+ private var lastMatchIsValid = false
+ private var canStillFind = true
+
+ // Append state (updated by replacement methods)
+ private var appendPos: Int = 0
+
+ // Lookup methods
+
+ def matches(): Boolean = {
+ reset()
+ find()
+ // TODO this check is wrong with non-greedy patterns
+ // Further, it might be wrong to just use ^$ delimiters for two reasons:
+ // - They might already be there
+ // - They might not behave as expected when newline characters are present
+ if ((lastMatch ne null) && (start != 0 || end != inputstr.length))
+ reset()
+ lastMatch ne null
+ }
+
+ def lookingAt(): Boolean = {
+ reset()
+ find()
+ if ((lastMatch ne null) && (start != 0))
+ reset()
+ lastMatch ne null
+ }
+
+ def find(): Boolean = if (canStillFind) {
+ lastMatchIsValid = true
+ lastMatch = regexp.exec(inputstr)
+ if (lastMatch ne null) {
+ if (lastMatch(0).get.isEmpty)
+ regexp.lastIndex += 1
+ } else {
+ canStillFind = false
+ }
+ lastMatch ne null
+ } else false
+
+ def find(start: Int): Boolean = {
+ reset()
+ regexp.lastIndex = start
+ find()
+ }
+
+ // Replace methods
+
+ def appendReplacement(sb: StringBuffer, replacement: String): Matcher = {
+ sb.append(inputstr.substring(appendPos, start))
+
+ @inline def isDigit(c: Char) = c >= '0' && c <= '9'
+
+ val len = replacement.length
+ var i = 0
+ while (i < len) {
+ replacement.charAt(i) match {
+ case '$' =>
+ i += 1
+ val j = i
+ while (i < len && isDigit(replacement.charAt(i)))
+ i += 1
+ val group = Integer.parseInt(replacement.substring(j, i))
+ sb.append(this.group(group))
+
+ case '\\' =>
+ i += 1
+ if (i < len)
+ sb.append(replacement.charAt(i))
+ i += 1
+
+ case c =>
+ sb.append(c)
+ i += 1
+ }
+ }
+
+ appendPos = end
+ this
+ }
+
+ def appendTail(sb: StringBuffer): StringBuffer = {
+ sb.append(inputstr.substring(appendPos))
+ appendPos = inputstr.length
+ sb
+ }
+
+ def replaceFirst(replacement: String): String = {
+ reset()
+
+ if (find()) {
+ val sb = new StringBuffer
+ appendReplacement(sb, replacement)
+ appendTail(sb)
+ sb.toString
+ } else {
+ inputstr
+ }
+ }
+
+ def replaceAll(replacement: String): String = {
+ reset()
+
+ val sb = new StringBuffer
+ while (find()) {
+ appendReplacement(sb, replacement)
+ }
+ appendTail(sb)
+
+ sb.toString
+ }
+
+ // Reset methods
+
+ def reset(): Matcher = {
+ regexp.lastIndex = 0
+ lastMatch = null
+ lastMatchIsValid = false
+ canStillFind = true
+ appendPos = 0
+ this
+ }
+
+ def reset(input: CharSequence): Matcher = {
+ regionStart0 = 0
+ regionEnd0 = input.length()
+ input0 = input
+ inputstr = input0.toString
+ reset()
+ }
+
+ def usePattern(pattern: Pattern): Matcher = {
+ val prevLastIndex = regexp.lastIndex
+ pattern0 = pattern
+ regexp = new js.RegExp(pattern.jspattern, pattern.jsflags)
+ regexp.lastIndex = prevLastIndex
+ lastMatch = null
+ this
+ }
+
+ // Query state methods - implementation of MatchResult
+
+ private def ensureLastMatch: js.RegExp.ExecResult = {
+ if (lastMatch == null)
+ throw new IllegalStateException("No match available")
+ lastMatch
+ }
+
+ def groupCount(): Int = ensureLastMatch.length-1
+
+ def start(): Int = ensureLastMatch.index
+ def end(): Int = start() + group().length
+ def group(): String = ensureLastMatch(0).get
+
+ def start(group: Int): Int = {
+ if (group == 0) start()
+ else {
+ val last = ensureLastMatch
+ // not provided by JS RegExp, so we make up something that at least
+ // will have some sound behavior from scala.util.matching.Regex
+ last(group).fold(-1) {
+ groupStr => inputstr.indexOf(groupStr, last.index)
+ }
+ }
+ }
+
+ def end(group: Int): Int = {
+ val s = start(group)
+ if (s == -1) -1
+ else s + this.group(group).length
+ }
+
+ def group(group: Int): String = ensureLastMatch(group).orNull
+
+ // Seal the state
+
+ def toMatchResult(): MatchResult = new SealedResult(inputstr, lastMatch)
+
+ // Other query state methods
+
+ def hitEnd(): Boolean =
+ lastMatchIsValid && (lastMatch == null || end() == inputstr.length)
+
+ //def requireEnd(): Boolean // I don't understand the spec
+
+ // Stub methods for region management
+
+ def regionStart(): Int = regionStart0
+ def regionEnd(): Int = regionEnd0
+ def region(start: Int, end: Int): Matcher =
+ new Matcher(pattern0, input0, start, end)
+
+ def hasTransparentBounds(): Boolean = false
+ //def useTransparentBounds(b: Boolean): Matcher
+
+ def hasAnchoringBounds(): Boolean = true
+ //def useAnchoringBounds(b: Boolean): Matcher
+}
+
+object Matcher {
+ def quoteReplacement(s: String): String = {
+ var result = ""
+ var i = 0
+ while (i < s.length) {
+ val c = s.charAt(i)
+ result += ((c: @switch) match {
+ case '\\' | '$' => "\\"+c
+ case _ => c
+ })
+ i += 1
+ }
+ result
+ }
+
+ private final class SealedResult(inputstr: String,
+ lastMatch: js.RegExp.ExecResult) extends MatchResult {
+
+ def groupCount(): Int = ensureLastMatch.length-1
+
+ def start(): Int = ensureLastMatch.index
+ def end(): Int = start() + group().length
+ def group(): String = ensureLastMatch(0).get
+
+ def start(group: Int): Int = {
+ if (group == 0) start()
+ else {
+ val last = ensureLastMatch
+
+ // not provided by JS RegExp, so we make up something that at least
+ // will have some sound behavior from scala.util.matching.Regex
+ last(group).fold(-1) {
+ groupStr => inputstr.indexOf(groupStr, last.index)
+ }
+ }
+ }
+
+ def end(group: Int): Int = {
+ val s = start(group)
+ if (s == -1) -1
+ else s + this.group(group).length
+ }
+
+ def group(group: Int): String = ensureLastMatch(group).orNull
+
+ private def ensureLastMatch: js.RegExp.ExecResult = {
+ if (lastMatch == null)
+ throw new IllegalStateException("No match available")
+ lastMatch
+ }
+ }
+}
diff --git a/examples/scala-js/javalib/src/main/scala/java/util/regex/Pattern.scala b/examples/scala-js/javalib/src/main/scala/java/util/regex/Pattern.scala
new file mode 100644
index 0000000..fda103f
--- /dev/null
+++ b/examples/scala-js/javalib/src/main/scala/java/util/regex/Pattern.scala
@@ -0,0 +1,154 @@
+package java.util.regex
+
+import scala.annotation.switch
+
+import scala.scalajs.js
+
+final class Pattern private (pattern0: String, flags0: Int)
+ extends Serializable {
+
+ import Pattern._
+
+ def pattern(): String = pattern0
+ def flags(): Int = flags1
+
+ private[regex] val (jspattern, flags1) = {
+ if ((flags0 & LITERAL) != 0) (quote(pattern0), flags0)
+ else {
+ trySplitHack(pattern0, flags0) orElse
+ tryFlagHack(pattern0, flags0) getOrElse
+ (pattern0, flags0)
+ }
+ }
+
+ private[regex] val jsflags = {
+ var f = "g"
+ if ((flags & CASE_INSENSITIVE) != 0)
+ f += "i"
+ if ((flags & MULTILINE) != 0)
+ f += "m"
+ f
+ }
+
+ override def toString(): String = pattern0
+
+ def matcher(input: CharSequence): Matcher =
+ new Matcher(this, input, 0, input.length)
+
+ def split(input: CharSequence): Array[String] =
+ split(input, 0)
+
+ def split(input: CharSequence, limit: Int): Array[String] = {
+ val lim = if (limit > 0) limit else Int.MaxValue
+
+ val result = js.Array[String]()
+ val inputStr = input.toString
+ val matcher = this.matcher(inputStr)
+ var prevEnd = 0
+
+ // Actually split original string
+ while ((result.length < lim-1) && matcher.find()) {
+ result.push(inputStr.substring(prevEnd, matcher.start))
+ prevEnd = matcher.end
+ }
+ result.push(inputStr.substring(prevEnd))
+
+ // Remove a leading empty element iff the first match was zero-length
+ // and there is no other place the regex matches
+ if (prevEnd == 0 && result.length == 2 && (lim > 2 || !matcher.find())) {
+ Array(inputStr)
+ } else {
+ var len = result.length
+ if (limit == 0) {
+ while (len > 1 && result(len-1).isEmpty)
+ len -= 1
+ }
+
+ val actualResult = new Array[String](len)
+ result.copyToArray(actualResult)
+ actualResult
+ }
+ }
+}
+
+object Pattern {
+ final val UNIX_LINES = 0x01
+ final val CASE_INSENSITIVE = 0x02
+ final val COMMENTS = 0x04
+ final val MULTILINE = 0x08
+ final val LITERAL = 0x10
+ final val DOTALL = 0x20
+ final val UNICODE_CASE = 0x40
+ final val CANON_EQ = 0x80
+ final val UNICODE_CHARACTER_CLASS = 0x100
+
+ def compile(regex: String, flags: Int): Pattern =
+ new Pattern(regex, flags)
+
+ def compile(regex: String): Pattern =
+ new Pattern(regex, 0)
+
+ def matches(regex: String, input: CharSequence): Boolean =
+ compile(regex).matcher(input).matches()
+
+ def quote(s: String): String = {
+ var result = ""
+ var i = 0
+ while (i < s.length) {
+ val c = s.charAt(i)
+ result += ((c: @switch) match {
+ case '\\' | '.' | '(' | ')' | '[' | ']' | '{' | '}' | '|'
+ | '?' | '*' | '+' | '^' | '$' => "\\"+c
+ case _ => c
+ })
+ i += 1
+ }
+ result
+ }
+
+ /** This is a hack to support StringLike.split().
+ * It replaces occurrences of \Q<char>\E by quoted(<char>)
+ */
+ @inline
+ private def trySplitHack(pat: String, flags: Int) = {
+ val m = splitHackPat.exec(pat)
+ if (m != null)
+ Some((quote(m(1).get), flags))
+ else
+ None
+ }
+
+ @inline
+ private def tryFlagHack(pat: String, flags0: Int) = {
+ val m = flagHackPat.exec(pat)
+ if (m != null) {
+ val newPat = pat.substring(m(0).get.length) // cut off the flag specifiers
+ val flags1 = m(1).fold(flags0) { chars =>
+ chars.foldLeft(flags0) { (f, c) => f | charToFlag(c) }
+ }
+ val flags2 = m(2).fold(flags1) { chars =>
+ chars.foldLeft(flags1) { (f, c) => f & ~charToFlag(c) }
+ }
+ Some((newPat, flags2))
+ } else
+ None
+ }
+
+ private def charToFlag(c: Char) = (c: @switch) match {
+ case 'i' => CASE_INSENSITIVE
+ case 'd' => UNIX_LINES
+ case 'm' => MULTILINE
+ case 's' => DOTALL
+ case 'u' => UNICODE_CASE
+ case 'x' => COMMENTS
+ case 'U' => UNICODE_CHARACTER_CLASS
+ case _ => sys.error("bad in-pattern flag")
+ }
+
+ /** matches \Q<char>\E to support StringLike.split */
+ private val splitHackPat = new js.RegExp("^\\\\Q(.|\\n|\\r)\\\\E$")
+
+ /** regex to match flag specifiers in regex. E.g. (?u), (?-i), (?U-i) */
+ private val flagHackPat =
+ new js.RegExp("^\\(\\?([idmsuxU]*)(?:-([idmsuxU]*))?\\)")
+}
diff --git a/examples/scala-js/library-aux/src/main/scala/scala/runtime/ArrayRuntime.scala b/examples/scala-js/library-aux/src/main/scala/scala/runtime/ArrayRuntime.scala
new file mode 100644
index 0000000..ceda199
--- /dev/null
+++ b/examples/scala-js/library-aux/src/main/scala/scala/runtime/ArrayRuntime.scala
@@ -0,0 +1,16 @@
+package scala.runtime
+
+/** Not for public consumption. Usage by the runtime only.
+ */
+
+object ArrayRuntime {
+ def cloneArray(array: Array[Boolean]): Array[Boolean] = array.clone()
+ def cloneArray(array: Array[Char]): Array[Char] = array.clone()
+ def cloneArray(array: Array[Byte]): Array[Byte] = array.clone()
+ def cloneArray(array: Array[Short]): Array[Short] = array.clone()
+ def cloneArray(array: Array[Int]): Array[Int] = array.clone()
+ def cloneArray(array: Array[Long]): Array[Long] = array.clone()
+ def cloneArray(array: Array[Float]): Array[Float] = array.clone()
+ def cloneArray(array: Array[Double]): Array[Double] = array.clone()
+ def cloneArray(array: Array[Object]): Array[Object] = array.clone()
+}
diff --git a/examples/scala-js/library-aux/src/main/scala/scala/runtime/BoxedUnit.scala b/examples/scala-js/library-aux/src/main/scala/scala/runtime/BoxedUnit.scala
new file mode 100644
index 0000000..b6ac773
--- /dev/null
+++ b/examples/scala-js/library-aux/src/main/scala/scala/runtime/BoxedUnit.scala
@@ -0,0 +1,18 @@
+package scala.runtime
+
+/* This is a hijacked class. Its only instance is the value 'undefined'.
+ * Constructors are not emitted.
+ */
+class BoxedUnit private {
+ @inline override def equals(that: Any): Boolean =
+ this eq that.asInstanceOf[AnyRef]
+
+ @inline override def hashCode(): Int = 0
+
+ @inline override def toString(): String = "undefined"
+}
+
+object BoxedUnit {
+ val UNIT: BoxedUnit = sys.error("stub")
+ val TYPE: Class[Void] = sys.error("stub")
+}
diff --git a/examples/scala-js/library-aux/src/main/scala/scala/runtime/RefTypes.scala b/examples/scala-js/library-aux/src/main/scala/scala/runtime/RefTypes.scala
new file mode 100644
index 0000000..4724d13
--- /dev/null
+++ b/examples/scala-js/library-aux/src/main/scala/scala/runtime/RefTypes.scala
@@ -0,0 +1,165 @@
+package scala.runtime
+
+import java.io.Serializable
+
+@inline
+class BooleanRef(var elem: Boolean) extends Serializable {
+ override def toString() = String.valueOf(elem)
+}
+object BooleanRef {
+ def create(elem: Boolean): BooleanRef = new BooleanRef(elem)
+ def zero(): BooleanRef = new BooleanRef(false)
+}
+
+@inline
+class VolatileBooleanRef(var elem: Boolean) extends Serializable {
+ override def toString() = String.valueOf(elem)
+}
+object VolatileBooleanRef {
+ def create(elem: Boolean): VolatileBooleanRef = new VolatileBooleanRef(elem)
+ def zero(): VolatileBooleanRef = new VolatileBooleanRef(false)
+}
+
+@inline
+class CharRef(var elem: Char) extends Serializable {
+ override def toString() = String.valueOf(elem)
+}
+object CharRef {
+ def create(elem: Char): CharRef = new CharRef(elem)
+ def zero(): CharRef = new CharRef(0.toChar)
+}
+
+@inline
+class VolatileCharRef(var elem: Char) extends Serializable {
+ override def toString() = String.valueOf(elem)
+}
+object VolatileCharRef {
+ def create(elem: Char): VolatileCharRef = new VolatileCharRef(elem)
+ def zero(): VolatileCharRef = new VolatileCharRef(0.toChar)
+}
+
+@inline
+class ByteRef(var elem: Byte) extends Serializable {
+ override def toString() = String.valueOf(elem)
+}
+object ByteRef {
+ def create(elem: Byte): ByteRef = new ByteRef(elem)
+ def zero(): ByteRef = new ByteRef(0)
+}
+
+@inline
+class VolatileByteRef(var elem: Byte) extends Serializable {
+ override def toString() = String.valueOf(elem)
+}
+object VolatileByteRef {
+ def create(elem: Byte): VolatileByteRef = new VolatileByteRef(elem)
+ def zero(): VolatileByteRef = new VolatileByteRef(0)
+}
+
+@inline
+class ShortRef(var elem: Short) extends Serializable {
+ override def toString() = String.valueOf(elem)
+}
+object ShortRef {
+ def create(elem: Short): ShortRef = new ShortRef(elem)
+ def zero(): ShortRef = new ShortRef(0)
+}
+
+@inline
+class VolatileShortRef(var elem: Short) extends Serializable {
+ override def toString() = String.valueOf(elem)
+}
+object VolatileShortRef {
+ def create(elem: Short): VolatileShortRef = new VolatileShortRef(elem)
+ def zero(): VolatileShortRef = new VolatileShortRef(0)
+}
+
+@inline
+class IntRef(var elem: Int) extends Serializable {
+ override def toString() = String.valueOf(elem)
+}
+object IntRef {
+ def create(elem: Int): IntRef = new IntRef(elem)
+ def zero(): IntRef = new IntRef(0)
+}
+
+@inline
+class VolatileIntRef(var elem: Int) extends Serializable {
+ override def toString() = String.valueOf(elem)
+}
+object VolatileIntRef {
+ def create(elem: Int): VolatileIntRef = new VolatileIntRef(elem)
+ def zero(): VolatileIntRef = new VolatileIntRef(0)
+}
+
+@inline
+class LongRef(var elem: Long) extends Serializable {
+ override def toString() = String.valueOf(elem)
+}
+object LongRef {
+ def create(elem: Long): LongRef = new LongRef(elem)
+ def zero(): LongRef = new LongRef(0)
+}
+
+@inline
+class VolatileLongRef(var elem: Long) extends Serializable {
+ override def toString() = String.valueOf(elem)
+}
+object VolatileLongRef {
+ def create(elem: Long): VolatileLongRef = new VolatileLongRef(elem)
+ def zero(): VolatileLongRef = new VolatileLongRef(0)
+}
+
+@inline
+class FloatRef(var elem: Float) extends Serializable {
+ override def toString() = String.valueOf(elem)
+}
+object FloatRef {
+ def create(elem: Float): FloatRef = new FloatRef(elem)
+ def zero(): FloatRef = new FloatRef(0)
+}
+
+@inline
+class VolatileFloatRef(var elem: Float) extends Serializable {
+ override def toString() = String.valueOf(elem)
+}
+object VolatileFloatRef {
+ def create(elem: Float): VolatileFloatRef = new VolatileFloatRef(elem)
+ def zero(): VolatileFloatRef = new VolatileFloatRef(0)
+}
+
+@inline
+class DoubleRef(var elem: Double) extends Serializable {
+ override def toString() = String.valueOf(elem)
+}
+object DoubleRef {
+ def create(elem: Double): DoubleRef = new DoubleRef(elem)
+ def zero(): DoubleRef = new DoubleRef(0)
+}
+
+@inline
+class VolatileDoubleRef(var elem: Double) extends Serializable {
+ override def toString() = String.valueOf(elem)
+}
+object VolatileDoubleRef {
+ def create(elem: Double): VolatileDoubleRef = new VolatileDoubleRef(elem)
+ def zero(): VolatileDoubleRef = new VolatileDoubleRef(0)
+}
+
+@inline
+class ObjectRef[A](var elem: A) extends Serializable {
+ override def toString() = String.valueOf(elem)
+}
+object ObjectRef {
+ def create[A](elem: A): ObjectRef[A] = new ObjectRef(elem)
+ def zero(): ObjectRef[Object] = new ObjectRef(null)
+}
+
+@inline
+class VolatileObjectRef[A](var elem: A) extends Serializable {
+ override def toString() = String.valueOf(elem)
+}
+object VolatileObjectRef {
+ def create[A](elem: A): VolatileObjectRef[A] = new VolatileObjectRef(elem)
+ def zero(): VolatileObjectRef[Object] = new VolatileObjectRef(null)
+}
diff --git a/examples/scala-js/library-aux/src/main/scala/scala/runtime/Statics.scala b/examples/scala-js/library-aux/src/main/scala/scala/runtime/Statics.scala
new file mode 100644
index 0000000..b4e7e52
--- /dev/null
+++ b/examples/scala-js/library-aux/src/main/scala/scala/runtime/Statics.scala
@@ -0,0 +1,89 @@
+package scala.runtime
+
+/** Not for public consumption. Usage by the runtime only.
+ */
+
+object Statics {
+ def mix(hash: Int, data: Int): Int = {
+ var h = mixLast(hash, data)
+ h = Integer.rotateLeft(h, 13)
+ (h * 5) + 0xe6546b64
+ }
+
+ def mixLast(hash: Int, data: Int): Int = {
+ var k = data
+ k *= 0xcc9e2d51
+ k = Integer.rotateLeft(k, 15)
+ k *= 0x1b873593
+ hash ^ k
+ }
+
+ def finalizeHash(hash: Int, length: Int): Int = {
+ avalanche(hash ^ length)
+ }
+
+ /** Force all bits of the hash to avalanche. Used for finalizing the hash. */
+ def avalanche(h0: Int): Int = {
+ var h = h0
+ h ^= h >>> 16
+ h *= 0x85ebca6b
+ h ^= h >>> 13
+ h *= 0xc2b2ae35
+ h ^= h >>> 16
+ h
+ }
+
+ def longHash(lv: Long): Int = {
+ lv.toInt
+ /*
+ val iv = lv.toInt | 0
+ if (iv == lv) iv
+ else (lv ^ (lv >>> 32)).toInt | 0
+ */
+ }
+
+ def doubleHash(dv: Double): Int = {
+ dv.toInt
+ /*
+ val iv = dv.toInt | 0
+ if (iv == dv)
+ return iv
+
+ val fv = dv.toFloat
+ if (fv == dv)
+ return java.lang.Float.floatToIntBits(fv)
+
+ val lv = dv.toLong
+ if (lv == dv)
+ return lv.toInt | 0
+
+ val lv2 == java.lang.Double.doubleToLongBits(dv)
+ return (lv2 ^ (lv2 >>> 32)).toInt | 0
+ */
+ }
+
+ def floatHash(fv: Float): Int = {
+ fv.toInt
+ /*
+ val iv = fv.toInt
+ if (iv == fv)
+ return iv
+
+ val lv = fv.toLong
+ if (lv == fv)
+ return (lv ^ (lv >>> 32)).toInt | 0
+
+ return java.lang.Float.floatToIntBits(fv)
+ */
+ }
+
+ def anyHash(x: Any): Int = {
+ x match {
+ case null => 0
+ case x: Long => longHash(x)
+ case x: Double => doubleHash(x)
+ case x: Float => floatHash(x)
+ case _ => x.hashCode()
+ }
+ }
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/concurrent/JSExecutionContext.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/concurrent/JSExecutionContext.scala
new file mode 100644
index 0000000..c159dcb
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/concurrent/JSExecutionContext.scala
@@ -0,0 +1,24 @@
+package scala.scalajs.concurrent
+
+import scala.concurrent.ExecutionContext
+
+/**
+ * Execution contexts for use in JavaScript
+ *
+ * Enables the use of Futures/Promises
+ * @author Tobias Schlatter
+ */
+object JSExecutionContext {
+
+ /** execution context that runs immediately. beware of stack growth! */
+ val runNow = RunNowExecutionContext
+ /** execution context that submits into the JavaScript runtime's
+ * task queue */
+ val queue = QueueExecutionContext
+
+ object Implicits {
+ implicit val runNow: ExecutionContext = JSExecutionContext.runNow
+ implicit val queue: ExecutionContext = JSExecutionContext.queue
+ }
+
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/concurrent/QueueExecutionContext.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/concurrent/QueueExecutionContext.scala
new file mode 100644
index 0000000..1f2ee6f
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/concurrent/QueueExecutionContext.scala
@@ -0,0 +1,17 @@
+package scala.scalajs.concurrent
+
+import scala.concurrent.ExecutionContext
+import scalajs.js
+
+private[concurrent] object QueueExecutionContext extends ExecutionContext {
+
+ def execute(runnable: Runnable) = {
+ val lambda: js.Function = () =>
+ try { runnable.run() } catch { case t: Throwable => reportFailure(t) }
+ js.Dynamic.global.setTimeout(lambda, 0)
+ }
+
+ def reportFailure(t: Throwable) =
+ Console.err.println("Failure in async execution: " + t)
+
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/concurrent/RunNowExcecutionContext.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/concurrent/RunNowExcecutionContext.scala
new file mode 100644
index 0000000..ba113b4
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/concurrent/RunNowExcecutionContext.scala
@@ -0,0 +1,14 @@
+package scala.scalajs.concurrent
+
+import scala.concurrent.ExecutionContext
+
+private[concurrent] object RunNowExecutionContext extends ExecutionContext {
+
+ def execute(runnable: Runnable) =
+ try { runnable.run() }
+ catch { case t: Throwable => reportFailure(t) }
+
+ def reportFailure(t: Throwable) =
+ Console.err.println("Failure in async execution: " + t)
+
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/Array.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/Array.scala
new file mode 100644
index 0000000..4c9cf23
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/Array.scala
@@ -0,0 +1,173 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+/**
+ * All doc-comments marked as "MDN" are by Mozilla Contributors,
+ * distributed under the Creative Commons Attribution-ShareAlike license from
+ * https://developer.mozilla.org/en-US/docs/Web/Reference/API
+ */
+package scala.scalajs.js
+
+import annotation._
+
+/**
+ * Arrays are list-like objects whose prototype has methods to perform
+ * traversal and mutation operations. Neither the length of a JavaScript
+ * array nor the types of its elements are fixed. Since an array's size
+ * length grow or shrink at any time, JavaScript arrays are not guaranteed
+ * to be dense. In general, these are convenient characteristics; but if
+ * these features are not desirable for your particular use, you might
+ * consider using typed arrays.
+ *
+ * MDN
+ *
+ * To construct a new array with uninitialized elements, use the constructor
+ * of this class. To construct a new array with specified elements, as if
+ * you used the array literal syntax in JavaScript, use the
+ * [[Array$.apply Array.apply]] method instead.
+ *
+ * @tparam A Type of the elements of the array
+ *
+ * @constructor Creates a new array of length 0.
+ */
+class Array[A] extends Object {
+ /** Creates a new array with the given length.
+ * @param arrayLength Initial length of the array.
+ */
+ def this(arrayLength: Int) = this()
+
+ // Do not expose this one - use js.Array(item1, item2, ...) instead
+ // def this(items: A*) = this()
+
+ /** Length of the array. */
+ def length: Int = native
+
+ /** Sets the length of the array.
+ * If the new length is bigger than the old length, created slots are
+ * filled with `undefined` (irrespective of the type argument `A`!).
+ * If the new length is smaller than the old length, the array is shrunk.
+ */
+ def length_=(v: Int): Unit = native
+
+ /** Access the element at the given index. */
+ @JSBracketAccess
+ def apply(index: Int): A = native
+ /** Set the element at the given index. */
+ @JSBracketAccess
+ def update(index: Int, value: A): Unit = native
+
+ /**
+ * concat creates a new array consisting of the elements in the this object
+ * on which it is called, followed in order by, for each argument, the
+ * elements of that argument (if the argument is an array) or the argument
+ * itself (if the argument is not an array).
+ *
+ * MDN
+ */
+ def concat[B >: A](items: Array[_ <: B]*): Array[B] = native
+
+ /**
+ * The join() method joins all elements of an array into a string.
+ *
+ * separator Specifies a string to separate each element of the array.
+ * The separator is converted to a string if necessary. If omitted, the
+ * array elements are separated with a comma.
+ */
+ def join(seperator: String = ","): String = native
+
+ /**
+ * The pop() method removes the last element from an array and returns that
+ * element.
+ *
+ * MDN
+ */
+ def pop(): A = native
+
+ /**
+ * The push() method mutates an array by appending the given elements and
+ * returning the new length of the array.
+ *
+ * MDN
+ */
+ def push(items: A*): Int = native
+
+ /**
+ * The reverse() method reverses an array in place. The first array element
+ * becomes the last and the last becomes the first.
+ *
+ * MDN
+ */
+ @JSName("reverse")
+ def reverseInPlace(): Array[A] = native
+
+ /**
+ * The shift() method removes the first element from an array and returns that
+ * element. This method changes the length of the array.
+ *
+ * MDN
+ */
+ def shift(): A = native
+
+ /**
+ * The slice() method returns a shallow copy of a portion of an array.
+ *
+ * MDN
+ */
+ @JSName("slice")
+ def jsSlice(start: Int = 0, end: Int = Int.MaxValue): Array[A] = native
+
+ /**
+ * The sort() method sorts the elements of an array in place and returns the
+ * array. The sort is not necessarily stable. The default sort order is
+ * lexicographic (not numeric).
+ *
+ * If compareFunction is not supplied, elements are sorted by converting them
+ * to strings and comparing strings in lexicographic ("dictionary" or "telephone
+ * book," not numerical) order. For example, "80" comes before "9" in
+ * lexicographic order, but in a numeric sort 9 comes before 80.
+ *
+ * MDN
+ */
+ def sort(compareFn: Function2[A, A, Int] = ???): Array[A] = native
+
+ /** Removes and adds new elements at a given index in the array.
+ *
+ * This method first removes `deleteCount` elements starting from the index
+ * `index`, then inserts the new elements `items` at that index.
+ *
+ * If `index` is negative, it is treated as that number of elements starting
+ * from the end of the array.
+ *
+ * @param index Index where to start changes
+ * @param deleteCount Number of elements to delete from index
+ * @param items Elements to insert at index
+ * @return An array of the elements that were deleted
+ */
+ def splice(index: Int, deleteCount: Int, items: A*): Array[A] = native
+
+ /**
+ * The unshift() method adds one or more elements to the beginning of an array
+ * and returns the new length of the array.
+ *
+ * MDN
+ */
+ def unshift(items: A*): Int = native
+}
+
+/** Factory for [[js.Array]] objects. */
+object Array extends Object {
+ // Do not expose this one - use new Array(len) instead
+ // def apply[A](arrayLength: Int): Array[A] = native
+
+ /** Creates a new array with the given items. */
+ def apply[A](items: A*): Array[A] = sys.error("stub")
+
+ /** Returns true if the given value is an array. */
+ def isArray(arg: Any): Boolean = native
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/ArrayOps.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/ArrayOps.scala
new file mode 100644
index 0000000..f7948ca
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/ArrayOps.scala
@@ -0,0 +1,119 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+package scala.scalajs.js
+
+import scala.annotation.tailrec
+
+import scala.collection.mutable
+import mutable.Builder
+
+/** Equivalent of scm.ArrayOps for js.Array */
+@inline
+final class ArrayOps[A](private[this] val array: Array[A])
+ extends mutable.ArrayLike[A, Array[A]]
+ with Builder[A, Array[A]] {
+
+ import ArrayOps._
+
+ /** Creates a new empty [[ArrayOps]]. */
+ def this() = this(Array())
+
+ // Implementation of ArrayLike
+
+ @inline def apply(index: Int): A = array(index)
+ @inline def length: Int = array.length
+ @inline def update(index: Int, element: A): Unit = array(index) = element
+
+ def seq: IndexedSeq[A] = new WrappedArray(array)
+
+ override def repr: Array[A] = array
+
+ override protected[this] def thisCollection: mutable.IndexedSeq[A] =
+ toCollection(array)
+ override protected[this] def toCollection(
+ repr: Array[A]): mutable.IndexedSeq[A] = new WrappedArray(repr)
+
+ protected[this] def newBuilder: Builder[A, Array[A]] =
+ new ArrayOps[A]
+
+ // Implementation of Builder
+
+ @inline def +=(elem: A): this.type = {
+ array.push(elem)
+ this
+ }
+
+ @inline def clear(): Unit =
+ array.length = 0
+
+ @inline def result(): Array[A] = array
+
+ // Scala notation for a fast concat()
+
+ @inline def ++[B >: A](that: Array[_ <: B]): Array[B] =
+ concat(array, that)
+
+ // Methods whose inherited implementations do not play nice with the optimizer
+
+ override def reduceLeft[B >: A](op: (B, A) => B): B = {
+ val length = this.length
+ if (length <= 0)
+ throwUnsupported("empty.reduceLeft")
+
+ @inline
+ @tailrec
+ def loop(start: Int, z: B): B =
+ if (start == length) z
+ else loop(start+1, op(z, this(start)))
+
+ loop(1, this(0))
+ }
+
+ override def reduceRight[B >: A](op: (A, B) => B): B = {
+ val length = this.length
+ if (length <= 0)
+ throwUnsupported("empty.reduceRight")
+
+ @inline
+ @tailrec
+ def loop(end: Int, z: B): B =
+ if (end == 0) z
+ else loop(end-1, op(this(end-1), z))
+
+ loop(length-1, this(length-1))
+ }
+
+}
+
+object ArrayOps {
+
+ /** Extract the throw in a separate, non-inlineable method. */
+ private def throwUnsupported(msg: String): Nothing =
+ throw new UnsupportedOperationException(msg)
+
+ /** Non-inlined implementation of [[ArrayOps.++]]. */
+ private def concat[A](left: Array[_ <: A], right: Array[_ <: A]): Array[A] = {
+ val leftLength = left.length
+ val rightLength = right.length
+ val result = new Array[A](leftLength + rightLength)
+
+ @inline
+ @tailrec
+ def loop(src: Array[_ <: A], i: Int, len: Int, offset: Int): Unit =
+ if (i != len) {
+ result(i+offset) = src(i)
+ loop(src, i+1, len, offset)
+ }
+
+ loop(left, 0, leftLength, 0)
+ loop(right, 0, rightLength, leftLength)
+ result
+ }
+
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/Date.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/Date.scala
new file mode 100644
index 0000000..18795c7
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/Date.scala
@@ -0,0 +1,225 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+/**
+ * All doc-comments marked as "MDN" are by Mozilla Contributors,
+ * distributed under the Creative Commons Attribution-ShareAlike license from
+ * https://developer.mozilla.org/en-US/docs/Web/Reference/API
+ */
+package scala.scalajs.js
+
+/**
+ * Creates a JavaScript Date instance that represents a single moment in time.
+ * Date objects are based on a time value that is the number of milliseconds
+ * since 1 January, 1970 UTC.
+ *
+ * MDN
+ */
+class Date extends Object {
+
+ def this(value: Double) = this()
+ def this(value: String) = this()
+
+ def this(year: Int, month: Int, date: Int = 1, hours: Int = 0,
+ minutes: Int = 0, seconds: Int = 0, ms: Int = 0) = this()
+
+ def toDateString(): String = native
+ def toTimeString(): String = native
+ def toLocaleDateString(): String = native
+ def toLocaleTimeString(): String = native
+
+ override def valueOf(): Double = native
+
+ def getTime(): Double = native
+
+ /**
+ * Returns the year (4 digits for 4-digit years) of the specified date according to local time.
+ *
+ * MDN
+ */
+ def getFullYear(): Int = native
+
+ /**
+ * Returns the year (4 digits for 4-digit years) in the specified date according to universal time.
+ *
+ * MDN
+ */
+ def getUTCFullYear(): Int = native
+
+ /**
+ * Returns the month (0-11) in the specified date according to local time.
+ *
+ * MDN
+ */
+ def getMonth(): Int = native
+
+ /**
+ * Returns the month (0-11) in the specified date according to universal time.
+ *
+ * MDN
+ */
+ def getUTCMonth(): Int = native
+
+ /**
+ * Returns the day of the month (1-31) for the specified date according to local time.
+ *
+ * MDN
+ */
+ def getDate(): Int = native
+
+ /**
+ * Returns the day (date) of the month (1-31) in the specified date according to universal time.
+ *
+ * MDN
+ */
+ def getUTCDate(): Int = native
+
+ /**
+ * Returns the day of the week (0-6) for the specified date according to local time.
+ *
+ * MDN
+ */
+ def getDay(): Int = native
+
+ /**
+ * Returns the day of the week (0-6) in the specified date according to universal time.
+ * MDN
+ */
+ def getUTCDay(): Int = native
+
+ /**
+ * Returns the hour (0-23) in the specified date according to local time.
+ *
+ * MDN
+ */
+ def getHours(): Int = native
+
+ /**
+ * Returns the hours (0-23) in the specified date according to universal time.
+ *
+ * MDN
+ */
+ def getUTCHours(): Int = native
+
+ /**
+ * Returns the minutes (0-59) in the specified date according to local time.
+ *
+ * MDN
+ */
+ def getMinutes(): Int = native
+
+ /**
+ * Returns the minutes (0-59) in the specified date according to universal time.
+ *
+ * MDN
+ */
+ def getUTCMinutes(): Int = native
+
+ /**
+ * Returns the seconds (0-59) in the specified date according to local time.
+ *
+ * MDN
+ */
+ def getSeconds(): Int = native
+
+ /**
+ * Returns the seconds (0-59) in the specified date according to universal time.
+ *
+ * MDN
+ */
+ def getUTCSeconds(): Int = native
+
+ /**
+ * Returns the milliseconds (0-999) in the specified date according to local time.
+ *
+ * MDN
+ */
+ def getMilliseconds(): Int = native
+
+ /**
+ * Returns the milliseconds (0-999) in the specified date according to universal time.
+ *
+ * MDN
+ */
+ def getUTCMilliseconds(): Int = native
+
+ /**
+ * Returns the time-zone offset in minutes for the current locale.
+ *
+ * MDN
+ */
+ def getTimezoneOffset(): Int = native
+
+ def setTime(time: Double): Unit = native
+ def setMilliseconds(ms: Int): Unit = native
+ def setUTCMilliseconds(ms: Int): Unit = native
+ def setSeconds(sec: Int, ms: Int = getMilliseconds()): Unit = native
+ def setUTCSeconds(sec: Int, ms: Int = getMilliseconds()): Unit = native
+ def setMinutes(min: Int, sec: Int = getSeconds(),
+ ms: Int = getMilliseconds()): Unit = native
+ def setUTCMinutes(min: Int, sec: Int = getSeconds(),
+ ms: Int = getMilliseconds()): Unit = native
+ def setHours(hours: Int, min: Int = getMinutes(),
+ sec: Int = getSeconds(), ms: Int = getMilliseconds()): Unit = native
+ def setUTCHours(hours: Int, min: Int = getMinutes(),
+ sec: Int = getSeconds(), ms: Int = getMilliseconds()): Unit = native
+
+ def setDate(date: Int): Unit = native
+ def setUTCDate(date: Int): Unit = native
+ def setMonth(month: Int, date: Int = getDate()): Unit = native
+ def setUTCMonth(month: Int, date: Int = getDate()): Unit = native
+ def setFullYear(year: Int, month: Int = getMonth(),
+ date: Int = getDate()): Unit = native
+ def setUTCFullYear(year: Int, month: Int = getMonth(),
+ date: Int = getDate()): Unit = native
+
+ def toUTCString(): String = native
+ def toISOString(): String = native
+ def toJSON(key: Any): String = native
+ def toJSON(): String = native
+}
+
+/** Factory for [[js.Date]] objects. */
+object Date extends Object {
+ def apply(): String = native
+
+ /**
+ * Parses a string representation of a date and returns the number of
+ * milliseconds since 1 January, 1970, 00:00:00, local time.
+ *
+ * The parse method takes a date string (such as "Dec 25, 1995") and returns
+ * the number of milliseconds since January 1, 1970, 00:00:00 UTC. The local
+ * time zone is used to interpret arguments that do not contain time zone
+ * information. This function is useful for setting date values based on
+ * string values, for example in conjunction with the setTime() method and
+ * the Date object.
+ *
+ * Given a string representing a time, parse returns the time value. It
+ * accepts the RFC2822 / IETF date syntax (RFC2822 Section 3.3), e.g.
+ * "Mon, 25 Dec 1995 13:30:00 GMT". It understands the continental US time-
+ * zone abbreviations, but for general use, use a time-zone offset, for
+ * example, "Mon, 25 Dec 1995 13:30:00 +0430" (4 hours, 30 minutes east of
+ * the Greenwich meridian). If you do not specify a time zone, the local time
+ * zone is assumed. GMT and UTC are considered equivalent.
+ *
+ * MDN
+ */
+ def parse(s: String): Int = native
+
+ def UTC(year: Int, month: Int, date: Int = 1, hours: Int = 0,
+ minutes: Int = 0, seconds: Int = 0, ms: Int = 0): Double = native
+
+ /**
+ * Returns the numeric value corresponding to the current time - the number
+ * of milliseconds elapsed since 1 January 1970 00:00:00 UTC.
+ *
+ * MDN
+ */
+ def now(): Double = native
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/Dictionary.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/Dictionary.scala
new file mode 100644
index 0000000..fa68d08
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/Dictionary.scala
@@ -0,0 +1,92 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+/**
+ * All doc-comments marked as "MDN" are by Mozilla Contributors,
+ * distributed under the Creative Commons Attribution-ShareAlike license from
+ * https://developer.mozilla.org/en-US/docs/Web/Reference/API
+ */
+package scala.scalajs.js
+
+import annotation.JSBracketAccess
+
+/** Dictionary "view" of a JavaScript value.
+ *
+ * Using objects as dictionaries (maps from strings to values) through their
+ * properties is a common idiom in JavaScript. This trait lets you treat an
+ * object as such a dictionary.
+ *
+ * To use it, cast your object, say `x`, into a [[Dictionary]] using
+ * {{{
+ * val xDict = x.asInstanceOf[js.Dictionary[Int]]
+ * }}}
+ * then use it as
+ * {{{
+ * xDict("prop") = 5
+ * println(xDict("prop")) // displays 5
+ * xDict.delete("prop") // removes the property "prop"
+ * println(xDict("prop")) // displays undefined
+ * }}}
+ *
+ * To enumerate all the keys of a dictionary, use [[js.Object.keys]], which
+ * returns a [[js.Array]] of the properties. It can be used in a for
+ * comprehension like this:
+ * {{{
+ * for (prop <- js.Object.keys(xDict)) {
+ * val value = xDict(prop)
+ * println(prop + " -> " + value)
+ * }
+ * }}}
+ */
+sealed trait Dictionary[A] extends Object {
+ /** Reads a field of this object by its name.
+ *
+ * This will fail with a ClassCastException if the key doesn't exist and
+ * the return type doesn't allow js.undefined as value. If the return type
+ * does allow js.undefined, applying with a non-existent key will return
+ * js.undefined.
+ */
+ @JSBracketAccess
+ def apply(key: String): A = native
+
+ /** Reads a field of this object by its name.
+ *
+ * This will return undefined if the key doesn't exist. It will also return
+ * undefined if the value associated with the key is undefined. To truly
+ * check for the existence of a property, use [[js.Object.hasOwnProperty]].
+ */
+ @JSBracketAccess
+ def get(key: String): UndefOr[A] = native
+
+ /** Writes a field of this object by its name. */
+ @JSBracketAccess
+ def update(key: String, value: A): Unit = native
+
+ /** Deletes a property of this object by its name.
+ * The property must be configurable.
+ * This method is equivalent to the "delete" keyword in JavaScript.
+ *
+ * Since we are using strict mode, this throws an exception, if the property
+ * isn't configurable.
+ */
+ def delete(key: String): Unit = sys.error("stub")
+}
+
+/** Factory for [[Dictionary]] instances. */
+object Dictionary {
+ /** Returns a new empty dictionary */
+ def empty[A]: Dictionary[A] = (new Object).asInstanceOf[Dictionary[A]]
+
+ def apply[A](properties: (String, A)*): Dictionary[A] = {
+ val result = empty[A]
+ for ((key, value) <- properties)
+ result(key) = value
+ result
+ }
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/Error.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/Error.scala
new file mode 100644
index 0000000..129ff41
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/Error.scala
@@ -0,0 +1,114 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+/**
+ * All doc-comments marked as "MDN" are by Mozilla Contributors,
+ * distributed under the Creative Commons Attribution-ShareAlike license from
+ * https://developer.mozilla.org/en-US/docs/Web/Reference/API
+ */
+package scala.scalajs.js
+
+class Error(message0: String = "") extends Object {
+ val name: String = native
+ /**
+ * Human-readable description of the error
+ *
+ * MDN
+ */
+ val message: String = native
+}
+
+object Error extends Object {
+ def apply(message: String = ""): Error = native
+}
+
+/**
+ * An instance representing an error that occurs regarding the global function
+ * eval()
+ *
+ * MDN
+ */
+class EvalError(message: String = "") extends Error
+
+object EvalError extends Object {
+ def apply(message: String = ""): EvalError = native
+}
+
+/**
+ * An instance representing an error that occurs when a numeric variable or
+ * parameter is outside of its valid range.
+ *
+ * A RangeError is thrown when trying to pass a number as an argument to a
+ * function that does not allow a range that includes that number. This can
+ * be encountered when to create an array of an illegal length with the Array
+ * constructor, or when passing bad values to the numeric methods toExponential,
+ * toFixed, or toPrecision.
+ *
+ * MDN
+ */
+class RangeError(message: String = "") extends Error
+
+object RangeError extends Object {
+ def apply(message: String = ""): RangeError = native
+}
+
+/**
+ * Represents an error when a non-existent variable is referenced.
+ *
+ * A ReferenceError is thrown when trying to dereference a variable that has
+ * not been declared.
+ *
+ * MDN
+ */
+class ReferenceError(message: String = "") extends Error
+
+object ReferenceError extends Object {
+ def apply(message: String = ""): ReferenceError = native
+}
+
+/**
+ * Represents an error when trying to interpret syntactically invalid code.
+ *
+ * A SyntaxError is thrown when the JavaScript engine encounters tokens or
+ * token order that does not conform to the syntax of the language when parsing code.
+ *
+ * MDN
+ */
+class SyntaxError(message: String = "") extends Error
+
+object SyntaxError extends Object {
+ def apply(message: String = ""): SyntaxError = native
+}
+
+/**
+ * Represents an error when a value is not of the expected type.
+ *
+ * A TypeError is thrown when an operand or argument passed to a function is
+ * incompatible with the type expected by that operator or function.
+ *
+ * MDN
+ */
+class TypeError(message: String = "") extends Error
+
+object TypeError extends Object {
+ def apply(message: String = ""): TypeError = native
+}
+
+/**
+ * Represents an error when a malformed URI is encountered.
+ *
+ * A URIError is thrown when the URI handling functions are passed a malformed URI.
+ *
+ * MDN
+ */
+class URIError(message: String = "") extends Error
+
+object URIError extends Object {
+ def apply(message: String = ""): URIError = native
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/Function.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/Function.scala
new file mode 100644
index 0000000..4a7d1d9
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/Function.scala
@@ -0,0 +1,194 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+/**
+ * All doc-comments marked as "MDN" are by Mozilla Contributors,
+ * distributed under the Creative Commons Attribution-ShareAlike license from
+ * https://developer.mozilla.org/en-US/docs/Web/Reference/API
+ */
+package scala.scalajs.js
+
+/**
+ * The Function constructor creates a new Function object. In JavaScript every
+ * function is actually a Function object.
+ *
+ * Function objects created with the Function constructor are parsed when the
+ * function is created. This is less efficient than declaring a function and calling
+ * it within your code, because functions declared with the function statement
+ * are parsed with the rest of the code.
+ *
+ * All arguments passed to the function are treated as the names of the
+ * identifiers of the parameters in the function to be created, in the order
+ * in which they are passed.
+ *
+ * Note: Functions created with the Function constructor do not create closures
+ * to their creation contexts; they always are created in the global scope.
+ * When running them, they will only be able to access their own local
+ * variables and global ones, not the ones from the scope in which the Function
+ * constructor was called. This is different from using eval with code for
+ * a function expression.
+ *
+ * Invoking the Function constructor as a function (without using the new
+ * operator) has the same effect as invoking it as a constructor.
+ *
+ * MDN
+ */
+class Function(args: String*) extends Object {
+ /**
+ * length is a property of a function object, and indicates how many arguments
+ * the function expects, i.e. the number of formal parameters. This number
+ * does not include the rest parameter. By contrast, arguments.length is local
+ * to a function and provides the number of arguments actually passed to the
+ * function.
+ *
+ * MDN
+ */
+ val length: Int = native
+
+ /**
+ * The call() method calls a function with a given this value and arguments
+ * provided individually.
+ *
+ * You can assign a different this object when calling an existing function.
+ * this refers to the current object, the calling object. With call, you
+ * can write a method once and then inherit it in another object, without
+ * having to rewrite the method for the new object.
+ *
+ * apply is very similar to call(), except for the type of arguments it supports.
+ * You can use an arguments array instead of a named set of parameters. With
+ * apply, you can use an array literal, for example,
+ *
+ * fun.apply(this, ['eat', 'bananas'])
+ *
+ * or an Array object, for example,
+ *
+ * fun.apply(this, new Array('eat', 'bananas')).
+ *
+ * MDN
+ *
+ * Scala.js-specific note: call() can be used instead of the apply() method
+ * available in JavaScript. Simply use the :_* notation to expand a Seq as
+ * variadic arguments, e.g.,
+ *
+ * {{{
+ * someFun.call(thisArg, argSeq: _*)
+ * }}}
+ *
+ */
+ def call(thisArg: Any, argArray: Any*): Dynamic = native
+
+ // Do not expose apply: use call(thisArg, argArray: _*) instead.
+ // def apply[A](thisArg: Any, argArray: Array[A]): Dynamic = native
+ // def apply(thisArg: Any): Dynamic = native
+
+ /**
+ * The bind() method creates a new function that, when called, has its this
+ * keyword set to the provided value, with a given sequence of arguments
+ * preceding any provided when the new function is called.
+ *
+ * MDN
+ */
+ def bind(thisArg: Any, argArray: Any*): Dynamic = native
+}
+
+object Function extends Object {
+ def apply(args: String*): Function = native
+}
+
+trait Function0[+R] extends Function {
+ def apply(): R
+}
+
+trait Function1[-T1, +R] extends Function {
+ def apply(arg1: T1): R
+}
+
+trait Function2[-T1, -T2, +R] extends Function {
+ def apply(arg1: T1, arg2: T2): R
+}
+
+trait Function3[-T1, -T2, -T3, +R] extends Function {
+ def apply(arg1: T1, arg2: T2, arg3: T3): R
+}
+
+trait Function4[-T1, -T2, -T3, -T4, +R] extends Function {
+ def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4): R
+}
+
+trait Function5[-T1, -T2, -T3, -T4, -T5, +R] extends Function {
+ def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5): R
+}
+
+trait Function6[-T1, -T2, -T3, -T4, -T5, -T6, +R] extends Function {
+ def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6): R
+}
+
+trait Function7[-T1, -T2, -T3, -T4, -T5, -T6, -T7, +R] extends Function {
+ def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7): R
+}
+
+trait Function8[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, +R] extends Function {
+ def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8): R
+}
+
+trait Function9[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, +R] extends Function {
+ def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9): R
+}
+
+trait Function10[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, +R] extends Function {
+ def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10): R
+}
+
+trait Function11[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, +R] extends Function {
+ def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11): R
+}
+
+trait Function12[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, +R] extends Function {
+ def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12): R
+}
+
+trait Function13[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, +R] extends Function {
+ def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13): R
+}
+
+trait Function14[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, +R] extends Function {
+ def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14): R
+}
+
+trait Function15[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, +R] extends Function {
+ def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14, arg15: T15): R
+}
+
+trait Function16[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, +R] extends Function {
+ def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14, arg15: T15, arg16: T16): R
+}
+
+trait Function17[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, +R] extends Function {
+ def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14, arg15: T15, arg16: T16, arg17: T17): R
+}
+
+trait Function18[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, +R] extends Function {
+ def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14, arg15: T15, arg16: T16, arg17: T17, arg18: T18): R
+}
+
+trait Function19[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, +R] extends Function {
+ def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14, arg15: T15, arg16: T16, arg17: T17, arg18: T18, arg19: T19): R
+}
+
+trait Function20[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, -T20, +R] extends Function {
+ def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14, arg15: T15, arg16: T16, arg17: T17, arg18: T18, arg19: T19, arg20: T20): R
+}
+
+trait Function21[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, -T20, -T21, +R] extends Function {
+ def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14, arg15: T15, arg16: T16, arg17: T17, arg18: T18, arg19: T19, arg20: T20, arg21: T21): R
+}
+
+trait Function22[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, -T20, -T21, -T22, +R] extends Function {
+ def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14, arg15: T15, arg16: T16, arg17: T17, arg18: T18, arg19: T19, arg20: T20, arg21: T21, arg22: T22): R
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/GlobalScope.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/GlobalScope.scala
new file mode 100644
index 0000000..67c09ed
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/GlobalScope.scala
@@ -0,0 +1,21 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+
+package scala.scalajs.js
+
+/** Marker trait for top-level objects representing the JS global scope.
+ *
+ * When calling method on a top-level object or package object that is a
+ * subtype of GlobalScope, the receiver is dropped, and the JavaScript global
+ * scope is used instead.
+ *
+ * @see [[http://www.scala-js.org/doc/calling-javascript.html Calling JavaScript from Scala.js]]
+ */
+trait GlobalScope extends Object
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/JSApp.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/JSApp.scala
new file mode 100644
index 0000000..fd12207
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/JSApp.scala
@@ -0,0 +1,20 @@
+package scala.scalajs.js
+
+import annotation.{JSExport, JSExportDescendentObjects}
+
+/** Base class for top-level, entry point main objects.
+ *
+ * Objects inheriting from [[JSApp]] are automatically exported to JavaScript
+ * under their fully qualified name, and their [[main]] method as well.
+ *
+ * [[JSApp]] is typically used to mark the entry point of a Scala.js
+ * application. As such, the sbt plugin also recognizes top-level objects
+ * extending [[JSApp]]. It allows to run their [[main]] method with `sbt run`,
+ * and can also generate a tiny JavaScript launcher snippet executing the
+ * [[main]] method of one specific [[JSApp]] object.
+ */
+@JSExportDescendentObjects
+trait JSApp {
+ @JSExport
+ def main(): Unit
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/JSArrayOps.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/JSArrayOps.scala
new file mode 100644
index 0000000..71f705d
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/JSArrayOps.scala
@@ -0,0 +1,264 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+/**
+ * All doc-comments marked as "MDN" are by Mozilla Contributors,
+ * distributed under the Creative Commons Attribution-ShareAlike license from
+ * https://developer.mozilla.org/en-US/docs/Web/Reference/API
+ */
+package scala.scalajs.js
+
+import scala.language.implicitConversions
+
+import scala.scalajs.js.annotation._
+
+/** Discouraged native JavaScript Array methods.
+ *
+ * In general, you should prefer the Scala collection methods available
+ * implicitly through [[ArrayOps]], because they are inlineable, and hence
+ * faster.
+ *
+ * To enable the use of these functions on js.[[Array]]s, import the implicit
+ * conversion [[JSArrayOps.jsArrayOps]].
+ */
+trait JSArrayOps[A] extends Object {
+
+ /**
+ * The indexOf() method returns the first index at which a given element can
+ * be found in the array, or -1 if it is not present.
+ *
+ * MDN
+ */
+ @JSName("indexOf")
+ def jsIndexOf(searchElement: A, fromIndex: Int): Int = native
+ @JSName("indexOf")
+ def jsIndexOf(searchElement: A): Int = native
+
+ /**
+ * The lastIndexOf() method returns the last index at which a given element
+ * can be found in the array, or -1 if it is not present. The array is
+ * searched backwards, starting at fromIndex.
+ *
+ * MDN
+ */
+ @JSName("lastIndexOf")
+ def jsLastIndexOf(searchElement: A, fromIndex: Int): Int = native
+ @JSName("lastIndexOf")
+ def jsLastIndexOf(searchElement: A): Int = native
+
+ /**
+ * The every method executes the provided callback function once for each
+ * element present in the array until it finds one where callback returns
+ * a falsy value (a value that becomes false when converted to a Boolean).
+ * If such an element is found, the every method immediately returns false.
+ * Otherwise, if callback returned a true value for all elements, every
+ * will return true. callback is invoked only for indexes of the array
+ * which have assigned values; it is not invoked for indexes which have been
+ * deleted or which have never been assigned values.
+ *
+ * callback is invoked with three arguments:
+ *
+ * - the value of the element
+ * - the index of the element
+ * - and the Array object being traversed.
+ *
+ * If a thisObject parameter is provided to every, it will be used as the
+ * this for each invocation of the callback. If it is not provided, or is
+ * null, the global object associated with callback is used instead.
+ *
+ * every does not mutate the array on which it is called.
+ *
+ * every acts like the "for all" quantifier in mathematics. In particular, for
+ * an empty array, it returns true. (It is vacuously true that all elements of
+ * the empty set satisfy any given condition.)
+ *
+ * MDN
+ */
+ @JSName("every")
+ def jsEvery[T](callbackfn: ThisFunction3[T, A, Int, Array[A], Boolean],
+ thisArg: T): Boolean = native
+ @JSName("every")
+ def jsEvery(callbackfn: Function3[A, Int, Array[A], Boolean]): Boolean = native
+
+ /**
+ * some executes the callback function once for each element present in the
+ * array until it finds one where callback returns a true value. If such an
+ * element is found, some immediately returns true. Otherwise, some returns
+ * false. callback is invoked only for indexes of the array which have assigned
+ * values; it is not invoked for indexes which have been deleted or which
+ * have never been assigned values.
+ *
+ * callback is invoked with three arguments: the value of the element, the index
+ * of the element, and the Array object being traversed.
+ *
+ * If a thisObject parameter is provided to some, it will be used as the this
+ * for each invocation of the callback. If it is not provided, or is null,
+ * the global object associated with callback is used instead.
+ *
+ * some does not mutate the array on which it is called.
+ *
+ * MDN
+ */
+ @JSName("some")
+ def jsSome[T](callbackfn: ThisFunction3[T, A, Int, Array[A], Boolean],
+ thisArg: T): Boolean = native
+ @JSName("some")
+ def jsSome(callbackfn: Function3[A, Int, Array[A], Boolean]): Boolean = native
+ @JSName("some")
+ def jsSome(callbackfn: Function2[A, Int, Boolean]): Boolean = native
+ @JSName("some")
+ def jsSome(callbackfn: Function1[A, Boolean]): Boolean = native
+
+ /**
+ * forEach executes the provided callback once for each element of the array
+ * with an assigned value. It is not invoked for indexes which have been deleted
+ * or which have been initialized to undefined.
+ *
+ * callback is invoked with three arguments:
+ *
+ * - the element value
+ * - the element index
+ * - the array being traversed
+ *
+ * If a thisArg parameter is provided to forEach, it will be used as the
+ * this value for each callback invocation as if callback.call(thisArg,
+ * element, index, array) was called. If thisArg is undefined or null,
+ * the this value within the function depends on whether the function
+ * is in strict mode or not (passed value if in strict mode, global object
+ * if in non-strict mode).
+ *
+ * MDN
+ */
+ @JSName("forEach")
+ def jsForEach[T](callbackfn: ThisFunction3[T, A, Int, Array[A], _],
+ thisArg: T): Unit = native
+ @JSName("forEach")
+ def jsForEach(callbackfn: Function3[A, Int, Array[A], _]): Unit = native
+ @JSName("forEach")
+ def jsForEach(callbackfn: Function2[A, Int, _]): Unit = native
+ @JSName("forEach")
+ def jsForEach(callbackfn: Function1[A, _]): Unit = native
+
+ /**
+ * map calls a provided callback function once for each element in an array,
+ * in order, and constructs a new array from the results. callback is
+ * invoked only for indexes of the array which have assigned values; it is
+ * not invoked for indexes which have been deleted or which have never been
+ * assigned values.
+ *
+ * callback is invoked with three arguments: the value of the element, the
+ * index of the element, and the Array object being traversed.
+ *
+ * If a thisArg parameter is provided to map, it will be used as the this for
+ * each invocation of the callback. If it is not provided, or is null, the
+ * global object associated with callback is used instead.
+ *
+ * map does not mutate the array on which it is called.
+ *
+ * MDN
+ */
+ @JSName("map")
+ def jsMap[B, T](callbackfn: ThisFunction3[T, A, Int, Array[A], B],
+ thisArg: T): Array[B] = native
+ @JSName("map")
+ def jsMap[B](callbackfn: Function3[A, Int, Array[A], B]): Array[B] = native
+ @JSName("map")
+ def jsMap[B](callbackfn: Function2[A, Int, B]): Array[B] = native
+ @JSName("map")
+ def jsMap[B](callbackfn: Function1[A, B]): Array[B] = native
+
+ /**
+ * filter calls a provided callback function once for each element in an array,
+ * and constructs a new array of all the values for which callback returns a true
+ * value. callback is invoked only for indexes of the array which have assigned
+ * values; it is not invoked for indexes which have been deleted or which have
+ * never been assigned values. Array elements which do not pass the callback
+ * test are simply skipped, and are not included in the new array.
+ *
+ * callback is invoked with three arguments:
+ *
+ * - the value of the element
+ * - the index of the element
+ * - the Array object being traversed
+ *
+ * If a thisObject parameter is provided to filter, it will be used as the this
+ * for each invocation of the callback. If it is not provided, or is null, the
+ * global object associated with callback is used instead.
+ *
+ * filter does not mutate the array on which it is called.
+ *
+ * MDN
+ */
+ @JSName("filter")
+ def jsFilter[T](callbackfn: ThisFunction3[T, A, Int, Array[A], Boolean],
+ thisArg: T): Array[A] = native
+ @JSName("filter")
+ def jsFilter(callbackfn: Function3[A, Int, Array[A], Boolean]): Array[A] = native
+ @JSName("filter")
+ def jsFilter(callbackfn: Function2[A, Int, Boolean]): Array[A] = native
+ @JSName("filter")
+ def jsFilter(callbackfn: Function1[A, Boolean]): Array[A] = native
+
+ /**
+ * reduce executes the callback function once for each element present in
+ * the array, excluding holes in the array, receiving four arguments: the
+ * initial value (or value from the previous callback call), the value of
+ * the current element, the current index, and the array over which
+ * iteration is occurring.
+ *
+ * The first time the callback is called, previousValue and currentValue can
+ * be one of two values. If initialValue is provided in the call to reduce,
+ * then previousValue will be equal to initialValue and currentValue will be
+ * equal to the first value in the array. If no initialValue was provided,
+ * then previousValue will be equal to the first value in the array and
+ * currentValue will be equal to the second.
+ *
+ * MDN
+ */
+ @JSName("reduce")
+ def jsReduce[B](callbackfn: Function4[B, A, Int, Array[A], B], initialValue: B): B = native
+ @JSName("reduce")
+ def jsReduce[B](callbackfn: Function3[B, A, Int, B], initialValue: B): B = native
+ @JSName("reduce")
+ def jsReduce[B](callbackfn: Function2[B, A, B], initialValue: B): B = native
+ @JSName("reduce")
+ def jsReduce[B](callbackfn: Function4[B, A, Int, Array[A], B]): B = native
+ @JSName("reduce")
+ def jsReduce[B](callbackfn: Function3[B, A, Int, B]): B = native
+ @JSName("reduce")
+ def jsReduce[B](callbackfn: Function2[B, A, B]): B = native
+
+ /**
+ * reduceRight executes the callback function once for each element present
+ * in the array, excluding holes in the array, receiving four arguments:
+ * the initial value (or value from the previous callback call), the value
+ * of the current element, the current index, and the array over which
+ * iteration is occurring.
+ *
+ * MDN
+ */
+ @JSName("reduceRight")
+ def jsReduceRight[B](callbackfn: Function4[B, A, Int, Array[A], B], initialValue: B): B = native
+ @JSName("reduceRight")
+ def jsReduceRight[B](callbackfn: Function3[B, A, Int, B], initialValue: B): B = native
+ @JSName("reduceRight")
+ def jsReduceRight[B](callbackfn: Function2[B, A, B], initialValue: B): B = native
+ @JSName("reduceRight")
+ def jsReduceRight[B](callbackfn: Function4[B, A, Int, Array[A], B]): B = native
+ @JSName("reduceRight")
+ def jsReduceRight[B](callbackfn: Function3[B, A, Int, B]): B = native
+ @JSName("reduceRight")
+ def jsReduceRight[B](callbackfn: Function2[B, A, B]): B = native
+
+}
+
+object JSArrayOps {
+ @inline implicit def jsArrayOps[A](array: Array[A]): JSArrayOps[A] =
+ array.asInstanceOf[JSArrayOps[A]]
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/JSConverters.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/JSConverters.scala
new file mode 100644
index 0000000..1b21d61
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/JSConverters.scala
@@ -0,0 +1,54 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.js
+
+import scala.language.implicitConversions
+
+import scala.collection._
+
+import scala.scalajs.runtime.genTraversableOnce2jsArray
+
+/** A collection of decorators that allow converting Scala types to
+ * corresponding JS facade types
+ */
+object JSConverters {
+
+ implicit class JSRichOption[T](val opt: Option[T]) extends AnyVal {
+ @inline final def orUndefined: UndefOr[T] =
+ opt.fold[UndefOr[T]](undefined)(v => v)
+ }
+
+ implicit class JSRichGenTraversableOnce[T](
+ val col: GenTraversableOnce[T]) extends AnyVal {
+ @inline final def toJSArray: Array[T] = genTraversableOnce2jsArray(col)
+ }
+
+ implicit class JSRichGenMap[T](val map: GenMap[String, T]) extends AnyVal {
+ @inline final def toJSDictionary: Dictionary[T] = {
+ val result = Dictionary.empty[T]
+ map.foreach { case (key, value) => result(key) = value }
+ result
+ }
+ }
+
+ @inline
+ implicit def genTravConvertible2JSRichGenTrav[T, C](coll: C)(
+ implicit ev: C => GenTraversableOnce[T]): JSRichGenTraversableOnce[T] =
+ new JSRichGenTraversableOnce(coll)
+
+ /** Special case for scala.Array of [[genTravConvertible2JSRichGenTrav]].
+ * Needed for the 2.10.x series.
+ */
+ @inline
+ implicit def array2JSRichGenTrav[T](
+ arr: scala.Array[T]): JSRichGenTraversableOnce[T] =
+ new JSRichGenTraversableOnce(arr)
+
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/JSON.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/JSON.scala
new file mode 100644
index 0000000..8404f40
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/JSON.scala
@@ -0,0 +1,51 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+/**
+ * All doc-comments marked as "MDN" are by Mozilla Contributors,
+ * distributed under the Creative Commons Attribution-ShareAlike license from
+ * https://developer.mozilla.org/en-US/docs/Web/Reference/API
+ */
+package scala.scalajs.js
+
+/**
+ * The JSON object contains methods for converting values to JavaScript Object
+ * Notation (JSON) and for converting JSON to values.
+ *
+ * MDN
+ */
+object JSON extends Object {
+ /**
+ * Parse a string as JSON, optionally transforming the value produced by parsing.
+ * @param text The string to parse as JSON. See the JSON object for a
+ * description of JSON syntax.
+ * @param reviver If a function, prescribes how the value originally produced
+ * by parsing is transformed, before being returned.
+ *
+ * MDN
+ */
+ def parse(text: String, reviver: Function2[Any, Any, Any] = ???): Dynamic = native
+
+ /**
+ * Convert a value to JSON, optionally replacing values if a replacer function
+ * is specified, or optionally including only the specified properties if a
+ * replacer array is specified.
+ *
+ * @param value The value to convert to a JSON string.
+ * @param replacer If a function, transforms values and properties encountered
+ * while stringifying; if an array, specifies the set of
+ * properties included in objects in the final string.
+ * @param space Causes the resulting string to be pretty-printed.
+ *
+ * MDN
+ */
+ def stringify(value: Any, replacer: Function2[String, Any, Any] = ???, space: Any = ???): String = native
+ def stringify(value: Any, replacer: Array[Any]): String = native
+ def stringify(value: Any, replacer: Array[Any], space: Any): String = native
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/JavaScriptException.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/JavaScriptException.scala
new file mode 100644
index 0000000..e4803c8
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/JavaScriptException.scala
@@ -0,0 +1,20 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+
+package scala.scalajs.js
+
+case class JavaScriptException(exception: scala.Any) extends RuntimeException {
+ override def toString() = exception.toString()
+
+ override def fillInStackTrace(): Throwable = {
+ scala.scalajs.runtime.StackTrace.captureState(this, exception)
+ this
+ }
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/Math.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/Math.scala
new file mode 100644
index 0000000..02caaa0
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/Math.scala
@@ -0,0 +1,277 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+/**
+ * All doc-comments marked as "MDN" are by Mozilla Contributors,
+ * distributed under the Creative Commons Attribution-ShareAlike license from
+ * https://developer.mozilla.org/en-US/docs/Web/Reference/API
+ */
+package scala.scalajs.js
+
+/**
+ * Math is a built-in object that has properties and methods for mathematical
+ * constants and functions. Not a function object.
+ *
+ * MDN
+ */
+object Math extends Object {
+ /**
+ * Euler's constant and the base of natural logarithms, approximately 2.718.
+ *
+ * MDN
+ */
+ val E: Double = native
+ /**
+ * Natural logarithm of 10, approximately 2.303.
+ *
+ * MDN
+ */
+ val LN10: Double = native
+ /**
+ * Natural logarithm of 2, approximately 0.693.
+ *
+ * MDN
+ */
+ val LN2: Double = native
+ /**
+ * Base 2 logarithm of E, approximately 1.443.
+ *
+ * MDN
+ */
+ val LOG2E: Double = native
+ /**
+ * Base 10 logarithm of E, approximately 0.434.
+ *
+ * MSN
+ */
+ val LOG10E: Double = native
+ /**
+ * Ratio of the circumference of a circle to its diameter, approximately 3.14159.
+ *
+ * MDN
+ */
+ val PI: Double = native
+
+ /**
+ * Square root of 1/2; equivalently, 1 over the square root of 2, approximately 0.707.
+ *
+ * MDN
+ */
+ val SQRT1_2: Double = native
+
+ /**
+ * Square root of 2, approximately 1.414.
+ *
+ * MDN
+ */
+ val SQRT2: Double = native
+
+ /**
+ * Returns the absolute value of a number.
+ *
+ * Passing a non-numeric string or undefined/empty variable returns NaN.
+ * Passing null returns 0.
+ *
+ * MDN
+ */
+ def abs(x: Int): Int = native
+
+ /**
+ * Returns the absolute value of a number.
+ *
+ * Passing a non-numeric string or undefined/empty variable returns NaN.
+ * Passing null returns 0.
+ *
+ * MDN
+ */
+ def abs(x: Double): Double = native
+
+ /**
+ * The Math.acos() function returns the arccosine (in radians) of a number.
+ *
+ * The acos method returns a numeric value between 0 and pi radians for x
+ * between -1 and 1. If the value of number is outside this range, it returns NaN.
+ *
+ * MDN
+ */
+ def acos(x: Double): Double = native
+
+ /**
+ * The Math.asin() function returns the arcsine (in radians) of a number.
+ *
+ * The asin method returns a numeric value between -pi/2 and pi/2 radians for x
+ * between -1 and 1. If the value of number is outside this range, it returns NaN.
+ *
+ * MDN
+ */
+ def asin(x: Double): Double = native
+
+ /**
+ * The Math.atan() function returns the arctangent (in radians) of a number.
+ *
+ * The atan method returns a numeric value between -pi/2 and pi/2 radians.
+ *
+ * MDN
+ */
+ def atan(x: Double): Double = native
+
+ /**
+ * The Math.atan2() function returns the arctangent of the quotient of its
+ * arguments.
+ *
+ * The atan2 method returns a numeric value between -pi and pi representing
+ * the angle theta of an (x,y) point. This is the counterclockwise angle,
+ * measured in radians, between the positive X axis, and the point (x,y).
+ * Note that the arguments to this function pass the y-coordinate first and
+ * the x-coordinate second.
+ *
+ * atan2 is passed separate x and y arguments, and atan is passed the ratio
+ * of those two arguments.
+ *
+ * MDN
+ */
+ def atan2(y: Double, x: Double): Double = native
+
+ /**
+ * The Math.ceil() function returns the smallest integer greater than or
+ * equal to a number.
+ *
+ * MDN
+ */
+ def ceil(x: Double): Double = native
+
+ /**
+ * The Math.cos() function returns the cosine of a number.
+ *
+ * The cos method returns a numeric value between -1 and 1, which represents
+ * the cosine of the angle.
+ *
+ * MDN
+ */
+ def cos(x: Double): Double = native
+
+ /**
+ * The Math.exp() function returns E^x, where x is the argument, and E is
+ * Euler's constant, the base of the natural logarithms.
+ *
+ * MDN
+ */
+ def exp(x: Double): Double = native
+
+ /**
+ * The Math.floor() function returns the largest integer less than or equal
+ * to a number.
+ *
+ * MDN
+ */
+ def floor(x: Double): Double = native
+
+ /**
+ * The Math.log() function returns the natural logarithm (base E) of a number.
+ *
+ * If the value of number is negative, the return value is always NaN.
+ *
+ * MDN
+ */
+ def log(x: Double): Double = native
+
+ /**
+ * The Math.max() function returns the largest of zero or more numbers.
+ *
+ * MDN
+ */
+ def max(value1: Int, values: Int*): Int = native
+
+ /**
+ * The Math.max() function returns the largest of zero or more numbers.
+ *
+ * If no arguments are given, the result is - Infinity.
+ *
+ * If at least one of arguments cannot be converted to a number, the result is NaN.
+ *
+ * MDN
+ */
+ def max(values: Double*): Double = native
+
+ /**
+ * The Math.min() function returns the smallest of zero or more numbers.
+ *
+ * MDN
+ */
+ def min(value1: Int, values: Int*): Int = native
+
+ /**
+ * The Math.min() function returns the smallest of zero or more numbers.
+ *
+ * If no arguments are given, the result is Infinity.
+ *
+ * If at least one of arguments cannot be converted to a number, the result is NaN.
+ *
+ * MDN
+ */
+ def min(values: Double*): Double = native
+
+ /**
+ * The Math.pow() function returns the base to the exponent Power, that is, base^^exponent.
+ *
+ * MDN
+ */
+ def pow(x: Double, y: Double): Double = native
+
+ /**
+ * The Math.random() function returns a floating-point, pseudo-random number in
+ * the range [0, 1) that is, from 0 (inclusive) up to but not including 1
+ * (exclusive), which you can then scale to your desired range.
+ *
+ * The random number generator is seeded from the current time, as in Java.
+ *
+ * MDN
+ */
+ def random(): Double = native
+
+ /**
+ * The Math.round() function returns the value of a number rounded to the
+ * nearest integer.
+ *
+ * If the fractional portion of number is .5 or greater, the argument is
+ * rounded to the next higher integer. If the fractional portion of number
+ * is less than .5, the argument is rounded to the next lower integer.
+ *
+ * MDN
+ */
+ def round(x: Double): Double = native
+
+ /**
+ * The Math.sin() function returns the sine of a number.
+ *
+ * The sin method returns a numeric value between -1 and 1, which represents
+ * the sine of the angle given in radians.
+ *
+ * MDN
+ */
+ def sin(x: Double): Double = native
+
+ /**
+ * The Math.sqrt() function returns the square root (x\sqrt{x}) of a number.
+ *
+ * If the value of number is negative, sqrt returns NaN.
+ *
+ * MDN
+ */
+ def sqrt(x: Double): Double = native
+
+ /**
+ * The Math.tan() function returns the tangent of a number.
+ *
+ * The tan method returns a numeric value that represents the tangent of the angle.
+ *
+ * MDN
+ */
+ def tan(x: Double): Double = native
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/Primitives.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/Primitives.scala
new file mode 100644
index 0000000..1a3d88c
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/Primitives.scala
@@ -0,0 +1,872 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+/**
+ * All doc-comments marked as "MDN" are by Mozilla Contributors,
+ * distributed under the Creative Commons Attribution-ShareAlike license from
+ * https://developer.mozilla.org/en-US/docs/Web/Reference/API
+ */
+package scala.scalajs.js
+
+import annotation.JSBracketAccess
+
+import scala.language.{ dynamics, implicitConversions }
+import scala.reflect.ClassTag
+import scala.collection.{ immutable, mutable }
+import scala.collection.generic.CanBuildFrom
+
+/** Super-type of all JavaScript values.
+ *
+ * All values of a subtype of this trait represent JavaScript values, without
+ * boxing of proxying of any kind.
+ */
+sealed trait Any extends scala.AnyRef {
+}
+
+/** Provides implicit conversions from Scala values to JavaScript values. */
+object Any extends LowPrioAnyImplicits {
+ @inline implicit def fromUnit(value: Unit): prim.Undefined =
+ value.asInstanceOf[prim.Undefined]
+ @inline implicit def fromBoolean(value: scala.Boolean): prim.Boolean =
+ value.asInstanceOf[prim.Boolean]
+ @inline implicit def fromByte(value: scala.Byte): prim.Number =
+ value.asInstanceOf[prim.Number]
+ @inline implicit def fromShort(value: scala.Short): prim.Number =
+ value.asInstanceOf[prim.Number]
+ @inline implicit def fromInt(value: scala.Int): prim.Number =
+ value.asInstanceOf[prim.Number]
+ @inline implicit def fromLong(value: scala.Long): prim.Number =
+ value.toDouble.asInstanceOf[prim.Number]
+ @inline implicit def fromFloat(value: scala.Float): prim.Number =
+ value.asInstanceOf[prim.Number]
+ @inline implicit def fromDouble(value: scala.Double): prim.Number =
+ value.asInstanceOf[prim.Number]
+ @inline implicit def fromString(s: java.lang.String): prim.String =
+ s.asInstanceOf[prim.String]
+
+ @inline implicit def toDouble(value: prim.Number): scala.Double =
+ value.asInstanceOf[scala.Double]
+ @inline implicit def toBoolean(value: prim.Boolean): scala.Boolean =
+ value.asInstanceOf[scala.Boolean]
+ @inline implicit def toScalaString(value: prim.String): java.lang.String =
+ value.asInstanceOf[java.lang.String]
+
+ implicit def jsArrayOps[A](array: Array[A]): ArrayOps[A] =
+ new ArrayOps(array)
+
+ implicit def canBuildFromArray[A]: CanBuildFrom[Array[_], A, Array[A]] = {
+ @inline
+ class CanBuildFromArray extends CanBuildFrom[Array[_], A, Array[A]] {
+ def apply(from: Array[_]): mutable.Builder[A, Array[A]] =
+ new ArrayOps[A]
+ def apply(): mutable.Builder[A, Array[A]] =
+ new ArrayOps[A]
+ }
+ new CanBuildFromArray
+ }
+
+ implicit def fromFunction0[R](f: scala.Function0[R]): Function0[R] = sys.error("stub")
+ implicit def fromFunction1[T1, R](f: scala.Function1[T1, R]): Function1[T1, R] = sys.error("stub")
+ implicit def fromFunction2[T1, T2, R](f: scala.Function2[T1, T2, R]): Function2[T1, T2, R] = sys.error("stub")
+ implicit def fromFunction3[T1, T2, T3, R](f: scala.Function3[T1, T2, T3, R]): Function3[T1, T2, T3, R] = sys.error("stub")
+ implicit def fromFunction4[T1, T2, T3, T4, R](f: scala.Function4[T1, T2, T3, T4, R]): Function4[T1, T2, T3, T4, R] = sys.error("stub")
+ implicit def fromFunction5[T1, T2, T3, T4, T5, R](f: scala.Function5[T1, T2, T3, T4, T5, R]): Function5[T1, T2, T3, T4, T5, R] = sys.error("stub")
+ implicit def fromFunction6[T1, T2, T3, T4, T5, T6, R](f: scala.Function6[T1, T2, T3, T4, T5, T6, R]): Function6[T1, T2, T3, T4, T5, T6, R] = sys.error("stub")
+ implicit def fromFunction7[T1, T2, T3, T4, T5, T6, T7, R](f: scala.Function7[T1, T2, T3, T4, T5, T6, T7, R]): Function7[T1, T2, T3, T4, T5, T6, T7, R] = sys.error("stub")
+ implicit def fromFunction8[T1, T2, T3, T4, T5, T6, T7, T8, R](f: scala.Function8[T1, T2, T3, T4, T5, T6, T7, T8, R]): Function8[T1, T2, T3, T4, T5, T6, T7, T8, R] = sys.error("stub")
+ implicit def fromFunction9[T1, T2, T3, T4, T5, T6, T7, T8, T9, R](f: scala.Function9[T1, T2, T3, T4, T5, T6, T7, T8, T9, R]): Function9[T1, T2, T3, T4, T5, T6, T7, T8, T9, R] = sys.error("stub")
+ implicit def fromFunction10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R](f: scala.Function10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R]): Function10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R] = sys.error("stub")
+ implicit def fromFunction11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, R](f: scala.Function11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, R]): Function11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, R] = sys.error("stub")
+ implicit def fromFunction12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, R](f: scala.Function12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, R]): Function12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, R] = sys.error("stub")
+ implicit def fromFunction13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, R](f: scala.Function13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, R]): Function13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, R] = sys.error("stub")
+ implicit def fromFunction14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, R](f: scala.Function14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, R]): Function14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, R] = sys.error("stub")
+ implicit def fromFunction15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, R](f: scala.Function15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, R]): Function15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, R] = sys.error("stub")
+ implicit def fromFunction16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, R](f: scala.Function16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, R]): Function16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, R] = sys.error("stub")
+ implicit def fromFunction17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, R](f: scala.Function17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, R]): Function17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, R] = sys.error("stub")
+ implicit def fromFunction18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, R](f: scala.Function18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, R]): Function18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, R] = sys.error("stub")
+ implicit def fromFunction19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, R](f: scala.Function19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, R]): Function19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, R] = sys.error("stub")
+ implicit def fromFunction20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, R](f: scala.Function20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, R]): Function20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, R] = sys.error("stub")
+ implicit def fromFunction21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, R](f: scala.Function21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, R]): Function21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, R] = sys.error("stub")
+ implicit def fromFunction22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, R](f: scala.Function22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, R]): Function22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, R] = sys.error("stub")
+
+ implicit def toFunction0[R](f: Function0[R]): scala.Function0[R] = () => f()
+ implicit def toFunction1[T1, R](f: Function1[T1, R]): scala.Function1[T1, R] = (x1) => f(x1)
+ implicit def toFunction2[T1, T2, R](f: Function2[T1, T2, R]): scala.Function2[T1, T2, R] = (x1, x2) => f(x1, x2)
+ implicit def toFunction3[T1, T2, T3, R](f: Function3[T1, T2, T3, R]): scala.Function3[T1, T2, T3, R] = (x1, x2, x3) => f(x1, x2, x3)
+ implicit def toFunction4[T1, T2, T3, T4, R](f: Function4[T1, T2, T3, T4, R]): scala.Function4[T1, T2, T3, T4, R] = (x1, x2, x3, x4) => f(x1, x2, x3, x4)
+ implicit def toFunction5[T1, T2, T3, T4, T5, R](f: Function5[T1, T2, T3, T4, T5, R]): scala.Function5[T1, T2, T3, T4, T5, R] = (x1, x2, x3, x4, x5) => f(x1, x2, x3, x4, x5)
+ implicit def toFunction6[T1, T2, T3, T4, T5, T6, R](f: Function6[T1, T2, T3, T4, T5, T6, R]): scala.Function6[T1, T2, T3, T4, T5, T6, R] = (x1, x2, x3, x4, x5, x6) => f(x1, x2, x3, x4, x5, x6)
+ implicit def toFunction7[T1, T2, T3, T4, T5, T6, T7, R](f: Function7[T1, T2, T3, T4, T5, T6, T7, R]): scala.Function7[T1, T2, T3, T4, T5, T6, T7, R] = (x1, x2, x3, x4, x5, x6, x7) => f(x1, x2, x3, x4, x5, x6, x7)
+ implicit def toFunction8[T1, T2, T3, T4, T5, T6, T7, T8, R](f: Function8[T1, T2, T3, T4, T5, T6, T7, T8, R]): scala.Function8[T1, T2, T3, T4, T5, T6, T7, T8, R] = (x1, x2, x3, x4, x5, x6, x7, x8) => f(x1, x2, x3, x4, x5, x6, x7, x8)
+ implicit def toFunction9[T1, T2, T3, T4, T5, T6, T7, T8, T9, R](f: Function9[T1, T2, T3, T4, T5, T6, T7, T8, T9, R]): scala.Function9[T1, T2, T3, T4, T5, T6, T7, T8, T9, R] = (x1, x2, x3, x4, x5, x6, x7, x8, x9) => f(x1, x2, x3, x4, x5, x6, x7, x8, x9)
+ implicit def toFunction10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R](f: Function10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R]): scala.Function10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R] = (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10) => f(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)
+ implicit def toFunction11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, R](f: Function11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, R]): scala.Function11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, R] = (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11) => f(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11)
+ implicit def toFunction12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, R](f: Function12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, R]): scala.Function12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, R] = (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12) => f(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12)
+ implicit def toFunction13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, R](f: Function13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, R]): scala.Function13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, R] = (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13) => f(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13)
+ implicit def toFunction14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, R](f: Function14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, R]): scala.Function14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, R] = (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14) => f(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14)
+ implicit def toFunction15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, R](f: Function15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, R]): scala.Function15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, R] = (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15) => f(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15)
+ implicit def toFunction16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, R](f: Function16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, R]): scala.Function16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, R] = (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16) => f(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16)
+ implicit def toFunction17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, R](f: Function17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, R]): scala.Function17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, R] = (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17) => f(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17)
+ implicit def toFunction18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, R](f: Function18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, R]): scala.Function18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, R] = (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18) => f(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18)
+ implicit def toFunction19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, R](f: Function19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, R]): scala.Function19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, R] = (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19) => f(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19)
+ implicit def toFunction20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, R](f: Function20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, R]): scala.Function20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, R] = (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20) => f(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20)
+ implicit def toFunction21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, R](f: Function21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, R]): scala.Function21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, R] = (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21) => f(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21)
+ implicit def toFunction22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, R](f: Function22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, R]): scala.Function22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, R] = (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22) => f(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22)
+}
+
+trait LowPrioAnyImplicits {
+ @inline implicit def richDouble(num: prim.Number): scala.runtime.RichDouble =
+ new scala.runtime.RichDouble(Any.toDouble(num))
+ @inline implicit def richBoolean(b: prim.Boolean): scala.runtime.RichBoolean =
+ new scala.runtime.RichBoolean(Any.toBoolean(b))
+
+ @inline implicit def stringOps(string: prim.String): immutable.StringOps =
+ new immutable.StringOps(Any.toScalaString(string))
+
+ implicit def wrapArray[A](array: Array[A]): WrappedArray[A] =
+ new WrappedArray(array)
+ implicit def wrapDictionary[A](dict: Dictionary[A]): WrappedDictionary[A] =
+ new WrappedDictionary(dict)
+}
+
+/** Dynamically typed JavaScript value.
+ *
+ * Values of this trait accept all possible JavaScript operations in a
+ * dynamically typed way. You can read and write any field, call any method,
+ * apply any JavaScript operator to values of this type.
+ */
+sealed trait Dynamic extends Any with scala.Dynamic {
+ /** Calls a method of this object. */
+ def applyDynamic(name: java.lang.String)(args: Any*): Dynamic = sys.error("stub")
+
+ /** Reads a field of this object. */
+ def selectDynamic(name: java.lang.String): Dynamic = sys.error("stub")
+
+ /** Writes a field of this object. */
+ def updateDynamic(name: java.lang.String)(value: Any): Unit = sys.error("stub")
+
+ /** Calls this object as a callable. */
+ def apply(args: Any*): Dynamic = native
+
+ import prim.Number
+
+ def unary_!(): Boolean = native
+
+ def unary_+(): Number = native
+ def unary_-(): Number = native
+ def unary_~(): Number = native
+
+ def +(that: Number): Dynamic = native // could be a String if this is a String
+ def -(that: Number): Number = native
+ def *(that: Number): Number = native
+ def /(that: Number): Number = native
+ def %(that: Number): Number = native
+ def <<(that: Number): Number = native
+ def >>(that: Number): Number = native
+ def >>>(that: Number): Number = native
+ def &(that: Number): Number = native
+ def |(that: Number): Number = native
+ def ^(that: Number): Number = native
+
+ def +(that: Dynamic): Dynamic = native // could be String if this or that is a String
+ def -(that: Dynamic): Number = native
+ def *(that: Dynamic): Number = native
+ def /(that: Dynamic): Number = native
+ def %(that: Dynamic): Number = native
+ def <<(that: Dynamic): Number = native
+ def >>(that: Dynamic): Number = native
+ def >>>(that: Dynamic): Number = native
+ def &(that: Dynamic): Number = native
+ def |(that: Dynamic): Number = native
+ def ^(that: Dynamic): Number = native
+
+ def <(that: Number): Boolean = native
+ def >(that: Number): Boolean = native
+ def <=(that: Number): Boolean = native
+ def >=(that: Number): Boolean = native
+
+ def <(that: String): Boolean = native
+ def >(that: String): Boolean = native
+ def <=(that: String): Boolean = native
+ def >=(that: String): Boolean = native
+
+ def <(that: Dynamic): Boolean = native
+ def >(that: Dynamic): Boolean = native
+ def <=(that: Dynamic): Boolean = native
+ def >=(that: Dynamic): Boolean = native
+
+ /* The result of (dyn && bool) and (dyn || bool) has, in theory, type
+ * (Dynamic v Boolean). This type cannot be expressed in Scala, but if it
+ * could, the operations one could apply on a (Dynamic v Boolean) would be
+ * the *intersection* of the operations one can apply on a Dynamic and on a
+ * Boolean. Since any operation can be applied on a Dynamic, this
+ * intersection is equal to the set of operations supported by Boolean.
+ * Hence the result type is restricted to Boolean.
+ */
+ def &&(that: Boolean): Boolean = native
+ def ||(that: Boolean): Boolean = native
+
+ def &&(that: Dynamic): Dynamic = native
+ def ||(that: Dynamic): Dynamic = native
+
+ // Work around the annoying implicits in Predef in Scala 2.10.
+ def x: Dynamic = native
+ def x_=(value: Any): Unit = native
+}
+
+/** Factory for dynamically typed JavaScript values. */
+object Dynamic {
+ /** Dynamic view of the global scope. */
+ @inline def global: Dynamic = scala.scalajs.runtime.environmentInfo.global
+
+ /** Instantiates a new object of a JavaScript class. */
+ def newInstance(clazz: Dynamic)(args: Any*): Object with Dynamic = sys.error("stub")
+
+ /** Creates a new object with a literal syntax.
+ *
+ * For example,
+ * js.Dynamic.literal(foo = 3, bar = "foobar")
+ * returns the JavaScript object
+ * {foo: 3, bar: "foobar"}
+ */
+ object literal extends scala.Dynamic {
+ /** literal creation like this:
+ * js.Dynamic.literal(name1 = "value", name2 = "value")
+ */
+ def applyDynamicNamed(name: java.lang.String)(
+ fields: (java.lang.String, Any)*): Object with Dynamic = sys.error("stub")
+
+ /** literal creation like this:
+ * js.Dynamic.literal("name1" -> "value", "name2" -> "value")
+ *
+ * Note that this could be simply `def apply`, but this would make the
+ * applyDynamicNamed fail, since a call with named arguments would
+ * be routed to the `def apply`, rather than def dynamic version.
+ */
+ def applyDynamic(name: java.lang.String)(
+ fields: (java.lang.String, Any)*): Object with Dynamic = sys.error("stub")
+
+ }
+}
+
+/** Base class of all JavaScript objects. */
+class Object extends Any {
+ def this(value: Any) = this()
+
+ def toLocaleString(): String = native
+ def valueOf(): scala.Any = native
+
+ /** Tests whether this object has the specified property as a direct property.
+ *
+ * Unlike [[js.Object.hasProperty]], this method does not check down the
+ * object's prototype chain.
+ *
+ * MDN
+ */
+ def hasOwnProperty(v: String): Boolean = native
+
+ /** Tests whether this object is in the prototype chain of another object. */
+ def isPrototypeOf(v: Object): Boolean = native
+
+ /** Tests whether the specified property in an object can be enumerated by a
+ * call to [[js.Object.properties]], with the exception of properties
+ * inherited through the prototype chain. If the object does not have the
+ * specified property, this method returns false.
+ *
+ * MDN
+ */
+ def propertyIsEnumerable(v: String): Boolean = native
+}
+
+/** The top-level `Object` JavaScript object. */
+object Object extends Object {
+ def apply(): Object = native
+ def apply(value: Any): Object = native
+
+ /** Tests whether the object has a property on itself or in its prototype
+ * chain. This method is the equivalent of `p in o` in JavaScript.
+ */
+ def hasProperty(o: Object, p: String): Boolean = sys.error("stub")
+
+ /**
+ * The Object.getPrototypeOf() method returns the prototype (i.e. the
+ * internal [[Prototype]]) of the specified object.
+ *
+ * MDN
+ */
+ def getPrototypeOf(o: Object): Object = native
+
+ /**
+ * The Object.getOwnPropertyDescriptor() method returns a property descriptor
+ * for an own property (that is, one directly present on an object, not
+ * present by dint of being along an object's prototype chain) of a given object.
+ *
+ * MDN
+ */
+ def getOwnPropertyDescriptor(o: Object, p: String): PropertyDescriptor = native
+
+ /**
+ * Object.getOwnPropertyNames returns an array whose elements are strings
+ * corresponding to the enumerable and non-enumerable properties found
+ * directly upon obj. The ordering of the enumerable properties in the array
+ * is consistent with the ordering exposed by a for...in loop (or by Object.keys)
+ * over the properties of the object. The ordering of the non-enumerable
+ * properties in the array, and among the enumerable properties, is not defined.
+ *
+ * MDN
+ */
+ def getOwnPropertyNames(o: Object): Array[String] = native
+
+ /**
+ * The Object.create() method creates a new object with the specified
+ * prototype object and properties.
+ *
+ * MDN
+ */
+ def create(o: Object, properties: Any): Object = native
+ def create(o: Object): Object = native
+
+ /**
+ * The Object.defineProperty() method defines a new property directly on an
+ * object, or modifies an existing property on an object, and returns the
+ * object.
+ *
+ * This method allows precise addition to or modification of a property on an
+ * object. Normal property addition through assignment creates properties
+ * which show up during property enumeration (for...in loop or Object.keys method),
+ * whose values may be changed, and which may be deleted. This method allows
+ * these extra details to be changed from their defaults.
+ *
+ * Property descriptors present in objects come in two main flavors: data
+ * descriptors and accessor descriptors. A data descriptor is a property
+ * that has a value, which may or may not be writable. An accessor descriptor
+ * is a property described by a getter-setter pair of functions. A descriptor
+ * must be one of these two flavors; it cannot be both.
+ *
+ * MDN
+ */
+ def defineProperty(o: Object, p: String, attributes: PropertyDescriptor): o.type = native
+
+ /**
+ * The Object.defineProperties() method defines new or modifies existing
+ * properties directly on an object, returning the object.
+ *
+ * MDN
+ */
+ def defineProperties(o: Object, properties: Any): o.type = native
+
+ /**
+ * The Object.seal() method seals an object, preventing new properties from
+ * being added to it and marking all existing properties as non-configurable.
+ * Values of present properties can still be changed as long as they are
+ * writable.
+ *
+ * MDN
+ */
+ def seal(o: Object): o.type = native
+
+ /**
+ * The Object.freeze() method freezes an object: that is, prevents new properties
+ * from being added to it; prevents existing properties from being removed;
+ * and prevents existing properties, or their enumerability, configurability,
+ * or writability, from being changed. In essence the object is made effectively
+ * immutable. The method returns the object being frozen.
+ *
+ * MDN
+ */
+ def freeze(o: Object): o.type = native
+
+ /**
+ * The Object.preventExtensions() method prevents new properties from ever
+ * being added to an object (i.e. prevents future extensions to the object).
+ *
+ * An object is extensible if new properties can be added to it. preventExtensions
+ * marks an object as no longer extensible, so that it will never have
+ * properties beyond the ones it had at the time it was marked as non-extensible.
+ * Note that the properties of a non-extensible object, in general, may still be
+ * deleted. Attempting to add new properties to a non-extensible object will
+ * fail, either silently or by throwing a TypeError (most commonly, but not
+ * exclusively, when in strict mode).
+ *
+ * Object.preventExtensions only prevents addition of own properties. Properties
+ * can still be added to the object prototype. However, calling Object.preventExtensions
+ * on an object will also prevent extensions on its __proto__ property.
+ *
+ * MDN
+ */
+ def preventExtensions(o: Object): o.type = native
+
+ /**
+ * Returns true if the object is sealed, otherwise false. An object is sealed
+ * if it is not extensible and if all its properties are non-configurable and
+ * therefore not removable (but not necessarily non-writable).
+ *
+ * MDN
+ */
+ def isSealed(o: Object): Boolean = native
+
+ /**
+ * The Object.isFrozen() determines if an object is frozen.
+ *
+ * An object is frozen if and only if it is not extensible, all its properties
+ * are non-configurable, and all its data properties (that is, properties which
+ * are not accessor properties with getter or setter components) are non-writable.
+ *
+ * MDN
+ */
+ def isFrozen(o: Object): Boolean = native
+
+ /**
+ * Determines if extending of an object is allowed
+ *
+ * Objects are extensible by default: they can have new properties added to
+ * them, and (in engines that support __proto__ their __proto__ property)
+ * can be modified. An object can be marked as non-extensible using
+ * Object.preventExtensions, Object.seal, or Object.freeze
+ *
+ * MDN
+ */
+ def isExtensible(o: Object): Boolean = native
+
+ /**
+ * The Object.keys() method returns an array of a given object's own enumerable
+ * properties, in the same order as that provided by a for...in loop (the
+ * difference being that a for-in loop enumerates properties in the prototype
+ * chain as well).
+ *
+ * MDN
+ */
+ def keys(o: Object): Array[String] = native
+
+ /** Returns the names of all the enumerable properties of this object,
+ * including properties in its prototype chain.
+ *
+ * This method returns the same set of names that would be enumerated by
+ * a for-in loop in JavaScript, but not necessarily in the same order.
+ *
+ * If the underlying implementation guarantees an order for for-in loops,
+ * then this is guaranteed to be consistent with [[keys]], in the sense
+ * that the list returned by [[keys]] is a sublist of the list returned by
+ * this method (not just a subset).
+ */
+ def properties(o: Any): Array[String] = sys.error("stub")
+}
+
+package prim {
+
+/** Primitive JavaScript number.
+ *
+ * In most situations, you should not need this trait, and use
+ * [[scala.Double]] instead (or [[scala.Int]] where appropriate).
+ */
+sealed trait Number extends Any {
+ def unary_+(): Number = native
+ def unary_-(): Number = native
+ def unary_~(): Number = native
+
+ def +(that: Number): Number = native
+ def -(that: Number): Number = native
+ def *(that: Number): Number = native
+ def /(that: Number): Number = native
+ def %(that: Number): Number = native
+ def <<(that: Number): Number = native
+ def >>(that: Number): Number = native
+ def >>>(that: Number): Number = native
+ def &(that: Number): Number = native
+ def |(that: Number): Number = native
+ def ^(that: Number): Number = native
+
+ def +(that: Dynamic): Dynamic = native // could be a String if that is a String
+ def -(that: Dynamic): Number = native
+ def *(that: Dynamic): Number = native
+ def /(that: Dynamic): Number = native
+ def %(that: Dynamic): Number = native
+ def <<(that: Dynamic): Number = native
+ def >>(that: Dynamic): Number = native
+ def >>>(that: Dynamic): Number = native
+ def &(that: Dynamic): Number = native
+ def |(that: Dynamic): Number = native
+ def ^(that: Dynamic): Number = native
+
+ def <(that: Number): Boolean = native
+ def >(that: Number): Boolean = native
+ def <=(that: Number): Boolean = native
+ def >=(that: Number): Boolean = native
+
+ def <(that: Dynamic): Boolean = native
+ def >(that: Dynamic): Boolean = native
+ def <=(that: Dynamic): Boolean = native
+ def >=(that: Dynamic): Boolean = native
+
+ def toString(radix: Number): String = native
+
+ /**
+ * Returns a string representation of number that does not use exponential
+ * notation and has exactly digits digits after the decimal place. The number
+ * is rounded if necessary, and the fractional part is padded with zeros if
+ * necessary so that it has the specified length. If number is greater than
+ * 1e+21, this method simply calls Number.prototype.toString() and returns
+ * a string in exponential notation.
+ *
+ * MDN
+ */
+ def toFixed(fractionDigits: Number): String = native
+ def toFixed(): String = native
+
+ /**
+ * Returns a string representing a Number object in exponential notation with one
+ * digit before the decimal point, rounded to fractionDigits digits after the
+ * decimal point. If the fractionDigits argument is omitted, the number of
+ * digits after the decimal point defaults to the number of digits necessary
+ * to represent the value uniquely.
+ *
+ * If a number has more digits that requested by the fractionDigits parameter,
+ * the number is rounded to the nearest number represented by fractionDigits
+ * digits. See the discussion of rounding in the description of the toFixed()
+ * method, which also applies to toExponential().
+ *
+ * MDN
+ */
+ def toExponential(fractionDigits: Number): String = native
+ def toExponential(): String = native
+
+ /**
+ * Returns a string representing a Number object in fixed-point or exponential
+ * notation rounded to precision significant digits. See the discussion of
+ * rounding in the description of the Number.prototype.toFixed() method, which
+ * also applies to toPrecision.
+ *
+ * If the precision argument is omitted, behaves as Number.prototype.toString().
+ * If it is a non-integer value, it is rounded to the nearest integer.
+ *
+ * MDN
+ */
+ def toPrecision(precision: Number): String = native
+ def toPrecision(): String = native
+}
+
+/** The top-level `Number` JavaScript object */
+object Number extends Object {
+ /**
+ * The Number.MAX_VALUE property represents the maximum numeric value
+ * representable in JavaScript.
+ *
+ * The MAX_VALUE property has a value of approximately 1.79E+308. Values
+ * larger than MAX_VALUE are represented as "Infinity".
+ *
+ * MDN
+ */
+ val MAX_VALUE: Double = native
+ /**
+ * The Number.MIN_VALUE property represents the smallest positive numeric
+ * value representable in JavaScript.
+ *
+ * The MIN_VALUE property is the number closest to 0, not the most negative
+ * number, that JavaScript can represent.
+ *
+ * MIN_VALUE has a value of approximately 5e-324. Values smaller than MIN_VALUE
+ * ("underflow values") are converted to 0.
+ *
+ * MDN
+ */
+ val MIN_VALUE: Double = native
+ /**
+ * The Number.NaN property represents Not-A-Number. Equivalent of NaN.
+ *
+ * MDN
+ */
+ val NaN: Double = native
+
+ /**
+ * The Number.NEGATIVE_INFINITY property represents the negative Infinity value.
+ *
+ * MDN
+ */
+ val NEGATIVE_INFINITY: Double = native
+ /**
+ * The Number.POSITIVE_INFINITY property represents the positive Infinity value.
+ *
+ * MDN
+ */
+ val POSITIVE_INFINITY: Double = native
+}
+
+/** Primitive JavaScript boolean.
+ *
+ * In most situations, you should not need this trait, and use
+ * [[scala.Boolean]] instead.
+ */
+sealed trait Boolean extends Any {
+ def unary_!(): scala.Boolean = native
+
+ def &&(that: Boolean): Boolean = native
+ def ||(that: Boolean): Boolean = native
+
+ // See the comment in `Dynamic` for the rationale of returning Boolean here.
+ def &&(that: Dynamic): Boolean = native
+ def ||(that: Dynamic): Boolean = native
+}
+
+/** The top-level `Boolean` JavaScript object. */
+object Boolean extends Object
+
+/** Primitive JavaScript string.
+ *
+ * In most situations, you should not need this trait, and use
+ * [[java.lang.String]] instead.
+ */
+sealed trait String extends Any {
+ def +(that: String): String = native
+ def +(that: Any): String = native
+ def +(that: Dynamic): String = native
+
+ def < (that: String): Boolean = native
+ def < (that: Dynamic): Boolean = native
+
+ def > (that: String): Boolean = native
+ def > (that: Dynamic): Boolean = native
+
+ def <=(that: String): Boolean = native
+ def <=(that: Dynamic): Boolean = native
+
+ def >=(that: String): Boolean = native
+ def >=(that: Dynamic): Boolean = native
+
+ /**
+ * This property returns the number of code units in the string. UTF-16,
+ * the string format used by JavaScript, uses a single 16-bit code unit to
+ * represent the most common characters, but needs to use two code units for
+ * less commonly-used characters, so it's possible for the value returned by
+ * length to not match the actual number of characters in the string.
+ *
+ * For an empty string, length is 0.
+ *
+ * MDN
+ */
+ val length: Number = native
+
+ /**
+ * The chartAt() method returns the specified character from a string.
+ *
+ * Characters in a string are indexed from left to right. The index of the
+ * first character is 0, and the index of the last character in a string
+ * called stringName is stringName.length - 1. If the index you supply is out
+ * of range, JavaScript returns an empty string.
+ *
+ * MDN
+ */
+ def charAt(pos: Number): String = native
+
+ /**
+ * The charCodeAt() method returns the numeric Unicode value of the character
+ * at the given index (except for unicode codepoints > 0x10000).
+ *
+ * MDN
+ */
+ def charCodeAt(index: Number): Number = native
+
+ /**
+ * concat combines the text from one or more strings and returns a new string.
+ * Changes to the text in one string do not affect the other string.
+ * MDN
+ */
+ def concat(strings: String*): String = native
+
+ /**
+ * Returns the index within the calling String object of the first occurrence
+ * of the specified value, starting the search at fromIndex,
+ *
+ * returns -1 if the value is not found.
+ *
+ * MDN
+ */
+ def indexOf(searchString: String, position: Number): Number = native
+ def indexOf(searchString: String): Number = native
+
+ /**
+ * Returns the index within the calling String object of the last occurrence
+ * of the specified value, or -1 if not found. The calling string is searched
+ * backward, starting at fromIndex.
+ *
+ * MDN
+ */
+ def lastIndexOf(searchString: String, position: Number): Number = native
+ def lastIndexOf(searchString: String): Number = native
+
+ /**
+ * Returns a number indicating whether a reference string comes before or
+ * after or is the same as the given string in sort order. The new locales
+ * and options arguments let applications specify the language whose sort
+ * order should be used and customize the behavior of the function. In older
+ * implementations, which ignore the locales and options arguments, the locale
+ * and sort order used are entirely implementation dependent.
+ *
+ * MDN
+ */
+ def localeCompare(that: String): Number = native
+
+ /**
+ * Used to retrieve the matches when matching a string against a regular
+ * expression.
+ *
+ * If the regular expression does not include the g flag, returns the same
+ * result as regexp.exec(string). The returned Array has an extra input
+ * property, which contains the original string that was parsed. In addition,
+ * it has an index property, which represents the zero-based index of the
+ * match in the string.
+ *
+ * If the regular expression includes the g flag, the method returns an Array
+ * containing all matches. If there were no matches, the method returns null.
+ *
+ * MDN
+ */
+ def `match`(regexp: String): Array[String] = native
+ def `match`(regexp: RegExp): Array[String] = native
+
+ /**
+ * Returns a new string with some or all matches of a pattern replaced by a
+ * replacement. The pattern can be a string or a RegExp, and the replacement
+ * can be a string or a function to be called for each match.
+ *
+ * This method does not change the String object it is called on. It simply
+ * returns a new string.
+ *
+ * To perform a global search and replace, either include the g switch in the
+ * regular expression or if the first parameter is a string, include g in the
+ * flags parameter.
+ *
+ * MDN
+ */
+ def replace(searchValue: String, replaceValue: String): String = native
+ def replace(searchValue: String, replaceValue: Any): String = native
+ def replace(searchValue: RegExp, replaceValue: String): String = native
+ def replace(searchValue: RegExp, replaceValue: Any): String = native
+
+ /**
+ * If successful, search returns the index of the regular expression inside
+ * the string. Otherwise, it returns -1.
+ *
+ * When you want to know whether a pattern is found in a string use search
+ * (similar to the regular expression test method); for more information
+ * (but slower execution) use match (similar to the regular expression exec
+ * method).
+ *
+ * MDN
+ */
+ def search(regexp: String): Number = native
+ def search(regexp: RegExp): Number = native
+
+ /**
+ * slice extracts the text from one string and returns a new string. Changes
+ * to the text in one string do not affect the other string.
+ *
+ * slice extracts up to but not including endSlice. string.slice(1,4) extracts
+ * the second character through the fourth character (characters indexed 1, 2,
+ * and 3).
+ *
+ * As an example, string.slice(2,-1) extracts the third character through the
+ * second to last character in the string.
+ *
+ * MDN
+ */
+ def slice(start: Number, end: Number): String = native
+ def slice(start: Number): String = native
+
+ /**
+ * Splits a String object into an array of strings by separating the string
+ * into substrings.
+ *
+ * When found, separator is removed from the string and the substrings are
+ * returned in an array. If separator is omitted, the array contains one
+ * element consisting of the entire string. If separator is an empty string,
+ * string is converted to an array of characters.
+ *
+ * If separator is a regular expression that contains capturing parentheses,
+ * then each time separator is matched, the results (including any undefined
+ * results) of the capturing parentheses are spliced into the output array.
+ * However, not all browsers support this capability.
+ *
+ * Note: When the string is empty, split returns an array containing one
+ * empty string, rather than an empty array.
+ *
+ * MDN
+ */
+ def split(separator: String, limit: Number): Array[String] = native
+ def split(separator: String): Array[String] = native
+ def split(separator: RegExp, limit: Number): Array[String] = native
+ def split(separator: RegExp): Array[String] = native
+
+ /**
+ * Returns a subset of a string between one index and another, or through
+ * the end of the string.
+ *
+ * MDN
+ */
+ def substring(start: Number, end: Number): String = native
+ def substring(start: Number): String = native
+
+ /**
+ * Returns the calling string value converted to lowercase.
+ *
+ * MDN
+ */
+ def toLowerCase(): String = native
+
+ /**
+ * The toLocaleLowerCase method returns the value of the string converted to
+ * lower case according to any locale-specific case mappings. toLocaleLowerCase
+ * does not affect the value of the string itself. In most cases, this will
+ * produce the same result as toLowerCase(), but for some locales, such as
+ * Turkish, whose case mappings do not follow the default case mappings in Unicode,
+ * there may be a different result.
+ *
+ * MDN
+ */
+ def toLocaleLowerCase(): String = native
+
+ /**
+ * Returns the calling string value converted to uppercase.
+ *
+ * MDN
+ */
+ def toUpperCase(): String = native
+
+ /**
+ * The toLocaleUpperCase method returns the value of the string converted to
+ * upper case according to any locale-specific case mappings. toLocaleUpperCase
+ * does not affect the value of the string itself. In most cases, this will
+ * produce the same result as toUpperCase(), but for some locales, such as
+ * Turkish, whose case mappings do not follow the default case mappings in Unicode,
+ * there may be a different result.
+ *
+ * MDN
+ */
+ def toLocaleUpperCase(): String = native
+
+ /**
+ * Removes whitespace from both ends of the string.
+ *
+ * MDN
+ */
+ def trim(): String = native
+}
+
+/** The top-level `String` JavaScript object. */
+object String extends Object {
+ def fromCharCode(codes: Int*): java.lang.String = native
+}
+
+/** Primitive JavaScript undefined value.
+ *
+ * In most situations, you should not need this trait, and use
+ * [[scala.Unit]] instead.
+ */
+sealed trait Undefined extends Any
+
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/PropertyDescriptor.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/PropertyDescriptor.scala
new file mode 100644
index 0000000..b91e01d
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/PropertyDescriptor.scala
@@ -0,0 +1,20 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+
+package scala.scalajs.js
+
+trait PropertyDescriptor extends Object {
+ var configurable: Boolean = native
+ var enumerable: Boolean = native
+ var value: Any = native
+ var writable: Boolean = native
+ var get: Function0[Any] = native
+ var set: Function1[Any, Any] = native
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/RegExp.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/RegExp.scala
new file mode 100644
index 0000000..73f465a
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/RegExp.scala
@@ -0,0 +1,108 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+/**
+ * All doc-comments marked as "MDN" are by Mozilla Contributors,
+ * distributed under the Creative Commons Attribution-ShareAlike license from
+ * https://developer.mozilla.org/en-US/docs/Web/Reference/API
+ */
+package scala.scalajs.js
+
+/**
+ * The RegExp constructor creates a regular expression object for matching
+ * text with a pattern.
+ *
+ * MDN
+ */
+class RegExp(pattern: String, flags: String = "") extends Object {
+ /**
+ * The source property returns a String containing the text of the pattern,
+ * excluding the forward slashes. It is a read-only property of an individual
+ * regular expression instance. source does not contain any flags (like "g",
+ * "i" or "m") of the regular expression.
+ *
+ * MDN
+ */
+ val source: String = native
+ /**
+ * The value of global is a Boolean and true if the "g" flag was used;
+ * otherwise, false. The "g" flag indicates that the regular expression
+ * should be tested against all possible matches in a string.
+ *
+ * MDN
+ */
+ val global: Boolean = native
+ /**
+ * The value of ignoreCase is a Boolean and true if the "i" flag was used;
+ * otherwise, false. The "i" flag indicates that case should be ignored while
+ * attempting a match in a string.
+ *
+ * MDN
+ */
+ val ignoreCase: Boolean = native
+ /**
+ * The value of multiline is a Boolean and is true if the "m" flag was used;
+ * otherwise, false. The "m" flag indicates that a multiline input string
+ * should be treated as multiple lines. For example, if "m" is used, "^" and
+ * "$" change from matching at only the start or end of the entire string to
+ * the start or end of any line within the string.
+
+ * MDN
+ */
+ val multiline: Boolean = native
+
+ /**
+ * The lastIndex is a read/write integer property of regular expressions that
+ * specifies the index at which to start the next match.
+ *
+ * MDN
+ */
+ var lastIndex: Int = native
+
+ /**
+ * The exec() method executes a search for a match in a specified string.
+ * Returns a result array, or null.
+ *
+ * If you are executing a match simply to find true or false, use the
+ * RegExp.test() method or the String.search() method.
+ *
+ * If the match succeeds, the exec method returns an array and updates properties
+ * of the regular expression object. The returned array has the matched text
+ * as the first item, and then one item for each capturing parenthesis that
+ * matched containing the text that was captured.
+ *
+ * If the match fails, the exec method returns null.
+ *
+ * MDN
+ */
+ def exec(string: String): RegExp.ExecResult = native
+
+ /**
+ * The test() method executes a search for a match between a regular expression
+ * and a specified string. Returns true or false.
+ *
+ * You can use test() whenever want to know whether a pattern is found in a
+ * string (similar to the String.search method); for more information (but
+ * slower execution) use the exec method (similar to the String.match method).
+ * As with exec (or in combination with it), test called multiple times on the
+ * same global regular expression instance will advance past the previous match.
+ *
+ * MDN
+ */
+ def test(string: String): Boolean = native
+}
+
+object RegExp extends Object {
+ def apply(pattern: String, flags: String = ""): RegExp = native
+
+ trait ExecResult extends Array[UndefOr[String]] {
+ var index: Int = native
+ var input: String = native
+ }
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/ThisFunction.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/ThisFunction.scala
new file mode 100644
index 0000000..2506eed
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/ThisFunction.scala
@@ -0,0 +1,160 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+/**
+ * All doc-comments marked as "MDN" are by Mozilla Contributors,
+ * distributed under the Creative Commons Attribution-ShareAlike license from
+ * https://developer.mozilla.org/en-US/docs/Web/Reference/API
+ */
+package scala.scalajs.js
+
+import scala.language.implicitConversions
+
+/** A JavaScript function where `this` is considered as a first parameter.
+ *
+ * @see [[http://www.scala-js.org/doc/calling-javascript.html Calling JavaScript from Scala.js]]
+ */
+trait ThisFunction extends Function {
+}
+
+object ThisFunction {
+ implicit def fromFunction1[T1, R](f: scala.Function1[T1, R]): ThisFunction0[T1, R] = sys.error("stub")
+ implicit def fromFunction2[T1, T2, R](f: scala.Function2[T1, T2, R]): ThisFunction1[T1, T2, R] = sys.error("stub")
+ implicit def fromFunction3[T1, T2, T3, R](f: scala.Function3[T1, T2, T3, R]): ThisFunction2[T1, T2, T3, R] = sys.error("stub")
+ implicit def fromFunction4[T1, T2, T3, T4, R](f: scala.Function4[T1, T2, T3, T4, R]): ThisFunction3[T1, T2, T3, T4, R] = sys.error("stub")
+ implicit def fromFunction5[T1, T2, T3, T4, T5, R](f: scala.Function5[T1, T2, T3, T4, T5, R]): ThisFunction4[T1, T2, T3, T4, T5, R] = sys.error("stub")
+ implicit def fromFunction6[T1, T2, T3, T4, T5, T6, R](f: scala.Function6[T1, T2, T3, T4, T5, T6, R]): ThisFunction5[T1, T2, T3, T4, T5, T6, R] = sys.error("stub")
+ implicit def fromFunction7[T1, T2, T3, T4, T5, T6, T7, R](f: scala.Function7[T1, T2, T3, T4, T5, T6, T7, R]): ThisFunction6[T1, T2, T3, T4, T5, T6, T7, R] = sys.error("stub")
+ implicit def fromFunction8[T1, T2, T3, T4, T5, T6, T7, T8, R](f: scala.Function8[T1, T2, T3, T4, T5, T6, T7, T8, R]): ThisFunction7[T1, T2, T3, T4, T5, T6, T7, T8, R] = sys.error("stub")
+ implicit def fromFunction9[T1, T2, T3, T4, T5, T6, T7, T8, T9, R](f: scala.Function9[T1, T2, T3, T4, T5, T6, T7, T8, T9, R]): ThisFunction8[T1, T2, T3, T4, T5, T6, T7, T8, T9, R] = sys.error("stub")
+ implicit def fromFunction10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R](f: scala.Function10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R]): ThisFunction9[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R] = sys.error("stub")
+ implicit def fromFunction11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, R](f: scala.Function11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, R]): ThisFunction10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, R] = sys.error("stub")
+ implicit def fromFunction12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, R](f: scala.Function12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, R]): ThisFunction11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, R] = sys.error("stub")
+ implicit def fromFunction13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, R](f: scala.Function13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, R]): ThisFunction12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, R] = sys.error("stub")
+ implicit def fromFunction14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, R](f: scala.Function14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, R]): ThisFunction13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, R] = sys.error("stub")
+ implicit def fromFunction15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, R](f: scala.Function15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, R]): ThisFunction14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, R] = sys.error("stub")
+ implicit def fromFunction16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, R](f: scala.Function16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, R]): ThisFunction15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, R] = sys.error("stub")
+ implicit def fromFunction17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, R](f: scala.Function17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, R]): ThisFunction16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, R] = sys.error("stub")
+ implicit def fromFunction18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, R](f: scala.Function18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, R]): ThisFunction17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, R] = sys.error("stub")
+ implicit def fromFunction19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, R](f: scala.Function19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, R]): ThisFunction18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, R] = sys.error("stub")
+ implicit def fromFunction20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, R](f: scala.Function20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, R]): ThisFunction19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, R] = sys.error("stub")
+ implicit def fromFunction21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, R](f: scala.Function21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, R]): ThisFunction20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, R] = sys.error("stub")
+ implicit def fromFunction22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, R](f: scala.Function22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, R]): ThisFunction21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, R] = sys.error("stub")
+
+ implicit def toFunction1[T1, R](f: ThisFunction0[T1, R]): scala.Function1[T1, R] = (x1) => f(x1)
+ implicit def toFunction2[T1, T2, R](f: ThisFunction1[T1, T2, R]): scala.Function2[T1, T2, R] = (x1, x2) => f(x1, x2)
+ implicit def toFunction3[T1, T2, T3, R](f: ThisFunction2[T1, T2, T3, R]): scala.Function3[T1, T2, T3, R] = (x1, x2, x3) => f(x1, x2, x3)
+ implicit def toFunction4[T1, T2, T3, T4, R](f: ThisFunction3[T1, T2, T3, T4, R]): scala.Function4[T1, T2, T3, T4, R] = (x1, x2, x3, x4) => f(x1, x2, x3, x4)
+ implicit def toFunction5[T1, T2, T3, T4, T5, R](f: ThisFunction4[T1, T2, T3, T4, T5, R]): scala.Function5[T1, T2, T3, T4, T5, R] = (x1, x2, x3, x4, x5) => f(x1, x2, x3, x4, x5)
+ implicit def toFunction6[T1, T2, T3, T4, T5, T6, R](f: ThisFunction5[T1, T2, T3, T4, T5, T6, R]): scala.Function6[T1, T2, T3, T4, T5, T6, R] = (x1, x2, x3, x4, x5, x6) => f(x1, x2, x3, x4, x5, x6)
+ implicit def toFunction7[T1, T2, T3, T4, T5, T6, T7, R](f: ThisFunction6[T1, T2, T3, T4, T5, T6, T7, R]): scala.Function7[T1, T2, T3, T4, T5, T6, T7, R] = (x1, x2, x3, x4, x5, x6, x7) => f(x1, x2, x3, x4, x5, x6, x7)
+ implicit def toFunction8[T1, T2, T3, T4, T5, T6, T7, T8, R](f: ThisFunction7[T1, T2, T3, T4, T5, T6, T7, T8, R]): scala.Function8[T1, T2, T3, T4, T5, T6, T7, T8, R] = (x1, x2, x3, x4, x5, x6, x7, x8) => f(x1, x2, x3, x4, x5, x6, x7, x8)
+ implicit def toFunction9[T1, T2, T3, T4, T5, T6, T7, T8, T9, R](f: ThisFunction8[T1, T2, T3, T4, T5, T6, T7, T8, T9, R]): scala.Function9[T1, T2, T3, T4, T5, T6, T7, T8, T9, R] = (x1, x2, x3, x4, x5, x6, x7, x8, x9) => f(x1, x2, x3, x4, x5, x6, x7, x8, x9)
+ implicit def toFunction10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R](f: ThisFunction9[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R]): scala.Function10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R] = (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10) => f(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)
+ implicit def toFunction11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, R](f: ThisFunction10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, R]): scala.Function11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, R] = (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11) => f(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11)
+ implicit def toFunction12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, R](f: ThisFunction11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, R]): scala.Function12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, R] = (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12) => f(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12)
+ implicit def toFunction13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, R](f: ThisFunction12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, R]): scala.Function13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, R] = (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13) => f(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13)
+ implicit def toFunction14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, R](f: ThisFunction13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, R]): scala.Function14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, R] = (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14) => f(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14)
+ implicit def toFunction15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, R](f: ThisFunction14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, R]): scala.Function15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, R] = (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15) => f(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15)
+ implicit def toFunction16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, R](f: ThisFunction15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, R]): scala.Function16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, R] = (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16) => f(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16)
+ implicit def toFunction17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, R](f: ThisFunction16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, R]): scala.Function17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, R] = (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17) => f(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17)
+ implicit def toFunction18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, R](f: ThisFunction17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, R]): scala.Function18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, R] = (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18) => f(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18)
+ implicit def toFunction19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, R](f: ThisFunction18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, R]): scala.Function19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, R] = (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19) => f(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19)
+ implicit def toFunction20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, R](f: ThisFunction19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, R]): scala.Function20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, R] = (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20) => f(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20)
+ implicit def toFunction21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, R](f: ThisFunction20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, R]): scala.Function21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, R] = (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21) => f(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21)
+ implicit def toFunction22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, R](f: ThisFunction21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, R]): scala.Function22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, R] = (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22) => f(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22)
+}
+
+trait ThisFunction0[-T0, +R] extends ThisFunction {
+ def apply(thisArg: T0): R
+}
+
+trait ThisFunction1[-T0, -T1, +R] extends ThisFunction {
+ def apply(thisArg: T0, arg1: T1): R
+}
+
+trait ThisFunction2[-T0, -T1, -T2, +R] extends ThisFunction {
+ def apply(thisArg: T0, arg1: T1, arg2: T2): R
+}
+
+trait ThisFunction3[-T0, -T1, -T2, -T3, +R] extends ThisFunction {
+ def apply(thisArg: T0, arg1: T1, arg2: T2, arg3: T3): R
+}
+
+trait ThisFunction4[-T0, -T1, -T2, -T3, -T4, +R] extends ThisFunction {
+ def apply(thisArg: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4): R
+}
+
+trait ThisFunction5[-T0, -T1, -T2, -T3, -T4, -T5, +R] extends ThisFunction {
+ def apply(thisArg: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5): R
+}
+
+trait ThisFunction6[-T0, -T1, -T2, -T3, -T4, -T5, -T6, +R] extends ThisFunction {
+ def apply(thisArg: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6): R
+}
+
+trait ThisFunction7[-T0, -T1, -T2, -T3, -T4, -T5, -T6, -T7, +R] extends ThisFunction {
+ def apply(thisArg: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7): R
+}
+
+trait ThisFunction8[-T0, -T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, +R] extends ThisFunction {
+ def apply(thisArg: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8): R
+}
+
+trait ThisFunction9[-T0, -T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, +R] extends ThisFunction {
+ def apply(thisArg: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9): R
+}
+
+trait ThisFunction10[-T0, -T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, +R] extends ThisFunction {
+ def apply(thisArg: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10): R
+}
+
+trait ThisFunction11[-T0, -T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, +R] extends ThisFunction {
+ def apply(thisArg: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11): R
+}
+
+trait ThisFunction12[-T0, -T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, +R] extends ThisFunction {
+ def apply(thisArg: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12): R
+}
+
+trait ThisFunction13[-T0, -T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, +R] extends ThisFunction {
+ def apply(thisArg: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13): R
+}
+
+trait ThisFunction14[-T0, -T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, +R] extends ThisFunction {
+ def apply(thisArg: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14): R
+}
+
+trait ThisFunction15[-T0, -T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, +R] extends ThisFunction {
+ def apply(thisArg: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14, arg15: T15): R
+}
+
+trait ThisFunction16[-T0, -T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, +R] extends ThisFunction {
+ def apply(thisArg: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14, arg15: T15, arg16: T16): R
+}
+
+trait ThisFunction17[-T0, -T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, +R] extends ThisFunction {
+ def apply(thisArg: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14, arg15: T15, arg16: T16, arg17: T17): R
+}
+
+trait ThisFunction18[-T0, -T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, +R] extends ThisFunction {
+ def apply(thisArg: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14, arg15: T15, arg16: T16, arg17: T17, arg18: T18): R
+}
+
+trait ThisFunction19[-T0, -T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, +R] extends ThisFunction {
+ def apply(thisArg: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14, arg15: T15, arg16: T16, arg17: T17, arg18: T18, arg19: T19): R
+}
+
+trait ThisFunction20[-T0, -T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, -T20, +R] extends ThisFunction {
+ def apply(thisArg: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14, arg15: T15, arg16: T16, arg17: T17, arg18: T18, arg19: T19, arg20: T20): R
+}
+
+trait ThisFunction21[-T0, -T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, -T20, -T21, +R] extends ThisFunction {
+ def apply(thisArg: T0, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14, arg15: T15, arg16: T16, arg17: T17, arg18: T18, arg19: T19, arg20: T20, arg21: T21): R
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/UndefOr.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/UndefOr.scala
new file mode 100644
index 0000000..b356e3a
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/UndefOr.scala
@@ -0,0 +1,254 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.js
+
+import scala.language.implicitConversions
+
+/** Value of type A or the JS undefined value.
+ * In a type system with union types, this would really be
+ * `A | js.prim.Undefined`. Since Scala does not have union types, but this
+ * particular union is crucial to many interoperability scenarios, it is
+ * provided as this trait.
+ *
+ * An API similar to that of [[scala.Option]] is provided through the
+ * [[UndefOrOps]] implicit class, with the understanding that `undefined` is
+ * the None value.
+ */
+@scala.scalajs.js.annotation.RawJSType // Don't do this at home!
+sealed trait UndefOr[+A]
+
+object UndefOr {
+ implicit def any2undefOrA[A](value: A): UndefOr[A] =
+ value.asInstanceOf[UndefOr[A]]
+
+ implicit def undef2undefOr(value: prim.Undefined): UndefOr[Nothing] =
+ value.asInstanceOf[UndefOr[Nothing]]
+
+ implicit def undefOr2ops[A](value: UndefOr[A]): UndefOrOps[A] =
+ new UndefOrOps(value)
+
+ implicit def undefOr2jsAny[A](value: UndefOr[A])(implicit ev: A => Any): Any =
+ value.map(ev).asInstanceOf[Any]
+}
+
+final class UndefOrOps[A](val self: UndefOr[A]) extends AnyVal {
+ import UndefOrOps._
+
+ /** Returns true if the option is `undefined`, false otherwise.
+ */
+ @inline final def isEmpty: Boolean = isUndefined(self)
+
+ /** Returns true if the option is not `undefined`, false otherwise.
+ */
+ @inline final def isDefined: Boolean = !isEmpty
+
+ /** Returns the option's value.
+ * @note The option must be nonEmpty.
+ * @throws Predef.NoSuchElementException if the option is empty.
+ */
+ @inline final def get: A =
+ if (isEmpty) throw new NoSuchElementException("undefined.get")
+ else self.asInstanceOf[A]
+
+ @inline final private def forceGet: A = self.asInstanceOf[A]
+
+ /** Returns the option's value if the option is nonempty, otherwise
+ * return the result of evaluating `default`.
+ *
+ * @param default the default expression.
+ */
+ @inline final def getOrElse[B >: A](default: => B): B =
+ if (isEmpty) default else this.forceGet
+
+ /** Returns the option's value if it is nonempty,
+ * or `null` if it is empty.
+ * Although the use of null is discouraged, code written to use
+ * $option must often interface with code that expects and returns nulls.
+ * @example {{{
+ * val initalText: Option[String] = getInitialText
+ * val textField = new JComponent(initalText.orNull,20)
+ * }}}
+ */
+ @inline final def orNull[A1 >: A](implicit ev: Null <:< A1): A1 =
+ this getOrElse ev(null)
+
+ /** Returns a $some containing the result of applying $f to this $option's
+ * value if this $option is nonempty.
+ * Otherwise return $none.
+ *
+ * @note This is similar to `flatMap` except here,
+ * $f does not need to wrap its result in an $option.
+ *
+ * @param f the function to apply
+ * @see flatMap
+ * @see foreach
+ */
+ @inline final def map[B](f: A => B): UndefOr[B] =
+ if (isEmpty) undefined else f(this.forceGet)
+
+ /** Returns the result of applying $f to this $option's
+ * value if the $option is nonempty. Otherwise, evaluates
+ * expression `ifEmpty`.
+ *
+ * @note This is equivalent to `$option map f getOrElse ifEmpty`.
+ *
+ * @param ifEmpty the expression to evaluate if empty.
+ * @param f the function to apply if nonempty.
+ */
+ @inline final def fold[B](ifEmpty: => B)(f: A => B): B =
+ if (isEmpty) ifEmpty else f(this.forceGet)
+
+ /** Returns the result of applying $f to this $option's value if
+ * this $option is nonempty.
+ * Returns $none if this $option is empty.
+ * Slightly different from `map` in that $f is expected to
+ * return an $option (which could be $none).
+ *
+ * @param f the function to apply
+ * @see map
+ * @see foreach
+ */
+ @inline final def flatMap[B](f: A => UndefOr[B]): UndefOr[B] =
+ if (isEmpty) undefined else f(this.forceGet)
+
+ def flatten[B](implicit ev: A <:< UndefOr[B]): UndefOr[B] =
+ if (isEmpty) undefined else ev(this.forceGet)
+
+ /** Returns this $option if it is nonempty '''and''' applying the predicate $p to
+ * this $option's value returns true. Otherwise, return $none.
+ *
+ * @param p the predicate used for testing.
+ */
+ @inline final def filter(p: A => Boolean): UndefOr[A] =
+ if (isEmpty || p(this.forceGet)) self else undefined
+
+ /** Returns this $option if it is nonempty '''and''' applying the predicate $p to
+ * this $option's value returns false. Otherwise, return $none.
+ *
+ * @param p the predicate used for testing.
+ */
+ @inline final def filterNot(p: A => Boolean): UndefOr[A] =
+ if (isEmpty || !p(this.forceGet)) self else undefined
+
+ /** Returns false if the option is $none, true otherwise.
+ * @note Implemented here to avoid the implicit conversion to Iterable.
+ */
+ final def nonEmpty = isDefined
+
+ /** Necessary to keep $option from being implicitly converted to
+ * [[scala.collection.Iterable]] in `for` comprehensions.
+ */
+ @inline final def withFilter(p: A => Boolean): WithFilter[A] =
+ new WithFilter(self, p)
+
+ /** Returns true if this option is nonempty '''and''' the predicate
+ * $p returns true when applied to this $option's value.
+ * Otherwise, returns false.
+ *
+ * @param p the predicate to test
+ */
+ @inline final def exists(p: A => Boolean): Boolean =
+ !isEmpty && p(this.forceGet)
+
+ /** Returns true if this option is empty '''or''' the predicate
+ * $p returns true when applied to this $option's value.
+ *
+ * @param p the predicate to test
+ */
+ @inline final def forall(p: A => Boolean): Boolean =
+ isEmpty || p(this.forceGet)
+
+ /** Apply the given procedure $f to the option's value,
+ * if it is nonempty. Otherwise, do nothing.
+ *
+ * @param f the procedure to apply.
+ * @see map
+ * @see flatMap
+ */
+ @inline final def foreach[U](f: A => U): Unit =
+ if (!isEmpty) f(this.forceGet)
+
+ /** Returns a $some containing the result of
+ * applying `pf` to this $option's contained
+ * value, '''if''' this option is
+ * nonempty '''and''' `pf` is defined for that value.
+ * Returns $none otherwise.
+ *
+ * @param pf the partial function.
+ * @return the result of applying `pf` to this $option's
+ * value (if possible), or $none.
+ */
+ @inline final def collect[B](pf: PartialFunction[A, B]): UndefOr[B] =
+ if (isEmpty) undefined
+ else pf.applyOrElse(this.forceGet, (_: A) => undefined).asInstanceOf[UndefOr[B]]
+
+ /** Returns this $option if it is nonempty,
+ * otherwise return the result of evaluating `alternative`.
+ * @param alternative the alternative expression.
+ */
+ @inline final def orElse[B >: A](alternative: => UndefOr[B]): UndefOr[B] =
+ if (isEmpty) alternative else self
+
+ /** Returns a singleton iterator returning the $option's value
+ * if it is nonempty, or an empty iterator if the option is empty.
+ */
+ def iterator: Iterator[A] =
+ if (isEmpty) scala.collection.Iterator.empty
+ else scala.collection.Iterator.single(this.forceGet)
+
+ /** Returns a singleton list containing the $option's value
+ * if it is nonempty, or the empty list if the $option is empty.
+ */
+ def toList: List[A] =
+ if (isEmpty) Nil else this.forceGet :: Nil
+
+ /** Returns a [[scala.util.Left]] containing the given
+ * argument `left` if this $option is empty, or
+ * a [[scala.util.Right]] containing this $option's value if
+ * this is nonempty.
+ *
+ * @param left the expression to evaluate and return if this is empty
+ * @see toLeft
+ */
+ @inline final def toRight[X](left: => X): Either[X, A] =
+ if (isEmpty) Left(left) else Right(this.forceGet)
+
+ /** Returns a [[scala.util.Right]] containing the given
+ * argument `right` if this is empty, or
+ * a [[scala.util.Left]] containing this $option's value
+ * if this $option is nonempty.
+ *
+ * @param right the expression to evaluate and return if this is empty
+ * @see toRight
+ */
+ @inline final def toLeft[X](right: => X): Either[A, X] =
+ if (isEmpty) Right(right) else Left(this.forceGet)
+
+ /** Returns a [[scala.Some]] containing this $options's value
+ * if this $option is nonempty, [[scala.None]] otherwise.
+ */
+ @inline final def toOption: Option[A] =
+ if (isEmpty) None else Some(this.forceGet)
+}
+
+object UndefOrOps {
+
+ /** We need a whole WithFilter class to honor the "doesn't create a new
+ * collection" contract even though it seems unlikely to matter much in a
+ * collection with max size 1.
+ */
+ class WithFilter[A](self: UndefOr[A], p: A => Boolean) {
+ def map[B](f: A => B): UndefOr[B] = self filter p map f
+ def flatMap[B](f: A => UndefOr[B]): UndefOr[B] = self filter p flatMap f
+ def foreach[U](f: A => U): Unit = self filter p foreach f
+ def withFilter(q: A => Boolean): WithFilter[A] =
+ new WithFilter[A](self, x => p(x) && q(x))
+ }
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/WrappedArray.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/WrappedArray.scala
new file mode 100644
index 0000000..3626c15
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/WrappedArray.scala
@@ -0,0 +1,92 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+package scala.scalajs.js
+
+import scala.language.implicitConversions
+
+import scala.collection.mutable
+import mutable.Builder
+
+import scala.collection.generic.{CanBuildFrom, GenericCompanion, SeqFactory}
+
+/** Equivalent of scm.WrappedArray for js.Array */
+@inline
+final class WrappedArray[A](val array: Array[A])
+ extends mutable.AbstractBuffer[A]
+ with scala.collection.generic.GenericTraversableTemplate[A, WrappedArray]
+ with mutable.IndexedSeq[A]
+ with mutable.BufferLike[A, WrappedArray[A]]
+ with mutable.ArrayLike[A, WrappedArray[A]]
+ with Builder[A, WrappedArray[A]] {
+
+ /** Creates a new empty [[WrappedArray]]. */
+ def this() = this(Array())
+
+ override def companion: GenericCompanion[WrappedArray] = WrappedArray
+
+ // IndexedSeq interface
+
+ @inline def update(index: Int, elem: A): Unit = array(index) = elem
+ @inline def apply(index: Int): A = array(index)
+ @inline def length: Int = array.length
+
+ // Builder interface
+
+ @inline def +=(elem: A): this.type = {
+ array.push(elem)
+ this
+ }
+
+ @inline def clear(): Unit =
+ array.length = 0
+
+ @inline def result(): WrappedArray[A] = this
+
+ // Rest of BufferLike interface
+
+ @inline def +=:(elem: A): this.type = {
+ array.unshift(elem)
+ this
+ }
+
+ @inline override def ++=:(xs: TraversableOnce[A]): this.type = {
+ array.unshift(xs.toSeq: _*)
+ this
+ }
+
+ @inline def insertAll(n: Int,
+ elems: scala.collection.Traversable[A]): Unit = {
+ array.splice(n, 0, elems.toSeq: _*)
+ }
+
+ @inline def remove(n: Int): A =
+ array.splice(n, 1)(0)
+
+ @inline override def remove(n: Int, count: Int): Unit =
+ array.splice(n, count)
+
+ @inline override def stringPrefix: String = "WrappedArray"
+
+}
+
+/** $factoryInfo
+ * @define coll wrapped array
+ * @define Coll `WrappedArray`
+ */
+object WrappedArray extends SeqFactory[WrappedArray] {
+ /** $genericCanBuildFromInfo */
+ implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, WrappedArray[A]] =
+ ReusableCBF.asInstanceOf[GenericCanBuildFrom[A]]
+
+ def newBuilder[A]: Builder[A, WrappedArray[A]] = new WrappedArray[A]
+
+ implicit def toJSArray[A](wrappedArray: WrappedArray[A]): Array[A] =
+ wrappedArray.array
+
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/WrappedDictionary.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/WrappedDictionary.scala
new file mode 100644
index 0000000..f215e6e
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/WrappedDictionary.scala
@@ -0,0 +1,89 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+package scala.scalajs.js
+
+import scala.collection.mutable
+import mutable.Builder
+
+import scala.collection.generic.CanBuildFrom
+
+/** Wrapper to use a js.Dictionary as a scala.mutable.Map */
+@inline
+class WrappedDictionary[A](val dict: Dictionary[A])
+ extends mutable.AbstractMap[String, A]
+ with mutable.Map[String, A]
+ with mutable.MapLike[String, A, WrappedDictionary[A]] {
+
+ def get(key: String): Option[A] = {
+ if (contains(key))
+ Some(dict(key))
+ else
+ None
+ }
+
+ override def contains(key: String): Boolean =
+ dict.hasOwnProperty(key)
+
+ def -=(key: String): this.type = {
+ dict.delete(key)
+ this
+ }
+
+ def +=(kv: (String, A)): this.type = {
+ dict(kv._1) = kv._2
+ this
+ }
+
+ def iterator: Iterator[(String, A)] = new Iterator[(String, A)] {
+ private[this] val keys = Object.keys(dict)
+ private[this] var index: Int = 0
+ def hasNext(): Boolean = index < keys.length
+ def next(): (String, A) = {
+ val key = keys(index)
+ index += 1
+ (key, dict(key))
+ }
+ }
+
+ override def keys: Iterable[String] =
+ Object.keys(dict)
+
+ override def empty: WrappedDictionary[A] =
+ new WrappedDictionary(Dictionary.empty)
+
+}
+
+object WrappedDictionary {
+ // Note: We can't extend MutableMapFactory[WrappedDictionary] since
+ // it requires support for any type of key
+
+ def empty[A]: WrappedDictionary[A] = new WrappedDictionary(Dictionary.empty)
+
+ type CBF[A] = CanBuildFrom[WrappedDictionary[_], (String, A), WrappedDictionary[A]]
+ implicit def canBuildFrom[A]: CBF[A] = new CBF[A] {
+ def apply(from: WrappedDictionary[_]): Builder[(String, A), WrappedDictionary[A]] =
+ new WrappedDictionaryBuilder[A]
+ def apply(): Builder[(String, A), WrappedDictionary[A]] =
+ new WrappedDictionaryBuilder[A]
+ }
+
+ class WrappedDictionaryBuilder[A]
+ extends Builder[(String, A), WrappedDictionary[A]] {
+ private[this] var dict: Dictionary[A] = Dictionary.empty
+ def +=(elem: (String, A)): this.type = {
+ dict(elem._1) = elem._2
+ this
+ }
+ def clear(): Unit =
+ dict = Dictionary.empty
+ def result(): WrappedDictionary[A] =
+ new WrappedDictionary(dict)
+ }
+
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/JSBracketAccess.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/JSBracketAccess.scala
new file mode 100644
index 0000000..596e327
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/JSBracketAccess.scala
@@ -0,0 +1,17 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+
+
+package scala.scalajs.js.annotation
+
+/** Marks the annotated method as representing bracket access in JavaScript.
+ *
+ * @see [[http://www.scala-js.org/doc/calling-javascript.html Calling JavaScript from Scala.js]]
+ */
+class JSBracketAccess extends scala.annotation.StaticAnnotation
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/JSExport.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/JSExport.scala
new file mode 100644
index 0000000..0fa9a4e
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/JSExport.scala
@@ -0,0 +1,19 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+
+
+package scala.scalajs.js.annotation
+
+/** Specifies that the given entity should be exported for use in raw JS.
+ *
+ * @see [[http://www.scala-js.org/doc/export-to-javascript.html Export Scala.js APIs to JavaScript]]
+ */
+class JSExport extends scala.annotation.StaticAnnotation {
+ def this(name: String) = this()
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/JSExportAll.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/JSExportAll.scala
new file mode 100644
index 0000000..8174595
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/JSExportAll.scala
@@ -0,0 +1,21 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+
+
+package scala.scalajs.js.annotation
+
+/** Exports all public 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.
+ *
+ * @see [[http://www.scala-js.org/doc/export-to-javascript.html Export Scala.js APIs to JavaScript]]
+ */
+class JSExportAll extends scala.annotation.StaticAnnotation
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/JSExportDescendentClasses.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/JSExportDescendentClasses.scala
new file mode 100644
index 0000000..9f2be96
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/JSExportDescendentClasses.scala
@@ -0,0 +1,20 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+
+package scala.scalajs.js.annotation
+
+/** Specifies that all the concrete classes extending the annotated class or
+ * should have all their public constructors exported for use in raw JS.
+ * The constructors exported this way are exported under their fully
+ * qualified name.
+ *
+ * @see [[http://www.scala-js.org/doc/export-to-javascript.html Export Scala.js APIs to JavaScript]]
+ */
+class JSExportDescendentClasses extends scala.annotation.StaticAnnotation
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/JSExportDescendentObjects.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/JSExportDescendentObjects.scala
new file mode 100644
index 0000000..c196b53
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/JSExportDescendentObjects.scala
@@ -0,0 +1,20 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+
+package scala.scalajs.js.annotation
+
+/** Specifies that all the objects extending the annotated class or trait
+ * should be exported for use in raw JS.
+ * Note that objects exported this way are exported under their fully
+ * qualified name.
+ *
+ * @see [[http://www.scala-js.org/doc/export-to-javascript.html Export Scala.js APIs to JavaScript]]
+ */
+class JSExportDescendentObjects extends scala.annotation.StaticAnnotation
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/JSExportNamed.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/JSExportNamed.scala
new file mode 100644
index 0000000..718404a
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/JSExportNamed.scala
@@ -0,0 +1,38 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+
+
+package scala.scalajs.js.annotation
+
+/** Exports the given method to JavaScript with named parameters.
+ *
+ * It can then be called like this:
+ * {{{
+ * obj.foo({
+ * param1: value1
+ * param2: value2
+ * param7: value3
+ * });
+ * }}}
+ *
+ * Note that named exports don't support overloading. Therefore the
+ * following will fail:
+ * {{{
+ * class A {
+ * @JSExportNamed
+ * def a(foo: Int) = foo + 1
+ * @JSExportNamed
+ * def a(bar: String) = "Hello " + bar
+ * }
+ * }}}
+ * @see [[http://www.scala-js.org/doc/export-to-javascript.html Export Scala.js APIs to JavaScript]]
+ */
+class JSExportNamed extends scala.annotation.StaticAnnotation {
+ def this(name: String) = this()
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/JSName.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/JSName.scala
new file mode 100644
index 0000000..5401749
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/JSName.scala
@@ -0,0 +1,17 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+
+
+package scala.scalajs.js.annotation
+
+/** Specifies the JavaScript name of an entity.
+ *
+ * @see [[http://www.scala-js.org/doc/calling-javascript.html Calling JavaScript from Scala.js]]
+ */
+class JSName(name: String) extends scala.annotation.StaticAnnotation
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/README.md b/examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/README.md
new file mode 100644
index 0000000..9ce7ebf
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/README.md
@@ -0,0 +1,3 @@
+**Attention**: Some files in here are also published in the Scala.js stubs JVM library (see the stubs project in the Scala.js build).
+
+If you add (or rename) a file, make sure the files in the stubs project are up to date.
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/RawJSType.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/RawJSType.scala
new file mode 100644
index 0000000..a5bb771
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/annotation/RawJSType.scala
@@ -0,0 +1,23 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+
+
+package scala.scalajs.js.annotation
+
+/** Marks the annotated class, trait or object as a raw JavaScript type.
+ *
+ * This annotation is added automatically by the compiler to all classes,
+ * traits and objects inheriting directly or indirectly from
+ * [[scala.scalajs.js.Any]]. It marks the annotated entity as being a raw
+ * JavaScript type, i.e., one that represents type information for an entity
+ * defined in JavaScript code.
+ *
+ * Do not use this annotation yourself.
+ */
+class RawJSType extends scala.annotation.StaticAnnotation
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/package.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/package.scala
new file mode 100644
index 0000000..4a17ba6
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/package.scala
@@ -0,0 +1,161 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+
+package scala.scalajs
+
+/** Contains primitive types for interoperability with JavaScript libraries.
+ * This package is only relevant to the Scala.js compiler, and should not be
+ * referenced by any project compiled to the JVM.
+ *
+ * All the values and methods in this package object are representatives of
+ * standard variables and functions available in the top-level scope, as
+ * standardized in ECMAScript 5.1.
+ *
+ * == Guide ==
+ *
+ * General documentation on Scala.js is available at
+ * [[http://www.scala-js.org/doc/]].
+ *
+ * == Overview ==
+ *
+ * The trait [[js.Any]] is the super type of all JavaScript values.
+ *
+ * All class, trait and object definitions that inherit, directly or
+ * indirectly, from [[js.Any]] do not have actual implementations in Scala.
+ * They are only the manifestation of static types representing libraries
+ * written directly in JavaScript. It is not possible to implement yourself
+ * a subclass of [[js.Any]]: all the method definitions will be ignored when
+ * compiling to JavaScript.
+ *
+ * Implicit conversions to and from standard Scala types to their equivalent
+ * in JavaScript are provided. For example, from Scala arrays to JavaScript
+ * arrays and back.
+ *
+ * The most important subclasses of [[js.Any]] are:
+ * - [[js.Dynamic]], a dynamically typed interface to JavaScript APIs
+ * - [[js.Object]], the superclass of all statically typed JavaScript classes,
+ * which has subclasses for all the classes standardized in ECMAScript 5.1,
+ * among which:
+ * - [[js.Array]]
+ * - [[js.Function]] (and subtraits with specific number of parameters)
+ * - [[js.ThisFunction]] and its subtraits for functions that take the
+ * JavaScript `this` as an explicit parameters
+ * - [[js.Dictionary]] to access the properties of an object in a
+ * dictionary-like way
+ * - [[js.Date]]
+ * - [[js.RegExp]]
+ *
+ * The trait [[js.Dynamic]] is a special subtrait of [[js.Any]]. It can
+ * represent any JavaScript value in a dynamically-typed way. It is possible
+ * to call any method and read and write any field of a value of type
+ * [[js.Dynamic]].
+ *
+ * The package [[scala.scalajs.js.prim]] gives definitions for the four
+ * primitive types of JavaScript as subtraits of [[js.Any]], but generally
+ * it is preferable to use the corresponding Scala type.
+ * - [[js.prim.Number]] corresponds to [[scala.Double]]
+ * - [[js.prim.Boolean]] corresponds to [[scala.Boolean]]
+ * - [[js.prim.String]] corresponds to [[java.lang.String]]
+ * - [[js.prim.Undefined]] corresponds to [[scala.Unit]]
+ *
+ * [[js.UndefOr]] gives a [[scala.Option]]-like interface where the JavaScript
+ * value `undefined` takes the role of `None`.
+ */
+package object js extends js.GlobalScope {
+ /** The type of JavaScript numbers, which is [[scala.Double]]. */
+ type Number = scala.Double
+ /** The type of JavaScript booleans, which is [[scala.Boolean]]. */
+ type Boolean = scala.Boolean
+ /** The type of JavaScript strings, which is [[java.lang.String]]. */
+ type String = java.lang.String
+ /** The type of the JavaScript undefined value, which is [[scala.Unit]]. */
+ type Undefined = scala.Unit
+
+ /** The top-level `Number` JavaScript object. */
+ val Number: js.prim.Number.type = native
+ /** The top-level `Boolean` JavaScript object. */
+ val Boolean: js.prim.Boolean.type = native
+ /** The top-level `String` JavaScript object. */
+ val String: js.prim.String.type = native
+
+ /** The constant Not-a-Number. */
+ val NaN: Double = native
+ /** The constant Positive Infinity. */
+ val Infinity: Double = native
+
+ /** The undefined value. */
+ def undefined: js.prim.Undefined = sys.error("stub")
+
+ /** Tests whether the given value is undefined. */
+ def isUndefined(v: scala.Any): Boolean = sys.error("stub")
+
+ /** Returns the type of `x` as identified by `typeof x` in JavaScript. */
+ def typeOf(x: Any): String = sys.error("stub")
+
+ /** Invokes any available debugging functionality.
+ * If no debugging functionality is available, this statement has no effect.
+ *
+ * MDN
+ *
+ * Browser support:
+ * - Has no effect in Rhino nor, apparently, in Firefox
+ * - In Chrome, it has no effect unless the developer tools are opened
+ * beforehand.
+ */
+ def debugger(): Unit = sys.error("stub")
+
+ /** Evaluates JavaScript code and returns the result. */
+ def eval(x: String): Any = native
+
+ /** Parses a string as an integer with a given radix. */
+ def parseInt(s: String, radix: Int): js.Number = native
+ /** Parses a string as an integer with auto-detected radix. */
+ def parseInt(s: String): js.Number = native
+ /** Parses a string as a floating point number. */
+ def parseFloat(string: String): Double = native
+
+ /** Tests whether the given value is Not-a-Number. */
+ def isNaN(number: Double): Boolean = native
+ /** Tests whether the given value is a finite number. */
+ def isFinite(number: Double): Boolean = native
+
+ /** Decodes a Uniform Resource Identifier (URI).
+ * @see [[encodeURI]]
+ */
+ def decodeURI(encodedURI: String): String = native
+
+ /** Decodes a Uniform Resource Identifier (URI) component.
+ * @see [[encodeURIComponent]]
+ */
+ def decodeURIComponent(encodedURIComponent: String): String = native
+
+ /** Encodes a Uniform Resource Identifier (URI).
+ * @see [[decodeURI]]
+ */
+ def encodeURI(uri: String): String = native
+
+ /** Encodes a Uniform Resource Identifier (URI) component.
+ * @see [[decodeURIComponent]]
+ */
+ def encodeURIComponent(uriComponent: String): String = native
+
+ /** Denotes a method body as native JavaScript. For use in facade types:
+ *
+ * {{{
+ * class MyJSClass extends js.Object {
+ * def myMethod(x: String): Int = js.native
+ * }
+ * }}}
+ */
+ def native: Nothing = sys.error("A method defined in a JavaScript raw " +
+ "type of a Scala.js library has been called. This is most likely " +
+ "because you tried to run Scala.js binaries on the JVM. Make sure you " +
+ "are using the JVM version of the libraries.")
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/ArrayBuffer.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/ArrayBuffer.scala
new file mode 100644
index 0000000..b8b8160
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/ArrayBuffer.scala
@@ -0,0 +1,17 @@
+package scala.scalajs.js.typedarray
+
+import scala.scalajs.js
+
+class ArrayBuffer(length: Int) extends js.Object {
+
+ /** Length of this buffer in bytes */
+ val byteLength: Int = js.native
+
+ /** Returns a copy of the given slice of this array buffer */
+ def slice(begin: Int, end: Int = ???): ArrayBuffer = js.native
+
+ // Note: Some specifications specify a static isView method on ArrayBuffer
+ // that checks whether a given object is an ArrayBufferView. We omit it here
+ // since neither Node.js nor PhantomJS support it at the time of writing.
+
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/ArrayBufferInputStream.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/ArrayBufferInputStream.scala
new file mode 100644
index 0000000..f3d2afb
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/ArrayBufferInputStream.scala
@@ -0,0 +1,88 @@
+package scala.scalajs.js.typedarray
+
+import java.io.InputStream
+
+/** A java.io.InputStream wrapping a JavaScript ArrayBuffer
+ *
+ * This class is extremely similar to a ByteArrayInputStream, but
+ * uses ArrayBuffers as the underlying representation. Stream
+ * implementations may special case on this stream for better
+ * performance and access the underlying buffer directly. (They still
+ * need to make sure the internal pointers are properly aligned
+ * though).
+ *
+ * This stream has several public members (n.b. [[buffer]], [[offset]],
+ * [[length]] and [[pos]]) in order to allow JavaScript aware applications to
+ * special case on this kind of stream and access the underlying
+ * [[ArrayBuffer]] directly for efficiency. In this case it is the client's
+ * responsibility to synchronize [[pos]], as if the stream were read normally
+ * (if the context in which it is used requires this).
+ *
+ * @param buffer Underlying ArrayBuffer
+ * @param offset Offset in bytes in [[buffer]]
+ * @param length Length in bytes in [[buffer]]
+ */
+class ArrayBufferInputStream(val buffer: ArrayBuffer, val offset: Int,
+ val length: Int) extends InputStream {
+
+ /** Convenience constructor. Strictly equivalent to
+ * {{new ArrayBufferInputStream(buffer, 0, buffer.byteLength)}
+ */
+ def this(buffer: ArrayBuffer) = this(buffer, 0, buffer.byteLength)
+
+ private val uintView = new Uint8Array(buffer, offset, length)
+ private val byteView = new Int8Array(buffer, offset, length)
+
+ /** Used to persist [[pos]] when mark is called */
+ protected var mark: Int = 0
+
+ /** Next byte to read in the buffer (after adding offset).
+ *
+ * Use [[skip]] to update (protects from overrun and moving backwards).
+ */
+ @inline def pos: Int = _pos
+ @inline protected def pos_=(x: Int): Unit = _pos = x
+ private[this] var _pos: Int = 0
+
+ override def available(): Int = length - pos
+ override def mark(readlimit: Int): Unit = { mark = pos }
+ override def markSupported(): Boolean = true
+ def read(): Int = {
+ if (pos < length) {
+ val res = uintView(pos)
+ pos += 1
+ res
+ } else -1
+ }
+
+ override def read(b: Array[Byte], off: Int, reqLen: Int): Int = {
+ if (off < 0 || reqLen < 0 || reqLen > b.length - off)
+ throw new IndexOutOfBoundsException
+
+ val len = Math.min(reqLen, length - pos)
+
+ if (reqLen == 0)
+ 0 // 0 requested, 0 returned
+ else if (len == 0)
+ -1 // nothing to read at all
+ else {
+ var i = 0
+ while (i < len) {
+ b(i + off) = byteView(pos + i)
+ i += 1
+ }
+ pos += len
+ len
+ }
+ }
+
+ override def reset(): Unit = { pos = mark }
+
+ /** Skips a given number of bytes. Always skips the maximum number possible */
+ override def skip(n: Long): Long = {
+ val k = Math.max(0, Math.min(n, length - pos)).toInt
+ pos += k
+ k.toLong
+ }
+
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/ArrayBufferView.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/ArrayBufferView.scala
new file mode 100644
index 0000000..6b25bf5
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/ArrayBufferView.scala
@@ -0,0 +1,14 @@
+package scala.scalajs.js.typedarray
+
+import scala.scalajs.js
+
+trait ArrayBufferView extends js.Object {
+ /** The underlying buffer of this ArrayBufferView */
+ val buffer: ArrayBuffer = js.native
+
+ /** The number of bytes of this ArrayBufferView */
+ val byteLength: Int = js.native
+
+ /** The offset of this ArrayBufferView in the underlying buffer */
+ val byteOffset: Int = js.native
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/DataView.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/DataView.scala
new file mode 100644
index 0000000..d97544c
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/DataView.scala
@@ -0,0 +1,26 @@
+package scala.scalajs.js.typedarray
+
+import scala.scalajs.js
+
+class DataView(buffer: ArrayBuffer, byteOffset: Int = 0,
+ byteLength: Int = ???) extends ArrayBufferView {
+
+ def getInt8(byteOffset: Int): Byte = js.native
+ def getUint8(byteOffset: Int): Short = js.native
+ def getInt16(byteOffset: Int, littleEndian: Boolean = false): Short = js.native
+ def getUint16(byteOffset: Int, littleEndian: Boolean = false): Int = js.native
+ def getInt32(byteOffset: Int, littleEndian: Boolean = false): Int = js.native
+ def getUint32(byteOffset: Int, littleEndian: Boolean = false): Double = js.native
+ def getFloat32(byteOffset: Int, littleEndian: Boolean = false): Float = js.native
+ def getFloat64(byteOffset: Int, littleEndian: Boolean = false): Double = js.native
+
+ def setInt8(byteOffset: Int, value: Byte): Unit = js.native
+ def setUint8(byteOffset: Int, value: Short): Unit = js.native
+ def setInt16(byteOffset: Int, value: Short, littleEndian: Boolean = false): Unit = js.native
+ def setUint16(byteOffset: Int, value: Int, littleEndian: Boolean = false): Unit = js.native
+ def setInt32(byteOffset: Int, value: Int, littleEndian: Boolean = false): Unit = js.native
+ def setUint32(byteOffset: Int, value: Double, littleEndian: Boolean = false): Unit = js.native
+ def setFloat32(byteOffset: Int, value: Float, littleEndian: Boolean = false): Unit = js.native
+ def setFloat64(byteOffset: Int, value: Double, littleEndian: Boolean = false): Unit = js.native
+
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Float32Array.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Float32Array.scala
new file mode 100644
index 0000000..abb0dd9
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Float32Array.scala
@@ -0,0 +1,24 @@
+package scala.scalajs.js.typedarray
+
+import scala.scalajs.js
+
+class Float32Array private extends TypedArray[Float, Float32Array] {
+
+ /** Constructs a Float32Array with the given length. Initialized to all 0 */
+ def this(length: Int) = this()
+
+ /** Creates a new Float32Array with the same elements than the given TypedArray
+ *
+ * The elements are converted before being stored in the new Int8Array.
+ */
+ def this(typedArray: TypedArray[_, _]) = this()
+
+ /** Creates a new Float32Array with the elements in the given array */
+ def this(array: js.Array[_]) = this()
+
+ /** Creates a Float32Array view on the given ArrayBuffer */
+ def this(buffer: ArrayBuffer, byteOffset: Int = 0, length: Int = ???) = this()
+
+}
+
+object Float32Array extends TypedArrayStatic
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Float64Array.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Float64Array.scala
new file mode 100644
index 0000000..526b376
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Float64Array.scala
@@ -0,0 +1,24 @@
+package scala.scalajs.js.typedarray
+
+import scala.scalajs.js
+
+class Float64Array private extends TypedArray[Double, Float64Array] {
+
+ /** Constructs a Float64Array with the given length. Initialized to all 0 */
+ def this(length: Int) = this()
+
+ /** Creates a new Float64Array with the same elements than the given TypedArray
+ *
+ * The elements are converted before being stored in the new Int8Array.
+ */
+ def this(typedArray: TypedArray[_, _]) = this()
+
+ /** Creates a new Float64Array with the elements in the given array */
+ def this(array: js.Array[_]) = this()
+
+ /** Creates a Float64Array view on the given ArrayBuffer */
+ def this(buffer: ArrayBuffer, byteOffset: Int = 0, length: Int = ???) = this()
+
+}
+
+object Float64Array extends TypedArrayStatic
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Int16Array.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Int16Array.scala
new file mode 100644
index 0000000..c71f101
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Int16Array.scala
@@ -0,0 +1,24 @@
+package scala.scalajs.js.typedarray
+
+import scala.scalajs.js
+
+class Int16Array private extends TypedArray[Short, Int16Array] {
+
+ /** Constructs a Int16Array with the given length. Initialized to all 0 */
+ def this(length: Int) = this()
+
+ /** Creates a new Int16Array with the same elements than the given TypedArray
+ *
+ * The elements are converted before being stored in the new Int8Array.
+ */
+ def this(typedArray: TypedArray[_, _]) = this()
+
+ /** Creates a new Int16Array with the elements in the given array */
+ def this(array: js.Array[_]) = this()
+
+ /** Creates a Int16Array view on the given ArrayBuffer */
+ def this(buffer: ArrayBuffer, byteOffset: Int = 0, length: Int = ???) = this()
+
+}
+
+object Int16Array extends TypedArrayStatic
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Int32Array.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Int32Array.scala
new file mode 100644
index 0000000..37208e9
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Int32Array.scala
@@ -0,0 +1,24 @@
+package scala.scalajs.js.typedarray
+
+import scala.scalajs.js
+
+class Int32Array private extends TypedArray[Int, Int32Array] {
+
+ /** Constructs a Int32Array with the given length. Initialized to all 0 */
+ def this(length: Int) = this()
+
+ /** Creates a new Int32Array with the same elements than the given TypedArray
+ *
+ * The elements are converted before being stored in the new Int8Array.
+ */
+ def this(typedArray: TypedArray[_, _]) = this()
+
+ /** Creates a new Int32Array with the elements in the given array */
+ def this(array: js.Array[_]) = this()
+
+ /** Creates a Int32Array view on the given ArrayBuffer */
+ def this(buffer: ArrayBuffer, byteOffset: Int = 0, length: Int = ???) = this()
+
+}
+
+object Int32Array extends TypedArrayStatic
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Int8Array.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Int8Array.scala
new file mode 100644
index 0000000..690ff07
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Int8Array.scala
@@ -0,0 +1,24 @@
+package scala.scalajs.js.typedarray
+
+import scala.scalajs.js
+
+class Int8Array private extends TypedArray[Byte, Int8Array] {
+
+ /** Constructs a Int8Array with the given length. Initialized to all 0 */
+ def this(length: Int) = this()
+
+ /** Creates a new Int8Array with the same elements than the given TypedArray
+ *
+ * The elements are converted before being stored in the new Int8Array.
+ */
+ def this(typedArray: TypedArray[_, _]) = this()
+
+ /** Creates a new Int8Array with the elements in the given array */
+ def this(array: js.Array[_]) = this()
+
+ /** Creates a Int8Array view on the given ArrayBuffer */
+ def this(buffer: ArrayBuffer, byteOffset: Int = 0, length: Int = ???) = this()
+
+}
+
+object Int8Array extends TypedArrayStatic
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/TypedArray.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/TypedArray.scala
new file mode 100644
index 0000000..4e33b5d
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/TypedArray.scala
@@ -0,0 +1,46 @@
+package scala.scalajs.js.typedarray
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSBracketAccess
+
+trait TypedArray[T, Repr] extends ArrayBufferView {
+
+ /** The number of elements in this TypedArray */
+ val length: Int = js.native
+
+ /** Retrieve element at index */
+ @JSBracketAccess
+ def apply(index: Int): T = js.native
+
+ /** Set element at index */
+ @JSBracketAccess
+ def update(index: Int, value: T): Unit = js.native
+
+ /** Retrieve element at index */
+ @JSBracketAccess
+ def get(index: Int): T = js.native
+
+ /** Set element at index */
+ @JSBracketAccess
+ def set(index: Int, value: T): Unit = js.native
+
+ /** Set the values of typedArray in this TypedArray */
+ def set(typedArray: TypedArray[_, _]): Unit = js.native
+
+ /** Set the values of typedArray in this TypedArray at given offset */
+ def set(typedArray: TypedArray[_, _], offset: Int): Unit = js.native
+
+ /** Set the values from array in this TypedArray */
+ def set(array: js.Array[_]): Unit = js.native
+
+ /** Set the values from array in this TypedArray at given offset */
+ def set(array: js.Array[_], offset: Int): Unit = js.native
+
+ /** Create a new TypedArray view of this TypedArray at given location */
+ def subarray(begin: Int, end: Int = ???): Repr = js.native
+
+}
+
+trait TypedArrayStatic extends js.Object {
+ val BYTES_PER_ELEMENT: Int = js.native
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Uint16Array.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Uint16Array.scala
new file mode 100644
index 0000000..82d2847
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Uint16Array.scala
@@ -0,0 +1,24 @@
+package scala.scalajs.js.typedarray
+
+import scala.scalajs.js
+
+class Uint16Array private extends TypedArray[Int, Uint16Array] {
+
+ /** Constructs a Uint16Array with the given length. Initialized to all 0 */
+ def this(length: Int) = this()
+
+ /** Creates a new Uint16Array with the same elements than the given TypedArray
+ *
+ * The elements are converted before being stored in the new Int8Array.
+ */
+ def this(typedArray: TypedArray[_, _]) = this()
+
+ /** Creates a new Uint16Array with the elements in the given array */
+ def this(array: js.Array[_]) = this()
+
+ /** Creates a Uint16Array view on the given ArrayBuffer */
+ def this(buffer: ArrayBuffer, byteOffset: Int = 0, length: Int = ???) = this()
+
+}
+
+object Uint16Array extends TypedArrayStatic
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Uint32Array.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Uint32Array.scala
new file mode 100644
index 0000000..9742e19
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Uint32Array.scala
@@ -0,0 +1,24 @@
+package scala.scalajs.js.typedarray
+
+import scala.scalajs.js
+
+class Uint32Array private extends TypedArray[Double, Uint32Array] {
+
+ /** Constructs a Uint32Array with the given length. Initialized to all 0 */
+ def this(length: Int) = this()
+
+ /** Creates a new Uint32Array with the same elements than the given TypedArray
+ *
+ * The elements are converted before being stored in the new Int8Array.
+ */
+ def this(typedArray: TypedArray[_, _]) = this()
+
+ /** Creates a new Uint32Array with the elements in the given array */
+ def this(array: js.Array[_]) = this()
+
+ /** Creates a Uint32Array view on the given ArrayBuffer */
+ def this(buffer: ArrayBuffer, byteOffset: Int = 0, length: Int = ???) = this()
+
+}
+
+object Uint32Array extends TypedArrayStatic
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Uint8Array.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Uint8Array.scala
new file mode 100644
index 0000000..f54904c
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Uint8Array.scala
@@ -0,0 +1,24 @@
+package scala.scalajs.js.typedarray
+
+import scala.scalajs.js
+
+class Uint8Array private extends TypedArray[Short, Uint8Array] {
+
+ /** Constructs a Uint8Array with the given length. Initialized to all 0 */
+ def this(length: Int) = this()
+
+ /** Creates a new Uint8Array with the same elements than the given TypedArray
+ *
+ * The elements are converted before being stored in the new Int8Array.
+ */
+ def this(typedArray: TypedArray[_, _]) = this()
+
+ /** Creates a new Uint8Array with the elements in the given array */
+ def this(array: js.Array[_]) = this()
+
+ /** Creates a Uint8Array view on the given ArrayBuffer */
+ def this(buffer: ArrayBuffer, byteOffset: Int = 0, length: Int = ???) = this()
+
+}
+
+object Uint8Array extends TypedArrayStatic
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Uint8ClampedArray.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Uint8ClampedArray.scala
new file mode 100644
index 0000000..601d65c
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/Uint8ClampedArray.scala
@@ -0,0 +1,24 @@
+package scala.scalajs.js.typedarray
+
+import scala.scalajs.js
+
+class Uint8ClampedArray private extends TypedArray[Int, Uint8ClampedArray] {
+
+ /** Constructs a Uint8ClampedArray with the given length. Initialized to all 0 */
+ def this(length: Int) = this()
+
+ /** Creates a new Uint8ClampedArray with the same elements than the given TypedArray
+ *
+ * The elements are converted before being stored in the new Int8Array.
+ */
+ def this(typedArray: TypedArray[_, _]) = this()
+
+ /** Creates a new Uint8ClampedArray with the elements in the given array */
+ def this(array: js.Array[_]) = this()
+
+ /** Creates a Uint8ClampedArray view on the given ArrayBuffer */
+ def this(buffer: ArrayBuffer, byteOffset: Int = 0, length: Int = ???) = this()
+
+}
+
+object Uint8ClampedArray extends TypedArrayStatic
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/package.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/package.scala
new file mode 100644
index 0000000..0ab5a05
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/js/typedarray/package.scala
@@ -0,0 +1,145 @@
+package scala.scalajs.js
+
+import JSConverters._
+
+/** The typdearray package provides facade types for JavaScript
+ * ArrayBuffer, TypeArrays and DataView. Further, it provides
+ * conversions between primitive Scala arrays and TypedArrays
+ */
+package object typedarray {
+
+ // Implicit classes scala.Array -> TypedArray
+ implicit class AB2TA(val array: scala.Array[Byte]) extends AnyVal {
+ def toTypedArray: Int8Array = byteArray2Int8Array(array)
+ }
+
+ implicit class AS2TA(val array: scala.Array[Short]) extends AnyVal {
+ def toTypedArray: Int16Array = shortArray2Int16Array(array)
+ }
+
+ implicit class AC2TA(val array: scala.Array[Char]) extends AnyVal {
+ def toTypedArray: Uint16Array = charArray2Uint16Array(array)
+ }
+
+ implicit class AI2TA(val array: scala.Array[Int]) extends AnyVal {
+ def toTypedArray: Int32Array = intArray2Int32Array(array)
+ }
+
+ implicit class AF2TA(val array: scala.Array[Float]) extends AnyVal {
+ def toTypedArray: Float32Array = floatArray2Float32Array(array)
+ }
+
+ implicit class AD2TA(val array: scala.Array[Double]) extends AnyVal {
+ def toTypedArray: Float64Array = doubleArray2Float64Array(array)
+ }
+
+ // Implicit classes TypedArray -> scala.Array
+ implicit class TA2AB(val array: Int8Array) extends AnyVal {
+ def toArray: scala.Array[Byte] = int8Array2ByteArray(array)
+ }
+
+ implicit class TA2AS(val array: Int16Array) extends AnyVal {
+ def toArray: scala.Array[Short] = int16Array2ShortArray(array)
+ }
+
+ implicit class TA2AC(val array: Uint16Array) extends AnyVal {
+ def toArray: scala.Array[Char] = uint16Array2CharArray(array)
+ }
+
+ implicit class TA2AI(val array: Int32Array) extends AnyVal {
+ def toArray: scala.Array[Int] = int32Array2IntArray(array)
+ }
+
+ implicit class TA2AF(val array: Float32Array) extends AnyVal {
+ def toArray: scala.Array[Float] = float32Array2FloatArray(array)
+ }
+
+ implicit class TA2AD(val array: Float64Array) extends AnyVal {
+ def toArray: scala.Array[Double] = float64Array2DoubleArray(array)
+ }
+
+ // scala.Array -> TypedArray
+
+ def byteArray2Int8Array(array: scala.Array[Byte]): Int8Array =
+ array2typedArrayImpl(array, new Int8Array(array.length))
+
+ def shortArray2Int16Array(array: scala.Array[Short]): Int16Array =
+ array2typedArrayImpl(array, new Int16Array(array.length))
+
+ def charArray2Uint16Array(array: scala.Array[Char]): Uint16Array = {
+ // Can't use array2typedArrayImpl because Uint16Array contains Ints
+ val len = array.length
+ val dest = new Uint16Array(len)
+ var i = 0
+ while (i < len) {
+ dest(i) = array(i).toInt
+ i += 1
+ }
+ dest
+ }
+
+ def intArray2Int32Array(array: scala.Array[Int]): Int32Array =
+ array2typedArrayImpl(array, new Int32Array(array.length))
+
+ def floatArray2Float32Array(array: scala.Array[Float]): Float32Array =
+ array2typedArrayImpl(array, new Float32Array(array.length))
+
+ def doubleArray2Float64Array(array: scala.Array[Double]): Float64Array =
+ array2typedArrayImpl(array, new Float64Array(array.length))
+
+ @inline private def array2typedArrayImpl[
+ @specialized(Byte, Short, Int, Float, Double) T,
+ Repr <: TypedArray[T, Repr]](
+ array: scala.Array[T], dest: Repr): Repr = {
+ val len = array.length
+ var i = 0
+ while (i < len) {
+ dest(i) = array(i)
+ i += 1
+ }
+ dest
+ }
+
+ // TypedArray -> scala.Array
+
+ def int8Array2ByteArray(array: Int8Array): scala.Array[Byte] =
+ typedArray2arrayImpl(array, new scala.Array(array.length))
+
+ def int16Array2ShortArray(array: Int16Array): scala.Array[Short] =
+ typedArray2arrayImpl(array, new scala.Array(array.length))
+
+ def uint16Array2CharArray(array: Uint16Array): scala.Array[Char] = {
+ // Can't use typedArray2arrayImpl because Uint16Array contains Ints
+ val len = array.length
+ val dest = new scala.Array[Char](len)
+ var i = 0
+ while (i < len) {
+ dest(i) = array(i).toChar
+ i += 1
+ }
+ dest
+ }
+
+ def int32Array2IntArray(array: Int32Array): scala.Array[Int] =
+ typedArray2arrayImpl(array, new scala.Array(array.length))
+
+ def float32Array2FloatArray(array: Float32Array): scala.Array[Float] =
+ typedArray2arrayImpl(array, new scala.Array(array.length))
+
+ def float64Array2DoubleArray(array: Float64Array): scala.Array[Double] =
+ typedArray2arrayImpl(array, new scala.Array(array.length))
+
+ @inline private def typedArray2arrayImpl[
+ @specialized(Byte, Short, Int, Float, Double) T,
+ Repr <: TypedArray[T, Repr]](
+ array: Repr, dest: scala.Array[T]): scala.Array[T] = {
+ val len = dest.length
+ var i = 0
+ while (i < len) {
+ dest(i) = array(i)
+ i += 1
+ }
+ dest
+ }
+
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/ISO_8859_1.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/ISO_8859_1.scala
new file mode 100644
index 0000000..7765f0c
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/ISO_8859_1.scala
@@ -0,0 +1,19 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.niocharset
+
+import java.nio.charset._
+
+private[niocharset] object ISO_8859_1 extends ISO_8859_1_And_US_ASCII_Common(
+ "ISO-8859-1", Array(
+ "csISOLatin1", "IBM-819", "iso-ir-100", "8859_1", "ISO_8859-1", "l1",
+ "ISO8859-1", "ISO_8859_1", "cp819", "ISO8859_1", "latin1",
+ "ISO_8859-1:1987", "819", "IBM819"),
+ maxValue = 0xff)
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/ISO_8859_1_And_US_ASCII_Common.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/ISO_8859_1_And_US_ASCII_Common.scala
new file mode 100644
index 0000000..ddc83db
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/ISO_8859_1_And_US_ASCII_Common.scala
@@ -0,0 +1,197 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.niocharset
+
+import scala.annotation.tailrec
+
+import java.nio._
+import java.nio.charset._
+
+/** This is a very specific common implementation for ISO_8859_1 and US_ASCII.
+ * Only a single constant changes between the two algorithms (`maxValue`).
+ * No attempt was made at generalizing this to other potential charsets.
+ *
+ * `maxValue` is therefore either 0xff (ISO_8859_1) or 0x7f (US_ASCII).
+ */
+private[niocharset] abstract class ISO_8859_1_And_US_ASCII_Common protected (
+ name: String, aliases: Array[String],
+ private val maxValue: Int) extends Charset(name, aliases) {
+
+ def contains(that: Charset): Boolean = that match {
+ case that: ISO_8859_1_And_US_ASCII_Common => this.maxValue >= that.maxValue
+ case _ => false
+ }
+
+ def newDecoder(): CharsetDecoder = new Decoder
+ def newEncoder(): CharsetEncoder = new Encoder
+
+ private class Decoder extends CharsetDecoder(
+ ISO_8859_1_And_US_ASCII_Common.this, 1.0f, 1.0f) {
+ def decodeLoop(in: ByteBuffer, out: CharBuffer): CoderResult = {
+ val maxValue = ISO_8859_1_And_US_ASCII_Common.this.maxValue
+ val inRemaining = in.remaining
+ if (inRemaining == 0) {
+ CoderResult.UNDERFLOW
+ } else {
+ val outRemaining = out.remaining
+ val overflow = outRemaining < inRemaining
+ val rem = if (overflow) outRemaining else inRemaining
+
+ if (in.hasArray && out.hasArray) {
+ val inArr = in.array
+ val inOffset = in.arrayOffset
+ val inStart = in.position + inOffset
+ val inEnd = inStart + rem
+
+ val outArr = out.array
+ val outOffset = out.arrayOffset
+ val outStart = out.position + outOffset
+
+ var inPos = inStart
+ var outPos = outStart
+ while (inPos != inEnd) {
+ // Apparently ignoring the bit 7 in US_ASCII is the expected behavior
+ outArr(outPos) = (inArr(inPos).toInt & maxValue).toChar
+ inPos += 1
+ outPos += 1
+ }
+
+ in.position(inPos - inOffset)
+ out.position(outPos - outOffset)
+ } else {
+ /* Here, it's fine to read all the remaining bytes from the input,
+ * because we will *always* use all of them.
+ */
+ var i = 0
+ while (i != rem) {
+ // Apparently ignoring the bit 7 in US_ASCII is the expected behavior
+ out.put((in.get(i).toInt & maxValue).toChar)
+ i += 1
+ }
+ }
+
+ if (overflow) CoderResult.OVERFLOW
+ else CoderResult.UNDERFLOW
+ }
+ }
+ }
+
+ private class Encoder extends CharsetEncoder(
+ ISO_8859_1_And_US_ASCII_Common.this, 1.0f, 1.0f) {
+ def encodeLoop(in: CharBuffer, out: ByteBuffer): CoderResult = {
+ import java.lang.Character.{MIN_SURROGATE, MAX_SURROGATE}
+
+ val maxValue = ISO_8859_1_And_US_ASCII_Common.this.maxValue
+ val inRemaining = in.remaining
+ if (inRemaining == 0) {
+ CoderResult.UNDERFLOW
+ } else {
+ if (in.hasArray && out.hasArray) {
+ val outRemaining = out.remaining
+ val overflow = outRemaining < inRemaining
+ val rem = if (overflow) outRemaining else inRemaining
+
+ val inArr = in.array
+ val inOffset = in.arrayOffset
+ val inStart = in.position + inOffset
+ val inEnd = inStart + rem
+
+ val outArr = out.array
+ val outOffset = out.arrayOffset
+ val outStart = out.position + outOffset
+
+ @inline
+ @tailrec
+ def loop(inPos: Int, outPos: Int): CoderResult = {
+ @inline
+ def finalize(result: CoderResult): CoderResult = {
+ in.position(inPos - inOffset)
+ out.position(outPos - outOffset)
+ result
+ }
+
+ if (inPos == inEnd) {
+ finalize {
+ if (overflow) CoderResult.OVERFLOW
+ else CoderResult.UNDERFLOW
+ }
+ } else {
+ val c = inArr(inPos)
+ if (c <= maxValue) {
+ outArr(outPos) = c.toByte
+ loop(inPos+1, outPos+1)
+ } else {
+ finalize {
+ if (Character.isLowSurrogate(c)) {
+ CoderResult.malformedForLength(1)
+ } else if (Character.isHighSurrogate(c)) {
+ if (inPos + 1 < in.limit) {
+ val c2 = inArr(inPos+1)
+ if (Character.isLowSurrogate(c2))
+ CoderResult.unmappableForLength(2)
+ else
+ CoderResult.malformedForLength(1)
+ } else {
+ CoderResult.UNDERFLOW
+ }
+ } else {
+ CoderResult.unmappableForLength(1)
+ }
+ }
+ }
+ }
+ }
+
+ loop(inStart, outStart)
+ } else {
+ // Not both have arrays
+ @inline
+ @tailrec
+ def loop(): CoderResult = {
+ if (!in.hasRemaining) {
+ CoderResult.UNDERFLOW
+ } else if (!out.hasRemaining) {
+ CoderResult.OVERFLOW
+ } else {
+ val c = in.get()
+ if (c <= maxValue) {
+ out.put(c.toByte)
+ loop()
+ } else {
+ if (Character.isLowSurrogate(c)) {
+ in.position(in.position - 1)
+ CoderResult.malformedForLength(1)
+ } else if (Character.isHighSurrogate(c)) {
+ if (in.hasRemaining) {
+ val c2 = in.get()
+ in.position(in.position - 2)
+ if (Character.isLowSurrogate(c2)) {
+ CoderResult.unmappableForLength(2)
+ } else {
+ CoderResult.malformedForLength(1)
+ }
+ } else {
+ in.position(in.position - 1)
+ CoderResult.UNDERFLOW
+ }
+ } else {
+ in.position(in.position - 1)
+ CoderResult.unmappableForLength(1)
+ }
+ }
+ }
+ }
+
+ loop()
+ }
+ }
+ }
+ }
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/StandardCharsets.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/StandardCharsets.scala
new file mode 100644
index 0000000..38615f6
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/StandardCharsets.scala
@@ -0,0 +1,42 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.niocharset
+
+import java.nio.charset._
+
+/** Standard charsets.
+ * This is basically the same as [[java.nio.charset.StandardCharsets]], but
+ * it is also available when compiling with a JDK 6.
+ */
+object StandardCharsets {
+ import scala.scalajs.niocharset
+
+ /** ISO-8859-1, aka latin1. */
+ def ISO_8859_1: Charset = niocharset.ISO_8859_1
+
+ /** US-ASCII. */
+ def US_ASCII: Charset = niocharset.US_ASCII
+
+ /** UTF-8. */
+ def UTF_8: Charset = niocharset.UTF_8
+
+ /** UTF-16 Big Endian without BOM. */
+ def UTF_16BE: Charset = niocharset.UTF_16BE
+
+ /** UTF-16 Little Endian without BOM. */
+ def UTF_16LE: Charset = niocharset.UTF_16LE
+
+ /** UTF-16 with an optional BOM.
+ * When encoding, Big Endian is always used.
+ * When decoding, the BOM specifies what endianness to use. If no BOM is
+ * found, it defaults to Big Endian.
+ */
+ def UTF_16: Charset = niocharset.UTF_16
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/US_ASCII.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/US_ASCII.scala
new file mode 100644
index 0000000..746c75b
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/US_ASCII.scala
@@ -0,0 +1,19 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.niocharset
+
+import java.nio.charset._
+
+private[niocharset] object US_ASCII extends ISO_8859_1_And_US_ASCII_Common(
+ "US-ASCII", Array(
+ "cp367", "ascii7", "ISO646-US", "646", "csASCII", "us", "iso_646.irv:1983",
+ "ISO_646.irv:1991", "IBM367", "ASCII", "default", "ANSI_X3.4-1986",
+ "ANSI_X3.4-1968", "iso-ir-6"),
+ maxValue = 0x7f)
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/UTF_16.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/UTF_16.scala
new file mode 100644
index 0000000..9d1748a
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/UTF_16.scala
@@ -0,0 +1,17 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.niocharset
+
+import java.nio.charset._
+
+private[niocharset] object UTF_16 extends UTF_16_Common(
+ "UTF-16", Array(
+ "utf16", "UTF_16", "UnicodeBig", "unicode"),
+ endianness = UTF_16_Common.AutoEndian)
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/UTF_16BE.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/UTF_16BE.scala
new file mode 100644
index 0000000..dece191
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/UTF_16BE.scala
@@ -0,0 +1,17 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.niocharset
+
+import java.nio.charset._
+
+private[niocharset] object UTF_16BE extends UTF_16_Common(
+ "UTF-16BE", Array(
+ "X-UTF-16BE", "UTF_16BE", "ISO-10646-UCS-2", "UnicodeBigUnmarked"),
+ endianness = UTF_16_Common.BigEndian)
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/UTF_16LE.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/UTF_16LE.scala
new file mode 100644
index 0000000..de469c4
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/UTF_16LE.scala
@@ -0,0 +1,17 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.niocharset
+
+import java.nio.charset._
+
+private[niocharset] object UTF_16LE extends UTF_16_Common(
+ "UTF-16LE", Array(
+ "UnicodeLittleUnmarked", "UTF_16LE", "X-UTF-16LE"),
+ endianness = UTF_16_Common.LittleEndian)
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/UTF_16_Common.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/UTF_16_Common.scala
new file mode 100644
index 0000000..3330d9c
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/UTF_16_Common.scala
@@ -0,0 +1,205 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.niocharset
+
+import scala.annotation.tailrec
+
+import java.nio._
+import java.nio.charset._
+
+/** This is a very specific common implementation for UTF_16BE and UTF_16LE.
+ */
+private[niocharset] abstract class UTF_16_Common protected (
+ name: String, aliases: Array[String],
+ private val endianness: Int) extends Charset(name, aliases) {
+
+ import UTF_16_Common._
+
+ def contains(that: Charset): Boolean = true
+
+ def newDecoder(): CharsetDecoder = new Decoder
+ def newEncoder(): CharsetEncoder = new Encoder
+
+ private class Decoder extends CharsetDecoder(
+ UTF_16_Common.this, 0.5f, 1.0f) {
+ private var endianness = UTF_16_Common.this.endianness
+
+ override protected def implReset(): Unit = {
+ super.implReset()
+ endianness = UTF_16_Common.this.endianness
+ }
+
+ def decodeLoop(in: ByteBuffer, out: CharBuffer): CoderResult = {
+ @inline
+ @tailrec
+ def loop(): CoderResult = {
+ if (in.remaining < 2) CoderResult.UNDERFLOW
+ else {
+ val b1 = in.get() & 0xff
+ val b2 = in.get() & 0xff
+
+ val wasBOM = if (endianness == AutoEndian) {
+ // Read BOM
+ if (b1 == 0xfe && b2 == 0xff) {
+ endianness = BigEndian
+ true
+ } else if (b1 == 0xff && b2 == 0xfe) {
+ endianness = LittleEndian
+ true
+ } else {
+ // Not a valid BOM: default to BigEndian and start reading
+ endianness = BigEndian
+ false
+ }
+ } else false
+
+ if (wasBOM) {
+ loop()
+ } else {
+ val bigEndian = endianness == BigEndian
+
+ @inline def bytes2char(hi: Int, lo: Int): Char =
+ (if (bigEndian) (hi << 8) | lo else (lo << 8) | hi).toChar
+
+ val c1 = bytes2char(b1, b2)
+
+ if (Character.isLowSurrogate(c1)) {
+ in.position(in.position - 2)
+ CoderResult.malformedForLength(2)
+ } else if (!Character.isHighSurrogate(c1)) {
+ if (out.remaining == 0) {
+ in.position(in.position - 2)
+ CoderResult.OVERFLOW
+ } else {
+ out.put(c1)
+ loop()
+ }
+ } else {
+ if (in.remaining < 2) {
+ in.position(in.position - 2)
+ CoderResult.UNDERFLOW
+ } else {
+ val b3 = in.get() & 0xff
+ val b4 = in.get() & 0xff
+ val c2 = bytes2char(b3, b4)
+
+ if (!Character.isLowSurrogate(c2)) {
+ in.position(in.position - 4)
+ CoderResult.malformedForLength(2)
+ } else {
+ if (out.remaining < 2) {
+ in.position(in.position - 4)
+ CoderResult.OVERFLOW
+ } else {
+ out.put(c1)
+ out.put(c2)
+ loop()
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ loop()
+ }
+ }
+
+ private class Encoder extends CharsetEncoder(
+ UTF_16_Common.this, 2.0f, 2.0f,
+ // Character 0xfffd
+ if (endianness == LittleEndian) Array(-3, -1) else Array(-1, -3)) {
+
+ private var needToWriteBOM: Boolean = endianness == AutoEndian
+
+ override protected def implReset(): Unit = {
+ super.implReset()
+ needToWriteBOM = endianness == AutoEndian
+ }
+
+ def encodeLoop(in: CharBuffer, out: ByteBuffer): CoderResult = {
+ if (needToWriteBOM) {
+ if (out.remaining < 2) {
+ return CoderResult.OVERFLOW
+ } else {
+ // Always encode in big endian
+ out.put(0xfe.toByte)
+ out.put(0xff.toByte)
+ needToWriteBOM = false
+ }
+ }
+
+ val bigEndian = endianness != LittleEndian
+
+ @inline
+ def putChar(c: Char): Unit = {
+ if (bigEndian) {
+ out.put((c >> 8).toByte)
+ out.put(c.toByte)
+ } else {
+ out.put(c.toByte)
+ out.put((c >> 8).toByte)
+ }
+ }
+
+ @inline
+ @tailrec
+ def loop(): CoderResult = {
+ if (in.remaining == 0) CoderResult.UNDERFLOW
+ else {
+ val c1 = in.get()
+
+ if (Character.isLowSurrogate(c1)) {
+ in.position(in.position - 1)
+ CoderResult.malformedForLength(1)
+ } else if (!Character.isHighSurrogate(c1)) {
+ if (out.remaining < 2) {
+ in.position(in.position - 1)
+ CoderResult.OVERFLOW
+ } else {
+ putChar(c1)
+ loop()
+ }
+ } else {
+ if (in.remaining < 1) {
+ in.position(in.position - 1)
+ CoderResult.UNDERFLOW
+ } else {
+ val c2 = in.get()
+
+ if (!Character.isLowSurrogate(c2)) {
+ in.position(in.position - 2)
+ CoderResult.malformedForLength(1)
+ } else {
+ if (out.remaining < 4) {
+ in.position(in.position - 2)
+ CoderResult.OVERFLOW
+ } else {
+ putChar(c1)
+ putChar(c2)
+ loop()
+ }
+ }
+ }
+ }
+ }
+ }
+
+ loop()
+ }
+ }
+}
+
+private[niocharset] object UTF_16_Common {
+ final val AutoEndian = 0
+ final val BigEndian = 1
+ final val LittleEndian = 2
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/UTF_8.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/UTF_8.scala
new file mode 100644
index 0000000..57f4ad6
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/niocharset/UTF_8.scala
@@ -0,0 +1,455 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.niocharset
+
+import scala.annotation.{switch, tailrec}
+
+import java.nio._
+import java.nio.charset._
+
+private[niocharset] object UTF_8 extends Charset("UTF-8", Array(
+ "UTF8", "unicode-1-1-utf-8")) {
+
+ import java.lang.Character._
+
+ def contains(that: Charset): Boolean = true
+
+ def newDecoder(): CharsetDecoder = new Decoder
+ def newEncoder(): CharsetEncoder = new Encoder
+
+ /* The next table contains information about UTF-8 charset and
+ * correspondence of 1st byte to the length of sequence
+ * For information please visit http://www.ietf.org/rfc/rfc3629.txt
+ *
+ * -------------------------------------------------------------------
+ * 0 1 2 3 Value
+ * -------------------------------------------------------------------
+ * 0xxxxxxx 00000000 00000000 0xxxxxxx
+ * 110yyyyy 10xxxxxx 00000000 00000yyy yyxxxxxx
+ * 1110zzzz 10yyyyyy 10xxxxxx 00000000 zzzzyyyy yyxxxxxx
+ * 11110uuu 10zzzzzz 10yyyyyy 10xxxxxx 000uuuzz zzzzyyyy yyxxxxxx
+ */
+
+ private val lengthByLeading: Array[Int] = Array(
+ // 10wwwwww
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ // 110yyyyy
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ // 1110zzzz
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ // 11110uuu
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ // > 11110111
+ -1, -1, -1, -1, -1, -1, -1, -1
+ )
+
+ @inline
+ private class DecodedMultiByte(val failure: CoderResult,
+ val high: Char, val low: Char)
+
+ private object DecodedMultiByte {
+ @inline def apply(failure: CoderResult): DecodedMultiByte =
+ new DecodedMultiByte(failure, 0, 0)
+
+ @inline def apply(single: Char): DecodedMultiByte =
+ new DecodedMultiByte(null, single, 0)
+
+ @inline def apply(high: Char, low: Char): DecodedMultiByte =
+ new DecodedMultiByte(null, high, low)
+ }
+
+ private class Decoder extends CharsetDecoder(UTF_8, 1.0f, 1.0f) {
+ def decodeLoop(in: ByteBuffer, out: CharBuffer): CoderResult = {
+ if (in.hasArray && out.hasArray)
+ decodeLoopArray(in, out)
+ else
+ decodeLoopNoArray(in, out)
+ }
+
+ private def decodeLoopArray(in: ByteBuffer, out: CharBuffer): CoderResult = {
+ val inArray = in.array
+ val inOffset = in.arrayOffset
+ val inStart = in.position + inOffset
+ val inEnd = in.limit + inOffset
+
+ val outArray = out.array
+ val outOffset = out.arrayOffset
+ val outStart = out.position + outOffset
+ val outEnd = out.limit + outOffset
+
+ @inline
+ @tailrec
+ def loop(inPos: Int, outPos: Int): CoderResult = {
+ @inline
+ def finalize(result: CoderResult): CoderResult = {
+ in.position(inPos - inOffset)
+ out.position(outPos - outOffset)
+ result
+ }
+
+ if (inPos == inEnd) {
+ finalize(CoderResult.UNDERFLOW)
+ } else {
+ val leading = inArray(inPos).toInt
+ if (leading >= 0) {
+ // US-ASCII repertoire
+ if (outPos == outEnd) {
+ finalize(CoderResult.OVERFLOW)
+ } else {
+ outArray(outPos) = leading.toChar
+ loop(inPos+1, outPos+1)
+ }
+ } else {
+ // Multi-byte
+ val length = lengthByLeading(leading & 0x7f)
+ if (length == -1) {
+ finalize(CoderResult.malformedForLength(1))
+ } else if (inPos + length > inEnd) {
+ finalize(CoderResult.UNDERFLOW)
+ } else {
+ val decoded = {
+ val b2 = inArray(inPos+1)
+ if (length == 2) decode2(leading, b2)
+ else if (length == 3) decode3(leading, b2, inArray(inPos+2))
+ else decode4(leading, b2, inArray(inPos+2), inArray(inPos+3))
+ }
+
+ if (decoded.failure != null) {
+ finalize(decoded.failure)
+ } else if (decoded.low == 0) {
+ // not a surrogate pair
+ if (outPos == outEnd)
+ finalize(CoderResult.OVERFLOW)
+ else {
+ outArray(outPos) = decoded.high
+ loop(inPos+length, outPos+1)
+ }
+ } else {
+ // a surrogate pair
+ if (outPos + 2 > outEnd)
+ finalize(CoderResult.OVERFLOW)
+ else {
+ outArray(outPos) = decoded.high
+ outArray(outPos+1) = decoded.low
+ loop(inPos+length, outPos+2)
+ }
+ }
+ }
+ }
+ }
+ }
+
+ loop(inStart, outStart)
+ }
+
+ private def decodeLoopNoArray(in: ByteBuffer, out: CharBuffer): CoderResult = {
+ @inline
+ @tailrec
+ def loop(): CoderResult = {
+ @inline
+ def finalize(read: Int, result: CoderResult): CoderResult = {
+ in.position(in.position - read)
+ result
+ }
+
+ if (!in.hasRemaining) {
+ CoderResult.UNDERFLOW
+ } else {
+ val leading = in.get().toInt
+ if (leading >= 0) {
+ // US-ASCII repertoire
+ if (!out.hasRemaining) {
+ finalize(1, CoderResult.OVERFLOW)
+ } else {
+ out.put(leading.toChar)
+ loop()
+ }
+ } else {
+ // Multi-byte
+ val length = lengthByLeading(leading & 0x7f)
+ if (length == -1) {
+ finalize(1, CoderResult.malformedForLength(1))
+ } else if (in.remaining < length-1) {
+ finalize(1, CoderResult.UNDERFLOW)
+ } else {
+ val decoded = {
+ if (length == 2) decode2(leading, in.get())
+ else if (length == 3) decode3(leading, in.get(), in.get())
+ else decode4(leading, in.get(), in.get(), in.get())
+ }
+
+ if (decoded.failure != null) {
+ finalize(length, decoded.failure)
+ } else if (decoded.low == 0) {
+ // not a surrogate pair
+ if (!out.hasRemaining)
+ finalize(length, CoderResult.OVERFLOW)
+ else {
+ out.put(decoded.high)
+ loop()
+ }
+ } else {
+ // a surrogate pair
+ if (out.remaining < 2)
+ finalize(length, CoderResult.OVERFLOW)
+ else {
+ out.put(decoded.high)
+ out.put(decoded.low)
+ loop()
+ }
+ }
+ }
+ }
+ }
+ }
+
+ loop()
+ }
+
+ @inline private def isInvalidNextByte(b: Int): Boolean =
+ (b & 0xc0) != 0x80
+
+ @inline private def decode2(b1: Int, b2: Int): DecodedMultiByte = {
+ if (isInvalidNextByte(b2))
+ DecodedMultiByte(CoderResult.malformedForLength(1))
+ else {
+ val codePoint = (((b1 & 0x1f) << 6) | (b2 & 0x3f))
+ // By construction, 0 <= codePoint <= 0x7ff < MIN_SURROGATE
+ if (codePoint < 0x80) {
+ // Should have been encoded with only 1 byte
+ DecodedMultiByte(CoderResult.malformedForLength(2))
+ } else {
+ DecodedMultiByte(codePoint.toChar)
+ }
+ }
+ }
+
+ @inline private def decode3(b1: Int, b2: Int, b3: Int): DecodedMultiByte = {
+ if (isInvalidNextByte(b2))
+ DecodedMultiByte(CoderResult.malformedForLength(1))
+ else if (isInvalidNextByte(b3))
+ DecodedMultiByte(CoderResult.malformedForLength(2))
+ else {
+ val codePoint = (((b1 & 0xf) << 12) | ((b2 & 0x3f) << 6) | (b3 & 0x3f))
+ // By construction, 0 <= codePoint <= 0xffff < MIN_SUPPLEMENTARY_CODE_POINT
+ if ((codePoint < 0x800) ||
+ (codePoint >= MIN_SURROGATE && codePoint <= MAX_SURROGATE)) {
+ // Should have been encoded with only 1 or 2 bytes
+ // or it is a surrogate, which is not a valid code point
+ DecodedMultiByte(CoderResult.malformedForLength(3))
+ } else {
+ DecodedMultiByte(codePoint.toChar)
+ }
+ }
+ }
+
+ @inline private def decode4(b1: Int, b2: Int, b3: Int, b4: Int): DecodedMultiByte = {
+ if (isInvalidNextByte(b2))
+ DecodedMultiByte(CoderResult.malformedForLength(1))
+ else if (isInvalidNextByte(b3))
+ DecodedMultiByte(CoderResult.malformedForLength(2))
+ else if (isInvalidNextByte(b4))
+ DecodedMultiByte(CoderResult.malformedForLength(3))
+ else {
+ val codePoint = (((b1 & 0x7) << 18) | ((b2 & 0x3f) << 12) |
+ ((b3 & 0x3f) << 6) | (b4 & 0x3f))
+ // By construction, 0 <= codePoint <= 0x1fffff
+ if (codePoint < 0x10000 || codePoint > MAX_CODE_POINT) {
+ // It should have been encoded with 1, 2, or 3 bytes
+ // or it is not a valid code point
+ DecodedMultiByte(CoderResult.malformedForLength(4))
+ } else {
+ // Here, we need to encode the code point as a surrogate pair.
+ // http://en.wikipedia.org/wiki/UTF-16
+ val offsetCodePoint = codePoint - 0x10000
+ DecodedMultiByte(
+ ((offsetCodePoint >> 10) | 0xd800).toChar,
+ ((offsetCodePoint & 0x3ff) | 0xdc00).toChar)
+ }
+ }
+ }
+ }
+
+ private class Encoder extends CharsetEncoder(UTF_8, 1.1f, 4.0f) {
+ def encodeLoop(in: CharBuffer, out: ByteBuffer): CoderResult = {
+ if (in.hasArray && out.hasArray)
+ encodeLoopArray(in, out)
+ else
+ encodeLoopNoArray(in, out)
+ }
+
+ private def encodeLoopArray(in: CharBuffer, out: ByteBuffer): CoderResult = {
+ val inArray = in.array
+ val inOffset = in.arrayOffset
+ val inStart = in.position + inOffset
+ val inEnd = in.limit + inOffset
+
+ val outArray = out.array
+ val outOffset = out.arrayOffset
+ val outStart = out.position + outOffset
+ val outEnd = out.limit + outOffset
+
+ @inline
+ @tailrec
+ def loop(inPos: Int, outPos: Int): CoderResult = {
+ @inline
+ def finalize(result: CoderResult): CoderResult = {
+ in.position(inPos - inOffset)
+ out.position(outPos - outOffset)
+ result
+ }
+
+ if (inPos == inEnd) {
+ finalize(CoderResult.UNDERFLOW)
+ } else {
+ val c1 = inArray(inPos)
+
+ if (c1 < 0x80) {
+ // Encoding in one byte
+ if (outPos == outEnd)
+ finalize(CoderResult.OVERFLOW)
+ else {
+ outArray(outPos) = c1.toByte
+ loop(inPos+1, outPos+1)
+ }
+ } else if (c1 < 0x800) {
+ // Encoding in 2 bytes (by construction, not a surrogate)
+ if (outPos + 2 > outEnd)
+ finalize(CoderResult.OVERFLOW)
+ else {
+ outArray(outPos) = ((c1 >> 6) | 0xc0).toByte
+ outArray(outPos+1) = ((c1 & 0x3f) | 0x80).toByte
+ loop(inPos+1, outPos+2)
+ }
+ } else if (!isSurrogate(c1)) {
+ // Not a surrogate, encoding in 3 bytes
+ if (outPos + 3 > outEnd)
+ finalize(CoderResult.OVERFLOW)
+ else {
+ outArray(outPos) = ((c1 >> 12) | 0xe0).toByte
+ outArray(outPos+1) = (((c1 >> 6) & 0x3f) | 0x80).toByte
+ outArray(outPos+2) = ((c1 & 0x3f) | 0x80).toByte
+ loop(inPos+1, outPos+3)
+ }
+ } else if (isHighSurrogate(c1)) {
+ // Should have a low surrogate that follows
+ if (inPos + 1 == inEnd)
+ finalize(CoderResult.UNDERFLOW)
+ else {
+ val c2 = inArray(inPos+1)
+ if (!isLowSurrogate(c2)) {
+ finalize(CoderResult.malformedForLength(1))
+ } else {
+ // Surrogate pair, encoding in 4 bytes
+ if (outPos + 4 > outEnd)
+ finalize(CoderResult.OVERFLOW)
+ else {
+ val cp = toCodePoint(c1, c2)
+ outArray(outPos) = ((cp >> 18) | 0xf0).toByte
+ outArray(outPos+1) = (((cp >> 12) & 0x3f) | 0x80).toByte
+ outArray(outPos+2) = (((cp >> 6) & 0x3f) | 0x80).toByte
+ outArray(outPos+3) = ((cp & 0x3f) | 0x80).toByte
+ loop(inPos+2, outPos+4)
+ }
+ }
+ }
+ } else {
+ finalize(CoderResult.malformedForLength(1))
+ }
+ }
+ }
+
+ loop(inStart, outStart)
+ }
+
+ private def encodeLoopNoArray(in: CharBuffer, out: ByteBuffer): CoderResult = {
+ @inline
+ @tailrec
+ def loop(): CoderResult = {
+ @inline
+ def finalize(read: Int, result: CoderResult): CoderResult = {
+ in.position(in.position - read)
+ result
+ }
+
+ if (!in.hasRemaining) {
+ CoderResult.UNDERFLOW
+ } else {
+ val c1 = in.get()
+
+ if (c1 < 0x80) {
+ // Encoding in one byte
+ if (!out.hasRemaining)
+ finalize(1, CoderResult.OVERFLOW)
+ else {
+ out.put(c1.toByte)
+ loop()
+ }
+ } else if (c1 < 0x800) {
+ // Encoding in 2 bytes (by construction, not a surrogate)
+ if (out.remaining < 2)
+ finalize(1, CoderResult.OVERFLOW)
+ else {
+ out.put(((c1 >> 6) | 0xc0).toByte)
+ out.put(((c1 & 0x3f) | 0x80).toByte)
+ loop()
+ }
+ } else if (!isSurrogate(c1)) {
+ // Not a surrogate, encoding in 3 bytes
+ if (out.remaining < 3)
+ finalize(1, CoderResult.OVERFLOW)
+ else {
+ out.put(((c1 >> 12) | 0xe0).toByte)
+ out.put((((c1 >> 6) & 0x3f) | 0x80).toByte)
+ out.put(((c1 & 0x3f) | 0x80).toByte)
+ loop()
+ }
+ } else if (isHighSurrogate(c1)) {
+ // Should have a low surrogate that follows
+ if (!in.hasRemaining)
+ finalize(1, CoderResult.UNDERFLOW)
+ else {
+ val c2 = in.get()
+ if (!isLowSurrogate(c2)) {
+ finalize(2, CoderResult.malformedForLength(1))
+ } else {
+ // Surrogate pair, encoding in 4 bytes
+ if (out.remaining < 4)
+ finalize(2, CoderResult.OVERFLOW)
+ else {
+ val cp = toCodePoint(c1, c2)
+ out.put(((cp >> 18) | 0xf0).toByte)
+ out.put((((cp >> 12) & 0x3f) | 0x80).toByte)
+ out.put((((cp >> 6) & 0x3f) | 0x80).toByte)
+ out.put(((cp & 0x3f) | 0x80).toByte)
+ loop()
+ }
+ }
+ }
+ } else {
+ finalize(1, CoderResult.malformedForLength(1))
+ }
+ }
+ }
+
+ loop()
+ }
+ }
+
+ private final val SurrogateMask = 0xf800 // 11111 0 00 00000000
+ private final val SurrogateID = 0xd800 // 11011 0 00 00000000
+
+ @inline private def isSurrogate(c: Char): Boolean =
+ (c & SurrogateMask) == SurrogateID
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/AnonFunctions.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/AnonFunctions.scala
new file mode 100644
index 0000000..861d81a
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/AnonFunctions.scala
@@ -0,0 +1,119 @@
+package scala.scalajs.runtime
+
+import scala.scalajs.js
+import scala.runtime._
+
+@inline
+final class AnonFunction0[+R](f: js.Function0[R]) extends AbstractFunction0[R] {
+ override def apply(): R = f()
+}
+
+@inline
+final class AnonFunction1[-T1, +R](f: js.Function1[T1, R]) extends AbstractFunction1[T1, R] {
+ override def apply(arg1: T1): R = f(arg1)
+}
+
+@inline
+final class AnonFunction2[-T1, -T2, +R](f: js.Function2[T1, T2, R]) extends AbstractFunction2[T1, T2, R] {
+ override def apply(arg1: T1, arg2: T2): R = f(arg1, arg2)
+}
+
+@inline
+final class AnonFunction3[-T1, -T2, -T3, +R](f: js.Function3[T1, T2, T3, R]) extends AbstractFunction3[T1, T2, T3, R] {
+ override def apply(arg1: T1, arg2: T2, arg3: T3): R = f(arg1, arg2, arg3)
+}
+
+@inline
+final class AnonFunction4[-T1, -T2, -T3, -T4, +R](f: js.Function4[T1, T2, T3, T4, R]) extends AbstractFunction4[T1, T2, T3, T4, R] {
+ override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4): R = f(arg1, arg2, arg3, arg4)
+}
+
+@inline
+final class AnonFunction5[-T1, -T2, -T3, -T4, -T5, +R](f: js.Function5[T1, T2, T3, T4, T5, R]) extends AbstractFunction5[T1, T2, T3, T4, T5, R] {
+ override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5): R = f(arg1, arg2, arg3, arg4, arg5)
+}
+
+@inline
+final class AnonFunction6[-T1, -T2, -T3, -T4, -T5, -T6, +R](f: js.Function6[T1, T2, T3, T4, T5, T6, R]) extends AbstractFunction6[T1, T2, T3, T4, T5, T6, R] {
+ override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6): R = f(arg1, arg2, arg3, arg4, arg5, arg6)
+}
+
+@inline
+final class AnonFunction7[-T1, -T2, -T3, -T4, -T5, -T6, -T7, +R](f: js.Function7[T1, T2, T3, T4, T5, T6, T7, R]) extends AbstractFunction7[T1, T2, T3, T4, T5, T6, T7, R] {
+ override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7): R = f(arg1, arg2, arg3, arg4, arg5, arg6, arg7)
+}
+
+@inline
+final class AnonFunction8[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, +R](f: js.Function8[T1, T2, T3, T4, T5, T6, T7, T8, R]) extends AbstractFunction8[T1, T2, T3, T4, T5, T6, T7, T8, R] {
+ override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8): R = f(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
+}
+
+@inline
+final class AnonFunction9[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, +R](f: js.Function9[T1, T2, T3, T4, T5, T6, T7, T8, T9, R]) extends AbstractFunction9[T1, T2, T3, T4, T5, T6, T7, T8, T9, R] {
+ override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9): R = f(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
+}
+
+@inline
+final class AnonFunction10[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, +R](f: js.Function10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R]) extends AbstractFunction10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R] {
+ override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10): R = f(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10)
+}
+
+@inline
+final class AnonFunction11[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, +R](f: js.Function11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, R]) extends AbstractFunction11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, R] {
+ override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11): R = f(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11)
+}
+
+@inline
+final class AnonFunction12[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, +R](f: js.Function12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, R]) extends AbstractFunction12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, R] {
+ override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12): R = f(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12)
+}
+
+@inline
+final class AnonFunction13[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, +R](f: js.Function13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, R]) extends AbstractFunction13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, R] {
+ override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13): R = f(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13)
+}
+
+@inline
+final class AnonFunction14[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, +R](f: js.Function14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, R]) extends AbstractFunction14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, R] {
+ override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14): R = f(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14)
+}
+
+@inline
+final class AnonFunction15[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, +R](f: js.Function15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, R]) extends AbstractFunction15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, R] {
+ override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14, arg15: T15): R = f(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15)
+}
+
+@inline
+final class AnonFunction16[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, +R](f: js.Function16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, R]) extends AbstractFunction16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, R] {
+ override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14, arg15: T15, arg16: T16): R = f(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16)
+}
+
+@inline
+final class AnonFunction17[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, +R](f: js.Function17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, R]) extends AbstractFunction17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, R] {
+ override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14, arg15: T15, arg16: T16, arg17: T17): R = f(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17)
+}
+
+@inline
+final class AnonFunction18[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, +R](f: js.Function18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, R]) extends AbstractFunction18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, R] {
+ override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14, arg15: T15, arg16: T16, arg17: T17, arg18: T18): R = f(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18)
+}
+
+@inline
+final class AnonFunction19[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, +R](f: js.Function19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, R]) extends AbstractFunction19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, R] {
+ override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14, arg15: T15, arg16: T16, arg17: T17, arg18: T18, arg19: T19): R = f(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19)
+}
+
+@inline
+final class AnonFunction20[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, -T20, +R](f: js.Function20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, R]) extends AbstractFunction20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, R] {
+ override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14, arg15: T15, arg16: T16, arg17: T17, arg18: T18, arg19: T19, arg20: T20): R = f(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20)
+}
+
+@inline
+final class AnonFunction21[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, -T20, -T21, +R](f: js.Function21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, R]) extends AbstractFunction21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, R] {
+ override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14, arg15: T15, arg16: T16, arg17: T17, arg18: T18, arg19: T19, arg20: T20, arg21: T21): R = f(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20, arg21)
+}
+
+@inline
+final class AnonFunction22[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, -T20, -T21, -T22, +R](f: js.Function22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, R]) extends AbstractFunction22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, R] {
+ override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14, arg15: T15, arg16: T16, arg17: T17, arg18: T18, arg19: T19, arg20: T20, arg21: T21, arg22: T22): R = f(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20, arg21, arg22)
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/Bits.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/Bits.scala
new file mode 100644
index 0000000..38b2c3e
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/Bits.scala
@@ -0,0 +1,240 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.runtime
+
+import scala.scalajs.js
+import js.Dynamic.global
+import js.typedarray
+
+/** Low-level stuff. */
+object Bits {
+
+ val areTypedArraysSupported = (
+ !(!global.ArrayBuffer) && !(!global.Int32Array) &&
+ !(!global.Float32Array) && !(!global.Float64Array))
+
+ private val arrayBuffer =
+ if (areTypedArraysSupported) new typedarray.ArrayBuffer(8)
+ else null
+
+ private val int32Array =
+ if (areTypedArraysSupported) new typedarray.Int32Array(arrayBuffer, 0, 2)
+ else null
+
+ private val float32Array =
+ if (areTypedArraysSupported) new typedarray.Float32Array(arrayBuffer, 0, 2)
+ else null
+
+ private val float64Array =
+ if (areTypedArraysSupported) new typedarray.Float64Array(arrayBuffer, 0, 1)
+ else null
+
+ val areTypedArraysBigEndian = {
+ if (areTypedArraysSupported) {
+ int32Array(0) = 0x01020304
+ (new typedarray.Int8Array(arrayBuffer, 0, 8))(0) == 0x01
+ } else {
+ true // as good a value as any
+ }
+ }
+
+ private val highOffset = if (areTypedArraysBigEndian) 0 else 1
+ private val lowOffset = if (areTypedArraysBigEndian) 1 else 0
+
+ /** Hash code of a number (excluding Longs).
+ *
+ * Because of the common encoding for integer and floating point values,
+ * the hashCode of Floats and Doubles must align with that of Ints for the
+ * common values.
+ *
+ * For other values, we use the hashCode specified by the JavaDoc for
+ * *Doubles*, even for values which are valid Float values. Because of the
+ * previous point, we cannot align completely with the Java specification,
+ * so there is no point trying to be a bit more aligned here. Always using
+ * the Double version should typically be faster on VMs without fround
+ * support because we avoid several fround operations.
+ */
+ def numberHashCode(value: Double): Int = {
+ val iv = value.toInt
+ if (iv == value) iv
+ else doubleToLongBits(value).hashCode()
+ }
+
+ def intBitsToFloat(bits: Int): Float = {
+ if (areTypedArraysSupported) {
+ int32Array(0) = bits
+ float32Array(0)
+ } else {
+ intBitsToFloatPolyfill(bits).toFloat
+ }
+ }
+
+ def floatToIntBits(value: Float): Int = {
+ if (areTypedArraysSupported) {
+ float32Array(0) = value
+ int32Array(0)
+ } else {
+ floatToIntBitsPolyfill(value.toDouble)
+ }
+ }
+
+ def longBitsToDouble(bits: Long): Double = {
+ if (areTypedArraysSupported) {
+ int32Array(highOffset) = (bits >>> 32).toInt
+ int32Array(lowOffset) = bits.toInt
+ float64Array(0)
+ } else {
+ longBitsToDoublePolyfill(bits)
+ }
+ }
+
+ def doubleToLongBits(value: Double): Long = {
+ if (areTypedArraysSupported) {
+ float64Array(0) = value
+ ((int32Array(highOffset).toLong << 32) |
+ (int32Array(lowOffset).toLong & 0xffffffffL))
+ } else {
+ doubleToLongBitsPolyfill(value)
+ }
+ }
+
+ /* --- Polyfills for floating point bit manipulations ---
+ *
+ * Originally inspired by
+ * https://github.com/inexorabletash/polyfill/blob/a682f42c1092280bb01907c245979fb07219513d/typedarray.js#L150-L255
+ *
+ * Note that if typed arrays are not supported, it is almost certain that
+ * fround is not supported natively, so Float operations are extremely slow.
+ *
+ * We therefore do all computations in Doubles here, which is also more
+ * predictable, since the results do not depend on strict floats semantics.
+ */
+
+ private def intBitsToFloatPolyfill(bits: Int): Double = {
+ val ebits = 8
+ val fbits = 23
+ val s = bits < 0
+ val e = (bits >> fbits) & ((1 << ebits) - 1)
+ val f = bits & ((1 << fbits) - 1)
+ decodeIEEE754(ebits, fbits, s, e, f)
+ }
+
+ private def floatToIntBitsPolyfill(value: Double): Int = {
+ val ebits = 8
+ val fbits = 23
+ val (s, e, f) = encodeIEEE754(ebits, fbits, value)
+ (if (s) 0x80000000 else 0) | (e << fbits) | f.toInt
+ }
+
+ private def longBitsToDoublePolyfill(bits: Long): Double = {
+ val ebits = 11
+ val fbits = 52
+ val hifbits = fbits-32
+ val hi = (bits >>> 32).toInt
+ val lo = ((bits.toInt: js.prim.Number) >>> 0).toDouble
+ val s = hi < 0
+ val e = (hi >> hifbits) & ((1 << ebits) - 1)
+ val f = (hi & ((1 << hifbits) - 1)).toDouble * 0x100000000L.toDouble + lo
+ decodeIEEE754(ebits, fbits, s, e, f)
+ }
+
+ private def doubleToLongBitsPolyfill(value: Double): Long = {
+ val ebits = 11
+ val fbits = 52
+ val hifbits = fbits-32
+ val (s, e, f) = encodeIEEE754(ebits, fbits, value)
+ val hif = (f / 0x100000000L.toDouble).toInt
+ val hi = (if (s) 0x80000000 else 0) | (e << hifbits) | hif
+ val lo = f.toInt
+ (hi.toLong << 32) | (lo.toLong & 0xffffffffL)
+ }
+
+ @inline private def decodeIEEE754(ebits: Int, fbits: Int,
+ s: Boolean, e: Int, f: Double): Double = {
+
+ import Math.pow
+
+ val bias = (1 << (ebits-1)) - 1 // constant
+
+ if (e == (1 << ebits) - 1) {
+ // Special
+ if (f != 0.0) Double.NaN
+ else if (s) Double.NegativeInfinity
+ else Double.PositiveInfinity
+ } else if (e > 0) {
+ // Normalized
+ val x = pow(2, e-bias) * (1 + f / pow(2, fbits))
+ if (s) -x else x
+ } else if (f != 0.0) {
+ // Subnormal
+ val x = pow(2, -(bias-1)) * (f / pow(2, fbits))
+ if (s) -x else x
+ } else {
+ // Zero
+ if (s) -0.0 else 0.0
+ }
+ }
+
+ @inline private def encodeIEEE754(ebits: Int, fbits: Int,
+ v: Double): (Boolean, Int, Double) = {
+
+ import Math._
+
+ val bias = (1 << (ebits-1)) - 1 // constant
+
+ if (v.isNaN) {
+ // http://dev.w3.org/2006/webapi/WebIDL/#es-type-mapping
+ (false, (1 << ebits) - 1, pow(2, fbits-1))
+ } else if (v.isInfinite) {
+ (v < 0, (1 << ebits) - 1, 0.0)
+ } else if (v == 0.0) {
+ (1 / v == Double.NegativeInfinity, 0, 0.0)
+ } else {
+ val LN2 = 0.6931471805599453
+
+ val s = v < 0
+ val av = if (s) -v else v
+
+ if (av >= pow(2, 1-bias)) {
+ val twoPowFbits = pow(2, fbits)
+
+ var e = min(floor(log(av) / LN2).toInt, 1023)
+ var f = roundToEven(av / pow(2, e) * twoPowFbits)
+ if (f / twoPowFbits >= 2) {
+ e = e + 1
+ f = 1
+ }
+ if (e > bias) {
+ // Overflow
+ e = (1 << ebits) - 1
+ f = 0
+ } else {
+ // Normalized
+ e = e + bias
+ f = f - twoPowFbits
+ }
+ (s, e, f)
+ } else {
+ // Subnormal
+ (s, 0, roundToEven(av / pow(2, 1-bias-fbits)))
+ }
+ }
+ }
+
+ @inline private[runtime] def roundToEven(n: Double): Double = {
+ val w = Math.floor(n)
+ val f = n - w
+ if (f < 0.5) w
+ else if (f > 0.5) w + 1
+ else if (w % 2 != 0) w + 1
+ else w
+ }
+
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/BooleanReflectiveCall.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/BooleanReflectiveCall.scala
new file mode 100644
index 0000000..0cd562a
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/BooleanReflectiveCall.scala
@@ -0,0 +1,31 @@
+package scala.scalajs.runtime
+
+import java.lang.{Boolean => JBoolean}
+
+/** Explicit box for boolean values when doing a reflective call.
+ * This class and its methods are only here to properly support reflective
+ * calls on booleans.
+ */
+class BooleanReflectiveCall(value: Boolean) {
+
+ // Methods of java.lang.Boolean
+
+ def booleanValue(): Boolean = value
+
+ def compareTo(that: JBoolean): Int =
+ new JBoolean(value).compareTo(that)
+ def compareTo(that: AnyRef): Int =
+ new JBoolean(value).compareTo(that.asInstanceOf[JBoolean])
+
+ // Methods of scala.Boolean
+
+ def unary_! : Boolean = !value
+ def ==(x: Boolean): Boolean = value == x
+ def !=(x: Boolean): Boolean = value != x
+ def ||(x: Boolean): Boolean = value || x
+ def &&(x: Boolean): Boolean = value && x
+ def |(x: Boolean): Boolean = value | x
+ def &(x: Boolean): Boolean = value & x
+ def ^(x: Boolean): Boolean = value ^ x
+
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/IntegerReflectiveCall.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/IntegerReflectiveCall.scala
new file mode 100644
index 0000000..ddf65df
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/IntegerReflectiveCall.scala
@@ -0,0 +1,87 @@
+package scala.scalajs.runtime
+
+import java.lang.{Double => JDouble, Integer => JInteger}
+
+/** Explicit box for number values when doing a reflective call that was
+ * identified to be a call on Int rather than on Double (based on the
+ * result type of the method called reflectively).
+ * This class and its methods are only here to properly support reflective
+ * calls on numbers.
+ */
+class IntegerReflectiveCall(value: Int) {
+
+ // Methods of scala.Int whose result type is different than in scala.Double
+
+ def unary_+ : scala.Int = value
+ def unary_- : scala.Int = -value
+
+ def +(x: scala.Byte): scala.Int = value + x
+ def +(x: scala.Short): scala.Int = value + x
+ def +(x: scala.Char): scala.Int = value + x
+ def +(x: scala.Int): scala.Int = value + x
+ def +(x: scala.Long): scala.Long = value + x
+ def +(x: scala.Float): scala.Float = value + x
+ def +(x: scala.Double): scala.Double = value + x
+
+ def -(x: scala.Byte): scala.Int = value - x
+ def -(x: scala.Short): scala.Int = value - x
+ def -(x: scala.Char): scala.Int = value - x
+ def -(x: scala.Int): scala.Int = value - x
+ def -(x: scala.Long): scala.Long = value - x
+ def -(x: scala.Float): scala.Float = value - x
+ def -(x: scala.Double): scala.Double = value - x
+
+ def *(x: scala.Byte): scala.Int = value * x
+ def *(x: scala.Short): scala.Int = value * x
+ def *(x: scala.Char): scala.Int = value * x
+ def *(x: scala.Int): scala.Int = value * x
+ def *(x: scala.Long): scala.Long = value * x
+ def *(x: scala.Float): scala.Float = value * x
+ def *(x: scala.Double): scala.Double = value * x
+
+ def /(x: scala.Byte): scala.Int = value / x
+ def /(x: scala.Short): scala.Int = value / x
+ def /(x: scala.Char): scala.Int = value / x
+ def /(x: scala.Int): scala.Int = value / x
+ def /(x: scala.Long): scala.Long = value / x
+ def /(x: scala.Float): scala.Float = value / x
+ def /(x: scala.Double): scala.Double = value / x
+
+ def %(x: scala.Byte): scala.Int = value % x
+ def %(x: scala.Short): scala.Int = value % x
+ def %(x: scala.Char): scala.Int = value % x
+ def %(x: scala.Int): scala.Int = value % x
+ def %(x: scala.Long): scala.Long = value % x
+ def %(x: scala.Float): scala.Float = value % x
+ def %(x: scala.Double): scala.Double = value % x
+
+ // Methods of scala.Int that are not defined on scala.Double
+
+ def unary_~ : scala.Int = ~value
+
+ def <<(x: scala.Int): scala.Int = value << x
+ def <<(x: scala.Long): scala.Int = value << x
+ def >>>(x: scala.Int): scala.Int = value >>> x
+ def >>>(x: scala.Long): scala.Int = value >>> x
+ def >>(x: scala.Int): scala.Int = value >> x
+ def >>(x: scala.Long): scala.Int = value >> x
+
+ def |(x: scala.Byte): scala.Int = value | x
+ def |(x: scala.Short): scala.Int = value | x
+ def |(x: scala.Char): scala.Int = value | x
+ def |(x: scala.Int): scala.Int = value | x
+ def |(x: scala.Long): scala.Long = value | x
+
+ def &(x: scala.Byte): scala.Int = value & x
+ def &(x: scala.Short): scala.Int = value & x
+ def &(x: scala.Char): scala.Int = value & x
+ def &(x: scala.Int): scala.Int = value & x
+ def &(x: scala.Long): scala.Long = value & x
+
+ def ^(x: scala.Byte): scala.Int = value ^ x
+ def ^(x: scala.Short): scala.Int = value ^ x
+ def ^(x: scala.Char): scala.Int = value ^ x
+ def ^(x: scala.Int): scala.Int = value ^ x
+ def ^(x: scala.Long): scala.Long = value ^ x
+
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/NumberReflectiveCall.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/NumberReflectiveCall.scala
new file mode 100644
index 0000000..a237861
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/NumberReflectiveCall.scala
@@ -0,0 +1,162 @@
+package scala.scalajs.runtime
+
+import java.lang.{Double => JDouble, Integer => JInteger}
+
+/** Explicit box for number values when doing a reflective call.
+ * This class and its methods are only here to properly support reflective
+ * calls on numbers.
+ */
+class NumberReflectiveCall(value: Double) {
+
+ // Methods of java.lang.Double and java.lang.Integer
+
+ def byteValue(): Byte = value.toByte
+ def shortValue(): Short = value.toShort
+ def intValue(): Int = value.toInt
+ def longValue(): scala.Long = value.toLong
+ def floatValue(): Float = value.toFloat
+ def doubleValue(): Double = value
+
+ def compareTo(that: JDouble): Int =
+ new JDouble(value).compareTo(that)
+ def compareTo(that: JInteger): Int =
+ new JDouble(value).compareTo(new JDouble(that.doubleValue()))
+ def compareTo(that: AnyRef): Int =
+ new JDouble(value).compareTo(that.asInstanceOf[JDouble])
+
+ def isNaN(): scala.Boolean = new JDouble(value).isNaN()
+ def isInfinite(): scala.Boolean = new JDouble(value).isInfinite()
+
+ // Methods of scala.Double
+
+ def toByte: scala.Byte = value.toByte
+ def toShort: scala.Short = value.toShort
+ def toChar: scala.Char = value.toChar
+ def toInt: scala.Int = value.toInt
+ def toLong: scala.Long = value.toLong
+ def toFloat: scala.Float = value.toFloat
+ def toDouble: scala.Double = value
+
+ def unary_+ : scala.Double = value
+ def unary_- : scala.Double = -value
+
+ def +(x: String): String = value + x
+
+ def ==(x: scala.Byte): scala.Boolean = value == x
+ def ==(x: scala.Short): scala.Boolean = value == x
+ def ==(x: scala.Char): scala.Boolean = value == x
+ def ==(x: scala.Int): scala.Boolean = value == x
+ def ==(x: scala.Long): scala.Boolean = value == x
+ def ==(x: scala.Float): scala.Boolean = value == x
+ def ==(x: scala.Double): scala.Boolean = value == x
+
+ def !=(x: scala.Byte): scala.Boolean = value != x
+ def !=(x: scala.Short): scala.Boolean = value != x
+ def !=(x: scala.Char): scala.Boolean = value != x
+ def !=(x: scala.Int): scala.Boolean = value != x
+ def !=(x: scala.Long): scala.Boolean = value != x
+ def !=(x: scala.Float): scala.Boolean = value != x
+ def !=(x: scala.Double): scala.Boolean = value != x
+
+ def <(x: scala.Byte): scala.Boolean = value < x
+ def <(x: scala.Short): scala.Boolean = value < x
+ def <(x: scala.Char): scala.Boolean = value < x
+ def <(x: scala.Int): scala.Boolean = value < x
+ def <(x: scala.Long): scala.Boolean = value < x
+ def <(x: scala.Float): scala.Boolean = value < x
+ def <(x: scala.Double): scala.Boolean = value < x
+
+ def <=(x: scala.Byte): scala.Boolean = value <= x
+ def <=(x: scala.Short): scala.Boolean = value <= x
+ def <=(x: scala.Char): scala.Boolean = value <= x
+ def <=(x: scala.Int): scala.Boolean = value <= x
+ def <=(x: scala.Long): scala.Boolean = value <= x
+ def <=(x: scala.Float): scala.Boolean = value <= x
+ def <=(x: scala.Double): scala.Boolean = value <= x
+
+ def >(x: scala.Byte): scala.Boolean = value > x
+ def >(x: scala.Short): scala.Boolean = value > x
+ def >(x: scala.Char): scala.Boolean = value > x
+ def >(x: scala.Int): scala.Boolean = value > x
+ def >(x: scala.Long): scala.Boolean = value > x
+ def >(x: scala.Float): scala.Boolean = value > x
+ def >(x: scala.Double): scala.Boolean = value > x
+
+ def >=(x: scala.Byte): scala.Boolean = value >= x
+ def >=(x: scala.Short): scala.Boolean = value >= x
+ def >=(x: scala.Char): scala.Boolean = value >= x
+ def >=(x: scala.Int): scala.Boolean = value >= x
+ def >=(x: scala.Long): scala.Boolean = value >= x
+ def >=(x: scala.Float): scala.Boolean = value >= x
+ def >=(x: scala.Double): scala.Boolean = value >= x
+
+ def +(x: scala.Byte): scala.Double = value + x
+ def +(x: scala.Short): scala.Double = value + x
+ def +(x: scala.Char): scala.Double = value + x
+ def +(x: scala.Int): scala.Double = value + x
+ def +(x: scala.Long): scala.Double = value + x
+ def +(x: scala.Float): scala.Double = value + x
+ def +(x: scala.Double): scala.Double = value + x
+
+ def -(x: scala.Byte): scala.Double = value - x
+ def -(x: scala.Short): scala.Double = value - x
+ def -(x: scala.Char): scala.Double = value - x
+ def -(x: scala.Int): scala.Double = value - x
+ def -(x: scala.Long): scala.Double = value - x
+ def -(x: scala.Float): scala.Double = value - x
+ def -(x: scala.Double): scala.Double = value - x
+
+ def *(x: scala.Byte): scala.Double = value * x
+ def *(x: scala.Short): scala.Double = value * x
+ def *(x: scala.Char): scala.Double = value * x
+ def *(x: scala.Int): scala.Double = value * x
+ def *(x: scala.Long): scala.Double = value * x
+ def *(x: scala.Float): scala.Double = value * x
+ def *(x: scala.Double): scala.Double = value * x
+
+ def /(x: scala.Byte): scala.Double = value / x
+ def /(x: scala.Short): scala.Double = value / x
+ def /(x: scala.Char): scala.Double = value / x
+ def /(x: scala.Int): scala.Double = value / x
+ def /(x: scala.Long): scala.Double = value / x
+ def /(x: scala.Float): scala.Double = value / x
+ def /(x: scala.Double): scala.Double = value / x
+
+ def %(x: scala.Byte): scala.Double = value % x
+ def %(x: scala.Short): scala.Double = value % x
+ def %(x: scala.Char): scala.Double = value % x
+ def %(x: scala.Int): scala.Double = value % x
+ def %(x: scala.Long): scala.Double = value % x
+ def %(x: scala.Float): scala.Double = value % x
+ def %(x: scala.Double): scala.Double = value % x
+
+ // Methods of scala.Int that are not defined on scala.Double
+
+ def unary_~ : scala.Int = ~value.toInt
+
+ def <<(x: scala.Int): scala.Int = value.toInt << x
+ def <<(x: scala.Long): scala.Int = value.toInt << x
+ def >>>(x: scala.Int): scala.Int = value.toInt >>> x
+ def >>>(x: scala.Long): scala.Int = value.toInt >>> x
+ def >>(x: scala.Int): scala.Int = value.toInt >> x
+ def >>(x: scala.Long): scala.Int = value.toInt >> x
+
+ def |(x: scala.Byte): scala.Int = value.toInt | x
+ def |(x: scala.Short): scala.Int = value.toInt | x
+ def |(x: scala.Char): scala.Int = value.toInt | x
+ def |(x: scala.Int): scala.Int = value.toInt | x
+ def |(x: scala.Long): scala.Long = value.toInt | x
+
+ def &(x: scala.Byte): scala.Int = value.toInt & x
+ def &(x: scala.Short): scala.Int = value.toInt & x
+ def &(x: scala.Char): scala.Int = value.toInt & x
+ def &(x: scala.Int): scala.Int = value.toInt & x
+ def &(x: scala.Long): scala.Long = value.toInt & x
+
+ def ^(x: scala.Byte): scala.Int = value.toInt ^ x
+ def ^(x: scala.Short): scala.Int = value.toInt ^ x
+ def ^(x: scala.Char): scala.Int = value.toInt ^ x
+ def ^(x: scala.Int): scala.Int = value.toInt ^ x
+ def ^(x: scala.Long): scala.Long = value.toInt ^ x
+
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/RuntimeLong.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/RuntimeLong.scala
new file mode 100644
index 0000000..3bd6fb6
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/RuntimeLong.scala
@@ -0,0 +1,686 @@
+package scala.scalajs.runtime
+
+import scala.annotation.tailrec
+
+/**
+ * emulate a Java-Long using three integers.
+ * taken from gwt LongLib:
+ * com.google.gwt.lang.LongLib
+ *
+ * only used by runtime
+ *
+ * holds values l, m, h (low, middle, high)
+ * s.t. (x.l + ((long) x.m << 22) + ((long) x.h << 44)) is equal to
+ * the original value
+ */
+final class RuntimeLong(
+ val l: Int,
+ val m: Int,
+ val h: Int
+) extends Number with Comparable[java.lang.Long] { x =>
+
+ import RuntimeLong._
+
+ /** Construct from an Int.
+ * This is the implementation of RuntimeLong.fromInt() in a way that does not
+ * require to load to module RuntimeLong.
+ */
+ def this(value: Int) = this(
+ value & RuntimeLong.MASK,
+ (value >> RuntimeLong.BITS) & RuntimeLong.MASK,
+ if (value < 0) RuntimeLong.MASK_2 else 0)
+
+ /** Creates a new RuntimeLong but masks bits as follows:
+ * l & MASK, m & MASK, h & MASK_2
+ */
+ @inline private def masked(l: Int, m: Int, h: Int) =
+ new RuntimeLong(l & MASK, m & MASK, h & MASK_2)
+
+ def toByte: Byte = toInt.toByte
+ def toShort: Short = toInt.toShort
+ def toChar: Char = toInt.toChar
+ def toInt: Int = l | (m << BITS)
+ def toLong: Long = x.asInstanceOf[Long]
+ def toFloat: Float = toDouble.toFloat
+ def toDouble: Double =
+ if (isMinValue) -9223372036854775808.0
+ else if (isNegative) -((-x).toDouble)
+ else l + m * TWO_PWR_22_DBL + h * TWO_PWR_44_DBL
+
+ // java.lang.Number
+ override def byteValue(): Byte = toByte
+ override def shortValue(): Short = toShort
+ def intValue(): Int = toInt
+ def longValue(): Long = toLong
+ def floatValue(): Float = toFloat
+ def doubleValue(): Double = toDouble
+
+ // java.lang.Comparable + overload taking scala.Long
+ def compareTo(that: RuntimeLong): Int =
+ if (this equals that) 0 else if (this > that) 1 else -1
+ def compareTo(that: java.lang.Long): Int =
+ compareTo(that.asInstanceOf[RuntimeLong])
+
+ def unary_~ : RuntimeLong = masked(~x.l, ~x.m, ~x.h)
+ def unary_+ : RuntimeLong = x
+ def unary_- : RuntimeLong = {
+ val neg0 = (~x.l + 1) & MASK
+ val neg1 = (~x.m + (if (neg0 == 0) 1 else 0)) & MASK
+ val neg2 = (~x.h + (if (neg0 == 0 && neg1 == 0) 1 else 0)) & MASK_2
+ new RuntimeLong(neg0, neg1, neg2)
+ }
+
+ def +(y: String): String = x.toString + y
+
+ def <<(n_in: Int): RuntimeLong = {
+ /* crop MSB. Note: This will cause (2L << 65 == 2L << 1)
+ * apparently this is as specified
+ */
+ val n = n_in & 63
+
+ if (n < BITS) {
+ val remBits = BITS - n
+ masked(x.l << n,
+ (x.m << n) | (x.l >> remBits),
+ (x.h << n) | (x.m >> remBits))
+ } else if (n < BITS01) {
+ val shfBits = n - BITS
+ val remBits = BITS01 - n
+ masked(0, x.l << shfBits, (x.m << shfBits) | (x.l >> remBits))
+ } else {
+ masked(0, 0, x.l << (n - BITS01))
+ }
+
+ }
+
+ /**
+ * logical right shift
+ */
+ def >>>(n_in: Int): RuntimeLong = {
+ val n = n_in & 63
+ if (n < BITS) {
+ val remBits = BITS - n
+ masked((x.l >> n) | (x.m << remBits),
+ // FIXME is this really >> and not >>>??
+ (x.m >> n) | (x.h << remBits),
+ x.h >>> n)
+ } else if (n < BITS01) {
+ val shfBits = n - BITS
+ val remBits = BITS01 - n
+ // FIXME is this really >> and not >>>??
+ masked((x.m >> shfBits) | (x.h << remBits),
+ x.h >>> shfBits, 0)
+ } else {
+ masked(x.h >>> (n - BITS01), 0, 0)
+ }
+ }
+
+ /**
+ * arithmetic right shift
+ */
+ def >>(n_in: Int): RuntimeLong = {
+ val n = n_in & 63;
+
+ // Sign extend x.h
+ val negative = (x.h & SIGN_BIT_VALUE) != 0
+ val xh = if (negative) x.h | ~MASK_2 else x.h
+
+ if (n < BITS) {
+ val remBits = BITS - n
+ // FIXME IMHO the first two >> should be >>>
+ masked((x.l >> n) | (x.m << remBits),
+ (x.m >> n) | (xh << remBits),
+ xh >> n)
+ } else if (n < BITS01) {
+ val shfBits = n - BITS
+ val remBits = BITS01 - n
+ // FIXME IMHO the first >> should be >>>
+ masked((x.m >> shfBits) | (xh << remBits),
+ xh >> shfBits,
+ if (negative) MASK_2 else 0)
+ } else {
+ masked(xh >> (n - BITS01),
+ if (negative) MASK else 0,
+ if (negative) MASK_2 else 0)
+ }
+
+ }
+
+ def equals(y: RuntimeLong): Boolean =
+ x.l == y.l && x.m == y.m && x.h == y.h
+
+ override def equals(that: Any): Boolean = that match {
+ case y: RuntimeLong => x.equals(y)
+ case _ => false
+ }
+
+ def notEquals(that: RuntimeLong) = !equals(that)
+
+ override def hashCode(): Int = {
+ (this ^ (this >>> 32)).toInt
+ }
+
+ @inline
+ def <(y: RuntimeLong): Boolean = y > x
+ @inline
+ def <=(y: RuntimeLong): Boolean = y >= x
+
+ def >(y: RuntimeLong): Boolean = {
+ if (!x.isNegative)
+ y.isNegative ||
+ x.h > y.h ||
+ x.h == y.h && x.m > y.m ||
+ x.h == y.h && x.m == y.m && x.l > y.l
+ else !(
+ !y.isNegative ||
+ x.h < y.h ||
+ x.h == y.h && x.m < y.m ||
+ x.h == y.h && x.m == y.m && x.l <= y.l
+ )
+ }
+
+ def >=(y: RuntimeLong): Boolean = {
+ if (!x.isNegative)
+ y.isNegative ||
+ x.h > y.h ||
+ x.h == y.h && x.m > y.m ||
+ x.h == y.h && x.m == y.m && x.l >= y.l
+ else !(
+ !y.isNegative ||
+ x.h < y.h ||
+ x.h == y.h && x.m < y.m ||
+ x.h == y.h && x.m == y.m && x.l < y.l
+ )
+ }
+
+ def |(y: RuntimeLong): RuntimeLong =
+ new RuntimeLong(x.l | y.l, x.m | y.m, x.h | y.h)
+ def &(y: RuntimeLong): RuntimeLong =
+ new RuntimeLong(x.l & y.l, x.m & y.m, x.h & y.h)
+ def ^(y: RuntimeLong): RuntimeLong =
+ new RuntimeLong(x.l ^ y.l, x.m ^ y.m, x.h ^ y.h)
+
+ def +(y: RuntimeLong): RuntimeLong = {
+ val sum0 = x.l + y.l
+ val sum1 = x.m + y.m + (sum0 >> BITS)
+ val sum2 = x.h + y.h + (sum1 >> BITS)
+ masked(sum0, sum1, sum2)
+ }
+
+ /**
+ * subtraction
+ * note: gwt implements this individually
+ */
+ def -(y: RuntimeLong): RuntimeLong = x + (-y)
+
+ // This assumes that BITS == 22
+ def *(y: RuntimeLong): RuntimeLong = {
+
+ /** divides v in 13bit chunks */
+ @inline def chunk13(v: RuntimeLong) = (
+ v.l & 0x1fff,
+ (v.l >> 13) | ((v.m & 0xf) << 9),
+ (v.m >> 4) & 0x1fff,
+ (v.m >> 17) | ((v.h & 0xff) << 5),
+ (v.h & 0xfff00) >> 8
+ )
+
+ val (a0, a1, a2, a3, a4) = chunk13(x)
+ val (b0, b1, b2, b3, b4) = chunk13(y)
+
+ // Compute partial products
+ // Optimization: if b is small, avoid multiplying by parts that are 0
+ var p0 = a0 * b0; // << 0
+ var p1 = a1 * b0; // << 13
+ var p2 = a2 * b0; // << 26
+ var p3 = a3 * b0; // << 39
+ var p4 = a4 * b0; // << 52
+
+ if (b1 != 0) {
+ p1 += a0 * b1;
+ p2 += a1 * b1;
+ p3 += a2 * b1;
+ p4 += a3 * b1;
+ }
+ if (b2 != 0) {
+ p2 += a0 * b2;
+ p3 += a1 * b2;
+ p4 += a2 * b2;
+ }
+ if (b3 != 0) {
+ p3 += a0 * b3;
+ p4 += a1 * b3;
+ }
+ if (b4 != 0) {
+ p4 += a0 * b4;
+ }
+
+ // Accumulate into 22-bit chunks:
+ // .........................................c10|...................c00|
+ // |....................|..................xxxx|xxxxxxxxxxxxxxxxxxxxxx| p0
+ // |....................|......................|......................|
+ // |....................|...................c11|......c01.............|
+ // |....................|....xxxxxxxxxxxxxxxxxx|xxxxxxxxx.............| p1
+ // |....................|......................|......................|
+ // |.................c22|...............c12....|......................|
+ // |..........xxxxxxxxxx|xxxxxxxxxxxxxxxxxx....|......................| p2
+ // |....................|......................|......................|
+ // |.................c23|..c13.................|......................|
+ // |xxxxxxxxxxxxxxxxxxxx|xxxxx.................|......................| p3
+ // |....................|......................|......................|
+ // |.........c24........|......................|......................|
+ // |xxxxxxxxxxxx........|......................|......................| p4
+
+ val c00 = p0 & 0x3fffff;
+ val c01 = (p1 & 0x1ff) << 13;
+ val c0 = c00 + c01;
+
+ val c10 = p0 >> 22;
+ val c11 = p1 >> 9;
+ val c12 = (p2 & 0x3ffff) << 4;
+ val c13 = (p3 & 0x1f) << 17;
+ val c1 = c10 + c11 + c12 + c13;
+
+ val c22 = p2 >> 18;
+ val c23 = p3 >> 5;
+ val c24 = (p4 & 0xfff) << 8;
+ val c2 = c22 + c23 + c24;
+
+ // Propagate high bits from c0 -> c1, c1 -> c2
+ val c1n = c1 + (c0 >> BITS)
+
+ masked(c0, c1n, c2 + (c1n >> BITS))
+ }
+
+ def /(y: RuntimeLong): RuntimeLong = (x divMod y)(0)
+ def %(y: RuntimeLong): RuntimeLong = (x divMod y)(1)
+
+ //override def getClass(): Class[Long] = null
+
+ def toBinaryString: String = {
+ val zeros = "0000000000000000000000" // 22 zeros
+ @inline def padBinary22(i: Int) = {
+ val s = Integer.toBinaryString(i)
+ zeros.substring(s.length) + s
+ }
+
+ if (h != 0) Integer.toBinaryString(h) + padBinary22(m) + padBinary22(l)
+ else if (m != 0) Integer.toBinaryString(m) + padBinary22(l)
+ else Integer.toBinaryString(l)
+ }
+
+ def toHexString: String = {
+ val zeros = "000000" // 6 zeros
+ @inline def padHex(i: Int, len: Int) = {
+ val s = Integer.toHexString(i)
+ zeros.substring(s.length + (6-len)) + s
+ }
+
+ val mp = m >> 2
+ val lp = l | ((m & 0x3) << BITS)
+
+ if (h != 0) Integer.toHexString(h) + padHex(mp, 5) + padHex(lp, 6)
+ else if (mp != 0) Integer.toHexString(mp) + padHex(lp, 6)
+ else Integer.toHexString(lp)
+ }
+
+ def toOctalString: String = {
+ val zeros = "0000000" // 7 zeros
+ @inline def padOctal7(i: Int) = {
+ val s = Integer.toOctalString(i)
+ zeros.substring(s.length) + s
+ }
+
+ val lp = l & (MASK >> 1)
+ val mp = ((m & (MASK >> 2)) << 1) | (l >> (BITS - 1))
+ val hp = (h << 2) | (m >> (BITS - 2))
+
+ if (hp != 0) Integer.toOctalString(hp) + padOctal7(mp) + padOctal7(lp)
+ else if (mp != 0) Integer.toOctalString(mp) + padOctal7(lp)
+ else Integer.toOctalString(lp)
+ }
+
+ // Any API //
+
+ override def toString: String = {
+ if (isZero) "0"
+ // Check for MinValue, because its not negatable
+ else if (isMinValue) "-9223372036854775808"
+ else if (isNegative) "-" + (-x).toString
+ else {
+ val tenPow9 = TenPow9 // local copy to access CachedConstants only once
+
+ @tailrec
+ @inline
+ def toString0(v: RuntimeLong, acc: String): String =
+ if (v.isZero) acc
+ else {
+ val quotRem = v.divMod(tenPow9)
+ val quot = quotRem(0)
+ val rem = quotRem(1)
+
+ val digits = rem.toInt.toString
+ val zeroPrefix =
+ if (quot.isZero) ""
+ else "000000000".substring(digits.length) // (9 - digits.length) zeros
+
+ toString0(quot, zeroPrefix + digits + acc)
+ }
+
+ toString0(x, "")
+ }
+ }
+
+ def bitCount: Int =
+ Integer.bitCount(l) + Integer.bitCount(m) + Integer.bitCount(h)
+
+ // helpers //
+
+ @inline private def isZero = l == 0 && m == 0 && h == 0
+ @inline private def isMinValue = x.equals(MinValue)
+ @inline private def isNegative = (h & SIGN_BIT_VALUE) != 0
+ @inline private def abs = if (isNegative) -x else x
+
+ def signum: RuntimeLong =
+ if (isNegative) MinusOne else if (isZero) Zero else One
+
+ def numberOfLeadingZeros: Int =
+ if (h != 0) Integer.numberOfLeadingZeros(h) - (32 - BITS2)
+ else if (m != 0) Integer.numberOfLeadingZeros(m) - (32 - BITS) + (64 - BITS01)
+ else Integer.numberOfLeadingZeros(l) - (32 - BITS) + (64 - BITS)
+
+ def numberOfTrailingZeros: Int =
+ if (l != 0) Integer.numberOfTrailingZeros(l)
+ else if (m != 0) Integer.numberOfTrailingZeros(m) + BITS
+ else Integer.numberOfTrailingZeros(h) + BITS01
+
+ /** return log_2(x) if power of 2 or -1 otherwise */
+ private def powerOfTwo =
+ if (h == 0 && m == 0 && l != 0 && (l & (l - 1)) == 0)
+ Integer.numberOfTrailingZeros(l)
+ else if (h == 0 && m != 0 && l == 0 && (m & (m - 1)) == 0)
+ Integer.numberOfTrailingZeros(m) + BITS
+ else if (h != 0 && m == 0 && l == 0 && (h & (h - 1)) == 0)
+ Integer.numberOfTrailingZeros(h) + BITS01
+ else
+ -1
+
+ private def setBit(bit: Int) =
+ if (bit < BITS)
+ new RuntimeLong(l | (1 << bit), m, h)
+ else if (bit < BITS01)
+ new RuntimeLong(l, m | (1 << (bit - BITS)), h)
+ else
+ new RuntimeLong(l, m, h | (1 << (bit - BITS01)))
+
+ private def divMod(y: RuntimeLong): scala.scalajs.js.Array[RuntimeLong] = {
+ import scala.scalajs.js
+ if (y.isZero) throw new ArithmeticException("/ by zero")
+ else if (x.isZero) js.Array(Zero, Zero)
+ else if (y.isMinValue) {
+ // MinValue / MinValue == 1, rem = 0
+ // otherwise == 0, rem x
+ if (x.isMinValue) js.Array(One, Zero)
+ else js.Array(Zero, x)
+ } else {
+ val xNegative = x.isNegative
+ val yNegative = y.isNegative
+
+ val xMinValue = x.isMinValue
+
+ val pow = y.powerOfTwo
+ if (pow >= 0) {
+ if (xMinValue) {
+ val z = x >> pow
+ js.Array(if (yNegative) -z else z, Zero)
+ } else {
+ // x is not min value, so we can calculate absX
+ val absX = x.abs
+ val absZ = absX >> pow
+ val z = if (xNegative ^ yNegative) -absZ else absZ
+ val remAbs = absX.maskRight(pow)
+ val rem = if (xNegative) -remAbs else remAbs
+ js.Array(z, rem)
+ }
+ } else {
+ val absY = y.abs
+
+ val newX = {
+ if (xMinValue)
+ MaxValue
+ else {
+ val absX = x.abs
+ if (absX < absY)
+ return js.Array(Zero, x) // <-- ugly but fast
+ else
+ absX
+ }
+ }
+ divModHelper(newX, absY, xNegative, yNegative, xMinValue)
+ }
+ }
+ }
+
+ @inline
+ private def maskRight(bits: Int) = {
+ if (bits <= BITS)
+ new RuntimeLong(l & ((1 << bits) - 1), 0, 0)
+ else if (bits <= BITS01)
+ new RuntimeLong(l, m & ((1 << (bits - BITS)) - 1), 0)
+ else
+ new RuntimeLong(l, m, h & ((1 << (bits - BITS01)) - 1))
+ }
+
+ /**
+ * performs division in "normal cases"
+ * @param x absolute value of the numerator
+ * @param y absolute value of the denominator
+ * @param xNegative whether numerator was negative
+ * @param yNegative whether denominator was negative
+ * @param xMinValue whether numerator was Long.minValue
+ */
+ @inline
+ private def divModHelper(x: RuntimeLong, y: RuntimeLong,
+ xNegative: Boolean, yNegative: Boolean,
+ xMinValue: Boolean): scala.scalajs.js.Array[RuntimeLong] = {
+ import scala.scalajs.js
+
+ @inline
+ @tailrec
+ def divide0(shift: Int, yShift: RuntimeLong, curX: RuntimeLong,
+ quot: RuntimeLong): (RuntimeLong, RuntimeLong) =
+ if (shift < 0 || curX.isZero) (quot, curX) else {
+ val newX = curX - yShift
+ if (!newX.isNegative)
+ divide0(shift-1, yShift >> 1, newX, quot.setBit(shift))
+ else
+ divide0(shift-1, yShift >> 1, curX, quot)
+ }
+
+ val shift = y.numberOfLeadingZeros - x.numberOfLeadingZeros
+ val yShift = y << shift
+
+ val (absQuot, absRem) = divide0(shift, yShift, x, Zero)
+
+ val quot = if (xNegative ^ yNegative) -absQuot else absQuot
+ val rem =
+ if (xNegative && xMinValue) -absRem - One
+ else if (xNegative) -absRem
+ else absRem
+
+ js.Array(quot, rem)
+ }
+
+ /*
+ * Methods of scala.Long
+ * The following methods are only here to properly support reflective calls
+ * on longs. YOU MUST NOT USE THESE METHODS.
+ */
+
+ //protected def unary_~ : Long = ~toLong // already defined
+ //protected def unary_+ : Long = toLong // already defined
+ //protected def unary_- : Long = -toLong // already defined
+
+ //protected def <<(y: Int): Long = toLong << y // already defined
+ protected def <<(y: Long): Long = toLong << y
+ //protected def >>>(y: Int): Long = toLong >>> y // already defined
+ protected def >>>(y: Long): Long = toLong >>> y
+ //protected def >>(y: Int): Long = toLong >> y // already defined
+ protected def >>(y: Long): Long = toLong >> y
+
+ protected def ==(y: Byte): Boolean = toLong == y
+ protected def ==(y: Short): Boolean = toLong == y
+ protected def ==(y: Char): Boolean = toLong == y
+ protected def ==(y: Int): Boolean = toLong == y
+ protected def ==(y: Long): Boolean = toLong == y
+ protected def ==(y: Float): Boolean = toLong == y
+ protected def ==(y: Double): Boolean = toLong == y
+
+ protected def !=(y: Byte): Boolean = toLong != y
+ protected def !=(y: Short): Boolean = toLong != y
+ protected def !=(y: Char): Boolean = toLong != y
+ protected def !=(y: Int): Boolean = toLong != y
+ protected def !=(y: Long): Boolean = toLong != y
+ protected def !=(y: Float): Boolean = toLong != y
+ protected def !=(y: Double): Boolean = toLong != y
+
+ protected def <(y: Byte): Boolean = toLong < y
+ protected def <(y: Short): Boolean = toLong < y
+ protected def <(y: Char): Boolean = toLong < y
+ protected def <(y: Int): Boolean = toLong < y
+ protected def <(y: Long): Boolean = toLong < y
+ protected def <(y: Float): Boolean = toLong < y
+ protected def <(y: Double): Boolean = toLong < y
+
+ protected def <=(y: Byte): Boolean = toLong <= y
+ protected def <=(y: Short): Boolean = toLong <= y
+ protected def <=(y: Char): Boolean = toLong <= y
+ protected def <=(y: Int): Boolean = toLong <= y
+ protected def <=(y: Long): Boolean = toLong <= y
+ protected def <=(y: Float): Boolean = toLong <= y
+ protected def <=(y: Double): Boolean = toLong <= y
+
+ protected def >(y: Byte): Boolean = toLong > y
+ protected def >(y: Short): Boolean = toLong > y
+ protected def >(y: Char): Boolean = toLong > y
+ protected def >(y: Int): Boolean = toLong > y
+ protected def >(y: Long): Boolean = toLong > y
+ protected def >(y: Float): Boolean = toLong > y
+ protected def >(y: Double): Boolean = toLong > y
+
+ protected def >=(y: Byte): Boolean = toLong >= y
+ protected def >=(y: Short): Boolean = toLong >= y
+ protected def >=(y: Char): Boolean = toLong >= y
+ protected def >=(y: Int): Boolean = toLong >= y
+ protected def >=(y: Long): Boolean = toLong >= y
+ protected def >=(y: Float): Boolean = toLong >= y
+ protected def >=(y: Double): Boolean = toLong >= y
+
+ protected def |(y: Byte): Long = toLong | y
+ protected def |(y: Short): Long = toLong | y
+ protected def |(y: Char): Long = toLong | y
+ protected def |(y: Int): Long = toLong | y
+ protected def |(y: Long): Long = toLong | y
+
+ protected def &(y: Byte): Long = toLong & y
+ protected def &(y: Short): Long = toLong & y
+ protected def &(y: Char): Long = toLong & y
+ protected def &(y: Int): Long = toLong & y
+ protected def &(y: Long): Long = toLong & y
+
+ protected def ^(y: Byte): Long = toLong ^ y
+ protected def ^(y: Short): Long = toLong ^ y
+ protected def ^(y: Char): Long = toLong ^ y
+ protected def ^(y: Int): Long = toLong ^ y
+ protected def ^(y: Long): Long = toLong ^ y
+
+ protected def +(y: Byte): Long = toLong + y
+ protected def +(y: Short): Long = toLong + y
+ protected def +(y: Char): Long = toLong + y
+ protected def +(y: Int): Long = toLong + y
+ protected def +(y: Long): Long = toLong + y
+ protected def +(y: Float): Float = toLong + y
+ protected def +(y: Double): Double = toLong + y
+
+ protected def -(y: Byte): Long = toLong - y
+ protected def -(y: Short): Long = toLong - y
+ protected def -(y: Char): Long = toLong - y
+ protected def -(y: Int): Long = toLong - y
+ protected def -(y: Long): Long = toLong - y
+ protected def -(y: Float): Float = toLong - y
+ protected def -(y: Double): Double = toLong - y
+
+ protected def *(y: Byte): Long = toLong - y
+ protected def *(y: Short): Long = toLong - y
+ protected def *(y: Char): Long = toLong - y
+ protected def *(y: Int): Long = toLong - y
+ protected def *(y: Long): Long = toLong - y
+ protected def *(y: Float): Float = toLong - y
+ protected def *(y: Double): Double = toLong - y
+
+ protected def /(y: Byte): Long = toLong / y
+ protected def /(y: Short): Long = toLong / y
+ protected def /(y: Char): Long = toLong / y
+ protected def /(y: Int): Long = toLong / y
+ protected def /(y: Long): Long = toLong / y
+ protected def /(y: Float): Float = toLong / y
+ protected def /(y: Double): Double = toLong / y
+
+ protected def %(y: Byte): Long = toLong % y
+ protected def %(y: Short): Long = toLong % y
+ protected def %(y: Char): Long = toLong % y
+ protected def %(y: Int): Long = toLong % y
+ protected def %(y: Long): Long = toLong % y
+ protected def %(y: Float): Float = toLong % y
+ protected def %(y: Double): Double = toLong % y
+
+}
+
+object RuntimeLong {
+
+ /** number of relevant bits in each Long.l and Long.m */
+ private final val BITS = 22
+ /** number of relevant bits in Long.l and Long.m together */
+ private final val BITS01 = 2 * BITS
+ /** number of relevant bits in Long.h */
+ private final val BITS2 = 64 - BITS01
+ /** bitmask for Long.l and Long.m */
+ private final val MASK = (1 << BITS) - 1
+ /** bitmask for Long.h */
+ private final val MASK_2 = (1 << BITS2) - 1
+
+ private[runtime] final val SIGN_BIT = BITS2 - 1
+ private[runtime] final val SIGN_BIT_VALUE = 1 << SIGN_BIT
+ private[runtime] final val TWO_PWR_15_DBL = 0x8000 * 1.0
+ private[runtime] final val TWO_PWR_16_DBL = 0x10000 * 1.0
+ private[runtime] final val TWO_PWR_22_DBL = 0x400000 * 1.0
+ private[runtime] final val TWO_PWR_31_DBL = TWO_PWR_16_DBL * TWO_PWR_15_DBL
+ private[runtime] final val TWO_PWR_32_DBL = TWO_PWR_16_DBL * TWO_PWR_16_DBL
+ private[runtime] final val TWO_PWR_44_DBL = TWO_PWR_22_DBL * TWO_PWR_22_DBL
+ private[runtime] final val TWO_PWR_63_DBL = TWO_PWR_32_DBL * TWO_PWR_31_DBL
+
+ // Cache the instances for some "literals" used in this implementation
+ val Zero = new RuntimeLong( 0, 0, 0) // 0L
+ val One = new RuntimeLong( 1, 0, 0) // 1L
+ val MinusOne = new RuntimeLong( MASK, MASK, MASK_2) // -1L
+ val MinValue = new RuntimeLong( 0, 0, 524288) // Long.MinValue
+ val MaxValue = new RuntimeLong(4194303, 4194303, 524287) // Long.MaxValue
+ val TenPow9 = new RuntimeLong(1755648, 238, 0) // 1000000000L with 9 zeros
+
+ def fromDouble(value: Double): RuntimeLong = {
+ if (java.lang.Double.isNaN(value)) Zero
+ else if (value < -TWO_PWR_63_DBL) MinValue
+ else if (value >= TWO_PWR_63_DBL) MaxValue
+ else if (value < 0) -fromDouble(-value)
+ else {
+ var acc = value
+ val a2 = if (acc >= TWO_PWR_44_DBL) (acc / TWO_PWR_44_DBL).toInt else 0
+ acc -= a2 * TWO_PWR_44_DBL
+ val a1 = if (acc >= TWO_PWR_22_DBL) (acc / TWO_PWR_22_DBL).toInt else 0
+ acc -= a1 * TWO_PWR_22_DBL
+ val a0 = acc.toInt
+ new RuntimeLong(a0, a1, a2)
+ }
+ }
+
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/RuntimeString.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/RuntimeString.scala
new file mode 100644
index 0000000..f65b1b5
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/RuntimeString.scala
@@ -0,0 +1,338 @@
+package scala.scalajs.runtime
+
+import scala.scalajs.js
+import scala.scalajs.js.prim.{String => jsString}
+
+import java.nio.ByteBuffer
+import java.nio.charset.Charset
+import java.util.regex._
+
+/** Implementation for methods on java.lang.String.
+ *
+ * Strings are represented at runtime by JavaScript strings, but they have
+ * a lot of methods. The compiler forwards methods on java.lang.String to the
+ * methods in the object, passing `this` as the first argument, that we
+ * consistently call `thiz` in this object.
+ */
+private[runtime] object RuntimeString {
+
+ @inline
+ def charAt(thiz: String, index: Int): Char =
+ (thiz: jsString).charCodeAt(index).asInstanceOf[Int].toChar
+
+ def codePointAt(thiz: String, index: Int): Int = {
+ val high = thiz.charAt(index)
+ if (index+1 < thiz.length) {
+ val low = thiz.charAt(index+1)
+ if (Character.isSurrogatePair(high, low))
+ Character.toCodePoint(high, low)
+ else
+ high.toInt
+ } else {
+ high.toInt
+ }
+ }
+
+ def hashCode(thiz: String): Int = {
+ var res = 0
+ var mul = 1 // holds pow(31, length-i-1)
+ var i = thiz.length-1
+ while (i >= 0) {
+ res += thiz.charAt(i) * mul
+ mul *= 31
+ i -= 1
+ }
+ res
+ }
+
+ @inline
+ def compareTo(thiz: String, anotherString: String): Int = {
+ if (thiz.equals(anotherString)) 0
+ else if ((thiz: jsString) < (anotherString: jsString)) -1
+ else 1
+ }
+
+ def compareToIgnoreCase(thiz: String, str: String): Int =
+ thiz.toLowerCase().compareTo(str.toLowerCase())
+
+ @inline
+ def equalsIgnoreCase(thiz: String, that: String): Boolean =
+ thiz.toLowerCase() == (if (that == null) null else that.toLowerCase())
+
+ @inline
+ def concat(thiz: String, s: String): String =
+ checkNull(thiz) + s
+
+ @inline
+ def contains(thiz: String, s: CharSequence): Boolean =
+ thiz.indexOf(s.toString) != -1
+
+ def endsWith(thiz: String, suffix: String): Boolean =
+ ((thiz: jsString).substring(thiz.length - suffix.length): String) == suffix
+
+ def getBytes(thiz: String): Array[Byte] =
+ thiz.getBytes(Charset.defaultCharset)
+
+ def getBytes(thiz: String, charsetName: String): Array[Byte] =
+ thiz.getBytes(Charset.forName(charsetName))
+
+ def getBytes(thiz: String, charset: Charset): Array[Byte] =
+ charset.encode(thiz).array()
+
+ def getChars(thiz: String, srcBegin: Int, srcEnd: Int,
+ dst: Array[Char], dstBegin: Int): Unit = {
+ if (srcEnd > thiz.length || // first test uses thiz
+ srcBegin < 0 ||
+ srcEnd < 0 ||
+ srcBegin > srcEnd) {
+ throw new StringIndexOutOfBoundsException("Index out of Bound")
+ }
+
+ val offset = dstBegin - srcBegin
+ var i = srcBegin
+ while (i < srcEnd) {
+ dst(i+offset) = thiz.charAt(i)
+ i += 1
+ }
+ }
+
+ def indexOf(thiz: String, ch: Int): Int =
+ thiz.indexOf(fromCodePoint(ch))
+
+ def indexOf(thiz: String, ch: Int, fromIndex: Int): Int =
+ thiz.indexOf(fromCodePoint(ch), fromIndex)
+
+ @inline
+ def indexOf(thiz: String, str: String): Int =
+ (thiz: jsString).indexOf(str).asInstanceOf[Int]
+
+ @inline
+ def indexOf(thiz: String, str: String, fromIndex: Int): Int =
+ (thiz: jsString).indexOf(str, fromIndex).asInstanceOf[Int]
+
+ /* Just returning this string is a valid implementation for `intern` in
+ * JavaScript, since strings are primitive values. Therefore, value equality
+ * and reference equality is the same.
+ */
+ @inline
+ def intern(thiz: String): String =
+ checkNull(thiz)
+
+ @inline
+ def isEmpty(thiz: String): Boolean =
+ checkNull(thiz) == ""
+
+ def lastIndexOf(thiz: String, ch: Int): Int =
+ thiz.lastIndexOf(fromCodePoint(ch))
+
+ def lastIndexOf(thiz: String, ch: Int, fromIndex: Int): Int =
+ thiz.lastIndexOf(fromCodePoint(ch), fromIndex)
+
+ @inline
+ def lastIndexOf(thiz: String, str: String): Int =
+ (thiz: jsString).lastIndexOf(str).asInstanceOf[Int]
+
+ @inline
+ def lastIndexOf(thiz: String, str: String, fromIndex: Int): Int =
+ (thiz: jsString).lastIndexOf(str, fromIndex).asInstanceOf[Int]
+
+ @inline
+ def length(thiz: String): Int =
+ (thiz: jsString).length.asInstanceOf[Int]
+
+ @inline
+ def matches(thiz: String, regex: String): Boolean = {
+ checkNull(thiz)
+ Pattern.matches(regex, thiz)
+ }
+
+ @inline
+ def replace(thiz: String, oldChar: Char, newChar: Char): String =
+ (thiz: String).replace(oldChar.toString, newChar.toString)
+
+ @inline
+ def replace(thiz: String, target: CharSequence, replacement: CharSequence): String =
+ (thiz: jsString).split(target.toString).join(replacement.toString)
+
+ def replaceAll(thiz: String, regex: String, replacement: String): String = {
+ checkNull(thiz)
+ Pattern.compile(regex).matcher(thiz).replaceAll(replacement)
+ }
+
+ def replaceFirst(thiz: String, regex: String, replacement: String): String = {
+ checkNull(thiz)
+ Pattern.compile(regex).matcher(thiz).replaceFirst(replacement)
+ }
+
+ @inline
+ def split(thiz: String, regex: String): Array[String] =
+ thiz.split(regex, 0)
+
+ def split(thiz: String, regex: String, limit: Int): Array[String] = {
+ checkNull(thiz)
+ Pattern.compile(regex).split(thiz, limit)
+ }
+
+ @inline
+ def startsWith(thiz: String, prefix: String): Boolean =
+ thiz.startsWith(prefix, 0)
+
+ @inline
+ def startsWith(thiz: String, prefix: String, toffset: Int): Boolean =
+ ((thiz: jsString).substring(toffset, prefix.length): String) == prefix
+
+ @inline
+ def subSequence(thiz: String, beginIndex: Int, endIndex: Int): CharSequence =
+ thiz.substring(beginIndex, endIndex)
+
+ @inline
+ def substring(thiz: String, beginIndex: Int): String =
+ (thiz: jsString).substring(beginIndex)
+
+ @inline
+ def substring(thiz: String, beginIndex: Int, endIndex: Int): String =
+ (thiz: jsString).substring(beginIndex, endIndex)
+
+ def toCharArray(thiz: String): Array[Char] = {
+ val length = thiz.length
+ val result = new Array[Char](length)
+ var i = 0
+ while (i < length) {
+ result(i) = thiz.charAt(i)
+ i += 1
+ }
+ result
+ }
+
+ @inline
+ def toLowerCase(thiz: String): String =
+ (thiz: jsString).toLowerCase()
+
+ @inline
+ def toUpperCase(thiz: String): String =
+ (thiz: jsString).toUpperCase()
+
+ @inline
+ def trim(thiz: String): String =
+ (thiz: jsString).trim()
+
+ // Constructors
+
+ def newString(): String = ""
+
+ def newString(value: Array[Char]): String =
+ newString(value, 0, value.length)
+
+ def newString(value: Array[Char], offset: Int, count: Int): String = {
+ val end = offset + count
+ if (offset < 0 || end < offset || end > value.length)
+ throw new StringIndexOutOfBoundsException
+
+ val charCodes = new js.Array[Int]
+ var i = offset
+ while (i != end) {
+ charCodes += value(i).toInt
+ i += 1
+ }
+ js.String.fromCharCode(charCodes: _*)
+ }
+
+ def newString(bytes: Array[Byte]): String =
+ newString(bytes, Charset.defaultCharset)
+
+ def newString(bytes: Array[Byte], charsetName: String): String =
+ newString(bytes, Charset.forName(charsetName))
+
+ def newString(bytes: Array[Byte], charset: Charset): String =
+ charset.decode(ByteBuffer.wrap(bytes)).toString()
+
+ def newString(bytes: Array[Byte], offset: Int, length: Int): String =
+ newString(bytes, offset, length, Charset.defaultCharset)
+
+ def newString(bytes: Array[Byte], offset: Int, length: Int,
+ charsetName: String): String =
+ newString(bytes, offset, length, Charset.forName(charsetName))
+
+ def newString(bytes: Array[Byte], offset: Int, length: Int,
+ charset: Charset): String =
+ charset.decode(ByteBuffer.wrap(bytes, offset, length)).toString()
+
+ def newString(codePoints: Array[Int], offset: Int, count: Int): String = {
+ val end = offset + count
+ if (offset < 0 || end < offset || end > codePoints.length)
+ throw new StringIndexOutOfBoundsException
+
+ val charCodes = new js.Array[Int]
+ var i = offset
+ while (i != end) {
+ val cp = codePoints(i)
+ if (cp < 0 || cp > Character.MAX_CODE_POINT)
+ throw new IllegalArgumentException
+ if (cp <= Character.MAX_VALUE) {
+ charCodes += cp
+ } else {
+ val offsetCp = cp - 0x10000
+ charCodes += (offsetCp >> 10) | 0xd800
+ charCodes += (offsetCp & 0x3ff) | 0xdc00
+ }
+ i += 1
+ }
+ js.String.fromCharCode(charCodes: _*)
+ }
+
+ def newString(original: String): String =
+ checkNull(original)
+
+ def newString(buffer: java.lang.StringBuffer): String =
+ buffer.toString
+
+ def newString(builder: java.lang.StringBuilder): String =
+ builder.toString
+
+ // Static methods (aka methods on the companion object)
+
+ def valueOf(value: Boolean): String = value.toString()
+ def valueOf(value: Char): String = value.toString()
+ def valueOf(value: Byte): String = value.toString()
+ def valueOf(value: Short): String = value.toString()
+ def valueOf(value: Int): String = value.toString()
+ def valueOf(value: Long): String = value.toString()
+ def valueOf(value: Float): String = value.toString()
+ def valueOf(value: Double): String = value.toString()
+
+ def valueOf(value: Object): String =
+ if (value eq null) "null" else value.toString()
+
+ def valueOf(data: Array[Char]): String =
+ valueOf(data, 0, data.length)
+
+ def valueOf(data: Array[Char], offset: Int, count: Int): String =
+ newString(data, offset, count)
+
+ def format(format: String, args: Array[AnyRef]): String = {
+ val frm = new java.util.Formatter()
+ val res = frm.format(format, args: _*).toString()
+ frm.close()
+ res
+ }
+
+ // Helpers
+
+ @inline
+ private def checkNull(s: String): s.type =
+ if (s == null) throw new NullPointerException()
+ else s
+
+ private def fromCodePoint(codePoint: Int): String = {
+ if ((codePoint & ~Character.MAX_VALUE) == 0)
+ js.String.fromCharCode(codePoint)
+ else if (codePoint < 0 || codePoint > Character.MAX_CODE_POINT)
+ throw new IllegalArgumentException
+ else {
+ val offsetCp = codePoint - 0x10000
+ js.String.fromCharCode(
+ (offsetCp >> 10) | 0xd800, (offsetCp & 0x3ff) | 0xdc00)
+ }
+ }
+
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/StackTrace.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/StackTrace.scala
new file mode 100644
index 0000000..a9e2c00
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/StackTrace.scala
@@ -0,0 +1,507 @@
+package scala.scalajs.runtime
+
+import scala.annotation.tailrec
+
+import scala.scalajs.js
+import scala.scalajs.js.prim.{String => jsString}
+
+/** Conversions of JavaScript stack traces to Java stack traces.
+ */
+object StackTrace {
+
+ /* !!! Note that in this unit, we go to great lengths *not* to use anything
+ * from the Scala collections library.
+ *
+ * This minimizes the risk of runtime errors during the process of decoding
+ * errors, which would be very bad if it happened.
+ */
+
+ /** Captures browser-specific state recording the current stack trace.
+ * The state is stored as a magic field of the throwable, and will be used
+ * by `extract()` to create an Array[StackTraceElement].
+ */
+ def captureState(throwable: Throwable): Unit = {
+ captureState(throwable, createException())
+ }
+
+ /** Creates a JS Error with the current stack trace state. */
+ private def createException(): Any = {
+ try {
+ this.asInstanceOf[js.Dynamic].undef() // it does not exist, that's the point
+ } catch {
+ case js.JavaScriptException(e) => e
+ }
+ }
+
+ /** Captures browser-specific state recording the stack trace of a JS error.
+ * The state is stored as a magic field of the throwable, and will be used
+ * by `extract()` to create an Array[StackTraceElement].
+ */
+ def captureState(throwable: Throwable, e: Any): Unit = {
+ throwable.asInstanceOf[js.Dynamic].stackdata = e.asInstanceOf[js.Any]
+ }
+
+ /** Tests whether we're running under Rhino. */
+ private lazy val isRhino: Boolean = {
+ try {
+ js.Dynamic.global.Packages.org.mozilla.javascript.JavaScriptException
+ true
+ } catch {
+ case js.JavaScriptException(_) => false
+ }
+ }
+
+ /** Extracts a throwable's stack trace from captured browser-specific state.
+ * If no stack trace state has been recorded, or if the state cannot be
+ * analyzed in meaningful way (because we don't know the browser), an
+ * empty array is returned.
+ */
+ def extract(throwable: Throwable): Array[StackTraceElement] =
+ extract(throwable.asInstanceOf[js.Dynamic].stackdata)
+
+ /** Extracts a stack trace from captured browser-specific stackdata.
+ * If no stack trace state has been recorded, or if the state cannot be
+ * analyzed in meaningful way (because we don't know the browser), an
+ * empty array is returned.
+ */
+ def extract(stackdata: js.Dynamic): Array[StackTraceElement] = {
+ val lines = normalizeStackTraceLines(stackdata)
+ normalizedLinesToStackTrace(lines)
+ }
+
+ /* Converts an array of frame entries in normalized form to a stack trace.
+ * Each line must have either the format
+ * <functionName>@<fileName>:<lineNumber>:<columnNumber>
+ * or
+ * <functionName>@<fileName>:<lineNumber>
+ * For some reason, on some browsers, we sometimes have empty lines too.
+ * In the rest of the function, we convert the non-empty lines into
+ * StackTraceElements.
+ */
+ private def normalizedLinesToStackTrace(
+ lines: js.Array[jsString]): Array[StackTraceElement] = {
+ val NormalizedFrameLine = """^([^\@]*)\@(.*):([0-9]+)$""".re
+ val NormalizedFrameLineWithColumn = """^([^\@]*)\@(.*):([0-9]+):([0-9]+)$""".re
+
+ val trace = new js.Array[JSStackTraceElem]
+ var i = 0
+ while (i < lines.length) {
+ val line = lines(i)
+ if (!line.isEmpty) {
+ val mtch1 = NormalizedFrameLineWithColumn.exec(line)
+ if (mtch1 ne null) {
+ val (className, methodName) = extractClassMethod(mtch1(1).get)
+ trace.push(JSStackTraceElem(className, methodName, mtch1(2).get,
+ mtch1(3).get.toInt, mtch1(4).get.toInt))
+ } else {
+ val mtch2 = NormalizedFrameLine.exec(line)
+ if (mtch2 ne null) {
+ val (className, methodName) = extractClassMethod(mtch2(1).get)
+ trace.push(JSStackTraceElem(className,
+ methodName, mtch2(2).get, mtch2(3).get.toInt))
+ } else {
+ // just in case
+ trace.push(JSStackTraceElem("<jscode>", line, null, -1))
+ }
+ }
+ }
+ i += 1
+ }
+
+ // Map stack trace through environment (if supported)
+ val envInfo = environmentInfo
+ val hasMapper = envInfo != js.undefined && envInfo != null &&
+ js.typeOf(envInfo.sourceMapper) == "function"
+
+ val mappedTrace =
+ if (hasMapper)
+ envInfo.sourceMapper(trace).asInstanceOf[js.Array[JSStackTraceElem]]
+ else
+ trace
+
+ // Convert JS objects to java.lang.StackTraceElements
+ // While loop due to space concerns
+ val result = new Array[StackTraceElement](mappedTrace.length)
+
+ i = 0
+ while (i < mappedTrace.length) {
+ val jsSte = mappedTrace(i)
+ val ste = new StackTraceElement(jsSte.declaringClass, jsSte.methodName,
+ jsSte.fileName, jsSte.lineNumber)
+
+ jsSte.columnNumber foreach { cn =>
+ // Store column in magic field
+ ste.asInstanceOf[js.Dynamic].columnNumber = cn
+ }
+
+ result(i) = ste
+ i += 1
+ }
+
+ result
+ }
+
+ /** Tries and extract the class name and method from the JS function name.
+ * The recognized patterns are
+ * ScalaJS.c.<encoded class name>.prototype.<encoded method name>
+ * ScalaJS.c.<encoded class name>.<encoded method name>
+ * ScalaJS.i.<encoded trait impl name>__<encoded method name>
+ * ScalaJS.m.<encoded module name>
+ * When the function name is none of those, the pair
+ * ("<jscode>", functionName)
+ * is returned, which will instruct StackTraceElement.toString() to only
+ * display the function name.
+ */
+ private def extractClassMethod(functionName: String): (String, String) = {
+ val PatC = """^ScalaJS\.c\.([^\.]+)(?:\.prototype)?\.([^\.]+)$""".re
+ val PatI = """^(?:Object\.)?ScalaJS\.i\.((?:_[^_]|[^_])+)__([^\.]+)$""".re
+ val PatM = """^(?:Object\.)?ScalaJS\.m\.([^.\.]+)$""".re
+
+ var isModule = false
+ var mtch = PatC.exec(functionName)
+ if (mtch eq null) {
+ mtch = PatI.exec(functionName)
+ if (mtch eq null) {
+ mtch = PatM.exec(functionName)
+ isModule = true
+ }
+ }
+
+ if (mtch ne null) {
+ val className = decodeClassName(mtch(1).get + (if (isModule) "$" else ""))
+ val methodName = if (isModule)
+ "<clinit>" // that's how it would be reported on the JVM
+ else
+ decodeMethodName(mtch(2).get)
+ (className, methodName)
+ } else {
+ ("<jscode>", functionName)
+ }
+ }
+
+ // decodeClassName -----------------------------------------------------------
+
+ // !!! Duplicate logic: this code must be in sync with ir.Definitions
+
+ private def decodeClassName(encodedName: String): String = {
+ val encoded =
+ if (encodedName.charAt(0) == '$') encodedName.substring(1)
+ else encodedName
+ val base = if (decompressedClasses.hasOwnProperty(encoded)) {
+ decompressedClasses(encoded)
+ } else {
+ @tailrec
+ def loop(i: Int): String = {
+ if (i < compressedPrefixes.length) {
+ val prefix = compressedPrefixes(i)
+ if (encoded.startsWith(prefix))
+ decompressedPrefixes(prefix) + encoded.substring(prefix.length)
+ else
+ loop(i+1)
+ } else {
+ // no prefix matches
+ if (encoded.startsWith("L")) encoded.substring(1)
+ else encoded // just in case
+ }
+ }
+ loop(0)
+ }
+ base.replace("_", ".").replace("$und", "_")
+ }
+
+ private val decompressedClasses: js.Dictionary[String] = {
+ val dict = js.Dynamic.literal(
+ O = "java_lang_Object",
+ T = "java_lang_String",
+ V = "scala_Unit",
+ Z = "scala_Boolean",
+ C = "scala_Char",
+ B = "scala_Byte",
+ S = "scala_Short",
+ I = "scala_Int",
+ J = "scala_Long",
+ F = "scala_Float",
+ D = "scala_Double"
+ ).asInstanceOf[js.Dictionary[String]]
+
+ var index = 0
+ while (index <= 22) {
+ if (index >= 2)
+ dict("T"+index) = "scala_Tuple"+index
+ dict("F"+index) = "scala_Function"+index
+ index += 1
+ }
+
+ dict
+ }
+
+ private val decompressedPrefixes = js.Dynamic.literal(
+ sjsr_ = "scala_scalajs_runtime_",
+ sjs_ = "scala_scalajs_",
+ sci_ = "scala_collection_immutable_",
+ scm_ = "scala_collection_mutable_",
+ scg_ = "scala_collection_generic_",
+ sc_ = "scala_collection_",
+ sr_ = "scala_runtime_",
+ s_ = "scala_",
+ jl_ = "java_lang_",
+ ju_ = "java_util_"
+ ).asInstanceOf[js.Dictionary[String]]
+
+ private val compressedPrefixes = js.Object.keys(decompressedPrefixes)
+
+ // end of decodeClassName ----------------------------------------------------
+
+ private def decodeMethodName(encodedName: String): String = {
+ if (encodedName startsWith "init___") {
+ "<init>"
+ } else {
+ val methodNameLen = encodedName.indexOf("__")
+ if (methodNameLen < 0) encodedName
+ else encodedName.substring(0, methodNameLen)
+ }
+ }
+
+ private implicit class StringRE(val s: String) extends AnyVal {
+ def re: js.RegExp = new js.RegExp(s)
+ def re(mods: String): js.RegExp = new js.RegExp(s, mods)
+ }
+
+ /* ---------------------------------------------------------------------------
+ * Start copy-paste-translate from stacktrace.js
+ *
+ * From here on, most of the code has been copied from
+ * https://github.com/stacktracejs/stacktrace.js
+ * and translated to Scala.js almost literally, with some adaptations.
+ *
+ * Most comments -and lack thereof- have also been copied therefrom.
+ */
+
+ private def normalizeStackTraceLines(e: js.Dynamic): js.Array[jsString] = {
+ /* You would think that we could test once and for all which "mode" to
+ * adopt. But the format can actually differ for different exceptions
+ * on some browsers, e.g., exceptions in Chrome there may or may not have
+ * arguments or stack.
+ */
+ if (!e) {
+ js.Array[jsString]()
+ } else if (isRhino) {
+ extractRhino(e)
+ } else if (!(!e.arguments) && !(!e.stack)) {
+ extractChrome(e)
+ } else if (!(!e.stack) && !(!e.sourceURL)) {
+ extractSafari(e)
+ } else if (!(!e.stack) && !(!e.number)) {
+ extractIE(e)
+ } else if (!(!e.stack) && !(!e.fileName)) {
+ extractFirefox(e)
+ } else if (!(!e.message) && !(!e.`opera#sourceloc`)) {
+ // e.message.indexOf("Backtrace:") > -1 -> opera9
+ // 'opera#sourceloc' in e -> opera9, opera10a
+ // !e.stacktrace -> opera9
+ if (!e.stacktrace) {
+ extractOpera9(e) // use e.message
+ } else if ((e.message.indexOf("\n") > -1) &&
+ (e.message.split("\n").length > e.stacktrace.split("\n").length)) {
+ // e.message may have more stack entries than e.stacktrace
+ extractOpera9(e) // use e.message
+ } else {
+ extractOpera10a(e) // use e.stacktrace
+ }
+ } else if (!(!e.message) && !(!e.stack) && !(!e.stacktrace)) {
+ // e.stacktrace && e.stack -> opera10b
+ if (e.stacktrace.indexOf("called from line") < 0) {
+ extractOpera10b(e)
+ } else {
+ extractOpera11(e)
+ }
+ } else if (!(!e.stack) && !e.fileName) {
+ /* Chrome 27 does not have e.arguments as earlier versions,
+ * but still does not have e.fileName as Firefox */
+ extractChrome(e)
+ } else {
+ extractOther(e)
+ }
+ }
+
+ private def extractRhino(e: js.Dynamic): js.Array[jsString] = {
+ (e.stack.asInstanceOf[js.UndefOr[jsString]]).getOrElse[jsString]("")
+ .replace("""^\s+at\s+""".re("gm"), "") // remove 'at' and indentation
+ .replace("""^(.+?)(?: \((.+)\))?$""".re("gm"), "$2@$1")
+ .replace("""\r\n?""".re("gm"), "\n") // Rhino has platform-dependent EOL's
+ .split("\n")
+ }
+
+ private def extractChrome(e: js.Dynamic): js.Array[jsString] = {
+ (e.stack.asInstanceOf[jsString] + "\n")
+ .replace("""^[\s\S]+?\s+at\s+""".re, " at ") // remove message
+ .replace("""^\s+(at eval )?at\s+""".re("gm"), "") // remove 'at' and indentation
+ .replace("""^([^\(]+?)([\n])""".re("gm"), "{anonymous}() ($1)$2") // see note
+ .replace("""^Object.<anonymous>\s*\(([^\)]+)\)""".re("gm"), "{anonymous}() ($1)")
+ .replace("""^([^\(]+|\{anonymous\}\(\)) \((.+)\)$""".re("gm"), "$1@$2")
+ .split("\n")
+ .jsSlice(0, -1)
+
+ /* Note: there was a $ next to the \n here in the original code, but it
+ * chokes with method names with $'s, which are generated often by Scala.js.
+ */
+ }
+
+ private def extractFirefox(e: js.Dynamic): js.Array[jsString] = {
+ (e.stack.asInstanceOf[jsString])
+ .replace("""(?:\n@:0)?\s+$""".re("m"), "")
+ .replace("""^(?:\((\S*)\))?@""".re("gm"), "{anonymous}($1)@")
+ .split("\n")
+ }
+
+ private def extractIE(e: js.Dynamic): js.Array[jsString] = {
+ (e.stack.asInstanceOf[jsString])
+ .replace("""^\s*at\s+(.*)$""".re("gm"), "$1")
+ .replace("""^Anonymous function\s+""".re("gm"), "{anonymous}() ")
+ .replace("""^([^\(]+|\{anonymous\}\(\))\s+\((.+)\)$""".re("gm"), "$1@$2")
+ .split("\n")
+ .jsSlice(1)
+ }
+
+ private def extractSafari(e: js.Dynamic): js.Array[jsString] = {
+ (e.stack.asInstanceOf[jsString])
+ .replace("""\[native code\]\n""".re("m"), "")
+ .replace("""^(?=\w+Error\:).*$\n""".re("m"), "")
+ .replace("""^@""".re("gm"), "{anonymous}()@")
+ .split("\n")
+ }
+
+ private def extractOpera9(e: js.Dynamic): js.Array[jsString] = {
+ // " Line 43 of linked script file://localhost/G:/js/stacktrace.js\n"
+ // " Line 7 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html\n"
+ val lineRE = """Line (\d+).*script (?:in )?(\S+)""".re("i")
+ val lines = (e.message.asInstanceOf[jsString]).split("\n")
+ val result = new js.Array[jsString]
+
+ var i = 2
+ val len = lines.length.toInt
+ while (i < len) {
+ val mtch = lineRE.exec(lines(i))
+ if (mtch ne null) {
+ result.push("{anonymous}()@" + mtch(2).get + ":" + mtch(1).get
+ /* + " -- " + lines(i+1).replace("""^\s+""".re, "") */)
+ }
+ i += 2
+ }
+
+ result
+ }
+
+ private def extractOpera10a(e: js.Dynamic): js.Array[jsString] = {
+ // " Line 27 of linked script file://localhost/G:/js/stacktrace.js\n"
+ // " Line 11 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html: In function foo\n"
+ val lineRE = """Line (\d+).*script (?:in )?(\S+)(?:: In function (\S+))?$""".re("i")
+ val lines = (e.stacktrace.asInstanceOf[jsString]).split("\n")
+ val result = new js.Array[jsString]
+
+ var i = 0
+ val len = lines.length.toInt
+ while (i < len) {
+ val mtch = lineRE.exec(lines(i))
+ if (mtch ne null) {
+ val fnName = mtch(3).getOrElse("{anonymous}")
+ result.push(fnName + "()@" + mtch(2).get + ":" + mtch(1).get
+ /* + " -- " + lines(i+1).replace("""^\s+""".re, "")*/)
+ }
+ i += 2
+ }
+
+ result
+ }
+
+ private def extractOpera10b(e: js.Dynamic): js.Array[jsString] = {
+ // "<anonymous function: run>([arguments not available])@file://localhost/G:/js/stacktrace.js:27\n" +
+ // "printStackTrace([arguments not available])@file://localhost/G:/js/stacktrace.js:18\n" +
+ // "@file://localhost/G:/js/test/functional/testcase1.html:15"
+ val lineRE = """^(.*)@(.+):(\d+)$""".re
+ val lines = (e.stacktrace.asInstanceOf[jsString]).split("\n")
+ val result = new js.Array[jsString]
+
+ var i = 0
+ val len = lines.length.toInt
+ while (i < len) {
+ val mtch = lineRE.exec(lines(i))
+ if (mtch ne null) {
+ val fnName = mtch(1).fold("global code")(_ + "()")
+ result.push(fnName + "@" + mtch(2).get + ":" + mtch(3).get)
+ }
+ i += 1
+ }
+
+ result
+ }
+
+ private def extractOpera11(e: js.Dynamic): js.Array[jsString] = {
+ val lineRE = """^.*line (\d+), column (\d+)(?: in (.+))? in (\S+):$""".re
+ val lines = (e.stacktrace.asInstanceOf[jsString]).split("\n")
+ val result = new js.Array[jsString]
+
+ var i = 0
+ val len = lines.length.toInt
+ while (i < len) {
+ val mtch = lineRE.exec(lines(i))
+ if (mtch ne null) {
+ val location = mtch(4).get + ":" + mtch(1).get + ":" + mtch(2).get
+ val fnName0 = mtch(2).getOrElse("global code")
+ val fnName = (fnName0: jsString)
+ .replace("""<anonymous function: (\S+)>""".re, "$1")
+ .replace("""<anonymous function>""".re, "{anonymous}")
+ result.push(fnName + "@" + location
+ /* + " -- " + lines(i+1).replace("""^\s+""".re, "")*/)
+ }
+ i += 2
+ }
+
+ result
+ }
+
+ private def extractOther(e: js.Dynamic): js.Array[jsString] = {
+ js.Array()
+ }
+
+ /* End copy-paste-translate from stacktrace.js
+ * ---------------------------------------------------------------------------
+ */
+
+ trait JSStackTraceElem extends js.Object {
+ var declaringClass: String = js.native
+ var methodName: String = js.native
+ var fileName: String = js.native
+ /** 1-based line number */
+ var lineNumber: Int = js.native
+ /** 1-based optional columnNumber */
+ var columnNumber: js.UndefOr[Int] = js.native
+ }
+
+ object JSStackTraceElem {
+ @inline
+ def apply(declaringClass: String, methodName: String,
+ fileName: String, lineNumber: Int,
+ columnNumber: js.UndefOr[Int] = js.undefined): JSStackTraceElem = {
+ js.Dynamic.literal(
+ declaringClass = declaringClass,
+ methodName = methodName,
+ fileName = fileName,
+ lineNumber = lineNumber,
+ columnNumber = columnNumber
+ ).asInstanceOf[JSStackTraceElem]
+ }
+ }
+
+ /**
+ * Implicit class to access magic column element created in STE
+ */
+ implicit class ColumnStackTraceElement(ste: StackTraceElement) {
+ def getColumnNumber: Int = {
+ val num = ste.asInstanceOf[js.Dynamic].columnNumber
+ if (!(!num)) num.asInstanceOf[Int]
+ else -1 // Not very Scala-ish, but consistent with StackTraceElemnt
+ }
+ }
+
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/UndefinedBehaviorError.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/UndefinedBehaviorError.scala
new file mode 100644
index 0000000..b06ed7d
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/UndefinedBehaviorError.scala
@@ -0,0 +1,23 @@
+package scala.scalajs.runtime
+
+import scala.util.control.ControlThrowable
+
+/** Error thrown when an undefined behavior in Fatal mode has been detected.
+ * This error should never be caught. It indicates a severe programming bug.
+ * In Unchecked mode, the program may behave arbitrarily.
+ * The `cause` is set to the exception that would have been thrown if the
+ * given behavior was in Compliant mode.
+ * If your program relies on the proper kind of exception being thrown, as if
+ * running on the JVM, you should set the appropriate behavior to Compliant.
+ * Note that this will have (potentially major) performance impacts.
+ */
+class UndefinedBehaviorError(message: String, cause: Throwable)
+ extends java.lang.Error(message, cause) with ControlThrowable {
+
+ def this(cause: Throwable) =
+ this("An undefined behavior was detected" +
+ (if (cause == null) "" else ": "+cause.getMessage), cause)
+
+ override def fillInStackTrace(): Throwable =
+ super[Error].fillInStackTrace()
+}
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/package.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/package.scala
new file mode 100644
index 0000000..59c774c
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/package.scala
@@ -0,0 +1,176 @@
+package scala.scalajs
+
+import scala.annotation.tailrec
+
+import scala.collection.GenTraversableOnce
+
+package object runtime {
+
+ def wrapJavaScriptException(e: Any): Throwable = e match {
+ case e: Throwable => e
+ case _ => js.JavaScriptException(e)
+ }
+
+ def unwrapJavaScriptException(th: Throwable): Any = th match {
+ case js.JavaScriptException(e) => e
+ case _ => th
+ }
+
+ def cloneObject(from: js.Object): js.Object = {
+ val ctor = ({ (self: js.Dictionary[js.Any], from: js.Dictionary[js.Any]) =>
+ for (key <- from.keys)
+ self(key) = from(key)
+ }: js.ThisFunction).asInstanceOf[js.Dynamic]
+ ctor.prototype = js.Object.getPrototypeOf(from)
+ js.Dynamic.newInstance(ctor)(from)
+ }
+
+ @inline final def genTraversableOnce2jsArray[A](
+ col: GenTraversableOnce[A]): js.Array[A] = {
+ col match {
+ case col: js.ArrayOps[A] => col.result()
+ case col: js.WrappedArray[A] => col.array
+ case _ =>
+ val result = new js.Array[A]
+ col.foreach(x => result.push(x))
+ result
+ }
+ }
+
+ /** Instantiates a JS object with variadic arguments to the constructor. */
+ def newJSObjectWithVarargs(ctor: js.Dynamic, args: js.Array[_]): js.Any = {
+ // Not really "possible" in JavaScript, so we emulate what it would be.
+ val c = ((() => ()): js.Function).asInstanceOf[js.Dynamic]
+ c.prototype = ctor.prototype
+ val instance = js.Dynamic.newInstance(c)()
+ val result = ctor.applyDynamic("apply")(instance, args)
+ (result: js.Any) match {
+ case _:js.prim.Undefined | _:js.prim.Number | _:js.prim.Boolean |
+ _:js.prim.String | null =>
+ instance
+ case _ =>
+ result
+ }
+ }
+
+ /** Returns an array of the enumerable properties in an object's prototype
+ * chain.
+ *
+ * This is the implementation of [[js.Object.properties]].
+ */
+ def propertiesOf(obj: js.Any): js.Array[String] = {
+ // See http://stackoverflow.com/questions/26445248/
+ if (obj == null || js.isUndefined(obj)) {
+ js.Array()
+ } else {
+ val result = new js.Array[String]
+ val alreadySeen = js.Dictionary.empty[Boolean]
+
+ @tailrec
+ def loop(obj: js.Object): Unit = {
+ if (obj != null) {
+ // Add own enumerable properties that have not been seen yet
+ val enumProps = js.Object.keys(obj)
+ val enumPropsLen = enumProps.length
+ var i = 0
+ while (i < enumPropsLen) {
+ val prop = enumProps(i)
+ if (!alreadySeen.get(prop).isDefined)
+ result.push(prop)
+ i += 1
+ }
+
+ /* Add all own properties to the alreadySeen set, including
+ * non-enumerable ones.
+ */
+ val allProps = js.Object.getOwnPropertyNames(obj)
+ val allPropsLen = allProps.length
+ var j = 0
+ while (j < allPropsLen) {
+ alreadySeen(allProps(j)) = true
+ j += 1
+ }
+
+ // Continue with the next object in the prototype chain
+ loop(js.Object.getPrototypeOf(obj))
+ }
+ }
+ loop(js.Object(obj))
+
+ result
+ }
+ }
+
+ /** Information about the environment Scala.js runs in. */
+ def environmentInfo: js.Dynamic = sys.error("stub")
+
+ /** Polyfill for fround in case we use strict Floats and even Typed Arrays
+ * are not available.
+ * Note: this method returns a Double, even though the value is meant
+ * to be a Float. It cannot return a Float because that would require to
+ * do `x.toFloat` somewhere in here, which would itself, in turn, call this
+ * method.
+ */
+ def froundPolyfill(v: Double): Double = {
+ /* Originally inspired by the Typed Array polyfills written by Joshua Bell:
+ * https://github.com/inexorabletash/polyfill/blob/a682f42c1092280bb01907c245979fb07219513d/typedarray.js#L150-L255
+ * Then simplified quite a lot because
+ * 1) we do not need to produce the actual bit string that serves as
+ * storage of the floats, and
+ * 2) we are only interested in the float32 case.
+ */
+ import Math._
+
+ // Special cases
+ if (v.isNaN || v == 0.0 || v.isInfinite) {
+ v
+ } else {
+ val LN2 = 0.6931471805599453
+ val ebits = 8
+ val fbits = 23
+ val bias = (1 << (ebits-1)) - 1
+ val twoPowFbits = (1 << fbits).toDouble
+ val SubnormalThreshold = 1.1754943508222875E-38 // pow(2, 1-bias)
+
+ val isNegative = v < 0
+ val av = if (isNegative) -v else v
+
+ val absResult = if (av >= SubnormalThreshold) {
+ val e0 = floor(log(av) / LN2)
+ // 1-bias <= e0 <= 1024
+ if (e0 > bias) {
+ // Overflow
+ Double.PositiveInfinity
+ } else {
+ val twoPowE0 = pow(2, e0)
+ val f0 = Bits.roundToEven(av / twoPowE0 * twoPowFbits)
+ if (f0 / twoPowFbits >= 2) {
+ //val e = e0 + 1.0 // not used
+ val f = 1.0
+ if (e0 > bias-1) { // === (e > bias) because e0 is whole
+ // Overflow
+ Double.PositiveInfinity
+ } else {
+ // Normalized case 1
+ val twoPowE = 2*twoPowE0
+ twoPowE * (1.0 + (f - twoPowFbits) / twoPowFbits)
+ }
+ } else {
+ // Normalized case 2
+ // val e = e0 // not used
+ val f = f0
+ val twoPowE = twoPowE0
+ twoPowE * (1.0 + (f - twoPowFbits) / twoPowFbits)
+ }
+ }
+ } else {
+ // Subnormal
+ val rounder = Float.MinPositiveValue.toDouble
+ Bits.roundToEven(av / rounder) * rounder
+ }
+
+ if (isNegative) -absResult else absResult
+ }
+ }
+
+}
diff --git a/examples/scala-js/no-ir-check-test/src/test/scala/scala/scalajs/testsuite/noircheck/DummyParentsTest.scala b/examples/scala-js/no-ir-check-test/src/test/scala/scala/scalajs/testsuite/noircheck/DummyParentsTest.scala
new file mode 100644
index 0000000..30c4172
--- /dev/null
+++ b/examples/scala-js/no-ir-check-test/src/test/scala/scala/scalajs/testsuite/noircheck/DummyParentsTest.scala
@@ -0,0 +1,34 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.noircheck
+
+import org.scalajs.jasminetest.JasmineTest
+
+object DummyParentsTest extends JasmineTest {
+
+ describe("Linking Stages") {
+
+ it("should provide dummy parents if required") {
+
+ import scala.concurrent.forkjoin._
+
+ // scala.concurrent.forkjoin.ForkJoinWorkerThread is not defined
+ class DummyFJWorkerThread extends ForkJoinWorkerThread(null) {
+ override def onStart(): Unit = { /* something */ }
+ }
+
+ val x = "1".toInt
+
+ if (x + x < 0) {
+ // Ensure DummyFuture is not DCEd, but never instantiated
+ new DummyFJWorkerThread()
+ }
+ }
+
+ }
+}
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/BlacklistedTests.txt b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/BlacklistedTests.txt
new file mode 100644
index 0000000..241b188
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/BlacklistedTests.txt
@@ -0,0 +1,891 @@
+#
+# POS
+#
+
+# Using Jsoup, what's that?
+pos/cycle-jsoup.scala
+
+# Using scala.actors
+pos/t533.scala
+pos/functions.scala
+pos/MailBox.scala
+
+#
+# NEG
+#
+
+# Using the compiler API
+
+neg/t6446-additional
+neg/t6446-list
+neg/t6446-missing
+neg/t6446-show-phases.scala
+
+# 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
+
+# Uses .java files
+neg/t6289
+
+#
+# RUN
+#
+
+# Relies on the exact toString() representation of Floats/Doubles
+run/t2378.scala
+
+# Relies on !box(+0.0).equals(box(-0.0))
+run/number-parsing.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/t0412.scala
+run/t3518.scala
+run/t6198.scala
+run/t5018.scala
+run/t2417.scala
+run/t6197.scala
+run/t4813.scala
+run/lazy-concurrent.scala
+run/t5880.scala
+run/mapConserve.scala
+run/t3667.scala
+run/bigDecimalTest.scala
+run/t3038d.scala
+run/numbereq.scala
+run/shutdownhooks.scala
+run/t4658.scala
+run/t4201.scala
+run/t5590.scala
+run/deeps.scala
+run/t3895b.scala
+run/t2813.2.scala
+run/t5974.scala
+run/hashset.scala
+run/t5262.scala
+run/t5293.scala
+run/t5293-map.scala
+run/serialize-stream.scala
+run/sysprops.scala
+
+run/colltest.scala
+run/equality.scala
+run/t2849.scala
+run/t1360.scala
+run/t6114.scala
+run/t7269.scala
+run/t3199b.scala
+
+# Uses java.util.Collections
+run/java-erasure.scala
+run/t2250.scala
+
+# Uses java.math.BigDecimal / BigInteger
+run/bigDecimalCache.scala
+run/hashhash.scala
+run/is-valid-num.scala
+run/range.scala
+run/stringinterpolation_macro-run.scala
+run/t5356.scala
+run/t6064.scala
+
+# Documented semantic difference on numbers (float precision)
+run/interpolation.scala
+run/interpolationMultiline1.scala
+
+# Documented semantic difference on String.split(x: Array[Char])
+run/t0325.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
+
+# 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
+
+# Crashes Rhino
+
+run/bridges.scala
+run/patmat-exprs.scala
+
+# Using partest properties
+
+run/tailcalls.scala
+run/t4294.scala
+run/t6331b.scala
+
+# Using IO
+
+run/Predef.readLine.scala
+run/t6488.scala
+
+# Object{Output|Input}Streams
+run/t6935.scala
+run/t8188.scala
+
+# Using System.getProperties
+
+run/t4426.scala
+
+# Using ExecutionContext.global
+run/t7336.scala
+run/t7775.scala
+run/future-flatmap-exec-count.scala
+
+# Using reflection
+
+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/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/reflection-mem-typecheck.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/t6220.scala
+run/t6240-universe-code-gen.scala
+run/t6221
+run/t6260b.scala
+run/t6259.scala
+run/t6287.scala
+run/t6261.scala
+run/t6308.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_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/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
+
+# 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
+
+# Expecting some particular value of hashCode()
+
+run/MeterCaseClass.scala
+run/t5608.scala
+run/caseClassHash.scala
+run/Meter.scala
+
+# Exceptions that become JavaScriptException
+
+run/pf-catch.scala
+run/exceptions-2.scala
+run/exceptions-nest.scala
+
+# Expecting unsupported exceptions (e.g. ArrayIndexOutOfBounds)
+run/optimizer-array-load.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/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
+
+# Using Scala Script (partest.ScriptTest)
+
+run/t7711-script-args.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
+run/t7933.scala
+run/t7843-jsr223-service.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
+
+# partest.BytecodeTest
+run/t6546
+run/t7106
+run/t7974
+
+# 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
+
+# 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
+
+### Incorrect partests ###
+# Badly uses constract of Console.print (no flush)
+run/t429.scala
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/BuglistedTests.txt b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/BuglistedTests.txt
new file mode 100644
index 0000000..42c6146
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/BuglistedTests.txt
@@ -0,0 +1,4 @@
+# The tests in this file should pass but have never passed so far
+# use scala.tools.partest.scalajs.testunknownonly to only run tests
+# which are neither in BuglistedTests.txt, WhitelistedTests.txt or
+# BlacklistedTests.txt
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/NoDCEWarn.txt b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/NoDCEWarn.txt
new file mode 100644
index 0000000..cc5aff0
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/NoDCEWarn.txt
@@ -0,0 +1,8 @@
+Ljava_math_MathContext$
+Ljava_math_BigDecimal$
+Ljava_math_BigDecimal
+Ljava_math_BigInteger$
+jl_Class$
+jl_Class.getClassLoader__jl_ClassLoader
+jl_Class.getPackage__jl_Package
+jl_Class.getInterfaces__Ajl_Class
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/WhitelistedTests.txt b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/WhitelistedTests.txt
new file mode 100644
index 0000000..c4df1ec
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/WhitelistedTests.txt
@@ -0,0 +1,2929 @@
+pos/spec-super.scala
+pos/t1035.scala
+pos/t5897.scala
+pos/irrefutable.scala
+pos/spec-partialmap.scala
+pos/tcpoly_seq.scala
+pos/partialfun.scala
+pos/t2795-new.scala
+pos/clsrefine.scala
+pos/t0774
+pos/t1070.scala
+pos/t5957
+pos/looping-jsig.scala
+pos/t3274.scala
+pos/spec-fields-old.scala
+pos/t262.scala
+pos/t7486.scala
+pos/t2261.scala
+pos/t6600.scala
+pos/t4786.scala
+pos/t5406.scala
+pos/tcpoly_late_method_params.scala
+pos/t2726
+pos/pos-bug1210.scala
+pos/t3312.scala
+pos/manifest1-old.scala
+pos/gadt-gilles.scala
+pos/t4842.scala
+pos/ted.scala
+pos/NoCyclicReference.scala
+pos/t3568.scala
+pos/t0030.scala
+pos/t2635.scala
+pos/t7232b
+pos/t0017.scala
+pos/t812.scala
+pos/t2179.scala
+pos/t651.scala
+pos/spurious-overload.scala
+pos/t758.scala
+pos/t4760.scala
+pos/t1672.scala
+pos/mixins.scala
+pos/patterns.scala
+pos/t1260.scala
+pos/t6551.scala
+pos/t2060.scala
+pos/t6575a.scala
+pos/t1318.scala
+pos/t4266.scala
+pos/t0695
+pos/protected-static
+pos/t5738.scala
+pos/t1226.scala
+pos/t5013
+pos/t6215.scala
+pos/t5692b
+pos/traits.scala
+pos/t2994a.scala
+pos/t3371.scala
+pos/t613.scala
+pos/t6499.scala
+pos/xlint1.scala
+pos/t1150
+pos/sealed-final.scala
+pos/test4a.scala
+pos/t2664.scala
+pos/t3528.scala
+pos/t3174.scala
+pos/t6994.scala
+pos/t4812.scala
+pos/t5777.scala
+pos/t5223.scala
+pos/t439.scala
+pos/t3079.scala
+pos/t5829.scala
+pos/t0036.scala
+pos/scoping2.scala
+pos/t4717.scala
+pos/t4257.scala
+pos/t1210a.scala
+pos/getClassType.scala
+pos/t5330.scala
+pos/t4524.scala
+pos/t2945.scala
+pos/t6562.scala
+pos/t0273.scala
+pos/override-object-yes.scala
+pos/t7426.scala
+pos/t6601
+pos/t3076
+pos/seq-ordering.scala
+pos/spec-groups.scala
+pos/t296.scala
+pos/t5545
+pos/spec-multiplectors.scala
+pos/t1789.scala
+pos/t2569
+pos/ksbug1.scala
+pos/t0599.scala
+pos/local-objects.scala
+pos/t0081.scala
+pos/t5756.scala
+pos/t7126.scala
+pos/t7716.scala
+pos/t2797.scala
+pos/t5399.scala
+pos/t1101
+pos/t767.scala
+pos/contrib467.scala
+pos/t7532b
+pos/self-type-override.scala
+pos/t4853.scala
+pos/t839.scala
+pos/t5644
+pos/t5853.scala
+pos/t5178.scala
+pos/unapplyNeedsMemberType.scala
+pos/t5390.scala
+pos/t6575b.scala
+pos/t151.scala
+pos/t2665.scala
+pos/t5120.scala
+pos/erasure-nsquared.scala
+pos/arrays3.scala
+pos/t3136.scala
+pos/inline-access-levels
+pos/t3972.scala
+pos/t2591.scala
+pos/t3486
+pos/variances-flip.scala
+pos/annotated-original
+pos/typesafecons.scala
+pos/stable.scala
+pos/t1996.scala
+pos/t3037.scala
+pos/t1711
+pos/t3374.scala
+pos/t0029.scala
+pos/t3278.scala
+pos/matthias3.scala
+pos/t5546.scala
+pos/t4020.scala
+pos/matthias4.scala
+pos/value-class-override-spec.scala
+pos/arrays2.scala
+pos/t5119.scala
+pos/t2613.scala
+pos/t4070b.scala
+pos/virtpatmat_exist_uncurry.scala
+pos/modules1.scala
+pos/spec-constr-new.scala
+pos/t6335.scala
+pos/t675.scala
+pos/t0644.scala
+pos/t5892.scala
+pos/t360.scala
+pos/override.scala
+pos/t1798.scala
+pos/strip-tvars-for-lubbasetypes.scala
+pos/hk-infer.scala
+pos/t2119.scala
+pos/t0231.scala
+pos/t1459
+pos/t1381-new.scala
+pos/t2610.scala
+pos/t2708.scala
+pos/t5604b
+pos/t3951
+pos/t361.scala
+pos/t319.scala
+pos/largecasetest.scala
+pos/switchUnbox.scala
+pos/typetags.scala
+pos/java-access-pos
+pos/t803.scala
+pos/t3898.scala
+pos/t5692a
+pos/t2421.scala
+pos/t1102
+pos/t0654.scala
+pos/exhaust_alternatives.scala
+pos/t807.scala
+pos/t5702-pos-infix-star.scala
+pos/t1186
+pos/t1439.scala
+pos/t7427.scala
+pos/virtpatmat_binding_opt.scala
+pos/t247.scala
+pos/abstract.scala
+pos/gen-traversable-methods.scala
+pos/t2795-old.scala
+pos/t5639
+pos/t2667.scala
+pos/t2405.scala
+pos/t1438.scala
+pos/SI-7100.scala
+pos/t1659.scala
+pos/unchecked-a.scala
+pos/t3636.scala
+pos/t6745.scala
+pos/t2809.scala
+pos/t7022.scala
+pos/t6447.scala
+pos/t6367.scala
+pos/t5846.scala
+pos/lubs.scala
+pos/t1987a.scala
+pos/spec-arrays.scala
+pos/virtpatmat_anonfun_for.scala
+pos/listpattern.scala
+pos/t5742.scala
+pos/test5refine.scala
+pos/switch-small.scala
+pos/t5604
+pos/return_thistype.scala
+pos/t348plus.scala
+pos/t3420.scala
+pos/t3440.scala
+pos/maxim1.scala
+pos/caseClassInMethod.scala
+pos/t7239.scala
+pos/t3833.scala
+pos/t6675.scala
+pos/t4402
+pos/t5953.scala
+pos/t1152
+pos/t0591.scala
+pos/t210.scala
+pos/t7035.scala
+pos/t5769.scala
+pos/pmbug.scala
+pos/t2331.scala
+pos/t5240.scala
+pos/t304.scala
+pos/annotated-treecopy
+pos/t2081.scala
+pos/t0904.scala
+pos/t7649.scala
+pos/t3498-new.scala
+pos/contrib701.scala
+pos/t6624.scala
+pos/t3924.scala
+pos/t374.scala
+pos/t1642
+pos/t1591_pos.scala
+pos/depmet_implicit_oopsla_session_2.scala
+pos/t5899.scala
+pos/thistype.scala
+pos/t4176b.scala
+pos/elidable-tparams.scala
+pos/lambdalift.scala
+pos/nothing_manifest_disambig-old.scala
+pos/t372.scala
+pos/t5399a.scala
+pos/t2782.scala
+pos/patmat-extract-tparam.scala
+pos/t4114.scala
+pos/unapplyVal.scala
+pos/t2486.scala
+pos/t5877b.scala
+pos/t0625.scala
+pos/t6358_2.scala
+pos/viewtest1.scala
+pos/t1237.scala
+pos/scala-singleton.scala
+pos/t1254
+pos/t5504
+pos/bounds.scala
+pos/t3631.scala
+pos/t3177.scala
+pos/unapplyContexts2.scala
+pos/t0438.scala
+pos/t1642b.scala
+pos/inferbroadtype.scala
+pos/t1858.scala
+pos/t3731.scala
+pos/t6963c.scala
+pos/classtag-pos.scala
+pos/t6221.scala
+pos/t3343.scala
+pos/spec-asseenfrom.scala
+pos/t604.scala
+pos/spec-example1.scala
+pos/t0786.scala
+pos/annot-inner.scala
+pos/t5886.scala
+pos/t1056.scala
+pos/t294
+pos/spec-Function1.scala
+pos/t1836
+pos/spec-private.scala
+pos/depmet_implicit_tpbetareduce.scala
+pos/exhaust_2.scala
+pos/t7532
+pos/t5175.scala
+pos/t802.scala
+pos/t5809.scala
+pos/tcpoly_typesub.scala
+pos/t6029.scala
+pos/contextbounds-implicits-new.scala
+pos/t3480.scala
+pos/patterns3.scala
+pos/caseaccs.scala
+pos/spec-sparsearray-old.scala
+pos/patterns1213.scala
+pos/spec-traits.scala
+pos/t0020.scala
+pos/cycle
+pos/t5968.scala
+pos/typealiases.scala
+pos/init.scala
+pos/t697.scala
+pos/t2693.scala
+pos/t2377
+pos/unapplyGeneric.scala
+pos/t1385.scala
+pos/t3363-old.scala
+pos/t1236.scala
+pos/t0068.scala
+pos/t4052.scala
+pos/lambdalift1.scala
+pos/z1730.scala
+pos/variances-local.scala
+pos/virtpatmat_gadt_array.scala
+pos/t2421_delitedsl.scala
+pos/t5626.scala
+pos/t690.scala
+pos/t711.scala
+pos/t6547.scala
+pos/t1937
+pos/t3999
+pos/SI-7060.scala
+pos/t2305.scala
+pos/t2168.scala
+pos/t2660.scala
+pos/t1693.scala
+pos/inliner2.scala
+pos/t2799.scala
+pos/t6966.scala
+pos/t1001.scala
+pos/S5.scala
+pos/t0301.scala
+pos/t1048.scala
+pos/t415.scala
+pos/t6386.scala
+pos/t2187.scala
+pos/hashhash-overloads.scala
+pos/t6921.scala
+pos/t0227.scala
+pos/t6556.scala
+pos/t3946
+pos/t1053.scala
+pos/t1000.scala
+pos/t0586.scala
+pos/t7011.scala
+pos/t7329.scala
+pos/t4975.scala
+pos/t1131.scala
+pos/t1027.scala
+pos/t2913.scala
+pos/t3494.scala
+pos/t5606.scala
+pos/t4716.scala
+pos/tcpoly_gm.scala
+pos/t4859.scala
+pos/t514.scala
+pos/lexical.scala
+pos/t2624.scala
+pos/t4036.scala
+pos/t2741
+pos/t703.scala
+pos/five-dot-f.scala
+pos/t805.scala
+pos/strings.scala
+pos/t2433
+pos/t6925.scala
+pos/t1085.scala
+pos/t7461
+pos/t1942
+pos/spec-lists.scala
+pos/t3349
+pos/tcpoly_infer_ticket474.scala
+pos/t1614
+pos/virtpatmat_reach_const.scala
+pos/t2194.scala
+pos/t6976
+pos/t1560.scala
+pos/t6891.scala
+pos/t3883.scala
+pos/infersingle.scala
+pos/gui.scala
+pos/t1164.scala
+pos/t3175-pos.scala
+pos/t4336.scala
+pos/annotations2.scala
+pos/proj-rec-test.scala
+pos/t2973.scala
+pos/t1123.scala
+pos/t6205.scala
+pos/t5727.scala
+pos/t6537.scala
+pos/t6712.scala
+pos/t3866.scala
+pos/t4831.scala
+pos/selftails.scala
+pos/t397.scala
+pos/spec-vector.scala
+pos/t7233b.scala
+pos/t1391.scala
+pos/spec.scala
+pos/t3106.scala
+pos/contextbounds-implicits-old.scala
+pos/packageobjs.scala
+pos/michel3.scala
+pos/t628.scala
+pos/collections.scala
+pos/tcpoly_boundedmonad.scala
+pos/t7668.scala
+pos/t0032.scala
+pos/t0069.scala
+pos/t4345.scala
+pos/t3521
+pos/t3071.scala
+pos/tcpoly_infer_easy.scala
+pos/t289.scala
+pos/t4365
+pos/rangepos-anonapply.scala
+pos/t5033.scala
+pos/lambda.scala
+pos/S8.scala
+pos/t6014.scala
+pos/t1785.scala
+pos/t6034.scala
+pos/t7433.scala
+pos/imp2-pos.scala
+pos/t0504.scala
+pos/t1272.scala
+pos/t0612
+pos/value-class-override-no-spec.scala
+pos/overloaded-unapply.scala
+pos/t5859.scala
+pos/chang
+pos/localmodules.scala
+pos/t4237.scala
+pos/rangepos-patmat.scala
+pos/t1974.scala
+pos/t0054.scala
+pos/michel2.scala
+pos/t0770.scala
+pos/t1146.scala
+pos/t2441pos.scala
+pos/t5099.scala
+pos/tcpoly_seq_typealias.scala
+pos/t946.scala
+pos/tcpoly_infer_ticket1864.scala
+pos/t4579.scala
+pos/t4737
+pos/t7377b.scala
+pos/t616.scala
+pos/t201.scala
+pos/t6355pos.scala
+pos/escapes2.scala
+pos/t1675.scala
+pos/t3890.scala
+pos/t6040.scala
+pos/spec-tailcall.scala
+pos/existentials.scala
+pos/t5317.scala
+pos/t7782b.scala
+pos/t4758.scala
+pos/t7296.scala
+pos/t6896.scala
+pos/cls1.scala
+pos/t402.scala
+pos/gosh.scala
+pos/t2619.scala
+pos/javaConversions-2.10-regression.scala
+pos/t759.scala
+pos/t5259.scala
+pos/t5130.scala
+pos/t5156.scala
+pos/t0905.scala
+pos/package-implicit
+pos/t2669.scala
+pos/trait-parents.scala
+pos/virtpatmat_exhaust.scala
+pos/patterns1.scala
+pos/t7014
+pos/t1231
+pos/t1751
+pos/t7233.scala
+pos/t6022.scala
+pos/tcpoly_checkkinds_mix.scala
+pos/depmet_implicit_norm_ret.scala
+pos/package-case.scala
+pos/philippe4.scala
+pos/michel6.scala
+pos/t4188.scala
+pos/t3936
+pos/t1280.scala
+pos/t6722.scala
+pos/t796.scala
+pos/t5542.scala
+pos/t3927.scala
+pos/t2293.scala
+pos/t3800.scala
+pos/t7285a.scala
+pos/t927.scala
+pos/t4494.scala
+pos/t3864
+pos/ilya2
+pos/t2940
+pos/S1.scala
+pos/tcpoly_wildcards.scala
+pos/tryexpr.scala
+pos/t6089b.scala
+pos/depmet_implicit_oopsla_zipwith.scala
+pos/t245.scala
+pos/t6146.scala
+pos/t1782
+pos/t851.scala
+pos/spec-thistype.scala
+pos/tcpoly_poly.scala
+pos/t6815_import.scala
+pos/t4649.scala
+pos/t0453.scala
+pos/t5020.scala
+pos/ilya
+pos/t2435.scala
+pos/t1279a.scala
+pos/t2171.scala
+pos/t1957.scala
+pos/gadts2.scala
+pos/t3567
+pos/Z.scala
+pos/t1203b
+pos/nested2.scala
+pos/t1896
+pos/viewtest2.scala
+pos/t5541.scala
+pos/existentials-harmful.scala
+pos/t4063.scala
+pos/t6485a
+pos/t1208.scala
+pos/t5041.scala
+pos/unapplyComplex.scala
+pos/t3384.scala
+pos/t4112.scala
+pos/t788.scala
+pos/hklub0.scala
+pos/t757.scala
+pos/t1197
+pos/t359.scala
+pos/t5667.scala
+pos/t1107a.scala
+pos/virtpatmat_castbinder.scala
+pos/t267.scala
+pos/t3419
+pos/t3861.scala
+pos/t6797.scala
+pos/spec-localdefs.scala
+pos/t3404
+pos/t4457_1.scala
+pos/matthias5.scala
+pos/spec-polymeth.scala
+pos/kinds.scala
+pos/t2310.scala
+pos/t6552.scala
+pos/valdefs.scala
+pos/hkarray.scala
+pos/homonym.scala
+pos/t1235
+pos/t3429
+pos/t0053.scala
+pos/depmet_implicit_chaining_zw.scala
+pos/virtpatmat_partialfun_nsdnho.scala
+pos/t6664.scala
+pos/ticket2251.scala
+pos/t3495.scala
+pos/super
+pos/t121.scala
+pos/javaConversions-2.10-ambiguity.scala
+pos/t1803.scala
+pos/t5877.scala
+pos/t0085.scala
+pos/t3582.scala
+pos/t2939.scala
+pos/t1422_pos.scala
+pos/manifest1-new.scala
+pos/t7505.scala
+pos/t5720-ownerous.scala
+pos/misc-unapply_pos.scala
+pos/tcpoly_variance_pos.scala
+pos/t5127.scala
+pos/t6123-explaintypes-implicits.scala
+pos/t2764
+pos/presuperContext.scala
+pos/spec-simple.scala
+pos/t3120
+pos/t5729.scala
+pos/tcpoly_infer_ticket716.scala
+pos/tcpoly_bounds1.scala
+pos/t7369.scala
+pos/imports-pos.scala
+pos/t5654.scala
+pos/t0123.scala
+pos/raw-map
+pos/t5330b.scala
+pos/t6485b
+pos/t6072.scala
+pos/t5692c.scala
+pos/t3430.scala
+pos/tcpoly_param_scoping.scala
+pos/t6204-b.scala
+pos/attachments-typed-another-ident
+pos/t5359.scala
+pos/ticket2197.scala
+pos/t720.scala
+pos/t2130-2.scala
+pos/t2260.scala
+pos/t0304.scala
+pos/t464.scala
+pos/spec-maps.scala
+pos/annotDepMethType.scala
+pos/t6117.scala
+pos/t911.scala
+pos/t757a.scala
+pos/t2504.scala
+pos/t1381-old.scala
+pos/t1232
+pos/needstypeearly.scala
+pos/moduletrans.scala
+pos/t4957.scala
+pos/kinzer.scala
+pos/t318.scala
+pos/widen-existential.scala
+pos/t0095.scala
+pos/t566.scala
+pos/tcpoly_overloaded.scala
+pos/t7516
+pos/t7232
+pos/t698.scala
+pos/t0002.scala
+pos/t0288
+pos/t2994b.scala
+pos/cls.scala
+pos/t3622
+pos/t3671.scala
+pos/tcpoly_subst.scala
+pos/t5703
+pos/depmet_implicit_oopsla_session_simpler.scala
+pos/t5022.scala
+pos/builders.scala
+pos/spec-foo.scala
+pos/t756.scala
+pos/t1569.scala
+pos/implicit-unwrap-tc.scala
+pos/t3688.scala
+pos/t5198.scala
+pos/t432.scala
+pos/t6022b.scala
+pos/channels.scala
+pos/t1075.scala
+pos/null.scala
+pos/t1840
+pos/t6479.scala
+pos/t6311.scala
+pos/t0039.scala
+pos/t1119.scala
+pos/t573.scala
+pos/t1136.scala
+pos/t3938
+pos/spec-sealed.scala
+pos/tcpoly_return_overriding.scala
+pos/t3582b.scala
+pos/t229.scala
+pos/t3498-old.scala
+pos/t531.scala
+pos/t4545.scala
+pos/t6651.scala
+pos/t2133.scala
+pos/tinondefcons.scala
+pos/t6157.scala
+pos/t6358.scala
+pos/t7690.scala
+pos/t5779-numeq-warn.scala
+pos/list-extractor.scala
+pos/t892.scala
+pos/t2127.scala
+pos/t7180.scala
+pos/nullary_poly.scala
+pos/virtpatmat_exist3.scala
+pos/t1176
+pos/spec-funs.scala
+pos/specialize10.scala
+pos/t6514.scala
+pos/exhaustive_heuristics.scala
+pos/t0066.scala
+pos/t460.scala
+pos/t2130-1.scala
+pos/t124.scala
+pos/annotations.scala
+pos/pat_gilles.scala
+pos/array-interfaces.scala
+pos/t6210.scala
+pos/t3792.scala
+pos/implicits-old.scala
+pos/t389.scala
+pos/t115.scala
+pos/virtpatmat_exhaust_unchecked.scala
+pos/scoping3.scala
+pos/t6033.scala
+pos/depmet_implicit_oopsla_session.scala
+pos/t602.scala
+pos/test5.scala
+pos/t611.scala
+pos/t5932.scala
+pos/t4910.scala
+pos/unapplySeq.scala
+pos/t344.scala
+pos/t3363-new.scala
+pos/t4018.scala
+pos/t4553.scala
+pos/t5082.scala
+pos/t3869.scala
+pos/t3836.scala
+pos/tcpoly_typeapp.scala
+pos/t1409
+pos/nonlocal-unchecked.scala
+pos/t0082.scala
+pos/z1720.scala
+pos/t7232c
+pos/t2018.scala
+pos/t3943
+pos/t2187-2.scala
+pos/unicode-decode.scala
+pos/t4757
+pos/t0710.scala
+pos/t0305.scala
+pos/t160.scala
+pos/t7591
+pos/simplelists.scala
+pos/List1.scala
+pos/t516.scala
+pos/t6648.scala
+pos/t5165
+pos/t0055.scala
+pos/t4744
+pos/t7377
+pos/t5726.scala
+pos/t0091.scala
+pos/t6595.scala
+pos/compile.scala
+pos/depmet_1_pos.scala
+pos/t7364
+pos/philippe3.scala
+pos/spec-doubledef-old.scala
+pos/t4651.scala
+pos/tcpoly_infer_implicit_tuple_wrapper.scala
+pos/t6274.scala
+pos/tcpoly_infer_explicit_tuple_wrapper.scala
+pos/ticket2201.scala
+pos/spec-fields-new.scala
+pos/optmatch.scala
+pos/t7517.scala
+pos/t3560.scala
+pos/t0165.scala
+pos/t0872.scala
+pos/t522.scala
+pos/t2234.scala
+pos/t5031_2.scala
+pos/tcpoly_method.scala
+pos/t6482.scala
+pos/pos-bug1241.scala
+pos/implicits-new.scala
+pos/t2484.scala
+pos/t2425.scala
+pos/t1049.scala
+pos/michel4.scala
+pos/t5958.scala
+pos/virtpatmat_instof_valuetype.scala
+pos/spec-t6286.scala
+pos/t873.scala
+pos/t3137.scala
+pos/Transactions.scala
+pos/t0064.scala
+pos/t7486-named.scala
+pos/t5444.scala
+pos/simple-exceptions.scala
+pos/t1006.scala
+pos/t7200b.scala
+pos/t3777.scala
+pos/t4840.scala
+pos/t211.scala
+pos/nullary.scala
+pos/michel1.scala
+pos/t5031_3
+pos/typealias_dubious.scala
+pos/spec-doubledef-new.scala
+pos/philippe1.scala
+pos/thistypes.scala
+pos/t3570.scala
+pos/t6516.scala
+pos/context.scala
+pos/t3808.scala
+pos/philippe2.scala
+pos/constfold.scala
+pos/t1292.scala
+pos/t1147.scala
+pos/t404.scala
+pos/t4430.scala
+pos/A.scala
+pos/spec-partially.scala
+pos/t5796.scala
+pos/t2409
+pos/t284-pos.scala
+pos/t5313.scala
+pos/t2464
+pos/t1591b.scala
+pos/hk-match
+pos/t595.scala
+pos/t6846.scala
+pos/t6162-inheritance.scala
+pos/relax_implicit_divergence.scala
+pos/patterns2.scala
+pos/t4692.scala
+pos/t3837.scala
+pos/t661.scala
+pos/t2810.scala
+pos/depexists.scala
+pos/virtpatmat_exist4.scala
+pos/t5245.scala
+pos/t7190.scala
+pos/isApplicableSafe.scala
+pos/t6204-a.scala
+pos/t0076.scala
+pos/t1756.scala
+pos/t1745
+pos/t6091.scala
+pos/t0154.scala
+pos/t530.scala
+pos/t2094.scala
+pos/t1034.scala
+pos/t6084.scala
+pos/t2454.scala
+pos/t2956
+pos/tcpoly_ticket2096.scala
+pos/attachments-typed-ident
+pos/polymorphic-case-class.scala
+pos/t252.scala
+pos/spec-constr-old.scala
+pos/t2421c.scala
+pos/t122.scala
+pos/t6574.scala
+pos/t3859.scala
+pos/spec-params-old.scala
+pos/t1196
+pos/t4593.scala
+pos/t596.scala
+pos/t615.scala
+pos/t7689.scala
+pos/t3960.scala
+pos/t3986.scala
+pos/exbound.scala
+pos/t2545.scala
+pos/t1722
+pos/t159.scala
+pos/t3272.scala
+pos/t6301.scala
+pos/t2794.scala
+pos/t3048.scala
+pos/t4970.scala
+pos/t607.scala
+pos/FPTest.scala
+pos/test1.scala
+pos/t3252.scala
+pos/t4176.scala
+pos/t112606A.scala
+pos/t2183.scala
+pos/t430-feb09.scala
+pos/t6275.scala
+pos/t1832.scala
+
+neg/volatile_no_override.scala
+neg/t800.scala
+neg/t5426.scala
+neg/t2462a.scala
+neg/t2641.scala
+neg/classtags_dont_use_typetags.scala
+neg/t5031
+neg/t2275b.scala
+neg/macro-qmarkqmarkqmark.scala
+neg/t4879.scala
+neg/t5956.scala
+neg/t4196.scala
+neg/reify_ann2b.scala
+neg/t6666b.scala
+neg/warn-unused-privates.scala
+neg/t6928.scala
+neg/t6337.scala
+neg/sealed-java-enums.scala
+neg/t563.scala
+neg/t900.scala
+neg/deadline-inf-illegal.scala
+neg/t766.scala
+neg/t5429.scala
+neg/overloaded-implicit.scala
+neg/t875.scala
+neg/abstract-class-error
+neg/unchecked2.scala
+neg/predef-masking.scala
+neg/viewtest.scala
+neg/macro-noexpand
+neg/varargs.scala
+neg/t963b.scala
+neg/t909.scala
+neg/sensitive2.scala
+neg/t5390b.scala
+neg/abstraction-from-volatile-type-error.scala
+neg/macro-exception
+neg/t4431.scala
+neg/t5689.scala
+neg/valueclasses.scala
+neg/overload.scala
+neg/t0204.scala
+neg/t908.scala
+neg/t750
+neg/patmatexhaust.scala
+neg/macro-invalidusage-badtargs
+neg/t1168.scala
+neg/t5761.scala
+neg/t0503.scala
+neg/t7235.scala
+neg/t1215.scala
+neg/primitive-sigs-1
+neg/t5578.scala
+neg/names-defaults-neg-warn.scala
+neg/t6436b.scala
+neg/t3098
+neg/t910.scala
+neg/parstar.scala
+neg/t4568.scala
+neg/newpat_unreachable.scala
+neg/warn-unused-imports.scala
+neg/t1181.scala
+neg/t5903c
+neg/t7294.scala
+neg/t4091.scala
+neg/t5452-old.scala
+neg/t5696.scala
+neg/t0209.scala
+neg/t2910.scala
+neg/t7388.scala
+neg/noMember2.scala
+neg/no-predef.scala
+neg/t6952.scala
+neg/t1909b.scala
+neg/abstract-report2.scala
+neg/t5318.scala
+neg/t6074.scala
+neg/t7171.scala
+neg/abstract-vars.scala
+neg/unchecked-impossible.scala
+neg/variances-refinement.scala
+neg/t3453.scala
+neg/t5189.scala
+neg/t4302.scala
+neg/xmltruncated7.scala
+
+run/t7249.scala
+run/t3563.scala
+run/t6111.scala
+run/classtags_multi.scala
+run/t5201.scala
+run/checked.scala
+run/valueclasses-classtag-basic.scala
+run/t7171.scala
+run/t5053.scala
+run/t4535.scala
+run/t5923d
+run/t7291.scala
+run/partialfun.scala
+run/macro-term-declared-in-package-object
+run/mapValues.scala
+run/gadts.scala
+run/t2386-new.scala
+run/virtpatmat_stringinterp.scala
+run/t657.scala
+run/t0017.scala
+run/t5713
+run/t576.scala
+run/t3580.scala
+run/virtpatmat_partial.scala
+run/t6646.scala
+run/mixins.scala
+run/t1672.scala
+run/macro-expand-implicit-macro-has-implicit
+run/tuple-match.scala
+run/t7039.scala
+run/virtpatmat_opt_sharing.scala
+run/virtpatmat_casting.scala
+run/t2176.scala
+run/eta-expand-star2.scala
+run/macro-impl-relaxed
+run/intmap.scala
+run/t751.scala
+run/t1591.scala
+run/macro-typecheck-implicitsdisabled
+run/t6911.scala
+run/t5604.scala
+run/macro-term-declared-in-default-param
+run/collection-stacks.scala
+run/multi-array.scala
+run/t4560b.scala
+run/buffer-slice.scala
+run/t5629.scala
+run/t6690.scala
+run/matchonstream.scala
+run/t3603.scala
+run/lazy-exprs.scala
+run/macro-quasiquotes
+run/Course-2002-13.scala
+run/t6337a.scala
+run/exoticnames.scala
+run/t0936.scala
+run/existentials3-old.scala
+run/runtime-richChar.scala
+run/t6272.scala
+run/t7215.scala
+run/t1939.scala
+run/ReverseSeqView.scala
+run/lazy-leaks.scala
+run/t0048.scala
+run/t3994.scala
+run/t2241.scala
+run/t627.scala
+run/t5966.scala
+run/getClassTest-valueClass.scala
+run/t3619.scala
+run/t1300.scala
+run/t2177.scala
+run/t3760.scala
+run/t1829.scala
+run/macro-expand-implicit-macro-is-view
+run/t889.scala
+run/QueueTest.scala
+run/t4537
+run/t3699.scala
+run/valueclasses-manifest-basic.scala
+run/t1192.scala
+run/macro-expand-tparams-bounds
+run/macro-expand-nullary-generic
+run/t1434.scala
+run/t6443-varargs.scala
+run/macro-term-declared-in-trait
+run/t4080.scala
+run/t2236-old.scala
+run/matcharraytail.scala
+run/infiniteloop.scala
+run/t5733.scala
+run/virtpatmat_nested_lists.scala
+run/t5158.scala
+run/t6695.scala
+run/t6070.scala
+run/t4558.scala
+run/exc2.scala
+run/patmat-behavior-2.scala
+run/overloads.scala
+run/iterator-iterate-lazy.scala
+run/t6957.scala
+run/transform.scala
+run/t5500.scala
+run/t6663.scala
+run/castsingleton.scala
+run/t4147.scala
+run/virtpatmat_staging.scala
+run/t4565_1.scala
+run/t5588.scala
+run/run-bug4840.scala
+run/t3496.scala
+run/t5867.scala
+run/search.scala
+run/t3112.scala
+run/hashsetremove.scala
+run/interop_manifests_are_classtags.scala
+run/t6443.scala
+run/macro-expand-tparams-prefix
+run/contrib674.scala
+run/t3508.scala
+run/t4300.scala
+run/virtpatmat_typed.scala
+run/macro-term-declared-in-class-object
+run/map_test.scala
+run/t5040.scala
+run/t4827b.scala
+run/lift-and-unlift.scala
+run/t6574b.scala
+run/t7240
+run/t3984.scala
+run/virtpatmat_tailcalls_verifyerror.scala
+run/macro-term-declared-in-class-class
+run/emptypf.scala
+run/t6631.scala
+run/t6104.scala
+run/t2818.scala
+run/t3761-overload-byname.scala
+run/t2526.scala
+run/phantomValueClass.scala
+run/t3126.scala
+run/arybufgrow.scala
+run/t3980.scala
+run/t7375b
+run/t6077_patmat_cse_irrefutable.scala
+run/classmanifests_new_core.scala
+run/t3395.scala
+run/name-based-patmat.scala
+run/inliner-infer.scala
+run/t5171.scala
+run/t3726.scala
+run/null-hash.scala
+run/t4027.scala
+run/t2544.scala
+run/patmatnew.scala
+run/t5923b
+run/t7242.scala
+run/classtags_core.scala
+run/streamWithFilter.scala
+run/t3038b.scala
+run/macro-expand-varargs-explicit-over-nonvarargs-good
+run/macro-divergence-spurious
+run/macro-duplicate
+run/t2958.scala
+run/patch-boundary.scala
+run/t2333.scala
+run/lazy-override-run.scala
+run/macro-quasiinvalidbody-c
+run/t5037.scala
+run/takeAndDrop.scala
+run/t6126.scala
+run/t0883.scala
+run/t7617a
+run/t4171.scala
+run/empty-array.scala
+run/t7198.scala
+run/t493.scala
+run/genericValueClass.scala
+run/t0677-old.scala
+run/t1373.scala
+run/t4461.scala
+run/t6011b.scala
+run/t7584.scala
+run/t3935.scala
+run/t6928-run.scala
+run/t744.scala
+run/t3241.scala
+run/blame_eye_triple_eee-double.scala
+run/t3829.scala
+run/t5577.scala
+run/t5914.scala
+run/t601.scala
+run/t5610.scala
+run/macro-basic-mamd-mi
+run/t6150.scala
+run/stringbuilder.scala
+run/t7290.scala
+run/t6888.scala
+run/t6327.scala
+run/virtpatmat_unapplyseq.scala
+run/t4656.scala
+run/macro-term-declared-in-method
+run/macro-expand-implicit-macro-is-implicit
+run/blame_eye_triple_eee-float.scala
+run/t4482.scala
+run/t5488.scala
+run/matchemptyarray.scala
+run/t3714.scala
+run/richWrapperEquals.scala
+run/t5328.scala
+run/stream_flatmap_odds.scala
+run/implicitclasses.scala
+run/t6827.scala
+run/t6394b
+run/complicatedmatch.scala
+run/valueclasses-classmanifest-basic.scala
+run/unreachable.scala
+run/caseclasses.scala
+run/withIndex.scala
+run/exc1.scala
+run/amp.scala
+run/t1423.scala
+run/t594.scala
+run/t6353.scala
+run/byname.scala
+run/vector1.scala
+run/t5879.scala
+run/t1048.scala
+run/t5080.scala
+run/t4190.scala
+run/caseClassEquality.scala
+run/macro-enclosures
+run/collections-toSelf.scala
+run/implicits.scala
+run/finalvar.scala
+run/lazy-locals.scala
+run/t7231.scala
+run/t0508.scala
+run/t6628.scala
+run/t6406-regextract.scala
+run/t0911.scala
+run/t4013c.scala
+run/t3502.scala
+run/t5648.scala
+run/retclosure.scala
+run/t2857.scala
+run/t4859.scala
+run/t5162.scala
+run/t3038.scala
+run/classof.scala
+run/t4062.scala
+run/unapplyArray.scala
+run/t4297.scala
+run/t5923a
+run/iterators.scala
+run/t1537.scala
+run/boolexprs.scala
+run/valueclasses-classtag-generic.scala
+run/macro-term-declared-in-anonymous
+run/tcpoly_monads.scala
+run/t5407.scala
+run/scan.scala
+run/forvaleq.scala
+run/null-and-intersect.scala
+run/t7047
+run/t0607.scala
+run/sequenceComparisons.scala
+run/t4396.scala
+run/macro-undetparams-consfromsls
+run/t2029.scala
+run/t1220.scala
+run/option-fold.scala
+run/t5284c.scala
+run/macro-auto-duplicate
+run/t3529.scala
+run/t4697.scala
+run/t2251.scala
+run/t5300.scala
+run/virtpatmat_valdef.scala
+run/t2147.scala
+run/virtpatmat_extends_product.scala
+run/list_map.scala
+run/t1333.scala
+run/matchbytes.scala
+run/valueclasses-classmanifest-existential.scala
+run/records.scala
+run/t3088.scala
+run/macro-def-path-dependent
+run/t6443-by-name.scala
+run/t1044.scala
+run/delay-good.scala
+run/case-class-23.scala
+run/weakconform.scala
+run/patmat-bind-typed.scala
+run/t4835.scala
+run/t3097.scala
+run/t405.scala
+run/existentials.scala
+run/t2876.scala
+run/t4809.scala
+run/t1427.scala
+run/t6135.scala
+run/t3575.scala
+run/t5688.scala
+run/t6900.scala
+run/macro-expand-unapply-a
+run/t6677b.scala
+run/t7375a.scala
+run/t7300.scala
+run/t6246.scala
+run/typed-annotated
+run/elidable-noflags.scala
+run/t0042.scala
+run/t3050.scala
+run/t4536.scala
+run/NestedClasses.scala
+run/t3877.scala
+run/seqlike-kmp.scala
+run/t5907.scala
+run/t266.scala
+run/missingparams.scala
+run/t2255.scala
+run/private-inline.scala
+run/t3488.scala
+run/t3950.scala
+run/typealias_overriding.scala
+run/constant-optimization.scala
+run/t7507.scala
+run/t6090.scala
+run/iterator-concat.scala
+run/t4582.scala
+run/macro-term-declared-in-class
+run/macro-typecheck-macrosdisabled2
+run/t3425.scala
+run/t4935.scala
+run/t3326.scala
+run/boolord.scala
+run/t1141.scala
+run/virtpatmat_unapply.scala
+run/t5971.scala
+run/t3651.scala
+run/macro-sip19-revised
+run/pure-args-byname-noinline.scala
+run/preinits.scala
+run/t5532.scala
+run/concat-two-strings.scala
+run/t3269.scala
+run/macro-impl-default-params
+run/t2162.scala
+run/matchonseq.scala
+run/t5428.scala
+run/macro-expand-overload
+run/t4660.scala
+run/enrich-gentraversable.scala
+run/macro-expand-override
+run/t4054.scala
+run/t4753.scala
+run/valueclasses-manifest-generic.scala
+run/macro-typecheck-macrosdisabled
+run/t2308a.scala
+run/duplicate-meth.scala
+run/interop_classtags_are_classmanifests.scala
+run/t3232.scala
+run/t2075.scala
+run/virtpatmat_partial_backquoted.scala
+run/try-2.scala
+run/macro-openmacros
+run/macro-undetparams-macroitself
+run/t6318_derived.scala
+run/deprecate-early-type-defs.scala
+run/dead-code-elimination.scala
+run/t4827.scala
+run/Course-2002-07.scala
+run/slice-strings.scala
+run/t6292.scala
+run/t6206.scala
+run/t1042.scala
+run/t1718.scala
+run/t2074_2.scala
+run/arraycopy.scala
+run/indexedSeq.scala
+run/macro-term-declared-in-implicit-class
+run/t3511.scala
+run/t6290.scala
+run/distinct.scala
+run/virtpatmat_alts.scala
+run/valueclasses-pavlov.scala
+run/exceptions.scala
+run/t1368.scala
+run/t5856.scala
+run/t6968.scala
+run/names-defaults.scala
+run/macro-expand-tparams-implicit
+run/t5881.scala
+run/t3540.scala
+run/virtpatmat_try.scala
+run/t7181.scala
+run/value-class-extractor.scala
+run/value-class-extractor-2.scala
+run/t3150.scala
+run/exc.scala
+run/t3516.scala
+run/delay-bad.scala
+run/infix.scala
+run/t1309.scala
+run/t6370.scala
+run/t6725-2.scala
+run/macro-impl-tparam-typetag-is-optional
+run/macro-term-declared-in-block
+run/matchnull.scala
+run/t2127.scala
+run/t7325.scala
+run/groupby.scala
+run/t3932.scala
+run/t4871.scala
+run/longmap.scala
+run/t1524.scala
+run/t6187b.scala
+run/kmpSliceSearch.scala
+run/t7088.scala
+run/t5804.scala
+run/stringbuilder-drop.scala
+run/t5753_1
+pos/cyclics-pos.scala
+pos/cfcrash.scala
+pos/tcpoly_higherorder_bound_method.scala
+pos/t5084.scala
+pos/trait-force-info.scala
+pos/macro-qmarkqmarkqmark.scala
+pos/t7785.scala
+pos/nested.scala
+pos/t3152.scala
+pos/t5031
+pos/t6925b.scala
+pos/t1107b
+pos/t5012.scala
+pos/virtpatmat_obj_in_case.scala
+pos/t4938.scala
+pos/t3856.scala
+pos/spec-cyclic.scala
+pos/aliases.scala
+pos/typerep_pos.scala
+pos/t119.scala
+pos/t1050.scala
+pos/t3670.scala
+pos/t6145.scala
+pos/t7315.scala
+pos/t5930.scala
+pos/t789.scala
+pos/t5071.scala
+pos/t4731.scala
+pos/t4547.scala
+pos/t2038.scala
+pos/testCoercionThis.scala
+pos/t2444.scala
+pos/t5744
+pos/t780.scala
+pos/t1722-A.scala
+pos/virtpatmat_exist1.scala
+pos/t6225.scala
+pos/t762.scala
+pos/t0204.scala
+pos/rebind.scala
+pos/spec-short.scala
+pos/comp-rec-test.scala
+pos/lub-dealias-widen.scala
+pos/t1168.scala
+pos/modules.scala
+pos/t4220.scala
+pos/t4070.scala
+pos/t175.scala
+pos/t2500.scala
+pos/t5029.scala
+pos/itay.scala
+pos/t4202.scala
+pos/t1987b
+pos/t3534.scala
+pos/infer2-pos.scala
+pos/spec-sparsearray-new.scala
+pos/t7091.scala
+pos/ticket0137.scala
+pos/collectGenericCC.scala
+pos/t640.scala
+pos/t4305.scala
+pos/extractor-types.scala
+pos/t3880.scala
+pos/spec-annotations.scala
+pos/t3577.scala
+pos/compile1.scala
+pos/spec-t3497.scala
+pos/hkrange.scala
+pos/t287.scala
+pos/t7294.scala
+pos/t6008.scala
+pos/t4432.scala
+pos/CustomGlobal.scala
+pos/patmat.scala
+pos/t2413
+pos/t2910.scala
+pos/t592.scala
+pos/t6245
+pos/infer.scala
+pos/t7228.scala
+pos/compound.scala
+pos/attributes.scala
+pos/t6771.scala
+pos/t1090.scala
+pos/t684.scala
+pos/t577.scala
+pos/t4273.scala
+pos/t6278-synth-def.scala
+pos/t6184.scala
+neg/t0214.scala
+neg/t4842.scala
+neg/t6214.scala
+neg/reify_nested_inner_refers_to_local.scala
+neg/t576.scala
+neg/t5969.scala
+neg/tcpoly_variance.scala
+neg/t7509.scala
+neg/mixins.scala
+neg/parent-inherited-twice-error.scala
+neg/macro-abort
+neg/constructor-init-order.scala
+neg/t6042.scala
+neg/t0590.scala
+neg/eta-expand-star-deprecation.scala
+neg/t4221.scala
+neg/t6263.scala
+neg/t783.scala
+neg/t5554.scala
+neg/macro-invalidsig-params-badtype
+neg/multi-array.scala
+neg/raw-types-stubs
+neg/spec-overrides.scala
+neg/t836.scala
+neg/t7289_status_quo.scala
+neg/t5675.scala
+neg/macro-quasiquotes
+neg/t6667.scala
+neg/t6597.scala
+neg/t6264.scala
+neg/t0345.scala
+neg/t7294b.scala
+neg/t5340.scala
+neg/t2144.scala
+neg/t1010.scala
+neg/t1838.scala
+neg/t5189b.scala
+neg/reify_metalevel_breach_-1_refers_to_1.scala
+neg/t6601
+neg/wellkinded_wrongarity.scala
+neg/t3909.scala
+neg/t876.scala
+neg/t5390.scala
+neg/unit2anyref.scala
+neg/t0351.scala
+neg/t5120.scala
+neg/t1038.scala
+neg/t5878.scala
+neg/qualifying-class-error-2.scala
+neg/t3816.scala
+neg/tailrec.scala
+neg/volatile.scala
+neg/t944.scala
+neg/t1705.scala
+neg/t3977.scala
+neg/t5553_2.scala
+neg/t5318c.scala
+neg/overload-msg.scala
+neg/t5440.scala
+neg/t6335.scala
+neg/compile-time-only-b.scala
+neg/t501.scala
+neg/override.scala
+neg/t663.scala
+neg/t5892.scala
+neg/t1980.scala
+neg/macro-false-deprecation-warning
+neg/t5148.scala
+neg/t585.scala
+neg/t3776.scala
+neg/interop_classtags_arenot_manifests.scala
+neg/t4044.scala
+neg/macro-invalidusage-nontypeable
+neg/t6375.scala
+neg/t500.scala
+neg/t4877.scala
+neg/t5357.scala
+neg/interop_abstypetags_arenot_manifests.scala
+neg/t4460a.scala
+neg/t5318b.scala
+neg/t3234.scala
+neg/t4440.scala
+neg/t6663.scala
+neg/t6357.scala
+neg/gadts1.scala
+neg/cyclics.scala
+neg/t5060.scala
+neg/scopes.scala
+run/t4013.scala
+run/value-class-extractor-seq.scala
+run/macro-expand-tparams-explicit
+run/tuples.scala
+run/t5753_2
+run/t0528.scala
+run/t5105.scala
+run/t1195-old.scala
+run/t7341.scala
+run/t3670.scala
+run/t2594_tcpoly.scala
+run/t3895.scala
+run/t0668.scala
+run/slices.scala
+run/t6666a.scala
+run/valueclasses-classmanifest-generic.scala
+run/t2316_run.scala
+run/t3004.scala
+run/viewtest.scala
+run/t6481.scala
+run/t0005.scala
+run/t4110-old.scala
+run/t4766.scala
+run/t5500b.scala
+run/t7407b.scala
+run/backreferences.scala
+run/arrayview.scala
+run/t629.scala
+run/t5903c
+run/unittest_collection.scala
+run/spec-nlreturn.scala
+run/macro-term-declared-in-object-object
+run/triple-quoted-expr.scala
+run/t5937.scala
+run/t6011c.scala
+run/macro-expand-implicit-argument
+run/try.scala
+run/t1987b
+run/t6089.scala
+run/macro-range
+run/t2524.scala
+run/t4770.scala
+run/virtpatmat_unapplyprod.scala
+run/t1535.scala
+run/ctor-order.scala
+pos/t5210.scala
+pos/t5384.scala
+pos/rangepos.scala
+pos/t443.scala
+pos/t1480.scala
+pos/t116.scala
+pos/seqtest2.scala
+pos/scoping1.scala
+pos/t4269.scala
+pos/lookupswitch.scala
+pos/t3642
+pos/t5706.scala
+pos/SI-5788.scala
+pos/t7264
+pos/t0031.scala
+pos/macro-deprecate-dont-touch-backquotedidents.scala
+pos/t6815.scala
+pos/test4refine.scala
+pos/michel5.scala
+pos/t0851.scala
+pos/t1185.scala
+pos/sudoku.scala
+pos/t7520.scala
+pos/t6208.scala
+pos/t3411.scala
+pos/t295.scala
+pos/S3.scala
+pos/t0674.scala
+pos/t6664b.scala
+pos/variances_pos.scala
+pos/liftcode_polymorphic.scala
+pos/t3174b.scala
+pos/t7232d
+pos/t578.scala
+pos/implicit-infix-ops.scala
+pos/t4363.scala
+pos/t532.scala
+pos/exponential-spec.scala
+pos/t599.scala
+pos/t5862.scala
+pos/t4603
+pos/t3676.scala
+pos/t1357.scala
+pos/native-warning.scala
+pos/t1230
+pos/t6028
+pos/t4275.scala
+pos/overloaded_extractor_and_regular_def.scala
+pos/t4205
+pos/matthias1.scala
+pos/testcast.scala
+pos/generic-sigs.scala
+pos/t0093.scala
+pos/specializes-sym-crash.scala
+pos/t0061.scala
+pos/t2429.scala
+pos/t694.scala
+pos/javaReadsSigs
+pos/t2023.scala
+pos/t704.scala
+pos/t2208_pos.scala
+pos/t5137.scala
+pos/t2683.scala
+pos/t0049.scala
+pos/t1029
+pos/t4243.scala
+pos/typerep-stephane.scala
+pos/t177.scala
+pos/t5967.scala
+pos/t430.scala
+pos/virtpatmat_infer_single_1.scala
+pos/pat_iuli.scala
+pos/t1071.scala
+pos/t7226.scala
+pos/t1843.scala
+pos/t419.scala
+pos/t7364b
+pos/t1159.scala
+pos/t5305.scala
+pos/t7694.scala
+pos/t6047.scala
+pos/t3578.scala
+pos/t2082.scala
+pos/setter-not-implicit.scala
+pos/t1133.scala
+pos/t3862.scala
+pos/t942
+pos/nothing_manifest_disambig-new.scala
+pos/iterator-traversable-mix.scala
+pos/eta.scala
+pos/test4.scala
+pos/t2691.scala
+pos/t4502.scala
+pos/t7183.scala
+pos/protected-t1010.scala
+pos/X.scala
+pos/virtpatmat_exist2.scala
+pos/t4911.scala
+pos/t3477.scala
+pos/t4173.scala
+pos/t7782.scala
+pos/t2399.scala
+pos/virtpatmat_alts_subst.scala
+pos/propagate.scala
+pos/t2421b_pos.scala
+pos/t183.scala
+pos/t7033.scala
+pos/t3612.scala
+pos/t5330c.scala
+pos/t3020.scala
+pos/t4869.scala
+pos/t3373.scala
+pos/spec-params-new.scala
+pos/t3672.scala
+pos/t4501.scala
+pos/t1565.scala
+pos/t3774.scala
+pos/t6942
+neg/t3275.scala
+neg/t421.scala
+neg/t5702-neg-bad-brace.scala
+neg/t3663
+neg/badtok-1.scala
+neg/t677.scala
+neg/t7756b.scala
+neg/t6534.scala
+neg/t6276.scala
+neg/t5762.scala
+neg/abstract.scala
+neg/t2405.scala
+neg/t0418.scala
+neg/t5390c.scala
+neg/lazyvals.scala
+neg/lubs.scala
+neg/abstract-report.scala
+neg/t4163.scala
+neg/t5702-neg-bad-and-wild.scala
+neg/macro-invalidret
+neg/t6728.scala
+neg/t5152.scala
+neg/t1432.scala
+neg/abstract-inaccessible.scala
+neg/import-precedence.scala
+neg/t2462b.scala
+neg/macro-invalidusage-presuper
+neg/specification-scopes
+neg/t6048.scala
+neg/t4079
+neg/macro-basic-mamdmi
+neg/t7020.scala
+neg/t3015.scala
+neg/t0207.scala
+neg/t2296b
+neg/t0673
+neg/t3761-overload-byname.scala
+neg/t6675.scala
+neg/t5529.scala
+neg/sensitive.scala
+neg/t742.scala
+neg/t5067.scala
+neg/t6162-overriding.scala
+neg/variances.scala
+neg/t5728.scala
+neg/t6323a.scala
+neg/compile-time-only-a.scala
+neg/t6795.scala
+neg/t2494.scala
+neg/t3649.scala
+neg/macro-invalidsig
+neg/t2796.scala
+neg/t112706A.scala
+neg/t0764.scala
+neg/t3757
+neg/t1431.scala
+neg/exhausting.scala
+neg/t1523.scala
+neg/t779.scala
+neg/xmltruncated1.scala
+neg/t2208.scala
+neg/t2078.scala
+neg/t521.scala
+neg/null-unsoundness.scala
+neg/stmt-expr-discard.scala
+neg/t0513.scala
+neg/unchecked-abstract.scala
+neg/t4460c.scala
+neg/divergent-implicit.scala
+neg/t5078.scala
+neg/t1701.scala
+neg/t0816.scala
+neg/t1672b.scala
+neg/macro-invalidusage-badbounds
+neg/tailrec-2.scala
+neg/t4064.scala
+neg/reflection-names-neg.scala
+neg/t5510.scala
+neg/t3873.scala
+neg/tailrec-3.scala
+neg/t0226.scala
+neg/t2031.scala
+neg/t633.scala
+neg/constrs.scala
+neg/anyval-anyref-parent.scala
+neg/t7290.scala
+neg/t1041.scala
+neg/patternalts.scala
+neg/error_tooManyArgsPattern.scala
+neg/checksensibleUnit.scala
+neg/t6539
+neg/t4417.scala
+neg/wellkinded_app.scala
+neg/for-comprehension-old.scala
+neg/t2779.scala
+neg/object-not-a-value.scala
+neg/t2968b.scala
+neg/t6483.scala
+neg/t6902.scala
+neg/t6963a.scala
+neg/t3399.scala
+neg/t0015.scala
+neg/t3995.scala
+neg/t276.scala
+neg/t6758.scala
+neg/t2441.scala
+neg/cycle-bounds.scala
+neg/t1241.scala
+neg/t4137.scala
+neg/unicode-unterminated-quote.scala
+neg/t4762.scala
+neg/typeerror.scala
+neg/implicits.scala
+neg/t961.scala
+neg/ambiguous-float-dots2.scala
+neg/t2416.scala
+neg/t5799.scala
+neg/t7285.scala
+neg/implicit-shadow.scala
+neg/t2388.scala
+neg/java-access-neg
+neg/found-req-variance.scala
+neg/hk-bad-bounds.scala
+neg/t3224.scala
+neg/t1033.scala
+neg/t7385.scala
+neg/t5882.scala
+neg/t4541.scala
+neg/t2973.scala
+neg/t6406-regextract.scala
+neg/t6666.scala
+neg/t4831.scala
+neg/t425.scala
+neg/t1845.scala
+neg/t3683b.scala
+neg/t2801.scala
+neg/t6083.scala
+neg/t0528neg.scala
+neg/stringinterpolation_macro-neg.scala
+neg/t668.scala
+neg/t5666.scala
+neg/t4271.scala
+neg/interop_typetags_arenot_classmanifests.scala
+neg/t1355.scala
+neg/t715.scala
+neg/t7238.scala
+neg/t7473.scala
+neg/t7292-removal.scala
+neg/tcpoly_infer_ticket1162.scala
+neg/t4098.scala
+neg/t6013
+neg/t6227.scala
+neg/t464-neg.scala
+neg/badtok-3.scala
+neg/t6082.scala
+neg/anytrait.scala
+neg/valueclasses-doubledefs.scala
+neg/t7519.scala
+neg/overloaded-unapply.scala
+neg/t1163.scala
+neg/wellkinded_bounds.scala
+neg/t7292-deprecation.scala
+neg/t5044.scala
+neg/t0842.scala
+neg/t6436.scala
+neg/interop_typetags_arenot_classtags.scala
+neg/t3653.scala
+neg/higherkind_novalue.scala
+neg/t935.scala
+neg/t6040.scala
+neg/annot-nonconst.scala
+neg/macro-deprecate-idents.scala
+neg/illegal-stmt-start.scala
+neg/t565.scala
+neg/case-collision.scala
+neg/t3209.scala
+neg/t5821.scala
+neg/abstract-class-2.scala
+neg/t846.scala
+neg/quasiquotes-syntax-error-position.scala
+neg/t3987.scala
+neg/t877.scala
+neg/t0117.scala
+neg/t692.scala
+neg/t6666d.scala
+neg/t5702-neg-ugly-xbrace.scala
+neg/t7752.scala
+neg/case-collision2.scala
+neg/t6526.scala
+neg/t2213.scala
+neg/t7756a.scala
+neg/t845.scala
+neg/macro-override-macro-overrides-abstract-method-a
+neg/tcpoly_ticket2101.scala
+neg/delayed-init-ref.scala
+neg/caseinherit.scala
+neg/t3189.scala
+neg/unchecked-suppress.scala
+neg/t2180.scala
+neg/t1371.scala
+neg/macro-cyclic
+neg/t6123-explaintypes-macros
+neg/t4134.scala
+neg/t691.scala
+neg/t2421b.scala
+neg/t4691_exhaust_extractor.scala
+neg/t4419.scala
+neg/t5801.scala
+neg/t650.scala
+neg/t5735.scala
+neg/t696.scala
+neg/t882.scala
+neg/t2968.scala
+neg/t7507.scala
+neg/macro-invalidusage-badargs
+neg/macro-reify-typetag-typeparams-notags
+neg/wellkinded_app2.scala
+neg/t4425b.scala
+neg/t2296a
+neg/t1878.scala
+neg/t649.scala
+neg/override-object-no.scala
+neg/t4174.scala
+neg/t2070.scala
+neg/sabin2.scala
+neg/t5903e
+neg/t6566a.scala
+neg/finitary-error.scala
+neg/t4818.scala
+neg/t3614.scala
+neg/t6666c.scala
+neg/ticket513.scala
+neg/suggest-similar.scala
+neg/t4457_1.scala
+neg/t6666e.scala
+neg/tcpoly_bounds.scala
+neg/t4727.scala
+neg/t4425.scala
+neg/macro-invalidusage-methodvaluesyntax
+neg/t3854.scala
+neg/t3006.scala
+neg/t5580b.scala
+neg/t5378.scala
+neg/t639.scala
+neg/wrong-args-for-none.scala
+neg/t7171b.scala
+neg/t5361.scala
+neg/unreachablechar.scala
+neg/t5572.scala
+neg/t7757a.scala
+neg/macro-invalidimpl
+neg/t2773.scala
+neg/t6359.scala
+neg/saito.scala
+neg/xmltruncated2.scala
+neg/t667.scala
+neg/t3934.scala
+neg/t6771b.scala
+neg/t4584.scala
+neg/wellkinded_wrongarity2.scala
+neg/t7369.scala
+neg/t1477.scala
+neg/t5617.scala
+neg/t7299.scala
+neg/faculty.scala
+neg/virtpatmat_reach_null.scala
+neg/macro-reify-typetag-hktypeparams-notags
+neg/t1224.scala
+neg/xmltruncated3.scala
+neg/t1872.scala
+neg/t558.scala
+neg/t7110.scala
+neg/any-vs-anyref.scala
+neg/t6340.scala
+neg/t4166.scala
+neg/t2918.scala
+neg/t5856.scala
+neg/t4989.scala
+neg/t0003.scala
+neg/t1183.scala
+neg/t963.scala
+neg/t4515.scala
+neg/valueclasses-pavlov.scala
+neg/t608.scala
+neg/choices.scala
+neg/patmat-type-check.scala
+neg/valueclasses-impl-restrictions.scala
+neg/imp2.scala
+neg/protected-constructors.scala
+neg/t6788.scala
+neg/nullary-override.scala
+neg/t200.scala
+neg/t343.scala
+neg/names-defaults-neg-ref.scala
+neg/tcpoly_typealias.scala
+neg/classtags_contextbound_b.scala
+neg/t729.scala
+neg/t5683.scala
+neg/t4928.scala
+neg/t700.scala
+neg/t7669.scala
+neg/macro-invalidshape
+neg/t6011.scala
+neg/t7325.scala
+neg/check-dead.scala
+neg/t550.scala
+neg/t5663-badwarneq.scala
+neg/t0699
+neg/nopredefs.scala
+neg/t3507-old.scala
+neg/t5352.scala
+neg/t6336.scala
+neg/interop_classmanifests_arenot_typetags.scala
+neg/sealed-final-neg.scala
+neg/t2102.scala
+neg/t7636.scala
+neg/t5031b
+neg/t798.scala
+neg/t5702-neg-bad-xbrace.scala
+neg/t0899.scala
+neg/cyclics-import.scala
+neg/badtok-2.scala
+neg/t473.scala
+neg/t3160ambiguous.scala
+neg/t5106.scala
+neg/t1286
+neg/macro-override-macro-overrides-abstract-method-b
+neg/t0259.scala
+neg/t510.scala
+neg/t3836.scala
+neg/t5830.scala
+neg/t1548
+neg/t5580a.scala
+neg/forward.scala
+neg/t591.scala
+neg/t6558b.scala
+neg/t556.scala
+neg/xmltruncated4.scala
+neg/t5497.scala
+neg/t409.scala
+neg/t6283.scala
+neg/override-object-flag.scala
+neg/constructor-prefix-error.scala
+neg/eta-expand-star.scala
+neg/t3392.scala
+neg/t1275.scala
+neg/nested-fn-print.scala
+neg/t7330.scala
+neg/t2275a.scala
+neg/t630.scala
+neg/t4270.scala
+neg/t2775.scala
+neg/pat_unreachable.scala
+neg/t4158.scala
+neg/unit-returns-value.scala
+neg/t1422.scala
+neg/reify_metalevel_breach_-1_refers_to_0_b.scala
+neg/reassignment.scala
+neg/t3683a.scala
+neg/noMember1.scala
+neg/macro-without-xmacros-b
+neg/t1106.scala
+neg/t5182.scala
+neg/t6889.scala
+neg/t4217.scala
+neg/t7501
+neg/t5063.scala
+neg/t1009.scala
+neg/t997.scala
+neg/unchecked.scala
+neg/classtags_contextbound_c.scala
+neg/applydynamic_sip.scala
+neg/t7715.scala
+neg/t588.scala
+neg/t6667b.scala
+neg/t7757b.scala
+neg/t4069.scala
+neg/t515.scala
+neg/variances2.scala
+neg/t1049.scala
+neg/t7289.scala
+neg/t1623.scala
+neg/permanent-blindness.scala
+neg/t5803.scala
+neg/super-cast-or-test.scala
+neg/nonlocal-warning.scala
+neg/t5687.scala
+neg/t5903a
+neg/t6566b.scala
+neg/unchecked-knowable.scala
+neg/t5093.scala
+neg/protected-static-fail
+neg/type-diagnostics.scala
+neg/forgot-interpolator.scala
+neg/interop_abstypetags_arenot_classmanifests.scala
+neg/t5376.scala
+neg/t545.scala
+neg/xmlcorner.scala
+neg/switch.scala
+neg/depmet_1.scala
+neg/abstract-concrete-methods.scala
+neg/t4987.scala
+neg/t5452-new.scala
+neg/t750b
+neg/unchecked-refinement.scala
+neg/t418.scala
+neg/t5354.scala
+neg/t3736.scala
+neg/t631.scala
+neg/t6829.scala
+neg/t0218.scala
+neg/volatile-intersection.scala
+neg/t412.scala
+neg/t693.scala
+neg/t4882.scala
+neg/t1960.scala
+neg/macro-divergence-controlled
+neg/t712.scala
+neg/t5544
+neg/t3222.scala
+neg/t3604.scala
+neg/t1112.scala
+neg/t7157
+neg/accesses.scala
+neg/t452.scala
+neg/t6162-inheritance
+neg/t2442
+neg/t6567.scala
+neg/lazy-override.scala
+neg/abstract-explaintypes.scala
+neg/nested-annotation.scala
+neg/t5753
+neg/t4283b
+neg/t3691.scala
+neg/infix-op-positions.scala
+neg/t3403.scala
+neg/t4851
+neg/structural.scala
+neg/error_dependentMethodTpeConversionToFunction.scala
+neg/t5839.scala
+neg/t5553_1.scala
+neg/reify_metalevel_breach_+0_refers_to_1.scala
+neg/t752.scala
+neg/t6574.scala
+neg/t3714-neg.scala
+neg/t4457_2.scala
+neg/t2148.scala
+neg/t3240.scala
+neg/t1364.scala
+neg/saferJavaConversions.scala
+neg/t414.scala
+neg/t5493.scala
+neg/classtags_contextbound_a.scala
+neg/reify_metalevel_breach_-1_refers_to_0_a.scala
+neg/t3118.scala
+neg/t512.scala
+neg/t2336.scala
+neg/t856.scala
+neg/xmltruncated6.scala
+neg/t2206.scala
+neg/virtpatmat_unreach_select.scala
+neg/t6258.scala
+neg/t6815.scala
+neg/not-possible-cause.scala
+neg/dbldef.scala
+neg/qualifying-class-error-1.scala
+neg/t835.scala
+neg/t5455.scala
+neg/t6558.scala
+neg/t708.scala
+neg/macro-nontypeablebody
+neg/t0565.scala
+neg/xmltruncated5.scala
+neg/t5390d.scala
+neg/t520.scala
+neg/t6138.scala
+neg/macro-without-xmacros-a
+neg/t7214neg.scala
+neg/t2870.scala
+neg/t593.scala
+neg/t4541b.scala
+neg/t4460b.scala
+neg/t284.scala
+neg/t2488.scala
+neg/macro-override-method-overrides-macro
+neg/interop_abstypetags_arenot_classtags.scala
+neg/t3769.scala
+neg/warn-inferred-any.scala
+neg/t664.scala
+neg/t5903d
+neg/t562.scala
+neg/t2316.scala
+neg/t0152.scala
+neg/migration28.scala
+neg/t6443c.scala
+neg/tcpoly_override.scala
+neg/t7324.scala
+neg/t987.scala
+neg/t5903b
+neg/t3481.scala
+neg/t6912.scala
+neg/tcpoly_variance_enforce.scala
+neg/t3913.scala
+neg/names-defaults-neg.scala
+neg/t765.scala
+neg/t5358.scala
+neg/t391.scala
+neg/serialversionuid-not-const.scala
+neg/t771.scala
+neg/t0903.scala
+neg/catch-all.scala
+neg/classmanifests_new_deprecations.scala
+neg/t0606.scala
+neg/t5189_inferred.scala
+neg/macro-reify-typetag-useabstypetag
+neg/t5543.scala
+neg/logImplicits.scala
+neg/interop_typetags_without_classtags_arenot_manifests.scala
+neg/t6535.scala
+neg/t7259.scala
+neg/t2139.scala
+neg/t278.scala
+neg/t5564.scala
+neg/unchecked3.scala
+neg/virtpatmat_reach_sealed_unsealed.scala
+neg/checksensible.scala
+neg/t7721.scala
+run/t3798.scala
+run/macro-expand-varargs-explicit-over-varargs
+run/t3888.scala
+run/t0677-new.scala
+run/t3273.scala
+run/t3763.scala
+run/t2755.scala
+run/t920.scala
+run/t5610a.scala
+run/literals.scala
+run/proxy.scala
+run/unapply.scala
+run/t5830.scala
+run/array-addition.scala
+run/macro-expand-nullary-nongeneric
+run/macro-basic-ma-mdmi
+run/valueclasses-constr.scala
+run/t1247.scala
+run/t3487.scala
+run/rawstrings.scala
+run/patmat-seqs.scala
+run/eta-expand-star.scala
+run/t7436.scala
+run/t3996.scala
+run/constructors.scala
+run/t498.scala
+run/t3835.scala
+run/t298.scala
+run/t2867.scala
+run/t7120
+run/virtpatmat_literal.scala
+run/t2175.scala
+run/t2503.scala
+run/t3026.scala
+run/t603.scala
+run/t0091.scala
+run/t6394a
+run/macro-expand-varargs-implicit-over-varargs
+run/t7407.scala
+run/t2552.scala
+run/priorityQueue.scala
+run/virtpatmat_npe.scala
+run/macro-sip19
+run/t6644.scala
+run/t6614.scala
+run/t2005.scala
+run/t4680.scala
+run/t5903a
+run/classtags_contextbound.scala
+run/Course-2002-05.scala
+run/applydynamic_sip.scala
+run/t1766.scala
+run/retsynch.scala
+run/t7715.scala
+run/t102.scala
+run/nonlocalreturn.scala
+run/macro-reify-staticXXX
+run/Course-2002-06.scala
+run/t6863.scala
+run/t6500.scala
+run/macro-impl-rename-context
+run/t4351.scala
+run/t5009.scala
+run/macro-term-declared-in-annotation
+run/t6271.scala
+run/array-existential-bound.scala
+run/t6443b.scala
+run/t1987.scala
+run/MutableListTest.scala
+run/t7571.scala
+run/t5488-fn.scala
+run/macro-bodyexpandstoimpl
+run/macro-reify-ref-to-packageless
+run/t2212.scala
+run/macro-expand-varargs-implicit-over-nonvarargs
+run/t0807.scala
+run/patmat-behavior.scala
+run/t2446.scala
+run/tuple-zipped.scala
+run/breakout.scala
+run/t4122.scala
+run/macro-settings
+run/t7157
+run/t1323.scala
+run/t4013b.scala
+run/t6309.scala
+run/t4047.scala
+run/t5544
+run/t978.scala
+run/t3361.scala
+run/t6611.scala
+run/t5387.scala
+run/t5656.scala
+run/t4897.scala
+run/numeric-range.scala
+run/t4777.scala
+run/Course-2002-03.scala
+run/string-extractor.scala
+run/view-headoption.scala
+run/patmat_unapp_abstype-new.scala
+run/stream-stack-overflow-filter-map.scala
+run/macro-impl-tparam-only-in-impl
+run/t6559.scala
+run/macro-reify-tagful-a
+run/macro-expand-multiple-arglists
+run/t4709.scala
+run/t3509.scala
+run/t5284b.scala
+run/t7617b
+run/t3923.scala
+run/virtpatmat_apply.scala
+run/t363.scala
+run/manifests-undeprecated-in-2.10.0.scala
+run/matchintasany.scala
+run/t3970.scala
+run/t4996.scala
+run/t5530.scala
+run/macro-term-declared-in-object-class
+run/t3242b.scala
+run/indexedSeq-apply.scala
+run/t107.scala
+run/t2337.scala
+run/t3758-old.scala
+run/t2754.scala
+run/valueclasses-manifest-existential.scala
+run/flat-flat-flat.scala
+run/t6673.scala
+run/interpolationMultiline2.scala
+run/t3493.scala
+run/t0631.scala
+run/t2800.scala
+run/t6506.scala
+run/t6260.scala
+run/t2418.scala
+run/t4415.scala
+run/classmanifests_new_alias.scala
+run/t5380.scala
+run/tcpoly_parseridioms.scala
+run/t1747.scala
+run/t5903d
+run/t3530.scala
+run/t216.scala
+run/macro-term-declared-in-refinement
+run/t4592.scala
+run/t2488.scala
+run/t3327.scala
+run/t5614.scala
+run/t5903b
+run/iterables.scala
+run/t3964.scala
+run/t6329_vanilla.scala
+run/t3038c
+run/t1697.scala
+run/t2030.scala
+run/t3397.scala
+run/t1005.scala
+run/t3353.scala
+run/t1466.scala
+run/t3186.scala
+run/tcpoly_overriding.scala
+run/t5394.scala
+run/t5284.scala
+run/unboxingBug.scala
+run/t7200.scala
+run/macro-reify-basic
+run/t153.scala
+run/iterator3444.scala
+run/macro-expand-implicit-macro-is-val
+run/macro-basic-ma-md-mi
+run/interpolationArgs.scala
+run/t4954.scala
+run/t3645.scala
+run/transpose.scala
+run/t3887.scala
+run/t4288.scala
+run/unittest_iterator.scala
+run/t5543.scala
+run/macro-term-declared-in-object
+run/iq.scala
+run/t2788.scala
+run/t2027.scala
+run/macro-expand-recursive
+run/t949.scala
+run/t1909b.scala
+run/delambdafy-nested-by-name.scala
+run/delambdafy-two-lambdas.scala
+run/macro-blackbox-materialization
+run/lists-run.scala
+run/macro-parse-position
+run/macro-parse-position-malformed
+run/macro-whitebox-dynamic-materialization
+run/macro-whitebox-extractor
+run/macro-vampire-false-warning
+run/macro-whitebox-fundep-materialization
+run/macro-whitebox-structural
+run/mutable-treeset.scala
+run/static-module-method.scala
+run/sort.scala
+run/t1909.scala
+run/t1909c.scala
+run/t3346a.scala
+run/t3346d.scala
+run/t3346f.scala
+run/t3346h.scala
+run/t3346g.scala
+run/t3832.scala
+run/t4742.scala
+run/t5377.scala
+run/t5923c.scala
+run/t6188.scala
+run/t6333.scala
+run/t6385.scala
+run/t7899.scala
+run/t7899-regression.scala
+run/t7584b.scala
+run/t7223.scala
+run/t7859
+run/t7868.scala
+run/t7871
+run/arrayclone-new.scala
+run/arrayclone-old.scala
+run/bitsets.scala
+run/comparable-comparator.scala
+run/colltest1.scala
+run/t2106.scala
+run/t5986.scala
+run/view-iterator-stream.scala
+run/array-charSeq.scala
+pos/signatures
+pos/t1263
+pos/t3249
+neg/t4749.scala
+neg/main1.scala
+neg/t7251
+neg/t7494-after-terminal
+neg/t7494-before-parser
+neg/t7494-right-after-terminal
+run/lazy-traits.scala
+run/OrderingTest.scala
+run/ReplacementMatching.scala
+run/patmat-finally.scala
+run/t3158.scala
+run/t3346e.scala
+run/t4398.scala
+run/t4930.scala
+run/t6534.scala
+pos/sammy_scope.scala
+pos/delambdafy-patterns.scala
+pos/private-types-after-typer.scala
+pos/delambdafy-lambdalift.scala
+pos/sammy_poly.scala
+pos/sammy_single.scala
+pos/SI-4012-b.scala
+pos/sammy_twice.scala
+pos/t3160.scala
+pos/t1014.scala
+pos/t4970b.scala
+pos/t2698.scala
+pos/t5845.scala
+pos/t6201.scala
+pos/t6260a.scala
+pos/t7688.scala
+pos/t7818.scala
+pos/t1203a.scala
+pos/t7834.scala
+pos/t7853.scala
+pos/t7815.scala
+pos/t7853-partial-function.scala
+pos/t7864.scala
+pos/t7928.scala
+pos/t7902.scala
+pos/t7944.scala
+pos/t7847
+neg/accesses2.scala
+neg/bad-advice.scala
+neg/gadts2.scala
+neg/gadts2-strict.scala
+neg/macro-bundle-abstract.scala
+neg/macro-bundle-object.scala
+neg/macro-bundle-trait.scala
+neg/macro-blackbox-dynamic-materialization
+neg/macro-blackbox-extractor
+neg/run-gadts-strict.scala
+neg/macro-blackbox-structural
+neg/sammy_restrictions.scala
+neg/sammy_wrong_arity.scala
+neg/t2462c.scala
+neg/t3346b.scala
+neg/t1909-object.scala
+neg/macro-blackbox-fundep-materialization
+neg/t3346c.scala
+neg/t3871.scala
+neg/t3871b.scala
+neg/t3971.scala
+neg/t3346i.scala
+neg/t6120.scala
+neg/t6260c.scala
+neg/t6680a.scala
+neg/t7239.scala
+neg/t7007.scala
+neg/t7605-deprecation.scala
+neg/t7622-missing-required.scala
+neg/t7629-view-bounds-deprecation.scala
+neg/t7834neg.scala
+neg/t7783.scala
+neg/t7848-interp-warn.scala
+neg/t7519-b
+neg/t7622-missing-dependency
+neg/t7870.scala
+neg/t7877.scala
+neg/t7895.scala
+neg/t7895b.scala
+neg/t7899.scala
+neg/t7895c.scala
+neg/t7859
+run/t4752.scala
+run/t2087-and-2400.scala
+run/t3855.scala
+run/t6637.scala
+run/t6731.scala
+pos/t3999b.scala
+run/t0432.scala
+run/t2514.scala
+run/t7817.scala
+run/t874.scala
+run/type-currying.scala
+run/t3616.scala
+run/t3687.scala
+run/t4570.scala
+run/t5612.scala
+run/t1110.scala
+run/t2636.scala
+run/verify-ctor.scala
+run/t3647.scala
+run/t4560.scala
+run/t6632.scala
+run/hashCodeBoxesRunTime.scala
+run/richs.scala
+run/t6725-1.scala
+pos/t7776.scala
+run/fors.scala
+run/t6706.scala
+run/t3175.scala
+run/delambdafy-dependent-on-param-subst.scala
+run/t4332b.scala
+run/t8048a
+run/t8017
+run/t7985b.scala
+run/t8100.scala
+run/patmat-mix-case-extractor.scala
+run/t4750.scala
+run/t7912.scala
+run/delambdafy-dependent-on-param-subst-2.scala
+run/t8048b
+run/t8091.scala
+run/macroPlugins-macroRuntime
+run/macro-default-params
+run/t6355.scala
+run/t7777
+run/t8002.scala
+run/t8015-ffc.scala
+run/macro-subpatterns
+run/t7985.scala
+run/macroPlugins-macroArgs
+run/t7326.scala
+run/t5045.scala
+run/value-class-partial-func-depmet.scala
+run/t6329_vanilla_bug.scala
+run/macroPlugins-macroExpand
+run/t8010.scala
+run/macroPlugins-typedMacroBody
+run/t7406.scala
+run/t6253c.scala
+run/t6253a.scala
+run/t6253b.scala
+pos/t8146a.scala
+pos/t8046c.scala
+pos/t8002-nested-scope.scala
+pos/t8132.scala
+pos/t8045.scala
+pos/overzealous-assert-genbcode.scala
+pos/t8128.scala
+pos/t8013
+pos/t8064b
+pos/t6780.scala
+pos/t7987
+pos/bcode_throw_null
+pos/t8064
+pos/t8046.scala
+pos/t6231.scala
+pos/t7983.scala
+pos/t5508.scala
+pos/t5508-min.scala
+pos/t8023b.scala
+pos/t6231b.scala
+pos/debug-reset-local-attrs.scala
+pos/t8054.scala
+pos/t2066.scala
+pos/dotless-targs.scala
+pos/t8120.scala
+pos/t5508-min-okay.scala
+pos/t8060.scala
+pos/t8001
+pos/t8138.scala
+pos/t8111.scala
+pos/t8062
+pos/t8011.scala
+pos/t8146b.scala
+pos/t8046b.scala
+pos/t8023.scala
+pos/t5508-min-okay2.scala
+pos/macro-implicit-invalidate-on-error.scala
+neg/t6563.scala
+neg/missing-param-type-tuple.scala
+neg/not-a-legal-formal-parameter-tuple.scala
+neg/t7897.scala
+neg/t8015-ffa.scala
+neg/quasiquotes-unliftable-not-found.scala
+neg/t2066b.scala
+neg/dotless-targs.scala
+neg/patmat-classtag-compound.scala
+neg/t2066.scala
+neg/t8035-deprecated.scala
+neg/t6675b.scala
+neg/t8104
+neg/t7872.scala
+neg/t7850.scala
+neg/t7967.scala
+neg/macro-bundle-overloaded.scala
+neg/t6355a.scala
+neg/class-of-double-targs.scala
+neg/t6355b.scala
+neg/macro-reify-splice-splice
+neg/macro-bundle-noncontext.scala
+neg/t8015-ffb.scala
+neg/t8035-removed.scala
+neg/t7984.scala
+neg/t8024.scala
+neg/t8024b.scala
+neg/t8157.scala
+neg/t8146-non-finitary-2.scala
+neg/t8006.scala
+neg/t7872c.scala
+neg/t8146-non-finitary.scala
+neg/t7872b.scala
+neg/t6920.scala
+run/t6200.scala
+run/t6196.scala
+run/macro-bundle-context-refinement
+run/macro-enclosingowner-detectvar
+run/macro-enclosingowner-sbt
+run/macro-bundle-context-alias
+run/macro-bundle-whitebox-use-refined
+run/macro-bundle-whitebox-use-raw
+neg/name-lookup-stable.scala
+neg/t0764b.scala
+neg/no-implicit-to-anyref-any-val.scala
+neg/t1503.scala
+neg/t4728.scala
+neg/t6455.scala
+neg/t6260-named.scala
+neg/t6844.scala
+neg/t7475c.scala
+neg/t7475e.scala
+neg/t7475f.scala
+neg/macro-bundle-whitebox-use-raw
+neg/macro-bundle-whitebox-use-refined
+neg/macro-incompatible-macro-engine-b
+neg/t7980.scala
+neg/macro-incompatible-macro-engine-a
+neg/t8143a.scala
+neg/t8072.scala
+neg/t8207.scala
+neg/t8182.scala
+neg/t8219-any-any-ref-equals.scala
+neg/t8177a.scala
+neg/t8228.scala
+neg/t8229.scala
+neg/t8237-default.scala
+neg/t8244b.scala
+neg/t8244e
+neg/t8244c.scala
+neg/t8265.scala
+neg/t8266-invalid-interp.scala
+neg/t6931
+neg/t8376
+neg/t8372.scala
+neg/t8300-overloading.scala
+neg/t8244
+neg/t8158
+neg/t8431.scala
+pos/implicit-anyval-2.10.scala
+pos/delambdafy_t6260_method.scala
+pos/macro-bundle-disambiguate-bundle.scala
+pos/macro-bundle-disambiguate-nonbundle.scala
+pos/package-ob-case
+pos/t1786-counter.scala
+pos/reflection-compat-api-universe.scala
+pos/list-optim-check.scala
+pos/existential-java-case-class
+pos/t1786-cycle.scala
+pos/reflection-compat-c.scala
+pos/t3452f.scala
+pos/reflection-compat-ru.scala
+pos/t2066-2.10-compat.scala
+pos/reflection-compat-macro-universe.scala
+pos/t5900a.scala
+pos/t5760-pkgobj-warn
+pos/t5954a
+pos/t5954b
+pos/t5954d
+pos/t6260.scala
+pos/t5165b
+pos/t5954c
+pos/t6260b.scala
+pos/t7475b.scala
+pos/t7475a.scala
+pos/t7753.scala
+pos/t7322.scala
+pos/t6948.scala
+pos/t7475d.scala
+pos/t7475e.scala
+pos/t6169
+pos/t7788.scala
+pos/t7919.scala
+pos/t8177a.scala
+pos/t8177.scala
+pos/t8170.scala
+pos/t8170b.scala
+pos/t8177d.scala
+pos/t8177b.scala
+pos/t8177e.scala
+pos/t8134
+pos/t8177h.scala
+pos/t8177g.scala
+pos/t8207.scala
+pos/t8187.scala
+pos/t8219.scala
+pos/t8219b.scala
+pos/t8224.scala
+pos/t8237.scala
+pos/t8223.scala
+pos/t8237b.scala
+pos/t8300-conversions-a.scala
+pos/t8300-conversions-b.scala
+pos/t8209a
+pos/t8209b
+pos/t8244d
+pos/t8300-overloading.scala
+pos/t8300-patmat-a.scala
+pos/t8300-patmat-b.scala
+pos/t8315b.scala
+pos/t8306.scala
+pos/t8301.scala
+pos/t8324.scala
+pos/t8315.scala
+pos/t8301b.scala
+pos/t8363.scala
+pos/t8367.scala
+pos/t8369a.scala
+pos/t8369b.scala
+pos/t8403.scala
+pos/t8364.scala
+pos/t8352
+pos/t8376
+neg/macro-bundle-nonpublic-c.scala
+neg/literate_existentials.scala
+neg/macro-bundle-nonpublic-impl.scala
+neg/macro-bundle-ambiguous.scala
+neg/macro-bundle-priority-bundle.scala
+neg/macro-bundle-need-qualifier.scala
+neg/macro-bundle-nonstatic.scala
+neg/macro-bundle-polymorphic.scala
+neg/macro-bundle-priority-nonbundle.scala
+neg/macro-bundle-wrongcontext-a.scala
+neg/macro-bundle-wrongcontext-b.scala
+run/t8425
+run/t8245.scala
+run/t8266-octal-interp.scala
+run/t8280.scala
+run/t8395.scala
+run/t8321
+run/t8153.scala
+run/t8233-bcode.scala
+run/t8197.scala
+run/t8197b.scala
+run/t8233.scala
+run/t8133
+run/t8133b
+run/t7475b.scala
+run/t7445.scala
+run/t6814
+run/t4577.scala
+run/t5134.scala
+run/t3452f.scala
+run/t3452h.scala
+run/t3452c.scala
+run/t3452.scala
+run/t261.scala
+run/t3235-minimal.scala
+run/t1503_future.scala
+run/t5565.scala
+pos/t8411
+pos/t8460.scala
+run/t8428.scala
+run/t8437
+run/absoverride.scala
+run/arrays.scala
+run/duration-coarsest.scala
+run/iterator-from.scala
+run/SymbolsTest.scala
+run/t1074.scala
+run/t1505.scala
+run/streams.scala
+run/t2111.scala
+run/t4601.scala
+pos/SI-4012-a.scala
+pos/SI-7638.scala
+neg/t3692-new.scala
+run/t7015.scala
+run/finally.scala
+run/bugs.scala
+run/t1503.scala
+run/t4148.scala
+run/t7763.scala
+run/issue192.scala
+
+# Adapt checkfiles for (1.0).toString == "1"
+run/Course-2002-01.scala
+run/t0421-new.scala
+run/runtime.scala
+run/t0421-old.scala
+run/spec-self.scala
+run/t5552.scala
+run/Course-2002-02.scala
+run/Course-2002-04.scala
+run/promotion.scala
+run/t4617.scala
+run/Course-2002-09.scala
+run/t5866.scala
+run/try-catch-unify.scala
+run/impconvtimes.scala
+run/Course-2002-10.scala
+run/Course-2002-08.scala
+run/t6318_primitives.scala
+
+# Adapt checkfiles for ().toString == "undefined"
+run/t5680.scala
+run/dynamic-anyval.scala
+run/macro-bundle-toplevel
+run/macro-bundle-whitebox-decl
+run/t6662
+run/t3702.scala
+run/t7657
+run/macro-bundle-static
+run/structural.scala
+
+# Adapt checkfiles for print & flush (which we cannot 100% emulate)
+run/imports.scala
+run/misc.scala
+
+# Adapt checkfiles for compiler phase list
+run/t6102.scala
+neg/t7494-no-options
+
+# Adapt checkfiles for different behavior with boxed types
+run/t5568.scala
+run/virtpatmat_typetag.scala
+run/virtpatmat_switch.scala
+run/t5629b.scala
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/neg/t7494-no-options.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/neg/t7494-no-options.check
new file mode 100644
index 0000000..581da38
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/neg/t7494-no-options.check
@@ -0,0 +1,42 @@
+error: Error: ploogin takes no options
+ phase name id description
+ ---------- -- -----------
+ parser 1 parse source into ASTs, perform simple desugaring
+ namer 2 resolve names, attach symbols to named trees
+packageobjects 3 load package objects
+ typer 4 the meat and potatoes: type the trees
+ jsinterop 5
+ patmat 6 translate match expressions
+superaccessors 7 add super accessors in traits and nested classes
+ extmethods 8 add extension methods for inline classes
+ pickler 9 serialize symbol tables
+ refchecks 10 reference/override checking, translate nested objects
+ uncurry 11 uncurry, translate function values to anonymous classes
+ tailcalls 12 replace tail calls by jumps
+ specialize 13 @specialized-driven class and method specialization
+ explicitouter 14 this refs to outer pointers
+ erasure 15 erase types, add interfaces for traits
+ posterasure 16 clean up erased inline classes
+ lazyvals 17 allocate bitmaps, translate lazy vals into lazified defs
+ lambdalift 18 move nested functions to top level
+ constructors 19 move field definitions into constructors
+ flatten 20 eliminate inner classes
+ mixin 21 mixin composition
+ jscode 22
+ cleanup 23 platform-specific cleanups, generate reflective calls
+ delambdafy 24 remove lambdas
+ icode 25 generate portable intermediate code
+#partest !-optimise
+ jvm 26 generate JVM bytecode
+ ploogin 27 A sample phase that does so many things it's kind of hard...
+ terminal 28 the last phase during a compilation run
+#partest -optimise
+ inliner 26 optimization: do inlining
+inlinehandlers 27 optimization: inline exception handlers
+ closelim 28 optimization: eliminate uncalled closures
+ constopt 29 optimization: optimize null and other constants
+ dce 30 optimization: eliminate dead code
+ jvm 31 generate JVM bytecode
+ ploogin 32 A sample phase that does so many things it's kind of hard...
+ terminal 33 the last phase during a compilation run
+#partest
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-01.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-01.check
new file mode 100644
index 0000000..fcda943
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-01.check
@@ -0,0 +1,37 @@
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-02.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-02.check
new file mode 100644
index 0000000..ab75cfd
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-02.check
@@ -0,0 +1,187 @@
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-04.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-04.check
new file mode 100644
index 0000000..fc6ad96
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-04.check
@@ -0,0 +1,64 @@
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-08.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-08.check
new file mode 100644
index 0000000..0585d5b
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-08.check
@@ -0,0 +1,171 @@
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-09.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-09.check
new file mode 100644
index 0000000..c921361
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-09.check
@@ -0,0 +1,50 @@
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-10.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-10.check
new file mode 100644
index 0000000..847f0fa
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-10.check
@@ -0,0 +1,46 @@
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/bugs.sem b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/bugs.sem
new file mode 100644
index 0000000..d36898b
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/bugs.sem
@@ -0,0 +1 @@
+asInstanceOfs
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/dynamic-anyval.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/dynamic-anyval.check
new file mode 100644
index 0000000..c125372
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/dynamic-anyval.check
@@ -0,0 +1,4 @@
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/impconvtimes.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/impconvtimes.check
new file mode 100644
index 0000000..082377e
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/impconvtimes.check
@@ -0,0 +1 @@
+3.0 * Hour = Measure(3,Hour)
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/imports.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/imports.check
new file mode 100644
index 0000000..1aad598
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/imports.check
@@ -0,0 +1,21 @@
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/issue192.sem b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/issue192.sem
new file mode 100644
index 0000000..10abbf7
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/issue192.sem
@@ -0,0 +1 @@
+strictFloats
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/macro-bundle-static.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/macro-bundle-static.check
new file mode 100644
index 0000000..e2e7628
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/macro-bundle-static.check
@@ -0,0 +1,6 @@
+undefined
+Int
+undefined
+true
+IntInt
+true
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/macro-bundle-toplevel.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/macro-bundle-toplevel.check
new file mode 100644
index 0000000..e2e7628
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/macro-bundle-toplevel.check
@@ -0,0 +1,6 @@
+undefined
+Int
+undefined
+true
+IntInt
+true
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/macro-bundle-whitebox-decl.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/macro-bundle-whitebox-decl.check
new file mode 100644
index 0000000..e2e7628
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/macro-bundle-whitebox-decl.check
@@ -0,0 +1,6 @@
+undefined
+Int
+undefined
+true
+IntInt
+true
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/misc.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/misc.check
new file mode 100644
index 0000000..6043817
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/misc.check
@@ -0,0 +1,62 @@
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/promotion.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/promotion.check
new file mode 100644
index 0000000..41e36c3
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/promotion.check
@@ -0,0 +1,4 @@
+2
+6
+20
+30
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/runtime.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/runtime.check
new file mode 100644
index 0000000..0450b94
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/runtime.check
@@ -0,0 +1,70 @@
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/spec-self.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/spec-self.check
new file mode 100644
index 0000000..fd3c81a
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/spec-self.check
@@ -0,0 +1,2 @@
+5
+5
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/structural.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/structural.check
new file mode 100644
index 0000000..2fec112
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/structural.check
@@ -0,0 +1,37 @@
+ 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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t0421-new.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t0421-new.check
new file mode 100644
index 0000000..00d29b7
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t0421-new.check
@@ -0,0 +1,3 @@
+[Array(0, 1),Array(2, 3),Array(4, 5)]
+[Array(31)]
+[Array(24, 32)]
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t0421-old.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t0421-old.check
new file mode 100644
index 0000000..00d29b7
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t0421-old.check
@@ -0,0 +1,3 @@
+[Array(0, 1),Array(2, 3),Array(4, 5)]
+[Array(31)]
+[Array(24, 32)]
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t1503.sem b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t1503.sem
new file mode 100644
index 0000000..d36898b
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t1503.sem
@@ -0,0 +1 @@
+asInstanceOfs
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t3702.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t3702.check
new file mode 100644
index 0000000..3fce987
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t3702.check
@@ -0,0 +1,2 @@
+undefined
+6
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t4148.sem b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t4148.sem
new file mode 100644
index 0000000..d36898b
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t4148.sem
@@ -0,0 +1 @@
+asInstanceOfs
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t4617.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t4617.check
new file mode 100644
index 0000000..a6790f1
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t4617.check
@@ -0,0 +1 @@
+Str 8
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t5552.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t5552.check
new file mode 100644
index 0000000..4704611
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t5552.check
@@ -0,0 +1,2 @@
+(3,3)
+(3,3)
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t5568.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t5568.check
new file mode 100644
index 0000000..6f30cc5
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t5568.check
@@ -0,0 +1,9 @@
+void
+int
+class scala.runtime.BoxedUnit
+class scala.runtime.BoxedUnit
+class java.lang.Byte
+class java.lang.Byte
+5
+5
+5
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t5629b.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t5629b.check
new file mode 100644
index 0000000..c65298a
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t5629b.check
@@ -0,0 +1,10 @@
+=== 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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t5680.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t5680.check
new file mode 100644
index 0000000..a3b8b64
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t5680.check
@@ -0,0 +1,3 @@
+[Lscala.runtime.BoxedUnit
+undefined
+undefined
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t5866.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t5866.check
new file mode 100644
index 0000000..64df1ce
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t5866.check
@@ -0,0 +1,2 @@
+0
+Foo(0)
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t6102.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t6102.check
new file mode 100644
index 0000000..120082e
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t6102.check
@@ -0,0 +1,27 @@
+[running phase parser 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 uncurry on t6102.scala]
+[running phase tailcalls on t6102.scala]
+[running phase specialize 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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t6318_primitives.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t6318_primitives.check
new file mode 100644
index 0000000..08decef
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t6318_primitives.check
@@ -0,0 +1,36 @@
+true
+Some(1)
+false
+None
+true
+Some(1)
+false
+None
+true
+Some()
+false
+None
+true
+Some(1)
+false
+None
+true
+Some(1)
+false
+None
+true
+Some(1)
+false
+None
+true
+Some(1)
+false
+None
+true
+Some(true)
+false
+None
+true
+Some(undefined)
+false
+None
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t6662.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t6662.check
new file mode 100644
index 0000000..417b7b5
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t6662.check
@@ -0,0 +1 @@
+undefined
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t7657.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t7657.check
new file mode 100644
index 0000000..1a87c1e
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t7657.check
@@ -0,0 +1,3 @@
+undefined
+undefined
+undefined
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t7763.sem b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t7763.sem
new file mode 100644
index 0000000..d36898b
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t7763.sem
@@ -0,0 +1 @@
+asInstanceOfs
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/try-catch-unify.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/try-catch-unify.check
new file mode 100644
index 0000000..813f011
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/try-catch-unify.check
@@ -0,0 +1,4 @@
+Failure(java.lang.NumberFormatException: For input string: "Hi")
+Success(5)
+O NOES
+Failure(java.lang.NumberFormatException: For input string: "Hi")
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/virtpatmat_switch.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/virtpatmat_switch.check
new file mode 100644
index 0000000..0900a9c
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/virtpatmat_switch.check
@@ -0,0 +1,7 @@
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/virtpatmat_typetag.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/virtpatmat_typetag.check
new file mode 100644
index 0000000..e95c3d0
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/virtpatmat_typetag.check
@@ -0,0 +1,10 @@
+1 is not a Int; it's a class java.lang.Byte
+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 not a Int; it's a class java.lang.Byte
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/BlacklistedTests.txt b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/BlacklistedTests.txt
new file mode 100644
index 0000000..18a1c5d
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/BlacklistedTests.txt
@@ -0,0 +1,899 @@
+#
+# POS
+#
+
+# Using Jsoup, what's that?
+pos/cycle-jsoup.scala
+
+# Using scala.actors
+pos/t533.scala
+pos/functions.scala
+pos/MailBox.scala
+
+#
+# NEG
+#
+
+# Using the compiler API
+
+neg/t6446-additional
+neg/t6446-list
+neg/t6446-missing
+neg/t6446-show-phases.scala
+
+# 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
+
+# Uses .java files
+neg/t6289
+
+#
+# RUN
+#
+
+# Relies on the exact toString() representation of Floats/Doubles
+run/t2378.scala
+
+# Relies on !box(+0.0).equals(box(-0.0))
+run/number-parsing.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/t0412.scala
+run/t3518.scala
+run/t6198.scala
+run/t5018.scala
+run/t2417.scala
+run/t6197.scala
+run/t4813.scala
+run/lazy-concurrent.scala
+run/t5880.scala
+run/mapConserve.scala
+run/t3667.scala
+run/bigDecimalTest.scala
+run/t3038d.scala
+run/numbereq.scala
+run/shutdownhooks.scala
+run/t4658.scala
+run/t4201.scala
+run/t5590.scala
+run/deeps.scala
+run/t3895b.scala
+run/t2813.2.scala
+run/t5974.scala
+run/hashset.scala
+run/t5262.scala
+run/t5293.scala
+run/t5293-map.scala
+run/serialize-stream.scala
+run/sysprops.scala
+
+run/colltest.scala
+run/equality.scala
+run/t2849.scala
+run/t1360.scala
+run/t6114.scala
+run/t7269.scala
+run/t3199b.scala
+
+# Uses java.util.Collections
+run/java-erasure.scala
+run/t2250.scala
+
+# Uses java.math.BigDecimal / BigInteger
+run/bigDecimalCache.scala
+run/hashhash.scala
+run/is-valid-num.scala
+run/range.scala
+run/stringinterpolation_macro-run.scala
+run/t5356.scala
+run/t6064.scala
+
+# Documented semantic difference on numbers (float precision)
+run/interpolation.scala
+run/interpolationMultiline1.scala
+
+# Documented semantic difference on String.split(x: Array[Char])
+run/t0325.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
+
+# 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
+
+# Crashes Rhino
+
+run/bridges.scala
+run/patmat-exprs.scala
+
+# Using partest properties
+
+run/tailcalls.scala
+run/t4294.scala
+run/t6331b.scala
+
+# Using IO
+
+run/Predef.readLine.scala
+run/t6488.scala
+run/t6988.scala
+
+# Object{Output|Input}Streams
+run/t6935.scala
+run/t8188.scala
+
+# Using System.getProperties
+
+run/t4426.scala
+
+# Using ExecutionContext.global
+run/t7336.scala
+run/t7775.scala
+run/future-flatmap-exec-count.scala
+
+# Using reflection
+
+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/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/reflection-mem-typecheck.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/t6220.scala
+run/t6240-universe-code-gen.scala
+run/t6221
+run/t6260b.scala
+run/t6259.scala
+run/t6287.scala
+run/t6261.scala
+run/t6308.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_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/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
+
+# 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
+
+# Expecting some particular value of hashCode()
+
+run/MeterCaseClass.scala
+run/t5608.scala
+run/caseClassHash.scala
+run/Meter.scala
+
+# Exceptions that become JavaScriptException
+
+run/pf-catch.scala
+run/exceptions-2.scala
+run/exceptions-nest.scala
+
+# Expecting unsupported exceptions (e.g. ArrayIndexOutOfBounds)
+run/optimizer-array-load.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/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
+
+# Using Scala Script (partest.ScriptTest)
+
+run/t7711-script-args.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
+run/t7933.scala
+run/t7843-jsr223-service.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
+
+# partest.BytecodeTest
+run/t6546
+run/t7106
+run/t7974
+
+# 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
+
+# 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
+
+### Incorrect partests ###
+# Badly uses constract of Console.print (no flush)
+run/t429.scala
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/BuglistedTests.txt b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/BuglistedTests.txt
new file mode 100644
index 0000000..42c6146
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/BuglistedTests.txt
@@ -0,0 +1,4 @@
+# The tests in this file should pass but have never passed so far
+# use scala.tools.partest.scalajs.testunknownonly to only run tests
+# which are neither in BuglistedTests.txt, WhitelistedTests.txt or
+# BlacklistedTests.txt
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/NoDCEWarn.txt b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/NoDCEWarn.txt
new file mode 100644
index 0000000..cc5aff0
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/NoDCEWarn.txt
@@ -0,0 +1,8 @@
+Ljava_math_MathContext$
+Ljava_math_BigDecimal$
+Ljava_math_BigDecimal
+Ljava_math_BigInteger$
+jl_Class$
+jl_Class.getClassLoader__jl_ClassLoader
+jl_Class.getPackage__jl_Package
+jl_Class.getInterfaces__Ajl_Class
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/WhitelistedTests.txt b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/WhitelistedTests.txt
new file mode 100644
index 0000000..1483b1e
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/WhitelistedTests.txt
@@ -0,0 +1,2949 @@
+pos/spec-super.scala
+pos/t1035.scala
+pos/t5897.scala
+pos/irrefutable.scala
+pos/spec-partialmap.scala
+pos/tcpoly_seq.scala
+pos/partialfun.scala
+pos/t2795-new.scala
+pos/clsrefine.scala
+pos/t0774
+pos/t1070.scala
+pos/t5957
+pos/looping-jsig.scala
+pos/t3274.scala
+pos/spec-fields-old.scala
+pos/t262.scala
+pos/t7486.scala
+pos/t2261.scala
+pos/t6600.scala
+pos/t4786.scala
+pos/t5406.scala
+pos/tcpoly_late_method_params.scala
+pos/t2726
+pos/pos-bug1210.scala
+pos/t3312.scala
+pos/manifest1-old.scala
+pos/gadt-gilles.scala
+pos/t4842.scala
+pos/ted.scala
+pos/NoCyclicReference.scala
+pos/t3568.scala
+pos/t0030.scala
+pos/t2635.scala
+pos/t7232b
+pos/t0017.scala
+pos/t812.scala
+pos/t2179.scala
+pos/t651.scala
+pos/spurious-overload.scala
+pos/t758.scala
+pos/t4760.scala
+pos/t1672.scala
+pos/mixins.scala
+pos/patterns.scala
+pos/t1260.scala
+pos/t6551.scala
+pos/t2060.scala
+pos/t6575a.scala
+pos/t1318.scala
+pos/t4266.scala
+pos/t0695
+pos/protected-static
+pos/t5738.scala
+pos/t1226.scala
+pos/t5013
+pos/t6215.scala
+pos/t5692b
+pos/traits.scala
+pos/t2994a.scala
+pos/t3371.scala
+pos/t613.scala
+pos/t6499.scala
+pos/xlint1.scala
+pos/t1150
+pos/sealed-final.scala
+pos/test4a.scala
+pos/t2664.scala
+pos/t3528.scala
+pos/t3174.scala
+pos/t6994.scala
+pos/t4812.scala
+pos/t5777.scala
+pos/t5223.scala
+pos/t439.scala
+pos/t3079.scala
+pos/t5829.scala
+pos/t0036.scala
+pos/scoping2.scala
+pos/t4717.scala
+pos/t4257.scala
+pos/t1210a.scala
+pos/getClassType.scala
+pos/t5330.scala
+pos/t4524.scala
+pos/t2945.scala
+pos/t6562.scala
+pos/t0273.scala
+pos/override-object-yes.scala
+pos/t7426.scala
+pos/t6601
+pos/t3076
+pos/seq-ordering.scala
+pos/spec-groups.scala
+pos/t296.scala
+pos/t5545
+pos/spec-multiplectors.scala
+pos/t1789.scala
+pos/t2569
+pos/ksbug1.scala
+pos/t0599.scala
+pos/local-objects.scala
+pos/t0081.scala
+pos/t5756.scala
+pos/t7126.scala
+pos/t7716.scala
+pos/t2797.scala
+pos/t5399.scala
+pos/t1101
+pos/t767.scala
+pos/contrib467.scala
+pos/t7532b
+pos/self-type-override.scala
+pos/t4853.scala
+pos/t839.scala
+pos/t5644
+pos/t5853.scala
+pos/t5178.scala
+pos/unapplyNeedsMemberType.scala
+pos/t5390.scala
+pos/t6575b.scala
+pos/t151.scala
+pos/t2665.scala
+pos/t5120.scala
+pos/erasure-nsquared.scala
+pos/arrays3.scala
+pos/t3136.scala
+pos/inline-access-levels
+pos/t3972.scala
+pos/t2591.scala
+pos/t3486
+pos/variances-flip.scala
+pos/annotated-original
+pos/typesafecons.scala
+pos/stable.scala
+pos/t1996.scala
+pos/t3037.scala
+pos/t1711
+pos/t3374.scala
+pos/t0029.scala
+pos/t3278.scala
+pos/matthias3.scala
+pos/t5546.scala
+pos/t4020.scala
+pos/matthias4.scala
+pos/value-class-override-spec.scala
+pos/arrays2.scala
+pos/t5119.scala
+pos/t2613.scala
+pos/t4070b.scala
+pos/virtpatmat_exist_uncurry.scala
+pos/modules1.scala
+pos/spec-constr-new.scala
+pos/t6335.scala
+pos/t675.scala
+pos/t0644.scala
+pos/t5892.scala
+pos/t360.scala
+pos/override.scala
+pos/t1798.scala
+pos/strip-tvars-for-lubbasetypes.scala
+pos/hk-infer.scala
+pos/t2119.scala
+pos/t0231.scala
+pos/t1459
+pos/t1381-new.scala
+pos/t2610.scala
+pos/t2708.scala
+pos/t5604b
+pos/t3951
+pos/t361.scala
+pos/t319.scala
+pos/largecasetest.scala
+pos/switchUnbox.scala
+pos/typetags.scala
+pos/java-access-pos
+pos/t803.scala
+pos/t3898.scala
+pos/t5692a
+pos/t2421.scala
+pos/t1102
+pos/t0654.scala
+pos/exhaust_alternatives.scala
+pos/t807.scala
+pos/t5702-pos-infix-star.scala
+pos/t1186
+pos/t1439.scala
+pos/t7427.scala
+pos/virtpatmat_binding_opt.scala
+pos/t247.scala
+pos/abstract.scala
+pos/gen-traversable-methods.scala
+pos/t2795-old.scala
+pos/t5639
+pos/t2667.scala
+pos/t2405.scala
+pos/t1438.scala
+pos/SI-7100.scala
+pos/t1659.scala
+pos/unchecked-a.scala
+pos/t3636.scala
+pos/t6745.scala
+pos/t2809.scala
+pos/t7022.scala
+pos/t6447.scala
+pos/t6367.scala
+pos/t5846.scala
+pos/lubs.scala
+pos/t1987a.scala
+pos/spec-arrays.scala
+pos/virtpatmat_anonfun_for.scala
+pos/listpattern.scala
+pos/t5742.scala
+pos/test5refine.scala
+pos/switch-small.scala
+pos/t5604
+pos/return_thistype.scala
+pos/t348plus.scala
+pos/t3420.scala
+pos/t3440.scala
+pos/maxim1.scala
+pos/caseClassInMethod.scala
+pos/t7239.scala
+pos/t3833.scala
+pos/t6675.scala
+pos/t4402
+pos/t5953.scala
+pos/t1152
+pos/t0591.scala
+pos/t210.scala
+pos/t7035.scala
+pos/t5769.scala
+pos/pmbug.scala
+pos/t2331.scala
+pos/t5240.scala
+pos/t304.scala
+pos/annotated-treecopy
+pos/t2081.scala
+pos/t0904.scala
+pos/t7649.scala
+pos/t3498-new.scala
+pos/contrib701.scala
+pos/t6624.scala
+pos/t3924.scala
+pos/t374.scala
+pos/t1642
+pos/t1591_pos.scala
+pos/depmet_implicit_oopsla_session_2.scala
+pos/t5899.scala
+pos/thistype.scala
+pos/t4176b.scala
+pos/elidable-tparams.scala
+pos/lambdalift.scala
+pos/nothing_manifest_disambig-old.scala
+pos/t372.scala
+pos/t5399a.scala
+pos/t2782.scala
+pos/patmat-extract-tparam.scala
+pos/t4114.scala
+pos/unapplyVal.scala
+pos/t2486.scala
+pos/t5877b.scala
+pos/t0625.scala
+pos/t6358_2.scala
+pos/viewtest1.scala
+pos/t1237.scala
+pos/scala-singleton.scala
+pos/t1254
+pos/t5504
+pos/bounds.scala
+pos/t3631.scala
+pos/t3177.scala
+pos/unapplyContexts2.scala
+pos/t0438.scala
+pos/t1642b.scala
+pos/inferbroadtype.scala
+pos/t1858.scala
+pos/t3731.scala
+pos/t6963c.scala
+pos/classtag-pos.scala
+pos/t6221.scala
+pos/t3343.scala
+pos/spec-asseenfrom.scala
+pos/t604.scala
+pos/spec-example1.scala
+pos/t0786.scala
+pos/annot-inner.scala
+pos/t5886.scala
+pos/t1056.scala
+pos/t294
+pos/spec-Function1.scala
+pos/t1836
+pos/spec-private.scala
+pos/depmet_implicit_tpbetareduce.scala
+pos/exhaust_2.scala
+pos/t7532
+pos/t5175.scala
+pos/t802.scala
+pos/t5809.scala
+pos/tcpoly_typesub.scala
+pos/t6029.scala
+pos/contextbounds-implicits-new.scala
+pos/t3480.scala
+pos/patterns3.scala
+pos/caseaccs.scala
+pos/spec-sparsearray-old.scala
+pos/patterns1213.scala
+pos/spec-traits.scala
+pos/t0020.scala
+pos/cycle
+pos/t5968.scala
+pos/typealiases.scala
+pos/init.scala
+pos/t697.scala
+pos/t2693.scala
+pos/t2377
+pos/unapplyGeneric.scala
+pos/t1385.scala
+pos/t3363-old.scala
+pos/t1236.scala
+pos/t0068.scala
+pos/t4052.scala
+pos/lambdalift1.scala
+pos/z1730.scala
+pos/variances-local.scala
+pos/virtpatmat_gadt_array.scala
+pos/t2421_delitedsl.scala
+pos/t5626.scala
+pos/t690.scala
+pos/t711.scala
+pos/t6547.scala
+pos/t1937
+pos/t3999
+pos/SI-7060.scala
+pos/t2305.scala
+pos/t2168.scala
+pos/t2660.scala
+pos/t1693.scala
+pos/inliner2.scala
+pos/t2799.scala
+pos/t6966.scala
+pos/t1001.scala
+pos/S5.scala
+pos/t0301.scala
+pos/t1048.scala
+pos/t415.scala
+pos/t6386.scala
+pos/t2187.scala
+pos/hashhash-overloads.scala
+pos/t6921.scala
+pos/t0227.scala
+pos/t6556.scala
+pos/t3946
+pos/t1053.scala
+pos/t1000.scala
+pos/t0586.scala
+pos/t7011.scala
+pos/t7329.scala
+pos/t4975.scala
+pos/t1131.scala
+pos/t1027.scala
+pos/t2913.scala
+pos/t3494.scala
+pos/t5606.scala
+pos/t4716.scala
+pos/tcpoly_gm.scala
+pos/t4859.scala
+pos/t514.scala
+pos/lexical.scala
+pos/t2624.scala
+pos/t4036.scala
+pos/t2741
+pos/t703.scala
+pos/five-dot-f.scala
+pos/t805.scala
+pos/strings.scala
+pos/t2433
+pos/t6925.scala
+pos/t1085.scala
+pos/t7461
+pos/t1942
+pos/spec-lists.scala
+pos/t3349
+pos/tcpoly_infer_ticket474.scala
+pos/t1614
+pos/virtpatmat_reach_const.scala
+pos/t2194.scala
+pos/t6976
+pos/t1560.scala
+pos/t6891.scala
+pos/t3883.scala
+pos/infersingle.scala
+pos/gui.scala
+pos/t1164.scala
+pos/t3175-pos.scala
+pos/t4336.scala
+pos/annotations2.scala
+pos/proj-rec-test.scala
+pos/t2973.scala
+pos/t1123.scala
+pos/t6205.scala
+pos/t5727.scala
+pos/t6537.scala
+pos/t6712.scala
+pos/t3866.scala
+pos/t4831.scala
+pos/selftails.scala
+pos/t397.scala
+pos/spec-vector.scala
+pos/t7233b.scala
+pos/t1391.scala
+pos/spec.scala
+pos/t3106.scala
+pos/contextbounds-implicits-old.scala
+pos/packageobjs.scala
+pos/michel3.scala
+pos/t628.scala
+pos/collections.scala
+pos/tcpoly_boundedmonad.scala
+pos/t7668.scala
+pos/t0032.scala
+pos/t0069.scala
+pos/t4345.scala
+pos/t3521
+pos/t3071.scala
+pos/tcpoly_infer_easy.scala
+pos/t289.scala
+pos/t4365
+pos/rangepos-anonapply.scala
+pos/t5033.scala
+pos/lambda.scala
+pos/S8.scala
+pos/t6014.scala
+pos/t1785.scala
+pos/t6034.scala
+pos/t7433.scala
+pos/imp2-pos.scala
+pos/t0504.scala
+pos/t1272.scala
+pos/t0612
+pos/value-class-override-no-spec.scala
+pos/overloaded-unapply.scala
+pos/t5859.scala
+pos/chang
+pos/localmodules.scala
+pos/t4237.scala
+pos/rangepos-patmat.scala
+pos/t1974.scala
+pos/t0054.scala
+pos/michel2.scala
+pos/t0770.scala
+pos/t1146.scala
+pos/t2441pos.scala
+pos/t5099.scala
+pos/tcpoly_seq_typealias.scala
+pos/t946.scala
+pos/tcpoly_infer_ticket1864.scala
+pos/t4579.scala
+pos/t4737
+pos/t7377b.scala
+pos/t616.scala
+pos/t201.scala
+pos/t6355pos.scala
+pos/escapes2.scala
+pos/t1675.scala
+pos/t3890.scala
+pos/t6040.scala
+pos/spec-tailcall.scala
+pos/existentials.scala
+pos/t5317.scala
+pos/t7782b.scala
+pos/t4758.scala
+pos/t7296.scala
+pos/t6896.scala
+pos/cls1.scala
+pos/t402.scala
+pos/gosh.scala
+pos/t2619.scala
+pos/javaConversions-2.10-regression.scala
+pos/t759.scala
+pos/t5259.scala
+pos/t5130.scala
+pos/t5156.scala
+pos/t0905.scala
+pos/package-implicit
+pos/t2669.scala
+pos/trait-parents.scala
+pos/virtpatmat_exhaust.scala
+pos/patterns1.scala
+pos/t7014
+pos/t1231
+pos/t1751
+pos/t7233.scala
+pos/t6022.scala
+pos/tcpoly_checkkinds_mix.scala
+pos/depmet_implicit_norm_ret.scala
+pos/package-case.scala
+pos/philippe4.scala
+pos/michel6.scala
+pos/t4188.scala
+pos/t3936
+pos/t1280.scala
+pos/t6722.scala
+pos/t796.scala
+pos/t5542.scala
+pos/t3927.scala
+pos/t2293.scala
+pos/t3800.scala
+pos/t7285a.scala
+pos/t927.scala
+pos/t4494.scala
+pos/t3864
+pos/ilya2
+pos/t2940
+pos/S1.scala
+pos/tcpoly_wildcards.scala
+pos/tryexpr.scala
+pos/t6089b.scala
+pos/depmet_implicit_oopsla_zipwith.scala
+pos/t245.scala
+pos/t6146.scala
+pos/t1782
+pos/t851.scala
+pos/spec-thistype.scala
+pos/tcpoly_poly.scala
+pos/t6815_import.scala
+pos/t4649.scala
+pos/t0453.scala
+pos/t5020.scala
+pos/ilya
+pos/t2435.scala
+pos/t1279a.scala
+pos/t2171.scala
+pos/t1957.scala
+pos/gadts2.scala
+pos/t3567
+pos/Z.scala
+pos/t1203b
+pos/nested2.scala
+pos/t1896
+pos/viewtest2.scala
+pos/t5541.scala
+pos/existentials-harmful.scala
+pos/t4063.scala
+pos/t6485a
+pos/t1208.scala
+pos/t5041.scala
+pos/unapplyComplex.scala
+pos/t3384.scala
+pos/t4112.scala
+pos/t788.scala
+pos/hklub0.scala
+pos/t757.scala
+pos/t1197
+pos/t359.scala
+pos/t5667.scala
+pos/t1107a.scala
+pos/virtpatmat_castbinder.scala
+pos/t267.scala
+pos/t3419
+pos/t3861.scala
+pos/t6797.scala
+pos/spec-localdefs.scala
+pos/t3404
+pos/t4457_1.scala
+pos/matthias5.scala
+pos/spec-polymeth.scala
+pos/kinds.scala
+pos/t2310.scala
+pos/t6552.scala
+pos/valdefs.scala
+pos/hkarray.scala
+pos/homonym.scala
+pos/t1235
+pos/t3429
+pos/t0053.scala
+pos/depmet_implicit_chaining_zw.scala
+pos/virtpatmat_partialfun_nsdnho.scala
+pos/t6664.scala
+pos/ticket2251.scala
+pos/t3495.scala
+pos/super
+pos/t121.scala
+pos/javaConversions-2.10-ambiguity.scala
+pos/t1803.scala
+pos/t5877.scala
+pos/t0085.scala
+pos/t3582.scala
+pos/t2939.scala
+pos/t1422_pos.scala
+pos/manifest1-new.scala
+pos/t7505.scala
+pos/t5720-ownerous.scala
+pos/misc-unapply_pos.scala
+pos/tcpoly_variance_pos.scala
+pos/t5127.scala
+pos/t6123-explaintypes-implicits.scala
+pos/t2764
+pos/presuperContext.scala
+pos/spec-simple.scala
+pos/t3120
+pos/t5729.scala
+pos/tcpoly_infer_ticket716.scala
+pos/tcpoly_bounds1.scala
+pos/t7369.scala
+pos/imports-pos.scala
+pos/t5654.scala
+pos/t0123.scala
+pos/raw-map
+pos/t5330b.scala
+pos/t6485b
+pos/t6072.scala
+pos/t5692c.scala
+pos/t3430.scala
+pos/tcpoly_param_scoping.scala
+pos/t6204-b.scala
+pos/attachments-typed-another-ident
+pos/t5359.scala
+pos/ticket2197.scala
+pos/t720.scala
+pos/t2130-2.scala
+pos/t2260.scala
+pos/t0304.scala
+pos/t464.scala
+pos/spec-maps.scala
+pos/annotDepMethType.scala
+pos/t6117.scala
+pos/t911.scala
+pos/t757a.scala
+pos/t2504.scala
+pos/t1381-old.scala
+pos/t1232
+pos/needstypeearly.scala
+pos/moduletrans.scala
+pos/t4957.scala
+pos/kinzer.scala
+pos/t318.scala
+pos/widen-existential.scala
+pos/t0095.scala
+pos/t566.scala
+pos/tcpoly_overloaded.scala
+pos/t7516
+pos/t7232
+pos/t698.scala
+pos/t0002.scala
+pos/t0288
+pos/t2994b.scala
+pos/cls.scala
+pos/t3622
+pos/t3671.scala
+pos/tcpoly_subst.scala
+pos/t5703
+pos/depmet_implicit_oopsla_session_simpler.scala
+pos/t5022.scala
+pos/builders.scala
+pos/spec-foo.scala
+pos/t756.scala
+pos/t1569.scala
+pos/implicit-unwrap-tc.scala
+pos/t3688.scala
+pos/t5198.scala
+pos/t432.scala
+pos/t6022b.scala
+pos/channels.scala
+pos/t1075.scala
+pos/null.scala
+pos/t1840
+pos/t6479.scala
+pos/t6311.scala
+pos/t0039.scala
+pos/t1119.scala
+pos/t573.scala
+pos/t1136.scala
+pos/t3938
+pos/spec-sealed.scala
+pos/tcpoly_return_overriding.scala
+pos/t3582b.scala
+pos/t229.scala
+pos/t3498-old.scala
+pos/t531.scala
+pos/t4545.scala
+pos/t6651.scala
+pos/t2133.scala
+pos/tinondefcons.scala
+pos/t6157.scala
+pos/t6358.scala
+pos/t7690.scala
+pos/t5779-numeq-warn.scala
+pos/list-extractor.scala
+pos/t892.scala
+pos/t2127.scala
+pos/t7180.scala
+pos/nullary_poly.scala
+pos/virtpatmat_exist3.scala
+pos/t1176
+pos/spec-funs.scala
+pos/specialize10.scala
+pos/t6514.scala
+pos/exhaustive_heuristics.scala
+pos/t0066.scala
+pos/t460.scala
+pos/t2130-1.scala
+pos/t124.scala
+pos/annotations.scala
+pos/pat_gilles.scala
+pos/array-interfaces.scala
+pos/t6210.scala
+pos/t3792.scala
+pos/implicits-old.scala
+pos/t389.scala
+pos/t115.scala
+pos/virtpatmat_exhaust_unchecked.scala
+pos/scoping3.scala
+pos/t6033.scala
+pos/depmet_implicit_oopsla_session.scala
+pos/t602.scala
+pos/test5.scala
+pos/t611.scala
+pos/t5932.scala
+pos/t4910.scala
+pos/unapplySeq.scala
+pos/t344.scala
+pos/t3363-new.scala
+pos/t4018.scala
+pos/t4553.scala
+pos/t5082.scala
+pos/t3869.scala
+pos/t3836.scala
+pos/tcpoly_typeapp.scala
+pos/t1409
+pos/nonlocal-unchecked.scala
+pos/t0082.scala
+pos/z1720.scala
+pos/t7232c
+pos/t2018.scala
+pos/t3943
+pos/t2187-2.scala
+pos/unicode-decode.scala
+pos/t4757
+pos/t0710.scala
+pos/t0305.scala
+pos/t160.scala
+pos/t7591
+pos/simplelists.scala
+pos/List1.scala
+pos/t516.scala
+pos/t6648.scala
+pos/t5165
+pos/t0055.scala
+pos/t4744
+pos/t7377
+pos/t5726.scala
+pos/t0091.scala
+pos/t6595.scala
+pos/compile.scala
+pos/depmet_1_pos.scala
+pos/t7364
+pos/philippe3.scala
+pos/spec-doubledef-old.scala
+pos/t4651.scala
+pos/tcpoly_infer_implicit_tuple_wrapper.scala
+pos/t6274.scala
+pos/tcpoly_infer_explicit_tuple_wrapper.scala
+pos/ticket2201.scala
+pos/spec-fields-new.scala
+pos/optmatch.scala
+pos/t7517.scala
+pos/t3560.scala
+pos/t0165.scala
+pos/t0872.scala
+pos/t522.scala
+pos/t2234.scala
+pos/t5031_2.scala
+pos/tcpoly_method.scala
+pos/t6482.scala
+pos/pos-bug1241.scala
+pos/implicits-new.scala
+pos/t2484.scala
+pos/t2425.scala
+pos/t1049.scala
+pos/michel4.scala
+pos/t5958.scala
+pos/virtpatmat_instof_valuetype.scala
+pos/spec-t6286.scala
+pos/t873.scala
+pos/t3137.scala
+pos/Transactions.scala
+pos/t0064.scala
+pos/t7486-named.scala
+pos/t5444.scala
+pos/simple-exceptions.scala
+pos/t1006.scala
+pos/t7200b.scala
+pos/t3777.scala
+pos/t4840.scala
+pos/t211.scala
+pos/nullary.scala
+pos/michel1.scala
+pos/t5031_3
+pos/typealias_dubious.scala
+pos/spec-doubledef-new.scala
+pos/philippe1.scala
+pos/thistypes.scala
+pos/t3570.scala
+pos/t6516.scala
+pos/context.scala
+pos/t3808.scala
+pos/philippe2.scala
+pos/constfold.scala
+pos/t1292.scala
+pos/t1147.scala
+pos/t404.scala
+pos/t4430.scala
+pos/A.scala
+pos/spec-partially.scala
+pos/t5796.scala
+pos/t2409
+pos/t284-pos.scala
+pos/t5313.scala
+pos/t2464
+pos/t1591b.scala
+pos/hk-match
+pos/t595.scala
+pos/t6846.scala
+pos/t6162-inheritance.scala
+pos/relax_implicit_divergence.scala
+pos/patterns2.scala
+pos/t4692.scala
+pos/t3837.scala
+pos/t661.scala
+pos/t2810.scala
+pos/depexists.scala
+pos/virtpatmat_exist4.scala
+pos/t5245.scala
+pos/t7190.scala
+pos/isApplicableSafe.scala
+pos/t6204-a.scala
+pos/t0076.scala
+pos/t1756.scala
+pos/t1745
+pos/t6091.scala
+pos/t0154.scala
+pos/t530.scala
+pos/t2094.scala
+pos/t1034.scala
+pos/t6084.scala
+pos/t2454.scala
+pos/t2956
+pos/tcpoly_ticket2096.scala
+pos/attachments-typed-ident
+pos/polymorphic-case-class.scala
+pos/t252.scala
+pos/spec-constr-old.scala
+pos/t2421c.scala
+pos/t122.scala
+pos/t6574.scala
+pos/t3859.scala
+pos/spec-params-old.scala
+pos/t1196
+pos/t4593.scala
+pos/t596.scala
+pos/t615.scala
+pos/t7689.scala
+pos/t3960.scala
+pos/t3986.scala
+pos/exbound.scala
+pos/t2545.scala
+pos/t1722
+pos/t159.scala
+pos/t3272.scala
+pos/t6301.scala
+pos/t2794.scala
+pos/t3048.scala
+pos/t4970.scala
+pos/t607.scala
+pos/FPTest.scala
+pos/test1.scala
+pos/t3252.scala
+pos/t4176.scala
+pos/t112606A.scala
+pos/t2183.scala
+pos/t430-feb09.scala
+pos/t6275.scala
+pos/t1832.scala
+
+neg/volatile_no_override.scala
+neg/t800.scala
+neg/t5426.scala
+neg/t2462a.scala
+neg/t2641.scala
+neg/classtags_dont_use_typetags.scala
+neg/t5031
+neg/t2275b.scala
+neg/macro-qmarkqmarkqmark.scala
+neg/t4879.scala
+neg/t5956.scala
+neg/t4196.scala
+neg/reify_ann2b.scala
+neg/t6666b.scala
+neg/warn-unused-privates.scala
+neg/t6928.scala
+neg/t6337.scala
+neg/sealed-java-enums.scala
+neg/t563.scala
+neg/t900.scala
+neg/deadline-inf-illegal.scala
+neg/t766.scala
+neg/t5429.scala
+neg/overloaded-implicit.scala
+neg/t875.scala
+neg/abstract-class-error
+neg/unchecked2.scala
+neg/predef-masking.scala
+neg/viewtest.scala
+neg/macro-noexpand
+neg/varargs.scala
+neg/t963b.scala
+neg/t909.scala
+neg/sensitive2.scala
+neg/t5390b.scala
+neg/abstraction-from-volatile-type-error.scala
+neg/macro-exception
+neg/t4431.scala
+neg/t5689.scala
+neg/valueclasses.scala
+neg/overload.scala
+neg/t0204.scala
+neg/t908.scala
+neg/t750
+neg/patmatexhaust.scala
+neg/macro-invalidusage-badtargs
+neg/t1168.scala
+neg/t5761.scala
+neg/t0503.scala
+neg/t7235.scala
+neg/t1215.scala
+neg/primitive-sigs-1
+neg/t5578.scala
+neg/names-defaults-neg-warn.scala
+neg/t6436b.scala
+neg/t3098
+neg/t910.scala
+neg/parstar.scala
+neg/t4568.scala
+neg/newpat_unreachable.scala
+neg/warn-unused-imports.scala
+neg/t1181.scala
+neg/t5903c
+neg/t7294.scala
+neg/t4091.scala
+neg/t5452-old.scala
+neg/t5696.scala
+neg/t0209.scala
+neg/t2910.scala
+neg/t7388.scala
+neg/noMember2.scala
+neg/no-predef.scala
+neg/t6952.scala
+neg/t1909b.scala
+neg/abstract-report2.scala
+neg/t5318.scala
+neg/t6074.scala
+neg/t7171.scala
+neg/abstract-vars.scala
+neg/unchecked-impossible.scala
+neg/variances-refinement.scala
+neg/t3453.scala
+neg/t5189.scala
+neg/t4302.scala
+neg/xmltruncated7.scala
+
+run/t7249.scala
+run/t3563.scala
+run/t6111.scala
+run/classtags_multi.scala
+run/t5201.scala
+run/checked.scala
+run/valueclasses-classtag-basic.scala
+run/t7171.scala
+run/t5053.scala
+run/t4535.scala
+run/t5923d
+run/t7291.scala
+run/partialfun.scala
+run/macro-term-declared-in-package-object
+run/mapValues.scala
+run/gadts.scala
+run/t2386-new.scala
+run/virtpatmat_stringinterp.scala
+run/t657.scala
+run/t0017.scala
+run/t5713
+run/t576.scala
+run/t3580.scala
+run/virtpatmat_partial.scala
+run/t6646.scala
+run/mixins.scala
+run/t1672.scala
+run/macro-expand-implicit-macro-has-implicit
+run/tuple-match.scala
+run/t7039.scala
+run/virtpatmat_opt_sharing.scala
+run/virtpatmat_casting.scala
+run/t2176.scala
+run/eta-expand-star2.scala
+run/macro-impl-relaxed
+run/intmap.scala
+run/t751.scala
+run/t1591.scala
+run/macro-typecheck-implicitsdisabled
+run/t6911.scala
+run/t5604.scala
+run/macro-term-declared-in-default-param
+run/collection-stacks.scala
+run/multi-array.scala
+run/t4560b.scala
+run/buffer-slice.scala
+run/t5629.scala
+run/t6690.scala
+run/matchonstream.scala
+run/t3603.scala
+run/lazy-exprs.scala
+run/macro-quasiquotes
+run/Course-2002-13.scala
+run/t6337a.scala
+run/exoticnames.scala
+run/t0936.scala
+run/existentials3-old.scala
+run/runtime-richChar.scala
+run/t6272.scala
+run/t7215.scala
+run/t1939.scala
+run/ReverseSeqView.scala
+run/lazy-leaks.scala
+run/t0048.scala
+run/t3994.scala
+run/t2241.scala
+run/t627.scala
+run/t5966.scala
+run/getClassTest-valueClass.scala
+run/t3619.scala
+run/t1300.scala
+run/t2177.scala
+run/t3760.scala
+run/t1829.scala
+run/macro-expand-implicit-macro-is-view
+run/t889.scala
+run/QueueTest.scala
+run/t4537
+run/t3699.scala
+run/valueclasses-manifest-basic.scala
+run/t1192.scala
+run/macro-expand-tparams-bounds
+run/macro-expand-nullary-generic
+run/t1434.scala
+run/t6443-varargs.scala
+run/macro-term-declared-in-trait
+run/t4080.scala
+run/t2236-old.scala
+run/matcharraytail.scala
+run/infiniteloop.scala
+run/t5733.scala
+run/virtpatmat_nested_lists.scala
+run/t5158.scala
+run/t6695.scala
+run/t6070.scala
+run/t4558.scala
+run/exc2.scala
+run/patmat-behavior-2.scala
+run/overloads.scala
+run/iterator-iterate-lazy.scala
+run/t6957.scala
+run/transform.scala
+run/t5500.scala
+run/t6663.scala
+run/castsingleton.scala
+run/t4147.scala
+run/virtpatmat_staging.scala
+run/t4565_1.scala
+run/t5588.scala
+run/run-bug4840.scala
+run/t3496.scala
+run/t5867.scala
+run/search.scala
+run/t3112.scala
+run/hashsetremove.scala
+run/interop_manifests_are_classtags.scala
+run/t6443.scala
+run/macro-expand-tparams-prefix
+run/contrib674.scala
+run/t3508.scala
+run/t4300.scala
+run/virtpatmat_typed.scala
+run/macro-term-declared-in-class-object
+run/map_test.scala
+run/t5040.scala
+run/t4827b.scala
+run/lift-and-unlift.scala
+run/t6574b.scala
+run/t7240
+run/t3984.scala
+run/virtpatmat_tailcalls_verifyerror.scala
+run/macro-term-declared-in-class-class
+run/emptypf.scala
+run/t6631.scala
+run/t6104.scala
+run/t2818.scala
+run/t3761-overload-byname.scala
+run/t2526.scala
+run/phantomValueClass.scala
+run/t3126.scala
+run/arybufgrow.scala
+run/t3980.scala
+run/t7375b
+run/t6077_patmat_cse_irrefutable.scala
+run/classmanifests_new_core.scala
+run/t3395.scala
+run/name-based-patmat.scala
+run/inliner-infer.scala
+run/t5171.scala
+run/t3726.scala
+run/null-hash.scala
+run/t4027.scala
+run/t2544.scala
+run/patmatnew.scala
+run/t5923b
+run/t7242.scala
+run/classtags_core.scala
+run/streamWithFilter.scala
+run/t3038b.scala
+run/macro-expand-varargs-explicit-over-nonvarargs-good
+run/macro-divergence-spurious
+run/macro-duplicate
+run/t2958.scala
+run/patch-boundary.scala
+run/t2333.scala
+run/lazy-override-run.scala
+run/macro-quasiinvalidbody-c
+run/t5037.scala
+run/takeAndDrop.scala
+run/t6126.scala
+run/t0883.scala
+run/t7617a
+run/t4171.scala
+run/empty-array.scala
+run/t7198.scala
+run/t493.scala
+run/genericValueClass.scala
+run/t0677-old.scala
+run/t1373.scala
+run/t4461.scala
+run/t6011b.scala
+run/t7584.scala
+run/t3935.scala
+run/t6928-run.scala
+run/t744.scala
+run/t3241.scala
+run/blame_eye_triple_eee-double.scala
+run/t3829.scala
+run/t5577.scala
+run/t5914.scala
+run/t601.scala
+run/t5610.scala
+run/macro-basic-mamd-mi
+run/t6150.scala
+run/stringbuilder.scala
+run/t7290.scala
+run/t6888.scala
+run/t6327.scala
+run/virtpatmat_unapplyseq.scala
+run/t4656.scala
+run/macro-term-declared-in-method
+run/macro-expand-implicit-macro-is-implicit
+run/blame_eye_triple_eee-float.scala
+run/t4482.scala
+run/t5488.scala
+run/matchemptyarray.scala
+run/t3714.scala
+run/richWrapperEquals.scala
+run/t5328.scala
+run/stream_flatmap_odds.scala
+run/implicitclasses.scala
+run/t6827.scala
+run/t6394b
+run/complicatedmatch.scala
+run/valueclasses-classmanifest-basic.scala
+run/unreachable.scala
+run/caseclasses.scala
+run/withIndex.scala
+run/exc1.scala
+run/amp.scala
+run/t1423.scala
+run/t594.scala
+run/t6353.scala
+run/byname.scala
+run/vector1.scala
+run/t5879.scala
+run/t1048.scala
+run/t5080.scala
+run/t4190.scala
+run/caseClassEquality.scala
+run/macro-enclosures
+run/collections-toSelf.scala
+run/implicits.scala
+run/finalvar.scala
+run/lazy-locals.scala
+run/t7231.scala
+run/t0508.scala
+run/t6628.scala
+run/t6406-regextract.scala
+run/t0911.scala
+run/t4013c.scala
+run/t3502.scala
+run/t5648.scala
+run/retclosure.scala
+run/t2857.scala
+run/t4859.scala
+run/t5162.scala
+run/t3038.scala
+run/classof.scala
+run/t4062.scala
+run/unapplyArray.scala
+run/t4297.scala
+run/t5923a
+run/iterators.scala
+run/t1537.scala
+run/boolexprs.scala
+run/valueclasses-classtag-generic.scala
+run/macro-term-declared-in-anonymous
+run/tcpoly_monads.scala
+run/t5407.scala
+run/scan.scala
+run/forvaleq.scala
+run/null-and-intersect.scala
+run/t7047
+run/t0607.scala
+run/sequenceComparisons.scala
+run/t4396.scala
+run/macro-undetparams-consfromsls
+run/t2029.scala
+run/t1220.scala
+run/option-fold.scala
+run/t5284c.scala
+run/macro-auto-duplicate
+run/t3529.scala
+run/t4697.scala
+run/t2251.scala
+run/t5300.scala
+run/virtpatmat_valdef.scala
+run/t2147.scala
+run/virtpatmat_extends_product.scala
+run/list_map.scala
+run/t1333.scala
+run/matchbytes.scala
+run/valueclasses-classmanifest-existential.scala
+run/records.scala
+run/t3088.scala
+run/macro-def-path-dependent
+run/t6443-by-name.scala
+run/t1044.scala
+run/delay-good.scala
+run/case-class-23.scala
+run/weakconform.scala
+run/patmat-bind-typed.scala
+run/t4835.scala
+run/t3097.scala
+run/t405.scala
+run/existentials.scala
+run/t2876.scala
+run/t4809.scala
+run/t1427.scala
+run/t6135.scala
+run/t3575.scala
+run/t5688.scala
+run/t6900.scala
+run/macro-expand-unapply-a
+run/t6677b.scala
+run/t7375a.scala
+run/t7300.scala
+run/t6246.scala
+run/typed-annotated
+run/elidable-noflags.scala
+run/t0042.scala
+run/t3050.scala
+run/t4536.scala
+run/NestedClasses.scala
+run/t3877.scala
+run/seqlike-kmp.scala
+run/t5907.scala
+run/t266.scala
+run/missingparams.scala
+run/t2255.scala
+run/private-inline.scala
+run/t3488.scala
+run/t3950.scala
+run/typealias_overriding.scala
+run/constant-optimization.scala
+run/t7507.scala
+run/t6090.scala
+run/iterator-concat.scala
+run/t4582.scala
+run/macro-term-declared-in-class
+run/macro-typecheck-macrosdisabled2
+run/t3425.scala
+run/t4935.scala
+run/t3326.scala
+run/boolord.scala
+run/t1141.scala
+run/virtpatmat_unapply.scala
+run/t5971.scala
+run/t3651.scala
+run/macro-sip19-revised
+run/pure-args-byname-noinline.scala
+run/preinits.scala
+run/t5532.scala
+run/concat-two-strings.scala
+run/t3269.scala
+run/macro-impl-default-params
+run/t2162.scala
+run/matchonseq.scala
+run/t5428.scala
+run/macro-expand-overload
+run/t4660.scala
+run/enrich-gentraversable.scala
+run/macro-expand-override
+run/t4054.scala
+run/t4753.scala
+run/valueclasses-manifest-generic.scala
+run/macro-typecheck-macrosdisabled
+run/t2308a.scala
+run/duplicate-meth.scala
+run/interop_classtags_are_classmanifests.scala
+run/t3232.scala
+run/t2075.scala
+run/virtpatmat_partial_backquoted.scala
+run/try-2.scala
+run/macro-openmacros
+run/macro-undetparams-macroitself
+run/t6318_derived.scala
+run/deprecate-early-type-defs.scala
+run/dead-code-elimination.scala
+run/t4827.scala
+run/Course-2002-07.scala
+run/slice-strings.scala
+run/t6292.scala
+run/t6206.scala
+run/t1042.scala
+run/t1718.scala
+run/t2074_2.scala
+run/arraycopy.scala
+run/indexedSeq.scala
+run/macro-term-declared-in-implicit-class
+run/t3511.scala
+run/t6290.scala
+run/distinct.scala
+run/virtpatmat_alts.scala
+run/valueclasses-pavlov.scala
+run/exceptions.scala
+run/t1368.scala
+run/t5856.scala
+run/t6968.scala
+run/names-defaults.scala
+run/macro-expand-tparams-implicit
+run/t5881.scala
+run/t3540.scala
+run/virtpatmat_try.scala
+run/t7181.scala
+run/value-class-extractor.scala
+run/value-class-extractor-2.scala
+run/t3150.scala
+run/exc.scala
+run/t3516.scala
+run/delay-bad.scala
+run/infix.scala
+run/t1309.scala
+run/t6370.scala
+run/t6725-2.scala
+run/macro-impl-tparam-typetag-is-optional
+run/macro-term-declared-in-block
+run/matchnull.scala
+run/t2127.scala
+run/t7325.scala
+run/groupby.scala
+run/t3932.scala
+run/t4871.scala
+run/longmap.scala
+run/t1524.scala
+run/t6187b.scala
+run/kmpSliceSearch.scala
+run/t7088.scala
+run/t5804.scala
+run/stringbuilder-drop.scala
+run/t5753_1
+pos/cyclics-pos.scala
+pos/cfcrash.scala
+pos/tcpoly_higherorder_bound_method.scala
+pos/t5084.scala
+pos/trait-force-info.scala
+pos/macro-qmarkqmarkqmark.scala
+pos/t7785.scala
+pos/nested.scala
+pos/t3152.scala
+pos/t5031
+pos/t6925b.scala
+pos/t1107b
+pos/t5012.scala
+pos/virtpatmat_obj_in_case.scala
+pos/t4938.scala
+pos/t3856.scala
+pos/spec-cyclic.scala
+pos/aliases.scala
+pos/typerep_pos.scala
+pos/t119.scala
+pos/t1050.scala
+pos/t3670.scala
+pos/t6145.scala
+pos/t7315.scala
+pos/t5930.scala
+pos/t789.scala
+pos/t5071.scala
+pos/t4731.scala
+pos/t4547.scala
+pos/t2038.scala
+pos/testCoercionThis.scala
+pos/t2444.scala
+pos/t5744
+pos/t780.scala
+pos/t1722-A.scala
+pos/virtpatmat_exist1.scala
+pos/t6225.scala
+pos/t762.scala
+pos/t0204.scala
+pos/rebind.scala
+pos/spec-short.scala
+pos/comp-rec-test.scala
+pos/lub-dealias-widen.scala
+pos/t1168.scala
+pos/modules.scala
+pos/t4220.scala
+pos/t4070.scala
+pos/t175.scala
+pos/t2500.scala
+pos/t5029.scala
+pos/itay.scala
+pos/t4202.scala
+pos/t1987b
+pos/t3534.scala
+pos/infer2-pos.scala
+pos/spec-sparsearray-new.scala
+pos/t7091.scala
+pos/ticket0137.scala
+pos/collectGenericCC.scala
+pos/t640.scala
+pos/t4305.scala
+pos/extractor-types.scala
+pos/t3880.scala
+pos/spec-annotations.scala
+pos/t3577.scala
+pos/compile1.scala
+pos/spec-t3497.scala
+pos/hkrange.scala
+pos/t287.scala
+pos/t7294.scala
+pos/t6008.scala
+pos/t4432.scala
+pos/CustomGlobal.scala
+pos/patmat.scala
+pos/t2413
+pos/t2910.scala
+pos/t592.scala
+pos/t6245
+pos/infer.scala
+pos/t7228.scala
+pos/compound.scala
+pos/attributes.scala
+pos/t6771.scala
+pos/t1090.scala
+pos/t684.scala
+pos/t577.scala
+pos/t4273.scala
+pos/t6278-synth-def.scala
+pos/t6184.scala
+neg/t0214.scala
+neg/t4842.scala
+neg/t6214.scala
+neg/reify_nested_inner_refers_to_local.scala
+neg/t576.scala
+neg/t5969.scala
+neg/tcpoly_variance.scala
+neg/t7509.scala
+neg/mixins.scala
+neg/parent-inherited-twice-error.scala
+neg/macro-abort
+neg/constructor-init-order.scala
+neg/t6042.scala
+neg/t0590.scala
+neg/eta-expand-star-deprecation.scala
+neg/t4221.scala
+neg/t6263.scala
+neg/t783.scala
+neg/t5554.scala
+neg/macro-invalidsig-params-badtype
+neg/multi-array.scala
+neg/raw-types-stubs
+neg/spec-overrides.scala
+neg/t836.scala
+neg/t7289_status_quo.scala
+neg/t5675.scala
+neg/macro-quasiquotes
+neg/t6667.scala
+neg/t6597.scala
+neg/t6264.scala
+neg/t0345.scala
+neg/t7294b.scala
+neg/t5340.scala
+neg/t2144.scala
+neg/t1010.scala
+neg/t1838.scala
+neg/t5189b.scala
+neg/reify_metalevel_breach_-1_refers_to_1.scala
+neg/t6601
+neg/wellkinded_wrongarity.scala
+neg/t3909.scala
+neg/t876.scala
+neg/t5390.scala
+neg/unit2anyref.scala
+neg/t0351.scala
+neg/t5120.scala
+neg/t1038.scala
+neg/t5878.scala
+neg/qualifying-class-error-2.scala
+neg/t3816.scala
+neg/tailrec.scala
+neg/volatile.scala
+neg/t944.scala
+neg/t1705.scala
+neg/t3977.scala
+neg/t5553_2.scala
+neg/t5318c.scala
+neg/overload-msg.scala
+neg/t5440.scala
+neg/t6335.scala
+neg/compile-time-only-b.scala
+neg/t501.scala
+neg/override.scala
+neg/t663.scala
+neg/t5892.scala
+neg/t1980.scala
+neg/macro-false-deprecation-warning
+neg/t5148.scala
+neg/t585.scala
+neg/t3776.scala
+neg/interop_classtags_arenot_manifests.scala
+neg/t4044.scala
+neg/macro-invalidusage-nontypeable
+neg/t6375.scala
+neg/t500.scala
+neg/t4877.scala
+neg/t5357.scala
+neg/interop_abstypetags_arenot_manifests.scala
+neg/t4460a.scala
+neg/t5318b.scala
+neg/t3234.scala
+neg/t4440.scala
+neg/t6663.scala
+neg/t6357.scala
+neg/gadts1.scala
+neg/cyclics.scala
+neg/t5060.scala
+neg/scopes.scala
+run/t4013.scala
+run/value-class-extractor-seq.scala
+run/macro-expand-tparams-explicit
+run/tuples.scala
+run/t5753_2
+run/t0528.scala
+run/t5105.scala
+run/t1195-old.scala
+run/t7341.scala
+run/t3670.scala
+run/t2594_tcpoly.scala
+run/t3895.scala
+run/t0668.scala
+run/slices.scala
+run/t6666a.scala
+run/valueclasses-classmanifest-generic.scala
+run/t2316_run.scala
+run/t3004.scala
+run/viewtest.scala
+run/t6481.scala
+run/t0005.scala
+run/t4110-old.scala
+run/t4766.scala
+run/t5500b.scala
+run/t7407b.scala
+run/backreferences.scala
+run/arrayview.scala
+run/t629.scala
+run/t5903c
+run/unittest_collection.scala
+run/spec-nlreturn.scala
+run/macro-term-declared-in-object-object
+run/triple-quoted-expr.scala
+run/t5937.scala
+run/t6011c.scala
+run/macro-expand-implicit-argument
+run/try.scala
+run/t1987b
+run/t6089.scala
+run/macro-range
+run/t2524.scala
+run/t4770.scala
+run/virtpatmat_unapplyprod.scala
+run/t1535.scala
+run/ctor-order.scala
+pos/t5210.scala
+pos/t5384.scala
+pos/rangepos.scala
+pos/t443.scala
+pos/t1480.scala
+pos/t116.scala
+pos/seqtest2.scala
+pos/scoping1.scala
+pos/t4269.scala
+pos/lookupswitch.scala
+pos/t3642
+pos/t5706.scala
+pos/SI-5788.scala
+pos/t7264
+pos/t0031.scala
+pos/macro-deprecate-dont-touch-backquotedidents.scala
+pos/t6815.scala
+pos/test4refine.scala
+pos/michel5.scala
+pos/t0851.scala
+pos/t1185.scala
+pos/sudoku.scala
+pos/t7520.scala
+pos/t6208.scala
+pos/t3411.scala
+pos/t295.scala
+pos/S3.scala
+pos/t0674.scala
+pos/t6664b.scala
+pos/variances_pos.scala
+pos/liftcode_polymorphic.scala
+pos/t3174b.scala
+pos/t7232d
+pos/t578.scala
+pos/implicit-infix-ops.scala
+pos/t4363.scala
+pos/t532.scala
+pos/exponential-spec.scala
+pos/t599.scala
+pos/t5862.scala
+pos/t4603
+pos/t3676.scala
+pos/t1357.scala
+pos/native-warning.scala
+pos/t1230
+pos/t6028
+pos/t4275.scala
+pos/overloaded_extractor_and_regular_def.scala
+pos/t4205
+pos/matthias1.scala
+pos/testcast.scala
+pos/generic-sigs.scala
+pos/t0093.scala
+pos/specializes-sym-crash.scala
+pos/t0061.scala
+pos/t2429.scala
+pos/t694.scala
+pos/javaReadsSigs
+pos/t2023.scala
+pos/t704.scala
+pos/t2208_pos.scala
+pos/t5137.scala
+pos/t2683.scala
+pos/t0049.scala
+pos/t1029
+pos/t4243.scala
+pos/typerep-stephane.scala
+pos/t177.scala
+pos/t5967.scala
+pos/t430.scala
+pos/virtpatmat_infer_single_1.scala
+pos/pat_iuli.scala
+pos/t1071.scala
+pos/t7226.scala
+pos/t1843.scala
+pos/t419.scala
+pos/t7364b
+pos/t1159.scala
+pos/t5305.scala
+pos/t7694.scala
+pos/t6047.scala
+pos/t3578.scala
+pos/t2082.scala
+pos/setter-not-implicit.scala
+pos/t1133.scala
+pos/t3862.scala
+pos/t942
+pos/nothing_manifest_disambig-new.scala
+pos/iterator-traversable-mix.scala
+pos/eta.scala
+pos/test4.scala
+pos/t2691.scala
+pos/t4502.scala
+pos/t7183.scala
+pos/protected-t1010.scala
+pos/X.scala
+pos/virtpatmat_exist2.scala
+pos/t4911.scala
+pos/t3477.scala
+pos/t4173.scala
+pos/t7782.scala
+pos/t2399.scala
+pos/virtpatmat_alts_subst.scala
+pos/propagate.scala
+pos/t2421b_pos.scala
+pos/t183.scala
+pos/t7033.scala
+pos/t3612.scala
+pos/t5330c.scala
+pos/t3020.scala
+pos/t4869.scala
+pos/t3373.scala
+pos/spec-params-new.scala
+pos/t3672.scala
+pos/t4501.scala
+pos/t1565.scala
+pos/t3774.scala
+pos/t6942
+neg/t3275.scala
+neg/t421.scala
+neg/t5702-neg-bad-brace.scala
+neg/t3663
+neg/badtok-1.scala
+neg/t677.scala
+neg/t7756b.scala
+neg/t6534.scala
+neg/t6276.scala
+neg/t5762.scala
+neg/abstract.scala
+neg/t2405.scala
+neg/t0418.scala
+neg/t5390c.scala
+neg/lazyvals.scala
+neg/lubs.scala
+neg/abstract-report.scala
+neg/t4163.scala
+neg/t5702-neg-bad-and-wild.scala
+neg/macro-invalidret
+neg/t6728.scala
+neg/t5152.scala
+neg/t1432.scala
+neg/abstract-inaccessible.scala
+neg/import-precedence.scala
+neg/t2462b.scala
+neg/macro-invalidusage-presuper
+neg/specification-scopes
+neg/t6048.scala
+neg/t4079
+neg/macro-basic-mamdmi
+neg/t7020.scala
+neg/t3015.scala
+neg/t0207.scala
+neg/t2296b
+neg/t0673
+neg/t3761-overload-byname.scala
+neg/t6675.scala
+neg/t5529.scala
+neg/sensitive.scala
+neg/t742.scala
+neg/t5067.scala
+neg/t6162-overriding.scala
+neg/variances.scala
+neg/t5728.scala
+neg/t6323a.scala
+neg/compile-time-only-a.scala
+neg/t6795.scala
+neg/t2494.scala
+neg/t3649.scala
+neg/macro-invalidsig
+neg/t2796.scala
+neg/t112706A.scala
+neg/t0764.scala
+neg/t3757
+neg/t1431.scala
+neg/exhausting.scala
+neg/t1523.scala
+neg/t779.scala
+neg/xmltruncated1.scala
+neg/t2208.scala
+neg/t2078.scala
+neg/t521.scala
+neg/null-unsoundness.scala
+neg/stmt-expr-discard.scala
+neg/t0513.scala
+neg/unchecked-abstract.scala
+neg/t4460c.scala
+neg/divergent-implicit.scala
+neg/t5078.scala
+neg/t1701.scala
+neg/t0816.scala
+neg/t1672b.scala
+neg/macro-invalidusage-badbounds
+neg/tailrec-2.scala
+neg/t4064.scala
+neg/reflection-names-neg.scala
+neg/t5510.scala
+neg/t3873.scala
+neg/tailrec-3.scala
+neg/t0226.scala
+neg/t2031.scala
+neg/t633.scala
+neg/constrs.scala
+neg/anyval-anyref-parent.scala
+neg/t7290.scala
+neg/t1041.scala
+neg/patternalts.scala
+neg/error_tooManyArgsPattern.scala
+neg/checksensibleUnit.scala
+neg/t6539
+neg/t4417.scala
+neg/wellkinded_app.scala
+neg/for-comprehension-old.scala
+neg/t2779.scala
+neg/object-not-a-value.scala
+neg/t2968b.scala
+neg/t6483.scala
+neg/t6902.scala
+neg/t6963a.scala
+neg/t3399.scala
+neg/t0015.scala
+neg/t3995.scala
+neg/t276.scala
+neg/t6758.scala
+neg/t2441.scala
+neg/cycle-bounds.scala
+neg/t1241.scala
+neg/t4137.scala
+neg/unicode-unterminated-quote.scala
+neg/t4762.scala
+neg/typeerror.scala
+neg/implicits.scala
+neg/t961.scala
+neg/ambiguous-float-dots2.scala
+neg/t2416.scala
+neg/t5799.scala
+neg/t7285.scala
+neg/implicit-shadow.scala
+neg/t2388.scala
+neg/java-access-neg
+neg/found-req-variance.scala
+neg/hk-bad-bounds.scala
+neg/t3224.scala
+neg/t1033.scala
+neg/t7385.scala
+neg/t5882.scala
+neg/t4541.scala
+neg/t2973.scala
+neg/t6406-regextract.scala
+neg/t6666.scala
+neg/t4831.scala
+neg/t425.scala
+neg/t1845.scala
+neg/t3683b.scala
+neg/t2801.scala
+neg/t6083.scala
+neg/t0528neg.scala
+neg/stringinterpolation_macro-neg.scala
+neg/t668.scala
+neg/t5666.scala
+neg/t4271.scala
+neg/interop_typetags_arenot_classmanifests.scala
+neg/t1355.scala
+neg/t715.scala
+neg/t7238.scala
+neg/t7473.scala
+neg/t7292-removal.scala
+neg/tcpoly_infer_ticket1162.scala
+neg/t4098.scala
+neg/t6013
+neg/t6227.scala
+neg/t464-neg.scala
+neg/badtok-3.scala
+neg/t6082.scala
+neg/anytrait.scala
+neg/valueclasses-doubledefs.scala
+neg/t7519.scala
+neg/overloaded-unapply.scala
+neg/t1163.scala
+neg/wellkinded_bounds.scala
+neg/t7292-deprecation.scala
+neg/t5044.scala
+neg/t0842.scala
+neg/t6436.scala
+neg/interop_typetags_arenot_classtags.scala
+neg/t3653.scala
+neg/higherkind_novalue.scala
+neg/t935.scala
+neg/t6040.scala
+neg/annot-nonconst.scala
+neg/macro-deprecate-idents.scala
+neg/illegal-stmt-start.scala
+neg/t565.scala
+neg/case-collision.scala
+neg/t3209.scala
+neg/t5821.scala
+neg/abstract-class-2.scala
+neg/t846.scala
+neg/quasiquotes-syntax-error-position.scala
+neg/t3987.scala
+neg/t877.scala
+neg/t0117.scala
+neg/t692.scala
+neg/t6666d.scala
+neg/t5702-neg-ugly-xbrace.scala
+neg/t7752.scala
+neg/case-collision2.scala
+neg/t6526.scala
+neg/t2213.scala
+neg/t7756a.scala
+neg/t845.scala
+neg/macro-override-macro-overrides-abstract-method-a
+neg/tcpoly_ticket2101.scala
+neg/delayed-init-ref.scala
+neg/caseinherit.scala
+neg/t3189.scala
+neg/unchecked-suppress.scala
+neg/t2180.scala
+neg/t1371.scala
+neg/macro-cyclic
+neg/t6123-explaintypes-macros
+neg/t4134.scala
+neg/t691.scala
+neg/t2421b.scala
+neg/t4691_exhaust_extractor.scala
+neg/t4419.scala
+neg/t5801.scala
+neg/t650.scala
+neg/t5735.scala
+neg/t696.scala
+neg/t882.scala
+neg/t2968.scala
+neg/t7507.scala
+neg/macro-invalidusage-badargs
+neg/macro-reify-typetag-typeparams-notags
+neg/wellkinded_app2.scala
+neg/t4425b.scala
+neg/t2296a
+neg/t1878.scala
+neg/t649.scala
+neg/override-object-no.scala
+neg/t4174.scala
+neg/t2070.scala
+neg/sabin2.scala
+neg/t5903e
+neg/t6566a.scala
+neg/finitary-error.scala
+neg/t4818.scala
+neg/t3614.scala
+neg/t6666c.scala
+neg/ticket513.scala
+neg/suggest-similar.scala
+neg/t4457_1.scala
+neg/t6666e.scala
+neg/tcpoly_bounds.scala
+neg/t4727.scala
+neg/t4425.scala
+neg/macro-invalidusage-methodvaluesyntax
+neg/t3854.scala
+neg/t3006.scala
+neg/t5580b.scala
+neg/t5378.scala
+neg/t639.scala
+neg/wrong-args-for-none.scala
+neg/t7171b.scala
+neg/t5361.scala
+neg/unreachablechar.scala
+neg/t5572.scala
+neg/t7757a.scala
+neg/macro-invalidimpl
+neg/t2773.scala
+neg/t6359.scala
+neg/saito.scala
+neg/xmltruncated2.scala
+neg/t667.scala
+neg/t3934.scala
+neg/t6771b.scala
+neg/t4584.scala
+neg/wellkinded_wrongarity2.scala
+neg/t7369.scala
+neg/t1477.scala
+neg/t5617.scala
+neg/t7299.scala
+neg/faculty.scala
+neg/virtpatmat_reach_null.scala
+neg/macro-reify-typetag-hktypeparams-notags
+neg/t1224.scala
+neg/xmltruncated3.scala
+neg/t1872.scala
+neg/t558.scala
+neg/t7110.scala
+neg/any-vs-anyref.scala
+neg/t6340.scala
+neg/t4166.scala
+neg/t2918.scala
+neg/t5856.scala
+neg/t4989.scala
+neg/t0003.scala
+neg/t1183.scala
+neg/t963.scala
+neg/t4515.scala
+neg/valueclasses-pavlov.scala
+neg/t608.scala
+neg/choices.scala
+neg/patmat-type-check.scala
+neg/valueclasses-impl-restrictions.scala
+neg/imp2.scala
+neg/protected-constructors.scala
+neg/t6788.scala
+neg/nullary-override.scala
+neg/t200.scala
+neg/t343.scala
+neg/names-defaults-neg-ref.scala
+neg/tcpoly_typealias.scala
+neg/classtags_contextbound_b.scala
+neg/t729.scala
+neg/t5683.scala
+neg/t4928.scala
+neg/t700.scala
+neg/t7669.scala
+neg/macro-invalidshape
+neg/t6011.scala
+neg/t7325.scala
+neg/check-dead.scala
+neg/t550.scala
+neg/t5663-badwarneq.scala
+neg/t0699
+neg/nopredefs.scala
+neg/t3507-old.scala
+neg/t5352.scala
+neg/t6336.scala
+neg/interop_classmanifests_arenot_typetags.scala
+neg/sealed-final-neg.scala
+neg/t2102.scala
+neg/t7636.scala
+neg/t5031b
+neg/t798.scala
+neg/t5702-neg-bad-xbrace.scala
+neg/t0899.scala
+neg/cyclics-import.scala
+neg/badtok-2.scala
+neg/t473.scala
+neg/t3160ambiguous.scala
+neg/t5106.scala
+neg/t1286
+neg/macro-override-macro-overrides-abstract-method-b
+neg/t0259.scala
+neg/t510.scala
+neg/t3836.scala
+neg/t5830.scala
+neg/t1548
+neg/t5580a.scala
+neg/forward.scala
+neg/t591.scala
+neg/t6558b.scala
+neg/t556.scala
+neg/xmltruncated4.scala
+neg/t5497.scala
+neg/t409.scala
+neg/t6283.scala
+neg/override-object-flag.scala
+neg/constructor-prefix-error.scala
+neg/eta-expand-star.scala
+neg/t3392.scala
+neg/t1275.scala
+neg/nested-fn-print.scala
+neg/t7330.scala
+neg/t2275a.scala
+neg/t630.scala
+neg/t4270.scala
+neg/t2775.scala
+neg/pat_unreachable.scala
+neg/t4158.scala
+neg/unit-returns-value.scala
+neg/t1422.scala
+neg/reify_metalevel_breach_-1_refers_to_0_b.scala
+neg/reassignment.scala
+neg/t3683a.scala
+neg/noMember1.scala
+neg/macro-without-xmacros-b
+neg/t1106.scala
+neg/t5182.scala
+neg/t6889.scala
+neg/t4217.scala
+neg/t7501
+neg/t5063.scala
+neg/t1009.scala
+neg/t997.scala
+neg/unchecked.scala
+neg/classtags_contextbound_c.scala
+neg/applydynamic_sip.scala
+neg/t7715.scala
+neg/t588.scala
+neg/t6667b.scala
+neg/t7757b.scala
+neg/t4069.scala
+neg/t515.scala
+neg/variances2.scala
+neg/t1049.scala
+neg/t7289.scala
+neg/t1623.scala
+neg/permanent-blindness.scala
+neg/t5803.scala
+neg/super-cast-or-test.scala
+neg/nonlocal-warning.scala
+neg/t5687.scala
+neg/t5903a
+neg/t6566b.scala
+neg/unchecked-knowable.scala
+neg/t5093.scala
+neg/protected-static-fail
+neg/type-diagnostics.scala
+neg/forgot-interpolator.scala
+neg/interop_abstypetags_arenot_classmanifests.scala
+neg/t5376.scala
+neg/t545.scala
+neg/xmlcorner.scala
+neg/switch.scala
+neg/depmet_1.scala
+neg/abstract-concrete-methods.scala
+neg/t4987.scala
+neg/t5452-new.scala
+neg/t750b
+neg/unchecked-refinement.scala
+neg/t418.scala
+neg/t5354.scala
+neg/t3736.scala
+neg/t631.scala
+neg/t6829.scala
+neg/t0218.scala
+neg/volatile-intersection.scala
+neg/t412.scala
+neg/t693.scala
+neg/t4882.scala
+neg/t1960.scala
+neg/macro-divergence-controlled
+neg/t712.scala
+neg/t5544
+neg/t3222.scala
+neg/t3604.scala
+neg/t1112.scala
+neg/t7157
+neg/accesses.scala
+neg/t452.scala
+neg/t6162-inheritance
+neg/t2442
+neg/t6567.scala
+neg/lazy-override.scala
+neg/abstract-explaintypes.scala
+neg/nested-annotation.scala
+neg/t5753
+neg/t4283b
+neg/t3691.scala
+neg/infix-op-positions.scala
+neg/t3403.scala
+neg/t4851
+neg/structural.scala
+neg/error_dependentMethodTpeConversionToFunction.scala
+neg/t5839.scala
+neg/t5553_1.scala
+neg/reify_metalevel_breach_+0_refers_to_1.scala
+neg/t752.scala
+neg/t6574.scala
+neg/t3714-neg.scala
+neg/t4457_2.scala
+neg/t2148.scala
+neg/t3240.scala
+neg/t1364.scala
+neg/saferJavaConversions.scala
+neg/t414.scala
+neg/t5493.scala
+neg/classtags_contextbound_a.scala
+neg/reify_metalevel_breach_-1_refers_to_0_a.scala
+neg/t3118.scala
+neg/t512.scala
+neg/t2336.scala
+neg/t856.scala
+neg/xmltruncated6.scala
+neg/t2206.scala
+neg/virtpatmat_unreach_select.scala
+neg/t6258.scala
+neg/t6815.scala
+neg/not-possible-cause.scala
+neg/dbldef.scala
+neg/qualifying-class-error-1.scala
+neg/t835.scala
+neg/t5455.scala
+neg/t6558.scala
+neg/t708.scala
+neg/macro-nontypeablebody
+neg/t0565.scala
+neg/xmltruncated5.scala
+neg/t5390d.scala
+neg/t520.scala
+neg/t6138.scala
+neg/macro-without-xmacros-a
+neg/t7214neg.scala
+neg/t2870.scala
+neg/t593.scala
+neg/t4541b.scala
+neg/t4460b.scala
+neg/t284.scala
+neg/t2488.scala
+neg/macro-override-method-overrides-macro
+neg/interop_abstypetags_arenot_classtags.scala
+neg/t3769.scala
+neg/warn-inferred-any.scala
+neg/t664.scala
+neg/t5903d
+neg/t562.scala
+neg/t2316.scala
+neg/t0152.scala
+neg/migration28.scala
+neg/t6443c.scala
+neg/tcpoly_override.scala
+neg/t7324.scala
+neg/t987.scala
+neg/t5903b
+neg/t3481.scala
+neg/t6912.scala
+neg/tcpoly_variance_enforce.scala
+neg/t3913.scala
+neg/names-defaults-neg.scala
+neg/t765.scala
+neg/t5358.scala
+neg/t391.scala
+neg/serialversionuid-not-const.scala
+neg/t771.scala
+neg/t0903.scala
+neg/catch-all.scala
+neg/classmanifests_new_deprecations.scala
+neg/t0606.scala
+neg/t5189_inferred.scala
+neg/macro-reify-typetag-useabstypetag
+neg/t5543.scala
+neg/logImplicits.scala
+neg/interop_typetags_without_classtags_arenot_manifests.scala
+neg/t6535.scala
+neg/t7259.scala
+neg/t2139.scala
+neg/t278.scala
+neg/t5564.scala
+neg/unchecked3.scala
+neg/virtpatmat_reach_sealed_unsealed.scala
+neg/checksensible.scala
+neg/t7721.scala
+run/t3798.scala
+run/macro-expand-varargs-explicit-over-varargs
+run/t3888.scala
+run/t0677-new.scala
+run/t3273.scala
+run/t3763.scala
+run/t2755.scala
+run/t920.scala
+run/t5610a.scala
+run/literals.scala
+run/proxy.scala
+run/unapply.scala
+run/t5830.scala
+run/array-addition.scala
+run/macro-expand-nullary-nongeneric
+run/macro-basic-ma-mdmi
+run/valueclasses-constr.scala
+run/t1247.scala
+run/t3487.scala
+run/rawstrings.scala
+run/patmat-seqs.scala
+run/eta-expand-star.scala
+run/t7436.scala
+run/t3996.scala
+run/constructors.scala
+run/t498.scala
+run/t3835.scala
+run/t298.scala
+run/t2867.scala
+run/t7120
+run/virtpatmat_literal.scala
+run/t2175.scala
+run/t2503.scala
+run/t3026.scala
+run/t603.scala
+run/t0091.scala
+run/t6394a
+run/macro-expand-varargs-implicit-over-varargs
+run/t7407.scala
+run/t2552.scala
+run/priorityQueue.scala
+run/virtpatmat_npe.scala
+run/macro-sip19
+run/t6644.scala
+run/t6614.scala
+run/t2005.scala
+run/t4680.scala
+run/t5903a
+run/classtags_contextbound.scala
+run/Course-2002-05.scala
+run/applydynamic_sip.scala
+run/t1766.scala
+run/retsynch.scala
+run/t7715.scala
+run/t102.scala
+run/nonlocalreturn.scala
+run/macro-reify-staticXXX
+run/Course-2002-06.scala
+run/t6863.scala
+run/t6500.scala
+run/macro-impl-rename-context
+run/t4351.scala
+run/t5009.scala
+run/macro-term-declared-in-annotation
+run/t6271.scala
+run/array-existential-bound.scala
+run/t6443b.scala
+run/t1987.scala
+run/MutableListTest.scala
+run/t7571.scala
+run/t5488-fn.scala
+run/macro-bodyexpandstoimpl
+run/macro-reify-ref-to-packageless
+run/t2212.scala
+run/macro-expand-varargs-implicit-over-nonvarargs
+run/t0807.scala
+run/patmat-behavior.scala
+run/t2446.scala
+run/tuple-zipped.scala
+run/breakout.scala
+run/t4122.scala
+run/macro-settings
+run/t7157
+run/t1323.scala
+run/t4013b.scala
+run/t6309.scala
+run/t4047.scala
+run/t5544
+run/t978.scala
+run/t3361.scala
+run/t6611.scala
+run/t5387.scala
+run/t5656.scala
+run/t4897.scala
+run/numeric-range.scala
+run/t4777.scala
+run/Course-2002-03.scala
+run/string-extractor.scala
+run/view-headoption.scala
+run/patmat_unapp_abstype-new.scala
+run/stream-stack-overflow-filter-map.scala
+run/macro-impl-tparam-only-in-impl
+run/t6559.scala
+run/macro-reify-tagful-a
+run/macro-expand-multiple-arglists
+run/t4709.scala
+run/t3509.scala
+run/t5284b.scala
+run/t7617b
+run/t3923.scala
+run/virtpatmat_apply.scala
+run/t363.scala
+run/manifests-undeprecated-in-2.10.0.scala
+run/matchintasany.scala
+run/t3970.scala
+run/t4996.scala
+run/t5530.scala
+run/macro-term-declared-in-object-class
+run/t3242b.scala
+run/indexedSeq-apply.scala
+run/t107.scala
+run/t2337.scala
+run/t3758-old.scala
+run/t2754.scala
+run/valueclasses-manifest-existential.scala
+run/flat-flat-flat.scala
+run/t6673.scala
+run/interpolationMultiline2.scala
+run/t3493.scala
+run/t0631.scala
+run/t2800.scala
+run/t6506.scala
+run/t6260.scala
+run/t2418.scala
+run/t4415.scala
+run/classmanifests_new_alias.scala
+run/t5380.scala
+run/tcpoly_parseridioms.scala
+run/t1747.scala
+run/t5903d
+run/t3530.scala
+run/t216.scala
+run/macro-term-declared-in-refinement
+run/t4592.scala
+run/t2488.scala
+run/t3327.scala
+run/t5614.scala
+run/t5903b
+run/iterables.scala
+run/t3964.scala
+run/t6329_vanilla.scala
+run/t3038c
+run/t1697.scala
+run/t2030.scala
+run/t3397.scala
+run/t1005.scala
+run/t3353.scala
+run/t1466.scala
+run/t3186.scala
+run/tcpoly_overriding.scala
+run/t5394.scala
+run/t5284.scala
+run/unboxingBug.scala
+run/t7200.scala
+run/macro-reify-basic
+run/t153.scala
+run/iterator3444.scala
+run/macro-expand-implicit-macro-is-val
+run/macro-basic-ma-md-mi
+run/interpolationArgs.scala
+run/t4954.scala
+run/t3645.scala
+run/transpose.scala
+run/t3887.scala
+run/t4288.scala
+run/unittest_iterator.scala
+run/t5543.scala
+run/macro-term-declared-in-object
+run/iq.scala
+run/t2788.scala
+run/t2027.scala
+run/macro-expand-recursive
+run/t949.scala
+run/t1909b.scala
+run/delambdafy-nested-by-name.scala
+run/delambdafy-two-lambdas.scala
+run/macro-blackbox-materialization
+run/lists-run.scala
+run/macro-parse-position
+run/macro-parse-position-malformed
+run/macro-whitebox-dynamic-materialization
+run/macro-whitebox-extractor
+run/macro-vampire-false-warning
+run/macro-whitebox-fundep-materialization
+run/macro-whitebox-structural
+run/mutable-treeset.scala
+run/static-module-method.scala
+run/sort.scala
+run/t1909.scala
+run/t1909c.scala
+run/t3346a.scala
+run/t3346d.scala
+run/t3346f.scala
+run/t3346h.scala
+run/t3346g.scala
+run/t3832.scala
+run/t4742.scala
+run/t5377.scala
+run/t5923c.scala
+run/t6188.scala
+run/t6333.scala
+run/t6385.scala
+run/t7899.scala
+run/t7899-regression.scala
+run/t7584b.scala
+run/t7223.scala
+run/t7859
+run/t7868.scala
+run/t7871
+run/arrayclone-new.scala
+run/arrayclone-old.scala
+run/bitsets.scala
+run/comparable-comparator.scala
+run/colltest1.scala
+run/t2106.scala
+run/t5986.scala
+run/view-iterator-stream.scala
+run/array-charSeq.scala
+pos/signatures
+pos/t1263
+pos/t3249
+neg/t4749.scala
+neg/main1.scala
+neg/t7251
+neg/t7494-after-terminal
+neg/t7494-before-parser
+neg/t7494-right-after-terminal
+run/lazy-traits.scala
+run/OrderingTest.scala
+run/ReplacementMatching.scala
+run/patmat-finally.scala
+run/t3158.scala
+run/t3346e.scala
+run/t4398.scala
+run/t4930.scala
+run/t6534.scala
+pos/sammy_scope.scala
+pos/delambdafy-patterns.scala
+pos/private-types-after-typer.scala
+pos/delambdafy-lambdalift.scala
+pos/sammy_poly.scala
+pos/sammy_single.scala
+pos/SI-4012-b.scala
+pos/sammy_twice.scala
+pos/t3160.scala
+pos/t1014.scala
+pos/t4970b.scala
+pos/t2698.scala
+pos/t5845.scala
+pos/t6201.scala
+pos/t6260a.scala
+pos/t7688.scala
+pos/t7818.scala
+pos/t1203a.scala
+pos/t7834.scala
+pos/t7853.scala
+pos/t7815.scala
+pos/t7853-partial-function.scala
+pos/t7864.scala
+pos/t7928.scala
+pos/t7902.scala
+pos/t7944.scala
+pos/t7847
+neg/accesses2.scala
+neg/bad-advice.scala
+neg/gadts2.scala
+neg/gadts2-strict.scala
+neg/macro-bundle-abstract.scala
+neg/macro-bundle-object.scala
+neg/macro-bundle-trait.scala
+neg/macro-blackbox-dynamic-materialization
+neg/macro-blackbox-extractor
+neg/run-gadts-strict.scala
+neg/macro-blackbox-structural
+neg/sammy_restrictions.scala
+neg/sammy_wrong_arity.scala
+neg/t2462c.scala
+neg/t3346b.scala
+neg/t1909-object.scala
+neg/macro-blackbox-fundep-materialization
+neg/t3346c.scala
+neg/t3871.scala
+neg/t3871b.scala
+neg/t3971.scala
+neg/t3346i.scala
+neg/t6120.scala
+neg/t6260c.scala
+neg/t6680a.scala
+neg/t7239.scala
+neg/t7007.scala
+neg/t7605-deprecation.scala
+neg/t7622-missing-required.scala
+neg/t7629-view-bounds-deprecation.scala
+neg/t7834neg.scala
+neg/t7783.scala
+neg/t7848-interp-warn.scala
+neg/t7519-b
+neg/t7622-missing-dependency
+neg/t7870.scala
+neg/t7877.scala
+neg/t7895.scala
+neg/t7895b.scala
+neg/t7899.scala
+neg/t7895c.scala
+neg/t7859
+run/t4752.scala
+run/t2087-and-2400.scala
+run/t3855.scala
+run/t6637.scala
+run/t6731.scala
+pos/t3999b.scala
+run/t0432.scala
+run/t2514.scala
+run/t7817.scala
+run/t874.scala
+run/type-currying.scala
+run/t3616.scala
+run/t3687.scala
+run/t4570.scala
+run/t5612.scala
+run/t1110.scala
+run/t2636.scala
+run/verify-ctor.scala
+run/t3647.scala
+run/t4560.scala
+run/t6632.scala
+run/hashCodeBoxesRunTime.scala
+run/richs.scala
+run/t6725-1.scala
+pos/t7776.scala
+run/fors.scala
+run/t6706.scala
+run/t3175.scala
+run/delambdafy-dependent-on-param-subst.scala
+run/t4332b.scala
+run/t8048a
+run/t8017
+run/t7985b.scala
+run/t8100.scala
+run/patmat-mix-case-extractor.scala
+run/t4750.scala
+run/t7912.scala
+run/delambdafy-dependent-on-param-subst-2.scala
+run/t8048b
+run/t8091.scala
+run/macroPlugins-macroRuntime
+run/macro-default-params
+run/t6355.scala
+run/t7777
+run/t8002.scala
+run/t8015-ffc.scala
+run/macro-subpatterns
+run/t7985.scala
+run/macroPlugins-macroArgs
+run/t7326.scala
+run/t5045.scala
+run/value-class-partial-func-depmet.scala
+run/t6329_vanilla_bug.scala
+run/macroPlugins-macroExpand
+run/t8010.scala
+run/macroPlugins-typedMacroBody
+run/t7406.scala
+run/t6253c.scala
+run/t6253a.scala
+run/t6253b.scala
+pos/t8146a.scala
+pos/t8046c.scala
+pos/t8002-nested-scope.scala
+pos/t8132.scala
+pos/t8045.scala
+pos/overzealous-assert-genbcode.scala
+pos/t8128.scala
+pos/t8013
+pos/t8064b
+pos/t6780.scala
+pos/t7987
+pos/bcode_throw_null
+pos/t8064
+pos/t8046.scala
+pos/t6231.scala
+pos/t7983.scala
+pos/t5508.scala
+pos/t5508-min.scala
+pos/t8023b.scala
+pos/t6231b.scala
+pos/debug-reset-local-attrs.scala
+pos/t8054.scala
+pos/t2066.scala
+pos/dotless-targs.scala
+pos/t8120.scala
+pos/t5508-min-okay.scala
+pos/t8060.scala
+pos/t8001
+pos/t8138.scala
+pos/t8111.scala
+pos/t8062
+pos/t8011.scala
+pos/t8146b.scala
+pos/t8046b.scala
+pos/t8023.scala
+pos/t5508-min-okay2.scala
+pos/macro-implicit-invalidate-on-error.scala
+neg/t6563.scala
+neg/missing-param-type-tuple.scala
+neg/not-a-legal-formal-parameter-tuple.scala
+neg/t7897.scala
+neg/t8015-ffa.scala
+neg/quasiquotes-unliftable-not-found.scala
+neg/t2066b.scala
+neg/dotless-targs.scala
+neg/patmat-classtag-compound.scala
+neg/t2066.scala
+neg/t8035-deprecated.scala
+neg/t6675b.scala
+neg/t8104
+neg/t7872.scala
+neg/t7850.scala
+neg/t7967.scala
+neg/macro-bundle-overloaded.scala
+neg/t6355a.scala
+neg/class-of-double-targs.scala
+neg/t6355b.scala
+neg/macro-reify-splice-splice
+neg/macro-bundle-noncontext.scala
+neg/t8015-ffb.scala
+neg/t8035-removed.scala
+neg/t7984.scala
+neg/t8024.scala
+neg/t8024b.scala
+neg/t8157.scala
+neg/t8146-non-finitary-2.scala
+neg/t8006.scala
+neg/t7872c.scala
+neg/t8146-non-finitary.scala
+neg/t7872b.scala
+neg/t6920.scala
+run/t6200.scala
+run/t6196.scala
+run/macro-bundle-context-refinement
+run/macro-enclosingowner-detectvar
+run/macro-enclosingowner-sbt
+run/macro-bundle-context-alias
+run/macro-bundle-whitebox-use-refined
+run/macro-bundle-whitebox-use-raw
+neg/name-lookup-stable.scala
+neg/t0764b.scala
+neg/no-implicit-to-anyref-any-val.scala
+neg/t1503.scala
+neg/t4728.scala
+neg/t6455.scala
+neg/t6260-named.scala
+neg/t6844.scala
+neg/t7475c.scala
+neg/t7475e.scala
+neg/t7475f.scala
+neg/macro-bundle-whitebox-use-raw
+neg/macro-bundle-whitebox-use-refined
+neg/macro-incompatible-macro-engine-b
+neg/t7980.scala
+neg/macro-incompatible-macro-engine-a
+neg/t8143a.scala
+neg/t8072.scala
+neg/t8207.scala
+neg/t8182.scala
+neg/t8219-any-any-ref-equals.scala
+neg/t8177a.scala
+neg/t8228.scala
+neg/t8229.scala
+neg/t8237-default.scala
+neg/t8244b.scala
+neg/t8244e
+neg/t8244c.scala
+neg/t8265.scala
+neg/t8266-invalid-interp.scala
+neg/t6931
+neg/t8376
+neg/t8372.scala
+neg/t8300-overloading.scala
+neg/t8244
+neg/t8158
+neg/t8431.scala
+pos/implicit-anyval-2.10.scala
+pos/delambdafy_t6260_method.scala
+pos/macro-bundle-disambiguate-bundle.scala
+pos/macro-bundle-disambiguate-nonbundle.scala
+pos/package-ob-case
+pos/t1786-counter.scala
+pos/reflection-compat-api-universe.scala
+pos/list-optim-check.scala
+pos/existential-java-case-class
+pos/t1786-cycle.scala
+pos/reflection-compat-c.scala
+pos/t3452f.scala
+pos/reflection-compat-ru.scala
+pos/t2066-2.10-compat.scala
+pos/reflection-compat-macro-universe.scala
+pos/t5900a.scala
+pos/t5760-pkgobj-warn
+pos/t5954a
+pos/t5954b
+pos/t5954d
+pos/t6260.scala
+pos/t5165b
+pos/t5954c
+pos/t6260b.scala
+pos/t7475b.scala
+pos/t7475a.scala
+pos/t7753.scala
+pos/t7322.scala
+pos/t6948.scala
+pos/t7475d.scala
+pos/t7475e.scala
+pos/t6169
+pos/t7788.scala
+pos/t7919.scala
+pos/t8177a.scala
+pos/t8177.scala
+pos/t8170.scala
+pos/t8170b.scala
+pos/t8177d.scala
+pos/t8177b.scala
+pos/t8177e.scala
+pos/t8134
+pos/t8177h.scala
+pos/t8177g.scala
+pos/t8207.scala
+pos/t8187.scala
+pos/t8219.scala
+pos/t8219b.scala
+pos/t8224.scala
+pos/t8237.scala
+pos/t8223.scala
+pos/t8237b.scala
+pos/t8300-conversions-a.scala
+pos/t8300-conversions-b.scala
+pos/t8209a
+pos/t8209b
+pos/t8244d
+pos/t8300-overloading.scala
+pos/t8300-patmat-a.scala
+pos/t8300-patmat-b.scala
+pos/t8315b.scala
+pos/t8306.scala
+pos/t8301.scala
+pos/t8324.scala
+pos/t8315.scala
+pos/t8301b.scala
+pos/t8363.scala
+pos/t8367.scala
+pos/t8369a.scala
+pos/t8369b.scala
+pos/t8403.scala
+pos/t8364.scala
+pos/t8352
+pos/t8376
+neg/macro-bundle-nonpublic-c.scala
+neg/literate_existentials.scala
+neg/macro-bundle-nonpublic-impl.scala
+neg/macro-bundle-ambiguous.scala
+neg/macro-bundle-priority-bundle.scala
+neg/macro-bundle-need-qualifier.scala
+neg/macro-bundle-nonstatic.scala
+neg/macro-bundle-polymorphic.scala
+neg/macro-bundle-priority-nonbundle.scala
+neg/macro-bundle-wrongcontext-a.scala
+neg/macro-bundle-wrongcontext-b.scala
+run/t8425
+run/t8245.scala
+run/t8266-octal-interp.scala
+run/t8280.scala
+run/t8395.scala
+run/t8321
+run/t8153.scala
+run/t8233-bcode.scala
+run/t8197.scala
+run/t8197b.scala
+run/t8233.scala
+run/t8133
+run/t8133b
+run/t7475b.scala
+run/t7445.scala
+run/t6814
+run/t4577.scala
+run/t5134.scala
+run/t3452f.scala
+run/t3452h.scala
+run/t3452c.scala
+run/t3452.scala
+run/t261.scala
+run/t3235-minimal.scala
+run/t1503_future.scala
+run/t5565.scala
+pos/t8411
+pos/t8460.scala
+run/t8428.scala
+run/t8437
+run/absoverride.scala
+run/arrays.scala
+run/duration-coarsest.scala
+run/iterator-from.scala
+run/SymbolsTest.scala
+run/t1074.scala
+run/t1505.scala
+run/streams.scala
+run/t2111.scala
+run/t4601.scala
+pos/SI-4012-a.scala
+pos/SI-7638.scala
+neg/t3692-new.scala
+run/t7015.scala
+run/t7992b.scala
+run/t7992.scala
+run/t8570.scala
+pos/t8157-2.10.scala
+pos/t8325.scala
+pos/t8523.scala
+pos/t8578.scala
+pos/t8329.scala
+pos/t8497
+pos/t8546.scala
+pos/t8531
+neg/t8325-c.scala
+neg/t8325-b.scala
+neg/t8325.scala
+neg/t6988.scala
+neg/t8463.scala
+neg/t8450.scala
+neg/t8430.scala
+run/finally.scala
+run/bugs.scala
+run/t1503.scala
+run/t4148.scala
+run/t7763.scala
+run/issue192.scala
+
+# Adapt checkfiles for (1.0).toString == "1"
+run/Course-2002-01.scala
+run/t0421-new.scala
+run/runtime.scala
+run/t0421-old.scala
+run/spec-self.scala
+run/t5552.scala
+run/Course-2002-02.scala
+run/Course-2002-04.scala
+run/promotion.scala
+run/t4617.scala
+run/Course-2002-09.scala
+run/t5866.scala
+run/try-catch-unify.scala
+run/impconvtimes.scala
+run/Course-2002-10.scala
+run/Course-2002-08.scala
+run/t6318_primitives.scala
+
+# Adapt checkfiles for ().toString == "undefined"
+run/t5680.scala
+run/dynamic-anyval.scala
+run/macro-bundle-toplevel
+run/macro-bundle-whitebox-decl
+run/t6662
+run/t8570a.scala
+run/t3702.scala
+run/t7657
+run/macro-bundle-static
+run/structural.scala
+
+# Adapt checkfiles for print & flush (which we cannot 100% emulate)
+run/imports.scala
+run/misc.scala
+
+# Adapt checkfiles for compiler phase list
+run/t6102.scala
+neg/t7494-no-options
+
+# Adapt checkfiles for different behavior with boxed types
+run/t5568.scala
+run/virtpatmat_typetag.scala
+run/virtpatmat_switch.scala
+run/t5629b.scala
+
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/neg/t7494-no-options.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/neg/t7494-no-options.check
new file mode 100644
index 0000000..581da38
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/neg/t7494-no-options.check
@@ -0,0 +1,42 @@
+error: Error: ploogin takes no options
+ phase name id description
+ ---------- -- -----------
+ parser 1 parse source into ASTs, perform simple desugaring
+ namer 2 resolve names, attach symbols to named trees
+packageobjects 3 load package objects
+ typer 4 the meat and potatoes: type the trees
+ jsinterop 5
+ patmat 6 translate match expressions
+superaccessors 7 add super accessors in traits and nested classes
+ extmethods 8 add extension methods for inline classes
+ pickler 9 serialize symbol tables
+ refchecks 10 reference/override checking, translate nested objects
+ uncurry 11 uncurry, translate function values to anonymous classes
+ tailcalls 12 replace tail calls by jumps
+ specialize 13 @specialized-driven class and method specialization
+ explicitouter 14 this refs to outer pointers
+ erasure 15 erase types, add interfaces for traits
+ posterasure 16 clean up erased inline classes
+ lazyvals 17 allocate bitmaps, translate lazy vals into lazified defs
+ lambdalift 18 move nested functions to top level
+ constructors 19 move field definitions into constructors
+ flatten 20 eliminate inner classes
+ mixin 21 mixin composition
+ jscode 22
+ cleanup 23 platform-specific cleanups, generate reflective calls
+ delambdafy 24 remove lambdas
+ icode 25 generate portable intermediate code
+#partest !-optimise
+ jvm 26 generate JVM bytecode
+ ploogin 27 A sample phase that does so many things it's kind of hard...
+ terminal 28 the last phase during a compilation run
+#partest -optimise
+ inliner 26 optimization: do inlining
+inlinehandlers 27 optimization: inline exception handlers
+ closelim 28 optimization: eliminate uncalled closures
+ constopt 29 optimization: optimize null and other constants
+ dce 30 optimization: eliminate dead code
+ jvm 31 generate JVM bytecode
+ ploogin 32 A sample phase that does so many things it's kind of hard...
+ terminal 33 the last phase during a compilation run
+#partest
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-01.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-01.check
new file mode 100644
index 0000000..fcda943
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-01.check
@@ -0,0 +1,37 @@
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-02.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-02.check
new file mode 100644
index 0000000..ab75cfd
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-02.check
@@ -0,0 +1,187 @@
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-04.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-04.check
new file mode 100644
index 0000000..fc6ad96
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-04.check
@@ -0,0 +1,64 @@
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-08.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-08.check
new file mode 100644
index 0000000..0585d5b
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-08.check
@@ -0,0 +1,171 @@
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-09.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-09.check
new file mode 100644
index 0000000..c921361
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-09.check
@@ -0,0 +1,50 @@
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-10.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-10.check
new file mode 100644
index 0000000..847f0fa
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-10.check
@@ -0,0 +1,46 @@
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/bugs.sem b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/bugs.sem
new file mode 100644
index 0000000..d36898b
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/bugs.sem
@@ -0,0 +1 @@
+asInstanceOfs
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/dynamic-anyval.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/dynamic-anyval.check
new file mode 100644
index 0000000..c125372
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/dynamic-anyval.check
@@ -0,0 +1,4 @@
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/impconvtimes.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/impconvtimes.check
new file mode 100644
index 0000000..082377e
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/impconvtimes.check
@@ -0,0 +1 @@
+3.0 * Hour = Measure(3,Hour)
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/imports.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/imports.check
new file mode 100644
index 0000000..1aad598
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/imports.check
@@ -0,0 +1,21 @@
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/issue192.sem b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/issue192.sem
new file mode 100644
index 0000000..10abbf7
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/issue192.sem
@@ -0,0 +1 @@
+strictFloats
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/macro-bundle-static.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/macro-bundle-static.check
new file mode 100644
index 0000000..e2e7628
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/macro-bundle-static.check
@@ -0,0 +1,6 @@
+undefined
+Int
+undefined
+true
+IntInt
+true
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/macro-bundle-toplevel.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/macro-bundle-toplevel.check
new file mode 100644
index 0000000..e2e7628
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/macro-bundle-toplevel.check
@@ -0,0 +1,6 @@
+undefined
+Int
+undefined
+true
+IntInt
+true
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/macro-bundle-whitebox-decl.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/macro-bundle-whitebox-decl.check
new file mode 100644
index 0000000..e2e7628
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/macro-bundle-whitebox-decl.check
@@ -0,0 +1,6 @@
+undefined
+Int
+undefined
+true
+IntInt
+true
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/misc.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/misc.check
new file mode 100644
index 0000000..6043817
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/misc.check
@@ -0,0 +1,62 @@
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/promotion.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/promotion.check
new file mode 100644
index 0000000..41e36c3
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/promotion.check
@@ -0,0 +1,4 @@
+2
+6
+20
+30
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/runtime.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/runtime.check
new file mode 100644
index 0000000..0450b94
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/runtime.check
@@ -0,0 +1,70 @@
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/spec-self.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/spec-self.check
new file mode 100644
index 0000000..fd3c81a
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/spec-self.check
@@ -0,0 +1,2 @@
+5
+5
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/structural.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/structural.check
new file mode 100644
index 0000000..2fec112
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/structural.check
@@ -0,0 +1,37 @@
+ 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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t0421-new.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t0421-new.check
new file mode 100644
index 0000000..00d29b7
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t0421-new.check
@@ -0,0 +1,3 @@
+[Array(0, 1),Array(2, 3),Array(4, 5)]
+[Array(31)]
+[Array(24, 32)]
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t0421-old.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t0421-old.check
new file mode 100644
index 0000000..00d29b7
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t0421-old.check
@@ -0,0 +1,3 @@
+[Array(0, 1),Array(2, 3),Array(4, 5)]
+[Array(31)]
+[Array(24, 32)]
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t1503.sem b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t1503.sem
new file mode 100644
index 0000000..d36898b
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t1503.sem
@@ -0,0 +1 @@
+asInstanceOfs
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t3702.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t3702.check
new file mode 100644
index 0000000..3fce987
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t3702.check
@@ -0,0 +1,2 @@
+undefined
+6
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t4148.sem b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t4148.sem
new file mode 100644
index 0000000..d36898b
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t4148.sem
@@ -0,0 +1 @@
+asInstanceOfs
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t4617.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t4617.check
new file mode 100644
index 0000000..a6790f1
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t4617.check
@@ -0,0 +1 @@
+Str 8
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t5552.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t5552.check
new file mode 100644
index 0000000..4704611
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t5552.check
@@ -0,0 +1,2 @@
+(3,3)
+(3,3)
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t5568.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t5568.check
new file mode 100644
index 0000000..6f30cc5
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t5568.check
@@ -0,0 +1,9 @@
+void
+int
+class scala.runtime.BoxedUnit
+class scala.runtime.BoxedUnit
+class java.lang.Byte
+class java.lang.Byte
+5
+5
+5
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t5629b.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t5629b.check
new file mode 100644
index 0000000..c65298a
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t5629b.check
@@ -0,0 +1,10 @@
+=== 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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t5680.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t5680.check
new file mode 100644
index 0000000..a3b8b64
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t5680.check
@@ -0,0 +1,3 @@
+[Lscala.runtime.BoxedUnit
+undefined
+undefined
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t5866.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t5866.check
new file mode 100644
index 0000000..64df1ce
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t5866.check
@@ -0,0 +1,2 @@
+0
+Foo(0)
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t6102.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t6102.check
new file mode 100644
index 0000000..120082e
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t6102.check
@@ -0,0 +1,27 @@
+[running phase parser 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 uncurry on t6102.scala]
+[running phase tailcalls on t6102.scala]
+[running phase specialize 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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t6318_primitives.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t6318_primitives.check
new file mode 100644
index 0000000..08decef
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t6318_primitives.check
@@ -0,0 +1,36 @@
+true
+Some(1)
+false
+None
+true
+Some(1)
+false
+None
+true
+Some()
+false
+None
+true
+Some(1)
+false
+None
+true
+Some(1)
+false
+None
+true
+Some(1)
+false
+None
+true
+Some(1)
+false
+None
+true
+Some(true)
+false
+None
+true
+Some(undefined)
+false
+None
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t6662.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t6662.check
new file mode 100644
index 0000000..417b7b5
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t6662.check
@@ -0,0 +1 @@
+undefined
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t7657.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t7657.check
new file mode 100644
index 0000000..1a87c1e
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t7657.check
@@ -0,0 +1,3 @@
+undefined
+undefined
+undefined
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t7763.sem b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t7763.sem
new file mode 100644
index 0000000..d36898b
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t7763.sem
@@ -0,0 +1 @@
+asInstanceOfs
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t8570a.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t8570a.check
new file mode 100644
index 0000000..417b7b5
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t8570a.check
@@ -0,0 +1 @@
+undefined
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/try-catch-unify.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/try-catch-unify.check
new file mode 100644
index 0000000..813f011
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/try-catch-unify.check
@@ -0,0 +1,4 @@
+Failure(java.lang.NumberFormatException: For input string: "Hi")
+Success(5)
+O NOES
+Failure(java.lang.NumberFormatException: For input string: "Hi")
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/virtpatmat_switch.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/virtpatmat_switch.check
new file mode 100644
index 0000000..0900a9c
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/virtpatmat_switch.check
@@ -0,0 +1,7 @@
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/virtpatmat_typetag.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/virtpatmat_typetag.check
new file mode 100644
index 0000000..e95c3d0
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/virtpatmat_typetag.check
@@ -0,0 +1,10 @@
+1 is not a Int; it's a class java.lang.Byte
+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 not a Int; it's a class java.lang.Byte
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/BlacklistedTests.txt b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/BlacklistedTests.txt
new file mode 100644
index 0000000..e04dabe
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/BlacklistedTests.txt
@@ -0,0 +1,914 @@
+#
+# POS
+#
+
+# Using Jsoup, what's that?
+pos/cycle-jsoup.scala
+
+# Using scala.actors
+pos/t533.scala
+pos/functions.scala
+pos/MailBox.scala
+
+#
+# NEG
+#
+
+# Using the compiler API
+
+neg/t6446-additional
+neg/t6446-list
+neg/t6446-missing
+neg/t6446-show-phases.scala
+
+# 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
+
+# Uses .java files
+neg/t6289
+
+#
+# RUN
+#
+
+# Relies on the exact toString() representation of Floats/Doubles
+run/t2378.scala
+
+# Relies on !box(+0.0).equals(box(-0.0))
+run/number-parsing.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/t0412.scala
+run/t3518.scala
+run/t6198.scala
+run/t5018.scala
+run/t2417.scala
+run/t6197.scala
+run/t4813.scala
+run/lazy-concurrent.scala
+run/t5880.scala
+run/mapConserve.scala
+run/t3667.scala
+run/bigDecimalTest.scala
+run/t3038d.scala
+run/numbereq.scala
+run/shutdownhooks.scala
+run/t4658.scala
+run/t4201.scala
+run/t5590.scala
+run/deeps.scala
+run/t3895b.scala
+run/t2813.2.scala
+run/t5974.scala
+run/hashset.scala
+run/t5262.scala
+run/t5293.scala
+run/t5293-map.scala
+run/serialize-stream.scala
+run/sysprops.scala
+
+run/colltest.scala
+run/equality.scala
+run/t2849.scala
+run/t1360.scala
+run/t6114.scala
+run/t7269.scala
+run/t3199b.scala
+run/t8690.scala
+
+# Uses java.util.Collections
+run/java-erasure.scala
+run/t2250.scala
+
+# Uses java.math.BigDecimal / BigInteger
+run/bigDecimalCache.scala
+run/hashhash.scala
+run/is-valid-num.scala
+run/range.scala
+run/stringinterpolation_macro-run.scala
+run/t5356.scala
+run/t6064.scala
+
+# Documented semantic difference on numbers (float precision)
+run/interpolation.scala
+run/interpolationMultiline1.scala
+
+# Documented semantic difference on String.split(x: Array[Char])
+run/t0325.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
+
+# 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
+
+# Crashes Rhino
+
+run/bridges.scala
+run/patmat-exprs.scala
+
+# Using partest properties
+
+run/tailcalls.scala
+run/t4294.scala
+run/t6331b.scala
+
+# Using IO
+
+run/Predef.readLine.scala
+run/t6488.scala
+run/t6988.scala
+
+# Object{Output|Input}Streams
+run/t6935.scala
+run/t8188.scala
+
+# Using System.getProperties
+
+run/t4426.scala
+
+# Using ExecutionContext.global
+run/t7336.scala
+run/t7775.scala
+run/future-flatmap-exec-count.scala
+
+# Using reflection
+
+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/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/reflection-mem-typecheck.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/t6220.scala
+run/t6240-universe-code-gen.scala
+run/t6221
+run/t6260b.scala
+run/t6259.scala
+run/t6287.scala
+run/t6261.scala
+run/t6308.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_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/t8346.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
+
+# 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
+
+# Expecting some particular value of hashCode()
+
+run/MeterCaseClass.scala
+run/t5608.scala
+run/caseClassHash.scala
+run/Meter.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/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
+
+# Using Scala Script (partest.ScriptTest)
+
+run/t7711-script-args.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
+run/t7933.scala
+run/t7843-jsr223-service.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
+
+# partest.BytecodeTest
+run/t6546
+run/t7106
+run/t7974
+run/t8601-closure-elim.scala
+
+# partest.JavapTest
+run/t8608-no-format.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
+
+# 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
+
+### Incorrect partests ###
+# Badly uses constract of Console.print (no flush)
+run/t429.scala
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/BuglistedTests.txt b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/BuglistedTests.txt
new file mode 100644
index 0000000..42c6146
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/BuglistedTests.txt
@@ -0,0 +1,4 @@
+# The tests in this file should pass but have never passed so far
+# use scala.tools.partest.scalajs.testunknownonly to only run tests
+# which are neither in BuglistedTests.txt, WhitelistedTests.txt or
+# BlacklistedTests.txt
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/NoDCEWarn.txt b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/NoDCEWarn.txt
new file mode 100644
index 0000000..cc5aff0
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/NoDCEWarn.txt
@@ -0,0 +1,8 @@
+Ljava_math_MathContext$
+Ljava_math_BigDecimal$
+Ljava_math_BigDecimal
+Ljava_math_BigInteger$
+jl_Class$
+jl_Class.getClassLoader__jl_ClassLoader
+jl_Class.getPackage__jl_Package
+jl_Class.getInterfaces__Ajl_Class
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/WhitelistedTests.txt b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/WhitelistedTests.txt
new file mode 100644
index 0000000..5538de1
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/WhitelistedTests.txt
@@ -0,0 +1,2976 @@
+pos/spec-super.scala
+pos/t1035.scala
+pos/t5897.scala
+pos/irrefutable.scala
+pos/spec-partialmap.scala
+pos/tcpoly_seq.scala
+pos/partialfun.scala
+pos/t2795-new.scala
+pos/clsrefine.scala
+pos/t0774
+pos/t1070.scala
+pos/t5957
+pos/looping-jsig.scala
+pos/t3274.scala
+pos/spec-fields-old.scala
+pos/t262.scala
+pos/t7486.scala
+pos/t2261.scala
+pos/t6600.scala
+pos/t4786.scala
+pos/t5406.scala
+pos/tcpoly_late_method_params.scala
+pos/t2726
+pos/pos-bug1210.scala
+pos/t3312.scala
+pos/manifest1-old.scala
+pos/gadt-gilles.scala
+pos/t4842.scala
+pos/ted.scala
+pos/NoCyclicReference.scala
+pos/t3568.scala
+pos/t0030.scala
+pos/t2635.scala
+pos/t7232b
+pos/t0017.scala
+pos/t812.scala
+pos/t2179.scala
+pos/t651.scala
+pos/spurious-overload.scala
+pos/t758.scala
+pos/t4760.scala
+pos/t1672.scala
+pos/mixins.scala
+pos/patterns.scala
+pos/t1260.scala
+pos/t6551.scala
+pos/t2060.scala
+pos/t6575a.scala
+pos/t1318.scala
+pos/t4266.scala
+pos/t0695
+pos/protected-static
+pos/t5738.scala
+pos/t1226.scala
+pos/t5013
+pos/t6215.scala
+pos/t5692b
+pos/traits.scala
+pos/t2994a.scala
+pos/t3371.scala
+pos/t613.scala
+pos/t6499.scala
+pos/xlint1.scala
+pos/t1150
+pos/sealed-final.scala
+pos/test4a.scala
+pos/t2664.scala
+pos/t3528.scala
+pos/t3174.scala
+pos/t6994.scala
+pos/t4812.scala
+pos/t5777.scala
+pos/t5223.scala
+pos/t439.scala
+pos/t3079.scala
+pos/t5829.scala
+pos/t0036.scala
+pos/scoping2.scala
+pos/t4717.scala
+pos/t4257.scala
+pos/t1210a.scala
+pos/getClassType.scala
+pos/t5330.scala
+pos/t4524.scala
+pos/t2945.scala
+pos/t6562.scala
+pos/t0273.scala
+pos/override-object-yes.scala
+pos/t7426.scala
+pos/t6601
+pos/t3076
+pos/seq-ordering.scala
+pos/spec-groups.scala
+pos/t296.scala
+pos/t5545
+pos/spec-multiplectors.scala
+pos/t1789.scala
+pos/t2569
+pos/ksbug1.scala
+pos/t0599.scala
+pos/local-objects.scala
+pos/t0081.scala
+pos/t5756.scala
+pos/t7126.scala
+pos/t7716.scala
+pos/t2797.scala
+pos/t5399.scala
+pos/t1101
+pos/t767.scala
+pos/contrib467.scala
+pos/t7532b
+pos/self-type-override.scala
+pos/t4853.scala
+pos/t839.scala
+pos/t5644
+pos/t5853.scala
+pos/t5178.scala
+pos/unapplyNeedsMemberType.scala
+pos/t5390.scala
+pos/t6575b.scala
+pos/t151.scala
+pos/t2665.scala
+pos/t5120.scala
+pos/erasure-nsquared.scala
+pos/arrays3.scala
+pos/t3136.scala
+pos/inline-access-levels
+pos/t3972.scala
+pos/t2591.scala
+pos/t3486
+pos/variances-flip.scala
+pos/annotated-original
+pos/typesafecons.scala
+pos/stable.scala
+pos/t1996.scala
+pos/t3037.scala
+pos/t1711
+pos/t3374.scala
+pos/t0029.scala
+pos/t3278.scala
+pos/matthias3.scala
+pos/t5546.scala
+pos/t4020.scala
+pos/matthias4.scala
+pos/value-class-override-spec.scala
+pos/arrays2.scala
+pos/t5119.scala
+pos/t2613.scala
+pos/t4070b.scala
+pos/virtpatmat_exist_uncurry.scala
+pos/modules1.scala
+pos/spec-constr-new.scala
+pos/t6335.scala
+pos/t675.scala
+pos/t0644.scala
+pos/t5892.scala
+pos/t360.scala
+pos/override.scala
+pos/t1798.scala
+pos/strip-tvars-for-lubbasetypes.scala
+pos/hk-infer.scala
+pos/t2119.scala
+pos/t0231.scala
+pos/t1459
+pos/t1381-new.scala
+pos/t2610.scala
+pos/t2708.scala
+pos/t5604b
+pos/t3951
+pos/t361.scala
+pos/t319.scala
+pos/largecasetest.scala
+pos/switchUnbox.scala
+pos/typetags.scala
+pos/java-access-pos
+pos/t803.scala
+pos/t3898.scala
+pos/t5692a
+pos/t2421.scala
+pos/t1102
+pos/t0654.scala
+pos/exhaust_alternatives.scala
+pos/t807.scala
+pos/t5702-pos-infix-star.scala
+pos/t1186
+pos/t1439.scala
+pos/t7427.scala
+pos/virtpatmat_binding_opt.scala
+pos/t247.scala
+pos/abstract.scala
+pos/gen-traversable-methods.scala
+pos/t2795-old.scala
+pos/t5639
+pos/t2667.scala
+pos/t2405.scala
+pos/t1438.scala
+pos/SI-7100.scala
+pos/t1659.scala
+pos/unchecked-a.scala
+pos/t3636.scala
+pos/t6745.scala
+pos/t2809.scala
+pos/t7022.scala
+pos/t6447.scala
+pos/t6367.scala
+pos/t5846.scala
+pos/lubs.scala
+pos/t1987a.scala
+pos/spec-arrays.scala
+pos/virtpatmat_anonfun_for.scala
+pos/listpattern.scala
+pos/t5742.scala
+pos/test5refine.scala
+pos/switch-small.scala
+pos/t5604
+pos/return_thistype.scala
+pos/t348plus.scala
+pos/t3420.scala
+pos/t3440.scala
+pos/maxim1.scala
+pos/caseClassInMethod.scala
+pos/t7239.scala
+pos/t3833.scala
+pos/t6675.scala
+pos/t4402
+pos/t5953.scala
+pos/t1152
+pos/t0591.scala
+pos/t210.scala
+pos/t7035.scala
+pos/t5769.scala
+pos/pmbug.scala
+pos/t2331.scala
+pos/t5240.scala
+pos/t304.scala
+pos/annotated-treecopy
+pos/t2081.scala
+pos/t0904.scala
+pos/t7649.scala
+pos/t3498-new.scala
+pos/contrib701.scala
+pos/t6624.scala
+pos/t3924.scala
+pos/t374.scala
+pos/t1642
+pos/t1591_pos.scala
+pos/depmet_implicit_oopsla_session_2.scala
+pos/t5899.scala
+pos/thistype.scala
+pos/t4176b.scala
+pos/elidable-tparams.scala
+pos/lambdalift.scala
+pos/nothing_manifest_disambig-old.scala
+pos/t372.scala
+pos/t5399a.scala
+pos/t2782.scala
+pos/patmat-extract-tparam.scala
+pos/t4114.scala
+pos/unapplyVal.scala
+pos/t2486.scala
+pos/t5877b.scala
+pos/t0625.scala
+pos/t6358_2.scala
+pos/viewtest1.scala
+pos/t1237.scala
+pos/scala-singleton.scala
+pos/t1254
+pos/t5504
+pos/bounds.scala
+pos/t3631.scala
+pos/t3177.scala
+pos/unapplyContexts2.scala
+pos/t0438.scala
+pos/t1642b.scala
+pos/inferbroadtype.scala
+pos/t1858.scala
+pos/t3731.scala
+pos/t6963c.scala
+pos/classtag-pos.scala
+pos/t6221.scala
+pos/t3343.scala
+pos/spec-asseenfrom.scala
+pos/t604.scala
+pos/spec-example1.scala
+pos/t0786.scala
+pos/annot-inner.scala
+pos/t5886.scala
+pos/t1056.scala
+pos/t294
+pos/spec-Function1.scala
+pos/t1836
+pos/spec-private.scala
+pos/depmet_implicit_tpbetareduce.scala
+pos/exhaust_2.scala
+pos/t7532
+pos/t5175.scala
+pos/t802.scala
+pos/t5809.scala
+pos/tcpoly_typesub.scala
+pos/t6029.scala
+pos/contextbounds-implicits-new.scala
+pos/t3480.scala
+pos/patterns3.scala
+pos/caseaccs.scala
+pos/spec-sparsearray-old.scala
+pos/patterns1213.scala
+pos/spec-traits.scala
+pos/t0020.scala
+pos/cycle
+pos/t5968.scala
+pos/typealiases.scala
+pos/init.scala
+pos/t697.scala
+pos/t2693.scala
+pos/t2377
+pos/unapplyGeneric.scala
+pos/t1385.scala
+pos/t3363-old.scala
+pos/t1236.scala
+pos/t0068.scala
+pos/t4052.scala
+pos/lambdalift1.scala
+pos/z1730.scala
+pos/variances-local.scala
+pos/virtpatmat_gadt_array.scala
+pos/t2421_delitedsl.scala
+pos/t5626.scala
+pos/t690.scala
+pos/t711.scala
+pos/t6547.scala
+pos/t1937
+pos/t3999
+pos/SI-7060.scala
+pos/t2305.scala
+pos/t2168.scala
+pos/t2660.scala
+pos/t1693.scala
+pos/inliner2.scala
+pos/t2799.scala
+pos/t6966.scala
+pos/t1001.scala
+pos/S5.scala
+pos/t0301.scala
+pos/t1048.scala
+pos/t415.scala
+pos/t6386.scala
+pos/t2187.scala
+pos/hashhash-overloads.scala
+pos/t6921.scala
+pos/t0227.scala
+pos/t6556.scala
+pos/t3946
+pos/t1053.scala
+pos/t1000.scala
+pos/t0586.scala
+pos/t7011.scala
+pos/t7329.scala
+pos/t4975.scala
+pos/t1131.scala
+pos/t1027.scala
+pos/t2913.scala
+pos/t3494.scala
+pos/t5606.scala
+pos/t4716.scala
+pos/tcpoly_gm.scala
+pos/t4859.scala
+pos/t514.scala
+pos/lexical.scala
+pos/t2624.scala
+pos/t4036.scala
+pos/t2741
+pos/t703.scala
+pos/five-dot-f.scala
+pos/t805.scala
+pos/strings.scala
+pos/t2433
+pos/t6925.scala
+pos/t1085.scala
+pos/t7461
+pos/t1942
+pos/spec-lists.scala
+pos/t3349
+pos/tcpoly_infer_ticket474.scala
+pos/t1614
+pos/virtpatmat_reach_const.scala
+pos/t2194.scala
+pos/t6976
+pos/t1560.scala
+pos/t6891.scala
+pos/t3883.scala
+pos/infersingle.scala
+pos/gui.scala
+pos/t1164.scala
+pos/t3175-pos.scala
+pos/t4336.scala
+pos/annotations2.scala
+pos/proj-rec-test.scala
+pos/t2973.scala
+pos/t1123.scala
+pos/t6205.scala
+pos/t5727.scala
+pos/t6537.scala
+pos/t6712.scala
+pos/t3866.scala
+pos/t4831.scala
+pos/selftails.scala
+pos/t397.scala
+pos/spec-vector.scala
+pos/t7233b.scala
+pos/t1391.scala
+pos/spec.scala
+pos/t3106.scala
+pos/contextbounds-implicits-old.scala
+pos/packageobjs.scala
+pos/michel3.scala
+pos/t628.scala
+pos/collections.scala
+pos/tcpoly_boundedmonad.scala
+pos/t7668.scala
+pos/t0032.scala
+pos/t0069.scala
+pos/t4345.scala
+pos/t3521
+pos/t3071.scala
+pos/tcpoly_infer_easy.scala
+pos/t289.scala
+pos/t4365
+pos/rangepos-anonapply.scala
+pos/t5033.scala
+pos/lambda.scala
+pos/S8.scala
+pos/t6014.scala
+pos/t1785.scala
+pos/t6034.scala
+pos/t7433.scala
+pos/imp2-pos.scala
+pos/t0504.scala
+pos/t1272.scala
+pos/t0612
+pos/value-class-override-no-spec.scala
+pos/overloaded-unapply.scala
+pos/t5859.scala
+pos/chang
+pos/localmodules.scala
+pos/t4237.scala
+pos/rangepos-patmat.scala
+pos/t1974.scala
+pos/t0054.scala
+pos/michel2.scala
+pos/t0770.scala
+pos/t1146.scala
+pos/t2441pos.scala
+pos/t5099.scala
+pos/tcpoly_seq_typealias.scala
+pos/t946.scala
+pos/tcpoly_infer_ticket1864.scala
+pos/t4579.scala
+pos/t4737
+pos/t7377b.scala
+pos/t616.scala
+pos/t201.scala
+pos/t6355pos.scala
+pos/escapes2.scala
+pos/t1675.scala
+pos/t3890.scala
+pos/t6040.scala
+pos/spec-tailcall.scala
+pos/existentials.scala
+pos/t5317.scala
+pos/t7782b.scala
+pos/t4758.scala
+pos/t7296.scala
+pos/t6896.scala
+pos/cls1.scala
+pos/t402.scala
+pos/gosh.scala
+pos/t2619.scala
+pos/javaConversions-2.10-regression.scala
+pos/t759.scala
+pos/t5259.scala
+pos/t5130.scala
+pos/t5156.scala
+pos/t0905.scala
+pos/package-implicit
+pos/t2669.scala
+pos/trait-parents.scala
+pos/virtpatmat_exhaust.scala
+pos/patterns1.scala
+pos/t7014
+pos/t1231
+pos/t1751
+pos/t7233.scala
+pos/t6022.scala
+pos/tcpoly_checkkinds_mix.scala
+pos/depmet_implicit_norm_ret.scala
+pos/package-case.scala
+pos/philippe4.scala
+pos/michel6.scala
+pos/t4188.scala
+pos/t3936
+pos/t1280.scala
+pos/t6722.scala
+pos/t796.scala
+pos/t5542.scala
+pos/t3927.scala
+pos/t2293.scala
+pos/t3800.scala
+pos/t7285a.scala
+pos/t927.scala
+pos/t4494.scala
+pos/t3864
+pos/ilya2
+pos/t2940
+pos/S1.scala
+pos/tcpoly_wildcards.scala
+pos/tryexpr.scala
+pos/t6089b.scala
+pos/depmet_implicit_oopsla_zipwith.scala
+pos/t245.scala
+pos/t6146.scala
+pos/t1782
+pos/t851.scala
+pos/spec-thistype.scala
+pos/tcpoly_poly.scala
+pos/t6815_import.scala
+pos/t4649.scala
+pos/t0453.scala
+pos/t5020.scala
+pos/ilya
+pos/t2435.scala
+pos/t1279a.scala
+pos/t2171.scala
+pos/t1957.scala
+pos/gadts2.scala
+pos/t3567
+pos/Z.scala
+pos/t1203b
+pos/nested2.scala
+pos/t1896
+pos/viewtest2.scala
+pos/t5541.scala
+pos/existentials-harmful.scala
+pos/t4063.scala
+pos/t6485a
+pos/t1208.scala
+pos/t5041.scala
+pos/unapplyComplex.scala
+pos/t3384.scala
+pos/t4112.scala
+pos/t788.scala
+pos/hklub0.scala
+pos/t757.scala
+pos/t1197
+pos/t359.scala
+pos/t5667.scala
+pos/t1107a.scala
+pos/virtpatmat_castbinder.scala
+pos/t267.scala
+pos/t3419
+pos/t3861.scala
+pos/t6797.scala
+pos/spec-localdefs.scala
+pos/t3404
+pos/t4457_1.scala
+pos/matthias5.scala
+pos/spec-polymeth.scala
+pos/kinds.scala
+pos/t2310.scala
+pos/t6552.scala
+pos/valdefs.scala
+pos/hkarray.scala
+pos/homonym.scala
+pos/t1235
+pos/t3429
+pos/t0053.scala
+pos/depmet_implicit_chaining_zw.scala
+pos/virtpatmat_partialfun_nsdnho.scala
+pos/t6664.scala
+pos/ticket2251.scala
+pos/t3495.scala
+pos/super
+pos/t121.scala
+pos/javaConversions-2.10-ambiguity.scala
+pos/t1803.scala
+pos/t5877.scala
+pos/t0085.scala
+pos/t3582.scala
+pos/t2939.scala
+pos/t1422_pos.scala
+pos/manifest1-new.scala
+pos/t7505.scala
+pos/t5720-ownerous.scala
+pos/misc-unapply_pos.scala
+pos/tcpoly_variance_pos.scala
+pos/t5127.scala
+pos/t6123-explaintypes-implicits.scala
+pos/t2764
+pos/presuperContext.scala
+pos/spec-simple.scala
+pos/t3120
+pos/t5729.scala
+pos/tcpoly_infer_ticket716.scala
+pos/tcpoly_bounds1.scala
+pos/t7369.scala
+pos/imports-pos.scala
+pos/t5654.scala
+pos/t0123.scala
+pos/raw-map
+pos/t5330b.scala
+pos/t6485b
+pos/t6072.scala
+pos/t5692c.scala
+pos/t3430.scala
+pos/tcpoly_param_scoping.scala
+pos/t6204-b.scala
+pos/attachments-typed-another-ident
+pos/t5359.scala
+pos/ticket2197.scala
+pos/t720.scala
+pos/t2130-2.scala
+pos/t2260.scala
+pos/t0304.scala
+pos/t464.scala
+pos/spec-maps.scala
+pos/annotDepMethType.scala
+pos/t6117.scala
+pos/t911.scala
+pos/t757a.scala
+pos/t2504.scala
+pos/t1381-old.scala
+pos/t1232
+pos/needstypeearly.scala
+pos/moduletrans.scala
+pos/t4957.scala
+pos/kinzer.scala
+pos/t318.scala
+pos/widen-existential.scala
+pos/t0095.scala
+pos/t566.scala
+pos/tcpoly_overloaded.scala
+pos/t7516
+pos/t7232
+pos/t698.scala
+pos/t0002.scala
+pos/t0288
+pos/t2994b.scala
+pos/cls.scala
+pos/t3622
+pos/t3671.scala
+pos/tcpoly_subst.scala
+pos/t5703
+pos/depmet_implicit_oopsla_session_simpler.scala
+pos/t5022.scala
+pos/builders.scala
+pos/spec-foo.scala
+pos/t756.scala
+pos/t1569.scala
+pos/implicit-unwrap-tc.scala
+pos/t3688.scala
+pos/t5198.scala
+pos/t432.scala
+pos/t6022b.scala
+pos/channels.scala
+pos/t1075.scala
+pos/null.scala
+pos/t1840
+pos/t6479.scala
+pos/t6311.scala
+pos/t0039.scala
+pos/t1119.scala
+pos/t573.scala
+pos/t1136.scala
+pos/t3938
+pos/spec-sealed.scala
+pos/tcpoly_return_overriding.scala
+pos/t3582b.scala
+pos/t229.scala
+pos/t3498-old.scala
+pos/t531.scala
+pos/t4545.scala
+pos/t6651.scala
+pos/t2133.scala
+pos/tinondefcons.scala
+pos/t6157.scala
+pos/t6358.scala
+pos/t7690.scala
+pos/t5779-numeq-warn.scala
+pos/list-extractor.scala
+pos/t892.scala
+pos/t2127.scala
+pos/t7180.scala
+pos/nullary_poly.scala
+pos/virtpatmat_exist3.scala
+pos/t1176
+pos/spec-funs.scala
+pos/specialize10.scala
+pos/t6514.scala
+pos/exhaustive_heuristics.scala
+pos/t0066.scala
+pos/t460.scala
+pos/t2130-1.scala
+pos/t124.scala
+pos/annotations.scala
+pos/pat_gilles.scala
+pos/array-interfaces.scala
+pos/t6210.scala
+pos/t3792.scala
+pos/implicits-old.scala
+pos/t389.scala
+pos/t115.scala
+pos/virtpatmat_exhaust_unchecked.scala
+pos/scoping3.scala
+pos/t6033.scala
+pos/depmet_implicit_oopsla_session.scala
+pos/t602.scala
+pos/test5.scala
+pos/t611.scala
+pos/t5932.scala
+pos/t4910.scala
+pos/unapplySeq.scala
+pos/t344.scala
+pos/t3363-new.scala
+pos/t4018.scala
+pos/t4553.scala
+pos/t5082.scala
+pos/t3869.scala
+pos/t3836.scala
+pos/tcpoly_typeapp.scala
+pos/t1409
+pos/nonlocal-unchecked.scala
+pos/t0082.scala
+pos/z1720.scala
+pos/t7232c
+pos/t2018.scala
+pos/t3943
+pos/t2187-2.scala
+pos/unicode-decode.scala
+pos/t4757
+pos/t0710.scala
+pos/t0305.scala
+pos/t160.scala
+pos/t7591
+pos/simplelists.scala
+pos/List1.scala
+pos/t516.scala
+pos/t6648.scala
+pos/t5165
+pos/t0055.scala
+pos/t4744
+pos/t7377
+pos/t5726.scala
+pos/t0091.scala
+pos/t6595.scala
+pos/compile.scala
+pos/depmet_1_pos.scala
+pos/t7364
+pos/philippe3.scala
+pos/spec-doubledef-old.scala
+pos/t4651.scala
+pos/tcpoly_infer_implicit_tuple_wrapper.scala
+pos/t6274.scala
+pos/tcpoly_infer_explicit_tuple_wrapper.scala
+pos/ticket2201.scala
+pos/spec-fields-new.scala
+pos/optmatch.scala
+pos/t7517.scala
+pos/t3560.scala
+pos/t0165.scala
+pos/t0872.scala
+pos/t522.scala
+pos/t2234.scala
+pos/t5031_2.scala
+pos/tcpoly_method.scala
+pos/t6482.scala
+pos/pos-bug1241.scala
+pos/implicits-new.scala
+pos/t2484.scala
+pos/t2425.scala
+pos/t1049.scala
+pos/michel4.scala
+pos/t5958.scala
+pos/virtpatmat_instof_valuetype.scala
+pos/spec-t6286.scala
+pos/t873.scala
+pos/t3137.scala
+pos/Transactions.scala
+pos/t0064.scala
+pos/t7486-named.scala
+pos/t5444.scala
+pos/simple-exceptions.scala
+pos/t1006.scala
+pos/t7200b.scala
+pos/t3777.scala
+pos/t4840.scala
+pos/t211.scala
+pos/nullary.scala
+pos/michel1.scala
+pos/t5031_3
+pos/typealias_dubious.scala
+pos/spec-doubledef-new.scala
+pos/philippe1.scala
+pos/thistypes.scala
+pos/t3570.scala
+pos/t6516.scala
+pos/context.scala
+pos/t3808.scala
+pos/philippe2.scala
+pos/constfold.scala
+pos/t1292.scala
+pos/t1147.scala
+pos/t404.scala
+pos/t4430.scala
+pos/A.scala
+pos/spec-partially.scala
+pos/t5796.scala
+pos/t2409
+pos/t284-pos.scala
+pos/t5313.scala
+pos/t2464
+pos/t1591b.scala
+pos/hk-match
+pos/t595.scala
+pos/t6846.scala
+pos/t6162-inheritance.scala
+pos/relax_implicit_divergence.scala
+pos/patterns2.scala
+pos/t4692.scala
+pos/t3837.scala
+pos/t661.scala
+pos/t2810.scala
+pos/depexists.scala
+pos/virtpatmat_exist4.scala
+pos/t5245.scala
+pos/t7190.scala
+pos/isApplicableSafe.scala
+pos/t6204-a.scala
+pos/t0076.scala
+pos/t1756.scala
+pos/t1745
+pos/t6091.scala
+pos/t0154.scala
+pos/t530.scala
+pos/t2094.scala
+pos/t1034.scala
+pos/t6084.scala
+pos/t2454.scala
+pos/t2956
+pos/tcpoly_ticket2096.scala
+pos/attachments-typed-ident
+pos/polymorphic-case-class.scala
+pos/t252.scala
+pos/spec-constr-old.scala
+pos/t2421c.scala
+pos/t122.scala
+pos/t6574.scala
+pos/t3859.scala
+pos/spec-params-old.scala
+pos/t1196
+pos/t4593.scala
+pos/t596.scala
+pos/t615.scala
+pos/t7689.scala
+pos/t3960.scala
+pos/t3986.scala
+pos/exbound.scala
+pos/t2545.scala
+pos/t1722
+pos/t159.scala
+pos/t3272.scala
+pos/t6301.scala
+pos/t2794.scala
+pos/t3048.scala
+pos/t4970.scala
+pos/t607.scala
+pos/FPTest.scala
+pos/test1.scala
+pos/t3252.scala
+pos/t4176.scala
+pos/t112606A.scala
+pos/t2183.scala
+pos/t430-feb09.scala
+pos/t6275.scala
+pos/t1832.scala
+
+neg/volatile_no_override.scala
+neg/t800.scala
+neg/t5426.scala
+neg/t2462a.scala
+neg/t2641.scala
+neg/classtags_dont_use_typetags.scala
+neg/t5031
+neg/t2275b.scala
+neg/macro-qmarkqmarkqmark.scala
+neg/t4879.scala
+neg/t5956.scala
+neg/t4196.scala
+neg/reify_ann2b.scala
+neg/t6666b.scala
+neg/warn-unused-privates.scala
+neg/t6928.scala
+neg/t6337.scala
+neg/sealed-java-enums.scala
+neg/t563.scala
+neg/t900.scala
+neg/deadline-inf-illegal.scala
+neg/t766.scala
+neg/t5429.scala
+neg/overloaded-implicit.scala
+neg/t875.scala
+neg/abstract-class-error
+neg/unchecked2.scala
+neg/predef-masking.scala
+neg/viewtest.scala
+neg/macro-noexpand
+neg/varargs.scala
+neg/t963b.scala
+neg/t909.scala
+neg/sensitive2.scala
+neg/t5390b.scala
+neg/abstraction-from-volatile-type-error.scala
+neg/macro-exception
+neg/t4431.scala
+neg/t5689.scala
+neg/valueclasses.scala
+neg/overload.scala
+neg/t0204.scala
+neg/t908.scala
+neg/t750
+neg/patmatexhaust.scala
+neg/macro-invalidusage-badtargs
+neg/t1168.scala
+neg/t5761.scala
+neg/t0503.scala
+neg/t7235.scala
+neg/t1215.scala
+neg/primitive-sigs-1
+neg/t5578.scala
+neg/names-defaults-neg-warn.scala
+neg/t6436b.scala
+neg/t3098
+neg/t910.scala
+neg/parstar.scala
+neg/t4568.scala
+neg/newpat_unreachable.scala
+neg/warn-unused-imports.scala
+neg/t1181.scala
+neg/t5903c
+neg/t7294.scala
+neg/t4091.scala
+neg/t5452-old.scala
+neg/t5696.scala
+neg/t0209.scala
+neg/t2910.scala
+neg/t7388.scala
+neg/noMember2.scala
+neg/no-predef.scala
+neg/t6952.scala
+neg/t1909b.scala
+neg/abstract-report2.scala
+neg/t5318.scala
+neg/t6074.scala
+neg/t7171.scala
+neg/abstract-vars.scala
+neg/unchecked-impossible.scala
+neg/variances-refinement.scala
+neg/t3453.scala
+neg/t5189.scala
+neg/t4302.scala
+neg/xmltruncated7.scala
+
+run/t7249.scala
+run/t3563.scala
+run/t6111.scala
+run/classtags_multi.scala
+run/t5201.scala
+run/checked.scala
+run/valueclasses-classtag-basic.scala
+run/t7171.scala
+run/t5053.scala
+run/t4535.scala
+run/t5923d
+run/t7291.scala
+run/partialfun.scala
+run/macro-term-declared-in-package-object
+run/mapValues.scala
+run/gadts.scala
+run/t2386-new.scala
+run/virtpatmat_stringinterp.scala
+run/t657.scala
+run/t0017.scala
+run/t5713
+run/t576.scala
+run/t3580.scala
+run/virtpatmat_partial.scala
+run/t6646.scala
+run/mixins.scala
+run/t1672.scala
+run/macro-expand-implicit-macro-has-implicit
+run/tuple-match.scala
+run/t7039.scala
+run/virtpatmat_opt_sharing.scala
+run/virtpatmat_casting.scala
+run/t2176.scala
+run/eta-expand-star2.scala
+run/macro-impl-relaxed
+run/intmap.scala
+run/t751.scala
+run/t1591.scala
+run/macro-typecheck-implicitsdisabled
+run/t6911.scala
+run/t5604.scala
+run/macro-term-declared-in-default-param
+run/collection-stacks.scala
+run/multi-array.scala
+run/t4560b.scala
+run/buffer-slice.scala
+run/t5629.scala
+run/t6690.scala
+run/matchonstream.scala
+run/t3603.scala
+run/lazy-exprs.scala
+run/macro-quasiquotes
+run/Course-2002-13.scala
+run/t6337a.scala
+run/exoticnames.scala
+run/t0936.scala
+run/existentials3-old.scala
+run/runtime-richChar.scala
+run/t6272.scala
+run/t7215.scala
+run/t1939.scala
+run/ReverseSeqView.scala
+run/lazy-leaks.scala
+run/t0048.scala
+run/t3994.scala
+run/t2241.scala
+run/t627.scala
+run/t5966.scala
+run/getClassTest-valueClass.scala
+run/t3619.scala
+run/t1300.scala
+run/t2177.scala
+run/t3760.scala
+run/t1829.scala
+run/macro-expand-implicit-macro-is-view
+run/t889.scala
+run/QueueTest.scala
+run/t4537
+run/t3699.scala
+run/valueclasses-manifest-basic.scala
+run/t1192.scala
+run/macro-expand-tparams-bounds
+run/macro-expand-nullary-generic
+run/t1434.scala
+run/t6443-varargs.scala
+run/macro-term-declared-in-trait
+run/t4080.scala
+run/t2236-old.scala
+run/matcharraytail.scala
+run/infiniteloop.scala
+run/t5733.scala
+run/virtpatmat_nested_lists.scala
+run/t5158.scala
+run/t6695.scala
+run/t6070.scala
+run/t4558.scala
+run/exc2.scala
+run/patmat-behavior-2.scala
+run/overloads.scala
+run/iterator-iterate-lazy.scala
+run/t6957.scala
+run/transform.scala
+run/t5500.scala
+run/t6663.scala
+run/castsingleton.scala
+run/t4147.scala
+run/virtpatmat_staging.scala
+run/t4565_1.scala
+run/t5588.scala
+run/run-bug4840.scala
+run/t3496.scala
+run/t5867.scala
+run/search.scala
+run/t3112.scala
+run/hashsetremove.scala
+run/interop_manifests_are_classtags.scala
+run/t6443.scala
+run/macro-expand-tparams-prefix
+run/contrib674.scala
+run/t3508.scala
+run/t4300.scala
+run/virtpatmat_typed.scala
+run/macro-term-declared-in-class-object
+run/map_test.scala
+run/t5040.scala
+run/t4827b.scala
+run/lift-and-unlift.scala
+run/t6574b.scala
+run/t7240
+run/t3984.scala
+run/virtpatmat_tailcalls_verifyerror.scala
+run/macro-term-declared-in-class-class
+run/emptypf.scala
+run/t6631.scala
+run/t6104.scala
+run/t2818.scala
+run/t3761-overload-byname.scala
+run/t2526.scala
+run/phantomValueClass.scala
+run/t3126.scala
+run/arybufgrow.scala
+run/t3980.scala
+run/t7375b
+run/t6077_patmat_cse_irrefutable.scala
+run/classmanifests_new_core.scala
+run/t3395.scala
+run/name-based-patmat.scala
+run/inliner-infer.scala
+run/t5171.scala
+run/t3726.scala
+run/null-hash.scala
+run/t4027.scala
+run/t2544.scala
+run/patmatnew.scala
+run/t5923b
+run/t7242.scala
+run/classtags_core.scala
+run/streamWithFilter.scala
+run/t3038b.scala
+run/macro-expand-varargs-explicit-over-nonvarargs-good
+run/macro-divergence-spurious
+run/macro-duplicate
+run/t2958.scala
+run/patch-boundary.scala
+run/t2333.scala
+run/lazy-override-run.scala
+run/macro-quasiinvalidbody-c
+run/t5037.scala
+run/takeAndDrop.scala
+run/t6126.scala
+run/t0883.scala
+run/t7617a
+run/t4171.scala
+run/empty-array.scala
+run/t7198.scala
+run/t493.scala
+run/genericValueClass.scala
+run/t0677-old.scala
+run/t1373.scala
+run/t4461.scala
+run/t6011b.scala
+run/t7584.scala
+run/t3935.scala
+run/t6928-run.scala
+run/t744.scala
+run/t3241.scala
+run/blame_eye_triple_eee-double.scala
+run/t3829.scala
+run/t5577.scala
+run/t5914.scala
+run/t601.scala
+run/t5610.scala
+run/macro-basic-mamd-mi
+run/t6150.scala
+run/stringbuilder.scala
+run/t7290.scala
+run/t6888.scala
+run/t6327.scala
+run/virtpatmat_unapplyseq.scala
+run/t4656.scala
+run/macro-term-declared-in-method
+run/macro-expand-implicit-macro-is-implicit
+run/blame_eye_triple_eee-float.scala
+run/t4482.scala
+run/t5488.scala
+run/matchemptyarray.scala
+run/t3714.scala
+run/richWrapperEquals.scala
+run/t5328.scala
+run/stream_flatmap_odds.scala
+run/implicitclasses.scala
+run/t6827.scala
+run/t6394b
+run/complicatedmatch.scala
+run/valueclasses-classmanifest-basic.scala
+run/unreachable.scala
+run/caseclasses.scala
+run/withIndex.scala
+run/exc1.scala
+run/amp.scala
+run/t1423.scala
+run/t594.scala
+run/t6353.scala
+run/byname.scala
+run/vector1.scala
+run/t5879.scala
+run/t1048.scala
+run/t5080.scala
+run/t4190.scala
+run/caseClassEquality.scala
+run/macro-enclosures
+run/collections-toSelf.scala
+run/implicits.scala
+run/finalvar.scala
+run/lazy-locals.scala
+run/t7231.scala
+run/t0508.scala
+run/t6628.scala
+run/t6406-regextract.scala
+run/t0911.scala
+run/t4013c.scala
+run/t3502.scala
+run/t5648.scala
+run/retclosure.scala
+run/t2857.scala
+run/t4859.scala
+run/t5162.scala
+run/t3038.scala
+run/classof.scala
+run/t4062.scala
+run/unapplyArray.scala
+run/t4297.scala
+run/t5923a
+run/iterators.scala
+run/t1537.scala
+run/boolexprs.scala
+run/valueclasses-classtag-generic.scala
+run/macro-term-declared-in-anonymous
+run/tcpoly_monads.scala
+run/t5407.scala
+run/scan.scala
+run/forvaleq.scala
+run/null-and-intersect.scala
+run/t7047
+run/t0607.scala
+run/sequenceComparisons.scala
+run/t4396.scala
+run/macro-undetparams-consfromsls
+run/t2029.scala
+run/t1220.scala
+run/option-fold.scala
+run/t5284c.scala
+run/macro-auto-duplicate
+run/t3529.scala
+run/t4697.scala
+run/t2251.scala
+run/t5300.scala
+run/virtpatmat_valdef.scala
+run/t2147.scala
+run/virtpatmat_extends_product.scala
+run/list_map.scala
+run/t1333.scala
+run/matchbytes.scala
+run/valueclasses-classmanifest-existential.scala
+run/records.scala
+run/t3088.scala
+run/macro-def-path-dependent
+run/t6443-by-name.scala
+run/t1044.scala
+run/delay-good.scala
+run/case-class-23.scala
+run/weakconform.scala
+run/patmat-bind-typed.scala
+run/t4835.scala
+run/t3097.scala
+run/t405.scala
+run/existentials.scala
+run/t2876.scala
+run/t4809.scala
+run/t1427.scala
+run/t6135.scala
+run/t3575.scala
+run/t5688.scala
+run/t6900.scala
+run/macro-expand-unapply-a
+run/t6677b.scala
+run/t7375a.scala
+run/t7300.scala
+run/t6246.scala
+run/typed-annotated
+run/elidable-noflags.scala
+run/t0042.scala
+run/t3050.scala
+run/t4536.scala
+run/NestedClasses.scala
+run/t3877.scala
+run/seqlike-kmp.scala
+run/t5907.scala
+run/t266.scala
+run/missingparams.scala
+run/t2255.scala
+run/private-inline.scala
+run/t3488.scala
+run/t3950.scala
+run/typealias_overriding.scala
+run/constant-optimization.scala
+run/t7507.scala
+run/t6090.scala
+run/iterator-concat.scala
+run/t4582.scala
+run/macro-term-declared-in-class
+run/macro-typecheck-macrosdisabled2
+run/t3425.scala
+run/t4935.scala
+run/t3326.scala
+run/boolord.scala
+run/t1141.scala
+run/virtpatmat_unapply.scala
+run/t5971.scala
+run/t3651.scala
+run/macro-sip19-revised
+run/pure-args-byname-noinline.scala
+run/preinits.scala
+run/t5532.scala
+run/concat-two-strings.scala
+run/t3269.scala
+run/macro-impl-default-params
+run/t2162.scala
+run/matchonseq.scala
+run/t5428.scala
+run/macro-expand-overload
+run/t4660.scala
+run/enrich-gentraversable.scala
+run/macro-expand-override
+run/t4054.scala
+run/t4753.scala
+run/valueclasses-manifest-generic.scala
+run/macro-typecheck-macrosdisabled
+run/t2308a.scala
+run/duplicate-meth.scala
+run/interop_classtags_are_classmanifests.scala
+run/t3232.scala
+run/t2075.scala
+run/virtpatmat_partial_backquoted.scala
+run/try-2.scala
+run/macro-openmacros
+run/macro-undetparams-macroitself
+run/t6318_derived.scala
+run/deprecate-early-type-defs.scala
+run/dead-code-elimination.scala
+run/t4827.scala
+run/Course-2002-07.scala
+run/slice-strings.scala
+run/t6292.scala
+run/t6206.scala
+run/t1042.scala
+run/t1718.scala
+run/t2074_2.scala
+run/arraycopy.scala
+run/indexedSeq.scala
+run/macro-term-declared-in-implicit-class
+run/t3511.scala
+run/t6290.scala
+run/distinct.scala
+run/virtpatmat_alts.scala
+run/valueclasses-pavlov.scala
+run/exceptions.scala
+run/t1368.scala
+run/t5856.scala
+run/t6968.scala
+run/names-defaults.scala
+run/macro-expand-tparams-implicit
+run/t5881.scala
+run/t3540.scala
+run/virtpatmat_try.scala
+run/t7181.scala
+run/value-class-extractor.scala
+run/value-class-extractor-2.scala
+run/t3150.scala
+run/exc.scala
+run/t3516.scala
+run/delay-bad.scala
+run/infix.scala
+run/t1309.scala
+run/t6370.scala
+run/t6725-2.scala
+run/macro-impl-tparam-typetag-is-optional
+run/macro-term-declared-in-block
+run/matchnull.scala
+run/t2127.scala
+run/t7325.scala
+run/groupby.scala
+run/t3932.scala
+run/t4871.scala
+run/longmap.scala
+run/t1524.scala
+run/t6187b.scala
+run/kmpSliceSearch.scala
+run/t7088.scala
+run/t5804.scala
+run/stringbuilder-drop.scala
+run/t5753_1
+pos/cyclics-pos.scala
+pos/cfcrash.scala
+pos/tcpoly_higherorder_bound_method.scala
+pos/t5084.scala
+pos/trait-force-info.scala
+pos/macro-qmarkqmarkqmark.scala
+pos/t7785.scala
+pos/nested.scala
+pos/t3152.scala
+pos/t5031
+pos/t6925b.scala
+pos/t1107b
+pos/t5012.scala
+pos/virtpatmat_obj_in_case.scala
+pos/t4938.scala
+pos/t3856.scala
+pos/spec-cyclic.scala
+pos/aliases.scala
+pos/typerep_pos.scala
+pos/t119.scala
+pos/t1050.scala
+pos/t3670.scala
+pos/t6145.scala
+pos/t7315.scala
+pos/t5930.scala
+pos/t789.scala
+pos/t5071.scala
+pos/t4731.scala
+pos/t4547.scala
+pos/t2038.scala
+pos/testCoercionThis.scala
+pos/t2444.scala
+pos/t5744
+pos/t780.scala
+pos/t1722-A.scala
+pos/virtpatmat_exist1.scala
+pos/t6225.scala
+pos/t762.scala
+pos/t0204.scala
+pos/rebind.scala
+pos/spec-short.scala
+pos/comp-rec-test.scala
+pos/lub-dealias-widen.scala
+pos/t1168.scala
+pos/modules.scala
+pos/t4220.scala
+pos/t4070.scala
+pos/t175.scala
+pos/t2500.scala
+pos/t5029.scala
+pos/itay.scala
+pos/t4202.scala
+pos/t1987b
+pos/t3534.scala
+pos/infer2-pos.scala
+pos/spec-sparsearray-new.scala
+pos/t7091.scala
+pos/ticket0137.scala
+pos/collectGenericCC.scala
+pos/t640.scala
+pos/t4305.scala
+pos/extractor-types.scala
+pos/t3880.scala
+pos/spec-annotations.scala
+pos/t3577.scala
+pos/compile1.scala
+pos/spec-t3497.scala
+pos/hkrange.scala
+pos/t287.scala
+pos/t7294.scala
+pos/t6008.scala
+pos/t4432.scala
+pos/CustomGlobal.scala
+pos/patmat.scala
+pos/t2413
+pos/t2910.scala
+pos/t592.scala
+pos/t6245
+pos/infer.scala
+pos/t7228.scala
+pos/compound.scala
+pos/attributes.scala
+pos/t6771.scala
+pos/t1090.scala
+pos/t684.scala
+pos/t577.scala
+pos/t4273.scala
+pos/t6278-synth-def.scala
+pos/t6184.scala
+neg/t0214.scala
+neg/t4842.scala
+neg/t6214.scala
+neg/reify_nested_inner_refers_to_local.scala
+neg/t576.scala
+neg/t5969.scala
+neg/tcpoly_variance.scala
+neg/t7509.scala
+neg/mixins.scala
+neg/parent-inherited-twice-error.scala
+neg/macro-abort
+neg/constructor-init-order.scala
+neg/t6042.scala
+neg/t0590.scala
+neg/eta-expand-star-deprecation.scala
+neg/t4221.scala
+neg/t6263.scala
+neg/t783.scala
+neg/t5554.scala
+neg/macro-invalidsig-params-badtype
+neg/multi-array.scala
+neg/raw-types-stubs
+neg/spec-overrides.scala
+neg/t836.scala
+neg/t7289_status_quo.scala
+neg/t5675.scala
+neg/macro-quasiquotes
+neg/t6667.scala
+neg/t6597.scala
+neg/t6264.scala
+neg/t0345.scala
+neg/t7294b.scala
+neg/t5340.scala
+neg/t2144.scala
+neg/t1010.scala
+neg/t1838.scala
+neg/t5189b.scala
+neg/reify_metalevel_breach_-1_refers_to_1.scala
+neg/t6601
+neg/wellkinded_wrongarity.scala
+neg/t3909.scala
+neg/t876.scala
+neg/t5390.scala
+neg/unit2anyref.scala
+neg/t0351.scala
+neg/t5120.scala
+neg/t1038.scala
+neg/t5878.scala
+neg/qualifying-class-error-2.scala
+neg/t3816.scala
+neg/tailrec.scala
+neg/volatile.scala
+neg/t944.scala
+neg/t1705.scala
+neg/t3977.scala
+neg/t5553_2.scala
+neg/t5318c.scala
+neg/overload-msg.scala
+neg/t5440.scala
+neg/t6335.scala
+neg/compile-time-only-b.scala
+neg/t501.scala
+neg/override.scala
+neg/t663.scala
+neg/t5892.scala
+neg/t1980.scala
+neg/macro-false-deprecation-warning
+neg/t5148.scala
+neg/t585.scala
+neg/t3776.scala
+neg/interop_classtags_arenot_manifests.scala
+neg/t4044.scala
+neg/macro-invalidusage-nontypeable
+neg/t6375.scala
+neg/t500.scala
+neg/t4877.scala
+neg/t5357.scala
+neg/interop_abstypetags_arenot_manifests.scala
+neg/t4460a.scala
+neg/t5318b.scala
+neg/t3234.scala
+neg/t4440.scala
+neg/t6663.scala
+neg/t6357.scala
+neg/gadts1.scala
+neg/cyclics.scala
+neg/t5060.scala
+neg/scopes.scala
+run/t4013.scala
+run/value-class-extractor-seq.scala
+run/macro-expand-tparams-explicit
+run/tuples.scala
+run/t5753_2
+run/t0528.scala
+run/t5105.scala
+run/t1195-old.scala
+run/t7341.scala
+run/t3670.scala
+run/t2594_tcpoly.scala
+run/t3895.scala
+run/t0668.scala
+run/slices.scala
+run/t6666a.scala
+run/valueclasses-classmanifest-generic.scala
+run/t2316_run.scala
+run/t3004.scala
+run/viewtest.scala
+run/t6481.scala
+run/t0005.scala
+run/t4110-old.scala
+run/t4766.scala
+run/t5500b.scala
+run/t7407b.scala
+run/backreferences.scala
+run/arrayview.scala
+run/t629.scala
+run/t5903c
+run/unittest_collection.scala
+run/spec-nlreturn.scala
+run/macro-term-declared-in-object-object
+run/triple-quoted-expr.scala
+run/t5937.scala
+run/t6011c.scala
+run/macro-expand-implicit-argument
+run/try.scala
+run/t1987b
+run/t6089.scala
+run/macro-range
+run/t2524.scala
+run/t4770.scala
+run/virtpatmat_unapplyprod.scala
+run/t1535.scala
+run/ctor-order.scala
+pos/t5210.scala
+pos/t5384.scala
+pos/rangepos.scala
+pos/t443.scala
+pos/t1480.scala
+pos/t116.scala
+pos/seqtest2.scala
+pos/scoping1.scala
+pos/t4269.scala
+pos/lookupswitch.scala
+pos/t3642
+pos/t5706.scala
+pos/SI-5788.scala
+pos/t7264
+pos/t0031.scala
+pos/macro-deprecate-dont-touch-backquotedidents.scala
+pos/t6815.scala
+pos/test4refine.scala
+pos/michel5.scala
+pos/t0851.scala
+pos/t1185.scala
+pos/sudoku.scala
+pos/t7520.scala
+pos/t6208.scala
+pos/t3411.scala
+pos/t295.scala
+pos/S3.scala
+pos/t0674.scala
+pos/t6664b.scala
+pos/variances_pos.scala
+pos/liftcode_polymorphic.scala
+pos/t3174b.scala
+pos/t7232d
+pos/t578.scala
+pos/implicit-infix-ops.scala
+pos/t4363.scala
+pos/t532.scala
+pos/exponential-spec.scala
+pos/t599.scala
+pos/t5862.scala
+pos/t4603
+pos/t3676.scala
+pos/t1357.scala
+pos/native-warning.scala
+pos/t1230
+pos/t6028
+pos/t4275.scala
+pos/overloaded_extractor_and_regular_def.scala
+pos/t4205
+pos/matthias1.scala
+pos/testcast.scala
+pos/generic-sigs.scala
+pos/t0093.scala
+pos/specializes-sym-crash.scala
+pos/t0061.scala
+pos/t2429.scala
+pos/t694.scala
+pos/javaReadsSigs
+pos/t2023.scala
+pos/t704.scala
+pos/t2208_pos.scala
+pos/t5137.scala
+pos/t2683.scala
+pos/t0049.scala
+pos/t1029
+pos/t4243.scala
+pos/typerep-stephane.scala
+pos/t177.scala
+pos/t5967.scala
+pos/t430.scala
+pos/virtpatmat_infer_single_1.scala
+pos/pat_iuli.scala
+pos/t1071.scala
+pos/t7226.scala
+pos/t1843.scala
+pos/t419.scala
+pos/t7364b
+pos/t1159.scala
+pos/t5305.scala
+pos/t7694.scala
+pos/t6047.scala
+pos/t3578.scala
+pos/t2082.scala
+pos/setter-not-implicit.scala
+pos/t1133.scala
+pos/t3862.scala
+pos/t942
+pos/nothing_manifest_disambig-new.scala
+pos/iterator-traversable-mix.scala
+pos/eta.scala
+pos/test4.scala
+pos/t2691.scala
+pos/t4502.scala
+pos/t7183.scala
+pos/protected-t1010.scala
+pos/X.scala
+pos/virtpatmat_exist2.scala
+pos/t4911.scala
+pos/t3477.scala
+pos/t4173.scala
+pos/t7782.scala
+pos/t2399.scala
+pos/virtpatmat_alts_subst.scala
+pos/propagate.scala
+pos/t2421b_pos.scala
+pos/t183.scala
+pos/t7033.scala
+pos/t3612.scala
+pos/t5330c.scala
+pos/t3020.scala
+pos/t4869.scala
+pos/t3373.scala
+pos/spec-params-new.scala
+pos/t3672.scala
+pos/t4501.scala
+pos/t1565.scala
+pos/t3774.scala
+pos/t6942
+neg/t3275.scala
+neg/t421.scala
+neg/t5702-neg-bad-brace.scala
+neg/t3663
+neg/badtok-1.scala
+neg/t677.scala
+neg/t7756b.scala
+neg/t6534.scala
+neg/t6276.scala
+neg/t5762.scala
+neg/abstract.scala
+neg/t2405.scala
+neg/t0418.scala
+neg/t5390c.scala
+neg/lazyvals.scala
+neg/lubs.scala
+neg/abstract-report.scala
+neg/t4163.scala
+neg/t5702-neg-bad-and-wild.scala
+neg/macro-invalidret
+neg/t6728.scala
+neg/t5152.scala
+neg/t1432.scala
+neg/abstract-inaccessible.scala
+neg/import-precedence.scala
+neg/t2462b.scala
+neg/macro-invalidusage-presuper
+neg/specification-scopes
+neg/t6048.scala
+neg/t4079
+neg/macro-basic-mamdmi
+neg/t7020.scala
+neg/t3015.scala
+neg/t0207.scala
+neg/t2296b
+neg/t0673
+neg/t3761-overload-byname.scala
+neg/t6675.scala
+neg/t5529.scala
+neg/sensitive.scala
+neg/t742.scala
+neg/t5067.scala
+neg/t6162-overriding.scala
+neg/variances.scala
+neg/t5728.scala
+neg/t6323a.scala
+neg/compile-time-only-a.scala
+neg/t6795.scala
+neg/t2494.scala
+neg/t3649.scala
+neg/macro-invalidsig
+neg/t2796.scala
+neg/t112706A.scala
+neg/t0764.scala
+neg/t3757
+neg/t1431.scala
+neg/exhausting.scala
+neg/t1523.scala
+neg/t779.scala
+neg/xmltruncated1.scala
+neg/t2208.scala
+neg/t2078.scala
+neg/t521.scala
+neg/null-unsoundness.scala
+neg/stmt-expr-discard.scala
+neg/t0513.scala
+neg/unchecked-abstract.scala
+neg/t4460c.scala
+neg/divergent-implicit.scala
+neg/t5078.scala
+neg/t1701.scala
+neg/t0816.scala
+neg/t1672b.scala
+neg/macro-invalidusage-badbounds
+neg/tailrec-2.scala
+neg/t4064.scala
+neg/reflection-names-neg.scala
+neg/t5510.scala
+neg/t3873.scala
+neg/tailrec-3.scala
+neg/t0226.scala
+neg/t2031.scala
+neg/t633.scala
+neg/constrs.scala
+neg/anyval-anyref-parent.scala
+neg/t7290.scala
+neg/t1041.scala
+neg/patternalts.scala
+neg/error_tooManyArgsPattern.scala
+neg/checksensibleUnit.scala
+neg/t6539
+neg/t4417.scala
+neg/wellkinded_app.scala
+neg/for-comprehension-old.scala
+neg/t2779.scala
+neg/object-not-a-value.scala
+neg/t2968b.scala
+neg/t6483.scala
+neg/t6902.scala
+neg/t6963a.scala
+neg/t3399.scala
+neg/t0015.scala
+neg/t3995.scala
+neg/t276.scala
+neg/t6758.scala
+neg/t2441.scala
+neg/cycle-bounds.scala
+neg/t1241.scala
+neg/t4137.scala
+neg/unicode-unterminated-quote.scala
+neg/t4762.scala
+neg/typeerror.scala
+neg/implicits.scala
+neg/t961.scala
+neg/ambiguous-float-dots2.scala
+neg/t2416.scala
+neg/t5799.scala
+neg/t7285.scala
+neg/implicit-shadow.scala
+neg/t2388.scala
+neg/java-access-neg
+neg/found-req-variance.scala
+neg/hk-bad-bounds.scala
+neg/t3224.scala
+neg/t1033.scala
+neg/t7385.scala
+neg/t5882.scala
+neg/t4541.scala
+neg/t2973.scala
+neg/t6406-regextract.scala
+neg/t6666.scala
+neg/t4831.scala
+neg/t425.scala
+neg/t1845.scala
+neg/t3683b.scala
+neg/t2801.scala
+neg/t6083.scala
+neg/t0528neg.scala
+neg/stringinterpolation_macro-neg.scala
+neg/t668.scala
+neg/t5666.scala
+neg/t4271.scala
+neg/interop_typetags_arenot_classmanifests.scala
+neg/t1355.scala
+neg/t715.scala
+neg/t7238.scala
+neg/t7473.scala
+neg/t7292-removal.scala
+neg/tcpoly_infer_ticket1162.scala
+neg/t4098.scala
+neg/t6013
+neg/t6227.scala
+neg/t464-neg.scala
+neg/badtok-3.scala
+neg/t6082.scala
+neg/anytrait.scala
+neg/valueclasses-doubledefs.scala
+neg/t7519.scala
+neg/overloaded-unapply.scala
+neg/t1163.scala
+neg/wellkinded_bounds.scala
+neg/t7292-deprecation.scala
+neg/t5044.scala
+neg/t0842.scala
+neg/t6436.scala
+neg/interop_typetags_arenot_classtags.scala
+neg/t3653.scala
+neg/higherkind_novalue.scala
+neg/t935.scala
+neg/t6040.scala
+neg/annot-nonconst.scala
+neg/macro-deprecate-idents.scala
+neg/illegal-stmt-start.scala
+neg/t565.scala
+neg/case-collision.scala
+neg/t3209.scala
+neg/t5821.scala
+neg/abstract-class-2.scala
+neg/t846.scala
+neg/quasiquotes-syntax-error-position.scala
+neg/t3987.scala
+neg/t877.scala
+neg/t0117.scala
+neg/t692.scala
+neg/t6666d.scala
+neg/t5702-neg-ugly-xbrace.scala
+neg/t7752.scala
+neg/case-collision2.scala
+neg/t6526.scala
+neg/t2213.scala
+neg/t7756a.scala
+neg/t845.scala
+neg/macro-override-macro-overrides-abstract-method-a
+neg/tcpoly_ticket2101.scala
+neg/delayed-init-ref.scala
+neg/caseinherit.scala
+neg/t3189.scala
+neg/unchecked-suppress.scala
+neg/t2180.scala
+neg/t1371.scala
+neg/macro-cyclic
+neg/t6123-explaintypes-macros
+neg/t4134.scala
+neg/t691.scala
+neg/t2421b.scala
+neg/t4691_exhaust_extractor.scala
+neg/t4419.scala
+neg/t5801.scala
+neg/t650.scala
+neg/t5735.scala
+neg/t696.scala
+neg/t882.scala
+neg/t2968.scala
+neg/t7507.scala
+neg/macro-invalidusage-badargs
+neg/macro-reify-typetag-typeparams-notags
+neg/wellkinded_app2.scala
+neg/t4425b.scala
+neg/t2296a
+neg/t1878.scala
+neg/t649.scala
+neg/override-object-no.scala
+neg/t4174.scala
+neg/t2070.scala
+neg/sabin2.scala
+neg/t5903e
+neg/t6566a.scala
+neg/finitary-error.scala
+neg/t4818.scala
+neg/t3614.scala
+neg/t6666c.scala
+neg/ticket513.scala
+neg/suggest-similar.scala
+neg/t4457_1.scala
+neg/t6666e.scala
+neg/tcpoly_bounds.scala
+neg/t4727.scala
+neg/t4425.scala
+neg/macro-invalidusage-methodvaluesyntax
+neg/t3854.scala
+neg/t3006.scala
+neg/t5580b.scala
+neg/t5378.scala
+neg/t639.scala
+neg/wrong-args-for-none.scala
+neg/t7171b.scala
+neg/t5361.scala
+neg/unreachablechar.scala
+neg/t5572.scala
+neg/t7757a.scala
+neg/macro-invalidimpl
+neg/t2773.scala
+neg/t6359.scala
+neg/saito.scala
+neg/xmltruncated2.scala
+neg/t667.scala
+neg/t3934.scala
+neg/t6771b.scala
+neg/t4584.scala
+neg/wellkinded_wrongarity2.scala
+neg/t7369.scala
+neg/t1477.scala
+neg/t5617.scala
+neg/t7299.scala
+neg/faculty.scala
+neg/virtpatmat_reach_null.scala
+neg/macro-reify-typetag-hktypeparams-notags
+neg/t1224.scala
+neg/xmltruncated3.scala
+neg/t1872.scala
+neg/t558.scala
+neg/t7110.scala
+neg/any-vs-anyref.scala
+neg/t6340.scala
+neg/t4166.scala
+neg/t2918.scala
+neg/t5856.scala
+neg/t4989.scala
+neg/t0003.scala
+neg/t1183.scala
+neg/t963.scala
+neg/t4515.scala
+neg/valueclasses-pavlov.scala
+neg/t608.scala
+neg/choices.scala
+neg/patmat-type-check.scala
+neg/valueclasses-impl-restrictions.scala
+neg/imp2.scala
+neg/protected-constructors.scala
+neg/t6788.scala
+neg/nullary-override.scala
+neg/t200.scala
+neg/t343.scala
+neg/names-defaults-neg-ref.scala
+neg/tcpoly_typealias.scala
+neg/classtags_contextbound_b.scala
+neg/t729.scala
+neg/t5683.scala
+neg/t4928.scala
+neg/t700.scala
+neg/t7669.scala
+neg/macro-invalidshape
+neg/t6011.scala
+neg/t7325.scala
+neg/check-dead.scala
+neg/t550.scala
+neg/t5663-badwarneq.scala
+neg/t0699
+neg/nopredefs.scala
+neg/t3507-old.scala
+neg/t5352.scala
+neg/t6336.scala
+neg/interop_classmanifests_arenot_typetags.scala
+neg/sealed-final-neg.scala
+neg/t2102.scala
+neg/t7636.scala
+neg/t5031b
+neg/t798.scala
+neg/t5702-neg-bad-xbrace.scala
+neg/t0899.scala
+neg/cyclics-import.scala
+neg/badtok-2.scala
+neg/t473.scala
+neg/t3160ambiguous.scala
+neg/t5106.scala
+neg/t1286
+neg/macro-override-macro-overrides-abstract-method-b
+neg/t0259.scala
+neg/t510.scala
+neg/t3836.scala
+neg/t5830.scala
+neg/t1548
+neg/t5580a.scala
+neg/forward.scala
+neg/t591.scala
+neg/t6558b.scala
+neg/t556.scala
+neg/xmltruncated4.scala
+neg/t5497.scala
+neg/t409.scala
+neg/t6283.scala
+neg/override-object-flag.scala
+neg/constructor-prefix-error.scala
+neg/eta-expand-star.scala
+neg/t3392.scala
+neg/t1275.scala
+neg/nested-fn-print.scala
+neg/t7330.scala
+neg/t2275a.scala
+neg/t630.scala
+neg/t4270.scala
+neg/t2775.scala
+neg/pat_unreachable.scala
+neg/t4158.scala
+neg/unit-returns-value.scala
+neg/t1422.scala
+neg/reify_metalevel_breach_-1_refers_to_0_b.scala
+neg/reassignment.scala
+neg/t3683a.scala
+neg/noMember1.scala
+neg/macro-without-xmacros-b
+neg/t1106.scala
+neg/t5182.scala
+neg/t6889.scala
+neg/t4217.scala
+neg/t7501
+neg/t5063.scala
+neg/t1009.scala
+neg/t997.scala
+neg/unchecked.scala
+neg/classtags_contextbound_c.scala
+neg/applydynamic_sip.scala
+neg/t7715.scala
+neg/t588.scala
+neg/t6667b.scala
+neg/t7757b.scala
+neg/t4069.scala
+neg/t515.scala
+neg/variances2.scala
+neg/t1049.scala
+neg/t7289.scala
+neg/t1623.scala
+neg/permanent-blindness.scala
+neg/t5803.scala
+neg/super-cast-or-test.scala
+neg/nonlocal-warning.scala
+neg/t5687.scala
+neg/t5903a
+neg/t6566b.scala
+neg/unchecked-knowable.scala
+neg/t5093.scala
+neg/protected-static-fail
+neg/type-diagnostics.scala
+neg/forgot-interpolator.scala
+neg/interop_abstypetags_arenot_classmanifests.scala
+neg/t5376.scala
+neg/t545.scala
+neg/xmlcorner.scala
+neg/switch.scala
+neg/depmet_1.scala
+neg/abstract-concrete-methods.scala
+neg/t4987.scala
+neg/t5452-new.scala
+neg/t750b
+neg/unchecked-refinement.scala
+neg/t418.scala
+neg/t5354.scala
+neg/t3736.scala
+neg/t631.scala
+neg/t6829.scala
+neg/t0218.scala
+neg/volatile-intersection.scala
+neg/t412.scala
+neg/t693.scala
+neg/t4882.scala
+neg/t1960.scala
+neg/macro-divergence-controlled
+neg/t712.scala
+neg/t5544
+neg/t3222.scala
+neg/t3604.scala
+neg/t1112.scala
+neg/t7157
+neg/accesses.scala
+neg/t452.scala
+neg/t6162-inheritance
+neg/t2442
+neg/t6567.scala
+neg/lazy-override.scala
+neg/abstract-explaintypes.scala
+neg/nested-annotation.scala
+neg/t5753
+neg/t4283b
+neg/t3691.scala
+neg/infix-op-positions.scala
+neg/t3403.scala
+neg/t4851
+neg/structural.scala
+neg/error_dependentMethodTpeConversionToFunction.scala
+neg/t5839.scala
+neg/t5553_1.scala
+neg/reify_metalevel_breach_+0_refers_to_1.scala
+neg/t752.scala
+neg/t6574.scala
+neg/t3714-neg.scala
+neg/t4457_2.scala
+neg/t2148.scala
+neg/t3240.scala
+neg/t1364.scala
+neg/saferJavaConversions.scala
+neg/t414.scala
+neg/t5493.scala
+neg/classtags_contextbound_a.scala
+neg/reify_metalevel_breach_-1_refers_to_0_a.scala
+neg/t3118.scala
+neg/t512.scala
+neg/t2336.scala
+neg/t856.scala
+neg/xmltruncated6.scala
+neg/t2206.scala
+neg/virtpatmat_unreach_select.scala
+neg/t6258.scala
+neg/t6815.scala
+neg/not-possible-cause.scala
+neg/dbldef.scala
+neg/qualifying-class-error-1.scala
+neg/t835.scala
+neg/t5455.scala
+neg/t6558.scala
+neg/t708.scala
+neg/macro-nontypeablebody
+neg/t0565.scala
+neg/xmltruncated5.scala
+neg/t5390d.scala
+neg/t520.scala
+neg/t6138.scala
+neg/macro-without-xmacros-a
+neg/t7214neg.scala
+neg/t2870.scala
+neg/t593.scala
+neg/t4541b.scala
+neg/t4460b.scala
+neg/t284.scala
+neg/t2488.scala
+neg/macro-override-method-overrides-macro
+neg/interop_abstypetags_arenot_classtags.scala
+neg/t3769.scala
+neg/warn-inferred-any.scala
+neg/t664.scala
+neg/t5903d
+neg/t562.scala
+neg/t2316.scala
+neg/t0152.scala
+neg/migration28.scala
+neg/t6443c.scala
+neg/tcpoly_override.scala
+neg/t7324.scala
+neg/t987.scala
+neg/t5903b
+neg/t3481.scala
+neg/t6912.scala
+neg/tcpoly_variance_enforce.scala
+neg/t3913.scala
+neg/names-defaults-neg.scala
+neg/t765.scala
+neg/t5358.scala
+neg/t391.scala
+neg/serialversionuid-not-const.scala
+neg/t771.scala
+neg/t0903.scala
+neg/catch-all.scala
+neg/classmanifests_new_deprecations.scala
+neg/t0606.scala
+neg/t5189_inferred.scala
+neg/macro-reify-typetag-useabstypetag
+neg/t5543.scala
+neg/logImplicits.scala
+neg/interop_typetags_without_classtags_arenot_manifests.scala
+neg/t6535.scala
+neg/t7259.scala
+neg/t2139.scala
+neg/t278.scala
+neg/t5564.scala
+neg/unchecked3.scala
+neg/virtpatmat_reach_sealed_unsealed.scala
+neg/checksensible.scala
+neg/t7721.scala
+run/t3798.scala
+run/macro-expand-varargs-explicit-over-varargs
+run/t3888.scala
+run/t0677-new.scala
+run/t3273.scala
+run/t3763.scala
+run/t2755.scala
+run/t920.scala
+run/t5610a.scala
+run/literals.scala
+run/proxy.scala
+run/unapply.scala
+run/t5830.scala
+run/array-addition.scala
+run/macro-expand-nullary-nongeneric
+run/macro-basic-ma-mdmi
+run/valueclasses-constr.scala
+run/t1247.scala
+run/t3487.scala
+run/rawstrings.scala
+run/patmat-seqs.scala
+run/eta-expand-star.scala
+run/t7436.scala
+run/t3996.scala
+run/constructors.scala
+run/t498.scala
+run/t3835.scala
+run/t298.scala
+run/t2867.scala
+run/t7120
+run/virtpatmat_literal.scala
+run/t2175.scala
+run/t2503.scala
+run/t3026.scala
+run/t603.scala
+run/t0091.scala
+run/t6394a
+run/macro-expand-varargs-implicit-over-varargs
+run/t7407.scala
+run/t2552.scala
+run/priorityQueue.scala
+run/virtpatmat_npe.scala
+run/macro-sip19
+run/t6644.scala
+run/t6614.scala
+run/t2005.scala
+run/t4680.scala
+run/t5903a
+run/classtags_contextbound.scala
+run/Course-2002-05.scala
+run/applydynamic_sip.scala
+run/t1766.scala
+run/retsynch.scala
+run/t7715.scala
+run/t102.scala
+run/nonlocalreturn.scala
+run/macro-reify-staticXXX
+run/Course-2002-06.scala
+run/t6863.scala
+run/t6500.scala
+run/macro-impl-rename-context
+run/t4351.scala
+run/t5009.scala
+run/macro-term-declared-in-annotation
+run/t6271.scala
+run/array-existential-bound.scala
+run/t6443b.scala
+run/t1987.scala
+run/MutableListTest.scala
+run/t7571.scala
+run/t5488-fn.scala
+run/macro-bodyexpandstoimpl
+run/macro-reify-ref-to-packageless
+run/t2212.scala
+run/macro-expand-varargs-implicit-over-nonvarargs
+run/t0807.scala
+run/patmat-behavior.scala
+run/t2446.scala
+run/tuple-zipped.scala
+run/breakout.scala
+run/t4122.scala
+run/macro-settings
+run/t7157
+run/t1323.scala
+run/t4013b.scala
+run/t6309.scala
+run/t4047.scala
+run/t5544
+run/t978.scala
+run/t3361.scala
+run/t6611.scala
+run/t5387.scala
+run/t5656.scala
+run/t4897.scala
+run/numeric-range.scala
+run/t4777.scala
+run/Course-2002-03.scala
+run/string-extractor.scala
+run/view-headoption.scala
+run/patmat_unapp_abstype-new.scala
+run/stream-stack-overflow-filter-map.scala
+run/macro-impl-tparam-only-in-impl
+run/t6559.scala
+run/macro-reify-tagful-a
+run/macro-expand-multiple-arglists
+run/t4709.scala
+run/t3509.scala
+run/t5284b.scala
+run/t7617b
+run/t3923.scala
+run/virtpatmat_apply.scala
+run/t363.scala
+run/manifests-undeprecated-in-2.10.0.scala
+run/matchintasany.scala
+run/t3970.scala
+run/t4996.scala
+run/t5530.scala
+run/macro-term-declared-in-object-class
+run/t3242b.scala
+run/indexedSeq-apply.scala
+run/t107.scala
+run/t2337.scala
+run/t3758-old.scala
+run/t2754.scala
+run/valueclasses-manifest-existential.scala
+run/flat-flat-flat.scala
+run/t6673.scala
+run/interpolationMultiline2.scala
+run/t3493.scala
+run/t0631.scala
+run/t2800.scala
+run/t6506.scala
+run/t6260.scala
+run/t2418.scala
+run/t4415.scala
+run/classmanifests_new_alias.scala
+run/t5380.scala
+run/tcpoly_parseridioms.scala
+run/t1747.scala
+run/t5903d
+run/t3530.scala
+run/t216.scala
+run/macro-term-declared-in-refinement
+run/t4592.scala
+run/t2488.scala
+run/t3327.scala
+run/t5614.scala
+run/t5903b
+run/iterables.scala
+run/t3964.scala
+run/t6329_vanilla.scala
+run/t3038c
+run/t1697.scala
+run/t2030.scala
+run/t3397.scala
+run/t1005.scala
+run/t3353.scala
+run/t1466.scala
+run/t3186.scala
+run/tcpoly_overriding.scala
+run/t5394.scala
+run/t5284.scala
+run/unboxingBug.scala
+run/t7200.scala
+run/macro-reify-basic
+run/t153.scala
+run/iterator3444.scala
+run/macro-expand-implicit-macro-is-val
+run/macro-basic-ma-md-mi
+run/interpolationArgs.scala
+run/t4954.scala
+run/t3645.scala
+run/transpose.scala
+run/t3887.scala
+run/t4288.scala
+run/unittest_iterator.scala
+run/t5543.scala
+run/macro-term-declared-in-object
+run/iq.scala
+run/t2788.scala
+run/t2027.scala
+run/macro-expand-recursive
+run/t949.scala
+run/t1909b.scala
+run/delambdafy-nested-by-name.scala
+run/delambdafy-two-lambdas.scala
+run/macro-blackbox-materialization
+run/lists-run.scala
+run/macro-parse-position
+run/macro-parse-position-malformed
+run/macro-whitebox-dynamic-materialization
+run/macro-whitebox-extractor
+run/macro-vampire-false-warning
+run/macro-whitebox-fundep-materialization
+run/macro-whitebox-structural
+run/mutable-treeset.scala
+run/static-module-method.scala
+run/sort.scala
+run/t1909.scala
+run/t1909c.scala
+run/t3346a.scala
+run/t3346d.scala
+run/t3346f.scala
+run/t3346h.scala
+run/t3346g.scala
+run/t3832.scala
+run/t4742.scala
+run/t5377.scala
+run/t5923c.scala
+run/t6188.scala
+run/t6333.scala
+run/t6385.scala
+run/t7899.scala
+run/t7899-regression.scala
+run/t7584b.scala
+run/t7223.scala
+run/t7859
+run/t7868.scala
+run/t7871
+run/arrayclone-new.scala
+run/arrayclone-old.scala
+run/bitsets.scala
+run/comparable-comparator.scala
+run/colltest1.scala
+run/t2106.scala
+run/t5986.scala
+run/view-iterator-stream.scala
+run/array-charSeq.scala
+pos/signatures
+pos/t1263
+pos/t3249
+neg/t4749.scala
+neg/main1.scala
+neg/t7251
+neg/t7494-after-terminal
+neg/t7494-before-parser
+neg/t7494-right-after-terminal
+run/lazy-traits.scala
+run/OrderingTest.scala
+run/ReplacementMatching.scala
+run/patmat-finally.scala
+run/t3158.scala
+run/t3346e.scala
+run/t4398.scala
+run/t4930.scala
+run/t6534.scala
+pos/sammy_scope.scala
+pos/delambdafy-patterns.scala
+pos/private-types-after-typer.scala
+pos/delambdafy-lambdalift.scala
+pos/sammy_poly.scala
+pos/sammy_single.scala
+pos/SI-4012-b.scala
+pos/sammy_twice.scala
+pos/t3160.scala
+pos/t1014.scala
+pos/t4970b.scala
+pos/t2698.scala
+pos/t5845.scala
+pos/t6201.scala
+pos/t6260a.scala
+pos/t7688.scala
+pos/t7818.scala
+pos/t1203a.scala
+pos/t7834.scala
+pos/t7853.scala
+pos/t7815.scala
+pos/t7853-partial-function.scala
+pos/t7864.scala
+pos/t7928.scala
+pos/t7902.scala
+pos/t7944.scala
+pos/t7847
+neg/accesses2.scala
+neg/bad-advice.scala
+neg/gadts2.scala
+neg/gadts2-strict.scala
+neg/macro-bundle-abstract.scala
+neg/macro-bundle-object.scala
+neg/macro-bundle-trait.scala
+neg/macro-blackbox-dynamic-materialization
+neg/macro-blackbox-extractor
+neg/run-gadts-strict.scala
+neg/macro-blackbox-structural
+neg/sammy_restrictions.scala
+neg/sammy_wrong_arity.scala
+neg/t2462c.scala
+neg/t3346b.scala
+neg/t1909-object.scala
+neg/macro-blackbox-fundep-materialization
+neg/t3346c.scala
+neg/t3871.scala
+neg/t3871b.scala
+neg/t3971.scala
+neg/t3346i.scala
+neg/t6120.scala
+neg/t6260c.scala
+neg/t6680a.scala
+neg/t7239.scala
+neg/t7007.scala
+neg/t7605-deprecation.scala
+neg/t7622-missing-required.scala
+neg/t7629-view-bounds-deprecation.scala
+neg/t7834neg.scala
+neg/t7783.scala
+neg/t7848-interp-warn.scala
+neg/t7519-b
+neg/t7622-missing-dependency
+neg/t7870.scala
+neg/t7877.scala
+neg/t7895.scala
+neg/t7895b.scala
+neg/t7899.scala
+neg/t7895c.scala
+neg/t7859
+run/t4752.scala
+run/t2087-and-2400.scala
+run/t3855.scala
+run/t6637.scala
+run/t6731.scala
+pos/t3999b.scala
+run/t0432.scala
+run/t2514.scala
+run/t7817.scala
+run/t874.scala
+run/type-currying.scala
+run/t3616.scala
+run/t3687.scala
+run/t4570.scala
+run/t5612.scala
+run/t1110.scala
+run/t2636.scala
+run/verify-ctor.scala
+run/t3647.scala
+run/t4560.scala
+run/t6632.scala
+run/hashCodeBoxesRunTime.scala
+run/richs.scala
+run/t6725-1.scala
+pos/t7776.scala
+run/fors.scala
+run/t6706.scala
+run/t3175.scala
+run/delambdafy-dependent-on-param-subst.scala
+run/t4332b.scala
+run/t8048a
+run/t8017
+run/t7985b.scala
+run/t8100.scala
+run/patmat-mix-case-extractor.scala
+run/t4750.scala
+run/t7912.scala
+run/delambdafy-dependent-on-param-subst-2.scala
+run/t8048b
+run/t8091.scala
+run/macroPlugins-macroRuntime
+run/macro-default-params
+run/t6355.scala
+run/t7777
+run/t8002.scala
+run/t8015-ffc.scala
+run/macro-subpatterns
+run/t7985.scala
+run/macroPlugins-macroArgs
+run/t7326.scala
+run/t5045.scala
+run/value-class-partial-func-depmet.scala
+run/t6329_vanilla_bug.scala
+run/macroPlugins-macroExpand
+run/t8010.scala
+run/macroPlugins-typedMacroBody
+run/t7406.scala
+run/t6253c.scala
+run/t6253a.scala
+run/t6253b.scala
+pos/t8146a.scala
+pos/t8046c.scala
+pos/t8002-nested-scope.scala
+pos/t8132.scala
+pos/t8045.scala
+pos/overzealous-assert-genbcode.scala
+pos/t8128.scala
+pos/t8013
+pos/t8064b
+pos/t6780.scala
+pos/t7987
+pos/bcode_throw_null
+pos/t8064
+pos/t8046.scala
+pos/t6231.scala
+pos/t7983.scala
+pos/t5508.scala
+pos/t5508-min.scala
+pos/t8023b.scala
+pos/t6231b.scala
+pos/debug-reset-local-attrs.scala
+pos/t8054.scala
+pos/t2066.scala
+pos/dotless-targs.scala
+pos/t8120.scala
+pos/t5508-min-okay.scala
+pos/t8060.scala
+pos/t8001
+pos/t8138.scala
+pos/t8111.scala
+pos/t8062
+pos/t8011.scala
+pos/t8146b.scala
+pos/t8046b.scala
+pos/t8023.scala
+pos/t5508-min-okay2.scala
+pos/macro-implicit-invalidate-on-error.scala
+neg/t6563.scala
+neg/missing-param-type-tuple.scala
+neg/not-a-legal-formal-parameter-tuple.scala
+neg/t7897.scala
+neg/t8015-ffa.scala
+neg/quasiquotes-unliftable-not-found.scala
+neg/t2066b.scala
+neg/dotless-targs.scala
+neg/patmat-classtag-compound.scala
+neg/t2066.scala
+neg/t8035-deprecated.scala
+neg/t6675b.scala
+neg/t8104
+neg/t7872.scala
+neg/t7850.scala
+neg/t7967.scala
+neg/macro-bundle-overloaded.scala
+neg/t6355a.scala
+neg/class-of-double-targs.scala
+neg/t6355b.scala
+neg/macro-reify-splice-splice
+neg/macro-bundle-noncontext.scala
+neg/t8015-ffb.scala
+neg/t8035-removed.scala
+neg/t7984.scala
+neg/t8024.scala
+neg/t8024b.scala
+neg/t8157.scala
+neg/t8146-non-finitary-2.scala
+neg/t8006.scala
+neg/t7872c.scala
+neg/t8146-non-finitary.scala
+neg/t7872b.scala
+neg/t6920.scala
+run/t6200.scala
+run/t6196.scala
+run/macro-bundle-context-refinement
+run/macro-enclosingowner-detectvar
+run/macro-enclosingowner-sbt
+run/macro-bundle-context-alias
+run/macro-bundle-whitebox-use-refined
+run/macro-bundle-whitebox-use-raw
+neg/name-lookup-stable.scala
+neg/t0764b.scala
+neg/no-implicit-to-anyref-any-val.scala
+neg/t1503.scala
+neg/t4728.scala
+neg/t6455.scala
+neg/t6260-named.scala
+neg/t6844.scala
+neg/t7475c.scala
+neg/t7475e.scala
+neg/t7475f.scala
+neg/macro-bundle-whitebox-use-raw
+neg/macro-bundle-whitebox-use-refined
+neg/macro-incompatible-macro-engine-b
+neg/t7980.scala
+neg/macro-incompatible-macro-engine-a
+neg/t8143a.scala
+neg/t8072.scala
+neg/t8207.scala
+neg/t8182.scala
+neg/t8219-any-any-ref-equals.scala
+neg/t8177a.scala
+neg/t8228.scala
+neg/t8229.scala
+neg/t8237-default.scala
+neg/t8244b.scala
+neg/t8244e
+neg/t8244c.scala
+neg/t8265.scala
+neg/t8266-invalid-interp.scala
+neg/t6931
+neg/t8376
+neg/t8372.scala
+neg/t8300-overloading.scala
+neg/t8244
+neg/t8158
+neg/t8431.scala
+pos/implicit-anyval-2.10.scala
+pos/delambdafy_t6260_method.scala
+pos/macro-bundle-disambiguate-bundle.scala
+pos/macro-bundle-disambiguate-nonbundle.scala
+pos/package-ob-case
+pos/t1786-counter.scala
+pos/reflection-compat-api-universe.scala
+pos/list-optim-check.scala
+pos/existential-java-case-class
+pos/t1786-cycle.scala
+pos/reflection-compat-c.scala
+pos/t3452f.scala
+pos/reflection-compat-ru.scala
+pos/t2066-2.10-compat.scala
+pos/reflection-compat-macro-universe.scala
+pos/t5900a.scala
+pos/t5760-pkgobj-warn
+pos/t5954a
+pos/t5954b
+pos/t5954d
+pos/t6260.scala
+pos/t5165b
+pos/t5954c
+pos/t6260b.scala
+pos/t7475b.scala
+pos/t7475a.scala
+pos/t7753.scala
+pos/t7322.scala
+pos/t6948.scala
+pos/t7475d.scala
+pos/t7475e.scala
+pos/t6169
+pos/t7788.scala
+pos/t7919.scala
+pos/t8177a.scala
+pos/t8177.scala
+pos/t8170.scala
+pos/t8170b.scala
+pos/t8177d.scala
+pos/t8177b.scala
+pos/t8177e.scala
+pos/t8134
+pos/t8177h.scala
+pos/t8177g.scala
+pos/t8207.scala
+pos/t8187.scala
+pos/t8219.scala
+pos/t8219b.scala
+pos/t8224.scala
+pos/t8237.scala
+pos/t8223.scala
+pos/t8237b.scala
+pos/t8300-conversions-a.scala
+pos/t8300-conversions-b.scala
+pos/t8209a
+pos/t8209b
+pos/t8244d
+pos/t8300-overloading.scala
+pos/t8300-patmat-a.scala
+pos/t8300-patmat-b.scala
+pos/t8315b.scala
+pos/t8306.scala
+pos/t8301.scala
+pos/t8324.scala
+pos/t8315.scala
+pos/t8301b.scala
+pos/t8363.scala
+pos/t8367.scala
+pos/t8369a.scala
+pos/t8369b.scala
+pos/t8403.scala
+pos/t8364.scala
+pos/t8352
+pos/t8376
+neg/macro-bundle-nonpublic-c.scala
+neg/literate_existentials.scala
+neg/macro-bundle-nonpublic-impl.scala
+neg/macro-bundle-ambiguous.scala
+neg/macro-bundle-priority-bundle.scala
+neg/macro-bundle-need-qualifier.scala
+neg/macro-bundle-nonstatic.scala
+neg/macro-bundle-polymorphic.scala
+neg/macro-bundle-priority-nonbundle.scala
+neg/macro-bundle-wrongcontext-a.scala
+neg/macro-bundle-wrongcontext-b.scala
+run/t8425
+run/t8245.scala
+run/t8266-octal-interp.scala
+run/t8280.scala
+run/t8395.scala
+run/t8321
+run/t8153.scala
+run/t8233-bcode.scala
+run/t8197.scala
+run/t8197b.scala
+run/t8233.scala
+run/t8133
+run/t8133b
+run/t7475b.scala
+run/t7445.scala
+run/t6814
+run/t4577.scala
+run/t5134.scala
+run/t3452f.scala
+run/t3452h.scala
+run/t3452c.scala
+run/t3452.scala
+run/t261.scala
+run/t3235-minimal.scala
+run/t1503_future.scala
+run/t5565.scala
+pos/t8411
+pos/t8460.scala
+run/t8428.scala
+run/t8437
+run/absoverride.scala
+run/arrays.scala
+run/duration-coarsest.scala
+run/iterator-from.scala
+run/SymbolsTest.scala
+run/t1074.scala
+run/t1505.scala
+run/streams.scala
+run/t2111.scala
+run/t4601.scala
+pos/SI-4012-a.scala
+pos/SI-7638.scala
+neg/t3692-new.scala
+run/t7015.scala
+run/t7992b.scala
+run/t7992.scala
+run/t8570.scala
+pos/t8157-2.10.scala
+pos/t8325.scala
+pos/t8523.scala
+pos/t8578.scala
+pos/t8329.scala
+pos/t8497
+pos/t8546.scala
+pos/t8531
+neg/t8325-c.scala
+neg/t8325-b.scala
+neg/t8325.scala
+neg/t6988.scala
+neg/t8463.scala
+neg/t8450.scala
+neg/t8430.scala
+run/finally.scala
+neg/t8630.scala
+neg/t8035-no-adapted-args.scala
+neg/t8675b.scala
+neg/t8610-arg.scala
+neg/t8736-c.scala
+neg/tailrec-4.scala
+neg/double-def-top-level
+neg/t8610.scala
+neg/aladdin1055
+neg/virtpatmat_exhaust_compound.scala
+neg/t8675.scala
+neg/t8525.scala
+pos/t8736.scala
+pos/t8625.scala
+pos/t8596.scala
+pos/t8617.scala
+pos/t8736-b.scala
+pos/t8708
+pos/macro-attachments
+run/t8611a.scala
+run/t8738.scala
+run/macro-rangepos-args
+run/t8610.scala
+run/macro-rangepos-subpatterns
+run/t8611c.scala
+run/macroPlugins-isBlackbox
+run/t8601d.scala
+run/t8607.scala
+run/bugs.scala
+run/t1503.scala
+run/t4148.scala
+run/t7763.scala
+run/issue192.scala
+
+# Adapt checkfiles for (1.0).toString == "1"
+run/Course-2002-01.scala
+run/t0421-new.scala
+run/runtime.scala
+run/t0421-old.scala
+run/spec-self.scala
+run/t5552.scala
+run/Course-2002-02.scala
+run/Course-2002-04.scala
+run/promotion.scala
+run/t4617.scala
+run/Course-2002-09.scala
+run/t5866.scala
+run/try-catch-unify.scala
+run/impconvtimes.scala
+run/Course-2002-10.scala
+run/Course-2002-08.scala
+
+# Adapt checkfiles for ().toString == "undefined"
+run/t5680.scala
+run/dynamic-anyval.scala
+run/macro-bundle-toplevel
+run/macro-bundle-whitebox-decl
+run/t6662
+run/t8570a.scala
+run/t3702.scala
+run/t7657
+run/macro-bundle-static
+run/structural.scala
+
+# Adapt checkfiles for print & flush (which we cannot 100% emulate)
+run/imports.scala
+run/misc.scala
+
+# Adapt checkfiles for compiler phase list
+run/t6102.scala
+neg/t7494-no-options
+
+# Adapt checkfiles for different behavior with boxed types
+run/t5568.scala
+run/virtpatmat_typetag.scala
+run/virtpatmat_switch.scala
+run/t5629b.scala
+run/t6318_primitives.scala
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/neg/t7494-no-options.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/neg/t7494-no-options.check
new file mode 100644
index 0000000..581da38
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/neg/t7494-no-options.check
@@ -0,0 +1,42 @@
+error: Error: ploogin takes no options
+ phase name id description
+ ---------- -- -----------
+ parser 1 parse source into ASTs, perform simple desugaring
+ namer 2 resolve names, attach symbols to named trees
+packageobjects 3 load package objects
+ typer 4 the meat and potatoes: type the trees
+ jsinterop 5
+ patmat 6 translate match expressions
+superaccessors 7 add super accessors in traits and nested classes
+ extmethods 8 add extension methods for inline classes
+ pickler 9 serialize symbol tables
+ refchecks 10 reference/override checking, translate nested objects
+ uncurry 11 uncurry, translate function values to anonymous classes
+ tailcalls 12 replace tail calls by jumps
+ specialize 13 @specialized-driven class and method specialization
+ explicitouter 14 this refs to outer pointers
+ erasure 15 erase types, add interfaces for traits
+ posterasure 16 clean up erased inline classes
+ lazyvals 17 allocate bitmaps, translate lazy vals into lazified defs
+ lambdalift 18 move nested functions to top level
+ constructors 19 move field definitions into constructors
+ flatten 20 eliminate inner classes
+ mixin 21 mixin composition
+ jscode 22
+ cleanup 23 platform-specific cleanups, generate reflective calls
+ delambdafy 24 remove lambdas
+ icode 25 generate portable intermediate code
+#partest !-optimise
+ jvm 26 generate JVM bytecode
+ ploogin 27 A sample phase that does so many things it's kind of hard...
+ terminal 28 the last phase during a compilation run
+#partest -optimise
+ inliner 26 optimization: do inlining
+inlinehandlers 27 optimization: inline exception handlers
+ closelim 28 optimization: eliminate uncalled closures
+ constopt 29 optimization: optimize null and other constants
+ dce 30 optimization: eliminate dead code
+ jvm 31 generate JVM bytecode
+ ploogin 32 A sample phase that does so many things it's kind of hard...
+ terminal 33 the last phase during a compilation run
+#partest
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-01.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-01.check
new file mode 100644
index 0000000..fcda943
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-01.check
@@ -0,0 +1,37 @@
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-02.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-02.check
new file mode 100644
index 0000000..ab75cfd
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-02.check
@@ -0,0 +1,187 @@
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-04.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-04.check
new file mode 100644
index 0000000..fc6ad96
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-04.check
@@ -0,0 +1,64 @@
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-08.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-08.check
new file mode 100644
index 0000000..0585d5b
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-08.check
@@ -0,0 +1,171 @@
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-09.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-09.check
new file mode 100644
index 0000000..c921361
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-09.check
@@ -0,0 +1,50 @@
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-10.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-10.check
new file mode 100644
index 0000000..847f0fa
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-10.check
@@ -0,0 +1,46 @@
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/bugs.sem b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/bugs.sem
new file mode 100644
index 0000000..d36898b
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/bugs.sem
@@ -0,0 +1 @@
+asInstanceOfs
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/dynamic-anyval.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/dynamic-anyval.check
new file mode 100644
index 0000000..c125372
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/dynamic-anyval.check
@@ -0,0 +1,4 @@
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/impconvtimes.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/impconvtimes.check
new file mode 100644
index 0000000..082377e
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/impconvtimes.check
@@ -0,0 +1 @@
+3.0 * Hour = Measure(3,Hour)
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/imports.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/imports.check
new file mode 100644
index 0000000..1aad598
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/imports.check
@@ -0,0 +1,21 @@
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/issue192.sem b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/issue192.sem
new file mode 100644
index 0000000..10abbf7
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/issue192.sem
@@ -0,0 +1 @@
+strictFloats
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/macro-bundle-static.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/macro-bundle-static.check
new file mode 100644
index 0000000..e2e7628
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/macro-bundle-static.check
@@ -0,0 +1,6 @@
+undefined
+Int
+undefined
+true
+IntInt
+true
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/macro-bundle-toplevel.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/macro-bundle-toplevel.check
new file mode 100644
index 0000000..e2e7628
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/macro-bundle-toplevel.check
@@ -0,0 +1,6 @@
+undefined
+Int
+undefined
+true
+IntInt
+true
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/macro-bundle-whitebox-decl.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/macro-bundle-whitebox-decl.check
new file mode 100644
index 0000000..e2e7628
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/macro-bundle-whitebox-decl.check
@@ -0,0 +1,6 @@
+undefined
+Int
+undefined
+true
+IntInt
+true
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/misc.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/misc.check
new file mode 100644
index 0000000..6043817
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/misc.check
@@ -0,0 +1,62 @@
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/promotion.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/promotion.check
new file mode 100644
index 0000000..41e36c3
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/promotion.check
@@ -0,0 +1,4 @@
+2
+6
+20
+30
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/runtime.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/runtime.check
new file mode 100644
index 0000000..0450b94
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/runtime.check
@@ -0,0 +1,70 @@
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/spec-self.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/spec-self.check
new file mode 100644
index 0000000..fd3c81a
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/spec-self.check
@@ -0,0 +1,2 @@
+5
+5
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/structural.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/structural.check
new file mode 100644
index 0000000..2fec112
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/structural.check
@@ -0,0 +1,37 @@
+ 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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t0421-new.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t0421-new.check
new file mode 100644
index 0000000..00d29b7
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t0421-new.check
@@ -0,0 +1,3 @@
+[Array(0, 1),Array(2, 3),Array(4, 5)]
+[Array(31)]
+[Array(24, 32)]
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t0421-old.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t0421-old.check
new file mode 100644
index 0000000..00d29b7
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t0421-old.check
@@ -0,0 +1,3 @@
+[Array(0, 1),Array(2, 3),Array(4, 5)]
+[Array(31)]
+[Array(24, 32)]
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t1503.sem b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t1503.sem
new file mode 100644
index 0000000..d36898b
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t1503.sem
@@ -0,0 +1 @@
+asInstanceOfs
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t3702.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t3702.check
new file mode 100644
index 0000000..3fce987
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t3702.check
@@ -0,0 +1,2 @@
+undefined
+6
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t4148.sem b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t4148.sem
new file mode 100644
index 0000000..d36898b
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t4148.sem
@@ -0,0 +1 @@
+asInstanceOfs
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t4617.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t4617.check
new file mode 100644
index 0000000..a6790f1
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t4617.check
@@ -0,0 +1 @@
+Str 8
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t5552.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t5552.check
new file mode 100644
index 0000000..4704611
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t5552.check
@@ -0,0 +1,2 @@
+(3,3)
+(3,3)
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t5568.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t5568.check
new file mode 100644
index 0000000..6f30cc5
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t5568.check
@@ -0,0 +1,9 @@
+void
+int
+class scala.runtime.BoxedUnit
+class scala.runtime.BoxedUnit
+class java.lang.Byte
+class java.lang.Byte
+5
+5
+5
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t5629b.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t5629b.check
new file mode 100644
index 0000000..c65298a
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t5629b.check
@@ -0,0 +1,10 @@
+=== 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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t5680.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t5680.check
new file mode 100644
index 0000000..a3b8b64
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t5680.check
@@ -0,0 +1,3 @@
+[Lscala.runtime.BoxedUnit
+undefined
+undefined
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t5866.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t5866.check
new file mode 100644
index 0000000..64df1ce
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t5866.check
@@ -0,0 +1,2 @@
+0
+Foo(0)
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t6102.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t6102.check
new file mode 100644
index 0000000..120082e
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t6102.check
@@ -0,0 +1,27 @@
+[running phase parser 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 uncurry on t6102.scala]
+[running phase tailcalls on t6102.scala]
+[running phase specialize 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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t6318_primitives.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t6318_primitives.check
new file mode 100644
index 0000000..654ef1b
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t6318_primitives.check
@@ -0,0 +1,54 @@
+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 scala.runtime.BoxedUnit matches void
+Some(undefined)
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t6662.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t6662.check
new file mode 100644
index 0000000..417b7b5
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t6662.check
@@ -0,0 +1 @@
+undefined
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t7657.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t7657.check
new file mode 100644
index 0000000..1a87c1e
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t7657.check
@@ -0,0 +1,3 @@
+undefined
+undefined
+undefined
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t7763.sem b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t7763.sem
new file mode 100644
index 0000000..d36898b
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t7763.sem
@@ -0,0 +1 @@
+asInstanceOfs
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t8570a.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t8570a.check
new file mode 100644
index 0000000..417b7b5
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t8570a.check
@@ -0,0 +1 @@
+undefined
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/try-catch-unify.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/try-catch-unify.check
new file mode 100644
index 0000000..813f011
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/try-catch-unify.check
@@ -0,0 +1,4 @@
+Failure(java.lang.NumberFormatException: For input string: "Hi")
+Success(5)
+O NOES
+Failure(java.lang.NumberFormatException: For input string: "Hi")
diff --git a/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/virtpatmat_switch.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/virtpatmat_switch.check
new file mode 100644
index 0000000..0900a9c
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/virtpatmat_switch.check
@@ -0,0 +1,7 @@
+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/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/virtpatmat_typetag.check b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/virtpatmat_typetag.check
new file mode 100644
index 0000000..048c3ae
--- /dev/null
+++ b/examples/scala-js/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/virtpatmat_typetag.check
@@ -0,0 +1,10 @@
+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/examples/scala-js/partest/src/main/scala/scala/tools/nsc/MainGenericRunner.scala b/examples/scala-js/partest/src/main/scala/scala/tools/nsc/MainGenericRunner.scala
new file mode 100644
index 0000000..6857142
--- /dev/null
+++ b/examples/scala-js/partest/src/main/scala/scala/tools/nsc/MainGenericRunner.scala
@@ -0,0 +1,220 @@
+package scala.tools.nsc
+
+/* Super hacky overriding of the MainGenericRunner used by partest */
+
+import scala.scalajs.ir
+
+import scala.scalajs.tools.sem.Semantics
+import scala.scalajs.tools.classpath._
+import scala.scalajs.tools.classpath.builder._
+import scala.scalajs.tools.logging._
+import scala.scalajs.tools.io._
+import scala.scalajs.tools.optimizer.ScalaJSOptimizer
+import scala.scalajs.tools.optimizer.ScalaJSClosureOptimizer
+import scala.scalajs.tools.optimizer.ParIncOptimizer
+import scala.scalajs.tools.env.JSConsole
+
+import scala.scalajs.sbtplugin.env.rhino.RhinoJSEnv
+import scala.scalajs.sbtplugin.env.nodejs.NodeJSEnv
+import scala.scalajs.sbtplugin.JSUtils._
+
+import scala.tools.partest.scalajs.ScalaJSPartestOptions._
+
+import java.io.File
+import scala.io.Source
+
+import Properties.{ versionString, copyrightString }
+import GenericRunnerCommand._
+
+class ScalaConsoleJSConsole extends JSConsole {
+ def log(msg: Any) = scala.Console.out.println(msg.toString)
+}
+
+class MainGenericRunner {
+ def errorFn(ex: Throwable): Boolean = {
+ ex.printStackTrace()
+ false
+ }
+ def errorFn(str: String): Boolean = {
+ scala.Console.err println str
+ false
+ }
+
+ val optMode = OptMode.fromId(sys.props("scalajs.partest.optMode"))
+
+ def noWarnMissing = {
+ import ScalaJSOptimizer._
+
+ for {
+ fname <- sys.props.get("scalajs.partest.noWarnFile").toList
+ line <- Source.fromFile(fname).getLines
+ if !line.startsWith("#")
+ } yield line.split('.') match {
+ case Array(className) => NoWarnClass(className)
+ case Array(className, methodName) => NoWarnMethod(className, methodName)
+ }
+ }
+
+ def readSemantics() = {
+ val opt = sys.props.get("scalajs.partest.compliantSems")
+ opt.fold(Semantics.Defaults) { str =>
+ val sems = str.split(',')
+ Semantics.compliantTo(sems.toList)
+ }
+ }
+
+ def process(args: Array[String]): Boolean = {
+ val command = new GenericRunnerCommand(args.toList, (x: String) => errorFn(x))
+ import command.{ settings, howToRun, thingToRun }
+
+ if (!command.ok) return errorFn("\n" + command.shortUsageMsg)
+ else if (settings.version) return errorFn("Scala code runner %s -- %s".format(versionString, copyrightString))
+ else if (command.shouldStopWithInfo) return errorFn("shouldStopWithInfo")
+
+ if (howToRun != AsObject)
+ return errorFn("Scala.js runner can only run an object")
+
+ // Load basic Scala.js classpath (used for running or further packaging)
+ val usefulClasspathEntries = for {
+ url <- settings.classpathURLs
+ f = urlToFile(url)
+ if (f.isDirectory || f.getName.startsWith("scalajs-library"))
+ } yield f
+ val classpath =
+ PartialClasspathBuilder.build(usefulClasspathEntries).resolve()
+
+ val logger = new ScalaConsoleLogger(Level.Warn)
+ val jsConsole = new ScalaConsoleJSConsole
+
+ val mainObjName = ir.Definitions.encodeClassName(thingToRun)
+ val baseRunner = runnerJSFile(mainObjName, command.arguments)
+ val semantics = readSemantics()
+
+ def fastOpted = fastOptimize(classpath, mainObjName, logger, semantics)
+ def fullOpted = fullOptimize(classpath, mainObjName, logger,
+ baseRunner, semantics.optimized)
+
+ val runner = {
+ if (optMode == FullOpt)
+ fullOptRunner()
+ else
+ baseRunner
+ }
+
+ val env =
+ if (optMode == NoOpt) new RhinoJSEnv(semantics)
+ else new NodeJSEnv
+
+ val runClasspath = optMode match {
+ case NoOpt => classpath
+ case FastOpt => fastOpted
+ case FullOpt => fullOpted
+ }
+
+ env.jsRunner(runClasspath, runner, logger, jsConsole).run()
+
+ true
+ }
+
+ private def runnerJSFile(mainObj: String, args: List[String]) = {
+ val jsObj = "ScalaJS.m." + mainObj
+ val jsArgs = argArray(args)
+ new MemVirtualJSFile("Generated launcher file").
+ withContent(s"$jsObj().main__AT__V($jsArgs);")
+ }
+
+ /** constructs a scala.Array[String] with the given elements */
+ private def argArray(args: List[String]) = {
+ s"""ScalaJS.makeNativeArrayWrapper(
+ ScalaJS.d.T.getArrayOf(),
+ ${listToJS(args)})"""
+ }
+
+ private def fastOptimize(
+ classpath: IRClasspath,
+ mainObjName: String,
+ logger: Logger,
+ semantics: Semantics) = {
+ import ScalaJSOptimizer._
+
+ val optimizer = newScalaJSOptimizer(semantics)
+ val output = WritableMemVirtualJSFile("partest fastOpt file")
+
+ optimizer.optimizeCP(
+ Inputs(classpath,
+ manuallyReachable = fastOptReachable(mainObjName),
+ noWarnMissing = noWarnMissing
+ ),
+ OutputConfig(
+ output = output,
+ wantSourceMap = false,
+ checkIR = true
+ ),
+ logger)
+ }
+
+ private def fastOptReachable(mainObjName: String) = {
+ import ScalaJSOptimizer._
+ List(
+ ReachObject(mainObjName),
+ ReachMethod(mainObjName + '$', "main__AT__V", static = false)
+ )
+ }
+
+ private def fullOptimize(
+ classpath: IRClasspath,
+ mainObjName: String,
+ logger: Logger,
+ runner: VirtualJSFile,
+ semantics: Semantics) = {
+ import ScalaJSClosureOptimizer._
+
+ val fastOptimizer = newScalaJSOptimizer(semantics)
+ val fullOptimizer = new ScalaJSClosureOptimizer(semantics)
+ val output = WritableMemVirtualJSFile("partest fullOpt file")
+ val exportFile = fullOptExportFile(runner)
+
+ fullOptimizer.optimizeCP(fastOptimizer, Inputs(
+ input = ScalaJSOptimizer.Inputs(
+ classpath,
+ manuallyReachable = fastOptReachable(mainObjName),
+ noWarnMissing = noWarnMissing),
+ additionalExports = exportFile :: Nil),
+ OutputConfig(
+ output,
+ checkIR = true,
+ wantSourceMap = false),
+ logger)
+
+ }
+
+ private def newScalaJSOptimizer(semantics: Semantics) =
+ new ScalaJSOptimizer(semantics, new ParIncOptimizer(_))
+
+ /** generates an exporter statement for the google closure compiler that runs
+ * what the normal test would
+ */
+ private def fullOptExportFile(runnerFile: VirtualJSFile) = {
+ new MemVirtualJSFile("partest fullOpt exports").withContent(
+ s"""this["runFullOptPartest"] = function() { ${runnerFile.content} };"""
+ )
+ }
+
+ private def fullOptRunner() = new MemVirtualJSFile("partest fullOpt runner").
+ withContent("runFullOptPartest();")
+
+ private def urlToFile(url: java.net.URL) = {
+ try {
+ new File(url.toURI())
+ } catch {
+ case e: java.net.URISyntaxException => new File(url.getPath())
+ }
+ }
+}
+
+object MainGenericRunner extends MainGenericRunner {
+ def main(args: Array[String]) {
+ if (!process(args))
+ sys.exit(1)
+ }
+}
diff --git a/examples/scala-js/partest/src/main/scala/scala/tools/partest/scalajs/PartestInterface.scala b/examples/scala-js/partest/src/main/scala/scala/tools/partest/scalajs/PartestInterface.scala
new file mode 100644
index 0000000..0dc2189
--- /dev/null
+++ b/examples/scala-js/partest/src/main/scala/scala/tools/partest/scalajs/PartestInterface.scala
@@ -0,0 +1,119 @@
+/* NOTE
+ * Most of this file is copy-pasted from
+ * https://github.com/scala/scala-partest-interface
+ * It is unfortunately not configurable enough, hence the duplication
+ */
+
+package scala.tools.partest
+package scalajs
+
+import scala.language.reflectiveCalls
+
+import sbt.testing.Fingerprint
+import sbt.testing.TaskDef
+import sbt.testing.EventHandler
+import sbt.testing.Logger
+import sbt.testing.Task
+import sbt.testing.AnnotatedFingerprint
+import java.net.URLClassLoader
+import java.io.File
+
+object Framework {
+ // as partest is not driven by test classes discovered by sbt, need to add this marker fingerprint to definedTests
+ val fingerprint = new AnnotatedFingerprint { def isModule = true; def annotationName = "partest" }
+
+ // TODO how can we export `fingerprint` so that a user can just add this to their build.sbt
+ // definedTests in Test += new sbt.TestDefinition("partest", fingerprint, true, Array())
+}
+class Framework extends sbt.testing.Framework {
+ def fingerprints: Array[Fingerprint] = Array(Framework.fingerprint)
+ def name: String = "partest"
+
+ def runner(args: Array[String], remoteArgs: Array[String], testClassLoader: ClassLoader): sbt.testing.Runner =
+ new Runner(args, remoteArgs, testClassLoader)
+}
+
+/** Represents one run of a suite of tests.
+ */
+case class Runner(args: Array[String], remoteArgs: Array[String], testClassLoader: ClassLoader) extends sbt.testing.Runner {
+ /** Returns an array of tasks that when executed will run tests and suites determined by the
+ * passed <code>TaskDef</code>s.
+ *
+ * <p>
+ * Each returned task, when executed, will run tests and suites determined by the
+ * test class name, fingerprints, "explicitly specified" field, and selectors of one of the passed <code>TaskDef</code>s.
+ * </p>
+ *
+ * <p>
+ * This <code>tasks</code> method may be called with <code>TaskDef</code>s containing the same value for <code>testClassName</code> but
+ * different fingerprints. For example, if both a class and its companion object were test classes, the <code>tasks</code> method could be
+ * passed an array containing <code>TaskDef</code>s with the same name but with a different value for <code>fingerprint.isModule</code>.
+ * </p>
+ *
+ * <p>
+ * A test framework may "reject" a requested task by returning no <code>Task</code> for that <code>TaskDef</code>.
+ * </p>
+ *
+ * @param taskDefs the <code>TaskDef</code>s for requested tasks
+ * @return an array of <code>Task</code>s
+ * @throws IllegalStateException if invoked after <code>done</code> has been invoked.
+ */
+ def tasks(taskDefs: Array[TaskDef]): Array[sbt.testing.Task] =
+ taskDefs map (PartestTask(_, args): sbt.testing.Task)
+
+ /** Indicates the client is done with this <code>Runner</code> instance.
+ *
+ * @return a possibly multi-line summary string, or the empty string if no summary is provided -- TODO
+ */
+ def done(): String = ""
+}
+
+/** Run partest in this VM. Assumes we're running in a forked VM!
+ *
+ * TODO: make configurable
+ */
+case class PartestTask(taskDef: TaskDef, args: Array[String]) extends Task {
+
+ // Get scala version through test name
+ val scalaVersion = taskDef.fullyQualifiedName.stripPrefix("partest-")
+
+ /** Executes this task, possibly returning to the client new tasks to execute. */
+ def execute(eventHandler: EventHandler, loggers: Array[Logger]): Array[Task] = {
+ val forkedCp = scala.util.Properties.javaClassPath
+ val classLoader = new URLClassLoader(forkedCp.split(java.io.File.pathSeparator).map(new File(_).toURI.toURL))
+
+ if (Runtime.getRuntime().maxMemory() / (1024*1024) < 800)
+ loggers foreach (_.warn(s"""Low heap size detected (~ ${Runtime.getRuntime().maxMemory() / (1024*1024)}M). Please add the following to your build.sbt: javaOptions in Test += "-Xmx1G""""))
+
+ val maybeOptions =
+ ScalaJSPartestOptions(args, str => loggers.foreach(_.error(str)))
+
+ maybeOptions foreach { options =>
+ val runner = SBTRunner(
+ Framework.fingerprint, eventHandler, loggers,
+ new File(s"../partest/fetchedSources/${scalaVersion}"),
+ classLoader, null, null, Array.empty[String], options, scalaVersion)
+
+ try runner execute Array("run", "pos", "neg")
+ catch {
+ case ex: ClassNotFoundException =>
+ loggers foreach { l => l.error("Please make sure partest is running in a forked VM by including the following line in build.sbt:\nfork in Test := true") }
+ throw ex
+ }
+ }
+
+ Array()
+ }
+
+ type SBTRunner = { def execute(kinds: Array[String]): String }
+
+ // use reflection to instantiate scala.tools.partest.scalajs.ScalaJSSBTRunner,
+ // casting to the structural type SBTRunner above so that method calls on the result will be invoked reflectively as well
+ private def SBTRunner(partestFingerprint: Fingerprint, eventHandler: EventHandler, loggers: Array[Logger], testRoot: File, testClassLoader: URLClassLoader, javaCmd: File, javacCmd: File, scalacArgs: Array[String], options: ScalaJSPartestOptions, scalaVersion: String): SBTRunner = {
+ val runnerClass = Class.forName("scala.tools.partest.scalajs.ScalaJSSBTRunner")
+ runnerClass.getConstructors()(0).newInstance(partestFingerprint, eventHandler, loggers, testRoot, testClassLoader, javaCmd, javacCmd, scalacArgs, options, scalaVersion).asInstanceOf[SBTRunner]
+ }
+
+ /** A possibly zero-length array of string tags associated with this task. */
+ def tags: Array[String] = Array()
+}
diff --git a/examples/scala-js/partest/src/main/scala/scala/tools/partest/scalajs/ScalaJSPartest.scala b/examples/scala-js/partest/src/main/scala/scala/tools/partest/scalajs/ScalaJSPartest.scala
new file mode 100644
index 0000000..edd0ea9
--- /dev/null
+++ b/examples/scala-js/partest/src/main/scala/scala/tools/partest/scalajs/ScalaJSPartest.scala
@@ -0,0 +1,203 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2013 LAMP/EPFL
+ * @author Sébastien Doeraene
+ */
+
+package scala.tools.partest
+package scalajs
+
+import nest._
+import Path._
+
+import scala.tools.nsc.{ Global, Settings }
+import scala.tools.nsc.reporters.{ Reporter }
+import scala.tools.nsc.plugins.Plugin
+
+import scala.scalajs.compiler.ScalaJSPlugin
+
+import scala.io.Source
+
+import sbt.testing.{ EventHandler, Logger, Fingerprint }
+import java.io.File
+import java.net.URLClassLoader
+
+trait ScalaJSDirectCompiler extends DirectCompiler {
+ override def newGlobal(settings: Settings, reporter: Reporter): PartestGlobal = {
+ new PartestGlobal(settings, reporter) {
+ override protected def loadRoughPluginsList(): List[Plugin] = {
+ (super.loadRoughPluginsList() :+
+ Plugin.instantiate(classOf[ScalaJSPlugin], this))
+ }
+ }
+ }
+}
+
+class ScalaJSRunner(testFile: File, suiteRunner: SuiteRunner,
+ scalaJSOverridePath: String, options: ScalaJSPartestOptions,
+ noWarnFile: File) extends nest.Runner(testFile, suiteRunner) {
+
+ private val compliantSems: List[String] = {
+ scalaJSConfigFile("sem").fold(List.empty[String]) { file =>
+ Source.fromFile(file).getLines.toList
+ }
+ }
+
+ override val checkFile: File = {
+ scalaJSConfigFile("check") getOrElse {
+ // this is super.checkFile, but apparently we can't do that
+ new FileOps(testFile).changeExtension("check")
+ }
+ }
+
+ private def scalaJSConfigFile(ext: String): Option[File] = {
+ val overrideFile = s"$scalaJSOverridePath/$kind/$fileBase.$ext"
+ val url = getClass.getResource(overrideFile)
+ Option(url).map(url => new File(url.toURI))
+ }
+
+ override def newCompiler = new DirectCompiler(this) with ScalaJSDirectCompiler
+ override def extraJavaOptions = {
+ super.extraJavaOptions ++ Seq(
+ s"-Dscalajs.partest.noWarnFile=${noWarnFile.getAbsolutePath}",
+ s"-Dscalajs.partest.optMode=${options.optMode.id}",
+ s"-Dscalajs.partest.compliantSems=${compliantSems.mkString(",")}"
+ )
+ }
+}
+
+trait ScalaJSSuiteRunner extends SuiteRunner {
+
+ // Stuff to mix in
+
+ val options: ScalaJSPartestOptions
+
+ /** Full scala version name. Used to discover blacklist (etc.) files */
+ val scalaVersion: String
+
+ // Stuff we provide
+
+ override def banner: String = {
+ import scala.scalajs.ir.ScalaJSVersions.{ current => currentVersion }
+
+ super.banner.trim + s"""
+ |Scala.js version is: $currentVersion
+ |Scala.js options are:
+ |optimizer: ${options.optMode.shortStr}
+ |testFilter: ${options.testFilter.descr}
+ """.stripMargin
+ }
+
+ override def runTest(testFile: File): TestState = {
+ // Mostly copy-pasted from SuiteRunner.runTest(), unfortunately :-(
+ val runner = new ScalaJSRunner(testFile, this, listDir, options, noWarnFile)
+
+ // when option "--failed" is provided execute test only if log
+ // is present (which means it failed before)
+ val state =
+ if (failed && !runner.logFile.canRead)
+ runner.genPass()
+ else {
+ val (state, elapsed) =
+ try timed(runner.run())
+ catch {
+ case t: Throwable => throw new RuntimeException(s"Error running $testFile", t)
+ }
+ NestUI.reportTest(state)
+ runner.cleanup()
+ state
+ }
+ onFinishTest(testFile, state)
+ }
+
+ override def runTestsForFiles(kindFiles: Array[File],
+ kind: String): Array[TestState] = {
+ super.runTestsForFiles(kindFiles.filter(shouldUseTest), kind)
+ }
+
+ private lazy val listDir =
+ s"/scala/tools/partest/scalajs/$scalaVersion"
+
+ private lazy val buglistedTestFileNames =
+ readTestList(s"$listDir/BuglistedTests.txt")
+
+ private lazy val blacklistedTestFileNames =
+ readTestList(s"$listDir/BlacklistedTests.txt")
+
+ private lazy val whitelistedTestFileNames =
+ readTestList(s"$listDir/WhitelistedTests.txt")
+
+ private lazy val noWarnFile: File = {
+ val url = getClass.getResource(s"$listDir/NoDCEWarn.txt")
+ assert(url != null, "Need NoDCEWarn.txt file")
+ new File(url.toURI).getAbsolutePath()
+ }
+
+ private def readTestList(resourceName: String): Set[String] = {
+ val source = scala.io.Source.fromURL(getClass.getResource(resourceName))
+
+ val fileNames = for {
+ line <- source.getLines
+ trimmed = line.trim
+ if trimmed != "" && !trimmed.startsWith("#")
+ } yield extendShortTestName(trimmed)
+
+ fileNames.toSet
+ }
+
+ private def extendShortTestName(testName: String) = {
+ val srcDir = PathSettings.srcDir
+ (srcDir / testName).toCanonical.getAbsolutePath
+ }
+
+ private lazy val testFilter: String => Boolean = {
+ import ScalaJSPartestOptions._
+ options.testFilter match {
+ case UnknownTests => { absPath =>
+ !blacklistedTestFileNames.contains(absPath) &&
+ !whitelistedTestFileNames.contains(absPath) &&
+ !buglistedTestFileNames.contains(absPath)
+ }
+ case BlacklistedTests => blacklistedTestFileNames
+ case BuglistedTests => buglistedTestFileNames
+ case WhitelistedTests => whitelistedTestFileNames
+ case SomeTests(names) => names.map(extendShortTestName _).toSet
+ }
+ }
+
+ private def shouldUseTest(testFile: File): Boolean = {
+ val absPath = testFile.toCanonical.getAbsolutePath
+ testFilter(absPath)
+ }
+}
+
+/* Pre-mixin ScalaJSSuiteRunner in SBTRunner, because this is looked up
+ * via reflection from the sbt partest interface of Scala.js
+ */
+class ScalaJSSBTRunner(
+ partestFingerprint: Fingerprint,
+ eventHandler: EventHandler,
+ loggers: Array[Logger],
+ testRoot: File,
+ testClassLoader: URLClassLoader,
+ javaCmd: File,
+ javacCmd: File,
+ scalacArgs: Array[String],
+ val options: ScalaJSPartestOptions,
+ val scalaVersion: String
+) extends SBTRunner(
+ partestFingerprint, eventHandler, loggers, "test/files", testClassLoader,
+ javaCmd, javacCmd, scalacArgs
+) with ScalaJSSuiteRunner {
+
+ // The test root for partest is read out through the system properties,
+ // not passed as an argument
+ sys.props("partest.root") = testRoot.getAbsolutePath()
+
+ // Partests take at least 5h. We double, just to be sure. (default is 4 hours)
+ sys.props("partest.timeout") = "10 hours"
+
+ // Set showDiff on global UI module
+ if (options.showDiff)
+ NestUI.setDiffOnFail()
+
+}
diff --git a/examples/scala-js/partest/src/main/scala/scala/tools/partest/scalajs/ScalaJSPartestOptions.scala b/examples/scala-js/partest/src/main/scala/scala/tools/partest/scalajs/ScalaJSPartestOptions.scala
new file mode 100644
index 0000000..1f1680a
--- /dev/null
+++ b/examples/scala-js/partest/src/main/scala/scala/tools/partest/scalajs/ScalaJSPartestOptions.scala
@@ -0,0 +1,109 @@
+package scala.tools.partest.scalajs
+
+class ScalaJSPartestOptions private (
+ val testFilter: ScalaJSPartestOptions.TestFilter,
+ val optMode: ScalaJSPartestOptions.OptMode,
+ val showDiff: Boolean
+)
+
+object ScalaJSPartestOptions {
+
+ sealed abstract class TestFilter {
+ def descr: String
+ }
+ case object UnknownTests extends TestFilter {
+ override def descr: String = "Unknown"
+ }
+ case object BlacklistedTests extends TestFilter {
+ override def descr: String = "Blacklisted"
+ }
+ case object WhitelistedTests extends TestFilter {
+ override def descr: String = "Whitelisted"
+ }
+ case object BuglistedTests extends TestFilter {
+ override def descr: String = "Buglisted"
+ }
+ case class SomeTests(names: List[String]) extends TestFilter {
+ override def descr: String = "Custom " + this.toString
+ override def toString() =
+ names.map(x => s""""$x"""").mkString("[", ", ", "]")
+ }
+
+ sealed abstract class OptMode {
+ def shortStr: String
+ def id: String
+ }
+ object OptMode {
+ def fromId(id: String): OptMode = id match {
+ case "none" => NoOpt
+ case "fast" => FastOpt
+ case "full" => FullOpt
+ case _ => sys.error(s"Unknown optimization mode: $id")
+ }
+ }
+ case object NoOpt extends OptMode {
+ def shortStr: String = "None"
+ def id: String = "none"
+ }
+ case object FastOpt extends OptMode {
+ def shortStr: String = "Fast"
+ def id: String = "fast"
+ }
+ case object FullOpt extends OptMode {
+ def shortStr: String = "Full"
+ def id: String = "full"
+ }
+
+ def apply(args: Array[String],
+ errorReporter: String => Unit): Option[ScalaJSPartestOptions] = {
+
+ var failed = false
+
+ var filter: Option[TestFilter] = None
+ var optMode: OptMode = NoOpt
+ var showDiff: Boolean = false
+
+ def error(msg: String) = {
+ failed = true
+ errorReporter(msg)
+ }
+
+ def setFilter(newFilter: TestFilter) = (filter, newFilter) match {
+ case (Some(SomeTests(oldNames)), SomeTests(newNames)) =>
+ // Merge test names
+ filter = Some(SomeTests(oldNames ++ newNames))
+ case (Some(fil), newFilter) =>
+ error(s"You cannot specify twice what tests to use (already specified: $fil, new: $newFilter)")
+ case (None, newFilter) =>
+ filter = Some(newFilter)
+ }
+
+ for (arg <- args) arg match {
+ case "--fastOpt" =>
+ optMode = FastOpt
+ case "--noOpt" =>
+ optMode = NoOpt
+ case "--fullOpt" =>
+ optMode = FullOpt
+ case "--blacklisted" =>
+ setFilter(BlacklistedTests)
+ case "--buglisted" =>
+ setFilter(BuglistedTests)
+ case "--whitelisted" =>
+ setFilter(WhitelistedTests)
+ case "--unknown" =>
+ setFilter(UnknownTests)
+ case "--showDiff" =>
+ showDiff = true
+ case _ =>
+ setFilter(SomeTests(arg :: Nil))
+ }
+
+ if (failed) None
+ else Some {
+ new ScalaJSPartestOptions(
+ filter.getOrElse(WhitelistedTests), optMode, showDiff)
+ }
+ }
+
+}
diff --git a/examples/scala-js/project/ExternalCompile.scala b/examples/scala-js/project/ExternalCompile.scala
new file mode 100644
index 0000000..7d37c81
--- /dev/null
+++ b/examples/scala-js/project/ExternalCompile.scala
@@ -0,0 +1,116 @@
+import sbt._
+import Keys._
+
+import scala.scalajs.sbtplugin.ScalaJSPlugin
+import ScalaJSPlugin.autoImport.jsDependencyManifest
+
+object ExternalCompile {
+
+ private val isWindows =
+ System.getProperty("os.name").toLowerCase().indexOf("win") >= 0
+
+ val scalaJSExternalCompileConfigSettings: Seq[Setting[_]] = inTask(compile)(
+ Defaults.runnerTask
+ ) ++ Seq(
+ fork in compile := true,
+ trapExit in compile := true,
+ javaOptions in compile += "-Xmx512M",
+
+ compile := {
+ val inputs = (compileInputs in compile).value
+ import inputs.config._
+
+ val s = streams.value
+ val logger = s.log
+ val cacheDir = s.cacheDirectory
+
+ // Discover classpaths
+
+ def cpToString(cp: Seq[File]) =
+ cp.map(_.getAbsolutePath).mkString(java.io.File.pathSeparator)
+
+ val compilerCp = inputs.compilers.scalac.scalaInstance.allJars
+ val cpStr = cpToString(classpath)
+
+ // List all my dependencies (recompile if any of these changes)
+
+ val allMyDependencies = classpath filterNot (_ == classesDirectory) flatMap { cpFile =>
+ if (cpFile.isDirectory) (cpFile ** "*.class").get
+ else Seq(cpFile)
+ }
+
+ // Compile
+
+ val cachedCompile = FileFunction.cached(cacheDir / "compile",
+ FilesInfo.lastModified, FilesInfo.exists) { dependencies =>
+
+ logger.info(
+ "Compiling %d Scala sources to %s..." format (
+ sources.size, classesDirectory))
+
+ if (classesDirectory.exists)
+ IO.delete(classesDirectory)
+ IO.createDirectory(classesDirectory)
+
+ val sourcesArgs = sources.map(_.getAbsolutePath()).toList
+
+ /* run.run() below in doCompileJS() will emit a call to its
+ * logger.info("Running scala.tools.nsc.scalajs.Main [...]")
+ * which we do not want to see. We use this patched logger to
+ * filter out that particular message.
+ */
+ val patchedLogger = new Logger {
+ def log(level: Level.Value, message: => String) = {
+ val msg = message
+ if (level != Level.Info ||
+ !msg.startsWith("Running scala.tools.nsc.Main"))
+ logger.log(level, msg)
+ }
+ def success(message: => String) = logger.success(message)
+ def trace(t: => Throwable) = logger.trace(t)
+ }
+
+ def doCompile(sourcesArgs: List[String]): Unit = {
+ val run = (runner in compile).value
+ run.run("scala.tools.nsc.Main", compilerCp,
+ "-cp" :: cpStr ::
+ "-d" :: classesDirectory.getAbsolutePath() ::
+ options ++:
+ sourcesArgs,
+ patchedLogger) foreach sys.error
+ }
+
+ /* Crude way of overcoming the Windows limitation on command line
+ * length.
+ */
+ if ((fork in compile).value && isWindows &&
+ (sourcesArgs.map(_.length).sum > 1536)) {
+ IO.withTemporaryFile("sourcesargs", ".txt") { sourceListFile =>
+ IO.writeLines(sourceListFile, sourcesArgs)
+ doCompile(List("@"+sourceListFile.getAbsolutePath()))
+ }
+ } else {
+ doCompile(sourcesArgs)
+ }
+
+ // Output is all files in classesDirectory
+ (classesDirectory ** AllPassFilter).get.toSet
+ }
+
+ cachedCompile((sources ++ allMyDependencies).toSet)
+
+ // We do not have dependency analysis when compiling externally
+ sbt.inc.Analysis.Empty
+ },
+
+ // Make sure jsDependencyManifest runs after compile, otherwise compile
+ // might remove the entire directory afterwards.
+ jsDependencyManifest <<= jsDependencyManifest.dependsOn(compile)
+ )
+
+ val scalaJSExternalCompileSettings = (
+ inConfig(Compile)(scalaJSExternalCompileConfigSettings) ++
+ inConfig(Test)(scalaJSExternalCompileConfigSettings)
+ )
+
+}
diff --git a/examples/scala-js/project/JavaLangObject.scala b/examples/scala-js/project/JavaLangObject.scala
new file mode 100644
index 0000000..ff82b94
--- /dev/null
+++ b/examples/scala-js/project/JavaLangObject.scala
@@ -0,0 +1,243 @@
+/*
+ * Hard-coded IR for java.lang.Object.
+ */
+
+import scala.scalajs.ir
+import ir._
+import ir.Definitions._
+import ir.Infos._
+import ir.Trees._
+import ir.Types._
+import ir.Position.NoPosition
+
+/** Hard-coded IR for java.lang.Object.
+ * We cannot so much as begin to fake a compilation of java.lang.Object,
+ * because Object is hijacked so much by scalac itself that it does not like
+ * at all to try to compile that class. So we have to bypass entirely the
+ * compiler to define java.lang.Object.
+ */
+object JavaLangObject {
+
+ /** Optimizer hints with `@inline`
+ * Unfortunately we do not have access to private details of
+ * [[OptimizerHints]], so we cannot do this cleanly. But it is fine
+ * somehow because we're part of the same project implementation.
+ */
+ private def inlineOptimizerHints = new OptimizerHints(2)
+
+ val InfoAndTree = (Info, Definition)
+
+ private def Info = ClassInfo(
+ name = "java.lang.Object",
+ encodedName = "O",
+ ancestorCount = 0,
+ kind = ClassKind.Class,
+ superClass = "",
+ ancestors = List("O"),
+ methods = List(
+ MethodInfo("__init__"),
+ MethodInfo("init___"),
+ MethodInfo("hashCode__I",
+ calledMethods = Map(
+ "jl_System$" -> List("identityHashCode__O__I")
+ ),
+ accessedModules = List("jl_System")
+ ),
+ MethodInfo("equals__O__Z",
+ optimizerHints = inlineOptimizerHints
+ ),
+ MethodInfo("clone__O",
+ calledMethods = Map(
+ "sjsr_package$" -> List("cloneObject__sjs_js_Object__sjs_js_Object"),
+ "jl_CloneNotSupportedException" -> List("init___")
+ ),
+ instantiatedClasses = List("jl_CloneNotSupportedException"),
+ accessedModules = List("sjsr_package"),
+ accessedClassData = List("jl_Cloneable"),
+ optimizerHints = inlineOptimizerHints
+ ),
+ MethodInfo("notify__V"),
+ MethodInfo("notifyAll__V"),
+ MethodInfo("toString__T",
+ calledMethods = Map(
+ "O" -> List("hashCode__I"),
+ "jl_Class" -> List("getName__T"),
+ "jl_Integer$" -> List("toHexString__I__T")
+ ),
+ accessedModules = List("jl_Integer")
+ ),
+ MethodInfo("finalize__V"),
+ MethodInfo("clone__",
+ calledMethods = Map(
+ "O" -> List("clone__O")
+ )
+ ),
+ MethodInfo("notify__",
+ calledMethods = Map(
+ "O" -> List("notify__V")
+ )
+ ),
+ MethodInfo("notifyAll__",
+ calledMethods = Map(
+ "O" -> List("notifyAll__V")
+ )
+ ),
+ MethodInfo("finalize__",
+ calledMethods = Map(
+ "O" -> List("finalize__V")
+ )
+ )
+ )
+ )
+
+ private def Definition = {
+ implicit val DummyPos = NoPosition
+
+ // ClassType(Object) is normally invalid, but not in this class def
+ val ThisType = ClassType(ObjectClass)
+
+ val classDef = ClassDef(
+ Ident("O", Some("java.lang.Object")),
+ ClassKind.Class,
+ None,
+ Nil,
+ List(
+ /* def this() = () */
+ MethodDef(
+ Ident("init___", Some("<init>")),
+ Nil,
+ AnyType,
+ This()(ThisType))(None),
+
+ /* def hashCode(): Int = System.identityHashCode(this) */
+ MethodDef(
+ Ident("hashCode__I", Some("hashCode__I")),
+ Nil,
+ IntType,
+ {
+ Apply(
+ LoadModule(ClassType("jl_System$")),
+ Ident("identityHashCode__O__I", Some("identityHashCode")),
+ List(This()(ThisType)))(IntType)
+ })(None),
+
+ /* def equals(that: Object): Boolean = this eq that */
+ MethodDef(
+ Ident("equals__O__Z", Some("equals__O__Z")),
+ List(ParamDef(Ident("that", Some("that")), AnyType, mutable = false)),
+ BooleanType,
+ {
+ BinaryOp(BinaryOp.===,
+ This()(ThisType),
+ VarRef(Ident("that", Some("that")), mutable = false)(AnyType))
+ })(None),
+
+ /* protected def clone(): Object =
+ * if (this.isInstanceOf[Cloneable]) <clone>(this)
+ * else throw new CloneNotSupportedException()
+ */
+ MethodDef(
+ Ident("clone__O", Some("clone__O")),
+ Nil,
+ AnyType,
+ {
+ If(IsInstanceOf(This()(ThisType), ClassType("jl_Cloneable")), {
+ Apply(LoadModule(ClassType("sjsr_package$")),
+ Ident("cloneObject__sjs_js_Object__sjs_js_Object", Some("cloneObject")),
+ List(This()(ThisType)))(AnyType)
+ }, {
+ Throw(New(ClassType("jl_CloneNotSupportedException"),
+ Ident("init___", Some("<init>")), Nil))
+ })(AnyType)
+ })(None),
+
+ /* def toString(): String =
+ * getClass().getName() + "@" + Integer.toHexString(hashCode())
+ */
+ MethodDef(
+ Ident("toString__T", Some("toString__T")),
+ Nil,
+ ClassType(StringClass),
+ {
+ BinaryOp(BinaryOp.String_+, BinaryOp(BinaryOp.String_+,
+ Apply(
+ GetClass(This()(ThisType)),
+ Ident("getName__T"), Nil)(ClassType(StringClass)),
+ // +
+ StringLiteral("@")),
+ // +
+ Apply(
+ LoadModule(ClassType("jl_Integer$")),
+ Ident("toHexString__I__T"),
+ List(Apply(This()(ThisType), Ident("hashCode__I"), Nil)(IntType)))(
+ ClassType(StringClass)))
+ })(None),
+
+ /* Since wait() is not supported in any way, a correct implementation
+ * of notify() and notifyAll() is to do nothing.
+ */
+
+ /* def notify(): Unit = () */
+ MethodDef(
+ Ident("notify__V", Some("notify__V")),
+ Nil,
+ NoType,
+ Skip())(None),
+
+ /* def notifyAll(): Unit = () */
+ MethodDef(
+ Ident("notifyAll__V", Some("notifyAll__V")),
+ Nil,
+ NoType,
+ Skip())(None),
+
+ /* def finalize(): Unit = () */
+ MethodDef(
+ Ident("finalize__V", Some("finalize__V")),
+ Nil,
+ NoType,
+ Skip())(None),
+
+ /* Reflective proxies
+ * Note that we do not need to proxy the following methods, since
+ * they are defined on Any in the Scala hierarchy and therefore a
+ * reflective call is never emitted:
+ * - equals
+ * - getClass
+ * - hashCode
+ * - toString
+ */
+
+ MethodDef(Ident("clone__"), Nil, AnyType,
+ Apply(This()(ThisType), Ident("clone__O"), Nil)(AnyType))(None),
+
+ MethodDef(Ident("notify__"), Nil, AnyType, Block(
+ Apply(This()(ThisType), Ident("notify__V"), Nil)(NoType),
+ Undefined()))(None),
+
+ MethodDef(Ident("notifyAll__"), Nil, AnyType, Block(
+ Apply(This()(ThisType), Ident("notifyAll__V"), Nil)(NoType),
+ Undefined()))(None),
+
+ MethodDef(Ident("finalize__"), Nil, AnyType, Block(
+ Apply(This()(ThisType), Ident("finalize__V"), Nil)(NoType),
+ Undefined()))(None),
+
+ // Exports
+
+ /* JSExport for toString(). */
+ MethodDef(
+ StringLiteral("toString"),
+ Nil,
+ AnyType,
+ {
+ Apply(This()(ThisType),
+ Ident("toString__T", Some("toString__T")),
+ Nil)(ClassType(StringClass))
+ })(None)
+ ))
+
+ Hashers.hashClassDef(classDef)
+ }
+
+}
diff --git a/examples/scala-js/project/JavaLangString.scala b/examples/scala-js/project/JavaLangString.scala
new file mode 100644
index 0000000..fc68b29
--- /dev/null
+++ b/examples/scala-js/project/JavaLangString.scala
@@ -0,0 +1,215 @@
+/*
+ * Hard-coded IR for java.lang.String.
+ */
+
+import scala.scalajs.ir
+import ir._
+import ir.Definitions._
+import ir.Infos._
+import ir.Trees._
+import ir.Types._
+import ir.Position.NoPosition
+
+/** Hard-coded IR for java.lang.String.
+ * Unlike for the other hijacked classes, scalac does not like at all to
+ * compile even a mocked version of java.lang.String. So we have to bypass
+ * entirely the compiler to define java.lang.String.
+ */
+object JavaLangString {
+
+ /** Optimizer hints with `@inline`
+ * Unfortunately we do not have access to private details of
+ * [[OptimizerHints]], so we cannot do this cleanly. But it is fine
+ * somehow because we're part of the same project implementation.
+ */
+ private def inlineOptimizerHints = new OptimizerHints(2)
+
+ val InfoAndTree = (Info, Definition)
+
+ private def Info = ClassInfo(
+ name = "java.lang.String",
+ encodedName = "T",
+ ancestorCount = 1,
+ kind = ClassKind.HijackedClass,
+ superClass = "O",
+ ancestors = List(
+ "T", "Ljava_io_Serializable", "jl_CharSequence", "jl_Comparable", "O"),
+ methods = List(
+ MethodInfo("equals__O__Z",
+ optimizerHints = inlineOptimizerHints
+ ),
+ MethodInfo("hashCode__I",
+ calledMethods = Map(
+ "sjsr_RuntimeString$" -> List("hashCode__T__I")
+ ),
+ accessedModules = List("sjsr_RuntimeString"),
+ optimizerHints = inlineOptimizerHints
+ ),
+ MethodInfo("compareTo__T__I",
+ calledMethods = Map(
+ "sjsr_RuntimeString$" -> List("compareTo__T__T__I")
+ ),
+ accessedModules = List("sjsr_RuntimeString"),
+ optimizerHints = inlineOptimizerHints
+ ),
+ MethodInfo("compareTo__O__I",
+ calledMethods = Map(
+ "T" -> List("compareTo__T__I")
+ ),
+ optimizerHints = inlineOptimizerHints
+ ),
+ MethodInfo("toString__T",
+ optimizerHints = inlineOptimizerHints
+ ),
+ MethodInfo("charAt__I__C",
+ calledMethods = Map(
+ "sjsr_RuntimeString$" -> List("charAt__T__I__C")
+ ),
+ accessedModules = List("sjsr_RuntimeString"),
+ optimizerHints = inlineOptimizerHints
+ ),
+ MethodInfo("length__I",
+ calledMethods = Map(
+ "sjsr_RuntimeString$" -> List("length__T__I")
+ ),
+ accessedModules = List("sjsr_RuntimeString"),
+ optimizerHints = inlineOptimizerHints
+ ),
+ MethodInfo("subSequence__I__I__jl_CharSequence",
+ calledMethods = Map(
+ "sjsr_RuntimeString$" -> List("subSequence__T__I__I__jl_CharSequence")
+ ),
+ accessedModules = List("sjsr_RuntimeString"),
+ optimizerHints = inlineOptimizerHints
+ )
+ )
+ )
+
+ private def Definition = {
+ implicit val DummyPos = NoPosition
+
+ val ThisType = ClassType(StringClass)
+
+ ClassDef(
+ Ident("T", Some("java.lang.String")),
+ ClassKind.HijackedClass,
+ Some(Ident("O", Some("java.lang.Object"))),
+ List(
+ Ident("Ljava_io_Serializable", Some("java.io.Serializable")),
+ Ident("jl_CharSequence", Some("java.lang.CharSequence")),
+ Ident("jl_Comparable", Some("java.lang.Comparable")),
+ Ident("O", Some("java.lang.Object"))
+ ),
+ List(
+ /* def equals(that: Object): Boolean = this eq that */
+ MethodDef(
+ Ident("equals__O__Z", Some("equals__O__Z")),
+ List(ParamDef(Ident("that", Some("that")), AnyType, mutable = false)),
+ BooleanType,
+ {
+ BinaryOp(BinaryOp.===,
+ This()(ThisType),
+ VarRef(Ident("that", Some("that")), mutable = false)(AnyType))
+ })(None),
+
+ /* def hashCode(): Int = RuntimeString.hashCode(this) */
+ MethodDef(
+ Ident("hashCode__I", Some("hashCode__I")),
+ Nil,
+ IntType,
+ {
+ Apply(
+ LoadModule(ClassType("sjsr_RuntimeString$")),
+ Ident("hashCode__T__I", Some("hashCode__T__I")),
+ List(This()(ThisType)))(IntType)
+ })(None),
+
+ /* def compareTo(that: String): Int = RuntimeString.compareTo(this, that) */
+ MethodDef(
+ Ident("compareTo__T__I", Some("compareTo__T__I")),
+ List(ParamDef(Ident("that", Some("that")), ThisType, mutable = false)),
+ IntType,
+ {
+ Apply(
+ LoadModule(ClassType("sjsr_RuntimeString$")),
+ Ident("compareTo__T__T__I", Some("compareTo__T__T__I")),
+ List(
+ This()(ThisType),
+ VarRef(Ident("that", Some("that")), mutable = false)(ThisType)))(IntType)
+ })(None),
+
+ /* def compareTo(that: Object): Int = compareTo(that.asInstanceOf[String]) */
+ MethodDef(
+ Ident("compareTo__O__I", Some("compareTo__O__I")),
+ List(ParamDef(Ident("that", Some("that")), AnyType, mutable = false)),
+ IntType,
+ {
+ Apply(
+ This()(ThisType),
+ Ident("compareTo__T__I", Some("compareTo__T__I")),
+ List(AsInstanceOf(
+ VarRef(Ident("that", Some("that")), mutable = false)(AnyType),
+ ThisType)))(IntType)
+ })(None),
+
+ /* def toString(): String = this */
+ MethodDef(
+ Ident("toString__T", Some("toString__T")),
+ Nil,
+ ClassType(StringClass),
+ {
+ This()(ThisType)
+ })(None),
+
+ /* def charAt(i: Int): Char = RuntimeString.charAt(this, i) */
+ MethodDef(
+ Ident("charAt__I__C", Some("charAt__I__C")),
+ List(ParamDef(Ident("i", Some("i")), IntType, mutable = false)),
+ IntType,
+ {
+ Apply(
+ LoadModule(ClassType("sjsr_RuntimeString$")),
+ Ident("charAt__T__I__C", Some("charAt__T__I__C")),
+ List(
+ This()(ThisType),
+ VarRef(Ident("i", Some("i")), mutable = false)(IntType)))(IntType)
+ })(None),
+
+ /* def length(): Int = RuntimeString.length(this) */
+ MethodDef(
+ Ident("length__I", Some("length__I")),
+ Nil,
+ IntType,
+ {
+ Apply(
+ LoadModule(ClassType("sjsr_RuntimeString$")),
+ Ident("length__T__I", Some("length__T__I")),
+ List(This()(ThisType)))(IntType)
+ })(None),
+
+ /* def subSequence(begin: Int, end: Int): CharSequence =
+ * RuntimeString.subSequence(this, begin, end)
+ */
+ MethodDef(
+ Ident("subSequence__I__I__jl_CharSequence",
+ Some("subSequence__I__I__jl_CharSequence")),
+ List(
+ ParamDef(Ident("begin", Some("begin")), IntType, mutable = false),
+ ParamDef(Ident("end", Some("end")), IntType, mutable = false)
+ ),
+ ClassType("jl_CharSequence"),
+ {
+ Apply(
+ LoadModule(ClassType("sjsr_RuntimeString$")),
+ Ident("subSequence__T__I__I__jl_CharSequence",
+ Some("subSequence__T__I__I__jl_CharSequence")),
+ List(
+ This()(ThisType),
+ VarRef(Ident("begin", Some("begin")), mutable = false)(IntType),
+ VarRef(Ident("end", Some("end")), mutable = false)(IntType)))(
+ ClassType("jl_CharSequence"))
+ })(None)
+ ))
+ }
+
+}
diff --git a/examples/scala-js/project/ScalaJSBuild.scala b/examples/scala-js/project/ScalaJSBuild.scala
new file mode 100644
index 0000000..5cbc405
--- /dev/null
+++ b/examples/scala-js/project/ScalaJSBuild.scala
@@ -0,0 +1,891 @@
+import sbt._
+import Keys._
+
+import bintray.Plugin.bintrayPublishSettings
+import bintray.Keys.{repository, bintrayOrganization, bintray}
+
+import java.io.{
+ BufferedOutputStream,
+ FileOutputStream,
+ BufferedWriter,
+ FileWriter
+}
+
+import scala.collection.mutable
+import scala.util.Properties
+
+import scala.scalajs.ir
+import scala.scalajs.sbtplugin._
+import scala.scalajs.sbtplugin.env.rhino.RhinoJSEnv
+import ScalaJSPlugin.autoImport._
+import ExternalCompile.scalaJSExternalCompileSettings
+import Implicits._
+
+import scala.scalajs.tools.sourcemap._
+import scala.scalajs.tools.io.MemVirtualJSFile
+
+import sbtassembly.Plugin.{AssemblyKeys, assemblySettings}
+import AssemblyKeys.{assembly, assemblyOption}
+
+object ScalaJSBuild extends Build {
+
+ val fetchedScalaSourceDir = settingKey[File](
+ "Directory the scala source is fetched to")
+ val fetchScalaSource = taskKey[File](
+ "Fetches the scala source for the current scala version")
+ val shouldPartest = settingKey[Boolean](
+ "Whether we should partest the current scala version (and fail if we can't)")
+
+ val commonSettings = Seq(
+ organization := "org.scala-lang.modules.scalajs",
+ version := scalaJSVersion,
+
+ normalizedName ~= {
+ _.replace("scala.js", "scalajs").replace("scala-js", "scalajs")
+ },
+
+ homepage := Some(url("http://scala-js.org/")),
+ licenses += ("BSD New",
+ url("https://github.com/scala-js/scala-js/blob/master/LICENSE")),
+
+ shouldPartest := {
+ val testListDir = (
+ (resourceDirectory in (partestSuite, Test)).value / "scala"
+ / "tools" / "partest" / "scalajs" / scalaVersion.value
+ )
+ testListDir.exists
+ },
+
+ scalacOptions ++= Seq(
+ "-deprecation",
+ "-unchecked",
+ "-feature",
+ "-encoding", "utf8"
+ )
+ )
+
+ private val snapshotsOrReleases =
+ if (scalaJSIsSnapshotVersion) "snapshots" else "releases"
+
+ private def publishToScalaJSRepoSettings = Seq(
+ publishTo := {
+ Seq("PUBLISH_USER", "PUBLISH_PASS").map(Properties.envOrNone) match {
+ case Seq(Some(user), Some(pass)) =>
+ Some(Resolver.sftp(
+ s"scala-js-$snapshotsOrReleases",
+ "repo.scala-js.org",
+ s"/home/scalajsrepo/www/repo/$snapshotsOrReleases")(
+ Resolver.ivyStylePatterns) as (user, pass))
+ case _ =>
+ None
+ }
+ }
+ )
+
+ private def publishToBintraySettings = (
+ bintrayPublishSettings
+ ) ++ Seq(
+ repository in bintray := "scala-js-releases",
+ bintrayOrganization in bintray := Some("scala-js")
+ )
+
+ val publishSettings = (
+ if (Properties.envOrNone("PUBLISH_TO_BINTRAY") == Some("true"))
+ publishToBintraySettings
+ else
+ publishToScalaJSRepoSettings
+ ) ++ Seq(
+ publishMavenStyle := false
+ )
+
+ val defaultSettings = commonSettings ++ Seq(
+ scalaVersion := "2.11.2"
+ )
+
+ val myScalaJSSettings = ScalaJSPluginInternal.scalaJSAbstractSettings ++ Seq(
+ autoCompilerPlugins := true,
+ scalaJSOptimizerOptions ~= (_.withCheckScalaJSIR(true))
+ )
+
+ val scalaJSSourceMapSettings = scalacOptions ++= {
+ if (scalaJSIsSnapshotVersion) Seq()
+ else Seq(
+ // Link source maps to github sources
+ "-P:scalajs:mapSourceURI:" + root.base.toURI +
+ "->https://raw.githubusercontent.com/scala-js/scala-js/v" +
+ scalaJSVersion + "/"
+ )
+ }
+
+ /** Depend library as if (exportJars in library) was set to false */
+ val compileWithLibrarySetting = {
+ internalDependencyClasspath in Compile ++= {
+ val prods = (products in (library, Compile)).value
+ val analysis = (compile in (library, Compile)).value
+
+ prods.map(p => Classpaths.analyzed(p, analysis))
+ }
+ }
+
+ // Used when compiling the compiler, adding it to scalacOptions does not help
+ scala.util.Properties.setProp("scalac.patmat.analysisBudget", "1024")
+
+ override lazy val settings = super.settings ++ Seq(
+ // Most of the projects cross-compile
+ crossScalaVersions := Seq(
+ "2.10.2",
+ "2.10.3",
+ "2.10.4",
+ "2.11.0",
+ "2.11.1",
+ "2.11.2",
+ "2.11.4"
+ ),
+ // Default stage
+ scalaJSStage in Global := PreLinkStage
+ )
+
+ lazy val root: Project = Project(
+ id = "scalajs",
+ base = file("."),
+ settings = defaultSettings ++ Seq(
+ name := "Scala.js",
+ publishArtifact in Compile := false,
+
+ clean := clean.dependsOn(
+ // compiler, library and jasmineTestFramework are aggregated
+ clean in tools, clean in toolsJS, clean in plugin,
+ clean in javalanglib, clean in javalib, clean in scalalib,
+ clean in libraryAux, clean in javalibEx,
+ clean in examples, clean in helloworld,
+ clean in reversi, clean in testingExample,
+ clean in testSuite, clean in noIrCheckTest,
+ clean in javalibExTestSuite,
+ clean in partest, clean in partestSuite).value,
+
+ publish := {},
+ publishLocal := {}
+ )
+ ).aggregate(
+ compiler, library, testBridge, jasmineTestFramework
+ )
+
+ /* This project is not really used in the build. Its sources are actually
+ * added as sources of the `compiler` project (and meant to be added to the
+ * `tools` project as well).
+ * It exists mostly to be used in IDEs, because they don't like very much to
+ * have code in a project that lives outside the project's directory, and
+ * like even less that code be shared by different projects.
+ */
+ lazy val irProject: Project = Project(
+ id = "ir",
+ base = file("ir"),
+ settings = defaultSettings ++ Seq(
+ name := "Scala.js IR",
+ exportJars := true
+ )
+ )
+
+ lazy val compiler: Project = Project(
+ id = "compiler",
+ base = file("compiler"),
+ settings = defaultSettings ++ publishSettings ++ Seq(
+ name := "Scala.js compiler",
+ crossVersion := CrossVersion.full, // because compiler api is not binary compatible
+ unmanagedSourceDirectories in Compile +=
+ (scalaSource in (irProject, Compile)).value,
+ libraryDependencies ++= Seq(
+ "org.scala-lang" % "scala-compiler" % scalaVersion.value,
+ "org.scala-lang" % "scala-reflect" % scalaVersion.value,
+ "com.novocode" % "junit-interface" % "0.9" % "test"
+ ),
+ testOptions += Tests.Setup { () =>
+ val testOutDir = (streams.value.cacheDirectory / "scalajs-compiler-test")
+ IO.createDirectory(testOutDir)
+ sys.props("scala.scalajs.compiler.test.output") =
+ testOutDir.getAbsolutePath
+ sys.props("scala.scalajs.compiler.test.scalajslib") =
+ (artifactPath in (library, Compile, packageBin)).value.getAbsolutePath
+ sys.props("scala.scalajs.compiler.test.scalalib") = {
+
+ def isScalaLib(att: Attributed[File]) = {
+ att.metadata.get(moduleID.key).exists { mId =>
+ mId.organization == "org.scala-lang" &&
+ mId.name == "scala-library" &&
+ mId.revision == scalaVersion.value
+ }
+ }
+
+ val lib = (managedClasspath in Test).value.find(isScalaLib)
+ lib.map(_.data.getAbsolutePath).getOrElse {
+ streams.value.log.error("Couldn't find Scala library on the classpath. CP: " + (managedClasspath in Test).value); ""
+ }
+ }
+ },
+ exportJars := true
+ )
+ )
+
+ val commonToolsSettings = Seq(
+ name := "Scala.js tools",
+
+ unmanagedSourceDirectories in Compile ++= Seq(
+ baseDirectory.value.getParentFile / "shared/src/main/scala",
+ (scalaSource in (irProject, Compile)).value),
+
+ sourceGenerators in Compile <+= Def.task {
+ ScalaJSEnvGenerator.generateEnvHolder(
+ baseDirectory.value.getParentFile,
+ (sourceManaged in Compile).value)
+ }
+ )
+
+ lazy val tools: Project = Project(
+ id = "tools",
+ base = file("tools/jvm"),
+ settings = defaultSettings ++ publishSettings ++ (
+ commonToolsSettings
+ ) ++ Seq(
+ scalaVersion := "2.10.4",
+
+ libraryDependencies ++= Seq(
+ "com.google.javascript" % "closure-compiler" % "v20130603",
+ "com.googlecode.json-simple" % "json-simple" % "1.1.1",
+ "com.novocode" % "junit-interface" % "0.9" % "test"
+ )
+ )
+ )
+
+ lazy val toolsJS: Project = Project(
+ id = "toolsJS",
+ base = file("tools/js"),
+ settings = defaultSettings ++ myScalaJSSettings ++ publishSettings ++ (
+ commonToolsSettings
+ ) ++ Seq(
+ crossVersion := ScalaJSCrossVersion.binary
+ ) ++ inConfig(Test) {
+ // Redefine test to run Node.js and link HelloWorld
+ test := {
+ if (scalaJSStage.value == Stage.PreLink)
+ error("Can't run toolsJS/test in preLink stage")
+
+ val cp = {
+ for (e <- (fullClasspath in Test).value)
+ yield JSUtils.toJSstr(e.data.getAbsolutePath)
+ }
+
+ val runCode = """
+ var framework = org.scalajs.jasminetest.JasmineTestFramework();
+ framework.setTags("typedarray")
+
+ // Load tests
+ scalajs.TestDetector().loadDetectedTests();
+
+ var reporter = new scalajs.JasmineConsoleReporter(true);
+
+ // Setup and run Jasmine
+ var jasmineEnv = jasmine.getEnv();
+ jasmineEnv.addReporter(reporter);
+ jasmineEnv.execute();
+ """
+
+ val code = {
+ s"""
+ var lib = scalajs.QuickLinker().linkNode(${cp.mkString(", ")});
+ var run = ${JSUtils.toJSstr(runCode)};
+
+ eval("(function() { 'use strict'; "+
+ "var __ScalaJSEnv = null; " +
+ lib + "; " + run + "}).call(this);");
+ """
+ }
+
+ val launcher = new MemVirtualJSFile("Generated launcher file")
+ .withContent(code)
+
+ val runner = jsEnv.value.jsRunner(scalaJSExecClasspath.value,
+ launcher, streams.value.log, scalaJSConsole.value)
+
+ runner.run()
+ }
+ }
+ ).dependsOn(compiler % "plugin", javalibEx, testSuite % "test->test")
+
+ lazy val plugin: Project = Project(
+ id = "sbtPlugin",
+ base = file("sbt-plugin"),
+ settings = commonSettings ++ publishSettings ++ Seq(
+ name := "Scala.js sbt plugin",
+ sbtPlugin := true,
+ scalaBinaryVersion :=
+ CrossVersion.binaryScalaVersion(scalaVersion.value),
+ libraryDependencies ++= Seq(
+ "org.mozilla" % "rhino" % "1.7R4",
+ "org.webjars" % "envjs" % "1.2",
+ "com.novocode" % "junit-interface" % "0.9" % "test"
+ ) ++ ScalaJSPluginInternal.phantomJSJettyModules.map(_ % "provided")
+ )
+ ).dependsOn(tools)
+
+ lazy val delambdafySetting = {
+ scalacOptions ++= (
+ if (scalaBinaryVersion.value == "2.10") Seq()
+ else Seq("-Ydelambdafy:method"))
+ }
+
+ private def serializeHardcodedIR(base: File,
+ infoAndTree: (ir.Infos.ClassInfo, ir.Trees.ClassDef)): File = {
+ // We assume that there are no weird characters in the full name
+ val fullName = infoAndTree._1.name
+ val output = base / (fullName.replace('.', '/') + ".sjsir")
+
+ if (!output.exists()) {
+ IO.createDirectory(output.getParentFile)
+ val stream = new BufferedOutputStream(new FileOutputStream(output))
+ try {
+ ir.InfoSerializers.serialize(stream, infoAndTree._1)
+ ir.Serializers.serialize(stream, infoAndTree._2)
+ } finally {
+ stream.close()
+ }
+ }
+ output
+ }
+
+ lazy val javalanglib: Project = Project(
+ id = "javalanglib",
+ base = file("javalanglib"),
+ settings = defaultSettings ++ myScalaJSSettings ++ Seq(
+ name := "java.lang library for Scala.js",
+ publishArtifact in Compile := false,
+ delambdafySetting,
+ scalacOptions += "-Yskip:cleanup,icode,jvm",
+ scalaJSSourceMapSettings,
+ compileWithLibrarySetting,
+
+ resourceGenerators in Compile <+= Def.task {
+ val base = (resourceManaged in Compile).value
+ Seq(
+ serializeHardcodedIR(base, JavaLangObject.InfoAndTree),
+ serializeHardcodedIR(base, JavaLangString.InfoAndTree)
+ )
+ }
+ ) ++ (
+ scalaJSExternalCompileSettings
+ )
+ ).dependsOn(compiler % "plugin")
+
+ lazy val javalib: Project = Project(
+ id = "javalib",
+ base = file("javalib"),
+ settings = defaultSettings ++ myScalaJSSettings ++ Seq(
+ name := "Java library for Scala.js",
+ publishArtifact in Compile := false,
+ delambdafySetting,
+ scalacOptions += "-Yskip:cleanup,icode,jvm",
+ scalaJSSourceMapSettings,
+ compileWithLibrarySetting
+ ) ++ (
+ scalaJSExternalCompileSettings
+ )
+ ).dependsOn(compiler % "plugin")
+
+ lazy val scalalib: Project = Project(
+ id = "scalalib",
+ base = file("scalalib"),
+ settings = defaultSettings ++ myScalaJSSettings ++ Seq(
+ name := "Scala library for Scala.js",
+ publishArtifact in Compile := false,
+ delambdafySetting,
+ compileWithLibrarySetting,
+
+ // The Scala lib is full of warnings we don't want to see
+ scalacOptions ~= (_.filterNot(
+ Set("-deprecation", "-unchecked", "-feature") contains _)),
+
+
+ scalacOptions ++= List(
+ // Do not generate .class files
+ "-Yskip:cleanup,icode,jvm",
+ // Tell plugin to hack fix bad classOf trees
+ "-P:scalajs:fixClassOf",
+ // Link source maps to github sources of original Scalalib
+ "-P:scalajs:mapSourceURI:" +
+ fetchedScalaSourceDir.value.toURI +
+ "->https://raw.githubusercontent.com/scala/scala/v" +
+ scalaVersion.value + "/"
+ ),
+
+ // Link sources in override directories to our GitHub repo
+ scalaJSSourceMapSettings,
+
+ fetchedScalaSourceDir := (
+ baseDirectory.value / "fetchedSources" /
+ scalaVersion.value
+ ),
+
+ fetchScalaSource := {
+ val s = streams.value
+ val cacheDir = s.cacheDirectory
+ val ver = scalaVersion.value
+ val trgDir = fetchedScalaSourceDir.value
+
+ val report = updateClassifiers.value
+ val scalaLibSourcesJar = report.select(
+ configuration = Set("compile"),
+ module = moduleFilter(name = "scala-library"),
+ artifact = artifactFilter(`type` = "src")).headOption.getOrElse {
+ sys.error(s"Could not fetch scala-library sources for version $ver")
+ }
+
+ FileFunction.cached(cacheDir / s"fetchScalaSource-$ver",
+ FilesInfo.lastModified, FilesInfo.exists) { dependencies =>
+ s.log.info(s"Fetching Scala library sources to $trgDir...")
+
+ if (trgDir.exists)
+ IO.delete(trgDir)
+ IO.createDirectory(trgDir)
+ IO.unzip(scalaLibSourcesJar, trgDir)
+ } (Set(scalaLibSourcesJar))
+
+ trgDir
+ },
+
+ unmanagedSourceDirectories in Compile := {
+ // Calculates all prefixes of the current Scala version
+ // (including the empty prefix) to construct override
+ // directories like the following:
+ // - override-2.10.2-RC1
+ // - override-2.10.2
+ // - override-2.10
+ // - override-2
+ // - override
+ val ver = scalaVersion.value
+ val base = baseDirectory.value
+ val parts = ver.split(Array('.','-'))
+ val verList = parts.inits.map { ps =>
+ val len = ps.mkString(".").length
+ // re-read version, since we lost '.' and '-'
+ ver.substring(0, len)
+ }
+ def dirStr(v: String) =
+ if (v.isEmpty) "overrides" else s"overrides-$v"
+ val dirs = verList.map(base / dirStr(_)).filter(_.exists)
+ dirs.toSeq // most specific shadow less specific
+ },
+
+ // Compute sources
+ // Files in earlier src dirs shadow files in later dirs
+ sources in Compile := {
+ // Sources coming from the sources of Scala
+ val scalaSrcDir = fetchScalaSource.value
+
+ // All source directories (overrides shadow scalaSrcDir)
+ val sourceDirectories =
+ (unmanagedSourceDirectories in Compile).value :+ scalaSrcDir
+
+ // Filter sources with overrides
+ def normPath(f: File): String =
+ f.getPath.replace(java.io.File.separator, "/")
+
+ val sources = mutable.ListBuffer.empty[File]
+ val paths = mutable.Set.empty[String]
+
+ for {
+ srcDir <- sourceDirectories
+ normSrcDir = normPath(srcDir)
+ src <- (srcDir ** "*.scala").get
+ } {
+ val normSrc = normPath(src)
+ val path = normSrc.substring(normSrcDir.length)
+ val useless =
+ path.contains("/scala/collection/parallel/") ||
+ path.contains("/scala/util/parsing/")
+ if (!useless) {
+ if (paths.add(path))
+ sources += src
+ else
+ streams.value.log.debug(s"not including $src")
+ }
+ }
+
+ sources.result()
+ },
+
+ // Continuation plugin (when using 2.10.x)
+ autoCompilerPlugins := true,
+ libraryDependencies ++= {
+ val ver = scalaVersion.value
+ if (ver.startsWith("2.10."))
+ Seq(compilerPlugin("org.scala-lang.plugins" % "continuations" % ver))
+ else
+ Nil
+ },
+ scalacOptions ++= {
+ if (scalaVersion.value.startsWith("2.10."))
+ Seq("-P:continuations:enable")
+ else
+ Nil
+ }
+ ) ++ (
+ scalaJSExternalCompileSettings
+ )
+ ).dependsOn(compiler % "plugin")
+
+ lazy val libraryAux: Project = Project(
+ id = "libraryAux",
+ base = file("library-aux"),
+ settings = defaultSettings ++ myScalaJSSettings ++ Seq(
+ name := "Scala.js aux library",
+ publishArtifact in Compile := false,
+ delambdafySetting,
+ scalacOptions += "-Yskip:cleanup,icode,jvm",
+ scalaJSSourceMapSettings,
+ compileWithLibrarySetting
+ ) ++ (
+ scalaJSExternalCompileSettings
+ )
+ ).dependsOn(compiler % "plugin")
+
+ lazy val library: Project = Project(
+ id = "library",
+ base = file("library"),
+ settings = defaultSettings ++ publishSettings ++ myScalaJSSettings ++ Seq(
+ name := "Scala.js library",
+ delambdafySetting,
+ scalaJSSourceMapSettings,
+ scalacOptions in (Compile, doc) += "-implicits",
+ exportJars := true
+ ) ++ (
+ scalaJSExternalCompileSettings
+ ) ++ inConfig(Compile)(Seq(
+ /* Add the .sjsir files from other lib projects
+ * (but not .class files)
+ */
+ mappings in packageBin ++= {
+ val allProducts = (
+ (products in javalanglib).value ++
+ (products in javalib).value ++
+ (products in scalalib).value ++
+ (products in libraryAux).value)
+ val filter = ("*.sjsir": NameFilter)
+ allProducts.flatMap(base => Path.selectSubpaths(base, filter))
+ }
+ ))
+ ).dependsOn(compiler % "plugin")
+
+ lazy val javalibEx: Project = Project(
+ id = "javalibEx",
+ base = file("javalib-ex"),
+ settings = defaultSettings ++ publishSettings ++ myScalaJSSettings ++ Seq(
+ name := "Scala.js JavaLib Ex",
+ delambdafySetting,
+ scalacOptions += "-Yskip:cleanup,icode,jvm",
+ scalaJSSourceMapSettings,
+ exportJars := true,
+ jsDependencies +=
+ "org.webjars" % "jszip" % "2.4.0" / "jszip.min.js" commonJSName "JSZip"
+ ) ++ (
+ scalaJSExternalCompileSettings
+ )
+ ).dependsOn(compiler % "plugin", library)
+
+ lazy val stubs: Project = Project(
+ id = "stubs",
+ base = file("stubs"),
+ settings = defaultSettings ++ publishSettings ++ Seq(
+ name := "Scala.js Stubs"
+ )
+ )
+
+ // Scala.js command line interface
+ lazy val cli: Project = Project(
+ id = "cli",
+ base = file("cli"),
+ settings = defaultSettings ++ assemblySettings ++ Seq(
+ name := "Scala.js CLI",
+ scalaVersion := "2.10.4", // adapt version to tools
+ libraryDependencies ++= Seq(
+ "com.github.scopt" %% "scopt" % "3.2.0"
+ ),
+
+ // assembly options
+ mainClass in assembly := None, // don't want an executable JAR
+ assemblyOption in assembly ~= { _.copy(includeScala = false) },
+ AssemblyKeys.jarName in assembly :=
+ s"${normalizedName.value}-assembly_${scalaBinaryVersion.value}-${version.value}.jar"
+ )
+ ).dependsOn(tools)
+
+ // Test framework
+ lazy val testBridge = Project(
+ id = "testBridge",
+ base = file("test-bridge"),
+ settings = defaultSettings ++ publishSettings ++ myScalaJSSettings ++ Seq(
+ name := "Scala.js test bridge",
+ delambdafySetting,
+ scalaJSSourceMapSettings
+ )
+ ).dependsOn(compiler % "plugin", library)
+
+ lazy val jasmineTestFramework = Project(
+ id = "jasmineTestFramework",
+ base = file("jasmine-test-framework"),
+ settings = defaultSettings ++ publishSettings ++ myScalaJSSettings ++ Seq(
+ name := "Scala.js jasmine test framework",
+
+ jsDependencies ++= Seq(
+ ProvidedJS / "jasmine-polyfills.js",
+ "org.webjars" % "jasmine" % "1.3.1" /
+ "jasmine.js" dependsOn "jasmine-polyfills.js"
+ ),
+ scalaJSSourceMapSettings
+ )
+ ).dependsOn(compiler % "plugin", library, testBridge)
+
+ // Examples
+
+ lazy val examples: Project = Project(
+ id = "examples",
+ base = file("examples"),
+ settings = defaultSettings ++ Seq(
+ name := "Scala.js examples"
+ )
+ ).aggregate(helloworld, reversi, testingExample)
+
+ lazy val exampleSettings = defaultSettings ++ myScalaJSSettings
+
+ lazy val helloworld: Project = Project(
+ id = "helloworld",
+ base = file("examples") / "helloworld",
+ settings = exampleSettings ++ Seq(
+ name := "Hello World - Scala.js example",
+ moduleName := "helloworld",
+ persistLauncher := true
+ )
+ ).dependsOn(compiler % "plugin", library)
+
+ lazy val reversi = Project(
+ id = "reversi",
+ base = file("examples") / "reversi",
+ settings = exampleSettings ++ Seq(
+ name := "Reversi - Scala.js example",
+ moduleName := "reversi"
+ )
+ ).dependsOn(compiler % "plugin", library)
+
+ lazy val testingExample = Project(
+ id = "testingExample",
+ base = file("examples") / "testing",
+ settings = exampleSettings ++ Seq(
+ name := "Testing - Scala.js example",
+ moduleName := "testing",
+
+ jsDependencies ++= Seq(
+ RuntimeDOM % "test",
+ "org.webjars" % "jquery" % "1.10.2" / "jquery.js" % "test"
+ )
+ )
+ ).dependsOn(compiler % "plugin", library, jasmineTestFramework % "test")
+
+ // Testing
+
+ lazy val testSuite: Project = Project(
+ id = "testSuite",
+ base = file("test-suite"),
+ settings = defaultSettings ++ myScalaJSSettings ++ Seq(
+ name := "Scala.js test suite",
+ publishArtifact in Compile := false,
+
+ scalacOptions ~= (_.filter(_ != "-deprecation")),
+
+ sources in Test ++= {
+ if (!scalaVersion.value.startsWith("2.10") &&
+ scalacOptions.value.contains("-Xexperimental")) {
+ (((sourceDirectory in Test).value / "require-sam") ** "*.scala").get
+ } else {
+ Nil
+ }
+ },
+
+ /* Generate a scala source file that throws exceptions in
+ various places (while attaching the source line to the
+ exception). When we catch the exception, we can then
+ compare the attached source line and the source line
+ calculated via the source maps.
+
+ see test-suite/src/test/resources/SourceMapTestTemplate.scala
+ */
+ sourceGenerators in Test <+= Def.task {
+ val dir = (sourceManaged in Test).value
+ IO.createDirectory(dir)
+
+ val template = IO.read((resourceDirectory in Test).value /
+ "SourceMapTestTemplate.scala")
+
+ def lineNo(cs: CharSequence) =
+ (0 until cs.length).count(i => cs.charAt(i) == '\n') + 1
+
+ var i = 0
+ val pat = "/\\*{2,3}/".r
+ val replaced = pat.replaceAllIn(template, { mat =>
+ val lNo = lineNo(mat.before)
+ val res =
+ if (mat.end - mat.start == 5)
+ // matching a /***/
+ s"if (TC.is($i)) { throw new TestException($lNo) } else "
+ else
+ // matching a /**/
+ s"; if (TC.is($i)) { throw new TestException($lNo) } ;"
+
+ i += 1
+
+ res
+ })
+
+ val outFile = dir / "SourceMapTest.scala"
+ IO.write(outFile, replaced.replace("0/*<testCount>*/", i.toString))
+ Seq(outFile)
+ }
+ )
+ ).dependsOn(compiler % "plugin", library, jasmineTestFramework % "test")
+
+ lazy val noIrCheckTest: Project = Project(
+ id = "noIrCheckTest",
+ base = file("no-ir-check-test"),
+ settings = defaultSettings ++ myScalaJSSettings ++ Seq(
+ name := "Scala.js not IR checked tests",
+ scalaJSOptimizerOptions ~= (_.withCheckScalaJSIR(false)),
+ publishArtifact in Compile := false
+ )
+ ).dependsOn(compiler % "plugin", library, jasmineTestFramework % "test")
+
+ lazy val javalibExTestSuite: Project = Project(
+ id = "javalibExTestSuite",
+ base = file("javalib-ex-test-suite"),
+ settings = defaultSettings ++ myScalaJSSettings ++ Seq(
+ name := "JavaLib Ex Test Suite",
+ publishArtifact in Compile := false,
+
+ scalacOptions in Test ~= (_.filter(_ != "-deprecation"))
+ )
+ ).dependsOn(compiler % "plugin", javalibEx, jasmineTestFramework % "test")
+
+ lazy val partest: Project = Project(
+ id = "partest",
+ base = file("partest"),
+ settings = defaultSettings ++ Seq(
+ name := "Partest for Scala.js",
+ moduleName := "scalajs-partest",
+
+ resolvers += Resolver.typesafeIvyRepo("releases"),
+
+ fetchedScalaSourceDir := (
+ baseDirectory.value / "fetchedSources" /
+ scalaVersion.value
+ ),
+
+ fetchScalaSource := {
+ import org.eclipse.jgit.api._
+
+ val s = streams.value
+ val ver = scalaVersion.value
+ val trgDir = fetchedScalaSourceDir.value
+
+ if (!trgDir.exists) {
+ s.log.info(s"Fetching Scala source version $ver")
+
+ // Make parent dirs and stuff
+ IO.createDirectory(trgDir)
+
+ // Clone scala source code
+ new CloneCommand()
+ .setDirectory(trgDir)
+ .setURI("https://github.com/scala/scala.git")
+ .call()
+ }
+
+ // Checkout proper ref. We do this anyway so we fail if
+ // something is wrong
+ val git = Git.open(trgDir)
+ s.log.info(s"Checking out Scala source version $ver")
+ git.checkout().setName(s"v$ver").call()
+
+ trgDir
+ },
+
+ libraryDependencies ++= {
+ if (shouldPartest.value)
+ Seq(
+ "org.scala-sbt" % "sbt" % sbtVersion.value,
+ "org.scala-lang.modules" %% "scala-partest" % "1.0.1",
+ "com.google.javascript" % "closure-compiler" % "v20130603",
+ "org.mozilla" % "rhino" % "1.7R4",
+ "com.googlecode.json-simple" % "json-simple" % "1.1.1"
+ )
+ else Seq()
+ },
+
+ sources in Compile := {
+ if (shouldPartest.value) {
+ // Partest sources and some sources of sbtplugin (see above)
+ val baseSrcs = (sources in Compile).value
+ // Sources for tools (and hence IR)
+ val toolSrcs = (sources in (tools, Compile)).value
+ // Individual sources from the sbtplugin
+ val pluginSrcs = {
+ val pluginBase = ((scalaSource in (plugin, Compile)).value /
+ "scala/scalajs/sbtplugin")
+
+ val scalaFilter: FileFilter = "*.scala"
+ val files = (
+ (pluginBase * "JSUtils.scala") +++
+ (pluginBase / "env" * scalaFilter) +++
+ (pluginBase / "env" / "nodejs" ** scalaFilter) +++
+ (pluginBase / "env" / "rhino" ** scalaFilter))
+
+ files.get
+ }
+ toolSrcs ++ baseSrcs ++ pluginSrcs
+ } else Seq()
+ }
+
+ )
+ ).dependsOn(compiler)
+
+ lazy val partestSuite: Project = Project(
+ id = "partestSuite",
+ base = file("partest-suite"),
+ settings = defaultSettings ++ Seq(
+ name := "Scala.js partest suite",
+
+ fork in Test := true,
+ javaOptions in Test += "-Xmx1G",
+
+ testFrameworks ++= {
+ if (shouldPartest.value)
+ Seq(new TestFramework("scala.tools.partest.scalajs.Framework"))
+ else Seq()
+ },
+
+ definedTests in Test <++= Def.taskDyn[Seq[sbt.TestDefinition]] {
+ if (shouldPartest.value) Def.task {
+ val _ = (fetchScalaSource in partest).value
+ Seq(new sbt.TestDefinition(
+ s"partest-${scalaVersion.value}",
+ // marker fingerprint since there are no test classes
+ // to be discovered by sbt:
+ new sbt.testing.AnnotatedFingerprint {
+ def isModule = true
+ def annotationName = "partest"
+ },
+ true,
+ Array()
+ ))
+ } else {
+ Def.task(Seq())
+ }
+ }
+ )
+ ).dependsOn(partest % "test", library)
+}
diff --git a/examples/scala-js/project/build.properties b/examples/scala-js/project/build.properties
new file mode 100644
index 0000000..64abd37
--- /dev/null
+++ b/examples/scala-js/project/build.properties
@@ -0,0 +1 @@
+sbt.version=0.13.6
diff --git a/examples/scala-js/project/build.sbt b/examples/scala-js/project/build.sbt
new file mode 100644
index 0000000..b110ef2
--- /dev/null
+++ b/examples/scala-js/project/build.sbt
@@ -0,0 +1,48 @@
+resolvers += Resolver.url(
+ "bintray-sbt-plugin-releases",
+ url("http://dl.bintray.com/content/sbt/sbt-plugin-releases"))(
+ Resolver.ivyStylePatterns)
+
+addSbtPlugin("me.lessis" % "bintray-sbt" % "0.1.2")
+
+addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.11.2")
+
+libraryDependencies += "com.google.javascript" % "closure-compiler" % "v20130603"
+
+libraryDependencies += "org.mozilla" % "rhino" % "1.7R4"
+
+libraryDependencies += "org.webjars" % "envjs" % "1.2"
+
+libraryDependencies += "org.eclipse.jgit" % "org.eclipse.jgit.pgm" % "3.2.0.201312181205-r"
+
+libraryDependencies += "com.googlecode.json-simple" % "json-simple" % "1.1.1"
+
+libraryDependencies += "org.eclipse.jetty" % "jetty-websocket" % "8.1.16.v20140903"
+
+libraryDependencies += "org.eclipse.jetty" % "jetty-server" % "8.1.16.v20140903"
+
+
+unmanagedSourceDirectories in Compile ++= {
+ val root = baseDirectory.value.getParentFile
+ Seq(
+ root / "ir/src/main/scala",
+ root / "tools/shared/src/main/scala",
+ root / "tools/jvm/src/main/scala",
+ root / "sbt-plugin/src/main/scala"
+ )
+}
+
+// Add the ScalaJSEnvGenerator to the build (its in the build of the build)
+sources in Compile +=
+ baseDirectory.value / "project" / "ScalaJSEnvGenerator.scala"
+
+sourceGenerators in Compile <+= Def.task {
+ ScalaJSEnvGenerator.generateEnvHolder(
+ baseDirectory.value.getParentFile / "tools",
+ (sourceManaged in Compile).value)
+}
+
+unmanagedResourceDirectories in Compile += {
+ val root = baseDirectory.value.getParentFile
+ root / "tools/src/main/resources"
+}
diff --git a/examples/scala-js/project/project/ScalaJSEnvGenerator.scala b/examples/scala-js/project/project/ScalaJSEnvGenerator.scala
new file mode 100644
index 0000000..a36479f
--- /dev/null
+++ b/examples/scala-js/project/project/ScalaJSEnvGenerator.scala
@@ -0,0 +1,31 @@
+import sbt._
+
+object ScalaJSEnvGenerator {
+
+ /** Generate a *.scala file that contains the scalajsenv as literal string
+ *
+ * We need this so the tools don't rely on I/O and/or resources.
+ */
+ def generateEnvHolder(baseDir: File, sourceDir: File): Seq[File] = {
+ val trg = sourceDir / "ScalaJSEnvHolder.scala"
+ val env = baseDir / "scalajsenv.js"
+
+ if (!trg.exists() || trg.lastModified() < env.lastModified()) {
+ val scalajsenv = IO.read(env).replaceAllLiterally("$", "$$")
+
+ val scalaCode =
+ s"""
+ package scala.scalajs.tools.corelib
+
+ private[corelib] object ScalaJSEnvHolder {
+ final val scalajsenv = raw\"\"\"$scalajsenv\"\"\"
+ }
+ """
+
+ IO.write(trg, scalaCode)
+ }
+
+ Seq(trg)
+ }
+
+}
diff --git a/examples/scala-js/sbt-plugin-test/README.md b/examples/scala-js/sbt-plugin-test/README.md
new file mode 100644
index 0000000..71414a3
--- /dev/null
+++ b/examples/scala-js/sbt-plugin-test/README.md
@@ -0,0 +1,7 @@
+This is a standalone SBT project to test the Scala.js SBT plugin as it
+will be used by the users. (Through normal SBT dependency
+management). This needs the Scala.js artifacts to be published
+locally.
+
+It has two subprojects, to test Scala.js with and without DOM. Both
+subprojects have a main class and a test.
diff --git a/examples/scala-js/sbt-plugin-test/build.sbt b/examples/scala-js/sbt-plugin-test/build.sbt
new file mode 100644
index 0000000..dd2e12e
--- /dev/null
+++ b/examples/scala-js/sbt-plugin-test/build.sbt
@@ -0,0 +1,44 @@
+import scala.scalajs.sbtplugin.RuntimeDOM
+
+name := "Scala.js sbt test"
+
+version := scalaJSVersion
+
+val baseSettings = Seq(
+ version := scalaJSVersion,
+ scalaVersion := "2.11.2",
+ libraryDependencies +=
+ "org.scala-lang.modules.scalajs" %% "scalajs-jasmine-test-framework" % scalaJSVersion % "test"
+)
+
+lazy val root = project.in(file(".")).
+ aggregate(noDOM, withDOM)
+
+lazy val noDOM = project.settings(baseSettings: _*).
+ enablePlugins(ScalaJSPlugin).
+ settings(
+ name := "Scala.js sbt test w/o DOM"
+ )
+
+lazy val withDOM = project.settings(baseSettings: _*).
+ enablePlugins(ScalaJSPlugin).
+ settings(
+ name := "Scala.js sbt test w/ DOM",
+ jsDependencies ++= Seq(
+ RuntimeDOM,
+ "org.webjars" % "jquery" % "1.10.2" / "jquery.js"
+ )
+ )
+
+lazy val jetty9 = project.settings(baseSettings: _*).
+ enablePlugins(ScalaJSPlugin).
+ settings(
+ name := "Scala.js sbt test with jetty9 on classpath",
+ jsDependencies ++= Seq(
+ RuntimeDOM,
+ "org.webjars" % "jquery" % "1.10.2" / "jquery.js"
+ ),
+ // Use PhantomJS, allow cross domain requests
+ postLinkJSEnv := PhantomJSEnv(args = Seq("--web-security=no")).value,
+ Jetty9Test.runSetting
+ )
diff --git a/examples/scala-js/sbt-plugin-test/jetty9/src/main/resources/test.txt b/examples/scala-js/sbt-plugin-test/jetty9/src/main/resources/test.txt
new file mode 100644
index 0000000..68300b8
--- /dev/null
+++ b/examples/scala-js/sbt-plugin-test/jetty9/src/main/resources/test.txt
@@ -0,0 +1 @@
+It works!
diff --git a/examples/scala-js/sbt-plugin-test/noDOM/src/main/scala/sbttest/noDOM/Lib.scala b/examples/scala-js/sbt-plugin-test/noDOM/src/main/scala/sbttest/noDOM/Lib.scala
new file mode 100644
index 0000000..884c422
--- /dev/null
+++ b/examples/scala-js/sbt-plugin-test/noDOM/src/main/scala/sbttest/noDOM/Lib.scala
@@ -0,0 +1,11 @@
+package sbttest.noDOM
+
+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/examples/scala-js/sbt-plugin-test/noDOM/src/main/scala/sbttest/noDOM/TestApp.scala b/examples/scala-js/sbt-plugin-test/noDOM/src/main/scala/sbttest/noDOM/TestApp.scala
new file mode 100644
index 0000000..16a4cbe
--- /dev/null
+++ b/examples/scala-js/sbt-plugin-test/noDOM/src/main/scala/sbttest/noDOM/TestApp.scala
@@ -0,0 +1,12 @@
+package sbttest.noDOM
+
+import scala.scalajs.js
+
+object TestApp extends js.JSApp {
+
+ def main(): Unit = {
+ println(Lib.foo("Hello World"))
+ println(Lib.sq(10))
+ }
+
+}
diff --git a/examples/scala-js/sbt-plugin-test/noDOM/src/test/scala/sbttest/noDOM/LibTest.scala b/examples/scala-js/sbt-plugin-test/noDOM/src/test/scala/sbttest/noDOM/LibTest.scala
new file mode 100644
index 0000000..bc24eda
--- /dev/null
+++ b/examples/scala-js/sbt-plugin-test/noDOM/src/test/scala/sbttest/noDOM/LibTest.scala
@@ -0,0 +1,19 @@
+package sbttest.noDOM
+
+import org.scalajs.jasminetest.JasmineTest
+
+object LibTest extends JasmineTest {
+
+ describe("Dummy Library") {
+ it("should provide `foo`") {
+ expect(Lib.foo("")).toEqual("foo")
+ expect(Lib.foo("a")).toEqual("afoo")
+ }
+
+ it("should provide `sq`") {
+ expect(Lib.sq(0)).toEqual(0)
+ expect(Lib.sq(10)).toEqual(100)
+ }
+ }
+
+}
diff --git a/examples/scala-js/sbt-plugin-test/project/Jetty9Test.scala b/examples/scala-js/sbt-plugin-test/project/Jetty9Test.scala
new file mode 100644
index 0000000..6a84114
--- /dev/null
+++ b/examples/scala-js/sbt-plugin-test/project/Jetty9Test.scala
@@ -0,0 +1,83 @@
+import sbt._
+import Keys._
+
+import scala.scalajs.sbtplugin._
+import ScalaJSPlugin.autoImport._
+import Implicits._
+
+import scala.scalajs.tools.env._
+import scala.scalajs.tools.io._
+
+import org.eclipse.jetty.server._
+import org.eclipse.jetty.server.handler._
+import org.eclipse.jetty.util.component._
+
+import java.io.File
+
+object Jetty9Test {
+
+ private val jettyPort = 23548
+
+ val runSetting = run <<= Def.inputTask {
+ val env = (jsEnv in Compile).value.asInstanceOf[ComJSEnv]
+ val cp = (scalaJSExecClasspath in Compile).value
+ val jsConsole = scalaJSConsole.value
+
+ val code = new MemVirtualJSFile("runner.js").withContent(
+ """
+ scalajsCom.init(function(msg) {
+ jQuery.ajax({
+ url: msg,
+ success: function(dat) {
+ scalajsCom.send(dat.trim());
+ scalajsCom.close();
+ },
+ error: function() {
+ scalajsCom.send("failed!");
+ scalajsCom.close();
+ }
+ });
+ });
+ """
+ )
+
+ val runner = env.comRunner(cp, code, streams.value.log, jsConsole)
+
+ runner.start()
+
+ val jetty = setupJetty((resourceDirectory in Compile).value)
+
+ jetty.addLifeCycleListener(new AbstractLifeCycle.AbstractLifeCycleListener {
+ override def lifeCycleStarted(event: LifeCycle): Unit = {
+ try {
+ runner.send(s"http://localhost:$jettyPort/test.txt")
+ val msg = runner.receive()
+ val expected = "It works!"
+ if (msg != expected)
+ sys.error(s"""received "$msg" instead of "$expected"""")
+ } finally {
+ runner.close()
+ jetty.stop()
+ }
+ }
+ })
+
+ jetty.start()
+ runner.await()
+ jetty.join()
+ }
+
+ private def setupJetty(dir: File): Server = {
+ val server = new Server(jettyPort)
+
+ val resource_handler = new ResourceHandler()
+ resource_handler.setResourceBase(dir.getAbsolutePath)
+
+ val handlers = new HandlerList()
+ handlers.setHandlers(Array(resource_handler, new DefaultHandler()))
+ server.setHandler(handlers)
+
+ server
+ }
+
+}
diff --git a/examples/scala-js/sbt-plugin-test/project/build.properties b/examples/scala-js/sbt-plugin-test/project/build.properties
new file mode 100644
index 0000000..64abd37
--- /dev/null
+++ b/examples/scala-js/sbt-plugin-test/project/build.properties
@@ -0,0 +1 @@
+sbt.version=0.13.6
diff --git a/examples/scala-js/sbt-plugin-test/project/build.sbt b/examples/scala-js/sbt-plugin-test/project/build.sbt
new file mode 100644
index 0000000..8419289
--- /dev/null
+++ b/examples/scala-js/sbt-plugin-test/project/build.sbt
@@ -0,0 +1,4 @@
+addSbtPlugin("org.scala-lang.modules.scalajs" % "scalajs-sbt-plugin" %
+ scala.scalajs.ir.ScalaJSVersions.current)
+
+libraryDependencies += "org.eclipse.jetty" % "jetty-server" % "9.2.3.v20140905"
diff --git a/examples/scala-js/sbt-plugin-test/project/project/build.sbt b/examples/scala-js/sbt-plugin-test/project/project/build.sbt
new file mode 100644
index 0000000..fb20cb7
--- /dev/null
+++ b/examples/scala-js/sbt-plugin-test/project/project/build.sbt
@@ -0,0 +1 @@
+sources in Compile += baseDirectory.value / "../../../ir/src/main/scala/scala/scalajs/ir/ScalaJSVersions.scala"
diff --git a/examples/scala-js/sbt-plugin-test/withDOM/src/main/scala/sbttest/withDOM/Lib.scala b/examples/scala-js/sbt-plugin-test/withDOM/src/main/scala/sbttest/withDOM/Lib.scala
new file mode 100644
index 0000000..e431557
--- /dev/null
+++ b/examples/scala-js/sbt-plugin-test/withDOM/src/main/scala/sbttest/withDOM/Lib.scala
@@ -0,0 +1,28 @@
+package sbttest.withDOM
+
+import scala.scalajs.js
+
+object Lib {
+
+ val document: js.Dynamic = js.Dynamic.global.document
+ val jQuery: js.Dynamic = js.Dynamic.global.jQuery
+
+ def getElementsByTagName(name: String): js.Array[js.Dynamic] =
+ document.getElementsByTagName(name).asInstanceOf[js.Array[js.Dynamic]]
+
+ /** appends a <p> with the message to the document */
+ def appendDocument(msg: String): Unit = {
+ val trg = {
+ val bodies = getElementsByTagName("body")
+ if (bodies.length > 0)
+ bodies(0)
+ else
+ document
+ }
+
+ val elem = document.createElement("p")
+ elem.appendChild(document.createTextNode(msg))
+ trg.appendChild(elem)
+ }
+
+}
diff --git a/examples/scala-js/sbt-plugin-test/withDOM/src/main/scala/sbttest/withDOM/TestApp.scala b/examples/scala-js/sbt-plugin-test/withDOM/src/main/scala/sbttest/withDOM/TestApp.scala
new file mode 100644
index 0000000..e61ed20
--- /dev/null
+++ b/examples/scala-js/sbt-plugin-test/withDOM/src/main/scala/sbttest/withDOM/TestApp.scala
@@ -0,0 +1,14 @@
+package sbttest.withDOM
+
+import scala.scalajs.js
+
+object TestApp extends js.JSApp {
+
+ def main(): Unit = {
+ Lib.appendDocument("Hello World")
+ Lib.appendDocument("Still Here!")
+
+ println(Lib.jQuery("p").text())
+ }
+
+}
diff --git a/examples/scala-js/sbt-plugin-test/withDOM/src/test/scala/sbttest/withDOM/LibTest.scala b/examples/scala-js/sbt-plugin-test/withDOM/src/test/scala/sbttest/withDOM/LibTest.scala
new file mode 100644
index 0000000..e08e6e3
--- /dev/null
+++ b/examples/scala-js/sbt-plugin-test/withDOM/src/test/scala/sbttest/withDOM/LibTest.scala
@@ -0,0 +1,24 @@
+package sbttest.withDOM
+
+import scala.scalajs.js
+
+import org.scalajs.jasminetest.JasmineTest
+
+object LibTest extends JasmineTest {
+
+ describe("Dummy Library") {
+
+ it("should provide jQuery") {
+ expect(Lib.jQuery).toBeDefined
+ }
+
+ it("should append an element") {
+ def count = Lib.jQuery("p").length.asInstanceOf[Int]
+ val oldCount = count
+ Lib.appendDocument("foo")
+ expect(count - oldCount).toEqual(1)
+ }
+
+ }
+
+}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/AbstractJSDeps.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/AbstractJSDeps.scala
new file mode 100644
index 0000000..9eb7f69
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/AbstractJSDeps.scala
@@ -0,0 +1,81 @@
+package scala.scalajs.sbtplugin
+
+import sbt._
+
+import StringUtilities.nonEmpty
+
+import scala.scalajs.tools.jsdep.JSDependency
+
+/** Something JavaScript related a project may depend on. Either a JavaScript
+ * module/library, or the DOM at runtime. */
+sealed trait AbstractJSDep {
+ def configurations: Option[String]
+
+ protected def withConfigs(configs: Option[String]): AbstractJSDep
+
+ def %(configurations: Configuration): AbstractJSDep = %(configurations.name)
+ def %(configurations: String): AbstractJSDep = {
+ require(this.configurations.isEmpty,
+ "Configurations already specified for jsModule " + this)
+ nonEmpty(configurations, "Configurations")
+ withConfigs(Some(configurations))
+ }
+
+}
+
+/** A JavaScript module/library a Scala.js project may depend on */
+sealed trait JSModuleID extends AbstractJSDep {
+ def jsDep: JSDependency
+
+ protected def withJSDep(jsDep: JSDependency): JSModuleID
+
+ def commonJSName(name: String): JSModuleID =
+ withJSDep(jsDep = jsDep.commonJSName(name))
+
+ def dependsOn(names: String*): JSModuleID =
+ withJSDep(jsDep = jsDep.dependsOn(names: _*))
+}
+
+/** A JavaScript module that resides inside a jar (probably webjar) */
+final case class JarJSModuleID(
+ module: ModuleID,
+ jsDep: JSDependency) extends JSModuleID {
+
+ def configurations: Option[String] = module.configurations
+
+ protected def withConfigs(configs: Option[String]): JSModuleID =
+ copy(module = module.copy(configurations = configs))
+ protected def withJSDep(jsDep: JSDependency): JSModuleID =
+ copy(jsDep = jsDep)
+}
+
+object JarJSModuleID {
+ def apply(module: ModuleID, name: String): JarJSModuleID =
+ JarJSModuleID(module, new JSDependency(name, Nil))
+}
+
+/** A JavaScript module that we depend on, but is provided externally or
+ * by the project itself */
+final case class ProvidedJSModuleID(
+ jsDep: JSDependency,
+ configurations: Option[String]) extends JSModuleID {
+
+ protected def withConfigs(configs: Option[String]): JSModuleID =
+ copy(configurations = configs)
+ protected def withJSDep(jsDep: JSDependency): JSModuleID =
+ copy(jsDep = jsDep)
+}
+
+object ProvidedJSModuleID {
+ def apply(name: String, configurations: Option[String]): ProvidedJSModuleID =
+ ProvidedJSModuleID(new JSDependency(name, Nil), configurations)
+}
+
+sealed case class RuntimeDOM(
+ configurations: Option[String]) extends AbstractJSDep {
+
+ protected def withConfigs(configs: Option[String]): RuntimeDOM =
+ copy(configurations = configs)
+}
+
+object RuntimeDOM extends RuntimeDOM(None)
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/Implicits.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/Implicits.scala
new file mode 100644
index 0000000..0c1559f
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/Implicits.scala
@@ -0,0 +1,34 @@
+package scala.scalajs.sbtplugin
+
+import scala.language.implicitConversions
+
+import scala.scalajs.tools.logging._
+import sbt.{Logger => SbtLogger, Level => SbtLevel}
+
+object Implicits {
+ private class SbtLoggerWrapper(underlying: SbtLogger) extends Logger {
+ def log(level: Level, message: => String): Unit =
+ underlying.log(level, message)
+ def success(message: => String): Unit =
+ underlying.success(message)
+ def trace(t: => Throwable): Unit =
+ underlying.trace(t)
+ }
+
+ implicit def sbtLogger2ToolsLogger(logger: SbtLogger): Logger =
+ new SbtLoggerWrapper(logger)
+
+ implicit def sbtLevel2ToolsLevel(level: SbtLevel.Value): Level = level match {
+ case SbtLevel.Error => Level.Error
+ case SbtLevel.Warn => Level.Warn
+ case SbtLevel.Info => Level.Info
+ case SbtLevel.Debug => Level.Debug
+ }
+
+ implicit def toolsLevel2sbtLevel(level: Level): SbtLevel.Value = level match {
+ case Level.Error => SbtLevel.Error
+ case Level.Warn => SbtLevel.Warn
+ case Level.Info => SbtLevel.Info
+ case Level.Debug => SbtLevel.Debug
+ }
+}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/JSUtils.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/JSUtils.scala
new file mode 100644
index 0000000..a59f105
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/JSUtils.scala
@@ -0,0 +1,35 @@
+package scala.scalajs.sbtplugin
+
+object JSUtils {
+ def listToJS(xs: List[String]): String =
+ xs.map(toJSstr _).mkString("[",",","]")
+
+ /** (almost) stolen from scala.scalajs.compiler.JSPrinters */
+ def toJSstr(str: String): String = {
+ /* Note that Java and JavaScript happen to use the same encoding for
+ * Unicode, namely UTF-16, which means that 1 char from Java always equals
+ * 1 char in JavaScript. */
+ val builder = new StringBuilder()
+ builder.append('"')
+ str foreach {
+ case '\\' => builder.append("\\\\")
+ case '"' => builder.append("\\\"")
+ case '\u0007' => builder.append("\\a")
+ case '\u0008' => builder.append("\\b")
+ case '\u0009' => builder.append("\\t")
+ case '\u000A' => builder.append("\\n")
+ case '\u000B' => builder.append("\\v")
+ case '\u000C' => builder.append("\\f")
+ case '\u000D' => builder.append("\\r")
+ case c =>
+ if (c >= 32 && c <= 126) builder.append(c.toChar) // ASCII printable characters
+ else builder.append(f"\\u$c%04x")
+ }
+ builder.append('"')
+ builder.result()
+ }
+
+ def dot2bracket(name: String): String = {
+ name.split('.').map(s => s"""[${toJSstr(s)}]""").mkString
+ }
+}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/LoggerJSConsole.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/LoggerJSConsole.scala
new file mode 100644
index 0000000..ecfb546
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/LoggerJSConsole.scala
@@ -0,0 +1,18 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.sbtplugin
+
+import sbt.Logger
+import scala.scalajs.tools.env.JSConsole
+
+/** A proxy for a Logger that looks like a Mozilla console object */
+class LoggerJSConsole(logger: Logger) extends JSConsole {
+ def log(msg: Any): Unit = logger.info(msg.toString)
+}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/OptimizerOptions.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/OptimizerOptions.scala
new file mode 100644
index 0000000..25d6178
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/OptimizerOptions.scala
@@ -0,0 +1,74 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.sbtplugin
+
+import OptimizerOptions._
+
+/** Various options for the Scala.js optimizer tool chain
+ *
+ * This is not a case class and does have a private constructor so that we
+ * can add fields in a binary-compatible manner.
+ *
+ * Use [[OptimizerOptions.apply]] and the `with` methods to create a configured
+ * instance.
+ */
+final class OptimizerOptions private (
+ /** Whether to parallelize the optimizer (currently fastOptJS only) **/
+ val parallel: Boolean = true,
+ /** Whether to run the optimizer in batch (i.e. non-incremental) mode */
+ val batchMode: Boolean = false,
+ /** Whether to run the Scala.js optimizer */
+ val disableOptimizer: Boolean = false,
+ /** Whether to pretty-print in fullOptJS */
+ val prettyPrintFullOptJS: Boolean = false,
+ /** Perform expensive checks of the sanity of the Scala.js IR */
+ val checkScalaJSIR: Boolean = false
+) {
+
+ def withParallel(parallel: Boolean): OptimizerOptions = {
+ new OptimizerOptions(parallel, batchMode,
+ disableOptimizer, prettyPrintFullOptJS, checkScalaJSIR)
+ }
+
+ def withBatchMode(batchMode: Boolean): OptimizerOptions = {
+ new OptimizerOptions(parallel, batchMode,
+ disableOptimizer, prettyPrintFullOptJS, checkScalaJSIR)
+ }
+
+ def withDisableOptimizer(disableOptimizer: Boolean): OptimizerOptions = {
+ new OptimizerOptions(parallel, batchMode,
+ disableOptimizer, prettyPrintFullOptJS, checkScalaJSIR)
+ }
+
+ def withPrettyPrintFullOptJS(prettyPrintFullOptJS: Boolean): OptimizerOptions = {
+ new OptimizerOptions(parallel, batchMode,
+ disableOptimizer, prettyPrintFullOptJS, checkScalaJSIR)
+ }
+
+ def withCheckScalaJSIR(checkScalaJSIR: Boolean): OptimizerOptions = {
+ new OptimizerOptions(parallel, batchMode,
+ disableOptimizer, prettyPrintFullOptJS, checkScalaJSIR)
+ }
+
+ override def toString: String = {
+ s"""OptimizerOptions(
+ | parallel = $parallel
+ | batchMode = $batchMode
+ | disableOptimizer = $disableOptimizer
+ | prettyPrintFullOptJS = $prettyPrintFullOptJS
+ | checkScalaJSIR = $checkScalaJSIR
+ |)""".stripMargin
+ }
+
+}
+
+object OptimizerOptions {
+ def apply() = new OptimizerOptions()
+}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/ScalaJSCrossVersion.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/ScalaJSCrossVersion.scala
new file mode 100644
index 0000000..d813622
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/ScalaJSCrossVersion.scala
@@ -0,0 +1,48 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.sbtplugin
+
+import sbt._
+
+import scala.scalajs.ir.ScalaJSVersions
+
+object ScalaJSCrossVersion {
+ private val scalaJSVersionUnmapped: String => String =
+ _ => s"sjs$currentBinaryVersion"
+
+ private val scalaJSVersionMap: String => String =
+ version => s"sjs${currentBinaryVersion}_$version"
+
+ private final val ReleaseVersion =
+ raw"""(\d+)\.(\d+)\.(\d+)""".r
+ private final val MinorSnapshotVersion =
+ raw"""(\d+)\.(\d+)\.([1-9]\d*)-SNAPSHOT""".r
+
+ val currentBinaryVersion = binaryScalaJSVersion(ScalaJSVersions.current)
+
+ def binaryScalaJSVersion(full: String): String = full match {
+ case ReleaseVersion(major, minor, release) => s"$major.$minor"
+ case MinorSnapshotVersion(major, minor, _) => s"$major.$minor"
+ case _ => full
+ }
+
+ def scalaJSMapped(cross: CrossVersion): CrossVersion = cross match {
+ case CrossVersion.Disabled =>
+ CrossVersion.binaryMapped(scalaJSVersionUnmapped)
+ case cross: CrossVersion.Binary =>
+ CrossVersion.binaryMapped(cross.remapVersion andThen scalaJSVersionMap)
+ case cross: CrossVersion.Full =>
+ CrossVersion.fullMapped(cross.remapVersion andThen scalaJSVersionMap)
+ }
+
+ def binary: CrossVersion = scalaJSMapped(CrossVersion.binary)
+
+ def full: CrossVersion = scalaJSMapped(CrossVersion.full)
+}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/ScalaJSPlugin.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/ScalaJSPlugin.scala
new file mode 100644
index 0000000..b33e2fb
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/ScalaJSPlugin.scala
@@ -0,0 +1,179 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.sbtplugin
+
+import sbt._
+
+import scala.scalajs.tools.sem.Semantics
+import scala.scalajs.tools.classpath._
+import scala.scalajs.tools.io.VirtualJSFile
+import scala.scalajs.tools.env.{JSEnv, JSConsole}
+import scala.scalajs.tools.optimizer.ScalaJSOptimizer
+
+import scala.scalajs.ir.ScalaJSVersions
+
+import scala.scalajs.sbtplugin.env.nodejs.NodeJSEnv
+import scala.scalajs.sbtplugin.env.phantomjs.PhantomJSEnv
+
+object ScalaJSPlugin extends AutoPlugin {
+ override def requires = plugins.JvmPlugin
+
+ object autoImport extends impl.DependencyBuilders {
+ import KeyRanks._
+
+ // Some constants
+ val scalaJSVersion = ScalaJSVersions.current
+ val scalaJSIsSnapshotVersion = ScalaJSVersions.currentIsSnapshot
+ val scalaJSBinaryVersion = ScalaJSCrossVersion.currentBinaryVersion
+
+ // Stage values
+ val PreLinkStage = Stage.PreLink
+ val FastOptStage = Stage.FastOpt
+ val FullOptStage = Stage.FullOpt
+
+ // Factory methods for JSEnvs
+
+ /**
+ * Creates a [[Def.Initialize]] for a NodeJSEnv. Use this to explicitly
+ * specify in your build that you would like to run with Node.js:
+ *
+ * {{{
+ * postLinkJSEnv := NodeJSEnv().value
+ * }}}
+ *
+ * Note that the resulting [[Setting]] is not scoped at all, but must be
+ * scoped in a project that has the ScalaJSPlugin enabled to work properly.
+ * Therefore, either put the upper line in your project settings (common
+ * case) or scope it manually, using [[Project.inScope]].
+ */
+ def NodeJSEnv(
+ executable: String = "node",
+ args: Seq[String] = Seq.empty,
+ env: Map[String, String] = Map.empty
+ ): Def.Initialize[Task[NodeJSEnv]] = Def.task {
+ new NodeJSEnv(executable, args, env)
+ }
+
+ /**
+ * Creates a [[Def.Initialize]] for a PhantomJSEnv. Use this to explicitly
+ * specify in your build that you would like to run with PhantomJS:
+ *
+ * {{{
+ * postLinkJSEnv := PhantomJSEnv().value
+ * }}}
+ *
+ * Note that the resulting [[Setting]] is not scoped at all, but must be
+ * scoped in a project that has the ScalaJSPlugin enabled to work properly.
+ * Therefore, either put the upper line in your project settings (common
+ * case) or scope it manually, using [[Project.inScope]].
+ */
+ def PhantomJSEnv(
+ executable: String = "phantomjs",
+ args: Seq[String] = Seq.empty,
+ env: Map[String, String] = Map.empty,
+ autoExit: Boolean = true
+ ): Def.Initialize[Task[PhantomJSEnv]] = Def.task {
+ val loader = scalaJSPhantomJSClassLoader.value
+ new PhantomJSEnv(executable, args, env, autoExit, loader)
+ }
+
+ // All our public-facing keys
+
+ val fastOptJS = TaskKey[Attributed[File]]("fastOptJS",
+ "Quickly link all compiled JavaScript into a single file", APlusTask)
+ val fullOptJS = TaskKey[Attributed[File]]("fullOptJS",
+ "Link all compiled JavaScript into a single file and fully optimize", APlusTask)
+
+ val scalaJSStage = SettingKey[Stage]("scalaJSStage",
+ "The optimization stage at which run and test are executed", APlusSetting)
+
+ val packageScalaJSLauncher = TaskKey[Attributed[File]]("packageScalaJSLauncher",
+ "Writes the persistent launcher file. Fails if the mainClass is ambigous", CTask)
+
+ val packageJSDependencies = TaskKey[File]("packageJSDependencies",
+ "Packages all dependencies of the preLink classpath in a single file. " +
+ "Set skip in packageJSDependencies := false to run automatically", AMinusTask)
+
+ val jsDependencyManifest = TaskKey[File]("jsDependencyManifest",
+ "Writes the JS_DEPENDENCIES file.", DTask)
+
+ val scalaJSPreLinkClasspath = TaskKey[IRClasspath]("scalaJSPreLinkClasspath",
+ "Completely resolved classpath just after compilation", DTask)
+
+ val scalaJSExecClasspath = TaskKey[CompleteClasspath]("scalaJSExecClasspath",
+ "The classpath used for running and testing", DTask)
+
+ val scalaJSLauncher = TaskKey[Attributed[VirtualJSFile]]("scalaJSLauncher",
+ "Code used to run. (Attributed with used class name)", DTask)
+
+ val scalaJSConsole = TaskKey[JSConsole]("scalaJSConsole",
+ "The JS console used by the Scala.js runner/tester", DTask)
+
+ val preLinkJSEnv = TaskKey[JSEnv]("preLinkJSEnv",
+ "The jsEnv used to execute before linking (packaging / optimizing) Scala.js files", BSetting)
+ val postLinkJSEnv = TaskKey[JSEnv]("postLinkJSEnv",
+ "The jsEnv used to execute after linking (packaging / optimizing) Scala.js files", AMinusSetting)
+
+ val jsEnv = TaskKey[JSEnv]("jsEnv",
+ "A JVM-like environment where Scala.js files can be run and tested", DTask)
+
+ val requiresDOM = SettingKey[Boolean]("requiresDOM",
+ "Whether this projects needs the DOM. Overrides anything inherited through dependencies.", AMinusSetting)
+
+ val scalaJSTestFramework = SettingKey[String]("scalaJSTestFramework",
+ "The Scala.js class that is used as a test framework, for example a class that wraps Jasmine", ASetting)
+
+ val relativeSourceMaps = SettingKey[Boolean]("relativeSourceMaps",
+ "Make the referenced paths on source maps relative to target path", BPlusSetting)
+
+ val emitSourceMaps = SettingKey[Boolean]("emitSourceMaps",
+ "Whether package and optimize stages should emit source maps at all", BPlusSetting)
+
+ val jsDependencies = SettingKey[Seq[AbstractJSDep]]("jsDependencies",
+ "JavaScript libraries this project depends upon. Also used to depend on the DOM.", APlusSetting)
+
+ val scalaJSSemantics = SettingKey[Semantics]("scalaJSSemantics",
+ "Configurable semantics of Scala.js.", BPlusSetting)
+
+ val jsDependencyFilter = SettingKey[PartialClasspath.DependencyFilter]("jsDependencyFilter",
+ "The filter applied to the raw JavaScript dependencies before execution", CSetting)
+
+ val checkScalaJSSemantics = SettingKey[Boolean]("checkScalaJSSemantics",
+ "Whether to check that the current semantics meet compliance " +
+ "requirements of dependencies.", CSetting)
+
+ val persistLauncher = SettingKey[Boolean]("persistLauncher",
+ "Tell optimize/package tasks to write the laucher file to disk. " +
+ "If this is set, your project may only have a single mainClass or you must explicitly set it", AMinusSetting)
+
+ val scalaJSOptimizerOptions = SettingKey[OptimizerOptions]("scalaJSOptimizerOptions",
+ "All kinds of options for the Scala.js optimizer stages", DSetting)
+
+ /** Class loader for PhantomJSEnv. Used to load jetty8. */
+ val scalaJSPhantomJSClassLoader = TaskKey[ClassLoader]("scalaJSPhantomJSClassLoader",
+ "Private class loader to load jetty8 without polluting classpath. Only use this " +
+ "as the `jettyClassLoader` argument of the PhantomJSEnv",
+ KeyRanks.Invisible)
+ }
+
+ import autoImport._
+ import ScalaJSPluginInternal._
+
+ override def globalSettings: Seq[Setting[_]] = {
+ super.globalSettings ++ Seq(
+ scalaJSStage := Stage.PreLink
+ )
+ }
+
+ override def projectSettings: Seq[Setting[_]] = (
+ scalaJSAbstractSettings ++
+ scalaJSEcosystemSettings
+ )
+}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/ScalaJSPluginInternal.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/ScalaJSPluginInternal.scala
new file mode 100644
index 0000000..fe97f0b
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/ScalaJSPluginInternal.scala
@@ -0,0 +1,598 @@
+package scala.scalajs.sbtplugin
+
+import sbt._
+import sbt.inc.{IncOptions, ClassfileManager}
+import Keys._
+
+import Implicits._
+import JSUtils._
+
+import scala.scalajs.tools.sem.Semantics
+import scala.scalajs.tools.io.{IO => toolsIO, _}
+import scala.scalajs.tools.classpath._
+import scala.scalajs.tools.classpath.builder._
+import scala.scalajs.tools.jsdep._
+import scala.scalajs.tools.optimizer.{
+ ScalaJSOptimizer,
+ ScalaJSClosureOptimizer,
+ IncOptimizer,
+ ParIncOptimizer
+}
+import scala.scalajs.tools.corelib.CoreJSLibs
+
+import scala.scalajs.tools.env._
+import scala.scalajs.sbtplugin.env.rhino.RhinoJSEnv
+import scala.scalajs.sbtplugin.env.nodejs.NodeJSEnv
+import scala.scalajs.sbtplugin.env.phantomjs.{PhantomJSEnv, PhantomJettyClassLoader}
+
+import scala.scalajs.ir.ScalaJSVersions
+
+import scala.scalajs.sbtplugin.testing.{TestFramework, JSClasspathLoader}
+
+import scala.util.Try
+
+import java.nio.charset.Charset
+import java.net.URLClassLoader
+
+/** Contains settings used by ScalaJSPlugin that should not be automatically
+ * be in the *.sbt file's scope.
+ */
+object ScalaJSPluginInternal {
+
+ import ScalaJSPlugin.autoImport._
+
+ /** Dummy setting to ensure we do not fork in Scala.js run & test. */
+ val scalaJSEnsureUnforked = SettingKey[Boolean]("ensureUnforked",
+ "Scala.js internal: Fails if fork is true.", KeyRanks.Invisible)
+
+ /** Dummy setting to persist Scala.js optimizer */
+ val scalaJSOptimizer = SettingKey[ScalaJSOptimizer]("scalaJSOptimizer",
+ "Scala.js internal: Setting to persist the optimizer", KeyRanks.Invisible)
+
+ /** Internal task to calculate whether a project requests the DOM
+ * (through jsDependencies or requiresDOM) */
+ val scalaJSRequestsDOM = TaskKey[Boolean]("scalaJSRequestsDOM",
+ "Scala.js internal: Whether a project really wants the DOM. " +
+ "Calculated using requiresDOM and jsDependencies", KeyRanks.Invisible)
+
+ /** Default post link environment */
+ val scalaJSDefaultPostLinkJSEnv = TaskKey[JSEnv]("scalaJSDefaultPostLinkJSEnv",
+ "Scala.js internal: Default for postLinkJSEnv", KeyRanks.Invisible)
+
+ /** Lookup key for CompleteClasspath in attribute maps */
+ val scalaJSCompleteClasspath =
+ AttributeKey[CompleteClasspath]("scalaJSCompleteClasspath")
+
+ /** Patches the IncOptions so that .sjsir files are pruned as needed.
+ *
+ * This complicated logic patches the ClassfileManager factory of the given
+ * IncOptions with one that is aware of .sjsir files emitted by the Scala.js
+ * compiler. This makes sure that, when a .class file must be deleted, the
+ * corresponding .sjsir file are also deleted.
+ */
+ def scalaJSPatchIncOptions(incOptions: IncOptions): IncOptions = {
+ val inheritedNewClassfileManager = incOptions.newClassfileManager
+ val newClassfileManager = () => new ClassfileManager {
+ private[this] val inherited = inheritedNewClassfileManager()
+
+ def delete(classes: Iterable[File]): Unit = {
+ inherited.delete(classes flatMap { classFile =>
+ val scalaJSFiles = if (classFile.getPath endsWith ".class") {
+ val f = FileVirtualFile.withExtension(classFile, ".class", ".sjsir")
+ if (f.exists) List(f)
+ else Nil
+ } else Nil
+ classFile :: scalaJSFiles
+ })
+ }
+
+ def generated(classes: Iterable[File]): Unit = inherited.generated(classes)
+ def complete(success: Boolean): Unit = inherited.complete(success)
+ }
+ incOptions.copy(newClassfileManager = newClassfileManager)
+ }
+
+ private def scalaJSOptimizerSetting(key: TaskKey[_]): Setting[_] = (
+ scalaJSOptimizer in key := {
+ val semantics = (scalaJSSemantics in key).value
+ if ((scalaJSOptimizerOptions in key).value.parallel)
+ new ScalaJSOptimizer(semantics, new ParIncOptimizer(_))
+ else
+ new ScalaJSOptimizer(semantics, new IncOptimizer(_))
+ }
+ )
+
+ val scalaJSConfigSettings: Seq[Setting[_]] = Seq(
+ incOptions ~= scalaJSPatchIncOptions
+ ) ++ Seq(
+
+ scalaJSPreLinkClasspath := {
+ val cp = fullClasspath.value
+ val pcp = PartialClasspathBuilder.build(Attributed.data(cp).toList)
+ val ccp = pcp.resolve(jsDependencyFilter.value)
+
+ if (checkScalaJSSemantics.value)
+ ccp.checkCompliance(scalaJSSemantics.value)
+
+ ccp
+ },
+
+ artifactPath in fastOptJS :=
+ ((crossTarget in fastOptJS).value /
+ ((moduleName in fastOptJS).value + "-fastopt.js")),
+
+ scalaJSOptimizerSetting(fastOptJS),
+
+ fastOptJS := {
+ val s = streams.value
+ val output = (artifactPath in fastOptJS).value
+ val taskCache =
+ WritableFileVirtualTextFile(s.cacheDirectory / "fastopt-js")
+
+ IO.createDirectory(output.getParentFile)
+
+ val relSourceMapBase =
+ if ((relativeSourceMaps in fastOptJS).value)
+ Some(output.getParentFile.toURI())
+ else None
+
+ val opts = (scalaJSOptimizerOptions in fastOptJS).value
+
+ import ScalaJSOptimizer._
+ val outCP = (scalaJSOptimizer in fastOptJS).value.optimizeCP(
+ Inputs(input = (scalaJSPreLinkClasspath in fastOptJS).value),
+ OutputConfig(
+ output = WritableFileVirtualJSFile(output),
+ cache = Some(taskCache),
+ wantSourceMap = (emitSourceMaps in fastOptJS).value,
+ relativizeSourceMapBase = relSourceMapBase,
+ checkIR = opts.checkScalaJSIR,
+ disableOptimizer = opts.disableOptimizer,
+ batchMode = opts.batchMode),
+ s.log)
+
+ Attributed.blank(output).put(scalaJSCompleteClasspath, outCP)
+ },
+ fastOptJS <<=
+ fastOptJS.dependsOn(packageJSDependencies, packageScalaJSLauncher),
+
+ artifactPath in fullOptJS :=
+ ((crossTarget in fullOptJS).value /
+ ((moduleName in fullOptJS).value + "-opt.js")),
+
+ scalaJSSemantics in fullOptJS :=
+ (scalaJSSemantics in fastOptJS).value.optimized,
+
+ scalaJSOptimizerSetting(fullOptJS),
+
+ fullOptJS := {
+ val s = streams.value
+ val output = (artifactPath in fullOptJS).value
+ val taskCache =
+ WritableFileVirtualTextFile(s.cacheDirectory / "fullopt-js")
+
+ IO.createDirectory(output.getParentFile)
+
+ val relSourceMapBase =
+ if ((relativeSourceMaps in fullOptJS).value)
+ Some(output.getParentFile.toURI())
+ else None
+
+ val opts = (scalaJSOptimizerOptions in fullOptJS).value
+
+ val semantics = (scalaJSSemantics in fullOptJS).value
+
+ import ScalaJSClosureOptimizer._
+ val outCP = new ScalaJSClosureOptimizer(semantics).optimizeCP(
+ (scalaJSOptimizer in fullOptJS).value,
+ Inputs(ScalaJSOptimizer.Inputs(
+ input = (scalaJSPreLinkClasspath in fullOptJS).value)),
+ OutputConfig(
+ output = WritableFileVirtualJSFile(output),
+ cache = Some(taskCache),
+ wantSourceMap = (emitSourceMaps in fullOptJS).value,
+ relativizeSourceMapBase = relSourceMapBase,
+ checkIR = opts.checkScalaJSIR,
+ disableOptimizer = opts.disableOptimizer,
+ batchMode = opts.batchMode,
+ prettyPrint = opts.prettyPrintFullOptJS),
+ s.log)
+
+ Attributed.blank(output).put(scalaJSCompleteClasspath, outCP)
+ },
+
+ artifactPath in packageScalaJSLauncher :=
+ ((crossTarget in packageScalaJSLauncher).value /
+ ((moduleName in packageScalaJSLauncher).value + "-launcher.js")),
+
+ skip in packageScalaJSLauncher := !persistLauncher.value,
+
+ packageScalaJSLauncher <<= Def.taskDyn {
+ if ((skip in packageScalaJSLauncher).value)
+ Def.task(Attributed.blank((artifactPath in packageScalaJSLauncher).value))
+ else Def.task {
+ mainClass.value map { mainCl =>
+ val file = (artifactPath in packageScalaJSLauncher).value
+ IO.write(file, launcherContent(mainCl), Charset.forName("UTF-8"))
+
+ // Attach the name of the main class used, (ab?)using the name key
+ Attributed(file)(AttributeMap.empty.put(name.key, mainCl))
+ } getOrElse {
+ sys.error("Cannot write launcher file, since there is no or multiple mainClasses")
+ }
+ }
+ },
+
+ artifactPath in packageJSDependencies :=
+ ((crossTarget in packageJSDependencies).value /
+ ((moduleName in packageJSDependencies).value + "-jsdeps.js")),
+
+ packageJSDependencies <<= Def.taskDyn {
+ if ((skip in packageJSDependencies).value)
+ Def.task((artifactPath in packageJSDependencies).value)
+ else Def.task {
+ val cp = scalaJSPreLinkClasspath.value
+ val output = (artifactPath in packageJSDependencies).value
+ val taskCache = WritableFileVirtualJSFile(
+ streams.value.cacheDirectory / "package-js-deps")
+
+ IO.createDirectory(output.getParentFile)
+
+ val outFile = WritableFileVirtualTextFile(output)
+ CacheUtils.cached(cp.version, outFile, Some(taskCache)) {
+ toolsIO.concatFiles(outFile, cp.jsLibs.map(_.lib))
+ }
+
+ output
+ }
+ },
+
+ jsDependencyManifest := {
+ val myModule = thisProject.value.id
+ val config = configuration.value.name
+
+ // Collect all libraries
+ val jsDeps = jsDependencies.value.collect {
+ case dep: JSModuleID if dep.configurations.forall(_ == config) =>
+ dep.jsDep
+ }
+
+ val requiresDOM = jsDependencies.value.exists {
+ case RuntimeDOM(configurations) =>
+ configurations.forall(_ == config)
+ case _ => false
+ }
+
+ val compliantSemantics = scalaJSSemantics.value.compliants
+
+ val manifest = new JSDependencyManifest(new Origin(myModule, config),
+ jsDeps.toList, requiresDOM, compliantSemantics)
+
+ // Write dependency file to class directory
+ val targetDir = classDirectory.value
+ IO.createDirectory(targetDir)
+
+ val file = targetDir / JSDependencyManifest.ManifestFileName
+ val vfile = WritableFileVirtualTextFile(file)
+
+ // Prevent writing if unnecessary to not invalidate dependencies
+ val needWrite = !vfile.exists || {
+ Try {
+ val readManifest = JSDependencyManifest.read(vfile)
+ readManifest != manifest
+ } getOrElse true
+ }
+
+ if (needWrite)
+ JSDependencyManifest.write(manifest, vfile)
+
+ file
+ },
+
+ products <<= products.dependsOn(jsDependencyManifest),
+
+ console <<= console.dependsOn(Def.task(
+ streams.value.log.warn("Scala REPL doesn't work with Scala.js. You " +
+ "are running a JVM REPL. JavaScript things won't work.")
+ )),
+
+ // Give tasks ability to check we are not forking at build reading time
+ scalaJSEnsureUnforked := {
+ if (fork.value)
+ sys.error("Scala.js cannot be run in a forked JVM")
+ else
+ true
+ },
+
+ scalaJSRequestsDOM :=
+ requiresDOM.?.value.getOrElse(scalaJSExecClasspath.value.requiresDOM),
+
+ // Default jsEnv
+ jsEnv <<= Def.taskDyn {
+ scalaJSStage.value match {
+ case Stage.PreLink =>
+ Def.task {
+ preLinkJSEnv.?.value.getOrElse {
+ new RhinoJSEnv(scalaJSSemantics.value,
+ withDOM = scalaJSRequestsDOM.value)
+ }
+ }
+ case Stage.FastOpt | Stage.FullOpt =>
+ Def.task(scalaJSDefaultPostLinkJSEnv.value)
+ }
+ },
+
+ // Wire jsEnv and sources for other stages
+ scalaJSDefaultPostLinkJSEnv := postLinkJSEnv.?.value.getOrElse {
+ if (scalaJSRequestsDOM.value)
+ new PhantomJSEnv(jettyClassLoader = scalaJSPhantomJSClassLoader.value)
+ else
+ new NodeJSEnv
+ },
+
+ scalaJSExecClasspath <<= Def.taskDyn {
+ scalaJSStage.value match {
+ case Stage.PreLink =>
+ Def.task { scalaJSPreLinkClasspath.value }
+ case Stage.FastOpt =>
+ Def.task { fastOptJS.value.get(scalaJSCompleteClasspath).get }
+ case Stage.FullOpt =>
+ Def.task { fullOptJS.value.get(scalaJSCompleteClasspath).get }
+ }
+ }
+ )
+
+ /** Run a class in a given environment using a given launcher */
+ private def jsRun(env: JSEnv, cp: CompleteClasspath, mainCl: String,
+ launcher: VirtualJSFile, jsConsole: JSConsole, log: Logger) = {
+
+ log.info("Running " + mainCl)
+ log.debug(s"with JSEnv of type ${env.getClass()}")
+ log.debug(s"with classpath of type ${cp.getClass}")
+
+ // Actually run code
+ env.jsRunner(cp, launcher, log, jsConsole).run()
+ }
+
+ private def launcherContent(mainCl: String) = {
+ // If we are running in Node.js, we need to bracket select on
+ // global rather than this
+ """((typeof global === "object" && global &&
+ global["Object"] === Object) ? global : this)""" +
+ s"${dot2bracket(mainCl)}().main();\n"
+ }
+
+ private def memLauncher(mainCl: String) = {
+ new MemVirtualJSFile("Generated launcher file")
+ .withContent(launcherContent(mainCl))
+ }
+
+ // These settings will be filtered by the stage dummy tasks
+ val scalaJSRunSettings = Seq(
+ mainClass in scalaJSLauncher := (mainClass in run).value,
+ scalaJSLauncher <<= Def.taskDyn {
+ if (persistLauncher.value)
+ Def.task(packageScalaJSLauncher.value.map(FileVirtualJSFile))
+ else Def.task {
+ (mainClass in scalaJSLauncher).value map { mainClass =>
+ val memLaunch = memLauncher(mainClass)
+ Attributed[VirtualJSFile](memLaunch)(
+ AttributeMap.empty.put(name.key, mainClass))
+ } getOrElse {
+ sys.error("No main class detected.")
+ }
+ }
+ },
+
+ /* We do currently not discover objects containing a
+ *
+ * def main(args: Array[String]): Unit
+ *
+ * Support will be added again, as soon as we can run them
+ * reliably (e.g. without implicitly requiring that an exported
+ *
+ * def main(): Unit
+ *
+ * exists alongside.
+ */
+ discoveredMainClasses := {
+ import xsbt.api.{Discovered, Discovery}
+
+ val jsApp = "scala.scalajs.js.JSApp"
+
+ def isJSApp(discovered: Discovered) =
+ discovered.isModule && discovered.baseClasses.contains(jsApp)
+
+ Discovery(Set(jsApp), Set.empty)(Tests.allDefs(compile.value)) collect {
+ case (definition, discovered) if isJSApp(discovered) =>
+ definition.name
+ }
+ },
+
+ run <<= Def.inputTask {
+ // use assert to prevent warning about pure expr in stat pos
+ assert(scalaJSEnsureUnforked.value)
+
+ val launch = scalaJSLauncher.value
+ val className = launch.get(name.key).getOrElse("<unknown class>")
+ jsRun(jsEnv.value, scalaJSExecClasspath.value, className,
+ launch.data, scalaJSConsole.value, streams.value.log)
+ },
+
+ runMain <<= {
+ // Implicits for parsing
+ import sbinary.DefaultProtocol.StringFormat
+ import Cache.seqFormat
+
+ val parser = Defaults.loadForParser(discoveredMainClasses)((s, names) =>
+ Defaults.runMainParser(s, names getOrElse Nil))
+
+ Def.inputTask {
+ // use assert to prevent warning about pure expr in stat pos
+ assert(scalaJSEnsureUnforked.value)
+
+ val mainCl = parser.parsed._1
+ jsRun(jsEnv.value, scalaJSExecClasspath.value, mainCl,
+ memLauncher(mainCl), scalaJSConsole.value, streams.value.log)
+ }
+ }
+ )
+
+ val scalaJSCompileSettings = (
+ scalaJSConfigSettings ++
+ scalaJSRunSettings
+ )
+
+ val scalaJSTestFrameworkSettings = Seq(
+ // Copied from Defaults, but scoped. We need a JVM loader in
+ // loadedTestFrameworks to find out whether the framework exists.
+ testLoader in loadedTestFrameworks := {
+ TestFramework.createTestLoader(
+ Attributed.data(fullClasspath.value),
+ scalaInstance.value,
+ IO.createUniqueDirectory(taskTemporaryDirectory.value))
+ },
+
+ loadedTestFrameworks := {
+ // use assert to prevent warning about pure expr in stat pos
+ assert(scalaJSEnsureUnforked.value)
+
+ val loader = (testLoader in loadedTestFrameworks).value
+ val isTestFrameworkDefined = try {
+ Class.forName(scalaJSTestFramework.value, false, loader)
+ true
+ } catch {
+ case _: ClassNotFoundException => false
+ }
+ if (isTestFrameworkDefined) {
+ loadedTestFrameworks.value.updated(
+ sbt.TestFramework(classOf[TestFramework].getName),
+ new TestFramework(
+ environment = jsEnv.value,
+ jsConsole = scalaJSConsole.value,
+ testFramework = scalaJSTestFramework.value)
+ )
+ } else {
+ loadedTestFrameworks.value
+ }
+ },
+
+ // Pseudo loader to pass classpath to test framework
+ testLoader := JSClasspathLoader(scalaJSExecClasspath.value)
+ )
+
+ val scalaJSTestBuildSettings = (
+ scalaJSConfigSettings
+ ) ++ (
+ Seq(fastOptJS, fullOptJS, packageScalaJSLauncher,
+ packageJSDependencies) map { packageJSTask =>
+ moduleName in packageJSTask := moduleName.value + "-test"
+ }
+ )
+
+ val scalaJSTestSettings = (
+ scalaJSTestBuildSettings ++
+ scalaJSTestFrameworkSettings
+ )
+
+ val scalaJSDependenciesSettings = Seq(
+ // add all the webjars your jsDependencies depend upon
+ libraryDependencies ++= jsDependencies.value.collect {
+ case JarJSModuleID(module, _) => module
+ }
+ )
+
+ val scalaJSDefaultBuildConfigs = (
+ inConfig(Compile)(scalaJSConfigSettings) ++ // build settings for Compile
+ inConfig(Test)(scalaJSTestBuildSettings) ++
+ scalaJSDependenciesSettings
+ )
+
+ val scalaJSDefaultConfigs = (
+ inConfig(Compile)(scalaJSCompileSettings) ++
+ inConfig(Test)(scalaJSTestSettings) ++
+ scalaJSDependenciesSettings
+ )
+
+ val phantomJSJettyModules = Seq(
+ "org.eclipse.jetty" % "jetty-websocket" % "8.1.16.v20140903",
+ "org.eclipse.jetty" % "jetty-server" % "8.1.16.v20140903"
+ )
+
+ val scalaJSProjectBaseSettings = Seq(
+ relativeSourceMaps := false,
+ persistLauncher := false,
+
+ skip in packageJSDependencies := true,
+
+ scalaJSTestFramework := "org.scalajs.jasminetest.JasmineTestFramework",
+
+ emitSourceMaps := true,
+
+ scalaJSOptimizerOptions := OptimizerOptions(),
+
+ jsDependencies := Seq(),
+ jsDependencyFilter := identity,
+
+ scalaJSSemantics := Semantics.Defaults,
+ checkScalaJSSemantics := true,
+
+ scalaJSConsole := ConsoleJSConsole,
+
+ clean <<= clean.dependsOn(Def.task {
+ // have clean reset incremental optimizer state
+ (scalaJSOptimizer in (Compile, fastOptJS)).value.clean()
+ (scalaJSOptimizer in (Test, fastOptJS)).value.clean()
+ }),
+
+ /* Depend on jetty artifacts in dummy configuration to be able to inject
+ * them into the PhantomJS runner if necessary.
+ * See scalaJSPhantomJSClassLoader
+ */
+ ivyConfigurations += config("phantom-js-jetty").hide,
+ libraryDependencies ++= phantomJSJettyModules.map(_ % "phantom-js-jetty"),
+ scalaJSPhantomJSClassLoader := {
+ val report = update.value
+ val jars = report.select(configurationFilter("phantom-js-jetty"))
+
+ val jettyLoader =
+ new URLClassLoader(jars.map(_.toURI.toURL).toArray, null)
+
+ new PhantomJettyClassLoader(jettyLoader, getClass.getClassLoader)
+ }
+ )
+
+ val scalaJSAbstractSettings: Seq[Setting[_]] = (
+ scalaJSProjectBaseSettings ++
+ scalaJSDefaultConfigs
+ )
+
+ val scalaJSAbstractBuildSettings: Seq[Setting[_]] = (
+ scalaJSProjectBaseSettings ++
+ scalaJSDefaultBuildConfigs
+ )
+
+ val scalaJSReleasesResolver = Resolver.url("scala-js-releases",
+ url("http://dl.bintray.com/content/scala-js/scala-js-releases"))(
+ Resolver.ivyStylePatterns)
+ val scalaJSSnapshotsResolver = Resolver.url("scala-js-snapshots",
+ url("http://repo.scala-js.org/repo/snapshots/"))(
+ Resolver.ivyStylePatterns)
+
+ val scalaJSEcosystemSettings = Seq(
+ // the resolver to find the compiler and library (and others)
+ resolvers ++= Seq(scalaJSReleasesResolver, scalaJSSnapshotsResolver),
+
+ // you will need the Scala.js compiler plugin
+ autoCompilerPlugins := true,
+ addCompilerPlugin(
+ "org.scala-lang.modules.scalajs" % "scalajs-compiler" % scalaJSVersion cross CrossVersion.full),
+
+ // and of course the Scala.js library
+ libraryDependencies += "org.scala-lang.modules.scalajs" %% "scalajs-library" % scalaJSVersion,
+
+ // and you will want to be cross-compiled on the Scala.js binary version
+ crossVersion := ScalaJSCrossVersion.binary
+ )
+
+}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/Stage.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/Stage.scala
new file mode 100644
index 0000000..7f7b916
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/Stage.scala
@@ -0,0 +1,18 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.sbtplugin
+
+sealed trait Stage
+
+object Stage {
+ case object PreLink extends Stage
+ case object FullOpt extends Stage
+ case object FastOpt extends Stage
+}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/ExternalJSEnv.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/ExternalJSEnv.scala
new file mode 100644
index 0000000..e0aa557
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/ExternalJSEnv.scala
@@ -0,0 +1,200 @@
+package scala.scalajs.sbtplugin.env
+
+import scala.scalajs.tools.io._
+import scala.scalajs.tools.classpath._
+import scala.scalajs.tools.env._
+import scala.scalajs.tools.logging._
+
+import scala.scalajs.sbtplugin.JSUtils._
+
+import java.io.{ Console => _, _ }
+import scala.io.Source
+
+import scala.concurrent.{Future, Promise}
+import scala.util.Try
+
+abstract class ExternalJSEnv(
+ final protected val additionalArgs: Seq[String],
+ final protected val additionalEnv: Map[String, String]) extends AsyncJSEnv {
+
+ /** Printable name of this VM */
+ protected def vmName: String
+
+ /** Command to execute (on shell) for this VM */
+ protected def executable: String
+
+ protected class AbstractExtRunner(protected val classpath: CompleteClasspath,
+ protected val code: VirtualJSFile, protected val logger: Logger,
+ protected val console: JSConsole) {
+
+ /** JS files used to setup VM */
+ protected def initFiles(): Seq[VirtualJSFile] = Nil
+
+ /** Sends required data to VM Stdin (can throw) */
+ protected def sendVMStdin(out: OutputStream): Unit = {}
+
+ /** VM arguments excluding executable. Override to adapt.
+ * Overrider is responsible to add additionalArgs.
+ */
+ protected def getVMArgs(): Seq[String] = additionalArgs
+
+ /** VM environment. Override to adapt.
+ *
+ * Default is `sys.env` and [[additionalEnv]]
+ */
+ protected def getVMEnv(): Map[String, String] =
+ sys.env ++ additionalEnv
+
+ /** Get files that are a library (i.e. that do not run anything) */
+ protected def getLibJSFiles(): Seq[VirtualJSFile] =
+ initFiles() ++ classpath.allCode
+
+ /** Get all files that are passed to VM (libraries and code) */
+ protected def getJSFiles(): Seq[VirtualJSFile] =
+ getLibJSFiles() :+ code
+
+ /** write a single JS file to a writer using an include fct if appropriate */
+ protected def writeJSFile(file: VirtualJSFile, writer: Writer): Unit = {
+ // The only platform-independent way to do this in JS is to dump the file.
+ writer.write(file.content)
+ writer.write('\n')
+ }
+
+ /** Pipe stdin and stdout from/to VM */
+ final protected def pipeVMData(vmInst: Process): Unit = {
+ // Send stdin to VM.
+ val out = vmInst.getOutputStream()
+ try { sendVMStdin(out) }
+ finally { out.close() }
+
+ // Pipe stdout to console
+ pipeToConsole(vmInst.getInputStream(), console)
+
+ // We are probably done (stdin is closed). Report any errors
+ val errSrc = Source.fromInputStream(vmInst.getErrorStream(), "UTF-8")
+ try { errSrc.getLines.foreach(err => logger.error(err)) }
+ finally { errSrc.close }
+ }
+
+ /** Wait for the VM to terminate, verify exit code */
+ final protected def waitForVM(vmInst: Process): Unit = {
+ // Make sure we are done.
+ vmInst.waitFor()
+
+ // Get return value and return
+ val retVal = vmInst.exitValue
+ if (retVal != 0)
+ sys.error(s"$vmName exited with code $retVal")
+ }
+
+ protected def startVM(): Process = {
+ val vmArgs = getVMArgs()
+ val vmEnv = getVMEnv()
+
+ val allArgs = executable +: vmArgs
+ val pBuilder = new ProcessBuilder(allArgs: _*)
+
+ pBuilder.environment().clear()
+ for ((name, value) <- vmEnv)
+ pBuilder.environment().put(name, value)
+
+ pBuilder.start()
+ }
+
+ /** send a bunch of JS files to an output stream */
+ final protected def sendJS(files: Seq[VirtualJSFile],
+ out: OutputStream): Unit = {
+ val writer = new BufferedWriter(new OutputStreamWriter(out, "UTF-8"))
+ try sendJS(files, writer)
+ finally writer.close()
+ }
+
+ /** send a bunch of JS files to a writer */
+ final protected def sendJS(files: Seq[VirtualJSFile], out: Writer): Unit =
+ files.foreach { writeJSFile(_, out) }
+
+ /** pipe lines from input stream to JSConsole */
+ final protected def pipeToConsole(in: InputStream, console: JSConsole) = {
+ val source = Source.fromInputStream(in, "UTF-8")
+ try { source.getLines.foreach(console.log _) }
+ finally { source.close() }
+ }
+
+ }
+
+ protected class ExtRunner(classpath: CompleteClasspath, code: VirtualJSFile,
+ logger: Logger, console: JSConsole
+ ) extends AbstractExtRunner(classpath, code, logger, console)
+ with JSRunner {
+
+ def run(): Unit = {
+ val vmInst = startVM()
+
+ pipeVMData(vmInst)
+ waitForVM(vmInst)
+ }
+ }
+
+ protected class AsyncExtRunner(classpath: CompleteClasspath,
+ code: VirtualJSFile, logger: Logger, console: JSConsole
+ ) extends AbstractExtRunner(classpath, code, logger, console)
+ with AsyncJSRunner {
+
+ private[this] var vmInst: Process = null
+ private[this] var ioThreadEx: Throwable = null
+ private[this] val promise = Promise[Unit]
+
+ private[this] val thread = new Thread {
+ override def run(): Unit = {
+ // This thread should not be interrupted, so it is safe to use Trys
+ val pipeResult = Try(pipeVMData(vmInst))
+ val vmComplete = Try(waitForVM(vmInst))
+
+ // Store IO exception
+ pipeResult recover {
+ case e => ioThreadEx = e
+ }
+
+ // Chain Try's the other way: We want VM failure first, then IO failure
+ promise.complete(pipeResult orElse vmComplete)
+ }
+ }
+
+ def start(): Future[Unit] = {
+ require(vmInst == null, "start() may only be called once")
+ vmInst = startVM()
+ thread.start()
+ promise.future
+ }
+
+ def stop(): Unit = {
+ require(vmInst != null, "start() must have been called")
+ vmInst.destroy()
+ }
+
+ def isRunning(): Boolean = {
+ require(vmInst != null, "start() must have been called")
+ // Emulate JDK 8 Process.isAlive
+ try {
+ vmInst.exitValue()
+ false
+ } catch {
+ case e: IllegalThreadStateException =>
+ true
+ }
+ }
+
+ def await(): Unit = {
+ require(vmInst != null, "start() must have been called")
+ thread.join()
+ waitForVM(vmInst)
+
+ // At this point, the VM itself didn't fail. We need to check if
+ // anything bad happened while piping the data from the VM
+
+ if (ioThreadEx != null)
+ throw ioThreadEx
+ }
+ }
+
+}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/VirtualFileMaterializer.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/VirtualFileMaterializer.scala
new file mode 100644
index 0000000..fca1c47
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/VirtualFileMaterializer.scala
@@ -0,0 +1,67 @@
+package scala.scalajs.sbtplugin.env
+
+import scala.scalajs.tools.io.{IO => _, _}
+
+import sbt.IO
+
+import java.io.File
+
+/** A helper class to temporarily store virtual files to the filesystem.
+ *
+ * Can be used with tools that require real files.
+ * @param singleDir if true, forces files to be copied into
+ * [[cacheDir]]. Useful to setup include directories for
+ * example.
+ */
+final class VirtualFileMaterializer(singleDir: Boolean = false) {
+
+ val cacheDir = {
+ val dir = IO.createTemporaryDirectory
+ dir.deleteOnExit()
+ dir
+ }
+
+ /** Create a target file to write/copy to. Will also call
+ * deleteOnExit on the file.
+ */
+ private def trgFile(name: String): File = {
+ val f = new File(cacheDir, name)
+ f.deleteOnExit()
+ f
+ }
+
+ private def materializeFileVF(vf: FileVirtualFile): File = {
+ if (!singleDir) vf.file
+ else {
+ val trg = trgFile(vf.name)
+ IO.copyFile(vf.file, trg)
+ trg
+ }
+ }
+
+ def materialize(vf: VirtualTextFile): File = vf match {
+ case vf: FileVirtualFile => materializeFileVF(vf)
+ case _ =>
+ val trg = trgFile(vf.name)
+ IO.write(trg, vf.content)
+ trg
+ }
+
+ def materialize(vf: VirtualBinaryFile): File = vf match {
+ case vf: FileVirtualFile => materializeFileVF(vf)
+ case _ =>
+ val trg = trgFile(vf.name)
+ IO.write(trg, vf.content)
+ trg
+ }
+
+ /** Removes the cache directory. Any operation on this
+ * VirtualFileMaterializer is invalid after [[close]] has been
+ * called.
+ */
+ def close(): Unit = {
+ cacheDir.listFiles().foreach(_.delete)
+ cacheDir.delete()
+ }
+
+}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/nodejs/NodeJSEnv.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/nodejs/NodeJSEnv.scala
new file mode 100644
index 0000000..dfabe23
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/nodejs/NodeJSEnv.scala
@@ -0,0 +1,306 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.sbtplugin.env.nodejs
+
+import scala.scalajs.sbtplugin.env._
+import scala.scalajs.sbtplugin.JSUtils.toJSstr
+
+import scala.scalajs.tools.io._
+import scala.scalajs.tools.classpath._
+import scala.scalajs.tools.env._
+import scala.scalajs.tools.jsdep._
+import scala.scalajs.tools.logging._
+
+import scala.scalajs.sbtplugin.JSUtils._
+
+import java.io.{ Console => _, _ }
+import java.net._
+
+import scala.io.Source
+
+class NodeJSEnv(
+ nodejsPath: String = "node",
+ addArgs: Seq[String] = Seq.empty,
+ addEnv: Map[String, String] = Map.empty
+) extends ExternalJSEnv(addArgs, addEnv) with ComJSEnv {
+
+ protected def vmName: String = "node.js"
+ protected def executable: String = nodejsPath
+
+ override def jsRunner(classpath: CompleteClasspath, code: VirtualJSFile,
+ logger: Logger, console: JSConsole): JSRunner = {
+ new NodeRunner(classpath, code, logger, console)
+ }
+
+ override def asyncRunner(classpath: CompleteClasspath, code: VirtualJSFile,
+ logger: Logger, console: JSConsole): AsyncJSRunner = {
+ new AsyncNodeRunner(classpath, code, logger, console)
+ }
+
+ override def comRunner(classpath: CompleteClasspath, code: VirtualJSFile,
+ logger: Logger, console: JSConsole): ComJSRunner = {
+ new ComNodeRunner(classpath, code, logger, console)
+ }
+
+ protected class NodeRunner(classpath: CompleteClasspath,
+ code: VirtualJSFile, logger: Logger, console: JSConsole
+ ) extends ExtRunner(classpath, code, logger, console)
+ with AbstractNodeRunner
+
+ protected class AsyncNodeRunner(classpath: CompleteClasspath,
+ code: VirtualJSFile, logger: Logger, console: JSConsole
+ ) extends AsyncExtRunner(classpath, code, logger, console)
+ with AbstractNodeRunner
+
+ protected class ComNodeRunner(classpath: CompleteClasspath,
+ code: VirtualJSFile, logger: Logger, console: JSConsole
+ ) extends AsyncNodeRunner(classpath, code, logger, console)
+ with ComJSRunner {
+
+ /** Retry-timeout to wait for the JS VM to connect */
+ private final val acceptTimeout = 1000
+
+ private[this] val serverSocket =
+ new ServerSocket(0, 0, InetAddress.getByName(null)) // Loopback address
+ private[this] var comSocket: Socket = _
+ private[this] var jvm2js: DataOutputStream = _
+ private[this] var js2jvm: DataInputStream = _
+
+ private def comSetup = new MemVirtualJSFile("comSetup.js").withContent(
+ s"""
+ (function() {
+ // The socket for communication
+ var socket = null;
+ // The callback where received messages go
+ var recvCallback = null;
+
+ // Buffers received data
+ var inBuffer = new Buffer(0);
+
+ function onData(data) {
+ inBuffer = Buffer.concat([inBuffer, data]);
+ tryReadMsg();
+ }
+
+ function tryReadMsg() {
+ if (inBuffer.length < 4) return;
+ var msgLen = inBuffer.readInt32BE(0);
+ var byteLen = 4 + msgLen * 2;
+
+ if (inBuffer.length < byteLen) return;
+ var res = "";
+
+ for (var i = 0; i < msgLen; ++i)
+ res += String.fromCharCode(inBuffer.readInt16BE(4 + i * 2));
+
+ inBuffer = inBuffer.slice(byteLen);
+
+ recvCallback(res);
+ }
+
+ global.scalajsCom = {
+ init: function(recvCB) {
+ if (socket !== null) throw new Error("Com already open");
+
+ var net = require('net');
+ recvCallback = recvCB;
+ socket = net.connect(${serverSocket.getLocalPort});
+ socket.on('data', onData);
+ },
+ send: function(msg) {
+ if (socket === null) throw new Error("Com not open");
+
+ var len = msg.length;
+ var buf = new Buffer(4 + len * 2);
+ buf.writeInt32BE(len, 0);
+ for (var i = 0; i < len; ++i)
+ buf.writeInt16BE(msg.charCodeAt(i), 4 + i * 2);
+ socket.write(buf);
+ },
+ close: function() {
+ if (socket === null) throw new Error("Com not open");
+ socket.end();
+ }
+ }
+ }).call(this);
+ """
+ )
+
+ def send(msg: String): Unit = {
+ if (awaitConnection()) {
+ jvm2js.writeInt(msg.length)
+ jvm2js.writeChars(msg)
+ jvm2js.flush()
+ }
+ }
+
+ def receive(): String = {
+ if (!awaitConnection())
+ throw new ComJSEnv.ComClosedException
+ try {
+ val len = js2jvm.readInt()
+ val carr = Array.fill(len)(js2jvm.readChar())
+ String.valueOf(carr)
+ } catch {
+ case e: EOFException =>
+ throw new ComJSEnv.ComClosedException
+ }
+ }
+
+ def close(): Unit = {
+ serverSocket.close()
+ if (jvm2js != null)
+ jvm2js.close()
+ if (js2jvm != null)
+ js2jvm.close()
+ if (comSocket != null)
+ comSocket.close()
+ }
+
+ override def stop(): Unit = {
+ close()
+ super.stop()
+ }
+
+ /** Waits until the JS VM has established a connection or terminates
+ * @return true if the connection was established
+ */
+ private def awaitConnection(): Boolean = {
+ serverSocket.setSoTimeout(acceptTimeout)
+ while (comSocket == null && isRunning) {
+ try {
+ comSocket = serverSocket.accept()
+ jvm2js = new DataOutputStream(
+ new BufferedOutputStream(comSocket.getOutputStream()))
+ js2jvm = new DataInputStream(
+ new BufferedInputStream(comSocket.getInputStream()))
+ } catch {
+ case to: SocketTimeoutException =>
+ }
+ }
+
+ comSocket != null
+ }
+
+ override protected def initFiles(): Seq[VirtualJSFile] =
+ super.initFiles :+ comSetup
+
+ override protected def finalize(): Unit = close()
+ }
+
+ protected trait AbstractNodeRunner extends AbstractExtRunner {
+
+ protected[this] val libCache = new VirtualFileMaterializer(true)
+
+ /** File(s) to automatically install source-map-support.
+ * Is used by [[initFiles]], override to change/disable.
+ */
+ protected def installSourceMap(): Seq[VirtualJSFile] = Seq(
+ new MemVirtualJSFile("sourceMapSupport.js").withContent(
+ """
+ try {
+ require('source-map-support').install();
+ } catch (e) {}
+ """
+ )
+ )
+
+ /** File(s) to hack console.log to prevent if from changing `%%` to `%`.
+ * Is used by [[initFiles]], override to change/disable.
+ */
+ protected def fixPercentConsole(): Seq[VirtualJSFile] = Seq(
+ new MemVirtualJSFile("nodeConsoleHack.js").withContent(
+ """
+ // Hack console log to duplicate double % signs
+ (function() {
+ var oldLog = console.log;
+ var newLog = function() {
+ var args = arguments;
+ if (args.length >= 1 && args[0] !== void 0 && args[0] !== null) {
+ args[0] = args[0].toString().replace(/%/g, "%%");
+ }
+ oldLog.apply(console, args);
+ };
+ console.log = newLog;
+ })();
+ """
+ )
+ )
+
+ /** File(s) to define `__ScalaJSEnv`. Defines `exitFunction`.
+ * Is used by [[initFiles]], override to change/disable.
+ */
+ protected def runtimeEnv(): Seq[VirtualJSFile] = Seq(
+ new MemVirtualJSFile("scalaJSEnvInfo.js").withContent(
+ """
+ __ScalaJSEnv = {
+ exitFunction: function(status) { process.exit(status); }
+ };
+ """
+ )
+ )
+
+ /** Concatenates results from [[installSourceMap]], [[fixPercentConsole]] and
+ * [[runtimeEnv]] (in this order).
+ */
+ override protected def initFiles(): Seq[VirtualJSFile] =
+ installSourceMap() ++ fixPercentConsole() ++ runtimeEnv()
+
+ /** Libraries are loaded via require in Node.js */
+ override protected def getLibJSFiles(): Seq[VirtualJSFile] = {
+ initFiles() ++
+ classpath.jsLibs.map(requireLibrary) :+
+ classpath.scalaJSCode
+ }
+
+ /** Rewrites a library virtual file to a require statement if possible */
+ protected def requireLibrary(dep: ResolvedJSDependency): VirtualJSFile = {
+ dep.info.commonJSName.fold(dep.lib) { varname =>
+ val fname = dep.lib.name
+ libCache.materialize(dep.lib)
+ new MemVirtualJSFile(s"require-$fname").withContent(
+ s"""$varname = require(${toJSstr(fname)});"""
+ )
+ }
+ }
+
+ // Send code to Stdin
+ override protected def sendVMStdin(out: OutputStream): Unit = {
+ sendJS(getJSFiles(), out)
+ }
+
+ /** write a single JS file to a writer using an include fct if appropriate
+ * uses `require` if the file exists on the filesystem
+ */
+ override protected def writeJSFile(file: VirtualJSFile,
+ writer: Writer): Unit = {
+ file match {
+ case file: FileVirtualJSFile =>
+ val fname = toJSstr(file.file.getAbsolutePath)
+ writer.write(s"require($fname);\n")
+ case _ =>
+ super.writeJSFile(file, writer)
+ }
+ }
+
+ // Node.js specific (system) environment
+ override protected def getVMEnv(): Map[String, String] = {
+ val baseNodePath = sys.env.get("NODE_PATH").filter(_.nonEmpty)
+ val nodePath = libCache.cacheDir.getAbsolutePath +
+ baseNodePath.fold("")(p => File.pathSeparator + p)
+
+ sys.env ++ Seq(
+ "NODE_MODULE_CONTEXTS" -> "0",
+ "NODE_PATH" -> nodePath
+ ) ++ additionalEnv
+ }
+ }
+
+}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/JettyWebsocketManager.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/JettyWebsocketManager.scala
new file mode 100644
index 0000000..3dec79c
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/JettyWebsocketManager.scala
@@ -0,0 +1,126 @@
+package scala.scalajs.sbtplugin.env.phantomjs
+
+import javax.servlet.http.HttpServletRequest
+
+import org.eclipse.jetty.server.Server
+import org.eclipse.jetty.server.nio.SelectChannelConnector
+import org.eclipse.jetty.websocket.{WebSocket, WebSocketHandler}
+import org.eclipse.jetty.util.component.{LifeCycle, AbstractLifeCycle}
+import org.eclipse.jetty.util.log
+
+private[phantomjs] final class JettyWebsocketManager(
+ wsListener: WebsocketListener) extends WebsocketManager { thisMgr =>
+
+ private[this] var webSocketConn: WebSocket.Connection = null
+ private[this] var closed = false
+
+ // We can just set the logger here, since we are supposed to be protected by
+ // the private ClassLoader that loads us reflectively.
+ log.Log.setLog(new WSLogger("root"))
+
+ private[this] val connector = new SelectChannelConnector
+
+ connector.setHost("localhost")
+ connector.setPort(0)
+
+ private[this] val server = new Server()
+
+ server.addConnector(connector)
+ server.setHandler(new WebSocketHandler {
+ // Support Hixie 76 for Phantom.js
+ getWebSocketFactory().setMinVersion(-1)
+
+ override def doWebSocketConnect(
+ request: HttpServletRequest, protocol: String): WebSocket =
+ new ComWebSocketListener
+ })
+
+ server.addLifeCycleListener(new AbstractLifeCycle.AbstractLifeCycleListener {
+ override def lifeCycleStarted(event: LifeCycle): Unit = {
+ if (event.isRunning())
+ wsListener.onRunning()
+ }
+ })
+
+ private class ComWebSocketListener extends WebSocket.OnTextMessage {
+ override def onOpen(connection: WebSocket.Connection): Unit = {
+ thisMgr.synchronized {
+ if (isConnected)
+ throw new IllegalStateException("Client connected twice")
+ webSocketConn = connection
+ }
+ wsListener.onOpen()
+ }
+
+ override def onClose(statusCode: Int, reason: String): Unit = {
+ thisMgr.synchronized {
+ webSocketConn = null
+ closed = true
+ }
+ wsListener.onClose()
+ server.stop()
+
+ if (statusCode != 1000) {
+ throw new Exception("Abnormal closing of connection. " +
+ s"Code: $statusCode, Reason: $reason")
+ }
+ }
+
+ override def onMessage(message: String): Unit =
+ wsListener.onMessage(message)
+ }
+
+ private class WSLogger(fullName: String) extends log.AbstractLogger {
+ private[this] var debugEnabled = false
+
+ def debug(msg: String, args: Object*): Unit =
+ if (debugEnabled) log("DEBUG", msg, args)
+
+ def debug(msg: String, thrown: Throwable): Unit =
+ if (debugEnabled) log("DEBUG", msg, thrown)
+
+ def debug(thrown: Throwable): Unit =
+ if (debugEnabled) log("DEBUG", thrown)
+
+ def getName(): String = fullName
+
+ def ignore(ignored: Throwable): Unit = ()
+
+ def info(msg: String, args: Object*): Unit = log("INFO", msg, args)
+ def info(msg: String, thrown: Throwable): Unit = log("INFO", msg, thrown)
+ def info(thrown: Throwable): Unit = log("INFO", thrown)
+
+ def warn(msg: String, args: Object*): Unit = log("WARN", msg, args)
+ def warn(msg: String, thrown: Throwable): Unit = log("WARN", msg, thrown)
+ def warn(thrown: Throwable): Unit = log("WARN", thrown)
+
+ def isDebugEnabled(): Boolean = debugEnabled
+ def setDebugEnabled(enabled: Boolean): Unit = debugEnabled = enabled
+
+ private def log(lvl: String, msg: String, args: Object*): Unit =
+ wsListener.log(s"$lvl: $msg " + args.mkString(", "))
+
+ private def log(lvl: String, msg: String, thrown: Throwable): Unit =
+ wsListener.log(s"$lvl: $msg $thrown\n{$thrown.getStackStrace}")
+
+ private def log(lvl: String, thrown: Throwable): Unit =
+ wsListener.log(s"$lvl: $thrown\n{$thrown.getStackStrace}")
+
+ protected def newLogger(fullName: String) = new WSLogger(fullName)
+ }
+
+ def start(): Unit = server.start()
+
+ def stop(): Unit = server.stop()
+
+ def isConnected: Boolean = webSocketConn != null && !closed
+ def isClosed: Boolean = closed
+
+ def localPort: Int = connector.getLocalPort()
+
+ def sendMessage(msg: String) = synchronized {
+ if (webSocketConn != null)
+ webSocketConn.sendMessage(msg)
+ }
+
+}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/PhantomJSEnv.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/PhantomJSEnv.scala
new file mode 100644
index 0000000..7bb47d2
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/PhantomJSEnv.scala
@@ -0,0 +1,466 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.sbtplugin.env.phantomjs
+
+import scala.scalajs.sbtplugin.env._
+
+import scala.scalajs.tools.io._
+import scala.scalajs.tools.classpath._
+import scala.scalajs.tools.env._
+import scala.scalajs.tools.logging._
+
+import scala.scalajs.sbtplugin.JSUtils._
+
+import java.io.{ Console => _, _ }
+import java.net._
+
+import scala.io.Source
+import scala.collection.mutable
+import scala.annotation.tailrec
+
+class PhantomJSEnv(
+ phantomjsPath: String = "phantomjs",
+ addArgs: Seq[String] = Seq.empty,
+ addEnv: Map[String, String] = Map.empty,
+ val autoExit: Boolean = true,
+ jettyClassLoader: ClassLoader = getClass().getClassLoader()
+) extends ExternalJSEnv(addArgs, addEnv) with ComJSEnv {
+
+ import PhantomJSEnv._
+
+ protected def vmName: String = "PhantomJS"
+ protected def executable: String = phantomjsPath
+
+ override def jsRunner(classpath: CompleteClasspath, code: VirtualJSFile,
+ logger: Logger, console: JSConsole): JSRunner = {
+ new PhantomRunner(classpath, code, logger, console)
+ }
+
+ override def asyncRunner(classpath: CompleteClasspath, code: VirtualJSFile,
+ logger: Logger, console: JSConsole): AsyncJSRunner = {
+ new AsyncPhantomRunner(classpath, code, logger, console)
+ }
+
+ override def comRunner(classpath: CompleteClasspath, code: VirtualJSFile,
+ logger: Logger, console: JSConsole): ComJSRunner = {
+ new ComPhantomRunner(classpath, code, logger, console)
+ }
+
+ protected class PhantomRunner(classpath: CompleteClasspath,
+ code: VirtualJSFile, logger: Logger, console: JSConsole
+ ) extends ExtRunner(classpath, code, logger, console)
+ with AbstractPhantomRunner
+
+ protected class AsyncPhantomRunner(classpath: CompleteClasspath,
+ code: VirtualJSFile, logger: Logger, console: JSConsole
+ ) extends AsyncExtRunner(classpath, code, logger, console)
+ with AbstractPhantomRunner
+
+ protected class ComPhantomRunner(classpath: CompleteClasspath,
+ code: VirtualJSFile, logger: Logger, console: JSConsole
+ ) extends AsyncPhantomRunner(classpath, code, logger, console)
+ with ComJSRunner with WebsocketListener {
+
+ private def loadMgr() = {
+ val clazz = jettyClassLoader.loadClass(
+ "scala.scalajs.sbtplugin.env.phantomjs.JettyWebsocketManager")
+
+ val ctors = clazz.getConstructors()
+ assert(ctors.length == 1, "JettyWebsocketManager may only have one ctor")
+
+ val mgr = ctors.head.newInstance(this)
+
+ mgr.asInstanceOf[WebsocketManager]
+ }
+
+ val mgr: WebsocketManager = loadMgr()
+
+ def onRunning(): Unit = synchronized(notifyAll())
+ def onOpen(): Unit = synchronized(notifyAll())
+ def onClose(): Unit = synchronized(notifyAll())
+
+ def onMessage(msg: String): Unit = synchronized {
+ recvBuf.enqueue(msg)
+ notifyAll()
+ }
+
+ def log(msg: String): Unit = logger.debug(s"PhantomJS WS Jetty: $msg")
+
+ private[this] val recvBuf = mutable.Queue.empty[String]
+
+ mgr.start()
+
+ /** The websocket server starts asynchronously, but we need the port it is
+ * running on. This method waits until the port is non-negative and
+ * returns its value.
+ */
+ private def waitForPort(): Int = {
+ while (mgr.localPort < 0)
+ wait()
+ mgr.localPort
+ }
+
+ private def comSetup = {
+ def maybeExit(code: Int) =
+ if (autoExit)
+ s"window.callPhantom({ action: 'exit', returnValue: $code });"
+ else
+ ""
+
+ val serverPort = waitForPort()
+
+ val code = s"""
+ |(function() {
+ | var MaxPayloadSize = $MaxCharPayloadSize;
+ |
+ | // The socket for communication
+ | var websocket = null;
+ |
+ | // Buffer for messages sent before socket is open
+ | var outMsgBuf = null;
+ |
+ | function sendImpl(msg) {
+ | var frags = (msg.length / MaxPayloadSize) | 0;
+ |
+ | for (var i = 0; i < frags; ++i) {
+ | var payload = msg.substring(
+ | i * MaxPayloadSize, (i + 1) * MaxPayloadSize);
+ | websocket.send("1" + payload);
+ | }
+ |
+ | websocket.send("0" + msg.substring(frags * MaxPayloadSize));
+ | }
+ |
+ | function recvImpl(recvCB) {
+ | var recvBuf = "";
+ |
+ | return function(evt) {
+ | var newData = recvBuf + evt.data.substring(1);
+ | if (evt.data.charAt(0) == "0") {
+ | recvBuf = "";
+ | recvCB(newData);
+ | } else if (evt.data.charAt(0) == "1") {
+ | recvBuf = newData;
+ | } else {
+ | throw new Error("Bad fragmentation flag in " + evt.data);
+ | }
+ | };
+ | }
+ |
+ | window.scalajsCom = {
+ | init: function(recvCB) {
+ | if (websocket !== null) throw new Error("Com already open");
+ |
+ | outMsgBuf = [];
+ |
+ | websocket = new WebSocket("ws://localhost:$serverPort");
+ |
+ | websocket.onopen = function(evt) {
+ | for (var i = 0; i < outMsgBuf.length; ++i)
+ | sendImpl(outMsgBuf[i]);
+ | outMsgBuf = null;
+ | };
+ | websocket.onclose = function(evt) {
+ | websocket = null;
+ | ${maybeExit(0)}
+ | };
+ | websocket.onmessage = recvImpl(recvCB);
+ | websocket.onerror = function(evt) {
+ | websocket = null;
+ | throw new Error("Websocket failed: " + evt);
+ | };
+ |
+ | // Take over responsibility to auto exit
+ | window.callPhantom({
+ | action: 'setAutoExit',
+ | autoExit: false
+ | });
+ | },
+ | send: function(msg) {
+ | if (websocket === null)
+ | return; // we are closed already. ignore message
+ |
+ | if (outMsgBuf !== null)
+ | outMsgBuf.push(msg);
+ | else
+ | sendImpl(msg);
+ | },
+ | close: function() {
+ | if (websocket === null)
+ | return; // we are closed already. all is well.
+ |
+ | if (outMsgBuf !== null)
+ | // Reschedule ourselves to give onopen a chance to kick in
+ | window.setTimeout(window.scalajsCom.close, 10);
+ | else
+ | websocket.close();
+ | }
+ | }
+ |}).call(this);""".stripMargin
+
+ new MemVirtualJSFile("comSetup.js").withContent(code)
+ }
+
+ def send(msg: String): Unit = synchronized {
+ if (awaitConnection()) {
+ val fragParts = msg.length / MaxCharPayloadSize
+
+ for (i <- 0 until fragParts) {
+ val payload = msg.substring(
+ i * MaxCharPayloadSize, (i + 1) * MaxCharPayloadSize)
+ mgr.sendMessage("1" + payload)
+ }
+
+ mgr.sendMessage("0" + msg.substring(fragParts * MaxCharPayloadSize))
+ }
+ }
+
+ def receive(): String = synchronized {
+ if (recvBuf.isEmpty && !awaitConnection())
+ throw new ComJSEnv.ComClosedException
+
+ @tailrec
+ def loop(acc: String): String = {
+ val frag = receiveFrag()
+ val newAcc = acc + frag.substring(1)
+
+ if (frag(0) == '0')
+ newAcc
+ else if (frag(0) == '1')
+ loop(newAcc)
+ else
+ throw new AssertionError("Bad fragmentation flag in " + frag)
+ }
+
+ loop("")
+ }
+
+ private def receiveFrag(): String = {
+ while (recvBuf.isEmpty && !mgr.isClosed)
+ wait()
+
+ if (recvBuf.isEmpty)
+ throw new ComJSEnv.ComClosedException
+ else
+ recvBuf.dequeue()
+ }
+
+ def close(): Unit = mgr.stop()
+
+ override def stop(): Unit = {
+ close()
+ super.stop()
+ }
+
+ /** Waits until the JS VM has established a connection, or the VM
+ * terminated. Returns true if a connection was established.
+ */
+ private def awaitConnection(): Boolean = {
+ while (!mgr.isConnected && !mgr.isClosed && isRunning)
+ wait(200) // We sleep-wait for isRunning
+
+ mgr.isConnected
+ }
+
+ override protected def initFiles(): Seq[VirtualJSFile] =
+ super.initFiles :+ comSetup
+ }
+
+ protected trait AbstractPhantomRunner extends AbstractExtRunner {
+
+ override protected def getVMArgs() =
+ // Add launcher file to arguments
+ additionalArgs :+ createTmpLauncherFile().getAbsolutePath
+
+ /** In phantom.js, we include JS using HTML */
+ override protected def writeJSFile(file: VirtualJSFile, writer: Writer) = {
+ file match {
+ case file: FileVirtualJSFile =>
+ val fname = htmlEscape(file.file.getAbsolutePath)
+ writer.write(
+ s"""<script type="text/javascript" src="$fname"></script>""" + "\n")
+ case _ =>
+ writer.write("""<script type="text/javascript">""" + "\n")
+ writer.write(s"// Virtual File: ${file.path}\n")
+ writer.write(file.content)
+ writer.write("</script>\n")
+ }
+ }
+
+ /**
+ * PhantomJS doesn't support Function.prototype.bind. We polyfill it.
+ * https://github.com/ariya/phantomjs/issues/10522
+ */
+ override protected def initFiles(): Seq[VirtualJSFile] = Seq(
+ new MemVirtualJSFile("bindPolyfill.js").withContent(
+ """
+ |// Polyfill for Function.bind from Facebook react:
+ |// https://github.com/facebook/react/blob/3dc10749080a460e48bee46d769763ec7191ac76/src/test/phantomjs-shims.js
+ |// Originally licensed under Apache 2.0
+ |(function() {
+ |
+ | var Ap = Array.prototype;
+ | var slice = Ap.slice;
+ | var Fp = Function.prototype;
+ |
+ | if (!Fp.bind) {
+ | // PhantomJS doesn't support Function.prototype.bind natively, so
+ | // polyfill it whenever this module is required.
+ | Fp.bind = function(context) {
+ | var func = this;
+ | var args = slice.call(arguments, 1);
+ |
+ | function bound() {
+ | var invokedAsConstructor = func.prototype && (this instanceof func);
+ | return func.apply(
+ | // Ignore the context parameter when invoking the bound function
+ | // as a constructor. Note that this includes not only constructor
+ | // invocations using the new keyword but also calls to base class
+ | // constructors such as BaseClass.call(this, ...) or super(...).
+ | !invokedAsConstructor && context || this,
+ | args.concat(slice.call(arguments))
+ | );
+ | }
+ |
+ | // The bound function must share the .prototype of the unbound
+ | // function so that any object created by one constructor will count
+ | // as an instance of both constructors.
+ | bound.prototype = func.prototype;
+ |
+ | return bound;
+ | };
+ | }
+ |
+ |})();
+ |""".stripMargin
+ ),
+ new MemVirtualJSFile("scalaJSEnvInfo.js").withContent(
+ """
+ |__ScalaJSEnv = {
+ | exitFunction: function(status) {
+ | window.callPhantom({
+ | action: 'exit',
+ | returnValue: status | 0
+ | });
+ | }
+ |};
+ """.stripMargin
+ )
+ )
+
+ protected def writeWebpageLauncher(out: Writer): Unit = {
+ out.write("<html>\n<head>\n<title>Phantom.js Launcher</title>\n")
+ sendJS(getLibJSFiles(), out)
+ writeCodeLauncher(code, out)
+ out.write("</head>\n<body></body>\n</html>\n")
+ }
+
+ protected def createTmpLauncherFile(): File = {
+ val webF = createTmpWebpage()
+
+ val launcherTmpF = File.createTempFile("phantomjs-launcher", ".js")
+ launcherTmpF.deleteOnExit()
+
+ val out = new FileWriter(launcherTmpF)
+
+ try {
+ out.write(
+ s"""// Scala.js Phantom.js launcher
+ |var page = require('webpage').create();
+ |var url = ${toJSstr(webF.getAbsolutePath)};
+ |var autoExit = $autoExit;
+ |page.onConsoleMessage = function(msg) {
+ | console.log(msg);
+ |};
+ |page.onError = function(msg, trace) {
+ | console.error(msg);
+ | if (trace && trace.length) {
+ | console.error('');
+ | trace.forEach(function(t) {
+ | console.error(' ' + t.file + ':' + t.line + (t.function ? ' (in function "' + t.function +'")' : ''));
+ | });
+ | }
+ |
+ | phantom.exit(2);
+ |};
+ |page.onCallback = function(data) {
+ | if (!data.action) {
+ | console.error('Called callback without action');
+ | phantom.exit(3);
+ | } else if (data.action === 'exit') {
+ | phantom.exit(data.returnValue || 0);
+ | } else if (data.action === 'setAutoExit') {
+ | if (typeof(data.autoExit) === 'boolean')
+ | autoExit = data.autoExit;
+ | else
+ | autoExit = true;
+ | } else {
+ | console.error('Unknown callback action ' + data.action);
+ | phantom.exit(4);
+ | }
+ |};
+ |page.open(url, function (status) {
+ | if (autoExit || status !== 'success')
+ | phantom.exit(status !== 'success');
+ |});
+ |""".stripMargin)
+ } finally {
+ out.close()
+ }
+
+ logger.debug(
+ "PhantomJS using launcher at: " + launcherTmpF.getAbsolutePath())
+
+ launcherTmpF
+ }
+
+ protected def createTmpWebpage(): File = {
+ val webTmpF = File.createTempFile("phantomjs-launcher-webpage", ".html")
+ webTmpF.deleteOnExit()
+
+ val out = new BufferedWriter(new FileWriter(webTmpF))
+ try {
+ writeWebpageLauncher(out)
+ } finally {
+ out.close()
+ }
+
+ logger.debug(
+ "PhantomJS using webpage launcher at: " + webTmpF.getAbsolutePath())
+
+ webTmpF
+ }
+
+ protected def writeCodeLauncher(code: VirtualJSFile, out: Writer): Unit = {
+ out.write("""<script type="text/javascript">""" + "\n")
+ out.write("// Phantom.js code launcher\n")
+ out.write(s"// Origin: ${code.path}\n")
+ out.write("window.addEventListener('load', function() {\n")
+ out.write(code.content)
+ out.write("}, false);\n")
+ out.write("</script>\n")
+ }
+ }
+
+ protected def htmlEscape(str: String): String = str.flatMap {
+ case '<' => "&lt;"
+ case '>' => "&gt;"
+ case '"' => "&quot;"
+ case '&' => "&amp;"
+ case c => c :: Nil
+ }
+
+}
+
+object PhantomJSEnv {
+ private final val MaxByteMessageSize = 32768 // 32 KB
+ private final val MaxCharMessageSize = MaxByteMessageSize / 2 // 2B per char
+ private final val MaxCharPayloadSize = MaxCharMessageSize - 1 // frag flag
+}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/PhantomJettyClassLoader.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/PhantomJettyClassLoader.scala
new file mode 100644
index 0000000..02c229b
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/PhantomJettyClassLoader.scala
@@ -0,0 +1,63 @@
+package scala.scalajs.sbtplugin.env.phantomjs
+
+import scala.scalajs.tools.io.IO
+
+/** A special [[ClassLoader]] to load the Jetty 8 dependency of [[PhantomJSEnv]]
+ * in a private space.
+ *
+ * It loads everything that belongs to [[JettyWebsocketManager]] itself (while
+ * retrieving the requested class file from its parent.
+ * For all other classes, it first tries to load them from [[jettyLoader]],
+ * which should only contain the Jetty 8 classpath.
+ * If this fails, it delegates to its parent.
+ *
+ * The rationale is, that [[JettyWebsocketManager]] and its dependees can use
+ * the classes on the Jetty 8 classpath, while they remain hidden from the rest
+ * of the Java world. This allows to load another version of Jetty in the same
+ * JVM for the rest of the project.
+ */
+private[sbtplugin] class PhantomJettyClassLoader(jettyLoader: ClassLoader,
+ parent: ClassLoader) extends ClassLoader(parent) {
+
+ def this(loader: ClassLoader) =
+ this(loader, ClassLoader.getSystemClassLoader())
+
+ /** Classes needed to bridge private jetty classpath and public PhantomJS
+ * Basically everything defined in JettyWebsocketManager.
+ */
+ private val bridgeClasses = Set(
+ "scala.scalajs.sbtplugin.env.phantomjs.JettyWebsocketManager",
+ "scala.scalajs.sbtplugin.env.phantomjs.JettyWebsocketManager$WSLogger",
+ "scala.scalajs.sbtplugin.env.phantomjs.JettyWebsocketManager$ComWebSocketListener",
+ "scala.scalajs.sbtplugin.env.phantomjs.JettyWebsocketManager$$anon$1",
+ "scala.scalajs.sbtplugin.env.phantomjs.JettyWebsocketManager$$anon$2"
+ )
+
+ override protected def loadClass(name: String, resolve: Boolean): Class[_] = {
+ if (bridgeClasses.contains(name)) {
+ // Load bridgeClasses manually since they must be associated to this
+ // class loader, rather than the parent class loader in order to find the
+ // jetty classes
+
+ // First check if we have loaded it already
+ Option(findLoadedClass(name)) getOrElse {
+ val wsManager =
+ parent.getResourceAsStream(name.replace('.', '/') + ".class")
+
+ if (wsManager == null) {
+ throw new ClassNotFoundException(name)
+ } else {
+ val buf = IO.readInputStreamToByteArray(wsManager)
+ defineClass(name, buf, 0, buf.length)
+ }
+ }
+ } else {
+ try {
+ jettyLoader.loadClass(name)
+ } catch {
+ case _: ClassNotFoundException =>
+ super.loadClass(name, resolve)
+ }
+ }
+ }
+}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/WebsocketListener.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/WebsocketListener.scala
new file mode 100644
index 0000000..4faac64
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/WebsocketListener.scala
@@ -0,0 +1,10 @@
+package scala.scalajs.sbtplugin.env.phantomjs
+
+private[phantomjs] trait WebsocketListener {
+ def onRunning(): Unit
+ def onOpen(): Unit
+ def onClose(): Unit
+ def onMessage(msg: String): Unit
+
+ def log(msg: String): Unit
+}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/WebsocketManager.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/WebsocketManager.scala
new file mode 100644
index 0000000..a466841
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/WebsocketManager.scala
@@ -0,0 +1,10 @@
+package scala.scalajs.sbtplugin.env.phantomjs
+
+private[phantomjs] trait WebsocketManager {
+ def start(): Unit
+ def stop(): Unit
+ def sendMessage(msg: String): Unit
+ def localPort: Int
+ def isConnected: Boolean
+ def isClosed: Boolean
+}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/LazyScalaJSScope.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/LazyScalaJSScope.scala
new file mode 100644
index 0000000..d4cdaee
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/LazyScalaJSScope.scala
@@ -0,0 +1,96 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.sbtplugin.env.rhino
+
+import scala.collection.mutable
+
+import org.mozilla.javascript.Scriptable
+
+/** A proxy for a ScalaJS "scope" field that loads scripts lazily
+ *
+ * E.g., ScalaJS.c, which is a scope with the Scala.js classes, can be
+ * turned to a LazyScalaJSScope. Upon first access to a field of ScalaJS.c,
+ * say ScalaJS.c.scala_Option, the script defining that particular
+ * field will be loaded.
+ * This is possible because the relative path to the script can be derived
+ * from the name of the property being accessed.
+ *
+ * It is immensely useful, because it allows to load lazily only the scripts
+ * that are actually needed.
+ */
+class LazyScalaJSScope(
+ coreLib: ScalaJSCoreLib,
+ globalScope: Scriptable,
+ base: Scriptable,
+ isModule: Boolean = false,
+ isTraitImpl: Boolean = false) extends Scriptable {
+
+ private val fields = mutable.HashMap.empty[String, Any]
+ private var prototype: Scriptable = _
+ private var parentScope: Scriptable = _
+
+ {
+ // Pre-fill fields with the properties of `base`
+ for (id <- base.getIds()) {
+ (id.asInstanceOf[Any]: @unchecked) match {
+ case name: String => put(name, this, base.get(name, base))
+ case index: Int => put(index, this, base.get(index, base))
+ }
+ }
+ }
+
+ private def load(name: String): Unit =
+ coreLib.load(globalScope, propNameToEncodedName(name))
+
+ private def propNameToEncodedName(name: String): String = {
+ if (isTraitImpl) name.split("__")(0)
+ else if (isModule) name + "$"
+ else name
+ }
+
+ override def getClassName() = "LazyScalaJSScope"
+
+ override def get(name: String, start: Scriptable) = {
+ fields.getOrElse(name, {
+ load(name)
+ fields.getOrElse(name, Scriptable.NOT_FOUND)
+ }).asInstanceOf[AnyRef]
+ }
+ override def get(index: Int, start: Scriptable) =
+ get(index.toString, start)
+
+ override def has(name: String, start: Scriptable) =
+ fields.contains(name)
+ override def has(index: Int, start: Scriptable) =
+ has(index.toString, start)
+
+ override def put(name: String, start: Scriptable, value: Any) = {
+ fields(name) = value
+ }
+ override def put(index: Int, start: Scriptable, value: Any) =
+ put(index.toString, start, value)
+
+ override def delete(name: String) = ()
+ override def delete(index: Int) = ()
+
+ override def getPrototype() = prototype
+ override def setPrototype(value: Scriptable) = prototype = value
+
+ override def getParentScope() = parentScope
+ override def setParentScope(value: Scriptable) = parentScope = value
+
+ override def getIds() = fields.keys.toArray
+
+ override def getDefaultValue(hint: java.lang.Class[_]) = {
+ base.getDefaultValue(hint)
+ }
+
+ override def hasInstance(instance: Scriptable) = false
+}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/RhinoJSEnv.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/RhinoJSEnv.scala
new file mode 100644
index 0000000..cd35ff6
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/RhinoJSEnv.scala
@@ -0,0 +1,303 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.sbtplugin.env.rhino
+
+import scala.scalajs.tools.sem.Semantics
+import scala.scalajs.tools.io._
+import scala.scalajs.tools.classpath._
+import scala.scalajs.tools.env._
+import scala.scalajs.tools.logging._
+
+import scala.io.Source
+
+import scala.collection.mutable
+
+import scala.concurrent.{Future, Promise, Await}
+import scala.concurrent.duration.Duration
+
+import org.mozilla.javascript._
+
+class RhinoJSEnv(semantics: Semantics,
+ withDOM: Boolean = false) extends ComJSEnv {
+
+ import RhinoJSEnv._
+
+ /** Executes code in an environment where the Scala.js library is set up to
+ * load its classes lazily.
+ *
+ * Other .js scripts in the inputs are executed eagerly before the provided
+ * `code` is called.
+ */
+ override def jsRunner(classpath: CompleteClasspath, code: VirtualJSFile,
+ logger: Logger, console: JSConsole): JSRunner = {
+ new Runner(classpath, code, logger, console)
+ }
+
+ private class Runner(classpath: CompleteClasspath, code: VirtualJSFile,
+ logger: Logger, console: JSConsole) extends JSRunner {
+ def run(): Unit = internalRunJS(classpath, code, logger, console, None)
+ }
+
+ override def asyncRunner(classpath: CompleteClasspath, code: VirtualJSFile,
+ logger: Logger, console: JSConsole): AsyncJSRunner = {
+ new AsyncRunner(classpath, code, logger, console)
+ }
+
+ private class AsyncRunner(classpath: CompleteClasspath, code: VirtualJSFile,
+ logger: Logger, console: JSConsole) extends AsyncJSRunner {
+
+ private[this] val promise = Promise[Unit]
+
+ private[this] val thread = new Thread {
+ override def run(): Unit = {
+ try {
+ internalRunJS(classpath, code, logger, console, optChannel)
+ promise.success(())
+ } catch {
+ case t: Throwable =>
+ promise.failure(t)
+ }
+ }
+ }
+
+ def start(): Future[Unit] = {
+ thread.start()
+ promise.future
+ }
+
+ def stop(): Unit = thread.interrupt()
+
+ def isRunning(): Boolean = !promise.isCompleted
+
+ def await(): Unit = Await.result(promise.future, Duration.Inf)
+
+ protected def optChannel(): Option[Channel] = None
+ }
+
+ override def comRunner(classpath: CompleteClasspath, code: VirtualJSFile,
+ logger: Logger, console: JSConsole): ComJSRunner = {
+ new ComRunner(classpath, code, logger, console)
+ }
+
+ private class ComRunner(classpath: CompleteClasspath, code: VirtualJSFile,
+ logger: Logger, console: JSConsole)
+ extends AsyncRunner(classpath, code, logger, console) with ComJSRunner {
+
+ private[this] val channel = new Channel
+
+ override protected def optChannel(): Option[Channel] = Some(channel)
+
+ def send(msg: String): Unit = {
+ try {
+ channel.sendToJS(msg)
+ } catch {
+ case _: ChannelClosedException =>
+ throw new ComJSEnv.ComClosedException
+ }
+ }
+
+ def receive(): String = {
+ try {
+ channel.recvJVM()
+ } catch {
+ case _: ChannelClosedException =>
+ throw new ComJSEnv.ComClosedException
+ }
+ }
+
+ def close(): Unit = channel.close()
+
+ override def stop(): Unit = {
+ close()
+ super.stop()
+ }
+
+ }
+
+ private def internalRunJS(classpath: CompleteClasspath, code: VirtualJSFile,
+ logger: Logger, console: JSConsole, optChannel: Option[Channel]): Unit = {
+
+ val context = Context.enter()
+ try {
+ val scope = context.initStandardObjects()
+
+ if (withDOM) {
+ // Fetch env.rhino.js from webjar
+ val name = "env.rhino.js"
+ val path = "/META-INF/resources/webjars/envjs/1.2/" + name
+ val resource = getClass.getResource(path)
+ assert(resource != null, s"need $name as resource")
+
+ // Rhino can't optimize envjs
+ context.setOptimizationLevel(-1)
+
+ // Don't print envjs header
+ scope.addFunction("print", args => ())
+
+ // Pipe file to Rhino
+ val reader = Source.fromURL(resource).bufferedReader
+ context.evaluateReader(scope, reader, name, 1, null);
+
+ // No need to actually define print here: It is captured by envjs to
+ // implement console.log, which we'll override in the next statement
+ }
+
+ // Make sure Rhino does not do its magic for JVM top-level packages (#364)
+ val PackagesObject =
+ ScriptableObject.getProperty(scope, "Packages").asInstanceOf[Scriptable]
+ val topLevelPackageIds = ScriptableObject.getPropertyIds(PackagesObject)
+ for (id <- topLevelPackageIds) (id: Any) match {
+ case name: String => ScriptableObject.deleteProperty(scope, name)
+ case index: Int => ScriptableObject.deleteProperty(scope, index)
+ case _ => // should not happen, I think, but with Rhino you never know
+ }
+
+ // Setup console.log
+ val jsconsole = context.newObject(scope)
+ jsconsole.addFunction("log", _.foreach(console.log _))
+ ScriptableObject.putProperty(scope, "console", jsconsole)
+
+ // Optionally setup scalaJSCom
+ var recvCallback: Option[String => Unit] = None
+ for (channel <- optChannel) {
+ val comObj = context.newObject(scope)
+
+ comObj.addFunction("send", s =>
+ channel.sendToJVM(Context.toString(s(0))))
+
+ comObj.addFunction("init", s => s(0) match {
+ case f: Function =>
+ val cb: String => Unit =
+ msg => f.call(context, scope, scope, Array(msg))
+ recvCallback = Some(cb)
+ case _ =>
+ sys.error("First argument to init must be a function")
+ })
+
+ comObj.addFunction("close", _ => {
+ // Tell JVM side we won't send anything
+ channel.close()
+ // Internally register that we're done
+ recvCallback = None
+ })
+
+ ScriptableObject.putProperty(scope, "scalajsCom", comObj)
+ }
+
+ try {
+ // Make the classpath available. Either through lazy loading or by
+ // simply inserting
+ classpath match {
+ case cp: IRClasspath =>
+ // Setup lazy loading classpath and source mapper
+ val optLoader = if (cp.scalaJSIR.nonEmpty) {
+ val loader = new ScalaJSCoreLib(semantics, cp)
+
+ // Setup sourceMapper
+ val scalaJSenv = context.newObject(scope)
+
+ scalaJSenv.addFunction("sourceMapper", args => {
+ val trace = Context.toObject(args(0), scope)
+ loader.mapStackTrace(trace, context, scope)
+ })
+
+ ScriptableObject.putProperty(scope, "__ScalaJSEnv", scalaJSenv)
+
+ Some(loader)
+ } else {
+ None
+ }
+
+ // Load JS libraries
+ cp.jsLibs.foreach(dep => context.evaluateFile(scope, dep.lib))
+
+ optLoader.foreach(_.insertInto(context, scope))
+ case cp =>
+ cp.allCode.foreach(context.evaluateFile(scope, _))
+ }
+
+ context.evaluateFile(scope, code)
+
+ // Callback the com channel if necessary (if comCallback = None, channel
+ // wasn't initialized on the client)
+ for ((channel, callback) <- optChannel zip recvCallback) {
+ try {
+ while (recvCallback.isDefined)
+ callback(channel.recvJS())
+ } catch {
+ case _: ChannelClosedException =>
+ // the JVM side closed the connection
+ }
+ }
+
+ // Enusre the channel is closed to release JVM side
+ optChannel.foreach(_.close)
+
+ } catch {
+ case e: RhinoException =>
+ // Trace here, since we want to be in the context to trace.
+ logger.trace(e)
+ sys.error(s"Exception while running JS code: ${e.getMessage}")
+ }
+ } finally {
+ Context.exit()
+ }
+ }
+
+}
+
+object RhinoJSEnv {
+
+ /** Communication channel between the Rhino thread and the rest of the JVM */
+ private class Channel {
+ private[this] var _closed = false
+ private[this] val js2jvm = mutable.Queue.empty[String]
+ private[this] val jvm2js = mutable.Queue.empty[String]
+
+ def sendToJS(msg: String): Unit = synchronized {
+ jvm2js.enqueue(msg)
+ notify()
+ }
+
+ def sendToJVM(msg: String): Unit = synchronized {
+ js2jvm.enqueue(msg)
+ notify()
+ }
+
+ def recvJVM(): String = synchronized {
+ while (js2jvm.isEmpty && ensureOpen())
+ wait()
+
+ js2jvm.dequeue()
+ }
+
+ def recvJS(): String = synchronized {
+ while (jvm2js.isEmpty && ensureOpen())
+ wait()
+
+ jvm2js.dequeue()
+ }
+
+ def close(): Unit = synchronized {
+ _closed = true
+ notify()
+ }
+
+ /** Throws if the channel is closed and returns true */
+ private def ensureOpen(): Boolean = {
+ if (_closed)
+ throw new ChannelClosedException
+ true
+ }
+ }
+
+ private class ChannelClosedException extends Exception
+
+}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/ScalaJSCoreLib.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/ScalaJSCoreLib.scala
new file mode 100644
index 0000000..e937e5b
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/ScalaJSCoreLib.scala
@@ -0,0 +1,173 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.sbtplugin.env.rhino
+
+import scala.collection.mutable
+
+import org.mozilla.javascript.{Context, Scriptable}
+
+import scala.scalajs.ir
+
+import scala.scalajs.tools.sem.Semantics
+import scala.scalajs.tools.javascript.{Printers, ScalaJSClassEmitter}
+import scala.scalajs.tools.io._
+import scala.scalajs.tools.classpath._
+import scala.scalajs.tools.corelib._
+
+class ScalaJSCoreLib(semantics: Semantics, classpath: IRClasspath) {
+ import ScalaJSCoreLib._
+
+ private val (providers, exportedSymbols) = {
+ val providers = mutable.Map.empty[String, VirtualScalaJSIRFile]
+ val exportedSymbols = mutable.ListBuffer.empty[String]
+
+ for (irFile <- classpath.scalaJSIR) {
+ val info = irFile.roughInfo
+ providers += info.encodedName -> irFile
+ if (info.isExported)
+ exportedSymbols += info.encodedName
+ }
+
+ (providers, exportedSymbols)
+ }
+
+ def insertInto(context: Context, scope: Scriptable) = {
+ CoreJSLibs.libs(semantics).foreach(context.evaluateFile(scope, _))
+ lazifyScalaJSFields(scope)
+
+ // Make sure exported symbols are loaded
+ val ScalaJS = Context.toObject(scope.get("ScalaJS", scope), scope)
+ val c = Context.toObject(ScalaJS.get("c", ScalaJS), scope)
+ for (encodedName <- exportedSymbols)
+ c.get(encodedName, c)
+ }
+
+ /** Source maps the given stack trace (where possible) */
+ def mapStackTrace(stackTrace: Scriptable,
+ context: Context, scope: Scriptable): Scriptable = {
+ val count = Context.toNumber(stackTrace.get("length", stackTrace)).toInt
+
+ // Maps file -> max line (0-based)
+ val neededMaps = mutable.Map.empty[String, Int]
+
+ // Collect required line counts
+ for (i <- 0 until count) {
+ val elem = Context.toObject(stackTrace.get(i, stackTrace), scope)
+ val fileName = Context.toString(elem.get("fileName", elem))
+
+ if (fileName.endsWith(PseudoFileSuffix) &&
+ providers.contains(fileName.stripSuffix(PseudoFileSuffix))) {
+
+ val curMaxLine = neededMaps.getOrElse(fileName, -1)
+ val reqLine = Context.toNumber(elem.get("lineNumber", elem)).toInt - 1
+
+ if (reqLine > curMaxLine)
+ neededMaps.put(fileName, reqLine)
+ }
+ }
+
+ // Map required files
+ val maps =
+ for ((fileName, maxLine) <- neededMaps)
+ yield (fileName, getSourceMapper(fileName, maxLine))
+
+ // Create new stack trace to return
+ val res = context.newArray(scope, count)
+
+ for (i <- 0 until count) {
+ val elem = Context.toObject(stackTrace.get(i, stackTrace), scope)
+ val fileName = Context.toString(elem.get("fileName", elem))
+ val line = Context.toNumber(elem.get("lineNumber", elem)).toInt - 1
+
+ val pos = maps.get(fileName).fold(ir.Position.NoPosition)(_(line))
+
+ val newElem =
+ if (pos.isDefined) newPosElem(scope, context, elem, pos)
+ else elem
+
+ res.put(i, res, newElem)
+ }
+
+ res
+ }
+
+ private def getSourceMapper(fileName: String, untilLine: Int) = {
+ val irFile = providers(fileName.stripSuffix(PseudoFileSuffix))
+ val mapper = new Printers.ReverseSourceMapPrinter(untilLine)
+ val classDef = irFile.tree
+ val desugared = new ScalaJSClassEmitter(semantics).genClassDef(classDef)
+ mapper.reverseSourceMap(desugared)
+ mapper
+ }
+
+ private def newPosElem(scope: Scriptable, context: Context,
+ origElem: Scriptable, pos: ir.Position): Scriptable = {
+ assert(pos.isDefined)
+
+ val elem = context.newObject(scope)
+
+ elem.put("declaringClass", elem, origElem.get("declaringClass", origElem))
+ elem.put("methodName", elem, origElem.get("methodName", origElem))
+ elem.put("fileName", elem, pos.source.toString)
+ elem.put("lineNumber", elem, pos.line + 1)
+ elem.put("columnNumber", elem, pos.column + 1)
+
+ elem
+ }
+
+ private val scalaJSLazyFields = Seq(
+ Info("d"),
+ Info("c"),
+ Info("h"),
+ Info("i", isTraitImpl = true),
+ Info("n", isModule = true),
+ Info("m", isModule = true),
+ Info("is"),
+ Info("as"),
+ Info("isArrayOf"),
+ Info("asArrayOf"))
+
+ private def lazifyScalaJSFields(scope: Scriptable) = {
+ val ScalaJS = Context.toObject(scope.get("ScalaJS", scope), scope)
+
+ def makeLazyScalaJSScope(base: Scriptable, isModule: Boolean, isTraitImpl: Boolean) =
+ new LazyScalaJSScope(this, scope, base, isModule, isTraitImpl)
+
+ for (Info(name, isModule, isTraitImpl) <- scalaJSLazyFields) {
+ val base = ScalaJS.get(name, ScalaJS).asInstanceOf[Scriptable]
+ val lazified = makeLazyScalaJSScope(base, isModule, isTraitImpl)
+ ScalaJS.put(name, ScalaJS, lazified)
+ }
+ }
+
+ private[rhino] def load(scope: Scriptable, encodedName: String): Unit = {
+ providers.get(encodedName) foreach { irFile =>
+ val codeWriter = new java.io.StringWriter
+ val printer = new Printers.JSTreePrinter(codeWriter)
+ val classDef = irFile.tree
+ val desugared = new ScalaJSClassEmitter(semantics).genClassDef(classDef)
+ printer.printTopLevelTree(desugared)
+ printer.complete()
+ val ctx = Context.getCurrentContext()
+ val fakeFileName = encodedName + PseudoFileSuffix
+ ctx.evaluateString(scope, codeWriter.toString(),
+ fakeFileName, 1, null)
+ }
+ }
+}
+
+object ScalaJSCoreLib {
+ private case class Info(name: String,
+ isModule: Boolean = false, isTraitImpl: Boolean = false)
+
+ private val EncodedNameLine = raw""""encodedName": *"([^"]+)"""".r.unanchored
+
+ private final val PseudoFileSuffix = ".sjsir"
+}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/package.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/package.scala
new file mode 100644
index 0000000..926fbb2
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/package.scala
@@ -0,0 +1,42 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.sbtplugin.env
+
+import org.mozilla.javascript._
+
+import scala.scalajs.tools.io._
+
+package object rhino {
+
+ implicit class ContextOps(val self: Context) extends AnyVal {
+ def evaluateFile(scope: Scriptable, file: VirtualJSFile,
+ securityDomain: AnyRef = null): Any = {
+ self.evaluateString(scope, file.content, file.path, 1, securityDomain)
+ }
+ }
+
+ implicit class ScriptableObjectOps(val self: Scriptable) {
+ def addFunction(name: String, function: Array[AnyRef] => Any) = {
+ val rhinoFunction =
+ new BaseFunction {
+ ScriptRuntime.setFunctionProtoAndParent(this, self)
+ override def call(context: Context, scope: Scriptable,
+ thisObj: Scriptable, args: Array[AnyRef]): AnyRef = {
+ function(args) match {
+ case () => Undefined.instance
+ case r => r.asInstanceOf[AnyRef]
+ }
+ }
+ }
+
+ ScriptableObject.putProperty(self, name, rhinoFunction)
+ }
+ }
+}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/impl/DependencyBuilders.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/impl/DependencyBuilders.scala
new file mode 100644
index 0000000..32ffb94
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/impl/DependencyBuilders.scala
@@ -0,0 +1,99 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.sbtplugin
+package impl
+
+import scala.language.implicitConversions
+import scala.language.experimental.macros
+
+import sbt._
+
+import StringUtilities.nonEmpty
+
+trait DependencyBuilders {
+ final implicit def toScalaJSGroupID(groupID: String): ScalaJSGroupID = {
+ nonEmpty(groupID, "Group ID")
+ new ScalaJSGroupID(groupID)
+ }
+
+ /** Builder to allow for stuff like:
+ *
+ * ProvidedJS / "foo.js"
+ * ProvidedJS / "foo.js" % "test"
+ *
+ */
+ object ProvidedJS {
+ def /(name: String): ProvidedJSModuleID = ProvidedJSModuleID(name, None)
+ }
+
+ /** Builder to allow for stuff like:
+ *
+ * "org.webjars" % "jquery" % "1.10.2" / "jquery.js"
+ * "org.webjars" % "jquery" % "1.10.2" / "jquery.js" % "test"
+ *
+ */
+ implicit class JSModuleIDBuilder(module: ModuleID) {
+ def /(name: String): JarJSModuleID = JarJSModuleID(module, name)
+ }
+}
+
+final class ScalaJSGroupID private[sbtplugin] (private val groupID: String) {
+ def %%%(artifactID: String): CrossGroupArtifactID =
+ macro ScalaJSGroupID.auto_impl
+
+ def %%%!(artifactID: String): CrossGroupArtifactID =
+ ScalaJSGroupID.withCross(this, artifactID, ScalaJSCrossVersion.binary)
+}
+
+object ScalaJSGroupID {
+ import scala.reflect.macros.Context
+
+ /** Internal. Used by the macro implementing [[ScalaJSGroupID.%%%]]. Use:
+ * {{{
+ * ("a" % artifactID % revision).cross(cross)
+ * }}}
+ * instead.
+ */
+ def withCross(groupID: ScalaJSGroupID, artifactID: String,
+ cross: CrossVersion): CrossGroupArtifactID = {
+ nonEmpty(artifactID, "Artifact ID")
+ new CrossGroupArtifactID(groupID.groupID, artifactID, cross)
+ }
+
+ def auto_impl(c: Context { type PrefixType = ScalaJSGroupID })(
+ artifactID: c.Expr[String]): c.Expr[CrossGroupArtifactID] = {
+ import c.universe._
+
+ // Hack to work around bug in sbt macros (wrong way of collecting local
+ // definitions)
+ val keysSym = rootMirror.staticModule(
+ "_root_.scala.scalajs.sbtplugin.ScalaJSPlugin.autoImport")
+ val keys = c.Expr[ScalaJSPlugin.autoImport.type](Ident(keysSym))
+
+ reify {
+ val cross = {
+ if (keys.splice.jsDependencies.?.value.isDefined)
+ ScalaJSCrossVersion.binary
+ else
+ CrossVersion.binary
+ }
+ ScalaJSGroupID.withCross(c.prefix.splice, artifactID.splice, cross)
+ }
+ }
+
+}
+
+final class CrossGroupArtifactID(groupID: String,
+ artifactID: String, crossVersion: CrossVersion) {
+ def %(revision: String): ModuleID = {
+ nonEmpty(revision, "Revision")
+ ModuleID(groupID, artifactID, revision).cross(crossVersion)
+ }
+}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/Events.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/Events.scala
new file mode 100644
index 0000000..f13c195
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/Events.scala
@@ -0,0 +1,35 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.sbtplugin.testing
+
+import sbt.testing.{Event => SbtEvent, _}
+
+class Events(taskDef: TaskDef) {
+
+ abstract class Event(val status: Status,
+ val throwable: OptionalThrowable = new OptionalThrowable) extends SbtEvent {
+ val fullyQualifiedName = taskDef.fullyQualifiedName
+ val fingerprint = taskDef.fingerprint
+ val selector = taskDef.selectors.headOption.getOrElse(new SuiteSelector)
+ val duration = -1L
+ }
+
+ case class Error(exception: Throwable) extends Event(
+ Status.Error, new OptionalThrowable(exception))
+
+ case class Failure(exception: Throwable) extends Event(
+ Status.Failure, new OptionalThrowable(exception))
+
+ case object Succeeded extends Event(Status.Success)
+ case object Skipped extends Event(Status.Skipped)
+ case object Pending extends Event(Status.Pending)
+ case object Ignored extends Event(Status.Ignored)
+ case object Canceled extends Event(Status.Canceled)
+}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/JSClasspathLoader.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/JSClasspathLoader.scala
new file mode 100644
index 0000000..bfe0ffc
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/JSClasspathLoader.scala
@@ -0,0 +1,15 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.sbtplugin.testing
+
+import scala.scalajs.tools.classpath.CompleteClasspath
+
+/** A dummy ClassLoader to pass on Scala.js ClasspathContents to tests */
+final case class JSClasspathLoader(cp: CompleteClasspath) extends ClassLoader
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/SbtTestLoggerAccWrapper.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/SbtTestLoggerAccWrapper.scala
new file mode 100644
index 0000000..dfebe00
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/SbtTestLoggerAccWrapper.scala
@@ -0,0 +1,22 @@
+package scala.scalajs.sbtplugin.testing
+
+import scala.scalajs.tools.logging._
+import sbt.testing.{ Logger => SbtTestLogger }
+
+class SbtTestLoggerAccWrapper(logger: Seq[SbtTestLogger]) extends Logger {
+
+ import scala.scalajs.sbtplugin.Implicits._
+ import Level._
+
+ def log(level: Level, message: => String): Unit = level match {
+ case Error => logger.foreach(_.error(message))
+ case Warn => logger.foreach(_.warn(message))
+ case Info => logger.foreach(_.info(message))
+ case Debug => logger.foreach(_.debug(message))
+ }
+
+ def success(message: => String): Unit = logger.foreach(_.info(message))
+
+ def trace(t: => Throwable): Unit = logger.foreach(_.trace(t))
+
+}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestException.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestException.scala
new file mode 100644
index 0000000..b4cb09b
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestException.scala
@@ -0,0 +1,9 @@
+package scala.scalajs.sbtplugin.testing
+
+/** Dummy Exception to wrap stack traces passed to SBT */
+class TestException(
+ message: String,
+ stackTrace: Array[StackTraceElement]
+) extends Exception(message) {
+ override def getStackTrace = stackTrace
+}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestFramework.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestFramework.scala
new file mode 100644
index 0000000..ab43bfe
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestFramework.scala
@@ -0,0 +1,52 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.sbtplugin.testing
+
+import scala.scalajs.tools.env._
+import scala.scalajs.tools.classpath._
+
+import sbt._
+import sbt.testing._
+import sbt.classpath.ClasspathFilter
+
+import java.net.URLClassLoader
+
+class TestFramework(
+ environment: JSEnv,
+ jsConsole: JSConsole,
+ testFramework: String) extends Framework {
+
+ val name = "Scala.js Test Framework"
+
+ lazy val fingerprints = Array[Fingerprint](f1)
+
+ private val f1 = new SubclassFingerprint {
+ val isModule = true
+ val superclassName = "scala.scalajs.testbridge.Test"
+ val requireNoArgConstructor = true
+ }
+
+ def runner(args: Array[String], remoteArgs: Array[String],
+ testClassLoader: ClassLoader): Runner = {
+
+ val jsClasspath = extractClasspath(testClassLoader)
+ new TestRunner(environment, jsClasspath, jsConsole,
+ testFramework, args, remoteArgs)
+ }
+
+ /** extract classpath from ClassLoader (which must be a JSClasspathLoader) */
+ private def extractClasspath(cl: ClassLoader) = cl match {
+ case cl: JSClasspathLoader => cl.cp
+ case _ =>
+ sys.error("The Scala.js framework only works with a class loader of " +
+ s"type JSClasspathLoader (${cl.getClass} given)")
+ }
+
+}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestOutputConsole.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestOutputConsole.scala
new file mode 100644
index 0000000..9aad956
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestOutputConsole.scala
@@ -0,0 +1,190 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.sbtplugin.testing
+
+import sbt.testing.Logger
+import sbt.testing.EventHandler
+
+import scala.scalajs.tools.env.JSConsole
+import scala.scalajs.tools.sourcemap.SourceMapper
+import scala.scalajs.tools.classpath.{CompleteClasspath, IRClasspath}
+
+import scala.collection.mutable.ArrayBuffer
+
+import scala.util.Try
+
+import java.util.regex._
+
+/** This parses the messages sent from the test bridge and forwards
+ * the calls to SBT. It also buffers all log messages and allows to
+ * pipe them to multiple loggers in a synchronized fashion. This
+ * ensures that log messages aren't interleaved due to parallelism.
+ */
+class TestOutputConsole(
+ base: JSConsole,
+ handler: EventHandler,
+ events: Events,
+ classpath: CompleteClasspath,
+ noSourceMap: Boolean) extends JSConsole {
+
+ import TestOutputConsole._
+ import events._
+
+ private val traceBuf = new ArrayBuffer[StackTraceElement]
+ private val logBuffer = new ArrayBuffer[LogElement]
+
+ /* See #727: source mapping does not work with CompleteIRClasspath, so
+ * don't bother to try.
+ */
+ private val ignoreSourceMapping =
+ noSourceMap || classpath.isInstanceOf[IRClasspath]
+
+ private lazy val sourceMapper = new SourceMapper(classpath)
+
+ override def log(msg: Any): Unit = {
+ val data = msg.toString
+ val sepPos = data.indexOf("|")
+
+ if (sepPos == -1)
+ log(_.error, s"Malformed message: $data")
+ else {
+ val op = data.substring(0, sepPos)
+ val message = unescape(data.substring(sepPos + 1))
+
+ op match {
+ case "console-log" =>
+ base.log(message)
+ case "error" =>
+ val trace = getTrace()
+ logWithEvent(_.error,
+ messageWithStack(message, trace),
+ Error(new TestException(message, trace))
+ )
+ case "failure" =>
+ val trace = getTrace()
+ logWithEvent(_.error,
+ messageWithStack(message, trace),
+ Failure(new TestException(message, trace))
+ )
+ case "succeeded" =>
+ noTrace()
+ logWithEvent(_.info, message, Succeeded)
+ case "skipped" =>
+ noTrace()
+ logWithEvent(_.info, message, Skipped)
+ case "pending" =>
+ noTrace()
+ logWithEvent(_.info, message, Pending)
+ case "ignored" =>
+ noTrace()
+ logWithEvent(_.info, message, Ignored)
+ case "canceled" =>
+ noTrace()
+ logWithEvent(_.info, message, Canceled)
+ case "error-log" =>
+ noTrace()
+ log(_.error, message)
+ case "info" =>
+ noTrace()
+ log(_.info, message)
+ case "warn" =>
+ noTrace()
+ log(_.warn, message)
+ case "trace" =>
+ val Array(className, methodName, fileName,
+ lineNumberStr, columnNumberStr) = message.split('|')
+
+ def tryParse(num: String, name: String) = Try(num.toInt).getOrElse {
+ log(_.warn, s"Couldn't parse $name number in StackTrace: $num")
+ -1
+ }
+
+ val lineNumber = tryParse(lineNumberStr, "line")
+ val columnNumber = tryParse(columnNumberStr, "column")
+
+ val ste =
+ new StackTraceElement(className, methodName, fileName, lineNumber)
+
+ if (ignoreSourceMapping)
+ traceBuf += ste
+ else
+ traceBuf += sourceMapper.map(ste, columnNumber)
+ case _ =>
+ noTrace()
+ log(_.error, s"Unknown op: $op. Originating log message: $data")
+ }
+ }
+ }
+
+ private def noTrace() = {
+ if (traceBuf.nonEmpty)
+ log(_.warn, s"Discarding ${traceBuf.size} stack elements")
+ traceBuf.clear()
+ }
+
+ private def getTrace() = {
+ val res = traceBuf.toArray
+ traceBuf.clear()
+ res
+ }
+
+ private def messageWithStack(message: String, stack: Array[StackTraceElement]): String =
+ message + stack.mkString("\n", "\n", "")
+
+ private def log(method: LogMethod, message: String): Unit =
+ logBuffer.append(LogElement(method, message))
+
+ private def logWithEvent(method: LogMethod,
+ message: String, event: Event): Unit = {
+ handler handle event
+ log(method, message)
+ }
+
+ def pipeLogsTo(loggers: Array[Logger]): Unit = {
+ TestOutputConsole.synchronized {
+ for {
+ LogElement(method, message) <- logBuffer
+ logger <- loggers
+ } method(logger) {
+ if (logger.ansiCodesSupported) message
+ else removeColors(message)
+ }
+ }
+ }
+
+ def allLogs: List[LogElement] = logBuffer.toList
+
+ private val colorPattern = raw"\033\[\d{1,2}m"
+
+ private def removeColors(message: String): String =
+ message.replaceAll(colorPattern, "")
+
+ private val unEscPat = Pattern.compile("(\\\\\\\\|\\\\n|\\\\r)")
+ private def unescape(message: String): String = {
+ val m = unEscPat.matcher(message)
+ val res = new StringBuffer()
+ while (m.find()) {
+ val repl = m.group() match {
+ case "\\\\" => "\\\\"
+ case "\\n" => "\n"
+ case "\\r" => "\r"
+ }
+ m.appendReplacement(res, repl);
+ }
+ m.appendTail(res);
+ res.toString
+ }
+
+}
+
+object TestOutputConsole {
+ type LogMethod = Logger => (String => Unit)
+ case class LogElement(method: LogMethod, message: String)
+}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestRunner.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestRunner.scala
new file mode 100644
index 0000000..e5ca2a2
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestRunner.scala
@@ -0,0 +1,37 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.sbtplugin.testing
+
+import sbt.testing._
+
+import scala.scalajs.tools.env._
+import scala.scalajs.tools.classpath._
+
+class TestRunner(
+ environment: JSEnv,
+ classpath: CompleteClasspath,
+ jsConsole: JSConsole,
+ testFramework: String,
+ val args: Array[String],
+ val remoteArgs: Array[String]) extends Runner {
+
+ def tasks(taskDefs: Array[TaskDef]): Array[Task] = if (_done) {
+ throw new IllegalStateException("Done has already been called")
+ } else {
+ taskDefs.map(TestTask(environment, classpath, jsConsole, testFramework, args))
+ }
+
+ def done(): String = {
+ _done = true
+ ""
+ }
+
+ private var _done = false
+}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestTask.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestTask.scala
new file mode 100644
index 0000000..b1cabb9
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestTask.scala
@@ -0,0 +1,110 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.sbtplugin.testing
+
+import sbt.testing._
+
+import scala.scalajs.tools.io._
+import scala.scalajs.tools.classpath._
+import scala.scalajs.tools.env._
+
+import scala.scalajs.sbtplugin.JSUtils._
+
+import scala.annotation.tailrec
+import scala.util.control.NonFatal
+
+class TestTask(
+ env: JSEnv,
+ classpath: CompleteClasspath,
+ jsConsole: JSConsole,
+ testFramework: String,
+ args: Array[String],
+ val taskDef: TaskDef) extends Task {
+
+ import TestTask._
+
+ val tags = Array.empty[String]
+ val options = readArgs(args.toList)
+
+ def execute(eventHandler: EventHandler,
+ loggers: Array[Logger]): Array[Task] = {
+
+ val runnerFile = testRunnerFile(options.frameworkArgs)
+ val testConsole = new TestOutputConsole(jsConsole, eventHandler,
+ new Events(taskDef), classpath, options.noSourceMap)
+ val logger = new SbtTestLoggerAccWrapper(loggers)
+
+ // Actually execute test
+ env.jsRunner(classpath, runnerFile, logger, testConsole).run()
+
+ testConsole.pipeLogsTo(loggers)
+
+ Array.empty
+ }
+
+ private def testRunnerFile(args: List[String]) = {
+ val testKey = taskDef.fullyQualifiedName
+
+ // Note that taskDef does also have the selector, fingerprint and
+ // explicitlySpecified value we could pass to the framework. However, we
+ // believe that these are only moderately useful. Therefore, we'll silently
+ // ignore them.
+
+ val jsArgArray = listToJS(args)
+ new MemVirtualJSFile("Generated test launcher file").
+ withContent(s"""this${dot2bracket(testFramework)}().safeRunTest(
+ | scala.scalajs.testbridge.internal.ConsoleTestOutput(),
+ | $jsArgArray,
+ | this${dot2bracket(testKey)});""".stripMargin)
+ }
+
+
+}
+
+object TestTask {
+
+ def apply(environment: JSEnv, classpath: CompleteClasspath,
+ jsConsole: JSConsole, testFramework: String, args: Array[String]
+ )(taskDef: TaskDef) =
+ new TestTask(environment, classpath, jsConsole,
+ testFramework, args, taskDef)
+
+ case class ArgOptions(
+ noSourceMap: Boolean,
+ frameworkArgs: List[String]
+ )
+
+ private def readArgs(args0: List[String]) = {
+ // State for each option
+ var noSourceMap = false
+
+ def mkOptions(frameworkArgs: List[String]) =
+ ArgOptions(noSourceMap, frameworkArgs)
+
+ @tailrec
+ def read0(args: List[String]): ArgOptions = args match {
+ case "-no-source-map" :: xs =>
+ noSourceMap = true
+ read0(xs)
+
+ // Explicitly end our argument list
+ case "--" :: xs =>
+ mkOptions(xs)
+
+ // Unknown argument
+ case xs =>
+ mkOptions(xs)
+
+ }
+
+ read0(args0)
+ }
+
+}
diff --git a/examples/scala-js/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/AsyncTests.scala b/examples/scala-js/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/AsyncTests.scala
new file mode 100644
index 0000000..422c17b
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/AsyncTests.scala
@@ -0,0 +1,37 @@
+package scala.scalajs.sbtplugin.test.env
+
+import scala.scalajs.tools.env._
+import scala.scalajs.tools.io._
+import scala.scalajs.tools.classpath.PartialClasspath
+import scala.scalajs.tools.logging._
+
+import org.junit.Test
+import org.junit.Assert._
+
+import scala.concurrent.Await
+import scala.concurrent.duration.Duration
+
+/** A couple of tests that test communication for mix-in into a test suite */
+trait AsyncTests {
+
+ protected def newJSEnv: AsyncJSEnv
+
+ private def emptyCP = PartialClasspath.empty.resolve()
+
+ private def asyncRunner(code: String) = {
+ val codeVF = new MemVirtualJSFile("testScript.js").withContent(code)
+ newJSEnv.asyncRunner(emptyCP, codeVF,
+ new ScalaConsoleLogger(Level.Warn), ConsoleJSConsole)
+ }
+
+ @Test
+ def futureTest = {
+ val runner = asyncRunner("")
+ val fut = runner.start()
+
+ Await.result(fut, Duration.Inf)
+
+ assertFalse("VM should be terminated", runner.isRunning)
+ }
+
+}
diff --git a/examples/scala-js/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/ComTests.scala b/examples/scala-js/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/ComTests.scala
new file mode 100644
index 0000000..c16decd
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/ComTests.scala
@@ -0,0 +1,206 @@
+package scala.scalajs.sbtplugin.test.env
+
+import scala.scalajs.tools.env._
+import scala.scalajs.tools.io._
+import scala.scalajs.tools.classpath.PartialClasspath
+import scala.scalajs.tools.logging._
+
+import org.junit.Test
+import org.junit.Assert._
+
+/** A couple of tests that test communication for mix-in into a test suite */
+trait ComTests extends AsyncTests {
+
+ protected def newJSEnv: ComJSEnv
+
+ private def emptyCP = PartialClasspath.empty.resolve()
+
+ private def comRunner(code: String) = {
+ val codeVF = new MemVirtualJSFile("testScript.js").withContent(code)
+ newJSEnv.comRunner(emptyCP, codeVF,
+ new ScalaConsoleLogger(Level.Warn), ConsoleJSConsole)
+ }
+
+ private def assertThrowClosed(msg: String, body: => Unit): Unit = {
+ val thrown = try {
+ body
+ false
+ } catch {
+ case _: ComJSEnv.ComClosedException =>
+ true
+ }
+
+ assertTrue(msg, thrown)
+ }
+
+ @Test
+ def comCloseJVMTest = {
+ val com = comRunner(s"""
+ scalajsCom.init(function(msg) { scalajsCom.send("received: " + msg); });
+ scalajsCom.send("Hello World");
+ """)
+
+ com.start()
+
+ assertEquals("Hello World", com.receive())
+
+ for (i <- 0 to 10) {
+ com.send(i.toString)
+ assertEquals(s"received: $i", com.receive())
+ }
+
+ com.close()
+ com.await()
+ }
+
+ def comCloseJSTestCommon(timeout: Long) = {
+ val com = comRunner(s"""
+ scalajsCom.init(function(msg) {});
+ for (var i = 0; i < 10; ++i)
+ scalajsCom.send("msg: " + i);
+ scalajsCom.close();
+ """)
+
+ com.start()
+
+ Thread.sleep(timeout)
+
+ for (i <- 0 until 10)
+ assertEquals(s"msg: $i", com.receive())
+
+ assertThrowClosed("Expect receive to throw after closing of channel",
+ com.receive())
+
+ com.close()
+ com.await()
+ }
+
+ @Test
+ def comCloseJSTest = comCloseJSTestCommon(0)
+
+ @Test
+ def comCloseJSTestDelayed = comCloseJSTestCommon(1000)
+
+ @Test
+ def doubleCloseTest = {
+ val n = 10
+ val com = pingPongRunner(n)
+
+ com.start()
+
+ for (i <- 0 until n) {
+ com.send("ping")
+ assertEquals("pong", com.receive())
+ }
+
+ com.close()
+ com.await()
+ }
+
+ @Test
+ def multiEnvTest = {
+ val n = 10
+ val envs = List.fill(5)(pingPongRunner(10))
+
+ envs.foreach(_.start())
+
+ val ops = List[ComJSRunner => Unit](
+ _.send("ping"),
+ com => assertEquals("pong", com.receive())
+ )
+
+ for {
+ i <- 0 until n
+ env <- envs
+ op <- ops
+ } op(env)
+
+ envs.foreach(_.close())
+ envs.foreach(_.await())
+ }
+
+ private def pingPongRunner(count: Int) = {
+ comRunner(s"""
+ var seen = 0;
+ scalajsCom.init(function(msg) {
+ scalajsCom.send("pong");
+ if (++seen >= $count)
+ scalajsCom.close();
+ });
+ """)
+ }
+
+ @Test
+ def largeMessageTest = {
+ // 1KB data
+ val baseMsg = new String(Array.tabulate(512)(_.toChar))
+ val baseLen = baseMsg.length
+
+ // Max message size: 1KB * 2^(2*iters+1) = 1MB
+ val iters = 4
+
+ val com = comRunner("""
+ scalajsCom.init(function(msg) {
+ scalajsCom.send(msg + msg);
+ });
+ """)
+
+ com.start()
+
+ com.send(baseMsg)
+
+ def resultFactor(iters: Int) = Math.pow(2, 2 * iters + 1).toInt
+
+ for (i <- 0 until iters) {
+ val reply = com.receive()
+
+ val factor = resultFactor(i)
+
+ assertEquals(baseLen * factor, reply.length)
+
+ for (j <- 0 until factor)
+ assertEquals(baseMsg, reply.substring(j * baseLen, (j + 1) * baseLen))
+
+ com.send(reply + reply)
+ }
+
+ val lastLen = com.receive().length
+ assertEquals(baseLen * resultFactor(iters), lastLen)
+
+ com.close()
+ com.await()
+ }
+
+ @Test
+ def noInitTest = {
+ val com = comRunner("")
+
+ com.start()
+ com.send("Dummy")
+ com.close()
+ com.await()
+ }
+
+ @Test
+ def stopTest = {
+ val com = comRunner(s"""scalajsCom.init(function(msg) {});""")
+
+ com.start()
+
+ // Make sure the VM doesn't terminate.
+ Thread.sleep(1000)
+
+ assertTrue("VM should still be running", com.isRunning)
+
+ // Stop VM instead of closing channel
+ com.stop()
+
+ try {
+ com.await()
+ fail("Stopped VM should be in failure state")
+ } catch {
+ case _: Throwable =>
+ }
+ }
+
+}
diff --git a/examples/scala-js/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/JSEnvTest.scala b/examples/scala-js/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/JSEnvTest.scala
new file mode 100644
index 0000000..2a44c80
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/JSEnvTest.scala
@@ -0,0 +1,44 @@
+package scala.scalajs.sbtplugin.test.env
+
+import scala.scalajs.tools.env.JSEnv
+import scala.scalajs.tools.io.MemVirtualJSFile
+import scala.scalajs.tools.classpath.PartialClasspath
+import scala.scalajs.tools.logging.NullLogger
+import scala.scalajs.tools.env.NullJSConsole
+
+import org.junit.Assert._
+
+abstract class JSEnvTest {
+
+ protected def newJSEnv: JSEnv
+
+ implicit class RunMatcher(codeStr: String) {
+
+ val emptyCP = PartialClasspath.empty.resolve()
+ val code = new MemVirtualJSFile("testScript.js").withContent(codeStr)
+
+ def hasOutput(expectedOut: String): Unit = {
+
+ val console = new StoreJSConsole()
+ val logger = new StoreLogger()
+
+ newJSEnv.jsRunner(emptyCP, code, logger, console).run()
+
+ val log = logger.getLog
+
+ assertTrue("VM shouldn't produce log. Log:\n" +
+ log.mkString("\n"), log.isEmpty)
+ assertEquals("Output should match", expectedOut, console.getLog)
+ }
+
+ def fails(): Unit = {
+ try {
+ newJSEnv.jsRunner(emptyCP, code, NullLogger, NullJSConsole).run()
+ assertTrue("Code snipped should fail", false)
+ } catch {
+ case e: Exception =>
+ }
+ }
+ }
+
+}
diff --git a/examples/scala-js/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/NodeJSTest.scala b/examples/scala-js/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/NodeJSTest.scala
new file mode 100644
index 0000000..9a58b5c
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/NodeJSTest.scala
@@ -0,0 +1,54 @@
+package scala.scalajs.sbtplugin.test.env
+
+import scala.scalajs.sbtplugin.env.nodejs.NodeJSEnv
+
+import org.junit.Test
+
+class NodeJSTest extends JSEnvTest with ComTests {
+
+ protected def newJSEnv = new NodeJSEnv
+
+ /** Node.js strips double percentage signs - #500 */
+ @Test
+ def percentageTest = {
+ val counts = 1 to 15
+ val argcs = 1 to 3
+ val strings = counts.map("%" * _)
+
+ val strlists = for {
+ count <- argcs
+ string <- strings
+ } yield List.fill(count)(string)
+
+ val codes = for {
+ strlist <- strlists
+ } yield {
+ val args = strlist.map(s => s""""$s"""").mkString(", ")
+ s"console.log($args);\n"
+ }
+
+ val result = strlists.map(_.mkString(" ") + "\n").mkString("")
+
+ codes.mkString("").hasOutput(result)
+ }
+
+ /** Node.js console.log hack didn't allow to log non-Strings - #561 */
+ @Test
+ def nonStringTest = {
+
+ """
+ console.log(1);
+ console.log(undefined);
+ console.log(null);
+ console.log({});
+ console.log([1,2]);
+ """ hasOutput
+ """|1
+ |undefined
+ |null
+ |[object Object]
+ |1,2
+ |""".stripMargin
+ }
+
+}
diff --git a/examples/scala-js/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/PhantomJSTest.scala b/examples/scala-js/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/PhantomJSTest.scala
new file mode 100644
index 0000000..23e240d
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/PhantomJSTest.scala
@@ -0,0 +1,21 @@
+package scala.scalajs.sbtplugin.test.env
+
+import scala.scalajs.sbtplugin.env.phantomjs.PhantomJSEnv
+
+import org.junit.Test
+
+class PhantomJSTest extends JSEnvTest with ComTests {
+
+ protected def newJSEnv = new PhantomJSEnv
+
+ @Test
+ def failureTest = {
+
+ """
+ var a = {};
+ a.foo();
+ """.fails()
+
+ }
+
+}
diff --git a/examples/scala-js/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/RhinoJSEnvTest.scala b/examples/scala-js/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/RhinoJSEnvTest.scala
new file mode 100644
index 0000000..4066c41
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/RhinoJSEnvTest.scala
@@ -0,0 +1,9 @@
+package scala.scalajs.sbtplugin.test.env
+
+import scala.scalajs.sbtplugin.env.rhino._
+
+import scala.scalajs.tools.sem._
+
+class RhinoJSEnvTest extends ComTests {
+ protected def newJSEnv = new RhinoJSEnv(Semantics.Defaults)
+}
diff --git a/examples/scala-js/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/StoreJSConsole.scala b/examples/scala-js/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/StoreJSConsole.scala
new file mode 100644
index 0000000..9c7a84a
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/StoreJSConsole.scala
@@ -0,0 +1,14 @@
+package scala.scalajs.sbtplugin.test.env
+
+import scala.scalajs.tools.env._
+
+class StoreJSConsole extends JSConsole {
+ private[this] val buf = new StringBuilder()
+
+ def log(msg: Any): Unit = {
+ buf.append(msg.toString)
+ buf.append('\n')
+ }
+
+ def getLog: String = buf.toString
+}
diff --git a/examples/scala-js/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/StoreLogger.scala b/examples/scala-js/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/StoreLogger.scala
new file mode 100644
index 0000000..985b149
--- /dev/null
+++ b/examples/scala-js/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/StoreLogger.scala
@@ -0,0 +1,29 @@
+package scala.scalajs.sbtplugin.test.env
+
+import scala.scalajs.tools.logging._
+
+import scala.collection.mutable.ListBuffer
+
+class StoreLogger extends Logger {
+ import StoreLogger._
+
+ private[this] val buf = new ListBuffer[LogElem]
+
+ def log(level: Level, message: => String): Unit =
+ buf += Log(level, message)
+ def success(message: => String): Unit =
+ buf += Success(message)
+ def trace(t: => Throwable): Unit =
+ buf += Trace(t)
+
+ def getLog: List[LogElem] = buf.toList
+}
+
+object StoreLogger {
+
+ abstract class LogElem
+ case class Log(level: Level, message: String) extends LogElem
+ case class Success(message: String) extends LogElem
+ case class Trace(t: Throwable) extends LogElem
+
+}
diff --git a/examples/scala-js/scalalib/overrides-2.10/scala/Console.scala b/examples/scala-js/scalalib/overrides-2.10/scala/Console.scala
new file mode 100644
index 0000000..7fc2d50
--- /dev/null
+++ b/examples/scala-js/scalalib/overrides-2.10/scala/Console.scala
@@ -0,0 +1,468 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+
+
+package scala
+
+import java.io.{BufferedReader, InputStream, InputStreamReader,
+ IOException, OutputStream, PrintStream, Reader}
+import java.text.MessageFormat
+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 {
+
+ /** Foreground color for ANSI black */
+ final val BLACK = "\033[30m"
+ /** Foreground color for ANSI red */
+ final val RED = "\033[31m"
+ /** Foreground color for ANSI green */
+ final val GREEN = "\033[32m"
+ /** Foreground color for ANSI yellow */
+ final val YELLOW = "\033[33m"
+ /** Foreground color for ANSI blue */
+ final val BLUE = "\033[34m"
+ /** Foreground color for ANSI magenta */
+ final val MAGENTA = "\033[35m"
+ /** Foreground color for ANSI cyan */
+ final val CYAN = "\033[36m"
+ /** Foreground color for ANSI white */
+ final val WHITE = "\033[37m"
+
+ /** Background color for ANSI black */
+ final val BLACK_B = "\033[40m"
+ /** Background color for ANSI red */
+ final val RED_B = "\033[41m"
+ /** Background color for ANSI green */
+ final val GREEN_B = "\033[42m"
+ /** Background color for ANSI yellow */
+ final val YELLOW_B = "\033[43m"
+ /** Background color for ANSI blue */
+ final val BLUE_B = "\033[44m"
+ /** Background color for ANSI magenta */
+ final val MAGENTA_B = "\033[45m"
+ /** Background color for ANSI cyan */
+ final val CYAN_B = "\033[46m"
+ /** Background color for ANSI white */
+ final val WHITE_B = "\033[47m"
+
+ /** Reset ANSI styles */
+ final val RESET = "\033[0m"
+ /** ANSI bold */
+ final val BOLD = "\033[1m"
+ /** ANSI underlines */
+ final val UNDERLINED = "\033[4m"
+ /** ANSI blink */
+ final val BLINK = "\033[5m"
+ /** ANSI reversed */
+ final val REVERSED = "\033[7m"
+ /** ANSI invisible */
+ final val INVISIBLE = "\033[8m"
+
+ 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)))
+
+ /** 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.
+ *
+ * @param out the new output stream.
+ */
+ def setOut(out: PrintStream) { outVar.value = out }
+
+ /** 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.
+ *
+ * @param out the new output stream.
+ */
+ def setOut(out: OutputStream): Unit =
+ setOut(new PrintStream(out))
+
+ /** 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)
+
+
+ /** Sets the default error stream.
+ *
+ * @param err the new error stream.
+ */
+ def setErr(err: PrintStream) { errVar.value = err }
+
+ /** 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.
+ *
+ * @param err the new error stream.
+ */
+ def setErr(err: OutputStream): Unit =
+ setErr(new PrintStream(err))
+
+ /** 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.
+ *
+ * @param reader specifies the new input stream.
+ */
+ def setIn(reader: Reader) {
+ inVar.value = new BufferedReader(reader)
+ }
+
+ /** 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.
+ *
+ * @param in the new input stream.
+ */
+ def setIn(in: InputStream) {
+ setIn(new InputStreamReader(in))
+ }
+
+ /** 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
+ * <a href="" target="contentFrame" class="java/util/Formatter">
+ * `java.util.Formatter`</a>.
+ *
+ * @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 : _*)) }
+
+ /** Read a full line from the default input. Returns `null` if the end of the
+ * input stream has been reached.
+ *
+ * @return the string read from the terminal or null if the end of stream was reached.
+ */
+ def readLine(): String = in.readLine()
+
+ /** Print formatted text to the default output and read a full line from the default input.
+ * Returns `null` if the end of the input stream has been reached.
+ *
+ * @param text the format of the text to print out, as in `printf`.
+ * @param args the parameters used to instantiate the format, as in `printf`.
+ * @return the string read from the default input
+ */
+ def readLine(text: String, args: Any*): String = {
+ printf(text, args: _*)
+ readLine()
+ }
+
+ /** Reads a boolean value from an entire line of the default input.
+ * Has a fairly liberal interpretation of the input.
+ *
+ * @return the boolean value read, or false if it couldn't be converted to a boolean
+ * @throws java.io.EOFException if the end of the input stream has been reached.
+ */
+ def readBoolean(): Boolean = {
+ val s = readLine()
+ if (s == null)
+ throw new java.io.EOFException("Console has reached end of input")
+ else
+ s.toLowerCase() match {
+ case "true" => true
+ case "t" => true
+ case "yes" => true
+ case "y" => true
+ case _ => false
+ }
+ }
+
+ /** Reads a byte value from an entire line of the default input.
+ *
+ * @return the Byte that was read
+ * @throws java.io.EOFException if the end of the
+ * input stream has been reached.
+ * @throws java.lang.NumberFormatException if the value couldn't be converted to a Byte
+ */
+ def readByte(): Byte = {
+ val s = readLine()
+ if (s == null)
+ throw new java.io.EOFException("Console has reached end of input")
+ else
+ s.toByte
+ }
+
+ /** Reads a short value from an entire line of the default input.
+ *
+ * @return the short that was read
+ * @throws java.io.EOFException if the end of the
+ * input stream has been reached.
+ * @throws java.lang.NumberFormatException if the value couldn't be converted to a Short
+ */
+ def readShort(): Short = {
+ val s = readLine()
+ if (s == null)
+ throw new java.io.EOFException("Console has reached end of input")
+ else
+ s.toShort
+ }
+
+ /** Reads a char value from an entire line of the default input.
+ *
+ * @return the Char that was read
+ * @throws java.io.EOFException if the end of the
+ * input stream has been reached.
+ * @throws java.lang.StringIndexOutOfBoundsException if the line read from default input was empty
+ */
+ def readChar(): Char = {
+ val s = readLine()
+ if (s == null)
+ throw new java.io.EOFException("Console has reached end of input")
+ else
+ s charAt 0
+ }
+
+ /** Reads an int value from an entire line of the default input.
+ *
+ * @return the Int that was read
+ * @throws java.io.EOFException if the end of the
+ * input stream has been reached.
+ * @throws java.lang.NumberFormatException if the value couldn't be converted to an Int
+ */
+ def readInt(): Int = {
+ val s = readLine()
+ if (s == null)
+ throw new java.io.EOFException("Console has reached end of input")
+ else
+ s.toInt
+ }
+
+ /** Reads an long value from an entire line of the default input.
+ *
+ * @return the Long that was read
+ * @throws java.io.EOFException if the end of the
+ * input stream has been reached.
+ * @throws java.lang.NumberFormatException if the value couldn't be converted to a Long
+ */
+ def readLong(): Long = {
+ val s = readLine()
+ if (s == null)
+ throw new java.io.EOFException("Console has reached end of input")
+ else
+ s.toLong
+ }
+
+ /** Reads a float value from an entire line of the default input.
+ * @return the Float that was read.
+ * @throws java.io.EOFException if the end of the
+ * input stream has been reached.
+ * @throws java.lang.NumberFormatException if the value couldn't be converted to a Float
+ *
+ */
+ def readFloat(): Float = {
+ val s = readLine()
+ if (s == null)
+ throw new java.io.EOFException("Console has reached end of input")
+ else
+ s.toFloat
+ }
+
+ /** Reads a double value from an entire line of the default input.
+ *
+ * @return the Double that was read.
+ * @throws java.io.EOFException if the end of the
+ * input stream has been reached.
+ * @throws java.lang.NumberFormatException if the value couldn't be converted to a Float
+ */
+ def readDouble(): Double = {
+ val s = readLine()
+ if (s == null)
+ throw new java.io.EOFException("Console has reached end of input")
+ else
+ s.toDouble
+ }
+
+ /** Reads in some structured input (from the default input), specified by
+ * a format specifier. See class `java.text.MessageFormat` for details of
+ * the format specification.
+ *
+ * @param format the format of the input.
+ * @return a list of all extracted values.
+ * @throws java.io.EOFException if the end of the input stream has been
+ * reached.
+ */
+ def readf(format: String): List[Any] = {
+ val s = readLine()
+ if (s == null)
+ throw new java.io.EOFException("Console has reached end of input")
+ else
+ textComponents(new MessageFormat(format).parse(s))
+ }
+
+ /** Reads in some structured input (from the default input), specified by
+ * a format specifier, returning only the first value extracted, according
+ * to the format specification.
+ *
+ * @param format format string, as accepted by `readf`.
+ * @return The first value that was extracted from the input
+ */
+ def readf1(format: String): Any = readf(format).head
+
+ /** Reads in some structured input (from the default input), specified
+ * by a format specifier, returning only the first two values extracted,
+ * according to the format specification.
+ *
+ * @param format format string, as accepted by `readf`.
+ * @return A [[scala.Tuple2]] containing the first two values extracted
+ */
+ def readf2(format: String): (Any, Any) = {
+ val res = readf(format)
+ (res.head, res.tail.head)
+ }
+
+ /** Reads in some structured input (from the default input), specified
+ * by a format specifier, returning only the first three values extracted,
+ * according to the format specification.
+ *
+ * @param format format string, as accepted by `readf`.
+ * @return A [[scala.Tuple3]] containing the first three values extracted
+ */
+ def readf3(format: String): (Any, Any, Any) = {
+ val res = readf(format)
+ (res.head, res.tail.head, res.tail.tail.head)
+ }
+
+ private def textComponents(a: Array[AnyRef]): List[Any] = {
+ var i: Int = a.length - 1
+ var res: List[Any] = Nil
+ while (i >= 0) {
+ res = (a(i) match {
+ case x: java.lang.Boolean => x.booleanValue()
+ case x: java.lang.Byte => x.byteValue()
+ case x: java.lang.Short => x.shortValue()
+ case x: java.lang.Character => x.charValue()
+ case x: java.lang.Integer => x.intValue()
+ case x: java.lang.Long => x.longValue()
+ case x: java.lang.Float => x.floatValue()
+ case x: java.lang.Double => x.doubleValue()
+ case x => x
+ }) :: res;
+ i -= 1
+ }
+ res
+ }
+}
diff --git a/examples/scala-js/scalalib/overrides-2.10/scala/collection/immutable/NumericRange.scala b/examples/scala-js/scalalib/overrides-2.10/scala/collection/immutable/NumericRange.scala
new file mode 100644
index 0000000..d3971ee
--- /dev/null
+++ b/examples/scala-js/scalalib/overrides-2.10/scala/collection/immutable/NumericRange.scala
@@ -0,0 +1,285 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+
+package scala.collection
+package immutable
+
+import mutable.{ Builder, ListBuffer }
+import generic._
+
+/** `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))
+ * }}}
+ *
+ * TODO: Now the specialization exists there is no clear reason to have
+ * separate classes for Range/NumericRange. Investigate and consolidate.
+ *
+ * @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.
+
+ // Counts how many elements from the start meet the given test.
+ private def skipCount(p: T => Boolean): Int = {
+ var current = start
+ var counted = 0
+
+ while (counted < length && p(current)) {
+ counted += 1
+ current += step
+ }
+ counted
+ }
+ // 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 =
+ if (ord eq defaultOrdering(num)) {
+ if (num.signum(step) > 0) start
+ else last
+ } else super.min(ord)
+
+ override def max[T1 >: T](implicit ord: Ordering[T1]): T =
+ if (ord eq defaultOrdering(num)) {
+ 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(x: Any): Boolean =
+ try containsTyped(x.asInstanceOf[T])
+ catch { case _: ClassCastException => false }
+
+ final override def sum[B >: T](implicit num: Numeric[B]): B = {
+ import num.Ops
+ if (isEmpty) this.num fromInt 0
+ else if (numRangeElements == 1) head
+ else ((this.num fromInt numRangeElements) * (head + last) / (this.num fromInt 2))
+ }
+
+ 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 {
+ val diff = num.minus(end, start)
+ val jumps = num.toLong(num.quot(diff, step))
+ val remainder = num.rem(diff, step)
+ val longCount = jumps + (
+ if (!isInclusive && zero == remainder) 0 else 1
+ )
+
+ /** The edge cases keep coming. Since e.g.
+ * Long.MaxValue + 1 == Long.MinValue
+ * we do some more improbable seeming checks lest
+ * overflow turn up as an empty range.
+ */
+ // The second condition contradicts an empty result.
+ val isOverflow = longCount == 0 && num.lt(num.plus(start, step), end) == upward
+
+ if (longCount > scala.Int.MaxValue || longCount < 0L || isOverflow) {
+ val word = if (isInclusive) "to" else "until"
+ val descr = List(start, word, end, "by", step) mkString " "
+
+ throw new IllegalArgumentException(descr + ": seqs cannot contain more than Int.MaxValue elements.")
+ }
+ longCount.toInt
+ }
+ }
+
+ 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/examples/scala-js/scalalib/overrides-2.10/scala/collection/immutable/Range.scala b/examples/scala-js/scalalib/overrides-2.10/scala/collection/immutable/Range.scala
new file mode 100644
index 0000000..5c8758d
--- /dev/null
+++ b/examples/scala-js/scalalib/overrides-2.10/scala/collection/immutable/Range.scala
@@ -0,0 +1,424 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+
+package scala.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
+ * }}}
+ *
+ * @param start the start of this range.
+ * @param end the exclusive end of the range.
+ * @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
+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)
+ )
+ 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
+ }
+ }
+ final val lastElement = start + (numRangeElements - 1) * step
+ final val terminalElement = start + numRangeElements * step
+
+ override def last = if (isEmpty) Nil.last else lastElement
+
+ override def min[A1 >: Int](implicit ord: Ordering[A1]): Int =
+ if (ord eq Ordering.Int) {
+ if (step > 0) start
+ 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 start
+ } 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()
+ }
+
+ def validateRangeBoundaries(f: Int => Any): Boolean = {
+ validateMaxLength()
+
+ start != Int.MinValue || end != Int.MinValue || {
+ var count = 0
+ var num = start
+ while (count < numRangeElements) {
+ f(num)
+ count += 1
+ num += step
+ }
+ false
+ }
+ }
+
+ 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) {
+ validateMaxLength()
+ val isCommonCase = (start != Int.MinValue || end != Int.MinValue)
+ var i = start
+ var count = 0
+ val terminal = terminalElement
+ val step = this.step
+ while(
+ if(isCommonCase) { i != terminal }
+ else { count < numRangeElements }
+ ) {
+ f(i)
+ count += 1
+ 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) this
+ else 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) newEmptyRange(end)
+ else 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)
+ }
+
+ // Counts how many elements from the start meet the given test.
+ private def skipCount(p: Int => Boolean): Int = {
+ var current = start
+ var counted = 0
+
+ while (counted < numRangeElements && p(current)) {
+ counted += 1
+ current += step
+ }
+ counted
+ }
+ // 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: Int) = !isEmpty && (
+ (step > 0 && start <= elem && elem <= last ) ||
+ (step < 0 && 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) = 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 = take(skipCount(p))
+ final override def dropWhile(p: Int => Boolean): Range = drop(skipCount(p))
+ final override def span(p: Int => Boolean): (Range, Range) = splitAt(skipCount(p))
+
+ /** 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 `length - n` last elements of the range.
+ *
+ * $doesNotUseBuilders
+ */
+ final override def takeRight(n: Int): Range = drop(numRangeElements - n)
+
+ /** Creates a new range consisting of the initial `length - n` elements of the range.
+ *
+ * $doesNotUseBuilders
+ */
+ final override def dropRight(n: Int): Range = take(numRangeElements - n)
+
+ /** 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) = isWithinBoundaries(x) && ((x - start) % step == 0)
+
+ final override def sum[B >: Int](implicit num: Numeric[B]): Int = {
+ if (isEmpty) 0
+ else if (numRangeElements == 1) head
+ else (numRangeElements.toLong * (head + last) / 2).toInt
+ }
+
+ override def toIterable = this
+
+ override def toSeq = this
+
+ override def equals(other: Any) = other match {
+ case x: Range =>
+ (x canEqual this) && (length == x.length) && (
+ isEmpty || // all empty sequences are equal
+ (start == x.start && last == x.last) // same length and same endpoints implies equality
+ )
+ 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) ", ... )" 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) =
+ "%d %s %d by %s".format(start, if (isInclusive) "to" else "until", end, 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, 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/examples/scala-js/scalalib/overrides-2.10/scala/collection/mutable/Buffer.scala b/examples/scala-js/scalalib/overrides-2.10/scala/collection/mutable/Buffer.scala
new file mode 100644
index 0000000..ec7763b
--- /dev/null
+++ b/examples/scala-js/scalalib/overrides-2.10/scala/collection/mutable/Buffer.scala
@@ -0,0 +1,50 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+
+
+package scala.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. */
+private[scala] abstract class AbstractBuffer[A] extends AbstractSeq[A] with Buffer[A]
diff --git a/examples/scala-js/scalalib/overrides-2.10/scala/compat/Platform.scala b/examples/scala-js/scalalib/overrides-2.10/scala/compat/Platform.scala
new file mode 100644
index 0000000..77bf7de
--- /dev/null
+++ b/examples/scala-js/scalalib/overrides-2.10/scala/compat/Platform.scala
@@ -0,0 +1,133 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+
+
+package scala.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/examples/scala-js/scalalib/overrides-2.10/scala/package.scala b/examples/scala-js/scalalib/overrides-2.10/scala/package.scala
new file mode 100644
index 0000000..5aad9a9
--- /dev/null
+++ b/examples/scala-js/scalalib/overrides-2.10/scala/package.scala
@@ -0,0 +1,138 @@
+/* __ *\
+** ________ ___ / / ___ 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"
+ }
+
+ @deprecated("instead of `@serializable class C`, use `class C extends Serializable`", "2.9.0")
+ type serializable = annotation.serializable
+
+ @deprecated("instead of `@cloneable class C`, use `class C extends Cloneable`", "2.10.0")
+ type cloneable = annotation.cloneable
+
+ 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]
+ type Integral[T] = scala.math.Integral[T]
+
+ 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 cloneable = annotation.cloneable
+ type deprecated = annotation.deprecated
+ type deprecatedName = annotation.deprecatedName
+ type inline = annotation.inline
+ type native = annotation.native
+ type noinline = noannotation.inline
+ type remote = annotation.remote
+ type serializable = annotation.serializable
+ type specialized = annotation.specialized
+ type transient = annotation.transient
+ type throws = annotation.throws
+ type unchecked = annotation.unchecked.unchecked
+ type volatile = annotation.volatile
+ */
+}
diff --git a/examples/scala-js/scalalib/overrides-2.11/scala/Console.scala b/examples/scala-js/scalalib/overrides-2.11/scala/Console.scala
new file mode 100644
index 0000000..b85f8dc
--- /dev/null
+++ b/examples/scala-js/scalalib/overrides-2.11/scala/Console.scala
@@ -0,0 +1,222 @@
+/* __ *\
+** ________ ___ / / ___ 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
+ * <a href="" target="contentFrame" class="java/util/Formatter">
+ * `java.util.Formatter`</a>.
+ *
+ * @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/examples/scala-js/scalalib/overrides-2.11/scala/collection/immutable/NumericRange.scala b/examples/scala-js/scalalib/overrides-2.11/scala/collection/immutable/NumericRange.scala
new file mode 100644
index 0000000..51f9f68
--- /dev/null
+++ b/examples/scala-js/scalalib/overrides-2.11/scala/collection/immutable/NumericRange.scala
@@ -0,0 +1,346 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala
+package collection
+package immutable
+
+import mutable.{ Builder, ListBuffer }
+
+/** `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))
+ * }}}
+ *
+ * TODO: Now the specialization exists there is no clear reason to have
+ * separate classes for Range/NumericRange. Investigate and consolidate.
+ *
+ * @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 =
+ if (ord eq defaultOrdering(num)) {
+ if (num.signum(step) > 0) start
+ else last
+ } else super.min(ord)
+
+ override def max[T1 >: T](implicit ord: Ordering[T1]): T =
+ if (ord eq defaultOrdering(num)) {
+ 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 = {
+ // arithmetic series formula can be used for regular addition
+ 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)||
+ (num eq scala.math.Numeric.LongIsIntegral)) {
+ val numAsIntegral = num.asInstanceOf[Integral[B]]
+ import numAsIntegral._
+ if (isEmpty) num fromInt 0
+ else if (numRangeElements == 1) head
+ else ((num fromInt numRangeElements) * (head + last) / (num fromInt 2))
+ } else {
+ // user provided custom Numeric, we cannot rely on arithmetic series formula
+ 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/examples/scala-js/scalalib/overrides-2.11/scala/collection/immutable/Range.scala b/examples/scala-js/scalalib/overrides-2.11/scala/collection/immutable/Range.scala
new file mode 100644
index 0000000..45eed20
--- /dev/null
+++ b/examples/scala-js/scalalib/overrides-2.11/scala/collection/immutable/Range.scala
@@ -0,0 +1,516 @@
+/* __ *\
+** ________ ___ / / ___ 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 exclusive end of the range.
+ * @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) {
+ validateMaxLength()
+ val isCommonCase = (start != Int.MinValue || end != Int.MinValue)
+ var i = start
+ var count = 0
+ val terminal = terminalElement
+ val step = this.step
+ while(
+ if(isCommonCase) { i != terminal }
+ else { count < numRangeElements }
+ ) {
+ f(i)
+ count += 1
+ 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 `length - n` last 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.toLong * (head + 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(i != terminalElement) {
+ acc = num.plus(acc, i)
+ i = i + step
+ }
+ num.toInt(acc)
+ }
+ }
+ }
+
+ 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) =
+ "%d %s %d by %s".format(start, if (isInclusive) "to" else "until", end, 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/examples/scala-js/scalalib/overrides-2.11/scala/collection/mutable/Buffer.scala b/examples/scala-js/scalalib/overrides-2.11/scala/collection/mutable/Buffer.scala
new file mode 100644
index 0000000..2171cb9
--- /dev/null
+++ b/examples/scala-js/scalalib/overrides-2.11/scala/collection/mutable/Buffer.scala
@@ -0,0 +1,51 @@
+/* __ *\
+** ________ ___ / / ___ 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/examples/scala-js/scalalib/overrides-2.11/scala/compat/Platform.scala b/examples/scala-js/scalalib/overrides-2.11/scala/compat/Platform.scala
new file mode 100644
index 0000000..cdb6916
--- /dev/null
+++ b/examples/scala-js/scalalib/overrides-2.11/scala/compat/Platform.scala
@@ -0,0 +1,132 @@
+/* __ *\
+** ________ ___ / / ___ 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/examples/scala-js/scalalib/overrides-2.11/scala/package.scala b/examples/scala-js/scalalib/overrides-2.11/scala/package.scala
new file mode 100644
index 0000000..21051d4
--- /dev/null
+++ b/examples/scala-js/scalalib/overrides-2.11/scala/package.scala
@@ -0,0 +1,133 @@
+/* __ *\
+** ________ ___ / / ___ 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/examples/scala-js/scalalib/overrides/scala/App.scala b/examples/scala-js/scalalib/overrides/scala/App.scala
new file mode 100644
index 0000000..c49817b
--- /dev/null
+++ b/examples/scala-js/scalalib/overrides/scala/App.scala
@@ -0,0 +1,83 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2010-2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala
+
+import scala.compat.Platform.currentTime
+import scala.collection.mutable.ListBuffer
+
+import scala.scalajs.js.annotation.JSExport
+
+/** The `App` trait can be used to quickly turn objects
+ * into executable programs. Here is an example:
+ * {{{
+ * object Main extends App {
+ * Console.println("Hello World: " + (args mkString ", "))
+ * }
+ * }}}
+ * Here, object `Main` inherits the `main` method of `App`.
+ *
+ * `args` returns the current command line arguments as an array.
+ *
+ * ==Caveats==
+ *
+ * '''''It should be noted that this trait is implemented using the [[DelayedInit]]
+ * functionality, which means that fields of the object will not have been initialized
+ * before the main method has been executed.'''''
+ *
+ * It should also be noted that the `main` method will not normally need to be overridden:
+ * the purpose is to turn the whole class body into the “main method”. You should only
+ * chose to override it if you know what you are doing.
+ *
+ * @author Martin Odersky
+ * @version 2.1, 15/02/2011
+ */
+trait App extends DelayedInit {
+
+ /** The time when the execution of this program started, in milliseconds since 1
+ * January 1970 UTC. */
+ val executionStart: Long = currentTime
+
+ /** The command line arguments passed to the application's `main` method.
+ */
+ protected def args: Array[String] = _args
+
+ private var _args: Array[String] = _
+
+ private val initCode = new ListBuffer[() => Unit]
+
+ /** The init hook. This saves all initialization code for execution within `main`.
+ * This method is normally never called directly from user code.
+ * Instead it is called as compiler-generated code for those classes and objects
+ * (but not traits) that inherit from the `DelayedInit` trait and that do not
+ * themselves define a `delayedInit` method.
+ * @param body the initialization code to be stored for later execution
+ */
+ override def delayedInit(body: => Unit) {
+ initCode += (() => body)
+ }
+
+ /** The main method.
+ * This stores all argument so that they can be retrieved with `args`
+ * and the executes all initialization code segments in the order they were
+ * passed to `delayedInit`
+ *
+ * @param args the arguments passed to the main method
+ */
+ def main(args: Array[String]) = {
+ this._args = args
+ for (proc <- initCode) proc()
+
+ /* DELETED for Scala.js
+ if (util.Properties.propIsSet("scala.time")) {
+ val total = currentTime - executionStart
+ Console.println("[total " + total + "ms]")
+ }
+ */
+ }
+}
diff --git a/examples/scala-js/scalalib/overrides/scala/Enumeration.scala b/examples/scala-js/scalalib/overrides/scala/Enumeration.scala
new file mode 100644
index 0000000..bdc1701
--- /dev/null
+++ b/examples/scala-js/scalalib/overrides/scala/Enumeration.scala
@@ -0,0 +1,284 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala
+
+import scala.collection.{ mutable, immutable, generic, SortedSetLike, AbstractSet }
+import java.lang.reflect.{ Modifier, Method => JMethod, Field => JField }
+import scala.reflect.NameTransformer._
+import java.util.regex.Pattern
+
+/** Defines a finite set of values specific to the enumeration. Typically
+ * these values enumerate all possible forms something can take and provide
+ * a lightweight alternative to case classes.
+ *
+ * Each call to a `Value` method adds a new unique value to the enumeration.
+ * To be accessible, these values are usually defined as `val` members of
+ * the evaluation.
+ *
+ * All values in an enumeration share a common, unique type defined as the
+ * `Value` type member of the enumeration (`Value` selected on the stable
+ * identifier path of the enumeration instance).
+ *
+ * @example {{{
+ * object Main extends App {
+ *
+ * object WeekDay extends Enumeration {
+ * type WeekDay = Value
+ * val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value
+ * }
+ * import WeekDay._
+ *
+ * def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun)
+ *
+ * WeekDay.values filter isWorkingDay foreach println
+ * }
+ * // output:
+ * // Mon
+ * // Tue
+ * // Wed
+ * // Thu
+ * // Fri
+ * }}}
+ *
+ * @param initial The initial value from which to count the integers that
+ * identifies values at run-time.
+ * @author Matthias Zenger
+ */
+@SerialVersionUID(8476000850333817230L)
+abstract class Enumeration (initial: Int) extends Serializable {
+ thisenum =>
+
+ def this() = this(0)
+
+ /* Note that `readResolve` cannot be private, since otherwise
+ the JVM does not invoke it when deserializing subclasses. */
+ protected def readResolve(): AnyRef = ???
+
+ /** The name of this enumeration.
+ */
+ override def toString =
+ (getClass.getName.stripSuffix("$").split('.')).last.split('$').last
+
+ /** The mapping from the integer used to identify values to the actual
+ * values. */
+ private val vmap: mutable.Map[Int, Value] = new mutable.HashMap
+
+ /** The cache listing all values of this enumeration. */
+ @transient private var vset: ValueSet = null
+ @transient @volatile private var vsetDefined = false
+
+ /** The mapping from the integer used to identify values to their
+ * names. */
+ private val nmap: mutable.Map[Int, String] = new mutable.HashMap
+
+ /** The values of this enumeration as a set.
+ */
+ def values: ValueSet = {
+ if (!vsetDefined) {
+ vset = (ValueSet.newBuilder ++= vmap.values).result()
+ vsetDefined = true
+ }
+ vset
+ }
+
+ /** The integer to use to identify the next created value. */
+ protected var nextId: Int = initial
+
+ /** The string to use to name the next created value. */
+ protected var nextName: Iterator[String] = _
+
+ private def nextNameOrNull =
+ if (nextName != null && nextName.hasNext) nextName.next() else null
+
+ /** The highest integer amongst those used to identify values in this
+ * enumeration. */
+ private var topId = initial
+
+ /** The lowest integer amongst those used to identify values in this
+ * enumeration, but no higher than 0. */
+ private var bottomId = if(initial < 0) initial else 0
+
+ /** The one higher than the highest integer amongst those used to identify
+ * values in this enumeration. */
+ final def maxId = topId
+
+ /** The value of this enumeration with given id `x`
+ */
+ final def apply(x: Int): Value = vmap(x)
+
+ /** Return a `Value` from this `Enumeration` whose name matches
+ * the argument `s`. The names are determined automatically via reflection.
+ *
+ * @param s an `Enumeration` name
+ * @return the `Value` of this `Enumeration` if its name matches `s`
+ * @throws NoSuchElementException if no `Value` with a matching
+ * name is in this `Enumeration`
+ */
+ final def withName(s: String): Value = {
+ val (unnamed, named) = values partition {
+ _.toString().startsWith("<Unknown name for enum field ")
+ }
+
+ named.find(_.toString == s) match {
+ case Some(v) => v
+ // If we have unnamed values, we issue a detailed error message
+ case None if unnamed.nonEmpty =>
+ throw new NoSuchElementException(
+ s"""Couldn't find enum field with name $s.
+ |However, there were the following unnamed fields:
+ |${unnamed.mkString(" ","\n ","")}""".stripMargin)
+ // Normal case (no unnamed Values)
+ case _ => None.get
+ }
+ }
+
+ /** Creates a fresh value, part of this enumeration. */
+ protected final def Value: Value = Value(nextId)
+
+ /** Creates a fresh value, part of this enumeration, identified by the
+ * integer `i`.
+ *
+ * @param i An integer that identifies this value at run-time. It must be
+ * unique amongst all values of the enumeration.
+ * @return Fresh value identified by `i`.
+ */
+ protected final def Value(i: Int): Value = Value(i, nextNameOrNull)
+
+ /** Creates a fresh value, part of this enumeration, called `name`.
+ *
+ * @param name A human-readable name for that value.
+ * @return Fresh value called `name`.
+ */
+ protected final def Value(name: String): Value = Value(nextId, name)
+
+ /** Creates a fresh value, part of this enumeration, called `name`
+ * and identified by the integer `i`.
+ *
+ * @param i An integer that identifies this value at run-time. It must be
+ * unique amongst all values of the enumeration.
+ * @param name A human-readable name for that value.
+ * @return Fresh value with the provided identifier `i` and name `name`.
+ */
+ protected final def Value(i: Int, name: String): Value = new Val(i, name)
+
+ /** The type of the enumerated values. */
+ @SerialVersionUID(7091335633555234129L)
+ abstract class Value extends Ordered[Value] with Serializable {
+ /** the id and bit location of this enumeration value */
+ def id: Int
+ /** a marker so we can tell whose values belong to whom come reflective-naming time */
+ private[Enumeration] val outerEnum = thisenum
+
+ override def compare(that: Value): Int =
+ if (this.id < that.id) -1
+ else if (this.id == that.id) 0
+ else 1
+ override def equals(other: Any) = other match {
+ case that: Enumeration#Value => (outerEnum eq that.outerEnum) && (id == that.id)
+ case _ => false
+ }
+ override def hashCode: Int = id.##
+
+ /** Create a ValueSet which contains this value and another one */
+ def + (v: Value) = ValueSet(this, v)
+ }
+
+ /** A class implementing the [[scala.Enumeration.Value]] type. This class
+ * can be overridden to change the enumeration's naming and integer
+ * identification behaviour.
+ */
+ @SerialVersionUID(0 - 3501153230598116017L)
+ protected class Val(i: Int, name: String)
+ extends Value with Serializable {
+
+ def this(i: Int) = this(i, nextNameOrNull)
+ def this(name: String) = this(nextId, name)
+ def this() = this(nextId)
+
+ assert(!vmap.isDefinedAt(i), "Duplicate id: " + i)
+ vmap(i) = this
+ vsetDefined = false
+ nextId = i + 1
+ if (nextId > topId) topId = nextId
+ if (i < bottomId) bottomId = i
+ def id = i
+ override def toString() =
+ if (name != null) name
+ // Scala.js specific
+ else s"<Unknown name for enum field #$i of class ${getClass}>"
+
+ protected def readResolve(): AnyRef = {
+ val enum = thisenum.readResolve().asInstanceOf[Enumeration]
+ if (enum.vmap == null) this
+ else enum.vmap(i)
+ }
+ }
+
+ /** An ordering by id for values of this set */
+ object ValueOrdering extends Ordering[Value] {
+ def compare(x: Value, y: Value): Int = x compare y
+ }
+
+ /** A class for sets of values.
+ * Iterating through this set will yield values in increasing order of their ids.
+ *
+ * @param nnIds The set of ids of values (adjusted so that the lowest value does
+ * not fall below zero), organized as a `BitSet`.
+ */
+ class ValueSet private[ValueSet] (private[this] var nnIds: immutable.BitSet)
+ extends AbstractSet[Value]
+ with immutable.SortedSet[Value]
+ with SortedSetLike[Value, ValueSet]
+ with Serializable {
+
+ implicit def ordering: Ordering[Value] = ValueOrdering
+ def rangeImpl(from: Option[Value], until: Option[Value]): ValueSet =
+ new ValueSet(nnIds.rangeImpl(from.map(_.id - bottomId), until.map(_.id - bottomId)))
+
+ override def empty = ValueSet.empty
+ def contains(v: Value) = nnIds contains (v.id - bottomId)
+ def + (value: Value) = new ValueSet(nnIds + (value.id - bottomId))
+ def - (value: Value) = new ValueSet(nnIds - (value.id - bottomId))
+ def iterator = nnIds.iterator map (id => thisenum.apply(bottomId + id))
+ // This is only defined in 2.11. We change its implementation so it also
+ // compiles on 2.10.
+ def keysIteratorFrom(start: Value) = from(start).keySet.toIterator
+ //nnIds keysIteratorFrom start.id map (id => thisenum.apply(bottomId + id))
+ override def stringPrefix = thisenum + ".ValueSet"
+ /** Creates a bit mask for the zero-adjusted ids in this set as a
+ * new array of longs */
+ def toBitMask: Array[Long] = nnIds.toBitMask
+ }
+
+ /** A factory object for value sets */
+ object ValueSet {
+ import generic.CanBuildFrom
+
+ /** The empty value set */
+ val empty = new ValueSet(immutable.BitSet.empty)
+ /** A value set consisting of given elements */
+ def apply(elems: Value*): ValueSet = (newBuilder ++= elems).result()
+ /** A value set containing all the values for the zero-adjusted ids
+ * corresponding to the bits in an array */
+ def fromBitMask(elems: Array[Long]): ValueSet = new ValueSet(immutable.BitSet.fromBitMask(elems))
+ /** A builder object for value sets */
+ def newBuilder: mutable.Builder[Value, ValueSet] = new mutable.Builder[Value, ValueSet] {
+ private[this] val b = new mutable.BitSet
+ def += (x: Value) = { b += (x.id - bottomId); this }
+ def clear() = b.clear()
+ def result() = new ValueSet(b.toImmutable)
+ }
+ /** The implicit builder for value sets */
+ implicit def canBuildFrom: CanBuildFrom[ValueSet, Value, ValueSet] =
+ new CanBuildFrom[ValueSet, Value, ValueSet] {
+ def apply(from: ValueSet) = newBuilder
+ def apply() = newBuilder
+ }
+ }
+} \ No newline at end of file
diff --git a/examples/scala-js/scalalib/overrides/scala/Symbol.scala b/examples/scala-js/scalalib/overrides/scala/Symbol.scala
new file mode 100644
index 0000000..1af9d28
--- /dev/null
+++ b/examples/scala-js/scalalib/overrides/scala/Symbol.scala
@@ -0,0 +1,117 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala
+
+import scala.scalajs.js
+
+/** This class provides a simple way to get unique objects for equal strings.
+ * Since symbols are interned, they can be compared using reference equality.
+ * Instances of `Symbol` can be created easily with Scala's built-in quote
+ * mechanism.
+ *
+ * For instance, the [[http://scala-lang.org/#_top Scala]] term `'mysym` will
+ * invoke the constructor of the `Symbol` class in the following way:
+ * `Symbol("mysym")`.
+ *
+ * @author Martin Odersky, Iulian Dragos
+ * @version 1.8
+ */
+final class Symbol private (val name: String) extends Serializable {
+ /** Converts this symbol to a string.
+ */
+ override def toString(): String = "'" + name
+
+ @throws(classOf[java.io.ObjectStreamException])
+ private def readResolve(): Any = Symbol.apply(name)
+ override def hashCode = name.hashCode()
+ override def equals(other: Any) = this eq other.asInstanceOf[AnyRef]
+}
+
+// Modified to use Scala.js specific cache
+object Symbol extends JSUniquenessCache[Symbol] {
+ override def apply(name: String): Symbol = super.apply(name)
+ protected def valueFromKey(name: String): Symbol = new Symbol(name)
+ protected def keyFromValue(sym: Symbol): Option[String] = Some(sym.name)
+}
+
+private[scala] abstract class JSUniquenessCache[V]
+{
+ private val map = js.Dictionary.empty[js.Any] // V | js.Undefined
+
+ protected def valueFromKey(k: String): V
+ protected def keyFromValue(v: V): Option[String]
+
+ def apply(name: String): V = {
+ val cachedSym = map(name)
+ if (js.isUndefined(cachedSym)) {
+ val sym = valueFromKey(name)
+ map(name) = sym.asInstanceOf[js.Any]
+ sym.asInstanceOf[V]
+ } else {
+ cachedSym.asInstanceOf[V]
+ }
+ }
+
+ def unapply(other: V): Option[String] = keyFromValue(other)
+}
+
+/** This is private so it won't appear in the library API, but
+ * abstracted to offer some hope of reusability. */
+/* DELETED for Scala.js
+private[scala] abstract class UniquenessCache[K >: js.String, V >: Null]
+{
+
+ import java.lang.ref.WeakReference
+ import java.util.WeakHashMap
+ import java.util.concurrent.locks.ReentrantReadWriteLock
+
+ private val rwl = new ReentrantReadWriteLock()
+ private val rlock = rwl.readLock
+ private val wlock = rwl.writeLock
+ private val map = new WeakHashMap[K, WeakReference[V]]
+
+ protected def valueFromKey(k: K): V
+ protected def keyFromValue(v: V): Option[K]
+
+ def apply(name: K): V = {
+ def cached(): V = {
+ rlock.lock
+ try {
+ val reference = map get name
+ if (reference == null) null
+ else reference.get // will be null if we were gc-ed
+ }
+ finally rlock.unlock
+ }
+ def updateCache(): V = {
+ wlock.lock
+ try {
+ val res = cached()
+ if (res != null) res
+ else {
+ // If we don't remove the old String key from the map, we can
+ // wind up with one String as the key and a different String as
+ // as the name field in the Symbol, which can lead to surprising
+ // GC behavior and duplicate Symbols. See SI-6706.
+ map remove name
+ val sym = valueFromKey(name)
+ map.put(name, new WeakReference(sym))
+ sym
+ }
+ }
+ finally wlock.unlock
+ }
+
+ val res = cached()
+ if (res == null) updateCache()
+ else res
+ }
+ def unapply(other: V): Option[K] = keyFromValue(other)
+}
+*/
diff --git a/examples/scala-js/scalalib/overrides/scala/concurrent/impl/AbstractPromise.scala b/examples/scala-js/scalalib/overrides/scala/concurrent/impl/AbstractPromise.scala
new file mode 100644
index 0000000..8ea135e
--- /dev/null
+++ b/examples/scala-js/scalalib/overrides/scala/concurrent/impl/AbstractPromise.scala
@@ -0,0 +1,29 @@
+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/examples/scala-js/scalalib/overrides/scala/math/ScalaNumber.scala b/examples/scala-js/scalalib/overrides/scala/math/ScalaNumber.scala
new file mode 100644
index 0000000..811346d
--- /dev/null
+++ b/examples/scala-js/scalalib/overrides/scala/math/ScalaNumber.scala
@@ -0,0 +1,21 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+
+
+package scala.math
+
+/** A marker class for Number types introduced by Scala
+ * @author Martin Odersky, Paul Phillips
+ * @version 2.8
+ * @since 2.8
+ */
+abstract class ScalaNumber extends java.lang.Number {
+ protected def isWhole(): Boolean
+ def underlying(): Object
+}
diff --git a/examples/scala-js/scalalib/overrides/scala/runtime/BoxesRunTime.scala b/examples/scala-js/scalalib/overrides/scala/runtime/BoxesRunTime.scala
new file mode 100644
index 0000000..5df7fd1
--- /dev/null
+++ b/examples/scala-js/scalalib/overrides/scala/runtime/BoxesRunTime.scala
@@ -0,0 +1,124 @@
+package scala.runtime
+
+import scala.math.ScalaNumber
+
+object BoxesRunTime {
+ def boxToCharacter(c: Char): java.lang.Character =
+ java.lang.Character.valueOf(c)
+
+ def unboxToChar(c: Object): Char =
+ if (c eq null) 0
+ else c.asInstanceOf[java.lang.Character].charValue()
+
+ def equals(x: Object, y: Object): Boolean =
+ if (x eq y) true
+ else equals2(x, y)
+
+ @inline // only called by equals(), not by codegen
+ def equals2(x: Object, y: Object): Boolean = {
+ x match {
+ case xn: java.lang.Number => equalsNumObject(xn, y)
+ case xc: java.lang.Character => equalsCharObject(xc, y)
+ case null => y eq null
+ case _ => x.equals(y)
+ }
+ }
+
+ def equalsNumObject(xn: java.lang.Number, y: Object): Boolean = {
+ y match {
+ case yn: java.lang.Number => equalsNumNum(xn, yn)
+ case yc: java.lang.Character => equalsNumChar(xn, yc)
+ case _ =>
+ if (xn eq null)
+ y eq null
+ else
+ xn.equals(y)
+ }
+ }
+
+ def equalsNumNum(xn: java.lang.Number, yn: java.lang.Number): Boolean = {
+ (xn: Any) match {
+ case xn: Double =>
+ (yn: Any) match {
+ case yn: Double => xn == yn
+ case yn: Long => xn == yn
+ case yn: ScalaNumber => yn.equals(xn) // xn is not a ScalaNumber
+ case _ => false // xn.equals(yn) must be false here
+ }
+ case xn: Long =>
+ (yn: Any) match {
+ case yn: Long => xn == yn
+ case yn: Double => xn == yn
+ case yn: ScalaNumber => yn.equals(xn) // xn is not a ScalaNumber
+ case _ => false // xn.equals(yn) must be false here
+ }
+ case null => yn eq null
+ case _ => xn.equals(yn)
+ }
+ }
+
+ def equalsCharObject(xc: java.lang.Character, y: Object): Boolean = {
+ y match {
+ case yc: java.lang.Character => xc.charValue() == yc.charValue()
+ case yn: java.lang.Number => equalsNumChar(yn, xc)
+ case _ =>
+ if (xc eq null)
+ y eq null
+ else
+ false // xc.equals(y) must be false here, because y is not a Char
+ }
+ }
+
+ @inline
+ private def equalsNumChar(xn: java.lang.Number, yc: java.lang.Character): Boolean = {
+ (xn: Any) match {
+ case xn: Double => xn == yc.charValue()
+ case xn: Long => xn == yc.charValue()
+ case _ =>
+ if (xn eq null) yc eq null
+ else xn.equals(yc)
+ }
+ }
+
+ def hashFromLong(n: java.lang.Long): Int = {
+ val iv = n.intValue()
+ if (iv == n.longValue()) iv
+ else n.hashCode()
+ }
+
+ def hashFromDouble(n: java.lang.Double): Int = {
+ val iv = n.intValue()
+ val dv = n.doubleValue()
+ if (iv == dv) {
+ iv
+ } else {
+ val lv = n.longValue()
+ if (lv == dv) {
+ java.lang.Long.valueOf(lv).hashCode()
+ } else {
+ // don't test the case floatValue() == dv
+ n.hashCode()
+ }
+ }
+ }
+
+ def hashFromFloat(n: java.lang.Float): Int = {
+ hashFromDouble(java.lang.Double.valueOf(n.doubleValue()))
+ }
+
+ def hashFromNumber(n: java.lang.Number): Int = {
+ (n: Any) match {
+ case n: Int => n
+ case n: java.lang.Long => hashFromLong(n)
+ case n: java.lang.Double => hashFromDouble(n)
+ case n => n.hashCode()
+ }
+ }
+
+ def hashFromObject(a: Object): Int = {
+ a match {
+ case a: java.lang.Number => hashFromNumber(a)
+ case a => a.hashCode()
+ }
+ }
+}
diff --git a/examples/scala-js/scalalib/overrides/scala/util/control/NoStackTrace.scala b/examples/scala-js/scalalib/overrides/scala/util/control/NoStackTrace.scala
new file mode 100644
index 0000000..bcc2839
--- /dev/null
+++ b/examples/scala-js/scalalib/overrides/scala/util/control/NoStackTrace.scala
@@ -0,0 +1,33 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala
+package util.control
+
+/** A trait for exceptions which, for efficiency reasons, do not
+ * fill in the stack trace. Stack trace suppression can be disabled
+ * on a global basis via a system property wrapper in
+ * [[scala.sys.SystemProperties]].
+ *
+ * @author Paul Phillips
+ * @since 2.8
+ */
+trait NoStackTrace extends Throwable {
+ override def fillInStackTrace(): Throwable =
+ if (NoStackTrace.noSuppression) super.fillInStackTrace()
+ else this
+}
+
+object NoStackTrace {
+ final def noSuppression = _noSuppression
+
+ // two-stage init to make checkinit happy, since sys.SystemProperties.noTraceSupression.value calls back into NoStackTrace.noSuppression
+ final private var _noSuppression = false
+ // !!! Disabled in Scala.js because SystemProperties is not supported
+ //_noSuppression = sys.SystemProperties.noTraceSupression.value
+}
diff --git a/examples/scala-js/scripts/assemble-cli.sh b/examples/scala-js/scripts/assemble-cli.sh
new file mode 100755
index 0000000..8d898e8
--- /dev/null
+++ b/examples/scala-js/scripts/assemble-cli.sh
@@ -0,0 +1,96 @@
+#! /bin/sh
+
+set -e
+
+# Assembles the CLI tools for a given Scala binary version.
+
+if [ $# -lt 1 ]; then
+ echo "Usage: $(basename $0) <binary scala version> [noclean|nobuild]" >&2
+ exit 1
+fi
+
+BINVER=$1
+case $BINVER in
+ 2.10)
+ FULLVERS="2.10.2 2.10.3 2.10.4"
+ BASEVER="2.10.4"
+ ;;
+ 2.11)
+ FULLVERS="2.11.0 2.11.1 2.11.2 2.11.4"
+ BASEVER="2.11.2" # Tools do not compile for 2.11.4 (see #1215)
+ ;;
+ *)
+ echo "Invalid Scala version $BINVER" >&2
+ exit 2
+esac
+
+if [ "$2" != "nobuild" ]; then
+ # Subshell to generate SBT commands
+ (
+ if [ "$2" != "noclean" ]; then
+ echo "clean"
+ fi
+
+ # Assemble cli-tools
+ echo "project cli"
+ echo "++$BASEVER"
+ echo "assembly"
+
+ # Package Scala.js library
+ echo "project library"
+ echo "++$BASEVER"
+ echo "package"
+
+ # Package compiler
+ echo "project compiler"
+ for i in $FULLVERS; do
+ echo "++$i"
+ echo "package"
+ done
+ ) | sbt || exit $?
+fi
+
+# Copy stuff to right location
+BASE="$(dirname $0)/.."
+
+# Target directories
+TRG_BASE="$BASE/cli/pack"
+TRG_VER="$TRG_BASE/$BINVER"
+TRG_LIB="$TRG_VER/lib"
+TRG_BIN="$TRG_VER/bin"
+
+rm -rf $TRG_VER
+mkdir -p $TRG_LIB
+mkdir -p $TRG_BIN
+
+SCALAJS_VER=$(ls $BASE/cli/target/scala-$BINVER/scalajs-cli-assembly_$BINVER-*.jar | grep -oE '[0-9]+\.[0-9]+\.[0-9]+(-SNAPSHOT|-RC[0-9]+|-M[0-9]+)?')
+
+cp $BASE/cli/target/scala-$BINVER/scalajs-cli-assembly_$BINVER-$SCALAJS_VER.jar $TRG_LIB
+cp $BASE/library/target/scala-$BINVER/scalajs-library_$BINVER-$SCALAJS_VER.jar $TRG_LIB
+
+for i in $FULLVERS; do
+ cp $BASE/compiler/target/scala-$BINVER/scalajs-compiler_$i-$SCALAJS_VER.jar $TRG_LIB
+done
+
+PAT="s/@SCALA_BIN_VER@/$BINVER/; s/@SCALAJS_VER@/$SCALAJS_VER/"
+PREF=$BASE/cli/src/main/resources/
+for i in $PREF*; do
+ out=$TRG_BIN/${i#$PREF}
+ # Redirect sed output, since in-place edit doesn't work
+ # cross-platform
+ sed "$PAT" $i > $out
+ # Add executable flag if required
+ if [ -x $i ]; then
+ chmod +x $out
+ fi
+done
+
+# Tar and zip the whole thing up
+OUT=scalajs_$BINVER-$SCALAJS_VER
+
+tar cfz $TRG_BASE/$OUT.tgz --exclude '*~' -C $TRG_VER lib bin
+(
+ if [ -f $OUT.zip ]; then rm $OUT.zip; fi
+ cd $TRG_VER
+ zip -r ../$OUT.zip -r lib bin -x '*~'
+)
diff --git a/examples/scala-js/scripts/build-all-js.sh b/examples/scala-js/scripts/build-all-js.sh
new file mode 100755
index 0000000..de0e295
--- /dev/null
+++ b/examples/scala-js/scripts/build-all-js.sh
@@ -0,0 +1,15 @@
+#! /bin/sh
+
+tasks="fastOptJS fullOptJS"
+projects="helloworld/ reversi/ testingExample/test: testSuite/test:"
+
+for v in 2.11.4 2.10.4; do
+ echo "++$v"
+ echo "package"
+
+ for p in $projects; do
+ for t in $tasks; do
+ echo "$p$t"
+ done
+ done
+done | sbt
diff --git a/examples/scala-js/scripts/publish-to-bintray.sh b/examples/scala-js/scripts/publish-to-bintray.sh
new file mode 100755
index 0000000..3b455ea
--- /dev/null
+++ b/examples/scala-js/scripts/publish-to-bintray.sh
@@ -0,0 +1,35 @@
+#! /bin/sh
+
+if [ $# -eq 1 -a "$1" = "-x" ]; then
+ export PUBLISH_TO_BINTRAY=true
+ CMD=sbt
+else
+ echo "Showing commands that would be issued to SBT. Use -x to run"
+ CMD=cat
+fi
+
+FULL_VERSIONS="2.10.2 2.10.3 2.10.4 2.11.0 2.11.1 2.11.2 2.11.4"
+BIN_VERSIONS="2.10.4 2.11.2" # Tools do not compile on 2.11.4 (see #1215)
+SBT_VERSION="2.10.4"
+
+LIBS="library javalibEx jasmineTestFramework tools toolsJS testBridge stubs"
+
+# Publish compiler
+for v in $FULL_VERSIONS; do
+ echo "++$v"
+ echo "compiler/publish"
+done | $CMD
+
+# Package libraries
+for p in $LIBS; do
+ for v in $BIN_VERSIONS; do
+ echo "++$v"
+ echo "$p/publish"
+ done | $CMD
+done
+
+# Publish sbt-plugin
+(
+ echo "++$SBT_VERSION"
+ echo "sbtPlugin/publish"
+) | $CMD
diff --git a/examples/scala-js/stubs/src/main/scala/scala/scalajs/js/annotation/ExportAnnotations.scala b/examples/scala-js/stubs/src/main/scala/scala/scalajs/js/annotation/ExportAnnotations.scala
new file mode 100644
index 0000000..be23596
--- /dev/null
+++ b/examples/scala-js/stubs/src/main/scala/scala/scalajs/js/annotation/ExportAnnotations.scala
@@ -0,0 +1,24 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+
+package scala.scalajs.js.annotation
+
+import scala.annotation.Annotation
+
+class JSExportAll extends scala.annotation.Annotation
+class JSExportDescendentObjects extends scala.annotation.Annotation
+class JSExportDescendentClasses extends scala.annotation.Annotation
+
+class JSExportNamed extends scala.annotation.Annotation {
+ def this(name: String) = this()
+}
+
+class JSExport extends scala.annotation.Annotation {
+ def this(name: String) = this()
+}
diff --git a/examples/scala-js/test-bridge/src/main/scala/scala/scalajs/testbridge/Test.scala b/examples/scala-js/test-bridge/src/main/scala/scala/scalajs/testbridge/Test.scala
new file mode 100644
index 0000000..05d3077
--- /dev/null
+++ b/examples/scala-js/test-bridge/src/main/scala/scala/scalajs/testbridge/Test.scala
@@ -0,0 +1,17 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.testbridge
+
+import scala.scalajs.js
+import js.annotation.JSExportDescendentObjects
+
+/** Marker trait for Scala.js tests */
+@JSExportDescendentObjects
+trait Test
diff --git a/examples/scala-js/test-bridge/src/main/scala/scala/scalajs/testbridge/TestFramework.scala b/examples/scala-js/test-bridge/src/main/scala/scala/scalajs/testbridge/TestFramework.scala
new file mode 100644
index 0000000..f855cf6
--- /dev/null
+++ b/examples/scala-js/test-bridge/src/main/scala/scala/scalajs/testbridge/TestFramework.scala
@@ -0,0 +1,36 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.testbridge
+
+import scala.scalajs.js
+import js.annotation.{ JSExportDescendentObjects, JSExport }
+
+/** This trait should be sub classed (as object) by a concrete test framework
+ *
+ * It will receive a call to runTest for each object on the classpath
+ * extending Test
+ */
+@JSExportDescendentObjects
+trait TestFramework {
+ @JSExport
+ final def safeRunTest(testOutput: TestOutput, args: js.Array[String])(
+ test: js.Function0[Test]): Unit = {
+ try {
+ runTest(testOutput, args)(test)
+ } catch {
+ case e: Throwable =>
+ testOutput.error(s"Test framework ${getClass.getName} failed:")
+ testOutput.error(e.getMessage, e.getStackTrace)
+ }
+ }
+
+ def runTest(testOutput: TestOutput, args: js.Array[String])(
+ test: js.Function0[Test]): Unit
+}
diff --git a/examples/scala-js/test-bridge/src/main/scala/scala/scalajs/testbridge/TestOutput.scala b/examples/scala-js/test-bridge/src/main/scala/scala/scalajs/testbridge/TestOutput.scala
new file mode 100644
index 0000000..5f3b8b6
--- /dev/null
+++ b/examples/scala-js/test-bridge/src/main/scala/scala/scalajs/testbridge/TestOutput.scala
@@ -0,0 +1,34 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.testbridge
+
+trait TestOutput {
+
+ type Color
+
+ val errorColor: Color
+ val successColor: Color
+ val infoColor: Color
+
+ def color(message: String, color: Color): String
+
+ def error(message: String, stack: Array[StackTraceElement]): Unit
+ def error(message: String): Unit
+ def failure(message: String, stack: Array[StackTraceElement]): Unit
+ def failure(message: String): Unit
+ def succeeded(message: String): Unit
+ def skipped(message: String): Unit
+ def pending(message: String): Unit
+ def ignored(message: String): Unit
+ def canceled(message: String): Unit
+
+ def log: TestOutputLog
+
+}
diff --git a/examples/scala-js/test-bridge/src/main/scala/scala/scalajs/testbridge/TestOutputLog.scala b/examples/scala-js/test-bridge/src/main/scala/scala/scalajs/testbridge/TestOutputLog.scala
new file mode 100644
index 0000000..3369592
--- /dev/null
+++ b/examples/scala-js/test-bridge/src/main/scala/scala/scalajs/testbridge/TestOutputLog.scala
@@ -0,0 +1,16 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js API **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.testbridge
+
+trait TestOutputLog {
+ def info(message: String): Unit
+ def warn(message: String): Unit
+ def error(message: String): Unit
+}
diff --git a/examples/scala-js/test-bridge/src/main/scala/scala/scalajs/testbridge/internal/ConsoleTestOutput.scala b/examples/scala-js/test-bridge/src/main/scala/scala/scalajs/testbridge/internal/ConsoleTestOutput.scala
new file mode 100644
index 0000000..c89a32e
--- /dev/null
+++ b/examples/scala-js/test-bridge/src/main/scala/scala/scalajs/testbridge/internal/ConsoleTestOutput.scala
@@ -0,0 +1,118 @@
+package scala.scalajs.testbridge.internal
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSExport
+
+import scala.scalajs.testbridge._
+
+import scala.scalajs.runtime.StackTrace.ColumnStackTraceElement
+
+/** Implementation of TestOutput.
+ *
+ * Attention: This class monkey-patches console.log. Make sure it is loaded
+ * before any output. It also should always be paired with a
+ * [[scala.scalajs.sbtplugin.testing.TestOutputConsole]] on the JVM side.
+ */
+@JSExport
+protected object ConsoleTestOutput extends TestOutput {
+
+ /** monkey-patches console.log when class is loaded */
+ private val savedConsoleLog: js.Function1[String, Unit] = {
+ import js.Dynamic.{ global => g }
+
+ val console = g.console
+ val savedLog = console.log
+
+ val patch = (new MonkeyPatchConsole).asInstanceOf[js.Dynamic]
+
+ // Need to write updateDynamic explicitly here. Since 2.10.x
+ // chokes on this ("erroneous or inaccessible type")
+ console.updateDynamic("log")(patch.log.bind(patch))
+
+ savedLog.bind(console).asInstanceOf[js.Function1[String, Unit]]
+ }
+
+ type Color = String
+
+ val errorColor = "\u001b[31m"
+ val successColor = "\u001b[32m"
+ val infoColor = "\u001b[34m"
+
+ private val reset = "\u001b[0m"
+
+ def color(message: String, color: String): String =
+ message.split('\n').mkString(color, reset + '\n' + color, reset)
+
+ def error(message: String,
+ stack: Array[StackTraceElement]): Unit = {
+ sendTrace(stack)
+ send("error", message)
+ }
+
+ def error(message: String): Unit =
+ error(message, Array.empty)
+
+ def failure(message: String,
+ stack: Array[StackTraceElement]): Unit = {
+ sendTrace(stack)
+ send("failure", message)
+ }
+
+ def failure(message: String): Unit =
+ failure(message, Array.empty)
+
+ def succeeded(message: String): Unit =
+ send("succeeded", message)
+
+ def skipped(message: String): Unit =
+ send("skipped", message)
+
+ def pending(message: String): Unit =
+ send("pending", message)
+
+ def ignored(message: String): Unit =
+ send("ignored", message)
+
+ def canceled(message: String): Unit =
+ send("canceled", message)
+
+ val log =
+ new TestOutputLog {
+ def info(message: String): Unit = send("info", message)
+ def warn(message: String): Unit = send("warn", message)
+ def error(message: String): Unit = send("error-log", message)
+ }
+
+ private def send(fct: String, msg: String) = {
+ val escaped = msg
+ .replace("\\", "\\\\")
+ .replace("\n", "\\n")
+ .replace("\r", "\\r")
+ savedConsoleLog(fct + "|" + escaped)
+ }
+
+ private def sendTrace(stack: Array[StackTraceElement]) = for {
+ el <- stack
+ } send("trace", serializeStackElem(el))
+
+ private def serializeStackElem(e: StackTraceElement) = {
+
+ val flds = List(
+ e.getClassName,
+ e.getMethodName,
+ e.getFileName,
+ e.getLineNumber.toString,
+ e.getColumnNumber.toString)
+
+ flds.mkString("|")
+ }
+
+ /** used to monkey-patch console (only log will be used)
+ * we can't write a simple lambda, because we need varargs
+ */
+ private class MonkeyPatchConsole {
+ @JSExport
+ def log(msg: js.Any*): Unit = send("console-log", msg.mkString(" "))
+ }
+
+}
diff --git a/examples/scala-js/test-suite/run-jasmine-tests.js b/examples/scala-js/test-suite/run-jasmine-tests.js
new file mode 100644
index 0000000..03bacf1
--- /dev/null
+++ b/examples/scala-js/test-suite/run-jasmine-tests.js
@@ -0,0 +1,18 @@
+window.onload = function() {
+ var framework = org.scalajs.jasminetest.JasmineTestFramework();
+ framework.setTags("typedarray")
+
+ // Load tests
+ scalajs.TestDetector().loadDetectedTests();
+
+ // Setup and run Jasmine
+ var jasmineEnv = jasmine.getEnv();
+ var htmlReporter = new jasmine.HtmlReporter();
+ jasmineEnv.addReporter(htmlReporter);
+ jasmineEnv.specFilter = function(spec) {
+ return htmlReporter.specFilter(spec);
+ };
+ jasmineEnv.execute();
+
+ framework.clearTags()
+};
diff --git a/examples/scala-js/test-suite/scalajs-test-suite-2.10-fastopt.html b/examples/scala-js/test-suite/scalajs-test-suite-2.10-fastopt.html
new file mode 100644
index 0000000..ee06db0
--- /dev/null
+++ b/examples/scala-js/test-suite/scalajs-test-suite-2.10-fastopt.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Scala.js Test Suite - Jasmine HTML reporter</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+ <link rel="stylesheet" type="text/css" href="http://cdn.jsdelivr.net/jasmine/1.3.1/jasmine.css"></link>
+</head>
+<body>
+<script type="text/javascript" src="http://cdn.jsdelivr.net/jasmine/1.3.1/jasmine.js"></script>
+<script type="text/javascript" src="http://cdn.jsdelivr.net/jasmine/1.3.1/jasmine-html.js"></script>
+<script type="text/javascript" src="./target/scala-2.10/scalajs-test-suite-test-fastopt.js"></script>
+<script type="text/javascript" src="run-jasmine-tests.js"></script>
+
+</body>
+</html>
diff --git a/examples/scala-js/test-suite/scalajs-test-suite-2.10.html b/examples/scala-js/test-suite/scalajs-test-suite-2.10.html
new file mode 100644
index 0000000..7885784
--- /dev/null
+++ b/examples/scala-js/test-suite/scalajs-test-suite-2.10.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Scala.js Test Suite - Jasmine HTML reporter</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+ <link rel="stylesheet" type="text/css" href="http://cdn.jsdelivr.net/jasmine/1.3.1/jasmine.css"></link>
+</head>
+<body>
+<script type="text/javascript" src="http://cdn.jsdelivr.net/jasmine/1.3.1/jasmine.js"></script>
+<script type="text/javascript" src="http://cdn.jsdelivr.net/jasmine/1.3.1/jasmine-html.js"></script>
+<script type="text/javascript" src="./target/scala-2.10/scalajs-test-suite-test-opt.js"></script>
+<script type="text/javascript" src="run-jasmine-tests.js"></script>
+
+</body>
+</html>
diff --git a/examples/scala-js/test-suite/scalajs-test-suite-2.11-fastopt.html b/examples/scala-js/test-suite/scalajs-test-suite-2.11-fastopt.html
new file mode 100644
index 0000000..5387586
--- /dev/null
+++ b/examples/scala-js/test-suite/scalajs-test-suite-2.11-fastopt.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Scala.js Test Suite - Jasmine HTML reporter</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+ <link rel="stylesheet" type="text/css" href="http://cdn.jsdelivr.net/jasmine/1.3.1/jasmine.css"></link>
+</head>
+<body>
+<script type="text/javascript" src="http://cdn.jsdelivr.net/jasmine/1.3.1/jasmine.js"></script>
+<script type="text/javascript" src="http://cdn.jsdelivr.net/jasmine/1.3.1/jasmine-html.js"></script>
+<script type="text/javascript" src="./target/scala-2.11/scalajs-test-suite-test-fastopt.js"></script>
+<script type="text/javascript" src="run-jasmine-tests.js"></script>
+
+</body>
+</html>
diff --git a/examples/scala-js/test-suite/scalajs-test-suite-2.11.html b/examples/scala-js/test-suite/scalajs-test-suite-2.11.html
new file mode 100644
index 0000000..038ac14
--- /dev/null
+++ b/examples/scala-js/test-suite/scalajs-test-suite-2.11.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Scala.js Test Suite - Jasmine HTML reporter</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+ <link rel="stylesheet" type="text/css" href="http://cdn.jsdelivr.net/jasmine/1.3.1/jasmine.css"></link>
+</head>
+<body>
+<script type="text/javascript" src="http://cdn.jsdelivr.net/jasmine/1.3.1/jasmine.js"></script>
+<script type="text/javascript" src="http://cdn.jsdelivr.net/jasmine/1.3.1/jasmine-html.js"></script>
+<script type="text/javascript" src="./target/scala-2.11/scalajs-test-suite-test-opt.js"></script>
+<script type="text/javascript" src="run-jasmine-tests.js"></script>
+
+</body>
+</html>
diff --git a/examples/scala-js/test-suite/src/test/require-sam/scala/scalajs/testsuite/jsinterop/ArraySAMTest.scala b/examples/scala-js/test-suite/src/test/require-sam/scala/scalajs/testsuite/jsinterop/ArraySAMTest.scala
new file mode 100644
index 0000000..534d203
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/require-sam/scala/scalajs/testsuite/jsinterop/ArraySAMTest.scala
@@ -0,0 +1,32 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.jsinterop
+
+import scala.scalajs.js
+
+import org.scalajs.jasminetest.JasmineTest
+
+object ArraySAMTest extends JasmineTest {
+
+ import js.JSArrayOps._
+
+ describe("scala.scalajs.js.Array with SAM support") {
+
+ it("should provide jsMap") {
+ expect(js.Array("Sc", "ala", ".", "js").jsMap(_.length)).toEqual(
+ js.Array(2, 3, 1, 2))
+ }
+
+ it("should provide jsFilter") {
+ expect(js.Array(56, 30, -20, 33, 54, 86).jsFilter(_ % 3 != 0)).toEqual(
+ js.Array(56, -20, 86))
+ }
+
+ }
+
+}
diff --git a/examples/scala-js/test-suite/src/test/resources/SourceMapTestTemplate.scala b/examples/scala-js/test-suite/src/test/resources/SourceMapTestTemplate.scala
new file mode 100644
index 0000000..2b135ef
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/resources/SourceMapTestTemplate.scala
@@ -0,0 +1,655 @@
+package scala.scalajs.testsuite.compiler
+
+import org.scalajs.jasminetest.{JasmineTest, JasmineTestFramework}
+
+/** The test counter */
+private[testsuite] object TC {
+ var testNum: Int = _
+
+ def is(x: Int): Boolean = testNum == x
+}
+
+/** Exception to test source maps. Not a ControlThrowable, because it has
+ * NoStackTrace which would defeat its purpose
+ */
+case class TestException(lineNo: Int) extends Exception
+
+/**
+ * Template to generate source-map tests, verifying that the line numbers
+ * reported in the source-mapped stacktraces match up with the line number
+ * that the error originated from.
+ *
+ * /two-star/s in this file are replaced with a code-snippet to throw
+ * an exception if `testNum` is set to the /two-star/'s index. The
+ * exception is then caught and its stacktrace checked to see
+ * that it reports the line number expected (stored in the error
+ * message). /three-star/s in are replaced with a dangling else (to
+ * allow throwing in expression position).
+ * `0/*<testCount>*/` is replaced by the number of /n-star/s in the
+ * file.
+ */
+object SourceMapTest extends JasmineTest {
+
+ val testCount: Int = 0/*<testCount>*/
+
+ if (JasmineTestFramework.hasTag("nodejs")) {
+ scalajs.js.Dynamic.global.require("source-map-support").install()
+ }
+
+ when("source-maps").
+ describe("Source Maps") {
+
+ for (i <- 0 until testCount) {
+ it(s"work (test $i)") {
+ TC.testNum = i
+
+ try {
+ run()
+ sys.error("No exception thrown")
+ } catch {
+ case e @ TestException(lineNo) =>
+ val trace0 = e.getStackTrace.toList
+ val trace1 = trace0.dropWhile(
+ _.getFileName.endsWith("/scala/scalajs/runtime/StackTrace.scala"))
+ val trace2 = trace1.dropWhile(
+ _.getFileName.endsWith("/java/lang/Throwables.scala"))
+
+ val exSte :: throwSte :: _ = trace2
+
+ expect(exSte.getFileName).toContain("/SourceMapTest.scala")
+ // line where `case class TestException is written` above
+ expect(exSte.getLineNumber).toBe(15)
+
+ expect(throwSte.getFileName).toContain("/SourceMapTest.scala")
+ expect(throwSte.getLineNumber).toBe(lineNo)
+ }
+ }
+ }
+ }
+
+ def get(json: JsValue, index: Int) = {
+ /**/
+ /**/json.asInstanceOf[JsArray].value(index).value
+ }
+
+ def get(json: JsValue, index: Int, fieldName: String) = {
+ /**/
+ /**//***/json.asInstanceOf[JsArray].value(index).asInstanceOf[JsObject].value(fieldName).value
+ }
+ def run() = {
+ /**/
+ /**/val ugly =
+ """
+ |[
+ | "JSON Test Pattern pass1",
+ | {"object with 1 member":["array with 1 element"]},
+ | {},
+ | [],
+ | -42,
+ | true,
+ | false,
+ | null,
+ | {
+ | "integer": 1234567890,
+ | "real": -9876.543210,
+ | "e": 0.123456789e-12,
+ | "E": 1.234567890E+34,
+ | "": 23456789012E66,
+ | "zero": 0,
+ | "one": 1,
+ | "space": " ",
+ | "quote": "\"",
+ | "backslash": "\\",
+ | "controls": "\b\f\n\r\t",
+ | "slash": "/ & \/",
+ | "alpha": "abcdefghijklmnopqrstuvwyz",
+ | "ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ",
+ | "digit": "0123456789",
+ | "0123456789": "digit",
+ | "special": "`1~!@#$%^&*()_+-={':[,]}|;.</>?",
+ | "hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A",
+ | "true": true,
+ | "false": false,
+ | "null": null,
+ | "array":[ ],
+ | "object":{ },
+ | "address": "50 St. James Street",
+ | "url": "http://www.JSON.org/",
+ | "comment": "// /* <!-- --",
+ | "# -- --> */": " ",
+ | " s p a c e d " :[1,2 , 3
+ |
+ |,
+ |
+ |4 , 5 , 6 ,7 ],"compact":[1,2,3,4,5,6,7],
+ | "jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}",
+ | "quotes": "&#34; \u005Cu0022 %22 0x22 034 &#x22;",
+ | "\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?"
+ |: "A key can be any string"
+ | },
+ | 0.5 ,98.6
+ |,
+ |99.44
+ |,
+ |
+ |1066,
+ |1e1,
+ |0.1e1,
+ |1e-1,
+ |1e00,2e+00,2e-00
+ |,"rosebud"]
+ """.stripMargin/**/
+ /**/val json = new Json()/**/
+ /**/val parsed = json.read(ugly)/**/
+ /**/
+ /**/val unparsed = json.write(parsed)/**/
+ /**/val reparsed = json.read(unparsed)/**/
+
+ for (json <- Seq(parsed, reparsed)){/**/
+ assert(get(json, 0) == "JSON Test Pattern pass1")
+ /**/
+ assert(get(json, 5) == true)
+ assert(get(json, 6) == false)
+ assert(get(json, 8, "real") == "-9876.543210")/**/
+ /**/assert(get(json, 8, "comment") == "// /* <!-- --")
+ assert(get(json, 8, "jsontext") == "{\"object with 1 member\":[\"array with 1 element\"]}")
+ assert(get(json, 19) == "rosebud")/**/
+ }
+ /**/
+ }
+}
+
+
+
+sealed trait JsValue {
+ def value: Any
+}
+
+case class JsString(value: java.lang.String) extends JsValue
+
+case class JsObject(value: Map[String, JsValue]) extends JsValue
+
+case class JsArray(value: Seq[JsValue]) extends JsValue
+
+case class JsNumber(value: java.lang.String) extends JsValue
+
+sealed trait JsBoolean extends JsValue {
+ def value: Boolean
+}
+
+case object JsFalse extends JsBoolean {
+ def value = {/**/false}
+}
+
+case object JsTrue extends JsBoolean {
+ def value = {/**/true}
+}
+
+case object JsNull extends JsValue {
+ def value = {null}
+}
+
+trait Writer{
+ /**/
+ def writeToBuffer(v: JsValue, sb: StringBuffer): Unit = v match {
+ case JsString(s) =>
+ /**/sb.append('"')/**/
+ /**/var i = 0/**/
+ while(i < s.length){/**/
+ /**/s.charAt(i) match {
+ case '\\' => /**/sb.append("\\\\")/**/
+ case '"' => sb.append("\\\"")
+ case '/' => /**/sb.append("\\/")/**/
+ case '\b' => sb.append("\\b")
+ case '\t' => sb.append("\\t")
+ case '\n' => /**/sb.append("\\n")/**/
+ case '\f' => sb.append("\\f")
+ case '\r' => sb.append("\\r")
+ case c =>
+ if (c < ' '){
+ val t = "000" + Integer.toHexString(c)
+ sb.append("\\u" + t.takeRight(4))
+ }else{
+ sb.append(c.toString)
+ }
+ }
+ i += 1
+ }
+ /**/
+ sb.append('"')
+ /**/
+ case JsObject(kvs) =>
+ /**/
+ sb.append("{")
+ /**/
+ var first = true
+ kvs.foreach(kv => {
+ /**/
+ val (k, v) = kv
+ if (first)
+ first = false
+ else
+ sb.append(", ")
+
+ /**/
+ writeToBuffer(JsString(k), sb)
+ sb.append(": ")
+ /**/
+ writeToBuffer(v, sb)
+ })
+ sb.append("}")
+
+ case JsArray(vs) => /**/
+ sb.append("[")
+ if (vs.length > 0) writeToBuffer(vs(0), sb)
+ var i = 1
+ while(i < vs.length){
+ sb.append(", ")
+ writeToBuffer(vs(i), sb)
+ i += 1
+ }
+ sb.append("]")
+ case JsNumber(d) => sb.append(d)
+ case JsFalse => sb.append("false")
+ case JsTrue => sb.append("true")
+ case JsNull => sb.append("null")
+ }
+ /**/
+}
+class Writer2 extends Writer{
+ /**/
+ def write(v: JsValue): String = {
+ /**/
+ val sb = new StringBuffer()
+ writeToBuffer(v, sb)
+ sb.toString
+ }
+ /**/
+}
+class Json extends Writer2{
+
+ /**
+ * Self-contained JSON parser adapted from
+ *
+ * https://github.com/nestorpersist/json
+ */
+ def read(s: String): JsValue = {
+
+ // *** Character Kinds
+ /**/
+ type CharKind = Int
+ val Letter = 0
+ val Digit = 1
+ val Minus = 2
+ val Quote = 3
+ val Colon = 4
+ val Comma = 5
+ val Lbra = 6
+ val Rbra = 7
+ val Larr = 8
+ val Rarr = 9
+ val Blank = 10
+ val Other = 11
+ val Eof = 12
+ val Slash = 13
+
+ // *** Token Kinds
+
+ type TokenKind = Int
+ val ID = 0
+ val STRING = 1
+ val NUMBER = 2
+ val BIGNUMBER = 3
+ val FLOATNUMBER = 4
+ val COLON = 5
+ val COMMA = 6
+ val LOBJ = 7
+ val ROBJ = 8
+ val LARR = 9
+ val RARR = 10
+ val BLANK = 11
+ val EOF = 12
+ /**/
+ // *** Character => CharKind Map ***
+
+ val charKind = (0 to 255).toArray.map {
+ case c if 'a'.toInt <= c && c <= 'z'.toInt => Letter
+ case c if 'A'.toInt <= c && c <= 'Z'.toInt => Letter
+ case c if '0'.toInt <= c && c <= '9'.toInt => Digit
+ case '-' => /**/Minus
+ case ',' => /**/Comma
+ case '"' => /**/Quote
+ case ':' => /**/Colon
+ case '{' => /**/Lbra
+ case '}' => Rbra
+ case '[' => Larr
+ case ']' => Rarr
+ case ' ' => Blank
+ case '\t' => Blank
+ case '\n' => Blank
+ case '\r' => Blank
+ case '/' => Slash
+ case _ => Other
+ }
+
+ // *** Character Escapes
+ /**/
+ val escapeMap = Map[Int, String](
+ '\\'.toInt -> "\\",
+ '/'.toInt -> "/",
+ '\"'.toInt -> "\"",
+ 'b'.toInt -> "\b",
+ 'f'.toInt -> "\f",
+ 'n'.toInt -> "\n",
+ 'r'.toInt -> "\r",
+ 't'.toInt -> "\t"
+ )
+ // *** Import Shared Data ***
+
+ // *** INPUT STRING ***
+
+ // array faster than accessing string directly using charAt
+ //final val s1 = s.toCharArray()
+ val size = s.size
+
+ // *** CHARACTERS ***
+
+ var pos = 0
+
+ var ch: Int = 0
+ var chKind: CharKind = 0
+ var chLinePos: Int = 0
+ var chCharPos: Int = 0
+
+ def chNext() = {/**/
+ if (pos < size) {/**/
+ //ch = s1(pos).toInt
+ /**/ch = s.charAt(pos)/**/
+ /**/chKind = /***/if (ch < 255) {/**/
+ /**//***/charKind(ch)
+ } else {/**/
+ /**//***/Other
+ }/**/
+ pos += 1
+ if (ch == '\n'.toInt) {
+ chLinePos += 1
+ chCharPos = 1
+ } else {/**/
+ chCharPos += 1/**/
+ }
+ } else {
+ ch = -1
+ pos = size + 1
+ chKind = Eof
+ }
+ }/**/
+ /**/
+ /**/
+ /**/def chError(msg: String): Nothing = {
+ throw new Json.Exception(msg, s, chLinePos, chCharPos)
+ }
+
+ def chMark = pos - 1
+
+ def chSubstr(first: Int, delta: Int = 0) = {
+ s.substring(first, pos - 1 - delta)
+ }
+
+ // *** LEXER ***
+
+ var tokenKind = BLANK
+ var tokenValue = ""
+ var linePos = 1
+ var charPos = 1
+
+ def getDigits() = {
+ while (chKind == Digit) chNext()
+ }
+
+ def handleDigit() {
+ val first = chMark
+ getDigits()
+ val k1 = if (ch == '.'.toInt) {
+ chNext()
+ getDigits()
+ BIGNUMBER
+ } else {
+ NUMBER
+ }
+ val k2 = if (ch == 'E'.toInt || ch == 'e'.toInt) {
+ chNext()
+ if (ch == '+'.toInt) {
+ chNext()
+ } else if (ch == '-'.toInt) {
+ chNext()
+ }
+ getDigits()
+ FLOATNUMBER
+ } else {
+ k1
+ }
+ /**/tokenKind = k2/**/
+ /**/tokenValue = chSubstr(first)/**/
+ /**/}/**/
+ /**/
+ def handleRaw() {
+ chNext()
+ val first = chMark
+ var state = 0
+ do {
+ if (chKind == Eof) chError("EOF encountered in raw string")
+ state = (ch, state) match {
+ case ('}', _) => 1
+ case ('"', 1) => 2
+ case ('"', 2) => 3
+ case ('"', 3) => 0
+ case _ => 0
+ }
+
+ chNext()
+ } while (state != 3)
+ tokenKind = STRING
+ tokenValue = chSubstr(first, 3)
+ }
+
+ def handle(i: Int) = {
+ chNext()
+ tokenKind = i
+ tokenValue = ""
+ }
+
+ def tokenNext() {
+ do {
+ linePos = chLinePos
+ charPos = chCharPos
+ val kind: Int = chKind
+ kind match {
+ case Letter =>
+ val first = chMark
+ while (chKind == Letter || chKind == Digit) {
+ chNext()
+ }
+ tokenKind = ID
+ tokenValue = chSubstr(first)
+
+ case Digit => handleDigit()
+ case Minus =>
+ chNext()
+ handleDigit()
+ tokenValue = "-" + tokenValue
+
+ case Quote =>
+ val sb = new StringBuilder(50)
+ chNext()
+ var first = chMark
+ while (ch != '"'.toInt && ch >= 32) {
+ if (ch == '\\'.toInt) {
+ sb.append(chSubstr(first))
+ chNext()
+ escapeMap.get(ch) match {
+ case Some(s) =>
+ sb.append(s)
+ chNext()
+
+ case None =>
+ if (ch != 'u'.toInt) chError("Illegal escape")
+ chNext()
+ var code = 0
+ for (i <- 1 to 4) {
+ val ch1 = ch.toChar.toString
+ val i = "0123456789abcdef".indexOf(ch1.toLowerCase)
+ if (i == -1) chError("Illegal hex character")
+ code = code * 16 + i
+ chNext()
+ }
+ sb.append(code.toChar.toString)
+ }
+ first = chMark
+ } else {
+ chNext()
+ }
+ }
+ if (ch != '"') chError("Unexpected string character: " + ch.toChar)
+
+ sb.append(chSubstr(first))
+
+ tokenKind = STRING
+
+ tokenValue = sb.toString()
+ chNext()
+ if (tokenValue.length() == 0 && ch == '{') {
+ handleRaw()
+ }
+
+ case Colon => handle(COLON)/**/
+ case Comma => handle(COMMA)/**/
+ case Lbra => handle(LOBJ)/**/
+ case Rbra => handle(ROBJ)/**/
+ case Larr => handle(LARR)/**/
+ case Rarr => handle(RARR)/**/
+ case Blank =>
+ do chNext() while (chKind == Blank)
+ tokenKind = BLANK
+ tokenValue = ""
+
+ case Other => chError("Unexpected character: " + ch.toChar + " " + ch)
+ case Eof =>
+ chNext()
+ tokenKind = EOF
+ tokenValue = ""
+
+ case Slash =>
+ if (chKind != Slash) chError("Expecting Slash")
+ do chNext() while (ch != '\n' && chKind != Eof)
+ tokenKind = BLANK
+ tokenValue = ""
+
+ }
+ } while (tokenKind == BLANK)
+ }
+ /**/
+ def tokenError(msg: String): Nothing = {
+ throw new Json.Exception(msg, s, linePos, charPos)
+ }
+ /**/
+ // *** PARSER ***
+
+ def handleEof() = tokenError("Unexpected eof")
+ def handleUnexpected(i: String) = tokenError(s"Unexpected input: [$i]")
+
+ def handleArray(): JsArray = {
+ tokenNext()
+ var result = List.empty[JsValue]
+ while (tokenKind != RARR) {/**/
+ result = getJson() :: result
+ /**/tokenKind match{
+ case COMMA => /**/tokenNext()
+ case RARR => // do nothing
+ case _ => tokenError("Expecting , or ]")
+ }
+ }
+ tokenNext()
+ JsArray(result.reverse)
+ }
+
+ def handleObject(): JsObject = {
+ tokenNext()
+ var result = List.empty[(String, JsValue)]
+ /**/
+ while (tokenKind != ROBJ) {
+ if (tokenKind != STRING && tokenKind != ID) tokenError("Expecting string or name")
+ val name = tokenValue
+ tokenNext()
+ if (tokenKind != COLON) tokenError("Expecting :")
+ tokenNext()
+ result = (name -> getJson()) :: result
+ tokenKind match{
+ case COMMA => tokenNext()
+ case ROBJ => // do nothing
+ case _ => tokenError("Expecting , or }")
+ }
+ }
+ tokenNext()
+ JsObject(result.toMap)
+ }
+ def handleNumber(name: String, f: String => Unit) = {
+ try {
+ f(tokenValue)
+ } catch {
+ case _: Throwable => tokenError("Bad " + name)
+ }
+ val old = tokenValue
+ tokenNext()
+
+ JsNumber(old)
+ }
+ def getJson(): JsValue = {
+ val kind: Int = tokenKind
+ val result: JsValue = kind match {
+ case ID =>
+ val result: JsValue = tokenValue match {
+ case "true" => JsTrue
+ case "false" => JsFalse
+ case "null" => JsNull
+ case _ => tokenError("Not true, false, or null")
+ }
+
+ tokenNext()
+ result
+
+ case STRING =>
+ val result = tokenValue
+ tokenNext()
+ JsString(result)
+
+ case NUMBER => handleNumber("NUMBER", _.toLong)
+ case BIGNUMBER => handleNumber("BIGNUMBER", _.toDouble)
+ case FLOATNUMBER => handleNumber("FLOATNUMBER", _.toDouble)
+ case COLON => handleUnexpected(":")
+ case COMMA => handleUnexpected(",")
+ case LOBJ => handleObject()
+ case ROBJ => handleUnexpected("}")
+ case LARR => handleArray()
+ case RARR => handleUnexpected("]")
+ case EOF => handleEof()
+ }
+ result
+ }
+ def parse(): JsValue = {
+ chNext()
+ tokenNext()
+ val result = getJson
+ if (tokenKind != EOF) tokenError("Excess input")
+ result
+ }
+ parse()
+ }
+}
+
+object Json {
+ class Exception(val msg: String,
+ val input: String,
+ val line: Int,
+ val char: Int)
+ extends scala.Exception(s"JsonParse Error: $msg line $line [$char] in $input")
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/BooleanTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/BooleanTest.scala
new file mode 100644
index 0000000..8343244
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/BooleanTest.scala
@@ -0,0 +1,41 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.compiler
+
+import org.scalajs.jasminetest.JasmineTest
+import scala.scalajs.js
+
+object BooleanTest extends JasmineTest {
+
+ describe("Boolean primitives") {
+
+ it("&, | and ^ on booleans should return booleans") {
+ expect(js.typeOf(true & false)).toEqual("boolean")
+ expect(js.typeOf(true | false)).toEqual("boolean")
+ expect(js.typeOf(true ^ false)).toEqual("boolean")
+ }
+
+ it("&, | and ^ on booleans should return correct results") {
+ expect(false & false).toBeFalsy
+ expect(false & true).toBeFalsy
+ expect(true & false).toBeFalsy
+ expect(true & true).toBeTruthy
+
+ expect(false | false).toBeFalsy
+ expect(true | false).toBeTruthy
+ expect(false | true).toBeTruthy
+ expect(true | true).toBeTruthy
+
+ expect(false ^ false).toBeFalsy
+ expect(true ^ false).toBeTruthy
+ expect(false ^ true).toBeTruthy
+ expect(true ^ true).toBeFalsy
+ }
+
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/ByteTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/ByteTest.scala
new file mode 100644
index 0000000..9f48993
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/ByteTest.scala
@@ -0,0 +1,40 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.compiler
+
+import org.scalajs.jasminetest.JasmineTest
+import scala.scalajs.js
+
+object ByteTest extends JasmineTest {
+
+ describe("Byte primitives") {
+
+ it("should always be in their range") {
+ def test(x: Int, y: Byte): Unit =
+ expect(x.toByte).toEqual(y)
+
+ test(0, 0)
+ test(127, 127)
+ test(128, -128)
+ test(-128, -128)
+ test(-500, 12)
+ test(-90000, 112)
+ test(123456789, 21)
+ test(-40000, -64)
+ test(65536, 0)
+ test(32768, 0)
+
+ def testC(x: Char, y: Byte): Unit =
+ expect(x.toByte).toEqual(y)
+
+ testC(-1.toChar, -1)
+ testC(200.toChar, -56)
+ }
+
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/CharTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/CharTest.scala
new file mode 100644
index 0000000..edc2660
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/CharTest.scala
@@ -0,0 +1,40 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.compiler
+
+import org.scalajs.jasminetest.JasmineTest
+import scala.scalajs.js.Any.fromInt
+
+object CharTest extends JasmineTest {
+
+ describe("Char primitives") {
+
+ it("should always be positive (when coerced)") {
+ expect(-3.toByte.toChar.toInt).toEqual(65533)
+ expect(-100.toShort.toChar.toInt).toEqual(65436)
+ expect(-66000.toChar.toInt).toEqual(65072)
+ expect(-4567L.toChar.toInt).toEqual(60969)
+ expect(-5.3f.toChar.toInt).toEqual(65531)
+ expect(-7.9.toChar.toInt).toEqual(65529)
+ }
+
+ it("should overflow (when coerced)") {
+ expect(347876543.toChar.toInt).toEqual(11455)
+ expect(34234567876543L.toChar.toInt).toEqual(57279)
+ }
+
+ it("should overflow with *") {
+ def test(a: Char, b: Char, expected: Int): Unit =
+ expect(a * b).toEqual(expected)
+
+ // note: expected values are constant-folded by the compiler on the JVM
+ test(Char.MaxValue, Char.MaxValue, Char.MaxValue * Char.MaxValue)
+ }
+
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/FloatTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/FloatTest.scala
new file mode 100644
index 0000000..5eda04a
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/FloatTest.scala
@@ -0,0 +1,73 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.compiler
+
+import org.scalajs.jasminetest.JasmineTest
+
+object FloatTest extends JasmineTest {
+
+ def froundNotInlined(x: Double): Float = {
+ val y = x // so you don't inline me
+ y.toFloat
+ }
+
+ when("strict-floats").
+ describe("Strict floats") {
+
+ it("fround for special values") {
+ expect(froundNotInlined(Double.NaN).isNaN).toBeTruthy
+ expect(1 / froundNotInlined(0.0).toDouble).toBe(Double.PositiveInfinity)
+ expect(1 / froundNotInlined(-0.0).toDouble).toBe(Double.NegativeInfinity)
+ expect(froundNotInlined(Double.PositiveInfinity)).toBe(Float.PositiveInfinity)
+ expect(froundNotInlined(Double.NegativeInfinity)).toBe(Float.NegativeInfinity)
+ }
+
+ it("fround overflows") {
+ expect(froundNotInlined(1e200)).toBe(Double.PositiveInfinity)
+ expect(froundNotInlined(-1e200)).toBe(Double.NegativeInfinity)
+ }
+
+ it("fround underflows") {
+ expect(1 / froundNotInlined(1e-300).toDouble).toBe(Double.PositiveInfinity)
+ expect(1 / froundNotInlined(-1e-300).toDouble).toBe(Double.NegativeInfinity)
+ }
+
+ it("fround normal cases") {
+ def test(input: Double, expected: Double): Unit =
+ expect(input.toFloat.toDouble).toBe(expected)
+
+ // From MDN documentation
+ test(0.0, 0.0)
+ test(1.0, 1.0)
+ test(1.5, 1.5)
+ test(1.337, 1.3370000123977661)
+ test(-4.3, -4.300000190734863)
+
+ // Some bounds
+ test(Float.MinPositiveValue.toDouble, Float.MinPositiveValue.toDouble)
+ test(Float.MaxValue.toDouble, Float.MaxValue.toDouble)
+ test(Float.MinValue.toDouble, Float.MinValue.toDouble)
+
+ // Randomly generated Doubles
+ test(2.705609035558863E20, 2.7056090763400262E20)
+ test(-1.447710531503027E15, -1.447710532042752E15)
+ test(-5.1970024617732836E13, -5.1970022834176E13)
+ test(1.627661085098256E31, 1.6276610930768024E31)
+ test(-3.7731947682593834E-32, -3.7731946313230934E-32)
+ test(34.48229849163326, 34.4822998046875)
+ test(26.62034396181652, 26.620344161987305)
+ test(8.198435190113375E-24, 8.198434961596576E-24)
+ test(-6.079928908440255E-23, -6.079928963558556E-23)
+ test(3.3756949828462674E-13, 3.37569490589662E-13)
+ test(-1.2599049874324274E33, -1.2599049641449257E33)
+ test(6.08574575776438E-10, 6.085745796191588E-10)
+ test(1.973497969450596E-21, 1.973498047135062E-21)
+ }
+
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/InstanceTestsHijackedBoxedClassesTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/InstanceTestsHijackedBoxedClassesTest.scala
new file mode 100644
index 0000000..1379b80
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/InstanceTestsHijackedBoxedClassesTest.scala
@@ -0,0 +1,98 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.compiler
+
+import org.scalajs.jasminetest.JasmineTest
+import scala.scalajs.js
+
+object InstanceTestsHijackedBoxedClassesTest extends JasmineTest {
+
+ describe("Instance tests for hijacked boxed classes") {
+
+ it("should support isInstanceOf (positive)") {
+ expect((() : Any).isInstanceOf[Unit] ).toBeTruthy
+ expect((false : Any).isInstanceOf[Boolean]).toBeTruthy
+ expect(('a' : Any).isInstanceOf[Char] ).toBeTruthy
+ expect((65.toByte : Any).isInstanceOf[Byte] ).toBeTruthy
+ expect((654.toShort: Any).isInstanceOf[Short] ).toBeTruthy
+ expect((-4321 : Any).isInstanceOf[Int] ).toBeTruthy
+ expect((684321L : Any).isInstanceOf[Long] ).toBeTruthy
+ expect((3.14f : Any).isInstanceOf[Float] ).toBeTruthy
+ expect((3.14 : Any).isInstanceOf[Double] ).toBeTruthy
+ }
+
+ it("should support isInstanceOf (negative)") {
+ expect((12345: Any).isInstanceOf[Unit] ).toBeFalsy
+ expect((12345: Any).isInstanceOf[Boolean]).toBeFalsy
+ expect((12345: Any).isInstanceOf[Char] ).toBeFalsy
+ expect(('a' : Any).isInstanceOf[Byte] ).toBeFalsy
+ expect(('b' : Any).isInstanceOf[Short] ).toBeFalsy
+ expect(('c' : Any).isInstanceOf[Int] ).toBeFalsy
+ expect(('d' : Any).isInstanceOf[Long] ).toBeFalsy
+ expect(('f' : Any).isInstanceOf[Float] ).toBeFalsy
+ expect(('g' : Any).isInstanceOf[Double] ).toBeFalsy
+ }
+
+ it("should support asInstanceOf (positive)") {
+ def swallow(x: Any): Unit = ()
+ swallow((() : Any).asInstanceOf[Unit] )
+ swallow((false : Any).asInstanceOf[Boolean])
+ swallow(('a' : Any).asInstanceOf[Char] )
+ swallow((65.toByte : Any).asInstanceOf[Byte] )
+ swallow((654.toShort: Any).asInstanceOf[Short] )
+ swallow((-4321 : Any).asInstanceOf[Int] )
+ swallow((684321L : Any).asInstanceOf[Long] )
+ swallow((3.14f : Any).asInstanceOf[Float] )
+ swallow((3.14 : Any).asInstanceOf[Double] )
+ }
+
+ when("compliant-asinstanceof").
+ it("should support asInstanceOf (negative)") {
+ expect(() => (12345: Any).asInstanceOf[Unit] ).toThrow
+ expect(() => (12345: Any).asInstanceOf[Boolean]).toThrow
+ expect(() => (12345: Any).asInstanceOf[Char] ).toThrow
+ expect(() => ('a' : Any).asInstanceOf[Byte] ).toThrow
+ expect(() => ('b' : Any).asInstanceOf[Short] ).toThrow
+ expect(() => ('c' : Any).asInstanceOf[Int] ).toThrow
+ expect(() => ('d' : Any).asInstanceOf[Long] ).toThrow
+ expect(() => ('f' : Any).asInstanceOf[Float] ).toThrow
+ expect(() => ('g' : Any).asInstanceOf[Double] ).toThrow
+ }
+
+ it("should support isInstanceOf via java.lang.Class (positive)") {
+ def test(x: Any, clazz: Class[_]): Unit =
+ expect(clazz.isInstance(x)).toBeTruthy
+
+ test(() , classOf[scala.runtime.BoxedUnit])
+ test(false , classOf[java.lang.Boolean])
+ test('a' , classOf[java.lang.Character])
+ test(65.toByte , classOf[java.lang.Byte])
+ test(654.toShort, classOf[java.lang.Short])
+ test(-4321 , classOf[java.lang.Integer])
+ test(684321L , classOf[java.lang.Long])
+ test(3.14f , classOf[java.lang.Float])
+ test(3.14 , classOf[java.lang.Double])
+ }
+
+ it("should support isInstanceOf via java.lang.Class (negative)") {
+ def test(x: Any, clazz: Class[_]): Unit =
+ expect(clazz.isInstance(x)).toBeFalsy
+
+ test(12345, classOf[scala.runtime.BoxedUnit])
+ test(12345, classOf[java.lang.Boolean])
+ test(12345, classOf[java.lang.Character])
+ test('a' , classOf[java.lang.Byte])
+ test('b' , classOf[java.lang.Short])
+ test('c' , classOf[java.lang.Integer])
+ test('d' , classOf[java.lang.Long])
+ test('e' , classOf[java.lang.Float])
+ test('f' , classOf[java.lang.Double])
+ }
+
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/IntTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/IntTest.scala
new file mode 100644
index 0000000..60f8773
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/IntTest.scala
@@ -0,0 +1,206 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.compiler
+
+import org.scalajs.jasminetest.JasmineTest
+
+object IntTest extends JasmineTest {
+
+ /* General note on the way these tests are written:
+ * We leverage the constant folding applied by the Scala compiler to write
+ * sound tests. We always perform the same operation, on the same operands,
+ * once in a way constant folding understands, and once in a way it doesn't.
+ * Since constant folding is performed on the JVM, we know it has the right
+ * semantics.
+ */
+
+ // final val without type ascription to make sure these are constant-folded
+ final val MinVal = Int.MinValue
+ final val MaxVal = Int.MaxValue
+ final val AlmostMinVal = Int.MinValue + 43
+ final val AlmostMaxVal = Int.MaxValue - 36
+
+ describe("Int primitives") {
+
+ it("should support unary -") {
+ def test(a: Int, expected: Int): Unit =
+ expect(-a).toEqual(expected)
+
+ test(56, -56)
+ test(0, 0)
+ test(-36, 36)
+
+ test(MaxVal, -MaxVal)
+ test(MinVal, -MinVal)
+ test(-MaxVal, MaxVal)
+ test(AlmostMinVal, -AlmostMinVal)
+ test(AlmostMaxVal, -AlmostMaxVal)
+ }
+
+ it("should support +") {
+ def test(a: Int, b: Int, expected: Int): Unit =
+ expect(a + b).toEqual(expected)
+
+ test(56, 654, 56 + 654)
+ test(0, 25, 0 + 25)
+ test(-36, 13, -36 + 13)
+
+ test(MaxVal, 1, MaxVal + 1)
+ test(MinVal, -1, MinVal - 1)
+ test(MaxVal, MinVal, MaxVal + MinVal)
+ test(AlmostMinVal, -100, AlmostMinVal - 100)
+ test(AlmostMaxVal, 123, AlmostMaxVal + 123)
+ }
+
+ it("should support -") {
+ def test(a: Int, b: Int, expected: Int): Unit =
+ expect(a - b).toEqual(expected)
+
+ test(56, 654, 56 - 654)
+ test(0, 25, 0 - 25)
+ test(-36, 13, -36 - 13)
+
+ test(MaxVal, -1, MaxVal + 1)
+ test(MinVal, 1, MinVal - 1)
+ test(MaxVal, MinVal, MaxVal - MinVal)
+ test(AlmostMinVal, 100, AlmostMinVal - 100)
+ test(AlmostMaxVal, -123, AlmostMaxVal + 123)
+ }
+
+ it("should support *") {
+ def test(a: Int, b: Int, expected: Int): Unit =
+ expect(a * b).toEqual(expected)
+
+ test(56, 654, 56 * 654)
+ test(0, 25, 0 * 25)
+ test(-36, 13, -36 * 13)
+ test(-5, -6, -5 * -6)
+
+ test(MinVal, 1, MinVal * 1)
+ test(MinVal, -1, MinVal * -1)
+ test(MaxVal, 1, MaxVal * 1)
+ test(MaxVal, -1, MaxVal * -1)
+
+ test(MaxVal, MinVal, MaxVal * MinVal)
+ test(MaxVal, MaxVal, MaxVal * MaxVal)
+ test(MinVal, MaxVal, MinVal * MaxVal)
+ test(MinVal, MinVal, MinVal * MinVal)
+
+ test(AlmostMaxVal, 2, AlmostMaxVal * 2)
+ test(AlmostMaxVal, 5, AlmostMaxVal * 5)
+ test(AlmostMaxVal, -7, AlmostMaxVal * -7)
+ test(AlmostMaxVal, -14, AlmostMaxVal * -14)
+ test(AlmostMinVal, 100, AlmostMinVal * 100)
+ test(AlmostMaxVal, -123, AlmostMaxVal * -123)
+ }
+
+ it("should support /") {
+ def test(a: Int, b: Int, expected: Int): Unit =
+ expect(a / b).toEqual(expected)
+
+ test(654, 56, 654 / 56)
+ test(0, 25, 0 / 25)
+ test(-36, 13, -36 / 13)
+ test(-55, -6, -55 / -6)
+
+ test(MinVal, 1, MinVal / 1)
+ test(MinVal, -1, MinVal / -1)
+ test(MaxVal, 1, MaxVal / 1)
+ test(MaxVal, -1, MaxVal / -1)
+
+ test(MaxVal, MinVal, MaxVal / MinVal)
+ test(MaxVal, MaxVal, MaxVal / MaxVal)
+ test(MinVal, MaxVal, MinVal / MaxVal)
+ test(MinVal, MinVal, MinVal / MinVal)
+
+ test(AlmostMaxVal, 2, AlmostMaxVal / 2)
+ test(AlmostMaxVal, 5, AlmostMaxVal / 5)
+ test(AlmostMaxVal, -7, AlmostMaxVal / -7)
+ test(AlmostMaxVal, -14, AlmostMaxVal / -14)
+ test(AlmostMinVal, 100, AlmostMinVal / 100)
+ test(AlmostMaxVal, -123, AlmostMaxVal / -123)
+ }
+
+ unless("phantomjs"). // see #593
+ it("should support %") {
+ def test(a: Int, b: Int, expected: Int): Unit =
+ expect(a % b).toEqual(expected)
+
+ test(654, 56, 654 % 56)
+ test(0, 25, 0 % 25)
+ test(-36, 13, -36 % 13)
+ test(-55, -6, -55 % -6)
+
+ test(MinVal, 1, MinVal % 1)
+ test(MinVal, -1, MinVal % -1)
+ test(MaxVal, 1, MaxVal % 1)
+ test(MaxVal, -1, MaxVal % -1)
+
+ test(MaxVal, MinVal, MaxVal % MinVal)
+ test(MaxVal, MaxVal, MaxVal % MaxVal)
+ test(MinVal, MaxVal, MinVal % MaxVal)
+ test(MinVal, MinVal, MinVal % MinVal)
+
+ test(AlmostMaxVal, 2, AlmostMaxVal % 2)
+ test(AlmostMaxVal, 5, AlmostMaxVal % 5)
+ test(AlmostMaxVal, -7, AlmostMaxVal % -7)
+ test(AlmostMaxVal, -14, AlmostMaxVal % -14)
+ test(AlmostMinVal, 100, AlmostMinVal % 100)
+ test(AlmostMaxVal, -123, AlmostMaxVal % -123)
+ }
+
+ it("should support <<") {
+ def test(a: Int, b: Int, expected: Int): Unit =
+ expect(a << b).toEqual(expected)
+
+ test(0, 5, 0 << 5)
+ test(1, 5, 1 << 5)
+ test(13, 4, 13 << 4)
+ test(-35, 5, -35 << 5)
+ test(345, 0, 345 << 0)
+
+ test(MinVal, 0, MinVal << 0)
+ test(MaxVal, 0, MaxVal << 0)
+ test(MinVal, 1, MinVal << 1)
+ test(MaxVal, 1, MaxVal << 1)
+ }
+
+ it("should support >>") {
+ def test(a: Int, b: Int, expected: Int): Unit =
+ expect(a >> b).toEqual(expected)
+
+ test(0, 5, 0 >> 5)
+ test(32, 5, 32 >> 5)
+ test(31, 4, 31 >> 4)
+ test(-355, 5, -355 >> 5)
+ test(345, 0, 345 >> 0)
+
+ test(MinVal, 0, MinVal >> 0)
+ test(MaxVal, 0, MaxVal >> 0)
+ test(MinVal, 1, MinVal >> 1)
+ test(MaxVal, 1, MaxVal >> 1)
+ }
+
+ it("should support >>>") {
+ def test(a: Int, b: Int, expected: Int): Unit =
+ expect(a >>> b).toEqual(expected)
+
+ test(0, 5, 0 >>> 5)
+ test(32, 5, 32 >>> 5)
+ test(31, 4, 31 >>> 4)
+ test(-355, 5, -355 >>> 5)
+ test(345, 0, 345 >>> 0)
+
+ test(MinVal, 0, MinVal >>> 0)
+ test(MaxVal, 0, MaxVal >>> 0)
+ test(MinVal, 1, MinVal >>> 1)
+ test(MaxVal, 1, MaxVal >>> 1)
+ }
+
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/InteroperabilityTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/InteroperabilityTest.scala
new file mode 100644
index 0000000..594345a
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/InteroperabilityTest.scala
@@ -0,0 +1,528 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.compiler
+
+import scala.scalajs.js
+import org.scalajs.jasminetest.{JasmineTest, JasmineTestFramework}
+import scala.scalajs.js.annotation._
+
+/*
+ * Based on examples in:
+ * http://lampwww.epfl.ch/~doeraene/scala-js/doc/js-interoperability.html
+ */
+object InteroperabilityTest extends JasmineTest {
+
+ describe("JavaScript interoperability") {
+
+ it("should support backquotes to escape Scala fields") {
+ val obj = js.eval("""
+ var interoperabilityTestFieldEscape = {
+ def: 0,
+ val: function(x) { if (x) this.def = x; return this.def; }
+ };
+ interoperabilityTestFieldEscape;
+ """).asInstanceOf[InteroperabilityTestFieldEscape]
+
+ obj.`def` = 7357
+ expect(obj.`def`).toEqual(7357)
+ expect(obj.`val`()).toEqual(7357)
+ expect(obj.`val`(42)).toEqual(42)
+ }
+
+ it("should support @JSName to specify the JavaScript name for fields") {
+ val obj = js.eval("""
+ var interoperabilityTestJSName = {
+ def: 42,
+ val: function(x) { if (x) this.def = x; return this.def; }
+ };
+ interoperabilityTestJSName;
+ """).asInstanceOf[InteroperabilityTestJSName]
+
+ expect(obj.value()).toEqual(42)
+ expect(obj.value(7357)).toEqual(7357)
+ }
+
+ it("should translate explicit getter and setter names to field access") {
+ val obj = js.eval("""
+ var interoperabilityTestProperty = { a: 1 };
+ interoperabilityTestProperty;
+ """).asInstanceOf[InteroperabilityTestProperty]
+
+ expect(obj.a).toEqual(1)
+ obj.a = 100
+ expect(obj.a).toEqual(100)
+ }
+
+ it("should support @JSName together with field access") {
+ val obj = js.eval("""
+ var interoperabilityTestProperty = { b: 1 };
+ interoperabilityTestProperty;
+ """).asInstanceOf[InteroperabilityTestPropertyNamed]
+
+ expect(obj.a).toEqual(1)
+ obj.a = 100
+ expect(obj.a).toEqual(100)
+ expect(obj.b).toEqual(100)
+ }
+
+ it("should support @JSBracketAccess to specify access using []-subscription") {
+ val obj = js.eval("""
+ var interoperabilityTestJSBracketAccess = [ 0, 1, 7357 ];
+ interoperabilityTestJSBracketAccess;
+ """).asInstanceOf[InteroperabilityTestJSBracketAccess]
+
+ expect(obj(2)).toEqual(7357)
+ obj(2) = 42
+ expect(obj(2)).toEqual(42)
+ }
+
+ it("should allow instanciation of JS classes inheriting from js.Object") {
+ js.eval("""
+ var InteroperabilityTestInherit = {
+ Pattern: function(x) {
+ this.field = 42;
+ this.method = function() {
+ return '42';
+ };
+ this.getConstructorParam = function() {
+ return x;
+ };
+ }
+ }
+ """)
+
+ val obj = new InteroperabilityTestPattern("Scala.js")
+ expect(obj.field).toEqual(42)
+ expect(obj.method).toEqual("42")
+ expect(obj.getConstructorParam).toEqual("Scala.js")
+ }
+
+ it("should acces top-level JS objects via Scala objects inheriting from js.Object") {
+ js.eval("""
+ var InteroperabilityTestTopLevelObject = function(value) {
+ return {
+ value: value,
+ valueAsInt: function() {
+ return parseInt(value);
+ }
+ };
+ }
+ """)
+
+ // Use alias for convenience: see end of file for definition
+ val TopLevel = InteroperabilityTestTopLevel
+
+ val obj = TopLevel("7357")
+ expect(obj.value).toEqual("7357")
+ expect(obj.valueAsInt).toEqual(7357)
+ }
+
+ it("should allow to call JS methods with variadic parameters") {
+ val obj = js.eval("""
+ var obj = {
+ foo: function() {
+ var args = new Array(arguments.length);
+ for (var i = 0; i < arguments.length; i++)
+ args[i] = arguments[i];
+ return args;
+ }
+ };
+ obj;
+ """)
+
+ val elems = Seq[js.Any]("plop", 42, 51)
+
+ val dyn = obj.asInstanceOf[js.Dynamic]
+ expect(dyn.foo()).toEqual(js.Array())
+ expect(dyn.foo(3, 6)).toEqual(js.Array(3, 6))
+ expect(dyn.foo("hello", false)).toEqual(js.Array("hello", false))
+ expect(dyn.applyDynamic("foo")(elems: _*)).toEqual(js.Array("plop", 42, 51))
+
+ val stat = obj.asInstanceOf[InteroperabilityTestVariadicMethod]
+ expect(stat.foo()).toEqual(js.Array())
+ expect(stat.foo(3, 6)).toEqual(js.Array(3, 6))
+ expect(stat.foo("hello", false)).toEqual(js.Array("hello", false))
+ expect(stat.foo(elems: _*)).toEqual(js.Array("plop", 42, 51))
+ }
+
+ it("should allow to call JS constructors with variadic parameters") {
+ import js.Dynamic.{newInstance => jsnew}
+
+ js.eval("""
+ var InteroperabilityTestVariadicCtor = function() {
+ var args = new Array(arguments.length);
+ for (var i = 0; i < arguments.length; i++)
+ args[i] = arguments[i];
+ this.args = args;
+ };
+ """)
+
+ val elems = Seq[js.Any]("plop", 42, 51)
+
+ val ctor = js.Dynamic.global.InteroperabilityTestVariadicCtor
+ expect(jsnew(ctor)().args).toEqual(js.Array())
+ expect(jsnew(ctor)(3, 6).args).toEqual(js.Array(3, 6))
+ expect(jsnew(ctor)("hello", false).args).toEqual(js.Array("hello", false))
+ expect(jsnew(ctor)(elems: _*).args).toEqual(js.Array("plop", 42, 51))
+
+ import scala.scalajs.testsuite.compiler.{InteroperabilityTestVariadicCtor => C}
+ expect(new C().args).toEqual(js.Array())
+ expect(new C(3, 6).args).toEqual(js.Array(3, 6))
+ expect(new C("hello", false).args).toEqual(js.Array("hello", false))
+ expect(new C(elems: _*).args).toEqual(js.Array("plop", 42, 51))
+ }
+
+ it("should acces top-level JS objects via Scala object inheriting from js.GlobalScope") {
+ js.eval("""
+ var interoperabilityTestGlobalScopeValue = "7357";
+ var interoperabilityTestGlobalScopeValueAsInt = function() {
+ return parseInt(interoperabilityTestGlobalScopeValue);
+ };
+ """)
+
+ // Use alias for convenience: see end of file for definition
+ val Global = InteroperabilityTestGlobalScope
+
+ expect(Global.interoperabilityTestGlobalScopeValue).toEqual("7357")
+ expect(Global.interoperabilityTestGlobalScopeValueAsInt).toEqual(7357)
+
+ Global.interoperabilityTestGlobalScopeValue = "42"
+ expect(Global.interoperabilityTestGlobalScopeValueAsInt).toEqual(42)
+ }
+
+ it("should protect receiver of raw JS apply if it's a select - #804") {
+ val rawReceiver = js.eval("""
+ var interoperabilityTestRawReceiver = {
+ member: 0xbad,
+ check: function(raw) { return this.member ? this.member : raw; }
+ };
+ interoperabilityTestRawReceiver;
+ """).asInstanceOf[InteroperabilityTestRawReceiver]
+
+ expect(rawReceiver.check(7357)).toEqual(7357)
+
+ val check = rawReceiver.check
+ expect(check(0x600d)).toEqual(0x600d)
+
+ class InScalaSelect(check: js.Function1[Int, Int]) {
+ @JSExport
+ val member: Int = 0xbad2
+ def test(): Unit = expect(check(5894)).toEqual(5894)
+ }
+ new InScalaSelect(check).test()
+ }
+
+ it("should properly handle default parameters") {
+ val obj = js.eval("""
+ var interoperabilityTestDefaultParam = {
+ fun: function() { return arguments; }
+ };
+ interoperabilityTestDefaultParam;
+ """).asInstanceOf[InteroperabilityTestDefaultParam]
+
+ // Helpers
+ import js.Dynamic.{literal => args}
+ val undef = js.undefined
+
+ expect(obj.simple(1)).toEqual(args(`0` = 1))
+ expect(obj.simple(1,5)).toEqual(args(`0` = 1, `1` = 5))
+ expect(obj.named(y = 5)).toEqual(args(`0` = undef, `1` = 5))
+ expect(obj.named(x = 5)).toEqual(args(`0` = 5))
+ expect(obj.multi()(1,2,3,4)()).toEqual(args(
+ `0` = undef,
+ `1` = 1,
+ `2` = 2,
+ `3` = 3,
+ `4` = 4))
+ expect(obj.multi(2)()(5)).toEqual(args(
+ `0` = 2,
+ `1` = 5))
+ }
+
+ it("should properly handle default parameters for constructors - #791") {
+ js.eval("""
+ var InteroperabilityTestCtor = function(x,y) {
+ this.values = Array(x || 6, y || 8)
+ }
+ """);
+
+ expect(new InteroperabilityTestCtor().values).toEqual(js.Array(6,8))
+ expect(new InteroperabilityTestCtor(y = 7).values).toEqual(js.Array(6,7))
+ expect(new InteroperabilityTestCtor(3).values).toEqual(js.Array(3,8))
+ expect(new InteroperabilityTestCtor(10, 2).values).toEqual(js.Array(10,2))
+ }
+
+ it("should generate exports for methods inherited from traits - #178") {
+ import js.annotation.JSExport
+
+ trait Foo {
+ @JSExport
+ def theValue = 1
+ }
+ class Bar extends Foo
+
+ val x = (new Bar).asInstanceOf[js.Dynamic]
+
+ // Call the export by using js.Dynamic
+ expect(x.theValue).toEqual(1)
+ }
+
+ it("should allow constructor params that are vals/vars in facades - #1277") {
+ js.eval("""
+ var InteroparabilityCtorInlineValue = function(x,y) {
+ this.x = x;
+ this.y = y;
+ }
+ """)
+
+ val obj = new InteroparabilityCtorInlineValue(10, -1)
+
+ expect(obj.x).toEqual(10)
+ expect(obj.y).toEqual(-1)
+
+ obj.y = 100
+
+ expect(obj.x).toEqual(10)
+ expect(obj.y).toEqual(100)
+ }
+
+ it("should unbox Chars received from calling a JS interop method") {
+ val obj = js.eval("""
+ var obj = {
+ get: function() { return JSUtils().stringToChar('e'); }
+ };
+ obj;
+ """).asInstanceOf[InteroperabilityTestCharResult]
+
+ expect(obj.get().toInt).toEqual('e'.toInt)
+ }
+
+ it("should box Chars given to a JS interop method") {
+ val obj = js.eval("""
+ var obj = {
+ twice: function(c) { c = JSUtils().charToString(c); return c+c; }
+ };
+ obj;
+ """).asInstanceOf[InteroperabilityTestCharParam]
+
+ expect(obj.twice('x')).toEqual("xx")
+ }
+
+ it("should unbox value classes received from calling a JS interop method") {
+ val obj = js.eval("""
+ var obj = {
+ test: function(vc) { return vc; }
+ };
+ obj;
+ """).asInstanceOf[InteroperabilityTestValueClassResult]
+
+ val r = obj.test(new SomeValueClass(5))
+ expect(r.i).toEqual(5)
+ }
+
+ it("should box value classes given to a JS interop method") {
+ val obj = js.eval("""
+ var obj = {
+ stringOf: function(vc) { return vc.toString(); }
+ };
+ obj;
+ """).asInstanceOf[InteroperabilityTestValueClassParam]
+
+ val vc = new SomeValueClass(7)
+ expect(obj.stringOf(vc)).toEqual("SomeValueClass(7)")
+ }
+
+ it("should not unbox values received from JS method in statement position") {
+ /* To test this, we verify that a purposefully ill-typed facade does not
+ * throw a ClassCastException when called in statement position.
+ */
+ val obj = js.eval("""
+ var obj = {
+ test: function() { return 4; } // typed as String in the trait
+ };
+ obj;
+ """).asInstanceOf[InteroperabilityTestNoUnboxResultInStatement]
+ obj.test() // in statement position, should not throw
+ if (JasmineTestFramework.hasTag("compliant-asinstanceof"))
+ expect(() => obj.test()).toThrow // in expression position, should throw
+ }
+
+ when("compliant-asinstanceof").
+ it("should protect conversions from JS types to Scala types") {
+ class Foo
+ val foo: Any = new Foo
+
+ val invalidNumber: js.prim.Number = foo.asInstanceOf[js.prim.Number]
+ val nullNumber: js.prim.Number = null
+ expect(() => invalidNumber: Double).toThrow
+ expect(nullNumber: Double).toEqual(0)
+
+ val invalidBoolean: js.prim.Boolean = foo.asInstanceOf[js.prim.Boolean]
+ val nullBoolean: js.prim.Boolean = null
+ expect(() => invalidBoolean: Boolean).toThrow
+ expect(nullBoolean: Boolean).toEqual(false)
+
+ val invalidString: js.prim.String = foo.asInstanceOf[js.prim.String]
+ val nullString: js.prim.String = null
+ expect(() => invalidString: String).toThrow
+ expect(nullString: String).toBeNull
+ }
+
+ when("compliant-asinstanceof").
+ it("should asInstanceOf values received from calling a JS interop method") {
+ val obj = js.eval("""
+ var obj = {
+ testChar: function() { return 5; },
+ testInt: function() { return 6.4; },
+ testShort: function() { return 60000; },
+ testDouble: function() { return JSUtils().stringToChar('e'); },
+ testString: function() { return {}; },
+ testValueClass: function() { return "hello"; },
+ testNormalClass: function() { return 45; },
+ testAny: function() { return {}; }
+ };
+ obj;
+ """).asInstanceOf[InteroperabilityTestAsInstanceOfResult]
+
+ expect(() => obj.testChar()).toThrow
+ expect(() => obj.testInt()).toThrow
+ expect(() => obj.testShort()).toThrow
+ expect(() => obj.testDouble()).toThrow
+ expect(() => obj.testString()).toThrow
+ expect(() => obj.testValueClass()).toThrow
+ expect(() => obj.testNormalClass()).toThrow
+ expect(() => obj.testAny()).not.toThrow
+ }
+
+ }
+
+ trait InteroperabilityTestFieldEscape extends js.Object {
+ var `def`: Int = js.native
+ def `val`(): Int = js.native
+ def `val`(n: Int): Int = js.native
+ }
+
+ trait InteroperabilityTestJSName extends js.Object {
+ @JSName("val")
+ def value(): Int = js.native
+ @JSName("val")
+ def value(n: Int): Int = js.native
+ }
+
+ trait InteroperabilityTestProperty extends js.Object {
+ def a_=(x: Int): Unit = js.native
+ def a: Int = js.native
+ }
+
+ trait InteroperabilityTestPropertyNamed extends js.Object {
+ @JSName("b")
+ def a_=(x: Int): Unit = js.native
+ @JSName("b")
+ def a: Int = js.native
+ def b: Int = js.native
+ }
+
+ trait InteroperabilityTestJSBracketAccess extends js.Object {
+ @JSBracketAccess
+ def apply(index: Int): Int = js.native
+ @JSBracketAccess
+ def update(index: Int, v: Int): Unit = js.native
+ }
+
+ trait InteroperabilityTestRawReceiver extends js.Object {
+ val check: js.Function1[Int, Int] = js.native
+ }
+
+ /** Trait with different method signatures, all forwarded to the same
+ * JS raw function that returns the argument list for inspection
+ */
+ trait InteroperabilityTestDefaultParam extends js.Object {
+ @JSName("fun")
+ def simple(x: Int, y: Int = 5): js.Any = js.native
+ @JSName("fun")
+ def named(x: Int = 1, y: Int = 1, z: Int = 1): js.Any = js.native
+ @JSName("fun")
+ def multi(x: Int = 1)(ys: Int*)(z: Int = 1): js.Any = js.native
+ }
+
+ trait InteroperabilityTestCharResult extends js.Object {
+ def get(): Char = js.native
+ }
+
+ trait InteroperabilityTestCharParam extends js.Object {
+ def twice(c: Char): String = js.native
+ }
+
+ trait InteroperabilityTestValueClassResult extends js.Object {
+ def test(vc: Any): SomeValueClass = js.native
+ }
+
+ trait InteroperabilityTestValueClassParam extends js.Object {
+ def stringOf(vc: SomeValueClass): String = js.native
+ }
+
+ trait InteroperabilityTestNoUnboxResultInStatement extends js.Object {
+ def test(): String = js.native
+ }
+
+ trait InteroperabilityTestAsInstanceOfResult extends js.Object {
+ def testChar(): Char = js.native
+ def testInt(): Int = js.native
+ def testShort(): Short = js.native
+ def testDouble(): Double = js.native
+ def testString(): String = js.native
+ def testValueClass(): SomeValueClass = js.native
+ def testNormalClass(): List[Int] = js.native
+ def testAny(): Any = js.native
+ }
+}
+
+/*
+ * Helper classes, traits and objects defined here since they cannot be nested.
+ */
+
+@JSName("InteroperabilityTestInherit.Pattern")
+class InteroperabilityTestPattern protected () extends js.Object {
+ def this(pattern: String) = this()
+ val field: Int = js.native
+ def method(): String = js.native
+ def getConstructorParam(): String = js.native
+}
+
+trait InteroperabilityTestTopLevel extends js.Object {
+ val value: String = js.native
+ def valueAsInt(): Int = js.native
+}
+
+@JSName("InteroperabilityTestTopLevelObject")
+object InteroperabilityTestTopLevel extends js.Object {
+ def apply(value: String): InteroperabilityTestTopLevel = js.native
+}
+
+trait InteroperabilityTestVariadicMethod extends js.Object {
+ def foo(args: Any*): js.Array[Any] = js.native
+}
+
+class InteroperabilityTestVariadicCtor(inargs: Any*) extends js.Object {
+ val args: js.Array[Any] = js.native
+}
+
+object InteroperabilityTestGlobalScope extends js.GlobalScope {
+ var interoperabilityTestGlobalScopeValue: String = js.native
+ def interoperabilityTestGlobalScopeValueAsInt(): Int = js.native
+}
+
+class SomeValueClass(val i: Int) extends AnyVal {
+ override def toString(): String = s"SomeValueClass($i)"
+}
+
+class InteroperabilityTestCtor(x: Int = 5, y: Int = ???) extends js.Object {
+ def values: js.Array[Int] = js.native
+}
+
+class InteroparabilityCtorInlineValue(val x: Int, var y: Int) extends js.Object
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/LongTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/LongTest.scala
new file mode 100644
index 0000000..01d6f6e
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/LongTest.scala
@@ -0,0 +1,159 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.compiler
+
+import scala.scalajs.js
+
+import org.scalajs.jasminetest.JasmineTest
+
+/**
+ * tests the compiler re-patching of native longs to
+ * scala.scalajs.runtime.Long
+ * see scala.scalajs.testsuite.jsinterop.RuntimeLongTest
+ * for a test of the implementation itself
+ */
+object LongTest extends JasmineTest {
+
+ describe("JavaScript 64-bit long compatibility") {
+ it("should correctly handle literals") {
+ expect(5L + 100L == 105L).toBeTruthy
+ expect(2147483649L + 2L == 2147483651L).toBeTruthy
+ expect(-2147483648L * 4 == -8589934592L).toBeTruthy
+ expect(4503599627370510L * (-4) == -18014398509482040L).toBeTruthy
+ }
+
+ it("should correctly dispatch unary ops on Longs") {
+ val x = 10L
+ expect(-x == -10L).toBeTruthy
+ val y = 5L
+ expect(-y == -5L).toBeTruthy
+ expect(+y == 5L).toBeTruthy
+ expect(~y == -6L).toBeTruthy
+ }
+
+ it("should correctly dispatch binary ops on Longs") {
+ expect(5L * 5F == 25F).toBeTruthy
+ expect(5L % 4F == 1F).toBeTruthy
+ expect(5F * 4L == 20F).toBeTruthy
+ }
+
+ it("should support shifts with Longs - #622") {
+ def l(x: Long): Long = x
+ def i(x: Int): Int = x
+
+ expect(l(-7L) >>> 100L == 268435455L).toBeTruthy
+ expect(l(-7L) >> 100L == -1L).toBeTruthy
+ expect(l(-7L) >> 100 == -1L).toBeTruthy
+ expect(l(-7L) >>> 100 == 268435455).toBeTruthy
+ expect(l(-7L) << 100L == -481036337152L).toBeTruthy
+ expect(l(-7L) << 100 == -481036337152L).toBeTruthy
+ expect(l(7L) << 100L == 481036337152L).toBeTruthy
+ expect(l(8L) << 100L == 549755813888L).toBeTruthy
+ expect(l(-7L) >>> 4 == 1152921504606846975L).toBeTruthy
+
+ expect(i(7) << 100).toEqual(112)
+ expect(i(-7) >> 100).toEqual(-1)
+ expect(i(-7) >>> 100).toEqual(268435455)
+ expect(i(-65) >> 100).toEqual(-5)
+ expect(i(-65) >> 4).toEqual(-5)
+ }
+
+ it("primitives should convert to Long") {
+ // Byte
+ expect(112.toByte.toLong == 112L).toBeTruthy
+ // Short
+ expect((-10).toShort.toLong == -10L).toBeTruthy
+ // Char
+ expect('A'.toLong == 65L).toBeTruthy
+ // Int
+ expect(5.toLong == 5L).toBeTruthy
+ // Long
+ expect(10L.toLong == 10L).toBeTruthy
+ // Float
+ expect(100000.6f.toLong == 100000L).toBeTruthy
+ // Double
+ expect(100000.6.toLong == 100000L).toBeTruthy
+ }
+
+ it("should support hashCode()") {
+ expect(0L .hashCode()).toEqual(0)
+ expect(55L .hashCode()).toEqual(55)
+ expect((-12L) .hashCode()).toEqual(11)
+ expect(10006548L .hashCode()).toEqual(10006548)
+ expect((-1098748L).hashCode()).toEqual(1098747)
+
+ expect(613354684553L .hashCode()).toEqual(-825638905)
+ expect(9863155567412L .hashCode()).toEqual(1910653900)
+ expect(3632147899696541255L.hashCode()).toEqual(1735398658)
+ expect(7632147899696541255L.hashCode()).toEqual(-1689438124)
+ }
+
+ it("should support ##") {
+ expect(0L .##).toEqual(0)
+ expect(55L .##).toEqual(55)
+ expect((-12L) .##).toEqual(-12)
+ expect(10006548L .##).toEqual(10006548)
+ expect((-1098748L).##).toEqual(-1098748)
+
+ expect(9863155567412L .##).toEqual(1910653900)
+ expect(3632147899696541255L.##).toEqual(1735398658)
+
+ // These two (correctly) give different results on 2.10 and 2.11
+ //expect(613354684553L .##).toEqual(-825638905) // xx06 on 2.10
+ //expect(7632147899696541255L.##).toEqual(-1689438124) // xx25 on 2.10
+ }
+
+ it("should correctly concat to string") {
+ val x = 20L
+ expect("asdf" + 5L + x + "hello").toEqual("asdf520hello")
+ expect(x + "hello").toEqual("20hello")
+ }
+
+ it("string should convert to Long") {
+ expect("45678901234567890".toLong == 45678901234567890L).toBeTruthy
+ }
+
+ it("should convert from and to js.prim.Number") {
+ val x = 5: js.prim.Number
+ expect((5L: js.prim.Number) == x).toBeTruthy
+ expect(x.toLong == 5L).toBeTruthy
+ }
+
+ it("should correctly implement is/asInstanceOf Longs") {
+ val dyn: Any = 5L
+ val stat: Long = 5L
+
+ expect(stat.asInstanceOf[Long]).toEqual(5L)
+ // models current scala behavior. See SI-1448
+ expect(stat.asInstanceOf[Int]).toEqual(5)
+
+ expect(stat.isInstanceOf[Long]).toBeTruthy
+ expect(stat.isInstanceOf[Int]).toBeFalsy
+
+ expect(dyn.asInstanceOf[Long]).toEqual(5L)
+
+ expect(dyn.isInstanceOf[Long]).toBeTruthy
+ expect(dyn.isInstanceOf[Int]).toBeFalsy
+ }
+
+ when("compliant-asinstanceof").
+ it("should correctly implement asInstanceOf Longs (negative)") {
+ val dyn: Any = 5L
+
+ expect(() => dyn.asInstanceOf[Int]).toThrow
+ }
+
+ it("should correctly compare to other numeric types") {
+ expect(5L == 5).toBeTruthy
+ expect(5 == 5L).toBeTruthy
+ expect(4 == 5l).toBeFalsy
+ expect('A' == 65L).toBeTruthy
+ }
+ }
+
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/OptimizerTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/OptimizerTest.scala
new file mode 100644
index 0000000..986c25a
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/OptimizerTest.scala
@@ -0,0 +1,43 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.compiler
+
+import org.scalajs.jasminetest.JasmineTest
+
+object OptimizerTest extends JasmineTest {
+
+ describe("Inlineable classes") {
+
+ it("must update fields of `this` in the computation of other fields - #1153") {
+ val foo = new InlineClassDependentFields(5)
+ expect(foo.x).toEqual(5)
+ expect(foo.b).toBeTruthy
+ expect(foo.y).toEqual(11)
+ }
+
+ it("must not break code that assigns `this` to a field") {
+ val foo = new InlineClassThisAlias(5)
+ expect(foo.z).toEqual(5)
+ }
+
+ }
+
+ @inline
+ class InlineClassDependentFields(val x: Int) {
+ val b = x > 3
+ val y = if (b) x + 6 else x-2
+ }
+
+ @inline
+ class InlineClassThisAlias(val x: Int) {
+ val t = this
+ val y = x
+ val z = t.y
+ }
+
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/ReflectionTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/ReflectionTest.scala
new file mode 100644
index 0000000..3e1d7a2
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/ReflectionTest.scala
@@ -0,0 +1,69 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.compiler
+
+import scala.language.implicitConversions
+
+import scala.scalajs.js
+
+import org.scalajs.jasminetest.JasmineTest
+
+/** Tests the little reflection we support */
+object ReflectionTest extends JasmineTest {
+
+ describe("Scala.js Reflection (through java.lang.Class)") {
+ it("should append $ to class name of objects") {
+ expect(TestObject.getClass.getName).toEqual(
+ "scala.scalajs.testsuite.compiler.ReflectionTest$TestObject$")
+ }
+
+ it("should support isInstance") {
+ class A
+ class B extends A
+ val b = new B
+ expect(classOf[A].isInstance(b)).toBeTruthy
+ expect(classOf[A].isInstance("hello")).toBeFalsy
+ }
+
+ it("getClass() for normal types") {
+ class Foo {
+ def bar() = super.getClass()
+ }
+ val foo = new Foo
+ expect(foo.getClass() eq classOf[Foo]).toBeTruthy
+ expect(foo.bar() eq classOf[Foo]).toBeTruthy
+ }
+
+ it("getClass() for anti-boxed primitive types") {
+ implicit def classAsAny(c: java.lang.Class[_]): js.Any =
+ c.asInstanceOf[js.Any]
+ expect((false: Any).getClass).toBe(classOf[java.lang.Boolean])
+ expect(('a': Any).getClass).toBe(classOf[java.lang.Character])
+ expect((1.toByte: Any).getClass).toBe(classOf[java.lang.Byte])
+ expect((1.toShort: Any).getClass).toBe(classOf[java.lang.Byte])
+ expect((1: Any).getClass).toBe(classOf[java.lang.Byte])
+ expect((1L: Any).getClass).toBe(classOf[java.lang.Long])
+ expect((1.5f: Any).getClass).toBe(classOf[java.lang.Float])
+ expect((1.5: Any).getClass).toBe(classOf[java.lang.Float])
+ expect(((): Any).getClass).toBe(classOf[scala.runtime.BoxedUnit])
+ }
+
+ it("Class.isAssignableFrom should mimic runtime type tests behavior - #879") {
+ expect(classOf[Short].isAssignableFrom(classOf[Byte])).toBeTruthy
+ expect(classOf[Byte].isAssignableFrom(classOf[Byte])).toBeTruthy
+ expect(classOf[Byte].isAssignableFrom(classOf[Short])).toBeFalsy
+ expect(classOf[Int].isAssignableFrom(classOf[Byte])).toBeTruthy
+ expect(classOf[Double].isAssignableFrom(classOf[Int])).toBeTruthy
+ expect(classOf[Int].isAssignableFrom(classOf[Double])).toBeFalsy
+ expect(classOf[Long].isAssignableFrom(classOf[Int])).toBeFalsy
+ }
+ }
+
+ object TestObject
+
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/ReflectiveCallTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/ReflectiveCallTest.scala
new file mode 100644
index 0000000..6020a5f
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/ReflectiveCallTest.scala
@@ -0,0 +1,330 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.compiler
+
+import scala.scalajs.js
+import org.scalajs.jasminetest.JasmineTest
+
+import language.reflectiveCalls
+
+import java.lang.{Float => JFloat, Double => JDouble}
+
+object ReflectiveCallTest extends JasmineTest {
+
+ describe("Reflective Calls") {
+ it("should allow subtyping in return types") {
+ class A { def x = 1 }
+ class B extends A { override def x = 2 }
+
+ object Generator {
+ def generate(): B = new B
+ }
+
+ def f(x: { def generate(): A }) = x.generate
+
+ expect(f(Generator).x).toEqual(2)
+ }
+
+ it("should allow this.type in return types") {
+ type valueType = { def value: this.type }
+ def f(x: valueType) = x.value
+
+ class StringValue(x: String) {
+ def value: this.type = this
+ override def toString = s"StringValue($x)"
+ }
+
+ expect(f(new StringValue("foo")).toString).toEqual("StringValue(foo)")
+ }
+
+ it("should allow generic return types") {
+ case class Tata(name: String)
+
+ object Rec {
+ def e(x: Tata) = new Tata("iei")
+ }
+
+ def m[T](r: Object { def e(x: Tata): T}) =
+ r.e(new Tata("foo"))
+
+ expect(m[Tata](Rec).toString).toEqual("Tata(iei)")
+ }
+
+ it("should work with unary methods on primitive types") {
+ def fInt(x: Any { def unary_- :Int }) = -x
+ expect(fInt(1.toByte)).toEqual(-1)
+ expect(fInt(1.toShort)).toEqual(-1)
+ expect(fInt(1.toChar)).toEqual(-1)
+ expect(fInt(1)).toEqual(-1)
+
+ def fLong(x: Any { def unary_- :Long }) = -x
+ expect(fLong(1L)).toEqual(-1L)
+
+ def fFloat(x: Any { def unary_- :Float}) = -x
+ expect(fFloat(1.5f)).toEqual(-1.5f)
+
+ def fDouble(x: Any { def unary_- :Double }) = -x
+ expect(fDouble(1.5)).toEqual(-1.5)
+
+ def fBoolean(x: Any { def unary_! :Boolean }) = !x
+ expect(fBoolean(false)).toBeTruthy
+ expect(fBoolean(true)).toBeFalsy
+ }
+
+ it("should work with binary operators on primitive types") {
+ def fLong(x: Any { def +(x: Long): Long }) = x + 5L
+ expect(fLong(5.toByte)).toEqual(10L)
+ expect(fLong(10.toShort)).toEqual(15L)
+ expect(fLong(10.toChar)).toEqual(15L)
+ expect(fLong(-1)).toEqual(4L)
+ expect(fLong(17L)).toEqual(22L)
+
+ def fInt(x: Any { def /(x: Int): Int }) = x / 7
+ expect(fInt(65.toByte)).toEqual(9)
+ expect(fInt(15.toShort)).toEqual(2)
+ expect(fInt(25.toChar)).toEqual(3)
+ expect(fInt(-40)).toEqual(-5)
+
+ def fShort(x: Any { def +(x: Short): Int }) = x + 6.toShort
+ expect(fShort(65.toByte)).toEqual(71)
+ expect(fShort(15.toShort)).toEqual(21)
+ expect(fShort(25.toChar)).toEqual(31)
+ expect(fShort(-40)).toEqual(-34)
+
+ def fFloat(x: Any { def %(x: Float): Float}) = x % 3.4f
+ expect(fFloat(5.5f)).toEqual(2.1f)
+
+ def fDouble(x: Any { def /(x: Double): Double }) = x / 1.4
+ expect(fDouble(-1.5)).toEqual(-1.0714285714285714)
+
+ def fBoolean(x: Any { def &&(x: Boolean): Boolean }) = x && true
+ expect(fBoolean(false)).toBeFalsy
+ expect(fBoolean(true)).toBeTruthy
+ }
+
+ it("should work with equality operators on primitive types") {
+ def fNum(obj: Any { def ==(x: Int): Boolean }) = obj == 5
+ expect(fNum(5.toByte)).toBeTruthy
+ expect(fNum(6.toByte)).toBeFalsy
+ expect(fNum(5.toShort)).toBeTruthy
+ expect(fNum(7.toShort)).toBeFalsy
+ expect(fNum(5.toChar)).toBeTruthy
+ expect(fNum('r')).toBeFalsy
+ expect(fNum(5)).toBeTruthy
+ expect(fNum(-4)).toBeFalsy
+ expect(fNum(5L)).toBeTruthy
+ expect(fNum(400L)).toBeFalsy
+ expect(fNum(5.0f)).toBeTruthy
+ expect(fNum(5.6f)).toBeFalsy
+ expect(fNum(5.0)).toBeTruthy
+ expect(fNum(7.9)).toBeFalsy
+ def fBool(obj: Any { def ==(x: Boolean): Boolean }) = obj == false
+ expect(fBool(true)).toBeFalsy
+ expect(fBool(false)).toBeTruthy
+
+ def fNumN(obj: Any { def !=(x: Int): Boolean }) = obj != 5
+ expect(fNumN(5.toByte)).toBeFalsy
+ expect(fNumN(6.toByte)).toBeTruthy
+ expect(fNumN(5.toShort)).toBeFalsy
+ expect(fNumN(7.toShort)).toBeTruthy
+ expect(fNumN(5.toChar)).toBeFalsy
+ expect(fNumN('r')).toBeTruthy
+ expect(fNumN(5)).toBeFalsy
+ expect(fNumN(-4)).toBeTruthy
+ expect(fNumN(5L)).toBeFalsy
+ expect(fNumN(400L)).toBeTruthy
+ expect(fNumN(5.0f)).toBeFalsy
+ expect(fNumN(5.6f)).toBeTruthy
+ expect(fNumN(5.0)).toBeFalsy
+ expect(fNumN(7.9)).toBeTruthy
+ def fBoolN(obj: Any { def !=(x: Boolean): Boolean }) = obj != false
+ expect(fBoolN(true)).toBeTruthy
+ expect(fBoolN(false)).toBeFalsy
+
+ }
+
+ it("should work with Arrays") {
+ type UPD = { def update(i: Int, x: String): Unit }
+ type APL = { def apply(i: Int): String }
+ type LEN = { def length: Int }
+ type CLONE = Any { def clone(): Object }
+ def upd(obj: UPD, i: Int, x: String) = obj.update(i,x)
+ def apl(obj: APL, i: Int) = obj.apply(i)
+ def len(obj: LEN) = obj.length
+ def clone(obj: CLONE) = obj.clone
+
+ val x = Array("asdf","foo","bar")
+ val y = clone(x).asInstanceOf[Array[String]]
+
+ expect(len(x)).toEqual(3)
+ expect(apl(x,0)).toEqual("asdf")
+ upd(x,1,"2foo")
+ expect(x(1)).toEqual("2foo")
+ expect(y(1)).toEqual("foo")
+ }
+
+ it("should work with Arrays of primitive values") {
+ type UPD = { def update(i: Int, x: Int): Unit }
+ type APL = { def apply(i: Int): Int}
+ type LEN = { def length: Int }
+ type CLONE = Any { def clone(): Object }
+ def upd(obj: UPD, i: Int, x: Int) = obj.update(i,x)
+ def apl(obj: APL, i: Int) = obj.apply(i)
+ def len(obj: LEN) = obj.length
+ def clone(obj: CLONE) = obj.clone
+
+ val x = Array(5,2,8)
+ val y = clone(x).asInstanceOf[Array[Int]]
+
+ expect(len(x)).toEqual(3)
+ expect(apl(x,0)).toEqual(5)
+ upd(x,1,1000)
+ expect(x(1)).toEqual(1000)
+ expect(y(1)).toEqual(2)
+ }
+
+ it("should work with Strings") {
+ def get(obj: { def codePointAt(str: Int): Int }) =
+ obj.codePointAt(1)
+ expect(get("Hi")).toEqual('i'.toInt)
+
+ def sub(x: { def substring(x: Int): AnyRef }) = x.substring(5)
+ expect(sub("asdfasdfasdf") == "sdfasdf").toBeTruthy
+
+ type LEN_A = { def length: Any }
+ def lenA(x: LEN_A) = x.length
+ expect(lenA("asdf") == 4).toBeTruthy
+ }
+
+ it("should properly generate forwarders for inherited methods") {
+ trait A {
+ def foo: Int
+ }
+
+ abstract class B extends A
+
+ class C extends B {
+ def foo = 1
+ }
+
+ def call(x: { def foo: Int }) = x.foo
+
+ expect(call(new C)).toEqual(1)
+ }
+
+ it("should work on java.lang.Object.{ notify, notifyAll } - #303") {
+ type ObjNotifyLike = Any {
+ def notify(): Unit
+ def notifyAll(): Unit
+ }
+ def objNotifyTest(obj: ObjNotifyLike) = {
+ obj.notify()
+ obj.notifyAll()
+ 1
+ }
+
+ class A
+
+ expect(objNotifyTest(new A())).toEqual(1)
+ }
+
+ it("should work on java.lang.Object.clone - #303") {
+ type ObjCloneLike = Any { def clone(): AnyRef }
+ def objCloneTest(obj: ObjCloneLike) = obj.clone()
+
+ class B(val x: Int) extends Cloneable {
+ override def clone() = super.clone
+ }
+
+ val b = new B(1)
+ val bClone = objCloneTest(b).asInstanceOf[B]
+
+ expect(b eq bClone).toBeFalsy
+ expect(bClone.x).toEqual(1)
+ }
+
+ it("should work on scala.AnyRef.{ eq, ne } - #303") {
+ type ObjEqLike = Any {
+ def eq(that: AnyRef): Boolean
+ def ne(that: AnyRef): Boolean
+ }
+ def objEqTest(obj: ObjEqLike, that: AnyRef) = obj eq that
+ def objNeTest(obj: ObjEqLike, that: AnyRef) = obj ne that
+
+ class A
+
+ val a1 = new A
+ val a2 = new A
+
+ expect(objEqTest(a1,a2)).toBeFalsy
+ expect(objEqTest(a1,a1)).toBeTruthy
+
+ expect(objNeTest(a1,a2)).toBeTruthy
+ expect(objNeTest(a1,a1)).toBeFalsy
+ }
+
+ it("should work on java.lang.{Float,Double}.{isNaN,isInfinite}") {
+ type FloatingNumberLike = Any {
+ def isNaN(): Boolean
+ def isInfinite(): Boolean
+ }
+ def test(x: FloatingNumberLike, isNaN: Boolean,
+ isInfinite: Boolean) = {
+ expect(x.isNaN()).toEqual(isNaN)
+ expect(x.isInfinite()).toEqual(isInfinite)
+ }
+
+ test(new JFloat(Float.NaN), true, false)
+ test(new JFloat(Float.PositiveInfinity), false, true)
+ test(new JFloat(Float.NegativeInfinity), false, true)
+ test(new JFloat(54.67), false, false)
+
+ test(new JDouble(Double.NaN), true, false)
+ test(new JDouble(Double.PositiveInfinity), false, true)
+ test(new JDouble(Double.NegativeInfinity), false, true)
+ test(new JDouble(54.67), false, false)
+ }
+
+ it("should work with default arguments - #390") {
+ def pimpIt(a: Int) = new {
+ def foo(b: Int, c: Int = 1): Int = a + b + c
+ }
+
+ expect(pimpIt(1).foo(2)).toEqual(4)
+ expect(pimpIt(2).foo(2,4)).toEqual(8)
+ }
+
+ it("should unbox all types of arguments - #899") {
+ class Foo {
+ def makeInt: Int = 5
+ def testInt(x: Int): Unit = expect(x).toEqual(5)
+
+ def makeRef: Option[String] = Some("hi")
+ def testRef(x: Option[String]): Unit = expect(x == Some("hi")).toBeTruthy
+ }
+
+ /* Note: we should also test with value classes, except that Scala itself
+ * does not support value classes as parameters or result type of
+ * methods in structural types.
+ */
+
+ def test(foo: {
+ def makeInt: Int
+ def testInt(x: Int): Unit
+ def makeRef: Option[String]
+ def testRef(x: Option[String]): Unit
+ }): Unit = {
+ foo.testInt(foo.makeInt)
+ foo.testRef(foo.makeRef)
+ }
+
+ test(new Foo)
+ }
+
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/RegressionTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/RegressionTest.scala
new file mode 100644
index 0000000..19cceb2
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/RegressionTest.scala
@@ -0,0 +1,287 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.compiler
+
+import scala.annotation.tailrec
+
+import scala.scalajs.js
+import org.scalajs.jasminetest.JasmineTest
+
+object RegressionTest extends JasmineTest {
+
+ class Bug218Foo[T](val x: T) extends AnyVal
+
+ describe("Scala.js compiler regression tests") {
+
+ it("Wrong division conversion (7 / 2.0) - #18") {
+ val div = 7 / 2.0
+ expect(div).toEqual(3.5)
+ expect(div.getClass.getName).toEqual("double")
+
+ val mod = 7 % 2.0
+ expect(mod).toEqual(1.0)
+ expect(mod.getClass.getName).toEqual("double")
+ }
+
+ it("js.prim.String + js.prim.String is ambiguous - #20") {
+ val a: js.prim.String = "a"
+ val b: js.prim.String = "b"
+ val c: js.prim.String = a + b
+ expect(c).toEqual("ab")
+ }
+
+ it("Abort with some pattern match guards - #22") {
+ object PatternMatchGuards {
+ def go(f: Int => Int) = f(1)
+ def main(): Unit = {
+ go {
+ case x if false => x
+ }
+ }
+ }
+ // Nothing to check
+ }
+
+ it("Bad encoding for characters spanning 2 UTF-16 chars - #23") {
+ val str = "A∀\uD835\uDCAB"
+ var s: String = ""
+ for (c <- str) {
+ val code: Int = c
+ s = s + code + " "
+ }
+ expect(s).toEqual("65 8704 55349 56491 ")
+ }
+
+ it("String concatenation with null - #26") {
+ val x: Object = null
+ expect(x + "check").toEqual("nullcheck")
+ }
+
+ class Bug66A(s: String, e: Object) {
+ def this(e: Object) = this("", e)
+ def this(s: String) = this(s, "")
+ }
+ class Bug66B(s: String, e: Object) extends Bug66A(s)
+
+ it("should emit static calls when forwarding to another constructor - #66") {
+ new Bug66B("", "")
+ }
+
+ it("should not swallow Unit expressions when converting to js.prim.Undefined - #83") {
+ var effectHappened = false
+ def doEffect(): Unit = effectHappened = true
+ def f(): js.prim.Undefined = doEffect()
+ f()
+ expect(effectHappened).toBeTruthy
+ }
+
+ it("should correctly call subSequence on non-string CharSequences - #55") {
+ val arr: CharSequence = Array('a','b','c','d')
+ val ss = arr.subSequence(2,3)
+ expect(ss.length()).toEqual(1)
+ expect(ss.charAt(0)).toEqual('c')
+ }
+
+ it("should correctly concat primitive values to strings - #113") {
+ expect(4 + "foo").toEqual("4foo")
+ expect('a' + "foo").toEqual("afoo")
+ }
+
+ it("should resolve overloads on scala.Function.apply when converting to js.Function - #125") {
+ class Fct extends Function1[Int,Any] {
+ def apply(n: Int) = n
+ }
+
+ val scalaFunction = new Fct
+ val jsFunction: js.Any = scalaFunction
+ val thisFunction: js.ThisFunction = scalaFunction
+ }
+
+ it("should correctly dispatch calls on private functions - #165") {
+ class A {
+ private def x = 1
+ def value = x
+ }
+ class B extends A {
+ private def x = 2
+ }
+ expect(new B().value).toEqual(1)
+ }
+
+ it("should correctly mangle JavaScript reserved identifiers - #153") {
+ // Class name
+ class break {
+ // class variable
+ var continue = 1
+ // method name
+ def switch = {
+ // local name
+ val default = 2
+ default
+ }
+ }
+ trait Foo {
+ // static member (through mixin)
+ def function = 3
+ }
+
+ val x = new break with Foo
+ expect(x.continue).toEqual(1)
+ expect(x.switch).toEqual(2)
+ expect(x.function).toEqual(3)
+ }
+
+ it("should correctly mangle identifiers starting with a digit - #153") {
+ // Class name
+ class `0` {
+ // class variable
+ var `1` = 1
+ // method name
+ def `2` = {
+ // local name
+ val `22` = 2
+ `22`
+ }
+ }
+ trait Foo {
+ // static member (through mixin)
+ def `3` = 3
+ }
+
+ val x = new `0` with Foo
+ expect(x.`1`).toEqual(1)
+ expect(x.`2`).toEqual(2)
+ expect(x.`3`).toEqual(3)
+ }
+
+ it("should reserve `eval` and `arguments` - #743") {
+ val eval = 5
+ expect(eval).toEqual(5)
+ val arguments = "hello"
+ expect(arguments).toEqual("hello")
+ }
+
+ it("should support class literals for existential value types - #218") {
+ expect(scala.reflect.classTag[Bug218Foo[_]].toString).toEqual(
+ "scala.scalajs.testsuite.compiler.RegressionTest$Bug218Foo")
+ }
+
+ it("should support Buffer - #268") {
+ val a = scala.collection.mutable.Buffer.empty[Int]
+ a.insert(0, 0)
+ a.remove(0)
+ for (i <- 0 to 10) {
+ a.insert(a.length / 2, i)
+ }
+ expect(a.mkString(", ")).toEqual("1, 3, 5, 7, 9, 10, 8, 6, 4, 2, 0")
+ }
+
+ it("should not call equals when comparing with a literal null - #362") {
+ class A { override def equals(x: Any) = !(this == null) }
+
+ val x = new A
+ val y = new A
+
+ // If the null comparisons actually call equals, the following two will
+ // cause infinite recursion
+ expect(x == y).toBeTruthy
+ expect(y == x).toBeTruthy
+ }
+
+ it("should unbox null to the zero of types - #674") {
+ class Box[A] {
+ var value: A = _
+ }
+ def zero[A]: A = new Box[A].value
+
+ /* Note: the same shape of test for Unit does not work, but it seems to
+ * be a problem in scalac because it does not work on the JVM either.
+ */
+
+ val bool = zero[Boolean]
+ expect((bool: Any).isInstanceOf[Boolean]).toBeTruthy
+ expect(bool == false).toBeTruthy
+
+ val char = zero[Char]
+ expect((char: Any).isInstanceOf[Char]).toBeTruthy
+ expect(char == '\u0000').toBeTruthy
+
+ val byte = zero[Byte]
+ expect((byte: Any).isInstanceOf[Byte]).toBeTruthy
+ expect(byte == 0.toByte).toBeTruthy
+
+ val short = zero[Short]
+ expect((short: Any).isInstanceOf[Short]).toBeTruthy
+ expect(short == 0.toShort).toBeTruthy
+
+ val int = zero[Int]
+ expect((int: Any).isInstanceOf[Int]).toBeTruthy
+ expect(int == 0).toBeTruthy
+
+ val long = zero[Long]
+ expect((long: Any).isInstanceOf[Long]).toBeTruthy
+ expect(long == 0L).toBeTruthy
+
+ val float = zero[Float]
+ expect((float: Any).isInstanceOf[Float]).toBeTruthy
+ expect(float == 0.0f).toBeTruthy
+
+ val double = zero[Double]
+ expect((double: Any).isInstanceOf[Double]).toBeTruthy
+ expect(double == 0.0).toBeTruthy
+
+ val ref = zero[AnyRef]
+ expect(ref == null).toBeTruthy
+ }
+
+ it("Param defs in tailrec methods should be considered mutable - #825") {
+ @tailrec
+ def foo(x: Int, y: Int): Unit = {
+ if (x < y) foo(y, x)
+ else {
+ expect(x).toEqual(4)
+ expect(y).toEqual(2)
+ }
+ }
+ foo(2, 4)
+ }
+
+ it("null.synchronized should throw - #874") {
+ expect(() => null.synchronized(5)).toThrow
+ }
+
+ it("x.synchronized should preserve side-effects of x") {
+ var c = 0
+ def x = { c += 1; this }
+ expect(x.synchronized(5)).toEqual(5)
+ expect(c).toEqual(1)
+ }
+
+ it("IR checker should allow Apply/Select on NullType and NothingType - #1123") {
+ def giveMeANull(): Null = null
+ expect(() => (giveMeANull(): StringBuilder).append(5)).toThrow
+ expect(() => (giveMeANull(): scala.runtime.IntRef).elem).toThrow
+
+ def giveMeANothing(): Nothing = sys.error("boom")
+ expect(() => (giveMeANothing(): StringBuilder).append(5)).toThrow
+ expect(() => (giveMeANothing(): scala.runtime.IntRef).elem).toThrow
+ }
+
+ it("should not put bad flags on caseaccessor export forwarders - #1191") {
+ // This test used to choke patmat
+
+ @scala.scalajs.js.annotation.JSExportAll
+ case class T(one: Int, two: Int)
+
+ val T(a, b) = T(1, 2)
+
+ expect(a).toEqual(1)
+ expect(b).toEqual(2)
+ }
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/RuntimeTypesTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/RuntimeTypesTest.scala
new file mode 100644
index 0000000..b1a32c4
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/RuntimeTypesTest.scala
@@ -0,0 +1,77 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.compiler
+
+import scala.scalajs.js
+import org.scalajs.jasminetest.JasmineTest
+
+import scala.util.{ Try, Failure }
+
+object RuntimeTypesTest extends JasmineTest {
+
+ describe("scala.Nothing") {
+
+ when("compliant-asinstanceof").
+ it("casts to scala.Nothing should fail") {
+ val msg = Try("a".asInstanceOf[Nothing]) match {
+ case Failure(thr: ClassCastException) => thr.getMessage
+ case _ => "not failed"
+ }
+ expect(msg).toEqual("a is not an instance of scala.runtime.Nothing$")
+ }
+
+ it("Array[Nothing] should be allowed to exists and be castable") {
+ val arr = Array[Nothing]()
+ arr.asInstanceOf[Array[Nothing]]
+ }
+
+ it("Array[Array[Nothing]], too") {
+ val arr = Array[Array[Nothing]]()
+ arr.asInstanceOf[Array[Array[Nothing]]]
+ // This apparently works too... Dunno why
+ arr.asInstanceOf[Array[Nothing]]
+ }
+
+ }
+
+ describe("scala.Null") {
+
+ when("compliant-asinstanceof").
+ it("casts to scala.Null should fail for everything else but null") {
+ val msg = Try("a".asInstanceOf[Null]) match {
+ case Failure(thr: ClassCastException) => thr.getMessage
+ case _ => "not failed"
+ }
+ expect(msg).toEqual("a is not an instance of scala.runtime.Null$")
+ }
+
+ it("classTag of scala.Null should contain proper Class[_] - #297") {
+ val tag = scala.reflect.classTag[Null]
+ expect(tag.runtimeClass != null).toBeTruthy
+ expect(tag.runtimeClass.getName).toEqual("scala.runtime.Null$")
+ }
+
+ it("casts to scala.Null should succeed on null") {
+ null.asInstanceOf[Null]
+ }
+
+ it("Array[Null] should be allowed to exist and be castable") {
+ val arr = Array.fill[Null](5)(null)
+ arr.asInstanceOf[Array[Null]]
+ }
+
+ it("Array[Array[Null]] too") {
+ val arr = Array.fill[Null](5,5)(null)
+ arr.asInstanceOf[Array[Array[Null]]]
+ // This apparently works too... Dunno why
+ arr.asInstanceOf[Array[Null]]
+ }
+
+ }
+
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/ShortTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/ShortTest.scala
new file mode 100644
index 0000000..10746ba
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/ShortTest.scala
@@ -0,0 +1,38 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.compiler
+
+import org.scalajs.jasminetest.JasmineTest
+import scala.scalajs.js
+
+object ShortTest extends JasmineTest {
+
+ describe("Short primitives") {
+
+ it("should always be in their range") {
+ def test(x: Int, y: Short): Unit =
+ expect(x.toShort).toEqual(y)
+
+ test(0, 0)
+ test(-500, -500)
+ test(-90000, -24464)
+ test(123456789, -13035)
+ test(-40000, 25536)
+ test(65536, 0)
+ test(32768, -32768)
+
+ def testC(x: Char, y: Short): Unit =
+ expect(x.toShort).toEqual(y)
+
+ testC(-1.toChar, -1)
+ testC(200.toChar, 200)
+ testC(60000.toChar, -5536)
+ }
+
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/UnitTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/UnitTest.scala
new file mode 100644
index 0000000..8e2be64
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/compiler/UnitTest.scala
@@ -0,0 +1,47 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.compiler
+
+import org.scalajs.jasminetest.JasmineTest
+import scala.scalajs.js
+
+object UnitTest extends JasmineTest {
+
+ describe("Unit primitive") {
+
+ it("should have toString()") {
+ expect(().toString()).toEqual("undefined")
+ expect(((): Any).toString()).toEqual("undefined")
+ }
+
+ it("should have hashCode()") {
+ expect(().hashCode()).toEqual(0)
+ expect(((): Any).hashCode()).toEqual(0)
+ expect(().##).toEqual(0)
+ }
+
+ it("should equal itself") {
+ expect(().equals(())).toBeTruthy
+ expect(((): Any).equals((): Any)).toBeTruthy
+ }
+
+ it("should not equal other values") {
+ def testAgainst(v: Any): Unit = {
+ expect(().equals(v)).toBeFalsy
+ expect(((): Any).equals(v)).toBeFalsy
+ }
+
+ testAgainst(0)
+ testAgainst(1)
+ testAgainst(null)
+ testAgainst(false)
+ testAgainst("")
+ }
+
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ArraysTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ArraysTest.scala
new file mode 100644
index 0000000..d0d97f1
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ArraysTest.scala
@@ -0,0 +1,749 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.javalib
+
+import language.implicitConversions
+
+import scala.scalajs.js
+import scala.scalajs.js.JSConverters._
+
+import org.scalajs.jasminetest.JasmineTest
+
+import java.util.{ Arrays, Comparator }
+
+import scala.reflect.ClassTag
+
+object ArraysTest extends ArraysTest
+
+/** This is also used in the typedarray package to test scala.Arrays backed
+ * by TypedArrays
+ */
+trait ArraysTest extends JasmineTest {
+
+ // Just in here, we allow ourselves to do this
+ implicit def array2jsArray[T](arr: Array[T]): js.Array[T] = arr.toJSArray
+
+ /** Overridden by typedarray tests */
+ def Array[T : ClassTag](v: T*): scala.Array[T] = scala.Array(v: _*)
+
+ /** Overridden by typedarray tests */
+ def testBody(suite: => Unit) = describe("java.util.Arrays")(suite)
+
+ val stringComparator = new Comparator[String]() {
+ def compare(s1: String, s2: String) = s1.compareTo(s2)
+ }
+
+ val intComparator = new Comparator[Int]() {
+ def compare(i1: Int, i2: Int) = i1 - i2
+ }
+
+ testBody {
+
+ it("should respond to `sort` for Int") {
+ val scalaInts = Array(5, 3, 6, 1, 2, 4)
+ val ints = new Array[Object](scalaInts.length)
+ for (i <- 0 until scalaInts.length)
+ ints(i) = scalaInts(i).asInstanceOf[Object]
+ val sorted = Array(1, 2, 3, 4, 5, 6)
+
+ Arrays.sort(ints, intComparator.asInstanceOf[Comparator[Object]])
+ expect(ints).toEqual(Array(1, 2, 3, 4, 5, 6))
+ }
+
+ it("should respond to `sort` for String") {
+ val scalajs: Array[Object] = Array("S", "c", "a", "l", "a", ".", "j", "s")
+ val sorted = Array(".", "S", "a", "a", "c", "j", "l", "s")
+
+ Arrays.sort(scalajs, stringComparator.asInstanceOf[Comparator[Object]])
+ expect(scalajs).toEqual(sorted)
+ }
+
+ it("should respond to `fill` for Boolean") {
+ val booleans = new Array[Boolean](6)
+ Arrays.fill(booleans, false)
+ expect(booleans).toEqual(Array(false, false, false, false, false, false))
+
+ Arrays.fill(booleans, true)
+ expect(booleans).toEqual(Array(true, true, true, true, true, true))
+ }
+
+ it("should respond to `fill` with start and end index for Boolean") {
+ val booleans = new Array[Boolean](6)
+ Arrays.fill(booleans, 1, 4, true)
+ expect(booleans).toEqual(Array(false, true, true, true, false, false))
+ }
+
+ it("should respond to `fill` for Byte") {
+ val bytes = new Array[Byte](6)
+ Arrays.fill(bytes, 42.toByte)
+ expect(bytes).toEqual(Array[Byte](42, 42, 42, 42, 42, 42))
+
+ Arrays.fill(bytes, -1.toByte)
+ expect(bytes).toEqual(Array[Byte](-1, -1, -1, -1, -1, -1))
+ }
+
+ it("should respond to `fill` with start and end index for Byte") {
+ val bytes = new Array[Byte](6)
+ Arrays.fill(bytes, 1, 4, 42.toByte)
+ expect(bytes).toEqual(Array[Byte](0, 42, 42, 42, 0, 0))
+
+ Arrays.fill(bytes, 2, 5, -1.toByte)
+ expect(bytes).toEqual(Array[Byte](0, 42, -1, -1, -1, 0))
+ }
+
+ it("should respond to `fill` for Short") {
+ val shorts = new Array[Short](6)
+ Arrays.fill(shorts, 42.toShort)
+ expect(shorts).toEqual(Array[Short](42, 42, 42, 42, 42, 42))
+
+ Arrays.fill(shorts, -1.toShort)
+ expect(shorts).toEqual(Array[Short](-1, -1, -1, -1, -1, -1))
+ }
+
+ it("should respond to `fill` with start and end index for Short") {
+ val shorts = new Array[Short](6)
+ Arrays.fill(shorts, 1, 4, 42.toShort)
+ expect(shorts).toEqual(Array[Short](0, 42, 42, 42, 0, 0))
+
+ Arrays.fill(shorts, 2, 5, -1.toShort)
+ expect(shorts).toEqual(Array[Short](0, 42, -1, -1, -1, 0))
+ }
+
+ it("should respond to `fill` for Int") {
+ val ints = new Array[Int](6)
+ Arrays.fill(ints, 42)
+ expect(ints).toEqual(Array(42, 42, 42, 42, 42, 42))
+
+ Arrays.fill(ints, -1)
+ expect(ints).toEqual(Array(-1, -1, -1, -1, -1, -1))
+ }
+
+ it("should respond to `fill` with start and end index for Int") {
+ val ints = new Array[Int](6)
+ Arrays.fill(ints, 1, 4, 42)
+ expect(ints).toEqual(Array(0, 42, 42, 42, 0, 0))
+
+ Arrays.fill(ints, 2, 5, -1)
+ expect(ints).toEqual(Array(0, 42, -1, -1, -1, 0))
+ }
+
+ it("should respond to `fill` for Long") {
+ val longs = new Array[Long](6)
+ Arrays.fill(longs, 42L)
+ expect(longs).toEqual(Array(42L, 42L, 42L, 42L, 42L, 42L))
+
+ Arrays.fill(longs, -1L)
+ expect(longs).toEqual(Array(-1L, -1L, -1L, -1L, -1L, -1L))
+ }
+
+ it("should respond to `fill` with start and end index for Long") {
+ val longs = new Array[Long](6)
+ Arrays.fill(longs, 1, 4, 42L)
+ expect(longs).toEqual(Array(0L, 42L, 42L, 42L, 0L, 0L))
+
+ Arrays.fill(longs, 2, 5, -1L)
+ expect(longs).toEqual(Array(0L, 42L, -1L, -1L, -1L, 0L))
+ }
+
+ it("should respond to `fill` for Float") {
+ val floats = new Array[Float](6)
+ Arrays.fill(floats, 42.0f)
+ expect(floats).toEqual(Array(42.0f, 42.0f, 42.0f, 42.0f, 42.0f, 42.0f))
+
+ Arrays.fill(floats, -1.0f)
+ expect(floats).toEqual(Array(-1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f))
+ }
+
+ it("should respond to `fill` with start and end index for Float") {
+ val floats = new Array[Float](6)
+ Arrays.fill(floats, 1, 4, 42.0f)
+ expect(floats).toEqual(Array(0.0f, 42.0f, 42.0f, 42.0f, 0.0f, 0.0f))
+
+ Arrays.fill(floats, 2, 5, -1.0f)
+ expect(floats).toEqual(Array(0.0f, 42.0f, -1.0f, -1.0f, -1.0f, 0.0f))
+ }
+
+ it("should respond to `fill` for Double") {
+ val doubles = new Array[Double](6)
+ Arrays.fill(doubles, 42.0)
+ expect(doubles).toEqual(Array(42.0, 42.0, 42.0, 42.0, 42.0, 42.0))
+
+ Arrays.fill(doubles, -1.0f)
+ expect(doubles).toEqual(Array(-1.0, -1.0, -1.0, -1.0, -1.0, -1.0))
+ }
+
+ it("should respond to `fill` with start and end index for Double") {
+ val doubles = new Array[Double](6)
+ Arrays.fill(doubles, 1, 4, 42.0)
+ expect(doubles).toEqual(Array(0.0, 42.0, 42.0, 42.0, 0.0, 0.0))
+
+ Arrays.fill(doubles, 2, 5, -1.0)
+ expect(doubles).toEqual(Array(0.0, 42.0, -1.0, -1.0, -1.0, 0.0))
+ }
+
+ it("should respond to `fill` for AnyRef") {
+ val array = new Array[AnyRef](6)
+ Arrays.fill(array, "a")
+ expect(array).toEqual(Array[AnyRef]("a", "a", "a", "a", "a", "a"))
+
+ Arrays.fill(array, "b")
+ expect(array).toEqual(Array[AnyRef]("b", "b", "b", "b", "b", "b"))
+ }
+
+ it("should respond to `fill` with start and end index for AnyRef") {
+ val bytes = new Array[AnyRef](6)
+ Arrays.fill(bytes, 1, 4, "a")
+ expect(bytes).toEqual(Array[AnyRef](null, "a", "a", "a", null, null))
+
+ Arrays.fill(bytes, 2, 5, "b")
+ expect(bytes).toEqual(Array[AnyRef](null, "a", "b", "b", "b", null))
+ }
+
+ it("should respond to `binarySearch` with start index, end index and key for Long") {
+ val longs: Array[Long] = Array(1, 2, 3, 5, 6, 7)
+ var ret = Arrays.binarySearch(longs, 0, 6, 5)
+ expect(ret).toEqual(3)
+
+ ret = Arrays.binarySearch(longs, 0, 6, 0)
+ expect(ret).toEqual(-1)
+
+ ret = Arrays.binarySearch(longs, 0, 6, 4)
+ expect(ret).toEqual(-4)
+
+ ret = Arrays.binarySearch(longs, 0, 6, 8)
+ expect(ret).toEqual(-7)
+ }
+
+ it("should respond to `binarySearch` with key for Long") {
+ val longs: Array[Long] = Array(1, 2, 3, 5, 6, 7)
+ var ret = Arrays.binarySearch(longs, 5)
+ expect(ret).toEqual(3)
+
+ ret = Arrays.binarySearch(longs, 0)
+ expect(ret).toEqual(-1)
+
+ ret = Arrays.binarySearch(longs, 4)
+ expect(ret).toEqual(-4)
+
+ ret = Arrays.binarySearch(longs, 8)
+ expect(ret).toEqual(-7)
+ }
+
+ it("should respond to `binarySearch` with start index, end index and key for Int") {
+ val ints: Array[Int] = Array(1, 2, 3, 5, 6, 7)
+ var ret = Arrays.binarySearch(ints, 0, 6, 5)
+ expect(ret).toEqual(3)
+
+ ret = Arrays.binarySearch(ints, 0, 6, 0)
+ expect(ret).toEqual(-1)
+
+ ret = Arrays.binarySearch(ints, 0, 6, 4)
+ expect(ret).toEqual(-4)
+
+ ret = Arrays.binarySearch(ints, 0, 6, 8)
+ expect(ret).toEqual(-7)
+ }
+
+ it("should respond to `binarySearch` with key for Int") {
+ val ints: Array[Int] = Array(1, 2, 3, 5, 6, 7)
+ var ret = Arrays.binarySearch(ints, 5)
+ expect(ret).toEqual(3)
+
+ ret = Arrays.binarySearch(ints, 0)
+ expect(ret).toEqual(-1)
+
+ ret = Arrays.binarySearch(ints, 4)
+ expect(ret).toEqual(-4)
+
+ ret = Arrays.binarySearch(ints, 8)
+ expect(ret).toEqual(-7)
+ }
+
+ it("should respond to `binarySearch` with start index, end index and key for Short") {
+ val shorts: Array[Short] = Array(1, 2, 3, 5, 6, 7)
+ var ret = Arrays.binarySearch(shorts, 0, 6, 5.toShort)
+ expect(ret).toEqual(3)
+
+ ret = Arrays.binarySearch(shorts, 0, 6, 0.toShort)
+ expect(ret).toEqual(-1)
+
+ ret = Arrays.binarySearch(shorts, 0, 6, 4.toShort)
+ expect(ret).toEqual(-4)
+
+ ret = Arrays.binarySearch(shorts, 0, 6, 8.toShort)
+ expect(ret).toEqual(-7)
+ }
+
+ it("should respond to `binarySearch` with key for Short") {
+ val shorts: Array[Short] = Array(1, 2, 3, 5, 6, 7)
+ var ret = Arrays.binarySearch(shorts, 5.toShort)
+ expect(ret).toEqual(3)
+
+ ret = Arrays.binarySearch(shorts, 0.toShort)
+ expect(ret).toEqual(-1)
+
+ ret = Arrays.binarySearch(shorts, 4.toShort)
+ expect(ret).toEqual(-4)
+
+ ret = Arrays.binarySearch(shorts, 8.toShort)
+ expect(ret).toEqual(-7)
+ }
+
+ it("should respond to `binarySearch` with start index, end index and key for Char") {
+ val chars: Array[Char] = Array('b', 'c', 'd', 'f', 'g', 'h')
+ var ret = Arrays.binarySearch(chars, 0, 6, 'f')
+ expect(ret).toEqual(3)
+
+ ret = Arrays.binarySearch(chars, 0, 6, 'a')
+ expect(ret).toEqual(-1)
+
+ ret = Arrays.binarySearch(chars, 0, 6, 'e')
+ expect(ret).toEqual(-4)
+
+ ret = Arrays.binarySearch(chars, 0, 6, 'i')
+ expect(ret).toEqual(-7)
+ }
+
+ it("should respond to `binarySearch` with key for Char") {
+ val chars: Array[Char] = Array('b', 'c', 'd', 'f', 'g', 'h')
+ var ret = Arrays.binarySearch(chars, 'f')
+ expect(ret).toEqual(3)
+
+ ret = Arrays.binarySearch(chars, 'a')
+ expect(ret).toEqual(-1)
+
+ ret = Arrays.binarySearch(chars, 'e')
+ expect(ret).toEqual(-4)
+
+ ret = Arrays.binarySearch(chars, 'i')
+ expect(ret).toEqual(-7)
+ }
+
+ it("should respond to `binarySearch` with start index, end index and key for Double") {
+ val doubles: Array[Double] = Array(0.1, 0.2, 0.3, 0.5, 0.6, 0.7)
+ var ret = Arrays.binarySearch(doubles, 0, 6, 0.5)
+ expect(ret).toEqual(3)
+
+ ret = Arrays.binarySearch(doubles, 0, 6, 0.0)
+ expect(ret).toEqual(-1)
+
+ ret = Arrays.binarySearch(doubles, 0, 6, 0.4)
+ expect(ret).toEqual(-4)
+
+ ret = Arrays.binarySearch(doubles, 0, 6, 0.8)
+ expect(ret).toEqual(-7)
+ }
+
+ it("should respond to `binarySearch` with key for Double") {
+ val doubles: Array[Double] = Array(0.1, 0.2, 0.3, 0.5, 0.6, 0.7)
+ var ret = Arrays.binarySearch(doubles, 0.5)
+ expect(ret).toEqual(3)
+
+ ret = Arrays.binarySearch(doubles, 0.0)
+ expect(ret).toEqual(-1)
+
+ ret = Arrays.binarySearch(doubles, 0.4)
+ expect(ret).toEqual(-4)
+
+ ret = Arrays.binarySearch(doubles, 0.8)
+ expect(ret).toEqual(-7)
+ }
+
+ it("should respond to `binarySearch` with start index, end index and key for Float") {
+ val floats: Array[Float] = Array(0.1f, 0.2f, 0.3f, 0.5f, 0.6f, 0.7f)
+ var ret = Arrays.binarySearch(floats, 0, 6, 0.5f)
+ expect(ret).toEqual(3)
+
+ ret = Arrays.binarySearch(floats, 0, 6, 0.0f)
+ expect(ret).toEqual(-1)
+
+ ret = Arrays.binarySearch(floats, 0, 6, 0.4f)
+ expect(ret).toEqual(-4)
+
+ ret = Arrays.binarySearch(floats, 0, 6, 0.8f)
+ expect(ret).toEqual(-7)
+ }
+
+ it("should respond to `binarySearch` with key for Float") {
+ val floats: Array[Float] = Array(0.1f, 0.2f, 0.3f, 0.5f, 0.6f, 0.7f)
+ var ret = Arrays.binarySearch(floats, 0.5f)
+ expect(ret).toEqual(3)
+
+ ret = Arrays.binarySearch(floats, 0.0f)
+ expect(ret).toEqual(-1)
+
+ ret = Arrays.binarySearch(floats, 0.4f)
+ expect(ret).toEqual(-4)
+
+ ret = Arrays.binarySearch(floats, 0.8f)
+ expect(ret).toEqual(-7)
+ }
+
+ it("should respond to `binarySearch` with start index, end index and key for AnyRef") {
+ val strings: Array[AnyRef] = Array("aa", "abc", "cc", "zz", "zzzs", "zzzt")
+ var ret = Arrays.binarySearch(strings, 0, 6, "zz")
+ expect(ret).toEqual(3)
+
+ ret = Arrays.binarySearch(strings, 0, 6, "a")
+ expect(ret).toEqual(-1)
+
+ ret = Arrays.binarySearch(strings, 0, 6, "cd")
+ expect(ret).toEqual(-4)
+
+ ret = Arrays.binarySearch(strings, 0, 6, "zzzz")
+ expect(ret).toEqual(-7)
+ }
+
+ it("should respond to `binarySearch` with key for AnyRef") {
+ val strings: Array[AnyRef] = Array("aa", "abc", "cc", "zz", "zzzs", "zzzt")
+ var ret = Arrays.binarySearch(strings, "zz")
+ expect(ret).toEqual(3)
+
+ ret = Arrays.binarySearch(strings, "a")
+ expect(ret).toEqual(-1)
+
+ ret = Arrays.binarySearch(strings, "cd")
+ expect(ret).toEqual(-4)
+
+ ret = Arrays.binarySearch(strings, "zzzz")
+ expect(ret).toEqual(-7)
+ }
+
+ it("should check ranges of input to `binarySearch`") {
+ def expectException(block: => Unit)(expected: PartialFunction[Throwable, Unit]): Unit = {
+ val catchAll: PartialFunction[Throwable, Unit] = {
+ case e: Throwable => expect(e.getClass.getName).toBe("not thrown")
+ }
+
+ try {
+ block
+ expect("exception").toBe("thrown")
+ } catch expected orElse catchAll
+ }
+
+ val array = Array(0, 1, 3, 4)
+
+ expectException({ Arrays.binarySearch(array, 3, 2, 2) }) {
+ case exception: IllegalArgumentException =>
+ expect(exception.getMessage).toBe("fromIndex(3) > toIndex(2)")
+ }
+
+ // start/end comparison is made before index ranges checks
+ expectException({ Arrays.binarySearch(array, 7, 5, 2) }) {
+ case exception: IllegalArgumentException =>
+ expect(exception.getMessage).toBe("fromIndex(7) > toIndex(5)")
+ }
+
+ expectException({ Arrays.binarySearch(array, -1, 4, 2) }) {
+ case exception: ArrayIndexOutOfBoundsException =>
+ expect(exception.getMessage).toBe("Array index out of range: -1")
+ }
+
+ expectException({ Arrays.binarySearch(array, 0, 5, 2) }) {
+ case exception: ArrayIndexOutOfBoundsException =>
+ expect(exception.getMessage).toBe("Array index out of range: 5")
+ }
+ }
+
+ it("should respond to `copyOf` with key for Int") {
+ val ints: Array[Int] = Array(1, 2, 3)
+ val intscopy = Arrays.copyOf(ints, 5)
+ expect(intscopy).toEqual(Array(1, 2, 3, 0, 0))
+ }
+
+ it("should respond to `copyOf` with key for Long") {
+ val longs: Array[Long] = Array(1, 2, 3)
+ val longscopy = Arrays.copyOf(longs, 5)
+ expect(longscopy).toEqual(Array[Long](1, 2, 3, 0, 0))
+ }
+
+ it("should respond to `copyOf` with key for Short") {
+ val shorts: Array[Short] = Array(1, 2, 3)
+ val shortscopy = Arrays.copyOf(shorts, 5)
+ expect(shortscopy).toEqual(Array[Short](1, 2, 3, 0, 0))
+ }
+
+ it("should respond to `copyOf` with key for Byte") {
+ val bytes: Array[Byte] = Array(42, 43, 44)
+ val floatscopy = Arrays.copyOf(bytes, 5)
+ expect(floatscopy).toEqual(Array[Byte](42, 43, 44, 0, 0))
+ }
+
+ it("should respond to `copyOf` with key for Char") {
+ val chars: Array[Char] = Array('a', 'b', '0')
+ val charscopy = Arrays.copyOf(chars, 5)
+ expect(charscopy(4)).toEqual(0.toChar)
+ }
+
+ it("should respond to `copyOf` with key for Double") {
+ val doubles: Array[Double] = Array(0.1, 0.2, 0.3)
+ val doublescopy = Arrays.copyOf(doubles, 5)
+ expect(doublescopy).toEqual(Array[Double](0.1, 0.2, 0.3, 0, 0))
+ }
+
+ it("should respond to `copyOf` with key for Float") {
+ val floats: Array[Float] = Array(0.1f, 0.2f, 0.3f)
+ val floatscopy = Arrays.copyOf(floats, 5)
+ expect(floatscopy).toEqual(Array[Float](0.1f, 0.2f, 0.3f, 0f, 0f))
+ }
+
+ it("should respond to `copyOf` with key for Boolean") {
+ val bools: Array[Boolean] = Array(false, true, false)
+ val boolscopy = Arrays.copyOf(bools, 5)
+ expect(boolscopy).toEqual(Array[Boolean](false, true, false, false, false))
+ }
+
+ it("should respond to `copyOf` with key for AnyRef") {
+ val anyrefs: Array[AnyRef] = Array("a", "b", "c")
+ val anyrefscopy = Arrays.copyOf(anyrefs, 5)
+ expect(anyrefscopy.getClass() == classOf[Array[AnyRef]]).toBeTruthy
+ expect(anyrefscopy).toEqual(Array[AnyRef]("a", "b", "c", null, null))
+
+ val sequences: Array[CharSequence] = Array("a", "b", "c")
+ val sequencescopy = Arrays.copyOf(sequences, 2)
+ expect(sequencescopy.getClass() == classOf[Array[CharSequence]])
+ expect(sequencescopy).toEqual(Array[CharSequence]("a", "b"))
+ }
+
+ it("should respond to `copyOfRange` for AnyRef") {
+ val anyrefs: Array[AnyRef] = Array("a", "b", "c", "d", "e")
+ val anyrefscopy = Arrays.copyOfRange(anyrefs, 2, 4)
+ expect(anyrefscopy.getClass() == classOf[Array[AnyRef]]).toBeTruthy
+ expect(anyrefscopy).toEqual(Array[AnyRef]("c", "d"))
+
+ val sequences: Array[CharSequence] = Array("a", "b", "c", "d", "e")
+ val sequencescopy = Arrays.copyOfRange(sequences, 1, 5)
+ expect(sequencescopy.getClass() == classOf[Array[CharSequence]])
+ expect(sequencescopy).toEqual(Array[CharSequence]("b", "c", "d", "e"))
+ }
+
+ it("should respond to `hashCode` for Boolean") {
+ expect(Arrays.hashCode(null: Array[Boolean])).toEqual(0)
+ expect(Arrays.hashCode(Array[Boolean]())).toEqual(1)
+ expect(Arrays.hashCode(Array[Boolean](false))).toEqual(1268)
+ expect(Arrays.hashCode(Array[Boolean](true, false))).toEqual(40359)
+ }
+
+ it("should respond to `hashCode` for Chars") {
+ expect(Arrays.hashCode(null: Array[Char])).toEqual(0)
+ expect(Arrays.hashCode(Array[Char]())).toEqual(1)
+ expect(Arrays.hashCode(Array[Char]('a'))).toEqual(128)
+ expect(Arrays.hashCode(Array[Char]('c', '&'))).toEqual(4068)
+ expect(Arrays.hashCode(Array[Char]('-', '5', 'q'))).toEqual(74792)
+ expect(Arrays.hashCode(Array[Char]('.', ' ', '\u4323', 'v', '~'))).toEqual(88584920)
+ }
+
+ it("should respond to `hashCode` for Bytes") {
+ expect(Arrays.hashCode(null: Array[Byte])).toEqual(0)
+ expect(Arrays.hashCode(Array[Byte]())).toEqual(1)
+ expect(Arrays.hashCode(Array[Byte](1))).toEqual(32)
+ expect(Arrays.hashCode(Array[Byte](7, -125))).toEqual(1053)
+ expect(Arrays.hashCode(Array[Byte](3, 0, 45))).toEqual(32719)
+ expect(Arrays.hashCode(Array[Byte](0, 45, 100, 1, 1))).toEqual(30065878)
+ }
+
+ it("should respond to `hashCode` for Shorts") {
+ expect(Arrays.hashCode(null: Array[Short])).toEqual(0)
+ expect(Arrays.hashCode(Array[Short]())).toEqual(1)
+ expect(Arrays.hashCode(Array[Short](1))).toEqual(32)
+ expect(Arrays.hashCode(Array[Short](7, -125))).toEqual(1053)
+ expect(Arrays.hashCode(Array[Short](3, 0, 4534))).toEqual(37208)
+ expect(Arrays.hashCode(Array[Short](0, 45, 100, 1, 1))).toEqual(30065878)
+ }
+
+ it("should respond to `hashCode` for Ints") {
+ expect(Arrays.hashCode(null: Array[Int])).toEqual(0)
+ expect(Arrays.hashCode(Array[Int]())).toEqual(1)
+ expect(Arrays.hashCode(Array[Int](1))).toEqual(32)
+ expect(Arrays.hashCode(Array[Int](7, -125))).toEqual(1053)
+ expect(Arrays.hashCode(Array[Int](3, 0, 4534))).toEqual(37208)
+ expect(Arrays.hashCode(Array[Int](0, 45, 100, 1, 1, Int.MaxValue))).toEqual(-1215441431)
+ }
+
+ it("should respond to `hashCode` for Longs") {
+ expect(Arrays.hashCode(null: Array[Long])).toEqual(0)
+ expect(Arrays.hashCode(Array[Long]())).toEqual(1)
+ expect(Arrays.hashCode(Array[Long](1L))).toEqual(32)
+ expect(Arrays.hashCode(Array[Long](7L, -125L))).toEqual(1302)
+ expect(Arrays.hashCode(Array[Long](3L, 0L, 4534L))).toEqual(37208)
+ expect(Arrays.hashCode(Array[Long](0L, 45L, 100L, 1L, 1L, Int.MaxValue))).toEqual(-1215441431)
+ expect(Arrays.hashCode(Array[Long](0L, 34573566354545L, 100L, 1L, 1L, Int.MaxValue))).toEqual(-1952288964)
+ }
+
+ it("should respond to `hashCode` for Floats") {
+ expect(Arrays.hashCode(null: Array[Float])).toEqual(0)
+ expect(Arrays.hashCode(Array[Float]())).toEqual(1)
+ expect(Arrays.hashCode(Array[Float](1f))).toEqual(32)
+ expect(Arrays.hashCode(Array[Float](7.2f, -125.2f))).toEqual(-2082726591)
+ expect(Arrays.hashCode(Array[Float](302.1f, 0.0f, 4534f))).toEqual(-1891539602)
+ expect(Arrays.hashCode(Array[Float](0.0f, 45f, -100f, 1.1f, -1f, 3567f))).toEqual(-1591440133)
+ }
+
+ it("should respond to `hashCode` for Doubles") {
+ expect(Arrays.hashCode(null: Array[Double])).toEqual(0)
+ expect(Arrays.hashCode(Array[Double]())).toEqual(1)
+ expect(Arrays.hashCode(Array[Double](1.1))).toEqual(-1503133662)
+ expect(Arrays.hashCode(Array[Double](7.3, -125.23))).toEqual(-2075734168)
+ expect(Arrays.hashCode(Array[Double](3.9, 0.2, 4534.9))).toEqual(-557562564)
+ expect(Arrays.hashCode(Array[Double](0.1, 45.1, -100.0, 1.1, 1.7))).toEqual(-1750344582)
+ expect(Arrays.hashCode(Array[Double](0.0, 34573566354545.9, 100.2, 1.1, 1.2, Int.MaxValue))).toEqual(-1764602991)
+ }
+
+ it("should respond to `hashCode` for AnyRef") {
+ expect(Arrays.hashCode(null: Array[AnyRef])).toEqual(0)
+ expect(Arrays.hashCode(Array[AnyRef]())).toEqual(1)
+ expect(Arrays.hashCode(Array[AnyRef](null, null))).toEqual(961)
+ expect(Arrays.hashCode(Array[AnyRef]("a", "b", null))).toEqual(126046)
+ expect(Arrays.hashCode(Array[AnyRef](null, "a", "b", null, "fooooo"))).toEqual(-1237252983)
+ }
+
+ it("should respond to `equals` for Booleans") {
+ val a1 = Array(true, false)
+
+ expect(Arrays.equals(a1, a1)).toBeTruthy
+ expect(Arrays.equals(a1, Array(true, false))).toBeTruthy
+
+ expect(Arrays.equals(a1, Array(true))).toBeFalsy
+ expect(Arrays.equals(a1, Array(false))).toBeFalsy
+ expect(Arrays.equals(a1, Array[Boolean]())).toBeFalsy
+ expect(Arrays.equals(a1, Array(false, true))).toBeFalsy
+ expect(Arrays.equals(a1, Array(false, true, false))).toBeFalsy
+ }
+
+ it("should respond to `equals` for Bytes") {
+ val a1 = Array[Byte](1, -7, 10)
+
+ expect(Arrays.equals(null: Array[Byte], null: Array[Byte])).toBeTruthy
+ expect(Arrays.equals(a1, a1)).toBeTruthy
+ expect(Arrays.equals(a1, Array[Byte](1, -7, 10))).toBeTruthy
+
+ expect(Arrays.equals(a1, null)).toBeFalsy
+ expect(Arrays.equals(a1, Array[Byte](3))).toBeFalsy
+ expect(Arrays.equals(a1, Array[Byte](1))).toBeFalsy
+ expect(Arrays.equals(a1, Array[Byte]())).toBeFalsy
+ expect(Arrays.equals(a1, Array[Byte](1, -7, 11))).toBeFalsy
+ expect(Arrays.equals(a1, Array[Byte](1, -7, 11, 20))).toBeFalsy
+ }
+
+ it("should respond to `equals` for Chars") {
+ val a1 = Array[Char]('a', '0', '-')
+
+ expect(Arrays.equals(null: Array[Char], null: Array[Char])).toBeTruthy
+ expect(Arrays.equals(a1, a1)).toBeTruthy
+ expect(Arrays.equals(a1, Array[Char]('a', '0', '-'))).toBeTruthy
+
+ expect(Arrays.equals(a1, null)).toBeFalsy
+ expect(Arrays.equals(a1, Array[Char]('z'))).toBeFalsy
+ expect(Arrays.equals(a1, Array[Char]('a'))).toBeFalsy
+ expect(Arrays.equals(a1, Array[Char]())).toBeFalsy
+ expect(Arrays.equals(a1, Array[Char]('a', '0', '+'))).toBeFalsy
+ expect(Arrays.equals(a1, Array[Char]('a', '0', '-', 'z'))).toBeFalsy
+ }
+
+ it("should respond to `equals` for Shorts") {
+ val a1 = Array[Short](1, -7, 10)
+
+ expect(Arrays.equals(null: Array[Short], null: Array[Short])).toBeTruthy
+ expect(Arrays.equals(a1, a1)).toBeTruthy
+ expect(Arrays.equals(a1, Array[Short](1, -7, 10))).toBeTruthy
+
+ expect(Arrays.equals(a1, null)).toBeFalsy
+ expect(Arrays.equals(a1, Array[Short](3))).toBeFalsy
+ expect(Arrays.equals(a1, Array[Short](1))).toBeFalsy
+ expect(Arrays.equals(a1, Array[Short]())).toBeFalsy
+ expect(Arrays.equals(a1, Array[Short](1, -7, 11))).toBeFalsy
+ expect(Arrays.equals(a1, Array[Short](1, -7, 11, 20))).toBeFalsy
+ }
+
+ it("should respond to `equals` for Ints") {
+ val a1 = Array[Int](1, -7, 10)
+
+ expect(Arrays.equals(null: Array[Int], null: Array[Int])).toBeTruthy
+ expect(Arrays.equals(a1, a1)).toBeTruthy
+ expect(Arrays.equals(a1, Array[Int](1, -7, 10))).toBeTruthy
+
+ expect(Arrays.equals(a1, null)).toBeFalsy
+ expect(Arrays.equals(a1, Array[Int](3))).toBeFalsy
+ expect(Arrays.equals(a1, Array[Int](1))).toBeFalsy
+ expect(Arrays.equals(a1, Array[Int]())).toBeFalsy
+ expect(Arrays.equals(a1, Array[Int](1, -7, 11))).toBeFalsy
+ expect(Arrays.equals(a1, Array[Int](1, -7, 11, 20))).toBeFalsy
+ }
+
+ it("should respond to `equals` for Longs") {
+ val a1 = Array[Long](1L, -7L, 10L)
+
+ expect(Arrays.equals(null: Array[Long], null: Array[Long])).toBeTruthy
+ expect(Arrays.equals(a1, a1)).toBeTruthy
+ expect(Arrays.equals(a1, Array[Long](1L, -7L, 10L))).toBeTruthy
+
+ expect(Arrays.equals(a1, null)).toBeFalsy
+ expect(Arrays.equals(a1, Array[Long](3L))).toBeFalsy
+ expect(Arrays.equals(a1, Array[Long](1L))).toBeFalsy
+ expect(Arrays.equals(a1, Array[Long]())).toBeFalsy
+ expect(Arrays.equals(a1, Array[Long](1L, -7L, 11L))).toBeFalsy
+ expect(Arrays.equals(a1, Array[Long](1L, -7L, 11L, 20L))).toBeFalsy
+ }
+
+ it("should respond to `equals` for Floats") {
+ val a1 = Array[Float](1.1f, -7.4f, 10.0f)
+
+ expect(Arrays.equals(null: Array[Float], null: Array[Float])).toBeTruthy
+ expect(Arrays.equals(a1, a1)).toBeTruthy
+ expect(Arrays.equals(a1, Array[Float](1.1f, -7.4f, 10.0f))).toBeTruthy
+
+ expect(Arrays.equals(a1, null)).toBeFalsy
+ expect(Arrays.equals(a1, Array[Float](3.0f))).toBeFalsy
+ expect(Arrays.equals(a1, Array[Float](1.1f))).toBeFalsy
+ expect(Arrays.equals(a1, Array[Float]())).toBeFalsy
+ expect(Arrays.equals(a1, Array[Float](1.1f, -7.4f, 11.0f))).toBeFalsy
+ expect(Arrays.equals(a1, Array[Float](1.1f, -7.4f, 10.0f, 20.0f))).toBeFalsy
+ }
+
+ it("should respond to `equals` for Doubles") {
+ val a1 = Array[Double](1.1, -7.4, 10.0)
+
+ expect(Arrays.equals(null: Array[Double], null: Array[Double])).toBeTruthy
+ expect(Arrays.equals(a1, a1)).toBeTruthy
+ expect(Arrays.equals(a1, Array[Double](1.1, -7.4, 10.0))).toBeTruthy
+
+ expect(Arrays.equals(a1, null)).toBeFalsy
+ expect(Arrays.equals(a1, Array[Double](3.0))).toBeFalsy
+ expect(Arrays.equals(a1, Array[Double](1.1))).toBeFalsy
+ expect(Arrays.equals(a1, Array[Double]())).toBeFalsy
+ expect(Arrays.equals(a1, Array[Double](1.1, -7.4, 11.0))).toBeFalsy
+ expect(Arrays.equals(a1, Array[Double](1.1, -7.4, 10.0, 20.0))).toBeFalsy
+ }
+
+ it("should respond to `equals` for AnyRefs") {
+ class A(private val x: Int) {
+ override def equals(that: Any) = that match {
+ case that: A => this.x == that.x
+ case _ => false
+ }
+ }
+
+ def A(x: Int) = new A(x)
+
+ val a1 = Array[AnyRef](A(1), A(-7), A(10))
+
+ expect(Arrays.equals(null: Array[AnyRef], null: Array[AnyRef])).toBeTruthy
+ expect(Arrays.equals(a1, a1)).toBeTruthy
+ expect(Arrays.equals(a1, Array[AnyRef](A(1), A(-7), A(10)))).toBeTruthy
+
+ expect(Arrays.equals(a1, null)).toBeFalsy
+ expect(Arrays.equals(a1, Array[AnyRef](A(3)))).toBeFalsy
+ expect(Arrays.equals(a1, Array[AnyRef](A(1)))).toBeFalsy
+ expect(Arrays.equals(a1, Array[AnyRef]())).toBeFalsy
+ expect(Arrays.equals(a1, Array[AnyRef](A(1), null, A(11)))).toBeFalsy
+ expect(Arrays.equals(a1, Array[AnyRef](A(1), A(-7), A(11), A(20)))).toBeFalsy
+ }
+
+ }
+
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/AtomicTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/AtomicTest.scala
new file mode 100644
index 0000000..c4f065a
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/AtomicTest.scala
@@ -0,0 +1,119 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.javalib
+
+import scala.language.implicitConversions
+
+import scala.scalajs.js
+
+import org.scalajs.jasminetest.JasmineTest
+
+object AtomicTest extends JasmineTest {
+
+ describe("java.util.concurrent.AtomicLong") {
+
+ it("Should have all the normal operations") {
+ val atomic = new java.util.concurrent.atomic.AtomicLong(10)
+ expect(atomic.get()).toEqual(10)
+ atomic.set(20)
+ expect(atomic.get()).toEqual(20)
+ expect(atomic.getAndIncrement()).toEqual(20)
+ expect(atomic.get()).toEqual(21)
+ expect(atomic.getAndDecrement()).toEqual(21)
+ expect(atomic.get()).toEqual(20)
+ expect(atomic.getAndSet(0)).toEqual(20)
+ expect(atomic.get()).toEqual(0)
+ expect(atomic.incrementAndGet()).toEqual(1)
+ expect(atomic.get()).toEqual(1)
+ expect(atomic.decrementAndGet()).toEqual(0)
+ expect(atomic.get()).toEqual(0)
+ expect(atomic.addAndGet(10)).toEqual(10)
+ expect(atomic.get()).toEqual(10)
+ expect(atomic.intValue).toEqual(10)
+ expect(atomic.longValue).toEqual(10L)
+ expect(atomic.floatValue).toEqual(10.0f)
+ expect(atomic.doubleValue).toEqual(10)
+ expect(atomic.compareAndSet(1, 20)).toEqual(false)
+ expect(atomic.get()).toEqual(10)
+ expect(atomic.compareAndSet(10, 20)).toEqual(true)
+ expect(atomic.get()).toEqual(20)
+ }
+ }
+ describe("java.util.concurrent.AtomicInteger") {
+ it("Should have all the normal operations") {
+ val atomic = new java.util.concurrent.atomic.AtomicInteger(10)
+ expect(atomic.get()).toEqual(10)
+ atomic.set(20)
+ expect(atomic.get()).toEqual(20)
+ expect(atomic.getAndIncrement()).toEqual(20)
+ expect(atomic.get()).toEqual(21)
+ expect(atomic.getAndDecrement()).toEqual(21)
+ expect(atomic.get()).toEqual(20)
+ expect(atomic.getAndSet(0)).toEqual(20)
+ expect(atomic.get()).toEqual(0)
+ expect(atomic.incrementAndGet()).toEqual(1)
+ expect(atomic.get()).toEqual(1)
+ expect(atomic.decrementAndGet()).toEqual(0)
+ expect(atomic.get()).toEqual(0)
+ expect(atomic.addAndGet(10)).toEqual(10)
+ expect(atomic.get()).toEqual(10)
+ expect(atomic.intValue).toEqual(10)
+ expect(atomic.longValue).toEqual(10L)
+ expect(atomic.floatValue).toEqual(10.0f)
+ expect(atomic.doubleValue).toEqual(10)
+ expect(atomic.compareAndSet(1, 20)).toEqual(false)
+ expect(atomic.get()).toEqual(10)
+ expect(atomic.compareAndSet(10, 20)).toEqual(true)
+ expect(atomic.get()).toEqual(20)
+ }
+ }
+ describe("java.util.concurrent.AtomicBoolean") {
+ it("Should have all the normal operations") {
+ val atomic = new java.util.concurrent.atomic.AtomicBoolean(true)
+ expect(atomic.get()).toEqual(true)
+ atomic.set(false)
+ expect(atomic.get()).toEqual(false)
+ expect(atomic.compareAndSet(true, true)).toEqual(false)
+ expect(atomic.get()).toEqual(false)
+ expect(atomic.compareAndSet(false, true)).toEqual(true)
+ expect(atomic.get()).toEqual(true)
+ expect(atomic.getAndSet(false)).toEqual(true)
+ expect(atomic.get()).toEqual(false)
+ }
+ }
+ describe("java.util.concurrent.AtomicReference") {
+ it("Should have all the normal operations") {
+ val thing1 = Foo(5)
+ val thing1bis = Foo(5) // equals(), but not the same reference
+ val thing2 = Foo(10)
+
+ implicit def foo2js(foo: Foo): js.Any = foo.asInstanceOf[js.Any]
+
+ // sanity
+ expect(thing1 == thing1bis).toBeTruthy
+ expect(thing1 == thing2).toBeFalsy
+ expect(thing1).toBe(thing1)
+ expect(thing1).not.toBe(thing1bis)
+
+ // actual test
+ val atomic = new java.util.concurrent.atomic.AtomicReference(thing1)
+ expect(atomic.get()).toBe(thing1)
+ atomic.set(thing2)
+ expect(atomic.get()).toBe(thing2)
+ expect(atomic.compareAndSet(thing1, thing1)).toEqual(false)
+ expect(atomic.get()).toBe(thing2)
+ expect(atomic.compareAndSet(thing2, thing1)).toEqual(true)
+ expect(atomic.get()).toBe(thing1)
+ expect(atomic.compareAndSet(thing1bis, thing2)).toEqual(false)
+ expect(atomic.getAndSet(thing2)).toBe(thing1)
+ expect(atomic.get()).toBe(thing2)
+ }
+ }
+
+ case class Foo(i: Int)
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/BooleanTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/BooleanTest.scala
new file mode 100644
index 0000000..87c65e9
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/BooleanTest.scala
@@ -0,0 +1,62 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.javalib
+
+import java.lang.{Boolean => JBoolean}
+
+import org.scalajs.jasminetest.JasmineTest
+
+/**
+ * tests the implementation of the java standard library Boolean
+ */
+object BooleanTest extends JasmineTest {
+
+ describe("java.lang.Boolean") {
+
+ it("should provide `booleanValue`") {
+ expect(JBoolean.TRUE.booleanValue()).toBe(true)
+ expect(JBoolean.FALSE.booleanValue()).toBe(false)
+ expect(() => (null: JBoolean).booleanValue()).toThrow
+ }
+
+ it("should provide `compareTo`") {
+ def compare(x: Boolean, y: Boolean): Int =
+ new JBoolean(x).compareTo(new JBoolean(y))
+
+ expect(compare(false, false)).toEqual(0)
+ expect(compare(false, true)).toBeLessThan(0)
+ expect(compare(true, false)).toBeGreaterThan(0)
+ expect(compare(true, true)).toEqual(0)
+ }
+
+ it("should be a Comparable") {
+ def compare(x: Any, y: Any): Int =
+ x.asInstanceOf[Comparable[Any]].compareTo(y)
+
+ expect(compare(false, false)).toEqual(0)
+ expect(compare(false, true)).toBeLessThan(0)
+ expect(compare(true, false)).toBeGreaterThan(0)
+ expect(compare(true, true)).toEqual(0)
+ }
+
+ it("should parse strings") {
+ def test(s: String, v: Boolean): Unit = {
+ expect(JBoolean.parseBoolean(s)).toEqual(v)
+ expect(JBoolean.valueOf(s).booleanValue()).toEqual(v)
+ expect(new JBoolean(s).booleanValue()).toEqual(v)
+ }
+
+ test("false", false)
+ test("true", true)
+ test("TrUe", true)
+ test(null, false)
+ test("truee", false)
+ }
+
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ByteTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ByteTest.scala
new file mode 100644
index 0000000..b033c59
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ByteTest.scala
@@ -0,0 +1,64 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.javalib
+
+import java.lang.{Byte => JByte}
+
+import org.scalajs.jasminetest.JasmineTest
+
+/**
+ * tests the implementation of the java standard library Byte
+ */
+object ByteTest extends JasmineTest {
+
+ describe("java.lang.Byte") {
+
+ it("should provide `compareTo`") {
+ def compare(x: Byte, y: Byte): Int =
+ new JByte(x).compareTo(new JByte(y))
+
+ expect(compare(0.toByte, 5.toByte)).toBeLessThan(0)
+ expect(compare(10.toByte, 9.toByte)).toBeGreaterThan(0)
+ expect(compare(-2.toByte, -1.toByte)).toBeLessThan(0)
+ expect(compare(3.toByte, 3.toByte)).toEqual(0)
+ }
+
+ it("should be a Comparable") {
+ def compare(x: Any, y: Any): Int =
+ x.asInstanceOf[Comparable[Any]].compareTo(y)
+
+ expect(compare(0.toByte, 5.toByte)).toBeLessThan(0)
+ expect(compare(10.toByte, 9.toByte)).toBeGreaterThan(0)
+ expect(compare(-2.toByte, -1.toByte)).toBeLessThan(0)
+ expect(compare(3.toByte, 3.toByte)).toEqual(0)
+ }
+
+ it("should parse strings") {
+ def test(s: String, v: Byte): Unit = {
+ expect(JByte.parseByte(s)).toEqual(v)
+ expect(JByte.valueOf(s).byteValue()).toEqual(v)
+ expect(new JByte(s).byteValue()).toEqual(v)
+ }
+
+ test("0", 0)
+ test("5", 5)
+ test("127", 127)
+ test("-100", -100)
+ }
+
+ it("should reject invalid strings when parsing") {
+ def test(s: String): Unit =
+ expect(() => JByte.parseByte(s)).toThrow
+
+ test("abc")
+ test("")
+ test("200") // out of range
+ }
+
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/CharacterTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/CharacterTest.scala
new file mode 100644
index 0000000..f206221
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/CharacterTest.scala
@@ -0,0 +1,672 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.javalib
+
+import org.scalajs.jasminetest.JasmineTest
+import scala.scalajs.js
+
+object CharacterTest extends JasmineTest {
+
+ describe("java.lang.Character") {
+
+ it("should provide `isISOControl`") {
+ val isoControlChars = (('\u0000' to '\u001F') ++ ('\u007F' to '\u009F')).map(_.toInt).toSet
+ isoControlChars foreach { c =>
+ expect(Character.isISOControl(c)).toEqual(true)
+ }
+
+ val randomInts = List.fill(100)(scala.util.Random.nextInt)
+ ((-1000 to 1000) ++ randomInts).filterNot(isoControlChars) foreach { c =>
+ expect(Character.isISOControl(c)).toEqual(false)
+ }
+ }
+
+ it("should provide `digit`") {
+ expect(Character.digit('a', 16)).toEqual(10)
+ expect(Character.digit('}', 5)).toEqual(-1)
+ expect(Character.digit('1', 50)).toEqual(-1)
+ expect(Character.digit('1', 36)).toEqual(1)
+ expect(Character.digit('Z', 36)).toEqual(35)
+ expect(Character.digit('\uFF22', 20)).toEqual(11)
+ }
+
+ it("should provide isDigit") {
+ expect(Character.isDigit('a')).toBeFalsy
+ expect(Character.isDigit('0')).toBeTruthy
+ expect(Character.isDigit('5')).toBeTruthy
+ expect(Character.isDigit('9')).toBeTruthy
+ expect(Character.isDigit('z')).toBeFalsy
+ expect(Character.isDigit(' ')).toBeFalsy
+ }
+
+ it("should provide `compareTo`") {
+ def compare(x: Char, y: Char): Int =
+ new Character(x).compareTo(new Character(y))
+
+ expect(compare('0', '5')).toBeLessThan(0)
+ expect(compare('o', 'g')).toBeGreaterThan(0)
+ expect(compare('A', 'a')).toBeLessThan(0)
+ expect(compare('b', 'b')).toEqual(0)
+ }
+
+ it("should be a Comparable") {
+ def compare(x: Any, y: Any): Int =
+ x.asInstanceOf[Comparable[Any]].compareTo(y)
+
+ expect(compare('0', '5')).toBeLessThan(0)
+ expect(compare('o', 'g')).toBeGreaterThan(0)
+ expect(compare('A', 'a')).toBeLessThan(0)
+ expect(compare('b', 'b')).toEqual(0)
+ }
+
+ it("should provide isIdentifierIgnorable") {
+ for (c <- '\u0000' to '\u0008')
+ expect(Character.isIdentifierIgnorable(c)).toBeTruthy
+
+ for (c <- '\u000E' to '\u001B')
+ expect(Character.isIdentifierIgnorable(c)).toBeTruthy
+
+ for (c <- '\u007F' to '\u009F')
+ expect(Character.isIdentifierIgnorable(c)).toBeTruthy
+
+ // Exhaustive list of Cf category. Unicode 7.0.0
+ expect(Character.isIdentifierIgnorable('\u00AD')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u0600')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u0601')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u0602')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u0603')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u0604')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u0605')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u061C')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u06DD')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u070F')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u180E')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u200B')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u200C')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u200D')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u200E')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u200F')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u202A')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u202B')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u202C')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u202D')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u202E')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u2060')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u2061')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u2062')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u2063')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u2064')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u2066')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u2067')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u2068')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u2069')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u206A')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u206B')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u206C')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u206D')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u206E')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\u206F')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\uFEFF')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\uFFF9')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\uFFFA')).toBeTruthy
+ expect(Character.isIdentifierIgnorable('\uFFFB')).toBeTruthy
+
+ // BUG in JDK? 17B4 should be "Mn", Java says "Cf"
+ //expect(Character.isIdentifierIgnorable('\u17b4')).toBeTruthy
+
+ // 100 randomly generated negatives
+ expect(Character.isIdentifierIgnorable('\u745a')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\ub445')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\ub23a')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\ub029')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\ufb5c')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u1b67')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u943b')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\ue766')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\uad12')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\ub80b')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u7341')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\ubc73')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\uabb9')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\ub34b')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u1063')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u272f')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u3801')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u53a6')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u2ec2')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u540c')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\uc85f')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\ud2c8')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u551b')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\uc0a1')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\ud25a')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u2b98')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u398b')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\ubc77')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u54cc')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\uc9a0')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\ud10f')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\uf7e1')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u0f29')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\uafcd')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\uf187')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u6287')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\uacb6')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\uff99')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\ub59e')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\uf630')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\ufaec')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\ua7d7')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u3eab')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u54a5')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u393a')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\uc621')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u766c')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\ud64c')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u8beb')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u44e2')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\ub6f6')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u58b6')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u3bad')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u3c28')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\ufbfd')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u585f')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u7227')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\ucea7')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u2c82')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u686d')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u120d')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\uf3db')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u320a')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\ud96e')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u85eb')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u9648')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u08a4')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u9db7')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u82c7')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\ufe12')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u0eaf')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u96dc')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u3a2a')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\uc72e')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u3745')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\ubcf9')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u5f66')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u9be1')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\ud81d')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u3ca3')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u3e82')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u7ce4')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u33ca')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\ue725')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\uef49')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\ue2cf')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\udcf0')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u5f2e')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u2a63')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\ud2d2')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u8023')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\ua957')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u10ba')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\uf85f')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\uc40d')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u2509')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u0d8e')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u9db8')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u824d')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u5670')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u6005')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\ub8de')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\uff5c')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\ub36d')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u0cf2')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u82f6')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u9206')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u95e1')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u990f')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u9fc7')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\udffb')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u0ecb')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u7563')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\uf0ff')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u6b2e')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u894c')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u8f06')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\uffa9')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u37b0')).toBeFalsy
+ expect(Character.isIdentifierIgnorable('\u3e04')).toBeFalsy
+
+ }
+
+ it("should provide isUnicodeIdentifierStart") {
+ // 100 randomly generated positives and 100 randomly generated negatives
+
+ expect(Character.isUnicodeIdentifierStart('\ud6d5')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u3f9c')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u3a40')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u53af')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u1636')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u4884')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\ucba4')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u1ee4')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u6dec')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u10d4')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u631f')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u3661')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u55f8')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\ub4ef')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\ud509')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u65b5')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u316b')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\ub270')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u7f0f')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\uff84')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u11cc')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u0294')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u51b1')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u9ae2')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u304a')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\ud5c7')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u3b4b')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u5e42')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u51fc')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\uc148')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\uc1ae')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u7372')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\uc116')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u5d29')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u8753')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u50f8')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u3f9d')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u1f44')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\ucd43')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u9126')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u8d2e')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u4f5c')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u66d7')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\ua30b')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u140b')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\ub264')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u7b35')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u15e4')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\ubb37')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u34e3')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\uac3e')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\ubd0e')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\ub641')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u1580')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u30c1')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\ub0c8')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u8681')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u7f14')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u4142')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u56c1')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u0444')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u9964')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\ub5c0')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u43d8')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u479e')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u0853')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\ube08')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u9346')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\uf9c1')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u0e8a')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u212c')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u810c')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u8089')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u1331')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\ua5f7')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u5e5e')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u613b')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u34a7')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\ud15b')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\uc1fc')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u92f1')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u3ae6')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\ufceb')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u7584')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\ufe98')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\ubb23')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u7961')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u4445')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u4d5f')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u61cb')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u5176')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\ub987')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u906a')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u4317')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u93ad')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u825a')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u7ff8')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u533a')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\u5617')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\ufcc6')).toBeTruthy
+ expect(Character.isUnicodeIdentifierStart('\ue398')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\ueab6')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\ue7bc')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\uf8ab')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\ue27f')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\uebea')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\ueedc')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\uf091')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u2785')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u287b')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\uf042')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u20f9')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u23d6')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\udc5b')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\ued16')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u1b6b')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\ue7ba')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\uf7fa')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u2125')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\uea97')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\ue624')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\ufbb8')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u2730')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\udb89')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\ue30d')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u2e24')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\uf03e')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\uda27')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u28fc')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u9ffe')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\ude19')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u0b70')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\uddfc')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\ued53')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\ue8cb')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\udccc')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u00a3')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u0bed')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u0c68')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\uf47b')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u0f96')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\ue9c3')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\uf784')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\uef4b')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\udee1')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u2f61')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\uf622')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u19f9')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\ud86a')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\ued83')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\uf7e4')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\uecce')).toBeFalsy
+
+ // BUG in JDK? A699 should be "Ll", Java says "Cn"
+ // expect(Character.isUnicodeIdentifierStart('\ua699')).toBeFalsy
+
+ expect(Character.isUnicodeIdentifierStart('\uaa5f')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\udf24')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u2e0e')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\uf322')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\ue137')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\ued19')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u21ab')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\ue972')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\udbf2')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\uf54c')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u4dd3')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u2769')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\ue363')).toBeFalsy
+
+ // BUG in JDK? 1BBB should be "Lo", Java says "Cn"
+ // expect(Character.isUnicodeIdentifierStart('\u1bbb')).toBeFalsy
+
+ expect(Character.isUnicodeIdentifierStart('\ueae7')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u2bf3')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\ue704')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u1c7f')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\uf52b')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\ue9e3')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u259b')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\uf250')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\uf42f')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\ue244')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u20d9')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\ua881')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u0ee6')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u2203')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u0fc7')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u07fc')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\udb86')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u2a70')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u2bb7')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\uecf0')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\ude48')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u0a3b')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u20b8')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\uf898')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u23e6')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\ud8ba')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\uda1e')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\udc12')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u2a06')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\u0888')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\ud9ec')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\uf81f')).toBeFalsy
+ expect(Character.isUnicodeIdentifierStart('\uf817')).toBeFalsy
+ }
+
+ it("should provide isUnicodeIdentifierPart") {
+ // 100 randomly generated positives and 100 randomly generated negatives
+
+ expect(Character.isUnicodeIdentifierPart('\u48d3')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u0905')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u8f51')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u9bcb')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\ud358')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u1538')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\uffcf')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u83ec')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u3a89')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\ub63a')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\ufe24')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u2d62')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u15ca')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u4fa4')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u47d1')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u831c')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u84e6')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u7783')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\ua03c')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u6ecf')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u147f')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u67a9')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u8b6c')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u3410')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u2cc0')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\ua332')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u9733')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u5df3')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u3fd7')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u6611')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u55b4')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u8bc8')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u6f74')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u6c97')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u6a86')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u6000')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u614f')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u206e')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\ua801')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u9edf')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\ub42c')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u7fcd')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u8a60')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u182f')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u5d0a')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\uaf9c')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u9d4b')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u5088')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\uc1a6')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\ubbe4')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\uad25')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u4653')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u8add')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u3d1c')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u80a8')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u810e')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\uc1d2')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\ub984')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u9d13')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u37c2')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u13cd')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u53f9')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u98b7')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u57f3')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\ub554')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u0176')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\ua318')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u9704')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u8d52')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u940a')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u0fa5')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u38d1')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u3b33')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u93bb')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u03bd')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u4c88')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\ud67d')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\ubcbf')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u3867')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u4368')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u8f2d')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u049a')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u4c01')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u5589')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u5e71')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\ua1fd')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u3a4a')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\uc111')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\ub465')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u95af')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\ubf2c')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u8488')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u4317')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u6b77')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u8995')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u7467')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u16b7')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u3ca0')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u5332')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\u8654')).toBeTruthy
+ expect(Character.isUnicodeIdentifierPart('\ua8c8')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\ue3ca')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\uebee')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\u270e')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\uf0ac')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\ue9ec')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\u296a')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\u33fd')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\ue5f4')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\ueb01')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\uf38b')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\u2e6f')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\uea69')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\uf155')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\u0f0e')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\ueb80')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\ud959')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\ue25e')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\uf566')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\ue4a3')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\uec44')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\u3297')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\u3214')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\u1bfd')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\u4dd0')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\uea99')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\u309b')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\uf592')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\uf4dd')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\udfaf')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\udd38')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\uf820')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\uaacd')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\uff5b')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\ude36')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\ue33b')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\udbce')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\ue1f6')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\uf78a')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\ueb44')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\uebd4')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\u1df7')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\u2f10')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\u1cbf')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\u2362')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\uebeb')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\u2ede')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\u221d')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\u2021')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\udf41')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\u05f5')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\u24ab')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\uee15')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\uf175')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\uf35c')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\udc7b')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\ud883')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\uf341')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\ueec6')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\u2f57')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\uff64')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\ue6a4')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\uec34')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\u22a5')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\uf5ac')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\u3360')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\u28b0')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\uf678')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\ue0e4')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\u233f')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\u0afa')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\u2013')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\ud7af')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\ud98e')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\ud8a5')).toBeFalsy
+
+ // BUG in JDK? A79E should be "Lu", Java says "Cn"
+ // expect(Character.isUnicodeIdentifierPart('\ua79e')).toBeFalsy
+
+ expect(Character.isUnicodeIdentifierPart('\u1806')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\ue07a')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\u2748')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\uabad')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\uec5c')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\ue832')).toBeFalsy
+
+ // BUG in JDK? 08A9 should be "Lo", Java says "Cn"
+ // expect(Character.isUnicodeIdentifierPart('\u08a9')).toBeFalsy
+
+ expect(Character.isUnicodeIdentifierPart('\ue4bd')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\u208a')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\uf840')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\uf570')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\uef1e')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\u2bd4')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\ue385')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\udc18')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\u0af0')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\u244a')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\uf01e')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\uf114')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\ue9c4')).toBeFalsy
+
+ // BUG in JDK? AAF4 should be "Lm", Java says "Cn"
+ // expect(Character.isUnicodeIdentifierPart('\uaaf4')).toBeFalsy
+
+ expect(Character.isUnicodeIdentifierPart('\uf7b9')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\udd2f')).toBeFalsy
+ expect(Character.isUnicodeIdentifierPart('\u2d2c')).toBeFalsy
+ }
+
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ClassTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ClassTest.scala
new file mode 100644
index 0000000..424edd6
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ClassTest.scala
@@ -0,0 +1,30 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.javalib
+
+import org.scalajs.jasminetest.JasmineTest
+import scala.scalajs.js
+
+object ClassTest extends JasmineTest {
+
+ describe("java.lang.Class") {
+
+ it("should provide getSimpleName()") {
+ expect(classOf[java.lang.Integer].getSimpleName()).toEqual("Integer")
+ expect(classOf[java.lang.Class[_]].getSimpleName()).toEqual("Class")
+ expect(classOf[scala.collection.Map[_, _]].getSimpleName()).toEqual("Map")
+ expect(classOf[ClassTestClass#InnerClass].getSimpleName()).toEqual("InnerClass")
+ }
+
+ }
+
+}
+
+class ClassTestClass {
+ class InnerClass
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/DateTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/DateTest.scala
new file mode 100644
index 0000000..f62f926
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/DateTest.scala
@@ -0,0 +1,87 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.javalib
+
+import java.util.Date
+
+import org.scalajs.jasminetest.JasmineTest
+
+/**
+ * tests the implementation of the java standard library Date
+ */
+object DateTest extends JasmineTest {
+
+ describe("java.lang.Date") {
+
+ it("should provide `compareTo`") {
+ def compare(x: Date, y: Date): Int = {
+ x.compareTo(y)
+ }
+
+ expect(compare(new Date(97, 11, 5, 0, 0), new Date(98, 11, 5, 0, 0))).toBeLessThan(0)
+ expect(compare(new Date(98, 11, 5, 0, 0), new Date(97, 11, 5, 0, 0))).toBeGreaterThan(0)
+ expect(compare(new Date(97, 11, 5, 0, 0), new Date(97, 11, 5))).toEqual(0)
+ expect(compare(new Date(97, 11, 5, 0, 0), new Date(97, 11, 5, 0, 1))).toBeLessThan(0)
+ expect(compare(new Date(97, 11, 5), new Date(97, 11, 5, 0, 0))).toEqual(0)
+ }
+
+ it("should be a Comparable") {
+ def compare(x: Any, y: Any): Int =
+ x.asInstanceOf[Comparable[Any]].compareTo(y)
+
+ expect(compare(new Date(97, 11, 5, 0, 0), new Date(98, 11, 5, 0, 0))).toBeLessThan(0)
+ expect(compare(new Date(98, 11, 5, 0, 0), new Date(97, 11, 5, 0, 0))).toBeGreaterThan(0)
+ expect(compare(new Date(97, 11, 5, 0, 0), new Date(97, 11, 5))).toEqual(0)
+ expect(compare(new Date(97, 11, 5, 0, 0), new Date(97, 11, 5, 0, 1))).toBeLessThan(0)
+ expect(compare(new Date(97, 11, 5), new Date(97, 11, 5, 0, 0))).toEqual(0)
+ }
+
+ it("should parse strings") {
+ def test(s: String, v: Date): Unit =
+ expect(new Date(s).compareTo(v)).toEqual(0)
+
+ test("Nov 5 1997 5:23:27 GMT", new Date(Date.UTC(97, 10, 5, 5, 23, 27)))
+ test("Nov 1 1997 GMT", new Date(Date.UTC(97,10,1, 0, 0, 0)))
+ test("Jan 1 1970 18:11:01 GMT", new Date(Date.UTC(70,0,1,18,11,1)))
+ }
+
+ it("should provide after") {
+ expect(new Date(97, 11, 5, 0, 0).after(new Date(98, 11, 5, 0, 0))).toBe(false)
+ expect(new Date(99, 11, 5, 0, 0).after(new Date(98, 11, 5, 0, 0))).toBe(true)
+ expect(new Date(99, 11, 5, 0, 0).after(new Date(99, 11, 5, 0, 0))).toBe(false)
+ }
+
+ it("should provide before") {
+ expect(new Date(97, 11, 5, 0, 0).before(new Date(98, 11, 5, 0, 0))).toBe(true)
+ expect(new Date(99, 11, 5, 0, 0).before(new Date(98, 11, 5, 0, 0))).toBe(false)
+ expect(new Date(99, 11, 5, 0, 0).before(new Date(99, 11, 5, 0, 0))).toBe(false)
+ }
+
+ it("should provide clone") {
+ def testClone(date: Date) = {
+ val cloned = date.clone()
+ date == cloned
+ }
+
+ expect(testClone(new Date(97, 11, 5, 0, 0))).toBe(true)
+ expect(testClone(new Date(92, 14, 6, 2, 1))).toBe(true)
+ expect(testClone(new Date(4, 1, 2, 3, 0, 0))).toBe(true)
+ }
+
+ it("should respond to getYear") {
+ def testYear(year: Int) = {
+ val date = new Date()
+ date.setYear(year)
+ expect(date.getYear).toEqual(year)
+ }
+ testYear(1940)
+ testYear(1920)
+ testYear(2030)
+ }
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/DoubleTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/DoubleTest.scala
new file mode 100644
index 0000000..59c6e10
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/DoubleTest.scala
@@ -0,0 +1,217 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.javalib
+
+import org.scalajs.jasminetest.JasmineTest
+import scala.scalajs.js
+
+import java.lang.{Double => JDouble}
+
+import scala.util.Try
+
+object DoubleTest extends JasmineTest {
+
+ describe("java.lang.Double") {
+
+ it("should provide proper `equals`") {
+ expect(Double.box(0.0) == Double.box(-0.0)).toBeTruthy
+ expect(Double.box(Double.NaN) == Double.box(Double.NaN)).toBeTruthy
+ }
+
+ it("hashCode") {
+ def hashCodeNotInlined(x: Any): Int = {
+ var y = x // do not inline
+ y.hashCode
+ }
+
+ def test(x: Double, expected: Int): Unit = {
+ expect(x.hashCode).toEqual(expected)
+ expect(hashCodeNotInlined(x)).toEqual(expected)
+ }
+
+ test(0.0, 0)
+ test(-0.0, 0)
+ test(1234.0, 1234)
+ test(1.5, 1073217536)
+ test(Math.PI, 340593891)
+ test(-54.0, -54)
+
+ test(Double.MinPositiveValue, 1)
+ test(Double.MinValue, 1048576)
+ test(Double.MaxValue, -2146435072)
+
+ test(Double.NaN, 2146959360)
+ test(Double.PositiveInfinity, 2146435072)
+ test(Double.NegativeInfinity, -1048576)
+ }
+
+ it("should provide `toString` with integer values when an integer") {
+ expect(0.0.toString).toEqual("0")
+ expect(-0.0.toString).toEqual("0")
+ expect(Double.NaN.toString).toEqual("NaN")
+ expect(Double.PositiveInfinity.toString).toEqual("Infinity")
+ expect(Double.NegativeInfinity.toString).toEqual("-Infinity")
+ expect(5.0.toString).toEqual("5")
+ expect(-5.0.toString).toEqual("-5")
+ expect(1.2.toString).toEqual("1.2")
+ }
+
+ it("should parse strings") {
+ expect("0.0".toDouble).toEqual(0.0f)
+ expect("NaN".toDouble.isNaN).toBeTruthy
+ expect(Try("asdf".toDouble).isFailure).toBeTruthy
+
+ def test(s: String, v: Double): Unit = {
+ expect(JDouble.parseDouble(s)).toBeCloseTo(v)
+ expect(JDouble.valueOf(s).doubleValue()).toBeCloseTo(v)
+ expect(new JDouble(s).doubleValue()).toBeCloseTo(v)
+ }
+
+ test("0", 0.0)
+ test("5.3", 5.3)
+ test("127e2", 12700.0)
+ test("127E-2", 1.27)
+ test("1E+1", 10)
+ test("-123.4", -123.4)
+ test("65432.1", 65432.10)
+ test("-87654.321", -87654.321)
+ test("+.3f", 0.3)
+ }
+
+ it("should reject invalid strings when parsing") {
+ def test(s: String): Unit =
+ expect(() => JDouble.parseDouble(s)).toThrow
+
+ test("4.3.5")
+ test("4e3.5")
+ test("hello world")
+ test("--4")
+ test("4E-3.2")
+ }
+
+ it("should provide `compareTo`") {
+ def compare(x: Double, y: Double): Int =
+ new JDouble(x).compareTo(new JDouble(y))
+
+ expect(compare(0.0, 5.5)).toBeLessThan(0)
+ expect(compare(10.5, 10.2)).toBeGreaterThan(0)
+ expect(compare(-2.1, -1.0)).toBeLessThan(0)
+ expect(compare(3.14, 3.14)).toEqual(0)
+
+ // From compareTo's point of view, NaN is equal to NaN
+ expect(compare(Double.NaN, Double.NaN)).toEqual(0)
+ }
+
+ it("should be a Comparable") {
+ def compare(x: Any, y: Any): Int =
+ x.asInstanceOf[Comparable[Any]].compareTo(y)
+
+ expect(compare(0.0, 5.5)).toBeLessThan(0)
+ expect(compare(10.5, 10.2)).toBeGreaterThan(0)
+ expect(compare(-2.1, -1.0)).toBeLessThan(0)
+ expect(compare(3.14, 3.14)).toEqual(0)
+
+ // From compareTo's point of view, NaN is equal to NaN
+ expect(compare(Double.NaN, Double.NaN)).toEqual(0)
+ }
+
+ it("should provide isInfinite - #515") {
+ expect(Double.PositiveInfinity.isInfinite).toBeTruthy
+ expect(Double.NegativeInfinity.isInfinite).toBeTruthy
+ expect((1.0/0).isInfinite).toBeTruthy
+ expect((-1.0/0).isInfinite).toBeTruthy
+ expect((0.0).isInfinite).toBeFalsy
+ }
+
+ it("isNaN") {
+ def f(v: Double): Boolean = {
+ var v2 = v // do not inline
+ v2.isNaN
+ }
+
+ expect(f(Double.NaN)).toBeTruthy
+
+ expect(f(Double.PositiveInfinity)).toBeFalsy
+ expect(f(Double.NegativeInfinity)).toBeFalsy
+ expect(f(1.0 / 0)).toBeFalsy
+ expect(f(-1.0 / 0)).toBeFalsy
+ expect(f(0.0)).toBeFalsy
+ expect(f(3.0)).toBeFalsy
+ expect(f(-1.5)).toBeFalsy
+ }
+
+ it("longBitsToDouble") {
+ def isZero(v: Double, neg: Boolean): Boolean = {
+ (v == 0.0) && (1 / v == (
+ if (neg) Double.NegativeInfinity
+ else Double.PositiveInfinity))
+ }
+
+ import JDouble.{longBitsToDouble => f}
+
+ // Specials
+ expect(f(0x7ff0000000000000L)).toEqual(Double.PositiveInfinity)
+ expect(f(0xfff0000000000000L)).toEqual(Double.NegativeInfinity)
+ expect(isZero(f(0x0000000000000000L), false)).toBeTruthy
+ expect(isZero(f(0x8000000000000000L), true)).toBeTruthy
+ expect(f(0x7ff8000000000000L).isNaN).toBeTruthy // canonical NaN
+
+ // Non-canonical NaNs
+ expect(f(0x7ff0000000000001L).isNaN).toBeTruthy // smallest positive NaN
+ expect(f(0x7ff15ab515d3cca1L).isNaN).toBeTruthy // an arbitrary positive NaN
+ expect(f(0x7fffffffffffffffL).isNaN).toBeTruthy // largest positive NaN
+ expect(f(0xfff0000000000001L).isNaN).toBeTruthy // smallest negative NaN
+ expect(f(0xfff15ab515d3cca1L).isNaN).toBeTruthy // an arbitrary negative NaN
+ expect(f(0xffffffffffffffffL).isNaN).toBeTruthy // largest negative NaN
+
+ // Normal forms
+ expect(f(0x0010000000000000L)).toEqual(2.2250738585072014e-308) // smallest pos normal form
+ expect(f(0x7fefffffffffffffL)).toEqual(1.7976931348623157e308) // largest pos normal form
+ expect(f(0x4d124568bc6584caL)).toEqual(1.8790766677624813e63) // an arbitrary pos normal form
+ expect(f(0x8010000000000000L)).toEqual(-2.2250738585072014e-308) // smallest neg normal form
+ expect(f(0xffefffffffffffffL)).toEqual(-1.7976931348623157e308) // largest neg normal form
+ expect(f(0xcd124568bc6584caL)).toEqual(-1.8790766677624813e63) // an arbitrary neg normal form
+
+ // Subnormal forms
+ expect(f(0x0000000000000001L)).toEqual(Double.MinPositiveValue) // smallest pos subnormal form
+ expect(f(0x000fffffffffffffL)).toEqual(2.225073858507201e-308) // largest pos subnormal form
+ expect(f(0x000c5d44ae45cb60L)).toEqual(1.719471609939382e-308) // an arbitrary pos subnormal form
+ expect(f(0x8000000000000001L)).toEqual(-Double.MinPositiveValue) // smallest neg subnormal form
+ expect(f(0x800fffffffffffffL)).toEqual(-2.225073858507201e-308) // largest neg subnormal form
+ expect(f(0x800c5d44ae45cb60L)).toEqual(-1.719471609939382e-308) // an arbitrary neg subnormal form
+ }
+
+ it("doubleToLongBits") {
+ import JDouble.{doubleToLongBits => f}
+
+ // Specials
+ expect(f(Double.PositiveInfinity) == 0x7ff0000000000000L).toBeTruthy
+ expect(f(Double.NegativeInfinity) == 0xfff0000000000000L)
+ expect(f(0.0) == 0x0000000000000000L).toBeTruthy
+ expect(f(-0.0) == 0x8000000000000000L).toBeTruthy
+ expect(f(Double.NaN) == 0x7ff8000000000000L).toBeTruthy // canonical NaN
+
+ // Normal forms
+ expect(f(2.2250738585072014e-308) == 0x0010000000000000L).toBeTruthy // smallest pos normal form
+ expect(f(1.7976931348623157e308) == 0x7fefffffffffffffL).toBeTruthy // largest pos normal form
+ expect(f(1.8790766677624813e63) == 0x4d124568bc6584caL).toBeTruthy // an arbitrary pos normal form
+ expect(f(-2.2250738585072014e-308) == 0x8010000000000000L).toBeTruthy // smallest neg normal form
+ expect(f(-1.7976931348623157e308) == 0xffefffffffffffffL).toBeTruthy // largest neg normal form
+ expect(f(-1.8790766677624813e63) == 0xcd124568bc6584caL).toBeTruthy // an arbitrary neg normal form
+
+ // Subnormal forms
+ expect(f(Double.MinPositiveValue) == 0x0000000000000001L).toBeTruthy // smallest pos subnormal form
+ expect(f(2.225073858507201e-308) == 0x000fffffffffffffL).toBeTruthy // largest pos subnormal form
+ expect(f(1.719471609939382e-308) == 0x000c5d44ae45cb60L).toBeTruthy // an arbitrary pos subnormal form
+ expect(f(-Double.MinPositiveValue) == 0x8000000000000001L).toBeTruthy // smallest neg subnormal form
+ expect(f(-2.225073858507201e-308) == 0x800fffffffffffffL).toBeTruthy // largest neg subnormal form
+ expect(f(-1.719471609939382e-308) == 0x800c5d44ae45cb60L).toBeTruthy // an arbitrary neg subnormal form
+ }
+
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/FloatTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/FloatTest.scala
new file mode 100644
index 0000000..e45a34a
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/FloatTest.scala
@@ -0,0 +1,221 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.javalib
+
+import org.scalajs.jasminetest.JasmineTest
+import scala.scalajs.js
+
+import java.lang.{Float => JFloat}
+
+import scala.util.Try
+
+object FloatTest extends JasmineTest {
+
+ describe("java.lang.Float") {
+
+ it("should provide proper `equals`") {
+ expect(Float.box(0.0f) == Float.box(-0.0f)).toBeTruthy
+ expect(Float.box(Float.NaN) == Float.box(Float.NaN)).toBeTruthy
+ }
+
+ it("hashCode") {
+ def hashCodeNotInlined(x: Any): Int = {
+ var y = x // do not inline
+ y.hashCode
+ }
+
+ def test(x: Float, expected: Int): Unit = {
+ expect(x.hashCode).toEqual(expected)
+ expect(hashCodeNotInlined(x)).toEqual(expected)
+ }
+
+ test(0.0f, 0)
+ test(-0.0f, 0)
+ test(1234.0f, 1234)
+ test(1.5f, 1073217536)
+ test(-54f, -54)
+
+ test(Float.MinPositiveValue, 916455424)
+ test(Float.MinValue, 670040063)
+ test(Float.MaxValue, -1477443585)
+
+ test(Float.NaN, 2146959360)
+ test(Float.PositiveInfinity, 2146435072)
+ test(Float.NegativeInfinity, -1048576)
+ }
+
+ it("should provide `toString` with integer values when an integer") {
+ expect(0.0f.toString).toEqual("0")
+ expect(-0.0f.toString).toEqual("0")
+ expect(Float.NaN.toString).toEqual("NaN")
+ expect(Float.PositiveInfinity.toString).toEqual("Infinity")
+ expect(Float.NegativeInfinity.toString).toEqual("-Infinity")
+ expect(5.0f.toString).toEqual("5")
+ expect(-5.0f.toString).toEqual("-5")
+
+ // We need to explicitly cut the string here, since floats are
+ // represented by doubles (but the literal is emitted as
+ // float). Therefore there may be some imprecision. This is
+ // documented as semantic difference.
+ expect(1.2f.toString.substring(0,3)).toEqual("1.2")
+ }
+
+ it("should parse strings") {
+ expect("0.0".toFloat).toEqual(0.0f)
+ expect("NaN".toFloat.isNaN).toBeTruthy
+ expect(Try("asdf".toFloat).isFailure).toBeTruthy
+
+ def test(s: String, v: Float): Unit = {
+ expect(JFloat.parseFloat(s)).toBeCloseTo(v)
+ expect(JFloat.valueOf(s).floatValue()).toBeCloseTo(v)
+ expect(new JFloat(s).floatValue()).toBeCloseTo(v)
+ }
+
+ test("0", 0.0f)
+ test("5.3", 5.3f)
+ test("127e2", 12700.0f)
+ test("127E-2", 1.27f)
+ test("1E+1", 10f)
+ test("-123.4", -123.4f)
+ test("65432.1", 65432.10f)
+ test("-87654.321", -87654.321f)
+ test("+.3f", 0.3f)
+ }
+
+ it("should reject invalid strings when parsing") {
+ def test(s: String): Unit =
+ expect(() => JFloat.parseFloat(s)).toThrow
+
+ test("4.3.5")
+ test("4e3.5")
+ test("hello world")
+ test("--4")
+ test("4E-3.2")
+ }
+
+ it("should provide `compareTo`") {
+ def compare(x: Float, y: Float): Int =
+ new JFloat(x).compareTo(new JFloat(y))
+
+ expect(compare(0.0f, 5.5f)).toBeLessThan(0)
+ expect(compare(10.5f, 10.2f)).toBeGreaterThan(0)
+ expect(compare(-2.1f, -1.0f)).toBeLessThan(0)
+ expect(compare(3.14f, 3.14f)).toEqual(0)
+
+ // From compareTo's point of view, NaN is equal to NaN
+ expect(compare(Float.NaN, Float.NaN)).toEqual(0)
+ }
+
+ it("should be a Comparable") {
+ def compare(x: Any, y: Any): Int =
+ x.asInstanceOf[Comparable[Any]].compareTo(y)
+
+ expect(compare(0.0f, 5.5f)).toBeLessThan(0)
+ expect(compare(10.5f, 10.2f)).toBeGreaterThan(0)
+ expect(compare(-2.1f, -1.0f)).toBeLessThan(0)
+ expect(compare(3.14f, 3.14f)).toEqual(0)
+
+ // From compareTo's point of view, NaN is equal to NaN
+ expect(compare(Float.NaN, Float.NaN)).toEqual(0)
+ }
+
+ it("should provide isInfinite - #515") {
+ expect(Float.PositiveInfinity.isInfinite).toBeTruthy
+ expect(Float.NegativeInfinity.isInfinite).toBeTruthy
+ expect((1f/0).isInfinite).toBeTruthy
+ expect((-1f/0).isInfinite).toBeTruthy
+ expect(0f.isInfinite).toBeFalsy
+ }
+
+ it("isNaN") {
+ def f(v: Float): Boolean = {
+ var v2 = v // do not inline
+ v2.isNaN
+ }
+
+ expect(f(Float.NaN)).toBeTruthy
+
+ expect(f(Float.PositiveInfinity)).toBeFalsy
+ expect(f(Float.NegativeInfinity)).toBeFalsy
+ expect(f(1f / 0)).toBeFalsy
+ expect(f(-1f / 0)).toBeFalsy
+ expect(f(0f)).toBeFalsy
+ expect(f(3f)).toBeFalsy
+ expect(f(-1.5f)).toBeFalsy
+ }
+
+ it("intBitsToFloat") {
+ def isZero(v: Float, neg: Boolean): Boolean = {
+ (v == 0.0f) && (1 / v == (
+ if (neg) Float.NegativeInfinity
+ else Float.PositiveInfinity))
+ }
+
+ import JFloat.{intBitsToFloat => f}
+
+ // Specials
+ expect(f(0x7f800000)).toEqual(Float.PositiveInfinity)
+ expect(f(0xff800000)).toEqual(Float.NegativeInfinity)
+ expect(isZero(f(0x00000000), false)).toBeTruthy
+ expect(isZero(f(0x80000000), true)).toBeTruthy
+ expect(f(0x7fc00000).isNaN).toBeTruthy // canonical NaN
+
+ // Non-canonical NaNs
+ expect(f(0x7f800001).isNaN).toBeTruthy // smallest positive NaN
+ expect(f(0x7f915ab5).isNaN).toBeTruthy // an arbitrary positive NaN
+ expect(f(0x7fffffff).isNaN).toBeTruthy // largest positive NaN
+ expect(f(0xff800001).isNaN).toBeTruthy // smallest negative NaN
+ expect(f(0xff915ab5).isNaN).toBeTruthy // an arbitrary negative NaN
+ expect(f(0xffffffff).isNaN).toBeTruthy // largest negative NaN
+
+ // Normal forms
+ expect(f(0x00800000)).toEqual(1.17549435e-38f) // smallest pos normal form
+ expect(f(0x7f7fffff)).toEqual(3.4028234e38f) // largest pos normal form
+ expect(f(0x4d124568)).toEqual(1.53376384e8f) // an arbitrary pos normal form
+ expect(f(0x80800000)).toEqual(-1.17549435e-38f) // smallest neg normal form
+ expect(f(0xff7fffff)).toEqual(-3.4028234e38f) // largest neg normal form
+ expect(f(0xcd124568)).toEqual(-1.53376384e8f) // an arbitrary neg normal form
+
+ // Subnormal forms
+ expect(f(0x00000001)).toEqual(Float.MinPositiveValue) // smallest pos subnormal form
+ expect(f(0x007fffff)).toEqual(1.1754942e-38f) // largest pos subnormal form
+ expect(f(0x007c5d44)).toEqual(1.1421059e-38f) // an arbitrary pos subnormal form
+ expect(f(0x80000001)).toEqual(-Float.MinPositiveValue) // smallest neg subnormal form
+ expect(f(0x807fffff)).toEqual(-1.1754942e-38f) // largest neg subnormal form
+ expect(f(0x807c5d44)).toEqual(-1.1421059e-38f) // an arbitrary neg subnormal form
+ }
+
+ it("floatToIntBits") {
+ import JFloat.{floatToIntBits => f}
+
+ // Specials
+ expect(f(Float.PositiveInfinity)).toEqual(0x7f800000)
+ expect(f(Float.NegativeInfinity)).toEqual(0xff800000)
+ expect(f(0.0f)).toEqual(0x00000000)
+ expect(f(-0.0f)).toEqual(0x80000000)
+ expect(f(Float.NaN)).toEqual(0x7fc00000) // canonical NaN
+
+ // Normal forms
+ expect(f(1.17549435e-38f)).toEqual(0x00800000) // smallest pos normal form
+ expect(f(3.4028234e38f)).toEqual(0x7f7fffff) // largest pos normal form
+ expect(f(1.53376384e8f)).toEqual(0x4d124568) // an arbitrary pos normal form
+ expect(f(-1.17549435e-38f)).toEqual(0x80800000) // smallest neg normal form
+ expect(f(-3.4028234e38f)).toEqual(0xff7fffff) // largest neg normal form
+ expect(f(-1.53376384e8f)).toEqual(0xcd124568) // an arbitrary neg normal form
+
+ // Subnormal forms
+ expect(f(Float.MinPositiveValue)).toEqual(0x00000001) // smallest pos subnormal form
+ expect(f(1.1754942e-38f)).toEqual(0x007fffff) // largest pos subnormal form
+ expect(f(1.1421059e-38f)).toEqual(0x007c5d44) // an arbitrary pos subnormal form
+ expect(f(-Float.MinPositiveValue)).toEqual(0x80000001) // smallest neg subnormal form
+ expect(f(-1.1754942e-38f)).toEqual(0x807fffff) // largest neg subnormal form
+ expect(f(-1.1421059e-38f)).toEqual(0x807c5d44) // an arbitrary neg subnormal form
+ }
+
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/FormatterTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/FormatterTest.scala
new file mode 100644
index 0000000..e7a705c
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/FormatterTest.scala
@@ -0,0 +1,241 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.javalib
+
+import scala.scalajs.js
+import org.scalajs.jasminetest.JasmineTest
+
+import java.util.{ Formatter, Formattable, FormattableFlags }
+
+import java.lang.{
+ Double => JDouble,
+ Float => JFloat,
+ Integer => JInteger,
+ Long => JLong,
+ Byte => JByte,
+ Short => JShort,
+ Boolean => JBoolean,
+ String => JString
+}
+
+
+object FormatterTest extends JasmineTest {
+
+ class HelperClass
+ class FormattableClass extends Formattable {
+ var frm: Formatter = _
+ var flags: Int = _
+ var width: Int = _
+ var precision: Int = _
+ var calls = 0
+ def formatTo(frm: Formatter, flags: Int, width: Int, precision: Int) = {
+ this.calls += 1
+ this.flags = flags
+ this.width = width
+ this.precision = precision
+ frm.out().append("foobar")
+ }
+
+ def expectCalled(times: Int, flags: Int, width: Int, precision: Int) = {
+ expect(this.calls).toEqual(times)
+ expect(this.flags).toEqual(flags)
+ expect(this.width).toEqual(width)
+ expect(this.precision).toEqual(precision)
+ }
+
+ }
+
+ def expectF(format: String, args: AnyRef*) = {
+ val fmt = new Formatter()
+ val res = fmt.format(format, args:_*).toString()
+ fmt.close()
+ expect(res)
+ }
+
+ def expectFC(format: String, flags: Int, width: Int, precision: Int) = {
+ val fc = new FormattableClass
+ val exp = expectF(format, fc)
+ fc.expectCalled(1, flags, width, precision)
+ exp
+ }
+
+ def expectThrow(format: String, args: AnyRef*) = {
+ val fmt = new Formatter()
+ expect(() => fmt.format(format, args:_*)).toThrow
+ }
+
+ describe("java.util.Formatter") {
+
+ // Explicitly define these as `var`'s to avoid any compile-time constant folding
+ var IntMax: Int = Int.MaxValue
+ var IntMin: Int = Int.MinValue
+ var ByteMax: Byte = Byte.MaxValue
+ var ByteMin: Byte = Byte.MinValue
+ var ShortMax: Short = Short.MaxValue
+ var ShortMin: Short = Short.MinValue
+
+ it("should provide 'b' conversion") {
+ expectF("%b", null).toEqual("false")
+ expectF("%b", true: JBoolean).toEqual(JString.valueOf(true))
+ expectF("%b", false: JBoolean).toEqual(JString.valueOf(false))
+ expectF("%b", new HelperClass).toEqual("true")
+ }
+
+ it("should provide 'h' conversion") {
+ val x = new HelperClass
+ expectF("%h", x).toEqual(Integer.toHexString(x.hashCode()))
+ expectF("%H", x).toEqual(Integer.toHexString(x.hashCode()).toUpperCase())
+ expectF("%h", null).toEqual("null")
+ }
+
+ it("should provide 's' conversion") {
+ expectFC("%s", 0, -1, -1).toEqual("foobar")
+ expectFC("%-s", FormattableFlags.LEFT_JUSTIFY, -1, -1).toEqual("foobar")
+ expectFC("%-10s", FormattableFlags.LEFT_JUSTIFY, 10, -1).toEqual("foobar")
+ expectFC("%#-10.2s", FormattableFlags.LEFT_JUSTIFY |
+ FormattableFlags.ALTERNATE, 10, 2).toEqual("foobar")
+ expectFC("%#10.2S", FormattableFlags.UPPERCASE |
+ FormattableFlags.ALTERNATE, 10, 2).toEqual("foobar")
+ expectF("%10s", "hello").toEqual(" hello")
+ expectF("%-10s", "hello").toEqual("hello ")
+ expectThrow("%#s", "hello")
+ }
+
+ it("should provide 'c' conversion") {
+ expectF("%-5c", new Character('!')).toEqual("! ")
+ }
+
+ it("should provide 'd' conversion") {
+ expectF("%d", new Integer(5)).toEqual("5")
+ expectF("%05d", new Integer(5)).toEqual("00005")
+ expectF("%5d", new Integer(-10)).toEqual(" -10")
+ expectF("%05d", new Integer(-10)).toEqual("-0010")
+ }
+
+ it("should provide 'o' conversion") {
+ expectF("%o", new JInteger(8)).toEqual("10")
+ expectF("%05o", new JInteger(16)).toEqual("00020")
+ expectF("%5o", new JInteger(-10)).toEqual("37777777766")
+ expectF("%05o", new JInteger(-10)).toEqual("37777777766")
+ expectF("%o", new JByte(8.toByte)).toEqual("10")
+ expectF("%05o", new JByte(16.toByte)).toEqual("00020")
+ expectF("%14o", new JByte(-10.toByte)).toEqual(" 37777777766")
+ expectF("%05o", new JByte(-10.toByte)).toEqual("37777777766")
+ expectF("%o", new JShort(8.toShort)).toEqual("10")
+ expectF("%05o", new JShort(16.toShort)).toEqual("00020")
+ expectF("%5o", new JShort(-10.toShort)).toEqual("37777777766")
+ expectF("%015o",new JShort(-10.toShort)).toEqual("000037777777766")
+ expectF("%05o", new JLong(-5L)).toEqual("1777777777777777777773")
+ }
+
+ it("should provide 'x' conversion") {
+ expectF("%0#5x", new JInteger(5)).toEqual("0x005")
+ expectF("%#5x", new JInteger(5)).toEqual(" 0x5")
+ expectF("%#5X", new JInteger(5)).toEqual(" 0X5")
+ expectF("%x", new JInteger(-3)).toEqual("fffffffd")
+ expectF("%x", new JByte(-4.toByte)).toEqual("fffffffc")
+ expectF("%0#5x", new JByte(5.toByte)).toEqual("0x005")
+ expectF("%#5x", new JByte(5.toByte)).toEqual(" 0x5")
+ expectF("%#5X", new JByte(5.toByte)).toEqual(" 0X5")
+ expectF("%x", new JByte(-3.toByte)).toEqual("fffffffd")
+ expectF("%0#5x", new JShort(5.toShort)).toEqual("0x005")
+ expectF("%#5x", new JShort(5.toShort)).toEqual(" 0x5")
+ expectF("%#5X", new JShort(5.toShort)).toEqual(" 0X5")
+ expectF("%x", new JShort(-3.toShort)).toEqual("fffffffd")
+ expectF("%x", new JLong(-5L)).toEqual("fffffffffffffffb")
+ expectF("%X", new JLong(26L)).toEqual("1A")
+ }
+
+ it("should provide 'e' conversion") {
+ expectF("%e", new JDouble(1000)).toEqual("1.000000e+03")
+ expectF("%.0e", new JDouble(1.2e100)).toEqual("1e+100")
+ // We use 1.51e100 in this test, since we seem to have a floating
+ // imprecision at exactly 1.5e100 that yields to a rounding error
+ // towards (1e+100 instead of 2e+100)
+ expectF("%.0e", new JDouble(1.51e100)).toEqual("2e+100")
+ expectF("%10.2e", new JDouble(1.2e100)).toEqual(" 1.20e+100")
+ expectF("%012.4e", new JFloat(1.2e-21f)).toEqual("001.2000e-21")
+ expectF("%012.4E", new JFloat(1.2e-21f)).toEqual("001.2000E-21")
+ expectF("%(015.4e", new JFloat(-1.2e-21f)).toEqual("(0001.2000e-21)")
+
+ // Tests with infinity and NaN
+ expectF("%e", new JDouble(Double.PositiveInfinity)).toEqual("Infinity")
+ expectF("%e", new JDouble(Double.NegativeInfinity)).toEqual("-Infinity")
+ expectF("%010e", new JDouble(Double.PositiveInfinity)).toEqual(" Infinity")
+ expectF("%-10e", new JDouble(Double.PositiveInfinity)).toEqual("Infinity ")
+ expectF("%(e", new JDouble(Double.NegativeInfinity)).toEqual("(Infinity)")
+ expectF("%010e", new JDouble(Double.NaN)).toEqual(" NaN")
+ }
+
+ it("should provide 'g' conversion") {
+ expectF("%g", new JDouble(.5e-4)).toEqual("5.00000e-05")
+ expectF("%g", new JDouble(3e-4)).toEqual("0.000300000")
+ expectF("%.3g", new JDouble(3e-4)).toEqual("0.000300")
+ expectF("%.2g", new JDouble(1e-3)).toEqual("0.0010")
+ expectF("%g", new JDouble(3e5)).toEqual("300000")
+ expectF("%.3g", new JDouble(3e5)).toEqual("3.00e+05")
+ expectF("%04g", new JDouble(Double.NaN)).toEqual(" NaN")
+ }
+
+ it("should provide 'f' conversion") {
+ expectF("%f", new JDouble(3.3)).toEqual("3.300000")
+ expectF("%0(9.4f", new JDouble(-4.6)).toEqual("(04.6000)")
+ expectF("%f", new JFloat(3e10f)).toEqual("30000001024.000000")
+ expectF("%f", new JDouble(3e10)).toEqual("30000000000.000000")
+ expectF("%04f", new JDouble(Double.NaN)).toEqual(" NaN")
+ }
+
+ it("should support '%%'") {
+ expectF("%d%%%d", new JInteger(1), new JInteger(2)).toEqual("1%2")
+ }
+
+ it("should support '%n'") {
+ expectF("%d%n%d", new JInteger(1), new JInteger(2)).toEqual("1\n2")
+ }
+
+ it("should survive `null` and `undefined`") {
+ expectF("%s", null).toEqual("null")
+ expectF("%s", js.undefined).toEqual("undefined")
+ }
+
+ it("should allow 'f' string interpolation to survive `null` and `undefined`") {
+ expect(f"${null}%s").toEqual("null")
+ expect(f"${js.undefined}%s").toEqual("undefined")
+ }
+
+ it("should allow positional arguments") {
+ expectF("%2$d %1$d", new JInteger(1), new JInteger(2)).toEqual("2 1")
+ expectF("%2$d %2$d %d", new JInteger(1), new JInteger(2)).toEqual("2 2 1")
+ expectF("%2$d %<d %d", new JInteger(1), new JInteger(2)).toEqual("2 2 1")
+ }
+
+ it("should fail when called after close") {
+ val f = new Formatter()
+ f.close()
+ expect(() => f.toString()).toThrow
+ }
+
+ it("should fail with bad format specifier") {
+ expectThrow("hello world%")
+ expectThrow("%%%")
+ expectThrow("%q")
+ expectThrow("%1")
+ expectThrow("%_f")
+ }
+
+ it("should fail with not enough arguments") {
+ expectThrow("%f")
+ expectThrow("%d%d%d", new JInteger(1), new JInteger(1))
+ expectThrow("%10$d", new JInteger(1))
+ }
+
+ }
+
+
+}
+
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/IntegerTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/IntegerTest.scala
new file mode 100644
index 0000000..5a01de4
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/IntegerTest.scala
@@ -0,0 +1,161 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.javalib
+
+import org.scalajs.jasminetest.JasmineTest
+import scala.scalajs.js
+
+object IntegerTest extends JasmineTest {
+
+ describe("java.lang.Integer") {
+
+ // Explicitly define these as `var`'s to avoid any compile-time constant folding
+ var MaxValue: Int = Int.MaxValue
+ var MinValue: Int = Int.MinValue
+
+ it("should provide `reverseBytes` used by scala.Enumeration") {
+ expect(Integer.reverseBytes(0xdeadbeef)).toEqual(0xefbeadde)
+ }
+
+ it("should provide `rotateLeft`") {
+ expect(Integer.rotateLeft(0x689cd401, 0)).toEqual(0x689cd401)
+ expect(Integer.rotateLeft(0x689cd401, 1)).toEqual(0xd139a802)
+ expect(Integer.rotateLeft(0x689cd401, 8)).toEqual(0x9cd40168)
+ expect(Integer.rotateLeft(0x689cd401, 13)).toEqual(0x9a802d13)
+ expect(Integer.rotateLeft(0x689cd401, 32)).toEqual(0x689cd401)
+ expect(Integer.rotateLeft(0x689cd401, 33)).toEqual(0xd139a802)
+ expect(Integer.rotateLeft(0x689cd401, 43)).toEqual(0xe6a00b44)
+ expect(Integer.rotateLeft(0x689cd401, -1)).toEqual(0xb44e6a00)
+ expect(Integer.rotateLeft(0x689cd401, -28)).toEqual(0x89cd4016)
+ expect(Integer.rotateLeft(0x689cd401, -39)).toEqual(0x2d139a8)
+ }
+
+ it("should provide `rotateRight`") {
+ expect(Integer.rotateRight(0x689cd401, 0)).toEqual(0x689cd401)
+ expect(Integer.rotateRight(0x689cd401, 1)).toEqual(0xb44e6a00)
+ expect(Integer.rotateRight(0x689cd401, 8)).toEqual(0x1689cd4)
+ expect(Integer.rotateRight(0x689cd401, 13)).toEqual(0xa00b44e6)
+ expect(Integer.rotateRight(0x689cd401, 32)).toEqual(0x689cd401)
+ expect(Integer.rotateRight(0x689cd401, 33)).toEqual(0xb44e6a00)
+ expect(Integer.rotateRight(0x689cd401, 43)).toEqual(0x802d139a)
+ expect(Integer.rotateRight(0x689cd401, -1)).toEqual(0xd139a802)
+ expect(Integer.rotateRight(0x689cd401, -28)).toEqual(0x1689cd40)
+ expect(Integer.rotateRight(0x689cd401, -39)).toEqual(0x4e6a00b4)
+ }
+
+ it("should provide `bitCount` used by Map") {
+ abstract sealed class Status
+ case object Used extends Status
+ case object Current extends Status
+ case object OneMove extends Status
+ case object MultipleMoves extends Status
+ case object Other extends Status
+
+ val map = Map(Used -> 0, Other -> 0, Current -> 0, MultipleMoves -> 1, OneMove -> 2)
+
+ expect(map.size).toEqual(5)
+ expect(map(MultipleMoves)).toEqual(1)
+ }
+
+ it("should provide `numberOfTrailingZeros`") {
+ expect(Integer.numberOfTrailingZeros(0xa3c49000)).toEqual(12)
+ expect(Integer.numberOfTrailingZeros(0x43f49020)).toEqual(5)
+ expect(Integer.numberOfTrailingZeros(0x43c08000)).toEqual(15)
+ expect(Integer.numberOfTrailingZeros(0)).toEqual(32)
+ }
+
+ it("should provide `toBinaryString` for values in range") {
+ expect(Integer.toBinaryString(-1)).toEqual("11111111111111111111111111111111")
+ expect(Integer.toBinaryString(-10001)).toEqual("11111111111111111101100011101111")
+ expect(Integer.toBinaryString(MinValue)).toEqual("10000000000000000000000000000000")
+ expect(Integer.toBinaryString(MaxValue)).toEqual("1111111111111111111111111111111")
+ }
+
+ it("should provide `toHexString` for values in range") {
+ expect(Integer.toHexString(-1)).toEqual("ffffffff")
+ expect(Integer.toHexString(-10001)).toEqual("ffffd8ef")
+ expect(Integer.toHexString(MinValue)).toEqual("80000000")
+ expect(Integer.toHexString(-2147000002)).toEqual("8007613e")
+ expect(Integer.toHexString(MaxValue)).toEqual("7fffffff")
+ }
+
+ it("should provide `toOctalString` for values in range") {
+ expect(Integer.toOctalString(-1)).toEqual("37777777777")
+ expect(Integer.toOctalString(-10001)).toEqual("37777754357")
+ expect(Integer.toOctalString(MinValue)).toEqual("20000000000")
+ expect(Integer.toOctalString(MaxValue)).toEqual("17777777777")
+ }
+
+ it("should provide `compareTo`") {
+ def compare(x: Int, y: Int): Int =
+ new Integer(x).compareTo(new Integer(y))
+
+ expect(compare(0, 5)).toBeLessThan(0)
+ expect(compare(10, 9)).toBeGreaterThan(0)
+ expect(compare(-2, -1)).toBeLessThan(0)
+ expect(compare(3, 3)).toEqual(0)
+ }
+
+ it("should be a Comparable") {
+ def compare(x: Any, y: Any): Int =
+ x.asInstanceOf[Comparable[Any]].compareTo(y)
+
+ expect(compare(0, 5)).toBeLessThan(0)
+ expect(compare(10, 9)).toBeGreaterThan(0)
+ expect(compare(-2, -1)).toBeLessThan(0)
+ expect(compare(3, 3)).toEqual(0)
+ }
+
+ it("should parse strings") {
+ def test(s: String, v: Int, radix: Int = 10): Unit = {
+ expect(Integer.parseInt(s, radix)).toEqual(v)
+ expect(Integer.valueOf(s, radix).intValue()).toEqual(v)
+ if (radix == 10)
+ expect(new Integer(s).intValue()).toEqual(v)
+ }
+
+ test("0", 0)
+ test("5", 5)
+ test("127", 127)
+ test("-100", -100)
+ test("30000", 30000)
+ test("-90000", -90000)
+ test("Kona", 411787, 27)
+ test("+42", 42)
+ test("-0", 0)
+ test("-FF", -255, 16)
+ }
+
+ it("should reject invalid strings when parsing") {
+ def test(s: String, radix: Int = 10): Unit =
+ expect(() => Integer.parseInt(s, radix)).toThrow
+
+ test("abc")
+ test("5a")
+ test("2147483648")
+ test("99", 8)
+ test("-")
+ test("")
+ }
+
+ it("should parse strings in base 16") {
+ def test(s: String, v: Int): Unit = {
+ expect(Integer.parseInt(s, 16)).toEqual(v)
+ expect(Integer.valueOf(s, 16).intValue()).toEqual(v)
+ }
+
+ test("0", 0x0)
+ test("5", 0x5)
+ test("ff", 0xff)
+ test("-24", -0x24)
+ test("30000", 0x30000)
+ test("-90000", -0x90000)
+ }
+
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/LongTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/LongTest.scala
new file mode 100644
index 0000000..86783c3
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/LongTest.scala
@@ -0,0 +1,178 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.javalib
+
+import java.lang.{Long => JLong}
+
+import org.scalajs.jasminetest.JasmineTest
+
+/**
+ * tests the implementation of the java standard library Long
+ * requires jsinterop/LongTest to work to make sense
+ */
+object LongTest extends JasmineTest {
+
+ describe("java.lang.Long") {
+ it("should provide `reverseBytes`") {
+ expect(JLong.reverseBytes(0xf5ab689cd401ff14L) == 0x14ff01d49c68abf5L).toBeTruthy
+ }
+
+ it("should provide `rotateLeft`") {
+ expect(JLong.rotateLeft(0xf5ab689cd401ff14L, 0) == 0xf5ab689cd401ff14L).toBeTruthy
+ expect(JLong.rotateLeft(0xf5ab689cd401ff14L, 1) == 0xeb56d139a803fe29L).toBeTruthy
+ expect(JLong.rotateLeft(0xf5ab689cd401ff14L, 8) == 0xab689cd401ff14f5L).toBeTruthy
+ expect(JLong.rotateLeft(0xf5ab689cd401ff14L, 13) == 0x6d139a803fe29eb5L).toBeTruthy
+ expect(JLong.rotateLeft(0xf5ab689cd401ff14L, 64) == 0xf5ab689cd401ff14L).toBeTruthy
+ expect(JLong.rotateLeft(0xf5ab689cd401ff14L, 65) == 0xeb56d139a803fe29L).toBeTruthy
+ expect(JLong.rotateLeft(0xf5ab689cd401ff14L, 80) == 0x689cd401ff14f5abL).toBeTruthy
+ expect(JLong.rotateLeft(0xf5ab689cd401ff14L, -1) == 0x7ad5b44e6a00ff8aL).toBeTruthy
+ expect(JLong.rotateLeft(0xf5ab689cd401ff14L, -56) == 0xab689cd401ff14f5L).toBeTruthy
+ expect(JLong.rotateLeft(0xf5ab689cd401ff14L, -70) == 0x53d6ada2735007fcL).toBeTruthy
+ }
+
+ it("should provide `rotateRight`") {
+ expect(JLong.rotateRight(0xf5ab689cd401ff14L, 0) == 0xf5ab689cd401ff14L).toBeTruthy
+ expect(JLong.rotateRight(0xf5ab689cd401ff14L, 1) == 0x7ad5b44e6a00ff8aL).toBeTruthy
+ expect(JLong.rotateRight(0xf5ab689cd401ff14L, 8) == 0x14f5ab689cd401ffL).toBeTruthy
+ expect(JLong.rotateRight(0xf5ab689cd401ff14L, 13) == 0xf8a7ad5b44e6a00fL).toBeTruthy
+ expect(JLong.rotateRight(0xf5ab689cd401ff14L, 64) == 0xf5ab689cd401ff14L).toBeTruthy
+ expect(JLong.rotateRight(0xf5ab689cd401ff14L, 65) == 0x7ad5b44e6a00ff8aL).toBeTruthy
+ expect(JLong.rotateRight(0xf5ab689cd401ff14L, 80) == 0xff14f5ab689cd401L).toBeTruthy
+ expect(JLong.rotateRight(0xf5ab689cd401ff14L, -1) == 0xeb56d139a803fe29L).toBeTruthy
+ expect(JLong.rotateRight(0xf5ab689cd401ff14L, -56) == 0x14f5ab689cd401ffL).toBeTruthy
+ expect(JLong.rotateRight(0xf5ab689cd401ff14L, -70) == 0x6ada2735007fc53dL).toBeTruthy
+ }
+
+ it("should implement bitCount") {
+ expect(JLong.bitCount(0L)).toEqual(0)
+ expect(JLong.bitCount(35763829229342837L)).toEqual(26)
+ expect(JLong.bitCount(-350003829229342837L)).toEqual(32)
+ }
+
+ it("should provide `compareTo`") {
+ def compare(x: Long, y: Long): Int =
+ new JLong(x).compareTo(new JLong(y))
+
+ expect(compare(0L, 5L)).toBeLessThan(0)
+ expect(compare(10L, 9L)).toBeGreaterThan(0)
+ expect(compare(-2L, -1L)).toBeLessThan(0)
+ expect(compare(3L, 3L)).toEqual(0)
+ }
+
+ it("should be a Comparable") {
+ def compare(x: Any, y: Any): Int =
+ x.asInstanceOf[Comparable[Any]].compareTo(y)
+
+ expect(compare(0L, 5L)).toBeLessThan(0)
+ expect(compare(10L, 9L)).toBeGreaterThan(0)
+ expect(compare(-2L, -1L)).toBeLessThan(0)
+ expect(compare(3L, 3L)).toEqual(0)
+ }
+
+ it("should parse strings") {
+ def test(s: String, v: Long): Unit = {
+ expect(JLong.parseLong(s)).toEqual(v)
+ expect(JLong.valueOf(s).longValue()).toEqual(v)
+ expect(new JLong(s).longValue()).toEqual(v)
+ }
+
+ test("0", 0L)
+ test("5", 5L)
+ test("127", 127L)
+ test("-100", -100L)
+ test("30000", 30000L)
+ test("-90000", -90000L)
+ test("4", 4L)
+ test("-4", -4L)
+ test("4000000000", 4000000000L)
+ test("-18014398509482040", -18014398509482040L)
+ }
+
+ it("should reject invalid strings when parsing") {
+ def test(s: String): Unit =
+ expect(() => JLong.parseLong(s)).toThrow
+
+ test("abc")
+ test("asdf")
+ test("")
+ }
+
+ it("should parse strings in base 16") {
+ def test(s: String, v: Long): Unit = {
+ expect(JLong.parseLong(s, 16)).toEqual(v)
+ expect(JLong.valueOf(s, 16).longValue()).toEqual(v)
+ }
+
+ test("0", 0x0L)
+ test("5", 0x5L)
+ test("ff", 0xffL)
+ test("-24", -0x24L)
+ test("30000", 0x30000L)
+ test("-90000", -0x90000L)
+ }
+
+ it("should implement toString") {
+ expect(Int.MaxValue.toLong.toString).toEqual("2147483647")
+ expect((-50L).toString).toEqual("-50")
+ expect((-1000000000L).toString).toEqual("-1000000000")
+ expect((Int.MaxValue.toLong+1L).toString).toEqual("2147483648")
+ expect(Int.MinValue.toLong.toString).toEqual("-2147483648")
+ }
+
+ it("should implement toBinaryString") {
+ expect(JLong.toBinaryString( 0L)).toEqual("0")
+ expect(JLong.toBinaryString( -1L)).toEqual("1111111111111111111111111111111111111111111111111111111111111111")
+ expect(JLong.toBinaryString( 456324454L)).toEqual("11011001100101111010101100110")
+ expect(JLong.toBinaryString( -456324454L)).toEqual("1111111111111111111111111111111111100100110011010000101010011010")
+ expect(JLong.toBinaryString( 98765432158845L)).toEqual("10110011101001110011110011111111111101001111101")
+ expect(JLong.toBinaryString(-49575304457780L)).toEqual("1111111111111111110100101110100101011001100101101001000111001100")
+ expect(JLong.toBinaryString(Long.MinValue )).toEqual("1000000000000000000000000000000000000000000000000000000000000000")
+ expect(JLong.toBinaryString(Long.MaxValue )).toEqual("111111111111111111111111111111111111111111111111111111111111111")
+ }
+
+ it("should implement toHexString") {
+ expect(JLong.toHexString( 0L)).toEqual("0")
+ expect(JLong.toHexString( -1L)).toEqual("ffffffffffffffff")
+ expect(JLong.toHexString( 456324454L)).toEqual("1b32f566")
+ expect(JLong.toHexString( -456324454L)).toEqual("ffffffffe4cd0a9a")
+ expect(JLong.toHexString( 98765432158845L)).toEqual("59d39e7ffa7d")
+ expect(JLong.toHexString(-49575304457780L)).toEqual("ffffd2e9599691cc")
+ expect(JLong.toHexString(Long.MinValue )).toEqual("8000000000000000")
+ expect(JLong.toHexString(Long.MaxValue )).toEqual("7fffffffffffffff")
+ }
+
+ it("should implement toOctalString") {
+ expect(JLong.toOctalString( 0L)).toEqual("0")
+ expect(JLong.toOctalString( -1L)).toEqual("1777777777777777777777")
+ expect(JLong.toOctalString( 456324454L)).toEqual("3314572546")
+ expect(JLong.toOctalString( -456324454L)).toEqual("1777777777774463205232")
+ expect(JLong.toOctalString( 98765432158845L)).toEqual("2635163637775175")
+ expect(JLong.toOctalString(-49575304457780L)).toEqual("1777776456453145510714")
+ expect(JLong.toOctalString(Long.MinValue )).toEqual("1000000000000000000000")
+ expect(JLong.toOctalString(Long.MaxValue )).toEqual("777777777777777777777")
+ }
+
+ it("should correctly compute trailing zeros") {
+ expect(JLong.numberOfTrailingZeros(0xff10000000000000L)).toEqual(52)
+ expect(JLong.numberOfTrailingZeros(0xff20000000000000L)).toEqual(53)
+ expect(JLong.numberOfTrailingZeros(0xff40000000000000L)).toEqual(54)
+ expect(JLong.numberOfTrailingZeros(0xff80000000000000L)).toEqual(55)
+
+ expect(JLong.numberOfTrailingZeros(0x0000010000000000L)).toEqual(40)
+ expect(JLong.numberOfTrailingZeros(0x0000020000000000L)).toEqual(41)
+ expect(JLong.numberOfTrailingZeros(0x0000040000000000L)).toEqual(42)
+ expect(JLong.numberOfTrailingZeros(0x0000080000000000L)).toEqual(43)
+
+ expect(JLong.numberOfTrailingZeros(0x0000000000010000L)).toEqual(16)
+ expect(JLong.numberOfTrailingZeros(0x0000000000020000L)).toEqual(17)
+ expect(JLong.numberOfTrailingZeros(0x0000000000040000L)).toEqual(18)
+ expect(JLong.numberOfTrailingZeros(0x0000000000080000L)).toEqual(19)
+ }
+
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/MathTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/MathTest.scala
new file mode 100644
index 0000000..a3c887a
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/MathTest.scala
@@ -0,0 +1,142 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.javalib
+
+import scala.scalajs.js
+import org.scalajs.jasminetest.JasmineTest
+import java.lang.Math
+
+object MathTest extends JasmineTest {
+
+ describe("java.lang.Math") {
+
+ it("should respond to `cbrt`") {
+ expect(1 / Math.cbrt(-0.0) < 0).toBeTruthy
+ expect(Math.cbrt(27.0)).toEqual(3.0)
+ expect(Math.cbrt(1000000.0)).toEqual(100.0)
+ expect(Math.cbrt(1000000000.0)).toEqual(1000.0)
+ expect(Math.cbrt(-1.0E24)).toEqual(-100000000.0)
+ expect(Math.cbrt(-65890311319.0E24)).toEqual(-4039.0E8)
+ }
+
+ it("should respond to `log1p`") {
+ expect(Math.log1p(-2.0).isNaN).toBeTruthy
+ expect(Math.log1p(js.Number.NaN.toDouble).isNaN).toBeTruthy
+ expect(Math.log1p(0.0)).toEqual(0.0)
+ }
+
+ it("should respond to `log10`") {
+ expect(Math.log10(-230.0).isNaN).toBeTruthy
+ expect(Math.log10(js.Number.NaN.toDouble).isNaN).toBeTruthy
+ }
+
+ it("should respond to `signum` for Double") {
+ expect(Math.signum(234394.2198273)).toEqual(1.0)
+ expect(Math.signum(-124937498.58)).toEqual(-1.0)
+
+ expect(Math.signum(+0.0)).toEqual(0.0)
+ expect(1 / Math.signum(+0.0) > 0).toBeTruthy
+
+ expect(Math.signum(-0.0)).toEqual(-0.0)
+ expect(1 / Math.signum(-0.0) < 0).toBeTruthy
+
+ expect(Math.signum(js.Number.NaN.toDouble).isNaN).toBeTruthy
+ }
+
+ it("should respond to `signum` for Float") {
+ expect(Math.signum(234394.2198273f)).toEqual(1.0f)
+ expect(Math.signum(-124937498.58f)).toEqual(-1.0f)
+
+ expect(Math.signum(+0.0f)).toEqual(0.0f)
+ expect(1 / Math.signum(+0.0f) > 0).toBeTruthy
+
+ expect(Math.signum(-0.0f)).toEqual(-0.0f)
+ expect(1 / Math.signum(-0.0f) < 0).toBeTruthy
+
+ expect(Math.signum(js.Number.NaN.toFloat).isNaN).toBeTruthy
+ }
+
+ it("should respond to `nextUp` for Double") {
+ expect(Math.nextUp(Double.PositiveInfinity)).toEqual(Double.PositiveInfinity)
+ expect(Math.nextUp(Double.NegativeInfinity)).toEqual(-Double.MaxValue)
+ expect(Math.nextUp(Double.MaxValue)).toEqual(Double.PositiveInfinity)
+ expect(Math.nextUp(-Double.MaxValue)).toEqual(-1.7976931348623155e+308)
+ expect(Math.nextUp(-Double.MinValue)).toEqual(Double.PositiveInfinity)
+ expect(Math.nextUp(0.0)).toEqual(Double.MinValue)
+ expect(Math.nextUp(-0.0)).toEqual(Double.MinValue)
+ expect(Math.nextUp(9007199254740991.0)).toEqual(9007199254740992.0)
+ expect(Math.nextUp(9007199254740992.0)).toEqual(9007199254740994.0)
+ expect(Math.nextUp(1.0)).toEqual(1 + 2.2204460492503130808472633361816E-16)
+ }
+
+ it("should respond to `nextAfter` for Double") {
+ expect(Math.nextAfter(1.0, js.Number.NaN.toDouble).isNaN).toBeTruthy
+ expect(Math.nextAfter(js.Number.NaN.toDouble, 1.0).isNaN).toBeTruthy
+ expect(Math.nextAfter(0.0, 0.0)).toEqual(0.0)
+ expect(Math.nextAfter(0.0, -0.0)).toEqual(-0.0)
+ expect(Math.nextAfter(-0.0, 0.0)).toEqual(0.0)
+ expect(Math.nextAfter(-0.0, -0.0)).toEqual(-0.0)
+ expect(Math.nextAfter(Double.MinValue, Double.NegativeInfinity)).toEqual(Double.NegativeInfinity)
+ expect(Math.nextAfter(-Double.MinValue, Double.PositiveInfinity)).toEqual(Double.PositiveInfinity)
+ expect(Math.nextAfter(Double.PositiveInfinity, Double.NegativeInfinity)).toEqual(Double.MaxValue)
+ expect(Math.nextAfter(Double.NegativeInfinity, Double.PositiveInfinity)).toEqual(-Double.MaxValue)
+ expect(Math.nextAfter(Double.MaxValue, Double.PositiveInfinity)).toEqual(Double.PositiveInfinity)
+ expect(Math.nextAfter(-Double.MaxValue, Double.NegativeInfinity)).toEqual(Double.NegativeInfinity)
+ expect(Math.nextAfter(1.0, 1.0)).toEqual(1.0)
+ }
+
+ it("should respond to `ulp` for Double") {
+ expect(Math.ulp(3.4)).toEqual(4.440892098500626E-16)
+ expect(Math.ulp(3.423E109)).toEqual(4.1718496795330275E93)
+ expect(Math.ulp(0.0)).toEqual(Double.MinValue)
+ }
+
+ it("should respond to `hypot`") {
+ expect(Math.hypot(0.0, 0.0)).toBeCloseTo(0.0)
+ expect(Math.hypot(3.0, 4.0)).toBeCloseTo(5.0)
+ expect(Math.hypot(3.0, js.Number.NaN.toDouble).isNaN).toBeTruthy
+ expect(Math.hypot(Double.NegativeInfinity, 4.0)).toEqual(Double.PositiveInfinity)
+ }
+
+ it("should respond to `expm1`") {
+ expect(1 / Math.expm1(-0.0) < 0).toBeTruthy
+ expect(Math.expm1(-0.0)).toBeCloseTo(0.0)
+ expect(Math.expm1(3.0)).toBeCloseTo(19.085536923187668)
+ expect(Math.expm1(15.0)).toBeCloseTo(3269016.3724721107)
+ expect(Math.expm1(1.8E10)).toEqual(Double.PositiveInfinity)
+ expect(Math.expm1(Double.PositiveInfinity)).toEqual(Double.PositiveInfinity)
+ expect(Math.expm1(Double.NegativeInfinity)).toBeCloseTo(-1.0)
+ expect(Math.expm1(4.9E-324)).toBeCloseTo(4.9E-324)
+ }
+
+ it("should respond to `sinh`") {
+ expect(Math.sinh(-1234.56)).toEqual(Double.NegativeInfinity)
+ expect(Math.sinh(1234.56)).toEqual(Double.PositiveInfinity)
+ expect(Math.sinh(0.0)).toBeCloseTo(0.0)
+ expect(Math.sinh(Double.PositiveInfinity)).toEqual(Double.PositiveInfinity)
+ }
+
+ it("should respond to `cosh`") {
+ expect(Math.cosh(-1234.56)).toEqual(Double.PositiveInfinity)
+ expect(Math.cosh(1234.56)).toEqual(Double.PositiveInfinity)
+ expect(Math.cosh(-0.0)).toBeCloseTo(1.0)
+ expect(Math.cosh(Double.PositiveInfinity)).toEqual(Double.PositiveInfinity)
+ }
+
+ it("should respond to `tanh`") {
+ expect(Math.tanh(-1234.56)).toBeCloseTo(-1.0)
+ expect(Math.tanh(-120.56)).toBeCloseTo(-1.0)
+ expect(Math.tanh(1234.56)).toBeCloseTo(1.0)
+ expect(Math.tanh(0.0)).toBeCloseTo(0.0)
+ expect(Math.tanh(Double.PositiveInfinity)).toBeCloseTo(1.0)
+ expect(Math.tanh(Double.NegativeInfinity)).toBeCloseTo(-1.0)
+ }
+
+ }
+
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/MockByteArrayOutputStream.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/MockByteArrayOutputStream.scala
new file mode 100644
index 0000000..3356a85
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/MockByteArrayOutputStream.scala
@@ -0,0 +1,47 @@
+package scala.scalajs.testsuite.javalib
+
+import java.io._
+
+/** A ByteArrayOutputStream that exposes various hooks for testing purposes. */
+class MockByteArrayOutputStream extends ByteArrayOutputStream {
+ private var _flushed: Boolean = true
+ private var _closed: Boolean = false
+
+ var throwing: Boolean = false
+
+ def flushed: Boolean = _flushed
+ def closed: Boolean = _closed
+
+ private def maybeThrow(): Unit = {
+ if (throwing)
+ throw new IOException("MockByteArrayOutputStream throws")
+ }
+
+ private def writeOp[A](op: => A): A = {
+ maybeThrow()
+ _flushed = false
+ op
+ }
+
+ override def flush(): Unit = {
+ maybeThrow()
+ super.flush()
+ _flushed = true
+ }
+
+ override def close(): Unit = {
+ maybeThrow()
+ super.close()
+ _closed = true
+ }
+
+ override def write(c: Int): Unit =
+ writeOp(super.write(c))
+
+ override def write(b: Array[Byte]): Unit =
+ writeOp(super.write(b))
+
+ override def write(b: Array[Byte], off: Int, len: Int): Unit =
+ writeOp(super.write(b, off, len))
+
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ObjectTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ObjectTest.scala
new file mode 100644
index 0000000..3584454
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ObjectTest.scala
@@ -0,0 +1,72 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.javalib
+
+import org.scalajs.jasminetest.JasmineTest
+
+import scala.scalajs.js
+
+object ObjectTest extends JasmineTest {
+
+ describe("java.lang.Object") {
+
+ it("should provide `equals`") {
+ case class xy(x: Int, y: Int)
+
+ val l = List(xy(1, 2), xy(2, 1))
+ val xy12 = xy(1, 2)
+
+ expect(l.contains(xy12)).toBeTruthy
+ expect(l.exists(_ == xy12)).toBeTruthy // the workaround
+ }
+
+ it("everything but null should be an Object") {
+ expect((() : Any).isInstanceOf[Object]).toBeTruthy
+ expect((true : Any).isInstanceOf[Object]).toBeTruthy
+ expect(('a' : Any).isInstanceOf[Object]).toBeTruthy
+ expect((1.toByte : Any).isInstanceOf[Object]).toBeTruthy
+ expect((658.toShort : Any).isInstanceOf[Object]).toBeTruthy
+ expect((60000 : Any).isInstanceOf[Object]).toBeTruthy
+ expect((12345678910112L: Any).isInstanceOf[Object]).toBeTruthy
+ expect((6.5f : Any).isInstanceOf[Object]).toBeTruthy
+ expect((12.4 : Any).isInstanceOf[Object]).toBeTruthy
+ expect((new Object : Any).isInstanceOf[Object]).toBeTruthy
+ expect(("hello" : Any).isInstanceOf[Object]).toBeTruthy
+ expect((List(1) : Any).isInstanceOf[Object]).toBeTruthy
+ expect((Array(1) : Any).isInstanceOf[Object]).toBeTruthy
+ expect((Array(Nil) : Any).isInstanceOf[Object]).toBeTruthy
+ expect((new js.Object : Any).isInstanceOf[Object]).toBeTruthy
+ expect((js.Array(5) : Any).isInstanceOf[Object]).toBeTruthy
+ }
+
+ it("null should not be an Object") {
+ expect((null: Any).isInstanceOf[Object]).toBeFalsy
+ }
+
+ it("everything should cast to Object successfully, including null") {
+ expect(() => (() : Any).asInstanceOf[Object]).not.toThrow
+ expect(() => (true : Any).asInstanceOf[Object]).not.toThrow
+ expect(() => ('a' : Any).asInstanceOf[Object]).not.toThrow
+ expect(() => (1.toByte : Any).asInstanceOf[Object]).not.toThrow
+ expect(() => (658.toShort : Any).asInstanceOf[Object]).not.toThrow
+ expect(() => (60000 : Any).asInstanceOf[Object]).not.toThrow
+ expect(() => (12345678910112L: Any).asInstanceOf[Object]).not.toThrow
+ expect(() => (6.5f : Any).asInstanceOf[Object]).not.toThrow
+ expect(() => (12.4 : Any).asInstanceOf[Object]).not.toThrow
+ expect(() => (new Object : Any).asInstanceOf[Object]).not.toThrow
+ expect(() => ("hello" : Any).asInstanceOf[Object]).not.toThrow
+ expect(() => (List(1) : Any).asInstanceOf[Object]).not.toThrow
+ expect(() => (Array(1) : Any).asInstanceOf[Object]).not.toThrow
+ expect(() => (Array(Nil) : Any).asInstanceOf[Object]).not.toThrow
+ expect(() => (new js.Object : Any).asInstanceOf[Object]).not.toThrow
+ expect(() => (js.Array(5) : Any).asInstanceOf[Object]).not.toThrow
+ expect(() => (null : Any).asInstanceOf[Object]).not.toThrow
+ }
+
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/OutputStreamWriterTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/OutputStreamWriterTest.scala
new file mode 100644
index 0000000..7987a4c
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/OutputStreamWriterTest.scala
@@ -0,0 +1,134 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.javalib
+
+import java.io._
+
+import scala.scalajs.js
+import js.JSConverters._
+import org.scalajs.jasminetest.JasmineTest
+
+object OutputStreamWriterTest extends JasmineTest {
+ private def newOSWriter(): (OutputStreamWriter, MockByteArrayOutputStream) = {
+ val bos = new MockByteArrayOutputStream
+ val osw = new OutputStreamWriter(bos)
+ (osw, bos)
+ }
+
+ describe("java.io.OutputStreamWriter") {
+ it("flush") {
+ val (osw, bos) = newOSWriter()
+ bos.write(1)
+ osw.write("ABC")
+ expect(bos.flushed).toBeFalsy
+ osw.flush()
+ expect(bos.flushed).toBeTruthy
+ }
+
+ it("close") {
+ val (osw, bos) = newOSWriter()
+ bos.write(1)
+ osw.write("ABC")
+ expect(bos.flushed).toBeFalsy
+
+ osw.close()
+ expect(bos.flushed).toBeTruthy
+ expect(bos.closed).toBeTruthy
+
+ // can double-close without error
+ osw.close()
+
+ // when closed, other operations cause error
+ expect(() => osw.write('A')).toThrow
+ expect(() => osw.write("never printed")).toThrow
+ expect(() => osw.write(Array('a', 'b'))).toThrow
+ expect(() => osw.append("hello", 1, 3)).toThrow
+ expect(() => osw.flush()).toThrow
+
+ // at the end of it all, bos is still what it was when it was closed
+ expect(bos.toByteArray().toJSArray).toEqual(js.Array(1, 65, 66, 67))
+ }
+
+ def testW(body: OutputStreamWriter => Unit,
+ expected: js.Array[Int], alreadyFlushed: Boolean = false): Unit = {
+ val (osw, bos) = newOSWriter()
+ body(osw)
+ if (!alreadyFlushed) {
+ expect(bos.size).toEqual(0) // write() methods should buffer
+ osw.flush()
+ }
+ expect(bos.flushed).toBeTruthy
+ expect(bos.toByteArray.toJSArray).toEqual(expected.map(_.toByte))
+ }
+
+ it("write(), ASCII repertoire") {
+ // Pure ASCII
+ testW(_.write('\n'), js.Array('\n'))
+ testW(_.write("hello\n"), js.Array('h', 'e', 'l', 'l', 'o', '\n'))
+ testW(_.write("hello\nworld", 3, 4), js.Array('l', 'o', '\n', 'w'))
+ testW(_.write(Array('A', '\n')), js.Array('A', '\n'))
+ testW(_.write(Array('A', 'B', '\n', 'C'), 1, 2), js.Array('B', '\n'))
+ }
+
+ it("write(), Unicode repertoire without surrogates") {
+ testW(_.write('é'), js.Array(0xc3, 0xa9))
+ testW(_.write("こんにちは"), js.Array(
+ 0xe3, 0x81, 0x93, 0xe3, 0x82, 0x93, 0xe3, 0x81, 0xab, 0xe3, 0x81, 0xa1, 0xe3, 0x81, 0xaf))
+ testW(_.write("Καλημέρα", 3, 4), js.Array(
+ 0xce, 0xb7, 0xce, 0xbc, 0xce, 0xad, 0xcf, 0x81))
+ }
+
+ it("write(), surrogate pairs") {
+ testW(_.write("\ud83d\udca9"), js.Array(0xf0, 0x9f, 0x92, 0xa9))
+ testW(_.write("ab\ud83d\udca9cd", 1, 3), js.Array('b', 0xf0, 0x9f, 0x92, 0xa9))
+ }
+
+ it("write(), surrogate pairs spread across multiple writes") {
+ testW({ osw => osw.write('\ud83d'); osw.write('\udca9') },
+ js.Array(0xf0, 0x9f, 0x92, 0xa9))
+
+ testW({ osw => osw.write('\ud83d'); osw.flush(); osw.write('\udca9') },
+ js.Array(0xf0, 0x9f, 0x92, 0xa9))
+
+ testW({ osw => osw.write("ab\ud83d"); osw.write('\udca9') },
+ js.Array('a', 'b', 0xf0, 0x9f, 0x92, 0xa9))
+
+ testW({ osw => osw.write("ab\ud83d"); osw.write("\udca9cd") },
+ js.Array('a', 'b', 0xf0, 0x9f, 0x92, 0xa9, 'c', 'd'))
+
+ testW({ osw => osw.write("ab\ud83dzz", 1, 2); osw.write("ww\udca9cd", 2, 2) },
+ js.Array('b', 0xf0, 0x9f, 0x92, 0xa9, 'c'))
+ }
+
+ it("write(), malformed surrogates") {
+ testW(_.write("\ud83da"), js.Array('?', 'a'))
+ testW(_.write("\udca9"), js.Array('?'))
+ }
+
+ it("write(), malformed surrogates spread across multiple writes") {
+ testW({ osw => osw.write('\ud83d'); osw.write('a') },
+ js.Array('?', 'a'))
+
+ testW({ osw => osw.write("ab\ud83d"); osw.write("\ud83d") },
+ js.Array('a', 'b', '?'))
+
+ testW({ osw => osw.write("ab\ud83d"); osw.write("\ud83dc") },
+ js.Array('a', 'b', '?', '?', 'c'))
+ }
+
+ it("write(), malformed surrogates at end of input") {
+ testW({ osw => osw.write('\ud83d'); osw.close() },
+ js.Array('?'), alreadyFlushed = true)
+
+ testW({ osw => osw.write("ab\ud83d"); osw.close() },
+ js.Array('a', 'b', '?'), alreadyFlushed = true)
+ }
+
+ }
+
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/PrintStreamTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/PrintStreamTest.scala
new file mode 100644
index 0000000..01c872b
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/PrintStreamTest.scala
@@ -0,0 +1,296 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.javalib
+
+import java.io._
+
+import scala.scalajs.js
+import js.JSConverters._
+import org.scalajs.jasminetest.JasmineTest
+
+object PrintStreamTest extends JasmineTest {
+ private def newPrintStream(
+ autoFlush: Boolean = false): (MockPrintStream, MockByteArrayOutputStream) = {
+ val bos = new MockByteArrayOutputStream
+ val ps = new MockPrintStream(bos, autoFlush)
+ (ps, bos)
+ }
+
+ describe("java.io.PrintStream") {
+ it("flush") {
+ val (ps, bos) = newPrintStream()
+ ps.print("hello")
+ expect(bos.flushed).toBeFalsy
+ ps.flush()
+ expect(bos.flushed).toBeTruthy
+ }
+
+ it("close") {
+ val (ps, bos) = newPrintStream()
+ ps.write(Array[Byte](1))
+ expect(bos.flushed).toBeFalsy
+
+ ps.close()
+ expect(bos.flushed).toBeTruthy
+ expect(bos.closed).toBeTruthy
+ expect(ps.checkError()).toBeFalsy
+
+ // can double-close without error
+ ps.close()
+ expect(ps.checkError()).toBeFalsy
+ ps.clearError()
+
+ // when closed, other operations cause error
+ def expectCausesError(body: => Unit): Unit = {
+ body
+ expect(ps.checkError()).toBeTruthy
+ ps.clearError()
+ }
+ expectCausesError(ps.print("never printed"))
+ expectCausesError(ps.write(Array[Byte]('a', 'b')))
+ expectCausesError(ps.append("hello", 1, 3))
+ expectCausesError(ps.flush())
+
+ // at the end of it all, bos is still what it was when it was closed
+ expect(bos.toByteArray.toJSArray).toEqual(js.Array(1))
+ }
+
+ it("write, pass the bytes through") {
+ def test(body: PrintStream => Unit, expected: js.Array[Int],
+ testFlushed: Boolean = false): Unit = {
+ val (ps, bos) = newPrintStream(autoFlush = true)
+ body(ps)
+ if (testFlushed)
+ expect(bos.flushed).toBeTruthy
+ expect(ps.checkError()).toBeFalsy
+ expect(bos.toByteArray.toJSArray).toEqual(expected.map(_.toByte))
+ }
+
+ test(_.write('a'), js.Array('a'))
+ test(_.write('\n'), js.Array('\n'), testFlushed = true)
+ test(_.write(Array[Byte]('A', '\n')),
+ js.Array('A', '\n'), testFlushed = true)
+ test(_.write(Array[Byte]('A', 'B', '\n', 'C'), 1, 2),
+ js.Array('B', '\n'), testFlushed = true)
+
+ test(_.write('é'.toByte), js.Array('é'))
+ test(_.write(Array[Byte]('é'.toByte, 'à'.toByte)), js.Array('é', 'à'))
+ }
+
+ it("print") {
+ def test(body: PrintStream => Unit, expected: String,
+ testFlushed: Boolean = false): Unit = {
+ val (ps, bos) = newPrintStream(autoFlush = true)
+ body(ps)
+ if (testFlushed)
+ expect(bos.flushed).toBeTruthy
+ expect(ps.checkError()).toBeFalsy
+ expect(bos.toString()).toBe(expected)
+ }
+
+ test(_.print(true), "true")
+ test(_.print('Z'), "Z")
+ test(_.print('\n'), "\n", testFlushed = true)
+ test(_.print(5), "5")
+ test(_.print(1234567891011L), "1234567891011")
+ test(_.print(1.5f), "1.5")
+ test(_.print(Math.PI), "3.141592653589793")
+ test(_.print(Array('A', '\n')), "A\n", testFlushed = true)
+ test(_.print("hello\n"), "hello\n", testFlushed = true)
+ test(_.print(null: String), "null")
+ test(_.print((1, 2)), "(1,2)")
+ test(_.print(null: AnyRef), "null")
+ }
+
+ it("print encodes in UTF-8") {
+ def test(body: PrintStream => Unit, expected: js.Array[Int]): Unit = {
+ val (ps, bos) = newPrintStream(autoFlush = false)
+ body(ps)
+ expect(ps.checkError()).toBeFalsy
+ expect(bos.toByteArray.toJSArray).toEqual(expected.map(_.toByte))
+ }
+
+ test(_.print('é'), js.Array(0xc3, 0xa9))
+ test(_.print("こんにちは"), js.Array(
+ 0xe3, 0x81, 0x93, 0xe3, 0x82, 0x93, 0xe3, 0x81, 0xab, 0xe3, 0x81, 0xa1, 0xe3, 0x81, 0xaf))
+ test(_.print("ημέρ"), js.Array(
+ 0xce, 0xb7, 0xce, 0xbc, 0xce, 0xad, 0xcf, 0x81))
+
+ test(_.print("\ud83d\udca9"), js.Array(0xf0, 0x9f, 0x92, 0xa9))
+ test(_.print("b\ud83d\udca9c"), js.Array('b', 0xf0, 0x9f, 0x92, 0xa9, 'c'))
+
+ test({ osw => osw.print("ab\ud83d"); osw.print('\udca9') },
+ js.Array('a', 'b', 0xf0, 0x9f, 0x92, 0xa9))
+
+ test({ osw => osw.print("ab\ud83d"); osw.print("\udca9cd") },
+ js.Array('a', 'b', 0xf0, 0x9f, 0x92, 0xa9, 'c', 'd'))
+
+ // Start of malformed sequences
+
+ test(_.print("\ud83da"), js.Array('?', 'a'))
+ test(_.print("\udca9"), js.Array('?'))
+
+ test({ osw => osw.print('\ud83d'); osw.print('a') },
+ js.Array('?', 'a'))
+
+ test({ osw => osw.print("ab\ud83d"); osw.print("\ud83d") },
+ js.Array('a', 'b', '?'))
+
+ test({ osw => osw.print("ab\ud83d"); osw.print("\ud83dc") },
+ js.Array('a', 'b', '?', '?', 'c'))
+
+ test({ osw => osw.print('\ud83d'); osw.close() },
+ js.Array('?'))
+
+ test({ osw => osw.print("ab\ud83d"); osw.close() },
+ js.Array('a', 'b', '?'))
+ }
+
+ for (autoFlush <- Seq(true, false)) {
+ val title =
+ if (autoFlush) "println forwards and flushes when autoFlush is true"
+ else "println forwards, does not flush when autoFlush is false"
+ it(title) {
+ def test(body: PrintStream => Unit, expected: String): Unit = {
+ val (ps, bos) = newPrintStream(autoFlush = autoFlush)
+ body(ps)
+ if (autoFlush) expect(bos.flushed).toBeTruthy
+ else expect(bos.flushed).toBeFalsy
+ expect(ps.checkError()).toBeFalsy
+ expect(bos.toString()).toBe(expected)
+ }
+
+ test(_.println(), "\n")
+ test(_.println(true), "true\n")
+ test(_.println('Z'), "Z\n")
+ test(_.println('\n'), "\n\n")
+ test(_.println(5), "5\n")
+ test(_.println(1234567891011L), "1234567891011\n")
+ test(_.println(1.5f), "1.5\n")
+ test(_.println(Math.PI), "3.141592653589793\n")
+ test(_.println(Array('A', '\n')), "A\n\n")
+ test(_.println("hello\n"), "hello\n\n")
+ test(_.println(null: String), "null\n")
+ test(_.println((1, 2)), "(1,2)\n")
+ test(_.println(null: AnyRef), "null\n")
+ }
+ }
+
+ for (autoFlush <- Seq(true, false)) {
+ val title =
+ if (autoFlush) "printf/format, which flushes when autoFlush is true"
+ else "printf/format, does not flush when autoFlush is false"
+ it(title) {
+ def test(body: PrintStream => Unit, expected: String): Unit = {
+ val (ps, bos) = newPrintStream(autoFlush = autoFlush)
+ body(ps)
+ if (autoFlush) expect(bos.flushed).toBeTruthy
+ else expect(bos.flushed).toBeFalsy
+ expect(ps.checkError()).toBeFalsy
+ expect(bos.toString()).toBe(expected)
+ }
+
+ test(_.printf("%04d", Int.box(5)), "0005")
+ test(_.format("%.5f", Double.box(Math.PI)), "3.14159")
+ }
+ }
+
+ it("append") {
+ def test(body: PrintStream => Unit, expected: String,
+ testFlushed: Boolean = false): Unit = {
+ val (ps, bos) = newPrintStream(autoFlush = true)
+ body(ps)
+ if (testFlushed)
+ expect(bos.flushed).toBeTruthy
+ expect(ps.checkError()).toBeFalsy
+ expect(bos.toString()).toBe(expected)
+ }
+
+ test(_.append("hello\n"), "hello\n", testFlushed = true)
+ test(_.append(null: CharSequence), "null")
+ test(_.append("hello\nworld", 3, 6), "lo\n", testFlushed = true)
+ test(_.append(null: CharSequence, 1, 2), "u")
+ test(_.append('A'), "A")
+ test(_.append('\n'), "\n", testFlushed = true)
+ }
+
+ it("traps all IOException and updates checkError") {
+ def test(body: PrintStream => Unit): Unit = {
+ val (ps, bos) = newPrintStream()
+ bos.throwing = true
+ body(ps)
+ expect(ps.checkError()).toBeTruthy
+ }
+
+ test(_.flush())
+ test(_.close())
+
+ test(_.write('Z'))
+ test(_.write(Array[Byte]('A', 'B')))
+ test(_.write(Array[Byte]('A', 'B'), 1, 1))
+
+ test(_.print(true))
+ test(_.print('Z'))
+ test(_.print('\n'))
+ test(_.print(5))
+ test(_.print(1234567891011L))
+ test(_.print(1.5f))
+ test(_.print(Math.PI))
+ test(_.print(Array('A', '\n')))
+ test(_.print("hello\n"))
+ test(_.print(null: String))
+ test(_.print((1, 2)))
+ test(_.print(null: AnyRef))
+
+ test(_.println())
+ test(_.println(true))
+ test(_.println('Z'))
+ test(_.println('\n'))
+ test(_.println(5))
+ test(_.println(1234567891011L))
+ test(_.println(1.5f))
+ test(_.println(Math.PI))
+ test(_.println(Array('A', '\n')))
+ test(_.println("hello\n"))
+ test(_.println(null: String))
+ test(_.println((1, 2)))
+ test(_.println(null: AnyRef))
+
+ test(_.append("hello\n"))
+ test(_.append(null: CharSequence))
+ test(_.append("hello\nworld", 3, 6))
+ test(_.append(null: CharSequence, 1, 2))
+ test(_.append('A'))
+ test(_.append('\n'))
+ }
+
+ it("write short-circuits pending high surrogates in print") {
+ val (ps, bos) = newPrintStream()
+ ps.print('A')
+ expect(bos.toByteArray.toJSArray).toEqual(js.Array[Byte]('A'))
+ ps.print('\ud83d')
+ expect(bos.toByteArray.toJSArray).toEqual(js.Array[Byte]('A'))
+ ps.flush()
+ expect(bos.toByteArray.toJSArray).toEqual(js.Array[Byte]('A'))
+ ps.write('Z')
+ expect(bos.toByteArray.toJSArray).toEqual(js.Array[Byte]('A', 'Z'))
+ ps.print('\udca9')
+ expect(bos.toByteArray.toJSArray).toEqual(js.Array[Byte](
+ 'A', 'Z', -16, -97, -110, -87))
+ }
+ }
+
+ /** A PrintStream that exposes various hooks for testing purposes. */
+ private class MockPrintStream(out: OutputStream,
+ autoFlush: Boolean) extends PrintStream(out, autoFlush) {
+ def this(out: OutputStream) = this(out, false)
+
+ override def clearError(): Unit = super.clearError()
+ }
+
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/PrintWriterTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/PrintWriterTest.scala
new file mode 100644
index 0000000..b2b3309
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/PrintWriterTest.scala
@@ -0,0 +1,287 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.javalib
+
+import scala.language.implicitConversions
+
+import java.io._
+
+import scala.scalajs.js
+import org.scalajs.jasminetest.JasmineTest
+
+object PrintWriterTest extends JasmineTest {
+ private def newPrintWriter(
+ autoFlush: Boolean = false): (MockPrintWriter, MockStringWriter) = {
+ val sw = new MockStringWriter
+ val pw = new MockPrintWriter(sw, autoFlush)
+ (pw, sw)
+ }
+
+ describe("java.io.PrintWriter") {
+ it("flush") {
+ val (pw, sw) = newPrintWriter()
+ pw.print("hello")
+ expect(sw.flushed).toBeFalsy
+ pw.flush()
+ expect(sw.flushed).toBeTruthy
+ }
+
+ it("close") {
+ val (pw, sw) = newPrintWriter()
+ pw.write("begin")
+ expect(sw.flushed).toBeFalsy
+
+ pw.close()
+ expect(sw.flushed).toBeTruthy
+ expect(sw.closed).toBeTruthy
+ expect(pw.checkError()).toBeFalsy
+
+ // can double-close without error
+ pw.close()
+ expect(pw.checkError()).toBeFalsy
+ pw.clearError()
+
+ // when closed, other operations cause error
+ def expectCausesError(body: => Unit): Unit = {
+ body
+ expect(pw.checkError()).toBeTruthy
+ pw.clearError()
+ }
+ expectCausesError(pw.print("never printed"))
+ expectCausesError(pw.write(Array('a', 'b')))
+ expectCausesError(pw.append("hello", 1, 3))
+ expectCausesError(pw.flush())
+
+ // at the end of it all, sw is still what it was when it was closed
+ expect(sw.toString()).toBe("begin")
+ }
+
+ it("write, does not flush even with \\n") {
+ def test(body: PrintWriter => Unit, expected: String): Unit = {
+ val (pw, sw) = newPrintWriter(autoFlush = true)
+ body(pw)
+ expect(sw.flushed).toBeFalsy
+ expect(pw.checkError()).toBeFalsy
+ expect(sw.toString()).toBe(expected)
+ }
+
+ test(_.write('\n'), "\n")
+ test(_.write("hello\n"), "hello\n")
+ test(_.write("hello\nworld", 3, 3), "lo\n")
+ test(_.write(Array('A', '\n')), "A\n")
+ test(_.write(Array('A', 'B', '\n', 'C'), 1, 2), "B\n")
+ }
+
+ it("print, does not flush even with \\n") {
+ def test(body: PrintWriter => Unit, expected: String): Unit = {
+ val (pw, sw) = newPrintWriter(autoFlush = true)
+ body(pw)
+ expect(sw.flushed).toBeFalsy
+ expect(pw.checkError()).toBeFalsy
+ expect(sw.toString()).toBe(expected)
+ }
+
+ test(_.print(true), "true")
+ test(_.print('Z'), "Z")
+ test(_.print('\n'), "\n")
+ test(_.print(5), "5")
+ test(_.print(1234567891011L), "1234567891011")
+ test(_.print(1.5f), "1.5")
+ test(_.print(Math.PI), "3.141592653589793")
+ test(_.print(Array('A', '\n')), "A\n")
+ test(_.print("hello\n"), "hello\n")
+ test(_.print(null: String), "null")
+ test(_.print((1, 2)), "(1,2)")
+ test(_.print(null: AnyRef), "null")
+ }
+
+ for (autoFlush <- Seq(true, false)) {
+ val title =
+ if (autoFlush) "println forwards and flushes when autoFlush is true"
+ else "println forwards, does not flush when autoFlush is false"
+ it(title) {
+ def test(body: PrintWriter => Unit, expected: String): Unit = {
+ val (pw, sw) = newPrintWriter(autoFlush = autoFlush)
+ body(pw)
+ if (autoFlush) expect(sw.flushed).toBeTruthy
+ else expect(sw.flushed).toBeFalsy
+ expect(pw.checkError()).toBeFalsy
+ expect(sw.toString()).toBe(expected)
+ }
+
+ test(_.println(), "\n")
+ test(_.println(true), "true\n")
+ test(_.println('Z'), "Z\n")
+ test(_.println('\n'), "\n\n")
+ test(_.println(5), "5\n")
+ test(_.println(1234567891011L), "1234567891011\n")
+ test(_.println(1.5f), "1.5\n")
+ test(_.println(Math.PI), "3.141592653589793\n")
+ test(_.println(Array('A', '\n')), "A\n\n")
+ test(_.println("hello\n"), "hello\n\n")
+ test(_.println(null: String), "null\n")
+ test(_.println((1, 2)), "(1,2)\n")
+ test(_.println(null: AnyRef), "null\n")
+ }
+ }
+
+ for (autoFlush <- Seq(true, false)) {
+ val title =
+ if (autoFlush) "printf/format, which flushes when autoFlush is true"
+ else "printf/format, does not flush when autoFlush is false"
+ it(title) {
+ def test(body: PrintWriter => Unit, expected: String): Unit = {
+ val (pw, sw) = newPrintWriter(autoFlush = autoFlush)
+ body(pw)
+ if (autoFlush) expect(sw.flushed).toBeTruthy
+ else expect(sw.flushed).toBeFalsy
+ expect(pw.checkError()).toBeFalsy
+ expect(sw.toString()).toBe(expected)
+ }
+
+ test(_.printf("%04d", Int.box(5)), "0005")
+ test(_.format("%.5f", Double.box(Math.PI)), "3.14159")
+ }
+ }
+
+ it("append, does not flush even with \\n") {
+ def test(body: PrintWriter => Unit, expected: String): Unit = {
+ val (pw, sw) = newPrintWriter(autoFlush = true)
+ body(pw)
+ expect(sw.flushed).toBeFalsy
+ expect(pw.checkError()).toBeFalsy
+ expect(sw.toString()).toBe(expected)
+ }
+
+ test(_.append("hello\n"), "hello\n")
+ test(_.append(null: CharSequence), "null")
+ test(_.append("hello\nworld", 3, 6), "lo\n")
+ test(_.append(null: CharSequence, 1, 2), "u")
+ test(_.append('A'), "A")
+ test(_.append('\n'), "\n")
+ }
+
+ it("traps all IOException and updates checkError") {
+ def test(body: PrintWriter => Unit): Unit = {
+ val (pw, sw) = newPrintWriter()
+ sw.throwing = true
+ body(pw)
+ expect(pw.checkError()).toBeTruthy
+ }
+
+ test(_.flush())
+ test(_.close())
+
+ test(_.write('Z'))
+ test(_.write("booh"))
+ test(_.write("booh", 1, 1))
+ test(_.write(Array('A', 'B')))
+ test(_.write(Array('A', 'B'), 1, 1))
+
+ test(_.print(true))
+ test(_.print('Z'))
+ test(_.print('\n'))
+ test(_.print(5))
+ test(_.print(1234567891011L))
+ test(_.print(1.5f))
+ test(_.print(Math.PI))
+ test(_.print(Array('A', '\n')))
+ test(_.print("hello\n"))
+ test(_.print(null: String))
+ test(_.print((1, 2)))
+ test(_.print(null: AnyRef))
+
+ test(_.println())
+ test(_.println(true))
+ test(_.println('Z'))
+ test(_.println('\n'))
+ test(_.println(5))
+ test(_.println(1234567891011L))
+ test(_.println(1.5f))
+ test(_.println(Math.PI))
+ test(_.println(Array('A', '\n')))
+ test(_.println("hello\n"))
+ test(_.println(null: String))
+ test(_.println((1, 2)))
+ test(_.println(null: AnyRef))
+
+ test(_.append("hello\n"))
+ test(_.append(null: CharSequence))
+ test(_.append("hello\nworld", 3, 6))
+ test(_.append(null: CharSequence, 1, 2))
+ test(_.append('A'))
+ test(_.append('\n'))
+ }
+ }
+
+ /** A PrintWriter that exposes various hooks for testing purposes. */
+ private class MockPrintWriter(out: Writer,
+ autoFlush: Boolean) extends PrintWriter(out, autoFlush) {
+ def this(out: Writer) = this(out, false)
+
+ override def clearError(): Unit = super.clearError()
+ }
+
+ /** A StringWriter that exposes various hooks for testing purposes. */
+ private class MockStringWriter extends StringWriter {
+ private var _flushed: Boolean = true
+ private var _closed: Boolean = false
+
+ var throwing: Boolean = false
+
+ def flushed: Boolean = _flushed
+ def closed: Boolean = _closed
+
+ private def maybeThrow(): Unit = {
+ if (throwing)
+ throw new IOException("MockStringWriter throws")
+ }
+
+ private def writeOp[A](op: => A): A = {
+ maybeThrow()
+ _flushed = false
+ op
+ }
+
+ override def flush(): Unit = {
+ maybeThrow()
+ super.flush()
+ _flushed = true
+ }
+
+ override def close(): Unit = {
+ maybeThrow()
+ super.close()
+ _closed = true
+ }
+
+ override def append(c: Char): StringWriter =
+ writeOp(super.append(c))
+
+ override def append(csq: CharSequence): StringWriter =
+ writeOp(super.append(csq))
+
+ override def append(csq: CharSequence, start: Int, end: Int): StringWriter =
+ writeOp(super.append(csq, start, end))
+
+ override def write(c: Int): Unit =
+ writeOp(super.write(c))
+
+ override def write(cbuf: Array[Char]): Unit =
+ writeOp(super.write(cbuf))
+
+ override def write(cbuf: Array[Char], off: Int, len: Int): Unit =
+ writeOp(super.write(cbuf, off, len))
+
+ override def write(str: String): Unit =
+ writeOp(super.write(str))
+
+ override def write(str: String, off: Int, len: Int): Unit =
+ writeOp(super.write(str, off, len))
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/RandomTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/RandomTest.scala
new file mode 100644
index 0000000..1794d4a
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/RandomTest.scala
@@ -0,0 +1,225 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.javalib
+
+import org.scalajs.jasminetest.JasmineTest
+
+import java.util.Random
+
+import scala.scalajs.js
+import scala.scalajs.js.JSConverters._
+
+object RandomTest extends JasmineTest {
+
+ describe("java.util.Random") {
+
+ it("should produce bits according to spec with seed=10") {
+ val random = new HackRandom(10)
+
+ expect(random.next(10)).toBe(747)
+ expect(random.next(1)).toBe(0)
+ expect(random.next(6)).toBe(16)
+ expect(random.next(20)).toBe(432970)
+ expect(random.next(32)).toBe(254270492)
+ }
+
+ it("should produce bits according to spec with seed=-5") {
+ val random = new HackRandom(-5)
+
+ expect(random.next(10)).toBe(275)
+ expect(random.next(1)).toBe(0)
+ expect(random.next(6)).toBe(21)
+ expect(random.next(20)).toBe(360349)
+ expect(random.next(32)).toBe(1635930704)
+ }
+
+ it("should produce bits according to spec with seed=MaxLong") {
+ val random = new HackRandom(Long.MaxValue)
+
+ expect(random.next(10)).toBe(275)
+ expect(random.next(1)).toBe(0)
+ expect(random.next(6)).toBe(0)
+ expect(random.next(20)).toBe(574655)
+ expect(random.next(32)).toBe(-1451336087)
+ }
+
+ it("should produce bits according to spec with seed=MinInt") {
+ val random = new HackRandom(Int.MinValue)
+
+ expect(random.next(10)).toBe(388)
+ expect(random.next(1)).toBe(0)
+ expect(random.next(6)).toBe(25)
+ expect(random.next(20)).toBe(352095)
+ expect(random.next(32)).toBe(-2140124682)
+ }
+
+ it("should allow resetting the seed") {
+ val random = new HackRandom(11)
+ expect(random.next(10)).toBe(747)
+ expect(random.next(1)).toBe(1)
+ expect(random.next(6)).toBe(27)
+
+ random.setSeed(11)
+ expect(random.next(10)).toBe(747)
+ expect(random.next(1)).toBe(1)
+ expect(random.next(6)).toBe(27)
+ }
+
+ it("should reset nextNextGaussian when setting the seed") {
+ val random = new Random(-1)
+ expect(random.nextGaussian()).toBe(1.7853314409882288)
+ random.setSeed(-1)
+ expect(random.nextGaussian()).toBe(1.7853314409882288)
+ }
+
+ it("should correctly implement nextDouble") {
+ val random = new Random(-45)
+ expect(random.nextDouble()).toBe(0.27288421395636253)
+ expect(random.nextDouble()).toBe(0.5523165360074201)
+ expect(random.nextDouble()).toBe(0.5689979434708298)
+ expect(random.nextDouble()).toBe(0.9961166166874871)
+ expect(random.nextDouble()).toBe(0.5368984665202684)
+ expect(random.nextDouble()).toBe(0.19849067496547423)
+ expect(random.nextDouble()).toBe(0.6021019223595357)
+ expect(random.nextDouble()).toBe(0.06132131151816378)
+ expect(random.nextDouble()).toBe(0.7303867762743866)
+ expect(random.nextDouble()).toBe(0.7426529384056163)
+ }
+
+ it("should correctly implement nextBoolean") {
+ val random = new Random(4782934)
+ expect(random.nextBoolean()).toBe(false)
+ expect(random.nextBoolean()).toBe(true)
+ expect(random.nextBoolean()).toBe(true)
+ expect(random.nextBoolean()).toBe(false)
+ expect(random.nextBoolean()).toBe(false)
+ expect(random.nextBoolean()).toBe(false)
+ expect(random.nextBoolean()).toBe(true)
+ expect(random.nextBoolean()).toBe(false)
+ }
+
+ it("should correctly implement nextInt") {
+ val random = new Random(-84638)
+ expect(random.nextInt()).toBe(-1217585344)
+ expect(random.nextInt()).toBe(1665699216)
+ expect(random.nextInt()).toBe(382013296)
+ expect(random.nextInt()).toBe(1604432482)
+ expect(random.nextInt()).toBe(-1689010196)
+ expect(random.nextInt()).toBe(1743354032)
+ expect(random.nextInt()).toBe(454046816)
+ expect(random.nextInt()).toBe(922172344)
+ expect(random.nextInt()).toBe(-1890515287)
+ expect(random.nextInt()).toBe(1397525728)
+ }
+
+ it("should correctly implement nextInt(n)") {
+ val random = new Random(7)
+ expect(random.nextInt(76543)).toBe(32736)
+ expect(() => random.nextInt(0)).toThrow
+ expect(random.nextInt(45)).toBe(29)
+ expect(random.nextInt(945)).toBe(60)
+ expect(random.nextInt(35694839)).toBe(20678044)
+ expect(random.nextInt(35699)).toBe(23932)
+ expect(random.nextInt(3699)).toBe(2278)
+ expect(random.nextInt(10)).toBe(8)
+ }
+
+ it("should correctly implement nextInt(n) for powers of 2") {
+ val random = new Random(-56938)
+
+ expect(random.nextInt(32)).toBe(8)
+ expect(random.nextInt(8)).toBe(3)
+ expect(random.nextInt(128)).toBe(3)
+ expect(random.nextInt(4096)).toBe(1950)
+ expect(random.nextInt(8192)).toBe(3706)
+ expect(random.nextInt(8192)).toBe(4308)
+ expect(random.nextInt(8192)).toBe(3235)
+ expect(random.nextInt(8192)).toBe(7077)
+ expect(random.nextInt(8192)).toBe(2392)
+ expect(random.nextInt(32)).toBe(31)
+ }
+
+ it("should correctly implement nextLong") {
+ val random = new Random(205620432625028L)
+ expect(random.nextLong()).toBe(3710537363280377478L)
+ expect(random.nextLong()).toBe(4121778334981170700L)
+ expect(random.nextLong()).toBe(289540773990891960L)
+ expect(random.nextLong()).toBe(307008980197674441L)
+ expect(random.nextLong()).toBe(7527069864796025013L)
+ expect(random.nextLong()).toBe(-4563192874520002144L)
+ expect(random.nextLong()).toBe(7619507045427546529L)
+ expect(random.nextLong()).toBe(-7888117030898487184L)
+ expect(random.nextLong()).toBe(-3499168703537933266L)
+ expect(random.nextLong()).toBe(-1998975913933474L)
+ }
+
+ it("should correctly implement nextFloat") {
+ val random = new Random(-3920005825473L)
+ expect(random.nextFloat()).toBeCloseTo(0.059591234, 7)
+ expect(random.nextFloat()).toBeCloseTo(0.7007871, 7)
+ expect(random.nextFloat()).toBeCloseTo(0.39173192, 7)
+ expect(random.nextFloat()).toBeCloseTo(0.0647918, 7)
+ expect(random.nextFloat()).toBeCloseTo(0.9029677, 7)
+ expect(random.nextFloat()).toBeCloseTo(0.18226051, 7)
+ expect(random.nextFloat()).toBeCloseTo(0.94444054, 7)
+ expect(random.nextFloat()).toBeCloseTo(0.008844078, 7)
+ expect(random.nextFloat()).toBeCloseTo(0.08891684, 7)
+ expect(random.nextFloat()).toBeCloseTo(0.06482434, 7)
+ }
+
+ it("should correctly implement nextBytes") {
+ val random = new Random(7399572013373333L)
+
+ def test(exps: Int*) = {
+ val exp = js.Array(exps.map(_.toByte): _*)
+ val buf = new Array[Byte](exp.length)
+ random.nextBytes(buf)
+ expect(buf.toJSArray).toEqual(exp)
+ }
+
+ test(62, 89, 68, -91, 10, 0, 85)
+ test(-89, -76, 88, 121, -25, 47, 58, -8, 78, 20, -77, 84, -3,
+ -33, 58, -9, 11, 57, -118, 40, -74, -86, 78, 123, 58)
+ test(-77, 112, -116)
+ test()
+ test(-84, -96, 108)
+ test(57, -106, 42, -100, -47, -84, 67, -48, 45)
+ }
+
+ it("should correctly implement nextGaussian") {
+ val random = new Random(2446004)
+ expect(random.nextGaussian()).toBe(-0.5043346938630431)
+ expect(random.nextGaussian()).toBe(-0.3250983270156675)
+ expect(random.nextGaussian()).toBe(-0.23799457294994966)
+ expect(random.nextGaussian()).toBe(0.4164610631507695)
+ expect(random.nextGaussian()).toBe(0.22086348814760687)
+ expect(random.nextGaussian()).toBe(-0.706833209972521)
+ expect(random.nextGaussian()).toBe(0.6730758289772553)
+ expect(random.nextGaussian()).toBe(0.2797393696191283)
+ expect(random.nextGaussian()).toBe(-0.2979099632667685)
+ expect(random.nextGaussian()).toBe(0.37443415981434314)
+ expect(random.nextGaussian()).toBe(0.9584801742918951)
+ expect(random.nextGaussian()).toBe(1.1762179112229345)
+ expect(random.nextGaussian()).toBe(0.8736960092848826)
+ expect(random.nextGaussian()).toBe(0.12301554931271008)
+ expect(random.nextGaussian()).toBe(-0.6052081187207353)
+ expect(random.nextGaussian()).toBe(-0.2015925608755316)
+ expect(random.nextGaussian()).toBe(-1.0071216119742104)
+ expect(random.nextGaussian()).toBe(0.6734222041441913)
+ expect(random.nextGaussian()).toBe(0.3990565555091522)
+ expect(random.nextGaussian()).toBe(2.0051627385915154)
+ }
+
+ }
+
+ /** Helper class to access next */
+ class HackRandom(seed: Long) extends Random(seed) {
+ override def next(bits: Int): Int = super.next(bits)
+ }
+
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ReadersTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ReadersTest.scala
new file mode 100644
index 0000000..f970141
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ReadersTest.scala
@@ -0,0 +1,244 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.javalib
+
+import scala.annotation.tailrec
+
+import java.io._
+
+import scala.scalajs.js
+import scala.scalajs.js.JSConverters._
+
+import org.scalajs.jasminetest.JasmineTest
+
+/** Tests for our implementation of java.io._ reader classes */
+object ReadersTest extends JasmineTest {
+
+ describe("java.io.StringReader") {
+ val str = "asdf"
+ def newReader = new StringReader(str)
+
+ it("should provide read()") {
+ val r = newReader
+
+ for (c <- str) {
+ expect(r.read().toChar).toEqual(c)
+ }
+
+ expect(r.read()).toEqual(-1)
+ }
+
+ it("should provide read(buf: Array[Char], off: Int, len: Int)") {
+ val r = newReader
+ val buf = new Array[Char](10)
+
+ expect(r.read(buf, 2, 8)).toBe(4)
+ expect(buf.map(_.toInt).toJSArray).toEqual(
+ js.Array[Int](0,0,'a','s','d','f',0,0,0,0))
+ }
+
+ it("should provide read(java.nio.CharBuffer)") {
+ val r = newReader
+ val buf0 = java.nio.CharBuffer.allocate(25)
+ buf0.position(3)
+ val buf = buf0.slice()
+ buf.position(4)
+ buf.limit(14)
+
+ expect(r.read(buf)).toBe(4)
+ expect(buf.position()).toBe(8)
+ buf.flip()
+ expect(buf.toString().map(_.toInt).toJSArray).toEqual(
+ js.Array[Int](0, 0, 0, 0, 'a', 's', 'd', 'f'))
+ }
+
+ it("should provide ready") {
+ val r = newReader
+
+ for (c <- str) {
+ expect(r.ready()).toBeTruthy
+ expect(r.read().toChar).toEqual(c)
+ }
+
+ expect(r.ready()).toBeFalsy
+ expect(r.read()).toEqual(-1)
+ }
+
+ it("should provide mark/reset") {
+ val r = newReader
+ r.mark(str.length)
+
+ for (c <- str) {
+ expect(r.read().toChar).toEqual(c)
+ }
+ expect(r.read()).toEqual(-1)
+
+ r.reset()
+
+ for (c <- str) {
+ expect(r.read().toChar).toEqual(c)
+ }
+ expect(r.read()).toEqual(-1)
+ }
+
+ it("should provide skip") {
+ val r = newReader
+
+ expect(r.read()).toEqual('a')
+ expect(r.skip(2L).toInt).toBe(2)
+
+ expect(r.read()).toEqual('f')
+ expect(r.read()).toEqual(-1)
+ }
+
+ it("should provide close") {
+ val r = newReader
+
+ r.close()
+ expect(() => r.read()).toThrow
+ }
+
+ it("should support marking") {
+ expect(newReader.markSupported).toBeTruthy
+ }
+ }
+
+ describe("java.io.BufferedReader") {
+ val str = "line1\nline2\r\n\nline4\rline5"
+ def newReader = new BufferedReader(new StringReader(str), 3)
+
+ it("should provide read()") {
+ val r = newReader
+
+ for (c <- str) {
+ expect(r.read().toChar).toEqual(c)
+ }
+ expect(r.read()).toEqual(-1)
+ }
+
+ it("should provide read(cbuf)") {
+ var read = 0
+ val r = newReader
+ val buf = new Array[Char](15)
+
+ // twice to force filling internal buffer
+ for (_ <- 0 to 1) {
+ val len = r.read(buf)
+ expect(len).toBeGreaterThan(0)
+
+ for (i <- 0 until len)
+ expect(buf(i)).toEqual(str.charAt(i+read))
+
+ read += len
+ }
+ }
+
+ it("should provide read(cbuf, off, len)") {
+ var read = 0
+ val r = newReader
+ val buf = new Array[Char](15)
+
+ // twice to force filling internal buffer
+ for (_ <- 0 to 1) {
+ val len = r.read(buf, 1, 10)
+ expect(len).toBeGreaterThan(0)
+ expect(len).toBeLessThan(11)
+
+ for (i <- 0 until len)
+ expect(buf(i+1)).toEqual(str.charAt(i+read))
+
+ read += len
+ }
+ }
+
+ it("should provide mark/reset") {
+ val r = newReader
+ expect(r.read()).toEqual('l')
+
+ // force moving and resizing buffer
+ r.mark(10)
+
+ for (i <- 0 until 10) {
+ expect(r.read()).toEqual(str.charAt(i+1))
+ }
+
+ r.reset()
+
+ for (i <- 1 until str.length) {
+ expect(r.read()).toEqual(str.charAt(i))
+ }
+ }
+
+ it("should provide readLine") {
+ val r = newReader
+
+ expect(r.readLine()).toEqual("line1")
+ expect(r.readLine()).toEqual("line2")
+ expect(r.readLine()).toEqual("")
+ expect(r.readLine()).toEqual("line4")
+ expect(r.readLine()).toEqual("line5")
+ expect(r.readLine()).toEqual(null)
+ }
+
+ it("should readLine on an empty stream") {
+ val r = new BufferedReader(new StringReader(""))
+
+ expect(r.readLine()).toEqual(null)
+ }
+
+ it("should readline with empty lines only") {
+ val r = new BufferedReader(new StringReader("\n\r\n\r\r\n"), 1)
+
+ for (_ <- 1 to 4)
+ expect(r.readLine()).toEqual("")
+
+ expect(r.readLine()).toEqual(null)
+ }
+
+ it("should support marking") {
+ expect(newReader.markSupported).toBeTruthy
+ }
+ }
+
+ describe("java.io.InputStreamReader") {
+
+ it("should read UTF8") {
+
+ val buf = Array[Byte](72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100,
+ 46, -29, -127, -109, -29, -126, -109, -29, -127, -85, -29, -127, -95,
+ -29, -127, -81, -26, -105, -91, -26, -100, -84, -24, -86, -98, -29,
+ -126, -110, -24, -86, -83, -29, -126, -127, -29, -127, -66, -29, -127,
+ -103, -29, -127, -117, -29, -128, -126)
+
+ val r = new InputStreamReader(new ByteArrayInputStream(buf))
+
+ def expectRead(str: String) = {
+ val buf = new Array[Char](str.length)
+ @tailrec
+ def readAll(readSoFar: Int): Int = {
+ if (readSoFar == buf.length) readSoFar
+ else {
+ val newlyRead = r.read(buf, readSoFar, buf.length - readSoFar)
+ if (newlyRead == -1) readSoFar
+ else readAll(readSoFar + newlyRead)
+ }
+ }
+ expect(readAll(0)).toBe(str.length)
+ expect(new String(buf)).toEqual(str)
+ }
+
+ expectRead("Hello World.")
+ expectRead("こんにちは")
+ expectRead("日本語を読めますか。")
+ expect(r.read()).toBe(-1)
+
+ }
+
+ }
+
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ReferenceTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ReferenceTest.scala
new file mode 100644
index 0000000..cf0fd0c
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ReferenceTest.scala
@@ -0,0 +1,28 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.javalib
+
+import org.scalajs.jasminetest.JasmineTest
+
+object ReferenceTest extends JasmineTest {
+
+ describe("java.land.ref.Reference") {
+
+ it("Should have all the normal operations") {
+ val s = "string"
+ val ref = new java.lang.ref.WeakReference(s)
+ expect(ref.get).toEqual(s)
+ expect(ref.enqueue).toEqual(false)
+ expect(ref.isEnqueued).toEqual(false)
+ ref.clear
+ // can't use `expect` because it tries to be clever and .toString things,
+ // which makes it blow up when you pass in null
+ assert(ref.get == null)
+ }
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/RegexTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/RegexTest.scala
new file mode 100644
index 0000000..a27584a
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/RegexTest.scala
@@ -0,0 +1,397 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.javalib
+
+import scala.scalajs.js
+import scala.scalajs.js.JSConverters._
+
+import org.scalajs.jasminetest.JasmineTest
+
+import java.util.regex.Pattern
+
+object RegexTest extends JasmineTest {
+
+ describe("java.util.regex.Pattern") {
+
+ it("should respond to `matches`") {
+ expect(Pattern.matches("[Scal]*\\.js", "Scala.js")).toBeTruthy
+ expect(Pattern.matches(".[cal]*\\.j.", "Scala.js")).toBeTruthy
+ expect(Pattern.matches(".*\\.js", "Scala.js")).toBeTruthy
+ expect(Pattern.matches("S[a-z]*", "Scala.js")).toBeFalsy
+ }
+
+ it("should respond to `matches` with flags") {
+ matches("scala.js", "Scala.js")
+ matches("SCALA.JS", "Scala.js")
+ matches("waz*up", "WAZZZZZZZZZZZUP")
+
+ def matches(regex: String, input: String): Unit = {
+ val result = Pattern.compile(regex, Pattern.CASE_INSENSITIVE).matcher(input)
+ expect(result.matches()).toBeTruthy
+ }
+ }
+
+ it("should respond to `split`") {
+ val result = Pattern.compile("[aj]").split("Scala.js")
+ val expected = js.Array("Sc", "l", ".", "s")
+ expect(result.length).toEqual(4)
+ expect(result.toJSArray).toEqual(expected)
+
+ // Tests from JavaDoc
+ split("boo:and:foo", ":", Array("boo", "and", "foo"))
+ split("boo:and:foo", "o", Array("b", "", ":and:f"))
+
+ // Splitting the empty string must return 1 element - #987
+ split("", "a", Array(""))
+ split("", "\\*", Array(""))
+ split("", "\n", Array(""))
+ split("", "", Array(""))
+
+ // Should remove leading empty match under some conditions - #1171
+ // These tests are "measured" on the JVM since the spec is unclear
+ split("abc", "(?=a)", Array("abc"))
+ split("abc", "(?=b)", Array("a", "bc"))
+ split("abc", "(?=a)|b", Array("", "a", "c"))
+ split("abc", "", Array("", "a", "b", "c"))
+ split("abc", "(?=a)|(?=b)", Array("", "a", "bc"))
+ split("abc", "(?=a)|(?=a)", Array("abc"))
+ split("abc", "(?=a|b)", Array("", "a", "bc"))
+ split("abc", "(?=a|d)", Array("abc"))
+ split("abc", "^d*", Array("abc"))
+ split("abc", "d*", Array("", "a", "b", "c"))
+ split("a", "", Array("", "a"))
+ split("a", "^d*", Array("a"))
+ split("a", "d*", Array("", "a"))
+ split("a", "(?=a)", Array("a"))
+ split("ab", "a", Array("", "b"))
+
+ def split(input: String, regex: String, expected: Array[String]): Unit = {
+ val result = Pattern.compile(regex).split(input)
+ expect(result.toJSArray).toEqual(expected.toJSArray)
+ }
+ }
+
+ it("should respond to `split` with limit") {
+ // Tests from JavaDoc
+ splitWithLimit("boo:and:foo", ":", 2, Array("boo", "and:foo"))
+ splitWithLimit("boo:and:foo", ":", 5, Array("boo", "and", "foo"))
+ splitWithLimit("boo:and:foo", ":", -2, Array("boo", "and", "foo"))
+ splitWithLimit("boo:and:foo", "o", 5, Array("b", "", ":and:f", "", ""))
+ splitWithLimit("boo:and:foo", "o", -2, Array("b", "", ":and:f", "", ""))
+ splitWithLimit("boo:and:foo", "o", 0, Array("b", "", ":and:f"))
+
+ // Splitting the empty string must return 1 element - #987
+ splitWithLimit("", "a", 0, Array(""))
+ splitWithLimit("", "\\*", 5, Array(""))
+ splitWithLimit("", "\n", -2, Array(""))
+ splitWithLimit("", "", 1, Array(""))
+
+ // Should remove leading empty match under some conditions - #1171
+ splitWithLimit("abc", "", 2, Array("", "abc"))
+ splitWithLimit("abc", "(?=a)", 2, Array("abc"))
+ splitWithLimit("ab", "a", 1, Array("ab"))
+
+ def splitWithLimit(input: String, regex: String, limit: Int, expected: Array[String]): Unit = {
+ val result = Pattern.compile(regex).split(input, limit)
+ expect(result.toJSArray).toEqual(expected.toJSArray)
+ }
+ }
+
+ it("should respond to `flags`") {
+ val pattern0 = Pattern.compile("a")
+ val pattern1 = Pattern.compile("a", 0)
+ val flags2 = Pattern.CASE_INSENSITIVE | Pattern.DOTALL
+ val pattern2 = Pattern.compile("a", flags2)
+
+ expect(pattern0.flags).toEqual(0)
+ expect(pattern1.flags).toEqual(0)
+ expect(pattern2.flags).toEqual(flags2)
+ }
+
+ it("should respond to `pattern` and `toString`") {
+ def checkPatternAndToString(regex: String): Unit = {
+ val pattern0 = Pattern.compile(regex)
+ expect(pattern0.pattern).toEqual(regex)
+ expect(pattern0.toString).toEqual(regex)
+
+ val pattern1 = Pattern.compile(regex, Pattern.CASE_INSENSITIVE)
+ expect(pattern1.pattern).toEqual(regex)
+ expect(pattern1.toString).toEqual(regex)
+ }
+
+ checkPatternAndToString("a*b+c")
+ checkPatternAndToString("\\S[(a1]a.js")
+ }
+
+ it("should respond to `quote`") {
+ val splitWithQuote = Pattern.compile(Pattern.quote("$1&$2")).split("Scala$1&$2.js")
+ val splitNoQuote = Pattern.compile("$1&$2").split("Scala$1&$2.js")
+ expect(splitWithQuote.mkString).toEqual("Scala.js")
+ expect(splitNoQuote.mkString).toEqual("Scala$1&$2.js")
+ }
+
+ }
+
+ describe("java.util.regex.Matcher") {
+
+ it("should respond to `find`") {
+ val matcher = Pattern.compile("a").matcher("Scala.js")
+
+ expect(matcher.find()).toBeTruthy
+ expect(matcher.find()).toBeTruthy
+ expect(matcher.find()).toBeFalsy
+ expect(matcher.find(4)).toBeTruthy
+ expect(matcher.find()).toBeFalsy
+ }
+
+ it("should respond to `start`, `end`, `group`, and `toMatchResult`") {
+ val matcher = Pattern.compile("\\s(([A-Za-z]{5}(hum)?).js)\\s").matcher("Write Scala.js everyday!")
+
+ def checkGroup0(start: Int, end: Int, group: String) =
+ checkGroup(start, 5, end, 15, group, " Scala.js ")
+
+ def checkGroup1(start: Int, end: Int, group: String) =
+ checkGroup(start, 6, end, 14, group, "Scala.js")
+
+ def checkGroup2(start: Int, end: Int, group: String) =
+ checkGroup(start, 6, end, 11, group, "Scala")
+
+ def checkGroup3(start: Int, end: Int, group: String) =
+ checkGroup(start, -1, end, -1, group, null)
+
+ def checkGroup(start: Int, startExpected: Int, end: Int, endExpected: Int,
+ group: String, groupExpected: String): Unit = {
+ expect(start).toEqual(startExpected)
+ expect(end).toEqual(endExpected)
+ expect(group).toEqual(groupExpected)
+ }
+
+ expect(matcher.find()).toBeTruthy
+ expect(matcher.groupCount).toEqual(3)
+ checkGroup0(matcher.start, matcher.end, matcher.group)
+ checkGroup0(matcher.start(0), matcher.end(0), matcher.group(0))
+ checkGroup1(matcher.start(1), matcher.end(1), matcher.group(1))
+ checkGroup2(matcher.start(2), matcher.end(2), matcher.group(2))
+ checkGroup3(matcher.start(3), matcher.end(3), matcher.group(3))
+
+ val matchResult = matcher.toMatchResult
+ expect(matchResult.groupCount).toEqual(3)
+ checkGroup0(matchResult.start, matchResult.end, matchResult.group)
+ checkGroup0(matchResult.start(0), matchResult.end(0), matchResult.group(0))
+ checkGroup1(matchResult.start(1), matchResult.end(1), matchResult.group(1))
+ checkGroup2(matchResult.start(2), matchResult.end(2), matchResult.group(2))
+ checkGroup3(matchResult.start(3), matchResult.end(3), matchResult.group(3))
+ }
+
+ it("should respond to `matches`") {
+ val matcher0 = Pattern.compile("S[a-z]+").matcher("Scala")
+ val matcher1 = Pattern.compile("S[a-z]+").matcher("Scala.js")
+
+ expect(matcher0.matches()).toBeTruthy
+ expect(matcher1.matches()).toBeFalsy
+ }
+
+ it("should respond to `reset`") {
+ val matcher = Pattern.compile("S[a-z]+").matcher("Scalable")
+
+ expect(matcher.find()).toBeTruthy
+ expect(matcher.find()).toBeFalsy
+ matcher.reset()
+ expect(matcher.find()).toBeTruthy
+ }
+
+ it("should respond to `reset(String)`") {
+ val matcher = Pattern.compile("S[a-z]+").matcher("Scalable")
+
+ expect(matcher.matches()).toBeTruthy
+ matcher.reset("Scala.js")
+ expect(matcher.matches()).toBeFalsy
+ }
+
+ it("should respond to `usePattern`") {
+ val patternNoDots = Pattern.compile("S[a-z]+")
+ val patternWithDots = Pattern.compile("S[a-z.]+")
+
+ val matcher0 = patternNoDots.matcher("Scala.js")
+ expect(matcher0.matches()).toBeFalsy
+ matcher0.usePattern(patternWithDots)
+ expect(matcher0.matches()).toBeTruthy
+
+ val matcher1 = patternWithDots.matcher("Scala.js")
+ expect(matcher1.matches()).toBeTruthy
+ matcher1.usePattern(patternNoDots)
+ expect(matcher1.matches()).toBeFalsy
+ }
+
+ it("should respond to `lookingAt`") {
+ val matcher0 = Pattern.compile("S[a-z]+").matcher("Scala")
+ val matcher1 = Pattern.compile("S[a-z]+").matcher("Scala.js")
+ val matcher2 = Pattern.compile("[a-z]+").matcher("Scala.js")
+
+ expect(matcher0.lookingAt()).toBeTruthy
+ expect(matcher1.lookingAt()).toBeTruthy
+ expect(matcher2.lookingAt()).toBeFalsy
+
+ val matcher3 = Pattern.compile("S[a-z]+").matcher("Scala.js")
+ expect(matcher3.find()).toBeTruthy
+ expect(matcher3.lookingAt()).toBeTruthy
+ }
+
+ it("should respond to `hitEnd`") {
+ val matcher0 = Pattern.compile("S[a-z]*").matcher("Scala.js")
+ expect(matcher0.find()).toBeTruthy
+ expect(matcher0.hitEnd).toBeFalsy
+ expect(matcher0.find()).toBeFalsy
+ expect(matcher0.hitEnd).toBeTruthy
+
+ val matcher1 = Pattern.compile("[A-Za-z]+").matcher("Scala.js")
+ expect(matcher1.find()).toBeTruthy
+ expect(matcher1.hitEnd).toBeFalsy
+ expect(matcher1.group).toBe("Scala")
+ expect(matcher1.find()).toBeTruthy
+ expect(matcher1.hitEnd).toBeTruthy
+ expect(matcher1.group).toBe("js")
+ expect(matcher1.lookingAt()).toBeTruthy
+ expect(matcher1.group).toBe("Scala")
+ expect(matcher1.hitEnd).toBeFalsy
+ }
+
+ it("should respond to `region`") {
+ val matcher0 = Pattern.compile("S[a-z]+").matcher("A Scalable Solution")
+
+ val region0to3 = matcher0.region(0, 3)
+ expect(region0to3.regionStart).toBe(0)
+ expect(region0to3.regionEnd).toBe(3)
+ expect(region0to3.find()).toBeFalsy
+
+ val region0to15 = matcher0.region(0, 15)
+ expect(region0to15.regionStart).toBe(0)
+ expect(region0to15.regionEnd).toBe(15)
+ expect(region0to15.find()).toBeTruthy
+ expect(region0to15.group).toEqual("Scalable")
+
+ val region2to7 = region0to15.region(2, 7)
+ expect(region2to7.regionStart).toBe(2)
+ expect(region2to7.regionEnd).toBe(7)
+ expect(region2to7.find()).toBeTruthy
+ expect(region2to7.group).toEqual("Scala")
+
+ val region5toEnd = matcher0.region(5, matcher0.regionEnd)
+ expect(region5toEnd.regionStart).toBe(5)
+ expect(region5toEnd.regionEnd).toBe(19)
+ expect(region5toEnd.find()).toBeTruthy
+ expect(region5toEnd.group).toEqual("Solution")
+
+ val matcher1 = Pattern.compile("0[xX][A-Fa-f0-9]{3}$").matcher("In CSS, 0xc4fe is not a color")
+
+ val region5to13 = matcher1.region(5, 13)
+ expect(region5to13.regionStart).toBe(5)
+ expect(region5to13.regionEnd).toBe(13)
+ expect(region5to13.find()).toBeTruthy
+ expect(region5to13.group).toEqual("0xc4f")
+
+ val region5to20 = matcher1.region(5, 20)
+ expect(region5to20.regionStart).toBe(5)
+ expect(region5to20.regionEnd).toBe(20)
+ expect(region5to20.find()).toBeFalsy
+ }
+
+ it("should respond to `appendReplacement` and `appendTail`") {
+ // From the JavaDoc
+ val matcher = Pattern.compile("cat").matcher("one cat two cats in the yard")
+ val sb = new StringBuffer
+
+ while (matcher.find()) {
+ matcher.appendReplacement(sb, "dog")
+ }
+ matcher.appendTail(sb)
+
+ expect(sb.toString).toBe("one dog two dogs in the yard")
+ }
+
+ it("should respond to `replaceAll`") {
+ // From the JavaDoc
+ val matcher = Pattern.compile("a*b").matcher("aabfooaabfooabfoob")
+ expect(matcher.replaceAll("-")).toBe("-foo-foo-foo-")
+ }
+
+ it("should respond to `replaceFirst`") {
+ // From the JavaDoc
+ val matcher = Pattern.compile("dog").matcher("zzzdogzzzdogzzz")
+ expect(matcher.replaceFirst("cat")).toBe("zzzcatzzzdogzzz")
+ }
+
+ it("should throw exception if match accessors are called before `find`") {
+ def checkInvalidAccess(block: => Unit): Unit = {
+ val exception: Throwable = try {
+ block
+ throw new Error("No exception thrown")
+ } catch {
+ case e: Throwable => e
+ }
+
+ expect(exception.getClass.getName).toBe("java.lang.IllegalStateException")
+ expect(exception.getMessage).toBe("No match available")
+ }
+
+ val matcher = Pattern.compile("(Sc([a-z]*))").matcher("Scala.js")
+
+ checkInvalidAccess { matcher.start }
+ checkInvalidAccess { matcher.end }
+ checkInvalidAccess { matcher.group }
+ checkInvalidAccess { matcher.group(42) }
+
+ val matchResult = matcher.toMatchResult
+
+ checkInvalidAccess { matchResult.start }
+ checkInvalidAccess { matchResult.end }
+ checkInvalidAccess { matchResult.group }
+ checkInvalidAccess { matchResult.group(42) }
+ }
+
+ it("should correctly handle zero-length matches") {
+ val pat = Pattern.compile("a*?")
+ val mat = pat.matcher("aaaaa")
+ for (i <- 0 to 5) {
+ expect(mat.find()).toBeTruthy
+ expect(mat.start).toEqual(i)
+ expect(mat.end).toEqual(i)
+ }
+
+ // Make sure we don't suddenly re-match
+ for (i <- 0 to 5) {
+ expect(mat.find()).toBeFalsy
+ }
+ }
+
+ it("should support in-pattern flags - #997") {
+ val p0 = Pattern.compile("(?i)abc")
+
+ expect(p0.flags() & Pattern.CASE_INSENSITIVE).not.toBe(0)
+
+ val m0 = p0.matcher("abcABC")
+
+ expect(m0.find()).toBeTruthy
+ expect(m0.group()).toEqual("abc")
+ expect(m0.find()).toBeTruthy
+ expect(m0.group()).toEqual("ABC")
+ expect(m0.find()).toBeFalsy
+
+ val p1 = Pattern.compile("(?-i)abc", Pattern.CASE_INSENSITIVE)
+
+ expect(p1.flags() & Pattern.CASE_INSENSITIVE).toBe(0)
+
+ val m1 = p1.matcher("abcABC")
+
+ expect(m1.find()).toBeTruthy
+ expect(m1.group()).toEqual("abc")
+ expect(m1.find()).toBeFalsy
+ }
+
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ShortTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ShortTest.scala
new file mode 100644
index 0000000..e59f2e9
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ShortTest.scala
@@ -0,0 +1,66 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.javalib
+
+import java.lang.{Short => JShort}
+
+import org.scalajs.jasminetest.JasmineTest
+
+/**
+ * tests the implementation of the java standard library Short
+ */
+object ShortTest extends JasmineTest {
+
+ describe("java.lang.Short") {
+
+ it("should provide `compareTo`") {
+ def compare(x: Short, y: Short): Int =
+ new JShort(x).compareTo(new JShort(y))
+
+ expect(compare(0.toShort, 5.toShort)).toBeLessThan(0)
+ expect(compare(10.toShort, 9.toShort)).toBeGreaterThan(0)
+ expect(compare(-2.toShort, -1.toShort)).toBeLessThan(0)
+ expect(compare(3.toShort, 3.toShort)).toEqual(0)
+ }
+
+ it("should be a Comparable") {
+ def compare(x: Any, y: Any): Int =
+ x.asInstanceOf[Comparable[Any]].compareTo(y)
+
+ expect(compare(0.toShort, 5.toShort)).toBeLessThan(0)
+ expect(compare(10.toShort, 9.toShort)).toBeGreaterThan(0)
+ expect(compare(-2.toShort, -1.toShort)).toBeLessThan(0)
+ expect(compare(3.toShort, 3.toShort)).toEqual(0)
+ }
+
+ it("should parse strings") {
+ def test(s: String, v: Short): Unit = {
+ expect(JShort.parseShort(s)).toEqual(v)
+ expect(JShort.valueOf(s).shortValue()).toEqual(v)
+ expect(new JShort(s).shortValue()).toEqual(v)
+ }
+
+ test("0", 0)
+ test("5", 5)
+ test("127", 127)
+ test("-100", -100)
+ test("30000", 30000)
+ }
+
+ it("should reject invalid strings when parsing") {
+ def test(s: String): Unit =
+ expect(() => JShort.parseShort(s)).toThrow
+
+ test("abc")
+ test("")
+ test("60000") // out of range
+ test("-90000") // out of range
+ }
+
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/StackTraceElementTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/StackTraceElementTest.scala
new file mode 100644
index 0000000..b02a889
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/StackTraceElementTest.scala
@@ -0,0 +1,29 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.javalib
+
+import scala.scalajs.js
+
+import org.scalajs.jasminetest.JasmineTest
+
+object StackTraceElementTest extends JasmineTest {
+
+ describe("java.lang.StackTraceElement") {
+ it("should use the magic columnNumber field in its toString") {
+ val st = new StackTraceElement("MyClass", "myMethod", "myFile.scala", 1)
+ st.asInstanceOf[js.Dynamic].columnNumber = 5
+ expect(st.toString).toEqual("MyClass.myMethod(myFile.scala:1:5)")
+ }
+
+ it("should leave toString unmodified without magic columnNumber") {
+ val st = new StackTraceElement("MyClass", "myMethod", "myFile.scala", 1)
+ expect(st.toString).toEqual("MyClass.myMethod(myFile.scala:1)")
+ }
+ }
+
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/StreamsTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/StreamsTest.scala
new file mode 100644
index 0000000..6975614
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/StreamsTest.scala
@@ -0,0 +1,308 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.javalib
+
+import java.io._
+
+import scala.language.implicitConversions
+
+import scala.scalajs.js
+
+import org.scalajs.jasminetest.JasmineTest
+
+/** Tests for our implementation of java.io._ stream classes */
+object StreamsTest extends JasmineTest with CommonStreamsTests {
+
+ // Need to define this again, otherwise conversion on function
+ // triggers for Seqs
+ override implicit def traversable2array[T](
+ a: TraversableOnce[T]): js.Array[T] = super.traversable2array(a)
+
+ describe("java.io.InputStream") {
+
+ class DummyInputStream(val length: Int) extends InputStream {
+ private var i: Int = 0
+ def read(): Int = if (i < length) { i += 1; i } else -1
+ }
+
+ it("should provide a default implementation of `read` to an array") {
+ val stream = new DummyInputStream(200)
+
+ val buf = new Array[Byte](50)
+
+ // Should read first 50 bytes (next: 51)
+ expect(stream.read(buf)).toBe(50)
+ expect(buf).toEqual((1 to 50))
+
+ // Should read another 20 (next: 71)
+ expect(stream.read(buf, 10, 20)).toBe(20)
+ expect(buf).toEqual((1 to 10) ++ (51 to 70) ++ (31 to 50))
+
+ // Test some Exception conditions
+ expect(() => stream.read(null, 0, 10)).toThrow
+ expect(() => stream.read(buf, -1, 10)).toThrow
+ expect(() => stream.read(buf, 0, -1)).toThrow
+ expect(() => stream.read(buf, 10, 100)).toThrow
+ // Buffer should be unmodified
+ expect(buf).toEqual((1 to 10) ++ (51 to 70) ++ (31 to 50))
+
+ // Should read nothing (next: 71)
+ expect(stream.read(buf, 10, 0)).toBe(0)
+ expect(buf).toEqual((1 to 10) ++ (51 to 70) ++ (31 to 50))
+
+ // Skip 40 bytes (next: 111)
+ expect(stream.skip(40)).toBe(40)
+
+ // Read 50 bytes, should wrap (next: 161)
+ expect(stream.read(buf)).toBe(50)
+ expect(buf).toEqual((111 to 127) ++ (-128 to -96))
+
+ // Read 45 bytes, should read 40 (next: EOF)
+ expect(stream.read(buf, 5, 45)).toBe(40)
+ expect(buf).toEqual((111 to 115) ++ (-95 to -56) ++ (-100 to -96))
+
+ // Read 50 bytes, should read nothing
+ expect(stream.read(buf)).toBe(-1)
+ expect(stream.read(buf, 0, 0)).toBe(0)
+ expect(buf).toEqual((111 to 115) ++ (-95 to -56) ++ (-100 to -96))
+ }
+
+ it("should provide a default implementation of `skip`") {
+ val stream = new DummyInputStream(10)
+
+ expect(stream.skip(5)).toBe(5)
+ expect(stream.read()).toBe(6)
+ expect(stream.skip(1)).toBe(1)
+ expect(stream.read()).toBe(8)
+ expect(stream.skip(-5)).toBe(0)
+ expect(stream.read()).toBe(9)
+ expect(stream.skip(0)).toBe(0)
+ expect(stream.read()).toBe(10)
+ expect(stream.skip(10)).toBe(0)
+ }
+
+ }
+
+ describe("java.io.ByteArrayInputStream") {
+ byteArrayInputStreamLikeTests(seq =>
+ new ByteArrayInputStream(seq.map(_.toByte).toArray))
+ }
+
+ describe("java.io.ByteArrayOutputStream") {
+
+ it("should support simple write(x: Int)") {
+ val out = new ByteArrayOutputStream()
+
+ for (i <- 0 to 9)
+ out.write(i)
+
+ expect(out.toByteArray).toEqual(js.Array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9))
+ }
+
+ it("should support simple write(x: Array[Byte])") {
+ val out = new ByteArrayOutputStream()
+ val arr = Array[Byte](0, 1, 2, 3, 4, 5)
+
+ out.write(arr, 1, 4)
+ out.write(arr)
+
+ expect(out.toByteArray).toEqual(js.Array(1, 2, 3, 4, 0, 1, 2, 3, 4, 5))
+ }
+
+ it("should support write(x: Array[Byte]) with buffer resize") {
+ val out = new ByteArrayOutputStream(16)
+ val arr = Array[Byte](0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
+
+ out.write(arr)
+ out.write(arr)
+
+ expect(out.toByteArray).toEqual(arr ++ arr)
+ }
+
+ it("should support toString (with UTF8)") {
+ val buf = Array[Byte](72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100,
+ 46, -29, -127, -109, -29, -126, -109, -29, -127, -85, -29, -127, -95,
+ -29, -127, -81, -26, -105, -91, -26, -100, -84, -24, -86, -98, -29,
+ -126, -110, -24, -86, -83, -29, -126, -127, -29, -127, -66, -29, -127,
+ -103, -29, -127, -117, -29, -128, -126)
+
+ val out = new ByteArrayOutputStream()
+ out.write(buf)
+
+ expect(out.toString).toEqual("Hello World.こんにちは日本語を読めますか。")
+ }
+
+ it("should support reset()") {
+ val out = new ByteArrayOutputStream()
+ for (i <- 0 to 9) out.write(i)
+ out.reset()
+ for (i <- 0 to 9) out.write(i)
+
+ expect(out.toByteArray).toEqual(js.Array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9))
+ }
+
+ }
+
+}
+
+/** tests also used by typedarray.ArrayBufferInputStreamTests */
+trait CommonStreamsTests extends JasmineTest {
+
+ implicit def traversable2array[T](a: TraversableOnce[T]): js.Array[T] = {
+ import js.JSConverters._
+ a.toJSArray
+ }
+
+ implicit def array2array[T](a: Array[T]): js.Array[T] = {
+ import js.JSConverters._
+ a.toJSArray
+ }
+
+ def byteArrayInputStreamLikeTests(mkStream: Seq[Int] => InputStream): Unit = {
+ val length = 50
+ def newStream = mkStream(1 to length)
+
+ it("should provide `read()`") {
+ val stream = newStream
+
+ for (i <- 1 to length)
+ expect(stream.read()).toBe(i)
+
+ for (_ <- 1 to 5)
+ expect(stream.read()).toBe(-1)
+ }
+
+ it("should provide `read(buf)`") {
+ val stream = newStream
+ val buf = new Array[Byte](10)
+
+ expect(stream.read(buf)).toBe(10)
+ expect(buf).toEqual(1 to 10)
+
+ expect(stream.skip(35)).toBe(35)
+
+ expect(stream.read(buf)).toBe(5)
+ expect(buf).toEqual((46 to 50) ++ (6 to 10))
+
+ expect(stream.read(buf)).toBe(-1)
+ expect(stream.read()).toBe(-1)
+ }
+
+ it("should provide full-argument `read`") {
+ val stream = newStream
+ val buf = new Array[Byte](20)
+
+ expect(stream.read(buf, 10, 5)).toBe(5)
+ expect(buf).toEqual(Seq.fill(10)(0) ++ (1 to 5) ++ Seq.fill(5)(0))
+
+ expect(stream.read(buf, 0, 20)).toBe(20)
+ expect(buf).toEqual(6 to 25)
+
+ expect(stream.read(buf, 10, 0)).toBe(0)
+ expect(buf).toEqual(6 to 25)
+
+ expect(() => stream.read(buf, -1, 0)).toThrow
+ expect(() => stream.read(buf, 0, -1)).toThrow
+ expect(() => stream.read(buf, 100, 0)).toThrow
+ expect(() => stream.read(buf, 10, 100)).toThrow
+ expect(buf).toEqual(6 to 25)
+
+ expect(stream.skip(20)).toBe(20)
+
+ expect(stream.read(buf, 0, 10)).toBe(5)
+ expect(buf).toEqual((46 to 50) ++ (11 to 25))
+
+ expect(stream.read(buf, 0, 10)).toBe(-1)
+ expect(stream.read(buf, 0, 0)).toBe(0)
+ expect(buf).toEqual((46 to 50) ++ (11 to 25))
+
+ }
+
+ it("should provide `available`") {
+ val stream = newStream
+
+ def mySkip(n: Int) = for (_ <- 1 to n) expect(stream.read()).not.toBe(-1)
+ def check(n: Int) = expect(stream.available).toBe(n)
+
+ check(50)
+ mySkip(5)
+ check(45)
+ expect(stream.skip(10)).toBe(10)
+ check(35)
+ mySkip(30)
+ check(5)
+ expect(stream.skip(20)).toBe(5)
+ check(0)
+ }
+
+ it("should provide `skip`") {
+ val stream = newStream
+
+ expect(stream.skip(7)).toBe(7)
+
+ for (i <- 8 to 32)
+ expect(stream.read()).toBe(i)
+
+ expect(stream.skip(0)).toBe(0)
+ expect(stream.read()).toBe(33)
+ expect(stream.skip(-4)).toBe(0)
+ expect(stream.read()).toBe(34)
+
+ expect(stream.skip(30)).toBe(16)
+ expect(stream.skip(30)).toBe(0)
+ }
+
+ it("should return true from `markSupported`") {
+ expect(newStream.markSupported).toBe(true)
+ }
+
+ it("should provide no-op `close`") {
+ val stream = newStream
+
+ for (i <- 1 to length) {
+ stream.close()
+ expect(stream.read()).toBe(i)
+ }
+ }
+
+ it("should provide `mark`/`reset`") {
+ val stream = newStream
+
+ def read(range: Range) = for (i <- range) expect(stream.read()).toBe(i)
+
+ read(1 to 10)
+ stream.reset() // mark must be 0 at creation
+ read(1 to 5)
+ stream.mark(length)
+ read(6 to 22)
+ stream.reset()
+ read(6 to 20)
+ stream.reset()
+ read(6 to 25)
+ stream.reset()
+ expect(stream.skip(40)).toBe(40)
+ stream.mark(length)
+ read(46 to 50)
+ stream.reset()
+ read(46 to 50)
+ stream.mark(length)
+ expect(stream.read()).toBe(-1)
+ stream.reset()
+ expect(stream.read()).toBe(-1)
+ }
+
+ it("should return positive integers when calling read") {
+ val stream = mkStream(Seq(-1, -2, -3))
+ expect(stream.read()).toBe(255)
+ expect(stream.read()).toBe(254)
+ expect(stream.read()).toBe(253)
+ expect(stream.read()).toBe(-1)
+ }
+ }
+
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/StringBufferTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/StringBufferTest.scala
new file mode 100644
index 0000000..a034ce6
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/StringBufferTest.scala
@@ -0,0 +1,219 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.javalib
+
+import scala.reflect.{classTag, ClassTag}
+import scala.scalajs.js
+import org.scalajs.jasminetest.JasmineTest
+
+object StringBufferTest extends JasmineTest {
+
+ def shouldThrow[T : ClassTag](fn: => Unit) =
+ try {
+ fn
+ expect("exception").toBe("thrown")
+ } catch {
+ case e: T =>
+ case x: Throwable => expect(x.toString).toBe(classTag[T].runtimeClass.getSimpleName)
+ }
+
+ describe("java.lang.StringBuffer") {
+
+ def newBuf = new java.lang.StringBuffer
+ def initBuf(str: String) = new java.lang.StringBuffer(str)
+
+ it("should respond to `append`") {
+ expect(newBuf.append("asdf").toString).toEqual("asdf")
+ expect(newBuf.append(null: AnyRef).toString).toEqual("null")
+ expect(newBuf.append(null: String).toString).toEqual("null")
+ expect(newBuf.append(null: CharSequence,0,2).toString).toEqual("nu")
+ expect(newBuf.append(js.undefined).toString).toEqual("undefined")
+ expect(newBuf.append(true).toString).toEqual("true")
+ expect(newBuf.append('a').toString).toEqual("a")
+ expect(newBuf.append(Array('a','b','c','d')).toString).toEqual("abcd")
+ expect(newBuf.append(Array('a','b','c','d'), 1, 2).toString).toEqual("bc")
+ expect(newBuf.append(4.toByte).toString).toEqual("4")
+ expect(newBuf.append(304.toShort).toString).toEqual("304")
+ expect(newBuf.append(100000).toString).toEqual("100000")
+ expect(newBuf.append(2.5f).toString).toEqual("2.5")
+ expect(newBuf.append(3.5).toString).toEqual("3.5")
+ }
+
+ it("should respond to `insert`") {
+ expect(newBuf.insert(0, "asdf").toString).toEqual("asdf")
+ expect(newBuf.insert(0, null: AnyRef).toString).toEqual("null")
+ expect(newBuf.insert(0, null: String).toString).toEqual("null")
+ expect(newBuf.insert(0, null: CharSequence,0,2).toString).toEqual("nu")
+ expect(newBuf.insert(0, js.undefined).toString).toEqual("undefined")
+ expect(newBuf.insert(0, true).toString).toEqual("true")
+ expect(newBuf.insert(0, 'a').toString).toEqual("a")
+ expect(newBuf.insert(0, Array('a','b','c','d')).toString).toEqual("abcd")
+ expect(newBuf.insert(0, Array('a','b','c','d'), 1, 2).toString).toEqual("bc")
+ expect(newBuf.insert(0, 4.toByte).toString).toEqual("4")
+ expect(newBuf.insert(0, 304.toShort).toString).toEqual("304")
+ expect(newBuf.insert(0, 100000).toString).toEqual("100000")
+ expect(newBuf.insert(0, 2.5f).toString).toEqual("2.5")
+ expect(newBuf.insert(0, 3.5).toString).toEqual("3.5")
+
+ expect(initBuf("adef").insert(1, "bc")).toEqual("abcdef")
+ expect(initBuf("abcd").insert(4, "ef")).toEqual("abcdef")
+ expect(initBuf("adef").insert(1, Array('b','c'))).toEqual("abcdef")
+ expect(initBuf("adef").insert(1, initBuf("bc"))).toEqual("abcdef")
+ expect(initBuf("abef").insert(2, Array('a','b','c','d','e'), 2, 2)).toEqual("abcdef")
+ expect(initBuf("abef").insert(2, initBuf("abcde"), 2, 4)).toEqual("abcdef")
+
+ shouldThrow[StringIndexOutOfBoundsException](initBuf("abcd").insert(5, "whatever"))
+ shouldThrow[StringIndexOutOfBoundsException](initBuf("abcd").insert(-1, "whatever"))
+ }
+
+ it("should respond to `deleteCharAt`") {
+ expect(initBuf("0123").deleteCharAt(1).toString).toEqual("023")
+ expect(initBuf("0123").deleteCharAt(0).toString).toEqual("123")
+ expect(initBuf("0123").deleteCharAt(3).toString).toEqual("012")
+ shouldThrow[StringIndexOutOfBoundsException](initBuf("0123").deleteCharAt(-1))
+ shouldThrow[StringIndexOutOfBoundsException](initBuf("0123").deleteCharAt(4))
+ }
+
+ it("should respond to `replace`") {
+ expect(initBuf("0123").replace(1,3,"bc").toString).toEqual("0bc3")
+ expect(initBuf("0123").replace(0,4,"abcd").toString).toEqual("abcd")
+ expect(initBuf("0123").replace(0,10,"abcd").toString).toEqual("abcd")
+ expect(initBuf("0123").replace(3,10,"defg").toString).toEqual("012defg")
+ expect(initBuf("0123").replace(0,1,"xxxx").toString).toEqual("xxxx123")
+ expect(initBuf("0123").replace(1,1,"xxxx").toString).toEqual("0xxxx123")
+
+ shouldThrow[StringIndexOutOfBoundsException](initBuf("0123").replace(-1,3,"x"))
+ shouldThrow[StringIndexOutOfBoundsException](initBuf("0123").replace(4,5,"x"))
+ }
+
+ it("should respond to `setCharAt`") {
+ val buf = newBuf
+ buf.append("foobar")
+
+ buf.setCharAt(2, 'x')
+ expect(buf.toString).toEqual("foxbar")
+
+ buf.setCharAt(5, 'h')
+ expect(buf.toString).toEqual("foxbah")
+
+ expect(() => buf.setCharAt(-1, 'h')).toThrow
+ expect(() => buf.setCharAt(6, 'h')).toThrow
+ }
+
+ it("should properly setLength") {
+ val buf = newBuf
+ buf.append("foobar")
+
+ expect(() => buf.setLength(-3)).toThrow
+
+ expect({ buf.setLength(3); buf.toString }).toEqual("foo")
+ expect({ buf.setLength(6); buf.toString }).toEqual("foo\u0000\u0000\u0000")
+ }
+
+ }
+
+ describe("java.lang.StringBuilder") {
+
+ def newBuilder = new java.lang.StringBuilder
+ def initBuilder(str: String) = new java.lang.StringBuilder(str)
+
+ it("should respond to `append`") {
+ expect(newBuilder.append("asdf").toString).toEqual("asdf")
+ expect(newBuilder.append(null: AnyRef).toString).toEqual("null")
+ expect(newBuilder.append(null: String).toString).toEqual("null")
+ expect(newBuilder.append(null: CharSequence,0,2).toString).toEqual("nu")
+ expect(newBuilder.append(js.undefined).toString).toEqual("undefined")
+ expect(newBuilder.append(true).toString).toEqual("true")
+ expect(newBuilder.append('a').toString).toEqual("a")
+ expect(newBuilder.append(Array('a','b','c','d')).toString).toEqual("abcd")
+ expect(newBuilder.append(Array('a','b','c','d'), 1, 2).toString).toEqual("bc")
+ expect(newBuilder.append(4.toByte).toString).toEqual("4")
+ expect(newBuilder.append(304.toShort).toString).toEqual("304")
+ expect(newBuilder.append(100000).toString).toEqual("100000")
+ expect(newBuilder.append(2.5f).toString).toEqual("2.5")
+ expect(newBuilder.append(3.5).toString).toEqual("3.5")
+ }
+
+ it("should respond to `insert`") {
+ expect(newBuilder.insert(0, "asdf").toString).toEqual("asdf")
+ expect(newBuilder.insert(0, null: AnyRef).toString).toEqual("null")
+ expect(newBuilder.insert(0, null: String).toString).toEqual("null")
+ expect(newBuilder.insert(0, null: CharSequence,0,2).toString).toEqual("nu")
+ expect(newBuilder.insert(0, js.undefined).toString).toEqual("undefined")
+ expect(newBuilder.insert(0, true).toString).toEqual("true")
+ expect(newBuilder.insert(0, 'a').toString).toEqual("a")
+ expect(newBuilder.insert(0, Array('a','b','c','d')).toString).toEqual("abcd")
+ expect(newBuilder.insert(0, Array('a','b','c','d'), 1, 2).toString).toEqual("bc")
+ expect(newBuilder.insert(0, 4.toByte).toString).toEqual("4")
+ expect(newBuilder.insert(0, 304.toShort).toString).toEqual("304")
+ expect(newBuilder.insert(0, 100000).toString).toEqual("100000")
+ expect(newBuilder.insert(0, 2.5f).toString).toEqual("2.5")
+ expect(newBuilder.insert(0, 3.5).toString).toEqual("3.5")
+
+ expect(initBuilder("adef").insert(1, "bc")).toEqual("abcdef")
+ expect(initBuilder("abcd").insert(4, "ef")).toEqual("abcdef")
+ expect(initBuilder("adef").insert(1, Array('b','c'))).toEqual("abcdef")
+ expect(initBuilder("adef").insert(1, initBuilder("bc"))).toEqual("abcdef")
+ expect(initBuilder("abef").insert(2, Array('a','b','c','d','e'), 2, 2)).toEqual("abcdef")
+ expect(initBuilder("abef").insert(2, initBuilder("abcde"), 2, 4)).toEqual("abcdef")
+
+ shouldThrow[StringIndexOutOfBoundsException](initBuilder("abcd").insert(5, "whatever"))
+ shouldThrow[StringIndexOutOfBoundsException](initBuilder("abcd").insert(-1, "whatever"))
+ }
+
+ it("should allow string interpolation to survive `null` and `undefined`") {
+ expect(s"${null}").toEqual("null")
+ expect(s"${js.undefined}").toEqual("undefined")
+ }
+
+ it("should respond to `deleteCharAt`") {
+ expect(initBuilder("0123").deleteCharAt(1).toString).toEqual("023")
+ expect(initBuilder("0123").deleteCharAt(0).toString).toEqual("123")
+ expect(initBuilder("0123").deleteCharAt(3).toString).toEqual("012")
+ shouldThrow[StringIndexOutOfBoundsException](initBuilder("0123").deleteCharAt(-1))
+ shouldThrow[StringIndexOutOfBoundsException](initBuilder("0123").deleteCharAt(4))
+ }
+
+ it("should respond to `replace`") {
+ expect(initBuilder("0123").replace(1,3,"bc").toString).toEqual("0bc3")
+ expect(initBuilder("0123").replace(0,4,"abcd").toString).toEqual("abcd")
+ expect(initBuilder("0123").replace(0,10,"abcd").toString).toEqual("abcd")
+ expect(initBuilder("0123").replace(3,10,"defg").toString).toEqual("012defg")
+ expect(initBuilder("0123").replace(0,1,"xxxx").toString).toEqual("xxxx123")
+ expect(initBuilder("0123").replace(1,1,"xxxx").toString).toEqual("0xxxx123")
+
+ shouldThrow[StringIndexOutOfBoundsException](initBuilder("0123").replace(-1,3,"x"))
+ shouldThrow[StringIndexOutOfBoundsException](initBuilder("0123").replace(4,5,"x"))
+ }
+
+ it("should respond to `setCharAt`") {
+ val b = newBuilder
+ b.append("foobar")
+
+ b.setCharAt(2, 'x')
+ expect(b.toString).toEqual("foxbar")
+
+ b.setCharAt(5, 'h')
+ expect(b.toString).toEqual("foxbah")
+
+ expect(() => b.setCharAt(-1, 'h')).toThrow
+ expect(() => b.setCharAt(6, 'h')).toThrow
+ }
+
+ it("should properly setLength") {
+ val b = newBuilder
+ b.append("foobar")
+
+ expect(() => b.setLength(-3)).toThrow
+
+ expect({ b.setLength(3); b.toString }).toEqual("foo")
+ expect({ b.setLength(6); b.toString }).toEqual("foo\u0000\u0000\u0000")
+ }
+
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/StringTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/StringTest.scala
new file mode 100644
index 0000000..a49e521
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/StringTest.scala
@@ -0,0 +1,237 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.javalib
+
+import scala.scalajs.js
+import scala.scalajs.js.JSConverters._
+
+import org.scalajs.jasminetest.JasmineTest
+
+object StringTest extends JasmineTest {
+
+ describe("java.lang.String") {
+
+ it("should respond to `length`") {
+ expect("Scala.js".length).toEqual(8)
+ expect("".length).toEqual(0)
+ }
+
+ it("should respond to `intern`") {
+ val s = "Scala.js"
+ expect(s.intern).toEqual(s)
+ }
+
+ it("should respond to `equals`") {
+ expect("Scala.js".equals("Scala.js")).toBeTruthy
+ expect("Scala.js".equals("Java")).toBeFalsy
+ }
+
+ it("should respond to `equalsIgnoreCase`") {
+ expect("Scala.JS".equalsIgnoreCase("Scala.js")).toBeTruthy
+ expect("åløb".equalsIgnoreCase("ÅLØb")).toBeTruthy
+ expect("Scala.js".equalsIgnoreCase("Java")).toBeFalsy
+ expect("Scala.js".equalsIgnoreCase(null)).toBeFalsy
+ }
+
+ it("should respond to `compareTo`") {
+ expect("Scala.js".compareTo("Scala")).toBeGreaterThan(0)
+ expect("Scala.js".compareTo("Scala.js")).toBe(0)
+ expect("Scala.js".compareTo("banana")).toBeLessThan(0)
+ }
+
+ it("should respond to `compareToIgnoreCase`") {
+ expect("Scala.JS".compareToIgnoreCase("Scala.js")).toBe(0)
+ expect("Scala.JS".compareToIgnoreCase("scala")).toBeGreaterThan(0)
+ expect("åløb".compareToIgnoreCase("ÅLØB")).toBe(0)
+ expect("Java".compareToIgnoreCase("Scala")).toBeLessThan(0)
+ }
+
+ it("should respond to `isEmpty`") {
+ expect("Scala.js".isEmpty).toBeFalsy
+ expect("".isEmpty).toBeTruthy
+ }
+
+ it("should respond to `contains`") {
+ expect("Scala.js".contains("Scala")).toBeTruthy
+ expect("Scala.js".contains("Scala.js")).toBeTruthy
+ expect("ananas".contains("na")).toBeTruthy
+ expect("Scala.js".contains("scala")).toBeFalsy
+ }
+
+ it("should respond to `startWith`") {
+ expect("Scala.js".startsWith("Scala")).toBeTruthy
+ expect("Scala.js".startsWith("Scala.js")).toBeTruthy
+ expect("Scala.js".startsWith("scala")).toBeFalsy
+ expect("ananas".startsWith("an")).toBeTruthy
+ }
+
+ it("should respond to `endsWith`") {
+ expect("Scala.js".endsWith("js")).toBeTruthy
+ expect("Scala.js".endsWith("Scala.js")).toBeTruthy
+ expect("Scala.js".endsWith("JS")).toBeFalsy
+ expect("banana".endsWith("na")).toBeTruthy
+ }
+
+ it("should respond to `indexOf(String)`") {
+ expect("Scala.js".indexOf("js")).toBe(6)
+ expect("Scala.js".indexOf("Scala.js")).toBe(0)
+ expect("ananas".indexOf("na")).toBe(1)
+ expect("Scala.js".indexOf("Java")).toBe(-1)
+ }
+
+ it("should respond to `indexOf(int)`") {
+ expect("abc\uD834\uDF06def\uD834\uDF06def".indexOf(0x61)).toEqual(0)
+ expect("abc\uD834\uDF06def\uD834\uDF06def".indexOf(0x1D306)).toEqual(3)
+ expect("abc\uD834\uDF06def\uD834\uDF06def".indexOf(0xD834)).toEqual(3)
+ expect("abc\uD834\uDF06def\uD834\uDF06def".indexOf(0xDF06)).toEqual(4)
+ expect("abc\uD834\uDF06def\uD834\uDF06def".indexOf(0x64)).toEqual(5)
+ }
+
+ it("should respond to `lastIndexOf(String)`") {
+ expect("Scala.js".lastIndexOf("Scala.js")).toBe(0)
+ expect("ananas".lastIndexOf("na")).toBe(3)
+ expect("Scala.js".lastIndexOf("Java")).toBe(-1)
+ }
+
+ it("should respond to `lastIndexOf(int)`") {
+ expect("abc\uD834\uDF06def\uD834\uDF06def".lastIndexOf(0x61)).toEqual(0)
+ expect("abc\uD834\uDF06def\uD834\uDF06def".lastIndexOf(0x1D306)).toEqual(8)
+ expect("abc\uD834\uDF06def\uD834\uDF06def".lastIndexOf(0xD834)).toEqual(8)
+ expect("abc\uD834\uDF06def\uD834\uDF06def".lastIndexOf(0xDF06)).toEqual(9)
+ expect("abc\uD834\uDF06def\uD834\uDF06def".lastIndexOf(0x64)).toEqual(10)
+ }
+
+ it("should respond to `toUpperCase`") {
+ expect("Scala.js".toUpperCase()).toBe("SCALA.JS")
+ }
+
+ it("should respond to `toLowerCase`") {
+ expect("Scala.js".toLowerCase()).toBe("scala.js")
+ }
+
+ it("should respond to `charAt`") {
+ expect("Scala.js".charAt(5)).toBe('.')
+ expect("Scala.js".charAt(6)).not.toBe('.')
+ }
+
+ when("compliant-asinstanceofs").
+ it("charAt() should throw with out-of-bound indices") {
+ // Type Strings both as CharSequence and String. One will invoke the
+ // helper, the other directly the method on RuntimeString.
+ expect(() => ("Scala.js": CharSequence).charAt(-3)).toThrow
+ expect(() => ("Scala.js": CharSequence).charAt(20)).toThrow
+ expect(() => "Scala.js".charAt(-3)).toThrow
+ expect(() => "Scala.js".charAt(20)).toThrow
+ }
+
+ it("should respond to `codePointAt`") {
+ // String that starts with a BMP symbol
+ expect("abc\uD834\uDF06def".codePointAt(0)).toEqual(0x61)
+ expect("abc\uD834\uDF06def".codePointAt(3)).toEqual(0x1D306)
+ expect("abc\uD834\uDF06def".codePointAt(4)).toEqual(0xDF06)
+ expect("abc\uD834\uDF06def".codePointAt(5)).toEqual(0x64)
+
+ // String that starts with an astral symbol
+ expect("\uD834\uDF06def".codePointAt(0)).toEqual(0x1D306)
+ expect("\uD834\uDF06def".codePointAt(1)).toEqual(0xDF06)
+
+ // Lone high surrogates
+ expect("\uD834abc".codePointAt(0)).toEqual(0xD834)
+
+ // Lone low surrogates
+ expect("\uDF06abc".codePointAt(0)).toEqual(0xDF06)
+ }
+
+ it("should respond to `subSequence`") {
+ expect("Scala.js".subSequence(0, 5)).toBe("Scala")
+ expect("Scala.js".subSequence(6, 8)).toBe("js")
+ expect("Scala.js".subSequence(3, 5)).toBe("la")
+ expect("Scala.js".subSequence(3, 3)).toBe("")
+ }
+
+ it("should respond to `replace`") {
+ expect("Scala.js".replace(".js", "")).toBe("Scala")
+ expect("Scala.js".replace("JS", "")).toBe("Scala.js")
+ expect("aa".replace('a', 'b')).toBe("bb") // #25
+ }
+
+ it("should respond to `matches`") {
+ expect("Scala.js".matches(".*js")).toBeTruthy
+ expect("Scala.js".matches(".*JS")).toBeFalsy
+ }
+
+ it("should respond to `split`") {
+ expect("Scala.js".split("a").toJSArray).toEqual(js.Array("Sc", "l", ".js"))
+ expect("asdf".split("").toJSArray).toEqual(js.Array("","a","s","d","f"))
+ expect("asdf".split("", -1).toJSArray).toEqual(js.Array("","a","s","d","f", ""))
+ }
+
+ it("should respond to `split` with char as argument") {
+ expect("Scala.js".split('.').toJSArray).toEqual(js.Array("Scala","js"))
+ for (i <- 0 to 32) {
+ val c = i.toChar
+ expect(s"blah${c}blah${c}blah${c}blah".split(c).toJSArray).toEqual(
+ js.Array("blah", "blah", "blah", "blah"))
+ }
+ }
+
+ it("should respond to `toCharArray`") {
+ expect("Scala.js".toCharArray()(5)).toEqual('.')
+ }
+
+ it("should respond to `hashCode`") {
+ expect("a`jkxzcbfaslkjfbkj,289oinkasdf".hashCode()).toEqual(-1395193631)
+ expect("-34".hashCode()).toEqual(44878)
+ expect("".hashCode()).toEqual(0)
+ }
+
+ it("should respond to `getChars`") {
+ val trg = new Array[Char](10)
+ "asdf_foo".getChars(2, 6, trg, 3)
+ val exp = Array(0,0,0,'d','f','_','f',0,0,0)
+
+ for ((i,e) <- trg zip exp) {
+ expect(i).toEqual(e)
+ }
+ }
+
+
+ it("should respond to `concat`") {
+ expect("asdf".concat("fdsa")).toEqual("asdffdsa")
+ }
+
+ it("should respond to constructors") {
+ val charArray =
+ Array('a', 'b', 'c', 'd', '\uD834', '\uDF06', 'e', 'f', 'g', 'h', 'i')
+ val codePointArray =
+ Array(65, 0x1D306, 67, 68, 0xD834, 69, 72, 0xDF06)
+
+ expect(new String()).toEqual("")
+ expect(new String(charArray)).toEqual("abcd\uD834\uDF06efghi")
+ expect(new String(charArray, 3, 5)).toEqual("d\uD834\uDF06ef")
+ expect(new String(codePointArray, 1, 5)).toEqual("\uD834\uDF06CD\uD834E")
+ expect(new String("foo")).toEqual("foo")
+ expect(new String(new StringBuffer("buffer-foo"))).toEqual("buffer-foo")
+ expect(new String(new java.lang.StringBuilder("builder-foo"))
+ ).toEqual("builder-foo")
+ }
+
+ it("should provide `format`") {
+ expect(String.format("%d", new Integer(5))).toEqual("5")
+ expect(String.format("%05d", new Integer(5))).toEqual("00005")
+ expect(String.format("%0#5x", new Integer(5))).toEqual("0x005")
+ expect(String.format("%#5x", new Integer(5))).toEqual(" 0x5")
+ expect(String.format("%#5X", new Integer(5))).toEqual(" 0X5")
+ expect(String.format("%5d", new Integer(-10))).toEqual(" -10")
+ expect(String.format("%05d", new Integer(-10))).toEqual("-0010")
+ expect(String.format("%x", new Integer(-3))).toEqual("fffffffd")
+ expect(String.format("%x", new java.lang.Byte(-4.toByte))).toEqual("fffffffc")
+ }
+
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/SystemTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/SystemTest.scala
new file mode 100644
index 0000000..4435e00
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/SystemTest.scala
@@ -0,0 +1,118 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.javalib
+
+import language.implicitConversions
+
+import scala.scalajs.js
+import scala.scalajs.js.JSConverters._
+
+import org.scalajs.jasminetest.JasmineTest
+
+object SystemTest extends JasmineTest {
+
+ // Just in here, we allow ourselves to do this
+ implicit def array2jsArray[T](arr: Array[T]): js.Array[T] = arr.toJSArray
+
+ describe("java.lang.System") {
+
+ it("should respond to `arraycopy`") {
+ val object0 = Array[Any]("[", "b", "c", "d", "e", "f", "]")
+ val object1 = Array[Any](() => true, 1, "2", '3', 4.0, true, object0)
+
+ System.arraycopy(object1, 1, object0, 1, 5)
+ expect(object0.mkString).toEqual("[1234true]")
+
+ val string0 = Array("a", "b", "c", "d", "e", "f")
+ val string1 = Array("1", "2", "3", "4")
+
+ System.arraycopy(string1, 0, string0, 3, 3)
+ expect(string0.mkString).toEqual("abc123")
+
+ val ab01Chars = Array("ab".toCharArray, "01".toCharArray)
+ val chars = new Array[Array[Char]](32)
+ System.arraycopy(ab01Chars, 0, chars, 0, 2)
+ for (i <- Seq(0, 2, 4, 8, 16)) {
+ System.arraycopy(chars, i / 4, chars, i, i)
+ }
+
+ expect(chars.filter(_ == null).length).toEqual(12)
+ expect(chars.filter(_ != null).map(_.mkString).mkString).
+ toEqual("ab01ab0101ab01ab0101ab0101ab01ab0101ab01")
+ }
+
+ it("should respond to `arraycopy` with range overlaps for the same array") {
+ val array = new Array[Int](10)
+
+ for (i <- 1 to 6) {
+ array(i) = i
+ }
+
+ expect(array).toEqual(Array(0, 1, 2, 3, 4, 5, 6, 0, 0, 0))
+ System.arraycopy(array, 0, array, 3, 7)
+ expect(array).toEqual(Array(0, 1, 2, 0, 1, 2, 3, 4, 5, 6))
+
+ System.arraycopy(array, 0, array, 1, 0)
+ expect(array).toEqual(Array(0, 1, 2, 0, 1, 2, 3, 4, 5, 6))
+
+ System.arraycopy(array, 0, array, 1, 9)
+ expect(array).toEqual(Array(0, 0, 1, 2, 0, 1, 2, 3, 4, 5))
+
+ System.arraycopy(array, 1, array, 0, 9)
+ expect(array).toEqual(Array(0, 1, 2, 0, 1, 2, 3, 4, 5, 5))
+
+ System.arraycopy(array, 0, array, 0, 10)
+ expect(array).toEqual(Array(0, 1, 2, 0, 1, 2, 3, 4, 5, 5))
+
+ val reversed = array.reverse
+ System.arraycopy(reversed, 5, array, 5, 5)
+ expect(array).toEqual(Array(0, 1, 2, 0, 1, 1, 0, 2, 1, 0))
+ }
+
+ it("should provide identityHashCode") {
+ /* This test is more restrictive than the spec, but we know our
+ * implementation will always pass the test.
+ */
+ class HasIDHashCode
+
+ val x1 = new HasIDHashCode
+ val x2 = new HasIDHashCode
+ val x1FirstHash = x1.hashCode()
+ expect(x1.hashCode()).toEqual(x1FirstHash)
+ expect(x1.hashCode()).not.toEqual(x2.hashCode())
+ expect(x1.hashCode()).toEqual(x1FirstHash)
+
+ expect(System.identityHashCode(x1)).toEqual(x1FirstHash)
+ expect(System.identityHashCode(x2)).toEqual(x2.hashCode())
+ }
+
+ it("identityHashCode should by-pass .hashCode()") {
+ val list1 = List(1, 3, 5)
+ val list2 = List(1, 3, 5)
+ expect(list1 == list2).toBeTruthy
+ expect(list1.hashCode()).toEqual(list2.hashCode())
+ expect(System.identityHashCode(list1)).not.toEqual(System.identityHashCode(list2))
+ }
+
+ it("identityHashCode(null)") {
+ expect(System.identityHashCode(null)).toEqual(0)
+ }
+
+ it("identityHashCode of values implemented as JS primitives") {
+ expect(System.identityHashCode("foo")).toEqual("foo".hashCode())
+ expect(System.identityHashCode("")).toEqual("".hashCode())
+
+ expect(System.identityHashCode(false)).toEqual(false.hashCode())
+ expect(System.identityHashCode(true)).toEqual(true.hashCode())
+
+ expect(System.identityHashCode(5)).toEqual(5.hashCode())
+ expect(System.identityHashCode(789456)).toEqual(789456.hashCode())
+ }
+
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ThrowablesTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ThrowablesTest.scala
new file mode 100644
index 0000000..7e53975
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ThrowablesTest.scala
@@ -0,0 +1,90 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.javalib
+
+import org.scalajs.jasminetest.JasmineTest
+
+object ThrowablesTest extends JasmineTest {
+ describe("java.lang.Throwables, java.util.Throwables") {
+
+ it("should define all java.lang and java.util Errors/Exceptions") {
+ new ArithmeticException()
+ new ArrayIndexOutOfBoundsException()
+ new ArrayStoreException()
+ new ClassCastException()
+ new ClassNotFoundException()
+ new CloneNotSupportedException()
+ // Needs an instance of java.lang.Enum.
+ // import scala.language.existentials
+ // new EnumConstantNotPresentException(null.asInstanceOf[Class[_ <: Enum[T] forSome { type T <: Enum[T] }]], null)
+ new Exception()
+ new IllegalAccessException()
+ new IllegalArgumentException()
+ new IllegalMonitorStateException()
+ new IllegalStateException()
+ new IllegalThreadStateException()
+ new IndexOutOfBoundsException()
+ new InstantiationException()
+ new InterruptedException()
+ new NegativeArraySizeException()
+ new NoSuchFieldException()
+ new NoSuchMethodException()
+ new NullPointerException()
+ new NumberFormatException()
+ new RuntimeException()
+ new SecurityException()
+ new StringIndexOutOfBoundsException()
+ new TypeNotPresentException(null, null)
+ new UnsupportedOperationException()
+ new AbstractMethodError()
+ new AssertionError()
+ new ClassCircularityError()
+ new ClassFormatError()
+ new Error()
+ new ExceptionInInitializerError()
+ new IllegalAccessError()
+ new IncompatibleClassChangeError()
+ new InstantiationError()
+ new InternalError()
+ new LinkageError()
+ new NoClassDefFoundError()
+ new NoSuchFieldError()
+ new NoSuchMethodError()
+ new OutOfMemoryError()
+ new StackOverflowError()
+ new UnknownError()
+ new UnsatisfiedLinkError()
+ new UnsupportedClassVersionError()
+ new VerifyError()
+ new VirtualMachineError() {}
+
+ import java.util._
+ new ServiceConfigurationError("")
+ new ConcurrentModificationException()
+ new DuplicateFormatFlagsException("")
+ new EmptyStackException()
+ new FormatFlagsConversionMismatchException("", '\u0000')
+ new FormatterClosedException()
+ new IllegalFormatCodePointException(0)
+ new IllegalFormatConversionException('\u0000', new Object().getClass)
+ new IllegalFormatFlagsException("")
+ new IllegalFormatPrecisionException(0)
+ new IllegalFormatWidthException(0)
+ new InputMismatchException()
+ // Needs java.io.IOException.
+ // new InvalidPropertiesFormatException("")
+ new MissingFormatArgumentException("")
+ new MissingFormatWidthException("")
+ new MissingResourceException(null, null, null)
+ new NoSuchElementException()
+ new TooManyListenersException()
+ new UnknownFormatConversionException("")
+ new UnknownFormatFlagsException("")
+ }
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/TimeUnitTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/TimeUnitTest.scala
new file mode 100644
index 0000000..79cd8a8
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/TimeUnitTest.scala
@@ -0,0 +1,135 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.javalib
+
+import scala.scalajs.js
+import org.scalajs.jasminetest.JasmineTest
+import java.util.concurrent.TimeUnit
+
+object TimeUnitTest extends JasmineTest {
+
+ describe("java.util.concurrent.TimeUnit") {
+
+ it("should respond to `toNanos`") {
+ expect(TimeUnit.NANOSECONDS.toNanos(42L)).toEqual(42L)
+ expect(TimeUnit.MICROSECONDS.toNanos(42L)).toEqual(42000L)
+ expect(TimeUnit.MILLISECONDS.toNanos(42L)).toEqual(42000000L)
+ expect(TimeUnit.SECONDS.toNanos(42L)).toEqual(42000000000L)
+ expect(TimeUnit.MINUTES.toNanos(42L)).toEqual(2520000000000L)
+ expect(TimeUnit.HOURS.toNanos(42L)).toEqual(151200000000000L)
+ expect(TimeUnit.DAYS.toNanos(42L)).toEqual(3628800000000000L)
+ }
+
+ it("should respond to `toMicros`") {
+ expect(TimeUnit.NANOSECONDS.toMicros(42L)).toEqual(0L)
+ expect(TimeUnit.NANOSECONDS.toMicros(42123L)).toEqual(42L)
+ expect(TimeUnit.MICROSECONDS.toMicros(42L)).toEqual(42L)
+ expect(TimeUnit.MILLISECONDS.toMicros(42L)).toEqual(42000L)
+ expect(TimeUnit.SECONDS.toMicros(42L)).toEqual(42000000L)
+ expect(TimeUnit.MINUTES.toMicros(42L)).toEqual(2520000000L)
+ expect(TimeUnit.HOURS.toMicros(42L)).toEqual(151200000000L)
+ expect(TimeUnit.DAYS.toMicros(42L)).toEqual(3628800000000L)
+ }
+
+ it("should respond to `toMillis`") {
+ expect(TimeUnit.NANOSECONDS.toMillis(42L)).toEqual(0L)
+ expect(TimeUnit.NANOSECONDS.toMillis(42000123L)).toEqual(42L)
+ expect(TimeUnit.MICROSECONDS.toMillis(42L)).toEqual(0L)
+ expect(TimeUnit.MICROSECONDS.toMillis(42123L)).toEqual(42L)
+ expect(TimeUnit.MILLISECONDS.toMillis(42L)).toEqual(42L)
+ expect(TimeUnit.SECONDS.toMillis(42L)).toEqual(42000L)
+ expect(TimeUnit.MINUTES.toMillis(42L)).toEqual(2520000L)
+ expect(TimeUnit.HOURS.toMillis(42L)).toEqual(151200000L)
+ expect(TimeUnit.DAYS.toMillis(42L)).toEqual(3628800000L)
+ }
+
+ it("should respond to `toSeconds`") {
+ expect(TimeUnit.NANOSECONDS.toSeconds(42L)).toEqual(0L)
+ expect(TimeUnit.NANOSECONDS.toSeconds(42000000123L)).toEqual(42L)
+ expect(TimeUnit.MICROSECONDS.toSeconds(42L)).toEqual(0L)
+ expect(TimeUnit.MICROSECONDS.toSeconds(42000123L)).toEqual(42L)
+ expect(TimeUnit.MILLISECONDS.toSeconds(42L)).toEqual(0L)
+ expect(TimeUnit.MILLISECONDS.toSeconds(42123L)).toEqual(42L)
+ expect(TimeUnit.SECONDS.toSeconds(42L)).toEqual(42L)
+ expect(TimeUnit.MINUTES.toSeconds(42L)).toEqual(2520L)
+ expect(TimeUnit.HOURS.toSeconds(42L)).toEqual(151200L)
+ expect(TimeUnit.DAYS.toSeconds(42L)).toEqual(3628800L)
+ }
+
+ it("should respond to `toMinutes`") {
+ expect(TimeUnit.NANOSECONDS.toMinutes(42L)).toEqual(0L)
+ expect(TimeUnit.NANOSECONDS.toMinutes(2520000007380L)).toEqual(42L)
+ expect(TimeUnit.MICROSECONDS.toMinutes(42L)).toEqual(0L)
+ expect(TimeUnit.MICROSECONDS.toMinutes(2520007380L)).toEqual(42L)
+ expect(TimeUnit.MILLISECONDS.toMinutes(42L)).toEqual(0L)
+ expect(TimeUnit.MILLISECONDS.toMinutes(2520738L)).toEqual(42L)
+ expect(TimeUnit.SECONDS.toMinutes(42L)).toEqual(0L)
+ expect(TimeUnit.SECONDS.toMinutes(2520L)).toEqual(42L)
+ expect(TimeUnit.MINUTES.toMinutes(42L)).toEqual(42L)
+ expect(TimeUnit.HOURS.toMinutes(42L)).toEqual(2520L)
+ expect(TimeUnit.DAYS.toMinutes(42L)).toEqual(60480L)
+ }
+
+ it("should respond to `toHours`") {
+ expect(TimeUnit.NANOSECONDS.toHours(42L)).toEqual(0L)
+ expect(TimeUnit.NANOSECONDS.toHours(151200000442800L)).toEqual(42L)
+ expect(TimeUnit.MICROSECONDS.toHours(42L)).toEqual(0L)
+ expect(TimeUnit.MICROSECONDS.toHours(151200442800L)).toEqual(42L)
+ expect(TimeUnit.MILLISECONDS.toHours(42L)).toEqual(0L)
+ expect(TimeUnit.MILLISECONDS.toHours(151244280L)).toEqual(42L)
+ expect(TimeUnit.SECONDS.toHours(42L)).toEqual(0L)
+ expect(TimeUnit.SECONDS.toHours(151200L)).toEqual(42L)
+ expect(TimeUnit.MINUTES.toHours(42L)).toEqual(0L)
+ expect(TimeUnit.MINUTES.toHours(2520L)).toEqual(42L)
+ expect(TimeUnit.HOURS.toHours(42L)).toEqual(42L)
+ expect(TimeUnit.DAYS.toHours(42L)).toEqual(1008L)
+ }
+
+ it("should respond to `toDays`") {
+ expect(TimeUnit.NANOSECONDS.toDays(42L)).toEqual(0L)
+ expect(TimeUnit.NANOSECONDS.toDays(3628800010627200L)).toEqual(42L)
+ expect(TimeUnit.MICROSECONDS.toDays(42L)).toEqual(0L)
+ expect(TimeUnit.MICROSECONDS.toDays(3628810627200L)).toEqual(42L)
+ expect(TimeUnit.MILLISECONDS.toDays(42L)).toEqual(0L)
+ expect(TimeUnit.MILLISECONDS.toDays(3629862720L)).toEqual(42L)
+ expect(TimeUnit.SECONDS.toDays(42L)).toEqual(0L)
+ expect(TimeUnit.SECONDS.toDays(3628800L)).toEqual(42L)
+ expect(TimeUnit.MINUTES.toDays(42L)).toEqual(0L)
+ expect(TimeUnit.MINUTES.toDays(60480L)).toEqual(42L)
+ expect(TimeUnit.HOURS.toDays(42L)).toEqual(1L)
+ expect(TimeUnit.HOURS.toDays(1008L)).toEqual(42L)
+ expect(TimeUnit.DAYS.toDays(42L)).toEqual(42L)
+ }
+
+ it("should respond to `values`") {
+ val values = TimeUnit.values()
+ expect(values.length).toEqual(7)
+ expectTimeUnit(values(0), TimeUnit.NANOSECONDS)
+ expectTimeUnit(values(1), TimeUnit.MICROSECONDS)
+ expectTimeUnit(values(2), TimeUnit.MILLISECONDS)
+ expectTimeUnit(values(3), TimeUnit.SECONDS)
+ expectTimeUnit(values(4), TimeUnit.MINUTES)
+ expectTimeUnit(values(5), TimeUnit.HOURS)
+ expectTimeUnit(values(6), TimeUnit.DAYS)
+ }
+
+ it("should respond to `valueOf`") {
+ expectTimeUnit(TimeUnit.valueOf("NANOSECONDS"), TimeUnit.NANOSECONDS)
+ expectTimeUnit(TimeUnit.valueOf("MICROSECONDS"), TimeUnit.MICROSECONDS)
+ expectTimeUnit(TimeUnit.valueOf("MILLISECONDS"), TimeUnit.MILLISECONDS)
+ expectTimeUnit(TimeUnit.valueOf("SECONDS"), TimeUnit.SECONDS)
+ expectTimeUnit(TimeUnit.valueOf("MINUTES"), TimeUnit.MINUTES)
+ expectTimeUnit(TimeUnit.valueOf("HOURS"), TimeUnit.HOURS)
+ expectTimeUnit(TimeUnit.valueOf("DAYS"), TimeUnit.DAYS)
+ }
+
+ }
+
+ def expectTimeUnit(actual: TimeUnit, expected: TimeUnit): Unit =
+ expect(actual.asInstanceOf[js.Any]).toEqual(expected.asInstanceOf[js.Any])
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/URITest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/URITest.scala
new file mode 100644
index 0000000..65a049f
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/URITest.scala
@@ -0,0 +1,312 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.javalib
+
+import scala.scalajs.js
+import org.scalajs.jasminetest.JasmineTest
+
+import java.net.URI
+
+object URITest extends JasmineTest {
+
+ def expectURI(uri: URI, isAbsolute: Boolean, isOpaque: Boolean)(
+ authority: String = null, fragment: String = null,
+ host: String = null, path: String = null, port: Int = -1,
+ query: String = null, scheme: String = null, userInfo: String = null,
+ schemeSpecificPart: String = null)(rawAuthority: String = authority,
+ rawFragment: String = fragment, rawPath: String = path,
+ rawQuery: String = query, rawUserInfo: String = userInfo,
+ rawSchemeSpecificPart: String = schemeSpecificPart): Unit = {
+
+ expect(uri.getAuthority()).toBe(authority)
+ expect(uri.getFragment()).toBe(fragment)
+ expect(uri.getHost()).toBe(host)
+ expect(uri.getPath()).toBe(path)
+ expect(uri.getPort()).toBe(port)
+ expect(uri.getQuery()).toBe(query)
+ expect(uri.getRawAuthority()).toBe(rawAuthority)
+ expect(uri.getRawFragment()).toBe(rawFragment)
+ expect(uri.getRawPath()).toBe(rawPath)
+ expect(uri.getRawQuery()).toBe(rawQuery)
+ expect(uri.getRawSchemeSpecificPart()).toBe(rawSchemeSpecificPart)
+ expect(uri.getRawUserInfo()).toBe(rawUserInfo)
+ expect(uri.getScheme()).toBe(scheme)
+ expect(uri.getSchemeSpecificPart()).toBe(schemeSpecificPart)
+ expect(uri.getUserInfo()).toBe(userInfo)
+ expect(uri.isAbsolute()).toBe(isAbsolute)
+ expect(uri.isOpaque()).toBe(isOpaque)
+
+ }
+
+ describe("java.net.URI") {
+
+ it("should parse vanilla absolute URIs") {
+ expectURI(new URI("http://java.sun.com/j2se/1.3/"), true, false)(
+ scheme = "http",
+ host = "java.sun.com",
+ path = "/j2se/1.3/",
+ authority = "java.sun.com",
+ schemeSpecificPart = "//java.sun.com/j2se/1.3/")()
+ }
+
+ it("should parse absolute URIs with IPv6") {
+ val uri = new URI("http://hans@[ffff::0:128.4.5.3]:345/~hans/")
+ expectURI(uri, true, false)(
+ scheme = "http",
+ host = "[ffff::0:128.4.5.3]",
+ userInfo = "hans",
+ port = 345,
+ path = "/~hans/",
+ authority = "hans@[ffff::0:128.4.5.3]:345",
+ schemeSpecificPart = "//hans@[ffff::0:128.4.5.3]:345/~hans/"
+ )()
+ }
+
+ it("should parse absolute URIs without authority") {
+ expectURI(new URI("file:/~/calendar"), true, false)(
+ scheme = "file",
+ path = "/~/calendar",
+ schemeSpecificPart = "/~/calendar")()
+ }
+
+ it("should parse absolute URIs with empty authority") {
+ expectURI(new URI("file:///~/calendar"), true, false)(
+ authority = "",
+ scheme = "file",
+ path = "/~/calendar",
+ schemeSpecificPart = "///~/calendar")()
+ }
+
+ it("should parse opaque URIs") {
+ expectURI(new URI("mailto:java-net@java.sun.com"), true, true)(
+ scheme = "mailto",
+ schemeSpecificPart = "java-net@java.sun.com")()
+
+ expectURI(new URI("news:comp.lang.java"), true, true)(
+ scheme = "news",
+ schemeSpecificPart = "comp.lang.java")()
+
+ expectURI(new URI("urn:isbn:096139210x"), true, true)(
+ scheme = "urn",
+ schemeSpecificPart = "isbn:096139210x")()
+ }
+
+ it("should parse relative URIs") {
+ expectURI(new URI("docs/guide/collections/designfaq.html#28"), false, false)(
+ path = "docs/guide/collections/designfaq.html",
+ fragment = "28",
+ schemeSpecificPart = "docs/guide/collections/designfaq.html#28"
+ )()
+ expectURI(new URI("../../../demo/jfc/SwingSet2/src/SwingSet2.java"), false, false)(
+ path = "../../../demo/jfc/SwingSet2/src/SwingSet2.java",
+ schemeSpecificPart = "../../../demo/jfc/SwingSet2/src/SwingSet2.java"
+ )()
+ }
+
+ it("should parse relative URIs with IPv4") {
+ expectURI(new URI("//123.5.6.3:45/bar"), false, false)(
+ authority = "123.5.6.3:45",
+ host = "123.5.6.3",
+ port = 45,
+ path = "/bar",
+ schemeSpecificPart = "//123.5.6.3:45/bar"
+ )()
+ }
+
+ it("should parse relative URIs with registry-based authority") {
+ expectURI(new URI("//foo:bar"), false, false)(
+ authority = "foo:bar",
+ schemeSpecificPart = "//foo:bar"
+ )()
+ }
+
+ it("should parse relative URIs with escapes") {
+ expectURI(new URI("//ma%5dx:secret@example.com:8000/foo"), false, false)(
+ authority = "ma]x:secret@example.com:8000",
+ userInfo = "ma]x:secret",
+ host = "example.com",
+ port = 8000,
+ path = "/foo",
+ schemeSpecificPart = "//ma]x:secret@example.com:8000/foo")(
+ rawUserInfo = "ma%5dx:secret",
+ rawAuthority = "ma%5dx:secret@example.com:8000",
+ rawSchemeSpecificPart = "//ma%5dx:secret@example.com:8000/foo")
+ }
+
+ it("should parse relative URIs with fragment only") {
+ expectURI(new URI("#foo"), false, false)(
+ fragment = "foo",
+ path = "",
+ schemeSpecificPart = "#foo"
+ )()
+ }
+
+ it("should provide compareTo") {
+ val x = new URI("http://example.com/asdf%6a")
+ val y = new URI("http://example.com/asdf%6A")
+ val z = new URI("http://example.com/asdfj")
+ val rel = new URI("/foo/bar")
+
+ expect(x.compareTo(y)).toBeGreaterThan(0)
+ expect(x.compareTo(z)).toBeLessThan(0)
+ expect(y.compareTo(z)).toBeLessThan(0)
+ expect(x.compareTo(x)).toBe(0)
+ expect(y.compareTo(y)).toBe(0)
+ expect(z.compareTo(z)).toBe(0)
+ expect(x.compareTo(rel)).toBeGreaterThan(0)
+ expect(y.compareTo(rel)).toBeGreaterThan(0)
+ expect(z.compareTo(rel)).toBeGreaterThan(0)
+ expect(rel.compareTo(rel)).toBe(0)
+ }
+
+ it("should provide equals") {
+ val x = new URI("http://example.com/asdf%6a")
+ val y = new URI("http://example.com/asdf%6A")
+ val z = new URI("http://example.com/asdfj")
+
+ expect(x == y).toBeTruthy
+ expect(x == z).toBeFalsy
+ expect(y == z).toBeFalsy
+ expect(x == x).toBeTruthy
+ expect(y == y).toBeTruthy
+ expect(z == z).toBeTruthy
+
+ expect(new URI("foo:helloWorld%6b%6C") == new URI("foo:helloWorld%6C%6b"))
+ }
+
+ it("should provide normalize") {
+ expectURI(new URI("http://example.com/../asef/../../").normalize, true, false)(
+ scheme = "http",
+ host = "example.com",
+ authority = "example.com",
+ path = "/../../",
+ schemeSpecificPart = "//example.com/../../")()
+ expectURI(new URI("http://example.com/../as/./ef/foo/../../").normalize, true, false)(
+ scheme = "http",
+ host = "example.com",
+ authority = "example.com",
+ path = "/../as/",
+ schemeSpecificPart = "//example.com/../as/")()
+ expectURI(new URI("bar/../fo:o/./bar").normalize, false, false)(
+ path = "./fo:o/bar",
+ schemeSpecificPart = "./fo:o/bar")()
+ expectURI(new URI("bar/..//fo:o//./bar").normalize, false, false)(
+ path = "./fo:o/bar",
+ schemeSpecificPart = "./fo:o/bar")()
+
+ val x = new URI("http://www.example.com/foo/bar")
+ expect(x.normalize eq x).toBeTruthy
+ }
+
+ it("should provide resolve - JavaDoc examples") {
+ val base = "http://java.sun.com/j2se/1.3/"
+ val relative1 = "docs/guide/collections/designfaq.html#28"
+ val resolved1 =
+ "http://java.sun.com/j2se/1.3/docs/guide/collections/designfaq.html#28"
+ val relative2 = "../../../demo/jfc/SwingSet2/src/SwingSet2.java"
+ val resolved2 =
+ "http://java.sun.com/j2se/1.3/demo/jfc/SwingSet2/src/SwingSet2.java"
+
+ expect(new URI(base).resolve(relative1).toString).toEqual(resolved1)
+ expect(new URI(resolved1).resolve(relative2).toString).toEqual(resolved2)
+ }
+
+ it("should provide resolve - RFC2396 examples") {
+ val base = new URI("http://a/b/c/d;p?q")
+ def resTest(ref: String, trg: String) =
+ expect(base.resolve(ref).toString).toEqual(trg)
+
+ // Normal examples
+ resTest("g:h" , "g:h")
+ resTest("g" , "http://a/b/c/g")
+ resTest("./g" , "http://a/b/c/g")
+ resTest("g/" , "http://a/b/c/g/")
+ resTest("/g" , "http://a/g")
+ resTest("//g" , "http://g")
+ resTest("?y" , "http://a/b/c/?y")
+ resTest("g?y" , "http://a/b/c/g?y")
+ resTest("#s" , "http://a/b/c/d;p?q#s")
+ resTest("g#s" , "http://a/b/c/g#s")
+ resTest("g?y#s" , "http://a/b/c/g?y#s")
+ resTest(";x" , "http://a/b/c/;x")
+ resTest("g;x" , "http://a/b/c/g;x")
+ resTest("g;x?y#s", "http://a/b/c/g;x?y#s")
+ resTest("." , "http://a/b/c/")
+ resTest("./" , "http://a/b/c/")
+ resTest(".." , "http://a/b/")
+ resTest("../" , "http://a/b/")
+ resTest("../g" , "http://a/b/g")
+ resTest("../.." , "http://a/")
+ resTest("../../" , "http://a/")
+ resTest("../../g", "http://a/g")
+
+ // Abnormal examples
+ resTest("../../../g" , "http://a/../g")
+ resTest("../../../../g", "http://a/../../g")
+ resTest("/./g" , "http://a/./g")
+ resTest("/../g" , "http://a/../g")
+ resTest("g." , "http://a/b/c/g.")
+ resTest(".g" , "http://a/b/c/.g")
+ resTest("g.." , "http://a/b/c/g..")
+ resTest("..g" , "http://a/b/c/..g")
+ resTest("./../g" , "http://a/b/g")
+ resTest("./g/." , "http://a/b/c/g/")
+ resTest("g/./h" , "http://a/b/c/g/h")
+ resTest("g/../h" , "http://a/b/c/h")
+ resTest("g;x=1/./y" , "http://a/b/c/g;x=1/y")
+ resTest("g;x=1/../y" , "http://a/b/c/y")
+ resTest("g?y/./x" , "http://a/b/c/g?y/./x")
+ resTest("g?y/../x" , "http://a/b/c/g?y/../x")
+ resTest("g#s/./x" , "http://a/b/c/g#s/./x")
+ resTest("g#s/../x" , "http://a/b/c/g#s/../x")
+ resTest("http:g" , "http:g")
+ }
+
+ it("should provide normalize - examples derived from RFC relativize") {
+ expectURI(new URI("http://a/b/c/..").normalize, true, false)(
+ scheme = "http",
+ host = "a",
+ authority = "a",
+ path = "/b/",
+ schemeSpecificPart = "//a/b/")()
+
+ expectURI(new URI("http://a/b/c/.").normalize, true, false)(
+ scheme = "http",
+ host = "a",
+ authority = "a",
+ path = "/b/c/",
+ schemeSpecificPart = "//a/b/c/")()
+ }
+
+ it("should provide relativize") {
+ val x = new URI("http://f%4Aoo@asdf/a")
+ val y = new URI("http://fJoo@asdf/a/b/")
+ val z = new URI("http://f%4aoo@asdf/a/b/")
+ expect(x.relativize(y) eq y).toBeTruthy
+ expect(x.relativize(z).toString()).toEqual("b/")
+
+ def relTest(base: String, trg: String, exp: String) =
+ expect(new URI(base).relativize(new URI(trg)).toString()).toEqual(exp)
+
+ relTest("http://a.ch/a", "http://a.ch/a/b", "b")
+ relTest("http://a.ch/a/", "http://a.ch/a/b", "b")
+ relTest("https://a.ch/a", "http://a.ch/a/b", "http://a.ch/a/b")
+ relTest("/a/b/c", "/a/b/c/d/e", "d/e")
+ relTest("/a/b/c/", "/a/b/c/d/e", "d/e")
+ relTest("/a/b/c/", "/a/b/c/foo:e/d", "foo:e/d") // see bug JDK-7037120
+ relTest("../a/b", "../a/b/c", "c")
+ }
+
+ it("should provide hashCode") {
+ expect(new URI("http://example.com/asdf%6a").hashCode).toEqual(
+ new URI("http://example.com/asdf%6A").hashCode)
+ }
+
+ }
+
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/UUIDTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/UUIDTest.scala
new file mode 100644
index 0000000..b22fe02
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/javalib/UUIDTest.scala
@@ -0,0 +1,180 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.javalib
+
+import scala.scalajs.js
+import org.scalajs.jasminetest.JasmineTest
+
+import java.util.UUID
+
+object UUIDTest extends JasmineTest {
+ describe("java.util.UUID") {
+ it("constructor") {
+ val uuid = new UUID(0xf81d4fae7dec11d0L, 0xa76500a0c91e6bf6L)
+ expect(uuid.getMostSignificantBits() == 0xf81d4fae7dec11d0L).toBeTruthy
+ expect(uuid.getLeastSignificantBits() == 0xa76500a0c91e6bf6L).toBeTruthy
+ expect(uuid.variant()).toEqual(2)
+ expect(uuid.version()).toEqual(1)
+ expect(uuid.timestamp() == 0x1d07decf81d4faeL).toBeTruthy
+ expect(uuid.clockSequence()).toEqual(0x2765)
+ expect(uuid.node() == 0xA0C91E6BF6L).toBeTruthy
+ }
+
+ it("getLeastSignificantBits") {
+ expect(new UUID(0L, 0L).getLeastSignificantBits() == 0L).toBeTruthy
+ expect(new UUID(0L, Long.MinValue).getLeastSignificantBits() == Long.MinValue).toBeTruthy
+ expect(new UUID(0L, Long.MaxValue).getLeastSignificantBits() == Long.MaxValue).toBeTruthy
+ }
+
+ it("getMostSignificantBits") {
+ expect(new UUID(0L, 0L).getMostSignificantBits() == 0L).toBeTruthy
+ expect(new UUID(Long.MinValue, 0L).getMostSignificantBits() == Long.MinValue).toBeTruthy
+ expect(new UUID(Long.MaxValue, 0L).getMostSignificantBits() == Long.MaxValue).toBeTruthy
+ }
+
+ it("version") {
+ expect(new UUID(0L, 0L).version()).toEqual(0)
+ expect(new UUID(0x0000000000001000L, 0L).version()).toEqual(1)
+ expect(new UUID(0x00000000000f2f00L, 0L).version()).toEqual(2)
+ }
+
+ it("variant") {
+ expect(new UUID(0L, 0L).variant()).toEqual(0)
+ expect(new UUID(0L, 0x7000000000000000L).variant()).toEqual(0)
+ expect(new UUID(0L, 0x3ff0000000000000L).variant()).toEqual(0)
+ expect(new UUID(0L, 0x1ff0000000000000L).variant()).toEqual(0)
+
+ expect(new UUID(0L, 0x8000000000000000L).variant()).toEqual(2)
+ expect(new UUID(0L, 0xb000000000000000L).variant()).toEqual(2)
+ expect(new UUID(0L, 0xaff0000000000000L).variant()).toEqual(2)
+ expect(new UUID(0L, 0x9ff0000000000000L).variant()).toEqual(2)
+
+ expect(new UUID(0L, 0xc000000000000000L).variant()).toEqual(6)
+ expect(new UUID(0L, 0xdf00000000000000L).variant()).toEqual(6)
+ }
+
+ it("timestamp") {
+ expect(new UUID(0x0000000000001000L,
+ 0x8000000000000000L).timestamp() == 0L).toBeTruthy
+ expect(new UUID(0x7777777755551333L,
+ 0x8000000000000000L).timestamp() == 0x333555577777777L).toBeTruthy
+
+ expect(() => new UUID(0x0000000000000000L, 0x8000000000000000L).timestamp()).toThrow
+ expect(() => new UUID(0x0000000000002000L, 0x8000000000000000L).timestamp()).toThrow
+ }
+
+ it("clockSequence") {
+ expect(new UUID(0x0000000000001000L, 0x8000000000000000L).clockSequence()).toEqual(0)
+ expect(new UUID(0x0000000000001000L, 0x8fff000000000000L).clockSequence()).toEqual(0x0fff)
+ expect(new UUID(0x0000000000001000L, 0xBfff000000000000L).clockSequence()).toEqual(0x3fff)
+
+ expect(() => new UUID(0x0000000000000000L, 0x8000000000000000L).clockSequence()).toThrow
+ expect(() => new UUID(0x0000000000002000L, 0x8000000000000000L).clockSequence()).toThrow
+ }
+
+ it("node") {
+ expect(new UUID(0x0000000000001000L, 0x8000000000000000L).node() == 0L).toBeTruthy
+ expect(new UUID(0x0000000000001000L, 0x8000ffffffffffffL).node() == 0xffffffffffffL).toBeTruthy
+
+ expect(() => new UUID(0x0000000000000000L, 0x8000000000000000L).node()).toThrow
+ expect(() => new UUID(0x0000000000002000L, 0x8000000000000000L).node()).toThrow
+ }
+
+ it("compareTo") {
+ val uuid0101 = new UUID(1L, 1L)
+ val uuid0111 = new UUID(1L, 0x100000001L)
+ val uuid1000 = new UUID(0x100000000L, 0L)
+
+ expect(uuid0101.compareTo(uuid0101)).toEqual(0)
+ expect(uuid0111.compareTo(uuid0111)).toEqual(0)
+ expect(uuid1000.compareTo(uuid1000)).toEqual(0)
+
+ expect(uuid0101.compareTo(uuid0111)).toBeLessThan(0)
+ expect(uuid0101.compareTo(uuid1000)).toBeLessThan(0)
+ expect(uuid0111.compareTo(uuid1000)).toBeLessThan(0)
+
+ expect(uuid0111.compareTo(uuid0101)).toBeGreaterThan(0)
+ expect(uuid1000.compareTo(uuid0101)).toBeGreaterThan(0)
+ expect(uuid1000.compareTo(uuid0111)).toBeGreaterThan(0)
+ }
+
+ it("hashCode") {
+ expect(new UUID(0L, 0L).hashCode()).toEqual(0)
+ expect(new UUID(123L, 123L).hashCode()).toEqual(new UUID(123L, 123L).hashCode())
+ }
+
+ it("equals") {
+ val uuid1 = new UUID(0L, 0L)
+ expect(uuid1.equals(uuid1)).toBeTruthy
+ expect(uuid1.equals(null)).toBeFalsy
+ expect(uuid1.equals("something else")).toBeFalsy
+
+ val uuid2 = new UUID(0L, 0L)
+ expect(uuid1.equals(uuid2)).toBeTruthy
+
+ val uuid3 = new UUID(0xf81d4fae7dec11d0L, 0xa76500a0c91e6bf6L)
+ val uuid4 = new UUID(0xf81d4fae7dec11d0L, 0xa76500a0c91e6bf6L)
+ expect(uuid3.equals(uuid4)).toBeTruthy
+ expect(uuid3.equals(uuid1)).toBeFalsy
+
+ expect(uuid3.equals(new UUID(0x781d4fae7dec11d0L, 0xa76500a0c91e6bf6L))).toBeFalsy
+ expect(uuid3.equals(new UUID(0xf81d4fae7dec11d1L, 0xa76500a0c91e6bf6L))).toBeFalsy
+ expect(uuid3.equals(new UUID(0xf81d4fae7dec11d0L, 0xa76530a0c91e6bf6L))).toBeFalsy
+ expect(uuid3.equals(new UUID(0xf81d4fae7dec11d0L, 0xa76500a0c91e6cf6L))).toBeFalsy
+ }
+
+ it("toString") {
+ expect(new UUID(0xf81d4fae7dec11d0L,
+ 0xa76500a0c91e6bf6L).toString()).toEqual("f81d4fae-7dec-11d0-a765-00a0c91e6bf6")
+ expect(new UUID(0x0000000000001000L,
+ 0x8000000000000000L).toString()).toEqual("00000000-0000-1000-8000-000000000000")
+ }
+
+ it("randomUUID") {
+ val uuid = UUID.randomUUID()
+ expect(uuid.variant()).toEqual(2)
+ expect(uuid.version()).toEqual(4)
+ }
+
+ it("fromString") {
+ val uuid1 = UUID.fromString("f81d4fae-7dec-11d0-a765-00a0c91e6bf6")
+ expect(uuid1.equals(new UUID(0xf81d4fae7dec11d0L, 0xa76500a0c91e6bf6L))).toBeTruthy
+ expect(uuid1.getMostSignificantBits() == 0xf81d4fae7dec11d0L).toBeTruthy
+ expect(uuid1.getLeastSignificantBits() == 0xa76500a0c91e6bf6L).toBeTruthy
+ expect(uuid1.variant()).toEqual(2)
+ expect(uuid1.version()).toEqual(1)
+ expect(uuid1.timestamp() == 130742845922168750L).toBeTruthy
+ expect(uuid1.clockSequence()).toEqual(10085)
+ expect(uuid1.node() == 690568981494L).toBeTruthy
+
+ val uuid2 = UUID.fromString("00000000-0000-1000-8000-000000000000")
+ expect(uuid2.equals(new UUID(0x0000000000001000L, 0x8000000000000000L)))
+ expect(uuid2.getMostSignificantBits() == 0x0000000000001000L).toBeTruthy
+ expect(uuid2.getLeastSignificantBits() == 0x8000000000000000L).toBeTruthy
+ expect(uuid2.variant()).toEqual(2)
+ expect(uuid2.version()).toEqual(1)
+ expect(uuid2.timestamp() == 0L).toBeTruthy
+ expect(uuid2.clockSequence()).toEqual(0)
+ expect(uuid2.node() == 0L).toBeTruthy
+
+ expect(() => UUID.fromString(null)).toThrow
+ expect(() => UUID.fromString("")).toThrow
+ expect(() => UUID.fromString("f81d4fae_7dec-11d0-a765-00a0c91e6bf6")).toThrow
+ expect(() => UUID.fromString("f81d4fae-7dec_11d0-a765-00a0c91e6bf6")).toThrow
+ expect(() => UUID.fromString("f81d4fae-7dec-11d0_a765-00a0c91e6bf6")).toThrow
+ expect(() => UUID.fromString("f81d4fae-7dec-11d0-a765_00a0c91e6bf6")).toThrow
+ expect(() => UUID.fromString("-7dec-11d0-a765-00a0c91e6bf6")).toThrow
+ expect(() => UUID.fromString("f81d4fae--11d0-a765-00a0c91e6bf6")).toThrow
+ expect(() => UUID.fromString("f81d4fae-7dec--a765-00a0c91e6bf6")).toThrow
+ expect(() => UUID.fromString("f81d4fae-7dec-11d0--00a0c91e6bf6")).toThrow
+ expect(() => UUID.fromString("f81d4fae-7dec-11d0-a765-")).toThrow
+ expect(() => UUID.fromString("f81d4fae-7dec-11d0-a765")).toThrow
+ expect(() => UUID.fromString("f81d4fae-7dZc-11d0-a765-00a0c91e6bf6")).toThrow
+ }
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/ArrayTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/ArrayTest.scala
new file mode 100644
index 0000000..2ffd6b7
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/ArrayTest.scala
@@ -0,0 +1,94 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.jsinterop
+
+import scala.scalajs.js
+import org.scalajs.jasminetest.JasmineTest
+
+object ArrayTest extends JasmineTest {
+
+ describe("scala.scalajs.js.Array") {
+
+ it("should provide implicit conversion from js.Array to ArrayOps - String") {
+ var propCount = 0
+ var propString = ""
+
+ for (item <- js.Array("Sc", "ala", ".", "js")) {
+ expect(item.isInstanceOf[String]).toBeTruthy
+ propCount += 1
+ propString += item
+ }
+
+ expect(propCount).toEqual(4)
+ expect(propString).toEqual("Scala.js")
+ }
+
+ it("should provide implicit conversion from js.Array to ArrayOps - Int") {
+ var propCount = 0
+ var propString = ""
+
+ for (item <- js.Array(7, 3, 5, 7)) {
+ expect(item.isInstanceOf[Int]).toBeTruthy
+ propCount += 1
+ propString += item
+ }
+
+ expect(propCount).toEqual(4)
+ expect(propString).toEqual("7357")
+ }
+
+ it("should provide implicit conversion from js.Array to ArrayOps - Char") {
+ var propCount = 0
+ var propString = ""
+
+ for (item <- js.Array('S', 'c', 'a', 'l', 'a')) {
+ expect(item.isInstanceOf[Char]).toBeTruthy
+ propCount += 1
+ propString += item
+ }
+
+ expect(propCount).toEqual(5)
+ expect(propString).toEqual("Scala")
+ }
+
+ it("should provide implicit conversion from js.Array to ArrayOps - value class") {
+ var propCount = 0
+ var propString = ""
+
+ for (item <- js.Array(new VC(5), new VC(-4))) {
+ expect(item.isInstanceOf[VC]).toBeTruthy
+ propCount += 1
+ propString += item
+ }
+
+ expect(propCount).toEqual(2)
+ expect(propString).toEqual("VC(5)VC(-4)")
+ }
+
+ }
+
+ describe("scala.scalajs.js.JSConverters.JSRichGenTraversableOnce") {
+
+ import js.JSConverters._
+
+ it("should provide toJSArray") {
+ expect(List("foo", "bar").toJSArray).toEqual(js.Array("foo", "bar"))
+ expect(Iterator(1, 2, 3).toJSArray).toEqual(js.Array(1, 2, 3))
+ expect(Array(0.3, 7.3, 8.9).toJSArray).toEqual(js.Array(0.3, 7.3, 8.9))
+ expect(None.toJSArray).toEqual(js.Array())
+ // The following fails on 2.10.x
+ //expect(Some("Hello World").toJSArray).toEqual(js.Array("Hello World"))
+ }
+
+ }
+
+ private class VC(val x: Int) extends AnyVal {
+ override def toString(): String = s"VC($x)"
+ }
+
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/AsyncTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/AsyncTest.scala
new file mode 100644
index 0000000..e37c89e
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/AsyncTest.scala
@@ -0,0 +1,121 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.jsinterop
+
+import scala.scalajs.js
+import scala.scalajs.js.JSConverters._
+
+import org.scalajs.jasminetest.JasmineTest
+
+import scala.concurrent.{Future, ExecutionContext}
+import scala.scalajs.concurrent.JSExecutionContext
+
+import scala.collection.mutable.ArrayBuffer
+
+import org.scalajs.jasmine.JasmineExpectation
+
+object AsyncTest extends JasmineTest {
+
+ def asyncTest(implicit executor: ExecutionContext) = {
+ val steps = new ArrayBuffer[String]
+
+ steps += "prep-future"
+
+ val f1 = Future {
+ steps += "future"
+ 1 + 2 + 3
+ }
+
+ steps += "prep-map"
+
+ val f2 = f1 map { x =>
+ steps += "map"
+ x * 2
+ }
+
+ steps += "prep-foreach"
+
+ f2 foreach { _ => steps += "foreach" }
+
+ steps += "done"
+
+ steps
+ }
+
+ def expect(abuf: ArrayBuffer[String]): JasmineExpectation =
+ expect(abuf.toJSArray)
+
+ describe("scala.scalajs.concurrent.JSExecutionContext.queue") {
+
+ beforeEach {
+ jasmine.Clock.useMock()
+ }
+
+ it("should correctly order future calls") {
+ val res = asyncTest(JSExecutionContext.queue)
+
+ expect(res).toEqual(js.Array(
+ "prep-future",
+ "prep-map",
+ "prep-foreach",
+ "done"))
+
+ jasmine.Clock.tick(1)
+
+ expect(res).toEqual(js.Array(
+ "prep-future",
+ "prep-map",
+ "prep-foreach",
+ "done",
+ "future",
+ "map",
+ "foreach"))
+ }
+
+ }
+
+ describe("scala.scalajs.concurrent.JSExecutionContext.runNow") {
+
+ it("should correctly order future calls") {
+ val res = asyncTest(JSExecutionContext.runNow)
+
+ expect(res).toEqual(js.Array(
+ "prep-future",
+ "future",
+ "prep-map",
+ "map",
+ "prep-foreach",
+ "foreach",
+ "done"))
+ }
+
+ }
+
+ describe("scala.concurrent.Future") {
+
+ it("should support map") {
+ implicit val ec = JSExecutionContext.runNow
+ val f = Future(3).map(x => x*2)
+ expect(f.value.get.get).toEqual(6)
+ }
+
+ it("should support flatMap") {
+ implicit val ec = JSExecutionContext.runNow
+ val f = Future(Future(3)).flatMap(x => x)
+ expect(f.value.get.get).toEqual(3)
+ }
+
+ it("should support sequence") {
+ implicit val ec = JSExecutionContext.runNow
+ val f = Future.sequence(Seq(Future(3), Future(5)))
+ expect(f.value.get.get.toJSArray).toEqual(js.Array(3, 5))
+ }
+
+ }
+
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/DictionaryTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/DictionaryTest.scala
new file mode 100644
index 0000000..8b45395
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/DictionaryTest.scala
@@ -0,0 +1,79 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.jsinterop
+
+import scala.scalajs.js
+import org.scalajs.jasminetest.JasmineTest
+
+object DictionaryTest extends JasmineTest {
+
+ describe("scala.scalajs.js.Dictionary") {
+
+ it("should provide an equivalent of the JS delete keyword - #255") {
+ val obj = js.Dictionary.empty[js.Any]
+ obj("foo") = 42
+ obj("bar") = "foobar"
+
+ expect(obj("foo")).toEqual(42)
+ expect(obj("bar")).toEqual("foobar")
+ obj.delete("foo")
+ expect(obj("foo")).toBeUndefined
+ expect(obj.asInstanceOf[js.Object].hasOwnProperty("foo")).toBeFalsy
+ expect(obj("bar")).toEqual("foobar")
+ }
+
+ // This doesn't work on Rhino due to lack of full strict mode support - #679
+ unless("rhino").
+ it("should behave as specified when deleting a non-configurable property - #461 - #679") {
+ val obj = js.Dictionary.empty[js.Any]
+ js.Object.defineProperty(obj.asInstanceOf[js.Object], "nonconfig",
+ js.Dynamic.literal(value = 4, writable = false).asInstanceOf[js.PropertyDescriptor])
+ expect(obj("nonconfig")).toEqual(4)
+ expect(() => obj.delete("nonconfig")).toThrow
+ expect(obj("nonconfig")).toEqual(4)
+ }
+
+ it("should provide `get`") {
+ val obj = js.Dictionary.empty[Int]
+ obj("hello") = 1
+
+ expect(obj.get("hello").isDefined).toBeTruthy
+ expect(obj.get("world").isDefined).toBeFalsy
+ }
+
+ it("should treat delete as a statement - #907") {
+ val obj = js.Dictionary("a" -> "A")
+ obj.delete("a")
+ }
+
+ it("should desugar arguments to delete statements - #908") {
+ val kh = js.Dynamic.literal( key = "a" ).asInstanceOf[KeyHolder]
+ val dict = js.Dictionary[String]("a" -> "A")
+ def a[T](foo: String) = dict.asInstanceOf[T]
+ a[js.Dictionary[String]]("foo").delete(kh.key)
+ }
+
+ }
+
+ trait KeyHolder extends js.Object {
+ def key: String = js.native
+ }
+
+ describe("scala.scalajs.js.JSConverters.JSRichGenMap") {
+
+ import js.JSConverters._
+
+ it("should provide toJSDictionary") {
+ expect(Map("a" -> 1, "b" -> 2).toJSDictionary).toEqual(
+ js.Dynamic.literal(a = 1, b = 2))
+ expect(Map("a" -> "foo", "b" -> "bar").toJSDictionary).toEqual(
+ js.Dynamic.literal(a = "foo", b = "bar"))
+ }
+
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/DynamicTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/DynamicTest.scala
new file mode 100644
index 0000000..2b6942f
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/DynamicTest.scala
@@ -0,0 +1,187 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.jsinterop
+
+import scala.scalajs.js
+import org.scalajs.jasminetest.JasmineTest
+
+import js.annotation.JSExport
+
+object DynamicTest extends JasmineTest {
+
+ describe("scala.scalajs.js.Dynamic") {
+
+ it("should workaround Scala 2.10 issue with implicit conversion for dynamic fields named x - #8") {
+ class Point(val x: Int, val y: Int)
+
+ def jsonToPoint(json: js.Dynamic) = {
+ new Point(json.x.toString.toInt, json.y.toString.toInt)
+ }
+
+ val json = js.eval("var dynamicTestPoint = { x: 1, y: 2 }; dynamicTestPoint;")
+ val point = jsonToPoint(json.asInstanceOf[js.Dynamic])
+
+ expect(point.x).toEqual(1)
+ expect(point.y).toEqual(2)
+ }
+
+ it("should allow to call functions with arguments named x") {
+ class A {
+ def a = 1
+ }
+
+ class B extends A {
+ @JSExport
+ def x(par: Int) = a + par // make sure `this` is bound correctly in JS
+ }
+
+ val b = (new B).asInstanceOf[js.Dynamic]
+
+ expect(b.x(10)).toEqual(11)
+ }
+
+ it("should allow instanciating JS classes dynamically - #10") {
+ val DynamicTestClass = js.eval("""
+ var DynamicTestClass = function(x) {
+ this.x = x;
+ };
+ DynamicTestClass;
+ """).asInstanceOf[js.Dynamic]
+ val obj = js.Dynamic.newInstance(DynamicTestClass)("Scala.js")
+ expect(obj.x).toEqual("Scala.js")
+ }
+
+ it("should allow instantiating JS classes dynamically with varargs - #708") {
+ val DynamicTestClassVarArgs = js.eval("""
+ var DynamicTestClassVarArgs = function() {
+ this.count = arguments.length;
+ for (var i = 0; i < arguments.length; i++)
+ this['elem'+i] = arguments[i];
+ };
+ DynamicTestClassVarArgs;
+ """).asInstanceOf[js.Dynamic]
+
+ val obj1 = js.Dynamic.newInstance(DynamicTestClassVarArgs)("Scala.js")
+ expect(obj1.count).toEqual(1)
+ expect(obj1.elem0).toEqual("Scala.js")
+
+ val obj2 = js.Dynamic.newInstance(DynamicTestClassVarArgs)(
+ "Scala.js", 42, true)
+ expect(obj2.count).toEqual(3)
+ expect(obj2.elem0).toEqual("Scala.js")
+ expect(obj2.elem1).toEqual(42)
+ expect(obj2.elem2).toEqual(true)
+
+ def obj3Args: Seq[js.Any] = Seq("Scala.js", 42, true)
+ val obj3 = js.Dynamic.newInstance(DynamicTestClassVarArgs)(obj3Args: _*)
+ expect(obj3.count).toEqual(3)
+ expect(obj3.elem0).toEqual("Scala.js")
+ expect(obj3.elem1).toEqual(42)
+ expect(obj3.elem2).toEqual(true)
+ }
+
+ it("should provide an object literal construction") {
+ import js.Dynamic.{ literal => obj }
+ val x = obj(foo = 3, bar = "foobar")
+ expect(x.foo).toEqual(3)
+ expect(x.bar).toEqual("foobar")
+ expect(x.unknown).toBeUndefined()
+
+ val y = obj(
+ inner = obj(name = "inner obj"),
+ fun = { () => 42 }
+ )
+ expect(y.inner.name).toEqual("inner obj")
+ expect(y.fun()).toEqual(42)
+
+ expect(obj().anything).toBeUndefined()
+ }
+
+ it("should provide object literal construction with dynamic naming") {
+ import js.Dynamic.{ literal => obj }
+ val x = obj("foo" -> 3, "bar" -> "foobar")
+ expect(x.foo).toEqual(3)
+ expect(x.bar).toEqual("foobar")
+ expect(x.unknown).toBeUndefined()
+
+ val tup1 = ("hello1", 3: js.Any)
+ val tup2 = ("hello2", 10: js.Any)
+
+ val y = obj(tup1, tup2)
+ expect(y.hello1).toEqual(3)
+ expect(y.hello2).toEqual(10)
+
+ var count = 0
+ val z = obj({ count += 1; ("foo", "bar")})
+ expect(z.foo).toEqual("bar")
+ expect(count).toEqual(1)
+ }
+
+ it("should allow to create an empty object with the literal syntax") {
+ import js.Dynamic.{ literal => obj }
+ val x = obj()
+ expect(x.isInstanceOf[js.Object]).toBeTruthy()
+ }
+
+ it("should properly encode object literal property names") {
+ import js.Dynamic.{ literal => obj }
+
+ val obj0 = obj("3-" -> 42)
+ expect(obj0.`3-`).toEqual(42)
+
+ val obj0Dict = obj0.asInstanceOf[js.Dictionary[js.Any]]
+ expect(obj0Dict("3-")).toEqual(42)
+
+ val checkEvilProperties = js.eval("""
+ function dynamicLiteralNameEncoding_checkEvilProperties(x) {
+ return x['.o[3√!|-pr()per7:3$];'] === ' such eval ';
+ }
+ dynamicLiteralNameEncoding_checkEvilProperties
+ """).asInstanceOf[js.Function1[js.Any, Boolean]]
+ val obj1 = obj(
+ ".o[3√!|-pr()per7:3$];" -> " such eval ").asInstanceOf[js.Dictionary[js.Any]]
+ expect(obj1(".o[3√!|-pr()per7:3$];")).toEqual(" such eval ")
+ expect(checkEvilProperties(obj1)).toEqual(true)
+
+ val checkQuotesProperty = js.eval("""
+ function dynamicLiteralNameEncoding_quote(x) {
+ return x["'" + '"'] === 7357;
+ }
+ dynamicLiteralNameEncoding_quote
+ """).asInstanceOf[js.Function1[js.Any, Boolean]]
+
+ val quote = '"'
+
+ Seq(
+ obj("'" + quote -> 7357),
+ obj(s"'$quote" -> 7357),
+ obj("'\"" -> 7357),
+ obj("'" + quote -> 7357)
+ ).foreach { o =>
+ val dict = o.asInstanceOf[js.Dictionary[js.Any]]
+ expect(dict("'\"")).toEqual(7357)
+ expect(dict("'" + quote)).toEqual(7357)
+ expect(dict(s"'$quote")).toEqual(7357)
+ expect(checkQuotesProperty(o)).toEqual(true)
+ }
+ }
+
+ it("should return subclasses of js.Object in literal construction - #783") {
+ import js.Dynamic.{ literal => obj }
+
+ val a: js.Object = obj(theValue = 1)
+ expect(a.hasOwnProperty("theValue")).toBeTruthy
+ expect(a.hasOwnProperty("noValue")).toBeFalsy
+
+ val b: js.Object = obj("theValue" -> 2)
+ expect(b.hasOwnProperty("theValue")).toBeTruthy
+ expect(b.hasOwnProperty("noValue")).toBeFalsy
+
+ }
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/ExportsTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/ExportsTest.scala
new file mode 100644
index 0000000..d577d8d
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/ExportsTest.scala
@@ -0,0 +1,1075 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.jsinterop
+
+import scala.scalajs.js
+import js.annotation._
+import org.scalajs.jasminetest.{JasmineTest, JasmineTestFramework}
+
+import scala.annotation.meta
+
+object ExportsTest extends JasmineTest {
+
+ /** This package in the JS (export) namespace */
+ val jsPackage = js.Dynamic.global.scala.scalajs.testsuite.jsinterop
+
+ describe("@JSExport") {
+
+ it("should offer exports for methods with implicit name") {
+ class Foo {
+ @JSExport
+ def bar(): Int = 42
+ @JSExport
+ def double(x: Int): Int = x*2
+ }
+
+ val foo = (new Foo).asInstanceOf[js.Dynamic]
+ expect(js.typeOf(foo.bar)).toBe("function")
+ expect(foo.bar()).toEqual(42)
+ expect(foo.double(3)).toEqual(6)
+ }
+
+ it("should offer exports for methods with explicit name") {
+ class Foo {
+ @JSExport("theAnswer")
+ def bar(): Int = 42
+ @JSExport("doubleTheParam")
+ def double(x: Int): Int = x*2
+ }
+
+ val foo = (new Foo).asInstanceOf[js.Dynamic]
+ expect(foo.bar).toBeUndefined
+ expect(js.typeOf(foo.theAnswer)).toBe("function")
+ expect(foo.theAnswer()).toEqual(42)
+ expect(foo.doubleTheParam(3)).toEqual(6)
+ }
+
+ it("should offer exports for methods with constant folded name") {
+ class Foo {
+ @JSExport(ExportNameHolder.methodName)
+ def bar(): Int = 42
+ }
+
+ val foo = (new Foo).asInstanceOf[js.Dynamic]
+ expect(foo.bar).toBeUndefined
+ expect(foo.myMethod()).toEqual(42)
+ }
+
+ it("should offer exports for protected methods") {
+ class Foo {
+ @JSExport
+ protected def bar(): Int = 42
+
+ @JSExport
+ protected[testsuite] def foo(): Int = 100
+ }
+
+ val foo = (new Foo).asInstanceOf[js.Dynamic]
+ expect(js.typeOf(foo.bar)).toBe("function")
+ expect(foo.bar()).toEqual(42)
+ expect(js.typeOf(foo.foo)).toBe("function")
+ expect(foo.foo()).toEqual(100)
+ }
+
+ it("should offer exports for properties with implicit name") {
+ class Foo {
+ private[this] var myY: String = "hello"
+ @JSExport
+ val answer: Int = 42
+ @JSExport
+ var x: Int = 3
+ @JSExport
+ def doubleX: Int = x*2
+ @JSExport
+ def y: String = myY + " get"
+ @JSExport
+ def y_=(v: String): Unit = myY = v + " set"
+ }
+
+ val foo = (new Foo).asInstanceOf[js.Dynamic]
+ expect(js.typeOf(foo.answer)).toBe("number")
+ expect(foo.answer).toEqual(42)
+ expect(foo.x).toEqual(3)
+ expect(foo.doubleX).toEqual(6)
+ foo.x = 23
+ expect(foo.x).toEqual(23)
+ expect(foo.doubleX).toEqual(46)
+ expect(foo.y).toEqual("hello get")
+ foo.y = "world"
+ expect(foo.y).toEqual("world set get")
+ }
+
+ it("should offer exports for properties with explicit name") {
+ class Foo {
+ private[this] var myY: String = "hello"
+ @JSExport("answer")
+ val answerScala: Int = 42
+ @JSExport("x")
+ var xScala: Int = 3
+ @JSExport("doubleX")
+ def doubleXScala: Int = xScala*2
+ @JSExport("y")
+ def yGetter: String = myY + " get"
+ @JSExport("y")
+ def ySetter_=(v: String): Unit = myY = v + " set"
+ }
+
+ val foo = (new Foo).asInstanceOf[js.Dynamic]
+ expect(foo.answerScala).toBeUndefined
+ expect(js.typeOf(foo.answer)).toBe("number")
+ expect(foo.answer).toEqual(42)
+ expect(foo.x).toEqual(3)
+ expect(foo.doubleX).toEqual(6)
+ foo.x = 23
+ expect(foo.x).toEqual(23)
+ expect(foo.doubleX).toEqual(46)
+ expect(foo.y).toEqual("hello get")
+ foo.y = "world"
+ expect(foo.y).toEqual("world set get")
+ }
+
+ it("should offer exports for protected properties") {
+ class Foo {
+ @JSExport
+ protected val x: Int = 42
+ @JSExport
+ protected[testsuite] val y: Int = 43
+ }
+
+ val foo = (new Foo).asInstanceOf[js.Dynamic]
+ expect(foo.x).toEqual(42)
+ expect(foo.y).toEqual(43)
+ }
+
+ it("should offer overloaded exports for methods") {
+ class Foo {
+ @JSExport("foobar")
+ def foo(): Int = 42
+ @JSExport("foobar")
+ def bar(x: Int): Int = x*2
+ }
+
+ val foo = (new Foo).asInstanceOf[js.Dynamic]
+ expect(js.typeOf(foo.foobar)).toBe("function")
+ expect(foo.foobar()).toEqual(42)
+ expect(foo.foobar(3)).toEqual(6)
+ }
+
+ it("should offer multiple exports for the same method") {
+ class Foo {
+ @JSExport
+ @JSExport("b")
+ @JSExport("c")
+ def a(): Int = 1
+ }
+
+ val foo = (new Foo).asInstanceOf[js.Dynamic]
+ expect(js.typeOf(foo.a)).toBe("function")
+ expect(js.typeOf(foo.b)).toBe("function")
+ expect(js.typeOf(foo.c)).toBe("function")
+
+ expect(foo.a()).toEqual(1)
+ expect(foo.b()).toEqual(1)
+ expect(foo.c()).toEqual(1)
+ }
+
+ it("should inherit exports from traits") {
+ trait Foo {
+ @JSExport
+ def x: Int
+
+ @JSExport
+ def method(x: Int): Int
+ }
+
+ class Bar extends Foo {
+ val x = 1
+ def method(x: Int) = 2 * x
+ }
+
+ val bar = (new Bar).asInstanceOf[js.Dynamic]
+ expect(bar.x).toEqual(1)
+ expect(js.typeOf(bar.method)).toBe("function")
+ expect(bar.method(2)).toEqual(4)
+ }
+
+ it("should offer overloading with inherited exports") {
+ class A {
+ @JSExport
+ def foo(x: Int) = 2*x
+ }
+
+ class B extends A{
+ @JSExport("foo")
+ def bar(x: String) = s"Hello $x"
+ }
+
+ val b = (new B).asInstanceOf[js.Dynamic]
+ expect(js.typeOf(b.foo)).toBe("function")
+ expect(b.foo(1)).toEqual(2)
+ expect(b.foo("World")).toEqual("Hello World")
+ }
+
+ it("should offer exports for generic methods") {
+ class Foo {
+ @JSExport
+ def gen[T <: AnyRef](x: T) = x
+ }
+
+ val x = (new Object).asInstanceOf[js.Any]
+
+ val foo = (new Foo).asInstanceOf[js.Dynamic]
+ expect(js.typeOf(foo.gen)).toBe("function")
+ expect(foo.gen(x)).toBe(x)
+ }
+
+ it("should offer exports for lambda return types") {
+ class Foo {
+ @JSExport
+ def lambda(x: Int) = (y: Int) => x + y
+ }
+
+ val foo = (new Foo).asInstanceOf[js.Dynamic]
+ expect(js.typeOf(foo.lambda)).toBe("function")
+
+ val lambda = foo.lambda(5).asInstanceOf[Function1[Int,Int]]
+
+ expect(lambda(4)).toEqual(9)
+ }
+
+ it("should offer exports for multi parameter lists") {
+ class Foo {
+ @JSExport
+ def multiParam(x: Int)(y: Int): Int = x + y
+ }
+
+ val foo = (new Foo).asInstanceOf[js.Dynamic]
+ expect(js.typeOf(foo.multiParam)).toBe("function")
+ expect(foo.multiParam(5,6)).toEqual(11)
+ }
+
+ it("should offer exports for default arguments") {
+ class Foo {
+ @JSExport
+ def defArg(x: Int = 1) = x
+ }
+
+ val foo = (new Foo).asInstanceOf[js.Dynamic]
+ expect(js.typeOf(foo.defArg)).toBe("function")
+ expect(foo.defArg(5)).toEqual(5)
+ }
+
+ it("should offer exports for weird stuff") {
+ class UhOh {
+ // Something no one should export
+ @JSExport
+ def ahem[T : Comparable](x: T)(implicit y: Int) = ???
+ }
+
+ val x = (new UhOh).asInstanceOf[js.Dynamic]
+ expect(js.typeOf(x.ahem)).toBe("function")
+ }
+
+ it("should offer exports with value class return types") {
+ class Foo {
+ @JSExport
+ def vc(x: Int) = new SomeValueClass(x)
+ }
+
+ val foo = (new Foo).asInstanceOf[js.Dynamic]
+ expect(js.typeOf(foo.vc)).toBe("function")
+
+ // The result should be a boxed SomeValueClass
+ val result = foo.vc(5)
+ expect(js.typeOf(result)).toEqual("object")
+ expect((result: Any).isInstanceOf[SomeValueClass]).toBeTruthy
+ expect((result: Any) == (new SomeValueClass(5))).toBeTruthy
+ }
+
+ it("should allow exports with Any as return type") {
+ class A
+ class Foo {
+ @JSExport
+ def foo(switch: Boolean): Any =
+ if (switch) 1 else new A
+ }
+
+ val foo = (new Foo).asInstanceOf[js.Dynamic]
+ expect(foo.foo(true).isInstanceOf[Int]).toBeTruthy
+ expect(foo.foo(false).isInstanceOf[A]).toBeTruthy
+ }
+
+ it("should accept boxed value classes as parameter") {
+ class Foo {
+ @JSExport
+ def vc(x: SomeValueClass) = x.i
+ }
+
+ val foo = (new Foo).asInstanceOf[js.Dynamic]
+ expect(js.typeOf(foo.vc)).toBe("function")
+
+ // The parameter should be a boxed SomeValueClass
+ val valueCls = new SomeValueClass(7)
+ val result = foo.vc(valueCls.asInstanceOf[js.Any])
+ expect(js.typeOf(result)).toEqual("number")
+ expect(result).toEqual(7)
+ }
+
+ it("should offer exports for overridden methods with refined return type") {
+ class A
+ class B extends A
+
+ class C1 {
+ @JSExport
+ def x: A = new A
+ }
+
+ class C2 extends C1 {
+ override def x: B = new B
+ }
+
+ val c2 = (new C2).asInstanceOf[js.Dynamic]
+ expect(c2.x.isInstanceOf[B]).toBeTruthy
+ }
+
+ it("should offer exports for methods with refined types as return type") {
+ class A {
+ @JSExport
+ def foo(x: String): js.Object with js.Dynamic =
+ js.Dynamic.literal(arg = x)
+ }
+
+ val a = (new A).asInstanceOf[js.Dynamic]
+ expect(a.foo("hello")).toEqual(js.Dynamic.literal(arg = "hello"))
+ }
+
+ it("should offer exports for variable argument methods - #393") {
+ class A {
+ @JSExport
+ def foo(i: String*) = i.mkString("|")
+ }
+
+ val a = (new A).asInstanceOf[js.Dynamic]
+
+ expect(a.foo()).toEqual("")
+ expect(a.foo("a", "b", "c")).toEqual("a|b|c")
+ expect(a.foo("a", "b", "c", "d")).toEqual("a|b|c|d")
+ }
+
+ it("should correctly overload in view of difficult repeated parameter lists") {
+ class A {
+ @JSExport
+ def foo(a: String, b: String, i: Int, c: String) = 1
+
+ @JSExport
+ def foo(a: String*) = 2
+
+ @JSExport
+ def foo(x: Int)(a: Int*) = x * 100000 + a.sum
+ }
+
+ val a = (new A).asInstanceOf[js.Dynamic]
+
+ expect(a.foo()).toEqual(2)
+ expect(a.foo("asdf")).toEqual(2)
+ expect(a.foo("asdf", "foo")).toEqual(2)
+ expect(a.foo("asdf", "foo", "bar")).toEqual(2)
+ expect(a.foo("asdf", "foo", 1, "bar")).toEqual(1)
+ expect(a.foo("asdf", "foo", "foo", "bar")).toEqual(2)
+ expect(a.foo(5, 1, 2, 3, 10)).toEqual(500016)
+ expect(a.foo(1)).toEqual(100000)
+ }
+
+ it("should offer exports with default arguments") {
+ class A {
+ var oneCount: Int = 0
+ def one = {
+ oneCount += 1
+ 1
+ }
+ @JSExport
+ def foo(a: Int = one)(b: Int = a + one)(c: Int = b + one) =
+ a + b + c
+ }
+
+ val a = new A
+ val jsa = a.asInstanceOf[js.Dynamic]
+
+ expect(jsa.foo()).toEqual(6)
+ expect(a.oneCount).toEqual(3)
+
+ expect(jsa.foo(2)).toEqual(9)
+ expect(a.oneCount).toEqual(5)
+
+ expect(jsa.foo(2,4)).toEqual(11)
+ expect(a.oneCount).toEqual(6)
+
+ expect(jsa.foo(2,4,10)).toEqual(16)
+ expect(a.oneCount).toEqual(6)
+
+ expect(jsa.foo((),4,10)).toEqual(15)
+ expect(a.oneCount).toEqual(7)
+
+ expect(jsa.foo((),4)).toEqual(10)
+ expect(a.oneCount).toEqual(9)
+ }
+
+ it("should correctly overload methods in presence of default parameters") {
+ class A {
+ @JSExport
+ def foo(a: Int)(b: Int = 5)(c: Int = 7) = 1000 + a + b + c
+
+ @JSExport
+ def foo(a: Int, b: String) = 2
+
+ @JSExport
+ def foo(a: Int, b: Int, c: String) = 3
+ }
+
+ val a = (new A).asInstanceOf[js.Dynamic]
+
+ expect(a.foo(1)).toEqual(1013)
+ expect(a.foo(1, 4)).toEqual(1012)
+ expect(a.foo(1, 4, 5)).toEqual(1010)
+ expect(a.foo(1, "foo")).toEqual(2)
+ expect(a.foo(1, 2, "foo")).toEqual(3)
+
+ }
+
+ it("should prefer overloads taking a js.Undefined over methods with default parameters") {
+ class A {
+ @JSExport
+ def foo(a: Int)(b: String = "asdf") = s"$a $b"
+
+ @JSExport
+ def foo(a: Int, b: js.prim.Undefined) = "woot"
+ }
+
+ val a = (new A).asInstanceOf[js.Dynamic]
+
+ expect(a.foo(1)).toEqual("1 asdf")
+ expect(a.foo(2, "omg")).toEqual("2 omg")
+ expect(a.foo(1, ())).toEqual("woot")
+
+ }
+
+ it("should correctly overload methods in presence of default parameters and repeated parameters") {
+ class A {
+ @JSExport
+ def foo(x: Int, y: Int = 1) = x + y
+ @JSExport
+ def foo(x: String*) = x.mkString("|")
+ }
+
+ val a = (new A).asInstanceOf[js.Dynamic]
+
+ expect(a.foo(1)).toEqual(2)
+ expect(a.foo(1, 2)).toEqual(3)
+ expect(a.foo()).toEqual("")
+ expect(a.foo("foo")).toEqual("foo")
+ expect(a.foo("foo","bar")).toEqual("foo|bar")
+
+ }
+
+ it("should correctly overload exports called `toString`") {
+ class A {
+ override def toString(): String = "no arg"
+ @JSExport
+ def toString(x: Int): String = s"with arg: $x"
+ }
+
+ val a = (new A).asInstanceOf[js.Dynamic]
+ expect(a.applyDynamic("toString")()).toEqual("no arg")
+ expect(a.applyDynamic("toString")(1)).toEqual("with arg: 1")
+ }
+
+ it("should allow to explicitly export toString") {
+ class A {
+ @JSExport("toString")
+ override def toString(): String = "called"
+ }
+
+ val a = (new A).asInstanceOf[js.Dynamic]
+ expect(a.applyDynamic("toString")()).toEqual("called")
+ }
+
+ it("should correctly box repeated parameter lists with value classes") {
+ class A {
+ @JSExport
+ def foo(vcs: SomeValueClass*) = vcs.map(_.i).sum
+ }
+
+ val vc1 = new SomeValueClass(1)
+ val vc2 = new SomeValueClass(2)
+ val a = (new A).asInstanceOf[js.Dynamic]
+
+ expect(a.foo(vc1.asInstanceOf[js.Any], vc2.asInstanceOf[js.Any])).toEqual(3)
+ }
+
+ it("should offer exports for objects with implicit name") {
+ val accessor = jsPackage.ExportedObject
+ expect(accessor).toBeDefined
+ expect(js.typeOf(accessor)).toEqual("function")
+ val obj = accessor()
+ expect(obj).toBeDefined
+ expect(js.typeOf(obj)).toEqual("object")
+ expect(obj.witness).toEqual("witness")
+ }
+
+ it("should offer exports for objects with explicit name") {
+ val accessor = js.Dynamic.global.TheExportedObject
+ expect(accessor).toBeDefined
+ expect(js.typeOf(accessor)).toEqual("function")
+ val obj = accessor()
+ expect(obj).toBeDefined
+ expect(js.typeOf(obj)).toEqual("object")
+ expect(obj.witness).toEqual("witness")
+ }
+
+ it("should offer exports for objects with qualified name") {
+ val accessor = js.Dynamic.global.qualified.testobject.ExportedObject
+ expect(accessor).toBeDefined
+ expect(js.typeOf(accessor)).toEqual("function")
+ val obj = accessor()
+ expect(obj).toBeDefined
+ expect(js.typeOf(obj)).toEqual("object")
+ expect(obj.witness).toEqual("witness")
+ }
+
+ it("should offer exports for objects with constant folded name") {
+ val accessor = js.Dynamic.global.ConstantFoldedObjectExport
+ expect(accessor).toBeDefined
+ expect(js.typeOf(accessor)).toEqual("function")
+ val obj = accessor()
+ expect(obj).toBeDefined
+ expect(js.typeOf(obj)).toEqual("object")
+ expect(obj.witness).toEqual("witness")
+ }
+
+ it("should offer exports for protected objects") {
+ val accessor = jsPackage.ProtectedExportedObject
+ expect(accessor).toBeDefined
+ expect(js.typeOf(accessor)).toEqual("function")
+ val obj = accessor()
+ expect(obj).toBeDefined
+ expect(js.typeOf(obj)).toEqual("object")
+ expect(obj.witness).toEqual("witness")
+ }
+
+ it("should offer exports for classes with implicit name") {
+ val constr = jsPackage.ExportedClass
+ expect(constr).toBeDefined
+ expect(js.typeOf(constr)).toEqual("function")
+ val obj = js.Dynamic.newInstance(constr)(5)
+ expect(obj.x).toEqual(5)
+ }
+
+ it("should offer exports for classes with explicit name") {
+ val constr = js.Dynamic.global.TheExportedClass
+ expect(constr).toBeDefined
+ expect(js.typeOf(constr)).toEqual("function")
+ val obj = js.Dynamic.newInstance(constr)(5)
+ expect(obj.x).toEqual(5)
+ }
+
+ it("should offer exports for classes with qualified name") {
+ val constr = js.Dynamic.global.qualified.testclass.ExportedClass
+ expect(constr).toBeDefined
+ expect(js.typeOf(constr)).toEqual("function")
+ val obj = js.Dynamic.newInstance(constr)(5)
+ expect(obj.x).toEqual(5)
+ }
+
+ it("should offer exports for classes with constant folded name") {
+ val constr = js.Dynamic.global.ConstantFoldedClassExport
+ expect(constr).toBeDefined
+ expect(js.typeOf(constr)).toEqual("function")
+ val obj = js.Dynamic.newInstance(constr)(5)
+ expect(obj.x).toEqual(5)
+ }
+
+ it("should offer exports for protected classes") {
+ val constr = jsPackage.ProtectedExportedClass
+ expect(constr).toBeDefined
+ expect(js.typeOf(constr)).toEqual("function")
+ val obj = js.Dynamic.newInstance(constr)(5)
+ expect(obj.x).toEqual(5)
+ }
+
+ it("should offer export for classes with repeated parameters in ctor") {
+ val constr = jsPackage.ExportedVarArgClass
+ expect(js.Dynamic.newInstance(constr)().result).toEqual("")
+ expect(js.Dynamic.newInstance(constr)("a").result).toEqual("a")
+ expect(js.Dynamic.newInstance(constr)("a", "b").result).toEqual("a|b")
+ expect(js.Dynamic.newInstance(constr)("a", "b", "c").result).toEqual("a|b|c")
+ expect(js.Dynamic.newInstance(constr)(5, "a").result).toEqual("Number: <5>|a")
+ }
+
+ it("should offer export for classes with default parameters in ctor") {
+ val constr = jsPackage.ExportedDefaultArgClass
+ expect(js.Dynamic.newInstance(constr)(1,2,3).result).toEqual(6)
+ expect(js.Dynamic.newInstance(constr)(1).result).toEqual(106)
+ expect(js.Dynamic.newInstance(constr)(1,2).result).toEqual(103)
+ }
+
+ it("should correctly disambiguate overloads involving longs") {
+
+ class Foo {
+ @JSExport
+ def foo(x: Int) = 1
+ @JSExport
+ def foo(x: Long) = 2
+ }
+
+ val foo = (new Foo).asInstanceOf[js.Dynamic]
+
+ // Create a long factory we can call dynamically to retrieve an unboxed
+ // long which is typed as a js.Any
+ object LongFactory {
+ @JSExport
+ def aLong = 1L
+ }
+ val trueJsLong = LongFactory.asInstanceOf[js.Dynamic].aLong
+
+ expect(foo.foo(1)).toEqual(1)
+ expect(foo.foo(trueJsLong)).toEqual(2)
+ }
+
+ it("should return boxed Chars") {
+ class Foo {
+ @JSExport
+ def bar(x: Int): Char = x.toChar
+ }
+ val foo = (new Foo).asInstanceOf[js.Dynamic]
+
+ val funs = js.eval("""
+ var funs = {
+ testIsChar: function(foo) { return JSUtils().isChar(foo.bar(65)); },
+ testCharValue: function(foo) { return JSUtils().charToString(foo.bar(65)); }
+ }; funs;
+ """).asInstanceOf[js.Dynamic]
+
+ expect(funs.testIsChar(foo)).toBeTruthy
+ expect(funs.testCharValue(foo)).toEqual("A")
+ }
+
+ it("should take boxed Chars as parameter") {
+ class Foo {
+ @JSExport
+ def bar(x: Char): Int = x.toInt
+ }
+ val foo = (new Foo).asInstanceOf[js.Dynamic]
+
+ val f = js.eval("""
+ var f = function(foo) { return foo.bar(JSUtils().stringToChar('e')); };
+ f;
+ """).asInstanceOf[js.Dynamic]
+
+ expect(f(foo)).toEqual('e'.toInt)
+ }
+
+ it("should be able to disambiguate an Int from a Char") {
+ class Foo {
+ @JSExport
+ def bar(x: Char): String = "char: "+x
+ @JSExport
+ def bar(x: Int): String = "int: "+x
+ }
+ val foo = (new Foo).asInstanceOf[js.Dynamic]
+
+ val funs = js.eval("""
+ var funs = {
+ testChar: function(foo) { return foo.bar(JSUtils().stringToChar('S')); },
+ testInt: function(foo) { return foo.bar(68); }
+ }; funs;
+ """).asInstanceOf[js.Dynamic]
+
+ expect(funs.testChar(foo)).toEqual("char: S")
+ expect(funs.testInt(foo)).toEqual("int: 68")
+ }
+
+ it("should support exporting constructor parameter fields - #970") {
+ class Foo(@(JSExport @meta.field) val x: Int)
+ val foo = (new Foo(1)).asInstanceOf[js.Dynamic]
+ expect(foo.x).toEqual(1)
+ }
+
+ it("should support exporting case class fields - #970") {
+ case class Foo(@(JSExport @meta.field) x: Int)
+ val foo = (new Foo(1)).asInstanceOf[js.Dynamic]
+ expect(foo.x).toEqual(1)
+ }
+
+ it("should support exporting lazy values - #977") {
+ class Foo {
+ @JSExport
+ lazy val x = 1
+ }
+ val foo = (new Foo).asInstanceOf[js.Dynamic]
+ expect(foo.x).toEqual(1)
+ }
+
+ it("should support exporting all members of a class") {
+ @JSExportAll
+ class Foo {
+ val a = 1
+
+ @JSExport // double annotation allowed
+ def b = 2
+
+ lazy val c = 3
+
+ class Bar // not exported, but should not fail
+ }
+
+ val foo = (new Foo).asInstanceOf[js.Dynamic]
+
+ expect(foo.a).toEqual(1)
+ expect(foo.b).toEqual(2)
+ expect(foo.c).toEqual(3)
+ }
+
+ it("should not export synthetic members with @JSExportAll - #1195") {
+ @JSExportAll
+ case class Foo(x: Int)
+
+ val foo = Foo(1).asInstanceOf[js.Dynamic]
+
+ expect(foo.x).toEqual(1)
+ expect(foo.copy).toBeUndefined
+ }
+
+ it("should allow mutliple equivalent JSExport annotations") {
+ class Foo {
+ @JSExport
+ @JSExport("a")
+ @JSExport
+ @JSExport("a")
+ def b = 1
+ }
+
+ val foo = (new Foo).asInstanceOf[js.Dynamic]
+
+ expect(foo.b).toEqual(1)
+ }
+
+ it("should support named exports") {
+ import js.Dynamic.{literal => lit}
+
+ class FooNamed {
+ @JSExportNamed("bar1")
+ def bar(x: Int, y: Int) = x + y
+
+ @JSExportNamed("bar2")
+ @JSExport
+ def bar(x: Int = 1)(y: Int = x)(z: Int = y) = x + y + z
+ }
+
+ val foo = (new FooNamed).asInstanceOf[js.Dynamic]
+
+ expect(foo.bar1(lit(x = 1, y = 2))).toEqual(3)
+ if (JasmineTestFramework.hasTag("compliant-asinstanceof"))
+ expect(() => foo.bar1(lit(x = 1))).toThrow // missing arg
+ expect(foo.bar2(lit())).toEqual(3)
+ expect(foo.bar2(lit(x = 2))).toEqual(6)
+ expect(foo.bar2(lit(y = 2))).toEqual(5)
+ expect(foo.bar2(lit(y = 2, z = 1))).toEqual(4)
+ expect(foo.bar(2)).toEqual(6)
+ expect(foo.bar(2,3)).toEqual(8)
+ }
+
+ it("should support named constructor exports") {
+ import js.Dynamic.{literal => lit}
+
+ val constr = jsPackage.ExportedNamedArgClass
+ expect(js.Dynamic.newInstance(constr)(lit(x = 2)).result).toEqual("22true")
+ expect(js.Dynamic.newInstance(constr)(lit(y = "foo")).result).toEqual("1foofalse")
+ expect(js.Dynamic.newInstance(constr)(lit(z = true, y = "foo")).result).toEqual("1footrue")
+ }
+
+ it("should support exporting under 'org' namespace - #364") {
+ val accessor = js.Dynamic.global.org.ExportedUnderOrgObject
+ expect(js.typeOf(accessor)).toEqual("function")
+ val obj = accessor()
+ expect(obj).toBe(ExportedUnderOrgObject.asInstanceOf[js.Any])
+ }
+
+ when("compliant-asinstanceof").
+ it("should reject bad values for arguments of primitive value type") {
+ class Foo {
+ @JSExport
+ def doBool(x: Boolean) = x
+ @JSExport
+ def doChar(x: Char) = x
+ @JSExport
+ def doByte(x: Byte) = x
+ @JSExport
+ def doShort(x: Short) = x
+ @JSExport
+ def doInt(x: Int) = x
+ @JSExport
+ def doLong(x: Long) = x
+ @JSExport
+ def doFloat(x: Float) = x
+ @JSExport
+ def doDouble(x: Double) = x
+ @JSExport
+ def doUnit(x: Unit) = x
+ }
+
+ val foo = (new Foo).asInstanceOf[js.Dynamic]
+
+ // Nulls
+ expect(() => foo.doBool(null)).toThrow
+ expect(() => foo.doChar(null)).toThrow
+ expect(() => foo.doByte(null)).toThrow
+ expect(() => foo.doShort(null)).toThrow
+ expect(() => foo.doInt(null)).toThrow
+ expect(() => foo.doLong(null)).toThrow
+ expect(() => foo.doFloat(null)).toThrow
+ expect(() => foo.doDouble(null)).toThrow
+ expect(() => foo.doUnit(null)).toThrow
+
+ // Class type
+ expect(() => foo.doBool(foo)).toThrow
+ expect(() => foo.doChar(foo)).toThrow
+ expect(() => foo.doByte(foo)).toThrow
+ expect(() => foo.doShort(foo)).toThrow
+ expect(() => foo.doInt(foo)).toThrow
+ expect(() => foo.doLong(foo)).toThrow
+ expect(() => foo.doFloat(foo)).toThrow
+ expect(() => foo.doDouble(foo)).toThrow
+ expect(() => foo.doUnit(foo)).toThrow
+
+ // Bad values
+ expect(() => foo.doBool(1)).toThrow
+ expect(() => foo.doBool("a")).toThrow
+
+ expect(() => foo.doChar(1)).toThrow
+ expect(() => foo.doChar("a")).toThrow
+
+ expect(() => foo.doByte(300)).toThrow
+ expect(() => foo.doByte("a")).toThrow
+
+ expect(() => foo.doShort(32768)).toThrow
+ expect(() => foo.doShort("a")).toThrow
+
+ expect(() => foo.doInt(3.2)).toThrow
+ expect(() => foo.doInt("a")).toThrow
+
+ expect(() => foo.doLong(3.2)).toThrow
+ expect(() => foo.doLong(3)).toThrow
+ expect(() => foo.doLong("a")).toThrow
+
+ expect(() => foo.doFloat("a")).toThrow
+ }
+
+ when("compliant-asinstanceof").
+ it("should reject bad values for arguments of value class type - #613") {
+ class Foo {
+ @JSExport
+ def doVC(x: SomeValueClass) = x
+ }
+
+ val foo = (new Foo).asInstanceOf[js.Dynamic]
+
+ expect(() => foo.doVC(null)).toThrow
+ expect(() => foo.doVC(foo)).toThrow
+ expect(() => foo.doVC(1)).toThrow
+ expect(() => foo.doVC("a")).toThrow
+ }
+
+ when("compliant-asinstanceof").
+ it("should reject bad values for arguments of class type") {
+ class A
+ class B
+
+ class Foo {
+ @JSExport
+ def doA(x: A) = x
+ }
+
+ val foo = (new Foo).asInstanceOf[js.Dynamic]
+
+ expect(() => foo.doA(1)).toThrow
+ expect(() => foo.doA((new B).asInstanceOf[js.Any])).toThrow
+ expect(() => foo.doA("a")).toThrow
+ }
+
+ it("should offer exports for classes ending in _= - #1090") {
+ val constr = jsPackage.ExportClassSetterNamed_=
+ val obj = js.Dynamic.newInstance(constr)()
+ expect(obj.x).toBe(1)
+ }
+
+ it("should offer exports for objects ending in _= - #1090") {
+ expect(jsPackage.ExportObjSetterNamed_=().x).toBe(1)
+ }
+
+ } // describe
+
+ describe("@JSExportDescendentObjects") {
+
+ it("should offer auto exports for objects extending a trait") {
+ val accessor =
+ js.Dynamic.global.scala.scalajs.testsuite.jsinterop.AutoExportedTraitObject
+ expect(accessor).toBeDefined
+ expect(js.typeOf(accessor)).toEqual("function")
+ val obj = accessor()
+ expect(obj).toBeDefined
+ expect(obj).toBe(AutoExportedTraitObject.asInstanceOf[js.Any])
+ }
+
+ it("should offer auto exports for objects extending a class") {
+ val accessor =
+ js.Dynamic.global.scala.scalajs.testsuite.jsinterop.AutoExportedClassObject
+ expect(accessor).toBeDefined
+ expect(js.typeOf(accessor)).toEqual("function")
+ val obj = accessor()
+ expect(obj).toBeDefined
+ expect(obj).toBe(AutoExportedClassObject.asInstanceOf[js.Any])
+ }
+
+ }
+
+ describe("@JSExportDescendentClasses") {
+
+ it("should offer auto exports for classes extending a trait") {
+ val ctor =
+ js.Dynamic.global.scala.scalajs.testsuite.jsinterop.AutoExportedTraitClass
+ expect(ctor).toBeDefined
+ expect(js.typeOf(ctor)).toEqual("function")
+
+ val obj1 = js.Dynamic.newInstance(ctor)()
+ expect(obj1).toBeDefined
+ expect(obj1.x).toBe(5)
+
+ val obj2 = js.Dynamic.newInstance(ctor)(100)
+ expect(obj2).toBeDefined
+ expect(obj2.x).toBe(100)
+ }
+
+ it("should offer auto exports for classes extending a class") {
+ val ctor =
+ js.Dynamic.global.scala.scalajs.testsuite.jsinterop.AutoExportedClassClass
+ expect(ctor).toBeDefined
+ expect(js.typeOf(ctor)).toEqual("function")
+
+ val obj1 = js.Dynamic.newInstance(ctor)()
+ expect(obj1).toBeDefined
+ expect(obj1.x).toBe(5)
+
+ val obj2 = js.Dynamic.newInstance(ctor)(100)
+ expect(obj2).toBeDefined
+ expect(obj2.x).toBe(100)
+ }
+
+ }
+
+}
+
+object ExportNameHolder {
+ final val className = "ConstantFoldedClassExport"
+ final val objectName = "ConstantFoldedObjectExport"
+ final val methodName = "myMethod"
+}
+
+@JSExport
+@JSExport("TheExportedObject")
+@JSExport("qualified.testobject.ExportedObject") // purposefully halfway the same as ExportedClass
+@JSExport(ExportNameHolder.objectName)
+object ExportedObject {
+ @JSExport
+ def witness: String = "witness"
+}
+
+@JSExport
+protected object ProtectedExportedObject {
+ @JSExport
+ def witness: String = "witness"
+}
+
+@JSExport
+@JSExport("TheExportedClass")
+@JSExport("qualified.testclass.ExportedClass") // purposefully halfway the same as ExportedObject
+@JSExport(ExportNameHolder.className)
+class ExportedClass(_x: Int) {
+ @JSExport
+ val x = _x
+}
+
+@JSExport
+protected class ProtectedExportedClass(_x: Int) {
+ @JSExport
+ val x = _x
+}
+
+@JSExport
+class ExportedVarArgClass(x: String*) {
+
+ @JSExport
+ def this(x: Int, y: String) = this(s"Number: <$x>", y)
+
+ @JSExport
+ def result = x.mkString("|")
+}
+
+@JSExport
+class ExportedDefaultArgClass(x: Int, y: Int, z: Int) {
+
+ @JSExport
+ def this(x: Int, y: Int = 5) = this(x, y, 100)
+
+ @JSExport
+ def result = x + y + z
+}
+
+@JSExport("org.ExportedUnderOrgObject")
+object ExportedUnderOrgObject
+
+@JSExportDescendentClasses
+@JSExportDescendentObjects
+trait AutoExportTrait
+
+object AutoExportedTraitObject extends AutoExportTrait
+class AutoExportedTraitClass(_x: Int) extends AutoExportTrait {
+ def this() = this(5)
+ @JSExport
+ def x: Int = _x
+}
+
+@JSExportDescendentClasses
+@JSExportDescendentObjects
+class AutoExportClass
+
+object AutoExportedClassObject extends AutoExportClass
+class AutoExportedClassClass(_x: Int) extends AutoExportTrait {
+ def this() = this(5)
+ @JSExport
+ def x: Int = _x
+}
+
+class SomeValueClass(val i: Int) extends AnyVal
+
+@JSExportNamed
+class ExportedNamedArgClass(x: Int = 1)(y: String = x.toString)(z: Boolean = y != "foo") {
+ @JSExport
+ val result = x + y + z
+}
+
+@JSExport
+class ExportClassSetterNamed_= {
+ @JSExport
+ val x = 1
+}
+
+@JSExport
+object ExportObjSetterNamed_= {
+ @JSExport
+ val x = 1
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/FunctionTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/FunctionTest.scala
new file mode 100644
index 0000000..4973e64
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/FunctionTest.scala
@@ -0,0 +1,41 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.jsinterop
+
+import scala.scalajs.js
+import org.scalajs.jasminetest.JasmineTest
+
+object FunctionTest extends JasmineTest {
+
+ import js.Dynamic.{literal => lit}
+
+ describe("scala.scalajs.js.Function") {
+
+ it("should support call() with expanded arguments") {
+ val f = js.eval("""
+ var f = function() { return arguments; }; f;
+ """).asInstanceOf[js.Function]
+
+ expect(f.call(null, 42, true)).toEqual(lit(
+ `0` = 42,
+ `1` = true))
+ }
+
+ it("should support call() with the :_* notation to expand a Seq") {
+ val f = js.eval("""
+ var f = function() { return arguments; }; f;
+ """).asInstanceOf[js.Function]
+
+ val args = Seq[js.Any](42, true)
+ expect(f.call(null, args: _*)).toEqual(lit(
+ `0` = 42,
+ `1` = true))
+ }
+
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/MiscInteropTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/MiscInteropTest.scala
new file mode 100644
index 0000000..08211c3
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/MiscInteropTest.scala
@@ -0,0 +1,89 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.jsinterop
+
+import scala.scalajs.js
+import org.scalajs.jasminetest.JasmineTest
+
+object MiscInteropTest extends JasmineTest {
+
+ describe("scala.scalajs.js.package") {
+
+ it("should provide an equivalent to `typeof x`") {
+ import js.typeOf
+ expect(typeOf(5)).toEqual("number")
+ expect(typeOf(false)).toEqual("boolean")
+ expect(typeOf("hello")).toEqual("string")
+ expect(typeOf(null)).toEqual("object")
+ expect(typeOf(new js.Object)).toEqual("object")
+ expect(typeOf(())).toEqual("undefined")
+ expect(typeOf(() => 42)).toEqual("function")
+ }
+ }
+
+ describe("scala.scalajs.js.Object") {
+
+ it("should provide an equivalent to `p in o`") {
+ import js.Object.{ hasProperty => hasProp }
+ val o = js.Dynamic.literal(foo = 5, bar = "foobar").asInstanceOf[js.Object]
+ expect(hasProp(o, "foo")).toBeTruthy
+ expect(hasProp(o, "foobar")).toBeFalsy
+ expect(hasProp(o, "toString")).toBeTruthy // in prototype
+ }
+
+ it("should respect evaluation order for `hasProperty`") {
+ import js.Object.{ hasProperty => hasProp }
+ var indicator = 3
+ def o() = {
+ indicator += 4
+ js.Dynamic.literal(x = 5).asInstanceOf[js.Object]
+ }
+ def p() = {
+ indicator *= 2
+ "x"
+ }
+ expect(hasProp(o(), p())).toBeTruthy
+ expect(indicator).toEqual(14)
+ }
+
+ it("should provide equivalent of JS for-in loop of {} - #13") {
+ val obj = js.eval("var dictionaryTest13 = { a: 'Scala.js', b: 7357 }; dictionaryTest13;")
+ val dict = obj.asInstanceOf[js.Dictionary[js.Any]]
+ var propCount = 0
+ var propString = ""
+
+ for (prop <- js.Object.properties(dict)) {
+ propCount += 1
+ propString += dict(prop)
+ }
+
+ expect(propCount).toEqual(2)
+ expect(propString).toEqual("Scala.js7357")
+ }
+
+ it("should provide equivalent of JS for-in loop of [] - #13") {
+ val obj = js.eval("var arrayTest13 = [ 7, 3, 5, 7 ]; arrayTest13;")
+ val array = obj.asInstanceOf[js.Dictionary[js.Any]]
+ var propCount = 0
+ var propString = ""
+
+ for (prop <- js.Object.properties(array)) {
+ propCount += 1
+ propString += array(prop)
+ }
+
+ expect(propCount).toEqual(4)
+ expect(propString).toEqual("7357")
+ }
+
+ it("should compile js.undefined") {
+ expect(() => js.undefined.asInstanceOf[js.prim.Number].toString(10)).toThrow
+ }
+ }
+
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/RuntimeLongTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/RuntimeLongTest.scala
new file mode 100644
index 0000000..589f379
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/RuntimeLongTest.scala
@@ -0,0 +1,140 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.jsinterop
+
+import scala.scalajs.runtime.RuntimeLong
+
+import org.scalajs.jasmine.JasmineExpectation
+import org.scalajs.jasminetest.JasmineTest
+
+import scala.util.Try
+
+/**
+ * test the runtime Long implementation directly
+ * does not depend on magic compiler Long rewriting
+ */
+object RuntimeLongTest extends JasmineTest {
+
+ import RuntimeLong.fromDouble
+
+ /** overload expect for long to add toString */
+ def expect(l: RuntimeLong): JasmineExpectation = expect(l.toHexString)
+
+ describe("scala.scalajs.runtime.RuntimeLong") {
+
+ def fromInt(x: Int): RuntimeLong = new RuntimeLong(x)
+
+ val maxInt = fromInt(Int.MaxValue)
+ val minInt = fromInt(Int.MinValue)
+ val one = fromInt(1)
+ val billion = fromInt(1000000000)
+
+ val `4503599627370510L` = new RuntimeLong( 14, 0, 256)
+ val `613354684553L` = new RuntimeLong( 639113, 146235, 0)
+ val `9863155567412L` = new RuntimeLong(2247476, 2351559, 0)
+ val `3632147899696541255L` = new RuntimeLong(1568327, 2954580, 206463)
+ val `7632147899696541255L` = new RuntimeLong(2616903, 1593290, 433837)
+
+ it("should correctly implement negation") {
+ expect(-fromInt(5)).toEqual("fffffffffffffffb")
+ expect(-fromInt(0)).toEqual("0")
+ expect(-minInt ).toEqual("80000000")
+ }
+
+ it("should correctly implement comparison") {
+ expect(fromInt(7) < fromInt(15)).toBe(true)
+ expect(fromInt(15) < fromInt(15)).toBe(false)
+ expect(fromInt(15) <= fromInt(15)).toBe(true)
+ expect(fromInt(14) <= fromInt(15)).toBe(true)
+ expect(fromInt(15) > fromInt(15)).toBe(false)
+ expect(fromInt(14) > fromInt(15)).toBe(false)
+ expect(fromInt(16) > fromInt(15)).toBe(true)
+ expect(fromInt(15) >= fromInt(15)).toBe(true)
+ expect(fromInt(14) >= fromInt(15)).toBe(false)
+ expect(fromInt(16) >= fromInt(15)).toBe(true)
+ }
+
+ it("should correctly implement addition") {
+ expect(fromInt(7) + fromInt(15)).toEqual("16")
+ expect( maxInt + maxInt ).toEqual("fffffffe")
+ expect( maxInt + one ).toEqual("80000000")
+ }
+
+ it("should correctly implement subtraction") {
+ expect(fromInt(7) - fromInt(15)).toEqual("fffffffffffffff8")
+ expect( maxInt - maxInt ).toEqual("0")
+ }
+
+ it("should correctly implement multiplication") {
+ expect(fromInt(7) * fromInt(15)).toEqual("69")
+ expect(fromInt(-7) * fromInt(15)).toEqual("ffffffffffffff97")
+ expect( maxInt * maxInt ).toEqual("3fffffff00000001")
+ expect(`4503599627370510L` * fromInt(-4)).toEqual("ffbfffffffffffc8")
+ }
+
+ it("should correctly implement division") {
+ expect( fromInt(7) / fromInt(15)).toEqual("0")
+ expect( fromInt(24) / fromInt(5) ).toEqual("4")
+ expect( fromInt(24) / fromInt(-5)).toEqual("fffffffffffffffc")
+ expect( maxInt / fromInt(-5)).toEqual("ffffffffe6666667")
+ expect( maxInt / billion ).toEqual("2")
+ expect((maxInt+one) / billion ).toEqual("2")
+ }
+
+ it("should correctly implement modulus") {
+ expect( fromInt(7) % fromInt(15)).toEqual("7")
+ expect( fromInt(24) % fromInt(5) ).toEqual("4")
+ expect( fromInt(24) % fromInt(-5)).toEqual("4")
+ expect( maxInt % billion ).toEqual("8ca6bff")
+ expect((maxInt+one) % billion ).toEqual("8ca6c00")
+ expect( maxInt % fromInt(-5)).toEqual("2")
+ }
+
+ it("should correctly implement toString") {
+ expect(maxInt.toString).toEqual("2147483647")
+ expect(fromInt(-50).toString).toEqual("-50")
+ expect(fromInt(-1000000000).toString).toEqual("-1000000000")
+ expect((maxInt+one).toString).toEqual("2147483648")
+ expect(minInt.toString).toEqual("-2147483648")
+ }
+
+ it("should correctly implement fromDouble") {
+ expect(fromDouble( 4.5)).toEqual("4")
+ expect(fromDouble(-4.5)).toEqual("fffffffffffffffc")
+ }
+
+ it("should correctly implement toDouble") {
+ expect(fromInt(5).toDouble).toEqual(5.0)
+ expect((maxInt+one).toDouble).toEqual(2147483648.0)
+ }
+
+ it("should correctly implement numberOfLeadingZeros") {
+ expect(fromInt( 0).numberOfLeadingZeros).toEqual(64)
+ expect(fromInt( 1).numberOfLeadingZeros).toEqual(63)
+ expect(fromInt(-1).numberOfLeadingZeros).toEqual(0)
+ expect(fromInt( 2).numberOfLeadingZeros).toEqual(62)
+ }
+
+ it("should implement hashCode() according to spec in j.l.Long") {
+ expect(fromInt(0 ).hashCode()).toEqual(0)
+ expect(fromInt(55 ).hashCode()).toEqual(55)
+ expect(fromInt(-12 ).hashCode()).toEqual(11)
+ expect(fromInt(10006548).hashCode()).toEqual(10006548)
+ expect(fromInt(-1098748).hashCode()).toEqual(1098747)
+
+ expect(`613354684553L` .hashCode()).toEqual(-825638905)
+ expect(`9863155567412L` .hashCode()).toEqual(1910653900)
+ expect(`3632147899696541255L`.hashCode()).toEqual(1735398658)
+ expect(`7632147899696541255L`.hashCode()).toEqual(-1689438124)
+ }
+
+ }
+
+}
+
+
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/StrangeNamedTests.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/StrangeNamedTests.scala
new file mode 100644
index 0000000..846c80b
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/StrangeNamedTests.scala
@@ -0,0 +1,28 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.jsinterop
+
+import org.scalajs.jasminetest.JasmineTest
+
+object `1_TestName` extends JasmineTest {
+ describe("a test with name 1_TestName") {
+ it("should run") {}
+ }
+}
+
+object eval extends JasmineTest {
+ describe("a test with name eval") {
+ it("should run") {}
+ }
+}
+
+object `\u1f4a7` extends JasmineTest {
+ describe("a test with name \u1f4a9") {
+ it("should run") {}
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/ThisFunctionTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/ThisFunctionTest.scala
new file mode 100644
index 0000000..4ac9058
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/ThisFunctionTest.scala
@@ -0,0 +1,70 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.jsinterop
+
+import scala.scalajs.js
+import org.scalajs.jasminetest.JasmineTest
+
+object ThisFunctionTest extends JasmineTest {
+
+ describe("scala.scalajs.js.ThisFunctionN") {
+
+ it("should provide an implicit conversion from Scala function to js.ThisFunction") {
+ val g = js.eval("""
+ var g = function(f, x) { return f.call(x, 42, x.foo); }; g;
+ """).asInstanceOf[js.Function2[js.ThisFunction2[
+ js.Dynamic, Int, String, String], js.Dynamic, String]]
+
+ val f = { (thiz: js.Dynamic, v: Int, u: String) =>
+ expect(thiz).toBeTruthy()
+ expect(thiz.foobar).toEqual("foobar")
+ u + v
+ }
+ val obj = js.Object().asInstanceOf[js.Dynamic]
+ obj.foo = "foo"
+ obj.foobar = "foobar"
+ expect(g(f, obj)).toEqual("foo42")
+ }
+
+ it("should accept a lambda where a js.ThisFunction is expected") {
+ val g = js.eval("""
+ var g = function(f, x) { return f.call(x, 42, x.foo); }; g;
+ """).asInstanceOf[js.Function2[js.ThisFunction2[
+ js.Dynamic, Int, String, String], js.Dynamic, String]]
+
+ val obj = js.Object().asInstanceOf[js.Dynamic]
+ obj.foo = "foo"
+ obj.foobar = "foobar"
+ expect(g({ (thiz: js.Dynamic, v: Int, u: String) =>
+ expect(thiz).toBeTruthy()
+ expect(thiz.foobar).toEqual("foobar")
+ u + v
+ }, obj)).toEqual("foo42")
+ }
+
+ it("should bind the first argument to this when applying js.ThisFunctionN") {
+ val g = js.eval("""
+ var g = function(x) { return this.foo + ":" + x; }; g;
+ """).asInstanceOf[js.ThisFunction1[js.Dynamic, Int, String]]
+ val obj = js.Object().asInstanceOf[js.Dynamic]
+ obj.foo = "foo"
+ expect(g(obj, 42)).toEqual("foo:42")
+ }
+
+ it("should provide an implicit conversion from js.ThisFunction to Scala function") {
+ val g = js.eval("""
+ var g = function(x) { return this.foo + ":" + x; }; g;
+ """).asInstanceOf[js.ThisFunction1[js.Dynamic, Int, String]]
+ val f: scala.Function2[js.Dynamic, Int, String] = g
+ val obj = js.Object().asInstanceOf[js.Dynamic]
+ obj.foo = "foo"
+ expect(f(obj, 42)).toEqual("foo:42")
+ }
+
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/UndefOrTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/UndefOrTest.scala
new file mode 100644
index 0000000..28eae15
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/UndefOrTest.scala
@@ -0,0 +1,191 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.jsinterop
+
+import scala.scalajs.js
+import org.scalajs.jasminetest.JasmineTest
+
+import js.annotation.JSExport
+
+object UndefOrTest extends JasmineTest {
+
+ def some[A](v: A): js.UndefOr[A] = v
+ def none[A]: js.UndefOr[A] = js.undefined
+
+ describe("scala.scalajs.js.UndefOr[A]") {
+
+ it("convert A to js.UndefOr[A]") {
+ val x: js.UndefOr[Int] = 42
+ expect(x.isEmpty).toBeFalsy
+ expect(x.isDefined).toBeTruthy
+ expect(x.nonEmpty).toBeTruthy
+ expect(x.get).toEqual(42)
+ }
+
+ it("convert undefined to js.UndefOr[A]") {
+ val x: js.UndefOr[Int] = js.undefined
+ expect(x.isEmpty).toBeTruthy
+ expect(x.isDefined).toBeFalsy
+ expect(x.nonEmpty).toBeFalsy
+ expect(() => x.get).toThrow
+ }
+
+ it("convert to js.Any when A <% js.Any") {
+ val x: js.UndefOr[Int] = 42
+ expect(x).toEqual(42)
+
+ val y: js.UndefOr[String] = js.undefined
+ expect(y).toBeUndefined
+ }
+
+ it("getOrElse") {
+ expect(some("hello").getOrElse("ko")).toEqual("hello")
+ expect(none[String].getOrElse("ok")).toEqual("ok")
+
+ var defaultComputed = false
+ expect(some("test") getOrElse {
+ defaultComputed = true
+ "ko"
+ }).toEqual("test")
+ expect(defaultComputed).toBeFalsy
+ }
+
+ it("orNull") {
+ expect(some("hello").orNull).toEqual("hello")
+ expect(none[String].orNull).toBeNull
+ }
+
+ it("map") {
+ expect(some(62).map(_ / 3)).toEqual(62 / 3)
+ expect(none[Int].map(_ / 3)).toBeUndefined
+ }
+
+ it("fold") {
+ expect(some(3).fold(10)(_ * 2)).toEqual(6)
+ expect(none[Int].fold(10)(_ * 2)).toEqual(10)
+ }
+
+ it("flatMap") {
+ def f(x: Int): js.UndefOr[Int] = if (x > 0) x+3 else js.undefined
+ expect(some(6).flatMap(f)).toEqual(9)
+ expect(some(-6).flatMap(f)).toBeUndefined
+ expect(none[Int].flatMap(f)).toBeUndefined
+ }
+
+ it("flatten") {
+ expect(some(some(7)).flatten.isDefined).toBeTruthy
+ expect(some(some(7)).flatten.get).toEqual(7)
+ expect(some(none[Int]).flatten.isDefined).toBeFalsy
+ expect(none[js.UndefOr[Int]].flatten.isDefined).toBeFalsy
+ }
+
+ it("filter") {
+ expect(some(7).filter(_ > 0).isDefined).toBeTruthy
+ expect(some(7).filter(_ > 0).get).toEqual(7)
+ expect(some(7).filter(_ < 0).isDefined).toBeFalsy
+ expect(none[Int].filter(_ < 0).isDefined).toBeFalsy
+ }
+
+ it("filterNot") {
+ expect(some(7).filterNot(_ < 0).isDefined).toBeTruthy
+ expect(some(7).filterNot(_ < 0).get).toEqual(7)
+ expect(some(7).filterNot(_ > 0).isDefined).toBeFalsy
+ expect(none[Int].filterNot(_ > 0).isDefined).toBeFalsy
+ }
+
+ it("exists") {
+ expect(some(7).exists(_ > 0)).toBeTruthy
+ expect(some(7).exists(_ < 0)).toBeFalsy
+ expect(none[Int].exists(_ > 0)).toBeFalsy
+ }
+
+ it("forall") {
+ expect(some(7).forall(_ > 0)).toBeTruthy
+ expect(some(7).forall(_ < 0)).toBeFalsy
+ expect(none[Int].forall(_ > 0)).toBeTruthy
+ }
+
+ it("foreach") {
+ var witness1 = 3
+ some(42).foreach(witness1 = _)
+ expect(witness1).toEqual(42)
+
+ var witness2 = 3
+ none[Int].foreach(witness2 = _)
+ expect(witness2).toEqual(3)
+ }
+
+ it("collect") {
+ expect(some("hello") collect {
+ case "hello" => "ok"
+ }).toEqual("ok")
+ expect(some("hello") collect {
+ case "notthis" => "ko"
+ }).toBeUndefined
+ expect(none[String] collect {
+ case "hello" => "ko"
+ }).toBeUndefined
+ }
+
+ it("collect should call guard at most once") {
+ var witness = 0
+ def guard(x: String) = {
+ witness += 1
+ true
+ }
+ expect(some("hello") collect {
+ case x @ "hello" if guard(x) => "ok"
+ }).toEqual("ok")
+ expect(witness).toEqual(1)
+ }
+
+ it("orElse") {
+ expect(some(true) orElse some(false)).toBeTruthy
+ expect(some("ok") orElse none).toEqual("ok")
+ expect(none orElse some("yes")).toEqual("yes")
+ expect(none orElse none).toBeUndefined
+ }
+
+ it("toList") {
+ import scala.scalajs.js.JSConverters._
+
+ expect(some("hello").toList.toJSArray).toEqual(js.Array("hello"))
+ expect(none[String].toList.toJSArray).toEqual(js.Array())
+ }
+
+ it("toLeft and toRight") {
+ expect(some("left").toLeft("right").isInstanceOf[Left[_, _]]).toBeTruthy
+ expect(none[String].toLeft("right").isInstanceOf[Right[_, _]]).toBeTruthy
+ expect(some("right").toRight("left").isInstanceOf[Right[_, _]]).toBeTruthy
+ expect(none[String].toRight("left").isInstanceOf[Left[_, _]]).toBeTruthy
+ }
+
+ it("toOption") {
+ expect(some("foo").toOption == Some("foo")).toBeTruthy
+ expect(none.toOption == None).toBeTruthy
+ }
+
+ }
+
+ describe("scala.scalajs.js.JSConverters.JSRichOption") {
+
+ import js.JSConverters._
+
+ it("should provide orUndefined") {
+ expect(Some("asdf").orUndefined).toEqual("asdf")
+ expect((None: Option[String]).orUndefined).toBeUndefined
+
+ // This doesn't work on 2.10, since it doesn't infer
+ // Nothing <:< js.Any to implicitly convert UndefOr[Nothing] to
+ // js.Any
+ // expect(None.orUndefined).toBeUndefined
+ }
+
+ }
+
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/library/ArrayOpsTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/library/ArrayOpsTest.scala
new file mode 100644
index 0000000..a1957a5
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/library/ArrayOpsTest.scala
@@ -0,0 +1,117 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.library
+
+import scala.scalajs.js
+import org.scalajs.jasminetest.JasmineTest
+
+import scala.reflect.ClassTag
+
+import js.JSConverters._
+
+object ArrayOpsTest extends JasmineTest {
+
+ describe("scala.scalajs.js.ArrayOps") {
+
+ // Methods we actually implement
+
+ it("should implement apply") {
+ val array = js.Array(3,4,5,6,3,4)
+ val ops: js.ArrayOps[Int] = array
+
+ expect(ops(0)).toEqual(3)
+ expect(ops(3)).toEqual(6)
+
+ array(0) = 4
+ expect(ops(0)).toEqual(4)
+ }
+
+ it("should implement update") {
+ val array = js.Array(3,4,5,6,3,4)
+ val ops: js.ArrayOps[Int] = array
+
+ expect(array(1)).toEqual(4)
+ ops(1) = 5
+ expect(array(1)).toEqual(5)
+
+ ops(5) = 10
+ expect(array(5)).toEqual(10)
+ }
+
+ it("should implement length") {
+ val array = js.Array(3,4,5,6,3,4)
+ val ops: js.ArrayOps[Int] = array
+
+ expect(ops.length).toEqual(6)
+ array.push(1)
+ expect(ops.length).toEqual(7)
+ }
+
+ it("should implement seq") {
+ val array = js.Array(3,4,5,6,3,4)
+ val ops: js.ArrayOps[Int] = array
+ val seq = ops.seq
+
+ expect(seq.toList == List(3,4,5,6,3,4)).toBeTruthy
+ }
+
+ it("should implement reduceLeft") {
+ val array = js.Array(100, 6, 2, 56, -1)
+ expect(array.reduceLeft(_ - _)).toEqual(37)
+ expect(() => js.Array[Int]().reduceLeft(_ + _)).toThrow
+ }
+
+ it("should implement reduceRight") {
+ val array = js.Array("hello", "world")
+ expect(array.reduceRight(_ + ", " + _)).toEqual("hello, world")
+ expect(() => js.Array[Int]().reduceRight(_ + _)).toThrow
+ }
+
+ it("should implement ++") {
+ val left = js.Array("hello", "world")
+ val right = js.Array("and", "everyone", "else")
+ expect(left ++ right).toEqual(
+ js.Array("hello", "world", "and", "everyone", "else"))
+
+ val ints = js.Array(4, 3)
+ expect(left ++ ints).toEqual(js.Array("hello", "world", 4, 3))
+ }
+
+ // Some arbitrary methods to test the builders
+
+ it("should implement collect") {
+ def ct[A : ClassTag](x: A) = implicitly[ClassTag[A]]
+ val array = js.Array(3,4,5,6,3,4)
+ val res = array.collect {
+ case x if x > 4 => 2*x
+ }
+
+ expect(ct(res).runtimeClass == classOf[js.Array[Int]]).toBeTruthy
+ expect(res).toEqual(js.Array(10, 12))
+ }
+
+ it("should implement diff") {
+ val array = js.Array(1,2,1,3,1,10,9)
+ val diff = array.diff(Seq(1,3,9))
+ expect(diff).toEqual(js.Array(2,1,1,10))
+ }
+
+ it("should implement toList - #843") {
+ val array = js.Array(1,2,1,3,1,10,9)
+ val list = array.toList
+ expect(list.toJSArray).toEqual(array)
+ }
+
+ it("should implement to[T] - #843") {
+ val array = js.Array(1,2,1,3,1,10,9)
+ val list = array.to[List]
+ expect(list.toJSArray).toEqual(array)
+ }
+
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/library/WrappedArrayTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/library/WrappedArrayTest.scala
new file mode 100644
index 0000000..e4fed0a
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/library/WrappedArrayTest.scala
@@ -0,0 +1,118 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.library
+
+import scala.scalajs.js
+import org.scalajs.jasminetest.JasmineTest
+
+import scala.collection.mutable
+
+object WrappedArrayTest extends JasmineTest {
+
+ describe("scala.scalajs.js.WrappedArray") {
+
+ // Methods we actually implement
+
+ it("should implement apply") {
+ val array = js.Array(3,4,5,6,3,4)
+ val seq: Seq[Int] = array
+
+ expect(seq(0)).toEqual(3)
+ expect(seq(3)).toEqual(6)
+
+ array(0) = 4
+ expect(seq(0)).toEqual(4)
+ }
+
+ it("should implement update") {
+ val array = js.Array(3,4,5,6,3,4)
+ val seq: mutable.Seq[Int] = array
+
+ expect(array(1)).toEqual(4)
+ seq(1) = 5
+ expect(array(1)).toEqual(5)
+
+ seq(5) = 10
+ expect(array(5)).toEqual(10)
+ }
+
+ it("should implement length") {
+ val array = js.Array(3,4,5,6,3,4)
+ val seq: Seq[Int] = array
+
+ expect(seq.length).toEqual(6)
+ array.push(1)
+ expect(seq.length).toEqual(7)
+ }
+
+ it("should implement +=:") {
+ val array = js.Array(5, 8, 9)
+ 3 +=: array
+ expect(array).toEqual(js.Array(3, 5, 8, 9))
+ }
+
+ it("should implement ++=:") {
+ val array = js.Array(5, 8, 9)
+ js.Array(2, 0) ++=: array
+ expect(array).toEqual(js.Array(2, 0, 5, 8, 9))
+ Seq(-3, -45, 1) ++=: array
+ expect(array).toEqual(js.Array(-3, -45, 1, 2, 0, 5, 8, 9))
+ }
+
+ it("should implement insertAll") {
+ val array = js.Array(5, 8, 9)
+ array.insertAll(2, js.Array(2, 0))
+ expect(array).toEqual(js.Array(5, 8, 2, 0, 9))
+ array.insertAll(1, Seq(-3, -45, 1))
+ expect(array).toEqual(js.Array(5, -3, -45, 1, 8, 2, 0, 9))
+ }
+
+ it("should implement remove") {
+ val array = js.Array(5, 8, 2, 0, 9)
+ expect(array.remove(1)).toEqual(8)
+ expect(array).toEqual(js.Array(5, 2, 0, 9))
+
+ array.remove(0, 3)
+ expect(array).toEqual(js.Array(9))
+ }
+
+ // Some arbitrary methods to test the builders
+
+ it("should implement collect") {
+ // Ascribe to right type here, so we'll actually produce a WrappedArray
+ val seq: js.WrappedArray[Int] = js.Array(3,4,5,6,3,4)
+ val res = seq.collect {
+ case x if x > 4 => 2*x
+ }
+
+ expect(res.getClass == classOf[js.WrappedArray[Int]]).toBeTruthy
+ expect(res.toList == List(10,12)).toBeTruthy
+ expect(res.array).toEqual(js.Array(10,12))
+ }
+
+ it("should implement diff") {
+ val seq: Seq[Int] = js.Array(1,2,1,3,1,10,9)
+ val diff = seq.diff(Seq(1,3,9))
+ expect(diff.toList == List(2,1,1,10)).toBeTruthy
+ }
+
+ it("should implement toList") {
+ val seq: Seq[Int] = js.Array(1,2,1,3,1,10,9)
+ val list = seq.toList
+ expect(list == List(1,2,1,3,1,10,9)).toBeTruthy
+ }
+
+ it("should implement to[T]") {
+ val seq: Seq[Int] = js.Array(1,2,1,3,1,10,9)
+ val list = seq.to[List]
+ expect(list == List(1,2,1,3,1,10,9)).toBeTruthy
+ }
+
+ }
+
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/library/WrappedDictionaryTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/library/WrappedDictionaryTest.scala
new file mode 100644
index 0000000..3b95f55
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/library/WrappedDictionaryTest.scala
@@ -0,0 +1,106 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.library
+
+import scala.scalajs.js
+import org.scalajs.jasminetest.JasmineTest
+
+import scala.collection.mutable
+
+import scala.reflect.ClassTag
+
+object WrappedDictionaryTest extends JasmineTest {
+
+ describe("scala.scalajs.js.WrappedDictionary") {
+
+ // Methods we actually implement
+
+ it("should implement get") {
+ val map: mutable.Map[String, Any] =
+ js.Dictionary("a" -> "a", "b" -> 6, "e" -> js.undefined)
+ expect(map.get("a") == Some("a")).toBeTruthy
+ expect(map.get("b") == Some(6)).toBeTruthy
+ expect(map.get("e") == Some(())).toBeTruthy
+ expect(map.get("f") == None).toBeTruthy
+ }
+
+ it("should implement += and -=") {
+ val dict = js.Dictionary[String]()
+ val map: mutable.Map[String, String] = dict
+
+ expect(js.Object.properties(dict)).toEqual(js.Array())
+
+ map += "hello" -> "world"
+ expect(dict("hello")).toEqual("world")
+ map += "foo" -> "bar"
+ expect(dict("foo")).toEqual("bar")
+ map -= "hello"
+ expect(dict.get("hello").isDefined).toBeFalsy
+ expect(js.Object.properties(dict)).toEqual(js.Array("foo"))
+ }
+
+ it("should implement iterator") {
+ val elems = ('a' to 'e').map(_.toString).zip(1 to 5)
+ val dict = js.Dictionary[Int]()
+ val map: mutable.Map[String, Int] = dict
+
+ dict ++= elems
+
+ expect(map.iterator.toList.sorted.sameElements(elems)).toBeTruthy
+ }
+
+ // Some arbitrary methods to test the builders
+
+ it("should implement map") {
+ def ct[A : ClassTag](x: A) = implicitly[ClassTag[A]]
+ val dict = js.Dictionary[Int]()
+ dict ++= Seq("one" -> 1, "two" -> 2, "three" -> 3)
+
+ val mapChr = dict.map { case (k,v) => k(0) -> v * 2 }
+ val mapStr = dict.map { case (k,v) => k(0).toString -> v * 2 }
+
+ expect(ct(mapChr).runtimeClass == classOf[js.WrappedDictionary[_]]).toBeFalsy
+ expect(ct(mapStr).runtimeClass == classOf[js.WrappedDictionary[_]]).toBeTruthy
+
+ expect(mapChr.size).toBe(2)
+ expect(mapStr.size).toBe(2)
+ }
+
+ it("should implement withFilter") {
+ val dict = js.Dictionary[Int]()
+ val flt = dict.withFilter { case (k,v) => v > 5 || k == "a" }
+ def size = flt.map(x => x).size
+
+ expect(size).toBe(0)
+ dict += "a" -> 1
+ expect(size).toBe(1)
+ dict += "b" -> 2
+ expect(size).toBe(1)
+ dict += "c" -> 6
+ expect(size).toBe(2)
+ dict += "b" -> 7
+ expect(size).toBe(3)
+ dict -= "a"
+ expect(size).toBe(2)
+ }
+
+ it("should implement toList") {
+ val dict = js.Dictionary("a" -> "a", "b" -> 6, "e" -> js.undefined)
+ val list = dict.toList
+ expect(list.size).toBe(3)
+ }
+
+ it("should implement to[T]") {
+ val dict = js.Dictionary("a" -> "a", "b" -> 6, "e" -> js.undefined)
+ val list = dict.to[List]
+ expect(list.size).toBe(3)
+ }
+
+ }
+
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niobuffer/BaseBufferTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niobuffer/BaseBufferTest.scala
new file mode 100644
index 0000000..a1f1b71
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niobuffer/BaseBufferTest.scala
@@ -0,0 +1,178 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.niobuffer
+
+import org.scalajs.jasminetest.JasmineTest
+
+import java.nio._
+
+abstract class BaseBufferTest extends JasmineTest {
+ trait BaseFactory {
+ type BufferType <: Buffer
+
+ val createsReadOnly: Boolean = false
+
+ def allocBuffer(capacity: Int): BufferType
+
+ def allocBuffer(pos: Int, limit: Int, capacity: Int): BufferType = {
+ val buf = allocBuffer(capacity)
+ buf.limit(limit).position(pos)
+ buf
+ }
+ }
+
+ type Factory <: BaseFactory
+
+ def commonTests(factory: Factory): Unit = {
+ import factory._
+
+ it("allocate") {
+ val buf = allocBuffer(10)
+ expect(buf.position).toEqual(0)
+ expect(buf.limit).toEqual(10)
+ expect(buf.capacity).toEqual(10)
+
+ expect(allocBuffer(0).capacity).toEqual(0)
+
+ expect(() => allocBuffer(-1)).toThrow
+
+ val buf2 = allocBuffer(1, 5, 9)
+ expect(buf2.position()).toEqual(1)
+ expect(buf2.limit()).toEqual(5)
+ expect(buf2.capacity()).toEqual(9)
+ }
+
+ it("isReadOnly()") {
+ val buf = allocBuffer(10)
+ if (createsReadOnly)
+ expect(buf.isReadOnly()).toBeTruthy
+ else
+ expect(buf.isReadOnly()).toBeFalsy
+ }
+
+ it("position") {
+ val buf = allocBuffer(10)
+ buf.position(3)
+ expect(buf.position()).toEqual(3)
+ buf.position(10)
+ expect(buf.position()).toEqual(10)
+ buf.position(0)
+ expect(buf.position()).toEqual(0)
+
+ expect(() => buf.position(-1)).toThrow
+ expect(() => buf.position(11)).toThrow
+ expect(buf.position()).toEqual(0)
+
+ val buf2 = allocBuffer(1, 5, 9)
+ expect(buf2.position()).toEqual(1)
+ buf2.position(5)
+ expect(buf2.position()).toEqual(5)
+ expect(() => buf2.position(6)).toThrow
+ expect(buf2.position()).toEqual(5)
+ }
+
+ it("limit") {
+ val buf = allocBuffer(10)
+ buf.position(3)
+ buf.limit(7)
+ expect(buf.limit()).toEqual(7)
+ expect(buf.position()).toEqual(3)
+ expect(() => buf.limit(11)).toThrow
+ expect(buf.limit()).toEqual(7)
+ expect(() => buf.limit(-1)).toThrow
+ expect(buf.limit()).toEqual(7)
+ expect(buf.position()).toEqual(3)
+
+ buf.position(5)
+ buf.limit(4)
+ expect(buf.limit()).toEqual(4)
+ expect(buf.position()).toEqual(4)
+ }
+
+ it("mark() and reset()") {
+ val buf = allocBuffer(10)
+
+ // Initially, the mark should not be set
+ expect(() => buf.reset()).toThrow
+
+ // Simple test
+ buf.position(3)
+ buf.mark()
+ buf.position(8)
+ buf.reset()
+ expect(buf.position()).toEqual(3)
+
+ // reset() should not have cleared the mark
+ buf.position(5)
+ buf.reset()
+ expect(buf.position()).toEqual(3)
+
+ // setting position() below the mark should clear the mark
+ buf.position(2)
+ expect(() => buf.reset()).toThrow
+ }
+
+ it("clear()") {
+ val buf = allocBuffer(3, 6, 10)
+ buf.mark()
+ buf.position(4)
+
+ buf.clear()
+ expect(buf.position()).toEqual(0)
+ expect(buf.limit()).toEqual(10) // the capacity
+ expect(buf.capacity()).toEqual(10)
+ expect(() => buf.reset()).toThrow
+ }
+
+ it("flip()") {
+ val buf = allocBuffer(3, 6, 10)
+ buf.mark()
+ buf.position(4)
+
+ buf.flip()
+ expect(buf.position()).toEqual(0)
+ expect(buf.limit()).toEqual(4) // old position
+ expect(buf.capacity()).toEqual(10)
+ expect(() => buf.reset()).toThrow
+ }
+
+ it("rewind()") {
+ val buf = allocBuffer(3, 6, 10)
+ buf.mark()
+ buf.position(4)
+
+ buf.rewind()
+ expect(buf.position()).toEqual(0)
+ expect(buf.limit()).toEqual(6) // unchanged
+ expect(buf.capacity()).toEqual(10)
+ expect(() => buf.reset()).toThrow
+ }
+
+ it("remaining() and hasRemaining()") {
+ val buf = allocBuffer(3, 7, 10)
+ expect(buf.remaining()).toEqual(7-3)
+ expect(buf.hasRemaining()).toBeTruthy
+
+ buf.position(6)
+ expect(buf.remaining()).toEqual(7-6)
+ expect(buf.hasRemaining()).toBeTruthy
+
+ buf.limit(9)
+ expect(buf.remaining()).toEqual(9-6)
+ expect(buf.hasRemaining()).toBeTruthy
+
+ buf.limit(2)
+ expect(buf.remaining()).toEqual(0)
+ expect(buf.hasRemaining()).toBeFalsy
+
+ buf.position(0)
+ expect(buf.remaining()).toEqual(2)
+ expect(buf.hasRemaining()).toBeTruthy
+ }
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niobuffer/ByteBufferTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niobuffer/ByteBufferTest.scala
new file mode 100644
index 0000000..9ec831f
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niobuffer/ByteBufferTest.scala
@@ -0,0 +1,330 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.niobuffer
+
+import java.nio._
+
+import scala.scalajs.js
+import js.JSConverters._
+
+object ByteBufferTest extends BaseBufferTest {
+ trait Factory extends BaseFactory {
+ type BufferType = ByteBuffer
+
+ def withContent(capacity: Int, content: Byte*): ByteBuffer =
+ withContent(0, capacity, capacity, content: _*)
+
+ def withContent(pos: Int, limit: Int, capacity: Int,
+ content: Byte*): ByteBuffer = {
+ val buf = allocBuffer(pos, limit, capacity)
+ buf.put(content.toArray)
+ buf.position(pos)
+ buf
+ }
+ }
+
+ def byteRange(start: Int, end: Int): Array[Byte] =
+ (start until end).map(_.toByte).toArray
+
+ def defineTests(factory: Factory): Unit = {
+ import factory._
+
+ commonTests(factory)
+
+ it("absolute get()") {
+ val buf = withContent(10, byteRange(0, 10): _*)
+ expect(buf.get(0)).toEqual(0)
+ expect(buf.position()).toEqual(0)
+ expect(buf.get(3)).toEqual(3)
+ expect(buf.position()).toEqual(0)
+
+ expect(() => buf.get(-1)).toThrow
+ expect(() => buf.get(15)).toThrow
+
+ buf.limit(4)
+ expect(() => buf.get(5)).toThrow
+ }
+
+
+ if (!createsReadOnly) {
+ it("absolute put()") {
+ val buf = allocBuffer(10)
+ buf.put(5, 42)
+ expect(buf.position()).toEqual(0)
+ buf.put(3, 2)
+ expect(buf.get(3)).toEqual(2)
+ expect(buf.get(5)).toEqual(42)
+ expect(buf.get(7)).toEqual(0)
+
+ expect(() => buf.put(-1, 2)).toThrow
+ expect(() => buf.put(14, 9)).toThrow
+
+ buf.limit(4)
+ expect(() => buf.put(4, 1)).toThrow
+ }
+ } else {
+ it("absolute put() - read-only") {
+ val buf = allocBuffer(10)
+ expect(() => buf.put(2, 1)).toThrow
+ expect(buf.get(2)).toEqual(0)
+ expect(buf.position()).toEqual(0)
+
+ expect(() => buf.put(-2, 1)).toThrow
+ expect(() => buf.put(12, 1)).toThrow
+ }
+ }
+
+ it("relative get()") {
+ val buf = withContent(10, byteRange(0, 10): _*)
+ expect(buf.get()).toEqual(0)
+ expect(buf.position()).toEqual(1)
+ buf.position(3)
+ expect(buf.get()).toEqual(3)
+ expect(buf.position()).toEqual(4)
+
+ buf.limit(4)
+ expect(() => buf.get()).toThrow
+ }
+
+ if (!createsReadOnly) {
+ it("relative put()") {
+ val buf = allocBuffer(10)
+ buf.put(5.toByte)
+ expect(buf.position()).toEqual(1)
+ expect(buf.get(0)).toEqual(5)
+
+ buf.position(3)
+ buf.put(36.toByte)
+ expect(buf.position()).toEqual(4)
+ expect(buf.get(3)).toEqual(36)
+
+ buf.position(10)
+ expect(() => buf.put(3.toByte)).toThrow
+ }
+ } else {
+ it("relative put() - read-only") {
+ val buf = allocBuffer(10)
+ expect(() => buf.put(5.toByte)).toThrow
+ expect(buf.position()).toEqual(0)
+ expect(buf.get(0)).toEqual(0)
+
+ buf.position(10)
+ expect(() => buf.put(3.toByte)).toThrow
+ }
+ }
+
+ it("relative bulk get()") {
+ val buf = withContent(10, byteRange(0, 10): _*)
+ val a = new Array[Byte](4)
+ buf.get(a)
+ expect(a.toJSArray).toEqual(js.Array(0, 1, 2, 3))
+ expect(buf.position()).toEqual(4)
+
+ buf.position(6)
+ buf.get(a, 1, 2)
+ expect(a.toJSArray).toEqual(js.Array(0, 6, 7, 3))
+ expect(buf.position()).toEqual(8)
+
+ expect(() => buf.get(a)).toThrow
+ expect(buf.position()).toEqual(8)
+ expect(a.toJSArray).toEqual(js.Array(0, 6, 7, 3))
+ }
+
+ if (!createsReadOnly) {
+ it("relative bulk put()") {
+ val buf = allocBuffer(10)
+ buf.put(Array[Byte](6, 7, 12))
+ expect((0 to 3).map(buf.get(_)).toJSArray).toEqual(js.Array(6, 7, 12, 0))
+ expect(buf.position()).toEqual(3)
+
+ buf.position(2)
+ buf.put(Array[Byte](44, 55, 66, 77, 88), 2, 2)
+ expect((0 to 4).map(buf.get(_)).toJSArray).toEqual(js.Array(6, 7, 66, 77, 0))
+ expect(buf.position()).toEqual(4)
+
+ expect(() => buf.put(Array.fill[Byte](10)(0))).toThrow
+ expect(buf.position()).toEqual(4)
+ expect((0 to 4).map(buf.get(_)).toJSArray).toEqual(js.Array(6, 7, 66, 77, 0))
+ }
+ } else {
+ it("relative bulk put() - read-only") {
+ val buf = allocBuffer(10)
+ expect(() => buf.put(Array[Byte](6, 7, 12))).toThrow
+ expect(buf.position()).toEqual(0)
+ expect(buf.get(0)).toEqual(0)
+
+ buf.position(8)
+ expect(() => buf.put(Array[Byte](6, 7, 12))).toThrow
+ expect(buf.position()).toEqual(8)
+ expect(buf.get(8)).toEqual(0)
+ }
+ }
+
+ if (!createsReadOnly) {
+ it("compact()") {
+ val buf = withContent(10, byteRange(0, 10): _*)
+ buf.position(6)
+ buf.mark()
+
+ buf.compact()
+ expect(buf.position()).toEqual(4)
+ expect(buf.limit()).toEqual(10)
+ expect(() => buf.reset()).toThrow
+
+ for (i <- 0 until 4)
+ expect(buf.get(i)).toEqual(i + 6)
+ }
+ } else {
+ it("compact() - read-only") {
+ val buf = allocBuffer(10)
+ expect(() => buf.compact()).toThrow
+ }
+ }
+
+ it("slice()" + (if (createsReadOnly) " - read-only" else "")) {
+ val buf1 = withContent(10, byteRange(0, 10): _*)
+ buf1.position(3)
+ buf1.limit(7)
+ buf1.mark()
+ val buf2 = buf1.slice()
+ expect(buf2.position()).toEqual(0)
+ expect(buf2.limit()).toEqual(4)
+ expect(buf2.capacity()).toEqual(4)
+ expect(() => buf2.reset()).toThrow
+ expect(buf2.get(1)).toEqual(4)
+
+ buf2.position(2)
+ expect(buf1.position()).toEqual(3)
+
+ if (!createsReadOnly) {
+ buf2.put(89.toByte)
+ expect(buf1.get(5)).toEqual(89)
+ expect(buf2.position()).toEqual(3)
+ expect(buf1.position()).toEqual(3)
+ }
+
+ expect(() => buf2.limit(5)).toThrow
+ expect(buf2.limit()).toEqual(4)
+
+ buf2.limit(3)
+ expect(buf1.limit()).toEqual(7)
+
+ if (!createsReadOnly) {
+ buf1.put(3, 23)
+ expect(buf2.get(0)).toEqual(23)
+ }
+ }
+
+ it("duplicate()" + (if (createsReadOnly) " - read-only" else "")) {
+ val buf1 = withContent(10, byteRange(0, 10): _*)
+ buf1.position(3)
+ buf1.limit(7)
+ buf1.mark()
+ val buf2 = buf1.duplicate()
+ expect(buf2.position()).toEqual(3)
+ expect(buf2.limit()).toEqual(7)
+ expect(buf2.capacity()).toEqual(10)
+ expect(buf2.get(4)).toEqual(4)
+
+ buf2.position(4)
+ expect(buf1.position()).toEqual(3)
+ expect(buf2.position()).toEqual(4)
+
+ buf2.reset()
+ expect(buf2.position()).toEqual(3)
+ buf2.position(4)
+
+ if (!createsReadOnly) {
+ buf2.put(89.toByte)
+ expect(buf1.get(4)).toEqual(89)
+ expect(buf2.position()).toEqual(5)
+ expect(buf1.position()).toEqual(3)
+ }
+
+ buf2.limit(5)
+ expect(buf1.limit()).toEqual(7)
+
+ if (!createsReadOnly) {
+ buf1.put(6, 23)
+ buf2.limit(10)
+ expect(buf2.get(6)).toEqual(23)
+ }
+ }
+ }
+
+ describe("Allocated ByteBuffer") {
+ defineTests(new Factory {
+ def allocBuffer(capacity: Int): ByteBuffer =
+ ByteBuffer.allocate(capacity)
+ })
+ }
+
+ class WrappedByteBufferFactory extends Factory {
+ def allocBuffer(capacity: Int): ByteBuffer =
+ ByteBuffer.wrap(new Array[Byte](capacity))
+
+ override def allocBuffer(pos: Int, limit: Int, capacity: Int): ByteBuffer =
+ ByteBuffer.wrap(new Array[Byte](capacity), pos, limit-pos)
+
+ override def withContent(pos: Int, limit: Int, capacity: Int,
+ content: Byte*): ByteBuffer = {
+ val after = capacity - (pos + content.size)
+ ByteBuffer.wrap(
+ (Seq.fill(pos)(0.toByte) ++ content ++ Seq.fill(after)(0.toByte)).toArray,
+ pos, limit-pos)
+ }
+ }
+
+ describe("Wrapped ByteBuffer") {
+ defineTests(new WrappedByteBufferFactory)
+ }
+
+ describe("Read-only wrapped ByteBuffer") {
+ defineTests(new WrappedByteBufferFactory {
+ override val createsReadOnly = true
+
+ override def allocBuffer(capacity: Int): ByteBuffer =
+ super.allocBuffer(capacity).asReadOnlyBuffer()
+
+ override def allocBuffer(pos: Int, limit: Int, capacity: Int): ByteBuffer =
+ super.allocBuffer(pos, limit, capacity).asReadOnlyBuffer()
+
+ override def withContent(pos: Int, limit: Int, capacity: Int,
+ content: Byte*): ByteBuffer =
+ super.withContent(pos, limit, capacity, content: _*).asReadOnlyBuffer()
+ })
+ }
+
+ describe("Sliced ByteBuffer") {
+ defineTests(new Factory {
+ def allocBuffer(capacity: Int): ByteBuffer = {
+ if (capacity < 0)
+ throw new IllegalArgumentException
+ val buf = ByteBuffer.allocate(capacity+25)
+ buf.position(17)
+ buf.limit(17+capacity)
+ buf.slice()
+ }
+
+ override def withContent(pos: Int, limit: Int, capacity: Int,
+ content: Byte*): ByteBuffer = {
+ if (!(0 <= pos && pos <= limit && limit <= capacity))
+ throw new IllegalArgumentException
+ val buf = ByteBuffer.allocate(capacity+25)
+ buf.position(9+pos)
+ buf.put(content.toArray)
+ buf.position(9)
+ buf.limit(9+capacity)
+ val buf2 = buf.slice()
+ buf2.position(pos)
+ buf2.limit(limit)
+ buf2
+ }
+ })
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niobuffer/CharBufferTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niobuffer/CharBufferTest.scala
new file mode 100644
index 0000000..b13c478
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niobuffer/CharBufferTest.scala
@@ -0,0 +1,388 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.niobuffer
+
+import java.nio._
+
+import scala.scalajs.js
+import js.JSConverters._
+
+object CharBufferTest extends BaseBufferTest {
+ trait Factory extends BaseFactory {
+ type BufferType = CharBuffer
+
+ def withContent(capacity: Int, content: Char*): CharBuffer =
+ withContent(0, capacity, capacity, content: _*)
+
+ def withContent(pos: Int, limit: Int, capacity: Int,
+ content: Char*): CharBuffer = {
+ val buf = allocBuffer(pos, limit, capacity)
+ buf.put(content.toArray)
+ buf.position(pos)
+ buf
+ }
+ }
+
+ def zeros(n: Int): String =
+ "\u0000"*n
+
+ def charRange(start: Int, end: Int): Array[Char] =
+ (start until end).map(_.toChar).toArray
+
+ def defineTests(factory: Factory): Unit = {
+ import factory._
+
+ commonTests(factory)
+
+ it("absolute get()") {
+ val buf = withContent(10, charRange(0, 10): _*)
+ expect(buf.get(0)).toEqual(0)
+ expect(buf.position()).toEqual(0)
+ expect(buf.get(3)).toEqual(3)
+ expect(buf.position()).toEqual(0)
+
+ expect(() => buf.get(-1)).toThrow
+ expect(() => buf.get(15)).toThrow
+
+ buf.limit(4)
+ expect(() => buf.get(5)).toThrow
+ }
+
+
+ if (!createsReadOnly) {
+ it("absolute put()") {
+ val buf = allocBuffer(10)
+ buf.put(5, 42)
+ expect(buf.position()).toEqual(0)
+ buf.put(3, 2)
+ expect(buf.get(3)).toEqual(2)
+ expect(buf.get(5)).toEqual(42)
+ expect(buf.get(7)).toEqual(0)
+
+ expect(() => buf.put(-1, 2)).toThrow
+ expect(() => buf.put(14, 9)).toThrow
+
+ buf.limit(4)
+ expect(() => buf.put(4, 1)).toThrow
+ }
+ } else {
+ it("absolute put() - read-only") {
+ val buf = allocBuffer(10)
+ expect(() => buf.put(2, 1)).toThrow
+ expect(buf.get(2)).toEqual(0)
+ expect(buf.position()).toEqual(0)
+
+ expect(() => buf.put(-2, 1)).toThrow
+ expect(() => buf.put(12, 1)).toThrow
+ }
+ }
+
+ it("relative get()") {
+ val buf = withContent(10, charRange(0, 10): _*)
+ expect(buf.get()).toEqual(0)
+ expect(buf.position()).toEqual(1)
+ buf.position(3)
+ expect(buf.get()).toEqual(3)
+ expect(buf.position()).toEqual(4)
+
+ buf.limit(4)
+ expect(() => buf.get()).toThrow
+ }
+
+ if (!createsReadOnly) {
+ it("relative put()") {
+ val buf = allocBuffer(10)
+ buf.put(5.toChar)
+ expect(buf.position()).toEqual(1)
+ expect(buf.get(0)).toEqual(5)
+
+ buf.position(3)
+ buf.put(36.toChar)
+ expect(buf.position()).toEqual(4)
+ expect(buf.get(3)).toEqual(36)
+
+ buf.position(10)
+ expect(() => buf.put(3.toChar)).toThrow
+ }
+ } else {
+ it("relative put() - read-only") {
+ val buf = allocBuffer(10)
+ expect(() => buf.put(5.toChar)).toThrow
+ expect(buf.position()).toEqual(0)
+ expect(buf.get(0)).toEqual(0)
+
+ buf.position(10)
+ expect(() => buf.put(3.toChar)).toThrow
+ }
+ }
+
+ it("relative bulk get()") {
+ val buf = withContent(10, charRange(0, 10): _*)
+ val a = new Array[Char](4)
+ buf.get(a)
+ expect(a.map(_.toInt).toJSArray).toEqual(js.Array(0, 1, 2, 3))
+ expect(buf.position()).toEqual(4)
+
+ buf.position(6)
+ buf.get(a, 1, 2)
+ expect(a.map(_.toInt).toJSArray).toEqual(js.Array(0, 6, 7, 3))
+ expect(buf.position()).toEqual(8)
+
+ expect(() => buf.get(a)).toThrow
+ expect(buf.position()).toEqual(8)
+ expect(a.map(_.toInt).toJSArray).toEqual(js.Array(0, 6, 7, 3))
+ }
+
+ if (!createsReadOnly) {
+ it("relative bulk put()") {
+ val buf = allocBuffer(10)
+ buf.put(Array[Char](6, 7, 12))
+ expect((0 to 3).map(buf.get(_).toInt).toJSArray).toEqual(js.Array(6, 7, 12, 0))
+ expect(buf.position()).toEqual(3)
+
+ buf.position(2)
+ buf.put(Array[Char](44, 55, 66, 77, 88), 2, 2)
+ expect((0 to 4).map(buf.get(_).toInt).toJSArray).toEqual(js.Array(6, 7, 66, 77, 0))
+ expect(buf.position()).toEqual(4)
+
+ expect(() => buf.put(Array.fill[Char](10)(0))).toThrow
+ expect(buf.position()).toEqual(4)
+ expect((0 to 4).map(buf.get(_).toInt).toJSArray).toEqual(js.Array(6, 7, 66, 77, 0))
+ }
+ } else {
+ it("relative bulk put() - read-only") {
+ val buf = allocBuffer(10)
+ expect(() => buf.put(Array[Char](6, 7, 12))).toThrow
+ expect(buf.position()).toEqual(0)
+ expect(buf.get(0)).toEqual(0)
+
+ buf.position(8)
+ expect(() => buf.put(Array[Char](6, 7, 12))).toThrow
+ expect(buf.position()).toEqual(8)
+ expect(buf.get(8)).toEqual(0)
+ }
+ }
+
+ if (!createsReadOnly) {
+ it("compact()") {
+ val buf = withContent(10, charRange(0, 10): _*)
+ buf.position(6)
+ buf.mark()
+
+ buf.compact()
+ expect(buf.position()).toEqual(4)
+ expect(buf.limit()).toEqual(10)
+ expect(() => buf.reset()).toThrow
+
+ for (i <- 0 until 4)
+ expect(buf.get(i).toInt).toEqual(i + 6)
+ }
+ } else {
+ it("compact() - read-only") {
+ val buf = allocBuffer(10)
+ expect(() => buf.compact()).toThrow
+ }
+ }
+
+ it("slice()" + (if (createsReadOnly) " - read-only" else "")) {
+ val buf1 = withContent(10, charRange(0, 10): _*)
+ buf1.position(3)
+ buf1.limit(7)
+ buf1.mark()
+ val buf2 = buf1.slice()
+ expect(buf2.position()).toEqual(0)
+ expect(buf2.limit()).toEqual(4)
+ expect(buf2.capacity()).toEqual(4)
+ expect(() => buf2.reset()).toThrow
+ expect(buf2.get(1)).toEqual(4)
+
+ buf2.position(2)
+ expect(buf1.position()).toEqual(3)
+
+ if (!createsReadOnly) {
+ buf2.put(89.toChar)
+ expect(buf1.get(5)).toEqual(89)
+ expect(buf2.position()).toEqual(3)
+ expect(buf1.position()).toEqual(3)
+ }
+
+ expect(() => buf2.limit(5)).toThrow
+ expect(buf2.limit()).toEqual(4)
+
+ buf2.limit(3)
+ expect(buf1.limit()).toEqual(7)
+
+ if (!createsReadOnly) {
+ buf1.put(3, 23)
+ expect(buf2.get(0)).toEqual(23)
+ }
+ }
+
+ it("duplicate()" + (if (createsReadOnly) " - read-only" else "")) {
+ val buf1 = withContent(10, charRange(0, 10): _*)
+ buf1.position(3)
+ buf1.limit(7)
+ buf1.mark()
+ val buf2 = buf1.duplicate()
+ expect(buf2.position()).toEqual(3)
+ expect(buf2.limit()).toEqual(7)
+ expect(buf2.capacity()).toEqual(10)
+ expect(buf2.get(4)).toEqual(4)
+
+ buf2.position(4)
+ expect(buf1.position()).toEqual(3)
+ expect(buf2.position()).toEqual(4)
+
+ buf2.reset()
+ expect(buf2.position()).toEqual(3)
+ buf2.position(4)
+
+ if (!createsReadOnly) {
+ buf2.put(89.toChar)
+ expect(buf1.get(4)).toEqual(89)
+ expect(buf2.position()).toEqual(5)
+ expect(buf1.position()).toEqual(3)
+ }
+
+ buf2.limit(5)
+ expect(buf1.limit()).toEqual(7)
+
+ if (!createsReadOnly) {
+ buf1.put(6, 23)
+ buf2.limit(10)
+ expect(buf2.get(6)).toEqual(23)
+ }
+ }
+ }
+
+ describe("Allocated CharBuffer") {
+ defineTests(new Factory {
+ def allocBuffer(capacity: Int): CharBuffer =
+ CharBuffer.allocate(capacity)
+ })
+ }
+
+ class WrappedCharBufferFactory extends Factory {
+ def allocBuffer(capacity: Int): CharBuffer =
+ CharBuffer.wrap(new Array[Char](capacity))
+
+ override def allocBuffer(pos: Int, limit: Int, capacity: Int): CharBuffer =
+ CharBuffer.wrap(new Array[Char](capacity), pos, limit-pos)
+
+ override def withContent(pos: Int, limit: Int, capacity: Int,
+ content: Char*): CharBuffer = {
+ val after = capacity - (pos + content.size)
+ CharBuffer.wrap(
+ (Seq.fill(pos)(0.toChar) ++ content ++ Seq.fill(after)(0.toChar)).toArray,
+ pos, limit-pos)
+ }
+ }
+
+ describe("Wrapped CharBuffer") {
+ defineTests(new WrappedCharBufferFactory)
+ }
+
+ describe("Read-only wrapped CharBuffer") {
+ defineTests(new WrappedCharBufferFactory {
+ override val createsReadOnly = true
+
+ override def allocBuffer(capacity: Int): CharBuffer =
+ super.allocBuffer(capacity).asReadOnlyBuffer()
+
+ override def allocBuffer(pos: Int, limit: Int, capacity: Int): CharBuffer =
+ super.allocBuffer(pos, limit, capacity).asReadOnlyBuffer()
+
+ override def withContent(pos: Int, limit: Int, capacity: Int,
+ content: Char*): CharBuffer =
+ super.withContent(pos, limit, capacity, content: _*).asReadOnlyBuffer()
+ })
+ }
+
+ describe("CharBuffer wrapping a CharSequence") {
+ defineTests(new Factory {
+ override val createsReadOnly = true
+
+ def allocBuffer(capacity: Int): CharBuffer = {
+ if (capacity < 0)
+ throw new IllegalArgumentException
+ CharBuffer.wrap(zeros(capacity))
+ }
+
+ override def allocBuffer(pos: Int, limit: Int, capacity: Int): CharBuffer = {
+ if (capacity < 0)
+ throw new IllegalArgumentException
+ CharBuffer.wrap(zeros(capacity), pos, limit-pos)
+ }
+
+ override def withContent(pos: Int, limit: Int, capacity: Int,
+ content: Char*): CharBuffer = {
+ val after = capacity - (pos + content.size)
+ CharBuffer.wrap(
+ zeros(pos) + content.mkString + zeros(after),
+ pos, limit-pos)
+ }
+ })
+ }
+
+ describe("Sliced CharBuffer") {
+ defineTests(new Factory {
+ def allocBuffer(capacity: Int): CharBuffer = {
+ if (capacity < 0)
+ throw new IllegalArgumentException
+ val buf = CharBuffer.allocate(capacity+25)
+ buf.position(17)
+ buf.limit(17+capacity)
+ buf.slice()
+ }
+
+ override def withContent(pos: Int, limit: Int, capacity: Int,
+ content: Char*): CharBuffer = {
+ if (!(0 <= pos && pos <= limit && limit <= capacity))
+ throw new IllegalArgumentException
+ val buf = CharBuffer.allocate(capacity+25)
+ buf.position(9+pos)
+ buf.put(content.toArray)
+ buf.position(9)
+ buf.limit(9+capacity)
+ val buf2 = buf.slice()
+ buf2.position(pos)
+ buf2.limit(limit)
+ buf2
+ }
+ })
+ }
+
+ describe("Sliced CharBuffer wrapping a CharSequence") {
+ defineTests(new Factory {
+ override val createsReadOnly = true
+
+ def allocBuffer(capacity: Int): CharBuffer = {
+ if (capacity < 0)
+ throw new IllegalArgumentException
+ val buf = CharBuffer.wrap(zeros(capacity+25))
+ buf.position(17)
+ buf.limit(17+capacity)
+ buf.slice()
+ }
+
+ override def withContent(pos: Int, limit: Int, capacity: Int,
+ content: Char*): CharBuffer = {
+ if (!(0 <= pos && pos <= limit && limit <= capacity))
+ throw new IllegalArgumentException
+ val after = (25+capacity) - (9+pos+content.size)
+ val buf = CharBuffer.wrap(zeros(9+pos) + content.mkString + zeros(after))
+ buf.position(9)
+ buf.limit(9+capacity)
+ val buf2 = buf.slice()
+ buf2.position(pos)
+ buf2.limit(limit)
+ buf2
+ }
+ })
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niocharset/BaseCharsetTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niocharset/BaseCharsetTest.scala
new file mode 100644
index 0000000..adda838
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niocharset/BaseCharsetTest.scala
@@ -0,0 +1,234 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.niocharset
+
+import scala.language.implicitConversions
+
+import scala.util._
+
+import java.nio._
+import java.nio.charset._
+
+import scala.scalajs.js
+import js.JSConverters._
+
+import org.scalajs.jasminetest.JasmineTest
+
+class BaseCharsetTest(val charset: Charset) extends JasmineTest {
+ import BaseCharsetTest._
+
+ protected val AllErrorActions = Seq(
+ CodingErrorAction.IGNORE,
+ CodingErrorAction.REPLACE,
+ CodingErrorAction.REPORT)
+
+ protected val ReportActions = Seq(
+ CodingErrorAction.REPORT)
+
+ protected def testDecode(in: ByteBuffer)(
+ outParts: OutPart[CharBuffer]*): Unit = {
+
+ def testOneConfig(malformedAction: CodingErrorAction,
+ unmappableAction: CodingErrorAction): Unit = {
+
+ val decoder = charset.newDecoder()
+ decoder.onMalformedInput(malformedAction)
+ decoder.onUnmappableCharacter(unmappableAction)
+
+ val actualTry = Try {
+ in.mark()
+ val buf =
+ try decoder.decode(in)
+ finally in.reset()
+ val actualChars = new Array[Char](buf.remaining())
+ buf.get(actualChars)
+ actualChars
+ }
+
+ val expectedTry = Try {
+ val expectedChars = Array.newBuilder[Char]
+ outParts foreach {
+ case BufferPart(buf) =>
+ val bufArray = new Array[Char](buf.remaining)
+ buf.mark()
+ try buf.get(bufArray)
+ finally buf.reset()
+ expectedChars ++= bufArray
+ case Malformed(len) =>
+ malformedAction match {
+ case CodingErrorAction.IGNORE =>
+ case CodingErrorAction.REPLACE =>
+ expectedChars ++= decoder.replacement()
+ case CodingErrorAction.REPORT =>
+ throw new MalformedInputException(len)
+ }
+ case Unmappable(len) =>
+ unmappableAction match {
+ case CodingErrorAction.IGNORE =>
+ case CodingErrorAction.REPLACE =>
+ expectedChars ++= decoder.replacement()
+ case CodingErrorAction.REPORT =>
+ throw new UnmappableCharacterException(len)
+ }
+ }
+ expectedChars.result()
+ }
+
+ (actualTry, expectedTry) match {
+ case (Failure(actualEx: MalformedInputException),
+ Failure(expectedEx: MalformedInputException)) =>
+ expect(actualEx.getInputLength()).toEqual(expectedEx.getInputLength())
+
+ case (Failure(actualEx: UnmappableCharacterException),
+ Failure(expectedEx: UnmappableCharacterException)) =>
+ expect(actualEx.getInputLength()).toEqual(expectedEx.getInputLength())
+
+ case (Success(actualChars), Success(expectedChars)) =>
+ expect(actualChars.map(_.toInt).toJSArray).toEqual(
+ expectedChars.map(_.toInt).toJSArray)
+
+ case _ =>
+ // For the error message
+ expect(actualTry.asInstanceOf[js.Any]).toBe(
+ expectedTry.asInstanceOf[js.Any])
+ }
+ }
+
+ val hasAnyMalformed = outParts.exists(_.isInstanceOf[Malformed])
+ val hasAnyUnmappable = outParts.exists(_.isInstanceOf[Unmappable])
+
+ for {
+ malformedAction <- if (hasAnyMalformed) AllErrorActions else ReportActions
+ unmappableAction <- if (hasAnyUnmappable) AllErrorActions else ReportActions
+ } {
+ testOneConfig(malformedAction, unmappableAction)
+ }
+ }
+
+ protected def testEncode(in: CharBuffer)(
+ outParts: OutPart[ByteBuffer]*): Unit = {
+
+ def testOneConfig(malformedAction: CodingErrorAction,
+ unmappableAction: CodingErrorAction): Unit = {
+
+ val encoder = charset.newEncoder()
+ encoder.onMalformedInput(malformedAction)
+ encoder.onUnmappableCharacter(unmappableAction)
+
+ val actualTry = Try {
+ in.mark()
+ val buf =
+ try encoder.encode(in)
+ finally in.reset()
+ val actualBytes = new Array[Byte](buf.remaining())
+ buf.get(actualBytes)
+ actualBytes
+ }
+
+ val expectedTry = Try {
+ val expectedBytes = Array.newBuilder[Byte]
+ outParts foreach {
+ case BufferPart(buf) =>
+ val bufArray = new Array[Byte](buf.remaining)
+ buf.mark()
+ try buf.get(bufArray)
+ finally buf.reset()
+ expectedBytes ++= bufArray
+ case Malformed(len) =>
+ malformedAction match {
+ case CodingErrorAction.IGNORE =>
+ case CodingErrorAction.REPLACE =>
+ expectedBytes ++= encoder.replacement()
+ case CodingErrorAction.REPORT =>
+ throw new MalformedInputException(len)
+ }
+ case Unmappable(len) =>
+ unmappableAction match {
+ case CodingErrorAction.IGNORE =>
+ case CodingErrorAction.REPLACE =>
+ expectedBytes ++= encoder.replacement()
+ case CodingErrorAction.REPORT =>
+ throw new UnmappableCharacterException(len)
+ }
+ }
+ expectedBytes.result()
+ }
+
+ (actualTry, expectedTry) match {
+ case (Failure(actualEx: MalformedInputException),
+ Failure(expectedEx: MalformedInputException)) =>
+ expect(actualEx.getInputLength()).toEqual(expectedEx.getInputLength())
+
+ case (Failure(actualEx: UnmappableCharacterException),
+ Failure(expectedEx: UnmappableCharacterException)) =>
+ expect(actualEx.getInputLength()).toEqual(expectedEx.getInputLength())
+
+ case (Success(actualBytes), Success(expectedBytes)) =>
+ expect(actualBytes.toJSArray).toEqual(expectedBytes.toJSArray)
+
+ case _ =>
+ // For the error message
+ expect(actualTry.asInstanceOf[js.Any]).toBe(
+ expectedTry.asInstanceOf[js.Any])
+ }
+ }
+
+ val hasAnyMalformed = outParts.exists(_.isInstanceOf[Malformed])
+ val hasAnyUnmappable = outParts.exists(_.isInstanceOf[Unmappable])
+
+ for {
+ malformedAction <- if (hasAnyMalformed) AllErrorActions else ReportActions
+ unmappableAction <- if (hasAnyUnmappable) AllErrorActions else ReportActions
+ } {
+ testOneConfig(malformedAction, unmappableAction)
+ }
+ }
+}
+
+object BaseCharsetTest {
+ sealed abstract class OutPart[+BufferType <: Buffer]
+ final case class BufferPart[BufferType <: Buffer](buf: BufferType) extends OutPart[BufferType]
+ final case class Malformed(length: Int) extends OutPart[Nothing]
+ final case class Unmappable(length: Int) extends OutPart[Nothing]
+
+ object OutPart {
+ implicit def fromBuffer[BufferType <: Buffer](buf: BufferType): BufferPart[BufferType] =
+ BufferPart(buf)
+ }
+
+ implicit class Interpolators(val sc: StringContext) extends AnyVal {
+ def bb(args: Any*): ByteBuffer = {
+ val strings = sc.parts.iterator
+ val expressions = args.iterator
+ val buf = Array.newBuilder[Byte]
+
+ def appendStr(s: String): Unit = {
+ val s1 = s.replace(" ", "")
+ require(s1.length % 2 == 0)
+ for (i <- 0 until s1.length by 2)
+ buf += java.lang.Integer.parseInt(s1.substring(i, i+2), 16).toByte
+ }
+
+ appendStr(strings.next())
+ while (strings.hasNext) {
+ expressions.next() match {
+ case b: Byte => buf += b
+ case bytes: Array[Byte] => buf ++= bytes
+ case bytes: Seq[_] =>
+ buf ++= bytes.map(_.asInstanceOf[Number].byteValue())
+ }
+ appendStr(strings.next())
+ }
+
+ ByteBuffer.wrap(buf.result())
+ }
+
+ def cb(args: Any*): CharBuffer =
+ CharBuffer.wrap(sc.s(args: _*))
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niocharset/CharsetTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niocharset/CharsetTest.scala
new file mode 100644
index 0000000..15b0150
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niocharset/CharsetTest.scala
@@ -0,0 +1,74 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.niocharset
+
+import scala.language.implicitConversions
+
+import java.nio._
+import java.nio.charset._
+
+import scala.scalajs.js
+import scala.scalajs.niocharset.StandardCharsets._
+
+import org.scalajs.jasminetest.JasmineTest
+
+object CharsetTest extends JasmineTest {
+ implicit def charsetAsJSAny(charset: Charset): js.Any =
+ charset.asInstanceOf[js.Any]
+
+ describe("java.nio.charset.Charset") {
+ it("defaultCharset") {
+ expect(Charset.defaultCharset()).toBe(UTF_8)
+ }
+
+ it("forName") {
+ expect(Charset.forName("ISO-8859-1")).toBe(ISO_8859_1)
+ expect(Charset.forName("Iso8859-1")).toBe(ISO_8859_1)
+ expect(Charset.forName("iso_8859_1")).toBe(ISO_8859_1)
+ expect(Charset.forName("LaTin1")).toBe(ISO_8859_1)
+ expect(Charset.forName("l1")).toBe(ISO_8859_1)
+
+ expect(Charset.forName("US-ASCII")).toBe(US_ASCII)
+ expect(Charset.forName("Default")).toBe(US_ASCII)
+
+ expect(Charset.forName("UTF-8")).toBe(UTF_8)
+ expect(Charset.forName("utf-8")).toBe(UTF_8)
+ expect(Charset.forName("UtF8")).toBe(UTF_8)
+ expect(Charset.forName("UTF_8")).toBe(UTF_8)
+ expect(Charset.forName("UTF-8")).toBe(UTF_8)
+
+ expect(Charset.forName("UTF-16BE")).toBe(UTF_16BE)
+ expect(Charset.forName("Utf_16BE")).toBe(UTF_16BE)
+ expect(Charset.forName("UnicodeBigUnmarked")).toBe(UTF_16BE)
+
+ expect(Charset.forName("UTF-16le")).toBe(UTF_16LE)
+ expect(Charset.forName("Utf_16le")).toBe(UTF_16LE)
+ expect(Charset.forName("UnicodeLittleUnmarked")).toBe(UTF_16LE)
+
+ expect(Charset.forName("UTF-16")).toBe(UTF_16)
+ expect(Charset.forName("Utf_16")).toBe(UTF_16)
+ expect(Charset.forName("unicode")).toBe(UTF_16)
+ expect(Charset.forName("UnicodeBig")).toBe(UTF_16)
+
+ expect(() => Charset.forName("this-charset-does-not-exist")).toThrow
+ }
+
+ it("isSupported") {
+ expect(Charset.isSupported("ISO-8859-1")).toBeTruthy
+ expect(Charset.isSupported("US-ASCII")).toBeTruthy
+ expect(Charset.isSupported("Default")).toBeTruthy
+ expect(Charset.isSupported("utf-8")).toBeTruthy
+ expect(Charset.isSupported("UnicodeBigUnmarked")).toBeTruthy
+ expect(Charset.isSupported("Utf_16le")).toBeTruthy
+ expect(Charset.isSupported("UTF-16")).toBeTruthy
+ expect(Charset.isSupported("unicode")).toBeTruthy
+
+ expect(Charset.isSupported("this-charset-does-not-exist")).toBeFalsy
+ }
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niocharset/Latin1Test.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niocharset/Latin1Test.scala
new file mode 100644
index 0000000..f6ddc36
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niocharset/Latin1Test.scala
@@ -0,0 +1,91 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.niocharset
+
+import java.nio._
+import java.nio.charset._
+
+import scala.scalajs.niocharset.StandardCharsets
+
+import BaseCharsetTest._
+
+object Latin1Test extends BaseCharsetTest(StandardCharsets.ISO_8859_1) {
+ describe("ISO-8859-1") {
+ it("decode") {
+ // Simple tests
+
+ testDecode(bb"48 65 6c 6c 6f")(cb"Hello")
+ testDecode(bb"42 6f 6e 6a 6f 75 72")(cb"Bonjour")
+
+ testDecode(bb"00 01 0a 10 20")(cb"\u0000\u0001\u000a\u0010 ")
+ testDecode(bb"7f 7f")(cb"\u007f\u007f")
+
+ testDecode(bb"c8 e5 ec ec ef")(cb"Èåììï")
+ testDecode(bb"c2 ef ee ea ef f5 f2")(cb"Âïîêïõò")
+
+ testDecode(bb"80 81 8a 90 a0")(cb"\u0080\u0081\u008a\u0090\u00a0")
+ testDecode(bb"ff ff")(cb"ÿÿ")
+ }
+
+ it("encode") {
+ // Simple tests
+
+ testEncode(cb"Hello")(bb"48 65 6c 6c 6f")
+ testEncode(cb"Bonjour")(bb"42 6f 6e 6a 6f 75 72")
+
+ testEncode(cb"\u0000\u0001\u000a\u0010 ")(bb"00 01 0a 10 20")
+ testEncode(cb"\u007f\u007f")(bb"7f 7f")
+
+ testEncode(cb"Èåììï")(bb"c8 e5 ec ec ef")
+ testEncode(cb"Âïîêïõò")(bb"c2 ef ee ea ef f5 f2")
+
+ testEncode(cb"\u0080\u0081\u008a\u0090\u00a0")(bb"80 81 8a 90 a0")
+ testEncode(cb"ÿÿ")(bb"ff ff")
+
+ // Unmappable characters
+
+ testEncode(cb"\u0100")(Unmappable(1))
+ testEncode(cb"\u07ff")(Unmappable(1))
+ testEncode(cb"\ue000")(Unmappable(1))
+ testEncode(cb"\uffff")(Unmappable(1))
+ testEncode(cb"\ud835\udcd7")(Unmappable(2))
+
+ testEncode(cb"\u0100A")(Unmappable(1), bb"41")
+ testEncode(cb"\u07ffA")(Unmappable(1), bb"41")
+ testEncode(cb"\ue000A")(Unmappable(1), bb"41")
+ testEncode(cb"\uffffA")(Unmappable(1), bb"41")
+ testEncode(cb"\ud835\udcd7A")(Unmappable(2), bb"41")
+
+ // Single UTF-16 surrogates
+ testEncode(cb"\ud800")(Malformed(1))
+ testEncode(cb"\udaff")(Malformed(1))
+ testEncode(cb"\udb80")(Malformed(1))
+ testEncode(cb"\udbff")(Malformed(1))
+ testEncode(cb"\udc00")(Malformed(1))
+ testEncode(cb"\udf80")(Malformed(1))
+ testEncode(cb"\udfff")(Malformed(1))
+
+ // High UTF-16 surrogates not followed by low surrogates
+ testEncode(cb"\ud800A")(Malformed(1), bb"41")
+ testEncode(cb"\ud800\ud800")(Malformed(1), Malformed(1))
+ testEncode(cb"\ud800\ud835\udcd7")(Malformed(1), Unmappable(2))
+ testEncode(cb"\udbffA")(Malformed(1), bb"41")
+ testEncode(cb"\udbff\udb8f")(Malformed(1), Malformed(1))
+ testEncode(cb"\udbff\ud835\udcd7")(Malformed(1), Unmappable(2))
+ }
+
+ it("isLegalReplacement") {
+ val encoder = charset.newEncoder
+ expect(encoder.isLegalReplacement(Array(0x00.toByte))).toBeTruthy
+ expect(encoder.isLegalReplacement(Array(0x41.toByte))).toBeTruthy
+ expect(encoder.isLegalReplacement(Array('?'.toByte))).toBeTruthy
+ expect(encoder.isLegalReplacement(Array(0x80.toByte))).toBeTruthy
+ expect(encoder.isLegalReplacement(Array(0xff.toByte))).toBeTruthy
+ }
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niocharset/USASCIITest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niocharset/USASCIITest.scala
new file mode 100644
index 0000000..2352d3e
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niocharset/USASCIITest.scala
@@ -0,0 +1,93 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.niocharset
+
+import java.nio._
+import java.nio.charset._
+
+import scala.scalajs.niocharset.StandardCharsets
+
+import BaseCharsetTest._
+
+object USASCIITest extends BaseCharsetTest(StandardCharsets.US_ASCII) {
+ describe("US-ASCII") {
+ it("decode") {
+ // Simple tests
+
+ testDecode(bb"48 65 6c 6c 6f")(cb"Hello")
+ testDecode(bb"42 6f 6e 6a 6f 75 72")(cb"Bonjour")
+
+ testDecode(bb"00 01 0a 10 20")(cb"\u0000\u0001\u000a\u0010 ")
+ testDecode(bb"7f 7f")(cb"\u007f\u007f")
+
+ // Bit 7 is ignored, giving the same results as above
+
+ testDecode(bb"c8 e5 ec ec ef")(cb"Hello")
+ testDecode(bb"c2 ef ee ea ef f5 f2")(cb"Bonjour")
+
+ testDecode(bb"80 81 8a 90 a0")(cb"\u0000\u0001\u000a\u0010 ")
+ testDecode(bb"ff ff")(cb"\u007f\u007f")
+ }
+
+ it("encode") {
+ // Simple tests
+
+ testEncode(cb"Hello")(bb"48 65 6c 6c 6f")
+ testEncode(cb"Bonjour")(bb"42 6f 6e 6a 6f 75 72")
+
+ testEncode(cb"\u0000\u0001\u000a\u0010 ")(bb"00 01 0a 10 20")
+ testEncode(cb"\u007f\u007f")(bb"7f 7f")
+
+ // Unmappable characters
+
+ testEncode(cb"é")(Unmappable(1))
+ testEncode(cb"\u0080")(Unmappable(1))
+ testEncode(cb"\u00ff")(Unmappable(1))
+ testEncode(cb"\u0100")(Unmappable(1))
+ testEncode(cb"\u07ff")(Unmappable(1))
+ testEncode(cb"\ue000")(Unmappable(1))
+ testEncode(cb"\uffff")(Unmappable(1))
+ testEncode(cb"\ud835\udcd7")(Unmappable(2))
+
+ testEncode(cb"éA")(Unmappable(1), bb"41")
+ testEncode(cb"\u0080A")(Unmappable(1), bb"41")
+ testEncode(cb"\u00ffA")(Unmappable(1), bb"41")
+ testEncode(cb"\u0100A")(Unmappable(1), bb"41")
+ testEncode(cb"\u07ffA")(Unmappable(1), bb"41")
+ testEncode(cb"\ue000A")(Unmappable(1), bb"41")
+ testEncode(cb"\uffffA")(Unmappable(1), bb"41")
+ testEncode(cb"\ud835\udcd7A")(Unmappable(2), bb"41")
+
+ // Single UTF-16 surrogates
+ testEncode(cb"\ud800")(Malformed(1))
+ testEncode(cb"\udaff")(Malformed(1))
+ testEncode(cb"\udb80")(Malformed(1))
+ testEncode(cb"\udbff")(Malformed(1))
+ testEncode(cb"\udc00")(Malformed(1))
+ testEncode(cb"\udf80")(Malformed(1))
+ testEncode(cb"\udfff")(Malformed(1))
+
+ // High UTF-16 surrogates not followed by low surrogates
+ testEncode(cb"\ud800A")(Malformed(1), bb"41")
+ testEncode(cb"\ud800\ud800")(Malformed(1), Malformed(1))
+ testEncode(cb"\ud800\ud835\udcd7")(Malformed(1), Unmappable(2))
+ testEncode(cb"\udbffA")(Malformed(1), bb"41")
+ testEncode(cb"\udbff\udb8f")(Malformed(1), Malformed(1))
+ testEncode(cb"\udbff\ud835\udcd7")(Malformed(1), Unmappable(2))
+ }
+
+ it("isLegalReplacement") {
+ val encoder = charset.newEncoder
+ expect(encoder.isLegalReplacement(Array(0x00.toByte))).toBeTruthy
+ expect(encoder.isLegalReplacement(Array(0x41.toByte))).toBeTruthy
+ expect(encoder.isLegalReplacement(Array('?'.toByte))).toBeTruthy
+ expect(encoder.isLegalReplacement(Array(0x80.toByte))).toBeTruthy
+ expect(encoder.isLegalReplacement(Array(0xff.toByte))).toBeTruthy
+ }
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niocharset/UTF16Test.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niocharset/UTF16Test.scala
new file mode 100644
index 0000000..85d4eff
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niocharset/UTF16Test.scala
@@ -0,0 +1,155 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.niocharset
+
+import java.nio._
+import java.nio.charset._
+
+import scala.scalajs.niocharset.StandardCharsets
+
+import BaseCharsetTest._
+
+abstract class BaseUTF16Test(charset: Charset) extends BaseCharsetTest(charset) {
+ describe(charset.name) {
+ it("decode") {
+ // ASCII characters
+ testDecode(bb"0042 006f 006e 006a 006f 0075 0072")(cb"Bonjour")
+
+ // Other characters without surrogate pairs
+ testDecode(bb"0047 0072 00fc 00df 0020 0047 006f 0074 0074")(cb"Grüß Gott")
+ testDecode(bb"039a 03b1 03bb 03b7 03bc 03ad 03c1 03b1")(cb"Καλημέρα")
+ testDecode(bb"0635 0628 0627 062d 0020 0627 0644 062e 064a 0631")(cb"صباح الخير")
+ testDecode(bb"3053 3093 306b 3061 306f")(cb"こんにちは")
+ testDecode(bb"0414 043e 0431 0440 044b 0439 0020 0434 0435 043d 044c")(cb"Добрый день")
+ testDecode(bb"4f60 597d")(cb"你好")
+
+ // 4-byte characters
+ testDecode(bb"d835 dcd7 d835 dcee d835 dcf5 d835 dcf5 d835 dcf8")(
+ cb"\ud835\udcd7\ud835\udcee\ud835\udcf5\ud835\udcf5\ud835\udcf8")
+
+ testDecode(bb"")(cb"")
+
+ // Here begin the sequences with at least one error
+
+ // Single UTF-16 surrogates
+ testDecode(bb"d800")(Malformed(2))
+ testDecode(bb"daff")(Malformed(2))
+ testDecode(bb"db80")(Malformed(2))
+ testDecode(bb"dbff")(Malformed(2))
+ testDecode(bb"dc00")(Malformed(2))
+ testDecode(bb"df80")(Malformed(2))
+ testDecode(bb"dfff")(Malformed(2))
+
+ // High UTF-16 surrogates not followed by low surrogates
+ testDecode(bb"d800 0041")(Malformed(2), cb"A")
+ testDecode(bb"d800 d800")(Malformed(2), Malformed(2))
+ testDecode(bb"d800 d835 dcd7")(Malformed(2), cb"\ud835\udcd7")
+ testDecode(bb"dbff 0041")(Malformed(2), cb"A")
+ testDecode(bb"dbff db8f")(Malformed(2), Malformed(2))
+ testDecode(bb"dbff d835 dcd7")(Malformed(2), cb"\ud835\udcd7")
+
+ // Lonely byte at the end
+ testDecode(bb"0041 41")(cb"A", Malformed(1))
+ }
+
+ it("encode") {
+ // ASCII characters
+ testEncode(cb"Bonjour")(bb"0042 006f 006e 006a 006f 0075 0072")
+
+ // Other characters without surrogate pairs
+ testEncode(cb"Grüß Gott")(bb"0047 0072 00fc 00df 0020 0047 006f 0074 0074")
+ testEncode(cb"Καλημέρα")(bb"039a 03b1 03bb 03b7 03bc 03ad 03c1 03b1")
+ testEncode(cb"صباح الخير")(bb"0635 0628 0627 062d 0020 0627 0644 062e 064a 0631")
+ testEncode(cb"こんにちは")(bb"3053 3093 306b 3061 306f")
+ testEncode(cb"Добрый день")(bb"0414 043e 0431 0440 044b 0439 0020 0434 0435 043d 044c")
+ testEncode(cb"你好")(bb"4f60 597d")
+
+ // 4-byte characters
+ testEncode(cb"\ud835\udcd7\ud835\udcee\ud835\udcf5\ud835\udcf5\ud835\udcf8")(
+ bb"d835 dcd7 d835 dcee d835 dcf5 d835 dcf5 d835 dcf8")
+
+ testEncode(cb"")(bb"")
+
+ // Here begin the sequences with at least one error
+
+ // Single UTF-16 surrogates
+ testEncode(cb"\ud800")(Malformed(1))
+ testEncode(cb"\udaff")(Malformed(1))
+ testEncode(cb"\udb80")(Malformed(1))
+ testEncode(cb"\udbff")(Malformed(1))
+ testEncode(cb"\udc00")(Malformed(1))
+ testEncode(cb"\udf80")(Malformed(1))
+ testEncode(cb"\udfff")(Malformed(1))
+
+ // High UTF-16 surrogates not followed by low surrogates
+ testEncode(cb"\ud800A")(Malformed(1), bb"0041")
+ testEncode(cb"\ud800\ud800")(Malformed(1), Malformed(1))
+ testEncode(cb"\ud800\ud835\udcd7")(Malformed(1), bb"d835 dcd7")
+ testEncode(cb"\udbffA")(Malformed(1), bb"0041")
+ testEncode(cb"\udbff\udb8f")(Malformed(1), Malformed(1))
+ testEncode(cb"\udbff\ud835\udcd7")(Malformed(1), bb"d835 dcd7")
+ }
+ }
+}
+
+object UTF16BETest extends BaseUTF16Test(StandardCharsets.UTF_16BE)
+
+object UTF16LETest extends BaseUTF16Test(StandardCharsets.UTF_16LE) {
+ override protected def testDecode(in: ByteBuffer)(
+ outParts: OutPart[CharBuffer]*): Unit = {
+ flipByteBuffer(in)
+ super.testDecode(in)(outParts: _*)
+ }
+
+ override protected def testEncode(in: CharBuffer)(
+ outParts: OutPart[ByteBuffer]*): Unit = {
+ for (BufferPart(buf) <- outParts)
+ flipByteBuffer(buf)
+ super.testEncode(in)(outParts: _*)
+ }
+
+ /** Flips all pairs of bytes in a byte buffer, except a potential lonely
+ * last byte.
+ */
+ def flipByteBuffer(buf: ByteBuffer): Unit = {
+ buf.mark()
+ while (buf.remaining() >= 2) {
+ val high = buf.get()
+ val low = buf.get()
+ buf.position(buf.position - 2)
+ buf.put(low)
+ buf.put(high)
+ }
+ buf.reset()
+ }
+}
+
+object UTF16Test extends BaseUTF16Test(StandardCharsets.UTF_16) {
+ def BigEndianBOM = ByteBuffer.wrap(Array(0xfe.toByte, 0xff.toByte))
+
+ override protected def testDecode(in: ByteBuffer)(
+ outParts: OutPart[CharBuffer]*): Unit = {
+ // Without BOM, big endian is assumed
+ super.testDecode(in)(outParts: _*)
+
+ // With BOM, big endian
+ val inWithBOM = ByteBuffer.allocate(2+in.remaining)
+ inWithBOM.put(BigEndianBOM).put(in).flip()
+ super.testDecode(inWithBOM)(outParts: _*)
+
+ // With BOM, little endian
+ UTF16LETest.flipByteBuffer(inWithBOM)
+ super.testDecode(inWithBOM)(outParts: _*)
+ }
+
+ override protected def testEncode(in: CharBuffer)(
+ outParts: OutPart[ByteBuffer]*): Unit = {
+ if (in.remaining == 0) super.testEncode(in)(outParts: _*)
+ else super.testEncode(in)(BufferPart(BigEndianBOM) +: outParts: _*)
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niocharset/UTF8Test.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niocharset/UTF8Test.scala
new file mode 100644
index 0000000..fbb6a9c
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/niocharset/UTF8Test.scala
@@ -0,0 +1,240 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.niocharset
+
+import java.nio._
+import java.nio.charset._
+
+import scala.scalajs.niocharset.StandardCharsets
+
+import BaseCharsetTest._
+
+object UTF8Test extends BaseCharsetTest(StandardCharsets.UTF_8) {
+ describe("UTF-8") {
+ it("decode") {
+ def OutSeq(elems: OutPart[CharBuffer]*): Seq[OutPart[CharBuffer]] =
+ Seq[OutPart[CharBuffer]](elems: _*)
+
+ // 1-byte characters
+ testDecode(bb"42 6f 6e 6a 6f 75 72")(cb"Bonjour")
+
+ // 2-byte characters
+ testDecode(bb"47 72 c3 bc c3 9f 20 47 6f 74 74")(cb"Grüß Gott")
+ testDecode(bb"ce 9a ce b1 ce bb ce b7 ce bc ce ad cf 81 ce b1")(cb"Καλημέρα")
+ testDecode(bb"d8 b5 d8 a8 d8 a7 d8 ad 20 d8 a7 d9 84 d8 ae d9 8a d8 b1")(cb"صباح الخير")
+
+ // 3-byte characters
+ testDecode(bb"e3 81 93 e3 82 93 e3 81 ab e3 81 a1 e3 81 af")(cb"こんにちは")
+ testDecode(bb"d0 94 d0 be d0 b1 d1 80 d1 8b d0 b9 20 d0 b4 d0 b5 d0 bd d1 8c")(cb"Добрый день")
+ testDecode(bb"e4 bd a0 e5 a5 bd")(cb"你好")
+
+ // 4-byte characters
+ testDecode(bb"f0 9d 93 97 f0 9d 93 ae f0 9d 93 b5 f0 9d 93 b5 f0 9d 93 b8")(
+ cb"\ud835\udcd7\ud835\udcee\ud835\udcf5\ud835\udcf5\ud835\udcf8")
+
+ testDecode(bb"")(cb"")
+
+ // First sequence of a certain length
+ testDecode(bb"00")(cb"\u0000")
+ testDecode(bb"c2 80")(cb"\u0080")
+ testDecode(bb"e0 a0 80")(cb"\u0800")
+ testDecode(bb"f0 90 80 80")(cb"\ud800\udc00")
+
+ // Last sequence of a certain length
+ testDecode(bb"7f")(cb"\u007f")
+ testDecode(bb"df bf")(cb"\u07ff")
+ testDecode(bb"ef bf bf")(cb"\uffff")
+ testDecode(bb"f4 8f bf bf")(cb"\udbff\udfff")
+
+ // Other boundary conditions
+ testDecode(bb"ed 9f bf")(cb"\ud7ff")
+ testDecode(bb"ee 80 80")(cb"\ue000")
+ testDecode(bb"ef bf bd")(cb"\ufffd")
+
+ // Here begin the sequences with at least one error
+
+ // Code point too big
+ testDecode(bb"f4 90 80 80")(Malformed(4))
+ testDecode(bb"41 f4 90 80 80 42")(cb"A", Malformed(4), cb"B")
+
+ // Unexpected continuation bytes (each is reported separately)
+ testDecode(bb"80")(Malformed(1))
+ testDecode(bb"bf")(Malformed(1))
+ testDecode(bb"80 80")(Malformed(1), Malformed(1))
+ testDecode(bb"80 80 80")(Malformed(1), Malformed(1), Malformed(1))
+ testDecode(bb"80 80 80 80")(Malformed(1), Malformed(1), Malformed(1), Malformed(1))
+ testDecode(bb"80 80 80 80 80")(Malformed(1), Malformed(1), Malformed(1), Malformed(1), Malformed(1))
+ testDecode(bb"41 80 80 42 80 43")(cb"A", Malformed(1), Malformed(1), cb"B", Malformed(1), cb"C")
+
+ // Lonely start characters, separated by spaces
+ /* We add 2 spaces at the end so that the last 4-byte start does not
+ * swallow the last space as an underflow. */
+ testDecode(bb"${(0xc0 to 0xf4).flatMap(c => Seq(c, 32)) ++ Seq[Byte](32, 32)}")(
+ (0xc0 to 0xf4).flatMap(i => OutSeq(Malformed(1), cb" ")) ++ OutSeq(cb" "): _*)
+ // Now we let it swallow the last space
+ testDecode(bb"${Seq[Byte](32) ++ (0xc0 to 0xf4).flatMap(c => Seq(c, 32))}")(
+ (0xc0 to 0xf4).flatMap(i => OutSeq(cb" ", Malformed(1))): _*)
+
+ // Sequences with some continuation bytes missing
+ testDecode(bb"c2")(Malformed(1))
+ testDecode(bb"e0")(Malformed(1))
+ testDecode(bb"e0 a0")(Malformed(2))
+ testDecode(bb"f0")(Malformed(1))
+ testDecode(bb"f0 90")(Malformed(2))
+ testDecode(bb"f0 90 80")(Malformed(3))
+ // and all of them concatenated
+ testDecode(bb"c2 e0 e0 a0 f0 f0 90 f0 90 80")(
+ Seq(1, 1, 2, 1, 2, 3).map(Malformed(_)): _*)
+ // and with normal sequences interspersed
+ testDecode(bb"c2 41 e0 41 e0 a0 41 f0 41 f0 90 41 f0 90 80 41")(
+ Seq(1, 1, 2, 1, 2, 3).flatMap(l => Seq[OutPart[CharBuffer]](Malformed(l), cb"A")): _*)
+
+ // Impossible bytes
+ testDecode(bb"fe")(Malformed(1))
+ testDecode(bb"ff")(Malformed(1))
+ testDecode(bb"fe fe ff ff")(Malformed(1), Malformed(1), Malformed(1), Malformed(1))
+ // Old 5-byte and 6-byte starts
+ testDecode(bb"f8 80 80 80 af")(
+ Malformed(1), Malformed(1), Malformed(1), Malformed(1), Malformed(1))
+ testDecode(bb"fc 80 80 80 80 af")(
+ Malformed(1), Malformed(1), Malformed(1), Malformed(1), Malformed(1), Malformed(1))
+
+ // Overlong sequences (encoded with more bytes than necessary)
+ // Overlong '/'
+ testDecode(bb"c0 af")(Malformed(2))
+ testDecode(bb"e0 80 af")(Malformed(3))
+ testDecode(bb"f0 80 80 af")(Malformed(4))
+ // Maximum overlong sequences
+ testDecode(bb"c1 bf")(Malformed(2))
+ testDecode(bb"e0 9f bf")(Malformed(3))
+ testDecode(bb"f0 8f bf bf")(Malformed(4))
+ // Overlong NUL
+ testDecode(bb"c0 80")(Malformed(2))
+ testDecode(bb"e0 80 80")(Malformed(3))
+ testDecode(bb"f0 80 80 80")(Malformed(4))
+
+ // Single UTF-16 surrogates
+ testDecode(bb"ed a0 80")(Malformed(3))
+ testDecode(bb"ed ad bf")(Malformed(3))
+ testDecode(bb"ed ae 80")(Malformed(3))
+ testDecode(bb"ed af bf")(Malformed(3))
+ testDecode(bb"ed b0 80")(Malformed(3))
+ testDecode(bb"ed be 80")(Malformed(3))
+ testDecode(bb"ed bf bf")(Malformed(3))
+
+ // Paired UTF-16 surrogates
+ testDecode(bb"ed a0 80 ed b0 80")(Malformed(3), Malformed(3))
+ testDecode(bb"ed a0 80 ed bf bf")(Malformed(3), Malformed(3))
+ testDecode(bb"ed ad bf ed b0 80")(Malformed(3), Malformed(3))
+ testDecode(bb"ed ad bf ed bf bf")(Malformed(3), Malformed(3))
+ testDecode(bb"ed ae 80 ed b0 80")(Malformed(3), Malformed(3))
+ testDecode(bb"ed ae 80 ed bf bf")(Malformed(3), Malformed(3))
+ testDecode(bb"ed af bf ed b0 80")(Malformed(3), Malformed(3))
+ testDecode(bb"ed af bf ed bf bf")(Malformed(3), Malformed(3))
+ }
+
+ it("encode") {
+ def OutSeq(elems: OutPart[ByteBuffer]*): Seq[OutPart[ByteBuffer]] =
+ Seq[OutPart[ByteBuffer]](elems: _*)
+
+ // 1-byte characters
+ testEncode(cb"Bonjour")(bb"42 6f 6e 6a 6f 75 72")
+
+ // 2-byte characters
+ testEncode(cb"Grüß Gott")(bb"47 72 c3 bc c3 9f 20 47 6f 74 74")
+ testEncode(cb"Καλημέρα")(bb"ce 9a ce b1 ce bb ce b7 ce bc ce ad cf 81 ce b1")
+ testEncode(cb"صباح الخير")(bb"d8 b5 d8 a8 d8 a7 d8 ad 20 d8 a7 d9 84 d8 ae d9 8a d8 b1")
+
+ // 3-byte characters
+ testEncode(cb"こんにちは")(bb"e3 81 93 e3 82 93 e3 81 ab e3 81 a1 e3 81 af")
+ testEncode(cb"Добрый день")(bb"d0 94 d0 be d0 b1 d1 80 d1 8b d0 b9 20 d0 b4 d0 b5 d0 bd d1 8c")
+ testEncode(cb"你好")(bb"e4 bd a0 e5 a5 bd")
+
+ // 4-byte characters
+ testEncode(cb"\ud835\udcd7\ud835\udcee\ud835\udcf5\ud835\udcf5\ud835\udcf8")(
+ bb"f0 9d 93 97 f0 9d 93 ae f0 9d 93 b5 f0 9d 93 b5 f0 9d 93 b8")
+
+ testEncode(cb"")(bb"")
+
+ // First sequence of a certain length
+ testEncode(cb"\u0000")(bb"00")
+ testEncode(cb"\u0080")(bb"c2 80")
+ testEncode(cb"\u0800")(bb"e0 a0 80")
+ testEncode(cb"\ud800\udc00")(bb"f0 90 80 80")
+
+ // Last sequence of a certain length
+ testEncode(cb"\u007f")(bb"7f")
+ testEncode(cb"\u07ff")(bb"df bf")
+ testEncode(cb"\uffff")(bb"ef bf bf")
+ testEncode(cb"\udbff\udfff")(bb"f4 8f bf bf")
+
+ // Other boundary conditions
+ testEncode(cb"\ud7ff")(bb"ed 9f bf")
+ testEncode(cb"\ue000")(bb"ee 80 80")
+ testEncode(cb"\ufffd")(bb"ef bf bd")
+
+ // Here begin the sequences with at least one error
+
+ // Single UTF-16 surrogates
+ testEncode(cb"\ud800")(Malformed(1))
+ testEncode(cb"\udaff")(Malformed(1))
+ testEncode(cb"\udb80")(Malformed(1))
+ testEncode(cb"\udbff")(Malformed(1))
+ testEncode(cb"\udc00")(Malformed(1))
+ testEncode(cb"\udf80")(Malformed(1))
+ testEncode(cb"\udfff")(Malformed(1))
+
+ // High UTF-16 surrogates not followed by low surrogates
+ testEncode(cb"\ud800A")(Malformed(1), bb"41")
+ testEncode(cb"\ud800\ud800")(Malformed(1), Malformed(1))
+ testEncode(cb"\ud800\ud835\udcd7")(Malformed(1), bb"f0 9d 93 97")
+ testEncode(cb"\udbffA")(Malformed(1), bb"41")
+ testEncode(cb"\udbff\udb8f")(Malformed(1), Malformed(1))
+ testEncode(cb"\udbff\ud835\udcd7")(Malformed(1), bb"f0 9d 93 97")
+ }
+
+ it("isLegalReplacement") {
+ val encoder = charset.newEncoder
+
+ // The good ones
+
+ expect(encoder.isLegalReplacement(Array(0x00.toByte))).toBeTruthy
+ expect(encoder.isLegalReplacement(Array(0x41.toByte))).toBeTruthy
+ expect(encoder.isLegalReplacement(Array('?'.toByte))).toBeTruthy
+ expect(encoder.isLegalReplacement(Array(0x7f.toByte))).toBeTruthy
+
+ expect(encoder.isLegalReplacement(
+ Array(0xc2.toByte, 0x80.toByte))).toBeTruthy
+ expect(encoder.isLegalReplacement(
+ Array(0xdf.toByte, 0xbf.toByte))).toBeTruthy
+
+ expect(encoder.isLegalReplacement(
+ Array(0xe0.toByte, 0xa0.toByte, 0x80.toByte))).toBeTruthy
+ expect(encoder.isLegalReplacement(
+ Array(0xef.toByte, 0xbf.toByte, 0xbf.toByte))).toBeTruthy
+
+ expect(encoder.isLegalReplacement(
+ Array(0xf0.toByte, 0x90.toByte, 0x80.toByte, 0x80.toByte))).toBeTruthy
+ expect(encoder.isLegalReplacement(
+ Array(0xf4.toByte, 0x8f.toByte, 0xbf.toByte, 0xbf.toByte))).toBeTruthy
+
+ // The bad ones
+
+ expect(encoder.isLegalReplacement(Array(0x80.toByte))).toBeFalsy
+ expect(encoder.isLegalReplacement(Array(0xbf.toByte))).toBeFalsy
+ expect(encoder.isLegalReplacement(Array(0xc2.toByte))).toBeFalsy
+ expect(encoder.isLegalReplacement(Array(0xdf.toByte))).toBeFalsy
+ expect(encoder.isLegalReplacement(Array(0xe0.toByte))).toBeFalsy
+ expect(encoder.isLegalReplacement(Array(0xef.toByte))).toBeFalsy
+ expect(encoder.isLegalReplacement(Array(0xf4.toByte))).toBeFalsy
+
+ expect(encoder.isLegalReplacement(
+ Array(0xe0.toByte, 0x80.toByte))).toBeFalsy
+ }
+ }
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/scalalib/EnumerationTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/scalalib/EnumerationTest.scala
new file mode 100644
index 0000000..f5b17d9
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/scalalib/EnumerationTest.scala
@@ -0,0 +1,95 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.scalalib
+
+import org.scalajs.jasminetest.JasmineTest
+
+object EnumerationTest extends JasmineTest {
+
+ describe("scala.Enumeration") {
+
+ it("should use explicit naming for enumerated values - #38") {
+ object HelpLevel extends Enumeration {
+ type HelpLevel = Value
+ val None = Value("None")
+ val Basic = Value("Basic")
+ val Medium = Value("Medium")
+ val Full = Value("Full")
+ }
+
+ val h = HelpLevel.None
+
+ expect(h.toString).toEqual("None")
+ }
+
+ it("should allow implicit naming for values") {
+ object HelpLevel extends Enumeration {
+ type HelpLevel = Value
+ val None, Basic, Medium, Full = Value
+ val Special = Value(100)
+ val / = Value
+ }
+
+ val h = HelpLevel.Medium
+ expect(h.toString).toEqual("Medium")
+ expect(HelpLevel.Special.toString).toEqual("Special")
+ expect(HelpLevel./.toString).toEqual("$div")
+ }
+
+ it("should give a pseudo toString to unnamed values") {
+ object Test extends Enumeration {
+ private val nullStr: String = null
+ val A = Value(nullStr) // Circumvent compiler replacement and warning
+ }
+
+ expect(Test.A.toString.startsWith(
+ "<Unknown name for enum field #0 of class ")).toBeTruthy
+ }
+
+ it("should give a graceful error message upon name based query when unnamed fields are present") {
+ object Test extends Enumeration {
+ private val nullStr: String = null
+ val A = Value(nullStr) // Circumvent compiler replacement and warning
+ }
+
+ expect(() => Test.withName("A")).toThrow
+ expect {
+ try { Test.withName("A"); ??? }
+ catch { case e: NoSuchElementException => e.getMessage }
+ } toContain {
+ """Couldn't find enum field with name A.
+ |However, there were the following unnamed fields:""".stripMargin
+ }
+ }
+
+ it("should respond to `toString`") {
+ expect(FooBarEnum.toString).toEqual("FooBarEnum")
+ }
+
+ it("should respond to `values`") {
+ expect(FooBarEnum.values.toString).toEqual(
+ "FooBarEnum.ValueSet(A, B, C, D, E, F)")
+ }
+
+ it("should allow setting nextName") {
+ object Test extends Enumeration {
+ nextName = Iterator("x","y","z")
+ val a,b,c = Value
+ }
+
+ expect(Test.values.mkString("|")).toEqual("x|y|z")
+ }
+
+ }
+
+ /** Object is here due to issues with Enumeration.toString inside closures */
+ object FooBarEnum extends Enumeration {
+ val A,B,C,D,E,F = Value
+ }
+
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/scalalib/RangesTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/scalalib/RangesTest.scala
new file mode 100644
index 0000000..511e183
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/scalalib/RangesTest.scala
@@ -0,0 +1,27 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.scalalib
+
+import org.scalajs.jasminetest.JasmineTest
+
+object RangesTest extends JasmineTest {
+
+ describe("Collection ranges") {
+
+ it("Iterable.range should not emit dce warnings - #650") {
+ Iterable.range(1, 10)
+ }
+
+ it("Iterable.range and simple range should be equal") {
+ // Mostly to exercise more methods of ranges for dce warnings
+ expect(Iterable.range(0, 10).toList == (0 until 10).toList).toBeTruthy
+ }
+
+ }
+
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/scalalib/SymbolTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/scalalib/SymbolTest.scala
new file mode 100644
index 0000000..3612629
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/scalalib/SymbolTest.scala
@@ -0,0 +1,63 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.scalalib
+
+import scala.scalajs.js
+import org.scalajs.jasminetest.JasmineTest
+
+object SymbolTest extends JasmineTest {
+
+ describe("scala.Symbol") {
+
+ it("should ensure unique identity") {
+ def expectEqual(sym1: Symbol, sym2: Symbol): Unit = {
+ expect(sym1 eq sym2).toBeTruthy
+ expect(sym1 == sym2).toBeTruthy
+ expect(sym1.equals(sym2)).toBeTruthy
+ expect(sym1.## == sym2.##).toBeTruthy
+ }
+
+ expectEqual('ScalaJS, Symbol("ScalaJS"))
+ expectEqual('$, Symbol("$"))
+ expectEqual('-, Symbol("-"))
+
+ val `42` = Symbol("42")
+ val map = Map[Symbol, js.Any](Symbol("ScalaJS") -> "Scala.js", '$ -> 1.2, `42` -> 42)
+ expect(map('ScalaJS)).toEqual("Scala.js")
+ expect(map(Symbol("$"))).toEqual(1.2)
+ expect(map(Symbol("42"))).toEqual(42)
+ expect(map(`42`)).toEqual(42)
+ }
+
+ it("should support `name`") {
+ val scalajs = 'ScalaJS
+
+ expect(scalajs.name).toEqual("ScalaJS")
+ expect(Symbol("$").name).toEqual("$")
+ expect('$$.name).toEqual("$$")
+ expect('-.name).toEqual("-")
+ expect('*.name).toEqual("*")
+ expect(Symbol("'").name).toEqual("'")
+ expect(Symbol("\"").name).toEqual("\"")
+ }
+
+ it("should support `toString`") {
+ val scalajs = 'ScalaJS
+
+ expect(scalajs.toString).toEqual("'ScalaJS")
+ expect(Symbol("$").toString).toEqual("'$")
+ expect('$$.toString).toEqual("'$$")
+ expect('-.toString).toEqual("'-")
+ expect('*.toString).toEqual("'*")
+ expect(Symbol("'").toString).toEqual("''")
+ expect(Symbol("\"").toString).toEqual("'\"")
+ }
+
+ }
+
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/typedarray/ArrayBufferInputStreamTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/typedarray/ArrayBufferInputStreamTest.scala
new file mode 100644
index 0000000..7c49d0c
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/typedarray/ArrayBufferInputStreamTest.scala
@@ -0,0 +1,36 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.typedarray
+
+import scala.scalajs.testsuite.javalib.CommonStreamsTests
+
+import scala.scalajs.js.typedarray._
+
+import org.scalajs.jasminetest.JasmineTest
+
+/** Tests for our implementation of java.io._ stream classes */
+object ArrayBufferInputStreamTest extends JasmineTest with CommonStreamsTests {
+
+ when("typedarray").
+ describe("scala.scalajs.js.typedarray.ArrayBufferInputStream") {
+ byteArrayInputStreamLikeTests(seq => new ArrayBufferInputStream(
+ new Int8Array(seq).buffer))
+ }
+
+ when("typedarray").
+ describe("scala.scalajs.js.typedarray.ArrayBufferInputStream - with offset") {
+ byteArrayInputStreamLikeTests { seq =>
+ val off = 100
+ val data = new Int8Array(seq.size + off)
+ data.set(seq, off)
+
+ new ArrayBufferInputStream(data.buffer, off, seq.size)
+ }
+ }
+
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/typedarray/ArrayBufferTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/typedarray/ArrayBufferTest.scala
new file mode 100644
index 0000000..9e64c7f
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/typedarray/ArrayBufferTest.scala
@@ -0,0 +1,39 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.typedarray
+
+import org.scalajs.jasminetest.JasmineTest
+
+import scala.scalajs.js.typedarray._
+
+object ArrayBufferTest extends JasmineTest {
+
+ when("typedarry").
+ describe("ArrayBuffer") {
+
+ it("should provide a length constructor") {
+ val x = new ArrayBuffer(100)
+ expect(x.isInstanceOf[ArrayBuffer]).toBeTruthy
+ expect(x.byteLength).toBe(100)
+ }
+
+ it("should provide `slice` with one argument") {
+ val x = new ArrayBuffer(100)
+ val y = x.slice(10)
+ expect(y.byteLength).toBe(90)
+ }
+
+ it("should provide `slice` with two arguments") {
+ val x = new ArrayBuffer(100)
+ val y = x.slice(10, 20)
+ expect(y.byteLength).toBe(10)
+ }
+
+ }
+
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/typedarray/ArraysTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/typedarray/ArraysTest.scala
new file mode 100644
index 0000000..1976f3f
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/typedarray/ArraysTest.scala
@@ -0,0 +1,45 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.typedarray
+
+import scala.scalajs.js.typedarray._
+import scala.scalajs.js.JSConverters._
+
+import scala.reflect._
+
+import org.scalajs.jasminetest.JasmineTest
+
+import scala.scalajs.testsuite.javalib
+
+object ArraysTest extends javalib.ArraysTest {
+
+ override def Array[T : ClassTag](v: T*): scala.Array[T] = classTag[T] match {
+ case ClassTag.Byte =>
+ new Int8Array(v.asInstanceOf[Seq[Byte]].toJSArray)
+ .toArray.asInstanceOf[scala.Array[T]]
+ case ClassTag.Short =>
+ new Int16Array(v.asInstanceOf[Seq[Short]].toJSArray)
+ .toArray.asInstanceOf[scala.Array[T]]
+ case ClassTag.Int =>
+ new Int32Array(v.asInstanceOf[Seq[Int]].toJSArray)
+ .toArray.asInstanceOf[scala.Array[T]]
+ case ClassTag.Float =>
+ new Float32Array(v.asInstanceOf[Seq[Float]].toJSArray)
+ .toArray.asInstanceOf[scala.Array[T]]
+ case ClassTag.Double =>
+ new Float64Array(v.asInstanceOf[Seq[Double]].toJSArray)
+ .toArray.asInstanceOf[scala.Array[T]]
+ case _ => scala.Array(v: _*)
+ }
+
+ override def testBody(suite: => Unit) = {
+ when("typedarray").
+ describe("java.util.Arrays backed with TypedArrays")(suite)
+ }
+
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/typedarray/DataViewTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/typedarray/DataViewTest.scala
new file mode 100644
index 0000000..76c5639
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/typedarray/DataViewTest.scala
@@ -0,0 +1,275 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.typedarray
+
+import org.scalajs.jasminetest.JasmineTest
+
+import scala.scalajs.js.typedarray._
+
+object DataViewTest extends JasmineTest {
+
+ when("typedarray").
+ describe("DataView") {
+
+ it("should provide an ArrayBuffer only constructor") {
+ val buf = new ArrayBuffer(10)
+ val view = new DataView(buf)
+ expect(view.isInstanceOf[DataView]).toBeTruthy
+ expect(view.byteLength).toBe(10)
+ expect(view.byteOffset).toBe(0)
+ expect(view.getInt8(0)).toBe(0)
+ }
+
+ it("should provide an ArrayBuffer and offset constructor") {
+ val buf = new ArrayBuffer(10)
+ val view = new DataView(buf, 2)
+ expect(view.isInstanceOf[DataView]).toBeTruthy
+ expect(view.byteLength).toBe(8)
+ expect(view.byteOffset).toBe(2)
+ expect(view.getInt8(0)).toBe(0)
+ }
+
+ it("should provide an ArrayBuffer, offset and length constructor") {
+ val buf = new ArrayBuffer(10)
+ val view = new DataView(buf, 3, 2)
+ expect(view.isInstanceOf[DataView]).toBeTruthy
+ expect(view.byteLength).toBe(2)
+ expect(view.byteOffset).toBe(3)
+ expect(view.getInt8(0)).toBe(0)
+ }
+
+ it("should provide `getInt8`") {
+ val view = new DataView(new ArrayBuffer(4))
+
+ view.setInt8(0, 1)
+ view.setInt8(1, 127)
+ view.setInt8(3, -50)
+
+ expect(view.getInt8(0)).toBe(1)
+ expect(view.getInt8(1)).toBe(127)
+ expect(view.getInt8(2)).toBe(0)
+ expect(view.getInt8(3)).toBe(-50)
+ }
+
+ it("should provide `getUint8`") {
+ val view = new DataView(new ArrayBuffer(4))
+
+ view.setInt8(0, 1)
+ view.setInt8(1, -127)
+
+ expect(view.getUint8(0)).toBe(1)
+ expect(view.getUint8(1)).toBe(129)
+ expect(view.getUint8(2)).toBe(0)
+ }
+
+ it("should provide `getInt16`") {
+ val view = new DataView(new ArrayBuffer(4))
+
+ view.setUint8(0, 0x01)
+ view.setUint8(1, 0xFF)
+ view.setUint8(2, 0xB3)
+ view.setUint8(3, 0x30)
+
+ expect(view.getInt16(0)).toBe(0x01FF)
+ expect(view.getInt16(0, true)).toBe(-255)
+ expect(view.getInt16(1)).toBe(-77)
+ expect(view.getInt16(2, true)).toBe(0x30B3)
+ }
+
+ it("should provide `getUint16`") {
+ val view = new DataView(new ArrayBuffer(4))
+
+ view.setUint8(0, 0x01)
+ view.setUint8(1, 0xFF)
+ view.setUint8(2, 0xB3)
+ view.setUint8(3, 0x30)
+
+ expect(view.getUint16(0)).toBe(0x01FF)
+ expect(view.getUint16(0, true)).toBe(0xFF01)
+ expect(view.getUint16(1)).toBe(0xFFB3)
+ expect(view.getUint16(2, true)).toBe(0x30B3)
+ }
+
+ it("should provide `getInt32`") {
+ val view = new DataView(new ArrayBuffer(5))
+
+ view.setUint8(0, 0x01)
+ view.setUint8(1, 0xFF)
+ view.setUint8(2, 0xB3)
+ view.setUint8(3, 0x30)
+ view.setUint8(4, 0x1A)
+
+ expect(view.getInt32(0)).toBe(0x01FFB330)
+ expect(view.getInt32(0, true)).toBe(0x30B3FF01)
+ expect(view.getInt32(1)).toBe(0xFFB3301A) // is negative since Int
+ expect(view.getInt32(1, true)).toBe(0x1A30B3FF)
+ }
+
+ it("should provide `getUint32`") {
+ val view = new DataView(new ArrayBuffer(5))
+
+ view.setUint8(0, 0x01)
+ view.setUint8(1, 0xFF)
+ view.setUint8(2, 0xB3)
+ view.setUint8(3, 0x30)
+ view.setUint8(4, 0x1A)
+
+ expect(view.getUint32(0)).toBe(0x01FFB330)
+ expect(view.getUint32(0, true)).toBe(0x30B3FF01)
+ expect(view.getUint32(1)).toBe(0xFFB3301AL.toDouble)
+ expect(view.getUint32(1, true)).toBe(0x1A30B3FF)
+ }
+
+ it("should provide `getFloat32`") {
+ val view = new DataView(new ArrayBuffer(5))
+
+ view.setFloat32(0, 0.1f)
+
+ expect(view.getFloat32(0)).toBeCloseTo(0.1, 7)
+ expect(view.getFloat32(1)).not.toBeCloseTo(0.1, 7)
+ expect(view.getFloat32(0, true)).not.toBeCloseTo(0.1, 7)
+ expect(view.getFloat32(1, true)).not.toBeCloseTo(0.1, 7)
+ }
+
+ it("should provide `getFloat64`") {
+ val view = new DataView(new ArrayBuffer(9))
+
+ view.setFloat64(0, 0.5)
+
+ expect(view.getFloat64(0)).toBe(0.5)
+ expect(view.getFloat64(1)).not.toBe(0.5)
+ expect(view.getFloat64(0, true)).not.toBe(0.5)
+ expect(view.getFloat64(1, true)).not.toBe(0.5)
+ }
+
+ it("should provide `setInt8`") {
+ val view = new DataView(new ArrayBuffer(4))
+
+ view.setInt8(0, 1)
+ view.setInt8(1, 127)
+ view.setInt8(2, -20)
+ view.setInt8(3, -50)
+
+ expect(view.getUint8(0)).toBe(0x01)
+ expect(view.getUint8(1)).toBe(0x7F)
+ expect(view.getUint8(2)).toBe(0xEC)
+ expect(view.getUint8(3)).toBe(0xCE)
+ }
+
+ it("should provide `setUint8`") {
+ val view = new DataView(new ArrayBuffer(4))
+
+ view.setUint8(0, 0x01)
+ view.setUint8(1, 0xFC)
+ view.setUint8(2, 0x4D)
+ view.setUint8(3, 0x8C)
+
+ expect(view.getInt8(0)).toBe(1)
+ expect(view.getInt8(1)).toBe(-4)
+ expect(view.getInt8(2)).toBe(0x4D)
+ expect(view.getInt8(3)).toBe(-116)
+ }
+
+ it("should provide `setInt16`") {
+ val view = new DataView(new ArrayBuffer(4))
+
+ view.setInt16(0, 0x7F3B, true)
+ view.setInt16(2, -1)
+
+ expect(view.getUint8(0)).toBe(0x3B)
+ expect(view.getUint8(1)).toBe(0x7F)
+ expect(view.getUint8(2)).toBe(0xFF)
+ expect(view.getUint8(3)).toBe(0xFF)
+ }
+
+ it("should provide `setUint16`") {
+ val view = new DataView(new ArrayBuffer(4))
+
+ view.setUint16(0, 0x7F3B, true)
+ view.setUint16(2, 0xFCBA)
+
+ expect(view.getUint8(0)).toBe(0x3B)
+ expect(view.getUint8(1)).toBe(0x7F)
+ expect(view.getUint8(2)).toBe(0xFC)
+ expect(view.getUint8(3)).toBe(0xBA)
+
+ view.setUint16(1, 0x03BC)
+
+ expect(view.getUint8(0)).toBe(0x3B)
+ expect(view.getUint8(1)).toBe(0x03)
+ expect(view.getUint8(2)).toBe(0xBC)
+ expect(view.getUint8(3)).toBe(0xBA)
+ }
+
+ it("should provide `setInt32`") {
+ val view = new DataView(new ArrayBuffer(8))
+
+ view.setInt32(0, 0x7F3B8342, true)
+ view.setInt32(3, 0xFCBA1020)
+
+ expect(view.getUint8(0)).toBe(0x42)
+ expect(view.getUint8(1)).toBe(0x83)
+ expect(view.getUint8(2)).toBe(0x3B)
+ expect(view.getUint8(3)).toBe(0xFC)
+ expect(view.getUint8(4)).toBe(0xBA)
+ expect(view.getUint8(5)).toBe(0x10)
+ expect(view.getUint8(6)).toBe(0x20)
+ expect(view.getUint8(7)).toBe(0x00)
+ }
+
+ it("should provide `setUint32`") {
+ val view = new DataView(new ArrayBuffer(8))
+
+ view.setUint32(0, 0x7F3B8342, true)
+ view.setUint32(3, 0xFCBA1020L.toDouble)
+
+ expect(view.getUint8(0)).toBe(0x42)
+ expect(view.getUint8(1)).toBe(0x83)
+ expect(view.getUint8(2)).toBe(0x3B)
+ expect(view.getUint8(3)).toBe(0xFC)
+ expect(view.getUint8(4)).toBe(0xBA)
+ expect(view.getUint8(5)).toBe(0x10)
+ expect(view.getUint8(6)).toBe(0x20)
+ expect(view.getUint8(7)).toBe(0x00)
+ }
+
+ it("should provide `setFloat32`") {
+ val view = new DataView(new ArrayBuffer(5))
+
+ view.setFloat32(0, 0.1f)
+ view.setFloat32(1, 0.4f)
+
+ expect(view.getFloat32(1)).toBeCloseTo(0.4, 7)
+ expect(view.getFloat32(0)).not.toBeCloseTo(0.1, 7)
+
+ view.setFloat32(0, 0.1f, true)
+ view.setFloat32(1, 0.4f, true)
+
+ expect(view.getFloat32(0, true)).not.toBeCloseTo(0.1, 7)
+ expect(view.getFloat32(1, true)).toBeCloseTo(0.4, 7)
+ }
+
+ it("should provide `getFloat64`") {
+ val view = new DataView(new ArrayBuffer(9))
+
+ view.setFloat64(0, 0.5)
+ view.setFloat64(1, 0.6)
+
+ expect(view.getFloat64(0)).not.toBe(0.5)
+ expect(view.getFloat64(1)).toBe(0.6)
+
+ view.setFloat64(0, 0.5, true)
+ view.setFloat64(1, 0.6, true)
+
+ expect(view.getFloat64(0, true)).not.toBe(0.5)
+ expect(view.getFloat64(1, true)).toBe(0.6)
+ }
+
+ }
+
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/typedarray/TypedArrayConversionTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/typedarray/TypedArrayConversionTest.scala
new file mode 100644
index 0000000..dc9a056
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/typedarray/TypedArrayConversionTest.scala
@@ -0,0 +1,197 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.typedarray
+
+import org.scalajs.jasminetest.JasmineTest
+
+import scala.scalajs.js
+import js.typedarray._
+
+object TypedArrayConversionTest extends JasmineTest {
+
+ when("typedarray").
+ describe("TypedArray to scala.Array conversions") {
+
+ def data(factor: Double) = js.Array(-1,1,2,3,4,5,6,7,8).map((_: Int) * factor)
+ def sum(factor: Double) = (8 * 9 / 2 - 1) * factor
+
+ it("should convert an Int8Array to a scala.Array[Byte]") {
+ val x = new Int8Array(data(1))
+ val y = x.toArray
+
+ expect(y.getClass == classOf[scala.Array[Byte]]).toBeTruthy
+ expect(y.sum).toEqual(sum(1))
+
+ // Ensure its a copy
+ x(0) = 0
+ expect(y.sum).toEqual(sum(1))
+ }
+
+ it("should convert an Int16Array to a scala.Array[Short]") {
+ val x = new Int16Array(data(100))
+ val y = x.toArray
+
+ expect(y.getClass == classOf[scala.Array[Short]]).toBeTruthy
+ expect(y.sum).toEqual(sum(100))
+
+ // Ensure its a copy
+ x(0) = 0
+ expect(y.sum).toEqual(sum(100))
+ }
+
+ it("should convert an Uint16Array to a scala.Array[Char]") {
+ val data = js.Array((1 to 6).map(_ * 10000): _*)
+ val sum = (6*7/2*10000).toChar
+
+ val x = new Uint16Array(data)
+ val y = x.toArray
+
+ expect(y.getClass == classOf[scala.Array[Char]]).toBeTruthy
+ expect(y.sum).toEqual(sum)
+
+ // Ensure its a copy
+ x(0) = 0
+ expect(y.sum).toEqual(sum)
+ }
+
+ it("should convert an Int32Array to a scala.Array[Int]") {
+ val x = new Int32Array(data(10000))
+ val y = x.toArray
+
+ expect(y.getClass == classOf[scala.Array[Int]]).toBeTruthy
+ expect(y.sum).toEqual(sum(10000))
+
+ // Ensure its a copy
+ x(0) = 0
+ expect(y.sum).toEqual(sum(10000))
+ }
+
+ it("should convert a Float32Array to a scala.Array[Float]") {
+ val x = new Float32Array(data(0.2))
+ val y = x.toArray
+
+ expect(y.getClass == classOf[scala.Array[Float]]).toBeTruthy
+ expect(y.sum).toBeCloseTo(sum(0.2), 6)
+
+ // Ensure its a copy
+ x(0) = 0
+ expect(y.sum).toBeCloseTo(sum(0.2), 6)
+ }
+
+ it("should convert a Float64Array to a scala.Array[Double]") {
+ val x = new Float64Array(data(0.2))
+ val y = x.toArray
+
+ expect(y.getClass == classOf[scala.Array[Double]]).toBeTruthy
+ expect(y.sum).toEqual(sum(0.2))
+
+ // Ensure its a copy
+ x(0) = 0
+ expect(y.sum).toEqual(sum(0.2))
+ }
+
+ }
+
+ when("typedarray").
+ describe("scala.Array to TypedArray conversions") {
+
+ it("should convert a scala.Array[Byte] to an Int8Array") {
+ val x = (Byte.MinValue to Byte.MaxValue).map(_.toByte).toArray
+ val y = x.toTypedArray
+
+ expect(y.isInstanceOf[Int8Array]).toBeTruthy
+ expect(y.length).toBe(x.length)
+
+ for (i <- 0 until y.length)
+ expect(y(i)).toBe(x(i))
+
+ // Ensure its a copy
+ x(0) = 0
+ expect(y(0)).toBe(Byte.MinValue)
+ }
+
+ it("should convert a scala.Array[Short] to an Int16Array") {
+ val x = ((Short.MinValue to (Short.MinValue + 1000)) ++
+ ((Short.MaxValue - 1000) to Short.MaxValue)).map(_.toShort).toArray
+ val y = x.toTypedArray
+
+ expect(y.isInstanceOf[Int16Array]).toBeTruthy
+ expect(y.length).toBe(x.length)
+
+ for (i <- 0 until y.length)
+ expect(y(i)).toBe(x(i))
+
+ // Ensure its a copy
+ x(0) = 0
+ expect(y(0)).toBe(Short.MinValue)
+ }
+
+ it("should convert a scala.Array[Char] to an Uint16Array") {
+ val x = ((Char.MaxValue - 1000) to Char.MaxValue).map(_.toChar).toArray
+ val y = x.toTypedArray
+
+ expect(y.isInstanceOf[Uint16Array]).toBeTruthy
+ expect(y.length).toBe(x.length)
+
+ for (i <- 0 until y.length)
+ expect(y(i)).toBe(x(i).toInt)
+
+ // Ensure its a copy
+ x(0) = 0
+ expect(y(0)).toBe(Char.MaxValue - 1000)
+ }
+
+ it("should convert a scala.Array[Int] to an Int32Array") {
+ val x = ((Int.MinValue to (Int.MinValue + 1000)) ++
+ ((Int.MaxValue - 1000) to Int.MaxValue)).toArray
+ val y = x.toTypedArray
+
+ expect(y.isInstanceOf[Int32Array]).toBeTruthy
+ expect(y.length).toBe(x.length)
+
+ for (i <- 0 until y.length)
+ expect(y(i)).toBe(x(i))
+
+ // Ensure its a copy
+ x(0) = 0
+ expect(y(0)).toBe(Int.MinValue)
+ }
+
+ it("should convert a scala.Array[Float] to a Float32Array") {
+ val x = Array[Float](1.0f, 2.0f, -2.3f, 5.3f)
+ val y = x.toTypedArray
+
+ expect(y.isInstanceOf[Float32Array]).toBeTruthy
+ expect(y.length).toBe(x.length)
+
+ for (i <- 0 until y.length)
+ expect(y(i)).toBe(x(i))
+
+ // Ensure its a copy
+ x(0) = 0
+ expect(y(0)).toBe(1.0f)
+ }
+
+ it("should convert a scala.Array[Double] to a Float64Array") {
+ val x = Array[Double](1.0, 2.0, -2.3, 5.3)
+ val y = x.toTypedArray
+
+ expect(y.isInstanceOf[Float64Array]).toBeTruthy
+ expect(y.length).toBe(x.length)
+
+ for (i <- 0 until y.length)
+ expect(y(i)).toBe(x(i))
+
+ // Ensure its a copy
+ x(0) = 0
+ expect(y(0)).toBe(1.0)
+ }
+
+ }
+
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/typedarray/TypedArrayTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/typedarray/TypedArrayTest.scala
new file mode 100644
index 0000000..1dfbbd4
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/typedarray/TypedArrayTest.scala
@@ -0,0 +1,332 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Suite **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+package scala.scalajs.testsuite.typedarray
+
+import org.scalajs.jasminetest.JasmineTest
+import org.scalajs.jasmine.JasmineExpectation
+
+import scala.scalajs.js
+import js.typedarray._
+
+/** Shallow test for TypedArrays. Basically just tests that the method exist and
+ * return something which could be a right result. It is probably sufficient to
+ * test whether a runtime supports TypedArrays.
+ */
+object TypedArrayTest extends JasmineTest {
+
+ /** Generalized tests for all TypedArrays */
+ def tests[V, T <: TypedArray[V, T]](name: String,
+ bytesPerElement: => Int,
+ lenCtor: Int => T,
+ tarrCtor: TypedArray[_, _] => T,
+ arrCtor: js.Array[_] => T,
+ bufCtor1: (ArrayBuffer) => T,
+ bufCtor2: (ArrayBuffer, Int) => T,
+ bufCtor3: (ArrayBuffer, Int, Int) => T,
+ hasType: Any => Boolean,
+ expectV: V => JasmineExpectation,
+ intToV: Int => V
+ ) = {
+
+ when("typedarray").
+ describe(name) {
+
+ it(s"should allow constructing a new $name with length") {
+ val x = lenCtor(10)
+ expect(hasType(x)).toBeTruthy
+ expect(x.length).toBe(10)
+ }
+
+ it(s"should allow constructing a new $name from an Int8Array") {
+ val x = tarrCtor(new Float32Array(js.Array(3, 7)))
+ expect(hasType(x)).toBeTruthy
+ expect(x.length).toBe(2)
+
+ expectV(x(0)).toEqual(3)
+ expectV(x(1)).toEqual(7)
+ }
+
+ it(s"should allow constructing a new $name from a js.Array") {
+ val x = arrCtor(js.Array(5,6,7))
+ expect(hasType(x)).toBeTruthy
+ expect(x.length).toBe(3)
+
+ expectV(x(0)).toEqual(5)
+ expectV(x(1)).toEqual(6)
+ expectV(x(2)).toEqual(7)
+ }
+
+ it(s"should allow constructing a new $name from an ArrayBuffer (1 arg)") {
+ val buf = arrCtor(js.Array(5, 6, 7, 8)).buffer
+ val x = bufCtor1(buf)
+ expect(hasType(x)).toBeTruthy
+ expect(x.length).toBe(4)
+
+ expectV(x(0)).toEqual(5)
+ expectV(x(1)).toEqual(6)
+ expectV(x(2)).toEqual(7)
+ expectV(x(3)).toEqual(8)
+ }
+
+ it(s"should allow constructing a new $name from an ArrayBuffer (2 arg)") {
+ val buf = arrCtor(js.Array(5, 6, 7, 8)).buffer
+ val x = bufCtor2(buf, bytesPerElement)
+ expect(hasType(x)).toBeTruthy
+ expect(x.length).toBe(3)
+
+ expectV(x(0)).toEqual(6)
+ expectV(x(1)).toEqual(7)
+ expectV(x(2)).toEqual(8)
+ }
+
+ it(s"should allow constructing a new $name from an ArrayBuffer (3 arg)") {
+ val buf = arrCtor(js.Array(5, 6, 7, 8)).buffer
+ val x = bufCtor3(buf, bytesPerElement, 2)
+ expect(hasType(x)).toBeTruthy
+ expect(x.length).toBe(2)
+
+ expectV(x(0)).toEqual(6)
+ expectV(x(1)).toEqual(7)
+ }
+
+ it("should allow retrieving the length") {
+ val x = lenCtor(100)
+ expect(x.length).toBe(100)
+ }
+
+ it("should allow retrieving an element") {
+ val x = arrCtor(js.Array(5))
+ expectV(x(0)).toBe(5)
+ }
+
+ it("should allow setting an element") {
+ val x = lenCtor(2)
+
+ x(0) = intToV(5)
+ x(1) = intToV(10)
+
+ expectV(x(0)).toBe(5)
+ expectV(x(1)).toBe(10)
+ }
+
+ it("should provide `get`") {
+ val x = arrCtor(js.Array(10))
+ expectV(x.get(0)).toBe(10)
+ }
+
+ it("should provide `set` for a single element") {
+ val x = lenCtor(10)
+ x.set(0, intToV(5))
+ x.set(1, intToV(6))
+
+ expectV(x(0)).toBe(5)
+ expectV(x(1)).toBe(6)
+ expectV(x(2)).toBe(0)
+ }
+
+ it("should provide `set` for a js.Array with one arguments") {
+ val x = lenCtor(10)
+ x.set(js.Array(5,6,7))
+ expectV(x(0)).toBe(5)
+ expectV(x(1)).toBe(6)
+ expectV(x(2)).toBe(7)
+ expectV(x(3)).toBe(0)
+ expectV(x(4)).toBe(0)
+ expectV(x(5)).toBe(0)
+ }
+
+ it("should provide `set` for a js.Array with two arguments") {
+ val x = lenCtor(10)
+ x.set(js.Array(5,6,7), 2)
+ expectV(x(0)).toBe(0)
+ expectV(x(1)).toBe(0)
+ expectV(x(2)).toBe(5)
+ expectV(x(3)).toBe(6)
+ expectV(x(4)).toBe(7)
+ expectV(x(5)).toBe(0)
+ }
+
+ it("should provide `set` for a TypedArray with one argument") {
+ val x = lenCtor(10)
+ x.set(new Int8Array(js.Array(5,6,7)))
+ expectV(x(0)).toBe(5)
+ expectV(x(1)).toBe(6)
+ expectV(x(2)).toBe(7)
+ expectV(x(3)).toBe(0)
+ expectV(x(4)).toBe(0)
+ expectV(x(5)).toBe(0)
+ }
+
+ it("should provide `set` for a TypedArray with two arguments") {
+ val x = lenCtor(10)
+ x.set(new Int8Array(js.Array(5,6,7)), 2)
+ expectV(x(0)).toBe(0)
+ expectV(x(1)).toBe(0)
+ expectV(x(2)).toBe(5)
+ expectV(x(3)).toBe(6)
+ expectV(x(4)).toBe(7)
+ expectV(x(5)).toBe(0)
+ }
+
+ it("should provide `subarray` with one argument") {
+ val x = arrCtor(js.Array(1,2,3,4,5,6,7,8,9))
+ val y = x.subarray(2)
+
+ expect(y.length).toBe(7)
+ expectV(y(0)).toBe(3)
+
+ x(2) = intToV(100)
+
+ expectV(y(0)).toBe(100)
+ }
+
+ it("should provide `subarray` with two arguments") {
+ val x = arrCtor(js.Array(1,2,3,4,5,6,7,8,9))
+ val y = x.subarray(2, 4)
+
+ expect(y.length).toBe(2)
+ expectV(y(0)).toBe(3)
+
+ x(2) = intToV(100)
+
+ expectV(y(0)).toBe(100)
+ }
+
+ it("should provide `buffer`") {
+ val x = arrCtor(js.Array(1,2,3,4,5,6,7,8,9))
+ val y = bufCtor3(x.buffer, 0, 2)
+
+ expect(x.buffer eq y.buffer).toBeTruthy
+ }
+
+ it("should provide `byteLength`") {
+ val x = arrCtor(js.Array(0 until bytesPerElement * 4: _*))
+ val y = bufCtor3(x.buffer, bytesPerElement, 3)
+
+ expect(y.byteLength).toBe(3 * bytesPerElement)
+ }
+
+ it("should provide `byteOffset`") {
+ val x = arrCtor(js.Array(0 until bytesPerElement * 4: _*))
+ val y = bufCtor3(x.buffer, bytesPerElement, 3)
+
+ expect(y.byteOffset).toBe(bytesPerElement)
+ }
+
+ }
+ }
+
+ tests("Int8Array",
+ Int8Array.BYTES_PER_ELEMENT,
+ len => new Int8Array(len),
+ tarr => new Int8Array(tarr),
+ arr => new Int8Array(arr),
+ buf => new Int8Array(buf),
+ (buf, start) => new Int8Array(buf, start),
+ (buf, start, end) => new Int8Array(buf, start, end),
+ _.isInstanceOf[Int8Array],
+ expect (_: Byte),
+ _.toByte)
+
+ tests("Uint8Array",
+ Uint8Array.BYTES_PER_ELEMENT,
+ len => new Uint8Array(len),
+ tarr => new Uint8Array(tarr),
+ arr => new Uint8Array(arr),
+ buf => new Uint8Array(buf),
+ (buf, start) => new Uint8Array(buf, start),
+ (buf, start, end) => new Uint8Array(buf, start, end),
+ _.isInstanceOf[Uint8Array],
+ expect (_: Short),
+ _.toShort)
+
+ tests("Uint8ClampedArray",
+ Uint8ClampedArray.BYTES_PER_ELEMENT,
+ len => new Uint8ClampedArray(len),
+ tarr => new Uint8ClampedArray(tarr),
+ arr => new Uint8ClampedArray(arr),
+ buf => new Uint8ClampedArray(buf),
+ (buf, start) => new Uint8ClampedArray(buf, start),
+ (buf, start, end) => new Uint8ClampedArray(buf, start, end),
+ _.isInstanceOf[Uint8ClampedArray],
+ expect (_: Int),
+ _.toInt)
+
+ tests("Int16Array",
+ Int16Array.BYTES_PER_ELEMENT,
+ len => new Int16Array(len),
+ tarr => new Int16Array(tarr),
+ arr => new Int16Array(arr),
+ buf => new Int16Array(buf),
+ (buf, start) => new Int16Array(buf, start),
+ (buf, start, end) => new Int16Array(buf, start, end),
+ _.isInstanceOf[Int16Array],
+ expect (_: Short),
+ _.toShort)
+
+ tests("Uint16Array",
+ Uint16Array.BYTES_PER_ELEMENT,
+ len => new Uint16Array(len),
+ tarr => new Uint16Array(tarr),
+ arr => new Uint16Array(arr),
+ buf => new Uint16Array(buf),
+ (buf, start) => new Uint16Array(buf, start),
+ (buf, start, end) => new Uint16Array(buf, start, end),
+ _.isInstanceOf[Uint16Array],
+ expect (_: Int),
+ _.toInt)
+
+ tests("Int32Array",
+ Int32Array.BYTES_PER_ELEMENT,
+ len => new Int32Array(len),
+ tarr => new Int32Array(tarr),
+ arr => new Int32Array(arr),
+ buf => new Int32Array(buf),
+ (buf, start) => new Int32Array(buf, start),
+ (buf, start, end) => new Int32Array(buf, start, end),
+ _.isInstanceOf[Int32Array],
+ expect (_: Int),
+ _.toInt)
+
+ tests("Uint32Array",
+ Uint32Array.BYTES_PER_ELEMENT,
+ len => new Uint32Array(len),
+ tarr => new Uint32Array(tarr),
+ arr => new Uint32Array(arr),
+ buf => new Uint32Array(buf),
+ (buf, start) => new Uint32Array(buf, start),
+ (buf, start, end) => new Uint32Array(buf, start, end),
+ _.isInstanceOf[Uint32Array],
+ expect (_: Double),
+ _.toDouble)
+
+ tests("Float32Array",
+ Float32Array.BYTES_PER_ELEMENT,
+ len => new Float32Array(len),
+ tarr => new Float32Array(tarr),
+ arr => new Float32Array(arr),
+ buf => new Float32Array(buf),
+ (buf, start) => new Float32Array(buf, start),
+ (buf, start, end) => new Float32Array(buf, start, end),
+ _.isInstanceOf[Float32Array],
+ expect (_: Float),
+ _.toFloat)
+
+ tests("Float64Array",
+ Float64Array.BYTES_PER_ELEMENT,
+ len => new Float64Array(len),
+ tarr => new Float64Array(tarr),
+ arr => new Float64Array(arr),
+ buf => new Float64Array(buf),
+ (buf, start) => new Float64Array(buf, start),
+ (buf, start, end) => new Float64Array(buf, start, end),
+ _.isInstanceOf[Float64Array],
+ expect (_: Double),
+ _.toDouble)
+
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/utils/JSUtils.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/utils/JSUtils.scala
new file mode 100644
index 0000000..4b2ac62
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/utils/JSUtils.scala
@@ -0,0 +1,26 @@
+package scala.scalajs.testsuite.utils
+
+import scala.scalajs.js
+import js.annotation.JSExport
+
+@JSExport("JSUtils")
+object JSUtils {
+ /* We use java.lang.Character explicitly, because this class is used by
+ * tests that check that Chars are actually boxed by the compiler.
+ * If we rely on the compiler doing the job in here, we might have false
+ * positives just because the value was never boxed, and never unboxed.
+ */
+
+ @JSExport
+ def isChar(c: Any): Boolean = c.isInstanceOf[java.lang.Character]
+
+ @JSExport
+ def stringToChar(s: String): java.lang.Character = {
+ assert(s.length == 1, "makeChar() requires a string of length 1")
+ new java.lang.Character(s.charAt(0))
+ }
+
+ @JSExport
+ def charToString(c: Any): String =
+ c.asInstanceOf[java.lang.Character].toString()
+}
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/utils/TestDetector.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/utils/TestDetector.scala
new file mode 100644
index 0000000..ad67a95
--- /dev/null
+++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/utils/TestDetector.scala
@@ -0,0 +1,33 @@
+package scala.scalajs.testsuite.utils
+
+import scala.scalajs.js
+import js.annotation.JSExport
+import js.JSConverters._
+
+/** An ad-hoc but centralized way to detect tests in this test suite */
+@JSExport("scalajs.TestDetector")
+object TestDetector {
+
+ private final val basePackage = "scala.scalajs.testsuite"
+
+ def detectTestNames(): List[String] = detectTestsInternal().map(_._2).toList
+
+ @JSExport
+ def loadDetectedTests(): Unit = detectTestsInternal().foreach(_._1())
+
+ private def detectTestsInternal() = {
+ val parts = basePackage.split('.')
+ val base = parts.foldLeft(js.Dynamic.global)(_.selectDynamic(_))
+
+ // We make sure to use only exported modules (not classes) by checking
+ // .prototype of the exporters.
+ for {
+ pName <- js.Object.properties(base)
+ testName <- js.Object.properties(base.selectDynamic(pName))
+ test = base.selectDynamic(pName).selectDynamic(testName)
+ if js.Object.getPrototypeOf(test.prototype.asInstanceOf[js.Object]) eq
+ js.Object.asInstanceOf[js.Dynamic].prototype
+ } yield (test, s"$basePackage.$pName.$testName")
+ }
+
+}
diff --git a/examples/scala-js/tools/js/src/main/scala/scala/scalajs/tools/classpath/builder/NodeFileSystem.scala b/examples/scala-js/tools/js/src/main/scala/scala/scalajs/tools/classpath/builder/NodeFileSystem.scala
new file mode 100644
index 0000000..d1eee10
--- /dev/null
+++ b/examples/scala-js/tools/js/src/main/scala/scala/scalajs/tools/classpath/builder/NodeFileSystem.scala
@@ -0,0 +1,67 @@
+package scala.scalajs.tools.classpath.builder
+
+import scala.scalajs.tools.io._
+
+import scala.scalajs.js
+
+import scala.collection.immutable.Traversable
+
+import java.io._
+
+/** FileSystem implementation using Node.js */
+trait NodeFileSystem extends FileSystem {
+
+ import NodeFileSystem.fs
+
+ type File = String
+
+ private def stats(f: String) = fs.statSync(f)
+
+ val DummyVersion: String = "DUMMY_FILE"
+
+ def isDirectory(f: String): Boolean =
+ stats(f).isDirectory().asInstanceOf[Boolean]
+
+ def isFile(f: String): Boolean =
+ stats(f).isFile().asInstanceOf[Boolean]
+
+ def isJSFile(f: String): Boolean =
+ isFile(f) && f.endsWith(".js")
+
+ def isIRFile(f: String): Boolean =
+ isFile(f) && f.endsWith(".sjsir")
+
+ def isJARFile(f: String): Boolean =
+ isFile(f) && f.endsWith(".jar")
+
+ def exists(f: String): Boolean =
+ fs.existsSync(f).asInstanceOf[Boolean]
+
+ def getName(f: String): String =
+ VirtualFile.nameFromPath(f)
+
+ def getAbsolutePath(f: String): String =
+ fs.realpathSync(f).asInstanceOf[String]
+
+ def getVersion(f: String): String =
+ stats(f).mtime.asInstanceOf[js.Date].getTime.toString
+
+ def listFiles(d: String): Traversable[String] = {
+ require(isDirectory(d))
+ val prefix = if (d.endsWith("/")) d else d + "/"
+
+ fs.readdirSync(d).asInstanceOf[js.Array[String]].toList.map(prefix + _)
+ }
+
+ def toJSFile(f: String): VirtualJSFile = new NodeVirtualJSFile(f)
+ def toIRFile(f: String): VirtualScalaJSIRFile = new NodeVirtualScalaJSIRFile(f)
+ def toReader(f: String): Reader =
+ new NodeVirtualTextFile(f).reader
+ def toInputStream(f: String): InputStream =
+ new NodeVirtualBinaryFile(f).inputStream
+
+}
+
+private object NodeFileSystem {
+ private val fs = js.Dynamic.global.require("fs")
+}
diff --git a/examples/scala-js/tools/js/src/main/scala/scala/scalajs/tools/io/NodeVirtualFiles.scala b/examples/scala-js/tools/js/src/main/scala/scala/scalajs/tools/io/NodeVirtualFiles.scala
new file mode 100644
index 0000000..6a0c3ee
--- /dev/null
+++ b/examples/scala-js/tools/js/src/main/scala/scala/scalajs/tools/io/NodeVirtualFiles.scala
@@ -0,0 +1,62 @@
+package scala.scalajs.tools.io
+
+import scala.scalajs.js
+import scala.scalajs.js.typedarray._
+
+import java.io._
+import java.net.URI
+
+class NodeVirtualFile(override val path: String) extends VirtualFile {
+ import NodeFS.fs
+
+ override def version: Option[String] = {
+ val stat = fs.statSync(path)
+ if (js.isUndefined(stat.mtime))
+ None
+ else
+ Some(stat.mtime.asInstanceOf[js.Date].getTime.toString)
+ }
+
+ override def exists: Boolean =
+ fs.existsSync(path).asInstanceOf[Boolean]
+
+ override def toURI: URI = {
+ val abspath = fs.realpathSync(path).asInstanceOf[String]
+ new URI("file", abspath, null)
+ }
+}
+
+class NodeVirtualTextFile(p: String) extends NodeVirtualFile(p)
+ with VirtualTextFile {
+ import NodeFS.fs
+
+ override def content: String = {
+ val options = js.Dynamic.literal(encoding = "UTF-8")
+ fs.readFileSync(path, options).asInstanceOf[String]
+ }
+}
+
+class NodeVirtualBinaryFile(p: String) extends NodeVirtualFile(p)
+ with VirtualBinaryFile {
+ import NodeFS.fs
+
+ private def buf: ArrayBuffer =
+ new Uint8Array(fs.readFileSync(path).asInstanceOf[js.Array[Int]]).buffer
+
+ override def content: Array[Byte] = new Int8Array(buf).toArray
+ override def inputStream: InputStream = new ArrayBufferInputStream(buf)
+}
+
+class NodeVirtualJSFile(p: String) extends NodeVirtualTextFile(p)
+ with VirtualJSFile {
+
+ /** Always returns None. We can't read them on JS anyway */
+ override def sourceMap: Option[String] = None
+}
+
+class NodeVirtualScalaJSIRFile(p: String)
+ extends NodeVirtualBinaryFile(p) with VirtualSerializedScalaJSIRFile
+
+private[io] object NodeFS {
+ val fs = js.Dynamic.global.require("fs")
+}
diff --git a/examples/scala-js/tools/js/src/main/scala/scala/scalajs/tools/json/Impl.scala b/examples/scala-js/tools/js/src/main/scala/scala/scalajs/tools/json/Impl.scala
new file mode 100644
index 0000000..89c7255
--- /dev/null
+++ b/examples/scala-js/tools/js/src/main/scala/scala/scalajs/tools/json/Impl.scala
@@ -0,0 +1,36 @@
+package scala.scalajs.tools.json
+
+import scala.scalajs.tools.io.IO
+
+import scala.scalajs.js
+
+import java.io.{Writer, Reader}
+
+private[json] object Impl extends AbstractJSONImpl {
+
+ type Repr = js.Any
+
+ def fromString(x: String): Repr = x
+ def fromNumber(x: Number): Repr = x.doubleValue()
+ def fromBoolean(x: Boolean): Repr = x
+ def fromList(x: List[Repr]): Repr = js.Array(x: _*)
+ def fromMap(x: Map[String, Repr]): Repr = js.Dictionary(x.toSeq: _*)
+
+ def toString(x: Repr): String = x.asInstanceOf[String]
+ def toNumber(x: Repr): Number = x.asInstanceOf[Double]
+ def toBoolean(x: Repr): Boolean = x.asInstanceOf[Boolean]
+ def toList(x: Repr): List[Repr] = x.asInstanceOf[js.Array[Repr]].toList
+ def toMap(x: Repr): Map[String, Repr] =
+ x.asInstanceOf[js.Dictionary[Repr]].toMap
+
+ def serialize(x: Repr): String = js.JSON.stringify(x)
+
+ def serialize(x: Repr, writer: Writer): Unit =
+ writer.write(serialize(x))
+
+ def deserialize(str: String): Repr = js.JSON.parse(str)
+
+ def deserialize(reader: Reader): Repr =
+ deserialize(IO.readReaderToString(reader))
+
+}
diff --git a/examples/scala-js/tools/js/src/test/scala/scala/scalajs/tools/js/test/JasmineReporter.scala b/examples/scala-js/tools/js/src/test/scala/scala/scalajs/tools/js/test/JasmineReporter.scala
new file mode 100644
index 0000000..7b63871
--- /dev/null
+++ b/examples/scala-js/tools/js/src/test/scala/scala/scalajs/tools/js/test/JasmineReporter.scala
@@ -0,0 +1,71 @@
+package scala.scalajs.tools.js.test
+
+import org.scalajs.jasmine.Suite
+
+import org.scalajs.jasminetest._
+
+import scala.scalajs.js.annotation.JSExport
+
+import scala.scalajs.testbridge._
+
+object JSConsoleTestOutput extends TestOutput {
+
+ type Color = Null
+
+ val errorColor: Color = null
+ val successColor: Color = null
+ val infoColor: Color = null
+
+ def color(message: String, color: Color): String = message
+
+ def error(message: String, stack: Array[StackTraceElement]): Unit =
+ withStack(message, stack)
+
+ def error(message: String): Unit = println(message)
+
+ def failure(message: String, stack: Array[StackTraceElement]): Unit =
+ withStack(message, stack)
+
+ def failure(message: String): Unit = println(message)
+ def succeeded(message: String): Unit = println(message)
+ def skipped(message: String): Unit = println(message)
+ def pending(message: String): Unit = println(message)
+ def ignored(message: String): Unit = println(message)
+ def canceled(message: String): Unit = println(message)
+
+ object log extends TestOutputLog {
+ def info(message: String): Unit = println(message)
+ def warn(message: String): Unit = println(message)
+ def error(message: String): Unit = println(message)
+ }
+
+ private def withStack(message: String, stack: Array[StackTraceElement]) =
+ println(message + stack.mkString("\n", "\n", ""))
+
+}
+
+@JSExport("scalajs.JasmineConsoleReporter")
+class JasmineConsoleReporter(throwOnFail: Boolean = false)
+ extends JasmineTestReporter(JSConsoleTestOutput) {
+
+ private var suiteFails: Int = 0
+ private var suiteCount: Int = 0
+
+ override def reportSuiteResults(suite: Suite): Unit = {
+ super.reportSuiteResults(suite)
+ if (suite.results().failedCount > 0)
+ suiteFails += 1
+ suiteCount += 1
+ }
+
+ override def reportRunnerResults(): Unit = {
+ super.reportRunnerResults()
+ val failed = suiteFails > 0
+ val resStr = if (failed) "Failed" else "Passed"
+ println(s"$resStr: Total $suiteCount, Failed $suiteFails")
+
+ if (failed && throwOnFail)
+ sys.error("Jasmine test suite failed.")
+ }
+
+}
diff --git a/examples/scala-js/tools/js/src/test/scala/scala/scalajs/tools/js/test/QuickLinker.scala b/examples/scala-js/tools/js/src/test/scala/scala/scalajs/tools/js/test/QuickLinker.scala
new file mode 100644
index 0000000..580c4c5
--- /dev/null
+++ b/examples/scala-js/tools/js/src/test/scala/scala/scalajs/tools/js/test/QuickLinker.scala
@@ -0,0 +1,37 @@
+package scala.scalajs.tools.js.test
+
+import scala.scalajs.tools.sem.Semantics
+import scala.scalajs.tools.io._
+import scala.scalajs.tools.logging._
+import scala.scalajs.tools.classpath._
+import scala.scalajs.tools.classpath.builder._
+import scala.scalajs.tools.optimizer._
+
+import scala.scalajs.js.annotation.JSExport
+
+@JSExport("scalajs.QuickLinker")
+object QuickLinker {
+
+ /** Link a Scala.js application on Node.js */
+ @JSExport
+ def linkNode(cpEntries: String*): String = {
+ val builder = new AbstractPartialClasspathBuilder with NodeFileSystem
+ val cp = builder.build(cpEntries.toList)
+
+ val complete = cp.resolve()
+
+ val optimizer = new ScalaJSOptimizer(Semantics.Defaults.optimized)
+
+ val out = WritableMemVirtualJSFile("out.js")
+
+ import ScalaJSOptimizer._
+ val optimized = optimizer.optimizeCP(
+ Inputs(complete),
+ OutputConfig(out),
+ new ScalaConsoleLogger
+ )
+
+ out.content
+ }
+
+}
diff --git a/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/classpath/builder/JarLibClasspathBuilder.scala b/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/classpath/builder/JarLibClasspathBuilder.scala
new file mode 100644
index 0000000..5bc488c
--- /dev/null
+++ b/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/classpath/builder/JarLibClasspathBuilder.scala
@@ -0,0 +1,13 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.classpath.builder
+
+class JarLibClasspathBuilder extends AbstractJarLibClasspathBuilder
+ with PhysicalFileSystem
diff --git a/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/classpath/builder/PartialClasspathBuilder.scala b/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/classpath/builder/PartialClasspathBuilder.scala
new file mode 100644
index 0000000..626c74c
--- /dev/null
+++ b/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/classpath/builder/PartialClasspathBuilder.scala
@@ -0,0 +1,38 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.classpath.builder
+
+import scala.scalajs.tools.classpath._
+import scala.collection.immutable.Seq
+
+/**
+ * Allows to create a PartialClasspathBuilder from a (filesystem) classpath
+ *
+ * Rules for classpath reading:
+ * - IR goes to scalaJSIR
+ * - Descends into JARs
+ * - Entries stay in order of ‘cp‘, IR remains unordered
+ * - Earlier IR entries shadow later IR entries with the same relative path
+ * - JS goes to availableLibs (earlier libs take precedence)
+ * - JS_DEPENDENCIES are added to dependencies
+ */
+class PartialClasspathBuilder extends AbstractPartialClasspathBuilder
+ with PhysicalFileSystem
+
+object PartialClasspathBuilder {
+ /** Convenience method. The same as
+ *
+ * {{{
+ * (new PartialClasspathBuilder).build(cp)
+ * }}}
+ */
+ def build(cp: Seq[java.io.File]): PartialClasspath =
+ (new PartialClasspathBuilder).build(cp)
+}
diff --git a/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/classpath/builder/PhysicalFileSystem.scala b/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/classpath/builder/PhysicalFileSystem.scala
new file mode 100644
index 0000000..a0dd7a5
--- /dev/null
+++ b/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/classpath/builder/PhysicalFileSystem.scala
@@ -0,0 +1,41 @@
+package scala.scalajs.tools.classpath.builder
+
+import scala.scalajs.tools.io._
+
+import scala.collection.immutable.Traversable
+
+import java.io._
+
+/** FileSystem implementation using java.io._ */
+trait PhysicalFileSystem extends FileSystem {
+
+ type File = java.io.File
+
+ val DummyVersion: String = "DUMMY_FILE"
+
+ def isDirectory(f: File): Boolean = f.isDirectory
+ def isFile(f: File): Boolean = f.isFile
+ def isJSFile(f: File): Boolean = f.isFile && f.getName.endsWith(".js")
+ def isIRFile(f: File): Boolean = f.isFile && f.getName.endsWith(".sjsir")
+ def isJARFile(f: File): Boolean = f.isFile && f.getName.endsWith(".jar")
+ def exists(f: File): Boolean = f.exists
+
+ def getName(f: File): String = f.getName
+ def getAbsolutePath(f: File): String = f.getAbsolutePath
+ def getVersion(f: File): String = f.lastModified.toString
+
+ def listFiles(d: File): Traversable[File] = {
+ require(d.isDirectory)
+ Option(d.listFiles).map(_.toList).getOrElse {
+ throw new IOException(s"Couldn't list files in $d")
+ }
+ }
+
+ def toJSFile(f: File): VirtualJSFile = FileVirtualJSFile(f)
+ def toIRFile(f: File): VirtualScalaJSIRFile = FileVirtualScalaJSIRFile(f)
+ def toReader(f: File): Reader =
+ new BufferedReader(new FileReader(f))
+ def toInputStream(f: File): InputStream =
+ new BufferedInputStream(new FileInputStream(f))
+
+}
diff --git a/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/io/FileVirtualFiles.scala b/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/io/FileVirtualFiles.scala
new file mode 100644
index 0000000..da29225
--- /dev/null
+++ b/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/io/FileVirtualFiles.scala
@@ -0,0 +1,157 @@
+package scala.scalajs.tools.io
+
+import scala.annotation.tailrec
+
+import java.io._
+import java.net.URI
+
+/** A [[VirtualFile]] implemented by an actual file on the file system. */
+class FileVirtualFile(val file: File) extends VirtualFile {
+ import FileVirtualFile._
+
+ override def path = file.getPath
+
+ override def name = file.getName
+
+ override def version: Option[String] = {
+ if (!file.isFile) None
+ else Some(file.lastModified.toString)
+ }
+
+ override def exists: Boolean = file.exists
+
+ override def toURI: URI = file.toURI
+}
+
+object FileVirtualFile extends (File => FileVirtualFile) {
+ def apply(f: File): FileVirtualFile =
+ new FileVirtualFile(f)
+
+ /** Tests whether the given file has the specified extension.
+ * Extension contain the '.', so a typical value for `ext` would be ".js".
+ * The comparison is case-sensitive.
+ */
+ def hasExtension(file: File, ext: String): Boolean =
+ file.getName.endsWith(ext)
+
+ /** Returns a new file with the same parent as the given file but a different
+ * name.
+ */
+ def withName(file: File, newName: String): File =
+ new File(file.getParentFile(), newName)
+
+ /** Returns a new file with the same path as the given file but a different
+ * extension.
+ * Extension contain the '.', so a typical value for `ext` would be ".js".
+ * Precondition: hasExtension(file, oldExt)
+ */
+ def withExtension(file: File, oldExt: String, newExt: String): File = {
+ require(hasExtension(file, oldExt),
+ s"File $file does not have extension '$oldExt'")
+ withName(file, file.getName.stripSuffix(oldExt) + newExt)
+ }
+}
+
+/** A [[VirtualTextFile]] implemented by an actual file on the file system. */
+class FileVirtualTextFile(f: File) extends FileVirtualFile(f)
+ with VirtualTextFile {
+ import FileVirtualTextFile._
+
+ override def content: String = readFileToString(file)
+ override def reader: Reader = new BufferedReader(new FileReader(f))
+}
+
+object FileVirtualTextFile extends (File => FileVirtualTextFile) {
+ def apply(f: File): FileVirtualTextFile =
+ new FileVirtualTextFile(f)
+
+ /** Reads the entire content of a file as a UTF-8 string. */
+ def readFileToString(file: File): String = {
+ val stream = new FileInputStream(file)
+ try IO.readInputStreamToString(stream)
+ finally stream.close()
+ }
+}
+
+trait WritableFileVirtualTextFile extends FileVirtualTextFile
+ with WritableVirtualTextFile {
+ override def contentWriter: Writer = {
+ new BufferedWriter(new OutputStreamWriter(
+ new FileOutputStream(file), "UTF-8"))
+ }
+}
+
+object WritableFileVirtualTextFile {
+ def apply(f: File): WritableFileVirtualTextFile =
+ new FileVirtualTextFile(f) with WritableFileVirtualTextFile
+}
+
+/** A [[VirtualBinaryFile]] implemented by an actual file on the file system. */
+class FileVirtualBinaryFile(f: File) extends FileVirtualFile(f)
+ with VirtualBinaryFile {
+ import FileVirtualBinaryFile._
+
+ override def inputStream: InputStream =
+ new BufferedInputStream(new FileInputStream(file))
+
+ override def content: Array[Byte] =
+ readFileToByteArray(file)
+}
+
+object FileVirtualBinaryFile extends (File => FileVirtualBinaryFile) {
+ def apply(f: File): FileVirtualBinaryFile =
+ new FileVirtualBinaryFile(f)
+
+ /** Reads the entire content of a file as byte array. */
+ def readFileToByteArray(file: File): Array[Byte] = {
+ val stream = new FileInputStream(file)
+ try IO.readInputStreamToByteArray(stream)
+ finally stream.close()
+ }
+}
+
+class FileVirtualJSFile(f: File) extends FileVirtualTextFile(f)
+ with VirtualJSFile {
+ import FileVirtualFile._
+ import FileVirtualTextFile._
+
+ val sourceMapFile: File = withExtension(file, ".js", ".js.map")
+
+ override def sourceMap: Option[String] = {
+ if (sourceMapFile.exists) Some(readFileToString(sourceMapFile))
+ else None
+ }
+}
+
+object FileVirtualJSFile extends (File => FileVirtualJSFile) {
+ def apply(f: File): FileVirtualJSFile =
+ new FileVirtualJSFile(f)
+}
+
+trait WritableFileVirtualJSFile extends FileVirtualJSFile
+ with WritableFileVirtualTextFile
+ with WritableVirtualJSFile {
+
+ override def sourceMapWriter: Writer = {
+ new BufferedWriter(new OutputStreamWriter(
+ new FileOutputStream(sourceMapFile), "UTF-8"))
+ }
+}
+
+object WritableFileVirtualJSFile {
+ def apply(f: File): WritableFileVirtualJSFile =
+ new FileVirtualJSFile(f) with WritableFileVirtualJSFile
+}
+
+class FileVirtualScalaJSIRFile(f: File)
+ extends FileVirtualBinaryFile(f) with VirtualSerializedScalaJSIRFile
+
+object FileVirtualScalaJSIRFile extends (File => FileVirtualScalaJSIRFile) {
+ import FileVirtualFile._
+
+ def apply(f: File): FileVirtualScalaJSIRFile =
+ new FileVirtualScalaJSIRFile(f)
+
+ def isScalaJSIRFile(file: File): Boolean =
+ hasExtension(file, ".sjsir")
+}
diff --git a/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/json/Impl.scala b/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/json/Impl.scala
new file mode 100644
index 0000000..ea847e3
--- /dev/null
+++ b/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/json/Impl.scala
@@ -0,0 +1,38 @@
+package scala.scalajs.tools.json
+
+import org.json.simple.JSONValue
+
+import scala.collection.JavaConverters._
+
+import java.io.{Writer, Reader}
+
+private[json] object Impl extends AbstractJSONImpl {
+
+ type Repr = Object
+
+ def fromString(x: String): Repr = x
+ def fromNumber(x: Number): Repr = x
+ def fromBoolean(x: Boolean): Repr = java.lang.Boolean.valueOf(x)
+ def fromList(x: List[Repr]): Repr = x.asJava
+ def fromMap(x: Map[String, Repr]): Repr = x.asJava
+
+ def toString(x: Repr): String = x.asInstanceOf[String]
+ def toNumber(x: Repr): Number = x.asInstanceOf[Number]
+ def toBoolean(x: Repr): Boolean =
+ x.asInstanceOf[java.lang.Boolean].booleanValue()
+ def toList(x: Repr): List[Repr] =
+ x.asInstanceOf[java.util.List[Repr]].asScala.toList
+ def toMap(x: Repr): Map[String, Repr] =
+ x.asInstanceOf[java.util.Map[String, Repr]].asScala.toMap
+
+ def serialize(x: Repr): String =
+ JSONValue.toJSONString(x)
+
+ def serialize(x: Repr, writer: Writer): Unit =
+ JSONValue.writeJSONString(x, writer)
+
+ def deserialize(str: String): Repr = JSONValue.parse(str)
+
+ def deserialize(reader: Reader): Repr = JSONValue.parse(reader)
+
+}
diff --git a/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/optimizer/ClosureAstBuilder.scala b/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/optimizer/ClosureAstBuilder.scala
new file mode 100644
index 0000000..8d2eb2b
--- /dev/null
+++ b/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/optimizer/ClosureAstBuilder.scala
@@ -0,0 +1,47 @@
+package scala.scalajs.tools.optimizer
+
+import scala.scalajs.ir
+import ir.Position.NoPosition
+
+import scala.scalajs.tools.javascript.Trees.Tree
+
+import com.google.javascript.rhino._
+import com.google.javascript.rhino.jstype.{StaticSourceFile, SimpleSourceFile}
+import com.google.javascript.jscomp._
+
+import scala.collection.mutable
+
+import java.net.URI
+
+class ClosureAstBuilder(
+ relativizeBaseURI: Option[URI] = None) extends JSTreeBuilder {
+
+ private val transformer = new ClosureAstTransformer(relativizeBaseURI)
+ private val treeBuf = mutable.ListBuffer.empty[Node]
+
+ def addJSTree(tree: Tree): Unit =
+ treeBuf += transformer.transformStat(tree)(NoPosition)
+
+ lazy val closureAST: SourceAst = {
+ val root = transformer.setNodePosition(IR.script(treeBuf: _*), NoPosition)
+
+ treeBuf.clear()
+
+ new ClosureAstBuilder.ScalaJSSourceAst(root)
+ }
+
+}
+
+object ClosureAstBuilder {
+ // Dummy Source AST class
+
+ class ScalaJSSourceAst(root: Node) extends SourceAst {
+ def getAstRoot(compiler: AbstractCompiler): Node = root
+ def clearAst(): Unit = () // Just for GC. Nonsensical here.
+ def getInputId(): InputId = root.getInputId()
+ def getSourceFile(): SourceFile =
+ root.getStaticSourceFile().asInstanceOf[SourceFile]
+ def setSourceFile(file: SourceFile): Unit =
+ if (getSourceFile() ne file) throw new IllegalStateException
+ }
+}
diff --git a/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/optimizer/ClosureAstTransformer.scala b/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/optimizer/ClosureAstTransformer.scala
new file mode 100644
index 0000000..af22501
--- /dev/null
+++ b/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/optimizer/ClosureAstTransformer.scala
@@ -0,0 +1,397 @@
+package scala.scalajs.tools.optimizer
+
+import scala.scalajs.ir
+import ir.Position
+import ir.Position.NoPosition
+
+import scala.scalajs.tools.javascript.Trees._
+
+import com.google.javascript.rhino._
+import com.google.javascript.jscomp._
+
+import scala.collection.mutable
+import scala.annotation.tailrec
+
+import java.net.URI
+
+class ClosureAstTransformer(val relativizeBaseURI: Option[URI] = None) {
+
+ private val inputId = new InputId("Scala.js IR")
+
+ private val dummySourceName = new java.net.URI("virtualfile:scala.js-ir")
+
+ def transformStat(tree: Tree)(implicit parentPos: Position): Node =
+ innerTransformStat(tree, tree.pos orElse parentPos)
+
+ private def innerTransformStat(tree: Tree, pos_in: Position): Node = {
+ implicit val pos = pos_in
+
+ wrapTransform(tree) {
+ case VarDef(ident, _, EmptyTree) =>
+ new Node(Token.VAR, transformName(ident))
+ case VarDef(ident, _, rhs) =>
+ val node = transformName(ident)
+ node.addChildToFront(transformExpr(rhs))
+ new Node(Token.VAR, node)
+ case Skip() =>
+ new Node(Token.EMPTY)
+ case Block(stats) =>
+ transformBlock(stats, pos)
+ case Labeled(label, body) =>
+ new Node(Token.LABEL, transformLabel(label), transformBlock(body))
+ case Return(EmptyTree) =>
+ new Node(Token.RETURN)
+ case Return(expr) =>
+ new Node(Token.RETURN, transformExpr(expr))
+ case If(cond, thenp, Skip()) =>
+ new Node(Token.IF, transformExpr(cond), transformBlock(thenp))
+ case If(cond, thenp, elsep) =>
+ new Node(Token.IF, transformExpr(cond),
+ transformBlock(thenp), transformBlock(elsep))
+ case While(cond, body, None) =>
+ new Node(Token.WHILE, transformExpr(cond), transformBlock(body))
+ case While(cond, body, Some(label)) =>
+ val whileNode =
+ new Node(Token.WHILE, transformExpr(cond), transformBlock(body))
+ new Node(Token.LABEL, transformLabel(label),
+ setNodePosition(whileNode, pos))
+ case DoWhile(body, cond, None) =>
+ new Node(Token.DO, transformBlock(body), transformExpr(cond))
+ case DoWhile(body, cond, Some(label)) =>
+ val doNode =
+ new Node(Token.DO, transformBlock(body), transformExpr(cond))
+ new Node(Token.LABEL, transformLabel(label),
+ setNodePosition(doNode, pos))
+ case Try(block, errVar, handler, EmptyTree) =>
+ val catchPos = handler.pos orElse pos
+ val catchNode =
+ new Node(Token.CATCH, transformName(errVar), transformBlock(handler))
+ val blockNode =
+ new Node(Token.BLOCK, setNodePosition(catchNode, catchPos))
+ new Node(Token.TRY, transformBlock(block),
+ setNodePosition(blockNode, catchPos))
+ case Try(block, _, EmptyTree, finalizer) =>
+ val blockNode = setNodePosition(new Node(Token.BLOCK), pos)
+ new Node(Token.TRY, transformBlock(block), blockNode,
+ transformBlock(finalizer))
+ case Try(block, errVar, handler, finalizer) =>
+ val catchPos = handler.pos orElse pos
+ val catchNode =
+ new Node(Token.CATCH, transformName(errVar), transformBlock(handler))
+ val blockNode =
+ new Node(Token.BLOCK, setNodePosition(catchNode, catchPos))
+ new Node(Token.TRY, transformBlock(block),
+ setNodePosition(blockNode, catchPos), transformBlock(finalizer))
+ case Throw(expr) =>
+ new Node(Token.THROW, transformExpr(expr))
+ case Break(None) =>
+ new Node(Token.BREAK)
+ case Break(Some(label)) =>
+ new Node(Token.BREAK, transformLabel(label))
+ case Continue(None) =>
+ new Node(Token.CONTINUE)
+ case Continue(Some(label)) =>
+ new Node(Token.CONTINUE, transformLabel(label))
+
+ case Switch(selector, cases, default) =>
+ val switchNode = new Node(Token.SWITCH, transformExpr(selector))
+
+ for ((expr, body) <- cases) {
+ val bodyNode = transformBlock(body)
+ bodyNode.putBooleanProp(Node.SYNTHETIC_BLOCK_PROP, true)
+ val caseNode = new Node(Token.CASE, transformExpr(expr), bodyNode)
+ switchNode.addChildToBack(
+ setNodePosition(caseNode, expr.pos orElse pos))
+ }
+
+ if (default != EmptyTree) {
+ val bodyNode = transformBlock(default)
+ bodyNode.putBooleanProp(Node.SYNTHETIC_BLOCK_PROP, true)
+ val caseNode = new Node(Token.DEFAULT_CASE, bodyNode)
+ switchNode.addChildToBack(
+ setNodePosition(caseNode, default.pos orElse pos))
+ }
+
+ switchNode
+
+ case Debugger() =>
+ new Node(Token.DEBUGGER)
+ case _ =>
+ // We just assume it is an expression
+ new Node(Token.EXPR_RESULT, transformExpr(tree))
+ }
+ }
+
+ def transformExpr(tree: Tree)(implicit parentPos: Position): Node =
+ innerTransformExpr(tree, tree.pos orElse parentPos)
+
+ private def innerTransformExpr(tree: Tree, pos_in: Position): Node = {
+ implicit val pos = pos_in
+
+ wrapTransform(tree) {
+ case Block(exprs) =>
+ exprs.map(transformExpr).reduceRight { (expr1, expr2) =>
+ setNodePosition(new Node(Token.COMMA, expr1, expr2), pos)
+ }
+ case If(cond, thenp, elsep) =>
+ new Node(Token.HOOK, transformExpr(cond),
+ transformExpr(thenp), transformExpr(elsep))
+ case Assign(lhs, rhs) =>
+ new Node(Token.ASSIGN, transformExpr(lhs), transformExpr(rhs))
+ case New(ctor, args) =>
+ val node = new Node(Token.NEW, transformExpr(ctor))
+ args.foreach(arg => node.addChildToBack(transformExpr(arg)))
+ node
+ case DotSelect(qualifier, item) =>
+ new Node(Token.GETPROP, transformExpr(qualifier), transformString(item))
+ case BracketSelect(qualifier, item) =>
+ new Node(Token.GETELEM, transformExpr(qualifier), transformExpr(item))
+
+ case Apply(fun, args) =>
+ val node = new Node(Token.CALL, transformExpr(fun))
+ args.foreach(arg => node.addChildToBack(transformExpr(arg)))
+
+ // Closure needs to know (from the parser), if the call has a bound
+ // `this` or not. Since JSDesugar inserts protects calls if necessary,
+ // it is sufficient to check if we have a select as target
+ if (!fun.isInstanceOf[DotSelect] &&
+ !fun.isInstanceOf[BracketSelect])
+ node.putBooleanProp(Node.FREE_CALL, true)
+
+ node
+
+ case Delete(prop) =>
+ new Node(Token.DELPROP, transformExpr(prop))
+ case UnaryOp(op, lhs) =>
+ mkUnaryOp(op, transformExpr(lhs))
+ case BinaryOp(op, lhs, rhs) =>
+ mkBinaryOp(op, transformExpr(lhs), transformExpr(rhs))
+ case ArrayConstr(items) =>
+ val node = new Node(Token.ARRAYLIT)
+ items.foreach(i => node.addChildToBack(transformExpr(i)))
+ node
+
+ case ObjectConstr(fields) =>
+ val node = new Node(Token.OBJECTLIT)
+
+ for ((name, expr) <- fields) {
+ val fldNode = transformStringKey(name)
+ fldNode.addChildToBack(transformExpr(expr))
+ node.addChildToBack(fldNode)
+ }
+
+ node
+
+ case Undefined() =>
+ new Node(Token.VOID, setNodePosition(Node.newNumber(0.0), pos))
+ case Null() =>
+ new Node(Token.NULL)
+ case BooleanLiteral(value) =>
+ if (value) new Node(Token.TRUE) else new Node(Token.FALSE)
+ case IntLiteral(value) =>
+ Node.newNumber(value)
+ case DoubleLiteral(value) =>
+ Node.newNumber(value)
+ case StringLiteral(value) =>
+ Node.newString(value)
+ case VarRef(ident, _) =>
+ transformName(ident)
+ case This() =>
+ new Node(Token.THIS)
+
+ case Function(args, body) =>
+ // Note that a Function may also be a statement (when it is named),
+ // but Scala.js does not have such an IR node
+ val paramList = new Node(Token.PARAM_LIST)
+ args.foreach(arg => paramList.addChildToBack(transformParam(arg)))
+
+ val emptyName = setNodePosition(Node.newString(Token.NAME, ""), pos)
+
+ new Node(Token.FUNCTION, emptyName, paramList, transformBlock(body))
+
+ case _ =>
+ throw new TransformException(s"Unknown tree of class ${tree.getClass()}")
+ }
+ }
+
+ def transformParam(param: ParamDef)(implicit parentPos: Position): Node =
+ transformName(param.name)
+
+ def transformName(ident: Ident)(implicit parentPos: Position): Node =
+ setNodePosition(Node.newString(Token.NAME, ident.name),
+ ident.pos orElse parentPos)
+
+ def transformLabel(ident: Ident)(implicit parentPos: Position): Node =
+ setNodePosition(Node.newString(Token.LABEL_NAME, ident.name),
+ ident.pos orElse parentPos)
+
+ def transformString(pName: PropertyName)(implicit parentPos: Position): Node =
+ setNodePosition(Node.newString(pName.name), pName.pos orElse parentPos)
+
+ def transformStringKey(pName: PropertyName)(
+ implicit parentPos: Position): Node = {
+ val node = Node.newString(Token.STRING_KEY, pName.name)
+
+ if (pName.isInstanceOf[StringLiteral])
+ node.setQuotedString()
+
+ setNodePosition(node, pName.pos orElse parentPos)
+ }
+
+ def transformBlock(tree: Tree)(implicit parentPos: Position): Node = {
+ val pos = if (tree.pos.isDefined) tree.pos else parentPos
+ wrapTransform(tree) {
+ case Block(stats) =>
+ transformBlock(stats, pos)
+ case tree =>
+ transformBlock(List(tree), pos)
+ } (pos)
+ }
+
+ def transformBlock(stats: List[Tree], blockPos: Position): Node = {
+ @inline def ctorDoc(node: Node) = {
+ val b = new JSDocInfoBuilder(false)
+ b.recordConstructor()
+ b.build(node)
+ }
+
+ val block = new Node(Token.BLOCK)
+
+ // The Rhino IR attaches DocComments to the following nodes (rather than
+ // having individual nodes). We preprocess these here.
+ @tailrec
+ def loop(ts: List[Tree], nextIsCtor: Boolean = false): Unit = ts match {
+ case DocComment(text) :: tss if text.startsWith("@constructor") =>
+ loop(tss, nextIsCtor = true)
+ case DocComment(text) :: tss =>
+ loop(tss)
+ case t :: tss =>
+ val node = transformStat(t)(blockPos)
+ if (nextIsCtor) {
+ // The @constructor must be propagated through an ExprResult node
+ val trg =
+ if (node.isExprResult()) node.getChildAtIndex(0)
+ else node
+
+ trg.setJSDocInfo(ctorDoc(trg))
+ }
+
+ block.addChildToBack(node)
+
+ loop(tss)
+ case Nil =>
+ }
+
+ loop(stats)
+
+ block
+ }
+
+ @inline
+ private def wrapTransform(tree: Tree)(body: Tree => Node)(
+ implicit pos: Position): Node = {
+ try {
+ setNodePosition(body(tree), pos)
+ } catch {
+ case e: TransformException =>
+ throw e // pass through
+ case e: RuntimeException =>
+ throw new TransformException(tree, e)
+ }
+ }
+
+ def setNodePosition(node: Node, pos: ir.Position): node.type = {
+ if (pos != ir.Position.NoPosition) {
+ attachSourceFile(node, pos.source)
+ node.setLineno(pos.line+1)
+ node.setCharno(pos.column)
+ } else {
+ attachSourceFile(node, dummySourceName)
+ }
+ node
+ }
+
+ private def attachSourceFile(node: Node, source: URI): node.type = {
+ val str = sourceUriToString(source)
+
+ node.setInputId(inputId)
+ node.setStaticSourceFile(new SourceFile(str))
+
+ node
+ }
+
+ private def sourceUriToString(uri: URI): String = {
+ val relURI = relativizeBaseURI.fold(uri)(ir.Utils.relativize(_, uri))
+ ir.Utils.fixFileURI(relURI).toASCIIString
+ }
+
+ // Helpers for IR
+ @inline
+ private def mkUnaryOp(op: String, lhs: Node): Node = {
+ val tok = op match {
+ case "!" => Token.NOT
+ case "~" => Token.BITNOT
+ case "+" => Token.POS
+ case "-" => Token.NEG
+ case "typeof" => Token.TYPEOF
+ case _ => throw new TransformException(s"Unhandled unary op: $op")
+ }
+
+ new Node(tok, lhs)
+ }
+
+ @inline
+ private def mkBinaryOp(op: String, lhs: Node, rhs: Node): Node = {
+ val tok = op match {
+ case "|" => Token.BITOR
+ case "&" => Token.BITAND
+ case "^" => Token.BITXOR
+ case "==" => Token.EQ
+ case "!=" => Token.NE
+ case "<" => Token.LT
+ case "<=" => Token.LE
+ case ">" => Token.GT
+ case ">=" => Token.GE
+ case "<<" => Token.LSH
+ case ">>" => Token.RSH
+ case ">>>" => Token.URSH
+ case "+" => Token.ADD
+ case "-" => Token.SUB
+ case "*" => Token.MUL
+ case "/" => Token.DIV
+ case "%" => Token.MOD
+ case "===" => Token.SHEQ
+ case "!==" => Token.SHNE
+ case "in" => Token.IN
+ case "instanceof" => Token.INSTANCEOF
+ case "||" => Token.OR
+ case "&&" => Token.AND
+ case _ =>
+ throw new TransformException(s"Unhandled binary op: $op")
+ }
+
+ new Node(tok, lhs, rhs)
+ }
+
+ // Exception wrapper in transforms
+
+ class TransformException private (msg: String, e: Throwable)
+ extends RuntimeException(msg, e) {
+
+ def this(tree: Tree, e: Throwable) =
+ this(TransformException.mkMsg(tree), e)
+
+ def this(msg: String) = this(msg, null)
+ }
+
+ object TransformException {
+ import ir.Printers._
+ import java.io._
+
+ private def mkMsg(tree: Tree): String = {
+ "Exception while translating Scala.js JS tree to GCC IR at tree:\n" +
+ tree.show
+ }
+ }
+
+}
diff --git a/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/optimizer/ConcurrencyUtils.scala b/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/optimizer/ConcurrencyUtils.scala
new file mode 100644
index 0000000..471ed65
--- /dev/null
+++ b/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/optimizer/ConcurrencyUtils.scala
@@ -0,0 +1,74 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.optimizer
+
+import scala.annotation.tailrec
+
+import scala.collection.concurrent.TrieMap
+
+import java.util.concurrent.atomic._
+
+private[optimizer] object ConcurrencyUtils {
+
+ /** An atomic accumulator supports adding single elements and retrieving and
+ * deleting all contained elements */
+ type AtomicAcc[T] = AtomicReference[List[T]]
+
+ object AtomicAcc {
+ @inline final def empty[T]: AtomicAcc[T] =
+ new AtomicReference[List[T]](Nil)
+ @inline final def apply[T](l: List[T]): AtomicAcc[T] =
+ new AtomicReference(l)
+ }
+
+ implicit class AtomicAccOps[T](val acc: AtomicAcc[T]) extends AnyVal {
+ @inline final def size: Int = acc.get.size
+
+ @inline
+ final def +=(x: T): Unit = AtomicAccOps.append(acc, x)
+
+ @inline
+ final def removeAll(): List[T] = AtomicAccOps.removeAll(acc)
+ }
+
+ object AtomicAccOps {
+ @inline
+ @tailrec
+ private final def append[T](acc: AtomicAcc[T], x: T): Boolean = {
+ val oldV = acc.get
+ val newV = x :: oldV
+ acc.compareAndSet(oldV, newV) || append(acc, x)
+ }
+
+ @inline
+ private final def removeAll[T](acc: AtomicAcc[T]): List[T] =
+ acc.getAndSet(Nil)
+ }
+
+ type TrieSet[T] = TrieMap[T, Null]
+
+ implicit class TrieSetOps[T](val set: TrieSet[T]) extends AnyVal {
+ @inline final def +=(x: T): Unit = set.put(x, null)
+ }
+
+ object TrieSet {
+ @inline final def empty[T]: TrieSet[T] = TrieMap.empty
+ }
+
+ implicit class TrieMapOps[K,V](val map: TrieMap[K,V]) extends AnyVal {
+ @inline final def getOrPut(k: K, default: => V): V = {
+ map.get(k).getOrElse {
+ val v = default
+ map.putIfAbsent(k, v).getOrElse(v)
+ }
+ }
+ }
+
+}
diff --git a/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/optimizer/LoggerErrorManager.scala b/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/optimizer/LoggerErrorManager.scala
new file mode 100644
index 0000000..d51dd7b
--- /dev/null
+++ b/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/optimizer/LoggerErrorManager.scala
@@ -0,0 +1,38 @@
+package scala.scalajs.tools.optimizer
+
+import com.google.javascript.jscomp.{ BasicErrorManager, CheckLevel, JSError }
+
+import scala.scalajs.tools.logging.Logger
+
+/** A Google Closure Error Manager that forwards to a tools.logging.Logger */
+class LoggerErrorManager(private val log: Logger) extends BasicErrorManager {
+
+ /** Ugly hack to disable FRACTIONAL_BITWISE_OPERAND warnings. Since its
+ * DiagnosticType (PeepholeFoldConstants.FRACTIONAL_BITWISE_OPERAND) is
+ * package private.
+ */
+ override def report(level: CheckLevel, error: JSError): Unit = {
+ if (error.getType.key == "JSC_FRACTIONAL_BITWISE_OPERAND")
+ super.report(CheckLevel.OFF, error)
+ else
+ super.report(level, error)
+ }
+
+ def println(level: CheckLevel, error: JSError): Unit = level match {
+ case CheckLevel.WARNING => log.warn (s"Closure: ${error}")
+ case CheckLevel.ERROR => log.error(s"Closure: ${error}")
+ case CheckLevel.OFF =>
+ }
+
+ protected def printSummary(): Unit = {
+ val msg = s"Closure: ${getErrorCount} error(s), ${getWarningCount} warning(s)"
+
+ if (getErrorCount > 0)
+ log.error(msg)
+ else if (getWarningCount > 0)
+ log.warn(msg)
+ else
+ log.info(msg)
+ }
+
+}
diff --git a/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/optimizer/ParIncOptimizer.scala b/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/optimizer/ParIncOptimizer.scala
new file mode 100644
index 0000000..422238e
--- /dev/null
+++ b/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/optimizer/ParIncOptimizer.scala
@@ -0,0 +1,188 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.optimizer
+
+import scala.collection.{GenTraversableOnce, GenIterable}
+import scala.collection.concurrent.TrieMap
+import scala.collection.parallel.mutable.{ParTrieMap, ParArray}
+
+import java.util.concurrent.atomic._
+
+import scala.scalajs.tools.sem.Semantics
+
+import ConcurrencyUtils._
+
+class ParIncOptimizer(semantics: Semantics) extends GenIncOptimizer(semantics) {
+
+ protected object CollOps extends GenIncOptimizer.AbsCollOps {
+ type Map[K, V] = TrieMap[K, V]
+ type ParMap[K, V] = ParTrieMap[K, V]
+ type AccMap[K, V] = TrieMap[K, AtomicAcc[V]]
+ type ParIterable[V] = ParArray[V]
+ type Addable[V] = AtomicAcc[V]
+
+ def emptyAccMap[K, V]: AccMap[K, V] = TrieMap.empty
+ def emptyMap[K, V]: Map[K, V] = TrieMap.empty
+ def emptyParMap[K, V]: ParMap[K, V] = ParTrieMap.empty
+ def emptyParIterable[V]: ParIterable[V] = ParArray.empty
+
+ // Operations on ParMap
+ def put[K, V](map: ParMap[K, V], k: K, v: V): Unit = map.put(k, v)
+ def remove[K, V](map: ParMap[K, V], k: K): Option[V] = map.remove(k)
+
+ def retain[K, V](map: ParMap[K, V])(p: (K, V) => Boolean): Unit = {
+ map.foreach { case (k, v) =>
+ if (!p(k, v))
+ map.remove(k)
+ }
+ }
+
+ // Operations on AccMap
+ def acc[K, V](map: AccMap[K, V], k: K, v: V): Unit =
+ map.getOrPut(k, AtomicAcc.empty) += v
+
+ def getAcc[K, V](map: AccMap[K, V], k: K): GenIterable[V] =
+ map.get(k).fold[Iterable[V]](Nil)(_.removeAll()).toParArray
+
+ def parFlatMapKeys[A, B](map: AccMap[A, _])(
+ f: A => GenTraversableOnce[B]): GenIterable[B] =
+ map.keys.flatMap(f).toParArray
+
+ // Operations on ParIterable
+ def prepAdd[V](it: ParIterable[V]): Addable[V] =
+ AtomicAcc(it.toList)
+
+ def add[V](addable: Addable[V], v: V): Unit =
+ addable += v
+
+ def finishAdd[V](addable: Addable[V]): ParIterable[V] =
+ addable.removeAll().toParArray
+ }
+
+ private val _interfaces = TrieMap.empty[String, InterfaceType]
+ protected def getInterface(encodedName: String): InterfaceType =
+ _interfaces.getOrPut(encodedName, new ParInterfaceType(encodedName))
+
+ private val methodsToProcess: AtomicAcc[MethodImpl] = AtomicAcc.empty
+ protected def scheduleMethod(method: MethodImpl): Unit =
+ methodsToProcess += method
+
+ protected def newMethodImpl(owner: MethodContainer,
+ encodedName: String): MethodImpl = new ParMethodImpl(owner, encodedName)
+
+ protected def processAllTaggedMethods(): Unit = {
+ val methods = methodsToProcess.removeAll().toParArray
+ logProcessingMethods(methods.count(!_.deleted))
+ for (method <- methods)
+ method.process()
+ }
+
+ private class ParInterfaceType(encName: String) extends InterfaceType(encName) {
+ private val ancestorsAskers = TrieSet.empty[MethodImpl]
+ private val dynamicCallers = TrieMap.empty[String, TrieSet[MethodImpl]]
+ private val staticCallers = TrieMap.empty[String, TrieSet[MethodImpl]]
+
+ private var _ancestors: List[String] = encodedName :: Nil
+
+ private val _instantiatedSubclasses: TrieSet[Class] = TrieSet.empty
+
+ /** PROCESS PASS ONLY. Concurrency safe except with
+ * [[addInstantiatedSubclass]] and [[removeInstantiatedSubclass]]
+ */
+ def instantiatedSubclasses: Iterable[Class] =
+ _instantiatedSubclasses.keys
+
+ /** UPDATE PASS ONLY. Concurrency safe except with
+ * [[instantiatedSubclasses]]
+ */
+ def addInstantiatedSubclass(x: Class): Unit =
+ _instantiatedSubclasses += x
+
+ /** UPDATE PASS ONLY. Concurrency safe except with
+ * [[instantiatedSubclasses]]
+ */
+ def removeInstantiatedSubclass(x: Class): Unit =
+ _instantiatedSubclasses -= x
+
+ /** PROCESS PASS ONLY. Concurrency safe except with [[ancestors_=]] */
+ def ancestors: List[String] = _ancestors
+
+ /** UPDATE PASS ONLY. Not concurrency safe. */
+ def ancestors_=(v: List[String]): Unit = {
+ if (v != _ancestors) {
+ _ancestors = v
+ ancestorsAskers.keysIterator.foreach(_.tag())
+ ancestorsAskers.clear()
+ }
+ }
+
+ /** PROCESS PASS ONLY. Concurrency safe except with [[ancestors_=]]. */
+ def registerAskAncestors(asker: MethodImpl): Unit =
+ ancestorsAskers += asker
+
+ /** PROCESS PASS ONLY. */
+ def registerDynamicCaller(methodName: String, caller: MethodImpl): Unit =
+ dynamicCallers.getOrPut(methodName, TrieSet.empty) += caller
+
+ /** PROCESS PASS ONLY. */
+ def registerStaticCaller(methodName: String, caller: MethodImpl): Unit =
+ staticCallers.getOrPut(methodName, TrieSet.empty) += caller
+
+ /** UPDATE PASS ONLY. */
+ def unregisterDependee(dependee: MethodImpl): Unit = {
+ ancestorsAskers -= dependee
+ dynamicCallers.valuesIterator.foreach(_ -= dependee)
+ staticCallers.valuesIterator.foreach(_ -= dependee)
+ }
+
+ /** UPDATE PASS ONLY. */
+ def tagDynamicCallersOf(methodName: String): Unit =
+ dynamicCallers.remove(methodName).foreach(_.keysIterator.foreach(_.tag()))
+
+ /** UPDATE PASS ONLY. */
+ def tagStaticCallersOf(methodName: String): Unit =
+ staticCallers.remove(methodName).foreach(_.keysIterator.foreach(_.tag()))
+ }
+
+ private class ParMethodImpl(owner: MethodContainer,
+ encodedName: String) extends MethodImpl(owner, encodedName) {
+
+ private val bodyAskers = TrieSet.empty[MethodImpl]
+
+ /** PROCESS PASS ONLY. */
+ def registerBodyAsker(asker: MethodImpl): Unit =
+ bodyAskers += asker
+
+ /** UPDATE PASS ONLY. */
+ def unregisterDependee(dependee: MethodImpl): Unit =
+ bodyAskers -= dependee
+
+ /** UPDATE PASS ONLY. */
+ def tagBodyAskers(): Unit = {
+ bodyAskers.keysIterator.foreach(_.tag())
+ bodyAskers.clear()
+ }
+
+ private val _registeredTo = AtomicAcc.empty[Unregisterable]
+ private val tagged = new AtomicBoolean(false)
+
+ protected def registeredTo(intf: Unregisterable): Unit =
+ _registeredTo += intf
+
+ protected def unregisterFromEverywhere(): Unit = {
+ _registeredTo.removeAll().foreach(_.unregisterDependee(this))
+ }
+
+ protected def protectTag(): Boolean = !tagged.getAndSet(true)
+ protected def resetTag(): Unit = tagged.set(false)
+
+ }
+
+}
diff --git a/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/optimizer/ScalaJSClosureOptimizer.scala b/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/optimizer/ScalaJSClosureOptimizer.scala
new file mode 100644
index 0000000..146b2b8
--- /dev/null
+++ b/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/optimizer/ScalaJSClosureOptimizer.scala
@@ -0,0 +1,216 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.optimizer
+
+import scala.scalajs.tools.sem.Semantics
+import scala.scalajs.tools.classpath._
+import scala.scalajs.tools.logging._
+import scala.scalajs.tools.io._
+import scala.scalajs.tools.corelib.CoreJSLibs
+
+import com.google.javascript.jscomp.{
+ SourceFile => ClosureSource,
+ Compiler => ClosureCompiler,
+ CompilerOptions => ClosureOptions,
+ _
+}
+import scala.collection.JavaConverters._
+import scala.collection.immutable.{Seq, Traversable}
+
+import java.net.URI
+
+/** Scala.js Closure optimizer: does advanced optimizations with Closure. */
+class ScalaJSClosureOptimizer(semantics: Semantics) {
+ import ScalaJSClosureOptimizer._
+
+ private def toClosureSource(file: VirtualJSFile) =
+ ClosureSource.fromReader(file.toURI.toString(), file.reader)
+
+ private def toClosureInput(file: VirtualJSFile) =
+ new CompilerInput(toClosureSource(file))
+
+ /** Fully optimizes an [[IRClasspath]] by asking the ScalaJSOptimizer to
+ * emit a closure AST and then compiling this AST directly
+ */
+ def optimizeCP(optimizer: ScalaJSOptimizer,
+ inputs: Inputs[ScalaJSOptimizer.Inputs[IRClasspath]],
+ outCfg: OutputConfig, logger: Logger): LinkedClasspath = {
+
+ val cp = inputs.input.input
+
+ CacheUtils.cached(cp.version, outCfg.output, outCfg.cache) {
+ logger.info(s"Full Optimizing ${outCfg.output.path}")
+
+ val irFastOptInput =
+ inputs.input.copy(input = inputs.input.input.scalaJSIR)
+ val irFullOptInput = inputs.copy(input = irFastOptInput)
+
+ optimizeIR(optimizer, irFullOptInput, outCfg, logger)
+ }
+
+ new LinkedClasspath(cp.jsLibs, outCfg.output, cp.requiresDOM, cp.version)
+ }
+
+ def optimizeIR(optimizer: ScalaJSOptimizer,
+ inputs: Inputs[ScalaJSOptimizer.Inputs[Traversable[VirtualScalaJSIRFile]]],
+ outCfg: OutputConfig, logger: Logger): Unit = {
+
+ // Build Closure IR via ScalaJSOptimizer
+ val builder = new ClosureAstBuilder(outCfg.relativizeSourceMapBase)
+
+ optimizer.optimizeIR(inputs.input, outCfg, builder, logger)
+
+ // Build a Closure JSModule which includes the core libs
+ val module = new JSModule("Scala.js")
+
+ for (lib <- CoreJSLibs.libs(semantics))
+ module.add(toClosureInput(lib))
+
+ val ast = builder.closureAST
+ module.add(new CompilerInput(ast, ast.getInputId(), false))
+
+ for (export <- inputs.additionalExports)
+ module.add(toClosureInput(export))
+
+ // Compile the module
+ val closureExterns =
+ (ScalaJSExternsFile +: inputs.additionalExterns).map(toClosureSource)
+
+ val options = closureOptions(outCfg)
+ val compiler = closureCompiler(logger)
+
+ val result = GenIncOptimizer.logTime(logger, "Closure Compiler pass") {
+ compiler.compileModules(
+ closureExterns.asJava, List(module).asJava, options)
+ }
+
+ GenIncOptimizer.logTime(logger, "Write Closure result") {
+ writeResult(result, compiler, outCfg.output)
+ }
+ }
+
+ private def writeResult(result: Result, compiler: ClosureCompiler,
+ output: WritableVirtualJSFile): Unit = {
+
+ val outputContent = if (result.errors.nonEmpty) ""
+ else "(function(){'use strict';" + compiler.toSource + "}).call(this);\n"
+
+ val sourceMap = Option(compiler.getSourceMap())
+
+ // Write optimized code
+ val w = output.contentWriter
+ try {
+ w.write(outputContent)
+ if (sourceMap.isDefined)
+ w.write("//# sourceMappingURL=" + output.name + ".map\n")
+ } finally w.close()
+
+ // Write source map (if available)
+ sourceMap.foreach { sm =>
+ val w = output.sourceMapWriter
+ try sm.appendTo(w, output.name)
+ finally w.close()
+ }
+ }
+
+ private def closureOptions(optConfig: OptimizerConfig,
+ noSourceMap: Boolean = false) = {
+
+ val options = new ClosureOptions
+ options.prettyPrint = optConfig.prettyPrint
+ CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(options)
+ options.setLanguageIn(ClosureOptions.LanguageMode.ECMASCRIPT5)
+ options.setCheckGlobalThisLevel(CheckLevel.OFF)
+
+ if (!noSourceMap && optConfig.wantSourceMap) {
+ options.setSourceMapOutputPath(optConfig.output.name + ".map")
+ options.setSourceMapDetailLevel(SourceMap.DetailLevel.ALL)
+ }
+
+ options
+ }
+
+ private def closureCompiler(logger: Logger) = {
+ val compiler = new ClosureCompiler
+ compiler.setErrorManager(new LoggerErrorManager(logger))
+ compiler
+ }
+}
+
+object ScalaJSClosureOptimizer {
+ /** Inputs of the Scala.js Closure optimizer. */
+ final case class Inputs[T](
+ /** Input to optimize (classpath or file-list) */
+ input: T,
+ /** Additional externs files to be given to Closure. */
+ additionalExterns: Seq[VirtualJSFile] = Nil,
+ /** Additional exports to be given to Closure.
+ * These files are just appended to the classpath, given to Closure,
+ * but not used in the Scala.js optimizer pass when direct optimizing
+ */
+ additionalExports: Seq[VirtualJSFile] = Nil
+ )
+
+ /** Configuration the closure part of the optimizer needs.
+ * See [[OutputConfig]] for a description of the fields.
+ */
+ trait OptimizerConfig {
+ val output: WritableVirtualJSFile
+ val cache: Option[WritableVirtualTextFile]
+ val wantSourceMap: Boolean
+ val prettyPrint: Boolean
+ val relativizeSourceMapBase: Option[URI]
+ }
+
+ /** Configuration for the output of the Scala.js Closure optimizer */
+ final case class OutputConfig(
+ /** Writer for the output */
+ output: WritableVirtualJSFile,
+ /** Cache file */
+ cache: Option[WritableVirtualTextFile] = None,
+ /** If true, performs expensive checks of the IR for the used parts. */
+ checkIR: Boolean = false,
+ /** If true, the optimizer removes trees that have not been used in the
+ * last run from the cache. Otherwise, all trees that has been used once,
+ * are kept in memory. */
+ unCache: Boolean = true,
+ /** If true, no optimizations are performed */
+ disableOptimizer: Boolean = false,
+ /** If true, nothing is performed incrementally */
+ batchMode: Boolean = false,
+ /** Ask to produce source map for the output */
+ wantSourceMap: Boolean = false,
+ /** Pretty-print the output. */
+ prettyPrint: Boolean = false,
+ /** Base path to relativize paths in the source map */
+ relativizeSourceMapBase: Option[URI] = None
+ ) extends OptimizerConfig with ScalaJSOptimizer.OptimizerConfig
+
+ /** Minimal set of externs to compile Scala.js-emitted code with Closure. */
+ val ScalaJSExterns = """
+ /** @constructor */
+ function Object() {}
+ Object.protoype.toString = function() {};
+ /** @constructor */
+ function Array() {}
+ Array.prototype.length = 0;
+ /** @constructor */
+ function Function() {}
+ Function.prototype.constructor = function() {};
+ Function.prototype.call = function() {};
+ Function.prototype.apply = function() {};
+ var global = {};
+ var __ScalaJSEnv = {};
+ """
+
+ val ScalaJSExternsFile = new MemVirtualJSFile("ScalaJSExterns.js").
+ withContent(ScalaJSExterns)
+
+}
diff --git a/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/sourcemap/SourceMapper.scala b/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/sourcemap/SourceMapper.scala
new file mode 100644
index 0000000..6f856a9
--- /dev/null
+++ b/examples/scala-js/tools/jvm/src/main/scala/scala/scalajs/tools/sourcemap/SourceMapper.scala
@@ -0,0 +1,88 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.sourcemap
+
+import scala.scalajs.tools.classpath._
+
+import com.google.debugging.sourcemap._
+
+class SourceMapper(classpath: CompleteClasspath) {
+
+ def map(ste: StackTraceElement, columnNumber: Int): StackTraceElement = {
+ val mapped = for {
+ sourceMap <- findSourceMap(ste.getFileName)
+ } yield map(ste, columnNumber, sourceMap)
+
+ mapped.getOrElse(ste)
+ }
+
+ def map(ste: StackTraceElement, columnNumber: Int,
+ sourceMap: String): StackTraceElement = {
+
+ val sourceMapConsumer =
+ SourceMapConsumerFactory
+ .parse(sourceMap)
+ .asInstanceOf[SourceMapConsumerV3]
+
+ /* **Attention**
+ * - StackTrace positions are 1-based
+ * - SourceMapConsumer.getMappingForLine() is 1-based
+ */
+
+ val lineNumber = ste.getLineNumber
+ val column =
+ if (columnNumber == -1) getFirstColumn(sourceMapConsumer, lineNumber)
+ else columnNumber
+
+ val originalMapping =
+ sourceMapConsumer.getMappingForLine(lineNumber, column)
+
+ if (originalMapping != null) {
+ new StackTraceElement(
+ ste.getClassName,
+ ste.getMethodName,
+ originalMapping.getOriginalFile,
+ originalMapping.getLineNumber)
+ } else ste
+ }
+
+ /** both `lineNumber` and the resulting column are 1-based */
+ private def getFirstColumn(sourceMapConsumer: SourceMapConsumerV3,
+ lineNumber1Based: Int) = {
+
+ /* **Attention**
+ * - SourceMapConsumerV3.EntryVisitor is 0-based!!!
+ */
+
+ val lineNumber = lineNumber1Based - 1
+
+ var column: Option[Int] = None
+
+ sourceMapConsumer.visitMappings(
+ new SourceMapConsumerV3.EntryVisitor {
+ def visit(sourceName: String,
+ symbolName: String,
+ sourceStartPosition: FilePosition,
+ startPosition: FilePosition,
+ endPosition: FilePosition): Unit =
+ if (!column.isDefined && startPosition.getLine == lineNumber)
+ column = Some(startPosition.getColumn)
+ })
+
+ val column0Based = column.getOrElse(0)
+ column0Based + 1
+ }
+
+ private def findSourceMap(path: String) = {
+ val candidates = classpath.allCode.filter(_.path == path)
+ if (candidates.size != 1) None // better no sourcemap than a wrong one
+ else candidates.head.sourceMap
+ }
+}
diff --git a/examples/scala-js/tools/jvm/src/test/resources/test.jar b/examples/scala-js/tools/jvm/src/test/resources/test.jar
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/examples/scala-js/tools/jvm/src/test/resources/test.jar
diff --git a/examples/scala-js/tools/jvm/src/test/scala/scala/scalajs/tools/classpath/builder/test/ClasspathElementsTraverserTest.scala b/examples/scala-js/tools/jvm/src/test/scala/scala/scalajs/tools/classpath/builder/test/ClasspathElementsTraverserTest.scala
new file mode 100644
index 0000000..72d01e4
--- /dev/null
+++ b/examples/scala-js/tools/jvm/src/test/scala/scala/scalajs/tools/classpath/builder/test/ClasspathElementsTraverserTest.scala
@@ -0,0 +1,42 @@
+package scala.scalajs.tools.classpath.builder.test
+
+import scala.scalajs.tools.classpath.builder._
+import scala.scalajs.tools.jsdep.JSDependencyManifest
+import scala.scalajs.tools.io.VirtualScalaJSIRFile
+import scala.scalajs.tools.io.VirtualJSFile
+
+import java.io.File
+
+import org.junit.Test
+import org.junit.Assert._
+
+class ClasspathElementsTraverserTest {
+ class TestElementTraverser extends ClasspathElementsTraverser with PhysicalFileSystem {
+ protected def handleDepManifest(m: => JSDependencyManifest): Unit = ???
+ protected def handleIR(relPath: String, ir: => VirtualScalaJSIRFile): Unit = ???
+ protected def handleJS(js: => VirtualJSFile): Unit = ???
+
+ def test(f: File): String = traverseClasspathElements(Seq(f))
+ }
+
+ val cpElementTraverser = new TestElementTraverser()
+
+ @Test
+ def testNotExistingJarFile(): Unit = {
+ val res = cpElementTraverser.test(new File("dummy.jar"))
+ assertTrue(res.endsWith(cpElementTraverser.DummyVersion))
+ }
+
+ @Test
+ def testExistingDirectory(): Unit = {
+ val res = cpElementTraverser.test(new File("tools/jvm/src/test/resources"))
+ assertTrue(res.indexOf(cpElementTraverser.DummyVersion) < 0)
+ }
+
+ @Test
+ def testExistingJarFile(): Unit = {
+ val res = cpElementTraverser.test(new File("tools/jvm/src/test/resources/test.jar"))
+ assertTrue(res.indexOf(cpElementTraverser.DummyVersion) < 0)
+ }
+}
+
diff --git a/examples/scala-js/tools/jvm/src/test/scala/scala/scalajs/tools/classpath/builder/test/JarBuilderTest.scala b/examples/scala-js/tools/jvm/src/test/scala/scala/scalajs/tools/classpath/builder/test/JarBuilderTest.scala
new file mode 100644
index 0000000..6ed68fc
--- /dev/null
+++ b/examples/scala-js/tools/jvm/src/test/scala/scala/scalajs/tools/classpath/builder/test/JarBuilderTest.scala
@@ -0,0 +1,74 @@
+package scala.scalajs.tools.classpath.builder.test
+
+import scala.scalajs.tools.classpath.builder._
+
+import org.junit.Test
+import org.junit.Assert._
+
+class JarBuilderTest {
+
+ val jsFileContent = "window.alert('Hello World');"
+ val jsFileName = "lib.js"
+
+ private def getJARInputStream = {
+ // Keep imports local so we don't accidentally use something we shouldn't
+ import java.util.zip._
+ import java.io._
+
+ // Write a ZIP to a byte array
+ val outBuf = new ByteArrayOutputStream
+ val out = new ZipOutputStream(outBuf)
+
+ out.putNextEntry(new ZipEntry(jsFileName))
+ val w = new OutputStreamWriter(out, "UTF-8")
+ w.write(jsFileContent)
+ w.flush()
+
+ out.close()
+
+ new ByteArrayInputStream(outBuf.toByteArray)
+ }
+
+ /** Trivial FS that has only one jar */
+ trait TrivialFS extends FileSystem {
+ // Keep imports local so we don't accidentally use something we shouldn't
+ import java.io.{InputStream, Reader}
+ import scala.scalajs.tools.io._
+ import scala.collection.immutable.Traversable
+
+ type File = Unit
+
+ val DummyVersion: String = "DUMMY"
+
+ def isDirectory(f: File): Boolean = false
+ def isFile(f: File): Boolean = true
+ def isJSFile(f: File): Boolean = false
+ def isIRFile(f: File): Boolean = false
+ def isJARFile(f: File): Boolean = true
+ def exists(f: File): Boolean = true
+
+ def getName(f: File): String = "jar"
+ def getAbsolutePath(f: File): String = "jar"
+ def getVersion(f: File): String = ""
+
+ def listFiles(d: File): Traversable[File] = ???
+
+ def toJSFile(f: File): VirtualJSFile = ???
+ def toIRFile(f: File): VirtualScalaJSIRFile = ???
+ def toReader(f: File): Reader = ???
+ def toInputStream(f: File): InputStream = getJARInputStream
+ }
+
+ class TestJARBuilder extends AbstractJarLibClasspathBuilder with TrivialFS
+
+ @Test
+ def readInMemoryJarClasspath {
+ val builder = new TestJARBuilder
+ val cp = builder.build(Seq(()))
+
+ assertArrayEquals(
+ Array[Object](jsFileName -> jsFileContent),
+ cp.availableLibs.mapValues(_.content).toArray[Object])
+ }
+
+}
diff --git a/examples/scala-js/tools/scalajsenv.js b/examples/scala-js/tools/scalajsenv.js
new file mode 100644
index 0000000..01a84ba
--- /dev/null
+++ b/examples/scala-js/tools/scalajsenv.js
@@ -0,0 +1,772 @@
+/* Scala.js runtime support
+ * Copyright 2013 LAMP/EPFL
+ * Author: Sébastien Doeraene
+ */
+
+/* ---------------------------------- *
+ * The top-level Scala.js environment *
+ * ---------------------------------- */
+
+var ScalaJS = {};
+
+// Get the environment info
+ScalaJS.env = (typeof __ScalaJSEnv === "object" && __ScalaJSEnv) ? __ScalaJSEnv : {};
+
+// Global scope
+ScalaJS.g =
+ (typeof ScalaJS.env["global"] === "object" && ScalaJS.env["global"])
+ ? ScalaJS.env["global"]
+ : ((typeof global === "object" && global && global["Object"] === Object) ? global : this);
+ScalaJS.env["global"] = ScalaJS.g;
+
+// Where to send exports
+ScalaJS.e =
+ (typeof ScalaJS.env["exportsNamespace"] === "object" && ScalaJS.env["exportsNamespace"])
+ ? ScalaJS.env["exportsNamespace"] : ScalaJS.g;
+ScalaJS.env["exportsNamespace"] = ScalaJS.e;
+
+// Freeze the environment info
+ScalaJS.g["Object"]["freeze"](ScalaJS.env);
+
+// Other fields
+ScalaJS.d = {}; // Data for types
+ScalaJS.c = {}; // Scala.js constructors
+ScalaJS.h = {}; // Inheritable constructors (without initialization code)
+ScalaJS.i = {}; // Implementation class modules
+ScalaJS.n = {}; // Module instances
+ScalaJS.m = {}; // Module accessors
+ScalaJS.is = {}; // isInstanceOf methods
+ScalaJS.isArrayOf = {}; // isInstanceOfArrayOf methods
+//!if asInstanceOfs != Unchecked
+ScalaJS.as = {}; // asInstanceOf methods
+ScalaJS.asArrayOf = {}; // asInstanceOfArrayOf methods
+//!endif
+ScalaJS.lastIDHash = 0; // last value attributed to an id hash code
+
+// Core mechanism
+
+ScalaJS.makeIsArrayOfPrimitive = function(primitiveData) {
+ return function(obj, depth) {
+ return !!(obj && obj.$classData &&
+ (obj.$classData.arrayDepth === depth) &&
+ (obj.$classData.arrayBase === primitiveData));
+ }
+};
+
+//!if asInstanceOfs != Unchecked
+ScalaJS.makeAsArrayOfPrimitive = function(isInstanceOfFunction, arrayEncodedName) {
+ return function(obj, depth) {
+ if (isInstanceOfFunction(obj, depth) || (obj === null))
+ return obj;
+ else
+ ScalaJS.throwArrayCastException(obj, arrayEncodedName, depth);
+ }
+};
+//!endif
+
+/** Encode a property name for runtime manipulation
+ * Usage:
+ * env.propertyName({someProp:0})
+ * Returns:
+ * "someProp"
+ * Useful when the property is renamed by a global optimizer (like Closure)
+ * but we must still get hold of a string of that name for runtime
+ * reflection.
+ */
+ScalaJS.propertyName = function(obj) {
+ var result;
+ for (var prop in obj)
+ result = prop;
+ return result;
+};
+
+// Runtime functions
+
+ScalaJS.isScalaJSObject = function(obj) {
+ return !!(obj && obj.$classData);
+};
+
+//!if asInstanceOfs != Unchecked
+ScalaJS.throwClassCastException = function(instance, classFullName) {
+//!if asInstanceOfs == Compliant
+ throw new ScalaJS.c.jl_ClassCastException().init___T(
+ instance + " is not an instance of " + classFullName);
+//!else
+ throw new ScalaJS.c.sjsr_UndefinedBehaviorError().init___jl_Throwable(
+ new ScalaJS.c.jl_ClassCastException().init___T(
+ instance + " is not an instance of " + classFullName));
+//!endif
+};
+
+ScalaJS.throwArrayCastException = function(instance, classArrayEncodedName, depth) {
+ for (; depth; --depth)
+ classArrayEncodedName = "[" + classArrayEncodedName;
+ ScalaJS.throwClassCastException(instance, classArrayEncodedName);
+};
+//!endif
+
+ScalaJS.makeNativeArrayWrapper = function(arrayClassData, nativeArray) {
+ return new arrayClassData.constr(nativeArray);
+};
+
+ScalaJS.newArrayObject = function(arrayClassData, lengths) {
+ return ScalaJS.newArrayObjectInternal(arrayClassData, lengths, 0);
+};
+
+ScalaJS.newArrayObjectInternal = function(arrayClassData, lengths, lengthIndex) {
+ var result = new arrayClassData.constr(lengths[lengthIndex]);
+
+ if (lengthIndex < lengths.length-1) {
+ var subArrayClassData = arrayClassData.componentData;
+ var subLengthIndex = lengthIndex+1;
+ var underlying = result.u;
+ for (var i = 0; i < underlying.length; i++) {
+ underlying[i] = ScalaJS.newArrayObjectInternal(
+ subArrayClassData, lengths, subLengthIndex);
+ }
+ }
+
+ return result;
+};
+
+ScalaJS.checkNonNull = function(obj) {
+ return obj !== null ? obj : ScalaJS.throwNullPointerException();
+};
+
+ScalaJS.throwNullPointerException = function() {
+ throw new ScalaJS.c.jl_NullPointerException().init___();
+};
+
+ScalaJS.objectToString = function(instance) {
+ if (instance === void 0)
+ return "undefined";
+ else
+ return instance.toString();
+};
+
+ScalaJS.objectGetClass = function(instance) {
+ switch (typeof instance) {
+ case "string":
+ return ScalaJS.d.T.getClassOf();
+ case "number":
+ var v = instance | 0;
+ if (v === instance) { // is the value integral?
+ if (ScalaJS.isByte(v))
+ return ScalaJS.d.jl_Byte.getClassOf();
+ else if (ScalaJS.isShort(v))
+ return ScalaJS.d.jl_Short.getClassOf();
+ else
+ return ScalaJS.d.jl_Integer.getClassOf();
+ } else {
+ if (ScalaJS.isFloat(instance))
+ return ScalaJS.d.jl_Float.getClassOf();
+ else
+ return ScalaJS.d.jl_Double.getClassOf();
+ }
+ case "boolean":
+ return ScalaJS.d.jl_Boolean.getClassOf();
+ case "undefined":
+ return ScalaJS.d.sr_BoxedUnit.getClassOf();
+ default:
+ if (instance === null)
+ ScalaJS.throwNullPointerException();
+ else if (ScalaJS.is.sjsr_RuntimeLong(instance))
+ return ScalaJS.d.jl_Long.getClassOf();
+ else if (ScalaJS.isScalaJSObject(instance))
+ return instance.$classData.getClassOf();
+ else
+ return null; // Exception?
+ }
+};
+
+ScalaJS.objectClone = function(instance) {
+ if (ScalaJS.isScalaJSObject(instance) || (instance === null))
+ return instance.clone__O();
+ else
+ throw new ScalaJS.c.jl_CloneNotSupportedException().init___();
+};
+
+ScalaJS.objectNotify = function(instance) {
+ // final and no-op in java.lang.Object
+ if (instance === null)
+ instance.notify__V();
+};
+
+ScalaJS.objectNotifyAll = function(instance) {
+ // final and no-op in java.lang.Object
+ if (instance === null)
+ instance.notifyAll__V();
+};
+
+ScalaJS.objectFinalize = function(instance) {
+ if (ScalaJS.isScalaJSObject(instance) || (instance === null))
+ instance.finalize__V();
+ // else no-op
+};
+
+ScalaJS.objectEquals = function(instance, rhs) {
+ if (ScalaJS.isScalaJSObject(instance) || (instance === null))
+ return instance.equals__O__Z(rhs);
+ else if (typeof instance === "number")
+ return typeof rhs === "number" && ScalaJS.numberEquals(instance, rhs);
+ else
+ return instance === rhs;
+};
+
+ScalaJS.numberEquals = function(lhs, rhs) {
+ return (
+ lhs === rhs // 0.0 === -0.0 to prioritize the Int case over the Double case
+ ) || (
+ // are they both NaN?
+ (lhs !== lhs) && (rhs !== rhs)
+ );
+};
+
+ScalaJS.objectHashCode = function(instance) {
+ switch (typeof instance) {
+ case "string":
+ return ScalaJS.m.sjsr_RuntimeString().hashCode__T__I(instance);
+ case "number":
+ return ScalaJS.m.sjsr_Bits().numberHashCode__D__I(instance);
+ case "boolean":
+ return instance ? 1231 : 1237;
+ case "undefined":
+ return 0;
+ default:
+ if (ScalaJS.isScalaJSObject(instance) || instance === null)
+ return instance.hashCode__I();
+ else
+ return 42; // TODO?
+ }
+};
+
+ScalaJS.comparableCompareTo = function(instance, rhs) {
+ switch (typeof instance) {
+ case "string":
+//!if asInstanceOfs != Unchecked
+ ScalaJS.as.T(rhs);
+//!endif
+ return instance === rhs ? 0 : (instance < rhs ? -1 : 1);
+ case "number":
+//!if asInstanceOfs != Unchecked
+ ScalaJS.as.jl_Number(rhs);
+//!endif
+ return ScalaJS.numberEquals(instance, rhs) ? 0 : (instance < rhs ? -1 : 1);
+ case "boolean":
+//!if asInstanceOfs != Unchecked
+ ScalaJS.asBoolean(rhs);
+//!endif
+ return instance - rhs; // yes, this gives the right result
+ default:
+ return instance.compareTo__O__I(rhs);
+ }
+};
+
+ScalaJS.charSequenceLength = function(instance) {
+ if (typeof(instance) === "string")
+//!if asInstanceOfs != Unchecked
+ return ScalaJS.uI(instance["length"]);
+//!else
+ return instance["length"] | 0;
+//!endif
+ else
+ return instance.length__I();
+};
+
+ScalaJS.charSequenceCharAt = function(instance, index) {
+ if (typeof(instance) === "string")
+//!if asInstanceOfs != Unchecked
+ return ScalaJS.uI(instance["charCodeAt"](index)) & 0xffff;
+//!else
+ return instance["charCodeAt"](index) & 0xffff;
+//!endif
+ else
+ return instance.charAt__I__C(index);
+};
+
+ScalaJS.charSequenceSubSequence = function(instance, start, end) {
+ if (typeof(instance) === "string")
+//!if asInstanceOfs != Unchecked
+ return ScalaJS.as.T(instance["substring"](start, end));
+//!else
+ return instance["substring"](start, end);
+//!endif
+ else
+ return instance.subSequence__I__I__jl_CharSequence(start, end);
+};
+
+ScalaJS.booleanBooleanValue = function(instance) {
+ if (typeof instance === "boolean") return instance;
+ else return instance.booleanValue__Z();
+};
+
+ScalaJS.numberByteValue = function(instance) {
+ if (typeof instance === "number") return (instance << 24) >> 24;
+ else return instance.byteValue__B();
+};
+ScalaJS.numberShortValue = function(instance) {
+ if (typeof instance === "number") return (instance << 16) >> 16;
+ else return instance.shortValue__S();
+};
+ScalaJS.numberIntValue = function(instance) {
+ if (typeof instance === "number") return instance | 0;
+ else return instance.intValue__I();
+};
+ScalaJS.numberLongValue = function(instance) {
+ if (typeof instance === "number")
+ return ScalaJS.m.sjsr_RuntimeLong().fromDouble__D__sjsr_RuntimeLong(instance);
+ else
+ return instance.longValue__J();
+};
+ScalaJS.numberFloatValue = function(instance) {
+ if (typeof instance === "number") return ScalaJS.fround(instance);
+ else return instance.floatValue__F();
+};
+ScalaJS.numberDoubleValue = function(instance) {
+ if (typeof instance === "number") return instance;
+ else return instance.doubleValue__D();
+};
+
+ScalaJS.isNaN = function(instance) {
+ return instance !== instance;
+};
+
+ScalaJS.isInfinite = function(instance) {
+ return !ScalaJS.g["isFinite"](instance) && !ScalaJS.isNaN(instance);
+};
+
+ScalaJS.propertiesOf = function(obj) {
+ var result = [];
+ for (var prop in obj)
+ result["push"](prop);
+ return result;
+};
+
+ScalaJS.systemArraycopy = function(src, srcPos, dest, destPos, length) {
+ var srcu = src.u;
+ var destu = dest.u;
+ if (srcu !== destu || destPos < srcPos || srcPos + length < destPos) {
+ for (var i = 0; i < length; i++)
+ destu[destPos+i] = srcu[srcPos+i];
+ } else {
+ for (var i = length-1; i >= 0; i--)
+ destu[destPos+i] = srcu[srcPos+i];
+ }
+};
+
+ScalaJS.systemIdentityHashCode = function(obj) {
+ if (ScalaJS.isScalaJSObject(obj)) {
+ var hash = obj["$idHashCode$0"];
+ if (hash !== void 0) {
+ return hash;
+ } else {
+ hash = (ScalaJS.lastIDHash + 1) | 0;
+ ScalaJS.lastIDHash = hash;
+ obj["$idHashCode$0"] = hash;
+ return hash;
+ }
+ } else if (obj === null) {
+ return 0;
+ } else {
+ return ScalaJS.objectHashCode(obj);
+ }
+};
+
+// is/as for hijacked boxed classes (the non-trivial ones)
+
+ScalaJS.isByte = function(v) {
+ return (v << 24 >> 24) === v;
+};
+
+ScalaJS.isShort = function(v) {
+ return (v << 16 >> 16) === v;
+};
+
+ScalaJS.isInt = function(v) {
+ return (v | 0) === v;
+};
+
+ScalaJS.isFloat = function(v) {
+ return v !== v || ScalaJS.fround(v) === v;
+};
+
+//!if asInstanceOfs != Unchecked
+ScalaJS.asUnit = function(v) {
+ if (v === void 0)
+ return v;
+ else
+ ScalaJS.throwClassCastException(v, "scala.runtime.BoxedUnit");
+};
+
+ScalaJS.asBoolean = function(v) {
+ if (typeof v === "boolean" || v === null)
+ return v;
+ else
+ ScalaJS.throwClassCastException(v, "java.lang.Boolean");
+};
+
+ScalaJS.asByte = function(v) {
+ if (ScalaJS.isByte(v) || v === null)
+ return v;
+ else
+ ScalaJS.throwClassCastException(v, "java.lang.Byte");
+};
+
+ScalaJS.asShort = function(v) {
+ if (ScalaJS.isShort(v) || v === null)
+ return v;
+ else
+ ScalaJS.throwClassCastException(v, "java.lang.Short");
+};
+
+ScalaJS.asInt = function(v) {
+ if (ScalaJS.isInt(v) || v === null)
+ return v;
+ else
+ ScalaJS.throwClassCastException(v, "java.lang.Integer");
+};
+
+ScalaJS.asFloat = function(v) {
+ if (ScalaJS.isFloat(v) || v === null)
+ return v;
+ else
+ ScalaJS.throwClassCastException(v, "java.lang.Float");
+};
+
+ScalaJS.asDouble = function(v) {
+ if (typeof v === "number" || v === null)
+ return v;
+ else
+ ScalaJS.throwClassCastException(v, "java.lang.Double");
+};
+//!endif
+
+// Unboxes
+
+//!if asInstanceOfs != Unchecked
+ScalaJS.uZ = function(value) {
+ return !!ScalaJS.asBoolean(value);
+};
+ScalaJS.uB = function(value) {
+ return ScalaJS.asByte(value) | 0;
+};
+ScalaJS.uS = function(value) {
+ return ScalaJS.asShort(value) | 0;
+};
+ScalaJS.uI = function(value) {
+ return ScalaJS.asInt(value) | 0;
+};
+ScalaJS.uJ = function(value) {
+ return null === value ? ScalaJS.m.sjsr_RuntimeLong().Zero$1
+ : ScalaJS.as.sjsr_RuntimeLong(value);
+};
+ScalaJS.uF = function(value) {
+ /* Here, it is fine to use + instead of fround, because asFloat already
+ * ensures that the result is either null or a float.
+ */
+ return +ScalaJS.asFloat(value);
+};
+ScalaJS.uD = function(value) {
+ return +ScalaJS.asDouble(value);
+};
+//!else
+ScalaJS.uJ = function(value) {
+ return null === value ? ScalaJS.m.sjsr_RuntimeLong().Zero$1 : value;
+};
+//!endif
+
+// TypeArray conversions
+
+ScalaJS.byteArray2TypedArray = function(value) { return new Int8Array(value.u); };
+ScalaJS.shortArray2TypedArray = function(value) { return new Int16Array(value.u); };
+ScalaJS.charArray2TypedArray = function(value) { return new Uint16Array(value.u); };
+ScalaJS.intArray2TypedArray = function(value) { return new Int32Array(value.u); };
+ScalaJS.floatArray2TypedArray = function(value) { return new Float32Array(value.u); };
+ScalaJS.doubleArray2TypedArray = function(value) { return new Float64Array(value.u); };
+
+ScalaJS.typedArray2ByteArray = function(value) {
+ var arrayClassData = ScalaJS.d.B.getArrayOf();
+ return new arrayClassData.constr(new Int8Array(value));
+};
+ScalaJS.typedArray2ShortArray = function(value) {
+ var arrayClassData = ScalaJS.d.S.getArrayOf();
+ return new arrayClassData.constr(new Int16Array(value));
+};
+ScalaJS.typedArray2CharArray = function(value) {
+ var arrayClassData = ScalaJS.d.C.getArrayOf();
+ return new arrayClassData.constr(new Uint16Array(value));
+};
+ScalaJS.typedArray2IntArray = function(value) {
+ var arrayClassData = ScalaJS.d.I.getArrayOf();
+ return new arrayClassData.constr(new Int32Array(value));
+};
+ScalaJS.typedArray2FloatArray = function(value) {
+ var arrayClassData = ScalaJS.d.F.getArrayOf();
+ return new arrayClassData.constr(new Float32Array(value));
+};
+ScalaJS.typedArray2DoubleArray = function(value) {
+ var arrayClassData = ScalaJS.d.D.getArrayOf();
+ return new arrayClassData.constr(new Float64Array(value));
+};
+
+/* We have to force a non-elidable *read* of ScalaJS.e, otherwise Closure will
+ * eliminate it altogether, along with all the exports, which is ... er ...
+ * plain wrong.
+ */
+this["__ScalaJSExportsNamespace"] = ScalaJS.e;
+
+// Type data constructors
+
+/** @constructor */
+ScalaJS.PrimitiveTypeData = function(zero, arrayEncodedName, displayName) {
+ // Runtime support
+ this.constr = undefined;
+ this.parentData = undefined;
+ this.ancestors = {};
+ this.componentData = null;
+ this.zero = zero;
+ this.arrayEncodedName = arrayEncodedName;
+ this._classOf = undefined;
+ this._arrayOf = undefined;
+ this.isArrayOf = function(obj, depth) { return false; };
+
+ // java.lang.Class support
+ this["name"] = displayName;
+ this["isPrimitive"] = true;
+ this["isInterface"] = false;
+ this["isArrayClass"] = false;
+ this["isInstance"] = function(obj) { return false; };
+};
+
+/** @constructor */
+ScalaJS.ClassTypeData = function(internalNameObj, isInterface, fullName,
+ parentData, ancestors, isInstance, isArrayOf) {
+ var internalName = ScalaJS.propertyName(internalNameObj);
+
+ isInstance = isInstance || function(obj) {
+ return !!(obj && obj.$classData && obj.$classData.ancestors[internalName]);
+ };
+
+ isArrayOf = isArrayOf || function(obj, depth) {
+ return !!(obj && obj.$classData && (obj.$classData.arrayDepth === depth)
+ && obj.$classData.arrayBase.ancestors[internalName])
+ };
+
+ // Runtime support
+ this.constr = undefined;
+ this.parentData = parentData;
+ this.ancestors = ancestors;
+ this.componentData = null;
+ this.zero = null;
+ this.arrayEncodedName = "L"+fullName+";";
+ this._classOf = undefined;
+ this._arrayOf = undefined;
+ this.isArrayOf = isArrayOf;
+
+ // java.lang.Class support
+ this["name"] = fullName;
+ this["isPrimitive"] = false;
+ this["isInterface"] = isInterface;
+ this["isArrayClass"] = false;
+ this["isInstance"] = isInstance;
+};
+
+/** @constructor */
+ScalaJS.ArrayTypeData = function(componentData) {
+ // The constructor
+
+ var componentZero = componentData.zero;
+
+ // The zero for the Long runtime representation
+ // is a special case here, since the class has not
+ // been defined yet, when this file is read
+ if (componentZero == "longZero")
+ componentZero = ScalaJS.m.sjsr_RuntimeLong().Zero$1;
+
+ /** @constructor */
+ var ArrayClass = function(arg) {
+ if (typeof(arg) === "number") {
+ // arg is the length of the array
+ this.u = new Array(arg);
+ for (var i = 0; i < arg; i++)
+ this.u[i] = componentZero;
+ } else {
+ // arg is a native array that we wrap
+ this.u = arg;
+ }
+ }
+ ArrayClass.prototype = new ScalaJS.h.O;
+ ArrayClass.prototype.constructor = ArrayClass;
+ ArrayClass.prototype.$classData = this;
+
+ ArrayClass.prototype.clone__O = function() {
+ if (this.u instanceof Array)
+ return new ArrayClass(this.u["slice"](0));
+ else
+ // The underlying Array is a TypedArray
+ return new ArrayClass(this.u.constructor(this.u));
+ };
+
+ // Don't generate reflective call proxies. The compiler special cases
+ // reflective calls to methods on scala.Array
+
+ // The data
+
+ var encodedName = "[" + componentData.arrayEncodedName;
+ var componentBase = componentData.arrayBase || componentData;
+ var componentDepth = componentData.arrayDepth || 0;
+ var arrayDepth = componentDepth + 1;
+
+ var isInstance = function(obj) {
+ return componentBase.isArrayOf(obj, arrayDepth);
+ }
+
+ // Runtime support
+ this.constr = ArrayClass;
+ this.parentData = ScalaJS.d.O;
+ this.ancestors = {O: 1};
+ this.componentData = componentData;
+ this.arrayBase = componentBase;
+ this.arrayDepth = arrayDepth;
+ this.zero = null;
+ this.arrayEncodedName = encodedName;
+ this._classOf = undefined;
+ this._arrayOf = undefined;
+ this.isArrayOf = undefined;
+
+ // java.lang.Class support
+ this["name"] = encodedName;
+ this["isPrimitive"] = false;
+ this["isInterface"] = false;
+ this["isArrayClass"] = true;
+ this["isInstance"] = isInstance;
+};
+
+ScalaJS.ClassTypeData.prototype.getClassOf = function() {
+ if (!this._classOf)
+ this._classOf = new ScalaJS.c.jl_Class().init___jl_ScalaJSClassData(this);
+ return this._classOf;
+};
+
+ScalaJS.ClassTypeData.prototype.getArrayOf = function() {
+ if (!this._arrayOf)
+ this._arrayOf = new ScalaJS.ArrayTypeData(this);
+ return this._arrayOf;
+};
+
+// java.lang.Class support
+
+ScalaJS.ClassTypeData.prototype["getFakeInstance"] = function() {
+ if (this === ScalaJS.d.T)
+ return "some string";
+ else if (this === ScalaJS.d.jl_Boolean)
+ return false;
+ else if (this === ScalaJS.d.jl_Byte ||
+ this === ScalaJS.d.jl_Short ||
+ this === ScalaJS.d.jl_Integer ||
+ this === ScalaJS.d.jl_Float ||
+ this === ScalaJS.d.jl_Double)
+ return 0;
+ else if (this === ScalaJS.d.jl_Long)
+ return ScalaJS.m.sjsr_RuntimeLong().Zero$1;
+ else if (this === ScalaJS.d.sr_BoxedUnit)
+ return void 0;
+ else
+ return {$classData: this};
+};
+
+ScalaJS.ClassTypeData.prototype["getSuperclass"] = function() {
+ return this.parentData ? this.parentData.getClassOf() : null;
+};
+
+ScalaJS.ClassTypeData.prototype["getComponentType"] = function() {
+ return this.componentData ? this.componentData.getClassOf() : null;
+};
+
+ScalaJS.ClassTypeData.prototype["newArrayOfThisClass"] = function(lengths) {
+ var arrayClassData = this;
+ for (var i = 0; i < lengths.length; i++)
+ arrayClassData = arrayClassData.getArrayOf();
+ return ScalaJS.newArrayObject(arrayClassData, lengths);
+};
+
+ScalaJS.PrimitiveTypeData.prototype = ScalaJS.ClassTypeData.prototype;
+ScalaJS.ArrayTypeData.prototype = ScalaJS.ClassTypeData.prototype;
+
+// Create primitive types
+
+ScalaJS.d.V = new ScalaJS.PrimitiveTypeData(undefined, "V", "void");
+ScalaJS.d.Z = new ScalaJS.PrimitiveTypeData(false, "Z", "boolean");
+ScalaJS.d.C = new ScalaJS.PrimitiveTypeData(0, "C", "char");
+ScalaJS.d.B = new ScalaJS.PrimitiveTypeData(0, "B", "byte");
+ScalaJS.d.S = new ScalaJS.PrimitiveTypeData(0, "S", "short");
+ScalaJS.d.I = new ScalaJS.PrimitiveTypeData(0, "I", "int");
+ScalaJS.d.J = new ScalaJS.PrimitiveTypeData("longZero", "J", "long");
+ScalaJS.d.F = new ScalaJS.PrimitiveTypeData(0.0, "F", "float");
+ScalaJS.d.D = new ScalaJS.PrimitiveTypeData(0.0, "D", "double");
+
+// Instance tests for array of primitives
+
+ScalaJS.isArrayOf.Z = ScalaJS.makeIsArrayOfPrimitive(ScalaJS.d.Z);
+ScalaJS.d.Z.isArrayOf = ScalaJS.isArrayOf.Z;
+
+ScalaJS.isArrayOf.C = ScalaJS.makeIsArrayOfPrimitive(ScalaJS.d.C);
+ScalaJS.d.C.isArrayOf = ScalaJS.isArrayOf.C;
+
+ScalaJS.isArrayOf.B = ScalaJS.makeIsArrayOfPrimitive(ScalaJS.d.B);
+ScalaJS.d.B.isArrayOf = ScalaJS.isArrayOf.B;
+
+ScalaJS.isArrayOf.S = ScalaJS.makeIsArrayOfPrimitive(ScalaJS.d.S);
+ScalaJS.d.S.isArrayOf = ScalaJS.isArrayOf.S;
+
+ScalaJS.isArrayOf.I = ScalaJS.makeIsArrayOfPrimitive(ScalaJS.d.I);
+ScalaJS.d.I.isArrayOf = ScalaJS.isArrayOf.I;
+
+ScalaJS.isArrayOf.J = ScalaJS.makeIsArrayOfPrimitive(ScalaJS.d.J);
+ScalaJS.d.J.isArrayOf = ScalaJS.isArrayOf.J;
+
+ScalaJS.isArrayOf.F = ScalaJS.makeIsArrayOfPrimitive(ScalaJS.d.F);
+ScalaJS.d.F.isArrayOf = ScalaJS.isArrayOf.F;
+
+ScalaJS.isArrayOf.D = ScalaJS.makeIsArrayOfPrimitive(ScalaJS.d.D);
+ScalaJS.d.D.isArrayOf = ScalaJS.isArrayOf.D;
+
+//!if asInstanceOfs != Unchecked
+// asInstanceOfs for array of primitives
+ScalaJS.asArrayOf.Z = ScalaJS.makeAsArrayOfPrimitive(ScalaJS.isArrayOf.Z, "Z");
+ScalaJS.asArrayOf.C = ScalaJS.makeAsArrayOfPrimitive(ScalaJS.isArrayOf.C, "C");
+ScalaJS.asArrayOf.B = ScalaJS.makeAsArrayOfPrimitive(ScalaJS.isArrayOf.B, "B");
+ScalaJS.asArrayOf.S = ScalaJS.makeAsArrayOfPrimitive(ScalaJS.isArrayOf.S, "S");
+ScalaJS.asArrayOf.I = ScalaJS.makeAsArrayOfPrimitive(ScalaJS.isArrayOf.I, "I");
+ScalaJS.asArrayOf.J = ScalaJS.makeAsArrayOfPrimitive(ScalaJS.isArrayOf.J, "J");
+ScalaJS.asArrayOf.F = ScalaJS.makeAsArrayOfPrimitive(ScalaJS.isArrayOf.F, "F");
+ScalaJS.asArrayOf.D = ScalaJS.makeAsArrayOfPrimitive(ScalaJS.isArrayOf.D, "D");
+//!endif
+
+// Polyfills
+
+ScalaJS.imul = ScalaJS.g["Math"]["imul"] || (function(a, b) {
+ // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul
+ var ah = (a >>> 16) & 0xffff;
+ var al = a & 0xffff;
+ var bh = (b >>> 16) & 0xffff;
+ var bl = b & 0xffff;
+ // the shift by 0 fixes the sign on the high part
+ // the final |0 converts the unsigned value into a signed value
+ return ((al * bl) + (((ah * bl + al * bh) << 16) >>> 0) | 0);
+});
+
+ScalaJS.fround = ScalaJS.g["Math"]["fround"] ||
+//!if floats == Strict
+ (ScalaJS.g["Float32Array"] ? (function(v) {
+ var array = new ScalaJS.g["Float32Array"](1);
+ array[0] = v;
+ return array[0];
+ }) : (function(v) {
+ return ScalaJS.m.sjsr_package().froundPolyfill__D__D(+v);
+ }));
+//!else
+ (function(v) {
+ return +v;
+ });
+//!endif
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/CompleteClasspath.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/CompleteClasspath.scala
new file mode 100644
index 0000000..6646a7b
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/CompleteClasspath.scala
@@ -0,0 +1,35 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.classpath
+
+import scala.collection.immutable.Seq
+
+import scala.scalajs.tools.io.VirtualJSFile
+import scala.scalajs.tools.jsdep.ResolutionInfo
+
+/** A classpath where nothing is missing. Therefore:
+ * - All JS libraries are resolved and ordered
+ * - The CoreJSLibs are present
+ * - Nothing can be added anymore
+ */
+abstract class CompleteClasspath(
+ /** Resolved JS libraries */
+ val jsLibs: Seq[ResolvedJSDependency],
+ val requiresDOM: Boolean,
+ val version: Option[String]
+) {
+
+ /** Fully linked Scala.js code */
+ def scalaJSCode: VirtualJSFile
+
+ /** All code in this complete classpath */
+ final def allCode: Seq[VirtualJSFile] = jsLibs.map(_.lib) :+ scalaJSCode
+
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/ComplianceRequirement.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/ComplianceRequirement.scala
new file mode 100644
index 0000000..f6ec36f
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/ComplianceRequirement.scala
@@ -0,0 +1,7 @@
+package scala.scalajs.tools.classpath
+
+import scala.scalajs.tools.jsdep.Origin
+
+/** Expresses a requirement for a given semantic to be compliant */
+final class ComplianceRequirement(
+ val semantics: String, val origins: List[Origin])
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/Exceptions.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/Exceptions.scala
new file mode 100644
index 0000000..62bf75f
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/Exceptions.scala
@@ -0,0 +1,42 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.classpath
+
+import scala.scalajs.tools.jsdep.ResolutionInfo
+
+class MissingJSLibException(val dependencies: List[ResolutionInfo])
+ extends Exception(MissingJSLibException.mkMsg(dependencies))
+
+object MissingJSLibException {
+ private def mkMsg(deps: List[ResolutionInfo]): String = {
+ val msg = new StringBuilder()
+ msg.append("Missing dependencies: \n")
+ for (d <- deps) {
+ msg.append(s"- ${d.resourceName}")
+ msg.append(s" originating from: ${d.origins.mkString(", ")}\n")
+ }
+ msg.toString()
+ }
+}
+
+class BadComplianceException(val unmet: List[ComplianceRequirement])
+ extends Exception(BadComplianceException.mkMsg(unmet))
+
+object BadComplianceException {
+ private def mkMsg(unmets: List[ComplianceRequirement]): String = {
+ val msg = new StringBuilder()
+ msg.append("Unmet required semantic compliance(s): \n")
+ for (unmet <- unmets) {
+ msg.append(s"- ${unmet.semantics}")
+ msg.append(s" originating from: ${unmet.origins.mkString(", ")}\n")
+ }
+ msg.toString
+ }
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/IRClasspath.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/IRClasspath.scala
new file mode 100644
index 0000000..a92293b
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/IRClasspath.scala
@@ -0,0 +1,69 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.classpath
+
+import scala.collection.immutable.{Seq, Traversable}
+
+import scala.scalajs.tools.sem.Semantics
+import scala.scalajs.tools.io._
+import scala.scalajs.tools.logging._
+import scala.scalajs.tools.optimizer.ScalaJSOptimizer
+import scala.scalajs.tools.jsdep.ResolutionInfo
+
+/** A [[CompleteClasspath]] that contains only IR as scalaJSCode */
+final class IRClasspath(
+ /** The JS libraries the IR code depends on */
+ jsLibs: Seq[ResolvedJSDependency],
+ val requiredCompliance: Traversable[ComplianceRequirement],
+ /** The IR itself. Ancestor count is used for later ordering */
+ val scalaJSIR: Traversable[VirtualScalaJSIRFile],
+ requiresDOM: Boolean,
+ version: Option[String]
+) extends CompleteClasspath(jsLibs, requiresDOM, version) {
+
+ /** Orders and optimizes the contained IR.
+ *
+ * Consider using [[ScalaJSOptimizer]] for a canonical way to do so. It
+ * allows to persist the resulting file and create a source map, as well as
+ * using non-default [[Semantics]].
+ */
+ override lazy val scalaJSCode: VirtualJSFile = {
+ import ScalaJSOptimizer._
+
+ val outName = "temporary-fastOpt.js"
+
+ if (scalaJSIR.nonEmpty) {
+ val semantics = Semantics.compliantTo(requiredCompliance.map(_.semantics))
+ val output = WritableMemVirtualJSFile(outName)
+ new ScalaJSOptimizer(semantics).optimizeCP(
+ Inputs(this),
+ OutputConfig(output),
+ NullLogger)
+ output
+ } else {
+ // We cannot run the optimizer without IR, because it will complain about
+ // java.lang.Object missing. However, an empty JS file is perfectly valid
+ // for no IR at all.
+ VirtualJSFile.empty(outName)
+ }
+ }
+
+ /** Checks whether the given semantics are compliant with the requirements of
+ * this CompleteClasspath. Throws an exception otherwise.
+ */
+ final def checkCompliance(semantics: Semantics): Unit = {
+ val unmet = requiredCompliance filterNot { compliance =>
+ semantics.isCompliant(compliance.semantics)
+ }
+
+ if (unmet.nonEmpty)
+ throw new BadComplianceException(unmet.toList)
+ }
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/LinkedClasspath.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/LinkedClasspath.scala
new file mode 100644
index 0000000..3ace785
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/LinkedClasspath.scala
@@ -0,0 +1,26 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.classpath
+
+import scala.scalajs.tools.io.VirtualJSFile
+import scala.scalajs.tools.jsdep.ResolutionInfo
+
+import scala.collection.immutable.Seq
+
+/** A [[CompleteClasspath]] that is fully linked (either with the
+ * [[ScalaJSOptimizer]] or the Closure Optimizer. It contains only a single
+ * file that is scalaJSCode.
+ */
+final class LinkedClasspath(
+ jsLibs: Seq[ResolvedJSDependency],
+ val scalaJSCode: VirtualJSFile,
+ requiresDOM: Boolean,
+ version: Option[String]
+) extends CompleteClasspath(jsLibs, requiresDOM, version)
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/PartialClasspath.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/PartialClasspath.scala
new file mode 100644
index 0000000..949cd6e
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/PartialClasspath.scala
@@ -0,0 +1,99 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.classpath
+
+import scala.collection.immutable.{Seq, Traversable}
+
+import scala.scalajs.tools.jsdep._
+import scala.scalajs.tools.io._
+import scala.scalajs.tools.corelib.CoreJSLibs
+
+/** A partial Scala.js classpath is a collection of:
+ * - Scala.js binary files *.sjsir
+ * - Native JavaScript libraries
+ * - Description of dependencies on other JavaScript libraries
+ *
+ * PartialClasspaths can be combined (using [[merge]]) and eventually resolved
+ * to a [[CompleteIRClasspath]]
+ */
+final class PartialClasspath(
+ /** Description of JS libraries the content of this classpath depends on */
+ val dependencies: Traversable[JSDependencyManifest],
+ /** JS libraries this partial classpath provides */
+ val availableLibs: Map[String, VirtualJSFile],
+ /** Scala.js IR contained in this PartialClasspath (unordered) */
+ val scalaJSIR: Traversable[VirtualScalaJSIRFile],
+ val version: Option[String]
+) {
+ import PartialClasspath.DependencyFilter
+
+ /** Merges another [[PartialClasspath]] with this one. This means:
+ * - Concatenate/merge dependencies
+ * - Merge availableLibs (libs in that shadow libs in this)
+ * - Merge Scala.js IR
+ */
+ def merge(that: PartialClasspath): PartialClasspath = {
+ new PartialClasspath(
+ this.dependencies ++ that.dependencies,
+ this.availableLibs ++ that.availableLibs,
+ this.scalaJSIR ++ that.scalaJSIR,
+ CacheUtils.joinVersions(this.version, that.version))
+ }
+
+ /** Construct a [[IRClasspath]] out of this [[PartialClasspath]] by
+ * resolving library dependencies (and failing if they are not met)
+ */
+ def resolve(filter: DependencyFilter = identity): IRClasspath = {
+ new IRClasspath(resolveDependencies(filter), mergeCompliance(), scalaJSIR,
+ dependencies.exists(_.requiresDOM), version)
+ }
+
+ /** Constructs an ordered list of JS libraries to include. Fails if:
+ * - Dependencies have cycles
+ * - Not all dependencies are available
+ */
+ protected def resolveDependencies(
+ filter: DependencyFilter): List[ResolvedJSDependency] = {
+ val flatDeps = filter(dependencies.flatMap(_.flatten))
+ val includeList = JSDependencyManifest.createIncludeList(flatDeps)
+
+ val missingDeps = includeList.filterNot { info =>
+ availableLibs.contains(info.resourceName)
+ }
+
+ if (missingDeps.nonEmpty)
+ throw new MissingJSLibException(missingDeps)
+
+ for (info <- includeList)
+ yield new ResolvedJSDependency(availableLibs(info.resourceName), info)
+ }
+
+ protected def mergeCompliance(): Traversable[ComplianceRequirement] = {
+ val flatTups = for {
+ dependency <- dependencies
+ semantics <- dependency.compliantSemantics
+ } yield (semantics, dependency.origin)
+
+ for {
+ (semantics, tups) <- flatTups.groupBy(_._1)
+ } yield new ComplianceRequirement(semantics, tups.map(_._2).toList)
+ }
+
+}
+
+object PartialClasspath {
+
+ type DependencyFilter =
+ Traversable[FlatJSDependency] => Traversable[FlatJSDependency]
+
+ /** Creates an empty PartialClasspath */
+ def empty: PartialClasspath =
+ new PartialClasspath(Nil, Map.empty, Nil, Some(""))
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/ResolvedJSDependency.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/ResolvedJSDependency.scala
new file mode 100644
index 0000000..12ae8dc
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/ResolvedJSDependency.scala
@@ -0,0 +1,10 @@
+package scala.scalajs.tools.classpath
+
+import scala.scalajs.tools.io._
+import scala.scalajs.tools.jsdep._
+
+/** A dependency on a native JavaScript library that has been successfully
+ * resolved
+ */
+final class ResolvedJSDependency(
+ val lib: VirtualJSFile, val info: ResolutionInfo)
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/AbstractJarLibClasspathBuilder.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/AbstractJarLibClasspathBuilder.scala
new file mode 100644
index 0000000..77f8509
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/AbstractJarLibClasspathBuilder.scala
@@ -0,0 +1,53 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.classpath.builder
+
+import scala.collection.mutable
+
+import scala.scalajs.tools.io._
+import scala.scalajs.tools.jsdep.JSDependencyManifest
+import scala.scalajs.tools.classpath._
+
+/** reads a ScalaJS library JAR into a CP
+ * - IR files go to scalaJSCode
+ * - JS files go to availableLibs
+ * - Reads a potential top-level JS_DEPENDENCIES file
+ */
+trait AbstractJarLibClasspathBuilder extends JarTraverser {
+
+ private val irFiles = mutable.ListBuffer.empty[VirtualScalaJSIRFile]
+ private val jsFiles = mutable.Map.empty[String, VirtualJSFile]
+ private var dependency: Option[JSDependencyManifest] = None
+
+ def build(jar: File): PartialClasspath = {
+ val v = traverseJar(jar)
+ new PartialClasspath(dependency.toList,
+ jsFiles.toMap, irFiles.toList, Some(v))
+ }
+
+ override protected def handleIR(relPath: String,
+ ir: => VirtualScalaJSIRFile): Unit = {
+ // We don't need to implement shadowing here: We have only a single JAR
+ irFiles += ir
+ }
+
+ override protected def handleJS(js: => VirtualJSFile): Unit = {
+ val file = js
+ if (!jsFiles.contains(file.name))
+ jsFiles += file.name -> file
+ }
+
+ override protected def handleDepManifest(m: => JSDependencyManifest): Unit = {
+ if (dependency.isDefined)
+ sys.error("A JAR cannot have multiple JS dependency manifests")
+ dependency = Some(m)
+ }
+
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/AbstractPartialClasspathBuilder.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/AbstractPartialClasspathBuilder.scala
new file mode 100644
index 0000000..9889f4c
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/AbstractPartialClasspathBuilder.scala
@@ -0,0 +1,47 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.classpath.builder
+
+import scala.scalajs.tools.jsdep.JSDependencyManifest
+import scala.scalajs.tools.classpath._
+import scala.scalajs.tools.io._
+
+import scala.collection.mutable
+import scala.collection.immutable.Seq
+
+trait AbstractPartialClasspathBuilder extends ClasspathContentHandler
+ with ClasspathElementsTraverser {
+
+ private val jsDepManifests = mutable.ListBuffer.empty[JSDependencyManifest]
+ private val irFiles = mutable.Map.empty[String, VirtualScalaJSIRFile]
+ private val otherJSFiles = mutable.Map.empty[String, VirtualJSFile]
+
+ override protected def handleIR(relPath: String,
+ ir: => VirtualScalaJSIRFile): Unit = {
+ if (!irFiles.contains(relPath))
+ irFiles += relPath -> ir
+ }
+
+ override protected def handleJS(js: => VirtualJSFile): Unit = {
+ val file = js
+ if (!otherJSFiles.contains(file.name))
+ otherJSFiles += file.name -> file
+ }
+
+ override protected def handleDepManifest(m: => JSDependencyManifest): Unit = {
+ jsDepManifests += m
+ }
+
+ def build(cp: Seq[File]): PartialClasspath = {
+ val version = traverseClasspathElements(cp)
+ new PartialClasspath(jsDepManifests.toList, otherJSFiles.toMap,
+ irFiles.values.toList, Some(version))
+ }
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/ClasspathContentHandler.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/ClasspathContentHandler.scala
new file mode 100644
index 0000000..71106c2
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/ClasspathContentHandler.scala
@@ -0,0 +1,25 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.classpath.builder
+
+import scala.scalajs.tools.io._
+import scala.scalajs.tools.jsdep._
+import scala.scalajs.tools.classpath._
+
+import java.io._
+
+import scala.collection.immutable.Seq
+
+/** Base-trait used by traversers to handle content with callbacks */
+trait ClasspathContentHandler {
+ protected def handleIR(relPath: String, ir: => VirtualScalaJSIRFile): Unit
+ protected def handleJS(js: => VirtualJSFile): Unit
+ protected def handleDepManifest(m: => JSDependencyManifest): Unit
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/ClasspathElementsTraverser.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/ClasspathElementsTraverser.scala
new file mode 100644
index 0000000..9403ce3
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/ClasspathElementsTraverser.scala
@@ -0,0 +1,38 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.classpath.builder
+
+import scala.scalajs.tools.io._
+
+/** A helper trait to traverse an arbitrary classpath element
+ * (i.e. a JAR or a directory).
+ */
+trait ClasspathElementsTraverser extends JarTraverser
+ with DirTraverser
+ with FileSystem {
+
+ protected def traverseClasspathElements(cp: Seq[File]): String =
+ CacheUtils.joinVersions(cp.map(readEntriesInClasspathElement _): _*)
+
+ /** Adds the Scala.js classpath entries in a directory or jar.
+ * Returns the accumulated version
+ */
+ private def readEntriesInClasspathElement(element: File): String = {
+ if (!exists(element))
+ getDummyVersion(element)
+ else if (isDirectory(element))
+ traverseDir(element)
+ else if (isJARFile(element))
+ traverseJar(element)
+ else
+ sys.error(s"$element (in classpath) exists and is neither JAR or directory")
+ }
+
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/DirTraverser.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/DirTraverser.scala
new file mode 100644
index 0000000..6609b29
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/DirTraverser.scala
@@ -0,0 +1,60 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.classpath.builder
+
+import scala.scalajs.tools.io._
+import scala.scalajs.tools.jsdep.JSDependencyManifest
+
+import scala.collection.mutable
+
+trait DirTraverser extends ClasspathContentHandler with FileSystem {
+
+ /** Traverses elements, returns a version string */
+ protected def traverseDir(dir: File): String = {
+ val versions = mutable.SortedSet.empty[String]
+
+ recurseDir(dir, "", versions)
+
+ // Construct version
+ CacheUtils.joinVersions(versions.toSeq: _*)
+ }
+
+ /** Recursively adds the Scala.js classpath entries in a directory */
+ private def recurseDir(dir: File, dirPath: String,
+ versions: mutable.SortedSet[String]): Unit = {
+ val files = listFiles(dir)
+ for (file <- files) {
+ val name = getName(file)
+ if (isDirectory(file)) {
+ recurseDir(file, dirPath + name + "/", versions)
+ } else {
+ val path = dirPath + name
+ path match {
+ case JSDependencyManifest.ManifestFileName =>
+ versions += getGlobalVersion(file)
+ val reader = toReader(file)
+ try handleDepManifest(JSDependencyManifest.read(reader))
+ finally reader.close()
+
+ case _ if isJSFile(file) =>
+ versions += getGlobalVersion(file)
+ handleJS(toJSFile(file))
+
+ case _ if isIRFile(file) =>
+ versions += getGlobalVersion(file)
+ handleIR(path, toIRFile(file))
+
+ case _ => // ignore other files
+ }
+ }
+ }
+ }
+
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/FileSystem.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/FileSystem.scala
new file mode 100644
index 0000000..99a8ca2
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/FileSystem.scala
@@ -0,0 +1,57 @@
+package scala.scalajs.tools.classpath.builder
+
+import scala.scalajs.tools.io._
+
+import scala.collection.immutable.Traversable
+
+import java.io.{InputStream, Reader}
+
+/** Abstraction of a FileSystem, so classpath builders can be used with virtual
+ * file systems
+ */
+trait FileSystem {
+
+ type File
+
+ /** Dummy version constant to identify files for which a version can not be
+ * found.
+ * This constant should never collide with the result of getVersion.
+ */
+ val DummyVersion: String
+
+ def isDirectory(f: File): Boolean
+ def isFile(f: File): Boolean
+ def isJSFile(f: File): Boolean
+ def isIRFile(f: File): Boolean
+ def isJARFile(f: File): Boolean
+ def exists(f: File): Boolean
+
+ def getName(f: File): String
+ /** A string that uniquely identifies this file's location */
+ def getAbsolutePath(f: File): String
+ /** A string that identifies the version of a file: If it equals the version
+ * of another file with the same absolute path, the two files must be equal.
+ * This is usually the lastModified date, but ordering is not required
+ */
+ def getVersion(f: File): String
+ /** A string that globally identifies the version of a file: If it equals the
+ * global version of any other file, they must equal.
+ */
+ def getGlobalVersion(f: File): String =
+ CacheUtils.joinVersions(getAbsolutePath(f), getVersion(f))
+
+ /** A string that globally identifies a file for which a version can not be
+ * found. Example: a file that does not exists.
+ */
+ def getDummyVersion(f: File): String =
+ CacheUtils.joinVersions(getAbsolutePath(f), DummyVersion)
+
+ /** List files in a directory */
+ def listFiles(d: File): Traversable[File]
+
+ def toJSFile(f: File): VirtualJSFile
+ def toIRFile(f: File): VirtualScalaJSIRFile
+ def toReader(f: File): Reader
+ def toInputStream(f: File): InputStream
+
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/JarTraverser.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/JarTraverser.scala
new file mode 100644
index 0000000..bbf5270
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/JarTraverser.scala
@@ -0,0 +1,85 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.classpath.builder
+
+import scala.collection.mutable
+import scala.annotation.tailrec
+
+import java.util.zip._
+import java.io.{InputStream, InputStreamReader, Reader}
+
+import scala.scalajs.tools.io._
+import scala.scalajs.tools.jsdep.JSDependencyManifest
+
+trait JarTraverser extends ClasspathContentHandler with FileSystem {
+
+ private val jsFiles = mutable.Map.empty[String, MemVirtualJSFile]
+
+ /** Traverse a Jar and return a version */
+ protected def traverseJar(jar: File): String = {
+ val zipStream = new ZipInputStream(toInputStream(jar))
+
+ try readEntries(zipStream, getAbsolutePath(jar))
+ finally zipStream.close()
+
+ for {
+ (_, jsFile) <- jsFiles
+ if jsFile.content != "" // drop if this is just a lone sourcemap
+ } handleJS(jsFile)
+
+ getGlobalVersion(jar)
+ }
+
+ private def getOrCreateJSFile(relPath: String, fullPath: String, fn: String) =
+ jsFiles.getOrElseUpdate(relPath, new MemVirtualJSFile(fullPath) {
+ override val name = fn
+ })
+
+ @tailrec
+ private def readEntries(in: ZipInputStream, jarPath: String): Unit = {
+ val entry = in.getNextEntry()
+ if (entry != null) {
+ readEntry(entry, in, jarPath)
+ readEntries(in, jarPath)
+ }
+ }
+
+ private def readEntry(entry: ZipEntry, in: InputStream, jarPath: String) = {
+ val longName = entry.getName
+ val shortName = VirtualFile.nameFromPath(longName)
+ val fullPath = jarPath + "#" + longName
+
+ def entryReader: Reader = new InputStreamReader(in, "UTF-8")
+ def entryContent: String = IO.readInputStreamToString(in)
+ def entryBinaryContent: Array[Byte] = IO.readInputStreamToByteArray(in)
+ def entryVersion: Option[String] = Some(entry.getTime.toString)
+
+ if (longName == JSDependencyManifest.ManifestFileName)
+ handleDepManifest(JSDependencyManifest.read(entryReader))
+ else if (longName.endsWith(".js")) {
+ val relPath = longName
+ getOrCreateJSFile(relPath, fullPath, shortName)
+ .withContent(entryContent)
+ .withVersion(entryVersion)
+ } else if (longName.endsWith(".js.map")) {
+ // assume the source map of a JS file
+ val relPath = longName.dropRight(".map".length)
+ getOrCreateJSFile(relPath, fullPath, shortName)
+ .withSourceMap(Some(entryContent))
+ } else if (longName.endsWith(".sjsir")) {
+ val vf = new MemVirtualSerializedScalaJSIRFile(fullPath) {
+ override val name = shortName
+ }.withContent(entryBinaryContent)
+ .withVersion(entryVersion)
+
+ handleIR(longName, vf)
+ }
+ }
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/corelib/CoreJSLibs.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/corelib/CoreJSLibs.scala
new file mode 100644
index 0000000..ecbea1f
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/corelib/CoreJSLibs.scala
@@ -0,0 +1,113 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.corelib
+
+import java.net.URI
+
+import scala.scalajs.ir.ScalaJSVersions
+import scala.scalajs.tools.io._
+
+import scala.scalajs.tools.sem._
+
+import scala.collection.immutable.Seq
+import scala.collection.mutable
+
+object CoreJSLibs {
+
+ private val cachedLibBySemantics =
+ mutable.HashMap.empty[Semantics, VirtualJSFile]
+
+ private val ScalaJSEnvLines =
+ ScalaJSEnvHolder.scalajsenv.split("\n|\r\n?")
+
+ private val gitHubBaseURI =
+ new URI("https://raw.githubusercontent.com/scala-js/scala-js/")
+
+ def libs(semantics: Semantics): Seq[VirtualJSFile] = synchronized {
+ Seq(cachedLibBySemantics.getOrElseUpdate(semantics, makeLib(semantics)))
+ }
+
+ private def makeLib(semantics: Semantics): VirtualJSFile = {
+ new ScalaJSEnvVirtualJSFile(makeContent(semantics))
+ }
+
+ private def makeContent(semantics: Semantics): String = {
+ // This is a basic sort-of-C-style preprocessor
+
+ def getOption(name: String): String = name match {
+ case "asInstanceOfs" =>
+ semantics.asInstanceOfs.toString()
+ case "floats" =>
+ if (semantics.strictFloats) "Strict"
+ else "Loose"
+ }
+
+ var skipping = false
+ var skipDepth = 0
+ val lines = for (line <- ScalaJSEnvLines) yield {
+ val includeThisLine = if (skipping) {
+ if (line == "//!else" && skipDepth == 1) {
+ skipping = false
+ skipDepth = 0
+ } else if (line == "//!endif") {
+ skipDepth -= 1
+ if (skipDepth == 0)
+ skipping = false
+ } else if (line.startsWith("//!if ")) {
+ skipDepth += 1
+ }
+ false
+ } else {
+ if (line.startsWith("//!")) {
+ if (line.startsWith("//!if ")) {
+ val Array(_, option, op, value) = line.split(" ")
+ val optionValue = getOption(option)
+ val success = op match {
+ case "==" => optionValue == value
+ case "!=" => optionValue != value
+ }
+ if (!success) {
+ skipping = true
+ skipDepth = 1
+ }
+ } else if (line == "//!else") {
+ skipping = true
+ skipDepth = 1
+ } else if (line == "//!endif") {
+ // nothing to do
+ } else {
+ throw new MatchError(line)
+ }
+ false
+ } else {
+ true
+ }
+ }
+ if (includeThisLine) line
+ else "" // blank line preserves line numbers in source maps
+ }
+
+ lines.mkString("", "\n", "\n")
+ }
+
+ private class ScalaJSEnvVirtualJSFile(override val content: String) extends VirtualJSFile {
+ override def path: String = "scalajsenv.js"
+ override def version: Option[String] = Some("")
+ override def exists: Boolean = true
+
+ override def toURI: URI = {
+ if (!ScalaJSVersions.currentIsSnapshot)
+ gitHubBaseURI.resolve(s"v${ScalaJSVersions.current}/tools/$path")
+ else
+ super.toURI
+ }
+ }
+
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/AsyncJSEnv.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/AsyncJSEnv.scala
new file mode 100644
index 0000000..d439ae2
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/AsyncJSEnv.scala
@@ -0,0 +1,19 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.env
+
+import scala.scalajs.tools.io._
+import scala.scalajs.tools.classpath._
+import scala.scalajs.tools.logging._
+
+trait AsyncJSEnv extends JSEnv {
+ def asyncRunner(classpath: CompleteClasspath, code: VirtualJSFile,
+ logger: Logger, console: JSConsole): AsyncJSRunner
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/AsyncJSRunner.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/AsyncJSRunner.scala
new file mode 100644
index 0000000..09e2dda
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/AsyncJSRunner.scala
@@ -0,0 +1,19 @@
+package scala.scalajs.tools.env
+
+import scala.concurrent.Future
+
+trait AsyncJSRunner {
+ /** Start the associated run and returns a Future that completes when the run
+ * terminates.
+ */
+ def start(): Future[Unit]
+
+ /** Abort the associated run */
+ def stop(): Unit
+
+ /** Checks whether this async runner is still running */
+ def isRunning(): Boolean
+
+ /** Await completion of the started Run. Throws if the run failed */
+ def await(): Unit
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/ComJSEnv.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/ComJSEnv.scala
new file mode 100644
index 0000000..882e46a
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/ComJSEnv.scala
@@ -0,0 +1,38 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.env
+
+import scala.scalajs.tools.io._
+import scala.scalajs.tools.classpath._
+import scala.scalajs.tools.logging._
+
+/** An [[AsyncJSEnv]] that provides communication to and from the JS VM.
+ *
+ * Inside the VM there is a global JavaScript object named `scalajsCom` that
+ * can be used to control the message channel. It's operations are:
+ * {{{
+ * // initialize com (with callback)
+ * scalajsCom.init(function(msg) { console.log("Received: " + msg); });
+ *
+ * // send a message to host system
+ * scalajsCom.send("my message");
+ *
+ * // close com (releases callback, allowing VM to terminate)
+ * scalajsCom.close();
+ * }}}
+ */
+trait ComJSEnv extends AsyncJSEnv {
+ def comRunner(classpath: CompleteClasspath, code: VirtualJSFile,
+ logger: Logger, console: JSConsole): ComJSRunner
+}
+
+object ComJSEnv {
+ class ComClosedException extends Exception("JSCom has been closed")
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/ComJSRunner.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/ComJSRunner.scala
new file mode 100644
index 0000000..44302b8
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/ComJSRunner.scala
@@ -0,0 +1,22 @@
+package scala.scalajs.tools.env
+
+trait ComJSRunner extends AsyncJSRunner {
+
+ /** Send a message to the JS VM. Throws if the message cannot be sent. */
+ def send(msg: String): Unit
+
+ /** Block until a message is received. Throws a [[ComClosedExcpetion]]
+ * if the channel is closed before a message is received.
+ */
+ def receive(): String
+
+ /** Close the communication channel. Allows the VM to terminate if it is
+ * still waiting for callback. The JVM side **must** call close in
+ * order to be able to expect termination of the VM.
+ *
+ * Calling [[stop]] on a [ComJSRunner]] automatically closes the
+ * channel.
+ */
+ def close(): Unit
+
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/ConsoleJSConsole.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/ConsoleJSConsole.scala
new file mode 100644
index 0000000..5b3d055
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/ConsoleJSConsole.scala
@@ -0,0 +1,17 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.env
+
+/** A JS console that prints on the console */
+object ConsoleJSConsole extends JSConsole {
+ override def log(msg: Any): Unit = {
+ Console.println(msg)
+ }
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/JSConsole.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/JSConsole.scala
new file mode 100644
index 0000000..a93768f
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/JSConsole.scala
@@ -0,0 +1,15 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.env
+
+/** Trait representing a JS console */
+trait JSConsole {
+ def log(msg: Any): Unit
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/JSEnv.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/JSEnv.scala
new file mode 100644
index 0000000..f1fbf44
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/JSEnv.scala
@@ -0,0 +1,20 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.env
+
+import scala.scalajs.tools.io._
+import scala.scalajs.tools.classpath._
+import scala.scalajs.tools.logging._
+
+trait JSEnv {
+ /** Prepare a runner for the code in the virtual file. */
+ def jsRunner(classpath: CompleteClasspath, code: VirtualJSFile,
+ logger: Logger, console: JSConsole): JSRunner
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/JSRunner.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/JSRunner.scala
new file mode 100644
index 0000000..460fff0
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/JSRunner.scala
@@ -0,0 +1,15 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.env
+
+trait JSRunner {
+ /** Run the associated JS code. Throw if an error occurs. */
+ def run(): Unit
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/NullJSConsole.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/NullJSConsole.scala
new file mode 100644
index 0000000..8147bbe
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/NullJSConsole.scala
@@ -0,0 +1,5 @@
+package scala.scalajs.tools.env
+
+object NullJSConsole extends JSConsole {
+ def log(msg: Any): Unit = {}
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/io/CacheUtils.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/io/CacheUtils.scala
new file mode 100644
index 0000000..14773f8
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/io/CacheUtils.scala
@@ -0,0 +1,52 @@
+package scala.scalajs.tools.io
+
+object CacheUtils {
+
+ def joinVersions(vs: Option[String]*): Option[String] = {
+ val bld = new StringBuilder
+
+ @scala.annotation.tailrec
+ def loop(vs: Seq[Option[String]]): Option[String] = {
+ vs match {
+ case Some(v) :: vss =>
+ bld.append(mangleVersionString(v))
+ loop(vss)
+ case None :: _ =>
+ None
+ case Nil =>
+ Some(bld.toString)
+ }
+ }
+
+ loop(vs.toList)
+ }
+
+ def joinVersions(vs: String*): String =
+ vs.map(mangleVersionString _).mkString
+
+ private def mangleVersionString(str: String) = s"${str.length}:$str"
+
+ def cached(version: Option[String], output: VirtualFile,
+ cache: Option[WritableVirtualTextFile])(action: => Unit): Unit = {
+
+ val upToDate = output.exists && (
+ for {
+ v <- version
+ c <- cache if c.exists
+ } yield c.content == v
+ ).getOrElse(false)
+
+ // Are we outdated?
+ if (!upToDate) {
+ action
+
+ // Write cache
+ for (c <- cache; v <- version) {
+ val w = c.contentWriter
+ try w.write(v)
+ finally w.close()
+ }
+ }
+ }
+
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/io/IO.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/io/IO.scala
new file mode 100644
index 0000000..b69b07c
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/io/IO.scala
@@ -0,0 +1,116 @@
+package scala.scalajs.tools.io
+
+import scala.annotation.tailrec
+
+import scala.reflect.ClassTag
+
+import java.io._
+
+object IO {
+ /** Returns the lines in an input stream.
+ * Lines do not contain the new line characters.
+ */
+ def readLines(stream: InputStream): List[String] =
+ readLines(new InputStreamReader(stream))
+
+ /** Returns the lines in a string.
+ * Lines do not contain the new line characters.
+ */
+ def readLines(content: String): List[String] =
+ readLines(new StringReader(content))
+
+ /** Returns the lines in a reader.
+ * Lines do not contain the new line characters.
+ */
+ def readLines(reader: Reader): List[String] = {
+ val br = new BufferedReader(reader)
+ try {
+ val builder = List.newBuilder[String]
+ @tailrec
+ def loop(): Unit = {
+ val line = br.readLine()
+ if (line ne null) {
+ builder += line
+ loop()
+ }
+ }
+ loop()
+ builder.result()
+ } finally {
+ br.close()
+ }
+ }
+
+ /** Reads the entire content of a reader as a string. */
+ def readReaderToString(reader: Reader): String = {
+ val buffer = newBuffer[Char]
+ val builder = new StringBuilder
+ @tailrec
+ def loop(): Unit = {
+ val len = reader.read(buffer)
+ if (len > 0) {
+ builder.appendAll(buffer, 0, len)
+ loop()
+ }
+ }
+ loop()
+ builder.toString()
+ }
+
+ /** Reads the entire content of an input stream as a UTF-8 string. */
+ def readInputStreamToString(stream: InputStream): String = {
+ val reader = new BufferedReader(new InputStreamReader(stream, "UTF-8"))
+ readReaderToString(reader)
+ }
+
+ /** Reads the entire content of an input stream as a byte array. */
+ def readInputStreamToByteArray(stream: InputStream): Array[Byte] = {
+ val builder = new ByteArrayOutputStream()
+ val buffer = newBuffer[Byte]
+ @tailrec
+ def loop(): Unit = {
+ val size = stream.read(buffer)
+ if (size > 0) {
+ builder.write(buffer, 0, size)
+ loop()
+ }
+ }
+ loop()
+ builder.toByteArray()
+ }
+
+ /** Concatenates a bunch of VirtualTextFiles to a WritableVirtualTextFile.
+ * Adds a '\n' after each file.
+ */
+ def concatFiles(output: WritableVirtualTextFile,
+ files: Seq[VirtualTextFile]): Unit = {
+ val buffer = newBuffer[Char]
+ val out = output.contentWriter
+
+ try {
+ for (file <- files) {
+ val reader = file.reader
+
+ @tailrec
+ def loop(): Unit = {
+ val size = reader.read(buffer)
+ if (size > 0) {
+ out.write(buffer, 0, size)
+ loop()
+ }
+ }
+
+ try loop()
+ finally reader.close()
+
+ // New line after each file
+ out.write('\n')
+ }
+ } finally {
+ out.close()
+ }
+ }
+
+ @inline
+ private def newBuffer[T : ClassTag] = new Array[T](4096)
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/io/MemFiles.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/io/MemFiles.scala
new file mode 100644
index 0000000..68f66dc
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/io/MemFiles.scala
@@ -0,0 +1,105 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.io
+
+import java.io._
+
+/** A base class for simple in-memory mutable virtual files. */
+class MemVirtualFile(val path: String) extends VirtualFile {
+ private[this] var _version: Option[String] = None
+
+ override def version: Option[String] = _version
+ def version_=(v: Option[String]): Unit = _version = v
+
+ final def withVersion(v: Option[String]): this.type = {
+ version = v
+ this
+ }
+
+ override def exists: Boolean = true
+}
+
+/** A simple in-memory mutable virtual text file. */
+class MemVirtualTextFile(p: String) extends MemVirtualFile(p)
+ with VirtualTextFile {
+ private[this] var _content: String = ""
+
+ override def content: String = _content
+ def content_=(v: String): Unit = _content = v
+
+ final def withContent(v: String): this.type = {
+ content = v
+ this
+ }
+}
+
+trait WritableMemVirtualTextFile extends MemVirtualTextFile
+ with WritableVirtualTextFile {
+ def contentWriter: Writer = new StringWriter {
+ override def close(): Unit = {
+ super.close()
+ WritableMemVirtualTextFile.this.content = this.toString
+ }
+ }
+}
+
+object WritableMemVirtualTextFile {
+ def apply(path: String): WritableMemVirtualTextFile =
+ new MemVirtualTextFile(path) with WritableMemVirtualTextFile
+}
+
+/** A simple in-memory mutable virtual binary file. */
+class MemVirtualBinaryFile(p: String) extends MemVirtualFile(p)
+ with VirtualBinaryFile {
+ private[this] var _content: Array[Byte] = new Array[Byte](0)
+
+ override def content: Array[Byte] = _content
+ def content_=(v: Array[Byte]): Unit = _content = v
+
+ final def withContent(v: Array[Byte]): this.type = {
+ content = v
+ this
+ }
+}
+
+/** A simple in-memory mutable virtual JS file. */
+class MemVirtualJSFile(p: String) extends MemVirtualTextFile(p)
+ with VirtualJSFile {
+ private[this] var _sourceMap: Option[String] = None
+
+ override def sourceMap: Option[String] = _sourceMap
+ def sourceMap_=(v: Option[String]): Unit = _sourceMap = v
+
+ final def withSourceMap(v: Option[String]): this.type = {
+ sourceMap = v
+ this
+ }
+}
+
+trait WritableMemVirtualJSFile extends MemVirtualJSFile
+ with WritableVirtualJSFile
+ with WritableMemVirtualTextFile {
+
+ def sourceMapWriter: Writer = new StringWriter {
+ override def close(): Unit = {
+ super.close()
+ WritableMemVirtualJSFile.this.sourceMap = Some(this.toString)
+ }
+ }
+}
+
+object WritableMemVirtualJSFile {
+ def apply(path: String): WritableMemVirtualJSFile =
+ new MemVirtualJSFile(path) with WritableMemVirtualJSFile
+}
+
+/** A simple in-memory mutable virtual serialized Scala.js IR file. */
+class MemVirtualSerializedScalaJSIRFile(p: String) extends MemVirtualBinaryFile(p)
+ with VirtualSerializedScalaJSIRFile
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/io/VirtualFiles.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/io/VirtualFiles.scala
new file mode 100644
index 0000000..c62ab5c
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/io/VirtualFiles.scala
@@ -0,0 +1,169 @@
+package scala.scalajs.tools.io
+
+import java.io._
+import java.net.URI
+
+import scala.scalajs.ir
+import scala.scalajs.tools.sourcemap._
+
+/** A virtual input file.
+ */
+trait VirtualFile {
+ /** Path of the file, including everything.
+ * Unique if possible (used for lookup). */
+ def path: String
+
+ /** Name of the file/writer, including extension */
+ def name: String = VirtualFile.nameFromPath(path)
+
+ /** Optionally returns an implementation-dependent "version" token.
+ * Versions are compared with ==.
+ * If non-empty, a different version must be returned when the content
+ * changes. It should be equal if the content has not changed, but it is
+ * not mandatory.
+ * Such a token can be used by caches: the file need not be read and
+ * processed again if its version has not changed.
+ */
+ def version: Option[String] = None
+
+ /** Whether this file exists. Reading a non-existent file may fail */
+ def exists: Boolean
+
+ /** URI for this virtual file */
+ def toURI: URI = {
+ new URI(
+ "virtualfile", // Pseudo-Scheme
+ path, // Scheme specific part
+ null // Fragment
+ )
+ }
+}
+
+object VirtualFile {
+ /** Splits at the last slash and returns remainder */
+ def nameFromPath(path: String): String = {
+ val pos = path.lastIndexOf('/')
+ if (pos == -1) path
+ else path.substring(pos + 1)
+ }
+}
+
+/** A virtual input file.
+ */
+trait VirtualTextFile extends VirtualFile {
+ /** Returns the content of the file. */
+ def content: String
+
+ /** Returns a new Reader of the file. */
+ def reader: Reader = new StringReader(content)
+
+ /** Returns the lines in the content.
+ * Lines do not contain the new line characters.
+ */
+ def readLines(): List[String] = IO.readLines(reader)
+}
+
+object VirtualTextFile {
+ def empty(path: String): VirtualTextFile =
+ new MemVirtualTextFile(path)
+}
+
+trait WritableVirtualTextFile extends VirtualTextFile {
+ def contentWriter: Writer
+}
+
+/** A virtual binary input file.
+ */
+trait VirtualBinaryFile extends VirtualFile {
+ /** Returns the content of the file. */
+ def content: Array[Byte]
+
+ /** Returns a new InputStream of the file. */
+ def inputStream: InputStream = new ByteArrayInputStream(content)
+}
+
+/** A virtual input file which contains JavaScript code.
+ * It may have a source map associated with it.
+ */
+trait VirtualJSFile extends VirtualTextFile {
+ /** Optionally, content of the source map file associated with this
+ * JavaScript source.
+ */
+ def sourceMap: Option[String] = None
+}
+
+object VirtualJSFile {
+ def empty(path: String): VirtualJSFile =
+ new MemVirtualJSFile(path).withVersion(Some(path))
+}
+
+trait WritableVirtualJSFile extends WritableVirtualTextFile with VirtualJSFile {
+ def sourceMapWriter: Writer
+}
+
+/** A virtual Scala.js IR file.
+ * It contains the class info and the IR tree.
+ */
+trait VirtualScalaJSIRFile extends VirtualFile {
+ /** Rough class info of this file. */
+ def roughInfo: ir.Infos.RoughClassInfo = info
+
+ /** Class info of this file. */
+ def info: ir.Infos.ClassInfo =
+ infoAndTree._1
+
+ /** IR Tree of this file. */
+ def tree: ir.Trees.ClassDef =
+ infoAndTree._2
+
+ /** Class info and IR tree of this file. */
+ def infoAndTree: (ir.Infos.ClassInfo, ir.Trees.ClassDef)
+}
+
+/** Base trait for virtual Scala.js IR files that are serialized as binary file.
+ */
+trait VirtualSerializedScalaJSIRFile extends VirtualBinaryFile with VirtualScalaJSIRFile {
+ /** Rough class info of this file. */
+ override def roughInfo: ir.Infos.RoughClassInfo = {
+ // Overridden to read only the necessary parts
+ val stream = inputStream
+ try {
+ ir.InfoSerializers.deserializeRoughInfo(stream)
+ } catch {
+ case e: IOException =>
+ throw new IOException(s"Failed to deserialize rough info of $path", e)
+ } finally {
+ stream.close()
+ }
+ }
+
+ /** Class info of this file. */
+ override def info: ir.Infos.ClassInfo = {
+ // Overridden to read only the necessary parts
+ val stream = inputStream
+ try {
+ ir.InfoSerializers.deserializeFullInfo(stream)
+ } catch {
+ case e: IOException =>
+ throw new IOException(s"Failed to deserialize info of $path", e)
+ } finally {
+ stream.close()
+ }
+ }
+
+ /** Class info and IR tree of this file. */
+ override def infoAndTree: (ir.Infos.ClassInfo, ir.Trees.ClassDef) = {
+ val stream = inputStream
+ try {
+ val (version, info) = ir.InfoSerializers.deserializeVersionFullInfo(stream)
+ val tree = ir.Serializers.deserialize(
+ stream, version).asInstanceOf[ir.Trees.ClassDef]
+ (info, tree)
+ } catch {
+ case e: IOException =>
+ throw new IOException(s"Failed to deserialize $path", e)
+ } finally {
+ stream.close()
+ }
+ }
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/JSDesugaring.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/JSDesugaring.scala
new file mode 100644
index 0000000..b4d4005
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/JSDesugaring.scala
@@ -0,0 +1,1525 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.javascript
+
+import scala.language.implicitConversions
+
+import scala.annotation.switch
+
+import scala.scalajs.ir._
+import Position._
+import Transformers._
+import scala.scalajs.ir.Trees._
+import scala.scalajs.ir.Types._
+
+import scala.scalajs.tools.sem._
+import CheckedBehavior._
+
+import scala.scalajs.tools.javascript.{Trees => js}
+
+/** Desugaring of the IR to regular ES5 JavaScript.
+ *
+ * The major difference between the IR and JS is that most constructs can be
+ * used in expression position. The main work of the desugaring is to
+ * unnest complex constructs in expression position so that they become
+ * statements.
+ *
+ * The general idea is two-folded:
+ * 1) Unnest complex constructs in "argument position":
+ * When a complex construct is used in a non-rhs expression position
+ * (argument to a function, operand, condition of an if, etc.), that we
+ * call "argument position", declare a variable before the statement,
+ * assign the complex construct to it and then use that variable in the
+ * argument position instead.
+ * 2) Push LHS's inside complex RHS's:
+ * When an rhs is a complex construct, push the lhs inside the complex
+ * construct. Are considered lhs:
+ * * Assign, i.e., `x =`
+ * * VarDef, i.e., `var x =`
+ * * Return, i.e., `return`
+ * * (EmptyTree is also used as a trick for code reuse)
+ * In fact, think that, in this context, LHS means: what to do with the
+ * result of evaluating the RHS.
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Typical example, consider the method call:
+ *
+ * obj.meth({
+ * var x = foo(42);
+ * x*x
+ * });
+ *
+ * According to rule 1), the block that is passed as a parameter to obj.meth
+ * is first extracted in a synthetic var:
+ *
+ * var x\$1 = {
+ * var x = foo(42);
+ * x*x
+ * }
+ * obj.meth(x\$1);
+ *
+ * Then, according to rule 2), the lhs `var x\$1 =` is pushed inside the block:
+ *
+ * {
+ * var x = foo(42);
+ * var x\$1 = x*x;
+ * }
+ * obj.meth(x\$1);
+ *
+ * Because bare blocks are non-significant in JS, this is equivalent to
+ *
+ * var x = foo(42);
+ * var x\$1 = x*x;
+ * obj.meth(x\$1);
+ *
+ * --------------------------------------------------------------------------
+ *
+ * JSDesugaring does all this in a single pass, but it helps to think that:
+ * * Rule 1) is implemented by unnest(), and used most notably in
+ * * transformStat() for statement-only constructs
+ * * pushLhsInto() for statement-or-expression constructs
+ * * Rule 2) is implemented by pushLhsInto()
+ * * Emitting the class structure is delegated to [[ScalaJSClassEmitter]].
+ *
+ * There are a few other things that JSDesugaring takes care of:
+ * * Transform Scala expressions into their JS equivalent, taking the
+ * Scala.js class encoding into account.
+ * * And tiny details.
+ *
+ * @author Sébastien Doeraene
+ */
+object JSDesugaring {
+
+ private final val ScalaJSEnvironmentName = "ScalaJS"
+
+ /** Desugars a statement of the IR into ES5 JavaScript. */
+ def desugarJavaScript(tree: Tree, semantics: Semantics): js.Tree = {
+ new JSDesugar(semantics).transformStat(tree)
+ }
+
+ private[javascript] implicit def transformIdent(ident: Ident): js.Ident =
+ js.Ident(ident.name, ident.originalName)(ident.pos)
+
+ private[javascript] def transformParamDef(paramDef: ParamDef): js.ParamDef =
+ js.ParamDef(paramDef.name, paramDef.mutable)(paramDef.pos)
+
+ private class JSDesugar(semantics: Semantics) {
+
+ // Synthetic variables
+
+ var syntheticVarCounter: Int = 0
+
+ def newSyntheticVar()(implicit pos: Position): Ident = {
+ syntheticVarCounter += 1
+ Ident("jsx$" + syntheticVarCounter, None)
+ }
+
+ def resetSyntheticVarCounterIn[A](f: => A): A = {
+ val savedCounter = syntheticVarCounter
+ syntheticVarCounter = 0
+ try f
+ finally syntheticVarCounter = savedCounter
+ }
+
+ // Record names
+
+ def makeRecordFieldIdent(recIdent: Ident, fieldIdent: Ident)(
+ implicit pos: Position): Ident =
+ makeRecordFieldIdent(recIdent.name, recIdent.originalName,
+ fieldIdent.name, fieldIdent.originalName)
+
+ def makeRecordFieldIdent(recIdent: Ident,
+ fieldName: String, fieldOrigiName: Option[String])(
+ implicit pos: Position): Ident =
+ makeRecordFieldIdent(recIdent.name, recIdent.originalName,
+ fieldName, fieldOrigiName)
+
+ def makeRecordFieldIdent(recName: String, recOrigName: Option[String],
+ fieldName: String, fieldOrigName: Option[String])(
+ implicit pos: Position): Ident = {
+ val name = recName + "_$_" + fieldName
+ val originalName = Some(recOrigName.getOrElse(recName) + "." +
+ fieldOrigName.getOrElse(fieldName))
+ Ident(name, originalName)
+ }
+
+ // LHS'es for labeled expressions
+
+ var labeledExprLHSes: Map[Ident, Tree] = Map.empty
+
+ // Now the work
+
+ /** Desugar a statement of the IR into ES5 JS */
+ def transformStat(tree: Tree): js.Tree = {
+ implicit val pos = tree.pos
+
+ tree match {
+ // Statement-only language constructs
+
+ case Skip() =>
+ js.Skip()
+
+ case VarDef(varIdent, RecordType(fields), recMutable, EmptyTree) =>
+ js.Block(for {
+ RecordType.Field(fieldName, fieldOrigName, tpe, fieldMutable) <- fields
+ } yield {
+ transformStat {
+ VarDef(makeRecordFieldIdent(varIdent, fieldName, fieldOrigName),
+ tpe, recMutable || fieldMutable, EmptyTree)
+ }
+ })
+
+ case VarDef(name, _, mutable, EmptyTree) =>
+ js.VarDef(name, mutable, js.EmptyTree)
+
+ case VarDef(_, _, _, rhs) =>
+ pushLhsInto(tree, rhs)
+
+ case Assign(RecordFieldVarRef(lhs), rhs) =>
+ pushLhsInto(Assign(lhs, EmptyTree), rhs)
+
+ case Assign(select @ Select(qualifier, item, mutable), rhs) =>
+ unnest(qualifier, rhs) { (newQualifier, newRhs) =>
+ js.Assign(
+ js.DotSelect(transformExpr(newQualifier), item)(select.pos),
+ transformExpr(newRhs))
+ }
+
+ case Assign(select @ ArraySelect(array, index), rhs) =>
+ unnest(List(array, index, rhs)) {
+ case List(newArray, newIndex, newRhs) =>
+ js.Assign(
+ js.BracketSelect(js.DotSelect(transformExpr(newArray),
+ js.Ident("u"))(select.pos),
+ transformExpr(newIndex))(select.pos),
+ transformExpr(newRhs))
+ }
+
+ case Assign(select @ JSDotSelect(qualifier, item), rhs) =>
+ unnest(qualifier, rhs) { (newQualifier, newRhs) =>
+ js.Assign(
+ js.DotSelect(transformExpr(newQualifier), item)(select.pos),
+ transformExpr(newRhs))
+ }
+
+ case Assign(select @ JSBracketSelect(qualifier, item), rhs) =>
+ unnest(List(qualifier, item, rhs)) {
+ case List(newQualifier, newItem, newRhs) =>
+ js.Assign(
+ js.BracketSelect(transformExpr(newQualifier),
+ transformExpr(newItem))(select.pos),
+ transformExpr(newRhs))
+ }
+
+ case Assign(_ : VarRef, rhs) =>
+ pushLhsInto(tree, rhs)
+
+ case Assign(_, _) =>
+ sys.error(s"Illegal Assign in transformStat: $tree")
+
+ case StoreModule(cls, value) =>
+ assert(cls.className.endsWith("$"),
+ s"Trying to store module for non-module class $cls")
+ val moduleName = cls.className.dropRight(1)
+ unnest(value) { newValue =>
+ js.Assign(
+ js.DotSelect(envField("n"), Ident(moduleName)),
+ transformExpr(newValue))
+ }
+
+ case While(cond, body, label) =>
+ /* We cannot simply unnest(cond) here, because that would eject the
+ * evaluation of the condition out of the loop.
+ */
+ val newLabel = label.map(transformIdent)
+ if (isExpression(cond)) {
+ js.While(transformExpr(cond), transformStat(body), newLabel)
+ } else {
+ js.While(js.BooleanLiteral(true), {
+ unnest(cond) { newCond =>
+ js.If(transformExpr(newCond), transformStat(body), js.Break())
+ }
+ }, newLabel)
+ }
+
+ case DoWhile(body, cond, label) =>
+ /* We cannot simply unnest(cond) here, because that would eject the
+ * evaluation of the condition out of the loop.
+ */
+ val newLabel = label.map(transformIdent)
+ if (isExpression(cond)) {
+ js.DoWhile(transformStat(body), transformExpr(cond), newLabel)
+ } else {
+ /* This breaks 'continue' statements for this loop, but we don't
+ * care because we never emit continue statements for do..while
+ * loops.
+ */
+ js.While(js.BooleanLiteral(true), {
+ js.Block(transformStat(body), {
+ unnest(cond) { newCond =>
+ js.If(transformExpr(newCond), js.Skip(), js.Break())
+ }
+ })
+ }, newLabel)
+ }
+
+ case Debugger() =>
+ js.Debugger()
+
+ case JSDelete(JSDotSelect(obj, prop)) =>
+ unnest(obj) { (newObj) =>
+ js.Delete(js.DotSelect(transformExpr(newObj), prop))
+ }
+
+ case JSDelete(JSBracketSelect(obj, prop)) =>
+ unnest(obj, prop) { (newObj, newProp) =>
+ js.Delete(js.BracketSelect(
+ transformExpr(newObj), transformExpr(newProp)))
+ }
+
+ // Treat 'return' as an LHS
+
+ case Return(expr, label) =>
+ pushLhsInto(tree, expr)
+
+ /* Anything else is an expression => pushLhsInto(EmptyTree, _)
+ * In order not to duplicate all the code of pushLhsInto() here, we
+ * use a trick: EmptyTree is a dummy LHS that says "do nothing
+ * with the result of the rhs".
+ * This is exactly what an expression statement is doing: it evaluates
+ * the expression, but does nothing with its result.
+ */
+
+ case _ =>
+ pushLhsInto(EmptyTree, tree)
+ }
+ }
+
+ private object RecordFieldVarRef {
+ def unapply(tree: Tree): Option[VarRef] = {
+ tree match {
+ case Select(RecordVarRef(VarRef(recIdent, recMutable)),
+ fieldIdent, fieldMutable) =>
+ implicit val pos = tree.pos
+ Some(VarRef(makeRecordFieldIdent(recIdent, fieldIdent),
+ recMutable || fieldMutable)(tree.tpe))
+ case _ =>
+ None
+ }
+ }
+ }
+
+ private object RecordVarRef {
+ def unapply(tree: Tree): Option[VarRef] = {
+ if (!tree.tpe.isInstanceOf[RecordType]) None
+ else {
+ tree match {
+ case tree: VarRef => Some(tree)
+ case Select(RecordVarRef(VarRef(recIdent, recMutable)),
+ fieldIdent, fieldMutable) =>
+ implicit val pos = tree.pos
+ Some(VarRef(makeRecordFieldIdent(recIdent, fieldIdent),
+ recMutable || fieldMutable)(tree.tpe))
+ }
+ }
+ }
+ }
+
+ /** Unnest complex constructs in argument position in temporary variables
+ *
+ * If all the arguments are JS expressions, there is nothing to do.
+ * Any argument that is not a JS expression must be unnested and stored
+ * in a temporary variable before the statement produced by `makeStat`.
+ *
+ * But *this changes the evaluation order!* In order not to lose it, it
+ * is necessary to also unnest arguments that are expressions but that
+ * are supposed to be evaluated before the argument-to-be-unnested and
+ * could have side-effects or even whose evaluation could be influenced
+ * by the side-effects of another unnested argument.
+ *
+ * Without deep effect analysis, which we do not do, we need to take
+ * a very pessimistic approach, and unnest any expression that contains
+ * an identifier (except those after the last non-expression argument).
+ * Hence the predicate `isPureExpressionWithoutIdent`.
+ */
+ def unnest(args: List[Tree])(
+ makeStat: List[Tree] => js.Tree): js.Tree = {
+ if (args forall isExpression) makeStat(args)
+ else {
+ val extractedStatements = new scala.collection.mutable.ListBuffer[js.Tree]
+
+ /* Attention! Everything must be processed recursively
+ * *right-to-left*! Indeed, the point is that noExtractYet will tell
+ * whether anything supposed to be evaluated *after* the currently
+ * being processed expression has been (at least partly) extracted
+ * in temporary variables (or simply statements, in the Block case).
+ * If nothing has, we can keep more in place without having to extract
+ * that expression in a temporary variable.
+ */
+
+ def rec(arg: Tree): Tree = {
+ def noExtractYet = extractedStatements.isEmpty
+
+ if (if (noExtractYet) isExpression(arg) else isPureExpression(arg)) {
+ arg
+ } else {
+ implicit val pos = arg.pos
+ arg match {
+ case Block(stats :+ expr) =>
+ val result = rec(expr) // right-to-left, remember?
+ // Put the stats in a Block because ++=: is not smart
+ js.Block(stats.map(transformStat)) +=: extractedStatements
+ result
+
+ case UnaryOp(op, lhs) =>
+ UnaryOp(op, rec(lhs))
+ case BinaryOp(op, lhs, rhs) =>
+ val newRhs = rec(rhs)
+ BinaryOp(op, rec(lhs), newRhs)
+ case JSBinaryOp(op, lhs, rhs) =>
+ val newRhs = rec(rhs)
+ JSBinaryOp(op, rec(lhs), newRhs)
+ case JSUnaryOp(op, lhs) =>
+ JSUnaryOp(op, rec(lhs))
+ case IsInstanceOf(expr, tpe) =>
+ IsInstanceOf(rec(expr), tpe)
+
+ case AsInstanceOf(expr, tpe)
+ if noExtractYet || semantics.asInstanceOfs == Unchecked =>
+ AsInstanceOf(rec(expr), tpe)
+ case Unbox(expr, tpe)
+ if noExtractYet || semantics.asInstanceOfs == Unchecked =>
+ Unbox(rec(expr), tpe)
+
+ case NewArray(tpe, lengths) =>
+ NewArray(tpe, recs(lengths))
+ case ArrayValue(tpe, elems) =>
+ ArrayValue(tpe, recs(elems))
+ case JSArrayConstr(items) =>
+ JSArrayConstr(recs(items))
+ case JSObjectConstr(items) =>
+ val newValues = recs(items.map(_._2))
+ JSObjectConstr(items.map(_._1) zip newValues)
+ case Closure(captureParams, params, body, captureValues) =>
+ Closure(captureParams, params, body, recs(captureValues))
+
+ case New(cls, constr, args) if noExtractYet =>
+ New(cls, constr, recs(args))
+ case Select(qualifier, item, mutable) if noExtractYet =>
+ Select(rec(qualifier), item, mutable)(arg.tpe)
+ case Apply(receiver, method, args) if noExtractYet =>
+ val newArgs = recs(args)
+ Apply(rec(receiver), method, newArgs)(arg.tpe)
+ case StaticApply(receiver, cls, method, args) if noExtractYet =>
+ val newArgs = recs(args)
+ StaticApply(rec(receiver), cls, method, newArgs)(arg.tpe)
+ case TraitImplApply(impl, method, args) if noExtractYet =>
+ TraitImplApply(impl, method, recs(args))(arg.tpe)
+ case ArrayLength(array) if noExtractYet =>
+ ArrayLength(rec(array))
+ case ArraySelect(array, index) if noExtractYet =>
+ val newIndex = rec(index)
+ ArraySelect(rec(array), newIndex)(arg.tpe)
+ case CallHelper(helper, args) if noExtractYet =>
+ CallHelper(helper, recs(args))(arg.tpe)
+
+ case If(cond, thenp, elsep)
+ if noExtractYet && isExpression(thenp) && isExpression(elsep) =>
+ If(rec(cond), thenp, elsep)(arg.tpe)
+
+ case _ =>
+ val temp = newSyntheticVar()
+ val computeTemp =
+ pushLhsInto(VarDef(temp, arg.tpe, mutable = false, EmptyTree), arg)
+ computeTemp +=: extractedStatements
+ VarRef(temp, mutable = false)(arg.tpe)
+ }
+ }
+ }
+
+ def recs(args: List[Tree]): List[Tree] = {
+ // This is a right-to-left map
+ args.foldRight[List[Tree]](Nil) { (arg, acc) =>
+ rec(arg) :: acc
+ }
+ }
+
+ val newArgs = recs(args)
+
+ assert(extractedStatements.nonEmpty,
+ "Reached computeTemps with no temp to compute")
+
+ val newStatement = makeStat(newArgs)
+ js.Block(extractedStatements.result() ::: List(newStatement))(newStatement.pos)
+ }
+ }
+
+ /** Same as above, for a single argument */
+ def unnest(arg: Tree)(
+ makeStat: Tree => js.Tree): js.Tree = {
+ unnest(List(arg)) {
+ case List(newArg) => makeStat(newArg)
+ }
+ }
+
+ /** Same as above, for two arguments */
+ def unnest(lhs: Tree, rhs: Tree)(
+ makeStat: (Tree, Tree) => js.Tree): js.Tree = {
+ unnest(List(lhs, rhs)) {
+ case List(newLhs, newRhs) => makeStat(newLhs, newRhs)
+ }
+ }
+
+ /** Same as above, for one head argument and a list of arguments */
+ def unnest(arg0: Tree, args: List[Tree])(
+ makeStat: (Tree, List[Tree]) => js.Tree): js.Tree = {
+ unnest(arg0 :: args) { newArgs =>
+ makeStat(newArgs.head, newArgs.tail)
+ }
+ }
+
+ /** Common implementation for the functions below.
+ * A pure expression can be moved around or executed twice, because it
+ * will always produce the same result and never have side-effects.
+ * A side-effect free expression can be elided if its result is not used.
+ */
+ private def isExpressionInternal(tree: Tree,
+ allowUnpure: Boolean, allowSideEffects: Boolean): Boolean = {
+
+ require(!allowSideEffects || allowUnpure)
+
+ def test(tree: Tree): Boolean = tree match {
+ // Atomic expressions
+ case _: Literal => true
+ case _: This => true
+ case _: JSEnvInfo => true
+
+ // Vars and fields (side-effect free, pure if immutable)
+ case VarRef(_, mutable) =>
+ allowUnpure || !mutable
+ case Select(qualifier, item, mutable) =>
+ (allowUnpure || !mutable) && test(qualifier)
+
+ // Expressions preserving pureness
+ 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 JSBinaryOp(_, lhs, rhs) => test(lhs) && test(rhs)
+ case JSUnaryOp(_, lhs) => test(lhs)
+ case ArrayLength(array) => test(array)
+ case IsInstanceOf(expr, _) => test(expr)
+
+ // Expressions preserving side-effect freedom
+ case NewArray(tpe, lengths) =>
+ allowUnpure && (lengths forall test)
+ case ArrayValue(tpe, elems) =>
+ allowUnpure && (elems forall test)
+ case ArraySelect(array, index) =>
+ allowUnpure && test(array) && test(index)
+ case JSArrayConstr(items) =>
+ allowUnpure && (items forall test)
+ case JSObjectConstr(items) =>
+ allowUnpure && (items forall (item => test(item._2)))
+ case Closure(captureParams, params, body, captureValues) =>
+ allowUnpure && (captureValues forall test)
+
+ // Scala expressions that can always have side-effects
+ case New(cls, constr, args) =>
+ allowSideEffects && (args forall test)
+ case LoadModule(cls) => // unfortunately
+ allowSideEffects
+ case Apply(receiver, method, args) =>
+ allowSideEffects && test(receiver) && (args forall test)
+ case StaticApply(receiver, cls, method, args) =>
+ allowSideEffects && test(receiver) && (args forall test)
+ case TraitImplApply(impl, method, args) =>
+ allowSideEffects && (args forall test)
+ case GetClass(arg) =>
+ allowSideEffects && test(arg)
+ case CallHelper(helper, args) =>
+ allowSideEffects && (args forall test)
+
+ // Casts
+ case AsInstanceOf(expr, _) =>
+ (allowSideEffects || semantics.asInstanceOfs == Unchecked) && test(expr)
+ case Unbox(expr, _) =>
+ (allowSideEffects || semantics.asInstanceOfs == Unchecked) && test(expr)
+
+ // Because the env is a frozen object, env["global"] is pure
+ case JSBracketSelect(JSEnvInfo(), StringLiteral("global")) => true
+
+ // JavaScript expressions that can always have side-effects
+ case JSNew(fun, args) =>
+ allowSideEffects && test(fun) && (args forall test)
+ case JSDotSelect(qualifier, item) =>
+ allowSideEffects && test(qualifier)
+ case JSBracketSelect(qualifier, item) =>
+ allowSideEffects && test(qualifier) && test(item)
+ case JSFunctionApply(fun, args) =>
+ allowSideEffects && test(fun) && (args forall test)
+ case JSDotMethodApply(receiver, method, args) =>
+ allowSideEffects && test(receiver) && (args forall test)
+ case JSBracketMethodApply(receiver, method, args) =>
+ allowSideEffects && test(receiver) && test(method) && (args forall test)
+
+ // Non-expressions
+ case _ => false
+ }
+ test(tree)
+ }
+
+ /** Test whether the given tree is a standard JS expression.
+ */
+ def isExpression(tree: Tree): Boolean =
+ isExpressionInternal(tree, allowUnpure = true, allowSideEffects = true)
+
+ /** Test whether the given tree is a side-effect-free standard JS expression.
+ */
+ def isSideEffectFreeExpression(tree: Tree): Boolean =
+ isExpressionInternal(tree, allowUnpure = true, allowSideEffects = false)
+
+ /** Test whether the given tree is a pure standard JS expression.
+ */
+ def isPureExpression(tree: Tree): Boolean =
+ isExpressionInternal(tree, allowUnpure = false, allowSideEffects = false)
+
+ def doVarDef(ident: Ident, tpe: Type, mutable: Boolean, rhs: Tree): js.Tree = {
+ implicit val pos = rhs.pos
+ tpe match {
+ case RecordType(fields) =>
+ val elems = (rhs: @unchecked) match {
+ case RecordValue(_, elems) =>
+ elems
+ case VarRef(rhsIdent, rhsMutable) =>
+ for (RecordType.Field(fName, fOrigName, fTpe, fMutable) <- fields)
+ yield VarRef(makeRecordFieldIdent(rhsIdent, fName, fOrigName),
+ rhsMutable || fMutable)(fTpe)
+ }
+ js.Block(for {
+ (RecordType.Field(fName, fOrigName, fTpe, fMutable),
+ fRhs) <- fields zip elems
+ } yield {
+ doVarDef(makeRecordFieldIdent(ident, fName, fOrigName),
+ fTpe, mutable || fMutable, fRhs)
+ })
+
+ case _ =>
+ js.VarDef(ident, mutable, transformExpr(rhs))
+ }
+ }
+
+ def doAssign(lhs: Tree, rhs: Tree): js.Tree = {
+ implicit val pos = rhs.pos
+ lhs.tpe match {
+ case RecordType(fields) =>
+ val VarRef(ident, mutable) = lhs
+ val elems = (rhs: @unchecked) match {
+ case VarRef(rhsIdent, rhsMutable) =>
+ for (RecordType.Field(fName, fOrigName, fTpe, fMutable) <- fields)
+ yield VarRef(makeRecordFieldIdent(rhsIdent, fName, fOrigName),
+ rhsMutable || fMutable)(fTpe)
+ }
+ js.Block(for {
+ (RecordType.Field(fName, fOrigName, fTpe, fMutable),
+ fRhs) <- fields zip elems
+ } yield {
+ doAssign(VarRef(makeRecordFieldIdent(ident, fName, fOrigName),
+ mutable || fMutable)(fTpe), fRhs)
+ })
+
+ case _ =>
+ js.Assign(transformExpr(lhs), transformExpr(rhs))
+ }
+ }
+
+ /** Push an lhs into a (potentially complex) rhs
+ * lhs can be either a EmptyTree, a VarDef, a Assign or a
+ * Return
+ */
+ def pushLhsInto(lhs: Tree, rhs: Tree): js.Tree = {
+ implicit val rhsPos = rhs.pos
+
+ /** Push the current lhs further into a deeper rhs */
+ @inline def redo(newRhs: Tree) = pushLhsInto(lhs, newRhs)
+
+ if (rhs.tpe == NothingType && lhs != EmptyTree) {
+ /* A touch of peephole dead code elimination.
+ * Actually necessary to handle pushing an lhs into an infinite loop,
+ * for example.
+ */
+ val transformedRhs = pushLhsInto(EmptyTree, rhs)
+ lhs match {
+ case VarDef(name, _, mutable, _) =>
+ /* We still need to declare the var, in case it is used somewhere
+ * else in the function, where we can't dce it.
+ */
+ js.Block(js.VarDef(name, true, js.EmptyTree), transformedRhs)
+ case _ =>
+ transformedRhs
+ }
+ } else (rhs match {
+ // Handle the Block before testing whether it is an expression
+
+ case Block(stats :+ expr) =>
+ js.Block((stats map transformStat) :+ redo(expr))
+
+ // Base case, rhs is already a regular JS expression
+
+ case _ if isExpression(rhs) =>
+ (lhs: @unchecked) match {
+ case EmptyTree =>
+ if (isSideEffectFreeExpression(rhs)) js.Skip()
+ else transformExpr(rhs)
+ case VarDef(name, tpe, mutable, _) =>
+ doVarDef(name, tpe, mutable, rhs)
+ case Assign(lhs, _) =>
+ doAssign(lhs, rhs)
+ case Return(_, None) =>
+ js.Return(transformExpr(rhs))
+ case Return(_, label @ Some(l)) =>
+ labeledExprLHSes(l) match {
+ case newLhs @ Return(_, _) =>
+ pushLhsInto(newLhs, rhs) // no need to break here
+ case newLhs =>
+ js.Block(pushLhsInto(newLhs, rhs),
+ js.Break(label.map(transformIdent)))
+ }
+ }
+
+ // Almost base case with RecordValue
+
+ case RecordValue(recTpe, elems) =>
+ (lhs: @unchecked) match {
+ case EmptyTree =>
+ js.Block(elems map transformStat)
+ case VarDef(name, tpe, mutable, _) =>
+ unnest(elems) { newElems =>
+ doVarDef(name, tpe, mutable, RecordValue(recTpe, newElems))
+ }
+ case Assign(lhs, _) =>
+ unnest(elems) { newElems =>
+ val temp = newSyntheticVar()
+ js.Block(
+ doVarDef(temp, recTpe, false, RecordValue(recTpe, newElems)),
+ doAssign(lhs, VarRef(temp, false)(recTpe)))
+ }
+ case Return(_, label @ Some(l)) =>
+ val newLhs = labeledExprLHSes(l)
+ js.Block(pushLhsInto(newLhs, rhs),
+ js.Break(label.map(transformIdent)))
+ }
+
+ // Control flow constructs
+
+ case Labeled(label, tpe, body) =>
+ val savedMap = labeledExprLHSes
+ labeledExprLHSes = labeledExprLHSes + (label -> lhs)
+ try {
+ lhs match {
+ case Return(_, _) => redo(body)
+ case _ => js.Labeled(label, redo(body))
+ }
+ } finally {
+ labeledExprLHSes = savedMap
+ }
+
+ case Return(expr, _) =>
+ pushLhsInto(rhs, expr)
+
+ case Continue(label) =>
+ js.Continue(label.map(transformIdent))
+
+ case If(cond, thenp, elsep) =>
+ unnest(cond) { newCond =>
+ js.If(transformExpr(newCond), redo(thenp), redo(elsep))
+ }
+
+ case Try(block, errVar, handler, finalizer) =>
+ val newHandler =
+ if (handler == EmptyTree) js.EmptyTree else redo(handler)
+ val newFinalizer =
+ if (finalizer == EmptyTree) js.EmptyTree else transformStat(finalizer)
+
+ if (newHandler != js.EmptyTree && newFinalizer != js.EmptyTree) {
+ /* The Google Closure Compiler wrongly eliminates finally blocks, if
+ * the catch block throws an exception.
+ * Issues: #563, google/closure-compiler#186
+ *
+ * Therefore, we desugar
+ *
+ * try { ... } catch { ... } finally { ... }
+ *
+ * into
+ *
+ * try { try { ... } catch { ... } } finally { ... }
+ */
+ js.Try(js.Try(redo(block), errVar, newHandler, js.EmptyTree),
+ errVar, js.EmptyTree, newFinalizer)
+ } else
+ js.Try(redo(block), errVar, newHandler, newFinalizer)
+
+ // TODO Treat throw as an LHS?
+ case Throw(expr) =>
+ unnest(expr) { newExpr =>
+ js.Throw(transformExpr(newExpr))
+ }
+
+ /** Matches are desugared into switches
+ *
+ * A match is different from a switch in two respects, both linked
+ * to match being designed to be used in expression position in
+ * Extended-JS.
+ *
+ * * There is no fall-through from one case to the next one, hence,
+ * no break statement.
+ * * Match supports _alternatives_ explicitly (with a switch, one
+ * would use the fall-through behavior to implement alternatives).
+ */
+ case Match(selector, cases, default) =>
+ unnest(selector) { newSelector =>
+ val newCases = {
+ for {
+ (values, body) <- cases
+ newValues = (values map transformExpr)
+ // add the break statement
+ newBody = js.Block(redo(body), js.Break())
+ // desugar alternatives into several cases falling through
+ caze <- (newValues.init map (v => (v, js.Skip()))) :+ (newValues.last, newBody)
+ } yield {
+ caze
+ }
+ }
+ val newDefault =
+ if (default == EmptyTree) js.EmptyTree
+ else redo(default)
+ js.Switch(transformExpr(newSelector), newCases, newDefault)
+ }
+
+ // Scala expressions (if we reach here their arguments are not expressions)
+
+ case New(cls, ctor, args) =>
+ unnest(args) { newArgs =>
+ redo(New(cls, ctor, newArgs))
+ }
+
+ case Select(qualifier, item, mutable) =>
+ unnest(qualifier) { newQualifier =>
+ redo(Select(newQualifier, item, mutable)(rhs.tpe))
+ }
+
+ case Apply(receiver, method, args) =>
+ unnest(receiver, args) { (newReceiver, newArgs) =>
+ redo(Apply(newReceiver, method, newArgs)(rhs.tpe))
+ }
+
+ case StaticApply(receiver, cls, method, args) =>
+ unnest(receiver, args) { (newReceiver, newArgs) =>
+ redo(StaticApply(newReceiver, cls, method, newArgs)(rhs.tpe))
+ }
+
+ case TraitImplApply(impl, method, args) =>
+ unnest(args) { newArgs =>
+ redo(TraitImplApply(impl, method, newArgs)(rhs.tpe))
+ }
+
+ case UnaryOp(op, lhs) =>
+ unnest(lhs) { newLhs =>
+ redo(UnaryOp(op, newLhs))
+ }
+
+ case BinaryOp(op, lhs, rhs) =>
+ unnest(lhs, rhs) { (newLhs, newRhs) =>
+ redo(BinaryOp(op, newLhs, newRhs))
+ }
+
+ case NewArray(tpe, lengths) =>
+ unnest(lengths) { newLengths =>
+ redo(NewArray(tpe, newLengths))
+ }
+
+ case ArrayValue(tpe, elems) =>
+ unnest(elems) { newElems =>
+ redo(ArrayValue(tpe, newElems))
+ }
+
+ case ArrayLength(array) =>
+ unnest(array) { newArray =>
+ redo(ArrayLength(newArray))
+ }
+
+ case ArraySelect(array, index) =>
+ unnest(array, index) { (newArray, newIndex) =>
+ redo(ArraySelect(newArray, newIndex)(rhs.tpe))
+ }
+
+ case IsInstanceOf(expr, cls) =>
+ unnest(expr) { newExpr =>
+ redo(IsInstanceOf(newExpr, cls))
+ }
+
+ case AsInstanceOf(expr, cls) =>
+ if (semantics.asInstanceOfs == Unchecked) {
+ redo(expr)
+ } else {
+ unnest(expr) { newExpr =>
+ redo(AsInstanceOf(newExpr, cls))
+ }
+ }
+
+ case Unbox(expr, charCode) =>
+ unnest(expr) { newExpr =>
+ redo(Unbox(newExpr, charCode))
+ }
+
+ case GetClass(expr) =>
+ unnest(expr) { newExpr =>
+ redo(GetClass(newExpr))
+ }
+
+ case CallHelper(helper, args) =>
+ unnest(args) { newArgs =>
+ redo(CallHelper(helper, newArgs)(rhs.tpe))
+ }
+
+ // JavaScript expressions (if we reach here their arguments are not expressions)
+
+ case JSNew(ctor, args) =>
+ unnest(ctor :: args) { newCtorAndArgs =>
+ val newCtor :: newArgs = newCtorAndArgs
+ redo(JSNew(newCtor, newArgs))
+ }
+
+ case JSFunctionApply(fun, args) =>
+ unnest(fun :: args) { newFunAndArgs =>
+ val newFun :: newArgs = newFunAndArgs
+ redo(JSFunctionApply(newFun, newArgs))
+ }
+
+ case JSDotMethodApply(receiver, method, args) =>
+ unnest(receiver :: args) { newReceiverAndArgs =>
+ val newReceiver :: newArgs = newReceiverAndArgs
+ redo(JSDotMethodApply(newReceiver, method, newArgs))
+ }
+
+ case JSBracketMethodApply(receiver, method, args) =>
+ unnest(receiver :: method :: args) { newReceiverAndArgs =>
+ val newReceiver :: newMethod :: newArgs = newReceiverAndArgs
+ redo(JSBracketMethodApply(newReceiver, newMethod, newArgs))
+ }
+
+ case JSDotSelect(qualifier, item) =>
+ unnest(qualifier) { newQualifier =>
+ redo(JSDotSelect(newQualifier, item))
+ }
+
+ case JSBracketSelect(qualifier, item) =>
+ unnest(qualifier, item) { (newQualifier, newItem) =>
+ redo(JSBracketSelect(newQualifier, newItem))
+ }
+
+ case JSUnaryOp(op, lhs) =>
+ unnest(lhs) { newLhs =>
+ redo(JSUnaryOp(op, newLhs))
+ }
+
+ case JSBinaryOp("&&", lhs, rhs) =>
+ if (lhs.tpe == BooleanType) {
+ redo(If(lhs, rhs, BooleanLiteral(false))(AnyType))
+ } else {
+ unnest(lhs) { newLhs =>
+ redo(If(newLhs, rhs, newLhs)(AnyType))
+ }
+ }
+
+ case JSBinaryOp("||", lhs, rhs) =>
+ if (lhs.tpe == BooleanType) {
+ redo(If(lhs, BooleanLiteral(true), rhs)(AnyType))
+ } else {
+ unnest(lhs) { newLhs =>
+ redo(If(newLhs, newLhs, rhs)(AnyType))
+ }
+ }
+
+ case JSBinaryOp(op, lhs, rhs) =>
+ unnest(lhs, rhs) { (newLhs, newRhs) =>
+ redo(JSBinaryOp(op, newLhs, newRhs))
+ }
+
+ case JSArrayConstr(items) =>
+ unnest(items) { newItems =>
+ redo(JSArrayConstr(newItems))
+ }
+
+ case JSObjectConstr(fields) =>
+ val names = fields map (_._1)
+ val items = fields map (_._2)
+ unnest(items) { newItems =>
+ redo(JSObjectConstr(names.zip(newItems)))
+ }
+
+ // Closures
+
+ case Closure(captureParams, params, body, captureValues) =>
+ unnest(captureValues) { newCaptureValues =>
+ redo(Closure(captureParams, params, body, newCaptureValues))
+ }
+
+ case _ =>
+ if (lhs == EmptyTree) {
+ /* Go "back" to transformStat() after having dived into
+ * expression statements. Remember that (lhs == EmptyTree)
+ * is a trick that we use to "add" all the code of pushLhsInto()
+ * to transformStat().
+ */
+ rhs match {
+ case _:Skip | _:VarDef | _:Assign | _:While | _:DoWhile |
+ _:Debugger | _:JSDelete | _:StoreModule | _:ClassDef =>
+ transformStat(rhs)
+ case _ =>
+ sys.error("Illegal tree in JSDesugar.pushLhsInto():\n" +
+ "lhs = " + lhs + "\n" + "rhs = " + rhs +
+ " of class " + rhs.getClass)
+ }
+ } else {
+ sys.error("Illegal tree in JSDesugar.pushLhsInto():\n" +
+ "lhs = " + lhs + "\n" + "rhs = " + rhs +
+ " of class " + rhs.getClass)
+ }
+ })
+ }
+
+ // Desugar Scala operations to JavaScript operations -----------------------
+
+ /** Desugar an expression of the IR into ES5 JS */
+ def transformExpr(tree: Tree): js.Tree = {
+ import TreeDSL._
+
+ implicit val pos = tree.pos
+
+ def or0(tree: js.Tree): js.Tree =
+ js.BinaryOp("|", tree, js.IntLiteral(0))
+
+ tree match {
+ // Control flow constructs
+
+ case Block(stats :+ expr) =>
+ js.Block((stats map transformStat) :+ transformExpr(expr))
+
+ // Note that these work even if thenp/elsep is not a BooleanType
+ case If(cond, BooleanLiteral(true), elsep) =>
+ js.BinaryOp("||", transformExpr(cond), transformExpr(elsep))
+ case If(cond, thenp, BooleanLiteral(false)) =>
+ js.BinaryOp("&&", transformExpr(cond), transformExpr(thenp))
+
+ case If(cond, thenp, elsep) =>
+ js.If(transformExpr(cond), transformExpr(thenp), transformExpr(elsep))
+
+ // Scala expressions
+
+ case New(cls, ctor, args) =>
+ js.Apply(js.New(encodeClassVar(cls.className), Nil) DOT ctor,
+ args map transformExpr)
+
+ case LoadModule(cls) =>
+ genLoadModule(cls.className)
+
+ case RecordFieldVarRef(VarRef(name, mutable)) =>
+ js.VarRef(name, mutable)
+
+ case Select(qualifier, item, _) =>
+ transformExpr(qualifier) DOT item
+
+ case Apply(receiver, method, args) =>
+ val newReceiver = transformExpr(receiver)
+ val newArgs = args map transformExpr
+ if (isMaybeHijackedClass(receiver.tpe) &&
+ !Definitions.isReflProxyName(method.name)) {
+ val helperName = hijackedClassMethodToHelperName(method.name)
+ genCallHelper(helperName, newReceiver :: newArgs: _*)
+ } else {
+ js.Apply(newReceiver DOT method, newArgs)
+ }
+
+ case StaticApply(receiver, cls, method, args) =>
+ val fun = encodeClassVar(cls.className).prototype DOT method
+ js.Apply(fun DOT "call", (receiver :: args) map transformExpr)
+
+ case TraitImplApply(impl, method, args) =>
+ js.Apply(envField("i") DOT method, args map transformExpr)
+
+ case UnaryOp(op, lhs) =>
+ import UnaryOp._
+ val newLhs = transformExpr(lhs)
+ (op: @switch) match {
+ case `typeof` => js.UnaryOp("typeof", newLhs)
+ case Boolean_! => js.UnaryOp("!", newLhs)
+ case DoubleToInt => js.BinaryOp("|", newLhs, js.IntLiteral(0))
+
+ case LongToInt => genLongMethodApply(newLhs, LongImpl.toInt)
+ case LongToDouble => genLongMethodApply(newLhs, LongImpl.toDouble)
+
+ case DoubleToFloat => genFround(newLhs)
+
+ case IntToLong =>
+ genNewLong(LongImpl.initFromInt, newLhs)
+ case DoubleToLong =>
+ genLongModuleApply(LongImpl.fromDouble, newLhs)
+ }
+
+ case BinaryOp(op, lhs, rhs) =>
+ import BinaryOp._
+ val lhs1 = lhs match {
+ case UnaryOp(UnaryOp.DoubleToInt, inner)
+ if op == Int_& || op == Int_<< =>
+ /* This case is emitted typically by conversions from
+ * Float/Double to Char/Byte/Short. We have to introduce an
+ * (int) cast in the IR so that it typechecks, but in JavaScript
+ * this is redundant because & and << already convert both their
+ * operands to ints. So we get rid of the conversion here.
+ */
+ inner
+ case _ =>
+ lhs
+ }
+
+ val newLhs = transformExpr(lhs1)
+ val newRhs = transformExpr(rhs)
+
+ (op: @switch) match {
+ case === | Num_== | Boolean_== => js.BinaryOp("===", newLhs, newRhs)
+ case !== | Num_!= | Boolean_!= => js.BinaryOp("!==", newLhs, newRhs)
+
+ case String_+ =>
+ if (lhs.tpe == StringType || rhs.tpe == StringType)
+ js.BinaryOp("+", newLhs, newRhs)
+ else
+ js.BinaryOp("+", js.BinaryOp("+", js.StringLiteral(""), newLhs), newRhs)
+
+ case `in` => js.BinaryOp("in", newLhs, newRhs)
+ case `instanceof` => js.BinaryOp("instanceof", newLhs, newRhs)
+
+ case Int_+ => or0(js.BinaryOp("+", newLhs, newRhs))
+ case Int_- =>
+ lhs match {
+ case IntLiteral(0) => or0(js.UnaryOp("-", newRhs))
+ case _ => or0(js.BinaryOp("-", newLhs, newRhs))
+ }
+ case Int_* => genCallHelper("imul", newLhs, newRhs)
+ case Int_/ => or0(js.BinaryOp("/", newLhs, newRhs))
+ case Int_% => js.BinaryOp("%", newLhs, newRhs)
+
+ case Int_| => js.BinaryOp("|", newLhs, newRhs)
+ case Int_& => js.BinaryOp("&", newLhs, newRhs)
+ case Int_^ =>
+ lhs match {
+ case IntLiteral(-1) => js.UnaryOp("~", newRhs)
+ case _ => js.BinaryOp("^", newLhs, newRhs)
+ }
+ case Int_<< => js.BinaryOp("<<", newLhs, newRhs)
+ case Int_>>> => or0(js.BinaryOp(">>>", newLhs, newRhs))
+ case Int_>> => js.BinaryOp(">>", newLhs, newRhs)
+
+ case Float_+ => genFround(js.BinaryOp("+", newLhs, newRhs))
+ case Float_- =>
+ genFround(lhs match {
+ case DoubleLiteral(0.0) => js.UnaryOp("-", newRhs)
+ case _ => js.BinaryOp("-", newLhs, newRhs)
+ })
+ case Float_* => genFround(js.BinaryOp("*", newLhs, newRhs))
+ case Float_/ => genFround(js.BinaryOp("/", newLhs, newRhs))
+ case Float_% => genFround(js.BinaryOp("%", newLhs, newRhs))
+
+ case Double_+ => js.BinaryOp("+", newLhs, newRhs)
+ case Double_- =>
+ lhs match {
+ case DoubleLiteral(0.0) => js.UnaryOp("-", newRhs)
+ case _ => js.BinaryOp("-", newLhs, newRhs)
+ }
+ case Double_* => js.BinaryOp("*", newLhs, newRhs)
+ case Double_/ => js.BinaryOp("/", newLhs, newRhs)
+ case Double_% => js.BinaryOp("%", newLhs, newRhs)
+
+ case Num_< => js.BinaryOp("<", newLhs, newRhs)
+ case Num_<= => js.BinaryOp("<=", newLhs, newRhs)
+ case Num_> => js.BinaryOp(">", newLhs, newRhs)
+ case Num_>= => js.BinaryOp(">=", newLhs, newRhs)
+
+ case Long_+ => genLongMethodApply(newLhs, LongImpl.+, newRhs)
+ case Long_- =>
+ lhs match {
+ case LongLiteral(0L) => genLongMethodApply(newRhs, LongImpl.UNARY_-)
+ case _ => genLongMethodApply(newLhs, LongImpl.-, newRhs)
+ }
+ case Long_* => genLongMethodApply(newLhs, LongImpl.*, newRhs)
+ case Long_/ => genLongMethodApply(newLhs, LongImpl./, newRhs)
+ case Long_% => genLongMethodApply(newLhs, LongImpl.%, newRhs)
+
+ case Long_| => genLongMethodApply(newLhs, LongImpl.|, newRhs)
+ case Long_& => genLongMethodApply(newLhs, LongImpl.&, newRhs)
+ case Long_^ =>
+ lhs match {
+ case LongLiteral(-1L) => genLongMethodApply(newRhs, LongImpl.UNARY_~)
+ case _ => genLongMethodApply(newLhs, LongImpl.^, newRhs)
+ }
+ case Long_<< => genLongMethodApply(newLhs, LongImpl.<<, newRhs)
+ case Long_>>> => genLongMethodApply(newLhs, LongImpl.>>>, newRhs)
+ case Long_>> => genLongMethodApply(newLhs, LongImpl.>>, newRhs)
+
+ case Long_== => genLongMethodApply(newLhs, LongImpl.===, newRhs)
+ case Long_!= => genLongMethodApply(newLhs, LongImpl.!==, newRhs)
+ case Long_< => genLongMethodApply(newLhs, LongImpl.<, newRhs)
+ case Long_<= => genLongMethodApply(newLhs, LongImpl.<=, newRhs)
+ case Long_> => genLongMethodApply(newLhs, LongImpl.>, newRhs)
+ case Long_>= => genLongMethodApply(newLhs, LongImpl.>=, newRhs)
+
+ case Boolean_| => !(!js.BinaryOp("|", newLhs, newRhs))
+ case Boolean_& => !(!js.BinaryOp("&", newLhs, newRhs))
+ }
+
+ case NewArray(tpe, lengths) =>
+ genCallHelper("newArrayObject",
+ genClassDataOf(tpe), js.ArrayConstr(lengths map transformExpr))
+
+ case ArrayValue(tpe, elems) =>
+ genCallHelper("makeNativeArrayWrapper",
+ genClassDataOf(tpe), js.ArrayConstr(elems map transformExpr))
+
+ case ArrayLength(array) =>
+ js.BracketSelect(js.DotSelect(transformExpr(array),
+ Ident("u")), js.StringLiteral("length"))
+
+ case ArraySelect(array, index) =>
+ js.BracketSelect(js.DotSelect(transformExpr(array),
+ Ident("u")), transformExpr(index))
+
+ case IsInstanceOf(expr, cls) =>
+ genIsInstanceOf(transformExpr(expr), cls)
+
+ case AsInstanceOf(expr, cls) =>
+ val newExpr = transformExpr(expr)
+ if (semantics.asInstanceOfs == Unchecked) newExpr
+ else genAsInstanceOf(newExpr, cls)
+
+ case Unbox(expr, charCode) =>
+ val newExpr = transformExpr(expr)
+
+ if (semantics.asInstanceOfs == Unchecked) {
+ (charCode: @switch) match {
+ case 'Z' => !(!newExpr)
+ case 'B' | 'S' | 'I' => js.BinaryOp("|", newExpr, js.IntLiteral(0))
+ case 'J' => genCallHelper("uJ", newExpr)
+ case 'F' => genFround(newExpr)
+ case 'D' => js.UnaryOp("+", newExpr)
+ }
+ } else {
+ genCallHelper("u"+charCode, newExpr)
+ }
+
+ case GetClass(expr) =>
+ genCallHelper("objectGetClass", transformExpr(expr))
+
+ case CallHelper(helper, args) =>
+ genCallHelper(helper, args map transformExpr: _*)
+
+ // JavaScript expressions
+
+ case JSBracketSelect(JSEnvInfo(), StringLiteral("global")) =>
+ // Shortcut for this field which is heavily used
+ envField("g")
+
+ case JSNew(constr, args) =>
+ js.New(transformExpr(constr), args map transformExpr)
+
+ case JSDotSelect(qualifier, item) =>
+ js.DotSelect(transformExpr(qualifier), item)
+
+ case JSBracketSelect(qualifier, item) =>
+ js.BracketSelect(transformExpr(qualifier), transformExpr(item))
+
+ case JSFunctionApply(fun, args) =>
+ /* Protect the fun so that if it is, e.g.,
+ * path.f
+ * we emit
+ * (0, path.f)(args...)
+ * instead of
+ * path.f(args...)
+ * If we emit the latter, then `this` will be bound to `path` in
+ * `f`, which is sometimes extremely harmful (e.g., for builtin
+ * methods of `window`).
+ */
+ val transformedFun = transformExpr(fun)
+ val protectedFun = transformedFun match {
+ case _:js.DotSelect | _:js.BracketSelect =>
+ js.Block(js.IntLiteral(0), transformedFun)
+ case _ =>
+ transformedFun
+ }
+ js.Apply(protectedFun, args map transformExpr)
+
+ case JSDotMethodApply(receiver, method, args) =>
+ js.Apply(js.DotSelect(transformExpr(receiver), method),
+ args map transformExpr)
+
+ case JSBracketMethodApply(receiver, method, args) =>
+ js.Apply(js.BracketSelect(transformExpr(receiver),
+ transformExpr(method)), args map transformExpr)
+
+ case JSUnaryOp(op, lhs) =>
+ js.UnaryOp(op, transformExpr(lhs))
+
+ case JSBinaryOp(op, lhs, rhs) =>
+ js.BinaryOp(op, transformExpr(lhs), transformExpr(rhs))
+
+ case JSArrayConstr(items) =>
+ js.ArrayConstr(items map transformExpr)
+
+ case JSObjectConstr(fields) =>
+ js.ObjectConstr(fields map {
+ case (name: Ident, value) =>
+ (transformIdent(name), transformExpr(value))
+ case (StringLiteral(name), value) =>
+ (js.StringLiteral(name), transformExpr(value))
+ })
+
+ case JSEnvInfo() =>
+ envField("env")
+
+ // Literals
+
+ case Undefined() => js.Undefined()
+ case Null() => js.Null()
+ case BooleanLiteral(value) => js.BooleanLiteral(value)
+ case IntLiteral(value) => js.IntLiteral(value)
+ case FloatLiteral(value) => js.DoubleLiteral(value.toDouble)
+ case DoubleLiteral(value) => js.DoubleLiteral(value)
+ case StringLiteral(value) => js.StringLiteral(value)
+
+ case LongLiteral(0L) =>
+ genLongModuleApply(LongImpl.Zero)
+ case LongLiteral(value) =>
+ val (l, m, h) = LongImpl.extractParts(value)
+ genNewLong(LongImpl.initFromParts,
+ js.IntLiteral(l), js.IntLiteral(m), js.IntLiteral(h))
+
+ case ClassOf(cls) =>
+ js.Apply(js.DotSelect(genClassDataOf(cls), Ident("getClassOf")), Nil)
+
+ // Atomic expressions
+
+ case VarRef(name, mutable) =>
+ js.VarRef(name, mutable)
+
+ case This() =>
+ js.This()
+
+ case Closure(captureParams, params, body, captureValues) =>
+ val transformedBody = {
+ val withReturn = Return(body, None)
+ transformStat(withReturn) match {
+ case js.Block(stats :+ js.Return(js.Undefined())) => js.Block(stats)
+ case other => other
+ }
+ }
+
+ val innerFunction =
+ js.Function(params.map(transformParamDef), transformedBody)
+
+ if (captureParams.isEmpty) {
+ innerFunction
+ } else {
+ js.Apply(
+ js.Function(captureParams.map(transformParamDef), {
+ js.Return(innerFunction)
+ }),
+ captureValues.map(transformExpr))
+ }
+
+ // Invalid trees
+
+ case _ =>
+ sys.error("Invalid tree in JSDesugar.transformExpr() "+
+ s"of class ${tree.getClass}")
+ }
+ }
+
+ def isMaybeHijackedClass(tpe: Type): Boolean = tpe match {
+ case ClassType(cls) =>
+ Definitions.HijackedClasses.contains(cls) ||
+ Definitions.AncestorsOfHijackedClasses.contains(cls)
+ case AnyType | UndefType | BooleanType | IntType | LongType |
+ FloatType | DoubleType | StringType =>
+ true
+ case _ =>
+ false
+ }
+
+ val hijackedClassMethodToHelperName: Map[String, String] = Map(
+ "toString__T" -> "objectToString",
+ "clone__O" -> "objectClone",
+ "finalize__V" -> "objectFinalize",
+ "notify__V" -> "objectNotify",
+ "notifyAll__V" -> "objectNotifyAll",
+ "equals__O__Z" -> "objectEquals",
+ "hashCode__I" -> "objectHashCode",
+
+ "length__I" -> "charSequenceLength",
+ "charAt__I__C" -> "charSequenceCharAt",
+ "subSequence__I__I__jl_CharSequence" -> "charSequenceSubSequence",
+
+ "compareTo__O__I" -> "comparableCompareTo",
+ "compareTo__jl_Boolean__I" -> "comparableCompareTo",
+ "compareTo__jl_Byte__I" -> "comparableCompareTo",
+ "compareTo__jl_Short__I" -> "comparableCompareTo",
+ "compareTo__jl_Integer__I" -> "comparableCompareTo",
+ "compareTo__jl_Long__I" -> "comparableCompareTo",
+ "compareTo__jl_Float__I" -> "comparableCompareTo",
+ "compareTo__jl_Double__I" -> "comparableCompareTo",
+ "compareTo__jl_String__I" -> "comparableCompareTo",
+
+ "booleanValue__Z" -> "booleanBooleanValue",
+
+ "byteValue__B" -> "numberByteValue",
+ "shortValue__S" -> "numberShortValue",
+ "intValue__I" -> "numberIntValue",
+ "longValue__J" -> "numberLongValue",
+ "floatValue__F" -> "numberFloatValue",
+ "doubleValue__D" -> "numberDoubleValue",
+
+ "isNaN__Z" -> "isNaN",
+ "isInfinite__Z" -> "isInfinite"
+ )
+
+ def genClassDataOf(cls: ReferenceType)(implicit pos: Position): js.Tree = {
+ cls match {
+ case ClassType(className) =>
+ encodeClassField("d", className)
+ case ArrayType(base, dims) =>
+ (1 to dims).foldLeft(encodeClassField("d", base)) { (prev, _) =>
+ js.Apply(js.DotSelect(prev, js.Ident("getArrayOf")), Nil)
+ }
+ }
+ }
+
+ private def genFround(arg: js.Tree)(implicit pos: Position): js.Tree = {
+ genCallHelper("fround", arg)
+ }
+
+ private def genNewLong(ctor: String, args: js.Tree*)(
+ implicit pos: Position): js.Tree = {
+ import TreeDSL._
+ js.Apply(
+ js.New(encodeClassVar(LongImpl.RuntimeLongClass), Nil) DOT ctor,
+ args.toList)
+ }
+
+ private def genLongMethodApply(receiver: js.Tree, methodName: String,
+ args: js.Tree*)(implicit pos: Position): js.Tree = {
+ import TreeDSL._
+ js.Apply(receiver DOT methodName, args.toList)
+ }
+
+ private def genLongModuleApply(methodName: String, args: js.Tree*)(
+ implicit pos: Position): js.Tree = {
+ import TreeDSL._
+ js.Apply(
+ genLoadModule(LongImpl.RuntimeLongModuleClass) DOT methodName,
+ args.toList)
+ }
+
+ private def genLoadModule(moduleClass: String)(
+ implicit pos: Position): js.Tree = {
+ import TreeDSL._
+ assert(moduleClass.endsWith("$"),
+ s"Trying to load module for non-module class $moduleClass")
+ val moduleName = moduleClass.dropRight(1)
+ js.Apply(envField("m") DOT moduleName, Nil)
+ }
+
+ }
+
+ // Helpers
+
+ private[javascript] def genIsInstanceOf(expr: js.Tree, cls: ReferenceType)(
+ implicit pos: Position): js.Tree =
+ genIsAsInstanceOf(expr, cls, test = true)
+
+ private def genAsInstanceOf(expr: js.Tree, cls: ReferenceType)(
+ implicit pos: Position): js.Tree =
+ genIsAsInstanceOf(expr, cls, test = false)
+
+ private def genIsAsInstanceOf(expr: js.Tree, cls: ReferenceType, test: Boolean)(
+ implicit pos: Position): js.Tree = {
+ import Definitions._
+ import TreeDSL._
+
+ cls match {
+ case ClassType(className0) =>
+ val className =
+ if (className0 == BoxedLongClass) LongImpl.RuntimeLongClass
+ else className0
+
+ if (HijackedBoxedClasses.contains(className)) {
+ if (test) {
+ className match {
+ case BoxedUnitClass => expr === js.Undefined()
+ case BoxedBooleanClass => typeof(expr) === "boolean"
+ case BoxedByteClass => genCallHelper("isByte", expr)
+ case BoxedShortClass => genCallHelper("isShort", expr)
+ case BoxedIntegerClass => genCallHelper("isInt", expr)
+ case BoxedFloatClass => genCallHelper("isFloat", expr)
+ case BoxedDoubleClass => typeof(expr) === "number"
+ }
+ } else {
+ className match {
+ case BoxedUnitClass => genCallHelper("asUnit", expr)
+ case BoxedBooleanClass => genCallHelper("asBoolean", expr)
+ case BoxedByteClass => genCallHelper("asByte", expr)
+ case BoxedShortClass => genCallHelper("asShort", expr)
+ case BoxedIntegerClass => genCallHelper("asInt", expr)
+ case BoxedFloatClass => genCallHelper("asFloat", expr)
+ case BoxedDoubleClass => genCallHelper("asDouble", expr)
+ }
+ }
+ } else {
+ js.Apply(
+ envField(if (test) "is" else "as") DOT js.Ident(className),
+ List(expr))
+ }
+
+ case ArrayType(base, depth) =>
+ js.Apply(
+ envField(if (test) "isArrayOf" else "asArrayOf") DOT js.Ident(base),
+ List(expr, js.IntLiteral(depth)))
+ }
+ }
+
+ private[javascript] def genCallHelper(helperName: String, args: js.Tree*)(
+ implicit pos: Position): js.Tree =
+ js.Apply(envField(helperName), args.toList)
+
+ private[javascript] def encodeClassVar(className: String)(
+ implicit pos: Position): js.Tree =
+ encodeClassField("c", className)
+
+ private[javascript] def encodeClassField(field: String, className: String)(
+ implicit pos: Position): js.Tree =
+ js.DotSelect(envField(field), js.Ident(className))
+
+ private[javascript] def envField(field: String)(implicit pos: Position): js.Tree =
+ js.DotSelect(js.VarRef(js.Ident(ScalaJSEnvironmentName), false),
+ js.Ident(field))
+
+ private[javascript] implicit class MyTreeOps(val self: js.Tree) {
+ def prototype(implicit pos: Position): js.Tree =
+ js.DotSelect(self, js.Ident("prototype"))
+ }
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/LongImpl.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/LongImpl.scala
new file mode 100644
index 0000000..70b81a3
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/LongImpl.scala
@@ -0,0 +1,116 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.javascript
+
+object LongImpl {
+ final val RuntimeLongClass = "sjsr_RuntimeLong"
+ final val RuntimeLongModuleClass = "sjsr_RuntimeLong$"
+
+ private final val SigUnary = "__sjsr_RuntimeLong"
+ private final val SigBinary = "__sjsr_RuntimeLong__sjsr_RuntimeLong"
+ private final val SigShift = "__I__sjsr_RuntimeLong"
+ private final val SigCompare = "__sjsr_RuntimeLong__Z"
+
+ final val UNARY_- = "unary$und$minus" + SigUnary
+ final val UNARY_~ = "unary$und$tilde" + SigUnary
+
+ final val + = "$$plus" + SigBinary
+ final val - = "$$minus" + SigBinary
+ final val * = "$$times" + SigBinary
+ final val / = "$$div" + SigBinary
+ final val % = "$$percent" + SigBinary
+
+ final val | = "$$bar" + SigBinary
+ final val & = "$$amp" + SigBinary
+ final val ^ = "$$up" + SigBinary
+
+ final val << = "$$less$less" + SigShift
+ final val >>> = "$$greater$greater$greater" + SigShift
+ final val >> = "$$greater$greater" + SigShift
+
+ final val === = "equals" + SigCompare
+ final val !== = "notEquals" + SigCompare
+ final val < = "$$less" + SigCompare
+ final val <= = "$$less$eq" + SigCompare
+ final val > = "$$greater" + SigCompare
+ final val >= = "$$greater$eq" + SigCompare
+
+ final val toInt = "toInt" + "__I"
+ final val toDouble = "toDouble" + "__D"
+
+ final val byteValue = "byteValue__B"
+ final val shortValue = "shortValue__S"
+ final val intValue = "intValue__I"
+ final val longValue = "longValue__J"
+ final val floatValue = "floatValue__F"
+ final val doubleValue = "doubleValue__D"
+
+ final val equals_ = "equals__O__Z"
+ final val hashCode_ = "hashCode__I"
+ final val compareTo = "compareTo__jl_Long__I"
+ final val compareToO = "compareTo__O__I"
+
+ private val OperatorMethods = Set(
+ UNARY_-, UNARY_~, this.+, this.-, *, /, %, |, &, ^, <<, >>>, >>,
+ ===, !==, <, <=, >, >=, toInt, toDouble)
+
+ private val BoxedLongMethods = Set(
+ byteValue, shortValue, intValue, longValue, floatValue, doubleValue,
+ equals_, hashCode_, compareTo, compareToO)
+
+ val AllMethods = OperatorMethods ++ BoxedLongMethods
+
+ // Methods used for intrinsics
+
+ final val bitCount = "bitCount__I"
+ final val signum = "signum__sjsr_RuntimeLong"
+ final val numberOfLeadingZeros = "numberOfLeadingZeros__I"
+ final val numberOfTrailingZeros = "numberOfTrailingZeros__I"
+ final val toBinaryString = "toBinaryString__T"
+ final val toHexString = "toHexString__T"
+ final val toOctalString = "toOctalString__T"
+
+ val AllIntrinsicMethods = Set(
+ bitCount, signum, numberOfLeadingZeros, numberOfTrailingZeros,
+ toBinaryString, toHexString, toOctalString)
+
+ // Constructors
+
+ final val initFromParts = "init___I__I__I"
+ final val initFromInt = "init___I"
+
+ val AllConstructors = Set(
+ initFromParts, initFromInt)
+
+ // Methods on the companion
+
+ final val fromDouble = "fromDouble__D__sjsr_RuntimeLong"
+
+ final val Zero = "Zero__sjsr_RuntimeLong"
+
+ val AllModuleMethods = Set(
+ fromDouble, Zero)
+
+ // Boldly copied from library/scala.scalajs.runtime.RuntimeLong
+
+ /** Number of relevant bits in l and m each. */
+ private final val BITS = 22
+ /** Number of relevant bits in l and m together. */
+ private final val BITS01 = 2 * BITS
+ /** Number of relevant bits in h. */
+ private final val BITS2 = 64 - BITS01
+ /** Bitmask for l and m. */
+ private final val MASK = (1 << BITS) - 1
+ /** Bitmask for h. */
+ private final val MASK_2 = (1 << BITS2) - 1
+
+ def extractParts(value: Long): (Int, Int, Int) =
+ (value.toInt & MASK, (value >> BITS).toInt & MASK, (value >> BITS01).toInt & MASK_2)
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/Printers.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/Printers.scala
new file mode 100644
index 0000000..264c548
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/Printers.scala
@@ -0,0 +1,420 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.javascript
+
+import scala.annotation.switch
+
+import scala.util.control.Breaks
+
+import java.io.Writer
+import java.net.URI
+
+import scala.scalajs.ir
+import ir.Position
+import ir.Position.NoPosition
+import ir.Printers.IndentationManager
+import ir.Utils.escapeJS
+
+import Trees._
+
+import scala.scalajs.tools.sourcemap.SourceMapWriter
+
+object Printers {
+
+ class JSTreePrinter(protected val out: Writer) extends IndentationManager {
+ def printTopLevelTree(tree: Tree) {
+ tree match {
+ case Skip() =>
+ // do not print anything
+ case Block(stats) =>
+ for (stat <- stats)
+ printTopLevelTree(stat)
+ case _ =>
+ printStat(tree)
+ if (shouldPrintSepAfterTree(tree))
+ print(";")
+ println()
+ }
+ }
+
+ protected def shouldPrintSepAfterTree(tree: Tree): Boolean =
+ !tree.isInstanceOf[DocComment]
+
+ protected def printBlock(tree: Tree): Unit = {
+ val trees = tree match {
+ case Block(trees) => trees
+ case _ => List(tree)
+ }
+ print("{"); indent(); println()
+ printSeq(trees) { x =>
+ printStat(x)
+ } { x =>
+ if (shouldPrintSepAfterTree(x))
+ print(";")
+ println()
+ }
+ undent(); println(); print("}")
+ }
+
+ protected def printSig(args: List[ParamDef]): Unit = {
+ printRow(args, "(", ", ", ")")
+ print(" ")
+ }
+
+ protected def printArgs(args: List[Tree]): Unit = {
+ printRow(args, "(", ", ", ")")
+ }
+
+ def printStat(tree: Tree): Unit =
+ printTree(tree, isStat = true)
+
+ def printTree(tree: Tree, isStat: Boolean): Unit = {
+ tree match {
+ case EmptyTree =>
+ print("<empty>")
+
+ // Comments
+
+ case DocComment(text) =>
+ val lines = text.split("\n").toList
+ if (lines.tail.isEmpty) {
+ print("/** ", lines.head, " */")
+ } else {
+ print("/** ", lines.head); println()
+ for (line <- lines.tail) {
+ print(" * ", line); println()
+ }
+ print(" */")
+ }
+
+ // Definitions
+
+ case VarDef(ident, mutable, rhs) =>
+ print("var ", ident)
+ if (rhs != EmptyTree)
+ print(" = ", rhs)
+
+ case ParamDef(ident, mutable) =>
+ print(ident)
+
+ // Control flow constructs
+
+ case Skip() =>
+ print("/*<skip>*/")
+
+ case tree @ Block(trees) =>
+ if (isStat)
+ printBlock(tree)
+ else
+ printRow(trees, "(", ", ", ")")
+
+ case Labeled(label, body) =>
+ print(label, ": ")
+ printBlock(body)
+
+ case Assign(lhs, rhs) =>
+ print(lhs, " = ", rhs)
+
+ case Return(expr) =>
+ print("return ", expr)
+
+ case If(cond, thenp, elsep) =>
+ if (isStat) {
+ print("if (", cond, ") ")
+ printBlock(thenp)
+ elsep match {
+ case Skip() => ()
+ case If(_, _, _) =>
+ print(" else ")
+ printTree(elsep, isStat)
+ case _ =>
+ print(" else ")
+ printBlock(elsep)
+ }
+ } else {
+ print("(", cond, " ? ", thenp, " : ", elsep, ")")
+ }
+
+ case While(cond, body, label) =>
+ if (label.isDefined)
+ print(label.get, ": ")
+ print("while (", cond, ") ")
+ printBlock(body)
+
+ case DoWhile(body, cond, label) =>
+ if (label.isDefined)
+ print(label.get, ": ")
+ print("do ")
+ printBlock(body)
+ print(" while (", cond, ")")
+
+ case Try(block, errVar, handler, finalizer) =>
+ print("try ")
+ printBlock(block)
+ if (handler != EmptyTree) {
+ print(" catch (", errVar, ") ")
+ printBlock(handler)
+ }
+ if (finalizer != EmptyTree) {
+ print(" finally ")
+ printBlock(finalizer)
+ }
+
+ case Throw(expr) =>
+ print("throw ", expr)
+
+ case Break(label) =>
+ if (label.isEmpty) print("break")
+ else print("break ", label.get)
+
+ case Continue(label) =>
+ if (label.isEmpty) print("continue")
+ else print("continue ", label.get)
+
+ case Switch(selector, cases, default) =>
+ print("switch (", selector, ") ")
+ print("{"); indent
+ for ((value, body) <- cases) {
+ println()
+ print("case ", value, ":"); indent; println()
+ printStat(body)
+ print(";")
+ undent
+ }
+ if (default != EmptyTree) {
+ println()
+ print("default:"); indent; println()
+ printStat(default)
+ print(";")
+ undent
+ }
+ undent; println(); print("}")
+
+ case Debugger() =>
+ print("debugger")
+
+ // Expressions
+
+ case New(ctor, args) =>
+ def containsOnlySelectsFromAtom(tree: Tree): Boolean = tree match {
+ case DotSelect(qual, _) => containsOnlySelectsFromAtom(qual)
+ case BracketSelect(qual, _) => containsOnlySelectsFromAtom(qual)
+ case VarRef(_, _) => true
+ case This() => true
+ case _ => false // in particular, Apply
+ }
+ if (containsOnlySelectsFromAtom(ctor))
+ print("new ", ctor)
+ else
+ print("new (", ctor, ")")
+ printArgs(args)
+
+ case DotSelect(qualifier, item) =>
+ print(qualifier, ".", item)
+
+ case BracketSelect(qualifier, item) =>
+ print(qualifier, "[", item, "]")
+
+ case Apply(fun, args) =>
+ print(fun)
+ printArgs(args)
+
+ case Delete(prop) =>
+ print("delete ", prop)
+
+ case UnaryOp("typeof", lhs) =>
+ print("typeof(", lhs, ")")
+
+ case UnaryOp(op, lhs) =>
+ print("(", op, lhs, ")")
+
+ case BinaryOp(op, lhs, rhs) =>
+ print("(", lhs, " ", op, " ", rhs, ")")
+
+ case ArrayConstr(items) =>
+ printRow(items, "[", ", ", "]")
+
+ case ObjectConstr(Nil) =>
+ print("{}")
+
+ case ObjectConstr(fields) =>
+ print("{"); indent; println()
+ printSeq(fields) {
+ case (name, value) => print(name, ": ", value)
+ } { _ =>
+ print(",")
+ println()
+ }
+ undent; println(); print("}")
+
+ // Literals
+
+ case Undefined() =>
+ print("(void 0)")
+
+ case Null() =>
+ print("null")
+
+ case BooleanLiteral(value) =>
+ print(if (value) "true" else "false")
+
+ case IntLiteral(value) =>
+ if (value >= 0)
+ print(value)
+ else
+ print("(", value, ")")
+
+ case DoubleLiteral(value) =>
+ if (value == 0 && 1 / value < 0)
+ print("(-0)")
+ else if (value >= 0)
+ print(value)
+ else
+ print("(", value, ")")
+
+ case StringLiteral(value) =>
+ print("\"", escapeJS(value), "\"")
+
+ // Atomic expressions
+
+ case VarRef(ident, _) =>
+ print(ident)
+
+ case This() =>
+ print("this")
+
+ case Function(args, body) =>
+ print("(function")
+ printSig(args)
+ printBlock(body)
+ print(")")
+
+ case _ =>
+ print(s"<error, elem of class ${tree.getClass()}>")
+ }
+ }
+
+ protected def printIdent(ident: Ident): Unit =
+ printString(escapeJS(ident.name))
+
+ def printOne(arg: Any): Unit = arg match {
+ case tree: Tree =>
+ printTree(tree, isStat = false)
+ case ident: Ident =>
+ printIdent(ident)
+ case arg =>
+ printString(if (arg == null) "null" else arg.toString)
+ }
+
+ protected def printString(s: String): Unit = {
+ out.write(s)
+ }
+
+ // Make it public
+ override def println(): Unit = super.println()
+
+ def complete(): Unit = ()
+ }
+
+ class JSTreePrinterWithSourceMap(_out: Writer,
+ sourceMap: SourceMapWriter) extends JSTreePrinter(_out) {
+
+ private var column = 0
+
+ override def printTree(tree: Tree, isStat: Boolean): Unit = {
+ val pos = tree.pos
+ if (pos.isDefined)
+ sourceMap.startNode(column, pos)
+
+ super.printTree(tree, isStat)
+
+ if (pos.isDefined)
+ sourceMap.endNode(column)
+ }
+
+ override protected def printIdent(ident: Ident): Unit = {
+ if (ident.pos.isDefined)
+ sourceMap.startNode(column, ident.pos, ident.originalName)
+ super.printIdent(ident)
+ if (ident.pos.isDefined)
+ sourceMap.endNode(column)
+ }
+
+ override def println(): Unit = {
+ super.println()
+ sourceMap.nextLine()
+ column = this.indentMargin
+ }
+
+ override protected def printString(s: String): Unit = {
+ // assume no EOL char in s, and assume s only has ASCII characters
+ super.printString(s)
+ column += s.length()
+ }
+
+ override def complete(): Unit = {
+ sourceMap.complete()
+ super.complete()
+ }
+ }
+
+ /** Prints a tree to find original locations based on line numbers.
+ * @param untilLine last 0-based line the positions should be recorded for
+ */
+ class ReverseSourceMapPrinter(untilLine: Int)
+ extends JSTreePrinter(ReverseSourceMapPrinter.NullWriter) {
+
+ private val positions = Array.fill(untilLine+1)(NoPosition)
+ private var curLine = 0
+
+ private val doneBreak = new Breaks
+
+ def apply(x: Int): Position = positions(x)
+
+ def reverseSourceMap(tree: Tree): Unit = doneBreak.breakable {
+ printTopLevelTree(tree)
+ }
+
+ override def printTree(tree: Tree, isStat: Boolean): Unit = {
+ if (positions(curLine).isEmpty)
+ positions(curLine) = tree.pos
+
+ super.printTree(tree, isStat)
+ }
+
+ override protected def printIdent(ident: Ident): Unit = {
+ if (positions(curLine).isEmpty)
+ positions(curLine) = ident.pos
+
+ super.printIdent(ident)
+ }
+
+ override def println(): Unit = {
+ super.println()
+ curLine += 1
+ if (curLine > untilLine)
+ doneBreak.break()
+ }
+
+ override protected def printString(s: String): Unit = {
+ // assume no EOL char in s, and assume s only has ASCII characters
+ // therefore, we fully ignore the string
+ }
+ }
+
+ object ReverseSourceMapPrinter {
+ private object NullWriter extends Writer {
+ def close(): Unit = ()
+ def flush(): Unit = ()
+ def write(buf: Array[Char], off: Int, len: Int): Unit = ()
+ }
+ }
+
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/ScalaJSClassEmitter.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/ScalaJSClassEmitter.scala
new file mode 100644
index 0000000..b249f88
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/ScalaJSClassEmitter.scala
@@ -0,0 +1,569 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.javascript
+
+import scala.scalajs.ir._
+import Position._
+import Transformers._
+import scala.scalajs.ir.Trees._
+import Types._
+
+import scala.scalajs.tools.sem._
+import CheckedBehavior.Unchecked
+
+import scala.scalajs.tools.javascript.{Trees => js}
+
+/** Defines methods to emit Scala.js classes to JavaScript code.
+ * The results are completely desugared.
+ */
+final class ScalaJSClassEmitter(semantics: Semantics) {
+
+ import JSDesugaring._
+
+ /** Desugar a Scala.js class into ECMAScript 5 constructs */
+ def genClassDef(tree: ClassDef): js.Tree = {
+ implicit val pos = tree.pos
+ val kind = tree.kind
+
+ var reverseParts: List[js.Tree] = Nil
+
+ if (kind == ClassKind.TraitImpl) {
+ reverseParts ::= genTraitImpl(tree)
+ } else {
+ if (kind.isClass)
+ reverseParts ::= genClass(tree)
+ if (kind.isClass || kind == ClassKind.Interface ||
+ tree.name.name == Definitions.StringClass)
+ reverseParts ::= genInstanceTests(tree)
+ reverseParts ::= genArrayInstanceTests(tree)
+ reverseParts ::= genTypeData(tree)
+ if (kind.isClass)
+ reverseParts ::= genSetTypeData(tree)
+ if (kind == ClassKind.ModuleClass)
+ reverseParts ::= genModuleAccessor(tree)
+ if (kind.isClass)
+ reverseParts ::= genClassExports(tree)
+ }
+
+ js.Block(reverseParts.reverse)
+ }
+
+ def genClass(tree: ClassDef): js.Tree = {
+ val className = tree.name.name
+ val typeFunctionDef = genConstructor(tree)
+ val memberDefs = tree.defs collect {
+ case m: MethodDef =>
+ genMethod(className, m)
+ case p: PropertyDef =>
+ genProperty(className, p)
+ }
+
+ js.Block(typeFunctionDef :: memberDefs)(tree.pos)
+ }
+
+ /** Generates the JS constructor for a class. */
+ def genConstructor(tree: ClassDef): js.Tree = {
+ assert(tree.kind.isClass)
+
+ val classIdent = tree.name
+ val className = classIdent.name
+ val tpe = ClassType(className)
+
+ assert(tree.parent.isDefined || className == Definitions.ObjectClass,
+ "Class $className is missing a parent class")
+
+ val ctorFun = {
+ val superCtorCall = tree.parent.fold[js.Tree] {
+ js.Skip()(tree.pos)
+ } { parentIdent =>
+ implicit val pos = tree.pos
+ js.Apply(
+ js.DotSelect(encodeClassVar(parentIdent.name), js.Ident("call")),
+ List(js.This()))
+ }
+ val fieldDefs = for {
+ field @ VarDef(name, vtpe, mutable, rhs) <- tree.defs
+ } yield {
+ implicit val pos = field.pos
+ desugarJavaScript(
+ Assign(Select(This()(tpe), name, mutable)(vtpe), rhs),
+ semantics)
+ }
+ js.Function(Nil,
+ js.Block(superCtorCall :: fieldDefs)(tree.pos))(tree.pos)
+ }
+
+ {
+ implicit val pos = tree.pos
+ val typeVar = encodeClassVar(className)
+ val docComment = js.DocComment("@constructor")
+ val ctorDef = js.Assign(typeVar, ctorFun)
+
+ val chainProto = tree.parent.fold[js.Tree] {
+ js.Skip()
+ } { parentIdent =>
+ js.Block(
+ js.Assign(typeVar.prototype,
+ js.New(js.DotSelect(envField("h"), parentIdent), Nil)),
+ genAddToPrototype(className, js.Ident("constructor"), typeVar)
+ )
+ }
+
+ val inheritableCtorDef = {
+ val inheritableCtorVar =
+ js.DotSelect(envField("h"), classIdent)
+ js.Block(
+ js.DocComment("@constructor"),
+ js.Assign(inheritableCtorVar, js.Function(Nil, js.Skip())),
+ js.Assign(inheritableCtorVar.prototype, typeVar.prototype)
+ )
+ }
+
+ js.Block(docComment, ctorDef, chainProto, inheritableCtorDef)
+ }
+ }
+
+ /** Generates a method. */
+ def genMethod(className: String, method: MethodDef): js.Tree = {
+ implicit val pos = method.pos
+ val methodFun = js.Function(method.args.map(transformParamDef),
+ desugarBody(method.body, method.resultType == NoType))
+ genAddToPrototype(className, method.name, methodFun)
+ }
+
+ /** Generates a property. */
+ def genProperty(className: String, property: PropertyDef): js.Tree = {
+ implicit val pos = property.pos
+ val classType = ClassType(className)
+
+ // defineProperty method
+ val defProp =
+ js.BracketSelect(js.VarRef(js.Ident("Object"), false),
+ js.StringLiteral("defineProperty"))
+
+ // class prototype
+ val proto = encodeClassVar(className).prototype
+
+ // property name
+ val name = property.name match {
+ case StringLiteral(value) =>
+ js.StringLiteral(value)
+ case id: Ident =>
+ // We need to work around the closure compiler. Call propertyName to
+ // get a string representation of the optimized name
+ genCallHelper("propertyName",
+ js.ObjectConstr(transformIdent(id) -> js.IntLiteral(0) :: Nil))
+ }
+
+ // Options passed to the defineProperty method
+ val descriptor = js.ObjectConstr {
+ // Basic config
+ val base =
+ js.StringLiteral("enumerable") -> js.BooleanLiteral(true) :: Nil
+
+ // Optionally add getter
+ val wget =
+ if (property.getterBody == EmptyTree) base
+ else js.StringLiteral("get") ->
+ js.Function(Nil, desugarBody(property.getterBody, isStat = false)) :: base
+
+ // Optionally add setter
+ if (property.setterBody == EmptyTree) wget
+ else js.StringLiteral("set") ->
+ js.Function(transformParamDef(property.setterArg) :: Nil,
+ desugarBody(property.setterBody, isStat = true)) :: wget
+ }
+
+ js.Apply(defProp, proto :: name :: descriptor :: Nil)
+ }
+
+ /** Generate `classVar.prototype.name = value` */
+ def genAddToPrototype(className: String, name: js.PropertyName,
+ value: js.Tree)(implicit pos: Position): js.Tree = {
+ val proto = encodeClassVar(className).prototype
+ val select = name match {
+ case name: js.Ident => js.DotSelect(proto, name)
+ case name: js.StringLiteral => js.BracketSelect(proto, name)
+ }
+ js.Assign(select, value)
+ }
+
+ /** Generate `classVar.prototype.name = value` */
+ def genAddToPrototype(className: String, name: PropertyName,
+ value: js.Tree)(implicit pos: Position): js.Tree = {
+ val newName = name match {
+ case ident: Ident => transformIdent(ident)
+ case StringLiteral(value) => js.StringLiteral(value)
+ }
+ genAddToPrototype(className, newName, value)
+ }
+
+ def genInstanceTests(tree: ClassDef): js.Tree = {
+ import Definitions._
+ import TreeDSL._
+
+ implicit val pos = tree.pos
+
+ val classIdent = transformIdent(tree.name)
+ val className = classIdent.name
+ val displayName = decodeClassName(className)
+
+ val isAncestorOfString =
+ AncestorsOfStringClass.contains(className)
+ val isAncestorOfHijackedNumberClass =
+ AncestorsOfHijackedNumberClasses.contains(className)
+ val isAncestorOfBoxedBooleanClass =
+ AncestorsOfBoxedBooleanClass.contains(className)
+
+ val objParam = js.ParamDef(Ident("obj"), mutable = false)
+ val obj = objParam.ref
+
+ val createIsStat = {
+ envField("is") DOT classIdent :=
+ js.Function(List(objParam), js.Return(className match {
+ case Definitions.ObjectClass =>
+ js.BinaryOp("!==", obj, js.Null())
+
+ case Definitions.StringClass =>
+ js.UnaryOp("typeof", obj) === js.StringLiteral("string")
+
+ case _ =>
+ var test = (obj && (obj DOT "$classData") &&
+ (obj DOT "$classData" DOT "ancestors" DOT classIdent))
+
+ if (isAncestorOfString)
+ test = test || (
+ js.UnaryOp("typeof", obj) === js.StringLiteral("string"))
+ if (isAncestorOfHijackedNumberClass)
+ test = test || (
+ js.UnaryOp("typeof", obj) === js.StringLiteral("number"))
+ if (isAncestorOfBoxedBooleanClass)
+ test = test || (
+ js.UnaryOp("typeof", obj) === js.StringLiteral("boolean"))
+
+ !(!test)
+ }))
+ }
+
+ val createAsStat = if (semantics.asInstanceOfs == Unchecked) {
+ js.Skip()
+ } else {
+ envField("as") DOT classIdent :=
+ js.Function(List(objParam), js.Return(className match {
+ case Definitions.ObjectClass =>
+ obj
+
+ case _ =>
+ js.If(js.Apply(envField("is") DOT classIdent, List(obj)) ||
+ (obj === js.Null()), {
+ obj
+ }, {
+ genCallHelper("throwClassCastException",
+ obj, js.StringLiteral(displayName))
+ })
+ }))
+ }
+
+ js.Block(createIsStat, createAsStat)
+ }
+
+ def genArrayInstanceTests(tree: ClassDef): js.Tree = {
+ import Definitions._
+ import TreeDSL._
+
+ implicit val pos = tree.pos
+
+ val classIdent = transformIdent(tree.name)
+ val className = classIdent.name
+ val displayName = decodeClassName(className)
+
+ val objParam = js.ParamDef(Ident("obj"), mutable = false)
+ val obj = objParam.ref
+
+ val depthParam = js.ParamDef(Ident("depth"), mutable = false)
+ val depth = depthParam.ref
+
+ val createIsArrayOfStat = {
+ envField("isArrayOf") DOT classIdent :=
+ js.Function(List(objParam, depthParam), className match {
+ case Definitions.ObjectClass =>
+ val dataVarDef = js.VarDef(Ident("data"), false, {
+ obj && (obj DOT "$classData")
+ })
+ val data = dataVarDef.ref
+ js.Block(
+ dataVarDef,
+ js.If(!data, {
+ js.Return(js.BooleanLiteral(false))
+ }, {
+ val arrayDepthVarDef = js.VarDef(Ident("arrayDepth"), false, {
+ (data DOT "arrayDepth") || js.IntLiteral(0)
+ })
+ val arrayDepth = arrayDepthVarDef.ref
+ js.Block(
+ arrayDepthVarDef,
+ js.Return {
+ // Array[A] </: Array[Array[A]]
+ !js.BinaryOp("<", arrayDepth, depth) && (
+ // Array[Array[A]] <: Array[Object]
+ js.BinaryOp(">", arrayDepth, depth) ||
+ // Array[Int] </: Array[Object]
+ !js.BracketSelect(data DOT "arrayBase", js.StringLiteral("isPrimitive"))
+ )
+ })
+ }))
+
+ case _ =>
+ js.Return(!(!(obj && (obj DOT "$classData") &&
+ ((obj DOT "$classData" DOT "arrayDepth") === depth) &&
+ (obj DOT "$classData" DOT "arrayBase" DOT "ancestors" DOT classIdent))))
+ })
+ }
+
+ val createAsArrayOfStat = if (semantics.asInstanceOfs == Unchecked) {
+ js.Skip()
+ } else {
+ envField("asArrayOf") DOT classIdent :=
+ js.Function(List(objParam, depthParam), js.Return {
+ js.If(js.Apply(envField("isArrayOf") DOT classIdent, List(obj, depth)) ||
+ (obj === js.Null()), {
+ obj
+ }, {
+ genCallHelper("throwArrayCastException",
+ obj, js.StringLiteral("L"+displayName+";"), depth)
+ })
+ })
+ }
+
+ js.Block(createIsArrayOfStat, createAsArrayOfStat)
+ }
+
+ def genTypeData(tree: ClassDef): js.Tree = {
+ import Definitions._
+ import TreeDSL._
+
+ implicit val pos = tree.pos
+
+ val classIdent = transformIdent(tree.name)
+ val className = classIdent.name
+ val kind = tree.kind
+ assert(kind.isType)
+
+ val isObjectClass =
+ className == ObjectClass
+ val isHijackedBoxedClass =
+ HijackedBoxedClasses.contains(className)
+ val isAncestorOfHijackedClass =
+ AncestorsOfHijackedClasses.contains(className)
+
+ val parentData = tree.parent.fold[js.Tree] {
+ if (isObjectClass) js.Null()
+ else js.Undefined()
+ } { parent =>
+ envField("d") DOT parent
+ }
+
+ val ancestorsRecord = js.ObjectConstr(
+ for (ancestor <- classIdent :: tree.ancestors.map(transformIdent))
+ yield (ancestor, js.IntLiteral(1)))
+
+ val typeData = js.New(envField("ClassTypeData"), List(
+ js.ObjectConstr(List(classIdent -> js.IntLiteral(0))),
+ js.BooleanLiteral(kind == ClassKind.Interface),
+ js.StringLiteral(decodeClassName(className)),
+ parentData,
+ ancestorsRecord
+ ) ++ (
+ // Optional parameter isInstance
+ if (isObjectClass) {
+ /* Object has special ScalaJS.is.O *and* ScalaJS.isArrayOf.O. */
+ List(
+ envField("is") DOT classIdent,
+ envField("isArrayOf") DOT classIdent)
+ } else if (isHijackedBoxedClass) {
+ /* Hijacked boxed classes have a special isInstanceOf test. */
+ val xParam = js.ParamDef(Ident("x"), mutable = false)
+ List(js.Function(List(xParam), js.Return {
+ genIsInstanceOf(xParam.ref, ClassType(className))
+ }))
+ } else if (isAncestorOfHijackedClass || className == StringClass) {
+ /* java.lang.String and ancestors of hijacked classes have a normal
+ * ScalaJS.is.pack_Class test but with a non-standard behavior. */
+ List(envField("is") DOT classIdent)
+ } else {
+ // For other classes, the isInstance function can be inferred.
+ Nil
+ }
+ ))
+
+ envField("d") DOT classIdent := typeData
+ }
+
+ def genSetTypeData(tree: ClassDef): js.Tree = {
+ import TreeDSL._
+
+ implicit val pos = tree.pos
+
+ assert(tree.kind.isClass)
+
+ encodeClassVar(tree.name.name).prototype DOT "$classData" :=
+ envField("d") DOT tree.name
+ }
+
+ def genModuleAccessor(tree: ClassDef): js.Tree = {
+ import TreeDSL._
+
+ implicit val pos = tree.pos
+
+ val classIdent = transformIdent(tree.name)
+ val className = classIdent.name
+ val tpe = ClassType(className)
+
+ require(tree.kind == ClassKind.ModuleClass,
+ s"genModuleAccessor called with non-module class: $className")
+ assert(className.endsWith("$"))
+
+ val moduleName = className.dropRight(1)
+ val moduleIdent = Ident(moduleName)
+
+ val moduleInstanceVar = envField("n") DOT moduleIdent
+ val accessorVar = envField("m") DOT moduleIdent
+
+ val createModuleInstanceField = {
+ moduleInstanceVar := js.Undefined()
+ }
+
+ val createAccessor = {
+ accessorVar := js.Function(Nil, js.Block(
+ js.If(!(moduleInstanceVar), {
+ moduleInstanceVar :=
+ js.Apply(js.New(encodeClassVar(className), Nil) DOT js.Ident("init___"), Nil)
+ }, js.Skip()),
+ js.Return(moduleInstanceVar)
+ ))
+ }
+
+ js.Block(createModuleInstanceField, createAccessor)
+ }
+
+ def genClassExports(tree: ClassDef): js.Tree = {
+ val exports = tree.defs collect {
+ case e: ConstructorExportDef =>
+ genConstructorExportDef(tree, e)
+ case e: ModuleExportDef =>
+ genModuleExportDef(tree, e)
+ }
+
+ js.Block(exports)(tree.pos)
+ }
+
+ def genConstructorExportDef(cd: ClassDef, tree: ConstructorExportDef): js.Tree = {
+ import TreeDSL._
+
+ implicit val pos = tree.pos
+ val classType = ClassType(cd.name.name)
+ val ConstructorExportDef(fullName, args, body) = tree
+
+ val baseCtor = envField("c") DOT cd.name
+ val (createNamespace, expCtorVar) = genCreateNamespaceInExports(fullName)
+
+ js.Block(
+ createNamespace,
+ js.DocComment("@constructor"),
+ expCtorVar := js.Function(args.map(transformParamDef), js.Block(
+ js.Apply(js.DotSelect(baseCtor, js.Ident("call")), List(js.This())),
+ desugarBody(body, isStat = true)
+ )),
+ expCtorVar DOT "prototype" := baseCtor DOT "prototype"
+ )
+ }
+
+ def genModuleExportDef(cd: ClassDef, tree: ModuleExportDef): js.Tree = {
+ import TreeDSL._
+
+ implicit val pos = tree.pos
+
+ val baseAccessor =
+ envField("m") DOT cd.name.name.dropRight(1)
+ val (createNamespace, expAccessorVar) =
+ genCreateNamespaceInExports(tree.fullName)
+
+ js.Block(
+ createNamespace,
+ expAccessorVar := baseAccessor
+ )
+ }
+
+ def genTraitImpl(tree: ClassDef): js.Tree = {
+ val traitImplName = tree.name.name
+ val defs = tree.defs collect {
+ case m: MethodDef =>
+ genTraitImplMethod(traitImplName, m)
+ }
+ js.Block(defs)(tree.pos)
+ }
+
+ def genTraitImplMethod(traitImplName: String, tree: MethodDef): js.Tree = {
+ implicit val pos = tree.pos
+ val MethodDef(name: Ident, args, resultType, body) = tree
+ js.Assign(
+ js.DotSelect(envField("i"), name),
+ js.Function(args.map(transformParamDef),
+ desugarBody(body, resultType == NoType)))
+ }
+
+ /** Generate a dummy parent. Used by ScalaJSOptimizer */
+ def genDummyParent(name: String): js.Tree = {
+ implicit val pos = Position.NoPosition
+
+ js.Block(
+ js.DocComment("@constructor (dummy parent)"))
+ js.Assign(js.DotSelect(envField("h"), js.Ident(name)),
+ js.Function(Nil, js.Skip())
+ )
+ }
+
+ // Helpers
+
+ /** Desugars a function body of the IR into ES5 JavaScript. */
+ private def desugarBody(tree: Tree, isStat: Boolean): js.Tree = {
+ implicit val pos = tree.pos
+ val withReturn =
+ if (isStat) tree
+ else Return(tree)
+ desugarJavaScript(withReturn, semantics) match {
+ case js.Block(stats :+ js.Return(js.Undefined())) => js.Block(stats)
+ case other => other
+ }
+ }
+
+ /** Gen JS code for assigning an rhs to a qualified name in the exports scope.
+ * For example, given the qualified name "foo.bar.Something", generates:
+ *
+ * ScalaJS.e["foo"] = ScalaJS.e["foo"] || {};
+ * ScalaJS.e["foo"]["bar"] = ScalaJS.e["foo"]["bar"] || {};
+ *
+ * Returns (statements, ScalaJS.e["foo"]["bar"]["Something"])
+ */
+ private def genCreateNamespaceInExports(qualName: String)(
+ implicit pos: Position): (js.Tree, js.Tree) = {
+ val parts = qualName.split("\\.")
+ val statements = List.newBuilder[js.Tree]
+ var namespace = envField("e")
+ for (i <- 0 until parts.length-1) {
+ namespace = js.BracketSelect(namespace, js.StringLiteral(parts(i)))
+ statements +=
+ js.Assign(namespace, js.BinaryOp("||", namespace, js.ObjectConstr(Nil)))
+ }
+ val lhs = js.BracketSelect(namespace, js.StringLiteral(parts.last))
+ (js.Block(statements.result()), lhs)
+ }
+
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/TreeDSL.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/TreeDSL.scala
new file mode 100644
index 0000000..3ac54d8
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/TreeDSL.scala
@@ -0,0 +1,50 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.javascript
+
+import scala.language.implicitConversions
+
+import scala.scalajs.ir.Position
+
+import Trees._
+
+private[javascript] object TreeDSL {
+ implicit class TreeOps(val self: Tree) extends AnyVal {
+ /** Select a member */
+ def DOT(field: Ident)(implicit pos: Position): DotSelect =
+ DotSelect(self, field)
+
+ /** Select a member */
+ def DOT(field: String)(implicit pos: Position): DotSelect =
+ DotSelect(self, Ident(field))
+
+ // Some operators that we use
+
+ def ===(that: Tree)(implicit pos: Position): Tree =
+ BinaryOp("===", self, that)
+ def ===(that: String)(implicit pos: Position): Tree =
+ BinaryOp("===", self, StringLiteral(that))
+
+ def unary_!()(implicit pos: Position): Tree =
+ UnaryOp("!", self)
+ def &&(that: Tree)(implicit pos: Position): Tree =
+ BinaryOp("&&", self, that)
+ def ||(that: Tree)(implicit pos: Position): Tree =
+ BinaryOp("||", self, that)
+
+ // Other constructs
+
+ def :=(that: Tree)(implicit pos: Position): Tree =
+ Assign(self, that)
+ }
+
+ def typeof(expr: Tree)(implicit pos: Position): Tree =
+ UnaryOp("typeof", expr)
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/Trees.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/Trees.scala
new file mode 100644
index 0000000..0b86d1b
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/Trees.scala
@@ -0,0 +1,194 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.javascript
+
+import scala.annotation.switch
+
+import scala.scalajs.ir
+import ir.Position
+import ir.Position.NoPosition
+
+object Trees {
+ import ir.Trees.requireValidIdent
+
+ /** AST node of JavaScript. */
+ abstract sealed class Tree {
+ val pos: Position
+
+ def show: String = {
+ val writer = new java.io.StringWriter
+ val printer = new Printers.JSTreePrinter(writer)
+ printer.printTree(this, isStat = true)
+ writer.toString()
+ }
+ }
+
+ case object EmptyTree extends Tree {
+ val pos = NoPosition
+ }
+
+ // Comments
+
+ case class DocComment(text: String)(implicit val pos: Position) extends Tree
+
+ // Identifiers and properties
+
+ sealed trait PropertyName {
+ def name: String
+ def pos: Position
+ }
+
+ case class Ident(name: String, originalName: Option[String])(
+ implicit val pos: Position) extends PropertyName {
+ requireValidIdent(name)
+ }
+
+ object Ident {
+ def apply(name: String)(implicit pos: Position): Ident =
+ new Ident(name, Some(name))
+ }
+
+ // Definitions
+
+ case class VarDef(name: Ident, mutable: Boolean, rhs: Tree)(implicit val pos: Position) extends Tree {
+ def ref(implicit pos: Position): Tree =
+ VarRef(name, mutable = mutable)
+ }
+
+ case class ParamDef(name: Ident, mutable: Boolean)(implicit val pos: Position) extends Tree {
+ def ref(implicit pos: Position): Tree =
+ VarRef(name, mutable = mutable)
+ }
+
+ // Control flow constructs
+
+ case class Skip()(implicit val pos: Position) extends Tree
+
+ class Block private (val stats: List[Tree])(implicit val pos: Position) extends Tree {
+ override def toString(): String =
+ stats.mkString("Block(", ",", ")")
+ }
+
+ object Block {
+ def apply(stats: List[Tree])(implicit pos: Position): Tree = {
+ val flattenedStats = stats flatMap {
+ case Skip() => Nil
+ case Block(subStats) => subStats
+ case other => other :: Nil
+ }
+ flattenedStats match {
+ case Nil => Skip()
+ case only :: Nil => only
+ case _ => new Block(flattenedStats)
+ }
+ }
+
+ def apply(stats: Tree*)(implicit pos: Position): Tree =
+ apply(stats.toList)
+
+ def unapply(block: Block): Some[List[Tree]] = Some(block.stats)
+ }
+
+ case class Labeled(label: Ident, body: Tree)(implicit val pos: Position) extends Tree
+
+ case class Assign(lhs: Tree, rhs: Tree)(implicit val pos: Position) extends Tree {
+ require(lhs match {
+ case _:VarRef | _:DotSelect | _:BracketSelect => true
+ case _ => false
+ }, s"Invalid lhs for Assign: $lhs")
+ }
+
+ case class Return(expr: Tree)(implicit val pos: Position) extends Tree
+
+ case class If(cond: Tree, thenp: Tree, elsep: Tree)(implicit val pos: Position) extends Tree
+
+ case class While(cond: Tree, body: Tree, label: Option[Ident] = None)(implicit val pos: Position) extends Tree
+
+ case class DoWhile(body: Tree, cond: Tree, label: Option[Ident] = None)(implicit val pos: Position) extends Tree
+
+ case class Try(block: Tree, errVar: Ident, handler: Tree, finalizer: Tree)(implicit val pos: Position) extends Tree
+
+ case class Throw(expr: Tree)(implicit val pos: Position) extends Tree
+
+ case class Break(label: Option[Ident] = None)(implicit val pos: Position) extends Tree
+
+ case class Continue(label: Option[Ident] = None)(implicit val pos: Position) extends Tree
+
+ case class Switch(selector: Tree, cases: List[(Tree, Tree)], default: Tree)(implicit val pos: Position) extends Tree
+
+ case class Debugger()(implicit val pos: Position) extends Tree
+
+ // Expressions
+
+ case class New(ctor: Tree, args: List[Tree])(implicit val pos: Position) extends Tree
+
+ case class DotSelect(qualifier: Tree, item: Ident)(implicit val pos: Position) extends Tree
+
+ case class BracketSelect(qualifier: Tree, item: Tree)(implicit val pos: Position) extends Tree
+
+ /** Syntactic apply.
+ * It is a method call if fun is a dot-select or bracket-select. It is a
+ * function call otherwise.
+ */
+ case class Apply(fun: Tree, args: List[Tree])(implicit val pos: Position) extends Tree
+
+ case class Delete(prop: Tree)(implicit val pos: Position) extends Tree {
+ require(prop match {
+ case _:DotSelect | _:BracketSelect => true
+ case _ => false
+ }, s"Invalid prop for Delete: $prop")
+ }
+
+ /** Unary operation (always preserves pureness).
+ *
+ * Operations which do not preserve pureness are not allowed in this tree.
+ * These are notably ++ and --
+ */
+ case class UnaryOp(op: String, lhs: Tree)(implicit val pos: Position) extends Tree
+
+ /** Binary operation (always preserves pureness).
+ *
+ * Operations which do not preserve pureness are not allowed in this tree.
+ * These are notably +=, -=, *=, /= and %=
+ */
+ case class BinaryOp(op: String, lhs: Tree, rhs: Tree)(implicit val pos: Position) extends Tree
+
+ case class ArrayConstr(items: List[Tree])(implicit val pos: Position) extends Tree
+
+ case class ObjectConstr(fields: List[(PropertyName, Tree)])(implicit val pos: Position) extends Tree
+
+ // Literals
+
+ /** Marker for literals. Literals are always pure. */
+ sealed trait Literal extends Tree
+
+ case class Undefined()(implicit val pos: Position) extends Literal
+
+ case class Null()(implicit val pos: Position) extends Literal
+
+ case class BooleanLiteral(value: Boolean)(implicit val pos: Position) extends Literal
+
+ case class IntLiteral(value: Int)(implicit val pos: Position) extends Literal
+
+ case class DoubleLiteral(value: Double)(implicit val pos: Position) extends Literal
+
+ case class StringLiteral(value: String)(
+ implicit val pos: Position) extends Literal with PropertyName {
+ override def name = value
+ }
+
+ // Atomic expressions
+
+ case class VarRef(ident: Ident, mutable: Boolean)(implicit val pos: Position) extends Tree
+
+ case class This()(implicit val pos: Position) extends Tree
+
+ case class Function(args: List[ParamDef], body: Tree)(implicit val pos: Position) extends Tree
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/Exceptions.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/Exceptions.scala
new file mode 100644
index 0000000..dd7f635
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/Exceptions.scala
@@ -0,0 +1,59 @@
+package scala.scalajs.tools.jsdep
+
+abstract class DependencyException(msg: String) extends Exception(msg)
+
+class MissingDependencyException(
+ val originatingLib: FlatJSDependency,
+ val missingLib: String
+) extends DependencyException(
+ s"The JS dependency ${originatingLib.resourceName} declared " +
+ s"from ${originatingLib.origin} has an unmet transitive " +
+ s"dependency $missingLib")
+
+class CyclicDependencyException(
+ val participants: List[ResolutionInfo]
+) extends DependencyException(
+ CyclicDependencyException.mkMsg(participants))
+
+object CyclicDependencyException {
+ private def mkMsg(parts: List[ResolutionInfo]) = {
+ val lookup = parts.map(p => (p.resourceName, p)).toMap
+
+ val msg = new StringBuilder()
+ msg.append("There is a loop in the following JS dependencies:\n")
+
+ def str(info: ResolutionInfo) =
+ s"${info.resourceName} from: ${info.origins.mkString(", ")}"
+
+ for (dep <- parts) {
+ msg.append(s" ${str(dep)} which depends on\n")
+ for (name <- dep.dependencies) {
+ val rdep = lookup(name)
+ msg.append(s" - ${str(rdep)}\n")
+ }
+ }
+
+ msg.toString()
+ }
+}
+
+class ConflictingNameException(
+ val participants: List[FlatJSDependency]
+) extends DependencyException(
+ ConflictingNameException.mkMsg(participants))
+
+object ConflictingNameException {
+ private def mkMsg(parts: List[FlatJSDependency]) = {
+ val resName = parts.head.resourceName
+
+ val msg = new StringBuilder()
+ msg.append(s"Name conflicts in:\n")
+
+ for (p <- parts) {
+ msg.append(p)
+ msg.append('\n')
+ }
+
+ sys.error(msg.toString())
+ }
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/FlatJSDependency.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/FlatJSDependency.scala
new file mode 100644
index 0000000..0c55e88
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/FlatJSDependency.scala
@@ -0,0 +1,17 @@
+package scala.scalajs.tools.jsdep
+
+import scala.scalajs.ir.Trees.isValidIdentifier
+
+/** The same as a [[JSDependency]] but containing the origin from the containing
+ * JSDependencyManifest. This class is used for filtering of dependencies.
+ */
+final class FlatJSDependency(
+ val origin: Origin,
+ val resourceName: String,
+ val dependencies: List[String] = Nil,
+ val commonJSName: Option[String] = None) {
+
+ require(commonJSName.forall(isValidIdentifier),
+ "commonJSName must be a valid JavaScript identifier")
+
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/JSDependency.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/JSDependency.scala
new file mode 100644
index 0000000..2e6f8d1
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/JSDependency.scala
@@ -0,0 +1,66 @@
+package scala.scalajs.tools.jsdep
+
+import scala.scalajs.tools.json._
+
+import scala.scalajs.ir.Trees.isValidIdentifier
+
+/** Expresses a dependency on a raw JS library and the JS libraries this library
+ * itself depends on.
+ *
+ * Both the [[resourceName]] and each element of [[dependencies]] is the
+ * unqualified filename of the library (e.g. "jquery.js").
+ *
+ * @param resourceName Filename of the JavaScript file to include
+ * (e.g. "jquery.js")
+ * @param dependencies Filenames of JavaScript files that must be included
+ * before this JavaScript file.
+ * @param commonJSName A JavaScript variable name this dependency should be
+ * required in a commonJS environment (n.b. Node.js). Should only be set if
+ * the JavaScript library will register its exports.
+ */
+final class JSDependency(
+ val resourceName: String,
+ val dependencies: List[String] = Nil,
+ val commonJSName: Option[String] = None) {
+
+ require(commonJSName.forall(isValidIdentifier),
+ "commonJSName must be a valid JavaScript identifier")
+
+ def dependsOn(names: String*): JSDependency =
+ copy(dependencies = dependencies ++ names)
+ def commonJSName(name: String): JSDependency =
+ copy(commonJSName = Some(name))
+ def withOrigin(origin: Origin): FlatJSDependency =
+ new FlatJSDependency(origin, resourceName, dependencies, commonJSName)
+
+ private def copy(
+ resourceName: String = this.resourceName,
+ dependencies: List[String] = this.dependencies,
+ commonJSName: Option[String] = this.commonJSName) = {
+ new JSDependency(resourceName, dependencies, commonJSName)
+ }
+}
+
+object JSDependency {
+
+ implicit object JSDepJSONSerializer extends JSONSerializer[JSDependency] {
+ def serialize(x: JSDependency): JSON = {
+ new JSONObjBuilder()
+ .fld("resourceName", x.resourceName)
+ .opt("dependencies",
+ if (x.dependencies.nonEmpty) Some(x.dependencies) else None)
+ .opt("commonJSName", x.commonJSName)
+ .toJSON
+ }
+ }
+
+ implicit object JSDepJSONDeserializer extends JSONDeserializer[JSDependency] {
+ def deserialize(x: JSON): JSDependency = {
+ val obj = new JSONObjExtractor(x)
+ new JSDependency(
+ obj.fld[String] ("resourceName"),
+ obj.opt[List[String]]("dependencies").getOrElse(Nil),
+ obj.opt[String] ("commonJSName"))
+ }
+ }
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/JSDependencyManifest.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/JSDependencyManifest.scala
new file mode 100644
index 0000000..24491b4
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/JSDependencyManifest.scala
@@ -0,0 +1,130 @@
+package scala.scalajs.tools.jsdep
+
+import scala.scalajs.tools.json._
+import scala.scalajs.tools.io._
+
+import scala.collection.immutable.{Seq, Traversable}
+
+import java.io.{Reader, Writer}
+
+/** The information written to a "JS_DEPENDENCIES" manifest file. */
+final class JSDependencyManifest(
+ val origin: Origin,
+ val libDeps: List[JSDependency],
+ val requiresDOM: Boolean,
+ val compliantSemantics: List[String]) {
+ def flatten: List[FlatJSDependency] = libDeps.map(_.withOrigin(origin))
+}
+
+object JSDependencyManifest {
+
+ final val ManifestFileName = "JS_DEPENDENCIES"
+
+ def createIncludeList(
+ flatDeps: Traversable[FlatJSDependency]): List[ResolutionInfo] = {
+ val jsDeps = mergeManifests(flatDeps)
+
+ // Verify all dependencies are met
+ for {
+ lib <- flatDeps
+ dep <- lib.dependencies
+ if !jsDeps.contains(dep)
+ } throw new MissingDependencyException(lib, dep)
+
+ // Sort according to dependencies and return
+
+ // Very simple O(n²) topological sort for elements assumed to be distinct
+ // Copied :( from GenJSExports (but different exception)
+ @scala.annotation.tailrec
+ def loop(coll: List[ResolutionInfo],
+ acc: List[ResolutionInfo]): List[ResolutionInfo] = {
+
+ if (coll.isEmpty) acc
+ else if (coll.tail.isEmpty) coll.head :: acc
+ else {
+ val (selected, pending) = coll.partition { x =>
+ coll forall { y => (x eq y) || !y.dependencies.contains(x.resourceName) }
+ }
+
+ if (selected.nonEmpty)
+ loop(pending, selected ::: acc)
+ else
+ throw new CyclicDependencyException(pending)
+ }
+ }
+
+ loop(jsDeps.values.toList, Nil)
+ }
+
+ /** Merges multiple JSDependencyManifests into a map of map:
+ * resourceName -> ResolutionInfo
+ */
+ private def mergeManifests(flatDeps: Traversable[FlatJSDependency]) = {
+ @inline
+ def hasConflict(x: FlatJSDependency, y: FlatJSDependency) = (
+ x.commonJSName.isDefined &&
+ y.commonJSName.isDefined &&
+ (x.resourceName == y.resourceName ^
+ x.commonJSName == y.commonJSName)
+ )
+
+ val conflicts = flatDeps.filter(x =>
+ flatDeps.exists(y => hasConflict(x,y)))
+
+ if (conflicts.nonEmpty)
+ throw new ConflictingNameException(conflicts.toList)
+
+ flatDeps.groupBy(_.resourceName).mapValues { sameName =>
+ new ResolutionInfo(
+ resourceName = sameName.head.resourceName,
+ dependencies = sameName.flatMap(_.dependencies).toSet,
+ origins = sameName.map(_.origin).toList,
+ commonJSName = sameName.flatMap(_.commonJSName).headOption
+ )
+ }
+ }
+
+ implicit object JSDepManJSONSerializer extends JSONSerializer[JSDependencyManifest] {
+ @inline def optList[T](x: List[T]): Option[List[T]] =
+ if (x.nonEmpty) Some(x) else None
+
+ def serialize(x: JSDependencyManifest): JSON = {
+ new JSONObjBuilder()
+ .fld("origin", x.origin)
+ .opt("libDeps", optList(x.libDeps))
+ .opt("requiresDOM", if (x.requiresDOM) Some(true) else None)
+ .opt("compliantSemantics", optList(x.compliantSemantics))
+ .toJSON
+ }
+ }
+
+ implicit object JSDepManJSONDeserializer extends JSONDeserializer[JSDependencyManifest] {
+ def deserialize(x: JSON): JSDependencyManifest = {
+ val obj = new JSONObjExtractor(x)
+ new JSDependencyManifest(
+ obj.fld[Origin] ("origin"),
+ obj.opt[List[JSDependency]]("libDeps").getOrElse(Nil),
+ obj.opt[Boolean] ("requiresDOM").getOrElse(false),
+ obj.opt[List[String]] ("compliantSemantics").getOrElse(Nil))
+ }
+ }
+
+ def write(dep: JSDependencyManifest, output: WritableVirtualTextFile): Unit = {
+ val writer = output.contentWriter
+ try write(dep, writer)
+ finally writer.close()
+ }
+
+ def write(dep: JSDependencyManifest, writer: Writer): Unit =
+ writeJSON(dep.toJSON, writer)
+
+ def read(file: VirtualTextFile): JSDependencyManifest = {
+ val reader = file.reader
+ try read(reader)
+ finally reader.close()
+ }
+
+ def read(reader: Reader): JSDependencyManifest =
+ fromJSON[JSDependencyManifest](readJSON(reader))
+
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/Origin.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/Origin.scala
new file mode 100644
index 0000000..a2c6b2d
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/Origin.scala
@@ -0,0 +1,28 @@
+package scala.scalajs.tools.jsdep
+
+import scala.scalajs.tools.json._
+
+/** The place a JSDependency originated from */
+final class Origin(val moduleName: String, val configuration: String) {
+ override def toString(): String = s"$moduleName:$configuration"
+}
+
+object Origin {
+ implicit object OriginJSONSerializer extends JSONSerializer[Origin] {
+ def serialize(x: Origin): JSON = {
+ new JSONObjBuilder()
+ .fld("moduleName", x.moduleName)
+ .fld("configuration", x.configuration)
+ .toJSON
+ }
+ }
+
+ implicit object OriginDeserializer extends JSONDeserializer[Origin] {
+ def deserialize(x: JSON): Origin = {
+ val obj = new JSONObjExtractor(x)
+ new Origin(
+ obj.fld[String]("moduleName"),
+ obj.fld[String]("configuration"))
+ }
+ }
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/ResolutionInfo.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/ResolutionInfo.scala
new file mode 100644
index 0000000..2aa177e
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/ResolutionInfo.scala
@@ -0,0 +1,21 @@
+package scala.scalajs.tools.jsdep
+
+import scala.scalajs.ir.Trees.isValidIdentifier
+
+/** Information about a resolved JSDependency
+ *
+ * @param resourceName Filename of the JavaScript file
+ * @param dependencies Filenames this dependency depends on
+ * @param origins Who declared this dependency
+ * @param commonJSName Variable name in commonJS environments
+ */
+final class ResolutionInfo(
+ val resourceName: String,
+ val dependencies: Set[String],
+ val origins: List[Origin],
+ val commonJSName: Option[String]) {
+
+ require(commonJSName.forall(isValidIdentifier),
+ "commonJSName must be a valid JavaScript identifier")
+
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/AbstractJSONImpl.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/AbstractJSONImpl.scala
new file mode 100644
index 0000000..ad5d79e
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/AbstractJSONImpl.scala
@@ -0,0 +1,32 @@
+package scala.scalajs.tools.json
+
+import java.io.{Reader, Writer}
+
+/** A JSON implementation. Has a representation type and methods to convert
+ * this type to/from primitives, lists and maps.
+ *
+ * Further, it can write/read a value of this type to a string.
+ */
+private[json] trait AbstractJSONImpl {
+
+ type Repr
+
+ def fromString(x: String): Repr
+ def fromNumber(x: Number): Repr
+ def fromBoolean(x: Boolean): Repr
+ def fromList(x: List[Repr]): Repr
+ def fromMap(x: Map[String, Repr]): Repr
+
+ def toString(x: Repr): String
+ def toNumber(x: Repr): Number
+ def toBoolean(x: Repr): Boolean
+ def toList(x: Repr): List[Repr]
+ def toMap(x: Repr): Map[String, Repr]
+
+ def serialize(x: Repr): String
+ def serialize(x: Repr, writer: Writer): Unit
+
+ def deserialize(str: String): Repr
+ def deserialize(reader: Reader): Repr
+
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/JSONDeserializer.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/JSONDeserializer.scala
new file mode 100644
index 0000000..e854e9a
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/JSONDeserializer.scala
@@ -0,0 +1,30 @@
+package scala.scalajs.tools.json
+
+trait JSONDeserializer[T] {
+ def deserialize(x: JSON): T
+}
+
+object JSONDeserializer {
+
+ implicit object stringJSON extends JSONDeserializer[String] {
+ def deserialize(x: JSON): String = Impl.toString(x)
+ }
+
+ implicit object intJSON extends JSONDeserializer[Int] {
+ def deserialize(x: JSON): Int = Impl.toNumber(x).intValue()
+ }
+
+ implicit object booleanJSON extends JSONDeserializer[Boolean] {
+ def deserialize(x: JSON): Boolean = Impl.toBoolean(x)
+ }
+
+ implicit def listJSON[T : JSONDeserializer] = new JSONDeserializer[List[T]] {
+ def deserialize(x: JSON): List[T] = Impl.toList(x).map(fromJSON[T] _)
+ }
+
+ implicit def mapJSON[V : JSONDeserializer] = new JSONDeserializer[Map[String, V]] {
+ def deserialize(x: JSON): Map[String, V] =
+ Impl.toMap(x).mapValues(fromJSON[V] _)
+ }
+
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/JSONObjBuilder.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/JSONObjBuilder.scala
new file mode 100644
index 0000000..dd98f49
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/JSONObjBuilder.scala
@@ -0,0 +1,20 @@
+package scala.scalajs.tools.json
+
+import scala.collection.mutable
+
+class JSONObjBuilder {
+
+ private val flds = mutable.Map.empty[String, JSON]
+
+ def fld[T : JSONSerializer](name: String, v: T): this.type = {
+ flds.put(name, v.toJSON)
+ this
+ }
+
+ def opt[T : JSONSerializer](name: String, v: Option[T]): this.type = {
+ v.foreach(v => flds.put(name, v.toJSON))
+ this
+ }
+
+ def toJSON: JSON = Impl.fromMap(flds.toMap)
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/JSONObjExtractor.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/JSONObjExtractor.scala
new file mode 100644
index 0000000..e49f7e4
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/JSONObjExtractor.scala
@@ -0,0 +1,13 @@
+package scala.scalajs.tools.json
+
+import scala.collection.mutable
+
+class JSONObjExtractor(rawData: JSON) {
+ private val data = Impl.toMap(rawData)
+
+ def fld[T : JSONDeserializer](name: String): T =
+ fromJSON[T](data(name))
+
+ def opt[T : JSONDeserializer](name: String): Option[T] =
+ data.get(name).map(fromJSON[T] _)
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/JSONSerializer.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/JSONSerializer.scala
new file mode 100644
index 0000000..e26c92a
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/JSONSerializer.scala
@@ -0,0 +1,32 @@
+package scala.scalajs.tools.json
+
+trait JSONSerializer[T] {
+ def serialize(x: T): JSON
+}
+
+object JSONSerializer {
+
+ implicit object stringJSON extends JSONSerializer[String] {
+ def serialize(x: String): JSON = Impl.fromString(x)
+ }
+
+ implicit object intJSON extends JSONSerializer[Int] {
+ def serialize(x: Int): JSON = Impl.fromNumber(x)
+ }
+
+ implicit object booleanJSON extends JSONSerializer[Boolean] {
+ def serialize(x: Boolean): JSON = Impl.fromBoolean(x)
+ }
+
+ implicit def listJSON[T : JSONSerializer] = new JSONSerializer[List[T]] {
+ def serialize(x: List[T]): JSON = Impl.fromList(x.map(_.toJSON))
+ }
+
+ implicit def mapJSON[V : JSONSerializer] = {
+ new JSONSerializer[Map[String, V]] {
+ def serialize(x: Map[String, V]): JSON =
+ Impl.fromMap(x.mapValues(_.toJSON))
+ }
+ }
+
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/package.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/package.scala
new file mode 100644
index 0000000..551893a
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/package.scala
@@ -0,0 +1,26 @@
+package scala.scalajs.tools
+
+import java.io.{Reader, Writer}
+
+/** Some type-class lightweight wrappers around simple-json.
+ *
+ * They allow to write [[xyz.toJSON]] to obtain classes that can be
+ * serialized by simple-json and [[fromJSON[T](xyz)]] to get an
+ * object back.
+ */
+package object json {
+ type JSON = Impl.Repr
+
+ implicit class JSONPimp[T : JSONSerializer](x: T) {
+ def toJSON: JSON = implicitly[JSONSerializer[T]].serialize(x)
+ }
+
+ def fromJSON[T](x: JSON)(implicit d: JSONDeserializer[T]): T =
+ d.deserialize(x)
+
+ def writeJSON(x: JSON, writer: Writer): Unit = Impl.serialize(x, writer)
+ def jsonToString(x: JSON): String = Impl.serialize(x)
+ def readJSON(str: String): JSON = Impl.deserialize(str)
+ def readJSON(reader: Reader): JSON = Impl.deserialize(reader)
+
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/logging/Level.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/logging/Level.scala
new file mode 100644
index 0000000..fbbf39d
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/logging/Level.scala
@@ -0,0 +1,24 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.logging
+
+import scala.math.Ordered
+
+abstract sealed class Level extends Ordered[Level] { x =>
+ protected val order: Int
+ def compare(y: Level) = x.order - y.order
+}
+
+object Level {
+ case object Error extends Level { protected val order = 4 }
+ case object Warn extends Level { protected val order = 3 }
+ case object Info extends Level { protected val order = 2 }
+ case object Debug extends Level { protected val order = 1 }
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/logging/Logger.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/logging/Logger.scala
new file mode 100644
index 0000000..3664f51
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/logging/Logger.scala
@@ -0,0 +1,25 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.logging
+
+/** Abstract logger for our tools. Designed after sbt's Loggers. */
+trait Logger {
+ def log(level: Level, message: => String): Unit
+ def success(message: => String): Unit
+ def trace(t: => Throwable): Unit
+
+ def error(message: => String): Unit = log(Level.Error, message)
+ def warn(message: => String): Unit = log(Level.Warn, message)
+ def info(message: => String): Unit = log(Level.Info, message)
+ def debug(message: => String): Unit = log(Level.Debug, message)
+
+ def time(title: String, nanos: Long): Unit =
+ debug(s"$title: ${nanos / 1000} us")
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/logging/NullLogger.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/logging/NullLogger.scala
new file mode 100644
index 0000000..0e36f89
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/logging/NullLogger.scala
@@ -0,0 +1,7 @@
+package scala.scalajs.tools.logging
+
+object NullLogger extends Logger {
+ def log(level: Level, message: => String): Unit = {}
+ def success(message: => String): Unit = {}
+ def trace(t: => Throwable): Unit = {}
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/logging/ScalaConsoleLogger.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/logging/ScalaConsoleLogger.scala
new file mode 100644
index 0000000..e2c9efc
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/logging/ScalaConsoleLogger.scala
@@ -0,0 +1,15 @@
+package scala.scalajs.tools.logging
+
+class ScalaConsoleLogger(minLevel: Level = Level.Debug) extends Logger {
+
+ def log(level: Level, message: =>String): Unit = if (level >= minLevel) {
+ if (level == Level.Warn || level == Level.Error)
+ scala.Console.err.println(message)
+ else
+ scala.Console.out.println(message)
+ }
+ def success(message: => String): Unit = info(message)
+ def trace(t: => Throwable): Unit =
+ // This is error level, so no checking
+ t.printStackTrace()
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/Analyzer.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/Analyzer.scala
new file mode 100644
index 0000000..9cdd764
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/Analyzer.scala
@@ -0,0 +1,587 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.optimizer
+
+import scala.annotation.tailrec
+
+import scala.collection.mutable
+
+import scala.scalajs.ir
+import ir.{ClassKind, Definitions, Infos}
+
+import scala.scalajs.tools.sem._
+import scala.scalajs.tools.javascript.LongImpl
+import scala.scalajs.tools.logging._
+
+import ScalaJSOptimizer._
+
+class Analyzer(logger0: Logger, semantics: Semantics,
+ allData: Seq[Infos.ClassInfo], globalWarnEnabled: Boolean,
+ isBeforeOptimizer: Boolean) {
+ /* Set this to true to debug the DCE analyzer.
+ * We don't rely on config to disable 'debug' messages because we want
+ * to use 'debug' for displaying more stack trace info that the user can
+ * see with the 'last' command.
+ */
+ val DebugAnalyzer = false
+
+ object logger extends Logger {
+ var indentation: String = ""
+
+ def indent(): Unit = indentation += " "
+ def undent(): Unit = indentation = indentation.substring(2)
+
+ def log(level: Level, message: => String) =
+ logger0.log(level, indentation+message)
+ def success(message: => String) =
+ logger0.success(indentation+message)
+ def trace(t: => Throwable) =
+ logger0.trace(t)
+
+ def indented[A](body: => A): A = {
+ indent()
+ try body
+ finally undent()
+ }
+
+ def debugIndent[A](message: => String)(body: => A): A = {
+ if (DebugAnalyzer) {
+ debug(message)
+ indented(body)
+ } else {
+ body
+ }
+ }
+
+ def temporarilyNotIndented[A](body: => A): A = {
+ val savedIndent = indentation
+ indentation = ""
+ try body
+ finally indentation = savedIndent
+ }
+ }
+
+ sealed trait From
+ case class FromMethod(methodInfo: MethodInfo) extends From
+ case object FromCore extends From
+ case object FromExports extends From
+ case object FromManual extends From
+
+ var allAvailable: Boolean = true
+
+ val classInfos: mutable.Map[String, ClassInfo] = {
+ val cs = for (classData <- allData)
+ yield (classData.encodedName, new ClassInfo(classData))
+ mutable.Map.empty[String, ClassInfo] ++ cs
+ }
+
+ def lookupClass(encodedName: String): ClassInfo = {
+ classInfos.get(encodedName) match {
+ case Some(info) => info
+ case None =>
+ val c = new ClassInfo(createMissingClassInfo(encodedName))
+ classInfos += encodedName -> c
+ c.nonExistent = true
+ c.linkClasses()
+ c
+ }
+ }
+
+ def lookupModule(encodedName: String): ClassInfo = {
+ lookupClass(encodedName+"$")
+ }
+
+ linkClasses()
+
+ def linkClasses(): Unit = {
+ if (!classInfos.contains(ir.Definitions.ObjectClass))
+ sys.error("Fatal error: could not find java.lang.Object on the classpath")
+ for (classInfo <- classInfos.values.toList)
+ classInfo.linkClasses()
+ }
+
+ def computeReachability(manuallyReachable: Seq[ManualReachability],
+ noWarnMissing: Seq[NoWarnMissing]): Unit = {
+ // Stuff reachable from core symbols always should warn
+ reachCoreSymbols()
+
+ // Disable warnings as requested
+ noWarnMissing.foreach(disableWarning _)
+
+ // Reach all user stuff
+ manuallyReachable.foreach(reachManually _)
+ for (classInfo <- classInfos.values)
+ classInfo.reachExports()
+ }
+
+ /** Reach symbols used directly by scalajsenv.js. */
+ def reachCoreSymbols(): Unit = {
+ import semantics._
+ import CheckedBehavior._
+
+ implicit val from = FromCore
+
+ def instantiateClassWith(className: String, constructor: String): ClassInfo = {
+ val info = lookupClass(className)
+ info.instantiated()
+ info.callMethod(constructor)
+ info
+ }
+
+ val ObjectClass = instantiateClassWith("O", "init___")
+ ObjectClass.callMethod("toString__T")
+ ObjectClass.callMethod("equals__O__Z")
+
+ instantiateClassWith("jl_NullPointerException", "init___")
+
+ if (asInstanceOfs != Unchecked)
+ instantiateClassWith("jl_ClassCastException", "init___T")
+
+ if (asInstanceOfs == Fatal)
+ instantiateClassWith("sjsr_UndefinedBehaviorError", "init___jl_Throwable")
+
+ instantiateClassWith("jl_Class", "init___jl_ScalaJSClassData")
+
+ val RTStringModuleClass = lookupClass("sjsr_RuntimeString$")
+ RTStringModuleClass.accessModule()
+ RTStringModuleClass.callMethod("hashCode__T__I")
+
+ val RTLongClass = lookupClass(LongImpl.RuntimeLongClass)
+ RTLongClass.instantiated()
+ for (method <- LongImpl.AllConstructors ++ LongImpl.AllMethods)
+ RTLongClass.callMethod(method)
+
+ if (isBeforeOptimizer) {
+ for (method <- LongImpl.AllIntrinsicMethods)
+ RTLongClass.callMethod(method)
+ }
+
+ val RTLongModuleClass = lookupClass(LongImpl.RuntimeLongModuleClass)
+ RTLongModuleClass.accessModule()
+ for (method <- LongImpl.AllModuleMethods)
+ RTLongModuleClass.callMethod(method)
+
+ if (isBeforeOptimizer) {
+ for (hijacked <- Definitions.HijackedClasses)
+ lookupClass(hijacked).instantiated()
+ } else {
+ for (hijacked <- Definitions.HijackedClasses)
+ lookupClass(hijacked).accessData()
+ }
+
+ if (semantics.strictFloats) {
+ val RuntimePackage = lookupClass("sjsr_package$")
+ RuntimePackage.accessModule()
+ RuntimePackage.callMethod("froundPolyfill__D__D")
+ }
+
+ val BitsModuleClass = lookupClass("sjsr_Bits$")
+ BitsModuleClass.accessModule()
+ BitsModuleClass.callMethod("numberHashCode__D__I")
+ }
+
+ def reachManually(info: ManualReachability) = {
+ implicit val from = FromManual
+
+ // Don't lookupClass here, since we don't want to create any
+ // symbols. If a symbol doesn't exist, we fail.
+ info match {
+ case ReachObject(name) => classInfos(name + "$").accessModule()
+ case Instantiate(name) => classInfos(name).instantiated()
+ case ReachMethod(className, methodName, static) =>
+ classInfos(className).callMethod(methodName, static)
+ }
+ }
+
+ def disableWarning(noWarn: NoWarnMissing) = noWarn match {
+ case NoWarnClass(className) =>
+ lookupClass(className).warnEnabled = false
+ case NoWarnMethod(className, methodName) =>
+ lookupClass(className).lookupMethod(methodName).warnEnabled = false
+ }
+
+ class ClassInfo(data: Infos.ClassInfo) {
+ val encodedName = data.encodedName
+ val ancestorCount = data.ancestorCount
+ val isStaticModule = data.kind == ClassKind.ModuleClass
+ val isInterface = data.kind == ClassKind.Interface
+ val isImplClass = data.kind == ClassKind.TraitImpl
+ val isRawJSType = data.kind == ClassKind.RawJSType
+ val isHijackedClass = data.kind == ClassKind.HijackedClass
+ val isClass = !isInterface && !isImplClass && !isRawJSType
+ val isExported = data.isExported
+
+ val hasData = !isImplClass
+ val hasMoreThanData = isClass && !isHijackedClass
+
+ var superClass: ClassInfo = _
+ val ancestors = mutable.ListBuffer.empty[ClassInfo]
+ val descendants = mutable.ListBuffer.empty[ClassInfo]
+
+ var nonExistent: Boolean = false
+ var warnEnabled: Boolean = true
+
+ def linkClasses(): Unit = {
+ if (data.superClass != "")
+ superClass = lookupClass(data.superClass)
+ ancestors ++= data.ancestors.map(lookupClass)
+ for (ancestor <- ancestors)
+ ancestor.descendants += this
+ }
+
+ lazy val descendentClasses = descendants.filter(_.isClass)
+
+ def optimizerHints: Infos.OptimizerHints = data.optimizerHints
+
+ var isInstantiated: Boolean = false
+ var isAnySubclassInstantiated: Boolean = false
+ var isModuleAccessed: Boolean = false
+ var isDataAccessed: Boolean = false
+
+ var instantiatedFrom: Option[From] = None
+
+ val delayedCalls = mutable.Map.empty[String, From]
+
+ def isNeededAtAll =
+ isDataAccessed ||
+ isAnySubclassInstantiated ||
+ (isImplClass && methodInfos.values.exists(_.isReachable))
+
+ lazy val methodInfos: mutable.Map[String, MethodInfo] = {
+ val ms = for (methodData <- data.methods)
+ yield (methodData.encodedName, new MethodInfo(this, methodData))
+ mutable.Map.empty[String, MethodInfo] ++ ms
+ }
+
+ def lookupMethod(methodName: String): MethodInfo = {
+ tryLookupMethod(methodName).getOrElse {
+ val syntheticData = createMissingMethodInfo(methodName)
+ val m = new MethodInfo(this, syntheticData)
+ m.nonExistent = true
+ methodInfos += methodName -> m
+ m
+ }
+ }
+
+ def tryLookupMethod(methodName: String): Option[MethodInfo] = {
+ assert(isClass || isImplClass,
+ s"Cannot call lookupMethod($methodName) on non-class $this")
+ @tailrec
+ def loop(ancestorInfo: ClassInfo): Option[MethodInfo] = {
+ if (ancestorInfo ne null) {
+ ancestorInfo.methodInfos.get(methodName) match {
+ case Some(m) if !m.isAbstract => Some(m)
+ case _ => loop(ancestorInfo.superClass)
+ }
+ } else {
+ None
+ }
+ }
+ loop(this)
+ }
+
+ override def toString(): String = encodedName
+
+ /** Start reachability algorithm with the exports for that class. */
+ def reachExports(): Unit = {
+ implicit val from = FromExports
+
+ // Myself
+ if (isExported) {
+ assert(!isImplClass, "An implementation class must not be exported")
+ if (isStaticModule) accessModule()
+ else instantiated()
+ }
+
+ // My methods
+ for (methodInfo <- methodInfos.values) {
+ if (methodInfo.isExported)
+ callMethod(methodInfo.encodedName)
+ }
+ }
+
+ def accessModule()(implicit from: From): Unit = {
+ assert(isStaticModule, s"Cannot call accessModule() on non-module $this")
+ if (!isModuleAccessed) {
+ logger.debugIndent(s"$this.isModuleAccessed = true") {
+ isModuleAccessed = true
+ instantiated()
+ callMethod("init___")
+ }
+ }
+ }
+
+ def instantiated()(implicit from: From): Unit = {
+ if (!isInstantiated && isClass) {
+ logger.debugIndent(s"$this.isInstantiated = true") {
+ isInstantiated = true
+ instantiatedFrom = Some(from)
+ ancestors.foreach(_.subclassInstantiated())
+ }
+
+ for ((methodName, from) <- delayedCalls)
+ delayedCallMethod(methodName)(from)
+ }
+ }
+
+ private def subclassInstantiated()(implicit from: From): Unit = {
+ if (!isAnySubclassInstantiated && isClass) {
+ logger.debugIndent(s"$this.isAnySubclassInstantiated = true") {
+ isAnySubclassInstantiated = true
+ if (instantiatedFrom.isEmpty)
+ instantiatedFrom = Some(from)
+ accessData()
+ methodInfos.get("__init__").foreach(_.reachStatic())
+ }
+ }
+ }
+
+ def accessData()(implicit from: From): Unit = {
+ if (!isDataAccessed && hasData) {
+ checkExistent()
+ if (DebugAnalyzer)
+ logger.debug(s"$this.isDataAccessed = true")
+ isDataAccessed = true
+ }
+ }
+
+ def checkExistent()(implicit from: From): Unit = {
+ if (nonExistent) {
+ if (warnEnabled && globalWarnEnabled) {
+ logger.warn(s"Referring to non-existent class $encodedName")
+ warnCallStack()
+ }
+ nonExistent = false
+ allAvailable = false
+ }
+ }
+
+ def callMethod(methodName: String, static: Boolean = false)(
+ implicit from: From): Unit = {
+ logger.debugIndent(s"calling${if (static) " static" else ""} $this.$methodName") {
+ if (isImplClass) {
+ // methods in impl classes are always implicitly called statically
+ lookupMethod(methodName).reachStatic()
+ } else if (isConstructorName(methodName)) {
+ // constructors are always implicitly called statically
+ lookupMethod(methodName).reachStatic()
+ } else if (static) {
+ assert(!isReflProxyName(methodName),
+ s"Trying to call statically refl proxy $this.$methodName")
+ lookupMethod(methodName).reachStatic()
+ } else {
+ for (descendentClass <- descendentClasses) {
+ if (descendentClass.isInstantiated)
+ descendentClass.delayedCallMethod(methodName)
+ else
+ descendentClass.delayedCalls += ((methodName, from))
+ }
+ }
+ }
+ }
+
+ private def delayedCallMethod(methodName: String)(implicit from: From): Unit = {
+ if (isReflProxyName(methodName)) {
+ tryLookupMethod(methodName).foreach(_.reach(this))
+ } else {
+ lookupMethod(methodName).reach(this)
+ }
+ }
+ }
+
+ class MethodInfo(val owner: ClassInfo, data: Infos.MethodInfo) {
+
+ val encodedName = data.encodedName
+ val isAbstract = data.isAbstract
+ val isExported = data.isExported
+ val isReflProxy = isReflProxyName(encodedName)
+
+ def optimizerHints: Infos.OptimizerHints = data.optimizerHints
+
+ var isReachable: Boolean = false
+
+ var calledFrom: Option[From] = None
+ var instantiatedSubclass: Option[ClassInfo] = None
+
+ var nonExistent: Boolean = false
+ var warnEnabled: Boolean = true
+
+ override def toString(): String = s"$owner.$encodedName"
+
+ def reachStatic()(implicit from: From): Unit = {
+ assert(!isAbstract,
+ s"Trying to reach statically the abstract method $this")
+
+ checkExistent()
+
+ if (!isReachable) {
+ logger.debugIndent(s"$this.isReachable = true") {
+ isReachable = true
+ calledFrom = Some(from)
+ doReach()
+ }
+ }
+ }
+
+ def reach(inClass: ClassInfo)(implicit from: From): Unit = {
+ assert(owner.isClass,
+ s"Trying to reach dynamically the non-class method $this")
+ assert(!isConstructorName(encodedName),
+ s"Trying to reach dynamically the constructor $this")
+
+ checkExistent()
+
+ if (!isReachable) {
+ logger.debugIndent(s"$this.isReachable = true") {
+ isReachable = true
+ calledFrom = Some(from)
+ instantiatedSubclass = Some(inClass)
+ doReach()
+ }
+ }
+ }
+
+ private def checkExistent()(implicit from: From) = {
+ if (nonExistent) {
+ if (warnEnabled && owner.warnEnabled && globalWarnEnabled) {
+ logger.temporarilyNotIndented {
+ logger.warn(s"Referring to non-existent method $this")
+ warnCallStack()
+ }
+ }
+ allAvailable = false
+ }
+ }
+
+ private[this] def doReach(): Unit = {
+ logger.debugIndent(s"$this.doReach()") {
+ implicit val from = FromMethod(this)
+
+ if (owner.isImplClass)
+ owner.checkExistent()
+
+ for (moduleName <- data.accessedModules) {
+ lookupModule(moduleName).accessModule()
+ }
+
+ for (className <- data.instantiatedClasses) {
+ lookupClass(className).instantiated()
+ }
+
+ for (className <- data.accessedClassData) {
+ lookupClass(className).accessData()
+ }
+
+ for ((className, methods) <- data.calledMethods) {
+ val classInfo = lookupClass(className)
+ for (methodName <- methods)
+ classInfo.callMethod(methodName)
+ }
+
+ for ((className, methods) <- data.calledMethodsStatic) {
+ val classInfo = lookupClass(className)
+ for (methodName <- methods)
+ classInfo.callMethod(methodName, static = true)
+ }
+ }
+ }
+ }
+
+ def isReflProxyName(encodedName: String): Boolean = {
+ encodedName.endsWith("__") &&
+ (encodedName != "init___") && (encodedName != "__init__")
+ }
+
+ def isConstructorName(encodedName: String): Boolean =
+ encodedName.startsWith("init___") || (encodedName == "__init__")
+
+ private def createMissingClassInfo(encodedName: String): Infos.ClassInfo = {
+ val kind =
+ if (encodedName.endsWith("$")) ClassKind.ModuleClass
+ else if (encodedName.endsWith("$class")) ClassKind.TraitImpl
+ else ClassKind.Class
+ Infos.ClassInfo(
+ name = s"<$encodedName>",
+ encodedName = encodedName,
+ isExported = false,
+ ancestorCount = if (kind.isClass) 1 else 0,
+ kind = kind,
+ superClass = if (kind.isClass) "O" else "",
+ ancestors = List(encodedName, "O"),
+ methods = List(
+ createMissingMethodInfo("__init__"),
+ createMissingMethodInfo("init___"))
+ )
+ }
+
+ private def createMissingMethodInfo(encodedName: String,
+ isAbstract: Boolean = false): Infos.MethodInfo = {
+ Infos.MethodInfo(encodedName = encodedName, isAbstract = isAbstract)
+ }
+
+ def warnCallStack()(implicit from: From): Unit = {
+ val seenInfos = mutable.Set.empty[AnyRef]
+
+ def rec(level: Level, optFrom: Option[From],
+ verb: String = "called"): Unit = {
+ val involvedClasses = new mutable.ListBuffer[ClassInfo]
+
+ def onlyOnce(info: AnyRef): Boolean = {
+ if (seenInfos.add(info)) {
+ true
+ } else {
+ logger.log(level, " (already seen, not repeating call stack)")
+ false
+ }
+ }
+
+ @tailrec
+ def loopTrace(optFrom: Option[From], verb: String = "called"): Unit = {
+ optFrom match {
+ case None =>
+ logger.log(level, s"$verb from ... er ... nowhere!? (this is a bug in dce)")
+ case Some(from) =>
+ from match {
+ case FromMethod(methodInfo) =>
+ logger.log(level, s"$verb from $methodInfo")
+ if (onlyOnce(methodInfo)) {
+ methodInfo.instantiatedSubclass.foreach(involvedClasses += _)
+ loopTrace(methodInfo.calledFrom)
+ }
+ case FromCore =>
+ logger.log(level, s"$verb from scalajs-corejslib.js")
+ case FromExports =>
+ logger.log(level, "exported to JavaScript with @JSExport")
+ case FromManual =>
+ logger.log(level, "manually made reachable")
+ }
+ }
+ }
+
+ logger.indented {
+ loopTrace(optFrom, verb = verb)
+ }
+
+ if (involvedClasses.nonEmpty) {
+ logger.log(level, "involving instantiated classes:")
+ logger.indented {
+ for (classInfo <- involvedClasses.result().distinct) {
+ logger.log(level, s"$classInfo")
+ if (onlyOnce(classInfo))
+ rec(Level.Debug, classInfo.instantiatedFrom, verb = "instantiated")
+ // recurse with Debug log level not to overwhelm the user
+ }
+ }
+ }
+ }
+
+ rec(Level.Warn, Some(from))
+ }
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/GenIncOptimizer.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/GenIncOptimizer.scala
new file mode 100644
index 0000000..47e1f87
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/GenIncOptimizer.scala
@@ -0,0 +1,921 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.optimizer
+
+import language.higherKinds
+
+import scala.annotation.{switch, tailrec}
+
+import scala.collection.{GenMap, GenTraversableOnce, GenIterable, GenIterableLike}
+import scala.collection.mutable
+
+import scala.scalajs.ir._
+import Definitions.isConstructorName
+import Infos.OptimizerHints
+import Trees._
+import Types._
+
+import scala.scalajs.tools.sem._
+
+import scala.scalajs.tools.javascript
+import javascript.Trees.{Tree => JSTree}
+import javascript.ScalaJSClassEmitter
+
+import scala.scalajs.tools.logging._
+
+/** Incremental optimizer.
+ * An incremental optimizer consumes the reachability analysis produced by
+ * an [[Analyzer]], as well as trees for classes, trait impls, etc., and
+ * optimizes them in an incremental way.
+ * It maintains state between runs to do a minimal amount of work on every
+ * run, based on detecting what parts of the program must be re-optimized,
+ * and keeping optimized results from previous runs for the rest.
+ */
+abstract class GenIncOptimizer(semantics: Semantics) {
+ import GenIncOptimizer._
+
+ protected val CollOps: AbsCollOps
+
+ private val classEmitter = new ScalaJSClassEmitter(semantics)
+
+ private var logger: Logger = _
+
+ /** Are we in batch mode? I.e., are we running from scratch?
+ * Various parts of the algorithm can be skipped entirely when running in
+ * batch mode.
+ */
+ private var batchMode: Boolean = false
+
+ /** Should positions be considered when comparing tree hashes */
+ private var considerPositions: Boolean = _
+
+ private var objectClass: Class = _
+ private val classes = CollOps.emptyMap[String, Class]
+ private val traitImpls = CollOps.emptyParMap[String, TraitImpl]
+
+ protected def getInterface(encodedName: String): InterfaceType
+
+ /** Schedule a method for processing in the PROCESS PASS */
+ protected def scheduleMethod(method: MethodImpl): Unit
+
+ protected def newMethodImpl(owner: MethodContainer,
+ encodedName: String): MethodImpl
+
+ def findTraitImpl(encodedName: String): TraitImpl = traitImpls(encodedName)
+ def findClass(encodedName: String): Class = classes(encodedName)
+
+ def getTraitImpl(encodedName: String): Option[TraitImpl] = traitImpls.get(encodedName)
+ def getClass(encodedName: String): Option[Class] = classes.get(encodedName)
+
+ type GetClassTreeIfChanged =
+ (String, Option[String]) => Option[(ClassDef, Option[String])]
+
+ private def withLogger[A](logger: Logger)(body: => A): A = {
+ assert(this.logger == null)
+ this.logger = logger
+ try body
+ finally this.logger = null
+ }
+
+ /** Update the incremental analyzer with a new run. */
+ def update(analyzer: Analyzer,
+ getClassTreeIfChanged: GetClassTreeIfChanged, considerPositions: Boolean,
+ logger: Logger): Unit = withLogger(logger) {
+
+ batchMode = objectClass == null
+ this.considerPositions = considerPositions
+ logger.debug(s"Optimizer batch mode: $batchMode")
+
+ logTime(logger, "Incremental part of inc. optimizer") {
+ /* UPDATE PASS */
+ updateAndTagEverything(analyzer, getClassTreeIfChanged)
+ }
+
+ logTime(logger, "Optimizer part of inc. optimizer") {
+ /* PROCESS PASS */
+ processAllTaggedMethods()
+ }
+ }
+
+ /** Incremental part: update state and detect what needs to be re-optimized.
+ * UPDATE PASS ONLY. (This IS the update pass).
+ */
+ private def updateAndTagEverything(analyzer: Analyzer,
+ getClassTreeIfChanged: GetClassTreeIfChanged): Unit = {
+
+ val neededClasses = CollOps.emptyParMap[String, analyzer.ClassInfo]
+ val neededTraitImpls = CollOps.emptyParMap[String, analyzer.ClassInfo]
+ for {
+ classInfo <- analyzer.classInfos.values
+ if classInfo.isNeededAtAll
+ } {
+ if (classInfo.isClass && classInfo.isAnySubclassInstantiated)
+ CollOps.put(neededClasses, classInfo.encodedName, classInfo)
+ else if (classInfo.isImplClass)
+ CollOps.put(neededTraitImpls, classInfo.encodedName, classInfo)
+ }
+
+ /* Remove deleted trait impls, and update existing trait impls.
+ * We don't even have to notify callers in case of additions or removals
+ * because callers have got to be invalidated by themselves.
+ * Only changed methods need to trigger notifications.
+ *
+ * Non-batch mode only.
+ */
+ assert(!batchMode || traitImpls.isEmpty)
+ if (!batchMode) {
+ CollOps.retain(traitImpls) { (traitImplName, traitImpl) =>
+ CollOps.remove(neededTraitImpls, traitImplName).fold {
+ /* Deleted trait impl. Mark all its methods as deleted, and remove it
+ * from known trait impls.
+ */
+ traitImpl.methods.values.foreach(_.delete())
+
+ false
+ } { traitImplInfo =>
+ /* Existing trait impl. Update it. */
+ val (added, changed, removed) =
+ traitImpl.updateWith(traitImplInfo, getClassTreeIfChanged)
+ for (method <- changed)
+ traitImpl.myInterface.tagStaticCallersOf(method)
+
+ true
+ }
+ }
+ }
+
+ /* Add new trait impls.
+ * Easy, we don't have to notify anyone.
+ */
+ for (traitImplInfo <- neededTraitImpls.values) {
+ val traitImpl = new TraitImpl(traitImplInfo.encodedName)
+ CollOps.put(traitImpls, traitImpl.encodedName, traitImpl)
+ traitImpl.updateWith(traitImplInfo, getClassTreeIfChanged)
+ }
+
+ if (!batchMode) {
+ /* Class removals:
+ * * If a class is deleted or moved, delete its entire subtree (because
+ * all its descendants must also be deleted or moved).
+ * * If an existing class was instantiated but is no more, notify callers
+ * of its methods.
+ *
+ * Non-batch mode only.
+ */
+ val objectClassStillExists =
+ objectClass.walkClassesForDeletions(neededClasses.get(_))
+ assert(objectClassStillExists, "Uh oh, java.lang.Object was deleted!")
+
+ /* Class changes:
+ * * Delete removed methods, update existing ones, add new ones
+ * * Update the list of ancestors
+ * * Class newly instantiated
+ *
+ * Non-batch mode only.
+ */
+ objectClass.walkForChanges(
+ CollOps.remove(neededClasses, _).get,
+ getClassTreeIfChanged,
+ Set.empty)
+ }
+
+ /* Class additions:
+ * * Add new classes (including those that have moved from elsewhere).
+ * In batch mode, we avoid doing notifications.
+ */
+
+ // Group children by (immediate) parent
+ val newChildrenByParent = CollOps.emptyAccMap[String, Analyzer#ClassInfo]
+
+ for (classInfo <- neededClasses.values) {
+ val superInfo = classInfo.superClass
+ if (superInfo == null) {
+ assert(batchMode, "Trying to add java.lang.Object in incremental mode")
+ objectClass = new Class(None, classInfo.encodedName)
+ classes += classInfo.encodedName -> objectClass
+ objectClass.setupAfterCreation(classInfo, getClassTreeIfChanged)
+ } else {
+ CollOps.acc(newChildrenByParent, superInfo.encodedName, classInfo)
+ }
+ }
+
+ val getNewChildren =
+ (name: String) => CollOps.getAcc(newChildrenByParent, name)
+
+ // Walk the tree to add children
+ if (batchMode) {
+ objectClass.walkForAdditions(getNewChildren, getClassTreeIfChanged)
+ } else {
+ val existingParents =
+ CollOps.parFlatMapKeys(newChildrenByParent)(classes.get)
+ for (parent <- existingParents)
+ parent.walkForAdditions(getNewChildren, getClassTreeIfChanged)
+ }
+
+ }
+
+ /** Optimizer part: process all methods that need reoptimizing.
+ * PROCESS PASS ONLY. (This IS the process pass).
+ */
+ protected def processAllTaggedMethods(): Unit
+
+ protected def logProcessingMethods(count: Int): Unit =
+ logger.debug(s"Optimizing $count methods.")
+
+ /** Base class for [[Class]] and [[TraitImpl]]. */
+ abstract class MethodContainer(val encodedName: String) {
+ def thisType: Type
+
+ val myInterface = getInterface(encodedName)
+
+ val methods = mutable.Map.empty[String, MethodImpl]
+
+ var lastVersion: Option[String] = None
+
+ private def reachableMethodsOf(info: Analyzer#ClassInfo): Set[String] = {
+ (for {
+ methodInfo <- info.methodInfos.values
+ if methodInfo.isReachable && !methodInfo.isAbstract
+ } yield {
+ methodInfo.encodedName
+ }).toSet
+ }
+
+ /** UPDATE PASS ONLY. Global concurrency safe but not on same instance */
+ def updateWith(info: Analyzer#ClassInfo,
+ getClassTreeIfChanged: GetClassTreeIfChanged): (Set[String], Set[String], Set[String]) = {
+ myInterface.ancestors = info.ancestors.map(_.encodedName).toList
+
+ val addedMethods = Set.newBuilder[String]
+ val changedMethods = Set.newBuilder[String]
+ val deletedMethods = Set.newBuilder[String]
+
+ val reachableMethods = reachableMethodsOf(info)
+ val methodSetChanged = methods.keySet != reachableMethods
+ if (methodSetChanged) {
+ // Remove deleted methods
+ methods retain { (methodName, method) =>
+ if (reachableMethods.contains(methodName)) {
+ true
+ } else {
+ deletedMethods += methodName
+ method.delete()
+ false
+ }
+ }
+ // Clear lastVersion if there are new methods
+ if (reachableMethods.exists(!methods.contains(_)))
+ lastVersion = None
+ }
+ for ((tree, version) <- getClassTreeIfChanged(encodedName, lastVersion)) {
+ lastVersion = version
+ this match {
+ case cls: Class =>
+ cls.isModuleClass = tree.kind == ClassKind.ModuleClass
+ cls.fields = for (field @ VarDef(_, _, _, _) <- tree.defs) yield field
+ case _ =>
+ }
+ tree.defs.foreach {
+ case methodDef: MethodDef if methodDef.name.isInstanceOf[Ident] &&
+ reachableMethods.contains(methodDef.name.name) =>
+ val methodName = methodDef.name.name
+
+ val methodInfo = info.methodInfos(methodName)
+ methods.get(methodName).fold {
+ addedMethods += methodName
+ val method = newMethodImpl(this, methodName)
+ method.updateWith(methodInfo, methodDef)
+ methods(methodName) = method
+ method
+ } { method =>
+ if (method.updateWith(methodInfo, methodDef))
+ changedMethods += methodName
+ method
+ }
+
+ case _ => // ignore
+ }
+ }
+
+ (addedMethods.result(), changedMethods.result(), deletedMethods.result())
+ }
+ }
+
+ /** Class in the class hierarchy (not an interface).
+ * A class may be a module class.
+ * A class knows its superclass and the interfaces it implements. It also
+ * maintains a list of its direct subclasses, so that the instances of
+ * [[Class]] form a tree of the class hierarchy.
+ */
+ class Class(val superClass: Option[Class],
+ _encodedName: String) extends MethodContainer(_encodedName) {
+ if (encodedName == Definitions.ObjectClass) {
+ assert(superClass.isEmpty)
+ assert(objectClass == null)
+ } else {
+ assert(superClass.isDefined)
+ }
+
+ /** Parent chain from this to Object. */
+ val parentChain: List[Class] =
+ this :: superClass.fold[List[Class]](Nil)(_.parentChain)
+
+ /** Reverse parent chain from Object to this. */
+ val reverseParentChain: List[Class] =
+ parentChain.reverse
+
+ def thisType: Type = ClassType(encodedName)
+
+ var interfaces: Set[InterfaceType] = Set.empty
+ var subclasses: CollOps.ParIterable[Class] = CollOps.emptyParIterable
+ var isInstantiated: Boolean = false
+
+ var isModuleClass: Boolean = false
+ var hasElidableModuleAccessor: Boolean = false
+
+ var fields: List[VarDef] = Nil
+ var isInlineable: Boolean = false
+ var tryNewInlineable: Option[RecordValue] = None
+
+ override def toString(): String =
+ encodedName
+
+ /** Walk the class hierarchy tree for deletions.
+ * This includes "deleting" classes that were previously instantiated but
+ * are no more.
+ * UPDATE PASS ONLY. Not concurrency safe on same instance.
+ */
+ def walkClassesForDeletions(
+ getClassInfoIfNeeded: String => Option[Analyzer#ClassInfo]): Boolean = {
+ def sameSuperClass(info: Analyzer#ClassInfo): Boolean =
+ if (info.superClass == null) superClass.isEmpty
+ else superClass.exists(_.encodedName == info.superClass.encodedName)
+
+ getClassInfoIfNeeded(encodedName) match {
+ case Some(classInfo) if sameSuperClass(classInfo) =>
+ // Class still exists. Recurse.
+ subclasses = subclasses.filter(
+ _.walkClassesForDeletions(getClassInfoIfNeeded))
+ if (isInstantiated && !classInfo.isInstantiated)
+ notInstantiatedAnymore()
+ true
+ case _ =>
+ // Class does not exist or has been moved. Delete the entire subtree.
+ deleteSubtree()
+ false
+ }
+ }
+
+ /** Delete this class and all its subclasses. UPDATE PASS ONLY. */
+ def deleteSubtree(): Unit = {
+ delete()
+ for (subclass <- subclasses)
+ subclass.deleteSubtree()
+ }
+
+ /** UPDATE PASS ONLY. */
+ private def delete(): Unit = {
+ if (isInstantiated)
+ notInstantiatedAnymore()
+ for (method <- methods.values)
+ method.delete()
+ classes -= encodedName
+ /* Note: no need to tag methods that call *statically* one of the methods
+ * of the deleted classes, since they've got to be invalidated by
+ * themselves.
+ */
+ }
+
+ /** UPDATE PASS ONLY. */
+ def notInstantiatedAnymore(): Unit = {
+ assert(isInstantiated)
+ isInstantiated = false
+ for (intf <- interfaces) {
+ intf.removeInstantiatedSubclass(this)
+ for (methodName <- allMethods().keys)
+ intf.tagDynamicCallersOf(methodName)
+ }
+ }
+
+ /** UPDATE PASS ONLY. */
+ def walkForChanges(
+ getClassInfo: String => Analyzer#ClassInfo,
+ getClassTreeIfChanged: GetClassTreeIfChanged,
+ parentMethodAttributeChanges: Set[String]): Unit = {
+
+ val classInfo = getClassInfo(encodedName)
+
+ val (addedMethods, changedMethods, deletedMethods) =
+ updateWith(classInfo, getClassTreeIfChanged)
+
+ val oldInterfaces = interfaces
+ val newInterfaces =
+ classInfo.ancestors.map(info => getInterface(info.encodedName)).toSet
+ interfaces = newInterfaces
+
+ val methodAttributeChanges =
+ (parentMethodAttributeChanges -- methods.keys ++
+ addedMethods ++ changedMethods ++ deletedMethods)
+
+ // Tag callers with dynamic calls
+ val wasInstantiated = isInstantiated
+ isInstantiated = classInfo.isInstantiated
+ assert(!(wasInstantiated && !isInstantiated),
+ "(wasInstantiated && !isInstantiated) should have been handled "+
+ "during deletion phase")
+
+ if (isInstantiated) {
+ if (wasInstantiated) {
+ val existingInterfaces = oldInterfaces.intersect(newInterfaces)
+ for {
+ intf <- existingInterfaces
+ methodName <- methodAttributeChanges
+ } {
+ intf.tagDynamicCallersOf(methodName)
+ }
+ if (newInterfaces.size != oldInterfaces.size ||
+ newInterfaces.size != existingInterfaces.size) {
+ val allMethodNames = allMethods().keys
+ for {
+ intf <- oldInterfaces ++ newInterfaces -- existingInterfaces
+ methodName <- allMethodNames
+ } {
+ intf.tagDynamicCallersOf(methodName)
+ }
+ }
+ } else {
+ val allMethodNames = allMethods().keys
+ for (intf <- interfaces) {
+ intf.addInstantiatedSubclass(this)
+ for (methodName <- allMethodNames)
+ intf.tagDynamicCallersOf(methodName)
+ }
+ }
+ }
+
+ // Tag callers with static calls
+ for (methodName <- methodAttributeChanges)
+ myInterface.tagStaticCallersOf(methodName)
+
+ // Module class specifics
+ updateHasElidableModuleAccessor()
+
+ // Inlineable class
+ if (updateIsInlineable(classInfo)) {
+ for (method <- methods.values; if isConstructorName(method.encodedName))
+ myInterface.tagStaticCallersOf(method.encodedName)
+ }
+
+ // Recurse in subclasses
+ for (cls <- subclasses)
+ cls.walkForChanges(getClassInfo, getClassTreeIfChanged,
+ methodAttributeChanges)
+ }
+
+ /** UPDATE PASS ONLY. */
+ def walkForAdditions(
+ getNewChildren: String => GenIterable[Analyzer#ClassInfo],
+ getClassTreeIfChanged: GetClassTreeIfChanged): Unit = {
+
+ val subclassAcc = CollOps.prepAdd(subclasses)
+
+ for (classInfo <- getNewChildren(encodedName)) {
+ val cls = new Class(Some(this), classInfo.encodedName)
+ CollOps.add(subclassAcc, cls)
+ classes += classInfo.encodedName -> cls
+ cls.setupAfterCreation(classInfo, getClassTreeIfChanged)
+ cls.walkForAdditions(getNewChildren, getClassTreeIfChanged)
+ }
+
+ subclasses = CollOps.finishAdd(subclassAcc)
+ }
+
+ /** UPDATE PASS ONLY. */
+ def updateHasElidableModuleAccessor(): Unit = {
+ hasElidableModuleAccessor =
+ isAdHocElidableModuleAccessor(encodedName) ||
+ (isModuleClass && lookupMethod("init___").exists(isElidableModuleConstructor))
+ }
+
+ /** UPDATE PASS ONLY. */
+ def updateIsInlineable(classInfo: Analyzer#ClassInfo): Boolean = {
+ val oldTryNewInlineable = tryNewInlineable
+ isInlineable = classInfo.optimizerHints.hasInlineAnnot
+ if (!isInlineable) {
+ tryNewInlineable = None
+ } else {
+ val allFields = reverseParentChain.flatMap(_.fields)
+ val (fieldValues, fieldTypes) = (for {
+ VarDef(Ident(name, originalName), tpe, mutable, rhs) <- allFields
+ } yield {
+ (rhs, RecordType.Field(name, originalName, tpe, mutable))
+ }).unzip
+ tryNewInlineable = Some(
+ RecordValue(RecordType(fieldTypes), fieldValues)(Position.NoPosition))
+ }
+ tryNewInlineable != oldTryNewInlineable
+ }
+
+ /** UPDATE PASS ONLY. */
+ def setupAfterCreation(classInfo: Analyzer#ClassInfo,
+ getClassTreeIfChanged: GetClassTreeIfChanged): Unit = {
+
+ updateWith(classInfo, getClassTreeIfChanged)
+ interfaces =
+ classInfo.ancestors.map(info => getInterface(info.encodedName)).toSet
+
+ isInstantiated = classInfo.isInstantiated
+
+ if (batchMode) {
+ if (isInstantiated) {
+ /* Only add the class to all its ancestor interfaces */
+ for (intf <- interfaces)
+ intf.addInstantiatedSubclass(this)
+ }
+ } else {
+ val allMethodNames = allMethods().keys
+
+ if (isInstantiated) {
+ /* Add the class to all its ancestor interfaces + notify all callers
+ * of any of the methods.
+ * TODO: be more selective on methods that are notified: it is not
+ * necessary to modify callers of methods defined in a parent class
+ * that already existed in the previous run.
+ */
+ for (intf <- interfaces) {
+ intf.addInstantiatedSubclass(this)
+ for (methodName <- allMethodNames)
+ intf.tagDynamicCallersOf(methodName)
+ }
+ }
+
+ /* Tag static callers because the class could have been *moved*,
+ * not just added.
+ */
+ for (methodName <- allMethodNames)
+ myInterface.tagStaticCallersOf(methodName)
+ }
+
+ updateHasElidableModuleAccessor()
+ updateIsInlineable(classInfo)
+ }
+
+ /** UPDATE PASS ONLY. */
+ private def isElidableModuleConstructor(impl: MethodImpl): Boolean = {
+ def isTriviallySideEffectFree(tree: Tree): Boolean = tree match {
+ case _:VarRef | _:Literal | _:This => true
+ case _ => false
+ }
+ def isElidableStat(tree: Tree): Boolean = tree match {
+ case Block(stats) =>
+ stats.forall(isElidableStat)
+ case Assign(Select(This(), _, _), rhs) =>
+ isTriviallySideEffectFree(rhs)
+ case TraitImplApply(ClassType(traitImpl), methodName, List(This())) =>
+ traitImpls(traitImpl).methods(methodName.name).originalDef.body match {
+ case Skip() => true
+ case _ => false
+ }
+ case StaticApply(This(), ClassType(cls), methodName, args) =>
+ Definitions.isConstructorName(methodName.name) &&
+ args.forall(isTriviallySideEffectFree) &&
+ impl.owner.asInstanceOf[Class].superClass.exists { superCls =>
+ superCls.encodedName == cls &&
+ superCls.lookupMethod(methodName.name).exists(isElidableModuleConstructor)
+ }
+ case StoreModule(_, _) =>
+ true
+ case _ =>
+ isTriviallySideEffectFree(tree)
+ }
+ isElidableStat(impl.originalDef.body)
+ }
+
+ /** All the methods of this class, including inherited ones.
+ * It has () so we remember this is an expensive operation.
+ * UPDATE PASS ONLY.
+ */
+ def allMethods(): scala.collection.Map[String, MethodImpl] = {
+ val result = mutable.Map.empty[String, MethodImpl]
+ for (parent <- reverseParentChain)
+ result ++= parent.methods
+ result
+ }
+
+ /** BOTH PASSES. */
+ @tailrec
+ final def lookupMethod(methodName: String): Option[MethodImpl] = {
+ methods.get(methodName) match {
+ case Some(impl) => Some(impl)
+ case none =>
+ superClass match {
+ case Some(p) => p.lookupMethod(methodName)
+ case none => None
+ }
+ }
+ }
+ }
+
+ /** Trait impl. */
+ class TraitImpl(_encodedName: String) extends MethodContainer(_encodedName) {
+ def thisType: Type = NoType
+ }
+
+ /** Thing from which a [[MethodImpl]] can unregister itself from. */
+ trait Unregisterable {
+ /** UPDATE PASS ONLY. */
+ def unregisterDependee(dependee: MethodImpl): Unit
+ }
+
+ /** Type of a class or interface.
+ * Types are created on demand when a method is called on a given
+ * [[ClassType]].
+ *
+ * Fully concurrency safe unless otherwise noted.
+ */
+ abstract class InterfaceType(val encodedName: String) extends Unregisterable {
+
+ override def toString(): String =
+ s"intf $encodedName"
+
+ /** PROCESS PASS ONLY. Concurrency safe except with
+ * [[addInstantiatedSubclass]] and [[removeInstantiatedSubclass]]
+ */
+ def instantiatedSubclasses: Iterable[Class]
+
+ /** UPDATE PASS ONLY. Concurrency safe except with
+ * [[instantiatedSubclasses]]
+ */
+ def addInstantiatedSubclass(x: Class): Unit
+
+ /** UPDATE PASS ONLY. Concurrency safe except with
+ * [[instantiatedSubclasses]]
+ */
+ def removeInstantiatedSubclass(x: Class): Unit
+
+ /** PROCESS PASS ONLY. Concurrency safe except with [[ancestors_=]] */
+ def ancestors: List[String]
+
+ /** UPDATE PASS ONLY. Not concurrency safe. */
+ def ancestors_=(v: List[String]): Unit
+
+ /** PROCESS PASS ONLY. Concurrency safe except with [[ancestors_=]]. */
+ def registerAskAncestors(asker: MethodImpl): Unit
+
+ /** PROCESS PASS ONLY. */
+ def registerDynamicCaller(methodName: String, caller: MethodImpl): Unit
+
+ /** PROCESS PASS ONLY. */
+ def registerStaticCaller(methodName: String, caller: MethodImpl): Unit
+
+ /** UPDATE PASS ONLY. */
+ def tagDynamicCallersOf(methodName: String): Unit
+
+ /** UPDATE PASS ONLY. */
+ def tagStaticCallersOf(methodName: String): Unit
+ }
+
+ /** A method implementation.
+ * It must be concrete, and belong either to a [[Class]] or a [[TraitImpl]].
+ *
+ * A single instance is **not** concurrency safe (unless otherwise noted in
+ * a method comment). However, the global state modifications are
+ * concurrency safe.
+ */
+ abstract class MethodImpl(val owner: MethodContainer,
+ val encodedName: String) extends OptimizerCore.MethodImpl
+ with OptimizerCore.AbstractMethodID
+ with Unregisterable {
+ private[this] var _deleted: Boolean = false
+
+ var optimizerHints: OptimizerHints = OptimizerHints.empty
+ var originalDef: MethodDef = _
+ var desugaredDef: JSTree = _
+ var preciseInfo: Infos.MethodInfo = _
+
+ def thisType: Type = owner.thisType
+ def deleted: Boolean = _deleted
+
+ override def toString(): String =
+ s"$owner.$encodedName"
+
+ /** PROCESS PASS ONLY. */
+ def registerBodyAsker(asker: MethodImpl): Unit
+
+ /** UPDATE PASS ONLY. */
+ def tagBodyAskers(): Unit
+
+ /** PROCESS PASS ONLY. */
+ private def registerAskAncestors(intf: InterfaceType): Unit = {
+ intf.registerAskAncestors(this)
+ registeredTo(intf)
+ }
+
+ /** PROCESS PASS ONLY. */
+ private def registerDynamicCall(intf: InterfaceType,
+ methodName: String): Unit = {
+ intf.registerDynamicCaller(methodName, this)
+ registeredTo(intf)
+ }
+
+ /** PROCESS PASS ONLY. */
+ private def registerStaticCall(intf: InterfaceType,
+ methodName: String): Unit = {
+ intf.registerStaticCaller(methodName, this)
+ registeredTo(intf)
+ }
+
+ /** PROCESS PASS ONLY. */
+ def registerAskBody(target: MethodImpl): Unit = {
+ target.registerBodyAsker(this)
+ registeredTo(target)
+ }
+
+ /** PROCESS PASS ONLY. */
+ protected def registeredTo(intf: Unregisterable): Unit
+
+ /** UPDATE PASS ONLY. */
+ protected def unregisterFromEverywhere(): Unit
+
+ /** Return true iff this is the first time this method is called since the
+ * last reset (via [[resetTag]]).
+ * UPDATE PASS ONLY.
+ */
+ protected def protectTag(): Boolean
+
+ /** PROCESS PASS ONLY. */
+ protected def resetTag(): Unit
+
+ /** 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
+ * [[OptimizerCore.AbstractMethodID]].
+ * In the process, tags all the body askers if the body changes.
+ * UPDATE PASS ONLY. Not concurrency safe on same instance.
+ */
+ def updateWith(methodInfo: Analyzer#MethodInfo,
+ methodDef: MethodDef): Boolean = {
+ assert(!_deleted, "updateWith() called on a deleted method")
+
+ val bodyChanged = {
+ originalDef == null ||
+ (methodDef.hash zip originalDef.hash).forall {
+ case (h1, h2) => !Hashers.hashesEqual(h1, h2, considerPositions)
+ }
+ }
+
+ if (bodyChanged)
+ tagBodyAskers()
+
+ val hints = methodInfo.optimizerHints
+ val changed = hints != optimizerHints || bodyChanged
+ if (changed) {
+ val oldAttributes = (inlineable, isTraitImplForwarder)
+
+ optimizerHints = hints
+ originalDef = methodDef
+ desugaredDef = null
+ preciseInfo = null
+ updateInlineable()
+ tag()
+
+ val newAttributes = (inlineable, isTraitImplForwarder)
+ newAttributes != oldAttributes
+ } else {
+ false
+ }
+ }
+
+ /** UPDATE PASS ONLY. Not concurrency safe on same instance. */
+ 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.
+ */
+ def tag(): Unit = if (protectTag()) {
+ scheduleMethod(this)
+ unregisterFromEverywhere()
+ }
+
+ /** PROCESS PASS ONLY. */
+ def process(): Unit = if (!_deleted) {
+ val (optimizedDef, info) = new Optimizer().optimize(thisType, originalDef)
+ desugaredDef =
+ if (owner.isInstanceOf[Class])
+ classEmitter.genMethod(owner.encodedName, optimizedDef)
+ else
+ classEmitter.genTraitImplMethod(owner.encodedName, optimizedDef)
+ preciseInfo = info
+ resetTag()
+ }
+
+ /** All methods are PROCESS PASS ONLY */
+ private class Optimizer extends OptimizerCore(semantics) {
+ type MethodID = MethodImpl
+
+ val myself: MethodImpl.this.type = MethodImpl.this
+
+ protected def getMethodBody(method: MethodID): MethodDef = {
+ MethodImpl.this.registerAskBody(method)
+ method.originalDef
+ }
+
+ protected def dynamicCall(intfName: String,
+ methodName: String): List[MethodID] = {
+ val intf = getInterface(intfName)
+ MethodImpl.this.registerDynamicCall(intf, methodName)
+ intf.instantiatedSubclasses.flatMap(_.lookupMethod(methodName)).toList
+ }
+
+ protected def staticCall(className: String,
+ methodName: String): Option[MethodID] = {
+ val clazz = classes(className)
+ MethodImpl.this.registerStaticCall(clazz.myInterface, methodName)
+ clazz.lookupMethod(methodName)
+ }
+
+ protected def traitImplCall(traitImplName: String,
+ methodName: String): Option[MethodID] = {
+ val traitImpl = traitImpls(traitImplName)
+ registerStaticCall(traitImpl.myInterface, methodName)
+ traitImpl.methods.get(methodName)
+ }
+
+ protected def getAncestorsOf(intfName: String): List[String] = {
+ val intf = getInterface(intfName)
+ registerAskAncestors(intf)
+ intf.ancestors
+ }
+
+ protected def hasElidableModuleAccessor(moduleClassName: String): Boolean =
+ classes(moduleClassName).hasElidableModuleAccessor
+
+ protected def tryNewInlineableClass(className: String): Option[RecordValue] =
+ classes(className).tryNewInlineable
+ }
+ }
+
+}
+
+object GenIncOptimizer {
+
+ private val isAdHocElidableModuleAccessor =
+ Set("s_Predef$")
+
+ private[optimizer] def logTime[A](logger: Logger,
+ title: String)(body: => A): A = {
+ val startTime = System.nanoTime()
+ val result = body
+ val endTime = System.nanoTime()
+ val elapsedTime = endTime - startTime
+ logger.time(title, elapsedTime)
+ result
+ }
+
+ private[optimizer] trait AbsCollOps {
+ type Map[K, V] <: mutable.Map[K, V]
+ type ParMap[K, V] <: GenMap[K, V]
+ type AccMap[K, V]
+ type ParIterable[V] <: GenIterableLike[V, ParIterable[V]]
+ type Addable[V]
+
+ def emptyAccMap[K, V]: AccMap[K, V]
+ def emptyMap[K, V]: Map[K, V]
+ def emptyParMap[K, V]: ParMap[K, V]
+ def emptyParIterable[V]: ParIterable[V]
+
+ // Operations on ParMap
+ def put[K, V](map: ParMap[K, V], k: K, v: V): Unit
+ def remove[K, V](map: ParMap[K, V], k: K): Option[V]
+ def retain[K, V](map: ParMap[K, V])(p: (K, V) => Boolean): Unit
+
+ // Operations on AccMap
+ def acc[K, V](map: AccMap[K, V], k: K, v: V): Unit
+ def getAcc[K, V](map: AccMap[K, V], k: K): GenIterable[V]
+ def parFlatMapKeys[A, B](map: AccMap[A, _])(
+ f: A => GenTraversableOnce[B]): GenIterable[B]
+
+ // Operations on ParIterable
+ def prepAdd[V](it: ParIterable[V]): Addable[V]
+ def add[V](addable: Addable[V], v: V): Unit
+ def finishAdd[V](addable: Addable[V]): ParIterable[V]
+
+ }
+
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/IRChecker.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/IRChecker.scala
new file mode 100644
index 0000000..6329826
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/IRChecker.scala
@@ -0,0 +1,854 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.optimizer
+
+import scala.language.implicitConversions
+
+import scala.annotation.switch
+
+import scala.collection.mutable
+
+import scala.scalajs.ir._
+import Definitions._
+import Trees._
+import Types._
+
+import scala.scalajs.tools.logging._
+
+/** Checker for the validity of the IR. */
+class IRChecker(analyzer: Analyzer, allClassDefs: Seq[ClassDef], logger: Logger) {
+ import IRChecker._
+
+ private var _errorCount: Int = 0
+ def errorCount: Int = _errorCount
+
+ private val classes: mutable.Map[String, CheckedClass] = {
+ mutable.Map.empty[String, CheckedClass] ++=
+ allClassDefs.map(new CheckedClass(_)).map(c => c.name -> c)
+ }
+
+ def check(): Boolean = {
+ for {
+ classDef <- allClassDefs
+ if analyzer.classInfos(classDef.name.name).isNeededAtAll
+ } {
+ classDef.kind match {
+ case ClassKind.Class | ClassKind.ModuleClass => checkClass(classDef)
+ case ClassKind.TraitImpl => checkTraitImpl(classDef)
+ case _ =>
+ }
+ }
+ errorCount == 0
+ }
+
+ def checkClass(classDef: ClassDef): Unit = {
+ if (!analyzer.classInfos(classDef.name.name).isAnySubclassInstantiated)
+ return
+
+ for (member <- classDef.defs) {
+ implicit val ctx = ErrorContext(member)
+ member match {
+ // Scala declarations
+ case v @ VarDef(_, _, _, _) =>
+ checkFieldDef(v, classDef)
+ case m: MethodDef if m.name.isInstanceOf[Ident] =>
+ checkMethodDef(m, classDef)
+
+ // Exports
+ case m: MethodDef if m.name.isInstanceOf[StringLiteral] =>
+ checkExportedMethodDef(m, classDef)
+ case member @ PropertyDef(_: StringLiteral, _, _, _) =>
+ checkExportedPropertyDef(member, classDef)
+ case member @ ConstructorExportDef(_, _, _) =>
+ checkConstructorExportDef(member, classDef)
+ case member @ ModuleExportDef(_) =>
+ checkModuleExportDef(member, classDef)
+
+ // Anything else is illegal
+ case _ =>
+ reportError(s"Illegal class member of type ${member.getClass.getName}")
+ }
+ }
+ }
+
+ def checkTraitImpl(classDef: ClassDef): Unit = {
+ for (member <- classDef.defs) {
+ implicit val ctx = ErrorContext(member)
+ member match {
+ case m: MethodDef =>
+ checkMethodDef(m, classDef)
+ case _ =>
+ reportError(s"Invalid member for a TraitImpl")
+ }
+ }
+ }
+
+ def checkFieldDef(fieldDef: VarDef, classDef: ClassDef): Unit = {
+ val VarDef(name, tpe, mutable, rhs) = fieldDef
+ implicit val ctx = ErrorContext(fieldDef)
+
+ if (tpe == NoType)
+ reportError(s"VarDef cannot have type NoType")
+ else
+ typecheckExpect(rhs, Env.empty, tpe)
+ }
+
+ def checkMethodDef(methodDef: MethodDef, classDef: ClassDef): Unit = {
+ val MethodDef(Ident(name, _), params, resultType, body) = methodDef
+ implicit val ctx = ErrorContext(methodDef)
+
+ if (!analyzer.classInfos(classDef.name.name).methodInfos(name).isReachable)
+ return
+
+ for (ParamDef(name, tpe, _) <- params)
+ if (tpe == NoType)
+ reportError(s"Parameter $name has type NoType")
+
+ val resultTypeForSig =
+ if (isConstructorName(name)) NoType
+ else resultType
+
+ val advertizedSig = (params.map(_.ptpe), resultTypeForSig)
+ val sigFromName = inferMethodType(name,
+ inTraitImpl = classDef.kind == ClassKind.TraitImpl)
+ if (advertizedSig != sigFromName) {
+ reportError(
+ s"The signature of ${classDef.name.name}.$name, which is "+
+ s"$advertizedSig, does not match its name (should be $sigFromName).")
+ }
+
+ val thisType =
+ if (!classDef.kind.isClass) NoType
+ else ClassType(classDef.name.name)
+ val bodyEnv = Env.fromSignature(thisType, params, resultType)
+ if (resultType == NoType)
+ typecheckStat(body, bodyEnv)
+ else
+ typecheckExpect(body, bodyEnv, resultType)
+ }
+
+ def checkExportedMethodDef(methodDef: MethodDef, classDef: ClassDef): Unit = {
+ val MethodDef(_, params, resultType, body) = methodDef
+ implicit val ctx = ErrorContext(methodDef)
+
+ if (!classDef.kind.isClass) {
+ reportError(s"Exported method def can only appear in a class")
+ return
+ }
+
+ for (ParamDef(name, tpe, _) <- params) {
+ if (tpe == NoType)
+ reportError(s"Parameter $name has type NoType")
+ else if (tpe != AnyType)
+ reportError(s"Parameter $name of exported method def has type $tpe, "+
+ "but must be Any")
+ }
+
+ if (resultType != AnyType) {
+ reportError(s"Result type of exported method def is $resultType, "+
+ "but must be Any")
+ }
+
+ val thisType = ClassType(classDef.name.name)
+ val bodyEnv = Env.fromSignature(thisType, params, resultType)
+ .withArgumentsVar(methodDef.pos)
+ typecheckExpect(body, bodyEnv, resultType)
+ }
+
+ def checkExportedPropertyDef(propDef: PropertyDef, classDef: ClassDef): Unit = {
+ val PropertyDef(_, getterBody, setterArg, setterBody) = propDef
+ implicit val ctx = ErrorContext(propDef)
+
+ if (!classDef.kind.isClass) {
+ reportError(s"Exported property def can only appear in a class")
+ return
+ }
+
+ val thisType = ClassType(classDef.name.name)
+
+ if (getterBody != EmptyTree) {
+ val getterBodyEnv = Env.fromSignature(thisType, Nil, AnyType)
+ typecheckExpect(getterBody, getterBodyEnv, AnyType)
+ }
+
+ if (setterBody != EmptyTree) {
+ if (setterArg.ptpe != AnyType)
+ reportError("Setter argument of exported property def has type "+
+ s"${setterArg.ptpe}, but must be Any")
+
+ val setterBodyEnv = Env.fromSignature(thisType, List(setterArg), NoType)
+ typecheckStat(setterBody, setterBodyEnv)
+ }
+ }
+
+ def checkConstructorExportDef(ctorDef: ConstructorExportDef,
+ classDef: ClassDef): Unit = {
+ val ConstructorExportDef(_, params, body) = ctorDef
+ implicit val ctx = ErrorContext(ctorDef)
+
+ if (!classDef.kind.isClass) {
+ reportError(s"Exported constructor def can only appear in a class")
+ return
+ }
+
+ for (ParamDef(name, tpe, _) <- params) {
+ if (tpe == NoType)
+ reportError(s"Parameter $name has type NoType")
+ else if (tpe != AnyType)
+ reportError(s"Parameter $name of exported constructor def has type "+
+ s"$tpe, but must be Any")
+ }
+
+ val thisType = ClassType(classDef.name.name)
+ val bodyEnv = Env.fromSignature(thisType, params, NoType)
+ .withArgumentsVar(ctorDef.pos)
+ typecheckStat(body, bodyEnv)
+ }
+
+ def checkModuleExportDef(moduleDef: ModuleExportDef,
+ classDef: ClassDef): Unit = {
+ implicit val ctx = ErrorContext(moduleDef)
+
+ if (classDef.kind != ClassKind.ModuleClass)
+ reportError(s"Exported module def can only appear in a module class")
+ }
+
+ def typecheckStat(tree: Tree, env: Env): Env = {
+ implicit val ctx = ErrorContext(tree)
+
+ tree match {
+ case VarDef(ident, vtpe, mutable, rhs) =>
+ typecheckExpect(rhs, env, vtpe)
+ env.withLocal(LocalDef(ident.name, vtpe, mutable)(tree.pos))
+
+ case Skip() =>
+ env
+
+ case Assign(select, rhs) =>
+ select match {
+ case Select(_, Ident(name, _), false) =>
+ /* TODO In theory this case would verify that we never assign to
+ * an immutable field. But we cannot do that because we *do* emit
+ * such assigns in constructors.
+ * In the future we might want to check that only these legal
+ * special cases happen, and nothing else. But it seems non-trivial
+ * to do so, so currently we trust scalac not to make us emit
+ * illegal assigns.
+ */
+ //reportError(s"Assignment to immutable field $name.")
+ case VarRef(Ident(name, _), false) =>
+ reportError(s"Assignment to immutable variable $name.")
+ case _ =>
+ }
+ val lhsTpe = typecheckExpr(select, env)
+ val expectedRhsTpe = select match {
+ case _:JSDotSelect | _:JSBracketSelect => AnyType
+ case _ => lhsTpe
+ }
+ typecheckExpect(rhs, env, expectedRhsTpe)
+ env
+
+ case StoreModule(cls, value) =>
+ if (!cls.className.endsWith("$"))
+ reportError("StoreModule of non-module class $cls")
+ typecheckExpect(value, env, ClassType(cls.className))
+ env
+
+ case Block(stats) =>
+ (env /: stats) { (prevEnv, stat) =>
+ typecheckStat(stat, prevEnv)
+ }
+ env
+
+ case Labeled(label, NoType, body) =>
+ typecheckStat(body, env.withLabeledReturnType(label.name, AnyType))
+ env
+
+ case If(cond, thenp, elsep) =>
+ typecheckExpect(cond, env, BooleanType)
+ typecheckStat(thenp, env)
+ typecheckStat(elsep, env)
+ env
+
+ case While(cond, body, label) =>
+ typecheckExpect(cond, env, BooleanType)
+ typecheckStat(body, env)
+ env
+
+ case DoWhile(body, cond, label) =>
+ typecheckStat(body, env)
+ typecheckExpect(cond, env, BooleanType)
+ env
+
+ case Try(block, errVar, handler, finalizer) =>
+ typecheckStat(block, env)
+ if (handler != EmptyTree) {
+ val handlerEnv =
+ env.withLocal(LocalDef(errVar.name, AnyType, false)(errVar.pos))
+ typecheckStat(handler, handlerEnv)
+ }
+ if (finalizer != EmptyTree) {
+ typecheckStat(finalizer, env)
+ }
+ env
+
+ case Match(selector, cases, default) =>
+ typecheckExpr(selector, env)
+ for ((alts, body) <- cases) {
+ alts.foreach(typecheckExpr(_, env))
+ typecheckStat(body, env)
+ }
+ typecheckStat(default, env)
+ env
+
+ case Debugger() =>
+ env
+
+ case JSDelete(JSDotSelect(obj, prop)) =>
+ typecheckExpr(obj, env)
+ env
+
+ case JSDelete(JSBracketSelect(obj, prop)) =>
+ typecheckExpr(obj, env)
+ typecheckExpr(prop, env)
+ env
+
+ case _ =>
+ typecheck(tree, env)
+ env
+ }
+ }
+
+ def typecheckExpect(tree: Tree, env: Env, expectedType: Type)(
+ implicit ctx: ErrorContext): Unit = {
+ val tpe = typecheckExpr(tree, env)
+ if (!isSubtype(tpe, expectedType))
+ reportError(s"$expectedType expected but $tpe found "+
+ s"for tree of type ${tree.getClass.getName}")
+ }
+
+ def typecheckExpr(tree: Tree, env: Env): Type = {
+ implicit val ctx = ErrorContext(tree)
+ if (tree.tpe == NoType)
+ reportError(s"Expression tree has type NoType")
+ typecheck(tree, env)
+ }
+
+ def typecheck(tree: Tree, env: Env): Type = {
+ implicit val ctx = ErrorContext(tree)
+
+ def checkApplyGeneric(methodName: String, methodFullName: String,
+ args: List[Tree], inTraitImpl: Boolean): Unit = {
+ val (methodParams, resultType) = inferMethodType(methodName, inTraitImpl)
+ if (args.size != methodParams.size)
+ reportError(s"Arity mismatch: ${methodParams.size} expected but "+
+ s"${args.size} found")
+ for ((actual, formal) <- args zip methodParams) {
+ typecheckExpect(actual, env, formal)
+ }
+ if (!isConstructorName(methodName) && tree.tpe != resultType)
+ reportError(s"Call to $methodFullName of type $resultType "+
+ s"typed as ${tree.tpe}")
+ }
+
+ tree match {
+ // Control flow constructs
+
+ case Block(statsAndExpr) =>
+ val stats :+ expr = statsAndExpr
+ val envAfterStats = (env /: stats) { (prevEnv, stat) =>
+ typecheckStat(stat, prevEnv)
+ }
+ typecheckExpr(expr, envAfterStats)
+
+ case Labeled(label, tpe, body) =>
+ typecheckExpect(body, env.withLabeledReturnType(label.name, tpe), tpe)
+
+ case Return(expr, label) =>
+ env.returnTypes.get(label.map(_.name)).fold[Unit] {
+ reportError(s"Cannot return to label $label.")
+ typecheckExpr(expr, env)
+ } { returnType =>
+ typecheckExpect(expr, env, returnType)
+ }
+
+ case If(cond, thenp, elsep) =>
+ val tpe = tree.tpe
+ typecheckExpect(cond, env, BooleanType)
+ typecheckExpect(thenp, env, tpe)
+ typecheckExpect(elsep, env, tpe)
+
+ case While(BooleanLiteral(true), body, label) if tree.tpe == NothingType =>
+ typecheckStat(body, env)
+
+ case Try(block, errVar, handler, finalizer) =>
+ val tpe = tree.tpe
+ typecheckExpect(block, env, tpe)
+ if (handler != EmptyTree) {
+ val handlerEnv =
+ env.withLocal(LocalDef(errVar.name, AnyType, false)(errVar.pos))
+ typecheckExpect(handler, handlerEnv, tpe)
+ }
+ if (finalizer != EmptyTree) {
+ typecheckStat(finalizer, env)
+ }
+
+ case Throw(expr) =>
+ typecheckExpr(expr, env)
+
+ case Continue(label) =>
+ /* Here we could check that it is indeed legal to break to the
+ * specified label. However, if we do anything illegal here, it will
+ * result in a SyntaxError in JavaScript anyway, so we do not really
+ * care.
+ */
+
+ case Match(selector, cases, default) =>
+ val tpe = tree.tpe
+ typecheckExpr(selector, env)
+ for ((alts, body) <- cases) {
+ alts.foreach(typecheckExpr(_, env))
+ typecheckExpect(body, env, tpe)
+ }
+ typecheckExpect(default, env, tpe)
+
+ // Scala expressions
+
+ case New(cls, ctor, args) =>
+ val clazz = lookupClass(cls)
+ if (!clazz.kind.isClass)
+ reportError(s"new $cls which is not a class")
+ checkApplyGeneric(ctor.name, s"$cls.$ctor", args,
+ inTraitImpl = false)
+
+ case LoadModule(cls) =>
+ if (!cls.className.endsWith("$"))
+ reportError("LoadModule of non-module class $cls")
+
+ case Select(qualifier, Ident(item, _), mutable) =>
+ val qualType = typecheckExpr(qualifier, env)
+ qualType match {
+ case ClassType(cls) =>
+ val clazz = lookupClass(cls)
+ if (!clazz.kind.isClass) {
+ reportError(s"Cannot select $item of non-class $cls")
+ } else {
+ clazz.lookupField(item).fold[Unit] {
+ reportError(s"Class $cls does not have a field $item")
+ } { fieldDef =>
+ if (fieldDef.tpe != tree.tpe)
+ reportError(s"Select $cls.$item of type "+
+ s"${fieldDef.tpe} typed as ${tree.tpe}")
+ if (fieldDef.mutable != mutable)
+ reportError(s"Select $cls.$item with "+
+ s"mutable=${fieldDef.mutable} marked as mutable=$mutable")
+ }
+ }
+ case NullType | NothingType =>
+ // always ok
+ case _ =>
+ reportError(s"Cannot select $item of non-class type $qualType")
+ }
+
+ case Apply(receiver, Ident(method, _), args) =>
+ val receiverType = typecheckExpr(receiver, env)
+ checkApplyGeneric(method, s"$receiverType.$method", args,
+ inTraitImpl = false)
+
+ case StaticApply(receiver, cls, Ident(method, _), args) =>
+ typecheckExpect(receiver, env, cls)
+ checkApplyGeneric(method, s"$cls.$method", args, inTraitImpl = false)
+
+ case TraitImplApply(impl, Ident(method, _), args) =>
+ val clazz = lookupClass(impl)
+ if (clazz.kind != ClassKind.TraitImpl)
+ reportError(s"Cannot trait-impl apply method of non-trait-impl $impl")
+ checkApplyGeneric(method, s"$impl.$method", args, inTraitImpl = true)
+
+ case UnaryOp(op, lhs) =>
+ import UnaryOp._
+ (op: @switch) match {
+ case `typeof` =>
+ typecheckExpr(lhs, env)
+ case IntToLong =>
+ typecheckExpect(lhs, env, IntType)
+ case LongToInt | LongToDouble =>
+ typecheckExpect(lhs, env, LongType)
+ case DoubleToInt | DoubleToFloat | DoubleToLong =>
+ typecheckExpect(lhs, env, DoubleType)
+ case Boolean_! =>
+ typecheckExpect(lhs, env, BooleanType)
+ }
+
+ case BinaryOp(op, lhs, rhs) =>
+ import BinaryOp._
+ (op: @switch) match {
+ case === | !== | String_+ =>
+ typecheckExpr(lhs, env)
+ typecheckExpr(rhs, env)
+ case `in` =>
+ typecheckExpect(lhs, env, ClassType(StringClass))
+ typecheckExpr(rhs, env)
+ case `instanceof` =>
+ typecheckExpr(lhs, env)
+ typecheckExpr(rhs, env)
+ case Int_+ | Int_- | Int_* | Int_/ | Int_% |
+ Int_| | Int_& | Int_^ | Int_<< | Int_>>> | Int_>> =>
+ typecheckExpect(lhs, env, IntType)
+ typecheckExpect(rhs, env, IntType)
+ case Float_+ | Float_- | Float_* | Float_/ | Float_% =>
+ typecheckExpect(lhs, env, FloatType)
+ typecheckExpect(lhs, env, FloatType)
+ case Long_+ | Long_- | Long_* | Long_/ | Long_% |
+ Long_| | Long_& | Long_^ |
+ Long_== | Long_!= | Long_< | Long_<= | Long_> | Long_>= =>
+ typecheckExpect(lhs, env, LongType)
+ typecheckExpect(rhs, env, LongType)
+ case Long_<< | Long_>>> | Long_>> =>
+ typecheckExpect(lhs, env, LongType)
+ typecheckExpect(rhs, env, IntType)
+ case Double_+ | Double_- | Double_* | Double_/ | Double_% |
+ Num_== | Num_!= | Num_< | Num_<= | Num_> | Num_>= =>
+ typecheckExpect(lhs, env, DoubleType)
+ typecheckExpect(lhs, env, DoubleType)
+ case Boolean_== | Boolean_!= | Boolean_| | Boolean_& =>
+ typecheckExpect(lhs, env, BooleanType)
+ typecheckExpect(rhs, env, BooleanType)
+ }
+
+ case NewArray(tpe, lengths) =>
+ for (length <- lengths)
+ typecheckExpect(length, env, IntType)
+
+ case ArrayValue(tpe, elems) =>
+ val elemType = arrayElemType(tpe)
+ for (elem <- elems)
+ typecheckExpect(elem, env, elemType)
+
+ case ArrayLength(array) =>
+ val arrayType = typecheckExpr(array, env)
+ if (!arrayType.isInstanceOf[ArrayType])
+ reportError(s"Array type expected but $arrayType found")
+
+ case ArraySelect(array, index) =>
+ typecheckExpect(index, env, IntType)
+ typecheckExpr(array, env) match {
+ case arrayType: ArrayType =>
+ if (tree.tpe != arrayElemType(arrayType))
+ reportError(s"Array select of array type $arrayType typed as ${tree.tpe}")
+ case arrayType =>
+ reportError(s"Array type expected but $arrayType found")
+ }
+
+ case IsInstanceOf(expr, cls) =>
+ typecheckExpr(expr, env)
+
+ case AsInstanceOf(expr, cls) =>
+ typecheckExpr(expr, env)
+
+ case Unbox(expr, _) =>
+ typecheckExpr(expr, env)
+
+ case GetClass(expr) =>
+ typecheckExpr(expr, env)
+
+ // JavaScript expressions
+
+ case JSNew(ctor, args) =>
+ typecheckExpr(ctor, env)
+ for (arg <- args)
+ typecheckExpr(arg, env)
+
+ case JSDotSelect(qualifier, item) =>
+ typecheckExpr(qualifier, env)
+
+ case JSBracketSelect(qualifier, item) =>
+ typecheckExpr(qualifier, env)
+ typecheckExpr(item, env)
+
+ case JSFunctionApply(fun, args) =>
+ typecheckExpr(fun, env)
+ for (arg <- args)
+ typecheckExpr(arg, env)
+
+ case JSDotMethodApply(receiver, method, args) =>
+ typecheckExpr(receiver, env)
+ for (arg <- args)
+ typecheckExpr(arg, env)
+
+ case JSBracketMethodApply(receiver, method, args) =>
+ typecheckExpr(receiver, env)
+ typecheckExpr(method, env)
+ for (arg <- args)
+ typecheckExpr(arg, env)
+
+ case JSUnaryOp(op, lhs) =>
+ typecheckExpr(lhs, env)
+
+ case JSBinaryOp(op, lhs, rhs) =>
+ typecheckExpr(lhs, env)
+ typecheckExpr(rhs, env)
+
+ case JSArrayConstr(items) =>
+ for (item <- items)
+ typecheckExpr(item, env)
+
+ case JSObjectConstr(fields) =>
+ for ((_, value) <- fields)
+ typecheckExpr(value, env)
+
+ case JSEnvInfo() =>
+
+ // Literals
+
+ case _: Literal =>
+
+ // Atomic expressions
+
+ case VarRef(Ident(name, _), mutable) =>
+ env.locals.get(name).fold[Unit] {
+ reportError(s"Cannot find variable $name in scope")
+ } { localDef =>
+ if (tree.tpe != localDef.tpe)
+ reportError(s"Variable $name of type ${localDef.tpe} "+
+ s"typed as ${tree.tpe}")
+ if (mutable != localDef.mutable)
+ reportError(s"Variable $name with mutable=${localDef.mutable} "+
+ s"marked as mutable=$mutable")
+ }
+
+ case This() =>
+ if (!isSubtype(env.thisTpe, tree.tpe))
+ reportError(s"this of type ${env.thisTpe} typed as ${tree.tpe}")
+
+ case Closure(captureParams, params, body, captureValues) =>
+ if (captureParams.size != captureValues.size)
+ reportError("Mismatched size for captures: "+
+ s"${captureParams.size} params vs ${captureValues.size} values")
+
+ for ((ParamDef(name, ctpe, mutable), value) <- captureParams zip captureValues) {
+ if (mutable)
+ reportError(s"Capture parameter $name cannot be mutable")
+ if (ctpe == NoType)
+ reportError(s"Parameter $name has type NoType")
+ else
+ typecheckExpect(value, env, ctpe)
+ }
+
+ for (ParamDef(name, ptpe, mutable) <- params) {
+ if (ptpe == NoType)
+ reportError(s"Parameter $name has type NoType")
+ else if (ptpe != AnyType)
+ reportError(s"Closure parameter $name has type $ptpe instead of any")
+ }
+
+ val bodyEnv = Env.fromSignature(
+ AnyType, captureParams ++ params, AnyType)
+ typecheckExpect(body, bodyEnv, AnyType)
+
+ case _ =>
+ reportError(s"Invalid expression tree")
+ }
+
+ tree.tpe
+ }
+
+ def inferMethodType(encodedName: String, inTraitImpl: Boolean)(
+ implicit ctx: ErrorContext): (List[Type], Type) = {
+ def dropPrivateMarker(params: List[String]): List[String] =
+ if (params.nonEmpty && params.head.startsWith("p")) params.tail
+ else params
+
+ if (isConstructorName(encodedName)) {
+ assert(!inTraitImpl, "Trait impl should not have a constructor")
+ val params = dropPrivateMarker(
+ encodedName.stripPrefix("init___").split("__").toList)
+ if (params == List("")) (Nil, NoType)
+ else (params.map(decodeType), NoType)
+ } else if (isReflProxyName(encodedName)) {
+ assert(!inTraitImpl, "Trait impl should not have refl proxy methods")
+ val params = dropPrivateMarker(encodedName.split("__").toList.tail)
+ (params.map(decodeType), AnyType)
+ } else {
+ val paramsAndResult0 =
+ encodedName.split("__").toList.tail
+ val paramsAndResult1 =
+ if (inTraitImpl) paramsAndResult0.tail
+ else paramsAndResult0
+ val paramsAndResult =
+ dropPrivateMarker(paramsAndResult1)
+ (paramsAndResult.init.map(decodeType), decodeType(paramsAndResult.last))
+ }
+ }
+
+ def decodeType(encodedName: String)(implicit ctx: ErrorContext): Type = {
+ if (encodedName.isEmpty) NoType
+ else if (encodedName.charAt(0) == 'A') {
+ // array type
+ val dims = encodedName.indexWhere(_ != 'A')
+ val base = encodedName.substring(dims)
+ ArrayType(base, dims)
+ } else if (encodedName.length == 1) {
+ (encodedName.charAt(0): @switch) match {
+ case 'V' => NoType
+ case 'Z' => BooleanType
+ case 'C' | 'B' | 'S' | 'I' => IntType
+ case 'J' => LongType
+ case 'F' => FloatType
+ case 'D' => DoubleType
+ case 'O' => AnyType
+ case 'T' => ClassType(StringClass) // NOT StringType
+ }
+ } else if (encodedName == "sr_Nothing$") {
+ NothingType
+ } else if (encodedName == "sr_Null$") {
+ NullType
+ } else {
+ val clazz = lookupClass(encodedName)
+ if (clazz.kind == ClassKind.RawJSType) AnyType
+ else ClassType(encodedName)
+ }
+ }
+
+ def arrayElemType(arrayType: ArrayType)(implicit ctx: ErrorContext): Type = {
+ if (arrayType.dimensions == 1) decodeType(arrayType.baseClassName)
+ else ArrayType(arrayType.baseClassName, arrayType.dimensions-1)
+ }
+
+ def reportError(msg: String)(implicit ctx: ErrorContext): Unit = {
+ logger.error(s"$ctx: $msg")
+ _errorCount += 1
+ }
+
+ def lookupClass(className: String)(implicit ctx: ErrorContext): CheckedClass = {
+ classes.getOrElseUpdate(className, {
+ reportError(s"Cannot find class $className")
+ new CheckedClass(className, ClassKind.Class,
+ Some(ObjectClass), Set(ObjectClass))
+ })
+ }
+
+ def lookupClass(classType: ClassType)(implicit ctx: ErrorContext): CheckedClass =
+ lookupClass(classType.className)
+
+ def isSubclass(lhs: String, rhs: String)(implicit ctx: ErrorContext): Boolean = {
+ lookupClass(lhs).isSubclass(lookupClass(rhs))
+ }
+
+ def isSubtype(lhs: Type, rhs: Type)(implicit ctx: ErrorContext): Boolean = {
+ Types.isSubtype(lhs, rhs)(isSubclass)
+ }
+
+ class Env(
+ /** Type of `this`. Can be NoType. */
+ val thisTpe: Type,
+ /** Local variables in scope (including through closures). */
+ val locals: Map[String, LocalDef],
+ /** Return types by label. */
+ val returnTypes: Map[Option[String], Type]
+ ) {
+ def withThis(thisTpe: Type): Env =
+ new Env(thisTpe, this.locals, this.returnTypes)
+
+ def withLocal(localDef: LocalDef): Env =
+ new Env(thisTpe, locals + (localDef.name -> localDef), returnTypes)
+
+ def withLocals(localDefs: TraversableOnce[LocalDef]): Env =
+ new Env(thisTpe, locals ++ localDefs.map(d => d.name -> d), returnTypes)
+
+ def withReturnType(returnType: Type): Env =
+ new Env(this.thisTpe, this.locals, returnTypes + (None -> returnType))
+
+ def withLabeledReturnType(label: String, returnType: Type): Env =
+ new Env(this.thisTpe, this.locals, returnTypes + (Some(label) -> returnType))
+
+ def withArgumentsVar(pos: Position): Env =
+ withLocal(LocalDef("arguments", AnyType, mutable = false)(pos))
+ }
+
+ object Env {
+ val empty: Env = new Env(NoType, Map.empty, Map.empty)
+
+ def fromSignature(thisType: Type, params: List[ParamDef],
+ resultType: Type): Env = {
+ val paramLocalDefs =
+ for (p @ ParamDef(name, tpe, mutable) <- params) yield
+ name.name -> LocalDef(name.name, tpe, mutable)(p.pos)
+ new Env(thisType, paramLocalDefs.toMap,
+ Map(None -> (if (resultType == NoType) AnyType else resultType)))
+ }
+ }
+
+ class CheckedClass(
+ val name: String,
+ val kind: ClassKind,
+ val superClassName: Option[String],
+ val ancestors: Set[String],
+ _fields: TraversableOnce[CheckedField] = Nil) {
+
+ val fields = _fields.map(f => f.name -> f).toMap
+
+ lazy val superClass = superClassName.map(classes)
+
+ def this(classDef: ClassDef) = {
+ this(classDef.name.name, classDef.kind,
+ classDef.parent.map(_.name),
+ classDef.ancestors.map(_.name).toSet,
+ CheckedClass.collectFields(classDef))
+ }
+
+ def isSubclass(that: CheckedClass): Boolean =
+ this == that || ancestors.contains(that.name)
+
+ def isAncestorOfHijackedClass: Boolean =
+ AncestorsOfHijackedClasses.contains(name)
+
+ def lookupField(name: String): Option[CheckedField] =
+ fields.get(name).orElse(superClass.flatMap(_.lookupField(name)))
+ }
+
+ object CheckedClass {
+ private def collectFields(classDef: ClassDef) = {
+ classDef.defs collect {
+ case VarDef(Ident(name, _), tpe, mutable, _) =>
+ new CheckedField(name, tpe, mutable)
+ }
+ }
+ }
+
+ class CheckedField(val name: String, val tpe: Type, val mutable: Boolean)
+}
+
+object IRChecker {
+ private final class ErrorContext(val tree: Tree) extends AnyVal {
+ override def toString(): String = {
+ val pos = tree.pos
+ s"${pos.source}(${pos.line+1}:${pos.column+1}:${tree.getClass.getSimpleName})"
+ }
+
+ def pos: Position = tree.pos
+ }
+
+ private object ErrorContext {
+ implicit def tree2errorContext(tree: Tree): ErrorContext =
+ ErrorContext(tree)
+
+ def apply(tree: Tree): ErrorContext =
+ new ErrorContext(tree)
+ }
+
+ private def isConstructorName(name: String): Boolean =
+ name.startsWith("init___")
+
+ private def isReflProxyName(name: String): Boolean =
+ name.endsWith("__") && !isConstructorName(name)
+
+ case class LocalDef(name: String, tpe: Type, mutable: Boolean)(val pos: Position)
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/IncOptimizer.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/IncOptimizer.scala
new file mode 100644
index 0000000..d115618
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/IncOptimizer.scala
@@ -0,0 +1,158 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.optimizer
+
+import scala.collection.{GenTraversableOnce, GenIterable}
+import scala.collection.mutable
+
+import scala.scalajs.tools.sem.Semantics
+
+class IncOptimizer(semantics: Semantics) extends GenIncOptimizer(semantics) {
+
+ protected object CollOps extends GenIncOptimizer.AbsCollOps {
+ type Map[K, V] = mutable.Map[K, V]
+ type ParMap[K, V] = mutable.Map[K, V]
+ type AccMap[K, V] = mutable.Map[K, mutable.ListBuffer[V]]
+ type ParIterable[V] = mutable.ListBuffer[V]
+ type Addable[V] = mutable.ListBuffer[V]
+
+ def emptyAccMap[K, V]: AccMap[K, V] = mutable.Map.empty
+ def emptyMap[K, V]: Map[K, V] = mutable.Map.empty
+ def emptyParMap[K, V]: ParMap[K, V] = mutable.Map.empty
+ def emptyParIterable[V]: ParIterable[V] = mutable.ListBuffer.empty
+
+ // Operations on ParMap
+ def put[K, V](map: ParMap[K, V], k: K, v: V): Unit = map.put(k, v)
+ def remove[K, V](map: ParMap[K, V], k: K): Option[V] = map.remove(k)
+
+ def retain[K, V](map: ParMap[K, V])(p: (K, V) => Boolean): Unit =
+ map.retain(p)
+
+ // Operations on AccMap
+ def acc[K, V](map: AccMap[K, V], k: K, v: V): Unit =
+ map.getOrElseUpdate(k, mutable.ListBuffer.empty) += v
+
+ def getAcc[K, V](map: AccMap[K, V], k: K): GenIterable[V] =
+ map.getOrElse(k, Nil)
+
+ def parFlatMapKeys[A, B](map: AccMap[A, _])(
+ f: A => GenTraversableOnce[B]): GenIterable[B] =
+ map.keys.flatMap(f).toList
+
+ // Operations on ParIterable
+ def prepAdd[V](it: ParIterable[V]): Addable[V] = it
+ def add[V](addable: Addable[V], v: V): Unit = addable += v
+ def finishAdd[V](addable: Addable[V]): ParIterable[V] = addable
+ }
+
+ private val _interfaces = mutable.Map.empty[String, InterfaceType]
+ protected def getInterface(encodedName: String): InterfaceType =
+ _interfaces.getOrElseUpdate(encodedName, new SeqInterfaceType(encodedName))
+
+ private val methodsToProcess = mutable.ListBuffer.empty[MethodImpl]
+ protected def scheduleMethod(method: MethodImpl): Unit =
+ methodsToProcess += method
+
+ protected def newMethodImpl(owner: MethodContainer,
+ encodedName: String): MethodImpl = new SeqMethodImpl(owner, encodedName)
+
+ protected def processAllTaggedMethods(): Unit = {
+ logProcessingMethods(methodsToProcess.count(!_.deleted))
+ for (method <- methodsToProcess)
+ method.process()
+ methodsToProcess.clear()
+ }
+
+ private class SeqInterfaceType(encName: String) extends InterfaceType(encName) {
+ private val ancestorsAskers = mutable.Set.empty[MethodImpl]
+ private val dynamicCallers = mutable.Map.empty[String, mutable.Set[MethodImpl]]
+ private val staticCallers = mutable.Map.empty[String, mutable.Set[MethodImpl]]
+
+ private var _ancestors: List[String] = encodedName :: Nil
+
+ private var _instantiatedSubclasses: Set[Class] = Set.empty
+
+ def instantiatedSubclasses: Iterable[Class] = _instantiatedSubclasses
+
+ def addInstantiatedSubclass(x: Class): Unit =
+ _instantiatedSubclasses += x
+
+ def removeInstantiatedSubclass(x: Class): Unit =
+ _instantiatedSubclasses -= x
+
+ def ancestors: List[String] = _ancestors
+
+ def ancestors_=(v: List[String]): Unit = {
+ if (v != _ancestors) {
+ _ancestors = v
+ ancestorsAskers.foreach(_.tag())
+ ancestorsAskers.clear()
+ }
+ }
+
+ def registerAskAncestors(asker: MethodImpl): Unit =
+ ancestorsAskers += asker
+
+ def registerDynamicCaller(methodName: String, caller: MethodImpl): Unit =
+ dynamicCallers.getOrElseUpdate(methodName, mutable.Set.empty) += caller
+
+ def registerStaticCaller(methodName: String, caller: MethodImpl): Unit =
+ staticCallers.getOrElseUpdate(methodName, mutable.Set.empty) += caller
+
+ def unregisterDependee(dependee: MethodImpl): Unit = {
+ ancestorsAskers -= dependee
+ dynamicCallers.values.foreach(_ -= dependee)
+ staticCallers.values.foreach(_ -= dependee)
+ }
+
+ def tagDynamicCallersOf(methodName: String): Unit =
+ dynamicCallers.remove(methodName).foreach(_.foreach(_.tag()))
+
+ def tagStaticCallersOf(methodName: String): Unit =
+ staticCallers.remove(methodName).foreach(_.foreach(_.tag()))
+ }
+
+ private class SeqMethodImpl(owner: MethodContainer,
+ encodedName: String) extends MethodImpl(owner, encodedName) {
+
+ private val bodyAskers = mutable.Set.empty[MethodImpl]
+
+ def registerBodyAsker(asker: MethodImpl): Unit =
+ bodyAskers += asker
+
+ def unregisterDependee(dependee: MethodImpl): Unit =
+ bodyAskers -= dependee
+
+ def tagBodyAskers(): Unit = {
+ bodyAskers.foreach(_.tag())
+ bodyAskers.clear()
+ }
+
+ private var _registeredTo: List[Unregisterable] = Nil
+ private var tagged = false
+
+ protected def registeredTo(intf: Unregisterable): Unit =
+ _registeredTo ::= intf
+
+ protected def unregisterFromEverywhere(): Unit = {
+ _registeredTo.foreach(_.unregisterDependee(this))
+ _registeredTo = Nil
+ }
+
+ protected def protectTag(): Boolean = {
+ val res = !tagged
+ tagged = true
+ res
+ }
+ protected def resetTag(): Unit = tagged = false
+
+ }
+
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/JSTreeBuilder.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/JSTreeBuilder.scala
new file mode 100644
index 0000000..3d37a56
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/JSTreeBuilder.scala
@@ -0,0 +1,16 @@
+package scala.scalajs.tools.optimizer
+
+import scala.scalajs.ir
+import scala.scalajs.tools.javascript
+
+/** An abstract builder taking IR or JSTrees */
+trait JSTreeBuilder {
+ /** Add a JavaScript tree representing a statement.
+ * The tree must be a valid JavaScript tree (typically obtained by
+ * desugaring a full-fledged IR tree).
+ */
+ def addJSTree(tree: javascript.Trees.Tree): Unit
+
+ /** Completes the builder. */
+ def complete(): Unit = ()
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/OptimizerCore.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/OptimizerCore.scala
new file mode 100644
index 0000000..364038b
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/OptimizerCore.scala
@@ -0,0 +1,3572 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.optimizer
+
+import scala.language.implicitConversions
+
+import scala.annotation.{switch, tailrec}
+
+import scala.collection.mutable
+
+import scala.util.control.{NonFatal, ControlThrowable, TailCalls}
+import scala.util.control.TailCalls.{done => _, _} // done is a too generic term
+
+import scala.scalajs.ir._
+import Definitions.{ObjectClass, isConstructorName, isReflProxyName}
+import Infos.OptimizerHints
+import Trees._
+import Types._
+
+import scala.scalajs.tools.sem.Semantics
+import scala.scalajs.tools.javascript.LongImpl
+import scala.scalajs.tools.logging._
+
+/** Optimizer core.
+ * Designed to be "mixed in" [[IncOptimizer#MethodImpl#Optimizer]].
+ * This is the core of the optimizer. It contains all the smart things the
+ * optimizer does. To perform inlining, it relies on abstract protected
+ * methods to identify the target of calls.
+ */
+private[optimizer] abstract class OptimizerCore(semantics: Semantics) {
+ import OptimizerCore._
+
+ type MethodID <: AbstractMethodID
+
+ val myself: MethodID
+
+ /** Returns the body of a method. */
+ protected def getMethodBody(method: MethodID): MethodDef
+
+ /** Returns the list of possible targets for a dynamically linked call. */
+ protected def dynamicCall(intfName: String,
+ methodName: String): List[MethodID]
+
+ /** Returns the target of a static call. */
+ protected def staticCall(className: String,
+ methodName: String): Option[MethodID]
+
+ /** Returns the target of a trait impl call. */
+ protected def traitImplCall(traitImplName: String,
+ methodName: String): Option[MethodID]
+
+ /** Returns the list of ancestors of a class or interface. */
+ protected def getAncestorsOf(encodedName: String): List[String]
+
+ /** Tests whether the given module class has an elidable accessor.
+ * In other words, whether it is safe to discard a LoadModule of that
+ * module class which is not used.
+ */
+ protected def hasElidableModuleAccessor(moduleClassName: String): Boolean
+
+ /** Tests whether the given class is inlineable.
+ * @return None if the class is not inlineable, Some(value) if it is, where
+ * value is a RecordValue with the initial value of its fields.
+ */
+ protected def tryNewInlineableClass(className: String): Option[RecordValue]
+
+ private val usedLocalNames = mutable.Set.empty[String]
+ private val usedLabelNames = mutable.Set.empty[String]
+ private var statesInUse: List[State[_]] = Nil
+
+ private var disableOptimisticOptimizations: Boolean = false
+ private var rollbacksCount: Int = 0
+
+ private val attemptedInlining = mutable.ListBuffer.empty[MethodID]
+
+ private var curTrampolineId = 0
+
+ def optimize(thisType: Type, originalDef: MethodDef): (MethodDef, Infos.MethodInfo) = {
+ try {
+ val MethodDef(name, params, resultType, body) = originalDef
+ val (newParams, newBody) = try {
+ transformIsolatedBody(Some(myself), thisType, params, resultType, body)
+ } catch {
+ case _: TooManyRollbacksException =>
+ usedLocalNames.clear()
+ usedLabelNames.clear()
+ statesInUse = Nil
+ disableOptimisticOptimizations = true
+ transformIsolatedBody(Some(myself), thisType, params, resultType, body)
+ }
+ val m = MethodDef(name, newParams, resultType, newBody)(None)(originalDef.pos)
+ val info = recreateInfo(m)
+ (m, info)
+ } catch {
+ case NonFatal(cause) =>
+ throw new OptimizeException(myself, 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))
+ throw e
+ }
+ }
+
+ private def withState[A, B](state: State[A])(body: => B): B = {
+ statesInUse ::= state
+ try body
+ finally statesInUse = statesInUse.tail
+ }
+
+ private def freshLocalName(base: String): String =
+ freshNameGeneric(usedLocalNames, base)
+
+ private def freshLabelName(base: String): String =
+ freshNameGeneric(usedLabelNames, base)
+
+ private val isReserved = isKeyword ++ Seq("arguments", "eval", "ScalaJS")
+
+ private def freshNameGeneric(usedNames: mutable.Set[String], base: String): String = {
+ val result = if (!usedNames.contains(base) && !isReserved(base)) {
+ base
+ } else {
+ var i = 1
+ while (usedNames.contains(base + "$" + i))
+ i += 1
+ base + "$" + i
+ }
+ usedNames += result
+ result
+ }
+
+ private def tryOrRollback(body: CancelFun => TailRec[Tree])(
+ fallbackFun: () => TailRec[Tree]): TailRec[Tree] = {
+ if (disableOptimisticOptimizations) {
+ fallbackFun()
+ } else {
+ val trampolineId = curTrampolineId
+ val savedUsedLocalNames = usedLocalNames.toSet
+ val savedUsedLabelNames = usedLabelNames.toSet
+ val savedStates = statesInUse.map(_.makeBackup())
+
+ body { () =>
+ throw new RollbackException(trampolineId, savedUsedLocalNames,
+ savedUsedLabelNames, savedStates, fallbackFun)
+ }
+ }
+ }
+
+ private def isSubclass(lhs: String, rhs: String): Boolean =
+ getAncestorsOf(lhs).contains(rhs)
+
+ private val isSubclassFun = isSubclass _
+ private def isSubtype(lhs: Type, rhs: Type): Boolean =
+ Types.isSubtype(lhs, rhs)(isSubclassFun)
+
+ /** Transforms a statement.
+ *
+ * For valid expression trees, it is always the case that
+ * {{{
+ * transformStat(tree)
+ * ===
+ * pretransformExpr(tree)(finishTransformStat)
+ * }}}
+ */
+ private def transformStat(tree: Tree)(implicit scope: Scope): Tree =
+ transform(tree, isStat = true)
+
+ /** Transforms an expression.
+ *
+ * It is always the case that
+ * {{{
+ * transformExpr(tree)
+ * ===
+ * pretransformExpr(tree)(finishTransformExpr)
+ * }}}
+ */
+ private def transformExpr(tree: Tree)(implicit scope: Scope): Tree =
+ transform(tree, isStat = false)
+
+ /** Transforms a tree. */
+ private def transform(tree: Tree, isStat: Boolean)(
+ implicit scope: Scope): Tree = {
+
+ @inline implicit def pos = tree.pos
+ val result = tree match {
+ // Definitions
+
+ case VarDef(_, _, _, rhs) =>
+ /* A local var that is last (or alone) in its block is not terribly
+ * useful. Get rid of it.
+ * (Non-last VarDefs in blocks are handled in transformBlock.)
+ */
+ transformStat(rhs)
+
+ // Control flow constructs
+
+ case tree: Block =>
+ transformBlock(tree, isStat)
+
+ case Labeled(ident @ Ident(label, _), tpe, body) =>
+ trampoline {
+ returnable(label, if (isStat) NoType else tpe, body, isStat,
+ usePreTransform = false)(finishTransform(isStat))
+ }
+
+ case Assign(lhs, rhs) =>
+ val cont = { (preTransLhs: PreTransform) =>
+ resolveLocalDef(preTransLhs) match {
+ case PreTransRecordTree(lhsTree, lhsOrigType, lhsCancelFun) =>
+ val recordType = lhsTree.tpe.asInstanceOf[RecordType]
+ pretransformNoLocalDef(rhs) {
+ case PreTransRecordTree(rhsTree, rhsOrigType, rhsCancelFun) =>
+ if (rhsTree.tpe != recordType || rhsOrigType != lhsOrigType)
+ lhsCancelFun()
+ TailCalls.done(Assign(lhsTree, rhsTree))
+ case _ =>
+ lhsCancelFun()
+ }
+ case PreTransTree(lhsTree, _) =>
+ TailCalls.done(Assign(lhsTree, transformExpr(rhs)))
+ }
+ }
+ trampoline {
+ lhs match {
+ case lhs: Select =>
+ pretransformSelectCommon(lhs, isLhsOfAssign = true)(cont)
+ case _ =>
+ pretransformExpr(lhs)(cont)
+ }
+ }
+
+ case Return(expr, optLabel) =>
+ val optInfo = optLabel match {
+ case Some(Ident(label, _)) =>
+ Some(scope.env.labelInfos(label))
+ case None =>
+ scope.env.labelInfos.get("")
+ }
+ optInfo.fold[Tree] {
+ Return(transformExpr(expr), None)
+ } { info =>
+ val newOptLabel = Some(Ident(info.newName, None))
+ if (!info.acceptRecords) {
+ val newExpr = transformExpr(expr)
+ info.returnedTypes.value ::= (newExpr.tpe, RefinedType(newExpr.tpe))
+ Return(newExpr, newOptLabel)
+ } else trampoline {
+ pretransformNoLocalDef(expr) { texpr =>
+ texpr match {
+ case PreTransRecordTree(newExpr, origType, cancelFun) =>
+ info.returnedTypes.value ::= (newExpr.tpe, origType)
+ TailCalls.done(Return(newExpr, newOptLabel))
+ case PreTransTree(newExpr, tpe) =>
+ info.returnedTypes.value ::= (newExpr.tpe, tpe)
+ TailCalls.done(Return(newExpr, newOptLabel))
+ }
+ }
+ }
+ }
+
+ case If(cond, thenp, elsep) =>
+ val newCond = transformExpr(cond)
+ newCond match {
+ case BooleanLiteral(condValue) =>
+ if (condValue) transform(thenp, isStat)
+ else transform(elsep, isStat)
+ case _ =>
+ val newThenp = transform(thenp, isStat)
+ val newElsep = transform(elsep, isStat)
+ val refinedType =
+ constrainedLub(newThenp.tpe, newElsep.tpe, tree.tpe)
+ foldIf(newCond, newThenp, newElsep)(refinedType)
+ }
+
+ case While(cond, body, optLabel) =>
+ val newCond = transformExpr(cond)
+ newCond match {
+ case BooleanLiteral(false) => Skip()
+ case _ =>
+ optLabel match {
+ case None =>
+ While(newCond, transformStat(body), None)
+
+ case Some(labelIdent @ Ident(label, _)) =>
+ val newLabel = freshLabelName(label)
+ val info = new LabelInfo(newLabel, acceptRecords = false)
+ While(newCond, {
+ val bodyScope = scope.withEnv(
+ scope.env.withLabelInfo(label, info))
+ transformStat(body)(bodyScope)
+ }, Some(Ident(newLabel, None)(labelIdent.pos)))
+ }
+ }
+
+ case DoWhile(body, cond, None) =>
+ val newBody = transformStat(body)
+ val newCond = transformExpr(cond)
+ newCond match {
+ case BooleanLiteral(false) => newBody
+ case _ => DoWhile(newBody, newCond, None)
+ }
+
+ case Try(block, errVar, EmptyTree, finalizer) =>
+ val newBlock = transform(block, isStat)
+ val newFinalizer = transformStat(finalizer)
+ Try(newBlock, errVar, EmptyTree, newFinalizer)(newBlock.tpe)
+
+ case Try(block, errVar @ Ident(name, originalName), handler, finalizer) =>
+ val newBlock = transform(block, isStat)
+
+ val newName = freshLocalName(name)
+ val newOriginalName = originalName.orElse(Some(name))
+ val localDef = LocalDef(RefinedType(AnyType), true,
+ ReplaceWithVarRef(newName, newOriginalName, new SimpleState(true), None))
+ val newHandler = {
+ val handlerScope = scope.withEnv(scope.env.withLocalDef(name, localDef))
+ transform(handler, isStat)(handlerScope)
+ }
+
+ val newFinalizer = transformStat(finalizer)
+
+ val refinedType = constrainedLub(newBlock.tpe, newHandler.tpe, tree.tpe)
+ Try(newBlock, Ident(newName, newOriginalName)(errVar.pos),
+ newHandler, newFinalizer)(refinedType)
+
+ case Throw(expr) =>
+ Throw(transformExpr(expr))
+
+ case Continue(optLabel) =>
+ val newOptLabel = optLabel map { label =>
+ Ident(scope.env.labelInfos(label.name).newName, None)(label.pos)
+ }
+ Continue(newOptLabel)
+
+ case Match(selector, cases, default) =>
+ val newSelector = transformExpr(selector)
+ newSelector match {
+ case newSelector: Literal =>
+ val body = cases collectFirst {
+ case (alts, body) if alts.exists(literal_===(_, newSelector)) => body
+ } getOrElse default
+ transform(body, isStat)
+ case _ =>
+ Match(newSelector,
+ cases map (c => (c._1, transform(c._2, isStat))),
+ transform(default, isStat))(tree.tpe)
+ }
+
+ // Scala expressions
+
+ case New(cls, ctor, args) =>
+ New(cls, ctor, args map transformExpr)
+
+ case StoreModule(cls, value) =>
+ StoreModule(cls, transformExpr(value))
+
+ case tree: Select =>
+ trampoline {
+ pretransformSelectCommon(tree, isLhsOfAssign = false)(
+ finishTransform(isStat = false))
+ }
+
+ case tree: Apply =>
+ trampoline {
+ pretransformApply(tree, isStat, usePreTransform = false)(
+ finishTransform(isStat))
+ }
+
+ case tree: StaticApply =>
+ trampoline {
+ pretransformStaticApply(tree, isStat, usePreTransform = false)(
+ finishTransform(isStat))
+ }
+
+ case tree: TraitImplApply =>
+ trampoline {
+ pretransformTraitImplApply(tree, isStat, usePreTransform = false)(
+ finishTransform(isStat))
+ }
+
+ case tree @ UnaryOp(_, arg) =>
+ if (isStat) transformStat(arg)
+ else transformUnaryOp(tree)
+
+ case tree @ BinaryOp(op, lhs, rhs) =>
+ if (isStat) Block(transformStat(lhs), transformStat(rhs))
+ else transformBinaryOp(tree)
+
+ case NewArray(tpe, lengths) =>
+ NewArray(tpe, lengths map transformExpr)
+
+ case ArrayValue(tpe, elems) =>
+ ArrayValue(tpe, elems map transformExpr)
+
+ case ArrayLength(array) =>
+ ArrayLength(transformExpr(array))
+
+ case ArraySelect(array, index) =>
+ ArraySelect(transformExpr(array), transformExpr(index))(tree.tpe)
+
+ case RecordValue(tpe, elems) =>
+ RecordValue(tpe, elems map transformExpr)
+
+ case IsInstanceOf(expr, ClassType(ObjectClass)) =>
+ transformExpr(BinaryOp(BinaryOp.!==, expr, Null()))
+
+ case IsInstanceOf(expr, tpe) =>
+ trampoline {
+ pretransformExpr(expr) { texpr =>
+ val result = {
+ if (isSubtype(texpr.tpe.base, tpe)) {
+ if (texpr.tpe.isNullable)
+ BinaryOp(BinaryOp.!==, finishTransformExpr(texpr), Null())
+ else
+ Block(finishTransformStat(texpr), BooleanLiteral(true))
+ } else {
+ if (texpr.tpe.isExact)
+ Block(finishTransformStat(texpr), BooleanLiteral(false))
+ else
+ IsInstanceOf(finishTransformExpr(texpr), tpe)
+ }
+ }
+ TailCalls.done(result)
+ }
+ }
+
+ case AsInstanceOf(expr, ClassType(ObjectClass)) =>
+ transformExpr(expr)
+
+ case AsInstanceOf(expr, cls) =>
+ trampoline {
+ pretransformExpr(tree)(finishTransform(isStat))
+ }
+
+ case Unbox(arg, charCode) =>
+ trampoline {
+ pretransformExpr(arg) { targ =>
+ foldUnbox(targ, charCode)(finishTransform(isStat))
+ }
+ }
+
+ case GetClass(expr) =>
+ GetClass(transformExpr(expr))
+
+ // JavaScript expressions
+
+ case JSNew(ctor, args) =>
+ JSNew(transformExpr(ctor), args map transformExpr)
+
+ case JSDotSelect(qualifier, item) =>
+ JSDotSelect(transformExpr(qualifier), item)
+
+ case JSBracketSelect(qualifier, item) =>
+ JSBracketSelect(transformExpr(qualifier), transformExpr(item))
+
+ case tree: JSFunctionApply =>
+ trampoline {
+ pretransformJSFunctionApply(tree, isStat, usePreTransform = false)(
+ finishTransform(isStat))
+ }
+
+ case JSDotMethodApply(receiver, method, args) =>
+ JSDotMethodApply(transformExpr(receiver), method,
+ args map transformExpr)
+
+ case JSBracketMethodApply(receiver, method, args) =>
+ JSBracketMethodApply(transformExpr(receiver), transformExpr(method),
+ args map transformExpr)
+
+ case JSDelete(JSDotSelect(obj, prop)) =>
+ JSDelete(JSDotSelect(transformExpr(obj), prop))
+
+ case JSDelete(JSBracketSelect(obj, prop)) =>
+ JSDelete(JSBracketSelect(transformExpr(obj), transformExpr(prop)))
+
+ case JSUnaryOp(op, lhs) =>
+ JSUnaryOp(op, transformExpr(lhs))
+
+ case JSBinaryOp(op, lhs, rhs) =>
+ JSBinaryOp(op, transformExpr(lhs), transformExpr(rhs))
+
+ case JSArrayConstr(items) =>
+ JSArrayConstr(items map transformExpr)
+
+ case JSObjectConstr(fields) =>
+ JSObjectConstr(fields map {
+ case (name, value) => (name, transformExpr(value))
+ })
+
+ // Atomic expressions
+
+ case _:VarRef | _:This =>
+ trampoline {
+ pretransformExpr(tree)(finishTransform(isStat))
+ }
+
+ case Closure(captureParams, params, body, captureValues) =>
+ transformClosureCommon(captureParams, params, body,
+ captureValues.map(transformExpr))
+
+ // Trees that need not be transformed
+
+ case _:Skip | _:Debugger | _:LoadModule |
+ _:JSEnvInfo | _:Literal | EmptyTree =>
+ tree
+ }
+
+ if (isStat) keepOnlySideEffects(result)
+ else result
+ }
+
+ private def transformClosureCommon(captureParams: List[ParamDef],
+ params: List[ParamDef], body: Tree, newCaptureValues: List[Tree])(
+ implicit pos: Position): Closure = {
+
+ val (allNewParams, newBody) =
+ transformIsolatedBody(None, AnyType, captureParams ++ params, AnyType, body)
+ val (newCaptureParams, newParams) =
+ allNewParams.splitAt(captureParams.size)
+
+ Closure(newCaptureParams, newParams, newBody, newCaptureValues)
+ }
+
+ private def transformBlock(tree: Block, isStat: Boolean)(
+ implicit scope: Scope): Tree = {
+ def transformList(stats: List[Tree])(
+ implicit scope: Scope): Tree = stats match {
+ case last :: Nil =>
+ transform(last, isStat)
+
+ case (VarDef(Ident(name, originalName), vtpe, mutable, rhs)) :: rest =>
+ trampoline {
+ pretransformExpr(rhs) { trhs =>
+ withBinding(Binding(name, originalName, vtpe, mutable, trhs)) {
+ (restScope, cont1) =>
+ val newRest = transformList(rest)(restScope)
+ cont1(PreTransTree(newRest, RefinedType(newRest.tpe)))
+ } (finishTransform(isStat))
+ }
+ }
+
+ case stat :: rest =>
+ val transformedStat = transformStat(stat)
+ if (transformedStat.tpe == NothingType) transformedStat
+ else Block(transformedStat, transformList(rest))(stat.pos)
+
+ case Nil => // silence the exhaustivity warning in a sensible way
+ Skip()(tree.pos)
+ }
+ transformList(tree.stats)(scope)
+ }
+
+ /** Pretransforms a list of trees as a list of [[PreTransform]]s.
+ * This is a convenience method to use pretransformExpr on a list.
+ */
+ private def pretransformExprs(trees: List[Tree])(
+ cont: List[PreTransform] => TailRec[Tree])(
+ implicit scope: Scope): TailRec[Tree] = {
+ trees match {
+ case first :: rest =>
+ pretransformExpr(first) { tfirst =>
+ pretransformExprs(rest) { trest =>
+ cont(tfirst :: trest)
+ }
+ }
+
+ case Nil =>
+ cont(Nil)
+ }
+ }
+
+ /** Pretransforms two trees as a pair of [[PreTransform]]s.
+ * This is a convenience method to use pretransformExpr on two trees.
+ */
+ private def pretransformExprs(tree1: Tree, tree2: Tree)(
+ cont: (PreTransform, PreTransform) => TailRec[Tree])(
+ implicit scope: Scope): TailRec[Tree] = {
+ pretransformExpr(tree1) { ttree1 =>
+ pretransformExpr(tree2) { ttree2 =>
+ cont(ttree1, ttree2)
+ }
+ }
+ }
+
+ /** Pretransforms a tree and a list of trees as [[PreTransform]]s.
+ * This is a convenience method to use pretransformExpr.
+ */
+ private def pretransformExprs(first: Tree, rest: List[Tree])(
+ cont: (PreTransform, List[PreTransform]) => TailRec[Tree])(
+ implicit scope: Scope): TailRec[Tree] = {
+ pretransformExpr(first) { tfirst =>
+ pretransformExprs(rest) { trest =>
+ cont(tfirst, trest)
+ }
+ }
+ }
+
+ /** Pretransforms a tree to get a refined type while avoiding to force
+ * things we might be able to optimize by folding and aliasing.
+ */
+ private def pretransformExpr(tree: Tree)(cont: PreTransCont)(
+ implicit scope: Scope): TailRec[Tree] = tailcall {
+ @inline implicit def pos = tree.pos
+
+ tree match {
+ case tree: Block =>
+ pretransformBlock(tree)(cont)
+
+ case VarRef(Ident(name, _), _) =>
+ val localDef = scope.env.localDefs.getOrElse(name,
+ sys.error(s"Cannot find local def '$name' at $pos\n" +
+ s"While optimizing $myself\n" +
+ s"Env is ${scope.env}\nInlining ${scope.implsBeingInlined}"))
+ cont(PreTransLocalDef(localDef))
+
+ case This() =>
+ val localDef = scope.env.localDefs.getOrElse("this",
+ sys.error(s"Found invalid 'this' at $pos\n" +
+ s"While optimizing $myself\n" +
+ s"Env is ${scope.env}\nInlining ${scope.implsBeingInlined}"))
+ cont(PreTransLocalDef(localDef))
+
+ case If(cond, thenp, elsep) =>
+ val newCond = transformExpr(cond)
+ newCond match {
+ case BooleanLiteral(condValue) =>
+ if (condValue) pretransformExpr(thenp)(cont)
+ else pretransformExpr(elsep)(cont)
+ case _ =>
+ tryOrRollback { cancelFun =>
+ pretransformNoLocalDef(thenp) { tthenp =>
+ pretransformNoLocalDef(elsep) { telsep =>
+ (tthenp, telsep) match {
+ case (PreTransRecordTree(thenTree, thenOrigType, thenCancelFun),
+ PreTransRecordTree(elseTree, elseOrigType, elseCancelFun)) =>
+ val commonType =
+ if (thenTree.tpe == elseTree.tpe &&
+ thenOrigType == elseOrigType) thenTree.tpe
+ else cancelFun()
+ val refinedOrigType =
+ constrainedLub(thenOrigType, elseOrigType, tree.tpe)
+ cont(PreTransRecordTree(
+ If(newCond, thenTree, elseTree)(commonType),
+ refinedOrigType,
+ cancelFun))
+
+ case (PreTransRecordTree(thenTree, thenOrigType, thenCancelFun), _)
+ if telsep.tpe.isNothingType =>
+ cont(PreTransRecordTree(
+ If(newCond, thenTree, finishTransformExpr(telsep))(thenTree.tpe),
+ thenOrigType,
+ thenCancelFun))
+
+ case (_, PreTransRecordTree(elseTree, elseOrigType, elseCancelFun))
+ if tthenp.tpe.isNothingType =>
+ cont(PreTransRecordTree(
+ If(newCond, finishTransformExpr(tthenp), elseTree)(elseTree.tpe),
+ elseOrigType,
+ elseCancelFun))
+
+ case _ =>
+ val newThenp = finishTransformExpr(tthenp)
+ val newElsep = finishTransformExpr(telsep)
+ val refinedType =
+ constrainedLub(newThenp.tpe, newElsep.tpe, tree.tpe)
+ cont(PreTransTree(
+ foldIf(newCond, newThenp, newElsep)(refinedType)))
+ }
+ }
+ }
+ } { () =>
+ val newThenp = transformExpr(thenp)
+ val newElsep = transformExpr(elsep)
+ val refinedType =
+ constrainedLub(newThenp.tpe, newElsep.tpe, tree.tpe)
+ cont(PreTransTree(
+ foldIf(newCond, newThenp, newElsep)(refinedType)))
+ }
+ }
+
+ case Match(selector, cases, default) =>
+ val newSelector = transformExpr(selector)
+ newSelector match {
+ case newSelector: Literal =>
+ val body = cases collectFirst {
+ case (alts, body) if alts.exists(literal_===(_, newSelector)) => body
+ } getOrElse default
+ pretransformExpr(body)(cont)
+ case _ =>
+ cont(PreTransTree(Match(newSelector,
+ cases map (c => (c._1, transformExpr(c._2))),
+ transformExpr(default))(tree.tpe)))
+ }
+
+ case Labeled(ident @ Ident(label, _), tpe, body) =>
+ returnable(label, tpe, body, isStat = false, usePreTransform = true)(cont)
+
+ case New(cls @ ClassType(className), ctor, args) =>
+ tryNewInlineableClass(className) match {
+ case Some(initialValue) =>
+ pretransformExprs(args) { targs =>
+ tryOrRollback { cancelFun =>
+ inlineClassConstructor(
+ new AllocationSite(tree),
+ cls, initialValue, ctor, targs, cancelFun)(cont)
+ } { () =>
+ cont(PreTransTree(
+ New(cls, ctor, targs.map(finishTransformExpr)),
+ RefinedType(cls, isExact = true, isNullable = false)))
+ }
+ }
+ case None =>
+ cont(PreTransTree(
+ New(cls, ctor, args.map(transformExpr)),
+ RefinedType(cls, isExact = true, isNullable = false)))
+ }
+
+ case tree: Select =>
+ pretransformSelectCommon(tree, isLhsOfAssign = false)(cont)
+
+ case tree: Apply =>
+ pretransformApply(tree, isStat = false,
+ usePreTransform = true)(cont)
+
+ case tree: StaticApply =>
+ pretransformStaticApply(tree, isStat = false,
+ usePreTransform = true)(cont)
+
+ case tree: TraitImplApply =>
+ pretransformTraitImplApply(tree, isStat = false,
+ usePreTransform = true)(cont)
+
+ case tree: JSFunctionApply =>
+ pretransformJSFunctionApply(tree, isStat = false,
+ usePreTransform = true)(cont)
+
+ case AsInstanceOf(expr, tpe) =>
+ pretransformExpr(expr) { texpr =>
+ tpe match {
+ case ClassType(ObjectClass) =>
+ cont(texpr)
+ case _ =>
+ if (isSubtype(texpr.tpe.base, tpe)) {
+ cont(texpr)
+ } else {
+ cont(PreTransTree(
+ AsInstanceOf(finishTransformExpr(texpr), tpe)))
+ }
+ }
+ }
+
+ case Closure(captureParams, params, body, captureValues) =>
+ pretransformExprs(captureValues) { tcaptureValues =>
+ tryOrRollback { cancelFun =>
+ val captureBindings = for {
+ (ParamDef(Ident(name, origName), tpe, mutable), value) <-
+ captureParams zip tcaptureValues
+ } yield {
+ Binding(name, origName, tpe, mutable, value)
+ }
+ withNewLocalDefs(captureBindings) { (captureLocalDefs, cont1) =>
+ val alreadyUsedState = new SimpleState[Boolean](false)
+ withState(alreadyUsedState) {
+ val replacement = TentativeClosureReplacement(
+ captureParams, params, body, captureLocalDefs,
+ alreadyUsedState, cancelFun)
+ val localDef = LocalDef(
+ RefinedType(AnyType, isExact = false, isNullable = false),
+ mutable = false,
+ replacement)
+ cont1(PreTransLocalDef(localDef))
+ }
+ } (cont)
+ } { () =>
+ val newClosure = transformClosureCommon(captureParams, params, body,
+ tcaptureValues.map(finishTransformExpr))
+ cont(PreTransTree(
+ newClosure,
+ RefinedType(AnyType, isExact = false, isNullable = false)))
+ }
+ }
+
+ case _ =>
+ val result = transformExpr(tree)
+ cont(PreTransTree(result, RefinedType(result.tpe)))
+ }
+ }
+
+ private def pretransformBlock(tree: Block)(
+ cont: PreTransCont)(
+ implicit scope: Scope): TailRec[Tree] = {
+ def pretransformList(stats: List[Tree])(
+ cont: PreTransCont)(
+ implicit scope: Scope): TailRec[Tree] = stats match {
+ case last :: Nil =>
+ pretransformExpr(last)(cont)
+
+ case (VarDef(Ident(name, originalName), vtpe, mutable, rhs)) :: rest =>
+ pretransformExpr(rhs) { trhs =>
+ withBinding(Binding(name, originalName, vtpe, mutable, trhs)) {
+ (restScope, cont1) =>
+ pretransformList(rest)(cont1)(restScope)
+ } (cont)
+ }
+
+ case stat :: rest =>
+ implicit val pos = tree.pos
+ val transformedStat = transformStat(stat)
+ transformedStat match {
+ case Skip() =>
+ pretransformList(rest)(cont)
+ case _ =>
+ if (transformedStat.tpe == NothingType)
+ cont(PreTransTree(transformedStat, RefinedType.Nothing))
+ else {
+ pretransformList(rest) { trest =>
+ cont(PreTransBlock(transformedStat :: Nil, trest))
+ }
+ }
+ }
+
+ case Nil => // silence the exhaustivity warning in a sensible way
+ TailCalls.done(Skip()(tree.pos))
+ }
+ pretransformList(tree.stats)(cont)(scope)
+ }
+
+ private def pretransformSelectCommon(tree: Select, isLhsOfAssign: Boolean)(
+ cont: PreTransCont)(
+ implicit scope: Scope): TailRec[Tree] = {
+ val Select(qualifier, item, mutable) = tree
+ pretransformExpr(qualifier) { preTransQual =>
+ pretransformSelectCommon(tree.tpe, preTransQual, item, mutable,
+ isLhsOfAssign)(cont)(scope, tree.pos)
+ }
+ }
+
+ private def pretransformSelectCommon(expectedType: Type,
+ preTransQual: PreTransform, item: Ident, mutable: Boolean,
+ isLhsOfAssign: Boolean)(
+ cont: PreTransCont)(
+ implicit scope: Scope, pos: Position): TailRec[Tree] = {
+ preTransQual match {
+ case PreTransLocalDef(LocalDef(_, _,
+ InlineClassBeingConstructedReplacement(fieldLocalDefs, cancelFun))) =>
+ val fieldLocalDef = fieldLocalDefs(item.name)
+ if (!isLhsOfAssign || fieldLocalDef.mutable) {
+ cont(PreTransLocalDef(fieldLocalDef))
+ } else {
+ /* This is an assignment to an immutable field of a inlineable class
+ * being constructed, but that does not appear at the "top-level" of
+ * one of its constructors. We cannot handle those, so we cancel.
+ * (Assignments at the top-level are normal initializations of these
+ * fields, and are transformed as vals in inlineClassConstructor.)
+ */
+ cancelFun()
+ }
+ case PreTransLocalDef(LocalDef(_, _,
+ InlineClassInstanceReplacement(_, fieldLocalDefs, cancelFun))) =>
+ val fieldLocalDef = fieldLocalDefs(item.name)
+ if (!isLhsOfAssign || fieldLocalDef.mutable) {
+ cont(PreTransLocalDef(fieldLocalDef))
+ } else {
+ /* In an ideal world, this should not happen (assigning to an
+ * immutable field of an already constructed object). However, since
+ * we cannot IR-check that this does not happen (see #1021), this is
+ * effectively allowed by the IR spec. We are therefore not allowed
+ * to crash. We cancel instead. This will become an actual field
+ * (rather than an optimized local val) which is not considered pure
+ * (for that same reason).
+ */
+ cancelFun()
+ }
+ case _ =>
+ resolveLocalDef(preTransQual) match {
+ case PreTransRecordTree(newQual, origType, cancelFun) =>
+ val recordType = newQual.tpe.asInstanceOf[RecordType]
+ val field = recordType.findField(item.name)
+ val sel = Select(newQual, item, mutable)(field.tpe)
+ sel.tpe match {
+ case _: RecordType =>
+ cont(PreTransRecordTree(sel, RefinedType(expectedType), cancelFun))
+ case _ =>
+ cont(PreTransTree(sel, RefinedType(sel.tpe)))
+ }
+
+ case PreTransTree(newQual, _) =>
+ cont(PreTransTree(Select(newQual, item, mutable)(expectedType),
+ RefinedType(expectedType)))
+ }
+ }
+ }
+
+ /** Resolves any LocalDef in a [[PreTransform]]. */
+ private def resolveLocalDef(preTrans: PreTransform): PreTransGenTree = {
+ implicit val pos = preTrans.pos
+ preTrans match {
+ case PreTransBlock(stats, result) =>
+ resolveLocalDef(result) match {
+ case PreTransRecordTree(tree, tpe, cancelFun) =>
+ PreTransRecordTree(Block(stats :+ tree), tpe, cancelFun)
+ case PreTransTree(tree, tpe) =>
+ PreTransTree(Block(stats :+ tree), tpe)
+ }
+
+ case PreTransLocalDef(localDef @ LocalDef(tpe, mutable, replacement)) =>
+ replacement match {
+ case ReplaceWithRecordVarRef(name, originalName,
+ recordType, used, cancelFun) =>
+ used.value = true
+ PreTransRecordTree(
+ VarRef(Ident(name, originalName), mutable)(recordType),
+ tpe, cancelFun)
+
+ case InlineClassInstanceReplacement(recordType, fieldLocalDefs, cancelFun) =>
+ if (!isImmutableType(recordType))
+ cancelFun()
+ PreTransRecordTree(
+ RecordValue(recordType, recordType.fields.map(
+ f => fieldLocalDefs(f.name).newReplacement)),
+ tpe, cancelFun)
+
+ case _ =>
+ PreTransTree(localDef.newReplacement, localDef.tpe)
+ }
+
+ case preTrans: PreTransGenTree =>
+ preTrans
+ }
+ }
+
+ /** Combines pretransformExpr and resolveLocalDef in one convenience method. */
+ private def pretransformNoLocalDef(tree: Tree)(
+ cont: PreTransGenTree => TailRec[Tree])(
+ implicit scope: Scope): TailRec[Tree] = {
+ pretransformExpr(tree) { ttree =>
+ cont(resolveLocalDef(ttree))
+ }
+ }
+
+ /** Finishes a pretransform, either a statement or an expression. */
+ private def finishTransform(isStat: Boolean): PreTransCont = { preTrans =>
+ TailCalls.done {
+ if (isStat) finishTransformStat(preTrans)
+ else finishTransformExpr(preTrans)
+ }
+ }
+
+ /** 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.
+ * By "per translation", we mean in an alternative path through
+ * `tryOrRollback`. It could still be called several times as long as
+ * it is once in the 'try' part and once in the 'fallback' part.
+ */
+ private def finishTransformExpr(preTrans: PreTransform): Tree = {
+ implicit val pos = preTrans.pos
+ preTrans match {
+ case PreTransBlock(stats, result) =>
+ Block(stats :+ finishTransformExpr(result))
+ case PreTransLocalDef(localDef) =>
+ localDef.newReplacement
+ case PreTransRecordTree(_, _, cancelFun) =>
+ cancelFun()
+ case PreTransTree(tree, _) =>
+ tree
+ }
+ }
+
+ /** Finishes a statement pretransform to get a normal [[Tree]].
+ * This method (together with finishTransformExpr) must not be called more
+ * than once per pretransform and per translation.
+ * By "per translation", we mean in an alternative path through
+ * `tryOrRollback`. It could still be called several times as long as
+ * it is once in the 'try' part and once in the 'fallback' part.
+ */
+ private def finishTransformStat(stat: PreTransform): Tree = stat match {
+ case PreTransBlock(stats, result) =>
+ Block(stats :+ finishTransformStat(result))(stat.pos)
+ case PreTransLocalDef(_) =>
+ Skip()(stat.pos)
+ case PreTransRecordTree(tree, _, _) =>
+ keepOnlySideEffects(tree)
+ case PreTransTree(tree, _) =>
+ keepOnlySideEffects(tree)
+ }
+
+ /** Keeps only the side effects of a Tree (overapproximation). */
+ private def keepOnlySideEffects(stat: Tree): Tree = stat match {
+ case _:VarRef | _:This | _:Literal =>
+ Skip()(stat.pos)
+ case Block(init :+ last) =>
+ Block(init :+ keepOnlySideEffects(last))(stat.pos)
+ case LoadModule(ClassType(moduleClassName)) =>
+ if (hasElidableModuleAccessor(moduleClassName)) Skip()(stat.pos)
+ else stat
+ case Select(LoadModule(ClassType(moduleClassName)), _, _) =>
+ if (hasElidableModuleAccessor(moduleClassName)) Skip()(stat.pos)
+ else stat
+ case Closure(_, _, _, captureValues) =>
+ Block(captureValues.map(keepOnlySideEffects))(stat.pos)
+ case UnaryOp(_, arg) =>
+ keepOnlySideEffects(arg)
+ case If(cond, thenp, elsep) =>
+ (keepOnlySideEffects(thenp), keepOnlySideEffects(elsep)) match {
+ case (Skip(), Skip()) => keepOnlySideEffects(cond)
+ case (newThenp, newElsep) => If(cond, newThenp, newElsep)(NoType)(stat.pos)
+ }
+ case BinaryOp(_, lhs, rhs) =>
+ Block(keepOnlySideEffects(lhs), keepOnlySideEffects(rhs))(stat.pos)
+ case RecordValue(_, elems) =>
+ Block(elems.map(keepOnlySideEffects))(stat.pos)
+ case _ =>
+ stat
+ }
+
+ private def pretransformApply(tree: Apply, isStat: Boolean,
+ usePreTransform: Boolean)(
+ cont: PreTransCont)(
+ implicit scope: Scope): TailRec[Tree] = {
+ val Apply(receiver, methodIdent @ Ident(methodName, _), args) = tree
+ implicit val pos = tree.pos
+
+ pretransformExpr(receiver) { treceiver =>
+ def treeNotInlined0(transformedArgs: List[Tree]) =
+ cont(PreTransTree(Apply(finishTransformExpr(treceiver), methodIdent,
+ transformedArgs)(tree.tpe)(tree.pos), RefinedType(tree.tpe)))
+
+ def treeNotInlined = treeNotInlined0(args.map(transformExpr))
+
+ treceiver.tpe.base match {
+ case NothingType =>
+ cont(treceiver)
+ case NullType =>
+ cont(PreTransTree(Block(
+ finishTransformStat(treceiver),
+ CallHelper("throwNullPointerException")(NothingType))))
+ case _ =>
+ if (isReflProxyName(methodName)) {
+ // Never inline reflective proxies
+ treeNotInlined
+ } else {
+ val cls = boxedClassForType(treceiver.tpe.base)
+ val impls =
+ if (treceiver.tpe.isExact) staticCall(cls, methodName).toList
+ else dynamicCall(cls, methodName)
+ val allocationSite = treceiver.tpe.allocationSite
+ if (impls.isEmpty || impls.exists(impl =>
+ scope.implsBeingInlined((allocationSite, impl)))) {
+ // isEmpty could happen, have to leave it as is for the TypeError
+ treeNotInlined
+ } else if (impls.size == 1) {
+ val target = impls.head
+ pretransformExprs(args) { targs =>
+ val intrinsicCode = getIntrinsicCode(target)
+ if (intrinsicCode >= 0) {
+ callIntrinsic(intrinsicCode, Some(treceiver), targs,
+ isStat, usePreTransform)(cont)
+ } else if (target.inlineable || shouldInlineBecauseOfArgs(treceiver :: targs)) {
+ inline(allocationSite, Some(treceiver), targs, target,
+ isStat, usePreTransform)(cont)
+ } else {
+ treeNotInlined0(targs.map(finishTransformExpr))
+ }
+ }
+ } else {
+ if (impls.forall(_.isTraitImplForwarder)) {
+ val reference = impls.head
+ val TraitImplApply(ClassType(traitImpl), Ident(methodName, _), _) =
+ getMethodBody(reference).body
+ if (!impls.tail.forall(getMethodBody(_).body match {
+ case TraitImplApply(ClassType(`traitImpl`),
+ Ident(`methodName`, _), _) => true
+ case _ => false
+ })) {
+ // Not all calling the same method in the same trait impl
+ treeNotInlined
+ } else {
+ pretransformExprs(args) { targs =>
+ inline(allocationSite, Some(treceiver), targs, reference,
+ isStat, usePreTransform)(cont)
+ }
+ }
+ } else {
+ // TODO? Inline multiple non-trait-impl-forwarder with the exact same body?
+ treeNotInlined
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private def boxedClassForType(tpe: Type): String = (tpe: @unchecked) match {
+ case ClassType(cls) => cls
+ case AnyType => Definitions.ObjectClass
+ case UndefType => Definitions.BoxedUnitClass
+ case BooleanType => Definitions.BoxedBooleanClass
+ case IntType => Definitions.BoxedIntegerClass
+ case LongType => Definitions.BoxedLongClass
+ case FloatType => Definitions.BoxedFloatClass
+ case DoubleType => Definitions.BoxedDoubleClass
+ case StringType => Definitions.StringClass
+ case ArrayType(_, _) => Definitions.ObjectClass
+ }
+
+ private def pretransformStaticApply(tree: StaticApply, isStat: Boolean,
+ usePreTransform: Boolean)(
+ cont: PreTransCont)(
+ implicit scope: Scope): TailRec[Tree] = {
+ val StaticApply(receiver, clsType @ ClassType(cls),
+ methodIdent @ Ident(methodName, _), args) = tree
+ implicit val pos = tree.pos
+
+ def treeNotInlined0(transformedReceiver: Tree, transformedArgs: List[Tree]) =
+ cont(PreTransTree(StaticApply(transformedReceiver, clsType,
+ methodIdent, transformedArgs)(tree.tpe), RefinedType(tree.tpe)))
+
+ def treeNotInlined =
+ treeNotInlined0(transformExpr(receiver), args.map(transformExpr))
+
+ if (isReflProxyName(methodName)) {
+ // Never inline reflective proxies
+ treeNotInlined
+ } else {
+ val optTarget = staticCall(cls, methodName)
+ if (optTarget.isEmpty) {
+ // just in case
+ treeNotInlined
+ } else {
+ val target = optTarget.get
+ pretransformExprs(receiver, args) { (treceiver, targs) =>
+ val intrinsicCode = getIntrinsicCode(target)
+ if (intrinsicCode >= 0) {
+ callIntrinsic(intrinsicCode, Some(treceiver), targs,
+ isStat, usePreTransform)(cont)
+ } else {
+ val shouldInline =
+ target.inlineable || shouldInlineBecauseOfArgs(treceiver :: targs)
+ val allocationSite = treceiver.tpe.allocationSite
+ val beingInlined =
+ scope.implsBeingInlined((allocationSite, target))
+
+ if (shouldInline && !beingInlined) {
+ inline(allocationSite, Some(treceiver), targs, target,
+ isStat, usePreTransform)(cont)
+ } else {
+ treeNotInlined0(finishTransformExpr(treceiver),
+ targs.map(finishTransformExpr))
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private def pretransformTraitImplApply(tree: TraitImplApply, isStat: Boolean,
+ usePreTransform: Boolean)(
+ cont: PreTransCont)(
+ implicit scope: Scope): TailRec[Tree] = {
+ val TraitImplApply(implType @ ClassType(impl),
+ methodIdent @ Ident(methodName, _), args) = tree
+ implicit val pos = tree.pos
+
+ def treeNotInlined0(transformedArgs: List[Tree]) =
+ cont(PreTransTree(TraitImplApply(implType, methodIdent,
+ transformedArgs)(tree.tpe), RefinedType(tree.tpe)))
+
+ def treeNotInlined = treeNotInlined0(args.map(transformExpr))
+
+ val optTarget = traitImplCall(impl, methodName)
+ if (optTarget.isEmpty) {
+ // just in case
+ treeNotInlined
+ } else {
+ val target = optTarget.get
+ pretransformExprs(args) { targs =>
+ val intrinsicCode = getIntrinsicCode(target)
+ if (intrinsicCode >= 0) {
+ callIntrinsic(intrinsicCode, None, targs,
+ isStat, usePreTransform)(cont)
+ } else {
+ val shouldInline =
+ target.inlineable || shouldInlineBecauseOfArgs(targs)
+ val allocationSite = targs.headOption.flatMap(_.tpe.allocationSite)
+ val beingInlined =
+ scope.implsBeingInlined((allocationSite, target))
+
+ if (shouldInline && !beingInlined) {
+ inline(allocationSite, None, targs, target,
+ isStat, usePreTransform)(cont)
+ } else {
+ treeNotInlined0(targs.map(finishTransformExpr))
+ }
+ }
+ }
+ }
+ }
+
+ private def pretransformJSFunctionApply(tree: JSFunctionApply,
+ isStat: Boolean, usePreTransform: Boolean)(
+ cont: PreTransCont)(
+ implicit scope: Scope, pos: Position): TailRec[Tree] = {
+ val JSFunctionApply(fun, args) = tree
+ implicit val pos = tree.pos
+
+ pretransformExpr(fun) { tfun =>
+ tfun match {
+ case PreTransLocalDef(LocalDef(_, false,
+ closure @ TentativeClosureReplacement(
+ captureParams, params, body, captureLocalDefs,
+ alreadyUsed, cancelFun))) if !alreadyUsed.value =>
+ alreadyUsed.value = true
+ pretransformExprs(args) { targs =>
+ inlineBody(
+ Some(PreTransTree(Undefined())), // `this` is `undefined`
+ captureParams ++ params, AnyType, body,
+ captureLocalDefs.map(PreTransLocalDef(_)) ++ targs, isStat,
+ usePreTransform)(cont)
+ }
+
+ case _ =>
+ cont(PreTransTree(
+ JSFunctionApply(finishTransformExpr(tfun), args.map(transformExpr))))
+ }
+ }
+ }
+
+ private def shouldInlineBecauseOfArgs(
+ receiverAndArgs: List[PreTransform]): Boolean = {
+ def isLikelyOptimizable(arg: PreTransform): Boolean = arg match {
+ case PreTransBlock(_, result) =>
+ isLikelyOptimizable(result)
+
+ case PreTransLocalDef(localDef) =>
+ localDef.replacement match {
+ case TentativeClosureReplacement(_, _, _, _, _, _) => true
+ case ReplaceWithRecordVarRef(_, _, _, _, _) => true
+ case InlineClassBeingConstructedReplacement(_, _) => true
+ case InlineClassInstanceReplacement(_, _, _) => true
+ case _ => false
+ }
+
+ case PreTransRecordTree(_, _, _) =>
+ true
+
+ case _ =>
+ arg.tpe.base match {
+ case ClassType("s_Predef$$less$colon$less" | "s_Predef$$eq$colon$eq") =>
+ true
+ case _ =>
+ false
+ }
+ }
+ receiverAndArgs.exists(isLikelyOptimizable)
+ }
+
+ private def inline(allocationSite: Option[AllocationSite],
+ optReceiver: Option[PreTransform],
+ args: List[PreTransform], target: MethodID, isStat: Boolean,
+ usePreTransform: Boolean)(
+ cont: PreTransCont)(
+ implicit scope: Scope, pos: Position): TailRec[Tree] = {
+
+ attemptedInlining += target
+
+ val MethodDef(_, formals, resultType, body) = getMethodBody(target)
+
+ body match {
+ case Skip() =>
+ assert(isStat, "Found Skip() in expression position")
+ cont(PreTransTree(
+ Block((optReceiver ++: args).map(finishTransformStat)),
+ RefinedType.NoRefinedType))
+
+ case _: Literal =>
+ cont(PreTransTree(
+ Block((optReceiver ++: args).map(finishTransformStat) :+ body),
+ RefinedType(body.tpe)))
+
+ case This() if args.isEmpty =>
+ assert(optReceiver.isDefined,
+ "There was a This(), there should be a receiver")
+ cont(optReceiver.get)
+
+ case Select(This(), field, mutable) if formals.isEmpty =>
+ assert(optReceiver.isDefined,
+ "There was a This(), there should be a receiver")
+ pretransformSelectCommon(body.tpe, optReceiver.get, field, mutable,
+ isLhsOfAssign = false)(cont)
+
+ case Assign(lhs @ Select(This(), field, mutable), VarRef(Ident(rhsName, _), _))
+ if formals.size == 1 && formals.head.name.name == rhsName =>
+ assert(isStat, "Found Assign in expression position")
+ assert(optReceiver.isDefined,
+ "There was a This(), there should be a receiver")
+ pretransformSelectCommon(lhs.tpe, optReceiver.get, field, mutable,
+ isLhsOfAssign = true) { preTransLhs =>
+ // TODO Support assignment of record
+ cont(PreTransTree(
+ Assign(finishTransformExpr(preTransLhs),
+ finishTransformExpr(args.head)),
+ RefinedType.NoRefinedType))
+ }
+
+ case _ =>
+ val targetID = (allocationSite, target)
+ inlineBody(optReceiver, formals, resultType, body, args, isStat,
+ usePreTransform)(cont)(scope.inlining(targetID), pos)
+ }
+ }
+
+ private def inlineBody(optReceiver: Option[PreTransform],
+ formals: List[ParamDef], resultType: Type, body: Tree,
+ args: List[PreTransform], isStat: Boolean,
+ usePreTransform: Boolean)(
+ cont: PreTransCont)(
+ implicit scope: Scope, pos: Position): TailRec[Tree] = tailcall {
+
+ val optReceiverBinding = optReceiver map { receiver =>
+ Binding("this", None, receiver.tpe.base, false, receiver)
+ }
+
+ val argsBindings = for {
+ (ParamDef(Ident(name, originalName), tpe, mutable), arg) <- formals zip args
+ } yield {
+ Binding(name, originalName, tpe, mutable, arg)
+ }
+
+ withBindings(optReceiverBinding ++: argsBindings) { (bodyScope, cont1) =>
+ returnable("", resultType, body, isStat, usePreTransform)(
+ cont1)(bodyScope, pos)
+ } (cont) (scope.withEnv(OptEnv.Empty))
+ }
+
+ private def callIntrinsic(code: Int, optTReceiver: Option[PreTransform],
+ targs: List[PreTransform], isStat: Boolean, usePreTransform: Boolean)(
+ cont: PreTransCont)(
+ implicit pos: Position): TailRec[Tree] = {
+
+ import Intrinsics._
+
+ implicit def string2ident(s: String): Ident = Ident(s, None)
+
+ lazy val newArgs = targs.map(finishTransformExpr)
+
+ @inline def contTree(result: Tree) = cont(PreTransTree(result))
+
+ @inline def StringClassType = ClassType(Definitions.StringClass)
+
+ def asRTLong(arg: Tree): Tree =
+ AsInstanceOf(arg, ClassType(LongImpl.RuntimeLongClass))
+ def firstArgAsRTLong: Tree =
+ asRTLong(newArgs.head)
+
+ (code: @switch) match {
+ // java.lang.System
+
+ case ArrayCopy =>
+ assert(isStat, "System.arraycopy must be used in statement position")
+ contTree(CallHelper("systemArraycopy", newArgs)(NoType))
+ case IdentityHashCode =>
+ contTree(CallHelper("systemIdentityHashCode", newArgs)(IntType))
+
+ // scala.scalajs.runtime package object
+
+ case PropertiesOf =>
+ contTree(CallHelper("propertiesOf", newArgs)(AnyType))
+
+ // java.lang.Long
+
+ case LongToString =>
+ contTree(Apply(firstArgAsRTLong, "toString__T", Nil)(StringClassType))
+ case LongCompare =>
+ contTree(Apply(firstArgAsRTLong, "compareTo__sjsr_RuntimeLong__I",
+ List(asRTLong(newArgs(1))))(IntType))
+ case LongBitCount =>
+ contTree(Apply(firstArgAsRTLong, LongImpl.bitCount, Nil)(IntType))
+ case LongSignum =>
+ contTree(Apply(firstArgAsRTLong, LongImpl.signum, Nil)(LongType))
+ case LongLeading0s =>
+ contTree(Apply(firstArgAsRTLong, LongImpl.numberOfLeadingZeros, Nil)(IntType))
+ case LongTrailing0s =>
+ contTree(Apply(firstArgAsRTLong, LongImpl.numberOfTrailingZeros, Nil)(IntType))
+ case LongToBinStr =>
+ contTree(Apply(firstArgAsRTLong, LongImpl.toBinaryString, Nil)(StringClassType))
+ case LongToHexStr =>
+ contTree(Apply(firstArgAsRTLong, LongImpl.toHexString, Nil)(StringClassType))
+ case LongToOctalStr =>
+ contTree(Apply(firstArgAsRTLong, LongImpl.toOctalString, Nil)(StringClassType))
+
+ // TypedArray conversions
+
+ case ByteArrayToInt8Array =>
+ contTree(CallHelper("byteArray2TypedArray", newArgs)(AnyType))
+ case ShortArrayToInt16Array =>
+ contTree(CallHelper("shortArray2TypedArray", newArgs)(AnyType))
+ case CharArrayToUint16Array =>
+ contTree(CallHelper("charArray2TypedArray", newArgs)(AnyType))
+ case IntArrayToInt32Array =>
+ contTree(CallHelper("intArray2TypedArray", newArgs)(AnyType))
+ case FloatArrayToFloat32Array =>
+ contTree(CallHelper("floatArray2TypedArray", newArgs)(AnyType))
+ case DoubleArrayToFloat64Array =>
+ contTree(CallHelper("doubleArray2TypedArray", newArgs)(AnyType))
+
+ case Int8ArrayToByteArray =>
+ contTree(CallHelper("typedArray2ByteArray", newArgs)(AnyType))
+ case Int16ArrayToShortArray =>
+ contTree(CallHelper("typedArray2ShortArray", newArgs)(AnyType))
+ case Uint16ArrayToCharArray =>
+ contTree(CallHelper("typedArray2CharArray", newArgs)(AnyType))
+ case Int32ArrayToIntArray =>
+ contTree(CallHelper("typedArray2IntArray", newArgs)(AnyType))
+ case Float32ArrayToFloatArray =>
+ contTree(CallHelper("typedArray2FloatArray", newArgs)(AnyType))
+ case Float64ArrayToDoubleArray =>
+ contTree(CallHelper("typedArray2DoubleArray", newArgs)(AnyType))
+ }
+ }
+
+ private def inlineClassConstructor(allocationSite: AllocationSite,
+ cls: ClassType, initialValue: RecordValue,
+ ctor: Ident, args: List[PreTransform], cancelFun: CancelFun)(
+ cont: PreTransCont)(
+ implicit scope: Scope, pos: Position): TailRec[Tree] = {
+
+ val RecordValue(recordType, initialFieldValues) = initialValue
+
+ pretransformExprs(initialFieldValues) { tinitialFieldValues =>
+ val initialFieldBindings = for {
+ (RecordType.Field(name, originalName, tpe, mutable), value) <-
+ recordType.fields zip tinitialFieldValues
+ } yield {
+ Binding(name, originalName, tpe, mutable, value)
+ }
+
+ withNewLocalDefs(initialFieldBindings) { (initialFieldLocalDefList, cont1) =>
+ val fieldNames = initialValue.tpe.fields.map(_.name)
+ val initialFieldLocalDefs =
+ Map(fieldNames zip initialFieldLocalDefList: _*)
+
+ inlineClassConstructorBody(allocationSite, initialFieldLocalDefs,
+ cls, cls, ctor, args, cancelFun) { (finalFieldLocalDefs, cont2) =>
+ cont2(PreTransLocalDef(LocalDef(
+ RefinedType(cls, isExact = true, isNullable = false,
+ allocationSite = Some(allocationSite)),
+ mutable = false,
+ InlineClassInstanceReplacement(recordType, finalFieldLocalDefs, cancelFun))))
+ } (cont1)
+ } (cont)
+ }
+ }
+
+ private def inlineClassConstructorBody(
+ allocationSite: AllocationSite,
+ inputFieldsLocalDefs: Map[String, LocalDef], cls: ClassType,
+ ctorClass: ClassType, ctor: Ident, args: List[PreTransform],
+ cancelFun: CancelFun)(
+ buildInner: (Map[String, LocalDef], PreTransCont) => TailRec[Tree])(
+ cont: PreTransCont)(
+ implicit scope: Scope): TailRec[Tree] = tailcall {
+
+ val target = staticCall(ctorClass.className, ctor.name).getOrElse(cancelFun())
+ val targetID = (Some(allocationSite), target)
+ if (scope.implsBeingInlined.contains(targetID))
+ cancelFun()
+
+ val MethodDef(_, formals, _, BlockOrAlone(stats, This())) =
+ getMethodBody(target)
+
+ val argsBindings = for {
+ (ParamDef(Ident(name, originalName), tpe, mutable), arg) <- formals zip args
+ } yield {
+ Binding(name, originalName, tpe, mutable, arg)
+ }
+
+ withBindings(argsBindings) { (bodyScope, cont1) =>
+ val thisLocalDef = LocalDef(
+ RefinedType(cls, isExact = true, isNullable = false), false,
+ InlineClassBeingConstructedReplacement(inputFieldsLocalDefs, cancelFun))
+ val statsScope = bodyScope.inlining(targetID).withEnv(
+ bodyScope.env.withLocalDef("this", thisLocalDef))
+ inlineClassConstructorBodyList(allocationSite, thisLocalDef,
+ inputFieldsLocalDefs, cls, stats, cancelFun)(
+ buildInner)(cont1)(statsScope)
+ } (cont) (scope.withEnv(OptEnv.Empty))
+ }
+
+ private def inlineClassConstructorBodyList(
+ allocationSite: AllocationSite,
+ thisLocalDef: LocalDef, inputFieldsLocalDefs: Map[String, LocalDef],
+ cls: ClassType, stats: List[Tree], cancelFun: CancelFun)(
+ buildInner: (Map[String, LocalDef], PreTransCont) => TailRec[Tree])(
+ cont: PreTransCont)(
+ implicit scope: Scope): TailRec[Tree] = {
+ stats match {
+ case This() :: rest =>
+ inlineClassConstructorBodyList(allocationSite, thisLocalDef,
+ inputFieldsLocalDefs, cls, rest, cancelFun)(buildInner)(cont)
+
+ case Assign(s @ Select(ths: This,
+ Ident(fieldName, fieldOrigName), false), value) :: rest =>
+ pretransformExpr(value) { tvalue =>
+ withNewLocalDef(Binding(fieldName, fieldOrigName, s.tpe, false,
+ tvalue)) { (localDef, cont1) =>
+ if (localDef.contains(thisLocalDef)) {
+ /* Uh oh, there is a `val x = ...this...`. We can't keep it,
+ * because this field will not be updated with `newThisLocalDef`.
+ */
+ cancelFun()
+ }
+ val newFieldsLocalDefs =
+ inputFieldsLocalDefs.updated(fieldName, localDef)
+ val newThisLocalDef = LocalDef(
+ RefinedType(cls, isExact = true, isNullable = false), false,
+ InlineClassBeingConstructedReplacement(newFieldsLocalDefs, cancelFun))
+ val restScope = scope.withEnv(scope.env.withLocalDef(
+ "this", newThisLocalDef))
+ inlineClassConstructorBodyList(allocationSite,
+ newThisLocalDef, newFieldsLocalDefs, cls, rest, cancelFun)(
+ buildInner)(cont1)(restScope)
+ } (cont)
+ }
+
+ /* if (cond)
+ * throw e
+ * else
+ * this.outer = value
+ *
+ * becomes
+ *
+ * this.outer =
+ * if (cond) throw e
+ * else value
+ *
+ * Typical shape of initialization of outer pointer of inner classes.
+ */
+ case If(cond, th: Throw,
+ Assign(Select(This(), _, false), value)) :: rest =>
+ // work around a bug of the compiler (these should be @-bindings)
+ val stat = stats.head.asInstanceOf[If]
+ val ass = stat.elsep.asInstanceOf[Assign]
+ val lhs = ass.lhs
+ inlineClassConstructorBodyList(allocationSite, thisLocalDef,
+ inputFieldsLocalDefs, cls,
+ Assign(lhs, If(cond, th, value)(lhs.tpe)(stat.pos))(ass.pos) :: rest,
+ cancelFun)(buildInner)(cont)
+
+ case StaticApply(ths: This, superClass, superCtor, args) :: rest
+ if isConstructorName(superCtor.name) =>
+ pretransformExprs(args) { targs =>
+ inlineClassConstructorBody(allocationSite, inputFieldsLocalDefs,
+ cls, superClass, superCtor, targs,
+ cancelFun) { (outputFieldsLocalDefs, cont1) =>
+ val newThisLocalDef = LocalDef(
+ RefinedType(cls, isExact = true, isNullable = false), false,
+ InlineClassBeingConstructedReplacement(outputFieldsLocalDefs, cancelFun))
+ val restScope = scope.withEnv(scope.env.withLocalDef(
+ "this", newThisLocalDef))
+ inlineClassConstructorBodyList(allocationSite,
+ newThisLocalDef, outputFieldsLocalDefs,
+ cls, rest, cancelFun)(buildInner)(cont1)(restScope)
+ } (cont)
+ }
+
+ case VarDef(Ident(name, originalName), tpe, mutable, rhs) :: rest =>
+ pretransformExpr(rhs) { trhs =>
+ withBinding(Binding(name, originalName, tpe, mutable, trhs)) { (restScope, cont1) =>
+ inlineClassConstructorBodyList(allocationSite,
+ thisLocalDef, inputFieldsLocalDefs,
+ cls, rest, cancelFun)(buildInner)(cont1)(restScope)
+ } (cont)
+ }
+
+ case stat :: rest =>
+ val transformedStat = transformStat(stat)
+ transformedStat match {
+ case Skip() =>
+ inlineClassConstructorBodyList(allocationSite,
+ thisLocalDef, inputFieldsLocalDefs,
+ cls, rest, cancelFun)(buildInner)(cont)
+ case _ =>
+ if (transformedStat.tpe == NothingType)
+ cont(PreTransTree(transformedStat, RefinedType.Nothing))
+ else {
+ inlineClassConstructorBodyList(allocationSite,
+ thisLocalDef, inputFieldsLocalDefs,
+ cls, rest, cancelFun) { (outputFieldsLocalDefs, cont1) =>
+ buildInner(outputFieldsLocalDefs, { tinner =>
+ cont1(PreTransBlock(transformedStat :: Nil, tinner))
+ })
+ }(cont)
+ }
+ }
+
+ case Nil =>
+ buildInner(inputFieldsLocalDefs, cont)
+ }
+ }
+
+ private def foldIf(cond: Tree, thenp: Tree, elsep: Tree)(tpe: Type)(
+ implicit pos: Position): Tree = {
+ import BinaryOp._
+
+ @inline def default = If(cond, thenp, elsep)(tpe)
+ cond match {
+ case BooleanLiteral(v) =>
+ if (v) thenp
+ else elsep
+
+ case _ =>
+ @inline def negCond = foldUnaryOp(UnaryOp.Boolean_!, cond)
+ if (thenp.tpe == BooleanType && elsep.tpe == BooleanType) {
+ (cond, thenp, elsep) match {
+ case (_, BooleanLiteral(t), BooleanLiteral(e)) =>
+ if (t == e) Block(keepOnlySideEffects(cond), thenp)
+ else if (t) cond
+ else negCond
+
+ case (_, BooleanLiteral(false), _) =>
+ foldIf(negCond, elsep, BooleanLiteral(false))(tpe) // canonical && form
+ case (_, _, BooleanLiteral(true)) =>
+ foldIf(negCond, BooleanLiteral(true), thenp)(tpe) // canonical || form
+
+ /* if (lhs === null) rhs === null else lhs === rhs
+ * -> lhs === rhs
+ * This is the typical shape of a lhs == rhs test where
+ * the equals() method has been inlined as a reference
+ * equality test.
+ */
+ case (BinaryOp(BinaryOp.===, VarRef(lhsIdent, _), Null()),
+ BinaryOp(BinaryOp.===, VarRef(rhsIdent, _), Null()),
+ BinaryOp(BinaryOp.===, VarRef(lhsIdent2, _), VarRef(rhsIdent2, _)))
+ if lhsIdent2 == lhsIdent && rhsIdent2 == rhsIdent =>
+ elsep
+
+ // Example: (x > y) || (x == y) -> (x >= y)
+ case (BinaryOp(op1 @ (Num_== | Num_!= | Num_< | Num_<= | Num_> | Num_>=), l1, r1),
+ BooleanLiteral(true),
+ BinaryOp(op2 @ (Num_== | Num_!= | Num_< | Num_<= | Num_> | Num_>=), l2, r2))
+ if ((l1.isInstanceOf[Literal] || l1.isInstanceOf[VarRef]) &&
+ (r1.isInstanceOf[Literal] || r1.isInstanceOf[VarRef]) &&
+ (l1 == l2 && r1 == r2)) =>
+ val canBeEqual =
+ ((op1 == Num_==) || (op1 == Num_<=) || (op1 == Num_>=)) ||
+ ((op2 == Num_==) || (op2 == Num_<=) || (op2 == Num_>=))
+ val canBeLessThan =
+ ((op1 == Num_!=) || (op1 == Num_<) || (op1 == Num_<=)) ||
+ ((op2 == Num_!=) || (op2 == Num_<) || (op2 == Num_<=))
+ val canBeGreaterThan =
+ ((op1 == Num_!=) || (op1 == Num_>) || (op1 == Num_>=)) ||
+ ((op2 == Num_!=) || (op2 == Num_>) || (op2 == Num_>=))
+
+ fold3WayComparison(canBeEqual, canBeLessThan, canBeGreaterThan, l1, r1)
+
+ // Example: (x >= y) && (x <= y) -> (x == y)
+ case (BinaryOp(op1 @ (Num_== | Num_!= | Num_< | Num_<= | Num_> | Num_>=), l1, r1),
+ BinaryOp(op2 @ (Num_== | Num_!= | Num_< | Num_<= | Num_> | Num_>=), l2, r2),
+ BooleanLiteral(false))
+ if ((l1.isInstanceOf[Literal] || l1.isInstanceOf[VarRef]) &&
+ (r1.isInstanceOf[Literal] || r1.isInstanceOf[VarRef]) &&
+ (l1 == l2 && r1 == r2)) =>
+ val canBeEqual =
+ ((op1 == Num_==) || (op1 == Num_<=) || (op1 == Num_>=)) &&
+ ((op2 == Num_==) || (op2 == Num_<=) || (op2 == Num_>=))
+ val canBeLessThan =
+ ((op1 == Num_!=) || (op1 == Num_<) || (op1 == Num_<=)) &&
+ ((op2 == Num_!=) || (op2 == Num_<) || (op2 == Num_<=))
+ val canBeGreaterThan =
+ ((op1 == Num_!=) || (op1 == Num_>) || (op1 == Num_>=)) &&
+ ((op2 == Num_!=) || (op2 == Num_>) || (op2 == Num_>=))
+
+ fold3WayComparison(canBeEqual, canBeLessThan, canBeGreaterThan, l1, r1)
+
+ case _ => default
+ }
+ } else {
+ (thenp, elsep) match {
+ case (Skip(), Skip()) => keepOnlySideEffects(cond)
+ case (Skip(), _) => foldIf(negCond, elsep, thenp)(tpe)
+
+ case _ => default
+ }
+ }
+ }
+ }
+
+ private def transformUnaryOp(tree: UnaryOp)(implicit scope: Scope): Tree = {
+ import UnaryOp._
+
+ implicit val pos = tree.pos
+ val UnaryOp(op, arg) = tree
+
+ (op: @switch) match {
+ case LongToInt =>
+ trampoline {
+ pretransformExpr(arg) { (targ) =>
+ TailCalls.done {
+ foldUnaryOp(op, finishTransformOptLongExpr(targ))
+ }
+ }
+ }
+
+ case _ =>
+ foldUnaryOp(op, transformExpr(arg))
+ }
+ }
+
+ private def transformBinaryOp(tree: BinaryOp)(implicit scope: Scope): Tree = {
+ import BinaryOp._
+
+ implicit val pos = tree.pos
+ val BinaryOp(op, lhs, rhs) = tree
+
+ (op: @switch) match {
+ case === | !== =>
+ trampoline {
+ pretransformExprs(lhs, rhs) { (tlhs, trhs) =>
+ TailCalls.done(foldReferenceEquality(tlhs, trhs, op == ===))
+ }
+ }
+
+ case Long_== | Long_!= | Long_< | Long_<= | Long_> | Long_>= =>
+ trampoline {
+ pretransformExprs(lhs, rhs) { (tlhs, trhs) =>
+ TailCalls.done {
+ if (isLiteralOrOptimizableLong(tlhs) &&
+ isLiteralOrOptimizableLong(trhs)) {
+ foldBinaryOp(op, finishTransformOptLongExpr(tlhs),
+ finishTransformOptLongExpr(trhs))
+ } else {
+ foldBinaryOp(op, finishTransformExpr(tlhs),
+ finishTransformExpr(trhs))
+ }
+ }
+ }
+ }
+
+ case _ =>
+ foldBinaryOp(op, transformExpr(lhs), transformExpr(rhs))
+ }
+ }
+
+ private def isLiteralOrOptimizableLong(texpr: PreTransform): Boolean = {
+ texpr match {
+ case PreTransTree(LongLiteral(_), _) =>
+ true
+ case PreTransLocalDef(LocalDef(_, _, replacement)) =>
+ replacement match {
+ case ReplaceWithVarRef(_, _, _, Some(_)) => true
+ case ReplaceWithConstant(LongLiteral(_)) => true
+ case _ => false
+ }
+ case _ =>
+ false
+ }
+ }
+
+ private def finishTransformOptLongExpr(targ: PreTransform): Tree = targ match {
+ case PreTransLocalDef(LocalDef(tpe, false,
+ ReplaceWithVarRef(_, _, _, Some(argValue)))) =>
+ argValue()
+ case _ =>
+ finishTransformExpr(targ)
+ }
+
+ private def foldUnaryOp(op: UnaryOp.Code, arg: Tree)(
+ implicit pos: Position): Tree = {
+ import UnaryOp._
+ @inline def default = UnaryOp(op, arg)
+ (op: @switch) match {
+ case Boolean_! =>
+ arg match {
+ case BooleanLiteral(v) => BooleanLiteral(!v)
+ case UnaryOp(Boolean_!, x) => x
+
+ case BinaryOp(innerOp, l, r) =>
+ val newOp = (innerOp: @switch) match {
+ case BinaryOp.=== => BinaryOp.!==
+ case BinaryOp.!== => BinaryOp.===
+
+ case BinaryOp.Num_== => BinaryOp.Num_!=
+ case BinaryOp.Num_!= => BinaryOp.Num_==
+ case BinaryOp.Num_< => BinaryOp.Num_>=
+ case BinaryOp.Num_<= => BinaryOp.Num_>
+ case BinaryOp.Num_> => BinaryOp.Num_<=
+ case BinaryOp.Num_>= => BinaryOp.Num_<
+
+ case BinaryOp.Long_== => BinaryOp.Long_!=
+ case BinaryOp.Long_!= => BinaryOp.Long_==
+ case BinaryOp.Long_< => BinaryOp.Long_>=
+ case BinaryOp.Long_<= => BinaryOp.Long_>
+ case BinaryOp.Long_> => BinaryOp.Long_<=
+ case BinaryOp.Long_>= => BinaryOp.Long_<
+
+ case BinaryOp.Boolean_== => BinaryOp.Boolean_!=
+ case BinaryOp.Boolean_!= => BinaryOp.Boolean_==
+
+ case _ => -1
+ }
+ if (newOp == -1) default
+ else BinaryOp(newOp, l, r)
+
+ case _ => default
+ }
+
+ case IntToLong =>
+ arg match {
+ case IntLiteral(v) => LongLiteral(v.toLong)
+ case _ => default
+ }
+
+ case LongToInt =>
+ arg match {
+ case LongLiteral(v) => IntLiteral(v.toInt)
+ case UnaryOp(IntToLong, x) => x
+
+ case BinaryOp(BinaryOp.Long_+, x, y) =>
+ foldBinaryOp(BinaryOp.Int_+,
+ foldUnaryOp(LongToInt, x),
+ foldUnaryOp(LongToInt, y))
+ case BinaryOp(BinaryOp.Long_-, x, y) =>
+ foldBinaryOp(BinaryOp.Int_-,
+ foldUnaryOp(LongToInt, x),
+ foldUnaryOp(LongToInt, y))
+
+ case _ => default
+ }
+
+ case LongToDouble =>
+ arg match {
+ case LongLiteral(v) => DoubleLiteral(v.toDouble)
+ case _ => default
+ }
+ case DoubleToInt =>
+ arg match {
+ case _ if arg.tpe == IntType => arg
+ case NumberLiteral(v) => IntLiteral(v.toInt)
+ case _ => default
+ }
+ case DoubleToFloat =>
+ arg match {
+ case _ if arg.tpe == FloatType => arg
+ case NumberLiteral(v) => FloatLiteral(v.toFloat)
+ case _ => default
+ }
+ case DoubleToLong =>
+ arg match {
+ case _ if arg.tpe == IntType => foldUnaryOp(IntToLong, arg)
+ case NumberLiteral(v) => LongLiteral(v.toLong)
+ case _ => default
+ }
+ case _ =>
+ default
+ }
+ }
+
+ /** Performs === for two literals.
+ * The result is always known statically.
+ */
+ private def literal_===(lhs: Literal, rhs: Literal): Boolean = {
+ (lhs, rhs) match {
+ case (IntLiteral(l), IntLiteral(r)) => l == r
+ case (FloatLiteral(l), FloatLiteral(r)) => l == r
+ case (NumberLiteral(l), NumberLiteral(r)) => l == r
+ case (LongLiteral(l), LongLiteral(r)) => l == r
+ case (BooleanLiteral(l), BooleanLiteral(r)) => l == r
+ case (StringLiteral(l), StringLiteral(r)) => l == r
+ case (Undefined(), Undefined()) => true
+ case (Null(), Null()) => true
+ case _ => false
+ }
+ }
+
+ private def foldBinaryOp(op: BinaryOp.Code, lhs: Tree, rhs: Tree)(
+ implicit pos: Position): Tree = {
+ import BinaryOp._
+ @inline def default = BinaryOp(op, lhs, rhs)
+ (op: @switch) match {
+ case === | !== =>
+ val positive = (op == ===)
+ (lhs, rhs) match {
+ case (lhs: Literal, rhs: Literal) =>
+ BooleanLiteral(literal_===(lhs, rhs) == positive)
+
+ case (_: Literal, _) => foldBinaryOp(op, rhs, lhs)
+ case _ => default
+ }
+
+ case Int_+ =>
+ (lhs, rhs) match {
+ case (IntLiteral(l), IntLiteral(r)) => IntLiteral(l + r)
+ case (_, IntLiteral(_)) => foldBinaryOp(Int_+, rhs, lhs)
+ case (IntLiteral(0), _) => rhs
+
+ case (IntLiteral(x),
+ BinaryOp(innerOp @ (Int_+ | Int_-), IntLiteral(y), z)) =>
+ foldBinaryOp(innerOp, IntLiteral(x+y), z)
+
+ case _ => default
+ }
+
+ case Int_- =>
+ (lhs, rhs) match {
+ case (_, IntLiteral(r)) => foldBinaryOp(Int_+, lhs, IntLiteral(-r))
+
+ case (IntLiteral(x), BinaryOp(Int_+, IntLiteral(y), z)) =>
+ foldBinaryOp(Int_-, IntLiteral(x-y), z)
+ case (IntLiteral(x), BinaryOp(Int_-, IntLiteral(y), z)) =>
+ foldBinaryOp(Int_+, IntLiteral(x-y), z)
+
+ case (_, BinaryOp(Int_-, IntLiteral(0), x)) =>
+ foldBinaryOp(Int_+, lhs, x)
+
+ case _ => default
+ }
+
+ case Int_* =>
+ (lhs, rhs) match {
+ case (IntLiteral(l), IntLiteral(r)) => IntLiteral(l * r)
+ case (_, IntLiteral(_)) => foldBinaryOp(Int_*, rhs, lhs)
+
+ case (IntLiteral(1), _) => rhs
+ case (IntLiteral(-1), _) => foldBinaryOp(Int_-, IntLiteral(0), lhs)
+
+ case _ => default
+ }
+
+ case Int_/ =>
+ (lhs, rhs) match {
+ case (IntLiteral(l), IntLiteral(r)) if r != 0 => IntLiteral(l / r)
+
+ case (_, IntLiteral(1)) => lhs
+ case (_, IntLiteral(-1)) => foldBinaryOp(Int_-, IntLiteral(0), lhs)
+
+ case _ => default
+ }
+
+ case Int_% =>
+ (lhs, rhs) match {
+ case (IntLiteral(l), IntLiteral(r)) if r != 0 => IntLiteral(l % r)
+ case (_, IntLiteral(1 | -1)) =>
+ Block(keepOnlySideEffects(lhs), IntLiteral(0))
+ case _ => default
+ }
+
+ case Int_| =>
+ (lhs, rhs) match {
+ case (IntLiteral(l), IntLiteral(r)) => IntLiteral(l | r)
+ case (_, IntLiteral(_)) => foldBinaryOp(Int_|, rhs, lhs)
+ case (IntLiteral(0), _) => rhs
+
+ case (IntLiteral(x), BinaryOp(Int_|, IntLiteral(y), z)) =>
+ foldBinaryOp(Int_|, IntLiteral(x | y), z)
+
+ case _ => default
+ }
+
+ case Int_& =>
+ (lhs, rhs) match {
+ case (IntLiteral(l), IntLiteral(r)) => IntLiteral(l & r)
+ case (_, IntLiteral(_)) => foldBinaryOp(Int_&, rhs, lhs)
+ case (IntLiteral(-1), _) => rhs
+
+ case (IntLiteral(x), BinaryOp(Int_&, IntLiteral(y), z)) =>
+ foldBinaryOp(Int_&, IntLiteral(x & y), z)
+
+ case _ => default
+ }
+
+ case Int_^ =>
+ (lhs, rhs) match {
+ case (IntLiteral(l), IntLiteral(r)) => IntLiteral(l ^ r)
+ case (_, IntLiteral(_)) => foldBinaryOp(Int_^, rhs, lhs)
+ case (IntLiteral(0), _) => rhs
+
+ case (IntLiteral(x), BinaryOp(Int_^, IntLiteral(y), z)) =>
+ foldBinaryOp(Int_^, IntLiteral(x ^ y), z)
+
+ case _ => default
+ }
+
+ case Int_<< =>
+ (lhs, rhs) match {
+ case (IntLiteral(l), IntLiteral(r)) => IntLiteral(l << r)
+ case (_, IntLiteral(x)) if x % 32 == 0 => lhs
+ case _ => default
+ }
+
+ case Int_>>> =>
+ (lhs, rhs) match {
+ case (IntLiteral(l), IntLiteral(r)) => IntLiteral(l >>> r)
+ case (_, IntLiteral(x)) if x % 32 == 0 => lhs
+ case _ => default
+ }
+
+ case Int_>> =>
+ (lhs, rhs) match {
+ case (IntLiteral(l), IntLiteral(r)) => IntLiteral(l >> r)
+ case (_, IntLiteral(x)) if x % 32 == 0 => lhs
+ case _ => default
+ }
+
+ case Long_+ =>
+ (lhs, rhs) match {
+ case (LongLiteral(l), LongLiteral(r)) => LongLiteral(l + r)
+ case (_, LongLiteral(_)) => foldBinaryOp(Long_+, rhs, lhs)
+ case (LongLiteral(0), _) => rhs
+
+ case (LongLiteral(x),
+ BinaryOp(innerOp @ (Long_+ | Long_-), LongLiteral(y), z)) =>
+ foldBinaryOp(innerOp, LongLiteral(x+y), z)
+
+ case _ => default
+ }
+
+ case Long_- =>
+ (lhs, rhs) match {
+ case (_, LongLiteral(r)) => foldBinaryOp(Long_+, LongLiteral(-r), lhs)
+
+ case (LongLiteral(x), BinaryOp(Long_+, LongLiteral(y), z)) =>
+ foldBinaryOp(Long_-, LongLiteral(x-y), z)
+ case (LongLiteral(x), BinaryOp(Long_-, LongLiteral(y), z)) =>
+ foldBinaryOp(Long_+, LongLiteral(x-y), z)
+
+ case (_, BinaryOp(BinaryOp.Long_-, LongLiteral(0L), x)) =>
+ foldBinaryOp(Long_+, lhs, x)
+
+ case _ => default
+ }
+
+ case Long_* =>
+ (lhs, rhs) match {
+ case (LongLiteral(l), LongLiteral(r)) => LongLiteral(l * r)
+ case (_, LongLiteral(_)) => foldBinaryOp(Long_*, rhs, lhs)
+
+ case (LongLiteral(1), _) => rhs
+ case (LongLiteral(-1), _) => foldBinaryOp(Long_-, LongLiteral(0), lhs)
+
+ case _ => default
+ }
+
+ case Long_/ =>
+ (lhs, rhs) match {
+ case (_, LongLiteral(0)) => default
+ case (LongLiteral(l), LongLiteral(r)) => LongLiteral(l / r)
+
+ case (_, LongLiteral(1)) => lhs
+ case (_, LongLiteral(-1)) => foldBinaryOp(Long_-, LongLiteral(0), lhs)
+
+ case (LongFromInt(x), LongFromInt(y: IntLiteral)) if y.value != -1 =>
+ LongFromInt(foldBinaryOp(Int_/, x, y))
+
+ case _ => default
+ }
+
+ case Long_% =>
+ (lhs, rhs) match {
+ case (_, LongLiteral(0)) => default
+ case (LongLiteral(l), LongLiteral(r)) => LongLiteral(l % r)
+
+ case (_, LongLiteral(1L | -1L)) =>
+ Block(keepOnlySideEffects(lhs), LongLiteral(0L))
+
+ case (LongFromInt(x), LongFromInt(y)) =>
+ LongFromInt(foldBinaryOp(Int_%, x, y))
+
+ case _ => default
+ }
+
+ case Long_| =>
+ (lhs, rhs) match {
+ case (LongLiteral(l), LongLiteral(r)) => LongLiteral(l | r)
+ case (_, LongLiteral(_)) => foldBinaryOp(Long_|, rhs, lhs)
+ case (LongLiteral(0), _) => rhs
+
+ case (LongLiteral(x), BinaryOp(Long_|, LongLiteral(y), z)) =>
+ foldBinaryOp(Long_|, LongLiteral(x | y), z)
+
+ case _ => default
+ }
+
+ case Long_& =>
+ (lhs, rhs) match {
+ case (LongLiteral(l), LongLiteral(r)) => LongLiteral(l & r)
+ case (_, LongLiteral(_)) => foldBinaryOp(Long_&, rhs, lhs)
+ case (LongLiteral(-1), _) => rhs
+
+ case (LongLiteral(x), BinaryOp(Long_&, LongLiteral(y), z)) =>
+ foldBinaryOp(Long_&, LongLiteral(x & y), z)
+
+ case _ => default
+ }
+
+ case Long_^ =>
+ (lhs, rhs) match {
+ case (LongLiteral(l), LongLiteral(r)) => LongLiteral(l ^ r)
+ case (_, LongLiteral(_)) => foldBinaryOp(Long_^, rhs, lhs)
+ case (LongLiteral(0), _) => rhs
+
+ case (LongLiteral(x), BinaryOp(Long_^, LongLiteral(y), z)) =>
+ foldBinaryOp(Long_^, LongLiteral(x ^ y), z)
+
+ case _ => default
+ }
+
+ case Long_<< =>
+ (lhs, rhs) match {
+ case (LongLiteral(l), IntLiteral(r)) => LongLiteral(l << r)
+ case (_, IntLiteral(x)) if x % 64 == 0 => lhs
+ case _ => default
+ }
+
+ case Long_>>> =>
+ (lhs, rhs) match {
+ case (LongLiteral(l), IntLiteral(r)) => LongLiteral(l >>> r)
+ case (_, IntLiteral(x)) if x % 64 == 0 => lhs
+ case _ => default
+ }
+
+ case Long_>> =>
+ (lhs, rhs) match {
+ case (LongLiteral(l), IntLiteral(r)) => LongLiteral(l >> r)
+ case (_, IntLiteral(x)) if x % 64 == 0 => lhs
+ case _ => default
+ }
+
+ case Long_== | Long_!= =>
+ val positive = (op == Long_==)
+ (lhs, rhs) match {
+ case (LongLiteral(l), LongLiteral(r)) =>
+ BooleanLiteral((l == r) == positive)
+
+ case (LongFromInt(x), LongFromInt(y)) =>
+ foldBinaryOp(if (positive) === else !==, x, y)
+ case (LongFromInt(x), LongLiteral(y)) =>
+ assert(y > Int.MaxValue || y < Int.MinValue)
+ Block(keepOnlySideEffects(x), BooleanLiteral(!positive))
+
+ case (BinaryOp(Long_+, LongLiteral(x), y), LongLiteral(z)) =>
+ foldBinaryOp(op, y, LongLiteral(z-x))
+ case (BinaryOp(Long_-, LongLiteral(x), y), LongLiteral(z)) =>
+ foldBinaryOp(op, y, LongLiteral(x-z))
+
+ case (LongLiteral(_), _) => foldBinaryOp(op, rhs, lhs)
+ case _ => default
+ }
+
+ case Long_< | Long_<= | Long_> | Long_>= =>
+ def flippedOp = (op: @switch) match {
+ case Long_< => Long_>
+ case Long_<= => Long_>=
+ case Long_> => Long_<
+ case Long_>= => Long_<=
+ }
+
+ def intOp = (op: @switch) match {
+ case Long_< => Num_<
+ case Long_<= => Num_<=
+ case Long_> => Num_>
+ case Long_>= => Num_>=
+ }
+
+ (lhs, rhs) match {
+ case (LongLiteral(l), LongLiteral(r)) =>
+ val result = (op: @switch) match {
+ case Long_< => l < r
+ case Long_<= => l <= r
+ case Long_> => l > r
+ case Long_>= => l >= r
+ }
+ BooleanLiteral(result)
+
+ case (_, LongLiteral(Long.MinValue)) =>
+ if (op == Long_< || op == Long_>=)
+ Block(keepOnlySideEffects(lhs), BooleanLiteral(op == Long_>=))
+ else
+ foldBinaryOp(if (op == Long_<=) Long_== else Long_!=, lhs, rhs)
+
+ case (_, LongLiteral(Long.MaxValue)) =>
+ if (op == Long_> || op == Long_<=)
+ Block(keepOnlySideEffects(lhs), BooleanLiteral(op == Long_<=))
+ else
+ foldBinaryOp(if (op == Long_>=) Long_== else Long_!=, lhs, rhs)
+
+ case (LongFromInt(x), LongFromInt(y)) =>
+ foldBinaryOp(intOp, x, y)
+ case (LongFromInt(x), LongLiteral(y)) =>
+ assert(y > Int.MaxValue || y < Int.MinValue)
+ val result =
+ if (y > Int.MaxValue) op == Long_< || op == Long_<=
+ else op == Long_> || op == Long_>=
+ Block(keepOnlySideEffects(x), BooleanLiteral(result))
+
+ /* x + y.toLong > z
+ * -x on both sides
+ * requires x + y.toLong not to overflow, and z - x likewise
+ * y.toLong > z - x
+ */
+ case (BinaryOp(Long_+, LongLiteral(x), y @ LongFromInt(_)), LongLiteral(z))
+ if canAddLongs(x, Int.MinValue) &&
+ canAddLongs(x, Int.MaxValue) &&
+ canSubtractLongs(z, x) =>
+ foldBinaryOp(op, y, LongLiteral(z-x))
+
+ /* x - y.toLong > z
+ * -x on both sides
+ * requires x - y.toLong not to overflow, and z - x likewise
+ * -(y.toLong) > z - x
+ */
+ case (BinaryOp(Long_-, LongLiteral(x), y @ LongFromInt(_)), LongLiteral(z))
+ if canSubtractLongs(x, Int.MinValue) &&
+ canSubtractLongs(x, Int.MaxValue) &&
+ canSubtractLongs(z, x) =>
+ if (z-x != Long.MinValue) {
+ // Since -(y.toLong) does not overflow, we can negate both sides
+ foldBinaryOp(flippedOp, y, LongLiteral(-(z-x)))
+ } else {
+ /* -(y.toLong) > Long.MinValue
+ * Depending on the operator, this is either always true or
+ * always false.
+ */
+ val result = (op == Long_>) || (op == Long_>=)
+ Block(keepOnlySideEffects(y), BooleanLiteral(result))
+ }
+
+ /* x.toLong + y.toLong > Int.MaxValue.toLong
+ *
+ * This is basically testing whether x+y overflows in positive.
+ * If x <= 0 or y <= 0, this cannot happen -> false.
+ * If x > 0 and y > 0, this can be detected with x+y < 0.
+ * Therefore, we rewrite as:
+ *
+ * x > 0 && y > 0 && x+y < 0.
+ *
+ * This requires to evaluate x and y once.
+ */
+ case (BinaryOp(Long_+, LongFromInt(x), LongFromInt(y)),
+ LongLiteral(Int.MaxValue)) =>
+ trampoline {
+ withNewLocalDefs(List(
+ Binding("x", None, IntType, false, PreTransTree(x)),
+ Binding("y", None, IntType, false, PreTransTree(y)))) {
+ (tempsLocalDefs, cont) =>
+ val List(tempXDef, tempYDef) = tempsLocalDefs
+ val tempX = tempXDef.newReplacement
+ val tempY = tempYDef.newReplacement
+ cont(PreTransTree(
+ AndThen(AndThen(
+ BinaryOp(Num_>, tempX, IntLiteral(0)),
+ BinaryOp(Num_>, tempY, IntLiteral(0))),
+ BinaryOp(Num_<, BinaryOp(Int_+, tempX, tempY), IntLiteral(0)))))
+ } (finishTransform(isStat = false))
+ }
+
+ case (LongLiteral(_), _) => foldBinaryOp(flippedOp, rhs, lhs)
+ case _ => default
+ }
+
+ case Float_+ =>
+ (lhs, rhs) match {
+ case (FloatLiteral(l), FloatLiteral(r)) => FloatLiteral(l + r)
+ case (FloatLiteral(0), _) => rhs
+ case (_, FloatLiteral(_)) => foldBinaryOp(Float_+, rhs, lhs)
+
+ case (FloatLiteral(x),
+ BinaryOp(innerOp @ (Float_+ | Float_-), FloatLiteral(y), z)) =>
+ foldBinaryOp(innerOp, FloatLiteral(x+y), z)
+
+ case _ => default
+ }
+
+ case Float_- =>
+ (lhs, rhs) match {
+ case (_, FloatLiteral(r)) => foldBinaryOp(Float_+, lhs, FloatLiteral(-r))
+
+ case (FloatLiteral(x), BinaryOp(Float_+, FloatLiteral(y), z)) =>
+ foldBinaryOp(Float_-, FloatLiteral(x-y), z)
+ case (FloatLiteral(x), BinaryOp(Float_-, FloatLiteral(y), z)) =>
+ foldBinaryOp(Float_+, FloatLiteral(x-y), z)
+
+ case (_, BinaryOp(BinaryOp.Float_-, FloatLiteral(0), x)) =>
+ foldBinaryOp(Float_+, lhs, x)
+
+ case _ => default
+ }
+
+ case Float_* =>
+ (lhs, rhs) match {
+ case (FloatLiteral(l), FloatLiteral(r)) => FloatLiteral(l * r)
+ case (_, FloatLiteral(_)) => foldBinaryOp(Float_*, rhs, lhs)
+
+ case (FloatLiteral(1), _) => rhs
+ case (FloatLiteral(-1), _) => foldBinaryOp(Float_-, FloatLiteral(0), lhs)
+
+ case _ => default
+ }
+
+ case Float_/ =>
+ (lhs, rhs) match {
+ case (FloatLiteral(l), FloatLiteral(r)) => FloatLiteral(l / r)
+
+ case (_, FloatLiteral(1)) => lhs
+ case (_, FloatLiteral(-1)) => foldBinaryOp(Float_-, FloatLiteral(0), lhs)
+
+ case _ => default
+ }
+
+ case Float_% =>
+ (lhs, rhs) match {
+ case (FloatLiteral(l), FloatLiteral(r)) => FloatLiteral(l % r)
+ case _ => default
+ }
+
+ case Double_+ =>
+ (lhs, rhs) match {
+ case (NumberLiteral(l), NumberLiteral(r)) => DoubleLiteral(l + r)
+ case (NumberLiteral(0), _) => rhs
+ case (_, NumberLiteral(_)) => foldBinaryOp(Double_+, rhs, lhs)
+
+ case (NumberLiteral(x),
+ BinaryOp(innerOp @ (Double_+ | Double_-), NumberLiteral(y), z)) =>
+ foldBinaryOp(innerOp, DoubleLiteral(x+y), z)
+
+ case _ => default
+ }
+
+ case Double_- =>
+ (lhs, rhs) match {
+ case (_, NumberLiteral(r)) => foldBinaryOp(Double_+, lhs, DoubleLiteral(-r))
+
+ case (NumberLiteral(x), BinaryOp(Double_+, NumberLiteral(y), z)) =>
+ foldBinaryOp(Double_-, DoubleLiteral(x-y), z)
+ case (NumberLiteral(x), BinaryOp(Double_-, NumberLiteral(y), z)) =>
+ foldBinaryOp(Double_+, DoubleLiteral(x-y), z)
+
+ case (_, BinaryOp(BinaryOp.Double_-, NumberLiteral(0), x)) =>
+ foldBinaryOp(Double_+, lhs, x)
+
+ case _ => default
+ }
+
+ case Double_* =>
+ (lhs, rhs) match {
+ case (NumberLiteral(l), NumberLiteral(r)) => DoubleLiteral(l * r)
+ case (_, NumberLiteral(_)) => foldBinaryOp(Double_*, rhs, lhs)
+
+ case (NumberLiteral(1), _) => rhs
+ case (NumberLiteral(-1), _) => foldBinaryOp(Double_-, DoubleLiteral(0), lhs)
+
+ case _ => default
+ }
+
+ case Double_/ =>
+ (lhs, rhs) match {
+ case (NumberLiteral(l), NumberLiteral(r)) => DoubleLiteral(l / r)
+
+ case (_, NumberLiteral(1)) => lhs
+ case (_, NumberLiteral(-1)) => foldBinaryOp(Double_-, DoubleLiteral(0), lhs)
+
+ case _ => default
+ }
+
+ case Double_% =>
+ (lhs, rhs) match {
+ case (NumberLiteral(l), NumberLiteral(r)) => DoubleLiteral(l % r)
+ case _ => default
+ }
+
+ case Boolean_== | Boolean_!= =>
+ val positive = (op == Boolean_==)
+ (lhs, rhs) match {
+ case (BooleanLiteral(l), _) =>
+ if (l == positive) rhs
+ else foldUnaryOp(UnaryOp.Boolean_!, rhs)
+ case (_, BooleanLiteral(r)) =>
+ if (r == positive) lhs
+ else foldUnaryOp(UnaryOp.Boolean_!, lhs)
+ case _ =>
+ default
+ }
+
+ case Boolean_| =>
+ (lhs, rhs) match {
+ case (_, BooleanLiteral(false)) => lhs
+ case (BooleanLiteral(false), _) => rhs
+ case _ => default
+ }
+
+ case Boolean_& =>
+ (lhs, rhs) match {
+ case (_, BooleanLiteral(true)) => lhs
+ case (BooleanLiteral(true), _) => rhs
+ case _ => default
+ }
+
+ case Num_== | Num_!= =>
+ val positive = (op == Num_==)
+ (lhs, rhs) match {
+ case (lhs: Literal, rhs: Literal) =>
+ BooleanLiteral(literal_===(lhs, rhs) == positive)
+
+ case (BinaryOp(Int_+, IntLiteral(x), y), IntLiteral(z)) =>
+ foldBinaryOp(op, y, IntLiteral(z-x))
+ case (BinaryOp(Int_-, IntLiteral(x), y), IntLiteral(z)) =>
+ foldBinaryOp(op, y, IntLiteral(x-z))
+
+ case (_: Literal, _) => foldBinaryOp(op, rhs, lhs)
+ case _ => default
+ }
+
+ case Num_< | Num_<= | Num_> | Num_>= =>
+ def flippedOp = (op: @switch) match {
+ case Num_< => Num_>
+ case Num_<= => Num_>=
+ case Num_> => Num_<
+ case Num_>= => Num_<=
+ }
+
+ if (lhs.tpe == IntType && rhs.tpe == IntType) {
+ (lhs, rhs) match {
+ case (IntLiteral(l), IntLiteral(r)) =>
+ val result = (op: @switch) match {
+ case Num_< => l < r
+ case Num_<= => l <= r
+ case Num_> => l > r
+ case Num_>= => l >= r
+ }
+ BooleanLiteral(result)
+
+ case (_, IntLiteral(Int.MinValue)) =>
+ if (op == Num_< || op == Num_>=)
+ Block(keepOnlySideEffects(lhs), BooleanLiteral(op == Num_>=))
+ else
+ foldBinaryOp(if (op == Num_<=) Num_== else Num_!=, lhs, rhs)
+
+ case (_, IntLiteral(Int.MaxValue)) =>
+ if (op == Num_> || op == Num_<=)
+ Block(keepOnlySideEffects(lhs), BooleanLiteral(op == Num_<=))
+ else
+ foldBinaryOp(if (op == Num_>=) Num_== else Num_!=, lhs, rhs)
+
+ case (IntLiteral(_), _) => foldBinaryOp(flippedOp, rhs, lhs)
+ case _ => default
+ }
+ } else {
+ (lhs, rhs) match {
+ case (NumberLiteral(l), NumberLiteral(r)) =>
+ val result = (op: @switch) match {
+ case Num_< => l < r
+ case Num_<= => l <= r
+ case Num_> => l > r
+ case Num_>= => l >= r
+ }
+ BooleanLiteral(result)
+
+ case _ => default
+ }
+ }
+
+ case _ =>
+ default
+ }
+ }
+
+ private def fold3WayComparison(canBeEqual: Boolean, canBeLessThan: Boolean,
+ canBeGreaterThan: Boolean, lhs: Tree, rhs: Tree)(
+ implicit pos: Position): Tree = {
+ import BinaryOp._
+ if (canBeEqual) {
+ if (canBeLessThan) {
+ if (canBeGreaterThan)
+ Block(keepOnlySideEffects(lhs), keepOnlySideEffects(rhs), BooleanLiteral(true))
+ else
+ foldBinaryOp(Num_<=, lhs, rhs)
+ } else {
+ if (canBeGreaterThan)
+ foldBinaryOp(Num_>=, lhs, rhs)
+ else
+ foldBinaryOp(Num_==, lhs, rhs)
+ }
+ } else {
+ if (canBeLessThan) {
+ if (canBeGreaterThan)
+ foldBinaryOp(Num_!=, lhs, rhs)
+ else
+ foldBinaryOp(Num_<, lhs, rhs)
+ } else {
+ if (canBeGreaterThan)
+ foldBinaryOp(Num_>, lhs, rhs)
+ else
+ Block(keepOnlySideEffects(lhs), keepOnlySideEffects(rhs), BooleanLiteral(false))
+ }
+ }
+ }
+
+ private def foldUnbox(arg: PreTransform, charCode: Char)(
+ cont: PreTransCont): TailRec[Tree] = {
+ (charCode: @switch) match {
+ case 'Z' if arg.tpe.base == BooleanType => cont(arg)
+ case 'I' if arg.tpe.base == IntType => cont(arg)
+ case 'F' if arg.tpe.base == FloatType => cont(arg)
+ case 'J' if arg.tpe.base == LongType => cont(arg)
+ case 'D' if arg.tpe.base == DoubleType ||
+ arg.tpe.base == IntType || arg.tpe.base == FloatType => cont(arg)
+ case _ =>
+ cont(PreTransTree(Unbox(finishTransformExpr(arg), charCode)(arg.pos)))
+ }
+ }
+
+ private def foldReferenceEquality(tlhs: PreTransform, trhs: PreTransform,
+ positive: Boolean = true)(implicit pos: Position): Tree = {
+ (tlhs, trhs) match {
+ case (_, PreTransTree(Null(), _)) if !tlhs.tpe.isNullable =>
+ Block(
+ finishTransformStat(tlhs),
+ BooleanLiteral(!positive))
+ case (PreTransTree(Null(), _), _) if !trhs.tpe.isNullable =>
+ Block(
+ finishTransformStat(trhs),
+ BooleanLiteral(!positive))
+ case _ =>
+ foldBinaryOp(if (positive) BinaryOp.=== else BinaryOp.!==,
+ finishTransformExpr(tlhs), finishTransformExpr(trhs))
+ }
+ }
+
+ private def finishTransformCheckNull(preTrans: PreTransform)(
+ implicit pos: Position): Tree = {
+ if (preTrans.tpe.isNullable) {
+ val transformed = finishTransformExpr(preTrans)
+ CallHelper("checkNonNull", transformed)(transformed.tpe)
+ } else {
+ finishTransformExpr(preTrans)
+ }
+ }
+
+ def transformIsolatedBody(optTarget: Option[MethodID],
+ thisType: Type, params: List[ParamDef], resultType: Type,
+ body: Tree): (List[ParamDef], Tree) = {
+ val (paramLocalDefs, newParamDefs) = (for {
+ p @ ParamDef(ident @ Ident(name, originalName), ptpe, mutable) <- params
+ } yield {
+ val newName = freshLocalName(name)
+ val newOriginalName = originalName.orElse(Some(newName))
+ val localDef = LocalDef(RefinedType(ptpe), mutable,
+ ReplaceWithVarRef(newName, newOriginalName, new SimpleState(true), None))
+ val newParamDef = ParamDef(
+ Ident(newName, newOriginalName)(ident.pos), ptpe, mutable)(p.pos)
+ ((name -> localDef), newParamDef)
+ }).unzip
+
+ val thisLocalDef =
+ if (thisType == NoType) None
+ else {
+ Some("this" -> LocalDef(
+ RefinedType(thisType, isExact = false, isNullable = false),
+ false, ReplaceWithThis()))
+ }
+
+ val allLocalDefs = thisLocalDef ++: paramLocalDefs
+
+ val scope0 = optTarget.fold(Scope.Empty)(
+ target => Scope.Empty.inlining((None, target)))
+ val scope = scope0.withEnv(OptEnv.Empty.withLocalDefs(allLocalDefs))
+ val newBody =
+ transform(body, resultType == NoType)(scope)
+
+ (newParamDefs, newBody)
+ }
+
+ private def returnable(oldLabelName: String, resultType: Type,
+ body: Tree, isStat: Boolean, usePreTransform: Boolean)(
+ cont: PreTransCont)(
+ implicit scope: Scope, pos: Position): TailRec[Tree] = tailcall {
+ val newLabel = freshLabelName(
+ if (oldLabelName.isEmpty) "inlinereturn" else oldLabelName)
+
+ def doMakeTree(newBody: Tree, returnedTypes: List[Type]): Tree = {
+ val refinedType =
+ returnedTypes.reduce(constrainedLub(_, _, resultType))
+ val returnCount = returnedTypes.size - 1
+
+ tryOptimizePatternMatch(oldLabelName, refinedType,
+ returnCount, newBody) getOrElse {
+ Labeled(Ident(newLabel, None), refinedType, newBody)
+ }
+ }
+
+ val info = new LabelInfo(newLabel, acceptRecords = usePreTransform)
+ withState(info.returnedTypes) {
+ val bodyScope = scope.withEnv(scope.env.withLabelInfo(oldLabelName, info))
+
+ if (usePreTransform) {
+ assert(!isStat, "Cannot use pretransform in statement position")
+ tryOrRollback { cancelFun =>
+ pretransformExpr(body) { tbody0 =>
+ val returnedTypes0 = info.returnedTypes.value
+ if (returnedTypes0.isEmpty) {
+ // no return to that label, we can eliminate it
+ cont(tbody0)
+ } else {
+ val tbody = resolveLocalDef(tbody0)
+ val (newBody, returnedTypes) = tbody match {
+ case PreTransRecordTree(bodyTree, origType, _) =>
+ (bodyTree, (bodyTree.tpe, origType) :: returnedTypes0)
+ case PreTransTree(bodyTree, tpe) =>
+ (bodyTree, (bodyTree.tpe, tpe) :: returnedTypes0)
+ }
+ val (actualTypes, origTypes) = returnedTypes.unzip
+ val refinedOrigType =
+ origTypes.reduce(constrainedLub(_, _, resultType))
+ actualTypes.collectFirst {
+ case actualType: RecordType => actualType
+ }.fold[TailRec[Tree]] {
+ // None of the returned types are records
+ cont(PreTransTree(
+ doMakeTree(newBody, actualTypes), refinedOrigType))
+ } { recordType =>
+ if (actualTypes.exists(t => t != recordType && t != NothingType))
+ cancelFun()
+
+ val resultTree = doMakeTree(newBody, actualTypes)
+
+ if (origTypes.exists(t => t != refinedOrigType && !t.isNothingType))
+ cancelFun()
+
+ cont(PreTransRecordTree(resultTree, refinedOrigType, cancelFun))
+ }
+ }
+ } (bodyScope)
+ } { () =>
+ returnable(oldLabelName, resultType, body, isStat,
+ usePreTransform = false)(cont)
+ }
+ } else {
+ val newBody = transform(body, isStat)(bodyScope)
+ val returnedTypes0 = info.returnedTypes.value.map(_._1)
+ if (returnedTypes0.isEmpty) {
+ // no return to that label, we can eliminate it
+ cont(PreTransTree(newBody, RefinedType(newBody.tpe)))
+ } else {
+ val returnedTypes = newBody.tpe :: returnedTypes0
+ val tree = doMakeTree(newBody, returnedTypes)
+ cont(PreTransTree(tree, RefinedType(tree.tpe)))
+ }
+ }
+ }
+ }
+
+ def tryOptimizePatternMatch(oldLabelName: String, refinedType: Type,
+ returnCount: Int, newBody: Tree): Option[Tree] = {
+ if (!oldLabelName.startsWith("matchEnd")) None
+ else {
+ newBody match {
+ case Block(stats) =>
+ @tailrec
+ def createRevAlts(xs: List[Tree], acc: List[(Tree, Tree)]): List[(Tree, Tree)] = xs match {
+ case If(cond, body, Skip()) :: xr =>
+ createRevAlts(xr, (cond, body) :: acc)
+ case remaining =>
+ (EmptyTree, Block(remaining)(remaining.head.pos)) :: acc
+ }
+ val revAlts = createRevAlts(stats, Nil)
+
+ if (revAlts.size == returnCount) {
+ @tailrec
+ def constructOptimized(revAlts: List[(Tree, Tree)], elsep: Tree): Option[Tree] = {
+ revAlts match {
+ case (cond, body) :: revAltsRest =>
+ body match {
+ case BlockOrAlone(prep,
+ Return(result, Some(Ident(newLabel, _)))) =>
+ val result1 =
+ if (refinedType == NoType) keepOnlySideEffects(result)
+ else result
+ val prepAndResult = Block(prep :+ result1)(body.pos)
+ if (cond == EmptyTree) {
+ assert(elsep == EmptyTree)
+ constructOptimized(revAltsRest, prepAndResult)
+ } else {
+ assert(elsep != EmptyTree)
+ constructOptimized(revAltsRest,
+ foldIf(cond, prepAndResult, elsep)(refinedType)(cond.pos))
+ }
+ case _ =>
+ None
+ }
+ case Nil =>
+ Some(elsep)
+ }
+ }
+ constructOptimized(revAlts, EmptyTree)
+ } else None
+ case _ =>
+ None
+ }
+ }
+ }
+
+ private def withBindings(bindings: List[Binding])(
+ buildInner: (Scope, PreTransCont) => TailRec[Tree])(
+ cont: PreTransCont)(
+ implicit scope: Scope): TailRec[Tree] = {
+ withNewLocalDefs(bindings) { (localDefs, cont1) =>
+ val newMappings = for {
+ (binding, localDef) <- bindings zip localDefs
+ } yield {
+ binding.name -> localDef
+ }
+ buildInner(scope.withEnv(scope.env.withLocalDefs(newMappings)), cont1)
+ } (cont)
+ }
+
+ private def withBinding(binding: Binding)(
+ buildInner: (Scope, PreTransCont) => TailRec[Tree])(
+ cont: PreTransCont)(
+ implicit scope: Scope): TailRec[Tree] = {
+ withNewLocalDef(binding) { (localDef, cont1) =>
+ buildInner(scope.withEnv(scope.env.withLocalDef(binding.name, localDef)),
+ cont1)
+ } (cont)
+ }
+
+ private def withNewLocalDefs(bindings: List[Binding])(
+ buildInner: (List[LocalDef], PreTransCont) => TailRec[Tree])(
+ cont: PreTransCont): TailRec[Tree] = {
+ bindings match {
+ case first :: rest =>
+ withNewLocalDef(first) { (firstLocalDef, cont1) =>
+ withNewLocalDefs(rest) { (restLocalDefs, cont2) =>
+ buildInner(firstLocalDef :: restLocalDefs, cont2)
+ } (cont1)
+ } (cont)
+
+ case Nil =>
+ buildInner(Nil, cont)
+ }
+ }
+
+ private def isImmutableType(tpe: Type): Boolean = tpe match {
+ case RecordType(fields) =>
+ fields.forall(f => !f.mutable && isImmutableType(f.tpe))
+ case _ =>
+ true
+ }
+
+ private def withNewLocalDef(binding: Binding)(
+ buildInner: (LocalDef, PreTransCont) => TailRec[Tree])(
+ cont: PreTransCont): TailRec[Tree] = tailcall {
+ val Binding(name, originalName, declaredType, mutable, value) = binding
+ implicit val pos = value.pos
+
+ def withDedicatedVar(tpe: RefinedType): TailRec[Tree] = {
+ val newName = freshLocalName(name)
+ val newOriginalName = originalName.orElse(Some(name))
+
+ val used = new SimpleState(false)
+ withState(used) {
+ def doBuildInner(localDef: LocalDef)(varDef: => VarDef)(
+ cont: PreTransCont): TailRec[Tree] = {
+ buildInner(localDef, { tinner =>
+ if (used.value) {
+ cont(PreTransBlock(varDef :: Nil, tinner))
+ } else {
+ tinner match {
+ case PreTransLocalDef(`localDef`) =>
+ cont(value)
+ case _ if tinner.contains(localDef) =>
+ cont(PreTransBlock(varDef :: Nil, tinner))
+ case _ =>
+ val rhsSideEffects = finishTransformStat(value)
+ rhsSideEffects match {
+ case Skip() =>
+ cont(tinner)
+ case _ =>
+ if (rhsSideEffects.tpe == NothingType)
+ cont(PreTransTree(rhsSideEffects, RefinedType.Nothing))
+ else
+ cont(PreTransBlock(rhsSideEffects :: Nil, tinner))
+ }
+ }
+ }
+ })
+ }
+
+ resolveLocalDef(value) match {
+ case PreTransRecordTree(valueTree, valueTpe, cancelFun) =>
+ val recordType = valueTree.tpe.asInstanceOf[RecordType]
+ if (!isImmutableType(recordType))
+ cancelFun()
+ val localDef = LocalDef(valueTpe, mutable,
+ ReplaceWithRecordVarRef(newName, newOriginalName, recordType,
+ used, cancelFun))
+ doBuildInner(localDef) {
+ VarDef(Ident(newName, newOriginalName), recordType, mutable,
+ valueTree)
+ } (cont)
+
+ case PreTransTree(valueTree, valueTpe) =>
+ def doDoBuildInner(optValueTree: Option[() => Tree])(
+ cont1: PreTransCont) = {
+ val localDef = LocalDef(tpe, mutable, ReplaceWithVarRef(
+ newName, newOriginalName, used, optValueTree))
+ doBuildInner(localDef) {
+ VarDef(Ident(newName, newOriginalName), tpe.base, mutable,
+ optValueTree.fold(valueTree)(_()))
+ } (cont1)
+ }
+ if (mutable) {
+ doDoBuildInner(None)(cont)
+ } else (valueTree match {
+ case LongFromInt(arg) =>
+ withNewLocalDef(
+ Binding("x", None, IntType, false, PreTransTree(arg))) {
+ (intLocalDef, cont1) =>
+ doDoBuildInner(Some(
+ () => LongFromInt(intLocalDef.newReplacement)))(
+ cont1)
+ } (cont)
+
+ case BinaryOp(op @ (BinaryOp.Long_+ | BinaryOp.Long_-),
+ LongFromInt(intLhs), LongFromInt(intRhs)) =>
+ withNewLocalDefs(List(
+ Binding("x", None, IntType, false, PreTransTree(intLhs)),
+ Binding("y", None, IntType, false, PreTransTree(intRhs)))) {
+ (intLocalDefs, cont1) =>
+ val List(lhsLocalDef, rhsLocalDef) = intLocalDefs
+ doDoBuildInner(Some(
+ () => BinaryOp(op,
+ LongFromInt(lhsLocalDef.newReplacement),
+ LongFromInt(rhsLocalDef.newReplacement))))(
+ cont1)
+ } (cont)
+
+ case _ =>
+ doDoBuildInner(None)(cont)
+ })
+ }
+ }
+ }
+
+ if (value.tpe.isNothingType) {
+ cont(value)
+ } else if (mutable) {
+ withDedicatedVar(RefinedType(declaredType))
+ } else {
+ val refinedType = value.tpe
+ value match {
+ case PreTransBlock(stats, result) =>
+ withNewLocalDef(binding.copy(value = result))(buildInner) { tresult =>
+ cont(PreTransBlock(stats, tresult))
+ }
+
+ case PreTransLocalDef(localDef) if !localDef.mutable =>
+ buildInner(localDef, cont)
+
+ case PreTransTree(literal: Literal, _) =>
+ buildInner(LocalDef(refinedType, false,
+ ReplaceWithConstant(literal)), cont)
+
+ case PreTransTree(VarRef(Ident(refName, refOriginalName), false), _) =>
+ buildInner(LocalDef(refinedType, false,
+ ReplaceWithVarRef(refName, refOriginalName,
+ new SimpleState(true), None)), cont)
+
+ case _ =>
+ withDedicatedVar(refinedType)
+ }
+ }
+ }
+
+ /** Finds a type as precise as possible which is a supertype of lhs and rhs
+ * but still a subtype of upperBound.
+ * Requires that lhs and rhs be subtypes of upperBound, obviously.
+ */
+ private def constrainedLub(lhs: RefinedType, rhs: RefinedType,
+ upperBound: Type): RefinedType = {
+ if (upperBound == NoType) RefinedType(upperBound)
+ else if (lhs == rhs) lhs
+ else if (lhs.isNothingType) rhs
+ else if (rhs.isNothingType) lhs
+ else {
+ RefinedType(constrainedLub(lhs.base, rhs.base, upperBound),
+ false, lhs.isNullable || rhs.isNullable)
+ }
+ }
+
+ /** Finds a type as precise as possible which is a supertype of lhs and rhs
+ * but still a subtype of upperBound.
+ * Requires that lhs and rhs be subtypes of upperBound, obviously.
+ */
+ private def constrainedLub(lhs: Type, rhs: Type, upperBound: Type): Type = {
+ // TODO Improve this
+ if (upperBound == NoType) upperBound
+ else if (lhs == rhs) lhs
+ else if (lhs == NothingType) rhs
+ else if (rhs == NothingType) lhs
+ else upperBound
+ }
+
+ /** Trampolines a pretransform */
+ private def trampoline(tailrec: => TailRec[Tree]): Tree = {
+ curTrampolineId += 1
+
+ val myTrampolineId = curTrampolineId
+
+ try {
+ var rec = () => tailrec
+
+ while (true) {
+ try {
+ return rec().result
+ } catch {
+ case e: RollbackException if e.trampolineId == myTrampolineId =>
+ rollbacksCount += 1
+ if (rollbacksCount > MaxRollbacksPerMethod)
+ throw new TooManyRollbacksException
+
+ usedLocalNames.clear()
+ usedLocalNames ++= e.savedUsedLocalNames
+ usedLabelNames.clear()
+ usedLabelNames ++= e.savedUsedLabelNames
+ for ((state, backup) <- statesInUse zip e.savedStates)
+ state.asInstanceOf[State[Any]].restore(backup)
+
+ rec = e.cont
+ }
+ }
+
+ sys.error("Reached end of infinite loop")
+ } finally {
+ curTrampolineId -= 1
+ }
+ }
+}
+
+private[optimizer] object OptimizerCore {
+
+ private final val MaxRollbacksPerMethod = 256
+
+ private final class TooManyRollbacksException
+ extends scala.util.control.ControlThrowable
+
+ private val AnonFunctionClassPrefix = "sjsr_AnonFunction"
+
+ private type CancelFun = () => Nothing
+ private type PreTransCont = PreTransform => TailRec[Tree]
+
+ private case class RefinedType private (base: Type, isExact: Boolean,
+ isNullable: Boolean)(
+ val allocationSite: Option[AllocationSite], dummy: Int = 0) {
+
+ def isNothingType: Boolean = base == NothingType
+ }
+
+ private object RefinedType {
+ def apply(base: Type, isExact: Boolean, isNullable: Boolean,
+ allocationSite: Option[AllocationSite]): RefinedType =
+ new RefinedType(base, isExact, isNullable)(allocationSite)
+
+ def apply(base: Type, isExact: Boolean, isNullable: Boolean): RefinedType =
+ RefinedType(base, isExact, isNullable, None)
+
+ def apply(tpe: Type): RefinedType = tpe match {
+ case BooleanType | IntType | FloatType | DoubleType | StringType |
+ UndefType | NothingType | _:RecordType | NoType =>
+ RefinedType(tpe, isExact = true, isNullable = false)
+ case NullType =>
+ RefinedType(tpe, isExact = true, isNullable = true)
+ case _ =>
+ RefinedType(tpe, isExact = false, isNullable = true)
+ }
+
+ val NoRefinedType = RefinedType(NoType)
+ val Nothing = RefinedType(NothingType)
+ }
+
+ private class AllocationSite(private val node: Tree) {
+ override def equals(that: Any): Boolean = that match {
+ case that: AllocationSite => this.node eq that.node
+ case _ => false
+ }
+
+ override def hashCode(): Int =
+ System.identityHashCode(node)
+
+ override def toString(): String =
+ s"AllocationSite($node)"
+ }
+
+ private case class LocalDef(
+ tpe: RefinedType,
+ mutable: Boolean,
+ replacement: LocalDefReplacement) {
+
+ def newReplacement(implicit pos: Position): Tree = replacement match {
+ case ReplaceWithVarRef(name, originalName, used, _) =>
+ used.value = true
+ VarRef(Ident(name, originalName), mutable)(tpe.base)
+
+ case ReplaceWithRecordVarRef(_, _, _, _, cancelFun) =>
+ cancelFun()
+
+ case ReplaceWithThis() =>
+ This()(tpe.base)
+
+ case ReplaceWithConstant(value) =>
+ value
+
+ case TentativeClosureReplacement(_, _, _, _, _, cancelFun) =>
+ cancelFun()
+
+ case InlineClassBeingConstructedReplacement(_, cancelFun) =>
+ cancelFun()
+
+ case InlineClassInstanceReplacement(_, _, cancelFun) =>
+ cancelFun()
+ }
+
+ def contains(that: LocalDef): Boolean = {
+ (this eq that) || (replacement match {
+ case TentativeClosureReplacement(_, _, _, captureLocalDefs, _, _) =>
+ captureLocalDefs.exists(_.contains(that))
+ case InlineClassInstanceReplacement(_, fieldLocalDefs, _) =>
+ fieldLocalDefs.valuesIterator.exists(_.contains(that))
+ case _ =>
+ false
+ })
+ }
+ }
+
+ private sealed abstract class LocalDefReplacement
+
+ private final case class ReplaceWithVarRef(name: String,
+ originalName: Option[String],
+ used: SimpleState[Boolean],
+ longOpTree: Option[() => Tree]) extends LocalDefReplacement
+
+ private final case class ReplaceWithRecordVarRef(name: String,
+ originalName: Option[String],
+ recordType: RecordType,
+ used: SimpleState[Boolean],
+ cancelFun: CancelFun) extends LocalDefReplacement
+
+ private final case class ReplaceWithThis() extends LocalDefReplacement
+
+ private final case class ReplaceWithConstant(
+ value: Tree) extends LocalDefReplacement
+
+ private final case class TentativeClosureReplacement(
+ captureParams: List[ParamDef], params: List[ParamDef], body: Tree,
+ captureValues: List[LocalDef],
+ alreadyUsed: SimpleState[Boolean],
+ cancelFun: CancelFun) extends LocalDefReplacement
+
+ private final case class InlineClassBeingConstructedReplacement(
+ fieldLocalDefs: Map[String, LocalDef],
+ cancelFun: CancelFun) extends LocalDefReplacement
+
+ private final case class InlineClassInstanceReplacement(
+ recordType: RecordType,
+ fieldLocalDefs: Map[String, LocalDef],
+ cancelFun: CancelFun) extends LocalDefReplacement
+
+ private final class LabelInfo(
+ val newName: String,
+ val acceptRecords: Boolean,
+ /** (actualType, originalType), actualType can be a RecordType. */
+ val returnedTypes: SimpleState[List[(Type, RefinedType)]] = new SimpleState(Nil))
+
+ private class OptEnv(
+ val localDefs: Map[String, LocalDef],
+ val labelInfos: Map[String, LabelInfo]) {
+
+ def withLocalDef(oldName: String, rep: LocalDef): OptEnv =
+ new OptEnv(localDefs + (oldName -> rep), labelInfos)
+
+ def withLocalDefs(reps: List[(String, LocalDef)]): OptEnv =
+ new OptEnv(localDefs ++ reps, labelInfos)
+
+ def withLabelInfo(oldName: String, info: LabelInfo): OptEnv =
+ new OptEnv(localDefs, labelInfos + (oldName -> info))
+
+ def withinFunction(paramLocalDefs: List[(String, LocalDef)]): OptEnv =
+ new OptEnv(localDefs ++ paramLocalDefs, Map.empty)
+
+ override def toString(): String = {
+ "localDefs:"+localDefs.mkString("\n ", "\n ", "\n") +
+ "labelInfos:"+labelInfos.mkString("\n ", "\n ", "")
+ }
+ }
+
+ private object OptEnv {
+ val Empty: OptEnv = new OptEnv(Map.empty, Map.empty)
+ }
+
+ private class Scope(val env: OptEnv,
+ val implsBeingInlined: Set[(Option[AllocationSite], AbstractMethodID)]) {
+ def withEnv(env: OptEnv): Scope =
+ new Scope(env, implsBeingInlined)
+
+ def inlining(impl: (Option[AllocationSite], AbstractMethodID)): Scope = {
+ assert(!implsBeingInlined(impl), s"Circular inlining of $impl")
+ new Scope(env, implsBeingInlined + impl)
+ }
+ }
+
+ private object Scope {
+ val Empty: Scope = new Scope(OptEnv.Empty, Set.empty)
+ }
+
+ /** The result of pretransformExpr().
+ * It has a `tpe` as precisely refined as if a full transformExpr() had
+ * been performed.
+ * It is also not dependent on the environment anymore. In some sense, it
+ * has "captured" its environment at definition site.
+ */
+ private sealed abstract class PreTransform {
+ def pos: Position
+ val tpe: RefinedType
+
+ def contains(localDef: LocalDef): Boolean = this match {
+ case PreTransBlock(_, result) =>
+ result.contains(localDef)
+ case PreTransLocalDef(thisLocalDef) =>
+ thisLocalDef.contains(localDef)
+ case _ =>
+ false
+ }
+ }
+
+ private final class PreTransBlock private (val stats: List[Tree],
+ val result: PreTransLocalDef) extends PreTransform {
+ def pos = result.pos
+ val tpe = result.tpe
+
+ assert(stats.nonEmpty)
+
+ override def toString(): String =
+ s"PreTransBlock($stats,$result)"
+ }
+
+ private object PreTransBlock {
+ def apply(stats: List[Tree], result: PreTransform): PreTransform = {
+ if (stats.isEmpty) result
+ else {
+ result match {
+ case PreTransBlock(innerStats, innerResult) =>
+ new PreTransBlock(stats ++ innerStats, innerResult)
+ case result: PreTransLocalDef =>
+ new PreTransBlock(stats, result)
+ case PreTransRecordTree(tree, tpe, cancelFun) =>
+ PreTransRecordTree(Block(stats :+ tree)(tree.pos), tpe, cancelFun)
+ case PreTransTree(tree, tpe) =>
+ PreTransTree(Block(stats :+ tree)(tree.pos), tpe)
+ }
+ }
+ }
+
+ def unapply(preTrans: PreTransBlock): Some[(List[Tree], PreTransLocalDef)] =
+ Some(preTrans.stats, preTrans.result)
+ }
+
+ private sealed abstract class PreTransNoBlock extends PreTransform
+
+ private final case class PreTransLocalDef(localDef: LocalDef)(
+ implicit val pos: Position) extends PreTransNoBlock {
+ val tpe: RefinedType = localDef.tpe
+ }
+
+ private sealed abstract class PreTransGenTree extends PreTransNoBlock
+
+ private final case class PreTransRecordTree(tree: Tree,
+ tpe: RefinedType, cancelFun: CancelFun) extends PreTransGenTree {
+ def pos = tree.pos
+
+ assert(tree.tpe.isInstanceOf[RecordType],
+ s"Cannot create a PreTransRecordTree with non-record type ${tree.tpe}")
+ }
+
+ private final case class PreTransTree(tree: Tree,
+ tpe: RefinedType) extends PreTransGenTree {
+ def pos: Position = tree.pos
+
+ assert(!tree.tpe.isInstanceOf[RecordType],
+ s"Cannot create a Tree with record type ${tree.tpe}")
+ }
+
+ private object PreTransTree {
+ def apply(tree: Tree): PreTransTree =
+ PreTransTree(tree, RefinedType(tree.tpe))
+ }
+
+ private final case class Binding(name: String, originalName: Option[String],
+ declaredType: Type, mutable: Boolean, value: PreTransform)
+
+ private object NumberLiteral {
+ def unapply(tree: Literal): Option[Double] = tree match {
+ case DoubleLiteral(v) => Some(v)
+ case IntLiteral(v) => Some(v.toDouble)
+ case FloatLiteral(v) => Some(v.toDouble)
+ case _ => None
+ }
+ }
+
+ private object LongFromInt {
+ def apply(x: Tree)(implicit pos: Position): Tree = x match {
+ case IntLiteral(v) => LongLiteral(v)
+ case _ => UnaryOp(UnaryOp.IntToLong, x)
+ }
+
+ def unapply(tree: Tree): Option[Tree] = tree match {
+ case LongLiteral(v) if v.toInt == v => Some(IntLiteral(v.toInt)(tree.pos))
+ case UnaryOp(UnaryOp.IntToLong, x) => Some(x)
+ case _ => None
+ }
+ }
+
+ private object AndThen {
+ def apply(lhs: Tree, rhs: Tree)(implicit pos: Position): Tree =
+ If(lhs, rhs, BooleanLiteral(false))(BooleanType)
+ }
+
+ /** Tests whether `x + y` is valid without falling out of range. */
+ private def canAddLongs(x: Long, y: Long): Boolean =
+ if (y >= 0) x+y >= x
+ else x+y < x
+
+ /** Tests whether `x - y` is valid without falling out of range. */
+ private def canSubtractLongs(x: Long, y: Long): Boolean =
+ if (y >= 0) x-y <= x
+ else x-y > x
+
+ /** Tests whether `-x` is valid without falling out of range. */
+ private def canNegateLong(x: Long): Boolean =
+ x != Long.MinValue
+
+ private object Intrinsics {
+ final val ArrayCopy = 1
+ final val IdentityHashCode = ArrayCopy + 1
+
+ final val PropertiesOf = IdentityHashCode + 1
+
+ final val LongToString = PropertiesOf + 1
+ final val LongCompare = LongToString + 1
+ final val LongBitCount = LongCompare + 1
+ final val LongSignum = LongBitCount + 1
+ final val LongLeading0s = LongSignum + 1
+ final val LongTrailing0s = LongLeading0s + 1
+ final val LongToBinStr = LongTrailing0s + 1
+ final val LongToHexStr = LongToBinStr + 1
+ final val LongToOctalStr = LongToHexStr + 1
+
+ final val ByteArrayToInt8Array = LongToOctalStr + 1
+ final val ShortArrayToInt16Array = ByteArrayToInt8Array + 1
+ final val CharArrayToUint16Array = ShortArrayToInt16Array + 1
+ final val IntArrayToInt32Array = CharArrayToUint16Array + 1
+ final val FloatArrayToFloat32Array = IntArrayToInt32Array + 1
+ final val DoubleArrayToFloat64Array = FloatArrayToFloat32Array + 1
+
+ final val Int8ArrayToByteArray = DoubleArrayToFloat64Array + 1
+ final val Int16ArrayToShortArray = Int8ArrayToByteArray + 1
+ final val Uint16ArrayToCharArray = Int16ArrayToShortArray + 1
+ final val Int32ArrayToIntArray = Uint16ArrayToCharArray + 1
+ final val Float32ArrayToFloatArray = Int32ArrayToIntArray + 1
+ final val Float64ArrayToDoubleArray = Float32ArrayToFloatArray + 1
+
+ val intrinsics: Map[String, Int] = Map(
+ "jl_System$.arraycopy__O__I__O__I__I__V" -> ArrayCopy,
+ "jl_System$.identityHashCode__O__I" -> IdentityHashCode,
+
+ "sjsr_package$.propertiesOf__sjs_js_Any__sjs_js_Array" -> PropertiesOf,
+
+ "jl_Long$.toString__J__T" -> LongToString,
+ "jl_Long$.compare__J__J__I" -> LongCompare,
+ "jl_Long$.bitCount__J__I" -> LongBitCount,
+ "jl_Long$.signum__J__J" -> LongSignum,
+ "jl_Long$.numberOfLeadingZeros__J__I" -> LongLeading0s,
+ "jl_Long$.numberOfTrailingZeros__J__I" -> LongTrailing0s,
+ "jl_long$.toBinaryString__J__T" -> LongToBinStr,
+ "jl_Long$.toHexString__J__T" -> LongToHexStr,
+ "jl_Long$.toOctalString__J__T" -> LongToOctalStr,
+
+ "sjs_js_typedarray_package$.byteArray2Int8Array__AB__sjs_js_typedarray_Int8Array" -> ByteArrayToInt8Array,
+ "sjs_js_typedarray_package$.shortArray2Int16Array__AS__sjs_js_typedarray_Int16Array" -> ShortArrayToInt16Array,
+ "sjs_js_typedarray_package$.charArray2Uint16Array__AC__sjs_js_typedarray_Uint16Array" -> CharArrayToUint16Array,
+ "sjs_js_typedarray_package$.intArray2Int32Array__AI__sjs_js_typedarray_Int32Array" -> IntArrayToInt32Array,
+ "sjs_js_typedarray_package$.floatArray2Float32Array__AF__sjs_js_typedarray_Float32Array" -> FloatArrayToFloat32Array,
+ "sjs_js_typedarray_package$.doubleArray2Float64Array__AD__sjs_js_typedarray_Float64Array" -> DoubleArrayToFloat64Array,
+
+ "sjs_js_typedarray_package$.int8Array2ByteArray__sjs_js_typedarray_Int8Array__AB" -> Int8ArrayToByteArray,
+ "sjs_js_typedarray_package$.int16Array2ShortArray__sjs_js_typedarray_Int16Array__AS" -> Int16ArrayToShortArray,
+ "sjs_js_typedarray_package$.uint16Array2CharArray__sjs_js_typedarray_Uint16Array__AC" -> Uint16ArrayToCharArray,
+ "sjs_js_typedarray_package$.int32Array2IntArray__sjs_js_typedarray_Int32Array__AI" -> Int32ArrayToIntArray,
+ "sjs_js_typedarray_package$.float32Array2FloatArray__sjs_js_typedarray_Float32Array__AF" -> Float32ArrayToFloatArray,
+ "sjs_js_typedarray_package$.float64Array2DoubleArray__sjs_js_typedarray_Float64Array__AD" -> Float64ArrayToDoubleArray
+ ).withDefaultValue(-1)
+ }
+
+ private def getIntrinsicCode(target: AbstractMethodID): Int =
+ Intrinsics.intrinsics(target.toString)
+
+ private trait State[A] {
+ def makeBackup(): A
+ def restore(backup: A): Unit
+ }
+
+ private class SimpleState[A](var value: A) extends State[A] {
+ def makeBackup(): A = value
+ def restore(backup: A): Unit = value = backup
+ }
+
+ trait AbstractMethodID {
+ def inlineable: Boolean
+ def isTraitImplForwarder: Boolean
+ }
+
+ /** Parts of [[GenIncOptimizer#MethodImpl]] with decisions about optimizations. */
+ abstract class MethodImpl {
+ def encodedName: String
+ def optimizerHints: OptimizerHints
+ def originalDef: MethodDef
+ def thisType: Type
+
+ var inlineable: Boolean = false
+ var isTraitImplForwarder: Boolean = false
+
+ protected def updateInlineable(): Unit = {
+ val MethodDef(Ident(methodName, _), params, _, body) = originalDef
+
+ isTraitImplForwarder = body match {
+ // Shape of forwarders to trait impls
+ case TraitImplApply(impl, method, args) =>
+ ((args.size == params.size + 1) &&
+ (args.head.isInstanceOf[This]) &&
+ (args.tail.zip(params).forall {
+ case (VarRef(Ident(aname, _), _),
+ ParamDef(Ident(pname, _), _, _)) => aname == pname
+ case _ => false
+ }))
+
+ case _ => false
+ }
+
+ inlineable = optimizerHints.hasInlineAnnot || isTraitImplForwarder || {
+ val MethodDef(_, params, _, body) = originalDef
+ body match {
+ case _:Skip | _:This | _:Literal => true
+
+ // Shape of accessors
+ case Select(This(), _, _) if params.isEmpty => true
+ case Assign(Select(This(), _, _), VarRef(_, _))
+ if params.size == 1 => true
+
+ // Shape of trivial call-super constructors
+ case Block(stats)
+ if params.isEmpty && isConstructorName(encodedName) &&
+ stats.forall(isTrivialConstructorStat) => true
+
+ // Simple method
+ case SimpleMethodBody() => true
+
+ case _ => false
+ }
+ }
+ }
+ }
+
+ private def isTrivialConstructorStat(stat: Tree): Boolean = stat match {
+ case This() =>
+ true
+ case StaticApply(This(), _, _, Nil) =>
+ true
+ case TraitImplApply(_, Ident(methodName, _), This() :: Nil) =>
+ methodName.contains("__$init$__")
+ case _ =>
+ false
+ }
+
+ private object SimpleMethodBody {
+ @tailrec
+ def unapply(body: Tree): Boolean = body match {
+ case New(_, _, args) => areSimpleArgs(args)
+ case Apply(receiver, _, args) => areSimpleArgs(receiver :: args)
+ case StaticApply(receiver, _, _, args) => areSimpleArgs(receiver :: args)
+ case TraitImplApply(_, _, args) => areSimpleArgs(args)
+ case Select(qual, _, _) => isSimpleArg(qual)
+ case IsInstanceOf(inner, _) => isSimpleArg(inner)
+
+ case Block(List(inner, Undefined())) =>
+ unapply(inner)
+
+ case Unbox(inner, _) => unapply(inner)
+ case AsInstanceOf(inner, _) => unapply(inner)
+
+ case _ => isSimpleArg(body)
+ }
+
+ private def areSimpleArgs(args: List[Tree]): Boolean =
+ args.forall(isSimpleArg)
+
+ @tailrec
+ private def isSimpleArg(arg: Tree): Boolean = arg match {
+ case New(_, _, Nil) => true
+ case Apply(receiver, _, Nil) => isTrivialArg(receiver)
+ case StaticApply(receiver, _, _, Nil) => isTrivialArg(receiver)
+ case TraitImplApply(_, _, Nil) => true
+
+ case ArrayLength(array) => isTrivialArg(array)
+ case ArraySelect(array, index) => isTrivialArg(array) && isTrivialArg(index)
+
+ case Unbox(inner, _) => isSimpleArg(inner)
+ case AsInstanceOf(inner, _) => isSimpleArg(inner)
+
+ case _ =>
+ isTrivialArg(arg)
+ }
+
+ private def isTrivialArg(arg: Tree): Boolean = arg match {
+ case _:VarRef | _:This | _:Literal | _:LoadModule =>
+ true
+ case _ =>
+ false
+ }
+ }
+
+ private object BlockOrAlone {
+ def unapply(tree: Tree): Some[(List[Tree], Tree)] = Some(tree match {
+ case Block(init :+ last) => (init, last)
+ case _ => (Nil, tree)
+ })
+ }
+
+ /** Recreates precise [[Infos.MethodInfo]] from the optimized [[MethodDef]]. */
+ private def recreateInfo(methodDef: MethodDef): Infos.MethodInfo = {
+ new RecreateInfoTraverser().recreateInfo(methodDef)
+ }
+
+ private final class RecreateInfoTraverser extends Traversers.Traverser {
+ import RecreateInfoTraverser._
+
+ private val calledMethods = mutable.Map.empty[String, mutable.Set[String]]
+ private val calledMethodsStatic = mutable.Map.empty[String, mutable.Set[String]]
+ private val instantiatedClasses = mutable.Set.empty[String]
+ private val accessedModules = mutable.Set.empty[String]
+ private val accessedClassData = mutable.Set.empty[String]
+
+ def recreateInfo(methodDef: MethodDef): Infos.MethodInfo = {
+ traverse(methodDef.body)
+ Infos.MethodInfo(
+ encodedName = methodDef.name.name,
+ calledMethods = calledMethods.toMap.mapValues(_.toList),
+ calledMethodsStatic = calledMethodsStatic.toMap.mapValues(_.toList),
+ instantiatedClasses = instantiatedClasses.toList,
+ accessedModules = accessedModules.toList,
+ accessedClassData = accessedClassData.toList)
+ }
+
+ private def addCalledMethod(container: String, methodName: String): Unit =
+ calledMethods.getOrElseUpdate(container, mutable.Set.empty) += methodName
+
+ private def addCalledMethodStatic(container: String, methodName: String): Unit =
+ calledMethodsStatic.getOrElseUpdate(container, mutable.Set.empty) += methodName
+
+ private def refTypeToClassData(tpe: ReferenceType): String = tpe match {
+ case ClassType(cls) => cls
+ case ArrayType(base, _) => base
+ }
+
+ def addAccessedClassData(encodedName: String): Unit = {
+ if (!AlwaysPresentClassData.contains(encodedName))
+ accessedClassData += encodedName
+ }
+
+ def addAccessedClassData(tpe: ReferenceType): Unit =
+ addAccessedClassData(refTypeToClassData(tpe))
+
+ override def traverse(tree: Tree): Unit = {
+ tree match {
+ case New(ClassType(cls), ctor, _) =>
+ instantiatedClasses += cls
+ addCalledMethodStatic(cls, ctor.name)
+
+ case Apply(receiver, method, _) =>
+ receiver.tpe match {
+ case ClassType(cls) if !Definitions.HijackedClasses.contains(cls) =>
+ addCalledMethod(cls, method.name)
+ case AnyType =>
+ addCalledMethod(Definitions.ObjectClass, method.name)
+ case ArrayType(_, _) if method.name != "clone__O" =>
+ /* clone__O is overridden in the pseudo Array classes and is
+ * always kept anyway, because it is in scalajsenv.js.
+ * Other methods delegate to Object, which we can model with
+ * a static call to Object.method.
+ */
+ addCalledMethodStatic(Definitions.ObjectClass, method.name)
+ case _ =>
+ // Nothing to do
+ }
+
+ case StaticApply(_, ClassType(cls), method, _) =>
+ addCalledMethodStatic(cls, method.name)
+ case TraitImplApply(ClassType(impl), method, _) =>
+ addCalledMethodStatic(impl, method.name)
+
+ case LoadModule(ClassType(cls)) =>
+ accessedModules += cls.stripSuffix("$")
+
+ case NewArray(tpe, _) =>
+ addAccessedClassData(tpe)
+ case ArrayValue(tpe, _) =>
+ addAccessedClassData(tpe)
+ case IsInstanceOf(_, cls) =>
+ addAccessedClassData(cls)
+ case AsInstanceOf(_, cls) =>
+ addAccessedClassData(cls)
+ case ClassOf(cls) =>
+ addAccessedClassData(cls)
+
+ case _ =>
+ }
+ super.traverse(tree)
+ }
+ }
+
+ private object RecreateInfoTraverser {
+ /** Class data that are never eliminated by dce, so we don't need to
+ * record them.
+ */
+ private val AlwaysPresentClassData = {
+ import Definitions._
+ Set("V", "Z", "C", "B", "S", "I", "J", "F", "D",
+ ObjectClass, StringClass)
+ }
+ }
+
+ private def exceptionMsg(myself: AbstractMethodID,
+ attemptedInlining: List[AbstractMethodID]) = {
+ val buf = new StringBuilder()
+
+ buf.append("The Scala.js optimizer crashed while optimizing " + myself)
+
+ buf.append("\nMethods attempted to inline:\n")
+
+ for (m <- attemptedInlining) {
+ buf.append("* ")
+ buf.append(m)
+ buf.append('\n')
+ }
+
+ buf.toString
+ }
+
+ private class RollbackException(val trampolineId: Int,
+ val savedUsedLocalNames: Set[String],
+ val savedUsedLabelNames: Set[String],
+ val savedStates: List[Any],
+ val cont: () => TailRec[Tree]) extends ControlThrowable
+
+ class OptimizeException(val myself: AbstractMethodID,
+ val attemptedInlining: List[AbstractMethodID], cause: Throwable
+ ) extends Exception(exceptionMsg(myself, attemptedInlining), cause)
+
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/ScalaJSOptimizer.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/ScalaJSOptimizer.scala
new file mode 100644
index 0000000..646484b
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/ScalaJSOptimizer.scala
@@ -0,0 +1,552 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.optimizer
+
+import scala.annotation.{switch, tailrec}
+
+import scala.collection.mutable
+import scala.collection.immutable.{Seq, Traversable}
+
+import java.net.URI
+
+import scala.scalajs.ir
+import ir.Infos
+import ir.ClassKind
+
+import scala.scalajs.tools.logging._
+import scala.scalajs.tools.io._
+import scala.scalajs.tools.classpath._
+import scala.scalajs.tools.sourcemap._
+import scala.scalajs.tools.corelib._
+
+import scala.scalajs.tools.sem.Semantics
+
+import scala.scalajs.tools.javascript
+import javascript.{Trees => js}
+
+/** Scala.js optimizer: does type-aware global dce. */
+class ScalaJSOptimizer(
+ semantics: Semantics,
+ optimizerFactory: (Semantics) => GenIncOptimizer) {
+ import ScalaJSOptimizer._
+
+ private val classEmitter = new javascript.ScalaJSClassEmitter(semantics)
+
+ private[this] var persistentState: PersistentState = new PersistentState
+ private[this] var optimizer: GenIncOptimizer = optimizerFactory(semantics)
+
+ def this(semantics: Semantics) = this(semantics, new IncOptimizer(_))
+
+ /** Applies Scala.js-specific optimizations to a CompleteIRClasspath.
+ * See [[ScalaJSOptimizer.Inputs]] for details about the required and
+ * optional inputs.
+ * See [[ScalaJSOptimizer.OutputConfig]] for details about the configuration
+ * for the output of this method.
+ * Returns a [[CompleteCIClasspath]] containing the result of the
+ * optimizations.
+ *
+ * analyzes, dead code eliminates and concatenates IR content
+ * - Maintains/establishes order
+ * - No IR in result
+ * - CoreJSLibs in result (since they are implicitly in the CompleteIRCP)
+ */
+ def optimizeCP(inputs: Inputs[IRClasspath], outCfg: OutputConfig,
+ logger: Logger): LinkedClasspath = {
+
+ val cp = inputs.input
+
+ CacheUtils.cached(cp.version, outCfg.output, outCfg.cache) {
+ logger.info(s"Fast optimizing ${outCfg.output.path}")
+ optimizeIR(inputs.copy(input = inputs.input.scalaJSIR), outCfg, logger)
+ }
+
+ new LinkedClasspath(cp.jsLibs, outCfg.output, cp.requiresDOM, cp.version)
+ }
+
+ def optimizeIR(inputs: Inputs[Traversable[VirtualScalaJSIRFile]],
+ outCfg: OutputConfig, logger: Logger): Unit = {
+
+ val builder = {
+ import outCfg._
+ if (wantSourceMap)
+ new JSFileBuilderWithSourceMap(output.name,
+ output.contentWriter,
+ output.sourceMapWriter,
+ relativizeSourceMapBase)
+ else
+ new JSFileBuilder(output.name, output.contentWriter)
+ }
+
+ builder.addLine("'use strict';")
+ CoreJSLibs.libs(semantics).foreach(builder.addFile _)
+
+ optimizeIR(inputs, outCfg, builder, logger)
+
+ builder.complete()
+ builder.closeWriters()
+ }
+
+ def optimizeIR(inputs: Inputs[Traversable[VirtualScalaJSIRFile]],
+ outCfg: OptimizerConfig, builder: JSTreeBuilder, logger: Logger): Unit = {
+
+ /* Handle tree equivalence: If we handled source maps so far, positions are
+ still up-to-date. Otherwise we need to flush the state if proper
+ positions are requested now.
+ */
+ if (outCfg.wantSourceMap && !persistentState.wasWithSourceMap)
+ clean()
+
+ persistentState.wasWithSourceMap = outCfg.wantSourceMap
+
+ persistentState.startRun()
+ try {
+ import inputs._
+ val allData =
+ GenIncOptimizer.logTime(logger, "Read info") {
+ readAllData(inputs.input, logger)
+ }
+ val (useOptimizer, refinedAnalyzer) = GenIncOptimizer.logTime(
+ logger, "Optimizations part") {
+ val analyzer =
+ GenIncOptimizer.logTime(logger, "Compute reachability") {
+ val analyzer = new Analyzer(logger, semantics, allData,
+ globalWarnEnabled = true,
+ isBeforeOptimizer = !outCfg.disableOptimizer)
+ analyzer.computeReachability(manuallyReachable, noWarnMissing)
+ analyzer
+ }
+ if (outCfg.checkIR) {
+ GenIncOptimizer.logTime(logger, "Check IR") {
+ if (analyzer.allAvailable)
+ checkIR(analyzer, logger)
+ else if (inputs.noWarnMissing.isEmpty)
+ sys.error("Could not check IR because there where linking errors.")
+ }
+ }
+ def getClassTreeIfChanged(encodedName: String,
+ lastVersion: Option[String]): Option[(ir.Trees.ClassDef, Option[String])] = {
+ val persistentFile = persistentState.encodedNameToPersistentFile(encodedName)
+ persistentFile.treeIfChanged(lastVersion)
+ }
+
+ val useOptimizer = analyzer.allAvailable && !outCfg.disableOptimizer
+
+ if (outCfg.batchMode)
+ optimizer = optimizerFactory(semantics)
+
+ val refinedAnalyzer = if (useOptimizer) {
+ GenIncOptimizer.logTime(logger, "Inliner") {
+ optimizer.update(analyzer, getClassTreeIfChanged,
+ outCfg.wantSourceMap, logger)
+ }
+ GenIncOptimizer.logTime(logger, "Refined reachability analysis") {
+ val refinedData = computeRefinedData(allData, optimizer)
+ val refinedAnalyzer = new Analyzer(logger, semantics, refinedData,
+ globalWarnEnabled = false,
+ isBeforeOptimizer = false)
+ refinedAnalyzer.computeReachability(manuallyReachable, noWarnMissing)
+ refinedAnalyzer
+ }
+ } else {
+ if (inputs.noWarnMissing.isEmpty && !outCfg.disableOptimizer)
+ logger.warn("Not running the inliner because there where linking errors.")
+ analyzer
+ }
+ (useOptimizer, refinedAnalyzer)
+ }
+ GenIncOptimizer.logTime(logger, "Write DCE'ed output") {
+ buildDCEedOutput(builder, refinedAnalyzer, useOptimizer)
+ }
+ } finally {
+ persistentState.endRun(outCfg.unCache)
+ logger.debug(
+ s"Inc. opt stats: reused: ${persistentState.statsReused} -- "+
+ s"invalidated: ${persistentState.statsInvalidated} -- "+
+ s"trees read: ${persistentState.statsTreesRead}")
+ }
+ }
+
+ /** Resets all persistent state of this optimizer */
+ def clean(): Unit = {
+ persistentState = new PersistentState
+ optimizer = optimizerFactory(semantics)
+ }
+
+ private def readAllData(ir: Traversable[VirtualScalaJSIRFile],
+ logger: Logger): scala.collection.Seq[Infos.ClassInfo] = {
+ ir.map(persistentState.getPersistentIRFile(_).info).toSeq
+ }
+
+ private def checkIR(analyzer: Analyzer, logger: Logger): Unit = {
+ val allClassDefs = for {
+ classInfo <- analyzer.classInfos.values
+ persistentIRFile <- persistentState.encodedNameToPersistentFile.get(
+ classInfo.encodedName)
+ } yield persistentIRFile.tree
+ val checker = new IRChecker(analyzer, allClassDefs.toSeq, logger)
+ if (!checker.check())
+ sys.error(s"There were ${checker.errorCount} IR checking errors.")
+ }
+
+ private def computeRefinedData(
+ allData: scala.collection.Seq[Infos.ClassInfo],
+ optimizer: GenIncOptimizer): scala.collection.Seq[Infos.ClassInfo] = {
+
+ def refineMethodInfo(container: optimizer.MethodContainer,
+ methodInfo: Infos.MethodInfo): Infos.MethodInfo = {
+ container.methods.get(methodInfo.encodedName).fold(methodInfo) {
+ methodImpl => methodImpl.preciseInfo
+ }
+ }
+
+ def refineMethodInfos(container: optimizer.MethodContainer,
+ methodInfos: List[Infos.MethodInfo]): List[Infos.MethodInfo] = {
+ methodInfos.map(m => refineMethodInfo(container, m))
+ }
+
+ def refineClassInfo(container: optimizer.MethodContainer,
+ info: Infos.ClassInfo): Infos.ClassInfo = {
+ val refinedMethods = refineMethodInfos(container, info.methods)
+ Infos.ClassInfo(info.name, info.encodedName, info.isExported,
+ info.ancestorCount, info.kind, info.superClass, info.ancestors,
+ Infos.OptimizerHints.empty, refinedMethods)
+ }
+
+ for {
+ info <- allData
+ } yield {
+ info.kind match {
+ case ClassKind.Class | ClassKind.ModuleClass =>
+ optimizer.getClass(info.encodedName).fold(info) {
+ cls => refineClassInfo(cls, info)
+ }
+
+ case ClassKind.TraitImpl =>
+ optimizer.getTraitImpl(info.encodedName).fold(info) {
+ impl => refineClassInfo(impl, info)
+ }
+
+ case _ =>
+ info
+ }
+ }
+ }
+
+ private def buildDCEedOutput(builder: JSTreeBuilder,
+ analyzer: Analyzer, useInliner: Boolean): Unit = {
+
+ def compareClassInfo(lhs: analyzer.ClassInfo, rhs: analyzer.ClassInfo) = {
+ if (lhs.ancestorCount != rhs.ancestorCount) lhs.ancestorCount < rhs.ancestorCount
+ else lhs.encodedName.compareTo(rhs.encodedName) < 0
+ }
+
+ def addPersistentFile(classInfo: analyzer.ClassInfo,
+ persistentFile: PersistentIRFile) = {
+ import ir.Trees._
+ import javascript.JSDesugaring.{desugarJavaScript => desugar}
+
+ val d = persistentFile.desugared
+ lazy val classDef = {
+ persistentState.statsTreesRead += 1
+ persistentFile.tree
+ }
+
+ def addTree(tree: js.Tree): Unit =
+ builder.addJSTree(tree)
+
+ def addReachableMethods(emitFun: (String, MethodDef) => js.Tree): Unit = {
+ /* This is a bit convoluted because we have to:
+ * * avoid to use classDef at all if we already know all the needed methods
+ * * if any new method is needed, better to go through the defs once
+ */
+ val methodNames = d.methodNames.getOrElseUpdate(
+ classDef.defs collect {
+ case MethodDef(Ident(encodedName, _), _, _, _) => encodedName
+ })
+ val reachableMethods = methodNames.filter(
+ name => classInfo.methodInfos(name).isReachable)
+ if (reachableMethods.forall(d.methods.contains(_))) {
+ for (encodedName <- reachableMethods) {
+ addTree(d.methods(encodedName))
+ }
+ } else {
+ classDef.defs.foreach {
+ case m: MethodDef if m.name.isInstanceOf[Ident] =>
+ if (classInfo.methodInfos(m.name.name).isReachable) {
+ addTree(d.methods.getOrElseUpdate(m.name.name,
+ emitFun(classInfo.encodedName, m)))
+ }
+ case _ =>
+ }
+ }
+ }
+
+ if (classInfo.isImplClass) {
+ if (useInliner) {
+ for {
+ method <- optimizer.findTraitImpl(classInfo.encodedName).methods.values
+ if (classInfo.methodInfos(method.encodedName).isReachable)
+ } {
+ addTree(method.desugaredDef)
+ }
+ } else {
+ addReachableMethods(classEmitter.genTraitImplMethod)
+ }
+ } else if (!classInfo.hasMoreThanData) {
+ // there is only the data anyway
+ addTree(d.wholeClass.getOrElseUpdate(
+ classEmitter.genClassDef(classDef)))
+ } else {
+ if (classInfo.isAnySubclassInstantiated) {
+ addTree(d.constructor.getOrElseUpdate(
+ classEmitter.genConstructor(classDef)))
+ if (useInliner) {
+ for {
+ method <- optimizer.findClass(classInfo.encodedName).methods.values
+ if (classInfo.methodInfos(method.encodedName).isReachable)
+ } {
+ addTree(method.desugaredDef)
+ }
+ } else {
+ addReachableMethods(classEmitter.genMethod)
+ }
+ addTree(d.exportedMembers.getOrElseUpdate(js.Block {
+ classDef.defs collect {
+ case m: MethodDef if m.name.isInstanceOf[StringLiteral] =>
+ classEmitter.genMethod(classInfo.encodedName, m)
+ case p: PropertyDef =>
+ classEmitter.genProperty(classInfo.encodedName, p)
+ }
+ }(classDef.pos)))
+ }
+ if (classInfo.isDataAccessed) {
+ addTree(d.typeData.getOrElseUpdate(js.Block(
+ classEmitter.genInstanceTests(classDef),
+ classEmitter.genArrayInstanceTests(classDef),
+ classEmitter.genTypeData(classDef)
+ )(classDef.pos)))
+ }
+ if (classInfo.isAnySubclassInstantiated)
+ addTree(d.setTypeData.getOrElseUpdate(
+ classEmitter.genSetTypeData(classDef)))
+ if (classInfo.isModuleAccessed)
+ addTree(d.moduleAccessor.getOrElseUpdate(
+ classEmitter.genModuleAccessor(classDef)))
+ addTree(d.classExports.getOrElseUpdate(
+ classEmitter.genClassExports(classDef)))
+ }
+ }
+
+
+ for {
+ classInfo <- analyzer.classInfos.values.toSeq.sortWith(compareClassInfo)
+ if classInfo.isNeededAtAll
+ } {
+ val optPersistentFile =
+ persistentState.encodedNameToPersistentFile.get(classInfo.encodedName)
+
+ // if we have a persistent file, this is not a dummy class
+ optPersistentFile.fold {
+ if (classInfo.isAnySubclassInstantiated) {
+ // Subclass will emit constructor that references this dummy class.
+ // Therefore, we need to emit a dummy parent.
+ builder.addJSTree(
+ classEmitter.genDummyParent(classInfo.encodedName))
+ }
+ } { pf => addPersistentFile(classInfo, pf) }
+ }
+ }
+}
+
+object ScalaJSOptimizer {
+ /** Inputs of the Scala.js optimizer. */
+ final case class Inputs[T](
+ /** The CompleteNCClasspath or the IR files to be packaged. */
+ input: T,
+ /** Manual additions to reachability */
+ manuallyReachable: Seq[ManualReachability] = Nil,
+ /** Elements we won't warn even if they don't exist */
+ noWarnMissing: Seq[NoWarnMissing] = Nil
+ )
+
+ sealed abstract class ManualReachability
+ final case class ReachObject(name: String) extends ManualReachability
+ final case class Instantiate(name: String) extends ManualReachability
+ final case class ReachMethod(className: String, methodName: String,
+ static: Boolean) extends ManualReachability
+
+ sealed abstract class NoWarnMissing
+ final case class NoWarnClass(className: String) extends NoWarnMissing
+ final case class NoWarnMethod(className: String, methodName: String)
+ extends NoWarnMissing
+
+ /** Configurations relevant to the optimizer */
+ trait OptimizerConfig {
+ /** Ask to produce source map for the output. Is used in the incremental
+ * optimizer to decide whether a position change should trigger re-inlining
+ */
+ val wantSourceMap: Boolean
+ /** If true, performs expensive checks of the IR for the used parts. */
+ val checkIR: Boolean
+ /** If true, the optimizer removes trees that have not been used in the
+ * last run from the cache. Otherwise, all trees that has been used once,
+ * are kept in memory. */
+ val unCache: Boolean
+ /** If true, no optimizations are performed */
+ val disableOptimizer: Boolean
+ /** If true, nothing is performed incrementally */
+ val batchMode: Boolean
+ }
+
+ /** Configuration for the output of the Scala.js optimizer. */
+ final case class OutputConfig(
+ /** Writer for the output. */
+ output: WritableVirtualJSFile,
+ /** Cache file */
+ cache: Option[WritableVirtualTextFile] = None,
+ /** Ask to produce source map for the output */
+ wantSourceMap: Boolean = false,
+ /** Base path to relativize paths in the source map. */
+ relativizeSourceMapBase: Option[URI] = None,
+ /** If true, performs expensive checks of the IR for the used parts. */
+ checkIR: Boolean = false,
+ /** If true, the optimizer removes trees that have not been used in the
+ * last run from the cache. Otherwise, all trees that has been used once,
+ * are kept in memory. */
+ unCache: Boolean = true,
+ /** If true, no optimizations are performed */
+ disableOptimizer: Boolean = false,
+ /** If true, nothing is performed incrementally */
+ batchMode: Boolean = false
+ ) extends OptimizerConfig
+
+ // Private helpers -----------------------------------------------------------
+
+ private final class PersistentState {
+ val files = mutable.Map.empty[String, PersistentIRFile]
+ val encodedNameToPersistentFile =
+ mutable.Map.empty[String, PersistentIRFile]
+
+ var statsReused: Int = 0
+ var statsInvalidated: Int = 0
+ var statsTreesRead: Int = 0
+
+ var wasWithSourceMap: Boolean = true
+
+ def startRun(): Unit = {
+ statsReused = 0
+ statsInvalidated = 0
+ statsTreesRead = 0
+ for (file <- files.values)
+ file.startRun()
+ }
+
+ def getPersistentIRFile(irFile: VirtualScalaJSIRFile): PersistentIRFile = {
+ val file = files.getOrElseUpdate(irFile.path,
+ new PersistentIRFile(irFile.path))
+ if (file.updateFile(irFile))
+ statsReused += 1
+ else
+ statsInvalidated += 1
+ encodedNameToPersistentFile += ((file.info.encodedName, file))
+ file
+ }
+
+ def endRun(unCache: Boolean): Unit = {
+ // "Garbage-collect" persisted versions of files that have disappeared
+ files.retain((_, f) => f.cleanAfterRun(unCache))
+ encodedNameToPersistentFile.clear()
+ }
+ }
+
+ private final class PersistentIRFile(val path: String) {
+ import ir.Trees._
+
+ private[this] var existedInThisRun: Boolean = false
+ private[this] var desugaredUsedInThisRun: Boolean = false
+
+ private[this] var irFile: VirtualScalaJSIRFile = null
+ private[this] var version: Option[String] = None
+ private[this] var _info: Infos.ClassInfo = null
+ private[this] var _tree: ClassDef = null
+ private[this] var _desugared: Desugared = null
+
+ def startRun(): Unit = {
+ existedInThisRun = false
+ desugaredUsedInThisRun = false
+ }
+
+ def updateFile(irFile: VirtualScalaJSIRFile): Boolean = {
+ existedInThisRun = true
+ this.irFile = irFile
+ if (version.isDefined && version == irFile.version) {
+ // yeepeeh, nothing to do
+ true
+ } else {
+ version = irFile.version
+ _info = irFile.info
+ _tree = null
+ _desugared = null
+ false
+ }
+ }
+
+ def info: Infos.ClassInfo = _info
+
+ def desugared: Desugared = {
+ desugaredUsedInThisRun = true
+ if (_desugared == null)
+ _desugared = new Desugared
+ _desugared
+ }
+
+ def tree: ClassDef = {
+ if (_tree == null)
+ _tree = irFile.tree
+ _tree
+ }
+
+ def treeIfChanged(lastVersion: Option[String]): Option[(ClassDef, Option[String])] = {
+ if (lastVersion.isDefined && lastVersion == version) None
+ else Some((tree, version))
+ }
+
+ /** Returns true if this file should be kept for the next run at all. */
+ def cleanAfterRun(unCache: Boolean): Boolean = {
+ irFile = null
+ if (unCache && !desugaredUsedInThisRun)
+ _desugared = null // free desugared if unused in this run
+ existedInThisRun
+ }
+ }
+
+ private final class Desugared {
+ // for class kinds that are not decomposed
+ val wholeClass = new OneTimeCache[js.Tree]
+
+ val constructor = new OneTimeCache[js.Tree]
+ val methodNames = new OneTimeCache[List[String]]
+ val methods = mutable.Map.empty[String, js.Tree]
+ val exportedMembers = new OneTimeCache[js.Tree]
+ val typeData = new OneTimeCache[js.Tree]
+ val setTypeData = new OneTimeCache[js.Tree]
+ val moduleAccessor = new OneTimeCache[js.Tree]
+ val classExports = new OneTimeCache[js.Tree]
+ }
+
+ private final class OneTimeCache[A >: Null] {
+ private[this] var value: A = null
+ def getOrElseUpdate(v: => A): A = {
+ if (value == null)
+ value = v
+ value
+ }
+ }
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/sem/CheckedBehavior.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/sem/CheckedBehavior.scala
new file mode 100644
index 0000000..4668b3c
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/sem/CheckedBehavior.scala
@@ -0,0 +1,24 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.sem
+
+sealed abstract class CheckedBehavior {
+ import CheckedBehavior._
+ def optimized: CheckedBehavior = this match {
+ case Fatal => Unchecked
+ case _ => this
+ }
+}
+
+object CheckedBehavior {
+ case object Compliant extends CheckedBehavior
+ case object Fatal extends CheckedBehavior
+ case object Unchecked extends CheckedBehavior
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/sem/Semantics.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/sem/Semantics.scala
new file mode 100644
index 0000000..9d17b06
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/sem/Semantics.scala
@@ -0,0 +1,97 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.sem
+
+import scala.collection.immutable.Traversable
+
+final class Semantics private (
+ val asInstanceOfs: CheckedBehavior,
+ val strictFloats: Boolean) {
+
+ import Semantics._
+
+ def withAsInstanceOfs(behavior: CheckedBehavior): Semantics =
+ copy(asInstanceOfs = behavior)
+
+ def withStrictFloats(strictFloats: Boolean): Semantics =
+ copy(strictFloats = strictFloats)
+
+ def optimized: Semantics =
+ copy(asInstanceOfs = this.asInstanceOfs.optimized)
+
+ override def equals(that: Any): Boolean = that match {
+ case that: Semantics =>
+ this.asInstanceOfs == that.asInstanceOfs &&
+ this.strictFloats == that.strictFloats
+ case _ =>
+ false
+ }
+
+ override def hashCode(): Int = {
+ import scala.util.hashing.MurmurHash3._
+ var acc = HashSeed
+ acc = mix(acc, asInstanceOfs.hashCode)
+ acc = mixLast(acc, strictFloats.##)
+ finalizeHash(acc, 1)
+ }
+
+ override def toString(): String = {
+ s"""Semantics(
+ | asInstanceOfs = $asInstanceOfs,
+ | strictFloats = $strictFloats
+ |)""".stripMargin
+ }
+
+ /** Checks whether the given semantics setting is Java compliant */
+ def isCompliant(name: String): Boolean = name match {
+ case "asInstanceOfs" => asInstanceOfs == CheckedBehavior.Compliant
+ case "strictFloats" => strictFloats
+ case _ => false
+ }
+
+ /** Retrieve a list of semantics which are set to compliant */
+ def compliants: List[String] = {
+ def cl(name: String, cond: Boolean) = if (cond) List(name) else Nil
+
+ cl("asInstanceOfs", asInstanceOfs == CheckedBehavior.Compliant) ++
+ cl("strictFloats", strictFloats)
+ }
+
+ private def copy(
+ asInstanceOfs: CheckedBehavior = this.asInstanceOfs,
+ strictFloats: Boolean = this.strictFloats): Semantics = {
+ new Semantics(
+ asInstanceOfs = asInstanceOfs,
+ strictFloats = strictFloats)
+ }
+}
+
+object Semantics {
+ private val HashSeed =
+ scala.util.hashing.MurmurHash3.stringHash(classOf[Semantics].getName)
+
+ val Defaults: Semantics = new Semantics(
+ asInstanceOfs = CheckedBehavior.Fatal,
+ strictFloats = false)
+
+ def compliantTo(semantics: Traversable[String]): Semantics = {
+ import Defaults._
+ import CheckedBehavior._
+
+ val semsSet = semantics.toSet
+
+ def sw[T](name: String, compliant: T, default: T): T =
+ if (semsSet.contains(name)) compliant else default
+
+ new Semantics(
+ asInstanceOfs = sw("asInstanceOfs", Compliant, asInstanceOfs),
+ strictFloats = sw("strictFloats", true, strictFloats))
+ }
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/sourcemap/JSFileBuilder.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/sourcemap/JSFileBuilder.scala
new file mode 100644
index 0000000..1bf2254
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/sourcemap/JSFileBuilder.scala
@@ -0,0 +1,144 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.sourcemap
+
+import scala.annotation.tailrec
+
+import scala.collection.mutable
+
+import java.io._
+import java.util.regex.Pattern
+import java.net.{ URI, URISyntaxException }
+
+import scala.scalajs.ir.Position
+import scala.scalajs.tools.{javascript => js}
+import scala.scalajs.tools.io._
+import scala.scalajs.tools.optimizer.JSTreeBuilder
+
+class JSFileBuilder(val name: String,
+ protected val outputWriter: Writer) extends JSTreeBuilder {
+ def addLine(line: String): Unit = {
+ outputWriter.write(line)
+ outputWriter.write('\n')
+ }
+
+ def addLines(lines: Seq[String]): Unit =
+ lines.foreach(addLine)
+
+ def addFile(file: VirtualJSFile): Unit =
+ addPartsOfFile(file)(!_.startsWith("//# sourceMappingURL="))
+
+ def addPartsOfFile(file: VirtualJSFile)(selector: String => Boolean): Unit = {
+ for (line <- file.readLines() if selector(line))
+ addLine(line)
+ }
+
+ /** Add a JavaScript tree representing a statement.
+ * The tree must be a valid JavaScript tree (typically obtained by
+ * desugaring a full-fledged IR tree).
+ */
+ def addJSTree(tree: js.Trees.Tree): Unit = {
+ val printer = new js.Printers.JSTreePrinter(outputWriter)
+ printer.printTopLevelTree(tree)
+ // Do not close the printer: we do not have ownership of the writers
+ }
+
+ /** Closes the underlying writer(s).
+ */
+ def closeWriters(): Unit = {
+ outputWriter.close()
+ }
+}
+
+class JSFileBuilderWithSourceMapWriter(n: String, ow: Writer,
+ protected val sourceMapWriter: SourceMapWriter)
+ extends JSFileBuilder(n, ow) {
+
+ override def addLine(line: String): Unit = {
+ super.addLine(line)
+ sourceMapWriter.nextLine()
+ }
+
+ private final val NotSelected = -1
+
+ override def addPartsOfFile(file: VirtualJSFile)(
+ selector: String => Boolean): Unit = {
+ val br = new BufferedReader(file.reader)
+ try {
+ // Select lines, and remember offsets
+ val offsets = new mutable.ArrayBuffer[Int] // (maybe NotSelected)
+ val selectedLineLengths = new mutable.ArrayBuffer[Int]
+ var line: String = br.readLine()
+ var selectedCount = 0
+ while (line != null) {
+ if (selector(line)) {
+ super.addLine(line) // super call not to advance line in source map
+ offsets += selectedCount
+ selectedLineLengths += line.length
+ selectedCount += 1
+ } else {
+ offsets += NotSelected
+ }
+ line = br.readLine()
+ }
+
+ /* We ignore a potential source map.
+ * This happens typically for corejslib.js and other helper files
+ * written directly in JS.
+ * We generate a fake line-by-line source map for these on the fly
+ */
+ val sourceFile = file.toURI
+
+ for (lineNumber <- 0 until offsets.size) {
+ val offset = offsets(lineNumber)
+ if (offset != NotSelected) {
+ val originalPos = Position(sourceFile, lineNumber, 0)
+ sourceMapWriter.startNode(0, originalPos, None)
+ sourceMapWriter.endNode(selectedLineLengths(offset))
+ sourceMapWriter.nextLine()
+ }
+ }
+ } finally {
+ br.close()
+ }
+ }
+
+ override def addJSTree(tree: js.Trees.Tree): Unit = {
+ val printer = new js.Printers.JSTreePrinterWithSourceMap(
+ outputWriter, sourceMapWriter)
+ printer.printTopLevelTree(tree)
+ // Do not close the printer: we do not have ownership of the writers
+ }
+
+ override def complete(): Unit = {
+ super.complete()
+ sourceMapWriter.complete()
+ }
+
+}
+
+class JSFileBuilderWithSourceMap(n: String, ow: Writer,
+ sourceMapOutputWriter: Writer,
+ relativizeSourceMapBasePath: Option[URI] = None)
+ extends JSFileBuilderWithSourceMapWriter(
+ n, ow,
+ new SourceMapWriter(sourceMapOutputWriter, n,
+ relativizeSourceMapBasePath)) {
+
+ override def complete(): Unit = {
+ addLine("//# sourceMappingURL=" + name + ".map")
+ super.complete()
+ }
+
+ override def closeWriters(): Unit = {
+ super.closeWriters()
+ sourceMapOutputWriter.close()
+ }
+}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/sourcemap/SourceMapWriter.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/sourcemap/SourceMapWriter.scala
new file mode 100644
index 0000000..5d8bdb1
--- /dev/null
+++ b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/sourcemap/SourceMapWriter.scala
@@ -0,0 +1,213 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js tools **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.tools.sourcemap
+
+import java.io.Writer
+import java.net.URI
+
+import scala.collection.mutable.{ ListBuffer, HashMap, Stack, StringBuilder }
+
+import scala.scalajs.ir
+import ir.Position
+import ir.Position._
+import ir.Utils
+
+object SourceMapWriter {
+ private val Base64Map =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
+ "abcdefghijklmnopqrstuvwxyz" +
+ "0123456789+/"
+
+ // Some constants for writeBase64VLQ
+ // Each base-64 digit covers 6 bits, but 1 is used for the continuation
+ private final val VLQBaseShift = 5
+ private final val VLQBase = 1 << VLQBaseShift
+ private final val VLQBaseMask = VLQBase - 1
+ private final val VLQContinuationBit = VLQBase
+
+ private def jsonString(s: String) =
+ "\"" + Utils.escapeJS(s) + "\""
+}
+
+class SourceMapWriter(
+ val out: Writer,
+ val generatedFile: String,
+ val relativizeBaseURI: Option[URI] = None) {
+
+ import SourceMapWriter._
+
+ private val sources = new ListBuffer[String]
+ private val _srcToIndex = new HashMap[SourceFile, Int]
+
+ private val names = new ListBuffer[String]
+ private val _nameToIndex = new HashMap[String, Int]
+
+ private val nodePosStack = new Stack[(Position, Option[String])]
+ nodePosStack.push((NoPosition, None))
+
+ private var lineCountInGenerated = 0
+ private var lastColumnInGenerated = 0
+ private var firstSegmentOfLine = true
+ private var lastSource: SourceFile = null
+ private var lastSourceIndex = 0
+ private var lastLine: Int = 0
+ private var lastColumn: Int = 0
+ private var lastNameIndex: Int = 0
+
+ private var pendingColumnInGenerated: Int = -1
+ private var pendingPos: Position = NoPosition
+ private var pendingName: Option[String] = None
+
+ writeHeader()
+
+ private def sourceToIndex(source: SourceFile) =
+ _srcToIndex.getOrElseUpdate(source,
+ (sources += sourceToURI(source)).size-1)
+
+ /** Relatively hacky way to get a Web-friendly URI to the source file */
+ private def sourceToURI(source: SourceFile): String = {
+ val uri = source
+ val relURI = relativizeBaseURI.fold(uri)(Utils.relativize(_, uri))
+
+ Utils.fixFileURI(relURI).toASCIIString
+ }
+
+ private def nameToIndex(name: String) =
+ _nameToIndex.getOrElseUpdate(name, (names += name).size-1)
+
+ private def writeHeader(): Unit = {
+ out.write("{\n")
+ out.write("\"version\": 3,\n")
+ out.write("\"file\": " + jsonString(generatedFile) + ",\n")
+ out.write("\"mappings\": \"")
+ }
+
+ def nextLine(): Unit = {
+ writePendingSegment()
+ out.write(';')
+ lineCountInGenerated += 1
+ lastColumnInGenerated = 0
+ firstSegmentOfLine = true
+ pendingColumnInGenerated = -1
+ pendingPos = nodePosStack.top._1
+ pendingName = nodePosStack.top._2
+ }
+
+ def startNode(column: Int, originalPos: Position,
+ originalName: Option[String] = None): Unit = {
+ nodePosStack.push((originalPos, originalName))
+ startSegment(column, originalPos, originalName)
+ }
+
+ def endNode(column: Int): Unit = {
+ nodePosStack.pop()
+ startSegment(column, nodePosStack.top._1, nodePosStack.top._2)
+ }
+
+ private def startSegment(startColumn: Int, originalPos: Position,
+ originalName: Option[String]): Unit = {
+ // There is no point in outputting a segment with the same information
+ if ((originalPos == pendingPos) && (originalName == pendingName))
+ return
+
+ // Write pending segment if it covers a non-empty range
+ if (startColumn != pendingColumnInGenerated)
+ writePendingSegment()
+
+ // New pending
+ pendingColumnInGenerated = startColumn
+ pendingPos = originalPos
+ pendingName =
+ if (startColumn != pendingColumnInGenerated) originalName
+ else pendingName orElse originalName
+ }
+
+ private def writePendingSegment() {
+ if (pendingColumnInGenerated < 0)
+ return
+
+ // Segments of a line are separated by ','
+ if (firstSegmentOfLine) firstSegmentOfLine = false
+ else out.write(',')
+
+ // Generated column field
+ writeBase64VLQ(pendingColumnInGenerated-lastColumnInGenerated)
+ lastColumnInGenerated = pendingColumnInGenerated
+
+ // If the position is NoPosition, stop here
+ if (!pendingPos.isDefined)
+ return
+
+ // Extract relevant properties of pendingPos
+ val source = pendingPos.source
+ val line = pendingPos.line
+ val column = pendingPos.column
+
+ // Source index field
+ if (source == lastSource) { // highly likely
+ writeBase64VLQ0()
+ } else {
+ val sourceIndex = sourceToIndex(source)
+ writeBase64VLQ(sourceIndex-lastSourceIndex)
+ lastSource = source
+ lastSourceIndex = sourceIndex
+ }
+
+ // Line field
+ writeBase64VLQ(line - lastLine)
+ lastLine = line
+
+ // Column field
+ writeBase64VLQ(column - lastColumn)
+ lastColumn = column
+
+ // Name field
+ if (pendingName.isDefined) {
+ val nameIndex = nameToIndex(pendingName.get)
+ writeBase64VLQ(nameIndex-lastNameIndex)
+ lastNameIndex = nameIndex
+ }
+ }
+
+ def complete(): Unit = {
+ writePendingSegment()
+
+ out.write("\",\n")
+ out.write(
+ sources.map(jsonString(_)).mkString("\"sources\": [", ", ", "],\n"))
+ out.write(
+ names.map(jsonString(_)).mkString("\"names\": [", ", ", "],\n"))
+ out.write("\"lineCount\": "+lineCountInGenerated+"\n")
+ out.write("}\n")
+ }
+
+ /** Write the Base 64 VLQ of an integer to the mappings
+ * Inspired by the implementation in Closure Compiler:
+ * http://code.google.com/p/closure-compiler/source/browse/src/com/google/debugging/sourcemap/Base64VLQ.java
+ */
+ private def writeBase64VLQ(value0: Int): Unit = {
+ // Sign is encoded in the least significant bit
+ var value =
+ if (value0 < 0) ((-value0) << 1) + 1
+ else value0 << 1
+
+ // Write as many base-64 digits as necessary to encode value
+ do {
+ var digit = value & VLQBaseMask
+ value = value >>> VLQBaseShift
+ if (value != 0)
+ digit |= VLQContinuationBit
+ out.write(Base64Map.charAt(digit))
+ } while (value != 0)
+ }
+
+ private def writeBase64VLQ0(): Unit =
+ out.write('A')
+}