Revision: 8401
Author: [email protected]
Date: Wed Jul 21 05:01:19 2010
Log: Add first-class support for [array].length in the compiler.

Review at http://gwt-code-reviews.appspot.com/702801

http://code.google.com/p/google-web-toolkit/source/detail?r=8401

Added:
 /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayLength.java
Modified:
 /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java
 /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/CloneExpressionVisitor.java
 /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/ExpressionAnalyzer.java
 /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
 /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
 /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/JavaPrecedenceVisitor.java
/trunk/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java /trunk/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Array.java
 /trunk/tools/api-checker/config/gwt20_21userApi.conf

=======================================
--- /dev/null
+++ /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayLength.java Wed Jul 21 05:01:19 2010
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2010 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;
+
+import com.google.gwt.dev.jjs.SourceInfo;
+
+/**
+ * Java array length expression.
+ */
+public class JArrayLength extends JExpression {
+
+  private JExpression instance;
+
+  public JArrayLength(SourceInfo info, JExpression instance) {
+    super(info);
+    assert (instance != null);
+    this.instance = instance;
+  }
+
+  public JExpression getInstance() {
+    return instance;
+  }
+
+  public JType getType() {
+    return JPrimitiveType.INT;
+  }
+
+  @Override
+  public boolean hasSideEffects() {
+    return instance.hasSideEffects();
+  }
+
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      instance = visitor.accept(instance);
+    }
+    visitor.endVisit(this, ctx);
+  }
+}
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java Thu Jul 8 09:38:20 2010 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java Wed Jul 21 05:01:19 2010
@@ -189,6 +189,10 @@
   public void endVisit(JAnnotation.Property x, Context ctx) {
     endVisit((JNode) x, ctx);
   }
