Revision: 5767 Author: [email protected] Date: Tue Jul 21 18:30:12 2009 Log: Fixes internal type hierarchy for overlay types.
All interfaces implemented by any overlay types are now directly implemented by JSO itself. This resolves a subtle bug where things could get tightened to subclasses of JSO, which is never correct. Review by: bobv http://code.google.com/p/google-web-toolkit/source/detail?r=5767 Modified: /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/JavaScriptObjectNormalizer.java ======================================= --- /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java Mon Jul 20 14:25:19 2009 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java Tue Jul 21 18:30:12 2009 @@ -23,7 +23,6 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -210,7 +209,7 @@ private final Map<JClassType, Set<JInterfaceType>> couldImplementMap = new IdentityHashMap<JClassType, Set<JInterfaceType>>(); - private final Set<JInterfaceType> dualImpl = new IdentityHashSet<JInterfaceType>(); + private final Set<JInterfaceType> dualImpls = new IdentityHashSet<JInterfaceType>(); private final Map<JClassType, Set<JInterfaceType>> implementsMap = new IdentityHashMap<JClassType, Set<JInterfaceType>>(); @@ -364,6 +363,8 @@ couldImplementMap.clear(); isImplementedMap.clear(); couldBeImplementedMap.clear(); + jsoSingleImpls.clear(); + dualImpls.clear(); for (int i = 0; i < program.getDeclaredTypes().size(); ++i) { JReferenceType type = program.getDeclaredTypes().get(i); @@ -373,6 +374,31 @@ recordSuperSubInfo((JInterfaceType) type); } } + + /* + * Now that the basic type hierarchy is computed, move all interfaces that + * are implemented by overlay types onto JavaScriptObject itself before + * building the full maps. + */ + JClassType jsoType = program.getJavaScriptObject(); + Set<JClassType> jsoSubTypes = Collections.emptySet(); + if (jsoType != null) { + assert jsoType.getImplements().size() == 0; + jsoSubTypes = get(subClassMap, jsoType); + for (JClassType jsoSubType : jsoSubTypes) { + for (JInterfaceType intf : jsoSubType.getImplements()) { + jsoType.addImplements(intf); + jsoSingleImpls.put(intf, jsoSubType); + for (JInterfaceType superIntf : get(superInterfaceMap, intf)) { + if (!jsoSingleImpls.containsKey(superIntf)) { + jsoSingleImpls.put(superIntf, jsoSubType); + } + } + } + jsoSubType.clearImplements(); + } + } + for (int i = 0; i < program.getDeclaredTypes().size(); ++i) { JReferenceType type = program.getDeclaredTypes().get(i); if (type instanceof JClassType) { @@ -392,26 +418,17 @@ } } - computeSingleJsoImplData(); - } - - /** - * Returns true if qType is a superinterface of type, directly or indirectly. - */ - public boolean extendsInterface(JInterfaceType type, JInterfaceType qType) { - return get(superInterfaceMap, type).contains(qType); - } - - public JMethod findConcreteImplementation(JMethod method, - JClassType concreteType) { - for (JMethod m : concreteType.getMethods()) { - if (getAllOverrides(m).contains(method)) { - if (!m.isAbstract()) { - return m; - } + // Create dual mappings for any jso interface with a Java implementor. + int totalJsoTypes = jsoSubTypes.size() + 1; + for (JInterfaceType jsoIntf : jsoSingleImpls.keySet()) { + Set<JClassType> implementors = get(isImplementedMap, jsoIntf); + if (implementors.size() == totalJsoTypes) { + assert implementors.contains(jsoType); + } else { + assert implementors.size() > totalJsoTypes; + dualImpls.add(jsoIntf); } } - return null; } public Set<JMethod> getAllOverrides(JMethod method) { @@ -454,20 +471,12 @@ } public Set<JInterfaceType> getInterfacesWithJavaAndJsoImpls() { - return Collections.unmodifiableSet(dualImpl); + return Collections.unmodifiableSet(dualImpls); } public Map<JInterfaceType, JClassType> getSingleJsoImpls() { return Collections.unmodifiableMap(jsoSingleImpls); } - - /** - * Returns true if qType is an implemented interface of type, directly or - * indirectly. - */ - public boolean implementsInterface(JClassType type, JInterfaceType qType) { - return get(implementsMap, type).contains(qType); - } public boolean isInstantiatedType(JReferenceType type) { return isInstantiatedType(type, instantiatedTypes); @@ -486,36 +495,6 @@ public boolean isSuperClass(JClassType type, JClassType qType) { return get(superClassMap, type).contains(qType); } - - /** - * Returns true if the given type and it's super-interfaces define no methods. - */ - public boolean isTagInterface(JInterfaceType type) { - Set<JInterfaceType> seen = new IdentityHashSet<JInterfaceType>(); - List<JInterfaceType> q = new LinkedList<JInterfaceType>(); - seen.add(type); - q.add(type); - - while (!q.isEmpty()) { - JInterfaceType intf = q.remove(0); - - List<JMethod> methods = intf.getMethods(); - int size = methods.size(); - if (size == 0 - || (size == 1 && methods.get(0).getName().equals("$clinit"))) { - // OK, add any super-interfaces; - for (JInterfaceType superIntf : intf.getImplements()) { - if (seen.add(superIntf)) { - q.add(superIntf); - } - } - } else { - return false; - } - } - - return true; - } /** * This method should be called after altering the types that are live in the @@ -529,8 +508,6 @@ computeHasClinit(type, computed); } } - - computeSingleJsoImplData(); } public void setInstantiatedTypes(Set<JReferenceType> instantiatedTypes) { @@ -541,48 +518,6 @@ private <K, V> void add(Map<K, Set<V>> map, K key, V value) { getOrCreate(map, key).add(value); } - - /** - * Collect all supertypes and superinterfaces for a type. - */ - private Set<JDeclaredType> allAssignableFrom(JDeclaredType type) { - Set<JDeclaredType> toReturn = new IdentityHashSet<JDeclaredType>(); - List<JDeclaredType> q = new LinkedList<JDeclaredType>(); - q.add(type); - - while (!q.isEmpty()) { - JDeclaredType t = q.remove(0); - - if (toReturn.add(t)) { - if (t.getSuperClass() != null) { - q.add(t.getSuperClass()); - } - - q.addAll(t.getImplements()); - } - } - - return toReturn; - } - - /** - * Computes the set of all interfaces implemented by a type. - */ - private Set<JInterfaceType> allSuperInterfaces(JDeclaredType type) { - Set<JInterfaceType> toReturn = new IdentityHashSet<JInterfaceType>(); - List<JInterfaceType> q = new LinkedList<JInterfaceType>(); - q.addAll(type.getImplements()); - - while (!q.isEmpty()) { - JInterfaceType t = q.remove(0); - - if (toReturn.add(t)) { - q.addAll(t.getImplements()); - } - } - - return toReturn; - } /** * Compute all of the things I might conceivably implement, either through @@ -687,73 +622,6 @@ } } } - - private void computeSingleJsoImplData() { - dualImpl.clear(); - jsoSingleImpls.clear(); - - JClassType jsoType = program.getJavaScriptObject(); - if (jsoType == null) { - return; - } - - jsoType.clearImplements(); - - for (JDeclaredType type : program.getDeclaredTypes()) { - if (!program.isJavaScriptObject(type)) { - if (type instanceof JClassType) { - dualImpl.addAll(allSuperInterfaces(type)); - } - continue; - } - - for (JReferenceType refType : allAssignableFrom(type)) { - if (!(refType instanceof JInterfaceType)) { - continue; - } - JInterfaceType intr = (JInterfaceType) refType; - - if (isTagInterface(intr)) { - /* - * 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(intr, program.getJavaScriptObject()); - - /* - * Pretend JSO had always implemented the tag interface. This helps - * simplify cast operations. - */ - jsoType.addImplements(intr); - add(couldBeImplementedMap, intr, jsoType); - add(isImplementedMap, intr, jsoType); - add(implementsMap, jsoType, intr); - continue; - } - - if (jsoSingleImpls.containsKey(intr)) { - // See if we're looking at a supertype - JClassType alreadySeen = jsoSingleImpls.get(intr); - - if (allAssignableFrom(alreadySeen).contains(type)) { - jsoSingleImpls.put(intr, (JClassType) type); - - } else { - assert allAssignableFrom(type).contains(alreadySeen) : "Already recorded " - + alreadySeen.getName() - + " as single impl for " - + intr.getName() - + " while looking at unrelated type " - + type.getName(); - } - } else { - jsoSingleImpls.put(intr, (JClassType) type); - } - } - } - dualImpl.retainAll(jsoSingleImpls.keySet()); - } /** * WEIRD: Suppose class Foo declares void f(){} and unrelated interface I also @@ -817,6 +685,13 @@ } } } + + /** + * Returns true if qType is a superinterface of type, directly or indirectly. + */ + private boolean extendsInterface(JInterfaceType type, JInterfaceType qType) { + return get(superInterfaceMap, type).contains(qType); + } private <K, V> Set<V> get(Map<K, Set<V>> map, K key) { Set<V> set = map.get(key); @@ -862,6 +737,14 @@ } return map2; } + + /** + * Returns true if qType is an implemented interface of type, directly or + * indirectly. + */ + private boolean implementsInterface(JClassType type, JInterfaceType qType) { + return get(implementsMap, type).contains(qType); + } private boolean isSameOrSuper(JClassType type, JClassType qType) { return (type == qType || isSuperClass(type, qType)); ======================================= --- /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java Thu Jul 16 16:35:08 2009 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java Tue Jul 21 18:30:12 2009 @@ -174,21 +174,6 @@ JType targetType = x.getCastType(); if (program.isJavaScriptObject(targetType)) { rescue((JReferenceType) targetType, true, true); - } else { - /* - * If there's a cast to a SingleJso interface, rescue the implementing - * JSO type. If the JSO type isn't rescued (and there's no other regular - * Java type implementing the interface), then the cast operation will - * be replaced with a throwCCEUnlessNull() call, since there's no type - * left in the type system that implements the interface. If there is an - * implementing Java type, then a dynamicCast() will be emitted which - * will throw a CCE if it hits a JSO type. - */ - JClassType maybeSingleJso = program.typeOracle.getSingleJsoImpls().get( - targetType); - if (maybeSingleJso != null) { - rescue(maybeSingleJso, true, true); - } } return true; ======================================= --- /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/JavaScriptObjectNormalizer.java Thu Apr 9 08:41:34 2009 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/JavaScriptObjectNormalizer.java Tue Jul 21 18:30:12 2009 @@ -120,7 +120,7 @@ JMethod jsoMethod = findJsoMethod(x.getTarget()); assert jsoMethod != null; - if (dualImpl.contains(targetClass)) { + if (dualImpls.contains(targetClass)) { /* * This is the special-case code to handle interfaces. */ @@ -174,14 +174,25 @@ currentMethodBody.push(x); return true; } + + private JMethod findConcreteImplementation(JMethod method, + JClassType concreteType) { + for (JMethod m : concreteType.getMethods()) { + if (program.typeOracle.getAllOverrides(m).contains(method)) { + if (!m.isAbstract()) { + return m; + } + } + } + return null; + } private JMethod findJsoMethod(JMethod interfaceMethod) { JClassType jsoClass = jsoSingleImpls.get(interfaceMethod.getEnclosingType()); assert program.isJavaScriptObject(jsoClass); assert jsoClass != null; - JMethod toReturn = program.typeOracle.findConcreteImplementation( - interfaceMethod, jsoClass); + JMethod toReturn = findConcreteImplementation(interfaceMethod, jsoClass); assert toReturn != null; assert !toReturn.isAbstract(); assert jsoClass.isFinal() || toReturn.isFinal(); @@ -226,8 +237,8 @@ if (program.isJavaScriptObject(type)) { return program.getJavaScriptObject(); - } else if (jsoSingleImpls.containsKey(type) && !dualImpl.contains(type)) { - // Narrow to JSO when possible + } else if (jsoSingleImpls.containsKey(type) && !dualImpls.contains(type)) { + // Optimization: narrow to JSO if it's not a dual impl. return program.getJavaScriptObject(); } else if (type instanceof JArrayType) { @@ -249,7 +260,7 @@ /** * Interfaces implemented both by a JSO type and a regular Java type. */ - private final Set<JInterfaceType> dualImpl; + private final Set<JInterfaceType> dualImpls; /** * Maps SingleJsoImpl interfaces onto the single JSO implementation. @@ -260,7 +271,7 @@ private JavaScriptObjectNormalizer(JProgram program) { this.program = program; - dualImpl = program.typeOracle.getInterfacesWithJavaAndJsoImpls(); + dualImpls = program.typeOracle.getInterfacesWithJavaAndJsoImpls(); jsoSingleImpls = program.typeOracle.getSingleJsoImpls(); } --~--~---------~--~----~------------~-------~--~----~ http://groups.google.com/group/Google-Web-Toolkit-Contributors -~----------~----~----~----~------~----~------~--~---
