Author: [email protected]
Date: Mon Jul  6 14:45:31 2009
New Revision: 5675

Added:
    trunk/dev/core/src/com/google/gwt/dev/javac/ArtificialRescueChecker.java
    trunk/dev/core/super/com/google/gwt/core/client/impl/
     
trunk/dev/core/super/com/google/gwt/core/client/impl/ArtificialRescue.java
     
trunk/dev/core/test/com/google/gwt/dev/javac/ArtificialRescueCheckerTest.java
    trunk/dev/core/test/com/google/gwt/dev/javac/CheckerTestCase.java
Modified:
     
trunk/dev/core/src/com/google/gwt/dev/javac/CompilationUnitInvalidator.java
    trunk/dev/core/src/com/google/gwt/dev/jdt/AbstractCompiler.java
    trunk/dev/core/src/com/google/gwt/dev/jdt/WebModeCompilerFrontEnd.java
    trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java
    trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
    trunk/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java
    trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
    trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
    trunk/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
    trunk/dev/core/src/com/google/gwt/dev/js/JsUnusedFunctionRemover.java
    trunk/dev/core/src/com/google/gwt/dev/js/ast/JsFunction.java
    trunk/dev/core/test/com/google/gwt/dev/javac/JsniCheckerTest.java
    trunk/user/src/com/google/gwt/core/client/impl/Impl.java

Log:
Add @ArtificialRescue annotation to the GWT compiler.

Patch by: bobv
Review by: scottb

Added:  
trunk/dev/core/src/com/google/gwt/dev/javac/ArtificialRescueChecker.java
==============================================================================
--- (empty file)
+++  
trunk/dev/core/src/com/google/gwt/dev/javac/ArtificialRescueChecker.java        
 
