Stephen Haberman has submitted this change and it was merged.

Change subject: Optimize initializing fields at the top scope.
......................................................................


Optimize initializing fields at the top scope.

Change-Id: I97a06eb36396a8b8659ce9a025b21a9cf93d0500
---
M dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
1 file changed, 108 insertions(+), 3 deletions(-)

Approvals:
  Roberto Lublinerman: Looks good to me, approved
  Leeroy Jenkins: Verified



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 240f6a0..7ec2231 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
@@ -15,10 +15,10 @@
  */
 package com.google.gwt.dev.jjs.impl;

+import com.google.gwt.core.ext.PropertyOracle;
 import com.google.gwt.core.ext.linker.CastableTypeMap;
 import com.google.gwt.core.ext.linker.impl.StandardCastableTypeMap;
 import com.google.gwt.core.ext.linker.impl.StandardSymbolData;
-import com.google.gwt.core.ext.PropertyOracle;
 import com.google.gwt.dev.jjs.HasSourceInfo;
 import com.google.gwt.dev.jjs.InternalCompilerException;
 import com.google.gwt.dev.jjs.JsOutputOption;
@@ -859,7 +859,7 @@
       JVariable target = x.getVariableRef().getTarget();
       if (target instanceof JField) {
         JField field = (JField) target;
- if (field.getLiteralInitializer() != null && (field.isStatic() || field.isFinal())) {
+        if (initializeAtTopScope(field)) {
           // Will initialize at top scope; no need to double-initialize.
           push(null);
           return;
@@ -893,7 +893,7 @@
     @Override
     public void endVisit(JField x, Context ctx) {
       // if we need an initial value, create an assignment
- if (x.getLiteralInitializer() != null && (x.isFinal() || x.isStatic())) {
+      if (initializeAtTopScope(x)) {
         // setup the constant value
         accept(x.getLiteralInitializer());
       } else if (x.getEnclosingType() == program.getTypeJavaLangObject()) {
@@ -2207,6 +2207,39 @@
jsInvocation.setQualifier(names.get(clinitMethod).makeRef(sourceInfo));
       return jsInvocation;
     }
+
+    /**
+ * If a field is a literal, we can potentially treat it as immutable and assign it once on the + * prototype, to be reused by all instances of the class, instead of re-assigning the same
+     * literal in each constructor.
+     *
+ * Technically, to match JVM semantics, we should only do this for final or static fields. For + * non-final/non-static fields, a super class's cstr, when it calls a polymorphic method that is + * overridden in the subclass, should actually see default values (not the literal initializer)
+     * before the subclass's cstr runs.
+     *
+ * However, cstr's calling polymorphic methods is admittedly an uncommon case, so we apply some + * heuristics to see if we can initialize the field on the prototype anyway.
+     */
+    private boolean initializeAtTopScope(JField x) {
+      if (x.getLiteralInitializer() == null) {
+        return false;
+      }
+      if (x.isFinal() || x.isStatic()) {
+ // we can definitely initialize at top-scope, as JVM does so as well
+        return true;
+      }
+ // if the superclass can observe the field, we need to leave the default value
+      JDeclaredType current = x.getEnclosingType().getSuperClass();
+      while (current != null) {
+        if (canObserveSubclassFields.contains(current)) {
+          return false;
+        }
+        current = current.getSuperClass();
+      }
+ // should be safe to initialize at top-scope, as no one can observe the difference
+      return true;
+    }
   }

   private static class JavaToJsOperatorMap {
@@ -2266,6 +2299,69 @@
     }
   }

+  /**
+ * Determines which classes can potentially see uninitialized values of their subclasses' fields.
+   *
+ * If a class can not observe subclass uninitialized fields then the initialization of those could
+   * be hoisted to the prototype.
+   */
+ private class CanObserveSubclassUninitializedFieldsVisitor extends JVisitor {
+    private JDeclaredType currentClass;
+
+    @Override
+    public boolean visit(JConstructor x, Context ctx) {
+      // Only look at constructor bodies.
+      assert currentClass == null;
+      currentClass = x.getEnclosingType();
+      return true;
+    }
+
+    @Override
+    public void endVisit(JConstructor x, Context ctx) {
+      currentClass = null;
+    }
+
+    @Override
+    public boolean visit(JMethod x, Context ctx) {
+      if (x.getName().equals("$$init")) {
+        assert currentClass == null;
+        currentClass = x.getEnclosingType();
+        return true;
+      }
+      // Do not traverse the method body if it is not a constructor.
+      return false;
+    }
+
+    @Override
+    public void endVisit(JMethod x, Context ctx) {
+      currentClass = null;
+    }
+
+    @Override
+    public void endVisit(JMethodCall x, Context ctx) {
+      // This is a method call inside a constructor.
+      assert currentClass != null;
+ // Calls to this/super constructors are okay, as they will also get examined + if (x.getTarget().isConstructor() && x.getInstance() instanceof JThisRef) {
+        return;
+      }
+ // Calls to the instance initializer are okay, as execution will not escape it
+      if (x.getTarget().getName().equals("$$init")) {
+        return;
+      }
+ // Calls to static methods with no arguments are safe, because they have no
+      // way to trace back into our new instance.
+ if (x.getTarget().isStatic() && x.getTarget().getOriginalParamTypes().size() == 0) {
+        return;
+      }
+ // Technically we could get fancier about trying to track the "this" reference + // through other variable assignments, field assignments, and methods calls, to + // see if it calls a polymorphic method against ourself (which would let subclasses + // observe their fields), but that gets tricky, so we'll use the above heuristics
+      // for now.
+      canObserveSubclassFields.add(currentClass);
+    }
+  }

private class RecordCrossClassCallsAndJSInlinableMethods extends JVisitor {

@@ -2417,6 +2513,12 @@
   private final JsProgram jsProgram;

private final Set<JConstructor> liveCtors = new IdentityHashSet<JConstructor>();
+
+  /**
+ * Classes that could potentially see uninitialized values for fields that are initialized in the
+   * declaration.
+   */
+ private final Set<JDeclaredType> canObserveSubclassFields = new HashSet<JDeclaredType>();

   /**
    * Sorted to avoid nondeterministic iteration.
@@ -2657,6 +2759,9 @@
   }

   private Pair<JavaToJavaScriptMap, Set<JsNode>> execImpl() {
+    CanObserveSubclassUninitializedFieldsVisitor canObserve =
+        new CanObserveSubclassUninitializedFieldsVisitor();
+    canObserve.accept(program);
     SortVisitor sorter = new SortVisitor();
     sorter.accept(program);
     RecordCrossClassCallsAndJSInlinableMethods recorder =

--
To view, visit https://gwt-review.googlesource.com/3440
To unsubscribe, visit https://gwt-review.googlesource.com/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I97a06eb36396a8b8659ce9a025b21a9cf93d0500
Gerrit-PatchSet: 9
Gerrit-Project: gwt
Gerrit-Branch: master
Gerrit-Owner: Stephen Haberman <[email protected]>
Gerrit-Reviewer: Brian Slesinsky <[email protected]>
Gerrit-Reviewer: Leeroy Jenkins <[email protected]>
Gerrit-Reviewer: Matthew Dempsky <[email protected]>
Gerrit-Reviewer: Roberto Lublinerman <[email protected]>
Gerrit-Reviewer: Stephen Haberman <[email protected]>

--
http://groups.google.com/group/Google-Web-Toolkit-Contributors
--- You received this message because you are subscribed to the Google Groups "GWT Contributors" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.


Reply via email to