A new model for external types

http://gwt-code-reviews.appspot.com/589801/show
Patch by: tobyr
Review by: me, bobv


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@8255 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ArtificialRescueRecorder.java b/dev/core/src/com/google/gwt/dev/jjs/ArtificialRescueRecorder.java
index 5d3e9bd..7720935 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ArtificialRescueRecorder.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ArtificialRescueRecorder.java
@@ -22,6 +22,7 @@
 import com.google.gwt.dev.jjs.ast.JAnnotation;
 import com.google.gwt.dev.jjs.ast.JDeclaredType;
 import com.google.gwt.dev.jjs.ast.JField;
+import com.google.gwt.dev.jjs.ast.JInterfaceType;
 import com.google.gwt.dev.jjs.ast.JNode;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JReferenceType;
@@ -124,12 +125,12 @@
     new ArtificialRescueRecorder(program).execImpl();
   }
 
-  private final JDeclaredType artificialRescueType;
+  private final JInterfaceType artificialRescueType;
   private final JProgram program;
 
   private ArtificialRescueRecorder(JProgram program) {
     this.program = program;
-    artificialRescueType = program.getFromTypeMap(ArtificialRescue.class.getName());
+    artificialRescueType = (JInterfaceType) program.getFromTypeMap(ArtificialRescue.class.getName());
   }
 
   private void execImpl() {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JAnnotation.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JAnnotation.java
index 1d2876b..4133976 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JAnnotation.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JAnnotation.java
@@ -240,14 +240,9 @@
     return null;
   }
 
-  private final JType type;
+  private final JInterfaceType type;
   private List<Property> properties = Lists.create();
 
-  public JAnnotation(SourceInfo sourceInfo, JExternalType type) {
-    super(sourceInfo);
-    this.type = type;
-  }
-
   public JAnnotation(SourceInfo sourceInfo, JInterfaceType type) {
     super(sourceInfo);
     this.type = type;
@@ -277,7 +272,7 @@
     return null;
   }
 
-  public JType getType() {
+  public JInterfaceType getType() {
     return type;
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayType.java
index 8fd0b19..3890392 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayType.java
@@ -80,6 +80,11 @@
     return false;
   }
 
+  @Override
+  public boolean isExternal() {
+    return elementType.isExternal();
+  }
+
   public boolean isFinal() {
     return leafType.isFinal();
   }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JClassLiteral.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JClassLiteral.java
index 92ba61c..a3ff614 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JClassLiteral.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JClassLiteral.java
@@ -131,7 +131,7 @@
       JClassLiteral componentLiteral = program.getLiteralClass(arrayType.getElementType());
       call.addArg(componentLiteral);
     } else {
-      assert (type instanceof JExternalType || type instanceof JInterfaceType || type instanceof JPrimitiveType);
+      assert (type instanceof JInterfaceType || type instanceof JPrimitiveType);
     }
     assert call.getArgs().size() == method.getParams().size() : "Argument / param mismatch "
         + call.toString() + " versus " + method.toString();
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java
index b539640..97964e7 100755
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java
@@ -67,6 +67,14 @@
   private JDeclaredType enclosingType;
 
   /**
+   * True if this class is provided externally to the program by the program's
+   * host execution environment. For example, while compiling for the JVM, JRE
+   * types are external types. External types definitions are provided by class
+   * files which are considered opaque by the GWT compiler.
+   */
+  private boolean isExternal;
+
+  /**
    * This type's super class.
    */
   private JClassType superClass;
@@ -226,10 +234,16 @@
     return clinitTarget != null;
   }
 
+  @Override
+  public boolean isExternal() {
+    return isExternal;
+  }
+
   /**
    * Removes the field at the specified index.
    */
   public void removeField(int i) {
+    assert !isExternal() : "External types can not be modiified.";
     fields = Lists.remove(fields, i);
   }
 
@@ -237,6 +251,7 @@
    * Removes the method at the specified index.
    */
   public void removeMethod(int i) {
+    assert !isExternal() : "External types can not be modiified.";
     methods = Lists.remove(methods, i);
   }
 
@@ -249,6 +264,10 @@
     this.enclosingType = enclosingType;
   }
 
