Allow overloads in native JsType methods.
Change-Id: I39cc4b82949eb37f544db17abbd0788f694b18ac
diff --git a/dev/core/src/com/google/gwt/dev/javac/JsInteropUtil.java b/dev/core/src/com/google/gwt/dev/javac/JsInteropUtil.java
index cf653ef..0a0087e 100644
--- a/dev/core/src/com/google/gwt/dev/javac/JsInteropUtil.java
+++ b/dev/core/src/com/google/gwt/dev/javac/JsInteropUtil.java
@@ -42,6 +42,7 @@
public static final String JSPROPERTY_CLASS = "com.google.gwt.core.client.js.JsProperty";
public static final String JSTYPE_CLASS = "com.google.gwt.core.client.js.JsType";
public static final String UNUSABLE_BY_JS = "unusable-by-js";
+ public static final String INVALID_JSNAME = "<invalid>";
public static void maybeSetJsInteropProperties(JDeclaredType type, Annotation... annotations) {
AnnotationBinding jsType = JdtUtil.getAnnotation(annotations, JSTYPE_CLASS);
@@ -172,7 +173,7 @@
String jsName = Introspector.decapitalize(methodName.substring(2));
method.setJsPropertyInfo(jsName, JsPropertyAccessorType.GETTER);
} else {
- method.setJsPropertyInfo("<invalid>", JsPropertyAccessorType.UNDEFINED);
+ method.setJsPropertyInfo(INVALID_JSNAME, JsPropertyAccessorType.UNDEFINED);
}
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
index 6f3dc0f..13ae108 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
@@ -16,6 +16,7 @@
package com.google.gwt.dev.jjs.ast;
import com.google.gwt.dev.common.InliningMode;
+import com.google.gwt.dev.javac.JsInteropUtil;
import com.google.gwt.dev.jjs.InternalCompilerException;
import com.google.gwt.dev.jjs.SourceInfo;
import com.google.gwt.dev.jjs.SourceOrigin;
@@ -161,7 +162,7 @@
continue;
}
if (jsMemberName != null && !jsMemberName.equals(jsMemberOverrideName)) {
- return null;
+ return JsInteropUtil.INVALID_JSNAME;
}
jsMemberName = jsMemberOverrideName;
}
@@ -223,7 +224,7 @@
return JsPropertyAccessorType.NONE;
}
- public boolean isJsPropertyAccessor() {
+ private boolean isJsPropertyAccessor() {
return jsPropertyType != JsPropertyAccessorType.NONE;
}
@@ -561,7 +562,8 @@
}
/**
- * Returns the transitive closure of all the methods this method overrides.
+ * Returns the transitive closure of all the methods this method overrides; this set is ordered
+ * from most specific to least specific, where class methods appear before interface methods.
*/
public Set<JMethod> getOverriddenMethods() {
return overriddenMethods;
@@ -569,7 +571,9 @@
/**
* Returns the transitive closure of all the methods that override this method; caveat this
- * list is only complete in monolithic compiles and should not be used in incremental compiles..
+ * list is only complete in monolithic compiles and should not be used in incremental compiles.
+ * The returned set is ordered in such a way that most specific overriding methods appear after
+ * less specific ones.
*/
public Set<JMethod> getOverridingMethods() {
return overridingMethods;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JType.java
index aa8d20a..82a40f5 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JType.java
@@ -132,7 +132,7 @@
* "class Name { .. }"), i.e. it is a name that does not include enclosing type names nor package.
*/
public String[] getCompoundName() {
- return new String[] { shortName };
+ return new String[] { getShortName() };
}
/**
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
index 5b7a3e1..ddaec08 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
@@ -868,6 +868,12 @@
return subclassesByClass.containsEntry(type.getName(), possibleSubType.getName());
}
+ public boolean isSubType(JDeclaredType type, JDeclaredType possibleSubType) {
+ return subclassesByClass.containsEntry(type.getName(), possibleSubType.getName())
+ || classesByImplementingInterface.containsEntry(type.getName(), possibleSubType.getName())
+ || subInterfacesByInterface.containsEntry(type.getName(), possibleSubType.getName());
+ }
+
public Iterable<String> getSubTypeNames(String typeName) {
return Iterables.concat(classesByImplementingInterface.get(typeName),
subclassesByClass.get(typeName), subInterfacesByInterface.get(typeName));
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
index 1f0bd91..f8c997a 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
@@ -1472,6 +1472,7 @@
for (JField field : type.getFields()) {
if (field.isJsInteropEntryPoint()) {
if (!field.isFinal()) {
+ // TODO(rluble): move waring to JsInteropRestrictionChecker.
logger.log(
TreeLogger.Type.WARN,
"Exporting effectively non-final field "
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JavaAstVerifier.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JavaAstVerifier.java
index 4d0d6c9..7537714 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/JavaAstVerifier.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JavaAstVerifier.java
@@ -30,9 +30,11 @@
import com.google.gwt.dev.jjs.ast.js.JsniFieldRef;
import com.google.gwt.dev.jjs.ast.js.JsniMethodRef;
import com.google.gwt.thirdparty.guava.common.collect.HashMultimap;
+import com.google.gwt.thirdparty.guava.common.collect.Lists;
import com.google.gwt.thirdparty.guava.common.collect.Multimap;
import com.google.gwt.thirdparty.guava.common.collect.Sets;
+import java.util.List;
import java.util.Set;
/**
@@ -48,8 +50,10 @@
private Set<String> seenTypeNames = Sets.newHashSet();
private Multimap<JDeclaredType, String> seenMethodsByType = HashMultimap.create();
private Multimap<JDeclaredType, String> seenFieldsByType = HashMultimap.create();
+ private JProgram program;
JavaAstVerifier(JProgram program) {
+ this.program = program;
for (JDeclaredType type :program.getModuleDeclaredTypes()) {
membersByType.putAll(type, type.getMethods());
membersByType.putAll(type, type.getFields());
@@ -65,6 +69,40 @@
}
}
+ public static void assertCorrectOverriddenOrder(JProgram program, JMethod method) {
+ // The order of in the overriden set is most specific to least.
+ List<JMethod> seenMethods = Lists.newArrayList(method);
+ JMethod lastMethod = method;
+ for (JMethod overriden : method.getOverriddenMethods()) {
+ for (JMethod seenMethod : seenMethods) {
+ assert !program.typeOracle.isSubType(
+ seenMethod.getEnclosingType(), overriden.getEnclosingType())
+ : "Superclass method '" + seenMethod.getQualifiedName()
+ + "' appeared before subclass method '" + overriden.getQualifiedName()
+ + "' in '" + method.getQualifiedName() + "' overridden list";
+ }
+ assert overriden.getEnclosingType() instanceof JInterfaceType
+ || lastMethod.getEnclosingType() instanceof JClassType
+ : "Class method '" + overriden.getQualifiedName()
+ + "' appeared before after interface method '" + lastMethod.getQualifiedName()
+ + "' in '" + method.getQualifiedName() + "' overridden list";
+ }
+ }
+
+ public static void assertCorrectOverridingOrder(JProgram program, JMethod method) {
+ // The order of in the overriden set is most specific to least.
+ List<JMethod> seenMethods = Lists.newArrayList(method);
+ for (JMethod overriden : method.getOverridingMethods()) {
+ for (JMethod seenMethod : seenMethods) {
+ assert !program.typeOracle.isSubType(
+ overriden.getEnclosingType(), seenMethod.getEnclosingType())
+ : "Subclass method '" + seenMethod.getQualifiedName()
+ + "' appeared before superclass method '" + overriden.getQualifiedName()
+ + "' in '" + method.getQualifiedName() + "' overriding list";
+ }
+ }
+ }
+
@Override
public void endVisit(JClassType x, Context ctx) {
assertNotSeenBefore(x);
@@ -97,6 +135,8 @@
assert !seenMethodsByType.containsEntry(enclosingType, methodSignature) :
"Method " + x + " is duplicated.";
seenMethodsByType.put(enclosingType, methodSignature);
+ assertCorrectOverriddenOrder(program, x);
+ assertCorrectOverridingOrder(program, x);
}
@Override
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JjsUtils.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JjsUtils.java
index 003f7fb..34b776f 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/JjsUtils.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JjsUtils.java
@@ -20,6 +20,7 @@
import com.google.gwt.dev.jjs.SourceOrigin;
import com.google.gwt.dev.jjs.ast.HasName;
import com.google.gwt.dev.jjs.ast.HasType;
+import com.google.gwt.dev.jjs.ast.JArrayType;
import com.google.gwt.dev.jjs.ast.JBinaryOperation;
import com.google.gwt.dev.jjs.ast.JBinaryOperator;
import com.google.gwt.dev.jjs.ast.JBlock;
@@ -30,6 +31,7 @@
import com.google.gwt.dev.jjs.ast.JDeclaredType;
import com.google.gwt.dev.jjs.ast.JDoubleLiteral;
import com.google.gwt.dev.jjs.ast.JExpression;
+import com.google.gwt.dev.jjs.ast.JField;
import com.google.gwt.dev.jjs.ast.JFloatLiteral;
import com.google.gwt.dev.jjs.ast.JIntLiteral;
import com.google.gwt.dev.jjs.ast.JInterfaceType;
@@ -66,6 +68,7 @@
import com.google.gwt.thirdparty.guava.common.base.Joiner;
import com.google.gwt.thirdparty.guava.common.base.Predicate;
import com.google.gwt.thirdparty.guava.common.base.Predicates;
+import com.google.gwt.thirdparty.guava.common.base.Strings;
import com.google.gwt.thirdparty.guava.common.collect.Collections2;
import com.google.gwt.thirdparty.guava.common.collect.FluentIterable;
import com.google.gwt.thirdparty.guava.common.collect.ImmutableMap;
@@ -297,6 +300,48 @@
return createEmptyMethodFromExample(type, superTypeMethod, true);
}
+ /**
+ * Returns a description for a type suitable for reporting errors to the users.
+ */
+ public static String getReadableDescription(JType type) {
+ if (type instanceof JArrayType) {
+ JArrayType arrayType = (JArrayType) type;
+ return getReadableDescription(arrayType.getLeafType()) + Strings.repeat("[]",
+ arrayType.getDims());
+ }
+ return Joiner.on(".").join(type.getCompoundName());
+ }
+
+ /**
+ * Returns a description for a member suitable for reporting errors to the users.
+ */
+ public static String getReadableDescription(JMember member) {
+ if (member instanceof JField) {
+ return String.format("%s %s.%s",
+ getReadableDescription(member.getType()),
+ getReadableDescription(member.getEnclosingType()),
+ member.getName());
+ }
+
+ JMethod method = (JMethod) member;
+ String printableDescription = "";
+ if (!method.isConstructor()) {
+ printableDescription += getReadableDescription(method.getType()) + " ";
+ }
+ printableDescription += String.format("%s.%s(%s)",
+ getReadableDescription(method.getEnclosingType()),
+ method.getName(),
+ Joiner.on(", ").join(
+ Iterables.transform(method.getOriginalParamTypes(), new Function<JType, String>() {
+ @Override
+ public String apply(JType type) {
+ return getReadableDescription(type);
+ }
+ }
+ )));
+ return printableDescription;
+ }
+
public static void replaceMethodBody(JMethod method, JExpression returnValue) {
JMethodBody body = (JMethodBody) method.getBody();
JBlock block = body.getBlock();
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JsInteropRestrictionChecker.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JsInteropRestrictionChecker.java
index 220af42..328936e 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/JsInteropRestrictionChecker.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JsInteropRestrictionChecker.java
@@ -14,9 +14,11 @@
package com.google.gwt.dev.jjs.impl;
import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.MinimalRebuildCache;
import com.google.gwt.dev.javac.JsInteropUtil;
+import com.google.gwt.dev.jjs.HasSourceInfo;
import com.google.gwt.dev.jjs.ast.CanHaveSuppressedWarnings;
import com.google.gwt.dev.jjs.ast.Context;
import com.google.gwt.dev.jjs.ast.JClassType;
@@ -44,11 +46,14 @@
import com.google.gwt.thirdparty.guava.common.collect.FluentIterable;
import com.google.gwt.thirdparty.guava.common.collect.Iterables;
import com.google.gwt.thirdparty.guava.common.collect.Maps;
+import com.google.gwt.thirdparty.guava.common.collect.Multimap;
import com.google.gwt.thirdparty.guava.common.collect.Sets;
+import com.google.gwt.thirdparty.guava.common.collect.TreeMultimap;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Set;
+import java.util.TreeSet;
/**
* Checks and throws errors for invalid JsInterop constructs.
@@ -58,30 +63,22 @@
public static void exec(TreeLogger logger, JProgram jprogram,
MinimalRebuildCache minimalRebuildCache) throws UnableToCompleteException {
JsInteropRestrictionChecker jsInteropRestrictionChecker =
- new JsInteropRestrictionChecker(logger, jprogram, minimalRebuildCache);
-
- jsInteropRestrictionChecker.checkProgram();
- if (jsInteropRestrictionChecker.hasErrors) {
+ new JsInteropRestrictionChecker(jprogram, minimalRebuildCache);
+ boolean success = jsInteropRestrictionChecker.checkProgram(logger);
+ if (!success) {
throw new UnableToCompleteException();
}
}
- private Map<String, String> currentJsMethodNameByGetterNames;
- private Map<String, String> currentJsMethodNameBySetterNames;
- private Map<String, JType> currentJsPropertyTypeByName;
- private Map<String, String> currentLocalNameByMemberNames;
- private Set<JMethod> currentProcessedMethods;
- private JDeclaredType currentType;
- private boolean hasErrors;
+ private Multimap<String, String> errorsByFilename = TreeMultimap.create();
+ private Multimap<String, String> warningsByFilename = TreeMultimap.create();
private final JProgram jprogram;
- private final TreeLogger logger;
private final MinimalRebuildCache minimalRebuildCache;
// TODO review any use of word export
- private JsInteropRestrictionChecker(TreeLogger logger, JProgram jprogram,
+ private JsInteropRestrictionChecker(JProgram jprogram,
MinimalRebuildCache minimalRebuildCache) {
- this.logger = logger;
this.jprogram = jprogram;
this.minimalRebuildCache = minimalRebuildCache;
}
@@ -185,12 +182,16 @@
}
}).toList();
+ if (x.isJsNative()) {
+ return;
+ }
+
if (jsConstructors.isEmpty()) {
return;
}
if (jsConstructors.size() > 1) {
- logError("More than one JsConstructor exists for %s.", x.getName());
+ logError(x, "More than one JsConstructor exists for %s.", JjsUtils.getReadableDescription(x));
}
final JConstructor jsConstructor = (JConstructor) jsConstructors.get(0);
@@ -204,8 +205,9 @@
});
if (anyNonDelegatingConstructor) {
- logError("Constructor '%s' can be a JsConstructor only if all constructors in the class are "
- + "delegating to it.", jsConstructor.getQualifiedName());
+ logError(jsConstructor,
+ "Constructor %s can be a JsConstructor only if all constructors in the class are "
+ + "delegating to it.", getMemberDescription(jsConstructor));
}
}
@@ -217,70 +219,127 @@
return call.getTarget().equals(targetCtor);
}
- private void checkField(JField x) {
- if (x.getEnclosingType().isJsNative()) {
- checkMemberOfNativeJsType(x);
+ private void checkField(Map<String, JsMember> localNames, JField field) {
+ if (field.getEnclosingType().isJsNative()) {
+ checkMemberOfNativeJsType(field);
}
- checkUnusableByJs(x);
- if (!x.isJsProperty()) {
+ checkUnusableByJs(field);
+
+ if (!field.isJsProperty()) {
return;
}
- if (x.needsDynamicDispatch()) {
- checkLocalName(x);
- } else if (!x.isJsNative() && currentType == x.getEnclosingType()) {
- checkGlobalName(x);
+ if (field.needsDynamicDispatch()) {
+ checkLocalName(localNames, field);
+ } else if (!field.isJsNative()) {
+ checkGlobalName(field);
}
}
- private void checkMethod(JMethod x) {
- if (!currentProcessedMethods.add(x)) {
- return;
- }
- currentProcessedMethods.addAll(x.getOverriddenMethods());
-
- if (x.getEnclosingType().isJsNative()) {
- checkMemberOfNativeJsType(x);
+ private void checkMethod(Map<String, JsMember> localNames, JMethod method) {
+ if (method.getEnclosingType().isJsNative()) {
+ checkMemberOfNativeJsType(method);
}
- if (x.isJsOverlay()) {
- checkJsOverlay(x);
+ if (method.isJsOverlay()) {
+ checkJsOverlay(method);
}
- checkUnusableByJs(x);
+ checkUnusableByJs(method);
- if (!x.isOrOverridesJsMethod()) {
+ if (!method.isOrOverridesJsMethod()) {
return;
}
- if (x.needsDynamicDispatch()) {
- checkJsMethod(x);
- } else if (!x.isJsNative() && currentType == x.getEnclosingType()) {
- checkGlobalName(x);
+ if (method.needsDynamicDispatch()) {
+ if (!isSyntheticBridgeMethod(method)) {
+ checkLocalName(localNames, method);
+ }
+ } else if (!method.isJsNative()) {
+ checkGlobalName(method);
}
}
- private void checkGlobalName(JMember x) {
- if (!minimalRebuildCache.addExportedGlobalName(x.getQualifiedJsName(), currentType.getName())) {
- logError("'%s' can't be exported because the global name '%s' is already taken.",
- x.getQualifiedName(), x.getQualifiedJsName());
+ private void checkGlobalName(JMember member) {
+ if (!minimalRebuildCache.addExportedGlobalName(member.getQualifiedJsName(),
+ member.getEnclosingType().getName())) {
+ logError(member, "%s cannot be exported because the global name '%s' is already taken.",
+ getMemberDescription(member), member.getQualifiedJsName());
}
}
- private void checkLocalName(JMember member) {
- String jsName = member.getJsName();
- if (currentLocalNameByMemberNames.put(jsName, member.getQualifiedName()) != null) {
- logError("'%s' can't be exported in type '%s' because the name '%s' is already taken.",
- member.getQualifiedName(), currentType.getName(), jsName);
+ private void checkLocalName(Map<String, JsMember> localNames, JMember member) {
+ if (member.getJsName().equals(JsInteropUtil.INVALID_JSNAME)) {
+ if (member instanceof JMethod
+ && ((JMethod) member).getJsPropertyAccessorType() == JsPropertyAccessorType.UNDEFINED) {
+ logError(member, "JsProperty %s doesn't follow Java Bean naming conventions.",
+ getMemberDescription(member));
+ } else {
+ logError(
+ member, "%s cannot be assigned a different JavaScript name than the method it overrides.",
+ getMemberDescription(member));
+ }
+ return;
+ }
+
+ JsMember oldMember = localNames.get(member.getJsName());
+ JsMember newMember = createOrUpdateJsMember(oldMember, member);
+
+ checkJsPropertyAccessor(member, newMember);
+
+ if (oldMember == null || newMember == oldMember) {
+ localNames.put(member.getJsName(), newMember);
+ return;
+ }
+
+ if (oldMember.isNativeMethod() && newMember.isNativeMethod()) {
+ return;
+ }
+
+ logError(member, "%s and %s cannot both use the same JavaScript name '%s'.",
+ getMemberDescription(member), getMemberDescription(oldMember.member), member.getJsName());
+ }
+
+ void checkJsPropertyAccessor(JMember x, JsMember newMember) {
+ if (newMember.setter != null) {
+ checkValidSetter(newMember.setter);
+ }
+ if (newMember.getter != null) {
+ checkValidGetter(newMember.getter);
+ }
+ if (newMember.setter != null && newMember.getter != null) {
+ checkJsPropertyGetterConsistentWithSetter(
+ x.getEnclosingType(), newMember.setter, newMember.getter);
}
}
- private void checkJsPropertyType(String propertyName, String enclosingTypeName, JType type) {
- JType recordedType = currentJsPropertyTypeByName.put(propertyName, type);
- if (recordedType != null && recordedType != type) {
- logError("The setter and getter for JsProperty '%s' in type '%s' must have consistent types.",
- propertyName, enclosingTypeName);
+ private void checkJsPropertyGetterConsistentWithSetter(
+ JType type, JMethod setter, JMethod getter) {
+ if (setter.getParams().size() == 1
+ && getter.getType() != setter.getParams().get(0).getType()) {
+ logError(setter,
+ "The setter and getter for JsProperty '%s' in type '%s' must have consistent types.",
+ setter.getJsName(), JjsUtils.getReadableDescription(type));
+ }
+ }
+
+ private void checkValidSetter(JMethod setter) {
+ if (setter.getParams().size() != 1 || setter.getType() != JPrimitiveType.VOID) {
+ logError(setter, "There needs to be single parameter and void return type for the "
+ + "JsProperty setter %s.", getMemberDescription(setter));
+ }
+ }
+
+ private void checkValidGetter(JMethod getter) {
+ if (!getter.getParams().isEmpty() || getter.getType() == JPrimitiveType.VOID) {
+ logError(getter,
+ "There cannot be void return type or any parameters for the JsProperty getter"
+ + " %s.", getMemberDescription(getter));
+ }
+ if (getter.getType() != JPrimitiveType.BOOLEAN && getter.getName().startsWith("is")) {
+ logError(getter, "There cannot be non-boolean return for the JsProperty 'is' getter %s.",
+ getMemberDescription(getter));
}
}
@@ -289,19 +348,22 @@
return;
}
- String qualifiedName = method.getQualifiedName();
+ String methodDescription = JjsUtils.getReadableDescription(method);
if (!method.getEnclosingType().isJsNative()) {
- logError("Method '%s' in non-native type cannot be @JsOverlay.", qualifiedName);
+ logError(method,
+ "Method '%s' in non-native type cannot be @JsOverlay.", methodDescription);
}
if (!method.getOverriddenMethods().isEmpty()) {
- logError("JsOverlay method '%s' cannot override a supertype method.", qualifiedName);
+ logError(method,
+ "JsOverlay method '%s' cannot override a supertype method.", methodDescription);
return;
}
if (method.isJsNative() || method.isJsniMethod() || method.isStatic() || !method.isFinal()) {
- logError("JsOverlay method '%s' cannot be non-final, static, nor native.", qualifiedName);
+ logError(method,
+ "JsOverlay method '%s' cannot be non-final, static, nor native.", methodDescription);
}
}
@@ -311,96 +373,12 @@
}
if (member.getJsName() == null && !member.isJsOverlay()) {
- logError("Native JsType member '%s' is not public or has @JsIgnore.",
- member.getQualifiedName());
+ logError(member, "Native JsType member %s is not public or has @JsIgnore.",
+ getMemberDescription(member));
return;
}
}
- private void checkJsMethod(JMethod method) {
- if (method.isSynthetic() && !method.isForwarding()) {
- // A name slot taken up by a synthetic method, such as a bridge method for a generic method,
- // is not the fault of the user and so should not be reported as an error. JS generation
- // should take responsibility for ensuring that only the correct method version (in this
- // particular set of colliding method names) is exported. Forwarding synthetic methods
- // (such as an accidental override forwarding method that occurs when a JsType interface
- // starts exposing a method in class B that is only ever implemented in its parent class A)
- // though should be checked since they are exported and do take up an name slot.
- return;
- }
-
- String jsMemberName = method.getJsName();
- String qualifiedMethodName = method.getQualifiedName();
- String typeName = method.getEnclosingType().getName();
- JsPropertyAccessorType accessorType = method.getJsPropertyAccessorType();
-
- if (jsMemberName == null) {
- logError("'%s' can't be exported because the method overloads multiple methods with "
- + "different names.", qualifiedMethodName);
- }
-
- if (accessorType == JsPropertyAccessorType.GETTER) {
- if (!method.getParams().isEmpty() || method.getType() == JPrimitiveType.VOID) {
- logError("There can't be void return type or any parameters for the JsProperty getter"
- + " '%s'.", qualifiedMethodName);
- return;
- }
- if (method.getType() != JPrimitiveType.BOOLEAN && method.getName().startsWith("is")) {
- logError("There can't be non-boolean return for the JsProperty 'is' getter '%s'.",
- qualifiedMethodName);
- return;
- }
- if (currentJsMethodNameByGetterNames.put(jsMemberName, qualifiedMethodName) != null) {
- // Don't allow multiple getters for the same property name.
- logError("There can't be more than one getter for JsProperty '%s' in type '%s'.",
- jsMemberName, typeName);
- return;
- }
- checkNameCollisionForGetterAndRegular(jsMemberName, typeName);
- checkJsPropertyType(jsMemberName, typeName, method.getOriginalReturnType());
- } else if (accessorType == JsPropertyAccessorType.SETTER) {
- if (method.getParams().size() != 1 || method.getType() != JPrimitiveType.VOID) {
- logError("There needs to be single parameter and void return type for the JsProperty setter"
- + " '%s'.", qualifiedMethodName);
- return;
- }
- if (currentJsMethodNameBySetterNames.put(jsMemberName, qualifiedMethodName) != null) {
- // Don't allow multiple setters for the same property name.
- logError("There can't be more than one setter for JsProperty '%s' in type '%s'.",
- jsMemberName, typeName);
- return;
- }
- checkNameCollisionForSetterAndRegular(jsMemberName, typeName);
- checkJsPropertyType(jsMemberName, typeName,
- Iterables.getOnlyElement(method.getParams()).getType());
- } else if (accessorType == JsPropertyAccessorType.UNDEFINED) {
- // We couldn't extract the JsPropertyType.
- logError("JsProperty '%s' doesn't follow Java Bean naming conventions.", qualifiedMethodName);
- } else {
- checkLocalName(method);
- checkNameCollisionForGetterAndRegular(jsMemberName, typeName);
- checkNameCollisionForSetterAndRegular(jsMemberName, typeName);
- }
- }
-
- private void checkNameCollisionForGetterAndRegular(String getterName, String typeName) {
- if (currentJsMethodNameByGetterNames.containsKey(getterName)
- && currentLocalNameByMemberNames.containsKey(getterName)) {
- logError("'%s' and '%s' can't both be named '%s' in type '%s'.",
- currentLocalNameByMemberNames.get(getterName),
- currentJsMethodNameByGetterNames.get(getterName), getterName, typeName);
- }
- }
-
- private void checkNameCollisionForSetterAndRegular(String setterName, String typeName) {
- if (currentJsMethodNameBySetterNames.containsKey(setterName)
- && currentLocalNameByMemberNames.containsKey(setterName)) {
- logError("'%s' and '%s' can't both be named '%s' in type '%s'.",
- currentLocalNameByMemberNames.get(setterName),
- currentJsMethodNameBySetterNames.get(setterName), setterName, typeName);
- }
- }
-
private void checkStaticJsPropertyCalls() {
new JVisitor() {
@Override
@@ -412,11 +390,10 @@
@Override
public void endVisit(JMethodCall x, Context ctx) {
JMethod target = x.getTarget();
- if (x.isStaticDispatchOnly() && target.isJsPropertyAccessor()) {
- logError("Cannot call property accessor '%s' via super (%s:%d).",
- target.getQualifiedName(),
- x.getSourceInfo().getFileName(),
- x.getSourceInfo().getStartLine());
+ if (x.isStaticDispatchOnly() &&
+ target.getJsPropertyAccessorType() != JsPropertyAccessorType.NONE) {
+ logError(x, "Cannot call property accessor %s via super.",
+ getMemberDescription(target));
}
}
}.accept(jprogram);
@@ -428,10 +405,8 @@
public boolean visit(JInstanceOf x, Context ctx) {
JReferenceType type = x.getTestType();
if (type.isJsNative() && type instanceof JInterfaceType) {
- logError("Cannot do instanceof against native JsType interface %s (%s:%d).",
- type.getName(),
- x.getSourceInfo().getFileName(),
- x.getSourceInfo().getStartLine());
+ logError(x, "Cannot do instanceof against native JsType interface '%s'.",
+ JjsUtils.getReadableDescription(type));
}
return true;
}
@@ -450,8 +425,8 @@
for (JConstructor constructor : type.getConstructors()) {
if (!isConstructorEmpty(constructor)) {
- logError("Native JsType constructor '%s' cannot have non-empty method body.",
- constructor.getQualifiedName());
+ logError(constructor, "Native JsType constructor %s cannot have non-empty method body.",
+ getMemberDescription(constructor));
}
}
}
@@ -468,16 +443,12 @@
if (type.isJsType()) {
logError("'%s' cannot be both a JsFunction and a JsType at the same time.", type);
}
-
- Set<String> subTypes = jprogram.typeOracle.getSubInterfaceNames(type.getName());
- if (!subTypes.isEmpty()) {
- logError("JsFunction '%s' cannot be extended by other interfaces:%s", type, subTypes);
- }
}
private void checkJsFunctionImplementation(JDeclaredType type) {
if (type.getImplements().size() != 1) {
- logError("JsFunction implementation '%s' cannot implement more than one interface.", type);
+ logError("JsFunction implementation '%s' cannot implement more than one interface.",
+ type);
}
if (type.isJsType()) {
@@ -488,29 +459,34 @@
if (type.getSuperClass() != jprogram.getTypeJavaLangObject()) {
logError("JsFunction implementation '%s' cannot extend a class.", type);
}
+ }
- Set<String> subTypes = jprogram.typeOracle.getSubClassNames(type.getName());
- if (!subTypes.isEmpty()) {
- logError("Implementation of JsFunction '%s' cannot be extended by other classes:%s", type,
- subTypes);
+ private void checkJsFunctionSubtype(JDeclaredType type) {
+ JClassType superClass = type.getSuperClass();
+ if (superClass != null && superClass.isJsFunctionImplementation()) {
+ logError(type, "'%s' cannot extend JsFunction implementation '%s'.",
+ JjsUtils.getReadableDescription(type), JjsUtils.getReadableDescription(superClass));
+ }
+ for (JInterfaceType superInterface : type.getImplements()) {
+ if (superInterface.isJsFunction()) {
+ logError(type, "'%s' cannot extend JsFunction '%s'.",
+ JjsUtils.getReadableDescription(type), JjsUtils.getReadableDescription(superInterface));
+ }
}
}
- private void checkProgram() {
+ private boolean checkProgram(TreeLogger logger) {
for (JDeclaredType type : jprogram.getModuleDeclaredTypes()) {
checkType(type);
}
checkStaticJsPropertyCalls();
checkInstanceOfNativeJsTypes();
+
+ boolean hasErrors = reportErrorsAndWarnings(logger);
+ return !hasErrors;
}
private void checkType(JDeclaredType type) {
- currentProcessedMethods = Sets.newHashSet();
- currentLocalNameByMemberNames = Maps.newHashMap();
- currentJsMethodNameByGetterNames = Maps.newHashMap();
- currentJsMethodNameBySetterNames = Maps.newHashMap();
- currentJsPropertyTypeByName = Maps.newHashMap();
- currentType = type;
minimalRebuildCache.removeExportedNames(type.getName());
if (type.isJsNative()) {
@@ -522,16 +498,17 @@
} else if (type.isJsFunctionImplementation()) {
checkJsFunctionImplementation(type);
} else {
+ checkJsFunctionSubtype(type);
checkJsConstructors(type);
}
- for (;type != null; type = type.getSuperClass()) {
- for (JField field : type.getFields()) {
- checkField(field);
- }
- for (JMethod method : type.getMethods()) {
- checkMethod(method);
- }
+ Map<String, JsMember> localNames = collectNames(type.getSuperClass());
+
+ for (JField field : type.getFields()) {
+ checkField(localNames, field);
+ }
+ for (JMethod method : type.getMethods()) {
+ checkMethod(localNames, method);
}
}
@@ -540,22 +517,22 @@
|| isUnusableByJsSuppressed(method)) {
return;
}
- String methodName = method.getQualifiedName();
// check parameters.
for (JParameter parameter : method.getParams()) {
if (!parameter.getType().canBeReferencedExternally()
&& !isUnusableByJsSuppressed(parameter)) {
logWarning(
- "[unusable-by-js] Type of parameter '%s' in method '%s' is not usable by but exposed to"
- + " JavaScript",
- parameter.getName(), methodName);
+ parameter,
+ "[unusable-by-js] Type of parameter '%s' in method %s is not usable by but exposed to"
+ + " JavaScript.",
+ parameter.getName(), getMemberDescription(method));
}
}
// check return type.
if (!method.getType().canBeReferencedExternally()) {
logWarning(
- "[unusable-by-js] Return type of '%s' is not usable by but exposed to JavaScript",
- methodName);
+ method, "[unusable-by-js] Return type of %s is not usable by but exposed to JavaScript.",
+ getMemberDescription(method));
}
}
@@ -566,35 +543,183 @@
}
if (!field.getType().canBeReferencedExternally()) {
logWarning(
- "[unusable-by-js] Type of field '%s' in type '%s' is not usable by but exposed to "
- + "JavaScript",
- field.getName(), field.getEnclosingType().getName());
+ field, "[unusable-by-js] Type of field '%s' in type '%s' is not usable by but exposed to "
+ + "JavaScript.",
+ field.getName(), JjsUtils.getReadableDescription(field.getEnclosingType()));
}
}
+ private static class JsMember {
+ private JMember member;
+ private JMethod setter;
+ private JMethod getter;
+
+ public JsMember(JMember member) {
+ this.member = member;
+ }
+
+ public JsMember(JMethod member, JMethod setter, JMethod getter) {
+ this.member = member;
+ this.setter = setter;
+ this.getter = getter;
+ }
+
+ public boolean isNativeMethod() {
+ return member instanceof JMethod && member.isJsNative() && !isPropertyAccessor();
+ }
+
+ public boolean isPropertyAccessor() {
+ return setter != null || getter != null;
+ }
+ }
+
+ private LinkedHashMap<String, JsMember> collectNames(JDeclaredType type) {
+ if (type == null) {
+ return Maps.newLinkedHashMap();
+ }
+
+ LinkedHashMap<String, JsMember> memberByLocalMemberNames = collectNames(type.getSuperClass());
+
+ for (JField field : type.getFields()) {
+ if (!field.isJsProperty() || !field.needsDynamicDispatch()) {
+ continue;
+ }
+ updateJsMembers(memberByLocalMemberNames, field);
+ }
+ for (JMethod method : type.getMethods()) {
+ if (!method.isOrOverridesJsMethod() || !method.needsDynamicDispatch()
+ || isSyntheticBridgeMethod(method)) {
+ continue;
+ }
+ updateJsMembers(memberByLocalMemberNames, method);
+ }
+ return memberByLocalMemberNames;
+ }
+
+ private boolean isSyntheticBridgeMethod(JMethod method) {
+ // A name slot taken up by a synthetic method, such as a bridge method for a generic method,
+ // is not the fault of the user and so should not be reported as an error. JS generation
+ // should take responsibility for ensuring that only the correct method version (in this
+ // particular set of colliding method names) is exported. Forwarding synthetic methods
+ // (such as an accidental override forwarding method that occurs when a JsType interface
+ // starts exposing a method in class B that is only ever implemented in its parent class A)
+ // though should be checked since they are exported and do take up an name slot.
+ return method.isSynthetic() && !method.isForwarding();
+ }
+
+ private void updateJsMembers(Map<String, JsMember> memberByLocalMemberNames, JMember member) {
+ JsMember oldJsMember = memberByLocalMemberNames.get(member.getJsName());
+ JsMember updatedJsMember = createOrUpdateJsMember(oldJsMember, member);
+ memberByLocalMemberNames.put(member.getJsName(), updatedJsMember);
+ }
+
+ private JsMember createOrUpdateJsMember(JsMember jsMember, JMember member) {
+ if (member instanceof JField) {
+ return new JsMember(member);
+ }
+
+ JMethod method = (JMethod) member;
+ switch (method.getJsPropertyAccessorType()) {
+ case GETTER:
+ if (jsMember != null && jsMember.isPropertyAccessor()) {
+ if (jsMember.getter == null || overrides(method, jsMember.getter)) {
+ jsMember.getter = method;
+ jsMember.member = method;
+ return jsMember;
+ }
+ }
+ return new JsMember(method, jsMember == null ? null : jsMember.setter, method);
+ case SETTER:
+ if (jsMember != null && jsMember.isPropertyAccessor()) {
+ if (jsMember.setter == null || overrides(method, jsMember.setter)) {
+ jsMember.setter = method;
+ jsMember.member = method;
+ return jsMember;
+ }
+ }
+ return new JsMember(method, method, jsMember == null ? null : jsMember.getter);
+ default:
+ if (jsMember != null) {
+ if (overrides(method, jsMember.member)) {
+ jsMember.member = method;
+ return jsMember;
+ }
+ }
+ return new JsMember(method);
+ }
+ }
+
+ private boolean overrides(JMethod method, JMember potentiallyOverriddenMember) {
+ if (potentiallyOverriddenMember instanceof JField) {
+ return false;
+ }
+ if (method.getOverriddenMethods().contains(potentiallyOverriddenMember)) {
+ return true;
+ }
+
+ // Consider methods that have the same name and parameter signature to be overrides.
+ // GWT models overrides similar to the JVM (not Java) in the sense that for a method to override
+ // another they must have identical signatures (includes parameters and return type).
+ // Methods that only differ in return types are Java overrides and need to be considered so
+ // for local name collision checking.
+ JMethod potentiallyOverriddenMethod = (JMethod) potentiallyOverriddenMember;
+ return method.getJsniSignature(false, false)
+ .equals(potentiallyOverriddenMethod.getJsniSignature(false, false));
+ }
+
+ private static String getMemberDescription(JMember member) {
+ if (member instanceof JField) {
+ return String.format("'%s'", JjsUtils.getReadableDescription(member));
+ }
+ JMethod method = (JMethod) member;
+ if ((method.isSyntheticAccidentalOverride() || method.isSynthetic())
+ // Some synthetic methods are created by JDT, it is not save to assume
+ // that they will always be overriding and crash the compiler.
+ && !method.getOverriddenMethods().isEmpty()) {
+ JMethod overridenMethod = method.getOverriddenMethods().iterator().next();
+ return String.format("'%s' (exposed by '%s')",
+ JjsUtils.getReadableDescription(overridenMethod),
+ JjsUtils.getReadableDescription(method.getEnclosingType()));
+ }
+ return String.format("'%s'", JjsUtils.getReadableDescription(method));
+ }
+
private boolean isUnusableByJsSuppressed(CanHaveSuppressedWarnings x) {
return x.getSuppressedWarnings() != null &&
x.getSuppressedWarnings().contains(JsInteropUtil.UNUSABLE_BY_JS);
}
private void logError(String format, JType type) {
- logError(format, type.getName());
+ logError(type, format, JjsUtils.getReadableDescription(type));
}
- private void logError(String format, JType type, Set<String> subTypes) {
- StringBuilder subTypeNames = new StringBuilder();
- for (String typeName : subTypes) {
- subTypeNames.append("\n\t").append(typeName);
+ private void logError(HasSourceInfo hasSourceInfo, String format, Object... args) {
+ errorsByFilename.put(hasSourceInfo.getSourceInfo().getFileName(),
+ String.format("Line %d: ", hasSourceInfo.getSourceInfo().getStartLine())
+ + String.format(format, args));
+ }
+
+ private void logWarning(HasSourceInfo hasSourceInfo, String format, Object... args) {
+ warningsByFilename.put(hasSourceInfo.getSourceInfo().getFileName(),
+ String.format("Line %d: ", hasSourceInfo.getSourceInfo().getStartLine())
+ + String.format(format, args));
+ }
+
+ private boolean reportErrorsAndWarnings(TreeLogger logger) {
+ TreeSet<String> filenamesToReport = Sets.newTreeSet(
+ Iterables.concat(errorsByFilename.keySet(), warningsByFilename.keySet()));
+ for (String fileName : filenamesToReport) {
+ boolean hasErrors = !errorsByFilename.get(fileName).isEmpty();
+ TreeLogger branch = logger.branch(
+ hasErrors ? Type.ERROR : Type.WARN,
+ (hasErrors ? "Errors" : "Warnings") + " in " + fileName);
+ for (String message : errorsByFilename.get(fileName)) {
+ branch.log(Type.ERROR, message);
+ }
+ for (String message :warningsByFilename.get(fileName)) {
+ branch.log(Type.WARN, message);
+ }
}
- logError(format, type.getName(), subTypeNames);
- }
-
- private void logError(String format, Object... args) {
- logger.log(TreeLogger.ERROR, String.format(format, args));
- hasErrors = true;
- }
-
- private void logWarning(String format, Object... args) {
- logger.log(TreeLogger.WARN, String.format(format, args));
+ return !errorsByFilename.isEmpty();
}
}
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/JsInteropRestrictionCheckerTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/JsInteropRestrictionCheckerTest.java
index a345792..259daaa 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/impl/JsInteropRestrictionCheckerTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/JsInteropRestrictionCheckerTest.java
@@ -16,11 +16,14 @@
package com.google.gwt.dev.jjs.impl;
import com.google.gwt.core.ext.TreeLogger;
-import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.MinimalRebuildCache;
import com.google.gwt.dev.javac.testing.impl.MockJavaResource;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.thirdparty.guava.common.collect.Lists;
+
+import java.util.Arrays;
+import java.util.List;
/**
* Tests for the JsInteropRestrictionChecker.
@@ -47,8 +50,10 @@
"}");
assertBuggyFails(
- "'test.EntryPoint$Buggy.doIt(Ltest/EntryPoint$Bar;)V' can't be exported in type "
- + "'test.EntryPoint$Buggy' because the name 'doIt' is already taken.");
+ "Line 14: 'void EntryPoint.ParentBuggy.doIt(EntryPoint.Bar)' "
+ + "(exposed by 'EntryPoint.Buggy') and "
+ + "'void EntryPoint.ParentBuggy.doIt(EntryPoint.Foo)' (exposed by 'EntryPoint.Buggy') "
+ + "cannot both use the same JavaScript name 'doIt'.");
}
public void testCollidingAccidentalOverrideAbstractMethodFails() throws Exception {
@@ -69,8 +74,9 @@
"public static class Buggy {} // Unrelated class");
assertBuggyFails(
- "'test.EntryPoint$Baz.doIt(Ltest/EntryPoint$Bar;)V' can't be exported in type "
- + "'test.EntryPoint$Baz' because the name 'doIt' is already taken.");
+ "Line 14: 'void EntryPoint.Baz.doIt(EntryPoint.Bar)' and "
+ + "'void EntryPoint.Baz.doIt(EntryPoint.Foo)' cannot both use the same "
+ + "JavaScript name 'doIt'.");
}
public void testCollidingAccidentalOverrideHalfAndHalfFails() throws Exception {
@@ -93,14 +99,15 @@
"public static class Buggy extends Parent implements Bar {}");
assertBuggyFails(
- "'test.EntryPoint$Parent.doIt(Ltest/EntryPoint$Foo;)V' can't be exported in type "
- + "'test.EntryPoint$Buggy' because the name 'doIt' is already taken.");
+ "Line 12: 'void EntryPoint.ParentParent.doIt(EntryPoint.Bar)' "
+ + "(exposed by 'EntryPoint.Buggy') and 'void EntryPoint.Parent.doIt(EntryPoint.Foo)' "
+ + "cannot both use the same JavaScript name 'doIt'.");
}
public void testCollidingFieldExportsFails() throws Exception {
addSnippetImport("jsinterop.annotations.JsProperty");
addSnippetClassDecl(
- "public static class Buggy {",
+ "public static class Buggy {",
" @JsProperty",
" public static final int show = 0;",
" @JsProperty(name = \"show\")",
@@ -108,8 +115,8 @@
"}");
assertBuggyFails(
- "'test.EntryPoint$Buggy.display' can't be exported because the "
- + "global name 'test.EntryPoint.Buggy.show' is already taken.");
+ "Line 8: 'int EntryPoint.Buggy.display' cannot be exported because the global "
+ + "name 'test.EntryPoint.Buggy.show' is already taken.");
}
public void testJsPropertyGetterStyleSucceeds() throws Exception {
@@ -142,18 +149,20 @@
"}");
assertBuggyFails(
- "There can't be non-boolean return for the JsProperty 'is' getter"
- + " 'test.EntryPoint$Buggy.isX()I'.",
- "There can't be void return type or any parameters for the JsProperty getter"
- + " 'test.EntryPoint$Buggy.getY(I)I'.",
- "There can't be void return type or any parameters for the JsProperty getter"
- + " 'test.EntryPoint$Buggy.getZ()V'.",
- "There needs to be single parameter and void return type for the JsProperty setter"
- + " 'test.EntryPoint$Buggy.setX(II)V'.",
- "There needs to be single parameter and void return type for the JsProperty setter"
- + " 'test.EntryPoint$Buggy.setY()V'.",
- "There needs to be single parameter and void return type for the JsProperty setter"
- + " 'test.EntryPoint$Buggy.setZ(I)I'.");
+ "Line 10: There needs to be single parameter and void return type for the JsProperty "
+ + "setter 'void EntryPoint.Buggy.setX(int, int)'.",
+ "Line 8: There cannot be void return type or any parameters for the JsProperty getter "
+ + "'int EntryPoint.Buggy.getY(int)'.",
+ "Line 9: There cannot be void return type or any parameters for the JsProperty getter "
+ + "'void EntryPoint.Buggy.getZ()'.",
+ "Line 7: There cannot be non-boolean return for the JsProperty 'is' "
+ + "getter 'int EntryPoint.Buggy.isX()'.",
+ "Line 11: There needs to be single parameter and void return type for the JsProperty "
+ + "setter 'void EntryPoint.Buggy.setY()'.",
+ "Line 12: There needs to be single parameter and void return type for the JsProperty "
+ + "setter 'int EntryPoint.Buggy.setZ(int)'.",
+ "Line 12: The setter and getter for JsProperty 'z' in type 'EntryPoint.Buggy' must "
+ + "have consistent types.");
}
public void testJsPropertyNonGetterStyleFails() throws Exception {
@@ -168,9 +177,12 @@
"}");
assertBuggyFails(
- "JsProperty 'test.EntryPoint$Buggy.hasX()Z' doesn't follow Java Bean naming conventions.",
- "JsProperty 'test.EntryPoint$Buggy.x()I' doesn't follow Java Bean naming conventions.",
- "JsProperty 'test.EntryPoint$Buggy.x(I)V' doesn't follow Java Bean naming conventions.");
+ "Line 7: JsProperty 'boolean EntryPoint.Buggy.hasX()' doesn't follow Java Bean "
+ + "naming conventions.",
+ "Line 8: JsProperty 'int EntryPoint.Buggy.x()' doesn't follow Java Bean naming"
+ + " conventions.",
+ "Line 9: JsProperty 'void EntryPoint.Buggy.x(int)' doesn't follow Java Bean naming "
+ + "conventions.");
}
public void testCollidingJsPropertiesTwoGettersFails() throws Exception {
@@ -178,20 +190,16 @@
addSnippetImport("jsinterop.annotations.JsProperty");
addSnippetClassDecl(
"@JsType",
- "public static interface IBuggy {",
+ "public static interface Buggy {",
" @JsProperty",
" boolean isX();",
" @JsProperty",
" boolean getX();",
- "}",
- "public static class Buggy implements IBuggy {",
- " public boolean isX() {return false;}",
- " public boolean getX() {return false;}",
"}");
assertBuggyFails(
- "There can't be more than one getter for JsProperty 'x' in type 'test.EntryPoint$IBuggy'.",
- "There can't be more than one getter for JsProperty 'x' in type 'test.EntryPoint$Buggy'.");
+ "Line 10: 'boolean EntryPoint.Buggy.getX()' and 'boolean EntryPoint.Buggy.isX()' "
+ + "cannot both use the same JavaScript name 'x'.");
}
public void testCollidingJsPropertiesTwoSettersFails() throws Exception {
@@ -199,23 +207,18 @@
addSnippetImport("jsinterop.annotations.JsProperty");
addSnippetClassDecl(
"@JsType",
- "public static interface IBuggy {",
+ "public static interface Buggy {",
" @JsProperty",
" void setX(boolean x);",
" @JsProperty",
" void setX(int x);",
- "}",
- "public static class Buggy implements IBuggy {",
- " public void setX(boolean x) {}",
- " public void setX(int x) {}",
"}");
assertBuggyFails(
- "There can't be more than one setter for JsProperty 'x' in type 'test.EntryPoint$IBuggy'.",
- "There can't be more than one setter for JsProperty 'x' in type 'test.EntryPoint$Buggy'.");
+ "Line 10: 'void EntryPoint.Buggy.setX(int)' and 'void EntryPoint.Buggy.setX(boolean)' "
+ + "cannot both use the same JavaScript name 'x'.");
}
- // TODO: duplicate this check with two @JsType interfaces.
public void testCollidingJsTypeAndJsPropertyGetterFails() throws Exception {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetImport("jsinterop.annotations.JsProperty");
@@ -232,10 +235,10 @@
"}");
assertBuggyFails(
- "'test.EntryPoint$IBuggy.x(Z)Z' and 'test.EntryPoint$IBuggy.getX()I' "
- + "can't both be named 'x' in type 'test.EntryPoint$IBuggy'.",
- "'test.EntryPoint$Buggy.x(Z)Z' and 'test.EntryPoint$Buggy.getX()I' "
- + "can't both be named 'x' in type 'test.EntryPoint$Buggy'.");
+ "Line 9: 'int EntryPoint.IBuggy.getX()' and 'boolean EntryPoint.IBuggy.x(boolean)' "
+ + "cannot both use the same JavaScript name 'x'.",
+ "Line 13: 'int EntryPoint.Buggy.getX()' and 'boolean EntryPoint.Buggy.x(boolean)' "
+ + "cannot both use the same JavaScript name 'x'.");
}
public void testCollidingJsTypeAndJsPropertySetterFails() throws Exception {
@@ -254,10 +257,10 @@
"}");
assertBuggyFails(
- "'test.EntryPoint$IBuggy.x(Z)Z' and 'test.EntryPoint$IBuggy.setX(I)V' "
- + "can't both be named 'x' in type 'test.EntryPoint$IBuggy'.",
- "'test.EntryPoint$Buggy.x(Z)Z' and 'test.EntryPoint$Buggy.setX(I)V' "
- + "can't both be named 'x' in type 'test.EntryPoint$Buggy'.");
+ "Line 9: 'void EntryPoint.IBuggy.setX(int)' and 'boolean EntryPoint.IBuggy.x(boolean)' "
+ + "cannot both use the same JavaScript name 'x'.",
+ "Line 13: 'void EntryPoint.Buggy.setX(int)' and 'boolean EntryPoint.Buggy.x(boolean)' "
+ + "cannot both use the same JavaScript name 'x'.");
}
public void testCollidingMethodExportsFails() throws Exception {
@@ -271,8 +274,8 @@
"}");
assertBuggyFails(
- "'test.EntryPoint$Buggy.display()V' can't be exported "
- + "because the global name 'test.EntryPoint.Buggy.show' is already taken.");
+ "Line 8: 'void EntryPoint.Buggy.display()' cannot be exported because the global name "
+ + "'test.EntryPoint.Buggy.show' is already taken.");
}
public void testCollidingMethodToFieldExportsFails() throws Exception {
@@ -287,8 +290,8 @@
"}");
assertBuggyFails(
- "'test.EntryPoint$Buggy.show()V' can't be exported because the "
- + "global name 'test.EntryPoint.Buggy.show' is already taken.");
+ "Line 7: 'void EntryPoint.Buggy.show()' cannot be exported because the global name "
+ + "'test.EntryPoint.Buggy.show' is already taken.");
}
public void testCollidingMethodToFieldJsTypeFails() throws Exception {
@@ -301,8 +304,8 @@
"}");
assertBuggyFails(
- "'test.EntryPoint$Buggy.show()V' can't be exported in type "
- + "'test.EntryPoint$Buggy' because the name 'show' is already taken.");
+ "Line 6: 'void EntryPoint.Buggy.show()' and 'int EntryPoint.Buggy.show' cannot both use "
+ + "the same JavaScript name 'show'.");
}
public void testCollidingMethodToMethodJsTypeFails() throws Exception {
@@ -315,8 +318,8 @@
"}");
assertBuggyFails(
- "'test.EntryPoint$Buggy.show()V' can't be exported in type "
- + "'test.EntryPoint$Buggy' because the name 'show' is already taken.");
+ "Line 7: 'void EntryPoint.Buggy.show()' and 'void EntryPoint.Buggy.show(int)' cannot both "
+ + "use the same JavaScript name 'show'.");
}
public void testCollidingSubclassExportedFieldToFieldJsTypeSucceeds() throws Exception {
@@ -402,8 +405,8 @@
"}");
assertBuggyFails(
- "'test.EntryPoint$ParentBuggy.foo' can't be exported in type "
- + "'test.EntryPoint$Buggy' because the name 'foo' is already taken.");
+ "Line 10: 'int EntryPoint.Buggy.foo' and 'int EntryPoint.ParentBuggy.foo' cannot both use "
+ + "the same JavaScript name 'foo'.");
}
public void testCollidingSubclassFieldToMethodJsTypeFails() throws Exception {
@@ -419,8 +422,8 @@
"}");
assertBuggyFails(
- "'test.EntryPoint$ParentBuggy.foo' can't be exported in type "
- + "'test.EntryPoint$Buggy' because the name 'foo' is already taken.");
+ "Line 10: 'void EntryPoint.Buggy.foo(int)' and 'int EntryPoint.ParentBuggy.foo' cannot "
+ + "both use the same JavaScript name 'foo'.");
}
public void testCollidingSubclassMethodToExportedMethodJsTypeSucceeds() throws Exception {
@@ -456,8 +459,8 @@
"}");
assertBuggyFails(
- "'test.EntryPoint$Buggy.show()V' can't be exported in type "
- + "'test.EntryPoint$Buggy2' because the name 'show' is already taken.");
+ "Line 16: 'void EntryPoint.Buggy2.show(boolean)' and 'void EntryPoint.Buggy.show()' cannot "
+ + "both use the same JavaScript name 'show'.");
}
public void testCollidingSubclassMethodToMethodJsTypeFails() throws Exception {
@@ -473,8 +476,8 @@
"}");
assertBuggyFails(
- "'test.EntryPoint$ParentBuggy.foo()V' can't be exported in type "
- + "'test.EntryPoint$Buggy' because the name 'foo' is already taken.");
+ "Line 10: 'void EntryPoint.Buggy.foo(int)' and 'void EntryPoint.ParentBuggy.foo()' "
+ + "cannot both use the same JavaScript name 'foo'.");
}
public void testCollidingSubclassMethodToMethodTwoLayerInterfaceJsTypeFails() throws Exception {
@@ -500,11 +503,11 @@
"}");
assertBuggyFails(
- "'test.EntryPoint$Buggy.show()V' can't be exported in type "
- + "'test.EntryPoint$Buggy2' because the name 'show' is already taken.");
+ "Line 20: 'void EntryPoint.Buggy2.show(boolean)' and 'void EntryPoint.Buggy.show()' "
+ + "cannot both use the same JavaScript name 'show'.");
}
- public void testCollidingSyntheticBridgeMethodSucceeds() throws Exception {
+ public void testNonCollidingSyntheticBridgeMethodSucceeds() throws Exception {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetImport("jsinterop.annotations.JsProperty");
addSnippetClassDecl(
@@ -520,6 +523,52 @@
assertBuggySucceeds();
}
+ public void testCollidingSyntheticBridgeMethodSucceeds() throws Exception {
+ addSnippetImport("jsinterop.annotations.JsType");
+ addSnippetClassDecl(
+ "@JsType",
+ "public static interface Comparable<T> {",
+ " int compareTo(T other);",
+ "}",
+ "@JsType",
+ "public static class Enum<E extends Enum<E>> implements Comparable<E> {",
+ " public int compareTo(E other) {return 0;}",
+ "}",
+ "public static class Buggy {}");
+
+ assertBuggySucceeds();
+ }
+
+ public void testSpecializeReturnTypeInImplementorSucceeds() throws Exception {
+ addSnippetImport("jsinterop.annotations.JsType");
+ addSnippetClassDecl(
+ "@JsType",
+ "interface I {",
+ " I m();",
+ "}",
+ "@JsType",
+ "public static class Buggy implements I {",
+ " public Buggy m() { return null; } ",
+ "}");
+
+ assertBuggySucceeds();
+ }
+
+ public void testSpecializeReturnTypeInSubclassSucceeds() throws Exception {
+ addSnippetImport("jsinterop.annotations.JsType");
+ addSnippetClassDecl(
+ "@JsType",
+ "public static class S {",
+ " public S m() { return null; }",
+ "}",
+ "@JsType",
+ "public static class Buggy extends S {",
+ " public Buggy m() { return null; } ",
+ "}");
+
+ assertBuggySucceeds();
+ }
+
public void testCollidingTwoLayerSubclassFieldToFieldJsTypeFails() throws Exception {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetClassDecl(
@@ -536,8 +585,93 @@
"}");
assertBuggyFails(
- "'test.EntryPoint$ParentParentBuggy.foo' can't be exported in type "
- + "'test.EntryPoint$Buggy' because the name 'foo' is already taken.");
+ "Line 13: 'int EntryPoint.Buggy.foo' and 'int EntryPoint.ParentParentBuggy.foo' cannot "
+ + "both use the same JavaScript name 'foo'.");
+ }
+
+ public void testRenamedSuperclassJsMethodFails() {
+ addSnippetImport("jsinterop.annotations.JsType");
+ addSnippetImport("jsinterop.annotations.JsMethod");
+ addSnippetClassDecl(
+ "@JsType",
+ "public static class ParentBuggy {",
+ " public void foo() {}",
+ "}",
+ "public static class Buggy extends ParentBuggy {",
+ " @JsMethod(name = \"bar\") public void foo() {}",
+ "}");
+
+ assertBuggyFails("Line 10: 'void EntryPoint.Buggy.foo()' cannot be assigned a different "
+ + "JavaScript name than the method it overrides.");
+ }
+
+ public void testRenamedSuperInterfaceJsMethodFails() {
+ addSnippetImport("jsinterop.annotations.JsType");
+ addSnippetImport("jsinterop.annotations.JsMethod");
+ addSnippetClassDecl(
+ "@JsType",
+ "public interface ParentBuggy {",
+ " void foo();;",
+ "}",
+ "public interface Buggy extends ParentBuggy {",
+ " @JsMethod(name = \"bar\") void foo();",
+ "}");
+
+ assertBuggyFails("Line 10: 'void EntryPoint.Buggy.foo()' cannot be assigned a different "
+ + "JavaScript name than the method it overrides.");
+ }
+
+ public void testAccidentallyRenamedSuperInterfaceJsMethodFails() {
+ addSnippetImport("jsinterop.annotations.JsType");
+ addSnippetImport("jsinterop.annotations.JsMethod");
+ addSnippetClassDecl(
+ "@JsType",
+ "public interface IBuggy {",
+ " void foo();",
+ "}",
+ "@JsType",
+ "public static class ParentBuggy {",
+ " @JsMethod(name = \"bar\") public void foo() {}",
+ "}",
+ "public static class Buggy extends ParentBuggy implements IBuggy {",
+ "}");
+
+ assertBuggyFails("Line 11: 'void EntryPoint.ParentBuggy.foo()' (exposed by 'EntryPoint.Buggy') "
+ + "cannot be assigned a different JavaScript name than the method it overrides.");
+ }
+
+ // TODO(goktug): enable once the property names are handled.
+ public void __disabled__testRenamedSuperclassJsPropertyFails() {
+ addSnippetImport("jsinterop.annotations.JsType");
+ addSnippetImport("jsinterop.annotations.JsProperty");
+ addSnippetClassDecl(
+ "@JsType",
+ "public static class ParentBuggy {",
+ " @JsProperty public int getFoo() { return 0; }",
+ "}",
+ "public static class Buggy extends ParentBuggy {",
+ " @JsProperty(name = \"bar\") public int getFoo() { return 0;}",
+ "}");
+
+ assertBuggyFails("'EntryPoint.Buggy.getFoo()I' cannot be exported because the method "
+ + "overrides a method with different name.");
+ }
+
+ public void testJsPropertyDifferentFlavourInSubclassFails() {
+ addSnippetImport("jsinterop.annotations.JsType");
+ addSnippetImport("jsinterop.annotations.JsProperty");
+ addSnippetClassDecl(
+ "@JsType",
+ "public static class ParentBuggy {",
+ " @JsProperty public boolean isFoo() { return false; }",
+ "}",
+ "public static class Buggy extends ParentBuggy {",
+ " @JsProperty public boolean getFoo() { return false;}",
+ "}");
+
+ assertBuggyFails(
+ "Line 10: 'boolean EntryPoint.Buggy.getFoo()' and 'boolean EntryPoint.ParentBuggy.isFoo()' "
+ + "cannot both use the same JavaScript name 'foo'.");
}
public void testConsistentPropertyTypeSucceeds() throws Exception {
@@ -576,9 +710,9 @@
"}");
assertBuggyFails(
- "The setter and getter for JsProperty 'foo' in type 'test.EntryPoint$IBuggy' "
+ "Line 10: The setter and getter for JsProperty 'foo' in type 'EntryPoint.IBuggy' "
+ "must have consistent types.",
- "The setter and getter for JsProperty 'foo' in type 'test.EntryPoint$Buggy' "
+ "Line 14: The setter and getter for JsProperty 'foo' in type 'EntryPoint.Buggy' "
+ "must have consistent types.");
}
@@ -599,14 +733,13 @@
"}");
assertBuggyFails(
- "The setter and getter for JsProperty 'foo' in type 'test.EntryPoint$IBuggy' "
+ "Line 10: The setter and getter for JsProperty 'foo' in type 'EntryPoint.IBuggy' "
+ "must have consistent types.",
- "The setter and getter for JsProperty 'foo' in type 'test.EntryPoint$Buggy' "
+ "Line 14: The setter and getter for JsProperty 'foo' in type 'EntryPoint.Buggy' "
+ "must have consistent types.");
}
- public void testJsPropertySuperCallFails()
- throws UnableToCompleteException {
+ public void testJsPropertySuperCallFails() throws Exception {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetImport("jsinterop.annotations.JsProperty");
addSnippetClassDecl(
@@ -618,12 +751,10 @@
"}");
assertBuggyFails(
- "Cannot call property accessor 'test.EntryPoint$Super.getX()I' via super "
- + "(test/EntryPoint.java:9).");
+ "Line 9: Cannot call property accessor 'int EntryPoint.Super.getX()' via super.");
}
- public void testJsPropertyCallSucceeds()
- throws UnableToCompleteException {
+ public void testJsPropertyCallSucceeds() throws Exception {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetImport("jsinterop.annotations.JsProperty");
addSnippetClassDecl(
@@ -638,7 +769,7 @@
}
public void testJsPropertyAccidentalSuperCallSucceeds()
- throws UnableToCompleteException {
+ throws Exception {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetImport("jsinterop.annotations.JsProperty");
addSnippetClassDecl(
@@ -655,6 +786,23 @@
assertBuggySucceeds();
}
+ public void testJsPropertyOverrideSucceeds()
+ throws Exception {
+ addSnippetImport("jsinterop.annotations.JsType");
+ addSnippetImport("jsinterop.annotations.JsProperty");
+ addSnippetClassDecl(
+ "@JsType public static class Super {",
+ " @JsProperty public void setX(int x) { }",
+ " @JsProperty public int getX() { return 5; }",
+ "}",
+
+ "@JsType public static class Buggy extends Super {",
+ " @JsProperty public void setX(int x) { }",
+ "}");
+
+ assertBuggySucceeds();
+ }
+
public void testMultiplePrivateConstructorsExportSucceeds() throws Exception {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetClassDecl(
@@ -667,8 +815,7 @@
assertBuggySucceeds();
}
- public void testMultiplePublicConstructorsAllDelegatesToJsConstructorSucceeds()
- throws Exception {
+ public void testMultiplePublicConstructorsAllDelegatesToJsConstructorSucceeds() throws Exception {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetImport("jsinterop.annotations.JsIgnore");
addSnippetClassDecl(
@@ -697,8 +844,8 @@
"}");
assertBuggyFails(
- "Constructor 'test.EntryPoint$Buggy.EntryPoint$Buggy() <init>' can be a JsConstructor only "
- + "if all constructors in the class are delegating to it.");
+ "Line 6: Constructor 'EntryPoint.Buggy.EntryPoint$Buggy()' can be a JsConstructor only if "
+ + "all constructors in the class are delegating to it.");
}
public void testMultiplePublicConstructorsExportFails() throws Exception {
@@ -713,9 +860,9 @@
"}");
assertBuggyFails(
- "More than one JsConstructor exists for test.EntryPoint$Buggy.",
- "'test.EntryPoint$Buggy.EntryPoint$Buggy(I) <init>' can't be "
- + "exported because the global name 'test.EntryPoint.Buggy' is already taken.");
+ "Line 5: More than one JsConstructor exists for EntryPoint.Buggy.",
+ "Line 7: 'EntryPoint.Buggy.EntryPoint$Buggy(int)' cannot be exported because the global "
+ + "name 'test.EntryPoint.Buggy' is already taken.");
}
public void testNonCollidingAccidentalOverrideSucceeds() throws Exception {
@@ -768,7 +915,7 @@
" void foo();",
"}");
- assertBuggyFails("JsFunction 'test.EntryPoint$Buggy' cannot extend other interfaces.");
+ assertBuggyFails("Line 6: JsFunction 'EntryPoint.Buggy' cannot extend other interfaces.");
}
public void testJsFunctionExtendedByInterfaceFails() throws Exception {
@@ -776,8 +923,8 @@
addSnippetClassDecl("public interface Buggy extends MyJsFunctionInterface {}");
- assertBuggyFails("JsFunction 'test.MyJsFunctionInterface' cannot be extended by other "
- + "interfaces:\n\ttest.EntryPoint$Buggy");
+ assertBuggyFails(
+ "Line 3: 'EntryPoint.Buggy' cannot extend JsFunction 'MyJsFunctionInterface'.");
}
public void testJsFunctionMarkedAsJsTypeFails() throws Exception {
@@ -790,7 +937,7 @@
"}");
assertBuggyFails(
- "'test.EntryPoint$Buggy' cannot be both a JsFunction and a JsType at the same time.");
+ "Line 6: 'EntryPoint.Buggy' cannot be both a JsFunction and a JsType at the same time.");
}
public void testJsFunctionImplementationWithSingleInterfaceSucceeds() throws Exception {
@@ -812,8 +959,8 @@
" public int bar(int x) { return 0; }",
"}");
- assertBuggyFails("JsFunction implementation 'test.EntryPoint$Buggy' cannot implement more than "
- + "one interface.");
+ assertBuggyFails("Line 4: JsFunction implementation 'EntryPoint.Buggy' cannot "
+ + "implement more than one interface.");
}
public void testJsFunctionImplementationWithSuperClassFails() throws Exception {
@@ -824,7 +971,8 @@
" public int foo(int x) { return 0; }",
"}");
- assertBuggyFails("JsFunction implementation 'test.EntryPoint$Buggy' cannot extend a class.");
+ assertBuggyFails("Line 4: JsFunction implementation 'EntryPoint.Buggy' cannot "
+ + "extend a class.");
}
public void testJsFunctionImplementationWithSubclassesFails() throws Exception {
@@ -836,8 +984,8 @@
"public static class Buggy extends BaseClass {",
"}");
- assertBuggyFails("Implementation of JsFunction 'test.EntryPoint$BaseClass' cannot be extended "
- + "by other classes:\n\ttest.EntryPoint$Buggy");
+ assertBuggyFails("Line 6: 'EntryPoint.Buggy' cannot extend "
+ + "JsFunction implementation 'EntryPoint.BaseClass'.");
}
public void testJsFunctionImplementationMarkedAsJsTypeFails() throws Exception {
@@ -850,8 +998,8 @@
"}");
assertBuggyFails(
- "'test.EntryPoint$Buggy' cannot be both a JsFunction implementation and a JsType at the "
- + "same time.");
+ "Line 5: 'EntryPoint.Buggy' cannot be both a JsFunction implementation and a JsType "
+ + "at the same time.");
}
public void testJsFunctionStaticInitializerFails() {
@@ -865,7 +1013,7 @@
"}");
assertBuggyFails(
- "JsFunction 'test.EntryPoint$Buggy' cannot have static initializer.");
+ "Line 6: JsFunction 'EntryPoint.Buggy' cannot have static initializer.");
}
public void testNativeJsTypeStaticInitializerFails() {
@@ -877,7 +1025,7 @@
"}");
assertBuggyFails(
- "Native JsType 'test.EntryPoint$Buggy' cannot have static initializer.");
+ "Line 4: Native JsType 'EntryPoint.Buggy' cannot have static initializer.");
}
public void testNativeJsTypeNonEmptyConstructorFails() {
@@ -890,8 +1038,8 @@
"}");
assertBuggyFails(
- "Native JsType constructor 'test.EntryPoint$Buggy.EntryPoint$Buggy(I) <init>' "
- + "cannot have non-empty method body.");
+ "Line 5: Native JsType constructor 'EntryPoint.Buggy.EntryPoint$Buggy(int)' cannot have "
+ + "non-empty method body.");
}
public void testNativeJsTypeInstanceInitializerFails() {
@@ -904,11 +1052,11 @@
"}");
assertBuggyFails(
- "Native JsType constructor 'test.EntryPoint$Buggy.EntryPoint$Buggy(I) <init>' "
+ "Line 6: Native JsType constructor 'EntryPoint.Buggy.EntryPoint$Buggy(int)' "
+ "cannot have non-empty method body.");
}
- public void testNativeJsTypeImplicitSuperSucceeds() throws UnableToCompleteException {
+ public void testNativeJsTypeImplicitSuperSucceeds() throws Exception {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetClassDecl(
"@JsType(isNative=true) public static class Super {",
@@ -923,7 +1071,7 @@
assertBuggySucceeds();
}
- public void testNativeJsTypeExplicitSuperSucceeds() throws UnableToCompleteException {
+ public void testNativeJsTypeExplicitSuperSucceeds() throws Exception {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetClassDecl(
"@JsType(isNative=true) public static class Super {",
@@ -939,7 +1087,7 @@
assertBuggySucceeds();
}
- public void testNativeJsTypeExplicitSuperWithEffectSucceeds() throws UnableToCompleteException {
+ public void testNativeJsTypeExplicitSuperWithEffectSucceeds() throws Exception {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetClassDecl(
"@JsType(isNative=true) public static class Super {",
@@ -963,7 +1111,7 @@
"}");
assertBuggyFails(
- "Native JsType 'test.EntryPoint$Buggy' cannot have static initializer.");
+ "Line 4: Native JsType 'EntryPoint.Buggy' cannot have static initializer.");
}
public void testNativeJsTypeInterfaceInlineInitializerFails() {
@@ -974,10 +1122,10 @@
"}");
assertBuggyFails(
- "Native JsType 'test.EntryPoint$Buggy' cannot have static initializer.");
+ "Line 4: Native JsType 'EntryPoint.Buggy' cannot have static initializer.");
}
- public void testNativeJsTypeCompileTimeConstantSucceeds() throws UnableToCompleteException {
+ public void testNativeJsTypeCompileTimeConstantSucceeds() throws Exception {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetClassDecl(
"@JsType(isNative=true) public static class Buggy {",
@@ -995,8 +1143,8 @@
" public Buggy() { if (new Object() instanceof IBuggy) {} }",
"}");
- assertBuggyFails("Cannot do instanceof against native JsType interface test.EntryPoint$IBuggy "
- + "(test/EntryPoint.java:6).");
+ assertBuggyFails("Line 6: Cannot do instanceof against native JsType interface "
+ + "'EntryPoint.IBuggy'.");
}
public void testNativeJsTypeEnumFails() {
@@ -1005,11 +1153,10 @@
"@JsType(isNative=true) public enum Buggy { A, B }");
assertBuggyFails(
- "Enum 'test.EntryPoint$Buggy' cannot be a native JsType.");
+ "Line 4: Enum 'EntryPoint.Buggy' cannot be a native JsType.");
}
- public void testNativeJsTypeInterfaceCompileTimeConstantSucceeds()
- throws UnableToCompleteException {
+ public void testNativeJsTypeInterfaceCompileTimeConstantSucceeds() throws Exception {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetClassDecl(
"@JsType(isNative=true) public interface Buggy {",
@@ -1019,7 +1166,7 @@
assertBuggySucceeds();
}
- public void testNativeJsTypeExtendsNativeJsTypeSucceeds() throws UnableToCompleteException {
+ public void testNativeJsTypeExtendsNativeJsTypeSucceeds() throws Exception {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetClassDecl(
"@JsType(isNative=true) public static class Super {",
@@ -1030,7 +1177,7 @@
assertBuggySucceeds();
}
- public void testNativeJsTypeImplementsNativeJsTypeSucceeds() throws UnableToCompleteException {
+ public void testNativeJsTypeImplementsNativeJsTypeSucceeds() throws Exception {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetClassDecl(
"@JsType(isNative=true) public interface Super {",
@@ -1041,8 +1188,7 @@
assertBuggySucceeds();
}
- public void testNativeJsTypeInterfaceImplementsNativeJsTypeSucceeds()
- throws UnableToCompleteException {
+ public void testNativeJsTypeInterfaceImplementsNativeJsTypeSucceeds() throws Exception {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetClassDecl(
"@JsType(isNative=true) public interface Super {",
@@ -1053,7 +1199,7 @@
assertBuggySucceeds();
}
- public void testJsOverlayOnNativeJsTypeMemberSucceeds() throws UnableToCompleteException {
+ public void testJsOverlayOnNativeJsTypeMemberSucceeds() throws Exception {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetImport("jsinterop.annotations.JsOverlay");
addSnippetClassDecl(
@@ -1075,8 +1221,8 @@
" @JsOverlay public static final void m() { }",
"}");
- assertBuggyFails(
- "JsOverlay method 'test.EntryPoint$Buggy.m()V' cannot be non-final, static, nor native.");
+ assertBuggyFails("Line 6: JsOverlay method 'void EntryPoint.Buggy.m()' cannot be "
+ + "non-final, static, nor native.");
}
public void testJsOverlayImplementingInterfaceMethodFails() {
@@ -1090,8 +1236,8 @@
" @JsOverlay public void m() { }",
"}");
- assertBuggyFails(
- "JsOverlay method 'test.EntryPoint$Buggy.m()V' cannot override a supertype method.");
+ assertBuggyFails("Line 9: JsOverlay method 'void EntryPoint.Buggy.m()' cannot override a "
+ + "supertype method.");
}
public void testJsOverlayOverridingSuperclassMethodFails() {
@@ -1106,7 +1252,7 @@
"}");
assertBuggyFails(
- "JsOverlay method 'test.EntryPoint$Buggy.m()V' cannot override a supertype method.");
+ "Line 9: JsOverlay method 'void EntryPoint.Buggy.m()' cannot override a supertype method.");
}
public void testJsOverlayOnNonFinalFails() {
@@ -1118,7 +1264,7 @@
"}");
assertBuggyFails(
- "JsOverlay method 'test.EntryPoint$Buggy.m()V' cannot be non-final, static, nor native.");
+ "Line 6: JsOverlay method 'void EntryPoint.Buggy.m()' cannot be non-final, static, nor native.");
}
public void testJsOverlayOnNativeMethodFails() {
@@ -1130,10 +1276,10 @@
"}");
assertBuggyFails(
- "JsOverlay method 'test.EntryPoint$Buggy.m()V' cannot be non-final, static, nor native.");
+ "Line 6: JsOverlay method 'void EntryPoint.Buggy.m()' cannot be non-final, static, nor native.");
}
- public void testJsOverlayOnJsoMethodSucceeds() throws UnableToCompleteException {
+ public void testJsOverlayOnJsoMethodSucceeds() throws Exception {
addSnippetImport("com.google.gwt.core.client.JavaScriptObject");
addSnippetImport("jsinterop.annotations.JsOverlay");
addSnippetClassDecl(
@@ -1145,7 +1291,7 @@
assertBuggySucceeds();
}
- public void testImplicitJsOverlayOnJsoMethodSucceeds() throws UnableToCompleteException {
+ public void testImplicitJsOverlayOnJsoMethodSucceeds() throws Exception {
addSnippetImport("com.google.gwt.core.client.JavaScriptObject");
addSnippetImport("jsinterop.annotations.JsOverlay");
addSnippetClassDecl(
@@ -1166,7 +1312,7 @@
"}");
assertBuggyFails(
- "Method 'test.EntryPoint$Buggy.m()V' in non-native type cannot be @JsOverlay.");
+ "Line 6: Method 'void EntryPoint.Buggy.m()' in non-native type cannot be @JsOverlay.");
}
public void testJsOverlayOnNonJsTypeFails() {
@@ -1177,10 +1323,10 @@
"}");
assertBuggyFails(
- "Method 'test.EntryPoint$Buggy.m()V' in non-native type cannot be @JsOverlay.");
+ "Line 5: Method 'void EntryPoint.Buggy.m()' in non-native type cannot be @JsOverlay.");
}
- public void testJsTypeExtendsNativeJsTypeSucceeds() throws UnableToCompleteException {
+ public void testJsTypeExtendsNativeJsTypeSucceeds() throws Exception {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetClassDecl(
"@JsType(isNative=true) public static class Super {",
@@ -1191,7 +1337,7 @@
assertBuggySucceeds();
}
- public void testJsTypeExtendsNonJsTypeSucceeds() throws UnableToCompleteException {
+ public void testJsTypeExtendsNonJsTypeSucceeds() throws Exception {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetClassDecl(
"public static class Super {",
@@ -1202,7 +1348,7 @@
assertBuggySucceeds();
}
- public void testJsTypeImplementsNativeJsTypeInterfaceSucceeds() throws UnableToCompleteException {
+ public void testJsTypeImplementsNativeJsTypeInterfaceSucceeds() throws Exception {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetClassDecl(
"@JsType(isNative=true) public interface Interface {",
@@ -1213,7 +1359,7 @@
assertBuggySucceeds();
}
- public void testJsTypeImplementsNonJsTypeInterfaceSucceeds() throws UnableToCompleteException {
+ public void testJsTypeImplementsNonJsTypeInterfaceSucceeds() throws Exception {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetClassDecl(
"public interface Interface {",
@@ -1224,8 +1370,7 @@
assertBuggySucceeds();
}
- public void testJsTypeIntefaceExtendsNativeJsTypeInterfaceSucceeds()
- throws UnableToCompleteException {
+ public void testJsTypeIntefaceExtendsNativeJsTypeInterfaceSucceeds() throws Exception {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetClassDecl(
"@JsType(isNative=true) public interface Interface {",
@@ -1237,7 +1382,7 @@
}
public void testJsTypeInterfaceExtendsNonJsTypeInterfaceSucceeds()
- throws UnableToCompleteException {
+ throws Exception {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetClassDecl(
"public interface Interface {",
@@ -1248,7 +1393,7 @@
assertBuggySucceeds();
}
- public void testNativeJsTypeExtendsNaiveJsTypeSucceeds() throws UnableToCompleteException {
+ public void testNativeJsTypeExtendsNaiveJsTypeSucceeds() throws Exception {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetClassDecl(
"@JsType(isNative=true) public static class Super {",
@@ -1267,7 +1412,7 @@
"}");
assertBuggyFails(
- "Native JsType member 'test.EntryPoint$Buggy.f' is not public or has @JsIgnore.");
+ "Line 5: Native JsType member 'int EntryPoint.Buggy.f' is not public or has @JsIgnore.");
}
public void testNativeJsTypeJsIgnoredFieldFails() {
@@ -1279,7 +1424,7 @@
"}");
assertBuggyFails(
- "Native JsType member 'test.EntryPoint$Buggy.x' is not public or has @JsIgnore.");
+ "Line 6: Native JsType member 'int EntryPoint.Buggy.x' is not public or has @JsIgnore.");
}
public void testNativeJsTypeNonPublicMethodFails() {
@@ -1290,7 +1435,7 @@
"}");
assertBuggyFails(
- "Native JsType member 'test.EntryPoint$Buggy.m()V' is not public or has @JsIgnore.");
+ "Line 5: Native JsType member 'void EntryPoint.Buggy.m()' is not public or has @JsIgnore.");
}
public void testNativeJsTypeJsIgnoredMethodFails() {
@@ -1302,7 +1447,7 @@
"}");
assertBuggyFails(
- "Native JsType member 'test.EntryPoint$Buggy.m()V' is not public or has @JsIgnore.");
+ "Line 6: Native JsType member 'void EntryPoint.Buggy.m()' is not public or has @JsIgnore.");
}
public void testNativeJsTypeJsIgnoredConstructorFails() {
@@ -1314,11 +1459,22 @@
"}");
assertBuggyFails(
- "Native JsType member 'test.EntryPoint$Buggy.EntryPoint$Buggy() <init>' "
- + "is not public or has @JsIgnore.");
+ "Line 6: Native JsType member 'EntryPoint.Buggy.EntryPoint$Buggy()' is not public or "
+ + "has @JsIgnore.");
}
- public void testNativeJsTypeNonPublicConstructorSucceeds() throws UnableToCompleteException {
+ public void testNativeJsTypeMutlipleConstructorSucceeds() throws Exception {
+ addSnippetImport("jsinterop.annotations.JsType");
+ addSnippetClassDecl(
+ "@JsType(isNative=true) static class Buggy {",
+ " public Buggy(int i) { }",
+ " public Buggy() { }",
+ "}");
+
+ assertBuggySucceeds();
+ }
+
+ public void testNativeJsTypeNonPublicConstructorSucceeds() throws Exception {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetClassDecl(
"@JsType(isNative=true) static class Buggy {",
@@ -1328,7 +1484,7 @@
assertBuggySucceeds();
}
- public void testNativeJsTypeDefaultConstructorSucceeds() throws UnableToCompleteException {
+ public void testNativeJsTypeDefaultConstructorSucceeds() throws Exception {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetClassDecl(
"@JsType(isNative=true) static class Buggy {",
@@ -1337,7 +1493,86 @@
assertBuggySucceeds();
}
- public void testNonJsTypeExtendsJsTypeSucceeds() throws UnableToCompleteException {
+ public void testNativeJsTypeInstanceMethodOverloadSucceeds() throws Exception {
+ addSnippetImport("jsinterop.annotations.JsType");
+ addSnippetClassDecl(
+ "@SuppressWarnings(\"unusable-by-js\")",
+ "@JsType(isNative=true) public static class Buggy {",
+ " public native void m(Object o);",
+ " public native void m(Object[] o);",
+ "}");
+
+ assertBuggySucceeds();
+ }
+
+ public void testNativeJsTypeStaticMethodOverloadSucceeds() throws Exception {
+ addSnippetImport("jsinterop.annotations.JsType");
+ addSnippetClassDecl(
+ "@SuppressWarnings(\"unusable-by-js\")",
+ "@JsType(isNative=true) public static class Buggy {",
+ " public static native void m(Object o);",
+ " public static native void m(Object[] o);",
+ "}");
+
+ assertBuggySucceeds();
+ }
+
+ public void testNonJsTypeExtendingNativeJsTypeWithInstanceMethodSucceeds() throws Exception {
+ addSnippetImport("jsinterop.annotations.JsType");
+ addSnippetClassDecl(
+ "@SuppressWarnings(\"unusable-by-js\")",
+ "@JsType(isNative=true) public static class Super {",
+ " public native void m(Object o);",
+ " public native void m(Object[] o);",
+ "}",
+ "@JsType public static class Buggy extends Super {",
+ " public void n(Object o) { }",
+ "}");
+
+ assertBuggySucceeds();
+ }
+
+ public void testNonJsTypeExtendingNativeJsTypeWithInstanceMethodOverloadsFails() {
+ addSnippetImport("jsinterop.annotations.JsType");
+ addSnippetClassDecl(
+ "@JsType(isNative=true) public static class Super {",
+ " public native void m(Object o);",
+ " public native void m(int o);",
+ "}",
+ "public static class Buggy extends Super {",
+ " public void m(Object o) { }",
+ "}");
+
+ assertBuggyFails(
+ "Line 9: 'void EntryPoint.Buggy.m(Object)' and 'void EntryPoint.Super.m(int)' "
+ + "cannot both use the same JavaScript name 'm'.");
+ }
+
+ public void testNonJsTypeWithNativeStaticMethodOverloadsSucceeds() throws Exception {
+ addSnippetImport("jsinterop.annotations.JsMethod");
+ addSnippetClassDecl(
+ "public static class Buggy {",
+ " @JsMethod public static native void m(Object o);",
+ " @JsMethod public static native void m(int o);",
+ "}");
+
+ assertBuggySucceeds();
+ }
+
+ public void testNonJsTypeWithNativeInstanceMethodOverloadsFails() throws Exception {
+ addSnippetImport("jsinterop.annotations.JsMethod");
+ addSnippetClassDecl(
+ "public static class Buggy {",
+ " @JsMethod public native void m(Object o);",
+ " @JsMethod public void m(int o) { }",
+ "}");
+
+ assertBuggyFails(
+ "Line 6: 'void EntryPoint.Buggy.m(int)' and 'void EntryPoint.Buggy.m(Object)' "
+ + "cannot both use the same JavaScript name 'm'.");
+ }
+
+ public void testNonJsTypeExtendsJsTypeSucceeds() throws Exception {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetClassDecl(
"@JsType public static class Super {",
@@ -1348,7 +1583,7 @@
assertBuggySucceeds();
}
- public void testNonJsTypeImplementsJsTypeInterfaceSucceeds() throws UnableToCompleteException {
+ public void testNonJsTypeImplementsJsTypeInterfaceSucceeds() throws Exception {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetClassDecl(
"@JsType public interface Interface {",
@@ -1359,8 +1594,7 @@
assertBuggySucceeds();
}
- public void testNonJsTypeInterfaceExtendsJsTypeInterfaceSucceeds()
- throws UnableToCompleteException {
+ public void testNonJsTypeInterfaceExtendsJsTypeInterfaceSucceeds() throws Exception {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetClassDecl(
"@JsType public interface Interface {",
@@ -1371,20 +1605,20 @@
assertBuggySucceeds();
}
- public void testNonJsTypeExtendsNativeJsTypeSucceeds()
- throws UnableToCompleteException {
+ public void testNonJsTypeExtendsNativeJsTypeSucceeds() throws Exception {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetClassDecl(
"@JsType(isNative=true) public static class Super {",
+ " public native void m();",
"}",
"public static class Buggy extends Super {",
+ " public void m() { }",
"}");
assertBuggySucceeds();
}
- public void testNonJsTypeImplementsNativeJsTypeInterfaceSucceeds()
- throws UnableToCompleteException {
+ public void testNonJsTypeImplementsNativeJsTypeInterfaceSucceeds() throws Exception {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetClassDecl(
"@JsType(isNative=true) public interface Interface {",
@@ -1395,8 +1629,7 @@
assertBuggySucceeds();
}
- public void testNonJsTypeInterfaceExtendsNativeJsTypeInterfaceSucceeds()
- throws UnableToCompleteException {
+ public void testNonJsTypeInterfaceExtendsNativeJsTypeInterfaceSucceeds() throws Exception {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetClassDecl(
"@JsType(isNative=true) public interface Interface {",
@@ -1469,7 +1702,7 @@
assertBuggySucceeds();
}
- public void testUnusuableByJsFails() throws Exception {
+ public void testUnusuableByJsWarns() throws Exception {
addSnippetImport("jsinterop.annotations.JsFunction");
addSnippetImport("jsinterop.annotations.JsType");
addSnippetImport("jsinterop.annotations.JsMethod");
@@ -1499,44 +1732,59 @@
"}");
assertBuggySucceeds(
- "[unusable-by-js] Return type of "
- + "'test.EntryPoint$Buggy.f1(Ltest/EntryPoint$A;)Ltest/EntryPoint$A;' "
- + "is not usable by but exposed to JavaScript",
- "[unusable-by-js] Return type of "
- + "'test.EntryPoint$Buggy.f2([Ltest/EntryPoint$A;)[Ltest/EntryPoint$A;' "
- + "is not usable by but exposed to JavaScript",
- "[unusable-by-js] Return type of "
- + "'test.EntryPoint$Buggy.f3(J)J' is not usable by but exposed to JavaScript",
- "[unusable-by-js] Return type of "
- + "'test.EntryPoint$Buggy.f4(Ltest/EntryPoint$B;)Ltest/EntryPoint$B;' "
- + "is not usable by but exposed to JavaScript",
- "[unusable-by-js] Type of field 'field' in type 'test.EntryPoint$Buggy' "
- + "is not usable by but exposed to JavaScript",
- "[unusable-by-js] Type of parameter 'a' in method "
- + "'test.EntryPoint$Buggy.f1(Ltest/EntryPoint$A;)Ltest/EntryPoint$A;' "
- + "is not usable by but exposed to JavaScript",
- "[unusable-by-js] Type of parameter 'a' in method "
- + "'test.EntryPoint$Buggy.f2([Ltest/EntryPoint$A;)[Ltest/EntryPoint$A;' "
- + "is not usable by but exposed to JavaScript",
- "[unusable-by-js] Type of parameter 'a' in method 'test.EntryPoint$Buggy.f3(J)J' "
- + "is not usable by but exposed to JavaScript",
- "[unusable-by-js] Type of parameter 'a' in method "
- + "'test.EntryPoint$Buggy.f4(Ltest/EntryPoint$B;)Ltest/EntryPoint$B;' "
- + "is not usable by but exposed to JavaScript",
- "[unusable-by-js] Type of parameter 'a' in method "
- + "'test.EntryPoint$Buggy.f5([[Ljava/lang/Object;)V' "
- + "is not usable by but exposed to JavaScript",
- "[unusable-by-js] Type of parameter 'a' in method "
- + "'test.EntryPoint$Buggy.f6([Ljava/lang/Object;)V' "
- + "is not usable by but exposed to JavaScript",
- "[unusable-by-js] Type of parameter 'a' in method " // JsFunction method
- + "'test.EntryPoint$FI.f(Ltest/EntryPoint$A;)V' "
- + "is not usable by but exposed to JavaScript",
- "[unusable-by-js] Type of parameter 'a' in method " // JsMethod in non-jstype class.
- + "'test.EntryPoint$C.fc1(Ltest/EntryPoint$A;)V' "
- + "is not usable by but exposed to JavaScript",
- "[unusable-by-js] Type of field 'a' in type " // JsProperty in non-jstype class.
- + "'test.EntryPoint$D' is not usable by but exposed to JavaScript");
+ "Line 21: [unusable-by-js] Return type of 'EntryPoint.A EntryPoint.Buggy.f1(EntryPoint.A)' "
+ + "is not usable by but exposed to JavaScript.",
+ "Line 22: [unusable-by-js] Return type of "
+ + "'EntryPoint.A[] EntryPoint.Buggy.f2(EntryPoint.A[])' is not usable by but "
+ + "exposed to JavaScript.",
+ "Line 23: [unusable-by-js] Return type of 'long EntryPoint.Buggy.f3(long)' is not "
+ + "usable by but exposed to JavaScript.",
+ "Line 24: [unusable-by-js] Return type of 'EntryPoint.B EntryPoint.Buggy.f4(EntryPoint.B)' "
+ + "is not usable by but exposed to JavaScript.",
+ "Line 20: [unusable-by-js] Type of field 'field' in type 'EntryPoint.Buggy' "
+ + "is not usable by but exposed to JavaScript.",
+ "Line 21: [unusable-by-js] Type of parameter 'a' in method "
+ + "'EntryPoint.A EntryPoint.Buggy.f1(EntryPoint.A)' is not usable by but "
+ + "exposed to JavaScript.",
+ "Line 22: [unusable-by-js] Type of parameter 'a' in method "
+ + "'EntryPoint.A[] EntryPoint.Buggy.f2(EntryPoint.A[])' is not usable by but "
+ + "exposed to JavaScript.",
+ "Line 23: [unusable-by-js] Type of parameter 'a' in method "
+ + "'long EntryPoint.Buggy.f3(long)' is not usable by but exposed to JavaScript.",
+ "Line 24: [unusable-by-js] Type of parameter 'a' in method "
+ + "'EntryPoint.B EntryPoint.Buggy.f4(EntryPoint.B)' is not usable by but "
+ + "exposed to JavaScript.",
+ "Line 25: [unusable-by-js] Type of parameter 'a' in method "
+ + "'void EntryPoint.Buggy.f5(Object[][])' is not usable by but exposed to JavaScript.",
+ "Line 26: [unusable-by-js] Type of parameter 'a' in method "
+ + "'void EntryPoint.Buggy.f6(Object[])' is not usable by but exposed to JavaScript.",
+ "Line 18: [unusable-by-js] Type of parameter 'a' in method "
+ + "'void EntryPoint.FI.f(EntryPoint.A)' is not usable by but exposed to JavaScript.",
+ "Line 12: [unusable-by-js] Type of parameter 'a' in method "
+ // JsMethod in non-jstype class.
+ + "'void EntryPoint.C.fc1(EntryPoint.A)' is not usable by but exposed to JavaScript.",
+ "Line 16: [unusable-by-js] Type of field 'a' in type " // JsProperty in non-jstype class.
+ + "'EntryPoint.D' is not usable by but exposed to JavaScript.");
+ }
+
+ public void testUnusableByJsAccidentalOverrideSuppressionWarns()
+ throws Exception {
+ addSnippetImport("jsinterop.annotations.JsType");
+ addSnippetClassDecl(
+ "@JsType",
+ "public static interface Foo {",
+ " @SuppressWarnings(\"unusable-by-js\") ",
+ " void doIt(Class foo);",
+ "}",
+ "public static class Parent {",
+ " public void doIt(Class x) {}",
+ "}",
+ "public static class Buggy extends Parent implements Foo {}");
+
+ assertBuggySucceeds(
+ "Line 10: [unusable-by-js] Type of parameter 'x' in method "
+ + "'void EntryPoint.Parent.doIt(Class)' (exposed by 'EntryPoint.Buggy') is not usable "
+ + "by but exposed to JavaScript.");
}
private static final MockJavaResource jsFunctionInterface = new MockJavaResource(
@@ -1554,21 +1802,31 @@
};
public final void assertBuggySucceeds(String... expectedWarnings)
- throws UnableToCompleteException {
- Result result = assertCompileSucceeds("Buggy buggy = null;", expectedWarnings);
+ throws Exception {
+ List<String> allWarnings = Lists.newArrayList();
+ if (expectedWarnings.length > 0) {
+ allWarnings.add("Warnings in test/EntryPoint.java");
+ allWarnings.addAll(Arrays.asList(expectedWarnings));
+ }
+ Result result = assertCompileSucceeds("Buggy buggy = null;",
+ allWarnings.toArray(new String[0]));
assertNotNull(result.findClass("test.EntryPoint$Buggy"));
}
public final void assertBuggyFails(String... expectedErrors) {
assertTrue(expectedErrors.length > 0);
- assertCompileFails("Buggy buggy = null;", expectedErrors);
+
+ List<String> allErrors = Lists.newArrayList();
+ allErrors.add("Errors in test/EntryPoint.java");
+ allErrors.addAll(Arrays.asList(expectedErrors));
+ assertCompileFails("Buggy buggy = null;", allErrors.toArray(new String[0]));
}
@Override
protected boolean doOptimizeMethod(TreeLogger logger, JProgram program, JMethod method) {
try {
JsInteropRestrictionChecker.exec(logger, program, new MinimalRebuildCache());
- } catch (UnableToCompleteException e) {
+ } catch (Exception e) {
throw new RuntimeException(e);
}
return false;
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/UnifyAstTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/UnifyAstTest.java
index 305e3f1..9938f40 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/impl/UnifyAstTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/UnifyAstTest.java
@@ -20,6 +20,7 @@
import com.google.gwt.dev.javac.testing.impl.JavaResourceBase;
import com.google.gwt.dev.javac.testing.impl.MockJavaResource;
import com.google.gwt.dev.jjs.ast.JClassType;
+import com.google.gwt.dev.jjs.ast.JDeclaredType;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.util.arg.SourceLevel;
@@ -92,6 +93,28 @@
assertOverrides(result, "b.E.m1()Lb/E;");
}
+ public void testOverrides_orderInOverriddenSet() throws Exception {
+ addAll(A_A, A_I, A_J, A_B, B_C, B_D, B_E);
+ Result result = optimize("void", "");
+
+ for (JDeclaredType type : result.getOptimizedProgram().getDeclaredTypes()) {
+ for (JMethod method : type.getMethods()) {
+ JavaAstVerifier.assertCorrectOverriddenOrder(result.getOptimizedProgram(), method);
+ }
+ }
+ }
+
+ public void testOverrides_orderInOverridingnSet() throws Exception {
+ addAll(A_A, A_I, A_J, A_B, B_C, B_D, B_E);
+ Result result = optimize("void", "");
+
+ for (JDeclaredType type : result.getOptimizedProgram().getDeclaredTypes()) {
+ for (JMethod method : type.getMethods()) {
+ JavaAstVerifier.assertCorrectOverridingOrder(result.getOptimizedProgram(), method);
+ }
+ }
+ }
+
public void testOverrides_packagePrivate() throws Exception {
addAll(A_A, A_I, A_J, A_B, B_C, B_D, B_E);
Result result = optimize("void", "");
diff --git a/dev/core/test/com/google/gwt/dev/util/UnitTestTreeLogger.java b/dev/core/test/com/google/gwt/dev/util/UnitTestTreeLogger.java
index 7e38525..b57a254 100644
--- a/dev/core/test/com/google/gwt/dev/util/UnitTestTreeLogger.java
+++ b/dev/core/test/com/google/gwt/dev/util/UnitTestTreeLogger.java
@@ -17,6 +17,7 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.thirdparty.guava.common.collect.ComparisonChain;
+import com.google.gwt.thirdparty.guava.common.collect.Lists;
import junit.framework.Assert;
@@ -148,6 +149,17 @@
return type;
}
+ public boolean equals(Object other) {
+ if (!(other instanceof LogEntry)) {
+ return false;
+ }
+ return this.toString().equals(other.toString());
+ }
+
+ public int hashCode() {
+ return toString().hashCode();
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
@@ -248,7 +260,11 @@
Collections.sort(actualEntries);
if (expectedEntries.size() != actualEntries.size()) {
- Assert.fail("Wrong log count: expected=" + expectedEntries + ", actual=" + actualEntries);
+ List<LogEntry> missingEntries = Lists.newArrayList(expectedEntries);
+ missingEntries.removeAll(actualEntries);
+ List<LogEntry> unexpectedEntries = Lists.newArrayList(actualEntries);
+ unexpectedEntries.removeAll(expectedEntries);
+ Assert.fail("Wrong log count: missing=" + missingEntries + ", unexpected=" + unexpectedEntries);
}
for (int i = 0; i < expectedEntries.size(); ++i) {