Adds a warning to JsniChecker to detect references to anonymous classes.
Review by: spoon (TBR)
git-svn-id: https://google-web-toolkit.googlecode.com/svn/releases/1.6@4723 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationUnitInvalidator.java b/dev/core/src/com/google/gwt/dev/javac/CompilationUnitInvalidator.java
index b70d348..0992cea 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompilationUnitInvalidator.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompilationUnitInvalidator.java
@@ -156,7 +156,7 @@
if (unit.getState() == State.COMPILED) {
CompilationUnitDeclaration jdtCud = unit.getJdtCud();
JSORestrictionsChecker.check(jdtCud);
- LongFromJSNIChecker.check(jdtCud);
+ JsniChecker.check(jdtCud);
BinaryTypeReferenceRestrictionsChecker.check(jdtCud,
validBinaryTypeNames);
}
diff --git a/dev/core/src/com/google/gwt/dev/javac/LongFromJSNIChecker.java b/dev/core/src/com/google/gwt/dev/javac/JsniChecker.java
similarity index 77%
rename from dev/core/src/com/google/gwt/dev/javac/LongFromJSNIChecker.java
rename to dev/core/src/com/google/gwt/dev/javac/JsniChecker.java
index 7e699ab..393bedd 100644
--- a/dev/core/src/com/google/gwt/dev/javac/LongFromJSNIChecker.java
+++ b/dev/core/src/com/google/gwt/dev/javac/JsniChecker.java
@@ -36,8 +36,10 @@
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.TagBits;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
+import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
@@ -45,15 +47,16 @@
import java.util.Set;
/**
- * Tests for access to Java longs from JSNI. Issues a warning for:
+ * Tests for access to Java from JSNI. Issues a warning for:
* <ul>
- * <li> JSNI methods with a parameter or return type of long.
- * <li> Access from JSNI to a field whose type is long.
- * <li> Access from JSNI to a method with a parameter or return type of long.
+ * <li>JSNI methods with a parameter or return type of long.</li>
+ * <li>Access from JSNI to a field whose type is long.</li>
+ * <li>Access from JSNI to a method with a parameter or return type of long.</li>
+ * <li>JSNI references to anonymous classes.</li>
* </ul>
* All tests also apply for arrays of longs, arrays of arrays of longs, etc.
*/
-public class LongFromJSNIChecker {
+public class JsniChecker {
private class CheckingVisitor extends ASTVisitor implements
ClassFileConstants {
@Override
@@ -82,9 +85,10 @@
}
}
- private void checkFieldRef(JsniRef jsniRef, Set<String> errors) {
+ private void checkFieldRef(ReferenceBinding clazz, JsniRef jsniRef,
+ Set<String> errors) {
assert jsniRef.isField();
- FieldBinding target = getField(jsniRef);
+ FieldBinding target = getField(clazz, jsniRef);
if (target == null) {
return;
}
@@ -95,9 +99,10 @@
}
}
- private void checkMethodRef(JsniRef jsniRef, Set<String> errors) {
+ private void checkMethodRef(ReferenceBinding clazz, JsniRef jsniRef,
+ Set<String> errors) {
assert jsniRef.isMethod();
- MethodBinding target = getMethod(jsniRef);
+ MethodBinding target = getMethod(clazz, jsniRef);
if (target == null) {
return;
}
@@ -136,13 +141,26 @@
JsniRef jsniRef = JsniRef.parse(jsniRefString);
if (jsniRef != null) {
Set<String> refErrors = new LinkedHashSet<String>();
- if (jsniRef.isMethod()) {
- checkMethodRef(jsniRef, refErrors);
- } else {
- checkFieldRef(jsniRef, refErrors);
- }
- if (!refErrors.isEmpty()) {
- errors.put(jsniRefString, refErrors);
+ ReferenceBinding clazz = findClass(jsniRef);
+ if (clazz != null) {
+ if (clazz.isAnonymousType()) {
+ GWTProblem.recordInCud(
+ ProblemSeverities.Warning,
+ meth,
+ cud,
+ "Referencing class '" + jsniRef.className()
+ + ": JSNI references to anonymous classes are deprecated",
+ null);
+ } else {
+ if (jsniRef.isMethod()) {
+ checkMethodRef(clazz, jsniRef, refErrors);
+ } else {
+ checkFieldRef(clazz, jsniRef, refErrors);
+ }
+ if (!refErrors.isEmpty()) {
+ errors.put(jsniRefString, refErrors);
+ }
+ }
}
}
}
@@ -189,11 +207,9 @@
if (binding instanceof ProblemReferenceBinding) {
ProblemReferenceBinding prb = (ProblemReferenceBinding) binding;
- if (prb.problemId() == ProblemReasons.NotVisible
- && prb.closestMatch() instanceof ReferenceBinding) {
- // It's just a visibility problem, so try drilling
- // down manually
- ReferenceBinding drilling = (ReferenceBinding) prb.closestMatch();
+ 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]);
}
@@ -201,31 +217,31 @@
}
}
- if (binding instanceof ProblemReferenceBinding) {
- return null;
- }
- if (binding instanceof ReferenceBinding) {
+ if (binding instanceof ReferenceBinding
+ && !(binding instanceof ProblemReferenceBinding)) {
return (ReferenceBinding) binding;
}
+ // See if it looks like an anonymous inner class, we can't look those up.
+ for (char[] part : compoundName) {
+ if (Character.isDigit(part[0])) {
+ return new ReferenceBinding() {
+ {
+ tagBits = TagBits.IsAnonymousType;
+ }
+ };
+ }
+ }
return null;
}
- private FieldBinding getField(JsniRef jsniRef) {
+ private FieldBinding getField(ReferenceBinding clazz, JsniRef jsniRef) {
assert jsniRef.isField();
- ReferenceBinding type = findClass(jsniRef);
- if (type == null) {
- return null;
- }
- return type.getField(jsniRef.memberName().toCharArray(), false);
+ return clazz.getField(jsniRef.memberName().toCharArray(), false);
}
- private MethodBinding getMethod(JsniRef jsniRef) {
+ private MethodBinding getMethod(ReferenceBinding clazz, JsniRef jsniRef) {
assert jsniRef.isMethod();
- ReferenceBinding type = findClass(jsniRef);
- if (type == null) {
- return null;
- }
- for (MethodBinding method : type.getMethods(jsniRef.memberName().toCharArray())) {
+ for (MethodBinding method : clazz.getMethods(jsniRef.memberName().toCharArray())) {
if (paramTypesMatch(method, jsniRef)) {
return method;
}
@@ -294,13 +310,13 @@
*
*/
public static void check(CompilationUnitDeclaration cud) {
- LongFromJSNIChecker checker = new LongFromJSNIChecker(cud);
+ JsniChecker checker = new JsniChecker(cud);
checker.check();
}
private final CompilationUnitDeclaration cud;
- private LongFromJSNIChecker(CompilationUnitDeclaration cud) {
+ private JsniChecker(CompilationUnitDeclaration cud) {
this.cud = cud;
}
diff --git a/dev/core/test/com/google/gwt/dev/javac/JavaCompilationSuite.java b/dev/core/test/com/google/gwt/dev/javac/JavaCompilationSuite.java
index 1505a36..eb14456 100644
--- a/dev/core/test/com/google/gwt/dev/javac/JavaCompilationSuite.java
+++ b/dev/core/test/com/google/gwt/dev/javac/JavaCompilationSuite.java
@@ -35,7 +35,7 @@
suite.addTestSuite(JdtCompilerTest.class);
suite.addTestSuite(JSORestrictionsTest.class);
suite.addTestSuite(JavaSourceOracleImplTest.class);
- suite.addTestSuite(LongFromJSNITest.class);
+ suite.addTestSuite(JsniCheckerTest.class);
suite.addTestSuite(TypeOracleMediatorTest.class);
return suite;
diff --git a/dev/core/test/com/google/gwt/dev/javac/LongFromJSNITest.java b/dev/core/test/com/google/gwt/dev/javac/JsniCheckerTest.java
similarity index 85%
rename from dev/core/test/com/google/gwt/dev/javac/LongFromJSNITest.java
rename to dev/core/test/com/google/gwt/dev/javac/JsniCheckerTest.java
index 7528d2a..5ebe56e 100644
--- a/dev/core/test/com/google/gwt/dev/javac/LongFromJSNITest.java
+++ b/dev/core/test/com/google/gwt/dev/javac/JsniCheckerTest.java
@@ -16,6 +16,7 @@
package com.google.gwt.dev.javac;
import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.dev.util.UnitTestTreeLogger;
@@ -27,7 +28,28 @@
/**
* Test access to longs from JSNI.
*/
-public class LongFromJSNITest extends TestCase {
+public class JsniCheckerTest extends TestCase {
+
+ /**
+ * 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");
+
+ shouldGenerateWarning(code, 7, "Referencing class \'Buggy$1: "
+ + "JSNI references to anonymous classes are deprecated");
+ }
+
public void testCyclicReferences() {
{
StringBuffer buggy = new StringBuffer();
@@ -281,24 +303,33 @@
units.toArray(new CompilationUnit[units.size()]));
}
- private void shouldGenerateError(CharSequence buggyCode,
- CharSequence extraCode, int line, String message) {
+ private void shouldGenerate(CharSequence buggyCode, CharSequence extraCode,
+ int line, Type logLevel, String logHeader, String message) {
UnitTestTreeLogger.Builder b = new UnitTestTreeLogger.Builder();
- b.setLowestLogLevel(TreeLogger.ERROR);
+ b.setLowestLogLevel(logLevel);
if (message != null) {
- b.expect(TreeLogger.ERROR, "Errors in '/mock/Buggy'", null);
+ b.expect(logLevel, logHeader + " in '/mock/Buggy'", null);
final String fullMessage = "Line " + line + ": " + message;
- b.expect(TreeLogger.ERROR, fullMessage, null);
+ b.expect(logLevel, fullMessage, null);
}
UnitTestTreeLogger logger = b.createLogger();
TypeOracle oracle = buildOracle(buggyCode, extraCode, logger);
logger.assertCorrectLogEntries();
- if (message != null) {
- assertEquals("Buggy compilation unit not removed from type oracle", null,
+ if (message != null && logLevel == TreeLogger.ERROR) {
+ assertNull("Buggy compilation unit not removed from type oracle",
+ oracle.findType("Buggy"));
+ } else {
+ assertNotNull("Buggy compilation unit removed with only a warning",
oracle.findType("Buggy"));
}
}
+ private void shouldGenerateError(CharSequence buggyCode,
+ CharSequence extraCode, int line, String message) {
+ shouldGenerate(buggyCode, extraCode, line, TreeLogger.ERROR, "Errors",
+ message);
+ }
+
private void shouldGenerateError(CharSequence buggyCode, int line,
String message) {
shouldGenerateError(buggyCode, null, line, message);
@@ -311,4 +342,15 @@
private void shouldGenerateNoError(CharSequence code, CharSequence extraCode) {
shouldGenerateError(code, extraCode, -1, null);
}
+
+ private void shouldGenerateWarning(CharSequence buggyCode,
+ CharSequence extraCode, int line, String message) {
+ shouldGenerate(buggyCode, extraCode, line, TreeLogger.WARN, "Warnings",
+ message);
+ }
+
+ private void shouldGenerateWarning(CharSequence buggyCode, int line,
+ String message) {
+ shouldGenerateWarning(buggyCode, null, line, message);
+ }
}