+  public void setExternal(boolean isExternal) {
+    this.isExternal = isExternal;
+  }
+
   /**
    * Sets this type's super class.
    */
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JExternalType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JExternalType.java
deleted file mode 100644
index 86d69a7..0000000
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JExternalType.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2010 Google Inc.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.dev.jjs.ast;
-
-import com.google.gwt.dev.jjs.SourceInfo;
-
-/**
- * Represents a type outside of the client type system, usually a binary-only
- * annotation reference.
- */
-public class JExternalType extends JDeclaredType {
-
-  JExternalType(SourceInfo info, String name) {
-    super(info, name);
-  }
-
-  @Override
-  public String getClassLiteralFactoryMethod() {
-    return "Class.createForInterface";
-  }
-
-  public boolean isAbstract() {
-    return true;
-  }
-
-  public boolean isFinal() {
-    return false;
-  }
-
-  public void traverse(JVisitor visitor, Context ctx) {
-    if (visitor.visit(this, ctx)) {
-      annotations = visitor.acceptImmutable(annotations);
-    }
-    visitor.endVisit(this, ctx);
-  }
-}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
index 3feb55d..7e1660e 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
@@ -148,6 +148,7 @@
   }
 
   public JAbstractMethodBody getBody() {
+    assert !enclosingType.isExternal() : "External types do not have method bodies.";
     return body;
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
index 92f4f0f..ea9d226 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
@@ -303,8 +303,6 @@
    */
   private final ArrayList<HashMap<JType, JArrayType>> dimensions = new ArrayList<HashMap<JType, JArrayType>>();
 
-  private final Map<String, JExternalType> externalTypes = new HashMap<String, JExternalType>();
-
   private final Map<String, JField> indexedFields = new HashMap<String, JField>();
 
   private final Map<String, JMethod> indexedMethods = new HashMap<String, JMethod>();
@@ -481,20 +479,6 @@
     return x;
   }
 
-  public JExternalType createExternalType(SourceInfo info, String name) {
-    JExternalType x;
-    x = externalTypes.get(name);
-    if (x != null) {
-      return x;
-    }
-
-    x = new JExternalType(info, name);
-    if (INDEX_TYPES_SET.contains(name)) {
-      indexedTypes.put(x.getShortName(), x);
-    }
-    return x;
-  }
-
   public JField createField(SourceInfo info, String name,
       JDeclaredType enclosingType, JType type, boolean isStatic,
       Disposition disposition) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JType.java
index 837e727..7391010 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JType.java
@@ -45,4 +45,8 @@
     return name;
   }
 
