Remove the need for an explicit SingleJsoImpl annotation. The single-JSO-ness of interfaces will be automatically calculated.
Hosted-mode bug fixes for SingleJsoImpl.
Patch by: bobv
Review by: spoon
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@4851 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JRealClassType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JRealClassType.java
index 201394f..26c4ab0 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JRealClassType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JRealClassType.java
@@ -15,8 +15,6 @@
*/
package com.google.gwt.core.ext.typeinfo;
-import com.google.gwt.core.client.SingleJsoImpl;
-
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.HashSet;
@@ -95,12 +93,6 @@
public void addAnnotations(
Map<Class<? extends Annotation>, Annotation> declaredAnnotations) {
annotations.addAnnotations(declaredAnnotations);
- for (Class<? extends Annotation> clazz : declaredAnnotations.keySet()) {
- if (SingleJsoImpl.class.equals(clazz)) {
- oracle.addSingleJsoInterface(this);
- break;
- }
- }
}
public void addImplementedInterface(JClassType intf) {
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java
index cda9804..0de6e96 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java
@@ -18,6 +18,7 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.generator.GenUtil;
+import com.google.gwt.dev.shell.JsValueGlue;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
@@ -199,7 +200,10 @@
private final Map<String, List<JWildcardType>> wildcardTypes = new HashMap<String, List<JWildcardType>>();
- private final Set<JRealClassType> singleJsoImplTypes = new HashSet<JRealClassType>();
+ /**
+ * Maps SingleJsoImpl interfaces to the implementing JSO subtype.
+ */
+ private final Map<JClassType, JClassType> jsoSingleImpls = new IdentityHashMap<JClassType, JClassType>();
public TypeOracle() {
// Always create the default package.
@@ -256,6 +260,7 @@
public void finish(TreeLogger logger) {
JClassType[] newTypes = recentTypes.toArray(NO_JCLASSES);
computeHierarchyRelationships(newTypes);
+ computeSingleJsoImplData(logger, newTypes);
consumeTypeArgMetaData(logger, newTypes);
recentTypes.clear();
}
@@ -437,11 +442,21 @@
}
/**
- * Returns an unmodifiable, live view of all interface types annotated with
- * the SingleJsoImpl annotation.
+ * Returns the single implementation type for an interface returned via
+ * {@link #getSingleJsoImplInterfaces()} or <code>null</code> if no JSO
+ * implementation is defined.
*/
- public Set<JRealClassType> getSingleJsoImplTypes() {
- return Collections.unmodifiableSet(singleJsoImplTypes);
+ public JClassType getSingleJsoImpl(JClassType intf) {
+ assert intf.isInterface() == intf;
+ return jsoSingleImpls.get(intf);
+ }
+
+ /**
+ * Returns an unmodifiable, live view of all interface types that are
+ * implemented by exactly one JSO subtype.
+ */
+ public Set<JClassType> getSingleJsoImplInterfaces() {
+ return Collections.unmodifiableSet(jsoSingleImpls.keySet());
}
/**
@@ -616,12 +631,6 @@
recentTypes.add(newType);
}
- void addSingleJsoInterface(JRealClassType singleJsoImplType) {
- assert singleJsoImplType.isInterface() == singleJsoImplType : singleJsoImplType.getName()
- + " has SingleJsoImpl, but is not an interface";
- singleJsoImplTypes.add(singleJsoImplType);
- }
-
void invalidate(JRealClassType realClassType) {
invalidatedTypes.add(realClassType);
}
@@ -635,6 +644,61 @@
}
}
+ /**
+ * Updates the list of jsoSingleImpl types from recently-added types.
+ */
+ private void computeSingleJsoImplData(TreeLogger logger, JClassType[] newTypes) {
+ JClassType jsoType = findType(JsValueGlue.JSO_CLASS);
+ if (jsoType == null) {
+ return;
+ }
+
+ for (JClassType type : newTypes) {
+ if (!jsoType.isAssignableFrom(type)) {
+ continue;
+ }
+
+ for (JClassType intf : JClassType.getFlattenedSuperTypeHierarchy(type)) {
+ if (intf.isInterface() == null) {
+ // Not an interface
+ continue;
+ }
+
+ if (intf.getOverridableMethods().length == 0) {
+ /*
+ * Record a tag interface as being implemented by JSO, since they
+ * don't actually have any methods and we want to avoid spurious
+ * messages about multiple JSO types implementing a common interface.
+ */
+ jsoSingleImpls.put(intf, jsoType);
+ continue;
+ }
+
+ /*
+ * If the previously-registered implementation type for a SingleJsoImpl
+ * interface is a subtype of the type we're currently looking at, we
+ * want to choose the least-derived class.
+ */
+ JClassType previousType = jsoSingleImpls.get(intf);
+ if (previousType == null) {
+ jsoSingleImpls.put(intf, type);
+ } else if (type.isAssignableFrom(previousType)) {
+ jsoSingleImpls.put(intf, type);
+ } else if (type.isAssignableTo(previousType)) {
+ // Do nothing
+ } else {
+ // This should have been taken care of by JSORetrictionsChecker
+ logger.log(TreeLogger.ERROR,
+ "Already seen an implementing JSO subtype ("
+ + previousType.getName() + ") for interface ("
+ + intf.getName() + ") while examining newly-added type ("
+ + type.getName() + "). This is a bug in "
+ + "JSORestrictionsChecker.");
+ }
+ }
+ }
+ }
+
private void consumeTypeArgMetaData(TreeLogger logger, JClassType[] types) {
if (GenUtil.warnAboutMetadata()) {
logger = logger.branch(
@@ -1000,7 +1064,7 @@
JRealClassType classType = iter.next();
String fqcn = classType.getQualifiedSourceName();
allTypes.remove(fqcn);
- singleJsoImplTypes.remove(classType);
+ jsoSingleImpls.remove(classType);
JPackage pkg = classType.getPackage();
if (pkg != null) {
pkg.remove(classType);
diff --git a/dev/core/src/com/google/gwt/dev/javac/JSORestrictionsChecker.java b/dev/core/src/com/google/gwt/dev/javac/JSORestrictionsChecker.java
index e1c3dff..1d56076 100644
--- a/dev/core/src/com/google/gwt/dev/javac/JSORestrictionsChecker.java
+++ b/dev/core/src/com/google/gwt/dev/javac/JSORestrictionsChecker.java
@@ -15,14 +15,12 @@
*/
package com.google.gwt.dev.javac;
-import com.google.gwt.core.client.SingleJsoImpl;
import com.google.gwt.dev.util.InstalledHelpInfo;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
-import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
@@ -37,7 +35,6 @@
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import java.util.Collection;
-import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
@@ -71,13 +68,6 @@
public static class CheckerState {
/**
- * This maps interfaces tagged with the SingleJsoImpl annotation to the
- * names of their declared super-interfaces.
- */
- private final Map<TypeDeclaration, Set<String>> interfacesToSuperInterfaces = new HashMap<TypeDeclaration, Set<String>>();
- private final Map<TypeDeclaration, Set<String>> trivialToSuperInterfaces = new HashMap<TypeDeclaration, Set<String>>();
-
- /**
* This maps JSO implementation types to their implemented SingleJsoImpl
* interfaces.
*/
@@ -89,63 +79,17 @@
private final Map<TypeDeclaration, CompilationUnitDeclaration> nodesToCuds = new IdentityHashMap<TypeDeclaration, CompilationUnitDeclaration>();
/**
- * A convenience for validity checking.
- */
- private final Set<String> singleJsoImplInterfaceNames = new HashSet<String>();
- private final Set<String> trivialInterfaceNames = new HashSet<String>();
-
- /**
- * Just used for sanity checking. Not populated unless assertions are on.
- */
- private final Set<String> regularInterfaceNames = getClass().desiredAssertionStatus()
- ? new HashSet<String>() : null;
-
- /**
* This method should be called after all CUDs are passed into check().
*/
public void finalCheck() {
/*
- * Compute all interfaces that are completely trivial.
- */
- Set<String> tagInterfaceNames = new HashSet<String>();
- for (Map.Entry<TypeDeclaration, Set<String>> entry : trivialToSuperInterfaces.entrySet()) {
- if (entry.getValue().isEmpty()
- || trivialInterfaceNames.containsAll(entry.getValue())) {
- tagInterfaceNames.add(CharOperation.toString(entry.getKey().binding.compoundName));
- }
- }
-
- /*
- * Make sure that all SingleJsoImpl interfaces extends only SingleJsoImpl
- * interfaces or tag interfaces.
- */
- for (Map.Entry<TypeDeclaration, Set<String>> entry : interfacesToSuperInterfaces.entrySet()) {
- TypeDeclaration node = entry.getKey();
-
- for (String intfName : entry.getValue()) {
- if (tagInterfaceNames.contains(intfName)
- || singleJsoImplInterfaceNames.contains(intfName)) {
- continue;
- } else {
- assert regularInterfaceNames.contains(intfName);
- errorOn(node, nodesToCuds.get(node), ERR_INTF_EXTENDS_BAD_INTF);
- }
- }
- }
-
- /*
- * Make sure that any interfaces implemented by the JSO types have the
- * SingleJsoImpl annotation. Also, check the one-and-only JSO
- * implementation of the interface types.
+ * Ensure that every interfaces has exactly zero or one JSO subtype that
+ * implements it.
*/
Map<String, TypeDeclaration> singleImplementations = new HashMap<String, TypeDeclaration>();
for (Map.Entry<TypeDeclaration, Set<String>> entry : jsoImplsToInterfaces.entrySet()) {
TypeDeclaration node = entry.getKey();
for (String intfName : entry.getValue()) {
- if (!singleJsoImplInterfaceNames.contains(intfName)) {
- errorOn(node, nodesToCuds.get(node),
- errInterfaceWithMethodsNoAnnotation(intfName));
- }
if (!singleImplementations.containsKey(intfName)) {
singleImplementations.put(intfName, node);
@@ -181,15 +125,8 @@
public void retainAll(Collection<CompilationUnit> units) {
// Fast-path for removing everything
if (units.isEmpty()) {
- interfacesToSuperInterfaces.clear();
- trivialToSuperInterfaces.clear();
jsoImplsToInterfaces.clear();
nodesToCuds.clear();
- singleJsoImplInterfaceNames.clear();
- trivialInterfaceNames.clear();
- if (regularInterfaceNames != null) {
- regularInterfaceNames.clear();
- }
return;
}
@@ -211,23 +148,9 @@
if (!retainedTypeNames.contains(CharOperation.toString(decl.binding.compoundName))) {
it.remove();
- assert interfacesToSuperInterfaces.containsKey(decl)
- || trivialToSuperInterfaces.containsKey(decl)
- || jsoImplsToInterfaces.containsKey(decl) : "TypeDeclaration "
- + CharOperation.toString(decl.binding.compoundName)
- + " in nodesToCuds, but not in any of the maps";
-
- interfacesToSuperInterfaces.remove(decl);
- trivialToSuperInterfaces.remove(decl);
jsoImplsToInterfaces.remove(decl);
}
}
-
- singleJsoImplInterfaceNames.retainAll(retainedTypeNames);
- trivialInterfaceNames.retainAll(retainedTypeNames);
- if (regularInterfaceNames != null) {
- regularInterfaceNames.retainAll(retainedTypeNames);
- }
}
private void add(Map<TypeDeclaration, Set<String>> map,
@@ -245,12 +168,6 @@
add(jsoImplsToInterfaces, jsoType, interfaceName);
}
- private void empty(Map<TypeDeclaration, Set<String>> map,
- TypeDeclaration key) {
- assert !map.containsKey(key);
- map.put(key, Collections.<String> emptySet());
- }
-
private boolean hasSupertypeNamed(TypeDeclaration type, char[][] qType) {
ReferenceBinding b = type.binding;
while (b != null) {
@@ -261,36 +178,6 @@
}
return false;
}
-
- private void jsoSingleImplInterface(TypeDeclaration type,
- CompilationUnitDeclaration cud, String superInterface) {
- nodesToCuds.put(type, cud);
- singleJsoImplInterfaceNames.add(CharOperation.toString(type.binding.compoundName));
- if (superInterface == null) {
- empty(interfacesToSuperInterfaces, type);
- } else {
- add(interfacesToSuperInterfaces, type, superInterface);
- }
- }
-
- private void regularInterface(TypeDeclaration intfType,
- CompilationUnitDeclaration cud, String superInterfaceName) {
- // Just used for sanity checking
- if (getClass().desiredAssertionStatus()) {
- regularInterfaceNames.add(CharOperation.toString(intfType.binding.compoundName));
- }
- }
-
- private void trivialInterface(TypeDeclaration intfType,
- CompilationUnitDeclaration cud, String superInterfaceName) {
- nodesToCuds.put(intfType, cud);
- trivialInterfaceNames.add(CharOperation.toString(intfType.binding.compoundName));
- if (superInterfaceName == null) {
- empty(trivialToSuperInterfaces, intfType);
- } else {
- add(trivialToSuperInterfaces, intfType, superInterfaceName);
- }
- }
}
private class JSORestrictionsVisitor extends ASTVisitor implements
@@ -387,64 +274,10 @@
}
private boolean checkType(TypeDeclaration type) {
- /*
- * See if we're looking at something tagged with a SingleJsoImpl
- * annotation.
- */
- if (type.annotations != null) {
- for (Annotation a : type.annotations) {
- if (!(a.resolvedType instanceof ReferenceBinding)) {
- continue;
- }
-
- ReferenceBinding refBinding = (ReferenceBinding) a.resolvedType;
- if (!SINGLE_JSO_IMPL_CLASS.equals(new String(
- refBinding.readableName()))) {
- continue;
- }
-
- if (!type.binding.isInterface()) {
- errorOn(type, ERR_ONLY_INTERFACES);
- continue;
- }
-
- // Interfaces should not have superclasses, just super-interfaces
- assert type.superclass == null;
-
- if (type.superInterfaces != null) {
- for (ReferenceBinding ref : type.binding.superInterfaces) {
- String superInterfaceName = CharOperation.toString(ref.compoundName);
- state.jsoSingleImplInterface(type, cud, superInterfaceName);
- }
- } else {
- state.jsoSingleImplInterface(type, cud, null);
- }
- }
- }
-
- if (type.binding.isInterface()) {
- boolean isTrivial = type.methods == null || type.methods.length == 0;
- if (type.superInterfaces != null) {
- for (ReferenceBinding ref : type.binding.superInterfaces) {
- String superInterfaceName = CharOperation.toString(ref.compoundName);
- if (isTrivial) {
- state.trivialInterface(type, cud, superInterfaceName);
- } else {
- state.regularInterface(type, cud, superInterfaceName);
- }
- }
- } else {
- if (isTrivial) {
- state.trivialInterface(type, cud, null);
- } else {
- state.regularInterface(type, cud, null);
- }
- }
- }
-
if (!isJsoSubclass(type.binding)) {
return false;
}
+
if (type.enclosingType != null && !type.binding.isStatic()) {
errorOn(type, ERR_IS_NONSTATIC_NESTED);
}
@@ -482,15 +315,12 @@
static final String ERR_CONSTRUCTOR_WITH_PARAMETERS = "Constructors must not have parameters in subclasses of JavaScriptObject";
static final String ERR_INSTANCE_FIELD = "Instance fields cannot be used in subclasses of JavaScriptObject";
static final String ERR_INSTANCE_METHOD_NONFINAL = "Instance methods must be 'final' in non-final subclasses of JavaScriptObject";
- static final String ERR_INTF_EXTENDS_BAD_INTF = "SingleJsoImpl interfaces must only extend other SingleJsoImpl interfaces or tag interfaces";
static final String ERR_IS_NONSTATIC_NESTED = "Nested classes must be 'static' if they extend JavaScriptObject";
static final String ERR_NEW_JSO = "'new' cannot be used to create instances of JavaScriptObject subclasses; instances must originate in JavaScript";
static final String ERR_NONEMPTY_CONSTRUCTOR = "Constructors must be totally empty in subclasses of JavaScriptObject";
static final String ERR_NONPROTECTED_CONSTRUCTOR = "Constructors must be 'protected' in subclasses of JavaScriptObject";
- static final String ERR_ONLY_INTERFACES = "SingleJsoImpl may only be applied to interface types";
static final String ERR_OVERRIDDEN_METHOD = "Methods cannot be overridden in JavaScriptObject subclasses";
static final String JSO_CLASS = "com/google/gwt/core/client/JavaScriptObject";
- static final String SINGLE_JSO_IMPL_CLASS = SingleJsoImpl.class.getName();
/**
* Checks an entire
@@ -504,16 +334,11 @@
static String errAlreadyImplemented(String intfName, String impl1,
String impl2) {
- return "Only one JavaScriptObject type may implement the methods of a "
- + "@SingleJsoImpl type interface. The interface (" + intfName
+ return "Only one JavaScriptObject type may implement the methods of an "
+ + "interface that declared methods. The interface (" + intfName
+ ") is implemented by both (" + impl1 + ") and (" + impl2 + ")";
}
- static String errInterfaceWithMethodsNoAnnotation(String intfName) {
- return "JavaScriptObject classes cannot implement interfaces with methods ("
- + intfName + ") that do not define the @SingleJsoImpl annotation";
- }
-
private static void errorOn(ASTNode node, CompilationUnitDeclaration cud,
String error) {
GWTProblem.recordInCud(node, cud, error, new InstalledHelpInfo(
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 c23140d..cdbd1de 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
@@ -665,10 +665,16 @@
continue;
}
JInterfaceType intr = (JInterfaceType) refType;
+
if (intr.methods.size() <= 1) {
- // Ignore tag interfaces
+ /*
+ * Record a tag interface as being implemented by JSO, since they
+ * don't actually have any methods and we want to avoid spurious
+ * messages about multiple JSO types implementing a common interface.
+ */
assert intr.methods.size() == 0
|| intr.methods.get(0).getName().equals("$clinit");
+ jsoSingleImpls.put(intr, program.getJavaScriptObject());
continue;
}
@@ -678,6 +684,7 @@
if (allAssignableFrom(alreadySeen).contains(type)) {
jsoSingleImpls.put(intr, (JClassType) type);
+
} else {
assert allAssignableFrom(type).contains(alreadySeen) : "Already recorded "
+ alreadySeen.getName()
diff --git a/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java b/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
index eaa4575..48a46d5 100644
--- a/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
+++ b/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
@@ -22,13 +22,14 @@
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JParameter;
+import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
import com.google.gwt.core.ext.typeinfo.JType;
-import com.google.gwt.core.ext.typeinfo.NotFoundException;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.dev.javac.CompilationState;
import com.google.gwt.dev.javac.CompilationUnit;
import com.google.gwt.dev.javac.CompiledClass;
import com.google.gwt.dev.javac.JsniMethod;
+import com.google.gwt.dev.jjs.InternalCompilerException;
import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter;
import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.InstanceMethodOracle;
import com.google.gwt.dev.util.JsniRef;
@@ -813,7 +814,7 @@
SortedMap<String, com.google.gwt.dev.asm.commons.Method> mangledNamesToImplementations) {
// Loop over all types declared with the SingleJsoImpl annotation
- typeLoop : for (JClassType type : typeOracle.getSingleJsoImplTypes()) {
+ typeLoop : for (JClassType type : typeOracle.getSingleJsoImplInterfaces()) {
assert type.isInterface() == type : "Expecting interfaces only";
/*
@@ -842,11 +843,10 @@
*
* @SingleJsoImpl interface C extends A, B {}
*
- * Methods interited from interfaces A and B must be dispatched to their
+ * Methods inherited from interfaces A and B must be dispatched to their
* respective JSO implementations.
*/
- JClassType implementingType = findImplementingJsoType(m,
- m.getEnclosingType().getSubtypes());
+ JClassType implementingType = typeOracle.getSingleJsoImpl(m.getEnclosingType());
if (implementingType == null) {
/*
@@ -880,11 +880,11 @@
*
* This must be kept in sync with the WriteJsoImpl class.
*/
- String decl = m.getReturnType().getParameterizedQualifiedSourceName()
- + " " + m.getName() + "$ (" + getBinaryName(implementingType);
+ String decl = getBinaryOrPrimitiveName(m.getReturnType()) + " "
+ + m.getName() + "$ (" + getBinaryOrPrimitiveName(implementingType);
for (JParameter p : m.getParameters()) {
decl += ",";
- decl += p.getType().getParameterizedQualifiedSourceName();
+ decl += getBinaryOrPrimitiveName(p.getType());
}
decl += ")";
@@ -992,41 +992,25 @@
return classBytes;
}
- private JClassType findImplementingJsoType(JMethod toImplement,
- JClassType[] types) {
- JClassType jsoType = typeOracle.findType(JsValueGlue.JSO_CLASS);
- JType[] paramTypes = new JType[toImplement.getParameters().length];
- for (int i = 0, j = paramTypes.length; i < j; i++) {
- paramTypes[i] = toImplement.getParameters()[i].getType();
- }
-
- for (JClassType type : types) {
- if (type == null) {
- continue;
- } else if (type.isAbstract()) {
- continue;
- } else if (!type.isAssignableTo(jsoType)) {
- continue;
- } else {
- try {
- JMethod m = type.getMethod(toImplement.getName(), paramTypes);
- if (!m.isAbstract()) {
- return type;
- }
- } catch (NotFoundException e) {
- // Ignore
- }
- }
- }
- return null;
- }
-
private String getBinaryName(JClassType type) {
String name = type.getPackage().getName() + '.';
name += type.getName().replace('.', '$');
return name;
}
+ private String getBinaryOrPrimitiveName(JType type) {
+ JClassType asClass = type.isClassOrInterface();
+ JPrimitiveType asPrimitive = type.isPrimitive();
+ if (asClass != null) {
+ return getBinaryName(asClass);
+ } else if (asPrimitive != null) {
+ return asPrimitive.getQualifiedSourceName();
+ } else {
+ throw new InternalCompilerException("Cannot create binary name for "
+ + type.getQualifiedSourceName());
+ }
+ }
+
/**
* Returns the compilationUnit corresponding to the className. For nested
* classes, the unit corresponding to the top level type is returned.
diff --git a/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteSingleJsoImplDispatches.java b/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteSingleJsoImplDispatches.java
index adb6326..b08e667 100644
--- a/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteSingleJsoImplDispatches.java
+++ b/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteSingleJsoImplDispatches.java
@@ -221,6 +221,10 @@
mv.visitVarInsn(t.getOpcode(Opcodes.ILOAD), var);
var += t.getSize();
}
+
+ // Make sure there's enough room for the return value
+ size = Math.max(size, toCall.getReturnType().getSize());
+
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, currentTypeName,
toCall.getName(), toCall.getDescriptor());
mv.visitInsn(toCall.getReturnType().getOpcode(Opcodes.IRETURN));
diff --git a/dev/core/src/com/google/gwt/dev/shell/rewrite/WriteJsoImpl.java b/dev/core/src/com/google/gwt/dev/shell/rewrite/WriteJsoImpl.java
index 4f1e265..4adf11c 100644
--- a/dev/core/src/com/google/gwt/dev/shell/rewrite/WriteJsoImpl.java
+++ b/dev/core/src/com/google/gwt/dev/shell/rewrite/WriteJsoImpl.java
@@ -150,6 +150,10 @@
mv.visitVarInsn(t.getOpcode(Opcodes.ILOAD), var);
var += t.getSize();
}
+
+ // Make sure there's enough room for the return value
+ size = Math.max(size, implementingMethod.getReturnType().getSize());
+
mv.visitMethodInsn(Opcodes.INVOKESTATIC,
implementingType.getInternalName(), implementingMethod.getName(),
implementingMethod.getDescriptor());
diff --git a/dev/core/super/com/google/gwt/core/client/SingleJsoImpl.java b/dev/core/super/com/google/gwt/core/client/SingleJsoImpl.java
deleted file mode 100644
index bbc2a55..0000000
--- a/dev/core/super/com/google/gwt/core/client/SingleJsoImpl.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2009 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.core.client;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Target;
-
-/**
- * The presence of this annotation on an interface type allows one
- * JavaScriptObject subclass to implement the interface. Any number of types
- * that are not derived from JavaScriptObject may also implement the interface.
- * <p>
- * The use of the SingleJsoImpl annotation is subject to the following
- * restrictions:
- * <ul>
- * <li>Must be applied only to interface types</li>
- * <li>Any super-interfaces of the annotated interface must also be annotated
- * with the SingleJsoImpl</li>
- * </ul>
- */
-@Documented
-@Target(ElementType.TYPE)
-public @interface SingleJsoImpl {
-}
\ No newline at end of file
diff --git a/dev/core/test/com/google/gwt/dev/javac/JSORestrictionsTest.java b/dev/core/test/com/google/gwt/dev/javac/JSORestrictionsTest.java
index 836dca5..34acec3 100644
--- a/dev/core/test/com/google/gwt/dev/javac/JSORestrictionsTest.java
+++ b/dev/core/test/com/google/gwt/dev/javac/JSORestrictionsTest.java
@@ -33,21 +33,6 @@
}
}
- public void testAnnotationOnClass() {
- StringBuffer buggyCode = new StringBuffer();
- buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
- buggyCode.append("import com.google.gwt.core.client.SingleJsoImpl;\n");
- buggyCode.append("public class Buggy {\n");
- buggyCode.append(" @SingleJsoImpl \n");
- buggyCode.append(" static class Squeaks {\n");
- buggyCode.append(" public void squeak() {}\n");
- buggyCode.append(" }\n");
- buggyCode.append("}\n");
-
- shouldGenerateError(buggyCode, "Line 5: "
- + JSORestrictionsChecker.ERR_ONLY_INTERFACES);
- }
-
public void testFinalClass() {
StringBuffer code = new StringBuffer();
code.append("import com.google.gwt.core.client.JavaScriptObject;\n");
@@ -62,13 +47,10 @@
public void testImplementsInterfaces() {
StringBuffer goodCode = new StringBuffer();
goodCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
- goodCode.append("import com.google.gwt.core.client.SingleJsoImpl;\n");
goodCode.append("public class Buggy {\n");
- goodCode.append(" @SingleJsoImpl\n");
goodCode.append(" static interface Squeaks {\n");
goodCode.append(" public void squeak();\n");
goodCode.append(" }\n");
- goodCode.append(" @SingleJsoImpl \n");
goodCode.append(" static interface Squeaks2 extends Squeaks {\n");
goodCode.append(" public void squeak();\n");
goodCode.append(" public void squeak2();\n");
@@ -112,9 +94,7 @@
public void testMultipleImplementations() {
StringBuffer buggyCode = new StringBuffer();
buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
- buggyCode.append("import com.google.gwt.core.client.SingleJsoImpl;\n");
buggyCode.append("public class Buggy {\n");
- buggyCode.append(" @SingleJsoImpl\n");
buggyCode.append(" static interface Squeaks {\n");
buggyCode.append(" public void squeak();\n");
buggyCode.append(" }\n");
@@ -128,9 +108,9 @@
buggyCode.append(" }\n");
buggyCode.append("}\n");
- shouldGenerateError(buggyCode, "Line 8: "
+ shouldGenerateError(buggyCode, "Line 6: "
+ JSORestrictionsChecker.errAlreadyImplemented("Buggy$Squeaks",
- "Buggy$Squeaker", "Buggy$Squeaker2"), "Line 12: "
+ "Buggy$Squeaker", "Buggy$Squeaker2"), "Line 10: "
+ JSORestrictionsChecker.errAlreadyImplemented("Buggy$Squeaks",
"Buggy$Squeaker", "Buggy$Squeaker2"));
}
@@ -152,13 +132,10 @@
public void testNoAnnotationOnInterfaceSubtype() {
StringBuffer goodCode = new StringBuffer();
goodCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
- goodCode.append("import com.google.gwt.core.client.SingleJsoImpl;\n");
goodCode.append("public class Buggy {\n");
- goodCode.append(" @SingleJsoImpl\n");
goodCode.append(" static interface Squeaks {\n");
goodCode.append(" public void squeak();\n");
goodCode.append(" }\n");
- goodCode.append(" /* @SingleJsoImpl */\n");
goodCode.append(" static interface Sub extends Squeaks {\n");
goodCode.append(" }\n");
goodCode.append("}\n");
@@ -166,91 +143,6 @@
shouldGenerateNoError(goodCode);
}
- public void testNoAnnotationOnInterfaceSupertype() {
- StringBuffer buggyCode = new StringBuffer();
- buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
- buggyCode.append("import com.google.gwt.core.client.SingleJsoImpl;\n");
- buggyCode.append("public class Buggy {\n");
- buggyCode.append(" /* @SingleJsoImpl */ \n");
- buggyCode.append(" static interface Squeaks {\n");
- buggyCode.append(" public void squeak();\n");
- buggyCode.append(" }\n");
- buggyCode.append(" @SingleJsoImpl \n");
- buggyCode.append(" static interface Sub extends Squeaks {\n");
- buggyCode.append(" }\n");
- buggyCode.append("}\n");
-
- shouldGenerateError(buggyCode, "Line 9: "
- + JSORestrictionsChecker.ERR_INTF_EXTENDS_BAD_INTF);
- }
-
- public void testNoAnnotationOnInterfaceSupertypeSandwich() {
- StringBuffer buggyCode = new StringBuffer();
- buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
- buggyCode.append("import com.google.gwt.core.client.SingleJsoImpl;\n");
- buggyCode.append("public class Buggy {\n");
- buggyCode.append(" @SingleJsoImpl\n");
- buggyCode.append(" static interface Squeaks {\n");
- buggyCode.append(" public void squeak();\n");
- buggyCode.append(" }\n");
- buggyCode.append(" /* @SingleJsoImpl */\n");
- buggyCode.append(" static interface Squeaks2 extends Squeaks {\n");
- buggyCode.append(" public void squeak2();\n");
- buggyCode.append(" }\n");
- buggyCode.append(" @SingleJsoImpl\n");
- buggyCode.append(" static interface Squeaks3 extends Squeaks2 {\n");
- buggyCode.append(" public void squeak3();\n");
- buggyCode.append(" }\n");
- buggyCode.append("}\n");
-
- shouldGenerateError(buggyCode, "Line 13: "
- + JSORestrictionsChecker.ERR_INTF_EXTENDS_BAD_INTF);
- }
-
- public void testNoAnnotationOnOtherwiseValidInterfaces() {
- StringBuffer buggyCode = new StringBuffer();
- buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
- buggyCode.append("public class Buggy {\n");
- buggyCode.append(" static interface Squeaks {\n");
- buggyCode.append(" public void squeak();\n");
- buggyCode.append(" }\n");
- buggyCode.append(" static class Squeaker extends JavaScriptObject implements Squeaks {\n");
- buggyCode.append(" public final void squeak() { }\n");
- buggyCode.append(" protected Squeaker() { }\n");
- buggyCode.append(" }\n");
- buggyCode.append("}\n");
-
- shouldGenerateError(
- buggyCode,
- "Line 6: "
- + JSORestrictionsChecker.errInterfaceWithMethodsNoAnnotation(("Buggy$Squeaks")));
- }
-
- public void testNoAnnotationOnOtherwiseValidDerivedInterfaces() {
- StringBuffer buggyCode = new StringBuffer();
- buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
- buggyCode.append("import com.google.gwt.core.client.SingleJsoImpl;\n");
- buggyCode.append("public class Buggy {\n");
- buggyCode.append(" @SingleJsoImpl\n");
- buggyCode.append(" static interface Squeaks {\n");
- buggyCode.append(" public void squeak();\n");
- buggyCode.append(" }\n");
- buggyCode.append(" static interface Squeaks2 extends Squeaks {\n");
- buggyCode.append(" public void squeak2();\n");
- buggyCode.append(" }\n");
- buggyCode.append(" static class Squeaker extends JavaScriptObject implements Squeaks2 {\n");
- buggyCode.append(" public final void squeak() { }\n");
- buggyCode.append(" public final void squeak2() { }\n");
- buggyCode.append(" protected Squeaker() { }\n");
- buggyCode.append(" }\n");
- buggyCode.append("}\n");
-
- shouldGenerateError(
- buggyCode,
- "Line 11: "
- + JSORestrictionsChecker.errInterfaceWithMethodsNoAnnotation(("Buggy$Squeaks2")));
- }
-
public void testNoConstructor() {
StringBuffer buggyCode = new StringBuffer();
buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
@@ -288,9 +180,7 @@
public void testNonJsoInterfaceExtension() {
StringBuffer goodCode = new StringBuffer();
goodCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
- goodCode.append("import com.google.gwt.core.client.SingleJsoImpl;\n");
goodCode.append("public class Buggy {\n");
- goodCode.append(" @SingleJsoImpl\n");
goodCode.append(" static interface Squeaks {\n");
goodCode.append(" public void squeak();\n");
goodCode.append(" }\n");
@@ -361,11 +251,9 @@
public void testTagInterfaces() {
StringBuffer goodCode = new StringBuffer();
goodCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
- goodCode.append("import com.google.gwt.core.client.SingleJsoImpl;\n");
goodCode.append("public class Buggy {\n");
goodCode.append(" static interface Tag {}\n");
goodCode.append(" static interface Tag2 extends Tag {}\n");
- goodCode.append(" @SingleJsoImpl \n");
goodCode.append(" static interface IntrExtendsTag extends Tag2 {\n");
goodCode.append(" public void intrExtendsTag();\n");
goodCode.append(" }\n");
diff --git a/distro-source/core/src/doc/helpInfo/jsoRestrictions.html b/distro-source/core/src/doc/helpInfo/jsoRestrictions.html
index 0354e56..b3ef73f 100644
--- a/distro-source/core/src/doc/helpInfo/jsoRestrictions.html
+++ b/distro-source/core/src/doc/helpInfo/jsoRestrictions.html
@@ -15,28 +15,21 @@
explicitly final, a member of a final class, or private. <em>Methods
of JSO classes cannot be overridden, because calls to such methods
could require dynamic dispatch.</em></li>
- <li>JSO classes may implement interfaces that define methods, but
- only if those interfaces are annotated with <code>@SingleJsoImpl</code>.
- <em>This ensures that polymorphic dispatch via a <code>@SingleJsoImpl</code>
- interface can be statically resolved to exactly one implementing JSO
- subtype.</em>
+ <li>An interface type may be implemented by at most one JSO
+ subtype. <em>This ensures that polymorphic dispatch via a
+ "SingleJsoImpl" interface can be statically resolved to exactly one
+ implementing JSO subtype.</em>
<ol>
- <li>A <code>@SingleJsoImpl</code> interface may only extend
- "tag" interfaces that declare no methods or other interfaces
- annotated with <code>@SingleJsoImpl</code>.</li>
- <li>It is valid for a <code>@SingleJsoImpl</code> interface to
- be extended by an interface that is not annotated in this fashion,
- however the sub-interface may not be implemented by any <code>JavaScriptObjects</code>.</li>
- <li>A <code>JavaScriptObject</code> that implements a <code>@SingleJsoImpl</code>
- interface may be further extended. The subclasses may implement
- additional <code>@SingleJsoImpl</code> interfaces. <em>The
- methods on a JSO must be effectively final, so each <code>@SingleJsoImpl</code>
+ <li>A <code>JavaScriptObject</code> that implements a
+ "SingleJsoImpl" interface may be further extended. The subclasses
+ may implement additional "SingleJsoImpl" interfaces. <em>The
+ methods on a JSO must be effectively final, so each "SingleJsoImpl"
method still has a 1:1 mapping to a method defined within a JSO
subtype.</em></li>
<li>It is valid for any number of any non-<code>JavaScriptObject</code>
- types to implement a <code>@SingleJsoImpl</code> interface. <em>There
- is a runtime dispatch penalty when a <code>@SingleJsoImpl</code>
- interface is implemented by both JSO and non-JSO types.</em></li>
+ types to implement a "SingleJsoImpl" interface. <em>There is a
+ slight runtime dispatch penalty when a "SingleJsoImpl" interface is
+ implemented by both JSO and non-JSO types.</em></li>
</ol>
</li>
<li>No instance methods on JSO classes may override another
diff --git a/user/test/com/google/gwt/dev/jjs/test/SingleJsoImplTest.java b/user/test/com/google/gwt/dev/jjs/test/SingleJsoImplTest.java
index 0914f54..7edef96 100644
--- a/user/test/com/google/gwt/dev/jjs/test/SingleJsoImplTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/SingleJsoImplTest.java
@@ -16,7 +16,7 @@
package com.google.gwt.dev.jjs.test;
import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.SingleJsoImpl;
+import com.google.gwt.dev.jjs.test.SingleJsoImplTest.JsoHasInnerJsoType.InnerType;
import com.google.gwt.junit.client.GWTTestCase;
import java.io.IOException;
@@ -26,7 +26,6 @@
*/
public class SingleJsoImplTest extends GWTTestCase {
- @SingleJsoImpl
interface Adder {
int ADDER_CONST = 6;
@@ -44,9 +43,28 @@
* stack, so this ensures that we're handling that case correctly.
*/
double add(double a, int b);
+
+ /*
+ * Ensure that we can return types whose sizes are larger than the size of
+ * the arguments.
+ */
+ long returnLong();
}
- @SingleJsoImpl
+ interface CallsMethodInInnerType {
+ interface InnerInterface {
+ void call(int a);
+
+ int get();
+ }
+
+ InnerInterface call(InnerInterface o, int a);
+ }
+
+ interface CallsStaticMethodInSubclass {
+ String call(int a, int b);
+ }
+
interface Divider extends Multiplier {
int divide(int a, int b);
}
@@ -55,13 +73,17 @@
public double add(double a, int b) {
return a + b;
}
+
+ public long returnLong() {
+ return 5L;
+ }
}
/**
* The extra declaration of implementing Multiplier should still be legal.
*/
static class JavaDivider extends JavaMultiplier implements Divider,
- Multiplier {
+ Multiplier, Tag {
public int divide(int a, int b) {
return a / b;
}
@@ -87,28 +109,82 @@
protected JsoAdder() {
}
- public final native double add(double a, int b) /*-{return this.offset * (a + b);}-*/;
+ public final native double add(double a, int b) /*-{
+ return this.offset * (a + b);
+ }-*/;
+
+ public final long returnLong() {
+ return 5L;
+ }
}
- static class JsoDivider extends JsoMultiplier implements Divider {
+ static class JsoCallsStaticMethodInSubclass extends JavaScriptObject
+ implements CallsStaticMethodInSubclass {
+ protected JsoCallsStaticMethodInSubclass() {
+ }
+
+ public final native String call(int a, int b) /*-{
+ return "foo" + @com.google.gwt.dev.jjs.test.SingleJsoImplTest.JsoCallsStaticMethodInSubclassSubclass::actual(II)(a, b);
+ }-*/;
+ }
+
+ static class JsoCallsStaticMethodInSubclassSubclass extends
+ JsoCallsStaticMethodInSubclass {
+ public static String actual(int a, int b) {
+ return String.valueOf(a + b);
+ }
+
+ protected JsoCallsStaticMethodInSubclassSubclass() {
+ }
+ }
+
+ static class JsoDivider extends JsoMultiplier implements Divider, Tag {
protected JsoDivider() {
}
- public final native int divide(int a, int b) /*-{return this.offset * a / b;}-*/;
+ public final native int divide(int a, int b) /*-{
+ return this.offset * a / b;
+ }-*/;
+ }
+
+ static class JsoHasInnerJsoType extends JavaScriptObject implements
+ CallsMethodInInnerType {
+ static class InnerType extends JavaScriptObject implements InnerInterface {
+ protected InnerType() {
+ }
+
+ public final native void call(int a) /*-{
+ this.foo = a;
+ }-*/;
+
+ public final native int get() /*-{
+ return this.foo;
+ }-*/;
+ }
+
+ protected JsoHasInnerJsoType() {
+ }
+
+ public final InnerInterface call(InnerInterface o, int a) {
+ o.call(a);
+ return o;
+ }
}
static class JsoMultiplier extends JavaScriptObject implements Multiplier {
protected JsoMultiplier() {
}
- public final native int multiply(int a, int b) /*-{return this.offset * a * b;}-*/;
+ public final native int multiply(int a, int b) /*-{
+ return this.offset * a * b;
+ }-*/;
}
/**
* Just a random JSO type for testing cross-casting.
*/
final static class JsoRandom extends JavaScriptObject implements
- SameDescriptors {
+ SameDescriptors, Tag {
protected JsoRandom() {
}
@@ -154,7 +230,6 @@
double log2(int a);
}
- @SingleJsoImpl
interface Multiplier {
int multiply(int a, int b);
}
@@ -163,7 +238,6 @@
* This interface makes sure that types with identical method signatures will
* still dispatch correctly.
*/
- @SingleJsoImpl
interface SameDescriptors {
int SAME_NAME = 6;
@@ -174,7 +248,6 @@
int multiply(int a, int b);
}
- @SingleJsoImpl
interface Simple {
String a();
@@ -192,24 +265,41 @@
}
}
- @SingleJsoImpl
interface SimpleOnlyJavaInterface {
String simpleOnlyJava();
}
- private static native JsoAdder makeAdder(int offset) /*-{return {offset:offset};}-*/;
+ interface Tag {
+ }
- private static native JsoDivider makeDivider(int offset) /*-{return {offset:offset};}-*/;
+ private static native JsoAdder makeAdder(int offset) /*-{
+ return {offset:offset};
+ }-*/;
- private static native JsoMultiplier makeMultiplier(int offset) /*-{return {offset:offset};}-*/;
+ private static native JsoDivider makeDivider(int offset) /*-{
+ return {offset:offset};
+ }-*/;
- private static native JsoSimple makeSimple() /*-{return {};}-*/;
+ private static native JsoMultiplier makeMultiplier(int offset) /*-{
+ return {offset:offset};
+ }-*/;
+
+ private static native JsoSimple makeSimple() /*-{
+ return {};
+ }-*/;
@Override
public String getModuleName() {
return "com.google.gwt.dev.jjs.CompilerSuite";
}
+ public void testCallsToInnerTypes() {
+ CallsMethodInInnerType a = (CallsMethodInInnerType) JavaScriptObject.createObject();
+ InnerType i = (InnerType) JavaScriptObject.createObject();
+ assertEquals(5, a.call(i, 5).get());
+ assertEquals(5, i.get());
+ }
+
public void testDualCase() {
// Direct dispatch
{
@@ -218,6 +308,7 @@
JsoAdder jso = makeAdder(2);
assertEquals(4.0, jso.add(1, 1));
+ assertEquals(5L, jso.returnLong());
}
// Just check dispatch via the interface
@@ -227,6 +318,7 @@
a = makeAdder(2);
assertEquals(4.0, a.add(1, 1));
+ assertEquals(5L, a.returnLong());
}
// Check casting
@@ -234,8 +326,11 @@
Object a = new JavaAdder();
assertEquals(2.0, ((Adder) a).add(1, 1));
assertEquals(2.0, ((JavaAdder) a).add(1, 1));
+ assertEquals(5L, ((Adder) a).returnLong());
+ assertEquals(5L, ((JavaAdder) a).returnLong());
assertTrue(a instanceof JavaAdder);
assertFalse(a instanceof JsoAdder);
+ assertFalse(a instanceof Tag);
try {
((JsoAdder) a).add(1, 1);
fail("Should have thrown CCE");
@@ -246,8 +341,12 @@
a = makeAdder(2);
assertEquals(4.0, ((Adder) a).add(1, 1));
assertEquals(4.0, ((JsoAdder) a).add(1, 1));
+ assertEquals(5L, ((Adder) a).returnLong());
+ assertEquals(5L, ((JsoAdder) a).returnLong());
assertTrue(a instanceof JsoAdder);
assertFalse(a instanceof JavaAdder);
+ // NB: This is unexpected until you consider JSO$ as a roll-up type
+ assertTrue(a instanceof Tag);
try {
((JavaAdder) a).add(1, 1);
fail("Should have thrown CCE");
@@ -334,12 +433,21 @@
}
}
+ public void testStaticCallsToSubclasses() {
+ Object o = "String";
+ assertEquals(String.class, o.getClass());
+ o = JavaScriptObject.createObject();
+ assertTrue(o instanceof CallsStaticMethodInSubclass);
+ assertEquals("foo5", ((CallsStaticMethodInSubclass) o).call(2, 3));
+ }
+
@SuppressWarnings("cast")
public void testSubclassing() {
{
JsoDivider d = makeDivider(1);
assertTrue(d instanceof Divider);
assertTrue(d instanceof Multiplier);
+ assertTrue(d instanceof Tag);
assertEquals(5, d.divide(10, 2));
assertEquals(10, d.multiply(5, 2));
assertEquals(10, ((JsoMultiplier) d).multiply(5, 2));
@@ -353,6 +461,7 @@
assertTrue(d instanceof JsoMultiplier);
assertFalse(d instanceof JavaDivider);
assertFalse(d instanceof JavaMultiplier);
+ assertTrue(d instanceof Tag);
assertEquals(5, ((Divider) d).divide(10, 2));
assertEquals(10, ((Divider) d).multiply(5, 2));
@@ -367,6 +476,7 @@
assertTrue(d instanceof JavaMultiplier);
assertFalse(d instanceof JsoDivider);
assertFalse(d instanceof JsoMultiplier);
+ assertTrue(d instanceof Tag);
assertEquals(5, ((Divider) d).divide(10, 2));
assertEquals(10, ((Divider) d).multiply(5, 2));