Correctly handle JSNI references to compile-time constants.

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@1513 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
index 36c45c8..70c616c 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -309,7 +309,7 @@
       //
 
       // Create the tree from JDT
-      GenerateJavaAST.exec(allTypeDeclarations, typeMap, jprogram);
+      GenerateJavaAST.exec(allTypeDeclarations, typeMap, jprogram, jsProgram);
 
       // GenerateJavaAST can uncover semantic JSNI errors; report & abort
       // 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
index e56caf1..fe37ed8 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
@@ -15,11 +15,10 @@
  */
 package com.google.gwt.dev.jjs.impl;
 
+import com.google.gwt.dev.jjs.HasSourceInfo;
 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.HasEnclosingType;
-import com.google.gwt.dev.jjs.ast.HasName;
 import com.google.gwt.dev.jjs.ast.JArrayRef;
 import com.google.gwt.dev.jjs.ast.JArrayType;
 import com.google.gwt.dev.jjs.ast.JAssertStatement;
@@ -87,9 +86,11 @@
 import com.google.gwt.dev.js.ast.JsContext;
 import com.google.gwt.dev.js.ast.JsExpression;
 import com.google.gwt.dev.js.ast.JsFunction;
+import com.google.gwt.dev.js.ast.JsInvocation;
+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.js.ast.JsSourceInfo;
-import com.google.gwt.dev.js.ast.JsVisitor;
 
 import org.eclipse.jdt.core.compiler.IProblem;
 import org.eclipse.jdt.internal.compiler.CompilationResult;
