Adds lvalue-tracking to JSNI field refs.  This improves error detection (user tries to reassign a method or compile-time constant) as well as optimization ability.

Review by: spoon


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2227 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsniFieldRef.java b/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsniFieldRef.java
index 7245843..c08c139 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsniFieldRef.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsniFieldRef.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
@@ -29,18 +29,24 @@
 public class JsniFieldRef extends JFieldRef {
 
   private final String ident;
+  private boolean isLvalue;
 
   public JsniFieldRef(JProgram program, SourceInfo info, String ident,
-      JField field, JReferenceType enclosingType) {
+      JField field, JReferenceType enclosingType, boolean isLvalue) {
     super(program, info, field.isStatic() ? null : program.getLiteralNull(),
         field, enclosingType);
     this.ident = ident;
+    this.isLvalue = isLvalue;
   }
 
   public String getIdent() {
     return ident;
   }
 
+  public boolean isLvalue() {
+    return isLvalue;
+  }
+
   public void traverse(JVisitor visitor, Context ctx) {
     if (visitor.visit(this, ctx)) {
     }
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
index 322f3b5..a6912bb 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/Finalizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/Finalizer.java
@@ -159,7 +159,9 @@
 
     @Override
     public void endVisit(JsniFieldRef x, Context ctx) {
-      recordAssignment(x);
+      if (x.isLvalue()) {
+        recordAssignment(x);
+      }
     }
 
     private void recordAssignment(JExpression lhs) {
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 917a79b..6e1215c 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
@@ -2589,6 +2589,12 @@
          * the field.
          */
         if (field.isCompileTimeConstant()) {
+          if (ctx.isLvalue()) {
+            reportJsniError(info, methodDecl,
+                "Cannot change the value of compile-time constant "
+                    + field.getName());
+          }
+
           JLiteral initializer = field.getConstInitializer();
           JType type = initializer.getType();
           if (type instanceof JPrimitiveType
@@ -2605,12 +2611,12 @@
 
         // Normal: create a jsniRef.
         JsniFieldRef fieldRef = new JsniFieldRef(program, info,
-            nameRef.getIdent(), field, currentClass);
+            nameRef.getIdent(), field, currentClass, ctx.isLvalue());
         nativeMethodBody.jsniFieldRefs.add(fieldRef);
       }
 
       private void processMethod(JsNameRef nameRef, SourceInfo info,
-          JMethod method) {
+          JMethod method, JsContext<JsExpression> ctx) {
         if (method.getEnclosingType() != null) {
           if (method.isStatic() && nameRef.getQualifier() != null) {
             reportJsniError(info, methodDecl,
@@ -2622,6 +2628,10 @@
                     + method.getName());
           }
         }
+        if (ctx.isLvalue()) {
+          reportJsniError(info, methodDecl, "Cannot reassign the Java method "
+              + method.getName());
+        }
 
         JsniMethodRef methodRef = new JsniMethodRef(program, info,
             nameRef.getIdent(), method);
@@ -2645,7 +2655,7 @@
         if (node instanceof JField) {
           processField(nameRef, info, (JField) node, ctx);
         } else if (node instanceof JMethod) {
-          processMethod(nameRef, info, (JMethod) node);
+          processMethod(nameRef, info, (JMethod) node, ctx);
         } else {
           throw new InternalCompilerException((HasSourceInfo) node,
               "JSNI reference to something other than a field or method?", null);
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 7185243..54be4a0 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
@@ -207,7 +207,8 @@
         JField nullField = program.getNullField();
         program.jsniMap.put(ident, nullField);
         JsniFieldRef nullFieldRef = new JsniFieldRef(program,
-            x.getSourceInfo(), ident, nullField, x.getEnclosingType());
+            x.getSourceInfo(), ident, nullField, x.getEnclosingType(),
+            x.isLvalue());
         ctx.replaceMe(nullFieldRef);
       }
     }
@@ -703,11 +704,10 @@
       /*
        * SPECIAL: this could be an assignment that passes a value from
        * JavaScript into Java.
-       * 
-       * TODO(later): technically we only need to do this if the field is being
-       * assigned to.
        */
-      maybeRescueJavaScriptObjectPassingIntoJava(x.getField().getType());
+      if (x.isLvalue()) {
+        maybeRescueJavaScriptObjectPassingIntoJava(x.getField().getType());
+      }
       // JsniFieldRef rescues as JFieldRef
       return visit((JFieldRef) x, ctx);
     }
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 51c7dd6..ef6f251 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
@@ -258,9 +258,11 @@
 
     @Override
     public void endVisit(JsniFieldRef x, Context ctx) {
-      // If this happens in JSNI, we can't make any type-tightening assumptions
-      // Fake an assignment-to-self to prevent tightening
-      addAssignment(x.getTarget(), x);
+      if (x.isLvalue()) {
+        // If this happens in JSNI, we can't make any type-tightening
+        // assumptions. Fake an assignment-to-self to prevent tightening.
+        addAssignment(x.getTarget(), x);
+      }
     }
 
     @Override