+  public boolean isExternal() {
+    return false;
+  }
+
 }
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 225c932..73c6ff1 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
@@ -611,7 +611,7 @@
 
   private void computeClinitTarget(JDeclaredType type,
       Set<JDeclaredType> computed) {
-    if (!type.hasClinit() || computed.contains(type)) {
+    if (type.isExternal() || !type.hasClinit() || computed.contains(type)) {
       return;
     }
     if (type.getSuperClass() != null) {
@@ -876,6 +876,18 @@
       Set<JReferenceType> instantiatedTypes) {
     type = program.getRunTimeType(type);
 
+    if (type.isExternal()) {
+      // TODO(tobyr) I don't know under what situations it is safe to assume
+      // that an external type won't be instantiated. For example, if we
+      // assumed that an external exception weren't instantiated, because we
+      // didn't see it constructed in our code, dead code elimination would
+      // incorrectly elide any catch blocks for that exception.
+      //
+      // We should see how this effects optimization and if we can limit its
+      // impact if necessary.
+      return true;
+    }
+
     if (instantiatedTypes == null) {
       return true;
     }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java b/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
index 66b6b5a..a90b297 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
@@ -259,11 +259,6 @@
     }
 
     @Override
-    public void endVisit(JClassType x, Context ctx) {
-      // previously set currentClass = null;
-    }
-
-    @Override
     public void endVisit(JConditional x, Context ctx) {
       JExpression updated = simplifier.conditional(x, x.getSourceInfo(),
           x.getType(), x.getIfTest(), x.getThenExpr(), x.getElseExpr());
@@ -614,8 +609,8 @@
 
     @Override
     public boolean visit(JClassType x, Context ctx) {
-      // previously set currentClass = x;
-      return true;
+      // We can't eliminate code from an external type
+      return !x.isExternal();
     }
 
     @Override
@@ -1463,8 +1458,8 @@
     /**
      * Simplify the expression <code>-exp</code>.
      * 
+     * @param original An expression equivalent to <code>-exp</code>.
      * @param exp The expression to negate.
-     * @param An expression equivalent to <code>-exp</code>.
      * 
      * @return A simplified expression equivalent to <code>- exp</code>.
      */
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/Finalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/Finalizer.java
index 60b4250..e15b90b 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/Finalizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/Finalizer.java
@@ -103,6 +103,13 @@
     }
 
     @Override
+    public boolean visit(JClassType x, Context ctx) {
+      // Don't visit external types, because we can't change their final
+      // specifiers.
+      return !x.isExternal();
+    }
+
+    @Override
     public boolean visit(JMethodBody x, Context ctx) {
       for (JLocal local : x.getLocals()) {
         maybeFinalize(local);
@@ -180,6 +187,13 @@
       }
     }
 
+    @Override
+    public boolean visit(JClassType x, Context ctx) {
+      // Don't visit external types, because we can't change their final
+      // specifiers.
+      return !x.isExternal();
+    }
+
     private void recordAssignment(JExpression lhs) {
       if (lhs instanceof JVariableRef) {
         JVariableRef variableRef = (JVariableRef) lhs;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
index bf45e4c..658c1cb 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
@@ -48,7 +48,6 @@
 import com.google.gwt.dev.jjs.ast.JEnumType;
 import com.google.gwt.dev.jjs.ast.JExpression;
 import com.google.gwt.dev.jjs.ast.JExpressionStatement;
-import com.google.gwt.dev.jjs.ast.JExternalType;
 import com.google.gwt.dev.jjs.ast.JField;
 import com.google.gwt.dev.jjs.ast.JFieldRef;
 import com.google.gwt.dev.jjs.ast.JFloatLiteral;
@@ -2011,12 +2010,9 @@
       }
     }
 
-    private void addThrownExceptions(MethodBinding methodBinding,
-        JMethod method) {
-      for (ReferenceBinding exceptionReference : 
-        methodBinding.thrownExceptions) {
-        method.addThrownException((JClassType) 
-            typeMap.get(exceptionReference.erasure()));
+    private void addThrownExceptions(MethodBinding methodBinding, JMethod method) {
+      for (ReferenceBinding exceptionReference : methodBinding.thrownExceptions) {
+        method.addThrownException((JClassType) typeMap.get(exceptionReference.erasure()));
       }
     }
 
@@ -2239,6 +2235,17 @@
       return (SourceTypeBinding) typeBinding;
     }
 
+    private JInterfaceType getOrCreateExternalType(SourceInfo info,
+        char[][] compoundName) {
+      String name = BuildTypeMap.dotify(compoundName);
+      JInterfaceType external = (JInterfaceType) program.getFromTypeMap(name);
+      if (external == null) {
+        external = program.createInterface(info, name);
+        external.setExternal(true);
+      }
+      return external;
+    }
+
     /**
      * Get a new label of a particular name, or create a new one if it doesn't
      * exist already.
@@ -2450,25 +2457,26 @@
 
       for (ElementValuePair pair : elementValuePairs) {
         String name = CharOperation.charToString(pair.getName());
-        List<JAnnotationArgument> values = processAnnotationPropertyValue(sourceInfo,
-            pair.getValue());
+        List<JAnnotationArgument> values = processAnnotationPropertyValue(
+            sourceInfo, pair.getValue());
         annotation.addValue(new Property(sourceInfo, name, values));
       }
     }
 
-    private List<JAnnotationArgument> processAnnotationPropertyValue(SourceInfo info,
-        Object value) {
+    private List<JAnnotationArgument> processAnnotationPropertyValue(
+        SourceInfo info, Object value) {
       if (value instanceof TypeBinding) {
         JType type = (JType) typeMap.tryGet((TypeBinding) value);
         if (type == null) {
           // Indicates a binary-only class literal
-          type = program.createExternalType(info,
-              BuildTypeMap.dotify(((ReferenceBinding) value).compoundName));
+          type = getOrCreateExternalType(info,
+              ((ReferenceBinding) value).compoundName);
         }
         return Lists.<JAnnotationArgument> create(program.getLiteralClass(type));
 
       } else if (value instanceof Constant) {
-        return Lists.create((JAnnotationArgument) dispatch("processConstant", value));
+        return Lists.create((JAnnotationArgument) dispatch("processConstant",
+            value));
 
       } else if (value instanceof Object[]) {
         Object[] array = (Object[]) value;
@@ -2487,8 +2495,8 @@
         if (type != null) {
           toReturn = new JAnnotation(info, type);
         } else {
-          JExternalType external = program.createExternalType(info,
-              BuildTypeMap.dotify(annotationType.compoundName));
+          JInterfaceType external = getOrCreateExternalType(info,
+              annotationType.compoundName);
           toReturn = new JAnnotation(info, external);
         }
 
@@ -2554,8 +2562,8 @@
           annotation = new JAnnotation(x.getSourceInfo(), annotationType);
         } else {
           // Indicates a binary-only annotation type
-          JExternalType externalType = program.createExternalType(
-              x.getSourceInfo(), BuildTypeMap.dotify(binding.compoundName));
+          JInterfaceType externalType = getOrCreateExternalType(
+              x.getSourceInfo(), binding.compoundName);
           annotation = new JAnnotation(x.getSourceInfo(), externalType);
         }
         processAnnotationProperties(x.getSourceInfo(), annotation,
@@ -2805,7 +2813,7 @@
         JDeclarationStatement declStmt = new JDeclarationStatement(sourceInfo,
             valuesRef, newExpr);
         JBlock clinitBlock = ((JMethodBody) type.getMethods().get(0).getBody()).getBlock();
-        
+
         /*
          * HACKY: the $VALUES array must be initialized immediately after all of
          * the enum fields, but before any user initialization (which might rely
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/MakeCallsStatic.java b/dev/core/src/com/google/gwt/dev/jjs/impl/MakeCallsStatic.java
index 5cd64e3..4cf8632 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/MakeCallsStatic.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/MakeCallsStatic.java
@@ -250,6 +250,15 @@
     public void endVisit(JMethodCall x, Context ctx) {
       JMethod method = x.getTarget();
 
+      if (method.getEnclosingType() != null) {
+        if (method.getEnclosingType().isExternal()) {
+          // Staticifying a method requires modifying the type, which we can't
+          // do for external types. Theoretically we could put the static method
+          // in some generated code, but what does that really buy us?
+          return;
+        }
+      }
+
       // Did we already do this one?
       if (program.getStaticImpl(method) != null
           || toBeMadeStatic.contains(method)) {
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/JAnnotationTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/JAnnotationTest.java
index c0990b1..129981c 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/impl/JAnnotationTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/JAnnotationTest.java
@@ -20,7 +20,6 @@
 import com.google.gwt.dev.jjs.ast.JAnnotation;
 import com.google.gwt.dev.jjs.ast.JClassLiteral;
 import com.google.gwt.dev.jjs.ast.JDeclaredType;
-import com.google.gwt.dev.jjs.ast.JExternalType;
 import com.google.gwt.dev.jjs.ast.JField;
 import com.google.gwt.dev.jjs.ast.JLocal;
 import com.google.gwt.dev.jjs.ast.JMethod;
@@ -231,7 +230,7 @@
     JAnnotation a = JAnnotation.findAnnotation(useDefaults,
         BinaryAnnotation.class.getName());
     assertNotNull(a);
-    assertTrue(a.getType() instanceof JExternalType);
+    assertTrue(a.getType().isExternal());
 
     BinaryAnnotation instance = JAnnotation.createAnnotation(
         BinaryAnnotation.class, a);
@@ -265,7 +264,8 @@
 
     Property p = a.getProperty("value");
     JClassLiteral literal = (JClassLiteral) p.getSingleValue();
-    JExternalType externalType = (JExternalType) literal.getRefType();
-    assertEquals(JAnnotationTest.class.getName(), externalType.getName());
+    JDeclaredType type = (JDeclaredType) literal.getRefType();
+    assertTrue(type.isExternal());
+    assertEquals(JAnnotationTest.class.getName(), type.getName());
   }
 }