Add Impl.getNameOf() to expose JNameOf nodes to Java code.

http://gwt-code-reviews.appspot.com/46802/show

Patch by: bobv
Review by: scottb

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@5640 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
index f093396..c601e7a 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
@@ -77,6 +77,7 @@
           "com.google.gwt.lang.ClassLiteralHolder",
           "com.google.gwt.core.client.RunAsyncCallback",
           "com.google.gwt.core.client.impl.AsyncFragmentLoader",
+          "com.google.gwt.core.client.impl.Impl",
           "com.google.gwt.lang.EntryMethodHolder",}));
 
   static final Map<String, Set<String>> traceMethods = new HashMap<String, Set<String>>();
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRebinds.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRebinds.java
index eccd7e5..ef8eb03 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRebinds.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRebinds.java
@@ -20,15 +20,21 @@
 import com.google.gwt.dev.jdt.RebindPermutationOracle;
 import com.google.gwt.dev.jjs.InternalCompilerException;
 import com.google.gwt.dev.jjs.ast.Context;
+import com.google.gwt.dev.jjs.ast.HasEnclosingType;
+import com.google.gwt.dev.jjs.ast.HasName;
 import com.google.gwt.dev.jjs.ast.JClassLiteral;
 import com.google.gwt.dev.jjs.ast.JClassType;
+import com.google.gwt.dev.jjs.ast.JDeclaredType;
 import com.google.gwt.dev.jjs.ast.JExpression;
 import com.google.gwt.dev.jjs.ast.JGwtCreate;
 import com.google.gwt.dev.jjs.ast.JMethod;
 import com.google.gwt.dev.jjs.ast.JMethodCall;
 import com.google.gwt.dev.jjs.ast.JModVisitor;
+import com.google.gwt.dev.jjs.ast.JNameOf;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JReferenceType;
+import com.google.gwt.dev.jjs.ast.JStringLiteral;
+import com.google.gwt.dev.util.JsniRef;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -40,30 +46,85 @@
 
   private class RebindVisitor extends JModVisitor {
 
+    private final JMethod nameOfMethod;
     private final JMethod rebindCreateMethod;
 
-    public RebindVisitor(JMethod rebindCreateMethod) {
+    public RebindVisitor(JMethod nameOfMethod, JMethod rebindCreateMethod) {
+      this.nameOfMethod = nameOfMethod;
       this.rebindCreateMethod = rebindCreateMethod;
     }
 
     @Override
     public void endVisit(JMethodCall x, Context ctx) {
       JMethod method = x.getTarget();
-      if (method == rebindCreateMethod) {
-        assert (x.getArgs().size() == 1);
-        JExpression arg = x.getArgs().get(0);
-        assert (arg instanceof JClassLiteral);
-        JClassLiteral classLiteral = (JClassLiteral) arg;
-        JReferenceType sourceType = (JReferenceType) classLiteral.getRefType();
-        List<JClassType> allRebindResults = getAllPossibleRebindResults(sourceType);
-        JGwtCreate gwtCreate = new JGwtCreate(x.getSourceInfo(), sourceType,
-            allRebindResults, program.getTypeJavaLangObject());
-        if (allRebindResults.size() == 1) {
-          // Just replace with the instantiation expression.
-          ctx.replaceMe(gwtCreate.getInstantiationExpressions().get(0));
-        } else {
-          ctx.replaceMe(gwtCreate);
+      if (method == nameOfMethod) {
+        replaceImplNameOf(x, ctx);
+
+      } else if (method == rebindCreateMethod) {
+        replaceGwtCreate(x, ctx);
+      }
+    }
+
+    private void replaceGwtCreate(JMethodCall x, Context ctx) {
+      assert (x.getArgs().size() == 1);
+      JExpression arg = x.getArgs().get(0);
+      assert (arg instanceof JClassLiteral);
+      JClassLiteral classLiteral = (JClassLiteral) arg;
+      JReferenceType sourceType = (JReferenceType) classLiteral.getRefType();
+      List<JClassType> allRebindResults = getAllPossibleRebindResults(sourceType);
+      JGwtCreate gwtCreate = new JGwtCreate(x.getSourceInfo(), sourceType,
+          allRebindResults, program.getTypeJavaLangObject());
+      if (allRebindResults.size() == 1) {
+        // Just replace with the instantiation expression.
+        ctx.replaceMe(gwtCreate.getInstantiationExpressions().get(0));
+      } else {
+        ctx.replaceMe(gwtCreate);
+      }
+    }
+
+    private void replaceImplNameOf(JMethodCall x, Context ctx) {
+      JExpression arg0 = x.getArgs().get(0);
+      assert arg0 instanceof JStringLiteral;
+      String stringLiteral = ((JStringLiteral) arg0).getValue();
+
+      HasName named = null;
+
+      JDeclaredType refType;
+      JsniRef ref = JsniRef.parse(stringLiteral);
+
+      if (ref != null) {
+        final List<String> errors = new ArrayList<String>();
+        HasEnclosingType node = JsniRefLookup.findJsniRefTarget(ref, program,
+            new JsniRefLookup.ErrorReporter() {
+              public void reportError(String error) {
+                errors.add(error);
+              }
+            });
+
+        if (!errors.isEmpty()) {
+          for (String error : errors) {
+            logger.log(TreeLogger.ERROR, error);
+          }
         }
+
+        if (node instanceof HasName) {
+          named = (HasName) node;
+        }
+
+      } else {
+        // See if it's just @foo.Bar, which would result in the class seed
+        refType = program.getFromTypeMap(stringLiteral.charAt(0) == '@'
+            ? stringLiteral.substring(1) : stringLiteral);
+        if (refType != null) {
+          named = refType;
+        }
+      }
+
+      if (named == null) {
+        // Not found, must be null
+        ctx.replaceMe(program.getLiteralNull());
+      } else {
+        ctx.replaceMe(new JNameOf(x.getSourceInfo(), program, named));
       }
     }
   }
@@ -108,6 +169,7 @@
 
   private boolean execImpl() {
     RebindVisitor rebinder = new RebindVisitor(
+        program.getIndexedMethod("Impl.getNameOf"),
         program.getIndexedMethod("GWT.create"));
     rebinder.accept(program);
     return rebinder.didChange();
diff --git a/user/src/com/google/gwt/core/client/impl/Impl.java b/user/src/com/google/gwt/core/client/impl/Impl.java
index 2bbeb48..149dd40 100644
--- a/user/src/com/google/gwt/core/client/impl/Impl.java
+++ b/user/src/com/google/gwt/core/client/impl/Impl.java
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.core.client.impl;
 
+import com.google.gwt.core.client.GWT;
+
 /**
  * Private implementation class for GWT core. This API is should not be
  * considered public or stable.
@@ -66,6 +68,29 @@
     return $moduleName;
   }-*/;
 
+  /**
+   * Returns the obfuscated name of members in the compiled output. This is a
+   * thin wrapper around JNameOf AST nodes and is therefore meaningless to
+   * implement in hosted mode.
+   * 
+   * @param jsniIdent a string literal specifying a type, field, or method. Raw
+   *          type names may also be used to obtain the name of the type's seed
+   *          function.
+   * @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
+   */
+  public static String getNameOf(String jsniIdent) {
+    /*
+     * In web mode, the compiler directly replaces calls to this method with a
+     * string literal expression.
+     */
+    assert !GWT.isScript() : "ReplaceRebinds failed to replace this method";
+    throw new UnsupportedOperationException(
+        "Impl.getNameOf() is unimplemented in hosted mode");
+  }
+
   public static native String getPermutationStrongName() /*-{
     return $strongName;
   }-*/;