Adds three new test cases to LongFromJSNITest, one of which
initially failed. To addresses the failing test,
LongFromJSNIChecker.findClass() has extra code for
looking up classes that cannot be accessed due to Java rules.
Suggested by: mmendez
Review by: scottb
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2599 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jdt/LongFromJSNIChecker.java b/dev/core/src/com/google/gwt/dev/jdt/LongFromJSNIChecker.java
index d9a424a..0ad43b3 100644
--- a/dev/core/src/com/google/gwt/dev/jdt/LongFromJSNIChecker.java
+++ b/dev/core/src/com/google/gwt/dev/jdt/LongFromJSNIChecker.java
@@ -32,6 +32,8 @@
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
+import org.eclipse.jdt.internal.compiler.lookup.ProblemReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
@@ -159,6 +161,26 @@
char[][] compoundName = CharOperation.splitOn('.',
className.toCharArray());
TypeBinding binding = cud.scope.getType(compoundName, compoundName.length);
+
+ if (binding instanceof ProblemReferenceBinding) {
+ ProblemReferenceBinding prb = (ProblemReferenceBinding) binding;
+ if (prb.problemId() == ProblemReasons.NotVisible) {
+ // It's just a visibility problem, so try drilling
+ // down manually
+ ReferenceBinding drilling = prb.closestMatch();
+ for (int i = prb.compoundName.length; i < compoundName.length; i++) {
+ drilling = drilling.getMemberType(compoundName[i]);
+ if (binding == null) {
+ return null;
+ }
+ }
+ binding = drilling;
+ }
+ }
+
+ if (binding instanceof ProblemReferenceBinding) {
+ return null;
+ }
if (binding instanceof ReferenceBinding) {
return (ReferenceBinding) binding;
}
diff --git a/dev/core/test/com/google/gwt/dev/jdt/LongFromJSNITest.java b/dev/core/test/com/google/gwt/dev/jdt/LongFromJSNITest.java
index d100f34..d11efd4 100644
--- a/dev/core/test/com/google/gwt/dev/jdt/LongFromJSNITest.java
+++ b/dev/core/test/com/google/gwt/dev/jdt/LongFromJSNITest.java
@@ -26,6 +26,52 @@
* Test access to longs from JSNI.
*/
public class LongFromJSNITest extends TestCase {
+ public void testCyclicReferences() throws UnableToCompleteException {
+ {
+ 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, 3, "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, 4, "Referencing field 'Extra.along': "
+ + "type 'long' is not safe to access in JSNI code");
+ }
+ }
+
public void testFieldAccess() throws UnableToCompleteException {
StringBuffer code = new StringBuffer();
code.append("class Buggy {\n");
@@ -38,6 +84,21 @@
"Referencing field 'Buggy.x': type 'long' is not safe to access in JSNI code");
}
+ public void testInnerClass() throws UnableToCompleteException {
+ StringBuffer code = new StringBuffer();
+ code.append("public class Buggy {\n");
+ code.append(" static class Inner {\n");
+ code.append(" 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, 5, "Referencing field 'Buggy.Inner.x': "
+ + "type 'long' is not safe to access in JSNI code");
+ }
+
/**
* The proper behavior here is a close call. In hosted mode, Java arrays are
* completely unusable in JavaScript, so the current reasoning is to allow
@@ -144,6 +205,48 @@
}
}
+ public void testViolator() throws UnableToCompleteException {
+ {
+ 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,
+ 2,
+ "Referencing field 'Extra.Inner.x': type 'long' is not safe to access in JSNI code");
+ }
+ }
+
private void addLongCheckingCups(TypeOracleBuilder builder)
throws UnableToCompleteException {
{
@@ -157,16 +260,21 @@
}
}
- private TypeOracle buildOracleWithCode(CharSequence code,
- UnitTestTreeLogger logger) throws UnableToCompleteException {
+ private TypeOracle buildOracle(CharSequence buggyCode,
+ CharSequence extraCode, UnitTestTreeLogger logger)
+ throws UnableToCompleteException {
TypeOracleBuilder builder = new TypeOracleBuilder();
TypeOracleTestingUtils.addStandardCups(builder);
addLongCheckingCups(builder);
- TypeOracleTestingUtils.addCup(builder, "Buggy", code);
+ TypeOracleTestingUtils.addCup(builder, "Buggy", buggyCode);
+ if (extraCode != null) {
+ TypeOracleTestingUtils.addCup(builder, "Extra", extraCode);
+ }
return builder.build(logger);
}
- private void shouldGenerateError(CharSequence code, int line, String message)
+ private void shouldGenerateError(CharSequence buggyCode,
+ CharSequence extraCode, int line, String message)
throws UnableToCompleteException {
UnitTestTreeLogger.Builder b = new UnitTestTreeLogger.Builder();
b.setLowestLogLevel(TreeLogger.ERROR);
@@ -178,7 +286,7 @@
"Compilation problem due to 'transient source for Buggy'", null);
}
UnitTestTreeLogger logger = b.createLogger();
- TypeOracle oracle = buildOracleWithCode(code, logger);
+ TypeOracle oracle = buildOracle(buggyCode, extraCode, logger);
logger.assertCorrectLogEntries();
if (message != null) {
assertEquals("Buggy compilation unit not removed from type oracle", null,
@@ -186,8 +294,18 @@
}
}
- private void shouldGenerateNoError(StringBuffer code)
+ private void shouldGenerateError(CharSequence buggyCode, int line,
+ String message) throws UnableToCompleteException {
+ shouldGenerateError(buggyCode, null, line, message);
+ }
+
+ private void shouldGenerateNoError(CharSequence code)
throws UnableToCompleteException {
- shouldGenerateError(code, -1, null);
+ shouldGenerateNoError(code, null);
+ }
+
+ private void shouldGenerateNoError(CharSequence code,
+ CharSequence extraCode) throws UnableToCompleteException {
+ shouldGenerateError(code, extraCode, -1, null);
}
}