Revision: 7539
Author: [email protected]
Date: Tue Feb  9 15:22:29 2010
Log: Compiler infrastructure addition to selectively retain Java annotations in the AST.
Patch by: bobv
Review by: scottb, spoon

http://code.google.com/p/google-web-toolkit/source/detail?r=7539

Added:
 /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/HasAnnotations.java
 /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JAnnotation.java
 /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JAnnotationArgument.java
 /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JExternalType.java
 /trunk/dev/core/test/com/google/gwt/dev/jjs/impl/JAnnotationTest.java
Modified:
 /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JClassLiteral.java
 /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JClassType.java
 /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java
 /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JField.java
 /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JInterfaceType.java
 /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JLocal.java
 /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
 /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JParameter.java
 /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
 /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JValueLiteral.java
 /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JVariable.java
 /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java
 /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
/trunk/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java
 /trunk/dev/core/test/com/google/gwt/dev/jjs/impl/OptimizerTestBase.java

=======================================
--- /dev/null
+++ /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/HasAnnotations.java Tue Feb 9 15:22:29 2010
@@ -0,0 +1,40 @@
+/*
+ * 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 java.util.List;
+
+/**
+ * Provides access to the annotations defined on an AST node.
+ */
+public interface HasAnnotations {
+  /**
+   * Add a annotation to the node.
+   */
+  void addAnnotation(JAnnotation annotation);
+
+  /**
+   * Returns the annotation declared on the node of the given type or
+   * <code>null</code> if no such annotation exists. For annotations on
+ * subclasses of JDeclaredType, this method will not search the supertypes.
+   */
+  JAnnotation findAnnotation(String className);
+
+  /**
+   * Return the annotations associated with the node.
+   */
+  List<JAnnotation> getAnnotations();
+}
=======================================
--- /dev/null
+++ /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JAnnotation.java Tue Feb 9 15:22:29 2010
@@ -0,0 +1,292 @@
+/*
+ * 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;
+import com.google.gwt.dev.util.collect.Lists;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.List;
+
+/**
+ * Represents a java annotation.
+ */
+public class JAnnotation extends JNode implements JAnnotationArgument {
+  /**
+   * Represents a value contained within an annotation. Single-valued and
+   * array-valued properties are both represented by this type.
+   *
+   * @param <T> the type of JAnnotationValue node that the Property
+   *          encapsulates.
+   * @see {...@link #of}
+   */
+  public static class Property extends JNode {
+    private final String name;
+    private List<JAnnotationArgument> values;
+
+    public Property(SourceInfo sourceInfo, String name,
+        List<JAnnotationArgument> values) {
+      super(sourceInfo);
+      this.name = name;
+      this.values = Lists.normalize(values);
+    }
+
+    public Property(SourceInfo sourceInfo, String name,
+        JAnnotationArgument value) {
+      this(sourceInfo, name, Lists.create(value));
+    }
+
+    public void addValue(JAnnotationArgument value) {
+      values = Lists.add(values, value);
+    }
+
+    public String getName() {
+      return name;
+    }
+
+    public JAnnotationArgument getSingleValue() {
+      if (values.size() != 1) {
+        throw new IllegalStateException(
+            "Expecting single-valued property, found " + values.size()
+                + " values");
+      }
+      return values.get(0);
+    }
+
+    public List<JAnnotationArgument> getValues() {
+      return Lists.normalizeUnmodifiable(values);
+    }
+
+    public List<JNode> getValuesAsNodes() {
+      // Lists.normalizeUnmodifiable would have allocated a new list anyway
+      List<JNode> toReturn = Lists.create();
+      for (JAnnotationArgument value : values) {
+        toReturn = Lists.add(toReturn, value.annotationNode());
+      }
+      return toReturn;
+    }
+
+    @SuppressWarnings("unchecked")
+    public void traverse(JVisitor visitor, Context ctx) {
+      if (visitor.visit(this, ctx)) {
+        // This is a shady cast to a raw list
+        List nodes = visitor.acceptImmutable((List) values);
+
+        // JNode and JAnnotationArgument types have disjoint hierarchies
+        if (JAnnotation.class.desiredAssertionStatus()) {
+          for (int i = 0, j = nodes.size(); i < j; i++) {
+ assert nodes.get(i) instanceof JAnnotationArgument : "Expecting a "
+                + "JAnnotationArgument at index " + i + " found a "
+                + nodes.get(i).getClass().getCanonicalName();
+          }
+        }
+
+        // This is a shady assignment
+        values = nodes;
+      }
+      visitor.endVisit(this, ctx);
+    }
+  }
+
+  /**
+ * This runtime exception is thrown when calling a Class-valued annotation
+   * method when the referenced class is not available to the JVM.
+   */
+  public static class SourceOnlyClassException extends RuntimeException {
+    private final JClassLiteral literal;
+
+    public SourceOnlyClassException(JClassLiteral literal) {
+      super("The type " + literal.getRefType().getName()
+          + " is available only in the module source");
+      this.literal = literal;
+    }
+
+    public JClassLiteral getLiteral() {
+      return literal;
+    }
+  }
+
+  /**
+ * This handles reflective dispatch for Proxy instances onto the data stored
+   * in the AST.
+   *
+   * @param <T> the type of Annotation
+   */
+  private static class AnnotationInvocationHandler<T> implements
+      InvocationHandler {
+    private final Class<T> clazz;
+    private final JAnnotation annotation;
+
+ private AnnotationInvocationHandler(Class<T> clazz, JAnnotation annotation) {
+      this.clazz = clazz;
+      this.annotation = annotation;
+    }
+
+    /**
+     * Handles method invocations.
+     */
+ public Object invoke(Object instance, Method method, Object[] arguments)
+        throws Throwable {
+
+      // Use the proxy object to handle trivial stuff
+      if (method.getDeclaringClass() == Object.class) {
+        return method.invoke(this, arguments);
+      }
+
+      Property prop = annotation.getProperty(method.getName());
+      if (prop == null) {
+        // This works because we're working with real Methods
+        return method.getDefaultValue();
+      }
+
+      if (method.getReturnType().isArray()) {
+        List<JAnnotationArgument> values = prop.getValues();
+        Object toReturn = Array.newInstance(
+            method.getReturnType().getComponentType(), values.size());
+        for (int i = 0, j = values.size(); i < j; i++) {
+          Object value = evaluate(values.get(i));
+          Array.set(toReturn, i, value);
+        }
+        return toReturn;
+      }
+
+      return evaluate(prop.getSingleValue());
+    }
+
+    /**
+     * Convert a JLiteral value into an Object the caller can work with.
+     */
+    private Object evaluate(JAnnotationArgument value)
+        throws ClassNotFoundException {
+
+      if (value instanceof JValueLiteral) {
+        // Primitives
+        return ((JValueLiteral) value).getValueObj();
+
+      } else if (value instanceof JClassLiteral) {
+        String clazzName = ((JClassLiteral) value).getRefType().getName();
+        try {
+          return Class.forName(clazzName, true, clazz.getClassLoader());
+        } catch (ClassNotFoundException e) {
+          throw new SourceOnlyClassException((JClassLiteral) value);
+        }
+
+      } else if (value instanceof JAnnotation) {
+        // Determine the synthetic annotation's type
+        String clazzName = ((JAnnotation) value).getType().getName();
+        // Load the annotation class
+ Class<? extends Annotation> annotationType = Class.forName(clazzName,
+            true, clazz.getClassLoader()).asSubclass(Annotation.class);
+        // Creating the backing annotation
+        return createAnnotation(annotationType, (JAnnotation) value);
+      }
+
+      // Unhandled type
+      throw new RuntimeException("Cannot convert "
+          + value.getClass().getCanonicalName() + " into an Object");
+    }
+  }
+
+  /**
+ * Create a synthetic Annotation instance, based on the data in a JAnnatation.
+   *
+   * @param clazz the type of Annotation
+   * @param annotation the backing data
+   *
+   * @param <T> the type of Annotation
+   * @return an instance of <code>clazz</code>
+   */
+  public static <T extends Annotation> T createAnnotation(Class<T> clazz,
+      JAnnotation annotation) {
+    // Create the annotation as a reflective Proxy instance
+    Object o = Proxy.newProxyInstance(clazz.getClassLoader(),
+        new Class<?>[] {clazz}, new AnnotationInvocationHandler<T>(clazz,
+            annotation));
+
+    @SuppressWarnings("unchecked")
+    T toReturn = (T) o;
+    return toReturn;
+  }
+
+  /**
+ * A utility method to retrieve an annotation of the named type. This method
+   * will not search supertypes or superinterfaces if <code>x</code> is a
+   * JDeclaredType.
+   *
+   * @return the annotation of the requested type, or <code>null</code>
+   */
+  public static JAnnotation findAnnotation(HasAnnotations x,
+      String annotationTypeName) {
+    for (JAnnotation a : x.getAnnotations()) {
+      if (a.getType().getName().equals(annotationTypeName)) {
+        return a;
+      }
+    }
+    return null;
+  }
+
+  private final JType 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;
+  }
+
+  public void addValue(Property value) {
+    properties = Lists.add(properties, value);
+  }
+
+  public JNode annotationNode() {
+    return this;
+  }
+
+  public List<Property> getProperties() {
+    return Lists.normalizeUnmodifiable(properties);
+  }
+
+  /**
+   * Returns the named property or <code>null</code> if it does not exist.
+   */
+  public Property getProperty(String name) {
+    for (Property p : properties) {
+      if (p.getName().equals(name)) {
+        return p;
+      }
+    }
+    return null;
+  }
+
+  public JType getType() {
+    return type;
+  }
+
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      properties = visitor.acceptImmutable(properties);
+    }
+    visitor.endVisit(this, ctx);
+  }
+}
=======================================
--- /dev/null
+++ /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JAnnotationArgument.java Tue Feb 9 15:22:29 2010
@@ -0,0 +1,26 @@
+/*
+ * 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;
+
+/**
+ * A tag interface for JNodes that are legal values for annotation properties.
+ */
+public interface JAnnotationArgument {
+  /**
+   * A convenience method to avoid explicit casts.
+   */
+  JNode annotationNode();
+}
=======================================
--- /dev/null
+++ /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JExternalType.java Tue Feb 9 15:22:29 2010
@@ -0,0 +1,49 @@
+/*
+ * 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);
+  }
+}
=======================================
--- /dev/null
+++ /trunk/dev/core/test/com/google/gwt/dev/jjs/impl/JAnnotationTest.java Tue Feb 9 15:22:29 2010
@@ -0,0 +1,271 @@
+/*
+ * 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.impl;
+
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.dev.javac.impl.MockJavaResource;
+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;
+import com.google.gwt.dev.jjs.ast.JParameter;
+import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JAnnotation.Property;
+import com.google.gwt.dev.jjs.ast.JAnnotation.SourceOnlyClassException;
+
+/**
+ * Tests AST setup of JAnnotation nodes as well as the reflective proxy for
+ * accessing their data.
+ */
+public class JAnnotationTest extends OptimizerTestBase {
+
+  /**
+   * A test class for binary-only annotations.
+   */
+  public @interface BinaryAnnotation {
+    Class<?> c() default Object.class;
+
+    int i() default 2;
+
+    int[] iOne() default 1;
+
+    int[] iTwo() default {1, 2};
+
+    int[] iZero() default {};
+
+    OtherAnnotation[] o() default {
+        @OtherAnnotation("Hello 1"), @OtherAnnotation("Hello 2")};
+
+    String s() default "Hello String";
+  }
+
+  /**
+   * Used to test binary-only class literal behavior.
+   */
+  public @interface ClassAnnotation {
+    Class<?> value();
+  }
+
+  /**
+   * A test case for meta-annotations.
+   */
+  public @interface OtherAnnotation {
+    String value();
+  }
+
+  private static void assertEquals(int[] a, int[] b) {
+    assertEquals(a.length, b.length);
+    for (int i = 0, j = a.length; i < j; i++) {
+      assertEquals(a[i], b[i]);
+    }
+  }
+
+  public void setUp() {
+    // These packages have annotations required by this test
+    JProgram.RECORDED_ANNOTATION_PACKAGES.add("com.google.gwt.dev.jjs");
+    JProgram.RECORDED_ANNOTATION_PACKAGES.add("test");
+
+ sourceOracle.addOrReplace(new MockJavaResource("test.SourceClassAnnotation") {
+      @Override
+      protected CharSequence getContent() {
+        StringBuffer code = new StringBuffer();
+        code.append("package test;\n");
+        code.append("public @interface SourceClassAnnotation {\n");
+        code.append("  Class<?> value();\n");
+        code.append("}\n");
+        return code;
+      }
+    });
+    sourceOracle.addOrReplace(new MockJavaResource("test.Tag") {
+      @Override
+      protected CharSequence getContent() {
+        StringBuffer code = new StringBuffer();
+        code.append("package test;\n");
+        code.append("@Tag\n");
+        code.append("public @interface Tag {\n");
+        code.append("}\n");
+        return code;
+      }
+    });
+    sourceOracle.addOrReplace(new MockJavaResource("test.WithTag") {
+      @Override
+      protected CharSequence getContent() {
+        StringBuffer code = new StringBuffer();
+        code.append("package test;\n");
+        code.append("@Tag\n");
+        code.append("public class WithTag {\n");
+        code.append("  @Tag String field;\n");
+        code.append("  @Tag WithTag(){}\n");
+ code.append(" @Tag void method(@Tag String p) {...@tag String local;}\n");
+        code.append("}\n");
+        return code;
+      }
+    });
+    sourceOracle.addOrReplace(new MockJavaResource("test.WithBinary") {
+      @Override
+      protected CharSequence getContent() {
+        StringBuffer code = new StringBuffer();
+        code.append("package test;\n");
+        code.append("import " + BinaryAnnotation.class.getCanonicalName()
+            + ";\n");
+        code.append("import " + ClassAnnotation.class.getCanonicalName()
+            + ";\n");
+        code.append("import " + OtherAnnotation.class.getCanonicalName()
+            + ";\n");
+        code.append("public class WithBinary {\n");
+ code.append(" public static final String EXPR = \"Expression\";\n");
+        code.append("  @ClassAnnotation("
+            + JAnnotationTest.class.getCanonicalName() + ".class)\n");
+        code.append("  void useBinaryClassReference() {}\n");
+        code.append("  @BinaryAnnotation\n");
+        code.append("  void useDefaults() {}\n");
+        code.append("  @SourceClassAnnotation("
+            + JAnnotationTest.class.getCanonicalName() + ".class)\n");
+        code.append("  void useSourceClassAnnotation() {}\n");
+ code.append(" @BinaryAnnotation(c=Tag.class, i=42, s=\"foo\", o= @OtherAnnotation(\"Hello \" + EXPR))\n");
+        code.append("  void useValues() {}\n");
+        code.append("}\n");
+        return code;
+      }
+    });
+  }
+
+  public void testAllElementTypes() throws UnableToCompleteException {
+    JProgram program = compileSnippet("void", "new test.WithTag();");
+
+    // ANNOTATION_TYPE
+    JDeclaredType tag = findType(program, "test.Tag");
+    assertEquals(1, tag.getAnnotations().size());
+
+    // TYPE
+    JDeclaredType withTag = findType(program, "test.WithTag");
+    assertEquals(1, withTag.getAnnotations().size());
+
+    // CONSTRUCTOR
+    JMethod constructor = findMethod(withTag, "WithTag");
+    assertEquals(1, constructor.getAnnotations().size());
+
+    // METHOD
+    JMethod method = findMethod(withTag, "method");
+    assertEquals(1, method.getAnnotations().size());
+
+    // FIELD
+    JField field = findField(withTag, "field");
+    assertEquals(1, field.getAnnotations().size());
+
+    // LOCAL_VARIABLE
+    JLocal local = findLocal(method, "local");
+    assertEquals(1, local.getAnnotations().size());
+
+    // PARAMETER
+    JParameter param = method.getParams().get(0);
+    assertEquals(1, param.getAnnotations().size());
+
+    // There are no representations for PACKAGE in our AST
+  }
+
+  public void testAnnotationBinaryOnlyClassLiterals()
+      throws UnableToCompleteException {
+    JProgram program = compileSnippet("void", "new test.WithBinary();");
+
+    JDeclaredType t = findType(program, "test.WithBinary");
+    JMethod m = findMethod(t, "useBinaryClassReference");
+    JAnnotation a = JAnnotation.findAnnotation(m,
+        ClassAnnotation.class.getName());
+    assertNotNull(a);
+
+    ClassAnnotation instance = JAnnotation.createAnnotation(
+        ClassAnnotation.class, a);
+
+    assertSame(JAnnotationTest.class, instance.value());
+  }
+
+  public void testAnnotationProxyCustomValues()
+      throws UnableToCompleteException {
+    JProgram program = compileSnippet("void", "new test.WithBinary();");
+
+    JDeclaredType t = findType(program, "test.WithBinary");
+    JMethod useValues = findMethod(t, "useValues");
+    JAnnotation a = JAnnotation.findAnnotation(useValues,
+        BinaryAnnotation.class.getName());
+    assertNotNull(a);
+    BinaryAnnotation instance = JAnnotation.createAnnotation(
+        BinaryAnnotation.class, a);
+    assertNotNull(instance);
+
+    // A source-only annotation, unavailable to the JVM
+    try {
+      instance.c();
+    } catch (SourceOnlyClassException e) {
+      // Expected
+ assertEquals(findType(program, "test.Tag"), e.getLiteral().getRefType());
+    }
+    assertEquals(42, instance.i());
+    assertEquals("foo", instance.s());
+    assertEquals(1, instance.o().length);
+    assertEquals("Hello Expression", instance.o()[0].value());
+  }
+
+  public void testAnnotationProxyDefaultValues()
+      throws UnableToCompleteException {
+    JProgram program = compileSnippet("void", "new test.WithBinary();");
+
+    JDeclaredType t = findType(program, "test.WithBinary");
+    JMethod useDefaults = findMethod(t, "useDefaults");
+    JAnnotation a = JAnnotation.findAnnotation(useDefaults,
+        BinaryAnnotation.class.getName());
+    assertNotNull(a);
+    assertTrue(a.getType() instanceof JExternalType);
+
+    BinaryAnnotation instance = JAnnotation.createAnnotation(
+        BinaryAnnotation.class, a);
+    assertNotNull(instance);
+
+    // Test Object methods
+    assertEquals(instance.hashCode(), instance.hashCode());
+    assertNotNull(instance.toString());
+
+    // Test default-valued
+    assertSame(Object.class, instance.c());
+    assertEquals(2, instance.i());
+    assertEquals(new int[] {1}, instance.iOne());
+    assertEquals(new int[] {1, 2}, instance.iTwo());
+    assertEquals(new int[] {}, instance.iZero());
+    assertNotNull(instance.o());
+    assertEquals(2, instance.o().length);
+    assertEquals("Hello 1", instance.o()[0].value());
+    assertEquals("Hello 2", instance.o()[1].value());
+    assertEquals("Hello String", instance.s());
+  }
+
+  public void testSourceAnnotationWithBinaryClass()
+      throws UnableToCompleteException {
+    JProgram program = compileSnippet("void", "new test.WithBinary();");
+
+    JDeclaredType t = findType(program, "test.WithBinary");
+    JMethod m = findMethod(t, "useSourceClassAnnotation");
+ JAnnotation a = JAnnotation.findAnnotation(m, "test.SourceClassAnnotation");
+    assertNotNull(a);
+
+    Property p = a.getProperty("value");
+    JClassLiteral literal = (JClassLiteral) p.getSingleValue();
+    JExternalType externalType = (JExternalType) literal.getRefType();
+    assertEquals(JAnnotationTest.class.getName(), externalType.getName());
+  }
+}
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JClassLiteral.java Fri Jun 5 05:17:27 2009 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JClassLiteral.java Tue Feb 9 15:22:29 2010
@@ -26,7 +26,7 @@
  * ClassLiteralHolder. That field contains the class object allocation
  * initializer.
  */
