Redo "Extend allocator-initialization to some primitive types"

Change-Id: Ib9495232da4d6abf1fee4485784ef45dbc50e5ed
Reviewed-on: https://dart-review.googlesource.com/c/88234
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Stephen Adams <sra@google.com>
diff --git a/pkg/compiler/lib/src/js_backend/allocator_analysis.dart b/pkg/compiler/lib/src/js_backend/allocator_analysis.dart
index 6e15c77..9312d15 100644
--- a/pkg/compiler/lib/src/js_backend/allocator_analysis.dart
+++ b/pkg/compiler/lib/src/js_backend/allocator_analysis.dart
@@ -31,12 +31,12 @@
 //     this.x = this.z = null;
 //
 class KAllocatorAnalysis implements AllocatorAnalysis {
+  final CompilerOptions _options;
   final KernelToElementMap _elementMap;
 
-  final Map<KField, ConstantValue> _fixedInitializers =
-      <KField, ConstantValue>{};
+  final Map<KField, ConstantValue> _fixedInitializers = {};
 
-  KAllocatorAnalysis(KernelFrontEndStrategy kernelStrategy)
+  KAllocatorAnalysis(this._options, KernelFrontEndStrategy kernelStrategy)
       : _elementMap = kernelStrategy.elementMap;
 
   // Register class during resolution. Use simple syntactic analysis to find
@@ -44,12 +44,29 @@
   void registerInstantiatedClass(KClass class_) {
     ir.Class classNode = _elementMap.getClassNode(class_);
 
-    Set<ir.Field> nulls = new Set<ir.Field>();
+    Map<ir.Field, ConstantValue> inits = {};
     for (ir.Field field in classNode.fields) {
       if (!field.isInstanceMember) continue;
       ir.Expression initializer = field.initializer;
+      // TODO(sra): Should really be using constant evaluator to determine
+      // value.
       if (initializer == null || initializer is ir.NullLiteral) {
-        nulls.add(field);
+        inits[field] = const NullConstantValue();
+      } else if (initializer is ir.IntLiteral) {
+        if (_options.useStartupEmitter) {
+          BigInt intValue = BigInt.from(initializer.value).toUnsigned(64);
+          inits[field] = IntConstantValue(intValue);
+        }
+      } else if (initializer is ir.BoolLiteral) {
+        if (_options.useStartupEmitter) {
+          inits[field] = BoolConstantValue(initializer.value);
+        }
+      } else if (initializer is ir.StringLiteral) {
+        if (_options.useStartupEmitter) {
+          if (initializer.value.length <= 20) {
+            inits[field] = StringConstantValue(initializer.value);
+          }
+        }
       }
     }
 
@@ -58,15 +75,14 @@
         if (initializer is ir.FieldInitializer) {
           // TODO(sra): Check explicit initializer value to see if consistent
           // over all constructors.
-          nulls.remove(initializer.field);
+          inits.remove(initializer.field);
         }
       }
     }
 
-    for (var fieldNode in nulls) {
-      _fixedInitializers[_elementMap.getField(fieldNode)] =
-          const NullConstantValue();
-    }
+    inits.forEach((ir.Field fieldNode, ConstantValue value) {
+      _fixedInitializers[_elementMap.getField(fieldNode)] = value;
+    });
   }
 }
 
@@ -77,8 +93,7 @@
 
   // --csp and --fast-startup have different constraints to the generated code.
   final CompilerOptions _options;
-  final Map<JField, ConstantValue> _fixedInitializers =
-      <JField, ConstantValue>{};
+  final Map<JField, ConstantValue> _fixedInitializers = {};
 
   JAllocatorAnalysis._(this._options);
 
@@ -90,8 +105,7 @@
     int fieldCount = source.readInt();
     for (int i = 0; i < fieldCount; i++) {
       JField field = source.readMember();
-      // TODO(sra): Deserialize constant, when non-null is supported.
-      ConstantValue value = const NullConstantValue();
+      ConstantValue value = source.readConstant();
       analysis._fixedInitializers[field] = value;
     }
     source.end(tag);
