summaryrefslogtreecommitdiff
path: root/compiler/src/test/scala/scala/scalajs/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/src/test/scala/scala/scalajs/compiler')
-rw-r--r--compiler/src/test/scala/scala/scalajs/compiler/test/DiverseErrorsTest.scala31
-rw-r--r--compiler/src/test/scala/scala/scalajs/compiler/test/EnumerationInteropTest.scala135
-rw-r--r--compiler/src/test/scala/scala/scalajs/compiler/test/JSDynamicLiteralTest.scala102
-rw-r--r--compiler/src/test/scala/scala/scalajs/compiler/test/JSExportASTTest.scala38
-rw-r--r--compiler/src/test/scala/scala/scalajs/compiler/test/JSExportTest.scala745
-rw-r--r--compiler/src/test/scala/scala/scalajs/compiler/test/JSInteropTest.scala350
-rw-r--r--compiler/src/test/scala/scala/scalajs/compiler/test/OptimizationTest.scala109
-rw-r--r--compiler/src/test/scala/scala/scalajs/compiler/test/PositionTest.scala37
-rw-r--r--compiler/src/test/scala/scala/scalajs/compiler/test/util/DirectTest.scala70
-rw-r--r--compiler/src/test/scala/scala/scalajs/compiler/test/util/JSASTTest.scala100
-rw-r--r--compiler/src/test/scala/scala/scalajs/compiler/test/util/TestHelpers.scala68
11 files changed, 1785 insertions, 0 deletions
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 = "<foo with illegal URI chars: %%>"
+ val source = new BatchSourceFile(name,
+ """class A { def x = 1 }""")
+
+ var found = false
+ sourceAST(source) traverse {
+ case lit: js.IntLiteral =>
+ found = true
+ assertEquals(
+ "Scheme of virtual file URI should be `virtualfile'",
+ "virtualfile", lit.pos.source.getScheme)
+ assertEquals(
+ "Scheme specific part of virtual file URI should be its path",
+ name, lit.pos.source.getSchemeSpecificPart)
+ }
+
+ assertTrue("Should have IntLiteral tree", found)
+
+ }
+
+}
diff --git a/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} }")
+ }
+
+}