Revision: 9653
Author: [email protected]
Date: Fri Jan 28 10:48:44 2011
Log: Improvements to JsniChecker.
- JsniChecker now implements ALL of the JSNI checks that were being done in
GenerateJavaAST.
- JsniCheckerTest tests them all.
- JsniChecker reports the resolved refs it collected.
- Tighter type on jsni method map.
http://gwt-code-reviews.appspot.com/1333801/show
Review by: [email protected]
http://code.google.com/p/google-web-toolkit/source/detail?r=9653
Modified:
/trunk/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java
/trunk/dev/core/src/com/google/gwt/dev/javac/JSORestrictionsChecker.java
/trunk/dev/core/src/com/google/gwt/dev/javac/JsniChecker.java
/trunk/dev/core/src/com/google/gwt/dev/javac/JsniCollector.java
/trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
/trunk/dev/core/test/com/google/gwt/dev/javac/JsniCheckerTest.java
/trunk/dev/core/test/com/google/gwt/dev/javac/JsniCollectorTest.java
/trunk/dev/core/test/com/google/gwt/dev/javac/impl/JavaResourceBase.java
=======================================
---
/trunk/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java
Thu Jan 27 12:46:16 2011
+++
/trunk/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java
Fri Jan 28 10:48:44 2011
@@ -31,8 +31,9 @@
import org.apache.commons.collections.map.ReferenceIdentityMap;
import org.apache.commons.collections.map.ReferenceMap;
import org.eclipse.jdt.core.compiler.CharOperation;
-import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
+import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import java.util.ArrayList;
@@ -44,8 +45,8 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.Map.Entry;
+import java.util.Set;
/**
* Manages a centralized cache for compiled units.
@@ -61,22 +62,25 @@
public void process(CompilationUnitBuilder builder,
CompilationUnitDeclaration cud, List<CompiledClass>
compiledClasses) {
- Map<AbstractMethodDeclaration, JsniMethod> jsniMethods =
JsniCollector.collectJsniMethods(
+ Map<MethodDeclaration, JsniMethod> jsniMethods =
JsniCollector.collectJsniMethods(
cud, builder.getSource(), jsProgram);
+ JSORestrictionsChecker.check(jsoState, cud);
+
// JSNI check + collect dependencies.
final Set<String> jsniDeps = new HashSet<String>();
- JsniChecker.check(cud, jsniMethods, new JsniChecker.TypeResolver()
{
- public ReferenceBinding resolveType(String typeName) {
- ReferenceBinding resolveType = compiler.resolveType(typeName);
- if (resolveType != null) {
-
jsniDeps.add(String.valueOf(resolveType.qualifiedSourceName()));
- }
- return resolveType;
- }
- });
-
- JSORestrictionsChecker.check(jsoState, cud);
+ Map<String, Binding> jsniRefs = new HashMap<String, Binding>();
+ JsniChecker.check(cud, jsoState, jsniMethods, jsniRefs,
+ new JsniChecker.TypeResolver() {
+ public ReferenceBinding resolveType(String typeName) {
+ ReferenceBinding resolveType =
compiler.resolveType(typeName);
+ if (resolveType != null) {
+
jsniDeps.add(String.valueOf(resolveType.qualifiedSourceName()));
+ }
+ return resolveType;
+ }
+ });
+
ArtificialRescueChecker.check(cud, builder.isGenerated());
BinaryTypeReferenceRestrictionsChecker.check(cud);
=======================================
---
/trunk/dev/core/src/com/google/gwt/dev/javac/JSORestrictionsChecker.java
Fri Jul 9 10:56:34 2010
+++
/trunk/dev/core/src/com/google/gwt/dev/javac/JSORestrictionsChecker.java
Fri Jan 28 10:48:44 2011
@@ -32,6 +32,7 @@
import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import java.util.HashMap;
@@ -65,17 +66,27 @@
private final Map<String, String> interfacesToJsoImpls = new
HashMap<String, String>();
public void addJsoInterface(TypeDeclaration jsoType,
- CompilationUnitDeclaration cud, String interfaceName) {
- String alreadyImplementor = interfacesToJsoImpls.get(interfaceName);
+ CompilationUnitDeclaration cud, ReferenceBinding interf) {
+ String intfName = CharOperation.toString(interf.compoundName);
+ String alreadyImplementor = interfacesToJsoImpls.get(intfName);
String myName = CharOperation.toString(jsoType.binding.compoundName);
if (alreadyImplementor == null) {
- interfacesToJsoImpls.put(interfaceName, myName);
+ interfacesToJsoImpls.put(intfName, myName);
} else {
- String msg = errAlreadyImplemented(interfaceName,
alreadyImplementor,
- myName);
+ String msg = errAlreadyImplemented(intfName, alreadyImplementor,
myName);
errorOn(jsoType, cud, msg);
}
}
+
+ public String getJsoImplementor(ReferenceBinding binding) {
+ String name = CharOperation.toString(binding.compoundName);
+ return interfacesToJsoImpls.get(name);
+ }
+
+ public boolean isJsoInterface(ReferenceBinding binding) {
+ String name = CharOperation.toString(binding.compoundName);
+ return interfacesToJsoImpls.containsKey(name);
+ }
}
private class JSORestrictionsVisitor extends SafeASTVisitor implements
@@ -183,15 +194,16 @@
}
private boolean checkType(TypeDeclaration type) {
- if (!isJsoSubclass(type.binding)) {
+ SourceTypeBinding binding = type.binding;
+ if (!isJsoSubclass(binding)) {
return false;
}
- if (type.enclosingType != null && !type.binding.isStatic()) {
+ if (type.enclosingType != null && !binding.isStatic()) {
errorOn(type, ERR_IS_NONSTATIC_NESTED);
}
- ReferenceBinding[] interfaces = type.binding.superInterfaces();
+ ReferenceBinding[] interfaces = binding.superInterfaces();
if (interfaces != null) {
for (ReferenceBinding interf : interfaces) {
if (interf.methods() == null) {
@@ -200,11 +212,10 @@
if (interf.methods().length > 0) {
// See if any of my superTypes implement it.
- ReferenceBinding superclass = type.binding.superclass();
+ ReferenceBinding superclass = binding.superclass();
if (superclass == null
|| !superclass.implementsInterface(interf, true)) {
- String intfName =
CharOperation.toString(interf.compoundName);
- state.addJsoInterface(type, cud, intfName);
+ state.addJsoInterface(type, cud, interf);
}
}
}
@@ -252,6 +263,36 @@
+ "interface that declared methods. The interface (" + intfName
+ ") is implemented by both (" + impl1 + ") and (" + impl2 + ")";
}
+
+ /**
+ * Returns {@code true} if {@code typeBinding} is {@code
JavaScriptObject} or
+ * any subtype.
+ */
+ static boolean isJso(TypeBinding typeBinding) {
+ if (!(typeBinding instanceof ReferenceBinding)) {
+ return false;
+ }
+ ReferenceBinding binding = (ReferenceBinding) typeBinding;
+ while (binding != null) {
+ if (JSO_CLASS.equals(String.valueOf(binding.constantPoolName()))) {
+ return true;
+ }
+ binding = binding.superclass();
+ }
+ return false;
+ }
+
+ /**
+ * Returns {@code true} if {@code typeBinding} is a subtype of
+ * {@code JavaScriptObject}, but not {@code JavaScriptObject} itself.
+ */
+ static boolean isJsoSubclass(TypeBinding typeBinding) {
+ if (!(typeBinding instanceof ReferenceBinding)) {
+ return false;
+ }
+ ReferenceBinding binding = (ReferenceBinding) typeBinding;
+ return isJso(binding.superclass());
+ }
private static void errorOn(ASTNode node, CompilationUnitDeclaration cud,
String error) {
@@ -276,18 +317,4 @@
private void errorOn(ASTNode node, String error) {
errorOn(node, cud, error);
}
-
- private boolean isJsoSubclass(TypeBinding typeBinding) {
- if (!(typeBinding instanceof ReferenceBinding)) {
- return false;
- }
- ReferenceBinding binding = (ReferenceBinding) typeBinding;
- while (binding.superclass() != null) {
- if
(JSO_CLASS.equals(String.valueOf(binding.superclass().constantPoolName())))
{
- return true;
- }
- binding = binding.superclass();
- }
- return false;
- }
-}
+}
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/javac/JsniChecker.java Fri Jan
28 06:04:41 2011
+++ /trunk/dev/core/src/com/google/gwt/dev/javac/JsniChecker.java Fri Jan
28 10:48:44 2011
@@ -16,6 +16,7 @@
package com.google.gwt.dev.javac;
import com.google.gwt.core.client.UnsafeNativeLong;
+import com.google.gwt.dev.javac.JSORestrictionsChecker.CheckerState;
import com.google.gwt.dev.jdt.SafeASTVisitor;
import com.google.gwt.dev.jjs.InternalCompilerException;
import com.google.gwt.dev.jjs.SourceInfo;
@@ -30,7 +31,6 @@
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
-import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
@@ -42,7 +42,9 @@
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
@@ -194,57 +196,94 @@
if (jsniRef == null) {
emitError("Malformed JSNI identifier '" + ident + "'");
} else {
- checkRef(jsniRef);
+ Binding binding = checkRef(jsniRef, x.getQualifier() != null,
+ ctx.isLvalue());
+ if (binding != null) {
+ jsniRefs.put(ident, binding);
+ }
}
}
this.errorInfo = null;
}
- private void checkFieldRef(ReferenceBinding clazz, JsniRef jsniRef) {
+ private FieldBinding checkFieldRef(ReferenceBinding clazz, JsniRef
jsniRef,
+ boolean hasQualifier, boolean isLvalue) {
assert jsniRef.isField();
- if ("class".equals(jsniRef.memberName())) {
- return;
- }
FieldBinding target = getField(clazz, jsniRef);
if (target == null) {
- emitError("JSNI Referencing field '" + jsniRef.className() + "."
- + jsniRef.memberName()
- + "': unable to resolve field, expect subsequent failures");
- return;
+ emitError("Referencing field '" + jsniRef.className() + "."
+ + jsniRef.memberName() + "': unable to resolve field");
+ return null;
}
if (target.isDeprecated()) {
emitWarning("deprecation",
"Referencing deprecated field '" + jsniRef.className() + "."
+ jsniRef.memberName() + "'");
}
+ if (isLvalue && target.constant() != Constant.NotAConstant) {
+ emitError("Illegal assignment to compile-time constant '"
+ + jsniRef.className() + "." + jsniRef.memberName() + "'");
+ }
+ if (target.isStatic() && hasQualifier) {
+ emitError("Unnecessary qualifier on static field '"
+ + jsniRef.className() + "." + jsniRef.memberName() + "'");
+ } else if (!target.isStatic() && !hasQualifier) {
+ emitError("Missing qualifier on instance field '" +
jsniRef.className()
+ + "." + jsniRef.memberName() + "'");
+ }
if (hasUnsafeLongsAnnotation) {
- return;
+ return target;
}
if (containsLong(target.type)) {
emitError("Referencing field '" + jsniRef.className() + "."
+ jsniRef.memberName() + "': type '" + typeString(target.type)
+ "' is not safe to access in JSNI code");
}
+ return target;
}
- private void checkMethodRef(ReferenceBinding clazz, JsniRef jsniRef) {
+ private MethodBinding checkMethodRef(ReferenceBinding clazz,
+ JsniRef jsniRef, boolean hasQualifier, boolean isLvalue) {
assert jsniRef.isMethod();
MethodBinding target = getMethod(clazz, jsniRef);
if (target == null) {
- emitError("JSNI Referencing method '" + jsniRef.className() + "."
- + jsniRef.memberSignature()
- + "': unable to resolve method, expect subsequent failures");
- return;
+ emitError("Referencing method '" + jsniRef.className() + "."
+ + jsniRef.memberSignature() + "': unable to resolve method");
+ return null;
}
if (target.isDeprecated()) {
emitWarning("deprecation",
"Referencing deprecated method '" + jsniRef.className() + "."
+ jsniRef.memberName() + "'");
}
+ if (isLvalue) {
+ emitError("Illegal assignment to method '" + jsniRef.className()
+ "."
+ + jsniRef.memberName() + "'");
+ }
+ boolean needsQualifer = !target.isStatic()
&& !target.isConstructor();
+ if (!needsQualifer && hasQualifier) {
+ emitError("Unnecessary qualifier on static method '"
+ + jsniRef.className() + "." + jsniRef.memberName() + "'");
+ } else if (needsQualifer && !hasQualifier) {
+ emitError("Missing qualifier on instance method '"
+ + jsniRef.className() + "." + jsniRef.memberName() + "'");
+ }
+ if (!target.isStatic() && JSORestrictionsChecker.isJso(clazz)) {
+ emitError("Referencing method '" + jsniRef.className() + "."
+ + jsniRef.memberSignature()
+ + "': references to instance methods in overlay types are
illegal");
+ }
+ if (checkerState.isJsoInterface(clazz)) {
+ String implementor = checkerState.getJsoImplementor(clazz);
+ emitError("Referencing interface method '" + jsniRef.className()
+ "."
+ + jsniRef.memberSignature() + "': implemented by '" +
implementor
+ + "'; references to instance methods in overlay types are
illegal"
+ + "; use a stronger type or a Java trampoline method");
+ }
if (hasUnsafeLongsAnnotation) {
- return;
+ return target;
}
if (containsLong(target.returnType)) {
emitError("Referencing method '" + jsniRef.className() + "."
@@ -266,26 +305,47 @@
}
}
}
+ return target;
}
- private void checkRef(JsniRef jsniRef) {
+ private Binding checkRef(JsniRef jsniRef, boolean hasQualifier,
+ boolean isLvalue) {
String className = jsniRef.className();
if ("null".equals(className)) {
- return;
+ if (jsniRef.isField()) {
+ if (!"nullField".equals(jsniRef.memberName())) {
+ emitError("Referencing field '" + jsniRef.className() + "."
+ + jsniRef.memberName()
+ + "': 'nullField' is the only legal field reference
for 'null'");
+ }
+ } else {
+ if (!"nullMethod()".equals(jsniRef.memberSignature())) {
+ emitError("Referencing method '" + jsniRef.className() + "."
+ + jsniRef.memberSignature()
+ + "': 'nullMethod()' is the only legal method for 'null'");
+ }
+ return null;
+ }
+ return null;
}
boolean isArray = false;
+ int dims = 0;
while (className.endsWith("[]")) {
+ ++dims;
isArray = true;
className = className.substring(0, className.length() - 2);
}
- boolean isPrimitive = false;
+ boolean isPrimitive;
+ ReferenceBinding clazz;
TypeBinding binding =
method.scope.getBaseType(className.toCharArray());
if (binding != null) {
isPrimitive = true;
+ clazz = null;
} else {
- binding = findClass(className);
+ isPrimitive = false;
+ binding = clazz = findClass(className);
}
// TODO(deprecation): remove this support eventually.
@@ -302,40 +362,49 @@
+ String.valueOf(binding.sourceName()) + "' instead");
}
- if (isArray || isPrimitive) {
- if (!jsniRef.isField() || !jsniRef.memberName().equals("class")) {
- emitError("Referencing member '" + jsniRef.className() + "."
- + jsniRef.memberName()
- + "': 'class' is the only legal reference for "
- + (isArray ? "array" : "primitive") + " types");
- return;
- }
- }
-
- if (isPrimitive) {
- return;
- }
-
- if (looksLikeAnonymousClass(jsniRef)
+ if ((binding == null && looksLikeAnonymousClass(jsniRef))
|| (binding != null && binding.isAnonymousType())) {
emitError("Referencing class '" + className
- + ": JSNI references to anonymous classes are illegal");
- return;
+ + "': JSNI references to anonymous classes are illegal");
+ return null;
} else if (binding == null) {
- emitError("JSNI Referencing class '" + className
- + "': unable to resolve class, expect subsequent failures");
- return;
- }
- ReferenceBinding clazz = (ReferenceBinding) binding;
- if (clazz.isDeprecated()) {
+ emitError("Referencing class '" + className
+ + "': unable to resolve class");
+ return null;
+ }
+
+ if (clazz != null && clazz.isDeprecated()) {
emitWarning("deprecation", "Referencing deprecated class '" +
className
+ "'");
}
+ if (jsniRef.isField() && "class".equals(jsniRef.memberName())) {
+ if (isLvalue) {
+ emitError("Illegal assignment to class literal '"
+ + jsniRef.className() + ".class'");
+ return null;
+ }
+ // Reference to the class itself.
+ if (isArray) {
+ return method.scope.createArrayType(binding, dims);
+ } else {
+ return binding;
+ }
+ }
+
+ if (isArray || isPrimitive) {
+ emitError("Referencing member '" + jsniRef.className() + "."
+ + jsniRef.memberName()
+ + "': 'class' is the only legal reference for "
+ + (isArray ? "array" : "primitive") + " types");
+ return null;
+ }
+
+ assert clazz != null;
if (jsniRef.isMethod()) {
- checkMethodRef(clazz, jsniRef);
+ return checkMethodRef(clazz, jsniRef, hasQualifier, isLvalue);
} else {
- checkFieldRef(clazz, jsniRef);
+ return checkFieldRef(clazz, jsniRef, hasQualifier, isLvalue);
}
}
@@ -485,9 +554,10 @@
*
*/
public static void check(CompilationUnitDeclaration cud,
- Map<AbstractMethodDeclaration, JsniMethod> jsniMethods,
- TypeResolver typeResolver) {
- new JsniChecker(cud, typeResolver, jsniMethods).check();
+ CheckerState checkerState,
+ Map<MethodDeclaration, JsniMethod> jsniMethods,
+ Map<String, Binding> jsniRefs, TypeResolver typeResolver) {
+ new JsniChecker(cud, checkerState, typeResolver, jsniMethods,
jsniRefs).check();
}
static Set<String> getSuppressedWarnings(Annotation[] annotations) {
@@ -523,17 +593,22 @@
return Sets.create();
}
+ private final CheckerState checkerState;
private final CompilationUnitDeclaration cud;
- private final Map<AbstractMethodDeclaration, JsniMethod> jsniMethods;
+ private final Map<MethodDeclaration, JsniMethod> jsniMethods;
+ private final Map<String, Binding> jsniRefs;
private final Stack<Set<String>> suppressWarningsStack = new
Stack<Set<String>>();
private final TypeResolver typeResolver;
private JsniChecker(CompilationUnitDeclaration cud,
- TypeResolver typeResolver,
- Map<AbstractMethodDeclaration, JsniMethod> jsniMethods) {
+ CheckerState checkerState, TypeResolver typeResolver,
+ Map<MethodDeclaration, JsniMethod> jsniMethods,
+ Map<String, Binding> jsniRefs) {
+ this.checkerState = checkerState;
this.cud = cud;
this.typeResolver = typeResolver;
this.jsniMethods = jsniMethods;
+ this.jsniRefs = jsniRefs;
}
private void check() {
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/javac/JsniCollector.java Tue Dec
14 13:56:04 2010
+++ /trunk/dev/core/src/com/google/gwt/dev/javac/JsniCollector.java Fri Jan
28 10:48:44 2011
@@ -20,7 +20,6 @@
import com.google.gwt.dev.jjs.InternalCompilerException;
import com.google.gwt.dev.jjs.SourceInfo;
import com.google.gwt.dev.jjs.SourceOrigin;
-import com.google.gwt.dev.jjs.ast.JAnnotation;
import com.google.gwt.dev.js.JsParser;
import com.google.gwt.dev.js.JsParserException;
import com.google.gwt.dev.js.JsParserException.SourceDetail;
@@ -131,7 +130,6 @@
return false;
}
for (Annotation a : method.annotations) {
- JAnnotation annotation;
ReferenceBinding binding = (ReferenceBinding) a.resolvedType;
String name = CharOperation.toString(binding.compoundName);
if (name.equals(GwtScriptOnly.class.getName())) {
@@ -141,12 +139,12 @@
return false;
}
- private final Map<AbstractMethodDeclaration, JsniMethod> jsniMethods;
+ private final Map<MethodDeclaration, JsniMethod> jsniMethods;
private final JsProgram jsProgram;
private final String source;
public Visitor(String source, JsProgram program,
- Map<AbstractMethodDeclaration, JsniMethod> jsniMethods) {
+ Map<MethodDeclaration, JsniMethod> jsniMethods) {
this.jsProgram = program;
this.jsniMethods = jsniMethods;
this.source = source;
@@ -159,14 +157,13 @@
@Override
protected void processMethod(TypeDeclaration typeDecl,
- AbstractMethodDeclaration method, String enclosingType,
- String loc) {
+ AbstractMethodDeclaration method, String enclosingType, String
loc) {
JsFunction jsFunction = parseJsniFunction(method, source,
enclosingType,
loc, jsProgram);
if (jsFunction != null) {
String jsniSignature = getJsniSignature(enclosingType, method);
- jsniMethods.put(method, new JsniMethodImpl(jsniSignature,
- jsFunction, isScriptOnly(method)));
+ jsniMethods.put((MethodDeclaration) method, new JsniMethodImpl(
+ jsniSignature, jsFunction, isScriptOnly(method)));
}
}
}
@@ -175,10 +172,10 @@
public static final String JSNI_BLOCK_START = "/*-{";
- public static Map<AbstractMethodDeclaration, JsniMethod>
collectJsniMethods(
+ public static Map<MethodDeclaration, JsniMethod> collectJsniMethods(
final CompilationUnitDeclaration cud, final String source,
final JsProgram program) {
- Map<AbstractMethodDeclaration, JsniMethod> jsniMethods = new
IdentityHashMap<AbstractMethodDeclaration, JsniMethod>();
+ Map<MethodDeclaration, JsniMethod> jsniMethods = new
IdentityHashMap<MethodDeclaration, JsniMethod>();
new Visitor(source, program, jsniMethods).collect(cud);
return IdentityMaps.normalizeUnmodifiable(jsniMethods);
}
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
Thu Jan 27 12:46:16 2011
+++ /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
Fri Jan 28 10:48:44 2011
@@ -2985,31 +2985,12 @@
private void processField(JsNameRef nameRef, SourceInfo info,
JField field, JsContext<JsExpression> ctx) {
- if (field.getEnclosingType() != null) {
- if (field.isStatic() && nameRef.getQualifier() != null) {
- JsniCollector.reportJsniError(info, methodDecl,
- "Cannot make a qualified reference to the static field "
- + field.getName());
- } else if (!field.isStatic() && nameRef.getQualifier() == null) {
- JsniCollector.reportJsniError(info, methodDecl,
- "Cannot make an unqualified reference to the instance
field "
- + field.getName());
- }
- }
-
/*
* We must replace any compile-time constants with the constant
value of
* the field.
*/
if (field.isCompileTimeConstant()) {
- if (ctx.isLvalue()) {
- JsniCollector.reportJsniError(
- info,
- methodDecl,
- "Cannot change the value of compile-time constant "
- + field.getName());
- }
-
+ assert !ctx.isLvalue();
JLiteral initializer = field.getConstInitializer();
JType type = initializer.getType();
if (type instanceof JPrimitiveType ||
program.isJavaLangString(type)) {
@@ -3031,43 +3012,7 @@
private void processMethod(JsNameRef nameRef, SourceInfo info,
JMethod method, JsContext<JsExpression> ctx) {
- JDeclaredType enclosingType = method.getEnclosingType();
- if (enclosingType != null) {
- JClassType jsoImplType =
program.typeOracle.getSingleJsoImpl(enclosingType);
- if (jsoImplType != null) {
- JsniCollector.reportJsniError(info, methodDecl,
- "Illegal reference to method '" + method.getName()
- + "' in type '" + enclosingType.getName()
- + "', which is implemented by an overlay type '"
- + jsoImplType.getName()
- + "'. Use a stronger type in the JSNI "
- + "identifier or a Java trampoline method.");
- } else if (!method.needsVtable() && nameRef.getQualifier() !=
null) {
- JsniCollector.reportJsniError(info, methodDecl,
- "Cannot make a qualified reference to the static method "
- + method.getName());
- } else if (method.needsVtable() && nameRef.getQualifier() == null
- && !(method instanceof JConstructor)) {
- JsniCollector.reportJsniError(info, methodDecl,
- "Cannot make an unqualified reference to the instance
method "
- + method.getName());
- } else if (!method.isStatic()
- && program.isJavaScriptObject(enclosingType)) {
- JsniCollector.reportJsniError(
- info,
- methodDecl,
- "Illegal reference to instance method '"
- + method.getName()
- + "' in type '"
- + enclosingType.getName()
- + "', which is an overlay type; only static references
to overlay types are allowed from JSNI");
- }
- }
- if (ctx.isLvalue()) {
- JsniCollector.reportJsniError(info, methodDecl,
- "Cannot reassign the Java method " + method.getName());
- }
-
+ assert !ctx.isLvalue();
JsniMethodRef methodRef = new JsniMethodRef(info,
nameRef.getIdent(),
method, program.getJavaScriptObject());
nativeMethodBody.addJsniRef(methodRef);
=======================================
--- /trunk/dev/core/test/com/google/gwt/dev/javac/JsniCheckerTest.java Fri
Jan 28 06:04:41 2011
+++ /trunk/dev/core/test/com/google/gwt/dev/javac/JsniCheckerTest.java Fri
Jan 28 10:48:44 2011
@@ -36,7 +36,7 @@
code.append(" }-*/;\n");
code.append("}\n");
- shouldGenerateError(code, 8, "Referencing class \'Buggy$1: "
+ shouldGenerateError(code, 8, "Referencing class 'Buggy$1': "
+ "JSNI references to anonymous classes are illegal");
}
@@ -58,7 +58,7 @@
code.append(" }-*/;\n");
code.append("}\n");
- shouldGenerateError(code, 10, "Referencing class \'Buggy$1.A: "
+ shouldGenerateError(code, 10, "Referencing class 'Buggy$1.A': "
+ "JSNI references to anonymous classes are illegal");
}
@@ -84,6 +84,27 @@
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() {
{
@@ -134,7 +155,7 @@
public void testDeprecationField() {
StringBuffer code = new StringBuffer();
code.append("class Buggy {\n");
- code.append(" @Deprecated int bar;\n");
+ code.append(" @Deprecated static int bar;\n");
code.append(" native void jsniMethod() /*-{\n");
code.append(" @Buggy::bar;\n");
code.append(" }-*/;\n");
@@ -146,7 +167,7 @@
public void testDeprecationMethod() {
StringBuffer code = new StringBuffer();
code.append("class Buggy {\n");
- code.append(" @Deprecated void foo(){}\n");
+ code.append(" @Deprecated static void foo(){}\n");
code.append(" native void jsniMethod() /*-{\n");
code.append(" @Buggy::foo();\n");
code.append(" }-*/;\n");
@@ -158,11 +179,11 @@
public void testDeprecationSuppression() {
StringBuffer code = new StringBuffer();
code.append("@Deprecated class D {\n");
- code.append(" int bar;\n");
+ code.append(" static int bar;\n");
code.append("}\n");
code.append("class Buggy {\n");
- code.append(" @Deprecated void foo(){}\n");
- code.append(" @Deprecated int bar;\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");
@@ -181,12 +202,12 @@
// Check inherited suppress warnings.
code = new StringBuffer();
code.append("@Deprecated class D {\n");
- code.append(" int bar;\n");
+ code.append(" static int bar;\n");
code.append("}\n");
code.append("@SuppressWarnings(\"deprecation\")\n");
code.append("class Buggy {\n");
- code.append(" @Deprecated void foo(){}\n");
- code.append(" @Deprecated int bar;\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");
@@ -204,7 +225,7 @@
public void testDeprecationType() {
StringBuffer code = new StringBuffer();
code.append("@Deprecated class D {\n");
- code.append(" int bar;\n");
+ code.append(" static int bar;\n");
code.append("}\n");
code.append("class Buggy {\n");
code.append(" native void jsniMethod() /*-{\n");
@@ -214,6 +235,17 @@
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();
@@ -226,12 +258,186 @@
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()[email protected]::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()[email protected]::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()[email protected]::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(" long x = 3;\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");
@@ -246,7 +452,7 @@
StringBuffer code = new StringBuffer();
code.append("public class Buggy {\n");
code.append(" static class Inner {\n");
- code.append(" long x = 3;\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");
@@ -270,8 +476,8 @@
code.append("}\n");
// Cannot resolve, missing synthetic enclosing instance.
- shouldGenerateError(code, 7, "JSNI Referencing
method 'Buggy.Inner.new(Z)': "
- + "unable to resolve method, expect subsequent failures");
+ shouldGenerateError(code, 7, "Referencing
method 'Buggy.Inner.new(Z)': "
+ + "unable to resolve method");
code = new StringBuffer();
code.append("public class Buggy {\n");
@@ -344,6 +550,17 @@
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();
@@ -355,7 +572,18 @@
shouldGenerateError(
code,
3,
- "Parameter 1 of method \'Buggy.print\': type 'long' may not be
passed out of JSNI code");
+ "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'");
}
/**
@@ -393,6 +621,41 @@
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();
@@ -420,8 +683,18 @@
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() {
@@ -431,8 +704,18 @@
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() {
@@ -517,7 +800,7 @@
code.append(" }-*/;\n");
code.append("}\n");
shouldGenerateError(code, 3,
- "JSNI Referencing class 'Foo': unable to resolve class, expect
subsequent failures");
+ "Referencing class 'Foo': unable to resolve class");
}
public void testUnresolvedField() {
@@ -527,10 +810,8 @@
code.append(" @Buggy::x;\n");
code.append(" }-*/;\n");
code.append("}\n");
- shouldGenerateError(
- code,
- 3,
- "JSNI Referencing field 'Buggy.x': unable to resolve field, expect
subsequent failures");
+ shouldGenerateError(code, 3,
+ "Referencing field 'Buggy.x': unable to resolve field");
}
public void testUnresolvedMethod() {
@@ -540,10 +821,8 @@
code.append(" @Buggy::x(Ljava/lang/String);\n");
code.append(" }-*/;\n");
code.append("}\n");
- shouldGenerateError(
- code,
- 3,
- "JSNI Referencing method 'Buggy.x(Ljava/lang/String)': unable to
resolve method, expect subsequent failures");
+ shouldGenerateError(code, 3,
+ "Referencing method 'Buggy.x(Ljava/lang/String)': unable to
resolve method");
}
public void testUnsafeAnnotation() {
=======================================
--- /trunk/dev/core/test/com/google/gwt/dev/javac/JsniCollectorTest.java
Wed Dec 1 13:52:36 2010
+++ /trunk/dev/core/test/com/google/gwt/dev/javac/JsniCollectorTest.java
Fri Jan 28 10:48:44 2011
@@ -56,7 +56,7 @@
assertEquals(3, problem.getSourceLineNumber());
assertTrue(problem.isError());
assertEquals(
- "JSNI Referencing method 'Foo.m(Ljava/lang/String)': unable to
resolve method, expect subsequent failures",
+ "Referencing method 'Foo.m(Ljava/lang/String)': unable to resolve
method",
problem.getMessage());
}
=======================================
---
/trunk/dev/core/test/com/google/gwt/dev/javac/impl/JavaResourceBase.java
Wed Sep 1 14:06:53 2010
+++
/trunk/dev/core/test/com/google/gwt/dev/javac/impl/JavaResourceBase.java
Fri Jan 28 10:48:44 2011
@@ -232,7 +232,9 @@
StringBuffer code = new StringBuffer();
code.append("package com.google.gwt.core.client;\n");
code.append("public class JavaScriptObject {\n");
+ code.append(" public static native JavaScriptObject createObject()
/*-{ return {}; }-*/;\n");
code.append(" protected JavaScriptObject() { }\n");
+ code.append(" public final String toString() { return
\"JavaScriptObject\"; }\n");
code.append("}\n");
return code;
}
@@ -396,7 +398,7 @@
};
public static MockJavaResource[] getStandardResources() {
- return new MockJavaResource[] {
+ return new MockJavaResource[]{
ANNOTATION, BYTE, CHARACTER, CLASS, CLASS_NOT_FOUND_EXCEPTION,
COLLECTION, DOUBLE, ENUM, EXCEPTION, ERROR, FLOAT, INTEGER,
IS_SERIALIZABLE, JAVASCRIPTOBJECT, LONG, MAP,
NO_CLASS_DEF_FOUND_ERROR,
--
http://groups.google.com/group/Google-Web-Toolkit-Contributors