+
+  public void endVisit(JArrayLength x, Context ctx) {
+    endVisit((JExpression) x, ctx);
+  }

   public void endVisit(JArrayRef x, Context ctx) {
     endVisit((JExpression) x, ctx);
@@ -505,6 +509,10 @@
   public boolean visit(JAnnotation.Property x, Context ctx) {
     return visit((JNode) x, ctx);
   }
+
+  public boolean visit(JArrayLength x, Context ctx) {
+    return visit((JExpression) x, ctx);
+  }

   public boolean visit(JArrayRef x, Context ctx) {
     return visit((JExpression) x, ctx);
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/CloneExpressionVisitor.java Fri Apr 2 14:35:01 2010 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/CloneExpressionVisitor.java Wed Jul 21 05:01:19 2010
@@ -18,6 +18,7 @@
 import com.google.gwt.dev.jjs.InternalCompilerException;
 import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JAbsentArrayDimension;
+import com.google.gwt.dev.jjs.ast.JArrayLength;
 import com.google.gwt.dev.jjs.ast.JArrayRef;
 import com.google.gwt.dev.jjs.ast.JBinaryOperation;
 import com.google.gwt.dev.jjs.ast.JBooleanLiteral;
@@ -97,6 +98,13 @@
     expression = x;
     return false;
   }
+
+  @Override
+  public boolean visit(JArrayLength x, Context ctx) {
+    expression = new JArrayLength(x.getSourceInfo(), cloneExpression(
+        x.getInstance()));
+    return false;
+  }

   @Override
   public boolean visit(JArrayRef x, Context ctx) {
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/ExpressionAnalyzer.java Tue Jul 20 11:02:21 2010 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/ExpressionAnalyzer.java Wed Jul 21 05:01:19 2010
@@ -16,6 +16,7 @@
 package com.google.gwt.dev.jjs.impl;

 import com.google.gwt.dev.jjs.ast.Context;
+import com.google.gwt.dev.jjs.ast.JArrayLength;
 import com.google.gwt.dev.jjs.ast.JArrayRef;
 import com.google.gwt.dev.jjs.ast.JBinaryOperation;
 import com.google.gwt.dev.jjs.ast.JBinaryOperator;
@@ -92,6 +93,15 @@
   public boolean createsObject() {
     return createsObject;
   }
+
+  @Override
+  public void endVisit(JArrayLength x, Context ctx) {
+    // TODO: Is setting accessesField necessary for array.length access?
+    accessesField = true;
+    // Can throw an NPE when the array instance is null at runtime.
+    JReferenceType refType = (JReferenceType) x.getInstance().getType();
+    canThrowException = refType.canBeNull();
+  }

   @Override
   public void endVisit(JArrayRef x, Context ctx) {
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java Tue Jul 13 15:21:54 2010 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java Wed Jul 21 05:01:19 2010
@@ -25,6 +25,7 @@
 import com.google.gwt.dev.jjs.ast.HasEnclosingType;
 import com.google.gwt.dev.jjs.ast.JAnnotation;
 import com.google.gwt.dev.jjs.ast.JAnnotationArgument;
+import com.google.gwt.dev.jjs.ast.JArrayLength;
 import com.google.gwt.dev.jjs.ast.JArrayRef;
 import com.google.gwt.dev.jjs.ast.JArrayType;
 import com.google.gwt.dev.jjs.ast.JAssertStatement;
@@ -230,6 +231,13 @@
   // Reflective invocation causes unused warnings.
   @SuppressWarnings("unused")
   private static class JavaASTGenerationVisitor {
+
+    /**
+     * The literal for the JLS identifier that represents the length
+     * field on an array.
+     */
+    private static final String ARRAY_LENGTH_FIELD = "length";
+
     private static InternalCompilerException translateException(JNode node,
         Throwable e) {
       if (e instanceof VirtualMachineError) {
@@ -1074,19 +1082,18 @@

     JExpression processExpression(FieldReference x) {
       FieldBinding fieldBinding = x.binding;
-      JField field;
+      SourceInfo info = makeSourceInfo(x);
+      JExpression instance = dispProcessExpression(x.receiver);
+      JExpression expr;
       if (fieldBinding.declaringClass == null) {
-        // probably array.length
-        field = program.getIndexedField("Array.length");
-        if (!field.getName().equals(String.valueOf(fieldBinding.name))) {
- throw new InternalCompilerException("Error matching fieldBinding.");
-        }
+ if (!ARRAY_LENGTH_FIELD.equals(String.valueOf(fieldBinding.name))) {
+          throw new InternalCompilerException("Expected [array].length.");
+        }
+        expr = new JArrayLength(info, instance);
       } else {
-        field = (JField) typeMap.get(fieldBinding);
-      }
-      SourceInfo info = makeSourceInfo(x);
-      JExpression instance = dispProcessExpression(x.receiver);
- JExpression fieldRef = new JFieldRef(info, instance, field, currentClass);
+        JField field = (JField) typeMap.get(fieldBinding);
+        expr = new JFieldRef(info, instance, field, currentClass);
+      }

       if (x.genericCast != null) {
         JType castType = (JType) typeMap.get(x.genericCast);
@@ -1094,9 +1101,9 @@
* Note, this may result in an invalid AST due to an LHS cast operation.
          * We fix this up in FixAssignmentToUnbox.
          */
-        return maybeCast(castType, fieldRef);
-      }
-      return fieldRef;
+        return maybeCast(castType, expr);
+      }
+      return expr;
     }

     JExpression processExpression(InstanceOfExpression x) {
@@ -1291,18 +1298,16 @@
       if (x.otherBindings != null) {
         for (int i = 0; i < x.otherBindings.length; ++i) {
           FieldBinding fieldBinding = x.otherBindings[i];
-          JField field;
           if (fieldBinding.declaringClass == null) {
             // probably array.length
-            field = program.getIndexedField("Array.length");
- if (!field.getName().equals(String.valueOf(fieldBinding.name))) {
-              throw new InternalCompilerException(
-                  "Error matching fieldBinding.");
-            }
+ if (!ARRAY_LENGTH_FIELD.equals(String.valueOf(fieldBinding.name))) { + throw new InternalCompilerException("Expected [array].length.");
+            }
+            curRef = new JArrayLength(info, curRef);
           } else {
-            field = (JField) typeMap.get(fieldBinding);
-          }
-          curRef = new JFieldRef(info, curRef, field, currentClass);
+            JField field = (JField) typeMap.get(fieldBinding);
+            curRef = new JFieldRef(info, curRef, field, currentClass);
+          }
if (x.otherGenericCasts != null && x.otherGenericCasts[i] != null) {
             JType castType = (JType) typeMap.get(x.otherGenericCasts[i]);
             curRef = maybeCast(castType, curRef);
@@ -1615,9 +1620,8 @@
         initializers.add(createDeclaration(info, indexVar,
             program.getLiteralInt(0)));
         // int i$max = i$array.length
- initializers.add(createDeclaration(info, maxVar, new JFieldRef(info,
-            createVariableRef(info, arrayVar),
-            program.getIndexedField("Array.length"), currentClass)));
+ initializers.add(createDeclaration(info, maxVar, new JArrayLength(info,
+            new JLocalRef(info, arrayVar))));

         // i$index < i$max
         JExpression condition = new JBinaryOperation(info,
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java Thu Jul 8 08:08:57 2010 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java Wed Jul 21 05:01:19 2010
@@ -26,6 +26,7 @@
 import com.google.gwt.dev.jjs.ast.HasName;
 import com.google.gwt.dev.jjs.ast.JAbsentArrayDimension;
 import com.google.gwt.dev.jjs.ast.JAbstractMethodBody;
+import com.google.gwt.dev.jjs.ast.JArrayLength;
 import com.google.gwt.dev.jjs.ast.JArrayRef;
 import com.google.gwt.dev.jjs.ast.JArrayType;
 import com.google.gwt.dev.jjs.ast.JAssertStatement;
@@ -160,8 +161,6 @@

   private class CreateNamesAndScopesVisitor extends JVisitor {

- private final JField arrayLengthField = program.getIndexedField("Array.length");
-
     /**
      * Cache of computed Java source file names to URI strings for symbol
      * export. By using a cache we also ensure the miminum number of String
@@ -193,10 +192,7 @@
         recordSymbol(x, jsName);
       } else {
         JsName jsName;
-        if (x == arrayLengthField) {
-          jsName = peek().declareName(name);
-          jsName.setObfuscatable(false);
-        } else if (belongsToSpecialObfuscatedType(x)) {
+        if (belongsToSpecialObfuscatedType(x)) {
           jsName = peek().declareName(mangleNameSpecialObfuscate(x));
           jsName.setObfuscatable(false);
         } else {
@@ -516,6 +512,8 @@
      */
     private Map<JMethod, Integer> entryMethodToIndex;

+    private final JsName arrayLength = objectScope.declareName("length");
+
     private final JsName globalTemp = topScope.declareName("_");

     private final JsName prototype = objectScope.declareName("prototype");
@@ -523,6 +521,7 @@
     {
       globalTemp.setObfuscatable(false);
       prototype.setObfuscatable(false);
+      arrayLength.setObfuscatable(false);
     }

     public GenerateJavaScriptVisitor() {
@@ -533,6 +532,15 @@
     public void endVisit(JAbsentArrayDimension x, Context ctx) {
       throw new InternalCompilerException("Should not get here.");
     }
+
+    @Override
+    public void endVisit(JArrayLength x, Context ctx) {
+ assert x.getInstance() != null : "Can't access the length of a null array";
+      JsExpression qualifier = (JsExpression) pop();
+      JsNameRef ref = arrayLength.makeRef(x.getSourceInfo());
+      ref.setQualifier(qualifier);
+      push(ref);
+    }

     @Override
     public void endVisit(JArrayRef x, Context ctx) {
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/JavaPrecedenceVisitor.java Tue Mar 9 10:54:56 2010 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/JavaPrecedenceVisitor.java Wed Jul 21 05:01:19 2010
@@ -18,6 +18,7 @@
 import com.google.gwt.dev.jjs.InternalCompilerException;
 import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JAbsentArrayDimension;
+import com.google.gwt.dev.jjs.ast.JArrayLength;
 import com.google.gwt.dev.jjs.ast.JArrayRef;
 import com.google.gwt.dev.jjs.ast.JBinaryOperation;
 import com.google.gwt.dev.jjs.ast.JBooleanLiteral;
@@ -74,6 +75,12 @@
     answer = 0;
     return false;
   }
+
+  @Override
+  public boolean visit(JArrayLength x, Context ctx) {
+    answer = 0;
+    return false;
+  }

   @Override
   public boolean visit(JArrayRef x, Context ctx) {
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java Thu Mar 11 17:23:20 2010 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java Wed Jul 21 05:01:19 2010
@@ -24,6 +24,7 @@
 import com.google.gwt.dev.jjs.ast.HasType;
 import com.google.gwt.dev.jjs.ast.JAbsentArrayDimension;
 import com.google.gwt.dev.jjs.ast.JAnnotation;
+import com.google.gwt.dev.jjs.ast.JArrayLength;
 import com.google.gwt.dev.jjs.ast.JArrayRef;
 import com.google.gwt.dev.jjs.ast.JArrayType;
 import com.google.gwt.dev.jjs.ast.JAssertStatement;
@@ -187,6 +188,16 @@
     }
     return false;
   }
+
+  @Override
+  public boolean visit(JArrayLength x, Context ctx) {
+    JExpression instance = x.getInstance();
+    parenPush(x, instance);
+    accept(instance);
+    parenPop(x, instance);
+    print(".length");
+    return false;
+  }

   @Override
   public boolean visit(JArrayRef x, Context ctx) {
=======================================
--- /trunk/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Array.java Thu Jun 24 15:39:22 2010 +++ /trunk/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Array.java Wed Jul 21 05:01:19 2010
@@ -266,12 +266,6 @@
    * ExpandoWrapper.initExpandos().
    */

-  /**
-   * Represents the array length. References to this field are magically
-   * translated.
-   */
-  public volatile int length = 0;
-
   /**
* Holds the real type-specific Class object for a given array instance. The * compiler produces a magic implementation of getClass() which returns this
=======================================
--- /trunk/tools/api-checker/config/gwt20_21userApi.conf Mon Jun 14 12:28:42 2010 +++ /trunk/tools/api-checker/config/gwt20_21userApi.conf Wed Jul 21 05:01:19 2010
@@ -139,6 +139,9 @@
 com.google.gwt.lang.LongLib::typeChange(J) MISSING
 com.google.gwt.lang.LongLib::xor([D[D) MISSING

+# Array.length is now intrinsic to the compiler
+com.google.gwt.lang.Array::length MISSING
+
 # Add DateTimeFormatInfo, PredefinedFormat overrides
com.google.gwt.i18n.client.DateTimeFormat::DateTimeFormat(Ljava/lang/String;Lcom/google/gwt/i18n/client/constants/DateTimeConstants;) OVERLOADED_METHOD_CALL com.google.gwt.i18n.client.DateTimeFormat::getFormat(Ljava/lang/String;) OVERLOADED_METHOD_CALL

--
http://groups.google.com/group/Google-Web-Toolkit-Contributors

Reply via email to