Fix SingleJsoImpl hosted mode crash with contravariant return types in virtual
override scenario.
Put simply
class B extends A{}
interface I {
A returnsA();
}
class Jso extends JavaScriptObject {
B returnsA();
}
class JsoSub extends Jso implements I {}
crashes in CCL$MyInstanceMethodOracle.findOriginalDeclaringClass().
Web mode already handles this correctly.
Patch by: bobv
Review by: scottb
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@6412 8db76d5a-ed1c-0410-87a9-c151d255dfc7
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 14f1a4c..61e9cea 100644
--- a/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
+++ b/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
@@ -35,7 +35,9 @@
import com.google.gwt.dev.shell.rewrite.HasAnnotation;
import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter;
import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.InstanceMethodOracle;
+import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.SingleJsoImplData;
import com.google.gwt.dev.util.JsniRef;
+import com.google.gwt.dev.util.Name;
import com.google.gwt.dev.util.Util;
import com.google.gwt.dev.util.Name.InternalName;
import com.google.gwt.dev.util.Name.SourceOrBinaryName;
@@ -61,9 +63,9 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.SortedMap;
+import java.util.SortedSet;
import java.util.Stack;
-import java.util.TreeMap;
+import java.util.TreeSet;
/**
* An isolated {@link ClassLoader} for running all user code. All user files are
@@ -360,21 +362,34 @@
private final Map<String, Set<JClassType>> signatureToDeclaringClasses = new HashMap<String, Set<JClassType>>();
public MyInstanceMethodOracle(Set<JClassType> jsoTypes,
- JClassType javaLangObject) {
- // Populate the map.
+ JClassType javaLangObject, SingleJsoImplData jsoData) {
+
+ // Record that the JSO implements its own methods
for (JClassType type : jsoTypes) {
for (JMethod method : type.getMethods()) {
if (!method.isStatic()) {
- String signature = createSignature(method);
- Set<JClassType> declaringClasses = signatureToDeclaringClasses.get(signature);
- if (declaringClasses == null) {
- declaringClasses = new HashSet<JClassType>();
- signatureToDeclaringClasses.put(signature, declaringClasses);
- }
- declaringClasses.add(type);
+ assert !method.isAbstract() : "Abstract method in JSO type "
+ + method;
+ add(type, method);
}
}
}
+
+ /*
+ * Record the implementing types for methods defined in SingleJsoImpl
+ * interfaces. We have to make this pass because of possible variance in
+ * the return types between the abstract method declaration in the
+ * interface and the concrete method.
+ */
+ for (String intfName : jsoData.getSingleJsoIntfTypes()) {
+ // We only store the name in the data block to keep it lightweight
+ JClassType intf = typeOracle.findType(Name.InternalName.toSourceName(intfName));
+ JClassType jso = typeOracle.getSingleJsoImpl(intf);
+ for (JMethod method : intf.getMethods()) {
+ add(jso, method);
+ }
+ }
+
// Object clobbers everything.
for (JMethod method : javaLangObject.getMethods()) {
if (!method.isStatic()) {
@@ -389,6 +404,7 @@
public String findOriginalDeclaringClass(String desc, String signature) {
// Lookup the method.
Set<JClassType> declaringClasses = signatureToDeclaringClasses.get(signature);
+ assert declaringClasses != null : "No classes for " + signature;
if (declaringClasses.size() == 1) {
// Shortcut: if there's only one answer, it must be right.
return createDescriptor(declaringClasses.iterator().next());
@@ -413,6 +429,20 @@
+ signature + "' from class '" + desc + "'");
}
+ /**
+ * Record that a given JSO type contains the concrete implementation of a
+ * (possibly abstract) method.
+ */
+ private void add(JClassType type, JMethod method) {
+ String signature = createSignature(method);
+ Set<JClassType> declaringClasses = signatureToDeclaringClasses.get(signature);
+ if (declaringClasses == null) {
+ declaringClasses = new HashSet<JClassType>();
+ signatureToDeclaringClasses.put(signature, declaringClasses);
+ }
+ declaringClasses.add(type);
+ }
+
private String createDescriptor(JClassType type) {
String jniSignature = type.getJNISignature();
return jniSignature.substring(1, jniSignature.length() - 1);
@@ -432,6 +462,177 @@
}
/**
+ * Cook up the data we need to support JSO subtypes that implement interfaces
+ * with methods. This includes the set of SingleJsoImpl interfaces actually
+ * implemented by a JSO type, the mangled method names, and the names of the
+ * Methods that should actually implement the virtual functions.
+ *
+ * Given the current implementation of JSO$ and incremental execution of
+ * rebinds, it's not possible for Generators to produce additional
+ * JavaScriptObject subtypes, so this data can remain static.
+ */
+ private class MySingleJsoImplData implements SingleJsoImplData {
+ private final SortedSet<String> mangledNames = new TreeSet<String>();
+ private final Map<String, com.google.gwt.dev.asm.commons.Method> mangledNamesToDeclarations = new HashMap<String, com.google.gwt.dev.asm.commons.Method>();
+ private final Map<String, com.google.gwt.dev.asm.commons.Method> mangledNamesToImplementations = new HashMap<String, com.google.gwt.dev.asm.commons.Method>();
+ private final SortedSet<String> unmodifiableNames = Collections.unmodifiableSortedSet(mangledNames);
+ private final Set<String> unmodifiableIntfNames = Collections.unmodifiableSet(singleJsoImplTypes);
+
+ public MySingleJsoImplData() {
+ // Loop over all interfaces with JSO implementations
+ typeLoop : for (JClassType type : typeOracle.getSingleJsoImplInterfaces()) {
+ assert type.isInterface() == type : "Expecting interfaces only";
+
+ /*
+ * By preemptively adding all possible mangled names by which a method
+ * could be called, we greatly simplify the logic necessary to rewrite
+ * the call-site.
+ *
+ * interface A {void m();}
+ *
+ * interface B extends A {void z();}
+ *
+ * becomes
+ *
+ * c_g_p_A_m() -> JsoA$.m$()
+ *
+ * c_g_p_B_m() -> JsoA$.m$()
+ *
+ * c_g_p_B_z() -> JsoB$.z$()
+ */
+ for (JMethod intfMethod : type.getOverridableMethods()) {
+ assert intfMethod.isAbstract() : "Expecting only abstract methods";
+
+ /*
+ * It is necessary to locate the implementing type on a per-method
+ * basis. Consider the case of
+ *
+ * @SingleJsoImpl interface C extends A, B {}
+ *
+ * Methods inherited from interfaces A and B must be dispatched to
+ * their respective JSO implementations.
+ */
+ JClassType implementingType = typeOracle.getSingleJsoImpl(intfMethod.getEnclosingType());
+
+ if (implementingType == null) {
+ /*
+ * This means that there is no concrete implementation of the
+ * interface by a JSO. Any implementation that might be created by a
+ * Generator won't be a JSO subtype, so we'll just ignore it as an
+ * actionable type. Were Generators ever able to create new JSO
+ * subtypes, we'd have to speculatively rewrite the callsite.
+ */
+ continue typeLoop;
+ }
+
+ /*
+ * Record the type as being actionable.
+ */
+ singleJsoImplTypes.add(canonicalizeClassName(getBinaryName(type)));
+
+ /*
+ * The mangled name adds the current interface like
+ *
+ * com_foo_Bar_methodName
+ */
+ String mangledName = getBinaryName(type).replace('.', '_') + "_"
+ + intfMethod.getName();
+ mangledNames.add(mangledName);
+
+ JType[] parameterTypes = new JType[intfMethod.getParameters().length];
+ for (int i = 0; i < parameterTypes.length; i++) {
+ parameterTypes[i] = intfMethod.getParameters()[i].getType();
+ }
+
+ /*
+ * Handle virtual overrides by finding the method that we would
+ * normally invoke and using its declaring class as the dispatch
+ * target.
+ */
+ JMethod implementingMethod;
+ while ((implementingMethod = implementingType.findMethod(
+ intfMethod.getName(), parameterTypes)) == null) {
+ implementingType = implementingType.getSuperclass();
+ }
+ assert implementingMethod != null && implementingType != null : "Unable to find virtual override for "
+ + intfMethod.toString();
+
+ /*
+ * Create a pseudo-method declaration for the interface method. This
+ * should look something like
+ *
+ * ReturnType method$ (ParamType, ParamType)
+ *
+ * This must be kept in sync with the WriteJsoImpl class.
+ */
+ {
+ String decl = getBinaryOrPrimitiveName(intfMethod.getReturnType())
+ + " " + intfMethod.getName() + "(";
+ for (JType paramType : parameterTypes) {
+ decl += ",";
+ decl += getBinaryOrPrimitiveName(paramType);
+ }
+ decl += ")";
+
+ com.google.gwt.dev.asm.commons.Method declaration = com.google.gwt.dev.asm.commons.Method.getMethod(decl);
+ mangledNamesToDeclarations.put(mangledName, declaration);
+ }
+
+ /*
+ * Cook up the a pseudo-method declaration for the concrete type. This
+ * should look something like
+ *
+ * ReturnType method$ (JsoType, ParamType, ParamType)
+ *
+ * This must be kept in sync with the WriteJsoImpl class.
+ */
+ {
+ String returnName = getBinaryOrPrimitiveName(implementingMethod.getReturnType());
+ String jsoName = getBinaryOrPrimitiveName(implementingType);
+
+ String decl = returnName + " " + intfMethod.getName() + "$ ("
+ + jsoName;
+ for (JType paramType : parameterTypes) {
+ decl += ",";
+ decl += getBinaryOrPrimitiveName(paramType);
+ }
+ decl += ")";
+
+ com.google.gwt.dev.asm.commons.Method toImplement = com.google.gwt.dev.asm.commons.Method.getMethod(decl);
+ mangledNamesToImplementations.put(mangledName, toImplement);
+ }
+ }
+ }
+
+ if (logger.isLoggable(Type.SPAM)) {
+ TreeLogger dumpLogger = logger.branch(Type.SPAM,
+ "SingleJsoImpl method mappings");
+ for (Map.Entry<String, com.google.gwt.dev.asm.commons.Method> entry : mangledNamesToImplementations.entrySet()) {
+ dumpLogger.log(Type.SPAM, entry.getKey() + " -> " + entry.getValue());
+ }
+ }
+ }
+
+ public com.google.gwt.dev.asm.commons.Method getDeclaration(
+ String mangledName) {
+ return mangledNamesToDeclarations.get(mangledName);
+ }
+
+ public com.google.gwt.dev.asm.commons.Method getImplementation(
+ String mangledName) {
+ return mangledNamesToImplementations.get(mangledName);
+ }
+
+ public SortedSet<String> getMangledNames() {
+ return unmodifiableNames;
+ }
+
+ public Set<String> getSingleJsoIntfTypes() {
+ return unmodifiableIntfNames;
+ }
+ }
+
+ /**
* The names of the bridge classes.
*/
private static final Map<String, Class<?>> BRIDGE_CLASS_NAMES = new HashMap<String, Class<?>>();
@@ -643,15 +844,12 @@
jsoSuperTypes.put(binaryName, types);
}
- // computeSingleJsoImplData has two out parameters
- SortedMap<String, com.google.gwt.dev.asm.commons.Method> mangledNamesToImplementations = new TreeMap<String, com.google.gwt.dev.asm.commons.Method>();
- computeSingleJsoImplData(singleJsoImplTypes,
- mangledNamesToImplementations);
+ SingleJsoImplData singleJsoImplData = new MySingleJsoImplData();
MyInstanceMethodOracle mapper = new MyInstanceMethodOracle(jsoTypes,
- typeOracle.getJavaLangObject());
+ typeOracle.getJavaLangObject(), singleJsoImplData);
classRewriter = new HostedModeClassRewriter(jsoTypeNames, jsoSuperTypes,
- mangledNamesToImplementations, singleJsoImplTypes, mapper);
+ singleJsoImplData, mapper);
} else {
// If we couldn't find the JSO class, we don't need to do any rewrites.
classRewriter = null;
@@ -838,125 +1036,6 @@
return lookupClassName;
}
- /**
- * Cook up the data we need to support JSO subtypes that implement interfaces
- * with methods. This includes the set of SingleJsoImpl interfaces actually
- * implemented by a JSO type, the mangled method names, and the names of the
- * Methods that should actually implement the virtual functions.
- *
- * Given the current implementation of JSO$ and incremental execution of
- * rebinds, it's not possible for Generators to produce additional
- * JavaScriptObject subtypes, so this data can remain static.
- */
- private void computeSingleJsoImplData(
- Set<String> singleJsoImplTypes,
- SortedMap<String, com.google.gwt.dev.asm.commons.Method> mangledNamesToImplementations) {
-
- // Loop over all types declared with the SingleJsoImpl annotation
- typeLoop : for (JClassType type : typeOracle.getSingleJsoImplInterfaces()) {
- assert type.isInterface() == type : "Expecting interfaces only";
-
- /*
- * By preemptively adding all possible mangled names by which a method
- * could be called, we greatly simplify the logic necessary to rewrite the
- * call-site.
- *
- * interface A {void m();}
- *
- * interface B extends A {void z();}
- *
- * becomes
- *
- * c_g_p_A_m() -> JsoA$.m$()
- *
- * c_g_p_B_m() -> JsoA$.m$()
- *
- * c_g_p_B_z() -> JsoB$.z$()
- */
- for (JMethod m : type.getOverridableMethods()) {
- assert m.isAbstract() : "Expecting only abstract methods";
-
- /*
- * It is necessary to locate the implementing type on a per-method
- * basis. Consider the case of
- *
- * @SingleJsoImpl interface C extends A, B {}
- *
- * Methods inherited from interfaces A and B must be dispatched to their
- * respective JSO implementations.
- */
- JClassType implementingType = typeOracle.getSingleJsoImpl(m.getEnclosingType());
-
- if (implementingType == null) {
- /*
- * This means that there is no concrete implementation of the
- * interface by a JSO. Any implementation that might be created by a
- * Generator won't be a JSO subtype, so we'll just ignore it as an
- * actionable type. Were Generators ever able to create new JSO
- * subtypes, we'd have to speculatively rewrite the callsite.
- */
- continue typeLoop;
- }
-
- /*
- * Record the type as being actionable.
- */
- singleJsoImplTypes.add(canonicalizeClassName(getBinaryName(type)));
-
- /*
- * The mangled name adds the current interface like
- *
- * com_foo_Bar_methodName
- */
- String mangledName = getBinaryName(type).replace('.', '_') + "_"
- + m.getName();
-
- JType[] parameterTypes = new JType[m.getParameters().length];
- for (int i = 0; i < parameterTypes.length; i++) {
- parameterTypes[i] = m.getParameters()[i].getType();
- }
-
- /*
- * Handle virtual overrides by finding the method that we would normally
- * invoke and using its declaring class as the dispatch target.
- */
- while (implementingType.findMethod(m.getName(), parameterTypes) == null) {
- implementingType = implementingType.getSuperclass();
- }
- assert implementingType != null : "Unable to find virtual override for "
- + m.toString();
-
- /*
- * Cook up the a pseudo-method declaration for the concrete type. This
- * should look something like
- *
- * ReturnType method$ (JsoType, ParamType, ParamType)
- *
- * This must be kept in sync with the WriteJsoImpl class.
- */
- String decl = getBinaryOrPrimitiveName(m.getReturnType()) + " "
- + m.getName() + "$ (" + getBinaryOrPrimitiveName(implementingType);
- for (JType paramType : parameterTypes) {
- decl += ",";
- decl += getBinaryOrPrimitiveName(paramType);
- }
- decl += ")";
-
- com.google.gwt.dev.asm.commons.Method toImplement = com.google.gwt.dev.asm.commons.Method.getMethod(decl);
-
- mangledNamesToImplementations.put(mangledName, toImplement);
- }
- }
-
- if (logger.isLoggable(Type.SPAM)) {
- TreeLogger dumpLogger = logger.branch(Type.SPAM,
- "SingleJsoImpl method mappings");
- for (Map.Entry<String, com.google.gwt.dev.asm.commons.Method> entry : mangledNamesToImplementations.entrySet()) {
- dumpLogger.log(Type.SPAM, entry.getKey() + " -> " + entry.getValue());
- }
- }
- }
-
private byte[] findClassBytes(String className) {
if (JavaScriptHost.class.getName().equals(className)) {
// No need to rewrite.
diff --git a/dev/core/src/com/google/gwt/dev/shell/rewrite/HostedModeClassRewriter.java b/dev/core/src/com/google/gwt/dev/shell/rewrite/HostedModeClassRewriter.java
index 03c32d5..79fc500 100644
--- a/dev/core/src/com/google/gwt/dev/shell/rewrite/HostedModeClassRewriter.java
+++ b/dev/core/src/com/google/gwt/dev/shell/rewrite/HostedModeClassRewriter.java
@@ -30,7 +30,7 @@
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
-import java.util.SortedMap;
+import java.util.SortedSet;
/**
* This class performs any and all byte code rewriting needed to make hosted
@@ -86,6 +86,33 @@
String findOriginalDeclaringClass(String declaredClass, String signature);
}
+ /**
+ * Contains data about how SingleJsoImpl methods are to be dispatched.
+ */
+ public interface SingleJsoImplData {
+ /**
+ * Returns a Method corresponding to the declaration of the abstract method
+ * in an interface type.
+ */
+ Method getDeclaration(String mangledName);
+
+ /**
+ * Return a Method corresponding to the concrete implementation of the
+ * method in a JSO type.
+ */
+ Method getImplementation(String mangledName);
+
+ /**
+ * Returns all of the mangled method names for SingleJsoImpl methods.
+ */
+ SortedSet<String> getMangledNames();
+
+ /**
+ * Returns the internal names of all interface types implemented by JSOs.
+ */
+ Set<String> getSingleJsoIntfTypes();
+ }
+
static final String JAVASCRIPTOBJECT_DESC = JsValueGlue.JSO_CLASS.replace(
'.', '/');
@@ -114,6 +141,8 @@
*/
private final Set<String> jsoIntfDescs;
+ private final SingleJsoImplData jsoData;
+
/**
* Records the superclass of every JSO for generating empty JSO interfaces.
*/
@@ -124,10 +153,6 @@
*/
private InstanceMethodOracle mapper;
- private final SortedMap<String, Method> mangledNamesToImplementations;
-
- private final Set<String> singleJsoImplTypes;
-
/**
* Creates a new {@link HostedModeClassRewriter} for a specified set of
* subclasses of JavaScriptObject.
@@ -137,9 +162,8 @@
* @param mapper maps methods to the class in which they are declared
*/
public HostedModeClassRewriter(Set<String> jsoSubtypes,
- Map<String, List<String>> jsoSuperTypes,
- SortedMap<String, Method> mangledNamesToImplementations,
- Set<String> singleJsoImplTypes, InstanceMethodOracle mapper) {
+ Map<String, List<String>> jsoSuperTypes, SingleJsoImplData jsoData,
+ InstanceMethodOracle mapper) {
Set<String> buildJsoIntfDescs = new HashSet<String>();
Set<String> buildJsoImplDescs = new HashSet<String>();
Map<String, List<String>> buildJsoSuperDescs = new HashMap<String, List<String>>();
@@ -160,8 +184,7 @@
this.jsoIntfDescs = Collections.unmodifiableSet(buildJsoIntfDescs);
this.jsoImplDescs = Collections.unmodifiableSet(buildJsoImplDescs);
this.jsoSuperDescs = Collections.unmodifiableMap(buildJsoSuperDescs);
- this.mangledNamesToImplementations = Collections.unmodifiableSortedMap(mangledNamesToImplementations);
- this.singleJsoImplTypes = Collections.unmodifiableSet(singleJsoImplTypes);
+ this.jsoData = jsoData;
this.mapper = mapper;
}
@@ -202,14 +225,12 @@
// v = new CheckClassAdapter(v);
// v = new TraceClassVisitor(v, new PrintWriter(System.out));
- v = new RewriteSingleJsoImplDispatches(v, typeOracle, singleJsoImplTypes,
- mangledNamesToImplementations);
+ v = new RewriteSingleJsoImplDispatches(v, typeOracle, jsoData);
v = new RewriteRefsToJsoClasses(v, jsoIntfDescs, mapper);
if (jsoImplDescs.contains(desc)) {
- v = WriteJsoImpl.create(v, desc, jsoIntfDescs, mapper,
- mangledNamesToImplementations);
+ v = WriteJsoImpl.create(v, desc, jsoIntfDescs, mapper, jsoData);
}
v = new RewriteJsniMethods(v, anonymousClassMap);
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 00bdca1..717a975 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
@@ -24,6 +24,7 @@
import com.google.gwt.dev.asm.Opcodes;
import com.google.gwt.dev.asm.Type;
import com.google.gwt.dev.asm.commons.Method;
+import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.SingleJsoImplData;
import com.google.gwt.dev.util.collect.Maps;
import com.google.gwt.dev.util.collect.Sets;
@@ -67,11 +68,10 @@
public void visitMethodInsn(int opcode, String owner, String name,
String desc) {
if (opcode == Opcodes.INVOKEINTERFACE) {
- if (singleJsoImplTypes.contains(owner)) {
+ if (jsoData.getSingleJsoIntfTypes().contains(owner)) {
// Simple case; referring directly to a SingleJso interface.
name = owner.replace('/', '_') + "_" + name;
- assert mangledNamesToImplementations.containsKey(name) : "Missing "
- + name;
+ assert jsoData.getMangledNames().contains(name) : "Missing " + name;
} else {
/*
@@ -86,7 +86,7 @@
* void bar() { ((IB) object).foo(); }
*/
for (String intf : computeAllInterfaces(owner)) {
- if (singleJsoImplTypes.contains(intf)) {
+ if (jsoData.getSingleJsoIntfTypes().contains(intf)) {
/*
* Check that it really should be mangled and is not a reference
* to a method defined in a non-singleJso super-interface. If
@@ -95,7 +95,7 @@
* is undefined.
*/
String maybeMangled = intf.replace('/', '_') + "_" + name;
- Method method = mangledNamesToImplementations.get(maybeMangled);
+ Method method = jsoData.getImplementation(maybeMangled);
if (method != null) {
/*
* Found a method with the right name, but we need to check the
@@ -127,17 +127,14 @@
private final Set<String> implementedMethods = new HashSet<String>();
private boolean inSingleJsoImplInterfaceType;
private Map<String, Set<String>> intfNamesToAllInterfaces = Maps.create();
- private final SortedMap<String, Method> mangledNamesToImplementations;
- private final Set<String> singleJsoImplTypes;
+ private final SingleJsoImplData jsoData;
private final TypeOracle typeOracle;
public RewriteSingleJsoImplDispatches(ClassVisitor v, TypeOracle typeOracle,
- Set<String> singleJsoImplTypes,
- SortedMap<String, Method> mangledNamesToImplementations) {
+ SingleJsoImplData jsoData) {
super(v);
this.typeOracle = typeOracle;
- this.singleJsoImplTypes = Collections.unmodifiableSet(singleJsoImplTypes);
- this.mangledNamesToImplementations = Collections.unmodifiableSortedMap(mangledNamesToImplementations);
+ this.jsoData = jsoData;
}
@Override
@@ -156,7 +153,8 @@
}
currentTypeName = name;
- inSingleJsoImplInterfaceType = singleJsoImplTypes.contains(name);
+ inSingleJsoImplInterfaceType = jsoData.getSingleJsoIntfTypes().contains(
+ name);
/*
* Implements objective #2: non-JSO types that implement a SingleJsoImpl
@@ -166,7 +164,7 @@
*/
if (interfaces != null && (access & Opcodes.ACC_INTERFACE) == 0) {
Set<String> toStub = computeAllInterfaces(interfaces);
- toStub.retainAll(singleJsoImplTypes);
+ toStub.retainAll(jsoData.getSingleJsoIntfTypes());
for (String stubIntr : toStub) {
writeTrampoline(stubIntr);
@@ -261,8 +259,11 @@
String name = typeName.replace('/', '_');
String prefix = name + "_";
String suffix = name + "`";
- SortedMap<String, Method> toReturn = new TreeMap<String, Method>(
- mangledNamesToImplementations.subMap(prefix, suffix));
+ SortedMap<String, Method> toReturn = new TreeMap<String, Method>();
+
+ for (String mangledName : jsoData.getMangledNames().subSet(prefix, suffix)) {
+ toReturn.put(mangledName, jsoData.getImplementation(mangledName));
+ }
toReturn.keySet().removeAll(implementedMethods);
return toReturn;
}
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 1054421..28081f1 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
@@ -23,9 +23,9 @@
import com.google.gwt.dev.asm.Type;
import com.google.gwt.dev.asm.commons.Method;
import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.InstanceMethodOracle;
+import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.SingleJsoImplData;
import java.util.ArrayList;
-import java.util.Map;
import java.util.Set;
/**
@@ -58,13 +58,13 @@
* <code>JavaScriptObject</code> and all subclasses.
*/
private final Set<String> jsoDescriptors;
- private final Map<String, Method> methodsToImplement;
+ private final SingleJsoImplData jsoData;
public ForJsoDollar(ClassVisitor cv, Set<String> jsoDescriptors,
- InstanceMethodOracle mapper, Map<String, Method> methodsToImplement) {
+ InstanceMethodOracle mapper, SingleJsoImplData jsoData) {
super(cv, mapper);
this.jsoDescriptors = jsoDescriptors;
- this.methodsToImplement = methodsToImplement;
+ this.jsoData = jsoData;
}
@Override
@@ -89,8 +89,9 @@
}
// Implement the trampoline methods
- for (Map.Entry<String, Method> entry : methodsToImplement.entrySet()) {
- writeTrampoline(entry.getKey(), entry.getValue());
+ for (String mangledName : jsoData.getMangledNames()) {
+ writeTrampoline(mangledName, jsoData.getDeclaration(mangledName),
+ jsoData.getImplementation(mangledName));
}
}
@@ -113,24 +114,31 @@
* In Java, it might look like:
*
* <pre>
- * public String com_google_Interface_someMethod(int a, double b) {
- * return com.google.MyJso$.someMethod$(this, a, b);
+ * interface Interface {
+ * String someMethod(int a, double b);
+ * }
+ *
+ * class J extends JSO implements I {
+ * public String com_google_Interface_someMethod(int a, double b) {
+ * return com.google.MyJso$.someMethod$(this, a, b);
+ * }
* }
* </pre>
*
* @param mangledName {@code com_google_gwt_sample_hello_client_Interface_a}
+ * @param interfaceMethod {@code java.lang.String a(int, double)}
* @param implementingMethod {@code static final java.lang.String
* a$(com.google.gwt.sample.hello.client.Jso, ...);}
*/
- private void writeTrampoline(String mangledName, Method implementingMethod) {
- /*
- * We derive the local descriptor by simply removing the first argument
- * from the static method we want to call.
- */
+ private void writeTrampoline(String mangledName, Method interfaceMethod,
+ Method implementingMethod) {
assert implementingMethod.getArgumentTypes().length > 0;
- String localDescriptor = "("
- + implementingMethod.getDescriptor().substring(
- 1 + implementingMethod.getArgumentTypes()[0].getDescriptor().length());
+
+ /*
+ * The local descriptor is the same as the descriptor from the abstract
+ * method in the interface.
+ */
+ String localDescriptor = interfaceMethod.getDescriptor();
Method localMethod = new Method(mangledName, localDescriptor);
/*
@@ -217,10 +225,10 @@
*/
public static ClassVisitor create(ClassVisitor cv, String classDescriptor,
Set<String> jsoDescriptors, InstanceMethodOracle mapper,
- Map<String, Method> methodsToImplement) {
+ SingleJsoImplData singleJsoImplData) {
if (classDescriptor.equals(HostedModeClassRewriter.JAVASCRIPTOBJECT_IMPL_DESC)) {
- return new ForJsoDollar(cv, jsoDescriptors, mapper, methodsToImplement);
+ return new ForJsoDollar(cv, jsoDescriptors, mapper, singleJsoImplData);
} else {
return new ForJsoInterface(cv, mapper);
}
diff --git a/user/test/com/google/gwt/dev/jjs/test/singlejso/TypeHierarchyTest.java b/user/test/com/google/gwt/dev/jjs/test/singlejso/TypeHierarchyTest.java
index 80966f5..c0f93d4 100644
--- a/user/test/com/google/gwt/dev/jjs/test/singlejso/TypeHierarchyTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/singlejso/TypeHierarchyTest.java
@@ -31,6 +31,12 @@
int getLength();
JavaScriptObject getObject(int i);
+
+ /**
+ * Used to test virtual override where the implementation has a narrower
+ * return type.
+ */
+ Wide wide();
}
/**
@@ -70,6 +76,15 @@
}
/**
+ * Used for testing virtual overrides.
+ */
+ static class Narrow extends Wide {
+ public String toString() {
+ return "Narrow";
+ }
+ }
+
+ /**
* This is a base class that is used to test adding interfaces to a JSO via a
* subclass.
*/
@@ -84,6 +99,10 @@
public final native JavaScriptObject getObject(int i) /*-{
return this[i];
}-*/;
+
+ public final Narrow wide() {
+ return new Narrow();
+ }
}
/**
@@ -98,6 +117,12 @@
}
}
+ /**
+ * Used for testing virtual overrides.
+ */
+ static class Wide {
+ }
+
@Override
public String getModuleName() {
return "com.google.gwt.dev.jjs.CompilerSuite";
@@ -151,5 +176,6 @@
Arrayish array = PlainJsoWithInterface.create();
assertEquals(0, array.getLength());
assertNull(array.getObject(0));
+ assertEquals("Narrow", array.wide().toString());
}
}