Merging long emulation branch into trunk.  Longs are now emulated in web mode as a pair of doubles, representing the upper and lower 32 
bits.

TODO(spoon): warn on long values passing between Java and JavaScript

Patch by: spoon (LongLib/LongLibTest), me (most everything else)
Review by: fabbott (LongLib/JRE changes), bobv & spoon (TBR, everything else)



git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2145 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/build.xml b/dev/core/build.xml
index 6c61218..a89c2eb 100755
--- a/dev/core/build.xml
+++ b/dev/core/build.xml
@@ -94,6 +94,7 @@
 		<antcall target="-filter.src" />
 
 		<mkdir dir="${javac.out}" />
+		<gwt.javac srcdir="super" excludes="com/google/gwt/dev/jjs/intrinsic/"/>
 		<gwt.javac srcdir="${src.filtered}" />
 		<gwt.javac srcdir="src" excludes="${filter.pattern}">
 			<classpath>
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 b36f237..dfd635a 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -48,8 +48,10 @@
 import com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST;
 import com.google.gwt.dev.jjs.impl.JavaScriptObjectNormalizer;
 import com.google.gwt.dev.jjs.impl.JsoDevirtualizer;
+import com.google.gwt.dev.jjs.impl.LongCastNormalizer;
+import com.google.gwt.dev.jjs.impl.LongEmulationNormalizer;
 import com.google.gwt.dev.jjs.impl.MakeCallsStatic;
-import com.google.gwt.dev.jjs.impl.MethodAndClassFinalizer;
+import com.google.gwt.dev.jjs.impl.Finalizer;
 import com.google.gwt.dev.jjs.impl.MethodCallTightener;
 import com.google.gwt.dev.jjs.impl.MethodInliner;
 import com.google.gwt.dev.jjs.impl.Pruner;
@@ -287,9 +289,7 @@
         String[] all = rpo.getAllPossibleRebindAnswers(logger, element);
         Util.addAll(allEntryPoints, all);
       }
-      allEntryPoints.add("com.google.gwt.lang.Array");
-      allEntryPoints.add("com.google.gwt.lang.Cast");
-      allEntryPoints.add("com.google.gwt.lang.Exceptions");
+      allEntryPoints.addAll(JProgram.CODEGEN_TYPES_SET);
       allEntryPoints.add("com.google.gwt.lang.Stats");
       allEntryPoints.add("java.lang.Object");
       allEntryPoints.add("java.lang.String");
@@ -399,7 +399,7 @@
         // Remove unreferenced types, fields, methods, [params, locals]
         didChange = Pruner.exec(jprogram, true) || didChange;
         // finalize locals, params, fields, methods, classes
-        didChange = MethodAndClassFinalizer.exec(jprogram) || didChange;
+        didChange = Finalizer.exec(jprogram) || didChange;
         // rewrite non-polymorphic calls as static calls; update all call sites
         didChange = MakeCallsStatic.exec(jprogram) || didChange;
 
@@ -428,9 +428,11 @@
       // (5) "Normalize" the high-level Java tree into a lower-level tree more
       // suited for JavaScript code generation. Don't go reordering these
       // willy-nilly because there are some subtle interdependencies.
+      LongCastNormalizer.exec(jprogram);
       JsoDevirtualizer.exec(jprogram);
       CatchBlockNormalizer.exec(jprogram);
       CompoundAssignmentNormalizer.exec(jprogram);
+      LongEmulationNormalizer.exec(jprogram);
       CastNormalizer.exec(jprogram);
       ArrayNormalizer.exec(jprogram);
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/CanBeSetFinal.java b/dev/core/src/com/google/gwt/dev/jjs/ast/CanBeSetFinal.java
index 0c4e2b4..39c9709 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/CanBeSetFinal.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/CanBeSetFinal.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -19,6 +19,6 @@
  * Characteristic interface to be overlaid on AST constructs that can retain a
  * boolean 'final' flag.
  */