Mon Jul  6 14:45:31 2009
@@ -0,0 +1,377 @@
+/*
+ * 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.dev.javac;
+
+import com.google.gwt.core.client.impl.ArtificialRescue;
+import com.google.gwt.dev.jjs.InternalCompilerException;
+import com.google.gwt.dev.util.Empty;
+import com.google.gwt.dev.util.JsniRef;
+import com.google.gwt.dev.util.collect.Lists;
+
+import org.eclipse.jdt.core.compiler.CharOperation;
+import org.eclipse.jdt.internal.compiler.ASTVisitor;
+import org.eclipse.jdt.internal.compiler.ast.Annotation;
+import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Expression;
+import org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
+import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation;
+import org.eclipse.jdt.internal.compiler.ast.StringLiteral;
+import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
+import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
+import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
+import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
+import org.eclipse.jdt.internal.compiler.lookup.ProblemReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+
+import java.util.List;
+
+/**
+ * Checks the validity of ArtificialRescue annotations.
+ *
+ * <ul>
+ * <li>(1) The ArtificialRescue annotation is only used in generated  
code.</li>
+ * <li>(2) The className value names a type known to GWT (ignoring access  
rules)
+ * </li>
+ * <li>(3) The methods and fields of the type are known to GWT</li>
+ * </ul>
+ */
+public class ArtificialRescueChecker {
+  private class Visitor extends ASTVisitor {
+
+    {
+      assert collectTypes || reportErrors : "No work to be done";
+    }
+
+    @Override
+    public void endVisit(TypeDeclaration localTypeDeclaration, BlockScope  
scope) {
+      processType(localTypeDeclaration);
+    }
+
+    @Override
+    public void endVisit(TypeDeclaration memberTypeDeclaration, ClassScope  
scope) {
+      processType(memberTypeDeclaration);
+    }
+
+    @Override
+    public void endVisit(TypeDeclaration typeDeclaration,
+        CompilationUnitScope scope) {
+      processType(typeDeclaration);
+    }
+
+    private void processArtificialRescue(Annotation rescue) {
+      if (!allowArtificialRescue) {
+        // Goal (1)
+        GWTProblem.recordInCud(rescue, cud, onlyGeneratedCode(), null);
+        return;
+      }
+
+      String className = null;
+      String[] methods = Empty.STRINGS;
+      String[] fields = Empty.STRINGS;
+      for (MemberValuePair pair : rescue.memberValuePairs()) {
+        String name = String.valueOf(pair.name);
+        if ("className".equals(name)) {
+          className = pair.value.constant.stringValue();
+        } else if ("methods".equals(name)) {
+          methods = stringArrayFromValue(pair.value);
+        } else if ("fields".equals(name)) {
+          fields = stringArrayFromValue(pair.value);
+        }
+      }
+
+      assert className != null;
+
+      if (collectTypes) {
+        referencedTypes = Lists.add(referencedTypes, className);
+      }
+
+      if (!reportErrors) {
+        // Nothing else to do
+        return;
+      }
+
+      // Goal (2)
+      // Strip off any array-like extensions and just find base type
+      boolean isArray = false;
+      while (className.endsWith("[]")) {
+        className = className.substring(0, className.length() - 2);
+        isArray = true;
+      }
+
+      // Fix JSNI primitive type names to something JDT will understand
+      if (isArray && className.length() == 1) {
+        switch (className.charAt(0)) {
+          case 'B':
+            className = "byte";
+            break;
+          case 'C':
+            className = "char";
+            break;
+          case 'D':
+            className = "double";
+            break;
+          case 'F':
+            className = "float";
+            break;
+          case 'I':
+            className = "int";
+            break;
+          case 'J':
+            className = "long";
+            break;
+          case 'S':
+            className = "short";
+            break;
+          case 'Z':
+            className = "boolean";
+            break;
+        }
+      }
+
+      char[][] compoundName = CharOperation.splitOn('.',
+          className.toCharArray());
+      TypeBinding typeBinding = cud.scope.getType(compoundName,
+          compoundName.length);
+      if (typeBinding == null) {
+        GWTProblem.recordInCud(rescue, cud, notFound(className), null);
+      } else if (typeBinding instanceof ProblemReferenceBinding) {
+        ProblemReferenceBinding problem = (ProblemReferenceBinding)  
typeBinding;
+        if (problem.problemId() == ProblemReasons.NotVisible) {
+          // Ignore
+        } else if (problem.problemId() == ProblemReasons.NotFound) {
+          GWTProblem.recordInCud(rescue, cud, notFound(className), null);
+        } else {
+          GWTProblem.recordInCud(rescue, cud,
+              unknownProblem(className, problem), null);
+        }
+      } else if (typeBinding instanceof BaseTypeBinding) {
+        // No methods or fields on primitive types (3)
+        if (methods.length > 0) {
+          GWTProblem.recordInCud(rescue, cud, noMethodsAllowed(), null);
+        }
+
+        if (fields.length > 0) {
+          GWTProblem.recordInCud(rescue, cud, noFieldsAllowed(), null);
+        }
+      } else if (typeBinding instanceof ReferenceBinding) {
+        ReferenceBinding ref = (ReferenceBinding) typeBinding;
+
+        if (isArray) {
+          // No methods or fields on array types (3)
+          if (methods.length > 0) {
+            GWTProblem.recordInCud(rescue, cud, noMethodsAllowed(), null);
+          }
+
+          if (fields.length > 0) {
+            GWTProblem.recordInCud(rescue, cud, noFieldsAllowed(), null);
+          }
+        } else {
+          // Check methods on reference types (3)
+          for (String method : methods) {
+            if (method.contains("@")) {
+              GWTProblem.recordInCud(rescue, cud, nameAndTypesOnly(),  
null);
+              continue;
+            }
+            JsniRef jsni = JsniRef.parse("@foo::" + method);
+            if (jsni == null) {
+              GWTProblem.recordInCud(rescue, cud,  
badMethodSignature(method),
+                  null);
+              continue;
+            }
+
+            if (jsni.memberName().equals(
+                String.valueOf(ref.compoundName[ref.compoundName.length -  
1]))) {
+              // Constructor
+            } else {
+              MethodBinding[] methodBindings =  
ref.getMethods(jsni.memberName().toCharArray());
+              if (methodBindings == null || methodBindings.length == 0) {
+                GWTProblem.recordInCud(rescue, cud, noMethod(className,
+                    jsni.memberName()), null);
+                continue;
+              }
+            }
+          }
+
+          // Check fields on reference types (3)
+          for (String field : fields) {
+            if (ref.getField(field.toCharArray(), false) == null) {
+              GWTProblem.recordInCud(rescue, cud, unknownField(field),  
null);
+            }
+          }
+        }
+      }
+    }
+
+    /**
+     * Examine a TypeDeclaration for ArtificialRescue annotations.  
Delegates to
+     * {...@link #processArtificialRescue(Annotation)} to complete the  
processing.
+     */
+    private void processType(TypeDeclaration x) {
+      if (x.annotations == null) {
+        return;
+      }
+
+      for (Annotation a : x.annotations) {
+        if (!ArtificialRescue.class.getName().equals(
+            CharOperation.toString(((ReferenceBinding)  
a.resolvedType).compoundName))) {
+          continue;
+        }
+
+        // Sometimes it's a SingleMemberAnnotation, other times it's not
+        Expression value = null;
+        if (a instanceof SingleMemberAnnotation) {
+          value = ((SingleMemberAnnotation) a).memberValue;
+        } else {
+          for (MemberValuePair pair : a.memberValuePairs()) {
+            if ("value".equals(String.valueOf(pair.name))) {
+              value = pair.value;
+              break;
+            }
+          }
+        }
+
+        assert value != null;
+        if (value instanceof ArrayInitializer) {
+          for (Expression e : ((ArrayInitializer) value).expressions) {
+            processArtificialRescue((Annotation) e);
+          }
+        } else if (value instanceof Annotation) {
+          processArtificialRescue((Annotation) value);
+        } else {
+          throw new InternalCompilerException(
+              "Unable to process annotation with value of type "
+                  + value.getClass().getName());
+        }
+
+        return;
+      }
+    }
+
+    private String[] stringArrayFromValue(Expression value) {
+      if (value instanceof StringLiteral) {
+        return new String[] {value.constant.stringValue()};
+      } else if (value instanceof ArrayInitializer) {
+        ArrayInitializer init = (ArrayInitializer) value;
+        String[] toReturn = new String[init.expressions == null ? 0
+            : init.expressions.length];
+        for (int i = 0; i < toReturn.length; i++) {
+          toReturn[i] = init.expressions[i].constant.stringValue();
+        }
+        return toReturn;
+      } else {
+        throw new InternalCompilerException("Unhandled value type "
+            + value.getClass().getName());
+      }
+    }
+  }
+
+  /**
+   * Check the {...@link ArtificialRescue} annotations in a CompilationUnit.  
Errors
+   * are reported through {...@link GWTProblem}.
+   */
+  public static void check(CompilationUnit cud) {
+    new ArtificialRescueChecker(cud).check();
+  }
+
+  /**
+   * Report all types named in {...@link ArtificialRescue} annotations in a  
CUD. No
+   * error checking is done.
+   */
+  public static List<String> collectReferencedTypes(
+      CompilationUnitDeclaration cud) {
+    return new ArtificialRescueChecker(cud).collect();
+  }
+
+  static String badMethodSignature(String method) {
+    return "Bad method signature " + method;
+  }
+
+  static String nameAndTypesOnly() {
+    return "Only method name and parameter types expected";
+  }
+
+  static String noFieldsAllowed() {
+    return "Cannot refer to fields on array or primitive types";
+  }
+
+  static String noMethod(String className, String methodName) {
+    return "No method named " + methodName + " in type " + className;
+  }
+
+  static String noMethodsAllowed() {
+    return "Cannot refer to methods on array or primitive types";
+  }
+
+  static String notFound(String className) {
+    return "Could not find type " + className;
+  }
+
+  static String onlyGeneratedCode() {
+    return "The " + ArtificialRescue.class.getName()
+        + " annotation may only be used in generated code and its use"
+        + " by third parties is not supported.";
+  }
+
+  static String unknownField(String field) {
+    return "Unknown field " + field;
+  }
+
+  static String unknownProblem(String className, ProblemReferenceBinding  
problem) {
+    return "Unknown problem: "
+        + ProblemReferenceBinding.problemReasonString(problem.problemId())
+        + " " + className;
+  }
+
+  private final boolean allowArtificialRescue;
+
+  private boolean collectTypes;
+
+  private boolean reportErrors;
+
+  private final CompilationUnitDeclaration cud;
+
+  private List<String> referencedTypes;
+
+  private ArtificialRescueChecker(CompilationUnit unit) {
+    allowArtificialRescue = unit.isGenerated();
+    cud = unit.getJdtCud();
+  }
+
+  private ArtificialRescueChecker(CompilationUnitDeclaration cud) {
+    allowArtificialRescue = true;
+    this.cud = cud;
+  }
+
+  private void check() {
+    collectTypes = false;
+    reportErrors = true;
+    cud.traverse(new Visitor(), cud.scope);
+  }
+
+  private List<String> collect() {
+    collectTypes = true;
+    referencedTypes = Lists.create();
+    reportErrors = false;
+    cud.traverse(new Visitor(), cud.scope);
+    return referencedTypes;
+  }
+
+}

