Added lvalue tracking based on context to the JS AST.  Simplified JsInliner to use this.

Review by: bobv.

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2223 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/js/JsInliner.java b/dev/core/src/com/google/gwt/dev/js/JsInliner.java
index fedde1f..c0f3123 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsInliner.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsInliner.java
@@ -25,7 +25,6 @@
 import com.google.gwt.dev.js.ast.JsCase;
 import com.google.gwt.dev.js.ast.JsConditional;
 import com.google.gwt.dev.js.ast.JsContext;
-import com.google.gwt.dev.js.ast.JsNumberLiteral;
 import com.google.gwt.dev.js.ast.JsDefault;
 import com.google.gwt.dev.js.ast.JsExprStmt;
 import com.google.gwt.dev.js.ast.JsExpression;
@@ -40,6 +39,7 @@
 import com.google.gwt.dev.js.ast.JsNew;
 import com.google.gwt.dev.js.ast.JsNode;
 import com.google.gwt.dev.js.ast.JsNullLiteral;
+import com.google.gwt.dev.js.ast.JsNumberLiteral;
 import com.google.gwt.dev.js.ast.JsObjectLiteral;
 import com.google.gwt.dev.js.ast.JsParameter;
 import com.google.gwt.dev.js.ast.JsPostfixOperation;
@@ -52,7 +52,6 @@
 import com.google.gwt.dev.js.ast.JsStringLiteral;
 import com.google.gwt.dev.js.ast.JsSwitchMember;
 import com.google.gwt.dev.js.ast.JsThisRef;
-import com.google.gwt.dev.js.ast.JsUnaryOperation;
 import com.google.gwt.dev.js.ast.JsVars;
 import com.google.gwt.dev.js.ast.JsVisitor;
 import com.google.gwt.dev.js.ast.JsWhile;
@@ -1068,57 +1067,21 @@
       this.parameterNames = parameterNames;
     }
 
-    /**
-     * Disallow inlining if the left-hand side of an assignment is a parameter.
-     */
     @Override
