Recognize and inline effectively constant fields

Change-Id: I243ee80ebc45d9cdab2f7c944cf5eceb26185b2c
Reviewed-on: https://dart-review.googlesource.com/c/93436
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Stephen Adams <sra@google.com>
diff --git a/pkg/compiler/lib/src/constant_system_dart.dart b/pkg/compiler/lib/src/constant_system_dart.dart
index 95a75db..c610490 100644
--- a/pkg/compiler/lib/src/constant_system_dart.dart
+++ b/pkg/compiler/lib/src/constant_system_dart.dart
@@ -443,6 +443,8 @@
   final round = const UnfoldedUnaryOperation('round');
   final abs = const UnfoldedUnaryOperation('abs');
 
+  // TODO(johnniwinther): Delete this (embed parts into JavaScript constant
+  // system where needed).
   const DartConstantSystem();
 
   @override
@@ -475,7 +477,7 @@
   @override
   MapConstantValue createMap(CommonElements commonElements, InterfaceType type,
       List<ConstantValue> keys, List<ConstantValue> values) {
-    return new MapConstantValue(type, keys, values);
+    throw new UnsupportedError('DartConstantSystem.createMap');
   }
 
   @override
diff --git a/pkg/compiler/lib/src/constants/values.dart b/pkg/compiler/lib/src/constants/values.dart
index 5ad14d9..4f2a8a42 100644
--- a/pkg/compiler/lib/src/constants/values.dart
+++ b/pkg/compiler/lib/src/constants/values.dart
@@ -582,7 +582,7 @@
   ConstantValueKind get kind => ConstantValueKind.SET;
 }
 
-class MapConstantValue extends ObjectConstantValue {
+abstract class MapConstantValue extends ObjectConstantValue {
   final List<ConstantValue> keys;
   final List<ConstantValue> values;
   final int hashCode;
diff --git a/pkg/compiler/lib/src/js_backend/annotations.dart b/pkg/compiler/lib/src/js_backend/annotations.dart
index 5290143..6c6f32e 100644
--- a/pkg/compiler/lib/src/js_backend/annotations.dart
+++ b/pkg/compiler/lib/src/js_backend/annotations.dart
@@ -10,6 +10,7 @@
 import '../diagnostics/messages.dart';
 import '../elements/entities.dart';
 import '../kernel/dart2js_target.dart';
+import '../options.dart';
 import '../serialization/serialization.dart';
 import '../util/enumset.dart';
 
@@ -95,6 +96,7 @@
 }
 
 Set<PragmaAnnotation> processMemberAnnotations(
+    CompilerOptions options,
     DiagnosticReporter reporter,
     KCommonElements commonElements,
     KElementEnvironment elementEnvironment,
@@ -107,7 +109,8 @@
   }
 
   LibraryEntity library = element.library;
-  bool platformAnnotationsAllowed = library.canonicalUri.scheme == 'dart' ||
+  bool platformAnnotationsAllowed = options.testMode ||
+      library.canonicalUri.scheme == 'dart' ||
       maybeEnableNative(library.canonicalUri);
 
   for (ConstantValue constantValue
diff --git a/pkg/compiler/lib/src/js_backend/field_analysis.dart b/pkg/compiler/lib/src/js_backend/field_analysis.dart
index 554a32b..8489a25 100644
--- a/pkg/compiler/lib/src/js_backend/field_analysis.dart
+++ b/pkg/compiler/lib/src/js_backend/field_analysis.dart
@@ -182,9 +182,12 @@
   // --csp and --fast-startup have different constraints to the generated code.
   final Map<FieldEntity, ConstantValue> _fixedInitializers;
 
+  final Map<FieldEntity, ConstantValue> _effectivelyConstantFields;
+
   final Set<FieldEntity> _elidedFields;
 
-  JFieldAnalysis._(this._fixedInitializers, this._elidedFields);
+  JFieldAnalysis._(this._fixedInitializers, this._effectivelyConstantFields,
+      this._elidedFields);
 
   /// Deserializes a [JFieldAnalysis] object from [source].
   factory JFieldAnalysis.readFromDataSource(
@@ -192,15 +195,19 @@
     source.begin(tag);
     Map<FieldEntity, ConstantValue> fixedInitializers =
         source.readMemberMap(source.readConstant);
+    Map<FieldEntity, ConstantValue> effectivelyConstantFields =
+        source.readMemberMap(source.readConstant);
     Set<FieldEntity> elidedFields = source.readMembers<FieldEntity>().toSet();
     source.end(tag);
-    return new JFieldAnalysis._(fixedInitializers, elidedFields);
+    return new JFieldAnalysis._(
+        fixedInitializers, effectivelyConstantFields, elidedFields);
   }
 
   /// Serializes this [JFieldAnalysis] to [sink].
   void writeToDataSink(DataSink sink) {
     sink.begin(tag);
     sink.writeMemberMap(_fixedInitializers, sink.writeConstant);
+    sink.writeMemberMap(_effectivelyConstantFields, sink.writeConstant);
     sink.writeMembers(_elidedFields);
     sink.end(tag);
   }
@@ -208,39 +215,64 @@
   factory JFieldAnalysis.from(
       KClosedWorld closedWorld, JsToFrontendMap map, CompilerOptions options) {
     Map<FieldEntity, ConstantValue> fixedInitializers = {};
+    Map<FieldEntity, ConstantValue> effectivelyConstantFields = {};
+    Set<FieldEntity> elidedFields = new Set();
+
+    bool canBeElided(FieldEntity field) {
+      return !closedWorld.annotationsData.hasNoElision(field) &&
+          !closedWorld.nativeData.isNativeMember(field);
+    }
+
     closedWorld.fieldAnalysis._fixedInitializers
         .forEach((KField kField, AllocatorData data) {
-      // TODO(johnniwinther): Use liveness of constructors and elided optional
-      // parameters to recognize more constant initializers.
-      if (data.initialValue != null && data.initializers.isEmpty) {
-        ConstantValue value = data.initialValue;
-        if (value.isNull || value.isInt || value.isBool || value.isString) {
-          // TODO(johnniwinther,sra): Support non-primitive constants in
-          // allocators when it does cause allocators to deoptimized because
-          // of deferred loading.
+      JField jField = map.toBackendMember(kField);
+      if (jField == null) {
+        return;
+      }
 
-          JField jField = map.toBackendMember(kField);
-          if (jField != null) {
-            fixedInitializers[jField] = map.toBackendConstant(value);
+      // TODO(johnniwinther): Should elided static fields be removed from the
+      // J model? Static setters might still assign to them.
+
+      MemberUsage memberUsage = closedWorld.liveMemberUsage[kField];
+      if (!memberUsage.hasRead) {
+        if (canBeElided(kField)) {
+          elidedFields.add(jField);
+        }
+      } else {
+        // TODO(johnniwinther): Use liveness of constructors and elided optional
+        // parameters to recognize more constant initializers.
+        if (data.initialValue != null && data.initializers.isEmpty) {
+          ConstantValue value = map.toBackendConstant(data.initialValue);
+          assert(value != null);
+          if (!memberUsage.hasWrite && canBeElided(kField)) {
+            elidedFields.add(jField);
+            effectivelyConstantFields[jField] = value;
+          } else if (value.isNull ||
+              value.isInt ||
+              value.isBool ||
+              value.isString) {
+            // TODO(johnniwinther,sra): Support non-primitive constants in
+            // allocators when it does cause allocators to deoptimized because
+            // of deferred loading.
+            fixedInitializers[jField] = value;
           }
         }
       }
     });
 
-    Set<FieldEntity> elidedFields = new Set();
+    // TODO(johnniwinther): Recognize effectively constant top level/static
+    // fields.
     closedWorld.liveMemberUsage
         .forEach((MemberEntity member, MemberUsage memberUsage) {
-      // TODO(johnniwinther): Should elided static fields be removed from the
-      // J model? Static setters might still assign to them.
-      if (member.isField &&
-          !memberUsage.hasRead &&
-          !closedWorld.annotationsData.hasNoElision(member) &&
-          !closedWorld.nativeData.isNativeMember(member)) {
-        elidedFields.add(map.toBackendMember(member));
+      if (member.isField && !member.isInstanceMember) {
+        if (!memberUsage.hasRead && canBeElided(member)) {
+          elidedFields.add(map.toBackendMember(member));
+        }
       }
     });
 
-    return new JFieldAnalysis._(fixedInitializers, elidedFields);
+    return new JFieldAnalysis._(
+        fixedInitializers, effectivelyConstantFields, elidedFields);
   }
 
   // TODO(sra): Add way to let injected fields be initialized to a constant in
@@ -254,18 +286,23 @@
   /// Return the constant for a field initialized in allocator. Returns `null`
   /// for fields not initialized in allocator.
   ConstantValue initializerValue(JField field) {
+    assert(isInitializedInAllocator(field));
     return _fixedInitializers[field];
   }
 
   /// Returns `true` if [field] can be elided from the output.
   ///
   /// This happens if a field is written to but never read.
-  // TODO(johnniwinther): Include fields that are effectively final.
   bool isElided(JField field) => _elidedFields.contains(field);
 
   /// Returns `true` if [field] is effectively constant and therefore only
   /// holds its [initializerValue].
-  // TODO(johnniwinther): Recognize fields that are initialized to a constant
-  // but never written to.
-  bool isEffectivelyConstant(JField field) => false;
+  bool isEffectivelyConstant(JField field) =>
+      _effectivelyConstantFields.containsKey(field);
+
+  /// Returns the [ConstantValue] for the effectively constant [field].
+  ConstantValue getConstantValue(JField field) {
+    assert(isEffectivelyConstant(field));
+    return _effectivelyConstantFields[field];
+  }
 }
diff --git a/pkg/compiler/lib/src/js_backend/namer.dart b/pkg/compiler/lib/src/js_backend/namer.dart
index 9f3dbda..087ede4 100644
--- a/pkg/compiler/lib/src/js_backend/namer.dart
+++ b/pkg/compiler/lib/src/js_backend/namer.dart
@@ -574,7 +574,7 @@
   }
 
   /// Used to disambiguate names for constants in [constantName].
-  final NamingScope constantScope = new NamingScope();
+  final NamingScope _constantScope = new NamingScope();
 
   /// Used to store scopes for instances of [PrivatelyNamedJsEntity]
   final Map<Entity, NamingScope> _privateNamingScopes = {};
@@ -583,8 +583,8 @@
 
   final Map<LibraryEntity, String> libraryLongNames = HashMap();
 
-  final Map<ConstantValue, jsAst.Name> constantNames = HashMap();
-  final Map<ConstantValue, String> constantLongNames = {};
+  final Map<ConstantValue, jsAst.Name> _constantNames = HashMap();
+  final Map<ConstantValue, String> _constantLongNames = {};
   ConstantCanonicalHasher _constantHasher;
 
   /// Maps private names to a library that may use that name without prefixing
@@ -718,25 +718,25 @@
     // function constants since the function-implementation itself serves as
     // constant and can be accessed directly.
     assert(!constant.isFunction);
-    jsAst.Name result = constantNames[constant];
+    jsAst.Name result = _constantNames[constant];
     if (result == null) {
       String longName = constantLongName(constant);
-      result = getFreshName(constantScope, longName);
-      constantNames[constant] = result;
+      result = getFreshName(_constantScope, longName);
+      _constantNames[constant] = result;
     }
     return _newReference(result);
   }
 
   /// Proposed name for [constant].
   String constantLongName(ConstantValue constant) {
-    String longName = constantLongNames[constant];
+    String longName = _constantLongNames[constant];
     if (longName == null) {
       _constantHasher ??=
           new ConstantCanonicalHasher(rtiEncoder, _codegenWorldBuilder);
       longName = new ConstantNamingVisitor(
               rtiEncoder, _codegenWorldBuilder, _constantHasher)
           .getName(constant);
-      constantLongNames[constant] = longName;
+      _constantLongNames[constant] = longName;
     }
     return longName;
   }
diff --git a/pkg/compiler/lib/src/js_emitter/model.dart b/pkg/compiler/lib/src/js_emitter/model.dart
index 9fb47bc..6f7b580 100644
--- a/pkg/compiler/lib/src/js_emitter/model.dart
+++ b/pkg/compiler/lib/src/js_emitter/model.dart
@@ -384,6 +384,8 @@
 
   final ConstantValue initializerInAllocator;
 
+  final ConstantValue constantValue;
+
   final bool isElided;
 
   // TODO(floitsch): support renamed fields.
@@ -395,6 +397,7 @@
       this.setterFlags,
       this.needsCheckedSetter,
       this.initializerInAllocator,
+      this.constantValue,
       this.isElided);
 
   bool get needsGetter => getterFlags != 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 77089be..371dbfe 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
@@ -1071,10 +1071,14 @@
         }
       }
 