Modified:  
trunk/dev/core/src/com/google/gwt/dev/javac/CompilationUnitInvalidator.java
==============================================================================
---  
trunk/dev/core/src/com/google/gwt/dev/javac/CompilationUnitInvalidator.java     
 
(original)
+++  
trunk/dev/core/src/com/google/gwt/dev/javac/CompilationUnitInvalidator.java     
 
Mon Jul  6 14:45:31 2009
@@ -202,6 +202,7 @@
          CompilationUnitDeclaration jdtCud = unit.getJdtCud();
          JSORestrictionsChecker.check(state.jsoState, jdtCud);
          JsniChecker.check(jdtCud);
+        ArtificialRescueChecker.check(unit);
          BinaryTypeReferenceRestrictionsChecker.check(jdtCud,
              validBinaryTypeNames);
        }

Modified: trunk/dev/core/src/com/google/gwt/dev/jdt/AbstractCompiler.java
==============================================================================
--- trunk/dev/core/src/com/google/gwt/dev/jdt/AbstractCompiler.java      
(original)
+++ trunk/dev/core/src/com/google/gwt/dev/jdt/AbstractCompiler.java     Mon  
Jul  6 14:45:31 2009
@@ -156,32 +156,13 @@
        // Examine the cud for magic types.
        //
        String[] typeNames = doFindAdditionalTypesUsingJsni(logger, unit);
-
-      // Accept each new compilation unit.
-      //
-      for (int i = 0; i < typeNames.length; i++) {
-        String typeName = typeNames[i];
-        final String msg = "Need additional type '" + typeName + "'";
-        logger.log(TreeLogger.SPAM, msg, null);
-
-        resolvePossiblyNestedType(typeName);
-      }
+      addAdditionalTypes(logger, typeNames);

        typeNames = doFindAdditionalTypesUsingRebinds(logger, unit);
+      addAdditionalTypes(logger, typeNames);

-      // Accept each new compilation unit, and check for instantiability
-      //
-      for (int i = 0; i < typeNames.length; i++) {
-        String typeName = typeNames[i];
-        final String msg = "Need additional type '" + typeName + "'";
-        logger.log(TreeLogger.SPAM, msg, null);
-
-        // This causes the compiler to find the additional type, possibly
-        // winding its back to ask for the compilation unit from the source
-        // oracle.
-        //
-        resolvePossiblyNestedType(typeName);
-      }
+      typeNames = doFindAdditionalTypesUsingArtificialRescues(logger,  
unit);
+      addAdditionalTypes(logger, typeNames);

        doCompilationUnitDeclarationValidation(unit, logger);

@@ -194,6 +175,21 @@
        jdtProcessNanos += System.nanoTime() - processBeginNanos;
      }

+    /**
+     * Helper method for process() that receives the types found by magic.  
This
+     * causes the compiler to find the additional type, possibly winding  
its
+     * back to ask for the compilation unit from the source oracle.
+     */
+    private void addAdditionalTypes(TreeLogger logger, String[] typeNames)  
{
+      for (int i = 0; i < typeNames.length; i++) {
+        String typeName = typeNames[i];
+        final String msg = "Need additional type '" + typeName + "'";
+        logger.log(TreeLogger.SPAM, msg, null);
+
+        resolvePossiblyNestedType(typeName);
+      }
+    }
+
      private void compile(ICompilationUnit[] units,
          Set<CompilationUnitDeclaration> cuds) {
        this.cuds = cuds;
@@ -503,6 +499,13 @@
    protected void doCompilationUnitDeclarationValidation(
        CompilationUnitDeclaration cud, TreeLogger logger) {
      // Do nothing by default.
+  }
+
+  @SuppressWarnings("unused")
+  // overrider may use unused parameter
+  protected String[] doFindAdditionalTypesUsingArtificialRescues(
+      TreeLogger logger, CompilationUnitDeclaration cud) {
+    return Empty.STRINGS;
    }

    @SuppressWarnings("unused")

Modified:  
trunk/dev/core/src/com/google/gwt/dev/jdt/WebModeCompilerFrontEnd.java
==============================================================================
--- trunk/dev/core/src/com/google/gwt/dev/jdt/WebModeCompilerFrontEnd.java      
 
(original)
+++ trunk/dev/core/src/com/google/gwt/dev/jdt/WebModeCompilerFrontEnd.java      
 
Mon Jul  6 14:45:31 2009
@@ -19,6 +19,7 @@
  import com.google.gwt.core.ext.UnableToCompleteException;
  import com.google.gwt.core.ext.typeinfo.JClassType;
  import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.dev.javac.ArtificialRescueChecker;
  import com.google.gwt.dev.javac.CompilationState;
  import com.google.gwt.dev.javac.CompilationUnit;
  import com.google.gwt.dev.javac.CompiledClass;
@@ -142,6 +143,14 @@
       * Anything that makes it here was already checked by AstCompiler while
       * building TypeOracle; no need to rerun checks.
       */
+  }
+
+  @Override
+  protected String[] doFindAdditionalTypesUsingArtificialRescues(
+      TreeLogger logger, CompilationUnitDeclaration cud) {
+    List<String> types =  
ArtificialRescueChecker.collectReferencedTypes(cud);
+    return types.isEmpty() ? Empty.STRINGS
+        : types.toArray(new String[types.size()]);
    }

    /**

Modified: trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java
==============================================================================
--- trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java     
(original)
+++ trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java    Mon  
Jul  6 14:45:31 2009
@@ -31,6 +31,12 @@
  public abstract class JDeclaredType extends JReferenceType {

    /**
+   * The other nodes that this node should implicitly rescue. Special
+   * serialization treatment.
+   */
+  protected transient List<JNode> artificialRescues = Lists.create();
+
+  /**
     * This type's fields. Special serialization treatment.
     */
    protected transient List<JField> fields = Lists.create();
@@ -60,6 +66,10 @@
      super(info, name);
    }

+  public void addArtificialRescue(JNode node) {
+    artificialRescues = Lists.add(artificialRescues, node);
+  }
+
    /**
     * Adds a field to this type.
     */
@@ -91,9 +101,9 @@
     * statically know the clinit method has already run when:
     * <ol>
     * <li><code>this == targetType</code></li>