@@ -214,6 +215,158 @@
    */
   private static class JavaASTGenerationVisitor {
 
+    private class JsniRefResolver extends JsModVisitor {
+      private final AbstractMethodDeclaration methodDecl;
+      private final JsniMethodBody nativeMethodBody;
+
+      private JsniRefResolver(AbstractMethodDeclaration methodDecl,
+          JsniMethodBody nativeMethodBody) {
+        this.methodDecl = methodDecl;
+        this.nativeMethodBody = nativeMethodBody;
+      }
+
+      @Override
+      public void endVisit(JsNameRef x, JsContext<JsExpression> ctx) {
+        String ident = x.getIdent();
+        if (ident.charAt(0) == '@') {
+          processNameRef(x, ctx);
+        }
+      }
+
+      private HasEnclosingType parseJsniRef(SourceInfo info, String ident) {
+        String[] parts = ident.substring(1).split("::");
+        assert (parts.length == 2);
+        String className = parts[0];
+        JReferenceType type = program.getFromTypeMap(className);
+        if (type == null) {
+          reportJsniError(info, methodDecl,
+              "Unresolvable native reference to type '" + className + "'");
+          return null;
+        }
+        String rhs = parts[1];
+        int parenPos = rhs.indexOf('(');
+        if (parenPos < 0) {
+          // look for a field
+          for (int i = 0; i < type.fields.size(); ++i) {
+            JField field = type.fields.get(i);
+            if (field.getName().equals(rhs)) {
+              return field;
+            }
+          }
+
+          reportJsniError(info, methodDecl,
+              "Unresolvable native reference to field '" + rhs + "' in type '"
+                  + className + "'");
+        } else {
+          // look for a method
+          String methodName = rhs.substring(0, parenPos);
+          String almostMatches = null;
+          for (int i = 0; i < type.methods.size(); ++i) {
+            JMethod method = type.methods.get(i);
+            if (method.getName().equals(methodName)) {
+              String jsniSig = getJsniSig(method);
+              if (jsniSig.equals(rhs)) {
+                return method;
+              } else if (almostMatches == null) {
+                almostMatches = "'" + jsniSig + "'";
+              } else {
+                almostMatches += ", '" + jsniSig + "'";
+              }
+            }
+          }
+
+          if (almostMatches == null) {
+            reportJsniError(info, methodDecl,
+                "Unresolvable native reference to method '" + methodName
+                    + "' in type '" + className + "'");
+          } else {
+            reportJsniError(info, methodDecl,
+                "Unresolvable native reference to method '" + methodName
+                    + "' in type '" + className + "' (did you mean "
+                    + almostMatches + "?)");
+          }
+        }
+        return null;
+      }
+
+      private void processField(JsNameRef nameRef, SourceInfo info,
+          JField field, JsContext<JsExpression> ctx) {
+        if (field.isStatic() && nameRef.getQualifier() != null) {
+          reportJsniError(info, methodDecl,
+              "Cannot make a qualified reference to the static field "
+                  + field.getName());
+        } else if (!field.isStatic() && nameRef.getQualifier() == null) {
+          reportJsniError(info, methodDecl,
+              "Cannot make an unqualified reference to the instance field "
+                  + field.getName());
+        }
+
+        /*
+         * We must replace any compile-time constants with the constant value of
+         * the field.
+         */
+        JLiteral initializer = field.constInitializer;
+        if (field.isStatic() && field.isFinal() && initializer != null) {
+          JType type = initializer.getType();
+          if (type instanceof JPrimitiveType
+              || type == program.getTypeJavaLangString()) {
+            GenerateJavaScriptLiterals generator = new GenerateJavaScriptLiterals(
+                jsProgram);
+            generator.accept(initializer);
+            JsExpression result = generator.peek();
+            assert (result != null);
+            ctx.replaceMe(result);
+            return;
+          }
+        }
+
+        // Normal: create a jsniRef.
+        JsniFieldRef fieldRef = new JsniFieldRef(program, info, field,
+            currentClass);
+        nativeMethodBody.jsniFieldRefs.add(fieldRef);
+      }
+
+      private void processMethod(JsNameRef nameRef, SourceInfo info,
+          JMethod method) {
+        if (method.isStatic() && nameRef.getQualifier() != null) {
+          reportJsniError(info, methodDecl,
+              "Cannot make a qualified reference to the static method "
+                  + method.getName());
+        } else if (!method.isStatic() && nameRef.getQualifier() == null) {
+          reportJsniError(info, methodDecl,
+              "Cannot make an unqualified reference to the instance method "
+                  + method.getName());
+        }
+
+        JsniMethodRef methodRef = new JsniMethodRef(program, info, method);
+        nativeMethodBody.jsniMethodRefs.add(methodRef);
+      }
+
+      private void processNameRef(JsNameRef nameRef, JsContext<JsExpression> ctx) {
+        SourceInfo info = nativeMethodBody.getSourceInfo();
+        // TODO: make this tighter when we have real source info
+        // JSourceInfo info = translateInfo(nameRef.getInfo());
+        String ident = nameRef.getIdent();
+        HasEnclosingType node = program.jsniMap.get(ident);
+        if (node == null) {
+          node = parseJsniRef(info, ident);
+          if (node == null) {
+            return; // already reported error
+          }
+          program.jsniMap.put(ident, node);
+        }
+
+        if (node instanceof JField) {
+          processField(nameRef, info, (JField) node, ctx);
+        } else if (node instanceof JMethod) {
+          processMethod(nameRef, info, (JMethod) node);
+        } else {
+          throw new InternalCompilerException((HasSourceInfo) node,
+              "JSNI reference to something other than a field or method?", null);
+        }
+      }
+    }
+
     private static String getJsniSig(JMethod method) {
       StringBuffer sb = new StringBuffer();
       sb.append(method.getName());
@@ -253,15 +406,19 @@
 
     private int[] currentSeparatorPositions;
 
+    private final JsProgram jsProgram;
+
     private final Map<JMethod, Map<String, JLabel>> labelMap = new IdentityHashMap<JMethod, Map<String, JLabel>>();
 
     private final JProgram program;
 
     private final TypeMap typeMap;
 
-    public JavaASTGenerationVisitor(TypeMap typeMap) {
+    public JavaASTGenerationVisitor(TypeMap typeMap, JProgram program,
+        JsProgram jsProgram) {
       this.typeMap = typeMap;
-      program = this.typeMap.getProgram();
+      this.program = program;
+      this.jsProgram = jsProgram;
     }
 
     public void processEnumType(JEnumType type) {
@@ -1403,60 +1560,7 @@
       }
 
       // resolve jsni refs
-      final List<JsNameRef> nameRefs = new ArrayList<JsNameRef>();
-      new JsVisitor() {
-        // @Override
-        public void endVisit(JsNameRef x, JsContext<JsExpression> ctx) {
-          String ident = x.getIdent();
-          if (ident.charAt(0) == '@') {
-            nameRefs.add(x);
-          }
-        }
-      }.accept(func);
-
-      for (int i = 0; i < nameRefs.size(); ++i) {
-        JsNameRef nameRef = nameRefs.get(i);
-        SourceInfo info = nativeMethodBody.getSourceInfo();
-        // TODO: make this tighter when we have real source info
-        // JSourceInfo info = translateInfo(nameRef.getInfo());
-        String ident = nameRef.getIdent();
-        HasEnclosingType node = program.jsniMap.get(ident);
-        if (node == null) {
-          node = parseJsniRef(info, x, ident);
-          if (node == null) {
-            continue; // already reported error
-          }
-          program.jsniMap.put(ident, node);
-        }
-        CanBeStatic canBeStatic = (CanBeStatic) node;
-        HasName hasName = (HasName) node;
-        boolean isField = node instanceof JField;
-        assert (isField || node instanceof JMethod);
-        if (canBeStatic.isStatic() && nameRef.getQualifier() != null) {
-          reportJsniError(info, x,
-              "Cannot make a qualified reference to the static "
-                  + (isField ? "field " : "method ") + hasName.getName());
-        } else if (!canBeStatic.isStatic() && nameRef.getQualifier() == null) {
-          reportJsniError(info, x,
-              "Cannot make an unqualified reference to the instance "
-                  + (isField ? "field " : "method ") + hasName.getName());
-        }
-
-        if (isField) {
-          /*
-           * TODO FIXME HACK: We should be replacing compile-time constant refs
-           * from JSNI with the literal value of the field.
-           */
-          JField field = (JField) node;
-          JsniFieldRef fieldRef = new JsniFieldRef(program, info, field,
-              currentClass);
-          nativeMethodBody.jsniFieldRefs.add(fieldRef);
-        } else {
-          JMethod method = (JMethod) node;
-          JsniMethodRef methodRef = new JsniMethodRef(program, info, method);
-          nativeMethodBody.jsniMethodRefs.add(methodRef);
-        }
-      }
+      new JsniRefResolver(x, nativeMethodBody).accept(func);
     }
 
     JStatement processStatement(AssertStatement x) {
@@ -2213,60 +2317,6 @@
           currentFileName);
     }
 
-    private HasEnclosingType parseJsniRef(SourceInfo info,
-        AbstractMethodDeclaration x, String ident) {
-      String[] parts = ident.substring(1).split("::");
-      assert (parts.length == 2);
-      String className = parts[0];
-      JReferenceType type = program.getFromTypeMap(className);
-      if (type == null) {
-        reportJsniError(info, x, "Unresolvable native reference to type '"
-            + className + "'");
-        return null;
-      }
-      String rhs = parts[1];
-      int parenPos = rhs.indexOf('(');
-      if (parenPos < 0) {
-        // look for a field
-        for (int i = 0; i < type.fields.size(); ++i) {
-          JField field = type.fields.get(i);
-          if (field.getName().equals(rhs)) {
-            return field;
-          }
-        }
-
-        reportJsniError(info, x, "Unresolvable native reference to field '"
-            + rhs + "' in type '" + className + "'");
-      } else {
-        // look for a method
-        String methodName = rhs.substring(0, parenPos);
-        String almostMatches = null;
-        for (int i = 0; i < type.methods.size(); ++i) {
-          JMethod method = type.methods.get(i);
-          if (method.getName().equals(methodName)) {
-            String jsniSig = getJsniSig(method);
-            if (jsniSig.equals(rhs)) {
-              return method;
-            } else if (almostMatches == null) {
-              almostMatches = "'" + jsniSig + "'";
-            } else {
-              almostMatches += ", '" + jsniSig + "'";
-            }
-          }
-        }
-
-        if (almostMatches == null) {
-          reportJsniError(info, x, "Unresolvable native reference to method '"
-              + methodName + "' in type '" + className + "'");
-        } else {
-          reportJsniError(info, x, "Unresolvable native reference to method '"
-              + methodName + "' in type '" + className + "' (did you mean "
-              + almostMatches + "?)");
-        }
-      }
-      return null;
-    }
-
     /**
      * Sometimes a variable reference can be to a local or parameter in an an
      * enclosing method. This is a tricky situation to detect. There's no
@@ -2546,8 +2596,9 @@
    * a JProgram structure.
    */
   public static void exec(TypeDeclaration[] types, TypeMap typeMap,
-      JProgram jprogram) {
-    JavaASTGenerationVisitor v = new JavaASTGenerationVisitor(typeMap);
+      JProgram jprogram, JsProgram jsProgram) {
+    JavaASTGenerationVisitor v = new JavaASTGenerationVisitor(typeMap,
+        jprogram, jsProgram);
     for (int i = 0; i < types.length; ++i) {
       v.processType(types[i]);
     }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
index e45e74e..923d174 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
@@ -26,38 +26,31 @@
 import com.google.gwt.dev.jjs.ast.JBinaryOperation;
 import com.google.gwt.dev.jjs.ast.JBinaryOperator;
 import com.google.gwt.dev.jjs.ast.JBlock;
-import com.google.gwt.dev.jjs.ast.JBooleanLiteral;
 import com.google.gwt.dev.jjs.ast.JBreakStatement;
 import com.google.gwt.dev.jjs.ast.JCaseStatement;
 import com.google.gwt.dev.jjs.ast.JCastOperation;
-import com.google.gwt.dev.jjs.ast.JCharLiteral;
 import com.google.gwt.dev.jjs.ast.JClassLiteral;
 import com.google.gwt.dev.jjs.ast.JClassType;
 import com.google.gwt.dev.jjs.ast.JConditional;
 import com.google.gwt.dev.jjs.ast.JContinueStatement;
 import com.google.gwt.dev.jjs.ast.JDoStatement;
-import com.google.gwt.dev.jjs.ast.JDoubleLiteral;
 import com.google.gwt.dev.jjs.ast.JExpressionStatement;
 import com.google.gwt.dev.jjs.ast.JField;
 import com.google.gwt.dev.jjs.ast.JFieldRef;
-import com.google.gwt.dev.jjs.ast.JFloatLiteral;
 import com.google.gwt.dev.jjs.ast.JForStatement;
 import com.google.gwt.dev.jjs.ast.JIfStatement;
 import com.google.gwt.dev.jjs.ast.JInstanceOf;
-import com.google.gwt.dev.jjs.ast.JIntLiteral;
 import com.google.gwt.dev.jjs.ast.JInterfaceType;
 import com.google.gwt.dev.jjs.ast.JLabel;
 import com.google.gwt.dev.jjs.ast.JLabeledStatement;
 import com.google.gwt.dev.jjs.ast.JLocal;
 import com.google.gwt.dev.jjs.ast.JLocalDeclarationStatement;
 import com.google.gwt.dev.jjs.ast.JLocalRef;
-import com.google.gwt.dev.jjs.ast.JLongLiteral;
 import com.google.gwt.dev.jjs.ast.JMethod;
 import com.google.gwt.dev.jjs.ast.JMethodBody;
 import com.google.gwt.dev.jjs.ast.JMethodCall;
 import com.google.gwt.dev.jjs.ast.JNewArray;
 import com.google.gwt.dev.jjs.ast.JNewInstance;
-import com.google.gwt.dev.jjs.ast.JNullLiteral;
 import com.google.gwt.dev.jjs.ast.JParameter;
 import com.google.gwt.dev.jjs.ast.JParameterRef;
 import com.google.gwt.dev.jjs.ast.JPostfixOperation;
@@ -66,7 +59,6 @@
 import com.google.gwt.dev.jjs.ast.JReferenceType;
 import com.google.gwt.dev.jjs.ast.JReturnStatement;
 import com.google.gwt.dev.jjs.ast.JStatement;
-import com.google.gwt.dev.jjs.ast.JStringLiteral;
 import com.google.gwt.dev.jjs.ast.JSwitchStatement;
 import com.google.gwt.dev.jjs.ast.JThisRef;
 import com.google.gwt.dev.jjs.ast.JThrowStatement;
@@ -125,12 +117,10 @@
 import com.google.gwt.dev.js.ast.JsUnaryOperation;
 import com.google.gwt.dev.js.ast.JsUnaryOperator;
 import com.google.gwt.dev.js.ast.JsVars;
-import com.google.gwt.dev.js.ast.JsVisitable;
 import com.google.gwt.dev.js.ast.JsWhile;
 import com.google.gwt.dev.js.ast.JsVars.JsVar;
 
 import java.math.BigInteger;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
@@ -364,7 +354,7 @@
     }
   }
 