-public class JClassLiteral extends JLiteral {
+public class JClassLiteral extends JLiteral implements JAnnotationArgument {
   /**
* Create an expression that will evaluate, at run time, to the class literal.
    * Cannot be called after optimizations begin.
@@ -116,7 +116,7 @@
JClassLiteral componentLiteral = program.getLiteralClass(arrayType.getElementType());
       call.addArg(componentLiteral);
     } else {
- assert (type instanceof JInterfaceType || type instanceof JPrimitiveType); + assert (type instanceof JExternalType || type instanceof JInterfaceType || type instanceof JPrimitiveType);
     }
assert call.getArgs().size() == method.getParams().size() : "Argument / param mismatch "
         + call.toString() + " versus " + method.toString();
@@ -162,6 +162,10 @@
     refType = type;
     this.field = field;
   }
+
+  public JNode annotationNode() {
+    return this;
+  }

   /**
    * Returns the field holding my allocated object.
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JClassType.java Mon Apr 20 15:21:46 2009 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JClassType.java Tue Feb 9 15:22:29 2010
@@ -58,6 +58,7 @@

   public void traverse(JVisitor visitor, Context ctx) {
     if (visitor.visit(this, ctx)) {
+      annotations = visitor.acceptImmutable(annotations);
       fields = visitor.acceptWithInsertRemoveImmutable(fields);
       methods = visitor.acceptWithInsertRemoveImmutable(methods);
     }
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java Wed Oct 28 09:10:53 2009 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java Tue Feb 9 15:22:29 2010
@@ -28,7 +28,12 @@
 /**
  * Base class for any reference type.
  */
-public abstract class JDeclaredType extends JReferenceType {
+public abstract class JDeclaredType extends JReferenceType implements
+    HasAnnotations {
+  /**
+   * Annotations applied to the type. Special serialization treatment.
+   */
+  protected transient List<JAnnotation> annotations = Lists.create();

   /**
    * The other nodes that this node should implicitly rescue. Special
@@ -65,6 +70,10 @@
   public JDeclaredType(SourceInfo info, String name) {
     super(info, name);
   }
+
+  public void addAnnotation(JAnnotation annotation) {
+    annotations = Lists.add(annotations, annotation);
+  }

   public void addArtificialRescue(JNode node) {
     artificialRescues = Lists.add(artificialRescues, node);
@@ -127,6 +136,14 @@
     }
     return true;
   }
+
+  public JAnnotation findAnnotation(String className) {
+    return JAnnotation.findAnnotation(this, className);
+  }
+
+  public List<JAnnotation> getAnnotations() {
+    return Lists.normalizeUnmodifiable(annotations);
+  }

   public List<JNode> getArtificialRescues() {
     return artificialRescues;
@@ -245,6 +262,7 @@
     fields = (List<JField>) stream.readObject();
     methods = (List<JMethod>) stream.readObject();
     artificialRescues = (List<JNode>) stream.readObject();
+    annotations = (List<JAnnotation>) stream.readObject();
   }

 /**
@@ -280,6 +298,7 @@
     stream.writeObject(fields);
     stream.writeObject(methods);
     stream.writeObject(artificialRescues);
+    stream.writeObject(annotations);
   }

   /**
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JField.java Tue Nov 10 12:46:07 2009 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JField.java Tue Feb 9 15:22:29 2010
@@ -21,7 +21,6 @@
  * Java field definition.
  */
public class JField extends JVariable implements CanBeStatic, HasEnclosingType {
-
   /**
    * Determines whether the variable is final, volatile, or neither.
    */
@@ -46,8 +45,8 @@
   private final boolean isStatic;
   private boolean isVolatile;

-  JField(SourceInfo info, String name, JDeclaredType enclosingType,
-      JType type, boolean isStatic, Disposition disposition) {
+ JField(SourceInfo info, String name, JDeclaredType enclosingType, JType type,
+      boolean isStatic, Disposition disposition) {
     super(info, name, type, disposition.isFinal());
     this.enclosingType = enclosingType;
     this.isStatic = isStatic;
@@ -101,6 +100,7 @@

   public void traverse(JVisitor visitor, Context ctx) {
     if (visitor.visit(this, ctx)) {
+      annotations = visitor.acceptImmutable(annotations);
       // Do not visit declStmt, it gets visited within its own code block.
     }
     visitor.endVisit(this, ctx);
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JInterfaceType.java Mon Apr 20 15:21:46 2009 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JInterfaceType.java Tue Feb 9 15:22:29 2010
@@ -41,6 +41,7 @@

   public void traverse(JVisitor visitor, Context ctx) {
     if (visitor.visit(this, ctx)) {
+      annotations = visitor.acceptImmutable(annotations);
       fields = visitor.acceptWithInsertRemoveImmutable(fields);
       methods = visitor.acceptWithInsertRemoveImmutable(methods);
     }
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JLocal.java Mon Oct 26 14:02:26 2009 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JLocal.java Tue Feb 9 15:22:29 2010
@@ -40,6 +40,7 @@

   public void traverse(JVisitor visitor, Context ctx) {
     if (visitor.visit(this, ctx)) {
+      annotations = visitor.acceptImmutable(annotations);
       // Do not visit declStmt, it gets visited within its own code block.
     }
     visitor.endVisit(this, ctx);
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java Mon Jan 11 09:04:38 2010 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java Tue Feb 9 15:22:29 2010
@@ -30,8 +30,9 @@
 /**
  * A Java method implementation.
  */
-public final class JMethod extends JNode implements HasEnclosingType, HasName,
-    HasType, CanBeAbstract, CanBeSetFinal, CanBeNative, CanBeStatic {
+public final class JMethod extends JNode implements HasAnnotations,
+    HasEnclosingType, HasName, HasType, CanBeAbstract, CanBeSetFinal,
+    CanBeNative, CanBeStatic {

   private static final String TRACE_METHOD_WILDCARD = "*";

@@ -47,6 +48,7 @@
    */
   private transient JAbstractMethodBody body = null;

+  private List<JAnnotation> annotations = Lists.create();
   private final JDeclaredType enclosingType;
   private final boolean isAbstract;
   private boolean isFinal;
@@ -84,6 +86,10 @@
     this.isFinal = isFinal;
     this.isPrivate = isPrivate;
   }
+
+  public void addAnnotation(JAnnotation annotation) {
+    annotations = Lists.add(annotations, annotation);
+  }

   /**
    * Add a method that this method overrides.
@@ -105,6 +111,10 @@
   public void addParam(JParameter x) {
     params = Lists.add(params, x);
   }
+
+  public JAnnotation findAnnotation(String className) {
+    return JAnnotation.findAnnotation(this, className);
+  }

   public void freezeParamTypes() {
     List<JType> paramTypes = new ArrayList<JType>();
@@ -113,6 +123,10 @@
     }
     setOriginalTypes(returnType, paramTypes);
   }
+
+  public List<JAnnotation> getAnnotations() {
+    return Lists.normalizeUnmodifiable(annotations);
+  }

   public JAbstractMethodBody getBody() {
     return body;
@@ -253,6 +267,7 @@
       }
     }
     if (visitor.visit(this, ctx)) {
+      annotations = visitor.acceptImmutable(annotations);
       params = visitor.acceptImmutable(params);
       if (body != null) {
         body = (JAbstractMethodBody) visitor.accept(body);
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JParameter.java Tue Jan 26 09:41:35 2010 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JParameter.java Tue Feb 9 15:22:29 2010
@@ -64,6 +64,7 @@

   public void traverse(JVisitor visitor, Context ctx) {
     if (visitor.visit(this, ctx)) {
+      annotations = visitor.acceptImmutable(annotations);
     }
     visitor.endVisit(this, ctx);
   }
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java Mon Jan 11 09:04:38 2010 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java Tue Feb 9 15:22:29 2010
@@ -83,6 +83,12 @@
           "com.google.gwt.lang.EntryMethodHolder",
           "com.google.gwt.core.client.prefetch.RunAsyncCode",}));

+  /**
+ * Only annotations defined in the following packages or sub-packages thereof
+   * will be recorded in the Java AST.
+   */
+ public static final Set<String> RECORDED_ANNOTATION_PACKAGES = new LinkedHashSet<String>();
+
static final Map<String, Set<String>> traceMethods = new HashMap<String, Set<String>>();

private static final Comparator<JArrayType> ARRAYTYPE_COMPARATOR = new ArrayTypeComparator();
@@ -274,6 +280,8 @@
    */
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>();
@@ -448,6 +456,21 @@
     enclosingType.addField(x);
     return x;
   }
+
+  public JExternalType createExternalType(SourceInfo info, char[][] name) {
+    JExternalType x;
+    String sname = dotify(name);
+    x = externalTypes.get(sname);
+    if (x != null) {
+      return x;
+    }
+
+    x = new JExternalType(info, sname);
+    if (INDEX_TYPES_SET.contains(sname)) {
+      indexedTypes.put(x.getShortName(), x);
+    }
+    return x;
+  }

   public JField createField(SourceInfo info, char[] name,
       JDeclaredType enclosingType, JType type, boolean isStatic,
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JValueLiteral.java Thu Apr 9 08:41:34 2009 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JValueLiteral.java Tue Feb 9 15:22:29 2010
@@ -20,11 +20,16 @@
 /**
  * Base class for any Java literal expression.
  */
-public abstract class JValueLiteral extends JLiteral {
+public abstract class JValueLiteral extends JLiteral implements
+    JAnnotationArgument {

   public JValueLiteral(SourceInfo sourceInfo) {
     super(sourceInfo);
   }
+
+  public JNode annotationNode() {
+    return this;
+  }

   public abstract Object getValueObj();

=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JVariable.java Wed Dec 9 09:10:40 2009 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JVariable.java Tue Feb 9 15:22:29 2010
@@ -16,13 +16,17 @@
 package com.google.gwt.dev.jjs.ast;

 import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.util.collect.Lists;
+
+import java.util.List;

 /**
  * Base class for any storage location.
  */
 public abstract class JVariable extends JNode implements CanBeSetFinal,
-    CanHaveInitializer, HasName, HasType {
-
+    CanHaveInitializer, HasAnnotations, HasName, HasType {
+
+  protected List<JAnnotation> annotations = Lists.create();
   protected JDeclarationStatement declStmt = null;
   private boolean isFinal;
   private final String name;
@@ -35,6 +39,18 @@
     this.type = type;
     this.isFinal = isFinal;
   }
+
+  public void addAnnotation(JAnnotation annotation) {
+    annotations = Lists.add(annotations, annotation);
+  }
+
+  public JAnnotation findAnnotation(String className) {
+    return JAnnotation.findAnnotation(this, className);
+  }
+
+  public List<JAnnotation> getAnnotations() {
+    return Lists.normalizeUnmodifiable(annotations);
+  }

   public JLiteral getConstInitializer() {
     JExpression initializer = getInitializer();
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java Tue Jan 26 11:40:29 2010 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java Tue Feb 9 15:22:29 2010
@@ -130,6 +130,14 @@
   public void endVisit(JAbstractMethodBody x, Context ctx) {
     endVisit((JNode) x, ctx);
   }
+
+  public void endVisit(JAnnotation x, Context ctx) {
+    endVisit((JNode) x, ctx);
+  }
+
+  public void endVisit(JAnnotation.Property x, Context ctx) {
+    endVisit((JNode) x, ctx);
+  }

   public void endVisit(JArrayRef x, Context ctx) {
     endVisit((JExpression) x, ctx);
@@ -438,6 +446,14 @@
   public boolean visit(JAbstractMethodBody x, Context ctx) {
     return visit((JNode) x, ctx);
   }
+
+  public boolean visit(JAnnotation x, Context ctx) {
+    return visit((JNode) x, ctx);
+  }
+
+  public boolean visit(JAnnotation.Property x, Context ctx) {
+    return visit((JNode) x, ctx);
+  }

   public boolean visit(JArrayRef x, Context ctx) {
     return visit((JExpression) x, ctx);
@@ -669,11 +685,11 @@
     /* NOTE: Skip JFieldRef */
     return visit((JVariableRef) x, ctx);
   }
-
+
   public boolean visit(JsniMethodBody x, Context ctx) {
     return visit((JAbstractMethodBody) x, ctx);
   }
-
+
   public boolean visit(JsniMethodRef x, Context ctx) {
     /* NOTE: Skip JMethodRef */
     return visit((JExpression) x, ctx);
@@ -690,7 +706,7 @@
   public boolean visit(JsonPropInit x, Context ctx) {
     return visit((JNode) x, ctx);
   }
-
+
   public boolean visit(JStatement x, Context ctx) {
     return visit((JNode) x, ctx);
   }
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java Tue Jan 26 11:40:29 2010 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java Tue Feb 9 15:22:29 2010
@@ -22,7 +22,10 @@
 import com.google.gwt.dev.jjs.JJSOptions;
 import com.google.gwt.dev.jjs.SourceInfo;
 import com.google.gwt.dev.jjs.ast.Context;
+import com.google.gwt.dev.jjs.ast.HasAnnotations;
 import com.google.gwt.dev.jjs.ast.HasEnclosingType;
+import com.google.gwt.dev.jjs.ast.JAnnotation;
+import com.google.gwt.dev.jjs.ast.JAnnotationArgument;
 import com.google.gwt.dev.jjs.ast.JArrayRef;
 import com.google.gwt.dev.jjs.ast.JArrayType;
 import com.google.gwt.dev.jjs.ast.JAssertStatement;
@@ -45,6 +48,7 @@
 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;
@@ -86,6 +90,7 @@
 import com.google.gwt.dev.jjs.ast.JVariable;
 import com.google.gwt.dev.jjs.ast.JVariableRef;
 import com.google.gwt.dev.jjs.ast.JWhileStatement;
+import com.google.gwt.dev.jjs.ast.JAnnotation.Property;
 import com.google.gwt.dev.jjs.ast.JField.Disposition;
 import com.google.gwt.dev.jjs.ast.js.JsniFieldRef;
 import com.google.gwt.dev.jjs.ast.js.JsniMethodBody;
@@ -98,6 +103,7 @@
 import com.google.gwt.dev.js.ast.JsProgram;
 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.ast.AND_AND_Expression;
@@ -169,8 +175,10 @@
 import org.eclipse.jdt.internal.compiler.impl.LongConstant;
 import org.eclipse.jdt.internal.compiler.impl.ShortConstant;
 import org.eclipse.jdt.internal.compiler.impl.StringConstant;
+import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
 import org.eclipse.jdt.internal.compiler.lookup.Binding;
 import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
+import org.eclipse.jdt.internal.compiler.lookup.ElementValuePair;
 import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
 import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
 import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
@@ -372,10 +380,15 @@
      */
     public void processType(TypeDeclaration x) {
       if (x.binding.isAnnotationType()) {
-        // Do not process.
+ // Do not process completely. Use tryGet for binary-only annotations
+        currentClass = (JDeclaredType) typeMap.tryGet(x.binding);
+        if (currentClass != null) {
+          processHasAnnotations(currentClass, x.annotations);
+        }
         return;
       }
       currentClass = (JDeclaredType) typeMap.get(x.binding);
+      processHasAnnotations(currentClass, x.annotations);
       try {
         currentClassScope = x.scope;
currentSeparatorPositions = x.compilationResult.lineSeparatorPositions;
@@ -605,7 +618,10 @@
     }

     JStringLiteral processConstant(StringConstant x) {
- return program.getLiteralString(currentMethod.getSourceInfo().makeChild(
+      // May be processing an annotation
+ SourceInfo info = currentMethod == null ? currentClass.getSourceInfo()
+          : currentMethod.getSourceInfo();
+      return program.getLiteralString(info.makeChild(
           JavaASTGenerationVisitor.class, "String literal"),
           x.stringValue().toCharArray());
     }
@@ -639,6 +655,7 @@
     void processConstructor(ConstructorDeclaration x) {
       JMethod ctor = (JMethod) typeMap.get(x.binding);
       try {
+        processHasAnnotations(ctor, x.annotations);
         SourceInfo info = ctor.getSourceInfo();

         currentMethod = ctor;
@@ -1423,6 +1440,7 @@

     void processField(FieldDeclaration declaration) {
       JField field = (JField) typeMap.tryGet(declaration.binding);
+      processHasAnnotations(field, declaration.annotations);
       if (field == null) {
         /*
* When anonymous classes declare constant fields, the field declaration
@@ -1469,6 +1487,14 @@
       MethodBinding b = x.binding;
       JMethod method = (JMethod) typeMap.get(b);
       try {
+        processHasAnnotations(method, x.annotations);
+        if (x.arguments != null) {
+          for (int i = 0, j = x.arguments.length; i < j; i++) {
+ JParameter p = (JParameter) typeMap.get(x.arguments[i].binding);
+            processHasAnnotations(p, x.arguments[i].annotations);
+          }
+        }
+
         if (!b.isStatic() && (b.isImplementing() || b.isOverriding())) {
           tryFindUpRefs(method, b);
         }
@@ -1732,6 +1758,7 @@
     JStatement processStatement(LocalDeclaration x) {
       SourceInfo info = makeSourceInfo(x);
       JLocal local = (JLocal) typeMap.get(x.binding);
+      processHasAnnotations(local, x.annotations);
       JLocalRef localRef = new JLocalRef(info, local);
       JExpression initializer = dispProcessExpression(x.initialization);
       return new JDeclarationStatement(info, localRef, initializer);
@@ -2447,6 +2474,73 @@
       }
       return variable;
     }
+
+    @SuppressWarnings("unchecked")
+    private void processAnnotationProperties(SourceInfo sourceInfo,
+        JAnnotation annotation, ElementValuePair[] elementValuePairs) {
+      if (elementValuePairs == null) {
+        return;
+      }
+
+      for (ElementValuePair pair : elementValuePairs) {
+        String name = CharOperation.charToString(pair.getName());
+ List<JAnnotationArgument> values = processAnnotationPropertyValue(sourceInfo,
+            pair.getValue());
+        annotation.addValue(new Property(sourceInfo, name, values));
+      }
+    }
+
+ 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,
+              ((ReferenceBinding) value).compoundName);
+        }
+ return Lists.<JAnnotationArgument> create(program.getLiteralClass(type));
+
+      } else if (value instanceof Constant) {
+ return Lists.create((JAnnotationArgument) dispatch("processConstant", value));
+
+      } else if (value instanceof Object[]) {
+        Object[] array = (Object[]) value;
+        List<JAnnotationArgument> toReturn = Lists.create();
+        for (int i = 0, j = array.length; i < j; i++) {
+ toReturn = Lists.add(toReturn, processAnnotationPropertyValue(info,
+              array[i]).get(0));
+        }
+        return toReturn;
+
+      } else if (value instanceof AnnotationBinding) {
+        AnnotationBinding annotationBinding = (AnnotationBinding) value;
+ ReferenceBinding annotationType = annotationBinding.getAnnotationType(); + JInterfaceType type = (JInterfaceType) typeMap.tryGet(annotationType);
+        JAnnotation toReturn;
+        if (type != null) {
+          toReturn = new JAnnotation(info, type);
+        } else {
+          JExternalType external = program.createExternalType(info,
+              annotationType.compoundName);
+          toReturn = new JAnnotation(info, external);
+        }
+
+        // Load the properties for the annotation value
+        processAnnotationProperties(info, toReturn,
+            annotationBinding.getElementValuePairs());
+
+        return Lists.<JAnnotationArgument> create(toReturn);
+      } else if (value instanceof FieldBinding) {
+        FieldBinding fieldBinding = (FieldBinding) value;
+ assert fieldBinding.constant() != null : "Expecting constant-valued field"; + return Lists.create((JAnnotationArgument) dispatch("processConstant",
+            fieldBinding.constant()));
+      }
+
+      throw new InternalCompilerException("Unable to process value "
+          + value.getClass().getName());
+    }

     private void processArtificialRescue(Annotation rescue) {
       assert rescue != null;
@@ -2587,6 +2681,44 @@
           exprArg1, exprArg2);
       return binaryOperation;
     }
+
+    /**
+     * It is safe to pass a null array.
+     */
+ private <T extends HasAnnotations & HasSourceInfo> void processHasAnnotations(
+        T x, Annotation[] annotations) {
+      if (annotations == null) {
+        return;
+      }
+
+      for (Annotation a : annotations) {
+        JAnnotation annotation;
+        ReferenceBinding binding = (ReferenceBinding) a.resolvedType;
+        String name = CharOperation.toString(binding.compoundName);
+        boolean record = false;
+        for (String prefix : JProgram.RECORDED_ANNOTATION_PACKAGES) {
+          if (name.startsWith(prefix + ".")) {
+            record = true;
+            break;
+          }
+        }
+        if (!record) {
+          continue;
+        }
+ JInterfaceType annotationType = (JInterfaceType) typeMap.tryGet(binding);
+        if (annotationType != null) {
+          annotation = new JAnnotation(x.getSourceInfo(), annotationType);
+        } else {
+          // Indicates a binary-only annotation type
+          JExternalType externalType = program.createExternalType(
+              x.getSourceInfo(), binding.compoundName);
+          annotation = new JAnnotation(x.getSourceInfo(), externalType);
+        }
+        processAnnotationProperties(x.getSourceInfo(), annotation,
+            a.computeElementValuePairs());
+        x.addAnnotation(annotation);
+      }
+    }

     private JExpression processQualifiedThisOrSuperRef(
         QualifiedThisReference x, JClassType qualType) {
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java Fri May 29 15:31:48 2009 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java Tue Feb 9 15:22:29 2010
@@ -23,6 +23,7 @@
 import com.google.gwt.dev.jjs.ast.HasName;
 import com.google.gwt.dev.jjs.ast.HasType;
 import com.google.gwt.dev.jjs.ast.JAbsentArrayDimension;
+import com.google.gwt.dev.jjs.ast.JAnnotation;
 import com.google.gwt.dev.jjs.ast.JArrayRef;
 import com.google.gwt.dev.jjs.ast.JArrayType;
 import com.google.gwt.dev.jjs.ast.JAssertStatement;
@@ -157,6 +158,34 @@
     // nothing to print, parent prints []
     return false;
   }
+
+  @Override
+  public boolean visit(JAnnotation x, Context ctx) {
+    print('@');
+    print(x.getType().getName());
+    lparen();
+    visitCollectionWithCommas(x.getProperties().iterator());
+    rparen();
+    return false;
+  }
+
+  @Override
+  public boolean visit(JAnnotation.Property x, Context ctx) {
+    print(x.getName());
+    print('=');
+    switch (x.getValues().size()) {
+      case 1:
+        accept(x.getSingleValue().annotationNode());
+        break;
+      case 0:
+      default:
+        print('{');
+        visitCollectionWithCommas(x.getValuesAsNodes().iterator());
+        print('}');
+        break;
+    }
+    return false;
+  }

   @Override
   public boolean visit(JArrayRef x, Context ctx) {
=======================================
--- /trunk/dev/core/test/com/google/gwt/dev/jjs/impl/OptimizerTestBase.java Tue Jan 19 12:32:37 2010 +++ /trunk/dev/core/test/com/google/gwt/dev/jjs/impl/OptimizerTestBase.java Tue Feb 9 15:22:29 2010
@@ -22,10 +22,13 @@
 import com.google.gwt.dev.javac.impl.MockJavaResource;
 import com.google.gwt.dev.javac.impl.MockResourceOracle;
 import com.google.gwt.dev.jjs.JavaAstConstructor;
+import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JDeclaredType;
 import com.google.gwt.dev.jjs.ast.JField;
+import com.google.gwt.dev.jjs.ast.JLocal;
 import com.google.gwt.dev.jjs.ast.JMethod;
 import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JVisitor;
 import com.google.gwt.dev.util.log.AbstractTreeLogger;
 import com.google.gwt.dev.util.log.PrintWriterTreeLogger;

@@ -63,6 +66,25 @@
     JField field = findField(type, fieldName);
     return field;
   }
+
+  /**
+   * Find a local variable declared within a JMethod.
+   */
+  public static JLocal findLocal(JMethod method, final String localName) {
+    class LocalVisitor extends JVisitor {
+      JLocal found;
+
+      @Override
+      public void endVisit(JLocal x, Context ctx) {
+        if (x.getName().equals(localName)) {
+          found = x;
+        }
+      }
+    }
+    LocalVisitor v = new LocalVisitor();
+    v.accept(method);
+    return v.found;
+  }

   public static JMethod findMainMethod(JProgram program) {
     return findMethod(program, "onModuleLoad");

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

Reply via email to