-   * <li><code>this</code> is a subclass of <code>targetType</code>,
-   * because my clinit would have already run this  
<code>targetType</code>'s
-   * clinit; see JLS 12.4</li>
+   * <li><code>this</code> is a subclass of <code>targetType</code>,  
because my
+   * clinit would have already run this <code>targetType</code>'s clinit;  
see
+   * JLS 12.4</li>
     * </ol>
     */
    public boolean checkClinitTo(JDeclaredType targetType) {
@@ -118,6 +128,10 @@
      return true;
    }

+  public List<JNode> getArtificialRescues() {
+    return artificialRescues;
+  }
+
    /**
     * Returns this type's fields;does not include fields defined in a super  
type
     * unless they are overridden by this type.
@@ -168,8 +182,7 @@
    }

    /**
-   * Returns <code>true</code> when this class's clinit must be run
-   * dynamically.
+   * Returns <code>true</code> when this class's clinit must be run  
dynamically.
     */
    public boolean hasClinit() {
      return hasClinit;
@@ -231,9 +244,10 @@
        ClassNotFoundException {
      fields = (List<JField>) stream.readObject();
      methods = (List<JMethod>) stream.readObject();
+    artificialRescues = (List<JNode>) stream.readObject();
    }

-  /**
+/**
     * See {...@link #writeMethodBodies(ObjectOutputStream).
     *
     * @see #writeMethodBodies(ObjectOutputStream)
@@ -265,6 +279,7 @@
    void writeMembers(ObjectOutputStream stream) throws IOException {
      stream.writeObject(fields);
      stream.writeObject(methods);
+    stream.writeObject(artificialRescues);
    }

    /**

Modified: trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
==============================================================================
--- trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java (original)
+++ trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java Mon Jul  6  
14:45:31 2009
@@ -1055,9 +1055,9 @@
    }

    /**
-   * If <code>method</code> is a static impl method, returns the instance
-   * method that <code>method</code> is the implementation of. Otherwise,
-   * returns <code>null</code>.
+   * If <code>method</code> is a static impl method, returns the instance  
method
+   * that <code>method</code> is the implementation of. Otherwise, returns
+   * <code>null</code>.
     */
    public JMethod staticImplFor(JMethod method) {
      return staticToInstanceMap.get(method);

Modified:  
trunk/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java
==============================================================================
--- trunk/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java     
 
(original)
+++ trunk/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java     
 
Mon Jul  6 14:45:31 2009
@@ -547,6 +547,19 @@

          if (doVisit) {
            accept(type);
+
+          if (type instanceof JDeclaredType) {
+            for (JNode artificial : ((JDeclaredType)  
type).getArtificialRescues()) {
+              if (artificial instanceof JReferenceType) {
+                rescue((JReferenceType) artificial, true, true);
+                rescue(program.getLiteralClass((JReferenceType)  
artificial).getField());
+              } else if (artificial instanceof JVariable) {
+                rescue((JVariable) artificial);
+              } else if (artificial instanceof JMethod) {
+                rescue((JMethod) artificial);
+              }
+            }
+          }
          }
        }
      }

Modified:  
trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
==============================================================================
--- trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java  
(original)
+++ trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java Mon  
Jul  6 14:45:31 2009
@@ -15,6 +15,7 @@
   */
  package com.google.gwt.dev.jjs.impl;

+import com.google.gwt.core.client.impl.ArtificialRescue;
  import com.google.gwt.dev.jjs.HasSourceInfo;
  import com.google.gwt.dev.jjs.InternalCompilerException;
  import com.google.gwt.dev.jjs.JJSOptions;
@@ -93,14 +94,17 @@
  import com.google.gwt.dev.js.ast.JsModVisitor;
  import com.google.gwt.dev.js.ast.JsNameRef;
  import com.google.gwt.dev.js.ast.JsProgram;
+import com.google.gwt.dev.util.Empty;
  import com.google.gwt.dev.util.JsniRef;

+import org.eclipse.jdt.core.compiler.CharOperation;
  import org.eclipse.jdt.core.compiler.IProblem;
  import org.eclipse.jdt.internal.compiler.CompilationResult;
  import org.eclipse.jdt.internal.compiler.ast.AND_AND_Expression;
  import org.eclipse.jdt.internal.compiler.ast.ASTNode;
  import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
  import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
+import org.eclipse.jdt.internal.compiler.ast.Annotation;
  import org.eclipse.jdt.internal.compiler.ast.ArrayAllocationExpression;
  import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
  import org.eclipse.jdt.internal.compiler.ast.ArrayReference;
@@ -131,6 +135,7 @@
  import org.eclipse.jdt.internal.compiler.ast.InstanceOfExpression;
  import org.eclipse.jdt.internal.compiler.ast.LabeledStatement;
  import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
  import org.eclipse.jdt.internal.compiler.ast.MessageSend;
  import org.eclipse.jdt.internal.compiler.ast.NullLiteral;
  import org.eclipse.jdt.internal.compiler.ast.OR_OR_Expression;
@@ -141,8 +146,10 @@
  import org.eclipse.jdt.internal.compiler.ast.QualifiedSuperReference;
  import org.eclipse.jdt.internal.compiler.ast.QualifiedThisReference;
  import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
+import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation;
  import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
  import org.eclipse.jdt.internal.compiler.ast.Statement;
+import org.eclipse.jdt.internal.compiler.ast.StringLiteral;
  import org.eclipse.jdt.internal.compiler.ast.SuperReference;
  import org.eclipse.jdt.internal.compiler.ast.SwitchStatement;
  import org.eclipse.jdt.internal.compiler.ast.SynchronizedStatement;
@@ -471,6 +478,8 @@
            processEnumType((JEnumType) currentClass);
          }

+        processArtificialRescues(x);
+
          currentClassScope = null;
          currentClass = null;
          currentSeparatorPositions = null;
@@ -2429,6 +2438,122 @@
        return variable;
      }