-      ConstantValue initializerInAllocator = null;
+      ConstantValue initializerInAllocator;
       if (_allocatorAnalysis.isInitializedInAllocator(field)) {
         initializerInAllocator = _allocatorAnalysis.initializerValue(field);
       }
+      ConstantValue constantValue;
+      if (_allocatorAnalysis.isEffectivelyConstant(field)) {
+        constantValue = _allocatorAnalysis.getConstantValue(field);
+      }
 
       fields.add(new Field(
           field,
@@ -1084,6 +1088,7 @@
           setterFlags,
           needsCheckedSetter,
           initializerInAllocator,
+          constantValue,
           _closedWorld.fieldAnalysis.isElided(field)));
     }
 
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 965e232..e47c77a 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
@@ -1078,17 +1078,33 @@
   Method generateGetter(Field field) {
     assert(field.needsGetter);
 
-    String template;
-    if (field.needsInterceptedGetterOnReceiver) {
-      template = "function(receiver) { return receiver[#]; }";
-    } else if (field.needsInterceptedGetterOnThis) {
-      template = "function(receiver) { return this[#]; }";
+    js.Expression code;
+    if (field.isElided) {
+      ConstantValue constantValue = field.constantValue;
+      if (constantValue == null) {
+        assert(_closedWorld.abstractValueDomain is TrivialAbstractValueDomain);
+        // Since static types are not used in invocation/access of instance
+        // members in codegen, we see dynamic uses in codegen that are not
+        // present in resolution when using the trivial abstract value domain.
+        // This means that resolution can determine that a field is never read
+        // but codegen thinks it is read.
+        constantValue = new NullConstantValue();
+      }
+      code = js.js(
+          "function() { return #; }", generateConstantReference(constantValue));
     } else {
-      assert(!field.needsInterceptedGetter);
-      template = "function() { return this[#]; }";
+      String template;
+      if (field.needsInterceptedGetterOnReceiver) {
+        template = "function(receiver) { return receiver[#]; }";
+      } else if (field.needsInterceptedGetterOnThis) {
+        template = "function(receiver) { return this[#]; }";
+      } else {
+        assert(!field.needsInterceptedGetter);
+        template = "function() { return this[#]; }";
+      }
+      js.Expression fieldName = js.quoteName(field.name);
+      code = js.js(template, fieldName);
     }
-    js.Expression fieldName = js.quoteName(field.name);
-    js.Expression code = js.js(template, fieldName);
     js.Name getterName = namer.deriveGetterName(field.accessorName);
     return new StubMethod(getterName, code);
   }
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
index 5304aac..6497bf4 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
@@ -32,10 +32,12 @@
 import '../../../compiler_new.dart';
 import '../../common.dart';
 import '../../compiler.dart' show Compiler;
-import '../../constants/values.dart' show ConstantValue, FunctionConstantValue;
+import '../../constants/values.dart'
+    show ConstantValue, FunctionConstantValue, NullConstantValue;
 import '../../common_elements.dart' show CommonElements;
 import '../../elements/entities.dart';
 import '../../hash/sha1.dart' show Hasher;
+import '../../inferrer/trivial.dart';
 import '../../io/code_output.dart';
 import '../../io/location_provider.dart' show LocationCollector;
 import '../../io/source_map_builder.dart' show SourceMapBuilder;
diff --git a/pkg/compiler/lib/src/js_model/js_world_builder.dart b/pkg/compiler/lib/src/js_model/js_world_builder.dart
index 6467d7c..89f07bb 100644
--- a/pkg/compiler/lib/src/js_model/js_world_builder.dart
+++ b/pkg/compiler/lib/src/js_model/js_world_builder.dart
@@ -16,8 +16,9 @@
 import '../inferrer/abstract_value_domain.dart';
 import '../ir/closure.dart';
 import '../js_backend/annotations.dart';
-import '../js_backend/field_analysis.dart';
 import '../js_backend/backend_usage.dart';
+import '../js_backend/constant_system_javascript.dart';
+import '../js_backend/field_analysis.dart';
 import '../js_backend/interceptor_data.dart';
 import '../js_backend/native_data.dart';
 import '../js_backend/no_such_method_registry.dart';
@@ -859,16 +860,19 @@
     return new SetConstantValue(type, values);
   }
 