@@ -104,8 +118,7 @@
     sink.writeInt(_fixedInitializers.length);
     _fixedInitializers.forEach((JField field, ConstantValue value) {
       sink.writeMember(field);
-      // TODO(sra): Serialize constant, when non-null is supported.
-      assert(value.isNull);
+      sink.writeConstant(value);
     });
     sink.end(tag);
   }
@@ -115,8 +128,9 @@
     var result = new JAllocatorAnalysis._(options);
 
     kAnalysis._fixedInitializers.forEach((KField kField, ConstantValue value) {
-      // TODO(sra): Translate constant, but Null does not need translating.
-      if (value.isNull) {
+      // TODO(sra): Translate constant, but Null and these primitives do not
+      // need translating.
+      if (value.isNull || value.isInt || value.isBool || value.isString) {
         JField jField = map.toBackendMember(kField);
         if (jField != null) {
           result._fixedInitializers[jField] = value;
diff --git a/pkg/compiler/lib/src/js_backend/backend.dart b/pkg/compiler/lib/src/js_backend/backend.dart
index 1bdea15..09bfcda 100644
--- a/pkg/compiler/lib/src/js_backend/backend.dart
+++ b/pkg/compiler/lib/src/js_backend/backend.dart
@@ -556,7 +556,7 @@
         nativeBasicData,
         _backendUsageBuilder);
     _allocatorResolutionAnalysis =
-        new KAllocatorAnalysis(compiler.frontendStrategy);
+        new KAllocatorAnalysis(compiler.options, compiler.frontendStrategy);
     ClassQueries classQueries = compiler.frontendStrategy.createClassQueries();
     ClassHierarchyBuilder classHierarchyBuilder =
         new ClassHierarchyBuilder(commonElements, classQueries);
diff --git a/pkg/compiler/lib/src/js_emitter/full_emitter/class_emitter.dart b/pkg/compiler/lib/src/js_emitter/full_emitter/class_emitter.dart
index 61df0af..be7634c 100644
--- a/pkg/compiler/lib/src/js_emitter/full_emitter/class_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/full_emitter/class_emitter.dart
@@ -133,7 +133,8 @@
       bool needsFieldsForConstructor = !emitStatics && !classIsNative;
       if (needsFieldsForConstructor || needsAccessor) {
         List<jsAst.Literal> fieldNameParts = <jsAst.Literal>[];
-        if (field.nullInitializerInAllocator) {
+        if (field.initializerInAllocator != null) {
+          assert(field.initializerInAllocator.isNull);
           fieldNameParts.add(js.stringPart('0'));
         }
         if (!needsAccessor) {
diff --git a/pkg/compiler/lib/src/js_emitter/model.dart b/pkg/compiler/lib/src/js_emitter/model.dart
index ab6fbae..933f41c 100644
--- a/pkg/compiler/lib/src/js_emitter/model.dart
+++ b/pkg/compiler/lib/src/js_emitter/model.dart
@@ -390,17 +390,11 @@
 
   final bool needsCheckedSetter;
 
-  final bool nullInitializerInAllocator; // TODO(sra): Generalize.
+  final ConstantValue initializerInAllocator;
 
   // TODO(floitsch): support renamed fields.
-  Field(
-      this.element,
-      this.name,
-      this.accessorName,
-      this.getterFlags,
-      this.setterFlags,
-      this.needsCheckedSetter,
-      this.nullInitializerInAllocator);
+  Field(this.element, this.name, this.accessorName, this.getterFlags,
+      this.setterFlags, this.needsCheckedSetter, this.initializerInAllocator);
 
   bool get needsGetter => getterFlags != 0;
   bool get needsUncheckedSetter => setterFlags != 0;
diff --git a/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart b/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart
index ea3c298..023faf2 100644
--- a/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart
+++ b/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart
@@ -1067,15 +1067,13 @@
         }
       }
 
-      // TODO(sra): Generalize for constants other than null.
-      bool nullInitializerInAllocator = false;
+      ConstantValue initializerInAllocator = null;
       if (_allocatorAnalysis.isInitializedInAllocator(field)) {
-        assert(_allocatorAnalysis.initializerValue(field).isNull);
-        nullInitializerInAllocator = true;
+        initializerInAllocator = _allocatorAnalysis.initializerValue(field);
       }
 
       fields.add(new Field(field, name, accessorName, getterFlags, setterFlags,
-          needsCheckedSetter, nullInitializerInAllocator));
+          needsCheckedSetter, initializerInAllocator));
     }
 
     FieldVisitor visitor = new FieldVisitor(_options, _elementEnvironment,
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
index b51116a..5ef3622 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
@@ -935,26 +935,31 @@
     const int maxChainLength = 30;
     js.Expression assignment = null;
     int chainLength = 0;
-    bool previousIsNull = false;
+    ConstantValue previousConstant = null;
     void flushAssignment() {
       if (assignment != null) {
         statements.add(js.js.statement('#;', assignment));
         assignment = null;
         chainLength = 0;
-        previousIsNull = false;
+        previousConstant = null;
       }
     }
 
     for (Field field in cls.fields) {
-      if (field.nullInitializerInAllocator) {
-        if (previousIsNull && chainLength < maxChainLength) {
+      ConstantValue constant = field.initializerInAllocator;
+      if (constant != null) {
+        if (constant == previousConstant && chainLength < maxChainLength) {
           assignment = js.js('#.# = #', [thisRef, field.name, assignment]);
         } else {
           flushAssignment();
-          assignment = js.js('#.# = null', [thisRef, field.name]);
+          assignment = js.js('#.# = #', [
+            thisRef,
+            field.name,
+            constantEmitter.generate(constant),
+          ]);
         }
         ++chainLength;
-        previousIsNull = true;
+        previousConstant = constant;
       } else {
         flushAssignment();
         js.Parameter parameter = new js.Parameter('t${parameters.length}');
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index 8bcaf1d..f2bdf20 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -498,9 +498,9 @@
           functionNode: node.function, checks: TargetChecks.none);
     }
 
-    // [fieldValues] accumulates the field initializer values, which may be
-    // overwritten by initializer-list initializers.
-    ConstructorData constructorData = new ConstructorData();
+    // [constructorData.fieldValues] accumulates the field initializer values,
+    // which may be overwritten by initializer-list initializers.
+    ConstructorData constructorData = ConstructorData();
     _buildInitializers(node, constructorData);
 
     List<HInstruction> constructorArguments = <HInstruction>[];
@@ -731,22 +731,28 @@
           failedAt(field, "Unexpected member definition $definition.");
       }
 
+      bool ignoreAllocatorAnalysis = false;
+      if (nativeData.isNativeOrExtendsNative(cls)) {
+        // @Native classes have 'fields' which are really getters/setter.  Do
+        // not try to initialize e.g. 'tagName'.
+        if (nativeData.isNativeClass(cls)) return;
+        // Fields that survive this test are fields of custom elements.
+        ignoreAllocatorAnalysis = true;
+      }
+
       if (node.initializer == null) {
-        // Unassigned fields of native classes are not initialized to
-        // prevent overwriting pre-initialized native properties.
-        if (!nativeData.isNativeOrExtendsNative(cls)) {
-          if (!_allocatorAnalysis.isInitializedInAllocator(field)) {
-            constructorData.fieldValues[field] =
-                graph.addConstantNull(closedWorld);
-          }
+        if (ignoreAllocatorAnalysis ||
+            !_allocatorAnalysis.isInitializedInAllocator(field)) {
+          constructorData.fieldValues[field] =
+              graph.addConstantNull(closedWorld);
         }
-      } else if (node.initializer is! ir.NullLiteral ||
-          !nativeData.isNativeClass(cls)) {
+      } else {
         // Compile the initializer in the context of the field so we know that
         // class type parameters are accessed as values.
         // TODO(sra): It would be sufficient to know the context was a field
         // initializer.
-        if (!_allocatorAnalysis.isInitializedInAllocator(field)) {
+        if (ignoreAllocatorAnalysis ||
+            !_allocatorAnalysis.isInitializedInAllocator(field)) {
           inlinedFrom(field,
               _sourceInformationBuilder.buildAssignment(node.initializer), () {
             node.initializer.accept(this);