+    private void processArtificialRescue(Annotation rescue) {
+      assert rescue != null;
+
+      JReferenceType classType = null;
+      String[] fields = Empty.STRINGS;
+      boolean instantiable = false;
+      String[] methods = Empty.STRINGS;
+      String typeName = null;
+
+      for (MemberValuePair pair : rescue.memberValuePairs()) {
+        String name = String.valueOf(pair.name);
+        Expression value = pair.value;
+
+        if ("className".equals(name)) {
+          typeName = value.constant.stringValue();
+
+          // Invalid references should be caught in ArtificialRescueChecker
+          classType = (JReferenceType)  
program.getTypeFromJsniRef(typeName);
+
+        } else if ("fields".equals(name)) {
+          if (value instanceof StringLiteral) {
+            fields = new String[] {value.constant.stringValue()};
+          } else if (value instanceof ArrayInitializer) {
+            ArrayInitializer init = (ArrayInitializer) value;
+            fields = new String[init.expressions == null ? 0
+                : init.expressions.length];
+            for (int i = 0, j = fields.length; i < j; i++) {
+              fields[i] = init.expressions[i].constant.stringValue();
+            }
+          }
+
+        } else if ("instantiable".equals(name)) {
+          instantiable = value.constant.booleanValue();
+
+        } else if ("methods".equals(name)) {
+          if (value instanceof StringLiteral) {
+            methods = new String[] {value.constant.stringValue()};
+          } else if (value instanceof ArrayInitializer) {
+            ArrayInitializer init = (ArrayInitializer) value;
+            methods = new String[init.expressions == null ? 0
+                : init.expressions.length];
+            for (int i = 0, j = methods.length; i < j; i++) {
+              methods[i] = init.expressions[i].constant.stringValue();
+            }
+          }
+        } else {
+          throw new InternalCompilerException(
+              "Unknown Rescue annotation member " + name);
+        }
+      }
+
+      assert classType != null : "classType " + typeName;
+      assert fields != null : "fields";
+      assert methods != null : "methods";
+
+      if (instantiable) {
+        currentClass.addArtificialRescue(classType);
+
+        // Make sure that a class literal for the type has been allocated
+        program.getLiteralClass(classType);
+      }
+
+      if (classType instanceof JDeclaredType) {
+        List<String> toRescue = new ArrayList<String>();
+        Collections.addAll(toRescue, fields);
+        Collections.addAll(toRescue, methods);
+
+        for (String name : toRescue) {
+          JsniRef ref = JsniRef.parse("@" + classType.getName() + "::" +  
name);
+          final String[] errors = {null};
+          HasEnclosingType node = JsniRefLookup.findJsniRefTarget(ref,  
program,
+              new JsniRefLookup.ErrorReporter() {
+                public void reportError(String error) {
+                  errors[0] = error;
+                }
+              });
+          if (errors[0] != null) {
+            // Should have been caught by ArtificialRescueChecker
+            throw new InternalCompilerException(
+                "Unable to artificially rescue " + name + ": " +  
errors[0]);
+          }
+
+          currentClass.addArtificialRescue((JNode) node);
+        }
+      }
+    }
+
+    private void processArtificialRescues(TypeDeclaration x) {
+      if (x.annotations == null) {
+        return;
+      }
+
+      for (Annotation a : x.annotations) {
+        if (!ArtificialRescue.class.getName().equals(
+            CharOperation.toString(((ReferenceBinding)  
a.resolvedType).compoundName))) {
+          continue;
+        }
+
+        Expression value = ((SingleMemberAnnotation) a).memberValue;
+        assert value != null;
+        if (value instanceof ArrayInitializer) {
+          for (Expression e : ((ArrayInitializer) value).expressions) {
+            processArtificialRescue((Annotation) e);
+          }
+        } else if (value instanceof Annotation) {
+          processArtificialRescue((Annotation) value);
+        } else {
+          throw new InternalCompilerException(
+              "Unable to process annotation with value of type "
+                  + value.getClass().getName());
+        }
+
+        return;
+      }
+    }
+
      /**
       * Helper for creating all JBinaryOperation. Several different JDT  
nodes can
       * result in binary operations: AND_AND_Expression, Assignment,
@@ -2673,7 +2798,8 @@
          }
        }

-      private HasEnclosingType findJsniRefTarget(final SourceInfo info,  
String ident) {
+      private HasEnclosingType findJsniRefTarget(final SourceInfo info,
+          String ident) {
          JsniRef parsed = JsniRef.parse(ident);
          if (parsed == null) {
            reportJsniError(info, methodDecl,

Modified:  
trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
==============================================================================
---  
trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java       
 
(original)
+++  
trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java       
 
Mon Jul  6 14:45:31 2009
@@ -60,6 +60,7 @@
  import com.google.gwt.dev.jjs.ast.JNameOf;
  import com.google.gwt.dev.jjs.ast.JNewArray;
  import com.google.gwt.dev.jjs.ast.JNewInstance;
+import com.google.gwt.dev.jjs.ast.JNode;
  import com.google.gwt.dev.jjs.ast.JParameter;
  import com.google.gwt.dev.jjs.ast.JParameterRef;
  import com.google.gwt.dev.jjs.ast.JPostfixOperation;
@@ -133,6 +134,7 @@
  import com.google.gwt.dev.js.ast.JsWhile;
  import com.google.gwt.dev.js.ast.JsVars.JsVar;

+import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collections;
  import java.util.EnumMap;
@@ -250,6 +252,12 @@
         */
        nullMethodName = topScope.declareName(nullMethod.getName());
        names.put(nullMethod, nullMethodName);
+
+      /*
+       * Make sure we record all of the program's array types since
+       * JProgram.traverse() doesn't iterate over them.
+       */
+      accept(new ArrayList<JArrayType>(program.getAllArrayTypes()));
      }

      @Override
@@ -643,6 +651,16 @@

        if (!vars.isEmpty()) {
          globalStmts.add(vars);
+      }
+
+      for (JNode node : x.getArtificialRescues()) {
+        if (node instanceof JMethod) {
+          JsName jsName = names.get(node);
+          if (jsName != null) {
+            JsFunction func = (JsFunction) jsName.getStaticRef();
+            func.setArtificiallyRescued(true);
+          }
+        }
        }
      }


Modified: trunk/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
==============================================================================
--- trunk/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java  (original)
+++ trunk/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java  Mon Jul  6  
14:45:31 2009
@@ -15,6 +15,7 @@
   */
  package com.google.gwt.dev.jjs.impl;

+import com.google.gwt.dev.jjs.InternalCompilerException;
  import com.google.gwt.dev.jjs.SourceInfo;
  import com.google.gwt.dev.jjs.ast.CanBeStatic;
  import com.google.gwt.dev.jjs.ast.Context;
