| /* |
| * Copyright 2015 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package com.google.gwt.dev.jjs.impl; |
| |
| import com.google.gwt.core.ext.TreeLogger; |
| import com.google.gwt.dev.MinimalRebuildCache; |
| import com.google.gwt.dev.javac.testing.impl.MockJavaResource; |
| import com.google.gwt.dev.jjs.ast.JMethod; |
| import com.google.gwt.dev.jjs.ast.JProgram; |
| import com.google.gwt.dev.util.arg.SourceLevel; |
| |
| /** |
| * Tests for the JsInteropRestrictionChecker. |
| */ |
| public class JsInteropRestrictionCheckerTest extends OptimizerTestBase { |
| @Override |
| public void setUp() { |
| sourceLevel = SourceLevel.JAVA8; |
| } |
| |
| // TODO: eventually test this for default methods in Java 8. |
| public void testCollidingAccidentalOverrideConcreteMethodFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType", |
| "public static interface Foo {", |
| " void doIt(Foo foo);", |
| "}", |
| "@JsType", |
| "public static interface Bar {", |
| " void doIt(Bar bar);", |
| "}", |
| "public static class ParentBuggy {", |
| " public void doIt(Foo foo) {}", |
| " public void doIt(Bar bar) {}", |
| "}", |
| "public static class Buggy extends ParentBuggy implements Foo, Bar {", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 14: 'void EntryPoint.ParentBuggy.doIt(EntryPoint.Bar)' " |
| + "(exposed by 'EntryPoint.Buggy') and " |
| + "'void EntryPoint.ParentBuggy.doIt(EntryPoint.Foo)' (exposed by 'EntryPoint.Buggy') " |
| + "cannot both use the same JavaScript name 'doIt'."); |
| } |
| |
| public void testCollidingAccidentalOverrideAbstractMethodFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType", |
| "public static interface Foo {", |
| " void doIt(Foo foo);", |
| "}", |
| "@JsType", |
| "public static interface Bar {", |
| " void doIt(Bar bar);", |
| "}", |
| "public static abstract class Baz implements Foo, Bar {", |
| " public abstract void doIt(Foo foo);", |
| " public abstract void doIt(Bar bar);", |
| "}", |
| "public static class Buggy {} // Unrelated class"); |
| |
| assertBuggyFails( |
| "Line 14: 'void EntryPoint.Baz.doIt(EntryPoint.Bar)' and " |
| + "'void EntryPoint.Baz.doIt(EntryPoint.Foo)' cannot both use the same " |
| + "JavaScript name 'doIt'."); |
| } |
| |
| public void testCollidingAccidentalOverrideHalfAndHalfFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "public static interface Foo {", |
| " void doIt(Foo foo);", |
| "}", |
| "@JsType", |
| "public static interface Bar {", |
| " void doIt(Bar bar);", |
| "}", |
| "public static class ParentParent {", |
| " public void doIt(Bar x) {}", |
| "}", |
| "@JsType", |
| "public static class Parent extends ParentParent {", |
| " public void doIt(Foo x) {}", |
| "}", |
| "public static class Buggy extends Parent implements Bar {}"); |
| |
| assertBuggyFails( |
| "Line 12: 'void EntryPoint.ParentParent.doIt(EntryPoint.Bar)' " |
| + "(exposed by 'EntryPoint.Buggy') and 'void EntryPoint.Parent.doIt(EntryPoint.Foo)' " |
| + "cannot both use the same JavaScript name 'doIt'."); |
| } |
| |
| public void testOverrideNoNameSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsIgnore"); |
| addSnippetImport("jsinterop.annotations.JsMethod"); |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "public static class Parent {", |
| " @JsMethod(name = \"a\")", |
| " public void ma() {}", |
| " @JsMethod(name = \"b\")", |
| " public void mb() {}", |
| "}", |
| "@JsType", |
| "public static class Child1 extends Parent {", |
| " public void ma() {}", |
| " public void mb() {}", |
| "}", |
| "public static class Child2 extends Parent {", |
| " @JsMethod", |
| " public void ma() {}", |
| " @JsMethod", |
| " public void mb() {}", |
| "}", |
| "public static class Child3 extends Parent {", |
| " public void ma() {}", |
| " public void mb() {}", |
| "}", |
| "@JsType", |
| "public static class Child4 extends Parent {", |
| " @JsIgnore", |
| " public void ma() {}", |
| " @JsIgnore", |
| " public void mb() {}", |
| "}", |
| "public static class Buggy extends Parent {", |
| " Child1 c1;", |
| " Child2 c2;", |
| " Child3 c3;", |
| " Child4 c4;", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| public void testCollidingFieldExportsFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsProperty"); |
| addSnippetClassDecl( |
| "public static class Buggy {", |
| " @JsProperty", |
| " public static final int show = 0;", |
| " @JsProperty(name = \"show\")", |
| " public static final int display = 0;", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 8: 'int EntryPoint.Buggy.display' cannot be exported because the global " |
| + "name 'test.EntryPoint.Buggy.show' is already taken by " |
| + "'int EntryPoint.Buggy.show'."); |
| } |
| |
| public void testJsPropertyNonGetterStyleSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsProperty"); |
| addSnippetClassDecl( |
| "@JsType", |
| "public interface Buggy {", |
| " @JsProperty(name = \"x\") int x();", |
| " @JsProperty(name = \"x\") void x(int x);", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testJsPropertyGetterStyleSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsProperty"); |
| addSnippetClassDecl( |
| "@JsType", |
| "public abstract static class Buggy {", |
| " @JsProperty static native int getStaticX();", |
| " @JsProperty static native void setStaticX(int x);", |
| " @JsProperty abstract int getX();", |
| " @JsProperty abstract void setX(int x);", |
| " @JsProperty abstract boolean isY();", |
| " @JsProperty abstract void setY(boolean y);", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testJsPropertyIncorrectGetterStyleFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsProperty"); |
| addSnippetClassDecl( |
| "public interface Buggy {", |
| " @JsProperty int isX();", |
| " @JsProperty int getY(int x);", |
| " @JsProperty void getZ();", |
| " @JsProperty void setX(int x, int y);", |
| " @JsProperty void setY();", |
| " @JsProperty int setZ(int z);", |
| " @JsProperty static void setStatic(){}", |
| " @JsProperty void setW(int... z);", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 6: JsProperty 'int EntryPoint.Buggy.isX()' cannot have a non-boolean return.", |
| "Line 7: JsProperty 'int EntryPoint.Buggy.getY(int)' should have a correct setter " |
| + "or getter signature.", |
| "Line 8: JsProperty 'void EntryPoint.Buggy.getZ()' should have a correct setter " |
| + "or getter signature.", |
| "Line 9: JsProperty 'void EntryPoint.Buggy.setX(int, int)' should have a correct setter " |
| + "or getter signature.", |
| "Line 10: JsProperty 'void EntryPoint.Buggy.setY()' should have a correct setter " |
| + "or getter signature.", |
| "Line 11: JsProperty 'int EntryPoint.Buggy.setZ(int)' should have a correct setter " |
| + "or getter signature.", |
| "Line 12: JsProperty 'void EntryPoint.Buggy.setStatic()' should have a correct setter " |
| + "or getter signature.", |
| "Line 13: JsProperty 'void EntryPoint.Buggy.setW(int[])' cannot have a vararg parameter."); |
| } |
| |
| public void testJsPropertyNonGetterStyleFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsProperty"); |
| addSnippetClassDecl( |
| "@JsType", |
| "public interface Buggy {", |
| " @JsProperty boolean hasX();", |
| " @JsProperty int x();", |
| " @JsProperty void x(int x);", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 7: JsProperty 'boolean EntryPoint.Buggy.hasX()' should either follow Java Bean " |
| + "naming conventions or provide a name.", |
| "Line 8: JsProperty 'int EntryPoint.Buggy.x()' should either follow Java Bean " |
| + "naming conventions or provide a name.", |
| "Line 9: JsProperty 'void EntryPoint.Buggy.x(int)' should either follow Java Bean " |
| + "naming conventions or provide a name."); |
| } |
| |
| public void testCollidingJsPropertiesTwoGettersFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsProperty"); |
| addSnippetClassDecl( |
| "@JsType", |
| "public static interface Buggy {", |
| " @JsProperty", |
| " boolean isX();", |
| " @JsProperty", |
| " boolean getX();", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 10: 'boolean EntryPoint.Buggy.getX()' and 'boolean EntryPoint.Buggy.isX()' " |
| + "cannot both use the same JavaScript name 'x'."); |
| } |
| |
| public void testCollidingNativeJsPropertiesSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsMethod"); |
| addSnippetImport("jsinterop.annotations.JsProperty"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true)", |
| "public static class Buggy {", |
| " @JsMethod", |
| " public native int now();", |
| " @JsProperty", |
| " public native Object getNow();", |
| " @JsMethod", |
| " public static native int other();", |
| " @JsProperty", |
| " public static native Object getOther();", |
| " @JsMethod", |
| " public static native int another();", |
| " @JsProperty", |
| " public static Object another;", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testCollidingJsPropertiesTwoSettersFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsProperty"); |
| addSnippetClassDecl( |
| "@JsType", |
| "public static interface Buggy {", |
| " @JsProperty", |
| " void setX(boolean x);", |
| " @JsProperty", |
| " void setX(int x);", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 10: 'void EntryPoint.Buggy.setX(int)' and 'void EntryPoint.Buggy.setX(boolean)' " |
| + "cannot both use the same JavaScript name 'x'."); |
| } |
| |
| public void testCollidingJsMethodAndJsPropertyGetterFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsMethod"); |
| addSnippetImport("jsinterop.annotations.JsProperty"); |
| addSnippetClassDecl( |
| "public static interface IBuggy {", |
| " @JsMethod", |
| " boolean x(boolean foo);", |
| " @JsProperty", |
| " int getX();", |
| "}", |
| "public static class Buggy implements IBuggy {", |
| " public boolean x(boolean foo) {return false;}", |
| " public int getX() {return 0;}", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 9: 'int EntryPoint.IBuggy.getX()' and 'boolean EntryPoint.IBuggy.x(boolean)' " |
| + "cannot both use the same JavaScript name 'x'.", |
| "Line 13: 'int EntryPoint.Buggy.getX()' and 'boolean EntryPoint.Buggy.x(boolean)' " |
| + "cannot both use the same JavaScript name 'x'."); |
| } |
| |
| public void testCollidingJsMethodAndJsPropertySetterFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsMethod"); |
| addSnippetImport("jsinterop.annotations.JsProperty"); |
| addSnippetClassDecl( |
| "public static interface IBuggy {", |
| " @JsMethod", |
| " boolean x(boolean foo);", |
| " @JsProperty", |
| " void setX(int a);", |
| "}", |
| "public static class Buggy implements IBuggy {", |
| " public boolean x(boolean foo) {return false;}", |
| " public void setX(int a) {}", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 9: 'void EntryPoint.IBuggy.setX(int)' and 'boolean EntryPoint.IBuggy.x(boolean)' " |
| + "cannot both use the same JavaScript name 'x'.", |
| "Line 13: 'void EntryPoint.Buggy.setX(int)' and 'boolean EntryPoint.Buggy.x(boolean)' " |
| + "cannot both use the same JavaScript name 'x'."); |
| } |
| |
| // TODO(rluble): enable when static property definitions are implemented. |
| public void __disabled__testCollidingPropertyAccessorExportsFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsProperty"); |
| addSnippetClassDecl( |
| "public static class Buggy {", |
| " @JsProperty", |
| " public static void setDisplay(int x) {}", |
| " @JsProperty(name = \"display\")", |
| " public static void setDisplay2(int x) {}", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 8: 'void EntryPoint.Buggy.setDisplay2(int)' cannot be exported because the global " |
| + "name 'test.EntryPoint.Buggy.display' is already taken " |
| + "by 'void EntryPoint.Buggy.setDisplay(int)'."); |
| } |
| |
| public void testCollidingMethodExportsFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsMethod"); |
| addSnippetClassDecl( |
| "public static class Buggy {", |
| " @JsMethod", |
| " public static void show() {}", |
| " @JsMethod(name = \"show\")", |
| " public static void display() {}", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 8: 'void EntryPoint.Buggy.display()' cannot be exported because the global name " |
| + "'test.EntryPoint.Buggy.show' is already taken " |
| + "by 'void EntryPoint.Buggy.show()'."); |
| } |
| |
| // TODO(rluble): enable when static property definitions are implemented. |
| public void __disabled__testCollidingMethodToPropertyAccessorExportsFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsMethod"); |
| addSnippetImport("jsinterop.annotations.JsProperty"); |
| addSnippetClassDecl( |
| "public static class Buggy {", |
| " @JsProperty", |
| " public static void setShow(int x) {}", |
| " @JsMethod", |
| " public static void show() {}", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 9: 'void EntryPoint.Buggy.show()' cannot be exported because the global name " |
| + "'test.EntryPoint.Buggy.show' is already taken by " |
| + "'void EntryPoint.Buggy.setShow(int)'."); |
| } |
| |
| public void testCollidingMethodToFieldExportsFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsMethod"); |
| addSnippetImport("jsinterop.annotations.JsProperty"); |
| addSnippetClassDecl( |
| "public static class Buggy {", |
| " @JsMethod", |
| " public static void show() {}", |
| " @JsProperty", |
| " public static final int show = 0;", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 7: 'void EntryPoint.Buggy.show()' cannot be exported because the global name " |
| + "'test.EntryPoint.Buggy.show' is already taken by " |
| + "'int EntryPoint.Buggy.show'."); |
| } |
| |
| public void testCollidingMethodToFieldJsTypeFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType", |
| "public static class Buggy {", |
| " public void show() {}", |
| " public final int show = 0;", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 6: 'void EntryPoint.Buggy.show()' and 'int EntryPoint.Buggy.show' cannot both use " |
| + "the same JavaScript name 'show'."); |
| } |
| |
| public void testCollidingMethodToMethodJsTypeFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType", |
| "public static class Buggy {", |
| " public void show(int x) {}", |
| " public void show() {}", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 7: 'void EntryPoint.Buggy.show()' and 'void EntryPoint.Buggy.show(int)' cannot both " |
| + "use the same JavaScript name 'show'."); |
| } |
| |
| public void testCollidingSubclassExportedFieldToFieldJsTypeSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType", |
| "public static class ParentBuggy {", |
| " public int foo = 55;", |
| "}", |
| "public static class Buggy extends ParentBuggy {", |
| " public int foo = 110;", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testCollidingSubclassExportedFieldToMethodJsTypeSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType", |
| "public static class ParentBuggy {", |
| " public int foo = 55;", |
| "}", |
| "public static class Buggy extends ParentBuggy {", |
| " public void foo(int a) {}", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testCollidingSubclassExportedMethodToMethodJsTypeSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType", |
| "public static class ParentBuggy {", |
| " public void foo() {}", |
| "}", |
| "public static class Buggy extends ParentBuggy {", |
| " public void foo(int a) {}", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testCollidingSubclassFieldToExportedFieldJsTypeSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "public static class ParentBuggy {", |
| " public int foo = 55;", |
| "}", |
| "@JsType", |
| "public static class Buggy extends ParentBuggy {", |
| " public int foo = 110;", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testCollidingSubclassFieldToExportedMethodJsTypeSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "public static class ParentBuggy {", |
| " public int foo = 55;", |
| "}", |
| "@JsType", |
| "public static class Buggy extends ParentBuggy {", |
| " public void foo(int a) {}", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testCollidingSubclassFieldToFieldJsTypeFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType", |
| "public static class ParentBuggy {", |
| " public int foo = 55;", |
| "}", |
| "@JsType", |
| "public static class Buggy extends ParentBuggy {", |
| " public int foo = 110;", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 10: 'int EntryPoint.Buggy.foo' and 'int EntryPoint.ParentBuggy.foo' cannot both use " |
| + "the same JavaScript name 'foo'."); |
| } |
| |
| public void testCollidingSubclassFieldToMethodJsTypeFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType", |
| "public static class ParentBuggy {", |
| " public int foo = 55;", |
| "}", |
| "@JsType", |
| "public static class Buggy extends ParentBuggy {", |
| " public void foo(int a) {}", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 10: 'void EntryPoint.Buggy.foo(int)' and 'int EntryPoint.ParentBuggy.foo' cannot " |
| + "both use the same JavaScript name 'foo'."); |
| } |
| |
| public void testCollidingSubclassMethodToExportedMethodJsTypeSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "public static class ParentBuggy {", |
| " public void foo() {}", |
| "}", |
| "@JsType", |
| "public static class Buggy extends ParentBuggy {", |
| " public void foo(int a) {}", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testCollidingSubclassMethodToMethodInterfaceJsTypeFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType", |
| "public interface IBuggy1 {", |
| " void show();", |
| "}", |
| "@JsType", |
| "public interface IBuggy2 {", |
| " void show(boolean b);", |
| "}", |
| "public static class Buggy implements IBuggy1 {", |
| " public void show() {}", |
| "}", |
| "public static class Buggy2 extends Buggy implements IBuggy2 {", |
| " public void show(boolean b) {}", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 16: 'void EntryPoint.Buggy2.show(boolean)' and 'void EntryPoint.Buggy.show()' cannot " |
| + "both use the same JavaScript name 'show'."); |
| } |
| |
| public void testCollidingSubclassMethodToMethodJsTypeFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType", |
| "public static class ParentBuggy {", |
| " public void foo() {}", |
| "}", |
| "@JsType", |
| "public static class Buggy extends ParentBuggy {", |
| " public void foo(int a) {}", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 10: 'void EntryPoint.Buggy.foo(int)' and 'void EntryPoint.ParentBuggy.foo()' " |
| + "cannot both use the same JavaScript name 'foo'."); |
| } |
| |
| public void testCollidingSubclassMethodToMethodTwoLayerInterfaceJsTypeFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType", |
| "public interface IParentBuggy1 {", |
| " void show();", |
| "}", |
| "public interface IBuggy1 extends IParentBuggy1 {", |
| "}", |
| "@JsType", |
| "public interface IParentBuggy2 {", |
| " void show(boolean b);", |
| "}", |
| "public interface IBuggy2 extends IParentBuggy2 {", |
| "}", |
| "public static class Buggy implements IBuggy1 {", |
| " public void show() {}", |
| "}", |
| "public static class Buggy2 extends Buggy implements IBuggy2 {", |
| " public void show(boolean b) {}", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 20: 'void EntryPoint.Buggy2.show(boolean)' and 'void EntryPoint.Buggy.show()' " |
| + "cannot both use the same JavaScript name 'show'."); |
| } |
| |
| public void testNonCollidingSyntheticBridgeMethodSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsProperty"); |
| addSnippetClassDecl( |
| "public static interface Comparable<T> {", |
| " int compareTo(T other);", |
| "}", |
| "@JsType", |
| "public static class Enum<E extends Enum<E>> implements Comparable<E> {", |
| " public int compareTo(E other) {return 0;}", |
| "}", |
| "public static class Buggy {}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testCollidingSyntheticBridgeMethodSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType", |
| "public static interface Comparable<T> {", |
| " int compareTo(T other);", |
| "}", |
| "@JsType", |
| "public static class Enum<E extends Enum<E>> implements Comparable<E> {", |
| " public int compareTo(E other) {return 0;}", |
| "}", |
| "public static class Buggy {}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testSpecializeReturnTypeInImplementorSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType", |
| "interface I {", |
| " I m();", |
| "}", |
| "@JsType", |
| "public static class Buggy implements I {", |
| " public Buggy m() { return null; } ", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testSpecializeReturnTypeInSubclassSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType", |
| "public static class S {", |
| " public S m() { return null; }", |
| "}", |
| "@JsType", |
| "public static class Buggy extends S {", |
| " public Buggy m() { return null; } ", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testCollidingTwoLayerSubclassFieldToFieldJsTypeFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType", |
| "public static class ParentParentBuggy {", |
| " public int foo = 55;", |
| "}", |
| "public static class ParentBuggy extends ParentParentBuggy {", |
| " public int foo = 55;", |
| "}", |
| "@JsType", |
| "public static class Buggy extends ParentBuggy {", |
| " public int foo = 110;", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 13: 'int EntryPoint.Buggy.foo' and 'int EntryPoint.ParentParentBuggy.foo' cannot " |
| + "both use the same JavaScript name 'foo'."); |
| } |
| |
| public void testShadowedSuperclassJsMethodFails() { |
| addSnippetImport("jsinterop.annotations.JsMethod"); |
| addSnippetClassDecl( |
| "public static class ParentBuggy {", |
| " @JsMethod private void foo() {}", |
| "}", |
| "public static class Buggy extends ParentBuggy {", |
| " @JsMethod private void foo() {}", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 8: 'void EntryPoint.Buggy.foo()' and 'void EntryPoint.ParentBuggy.foo()' cannot " |
| + "both use the same JavaScript name 'foo'."); |
| } |
| |
| public void testRenamedSuperclassJsMethodFails() { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsMethod"); |
| addSnippetClassDecl( |
| "@JsType", |
| "public static class ParentBuggy {", |
| " public void foo() {}", |
| "}", |
| "public static class Buggy extends ParentBuggy {", |
| " @JsMethod(name = \"bar\") public void foo() {}", |
| "}"); |
| |
| assertBuggyFails("Line 10: 'void EntryPoint.Buggy.foo()' cannot be assigned a different " |
| + "JavaScript name than the method it overrides."); |
| } |
| |
| public void testRenamedSuperInterfaceJsMethodFails() { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsMethod"); |
| addSnippetClassDecl( |
| "@JsType", |
| "public interface ParentBuggy {", |
| " void foo();;", |
| "}", |
| "public interface Buggy extends ParentBuggy {", |
| " @JsMethod(name = \"bar\") void foo();", |
| "}"); |
| |
| assertBuggyFails("Line 10: 'void EntryPoint.Buggy.foo()' cannot be assigned a different " |
| + "JavaScript name than the method it overrides."); |
| } |
| |
| public void testAccidentallyRenamedSuperInterfaceJsMethodFails() { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsMethod"); |
| addSnippetClassDecl( |
| "@JsType", |
| "public interface IBuggy {", |
| " void foo();", |
| "}", |
| "@JsType", |
| "public static class ParentBuggy {", |
| " @JsMethod(name = \"bar\") public void foo() {}", |
| "}", |
| "public static class Buggy extends ParentBuggy implements IBuggy {", |
| "}"); |
| |
| assertBuggyFails("Line 11: 'void EntryPoint.ParentBuggy.foo()' (exposed by 'EntryPoint.Buggy') " |
| + "cannot be assigned a different JavaScript name than the method it overrides."); |
| } |
| |
| public void testRenamedSuperclassJsPropertyFails() { |
| addSnippetImport("jsinterop.annotations.JsProperty"); |
| addSnippetClassDecl( |
| "public static class ParentBuggy {", |
| " @JsProperty public int getFoo() { return 0; }", |
| "}", |
| "public static class Buggy extends ParentBuggy {", |
| " @JsProperty(name = \"bar\") public int getFoo() { return 0; }", |
| "}"); |
| |
| assertBuggyFails("Line 8: 'int EntryPoint.Buggy.getFoo()' " |
| + "cannot be assigned a different JavaScript name than the method it overrides."); |
| } |
| |
| public void testJsPropertyDifferentFlavourInSubclassFails() { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsProperty"); |
| addSnippetClassDecl( |
| "@JsType", |
| "public static class ParentBuggy {", |
| " @JsProperty public boolean isFoo() { return false; }", |
| "}", |
| "public static class Buggy extends ParentBuggy {", |
| " @JsProperty public boolean getFoo() { return false;}", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 10: 'boolean EntryPoint.Buggy.getFoo()' and 'boolean EntryPoint.ParentBuggy.isFoo()' " |
| + "cannot both use the same JavaScript name 'foo'."); |
| } |
| |
| public void testConsistentPropertyTypeSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsProperty"); |
| addSnippetClassDecl( |
| "@JsType", |
| "public static interface IBuggy {", |
| " @JsProperty", |
| " public int getFoo();", |
| " @JsProperty", |
| " public void setFoo(int value);", |
| "}", |
| "public static class Buggy implements IBuggy {", |
| " public int getFoo() {return 0;}", |
| " public void setFoo(int value) {}", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testInconsistentGetSetPropertyTypeFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsProperty"); |
| addSnippetClassDecl( |
| "@JsType", |
| "public static interface IBuggy {", |
| " @JsProperty", |
| " public int getFoo();", |
| " @JsProperty", |
| " public void setFoo(Integer value);", |
| "}", |
| "public static class Buggy implements IBuggy {", |
| " public int getFoo() {return 0;}", |
| " public void setFoo(Integer value) {}", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 10: JsProperty setter 'void EntryPoint.IBuggy.setFoo(Integer)' and " |
| + "getter 'int EntryPoint.IBuggy.getFoo()' cannot have inconsistent types.", |
| "Line 14: JsProperty setter 'void EntryPoint.Buggy.setFoo(Integer)' and " |
| + "getter 'int EntryPoint.Buggy.getFoo()' cannot have inconsistent types."); |
| } |
| |
| public void testInconsistentIsSetPropertyTypeFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsProperty"); |
| addSnippetClassDecl( |
| "@JsType", |
| "public static interface IBuggy {", |
| " @JsProperty", |
| " public boolean isFoo();", |
| " @JsProperty", |
| " public void setFoo(Object value);", |
| "}", |
| "public static class Buggy implements IBuggy {", |
| " public boolean isFoo() {return false;}", |
| " public void setFoo(Object value) {}", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 10: JsProperty setter 'void EntryPoint.IBuggy.setFoo(Object)' and " |
| + "getter 'boolean EntryPoint.IBuggy.isFoo()' cannot have inconsistent types.", |
| "Line 14: JsProperty setter 'void EntryPoint.Buggy.setFoo(Object)' and " |
| + "getter 'boolean EntryPoint.Buggy.isFoo()' cannot have inconsistent types."); |
| } |
| |
| public void testJsPropertySuperCallFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsProperty"); |
| addSnippetClassDecl( |
| "@JsType public static class Super {", |
| " @JsProperty public int getX() { return 5; }", |
| "}", |
| "@JsType public static class Buggy extends Super {", |
| " public int m() { return super.getX(); }", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 9: Cannot call property accessor 'int EntryPoint.Super.getX()' via super."); |
| } |
| |
| public void testJsPropertyOnStaticMethodFails() { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsProperty"); |
| addSnippetClassDecl( |
| "@JsType public static class Buggy {", |
| " @JsProperty public static int getX() { return 0; }", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 6: Static property accessor 'int EntryPoint.Buggy.getX()' can only be native."); |
| } |
| |
| public void testJsPropertyCallSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsProperty"); |
| addSnippetClassDecl( |
| "@JsType public static class Super {", |
| " @JsProperty public int getX() { return 5; }", |
| "}", |
| "@JsType public static class Buggy extends Super {", |
| " public int m() { return getX(); }", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testJsPropertyAccidentalSuperCallSucceeds() |
| throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsProperty"); |
| addSnippetClassDecl( |
| "@JsType public static class Super {", |
| " @JsProperty public int getX() { return 5; }", |
| "}", |
| "@JsType public interface Interface {", |
| " @JsProperty int getX();", |
| "}", |
| |
| "@JsType public static class Buggy extends Super implements Interface {", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testJsPropertyOverrideSucceeds() |
| throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsProperty"); |
| addSnippetClassDecl( |
| "@JsType public static class Super {", |
| " @JsProperty public void setX(int x) { }", |
| " @JsProperty public int getX() { return 5; }", |
| "}", |
| |
| "@JsType public static class Buggy extends Super {", |
| " @JsProperty public void setX(int x) { }", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testMixingJsMethodJsPropertyFails() |
| throws Exception { |
| addSnippetImport("jsinterop.annotations.JsMethod"); |
| addSnippetImport("jsinterop.annotations.JsProperty"); |
| addSnippetClassDecl( |
| "public static class Super {", |
| " @JsMethod public int getY() { return 5; }", |
| " @JsProperty public void setZ(int z) {}", |
| "}", |
| |
| "public static class Buggy extends Super {", |
| " @JsProperty(name = \"getY\") public int getY() { return 6; }", |
| " @JsMethod(name = \"z\") public void setZ(int z) {}", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 10: 'int EntryPoint.Buggy.getY()' and 'int EntryPoint.Super.getY()' cannot " |
| + "both use the same JavaScript name 'getY'.", |
| "Line 11: 'void EntryPoint.Buggy.setZ(int)' and 'void EntryPoint.Super.setZ(int)' cannot " |
| + "both use the same JavaScript name 'z'."); |
| } |
| |
| public void testJsMethodJSNIVarargsWithNoReferenceSucceeds() |
| throws Exception { |
| addSnippetImport("jsinterop.annotations.JsMethod"); |
| addSnippetClassDecl( |
| "public static class Buggy {", |
| " @JsMethod public native void m(int i, int... z) /*-{ return arguments[i]; }-*/;", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testJsMethodJSNIVarargsWithReferenceFails() { |
| addSnippetImport("jsinterop.annotations.JsMethod"); |
| addSnippetClassDecl( |
| "public static class Buggy {", |
| " @JsMethod public native void m(int i, int... z) /*-{ return z[0];}-*/;", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 5: Cannot access vararg parameter 'z' from JSNI in JsMethod " |
| + "'void EntryPoint.Buggy.m(int, int[])'. Use 'arguments' instead."); |
| } |
| |
| public void testMultiplePrivateConstructorsExportSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType", |
| "public static class Buggy {", |
| " private Buggy() {}", |
| " private Buggy(int a) {}", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testMultiplePublicConstructorsAllDelegatesToJsConstructorSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsIgnore"); |
| addSnippetClassDecl( |
| "@JsType", |
| "static class Buggy {", |
| " public Buggy() {}", |
| " @JsIgnore", |
| " public Buggy(int a) {", |
| " this();", |
| " }", |
| "}", |
| "static class SubBuggy extends Buggy {", |
| " public SubBuggy() { this(1);}", |
| " public SubBuggy(int a) { super();}", |
| "}", |
| "@JsType", |
| "static class JsSubBuggy extends Buggy {", |
| " @JsIgnore", |
| " public JsSubBuggy() { this(1);}", |
| " public JsSubBuggy(int a) { super();}", |
| "}", |
| "@JsType (isNative = true)", |
| "static class NativeBuggy {", |
| " public NativeBuggy() {}", |
| " public NativeBuggy(int a) {}", |
| "}", |
| "@JsType (isNative = true)", |
| "static class NativeSubNativeBuggy extends NativeBuggy{", |
| " public NativeSubNativeBuggy() { super(1); }", |
| " public NativeSubNativeBuggy(int a) { super();}", |
| "}", |
| "static class SubNativeBuggy extends NativeBuggy {", |
| " public SubNativeBuggy() { this(1);}", |
| " public SubNativeBuggy(int a) { super();}", |
| "}", |
| "static class SubSubNativeBuggy extends NativeBuggy {", |
| " public SubSubNativeBuggy() { super(1);}", |
| " public SubSubNativeBuggy(int a) { this(); }", |
| "}", |
| "static class SubNativeBuggyImplicitConstructor extends NativeBuggy {", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testMultipleConstructorsNonJsSubtypeRestrictionFails() { |
| addSnippetImport("jsinterop.annotations.JsConstructor"); |
| addSnippetImport("jsinterop.annotations.JsIgnore"); |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType", |
| "static class BuggyJsType {", |
| " public BuggyJsType() {}", |
| " @JsIgnore", |
| " public BuggyJsType(int a) { this(); }", |
| "}", |
| "static class Buggy extends BuggyJsType {", |
| // Error: two non-delegation constructors" |
| " public Buggy() {}", |
| " public Buggy(int a) { super(a); }", |
| "}", |
| "static class SubBuggyJsType extends BuggyJsType {", |
| // Correct: one non-delegating constructor targeting super primary constructor |
| " public SubBuggyJsType() { this(1); }", |
| " public SubBuggyJsType(int a) { super(); }", |
| "}", |
| "static class SubSubBuggyJsType extends SubBuggyJsType {", |
| // Error: non-delegating constructor target the wrong super constructor. |
| " public SubSubBuggyJsType() { this(1);}", |
| " public SubSubBuggyJsType(int a) { super(); }", |
| "}", |
| "static class JsConstructorSubBuggyJsType extends SubBuggyJsType {", |
| // Error: non-delegating constructor target the wrong super constructor. |
| " public JsConstructorSubBuggyJsType() { super(1);}", |
| " @JsConstructor", |
| " public JsConstructorSubBuggyJsType(int a) { super(); }", |
| "}", |
| "static class OtherSubBuggyJsType extends BuggyJsType {", |
| // Error: JsConstructor not delegating to super primary constructor. |
| " public OtherSubBuggyJsType() { super();}", |
| " @JsConstructor", |
| " public OtherSubBuggyJsType(int a) { this(); }", |
| "}", |
| "static class AnotherSubBuggyJsType extends BuggyJsType {", |
| // Error: Multiple JsConstructors in JsConstructor subclass. |
| " @JsConstructor", |
| " public AnotherSubBuggyJsType() { super();}", |
| " @JsConstructor", |
| " public AnotherSubBuggyJsType(int a) { this(); }", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 12: Class 'EntryPoint.Buggy' should have only one constructor delegating to the " |
| + "superclass since it is subclass of a a type with JsConstructor.", |
| "Line 22: Constructor 'EntryPoint.SubSubBuggyJsType.EntryPoint$SubSubBuggyJsType(int)' " |
| + "can only delegate to super constructor " |
| + "'EntryPoint.SubBuggyJsType.EntryPoint$SubBuggyJsType(int)' since it is a subclass " |
| + "of a type with JsConstructor.", |
| "Line 24: Class 'EntryPoint.JsConstructorSubBuggyJsType' should have only one constructor " |
| + "delegating to the superclass since it is subclass of a a type with JsConstructor.", |
| "Line 27: Constructor " |
| + "'EntryPoint.JsConstructorSubBuggyJsType.EntryPoint$JsConstructorSubBuggyJsType(int)'" |
| + " can be a JsConstructor only if all constructors in the class are delegating to it.", |
| "Line 32: Constructor 'EntryPoint.OtherSubBuggyJsType.EntryPoint$OtherSubBuggyJsType(int)' " |
| + "can be a JsConstructor only if all constructors in the class are delegating to it.", |
| "Line 34: More than one JsConstructor exists for 'EntryPoint.AnotherSubBuggyJsType'.", |
| "Line 38: 'EntryPoint.AnotherSubBuggyJsType.EntryPoint$AnotherSubBuggyJsType(int)' cannot " |
| + "be exported because the global name 'test.EntryPoint.AnotherSubBuggyJsType' is " |
| + "already taken by " |
| + "'EntryPoint.AnotherSubBuggyJsType.EntryPoint$AnotherSubBuggyJsType()'."); |
| } |
| |
| public void testMultipleConstructorsNotAllDelegatedToJsConstructorFails() |
| throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType", |
| "public static class Buggy {", |
| " public Buggy() {}", |
| " private Buggy(int a) {", |
| " new Buggy();", |
| " }", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 6: Constructor 'EntryPoint.Buggy.EntryPoint$Buggy()' can be a JsConstructor only if " |
| + "all constructors in the class are delegating to it."); |
| } |
| |
| public void testMultiplePublicConstructorsExportFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType", |
| "public static class Buggy {", |
| " public Buggy() {}", |
| " public Buggy(int a) {", |
| " this();", |
| " }", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 5: More than one JsConstructor exists for 'EntryPoint.Buggy'.", |
| "Line 7: 'EntryPoint.Buggy.EntryPoint$Buggy(int)' cannot be exported because the global " |
| + "name 'test.EntryPoint.Buggy' is already taken by " |
| + "'EntryPoint.Buggy.EntryPoint$Buggy()'."); |
| } |
| |
| public void testNonCollidingAccidentalOverrideSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "public interface Foo {", |
| " void doIt(Object foo);", |
| "}", |
| "public static class ParentParent {", |
| " public void doIt(String x) {}", |
| "}", |
| "@JsType", |
| "public static class Parent extends ParentParent {", |
| " public void doIt(Object x) {}", |
| "}", |
| "public static class Buggy extends Parent implements Foo {}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testJsNameInvalidNamesFails() { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsMethod"); |
| addSnippetImport("jsinterop.annotations.JsPackage"); |
| addSnippetImport("jsinterop.annotations.JsProperty"); |
| addSnippetClassDecl( |
| "@JsType(name = \"a.b.c\") public static class Buggy {", |
| " @JsMethod(name = \"34s\") public void m() {}", |
| " @JsProperty(name = \"s^\") public int m;", |
| " @JsProperty(name = \"\") public int n;", |
| " @JsMethod(namespace = JsPackage.GLOBAL, name = \"a.b\") static void o() {}", |
| " @JsProperty(namespace = JsPackage.GLOBAL, name = \"a.c\") static int q;", |
| "}", |
| "@JsType(namespace=JsPackage.GLOBAL, name = \"a.b.d\") public static class OtherBuggy {", |
| "}" |
| ); |
| |
| assertBuggyFails( |
| "Line 7: 'EntryPoint.Buggy' has invalid name 'a.b.c'.", |
| "Line 8: 'void EntryPoint.Buggy.m()' has invalid name '34s'.", |
| "Line 9: 'int EntryPoint.Buggy.m' has invalid name 's^'.", |
| "Line 10: 'int EntryPoint.Buggy.n' cannot have an empty name.", |
| "Line 11: 'void EntryPoint.Buggy.o()' has invalid name 'a.b'.", |
| "Line 12: 'int EntryPoint.Buggy.q' has invalid name 'a.c'.", |
| "Line 14: 'EntryPoint.OtherBuggy' has invalid name 'a.b.d'."); |
| } |
| |
| public void testJsNameInvalidNamespacesFails() { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsMethod"); |
| addSnippetImport("jsinterop.annotations.JsProperty"); |
| addSnippetClassDecl( |
| "@JsType(namespace = \"a.b.\") public static class Buggy {", |
| " @JsMethod(namespace = \"34s\") public static void m() {}", |
| " @JsProperty(namespace = \"s^\") public static int n;", |
| " @JsMethod(namespace = \"\") public static void o() {}", |
| " @JsProperty(namespace = \"\") public int p;", |
| " @JsMethod(namespace = \"a\") public void q() {}", |
| "}", |
| "@JsType(namespace = \"<window>\") public static class JsTypeOnWindow{", |
| " @JsProperty(namespace = \"<window>\") public static int r;", |
| " @JsMethod(namespace = \"<window>\") public static void s() {}", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 6: 'EntryPoint.Buggy' has invalid namespace 'a.b.'.", |
| "Line 7: 'void EntryPoint.Buggy.m()' has invalid namespace '34s'.", |
| "Line 8: 'int EntryPoint.Buggy.n' has invalid namespace 's^'.", |
| "Line 9: 'void EntryPoint.Buggy.o()' cannot have an empty namespace.", |
| "Line 10: Instance member 'int EntryPoint.Buggy.p' cannot declare a namespace.", |
| "Line 11: Instance member 'void EntryPoint.Buggy.q()' cannot declare a namespace.", |
| "Line 13: '<window>' can only be used as a namespace of native types and members.", |
| "Line 14: '<window>' can only be used as a namespace of native types and members.", |
| "Line 15: '<window>' can only be used as a namespace of native types and members."); |
| } |
| |
| public void testJsNameGlobalNamespacesSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsMethod"); |
| addSnippetImport("jsinterop.annotations.JsProperty"); |
| addSnippetImport("jsinterop.annotations.JsPackage"); |
| addSnippetClassDecl( |
| "@JsType(namespace = JsPackage.GLOBAL) public static class Buggy {", |
| " @JsMethod(namespace = JsPackage.GLOBAL) public static void m() {}", |
| " @JsProperty(namespace = JsPackage.GLOBAL) public static int n;", |
| " @JsMethod(namespace = JsPackage.GLOBAL, name = \"a.b\") public static native void o();", |
| "}", |
| "@JsType(isNative = true, namespace = JsPackage.GLOBAL, name = \"a.c\")", |
| "public static class NativeOnGlobalNamespace {", |
| " @JsMethod(namespace = JsPackage.GLOBAL, name = \"a.d\") static native void o();", |
| " @JsMethod(namespace = JsPackage.GLOBAL, name = \"a.e\") static native void getP();", |
| " @JsProperty(namespace = JsPackage.GLOBAL, name = \"a.f\") public static int n;", |
| "}", |
| "@JsType(isNative = true, namespace = \"<window>\", name = \"a.g\")", |
| "public static class NativeOnWindowNamespace {", |
| " @JsMethod(namespace = \"<window>\", name = \"a.h\") static native void q();", |
| " @JsMethod(namespace = \"<window>\", name = \"a.i\") static native void getR();", |
| " @JsProperty(namespace = \"<window>\", name = \"a.j\") public static int s;", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testSingleJsTypeSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType", |
| "public static class Buggy {", |
| " public static void show1() {}", |
| " public void show2() {}", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testJsFunctionSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsFunction"); |
| addSnippetImport("jsinterop.annotations.JsOverlay"); |
| addSnippetClassDecl( |
| "@JsFunction", |
| "public interface Function {", |
| " int getFoo();", |
| " @JsOverlay", |
| " String s = someString();", |
| " @JsOverlay", |
| " default void m() {}", |
| " @JsOverlay", |
| " static void n() {}", |
| "}", |
| "public static final class Buggy implements Function {", |
| " public int getFoo() { return 0; }", |
| " public final void blah() {}", |
| " public void blat() {}", |
| " private void bleh() {}", |
| " static void blet() {", |
| " new Function() {", |
| " public int getFoo() { return 0; }", |
| " }.getFoo();", |
| " }", |
| " String x = someString();", |
| " static int y;", |
| "}", |
| "public static String someString() { return \"hello\"; }"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testJsFunctionFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsFunction"); |
| addSnippetImport("jsinterop.annotations.JsMethod"); |
| addSnippetImport("jsinterop.annotations.JsOverlay"); |
| addSnippetImport("jsinterop.annotations.JsProperty"); |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsFunction", |
| "public interface Function {", |
| " int getFoo();", |
| "}", |
| "public static final class Buggy implements Function {", |
| " @JsProperty", |
| " public int getFoo() { return 0; }", |
| " @JsMethod", |
| " private void bleh() {}", |
| " @JsProperty", |
| " public int prop = 0;", |
| " public String toString() { return \"\"; }", |
| " public boolean equals(Object o) { return false; }", |
| " public int hashCode() { return 0; }", |
| "}", |
| "@JsFunction", |
| "public interface InvalidFunction {", |
| " @JsProperty", |
| " int getFoo();", |
| " default void m() {}", |
| " int f = 0;", |
| " static void n() {}", |
| "}", |
| "static class NonFinalJsFunction implements Function {", |
| " public int getFoo() { return 0; }", |
| "}", |
| "@JsType", |
| "static final class JsFunctionMarkedAsJsType implements Function {", |
| " public int getFoo() { return 0; }", |
| "}", |
| "@JsFunction", |
| "interface JsFunctionExtendsInterface extends Cloneable {", |
| " void foo();", |
| "}", |
| "interface InterfaceExtendsJsFunction extends Function {}", |
| "static class BaseClass { { if (new Object() instanceof Buggy) {} }}", |
| "static final class JsFunctionExtendingBaseClass extends BaseClass implements Function {", |
| " public int getFoo() { return 0; }", |
| "}", |
| "static final class JsFunctionMultipleInterfaces implements Function, Cloneable {", |
| " public int getFoo() { return 0; }", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 14: JsFunction implementation member 'int EntryPoint.Buggy.getFoo()' " |
| + "cannot be JsMethod nor JsProperty.", |
| "Line 16: JsFunction implementation member 'void EntryPoint.Buggy.bleh()' cannot " |
| + "be JsMethod nor JsProperty.", |
| "Line 18: JsFunction implementation member 'int EntryPoint.Buggy.prop' cannot " |
| + "be JsMethod nor JsProperty.", |
| "Line 19: JsFunction implementation 'EntryPoint.Buggy' cannot implement method " |
| + "'String EntryPoint.Buggy.toString()'.", |
| "Line 20: JsFunction implementation 'EntryPoint.Buggy' cannot implement method " |
| + "'boolean EntryPoint.Buggy.equals(Object)'.", |
| "Line 21: JsFunction implementation 'EntryPoint.Buggy' cannot implement method " |
| + "'int EntryPoint.Buggy.hashCode()'.", |
| "Line 26: JsFunction interface member 'int EntryPoint.InvalidFunction.getFoo()' cannot " |
| + "be JsMethod nor JsProperty.", |
| "Line 27: JsFunction interface 'EntryPoint.InvalidFunction' cannot declare non-JsOverlay " |
| + "member 'void EntryPoint.InvalidFunction.m()'.", |
| "Line 28: JsFunction interface 'EntryPoint.InvalidFunction' cannot declare non-JsOverlay " |
| + "member 'int EntryPoint.InvalidFunction.f'.", |
| "Line 29: JsFunction interface 'EntryPoint.InvalidFunction' cannot declare non-JsOverlay " |
| + "member 'void EntryPoint.InvalidFunction.n()'.", |
| "Line 31: JsFunction implementation 'EntryPoint.NonFinalJsFunction' must be final.", |
| "Line 35: 'EntryPoint.JsFunctionMarkedAsJsType' cannot be both a JsFunction implementation " |
| + "and a JsType at the same time.", |
| "Line 39: JsFunction 'EntryPoint.JsFunctionExtendsInterface' cannot extend other" |
| + " interfaces.", |
| "Line 42: 'EntryPoint.InterfaceExtendsJsFunction' cannot extend " |
| + "JsFunction 'EntryPoint.Function'.", |
| "Line 43: Cannot do instanceof against JsFunction implementation " |
| + "'EntryPoint.Buggy'.", |
| "Line 44: JsFunction implementation 'EntryPoint.JsFunctionExtendingBaseClass' cannot " |
| + "extend a class.", |
| "Line 47: JsFunction implementation 'EntryPoint.JsFunctionMultipleInterfaces' cannot " |
| + "implement more than one interface."); |
| } |
| |
| public void testNativeJsTypeStaticInitializerSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) public static class Buggy {", |
| " static { int x = 1; }", |
| "}", |
| "@JsType(isNative=true) public static class Buggy2 {", |
| " static { Object.class.getName(); }", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testNativeJsTypeInstanceInitializerFails() { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) public static class Buggy {", |
| " { Object.class.getName(); }", |
| "}", |
| "@JsType(isNative=true) public static class Buggy2 {", |
| " { int x = 1; }", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 4: Native JsType 'EntryPoint.Buggy' cannot have initializer.", |
| "Line 7: Native JsType 'EntryPoint.Buggy2' cannot have initializer."); |
| } |
| |
| public void testNativeJsTypeNonEmptyConstructorFails() { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) public static class Buggy {", |
| " public Buggy(int n) {", |
| " n++;", |
| " }", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 5: Native JsType constructor 'EntryPoint.Buggy.EntryPoint$Buggy(int)' cannot have " |
| + "non-empty method body."); |
| } |
| |
| public void testNativeJsTypeImplicitSuperSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) public static class Super {", |
| " public Super() {", |
| " }", |
| "}", |
| "@JsType(isNative=true) public static class Buggy extends Super {", |
| " public Buggy(int n) {", |
| " }", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testNativeJsTypeExplicitSuperSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) public static class Super {", |
| " public Super(int x) {", |
| " }", |
| "}", |
| "@JsType(isNative=true) public static class Buggy extends Super {", |
| " public Buggy(int n) {", |
| " super(n);", |
| " }", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testNativeJsTypeExplicitSuperWithEffectSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) public static class Super {", |
| " public Super(int x) {", |
| " }", |
| "}", |
| "@JsType(isNative=true) public static class Buggy extends Super {", |
| " public Buggy(int n) {", |
| " super(n++);", |
| " }", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testJsTypeInterfaceInInstanceofFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) public interface IBuggy {}", |
| "@JsType public static class Buggy {", |
| " public Buggy() { if (new Object() instanceof IBuggy) {} }", |
| "}"); |
| |
| assertBuggyFails("Line 6: Cannot do instanceof against native JsType interface " |
| + "'EntryPoint.IBuggy'."); |
| } |
| |
| public void testNativeJsTypeEnumFails() { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) public enum Buggy { A, B }"); |
| |
| assertBuggyFails( |
| "Line 4: Enum 'EntryPoint.Buggy' cannot be a native JsType."); |
| } |
| |
| public void testInnerNativeJsTypeFails() { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) public class Buggy { }"); |
| |
| assertBuggyFails( |
| "Line 4: Non static inner class 'EntryPoint.Buggy' cannot be a native JsType."); |
| } |
| |
| public void testInnerJsTypeSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@SuppressWarnings(\"unusable-by-js\") @JsType public class Buggy { }"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testLocalJsTypeFails() { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "public class Buggy { void m() { @JsType class Local {} } }"); |
| |
| assertBuggyFails( |
| "Line 4: Local class 'EntryPoint.Buggy.1Local' cannot be a JsType."); |
| } |
| |
| public void testNativeJsTypeExtendsNativeJsTypeSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) public static class Super {", |
| "}", |
| "@JsType(isNative=true) public static class Buggy extends Super {", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testNativeJsTypeImplementsNativeJsTypeSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) public interface Super {", |
| "}", |
| "@JsType(isNative=true) public static class Buggy implements Super {", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testNativeJsTypeInterfaceImplementsNativeJsTypeSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) public interface Super {", |
| "}", |
| "@JsType(isNative=true) public interface Buggy extends Super {", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testNativeJsTypeExtendsJsTypeFails() { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType public static class Super {", |
| "}", |
| "@JsType(isNative=true) public static class Buggy extends Super {", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 6: Native JsType 'EntryPoint.Buggy' can only extend native JsType classes."); |
| } |
| |
| public void testNativeJsTypeImplementsJsTypeInterfaceFails() { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType public interface Interface {", |
| "}", |
| "@JsType(isNative=true) public static class Buggy implements Interface {", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 6: Native JsType ''EntryPoint.Buggy'' can only implement native JsType interfaces."); |
| } |
| |
| public void testNativeJsTypeInterfaceExtendsJsTypeInterfaceFails() { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType public interface Interface {", |
| "}", |
| "@JsType(isNative=true) public interface Buggy extends Interface {", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 6: Native JsType ''EntryPoint.Buggy'' can only extend native JsType interfaces."); |
| } |
| |
| public void testNativeJsTypeImplementsNonJsTypeFails() { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "public interface Super {", |
| "}", |
| "@JsType(isNative=true) public static class Buggy implements Super {", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 6: Native JsType ''EntryPoint.Buggy'' can only implement native JsType interfaces."); |
| } |
| |
| public void testNativeJsTypeInterfaceExtendsNonJsTypeFails() { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "public interface Super {", |
| "}", |
| "@JsType(isNative=true) public interface Buggy extends Super {", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 6: Native JsType ''EntryPoint.Buggy'' can only extend native JsType interfaces."); |
| } |
| |
| public void testNativeJsTypeInterfaceDefenderMethodsFails() { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsOverlay"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) public interface Interface {", |
| " @JsOverlay default void someOtherMethod(){}", |
| "}", |
| "public static class OtherClass implements Interface {", |
| " public void someOtherMethod() {}", |
| "}", |
| "@JsType(isNative=true) public interface Buggy extends Interface {", |
| " default void someMethod(){}", |
| " void someOtherMethod();", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 9: Method 'void EntryPoint.OtherClass.someOtherMethod()' cannot override a " |
| + "JsOverlay method 'void EntryPoint.Interface.someOtherMethod()'.", |
| "Line 12: Native JsType method 'void EntryPoint.Buggy.someMethod()' should be native " |
| + "or abstract.", |
| "Line 13: Method 'void EntryPoint.Buggy.someOtherMethod()' cannot override a JsOverlay" |
| + " method 'void EntryPoint.Interface.someOtherMethod()'."); |
| } |
| |
| public void testJsOptionalSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsConstructor"); |
| addSnippetImport("jsinterop.annotations.JsMethod"); |
| addSnippetImport("jsinterop.annotations.JsOptional"); |
| addSnippetClassDecl( |
| "public static class Buggy {", |
| " @JsConstructor public Buggy(@JsOptional Object a) {}", |
| " @JsMethod public void fun(int a, Object b, @JsOptional String c) {}", |
| " @JsMethod public void bar(int a, @JsOptional Object b, @JsOptional String c) {}", |
| " @JsMethod public void baz(@JsOptional String a, @JsOptional Object b) {}", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testJsOptionalWithVarargsSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsMethod"); |
| addSnippetImport("jsinterop.annotations.JsOptional"); |
| addSnippetClassDecl( |
| "public class Buggy {", |
| " @JsMethod public void fun(@JsOptional String c, Object... os) {}", |
| " @JsMethod public void bar(int a, @JsOptional Object b, @JsOptional String... c) {}", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testJsOptionalNotJsOptionalOverrideFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsMethod"); |
| addSnippetImport("jsinterop.annotations.JsOptional"); |
| addSnippetClassDecl( |
| "interface Interface {", |
| " @JsMethod void m(@JsOptional Object o);", |
| "}", |
| "public static class Buggy implements Interface {", |
| " @JsMethod public void m(Object o) {}", |
| "}"); |
| |
| assertBuggyFails("Line 9: Method 'void EntryPoint.Buggy.m(Object)' should declare " |
| + "parameter 'o' as JsOptional"); |
| } |
| |
| public void testJsOptionalNotAtEndFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsConstructor"); |
| addSnippetImport("jsinterop.annotations.JsMethod"); |
| addSnippetImport("jsinterop.annotations.JsOptional"); |
| addSnippetClassDecl( |
| "public static class Buggy {", |
| " @JsConstructor public Buggy(@JsOptional String a, Object b, @JsOptional String c) {}", |
| " @JsMethod public void bar(int a, @JsOptional Object b, String c) {}", |
| " @JsMethod public void baz(@JsOptional Object b, String c, Object... os) {}", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 7: JsOptional parameter 'b' in method 'EntryPoint.Buggy.EntryPoint$Buggy(String, " |
| + "Object, String)' cannot precede parameters that are not optional.", |
| "Line 8: JsOptional parameter 'c' in method 'void EntryPoint.Buggy.bar(int, Object," |
| + " String)' cannot precede parameters that are not optional.", |
| "Line 9: JsOptional parameter 'c' in method 'void EntryPoint.Buggy.baz(Object, String, " |
| + "Object[])' cannot precede parameters that are not optional."); |
| } |
| |
| public void testJsOptionalOnPrimitiveTypedParametersFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsMethod"); |
| addSnippetImport("jsinterop.annotations.JsOptional"); |
| addSnippetClassDecl( |
| "public static class Buggy {", |
| " @JsMethod public void m(@JsOptional int i, @JsOptional byte b) {}", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 6: JsOptional parameter 'b' in method 'void EntryPoint.Buggy.m(int, byte)' cannot be " |
| + "of primitive type.", |
| "Line 6: JsOptional parameter 'i' in method 'void EntryPoint.Buggy.m(int, byte)' cannot be " |
| + "of primitive type."); |
| } |
| |
| public void testJsOptionalOnNonJsExposedMethodsFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsProperty"); |
| addSnippetImport("jsinterop.annotations.JsOptional"); |
| addSnippetClassDecl( |
| "public static class Buggy {", |
| " public void fun(int a, @JsOptional Object b, @JsOptional String c) {}", |
| " @JsProperty public void setBar(@JsOptional Object o) {}", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 6: Method 'void EntryPoint.Buggy.fun(int, Object, String)' has JsOptional parameters " |
| + "and is not a JsMethod, a JsConstructor or a JsFunction method.", |
| "Line 7: Method 'void EntryPoint.Buggy.setBar(Object)' has JsOptional parameters and is " |
| + "not a JsMethod, a JsConstructor or a JsFunction method."); |
| } |
| |
| public void testJsOptionalOnJsOverlayMethodsFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsOptional"); |
| addSnippetImport("jsinterop.annotations.JsOverlay"); |
| addSnippetClassDecl( |
| "@JsType(isNative = true) public static class Buggy {", |
| " @JsOverlay public final void fun(@JsOptional Object a) {}", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 7: Method 'void EntryPoint.Buggy.fun(Object)' has JsOptional parameters and " |
| + "is not a JsMethod, a JsConstructor or a JsFunction method."); |
| } |
| |
| public void testJsOverlayOnNativeJsTypeInterfaceSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsOverlay"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) public interface Buggy {", |
| " @JsOverlay Object obj = new Object();", |
| " @JsOverlay default void someOverlayMethod(){}", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testJsOverlayOnNativeJsTypeMemberSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsOverlay"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) public static final class FinalType {", |
| " @JsOverlay public void n() { }", |
| "}", |
| "@JsType(isNative=true) public static class Buggy {", |
| " @JsOverlay public static Object object = new Object();", |
| " @JsOverlay public static void m() { }", |
| " @JsOverlay public static void m(int x) { }", |
| " @JsOverlay private static void m(boolean x) { }", |
| " @JsOverlay private void m(String x) { }", |
| " @JsOverlay public final void n() { }", |
| " @JsOverlay public final void n(int x) { }", |
| " @JsOverlay private final void n(boolean x) { }", |
| " @JsOverlay final void o() { }", |
| " @JsOverlay protected final void p() { }", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testJsOverlayImplementingInterfaceMethodFails() { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsOverlay"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) public interface IBuggy {", |
| " void m();", |
| "}", |
| "@JsType(isNative=true) public static class Buggy implements IBuggy {", |
| " @JsOverlay public void m() { }", |
| "}"); |
| |
| assertBuggyFails("Line 9: JsOverlay method 'void EntryPoint.Buggy.m()' cannot be nor override" |
| + " a JsProperty or a JsMethod."); |
| } |
| |
| public void testJsOverlayOverridingSuperclassMethodFails() { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsOverlay"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) public static class Super {", |
| " public native void m();", |
| "}", |
| "@JsType(isNative=true) public static class Buggy extends Super {", |
| " @JsOverlay public void m() { }", |
| "}"); |
| |
| assertBuggyFails("Line 9: JsOverlay method 'void EntryPoint.Buggy.m()' cannot be nor override" |
| + " a JsProperty or a JsMethod."); |
| } |
| |
| public void testJsOverlayOnNonFinalMethodAndInstanceFieldFails() { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsOverlay"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) public static class Buggy {", |
| " @JsOverlay public final int f2 = 2;", |
| " @JsOverlay public void m() { }", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 5: Native JsType 'EntryPoint.Buggy' cannot have initializer.", |
| "Line 6: JsOverlay field 'int EntryPoint.Buggy.f2' can only be static.", |
| "Line 7: JsOverlay method 'void EntryPoint.Buggy.m()' cannot be non-final nor native."); |
| } |
| |
| public void testJsOverlayWithStaticInitializerSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsOverlay"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) public static class Buggy {", |
| " @JsOverlay public final static Object f1 = new Object();", |
| " @JsOverlay public static int f2 = 2;", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testJsOverlayOnNativeMethodFails() { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsOverlay"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) public static class Buggy {", |
| " @JsOverlay public static final native void m1();", |
| " @JsOverlay public static final native void m2()/*-{}-*/;", |
| " @JsOverlay public final native void m3();", |
| " @JsOverlay public final native void m4()/*-{}-*/;", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 6: JsOverlay method 'void EntryPoint.Buggy.m1()' cannot be non-final nor native.", |
| "Line 7: JSNI method 'void EntryPoint.Buggy.m2()' is not allowed in a native JsType.", |
| "Line 8: JsOverlay method 'void EntryPoint.Buggy.m3()' cannot be non-final nor native.", |
| "Line 9: JSNI method 'void EntryPoint.Buggy.m4()' is not allowed in a native JsType."); |
| } |
| |
| public void testJsOverlayOnJsoMethodSucceeds() throws Exception { |
| addSnippetImport("com.google.gwt.core.client.JavaScriptObject"); |
| addSnippetImport("jsinterop.annotations.JsOverlay"); |
| addSnippetClassDecl( |
| "public static class Buggy extends JavaScriptObject {", |
| " protected Buggy() { }", |
| " @JsOverlay public final void m() { }", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testJsOverlayOnJsMemberFails() { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsOverlay"); |
| addSnippetImport("jsinterop.annotations.JsMethod"); |
| addSnippetImport("jsinterop.annotations.JsProperty"); |
| addSnippetImport("jsinterop.annotations.JsConstructor"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) public static class Buggy {", |
| " @JsOverlay public Buggy() { }", |
| " @JsOverlay @JsConstructor protected Buggy(int i) { }", |
| " @JsMethod @JsOverlay public final void m() { }", |
| " @JsMethod @JsOverlay public static void n() { }", |
| " @JsProperty @JsOverlay public static void setA(String value) { }", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 9: JsOverlay method 'EntryPoint.Buggy.EntryPoint$Buggy()' cannot be a constructor.", |
| "Line 10: JsOverlay method 'EntryPoint.Buggy.EntryPoint$Buggy(int)' cannot be a " |
| + "constructor.", |
| "Line 11: JsOverlay method 'void EntryPoint.Buggy.m()' cannot be nor override" |
| + " a JsProperty or a JsMethod.", |
| "Line 12: JsOverlay method 'void EntryPoint.Buggy.n()' cannot be nor override" |
| + " a JsProperty or a JsMethod.", |
| "Line 13: JsOverlay method 'void EntryPoint.Buggy.setA(String)' cannot be nor override" |
| + " a JsProperty or a JsMethod."); |
| } |
| |
| public void testImplicitJsOverlayOnJsoMethodSucceeds() throws Exception { |
| addSnippetImport("com.google.gwt.core.client.JavaScriptObject"); |
| addSnippetImport("jsinterop.annotations.JsOverlay"); |
| addSnippetClassDecl( |
| "public static class Buggy extends JavaScriptObject {", |
| " protected Buggy() { }", |
| " public final void m() { }", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testJsOverlayOnNonNativeJsTypeFails() { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsOverlay"); |
| addSnippetClassDecl( |
| "@JsType public static class Buggy {", |
| " @JsOverlay public static final int f = 2;", |
| " @JsOverlay public final void m() { };", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 6: JsOverlay 'int EntryPoint.Buggy.f' can only be declared in a native type " |
| + "or a JsFunction interface.", |
| "Line 7: JsOverlay 'void EntryPoint.Buggy.m()' can only be declared in a native type " |
| + "or a JsFunction interface."); |
| } |
| |
| public void testJsTypeExtendsNativeJsTypeSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) public static class Super {", |
| "}", |
| "@JsType public static class Buggy extends Super {", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testJsTypeExtendsNonJsTypeSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "public static class Super {", |
| "}", |
| "@JsType public static class Buggy extends Super {", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testJsTypeImplementsNativeJsTypeInterfaceSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) public interface Interface {", |
| "}", |
| "@JsType public static class Buggy implements Interface {", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testJsTypeImplementsNonJsTypeInterfaceSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "public interface Interface {", |
| "}", |
| "@JsType public static class Buggy implements Interface {", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testJsTypeIntefaceExtendsNativeJsTypeInterfaceSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) public interface Interface {", |
| "}", |
| "@JsType public interface Buggy extends Interface {", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testJsTypeInterfaceExtendsNonJsTypeInterfaceSucceeds() |
| throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "public interface Interface {", |
| "}", |
| "@JsType public interface Buggy extends Interface {", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testNativeJsTypeExtendsNaiveJsTypeSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsMethod"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) static class Super {", |
| " public native int hashCode();", |
| "}", |
| "@JsType(isNative=true) interface HasHashCode {", |
| " int hashCode();", |
| "}", |
| "@JsType(isNative=true) static class Buggy extends Super {", |
| " public native String toString();", |
| " public native boolean equals(Object obj);", |
| "}", |
| "@JsType(isNative=true) static class OtherBuggy implements HasHashCode {", |
| " public native String toString();", |
| " public native boolean equals(Object obj);", |
| " public native int hashCode();", |
| "}" , |
| "@JsType(isNative=true) static class NativeType {}", |
| "interface A { int hashCode(); }", |
| "static class SomeClass extends NativeType implements A {", |
| " public int hashCode() { return 0; }", |
| "}", |
| "@JsType(isNative=true) interface NativeInterface {}", |
| "static class B { @JsMethod(name=\"something\") public int hashCode() { return 0; } }", |
| "static class SomeClass3 extends B implements NativeInterface {}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testNativeJsTypeBadMembersFails() { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsIgnore"); |
| addSnippetImport("jsinterop.annotations.JsMethod"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) interface Interface {", |
| " @JsIgnore public void n();", |
| "}", |
| "@JsType(isNative=true) static class Buggy {", |
| " public static final int s = 42;", |
| " public int f = 42;", |
| " @JsIgnore public Buggy() { }", |
| " @JsIgnore public int x;", |
| " @JsIgnore public native void n();", |
| " public void o() {}", |
| " public native void p() /*-{}-*/;", |
| "}", |
| "@JsType(isNative=true) static class NativeType {}", |
| "interface A { @JsMethod(name=\"something\") int hashCode(); }", |
| "static class SomeClass extends NativeType implements A {", |
| " public int hashCode() { return 0; }", |
| "}", |
| "interface B { int hashCode(); }", |
| "static class SomeClass2 extends NativeType implements B {", |
| "}", |
| "@JsType(isNative=true) static class NativeTypeWithHashCode {", |
| " public native int hashCode();", |
| "}", |
| "static class SomeClass3 extends NativeTypeWithHashCode implements A {}"); |
| |
| assertBuggyFails( |
| "Line 7: Native JsType member 'void EntryPoint.Interface.n()' cannot have @JsIgnore.", |
| "Line 9: Native JsType 'EntryPoint.Buggy' cannot have initializer.", |
| "Line 10: Native JsType field 'int EntryPoint.Buggy.s' cannot have initializer.", |
| "Line 11: Native JsType field 'int EntryPoint.Buggy.f' cannot have initializer.", |
| "Line 12: Native JsType member 'EntryPoint.Buggy.EntryPoint$Buggy()' " |
| + "cannot have @JsIgnore.", |
| "Line 13: Native JsType member 'int EntryPoint.Buggy.x' cannot have @JsIgnore.", |
| "Line 14: Native JsType member 'void EntryPoint.Buggy.n()' cannot have @JsIgnore.", |
| "Line 15: Native JsType method 'void EntryPoint.Buggy.o()' should be native or abstract.", |
| "Line 16: JSNI method 'void EntryPoint.Buggy.p()' is not allowed in a native JsType.", |
| "Line 21: 'int EntryPoint.SomeClass.hashCode()' cannot be assigned a different JavaScript" |
| + " name than the method it overrides.", |
| "Line 24: Native JsType subclass 'EntryPoint.SomeClass2' can not implement interface " |
| + "'EntryPoint.B' that declares method 'hashCode' inherited from java.lang.Object.", |
| "Line 27: 'int EntryPoint.NativeTypeWithHashCode.hashCode()' " |
| + "(exposed by 'EntryPoint.SomeClass3') cannot be assigned a different JavaScript name" |
| + " than the method it overrides."); |
| } |
| |
| public void testSubclassOfNativeJsTypeBadMembersFails() { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsIgnore"); |
| addSnippetImport("jsinterop.annotations.JsMethod"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) static class NativeType {", |
| " @JsMethod(name =\"string\")", |
| " public native String toString();", |
| "}", |
| "static class Buggy extends NativeType {", |
| " public String toString() { return super.toString(); }", |
| " @JsMethod(name = \"blah\")", |
| " public int hashCode() { return super.hashCode(); }", |
| "}", |
| "static class SubBuggy extends Buggy {", |
| " public boolean equals(Object obj) { return super.equals(obj); }", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 8: Method 'String EntryPoint.NativeType.toString()' cannot override a method " |
| + "from 'java.lang.Object' and change its name." , |
| "Line 11: Cannot use super to call 'EntryPoint.NativeType.toString'. 'java.lang.Object' " |
| + "methods in native JsTypes cannot be called using super.", |
| "Line 13: 'int EntryPoint.Buggy.hashCode()' cannot be assigned a different JavaScript " |
| + "name than the method it overrides.", |
| "Line 13: Cannot use super to call 'EntryPoint.NativeType.hashCode'. " |
| + "'java.lang.Object' methods in native JsTypes cannot be called using super.", |
| "Line 16: Cannot use super to call 'EntryPoint.NativeType.equals'. 'java.lang.Object' " |
| + "methods in native JsTypes cannot be called using super." |
| ); |
| } |
| |
| public void testNativeMethodOnJsTypeSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsMethod"); |
| addSnippetClassDecl( |
| "public static class Buggy {", |
| " @JsMethod public native void m();", |
| " @JsMethod public native void n() /*-{}-*/;", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testNativeJsTypeSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsMethod"); |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) abstract static class Buggy {", |
| " public static native void m();", |
| " protected static native void m(Object o);", |
| " private static native void m(String o);", |
| " public Buggy() { }", |
| " protected Buggy(Object o) { }", |
| " private Buggy(String o) { }", |
| " public native void n();", |
| " protected native void n(Object o);", |
| " private native void n(String o);", |
| " public abstract void o();", |
| " protected abstract void o(Object o);", |
| " abstract void o(String o);", |
| "}", |
| "@JsType(isNative=true) abstract static class NativeClass {", |
| " public native String toString();", |
| " public abstract int hashCode();", |
| "}", |
| "static class NativeSubclass extends NativeClass {", |
| " public String toString() { return null; }", |
| " @JsMethod", |
| " public boolean equals(Object obj) { return false; }", |
| " public int hashCode() { return 0; }", |
| "}", |
| "static class SubNativeSubclass extends NativeSubclass {", |
| " public boolean equals(Object obj) { return super.equals(obj); }", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testNativeJsTypeFieldsSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) static class Buggy {", |
| " public static int f1;", |
| " protected static int f2;", |
| " private static int f3;", |
| " public int f4;", |
| " protected int f5;", |
| " private int f6;", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testNativeJsTypeDefaultConstructorSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) static class Buggy {", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testNonJsTypeExtendingNativeJsTypeWithInstanceMethodSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@SuppressWarnings(\"unusable-by-js\")", |
| "@JsType(isNative=true) public static class Super {", |
| " public native void m(Object o);", |
| " public native void m(Object[] o);", |
| "}", |
| "@JsType public static class Buggy extends Super {", |
| " public void n(Object o) { }", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testNonJsTypeExtendingNativeJsTypeWithInstanceMethodOverloadsFails() { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) public static class Super {", |
| " public native void m(Object o);", |
| " public native void m(int o);", |
| "}", |
| "public static class Buggy extends Super {", |
| " public void m(Object o) { }", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 9: 'void EntryPoint.Buggy.m(Object)' and 'void EntryPoint.Super.m(int)' " |
| + "cannot both use the same JavaScript name 'm'."); |
| } |
| |
| public void testNonJsTypeWithNativeStaticMethodOverloadsSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsMethod"); |
| addSnippetClassDecl( |
| "public static class Buggy {", |
| " @JsMethod public static native void m(Object o);", |
| " @JsMethod public static native void m(int o);", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testNonJsTypeWithNativeInstanceMethodOverloadsFails() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsMethod"); |
| addSnippetClassDecl( |
| "public static class Buggy {", |
| " @JsMethod public native void m(Object o);", |
| " @JsMethod public void m(int o) { }", |
| "}"); |
| |
| assertBuggyFails( |
| "Line 6: 'void EntryPoint.Buggy.m(int)' and 'void EntryPoint.Buggy.m(Object)' " |
| + "cannot both use the same JavaScript name 'm'."); |
| } |
| |
| public void testNonJsTypeExtendsJsTypeSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType public static class Super {", |
| "}", |
| "public static class Buggy extends Super {", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testNonJsTypeImplementsJsTypeInterfaceSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType public interface Interface {", |
| "}", |
| "public static class Buggy implements Interface {", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testNonJsTypeInterfaceExtendsJsTypeInterfaceSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType public interface Interface {", |
| "}", |
| "public interface Buggy extends Interface {", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testNonJsTypeExtendsNativeJsTypeSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) public static class Super {", |
| " public native void m();", |
| "}", |
| "public static class Buggy extends Super {", |
| " public void m() { }", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testNonJsTypeImplementsNativeJsTypeInterfaceSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) public interface Interface {", |
| "}", |
| "public static class Buggy implements Interface {", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testNonJsTypeInterfaceExtendsNativeJsTypeInterfaceSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType(isNative=true) public interface Interface {", |
| "}", |
| "public interface Buggy extends Interface {", |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testUnusableByJsSuppressionSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl("public static class A {}"); |
| addSnippetClassDecl( |
| "@JsType @SuppressWarnings(\"unusable-by-js\")", // SuppressWarnings on the class. |
| "public static class B {", |
| " public A field;", |
| " public A t0(A a, A b) { return null; }", |
| "}"); |
| addSnippetClassDecl( |
| "@JsType", |
| "public static class Buggy {", |
| " @SuppressWarnings(\"unusable-by-js\") public A field;", // add SuppressWarnings to field. |
| " @SuppressWarnings({\"unusable-by-js\", \"unused\"})", // test multiple warnings. |
| " public A t0(A a, A b) { return null; }", // add SuppressWarnings to the method. |
| " public void t1(", |
| " @SuppressWarnings(\"unusable-by-js\")A a,", |
| " @SuppressWarnings(\"unusable-by-js\")A b", |
| " ) {}", // add SuppressWarnings to parameters. |
| "}"); |
| |
| assertBuggySucceeds(); |
| } |
| |
| public void testUsableByJsTypesSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsFunction"); |
| addSnippetImport("com.google.gwt.core.client.JavaScriptObject"); |
| addSnippetClassDecl( |
| "@JsType public static class A {}", |
| "@JsType public static interface I {}", |
| "@JsFunction public static interface FI {void foo();}", |
| "public static class C extends JavaScriptObject {protected C(){}}", |
| "@JsType public static class Buggy {", |
| " public void f1(boolean a, int b, double c) {}", // primitive types work fine. |
| " public void f2(Boolean a, Double b, String c) {}", // unboxed types work fine. |
| " public void f3(A a) {}", // JsType works fine. |
| " public void f4(I a) {}", // JsType interface works fine. |
| " public void f5(FI a) {}", // JsFunction works fine. |
| " public void f6(C a) {}", // JavaScriptObject works fine. |
| " public void f7(Object a) {}", // Java Object works fine. |
| " public void f8(boolean[] a) {}", // array of primitive types work fine. |
| " public void f9(Boolean[] a, Double[] b, String[] c) {}", // array of unboxed types. |
| " public void f10(A[] a) {}", // array of JsType works fine. |
| " public void f11(FI[] a) {}", // array of JsFunction works fine. |
| " public void f12(C[][] a) {}", // array of JavaScriptObject works fine. |
| " public void f13(Object[] a) {}", // Object[] works fine. |
| " public void f14(Object[][] a) {}", // Object[][] works fine. |
| "}"); |
| assertBuggySucceeds(); |
| } |
| |
| public void testUnusableByJsNotExportedMembersSucceeds() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "public static class A {}", |
| "@JsType public static class Buggy {", |
| " private A field;", // private field. |
| " private A f1(A a) { return null; }", // private method. |
| "}"); |
| assertBuggySucceeds(); |
| } |
| |
| public void testUnusuableByJsWarns() throws Exception { |
| addSnippetImport("jsinterop.annotations.JsFunction"); |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetImport("jsinterop.annotations.JsMethod"); |
| addSnippetImport("jsinterop.annotations.JsProperty"); |
| addSnippetClassDecl( |
| "public static class A {}", |
| "@JsType public static interface I {}", |
| "public static class B implements I {}", |
| "public static class C {", // non-jstype class with JsMethod |
| " @JsMethod", |
| " public static void fc1(A a) {}", // JsMethod |
| "}", |
| "public static class D {", // non-jstype class with JsProperty |
| " @JsProperty", |
| " public static A a;", // JsProperty |
| "}", |
| "@JsFunction public static interface FI { void f(A a); }", // JsFunction method is checked. |
| "@JsType public static class Buggy {", |
| " public A f;", // exported field |
| " public A f1(A a) { return null; }", // regular class fails. |
| " public A[] f2(A[] a) { return null; }", // array of regular class fails. |
| " public long f3(long a) { return 1l; }", // long fails. |
| // non-JsType class that implements a JsType interface fails. |
| " public B f4(B a) { return null; }", |
| "}"); |
| |
| assertBuggySucceeds( |
| "Line 12: [unusable-by-js] Type of parameter 'a' in " |
| + "'void EntryPoint.C.fc1(EntryPoint.A)' is not usable by but exposed to JavaScript.", |
| "Line 16: [unusable-by-js] Type of 'EntryPoint.A EntryPoint.D.a' is not usable by but " |
| + "exposed to JavaScript.", |
| "Line 18: [unusable-by-js] Type of parameter 'a' in " |
| + "'void EntryPoint.FI.f(EntryPoint.A)' is not usable by but exposed to JavaScript.", |
| "Line 20: [unusable-by-js] Type of 'EntryPoint.A EntryPoint.Buggy.f' is not usable by but " |
| + "exposed to JavaScript.", |
| "Line 21: [unusable-by-js] Return type of 'EntryPoint.A EntryPoint.Buggy.f1(EntryPoint.A)' " |
| + "is not usable by but exposed to JavaScript.", |
| "Line 21: [unusable-by-js] Type of parameter 'a' in " |
| + "'EntryPoint.A EntryPoint.Buggy.f1(EntryPoint.A)' is not usable by but " |
| + "exposed to JavaScript.", |
| "Line 22: [unusable-by-js] Return type of " |
| + "'EntryPoint.A[] EntryPoint.Buggy.f2(EntryPoint.A[])' is not usable by but " |
| + "exposed to JavaScript.", |
| "Line 22: [unusable-by-js] Type of parameter 'a' in " |
| + "'EntryPoint.A[] EntryPoint.Buggy.f2(EntryPoint.A[])' is not usable by but " |
| + "exposed to JavaScript.", |
| "Line 23: [unusable-by-js] Return type of 'long EntryPoint.Buggy.f3(long)' is not " |
| + "usable by but exposed to JavaScript.", |
| "Line 23: [unusable-by-js] Type of parameter 'a' in " |
| + "'long EntryPoint.Buggy.f3(long)' is not usable by but exposed to JavaScript.", |
| "Line 24: [unusable-by-js] Return type of 'EntryPoint.B EntryPoint.Buggy.f4(EntryPoint.B)' " |
| + "is not usable by but exposed to JavaScript.", |
| "Line 24: [unusable-by-js] Type of parameter 'a' in " |
| + "'EntryPoint.B EntryPoint.Buggy.f4(EntryPoint.B)' is not usable by but " |
| + "exposed to JavaScript.", |
| "Suppress \"[unusable-by-js]\" warnings by adding a `@SuppressWarnings(\"unusable-by-js\")`" |
| + " annotation to the corresponding member."); |
| } |
| |
| public void testUnusableByJsAccidentalOverrideSuppressionWarns() |
| throws Exception { |
| addSnippetImport("jsinterop.annotations.JsType"); |
| addSnippetClassDecl( |
| "@JsType", |
| "public static interface Foo {", |
| " @SuppressWarnings(\"unusable-by-js\") ", |
| " void doIt(Class foo);", |
| "}", |
| "public static class Parent {", |
| " public void doIt(Class x) {}", |
| "}", |
| "public static class Buggy extends Parent implements Foo {}"); |
| |
| assertBuggySucceeds( |
| "Line 10: [unusable-by-js] Type of parameter 'x' in " |
| + "'void EntryPoint.Parent.doIt(Class)' (exposed by 'EntryPoint.Buggy') is not usable " |
| + "by but exposed to JavaScript.", |
| "Suppress \"[unusable-by-js]\" warnings by adding a `@SuppressWarnings(\"unusable-by-js\")`" |
| + " annotation to the corresponding member."); |
| } |
| |
| private static final MockJavaResource jsFunctionInterface = new MockJavaResource( |
| "test.MyJsFunctionInterface") { |
| @Override |
| public CharSequence getContent() { |
| StringBuilder code = new StringBuilder(); |
| code.append("package test;\n"); |
| code.append("import jsinterop.annotations.JsFunction;\n"); |
| code.append("@JsFunction public interface MyJsFunctionInterface {\n"); |
| code.append("int foo(int x);\n"); |
| code.append("}\n"); |
| return code; |
| } |
| }; |
| |
| public final void assertBuggySucceeds(String... expectedWarnings) |
| throws Exception { |
| Result result = assertCompileSucceeds("Buggy buggy = null;", expectedWarnings); |
| assertNotNull(result.findClass("test.EntryPoint$Buggy")); |
| } |
| |
| public final void assertBuggyFails(String... expectedErrors) { |
| assertTrue(expectedErrors.length > 0); |
| assertCompileFails("Buggy buggy = null;", expectedErrors); |
| } |
| |
| @Override |
| protected boolean doOptimizeMethod(TreeLogger logger, JProgram program, JMethod method) { |
| try { |
| JsInteropRestrictionChecker.exec(logger, program, new MinimalRebuildCache()); |
| } catch (Exception e) { |
| throw new RuntimeException(e); |
| } |
| return false; |
| } |
| } |