Method overloads fix for SingleJsoImpl.
Patch by: bobv
Review by: spoon
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@6669 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 7a3f2df..1f7e743 100644
--- a/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
+++ b/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
@@ -41,6 +41,7 @@
import com.google.gwt.dev.util.Util;
import com.google.gwt.dev.util.Name.InternalName;
import com.google.gwt.dev.util.Name.SourceOrBinaryName;
+import com.google.gwt.dev.util.collect.Lists;
import com.google.gwt.util.tools.Utility;
import org.apache.commons.collections.map.AbstractReferenceMap;
@@ -473,8 +474,8 @@
*/
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 Map<String, List<com.google.gwt.dev.asm.commons.Method>> mangledNamesToDeclarations = new HashMap<String, List<com.google.gwt.dev.asm.commons.Method>>();
+ private final Map<String, List<com.google.gwt.dev.asm.commons.Method>> mangledNamesToImplementations = new HashMap<String, List<com.google.gwt.dev.asm.commons.Method>>();
private final SortedSet<String> unmodifiableNames = Collections.unmodifiableSortedSet(mangledNames);
private final Set<String> unmodifiableIntfNames = Collections.unmodifiableSet(singleJsoImplTypes);
@@ -555,7 +556,7 @@
implementingType = implementingType.getSuperclass();
}
// implementingmethod and implementingType cannot be null here
-
+
/*
* Create a pseudo-method declaration for the interface method. This
* should look something like
@@ -574,7 +575,7 @@
decl += ")";
com.google.gwt.dev.asm.commons.Method declaration = com.google.gwt.dev.asm.commons.Method.getMethod(decl);
- mangledNamesToDeclarations.put(mangledName, declaration);
+ addToMap(mangledNamesToDeclarations, mangledName, declaration);
}
/*
@@ -598,7 +599,7 @@
decl += ")";
com.google.gwt.dev.asm.commons.Method toImplement = com.google.gwt.dev.asm.commons.Method.getMethod(decl);
- mangledNamesToImplementations.put(mangledName, toImplement);
+ addToMap(mangledNamesToImplementations, mangledName, toImplement);
}
}
}
@@ -606,20 +607,23 @@
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()) {
+ for (Map.Entry<String, List<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(
+ public List<com.google.gwt.dev.asm.commons.Method> getDeclarations(
String mangledName) {
- return mangledNamesToDeclarations.get(mangledName);
+ List<com.google.gwt.dev.asm.commons.Method> toReturn = mangledNamesToDeclarations.get(mangledName);
+ return toReturn == null ? null : Collections.unmodifiableList(toReturn);
}
- public com.google.gwt.dev.asm.commons.Method getImplementation(
+ public List<com.google.gwt.dev.asm.commons.Method> getImplementations(
String mangledName) {
- return mangledNamesToImplementations.get(mangledName);
+ List<com.google.gwt.dev.asm.commons.Method> toReturn = mangledNamesToImplementations.get(mangledName);
+ return toReturn == null ? toReturn
+ : Collections.unmodifiableList(toReturn);
}
public SortedSet<String> getMangledNames() {
@@ -629,6 +633,21 @@
public Set<String> getSingleJsoIntfTypes() {
return unmodifiableIntfNames;
}
+
+ /**
+ * Assumes that the usual case is a 1:1 mapping.
+ */
+ private <K, V> void addToMap(Map<K, List<V>> map, K key, V value) {
+ List<V> list = map.get(key);
+ if (list == null) {
+ map.put(key, Lists.create(value));
+ } else {
+ List<V> maybeOther = Lists.add(list, value);
+ if (maybeOther != list) {
+ map.put(key, maybeOther);
+ }
+ }
+ }
}
/**
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 79fc500..319aeb3 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
@@ -91,16 +91,18 @@
*/
public interface SingleJsoImplData {
/**
- * Returns a Method corresponding to the declaration of the abstract method
- * in an interface type.
+ * Returns the method declarations that should be generated for a given
+ * mangled method name. {@link #getDeclarations} and
+ * {@link #getImplementations} maintain a pairwise mapping.
*/
- Method getDeclaration(String mangledName);
+ List<Method> getDeclarations(String mangledName);
/**
- * Return a Method corresponding to the concrete implementation of the
- * method in a JSO type.
+ * Returns the implementations that the method declarations above should
+ * delegate to.{@link #getDeclarations} and {@link #getImplementations}
+ * maintain a pairwise mapping.
*/
- Method getImplementation(String mangledName);
+ List<Method> getImplementations(String mangledName);
/**
* Returns all of the mangled method names for SingleJsoImpl methods.
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 717a975..b6c7c1e 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
@@ -85,7 +85,7 @@
*
* void bar() { ((IB) object).foo(); }
*/
- for (String intf : computeAllInterfaces(owner)) {
+ outer : for (String intf : computeAllInterfaces(owner)) {
if (jsoData.getSingleJsoIntfTypes().contains(intf)) {
/*
* Check that it really should be mangled and is not a reference
@@ -95,23 +95,25 @@
* is undefined.
*/
String maybeMangled = intf.replace('/', '_') + "_" + name;
- Method method = jsoData.getImplementation(maybeMangled);
- if (method != null) {
- /*
- * Found a method with the right name, but we need to check the
- * parameters and the return type. In order to do this, we'll
- * look at the arguments and return type of the target method,
- * removing the first argument, which is the instance.
- */
- assert method.getArgumentTypes().length >= 1;
- Type[] argumentTypes = new Type[method.getArgumentTypes().length - 1];
- System.arraycopy(method.getArgumentTypes(), 1, argumentTypes,
- 0, argumentTypes.length);
- String maybeDescriptor = Type.getMethodDescriptor(
- method.getReturnType(), argumentTypes);
- if (maybeDescriptor.equals(desc)) {
- name = maybeMangled;
- break;
+ List<Method> methods = jsoData.getImplementations(maybeMangled);
+ if (methods != null) {
+ for (Method method : methods) {
+ /*
+ * Found a method with the right name, but we need to check
+ * the parameters and the return type. In order to do this,
+ * we'll look at the arguments and return type of the target
+ * method, removing the first argument, which is the instance.
+ */
+ assert method.getArgumentTypes().length >= 1;
+ Type[] argumentTypes = new Type[method.getArgumentTypes().length - 1];
+ System.arraycopy(method.getArgumentTypes(), 1, argumentTypes,
+ 0, argumentTypes.length);
+ String maybeDescriptor = Type.getMethodDescriptor(
+ method.getReturnType(), argumentTypes);
+ if (maybeDescriptor.equals(desc)) {
+ name = maybeMangled;
+ break outer;
+ }
}
}
}
@@ -179,8 +181,10 @@
* may be referenced via a more specific interface.
*/
if (inSingleJsoImplInterfaceType) {
- for (Map.Entry<String, Method> entry : toImplement(currentTypeName).entrySet()) {
- writeEmptyMethod(entry.getKey(), entry.getValue());
+ for (Map.Entry<String, List<Method>> entry : toImplement(currentTypeName).entrySet()) {
+ for (Method method : entry.getValue()) {
+ writeEmptyMethod(entry.getKey(), method);
+ }
}
}
super.visitEnd();
@@ -255,14 +259,14 @@
* Given a resource name of a class, find all mangled method names that must
* be implemented.
*/
- private SortedMap<String, Method> toImplement(String typeName) {
+ private SortedMap<String, List<Method>> toImplement(String typeName) {
String name = typeName.replace('/', '_');
String prefix = name + "_";
String suffix = name + "`";
- SortedMap<String, Method> toReturn = new TreeMap<String, Method>();
+ SortedMap<String, List<Method>> toReturn = new TreeMap<String, List<Method>>();
for (String mangledName : jsoData.getMangledNames().subSet(prefix, suffix)) {
- toReturn.put(mangledName, jsoData.getImplementation(mangledName));
+ toReturn.put(mangledName, jsoData.getImplementations(mangledName));
}
toReturn.keySet().removeAll(implementedMethods);
return toReturn;
@@ -293,51 +297,52 @@
* semantics of the dispatches that would make a common implementation far
* more awkward than the duplication of code.
*/
- for (Map.Entry<String, Method> entry : toImplement(stubIntr).entrySet()) {
- String mangledName = entry.getKey();
- Method method = entry.getValue();
+ for (Map.Entry<String, List<Method>> entry : toImplement(stubIntr).entrySet()) {
+ for (Method method : entry.getValue()) {
+ String mangledName = entry.getKey();
- String descriptor = "("
- + method.getDescriptor().substring(
- 1 + method.getArgumentTypes()[0].getDescriptor().length());
- String localName = method.getName().substring(0,
- method.getName().length() - 1);
- Method toCall = new Method(localName, descriptor);
+ String descriptor = "("
+ + method.getDescriptor().substring(
+ 1 + method.getArgumentTypes()[0].getDescriptor().length());
+ String localName = method.getName().substring(0,
+ method.getName().length() - 1);
+ Method toCall = new Method(localName, descriptor);
- // Must not be final
- MethodVisitor mv = super.visitMethod(Opcodes.ACC_PUBLIC
- | Opcodes.ACC_SYNTHETIC, mangledName, descriptor, null, null);
- if (mv != null) {
- mv.visitCode();
+ // Must not be final
+ MethodVisitor mv = super.visitMethod(Opcodes.ACC_PUBLIC
+ | Opcodes.ACC_SYNTHETIC, mangledName, descriptor, null, null);
+ if (mv != null) {
+ mv.visitCode();
- /*
- * It just so happens that the stack and local variable sizes are the
- * same, but they're kept distinct to aid in clarity should the dispatch
- * logic change.
- *
- * These start at 1 because we need to load "this" onto the stack
- */
- int var = 1;
- int size = 1;
+ /*
+ * It just so happens that the stack and local variable sizes are the
+ * same, but they're kept distinct to aid in clarity should the
+ * dispatch logic change.
+ *
+ * These start at 1 because we need to load "this" onto the stack
+ */
+ int var = 1;
+ int size = 1;
- // load this
- mv.visitVarInsn(Opcodes.ALOAD, 0);
+ // load this
+ mv.visitVarInsn(Opcodes.ALOAD, 0);
- // then the rest of the arguments
- for (Type t : toCall.getArgumentTypes()) {
- size += t.getSize();
- mv.visitVarInsn(t.getOpcode(Opcodes.ILOAD), var);
- var += t.getSize();
+ // then the rest of the arguments
+ for (Type t : toCall.getArgumentTypes()) {
+ size += t.getSize();
+ 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));
+ mv.visitMaxs(size, var);
+ mv.visitEnd();
}
-
- // 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));
- mv.visitMaxs(size, var);
- mv.visitEnd();
}
}
}
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 28081f1..4eafd89 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
@@ -26,6 +26,8 @@
import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.SingleJsoImplData;
import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
import java.util.Set;
/**
@@ -90,8 +92,17 @@
// Implement the trampoline methods
for (String mangledName : jsoData.getMangledNames()) {
- writeTrampoline(mangledName, jsoData.getDeclaration(mangledName),
- jsoData.getImplementation(mangledName));
+ List<Method> declarations = jsoData.getDeclarations(mangledName);
+ List<Method> implementations = jsoData.getImplementations(mangledName);
+ assert declarations.size() == implementations.size() : "Declaration / implementation size mismatch";
+
+ Iterator<Method> declIterator = declarations.iterator();
+ Iterator<Method> implIterator = implementations.iterator();
+
+ while (declIterator.hasNext()) {
+ assert implIterator.hasNext();
+ writeTrampoline(mangledName, declIterator.next(), implIterator.next());
+ }
}
}
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 5b21b02..f9b7e67 100644
--- a/user/test/com/google/gwt/dev/jjs/test/SingleJsoImplTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/SingleJsoImplTest.java
@@ -311,6 +311,10 @@
return "a";
}
+ public String a(boolean overload) {
+ return overload ? "Kaboom!" : "OK";
+ }
+
public String ex() throws IOException {
throw new IOException();
}
@@ -403,6 +407,8 @@
interface Simple {
String a();
+ String a(boolean overload);
+
String ex() throws IOException;
String rte();
@@ -589,6 +595,7 @@
assertTrue(asJso instanceof Object);
assertTrue(asJso instanceof Simple);
assertEquals("a", asJso.a());
+ assertEquals("OK", asJso.a(false));
try {
asJso.ex();
fail("Should have thrown IOException");
@@ -609,7 +616,9 @@
assertTrue(asSimple instanceof JavaScriptObject);
assertTrue(asSimple instanceof JsoSimple);
assertEquals("a", asSimple.a());
+ assertEquals("OK", asSimple.a(false));
assertEquals("a", ((JsoSimple) asSimple).a());
+ assertEquals("OK", ((JsoSimple) asSimple).a(false));
try {
asSimple.ex();
fail("Should have thrown IOException");
@@ -635,6 +644,8 @@
assertTrue(asObject instanceof Simple);
assertEquals("a", ((Simple) asObject).a());
assertEquals("a", ((JsoSimple) asObject).a());
+ assertEquals("OK", ((Simple) asObject).a(false));
+ assertEquals("OK", ((JsoSimple) asObject).a(false));
// Test a cross-cast that's normally not allowed by the type system
assertTrue(asObject instanceof JsoRandom);