Fix JSNIRestrictionChecker error messages.
Bug: #9356
Bug-Link: http://github.com/gwtproject/gwt/issues/9356
Change-Id: Ifbb582be9612e323a1a2e6e72a06bdcf813a9f1d
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/AbstractRestrictionChecker.java b/dev/core/src/com/google/gwt/dev/jjs/impl/AbstractRestrictionChecker.java
new file mode 100644
index 0000000..6dafe98
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/AbstractRestrictionChecker.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.jjs.impl;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.TreeLogger.Type;
+import com.google.gwt.dev.jjs.HasSourceInfo;
+import com.google.gwt.dev.jjs.ast.JDeclaredType;
+import com.google.gwt.dev.jjs.ast.JField;
+import com.google.gwt.dev.jjs.ast.JMember;
+import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JType;
+import com.google.gwt.dev.util.log.AbstractTreeLogger;
+import com.google.gwt.thirdparty.guava.common.collect.Iterables;
+import com.google.gwt.thirdparty.guava.common.collect.Multimap;
+import com.google.gwt.thirdparty.guava.common.collect.Ordering;
+import com.google.gwt.thirdparty.guava.common.collect.Sets;
+import com.google.gwt.thirdparty.guava.common.collect.TreeMultimap;
+
+import java.util.TreeSet;
+
+/**
+ * Abstract base class for error checking in the full AST.
+ */
+public abstract class AbstractRestrictionChecker {
+ private Multimap<String, String> errorsByFilename
+ = TreeMultimap.create(Ordering.natural(), AbstractTreeLogger.LOG_LINE_COMPARATOR);
+ private Multimap<String, String> warningsByFilename
+ = TreeMultimap.create(Ordering.natural(), AbstractTreeLogger.LOG_LINE_COMPARATOR);
+
+ protected static String getDescription(HasSourceInfo hasSourceInfo) {
+ if (hasSourceInfo instanceof JDeclaredType) {
+ return getTypeDescription((JDeclaredType) hasSourceInfo);
+ } else {
+ return getMemberDescription((JMember) hasSourceInfo);
+ }
+ }
+
+ protected 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 safe to assume
+ // that they will always be overriding and crash the compiler.
+ && !method.getOverriddenMethods().isEmpty()) {
+ JMethod overridenMethod = Iterables.getFirst(method.getOverriddenMethods(), null);
+ return String.format("'%s' (exposed by '%s')",
+ JjsUtils.getReadableDescription(overridenMethod),
+ JjsUtils.getReadableDescription(method.getEnclosingType()));
+ }
+ return String.format("'%s'", JjsUtils.getReadableDescription(method));
+ }
+
+ private static String getTypeDescription(JDeclaredType type) {
+ return String.format("'%s'", JjsUtils.getReadableDescription(type));
+ }
+
+ protected void logError(String format, JType type) {
+ logError(type, format, JjsUtils.getReadableDescription(type));
+ }
+
+ protected void logError(HasSourceInfo hasSourceInfo, String format, Object... args) {
+ errorsByFilename.put(hasSourceInfo.getSourceInfo().getFileName(),
+ String.format("Line %d: ", hasSourceInfo.getSourceInfo().getStartLine())
+ + String.format(format, args));
+ }
+
+ protected void logWarning(HasSourceInfo hasSourceInfo, String format, Object... args) {
+ warningsByFilename.put(hasSourceInfo.getSourceInfo().getFileName(),
+ String.format("Line %d: ", hasSourceInfo.getSourceInfo().getStartLine())
+ + String.format(format, args));
+ }
+
+ protected 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(
+ Type.INFO, (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);
+ }
+ }
+ return !errorsByFilename.isEmpty();
+ }
+}
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 a781602..1519ca6 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,7 +14,6 @@
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;
@@ -43,7 +42,6 @@
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.ast.JReferenceType;
import com.google.gwt.dev.jjs.ast.JStatement;
-import com.google.gwt.dev.jjs.ast.JType;
import com.google.gwt.dev.jjs.ast.JVisitor;
import com.google.gwt.dev.jjs.ast.js.JsniMethodBody;
import com.google.gwt.dev.js.JsUtils;
@@ -53,25 +51,19 @@
import com.google.gwt.dev.js.ast.JsParameter;
import com.google.gwt.dev.js.ast.JsVisitor;
import com.google.gwt.dev.util.Pair;
-import com.google.gwt.dev.util.log.AbstractTreeLogger;
import com.google.gwt.thirdparty.guava.common.base.Predicate;
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.Ordering;
-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.TreeSet;
/**
* Checks and throws errors for invalid JsInterop constructs.
*/
-public class JsInteropRestrictionChecker {
+public class JsInteropRestrictionChecker extends AbstractRestrictionChecker {
public static void exec(TreeLogger logger, JProgram jprogram,
MinimalRebuildCache minimalRebuildCache) throws UnableToCompleteException {
@@ -83,10 +75,6 @@
}
}
- private Multimap<String, String> errorsByFilename
- = TreeMultimap.create(Ordering.natural(), AbstractTreeLogger.LOG_LINE_COMPARATOR);
- private Multimap<String, String> warningsByFilename
- = TreeMultimap.create(Ordering.natural(), AbstractTreeLogger.LOG_LINE_COMPARATOR);
private final JProgram jprogram;
private final MinimalRebuildCache minimalRebuildCache;
@@ -923,70 +911,8 @@
.equals(potentiallyOverriddenMethod.getJsniSignature(false, false));
}
- private static String getDescription(HasSourceInfo hasSourceInfo) {
- if (hasSourceInfo instanceof JDeclaredType) {
- return getTypeDescription((JDeclaredType) hasSourceInfo);
- } else {
- return getMemberDescription((JMember) hasSourceInfo);
- }
- }
- 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 static String getTypeDescription(JDeclaredType type) {
- return String.format("'%s'", JjsUtils.getReadableDescription(type));
- }
-
private boolean isUnusableByJsSuppressed(CanHaveSuppressedWarnings x) {
return x.getSuppressedWarnings() != null &&
x.getSuppressedWarnings().contains(JsInteropUtil.UNUSABLE_BY_JS);
}
-
- private void logError(String format, JType type) {
- logError(type, format, JjsUtils.getReadableDescription(type));
- }
-
- 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);
- }
- }
- return !errorsByFilename.isEmpty();
- }
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JsniRestrictionChecker.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JsniRestrictionChecker.java
index 6ba6624..631213f 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/JsniRestrictionChecker.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JsniRestrictionChecker.java
@@ -14,16 +14,14 @@
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.jjs.HasSourceInfo;
import com.google.gwt.dev.jjs.ast.Context;
import com.google.gwt.dev.jjs.ast.JDeclaredType;
import com.google.gwt.dev.jjs.ast.JInterfaceType;
import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JMethodBody;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.ast.JVisitor;
-import com.google.gwt.dev.jjs.ast.js.JsniMethodBody;
import com.google.gwt.dev.jjs.ast.js.JsniMethodRef;
import com.google.gwt.thirdparty.guava.common.collect.Sets;
@@ -32,32 +30,78 @@
/**
* Checks and throws errors for invalid JSNI constructs.
*/
-public class JsniRestrictionChecker extends JVisitor {
+public class JsniRestrictionChecker extends AbstractRestrictionChecker {
- public static void exec(TreeLogger logger, JProgram jprogram) throws UnableToCompleteException {
- JsniRestrictionChecker jsniRestrictionChecker = new JsniRestrictionChecker(logger, jprogram);
- jsniRestrictionChecker.accept(jprogram);
- if (jsniRestrictionChecker.hasErrors) {
+ public static void exec(TreeLogger logger, JProgram program)
+ throws UnableToCompleteException {
+ new JsniRestrictionChecker().checkProgram(logger, program);
+ }
+
+ private void checkProgram(TreeLogger logger, final JProgram program)
+ throws UnableToCompleteException {
+ final Set<JDeclaredType> typesRequiringTrampolineDispatch = Sets.newHashSet();
+ for (JDeclaredType type : program.getRepresentedAsNativeTypes()) {
+ collectAllSuperTypes(type, typesRequiringTrampolineDispatch);
+ }
+ new JVisitor() {
+ @Override
+ public boolean visit(JMethodBody x, Context ctx) {
+ // Skip non jsni methods.
+ return false;
+ }
+
+ @Override
+ public boolean visit(JsniMethodRef x, Context ctx) {
+ checkJsniMethodReference(x);
+ return true;
+ }
+
+ private void checkJsniMethodReference(JsniMethodRef jsniMethodReference) {
+ JMethod method = jsniMethodReference.getTarget();
+ JDeclaredType enclosingType = method.getEnclosingType();
+
+ if (isNonStaticJsoClassDispatch(method, enclosingType)) {
+ logError(jsniMethodReference,
+ "Cannot call non-static method %s on an instance which is a "
+ + "subclass of JavaScriptObject. Only static method calls on JavaScriptObject "
+ + "subclasses are allowed in JSNI.",
+ getDescription(method));
+ } else if (isJsoInterface(enclosingType)) {
+ logError(jsniMethodReference,
+ "Cannot call method %s on an instance which might be a JavaScriptObject. "
+ + "Such a method call is only allowed in pure Java (non-JSNI) functions.",
+ getDescription(method));
+ } else if (program.isRepresentedAsNativeJsPrimitive(enclosingType)
+ && !method.isStatic()
+ && !method.isConstructor()) {
+ logError(jsniMethodReference,
+ "Cannot call method %s. Instance methods on %s cannot be called from JSNI.",
+ getDescription(method),
+ getDescription(enclosingType));
+ } else if (typesRequiringTrampolineDispatch.contains(enclosingType)
+ && !method.isStatic()
+ && !method.isConstructor()) {
+ logWarning(jsniMethodReference,
+ "Unsafe call to method %s. Instance methods from %s should "
+ + "not be called on Boolean, Double, String, Array or JSO instances from JSNI.",
+ getDescription(method),
+ getDescription(enclosingType));
+ }
+ }
+
+ private boolean isJsoInterface(JDeclaredType type) {
+ return program.typeOracle.isSingleJsoImpl(type)
+ || program.typeOracle.isDualJsoInterface(type);
+ }
+ }.accept(program);
+
+ boolean hasErrors = reportErrorsAndWarnings(logger);
+ if (hasErrors) {
throw new UnableToCompleteException();
}
}
- private JMethod currentJsniMethod;
- private final JProgram jprogram;
- private final Set<JDeclaredType> typesRequiringTrampolineDispatch;
- private TreeLogger logger;
- private boolean hasErrors;
-
- public JsniRestrictionChecker(TreeLogger logger, JProgram jprogram) {
- this.logger = logger;
- this.jprogram = jprogram;
- this.typesRequiringTrampolineDispatch = Sets.newHashSet();
- for (JDeclaredType type : jprogram.getRepresentedAsNativeTypes()) {
- collectAllSuperTypes(type , typesRequiringTrampolineDispatch);
- }
- }
-
- private void collectAllSuperTypes(JDeclaredType type, Set<JDeclaredType> allSuperTypes) {
+ private static void collectAllSuperTypes(JDeclaredType type, Set<JDeclaredType> allSuperTypes) {
if (type.getSuperClass() != null) {
allSuperTypes.add(type.getSuperClass());
collectAllSuperTypes(type.getSuperClass(), allSuperTypes);
@@ -68,77 +112,10 @@
}
}
- @Override
- public boolean visit(JDeclaredType x, Context ctx) {
- TreeLogger currentLogger = this.logger;
- this.logger = this.logger.branch(Type.INFO, "Errors in " + x.getSourceInfo().getFileName());
- accept(x.getMethods());
- this.logger = currentLogger;
- return false;
- }
-
- @Override
- public boolean visit(JsniMethodBody x, Context ctx) {
- currentJsniMethod = x.getMethod();
- return true;
- }
-
- @Override
- public boolean visit(JsniMethodRef x, Context ctx) {
- JMethod calledMethod = x.getTarget();
- JDeclaredType enclosingTypeOfCalledMethod = calledMethod.getEnclosingType();
-
- if (isNonStaticJsoClassDispatch(calledMethod, enclosingTypeOfCalledMethod)) {
- logError(x, "JSNI method %s calls non-static method %s on an instance which is a "
- + "subclass of JavaScriptObject. Only static method calls on JavaScriptObject subclasses "
- + "are allowed in JSNI.",
- currentJsniMethod.getQualifiedName(),
- calledMethod.getQualifiedName());
- } else if (isJsoInterface(enclosingTypeOfCalledMethod)) {
- logError(x, "JSNI method %s calls method %s on an instance which might be a "
- + "JavaScriptObject. Such a method call is only allowed in pure Java (non-JSNI) "
- + "functions.",
- currentJsniMethod.getQualifiedName(),
- calledMethod.getQualifiedName());
- } else if (jprogram.isRepresentedAsNativeJsPrimitive(enclosingTypeOfCalledMethod)
- && !calledMethod.isStatic()
- && !calledMethod.isConstructor()) {
- logError(x, "JSNI method %s calls method %s. Instance methods on %s "
- + "cannot be called from JSNI.",
- currentJsniMethod.getQualifiedName(),
- calledMethod.getQualifiedName(),
- enclosingTypeOfCalledMethod.getName());
- } else if (typesRequiringTrampolineDispatch.contains(enclosingTypeOfCalledMethod)
- && !calledMethod.isStatic()
- && !calledMethod.isConstructor()) {
- log(x, Type.WARN, "JSNI method %s calls method %s. Instance methods from %s should "
- + "not be called on Boolean, Double, String, Array or JSO instances from JSNI.",
- currentJsniMethod.getQualifiedName(),
- calledMethod.getQualifiedName(),
- enclosingTypeOfCalledMethod.getName());
- }
- return true;
- }
-
- private void log(HasSourceInfo hasSourceInfo, Type type, String format, Object... args) {
- logger.log(type, String.format(
- String.format("Line %d: %s",
- hasSourceInfo.getSourceInfo().getStartLine(),
- format),
- args));
- hasErrors |= type == Type.ERROR;
- }
-
- private void logError(HasSourceInfo hasSourceInfo, String format, Object... args) {
- log(hasSourceInfo, Type.ERROR, format, args);
- }
-
- private boolean isJsoInterface(JDeclaredType type) {
- return jprogram.typeOracle.isSingleJsoImpl(type)
- || jprogram.typeOracle.isDualJsoInterface(type);
- }
-
- private boolean isNonStaticJsoClassDispatch(JMethod method, JDeclaredType enclosingType) {
+ private static boolean isNonStaticJsoClassDispatch(JMethod method, JDeclaredType enclosingType) {
return !method.isStatic() && enclosingType.isJsoType();
}
+
+ private JsniRestrictionChecker() {
+ }
}
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 24b0ac5..ae5835a 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
@@ -21,10 +21,6 @@
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.util.arg.SourceLevel;
-import com.google.gwt.thirdparty.guava.common.collect.Lists;
-
-import java.util.Arrays;
-import java.util.List;
/**
* Tests for the JsInteropRestrictionChecker.
@@ -2221,23 +2217,13 @@
public final void assertBuggySucceeds(String... 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]));
+ Result result = assertCompileSucceeds("Buggy buggy = null;", expectedWarnings);
assertNotNull(result.findClass("test.EntryPoint$Buggy"));
}
public final void assertBuggyFails(String... expectedErrors) {
assertTrue(expectedErrors.length > 0);
-
- 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]));
+ assertCompileFails("Buggy buggy = null;", expectedErrors);
}
@Override
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/JsniRestrictionCheckerTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/JsniRestrictionCheckerTest.java
index 5ad8740..9aabbf9 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/impl/JsniRestrictionCheckerTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/JsniRestrictionCheckerTest.java
@@ -48,9 +48,8 @@
"}");
assertCompileFails("new Buggy().jsniMethod(null);",
- "Line 6: JSNI method test.EntryPoint$Buggy.jsniMethod(Ljava/lang/Object;)V calls method "
- + "java.lang.Double.doubleValue()D. Instance methods on java.lang.Double cannot be "
- + "called from JSNI.");
+ "Line 6: Cannot call method 'double Double.doubleValue()'. Instance methods on 'Double' "
+ + "cannot be called from JSNI.");
}
public void testInstanceCallToTrampolineWarns() throws Exception {
@@ -65,18 +64,14 @@
"}");
assertCompileSucceeds("new Buggy().jsniMethod(null);",
- "Line 6: JSNI method test.EntryPoint$Buggy.jsniMethod(Ljava/lang/Object;)V calls method "
- + "java.lang.Number.doubleValue()D. "
- + "Instance methods from java.lang.Number should not be called on "
- + "Boolean, Double, String, Array or JSO instances from JSNI.",
- "Line 7: JSNI method test.EntryPoint$Buggy.jsniMethod(Ljava/lang/Object;)V calls method "
- + "java.lang.CharSequence.charAt(I)C. "
- + "Instance methods from java.lang.CharSequence should not be called on "
- + "Boolean, Double, String, Array or JSO instances from JSNI.",
- "Line 8: JSNI method test.EntryPoint$Buggy.jsniMethod(Ljava/lang/Object;)V calls method "
- + "java.lang.Object.toString()Ljava/lang/String;. "
- + "Instance methods from java.lang.Object should not be called on "
- + "Boolean, Double, String, Array or JSO instances from JSNI.");
+ "Line 6: Unsafe call to method 'double Number.doubleValue()'. Instance methods from "
+ + "'Number' should not be called on Boolean, Double, String, Array or JSO instances "
+ + "from JSNI.",
+ "Line 7: Unsafe call to method 'char CharSequence.charAt(int)'. Instance methods from "
+ + "'CharSequence' should not be called on Boolean, Double, String, Array or JSO "
+ + "instances from JSNI.",
+ "Line 8: Unsafe call to method 'String Object.toString()'. Instance methods from 'Object' "
+ + "should not be called on Boolean, Double, String, Array or JSO instances from JSNI.");
}
public void testStaticJsoDispatchSucceeds() throws Exception {
@@ -112,10 +107,9 @@
"}");
assertCompileFails("new Buggy().jsniMethod(null);",
- "Line 13: JSNI method test.EntryPoint$Buggy.jsniMethod(Ljava/lang/Object;)V calls " +
- "method test.EntryPoint$Buggy$IFoo.foo()V on an instance which might be a " +
- "JavaScriptObject. Such a method call is only allowed in pure Java " +
- "(non-JSNI) functions.");
+ "Line 13: Cannot call method 'void EntryPoint.Buggy.IFoo.foo()' on an instance which might "
+ + "be a JavaScriptObject. Such a method call is only allowed in pure Java (non-JSNI) "
+ + "functions.");
}
public void testNonstaticJsoDispatchFails() throws Exception {
@@ -128,11 +122,9 @@
"}");
assertCompileFails("new Buggy().jsniMethod(null);",
- "Line 6: JSNI method test.EntryPoint$Buggy.jsniMethod(Ljava/lang/Object;)V calls " +
- "non-static method " +
- "com.google.gwt.core.client.JavaScriptObject.toString()Ljava/lang/String; on an " +
- "instance which is a subclass of JavaScriptObject. Only static method calls on " +
- "JavaScriptObject subclasses are allowed in JSNI.");
+ "Line 6: Cannot call non-static method 'String JavaScriptObject.toString()' on an instance "
+ + "which is a subclass of JavaScriptObject. Only static method calls on "
+ + "JavaScriptObject subclasses are allowed in JSNI.");
}
public void testNonstaticJsoSubclassDispatchFails() throws Exception {
@@ -149,10 +141,9 @@
"}");
assertCompileFails("new Buggy().jsniMethod(null);",
- "Line 10: JSNI method test.EntryPoint$Buggy.jsniMethod(Ljava/lang/Object;)V calls " +
- "non-static method test.EntryPoint$Buggy$Foo.foo()V on an instance which is a " +
- "subclass of JavaScriptObject. Only static method calls on JavaScriptObject " +
- "subclasses are allowed in JSNI.");
+ "Line 10: Cannot call non-static method 'void EntryPoint.Buggy.Foo.foo()' on an instance "
+ + "which is a subclass of JavaScriptObject. Only static method calls on "
+ + "JavaScriptObject subclasses are allowed in JSNI.");
}
public void testStringInstanceMethodCallFail() throws Exception {
@@ -165,9 +156,8 @@
"}");
assertCompileFails("new Buggy().jsniMethod(null);",
- "Line 6: JSNI method test.EntryPoint$Buggy.jsniMethod(Ljava/lang/Object;)V calls method " +
- "java.lang.String.length()I. Instance methods on java.lang.String cannot be called " +
- "from JSNI.");
+ "Line 6: Cannot call method 'int String.length()'. Instance methods on 'String' cannot be "
+ + "called from JSNI.");
}
public void testStringStaticMethodCallSucceeds() throws Exception {