-  private class GenerateJavaScriptVisitor extends JVisitor {
+  private class GenerateJavaScriptVisitor extends GenerateJavaScriptLiterals {
 
     private final Set<JClassType> alreadyRan = new HashSet<JClassType>();
 
@@ -372,8 +362,6 @@
 
     private final JsName globalTemp = topScope.declareName("_");
 
-    private final Stack<JsVisitable> nodeStack = new Stack<JsVisitable>();
-
     private final JsName prototype = objectScope.declareName("prototype");
 
     {
@@ -381,6 +369,10 @@
       prototype.setObfuscatable(false);
     }
 
+    public GenerateJavaScriptVisitor() {
+      super(jsProgram);
+    }
+
     @Override
     public void endVisit(JAbsentArrayDimension x, Context ctx) {
       throw new InternalCompilerException("Should not get here.");
@@ -438,12 +430,6 @@
     }
 
     @Override
-    public void endVisit(JBooleanLiteral x, Context ctx) {
-      push(x.getValue() ? jsProgram.getTrueLiteral()
-          : jsProgram.getFalseLiteral());
-    }
-
-    @Override
     public void endVisit(JBreakStatement x, Context ctx) {
       JsNameRef labelRef = null;
       if (x.getLabel() != null) {
@@ -470,11 +456,6 @@
     }
 
     @Override
-    public void endVisit(JCharLiteral x, Context ctx) {
-      push(jsProgram.getIntegralLiteral(BigInteger.valueOf(x.getValue())));
-    }
-
-    @Override
     public void endVisit(JClassLiteral x, Context ctx) {
       JsExpression classObjectAllocation = pop(); // classObjectAllocation
 
@@ -570,11 +551,6 @@
     }
 
     @Override
-    public void endVisit(JDoubleLiteral x, Context ctx) {
-      push(jsProgram.getDecimalLiteral(String.valueOf(x.getValue())));
-    }
-
-    @Override
     public void endVisit(JExpressionStatement x, Context ctx) {
       JsExpression expr = (JsExpression) pop(); // expr
       push(expr.makeStmt());
@@ -651,11 +627,6 @@
     }
 
     @Override
-    public void endVisit(JFloatLiteral x, Context ctx) {
-      push(jsProgram.getDecimalLiteral(String.valueOf(x.getValue())));
-    }
-
-    @Override
     public void endVisit(JForStatement x, Context ctx) {
       JsFor jsFor = new JsFor();
 
@@ -742,11 +713,6 @@
     }
 
     @Override
-    public void endVisit(JIntLiteral x, Context ctx) {
-      push(jsProgram.getIntegralLiteral(BigInteger.valueOf(x.getValue())));
-    }
-
-    @Override
     public void endVisit(JLabel x, Context ctx) {
       push(new JsLabel(names.get(x)));
     }
@@ -792,11 +758,6 @@
     }
 
     @Override
-    public void endVisit(JLongLiteral x, Context ctx) {
-      push(jsProgram.getIntegralLiteral(BigInteger.valueOf(x.getValue())));
-    }
-
-    @Override
     public void endVisit(JMethod x, Context ctx) {
       if (x.isAbstract()) {
         push(null);
@@ -930,11 +891,6 @@
     }
 
     @Override
-    public void endVisit(JNullLiteral x, Context ctx) {
-      push(jsProgram.getNullLiteral());
-    }
-
-    @Override
     public void endVisit(JParameter x, Context ctx) {
       push(new JsParameter(names.get(x)));
     }
@@ -1070,11 +1026,6 @@
     }
 
     @Override
-    public void endVisit(JStringLiteral x, Context ctx) {
-      push(jsProgram.getStringLiteral(x.getValue()));
-    }
-
-    @Override
     public void endVisit(JThisRef x, Context ctx) {
       push(new JsThisRef());
     }
@@ -1449,41 +1400,6 @@
       jsInvocation.setQualifier(names.get(clinitMethod).makeRef());
       return jsInvocation;
     }
-
-    private <T extends JsVisitable> T pop() {
-      return (T) nodeStack.pop();
-    }
-
-    private <T extends JsVisitable> List<T> popList(int count) {
-      List<T> list = new ArrayList<T>();
-      while (count > 0) {
-        T item = this.<T> pop();
-        if (item != null) {
-          list.add(item);
-        }
-        --count;
-      }
-      Collections.reverse(list);
-      return list;
-    }
-
-    private <T extends JsVisitable<T>> void popList(List<T> collection,
-        int count) {
-      List<T> list = new ArrayList<T>();
-      while (count > 0) {
-        T item = this.<T> pop();
-        if (item != null) {
-          list.add(item);
-        }
-        --count;
-      }
-      Collections.reverse(list);
-      collection.addAll(list);
-    }
-
-    private <T extends JsVisitable> void push(T node) {
-      nodeStack.push(node);
-    }
   }
 
   private static class JavaToJsOperatorMap {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptLiterals.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptLiterals.java
new file mode 100644
index 0000000..a8cda95
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptLiterals.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2007 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.dev.jjs.ast.Context;
+import com.google.gwt.dev.jjs.ast.JBooleanLiteral;
+import com.google.gwt.dev.jjs.ast.JCharLiteral;
+import com.google.gwt.dev.jjs.ast.JDoubleLiteral;
+import com.google.gwt.dev.jjs.ast.JFloatLiteral;
+import com.google.gwt.dev.jjs.ast.JIntLiteral;
+import com.google.gwt.dev.jjs.ast.JLongLiteral;
+import com.google.gwt.dev.jjs.ast.JNullLiteral;
+import com.google.gwt.dev.jjs.ast.JStringLiteral;
+import com.google.gwt.dev.jjs.ast.JVisitor;
+import com.google.gwt.dev.js.ast.JsProgram;
+import com.google.gwt.dev.js.ast.JsVisitable;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Stack;
+
+/**
+ * Translates Java literals into JavaScript literals.
+ */
+public class GenerateJavaScriptLiterals extends JVisitor {
+
+  private final JsProgram program;
+  private final Stack<JsVisitable<?>> nodeStack = new Stack<JsVisitable<?>>();
+
+  public GenerateJavaScriptLiterals(JsProgram program) {
+    this.program = program;
+  }
+
+  @Override
+  public final void endVisit(JBooleanLiteral x, Context ctx) {
+    push(x.getValue() ? program.getTrueLiteral() : program.getFalseLiteral());
+  }
+
+  @Override
+  public final void endVisit(JCharLiteral x, Context ctx) {
+    push(program.getIntegralLiteral(BigInteger.valueOf(x.getValue())));
+  }
+
+  @Override
+  public final void endVisit(JDoubleLiteral x, Context ctx) {
+    push(program.getDecimalLiteral(String.valueOf(x.getValue())));
+  }
+
+  @Override
+  public final void endVisit(JFloatLiteral x, Context ctx) {
+    push(program.getDecimalLiteral(String.valueOf(x.getValue())));
+  }
+
+  @Override
+  public final void endVisit(JIntLiteral x, Context ctx) {
+    push(program.getIntegralLiteral(BigInteger.valueOf(x.getValue())));
+  }
+
+  @Override
+  public final void endVisit(JLongLiteral x, Context ctx) {
+    push(program.getIntegralLiteral(BigInteger.valueOf(x.getValue())));
+  }
+
+  @Override
+  public final void endVisit(JNullLiteral x, Context ctx) {
+    push(program.getNullLiteral());
+  }
+
+  @Override
+  public final void endVisit(JStringLiteral x, Context ctx) {
+    push(program.getStringLiteral(x.getValue()));
+  }
+
+  public final <T extends JsVisitable> T peek() {
+    return (T) nodeStack.peek();
+  }
+
+  protected final <T extends JsVisitable> T pop() {
+    return (T) nodeStack.pop();
+  }
+
+  protected final <T extends JsVisitable> List<T> popList(int count) {
+    List<T> list = new ArrayList<T>();
+    while (count > 0) {
+      T item = this.<T> pop();
+      if (item != null) {
+        list.add(item);
+      }
+      --count;
+    }
+    Collections.reverse(list);
+    return list;
+  }
+
+  protected final <T extends JsVisitable> void popList(List<T> collection,
+      int count) {
+    List<T> list = new ArrayList<T>();
+    while (count > 0) {
+      T item = this.<T> pop();
+      if (item != null) {
+        list.add(item);
+      }
+      --count;
+    }
+    Collections.reverse(list);
+    collection.addAll(list);
+  }
+
+  protected final <T extends JsVisitable> void push(T node) {
+    nodeStack.push(node);
+  }
+}
\ No newline at end of file