| /* |
| * Copyright 2008 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.javac; |
| |
| /** |
| * Test access to longs from JSNI. |
| */ |
| public class JsniCheckerTest extends CheckerTestCase { |
| |
| /** |
| * JSNI references to anonymous inner classes is deprecated. |
| */ |
| public void testAnoymousJsniRef() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" static void main() {\n"); |
| code.append(" new Object() {\n"); |
| code.append(" int foo = 3;\n"); |
| code.append(" };\n"); |
| code.append(" }\n"); |
| code.append(" native void jsniMeth(Object o) /*-{\n"); |
| code.append(" o.@Buggy$1::foo;\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| |
| shouldGenerateError(code, 8, "Referencing class 'Buggy$1': " |
| + "JSNI references to anonymous classes are illegal"); |
| } |
| |
| /** |
| * JSNI references to anonymous inner classes is deprecated. |
| */ |
| public void testAnoymousJsniRefNested() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" static void main() {\n"); |
| code.append(" new Object() {\n"); |
| code.append(" class A {\n"); |
| code.append(" int foo = 3;\n"); |
| code.append(" };\n"); |
| code.append(" };\n"); |
| code.append(" }\n"); |
| code.append(" native void jsniMeth(Object o) /*-{\n"); |
| code.append(" o.@Buggy$1.A::foo;\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| |
| shouldGenerateError(code, 10, "Referencing class 'Buggy$1.A': " |
| + "JSNI references to anonymous classes are illegal"); |
| } |
| |
| public void testArrayBadMember() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" native void jsniMethod() /*-{\n"); |
| code.append(" @Buggy[][]::blah;\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateError( |
| code, |
| 3, |
| "Referencing member 'Buggy[][].blah': 'class' is the only legal reference for array types"); |
| } |
| |
| public void testArrayClass() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" native void jsniMethod() /*-{\n"); |
| code.append(" @Buggy[][]::class;\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateNoWarning(code); |
| } |
| |
| public void testClass() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" native void jsniMethod() /*-{\n"); |
| code.append(" @Buggy::class;\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateNoWarning(code); |
| } |
| |
| public void testClassAssignment() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" native void jsniMethod() /*-{\n"); |
| code.append(" @Buggy::class = null;\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateError(code, 3, |
| "Illegal assignment to class literal 'Buggy.class'"); |
| } |
| |
| public void testCyclicReferences() { |
| { |
| StringBuffer buggy = new StringBuffer(); |
| buggy.append("class Buggy {\n"); |
| buggy.append(" static int anint = 3;\n"); |
| buggy.append(" native void jsniMeth() /*-{\n"); |
| buggy.append(" $wnd.alert(@Extra::along);\n"); |
| buggy.append(" }-*/;\n"); |
| buggy.append("}\n"); |
| |
| StringBuffer extra = new StringBuffer(); |
| extra.append("class Extra {\n"); |
| extra.append(" static long along = 3;\n"); |
| extra.append(" native void jsniMeth() /*-{\n"); |
| extra.append(" $wnd.alert(@Buggy::anint);\n"); |
| extra.append(" }-*/;\n"); |
| extra.append("}\n"); |
| |
| shouldGenerateError(buggy, extra, 4, "Referencing field 'Extra.along': " |
| + "type 'long' is not safe to access in JSNI code"); |
| } |
| |
| { |
| StringBuffer buggy = new StringBuffer(); |
| buggy.append("class Buggy {\n"); |
| buggy.append(" Extra anExtra = new Extra();\n"); |
| buggy.append(" static int anint = 3;\n"); |
| buggy.append(" native void jsniMeth() /*-{\n"); |
| buggy.append(" $wnd.alert(@Extra::along);\n"); |
| buggy.append(" }-*/;\n"); |
| buggy.append("}\n"); |
| |
| StringBuffer extra = new StringBuffer(); |
| extra.append("class Extra {\n"); |
| extra.append(" Buggy mattress = new Buggy();\n"); |
| extra.append(" static long along = 3;\n"); |
| extra.append(" native void jsniMeth() /*-{\n"); |
| extra.append(" $wnd.alert(@Buggy::anint);\n"); |
| extra.append(" }-*/;\n"); |
| extra.append("}\n"); |
| |
| shouldGenerateError(buggy, extra, 5, "Referencing field 'Extra.along': " |
| + "type 'long' is not safe to access in JSNI code"); |
| } |
| } |
| |
| public void testDeprecationField() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" @Deprecated static int bar;\n"); |
| code.append(" native void jsniMethod() /*-{\n"); |
| code.append(" @Buggy::bar;\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| |
| shouldGenerateWarning(code, 4, "Referencing deprecated field 'Buggy.bar'"); |
| } |
| |
| public void testDeprecationMethod() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" @Deprecated static void foo(){}\n"); |
| code.append(" native void jsniMethod() /*-{\n"); |
| code.append(" @Buggy::foo();\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| |
| shouldGenerateWarning(code, 4, "Referencing deprecated method 'Buggy.foo'"); |
| } |
| |
| public void testDeprecationSuppression() { |
| StringBuffer code = new StringBuffer(); |
| code.append("@Deprecated class D {\n"); |
| code.append(" static int bar;\n"); |
| code.append("}\n"); |
| code.append("class Buggy {\n"); |
| code.append(" @Deprecated static void foo(){}\n"); |
| code.append(" @Deprecated static int bar;\n"); |
| code.append(" @SuppressWarnings(\"deprecation\")\n"); |
| code.append(" native void jsniMethod1() /*-{\n"); |
| code.append(" @Buggy::foo();\n"); |
| code.append(" @Buggy::bar;\n"); |
| code.append(" @D::bar;\n"); |
| code.append(" }-*/;\n"); |
| code.append(" @SuppressWarnings({\"deprecation\", \"other\"})\n"); |
| code.append(" native void jsniMethod2() /*-{\n"); |
| code.append(" @Buggy::foo();\n"); |
| code.append(" @Buggy::bar;\n"); |
| code.append(" @D::bar;\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateNoWarning(code); |
| |
| // Check inherited suppress warnings. |
| code = new StringBuffer(); |
| code.append("@Deprecated class D {\n"); |
| code.append(" static int bar;\n"); |
| code.append("}\n"); |
| code.append("@SuppressWarnings(\"deprecation\")\n"); |
| code.append("class Buggy {\n"); |
| code.append(" @Deprecated static void foo(){}\n"); |
| code.append(" @Deprecated static int bar;\n"); |
| code.append(" native void jsniMethod1() /*-{\n"); |
| code.append(" @Buggy::foo();\n"); |
| code.append(" @Buggy::bar;\n"); |
| code.append(" @D::bar;\n"); |
| code.append(" }-*/;\n"); |
| code.append(" native void jsniMethod2() /*-{\n"); |
| code.append(" @Buggy::foo();\n"); |
| code.append(" @Buggy::bar;\n"); |
| code.append(" @D::bar;\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateNoWarning(code); |
| } |
| |
| public void testDeprecationType() { |
| StringBuffer code = new StringBuffer(); |
| code.append("@Deprecated class D {\n"); |
| code.append(" static int bar;\n"); |
| code.append("}\n"); |
| code.append("class Buggy {\n"); |
| code.append(" native void jsniMethod() /*-{\n"); |
| code.append(" @D::bar;\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| |
| shouldGenerateWarning(code, 6, "Referencing deprecated class 'D'"); |
| } |
| |
| public void testField() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" int foo = 3;\n"); |
| code.append(" native void jsniMethod() /*-{\n"); |
| code.append(" this.@Buggy::foo;\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateNoWarning(code); |
| } |
| |
| public void testFieldAccess() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append("volatile long x = -1;\n"); |
| code.append("native void jsniMeth() /*-{\n"); |
| code.append(" $wnd.alert(\"x is: \"+this.@Buggy::x); }-*/;\n"); |
| code.append("}\n"); |
| |
| shouldGenerateError(code, 4, |
| "Referencing field 'Buggy.x': type 'long' is not safe to access in JSNI code"); |
| } |
| |
| public void testFieldAssignment() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" int foo = 3;\n"); |
| code.append(" native void jsniMethod() /*-{\n"); |
| code.append(" this.@Buggy::foo = 4;\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateNoWarning(code); |
| } |
| |
| public void testFieldAssignmentStatic() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" static int foo = 3;\n"); |
| code.append(" native void jsniMethod() /*-{\n"); |
| code.append(" @Buggy::foo = 4;\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateNoWarning(code); |
| } |
| |
| public void testFieldConstant() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" static final int foo = 3;\n"); |
| code.append(" native void jsniMethod() /*-{\n"); |
| code.append(" @Buggy::foo;\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateNoWarning(code); |
| } |
| |
| public void testFieldConstantAssignment() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" static final int foo = 3;\n"); |
| code.append(" native void jsniMethod() /*-{\n"); |
| code.append(" @Buggy::foo = 4;\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateError(code, 4, |
| "Illegal assignment to compile-time constant 'Buggy.foo'"); |
| |
| code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" static final String foo = \"asdf\";\n"); |
| code.append(" native void jsniMethod() /*-{\n"); |
| code.append(" @Buggy::foo = null;\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateError(code, 4, |
| "Illegal assignment to compile-time constant 'Buggy.foo'"); |
| |
| // Not a compile-time constant. |
| code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" static final Object foo = new Object();\n"); |
| code.append(" native void jsniMethod() /*-{\n"); |
| code.append(" @Buggy::foo = null;\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateNoWarning(code); |
| } |
| |
| public void testJsoStaticMethod() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" native void jsniMeth(Object o) /*-{\n"); |
| code.append(" @com.google.gwt.core.client.JavaScriptObject::createObject()();\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateNoWarning(code); |
| } |
| |
| public void testJsoInstanceMethod() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" native void jsniMeth(Object o) /*-{\n"); |
| code.append(" new Object().@com.google.gwt.core.client.JavaScriptObject::toString()();\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateError( |
| code, |
| 3, |
| "Referencing method 'com.google.gwt.core.client.JavaScriptObject.toString()': references to instance methods in overlay types are illegal"); |
| } |
| |
| public void testJsoInterfaceMethod() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" interface IFoo {\n"); |
| code.append(" void foo();\n"); |
| code.append(" }\n"); |
| code.append(" static final class Foo extends com.google.gwt.core.client.JavaScriptObject implements IFoo{\n"); |
| code.append(" protected Foo() { };\n"); |
| code.append(" public void foo() { };\n"); |
| code.append(" }\n"); |
| code.append(" native void jsniMeth(Object o) /*-{\n"); |
| code.append(" new Object().@Buggy.IFoo::foo()();\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateError( |
| code, |
| 10, |
| "Referencing interface method 'Buggy.IFoo.foo()': implemented by 'Buggy$Foo'; references to instance methods in overlay types are illegal; use a stronger type or a Java trampoline method"); |
| } |
| |
| public void testJsoSubclassInstanceMethod() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" static final class Foo extends com.google.gwt.core.client.JavaScriptObject {\n"); |
| code.append(" protected Foo() { };\n"); |
| code.append(" void foo() { };\n"); |
| code.append(" }\n"); |
| code.append(" native void jsniMeth(Object o) /*-{\n"); |
| code.append(" new Object().@Buggy.Foo::foo()();\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateError( |
| code, |
| 7, |
| "Referencing method 'Buggy.Foo.foo()': references to instance methods in overlay types are illegal"); |
| } |
| |
| public void testJsoSubclassStaticMethod() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" static final class Foo extends com.google.gwt.core.client.JavaScriptObject {\n"); |
| code.append(" protected Foo() { };\n"); |
| code.append(" static void foo() { };\n"); |
| code.append(" }\n"); |
| code.append(" native void jsniMeth(Object o) /*-{\n"); |
| code.append(" @Buggy.Foo::foo()();\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateNoWarning(code); |
| } |
| |
| public void testFieldStatic() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" static int foo = 3;\n"); |
| code.append(" native void jsniMethod() /*-{\n"); |
| code.append(" @Buggy::foo;\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateNoWarning(code); |
| } |
| |
| public void testFieldStaticQualified() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" static int foo = 3;\n"); |
| code.append(" native void jsniMethod() /*-{\n"); |
| code.append(" this.@Buggy::foo;\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateError(code, 4, |
| "Unnecessary qualifier on static field 'Buggy.foo'"); |
| } |
| |
| public void testFieldUnqualified() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" int foo = 3;\n"); |
| code.append(" native void jsniMethod() /*-{\n"); |
| code.append(" @Buggy::foo;\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateError(code, 4, |
| "Missing qualifier on instance field 'Buggy.foo'"); |
| } |
| |
| public void testInnerClass() { |
| StringBuffer code = new StringBuffer(); |
| code.append("public class Buggy {\n"); |
| code.append(" static class Inner {\n"); |
| code.append(" static long x = 3;\n"); |
| code.append(" }\n"); |
| code.append(" native void jsniMeth() /*-{\n"); |
| code.append(" $wnd.alert(@Buggy.Inner::x);\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| |
| shouldGenerateError(code, 6, "Referencing field 'Buggy.Inner.x': " |
| + "type 'long' is not safe to access in JSNI code"); |
| } |
| |
| public void testInnerClassDollar() { |
| StringBuffer code = new StringBuffer(); |
| code.append("public class Buggy {\n"); |
| code.append(" static class Inner {\n"); |
| code.append(" static long x = 3;\n"); |
| code.append(" }\n"); |
| code.append(" native void jsniMeth() /*-{\n"); |
| code.append(" $wnd.alert(@Buggy$Inner::x);\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| |
| shouldGenerateError(code, 6, "Referencing field 'Buggy$Inner.x': " |
| + "type 'long' is not safe to access in JSNI code"); |
| } |
| |
| public void testInnerNew() { |
| StringBuffer code = new StringBuffer(); |
| code.append("public class Buggy {\n"); |
| code.append(" class Inner {\n"); |
| code.append(" long x = 3;\n"); |
| code.append(" Inner(boolean b) { };\n"); |
| code.append(" }\n"); |
| code.append(" native void jsniMeth() /*-{\n"); |
| code.append(" $wnd.alert(@Buggy.Inner::new(Z)(true).toString());\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| |
| // Cannot resolve, missing synthetic enclosing instance. |
| shouldGenerateError(code, 7, "Referencing method 'Buggy.Inner.new(Z)': " |
| + "unable to resolve method"); |
| |
| code = new StringBuffer(); |
| code.append("public class Buggy {\n"); |
| code.append(" static class Inner {\n"); |
| code.append(" long x = 3;\n"); |
| code.append(" Inner(boolean b) { };\n"); |
| code.append(" }\n"); |
| code.append(" native void jsniMeth() /*-{\n"); |
| code.append(" $wnd.alert(@Buggy.Inner::new(Z)(this, true).toString());\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateNoWarning(code); |
| |
| code = new StringBuffer(); |
| code.append("public class Buggy {\n"); |
| code.append(" class Inner {\n"); |
| code.append(" long x = 3;\n"); |
| code.append(" Inner(boolean b) { };\n"); |
| code.append(" }\n"); |
| code.append(" native void jsniMeth() /*-{\n"); |
| code.append(" $wnd.alert(@Buggy.Inner::new(LBuggy;Z)(this, true).toString());\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateNoWarning(code); |
| } |
| |
| /** |
| * The proper behavior here is a close call. In Development Mode, Java arrays |
| * are completely unusable in JavaScript, so the current reasoning is to allow |
| * them. |
| */ |
| public void testLongArray() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" long[] m() { return new long[] { -1 }; }\n"); |
| code.append(" native void jsniMeth() /*-{\n"); |
| code.append(" $wnd.alert(this.@Buggy::m()()); }-*/;\n"); |
| code.append("}\n"); |
| |
| shouldGenerateNoError(code); |
| } |
| |
| public void testLongParameter() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" native void jsniMeth(long x) /*-{ return; }-*/;\n"); |
| code.append("}\n"); |
| |
| shouldGenerateError(code, 2, |
| "Parameter 'x': type 'long' is not safe to access in JSNI code"); |
| } |
| |
| public void testLongReturn() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" native long jsniMeth() /*-{ return 0; }-*/;\n"); |
| code.append("}\n"); |
| |
| shouldGenerateError(code, 2, |
| "Type 'long' may not be returned from a JSNI method"); |
| } |
| |
| public void testMalformedJsniRef() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" native void jsniMethod() /*-{\n"); |
| code.append(" @Buggy;\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateError(code, 3, |
| "Expected \":\" in JSNI reference\n> @Buggy;\n" + "> ----------^"); |
| } |
| |
| public void testMethod() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" void foo() { }\n"); |
| code.append(" native void jsniMethod() /*-{\n"); |
| code.append(" this.@Buggy::foo()();\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateNoWarning(code); |
| } |
| |
| public void testMethodArgument() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" void print(long x) { }\n"); |
| code.append(" native void jsniMeth() /*-{ this.@Buggy::print(J)(0); }-*/;\n"); |
| code.append("}\n"); |
| |
| shouldGenerateError( |
| code, |
| 3, |
| "Parameter 1 of method 'Buggy.print': type 'long' may not be passed out of JSNI code"); |
| } |
| |
| public void testMethodAssignment() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" void foo() { }\n"); |
| code.append(" native void jsniMethod() /*-{\n"); |
| code.append(" this.@Buggy::foo() = null;\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateError(code, 4, "Illegal assignment to method 'Buggy.foo'"); |
| } |
| |
| /** |
| * Test JSNI references to methods defined in superclass/superinterfaces. |
| */ |
| public void testMethodInheritance() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" interface A1 { void a1(); }\n"); |
| code.append(" interface A2 extends A1 { void a2(); }\n"); |
| code.append(" static abstract class C1 implements A2 { public abstract void c1(); }\n"); |
| code.append(" native void jsniMeth(Object o) /*-{\n"); |
| code.append(" o.@Buggy.A1::a1()();\n"); |
| code.append(" o.@Buggy.A2::a1()();\n"); |
| code.append(" o.@Buggy.A2::a2()();\n"); |
| code.append(" o.@Buggy.C1::a1()();\n"); |
| code.append(" o.@Buggy.C1::a2()();\n"); |
| code.append(" o.@Buggy.C1::c1()();\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| |
| shouldGenerateNoWarning(code); |
| } |
| |
| public void testMethodReturn() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" long m() { return -1; }\n"); |
| code.append(" native void jsniMeth() /*-{\n"); |
| code.append(" $wnd.alert(this.@Buggy::m()()); }-*/;\n"); |
| code.append("}\n"); |
| |
| shouldGenerateError( |
| code, |
| 4, |
| "Referencing method 'Buggy.m': return type 'long' is not safe to access in JSNI code"); |
| } |
| |
| public void testMethodStatic() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" static void foo() { }\n"); |
| code.append(" native void jsniMethod() /*-{\n"); |
| code.append(" @Buggy::foo()();\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateNoWarning(code); |
| } |
| |
| public void testMethodStaticQualified() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" static void foo() { }\n"); |
| code.append(" native void jsniMethod() /*-{\n"); |
| code.append(" this.@Buggy::foo()();\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateError(code, 4, |
| "Unnecessary qualifier on static method 'Buggy.foo'"); |
| } |
| |
| public void testMethodUnqualified() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" void foo() { }\n"); |
| code.append(" native void jsniMethod() /*-{\n"); |
| code.append(" @Buggy::foo()();\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateError(code, 4, |
| "Missing qualifier on instance method 'Buggy.foo'"); |
| } |
| |
| public void testNew() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" static native Object main() /*-{\n"); |
| code.append(" return @Buggy::new()();\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateNoWarning(code); |
| |
| code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" Buggy(boolean b) { }\n"); |
| code.append(" static native Object main() /*-{\n"); |
| code.append(" return @Buggy::new(Z)(true);\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateNoWarning(code); |
| } |
| |
| public void testNullField() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" static native Object main() /*-{\n"); |
| code.append(" return @null::nullField;\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateNoWarning(code); |
| |
| code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" static native Object main() /*-{\n"); |
| code.append(" return @null::foo;\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateError( |
| code, |
| 3, |
| "Referencing field 'null.foo': 'nullField' is the only legal field reference for 'null'"); |
| } |
| |
| public void testNullMethod() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" static native Object main() /*-{\n"); |
| code.append(" return @null::nullMethod()();\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateNoWarning(code); |
| |
| code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" static native Object main() /*-{\n"); |
| code.append(" return @null::foo()();\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateError( |
| code, |
| 3, |
| "Referencing method 'null.foo()': 'nullMethod()' is the only legal method for 'null'"); |
| } |
| |
| public void testOverloadedMethodWithNoWarning() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" long m(int x) { return -1; }\n"); |
| code.append(" int m(String x) { return -1; }\n"); |
| code.append(" native void jsniMeth() /*-{\n"); |
| code.append(" $wnd.alert(this.@Buggy::m(Ljava/lang/String;)(\"hello\")); }-*/;\n"); |
| code.append("}\n"); |
| |
| shouldGenerateNoError(code); |
| } |
| |
| public void testOverloadedMethodWithWarning() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" long m(int x) { return -1; }\n"); |
| code.append(" int m(String x) { return -1; }\n"); |
| code.append(" native void jsniMeth() /*-{\n"); |
| code.append(" $wnd.alert(this.@Buggy::m(I)(10)); }-*/;\n"); |
| code.append("}\n"); |
| |
| shouldGenerateError( |
| code, |
| 5, |
| "Referencing method 'Buggy.m': return type 'long' is not safe to access in JSNI code"); |
| } |
| |
| public void testPrimitiveBadMember() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" native void jsniMethod() /*-{\n"); |
| code.append(" @boolean::blah;\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateError( |
| code, |
| 3, |
| "Referencing member 'boolean.blah': 'class' is the only legal reference for primitive types"); |
| } |
| |
| public void testPrimitiveClass() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" native void jsniMethod() /*-{\n"); |
| code.append(" @boolean::class;\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateNoWarning(code); |
| } |
| |
| public void testPrimitiveClassDeprecated() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" native void jsniMethod() /*-{\n"); |
| code.append(" @Z::class;\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateWarning(code, 3, |
| "Referencing primitive type 'Z': this is deprecated, use 'boolean' instead"); |
| } |
| |
| public void testRefInString() { |
| { |
| StringBuffer code = new StringBuffer(); |
| code.append("import com.google.gwt.core.client.UnsafeNativeLong;"); |
| code.append("class Buggy {\n"); |
| code.append(" void print(long x) { }\n"); |
| code.append(" native void jsniMeth() /*-{ 'this.@Buggy::print(J)(0)'; }-*/;\n"); |
| code.append("}\n"); |
| |
| shouldGenerateNoError(code); |
| } |
| } |
| |
| public void testUnresolvedClass() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" native void jsniMethod() /*-{\n"); |
| code.append(" @Foo::x;\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateError(code, 3, |
| "Referencing class 'Foo': unable to resolve class"); |
| } |
| |
| public void testUnresolvedField() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" native void jsniMethod() /*-{\n"); |
| code.append(" @Buggy::x;\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateError(code, 3, |
| "Referencing field 'Buggy.x': unable to resolve field"); |
| } |
| |
| public void testUnresolvedMethod() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" native void jsniMethod() /*-{\n"); |
| code.append(" @Buggy::x(Ljava/lang/String);\n"); |
| code.append(" }-*/;\n"); |
| code.append("}\n"); |
| shouldGenerateError(code, 3, |
| "Referencing method 'Buggy.x(Ljava/lang/String)': unable to resolve method"); |
| } |
| |
| public void testUnsafeAnnotation() { |
| { |
| StringBuffer code = new StringBuffer(); |
| code.append("import com.google.gwt.core.client.UnsafeNativeLong;"); |
| code.append("class Buggy {\n"); |
| code.append(" void print(long x) { }\n"); |
| code.append(" @UnsafeNativeLong\n"); |
| code.append(" native void jsniMeth() /*-{ this.@Buggy::print(J)(0); }-*/;\n"); |
| code.append("}\n"); |
| |
| shouldGenerateNoError(code); |
| } |
| } |
| |
| public void testViolator() { |
| { |
| StringBuffer okay = new StringBuffer(); |
| okay.append("class Buggy {\n"); |
| okay.append(" native void jsniMeth() /*-{\n"); |
| okay.append(" $wnd.alert(@Extra.Inner::x);\n"); |
| okay.append(" }-*/;\n"); |
| okay.append("}\n"); |
| |
| StringBuffer extra = new StringBuffer(); |
| extra.append("class Extra {\n"); |
| extra.append(" private static class Inner { \n"); |
| extra.append(" private static int x = 3;\n"); |
| extra.append(" }\n"); |
| extra.append("}\n"); |
| |
| shouldGenerateNoError(okay, extra); |
| } |
| |
| { |
| StringBuffer buggy = new StringBuffer(); |
| buggy.append("class Buggy {\n"); |
| buggy.append(" native void jsniMeth() /*-{\n"); |
| buggy.append(" $wnd.alert(@Extra.Inner::x);\n"); |
| buggy.append(" }-*/;\n"); |
| buggy.append("}\n"); |
| |
| StringBuffer extra = new StringBuffer(); |
| extra.append("class Extra {\n"); |
| extra.append(" private static class Inner { \n"); |
| extra.append(" private static long x = 3;\n"); |
| extra.append(" }\n"); |
| extra.append("}\n"); |
| |
| shouldGenerateError( |
| buggy, |
| extra, |
| 3, |
| "Referencing field 'Extra.Inner.x': type 'long' is not safe to access in JSNI code"); |
| } |
| } |
| |
| public void testWildcardRef() { |
| StringBuffer code = new StringBuffer(); |
| code.append("class Buggy {\n"); |
| code.append(" int m(String x) { return -1; }\n"); |
| code.append(" native void jsniMeth() /*-{\n"); |
| code.append(" $wnd.alert(this.@Buggy::m(*)(\"hello\")); }-*/;\n"); |
| code.append("}\n"); |
| |
| shouldGenerateNoError(code); |
| } |
| } |