-public interface CanBeSetFinal {
-  void setFinal(boolean b);
+public interface CanBeSetFinal extends CanBeFinal {
+  void setFinal();
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/HasInitializer.java b/dev/core/src/com/google/gwt/dev/jjs/ast/CanHaveInitializer.java
similarity index 84%
rename from dev/core/src/com/google/gwt/dev/jjs/ast/HasInitializer.java
rename to dev/core/src/com/google/gwt/dev/jjs/ast/CanHaveInitializer.java
index 20a97f7..a6cdbfc 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/HasInitializer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/CanHaveInitializer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -16,9 +16,11 @@
 package com.google.gwt.dev.jjs.ast;
 
 /**
- * Interface implemented by Java entities that can have an initialization 
+ * Interface implemented by Java entities that can have an initialization
  * expression.
  */
-public interface HasInitializer {
+public interface CanHaveInitializer {
+  JLiteral getConstInitializer();
+  boolean hasInitializer();
   void setInitializer(JExpression expression);
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayRef.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayRef.java
index d745816..c6d368a 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayRef.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayRef.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -56,7 +56,9 @@
   }
 
   public boolean hasSideEffects() {
-    return instance.hasSideEffects() || indexExpr.hasSideEffects();
+    // TODO: make the last test better when we have null tracking.
+    return instance.hasSideEffects() || indexExpr.hasSideEffects()
+        || instance.getType() == program.getTypeNull();
   }
 
   public void traverse(JVisitor visitor, Context ctx) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayType.java
index 82261db..a220699 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayType.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -81,7 +81,7 @@
   }
 
   public boolean isFinal() {
-    return false;
+    return leafType.isFinal();
   }
 
   public void traverse(JVisitor visitor, Context ctx) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JBinaryOperation.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JBinaryOperation.java
index b8ac8a9..1694a80 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JBinaryOperation.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JBinaryOperation.java
@@ -49,11 +49,8 @@
   }
 
   public JType getType() {
-    if (op == JBinaryOperator.ASG) {
-      // Use rhs because (generality lhs >= generality rhs)
-      return getRhs().getType();
-    } else if (isAssignment()) {
-      // Use lhs because this is really a write-then-read
+    if (isAssignment()) {
+      // Use the type of the lhs
       return getLhs().getType();
     } else {
       // Most binary operators never change type
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JBinaryOperator.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JBinaryOperator.java
index 72785fd..1922711 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JBinaryOperator.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JBinaryOperator.java
@@ -20,58 +20,35 @@
  * 750, Table 2. I just numbered the table top to bottom as 0 through 14. Lower
  * number means higher precedence.
  */
-public class JBinaryOperator {
+public enum JBinaryOperator {
 
-  public static final JBinaryOperator MUL = new JBinaryOperator("*", 3);
-  public static final JBinaryOperator DIV = new JBinaryOperator("/", 3);
-  public static final JBinaryOperator MOD = new JBinaryOperator("%", 3);
-  public static final JBinaryOperator ADD = new JBinaryOperator("+", 4);
-  public static final JBinaryOperator SUB = new JBinaryOperator("-", 4);
+  // Don't renumber precs without checking implementation of isShiftOperator()
 
-  public static final JBinaryOperator SHL = new JBinaryOperator("<<", 5);
-  public static final JBinaryOperator SHR = new JBinaryOperator(">>", 5);
-  public static final JBinaryOperator SHRU = new JBinaryOperator(">>>", 5);
-
-  public static final JBinaryOperator LT = new JBinaryOperator("<", 6);
-  public static final JBinaryOperator LTE = new JBinaryOperator("<=", 6);
-  public static final JBinaryOperator GT = new JBinaryOperator(">", 6);
-  public static final JBinaryOperator GTE = new JBinaryOperator(">=", 6);
-
-  public static final JBinaryOperator EQ = new JBinaryOperator("==", 7);
-  public static final JBinaryOperator NEQ = new JBinaryOperator("!=", 7);
-
-  public static final JBinaryOperator BIT_AND = new JBinaryOperator("&", 8);
-
-  public static final JBinaryOperator BIT_XOR = new JBinaryOperator("^", 9);
-
-  public static final JBinaryOperator BIT_OR = new JBinaryOperator("|", 10);
-
-  public static final JBinaryOperator AND = new JBinaryOperator("&&", 11);
-
-  public static final JBinaryOperator OR = new JBinaryOperator("||", 12);
-
-  // Don't renumber ASG precs without checking implementation of isAssignment()
-  public static final JBinaryOperator ASG = new JBinaryOperator("=", 14);
-  public static final JBinaryOperator ASG_ADD = new JBinaryOperator("+=", 14);
-  public static final JBinaryOperator ASG_SUB = new JBinaryOperator("-=", 14);
-  public static final JBinaryOperator ASG_MUL = new JBinaryOperator("*=", 14);
-  public static final JBinaryOperator ASG_DIV = new JBinaryOperator("/=", 14);
-  public static final JBinaryOperator ASG_MOD = new JBinaryOperator("%=", 14);
-  public static final JBinaryOperator ASG_SHL = new JBinaryOperator("<<=", 14);
-  public static final JBinaryOperator ASG_SHR = new JBinaryOperator(">>=", 14);
-  public static final JBinaryOperator ASG_SHRU = new JBinaryOperator(">>>=", 14);
-  public static final JBinaryOperator ASG_BIT_AND = new JBinaryOperator("&=",
-      14);
-  public static final JBinaryOperator ASG_BIT_OR = new JBinaryOperator("|=", 14);
-  public static final JBinaryOperator ASG_BIT_XOR = new JBinaryOperator("^=",
-      14);
+  MUL("*", 3), DIV("/", 3), MOD("%", 3), ADD("+", 4), SUB("-", 4), SHL("<<", 5), SHR(
+      ">>", 5), SHRU(">>>", 5), LT("<", 6), LTE("<=", 6), GT(">", 6), GTE(">=",
+      6), EQ("==", 7), NEQ("!=", 7), BIT_AND("&", 8), BIT_XOR("^", 9), BIT_OR(
+      "|", 10), AND("&&", 11), OR("||", 12), ASG("=", 14), ASG_ADD("+=", 14,
+      ADD), ASG_SUB("-=", 14, SUB), ASG_MUL("*=", 14, MUL), ASG_DIV("/=", 14,
+      DIV), ASG_MOD("%=", 14, MOD), ASG_SHL("<<=", 14, SHL), ASG_SHR(">>=", 14,
+      SHR), ASG_SHRU(">>>=", 14, SHRU), ASG_BIT_AND("&=", 14, BIT_AND), ASG_BIT_OR(
+      "|=", 14, BIT_OR), ASG_BIT_XOR("^=", 14, BIT_XOR);
 
   private final char[] symbol;
+  private final JBinaryOperator nonAsg;
   private final int precedence;
 
   private JBinaryOperator(String symbol, int precedence) {
+    this(symbol, precedence, null);
+  }
+
+  private JBinaryOperator(String symbol, int precedence, JBinaryOperator nonAsg) {
     this.symbol = symbol.toCharArray();
     this.precedence = precedence;
+    this.nonAsg = nonAsg;
+  }
+
+  public JBinaryOperator getNonAssignmentOf() {
+    return nonAsg;
   }
 
   public int getPrecedence() {
@@ -83,11 +60,12 @@
   }
 
   public boolean isAssignment() {
-    /*
-     * Beware, flaky! Maybe I should have added Yet Another Field to
-     * BinaryOperator?
-     */
-    return (precedence == ASG.getPrecedence());
+    return (this == ASG) || (getNonAssignmentOf() != null);
+  }
+
+  public boolean isShiftOperator() {
+    // Fragile implementation.
+    return precedence == 5 || (nonAsg != null && nonAsg.precedence == 5);
   }
 
   public String toString() {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JClassType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JClassType.java
index 0272c16..ebad0df 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JClassType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JClassType.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -52,8 +52,8 @@
     return isFinal;
   }
 
-  public void setFinal(boolean b) {
-    isFinal = b;
+  public void setFinal() {
+    isFinal = true;
   }
 
   public void traverse(JVisitor visitor, Context ctx) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JLocalDeclarationStatement.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclarationStatement.java
similarity index 68%
rename from dev/core/src/com/google/gwt/dev/jjs/ast/JLocalDeclarationStatement.java
rename to dev/core/src/com/google/gwt/dev/jjs/ast/JDeclarationStatement.java
index 7b5085c..c85bd77 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JLocalDeclarationStatement.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclarationStatement.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -20,29 +20,31 @@
 /**
  * Java initialized local variable statement.
  */
-public class JLocalDeclarationStatement extends JStatement {
+public class JDeclarationStatement extends JStatement {
 
   public JExpression initializer;
-  private JLocalRef localRef;
+  private JVariableRef variableRef;
 
-  public JLocalDeclarationStatement(JProgram program, SourceInfo info,
-      JLocalRef localRef, JExpression intializer) {
+  public JDeclarationStatement(JProgram program, SourceInfo info,
+      JVariableRef variableRef, JExpression intializer) {
     super(program, info);
-    this.localRef = localRef;
+    this.variableRef = variableRef;
     this.initializer = intializer;
+    CanHaveInitializer variable = variableRef.getTarget();
+    variable.setInitializer(intializer);
   }
 
   public JExpression getInitializer() {
     return initializer;
   }
 
-  public JLocalRef getLocalRef() {
-    return localRef;
+  public JVariableRef getVariableRef() {
+    return variableRef;
   }
 
   public void traverse(JVisitor visitor, Context ctx) {
     if (visitor.visit(this, ctx)) {
-      localRef = (JLocalRef) visitor.accept(localRef);
+      variableRef = (JVariableRef) visitor.accept(variableRef);
       if (initializer != null) {
         initializer = visitor.accept(initializer);
       }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JEnumField.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JEnumField.java
index c8942d1..42e94bc 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JEnumField.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JEnumField.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -26,7 +26,7 @@
 
   public JEnumField(JProgram program, SourceInfo info, String name,
       int ordinal, JEnumType enclosingType, JClassType type) {
-    super(program, info, name, enclosingType, type, true, true, true);
+    super(program, info, name, enclosingType, type, true, true, false);
     this.ordinal = ordinal;
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JField.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JField.java
index 534e772..5a319f4 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JField.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JField.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -18,35 +18,51 @@
 import com.google.gwt.dev.jjs.SourceInfo;
 
 /**
- * Java field definition. 
+ * Java field definition.
  */
-public class JField extends JVariable implements CanBeStatic, HasEnclosingType {
+public class JField extends JVariable implements CanBeStatic, HasEnclosingType,
+    CanHaveInitializer {
 
-  private JReferenceType enclosingType;
-  public JLiteral constInitializer;
+  private final JReferenceType enclosingType;
+  private final boolean isCompileTimeConstant;
   private final boolean isStatic;
-  private final boolean hasInitializer;
+  private boolean isVolatile;
 
   JField(JProgram program, SourceInfo info, String name,
-      JReferenceType enclosingType, JType type, boolean isStatic, boolean isFinal, boolean hasInitializer) {
+      JReferenceType enclosingType, JType type, boolean isStatic,
+      boolean isFinal, boolean isCompileTimeConstant) {
     super(program, info, name, type, isFinal);
     this.enclosingType = enclosingType;
     this.isStatic = isStatic;
-    this.hasInitializer  = hasInitializer;
+    this.isCompileTimeConstant = isCompileTimeConstant;
+    assert (isFinal || !isCompileTimeConstant);
   }
 
   public JReferenceType getEnclosingType() {
     return enclosingType;
   }
 
-  public boolean hasInitializer() {
-    return hasInitializer;
+  public boolean isCompileTimeConstant() {
+    return isCompileTimeConstant;
+  }
+
+  @Override
+  public boolean isFinal() {
+    return !isVolatile && super.isFinal();
   }
 
   public boolean isStatic() {
     return isStatic;
   }
 
+  public void setInitializer(JExpression initializer) {
+    this.initializer = initializer;
+  }
+
+  public void setVolatile() {
+    isVolatile = true;
+  }
+
   public void traverse(JVisitor visitor, Context ctx) {
     if (visitor.visit(this, ctx)) {
     }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JFieldRef.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JFieldRef.java
index c78e8c7..5abe912 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JFieldRef.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JFieldRef.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -61,7 +61,7 @@
   public boolean hasSideEffects() {
     // A cross-class reference to a static, non constant field forces clinit
     if (field.isStatic()
-        && (!field.isFinal() || field.constInitializer == null)) {
+        && (!field.isFinal() || !field.isCompileTimeConstant())) {
       if (program.typeOracle.checkClinit(enclosingType,
           field.getEnclosingType())) {
         // Therefore, we have side effects
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JLocal.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JLocal.java
index 924bba5..66a6e2d 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JLocal.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JLocal.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -20,7 +20,8 @@
 /**
  * Java local variable definition.
  */
-public class JLocal extends JVariable implements HasEnclosingMethod {
+public class JLocal extends JVariable implements HasEnclosingMethod,
+    CanHaveInitializer {
 
   private final JMethodBody enclosingMethodBody;
 
@@ -34,6 +35,10 @@
     return enclosingMethodBody.method;
   }
 
+  public void setInitializer(JExpression initializer) {
+    this.initializer = initializer;
+  }
+
   public void traverse(JVisitor visitor, Context ctx) {
     if (visitor.visit(this, ctx)) {
     }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
index 75ba9e9..e2976b3 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -25,8 +25,8 @@
  * A Java method implementation.
  */
 public final class JMethod extends JNode implements HasEnclosingType, HasName,
-    HasType, HasSettableType, CanBeAbstract, CanBeFinal, CanBeSetFinal,
-    CanBeNative, CanBeStatic {
+    HasSettableType, CanBeAbstract, CanBeSetFinal, CanBeNative,
+    CanBeStatic {
 
   /**
    * References to any methods which this method overrides. This should be an
@@ -35,10 +35,8 @@
    */
   public final List<JMethod> overrides = new ArrayList<JMethod>();
 
-  public final ArrayList<JParameter> params =
-    new ArrayList<JParameter>();
-  public final ArrayList<JClassType> thrownExceptions =
-    new ArrayList<JClassType>();
+  public final ArrayList<JParameter> params = new ArrayList<JParameter>();
+  public final ArrayList<JClassType> thrownExceptions = new ArrayList<JClassType>();
   private JAbstractMethodBody body = null;
   private final JReferenceType enclosingType;
   private final boolean isAbstract;
@@ -131,8 +129,8 @@
     body.setMethod(this);
   }
 
-  public void setFinal(boolean b) {
-    isFinal = b;
+  public void setFinal() {
+    isFinal = true;
   }
 
   public void setType(JType newType) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JModVisitor.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JModVisitor.java
index 0f6ae4e..0468afb 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JModVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JModVisitor.java
@@ -23,6 +23,7 @@
 /**
  * A visitor for iterating through and modifying an AST.
  */
+@SuppressWarnings("unchecked")
 public class JModVisitor extends JVisitor {
 
   private interface ContextFactory {
@@ -92,13 +93,6 @@
       didChange = replaced = true;
     }
 
-    protected void doReplace(Class targetClass, JNode x) {
-      checkState();
-      checkReplacement((JNode) list.get(index), x);
-      list.set(index, x);
-      didChange = replaced = true;
-    }
-
     protected void traverse(List list) {
       this.list = list;
       for (index = 0; index < list.size(); ++index) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JParameter.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JParameter.java
index f8a644b..f49d3bc 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JParameter.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JParameter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -44,6 +44,11 @@
     return isThis;
   }
 
+  public void setInitializer(JExpression expression) {
+    throw new UnsupportedOperationException(
+        "A JParameter cannot have an initializer");
+  }
+
   public void traverse(JVisitor visitor, Context ctx) {
     if (visitor.visit(this, ctx)) {
     }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JPrimitiveType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JPrimitiveType.java
index d6e9aef..9f05640 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JPrimitiveType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JPrimitiveType.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -50,6 +50,10 @@
     return wrapperTypeName;
   }
 
+  public boolean isFinal() {
+    return true;
+  }
+
   public void traverse(JVisitor visitor, Context ctx) {
     if (visitor.visit(this, ctx)) {
     }
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 63d520b..9bc354a 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
@@ -33,6 +33,7 @@
 import java.util.HashSet;
 import java.util.IdentityHashMap;
 import java.util.Iterator;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -43,18 +44,18 @@
  */
 public class JProgram extends JNode {
 
-  private static final Set<String> CODEGEN_TYPES_SET = new HashSet<String>(
+  public static final Set<String> CODEGEN_TYPES_SET = new LinkedHashSet<String>(
       Arrays.asList(new String[] {
           "com.google.gwt.lang.Array", "com.google.gwt.lang.Cast",
-          "com.google.gwt.lang.Exceptions", "com.google.gwt.lang.Stats"}));
+          "com.google.gwt.lang.Exceptions", "com.google.gwt.lang.LongLib",
+          "com.google.gwt.lang.Stats",}));
 
   private static final Set<String> INDEX_TYPES_SET = new HashSet<String>(
       Arrays.asList(new String[] {
           "java.lang.Object", "java.lang.String", "java.lang.Class",
           "java.lang.CharSequence", "java.lang.Comparable", "java.lang.Enum",
           "java.lang.Iterable", "java.util.Iterator",
-          "com.google.gwt.core.client.JavaScriptObject",
-          "com.google.gwt.lang.Array"}));
+          "com.google.gwt.core.client.JavaScriptObject"}));
 
   private static final int IS_ARRAY = 2;
 
@@ -305,22 +306,14 @@
 
   public JField createField(SourceInfo info, char[] name,
       JReferenceType enclosingType, JType type, boolean isStatic,
-      boolean isFinal, boolean hasInitializer) {
+      boolean isFinal, boolean isCompileTimeConstant) {
     assert (name != null);
     assert (enclosingType != null);
     assert (type != null);
 
-    /*
-     * MAGIC: filled in during code gen, don't bother synthesizing dummy
-     * initializations.
-     */
-    if (enclosingType == typeJavaLangObject) {
-      hasInitializer = true;
-    }
-
     String sname = String.valueOf(name);
     JField x = new JField(this, info, sname, enclosingType, type, isStatic,
-        isFinal, hasInitializer);
+        isFinal, isCompileTimeConstant);
 
     if (indexedTypes.containsValue(enclosingType)) {
       indexedFields.put(enclosingType.getShortName() + '.' + sname, x);
@@ -540,7 +533,7 @@
   public JField getNullField() {
     if (nullField == null) {
       nullField = new JField(this, null, "nullField", null, typeNull, false,
-          true, true);
+          true, false);
     }
     return nullField;
   }
@@ -678,7 +671,12 @@
 
   public boolean isClinit(JMethod method) {
     JReferenceType enclosingType = method.getEnclosingType();
-    return (enclosingType != null) && (method == enclosingType.methods.get(0));
+    if ((enclosingType != null) && (method == enclosingType.methods.get(0))) {
+      assert (method.getName().equals("$clinit"));
+      return true;
+    } else {
+      return false;
+    }
   }
 
   public boolean isJavaScriptObject(JType type) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JReferenceType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JReferenceType.java
index b8e2ae0..76e244a 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JReferenceType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JReferenceType.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -23,8 +23,7 @@
 /**
  * Base class for any reference type.
  */
-public abstract class JReferenceType extends JType implements CanBeAbstract,
-    CanBeFinal {
+public abstract class JReferenceType extends JType implements CanBeAbstract {
 
   public List<JField> fields = new ArrayList<JField>();
   public List<JMethod> methods = new ArrayList<JMethod>();
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JType.java
index e6232d7..2223750 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JType.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -20,7 +20,7 @@
 /**
  * Base class for any types entity.
  */
-public abstract class JType extends JNode implements HasName {
+public abstract class JType extends JNode implements HasName, CanBeFinal {
 
   protected final String name;
   private final JLiteral defaultValue;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JUnaryOperator.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JUnaryOperator.java
index d66851d..49b865a 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JUnaryOperator.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JUnaryOperator.java
@@ -18,13 +18,9 @@
 /**
  * An enumeration of the available unary operators.
  */
-public class JUnaryOperator {
+public enum JUnaryOperator {
 
-  public static final JUnaryOperator INC = new JUnaryOperator("++");
-  public static final JUnaryOperator DEC = new JUnaryOperator("--");
-  public static final JUnaryOperator NEG = new JUnaryOperator("-");
-  public static final JUnaryOperator NOT = new JUnaryOperator("!");
-  public static final JUnaryOperator BIT_NOT = new JUnaryOperator("~");
+  INC("++"), DEC("--"), NEG("-"), NOT("!"), BIT_NOT("~");
 
   private final char[] symbol;
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JVariable.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JVariable.java
index 9f784f9..32416b8 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JVariable.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JVariable.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -20,9 +20,10 @@
 /**
  * Base class for any storage location.
  */
-public abstract class JVariable extends JNode implements CanBeFinal, HasName,
-    HasType, HasSettableType {
+public abstract class JVariable extends JNode implements CanBeSetFinal,
+    CanHaveInitializer, HasName, HasSettableType {
 
+  protected JExpression initializer = null;
   private boolean isFinal;
   private final String name;
   private JType type;
@@ -35,6 +36,13 @@
     this.isFinal = isFinal;
   }
 
+  public JLiteral getConstInitializer() {
+    if (isFinal() && initializer instanceof JLiteral) {
+      return (JLiteral) initializer;
+    }
+    return null;
+  }
+
   public String getName() {
     return name;
   }
@@ -43,10 +51,18 @@
     return type;
   }
 
+  public boolean hasInitializer() {
+    return initializer != null;
+  }
+
   public boolean isFinal() {
     return isFinal;
   }
 
+  public void setFinal() {
+    isFinal = true;
+  }
+
   public void setType(JType newType) {
     type = newType;
   }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java
index 33d3d2d..7e61a47 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java
@@ -25,12 +25,12 @@
 import com.google.gwt.dev.jjs.ast.js.JsonObject;
 import com.google.gwt.dev.jjs.ast.js.JsonObject.JsonPropInit;
 
-import java.util.Iterator;
 import java.util.List;
 
 /**
  * A visitor for iterating through an AST.
  */
+@SuppressWarnings({"unused", "unchecked"})
 public class JVisitor {
 
   protected static final Context UNMODIFIABLE_CONTEXT = new Context() {
@@ -133,6 +133,9 @@
   public void endVisit(JContinueStatement x, Context ctx) {
   }
 
+  public void endVisit(JDeclarationStatement x, Context ctx) {
+  }
+
   public void endVisit(JDoStatement x, Context ctx) {
   }
 
@@ -175,9 +178,6 @@
   public void endVisit(JLocal x, Context ctx) {
   }
 
-  public void endVisit(JLocalDeclarationStatement x, Context ctx) {
-  }
-
   public void endVisit(JLocalRef x, Context ctx) {
   }
 
@@ -329,6 +329,10 @@
     return true;
   }
 
+  public boolean visit(JDeclarationStatement x, Context ctx) {
+    return true;
+  }
+
   public boolean visit(JDoStatement x, Context ctx) {
     return true;
   }
@@ -385,10 +389,6 @@
     return true;
   }
 
-  public boolean visit(JLocalDeclarationStatement x, Context ctx) {
-    return true;
-  }
-
   public boolean visit(JLocalRef x, Context ctx) {
     return true;
   }
@@ -511,14 +511,14 @@
   }
 
   protected void doAccept(List list) {
-    for (Iterator it = list.iterator(); it.hasNext();) {
-      doTraverse((JNode) it.next(), UNMODIFIABLE_CONTEXT);
+    for (Object node : list) {
+      doTraverse((JNode) node, UNMODIFIABLE_CONTEXT);
     }
   }
 
   protected void doAcceptWithInsertRemove(List list) {
-    for (Iterator it = list.iterator(); it.hasNext();) {
-      doTraverse((JNode) it.next(), UNMODIFIABLE_CONTEXT);
+    for (Object node : list) {
+      doTraverse((JNode) node, UNMODIFIABLE_CONTEXT);
     }
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java b/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java
index f6b8906..66a7df0 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java
@@ -29,6 +29,7 @@
 import com.google.gwt.dev.jjs.ast.JNewInstance;
 import com.google.gwt.dev.jjs.ast.JParameter;
 import com.google.gwt.dev.jjs.ast.JParameterRef;
+import com.google.gwt.dev.jjs.ast.JPrimitiveType;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JReferenceType;
 import com.google.gwt.dev.jjs.ast.JReturnStatement;
@@ -60,6 +61,7 @@
 import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
 import org.eclipse.jdt.internal.compiler.ast.Statement;
 import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.impl.Constant;
 import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
 import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
 import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
@@ -273,7 +275,7 @@
             && ((AllocationExpression) initialization).enumConstant != null) {
           createEnumField(info, b, enclosingType);
         } else {
-          createField(info, b, enclosingType, initialization != null);
+          createField(info, b, enclosingType);
         }
         return true;
       } catch (Throwable e) {
@@ -342,10 +344,19 @@
     }
 
     private JField createField(SourceInfo info, FieldBinding binding,
-        JReferenceType enclosingType, boolean hasInitializer) {
+        JReferenceType enclosingType) {
       JType type = (JType) typeMap.get(binding.type);
+
+      boolean isCompileTimeConstant = binding.isStatic() && (binding.isFinal())
+          && (binding.constant() != Constant.NotAConstant)
+          && (binding.type.isBaseType());
+      assert (type instanceof JPrimitiveType || !isCompileTimeConstant);
+
       JField field = program.createField(info, binding.name, enclosingType,
-          type, binding.isStatic(), binding.isFinal(), hasInitializer);
+          type, binding.isStatic(), binding.isFinal(), isCompileTimeConstant);
+      if (binding.isVolatile()) {
+        field.setVolatile();
+      }
       typeMap.put(binding, field);
       return field;
     }
@@ -354,7 +365,7 @@
         JReferenceType enclosingType) {
       JType type = (JType) typeMap.get(binding.type);
       JField field = program.createField(null, binding.name, enclosingType,
-          type, false, true, true);
+          type, false, true, false);
       if (binding.matchingField != null) {
         typeMap.put(binding.matchingField, field);
       }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/CastNormalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/CastNormalizer.java
index c340f98..02eb46b 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/CastNormalizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/CastNormalizer.java
@@ -48,8 +48,8 @@
 
 /**
  * Replace cast and instanceof operations with calls to the Cast class. Depends
- * on {@link CatchBlockNormalizer}, {@link CompoundAssignmentNormalizer}, and
- * {@link JsoDevirtualizer} having already run.
+ * on {@link CatchBlockNormalizer}, {@link CompoundAssignmentNormalizer},
+ * {@link JsoDevirtualizer}, and {@link LongCastNormalizer} having already run.
  * 
  * <p>
  * Object and String always get a typeId of 1 and 2, respectively. 0 is reserved
@@ -316,13 +316,12 @@
   }
 
   /**
-   * Explicitly convert any char-typed expressions within a concat operation
-   * into strings.
+   * Explicitly convert any char or long type expressions within a concat
+   * operation into strings because normal JavaScript conversion does not work
+   * correctly.
    */
   private class ConcatVisitor extends JModVisitor {
 
-    private JMethod stringValueOfChar = null;
-
     @Override
     public void endVisit(JBinaryOperation x, Context ctx) {
       if (x.getType() != program.getTypeJavaLangString()) {
@@ -330,8 +329,8 @@
       }
 
       if (x.getOp() == JBinaryOperator.ADD) {
-        JExpression newLhs = convertCharString(x.getLhs());
-        JExpression newRhs = convertCharString(x.getRhs());
+        JExpression newLhs = convertString(x.getLhs());
+        JExpression newRhs = convertString(x.getRhs());
         if (newLhs != x.getLhs() || newRhs != x.getRhs()) {
           JBinaryOperation newExpr = new JBinaryOperation(program,
               x.getSourceInfo(), program.getTypeJavaLangString(),
@@ -339,7 +338,7 @@
           ctx.replaceMe(newExpr);
         }
       } else if (x.getOp() == JBinaryOperator.ASG_ADD) {
-        JExpression newRhs = convertCharString(x.getRhs());
+        JExpression newRhs = convertString(x.getRhs());
         if (newRhs != x.getRhs()) {
           JBinaryOperation newExpr = new JBinaryOperation(program,
               x.getSourceInfo(), program.getTypeJavaLangString(),
@@ -349,16 +348,18 @@
       }
     }
 
-    private JExpression convertCharString(JExpression expr) {
+    private JExpression convertString(JExpression expr) {
       JPrimitiveType charType = program.getTypePrimitiveChar();
       if (expr.getType() == charType) {
-        // Replace the character with a call to Cast.charToString()
-        if (stringValueOfChar == null) {
-          stringValueOfChar = program.getIndexedMethod("Cast.charToString");
-          assert (stringValueOfChar != null);
-        }
+        // Replace with Cast.charToString(c)
         JMethodCall call = new JMethodCall(program, expr.getSourceInfo(), null,
-            stringValueOfChar);
+            program.getIndexedMethod("Cast.charToString"));
+        call.getArgs().add(expr);
+        return call;
+      } else if (expr.getType() == program.getTypePrimitiveLong()) {
+        // Replace with LongLib.toString(l)
+        JMethodCall call = new JMethodCall(program, expr.getSourceInfo(), null,
+            program.getIndexedMethod("LongLib.toString"));
         call.getArgs().add(expr);
         return call;
       }
@@ -396,6 +397,7 @@
     public void endVisit(JCastOperation x, Context ctx) {
       JExpression replaceExpr;
       JType toType = x.getCastType();
+      JExpression expr = x.getExpr();
       if (toType instanceof JNullType) {
         /*
          * Magic: a null type cast means the user tried a cast that couldn't
@@ -410,12 +412,12 @@
          */
         JMethodCall call = new JMethodCall(program, x.getSourceInfo(), null,
             method, program.getTypeNull());
-        call.getArgs().add(x.getExpr());
+        call.getArgs().add(expr);
         replaceExpr = call;
       } else if (toType instanceof JReferenceType) {
-        JExpression curExpr = x.getExpr();
+        JExpression curExpr = expr;
         JReferenceType refType = (JReferenceType) toType;
-        JReferenceType argType = (JReferenceType) x.getExpr().getType();
+        JReferenceType argType = (JReferenceType) expr.getType();
         if (program.typeOracle.canTriviallyCast(argType, refType)) {
           // just remove the cast
           replaceExpr = curExpr;
@@ -439,7 +441,6 @@
          * call a narrowing conversion function. EXCEPTION: we currently have no
          * way to narrow double to float, so don't bother.
          */
-        boolean narrow = false, round = false;
         JPrimitiveType tByte = program.getTypePrimitiveByte();
         JPrimitiveType tChar = program.getTypePrimitiveChar();
         JPrimitiveType tShort = program.getTypePrimitiveShort();
@@ -447,47 +448,70 @@
         JPrimitiveType tLong = program.getTypePrimitiveLong();
         JPrimitiveType tFloat = program.getTypePrimitiveFloat();
         JPrimitiveType tDouble = program.getTypePrimitiveDouble();
-        JType fromType = x.getExpr().getType();
-        if (tByte == fromType) {
-          if (tChar == toType) {
-            narrow = true;
-          }
-        } else if (tShort == fromType) {
-          if (tByte == toType || tChar == toType) {
-            narrow = true;
-          }
-        } else if (tChar == fromType) {
-          if (tByte == toType || tShort == toType) {
-            narrow = true;
-          }
-        } else if (tInt == fromType) {
+        JType fromType = expr.getType();
+
+        String methodName = null;
+
+        if (tLong == fromType && tLong != toType) {
           if (tByte == toType || tShort == toType || tChar == toType) {
-            narrow = true;
-          }
-        } else if (tLong == fromType) {
-          if (tByte == toType || tShort == toType || tChar == toType
-              || tInt == toType) {
-            narrow = true;
-          }
-        } else if (tFloat == fromType || tDouble == fromType) {
-          if (tByte == toType || tShort == toType || tChar == toType
-              || tInt == toType || tLong == toType) {
-            round = true;
+            /*
+             * We need a double call here, one to convert long->int, and another
+             * one to narrow. Construct the inner call here and fall through to
+             * do the narrowing conversion.
+             */
+            JMethod castMethod = program.getIndexedMethod("LongLib.toInt");
+            JMethodCall call = new JMethodCall(program, x.getSourceInfo(),
+                null, castMethod);
+            call.getArgs().add(expr);
+            expr = call;
+            fromType = tInt;
+          } else if (tInt == toType) {
+            methodName = "LongLib.toInt";
+          } else if (tFloat == toType || tDouble == toType) {
+            methodName = "LongLib.toDouble";
           }
         }
 
-        if (narrow || round) {
-          // Replace the expression with a call to the narrow or round method
-          String methodName = "Cast." + (narrow ? "narrow_" : "round_")
-              + toType.getName();
+        if (toType == tLong && fromType != tLong) {
+          // Longs get special treatment.
+          if (tByte == fromType || tShort == fromType || tChar == fromType
+              || tInt == fromType) {
+            methodName = "LongLib.fromInt";
+          } else if (tFloat == fromType || tDouble == fromType) {
+            methodName = "LongLib.fromDouble";
+          }
+        } else if (tByte == fromType) {
+          if (tChar == toType) {
+            methodName = "Cast.narrow_" + toType.getName();
+          }
+        } else if (tShort == fromType) {
+          if (tByte == toType || tChar == toType) {
+            methodName = "Cast.narrow_" + toType.getName();
+          }
+        } else if (tChar == fromType) {
+          if (tByte == toType || tShort == toType) {
+            methodName = "Cast.narrow_" + toType.getName();
+          }
+        } else if (tInt == fromType) {
+          if (tByte == toType || tShort == toType || tChar == toType) {
+            methodName = "Cast.narrow_" + toType.getName();
+          }
+        } else if (tFloat == fromType || tDouble == fromType) {
+          if (tByte == toType || tShort == toType || tChar == toType
+              || tInt == toType) {
+            methodName = "Cast.round_" + toType.getName();
+          }
+        }
+
+        if (methodName != null) {
           JMethod castMethod = program.getIndexedMethod(methodName);
           JMethodCall call = new JMethodCall(program, x.getSourceInfo(), null,
-              castMethod);
-          call.getArgs().add(x.getExpr());
+              castMethod, toType);
+          call.getArgs().add(expr);
           replaceExpr = call;
         } else {
           // Just remove the cast
-          replaceExpr = x.getExpr();
+          replaceExpr = expr;
         }
       }
       ctx.replaceMe(replaceExpr);
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/CompoundAssignmentNormalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/CompoundAssignmentNormalizer.java
index f738203..1ef357d 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/CompoundAssignmentNormalizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/CompoundAssignmentNormalizer.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.dev.jjs.impl;
 
+import com.google.gwt.dev.jjs.InternalCompilerException;
 import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JArrayRef;
 import com.google.gwt.dev.jjs.ast.JBinaryOperation;
@@ -26,8 +27,12 @@
 import com.google.gwt.dev.jjs.ast.JMethodBody;
 import com.google.gwt.dev.jjs.ast.JModVisitor;
 import com.google.gwt.dev.jjs.ast.JParameterRef;
+import com.google.gwt.dev.jjs.ast.JPostfixOperation;
+import com.google.gwt.dev.jjs.ast.JPrefixOperation;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JThisRef;
+import com.google.gwt.dev.jjs.ast.JType;
+import com.google.gwt.dev.jjs.ast.JUnaryOperator;
 import com.google.gwt.dev.jjs.ast.js.JMultiExpression;
 
 import java.util.ArrayList;
@@ -45,62 +50,146 @@
    */
   private class BreakupAssignOpsVisitor extends JModVisitor {
 
-    // @Override
+    @Override
     public void endVisit(JBinaryOperation x, Context ctx) {
-      /*
-       * Convert to a normal divide operation so we can cast the result. Since
-       * the left hand size must be computed twice, we have to replace any
-       * left-hand side expressions that could have side effects with
-       * temporaries, so that they are only run once.
-       */
-      if (x.getOp() == JBinaryOperator.ASG_DIV
+      JBinaryOperator op = x.getOp();
+      if (op.getNonAssignmentOf() == null) {
+        return;
+      }
+
+      boolean doIt = false;
+      if (x.getType() == program.getTypePrimitiveLong()) {
+        doIt = true;
+      }
+      if (op == JBinaryOperator.ASG_DIV
           && x.getType() != program.getTypePrimitiveFloat()
           && x.getType() != program.getTypePrimitiveDouble()) {
+        doIt = true;
+      }
 
-        /*
-         * Convert to a normal divide operation so we can cast the result. Since
-         * the left hand size must be computed twice, we have to replace any
-         * left-hand side expressions that could have side effects with
-         * temporaries, so that they are only run once.
-         */
-        final int pushUsedLocals = localIndex;
-        JMultiExpression multi = new JMultiExpression(program,
-            x.getSourceInfo());
-        ReplaceSideEffectsInLvalue replacer = new ReplaceSideEffectsInLvalue(
-            multi);
-        JExpression newLhs = replacer.accept(x.getLhs());
-        localIndex = pushUsedLocals;
+      if (!doIt) {
+        return;
+      }
 
-        JBinaryOperation operation = new JBinaryOperation(program,
-            x.getSourceInfo(), newLhs.getType(), JBinaryOperator.DIV, newLhs,
-            x.getRhs());
-        JBinaryOperation asg = new JBinaryOperation(program, x.getSourceInfo(),
-            newLhs.getType(), JBinaryOperator.ASG, newLhs, operation);
+      /*
+       * Convert to an assignment and binary operation. Since the left hand size
+       * must be computed twice, we have to replace any left-hand side
+       * expressions that could have side effects with temporaries, so that they
+       * are only run once.
+       */
+      final int pushLocalIndex = localIndex;
+      ReplaceSideEffectsInLvalue replacer = new ReplaceSideEffectsInLvalue(
+          new JMultiExpression(program, x.getSourceInfo()));
+      JExpression newLhs = replacer.accept(x.getLhs());
+      localIndex = pushLocalIndex;
 
-        JMultiExpression multiExpr = replacer.getMultiExpr();
-        if (multiExpr.exprs.isEmpty()) {
-          // just use the split assignment expression
-          ctx.replaceMe(asg);
-        } else {
-          // add the assignment as the last item in the multi
-          multi.exprs.add(asg);
-          ctx.replaceMe(multi);
-        }
+      JBinaryOperation operation = new JBinaryOperation(program,
+          x.getSourceInfo(), newLhs.getType(), op.getNonAssignmentOf(), newLhs,
+          x.getRhs());
+      JBinaryOperation asg = new JBinaryOperation(program, x.getSourceInfo(),
+          newLhs.getType(), JBinaryOperator.ASG, newLhs, operation);
+
+      JMultiExpression multiExpr = replacer.getMultiExpr();
+      if (multiExpr.exprs.isEmpty()) {
+        // just use the split assignment expression
+        ctx.replaceMe(asg);
+      } else {
+        // add the assignment as the last item in the multi
+        multiExpr.exprs.add(asg);
+        ctx.replaceMe(multiExpr);
       }
     }
 
-    // @Override
+    @Override
     public void endVisit(JMethodBody x, Context ctx) {
       clearLocals();
       currentMethodBody = null;
     }
 
-    // @Override
+    @Override
+    public void endVisit(JPostfixOperation x, Context ctx) {
+      JUnaryOperator op = x.getOp();
+      if (!op.isModifying()) {
+        return;
+      }
+      if (x.getType() != program.getTypePrimitiveLong()) {
+        return;
+      }
+
+      // Convert into a comma operation, such as:
+      // (t = x, x += 1, t)
+
+      // First, replace the arg with a non-side-effect causing one.
+      final int pushLocalIndex = localIndex;
+      JMultiExpression multi = new JMultiExpression(program, x.getSourceInfo());
+      ReplaceSideEffectsInLvalue replacer = new ReplaceSideEffectsInLvalue(
+          multi);
+      JExpression newArg = replacer.accept(x.getArg());
+
+      // Now generate the appropriate expressions.
+      JLocal tempLocal = getTempLocal(newArg.getType());
+
+      // t = x
+      JLocalRef tempRef = new JLocalRef(program, x.getSourceInfo(), tempLocal);
+      JBinaryOperation asg = new JBinaryOperation(program, x.getSourceInfo(),
+          x.getType(), JBinaryOperator.ASG, tempRef, newArg);
+      multi.exprs.add(asg);
+
+      // x += 1
+      asg = createAsgOpFromUnary(newArg, op);
+      // Break the resulting asg op before adding to multi.
+      multi.exprs.add(accept(asg));
+
+      // t
+      tempRef = new JLocalRef(program, x.getSourceInfo(), tempLocal);
+      multi.exprs.add(tempRef);
+
+      ctx.replaceMe(multi);
+      localIndex = pushLocalIndex;
+    }
+
+    @Override
+    public void endVisit(JPrefixOperation x, Context ctx) {
+      JUnaryOperator op = x.getOp();
+      if (!op.isModifying()) {
+        return;
+      }
+      if (x.getType() != program.getTypePrimitiveLong()) {
+        return;
+      }
+
+      // Convert into the equivalent binary assignment operation, such as:
+      // x += 1
+      JBinaryOperation asg = createAsgOpFromUnary(x.getArg(), op);
+
+      // Visit the result to break it up even more.
+      ctx.replaceMe(accept(asg));
+    }
+
+    @Override
     public boolean visit(JMethodBody x, Context ctx) {
       currentMethodBody = x;
       clearLocals();
       return true;
     }
+
+    private JBinaryOperation createAsgOpFromUnary(JExpression arg,
+        JUnaryOperator op) {
+      JBinaryOperator newOp;
+      if (op == JUnaryOperator.INC) {
+        newOp = JBinaryOperator.ASG_ADD;
+      } else if (op == JUnaryOperator.DEC) {
+        newOp = JBinaryOperator.ASG_SUB;
+      } else {
+        throw new InternalCompilerException(
+            "Unexpected modifying unary operator: "
+                + String.valueOf(op.getSymbol()));
+      }
+
+      JBinaryOperation asg = new JBinaryOperation(program, arg.getSourceInfo(),
+          arg.getType(), newOp, arg, program.getLiteralLong(1));
+      return asg;
+    }
   }
   /**
    * Replaces side effects in lvalue.
@@ -117,7 +206,7 @@
       return multi;
     }
 
-    // @Override
+    @Override
     public boolean visit(JArrayRef x, Context ctx) {
       JExpression newInstance = possiblyReplace(x.getInstance());
       JExpression newIndexExpr = possiblyReplace(x.getIndexExpr());
@@ -129,7 +218,7 @@
       return false;
     }
 
-    // @Override
+    @Override
     public boolean visit(JFieldRef x, Context ctx) {
       if (x.getInstance() != null) {
         JExpression newInstance = possiblyReplace(x.getInstance());
@@ -142,17 +231,17 @@
       return false;
     }
 
-    // @Override
+    @Override
     public boolean visit(JLocalRef x, Context ctx) {
       return false;
     }
 
-    // @Override
+    @Override
     public boolean visit(JParameterRef x, Context ctx) {
       return false;
     }
 
-    // @Override
+    @Override
     public boolean visit(JThisRef x, Context ctx) {
       return false;
     }
@@ -163,7 +252,7 @@
       }
 
       // Create a temp local
-      JLocal tempLocal = getTempLocal();
+      JLocal tempLocal = getTempLocal(x.getType());
 
       // Create an assignment for this temp and add it to multi.
       JLocalRef tempRef = new JLocalRef(program, x.getSourceInfo(), tempLocal);
@@ -198,13 +287,12 @@
     breaker.accept(program);
   }
 
-  private JLocal getTempLocal() {
+  private JLocal getTempLocal(JType type) {
     if (localIndex < tempLocals.size()) {
       return tempLocals.get(localIndex++);
     }
     JLocal newTemp = program.createLocal(null,
-        ("$t" + localIndex++).toCharArray(), program.getTypeVoid(), false,
-        currentMethodBody);
+        ("$t" + localIndex++).toCharArray(), type, false, currentMethodBody);
     tempLocals.add(newTemp);
     return newTemp;
   }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java b/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
index dcd896a..c14e418 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -23,23 +23,28 @@
 import com.google.gwt.dev.jjs.ast.JBreakStatement;
 import com.google.gwt.dev.jjs.ast.JCaseStatement;
 import com.google.gwt.dev.jjs.ast.JCharLiteral;
+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.JDeclarationStatement;
 import com.google.gwt.dev.jjs.ast.JDoStatement;
 import com.google.gwt.dev.jjs.ast.JDoubleLiteral;
 import com.google.gwt.dev.jjs.ast.JExpression;
 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.JForStatement;
 import com.google.gwt.dev.jjs.ast.JIfStatement;
 import com.google.gwt.dev.jjs.ast.JIntLiteral;
-import com.google.gwt.dev.jjs.ast.JLocalDeclarationStatement;
+import com.google.gwt.dev.jjs.ast.JLiteral;
 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.JMethodCall;
 import com.google.gwt.dev.jjs.ast.JModVisitor;
 import com.google.gwt.dev.jjs.ast.JNode;
+import com.google.gwt.dev.jjs.ast.JParameterRef;
+import com.google.gwt.dev.jjs.ast.JPostfixOperation;
 import com.google.gwt.dev.jjs.ast.JPrefixOperation;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JReferenceType;
@@ -50,6 +55,7 @@
 import com.google.gwt.dev.jjs.ast.JType;
 import com.google.gwt.dev.jjs.ast.JUnaryOperator;
 import com.google.gwt.dev.jjs.ast.JValueLiteral;
+import com.google.gwt.dev.jjs.ast.JVariableRef;
 import com.google.gwt.dev.jjs.ast.JVisitor;
 import com.google.gwt.dev.jjs.ast.JWhileStatement;
 import com.google.gwt.dev.jjs.ast.js.JMultiExpression;
@@ -73,10 +79,17 @@
    */
   public class DeadCodeVisitor extends JModVisitor {
 
+    private JClassType currentClass;
+
     /**
-     * An expression whose result does not matter.
+     * Expressions whose result does not matter.
      */
-    private JExpression ignoringExpressionOutput;
+    private Set<JExpression> ignoringExpressionOutput = new HashSet<JExpression>();
+
+    /**
+     * Expressions being used as lvalues.
+     */
+    private Set<JExpression> lvalues = new HashSet<JExpression>();
 
     private Set<JBlock> switchBlocks = new HashSet<JBlock>();
 
@@ -88,72 +101,37 @@
       JBinaryOperator op = x.getOp();
       JExpression lhs = x.getLhs();
       JExpression rhs = x.getRhs();
-      if (op == JBinaryOperator.AND) {
-        // simplify short circuit AND expressions
-        if (lhs instanceof JBooleanLiteral) {
-          // eg: if (false && isWhatever()) -> if (false)
-          // eg: if (true && isWhatever()) -> if (isWhatever())
-          JBooleanLiteral booleanLiteral = (JBooleanLiteral) lhs;
-          if (booleanLiteral.getValue()) {
-            ctx.replaceMe(rhs);
-          } else {
-            ctx.replaceMe(lhs);
+      switch (op) {
+        case AND:
+          shortCircuitAnd(lhs, rhs, ctx);
+          break;
+        case OR:
+          shortCircuitOr(lhs, rhs, ctx);
+          break;
+        case EQ:
+          // simplify: null == null -> true
+          if (lhs.getType() == program.getTypeNull()
+              && rhs.getType() == program.getTypeNull() && !x.hasSideEffects()) {
+            ctx.replaceMe(program.getLiteralBoolean(true));
           }
-
-        } else if (rhs instanceof JBooleanLiteral) {
-          // eg: if (isWhatever() && true) -> if (isWhatever())
-          // eg: if (isWhatever() && false) -> if (false), unless side effects
-          JBooleanLiteral booleanLiteral = (JBooleanLiteral) rhs;
-          if (booleanLiteral.getValue()) {
-            ctx.replaceMe(lhs);
-          } else if (!lhs.hasSideEffects()) {
-            ctx.replaceMe(rhs);
+          break;
+        case NEQ:
+          // simplify: null != null -> false
+          if (lhs.getType() == program.getTypeNull()
+              && rhs.getType() == program.getTypeNull() && !x.hasSideEffects()) {
+            ctx.replaceMe(program.getLiteralBoolean(false));
           }
-        }
-
-      } else if (op == JBinaryOperator.OR) {
-        // simplify short circuit OR expressions
-        if (lhs instanceof JBooleanLiteral) {
-          // eg: if (true || isWhatever()) -> if (true)
-          // eg: if (false || isWhatever()) -> if (isWhatever())
-          JBooleanLiteral booleanLiteral = (JBooleanLiteral) lhs;
-          if (booleanLiteral.getValue()) {
-            ctx.replaceMe(lhs);
-          } else {
-            ctx.replaceMe(rhs);
+          break;
+        case ADD:
+          if (x.getType() == program.getTypeJavaLangString()) {
+            evalConcat(ctx, lhs, rhs);
           }
-
-        } else if (rhs instanceof JBooleanLiteral) {
-          // eg: if (isWhatever() || false) -> if (isWhatever())
-          // eg: if (isWhatever() && true) -> if (true), unless side effects
-          JBooleanLiteral booleanLiteral = (JBooleanLiteral) rhs;
-          if (!booleanLiteral.getValue()) {
-            ctx.replaceMe(lhs);
-          } else if (!lhs.hasSideEffects()) {
-            ctx.replaceMe(rhs);
+          break;
+        default:
+          if (op.isAssignment()) {
+            lvalues.remove(lhs);
           }
-        }
-      } else if (op == JBinaryOperator.EQ) {
-        // simplify: null == null -> true
-        if (lhs.getType() == program.getTypeNull()
-            && rhs.getType() == program.getTypeNull()) {
-          ctx.replaceMe(program.getLiteralBoolean(true));
-        }
-      } else if (op == JBinaryOperator.NEQ) {
-        // simplify: null != null -> false
-        if (lhs.getType() == program.getTypeNull()
-            && rhs.getType() == program.getTypeNull()) {
-          ctx.replaceMe(program.getLiteralBoolean(false));
-        }
-      } else if (op == JBinaryOperator.ADD
-          && x.getType() == program.getTypeJavaLangString()) {
-        // try to statically evaluate concatentation
-        if (lhs instanceof JValueLiteral && rhs instanceof JValueLiteral) {
-          Object lhsObj = ((JValueLiteral) lhs).getValueObj();
-          Object rhsObj = ((JValueLiteral) rhs).getValueObj();
-          ctx.replaceMe(program.getLiteralString(String.valueOf(lhsObj)
-              + String.valueOf(rhsObj)));
-        }
+          break;
       }
     }
 
@@ -205,6 +183,11 @@
     }
 
     @Override
+    public void endVisit(JClassType x, Context ctx) {
+      currentClass = null;
+    }
+
+    @Override
     public void endVisit(JConditional x, Context ctx) {
       JExpression condExpr = x.getIfTest();
       JExpression thenExpr = x.getThenExpr();
@@ -252,6 +235,11 @@
       }
     }
 
+    @Override
+    public void endVisit(JDeclarationStatement x, Context ctx) {
+      lvalues.remove(x.getVariableRef());
+    }
+
     /**
      * Convert do { } while (false); into a block.
      */
@@ -275,11 +263,44 @@
 
     @Override
     public void endVisit(JExpressionStatement x, Context ctx) {
+      ignoringExpressionOutput.remove(x.getExpr());
       if (!x.getExpr().hasSideEffects()) {
         removeMe(x, ctx);
       }
     }
 
+    @Override
+    public void endVisit(JFieldRef x, Context ctx) {
+      JLiteral literal = tryGetConstant(x);
+      if (literal == null && !ignoringExpressionOutput.contains(x)) {
+        return;
+      }
+      /*
+       * At this point, either we have a constant replacement, or our value is
+       * irrelevant. We can inline the constant, if any, but we might also need
+       * to evaluate an instance and run a clinit.
+       */
+      // We can inline the constant, but we might also need to evaluate an
+      // instance and run a clinit.
+      JMultiExpression multi = new JMultiExpression(program, x.getSourceInfo());
+
+      JExpression instance = x.getInstance();
+      if (instance != null) {
+        multi.exprs.add(instance);
+      }
+
+      JMethodCall clinit = maybeCreateClinitCall(x);
+      if (clinit != null) {
+        multi.exprs.add(clinit);
+      }
+
+      if (literal != null) {
+        multi.exprs.add(literal);
+      }
+
+      ctx.replaceMe(accept(multi));
+    }
+
     /**
      * Prune for (X; false; Y) statements, but make sure X is run.
      */
@@ -324,6 +345,15 @@
       }
     }
 
+    @Override
+    public void endVisit(JLocalRef x, Context ctx) {
+      JLiteral literal = tryGetConstant(x);
+      if (literal != null) {
+        assert (!x.hasSideEffects());
+        ctx.replaceMe(literal);
+      }
+    }
+
     /**
      * Resolve method calls that can be computed statically.
      */
@@ -346,6 +376,12 @@
      */
     @Override
     public void endVisit(JMultiExpression x, Context ctx) {
+      List<JExpression> exprs = x.exprs;
+      if (exprs.size() > 1) {
+        List<JExpression> nonFinalChildren = exprs.subList(0, exprs.size() - 1);
+        ignoringExpressionOutput.removeAll(nonFinalChildren);
+      }
+
       /*
        * If we're ignoring the output of this expression, we don't need to
        * maintain the final multi value.
@@ -365,22 +401,6 @@
           i--;
           continue;
         }
-
-        if (expr instanceof JFieldRef) {
-          JFieldRef fieldRef = (JFieldRef) expr;
-          JExpression instance = fieldRef.getInstance();
-          if (instance == null || !instance.hasSideEffects()) {
-            // If the instance doesn't have side-effects, but the field ref
-            // does, it's because a clinit() needs to happen.
-            JMethod clinit = fieldRef.getField().getEnclosingType().methods.get(0);
-            assert (clinit.getName().equals("$clinit"));
-
-            // Replace the field ref with a direct call to the clinit.
-            JMethodCall methodCall = new JMethodCall(program,
-                expr.getSourceInfo(), instance, clinit);
-            x.exprs.set(i, methodCall);
-          }
-        }
       }
 
       if (x.exprs.size() == 1) {
@@ -388,11 +408,38 @@
       }
     }
 
+    @Override
+    public void endVisit(JParameterRef x, Context ctx) {
+      JLiteral literal = tryGetConstant(x);
+      if (literal != null) {
+        assert (!x.hasSideEffects());
+        ctx.replaceMe(literal);
+      }
+    }
+
+    /**
+     * Replace post-inc/dec with pre-inc/dec if the result doesn't matter.
+     */
+    @Override
+    public void endVisit(JPostfixOperation x, Context ctx) {
+      if (x.getOp().isModifying()) {
+        lvalues.remove(x.getArg());
+      }
+      if (x == ignoringExpressionOutput) {
+        JPrefixOperation newOp = new JPrefixOperation(program,
+            x.getSourceInfo(), x.getOp(), x.getArg());
+        ctx.replaceMe(newOp);
+      }
+    }
+
     /**
      * Simplify the ! operator if possible.
      */
     @Override
     public void endVisit(JPrefixOperation x, Context ctx) {
+      if (x.getOp().isModifying()) {
+        lvalues.remove(x.getArg());
+      }
       if (x.getOp() == JUnaryOperator.NOT) {
         JExpression arg = x.getArg();
         if (arg instanceof JBooleanLiteral) {
@@ -516,8 +563,54 @@
     }
 
     @Override
+    public boolean visit(JBinaryOperation x, Context ctx) {
+      if (x.getOp().isAssignment()) {
+        lvalues.add(x.getLhs());
+      }
+      return true;
+    }
+
+    @Override
+    public boolean visit(JClassType x, Context ctx) {
+      currentClass = x;
+      return true;
+    }
+
+    @Override
+    public boolean visit(JDeclarationStatement x, Context ctx) {
+      lvalues.add(x.getVariableRef());
+      return true;
+    }
+
+    @Override
     public boolean visit(JExpressionStatement x, Context ctx) {
-      ignoringExpressionOutput = x.getExpr();
+      ignoringExpressionOutput.add(x.getExpr());
+      return true;
+    }
+
+    @Override
+    public boolean visit(JMultiExpression x, Context ctx) {
+      List<JExpression> exprs = x.exprs;
+      if (exprs.size() > 1) {
+        List<JExpression> nonFinalChildren = exprs.subList(0, exprs.size() - 1);
+        ignoringExpressionOutput.addAll(nonFinalChildren);
+      }
+      return true;
+    }
+
+    @Override
+    public boolean visit(JPostfixOperation x, Context ctx) {
+      if (x.getOp().isModifying()) {
+        lvalues.add(x.getArg());
+      }
+      return true;
+    }
+
+    @Override
+    public boolean visit(JPrefixOperation x, Context ctx) {
+      if (x.getOp().isModifying()) {
+        lvalues.add(x.getArg());
+      }
       return true;
     }
 
@@ -533,13 +626,25 @@
      */
     private boolean canPromoteBlock(JBlock block) {
       for (JStatement nestedStmt : block.statements) {
-        if (nestedStmt instanceof JLocalDeclarationStatement) {
-          return false;
+        if (nestedStmt instanceof JDeclarationStatement) {
+          JDeclarationStatement decl = (JDeclarationStatement) nestedStmt;
+          if (decl.getVariableRef() instanceof JLocalRef) {
+            return false;
+          }
         }
       }
       return true;
     }
 
+    private void evalConcat(Context ctx, JExpression lhs, JExpression rhs) {
+      if (lhs instanceof JValueLiteral && rhs instanceof JValueLiteral) {
+        Object lhsObj = ((JValueLiteral) lhs).getValueObj();
+        Object rhsObj = ((JValueLiteral) rhs).getValueObj();
+        ctx.replaceMe(program.getLiteralString(String.valueOf(lhsObj)
+            + String.valueOf(rhsObj)));
+      }
+    }
+
     private boolean hasNoDefaultCase(JSwitchStatement x) {
       JBlock body = x.getBody();
       boolean inDefault = false;
@@ -589,8 +694,24 @@
       return typeClassMap.get(type);
     }
 
+    private JMethodCall maybeCreateClinitCall(JFieldRef x) {
+      JMethodCall call;
+      JField field = x.getField();
+      if (field.isStatic()
+          && !field.isCompileTimeConstant()
+          && program.typeOracle.checkClinit(currentClass,
+              field.getEnclosingType())) {
+        JMethod clinit = field.getEnclosingType().methods.get(0);
+        assert (program.isClinit(clinit));
+        call = new JMethodCall(program, x.getSourceInfo(), null, clinit);
+      } else {
+        call = null;
+      }
+      return call;
+    }
+
     private int numRemovableExpressions(JMultiExpression x) {
-      if (x == ignoringExpressionOutput) {
+      if (ignoringExpressionOutput.contains(x)) {
         // All expressions can be removed.
         return x.exprs.size();
       } else {
@@ -663,6 +784,73 @@
     }
 
     /**
+     * Simplify short circuit AND expressions.
+     * 
+     * <pre>
+     * if (true || isWhatever()) -> if (true)
+     * if (false || isWhatever()) -> if (isWhatever())
+     * 
+     * if (isWhatever() && true) -> if (isWhatever())
+     * if (isWhatever() && false) -> if (false), unless side effects
+     * </pre>
+     */
+    private void shortCircuitAnd(JExpression lhs, JExpression rhs, Context ctx) {
+      if (lhs instanceof JBooleanLiteral) {
+        JBooleanLiteral booleanLiteral = (JBooleanLiteral) lhs;
+        if (booleanLiteral.getValue()) {
+          ctx.replaceMe(rhs);
+        } else {
+          ctx.replaceMe(lhs);
+        }
+
+      } else if (rhs instanceof JBooleanLiteral) {
+        JBooleanLiteral booleanLiteral = (JBooleanLiteral) rhs;
+        if (booleanLiteral.getValue()) {
+          ctx.replaceMe(lhs);
+        } else if (!lhs.hasSideEffects()) {
+          ctx.replaceMe(rhs);
+        }
+      }
+    }
+
+    /**
+     * Simplify short circuit OR expressions.
+     * 
+     * <pre>
+     * if (true || isWhatever()) -> if (true)
+     * if (false || isWhatever()) -> if (isWhatever())
+     * 
+     * if (isWhatever() || false) -> if (isWhatever())
+     * if (isWhatever() && true) -> if (true), unless side effects
+     * </pre>
+     */
+    private void shortCircuitOr(JExpression lhs, JExpression rhs, Context ctx) {
+      if (lhs instanceof JBooleanLiteral) {
+        JBooleanLiteral booleanLiteral = (JBooleanLiteral) lhs;
+        if (booleanLiteral.getValue()) {
+          ctx.replaceMe(lhs);
+        } else {
+          ctx.replaceMe(rhs);
+        }
+
+      } else if (rhs instanceof JBooleanLiteral) {
+        JBooleanLiteral booleanLiteral = (JBooleanLiteral) rhs;
+        if (!booleanLiteral.getValue()) {
+          ctx.replaceMe(lhs);
+        } else if (!lhs.hasSideEffects()) {
+          ctx.replaceMe(rhs);
+        }
+      }
+    }
+
+    private JLiteral tryGetConstant(JVariableRef x) {
+      if (!lvalues.contains(x)) {
+        return x.getTarget().getConstInitializer();
+      }
+      return null;
+    }
+
+    /**
      * Replace String methods having literal args with the static result.
      */
     private void tryOptimizeStringCall(JMethodCall x, Context ctx,
@@ -671,12 +859,12 @@
       if (method.getType() == program.getTypeVoid()) {
         return;
       }
-      
+
       if (method.getOriginalParamTypes().size() != method.params.size()) {
         // One or more parameters were pruned, abort.
         return;
       }
-      
+
       if (method.getName().endsWith("hashCode")) {
         // This cannot be computed at compile time because our implementation
         // differs from the JRE.
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/Finalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/Finalizer.java
new file mode 100644
index 0000000..322f3b5
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/Finalizer.java
@@ -0,0 +1,195 @@
+/*
+ * 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.CanBeSetFinal;
+import com.google.gwt.dev.jjs.ast.Context;
+import com.google.gwt.dev.jjs.ast.JBinaryOperation;
+import com.google.gwt.dev.jjs.ast.JClassType;
+import com.google.gwt.dev.jjs.ast.JDeclarationStatement;
+import com.google.gwt.dev.jjs.ast.JExpression;
+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.JMethodBody;
+import com.google.gwt.dev.jjs.ast.JParameter;
+import com.google.gwt.dev.jjs.ast.JPostfixOperation;
+import com.google.gwt.dev.jjs.ast.JPrefixOperation;
+import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JVariable;
+import com.google.gwt.dev.jjs.ast.JVariableRef;
+import com.google.gwt.dev.jjs.ast.JVisitor;
+import com.google.gwt.dev.jjs.ast.js.JsniFieldRef;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Finds all items are effectively final. That is, methods that are never
+ * overridden, classes that are never subclassed, and variables that are never
+ * reassigned. Mark all such methods and classes as final, since it helps us
+ * optimize.
+ */
+public class Finalizer {
+
+  /**
+   * Any items that weren't marked during MarkVisitor can be set final.
+   * 
+   * Open question: What does it mean if an interface/abstract method becomes
+   * final? Is this possible after Pruning? I guess it means that someone tried
+   * to make a call to method that wasn't actually implemented anywhere in the
+   * program. But if it wasn't implemented, then the enclosing class should have
+   * come up as not instantiated and been culled. So I think it's not possible.
+   */
+  private class FinalizeVisitor extends JVisitor {
+
+    private boolean didChange = false;
+
+    @Override
+    public boolean didChange() {
+      return didChange;
+    }
+
+    @Override
+    public void endVisit(JClassType x, Context ctx) {
+      if (!x.isFinal() && !isSubclassed.contains(x)) {
+        setFinal(x);
+      }
+    }
+
+    @Override
+    public void endVisit(JField x, Context ctx) {
+      maybeFinalize(x);
+    }
+
+    @Override
+    public void endVisit(JLocal x, Context ctx) {
+      maybeFinalize(x);
+    }
+
+    @Override
+    public void endVisit(JMethod x, Context ctx) {
+      if (!x.isFinal() && !isOverriden.contains(x)) {
+        setFinal(x);
+      }
+    }
+
+    @Override
+    public void endVisit(JParameter x, Context ctx) {
+      maybeFinalize(x);
+    }
+
+    @Override
+    public boolean visit(JMethodBody x, Context ctx) {
+      accept(x.locals);
+      return false;
+    }
+
+    private void maybeFinalize(JVariable x) {
+      if (!x.isFinal() && !isReassigned.contains(x)) {
+        setFinal(x);
+      }
+    }
+
+    private void setFinal(CanBeSetFinal x) {
+      x.setFinal();
+      if (x.isFinal()) {
+        didChange = true;
+      }
+    }
+  }
+  /**
+   * Find all items that ARE overriden/subclassed/reassigned.
+   */
+  private class MarkVisitor extends JVisitor {
+
+    @Override
+    public void endVisit(JBinaryOperation x, Context ctx) {
+      if (x.getOp().isAssignment()) {
+        recordAssignment(x.getLhs());
+      }
+    }
+
+    @Override
+    public void endVisit(JClassType x, Context ctx) {
+      if (x.extnds != null) {
+        isSubclassed.add(x.extnds);
+      }
+    }
+
+    @Override
+    public void endVisit(JDeclarationStatement x, Context ctx) {
+      // This is not a reassignment, the target may still be final.
+    }
+
+    @Override
+    public void endVisit(JMethod x, Context ctx) {
+      for (int i = 0; i < x.overrides.size(); ++i) {
+        JMethod it = x.overrides.get(i);
+        isOverriden.add(it);
+      }
+    }
+
+    @Override
+    public void endVisit(JPostfixOperation x, Context ctx) {
+      if (x.getOp().isModifying()) {
+        recordAssignment(x.getArg());
+      }
+    }
+
+    @Override
+    public void endVisit(JPrefixOperation x, Context ctx) {
+      if (x.getOp().isModifying()) {
+        recordAssignment(x.getArg());
+      }
+    }
+
+    @Override
+    public void endVisit(JsniFieldRef x, Context ctx) {
+      recordAssignment(x);
+    }
+
+    private void recordAssignment(JExpression lhs) {
+      if (lhs instanceof JVariableRef) {
+        JVariableRef variableRef = (JVariableRef) lhs;
+        isReassigned.add(variableRef.getTarget());
+      }
+    }
+  }
+
+  public static boolean exec(JProgram program) {
+    return new Finalizer().execImpl(program);
+  }
+
+  private final Set<JMethod> isOverriden = new HashSet<JMethod>();
+
+  private final Set<JVariable> isReassigned = new HashSet<JVariable>();
+
+  private final Set<JClassType> isSubclassed = new HashSet<JClassType>();
+
+  private Finalizer() {
+  }
+
+  private boolean execImpl(JProgram program) {
+    MarkVisitor marker = new MarkVisitor();
+    marker.accept(program);
+
+    FinalizeVisitor finalizer = new FinalizeVisitor();
+    finalizer.accept(program);
+    return finalizer.didChange();
+  }
+
+}
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 4687d70..fd392c4 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
@@ -52,7 +52,7 @@
 import com.google.gwt.dev.jjs.ast.JLabeledStatement;
 import com.google.gwt.dev.jjs.ast.JLiteral;
 import com.google.gwt.dev.jjs.ast.JLocal;
-import com.google.gwt.dev.jjs.ast.JLocalDeclarationStatement;
+import com.google.gwt.dev.jjs.ast.JDeclarationStatement;
 import com.google.gwt.dev.jjs.ast.JLocalRef;
 import com.google.gwt.dev.jjs.ast.JLongLiteral;
 import com.google.gwt.dev.jjs.ast.JMethod;
@@ -322,8 +322,8 @@
          * 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) {
+        if (field.isCompileTimeConstant()) {
+          JLiteral initializer = field.getConstInitializer();
           JType type = initializer.getType();
           if (type instanceof JPrimitiveType
               || type == program.getTypeJavaLangString()) {
@@ -1081,10 +1081,6 @@
       return processBinaryOperation(info, op, type, x.left, x.right);
     }
 
-    JExpression processExpression(CombinedBinaryExpression x) {
-      return processExpression((BinaryExpression) x);
-    }
-
     JExpression processExpression(CastExpression x) {
       SourceInfo info = makeSourceInfo(x);
       JType type = (JType) typeMap.get(x.resolvedType);
@@ -1098,6 +1094,10 @@
       return program.getLiteralClass(type);
     }
 
+    JExpression processExpression(CombinedBinaryExpression x) {
+      return processExpression((BinaryExpression) x);
+    }
+
     JExpression processExpression(CompoundAssignment x) {
       JBinaryOperator op;
 
@@ -1542,15 +1542,13 @@
           assert (initializer instanceof JMethodCall);
         }
 
-        if (initializer instanceof JLiteral) {
-          field.constInitializer = (JLiteral) initializer;
-        } else if (initializer != null) {
+        if (initializer != null) {
           SourceInfo info = makeSourceInfo(declaration);
-          JStatement assignStmt = program.createAssignmentStmt(info,
+          JStatement decl = new JDeclarationStatement(program, info,
               createVariableRef(info, field), initializer);
 
           // will either be init or clinit
-          currentMethodBody.getStatements().add(assignStmt);
+          currentMethodBody.getStatements().add(decl);
         }
       } catch (Throwable e) {
         throw translateException(field, e);
@@ -1693,7 +1691,7 @@
       JLocal elementVar = (JLocal) typeMap.get(x.elementVariable.binding);
       String elementVarName = elementVar.getName();
 
-      JLocalDeclarationStatement elementDecl = (JLocalDeclarationStatement) processStatement(x.elementVariable);
+      JDeclarationStatement elementDecl = (JDeclarationStatement) processStatement(x.elementVariable);
       assert (elementDecl.initializer == null);
 
       JForStatement result;
@@ -1716,14 +1714,14 @@
 
         List<JStatement> initializers = new ArrayList<JStatement>(3);
         // T[] i$array = arr
-        initializers.add(createLocalDeclaration(info, arrayVar,
+        initializers.add(createDeclaration(info, arrayVar,
             dispProcessExpression(x.collection)));
         // int i$index = 0
-        initializers.add(createLocalDeclaration(info, indexVar,
+        initializers.add(createDeclaration(info, indexVar,
             program.getLiteralInt(0)));
         // int i$max = i$array.length
-        initializers.add(createLocalDeclaration(info, maxVar, new JFieldRef(
-            program, info, createVariableRef(info, arrayVar),
+        initializers.add(createDeclaration(info, maxVar, new JFieldRef(program,
+            info, createVariableRef(info, arrayVar),
             program.getIndexedField("Array.length"), currentClass)));
 
         // i$index < i$max
@@ -1759,9 +1757,9 @@
 
         List<JStatement> initializers = new ArrayList<JStatement>(1);
         // Iterator<T> i$iterator = collection.iterator()
-        initializers.add(createLocalDeclaration(info, iteratorVar,
-            new JMethodCall(program, info, dispProcessExpression(x.collection),
-                program.getIndexedMethod("Iterable.iterator"))));
+        initializers.add(createDeclaration(info, iteratorVar, new JMethodCall(
+            program, info, dispProcessExpression(x.collection),
+            program.getIndexedMethod("Iterable.iterator"))));
 
         // i$iterator.hasNext()
         JExpression condition = new JMethodCall(program, info,
@@ -1837,8 +1835,7 @@
       JLocal local = (JLocal) typeMap.get(x.binding);
       JLocalRef localRef = new JLocalRef(program, info, local);
       JExpression initializer = dispProcessExpression(x.initialization);
-      return new JLocalDeclarationStatement(program, info, localRef,
-          initializer);
+      return new JDeclarationStatement(program, info, localRef, initializer);
     }
 
     JStatement processStatement(ReturnStatement x) {
@@ -2154,6 +2151,12 @@
       return box(toBox, wrapperType);
     }
 
+    private JDeclarationStatement createDeclaration(SourceInfo info,
+        JLocal local, JExpression value) {
+      return new JDeclarationStatement(program, info, new JLocalRef(program,
+          info, local), value);
+    }
+
     private JField createEnumValueMap(JEnumType type) {
       JsonObject map = new JsonObject(program);
       for (JEnumField field : type.enumList) {
@@ -2163,7 +2166,7 @@
         map.propInits.add(new JsonObject.JsonPropInit(program, key, value));
       }
       JField mapField = program.createField(null, "enum$map".toCharArray(),
-          type, map.getType(), true, true, true);
+          type, map.getType(), true, true, false);
 
       // Initialize in clinit.
       JMethodBody clinitBody = (JMethodBody) type.methods.get(0).getBody();
@@ -2173,12 +2176,6 @@
       return mapField;
     }
 
-    private JLocalDeclarationStatement createLocalDeclaration(SourceInfo info,
-        JLocal arrayVar, JExpression value) {
-      return new JLocalDeclarationStatement(program, info, new JLocalRef(
-          program, info, arrayVar), value);
-    }
-
     /**
      * Helper to create a qualified "this" ref (really a synthetic this field
      * access) of the appropriate type. Always use this method instead of
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 e7baca2..05cdad3 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
@@ -46,8 +46,9 @@
 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.JDeclarationStatement;
 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;
@@ -68,6 +69,7 @@
 import com.google.gwt.dev.jjs.ast.JType;
 import com.google.gwt.dev.jjs.ast.JTypeOracle;
 import com.google.gwt.dev.jjs.ast.JUnaryOperator;
+import com.google.gwt.dev.jjs.ast.JVariable;
 import com.google.gwt.dev.jjs.ast.JVisitor;
 import com.google.gwt.dev.jjs.ast.JWhileStatement;
 import com.google.gwt.dev.jjs.ast.js.JClassSeed;
@@ -135,6 +137,7 @@
 import java.util.Set;
 import java.util.Stack;
 import java.util.TreeMap;
+import java.util.Map.Entry;
 
 /**
  * Creates a JavaScript AST from a <code>JProgram</code> node.
@@ -361,14 +364,14 @@
 
     private final Set<JClassType> alreadyRan = new HashSet<JClassType>();
 
+    private Map<JClassType, JsFunction> clinitMap = new HashMap<JClassType, JsFunction>();
+
     private JMethod currentMethod = null;
 
     private final JsName globalTemp = topScope.declareName("_");
 
     private final JsName prototype = objectScope.declareName("prototype");
 
-    private Map<JClassType, JsFunction> clinitMap = new HashMap<JClassType, JsFunction>();
-
     {
       globalTemp.setObfuscatable(false);
       prototype.setObfuscatable(false);
@@ -477,6 +480,7 @@
       push(names.get(x.getRefType()).makeRef());
     }
 
+    @SuppressWarnings("unchecked")
     @Override
     public void endVisit(JClassType x, Context ctx) {
       if (alreadyRan.contains(x)) {
@@ -547,6 +551,34 @@
     }
 
     @Override
+    public void endVisit(JDeclarationStatement x, Context ctx) {
+      if (x.getInitializer() == null) {
+        pop(); // variableRef
+        /*
+         * Declaration statements can only appear in blocks, so it's okay to
+         * push null instead of an empty statement
+         */
+        push(null);
+        return;
+      }
+
+      JsExpression initializer = (JsExpression) pop(); // initializer
+      JsNameRef localRef = (JsNameRef) pop(); // localRef
+
+      JVariable target = x.getVariableRef().getTarget();
+      if (target instanceof JField && target.getConstInitializer() != null) {
+        // Will initialize at top scope; no need to double-initialize.
+        push(null);
+        return;
+      }
+
+      JsBinaryOperation binOp = new JsBinaryOperation(JsBinaryOperator.ASG,
+          localRef, initializer);
+
+      push(binOp.makeStmt());
+    }
+
+    @Override
     public void endVisit(JDoStatement x, Context ctx) {
       JsDoWhile stmt = new JsDoWhile();
       if (x.getBody() != null) {
@@ -567,13 +599,14 @@
     @Override
     public void endVisit(JField x, Context ctx) {
       // if we need an initial value, create an assignment
-      if (x.constInitializer != null) {
+      if (x.getConstInitializer() != null) {
         // setup the constant value
-        accept(x.constInitializer);
+        accept(x.getConstInitializer());
       } else if (x == program.getIndexedField("Cast.typeIdArray")) {
         // magic: setup the type id table
         push(generateTypeTable());
-      } else if (!x.hasInitializer()) {
+      } else if (!x.hasInitializer()
+          && x.getEnclosingType() != program.getTypeJavaLangObject()) {
         // setup a default value
         accept(x.getType().getDefaultValue());
       } else {
@@ -739,30 +772,27 @@
     }
 
     @Override
-    public void endVisit(JLocalDeclarationStatement x, Context ctx) {
-
-      if (x.getInitializer() == null) {
-        pop(); // localRef
-        /*
-         * local decls can only appear in blocks, so it's okay to push null
-         * instead of an empty statement
-         */
-        push(null);
-        return;
-      }
-
-      JsExpression initializer = (JsExpression) pop(); // initializer
-      JsNameRef localRef = (JsNameRef) pop(); // localRef
-
-      JsBinaryOperation binOp = new JsBinaryOperation(JsBinaryOperator.ASG,
-          localRef, initializer);
-
-      push(binOp.makeStmt());
+    public void endVisit(JLocalRef x, Context ctx) {
+      push(names.get(x.getTarget()).makeRef());
     }
 
     @Override
-    public void endVisit(JLocalRef x, Context ctx) {
-      push(names.get(x.getTarget()).makeRef());
+    public void endVisit(JLongLiteral x, Context ctx) {
+      super.endVisit(x, ctx);
+      JsExpression longLiteralAllocation = pop();
+
+      // My seed function name
+      String nameString = Long.toString(x.getValue(), 16);
+      if (nameString.charAt(0) == '-') {
+        nameString = "N" + nameString.substring(1);
+      } else {
+        nameString = "P" + nameString;
+      }
+      nameString += "_longLit";
+      JsName longLit = topScope.declareName(nameString);
+      longLits.put(x.getValue(), longLit);
+      longObjects.put(longLit, longLiteralAllocation);
+      push(longLit.makeRef());
     }
 
     @Override
@@ -946,6 +976,12 @@
       JsVars vars = new JsVars();
       vars.add(new JsVar(globalTemp));
       globalStmts.add(0, vars);
+      
+      /*
+       * Long lits must got at the top, they can serve as constant field
+       * initializers.
+       */
+      generateLongLiterals(vars);
 
       // Generate class objects.
       vars = new JsVars();
@@ -1141,7 +1177,7 @@
             assert (curStatements != null);
             JsStatement newStmt = (JsStatement) pop(); // stmt
             if (newStmt != null) {
-              // Empty JLocalDeclarationStatement produces a null
+              // Empty JDeclarationStatement produces a null
               curStatements.add(newStmt);
             }
           }
@@ -1168,8 +1204,7 @@
 
     private void generateClassLiterals(JsVars vars) {
       Set<JType> alreadyGenerated = new HashSet<JType>();
-      for (Object element : classLits.keySet()) {
-        JType type = (JType) element;
+      for (JType type : classLits.keySet()) {
         generateClassLiteralsRecursive(alreadyGenerated, type, vars);
       }
     }
@@ -1212,7 +1247,7 @@
         // special: setup a "toString" alias for java.lang.Object.toString()
         generateToStringAlias(x, globalStmts);
         // special: setup the identifying typeMarker field
-        generateTypeMarker(x, globalStmts);
+        generateTypeMarker(globalStmts);
       }
 
       generateTypeId(x, globalStmts);
@@ -1285,6 +1320,16 @@
       errCall.getArguments().add(modName.makeRef());
     }
 
+    private void generateLongLiterals(JsVars vars) {
+      for (Entry<Long, JsName> entry : longLits.entrySet()) {
+        JsName jsName = entry.getValue();
+        JsExpression longObjectAlloc = longObjects.get(jsName);
+        JsVar var = new JsVar(jsName);
+        var.setInitExpr(longObjectAlloc);
+        vars.add(var);
+      }
+    }
+
     private void generateNullFunc(List<JsStatement> globalStatements) {
       // handle null method
       JsFunction nullFunc = new JsFunction(topScope, nullMethodName);
@@ -1378,7 +1423,7 @@
       }
     }
 
-    private void generateTypeMarker(JClassType x, List<JsStatement> globalStmts) {
+    private void generateTypeMarker(List<JsStatement> globalStmts) {
       JField typeMarkerField = program.getIndexedField("Object.typeMarker");
       JsName typeMarkerName = names.get(typeMarkerField);
       if (typeMarkerName == null) {
@@ -1605,7 +1650,6 @@
    * clinit).
    */
   private Set<JMethod> crossClassTargets = new HashSet<JMethod>();
-
   /**
    * Contains JsNames for all interface methods. A special scope is needed so
    * that independent classes will obfuscate their interface implementation
@@ -1614,6 +1658,13 @@
   private final JsScope interfaceScope;
 
   private final JsProgram jsProgram;
+
+  /**
+   * Sorted to avoid nondeterministic iteration.
+   */
+  private final Map<Long, JsName> longLits = new TreeMap<Long, JsName>();
+
+  private final Map<JsName, JsExpression> longObjects = new IdentityHashMap<JsName, JsExpression>();
   private final Map<JAbstractMethodBody, JsFunction> methodBodyMap = new IdentityHashMap<JAbstractMethodBody, JsFunction>();
   private final Map<HasName, JsName> names = new IdentityHashMap<HasName, JsName>();
   private JsName nullMethodName;
@@ -1623,7 +1674,9 @@
    * and toString. All other class scopes have this scope as an ultimate parent.
    */
   private final JsScope objectScope;
+  private final JsOutputOption output;
   private final Map<JMethod, JsName> polymorphicNames = new IdentityHashMap<JMethod, JsName>();
+
   private final JProgram program;
 
   /**
@@ -1646,9 +1699,7 @@
    * Contains JsNames for all globals, such as static fields and methods.
    */
   private final JsScope topScope;
-
   private final JTypeOracle typeOracle;
-  private final JsOutputOption output;
 
   private GenerateJavaScriptAST(JProgram program, JsProgram jsProgram,
       JsOutputOption output) {
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
index a8cda95..07137c0 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptLiterals.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptLiterals.java
@@ -25,8 +25,10 @@
 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.JsArrayLiteral;
 import com.google.gwt.dev.js.ast.JsProgram;
 import com.google.gwt.dev.js.ast.JsVisitable;
+import com.google.gwt.lang.LongLib;
 
 import java.math.BigInteger;
 import java.util.ArrayList;
@@ -39,6 +41,10 @@
  */
 public class GenerateJavaScriptLiterals extends JVisitor {
 
+  static {
+    LongLib.RUN_IN_JVM = true;
+  }
+
   private final JsProgram program;
   private final Stack<JsVisitable<?>> nodeStack = new Stack<JsVisitable<?>>();
 
@@ -72,8 +78,14 @@
   }
 
   @Override
-  public final void endVisit(JLongLiteral x, Context ctx) {
-    push(program.getIntegralLiteral(BigInteger.valueOf(x.getValue())));
+  public void endVisit(JLongLiteral x, Context ctx) {
+    JsArrayLiteral arrayLit = new JsArrayLiteral();
+    double[] doubleArray = LongLib.typeChange(x.getValue());
+    arrayLit.getExpressions().add(
+        program.getDecimalLiteral(String.valueOf(doubleArray[0])));
+    arrayLit.getExpressions().add(
+        program.getDecimalLiteral(String.valueOf(doubleArray[1])));
+    push(arrayLit);
   }
 
   @Override
@@ -86,14 +98,17 @@
     push(program.getStringLiteral(x.getValue()));
   }
 
+  @SuppressWarnings("unchecked")
   public final <T extends JsVisitable> T peek() {
     return (T) nodeStack.peek();
   }
 
+  @SuppressWarnings("unchecked")
   protected final <T extends JsVisitable> T pop() {
     return (T) nodeStack.pop();
   }
 
+  @SuppressWarnings("unchecked")
   protected final <T extends JsVisitable> List<T> popList(int count) {
     List<T> list = new ArrayList<T>();
     while (count > 0) {
@@ -107,6 +122,7 @@
     return list;
   }
 
+  @SuppressWarnings("unchecked")
   protected final <T extends JsVisitable> void popList(List<T> collection,
       int count) {
     List<T> list = new ArrayList<T>();
@@ -121,6 +137,7 @@
     collection.addAll(list);
   }
 
+  @SuppressWarnings("unchecked")
   protected final <T extends JsVisitable> void push(T node) {
     nodeStack.push(node);
   }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/LongCastNormalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/LongCastNormalizer.java
new file mode 100644
index 0000000..b82b4bb
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/LongCastNormalizer.java
@@ -0,0 +1,217 @@
+/*

+ * Copyright 2008 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.JBinaryOperation;

+import com.google.gwt.dev.jjs.ast.JCastOperation;

+import com.google.gwt.dev.jjs.ast.JConditional;

+import com.google.gwt.dev.jjs.ast.JExpression;

+import com.google.gwt.dev.jjs.ast.JDeclarationStatement;

+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.JNewArray;

+import com.google.gwt.dev.jjs.ast.JParameter;

+import com.google.gwt.dev.jjs.ast.JPrimitiveType;

+import com.google.gwt.dev.jjs.ast.JProgram;

+import com.google.gwt.dev.jjs.ast.JReturnStatement;

+import com.google.gwt.dev.jjs.ast.JType;

+

+import java.util.List;

+

+/**

+ * Synthesize explicit casts to and from the primitive long type where such a

+ * cast would have been implicit. The explicit casts serve as markers for

+ * {@link CastNormalizer}.

+ */

+public class LongCastNormalizer {

+

+  /**

+   * Synthesize casts to longs and from long to trigger conversions.

+   */

+  private class ImplicitCastVisitor extends JModVisitor {

+

+    private JMethod currentMethod;

+    private final JPrimitiveType longType;

+

+    public ImplicitCastVisitor(JPrimitiveType longType) {

+      this.longType = longType;

+    }

+

+    @Override

+    public void endVisit(JBinaryOperation x, Context ctx) {

+      JType lhsType = x.getLhs().getType();

+      JType rhsType = x.getRhs().getType();

+      JType resultType = x.getType();

+

+      if (resultType == program.getTypeJavaLangString()) {

+        // Don't mess with concat.

+        return;

+      }

+

+      // Special case: shift operators always coerce a long RHS to int.

+      if (x.getOp().isShiftOperator()) {

+        if (rhsType == longType) {

+          rhsType = program.getTypePrimitiveInt();

+        }

+      } else if (lhsType == longType || rhsType == longType) {

+        // We must coerce lhs and rhs to the same type, either long or a float.

+

+        // Assume a long type.

+        JType coerceTo = longType;

+

+        // But double / float takes precedence over long.

+        JPrimitiveType floatType = program.getTypePrimitiveFloat();

+        JPrimitiveType doubleType = program.getTypePrimitiveDouble();

+        // See if the lhs can coerce the rhs

+        if ((lhsType == floatType || lhsType == doubleType)) {

+          coerceTo = lhsType;

+        }

+        if (x.getOp().isAssignment()) {

+          // In an assignment, the lhs must coerce the rhs

+          coerceTo = lhsType;

+        } else if ((rhsType == floatType || rhsType == doubleType)) {

+          coerceTo = rhsType;

+        }

+        lhsType = rhsType = coerceTo;

+      }

+

+      JExpression newLhs = checkAndReplace(x.getLhs(), lhsType);

+      JExpression newRhs = checkAndReplace(x.getRhs(), rhsType);

+      if (newLhs != x.getLhs() || newRhs != x.getRhs()) {

+        JBinaryOperation binOp = new JBinaryOperation(program,

+            x.getSourceInfo(), resultType, x.getOp(), newLhs, newRhs);

+        ctx.replaceMe(binOp);

+      }

+    }

+

+    @Override

+    public void endVisit(JConditional x, Context ctx) {

+      JExpression newThen = checkAndReplace(x.getThenExpr(), x.getType());

+      JExpression newElse = checkAndReplace(x.getElseExpr(), x.getType());

+      if (newThen != x.getThenExpr() || newElse != x.getElseExpr()) {

+        JConditional newCond = new JConditional(program, x.getSourceInfo(),

+            x.getType(), x.getIfTest(), newThen, newElse);

+        ctx.replaceMe(newCond);

+      }

+    }

+

+    @Override

+    public void endVisit(JDeclarationStatement x, Context ctx) {

+      JExpression init = x.getInitializer();

+      if (init != null) {

+        init = checkAndReplace(init, x.getVariableRef().getType());

+        if (init != x.getInitializer()) {

+          JDeclarationStatement newStmt = new JDeclarationStatement(program,

+              x.getSourceInfo(), x.getVariableRef(), init);

+          ctx.replaceMe(newStmt);

+        }

+      }

+    }

+

+    @Override

+    public void endVisit(JMethod x, Context ctx) {

+      currentMethod = null;

+    }

+

+    @Override

+    public void endVisit(JMethodCall x, Context ctx) {

+      List<JParameter> params = x.getTarget().params;

+      List<JExpression> args = x.getArgs();

+      for (int i = 0; i < params.size(); ++i) {

+        JParameter param = params.get(i);

+        JExpression arg = args.get(i);

+        JExpression newArg = checkAndReplace(arg, param.getType());

+        if (arg != newArg) {

+          args.set(i, newArg);

+          this.didChange = true;

+        }

+      }

+    }

+

+    @Override

+    public void endVisit(JNewArray x, Context ctx) {

+      JType elementType = x.getArrayType().getElementType();

+      List<JExpression> initializers = x.initializers;

+      if (initializers != null) {

+        for (int i = 0; i < initializers.size(); ++i) {

+          JExpression initializer = initializers.get(i);

+          JExpression newInitializer = checkAndReplace(initializer, elementType);

+          if (initializer != newInitializer) {

+            initializers.set(i, newInitializer);

+            this.didChange = true;

+          }

+        }

+      }

+    }

+

+    @Override

+    public void endVisit(JReturnStatement x, Context ctx) {

+      JExpression expr = x.getExpr();

+      if (expr != null) {

+        JExpression newExpr = checkAndReplace(expr, currentMethod.getType());

+        if (expr != newExpr) {

+          JReturnStatement newStmt = new JReturnStatement(program,

+              x.getSourceInfo(), newExpr);

+          ctx.replaceMe(newStmt);

+        }

+      }

+    }

+

+    @Override

+    public boolean visit(JMethod x, Context ctx) {

+      currentMethod = x;

+      return true;

+    }

+

+    /**

+     * Returns an explicit cast if the target type is long and the input

+     * expression is not a long, or if the target type is floating point and the

+     * expression is a long.

+     */

+    private JExpression checkAndReplace(JExpression arg, JType targetType) {

+      JType argType = arg.getType();

+      if (targetType == argType) {

+        return arg;

+      }

+      if (targetType != longType && argType != longType) {

+        return arg;

+      }

+      // Synthesize a cast to long to force explicit conversion.

+      JCastOperation cast = new JCastOperation(program, arg.getSourceInfo(),

+          targetType, arg);

+      return cast;

+    }

+  }

+

+  public static void exec(JProgram program) {

+    new LongCastNormalizer(program).execImpl();

+  }

+

+  private final JProgram program;

+

+  private LongCastNormalizer(JProgram program) {

+    this.program = program;

+  }

+

+  private void execImpl() {

+    ImplicitCastVisitor visitor = new ImplicitCastVisitor(

+        program.getTypePrimitiveLong());

+    visitor.accept(program);

+  }

+

+}

diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/LongEmulationNormalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/LongEmulationNormalizer.java
new file mode 100644
index 0000000..bdc3eb8
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/LongEmulationNormalizer.java
@@ -0,0 +1,210 @@
+/*

+ * Copyright 2008 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.InternalCompilerException;

+import com.google.gwt.dev.jjs.ast.Context;

+import com.google.gwt.dev.jjs.ast.JBinaryOperation;

+import com.google.gwt.dev.jjs.ast.JBinaryOperator;

+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.JPostfixOperation;

+import com.google.gwt.dev.jjs.ast.JPrefixOperation;

+import com.google.gwt.dev.jjs.ast.JPrimitiveType;

+import com.google.gwt.dev.jjs.ast.JProgram;

+import com.google.gwt.dev.jjs.ast.JType;

+import com.google.gwt.dev.jjs.ast.JUnaryOperator;

+

+/**

+ * Replaces long operations with calls to the emulation library. Depends on

+ * {@link LongCastNormalizer} and {@link CompoundAssignmentNormalizer} having

+ * been run.

+ */

+public class LongEmulationNormalizer {

+

+  /**

+   * Replace all long math with calls into the long emulation library.

+   */

+  private class LongOpVisitor extends JModVisitor {

+

+    private final JPrimitiveType longType;

+

+    public LongOpVisitor(JPrimitiveType longType) {

+      this.longType = longType;

+    }

+

+    @Override

+    public void endVisit(JBinaryOperation x, Context ctx) {

+      JType lhsType = x.getLhs().getType();

+      JType rhsType = x.getRhs().getType();

+      if (lhsType != longType) {

+        return;

+      }

+

+      String methodName = getEmulationMethod(x.getOp());

+      if (methodName == null) {

+        return;

+      }

+

+      // Check operand types.

+      switch (x.getOp()) {

+        case SHL:

+        case SHR:

+        case SHRU:

+          if (rhsType != program.getTypePrimitiveInt()) {

+            throw new InternalCompilerException(

+                "Expected right operand to be of type int");

+          }

+          break;

+        default:

+          if (rhsType != longType) {

+            throw new InternalCompilerException(

+                "Expected right operand to be of type long");

+          }

+      }

+

+      JMethod method = program.getIndexedMethod("LongLib." + methodName);

+      JMethodCall call = new JMethodCall(program, x.getSourceInfo(), null,

+          method, x.getType());

+      call.getArgs().add(x.getLhs());

+      call.getArgs().add(x.getRhs());

+      ctx.replaceMe(call);

+    }

+

+    @Override

+    public void endVisit(JPostfixOperation x, Context ctx) {

+      JType argType = x.getArg().getType();

+      if (argType == longType) {

+        throw new InternalCompilerException(

+            "Postfix operations on longs should not reach here");

+      }

+    }

+

+    @Override

+    public void endVisit(JPrefixOperation x, Context ctx) {

+      JType argType = x.getArg().getType();

+      if (argType != longType) {

+        return;

+      }

+

+      String methodName = getEmulationMethod(x.getOp());

+      JMethod method = program.getIndexedMethod("LongLib." + methodName);

+      JMethodCall call = new JMethodCall(program, x.getSourceInfo(), null,

+          method, x.getType());

+      call.getArgs().add(x.getArg());

+      ctx.replaceMe(call);

+    }

+

+    private String getEmulationMethod(JBinaryOperator op) {

+      switch (op) {

+        case MUL:

+          return "mul";

+        case DIV:

+          return "div";

+        case MOD:

+          return "mod";

+        case ADD:

+          return "add";

+        case SUB:

+          return "sub";

+        case SHL:

+          return "shl";

+        case SHR:

+          return "shr";

+        case SHRU:

+          return "shru";

+        case LT:

+          return "lt";

+        case LTE:

+          return "lte";

+        case GT:

+          return "gt";

+        case GTE:

+          return "gte";

+        case EQ:

+          return "eq";

+        case NEQ:

+          return "neq";

+        case BIT_AND:

+          return "and";

+        case BIT_XOR:

+          return "xor";

+        case BIT_OR:

+          return "or";

+

+        case AND:

+        case OR:

+          throw new InternalCompilerException(

+              "AND and OR sould not have long operands");

+

+        case ASG:

+          // Nothing to do.

+          return null;

+

+        case ASG_ADD:

+        case ASG_SUB:

+        case ASG_MUL:

+        case ASG_DIV:

+        case ASG_MOD:

+        case ASG_SHL:

+        case ASG_SHR:

+        case ASG_SHRU:

+        case ASG_BIT_AND:

+        case ASG_BIT_OR:

+        case ASG_BIT_XOR:

+          throw new InternalCompilerException(

+              "Modifying long ops should not reach here");

+        default:

+          throw new InternalCompilerException("Should not reach here");

+      }

+    }

+

+    private String getEmulationMethod(JUnaryOperator op) {

+      switch (op) {

+        case INC:

+        case DEC:

+          throw new InternalCompilerException(

+              "Modifying long ops should not reach here");

+        case NEG:

+          return "neg";

+        case NOT:

+          throw new InternalCompilerException(

+              "NOT sould not have a long operand");

+        case BIT_NOT:

+          return "not";

+        default:

+          throw new InternalCompilerException("Should not reach here");

+      }

+    }

+  }

+

+  public static void exec(JProgram program) {

+    new LongEmulationNormalizer(program).execImpl();

+  }

+

+  private final JProgram program;

+

+  private LongEmulationNormalizer(JProgram program) {

+    this.program = program;

+  }

+

+  private void execImpl() {

+    LongOpVisitor visitor = new LongOpVisitor(program.getTypePrimitiveLong());

+    visitor.accept(program);

+  }

+

+}

diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/MethodAndClassFinalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/MethodAndClassFinalizer.java
deleted file mode 100644
index 5367faa..0000000
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/MethodAndClassFinalizer.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * 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.JClassType;
-import com.google.gwt.dev.jjs.ast.JInterfaceType;
-import com.google.gwt.dev.jjs.ast.JMethod;
-import com.google.gwt.dev.jjs.ast.JProgram;
-import com.google.gwt.dev.jjs.ast.JReferenceType;
-import com.google.gwt.dev.jjs.ast.JVisitor;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Finds all methods and classes are effectively final. That is, methods that
- * are never overridden and classes that are never subclassed. Mark all such
- * methods and classes as final, since it helps us optimize.
- */
-public class MethodAndClassFinalizer {
-
-  /**
-   * Any method and classes that weren't marked during MarkVisitor can be set
-   * final.
-   * 
-   * Open question: What does it mean if an interface/abstract method becomes
-   * final? Is this possible after Pruning? I guess it means that someone tried
-   * to make a call to method that wasn't actually implemented anywhere in the
-   * program. But if it wasn't implemented, then the enclosing class should have
-   * come up as not instantiated and been culled. So I think it's not possible.
-   */
-  private class FinalizeVisitor extends JVisitor {
-
-    private boolean didChange = false;
-
-    @Override
-    public boolean didChange() {
-      return didChange;
-    }
-
-    // @Override
-    @Override
-    public boolean visit(JClassType x, Context ctx) {
-      if (!x.isFinal() && !isSubclassed.contains(x)) {
-        x.setFinal(true);
-        didChange = true;
-      }
-      for (int i = 0; i < x.methods.size(); ++i) {
-        JMethod method = x.methods.get(i);
-        accept(method);
-      }
-      return false;
-    }
-
-    // @Override
-    @Override
-    public boolean visit(JInterfaceType x, Context ctx) {
-      for (int i = 0; i < x.methods.size(); ++i) {
-        JMethod method = x.methods.get(i);
-        accept(method);
-      }
-      return false;
-    }
-
-    // @Override
-    @Override
-    public boolean visit(JMethod x, Context ctx) {
-      if (!x.isFinal() && !isOverriden.contains(x)) {
-        x.setFinal(true);
-        didChange = true;
-      }
-      return false;
-    }
-  }
-  /**
-   * Find all methods and classes that ARE overriden/subclassed.
-   */
-  private class MarkVisitor extends JVisitor {
-
-    // @Override
-    @Override
-    public boolean visit(JClassType x, Context ctx) {
-      if (x.extnds != null) {
-        isSubclassed.add(x.extnds);
-      }
-
-      for (int i = 0; i < x.methods.size(); ++i) {
-        JMethod method = x.methods.get(i);
-        accept(method);
-      }
-      return false;
-    }
-
-    // @Override
-    @Override
-    public boolean visit(JInterfaceType x, Context ctx) {
-      for (int i = 0; i < x.methods.size(); ++i) {
-        JMethod method = x.methods.get(i);
-        accept(method);
-      }
-      return false;
-    }
-
-    // @Override
-    @Override
-    public boolean visit(JMethod x, Context ctx) {
-      for (int i = 0; i < x.overrides.size(); ++i) {
-        JMethod it = x.overrides.get(i);
-        isOverriden.add(it);
-      }
-      return false;
-    }
-
-    // @Override
-    @Override
-    public boolean visit(JProgram x, Context ctx) {
-      for (int i = 0; i < x.getDeclaredTypes().size(); ++i) {
-        JReferenceType type = x.getDeclaredTypes().get(i);
-        accept(type);
-      }
-      return false;
-    }
-  }
-
-  public static boolean exec(JProgram program) {
-    return new MethodAndClassFinalizer().execImpl(program);
-  }
-
-  private final Set<JMethod> isOverriden = new HashSet<JMethod>();
-
-  private final Set<JClassType> isSubclassed = new HashSet<JClassType>();
-
-  private MethodAndClassFinalizer() {
-  }
-
-  private boolean execImpl(JProgram program) {
-    MarkVisitor marker = new MarkVisitor();
-    marker.accept(program);
-
-    FinalizeVisitor finalizer = new FinalizeVisitor();
-    finalizer.accept(program);
-    return finalizer.didChange();
-  }
-
-}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/MethodInliner.java b/dev/core/src/com/google/gwt/dev/jjs/impl/MethodInliner.java
index b09f80c..eaab6ad 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/MethodInliner.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/MethodInliner.java
@@ -17,6 +17,7 @@
 
 import com.google.gwt.dev.jjs.InternalCompilerException;
 import com.google.gwt.dev.jjs.ast.Context;
+import com.google.gwt.dev.jjs.ast.JCastOperation;
 import com.google.gwt.dev.jjs.ast.JExpression;
 import com.google.gwt.dev.jjs.ast.JExpressionStatement;
 import com.google.gwt.dev.jjs.ast.JLocalRef;
@@ -452,6 +453,15 @@
       CloneExpressionVisitor cloner = new CloneExpressionVisitor(program);
       JExpression arg = methodCall.getArgs().get(paramIndex);
       JExpression clone = cloner.cloneExpression(arg);
+
+      /*
+       * Insert an implicit cast if the types differ; it might get optimized out
+       * later, but in some cases it will force correct math evaluation.
+       */
+      if (clone.getType() != x.getType()) {
+        clone = new JCastOperation(program, clone.getSourceInfo(), x.getType(),
+            clone);
+      }
       ctx.replaceMe(clone);
     }
   }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java b/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
index 186890f..7185243 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
@@ -22,15 +22,14 @@
 import com.google.gwt.dev.jjs.ast.JArrayType;
 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.JCastOperation;
 import com.google.gwt.dev.jjs.ast.JClassType;
+import com.google.gwt.dev.jjs.ast.JDeclarationStatement;
 import com.google.gwt.dev.jjs.ast.JExpression;
 import com.google.gwt.dev.jjs.ast.JField;
 import com.google.gwt.dev.jjs.ast.JFieldRef;
 import com.google.gwt.dev.jjs.ast.JInterfaceType;
 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.JMethod;
 import com.google.gwt.dev.jjs.ast.JMethodBody;
@@ -114,22 +113,30 @@
       }
     }
 
-    public void endVisit(JLocalDeclarationStatement x, Context ctx) {
+    public void endVisit(JDeclarationStatement x, Context ctx) {
       // The variable may have been pruned.
-      if (!referencedNonTypes.contains(x.getLocalRef().getTarget())) {
-        // If there is an initializer, just replace with that.
-        if (x.getInitializer() != null) {
-          ctx.replaceMe(x.getInitializer().makeStatement());
-        } else {
-          // No initializer, prune this entirely.
-          if (ctx.canRemove()) {
-            // Just remove it if we can.
-            ctx.removeMe();
-          } else {
-            // Replace with empty block.
-            ctx.replaceMe(new JBlock(program, x.getSourceInfo()));
+      if (!referencedNonTypes.contains(x.getVariableRef().getTarget())) {
+        // Replace with a multi, which may wind up empty.
+        JMultiExpression multi = new JMultiExpression(program,
+            x.getSourceInfo());
+
+        // If the lhs is a field ref, evaluate it first.
+        JVariableRef variableRef = x.getVariableRef();
+        if (variableRef instanceof JFieldRef) {
+          JFieldRef fieldRef = (JFieldRef) variableRef;
+          JExpression instance = fieldRef.getInstance();
+          if (instance != null) {
+            multi.exprs.add(instance);
           }
         }
+
+        // If there is an initializer, evaluate it second.
+        JExpression initializer = x.getInitializer();
+        if (initializer != null) {
+          multi.exprs.add(initializer);
+        }
+
+        ctx.replaceMe(multi.makeStatement());
       }
     }
 
@@ -574,7 +581,7 @@
       return false;
     }
 
-    public boolean visit(JLocalDeclarationStatement x, Context ctx) {
+    public boolean visit(JDeclarationStatement x, Context ctx) {
       /*
        * A declaration by itself doesn't rescue a local (even if it has an
        * initializer). Writes don't count, only reads.
@@ -582,6 +589,16 @@
       if (x.getInitializer() != null) {
         accept(x.getInitializer());
       }
+
+      // If the lhs is a field ref, we have to visit its qualifier.
+      JVariableRef variableRef = x.getVariableRef();
+      if (variableRef instanceof JFieldRef) {
+        JFieldRef fieldRef = (JFieldRef) variableRef;
+        JExpression instance = fieldRef.getInstance();
+        if (instance != null) {
+          accept(instance);
+        }
+      }
       return false;
     }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/SourceGenerationVisitor.java b/dev/core/src/com/google/gwt/dev/jjs/impl/SourceGenerationVisitor.java
index d701d01..c457fe9 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/SourceGenerationVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/SourceGenerationVisitor.java
@@ -89,9 +89,9 @@
   public boolean visit(JField x, Context ctx) {
     super.visit(x, ctx);
 
-    if (x.constInitializer != null) {
+    if (x.getConstInitializer() != null) {
       print(" = ");
-      accept(x.constInitializer);
+      accept(x.getConstInitializer());
     }
     semi();
     return false;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java
index b740e13..f5d44be 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java
@@ -52,7 +52,7 @@
 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.JDeclarationStatement;
 import com.google.gwt.dev.jjs.ast.JLocalRef;
 import com.google.gwt.dev.jjs.ast.JLongLiteral;
 import com.google.gwt.dev.jjs.ast.JMethod;
@@ -383,7 +383,7 @@
   public boolean visit(JField x, Context ctx) {
     // Due to our wacky construction model, only constant fields may be final
     // when generating source
-    if (x.constInitializer != null) {
+    if (x.getConstInitializer() != null) {
       printFinalFlag(x);
     } else {
       printMemberFinalFlag(x);
@@ -552,11 +552,11 @@
   }
 
   @Override
-  public boolean visit(JLocalDeclarationStatement x, Context ctx) {
+  public boolean visit(JDeclarationStatement x, Context ctx) {
     if (!suppressType) {
-      accept(x.getLocalRef().getTarget());
+      accept(x.getVariableRef().getTarget());
     } else {
-      accept(x.getLocalRef());
+      accept(x.getVariableRef());
     }
     JExpression initializer = x.getInitializer();
     if (initializer != null) {
@@ -607,8 +607,12 @@
 
   @Override
   public boolean visit(JMethodBody x, Context ctx) {
-    visitCollectionWithCommas(x.locals.iterator());
-    return false;
+    if (shouldPrintMethodBody()) {
+      return true;
+    } else {
+      visitCollectionWithCommas(x.locals.iterator());
+      return false;
+    }
   }
 
   @Override
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
index 09b712d..51c7dd6 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
@@ -28,7 +28,7 @@
 import com.google.gwt.dev.jjs.ast.JInstanceOf;
 import com.google.gwt.dev.jjs.ast.JInterfaceType;
 import com.google.gwt.dev.jjs.ast.JLocal;
-import com.google.gwt.dev.jjs.ast.JLocalDeclarationStatement;
+import com.google.gwt.dev.jjs.ast.JDeclarationStatement;
 import com.google.gwt.dev.jjs.ast.JLocalRef;
 import com.google.gwt.dev.jjs.ast.JMethod;
 import com.google.gwt.dev.jjs.ast.JMethodCall;
@@ -208,19 +208,20 @@
     }
 
     @Override
-    public void endVisit(JField x, Context ctx) {
-      if (x.constInitializer != null) {
-        addAssignment(x, x.constInitializer);
+    public void endVisit(JDeclarationStatement x, Context ctx) {
+      JExpression initializer = x.getInitializer();
+      if (initializer != null) {
+        addAssignment(x.getVariableRef().getTarget(), initializer);
       }
-      currentMethod = null;
     }
 
     @Override
-    public void endVisit(JLocalDeclarationStatement x, Context ctx) {
-      JExpression initializer = x.getInitializer();
-      if (initializer != null) {
-        addAssignment(x.getLocalRef().getTarget(), initializer);
+    public void endVisit(JField x, Context ctx) {
+      if (x.getConstInitializer() != null) {
+        // TODO: do I still need this?
+        addAssignment(x, x.getConstInitializer());
       }
+      currentMethod = null;
     }
 
     @Override
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/HasInitializer.java b/dev/core/src/com/google/gwt/dev/shell/JavaLong.java
similarity index 60%
copy from dev/core/src/com/google/gwt/dev/jjs/ast/HasInitializer.java
copy to dev/core/src/com/google/gwt/dev/shell/JavaLong.java
index 20a97f7..3217f4f 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/HasInitializer.java
+++ b/dev/core/src/com/google/gwt/dev/shell/JavaLong.java
@@ -1,24 +1,38 @@
-/*
- * 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.ast;
-
-/**
- * Interface implemented by Java entities that can have an initialization 
- * expression.
- */
-public interface HasInitializer {
-  void setInitializer(JExpression expression);
-}
+/*

+ * Copyright 2008 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.shell;

+

+/**

+ * Hosted mode wrapper for a Java long value.

+ */

+public class JavaLong {

+

+  private final long longVal;

+

+  public JavaLong(long longVal) {

+    this.longVal = longVal;

+  }

+

+  public long longValue() {

+    return longVal;

+  }

+

+  @Override

+  public String toString() {

+    return "Java long value: " + longVal;

+  }

+

+}

diff --git a/dev/core/src/com/google/gwt/dev/shell/JsValueGlue.java b/dev/core/src/com/google/gwt/dev/shell/JsValueGlue.java
index 4d6f1e7..611f95d 100644
--- a/dev/core/src/com/google/gwt/dev/shell/JsValueGlue.java
+++ b/dev/core/src/com/google/gwt/dev/shell/JsValueGlue.java
@@ -106,23 +106,12 @@
         return (T) Integer.valueOf(getIntRange(value, Integer.MIN_VALUE,
             Integer.MAX_VALUE, "int", msgPrefix));
       } else if (type == Long.TYPE) {
-        if (!value.isNumber()) {
+        if (!value.isWrappedJavaObject()) {
           throw new HostedModeException(msgPrefix + ": JS value of type "
-              + value.getTypeString() + ", expected long");
+              + value.getTypeString() + ", expected Java long");
         }
-        double doubleVal = value.getNumber();
-        if (doubleVal < Long.MIN_VALUE || doubleVal > Long.MAX_VALUE) {
-          throw new HostedModeException(msgPrefix + ": JS double value "
-              + doubleVal + " out of range for a long");
-        }
-        // TODO(jat): can this actually detect loss of precision?
-        long longVal = (long) doubleVal;
-        if (doubleVal != longVal) {
-          // TODO(jat): should this be an error or exception?
-          ModuleSpace.getLogger().log(TreeLogger.WARN,
-              msgPrefix + ": Loss of precision converting double to long", null);
-        }
-        return (T) Long.valueOf(longVal);
+        JavaLong javaLong = (JavaLong) value.getWrappedJavaObject();
+        return (T) Long.valueOf(javaLong.longValue());
       } else if (type == Short.TYPE) {
         return (T) Short.valueOf((short) getIntRange(value, Short.MIN_VALUE,
             Short.MAX_VALUE, "short", msgPrefix));
@@ -172,13 +161,7 @@
         value.setInt(((Integer) obj).intValue());
       } else if (type == Long.TYPE) {
         long longVal = ((Long) obj).longValue();
-        double doubleVal = longVal;
-        if ((long) doubleVal != longVal) {
-          // TODO(jat): should this be an error or exception?
-          ModuleSpace.getLogger().log(TreeLogger.WARN,
-              "Loss of precision converting long to double", null);
-        }
-        value.setDouble(doubleVal);
+        value.setWrappedJavaObject(cl, new JavaLong(longVal));
       } else if (type == Short.TYPE) {
         value.setInt(((Short) obj).shortValue());
       } else if (type == Void.TYPE) {
diff --git a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/Intrinsic.gwt.xml b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/Intrinsic.gwt.xml
index c76608c..dc7c8fe 100644
--- a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/Intrinsic.gwt.xml
+++ b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/Intrinsic.gwt.xml
@@ -18,5 +18,6 @@
 <!-- Types from this module are not visible to nor imported into user code. -->
 <!--                                                                        -->
 <module>
+   <inherits name="com.google.gwt.lang.LongLib"/>
    <super-source/>
 </module>
diff --git a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Array.java b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Array.java
index 7c6e2b5..eb2874a 100644
--- a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Array.java
+++ b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Array.java
@@ -218,7 +218,7 @@
    * Explicitly initialize all fields to JS false values; see comment in
    * wrapArray(Array, Array).
    */
-  public int length = 0;
+  public volatile int length = 0;
   protected Class arrayClass = null;
   protected int queryId = 0;
 
diff --git a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Cast.java b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Cast.java
index e0177fc..bda5433 100644
--- a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Cast.java
+++ b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Cast.java
@@ -81,28 +81,28 @@
   /**
    * See JLS 5.1.3.
    */
-  static native byte narrow_byte(Object x) /*-{
+  static native byte narrow_byte(double x) /*-{
     return x << 24 >> 24;
   }-*/;
 
   /**
    * See JLS 5.1.3.
    */
-  static native char narrow_char(Object x) /*-{
+  static native char narrow_char(double x) /*-{
     return x & 0xFFFF;
   }-*/;
 
   /**
    * See JLS 5.1.3.
    */
-  static native int narrow_int(Object x) /*-{
+  static native int narrow_int(double x) /*-{
     return ~~x;
   }-*/;
 
   /**
    * See JLS 5.1.3.
    */
-  static native short narrow_short(Object x) /*-{
+  static native short narrow_short(double x) /*-{
     return x << 16 >> 16;
   }-*/;
 
@@ -110,7 +110,7 @@
    * See JLS 5.1.3 for why we do a two-step cast. First we round to int, then
    * narrow to byte.
    */
-  static byte round_byte(Object x) {
+  static byte round_byte(double x) {
     return narrow_byte(round_int(x));
   }
 
@@ -118,32 +118,23 @@
    * See JLS 5.1.3 for why we do a two-step cast. First we round to int, then
    * narrow to char.
    */
-  static char round_char(Object x) {
+  static char round_char(double x) {
     return narrow_char(round_int(x));
   }
 
   /**
    * See JLS 5.1.3.
    */
-  static native int round_int(Object x) /*-{
+  static native int round_int(double x) /*-{
     // TODO: reference java.lang.Integer::MAX_VALUE when we get clinits fixed
     return ~~Math.max(Math.min(x, 2147483647), -2147483648);
   }-*/;
 
   /**
-   * See JLS 5.1.3.
-   */
-  static native long round_long(Object x) /*-{
-    // TODO: reference java.lang.Long::MAX_VALUE when we get clinits fixed
-    x = Math.max(Math.min(x, 9223372036854775807), -9223372036854775808);
-    return (x >= 0) ? Math.floor(x) : Math.ceil(x);
-  }-*/;
-
-  /**
    * See JLS 5.1.3 for why we do a two-step cast. First we rount to int, then
    * narrow to short.
    */
-  static short round_short(Object x) {
+  static short round_short(double x) {
     return narrow_short(round_int(x));
   }
 
diff --git a/dev/core/super/com/google/gwt/lang/LongLib.gwt.xml b/dev/core/super/com/google/gwt/lang/LongLib.gwt.xml
new file mode 100644
index 0000000..1988a50
--- /dev/null
+++ b/dev/core/super/com/google/gwt/lang/LongLib.gwt.xml
@@ -0,0 +1,22 @@
+<!--                                                                        -->
+<!-- Copyright 2008 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   -->
+<!-- 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. License for the specific language governing permissions and   -->
+<!-- limitations under the License.                                         -->
+
+<!-- Internal long emulation library.                                       -->
+<!-- Do not inherit this module directly; inherit com.google.gwt.core.Core. -->
+<!--                                                                        -->
+<!-- Types from this module are not visible to nor imported into user code. -->
+<!--                                                                        -->
+<module>
+   <source path=""/>
+</module>
diff --git a/dev/core/super/com/google/gwt/lang/LongLib.java b/dev/core/super/com/google/gwt/lang/LongLib.java
new file mode 100644
index 0000000..be4bab3
--- /dev/null
+++ b/dev/core/super/com/google/gwt/lang/LongLib.java
@@ -0,0 +1,692 @@
+/*
+ * Copyright 2008 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.lang;
+
+import static com.google.gwt.lang.LongLib.Const.LN_2;
+import static com.google.gwt.lang.LongLib.Const.MAX_VALUE;
+import static com.google.gwt.lang.LongLib.Const.MIN_VALUE;
+import static com.google.gwt.lang.LongLib.Const.NEG_ONE;
+import static com.google.gwt.lang.LongLib.Const.ONE;
+import static com.google.gwt.lang.LongLib.Const.TWO;
+import static com.google.gwt.lang.LongLib.Const.TWO_PWR_24;
+import static com.google.gwt.lang.LongLib.Const.ZERO;
+
+/**
+ * Implements a Java <code>long</code> in a way that can be translated to
+ * JavaScript.
+ */
+public class LongLib {
+  /*
+   * Implementation: An array of two doubles, low and high, such that high+low
+   * is mathematically equivalent to the original integer. Since a JavaScript
+   * Number does not hold enough bits to precisely calculate high+low, all
+   * operations must be implemented carefully. "low" is always between 0 and
+   * 2^32-1 inclusive. "high" is always between -2^63 and 2^63-2^32 inclusive
+   * and is a multiple of 2^32. The sign of the number is determined entirely by
+   * "high". Since low is always positive, small negative numbers are encoded
+   * with high=-2^32. For example, -1 is encoded as { high=-2^32, low=2^32-1 }.
+   * 
+   * Note that this class must be careful using type "long". Being the
+   * implementation of the long type for web mode, any place it uses a long is
+   * not usable in web mode. There are currently two such methods:
+   * {@link #toLong(double[])} and {@link #make(long)}.
+   */
+
+  /**
+   * Number of bits we expect to be accurate for a double representing a large
+   * integer.
+   */
+  private static final int PRECISION_BITS = 48;
+
+  /**
+   * Use nested class to avoid clinit on outer.
+   */
+  static class CachedInts {
+    // Between -128 and 127.
+    static double[][] boxedValues = new double[256][];
+  }
+
+  static class Const {
+    static final double LN_2 = Math.log(2);
+    static final double[] MAX_VALUE = typeChange(Long.MAX_VALUE);
+    static final double[] MIN_VALUE = typeChange(Long.MIN_VALUE);
+    static final double[] NEG_ONE = fromInt(-1);
+    static final double[] ONE = fromInt(1);
+    static final double[] TWO = fromInt(2);
+
+    /**
+     * Half of the number of bits we expect to be precise.
+     * 
+     * @see LongLib#PRECISION_BITS
+     */
+    static final double[] TWO_PWR_24 = typeChange(0x1000000L);
+
+    static final double[] ZERO = fromInt(0);
+  }
+
+  /**
+   * Set this to false before calling any methods when using this class outside
+   * of GWT!
+   */
+  public static volatile boolean RUN_IN_JVM = false;
+
+  /**
+   * Index of the high bits in a 2-double array.
+   */
+  private static final int HIGH = 1;
+  private static final double HIGH_MAX = 9223372032559808512d;
+  private static final double HIGH_MIN = -9223372036854775808d;
+
+  /**
+   * Index of the low bits in a 2-double array.
+   */
+  private static final int LOW = 0;
+  private static final double LOW_MAX = 4294967295d;
+  private static final double LOW_MIN = 0;
+
+  private static final double TWO_PWR_15_DBL = 0x8000;
+  private static final double TWO_PWR_16_DBL = 0x10000;
+  private static final double TWO_PWR_31_DBL = TWO_PWR_16_DBL * TWO_PWR_15_DBL;
+  private static final double TWO_PWR_32_DBL = TWO_PWR_16_DBL * TWO_PWR_16_DBL;
+  private static final double TWO_PWR_48_DBL = TWO_PWR_32_DBL * TWO_PWR_16_DBL;
+  private static final double TWO_PWR_63_DBL = TWO_PWR_32_DBL * TWO_PWR_31_DBL;
+  private static final double TWO_PWR_64_DBL = TWO_PWR_32_DBL * TWO_PWR_32_DBL;
+
+  public static double[] add(double[] a, double[] b) {
+    double newHigh = a[HIGH] + b[HIGH];
+    double newLow = a[LOW] + b[LOW];
+    return create(newLow, newHigh);
+  }
+
+  public static double[] and(double[] a, double[] b) {
+    return makeFromBits(highBits(a) & highBits(b), lowBits(a) & lowBits(b));
+  }
+
+  public static double[] div(double[] a, double[] b) {
+    if (isZero(b)) {
+      throw new ArithmeticException("/ by zero");
+    }
+    if (isZero(a)) {
+      return ZERO;
+    }
+
+    if (eq(a, MIN_VALUE)) {
+      // handle a==MIN_VALUE carefully because of overflow issues
+      if (eq(b, ONE) || eq(b, NEG_ONE)) {
+        // this strange exception is described in JLS3 17.17.2
+        return MIN_VALUE;
+      }
+      // at this point, abs(b) >= 2, so |a/b| < -MIN_VALUE
+      double[] halfa = shr(a, 1);
+      double[] approx = shl(div(halfa, b), 1);
+      double[] rem = sub(a, mul(b, approx));
+      assert gt(rem, MIN_VALUE);
+      return add(approx, div(rem, b));
+    }
+
+    if (eq(b, MIN_VALUE)) {
+      assert !eq(a, MIN_VALUE);
+      return ZERO;
+    }
+
+    // To keep the implementation compact, make a and be
+    // both be positive and swap the sign of the result
+    // if necessary.
+    if (isNegative(a)) {
+      if (isNegative(b)) {
+        return div(neg(a), neg(b));
+      } else {
+        return neg(div(neg(a), b));
+      }
+    }
+    assert (!isNegative(a));
+    if (isNegative(b)) {
+      return neg(div(a, neg(b)));
+    }
+    assert (!isNegative(b));
+
+    // Use float division to approximate the answer.
+    // Repeat until the remainder is less than b.
+    double[] result = ZERO;
+    double[] rem = a;
+    while (gte(rem, b)) {
+      // approximate using float division
+      double[] deltaResult = fromDouble(Math.floor(toDoubleRoundDown(rem)
+          / toDoubleRoundUp(b)));
+      if (isZero(deltaResult)) {
+        deltaResult = Const.ONE;
+      }
+      double[] deltaRem = mul(deltaResult, b);
+
+      assert gte(deltaResult, ONE);
+      assert lte(deltaRem, rem);
+      result = add(result, deltaResult);
+      rem = sub(rem, deltaRem);
+    }
+
+    return result;
+  }
+
+  public static boolean eq(double[] a, double[] b) {
+    return ((a[LOW] == b[LOW]) && (a[HIGH] == b[HIGH]));
+  }
+
+  public static double[] fromDouble(double value) {
+    if (Double.isNaN(value)) {
+      return ZERO;
+    }
+    if (value < -TWO_PWR_63_DBL) {
+      return MIN_VALUE;
+    }
+    if (value >= TWO_PWR_63_DBL) {
+      return MAX_VALUE;
+    }
+    if (value > 0) {
+      return create(Math.floor(value), 0.0);
+    } else {
+      return create(Math.ceil(value), 0.0);
+    }
+  }
+
+  public static double[] fromInt(int value) {
+    if (value > -129 && value < 128) {
+      int rebase = value + 128;
+      double[] result = CachedInts.boxedValues[rebase];
+      if (result == null) {
+        result = CachedInts.boxedValues[rebase] = internalFromInt(value);
+      }
+      return result;
+    }
+    return internalFromInt(value);
+  }
+
+  public static boolean gt(double[] a, double[] b) {
+    return compare(a, b) > 0;
+  }
+
+  public static boolean gte(double[] a, double[] b) {
+    return compare(a, b) >= 0;
+  }
+
+  public static boolean lt(double[] a, double[] b) {
+    return compare(a, b) < 0;
+  }
+
+  public static boolean lte(double[] a, double[] b) {
+    return compare(a, b) <= 0;
+  }
+
+  public static double[] mod(double[] a, double[] b) {
+    return sub(a, mul(div(a, b), b));
+  }
+
+  public static double[] mul(double[] a, double[] b) {
+    if (isZero(a)) {
+      return ZERO;
+    }
+    if (isZero(b)) {
+      return ZERO;
+    }
+
+    // handle MIN_VALUE carefully, because neg(MIN_VALUE)==MIN_VALUE
+    if (eq(a, MIN_VALUE)) {
+      return multByMinValue(b);
+    }
+    if (eq(b, MIN_VALUE)) {
+      return multByMinValue(a);
+    }
+
+    // If either argument is negative, change it to positive, multiply,
+    // and then negate the result.
+    if (isNegative(a)) {
+      if (isNegative(b)) {
+        return mul(neg(a), neg(b));
+      } else {
+        return neg(mul(neg(a), b));
+      }
+    }
+    assert (!isNegative(a));
+    if (isNegative(b)) {
+      return neg(mul(a, neg(b)));
+    }
+    assert (!isNegative(b));
+
+    // If both numbers are small, use float multiplication
+    if (lt(a, TWO_PWR_24) && lt(b, TWO_PWR_24)) {
+      return create(toDouble(a) * toDouble(b), 0.0);
+    }
+
+    // Divide each number into 4 chunks of 16 bits, and then add
+    // up 4x4 multiplies. Skip the six multiplies where the result
+    // mod 2^64 would be 0.
+    double a3 = a[HIGH] % TWO_PWR_48_DBL;
+    double a4 = a[HIGH] - a3;
+    double a1 = a[LOW] % TWO_PWR_16_DBL;
+    double a2 = a[LOW] - a1;
+
+    double b3 = b[HIGH] % TWO_PWR_48_DBL;
+    double b4 = b[HIGH] - b3;
+    double b1 = b[LOW] % TWO_PWR_16_DBL;
+    double b2 = b[LOW] - b1;
+
+    double[] res = ZERO;
+
+    res = addTimes(res, a4, b1);
+    res = addTimes(res, a3, b2);
+    res = addTimes(res, a3, b1);
+    res = addTimes(res, a2, b3);
+    res = addTimes(res, a2, b2);
+    res = addTimes(res, a2, b1);
+    res = addTimes(res, a1, b4);
+    res = addTimes(res, a1, b3);
+    res = addTimes(res, a1, b2);
+    res = addTimes(res, a1, b1);
+
+    return res;
+  }
+
+  public static double[] neg(double[] a) {
+    if (eq(a, MIN_VALUE)) {
+      return MIN_VALUE;
+    }
+    double newHigh = -a[HIGH];
+    double newLow = -a[LOW];
+    if (newLow > LOW_MAX) {
+      newLow -= TWO_PWR_32_DBL;
+      newHigh += TWO_PWR_32_DBL;
+    }
+    if (newLow < LOW_MIN) {
+      newLow += TWO_PWR_32_DBL;
+      newHigh -= TWO_PWR_32_DBL;
+    }
+    return createNormalized(newLow, newHigh);
+  }
+
+  public static boolean neq(double[] a, double[] b) {
+    return ((a[LOW] != b[LOW]) || (a[HIGH] != b[HIGH]));
+  }
+
+  public static double[] not(double[] a) {
+    return makeFromBits(~highBits(a), ~lowBits(a));
+  }
+
+  public static double[] or(double[] a, double[] b) {
+    return makeFromBits(highBits(a) | highBits(b), lowBits(a) | lowBits(b));
+  }
+
+  public static double[] shl(double[] a, int n) {
+    n &= 63;
+
+    if (eq(a, MIN_VALUE)) {
+      if (n == 0) {
+        return a;
+      } else {
+        return ZERO;
+      }
+    }
+
+    if (isNegative(a)) {
+      return neg(shl(neg(a), n));
+    }
+
+    final double twoToN = pwrAsDouble(n);
+
+    double newHigh = a[HIGH] * twoToN % TWO_PWR_64_DBL;
+    double newLow = a[LOW] * twoToN;
+    double diff = newLow - (newLow % TWO_PWR_32_DBL);
+    newHigh += diff;
+    newLow -= diff;
+    if (newHigh >= TWO_PWR_63_DBL) {
+      newHigh -= TWO_PWR_64_DBL;
+    }
+
+    return createNormalized(newLow, newHigh);
+  }
+
+  public static double[] shr(double[] a, int n) {
+    n &= 63;
+    double shiftFact = pwrAsDouble(n);
+    double newHigh = a[HIGH] / shiftFact;
+    double newLow = Math.floor(a[LOW] / shiftFact);
+    return create(newLow, newHigh);
+  }
+
+  /**
+   * Logical right shift. It does not preserve the sign of the input.
+   */
+  public static double[] shru(double[] a, int n) {
+    n &= 63;
+    double[] sr = shr(a, n);
+    if (isNegative(a)) {
+      // the following changes the high bits to 0, using
+      // a formula from JLS3 section 15.19
+      sr = add(sr, shl(TWO, 63 - n));
+    }
+
+    return sr;
+  }
+
+  public static double[] sub(double[] a, double[] b) {
+    double newHigh = a[HIGH] - b[HIGH];
+    double newLow = a[LOW] - b[LOW];
+    return create(newLow, newHigh);
+  }
+
+  /**
+   * Cast from long to double or float.
+   */
+  public static double toDouble(double[] a) {
+    return a[HIGH] + a[LOW];
+  }
+
+  /**
+   * Cast from long to int.
+   */
+  public static int toInt(double[] a) {
+    return lowBits(a);
+  }
+
+  /**
+   * Implicit conversion from long to String.
+   */
+  public static String toString(double[] a) {
+    if (isZero(a)) {
+      return "0";
+    }
+
+    if (eq(a, MIN_VALUE)) {
+      // Special-case MIN_VALUE because neg(MIN_VALUE)==MIN_VALUE
+      return "-9223372036854775808";
+    }
+
+    if (isNegative(a)) {
+      return "-" + toString(neg(a));
+    }
+
+    double[] rem = a;
+    String res = "";
+
+    while (!isZero(rem)) {
+      // Do several digits each time through the loop, so as to
+      // minimize the calls to the very expensive emulated div.
+      final int divByZeroes = 9;
+      final int divBy = 1000000000;
+
+      String digits = "" + toInt(mod(rem, fromInt(divBy)));
+      rem = div(rem, fromInt(divBy));
+
+      if (!isZero(rem)) {
+        int zeroesNeeded = divByZeroes - digits.length();
+        for (; zeroesNeeded > 0; zeroesNeeded--) {
+          digits = "0" + digits;
+        }
+      }
+
+      res = digits + res;
+    }
+
+    return res;
+  }
+
+  public static double[] typeChange(long value) {
+    if (RUN_IN_JVM) {
+      return makeFromBits((int) (value >> 32), (int) value);
+    } else {
+      return typeChange0(value);
+    }
+  }
+
+  public static double[] xor(double[] a, double[] b) {
+    return makeFromBits(highBits(a) ^ highBits(b), lowBits(a) ^ lowBits(b));
+  }
+
+  /**
+   * Because this is a code gen type, this function will keep the double[] type
+   * from being pruned during optimizations. If double[] gets pruned, bad stuff
+   * happens.
+   */
+  static double[] saveDoubleArrayFromPruning() {
+    return new double[2];
+  }
+
+  static long toLong(double[] a) {
+    return (long) a[HIGH] + (long) a[LOW];
+  }
+
+  private static double[] addTimes(double[] accum, double a, double b) {
+    if (a == 0.0) {
+      return accum;
+    }
+    if (b == 0.0) {
+      return accum;
+    }
+    return add(accum, create(a * b, 0.0));
+  }
+
+  /**
+   * Compare the receiver to the argument.
+   * 
+   * @return 0 if they are the same, 1 if the receiver is greater, -1 if the
+   *         argument is greater.
+   */
+  private static int compare(double[] a, double[] b) {
+    if (eq(a, b)) {
+      return 0;
+    }
+
+    boolean nega = isNegative(a);
+    boolean negb = isNegative(b);
+    if (nega && !negb) {
+      return -1;
+    }
+    if (!nega && negb) {
+      return 1;
+    }
+
+    // at this point, the signs are the same, so subtraction will not overflow
+    assert (nega == negb);
+    if (isNegative(sub(a, b))) {
+      return -1;
+    } else {
+      return 1;
+    }
+  }
+
+  /*
+   * Make a new instance equal to valueLow+valueHigh. The arguments do not need
+   * to be normalized, though by convention valueHigh and valueLow will hold the
+   * high and low bits, respectively.
+   */
+  private static double[] create(double valueLow, double valueHigh) {
+    assert (!Double.isNaN(valueHigh));
+    assert (!Double.isNaN(valueLow));
+    assert (!Double.isInfinite(valueHigh));
+    assert (!Double.isInfinite(valueLow));
+    assert (Math.floor(valueHigh) == valueHigh);
+    assert (Math.floor(valueLow) == valueLow);
+
+    // remove overly high bits
+    valueHigh %= TWO_PWR_64_DBL;
+    valueLow %= TWO_PWR_64_DBL;
+
+    // segregate high and low bits between high and low
+    {
+      double diffHigh = valueHigh % TWO_PWR_32_DBL;
+      double diffLow = Math.floor(valueLow / TWO_PWR_32_DBL) * TWO_PWR_32_DBL;
+
+      valueHigh = (valueHigh - diffHigh) + diffLow;
+      valueLow = (valueLow - diffLow) + diffHigh;
+    }
+
+    // Most or all of the while's in this implementation could probably be if's,
+    // but they are left as while's for now pending a careful review.
+
+    // make valueLow be positive
+    while (valueLow < LOW_MIN) {
+      valueLow += TWO_PWR_32_DBL;
+      valueHigh -= TWO_PWR_32_DBL;
+    }
+
+    // make valueLow not too large
+    while (valueLow > LOW_MAX) {
+      valueLow -= TWO_PWR_32_DBL;
+      valueHigh += TWO_PWR_32_DBL;
+    }
+
+    // make valueHigh within range
+    valueHigh = valueHigh % TWO_PWR_64_DBL;
+    while (valueHigh > HIGH_MAX) {
+      valueHigh -= TWO_PWR_64_DBL;
+    }
+    while (valueHigh < HIGH_MIN) {
+      valueHigh += TWO_PWR_64_DBL;
+    }
+
+    return createNormalized(valueLow, valueHigh);
+  }
+
+  /**
+   * Create an instance. The high and low parts must be normalized. Normal
+   * callers should use the factory method {@link #make(double, double) make}.
+   */
+  private static double[] createNormalized(double valueLow, double valueHigh) {
+    assert (valueHigh <= HIGH_MAX);
+    assert (valueHigh >= HIGH_MIN);
+    assert (valueLow >= 0);
+    assert (valueLow <= LOW_MAX);
+    assert (valueHigh % TWO_PWR_32_DBL == 0);
+    assert (Math.floor(valueLow) == valueLow); // no fractional bits allowed
+
+    if (RUN_IN_JVM) {
+      return new double[] {valueLow, valueHigh};
+    } else {
+      return newLong0(valueLow, valueHigh);
+    }
+  }
+
+  private static int highBits(double[] a) {
+    return (int) (a[HIGH] / TWO_PWR_32_DBL);
+  }
+
+  private static double[] internalFromInt(int value) {
+    if (value >= 0) {
+      return createNormalized(value, 0.0);
+    } else {
+      return createNormalized(value + TWO_PWR_32_DBL, -TWO_PWR_32_DBL);
+    }
+  }
+
+  private static boolean isNegative(double[] a) {
+    return a[HIGH] < 0;
+  }
+
+  private static boolean isOdd(double[] a) {
+    return (lowBits(a) & 1) == 1;
+  }
+
+  private static boolean isZero(double[] a) {
+    return a[LOW] == 0.0 && a[HIGH] == 0.0;
+  }
+
+  private static int lowBits(double[] a) {
+    if (a[LOW] >= TWO_PWR_31_DBL) {
+      return (int) (a[LOW] - TWO_PWR_32_DBL);
+    } else {
+      return (int) a[LOW];
+    }
+  }
+
+  /**
+   * Make an instance equivalent to stringing highBits next to lowBits, where
+   * highBits and lowBits are assumed to be in 32-bit twos-complement notation.
+   * As a result, for any double[] l, the following identity holds:
+   * 
+   * <blockquote> <code>l == makeFromBits(l.highBits(), l.lowBits())</code>
+   * </blockquote>
+   */
+  private static double[] makeFromBits(int highBits, int lowBits) {
+    double high = highBits * TWO_PWR_32_DBL;
+    double low = lowBits;
+    if (lowBits < 0) {
+      low += TWO_PWR_32_DBL;
+    }
+    return createNormalized(low, high);
+  }
+
+  private static double[] multByMinValue(double[] a) {
+    if (isOdd(a)) {
+      return MIN_VALUE;
+    } else {
+      return ZERO;
+    }
+  }
+
+  /**
+   * Faster web mode implementation doesn't need full type semantics.
+   */
+  private static native double[] newLong0(double valueLow, double valueHigh) /*-{
+    return [valueLow, valueHigh];
+  }-*/;
+
+  /**
+   * Return a power of two as a double.
+   * 
+   * @return 2 raised to the <code>n</code>
+   */
+  private static double pwrAsDouble(int n) {
+    if (n <= 30) {
+      return (1 << n);
+    } else {
+      return pwrAsDouble(30) * pwrAsDouble(n - 30);
+    }
+  }
+
+  private static double toDoubleRoundDown(double[] a) {
+    int magnitute = (int) (Math.log(a[HIGH]) / LN_2);
+    if (magnitute <= PRECISION_BITS) {
+      return toDouble(a);
+    } else {
+      int diff = magnitute - PRECISION_BITS;
+      int toSubtract = (1 << diff) - 1;
+      return a[HIGH] + (a[LOW] - toSubtract);
+    }
+  }
+
+  private static double toDoubleRoundUp(double[] a) {
+    int magnitute = (int) (Math.log(a[HIGH]) / LN_2);
+    if (magnitute <= PRECISION_BITS) {
+      return toDouble(a);
+    } else {
+      int diff = magnitute - PRECISION_BITS;
+      int toAdd = (1 << diff) - 1;
+      return a[HIGH] + (a[LOW] + toAdd);
+    }
+  }
+
+  /**
+   * Web mode implementation; the long is already the right object.
+   */
+  private static native double[] typeChange0(long value) /*-{
+    return value;
+  }-*/;
+
+  /**
+   * Not instantiable.
+   */
+  private LongLib() {
+  }
+
+}
diff --git a/dev/core/test/com/google/gwt/lang/LongLibJreTest.java b/dev/core/test/com/google/gwt/lang/LongLibJreTest.java
new file mode 100644
index 0000000..9cc6a8f
--- /dev/null
+++ b/dev/core/test/com/google/gwt/lang/LongLibJreTest.java
@@ -0,0 +1,82 @@
+/*

+ * Copyright 2008 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.lang;

+

+import junit.framework.TestCase;

+

+/**

+ * Test the LongLib class as a GWTTestCase.

+ */

+public class LongLibJreTest extends TestCase {

+

+  static {

+    LongLib.RUN_IN_JVM = true;

+  }

+

+  private LongLibTestBase impl = new LongLibTestBase();

+

+  public void testAdditive() {

+    impl.testAdditive();

+  }

+

+  public void testBitOps() {

+    impl.testBitOps();

+  }

+

+  public void testComparisons() {

+    impl.testComparisons();

+  }

+

+  public void testConversions() {

+    impl.testConversions();

+  }

+

+  public void testDiv() {

+    impl.testDiv();

+  }

+

+  public void testFactorial() {

+    impl.testFactorial();

+  }

+

+  public void testFromDouble() {

+    impl.testFromDouble();

+  }

+

+  public void testMinMax() {

+    impl.testMinMax();

+  }

+

+  public void testMultiplicative() {

+    impl.testMultiplicative();

+  }

+

+  public void testNegate() {

+    impl.testNegate();

+  }

+

+  public void testShift() {

+    impl.testShift();

+  }

+

+  public void testToHexString() {

+    impl.testToHexString();

+  }

+

+  public void testToString() {

+    impl.testToString();

+  }

+}

diff --git a/dev/core/test/com/google/gwt/lang/LongLibTestBase.java b/dev/core/test/com/google/gwt/lang/LongLibTestBase.java
new file mode 100644
index 0000000..618df5a
--- /dev/null
+++ b/dev/core/test/com/google/gwt/lang/LongLibTestBase.java
@@ -0,0 +1,353 @@
+/*

+ * Copyright 2008 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.lang;

+

+import com.google.gwt.lang.LongLib.Const;

+

+import junit.framework.TestCase;

+

+/**

+ * Test the LongLib class. The magic expected values were computed by using a

+ * Java println on normal Java longs.

+ */

+public class LongLibTestBase extends TestCase {

+

+  static void assertEquals(double[] expected, double[] actual) {

+    assertTrue("expected=" + LongLib.toString(expected) + "  actual="

+        + LongLib.toString(actual), LongLib.eq(expected, actual));

+  }

+

+  public void testAdditive() {

+    {

+      final double[] n1 = LongLib.fromInt(1234);

+      final double[] n2 = LongLib.fromInt(9876);

+      assertEquals(LongLib.fromInt(11110), LongLib.add(n1, n2));

+      assertEquals(LongLib.fromInt(-8642), LongLib.sub(n1, n2));

+    }

+

+    {

+      final double[] n1 = LongLib.fromInt(-1234);

+      final double[] n2 = LongLib.fromInt(9876);

+      assertEquals(LongLib.fromInt(8642), LongLib.add(n1, n2));

+      assertEquals(LongLib.fromInt(-11110), LongLib.sub(n1, n2));

+    }

+

+    {

+      final double[] n1 = LongLib.fromInt(-1234);

+      final double[] n2 = LongLib.fromInt(-9876);

+      assertEquals(LongLib.fromInt(-11110), LongLib.add(n1, n2));

+      assertEquals(LongLib.fromInt(8642), LongLib.sub(n1, n2));

+    }

+

+    {

+      final double[] n1 = longFromBits(0x12345678, 0xabcdabcd);

+      final double[] n2 = longFromBits(0x77773333, 0x22224444);

+      assertEquals(longFromBits(0x89ab89ab, 0xcdeff011), LongLib.add(n1, n2));

+      assertEquals(longFromBits(0x9abd2345, 0x89ab6789), LongLib.sub(n1, n2));

+    }

+  }

+

+  public void testBitOps() {

+    {

+      final double[] n1 = LongLib.fromInt(1234);

+      final double[] n2 = LongLib.fromInt(9876);

+

+      assertEquals(LongLib.fromInt(1168), LongLib.and(n1, n2));

+      assertEquals(LongLib.fromInt(9942), LongLib.or(n1, n2));

+      assertEquals(LongLib.fromInt(8774), LongLib.xor(n1, n2));

+      assertEquals(LongLib.fromInt(-1235), LongLib.not(n1));

+      assertEquals(LongLib.fromInt(-9877), LongLib.not(n2));

+    }

+

+    {

+      final double[] n1 = LongLib.fromInt(-1234);

+      final double[] n2 = LongLib.fromInt(9876);

+      assertEquals(LongLib.fromInt(8708), LongLib.and(n1, n2));

+      assertEquals(LongLib.fromInt(-66), LongLib.or(n1, n2));

+      assertEquals(LongLib.fromInt(-8774), LongLib.xor(n1, n2));

+      assertEquals(LongLib.fromInt(1233), LongLib.not(n1));

+      assertEquals(LongLib.fromInt(-9877), LongLib.not(n2));

+    }

+

+    {

+      final double[] n1 = LongLib.shl(LongLib.fromInt(0x1234), 32);

+      final double[] n2 = LongLib.shl(LongLib.fromInt(0x9876), 32);

+      assertEquals(LongLib.shl(LongLib.fromInt(0x1034), 32),

+          LongLib.and(n1, n2));

+      assertEquals(LongLib.shl(LongLib.fromInt(0x9a76), 32), LongLib.or(n1, n2));

+      assertEquals(LongLib.shl(LongLib.fromInt(0x8a42), 32),

+          LongLib.xor(n1, n2));

+      assertEquals(longFromBits(0xffffedcb, 0xffffffff), LongLib.not(n1));

+      assertEquals(longFromBits(0xffff6789, 0xffffffff), LongLib.not(n2));

+    }

+  }

+

+  public void testComparisons() {

+    assertTrue(LongLib.lt(LongLib.fromInt(10), LongLib.fromInt(11)));

+    assertTrue(LongLib.lte(LongLib.fromInt(10), LongLib.fromInt(11)));

+    assertTrue(!LongLib.eq(LongLib.fromInt(10), LongLib.fromInt(11)));

+    assertTrue(!LongLib.gte(LongLib.fromInt(10), LongLib.fromInt(11)));

+    assertTrue(!LongLib.gt(LongLib.fromInt(10), LongLib.fromInt(11)));

+

+    assertTrue(!LongLib.lt(LongLib.fromInt(10), LongLib.fromInt(10)));

+    assertTrue(LongLib.lte(LongLib.fromInt(10), LongLib.fromInt(10)));

+    assertTrue(LongLib.eq(LongLib.fromInt(10), LongLib.fromInt(10)));

+    assertTrue(LongLib.gte(LongLib.fromInt(10), LongLib.fromInt(10)));

+    assertTrue(!LongLib.gt(LongLib.fromInt(10), LongLib.fromInt(10)));

+

+    assertTrue(!LongLib.lt(LongLib.fromInt(12), LongLib.fromInt(11)));

+    assertTrue(!LongLib.lte(LongLib.fromInt(12), LongLib.fromInt(11)));

+    assertTrue(!LongLib.eq(LongLib.fromInt(12), LongLib.fromInt(11)));

+    assertTrue(LongLib.gte(LongLib.fromInt(12), LongLib.fromInt(11)));

+    assertTrue(LongLib.gt(LongLib.fromInt(12), LongLib.fromInt(11)));

+

+    // the following three comparisons cannot be implemented by

+    // subtracting the arguments, because the subtraction causes an overflow

+    final double[] largeNeg = longFromBits(0x82341234, 0x0);

+    final double[] largePos = longFromBits(0x12341234, 0x0);

+    assertTrue(LongLib.lt(largeNeg, largePos));

+

+    assertTrue(LongLib.lt(Const.MIN_VALUE, LongLib.fromInt(0)));

+    assertTrue(LongLib.gt(LongLib.fromInt(0), Const.MIN_VALUE));

+

+    final double[] largePosPlusOne = LongLib.add(largePos, LongLib.fromInt(1));

+

+    assertTrue(LongLib.lt(largePos, largePosPlusOne));

+    assertTrue(LongLib.lte(largePos, largePosPlusOne));

+    assertTrue(!LongLib.eq(largePos, largePosPlusOne));

+    assertTrue(!LongLib.gte(largePos, largePosPlusOne));

+    assertTrue(!LongLib.gt(largePos, largePosPlusOne));

+

+    assertTrue(!LongLib.lt(largePos, largePos));

+    assertTrue(LongLib.lte(largePos, largePos));

+    assertTrue(LongLib.eq(largePos, largePos));

+    assertTrue(LongLib.gte(largePos, largePos));

+    assertTrue(!LongLib.gt(largePos, largePos));

+

+    assertTrue(!LongLib.lt(largePosPlusOne, largePos));

+    assertTrue(!LongLib.lte(largePosPlusOne, largePos));

+    assertTrue(!LongLib.eq(largePosPlusOne, largePos));

+    assertTrue(LongLib.gte(largePosPlusOne, largePos));

+    assertTrue(LongLib.gt(largePosPlusOne, largePos));

+  }

+

+  public void testConversions() {

+    assertEquals(10, LongLib.toInt(longFromBits(0, 10)));

+    assertEquals(-10, LongLib.toInt(longFromBits(0, -10)));

+    assertEquals(-10, LongLib.toInt(longFromBits(100, -10)));

+    assertEquals(-10, LongLib.toInt(longFromBits(-100000, -10)));

+  }

+

+  public void testDiv() {

+    double[] deadBeef = LongLib.typeChange(0xdeadbeefdeadbeefL);

+    double[] ten = LongLib.fromInt(10);

+    assertEquals(LongLib.typeChange(-240105308887621659L), LongLib.div(

+        deadBeef, ten));

+    assertEquals(Const.ZERO, LongLib.div(Const.ONE, Const.TWO));

+    assertEquals(LongLib.typeChange(4611686018427387903L), LongLib.div(

+        Const.MAX_VALUE, Const.TWO));

+  }

+

+  public void testFactorial() {

+    double[] fact18 = fact(LongLib.fromInt(18));

+    double[] fact17 = fact(LongLib.fromInt(17));

+    assertEquals(LongLib.fromInt(18), LongLib.div(fact18, fact17));

+  }

+

+  public void testFromDouble() {

+    // these tests are based on JLS3, section 5.1.3

+

+    assertEquals(LongLib.fromInt(10), LongLib.fromDouble(10.5));

+    assertEquals(LongLib.fromInt(-10), LongLib.fromDouble(-10.5));

+    assertEquals(LongLib.shl(LongLib.fromInt(1), 55),

+        LongLib.fromDouble(Math.pow(2.0, 55) + 0.5));

+    assertEquals(LongLib.neg(LongLib.shl(LongLib.fromInt(1), 55)),

+        LongLib.fromDouble(-Math.pow(2.0, 55) - 0.5));

+

+    assertEquals(LongLib.fromInt(0), LongLib.fromDouble(Double.NaN));

+

+    assertEquals(Const.MAX_VALUE, LongLib.fromDouble(Math.pow(2.0, 100)));

+    assertEquals(Const.MAX_VALUE, LongLib.fromDouble(Double.POSITIVE_INFINITY));

+    assertEquals(Const.MIN_VALUE, LongLib.fromDouble(-Math.pow(2.0, 100)));

+    assertEquals(Const.MIN_VALUE, LongLib.fromDouble(Double.NEGATIVE_INFINITY));

+  }

+

+  public void testMinMax() {

+    assertEquals(Const.MIN_VALUE, LongLib.shl(LongLib.fromInt(1), 63));

+    assertEquals(Const.MAX_VALUE, LongLib.neg(LongLib.add(Const.MIN_VALUE,

+        LongLib.fromInt(1))));

+  }

+

+  public void testMultiplicative() {

+    assertEquals(LongLib.fromInt(3333), LongLib.mul(LongLib.fromInt(1111),

+        LongLib.fromInt(3)));

+    assertEquals(LongLib.fromInt(-3333), LongLib.mul(LongLib.fromInt(1111),

+        LongLib.fromInt(-3)));

+    assertEquals(LongLib.fromInt(-3333), LongLib.mul(LongLib.fromInt(-1111),

+        LongLib.fromInt(3)));

+    assertEquals(LongLib.fromInt(3333), LongLib.mul(LongLib.fromInt(-1111),

+        LongLib.fromInt(-3)));

+    assertEquals(LongLib.fromInt(0), LongLib.mul(LongLib.fromInt(100),

+        LongLib.fromInt(0)));

+

+    assertEquals(longFromBits(0x7ff63f7c, 0x1df4d840), LongLib.mul(

+        longFromBits(0x12345678, 0x12345678), longFromBits(0x1234, 0x12345678)));

+    assertEquals(longFromBits(0x7ff63f7c, 0x1df4d840), LongLib.mul(

+        longFromBits(0xf2345678, 0x12345678), longFromBits(0x1234, 0x12345678)));

+    assertEquals(longFromBits(0x297e3f7c, 0x1df4d840), LongLib.mul(

+        longFromBits(0xf2345678, 0x12345678), longFromBits(0xffff1234,

+            0x12345678)));

+

+    assertEquals(LongLib.fromInt(0), LongLib.mul(Const.MIN_VALUE,

+        LongLib.fromInt(2)));

+    assertEquals(Const.MIN_VALUE, LongLib.mul(Const.MIN_VALUE,

+        LongLib.fromInt(1)));

+    assertEquals(Const.MIN_VALUE, LongLib.mul(Const.MIN_VALUE,

+        LongLib.fromInt(-1)));

+

+    assertEquals(LongLib.fromInt(1), LongLib.div(LongLib.fromInt(5),

+        LongLib.fromInt(5)));

+    assertEquals(LongLib.fromInt(333), LongLib.div(LongLib.fromInt(1000),

+        LongLib.fromInt(3)));

+    assertEquals(LongLib.fromInt(-333), LongLib.div(LongLib.fromInt(1000),

+        LongLib.fromInt(-3)));

+    assertEquals(LongLib.fromInt(-333), LongLib.div(LongLib.fromInt(-1000),

+        LongLib.fromInt(3)));

+    assertEquals(LongLib.fromInt(333), LongLib.div(LongLib.fromInt(-1000),

+        LongLib.fromInt(-3)));

+    assertEquals(LongLib.fromInt(0), LongLib.div(LongLib.fromInt(3),

+        LongLib.fromInt(1000)));

+    assertEquals(longFromBits(0x1003d0, 0xe84f5ae8), LongLib.div(longFromBits(

+        0x12345678, 0x12345678), longFromBits(0x0, 0x123)));

+    assertEquals(longFromBits(0x0, 0x10003), LongLib.div(longFromBits(

+        0x12345678, 0x12345678), longFromBits(0x1234, 0x12345678)));

+    assertEquals(longFromBits(0xffffffff, 0xffff3dfe), LongLib.div(

+        longFromBits(0xf2345678, 0x12345678), longFromBits(0x1234, 0x12345678)));

+    assertEquals(longFromBits(0x0, 0xeda), LongLib.div(longFromBits(0xf2345678,

+        0x12345678), longFromBits(0xffff1234, 0x12345678)));

+

+    try {

+      LongLib.div(LongLib.fromInt(1), LongLib.fromInt(0));

+      fail("Expected an ArithmeticException");

+    } catch (ArithmeticException e) {

+    }

+

+    assertEquals(longFromBits(0xc0000000, 0x00000000), LongLib.div(

+        Const.MIN_VALUE, LongLib.fromInt(2)));

+    assertEquals(Const.MIN_VALUE, LongLib.div(Const.MIN_VALUE,

+        LongLib.fromInt(1)));

+    assertEquals(Const.MIN_VALUE, LongLib.div(Const.MIN_VALUE,

+        LongLib.fromInt(-1))); // JLS3 section 15.17.2

+  }

+

+  public void testNegate() {

+    assertEquals(LongLib.fromInt(-1), LongLib.neg(LongLib.fromInt(1)));

+    assertEquals(LongLib.fromInt(1), LongLib.neg(LongLib.fromInt(-1)));

+

+    // JLS3 15.15.4

+    assertEquals(Const.MIN_VALUE, LongLib.neg(Const.MIN_VALUE));

+  }

+

+  public void testShift() {

+    assertEquals(longFromBits(0xd048d115, 0x9d159c00), LongLib.shl(

+        longFromBits(0x12341234, 0x45674567), 10));

+    assertEquals(longFromBits(0x48d04, 0x8d1159d1), LongLib.shr(longFromBits(

+        0x12341234, 0x45674567), 10));

+    assertEquals(longFromBits(0x48d04, 0x8d1159d1), LongLib.shru(longFromBits(

+        0x12341234, 0x45674567), 10));

+    assertEquals(longFromBits(0xd048d115, 0x9d159c00), LongLib.shl(

+        longFromBits(0x92341234, 0x45674567), 10));

+    assertEquals(longFromBits(0xffe48d04, 0x8d1159d1), LongLib.shr(

+        longFromBits(0x92341234, 0x45674567), 10));

+    assertEquals(longFromBits(0x248d04, 0x8d1159d1), LongLib.shru(longFromBits(

+        0x92341234, 0x45674567), 10));

+

+    assertEquals(LongLib.fromInt(-1), LongLib.shr(LongLib.fromInt(-1), 10));

+

+    assertEquals(LongLib.fromInt(-1 << 5), LongLib.shl(LongLib.fromInt(-1), 5));

+    assertEquals(LongLib.fromInt(-1), LongLib.shl(LongLib.fromInt(-1), 0));

+    assertEquals(LongLib.neg(LongLib.typeChange(0x4000000000000000L)),

+        LongLib.shr(LongLib.shl(LongLib.fromInt(1), 63), 1));

+    assertEquals(LongLib.fromInt(0), LongLib.shl(LongLib.shl(

+        LongLib.fromInt(-1), 32), 32));

+    assertEquals(Const.MIN_VALUE, LongLib.shl(Const.MIN_VALUE, 0));

+    assertEquals(LongLib.fromInt(0), LongLib.shl(Const.MIN_VALUE, 1));

+    assertEquals(longFromBits(0xfffffffc, 0x00000000), LongLib.shr(

+        LongLib.neg(longFromBits(8, 0)), 1));

+    assertEquals(longFromBits(0x7ffffffc, 0x0), LongLib.shru(

+        LongLib.neg(longFromBits(8, 0)), 1));

+  }

+

+  // Issue 1198, and also a good exercise of several methods.

+  public void testToHexString() {

+    double[] deadbeaf12341234 = longFromBits(0xdeadbeaf, 0x12341234);

+

+    assertEquals("deadbeaf12341234", toHexString(deadbeaf12341234));

+  }

+

+  public void testToString() {

+    assertEquals("0", LongLib.toString(LongLib.fromInt(0)));

+    assertEquals("1", LongLib.toString(LongLib.fromInt(1)));

+    assertEquals("-1", LongLib.toString(LongLib.fromInt(-1)));

+    assertEquals("-10", LongLib.toString(LongLib.fromInt(-10)));

+    assertEquals("-9223372036854775808", LongLib.toString(Const.MIN_VALUE));

+

+    int top = 922337201;

+    int bottom = 967490662;

+    double[] fullnum = LongLib.add(LongLib.mul(LongLib.fromInt(1000000000),

+        LongLib.fromInt(top)), LongLib.fromInt(bottom));

+

+    assertEquals("922337201967490662", LongLib.toString(fullnum));

+    assertEquals("-922337201967490662", LongLib.toString(LongLib.neg(fullnum)));

+  }

+

+  private double[] fact(double[] n) {

+    if (LongLib.eq(n, LongLib.fromInt(0))) {

+      return LongLib.fromInt(1);

+    } else {

+      return LongLib.mul(n, fact(LongLib.sub(n, LongLib.fromInt(1))));

+    }

+  }

+

+  private double[] longFromBits(int top, int bottom) {

+    double[] topHalf = LongLib.shl(LongLib.fromInt(top), 32);

+    double[] bottomHalf = LongLib.fromInt(bottom);

+    if (LongLib.lt(bottomHalf, Const.ZERO)) {

+      bottomHalf = LongLib.add(bottomHalf, LongLib.shl(LongLib.fromInt(1), 32));

+    }

+    double[] total = LongLib.add(topHalf, bottomHalf);

+    return total;

+  }

+

+  private String toHexString(double[] x) {

+    // copied from the GWT Long class and modified for double[]

+    double[] zero = LongLib.fromInt(0);

+

+    if (LongLib.eq(x, zero)) {

+      return "0";

+    }

+    String[] hexDigits = new String[] {

+        "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d",

+        "e", "f"};

+    String hexStr = "";

+    while (!LongLib.eq(x, zero)) {

+      int nibble = LongLib.toInt(x) & 0xF;

+      hexStr = hexDigits[nibble] + hexStr;

+      x = LongLib.shru(x, 4);

+    }

+    return hexStr;

+  }

+}

diff --git a/eclipse/dev/linux/.classpath b/eclipse/dev/linux/.classpath
index bd7b01b..474df8f 100644
--- a/eclipse/dev/linux/.classpath
+++ b/eclipse/dev/linux/.classpath
@@ -3,6 +3,7 @@
 	<classpathentry kind="src" path="core/src"/>
 	<classpathentry kind="src" path="core/test"/>
 	<classpathentry kind="src" path="linux/src"/>
+	<classpathentry excluding="com/google/gwt/dev/jjs/intrinsic/" kind="src" path="core/super"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
 	<classpathentry kind="var" path="GWT_TOOLS/lib/eclipse/org.eclipse.swt.gtk-linux-3.2.1.jar" sourcepath="/GWT_TOOLS/lib/eclipse/org.eclipse.swt.gtk-linux-3.2.1.src.zip"/>
 	<classpathentry kind="var" path="GWT_TOOLS/lib/apache/ant-1.6.5.jar" sourcepath="/GWT_TOOLS/lib/apache/ant-1.6.5-src.zip"/>
diff --git a/eclipse/dev/mac/.classpath b/eclipse/dev/mac/.classpath
index b5c179a..0f3a28d 100644
--- a/eclipse/dev/mac/.classpath
+++ b/eclipse/dev/mac/.classpath
@@ -3,6 +3,7 @@
 	<classpathentry kind="src" path="core/src"/>
 	<classpathentry kind="src" path="core/test"/>
 	<classpathentry kind="src" path="mac/src"/>
+	<classpathentry excluding="com/google/gwt/dev/jjs/intrinsic/" kind="src" path="core/super"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
 	<classpathentry kind="var" path="GWT_TOOLS/lib/eclipse/org.eclipse.swt.carbon-macosx-3.2.1.jar" sourcepath="/GWT_TOOLS/lib/eclipse/org.eclipse.swt.carbon-macosx-3.2.1.src.zip"/>
 	<classpathentry kind="var" path="GWT_TOOLS/lib/apache/ant-1.6.5.jar" sourcepath="/GWT_TOOLS/lib/apache/ant-1.6.5-src.zip"/>
diff --git a/eclipse/dev/windows/.classpath b/eclipse/dev/windows/.classpath
index cf646da..f35f9ec 100644
--- a/eclipse/dev/windows/.classpath
+++ b/eclipse/dev/windows/.classpath
@@ -3,6 +3,7 @@
 	<classpathentry kind="src" path="core/src"/>
 	<classpathentry kind="src" path="core/test"/>
 	<classpathentry kind="src" path="windows/src"/>
+	<classpathentry excluding="com/google/gwt/dev/jjs/intrinsic/" kind="src" path="core/super"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
 	<classpathentry kind="var" path="GWT_TOOLS/lib/eclipse/org.eclipse.swt.win32-win32-3.2.1.jar" sourcepath="/GWT_TOOLS/lib/eclipse/org.eclipse.swt.win32-win32-3.2.1.src.zip"/>
 	<classpathentry kind="var" path="GWT_TOOLS/lib/apache/ant-1.6.5.jar" sourcepath="/GWT_TOOLS/lib/apache/ant-1.6.5-src.zip"/>
diff --git a/user/build.xml b/user/build.xml
index fab6c83..fb08c64 100755
--- a/user/build.xml
+++ b/user/build.xml
@@ -38,7 +38,7 @@
 
 	<target name="compile.tests" description="Compiles the test code for this project">
 		<mkdir dir="${javac.junit.out}" />
-		<gwt.javac srcdir="test" destdir="${javac.junit.out}">
+		<gwt.javac srcdir="test" excludes="com/google/gwt/langtest/**" destdir="${javac.junit.out}">
 			<classpath>
 				<pathelement location="${javac.out}" />
 				<pathelement location="${gwt.tools.lib}/junit/junit-3.8.1.jar" />
diff --git a/user/src/com/google/gwt/user/client/Cookies.java b/user/src/com/google/gwt/user/client/Cookies.java
index 829b1f0..15554ce 100644
--- a/user/src/com/google/gwt/user/client/Cookies.java
+++ b/user/src/com/google/gwt/user/client/Cookies.java
@@ -147,7 +147,7 @@
   }-*/;
 
   private static native void setCookieImpl(String name, String value,
-      long expires, String domain, String path, boolean secure) /*-{
+      double expires, String domain, String path, boolean secure) /*-{
     var c = encodeURIComponent(name) + '=' + encodeURIComponent(value);
     if ( expires )
       c += ';expires=' + (new Date(expires)).toGMTString();
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamWriter.java b/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamWriter.java
index e8b86bc..a89fa39 100644
--- a/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamWriter.java
+++ b/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamWriter.java
@@ -54,9 +54,10 @@
     append(String.valueOf(fieldValue));
   }
 
-  public void writeLong(long fieldValue) {
-    append(String.valueOf(fieldValue));
-  }
+  /**
+   * Asymmetric implementation; see subclasses.
+   */
+  public abstract void writeLong(long value) throws SerializationException;
 
   public void writeObject(Object instance) throws SerializationException {
     if (instance == null) {
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamReader.java b/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamReader.java
index 7ff2cad..e2c20d1 100644
--- a/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamReader.java
+++ b/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamReader.java
@@ -76,9 +76,13 @@
     return this.@com.google.gwt.user.client.rpc.impl.ClientSerializationStreamReader::results[--this.@com.google.gwt.user.client.rpc.impl.ClientSerializationStreamReader::index];
   }-*/;
 
-  public native long readLong() /*-{
-    return this.@com.google.gwt.user.client.rpc.impl.ClientSerializationStreamReader::results[--this.@com.google.gwt.user.client.rpc.impl.ClientSerializationStreamReader::index];
-  }-*/;
+  public long readLong() {
+    /*
+     * Sent as a string; if we tried to marshal as a number the JSON eval would
+     * lose precision. We use hex due to specially optimized code paths in Long.
+     */
+    return Long.parseLong(readString(), 16);
+  }
 
   public native short readShort() /*-{
     return this.@com.google.gwt.user.client.rpc.impl.ClientSerializationStreamReader::results[--this.@com.google.gwt.user.client.rpc.impl.ClientSerializationStreamReader::index];
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java b/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java
index 5915306..d466958 100644
--- a/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java
+++ b/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java
@@ -115,6 +115,10 @@
     return buffer.toString();
   }
 
+  public void writeLong(long fieldValue) {
+    append(Long.toString(fieldValue, 16));
+  }
+
   @Override
   protected int addString(String string) {
     if (string == null) {
diff --git a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java
index 8fecf23..fea0b56 100644
--- a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java
+++ b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java
@@ -337,7 +337,7 @@
   }
 
   public long readLong() {
-    return Long.parseLong(extract());
+    return Long.parseLong(extract(), 16);
   }
 
   public short readShort() {
diff --git a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java
index 2549eb2..9dc1e9d 100644
--- a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java
+++ b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java
@@ -496,6 +496,14 @@
     return buffer.toString();
   }
 
+  public void writeLong(long fieldValue) {
+    /*
+     * Marshal down to client as a string; if we tried to marshal as a number
+     * the JSON eval would lose precision.
+     */
+    writeString(Long.toString(fieldValue, 16));
+  }
+
   @Override
   protected int addString(String string) {
     if (string == null) {
diff --git a/user/super/com/google/gwt/emul/java/lang/ArithmeticException.java b/user/super/com/google/gwt/emul/java/lang/ArithmeticException.java
new file mode 100644
index 0000000..0f1a68a
--- /dev/null
+++ b/user/super/com/google/gwt/emul/java/lang/ArithmeticException.java
@@ -0,0 +1,32 @@
+/*

+ * Copyright 2008 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 java.lang;

+

+/**

+ * NOTE: in GWT this is only thrown for division by zero on longs.

+ * 

+ * See <a

+ * href="http://java.sun.com/j2se/1.5.0/docs/api/java/lang/ArrayIndexOutOfBoundsException.html">the

+ * official Java API doc</a> for details.

+ */

+public class ArithmeticException extends RuntimeException {

+  public ArithmeticException(String explanation) {

+    super(explanation);

+  }

+  public ArithmeticException() {

+    super();

+  }

+}

diff --git a/user/super/com/google/gwt/emul/java/lang/Byte.java b/user/super/com/google/gwt/emul/java/lang/Byte.java
index eacee7e..4033f80 100644
--- a/user/super/com/google/gwt/emul/java/lang/Byte.java
+++ b/user/super/com/google/gwt/emul/java/lang/Byte.java
@@ -24,11 +24,16 @@
   public static final byte MAX_VALUE = (byte) 0x7F;
   public static final int SIZE = 8;
 
-  // Box all values according to JLS
-  private static Byte[] boxedValues = new Byte[256];
+  /**
+   * Use nested class to avoid clinit on outer.
+   */
+  private static class BoxedValues {
+    // Box all values according to JLS
+    private static Byte[] boxedValues = new Byte[256];
+  }
 
   public static Byte decode(String s) throws NumberFormatException {
-    return new Byte((byte) __decodeAndValidateLong(s, MIN_VALUE, MAX_VALUE));
+    return new Byte((byte) __decodeAndValidateInt(s, MIN_VALUE, MAX_VALUE));
   }
 
   /**
@@ -47,7 +52,7 @@
 
   public static byte parseByte(String s, int radix)
       throws NumberFormatException {
-    return (byte) __parseAndValidateLong(s, radix, MIN_VALUE, MAX_VALUE);
+    return (byte) __parseAndValidateInt(s, radix, MIN_VALUE, MAX_VALUE);
   }
 
   public static String toString(byte b) {
@@ -56,10 +61,11 @@
 
   public static Byte valueOf(byte b) {
     int rebase = b + 128;
-    if (boxedValues[rebase] == null) {
-      boxedValues[rebase] = new Byte(b);
+    Byte result = BoxedValues.boxedValues[rebase];
+    if (result == null) {
+      result = BoxedValues.boxedValues[rebase] = new Byte(b);
     }
-    return boxedValues[rebase];
+    return result;
   }
 
   public static Byte valueOf(String s) throws NumberFormatException {
diff --git a/user/super/com/google/gwt/emul/java/lang/Character.java b/user/super/com/google/gwt/emul/java/lang/Character.java
index 9dd6ad3..fbe8f5c 100644
--- a/user/super/com/google/gwt/emul/java/lang/Character.java
+++ b/user/super/com/google/gwt/emul/java/lang/Character.java
@@ -28,8 +28,13 @@
   public static final char MIN_VALUE = '\u0000';
   public static final char MAX_VALUE = '\uFFFF';
 
-  // Box values according to JLS - from \u0000 to \u007f
-  private static Character[] boxedValues = new Character[128];
+  /**
+   * Use nested class to avoid clinit on outer.
+   */
+  private static class BoxedValues {
+    // Box values according to JLS - from \u0000 to \u007f
+    private static Character[] boxedValues = new Character[128];
+  }
 
   public static int digit(char c, int radix) {
     if (radix < MIN_RADIX || radix > MAX_RADIX) {
@@ -129,10 +134,11 @@
 
   public static Character valueOf(char c) {
     if (c < 128) {
-      if (boxedValues[c] == null) {
-        boxedValues[c] = new Character(c);
+      Character result = BoxedValues.boxedValues[c];
+      if (result == null) {
+        result = BoxedValues.boxedValues[c] = new Character(c);
       }
-      return boxedValues[c];
+      return result;
     }
     return new Character(c);
   }
diff --git a/user/super/com/google/gwt/emul/java/lang/Integer.java b/user/super/com/google/gwt/emul/java/lang/Integer.java
index c5b0c15..ac945f5 100644
--- a/user/super/com/google/gwt/emul/java/lang/Integer.java
+++ b/user/super/com/google/gwt/emul/java/lang/Integer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -20,12 +20,30 @@
  */
 public final class Integer extends Number implements Comparable<Integer> {
 
-  public static final int MIN_VALUE = 0x80000000;
   public static final int MAX_VALUE = 0x7fffffff;
+  public static final int MIN_VALUE = 0x80000000;
   public static final int SIZE = 32;
 
-  // Box values according to JLS - between -128 and 127
-  private static Integer[] boxedValues = new Integer[256];
+  /**
+   * Use nested class to avoid clinit on outer.
+   */
+  private static class BoxedValues {
+    // Box values according to JLS - between -128 and 127
+    private static Integer[] boxedValues = new Integer[256];
+  }
+
+  /**
+   * Use nested class to avoid clinit on outer.
+   */
+  private static class ReverseNibbles {
+    /**
+     * A fast-lookup of the reversed bits of all the nibbles 0-15. Used to
+     * implement {@link #reverse(int)}.
+     */
+    private static int[] reverseNibbles = {
+        0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, 0x1, 0x9, 0x5, 0xd, 0x3, 0xb,
+        0x7, 0xf};
+  }
 
   public static int bitCount(int x) {
     // Courtesy the University of Kentucky
@@ -39,7 +57,7 @@
   }
 
   public static Integer decode(String s) throws NumberFormatException {
-    return new Integer((int) __decodeAndValidateLong(s, MIN_VALUE, MAX_VALUE));
+    return new Integer((int) __decodeAndValidateInt(s, MIN_VALUE, MAX_VALUE));
   }
 
   /**
@@ -58,25 +76,15 @@
       return 0;
     } else {
       int rtn;
-      for (rtn = 0x40000000; (rtn & i) == 0; rtn = rtn >> 1) {
-        // loop down until smaller
+      for (rtn = 0x40000000; (rtn & i) == 0; rtn >>= 1) {
+        // loop down until matched
       }
       return rtn;
     }
   }
 
   public static int lowestOneBit(int i) {
-    if (i == 0) {
-      return 0;
-    } else if (i == Integer.MIN_VALUE) {
-      return 0x80000000;
-    } else {
-      int r = 1;
-      while ((r & i) == 0) {
-        r = r * 2;
-      }
-      return r;
-    }
+    return i & -i;
   }
 
   public static int numberOfLeadingZeros(int i) {
@@ -91,10 +99,10 @@
 
   public static int numberOfTrailingZeros(int i) {
     if (i == 0) {
-      return 32;
+      return SIZE;
     } else {
       int rtn = 0;
-      for (int r = 1; (r & i) == 0; r = r * 2) {
+      for (int r = 1; (r & i) == 0; r <<= 1) {
         rtn++;
       }
       return rtn;
@@ -106,25 +114,15 @@
   }
 
   public static int parseInt(String s, int radix) throws NumberFormatException {
-    return (int) __parseAndValidateLong(s, radix, MIN_VALUE, MAX_VALUE);
+    return __parseAndValidateInt(s, radix, MIN_VALUE, MAX_VALUE);
   }
 
   public static int reverse(int i) {
-    int ui = i & 0x7fffffff; // avoid sign extension
-    int acc = 0;  
-    int front = 0x80000000;
-    int back = 1;
-    int swing = 31;
-    while (swing > 0) {
-      acc = acc | ((ui & front) >> swing) | ((ui & back) << swing);
-      swing -= 2;
-      front = front >> 1;
-      back = back << 1;
-    }
-    if (i < 0) {
-      acc = acc | 0x1; // restore the real value of 0x80000000
-    }
-    return acc;
+    int[] nibbles = ReverseNibbles.reverseNibbles;
+    return (nibbles[i >>> 28]) | (nibbles[(i >> 24) & 0xf] << 4)
+        | (nibbles[(i >> 20) & 0xf] << 8) | (nibbles[(i >> 16) & 0xf] << 12)
+        | (nibbles[(i >> 12) & 0xf] << 16) | (nibbles[(i >> 8) & 0xf] << 20)
+        | (nibbles[(i >> 4) & 0xf] << 24) | (nibbles[i & 0xf] << 28);
   }
 
   public static int reverseBytes(int i) {
@@ -140,15 +138,15 @@
   }
 
   public static int rotateRight(int i, int distance) {
-    int ui = i & 0x7fffffff; // avoid sign extension
-    int carry = (i < 0) ? 0x40000000 : 0; // 0x80000000 rightshifted 1
+    int ui = i & MAX_VALUE; // avoid sign extension
+    int carry = (i < 0) ? 0x40000000 : 0; // MIN_VALUE rightshifted 1
     while (distance-- > 0) {
       int nextcarry = ui & 1;
       ui = carry | (ui >> 1);
       carry = (nextcarry == 0) ? 0 : 0x40000000;
     }
     if (carry != 0) {
-      ui = ui | 0x80000000;
+      ui = ui | MIN_VALUE;
     }
     return ui;
   }
@@ -163,25 +161,51 @@
     }
   }
 
-  public static String toBinaryString(int x) {
-    return Long.toBinaryString(x);
+  public static String toBinaryString(int value) {
+    return toPowerOfTwoString(value, 1);
   }
 
-  public static String toHexString(int x) {
-    return Long.toHexString(x);
+  public static String toHexString(int value) {
+    return toPowerOfTwoString(value, 4);
   }
 
-  public static String toString(int b) {
-    return String.valueOf(b);
+  public static String toString(int value) {
+    return String.valueOf(value);
+  }
+
+  public static String toString(int value, int radix) {
+    if (radix == 10 || radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
+      return String.valueOf(value);
+    }
+
+    final int bufSize = 33;
+    char[] buf = new char[bufSize];
+    int pos = bufSize - 1;
+    if (value >= 0) {
+      while (value >= radix) {
+        buf[pos--] = __Digits.digits[value % radix];
+        value /= radix;
+      }
+      buf[pos] = __Digits.digits[value];
+    } else {
+      while (value <= -radix) {
+        buf[pos--] = __Digits.digits[-(value % radix)];
+        value /= radix;
+      }
+      buf[pos--] = __Digits.digits[-value];
+      buf[pos] = '-';
+    }
+    return String.__valueOf(buf, pos, bufSize);
   }
 
   public static Integer valueOf(int i) {
     if (i > -129 && i < 128) {
       int rebase = i + 128;
-      if (boxedValues[rebase] == null) {
-        boxedValues[rebase] = new Integer(i);
+      Integer result = BoxedValues.boxedValues[rebase];
+      if (result == null) {
+        result = BoxedValues.boxedValues[rebase] = new Integer(i);
       }
-      return boxedValues[rebase];
+      return result;
     }
     return new Integer(i);
   }
@@ -195,6 +219,26 @@
     return new Integer(Integer.parseInt(s, radix));
   }
 
+  private static String toPowerOfTwoString(int value, int shift) {
+    final int bufSize = 32 / shift;
+    int bitMask = (1 << shift) - 1;
+    char[] buf = new char[bufSize];
+    int pos = bufSize - 1;
+    if (value >= 0) {
+      while (value > bitMask) {
+        buf[pos--] = __Digits.digits[value & bitMask];
+        value >>= shift;
+      }
+    } else {
+      while (pos > 0) {
+        buf[pos--] = __Digits.digits[value & bitMask];
+        value >>= shift;
+      }
+    }
+    buf[pos] = __Digits.digits[value & bitMask];
+    return String.__valueOf(buf, pos, bufSize);
+  }
+
   private final transient int value;
 
   public Integer(int value) {
diff --git a/user/super/com/google/gwt/emul/java/lang/Long.java b/user/super/com/google/gwt/emul/java/lang/Long.java
index 9aab04c..97c1f88 100644
--- a/user/super/com/google/gwt/emul/java/lang/Long.java
+++ b/user/super/com/google/gwt/emul/java/lang/Long.java
@@ -19,25 +19,46 @@
  * Wraps a primitive <code>long</code> as an object.
  */
 public final class Long extends Number implements Comparable<Long> {
-  public static final long MIN_VALUE = 0x8000000000000000L;
-  public static final long MAX_VALUE = 0x7fffffffffffffffL;
-  public static final int SIZE = 64;
+  /**
+   * Use nested class to avoid clinit on outer.
+   */
+  static class BoxedValues {
+    // Box values according to JLS - between -128 and 127
+    static Long[] boxedValues = new Long[256];
+  }
 
-  // Box values according to JLS - between -128 and 127
-  private static Long[] boxedValues = new Long[256];
+  static class HexLookup {
+    /**
+     * Super fast char->digit conversion.
+     */
+    static int[] hexLookup = new int[0];
 
-  public static int bitCount(long i) {
-    int cnt = 0;
-    for (long q = MIN_VALUE; q > 0; q = q >> 1) {
-      if ((q & i) != 0) {
-        cnt++;
+    static {
+      for (char c = '0'; c <= '9'; ++c) {
+        hexLookup[c] = c - '0';
+      }
+      for (char c = 'A'; c <= 'F'; ++c) {
+        hexLookup[c] = c - 'A' + 10;
+      }
+      for (char c = 'a'; c <= 'f'; ++c) {
+        hexLookup[c] = c - 'a' + 10;
       }
     }
-    return cnt;
+  }
+
+  public static final long MAX_VALUE = 0x7fffffffffffffffL;
+  public static final long MIN_VALUE = 0x8000000000000000L;
+  public static final int SIZE = 64;
+
+  public static int bitCount(long i) {
+    int high = (int) (i >> 32);
+    int low = (int) i;
+    return Integer.bitCount(high) + Integer.bitCount(low);
   }
 
   public static Long decode(String s) throws NumberFormatException {
-    return new Long(__decodeAndValidateLong(s, MIN_VALUE, MAX_VALUE));
+    __Decode decode = __decodeNumberString(s);
+    return new Long(parseLong(decode.payload, decode.radix));
   }
 
   /**
@@ -48,50 +69,33 @@
   }
 
   public static long highestOneBit(long i) {
-    if (i < 0) {
-      return MIN_VALUE;
+    int high = (int) (i >> 32);
+    if (high != 0) {
+      return ((long) Integer.highestOneBit(high)) << 32;
     } else {
-      long rtn;
-      for (rtn = 0x4000000000000000L; (rtn >> 1) > i; rtn = rtn >> 1) {
-        // loop down until smaller
-      }
-      return rtn;
+      return Integer.highestOneBit((int) i);
     }
   }
 
   public static long lowestOneBit(long i) {
-    if (i == 0) {
-      return SIZE;
-    } else {
-      long r = 1;
-      while ((r & i) != 0) {
-        r = r << 1;
-      }
-      return r;
-    }
+    return i & -i;
   }
 
   public static int numberOfLeadingZeros(long i) {
-    if (i < 0) {
-      return 0;
-    } else if (i == 0) {
-      return SIZE;
+    int high = (int) (i >> 32);
+    if (high != 0) {
+      return Integer.numberOfLeadingZeros(high);
     } else {
-      return SIZE - 1 - (int) Math.floor(Math.log(i) / Math.log(2.0d));
+      return Integer.numberOfLeadingZeros((int) i) + 32;
     }
   }
 
   public static int numberOfTrailingZeros(long i) {
-    if (i < 0) {
-      return 0;
-    } else if (i == 0) {
-      return SIZE;
+    int low = (int) i;
+    if (low != 0) {
+      return Integer.numberOfTrailingZeros(low);
     } else {
-      int rtn = 0;
-      for (int r = 1; (r & i) != 0; r = r * 2) {
-        rtn++;
-      }
-      return rtn;
+      return Integer.numberOfTrailingZeros((int) (i >> 32)) + 32;
     }
   }
 
@@ -99,30 +103,66 @@
     return parseLong(s, 10);
   }
 
-  public static long parseLong(String s, int radix)
+  public static long parseLong(String orig, int intRadix)
       throws NumberFormatException {
-    return __parseAndValidateLong(s, radix, MIN_VALUE, MAX_VALUE);
+    if (orig == null) {
+      throw new NumberFormatException("null");
+    }
+    if (intRadix < Character.MIN_RADIX || intRadix > Character.MAX_RADIX) {
+      throw new NumberFormatException("radix " + intRadix + " out of range");
+    }
+
+    boolean neg = false;
+    String s;
+    if (orig.charAt(0) == '-') {
+      neg = true;
+      s = orig.substring(1);
+    } else {
+      s = orig;
+    }
+
+    long result = 0;
+    if (intRadix == 16) {
+      result = parseHex(s);
+    } else {
+      // Cache a converted version for performance (pure long ops are faster).
+      long radix = intRadix;
+      for (int i = 0, len = s.length(); i < len; ++i) {
+        if (result < 0) {
+          throw NumberFormatException.forInputString(s);
+        }
+        result *= radix;
+        char c = s.charAt(i);
+        int value = Character.digit(c, intRadix);
+        if (value < 0) {
+          throw NumberFormatException.forInputString(s);
+        }
+        result += value;
+      }
+    }
+
+    if (result < 0 && result != MIN_VALUE) {
+      throw NumberFormatException.forInputString(s);
+    }
+    if (neg) {
+      return -result;
+    } else {
+      return result;
+    }
   }
 
   public static long reverse(long i) {
-    long acc = 0;
-    long front = MIN_VALUE;
-    int back = 1;
-    int swing = SIZE - 1;
-    while (swing > 15) {
-      acc = acc | ((i & front) >> swing) | ((i & back) << swing);
-      swing--;
-      front = front >> 1;
-      back = back << 1;
-    }
-    return acc;
+    int high = (int) (i >>> 32);
+    int low = (int) i;
+    return ((long) Integer.reverse(low) << 32)
+        | (Integer.reverse(high) & 0xffffffffL);
   }
 
   public static long reverseBytes(long i) {
-    return ((i & 0xffL) << 56) | ((i & 0xff00L) << 40)
-        | ((i & 0xff0000L) << 24) | ((i & 0xff000000L) << 8)
-        | ((i & 0xff00000000L) >> 8) | ((i & 0xff0000000000L) >> 24)
-        | ((i & 0xff000000000000L) >> 40) | ((i & 0xff00000000000000L) >> 56);
+    int high = (int) (i >>> 32);
+    int low = (int) i;
+    return ((long) Integer.reverseBytes(low) << 32)
+        | (Integer.reverseBytes(high) & 0xffffffffL);
   }
 
   public static long rotateLeft(long i, int distance) {
@@ -133,10 +173,17 @@
   }
 
   public static long rotateRight(long i, int distance) {
+    long ui = i & MAX_VALUE; // avoid sign extension
+    long carry = (i < 0) ? 0x4000000000000000L : 0; // MIN_VALUE rightshifted 1
     while (distance-- > 0) {
-      i = ((i & 1) == 0 ? 0 : 0x80000000) | i >> 1;
+      long nextcarry = ui & 1;
+      ui = carry | (ui >> 1);
+      carry = (nextcarry == 0) ? 0 : 0x4000000000000000L;
     }
-    return i;
+    if (carry != 0) {
+      ui = ui | MIN_VALUE;
+    }
+    return ui;
   }
 
   public static int signum(long i) {
@@ -149,43 +196,54 @@
     }
   }
 
-  public static String toBinaryString(long x) {
-    if (x == 0) {
-      return "0";
-    }
-    String binStr = "";
-    while (x != 0) {
-      int bit = (int) x & 0x1;
-      binStr = __hexDigits[bit] + binStr;
-      x = x >>> 1;
-    }
-    return binStr;
+  public static String toBinaryString(long value) {
+    return toPowerOfTwoString(value, 1);
   }
 
-  public static String toHexString(long x) {
-    if (x == 0) {
-      return "0";
-    }
-    String hexStr = "";
-    while (x != 0) {
-      int nibble = (int) x & 0xF;
-      hexStr = __hexDigits[nibble] + hexStr;
-      x = x >>> 4;
-    }
-    return hexStr;
+  public static String toHexString(long value) {
+    return toPowerOfTwoString(value, 4);
   }
 
-  public static String toString(long b) {
-    return String.valueOf(b);
+  public static String toString(long value) {
+    return String.valueOf(value);
+  }
+
+  public static String toString(long value, int intRadix) {
+    if (intRadix == 10 || intRadix < Character.MIN_RADIX
+        || intRadix > Character.MAX_RADIX) {
+      return String.valueOf(value);
+    }
+
+    final int bufSize = 65;
+    char[] buf = new char[bufSize];
+    int pos = bufSize - 1;
+    // Cache a converted version for performance (pure long ops are faster).
+    long radix = intRadix;
+    if (value >= 0) {
+      while (value >= radix) {
+        buf[pos--] = __Digits.digits[(int) (value % radix)];
+        value /= radix;
+      }
+      buf[pos] = __Digits.digits[(int) value];
+    } else {
+      while (value <= -radix) {
+        buf[pos--] = __Digits.digits[(int) -(value % radix)];
+        value /= radix;
+      }
+      buf[pos--] = __Digits.digits[(int) -value];
+      buf[pos] = '-';
+    }
+    return String.__valueOf(buf, pos, bufSize);
   }
 
   public static Long valueOf(long i) {
     if (i > -129 && i < 128) {
       int rebase = (int) i + 128;
-      if (boxedValues[rebase] == null) {
-        boxedValues[rebase] = new Long(i);
+      Long result = BoxedValues.boxedValues[rebase];
+      if (result == null) {
+        result = BoxedValues.boxedValues[rebase] = new Long(i);
       }
-      return boxedValues[rebase];
+      return result;
     }
     return new Long(i);
   }
@@ -198,6 +256,49 @@
     return new Long(Long.parseLong(s, radix));
   }
 
+  private static native int hexDigit(char c, String s) /*-{
+    var val = @java.lang.Long.HexLookup::hexLookup[c];
+    if (val == null) {
+      throw @java.lang.NumberFormatException::forInputString(Ljava/lang/String;)(s);
+    }
+    return val;
+  }-*/;
+
+  private static long parseHex(String s) {
+    // TODO: make faster using int math!
+    int len = s.length();
+    if (len > 16) {
+      throw NumberFormatException.forInputString(s);
+    }
+    long result = 0;
+    for (int i = 0; i < len; ++i) {
+      result <<= 4;
+      result += hexDigit(s.charAt(i), s);
+    }
+    return result;
+  }
+
+  private static String toPowerOfTwoString(long value, int shift) {
+    // TODO: make faster using int math!
+    final int bufSize = 64 / shift;
+    long bitMask = (1 << shift) - 1;
+    char[] buf = new char[bufSize];
+    int pos = bufSize - 1;
+    if (value >= 0) {
+      while (value > bitMask) {
+        buf[pos--] = __Digits.digits[(int) (value & bitMask)];
+        value >>= shift;
+      }
+    } else {
+      while (pos > 0) {
+        buf[pos--] = __Digits.digits[(int) (value & bitMask)];
+        value >>= shift;
+      }
+    }
+    buf[pos] = __Digits.digits[(int) (value & bitMask)];
+    return String.__valueOf(buf, pos, bufSize);
+  }
+
   private final transient long value;
 
   public Long(long value) {
diff --git a/user/super/com/google/gwt/emul/java/lang/Math.java b/user/super/com/google/gwt/emul/java/lang/Math.java
index 5ff0aed..debd2df 100644
--- a/user/super/com/google/gwt/emul/java/lang/Math.java
+++ b/user/super/com/google/gwt/emul/java/lang/Math.java
@@ -201,9 +201,9 @@
     }
   };
 
-  public static native long round(double x) /*-{
-    return Math.round(x);
-  }-*/;
+  public static long round(double x) {
+    return (long) round0(x);
+  }
 
   public static native int round(float x) /*-{
     return Math.round(x);
@@ -277,6 +277,10 @@
     return x * PI_OVER_180;
   }
 
+  private static native double round0(double x) /*-{
+    return Math.round(x);
+  }-*/;
+
   /* NYI: Java 1.5 includes this, but JS doesn't give us the ingredients.
   public static double ulp (double x) {
   };
diff --git a/user/super/com/google/gwt/emul/java/lang/Number.java b/user/super/com/google/gwt/emul/java/lang/Number.java
index 2360eff..f3d2e3a 100644
--- a/user/super/com/google/gwt/emul/java/lang/Number.java
+++ b/user/super/com/google/gwt/emul/java/lang/Number.java
@@ -1,12 +1,12 @@
 /*
  * 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
@@ -25,37 +25,48 @@
 public abstract class Number implements Serializable {
 
   /**
-   *  Stores a regular expression object to verify format of float values.
+   * Stores a regular expression object to verify format of float values.
    */
   protected static JavaScriptObject floatRegex;
 
   // CHECKSTYLE_OFF: A special need to use unusual identifiers to avoid
   // introducing name collisions.
 
-  /**
-   * @skip
-   */
-  protected static String[] __hexDigits = new String[] {
-      "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d",
-      "e", "f"};
+  static class __Decode {
+    public final String payload;
+    public final int radix;
 
-  static {
-    initNative();
+    public __Decode(int radix, String payload) {
+      this.radix = radix;
+      this.payload = payload;
+    }
   }
 
-  private static native void initNative() /*-{
-    @java.lang.Number::floatRegex = /^[+-]?\d*\.?\d*(e[+-]?\d+)?$/i;
-  }-*/;
+  /**
+   * Use nested class to avoid clinit on outer.
+   */
+  static class __Digits {
+    final static char[] digits = {
+        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
+        'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
+        's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
+  }
 
   /**
    * @skip
-   *
+   * 
    * This function will determine the radix that the string is expressed in
    * based on the parsing rules defined in the Javadocs for Integer.decode() and
-   * invoke __parseAndValidateLong.
+   * invoke __parseAndValidateInt.
    */
-  protected static long __decodeAndValidateLong(String s, long lowerBound,
-      long upperBound) throws NumberFormatException {
+  protected static long __decodeAndValidateInt(String s, int lowerBound,
+      int upperBound) throws NumberFormatException {
+    __Decode decode = __decodeNumberString(s);
+    return __parseAndValidateInt(decode.payload, decode.radix, lowerBound,
+        upperBound);
+  }
+
+  protected static __Decode __decodeNumberString(String s) {
     final boolean negative;
     if (s.startsWith("-")) {
       negative = true;
@@ -76,50 +87,16 @@
     } else {
       radix = 10;
     }
-    
+
     if (negative) {
       s = "-" + s;
     }
-    
-    return __parseAndValidateLong(s, radix, lowerBound, upperBound);
+    return new __Decode(radix, s);
   }
 
   /**
    * @skip
-   *
-   * This function contains common logic for parsing a String in a given
-   * radix and validating the result.
-   */
-  protected static long __parseAndValidateLong(String s, int radix,
-      long lowerBound, long upperBound) throws NumberFormatException {
-  
-    if (s == null) {
-      throw new NumberFormatException("Unable to parse null");
-    }
-    int length = s.length();
-    int startIndex = (length > 0) && (s.charAt(0) == '-') ? 1 : 0;
-  
-    for (int i = startIndex; i < length; i++) {
-      if (Character.digit(s.charAt(i), radix) == -1) {
-        throw new NumberFormatException("Could not parse " + s +
-            " in radix " + radix);
-      }
-    }
-
-    long toReturn =  __parseInt(s, radix);
-    if (__isLongNaN(toReturn)) {
-      throw new NumberFormatException("Unable to parse " + s);
-    } else if (toReturn < lowerBound || toReturn > upperBound) {
-      throw new NumberFormatException(
-        "The string " + s + " exceeds the range for the requested data type");
-    }
-    
-    return toReturn;
-  }
-
-  /**
-   * @skip
-   *
+   * 
    * This function contains common logic for parsing a String as a floating-
    * point number and validating the range.
    */
@@ -128,63 +105,84 @@
 
     double toReturn = __parseDouble(s);
 
-    if (__isDoubleNaN(toReturn)) {
-      throw new NumberFormatException("Unable to parse " + s);
+    if (__isNaN(toReturn)) {
+      throw NumberFormatException.forInputString(s);
     }
-    
+
+    return toReturn;
+  }
+
+  /**
+   * @skip
+   * 
+   * This function contains common logic for parsing a String in a given radix
+   * and validating the result.
+   */
+  protected static int __parseAndValidateInt(String s, int radix,
+      int lowerBound, int upperBound) throws NumberFormatException {
+
+    if (s == null) {
+      throw new NumberFormatException("null");
+    }
+    if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
+      throw new NumberFormatException("radix " + radix + " out of range");
+    }
+
+    int length = s.length();
+    int startIndex = (length > 0) && (s.charAt(0) == '-') ? 1 : 0;
+
+    for (int i = startIndex; i < length; i++) {
+      if (Character.digit(s.charAt(i), radix) == -1) {
+        throw NumberFormatException.forInputString(s);
+      }
+    }
+
+    int toReturn = __parseInt(s, radix);
+    if (__isNaN(toReturn)) {
+      throw NumberFormatException.forInputString(s);
+    } else if (toReturn < lowerBound || toReturn > upperBound) {
+      throw NumberFormatException.forInputString(s);
+    }
+
     return toReturn;
   }
 
   /**
    * @skip
    */
-  private static native boolean __isDoubleNaN(double x) /*-{
+  private static native boolean __isNaN(double x) /*-{
     return isNaN(x);
   }-*/;
 
   /**
    * @skip
-   */
-  private static native boolean __isLongNaN(long x) /*-{
-    return isNaN(x);
-  }-*/;
-
-  /**
-   * @skip
-   *
-   * Invokes the global JS function <code>parseInt()</code>.
-   */
-  private static native long __parseInt(String s, int radix) /*-{
-    return parseInt(s, radix);
-  }-*/;
-  
-  /**
-   * @skip
-   *
+   * 
    * @return The floating-point representation of <code>str</code> or
-   *           <code>Number.NaN</code> if the string does not match
-   *           {@link floatRegex}.
+   *         <code>Number.NaN</code> if the string does not match
+   *         {@link floatRegex}.
    */
   private static native double __parseDouble(String str) /*-{
-    if (@java.lang.Number::floatRegex.test(str)) {
+    var floatRegex = @java.lang.Number::floatRegex;
+    if (!floatRegex) {
+      floatRegex = @java.lang.Number::floatRegex = /^[+-]?\d*\.?\d*(e[+-]?\d+)?$/i;
+    }
+    if (floatRegex.test(str)) {
       return parseFloat(str);
     } else {
       return Number.NaN;
     }
   }-*/;
 
-  // CHECKSTYLE_ON
-
   /**
-   *  Used by JSNI methods to report badly formatted strings.
-   *  @param s the unparseable string
-   *  @throws NumberFormatException every time
+   * @skip
+   * 
+   * Invokes the global JS function <code>parseInt()</code>.
    */
-  @SuppressWarnings("unused") // called by JSNI
-  private static void throwNumberFormatException(String s)
-      throws NumberFormatException {
-    throw new NumberFormatException("Could not parse " + s);
-  }
+  private static native int __parseInt(String s, int radix) /*-{
+    return parseInt(s, radix);
+  }-*/;
+
+  // CHECKSTYLE_ON
 
   public abstract byte byteValue();
 
diff --git a/user/super/com/google/gwt/emul/java/lang/NumberFormatException.java b/user/super/com/google/gwt/emul/java/lang/NumberFormatException.java
index 7754e0b..c98608c 100644
--- a/user/super/com/google/gwt/emul/java/lang/NumberFormatException.java
+++ b/user/super/com/google/gwt/emul/java/lang/NumberFormatException.java
@@ -22,11 +22,14 @@
  */
 public class NumberFormatException extends IllegalArgumentException {
 
+  static NumberFormatException forInputString(String s) {
+    return new NumberFormatException("For input string: \"" + s + "\"");
+  }
+
   public NumberFormatException() {
   }
 
   public NumberFormatException(String message) {
     super(message);
   }
-
 }
diff --git a/user/super/com/google/gwt/emul/java/lang/Short.java b/user/super/com/google/gwt/emul/java/lang/Short.java
index a570d95..a27fa68 100644
--- a/user/super/com/google/gwt/emul/java/lang/Short.java
+++ b/user/super/com/google/gwt/emul/java/lang/Short.java
@@ -24,11 +24,16 @@
   public static final short MAX_VALUE = (short) 0x7fff;
   public static final int SIZE = 16;
 
-  // Box values according to JLS - between -128 and 127
-  private static Short[] boxedValues = new Short[256];
+  /**
+   * Use nested class to avoid clinit on outer.
+   */
+  private static class BoxedValues {
+    // Box values according to JLS - between -128 and 127
+    private static Short[] boxedValues = new Short[256];
+  }
 
   public static Short decode(String s) throws NumberFormatException {
-    return new Short((short) __decodeAndValidateLong(s, MIN_VALUE, MAX_VALUE));
+    return new Short((short) __decodeAndValidateInt(s, MIN_VALUE, MAX_VALUE));
   }
 
   /**
@@ -44,7 +49,7 @@
 
   public static short parseShort(String s, int radix)
       throws NumberFormatException {
-    return (short) __parseAndValidateLong(s, radix, MIN_VALUE, MAX_VALUE);
+    return (short) __parseAndValidateInt(s, radix, MIN_VALUE, MAX_VALUE);
   }
 
   public static short reverseBytes(short s) {
@@ -58,10 +63,11 @@
   public static Short valueOf(short s) {
     if (s > -129 && s < 128) {
       int rebase = s + 128;
-      if (boxedValues[rebase] == null) {
-        boxedValues[rebase] = new Short(s);
+      Short result = BoxedValues.boxedValues[rebase];
+      if (result == null) {
+        result = BoxedValues.boxedValues[rebase] = new Short(s);
       }
-      return boxedValues[rebase];
+      return result;
     }
     return new Short(s);
   }
diff --git a/user/super/com/google/gwt/emul/java/lang/System.java b/user/super/com/google/gwt/emul/java/lang/System.java
index 1811bee..9a2c21c 100644
--- a/user/super/com/google/gwt/emul/java/lang/System.java
+++ b/user/super/com/google/gwt/emul/java/lang/System.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -84,18 +84,18 @@
       nativeArraycopy(src, srcOfs, dest, destOfs, len);
     }
   }
-  
-  public static native long currentTimeMillis() /*-{
-    return (new Date()).getTime();
-  }-*/;
+
+  public static long currentTimeMillis() {
+    return (long) currentTimeMillis0();
+  };
 
   /**
    * Has no effect; just here for source compatibility.
    * 
    * @skip
    */
-  public static native void gc() /*-{
-  }-*/;
+  public static void gc() {
+  };
 
   public static native int identityHashCode(Object o) /*-{
     return (o == null) ? 0 : @com.google.gwt.core.client.Impl::getHashCode(Ljava/lang/Object;)(o);
@@ -109,6 +109,10 @@
     @java.lang.System::out = out;
   }-*/;
 
+  private static native double currentTimeMillis0() /*-{
+    return (new Date()).getTime();
+  }-*/;
+
   /**
    * Returns the length of an array via Javascript.
    */
diff --git a/user/super/com/google/gwt/emul/java/util/Date.java b/user/super/com/google/gwt/emul/java/util/Date.java
index bc730e3..1b55c4a 100644
--- a/user/super/com/google/gwt/emul/java/util/Date.java
+++ b/user/super/com/google/gwt/emul/java/util/Date.java
@@ -36,15 +36,8 @@
       "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
   };
 
-  // CHECKSTYLE_OFF: The underscore prefix is an old convention that could be
-  // easily replaced.
-  public static native long __parse(String s) /*-{
-    var d = Date.parse(s);
-    return isNaN(d) ? -1 : d;
-  }-*/;
-
   public static long parse(String s) {
-    long d = __parse(s);
+    long d = (long) parse0(s);
     if (d != -1) {
       return d;
     } else {
@@ -53,10 +46,11 @@
   }
 
   // CHECKSTYLE_OFF: Matching the spec.
-  public static native long UTC(int year, int month, int date, int hrs,
-      int min, int sec) /*-{
-    return Date.UTC(year + 1900, month, date, hrs, min, sec);
-  }-*/;
+  public static long UTC(int year, int month, int date, int hrs,
+      int min, int sec) {
+    return (long) utc0(year, month, date, hrs, min, sec);
+  }
+  // CHECKSTYLE_ON
 
   /**
    *  Return the names for the days of the week as specified by the Date
@@ -89,6 +83,16 @@
     }
   }
 
+  private static native double parse0(String s) /*-{
+    var d = Date.parse(s);
+    return isNaN(d) ? -1 : d;
+  }-*/;
+
+  private static native double utc0(int year, int month, int date, int hrs,
+      int min, int sec) /*-{
+    return Date.UTC(year + 1900, month, date, hrs, min, sec);
+  }-*/;
+
   public Date() {
     init();
   }
@@ -166,9 +170,9 @@
     return this.jsdate.getSeconds();
   }-*/;
 
-  public native long getTime() /*-{
-    return this.jsdate.getTime();
-  }-*/;
+  public long getTime() {
+    return (long) getTime0();
+  }
 
   public native int getTimezoneOffset() /*-{
     return this.jsdate.getTimezoneOffset();
@@ -199,17 +203,13 @@
     this.jsdate.setMonth(month);
   }-*/;
 
-  // CHECKSTYLE_ON
-
   public native void setSeconds(int seconds) /*-{
     this.jsdate.setSeconds(seconds);
-  }-*/;;
-
-  public native void setTime(long time) /*-{
-    this.jsdate.setTime(time);
   }-*/;
 
-  // CHECKSTYLE_ON
+  public void setTime(long time) {
+    setTime0(time);
+  }
 
   public native void setYear(int year) /*-{
     this.jsdate.setFullYear(year + 1900);
@@ -259,10 +259,18 @@
         + " " + d.getFullYear();
   }-*/;
 
+  private native double getTime0() /*-{
+    return this.jsdate.getTime();
+  }-*/;
+
   private native void init() /*-{
     this.jsdate = new Date();
   }-*/;
 
+  private native void init(double date) /*-{
+    this.jsdate = new Date(date);
+  }-*/;
+
   private native void init(int year, int month, int date, int hrs, int min,
       int sec) /*-{
     this.jsdate = new Date();
@@ -270,7 +278,7 @@
     this.jsdate.setHours(hrs, min, sec, 0);
   }-*/;
 
-  private native void init(long date) /*-{
-    this.jsdate = new Date(date);
+  private native void setTime0(double time) /*-{
+    this.jsdate.setTime(time);
   }-*/;
 }
diff --git a/user/test/com/google/gwt/dev/jjs/test/HostedTest.java b/user/test/com/google/gwt/dev/jjs/test/HostedTest.java
index 9df5500..6359842 100644
--- a/user/test/com/google/gwt/dev/jjs/test/HostedTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/HostedTest.java
@@ -148,6 +148,10 @@
     return val;
   }-*/;
 
+  private static native long passThroughLong(long val) /*-{
+    return val;
+  }-*/;
+
   private static native String sFooCall(String s) /*-{
     var func = @com.google.gwt.dev.jjs.test.HostedTest::sFoo(Ljava/lang/String;);
     return func.call(null, s);
@@ -393,8 +397,7 @@
   public void testLongMarshalling() {
     // a big number that cannot accurately be represented as a double
     long l = 1234567890123456789L;
-    double d = l;
-    assertTrue(isEq(l, d));
+    assertEquals(l, passThroughLong(l));
   }
 
   /*
@@ -511,10 +514,6 @@
     return this.@com.google.gwt.dev.jjs.test.HostedTest::foo(Ljava/lang/String;);
   }-*/;
 
-  private native boolean isEq(long l, double d) /*-{
-    return l == d;
-  }-*/;
-
   private native void jsniA()/*-{}-*/;
 
   private native void jsniB()/*-{
diff --git a/user/test/com/google/gwt/dev/jjs/test/NativeLongTest.java b/user/test/com/google/gwt/dev/jjs/test/NativeLongTest.java
index 3c3b692..3d2aabd 100644
--- a/user/test/com/google/gwt/dev/jjs/test/NativeLongTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/NativeLongTest.java
@@ -18,38 +18,166 @@
 import com.google.gwt.junit.client.GWTTestCase;
 
 /**
- * TODO: document me.
+ * Test direct uses of longs. Mostly this tests that LongLib is in fact being
+ * invoked in various cases. The behavior of LongLib itself is tested in
+ * LongLibTest.
  */
 public class NativeLongTest extends GWTTestCase {
+  /*
+   * These silly looking constants are made into public fields so that the
+   * compiler will not constant fold them. The problem is that if you write
+   * assertEquals(2L, 4L/2L), the compiler will emit assertEquals(2L, 2L).
+   */
+  private static long LONG_1234 = 0x1234123412341234L;
+  private static long LONG_1234_DECIMAL = 1234123412341234L;
+  private static long LONG_1234000012340000 = 0x1234000012340000L;
+  private static long LONG_5DEECE66D = 0x5DEECE66DL;
+  private static long LONG_B = 0xBL;
+  private static long LONG_DEADBEEF = 0xdeadbeefdeadbeefL;
+  private static long LONG_DEADBEEF12341234 = 0xdeadbeef12341234L;
+  private static long LONG_FFFFFFFF = 0xFFFFFFFFL;
+  private static long LONG_ONE = 1L;
+  private static long LONG_THREE = 3L;
+  private static long LONG_TWO = 2L;
+  private static long LONG_TWO_PWR_32 = 0x100000000L;
+  private static long LONG_ZERO = 0L;
 
+  @Override
   public String getModuleName() {
     return "com.google.gwt.dev.jjs.CompilerSuite";
   }
 
+  @Override
+  public void setUp() {
+    pretendToChangeTheConstants();
+  }
+
+  public void testArithmetic() {
+    assertEquals(-1089359682551557853L, LONG_1234 + LONG_DEADBEEF);
+    assertEquals(5024439901525534073L, 2 * LONG_1234 - LONG_DEADBEEF);
+    assertEquals(2476047018506819212L, LONG_1234 * LONG_DEADBEEF);
+    assertEquals(-240105308887621659L, LONG_DEADBEEF / 10);
+    assertEquals(-1089359682551557853L, LONG_DEADBEEF % LONG_1234);
+  }
+
+  public void testCasts() {
+    assertEquals(0x12341234, (int) LONG_1234);
+    assertEquals(0x1234, (short) LONG_1234);
+  }
+
   public void testConstants() {
-    assertEquals(0x5DEECE66DL, 0x5DEECE66DL);
-    assertTrue(0x5DEECE66DL > 0L);
-    assertTrue(0L < 0x5DEECE66DL);
-    assertEquals(0xBL, 0xBL);
-    assertTrue(0xBL > 0L);
-    assertTrue(0L < 0xBL);
+    assertEquals(LONG_5DEECE66D, LONG_5DEECE66D);
+    assertTrue(LONG_5DEECE66D > 0L);
+    assertTrue(0L < LONG_5DEECE66D);
+    assertEquals(LONG_B, LONG_B);
+    assertTrue(LONG_B > 0L);
+    assertTrue(0L < LONG_B);
+  }
+
+  public void testFor64Bits() {
+    long x = LONG_1234;
+    pretendToChangeTheConstants();
+    long y = LONG_1234 + LONG_ONE;
+    long z = y - x;
+    // with longs implemented as doubles, z will be 0 instead of 1
+    assertEquals(1L, z);
+  }
+
+  public void testImplicitCastFromLong() {
+    double d = LONG_ONE;
+    d += LONG_TWO;
+    assertEquals(0.0, 3L, d);
+    assertTrue(3L == d);
+
+    float f = LONG_ONE;
+    f += LONG_TWO;
+    assertEquals(0.0, 3L, f);
+    assertTrue(3L == f);
+  }
+
+  public void testImplicitCastToLong() {
+    long l = 10;
+    l += 5;
+    assertEquals(15, l);
+    assertTrue(15 == l);
+  }
+
+  public void testModifyingOps() {
+    long l = 20;
+    l += 10;
+    assertEquals(31, ++l);
+    assertEquals(31, l++);
+    assertEquals(32, l);
   }
 
   public void testLogicalAnd() {
-    assertEquals(1L & 0L, 0L);
-    assertEquals(1L & 3L, 1L);
+    assertEquals(LONG_1234, LONG_1234 & -LONG_ONE);
+    assertEquals(0x12341234L, LONG_1234 & LONG_FFFFFFFF);
+    assertEquals(0L, LONG_ONE & LONG_ZERO);
+    assertEquals(1L, LONG_ONE & LONG_THREE);
+  }
+
+  public void testLogicalOr() {
+    assertEquals(-1L, LONG_1234 | -LONG_ONE);
+    assertEquals(0x12341234FFFFFFFFL, LONG_1234 | LONG_FFFFFFFF);
+    assertEquals(1L, LONG_ONE | LONG_ZERO);
+    assertEquals(3L, LONG_ONE | LONG_THREE);
+  }
+
+  public void testLogicalXor() {
+    assertTrue((255L ^ LONG_5DEECE66D) != 0);
+
+    assertEquals(0L, LONG_1234 ^ LONG_1234);
+    assertEquals(0x0000123400001234L, LONG_1234 ^ LONG_1234000012340000);
+    assertEquals(1L, LONG_ONE ^ LONG_ZERO);
+    assertEquals(2L, LONG_ONE ^ LONG_THREE);
   }
 
   public void testShift() {
-    assertEquals(0x5DEECE66DL, 0x5DEECE66DL & ((1L << 48) - 1));
+    assertEquals(LONG_5DEECE66D, LONG_5DEECE66D & ((LONG_ONE << 48) - 1));
+    assertEquals(LONG_ONE << 12, (LONG_ONE << 60) >>> (48));
+
+    assertTrue((LONG_ONE << 35) > (LONG_ONE << 30));
+
+    assertEquals(1L, LONG_TWO_PWR_32 >> 32);
+    assertEquals(1L, LONG_TWO_PWR_32 >>> 32);
   }
 
-  public void testTripleShift() {
-    assertEquals(1 << 12, (1 << 60) >>> (48));
+  // Issue 1198
+  public void testToHexString() {
+    assertEquals("deadbeef12341234", Long.toHexString(LONG_DEADBEEF12341234));
   }
 
-  public void testXor() {
-    assertTrue((255L ^ 0x5DEECE66DL) != 0);
+  public void testToString() {
+    assertEquals("1234123412341234", "" + LONG_1234_DECIMAL);
   }
 
+  /**
+   * This method tries to trick the compiler into thinking the global constants
+   * are not fixed.
+   */
+  private void pretendToChangeTheConstants() {
+    int i = 10;
+    while (i > 1) {
+      i /= 2;
+    }
+    if (i == 1) {
+      return;
+    }
+
+    // not reached
+    ++LONG_1234;
+    ++LONG_1234_DECIMAL;
+    ++LONG_1234000012340000;
+    ++LONG_5DEECE66D;
+    ++LONG_B;
+    ++LONG_DEADBEEF;
+    ++LONG_DEADBEEF12341234;
+    ++LONG_FFFFFFFF;
+    ++LONG_ONE;
+    ++LONG_THREE;
+    ++LONG_TWO;
+    ++LONG_TWO_PWR_32;
+    ++LONG_ZERO;
+  }
 }
diff --git a/user/test/com/google/gwt/emultest/EmulSuite.java b/user/test/com/google/gwt/emultest/EmulSuite.java
index 6f11f17..7186c61 100644
--- a/user/test/com/google/gwt/emultest/EmulSuite.java
+++ b/user/test/com/google/gwt/emultest/EmulSuite.java
@@ -21,6 +21,7 @@
 import com.google.gwt.emultest.java.lang.DoubleTest;
 import com.google.gwt.emultest.java.lang.FloatTest;
 import com.google.gwt.emultest.java.lang.IntegerTest;
+import com.google.gwt.emultest.java.lang.LongTest;
 import com.google.gwt.emultest.java.lang.ObjectTest;
 import com.google.gwt.emultest.java.lang.ShortTest;
 import com.google.gwt.emultest.java.lang.StringBufferTest;
@@ -56,6 +57,7 @@
     suite.addTestSuite(CharacterTest.class);
     suite.addTestSuite(DoubleTest.class);
     suite.addTestSuite(FloatTest.class);
+    suite.addTestSuite(LongTest.class);
     suite.addTestSuite(IntegerTest.class);
     suite.addTestSuite(ObjectTest.class);
     suite.addTestSuite(ShortTest.class);
diff --git a/user/test/com/google/gwt/emultest/java/lang/IntegerTest.java b/user/test/com/google/gwt/emultest/java/lang/IntegerTest.java
index e33cf6e..3f34e83 100644
--- a/user/test/com/google/gwt/emultest/java/lang/IntegerTest.java
+++ b/user/test/com/google/gwt/emultest/java/lang/IntegerTest.java
@@ -142,7 +142,7 @@
     assertEquals(Integer.MIN_VALUE, Integer.highestOneBit(-1));
     assertEquals(Integer.MIN_VALUE, Integer.highestOneBit(-256));
     assertEquals(1, Integer.highestOneBit(1));
-    assertEquals(0x80, Integer.highestOneBit(0x80));
+    assertEquals(0x80, Integer.highestOneBit(0x88));
     assertEquals(0x40000000, Integer.highestOneBit(Integer.MAX_VALUE));
   }
 
@@ -151,7 +151,7 @@
     assertEquals(1, Integer.lowestOneBit(-1));
     assertEquals(0x100, Integer.lowestOneBit(-256));
     assertEquals(1, Integer.lowestOneBit(1));
-    assertEquals(0x80, Integer.lowestOneBit(0x80));
+    assertEquals(0x80, Integer.lowestOneBit(0x880));
     assertEquals(0x80000000, Integer.lowestOneBit(Integer.MIN_VALUE));
   }
 
@@ -181,6 +181,11 @@
     assertEquals(Integer.MIN_VALUE, Integer.reverse(1));
     assertEquals(1, Integer.reverse(Integer.MIN_VALUE));
     assertEquals(0xaaaaaaaa, Integer.reverse(0x55555555));
+    assertEquals(0xaaaa0000, Integer.reverse(0x00005555));
+    assertEquals(0xaa00aa00, Integer.reverse(0x00550055));
+    assertEquals(0x55555555, Integer.reverse(0xaaaaaaaa));
+    assertEquals(0x00005555, Integer.reverse(0xaaaa0000));
+    assertEquals(0x00550055, Integer.reverse(0xaa00aa00));
   }
 
   public void testReverseBytes() {
@@ -222,7 +227,8 @@
   }
 
   public void testToHexString() {
-    // TODO: not implemented in our JRE
+    assertEquals("12345", Integer.toHexString(0x12345));
+    assertEquals("fff12345", Integer.toHexString(0xFFF12345));
   }
 
   public void testToString() {
diff --git a/user/test/com/google/gwt/emultest/java/lang/LongTest.java b/user/test/com/google/gwt/emultest/java/lang/LongTest.java
new file mode 100644
index 0000000..12b5f70
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java/lang/LongTest.java
@@ -0,0 +1,139 @@
+/*

+ * 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.emultest.java.lang;

+

+import com.google.gwt.junit.client.GWTTestCase;

+

+/**

+ * TODO: document me.

+ */

+public class LongTest extends GWTTestCase {

+

+  public String getModuleName() {

+    return "com.google.gwt.emultest.EmulSuite";

+  }

+

+  public void testBinaryString() {

+    assertEquals("10001111101101101111011110001100100000000",

+        Long.toBinaryString(1234500000000L));

+    assertEquals("0", Long.toBinaryString(0L));

+    assertEquals(

+        "1111111111111111111111101110000010010010000100001110011100000000",

+        Long.toBinaryString(-1234500000000L));

+  }

+

+  public void testBitCount() {

+    assertEquals(0, Long.bitCount(0));

+    assertEquals(1, Long.bitCount(1));

+    assertEquals(64, Long.bitCount(-1));

+    assertEquals(63, Long.bitCount(Long.MAX_VALUE));

+    assertEquals(1, Long.bitCount(Long.MIN_VALUE));

+  }

+

+  public void testConstants() {

+    assertEquals(64, Long.SIZE);

+    assertEquals(0x7fffffffffffffffL, Long.MAX_VALUE);

+    assertEquals(0x8000000000000000L, Long.MIN_VALUE);

+  }

+

+  public void testHighestOneBit() {

+    assertEquals(0, Long.highestOneBit(0));

+    assertEquals(Long.MIN_VALUE, Long.highestOneBit(-1));

+    assertEquals(Long.MIN_VALUE, Long.highestOneBit(-256));

+    assertEquals(1, Long.highestOneBit(1));

+    assertEquals(0x80, Long.highestOneBit(0x88));

+    assertEquals(0x4000000000000000L, Long.highestOneBit(Long.MAX_VALUE));

+  }

+

+  public void testLowestOneBit() {

+    assertEquals(0, Long.lowestOneBit(0));

+    assertEquals(1, Long.lowestOneBit(-1));

+    assertEquals(0x100, Long.lowestOneBit(-256));

+    assertEquals(1, Long.lowestOneBit(1));

+    assertEquals(0x80, Long.lowestOneBit(0x780));

+    assertEquals(Long.MIN_VALUE, Long.lowestOneBit(Long.MIN_VALUE));

+  }

+

+  public void testNumberOfLeadingZeros() {

+    assertEquals(64, Long.numberOfLeadingZeros(0));

+    assertEquals(63, Long.numberOfLeadingZeros(1));

+    assertEquals(0, Long.numberOfLeadingZeros(-1));

+    assertEquals(32, Long.numberOfLeadingZeros(0x80000000L));

+    assertEquals(1, Long.numberOfLeadingZeros(Long.MAX_VALUE));

+    assertEquals(0, Long.numberOfLeadingZeros(Long.MIN_VALUE));

+    assertEquals(0, Long.numberOfLeadingZeros(-0x80000000L));

+  }

+

+  public void testNumberOfTrailingZeros() {

+    assertEquals(64, Long.numberOfTrailingZeros(0));

+    assertEquals(0, Long.numberOfTrailingZeros(1));

+    assertEquals(0, Long.numberOfTrailingZeros(-1));

+    assertEquals(31, Long.numberOfTrailingZeros(0x80000000L));

+    assertEquals(0, Long.numberOfTrailingZeros(Long.MAX_VALUE));

+    assertEquals(63, Long.numberOfTrailingZeros(Long.MIN_VALUE));

+    assertEquals(20, Long.numberOfTrailingZeros(-0x7ff00000L));

+  }

+

+  public void testReverse() {

+    assertEquals(0L, Long.reverse(0L));

+    assertEquals(-1L, Long.reverse(-1L));

+    assertEquals(Long.MIN_VALUE, Long.reverse(1L));

+    assertEquals(1L, Long.reverse(Long.MIN_VALUE));

+    assertEquals(0xaaaaaaaaaaaaaaaaL, Long.reverse(0x5555555555555555L));

+    assertEquals(0xaaaaaaaa00000000L, Long.reverse(0x55555555L));

+    assertEquals(0xaa00aa00aa00aa00L, Long.reverse(0x0055005500550055L));

+    assertEquals(0x5555555555555555L, Long.reverse(0xaaaaaaaaaaaaaaaaL));

+    assertEquals(0x55555555L, Long.reverse(0xaaaaaaaa00000000L));

+    assertEquals(0x0055005500550055L, Long.reverse(0xaa00aa00aa00aa00L));

+  }

+

+  public void testReverseBytes() {

+  }

+

+  public void testRotateLeft() {

+    assertEquals(0, Long.rotateLeft(0, 4));

+    assertEquals(0x2, Long.rotateLeft(1, 1));

+    assertEquals(0x10, Long.rotateLeft(1, 4));

+    assertEquals(-1, Long.rotateLeft(-1, 4));

+    assertEquals(Long.MIN_VALUE, Long.rotateLeft(0x4000000000000000L, 1));

+    assertEquals(1, Long.rotateLeft(Long.MIN_VALUE, 1));

+  }

+

+  public void testRotateRight() {

+    assertEquals(0, Long.rotateRight(0, 4));

+    assertEquals(Long.MIN_VALUE, Long.rotateRight(1, 1));

+    assertEquals(0x1000000000000000L, Long.rotateRight(1, 4));

+    assertEquals(-1, Long.rotateRight(-1, 4));

+  }

+

+  public void testSignum() {

+    assertEquals(0, Long.signum(0));

+    assertEquals(1, Long.signum(1));

+    assertEquals(-1, Long.signum(-1));

+    assertEquals(1, Long.signum(Long.MAX_VALUE));

+    assertEquals(-1, Long.signum(Long.MIN_VALUE));

+  }

+

+  public void testStaticValueOf() {

+    assertEquals(Long.MIN_VALUE, Long.valueOf(Long.MIN_VALUE).longValue());

+    assertEquals(Long.MAX_VALUE, Long.valueOf(Long.MAX_VALUE).longValue());

+  }

+

+  public void testToHexString() {

+    assertEquals("1234500000000", Long.toHexString(0x1234500000000L));

+    assertEquals("fff1234500000000", Long.toHexString(0xFFF1234500000000L));

+  }

+}

diff --git a/user/test/com/google/gwt/emultest/java/util/DateTest.java b/user/test/com/google/gwt/emultest/java/util/DateTest.java
index d4753de..3dc1240 100644
--- a/user/test/com/google/gwt/emultest/java/util/DateTest.java
+++ b/user/test/com/google/gwt/emultest/java/util/DateTest.java
@@ -1,12 +1,12 @@
 /*
- * Copyright 2007 Google Inc.
- *
+ * Copyright 2008 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
@@ -20,23 +20,25 @@
 import java.util.Date;
 
 /**
- * Tests for GWT's emulation of the JRE Date class. 
+ * Tests for GWT's emulation of the JRE Date class.
  */
+@SuppressWarnings("deprecation")
 public class DateTest extends GWTTestCase {
   public static final String CURRENT = "CURRENT";
-  public static final String TO_STRING_PATTERN =
-      "\\w{3} \\w{3} \\d{2} \\d{2}:\\d{2}:\\d{2}( .+)? \\d{4}";
+  public static final String TO_STRING_PATTERN = "\\w{3} \\w{3} \\d{2} \\d{2}:\\d{2}:\\d{2}( .+)? \\d{4}";
   public static final long DAY_MILLISECONDS_SHIFT = 27;
   public static final String FUTURE = "FUTURE";
   public static final String PAST = "PAST";
   public static final long SECOND_MILLISECONDS_SHIFT = 10;
 
-  /** Sets module name so that javascript compiler can operate */
+  /**
+   * Sets module name so that javascript compiler can operate.
+   */
   public String getModuleName() {
     return "com.google.gwt.emultest.EmulSuite";
   }
 
-  /** Testing for public boolean java.util.Date.after(java.util.Date)* */
+  /** Testing for public boolean java.util.Date.after(java.util.Date). */
   public void testAfter() {
 
     // /////////////////////////////
@@ -63,7 +65,7 @@
     assertTrue(a2);
   }
 
-  /** Testing for public boolean java.util.Date.before(java.util.Date)* */
+  /** Testing for public boolean java.util.Date.before(java.util.Date). */
   public void testBefore() {
 
     // /////////////////////////////
@@ -90,7 +92,7 @@
     assertFalse(a2);
   }
 
-  /** Testing for public java.lang.Object java.util.Date.clone()* */
+  /** Testing for public java.lang.Object java.util.Date.clone(). */
   public void testClone() {
 
     // /////////////////////////////
@@ -117,7 +119,7 @@
     assertEquals(a2, accum2);
   }
 
-  /** Testing for public int java.util.Date.compareTo(java.util.Date)* */
+  /** Testing for public int java.util.Date.compareTo(java.util.Date). */
   public void testCompareTo() {
 
     // /////////////////////////////
@@ -143,7 +145,7 @@
     assertEquals(a2, -1);
   }
 
-  /** Testing for public int java.util.Date.getDate()* */
+  /** Testing for public int java.util.Date.getDate(). */
   public void testGetDate() {
 
     // /////////////////////////////
@@ -160,7 +162,7 @@
     assertEquals(29, a2);
   }
 
-  /** Testing for public int java.util.Date.getDay()* */
+  /** Testing for public int java.util.Date.getDay(). */
   public void testGetDay() {
 
     // /////////////////////////////
@@ -182,15 +184,13 @@
     int a2 = accum2.getDay();
   }
 
-  /**
-   * Testing for public int java.util.Date.getHours()
-   */
+  /** Testing for public int java.util.Date.getHours(). */
   public void testGetHours() {
     // Cannot be done because each time zone will give a different
     // answer
   }
 
-  /** Testing for public int java.util.Date.getMinutes()* */
+  /** Testing for public int java.util.Date.getMinutes(). */
   public void testGetMinutes() {
 
     // /////////////////////////////
@@ -207,7 +207,7 @@
     assertEquals(a2, 4);
   }
 
-  /** Testing for public int java.util.Date.getMonth()* */
+  /** Testing for public int java.util.Date.getMonth(). */
   public void testGetMonth() {
 
     // /////////////////////////////
@@ -225,7 +225,7 @@
     assertEquals(11, a2);
   }
 
-  /** Testing for public int java.util.Date.getSeconds()* */
+  /** Testing for public int java.util.Date.getSeconds(). */
   public void testGetSeconds() {
 
     // /////////////////////////////
@@ -243,7 +243,7 @@
     assertEquals(5, a2);
   }
 
-  /** Testing for public long java.util.Date.getTime()* */
+  /** Testing for public long java.util.Date.getTime(). */
   public void testGetTime() {
 
     // /////////////////////////////
@@ -261,7 +261,7 @@
     assertEquals(1293678245000L, a2);
   }
 
-  /** Testing for public int java.util.Date.getTimezoneOffset()* */
+  /** Testing for public int java.util.Date.getTimezoneOffset(). */
   public void testGetTimezoneOffset() {
 
     // /////////////////////////////
@@ -283,7 +283,7 @@
     int a2 = accum2.getTimezoneOffset();
   }
 
-  /** Testing for public int java.util.Date.getYear()* */
+  /** Testing for public int java.util.Date.getYear(). */
   public void testGetYear() {
 
     // /////////////////////////////
@@ -301,7 +301,7 @@
     assertEquals(110, a2);
   }
 
-  /** Testing for public static long java.util.Date.parse(java.lang.String)* */
+  /** Testing for public static long java.util.Date.parse(java.lang.String). */
   public void testParse() {
 
     // /////////////////////////////
@@ -328,7 +328,7 @@
     assertEquals(1293678245000L, a2);
   }
 
-  /** Testing for public void java.util.Date.setDate(int)* */
+  /** Testing for public void java.util.Date.setDate(int). */
   public void testSetDate() {
     // We only go through dates from 0-28 here. There are some months that do
     // not
@@ -357,7 +357,7 @@
     assertEquals(dateWithThirtyDays.getDate(), newDayNum - numDaysInOldMonth);
   }
 
-  /** Testing for public void java.util.Date.setHours(int)* */
+  /** Testing for public void java.util.Date.setHours(int). */
   public void testSetHours() {
     for (int i = 0; i < 24; i++) {
       Date accum0 = create();
@@ -366,7 +366,7 @@
     }
   }
 
-  /** Testing for public void java.util.Date.setMinutes(int)* */
+  /** Testing for public void java.util.Date.setMinutes(int). */
   public void testSetMinutes() {
     for (int i = 0; i < 24; i++) {
       Date accum0 = create();
@@ -375,7 +375,7 @@
     }
   }
 
-  /** Testing for public void java.util.Date.setMonth(int)* */
+  /** Testing for public void java.util.Date.setMonth(int). */
   public void testSetMonth() {
     for (int i = 0; i < 12; i++) {
       // We want to use a fixed date here. If we use the current date, the
@@ -393,7 +393,7 @@
    * We want to test to see that if we are currently in a month with 31 days and
    * we set the month to one which has less than 31 days, that the month
    * returned by the date class will be one higher than the month that we
-   * originally set (according to the spec of java.util.date)
+   * originally set (according to the spec of java.util.date).
    */
   public void testSetInvalidMonthForDate() {
     int dayNum = 31;
@@ -405,7 +405,7 @@
     assertEquals(dateWithThirtyOneDays.getDate(), dayNum - numDaysInNewMonth);
   }
 
-  /** Testing for public void java.util.Date.setSeconds(int)* */
+  /** Testing for public void java.util.Date.setSeconds(int). */
   public void testSetSeconds() {
     for (int i = 0; i < 24; i++) {
       Date accum0 = create();
@@ -414,7 +414,7 @@
     }
   }
 
-  /** Testing for public void java.util.Date.setTime(long)* */
+  /** Testing for public void java.util.Date.setTime(long). */
   public void testSetTime() {
     long[] values = new long[] {-100000000000L, -100L, 0, 100L, 1000000000L};
     for (int i = 0; i < values.length; i++) {
@@ -424,7 +424,7 @@
     }
   }
 
-  /** Testing for public void java.util.Date.setYear(int)* */
+  /** Testing for public void java.util.Date.setYear(int). */
   public void testSetYear() {
     for (int i = 1880; i < 2050; i++) {
       // We want to use a fixed date here. If we use the current date, the
@@ -455,7 +455,7 @@
 
   /**
    * We want to test to see that if the date is Feb 29th (in a leap year) and we
-   * set the year to another leap year, that the month and day will be retained
+   * set the year to another leap year, that the month and day will be retained.
    */
   public void testSetValidLeapYearForDate() {
     int dayNum = 29;
@@ -469,7 +469,7 @@
     assertEquals(leapYearDate.getDate(), dayNum);
   }
 
-  /** Testing for public java.lang.String java.util.Date.toGMTString()* */
+  /** Testing for public java.lang.String java.util.Date.toGMTString(). */
   public void testToGMTString() {
 
     // /////////////////////////////
@@ -487,7 +487,7 @@
     assertEquals("30 Dec 2010 03:04:05 GMT", a2);
   }
 
-  /** Testing for public java.lang.String java.util.Date.toLocaleString()* */
+  /** Testing for public java.lang.String java.util.Date.toLocaleString(). */
   public void testToLocaleString() {
 
     // /////////////////////////////
@@ -504,7 +504,7 @@
     assertTrue(a2.indexOf("2010") != -1);
   }
 
-  /** Date docs specify an exact format for toString() */
+  /** Date docs specify an exact format for toString(). */
   public void testToString() {
     // /////////////////////////////
     // Past
@@ -513,8 +513,8 @@
     String s = d.toString();
 
     assertTrue("Bad format " + s, s.matches(TO_STRING_PATTERN));
-    assertEquals("Parsing returned unequal dates from " + s,
-        d, new Date(Date.parse(s)));
+    assertEquals("Parsing returned unequal dates from " + s, d, new Date(
+        Date.parse(s)));
 
     // /////////////////////////////
     // Future
@@ -523,11 +523,11 @@
     s = d.toString();
 
     assertTrue("Bad format " + s, s.matches(TO_STRING_PATTERN));
-    assertEquals("Parsing returned unequal dates from " + s,
-        d, new Date(Date.parse(s)));
+    assertEquals("Parsing returned unequal dates from " + s, d, new Date(
+        Date.parse(s)));
   }
 
-  /** Testing for public static long java.util.Date.UTC(int,int,int,int,int,int)* */
+  /** Testing for public static long java.util.Date.UTC(int,int,int,int,int,int). */
   public void testUTC() {
 
     // /////////////////////////////
diff --git a/user/test/com/google/gwt/i18n/client/NumberFormat_fr_Test.java b/user/test/com/google/gwt/i18n/client/NumberFormat_fr_Test.java
index 33ca691..fcc5abc 100644
--- a/user/test/com/google/gwt/i18n/client/NumberFormat_fr_Test.java
+++ b/user/test/com/google/gwt/i18n/client/NumberFormat_fr_Test.java
@@ -34,9 +34,8 @@
    * Add as many tests as you like.
    */
   public void testBasicFormat() {
-    String str = NumberFormat.getFormat("0.0000").format(
-        123.45789179565757f);
-    assertTrue(str.equals("123,4579"));
+    String str = NumberFormat.getFormat("0.0000").format(123.45789179565757f);
+    assertEquals("123,4579", str);
   }
 
   public void testCurrency() {
@@ -44,214 +43,212 @@
 
     str = NumberFormat.getFormat("\u00a4#,##0.00;-\u00a4#,##0.00").format(
         1234.56);
-    assertTrue(str.equals("\u20AC1\u00A0234,56"));
+    assertEquals("\u20AC1\u00A0234,56", str);
     str = NumberFormat.getFormat("\u00a4#,##0.00;-\u00a4#,##0.00").format(
         -1234.56);
-    assertTrue(str.equals("-\u20AC1\u00A0234,56"));
+    assertEquals("-\u20AC1\u00A0234,56", str);
 
-    str = NumberFormat.getFormat(
-        "\u00a4\u00a4 #,##0.00;-\u00a4\u00a4 #,##0.00").format(1234.56);
-    assertTrue(str.equals("EUR 1\u00A0234,56"));
-    str = NumberFormat.getFormat(
-        "\u00a4\u00a4 #,##0.00;\u00a4\u00a4 -#,##0.00").format(-1234.56);
-    assertTrue(str.equals("EUR -1\u00A0234,56"));
+    str = NumberFormat.getFormat("\u00a4\u00a4 #,##0.00;-\u00a4\u00a4 #,##0.00").format(
+        1234.56);
+    assertEquals("EUR 1\u00A0234,56", str);
+    str = NumberFormat.getFormat("\u00a4\u00a4 #,##0.00;\u00a4\u00a4 -#,##0.00").format(
+        -1234.56);
+    assertEquals("EUR -1\u00A0234,56", str);
 
     NumberFormat formatter = NumberFormat.getFormat(
         "\u00a4#,##0.00;-\u00a4#,##0.00", "BRL");
     str = formatter.format(1234.56);
-    assertTrue(str.equals("R$1\u00A0234,56"));
+    assertEquals("R$1\u00A0234,56", str);
     str = formatter.format(-1234.56);
-    assertTrue(str.equals("-R$1\u00A0234,56"));
+    assertEquals("-R$1\u00A0234,56", str);
 
     formatter = NumberFormat.getFormat(
         "\u00a4\u00a4 #,##0.00;(\u00a4\u00a4 #,##0.00)", "BRL");
     str = formatter.format(1234.56);
-    assertTrue(str.equals("BRL 1\u00A0234,56"));
+    assertEquals("BRL 1\u00A0234,56", str);
     str = formatter.format(-1234.56);
-    assertTrue(str.equals("(BRL 1\u00A0234,56)"));
+    assertEquals("(BRL 1\u00A0234,56)", str);
   }
 
   public void testExponential() {
     String str;
 
     str = NumberFormat.getFormat("0.####E0").format(0.01234);
-    assertTrue(str.equals("1,234E-2"));
+    assertEquals("1,234E-2", str);
     str = NumberFormat.getFormat("00.000E00").format(0.01234);
-    assertTrue(str.equals("12,340E-03"));
+    assertEquals("12,340E-03", str);
     str = NumberFormat.getFormat("##0.######E000").format(0.01234);
-    assertTrue(str.equals("12,34E-003"));
+    assertEquals("12,34E-003", str);
     str = NumberFormat.getFormat("0.###E0;[0.###E0]").format(0.01234);
-    assertTrue(str.equals("1,234E-2"));
+    assertEquals("1,234E-2", str);
 
     str = NumberFormat.getFormat("0.####E0").format(123456789);
-    assertTrue(str.equals("1,2346E8"));
+    assertEquals("1,2346E8", str);
     str = NumberFormat.getFormat("00.000E00").format(123456789);
-    assertTrue(str.equals("12,346E07"));
+    assertEquals("12,346E07", str);
     str = NumberFormat.getFormat("##0.######E000").format(123456789);
-    assertTrue(str.equals("123,456789E006"));
+    assertEquals("123,456789E006", str);
     str = NumberFormat.getFormat("0.###E0;[0.###E0]").format(123456789);
-    assertTrue(str.equals("1,235E8"));
+    assertEquals("1,235E8", str);
 
     str = NumberFormat.getFormat("0.####E0").format(1.23e300);
-    assertTrue(str.equals("1,23E300"));
+    assertEquals("1,23E300", str);
     str = NumberFormat.getFormat("00.000E00").format(1.23e300);
-    assertTrue(str.equals("12,300E299"));
+    assertEquals("12,300E299", str);
     str = NumberFormat.getFormat("##0.######E000").format(1.23e300);
-    assertTrue(str.equals("1,23E300"));
+    assertEquals("1,23E300", str);
     str = NumberFormat.getFormat("0.###E0;[0.###E0]").format(1.23e300);
-    assertTrue(str.equals("1,23E300"));
+    assertEquals("1,23E300", str);
 
     str = NumberFormat.getFormat("0.####E0").format(-3.141592653e-271);
-    assertTrue(str.equals("-3,1416E-271"));
+    assertEquals("-3,1416E-271", str);
     str = NumberFormat.getFormat("00.000E00").format(-3.141592653e-271);
-    assertTrue(str.equals("-31,416E-272"));
-    str = NumberFormat.getFormat("##0.######E000").format(
-        -3.141592653e-271);
-    assertTrue(str.equals("-314,159265E-273"));
-    str = NumberFormat.getFormat("0.###E0;[0.###E0]").format(
-        -3.141592653e-271);
-    assertTrue(str.equals("[3,142E-271]"));
+    assertEquals("-31,416E-272", str);
+    str = NumberFormat.getFormat("##0.######E000").format(-3.141592653e-271);
+    assertEquals("-314,159265E-273", str);
+    str = NumberFormat.getFormat("0.###E0;[0.###E0]").format(-3.141592653e-271);
+    assertEquals("[3,142E-271]", str);
 
     str = NumberFormat.getFormat("0.####E0").format(0);
-    assertTrue(str.equals("0E0"));
+    assertEquals("0E0", str);
     str = NumberFormat.getFormat("00.000E00").format(0);
-    assertTrue(str.equals("00,000E00"));
+    assertEquals("00,000E00", str);
     str = NumberFormat.getFormat("##0.######E000").format(0);
-    assertTrue(str.equals("0E000"));
+    assertEquals("0E000", str);
     str = NumberFormat.getFormat("0.###E0;[0.###E0]").format(0);
-    assertTrue(str.equals("0E0"));
+    assertEquals("0E0", str);
 
     str = NumberFormat.getFormat("0.####E0").format(-1);
-    assertTrue(str.equals("-1E0"));
+    assertEquals("-1E0", str);
     str = NumberFormat.getFormat("00.000E00").format(-1);
-    assertTrue(str.equals("-10,000E-01"));
+    assertEquals("-10,000E-01", str);
     str = NumberFormat.getFormat("##0.######E000").format(-1);
-    assertTrue(str.equals("-1E000"));
+    assertEquals("-1E000", str);
     str = NumberFormat.getFormat("0.###E0;[0.###E0]").format(-1);
-    assertTrue(str.equals("[1E0]"));
+    assertEquals("[1E0]", str);
 
     str = NumberFormat.getFormat("0.####E0").format(1);
-    assertTrue(str.equals("1E0"));
+    assertEquals("1E0", str);
     str = NumberFormat.getFormat("00.000E00").format(1);
-    assertTrue(str.equals("10,000E-01"));
+    assertEquals("10,000E-01", str);
     str = NumberFormat.getFormat("##0.######E000").format(1);
-    assertTrue(str.equals("1E000"));
+    assertEquals("1E000", str);
     str = NumberFormat.getFormat("0.###E0;[0.###E0]").format(1);
-    assertTrue(str.equals("1E0"));
+    assertEquals("1E0", str);
 
     str = NumberFormat.getFormat("#E0").format(12345.0);
-    assertTrue(str.equals("1E4"));
+    assertEquals("1E4", str);
     str = NumberFormat.getFormat("0E0").format(12345.0);
-    assertTrue(str.equals("1E4"));
+    assertEquals("1E4", str);
     str = NumberFormat.getFormat("##0.###E0").format(12345.0);
-    assertTrue(str.equals("12,345E3"));
+    assertEquals("12,345E3", str);
     str = NumberFormat.getFormat("##0.###E0").format(12345.00001);
-    assertTrue(str.equals("12,345E3"));
+    assertEquals("12,345E3", str);
     str = NumberFormat.getFormat("##0.###E0").format(12345);
-    assertTrue(str.equals("12,345E3"));
+    assertEquals("12,345E3", str);
 
     str = NumberFormat.getFormat("##0.####E0").format(789.12345e-9);
-    assertTrue(str.equals("789,1235E-9"));
+    assertEquals("789,1235E-9", str);
     str = NumberFormat.getFormat("##0.####E0").format(780.e-9);
-    assertTrue(str.equals("780E-9"));
+    assertEquals("780E-9", str);
     str = NumberFormat.getFormat(".###E0").format(45678.0);
-    assertTrue(str.equals(",457E5"));
+    assertEquals(",457E5", str);
     str = NumberFormat.getFormat(".###E0").format(0);
-    assertTrue(str.equals(",0E0"));
+    assertEquals(",0E0", str);
 
     str = NumberFormat.getFormat("#E0").format(45678000);
-    assertTrue(str.equals("5E7"));
+    assertEquals("5E7", str);
     str = NumberFormat.getFormat("##E0").format(45678000);
-    assertTrue(str.equals("46E6"));
+    assertEquals("46E6", str);
     str = NumberFormat.getFormat("####E0").format(45678000);
-    assertTrue(str.equals("4568E4"));
+    assertEquals("4568E4", str);
     str = NumberFormat.getFormat("0E0").format(45678000);
-    assertTrue(str.equals("5E7"));
+    assertEquals("5E7", str);
     str = NumberFormat.getFormat("00E0").format(45678000);
-    assertTrue(str.equals("46E6"));
+    assertEquals("46E6", str);
     str = NumberFormat.getFormat("000E0").format(45678000);
-    assertTrue(str.equals("457E5"));
+    assertEquals("457E5", str);
     str = NumberFormat.getFormat("###E0").format(0.0000123);
-    assertTrue(str.equals("12E-6"));
+    assertEquals("12E-6", str);
     str = NumberFormat.getFormat("###E0").format(0.000123);
-    assertTrue(str.equals("123E-6"));
+    assertEquals("123E-6", str);
     str = NumberFormat.getFormat("###E0").format(0.00123);
-    assertTrue(str.equals("1E-3"));
+    assertEquals("1E-3", str);
     str = NumberFormat.getFormat("###E0").format(0.0123);
-    assertTrue(str.equals("12E-3"));
+    assertEquals("12E-3", str);
     str = NumberFormat.getFormat("###E0").format(0.123);
-    assertTrue(str.equals("123E-3"));
+    assertEquals("123E-3", str);
     str = NumberFormat.getFormat("###E0").format(1.23);
-    assertTrue(str.equals("1E0"));
+    assertEquals("1E0", str);
     str = NumberFormat.getFormat("###E0").format(12.3);
-    assertTrue(str.equals("12E0"));
+    assertEquals("12E0", str);
     str = NumberFormat.getFormat("###E0").format(123.0);
-    assertTrue(str.equals("123E0"));
+    assertEquals("123E0", str);
     str = NumberFormat.getFormat("###E0").format(1230.0);
-    assertTrue(str.equals("1E3"));
+    assertEquals("1E3", str);
   }
 
   public void testGrouping() {
     String str;
 
     str = NumberFormat.getFormat("#,###").format(1234567890);
-    assertTrue(str.equals("1\u00a0234\u00a0567\u00a0890"));
+    assertEquals("1\u00a0234\u00a0567\u00a0890", str);
     str = NumberFormat.getFormat("#,####").format(1234567890);
-    assertTrue(str.equals("12\u00a03456\u00a07890"));
+    assertEquals("12\u00a03456\u00a07890", str);
 
     str = NumberFormat.getFormat("#").format(1234567890);
-    assertTrue(str.equals("1234567890"));
+    assertEquals("1234567890", str);
   }
 
   public void testPerMill() {
     String str;
 
     str = NumberFormat.getFormat("###.###\u2030").format(0.4857);
-    assertTrue(str.equals("485,7\u2030"));
+    assertEquals("485,7\u2030", str);
   }
 
   public void testQuotes() {
     String str;
 
     str = NumberFormat.getFormat("a'fo''o'b#").format(123);
-    assertTrue(str.equals("afo'ob123"));
+    assertEquals("afo'ob123", str);
 
     str = NumberFormat.getFormat("a''b#").format(123);
-    assertTrue(str.equals("a'b123"));
+    assertEquals("a'b123", str);
   }
 
   public void testStandardFormat() {
     String str;
 
     str = NumberFormat.getCurrencyFormat().format(1234.579);
-    assertTrue(str.equals("1\u00A0234,58 \u20AC"));
+    assertEquals("1\u00A0234,58 \u20AC", str);
     str = NumberFormat.getDecimalFormat().format(1234.579);
-    assertTrue(str.equals("1\u00A0234,579"));
+    assertEquals("1\u00A0234,579", str);
     str = NumberFormat.getPercentFormat().format(1234.579);
-    assertTrue(str.equals("123\u00A0458\u00A0%"));
+    assertEquals("123\u00A0458\u00A0%", str);
     str = NumberFormat.getScientificFormat().format(1234.579);
-    assertTrue(str.equals("1,235E3"));
+    assertEquals("1,235E3", str);
   }
 
   public void testZeros() {
     String str;
 
     str = NumberFormat.getFormat("#.#").format(0);
-    assertTrue(str.equals("0"));
+    assertEquals("0", str);
     str = NumberFormat.getFormat("#.").format(0);
-    assertTrue(str.equals("0,"));
+    assertEquals("0,", str);
     str = NumberFormat.getFormat(".#").format(0);
-    assertTrue(str.equals(",0"));
+    assertEquals(",0", str);
     str = NumberFormat.getFormat("#").format(0);
-    assertTrue(str.equals("0"));
+    assertEquals("0", str);
 
     str = NumberFormat.getFormat("#0.#").format(0);
-    assertTrue(str.equals("0"));
+    assertEquals("0", str);
     str = NumberFormat.getFormat("#0.").format(0);
-    assertTrue(str.equals("0,"));
+    assertEquals("0,", str);
     str = NumberFormat.getFormat("#.0").format(0);
-    assertTrue(str.equals(",0"));
+    assertEquals(",0", str);
     str = NumberFormat.getFormat("#").format(0);
-    assertTrue(str.equals("0"));
+    assertEquals("0", str);
   }
 }
diff --git a/user/test/com/google/gwt/i18n/client/NumberParse_fr_Test.java b/user/test/com/google/gwt/i18n/client/NumberParse_fr_Test.java
index 0abd555..e9855ed 100644
--- a/user/test/com/google/gwt/i18n/client/NumberParse_fr_Test.java
+++ b/user/test/com/google/gwt/i18n/client/NumberParse_fr_Test.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Google Inc.
+ * Copyright 2008 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
@@ -40,13 +40,13 @@
     Number value;
 
     value = numberParse("0.0000", "123,4579");
-    assertTrue(value.doubleValue() == 123.4579);
+    assertEquals(123.4579, value.doubleValue(), 0.0);
 
     value = numberParse("0.0000", "+123,4579");
-    assertTrue(value.doubleValue() == 123.4579);
+    assertEquals(123.4579, value.doubleValue(), 0.0);
 
     value = numberParse("0.0000", "-123,4579");
-    assertTrue(value.doubleValue() == -123.4579);
+    assertEquals(-123.4579, value.doubleValue(), 0.0);
   }
 
   public void testExponentParse() {
@@ -54,19 +54,19 @@
     Number value;
 
     value = numberParse("#E0", "1,234E3");
-    assertTrue(value.doubleValue() == 1.234E+3);
+    assertEquals(1.234E+3, value.doubleValue(), 0.0);
 
     value = numberParse("0.###E0", "1,234E3");
-    assertTrue(value.doubleValue() == 1.234E+3);
+    assertEquals(1.234E+3, value.doubleValue(), 0.0);
 
     value = numberParse("#E0", "1,2345E4");
-    assertTrue(value.doubleValue() == 12345.0);
+    assertEquals(12345.0, value.doubleValue(), 0.0);
 
     value = numberParse("0E0", "1,2345E4");
-    assertTrue(value.doubleValue() == 12345.0);
+    assertEquals(12345.0, value.doubleValue(), 0.0);
 
     value = numberParse("0E0", "1,2345E+4");
-    assertTrue(value.doubleValue() == 12345.0);
+    assertEquals(12345.0, value.doubleValue(), 0.0);
   }
 
   public void testGroupingParse() {
@@ -74,12 +74,12 @@
     Number value;
 
     value = numberParse("#,###", "1\u00a0234\u00a0567\u00a0890");
-    assertTrue(value.doubleValue() == 1234567890);
+    assertEquals(1234567890, value.doubleValue(), 0.0);
     value = numberParse("#,####", "12\u00a03456\u00a07890");
-    assertTrue(value.doubleValue() == 1234567890);
+    assertEquals(1234567890, value.doubleValue(), 0.0);
 
     value = numberParse("#", "1234567890");
-    assertTrue(value.doubleValue() == 1234567890);
+    assertEquals(1234567890, value.doubleValue(), 0.0);
   }
 
   public void testInfinityParse() {
@@ -87,10 +87,10 @@
     Number value;
 
     value = numberParse("0.0;(0.0)", "\u221E");
-    assertTrue(value.doubleValue() == Double.POSITIVE_INFINITY);
+    assertEquals(Double.POSITIVE_INFINITY, value.doubleValue(), 0.0);
 
     value = numberParse("0.0;(0.0)", "(\u221E)");
-    assertTrue(value.doubleValue() == Double.NEGATIVE_INFINITY);
+    assertEquals(Double.NEGATIVE_INFINITY, value.doubleValue(), 0.0);
   }
 
   public void testPrecentParse() {
@@ -98,16 +98,16 @@
     Number value;
 
     value = numberParse("0.0;(0.0)", "123,4579%");
-    assertTrue(value.doubleValue() == (123.4579 / 100));
+    assertEquals((123.4579 / 100), value.doubleValue(), 0.0);
 
     value = numberParse("0.0;(0.0)", "(%123,4579)");
-    assertTrue(value.doubleValue() == (-123.4579 / 100));
+    assertEquals((-123.4579 / 100), value.doubleValue(), 0.0);
 
     value = numberParse("0.0;(0.0)", "123,4579\u2030");
-    assertTrue(value.doubleValue() == (123.4579 / 1000));
+    assertEquals((123.4579 / 1000), value.doubleValue(), 0.0);
 
     value = numberParse("0.0;(0.0)", "(\u2030123,4579)");
-    assertTrue(value.doubleValue() == (-123.4579 / 1000));
+    assertEquals((-123.4579 / 1000), value.doubleValue(), 0.0);
   }
 
   public void testPrefixParse() {
@@ -115,10 +115,10 @@
     Number value;
 
     value = numberParse("0.0;(0.0)", "123,4579");
-    assertTrue(value.doubleValue() == 123.4579);
+    assertEquals(123.4579, value.doubleValue(), 0.0);
 
     value = numberParse("0.0;(0.0)", "(123,4579)");
-    assertTrue(value.doubleValue() == -123.4579);
+    assertEquals(-123.4579, value.doubleValue(), 0.0);
   }
 
 }
diff --git a/user/test/com/google/gwt/langtest/LongLibGwtTest.gwt.xml b/user/test/com/google/gwt/langtest/LongLibGwtTest.gwt.xml
new file mode 100644
index 0000000..fab5801
--- /dev/null
+++ b/user/test/com/google/gwt/langtest/LongLibGwtTest.gwt.xml
@@ -0,0 +1,16 @@
+<!--                                                                        -->
+<!-- Copyright 2008 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   -->
+<!-- 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. License for the specific language governing permissions and   -->
+<!-- limitations under the License.                                         -->
+<module>
+   <source path=""/>
+</module>
diff --git a/user/test/com/google/gwt/langtest/LongLibGwtTest.java b/user/test/com/google/gwt/langtest/LongLibGwtTest.java
new file mode 100644
index 0000000..66225de
--- /dev/null
+++ b/user/test/com/google/gwt/langtest/LongLibGwtTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2008 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.langtest;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.lang.LongLib;
+import com.google.gwt.lang.LongLibTestBase;
+
+/**
+ * Test the LongLib class as a GWTTestCase.
+ */
+public class LongLibGwtTest extends GWTTestCase {
+
+  static {
+    if (!GWT.isScript()) {
+      LongLib.RUN_IN_JVM = true;
+    }
+  }
+
+  private LongLibTestBase impl = new LongLibTestBase();
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.langtest.LongLibGwtTest";
+  }
+
+  public void testAdditive() {
+    impl.testAdditive();
+  }
+
+  public void testBitOps() {
+    impl.testBitOps();
+  }
+
+  public void testComparisons() {
+    impl.testComparisons();
+  }
+
+  public void testConversions() {
+    impl.testConversions();
+  }
+
+  public void testDiv() {
+    impl.testDiv();
+  }
+
+  public void testFactorial() {
+    impl.testFactorial();
+  }
+
+  public void testFromDouble() {
+    impl.testFromDouble();
+  }
+
+  public void testMinMax() {
+    impl.testMinMax();
+  }
+
+  public void testMultiplicative() {
+    impl.testMultiplicative();
+  }
+
+  public void testNegate() {
+    impl.testNegate();
+  }
+
+  public void testShift() {
+    impl.testShift();
+  }
+
+  public void testToHexString() {
+    impl.testToHexString();
+  }
+
+  public void testToString() {
+    impl.testToString();
+  }
+}
diff --git a/user/test/com/google/gwt/user/client/rpc/ValueTypesTest.java b/user/test/com/google/gwt/user/client/rpc/ValueTypesTest.java
index 2d3d190..7f82ce2 100644
--- a/user/test/com/google/gwt/user/client/rpc/ValueTypesTest.java
+++ b/user/test/com/google/gwt/user/client/rpc/ValueTypesTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -24,6 +24,8 @@
 public class ValueTypesTest extends GWTTestCase {
   private static final int TEST_DELAY = 5000;
 
+  private ValueTypesTestServiceAsync primitiveTypeTestService;
+
   public String getModuleName() {
     return "com.google.gwt.user.RPCSuite";
   }
@@ -217,8 +219,7 @@
   }
 
   /**
-   * Validate that NaNs (not-a-number, such as 0/0) propagate
-   * properly via RPC.
+   * Validate that NaNs (not-a-number, such as 0/0) propagate properly via RPC.
    */
   public void testDouble_NaN() {
     delayTestFinish(TEST_DELAY);
@@ -331,8 +332,7 @@
   }
 
   /**
-   * Validate that NaNs (not-a-number, such as 0/0) propagate
-   * properly via RPC.
+   * Validate that NaNs (not-a-number, such as 0/0) propagate properly via RPC.
    */
   public void testFloat_NaN() {
     delayTestFinish(TEST_DELAY);
@@ -444,7 +444,7 @@
     });
   }
 
-  public void disabledTestLong() {
+  public void testLong() {
     delayTestFinish(TEST_DELAY);
     ValueTypesTestServiceAsync service = getServiceAsync();
     service.echo(Long.MAX_VALUE / 2, new AsyncCallback() {
@@ -456,16 +456,13 @@
       public void onSuccess(Object result) {
         assertNotNull(result);
         long expected = Long.MAX_VALUE / 2;
-        // TODO: resolve off-by-one problem (probably just a side effect of
-        // loss of precision).
-        // ++expected;
         assertEquals(expected, ((Long) result).longValue());
         finishTest();
       }
     });
   }
 
-  public void disabledTestLong_MAX_VALUE() {
+  public void testLong_MAX_VALUE() {
     delayTestFinish(TEST_DELAY);
     ValueTypesTestServiceAsync service = getServiceAsync();
     service.echo_MAX_VALUE(Long.MAX_VALUE, new AsyncCallback() {
@@ -482,7 +479,7 @@
     });
   }
 
-  public void disabledTestLong_MIN_VALUE() {
+  public void testLong_MIN_VALUE() {
     delayTestFinish(TEST_DELAY);
     ValueTypesTestServiceAsync service = getServiceAsync();
     service.echo_MIN_VALUE(Long.MIN_VALUE, new AsyncCallback() {
@@ -558,6 +555,4 @@
     }
     return primitiveTypeTestService;
   }
-
-  private ValueTypesTestServiceAsync primitiveTypeTestService;
 }