@@ -143,12 +144,18 @@
      @Override
      public void endVisit(JNameOf x, Context ctx) {
        HasName node = x.getNode();
-      JReferenceType isType = node instanceof JReferenceType
-          ? (JReferenceType) node : null;
-      if (isType == null && !referencedNonTypes.contains(node)) {
-        ctx.replaceMe(program.getLiteralNull());
-      } else if (isType != null
-          && !program.typeOracle.isInstantiatedType(isType)) {
+      boolean pruned;
+      if (node instanceof JField) {
+        pruned = isPruned((JField) node);
+      } else if (node instanceof JMethod) {
+        pruned = isPruned((JMethod) node);
+      } else if (node instanceof JReferenceType) {
+        pruned = !program.typeOracle.isInstantiatedType((JReferenceType)  
node);
+      } else {
+        throw new InternalCompilerException("Unhandled JNameOf node: " +  
node);
+      }
+
+      if (pruned) {
          ctx.replaceMe(program.getLiteralNull());
        }
      }

Modified:  
trunk/dev/core/src/com/google/gwt/dev/js/JsUnusedFunctionRemover.java
==============================================================================
--- trunk/dev/core/src/com/google/gwt/dev/js/JsUnusedFunctionRemover.java       
 
(original)
+++ trunk/dev/core/src/com/google/gwt/dev/js/JsUnusedFunctionRemover.java       
 
Mon Jul  6 14:45:31 2009
@@ -43,7 +43,7 @@
      @Override
      public void endVisit(JsFunction x, JsContext<JsExpression> ctx) {
        // Anonymous function, ignore it
-      if (x.getName() != null) {
+      if (x.getName() != null && !x.isArtificiallyRescued()) {
          toRemove.put(x.getName(), x);
        }
      }

Modified: trunk/dev/core/src/com/google/gwt/dev/js/ast/JsFunction.java
==============================================================================
--- trunk/dev/core/src/com/google/gwt/dev/js/ast/JsFunction.java        
(original)
+++ trunk/dev/core/src/com/google/gwt/dev/js/ast/JsFunction.java        Mon Jul 
 6  
14:45:31 2009
@@ -35,6 +35,7 @@
    protected JsBlock body;
    protected final List<JsParameter> params = new ArrayList<JsParameter>();
    protected final JsScope scope;
+  private boolean artificiallyRescued;
    private boolean executeOnce;
    private boolean fromJava;
    private JsFunction impliedExecute;
@@ -105,6 +106,10 @@
      return name != null;
    }

+  public boolean isArtificiallyRescued() {
+    return artificiallyRescued;
+  }
+
    public boolean isBooleanFalse() {
      return false;
    }
@@ -123,6 +128,10 @@

    public boolean isFromJava() {
      return fromJava;
+  }
+
+  public void setArtificiallyRescued(boolean rescued) {
+    this.artificiallyRescued = rescued;
    }

    public void setBody(JsBlock body) {

Added:  
trunk/dev/core/super/com/google/gwt/core/client/impl/ArtificialRescue.java
==============================================================================
--- (empty file)
+++  
trunk/dev/core/super/com/google/gwt/core/client/impl/ArtificialRescue.java      
 
Mon Jul  6 14:45:31 2009
@@ -0,0 +1,54 @@
+/*
+ * 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.impl;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation to specify that if a class is rescued, other types,  
methods, or
+ * fields should be rescued as well. This annotation is an implementation  
detail
+ * of the deRPC code and its use by third parties is not supported.
+ */
+...@target(ElementType.TYPE)
+public @interface ArtificialRescue {
+  /**
+   * Specifies the elements of a single type to rescue.
+   */
+  @Target(value = {})
+  public @interface Rescue {
+    /**
+     * The class to be retained. Primitive array types should be referenced
+     * using their JSNI type name.
+     */
+    String className();
+
+    boolean instantiable() default false;
+
+    /**
+     * Fields are specified as raw names. That is, <code>fieldName</code>.
+     */
+    String[] fields() default {};
+
+    /**
+     * Methods are specified as unqualified JSNI signatures. That is,
+     * <code>methodName(Lsome/Type;...)</code>.
+     */
+    String[] methods() default {};
+  }
+
+  Rescue[] value();
+}

Added:  
trunk/dev/core/test/com/google/gwt/dev/javac/ArtificialRescueCheckerTest.java
==============================================================================
--- (empty file)
+++  
trunk/dev/core/test/com/google/gwt/dev/javac/ArtificialRescueCheckerTest.java   
 
Mon Jul  6 14:45:31 2009
@@ -0,0 +1,150 @@
+/*
+ * 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.dev.javac;
+
+import com.google.gwt.core.client.impl.ArtificialRescue;
+
+/**
+ * Tests for the ArtificialRescueChecker.
+ */
+public class ArtificialRescueCheckerTest extends CheckerTestCase {
+
+  public void testBadClassName() {
+    StringBuilder builder = builder();
+    builder.append("@ArtificialRescue(@Rescue(className=\"Fail\"))\n");
+    builder.append("class Buggy{}");
+    shouldGenerateError(builder, targetClass(), 3,
+        ArtificialRescueChecker.notFound("Fail"));
+  }
+
+  public void testBadFieldName() {
+    StringBuilder builder = builder();
+    builder.append("@ArtificialRescue(@Rescue(className=\"Target\",  
fields=\"foo\"))\n");
+    builder.append("class Buggy{}");
+    shouldGenerateError(builder, targetClass(), 3,
+        ArtificialRescueChecker.unknownField("foo"));
+  }
+
+  public void testBadMembersOnArray() {
+    StringBuilder builder = builder();
+    builder.append("@ArtificialRescue(@Rescue(className=\"Target[]\",  
fields=\"foo\"))\n");
+    builder.append("class Buggy{}");
+    shouldGenerateError(builder, targetClass(), 3,
+        ArtificialRescueChecker.noFieldsAllowed());
+  }
+
+  public void testBadMethodName() {
+    StringBuilder builder = builder();
+    builder.append("@ArtificialRescue(@Rescue(className=\"Target\",  
methods=\"blah()\"))\n");
+    builder.append("class Buggy{}");
+    shouldGenerateError(builder, targetClass(), 3,
+        ArtificialRescueChecker.noMethod("Target", "blah"));
+  }
+
+  public void testBadMethodSignature() {
+    StringBuilder builder = builder();
+    builder.append("@ArtificialRescue(@Rescue(className=\"Target\",  
methods=\"blah()()\"))\n");
+    builder.append("class Buggy{}");
+    shouldGenerateError(builder, targetClass(), 3,
+        ArtificialRescueChecker.badMethodSignature("blah()()"));
+  }
+
+  public void testBadMethodSignatureFullyQualified() {
+    StringBuilder builder = builder();
+    builder.append("@ArtificialRescue(@Rescue(className=\"Target\",  
methods=\"@Target::blah()\"))\n");
+    builder.append("class Buggy{}");
+    shouldGenerateError(builder, targetClass(), 3,
+        ArtificialRescueChecker.nameAndTypesOnly());
+  }
+
+  public void testOkArray() {
+    StringBuilder builder = builder();
+    builder.append("@ArtificialRescue(@Rescue(className=\"Target[]\"))\n");
+    builder.append("class Buggy{}");
+    shouldGenerateNoError(builder, targetClass());
+  }
+
+  public void testOkConstructor() {
+    StringBuilder builder = builder();
+    builder.append("@ArtificialRescue(@Rescue(className=\"Target\",  
methods=\"Target()\"))\n");
+    builder.append("class Buggy{}");
+    shouldGenerateNoError(builder, targetClass());
+  }
+
+  public void testOkInner() {
+    StringBuilder builder = builder();
+    builder.append("@ArtificialRescue(@Rescue(className=\"Target.Inner\",  
methods=\"Target$Inner()\"))\n");
+    builder.append("class Buggy{}");
+    shouldGenerateNoError(builder, targetClass());
+  }
+
+  public void testOkOneField() {
+    StringBuilder builder = builder();
+    builder.append("@ArtificialRescue(@Rescue(className=\"Target\",  
fields=\"i\"))\n");
+    builder.append("class Buggy{}");
+    shouldGenerateNoError(builder, targetClass());
+  }
+
+  public void testOkOneMethod() {
+    StringBuilder builder = builder();
+    builder.append("@ArtificialRescue(@Rescue(className=\"Target\",  
methods=\"getI()\"))\n");
+    builder.append("class Buggy{}");
+    shouldGenerateNoError(builder, targetClass());
+  }
+
+  public void testOkPrimitiveArray() {
+    StringBuilder builder = builder();
+    builder.append("@ArtificialRescue(@Rescue(className=\"Z[]\"))\n");
+    builder.append("class Buggy{}");
+    shouldGenerateNoError(builder, targetClass());
+  }
+
+  public void testOkTwoFields() {
+    StringBuilder builder = builder();
+    builder.append("@ArtificialRescue(@Rescue(className=\"Target\",  
fields={\"i\", \"str\"}))\n");
+    builder.append("class Buggy{}");
+    shouldGenerateNoError(builder, targetClass());
+  }
+
+  public void testOkTwoMethods() {
+    StringBuilder builder = builder();
+    builder.append("@ArtificialRescue(@Rescue(className=\"Target\",  
methods={\"getI()\", \"getI(Z)\"}))\n");
+    builder.append("class Buggy{}");
+    shouldGenerateNoError(builder, targetClass());
+  }
+
+  private StringBuilder builder() {
+    StringBuilder code = new StringBuilder();
+    code.append("import " + ArtificialRescue.class.getCanonicalName()  
+ ";\n");
+    code.append("import " +  
ArtificialRescue.Rescue.class.getCanonicalName()
+        + ";\n");
+    return code;
+  }
+
+  private StringBuilder targetClass() {
+    StringBuilder targetClass = new StringBuilder();
+    targetClass = new StringBuilder();
+    targetClass.append("class Target {\n");
+    targetClass.append("  public class Inner{}\n");
+    targetClass.append("  private String str;\n");
+    targetClass.append("  private int i;\n");
+    targetClass.append("  public int getI() { return i; }\n");
+    targetClass.append("  public int getI(boolean override) {return  
override ? 0 : i; }\n");
+    targetClass.append("  private String getStr() {return str;}");
+    targetClass.append("}");
+    return targetClass;
+  }
+}

Added: trunk/dev/core/test/com/google/gwt/dev/javac/CheckerTestCase.java
==============================================================================
--- (empty file)
+++ trunk/dev/core/test/com/google/gwt/dev/javac/CheckerTestCase.java   Mon  
Jul  6 14:45:31 2009
@@ -0,0 +1,108 @@
+/*
+ * 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.dev.javac;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.TreeLogger.Type;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.dev.util.UnitTestTreeLogger;
+
+import junit.framework.TestCase;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A base type for testing the code checkers.
+ */
+public class CheckerTestCase extends TestCase {
+
+  protected void shouldGenerate(CharSequence buggyCode, CharSequence  
extraCode,
+      int line, Type logLevel, String logHeader, String message) {
+    UnitTestTreeLogger.Builder b = new UnitTestTreeLogger.Builder();
+    b.setLowestLogLevel(logLevel);
+    if (message != null) {
+      b.expect(logLevel, logHeader + " in '/mock/Buggy'", null);
+      final String fullMessage = "Line " + line + ": " + message;
+      b.expect(logLevel, fullMessage, null);
+    }
+    UnitTestTreeLogger logger = b.createLogger();
+    TypeOracle oracle = buildOracle(buggyCode, extraCode, logger);
+    logger.assertCorrectLogEntries();
+    if (message != null && logLevel == TreeLogger.ERROR) {
+      assertNull("Buggy compilation unit not removed from type oracle",
+          oracle.findType("Buggy"));
+    } else {
+      assertNotNull("Buggy compilation unit removed with only a warning",
+          oracle.findType("Buggy"));
+    }
+  }
+
+  protected void shouldGenerateError(CharSequence buggyCode,
+      CharSequence extraCode, int line, String message) {
+    shouldGenerate(buggyCode, extraCode, line, TreeLogger.ERROR, "Errors",
+        message);
+  }
+
+  protected void shouldGenerateError(CharSequence buggyCode, int line,
+      String message) {
+    shouldGenerateError(buggyCode, null, line, message);
+  }
+
+  protected void shouldGenerateNoError(CharSequence code) {
+    shouldGenerateNoError(code, null);
+  }
+
+  protected void shouldGenerateNoError(CharSequence code, CharSequence  
extraCode) {
+    shouldGenerateError(code, extraCode, -1, null);
+  }
+
+  protected void shouldGenerateNoWarning(CharSequence code) {
+    shouldGenerateWarning(code, -1, null);
+  }
+
+  protected void shouldGenerateWarning(CharSequence buggyCode,
+      CharSequence extraCode, int line, String message) {
+    shouldGenerate(buggyCode, extraCode, line, TreeLogger.WARN, "Warnings",
+        message);
+  }
+
+  protected void shouldGenerateWarning(CharSequence buggyCode, int line,
+      String message) {
+    shouldGenerateWarning(buggyCode, null, line, message);
+  }
+
+  private void addLongCheckingCups(Set<CompilationUnit> units) {
+    StringBuilder code = new StringBuilder();
+    code.append("package com.google.gwt.core.client;\n");
+    code.append("public @interface UnsafeNativeLong {\n");
+    code.append("}\n");
+    units.add(new MockCompilationUnit(
+        "com.google.gwt.core.client.UnsafeNativeLong", code.toString()));
+  }
+
+  private TypeOracle buildOracle(CharSequence buggyCode,
+      CharSequence extraCode, UnitTestTreeLogger logger) {
+    Set<CompilationUnit> units = new HashSet<CompilationUnit>();
+    addLongCheckingCups(units);
+    units.add(new MockCompilationUnit("Buggy", buggyCode.toString()));
+    if (extraCode != null) {
+      units.add(new MockCompilationUnit("Extra", extraCode.toString()));
+    }
+    return TypeOracleTestingUtils.buildStandardTypeOracleWith(logger,
+        units.toArray(new CompilationUnit[units.size()]));
+  }
+}

Modified: trunk/dev/core/test/com/google/gwt/dev/javac/JsniCheckerTest.java
==============================================================================
--- trunk/dev/core/test/com/google/gwt/dev/javac/JsniCheckerTest.java    
(original)
+++ trunk/dev/core/test/com/google/gwt/dev/javac/JsniCheckerTest.java   Mon  
Jul  6 14:45:31 2009
@@ -15,20 +15,10 @@
   */
  package com.google.gwt.dev.javac;

-import com.google.gwt.core.ext.TreeLogger;
-import com.google.gwt.core.ext.TreeLogger.Type;
-import com.google.gwt.core.ext.typeinfo.TypeOracle;
-import com.google.gwt.dev.util.UnitTestTreeLogger;
-
-import junit.framework.TestCase;
-
-import java.util.HashSet;
-import java.util.Set;
-
  /**
   * Test access to longs from JSNI.
   */
-public class JsniCheckerTest extends TestCase {
+public class JsniCheckerTest extends CheckerTestCase {

    /**
     * JSNI references to anonymous inner classes is deprecated.
@@ -402,81 +392,5 @@
            2,
            "Referencing field 'Extra.Inner.x': type 'long' is not safe to  
access in JSNI code");
      }
-  }
-
-  private void addLongCheckingCups(Set<CompilationUnit> units) {
-    StringBuilder code = new StringBuilder();
-    code.append("package com.google.gwt.core.client;\n");
-    code.append("public @interface UnsafeNativeLong {\n");
-    code.append("}\n");
-    units.add(new MockCompilationUnit(
-        "com.google.gwt.core.client.UnsafeNativeLong", code.toString()));
-  }
-
-  private TypeOracle buildOracle(CharSequence buggyCode,
-      CharSequence extraCode, UnitTestTreeLogger logger) {
-    Set<CompilationUnit> units = new HashSet<CompilationUnit>();
-    addLongCheckingCups(units);
-    units.add(new MockCompilationUnit("Buggy", buggyCode.toString()));
-    if (extraCode != null) {
-      units.add(new MockCompilationUnit("Extra", extraCode.toString()));
-    }
-    return TypeOracleTestingUtils.buildStandardTypeOracleWith(logger,
-        units.toArray(new CompilationUnit[units.size()]));
-  }
-
-  private void shouldGenerate(CharSequence buggyCode, CharSequence  
extraCode,
-      int line, Type logLevel, String logHeader, String message) {
-    UnitTestTreeLogger.Builder b = new UnitTestTreeLogger.Builder();
-    b.setLowestLogLevel(logLevel);
-    if (message != null) {
-      b.expect(logLevel, logHeader + " in '/mock/Buggy'", null);
-      final String fullMessage = "Line " + line + ": " + message;
-      b.expect(logLevel, fullMessage, null);
-    }
-    UnitTestTreeLogger logger = b.createLogger();
-    TypeOracle oracle = buildOracle(buggyCode, extraCode, logger);
-    logger.assertCorrectLogEntries();
-    if (message != null && logLevel == TreeLogger.ERROR) {
-      assertNull("Buggy compilation unit not removed from type oracle",
-          oracle.findType("Buggy"));
-    } else {
-      assertNotNull("Buggy compilation unit removed with only a warning",
-          oracle.findType("Buggy"));
-    }
-  }
-
-  private void shouldGenerateError(CharSequence buggyCode,
-      CharSequence extraCode, int line, String message) {
-    shouldGenerate(buggyCode, extraCode, line, TreeLogger.ERROR, "Errors",
-        message);
-  }
-
-  private void shouldGenerateError(CharSequence buggyCode, int line,
-      String message) {
-    shouldGenerateError(buggyCode, null, line, message);
-  }
-
-  private void shouldGenerateNoError(CharSequence code) {
-    shouldGenerateNoError(code, null);
-  }
-
-  private void shouldGenerateNoError(CharSequence code, CharSequence  
extraCode) {
-    shouldGenerateError(code, extraCode, -1, null);
-  }
-
-  private void shouldGenerateNoWarning(CharSequence code) {
-    shouldGenerateWarning(code, -1, null);
-  }
-
-  private void shouldGenerateWarning(CharSequence buggyCode,
-      CharSequence extraCode, int line, String message) {
-    shouldGenerate(buggyCode, extraCode, line, TreeLogger.WARN, "Warnings",
-        message);
-  }
-
-  private void shouldGenerateWarning(CharSequence buggyCode, int line,
-      String message) {
-    shouldGenerateWarning(buggyCode, null, line, message);
    }
  }

Modified: trunk/user/src/com/google/gwt/core/client/impl/Impl.java
==============================================================================
--- trunk/user/src/com/google/gwt/core/client/impl/Impl.java    (original)
+++ trunk/user/src/com/google/gwt/core/client/impl/Impl.java    Mon Jul  6  
14:45:31 2009
@@ -79,7 +79,7 @@
     * @return the name by which the named member can be accessed at  
runtime, or
     *         <code>null</code> if the requested member has been pruned  
from the
     *         output.
-   * @see com.google.gwt.core.client.ArtificialRescue
+   * @see com.google.gwt.core.client.impl.ArtificialRescue
     */
    public static String getNameOf(String jsniIdent) {
      /*

--~--~---------~--~----~------------~-------~--~----~
http://groups.google.com/group/Google-Web-Toolkit-Contributors
-~----------~----~----~----~------~----~------~--~---

Reply via email to