-  ConstantValue visitMap(MapConstantValue constant, _) {
-    var type = typeConverter.visit(constant.type, toBackendEntity);
-    List<ConstantValue> keys = _handleValues(constant.keys);
+  ConstantValue visitMap(covariant JavaScriptMapConstant constant, _) {
+    DartType type = typeConverter.visit(constant.type, toBackendEntity);
+    ListConstantValue keys = constant.keyList.accept(this, null);
     List<ConstantValue> values = _handleValues(constant.values);
+    ConstantValue protoValue = constant.protoValue?.accept(this, null);
     if (identical(keys, constant.keys) &&
         identical(values, constant.values) &&
-        type == constant.type) {
+        type == constant.type &&
+        protoValue == constant.protoValue) {
       return constant;
     }
-    return new MapConstantValue(type, keys, values);
+    return new JavaScriptMapConstant(
+        type, keys, values, protoValue, constant.onlyStringKeys);
   }
 
   ConstantValue visitConstructed(ConstructedConstantValue constant, _) {
diff --git a/pkg/compiler/lib/src/kernel/kernel_strategy.dart b/pkg/compiler/lib/src/kernel/kernel_strategy.dart
index b8a9090..2986cb7 100644
--- a/pkg/compiler/lib/src/kernel/kernel_strategy.dart
+++ b/pkg/compiler/lib/src/kernel/kernel_strategy.dart
@@ -227,6 +227,7 @@
     return _compilerTask.measure(() {
       _nativeMemberResolver.resolveNativeMember(element);
       Set<PragmaAnnotation> annotations = processMemberAnnotations(
+          _elementMap.options,
           _elementMap.reporter,
           _elementMap.commonElements,
           _elementMap.elementEnvironment,
diff --git a/pkg/compiler/lib/src/serialization/abstract_sink.dart b/pkg/compiler/lib/src/serialization/abstract_sink.dart
index 9f58478..07db268 100644
--- a/pkg/compiler/lib/src/serialization/abstract_sink.dart
+++ b/pkg/compiler/lib/src/serialization/abstract_sink.dart
@@ -401,10 +401,12 @@
         writeConstants(constant.values);
         break;
       case ConstantValueKind.MAP:
-        MapConstantValue constant = value;
+        JavaScriptMapConstant constant = value;
         writeDartType(constant.type);
-        writeConstants(constant.keys);
+        writeConstant(constant.keyList);
         writeConstants(constant.values);
+        writeConstantOrNull(constant.protoValue);
+        writeBool(constant.onlyStringKeys);
         break;
       case ConstantValueKind.CONSTRUCTED:
         ConstructedConstantValue constant = value;
diff --git a/pkg/compiler/lib/src/serialization/abstract_source.dart b/pkg/compiler/lib/src/serialization/abstract_source.dart
index e67a868..4b6063b 100644
--- a/pkg/compiler/lib/src/serialization/abstract_source.dart
+++ b/pkg/compiler/lib/src/serialization/abstract_source.dart
@@ -463,9 +463,12 @@
         return new SetConstantValue(type, values);
       case ConstantValueKind.MAP:
         DartType type = readDartType();
-        List<ConstantValue> keys = readConstants();
+        ListConstantValue keyList = readConstant();
         List<ConstantValue> values = readConstants();
-        return new MapConstantValue(type, keys, values);
+        ConstantValue protoValue = readConstantOrNull();
+        bool onlyStringKeys = readBool();
+        return new JavaScriptMapConstant(
+            type, keyList, values, protoValue, onlyStringKeys);
       case ConstantValueKind.CONSTRUCTED:
         InterfaceType type = readDartType();
         Map<FieldEntity, ConstantValue> fields =
diff --git a/pkg/compiler/lib/src/serialization/mixins.dart b/pkg/compiler/lib/src/serialization/mixins.dart
index be891a1..385c282 100644
--- a/pkg/compiler/lib/src/serialization/mixins.dart
+++ b/pkg/compiler/lib/src/serialization/mixins.dart
@@ -256,6 +256,15 @@
   }
 
   @override
+  ConstantValue readConstantOrNull() {
+    bool hasClass = readBool();
+    if (hasClass) {
+      return readConstant();
+    }
+    return null;
+  }
+
+  @override
   List<E> readConstants<E extends ConstantValue>({bool emptyAsNull: false}) {
     int count = readInt();
     if (count == 0 && emptyAsNull) return null;
@@ -611,6 +620,14 @@
   }
 
   @override
+  void writeConstantOrNull(ConstantValue value) {
+    writeBool(value != null);
+    if (value != null) {
+      writeConstant(value);
+    }
+  }
+
+  @override
   void writeConstants(Iterable<ConstantValue> values, {bool allowNull: false}) {
     if (values == null) {
       assert(allowNull);
diff --git a/pkg/compiler/lib/src/serialization/serialization.dart b/pkg/compiler/lib/src/serialization/serialization.dart
index c4dab5a..74ce516 100644
--- a/pkg/compiler/lib/src/serialization/serialization.dart
+++ b/pkg/compiler/lib/src/serialization/serialization.dart
@@ -15,6 +15,7 @@
 import '../elements/indexed.dart';
 import '../elements/types.dart';
 import '../ir/static_type_base.dart';
+import '../js_backend/constant_system_javascript.dart';
 import '../js_model/closure.dart';
 import '../js_model/locals.dart';
 
@@ -315,6 +316,9 @@
   /// Writes the constant [value] to this data sink.
   void writeConstant(ConstantValue value);
 
+  /// Writes the potentially `null` constant [value] to this data sink.
+  void writeConstantOrNull(ConstantValue value);
+
   /// Writes constant [values] to this data sink. If [allowNull] is `true`,
   /// [values] is allowed to be `null`.
   ///
@@ -626,6 +630,9 @@
   /// Reads a constant value from this data source.
   ConstantValue readConstant();
 
+  /// Reads a potentially `null` constant value from this data source.
+  ConstantValue readConstantOrNull();
+
   /// Reads a double value from this data source.
   double readDoubleValue();
 
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index 609e083..0805cef 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -554,7 +554,8 @@
       // Null guard ensures an error if we are being called from an explicit
       // 'new' of the constructor instead of via an upgrade. It is optimized out
       // if there are field initializers.
-      add(new HFieldGet(null, newObject, abstractValueDomain.dynamicType,
+      add(new HFieldGet(
+          null, newObject, abstractValueDomain.dynamicType, sourceInformation,
           isAssignable: false));
       for (int i = 0; i < fields.length; i++) {
         add(new HFieldSet(abstractValueDomain, fields[i], newObject,
@@ -802,10 +803,11 @@
     var foundSuperOrRedirectCall = false;
     for (var initializer in constructor.initializers) {
       if (initializer is ir.FieldInitializer) {
-        // TODO(sra): Skip fields initialized in allocator.
-        initializer.value.accept(this);
-        constructorData.fieldValues[_elementMap.getField(initializer.field)] =
-            pop();
+        FieldEntity field = _elementMap.getField(initializer.field);
+        if (!closedWorld.fieldAnalysis.isInitializedInAllocator(field)) {
+          initializer.value.accept(this);
+          constructorData.fieldValues[field] = pop();
+        }
       } else if (initializer is ir.SuperInitializer) {
         assert(!foundSuperOrRedirectCall);
         foundSuperOrRedirectCall = true;
@@ -4761,6 +4763,11 @@
     if (member == null) {
       _generateSuperNoSuchMethod(node, _elementMap.getSelector(node).name,
           const <HInstruction>[], const <DartType>[], sourceInformation);
+    } else if (member.isField &&
+        closedWorld.fieldAnalysis.isEffectivelyConstant(member)) {
+      ConstantValue value = closedWorld.fieldAnalysis.getConstantValue(member);
+      stack.add(graph.addConstant(value, closedWorld,
+          sourceInformation: sourceInformation));
     } else {
       _buildInvokeSuper(
           _elementMap.getSelector(node),
@@ -5331,10 +5338,9 @@
           function is! ConstructorBodyEntity &&
           (mask == null ||
               abstractValueDomain.isNull(mask).isPotentiallyTrue)) {
-        add(new HFieldGet(
-            null, providedArguments[0], abstractValueDomain.dynamicType,
-            isAssignable: false)
-          ..sourceInformation = sourceInformation);
+        add(new HFieldGet(null, providedArguments[0],
+            abstractValueDomain.dynamicType, sourceInformation,
+            isAssignable: false));
       }
       List<HInstruction> compiledArguments = _completeCallArgumentsList(
           function, selector, providedArguments, currentNode);
diff --git a/pkg/compiler/lib/src/ssa/locals_handler.dart b/pkg/compiler/lib/src/ssa/locals_handler.dart
index 991cef0..b74bb8c 100644
--- a/pkg/compiler/lib/src/ssa/locals_handler.dart
+++ b/pkg/compiler/lib/src/ssa/locals_handler.dart
@@ -346,9 +346,10 @@
       AbstractValue type = local is BoxLocal
           ? _abstractValueDomain.nonNullType
           : getTypeOfCapturedVariable(redirect);
-      HInstruction fieldGet = new HFieldGet(redirect, receiver, type);
+      HInstruction fieldGet =
+          new HFieldGet(redirect, receiver, type, sourceInformation);
       builder.add(fieldGet);
-      return fieldGet..sourceInformation = sourceInformation;
+      return fieldGet;
     } else if (isBoxed(local)) {
       FieldEntity redirect = redirectionMapping[local];
       BoxLocal localBox;
@@ -363,10 +364,10 @@
       assert(localBox != null);
 
       HInstruction box = readLocal(localBox);
-      HInstruction lookup =
-          new HFieldGet(redirect, box, getTypeOfCapturedVariable(redirect));
+      HInstruction lookup = new HFieldGet(redirect, box,
+          getTypeOfCapturedVariable(redirect), sourceInformation);
       builder.add(lookup);
-      return lookup..sourceInformation = sourceInformation;
+      return lookup;
     } else {
       assert(_isUsedInTryOrGenerator(local));
       HLocalValue localValue = getLocal(local);
diff --git a/pkg/compiler/lib/src/ssa/logging.dart b/pkg/compiler/lib/src/ssa/logging.dart
index 68d648f..cdaef90 100644
--- a/pkg/compiler/lib/src/ssa/logging.dart
+++ b/pkg/compiler/lib/src/ssa/logging.dart
@@ -2,6 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+import '../elements/entities.dart';
 import '../util/features.dart';
 import 'nodes.dart';
 
@@ -28,8 +29,12 @@
 
   void registerFieldGet(HInvokeDynamicGetter original, HFieldGet converted) {
     Features features = new Features();
-    features['name'] =
-        '${converted.element.enclosingClass.name}.${converted.element.name}';
+    if (converted.element != null) {
+      features['name'] =
+          '${converted.element.enclosingClass.name}.${converted.element.name}';
+    } else {
+      features['name'] = '<null-guard>';
+    }
     entries.add(new OptimizationLogEntry('FieldGet', features));
   }
 
@@ -44,6 +49,33 @@
     entries.add(new OptimizationLogEntry('FieldSet', features));
   }
 
+  void registerFieldCall(HInvokeDynamicMethod original, HFieldGet converted) {
+    Features features = new Features();
+    if (converted.element != null) {
+      features['name'] =
+          '${converted.element.enclosingClass.name}.${converted.element.name}';
+    } else {
+      features['name'] = '<null-guard>';
+    }
+    entries.add(new OptimizationLogEntry('FieldCall', features));
+  }
+
+  void registerConstantFieldGet(
+      HInvokeDynamicGetter original, FieldEntity field, HConstant converted) {
+    Features features = new Features();
+    features['name'] = '${field.enclosingClass.name}.${field.name}';
+    features['value'] = converted.constant.toStructuredText();
+    entries.add(new OptimizationLogEntry('ConstantFieldGet', features));
+  }
+
+  void registerConstantFieldCall(
+      HInvokeDynamicMethod original, FieldEntity field, HConstant converted) {
+    Features features = new Features();
+    features['name'] = '${field.enclosingClass.name}.${field.name}';
+    features['value'] = converted.constant.toStructuredText();
+    entries.add(new OptimizationLogEntry('ConstantFieldCall', features));
+  }
+
   Features _registerSpecializer(
       HInvokeDynamic original, HInstruction converted, String name,
       [String unconvertedName]) {
diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart
index 69714ef..3095197 100644
--- a/pkg/compiler/lib/src/ssa/nodes.dart
+++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -1866,10 +1866,12 @@
   final bool isAssignable;
 
   HFieldGet(FieldEntity element, HInstruction receiver, AbstractValue type,
+      SourceInformation sourceInformation,
       {bool isAssignable})
       : this.isAssignable =
             (isAssignable != null) ? isAssignable : element.isAssignable,
         super(element, <HInstruction>[receiver], type) {
+    this.sourceInformation = sourceInformation;
     sideEffects.clearAllSideEffects();
     sideEffects.clearAllDependencies();
     setUseGvn();
diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart
index e2e0868..f8416603 100644
--- a/pkg/compiler/lib/src/ssa/optimize.dart
+++ b/pkg/compiler/lib/src/ssa/optimize.dart
@@ -696,8 +696,8 @@
       if (folded != node) return folded;
     }
 
-    AbstractValue receiverType =
-        node.getDartReceiver(_closedWorld).instructionType;
+    HInstruction receiver = node.getDartReceiver(_closedWorld);
+    AbstractValue receiverType = receiver.instructionType;
     MemberEntity element =
         _closedWorld.locateSingleMember(node.selector, receiverType);
     // TODO(ngeoffray): Also fold if it's a getter or variable.
@@ -734,20 +734,46 @@
       FieldEntity field = element;
       if (!_nativeData.isNativeMember(field) &&
           !node.isCallOnInterceptor(_closedWorld)) {
-        HInstruction receiver = node.getDartReceiver(_closedWorld);
-        AbstractValue type = AbstractValueFactory.inferredTypeForMember(
-            // ignore: UNNECESSARY_CAST
-            field as Entity,
-            _globalInferenceResults);
-        HInstruction load = new HFieldGet(field, receiver, type);
-        node.block.addBefore(node, load);
+        // Insertion point for the closure call.
+        HInstruction insertionPoint = node;
+        HInstruction load;
+        if (_closedWorld.fieldAnalysis.isEffectivelyConstant(field)) {
+          // The field is elided and replace it with its constant value.
+          if (_abstractValueDomain.isNull(receiverType).isPotentiallyTrue) {
+            // The receiver is potentially `null` so we insert a null receiver
+            // guard to trigger a null pointer exception.
+            //
+            // This could be inserted unconditionally and removed by later
+            // optimizations if unnecessary, but performance we do it
+            // conditionally here.
+            // TODO(35996): Replace with null receiver guard instruction.
+            HInstruction dummyGet = new HFieldGet(null, receiver,
+                _abstractValueDomain.dynamicType, node.sourceInformation,
+                isAssignable: false);
+            _log?.registerFieldCall(node, dummyGet);
+            node.block.addBefore(node, dummyGet);
+            insertionPoint = dummyGet;
+          }
+          ConstantValue value =
+              _closedWorld.fieldAnalysis.getConstantValue(field);
+          load = _graph.addConstant(value, _closedWorld,
+              sourceInformation: node.sourceInformation);
+          _log?.registerConstantFieldCall(node, field, load);
+        } else {
+          AbstractValue type = AbstractValueFactory.inferredTypeForMember(
+              field, _globalInferenceResults);
+          load = new HFieldGet(field, receiver, type, node.sourceInformation);
+          _log?.registerFieldCall(node, load);
+          node.block.addBefore(node, load);
+          insertionPoint = load;
+        }
         Selector callSelector = new Selector.callClosureFrom(node.selector);
         List<HInstruction> inputs = <HInstruction>[load]
           ..addAll(node.inputs.skip(node.isInterceptedCall ? 2 : 1));
         HInstruction closureCall = new HInvokeClosure(
             callSelector, inputs, node.instructionType, node.typeArguments)
           ..sourceInformation = node.sourceInformation;
-        node.block.addAfter(load, closureCall);
+        node.block.addAfter(insertionPoint, closureCall);
         return closureCall;
       }
     }
@@ -1215,17 +1241,42 @@
       if (folded != node) return folded;
     }
     HInstruction receiver = node.getDartReceiver(_closedWorld);
+    AbstractValue receiverType = receiver.instructionType;
     FieldEntity field = node.element is FieldEntity
         ? node.element
         : findConcreteFieldForDynamicAccess(node, receiver);
     if (field != null) {
-      HFieldGet result = _directFieldGet(receiver, field, node);
-      _log?.registerFieldGet(node, result);
-      return result;
+      if (_closedWorld.fieldAnalysis.isEffectivelyConstant(field)) {
+        // The field is elided and replace it with its constant value.
+        if (_abstractValueDomain.isNull(receiverType).isPotentiallyTrue) {
+          // The receiver is potentially `null` so we insert a null receiver
+          // guard to trigger a null pointer exception.
+          //
+          // This could be inserted unconditionally and removed by later
+          // optimizations if unnecessary, but performance we do it
+          // conditionally here.
+          // TODO(35996): Replace with null receiver guard instruction.
+          HInstruction dummyGet = new HFieldGet(null, receiver,
+              _abstractValueDomain.dynamicType, node.sourceInformation,
+              isAssignable: false);
+          _log?.registerFieldGet(node, dummyGet);
+          node.block.addBefore(node, dummyGet);
+        }
+        ConstantValue constant =
+            _closedWorld.fieldAnalysis.getConstantValue(field);
+        HConstant result = _graph.addConstant(constant, _closedWorld,
+            sourceInformation: node.sourceInformation);
+        _log?.registerConstantFieldGet(node, field, result);
+        return result;
+      } else {
+        HFieldGet result = _directFieldGet(receiver, field, node);
+        _log?.registerFieldGet(node, result);
+        return result;
+      }
     }
 
-    node.element ??= _closedWorld.locateSingleMember(
-        node.selector, receiver.instructionType);
+    node.element ??=
+        _closedWorld.locateSingleMember(node.selector, receiverType);
     if (node.element != null &&
         node.element.name == node.selector.name &&
         node.element.isFunction) {
@@ -1256,8 +1307,8 @@
           field, _globalInferenceResults);
     }
 
-    return new HFieldGet(field, receiver, type, isAssignable: isAssignable)
-      ..sourceInformation = node.sourceInformation;
+    return new HFieldGet(field, receiver, type, node.sourceInformation,
+        isAssignable: isAssignable);
   }
 
   HInstruction visitInvokeDynamicSetter(HInvokeDynamicSetter node) {
diff --git a/tests/compiler/dart2js/codegen/gvn_test.dart b/tests/compiler/dart2js/codegen/gvn_test.dart
index 678d9fc..cdf609c 100644
--- a/tests/compiler/dart2js/codegen/gvn_test.dart
+++ b/tests/compiler/dart2js/codegen/gvn_test.dart
@@ -65,6 +65,7 @@
 // Check that a gvn'able instruction in the loop header gets hoisted.
 const String TEST_SIX = r"""
 class A {
+  @pragma('dart2js:noElision')
   final field = 54;
 }
 
diff --git a/tests/compiler/dart2js/codegen/interceptor_test.dart b/tests/compiler/dart2js/codegen/interceptor_test.dart
index e77058e..cba680f 100644
--- a/tests/compiler/dart2js/codegen/interceptor_test.dart
+++ b/tests/compiler/dart2js/codegen/interceptor_test.dart
@@ -16,6 +16,7 @@
 
 const String TEST_TWO = r"""
   class A {
+    @pragma('dart2js:noElision')
     var length;
   }
   foo(a) {
@@ -37,11 +38,13 @@
     // intercepted, is turned into a regular getter call or field
     // access.
     await compile(TEST_TWO, entry: 'foo', check: (String generated) {
-      Expect.isFalse(generated.contains(r'a.get$length()'));
+      Expect.isFalse(generated.contains(r'a.get$length()'),
+          'a.get\$length() not expected in\n$generated');
+      Expect.isTrue(generated.contains(new RegExp(r'[$A-Z]+\.A\$\(\)\.length')),
+          '.length expected in\n$generated');
       Expect.isTrue(
-          generated.contains(new RegExp(r'[$A-Z]+\.A\$\(\)\.length')));
-      Expect.isTrue(
-          generated.contains(new RegExp(r'[$A-Z]+\.get\$length\$as\(a\)')));
+          generated.contains(new RegExp(r'[$A-Z]+\.get\$length\$as\(a\)')),
+          '*.get\$length expected in\n$generated');
     });
   }
 
diff --git a/tests/compiler/dart2js/codegen/model_data/dynamic_get.dart b/tests/compiler/dart2js/codegen/model_data/dynamic_get.dart
index f519f30..81c7601 100644
--- a/tests/compiler/dart2js/codegen/model_data/dynamic_get.dart
+++ b/tests/compiler/dart2js/codegen/model_data/dynamic_get.dart
@@ -14,10 +14,11 @@
 
 class Class1a {
   /*element: Class1a.field1:emitted*/
+  @pragma('dart2js:noElision')
   int field1;
 }
 
-/*element: method1:params=1*/
+/*element: method1:access=[field1],params=1*/
 @pragma('dart2js:noInline')
 method1(dynamic c) {
   return c.field1;
@@ -25,10 +26,11 @@
 
 class Class2a<T> {
   /*element: Class2a.field2:emitted*/
+  @pragma('dart2js:noElision')
   T field2;
 }
 
-/*element: method2:params=1*/
+/*element: method2:access=[field2],params=1*/
 @pragma('dart2js:noInline')
 method2(dynamic c) {
   return c.field2;
@@ -36,11 +38,13 @@
 
 class Class3a {
   /*element: Class3a.field3:emitted,get=simple*/
+  @pragma('dart2js:noElision')
   int field3;
 }
 
 class Class3b {
   /*element: Class3b.field3:emitted,get=simple*/
+  @pragma('dart2js:noElision')
   int field3;
 }
 
@@ -52,11 +56,13 @@
 
 class Class4a {
   /*element: Class4a.field4:emitted,get=simple*/
+  @pragma('dart2js:noElision')
   int field4;
 }
 
 class Class4b implements Class4a {
   /*element: Class4b.field4:emitted,get=simple*/
+  @pragma('dart2js:noElision')
   int field4;
 }
 
diff --git a/tests/compiler/dart2js/codegen/model_data/effectively_constant_fields.dart b/tests/compiler/dart2js/codegen/model_data/effectively_constant_fields.dart
new file mode 100644
index 0000000..fd838ae
--- /dev/null
+++ b/tests/compiler/dart2js/codegen/model_data/effectively_constant_fields.dart
@@ -0,0 +1,88 @@
+// Copyright (c) 2019, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:expect/expect.dart';
+
+/*element: _field4:params=0*/
+_field4() => 4;
+
+class Class1 {
+  /*element: Class1.field1:elided*/
+  var field1 = 0;
+
+  /*element: Class1.field2:emitted*/
+  @pragma('dart2js:noElision')
+  var field2 = 1;
+
+  /*element: Class1.field3:elided,get=simple*/
+  var field3 = 2;
+
+  /*element: Class1.field4:elided*/
+  var field4 = _field4;
+}
+
+/*element: method1:params=1*/
+@pragma('dart2js:noInline')
+method1(Class1 c) {
+  return c.field1;
+}
+
+/*element: method2:access=[field2],params=1*/
+@pragma('dart2js:noInline')
+method2(Class1 c) {
+  return c.field2;
+}
+
+class Class2 {
+  /*element: Class2.field3:elided,get=simple*/
+  final field3 = 3;
+}
+
+/*element: method3:calls=[get$field3(0)],params=1*/
+@pragma('dart2js:noInline')
+method3(c) {
+  return c.field3;
+}
+
+class Class3 extends Class1 {
+  /*element: Class3.method4:params=0*/
+  @pragma('dart2js:noInline')
+  method4() {
+    return super.field1;
+  }
+}
+
+class Class4 extends Class1 {
+  /*element: Class4.method5:calls=[_field4(0)],params=0*/
+  @pragma('dart2js:noInline')
+  method5() {
+    return super.field4();
+  }
+}
+
+/*element: method6:access=[toString],params=1*/
+@pragma('dart2js:noInline')
+method6(Class1 c) {
+  return c.field1;
+}
+
+/*element: method7:access=[toString],calls=[_field4(0)],params=1*/
+@pragma('dart2js:noInline')
+method7(Class1 c) {
+  return c.field4();
+}
+
+/*element: main:calls=*,params=0*/
+main() {
+  Expect.equals(0, method1(new Class1()));
+  Expect.equals(1, method2(new Class1()));
+  Expect.equals(2, method3(new Class1()));
+  Expect.equals(3, method3(new Class2()));
+  Expect.equals(0, new Class3().method4());
+  Expect.equals(4, new Class4().method5());
+  Expect.equals(0, method6(new Class1()));
+  Expect.throws(/*calls=[method6(1)],params=0*/ () => method6(null));
+  Expect.equals(4, method7(new Class1()));
+  Expect.throws(/*calls=[method7(1)],params=0*/ () => method7(null));
+}
diff --git a/tests/compiler/dart2js/codegen/model_data/field_set.dart b/tests/compiler/dart2js/codegen/model_data/field_set.dart
index 6c23abe..2c68409 100644
--- a/tests/compiler/dart2js/codegen/model_data/field_set.dart
+++ b/tests/compiler/dart2js/codegen/model_data/field_set.dart
@@ -2,7 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-/*element: main:calls=*,params=0*/
+/*element: main:access=*,calls=*,params=0*/
 main() {
   method1(new Class1a());
   method2(new Class2a());
diff --git a/tests/compiler/dart2js/codegen/model_data/fields.dart b/tests/compiler/dart2js/codegen/model_data/fields.dart
index 62c3b52..7815fbe 100644
--- a/tests/compiler/dart2js/codegen/model_data/fields.dart
+++ b/tests/compiler/dart2js/codegen/model_data/fields.dart
@@ -32,6 +32,7 @@
 
 class Class {
   /*element: Class.field1a:emitted*/
+  @pragma('dart2js:noElision')
   var field1a;
 
   /*element: Class.field1b:elided*/
diff --git a/tests/compiler/dart2js/codegen/model_data/native_unused_parameters.dart b/tests/compiler/dart2js/codegen/model_data/native_unused_parameters.dart
index 36197db..9317686 100644
--- a/tests/compiler/dart2js/codegen/model_data/native_unused_parameters.dart
+++ b/tests/compiler/dart2js/codegen/model_data/native_unused_parameters.dart
@@ -5,7 +5,7 @@
 // ignore: import_internal_library
 import 'dart:_js_helper';
 
-/*element: Class.:params=1*/
+/*element: Class.:access=[toString],params=1*/
 @Native('Class')
 class Class {
   /*element: Class.method1:calls=[method1()],params=1*/
diff --git a/tests/compiler/dart2js/codegen/model_test.dart b/tests/compiler/dart2js/codegen/model_test.dart
index e00c78d..974ca4a 100644
--- a/tests/compiler/dart2js/codegen/model_test.dart
+++ b/tests/compiler/dart2js/codegen/model_test.dart
@@ -63,6 +63,7 @@
   static const String isElided = 'elided';
   static const String assignment = 'assign';
   static const String isLazy = 'lazy';
+  static const String propertyAccess = 'access';
 }
 
 /// AST visitor for computing inference data for a member.
@@ -133,6 +134,8 @@
           features[Tags.parameterCount] = '${code.params.length}';
         }
 
+        Set<js.PropertyAccess> handledAccesses = new Set();
+
         void registerCalls(String tag, js.Node node, [String prefix = '']) {
           forEachNode(node, onCall: (js.Call node) {
             js.Node target = node.target;
@@ -159,6 +162,7 @@
                   features.addElement(
                       tag, '${prefix}${name}(${node.arguments.length})');
                 }
+                handledAccesses.add(target);
               }
             }
           });
@@ -170,6 +174,7 @@
             registerCalls(Tags.parameterStub, stub.code, '${stub.name.key}:');
           }
         }
+
         forEachNode(code, onAssignment: (js.Assignment node) {
           js.Expression leftHandSide = node.leftHandSide;
           if (leftHandSide is js.PropertyAccess) {
@@ -182,9 +187,41 @@
             }
             if (name != null) {
               features.addElement(Tags.assignment, '${name}');
+              handledAccesses.add(leftHandSide);
             }
           }
         });
+
+        forEachNode(code, onPropertyAccess: (js.PropertyAccess node) {
+          if (handledAccesses.contains(node)) {
+            return;
+          }
+
+          js.Node receiver = node.receiver;
+          String receiverName;
+          if (receiver is js.VariableUse) {
+            receiverName = receiver.name;
+            if (receiverName == receiverName.toUpperCase()) {
+              // Skip holder access.
+              receiverName = null;
+            }
+          }
+
+          js.Node selector = node.selector;
+          String name;
+          if (selector is js.Name) {
+            name = selector.key;
+          } else if (selector is js.LiteralString) {
+            /// Call to fixed backend name, so we include the argument
+            /// values to test encoding of optional parameters in native
+            /// methods.
+            name = selector.value.substring(1, selector.value.length - 1);
+          }
+
+          if (receiverName != null && name != null) {
+            features.addElement(Tags.propertyAccess, '${name}');
+          }
+        });
         return features;
       }
     }
diff --git a/tests/compiler/dart2js/field_analysis/jdata/multi_initializers.dart b/tests/compiler/dart2js/field_analysis/jdata/multi_initializers.dart
index 3a71206..d72731d 100644
--- a/tests/compiler/dart2js/field_analysis/jdata/multi_initializers.dart
+++ b/tests/compiler/dart2js/field_analysis/jdata/multi_initializers.dart
@@ -3,8 +3,16 @@
 // BSD-style license that can be found in the LICENSE file.
 
 main() {
-  new Class1.a();
+  var c = new Class1.a();
+  c.field4a = null;
   new Class1.b();
+
+  print(c.field1);
+  print(c.field2);
+  print(c.field3);
+  print(c.field4a);
+  print(c.field4b);
+  print(c.field5);
 }
 
 class Class1 {
@@ -12,8 +20,11 @@
   var field2;
   var field3;
 
-  /*element: Class1.field4:initial=IntConstant(4)*/
-  var field4 = 4;
+  /*element: Class1.field4a:initial=IntConstant(4)*/
+  var field4a = 4;
+
+  /*element: Class1.field4b:constant=IntConstant(4)*/
+  var field4b = 4;
 
   var field5 = 5;
 
diff --git a/tests/compiler/dart2js/field_analysis/jdata/simple_initializers.dart b/tests/compiler/dart2js/field_analysis/jdata/simple_initializers.dart
index 05a5f22..0a5a612 100644
--- a/tests/compiler/dart2js/field_analysis/jdata/simple_initializers.dart
+++ b/tests/compiler/dart2js/field_analysis/jdata/simple_initializers.dart
@@ -3,108 +3,336 @@
 // BSD-style license that can be found in the LICENSE file.
 
 main() {
-  new Class1();
-  new Class2();
+  use1(new Class1());
+  use2(new Class2());
+}
+
+@pragma('dart2js:noInline')
+use(Object o) {
+  print(o);
+}
+
+@pragma('dart2js:noInline')
+use1(Class1 c1) {
+  c1.field0a = null;
+  c1.field1a = null;
+  c1.field2a = null;
+  c1.field3a = null;
+  c1.field4a = null;
+  c1.field5a = null;
+  c1.field6a = null;
+  c1.field7a = null;
+  c1.field8a = null;
+  c1.field9a = null;
+  c1.field9c = null;
+  c1.field10a = null;
+  c1.field10c = null;
+  c1.field11a = null;
+  c1.field12a = null;
+  c1.field13a = null;
+  use(c1.field0a);
+  use(c1.field0b);
+  use(c1.field1a);
+  use(c1.field1b);
+  use(c1.field2a);
+  use(c1.field2b);
+  use(c1.field3a);
+  use(c1.field3b);
+  use(c1.field4a);
+  use(c1.field4b);
+  use(c1.field5a);
+  use(c1.field5b);
+  use(c1.field6a);
+  use(c1.field6b);
+  use(c1.field7a);
+  use(c1.field7b);
+  use(c1.field8a);
+  use(c1.field8b);
+  use(c1.field9a);
+  use(c1.field9b);
+  use(c1.field9c);
+  use(c1.field9d);
+  use(c1.field10a);
+  use(c1.field10b);
+  use(c1.field10c);
+  use(c1.field10d);
+  use(c1.field11a);
+  use(c1.field11b);
+  use(c1.field12a);
+  use(c1.field12b);
+  use(c1.field13a);
+  use(c1.field13b);
+}
+
+@pragma('dart2js:noInline')
+use2(Class2 c2) {
+  c2.field1a = null;
+  c2.field2a = null;
+  c2.field3a = null;
+  c2.field4a = null;
+  c2.field5a = null;
+  c2.field6a = null;
+  c2.field7a = null;
+  c2.field8a = null;
+  c2.field9a = null;
+  c2.field9c = null;
+  c2.field10a = null;
+  c2.field10c = null;
+  c2.field11a = null;
+  c2.field12a = null;
+  c2.field13a = null;
+  use(c2.field1a);
+  use(c2.field1b);
+  use(c2.field2a);
+  use(c2.field2b);
+  use(c2.field3a);
+  use(c2.field3b);
+  use(c2.field4a);
+  use(c2.field4b);
+  use(c2.field5a);
+  use(c2.field5b);
+  use(c2.field6a);
+  use(c2.field6b);
+  use(c2.field7a);
+  use(c2.field7b);
+  use(c2.field8a);
+  use(c2.field8b);
+  use(c2.field9a);
+  use(c2.field9b);
+  use(c2.field9c);
+  use(c2.field9d);
+  use(c2.field10a);
+  use(c2.field10b);
+  use(c2.field10c);
+  use(c2.field10d);
+  use(c2.field11a);
+  use(c2.field11b);
+  use(c2.field12a);
+  use(c2.field12b);
+  use(c2.field13a);
+  use(c2.field13b);
 }
 
 const bool const1 = true;
 
 class Class1 {
-  /*element: Class1.field0:initial=NullConstant*/
-  var field0;
+  /*element: Class1.field0a:initial=NullConstant*/
+  var field0a;
 
-  /*element: Class1.field1:initial=NullConstant*/
-  var field1 = null;
+  /*element: Class1.field0b:constant=NullConstant*/
+  var field0b;
 
-  /*element: Class1.field2:initial=BoolConstant(true)*/
-  var field2 = true;
+  /*element: Class1.field1a:initial=NullConstant*/
+  var field1a = null;
 
-  /*element: Class1.field3:initial=BoolConstant(false)*/
-  var field3 = false;
+  /*element: Class1.field1b:constant=NullConstant*/
+  var field1b = null;
 
-  /*element: Class1.field4:initial=IntConstant(0)*/
-  var field4 = 0;
+  /*element: Class1.field2a:initial=BoolConstant(true)*/
+  var field2a = true;
 
-  /*element: Class1.field5:initial=IntConstant(1)*/
-  var field5 = 1;
+  /*element: Class1.field2b:constant=BoolConstant(true)*/
+  var field2b = true;
 
-  /*element: Class1.field6:initial=StringConstant("")*/
-  var field6 = '';
+  /*element: Class1.field3a:initial=BoolConstant(false)*/
+  var field3a = false;
 
-  /*element: Class1.field7:initial=StringConstant("foo")*/
-  var field7 = 'foo';
+  /*element: Class1.field3b:constant=BoolConstant(false)*/
+  var field3b = false;
 
-  /*element: Class1.field8:*/
-  var field8 = 0.5;
+  /*element: Class1.field4a:initial=IntConstant(0)*/
+  var field4a = 0;
 
-  /*element: Class1.field9:*/
-  var field9 = const [];
+  /*element: Class1.field4b:constant=IntConstant(0)*/
+  var field4b = 0;
 
-  /*element: Class1.field10:*/
-  var field10 = const {};
+  /*element: Class1.field5a:initial=IntConstant(1)*/
+  var field5a = 1;
 
-  /*element: Class1.field11:*/
-  var field11 = #foo;
+  /*element: Class1.field5b:constant=IntConstant(1)*/
+  var field5b = 1;
 
-  /*element: Class1.field12:initial=IntConstant(5)*/
-  var field12 = 2 + 3;
+  /*element: Class1.field6a:initial=StringConstant("")*/
+  var field6a = '';
 
-  /*element: Class1.field13:initial=BoolConstant(true)*/
-  var field13 = const1;
+  /*element: Class1.field6b:constant=StringConstant("")*/
+  var field6b = '';
+
+  /*element: Class1.field7a:initial=StringConstant("foo")*/
+  var field7a = 'foo';
+
+  /*element: Class1.field7b:constant=StringConstant("foo")*/
+  var field7b = 'foo';
+
+  /*element: Class1.field8a:*/
+  var field8a = 0.5;
+
+  /*element: Class1.field8b:constant=DoubleConstant(0.5)*/
+  var field8b = 0.5;
+
+  /*element: Class1.field9a:*/
+  var field9a = const [];
+
+  /*element: Class1.field9b:constant=ListConstant([])*/
+  var field9b = const [];
+
+  /*element: Class1.field9c:*/
+  var field9c = const [0, 1];
+
+  /*element: Class1.field9d:constant=ListConstant(<int>[IntConstant(0), IntConstant(1), IntConstant(2)])*/
+  var field9d = const [0, 1, 2];
+
+  /*element: Class1.field10a:*/
+  var field10a = const {};
+
+  /*element: Class1.field10b:constant=MapConstant({})*/
+  var field10b = const {};
+
+  /*element: Class1.field10c:*/
+  var field10c = const {0: 1, 2: 3};
+
+  /*element: Class1.field10d:constant=MapConstant(<int, int>{IntConstant(0): IntConstant(1), IntConstant(2): IntConstant(3), IntConstant(4): IntConstant(5)})*/
+  var field10d = const {0: 1, 2: 3, 4: 5};
+
+  /*element: Class1.field11a:*/
+  var field11a = #foo;
+
+  /*element: Class1.field11b:constant=ConstructedConstant(Symbol(_name=StringConstant("foo")))*/
+  var field11b = #foo;
+
+  /*element: Class1.field12a:initial=IntConstant(5)*/
+  var field12a = 2 + 3;
+
+  /*element: Class1.field12b:constant=IntConstant(5)*/
+  var field12b = 2 + 3;
+
+  /*element: Class1.field13a:initial=BoolConstant(true)*/
+  var field13a = const1;
+
+  /*element: Class1.field13b:constant=BoolConstant(true)*/
+  var field13b = const1;
 }
 
 class Class2 {
-  /*element: Class2.field1:*/
-  var field1;
+  /*element: Class2.field1a:*/
+  var field1a;
 
-  /*element: Class2.field2:*/
-  var field2;
+  /*element: Class2.field1b:*/
+  var field1b;
 
-  /*element: Class2.field3:*/
-  var field3;
+  /*element: Class2.field2a:*/
+  var field2a;
 
-  /*element: Class2.field4:*/
-  var field4;
+  /*element: Class2.field2b:*/
+  var field2b;
 
-  /*element: Class2.field5:*/
-  var field5;
+  /*element: Class2.field3a:*/
+  var field3a;
 
-  /*element: Class2.field6:*/
-  var field6;
+  /*element: Class2.field3b:*/
+  var field3b;
 
-  /*element: Class2.field7:*/
-  var field7;
+  /*element: Class2.field4a:*/
+  var field4a;
 
-  /*element: Class2.field8:*/
-  var field8;
+  /*element: Class2.field4b:*/
+  var field4b;
 
-  /*element: Class2.field9:*/
-  var field9;
+  /*element: Class2.field5a:*/
+  var field5a;
 
-  /*element: Class2.field10:*/
-  var field10;
+  /*element: Class2.field5b:*/
+  var field5b;
 
-  /*element: Class2.field11:*/
-  var field11;
+  /*element: Class2.field6a:*/
+  var field6a;
 
-  /*element: Class2.field12:*/
-  var field12;
+  /*element: Class2.field6b:*/
+  var field6b;
 
-  /*element: Class2.field13:*/
-  var field13;
+  /*element: Class2.field7a:*/
+  var field7a;
+
+  /*element: Class2.field7b:*/
+  var field7b;
+
+  /*element: Class2.field8a:*/
+  var field8a;
+
+  /*element: Class2.field8b:*/
+  var field8b;
+
+  /*element: Class2.field9a:*/
+  var field9a;
+
+  /*element: Class2.field9b:*/
+  var field9b;
+
+  /*element: Class2.field9c:*/
+  var field9c;
+
+  /*element: Class2.field9d:*/
+  var field9d;
+
+  /*element: Class2.field10a:*/
+  var field10a;
+
+  /*element: Class2.field10b:*/
+  var field10b;
+
+  /*element: Class2.field10c:*/
+  var field10c;
+
+  /*element: Class2.field10d:*/
+  var field10d;
+
+  /*element: Class2.field11a:*/
+  var field11a;
+
+  /*element: Class2.field11b:*/
+  var field11b;
+
+  /*element: Class2.field12a:*/
+  var field12a;
+
+  /*element: Class2.field12b:*/
+  var field12b;
+
+  /*element: Class2.field13a:*/
+  var field13a;
+
+  /*element: Class2.field13b:*/
+  var field13b;
 
   Class2()
-      : field1 = null,
-        field2 = true,
-        field3 = false,
-        field4 = 0,
-        field5 = 1,
-        field6 = '',
-        field7 = 'foo',
-        field8 = 0.5,
-        field9 = const [],
-        field10 = const {},
-        field11 = #foo,
-        field12 = 2 + 3,
-        field13 = const1;
+      : field1a = null,
+        field1b = null,
+        field2a = true,
+        field2b = true,
+        field3a = false,
+        field3b = false,
+        field4a = 0,
+        field4b = 0,
+        field5a = 1,
+        field5b = 1,
+        field6a = '',
+        field6b = '',
+        field7a = 'foo',
+        field7b = 'foo',
+        field8a = 0.5,
+        field8b = 0.5,
+        field9a = const [],
+        field9b = const [],
+        field9c = const [0, 1],
+        field9d = const [0, 1, 2],
+        field10a = const {},
+        field10b = const {},
+        field10c = const {0: 1, 2: 3},
+        field10d = const {0: 1, 2: 3, 4: 5},
+        field11a = #foo,
+        field11b = #foo,
+        field12a = 2 + 3,
+        field12b = 2 + 3,
+        field13a = const1,
+        field13b = const1;
 }
diff --git a/tests/compiler/dart2js/field_analysis/jfield_analysis_test.dart b/tests/compiler/dart2js/field_analysis/jfield_analysis_test.dart
index 207863e..ebfb525 100644
--- a/tests/compiler/dart2js/field_analysis/jfield_analysis_test.dart
+++ b/tests/compiler/dart2js/field_analysis/jfield_analysis_test.dart
@@ -5,7 +5,6 @@
 import 'dart:io';
 import 'package:async_helper/async_helper.dart';
 import 'package:compiler/src/compiler.dart';
-import 'package:compiler/src/constants/values.dart';
 import 'package:compiler/src/elements/entities.dart';
 import 'package:compiler/src/ir/util.dart';
 import 'package:compiler/src/js_backend/field_analysis.dart';
@@ -25,6 +24,7 @@
 
 class Tags {
   static const String initialValue = 'initial';
+  static const String constantValue = 'constant';
 }
 
 class JAllocatorAnalysisDataComputer extends DataComputer<Features> {
@@ -38,10 +38,14 @@
       JsClosedWorld closedWorld = compiler.backendClosedWorldForTesting;
       JFieldAnalysis allocatorAnalysis = closedWorld.fieldAnalysis;
       ir.Member node = closedWorld.elementMap.getMemberDefinition(member).node;
-      ConstantValue initialValue = allocatorAnalysis.initializerValue(member);
       Features features = new Features();
-      if (initialValue != null) {
-        features[Tags.initialValue] = initialValue.toStructuredText();
+      if (allocatorAnalysis.isInitializedInAllocator(member)) {
+        features[Tags.initialValue] =
+            allocatorAnalysis.initializerValue(member).toStructuredText();
+      }
+      if (allocatorAnalysis.isEffectivelyConstant(member)) {
+        features[Tags.constantValue] =
+            allocatorAnalysis.getConstantValue(member).toStructuredText();
       }
       Id id = computeEntityId(node);
       actualMap[id] = new ActualData<Features>(
diff --git a/tests/compiler/dart2js/model/constant_expression_evaluate_test.dart b/tests/compiler/dart2js/model/constant_expression_evaluate_test.dart
index c35cadf..0660b7f 100644
--- a/tests/compiler/dart2js/model/constant_expression_evaluate_test.dart
+++ b/tests/compiler/dart2js/model/constant_expression_evaluate_test.dart
@@ -14,10 +14,10 @@
 import 'package:compiler/src/constants/evaluation.dart';
 import 'package:compiler/src/constants/expressions.dart';
 import 'package:compiler/src/constants/values.dart';
-import 'package:compiler/src/constant_system_dart.dart';
 import 'package:compiler/src/diagnostics/messages.dart';
 import 'package:compiler/src/elements/entities.dart';
 import 'package:compiler/src/elements/types.dart';
+import 'package:compiler/src/js_backend/constant_system_javascript.dart';
 import 'package:compiler/src/kernel/kernel_strategy.dart';
 import 'package:compiler/src/kernel/element_map_impl.dart';
 import '../helpers/memory_compiler.dart';
@@ -146,7 +146,8 @@
     const ConstantData('false', 'BoolConstant(false)'),
     const ConstantData('true', 'BoolConstant(true)'),
     const ConstantData('0', 'IntConstant(0)'),
-    const ConstantData('0.0', 'DoubleConstant(0.0)'),
+    const ConstantData('0.0', 'IntConstant(0)'),
+    const ConstantData('0.5', 'DoubleConstant(0.5)'),
     const ConstantData('"foo"', 'StringConstant("foo")'),
     const ConstantData('1 + 2', 'IntConstant(3)'),
     const ConstantData('-(1)', 'IntConstant(-1)'),
@@ -633,7 +634,7 @@
           MemoryEnvironment environment =
               new MemoryEnvironment(getEnvironment(compiler, field), env);
           ConstantValue value =
-              constant.evaluate(environment, DART_CONSTANT_SYSTEM);
+              constant.evaluate(environment, JavaScriptConstantSystem.only);
 
           Expect.isNotNull(
               value,
diff --git a/tests/compiler/dart2js/optimization/data/effectively_constant_fields.dart b/tests/compiler/dart2js/optimization/data/effectively_constant_fields.dart
new file mode 100644
index 0000000..70578fc
--- /dev/null
+++ b/tests/compiler/dart2js/optimization/data/effectively_constant_fields.dart
@@ -0,0 +1,83 @@
+// Copyright (c) 2019, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:expect/expect.dart';
+
+class Class1 {
+  var field1 = 0;
+
+  @pragma('dart2js:noElision')
+  var field2 = 0;
+
+  var field3 = 0;
+}
+
+/*element: method1:
+ ConstantFieldGet=[name=Class1.field1&value=IntConstant(0)],
+ FieldGet=[]
+*/
+@pragma('dart2js:noInline')
+method1(Class1 c) {
+  return c.field1;
+}
+
+/*element: method2:FieldGet=[name=Class1.field2]*/
+@pragma('dart2js:noInline')
+method2(Class1 c) {
+  return c.field2;
+}
+
+class Class2 {
+  var field3 = 0;
+}
+
+@pragma('dart2js:noInline')
+method3(c) {
+  return c.field3;
+}
+
+int _field4() => 0;
+
+class Class3 {
+  int Function() field4 = _field4;
+}
+
+/*element: method4:
+ ConstantFieldCall=[name=Class3.field4&value=FunctionConstant(_field4)],
+ FieldCall=[]
+*/
+@pragma('dart2js:noInline')
+method4(Class3 c) {
+  return c.field4();
+}
+
+/*element: method6:
+ ConstantFieldGet=[name=Class1.field1&value=IntConstant(0)],
+ FieldGet=[name=<null-guard>]
+*/
+@pragma('dart2js:noInline')
+method6(Class1 c) {
+  return c.field1;
+}
+
+/*element: method7:
+ ConstantFieldCall=[name=Class3.field4&value=FunctionConstant(_field4)],
+ FieldCall=[name=<null-guard>]
+*/
+@pragma('dart2js:noInline')
+method7(Class3 c) {
+  return c.field4();
+}
+
+main() {
+  Expect.equals(0, method1(new Class1()));
+  Expect.equals(0, method2(new Class1()));
+  Expect.equals(0, method3(new Class1()));
+  Expect.equals(0, method3(new Class2()));
+  Expect.equals(0, method4(new Class3()));
+  Expect.equals(0, method6(new Class1()));
+  Expect.throws(() => method6(null));
+  Expect.equals(4, method7(new Class3()));
+  Expect.throws(() => method7(null));
+}
diff --git a/tests/compiler/dart2js/optimization/data/field_get.dart b/tests/compiler/dart2js/optimization/data/field_get.dart
index 349edca..e9101e5 100644
--- a/tests/compiler/dart2js/optimization/data/field_get.dart
+++ b/tests/compiler/dart2js/optimization/data/field_get.dart
@@ -10,9 +10,11 @@
   method3(new Class3b());
   method4(new Class4a());
   method4(new Class4b());
+  method5(new Class5a());
 }
 
 class Class1a {
+  @pragma('dart2js:noElision')
   int field1;
 }
 
@@ -23,6 +25,7 @@
 }
 
 class Class2a {
+  @pragma('dart2js:noElision')
   int field2;
 }
 
@@ -60,3 +63,14 @@
 method4(Class4a c) {
   return c.field4;
 }
+
+class Class5a {
+  @pragma('dart2js:noElision')
+  int Function() field5;
+}
+
+/*element: method5:FieldCall=[name=Class5a.field5]*/
+@pragma('dart2js:noInline')
+method5(Class5a c) {
+  return c.field5();
+}
diff --git a/tests/compiler/dart2js/sourcemaps/stacktrace/null_instance_field.dart b/tests/compiler/dart2js/sourcemaps/stacktrace/null_instance_field.dart
index a909569..3788fd0 100644
--- a/tests/compiler/dart2js/sourcemaps/stacktrace/null_instance_field.dart
+++ b/tests/compiler/dart2js/sourcemaps/stacktrace/null_instance_field.dart
@@ -14,5 +14,6 @@
 }
 
 class Class {
+  @pragma('dart2js:noElision')
   var field;
 }
diff --git a/tests/compiler/dart2js/sourcemaps/stacktrace/null_instance_field_elided.dart b/tests/compiler/dart2js/sourcemaps/stacktrace/null_instance_field_elided.dart
new file mode 100644
index 0000000..832d6ad
--- /dev/null
+++ b/tests/compiler/dart2js/sourcemaps/stacktrace/null_instance_field_elided.dart
@@ -0,0 +1,18 @@
+// Copyright (c) 2019, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:expect/expect.dart';
+
+main() {
+  /*1:main*/ test(new Class());
+}
+
+@NoInline()
+test(c) {
+  c.field. /*2:test*/ method();
+}
+
+class Class {
+  var field;
+}
diff --git a/tests/compiler/dart2js/sourcemaps/stacktrace/sync_throw_in_async.dart b/tests/compiler/dart2js/sourcemaps/stacktrace/sync_throw_in_async.dart
index 13e5ecd..79a0fc9 100644
--- a/tests/compiler/dart2js/sourcemaps/stacktrace/sync_throw_in_async.dart
+++ b/tests/compiler/dart2js/sourcemaps/stacktrace/sync_throw_in_async.dart
@@ -9,7 +9,8 @@
   /*1:main*/ test();
 }
 
+// TODO(34942): Step 2 should point to the body block.
 @NoInline()
-test() async /*2:test*/ {
+test /*2:test*/ () async {
   /*4:test*/ throw '>ExceptionMarker<';
 }
diff --git a/tests/compiler/dart2js/sourcemaps/stacktrace/sync_throw_in_awaited_async.dart b/tests/compiler/dart2js/sourcemaps/stacktrace/sync_throw_in_awaited_async.dart
index 49c7a18..41e377e 100644
--- a/tests/compiler/dart2js/sourcemaps/stacktrace/sync_throw_in_awaited_async.dart
+++ b/tests/compiler/dart2js/sourcemaps/stacktrace/sync_throw_in_awaited_async.dart
@@ -9,13 +9,15 @@
   /*1:main*/ test1();
 }
 
+// TODO(34942): Step 3 should point to the body block.
 @NoInline()
-test1() async /*3:test1*/ {
+test1 /*3:test1*/ () async {
   // This call is on the stack when the error is thrown.
   await /*5:test1*/ test2();
 }
 
+// TODO(34942): Step 7 should point to the body block.
 @NoInline()
-test2() async /*7:test2*/ {
+test2 /*7:test2*/ () async {
   /*9:test2*/ throw '>ExceptionMarker<';
 }
diff --git a/tests/compiler/dart2js/sourcemaps/stacktrace/sync_throw_in_top_level_method_from_async.dart b/tests/compiler/dart2js/sourcemaps/stacktrace/sync_throw_in_top_level_method_from_async.dart
index 7ea1cf9..69e9741 100644
--- a/tests/compiler/dart2js/sourcemaps/stacktrace/sync_throw_in_top_level_method_from_async.dart
+++ b/tests/compiler/dart2js/sourcemaps/stacktrace/sync_throw_in_top_level_method_from_async.dart
@@ -8,8 +8,9 @@
   /*1:main*/ test1();
 }
 
+// TODO(34942): Step 2 should point to the body block.
 @NoInline()
-test1() async /*2:test1*/ {
+test1 /*2:test1*/ () async {
   /*9:test1*/ test2();
 }
 
diff --git a/tests/compiler/dart2js/sourcemaps/stacktrace_test.dart b/tests/compiler/dart2js/sourcemaps/stacktrace_test.dart
index 5d4d3b7..a4a6fa0 100644
--- a/tests/compiler/dart2js/sourcemaps/stacktrace_test.dart
+++ b/tests/compiler/dart2js/sourcemaps/stacktrace_test.dart
@@ -8,6 +8,7 @@
 import 'package:args/args.dart';
 import 'package:async_helper/async_helper.dart';
 import 'package:compiler/compiler_new.dart';
+import 'package:compiler/src/commandline_options.dart';
 import 'package:compiler/src/dart2js.dart' as entry;
 
 import 'package:sourcemap_testing/src/stacktrace_helper.dart';
@@ -83,6 +84,7 @@
       '-o$output',
       '--libraries-spec=sdk/lib/libraries.json',
       '--packages=${Platform.packageConfig}',
+      Flags.testMode,
       input,
     ]..addAll(options);
     print("Compiling dart2js ${arguments.join(' ')}");
diff --git a/tests/compiler/dart2js_extra/effectively_constant_fields_test.dart b/tests/compiler/dart2js_extra/effectively_constant_fields_test.dart
new file mode 100644
index 0000000..6c9c18b
--- /dev/null
+++ b/tests/compiler/dart2js_extra/effectively_constant_fields_test.dart
@@ -0,0 +1,74 @@
+// Copyright (c) 2019, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:expect/expect.dart';
+
+_field4() => 4;
+
+class Class1 {
+  var field1 = 0;
+
+  @pragma('dart2js:noElision')
+  var field2 = 1;
+
+  var field3 = 2;
+
+  var field4 = _field4;
+}
+
+@pragma('dart2js:noInline')
+method1(Class1 c) {
+  return c.field1;
+}
+
+@pragma('dart2js:noInline')
+method2(Class1 c) {
+  return c.field2;
+}
+
+class Class2 {
+  var field3 = 3;
+}
+
+@pragma('dart2js:noInline')
+method3(c) {
+  return c.field3;
+}
+
+class Class3 extends Class1 {
+  @pragma('dart2js:noInline')
+  method4() {
+    return super.field1;
+  }
+}
+
+class Class4 extends Class1 {
+  @pragma('dart2js:noInline')
+  method5() {
+    return super.field4();
+  }
+}
+
+@pragma('dart2js:noInline')
+method6(Class1 c) {
+  return c.field1;
+}
+
+@pragma('dart2js:noInline')
+method7(Class1 c) {
+  return c.field4();
+}
+
+main() {
+  Expect.equals(0, method1(new Class1()));
+  Expect.equals(1, method2(new Class1()));
+  Expect.equals(2, method3(new Class1()));
+  Expect.equals(3, method3(new Class2()));
+  Expect.equals(0, new Class3().method4());
+  Expect.equals(4, new Class4().method5());
+  Expect.equals(0, method6(new Class1()));
+  Expect.throws(() => method6(null));
+  Expect.equals(4, method7(new Class1()));
+  Expect.throws(() => method7(null));
+}