From 2c4b142503bd2d871e6818b5cab8c38627d9e4a0 Mon Sep 17 00:00:00 2001 From: Haoyi Li Date: Wed, 26 Nov 2014 00:45:31 -0800 Subject: Squashed 'examples/scala-js/' content from commit 47311ba git-subtree-dir: examples/scala-js git-subtree-split: 47311ba693f949f204f27ea9475bb63425fbd4f3 --- .gitignore | 10 + LICENSE | 27 + README.md | 116 + TESTING | 67 + ci/check-partest-coverage.sh | 58 + ci/checksizes.sh | 65 + ci/matrix.xml | 360 ++ ci/scalajs-matrix-build.groovy | 57 + cli/src/main/resources/scalajsc | 16 + cli/src/main/resources/scalajsc.bat | 14 + cli/src/main/resources/scalajsld | 10 + cli/src/main/resources/scalajsld.bat | 8 + cli/src/main/resources/scalajsp | 9 + cli/src/main/resources/scalajsp.bat | 7 + .../main/scala/scala/scalajs/cli/Scalajsld.scala | 180 + .../main/scala/scala/scalajs/cli/Scalajsp.scala | 158 + compiler/src/main/resources/scalac-plugin.xml | 4 + .../scala/scala/scalajs/compiler/ClassInfos.scala | 143 + .../scalajs/compiler/Compat210Component.scala | 108 + .../scala/scala/scalajs/compiler/GenJSCode.scala | 3911 ++++++++++++++++++++ .../scala/scalajs/compiler/GenJSExports.scala | 751 ++++ .../scala/scala/scalajs/compiler/GenJSFiles.scala | 51 + .../scala/scalajs/compiler/JSDefinitions.scala | 128 + .../scala/scala/scalajs/compiler/JSEncoding.scala | 261 ++ .../scala/scalajs/compiler/JSGlobalAddons.scala | 244 ++ .../scala/scalajs/compiler/JSPrimitives.scala | 119 + .../scala/scalajs/compiler/JSTreeExtractors.scala | 66 + .../scala/scalajs/compiler/PrepJSExports.scala | 251 ++ .../scala/scalajs/compiler/PrepJSInterop.scala | 621 ++++ .../scala/scalajs/compiler/ScalaJSOptions.scala | 30 + .../scala/scalajs/compiler/ScalaJSPlugin.scala | 143 + .../scala/scala/scalajs/compiler/TypeKinds.scala | 252 ++ .../scala/scalajs/compiler/util/ScopedVar.scala | 38 + .../scalajs/compiler/test/DiverseErrorsTest.scala | 31 + .../compiler/test/EnumerationInteropTest.scala | 135 + .../compiler/test/JSDynamicLiteralTest.scala | 102 + .../scalajs/compiler/test/JSExportASTTest.scala | 38 + .../scala/scalajs/compiler/test/JSExportTest.scala | 745 ++++ .../scalajs/compiler/test/JSInteropTest.scala | 350 ++ .../scalajs/compiler/test/OptimizationTest.scala | 109 + .../scala/scalajs/compiler/test/PositionTest.scala | 37 + .../scalajs/compiler/test/util/DirectTest.scala | 70 + .../scalajs/compiler/test/util/JSASTTest.scala | 100 + .../scalajs/compiler/test/util/TestHelpers.scala | 68 + examples/helloworld/HelloWorld.scala | 86 + examples/helloworld/helloworld-2.10-fastopt.html | 19 + examples/helloworld/helloworld-2.10.html | 19 + examples/helloworld/helloworld-2.11-fastopt.html | 19 + examples/helloworld/helloworld-2.11.html | 19 + examples/helloworld/startup.js | 8 + examples/reversi/JSTypes.scala | 87 + examples/reversi/Reversi.scala | 266 ++ examples/reversi/reversi-2.10-fastopt.html | 30 + examples/reversi/reversi-2.10.html | 30 + examples/reversi/reversi-2.11-fastopt.html | 30 + examples/reversi/reversi-2.11.html | 30 + .../testing/src/main/scala/ElementCreator.scala | 7 + .../testing/src/test/scala/CollectionTest.scala | 17 + .../src/test/scala/ElementCreatorTest.scala | 24 + examples/testing/testing-2.10-fastopt.html | 32 + examples/testing/testing-2.10.html | 32 + examples/testing/testing-2.11-fastopt.html | 32 + examples/testing/testing-2.11.html | 32 + ir/src/main/scala/scala/scalajs/ir/ClassKind.scala | 53 + .../main/scala/scala/scalajs/ir/Definitions.scala | 144 + ir/src/main/scala/scala/scalajs/ir/Hashers.scala | 459 +++ .../scala/scala/scalajs/ir/InfoSerializers.scala | 180 + ir/src/main/scala/scala/scalajs/ir/Infos.scala | 118 + ir/src/main/scala/scala/scalajs/ir/Position.scala | 42 + ir/src/main/scala/scala/scalajs/ir/Printers.scala | 709 ++++ .../scala/scala/scalajs/ir/ScalaJSVersions.scala | 25 + .../main/scala/scala/scalajs/ir/Serializers.scala | 790 ++++ ir/src/main/scala/scala/scalajs/ir/Tags.scala | 107 + .../main/scala/scala/scalajs/ir/Transformers.scala | 218 ++ .../main/scala/scala/scalajs/ir/Traversers.scala | 197 + ir/src/main/scala/scala/scalajs/ir/Trees.scala | 536 +++ ir/src/main/scala/scala/scalajs/ir/Types.scala | 182 + ir/src/main/scala/scala/scalajs/ir/Utils.scala | 110 + .../src/main/resources/jasmine-polyfills.js | 9 + .../main/scala/org/scalajs/jasmine/Jasmine.scala | 17 + .../scala/org/scalajs/jasmine/JasmineEnv.scala | 14 + .../org/scalajs/jasmine/JasmineExpectation.scala | 21 + .../main/scala/org/scalajs/jasmine/Results.scala | 13 + .../src/main/scala/org/scalajs/jasmine/Spec.scala | 9 + .../scala/org/scalajs/jasmine/SpecResults.scala | 8 + .../src/main/scala/org/scalajs/jasmine/Suite.scala | 8 + .../scala/org/scalajs/jasmine/SuiteResults.scala | 9 + .../org/scalajs/jasminetest/JasmineTest.scala | 34 + .../scalajs/jasminetest/JasmineTestFramework.scala | 93 + .../scalajs/jasminetest/JasmineTestReporter.scala | 130 + .../org/scalajs/jasminetest/TestSuiteContext.scala | 55 + .../src/main/scala/java/lang/Appendable.scala | 7 + .../src/main/scala/java/lang/AutoCloseable.scala | 5 + javalanglib/src/main/scala/java/lang/Boolean.scala | 61 + javalanglib/src/main/scala/java/lang/Byte.scala | 71 + .../src/main/scala/java/lang/CharSequence.scala | 8 + .../src/main/scala/java/lang/Character.scala | 289 ++ javalanglib/src/main/scala/java/lang/Class.scala | 83 + .../src/main/scala/java/lang/Cloneable.scala | 3 + .../src/main/scala/java/lang/Comparable.scala | 5 + javalanglib/src/main/scala/java/lang/Double.scala | 110 + javalanglib/src/main/scala/java/lang/Float.scala | 96 + .../scala/java/lang/InheritableThreadLocal.scala | 5 + javalanglib/src/main/scala/java/lang/Integer.scala | 129 + javalanglib/src/main/scala/java/lang/Long.scala | 196 + javalanglib/src/main/scala/java/lang/Math.scala | 211 ++ javalanglib/src/main/scala/java/lang/Number.scala | 12 + .../src/main/scala/java/lang/Readable.scala | 7 + .../src/main/scala/java/lang/Runnable.scala | 5 + javalanglib/src/main/scala/java/lang/Runtime.scala | 47 + javalanglib/src/main/scala/java/lang/Short.scala | 73 + .../main/scala/java/lang/StackTraceElement.scala | 55 + .../src/main/scala/java/lang/StringBuffer.scala | 159 + .../src/main/scala/java/lang/StringBuilder.scala | 178 + javalanglib/src/main/scala/java/lang/System.scala | 275 ++ javalanglib/src/main/scala/java/lang/Thread.scala | 16 + .../src/main/scala/java/lang/ThreadLocal.scala | 24 + .../src/main/scala/java/lang/Throwables.scala | 363 ++ javalanglib/src/main/scala/java/lang/Void.scala | 7 + .../scala/java/lang/ref/PhantomReference.scala | 7 + .../src/main/scala/java/lang/ref/Reference.scala | 8 + .../main/scala/java/lang/ref/ReferenceQueue.scala | 3 + .../main/scala/java/lang/ref/SoftReference.scala | 9 + .../main/scala/java/lang/ref/WeakReference.scala | 7 + .../src/main/scala/java/lang/reflect/Array.scala | 176 + .../testsuite/javalibex/DataInputStreamTest.scala | 338 ++ .../testsuite/javalibex/ZipInputStreamTest.scala | 136 + .../src/main/scala/java/io/DataInputStream.scala | 274 ++ .../scala/java/util/zip/InflaterInputStream.scala | 18 + .../src/main/scala/java/util/zip/ZipEntry.scala | 67 + .../main/scala/java/util/zip/ZipInputStream.scala | 87 + .../src/main/scala/java/io/BufferedReader.scala | 145 + .../main/scala/java/io/ByteArrayInputStream.scala | 58 + .../main/scala/java/io/ByteArrayOutputStream.scala | 62 + javalib/src/main/scala/java/io/Closeable.scala | 6 + javalib/src/main/scala/java/io/DataInput.scala | 19 + .../src/main/scala/java/io/FilterInputStream.scala | 24 + .../main/scala/java/io/FilterOutputStream.scala | 16 + javalib/src/main/scala/java/io/Flushable.scala | 5 + javalib/src/main/scala/java/io/InputStream.scala | 53 + .../src/main/scala/java/io/InputStreamReader.scala | 216 ++ javalib/src/main/scala/java/io/OutputStream.scala | 25 + .../main/scala/java/io/OutputStreamWriter.scala | 160 + javalib/src/main/scala/java/io/PrintStream.scala | 218 ++ javalib/src/main/scala/java/io/PrintWriter.scala | 150 + javalib/src/main/scala/java/io/Reader.scala | 60 + javalib/src/main/scala/java/io/Serializable.scala | 3 + javalib/src/main/scala/java/io/StringReader.scala | 69 + javalib/src/main/scala/java/io/StringWriter.scala | 44 + javalib/src/main/scala/java/io/Throwables.scala | 19 + javalib/src/main/scala/java/io/Writer.scala | 45 + javalib/src/main/scala/java/net/URI.scala | 706 ++++ .../main/scala/java/net/URISyntaxException.scala | 15 + javalib/src/main/scala/java/nio/Buffer.scala | 83 + .../scala/java/nio/BufferOverflowException.scala | 3 + .../scala/java/nio/BufferUnderflowException.scala | 3 + javalib/src/main/scala/java/nio/ByteBuffer.scala | 227 ++ javalib/src/main/scala/java/nio/ByteOrder.scala | 15 + javalib/src/main/scala/java/nio/CharBuffer.scala | 228 ++ .../src/main/scala/java/nio/HeapByteBuffer.scala | 129 + .../src/main/scala/java/nio/HeapCharBuffer.scala | 138 + .../main/scala/java/nio/InvalidMarkException.scala | 3 + .../scala/java/nio/ReadOnlyBufferException.scala | 3 + .../src/main/scala/java/nio/StringCharBuffer.scala | 102 + .../nio/charset/CharacterCodingException.scala | 3 + .../src/main/scala/java/nio/charset/Charset.scala | 103 + .../scala/java/nio/charset/CharsetDecoder.scala | 217 ++ .../scala/java/nio/charset/CharsetEncoder.scala | 235 ++ .../java/nio/charset/CoderMalfunctionError.scala | 3 + .../main/scala/java/nio/charset/CoderResult.scala | 78 + .../scala/java/nio/charset/CodingErrorAction.scala | 11 + .../java/nio/charset/MalformedInputException.scala | 9 + .../scala/java/nio/charset/StandardCharsets.scala | 14 + .../nio/charset/UnmappableCharacterException.scala | 9 + .../nio/charset/UnsupportedCharsetException.scala | 6 + javalib/src/main/scala/java/util/Arrays.scala | 401 ++ javalib/src/main/scala/java/util/Comparator.scala | 6 + javalib/src/main/scala/java/util/Date.scala | 147 + javalib/src/main/scala/java/util/Formattable.scala | 5 + .../main/scala/java/util/FormattableFlags.scala | 7 + javalib/src/main/scala/java/util/Formatter.scala | 273 ++ javalib/src/main/scala/java/util/Random.scala | 119 + javalib/src/main/scala/java/util/Throwables.scala | 166 + javalib/src/main/scala/java/util/UUID.scala | 163 + .../java/util/concurrent/ExecutionException.scala | 9 + .../main/scala/java/util/concurrent/Executor.scala | 5 + .../main/scala/java/util/concurrent/TimeUnit.scala | 133 + .../util/concurrent/atomic/AtomicBoolean.scala | 33 + .../util/concurrent/atomic/AtomicInteger.scala | 63 + .../java/util/concurrent/atomic/AtomicLong.scala | 61 + .../util/concurrent/atomic/AtomicReference.scala | 34 + .../main/scala/java/util/regex/MatchResult.scala | 13 + .../src/main/scala/java/util/regex/Matcher.scala | 274 ++ .../src/main/scala/java/util/regex/Pattern.scala | 154 + .../main/scala/scala/runtime/ArrayRuntime.scala | 16 + .../src/main/scala/scala/runtime/BoxedUnit.scala | 18 + .../src/main/scala/scala/runtime/RefTypes.scala | 165 + .../src/main/scala/scala/runtime/Statics.scala | 89 + .../scalajs/concurrent/JSExecutionContext.scala | 24 + .../scalajs/concurrent/QueueExecutionContext.scala | 17 + .../concurrent/RunNowExcecutionContext.scala | 14 + .../src/main/scala/scala/scalajs/js/Array.scala | 173 + .../src/main/scala/scala/scalajs/js/ArrayOps.scala | 119 + library/src/main/scala/scala/scalajs/js/Date.scala | 225 ++ .../main/scala/scala/scalajs/js/Dictionary.scala | 92 + .../src/main/scala/scala/scalajs/js/Error.scala | 114 + .../src/main/scala/scala/scalajs/js/Function.scala | 194 + .../main/scala/scala/scalajs/js/GlobalScope.scala | 21 + .../src/main/scala/scala/scalajs/js/JSApp.scala | 20 + .../main/scala/scala/scalajs/js/JSArrayOps.scala | 264 ++ .../main/scala/scala/scalajs/js/JSConverters.scala | 54 + library/src/main/scala/scala/scalajs/js/JSON.scala | 51 + .../scala/scalajs/js/JavaScriptException.scala | 20 + library/src/main/scala/scala/scalajs/js/Math.scala | 277 ++ .../main/scala/scala/scalajs/js/Primitives.scala | 872 +++++ .../scala/scalajs/js/PropertyDescriptor.scala | 20 + .../src/main/scala/scala/scalajs/js/RegExp.scala | 108 + .../main/scala/scala/scalajs/js/ThisFunction.scala | 160 + .../src/main/scala/scala/scalajs/js/UndefOr.scala | 254 ++ .../main/scala/scala/scalajs/js/WrappedArray.scala | 92 + .../scala/scala/scalajs/js/WrappedDictionary.scala | 89 + .../scalajs/js/annotation/JSBracketAccess.scala | 17 + .../scala/scalajs/js/annotation/JSExport.scala | 19 + .../scala/scalajs/js/annotation/JSExportAll.scala | 21 + .../js/annotation/JSExportDescendentClasses.scala | 20 + .../js/annotation/JSExportDescendentObjects.scala | 20 + .../scalajs/js/annotation/JSExportNamed.scala | 38 + .../scala/scala/scalajs/js/annotation/JSName.scala | 17 + .../scala/scala/scalajs/js/annotation/README.md | 3 + .../scala/scalajs/js/annotation/RawJSType.scala | 23 + .../src/main/scala/scala/scalajs/js/package.scala | 161 + .../scala/scalajs/js/typedarray/ArrayBuffer.scala | 17 + .../js/typedarray/ArrayBufferInputStream.scala | 88 + .../scalajs/js/typedarray/ArrayBufferView.scala | 14 + .../scala/scalajs/js/typedarray/DataView.scala | 26 + .../scala/scalajs/js/typedarray/Float32Array.scala | 24 + .../scala/scalajs/js/typedarray/Float64Array.scala | 24 + .../scala/scalajs/js/typedarray/Int16Array.scala | 24 + .../scala/scalajs/js/typedarray/Int32Array.scala | 24 + .../scala/scalajs/js/typedarray/Int8Array.scala | 24 + .../scala/scalajs/js/typedarray/TypedArray.scala | 46 + .../scala/scalajs/js/typedarray/Uint16Array.scala | 24 + .../scala/scalajs/js/typedarray/Uint32Array.scala | 24 + .../scala/scalajs/js/typedarray/Uint8Array.scala | 24 + .../scalajs/js/typedarray/Uint8ClampedArray.scala | 24 + .../scala/scalajs/js/typedarray/package.scala | 145 + .../scala/scalajs/niocharset/ISO_8859_1.scala | 19 + .../ISO_8859_1_And_US_ASCII_Common.scala | 197 + .../scalajs/niocharset/StandardCharsets.scala | 42 + .../scala/scala/scalajs/niocharset/US_ASCII.scala | 19 + .../scala/scala/scalajs/niocharset/UTF_16.scala | 17 + .../scala/scala/scalajs/niocharset/UTF_16BE.scala | 17 + .../scala/scala/scalajs/niocharset/UTF_16LE.scala | 17 + .../scala/scalajs/niocharset/UTF_16_Common.scala | 205 + .../scala/scala/scalajs/niocharset/UTF_8.scala | 455 +++ .../scala/scalajs/runtime/AnonFunctions.scala | 119 + .../main/scala/scala/scalajs/runtime/Bits.scala | 240 ++ .../scalajs/runtime/BooleanReflectiveCall.scala | 31 + .../scalajs/runtime/IntegerReflectiveCall.scala | 87 + .../scalajs/runtime/NumberReflectiveCall.scala | 162 + .../scala/scala/scalajs/runtime/RuntimeLong.scala | 686 ++++ .../scala/scalajs/runtime/RuntimeString.scala | 338 ++ .../scala/scala/scalajs/runtime/StackTrace.scala | 507 +++ .../scalajs/runtime/UndefinedBehaviorError.scala | 23 + .../main/scala/scala/scalajs/runtime/package.scala | 176 + .../testsuite/noircheck/DummyParentsTest.scala | 34 + .../partest/scalajs/2.11.0/BlacklistedTests.txt | 891 +++++ .../partest/scalajs/2.11.0/BuglistedTests.txt | 4 + .../tools/partest/scalajs/2.11.0/NoDCEWarn.txt | 8 + .../partest/scalajs/2.11.0/WhitelistedTests.txt | 2929 +++++++++++++++ .../scalajs/2.11.0/neg/t7494-no-options.check | 42 + .../scalajs/2.11.0/run/Course-2002-01.check | 37 + .../scalajs/2.11.0/run/Course-2002-02.check | 187 + .../scalajs/2.11.0/run/Course-2002-04.check | 64 + .../scalajs/2.11.0/run/Course-2002-08.check | 171 + .../scalajs/2.11.0/run/Course-2002-09.check | 50 + .../scalajs/2.11.0/run/Course-2002-10.check | 46 + .../tools/partest/scalajs/2.11.0/run/bugs.sem | 1 + .../scalajs/2.11.0/run/dynamic-anyval.check | 4 + .../partest/scalajs/2.11.0/run/impconvtimes.check | 1 + .../tools/partest/scalajs/2.11.0/run/imports.check | 21 + .../tools/partest/scalajs/2.11.0/run/issue192.sem | 1 + .../scalajs/2.11.0/run/macro-bundle-static.check | 6 + .../scalajs/2.11.0/run/macro-bundle-toplevel.check | 6 + .../2.11.0/run/macro-bundle-whitebox-decl.check | 6 + .../tools/partest/scalajs/2.11.0/run/misc.check | 62 + .../partest/scalajs/2.11.0/run/promotion.check | 4 + .../tools/partest/scalajs/2.11.0/run/runtime.check | 70 + .../partest/scalajs/2.11.0/run/spec-self.check | 2 + .../partest/scalajs/2.11.0/run/structural.check | 37 + .../partest/scalajs/2.11.0/run/t0421-new.check | 3 + .../partest/scalajs/2.11.0/run/t0421-old.check | 3 + .../tools/partest/scalajs/2.11.0/run/t1503.sem | 1 + .../tools/partest/scalajs/2.11.0/run/t3702.check | 2 + .../tools/partest/scalajs/2.11.0/run/t4148.sem | 1 + .../tools/partest/scalajs/2.11.0/run/t4617.check | 1 + .../tools/partest/scalajs/2.11.0/run/t5552.check | 2 + .../tools/partest/scalajs/2.11.0/run/t5568.check | 9 + .../tools/partest/scalajs/2.11.0/run/t5629b.check | 10 + .../tools/partest/scalajs/2.11.0/run/t5680.check | 3 + .../tools/partest/scalajs/2.11.0/run/t5866.check | 2 + .../tools/partest/scalajs/2.11.0/run/t6102.check | 27 + .../scalajs/2.11.0/run/t6318_primitives.check | 36 + .../tools/partest/scalajs/2.11.0/run/t6662.check | 1 + .../tools/partest/scalajs/2.11.0/run/t7657.check | 3 + .../tools/partest/scalajs/2.11.0/run/t7763.sem | 1 + .../scalajs/2.11.0/run/try-catch-unify.check | 4 + .../scalajs/2.11.0/run/virtpatmat_switch.check | 7 + .../scalajs/2.11.0/run/virtpatmat_typetag.check | 10 + .../partest/scalajs/2.11.1/BlacklistedTests.txt | 899 +++++ .../partest/scalajs/2.11.1/BuglistedTests.txt | 4 + .../tools/partest/scalajs/2.11.1/NoDCEWarn.txt | 8 + .../partest/scalajs/2.11.1/WhitelistedTests.txt | 2949 +++++++++++++++ .../scalajs/2.11.1/neg/t7494-no-options.check | 42 + .../scalajs/2.11.1/run/Course-2002-01.check | 37 + .../scalajs/2.11.1/run/Course-2002-02.check | 187 + .../scalajs/2.11.1/run/Course-2002-04.check | 64 + .../scalajs/2.11.1/run/Course-2002-08.check | 171 + .../scalajs/2.11.1/run/Course-2002-09.check | 50 + .../scalajs/2.11.1/run/Course-2002-10.check | 46 + .../tools/partest/scalajs/2.11.1/run/bugs.sem | 1 + .../scalajs/2.11.1/run/dynamic-anyval.check | 4 + .../partest/scalajs/2.11.1/run/impconvtimes.check | 1 + .../tools/partest/scalajs/2.11.1/run/imports.check | 21 + .../tools/partest/scalajs/2.11.1/run/issue192.sem | 1 + .../scalajs/2.11.1/run/macro-bundle-static.check | 6 + .../scalajs/2.11.1/run/macro-bundle-toplevel.check | 6 + .../2.11.1/run/macro-bundle-whitebox-decl.check | 6 + .../tools/partest/scalajs/2.11.1/run/misc.check | 62 + .../partest/scalajs/2.11.1/run/promotion.check | 4 + .../tools/partest/scalajs/2.11.1/run/runtime.check | 70 + .../partest/scalajs/2.11.1/run/spec-self.check | 2 + .../partest/scalajs/2.11.1/run/structural.check | 37 + .../partest/scalajs/2.11.1/run/t0421-new.check | 3 + .../partest/scalajs/2.11.1/run/t0421-old.check | 3 + .../tools/partest/scalajs/2.11.1/run/t1503.sem | 1 + .../tools/partest/scalajs/2.11.1/run/t3702.check | 2 + .../tools/partest/scalajs/2.11.1/run/t4148.sem | 1 + .../tools/partest/scalajs/2.11.1/run/t4617.check | 1 + .../tools/partest/scalajs/2.11.1/run/t5552.check | 2 + .../tools/partest/scalajs/2.11.1/run/t5568.check | 9 + .../tools/partest/scalajs/2.11.1/run/t5629b.check | 10 + .../tools/partest/scalajs/2.11.1/run/t5680.check | 3 + .../tools/partest/scalajs/2.11.1/run/t5866.check | 2 + .../tools/partest/scalajs/2.11.1/run/t6102.check | 27 + .../scalajs/2.11.1/run/t6318_primitives.check | 36 + .../tools/partest/scalajs/2.11.1/run/t6662.check | 1 + .../tools/partest/scalajs/2.11.1/run/t7657.check | 3 + .../tools/partest/scalajs/2.11.1/run/t7763.sem | 1 + .../tools/partest/scalajs/2.11.1/run/t8570a.check | 1 + .../scalajs/2.11.1/run/try-catch-unify.check | 4 + .../scalajs/2.11.1/run/virtpatmat_switch.check | 7 + .../scalajs/2.11.1/run/virtpatmat_typetag.check | 10 + .../partest/scalajs/2.11.2/BlacklistedTests.txt | 914 +++++ .../partest/scalajs/2.11.2/BuglistedTests.txt | 4 + .../tools/partest/scalajs/2.11.2/NoDCEWarn.txt | 8 + .../partest/scalajs/2.11.2/WhitelistedTests.txt | 2976 +++++++++++++++ .../scalajs/2.11.2/neg/t7494-no-options.check | 42 + .../scalajs/2.11.2/run/Course-2002-01.check | 37 + .../scalajs/2.11.2/run/Course-2002-02.check | 187 + .../scalajs/2.11.2/run/Course-2002-04.check | 64 + .../scalajs/2.11.2/run/Course-2002-08.check | 171 + .../scalajs/2.11.2/run/Course-2002-09.check | 50 + .../scalajs/2.11.2/run/Course-2002-10.check | 46 + .../tools/partest/scalajs/2.11.2/run/bugs.sem | 1 + .../scalajs/2.11.2/run/dynamic-anyval.check | 4 + .../partest/scalajs/2.11.2/run/impconvtimes.check | 1 + .../tools/partest/scalajs/2.11.2/run/imports.check | 21 + .../tools/partest/scalajs/2.11.2/run/issue192.sem | 1 + .../scalajs/2.11.2/run/macro-bundle-static.check | 6 + .../scalajs/2.11.2/run/macro-bundle-toplevel.check | 6 + .../2.11.2/run/macro-bundle-whitebox-decl.check | 6 + .../tools/partest/scalajs/2.11.2/run/misc.check | 62 + .../partest/scalajs/2.11.2/run/promotion.check | 4 + .../tools/partest/scalajs/2.11.2/run/runtime.check | 70 + .../partest/scalajs/2.11.2/run/spec-self.check | 2 + .../partest/scalajs/2.11.2/run/structural.check | 37 + .../partest/scalajs/2.11.2/run/t0421-new.check | 3 + .../partest/scalajs/2.11.2/run/t0421-old.check | 3 + .../tools/partest/scalajs/2.11.2/run/t1503.sem | 1 + .../tools/partest/scalajs/2.11.2/run/t3702.check | 2 + .../tools/partest/scalajs/2.11.2/run/t4148.sem | 1 + .../tools/partest/scalajs/2.11.2/run/t4617.check | 1 + .../tools/partest/scalajs/2.11.2/run/t5552.check | 2 + .../tools/partest/scalajs/2.11.2/run/t5568.check | 9 + .../tools/partest/scalajs/2.11.2/run/t5629b.check | 10 + .../tools/partest/scalajs/2.11.2/run/t5680.check | 3 + .../tools/partest/scalajs/2.11.2/run/t5866.check | 2 + .../tools/partest/scalajs/2.11.2/run/t6102.check | 27 + .../scalajs/2.11.2/run/t6318_primitives.check | 54 + .../tools/partest/scalajs/2.11.2/run/t6662.check | 1 + .../tools/partest/scalajs/2.11.2/run/t7657.check | 3 + .../tools/partest/scalajs/2.11.2/run/t7763.sem | 1 + .../tools/partest/scalajs/2.11.2/run/t8570a.check | 1 + .../scalajs/2.11.2/run/try-catch-unify.check | 4 + .../scalajs/2.11.2/run/virtpatmat_switch.check | 7 + .../scalajs/2.11.2/run/virtpatmat_typetag.check | 10 + .../scala/scala/tools/nsc/MainGenericRunner.scala | 220 ++ .../tools/partest/scalajs/PartestInterface.scala | 119 + .../tools/partest/scalajs/ScalaJSPartest.scala | 203 + .../partest/scalajs/ScalaJSPartestOptions.scala | 109 + project/ExternalCompile.scala | 116 + project/JavaLangObject.scala | 243 ++ project/JavaLangString.scala | 215 ++ project/ScalaJSBuild.scala | 891 +++++ project/build.properties | 1 + project/build.sbt | 48 + project/project/ScalaJSEnvGenerator.scala | 31 + sbt-plugin-test/README.md | 7 + sbt-plugin-test/build.sbt | 44 + sbt-plugin-test/jetty9/src/main/resources/test.txt | 1 + .../noDOM/src/main/scala/sbttest/noDOM/Lib.scala | 11 + .../src/main/scala/sbttest/noDOM/TestApp.scala | 12 + .../src/test/scala/sbttest/noDOM/LibTest.scala | 19 + sbt-plugin-test/project/Jetty9Test.scala | 83 + sbt-plugin-test/project/build.properties | 1 + sbt-plugin-test/project/build.sbt | 4 + sbt-plugin-test/project/project/build.sbt | 1 + .../src/main/scala/sbttest/withDOM/Lib.scala | 28 + .../src/main/scala/sbttest/withDOM/TestApp.scala | 14 + .../src/test/scala/sbttest/withDOM/LibTest.scala | 24 + .../scala/scalajs/sbtplugin/AbstractJSDeps.scala | 81 + .../scala/scala/scalajs/sbtplugin/Implicits.scala | 34 + .../scala/scala/scalajs/sbtplugin/JSUtils.scala | 35 + .../scala/scalajs/sbtplugin/LoggerJSConsole.scala | 18 + .../scala/scalajs/sbtplugin/OptimizerOptions.scala | 74 + .../scalajs/sbtplugin/ScalaJSCrossVersion.scala | 48 + .../scala/scalajs/sbtplugin/ScalaJSPlugin.scala | 179 + .../scalajs/sbtplugin/ScalaJSPluginInternal.scala | 598 +++ .../main/scala/scala/scalajs/sbtplugin/Stage.scala | 18 + .../scalajs/sbtplugin/env/ExternalJSEnv.scala | 200 + .../sbtplugin/env/VirtualFileMaterializer.scala | 67 + .../scalajs/sbtplugin/env/nodejs/NodeJSEnv.scala | 306 ++ .../env/phantomjs/JettyWebsocketManager.scala | 126 + .../sbtplugin/env/phantomjs/PhantomJSEnv.scala | 466 +++ .../env/phantomjs/PhantomJettyClassLoader.scala | 63 + .../env/phantomjs/WebsocketListener.scala | 10 + .../sbtplugin/env/phantomjs/WebsocketManager.scala | 10 + .../sbtplugin/env/rhino/LazyScalaJSScope.scala | 96 + .../scalajs/sbtplugin/env/rhino/RhinoJSEnv.scala | 303 ++ .../sbtplugin/env/rhino/ScalaJSCoreLib.scala | 173 + .../scalajs/sbtplugin/env/rhino/package.scala | 42 + .../sbtplugin/impl/DependencyBuilders.scala | 99 + .../scala/scalajs/sbtplugin/testing/Events.scala | 35 + .../sbtplugin/testing/JSClasspathLoader.scala | 15 + .../testing/SbtTestLoggerAccWrapper.scala | 22 + .../scalajs/sbtplugin/testing/TestException.scala | 9 + .../scalajs/sbtplugin/testing/TestFramework.scala | 52 + .../sbtplugin/testing/TestOutputConsole.scala | 190 + .../scalajs/sbtplugin/testing/TestRunner.scala | 37 + .../scala/scalajs/sbtplugin/testing/TestTask.scala | 110 + .../scalajs/sbtplugin/test/env/AsyncTests.scala | 37 + .../scalajs/sbtplugin/test/env/ComTests.scala | 206 ++ .../scalajs/sbtplugin/test/env/JSEnvTest.scala | 44 + .../scalajs/sbtplugin/test/env/NodeJSTest.scala | 54 + .../scalajs/sbtplugin/test/env/PhantomJSTest.scala | 21 + .../sbtplugin/test/env/RhinoJSEnvTest.scala | 9 + .../sbtplugin/test/env/StoreJSConsole.scala | 14 + .../scalajs/sbtplugin/test/env/StoreLogger.scala | 29 + scalalib/overrides-2.10/scala/Console.scala | 468 +++ .../scala/collection/immutable/NumericRange.scala | 285 ++ .../scala/collection/immutable/Range.scala | 424 +++ .../scala/collection/mutable/Buffer.scala | 50 + .../overrides-2.10/scala/compat/Platform.scala | 133 + scalalib/overrides-2.10/scala/package.scala | 138 + scalalib/overrides-2.11/scala/Console.scala | 222 ++ .../scala/collection/immutable/NumericRange.scala | 346 ++ .../scala/collection/immutable/Range.scala | 516 +++ .../scala/collection/mutable/Buffer.scala | 51 + .../overrides-2.11/scala/compat/Platform.scala | 132 + scalalib/overrides-2.11/scala/package.scala | 133 + scalalib/overrides/scala/App.scala | 83 + scalalib/overrides/scala/Enumeration.scala | 284 ++ scalalib/overrides/scala/Symbol.scala | 117 + .../scala/concurrent/impl/AbstractPromise.scala | 29 + scalalib/overrides/scala/math/ScalaNumber.scala | 21 + .../overrides/scala/runtime/BoxesRunTime.scala | 124 + .../scala/util/control/NoStackTrace.scala | 33 + scripts/assemble-cli.sh | 96 + scripts/build-all-js.sh | 15 + scripts/publish-to-bintray.sh | 35 + .../scalajs/js/annotation/ExportAnnotations.scala | 24 + .../main/scala/scala/scalajs/testbridge/Test.scala | 17 + .../scala/scalajs/testbridge/TestFramework.scala | 36 + .../scala/scalajs/testbridge/TestOutput.scala | 34 + .../scala/scalajs/testbridge/TestOutputLog.scala | 16 + .../testbridge/internal/ConsoleTestOutput.scala | 118 + test-suite/run-jasmine-tests.js | 18 + test-suite/scalajs-test-suite-2.10-fastopt.html | 15 + test-suite/scalajs-test-suite-2.10.html | 15 + test-suite/scalajs-test-suite-2.11-fastopt.html | 15 + test-suite/scalajs-test-suite-2.11.html | 15 + .../scalajs/testsuite/jsinterop/ArraySAMTest.scala | 32 + .../src/test/resources/SourceMapTestTemplate.scala | 655 ++++ .../scalajs/testsuite/compiler/BooleanTest.scala | 41 + .../scalajs/testsuite/compiler/ByteTest.scala | 40 + .../scalajs/testsuite/compiler/CharTest.scala | 40 + .../scalajs/testsuite/compiler/FloatTest.scala | 73 + .../InstanceTestsHijackedBoxedClassesTest.scala | 98 + .../scala/scalajs/testsuite/compiler/IntTest.scala | 206 ++ .../testsuite/compiler/InteroperabilityTest.scala | 528 +++ .../scalajs/testsuite/compiler/LongTest.scala | 159 + .../scalajs/testsuite/compiler/OptimizerTest.scala | 43 + .../testsuite/compiler/ReflectionTest.scala | 69 + .../testsuite/compiler/ReflectiveCallTest.scala | 330 ++ .../testsuite/compiler/RegressionTest.scala | 287 ++ .../testsuite/compiler/RuntimeTypesTest.scala | 77 + .../scalajs/testsuite/compiler/ShortTest.scala | 38 + .../scalajs/testsuite/compiler/UnitTest.scala | 47 + .../scalajs/testsuite/javalib/ArraysTest.scala | 749 ++++ .../scalajs/testsuite/javalib/AtomicTest.scala | 119 + .../scalajs/testsuite/javalib/BooleanTest.scala | 62 + .../scala/scalajs/testsuite/javalib/ByteTest.scala | 64 + .../scalajs/testsuite/javalib/CharacterTest.scala | 672 ++++ .../scalajs/testsuite/javalib/ClassTest.scala | 30 + .../scala/scalajs/testsuite/javalib/DateTest.scala | 87 + .../scalajs/testsuite/javalib/DoubleTest.scala | 217 ++ .../scalajs/testsuite/javalib/FloatTest.scala | 221 ++ .../scalajs/testsuite/javalib/FormatterTest.scala | 241 ++ .../scalajs/testsuite/javalib/IntegerTest.scala | 161 + .../scala/scalajs/testsuite/javalib/LongTest.scala | 178 + .../scala/scalajs/testsuite/javalib/MathTest.scala | 142 + .../javalib/MockByteArrayOutputStream.scala | 47 + .../scalajs/testsuite/javalib/ObjectTest.scala | 72 + .../testsuite/javalib/OutputStreamWriterTest.scala | 134 + .../testsuite/javalib/PrintStreamTest.scala | 296 ++ .../testsuite/javalib/PrintWriterTest.scala | 287 ++ .../scalajs/testsuite/javalib/RandomTest.scala | 225 ++ .../scalajs/testsuite/javalib/ReadersTest.scala | 244 ++ .../scalajs/testsuite/javalib/ReferenceTest.scala | 28 + .../scalajs/testsuite/javalib/RegexTest.scala | 397 ++ .../scalajs/testsuite/javalib/ShortTest.scala | 66 + .../testsuite/javalib/StackTraceElementTest.scala | 29 + .../scalajs/testsuite/javalib/StreamsTest.scala | 308 ++ .../testsuite/javalib/StringBufferTest.scala | 219 ++ .../scalajs/testsuite/javalib/StringTest.scala | 237 ++ .../scalajs/testsuite/javalib/SystemTest.scala | 118 + .../scalajs/testsuite/javalib/ThrowablesTest.scala | 90 + .../scalajs/testsuite/javalib/TimeUnitTest.scala | 135 + .../scala/scalajs/testsuite/javalib/URITest.scala | 312 ++ .../scala/scalajs/testsuite/javalib/UUIDTest.scala | 180 + .../scalajs/testsuite/jsinterop/ArrayTest.scala | 94 + .../scalajs/testsuite/jsinterop/AsyncTest.scala | 121 + .../testsuite/jsinterop/DictionaryTest.scala | 79 + .../scalajs/testsuite/jsinterop/DynamicTest.scala | 187 + .../scalajs/testsuite/jsinterop/ExportsTest.scala | 1075 ++++++ .../scalajs/testsuite/jsinterop/FunctionTest.scala | 41 + .../testsuite/jsinterop/MiscInteropTest.scala | 89 + .../testsuite/jsinterop/RuntimeLongTest.scala | 140 + .../testsuite/jsinterop/StrangeNamedTests.scala | 28 + .../testsuite/jsinterop/ThisFunctionTest.scala | 70 + .../scalajs/testsuite/jsinterop/UndefOrTest.scala | 191 + .../scalajs/testsuite/library/ArrayOpsTest.scala | 117 + .../testsuite/library/WrappedArrayTest.scala | 118 + .../testsuite/library/WrappedDictionaryTest.scala | 106 + .../testsuite/niobuffer/BaseBufferTest.scala | 178 + .../testsuite/niobuffer/ByteBufferTest.scala | 330 ++ .../testsuite/niobuffer/CharBufferTest.scala | 388 ++ .../testsuite/niocharset/BaseCharsetTest.scala | 234 ++ .../scalajs/testsuite/niocharset/CharsetTest.scala | 74 + .../scalajs/testsuite/niocharset/Latin1Test.scala | 91 + .../scalajs/testsuite/niocharset/USASCIITest.scala | 93 + .../scalajs/testsuite/niocharset/UTF16Test.scala | 155 + .../scalajs/testsuite/niocharset/UTF8Test.scala | 240 ++ .../testsuite/scalalib/EnumerationTest.scala | 95 + .../scalajs/testsuite/scalalib/RangesTest.scala | 27 + .../scalajs/testsuite/scalalib/SymbolTest.scala | 63 + .../typedarray/ArrayBufferInputStreamTest.scala | 36 + .../testsuite/typedarray/ArrayBufferTest.scala | 39 + .../scalajs/testsuite/typedarray/ArraysTest.scala | 45 + .../testsuite/typedarray/DataViewTest.scala | 275 ++ .../typedarray/TypedArrayConversionTest.scala | 197 + .../testsuite/typedarray/TypedArrayTest.scala | 332 ++ .../scala/scalajs/testsuite/utils/JSUtils.scala | 26 + .../scalajs/testsuite/utils/TestDetector.scala | 33 + .../tools/classpath/builder/NodeFileSystem.scala | 67 + .../scala/scalajs/tools/io/NodeVirtualFiles.scala | 62 + .../main/scala/scala/scalajs/tools/json/Impl.scala | 36 + .../scalajs/tools/js/test/JasmineReporter.scala | 71 + .../scala/scalajs/tools/js/test/QuickLinker.scala | 37 + .../classpath/builder/JarLibClasspathBuilder.scala | 13 + .../builder/PartialClasspathBuilder.scala | 38 + .../classpath/builder/PhysicalFileSystem.scala | 41 + .../scala/scalajs/tools/io/FileVirtualFiles.scala | 157 + .../main/scala/scala/scalajs/tools/json/Impl.scala | 38 + .../tools/optimizer/ClosureAstBuilder.scala | 47 + .../tools/optimizer/ClosureAstTransformer.scala | 397 ++ .../scalajs/tools/optimizer/ConcurrencyUtils.scala | 74 + .../tools/optimizer/LoggerErrorManager.scala | 38 + .../scalajs/tools/optimizer/ParIncOptimizer.scala | 188 + .../tools/optimizer/ScalaJSClosureOptimizer.scala | 216 ++ .../scalajs/tools/sourcemap/SourceMapper.scala | 88 + tools/jvm/src/test/resources/test.jar | 0 .../test/ClasspathElementsTraverserTest.scala | 42 + .../classpath/builder/test/JarBuilderTest.scala | 74 + tools/scalajsenv.js | 772 ++++ .../tools/classpath/CompleteClasspath.scala | 35 + .../tools/classpath/ComplianceRequirement.scala | 7 + .../scala/scalajs/tools/classpath/Exceptions.scala | 42 + .../scalajs/tools/classpath/IRClasspath.scala | 69 + .../scalajs/tools/classpath/LinkedClasspath.scala | 26 + .../scalajs/tools/classpath/PartialClasspath.scala | 99 + .../tools/classpath/ResolvedJSDependency.scala | 10 + .../builder/AbstractJarLibClasspathBuilder.scala | 53 + .../builder/AbstractPartialClasspathBuilder.scala | 47 + .../builder/ClasspathContentHandler.scala | 25 + .../builder/ClasspathElementsTraverser.scala | 38 + .../tools/classpath/builder/DirTraverser.scala | 60 + .../tools/classpath/builder/FileSystem.scala | 57 + .../tools/classpath/builder/JarTraverser.scala | 85 + .../scala/scalajs/tools/corelib/CoreJSLibs.scala | 113 + .../scala/scala/scalajs/tools/env/AsyncJSEnv.scala | 19 + .../scala/scalajs/tools/env/AsyncJSRunner.scala | 19 + .../scala/scala/scalajs/tools/env/ComJSEnv.scala | 38 + .../scala/scalajs/tools/env/ComJSRunner.scala | 22 + .../scala/scalajs/tools/env/ConsoleJSConsole.scala | 17 + .../scala/scala/scalajs/tools/env/JSConsole.scala | 15 + .../main/scala/scala/scalajs/tools/env/JSEnv.scala | 20 + .../scala/scala/scalajs/tools/env/JSRunner.scala | 15 + .../scala/scalajs/tools/env/NullJSConsole.scala | 5 + .../scala/scala/scalajs/tools/io/CacheUtils.scala | 52 + .../src/main/scala/scala/scalajs/tools/io/IO.scala | 116 + .../scala/scala/scalajs/tools/io/MemFiles.scala | 105 + .../scala/scalajs/tools/io/VirtualFiles.scala | 169 + .../scalajs/tools/javascript/JSDesugaring.scala | 1525 ++++++++ .../scala/scalajs/tools/javascript/LongImpl.scala | 116 + .../scala/scalajs/tools/javascript/Printers.scala | 420 +++ .../tools/javascript/ScalaJSClassEmitter.scala | 569 +++ .../scala/scalajs/tools/javascript/TreeDSL.scala | 50 + .../scala/scalajs/tools/javascript/Trees.scala | 194 + .../scala/scalajs/tools/jsdep/Exceptions.scala | 59 + .../scalajs/tools/jsdep/FlatJSDependency.scala | 17 + .../scala/scalajs/tools/jsdep/JSDependency.scala | 66 + .../scalajs/tools/jsdep/JSDependencyManifest.scala | 130 + .../scala/scala/scalajs/tools/jsdep/Origin.scala | 28 + .../scala/scalajs/tools/jsdep/ResolutionInfo.scala | 21 + .../scalajs/tools/json/AbstractJSONImpl.scala | 32 + .../scalajs/tools/json/JSONDeserializer.scala | 30 + .../scala/scalajs/tools/json/JSONObjBuilder.scala | 20 + .../scalajs/tools/json/JSONObjExtractor.scala | 13 + .../scala/scalajs/tools/json/JSONSerializer.scala | 32 + .../scala/scala/scalajs/tools/json/package.scala | 26 + .../scala/scala/scalajs/tools/logging/Level.scala | 24 + .../scala/scala/scalajs/tools/logging/Logger.scala | 25 + .../scala/scalajs/tools/logging/NullLogger.scala | 7 + .../scalajs/tools/logging/ScalaConsoleLogger.scala | 15 + .../scala/scalajs/tools/optimizer/Analyzer.scala | 587 +++ .../scalajs/tools/optimizer/GenIncOptimizer.scala | 921 +++++ .../scala/scalajs/tools/optimizer/IRChecker.scala | 854 +++++ .../scalajs/tools/optimizer/IncOptimizer.scala | 158 + .../scalajs/tools/optimizer/JSTreeBuilder.scala | 16 + .../scalajs/tools/optimizer/OptimizerCore.scala | 3572 ++++++++++++++++++ .../scalajs/tools/optimizer/ScalaJSOptimizer.scala | 552 +++ .../scala/scalajs/tools/sem/CheckedBehavior.scala | 24 + .../scala/scala/scalajs/tools/sem/Semantics.scala | 97 + .../scalajs/tools/sourcemap/JSFileBuilder.scala | 144 + .../scalajs/tools/sourcemap/SourceMapWriter.scala | 213 ++ 657 files changed, 86304 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 TESTING create mode 100755 ci/check-partest-coverage.sh create mode 100755 ci/checksizes.sh create mode 100644 ci/matrix.xml create mode 100644 ci/scalajs-matrix-build.groovy create mode 100755 cli/src/main/resources/scalajsc create mode 100644 cli/src/main/resources/scalajsc.bat create mode 100755 cli/src/main/resources/scalajsld create mode 100644 cli/src/main/resources/scalajsld.bat create mode 100755 cli/src/main/resources/scalajsp create mode 100644 cli/src/main/resources/scalajsp.bat create mode 100644 cli/src/main/scala/scala/scalajs/cli/Scalajsld.scala create mode 100644 cli/src/main/scala/scala/scalajs/cli/Scalajsp.scala create mode 100644 compiler/src/main/resources/scalac-plugin.xml create mode 100644 compiler/src/main/scala/scala/scalajs/compiler/ClassInfos.scala create mode 100644 compiler/src/main/scala/scala/scalajs/compiler/Compat210Component.scala create mode 100644 compiler/src/main/scala/scala/scalajs/compiler/GenJSCode.scala create mode 100644 compiler/src/main/scala/scala/scalajs/compiler/GenJSExports.scala create mode 100644 compiler/src/main/scala/scala/scalajs/compiler/GenJSFiles.scala create mode 100644 compiler/src/main/scala/scala/scalajs/compiler/JSDefinitions.scala create mode 100644 compiler/src/main/scala/scala/scalajs/compiler/JSEncoding.scala create mode 100644 compiler/src/main/scala/scala/scalajs/compiler/JSGlobalAddons.scala create mode 100644 compiler/src/main/scala/scala/scalajs/compiler/JSPrimitives.scala create mode 100644 compiler/src/main/scala/scala/scalajs/compiler/JSTreeExtractors.scala create mode 100644 compiler/src/main/scala/scala/scalajs/compiler/PrepJSExports.scala create mode 100644 compiler/src/main/scala/scala/scalajs/compiler/PrepJSInterop.scala create mode 100644 compiler/src/main/scala/scala/scalajs/compiler/ScalaJSOptions.scala create mode 100644 compiler/src/main/scala/scala/scalajs/compiler/ScalaJSPlugin.scala create mode 100644 compiler/src/main/scala/scala/scalajs/compiler/TypeKinds.scala create mode 100644 compiler/src/main/scala/scala/scalajs/compiler/util/ScopedVar.scala create mode 100644 compiler/src/test/scala/scala/scalajs/compiler/test/DiverseErrorsTest.scala create mode 100644 compiler/src/test/scala/scala/scalajs/compiler/test/EnumerationInteropTest.scala create mode 100644 compiler/src/test/scala/scala/scalajs/compiler/test/JSDynamicLiteralTest.scala create mode 100644 compiler/src/test/scala/scala/scalajs/compiler/test/JSExportASTTest.scala create mode 100644 compiler/src/test/scala/scala/scalajs/compiler/test/JSExportTest.scala create mode 100644 compiler/src/test/scala/scala/scalajs/compiler/test/JSInteropTest.scala create mode 100644 compiler/src/test/scala/scala/scalajs/compiler/test/OptimizationTest.scala create mode 100644 compiler/src/test/scala/scala/scalajs/compiler/test/PositionTest.scala create mode 100644 compiler/src/test/scala/scala/scalajs/compiler/test/util/DirectTest.scala create mode 100644 compiler/src/test/scala/scala/scalajs/compiler/test/util/JSASTTest.scala create mode 100644 compiler/src/test/scala/scala/scalajs/compiler/test/util/TestHelpers.scala create mode 100644 examples/helloworld/HelloWorld.scala create mode 100644 examples/helloworld/helloworld-2.10-fastopt.html create mode 100644 examples/helloworld/helloworld-2.10.html create mode 100644 examples/helloworld/helloworld-2.11-fastopt.html create mode 100644 examples/helloworld/helloworld-2.11.html create mode 100644 examples/helloworld/startup.js create mode 100644 examples/reversi/JSTypes.scala create mode 100644 examples/reversi/Reversi.scala create mode 100644 examples/reversi/reversi-2.10-fastopt.html create mode 100644 examples/reversi/reversi-2.10.html create mode 100644 examples/reversi/reversi-2.11-fastopt.html create mode 100644 examples/reversi/reversi-2.11.html create mode 100644 examples/testing/src/main/scala/ElementCreator.scala create mode 100644 examples/testing/src/test/scala/CollectionTest.scala create mode 100644 examples/testing/src/test/scala/ElementCreatorTest.scala create mode 100644 examples/testing/testing-2.10-fastopt.html create mode 100644 examples/testing/testing-2.10.html create mode 100644 examples/testing/testing-2.11-fastopt.html create mode 100644 examples/testing/testing-2.11.html create mode 100644 ir/src/main/scala/scala/scalajs/ir/ClassKind.scala create mode 100644 ir/src/main/scala/scala/scalajs/ir/Definitions.scala create mode 100644 ir/src/main/scala/scala/scalajs/ir/Hashers.scala create mode 100644 ir/src/main/scala/scala/scalajs/ir/InfoSerializers.scala create mode 100644 ir/src/main/scala/scala/scalajs/ir/Infos.scala create mode 100644 ir/src/main/scala/scala/scalajs/ir/Position.scala create mode 100644 ir/src/main/scala/scala/scalajs/ir/Printers.scala create mode 100644 ir/src/main/scala/scala/scalajs/ir/ScalaJSVersions.scala create mode 100644 ir/src/main/scala/scala/scalajs/ir/Serializers.scala create mode 100644 ir/src/main/scala/scala/scalajs/ir/Tags.scala create mode 100644 ir/src/main/scala/scala/scalajs/ir/Transformers.scala create mode 100644 ir/src/main/scala/scala/scalajs/ir/Traversers.scala create mode 100644 ir/src/main/scala/scala/scalajs/ir/Trees.scala create mode 100644 ir/src/main/scala/scala/scalajs/ir/Types.scala create mode 100644 ir/src/main/scala/scala/scalajs/ir/Utils.scala create mode 100644 jasmine-test-framework/src/main/resources/jasmine-polyfills.js create mode 100644 jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Jasmine.scala create mode 100644 jasmine-test-framework/src/main/scala/org/scalajs/jasmine/JasmineEnv.scala create mode 100644 jasmine-test-framework/src/main/scala/org/scalajs/jasmine/JasmineExpectation.scala create mode 100644 jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Results.scala create mode 100644 jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Spec.scala create mode 100644 jasmine-test-framework/src/main/scala/org/scalajs/jasmine/SpecResults.scala create mode 100644 jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Suite.scala create mode 100644 jasmine-test-framework/src/main/scala/org/scalajs/jasmine/SuiteResults.scala create mode 100644 jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTest.scala create mode 100644 jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTestFramework.scala create mode 100644 jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTestReporter.scala create mode 100644 jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/TestSuiteContext.scala create mode 100644 javalanglib/src/main/scala/java/lang/Appendable.scala create mode 100644 javalanglib/src/main/scala/java/lang/AutoCloseable.scala create mode 100644 javalanglib/src/main/scala/java/lang/Boolean.scala create mode 100644 javalanglib/src/main/scala/java/lang/Byte.scala create mode 100644 javalanglib/src/main/scala/java/lang/CharSequence.scala create mode 100644 javalanglib/src/main/scala/java/lang/Character.scala create mode 100644 javalanglib/src/main/scala/java/lang/Class.scala create mode 100644 javalanglib/src/main/scala/java/lang/Cloneable.scala create mode 100644 javalanglib/src/main/scala/java/lang/Comparable.scala create mode 100644 javalanglib/src/main/scala/java/lang/Double.scala create mode 100644 javalanglib/src/main/scala/java/lang/Float.scala create mode 100644 javalanglib/src/main/scala/java/lang/InheritableThreadLocal.scala create mode 100644 javalanglib/src/main/scala/java/lang/Integer.scala create mode 100644 javalanglib/src/main/scala/java/lang/Long.scala create mode 100644 javalanglib/src/main/scala/java/lang/Math.scala create mode 100644 javalanglib/src/main/scala/java/lang/Number.scala create mode 100644 javalanglib/src/main/scala/java/lang/Readable.scala create mode 100644 javalanglib/src/main/scala/java/lang/Runnable.scala create mode 100644 javalanglib/src/main/scala/java/lang/Runtime.scala create mode 100644 javalanglib/src/main/scala/java/lang/Short.scala create mode 100644 javalanglib/src/main/scala/java/lang/StackTraceElement.scala create mode 100644 javalanglib/src/main/scala/java/lang/StringBuffer.scala create mode 100644 javalanglib/src/main/scala/java/lang/StringBuilder.scala create mode 100644 javalanglib/src/main/scala/java/lang/System.scala create mode 100644 javalanglib/src/main/scala/java/lang/Thread.scala create mode 100644 javalanglib/src/main/scala/java/lang/ThreadLocal.scala create mode 100644 javalanglib/src/main/scala/java/lang/Throwables.scala create mode 100644 javalanglib/src/main/scala/java/lang/Void.scala create mode 100644 javalanglib/src/main/scala/java/lang/ref/PhantomReference.scala create mode 100644 javalanglib/src/main/scala/java/lang/ref/Reference.scala create mode 100644 javalanglib/src/main/scala/java/lang/ref/ReferenceQueue.scala create mode 100644 javalanglib/src/main/scala/java/lang/ref/SoftReference.scala create mode 100644 javalanglib/src/main/scala/java/lang/ref/WeakReference.scala create mode 100644 javalanglib/src/main/scala/java/lang/reflect/Array.scala create mode 100644 javalib-ex-test-suite/src/test/scala/scala/scalajs/testsuite/javalibex/DataInputStreamTest.scala create mode 100644 javalib-ex-test-suite/src/test/scala/scala/scalajs/testsuite/javalibex/ZipInputStreamTest.scala create mode 100644 javalib-ex/src/main/scala/java/io/DataInputStream.scala create mode 100644 javalib-ex/src/main/scala/java/util/zip/InflaterInputStream.scala create mode 100644 javalib-ex/src/main/scala/java/util/zip/ZipEntry.scala create mode 100644 javalib-ex/src/main/scala/java/util/zip/ZipInputStream.scala create mode 100644 javalib/src/main/scala/java/io/BufferedReader.scala create mode 100644 javalib/src/main/scala/java/io/ByteArrayInputStream.scala create mode 100644 javalib/src/main/scala/java/io/ByteArrayOutputStream.scala create mode 100644 javalib/src/main/scala/java/io/Closeable.scala create mode 100644 javalib/src/main/scala/java/io/DataInput.scala create mode 100644 javalib/src/main/scala/java/io/FilterInputStream.scala create mode 100644 javalib/src/main/scala/java/io/FilterOutputStream.scala create mode 100644 javalib/src/main/scala/java/io/Flushable.scala create mode 100644 javalib/src/main/scala/java/io/InputStream.scala create mode 100644 javalib/src/main/scala/java/io/InputStreamReader.scala create mode 100644 javalib/src/main/scala/java/io/OutputStream.scala create mode 100644 javalib/src/main/scala/java/io/OutputStreamWriter.scala create mode 100644 javalib/src/main/scala/java/io/PrintStream.scala create mode 100644 javalib/src/main/scala/java/io/PrintWriter.scala create mode 100644 javalib/src/main/scala/java/io/Reader.scala create mode 100644 javalib/src/main/scala/java/io/Serializable.scala create mode 100644 javalib/src/main/scala/java/io/StringReader.scala create mode 100644 javalib/src/main/scala/java/io/StringWriter.scala create mode 100644 javalib/src/main/scala/java/io/Throwables.scala create mode 100644 javalib/src/main/scala/java/io/Writer.scala create mode 100644 javalib/src/main/scala/java/net/URI.scala create mode 100644 javalib/src/main/scala/java/net/URISyntaxException.scala create mode 100644 javalib/src/main/scala/java/nio/Buffer.scala create mode 100644 javalib/src/main/scala/java/nio/BufferOverflowException.scala create mode 100644 javalib/src/main/scala/java/nio/BufferUnderflowException.scala create mode 100644 javalib/src/main/scala/java/nio/ByteBuffer.scala create mode 100644 javalib/src/main/scala/java/nio/ByteOrder.scala create mode 100644 javalib/src/main/scala/java/nio/CharBuffer.scala create mode 100644 javalib/src/main/scala/java/nio/HeapByteBuffer.scala create mode 100644 javalib/src/main/scala/java/nio/HeapCharBuffer.scala create mode 100644 javalib/src/main/scala/java/nio/InvalidMarkException.scala create mode 100644 javalib/src/main/scala/java/nio/ReadOnlyBufferException.scala create mode 100644 javalib/src/main/scala/java/nio/StringCharBuffer.scala create mode 100644 javalib/src/main/scala/java/nio/charset/CharacterCodingException.scala create mode 100644 javalib/src/main/scala/java/nio/charset/Charset.scala create mode 100644 javalib/src/main/scala/java/nio/charset/CharsetDecoder.scala create mode 100644 javalib/src/main/scala/java/nio/charset/CharsetEncoder.scala create mode 100644 javalib/src/main/scala/java/nio/charset/CoderMalfunctionError.scala create mode 100644 javalib/src/main/scala/java/nio/charset/CoderResult.scala create mode 100644 javalib/src/main/scala/java/nio/charset/CodingErrorAction.scala create mode 100644 javalib/src/main/scala/java/nio/charset/MalformedInputException.scala create mode 100644 javalib/src/main/scala/java/nio/charset/StandardCharsets.scala create mode 100644 javalib/src/main/scala/java/nio/charset/UnmappableCharacterException.scala create mode 100644 javalib/src/main/scala/java/nio/charset/UnsupportedCharsetException.scala create mode 100644 javalib/src/main/scala/java/util/Arrays.scala create mode 100644 javalib/src/main/scala/java/util/Comparator.scala create mode 100644 javalib/src/main/scala/java/util/Date.scala create mode 100644 javalib/src/main/scala/java/util/Formattable.scala create mode 100644 javalib/src/main/scala/java/util/FormattableFlags.scala create mode 100644 javalib/src/main/scala/java/util/Formatter.scala create mode 100644 javalib/src/main/scala/java/util/Random.scala create mode 100644 javalib/src/main/scala/java/util/Throwables.scala create mode 100644 javalib/src/main/scala/java/util/UUID.scala create mode 100644 javalib/src/main/scala/java/util/concurrent/ExecutionException.scala create mode 100644 javalib/src/main/scala/java/util/concurrent/Executor.scala create mode 100644 javalib/src/main/scala/java/util/concurrent/TimeUnit.scala create mode 100644 javalib/src/main/scala/java/util/concurrent/atomic/AtomicBoolean.scala create mode 100644 javalib/src/main/scala/java/util/concurrent/atomic/AtomicInteger.scala create mode 100644 javalib/src/main/scala/java/util/concurrent/atomic/AtomicLong.scala create mode 100644 javalib/src/main/scala/java/util/concurrent/atomic/AtomicReference.scala create mode 100644 javalib/src/main/scala/java/util/regex/MatchResult.scala create mode 100644 javalib/src/main/scala/java/util/regex/Matcher.scala create mode 100644 javalib/src/main/scala/java/util/regex/Pattern.scala create mode 100644 library-aux/src/main/scala/scala/runtime/ArrayRuntime.scala create mode 100644 library-aux/src/main/scala/scala/runtime/BoxedUnit.scala create mode 100644 library-aux/src/main/scala/scala/runtime/RefTypes.scala create mode 100644 library-aux/src/main/scala/scala/runtime/Statics.scala create mode 100644 library/src/main/scala/scala/scalajs/concurrent/JSExecutionContext.scala create mode 100644 library/src/main/scala/scala/scalajs/concurrent/QueueExecutionContext.scala create mode 100644 library/src/main/scala/scala/scalajs/concurrent/RunNowExcecutionContext.scala create mode 100644 library/src/main/scala/scala/scalajs/js/Array.scala create mode 100644 library/src/main/scala/scala/scalajs/js/ArrayOps.scala create mode 100644 library/src/main/scala/scala/scalajs/js/Date.scala create mode 100644 library/src/main/scala/scala/scalajs/js/Dictionary.scala create mode 100644 library/src/main/scala/scala/scalajs/js/Error.scala create mode 100644 library/src/main/scala/scala/scalajs/js/Function.scala create mode 100644 library/src/main/scala/scala/scalajs/js/GlobalScope.scala create mode 100644 library/src/main/scala/scala/scalajs/js/JSApp.scala create mode 100644 library/src/main/scala/scala/scalajs/js/JSArrayOps.scala create mode 100644 library/src/main/scala/scala/scalajs/js/JSConverters.scala create mode 100644 library/src/main/scala/scala/scalajs/js/JSON.scala create mode 100644 library/src/main/scala/scala/scalajs/js/JavaScriptException.scala create mode 100644 library/src/main/scala/scala/scalajs/js/Math.scala create mode 100644 library/src/main/scala/scala/scalajs/js/Primitives.scala create mode 100644 library/src/main/scala/scala/scalajs/js/PropertyDescriptor.scala create mode 100644 library/src/main/scala/scala/scalajs/js/RegExp.scala create mode 100644 library/src/main/scala/scala/scalajs/js/ThisFunction.scala create mode 100644 library/src/main/scala/scala/scalajs/js/UndefOr.scala create mode 100644 library/src/main/scala/scala/scalajs/js/WrappedArray.scala create mode 100644 library/src/main/scala/scala/scalajs/js/WrappedDictionary.scala create mode 100644 library/src/main/scala/scala/scalajs/js/annotation/JSBracketAccess.scala create mode 100644 library/src/main/scala/scala/scalajs/js/annotation/JSExport.scala create mode 100644 library/src/main/scala/scala/scalajs/js/annotation/JSExportAll.scala create mode 100644 library/src/main/scala/scala/scalajs/js/annotation/JSExportDescendentClasses.scala create mode 100644 library/src/main/scala/scala/scalajs/js/annotation/JSExportDescendentObjects.scala create mode 100644 library/src/main/scala/scala/scalajs/js/annotation/JSExportNamed.scala create mode 100644 library/src/main/scala/scala/scalajs/js/annotation/JSName.scala create mode 100644 library/src/main/scala/scala/scalajs/js/annotation/README.md create mode 100644 library/src/main/scala/scala/scalajs/js/annotation/RawJSType.scala create mode 100644 library/src/main/scala/scala/scalajs/js/package.scala create mode 100644 library/src/main/scala/scala/scalajs/js/typedarray/ArrayBuffer.scala create mode 100644 library/src/main/scala/scala/scalajs/js/typedarray/ArrayBufferInputStream.scala create mode 100644 library/src/main/scala/scala/scalajs/js/typedarray/ArrayBufferView.scala create mode 100644 library/src/main/scala/scala/scalajs/js/typedarray/DataView.scala create mode 100644 library/src/main/scala/scala/scalajs/js/typedarray/Float32Array.scala create mode 100644 library/src/main/scala/scala/scalajs/js/typedarray/Float64Array.scala create mode 100644 library/src/main/scala/scala/scalajs/js/typedarray/Int16Array.scala create mode 100644 library/src/main/scala/scala/scalajs/js/typedarray/Int32Array.scala create mode 100644 library/src/main/scala/scala/scalajs/js/typedarray/Int8Array.scala create mode 100644 library/src/main/scala/scala/scalajs/js/typedarray/TypedArray.scala create mode 100644 library/src/main/scala/scala/scalajs/js/typedarray/Uint16Array.scala create mode 100644 library/src/main/scala/scala/scalajs/js/typedarray/Uint32Array.scala create mode 100644 library/src/main/scala/scala/scalajs/js/typedarray/Uint8Array.scala create mode 100644 library/src/main/scala/scala/scalajs/js/typedarray/Uint8ClampedArray.scala create mode 100644 library/src/main/scala/scala/scalajs/js/typedarray/package.scala create mode 100644 library/src/main/scala/scala/scalajs/niocharset/ISO_8859_1.scala create mode 100644 library/src/main/scala/scala/scalajs/niocharset/ISO_8859_1_And_US_ASCII_Common.scala create mode 100644 library/src/main/scala/scala/scalajs/niocharset/StandardCharsets.scala create mode 100644 library/src/main/scala/scala/scalajs/niocharset/US_ASCII.scala create mode 100644 library/src/main/scala/scala/scalajs/niocharset/UTF_16.scala create mode 100644 library/src/main/scala/scala/scalajs/niocharset/UTF_16BE.scala create mode 100644 library/src/main/scala/scala/scalajs/niocharset/UTF_16LE.scala create mode 100644 library/src/main/scala/scala/scalajs/niocharset/UTF_16_Common.scala create mode 100644 library/src/main/scala/scala/scalajs/niocharset/UTF_8.scala create mode 100644 library/src/main/scala/scala/scalajs/runtime/AnonFunctions.scala create mode 100644 library/src/main/scala/scala/scalajs/runtime/Bits.scala create mode 100644 library/src/main/scala/scala/scalajs/runtime/BooleanReflectiveCall.scala create mode 100644 library/src/main/scala/scala/scalajs/runtime/IntegerReflectiveCall.scala create mode 100644 library/src/main/scala/scala/scalajs/runtime/NumberReflectiveCall.scala create mode 100644 library/src/main/scala/scala/scalajs/runtime/RuntimeLong.scala create mode 100644 library/src/main/scala/scala/scalajs/runtime/RuntimeString.scala create mode 100644 library/src/main/scala/scala/scalajs/runtime/StackTrace.scala create mode 100644 library/src/main/scala/scala/scalajs/runtime/UndefinedBehaviorError.scala create mode 100644 library/src/main/scala/scala/scalajs/runtime/package.scala create mode 100644 no-ir-check-test/src/test/scala/scala/scalajs/testsuite/noircheck/DummyParentsTest.scala create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/BlacklistedTests.txt create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/BuglistedTests.txt create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/NoDCEWarn.txt create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/WhitelistedTests.txt create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/neg/t7494-no-options.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-01.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-02.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-04.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-08.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-09.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-10.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/bugs.sem create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/dynamic-anyval.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/impconvtimes.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/imports.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/issue192.sem create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/macro-bundle-static.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/macro-bundle-toplevel.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/macro-bundle-whitebox-decl.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/misc.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/promotion.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/runtime.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/spec-self.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/structural.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t0421-new.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t0421-old.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t1503.sem create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t3702.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t4148.sem create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t4617.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t5552.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t5568.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t5629b.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t5680.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t5866.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t6102.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t6318_primitives.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t6662.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t7657.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t7763.sem create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/try-catch-unify.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/virtpatmat_switch.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/virtpatmat_typetag.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/BlacklistedTests.txt create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/BuglistedTests.txt create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/NoDCEWarn.txt create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/WhitelistedTests.txt create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/neg/t7494-no-options.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-01.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-02.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-04.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-08.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-09.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-10.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/bugs.sem create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/dynamic-anyval.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/impconvtimes.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/imports.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/issue192.sem create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/macro-bundle-static.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/macro-bundle-toplevel.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/macro-bundle-whitebox-decl.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/misc.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/promotion.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/runtime.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/spec-self.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/structural.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t0421-new.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t0421-old.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t1503.sem create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t3702.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t4148.sem create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t4617.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t5552.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t5568.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t5629b.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t5680.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t5866.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t6102.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t6318_primitives.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t6662.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t7657.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t7763.sem create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t8570a.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/try-catch-unify.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/virtpatmat_switch.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/virtpatmat_typetag.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/BlacklistedTests.txt create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/BuglistedTests.txt create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/NoDCEWarn.txt create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/WhitelistedTests.txt create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/neg/t7494-no-options.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-01.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-02.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-04.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-08.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-09.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-10.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/bugs.sem create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/dynamic-anyval.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/impconvtimes.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/imports.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/issue192.sem create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/macro-bundle-static.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/macro-bundle-toplevel.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/macro-bundle-whitebox-decl.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/misc.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/promotion.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/runtime.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/spec-self.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/structural.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t0421-new.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t0421-old.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t1503.sem create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t3702.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t4148.sem create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t4617.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t5552.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t5568.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t5629b.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t5680.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t5866.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t6102.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t6318_primitives.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t6662.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t7657.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t7763.sem create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t8570a.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/try-catch-unify.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/virtpatmat_switch.check create mode 100644 partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/virtpatmat_typetag.check create mode 100644 partest/src/main/scala/scala/tools/nsc/MainGenericRunner.scala create mode 100644 partest/src/main/scala/scala/tools/partest/scalajs/PartestInterface.scala create mode 100644 partest/src/main/scala/scala/tools/partest/scalajs/ScalaJSPartest.scala create mode 100644 partest/src/main/scala/scala/tools/partest/scalajs/ScalaJSPartestOptions.scala create mode 100644 project/ExternalCompile.scala create mode 100644 project/JavaLangObject.scala create mode 100644 project/JavaLangString.scala create mode 100644 project/ScalaJSBuild.scala create mode 100644 project/build.properties create mode 100644 project/build.sbt create mode 100644 project/project/ScalaJSEnvGenerator.scala create mode 100644 sbt-plugin-test/README.md create mode 100644 sbt-plugin-test/build.sbt create mode 100644 sbt-plugin-test/jetty9/src/main/resources/test.txt create mode 100644 sbt-plugin-test/noDOM/src/main/scala/sbttest/noDOM/Lib.scala create mode 100644 sbt-plugin-test/noDOM/src/main/scala/sbttest/noDOM/TestApp.scala create mode 100644 sbt-plugin-test/noDOM/src/test/scala/sbttest/noDOM/LibTest.scala create mode 100644 sbt-plugin-test/project/Jetty9Test.scala create mode 100644 sbt-plugin-test/project/build.properties create mode 100644 sbt-plugin-test/project/build.sbt create mode 100644 sbt-plugin-test/project/project/build.sbt create mode 100644 sbt-plugin-test/withDOM/src/main/scala/sbttest/withDOM/Lib.scala create mode 100644 sbt-plugin-test/withDOM/src/main/scala/sbttest/withDOM/TestApp.scala create mode 100644 sbt-plugin-test/withDOM/src/test/scala/sbttest/withDOM/LibTest.scala create mode 100644 sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/AbstractJSDeps.scala create mode 100644 sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/Implicits.scala create mode 100644 sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/JSUtils.scala create mode 100644 sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/LoggerJSConsole.scala create mode 100644 sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/OptimizerOptions.scala create mode 100644 sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/ScalaJSCrossVersion.scala create mode 100644 sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/ScalaJSPlugin.scala create mode 100644 sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/ScalaJSPluginInternal.scala create mode 100644 sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/Stage.scala create mode 100644 sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/ExternalJSEnv.scala create mode 100644 sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/VirtualFileMaterializer.scala create mode 100644 sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/nodejs/NodeJSEnv.scala create mode 100644 sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/JettyWebsocketManager.scala create mode 100644 sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/PhantomJSEnv.scala create mode 100644 sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/PhantomJettyClassLoader.scala create mode 100644 sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/WebsocketListener.scala create mode 100644 sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/WebsocketManager.scala create mode 100644 sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/LazyScalaJSScope.scala create mode 100644 sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/RhinoJSEnv.scala create mode 100644 sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/ScalaJSCoreLib.scala create mode 100644 sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/package.scala create mode 100644 sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/impl/DependencyBuilders.scala create mode 100644 sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/Events.scala create mode 100644 sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/JSClasspathLoader.scala create mode 100644 sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/SbtTestLoggerAccWrapper.scala create mode 100644 sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestException.scala create mode 100644 sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestFramework.scala create mode 100644 sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestOutputConsole.scala create mode 100644 sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestRunner.scala create mode 100644 sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestTask.scala create mode 100644 sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/AsyncTests.scala create mode 100644 sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/ComTests.scala create mode 100644 sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/JSEnvTest.scala create mode 100644 sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/NodeJSTest.scala create mode 100644 sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/PhantomJSTest.scala create mode 100644 sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/RhinoJSEnvTest.scala create mode 100644 sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/StoreJSConsole.scala create mode 100644 sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/StoreLogger.scala create mode 100644 scalalib/overrides-2.10/scala/Console.scala create mode 100644 scalalib/overrides-2.10/scala/collection/immutable/NumericRange.scala create mode 100644 scalalib/overrides-2.10/scala/collection/immutable/Range.scala create mode 100644 scalalib/overrides-2.10/scala/collection/mutable/Buffer.scala create mode 100644 scalalib/overrides-2.10/scala/compat/Platform.scala create mode 100644 scalalib/overrides-2.10/scala/package.scala create mode 100644 scalalib/overrides-2.11/scala/Console.scala create mode 100644 scalalib/overrides-2.11/scala/collection/immutable/NumericRange.scala create mode 100644 scalalib/overrides-2.11/scala/collection/immutable/Range.scala create mode 100644 scalalib/overrides-2.11/scala/collection/mutable/Buffer.scala create mode 100644 scalalib/overrides-2.11/scala/compat/Platform.scala create mode 100644 scalalib/overrides-2.11/scala/package.scala create mode 100644 scalalib/overrides/scala/App.scala create mode 100644 scalalib/overrides/scala/Enumeration.scala create mode 100644 scalalib/overrides/scala/Symbol.scala create mode 100644 scalalib/overrides/scala/concurrent/impl/AbstractPromise.scala create mode 100644 scalalib/overrides/scala/math/ScalaNumber.scala create mode 100644 scalalib/overrides/scala/runtime/BoxesRunTime.scala create mode 100644 scalalib/overrides/scala/util/control/NoStackTrace.scala create mode 100755 scripts/assemble-cli.sh create mode 100755 scripts/build-all-js.sh create mode 100755 scripts/publish-to-bintray.sh create mode 100644 stubs/src/main/scala/scala/scalajs/js/annotation/ExportAnnotations.scala create mode 100644 test-bridge/src/main/scala/scala/scalajs/testbridge/Test.scala create mode 100644 test-bridge/src/main/scala/scala/scalajs/testbridge/TestFramework.scala create mode 100644 test-bridge/src/main/scala/scala/scalajs/testbridge/TestOutput.scala create mode 100644 test-bridge/src/main/scala/scala/scalajs/testbridge/TestOutputLog.scala create mode 100644 test-bridge/src/main/scala/scala/scalajs/testbridge/internal/ConsoleTestOutput.scala create mode 100644 test-suite/run-jasmine-tests.js create mode 100644 test-suite/scalajs-test-suite-2.10-fastopt.html create mode 100644 test-suite/scalajs-test-suite-2.10.html create mode 100644 test-suite/scalajs-test-suite-2.11-fastopt.html create mode 100644 test-suite/scalajs-test-suite-2.11.html create mode 100644 test-suite/src/test/require-sam/scala/scalajs/testsuite/jsinterop/ArraySAMTest.scala create mode 100644 test-suite/src/test/resources/SourceMapTestTemplate.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/compiler/BooleanTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/compiler/ByteTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/compiler/CharTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/compiler/FloatTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/compiler/InstanceTestsHijackedBoxedClassesTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/compiler/IntTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/compiler/InteroperabilityTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/compiler/LongTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/compiler/OptimizerTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/compiler/ReflectionTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/compiler/ReflectiveCallTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/compiler/RegressionTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/compiler/RuntimeTypesTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/compiler/ShortTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/compiler/UnitTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ArraysTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/javalib/AtomicTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/javalib/BooleanTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ByteTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/javalib/CharacterTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ClassTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/javalib/DateTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/javalib/DoubleTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/javalib/FloatTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/javalib/FormatterTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/javalib/IntegerTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/javalib/LongTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/javalib/MathTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/javalib/MockByteArrayOutputStream.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ObjectTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/javalib/OutputStreamWriterTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/javalib/PrintStreamTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/javalib/PrintWriterTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/javalib/RandomTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ReadersTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ReferenceTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/javalib/RegexTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ShortTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/javalib/StackTraceElementTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/javalib/StreamsTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/javalib/StringBufferTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/javalib/StringTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/javalib/SystemTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/javalib/ThrowablesTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/javalib/TimeUnitTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/javalib/URITest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/javalib/UUIDTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/ArrayTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/AsyncTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/DictionaryTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/DynamicTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/ExportsTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/FunctionTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/MiscInteropTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/RuntimeLongTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/StrangeNamedTests.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/ThisFunctionTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/UndefOrTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/library/ArrayOpsTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/library/WrappedArrayTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/library/WrappedDictionaryTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/niobuffer/BaseBufferTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/niobuffer/ByteBufferTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/niobuffer/CharBufferTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/niocharset/BaseCharsetTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/niocharset/CharsetTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/niocharset/Latin1Test.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/niocharset/USASCIITest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/niocharset/UTF16Test.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/niocharset/UTF8Test.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/scalalib/EnumerationTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/scalalib/RangesTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/scalalib/SymbolTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/typedarray/ArrayBufferInputStreamTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/typedarray/ArrayBufferTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/typedarray/ArraysTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/typedarray/DataViewTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/typedarray/TypedArrayConversionTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/typedarray/TypedArrayTest.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/utils/JSUtils.scala create mode 100644 test-suite/src/test/scala/scala/scalajs/testsuite/utils/TestDetector.scala create mode 100644 tools/js/src/main/scala/scala/scalajs/tools/classpath/builder/NodeFileSystem.scala create mode 100644 tools/js/src/main/scala/scala/scalajs/tools/io/NodeVirtualFiles.scala create mode 100644 tools/js/src/main/scala/scala/scalajs/tools/json/Impl.scala create mode 100644 tools/js/src/test/scala/scala/scalajs/tools/js/test/JasmineReporter.scala create mode 100644 tools/js/src/test/scala/scala/scalajs/tools/js/test/QuickLinker.scala create mode 100644 tools/jvm/src/main/scala/scala/scalajs/tools/classpath/builder/JarLibClasspathBuilder.scala create mode 100644 tools/jvm/src/main/scala/scala/scalajs/tools/classpath/builder/PartialClasspathBuilder.scala create mode 100644 tools/jvm/src/main/scala/scala/scalajs/tools/classpath/builder/PhysicalFileSystem.scala create mode 100644 tools/jvm/src/main/scala/scala/scalajs/tools/io/FileVirtualFiles.scala create mode 100644 tools/jvm/src/main/scala/scala/scalajs/tools/json/Impl.scala create mode 100644 tools/jvm/src/main/scala/scala/scalajs/tools/optimizer/ClosureAstBuilder.scala create mode 100644 tools/jvm/src/main/scala/scala/scalajs/tools/optimizer/ClosureAstTransformer.scala create mode 100644 tools/jvm/src/main/scala/scala/scalajs/tools/optimizer/ConcurrencyUtils.scala create mode 100644 tools/jvm/src/main/scala/scala/scalajs/tools/optimizer/LoggerErrorManager.scala create mode 100644 tools/jvm/src/main/scala/scala/scalajs/tools/optimizer/ParIncOptimizer.scala create mode 100644 tools/jvm/src/main/scala/scala/scalajs/tools/optimizer/ScalaJSClosureOptimizer.scala create mode 100644 tools/jvm/src/main/scala/scala/scalajs/tools/sourcemap/SourceMapper.scala create mode 100644 tools/jvm/src/test/resources/test.jar create mode 100644 tools/jvm/src/test/scala/scala/scalajs/tools/classpath/builder/test/ClasspathElementsTraverserTest.scala create mode 100644 tools/jvm/src/test/scala/scala/scalajs/tools/classpath/builder/test/JarBuilderTest.scala create mode 100644 tools/scalajsenv.js create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/classpath/CompleteClasspath.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/classpath/ComplianceRequirement.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/classpath/Exceptions.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/classpath/IRClasspath.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/classpath/LinkedClasspath.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/classpath/PartialClasspath.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/classpath/ResolvedJSDependency.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/AbstractJarLibClasspathBuilder.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/AbstractPartialClasspathBuilder.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/ClasspathContentHandler.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/ClasspathElementsTraverser.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/DirTraverser.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/FileSystem.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/JarTraverser.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/corelib/CoreJSLibs.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/env/AsyncJSEnv.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/env/AsyncJSRunner.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/env/ComJSEnv.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/env/ComJSRunner.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/env/ConsoleJSConsole.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/env/JSConsole.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/env/JSEnv.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/env/JSRunner.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/env/NullJSConsole.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/io/CacheUtils.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/io/IO.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/io/MemFiles.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/io/VirtualFiles.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/javascript/JSDesugaring.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/javascript/LongImpl.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/javascript/Printers.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/javascript/ScalaJSClassEmitter.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/javascript/TreeDSL.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/javascript/Trees.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/jsdep/Exceptions.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/jsdep/FlatJSDependency.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/jsdep/JSDependency.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/jsdep/JSDependencyManifest.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/jsdep/Origin.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/jsdep/ResolutionInfo.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/json/AbstractJSONImpl.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/json/JSONDeserializer.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/json/JSONObjBuilder.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/json/JSONObjExtractor.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/json/JSONSerializer.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/json/package.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/logging/Level.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/logging/Logger.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/logging/NullLogger.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/logging/ScalaConsoleLogger.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/optimizer/Analyzer.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/optimizer/GenIncOptimizer.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/optimizer/IRChecker.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/optimizer/IncOptimizer.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/optimizer/JSTreeBuilder.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/optimizer/OptimizerCore.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/optimizer/ScalaJSOptimizer.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/sem/CheckedBehavior.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/sem/Semantics.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/sourcemap/JSFileBuilder.scala create mode 100644 tools/shared/src/main/scala/scala/scalajs/tools/sourcemap/SourceMapWriter.scala diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..90977dc --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +target/ +.cache +.classpath +.project +.settings/ +/scalalib/fetchedSources/ +/partest/fetchedSources/ +/cli/pack/ +/.idea/ +/.idea_modules/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..79ec7ac --- /dev/null +++ b/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/README.md b/README.md new file mode 100644 index 0000000..51e65c9 --- /dev/null +++ b/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/TESTING b/TESTING new file mode 100644 index 0000000..7d393a4 --- /dev/null +++ b/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:/bin:/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/ci/check-partest-coverage.sh b/ci/check-partest-coverage.sh new file mode 100755 index 0000000..ca35f37 --- /dev/null +++ b/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/ci/checksizes.sh b/ci/checksizes.sh new file mode 100755 index 0000000..8e7a508 --- /dev/null +++ b/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/ci/matrix.xml b/ci/matrix.xml new file mode 100644 index 0000000..5c02146 --- /dev/null +++ b/ci/matrix.xml @@ -0,0 +1,360 @@ + + + + + + + + + + + +]> + + + + + + + + + + + + + + + + + + + + + + + + 2.10.2 + 1.6 + + + 2.10.2 + 1.7 + + + 2.10.2 + 1.8 + + + 2.11.2 + 1.6 + + + 2.11.2 + 1.7 + + + 2.11.2 + 1.8 + + + 2.11.4 + 1.7 + + + + + 2.10.2 + 1.6 + + + 2.10.2 + 1.7 + + + 2.10.2 + 1.8 + + + + 2.11.2 + 1.7 + + + 2.11.2 + 1.8 + + + + + + 2.10.4 + 1.6 + + + 2.10.4 + 1.7 + + + 2.10.4 + 1.8 + + + + 2.11.2 + 1.7 + + + 2.11.2 + 1.8 + + + + + + 2.11.0 + 1.7 + + + 2.11.1 + 1.7 + + + 2.11.2 + 1.7 + + + + + + + + + + + + 2.10.3 + 1.7 + + + 2.10.4 + 1.6 + + + 2.10.4 + 1.7 + + + 2.10.4 + 1.8 + + + 2.11.0 + 1.7 + + + 2.11.1 + 1.7 + + + 2.11.4 + 1.6 + + + 2.11.4 + 1.8 + + + + + 2.10.3 + 1.7 + + + 2.10.4 + 1.6 + + + 2.10.4 + 1.7 + + + 2.10.4 + 1.8 + + + 2.11.0 + 1.7 + + + 2.11.1 + 1.7 + + + + 2.11.2 + 1.7 + + + 2.11.2 + 1.7 + + + 2.11.2 + 1.7 + + + + + + + + 2.11.0 + 1.7 + + + 2.11.0 + 1.7 + + + 2.11.0 + 1.7 + + + 2.11.1 + 1.7 + + + 2.11.1 + 1.7 + + + 2.11.1 + 1.7 + + + 2.11.2 + 1.8 + + + 2.11.2 + 1.8 + + + 2.11.2 + 1.8 + + + + + diff --git a/ci/scalajs-matrix-build.groovy b/ci/scalajs-matrix-build.groovy new file mode 100644 index 0000000..f2a661a --- /dev/null +++ b/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/cli/src/main/resources/scalajsc b/cli/src/main/resources/scalajsc new file mode 100755 index 0000000..7fd1100 --- /dev/null +++ b/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/cli/src/main/resources/scalajsc.bat b/cli/src/main/resources/scalajsc.bat new file mode 100644 index 0000000..767c5df --- /dev/null +++ b/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/cli/src/main/resources/scalajsld b/cli/src/main/resources/scalajsld new file mode 100755 index 0000000..7732e2a --- /dev/null +++ b/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/cli/src/main/resources/scalajsld.bat b/cli/src/main/resources/scalajsld.bat new file mode 100644 index 0000000..e915237 --- /dev/null +++ b/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/cli/src/main/resources/scalajsp b/cli/src/main/resources/scalajsp new file mode 100755 index 0000000..e7a6e58 --- /dev/null +++ b/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/cli/src/main/resources/scalajsp.bat b/cli/src/main/resources/scalajsp.bat new file mode 100644 index 0000000..dd9745c --- /dev/null +++ b/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/cli/src/main/scala/scala/scalajs/cli/Scalajsld.scala b/cli/src/main/scala/scala/scalajs/cli/Scalajsld.scala new file mode 100644 index 0000000..55e61af --- /dev/null +++ b/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](" ...") + .unbounded() + .action { (x, c) => c.copy(cp = c.cp :+ x) } + .text("Entries of Scala.js classpath to link") + opt[File]('o', "output") + .valueName("") + .required() + .action { (x, c) => c.copy(output = x) } + .text("Output file of linker (required)") + opt[File]("jsoutput") + .valueName("") + .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("") + .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("") + .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/cli/src/main/scala/scala/scalajs/cli/Scalajsp.scala b/cli/src/main/scala/scala/scalajs/cli/Scalajsp.scala new file mode 100644 index 0000000..0d64b93 --- /dev/null +++ b/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](" ...") + .unbounded() + .action { (x, c) => c.copy(fileNames = c.fileNames :+ x) } + .text("*.sjsir file to display content of") + opt[File]('j', "jar") + .valueName("") + .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/compiler/src/main/resources/scalac-plugin.xml b/compiler/src/main/resources/scalac-plugin.xml new file mode 100644 index 0000000..76ff1b7 --- /dev/null +++ b/compiler/src/main/resources/scalac-plugin.xml @@ -0,0 +1,4 @@ + + scalajs + scala.scalajs.compiler.ScalaJSPlugin + diff --git a/compiler/src/main/scala/scala/scalajs/compiler/ClassInfos.scala b/compiler/src/main/scala/scala/scalajs/compiler/ClassInfos.scala new file mode 100644 index 0000000..026d664 --- /dev/null +++ b/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/compiler/src/main/scala/scala/scalajs/compiler/Compat210Component.scala b/compiler/src/main/scala/scala/scalajs/compiler/Compat210Component.scala new file mode 100644 index 0000000..f357337 --- /dev/null +++ b/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/compiler/src/main/scala/scala/scalajs/compiler/GenJSCode.scala b/compiler/src/main/scala/scala/scalajs/compiler/GenJSCode.scala new file mode 100644 index 0000000..f9885a0 --- /dev/null +++ b/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 in: + * ::= + * If(_, , ) + * | Block(_, ) + * | + * | _ + * 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 (outer, capture1, ..., captureM) extends AbstractionFunctionN[...] { + * def apply(param1, ..., paramN) = { + * + * } + * } + * new (o, c1, ..., cM) + * + * we generate a function maker that emits: + * + * lambda[notype]( + * outer, capture1, ..., captureM, param1, ..., paramN) { + * + * } + * + * 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 (outer, capture1, ..., captureM) extends js.FunctionN[...] { + * def apply(param1, ..., paramN) = { + * outer.lambdaImpl(param1, ..., paramN, capture1, ..., captureM) + * } + * } + * new (o, c1, ..., cM) + * + * we generate a function maker that emits: + * + * lambda[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, 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/compiler/src/main/scala/scala/scalajs/compiler/GenJSExports.scala b/compiler/src/main/scala/scala/scalajs/compiler/GenJSExports.scala new file mode 100644 index 0000000..92dc26b --- /dev/null +++ b/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 - + 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[ + 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/compiler/src/main/scala/scala/scalajs/compiler/GenJSFiles.scala b/compiler/src/main/scala/scala/scalajs/compiler/GenJSFiles.scala new file mode 100644 index 0000000..f754e70 --- /dev/null +++ b/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/compiler/src/main/scala/scala/scalajs/compiler/JSDefinitions.scala b/compiler/src/main/scala/scala/scalajs/compiler/JSDefinitions.scala new file mode 100644 index 0000000..b8a483a --- /dev/null +++ b/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/compiler/src/main/scala/scala/scalajs/compiler/JSEncoding.scala b/compiler/src/main/scala/scala/scalajs/compiler/JSEncoding.scala new file mode 100644 index 0000000..bc7f8be --- /dev/null +++ b/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 = "" + + // 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/compiler/src/main/scala/scala/scalajs/compiler/JSGlobalAddons.scala b/compiler/src/main/scala/scala/scalajs/compiler/JSGlobalAddons.scala new file mode 100644 index 0000000..3621050 --- /dev/null +++ b/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/compiler/src/main/scala/scala/scalajs/compiler/JSPrimitives.scala b/compiler/src/main/scala/scala/scalajs/compiler/JSPrimitives.scala new file mode 100644 index 0000000..b8c20e6 --- /dev/null +++ b/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/compiler/src/main/scala/scala/scalajs/compiler/JSTreeExtractors.scala b/compiler/src/main/scala/scala/scalajs/compiler/JSTreeExtractors.scala new file mode 100644 index 0000000..a18ad88 --- /dev/null +++ b/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/compiler/src/main/scala/scala/scalajs/compiler/PrepJSExports.scala b/compiler/src/main/scala/scala/scalajs/compiler/PrepJSExports.scala new file mode 100644 index 0000000..9223061 --- /dev/null +++ b/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/compiler/src/main/scala/scala/scalajs/compiler/PrepJSInterop.scala b/compiler/src/main/scala/scala/scalajs/compiler/PrepJSInterop.scala new file mode 100644 index 0000000..437576a --- /dev/null +++ b/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 + // + // + 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/compiler/src/main/scala/scala/scalajs/compiler/ScalaJSOptions.scala b/compiler/src/main/scala/scala/scalajs/compiler/ScalaJSOptions.scala new file mode 100644 index 0000000..72912bf --- /dev/null +++ b/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/compiler/src/main/scala/scala/scalajs/compiler/ScalaJSPlugin.scala b/compiler/src/main/scala/scala/scalajs/compiler/ScalaJSPlugin.scala new file mode 100644 index 0000000..c3916ab --- /dev/null +++ b/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: relativize emitted source maps with + | -P:$name:absSourceMap: absolutize emitted source maps with + | This option requires the use of relSourceMap + """.stripMargin) + +} diff --git a/compiler/src/main/scala/scala/scalajs/compiler/TypeKinds.scala b/compiler/src/main/scala/scala/scalajs/compiler/TypeKinds.scala new file mode 100644 index 0000000..774be68 --- /dev/null +++ b/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/compiler/src/main/scala/scala/scalajs/compiler/util/ScopedVar.scala b/compiler/src/main/scala/scala/scalajs/compiler/util/ScopedVar.scala new file mode 100644 index 0000000..3924955 --- /dev/null +++ b/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/compiler/src/test/scala/scala/scalajs/compiler/test/DiverseErrorsTest.scala b/compiler/src/test/scala/scala/scalajs/compiler/test/DiverseErrorsTest.scala new file mode 100644 index 0000000..0fe10f8 --- /dev/null +++ b/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/compiler/src/test/scala/scala/scalajs/compiler/test/EnumerationInteropTest.scala b/compiler/src/test/scala/scala/scalajs/compiler/test/EnumerationInteropTest.scala new file mode 100644 index 0000000..e186cf4 --- /dev/null +++ b/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/compiler/src/test/scala/scala/scalajs/compiler/test/JSDynamicLiteralTest.scala b/compiler/src/test/scala/scala/scalajs/compiler/test/JSDynamicLiteralTest.scala new file mode 100644 index 0000000..bc1a1b4 --- /dev/null +++ b/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/compiler/src/test/scala/scala/scalajs/compiler/test/JSExportASTTest.scala b/compiler/src/test/scala/scala/scalajs/compiler/test/JSExportASTTest.scala new file mode 100644 index 0000000..4a2b1af --- /dev/null +++ b/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/compiler/src/test/scala/scala/scalajs/compiler/test/JSExportTest.scala b/compiler/src/test/scala/scala/scalajs/compiler/test/JSExportTest.scala new file mode 100644 index 0000000..c675420 --- /dev/null +++ b/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/compiler/src/test/scala/scala/scalajs/compiler/test/JSInteropTest.scala b/compiler/src/test/scala/scala/scalajs/compiler/test/JSInteropTest.scala new file mode 100644 index 0000000..99c274f --- /dev/null +++ b/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/compiler/src/test/scala/scala/scalajs/compiler/test/OptimizationTest.scala b/compiler/src/test/scala/scala/scalajs/compiler/test/OptimizationTest.scala new file mode 100644 index 0000000..7f15c7a --- /dev/null +++ b/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/compiler/src/test/scala/scala/scalajs/compiler/test/PositionTest.scala b/compiler/src/test/scala/scala/scalajs/compiler/test/PositionTest.scala new file mode 100644 index 0000000..e25399b --- /dev/null +++ b/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 = "" + 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/compiler/src/test/scala/scala/scalajs/compiler/test/util/DirectTest.scala b/compiler/src/test/scala/scala/scalajs/compiler/test/util/DirectTest.scala new file mode 100644 index 0000000..8289129 --- /dev/null +++ b/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/compiler/src/test/scala/scala/scalajs/compiler/test/util/JSASTTest.scala b/compiler/src/test/scala/scala/scalajs/compiler/test/util/JSASTTest.scala new file mode 100644 index 0000000..d3dfd75 --- /dev/null +++ b/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/compiler/src/test/scala/scala/scalajs/compiler/test/util/TestHelpers.scala b/compiler/src/test/scala/scala/scalajs/compiler/test/util/TestHelpers.scala new file mode 100644 index 0000000..adad89c --- /dev/null +++ b/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/helloworld/HelloWorld.scala b/examples/helloworld/HelloWorld.scala new file mode 100644 index 0000000..fd33060 --- /dev/null +++ b/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! -- DOM" + playground.appendChild(newP) + } + + def sayHelloFromTypedDOM() { + val document = window.document + val playground = document.getElementById("playground") + + val newP = document.createElement("p") + newP.innerHTML = "Hello world! -- typed DOM" + 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("

").html("Hello world! -- jQuery") + newP.appendTo(jQuery("#playground")) + } + + def sayHelloFromTypedJQuery() { + val jQuery = helloworld.JQuery + val newP = jQuery("

").html("Hello world! -- typed jQuery") + 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/helloworld/helloworld-2.10-fastopt.html b/examples/helloworld/helloworld-2.10-fastopt.html new file mode 100644 index 0000000..98b2705 --- /dev/null +++ b/examples/helloworld/helloworld-2.10-fastopt.html @@ -0,0 +1,19 @@ + + + + Hello world - Scala.js example + + + + +

+
+ + + + + + + + + diff --git a/examples/helloworld/helloworld-2.10.html b/examples/helloworld/helloworld-2.10.html new file mode 100644 index 0000000..80b00b9 --- /dev/null +++ b/examples/helloworld/helloworld-2.10.html @@ -0,0 +1,19 @@ + + + + Hello world - Scala.js example + + + + +
+
+ + + + + + + + + diff --git a/examples/helloworld/helloworld-2.11-fastopt.html b/examples/helloworld/helloworld-2.11-fastopt.html new file mode 100644 index 0000000..dbf5598 --- /dev/null +++ b/examples/helloworld/helloworld-2.11-fastopt.html @@ -0,0 +1,19 @@ + + + + Hello world - Scala.js example + + + + +
+
+ + + + + + + + + diff --git a/examples/helloworld/helloworld-2.11.html b/examples/helloworld/helloworld-2.11.html new file mode 100644 index 0000000..9c9a3a1 --- /dev/null +++ b/examples/helloworld/helloworld-2.11.html @@ -0,0 +1,19 @@ + + + + Hello world - Scala.js example + + + + +
+
+ + + + + + + + + diff --git a/examples/helloworld/startup.js b/examples/helloworld/startup.js new file mode 100644 index 0000000..f45e4cb --- /dev/null +++ b/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/reversi/JSTypes.scala b/examples/reversi/JSTypes.scala new file mode 100644 index 0000000..cc0e5a4 --- /dev/null +++ b/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/reversi/Reversi.scala b/examples/reversi/Reversi.scala new file mode 100644 index 0000000..b4a34a4 --- /dev/null +++ b/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("", js.Dynamic.literal( + `type` = "button", value = "Reset" + )).click(reset _) + } + + def createPassButton() = { + jQuery("", js.Dynamic.literal( + `type` = "button", value = "Pass" + )).click(pass _) + } + + def createStatus() = { + jQuery("") + } + + 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( + "") + val domCanvas = boardCanvas.get(0).asInstanceOf[HTMLCanvasElement] + val context = domCanvas.getContext("2d").asInstanceOf[CanvasRenderingContext2D] + + playground.append(jQuery("
").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("

") + 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/reversi/reversi-2.10-fastopt.html b/examples/reversi/reversi-2.10-fastopt.html new file mode 100644 index 0000000..46cd1c7 --- /dev/null +++ b/examples/reversi/reversi-2.10-fastopt.html @@ -0,0 +1,30 @@ + + + + Reversi - Scala.js example + + + + +

Reversi - Scala.js example

+ +

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

+ +
+
+ + + + + + + + + diff --git a/examples/reversi/reversi-2.10.html b/examples/reversi/reversi-2.10.html new file mode 100644 index 0000000..5f7b696 --- /dev/null +++ b/examples/reversi/reversi-2.10.html @@ -0,0 +1,30 @@ + + + + Reversi - Scala.js example + + + + +

Reversi - Scala.js example

+ +

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

+ +
+
+ + + + + + + + + diff --git a/examples/reversi/reversi-2.11-fastopt.html b/examples/reversi/reversi-2.11-fastopt.html new file mode 100644 index 0000000..524e716 --- /dev/null +++ b/examples/reversi/reversi-2.11-fastopt.html @@ -0,0 +1,30 @@ + + + + Reversi - Scala.js example + + + + +

Reversi - Scala.js example

+ +

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

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

Reversi - Scala.js example

+ +

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

+ +
+
+ + + + + + + + + diff --git a/examples/testing/src/main/scala/ElementCreator.scala b/examples/testing/src/main/scala/ElementCreator.scala new file mode 100644 index 0000000..ccb3600 --- /dev/null +++ b/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("

Test

")) +} diff --git a/examples/testing/src/test/scala/CollectionTest.scala b/examples/testing/src/test/scala/CollectionTest.scala new file mode 100644 index 0000000..a586ca2 --- /dev/null +++ b/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/testing/src/test/scala/ElementCreatorTest.scala b/examples/testing/src/test/scala/ElementCreatorTest.scala new file mode 100644 index 0000000..43f6756 --- /dev/null +++ b/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/testing/testing-2.10-fastopt.html b/examples/testing/testing-2.10-fastopt.html new file mode 100644 index 0000000..04ded2f --- /dev/null +++ b/examples/testing/testing-2.10-fastopt.html @@ -0,0 +1,32 @@ + + + + Testing - Jasmine HTML reporter example + + + + + + + + + + + + + diff --git a/examples/testing/testing-2.10.html b/examples/testing/testing-2.10.html new file mode 100644 index 0000000..bbb0cfe --- /dev/null +++ b/examples/testing/testing-2.10.html @@ -0,0 +1,32 @@ + + + + Testing - Jasmine HTML reporter example + + + + + + + + + + + + + diff --git a/examples/testing/testing-2.11-fastopt.html b/examples/testing/testing-2.11-fastopt.html new file mode 100644 index 0000000..a87f635 --- /dev/null +++ b/examples/testing/testing-2.11-fastopt.html @@ -0,0 +1,32 @@ + + + + Testing - Jasmine HTML reporter example + + + + + + + + + + + + + diff --git a/examples/testing/testing-2.11.html b/examples/testing/testing-2.11.html new file mode 100644 index 0000000..9902c3f --- /dev/null +++ b/examples/testing/testing-2.11.html @@ -0,0 +1,32 @@ + + + + Testing - Jasmine HTML reporter example + + + + + + + + + + + + + diff --git a/ir/src/main/scala/scala/scalajs/ir/ClassKind.scala b/ir/src/main/scala/scala/scalajs/ir/ClassKind.scala new file mode 100644 index 0000000..5092d2c --- /dev/null +++ b/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/ir/src/main/scala/scala/scalajs/ir/Definitions.scala b/ir/src/main/scala/scala/scalajs/ir/Definitions.scala new file mode 100644 index 0000000..0762602 --- /dev/null +++ b/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/ir/src/main/scala/scala/scalajs/ir/Hashers.scala b/ir/src/main/scala/scala/scalajs/ir/Hashers.scala new file mode 100644 index 0000000..168d7c1 --- /dev/null +++ b/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/ir/src/main/scala/scala/scalajs/ir/InfoSerializers.scala b/ir/src/main/scala/scala/scalajs/ir/InfoSerializers.scala new file mode 100644 index 0000000..dfb520f --- /dev/null +++ b/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/ir/src/main/scala/scala/scalajs/ir/Infos.scala b/ir/src/main/scala/scala/scalajs/ir/Infos.scala new file mode 100644 index 0000000..66feec2 --- /dev/null +++ b/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/ir/src/main/scala/scala/scalajs/ir/Position.scala b/ir/src/main/scala/scala/scalajs/ir/Position.scala new file mode 100644 index 0000000..3b6d0a2 --- /dev/null +++ b/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/ir/src/main/scala/scala/scalajs/ir/Printers.scala b/ir/src/main/scala/scala/scalajs/ir/Printers.scala new file mode 100644 index 0000000..6208d5f --- /dev/null +++ b/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("") + + // 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("/**/") + + 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("") + + // Literals + + case Undefined() => + print("(void 0)") + + case UndefinedParam() => + print("") + + 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"") + + 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"") + } + } + + 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("") + + 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/ir/src/main/scala/scala/scalajs/ir/ScalaJSVersions.scala b/ir/src/main/scala/scala/scalajs/ir/ScalaJSVersions.scala new file mode 100644 index 0000000..2690939 --- /dev/null +++ b/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/ir/src/main/scala/scala/scalajs/ir/Serializers.scala b/ir/src/main/scala/scala/scalajs/ir/Serializers.scala new file mode 100644 index 0000000..04ec5c2 --- /dev/null +++ b/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/ir/src/main/scala/scala/scalajs/ir/Tags.scala b/ir/src/main/scala/scala/scalajs/ir/Tags.scala new file mode 100644 index 0000000..a03926c --- /dev/null +++ b/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/ir/src/main/scala/scala/scalajs/ir/Transformers.scala b/ir/src/main/scala/scala/scalajs/ir/Transformers.scala new file mode 100644 index 0000000..5e4f40c --- /dev/null +++ b/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/ir/src/main/scala/scala/scalajs/ir/Traversers.scala b/ir/src/main/scala/scala/scalajs/ir/Traversers.scala new file mode 100644 index 0000000..1b77e5e --- /dev/null +++ b/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/ir/src/main/scala/scala/scalajs/ir/Trees.scala b/ir/src/main/scala/scala/scalajs/ir/Trees.scala new file mode 100644 index 0000000..4f58ece --- /dev/null +++ b/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/ir/src/main/scala/scala/scalajs/ir/Types.scala b/ir/src/main/scala/scala/scalajs/ir/Types.scala new file mode 100644 index 0000000..4af493a --- /dev/null +++ b/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] 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/ir/src/main/scala/scala/scalajs/ir/Utils.scala b/ir/src/main/scala/scala/scalajs/ir/Utils.scala new file mode 100644 index 0000000..d4769dc --- /dev/null +++ b/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/jasmine-test-framework/src/main/resources/jasmine-polyfills.js b/jasmine-test-framework/src/main/resources/jasmine-polyfills.js new file mode 100644 index 0000000..b1a5d44 --- /dev/null +++ b/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/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Jasmine.scala b/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Jasmine.scala new file mode 100644 index 0000000..ca0a63f --- /dev/null +++ b/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/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/JasmineEnv.scala b/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/JasmineEnv.scala new file mode 100644 index 0000000..b81121e --- /dev/null +++ b/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/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/JasmineExpectation.scala b/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/JasmineExpectation.scala new file mode 100644 index 0000000..9aad02e --- /dev/null +++ b/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/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Results.scala b/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Results.scala new file mode 100644 index 0000000..aed78b9 --- /dev/null +++ b/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/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Spec.scala b/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Spec.scala new file mode 100644 index 0000000..afbfa13 --- /dev/null +++ b/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/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/SpecResults.scala b/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/SpecResults.scala new file mode 100644 index 0000000..50f073c --- /dev/null +++ b/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/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Suite.scala b/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Suite.scala new file mode 100644 index 0000000..cd4fb75 --- /dev/null +++ b/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/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/SuiteResults.scala b/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/SuiteResults.scala new file mode 100644 index 0000000..db98acf --- /dev/null +++ b/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/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTest.scala b/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTest.scala new file mode 100644 index 0000000..715d39d --- /dev/null +++ b/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/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTestFramework.scala b/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTestFramework.scala new file mode 100644 index 0000000..2686e31 --- /dev/null +++ b/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/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTestReporter.scala b/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTestReporter.scala new file mode 100644 index 0000000..79a7c75 --- /dev/null +++ b/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/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/TestSuiteContext.scala b/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/TestSuiteContext.scala new file mode 100644 index 0000000..827eefd --- /dev/null +++ b/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/javalanglib/src/main/scala/java/lang/Appendable.scala b/javalanglib/src/main/scala/java/lang/Appendable.scala new file mode 100644 index 0000000..9cd74ad --- /dev/null +++ b/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/javalanglib/src/main/scala/java/lang/AutoCloseable.scala b/javalanglib/src/main/scala/java/lang/AutoCloseable.scala new file mode 100644 index 0000000..21a3d0f --- /dev/null +++ b/javalanglib/src/main/scala/java/lang/AutoCloseable.scala @@ -0,0 +1,5 @@ +package java.lang + +trait AutoCloseable { + def close(): Unit +} diff --git a/javalanglib/src/main/scala/java/lang/Boolean.scala b/javalanglib/src/main/scala/java/lang/Boolean.scala new file mode 100644 index 0000000..94a9967 --- /dev/null +++ b/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/javalanglib/src/main/scala/java/lang/Byte.scala b/javalanglib/src/main/scala/java/lang/Byte.scala new file mode 100644 index 0000000..dc0c82f --- /dev/null +++ b/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/javalanglib/src/main/scala/java/lang/CharSequence.scala b/javalanglib/src/main/scala/java/lang/CharSequence.scala new file mode 100644 index 0000000..5875a2d --- /dev/null +++ b/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/javalanglib/src/main/scala/java/lang/Character.scala b/javalanglib/src/main/scala/java/lang/Character.scala new file mode 100644 index 0000000..1b2b565 --- /dev/null +++ b/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/javalanglib/src/main/scala/java/lang/Class.scala b/javalanglib/src/main/scala/java/lang/Class.scala new file mode 100644 index 0000000..e8ff46f --- /dev/null +++ b/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/javalanglib/src/main/scala/java/lang/Cloneable.scala b/javalanglib/src/main/scala/java/lang/Cloneable.scala new file mode 100644 index 0000000..4183bf5 --- /dev/null +++ b/javalanglib/src/main/scala/java/lang/Cloneable.scala @@ -0,0 +1,3 @@ +package java.lang + +trait Cloneable diff --git a/javalanglib/src/main/scala/java/lang/Comparable.scala b/javalanglib/src/main/scala/java/lang/Comparable.scala new file mode 100644 index 0000000..8d17c6f --- /dev/null +++ b/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/javalanglib/src/main/scala/java/lang/Double.scala b/javalanglib/src/main/scala/java/lang/Double.scala new file mode 100644 index 0000000..25987ac --- /dev/null +++ b/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/javalanglib/src/main/scala/java/lang/Float.scala b/javalanglib/src/main/scala/java/lang/Float.scala new file mode 100644 index 0000000..70cb33e --- /dev/null +++ b/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/javalanglib/src/main/scala/java/lang/InheritableThreadLocal.scala b/javalanglib/src/main/scala/java/lang/InheritableThreadLocal.scala new file mode 100644 index 0000000..92ef07c --- /dev/null +++ b/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/javalanglib/src/main/scala/java/lang/Integer.scala b/javalanglib/src/main/scala/java/lang/Integer.scala new file mode 100644 index 0000000..a002fb7 --- /dev/null +++ b/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/javalanglib/src/main/scala/java/lang/Long.scala b/javalanglib/src/main/scala/java/lang/Long.scala new file mode 100644 index 0000000..beeef32 --- /dev/null +++ b/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/javalanglib/src/main/scala/java/lang/Math.scala b/javalanglib/src/main/scala/java/lang/Math.scala new file mode 100644 index 0000000..c8cd7aa --- /dev/null +++ b/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/javalanglib/src/main/scala/java/lang/Number.scala b/javalanglib/src/main/scala/java/lang/Number.scala new file mode 100644 index 0000000..05ffc7a --- /dev/null +++ b/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/javalanglib/src/main/scala/java/lang/Readable.scala b/javalanglib/src/main/scala/java/lang/Readable.scala new file mode 100644 index 0000000..53e5689 --- /dev/null +++ b/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/javalanglib/src/main/scala/java/lang/Runnable.scala b/javalanglib/src/main/scala/java/lang/Runnable.scala new file mode 100644 index 0000000..c98cb41 --- /dev/null +++ b/javalanglib/src/main/scala/java/lang/Runnable.scala @@ -0,0 +1,5 @@ +package java.lang + +trait Runnable { + def run(): Unit +} diff --git a/javalanglib/src/main/scala/java/lang/Runtime.scala b/javalanglib/src/main/scala/java/lang/Runtime.scala new file mode 100644 index 0000000..25aaa9f --- /dev/null +++ b/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/javalanglib/src/main/scala/java/lang/Short.scala b/javalanglib/src/main/scala/java/lang/Short.scala new file mode 100644 index 0000000..135fe12 --- /dev/null +++ b/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/javalanglib/src/main/scala/java/lang/StackTraceElement.scala b/javalanglib/src/main/scala/java/lang/StackTraceElement.scala new file mode 100644 index 0000000..cc87aec --- /dev/null +++ b/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 != "") + 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/javalanglib/src/main/scala/java/lang/StringBuffer.scala b/javalanglib/src/main/scala/java/lang/StringBuffer.scala new file mode 100644 index 0000000..31ee89a --- /dev/null +++ b/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/javalanglib/src/main/scala/java/lang/StringBuilder.scala b/javalanglib/src/main/scala/java/lang/StringBuilder.scala new file mode 100644 index 0000000..e8bd2b7 --- /dev/null +++ b/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/javalanglib/src/main/scala/java/lang/System.scala b/javalanglib/src/main/scala/java/lang/System.scala new file mode 100644 index 0000000..6d80eaf --- /dev/null +++ b/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/javalanglib/src/main/scala/java/lang/Thread.scala b/javalanglib/src/main/scala/java/lang/Thread.scala new file mode 100644 index 0000000..e52d7f6 --- /dev/null +++ b/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/javalanglib/src/main/scala/java/lang/ThreadLocal.scala b/javalanglib/src/main/scala/java/lang/ThreadLocal.scala new file mode 100644 index 0000000..a36a40c --- /dev/null +++ b/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/javalanglib/src/main/scala/java/lang/Throwables.scala b/javalanglib/src/main/scala/java/lang/Throwables.scala new file mode 100644 index 0000000..a38fee9 --- /dev/null +++ b/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(" ") + } + + // 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(" ") + } + } + } + + 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/javalanglib/src/main/scala/java/lang/Void.scala b/javalanglib/src/main/scala/java/lang/Void.scala new file mode 100644 index 0000000..fbe68fb --- /dev/null +++ b/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/javalanglib/src/main/scala/java/lang/ref/PhantomReference.scala b/javalanglib/src/main/scala/java/lang/ref/PhantomReference.scala new file mode 100644 index 0000000..ecace8a --- /dev/null +++ b/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/javalanglib/src/main/scala/java/lang/ref/Reference.scala b/javalanglib/src/main/scala/java/lang/ref/Reference.scala new file mode 100644 index 0000000..76909cf --- /dev/null +++ b/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/javalanglib/src/main/scala/java/lang/ref/ReferenceQueue.scala b/javalanglib/src/main/scala/java/lang/ref/ReferenceQueue.scala new file mode 100644 index 0000000..e9c5110 --- /dev/null +++ b/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/javalanglib/src/main/scala/java/lang/ref/SoftReference.scala b/javalanglib/src/main/scala/java/lang/ref/SoftReference.scala new file mode 100644 index 0000000..eb0fdf7 --- /dev/null +++ b/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/javalanglib/src/main/scala/java/lang/ref/WeakReference.scala b/javalanglib/src/main/scala/java/lang/ref/WeakReference.scala new file mode 100644 index 0000000..2a74aa1 --- /dev/null +++ b/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/javalanglib/src/main/scala/java/lang/reflect/Array.scala b/javalanglib/src/main/scala/java/lang/reflect/Array.scala new file mode 100644 index 0000000..bc3696e --- /dev/null +++ b/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/javalib-ex-test-suite/src/test/scala/scala/scalajs/testsuite/javalibex/DataInputStreamTest.scala b/javalib-ex-test-suite/src/test/scala/scala/scalajs/testsuite/javalibex/DataInputStreamTest.scala new file mode 100644 index 0000000..61ec560 --- /dev/null +++ b/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/javalib-ex-test-suite/src/test/scala/scala/scalajs/testsuite/javalibex/ZipInputStreamTest.scala b/javalib-ex-test-suite/src/test/scala/scala/scalajs/testsuite/javalibex/ZipInputStreamTest.scala new file mode 100644 index 0000000..b2a2fe2 --- /dev/null +++ b/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/javalib-ex/src/main/scala/java/io/DataInputStream.scala b/javalib-ex/src/main/scala/java/io/DataInputStream.scala new file mode 100644 index 0000000..bd11731 --- /dev/null +++ b/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/javalib-ex/src/main/scala/java/util/zip/InflaterInputStream.scala b/javalib-ex/src/main/scala/java/util/zip/InflaterInputStream.scala new file mode 100644 index 0000000..10aa04e --- /dev/null +++ b/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/javalib-ex/src/main/scala/java/util/zip/ZipEntry.scala b/javalib-ex/src/main/scala/java/util/zip/ZipEntry.scala new file mode 100644 index 0000000..89f37a3 --- /dev/null +++ b/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/javalib-ex/src/main/scala/java/util/zip/ZipInputStream.scala b/javalib-ex/src/main/scala/java/util/zip/ZipInputStream.scala new file mode 100644 index 0000000..082f6fc --- /dev/null +++ b/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/javalib/src/main/scala/java/io/BufferedReader.scala b/javalib/src/main/scala/java/io/BufferedReader.scala new file mode 100644 index 0000000..0f06523 --- /dev/null +++ b/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/javalib/src/main/scala/java/io/ByteArrayInputStream.scala b/javalib/src/main/scala/java/io/ByteArrayInputStream.scala new file mode 100644 index 0000000..697e07b --- /dev/null +++ b/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/javalib/src/main/scala/java/io/ByteArrayOutputStream.scala b/javalib/src/main/scala/java/io/ByteArrayOutputStream.scala new file mode 100644 index 0000000..916002d --- /dev/null +++ b/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/javalib/src/main/scala/java/io/Closeable.scala b/javalib/src/main/scala/java/io/Closeable.scala new file mode 100644 index 0000000..e572390 --- /dev/null +++ b/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/javalib/src/main/scala/java/io/DataInput.scala b/javalib/src/main/scala/java/io/DataInput.scala new file mode 100644 index 0000000..37913b4 --- /dev/null +++ b/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/javalib/src/main/scala/java/io/FilterInputStream.scala b/javalib/src/main/scala/java/io/FilterInputStream.scala new file mode 100644 index 0000000..a85b9f6 --- /dev/null +++ b/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/javalib/src/main/scala/java/io/FilterOutputStream.scala b/javalib/src/main/scala/java/io/FilterOutputStream.scala new file mode 100644 index 0000000..299b7b6 --- /dev/null +++ b/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/javalib/src/main/scala/java/io/Flushable.scala b/javalib/src/main/scala/java/io/Flushable.scala new file mode 100644 index 0000000..2879ad2 --- /dev/null +++ b/javalib/src/main/scala/java/io/Flushable.scala @@ -0,0 +1,5 @@ +package java.io + +trait Flushable { + def flush(): Unit +} diff --git a/javalib/src/main/scala/java/io/InputStream.scala b/javalib/src/main/scala/java/io/InputStream.scala new file mode 100644 index 0000000..412d84b --- /dev/null +++ b/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/javalib/src/main/scala/java/io/InputStreamReader.scala b/javalib/src/main/scala/java/io/InputStreamReader.scala new file mode 100644 index 0000000..1ef957c --- /dev/null +++ b/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/javalib/src/main/scala/java/io/OutputStream.scala b/javalib/src/main/scala/java/io/OutputStream.scala new file mode 100644 index 0000000..729e69b --- /dev/null +++ b/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/javalib/src/main/scala/java/io/OutputStreamWriter.scala b/javalib/src/main/scala/java/io/OutputStreamWriter.scala new file mode 100644 index 0000000..18c1c57 --- /dev/null +++ b/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/javalib/src/main/scala/java/io/PrintStream.scala b/javalib/src/main/scala/java/io/PrintStream.scala new file mode 100644 index 0000000..68fa041 --- /dev/null +++ b/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/javalib/src/main/scala/java/io/PrintWriter.scala b/javalib/src/main/scala/java/io/PrintWriter.scala new file mode 100644 index 0000000..4e693e0 --- /dev/null +++ b/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/javalib/src/main/scala/java/io/Reader.scala b/javalib/src/main/scala/java/io/Reader.scala new file mode 100644 index 0000000..97be140 --- /dev/null +++ b/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/javalib/src/main/scala/java/io/Serializable.scala b/javalib/src/main/scala/java/io/Serializable.scala new file mode 100644 index 0000000..01dd228 --- /dev/null +++ b/javalib/src/main/scala/java/io/Serializable.scala @@ -0,0 +1,3 @@ +package java.io + +trait Serializable diff --git a/javalib/src/main/scala/java/io/StringReader.scala b/javalib/src/main/scala/java/io/StringReader.scala new file mode 100644 index 0000000..2ca8f90 --- /dev/null +++ b/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/javalib/src/main/scala/java/io/StringWriter.scala b/javalib/src/main/scala/java/io/StringWriter.scala new file mode 100644 index 0000000..13eca00 --- /dev/null +++ b/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/javalib/src/main/scala/java/io/Throwables.scala b/javalib/src/main/scala/java/io/Throwables.scala new file mode 100644 index 0000000..c312c4c --- /dev/null +++ b/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/javalib/src/main/scala/java/io/Writer.scala b/javalib/src/main/scala/java/io/Writer.scala new file mode 100644 index 0000000..d63b477 --- /dev/null +++ b/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/javalib/src/main/scala/java/net/URI.scala b/javalib/src/main/scala/java/net/URI.scala new file mode 100644 index 0000000..c969f55 --- /dev/null +++ b/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/javalib/src/main/scala/java/net/URISyntaxException.scala b/javalib/src/main/scala/java/net/URISyntaxException.scala new file mode 100644 index 0000000..85e0879 --- /dev/null +++ b/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/javalib/src/main/scala/java/nio/Buffer.scala b/javalib/src/main/scala/java/nio/Buffer.scala new file mode 100644 index 0000000..be7ab7f --- /dev/null +++ b/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/javalib/src/main/scala/java/nio/BufferOverflowException.scala b/javalib/src/main/scala/java/nio/BufferOverflowException.scala new file mode 100644 index 0000000..03f359e --- /dev/null +++ b/javalib/src/main/scala/java/nio/BufferOverflowException.scala @@ -0,0 +1,3 @@ +package java.nio + +class BufferOverflowException extends RuntimeException diff --git a/javalib/src/main/scala/java/nio/BufferUnderflowException.scala b/javalib/src/main/scala/java/nio/BufferUnderflowException.scala new file mode 100644 index 0000000..e286975 --- /dev/null +++ b/javalib/src/main/scala/java/nio/BufferUnderflowException.scala @@ -0,0 +1,3 @@ +package java.nio + +class BufferUnderflowException extends RuntimeException diff --git a/javalib/src/main/scala/java/nio/ByteBuffer.scala b/javalib/src/main/scala/java/nio/ByteBuffer.scala new file mode 100644 index 0000000..b743b39 --- /dev/null +++ b/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/javalib/src/main/scala/java/nio/ByteOrder.scala b/javalib/src/main/scala/java/nio/ByteOrder.scala new file mode 100644 index 0000000..20bac6a --- /dev/null +++ b/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/javalib/src/main/scala/java/nio/CharBuffer.scala b/javalib/src/main/scala/java/nio/CharBuffer.scala new file mode 100644 index 0000000..5e74953 --- /dev/null +++ b/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/javalib/src/main/scala/java/nio/HeapByteBuffer.scala b/javalib/src/main/scala/java/nio/HeapByteBuffer.scala new file mode 100644 index 0000000..ed3fd29 --- /dev/null +++ b/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/javalib/src/main/scala/java/nio/HeapCharBuffer.scala b/javalib/src/main/scala/java/nio/HeapCharBuffer.scala new file mode 100644 index 0000000..546c55d --- /dev/null +++ b/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/javalib/src/main/scala/java/nio/InvalidMarkException.scala b/javalib/src/main/scala/java/nio/InvalidMarkException.scala new file mode 100644 index 0000000..c2d3714 --- /dev/null +++ b/javalib/src/main/scala/java/nio/InvalidMarkException.scala @@ -0,0 +1,3 @@ +package java.nio + +class InvalidMarkException extends IllegalStateException diff --git a/javalib/src/main/scala/java/nio/ReadOnlyBufferException.scala b/javalib/src/main/scala/java/nio/ReadOnlyBufferException.scala new file mode 100644 index 0000000..ee0868b --- /dev/null +++ b/javalib/src/main/scala/java/nio/ReadOnlyBufferException.scala @@ -0,0 +1,3 @@ +package java.nio + +class ReadOnlyBufferException extends UnsupportedOperationException diff --git a/javalib/src/main/scala/java/nio/StringCharBuffer.scala b/javalib/src/main/scala/java/nio/StringCharBuffer.scala new file mode 100644 index 0000000..25bc594 --- /dev/null +++ b/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/javalib/src/main/scala/java/nio/charset/CharacterCodingException.scala b/javalib/src/main/scala/java/nio/charset/CharacterCodingException.scala new file mode 100644 index 0000000..8017348 --- /dev/null +++ b/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/javalib/src/main/scala/java/nio/charset/Charset.scala b/javalib/src/main/scala/java/nio/charset/Charset.scala new file mode 100644 index 0000000..6d1af47 --- /dev/null +++ b/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/javalib/src/main/scala/java/nio/charset/CharsetDecoder.scala b/javalib/src/main/scala/java/nio/charset/CharsetDecoder.scala new file mode 100644 index 0000000..a3532ba --- /dev/null +++ b/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/javalib/src/main/scala/java/nio/charset/CharsetEncoder.scala b/javalib/src/main/scala/java/nio/charset/CharsetEncoder.scala new file mode 100644 index 0000000..37d2296 --- /dev/null +++ b/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/javalib/src/main/scala/java/nio/charset/CoderMalfunctionError.scala b/javalib/src/main/scala/java/nio/charset/CoderMalfunctionError.scala new file mode 100644 index 0000000..33174f3 --- /dev/null +++ b/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/javalib/src/main/scala/java/nio/charset/CoderResult.scala b/javalib/src/main/scala/java/nio/charset/CoderResult.scala new file mode 100644 index 0000000..fdc63cc --- /dev/null +++ b/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/javalib/src/main/scala/java/nio/charset/CodingErrorAction.scala b/javalib/src/main/scala/java/nio/charset/CodingErrorAction.scala new file mode 100644 index 0000000..63b48bb --- /dev/null +++ b/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/javalib/src/main/scala/java/nio/charset/MalformedInputException.scala b/javalib/src/main/scala/java/nio/charset/MalformedInputException.scala new file mode 100644 index 0000000..4c91c1b --- /dev/null +++ b/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/javalib/src/main/scala/java/nio/charset/StandardCharsets.scala b/javalib/src/main/scala/java/nio/charset/StandardCharsets.scala new file mode 100644 index 0000000..38f3f98 --- /dev/null +++ b/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/javalib/src/main/scala/java/nio/charset/UnmappableCharacterException.scala b/javalib/src/main/scala/java/nio/charset/UnmappableCharacterException.scala new file mode 100644 index 0000000..5748f70 --- /dev/null +++ b/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/javalib/src/main/scala/java/nio/charset/UnsupportedCharsetException.scala b/javalib/src/main/scala/java/nio/charset/UnsupportedCharsetException.scala new file mode 100644 index 0000000..97a7a4e --- /dev/null +++ b/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/javalib/src/main/scala/java/util/Arrays.scala b/javalib/src/main/scala/java/util/Arrays.scala new file mode 100644 index 0000000..ed9afd1 --- /dev/null +++ b/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/javalib/src/main/scala/java/util/Comparator.scala b/javalib/src/main/scala/java/util/Comparator.scala new file mode 100644 index 0000000..d63c48e --- /dev/null +++ b/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/javalib/src/main/scala/java/util/Date.scala b/javalib/src/main/scala/java/util/Date.scala new file mode 100644 index 0000000..8f4e85f --- /dev/null +++ b/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/javalib/src/main/scala/java/util/Formattable.scala b/javalib/src/main/scala/java/util/Formattable.scala new file mode 100644 index 0000000..e651fbb --- /dev/null +++ b/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/javalib/src/main/scala/java/util/FormattableFlags.scala b/javalib/src/main/scala/java/util/FormattableFlags.scala new file mode 100644 index 0000000..02f5bce --- /dev/null +++ b/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/javalib/src/main/scala/java/util/Formatter.scala b/javalib/src/main/scala/java/util/Formatter.scala new file mode 100644 index 0000000..5e0ab22 --- /dev/null +++ b/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/javalib/src/main/scala/java/util/Random.scala b/javalib/src/main/scala/java/util/Random.scala new file mode 100644 index 0000000..80428fb --- /dev/null +++ b/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/javalib/src/main/scala/java/util/Throwables.scala b/javalib/src/main/scala/java/util/Throwables.scala new file mode 100644 index 0000000..c4bb3d6 --- /dev/null +++ b/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/javalib/src/main/scala/java/util/UUID.scala b/javalib/src/main/scala/java/util/UUID.scala new file mode 100644 index 0000000..9e6c1d4 --- /dev/null +++ b/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/javalib/src/main/scala/java/util/concurrent/ExecutionException.scala b/javalib/src/main/scala/java/util/concurrent/ExecutionException.scala new file mode 100644 index 0000000..6d04889 --- /dev/null +++ b/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/javalib/src/main/scala/java/util/concurrent/Executor.scala b/javalib/src/main/scala/java/util/concurrent/Executor.scala new file mode 100644 index 0000000..d030551 --- /dev/null +++ b/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/javalib/src/main/scala/java/util/concurrent/TimeUnit.scala b/javalib/src/main/scala/java/util/concurrent/TimeUnit.scala new file mode 100644 index 0000000..a77dbfc --- /dev/null +++ b/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/javalib/src/main/scala/java/util/concurrent/atomic/AtomicBoolean.scala b/javalib/src/main/scala/java/util/concurrent/atomic/AtomicBoolean.scala new file mode 100644 index 0000000..5675c31 --- /dev/null +++ b/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/javalib/src/main/scala/java/util/concurrent/atomic/AtomicInteger.scala b/javalib/src/main/scala/java/util/concurrent/atomic/AtomicInteger.scala new file mode 100644 index 0000000..1f24b7b --- /dev/null +++ b/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/javalib/src/main/scala/java/util/concurrent/atomic/AtomicLong.scala b/javalib/src/main/scala/java/util/concurrent/atomic/AtomicLong.scala new file mode 100644 index 0000000..5bfecf2 --- /dev/null +++ b/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/javalib/src/main/scala/java/util/concurrent/atomic/AtomicReference.scala b/javalib/src/main/scala/java/util/concurrent/atomic/AtomicReference.scala new file mode 100644 index 0000000..650b1e0 --- /dev/null +++ b/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/javalib/src/main/scala/java/util/regex/MatchResult.scala b/javalib/src/main/scala/java/util/regex/MatchResult.scala new file mode 100644 index 0000000..f321c60 --- /dev/null +++ b/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/javalib/src/main/scala/java/util/regex/Matcher.scala b/javalib/src/main/scala/java/util/regex/Matcher.scala new file mode 100644 index 0000000..331f56b --- /dev/null +++ b/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/javalib/src/main/scala/java/util/regex/Pattern.scala b/javalib/src/main/scala/java/util/regex/Pattern.scala new file mode 100644 index 0000000..fda103f --- /dev/null +++ b/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\E by quoted() + */ + @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\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/library-aux/src/main/scala/scala/runtime/ArrayRuntime.scala b/library-aux/src/main/scala/scala/runtime/ArrayRuntime.scala new file mode 100644 index 0000000..ceda199 --- /dev/null +++ b/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/library-aux/src/main/scala/scala/runtime/BoxedUnit.scala b/library-aux/src/main/scala/scala/runtime/BoxedUnit.scala new file mode 100644 index 0000000..b6ac773 --- /dev/null +++ b/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/library-aux/src/main/scala/scala/runtime/RefTypes.scala b/library-aux/src/main/scala/scala/runtime/RefTypes.scala new file mode 100644 index 0000000..4724d13 --- /dev/null +++ b/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/library-aux/src/main/scala/scala/runtime/Statics.scala b/library-aux/src/main/scala/scala/runtime/Statics.scala new file mode 100644 index 0000000..b4e7e52 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/concurrent/JSExecutionContext.scala b/library/src/main/scala/scala/scalajs/concurrent/JSExecutionContext.scala new file mode 100644 index 0000000..c159dcb --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/concurrent/QueueExecutionContext.scala b/library/src/main/scala/scala/scalajs/concurrent/QueueExecutionContext.scala new file mode 100644 index 0000000..1f2ee6f --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/concurrent/RunNowExcecutionContext.scala b/library/src/main/scala/scala/scalajs/concurrent/RunNowExcecutionContext.scala new file mode 100644 index 0000000..ba113b4 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/Array.scala b/library/src/main/scala/scala/scalajs/js/Array.scala new file mode 100644 index 0000000..4c9cf23 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/ArrayOps.scala b/library/src/main/scala/scala/scalajs/js/ArrayOps.scala new file mode 100644 index 0000000..f7948ca --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/Date.scala b/library/src/main/scala/scala/scalajs/js/Date.scala new file mode 100644 index 0000000..18795c7 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/Dictionary.scala b/library/src/main/scala/scala/scalajs/js/Dictionary.scala new file mode 100644 index 0000000..fa68d08 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/Error.scala b/library/src/main/scala/scala/scalajs/js/Error.scala new file mode 100644 index 0000000..129ff41 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/Function.scala b/library/src/main/scala/scala/scalajs/js/Function.scala new file mode 100644 index 0000000..4a7d1d9 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/GlobalScope.scala b/library/src/main/scala/scala/scalajs/js/GlobalScope.scala new file mode 100644 index 0000000..67c09ed --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/JSApp.scala b/library/src/main/scala/scala/scalajs/js/JSApp.scala new file mode 100644 index 0000000..fd12207 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/JSArrayOps.scala b/library/src/main/scala/scala/scalajs/js/JSArrayOps.scala new file mode 100644 index 0000000..71f705d --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/JSConverters.scala b/library/src/main/scala/scala/scalajs/js/JSConverters.scala new file mode 100644 index 0000000..1b21d61 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/JSON.scala b/library/src/main/scala/scala/scalajs/js/JSON.scala new file mode 100644 index 0000000..8404f40 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/JavaScriptException.scala b/library/src/main/scala/scala/scalajs/js/JavaScriptException.scala new file mode 100644 index 0000000..e4803c8 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/Math.scala b/library/src/main/scala/scala/scalajs/js/Math.scala new file mode 100644 index 0000000..02caaa0 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/Primitives.scala b/library/src/main/scala/scala/scalajs/js/Primitives.scala new file mode 100644 index 0000000..1a3d88c --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/PropertyDescriptor.scala b/library/src/main/scala/scala/scalajs/js/PropertyDescriptor.scala new file mode 100644 index 0000000..b91e01d --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/RegExp.scala b/library/src/main/scala/scala/scalajs/js/RegExp.scala new file mode 100644 index 0000000..73f465a --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/ThisFunction.scala b/library/src/main/scala/scala/scalajs/js/ThisFunction.scala new file mode 100644 index 0000000..2506eed --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/UndefOr.scala b/library/src/main/scala/scala/scalajs/js/UndefOr.scala new file mode 100644 index 0000000..b356e3a --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/WrappedArray.scala b/library/src/main/scala/scala/scalajs/js/WrappedArray.scala new file mode 100644 index 0000000..3626c15 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/WrappedDictionary.scala b/library/src/main/scala/scala/scalajs/js/WrappedDictionary.scala new file mode 100644 index 0000000..f215e6e --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/annotation/JSBracketAccess.scala b/library/src/main/scala/scala/scalajs/js/annotation/JSBracketAccess.scala new file mode 100644 index 0000000..596e327 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/annotation/JSExport.scala b/library/src/main/scala/scala/scalajs/js/annotation/JSExport.scala new file mode 100644 index 0000000..0fa9a4e --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/annotation/JSExportAll.scala b/library/src/main/scala/scala/scalajs/js/annotation/JSExportAll.scala new file mode 100644 index 0000000..8174595 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/annotation/JSExportDescendentClasses.scala b/library/src/main/scala/scala/scalajs/js/annotation/JSExportDescendentClasses.scala new file mode 100644 index 0000000..9f2be96 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/annotation/JSExportDescendentObjects.scala b/library/src/main/scala/scala/scalajs/js/annotation/JSExportDescendentObjects.scala new file mode 100644 index 0000000..c196b53 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/annotation/JSExportNamed.scala b/library/src/main/scala/scala/scalajs/js/annotation/JSExportNamed.scala new file mode 100644 index 0000000..718404a --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/annotation/JSName.scala b/library/src/main/scala/scala/scalajs/js/annotation/JSName.scala new file mode 100644 index 0000000..5401749 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/annotation/README.md b/library/src/main/scala/scala/scalajs/js/annotation/README.md new file mode 100644 index 0000000..9ce7ebf --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/annotation/RawJSType.scala b/library/src/main/scala/scala/scalajs/js/annotation/RawJSType.scala new file mode 100644 index 0000000..a5bb771 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/package.scala b/library/src/main/scala/scala/scalajs/js/package.scala new file mode 100644 index 0000000..4a17ba6 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/typedarray/ArrayBuffer.scala b/library/src/main/scala/scala/scalajs/js/typedarray/ArrayBuffer.scala new file mode 100644 index 0000000..b8b8160 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/typedarray/ArrayBufferInputStream.scala b/library/src/main/scala/scala/scalajs/js/typedarray/ArrayBufferInputStream.scala new file mode 100644 index 0000000..f3d2afb --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/typedarray/ArrayBufferView.scala b/library/src/main/scala/scala/scalajs/js/typedarray/ArrayBufferView.scala new file mode 100644 index 0000000..6b25bf5 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/typedarray/DataView.scala b/library/src/main/scala/scala/scalajs/js/typedarray/DataView.scala new file mode 100644 index 0000000..d97544c --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/typedarray/Float32Array.scala b/library/src/main/scala/scala/scalajs/js/typedarray/Float32Array.scala new file mode 100644 index 0000000..abb0dd9 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/typedarray/Float64Array.scala b/library/src/main/scala/scala/scalajs/js/typedarray/Float64Array.scala new file mode 100644 index 0000000..526b376 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/typedarray/Int16Array.scala b/library/src/main/scala/scala/scalajs/js/typedarray/Int16Array.scala new file mode 100644 index 0000000..c71f101 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/typedarray/Int32Array.scala b/library/src/main/scala/scala/scalajs/js/typedarray/Int32Array.scala new file mode 100644 index 0000000..37208e9 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/typedarray/Int8Array.scala b/library/src/main/scala/scala/scalajs/js/typedarray/Int8Array.scala new file mode 100644 index 0000000..690ff07 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/typedarray/TypedArray.scala b/library/src/main/scala/scala/scalajs/js/typedarray/TypedArray.scala new file mode 100644 index 0000000..4e33b5d --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/typedarray/Uint16Array.scala b/library/src/main/scala/scala/scalajs/js/typedarray/Uint16Array.scala new file mode 100644 index 0000000..82d2847 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/typedarray/Uint32Array.scala b/library/src/main/scala/scala/scalajs/js/typedarray/Uint32Array.scala new file mode 100644 index 0000000..9742e19 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/typedarray/Uint8Array.scala b/library/src/main/scala/scala/scalajs/js/typedarray/Uint8Array.scala new file mode 100644 index 0000000..f54904c --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/typedarray/Uint8ClampedArray.scala b/library/src/main/scala/scala/scalajs/js/typedarray/Uint8ClampedArray.scala new file mode 100644 index 0000000..601d65c --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/js/typedarray/package.scala b/library/src/main/scala/scala/scalajs/js/typedarray/package.scala new file mode 100644 index 0000000..0ab5a05 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/niocharset/ISO_8859_1.scala b/library/src/main/scala/scala/scalajs/niocharset/ISO_8859_1.scala new file mode 100644 index 0000000..7765f0c --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/niocharset/ISO_8859_1_And_US_ASCII_Common.scala b/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/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/library/src/main/scala/scala/scalajs/niocharset/StandardCharsets.scala b/library/src/main/scala/scala/scalajs/niocharset/StandardCharsets.scala new file mode 100644 index 0000000..38615f6 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/niocharset/US_ASCII.scala b/library/src/main/scala/scala/scalajs/niocharset/US_ASCII.scala new file mode 100644 index 0000000..746c75b --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/niocharset/UTF_16.scala b/library/src/main/scala/scala/scalajs/niocharset/UTF_16.scala new file mode 100644 index 0000000..9d1748a --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/niocharset/UTF_16BE.scala b/library/src/main/scala/scala/scalajs/niocharset/UTF_16BE.scala new file mode 100644 index 0000000..dece191 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/niocharset/UTF_16LE.scala b/library/src/main/scala/scala/scalajs/niocharset/UTF_16LE.scala new file mode 100644 index 0000000..de469c4 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/niocharset/UTF_16_Common.scala b/library/src/main/scala/scala/scalajs/niocharset/UTF_16_Common.scala new file mode 100644 index 0000000..3330d9c --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/niocharset/UTF_8.scala b/library/src/main/scala/scala/scalajs/niocharset/UTF_8.scala new file mode 100644 index 0000000..57f4ad6 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/runtime/AnonFunctions.scala b/library/src/main/scala/scala/scalajs/runtime/AnonFunctions.scala new file mode 100644 index 0000000..861d81a --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/runtime/Bits.scala b/library/src/main/scala/scala/scalajs/runtime/Bits.scala new file mode 100644 index 0000000..38b2c3e --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/runtime/BooleanReflectiveCall.scala b/library/src/main/scala/scala/scalajs/runtime/BooleanReflectiveCall.scala new file mode 100644 index 0000000..0cd562a --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/runtime/IntegerReflectiveCall.scala b/library/src/main/scala/scala/scalajs/runtime/IntegerReflectiveCall.scala new file mode 100644 index 0000000..ddf65df --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/runtime/NumberReflectiveCall.scala b/library/src/main/scala/scala/scalajs/runtime/NumberReflectiveCall.scala new file mode 100644 index 0000000..a237861 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/runtime/RuntimeLong.scala b/library/src/main/scala/scala/scalajs/runtime/RuntimeLong.scala new file mode 100644 index 0000000..3bd6fb6 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/runtime/RuntimeString.scala b/library/src/main/scala/scala/scalajs/runtime/RuntimeString.scala new file mode 100644 index 0000000..f65b1b5 --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/runtime/StackTrace.scala b/library/src/main/scala/scala/scalajs/runtime/StackTrace.scala new file mode 100644 index 0000000..a9e2c00 --- /dev/null +++ b/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 + * @:: + * or + * @: + * 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("", 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..prototype. + * ScalaJS.c.. + * ScalaJS.i.__ + * ScalaJS.m. + * When the function name is none of those, the pair + * ("", 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) + "" // that's how it would be reported on the JVM + else + decodeMethodName(mtch(2).get) + (className, methodName) + } else { + ("", 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___") { + "" + } 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.\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] = { + // "([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("""""".re, "$1") + .replace("""""".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/library/src/main/scala/scala/scalajs/runtime/UndefinedBehaviorError.scala b/library/src/main/scala/scala/scalajs/runtime/UndefinedBehaviorError.scala new file mode 100644 index 0000000..b06ed7d --- /dev/null +++ b/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/library/src/main/scala/scala/scalajs/runtime/package.scala b/library/src/main/scala/scala/scalajs/runtime/package.scala new file mode 100644 index 0000000..59c774c --- /dev/null +++ b/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/no-ir-check-test/src/test/scala/scala/scalajs/testsuite/noircheck/DummyParentsTest.scala b/no-ir-check-test/src/test/scala/scala/scalajs/testsuite/noircheck/DummyParentsTest.scala new file mode 100644 index 0000000..30c4172 --- /dev/null +++ b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/BlacklistedTests.txt b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/BlacklistedTests.txt new file mode 100644 index 0000000..241b188 --- /dev/null +++ b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/BuglistedTests.txt b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/BuglistedTests.txt new file mode 100644 index 0000000..42c6146 --- /dev/null +++ b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/NoDCEWarn.txt b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/NoDCEWarn.txt new file mode 100644 index 0000000..cc5aff0 --- /dev/null +++ b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/WhitelistedTests.txt b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/WhitelistedTests.txt new file mode 100644 index 0000000..c4df1ec --- /dev/null +++ b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/neg/t7494-no-options.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-01.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-02.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-04.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-08.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-09.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/Course-2002-10.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/bugs.sem b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/bugs.sem @@ -0,0 +1 @@ +asInstanceOfs diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/dynamic-anyval.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/impconvtimes.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/imports.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/issue192.sem b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/issue192.sem @@ -0,0 +1 @@ +strictFloats diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/macro-bundle-static.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/macro-bundle-toplevel.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/macro-bundle-whitebox-decl.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/misc.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/promotion.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/runtime.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/spec-self.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/structural.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t0421-new.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t0421-old.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t1503.sem b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t1503.sem @@ -0,0 +1 @@ +asInstanceOfs diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t3702.check b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t3702.check @@ -0,0 +1,2 @@ +undefined +6 diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t4148.sem b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t4148.sem @@ -0,0 +1 @@ +asInstanceOfs diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t4617.check b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t4617.check @@ -0,0 +1 @@ +Str 8 diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t5552.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t5568.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t5629b.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t5680.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t5866.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t6102.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t6318_primitives.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t6662.check b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t6662.check @@ -0,0 +1 @@ +undefined diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t7657.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t7763.sem b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/t7763.sem @@ -0,0 +1 @@ +asInstanceOfs diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/try-catch-unify.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/virtpatmat_switch.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.0/run/virtpatmat_typetag.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/BlacklistedTests.txt b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/BlacklistedTests.txt new file mode 100644 index 0000000..18a1c5d --- /dev/null +++ b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/BuglistedTests.txt b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/BuglistedTests.txt new file mode 100644 index 0000000..42c6146 --- /dev/null +++ b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/NoDCEWarn.txt b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/NoDCEWarn.txt new file mode 100644 index 0000000..cc5aff0 --- /dev/null +++ b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/WhitelistedTests.txt b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/WhitelistedTests.txt new file mode 100644 index 0000000..1483b1e --- /dev/null +++ b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/neg/t7494-no-options.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-01.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-02.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-04.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-08.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-09.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/Course-2002-10.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/bugs.sem b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/bugs.sem @@ -0,0 +1 @@ +asInstanceOfs diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/dynamic-anyval.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/impconvtimes.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/imports.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/issue192.sem b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/issue192.sem @@ -0,0 +1 @@ +strictFloats diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/macro-bundle-static.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/macro-bundle-toplevel.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/macro-bundle-whitebox-decl.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/misc.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/promotion.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/runtime.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/spec-self.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/structural.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t0421-new.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t0421-old.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t1503.sem b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t1503.sem @@ -0,0 +1 @@ +asInstanceOfs diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t3702.check b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t3702.check @@ -0,0 +1,2 @@ +undefined +6 diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t4148.sem b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t4148.sem @@ -0,0 +1 @@ +asInstanceOfs diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t4617.check b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t4617.check @@ -0,0 +1 @@ +Str 8 diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t5552.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t5568.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t5629b.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t5680.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t5866.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t6102.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t6318_primitives.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t6662.check b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t6662.check @@ -0,0 +1 @@ +undefined diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t7657.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t7763.sem b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t7763.sem @@ -0,0 +1 @@ +asInstanceOfs diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t8570a.check b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/t8570a.check @@ -0,0 +1 @@ +undefined diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/try-catch-unify.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/virtpatmat_switch.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.1/run/virtpatmat_typetag.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/BlacklistedTests.txt b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/BlacklistedTests.txt new file mode 100644 index 0000000..e04dabe --- /dev/null +++ b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/BuglistedTests.txt b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/BuglistedTests.txt new file mode 100644 index 0000000..42c6146 --- /dev/null +++ b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/NoDCEWarn.txt b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/NoDCEWarn.txt new file mode 100644 index 0000000..cc5aff0 --- /dev/null +++ b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/WhitelistedTests.txt b/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/WhitelistedTests.txt new file mode 100644 index 0000000..5538de1 --- /dev/null +++ b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/neg/t7494-no-options.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-01.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-02.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-04.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-08.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-09.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/Course-2002-10.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/bugs.sem b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/bugs.sem @@ -0,0 +1 @@ +asInstanceOfs diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/dynamic-anyval.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/impconvtimes.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/imports.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/issue192.sem b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/issue192.sem @@ -0,0 +1 @@ +strictFloats diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/macro-bundle-static.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/macro-bundle-toplevel.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/macro-bundle-whitebox-decl.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/misc.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/promotion.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/runtime.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/spec-self.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/structural.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t0421-new.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t0421-old.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t1503.sem b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t1503.sem @@ -0,0 +1 @@ +asInstanceOfs diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t3702.check b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t3702.check @@ -0,0 +1,2 @@ +undefined +6 diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t4148.sem b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t4148.sem @@ -0,0 +1 @@ +asInstanceOfs diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t4617.check b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t4617.check @@ -0,0 +1 @@ +Str 8 diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t5552.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t5568.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t5629b.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t5680.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t5866.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t6102.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t6318_primitives.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t6662.check b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t6662.check @@ -0,0 +1 @@ +undefined diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t7657.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t7763.sem b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t7763.sem @@ -0,0 +1 @@ +asInstanceOfs diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t8570a.check b/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/t8570a.check @@ -0,0 +1 @@ +undefined diff --git a/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/try-catch-unify.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/virtpatmat_switch.check b/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/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/partest-suite/src/test/resources/scala/tools/partest/scalajs/2.11.2/run/virtpatmat_typetag.check b/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/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/partest/src/main/scala/scala/tools/nsc/MainGenericRunner.scala b/partest/src/main/scala/scala/tools/nsc/MainGenericRunner.scala new file mode 100644 index 0000000..6857142 --- /dev/null +++ b/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/partest/src/main/scala/scala/tools/partest/scalajs/PartestInterface.scala b/partest/src/main/scala/scala/tools/partest/scalajs/PartestInterface.scala new file mode 100644 index 0000000..0dc2189 --- /dev/null +++ b/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 TaskDefs. + * + *

+ * 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 TaskDefs. + *

+ * + *

+ * This tasks method may be called with TaskDefs containing the same value for testClassName but + * different fingerprints. For example, if both a class and its companion object were test classes, the tasks method could be + * passed an array containing TaskDefs with the same name but with a different value for fingerprint.isModule. + *

+ * + *

+ * A test framework may "reject" a requested task by returning no Task for that TaskDef. + *

+ * + * @param taskDefs the TaskDefs for requested tasks + * @return an array of Tasks + * @throws IllegalStateException if invoked after done 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 Runner 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/partest/src/main/scala/scala/tools/partest/scalajs/ScalaJSPartest.scala b/partest/src/main/scala/scala/tools/partest/scalajs/ScalaJSPartest.scala new file mode 100644 index 0000000..edd0ea9 --- /dev/null +++ b/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/partest/src/main/scala/scala/tools/partest/scalajs/ScalaJSPartestOptions.scala b/partest/src/main/scala/scala/tools/partest/scalajs/ScalaJSPartestOptions.scala new file mode 100644 index 0000000..1f1680a --- /dev/null +++ b/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/project/ExternalCompile.scala b/project/ExternalCompile.scala new file mode 100644 index 0000000..7d37c81 --- /dev/null +++ b/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/project/JavaLangObject.scala b/project/JavaLangObject.scala new file mode 100644 index 0000000..ff82b94 --- /dev/null +++ b/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("")), + 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]) (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("")), 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/project/JavaLangString.scala b/project/JavaLangString.scala new file mode 100644 index 0000000..fc68b29 --- /dev/null +++ b/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/project/ScalaJSBuild.scala b/project/ScalaJSBuild.scala new file mode 100644 index 0000000..5cbc405 --- /dev/null +++ b/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/**/", 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/project/build.properties b/project/build.properties new file mode 100644 index 0000000..64abd37 --- /dev/null +++ b/project/build.properties @@ -0,0 +1 @@ +sbt.version=0.13.6 diff --git a/project/build.sbt b/project/build.sbt new file mode 100644 index 0000000..b110ef2 --- /dev/null +++ b/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/project/project/ScalaJSEnvGenerator.scala b/project/project/ScalaJSEnvGenerator.scala new file mode 100644 index 0000000..a36479f --- /dev/null +++ b/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/sbt-plugin-test/README.md b/sbt-plugin-test/README.md new file mode 100644 index 0000000..71414a3 --- /dev/null +++ b/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/sbt-plugin-test/build.sbt b/sbt-plugin-test/build.sbt new file mode 100644 index 0000000..dd2e12e --- /dev/null +++ b/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/sbt-plugin-test/jetty9/src/main/resources/test.txt b/sbt-plugin-test/jetty9/src/main/resources/test.txt new file mode 100644 index 0000000..68300b8 --- /dev/null +++ b/sbt-plugin-test/jetty9/src/main/resources/test.txt @@ -0,0 +1 @@ +It works! diff --git a/sbt-plugin-test/noDOM/src/main/scala/sbttest/noDOM/Lib.scala b/sbt-plugin-test/noDOM/src/main/scala/sbttest/noDOM/Lib.scala new file mode 100644 index 0000000..884c422 --- /dev/null +++ b/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/sbt-plugin-test/noDOM/src/main/scala/sbttest/noDOM/TestApp.scala b/sbt-plugin-test/noDOM/src/main/scala/sbttest/noDOM/TestApp.scala new file mode 100644 index 0000000..16a4cbe --- /dev/null +++ b/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/sbt-plugin-test/noDOM/src/test/scala/sbttest/noDOM/LibTest.scala b/sbt-plugin-test/noDOM/src/test/scala/sbttest/noDOM/LibTest.scala new file mode 100644 index 0000000..bc24eda --- /dev/null +++ b/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/sbt-plugin-test/project/Jetty9Test.scala b/sbt-plugin-test/project/Jetty9Test.scala new file mode 100644 index 0000000..6a84114 --- /dev/null +++ b/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/sbt-plugin-test/project/build.properties b/sbt-plugin-test/project/build.properties new file mode 100644 index 0000000..64abd37 --- /dev/null +++ b/sbt-plugin-test/project/build.properties @@ -0,0 +1 @@ +sbt.version=0.13.6 diff --git a/sbt-plugin-test/project/build.sbt b/sbt-plugin-test/project/build.sbt new file mode 100644 index 0000000..8419289 --- /dev/null +++ b/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/sbt-plugin-test/project/project/build.sbt b/sbt-plugin-test/project/project/build.sbt new file mode 100644 index 0000000..fb20cb7 --- /dev/null +++ b/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/sbt-plugin-test/withDOM/src/main/scala/sbttest/withDOM/Lib.scala b/sbt-plugin-test/withDOM/src/main/scala/sbttest/withDOM/Lib.scala new file mode 100644 index 0000000..e431557 --- /dev/null +++ b/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

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/sbt-plugin-test/withDOM/src/main/scala/sbttest/withDOM/TestApp.scala b/sbt-plugin-test/withDOM/src/main/scala/sbttest/withDOM/TestApp.scala new file mode 100644 index 0000000..e61ed20 --- /dev/null +++ b/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/sbt-plugin-test/withDOM/src/test/scala/sbttest/withDOM/LibTest.scala b/sbt-plugin-test/withDOM/src/test/scala/sbttest/withDOM/LibTest.scala new file mode 100644 index 0000000..e08e6e3 --- /dev/null +++ b/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/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/AbstractJSDeps.scala b/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/AbstractJSDeps.scala new file mode 100644 index 0000000..9eb7f69 --- /dev/null +++ b/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/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/Implicits.scala b/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/Implicits.scala new file mode 100644 index 0000000..0c1559f --- /dev/null +++ b/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/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/JSUtils.scala b/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/JSUtils.scala new file mode 100644 index 0000000..a59f105 --- /dev/null +++ b/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/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/LoggerJSConsole.scala b/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/LoggerJSConsole.scala new file mode 100644 index 0000000..ecfb546 --- /dev/null +++ b/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/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/OptimizerOptions.scala b/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/OptimizerOptions.scala new file mode 100644 index 0000000..25d6178 --- /dev/null +++ b/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/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/ScalaJSCrossVersion.scala b/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/ScalaJSCrossVersion.scala new file mode 100644 index 0000000..d813622 --- /dev/null +++ b/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/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/ScalaJSPlugin.scala b/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/ScalaJSPlugin.scala new file mode 100644 index 0000000..b33e2fb --- /dev/null +++ b/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/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/ScalaJSPluginInternal.scala b/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/ScalaJSPluginInternal.scala new file mode 100644 index 0000000..fe97f0b --- /dev/null +++ b/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("") + 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/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/Stage.scala b/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/Stage.scala new file mode 100644 index 0000000..7f7b916 --- /dev/null +++ b/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/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/ExternalJSEnv.scala b/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/ExternalJSEnv.scala new file mode 100644 index 0000000..e0aa557 --- /dev/null +++ b/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/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/VirtualFileMaterializer.scala b/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/VirtualFileMaterializer.scala new file mode 100644 index 0000000..fca1c47 --- /dev/null +++ b/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/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/nodejs/NodeJSEnv.scala b/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/nodejs/NodeJSEnv.scala new file mode 100644 index 0000000..dfabe23 --- /dev/null +++ b/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/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/JettyWebsocketManager.scala b/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/JettyWebsocketManager.scala new file mode 100644 index 0000000..3dec79c --- /dev/null +++ b/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/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/PhantomJSEnv.scala b/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/PhantomJSEnv.scala new file mode 100644 index 0000000..7bb47d2 --- /dev/null +++ b/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"""""" + "\n") + case _ => + writer.write("""\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("\n\nPhantom.js Launcher\n") + sendJS(getLibJSFiles(), out) + writeCodeLauncher(code, out) + out.write("\n\n\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("""\n") + } + } + + protected def htmlEscape(str: String): String = str.flatMap { + case '<' => "<" + case '>' => ">" + case '"' => """ + case '&' => "&" + 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/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/PhantomJettyClassLoader.scala b/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/PhantomJettyClassLoader.scala new file mode 100644 index 0000000..02c229b --- /dev/null +++ b/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/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/WebsocketListener.scala b/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/WebsocketListener.scala new file mode 100644 index 0000000..4faac64 --- /dev/null +++ b/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/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/WebsocketManager.scala b/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/WebsocketManager.scala new file mode 100644 index 0000000..a466841 --- /dev/null +++ b/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/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/LazyScalaJSScope.scala b/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/LazyScalaJSScope.scala new file mode 100644 index 0000000..d4cdaee --- /dev/null +++ b/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/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/RhinoJSEnv.scala b/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/RhinoJSEnv.scala new file mode 100644 index 0000000..cd35ff6 --- /dev/null +++ b/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/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/ScalaJSCoreLib.scala b/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/ScalaJSCoreLib.scala new file mode 100644 index 0000000..e937e5b --- /dev/null +++ b/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/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/package.scala b/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/package.scala new file mode 100644 index 0000000..926fbb2 --- /dev/null +++ b/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/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/impl/DependencyBuilders.scala b/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/impl/DependencyBuilders.scala new file mode 100644 index 0000000..32ffb94 --- /dev/null +++ b/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/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/Events.scala b/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/Events.scala new file mode 100644 index 0000000..f13c195 --- /dev/null +++ b/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/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/JSClasspathLoader.scala b/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/JSClasspathLoader.scala new file mode 100644 index 0000000..bfe0ffc --- /dev/null +++ b/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/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/SbtTestLoggerAccWrapper.scala b/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/SbtTestLoggerAccWrapper.scala new file mode 100644 index 0000000..dfebe00 --- /dev/null +++ b/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/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestException.scala b/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestException.scala new file mode 100644 index 0000000..b4cb09b --- /dev/null +++ b/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/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestFramework.scala b/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestFramework.scala new file mode 100644 index 0000000..ab43bfe --- /dev/null +++ b/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/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestOutputConsole.scala b/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestOutputConsole.scala new file mode 100644 index 0000000..9aad956 --- /dev/null +++ b/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/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestRunner.scala b/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestRunner.scala new file mode 100644 index 0000000..e5ca2a2 --- /dev/null +++ b/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/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestTask.scala b/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestTask.scala new file mode 100644 index 0000000..b1cabb9 --- /dev/null +++ b/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/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/AsyncTests.scala b/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/AsyncTests.scala new file mode 100644 index 0000000..422c17b --- /dev/null +++ b/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/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/ComTests.scala b/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/ComTests.scala new file mode 100644 index 0000000..c16decd --- /dev/null +++ b/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/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/JSEnvTest.scala b/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/JSEnvTest.scala new file mode 100644 index 0000000..2a44c80 --- /dev/null +++ b/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/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/NodeJSTest.scala b/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/NodeJSTest.scala new file mode 100644 index 0000000..9a58b5c --- /dev/null +++ b/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/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/PhantomJSTest.scala b/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/PhantomJSTest.scala new file mode 100644 index 0000000..23e240d --- /dev/null +++ b/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/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/RhinoJSEnvTest.scala b/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/RhinoJSEnvTest.scala new file mode 100644 index 0000000..4066c41 --- /dev/null +++ b/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/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/StoreJSConsole.scala b/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/StoreJSConsole.scala new file mode 100644 index 0000000..9c7a84a --- /dev/null +++ b/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/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/StoreLogger.scala b/sbt-plugin/src/test/scala/scala/scalajs/sbtplugin/test/env/StoreLogger.scala new file mode 100644 index 0000000..985b149 --- /dev/null +++ b/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/scalalib/overrides-2.10/scala/Console.scala b/scalalib/overrides-2.10/scala/Console.scala new file mode 100644 index 0000000..7fc2d50 --- /dev/null +++ b/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 + * + * `java.util.Formatter`. + * + * @param text the pattern for formatting the arguments. + * @param args the arguments used to instantiating the pattern. + * @throws java.lang.IllegalArgumentException if there was a problem with the format string or arguments + */ + def printf(text: String, args: Any*) { out.print(text format (args : _*)) } + + /** 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/scalalib/overrides-2.10/scala/collection/immutable/NumericRange.scala b/scalalib/overrides-2.10/scala/collection/immutable/NumericRange.scala new file mode 100644 index 0000000..d3971ee --- /dev/null +++ b/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/scalalib/overrides-2.10/scala/collection/immutable/Range.scala b/scalalib/overrides-2.10/scala/collection/immutable/Range.scala new file mode 100644 index 0000000..5c8758d --- /dev/null +++ b/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/scalalib/overrides-2.10/scala/collection/mutable/Buffer.scala b/scalalib/overrides-2.10/scala/collection/mutable/Buffer.scala new file mode 100644 index 0000000..ec7763b --- /dev/null +++ b/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/scalalib/overrides-2.10/scala/compat/Platform.scala b/scalalib/overrides-2.10/scala/compat/Platform.scala new file mode 100644 index 0000000..77bf7de --- /dev/null +++ b/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/scalalib/overrides-2.10/scala/package.scala b/scalalib/overrides-2.10/scala/package.scala new file mode 100644 index 0000000..5aad9a9 --- /dev/null +++ b/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/scalalib/overrides-2.11/scala/Console.scala b/scalalib/overrides-2.11/scala/Console.scala new file mode 100644 index 0000000..b85f8dc --- /dev/null +++ b/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 + * + * `java.util.Formatter`. + * + * @param text the pattern for formatting the arguments. + * @param args the arguments used to instantiating the pattern. + * @throws java.lang.IllegalArgumentException if there was a problem with the format string or arguments + */ + def printf(text: String, args: Any*) { out.print(text format (args : _*)) } +} + +private[scala] abstract class DeprecatedConsole { + self: Console.type => + + /** Internal usage only. */ + protected def setOutDirect(out: PrintStream): Unit + protected def setErrDirect(err: PrintStream): Unit + protected def setInDirect(in: BufferedReader): Unit + + @deprecated("Use the method in scala.io.StdIn", "2.11.0") def readBoolean(): Boolean = StdIn.readBoolean() + @deprecated("Use the method in scala.io.StdIn", "2.11.0") def readByte(): Byte = StdIn.readByte() + @deprecated("Use the method in scala.io.StdIn", "2.11.0") def readChar(): Char = StdIn.readChar() + @deprecated("Use the method in scala.io.StdIn", "2.11.0") def readDouble(): Double = StdIn.readDouble() + @deprecated("Use the method in scala.io.StdIn", "2.11.0") def readFloat(): Float = StdIn.readFloat() + @deprecated("Use the method in scala.io.StdIn", "2.11.0") def readInt(): Int = StdIn.readInt() + @deprecated("Use the method in scala.io.StdIn", "2.11.0") def readLine(): String = StdIn.readLine() + @deprecated("Use the method in scala.io.StdIn", "2.11.0") def readLine(text: String, args: Any*): String = StdIn.readLine(text, args: _*) + @deprecated("Use the method in scala.io.StdIn", "2.11.0") def readLong(): Long = StdIn.readLong() + @deprecated("Use the method in scala.io.StdIn", "2.11.0") def readShort(): Short = StdIn.readShort() + @deprecated("Use the method in scala.io.StdIn", "2.11.0") def readf(format: String): List[Any] = StdIn.readf(format) + @deprecated("Use the method in scala.io.StdIn", "2.11.0") def readf1(format: String): Any = StdIn.readf1(format) + @deprecated("Use the method in scala.io.StdIn", "2.11.0") def readf2(format: String): (Any, Any) = StdIn.readf2(format) + @deprecated("Use the method in scala.io.StdIn", "2.11.0") def readf3(format: String): (Any, Any, Any) = StdIn.readf3(format) + + /** Sets the default output stream. + * + * @param out the new output stream. + */ + @deprecated("Use withOut", "2.11.0") def setOut(out: PrintStream): Unit = setOutDirect(out) + + /** Sets the default output stream. + * + * @param out the new output stream. + */ + @deprecated("Use withOut", "2.11.0") def setOut(out: OutputStream): Unit = setOutDirect(new PrintStream(out)) + + /** Sets the default error stream. + * + * @param err the new error stream. + */ + @deprecated("Use withErr", "2.11.0") def setErr(err: PrintStream): Unit = setErrDirect(err) + + /** Sets the default error stream. + * + * @param err the new error stream. + */ + @deprecated("Use withErr", "2.11.0") def setErr(err: OutputStream): Unit = setErrDirect(new PrintStream(err)) + + /** Sets the default input stream. + * + * @param reader specifies the new input stream. + */ + @deprecated("Use withIn", "2.11.0") def setIn(reader: Reader): Unit = setInDirect(new BufferedReader(reader)) + + /** Sets the default input stream. + * + * @param in the new input stream. + */ + @deprecated("Use withIn", "2.11.0") def setIn(in: InputStream): Unit = setInDirect(new BufferedReader(new InputStreamReader(in))) +} diff --git a/scalalib/overrides-2.11/scala/collection/immutable/NumericRange.scala b/scalalib/overrides-2.11/scala/collection/immutable/NumericRange.scala new file mode 100644 index 0000000..51f9f68 --- /dev/null +++ b/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/scalalib/overrides-2.11/scala/collection/immutable/Range.scala b/scalalib/overrides-2.11/scala/collection/immutable/Range.scala new file mode 100644 index 0000000..45eed20 --- /dev/null +++ b/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/scalalib/overrides-2.11/scala/collection/mutable/Buffer.scala b/scalalib/overrides-2.11/scala/collection/mutable/Buffer.scala new file mode 100644 index 0000000..2171cb9 --- /dev/null +++ b/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/scalalib/overrides-2.11/scala/compat/Platform.scala b/scalalib/overrides-2.11/scala/compat/Platform.scala new file mode 100644 index 0000000..cdb6916 --- /dev/null +++ b/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/scalalib/overrides-2.11/scala/package.scala b/scalalib/overrides-2.11/scala/package.scala new file mode 100644 index 0000000..21051d4 --- /dev/null +++ b/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/scalalib/overrides/scala/App.scala b/scalalib/overrides/scala/App.scala new file mode 100644 index 0000000..c49817b --- /dev/null +++ b/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/scalalib/overrides/scala/Enumeration.scala b/scalalib/overrides/scala/Enumeration.scala new file mode 100644 index 0000000..bdc1701 --- /dev/null +++ b/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(" 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"" + + 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/scalalib/overrides/scala/Symbol.scala b/scalalib/overrides/scala/Symbol.scala new file mode 100644 index 0000000..1af9d28 --- /dev/null +++ b/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/scalalib/overrides/scala/concurrent/impl/AbstractPromise.scala b/scalalib/overrides/scala/concurrent/impl/AbstractPromise.scala new file mode 100644 index 0000000..8ea135e --- /dev/null +++ b/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/scalalib/overrides/scala/math/ScalaNumber.scala b/scalalib/overrides/scala/math/ScalaNumber.scala new file mode 100644 index 0000000..811346d --- /dev/null +++ b/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/scalalib/overrides/scala/runtime/BoxesRunTime.scala b/scalalib/overrides/scala/runtime/BoxesRunTime.scala new file mode 100644 index 0000000..5df7fd1 --- /dev/null +++ b/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/scalalib/overrides/scala/util/control/NoStackTrace.scala b/scalalib/overrides/scala/util/control/NoStackTrace.scala new file mode 100644 index 0000000..bcc2839 --- /dev/null +++ b/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/scripts/assemble-cli.sh b/scripts/assemble-cli.sh new file mode 100755 index 0000000..8d898e8 --- /dev/null +++ b/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) [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/scripts/build-all-js.sh b/scripts/build-all-js.sh new file mode 100755 index 0000000..de0e295 --- /dev/null +++ b/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/scripts/publish-to-bintray.sh b/scripts/publish-to-bintray.sh new file mode 100755 index 0000000..3b455ea --- /dev/null +++ b/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/stubs/src/main/scala/scala/scalajs/js/annotation/ExportAnnotations.scala b/stubs/src/main/scala/scala/scalajs/js/annotation/ExportAnnotations.scala new file mode 100644 index 0000000..be23596 --- /dev/null +++ b/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/test-bridge/src/main/scala/scala/scalajs/testbridge/Test.scala b/test-bridge/src/main/scala/scala/scalajs/testbridge/Test.scala new file mode 100644 index 0000000..05d3077 --- /dev/null +++ b/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/test-bridge/src/main/scala/scala/scalajs/testbridge/TestFramework.scala b/test-bridge/src/main/scala/scala/scalajs/testbridge/TestFramework.scala new file mode 100644 index 0000000..f855cf6 --- /dev/null +++ b/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/test-bridge/src/main/scala/scala/scalajs/testbridge/TestOutput.scala b/test-bridge/src/main/scala/scala/scalajs/testbridge/TestOutput.scala new file mode 100644 index 0000000..5f3b8b6 --- /dev/null +++ b/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/test-bridge/src/main/scala/scala/scalajs/testbridge/TestOutputLog.scala b/test-bridge/src/main/scala/scala/scalajs/testbridge/TestOutputLog.scala new file mode 100644 index 0000000..3369592 --- /dev/null +++ b/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/test-bridge/src/main/scala/scala/scalajs/testbridge/internal/ConsoleTestOutput.scala b/test-bridge/src/main/scala/scala/scalajs/testbridge/internal/ConsoleTestOutput.scala new file mode 100644 index 0000000..c89a32e --- /dev/null +++ b/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/test-suite/run-jasmine-tests.js b/test-suite/run-jasmine-tests.js new file mode 100644 index 0000000..03bacf1 --- /dev/null +++ b/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/test-suite/scalajs-test-suite-2.10-fastopt.html b/test-suite/scalajs-test-suite-2.10-fastopt.html new file mode 100644 index 0000000..ee06db0 --- /dev/null +++ b/test-suite/scalajs-test-suite-2.10-fastopt.html @@ -0,0 +1,15 @@ + + + + Scala.js Test Suite - Jasmine HTML reporter + + + + + + + + + + + diff --git a/test-suite/scalajs-test-suite-2.10.html b/test-suite/scalajs-test-suite-2.10.html new file mode 100644 index 0000000..7885784 --- /dev/null +++ b/test-suite/scalajs-test-suite-2.10.html @@ -0,0 +1,15 @@ + + + + Scala.js Test Suite - Jasmine HTML reporter + + + + + + + + + + + diff --git a/test-suite/scalajs-test-suite-2.11-fastopt.html b/test-suite/scalajs-test-suite-2.11-fastopt.html new file mode 100644 index 0000000..5387586 --- /dev/null +++ b/test-suite/scalajs-test-suite-2.11-fastopt.html @@ -0,0 +1,15 @@ + + + + Scala.js Test Suite - Jasmine HTML reporter + + + + + + + + + + + diff --git a/test-suite/scalajs-test-suite-2.11.html b/test-suite/scalajs-test-suite-2.11.html new file mode 100644 index 0000000..038ac14 --- /dev/null +++ b/test-suite/scalajs-test-suite-2.11.html @@ -0,0 +1,15 @@ + + + + Scala.js Test Suite - Jasmine HTML reporter + + + + + + + + + + + diff --git a/test-suite/src/test/require-sam/scala/scalajs/testsuite/jsinterop/ArraySAMTest.scala b/test-suite/src/test/require-sam/scala/scalajs/testsuite/jsinterop/ArraySAMTest.scala new file mode 100644 index 0000000..534d203 --- /dev/null +++ b/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/test-suite/src/test/resources/SourceMapTestTemplate.scala b/test-suite/src/test/resources/SourceMapTestTemplate.scala new file mode 100644 index 0000000..2b135ef --- /dev/null +++ b/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/**/` is replaced by the number of /n-star/s in the + * file. + */ +object SourceMapTest extends JasmineTest { + + val testCount: Int = 0/**/ + + 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": "" \u005Cu0022 %22 0x22 034 "", + | "\/\\\"\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") == "// /*