blob: 004a6674677db14e83a1cee47f3a848a833a85db [file] [log] [blame]
/*
* 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);
}
}