-    public void endVisit(JsBinaryOperation x, JsContext<JsExpression> ctx) {
-      JsBinaryOperator op = x.getOperator();
-
-      // Don't allow assignments to the left-hand side.
-      if (op.isAssignment() && isParameter(x.getArg1())) {
+    public void endVisit(JsNameRef x, JsContext<JsExpression> ctx) {
+      if (ctx.isLvalue() && isParameter(x)) {
         lvalue = true;
       }
     }
-
-    /**
-     * Delegates to {@link #checkUnaryOperation(JsUnaryOperation)}.
-     */
-    @Override
-    public void endVisit(JsPostfixOperation x, JsContext<JsExpression> ctx) {
-      checkUnaryOperation(x);
-    }
-
-    /**
-     * Delegates to {@link #checkUnaryOperation(JsUnaryOperation)}.
-     */
-    @Override
-    public void endVisit(JsPrefixOperation x, JsContext<JsExpression> ctx) {
-      checkUnaryOperation(x);
-    }
-
+    
     public boolean parameterAsLValue() {
       return lvalue;
     }
 
     /**
-     * Disallow modification of parameters via unary operations.
-     */
-    private void checkUnaryOperation(JsUnaryOperation x) {
-      if (x.getOperator().isModifying() && isParameter(x.getArg())) {
-        lvalue = true;
-      }
-    }
-
-    /**
      * Determine if a JsExpression is a JsNameRef that refers to a parameter.
      */
-    private boolean isParameter(JsExpression e) {
-      if (!(e instanceof JsNameRef)) {
-        return false;
-      }
-
-      JsNameRef ref = (JsNameRef) e;
+    private boolean isParameter(JsNameRef ref) {
       if (ref.getQualifier() != null) {
         return false;
       }
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsBinaryOperation.java b/dev/core/src/com/google/gwt/dev/js/ast/JsBinaryOperation.java
index 3181b61..ce14a46 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsBinaryOperation.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsBinaryOperation.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
@@ -103,7 +103,11 @@
 
   public void traverse(JsVisitor v, JsContext<JsExpression> ctx) {
     if (v.visit(this, ctx)) {
-      arg1 = v.accept(arg1);
+      if (op.isAssignment()) {
+        arg1 = v.acceptLvalue(arg1);
+      } else {
+        arg1 = v.accept(arg1);
+      }
       arg2 = v.accept(arg2);
     }
     v.endVisit(this, ctx);
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsContext.java b/dev/core/src/com/google/gwt/dev/js/ast/JsContext.java
index 2d90665..4d63cd1 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsContext.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsContext.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
@@ -32,6 +32,8 @@
 
   void insertBefore(T node);
 
+  boolean isLvalue();
+
   void removeMe();
 
   void replaceMe(T node);
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsModVisitor.java b/dev/core/src/com/google/gwt/dev/js/ast/JsModVisitor.java
index 2e6d7dd1..61aa765 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsModVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsModVisitor.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 @@
       didChange = true;
     }
 
+    public boolean isLvalue() {
+      return false;
+    }
+
     public void removeMe() {
       checkState();
       collection.remove(index--);
@@ -85,6 +89,13 @@
     }
   }
 
+  private class LvalueContext extends NodeContext<JsExpression> {
+    @Override
+    public boolean isLvalue() {
+      return true;
+    }
+  }
+
   private class NodeContext<T extends JsVisitable<T>> implements JsContext<T> {
     private T node;
     private boolean replaced;
@@ -105,6 +116,10 @@
       throw new UnsupportedOperationException();
     }
 
+    public boolean isLvalue() {
+      return false;
+    }
+
     public void removeMe() {
       throw new UnsupportedOperationException();
     }
@@ -156,6 +171,10 @@
     }
   }
 
+  protected JsExpression doAcceptLvalue(JsExpression expr) {
+    return new LvalueContext().traverse(expr);
+  }
+
   @Override
   protected <T extends JsVisitable<T>> void doAcceptWithInsertRemove(
       List<T> collection) {
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsUnaryOperation.java b/dev/core/src/com/google/gwt/dev/js/ast/JsUnaryOperation.java
index d28d897..6358f15 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsUnaryOperation.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsUnaryOperation.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
@@ -51,7 +51,15 @@
   }
 
   public void traverse(JsVisitor v, JsContext<JsExpression> ctx) {
-    arg = v.accept(arg);
+    if (op.isModifying()) {
+      /*
+       * The delete operator is practically like an assignment of undefined, so
+       * for practical purposes we're treating it as an lvalue.
+       */
+      arg = v.acceptLvalue(arg);
+    } else {
+      arg = v.accept(arg);
+    }
   }
 
 }
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsVisitor.java b/dev/core/src/com/google/gwt/dev/js/ast/JsVisitor.java
index b939349..a5a78fe 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsVisitor.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
@@ -28,6 +28,37 @@
 @SuppressWarnings("unused")
 public class JsVisitor {
 
+  protected static final JsContext<JsExpression> LVALUE_CONTEXT = new JsContext<JsExpression>() {
+
+    public boolean canInsert() {
+      return false;
+    }
+
+    public boolean canRemove() {
+      return false;
+    }
+
+    public void insertAfter(JsExpression node) {
+      throw new UnsupportedOperationException();
+    }
+
+    public void insertBefore(JsExpression node) {
+      throw new UnsupportedOperationException();
+    }
+
+    public boolean isLvalue() {
+      return true;
+    }
+
+    public void removeMe() {
+      throw new UnsupportedOperationException();
+    }
+
+    public void replaceMe(JsExpression node) {
+      throw new UnsupportedOperationException();
+    }
+  };
+
   protected static final JsContext UNMODIFIABLE_CONTEXT = new JsContext() {
 
     public boolean canInsert() {
@@ -46,6 +77,10 @@
       throw new UnsupportedOperationException();
     }
 
+    public boolean isLvalue() {
+      return false;
+    }
+
     public void removeMe() {
       throw new UnsupportedOperationException();
     }
@@ -63,6 +98,10 @@
     doAcceptList(collection);
   }
 
+  public JsExpression acceptLvalue(JsExpression expr) {
+    return doAcceptLvalue(expr);
+  }
+
   public final <T extends JsVisitable<T>> void acceptWithInsertRemove(
       List<T> collection) {
     doAcceptWithInsertRemove(collection);
@@ -372,6 +411,11 @@
     }
   }
 
+  protected JsExpression doAcceptLvalue(JsExpression expr) {
+    doTraverse(expr, LVALUE_CONTEXT);
+    return expr;
+  }
+
   protected <T extends JsVisitable<T>> void doAcceptWithInsertRemove(
       List<T> collection) {
     for (Iterator<T> it = collection.iterator(); it.hasNext();) {