Remove StaticMemberUsage and cleanup codegen world builder

Change-Id: I4fdc6148af671010d1aca55c05d08e55e9c1e9fd
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/100245
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
diff --git a/pkg/compiler/lib/src/enqueue.dart b/pkg/compiler/lib/src/enqueue.dart
index ef66b6e..aeca0ec 100644
--- a/pkg/compiler/lib/src/enqueue.dart
+++ b/pkg/compiler/lib/src/enqueue.dart
@@ -283,11 +283,10 @@
   void _registerInstantiatedType(InterfaceType type,
       {ConstructorEntity constructor,
       bool nativeUsage: false,
-      bool globalDependency: false,
-      bool isRedirection: false}) {
+      bool globalDependency: false}) {
     task.measure(() {
       _worldBuilder.registerTypeInstantiation(type, _applyClassUse,
-          constructor: constructor, isRedirection: isRedirection);
+          constructor: constructor);
       listener.registerInstantiatedType(type,
           isGlobal: globalDependency, nativeUsage: nativeUsage);
     });
@@ -371,12 +370,6 @@
         _registerInstantiatedType(staticUse.type,
             constructor: staticUse.element, globalDependency: false);
         break;
-      case StaticUseKind.REDIRECTION:
-        _registerInstantiatedType(staticUse.type,
-            constructor: staticUse.element,
-            globalDependency: false,
-            isRedirection: true);
-        break;
       default:
         break;
     }
diff --git a/pkg/compiler/lib/src/js_backend/codegen_listener.dart b/pkg/compiler/lib/src/js_backend/codegen_listener.dart
index 57e2fc0..2306ad6 100644
--- a/pkg/compiler/lib/src/js_backend/codegen_listener.dart
+++ b/pkg/compiler/lib/src/js_backend/codegen_listener.dart
@@ -82,8 +82,12 @@
       mainImpact.registerStaticUse(
           new StaticUse.staticInvoke(mainMethod, callStructure));
     }
-    mainImpact.registerStaticUse(
-        new StaticUse.staticInvoke(mainMethod, CallStructure.NO_ARGS));
+    if (mainMethod.isGetter) {
+      mainImpact.registerStaticUse(new StaticUse.staticGet(mainMethod));
+    } else {
+      mainImpact.registerStaticUse(
+          new StaticUse.staticInvoke(mainMethod, CallStructure.NO_ARGS));
+    }
     return mainImpact;
   }
 
diff --git a/pkg/compiler/lib/src/js_backend/enqueuer.dart b/pkg/compiler/lib/src/js_backend/enqueuer.dart
index 9eb8195..02eff30 100644
--- a/pkg/compiler/lib/src/js_backend/enqueuer.dart
+++ b/pkg/compiler/lib/src/js_backend/enqueuer.dart
@@ -171,7 +171,6 @@
     switch (staticUse.kind) {
       case StaticUseKind.CONSTRUCTOR_INVOKE:
       case StaticUseKind.CONST_CONSTRUCTOR_INVOKE:
-      case StaticUseKind.REDIRECTION:
         processTypeUse(new TypeUse.instantiation(staticUse.type));
         break;
       case StaticUseKind.INLINING:
diff --git a/pkg/compiler/lib/src/js_backend/resolution_listener.dart b/pkg/compiler/lib/src/js_backend/resolution_listener.dart
index e084b4c..04381e4 100644
--- a/pkg/compiler/lib/src/js_backend/resolution_listener.dart
+++ b/pkg/compiler/lib/src/js_backend/resolution_listener.dart
@@ -121,8 +121,12 @@
       mainImpact.registerStaticUse(
           new StaticUse.staticInvoke(mainMethod, callStructure));
     }
-    mainImpact.registerStaticUse(
-        new StaticUse.staticInvoke(mainMethod, CallStructure.NO_ARGS));
+    if (mainMethod.isGetter) {
+      mainImpact.registerStaticUse(new StaticUse.staticGet(mainMethod));
+    } else {
+      mainImpact.registerStaticUse(
+          new StaticUse.staticInvoke(mainMethod, CallStructure.NO_ARGS));
+    }
     return mainImpact;
   }
 
diff --git a/pkg/compiler/lib/src/js_backend/runtime_types.dart b/pkg/compiler/lib/src/js_backend/runtime_types.dart
index 5094315..c73ae24 100644
--- a/pkg/compiler/lib/src/js_backend/runtime_types.dart
+++ b/pkg/compiler/lib/src/js_backend/runtime_types.dart
@@ -2107,8 +2107,7 @@
       }
     });
 
-    for (FunctionEntity element
-        in codegenWorldBuilder.staticFunctionsNeedingGetter) {
+    for (FunctionEntity element in codegenWorldBuilder.closurizedStatics) {
       FunctionType functionType = _elementEnvironment.getFunctionType(element);
       liveTypeVisitor.visitType(functionType, TypeVisitorState.direct);
     }
diff --git a/pkg/compiler/lib/src/js_emitter/program_builder/field_visitor.dart b/pkg/compiler/lib/src/js_emitter/program_builder/field_visitor.dart
index 493afa9..28f512c 100644
--- a/pkg/compiler/lib/src/js_emitter/program_builder/field_visitor.dart
+++ b/pkg/compiler/lib/src/js_emitter/program_builder/field_visitor.dart
@@ -141,7 +141,7 @@
   bool fieldNeedsGetter(FieldEntity field) {
     assert(field.isField);
     if (fieldAccessNeverThrows(field)) return false;
-    return field.enclosingClass != null &&
+    return field.isInstanceMember &&
         _codegenWorldBuilder.hasInvokedGetter(field);
   }
 
@@ -149,7 +149,7 @@
     assert(field.isField);
     if (fieldAccessNeverThrows(field)) return false;
     if (!field.isAssignable) return false;
-    return field.enclosingClass != null &&
+    return field.isInstanceMember &&
         _codegenWorldBuilder.hasInvokedSetter(field);
   }
 
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 f5b5b0c..a416193 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
@@ -1148,8 +1148,8 @@
         !element.isConstructor && !element.isGetter && !element.isSetter;
     bool canBeApplied = _methodCanBeApplied(element);
 
-    bool needsTearOff = isApplyTarget &&
-        _worldBuilder.staticFunctionsNeedingGetter.contains(element);
+    bool needsTearOff =
+        isApplyTarget && _worldBuilder.closurizedStatics.contains(element);
 
     js.Name tearOffName =
         needsTearOff ? _namer.staticClosureName(element) : null;
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 1cac12f..e906986 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
@@ -1623,6 +1623,7 @@
         // avoid redundant declaration of local variable, for instance for
         // type arguments.
         js.Fun code = field.code;
+        assert(code != null, "No code for $field");
         if (code.params.isEmpty &&
             code.body.statements.length == 1 &&
             code.body.statements.last is js.Return) {
diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart
index 6c5852a..3ba7b09 100644
--- a/pkg/compiler/lib/src/ssa/codegen.dart
+++ b/pkg/compiler/lib/src/ssa/codegen.dart
@@ -2092,12 +2092,23 @@
       push(js.js('# || #', [arguments[0], right]).withSourceInformation(
           node.sourceInformation));
     } else {
-      CallStructure callStructure = new CallStructure.unnamed(
-          arguments.length, node.typeArguments.length);
-      _registry.registerStaticUse(element.isConstructor
-          ? new StaticUse.constructorInvoke(element, callStructure)
-          : new StaticUse.staticInvoke(
-              element, callStructure, node.typeArguments));
+      StaticUse staticUse;
+      if (element.isConstructor) {
+        CallStructure callStructure = new CallStructure.unnamed(
+            arguments.length, node.typeArguments.length);
+        staticUse = new StaticUse.constructorInvoke(element, callStructure);
+      } else if (element.isGetter) {
+        staticUse = new StaticUse.staticGet(element);
+      } else if (element.isSetter) {
+        staticUse = new StaticUse.staticSet(element);
+      } else {
+        assert(element.isFunction);
+        CallStructure callStructure = new CallStructure.unnamed(
+            arguments.length, node.typeArguments.length);
+        staticUse = new StaticUse.staticInvoke(
+            element, callStructure, node.typeArguments);
+      }
+      _registry.registerStaticUse(staticUse);
       push(_emitter.staticFunctionAccess(element));
       push(new js.Call(pop(), arguments,
           sourceInformation: node.sourceInformation));
@@ -2108,26 +2119,47 @@
   visitInvokeSuper(HInvokeSuper node) {
     MemberEntity superElement = node.element;
     ClassEntity superClass = superElement.enclosingClass;
+    Selector selector = node.selector;
+    bool useAliasedSuper = _superMemberData.maybeRegisterAliasedSuperMember(
+        superElement, selector);
+    if (selector.isGetter) {
+      if (superElement.isField || superElement.isGetter) {
+        _registry.registerStaticUse(new StaticUse.superGet(superElement));
+      } else {
+        _registry.registerStaticUse(new StaticUse.superTearOff(node.element));
+      }
+    } else if (selector.isSetter) {
+      if (superElement.isField) {
+        _registry.registerStaticUse(new StaticUse.superFieldSet(superElement));
+      } else {
+        assert(superElement.isSetter);
+        _registry.registerStaticUse(new StaticUse.superSetterSet(superElement));
+      }
+    } else {
+      if (_superMemberData.maybeRegisterAliasedSuperMember(
+          superElement, selector)) {
+        _registry.registerStaticUse(new StaticUse.superInvoke(
+            superElement, new CallStructure.unnamed(node.inputs.length)));
+      } else {
+        _registry.registerStaticUse(new StaticUse.superInvoke(
+            superElement, new CallStructure.unnamed(node.inputs.length - 1)));
+      }
+    }
+
     if (superElement.isField) {
       js.Name fieldName = _namer.instanceFieldPropertyName(superElement);
       use(node.inputs[0]);
       js.PropertyAccess access = new js.PropertyAccess(pop(), fieldName)
           .withSourceInformation(node.sourceInformation);
       if (node.isSetter) {
-        _registry.registerStaticUse(superElement.isSetter
-            ? new StaticUse.superSetterSet(superElement)
-            : new StaticUse.superFieldSet(superElement));
         use(node.value);
         push(new js.Assignment(access, pop())
             .withSourceInformation(node.sourceInformation));
       } else {
-        _registry.registerStaticUse(new StaticUse.superGet(superElement));
         push(access);
       }
     } else {
-      Selector selector = node.selector;
-      if (!_superMemberData.maybeRegisterAliasedSuperMember(
-          superElement, selector)) {
+      if (!useAliasedSuper) {
         js.Name methodName;
         if (selector.isGetter && !superElement.isGetter) {
           // If this is a tear-off, register the fact that a tear-off closure
@@ -2139,13 +2171,11 @@
               new CallStructure.unnamed(
                   node.inputs.length, node.typeArguments.length),
               node.typeArguments));
-          _registry.registerStaticUse(new StaticUse.superTearOff(node.element));
           methodName = _namer.invocationName(selector);
         } else {
           methodName = _namer.instanceMethodName(superElement);
         }
-        _registry.registerStaticUse(new StaticUse.superInvoke(
-            superElement, new CallStructure.unnamed(node.inputs.length)));
+
         push(js.js('#.#.call(#)', [
           _emitter.prototypeAccess(superClass, hasBeenInstantiated: true),
           methodName,
@@ -2153,8 +2183,6 @@
         ]).withSourceInformation(node.sourceInformation));
       } else {
         use(node.receiver);
-        _registry.registerStaticUse(new StaticUse.superInvoke(
-            superElement, new CallStructure.unnamed(node.inputs.length - 1)));
         push(js.js('#.#(#)', [
           pop(),
           _namer.aliasedSuperMemberPropertyName(superElement),
diff --git a/pkg/compiler/lib/src/universe/codegen_world_builder.dart b/pkg/compiler/lib/src/universe/codegen_world_builder.dart
index eb54121..d1f075e 100644
--- a/pkg/compiler/lib/src/universe/codegen_world_builder.dart
+++ b/pkg/compiler/lib/src/universe/codegen_world_builder.dart
@@ -4,6 +4,7 @@
 
 import 'dart:collection';
 
+import '../common.dart';
 import '../common/names.dart' show Identifiers;
 import '../common_elements.dart';
 import '../constants/values.dart';
@@ -20,6 +21,8 @@
 import 'use.dart'
     show ConstantUse, DynamicUse, DynamicUseKind, StaticUse, StaticUseKind;
 import 'world_builder.dart';
+import '../js_model/elements.dart' show JSignatureMethod;
+import 'call_structure.dart';
 
 /// World builder specific to codegen.
 ///
@@ -75,7 +78,6 @@
 
   Map<Selector, SelectorConstraints> setterInvocationsByName(String name);
 
-  Iterable<FunctionEntity> get staticFunctionsNeedingGetter;
   Iterable<FunctionEntity> get methodsNeedingSuperGetter;
 
   /// The set of all referenced static fields.
@@ -131,9 +133,6 @@
   final Set<FieldEntity> allReferencedStaticFields = new Set<FieldEntity>();
 
   @override
-  final Set<FunctionEntity> staticFunctionsNeedingGetter =
-      new Set<FunctionEntity>();
-  @override
   final Set<FunctionEntity> methodsNeedingSuperGetter =
       new Set<FunctionEntity>();
   final Map<String, Map<Selector, SelectorConstraints>> _invokedNames =
@@ -148,20 +147,10 @@
 
   Map<ClassEntity, ClassUsage> get classUsageForTesting => _processedClasses;
 
-  /// Map of registered usage of static members of live classes.
-  final Map<Entity, StaticMemberUsage> _staticMemberUsage =
-      <Entity, StaticMemberUsage>{};
-
-  Map<Entity, StaticMemberUsage> get staticMemberUsageForTesting =>
-      _staticMemberUsage;
-
-  /// Map of registered usage of instance members of live classes.
-  final Map<MemberEntity, MemberUsage> _instanceMemberUsage =
+  /// Map of registered usage of static and instance members.
+  final Map<MemberEntity, MemberUsage> _memberUsage =
       <MemberEntity, MemberUsage>{};
 
-  Map<MemberEntity, MemberUsage> get instanceMemberUsageForTesting =>
-      _instanceMemberUsage;
-
   /// Map containing instance members of live classes that are not yet live
   /// themselves.
   final Map<String, Set<MemberUsage>> _instanceMembersByName =
@@ -372,62 +361,23 @@
     isChecks.add(type.unaliased);
   }
 
-  void _registerStaticUse(StaticUse staticUse) {
-    if (staticUse.element is FieldEntity) {
-      FieldEntity field = staticUse.element;
-      if (field.isTopLevel || field.isStatic) {
-        allReferencedStaticFields.add(field);
-      }
-    }
-    switch (staticUse.kind) {
-      case StaticUseKind.STATIC_TEAR_OFF:
-        staticFunctionsNeedingGetter.add(staticUse.element);
-        break;
-      case StaticUseKind.SUPER_TEAR_OFF:
-        methodsNeedingSuperGetter.add(staticUse.element);
-        break;
-      case StaticUseKind.SUPER_FIELD_SET:
-      case StaticUseKind.FIELD_SET:
-      case StaticUseKind.CLOSURE:
-      case StaticUseKind.CLOSURE_CALL:
-      case StaticUseKind.CALL_METHOD:
-      case StaticUseKind.FIELD_GET:
-      case StaticUseKind.CONSTRUCTOR_INVOKE:
-      case StaticUseKind.CONST_CONSTRUCTOR_INVOKE:
-      case StaticUseKind.REDIRECTION:
-      case StaticUseKind.DIRECT_INVOKE:
-      case StaticUseKind.INLINING:
-      case StaticUseKind.INVOKE:
-      case StaticUseKind.GET:
-      case StaticUseKind.SET:
-      case StaticUseKind.FIELD_INIT:
-      case StaticUseKind.FIELD_CONSTANT_INIT:
-        break;
-    }
-  }
-
   void registerStaticUse(StaticUse staticUse, MemberUsedCallback memberUsed) {
-    Entity element = staticUse.element;
-    _registerStaticUse(staticUse);
-    StaticMemberUsage usage = _staticMemberUsage.putIfAbsent(element, () {
-      if (element is MemberEntity &&
-          (element.isStatic || element.isTopLevel) &&
-          element.isFunction) {
-        return new StaticFunctionUsage(element);
-      } else {
-        return new GeneralStaticMemberUsage(element);
+    MemberEntity element = staticUse.element;
+    if (element is FieldEntity) {
+      if (element.isTopLevel || element.isStatic) {
+        allReferencedStaticFields.add(element);
       }
-    });
+    }
+
     EnumSet<MemberUse> useSet = new EnumSet<MemberUse>();
+    MemberUsage usage = _getMemberUsage(element, useSet);
     switch (staticUse.kind) {
       case StaticUseKind.STATIC_TEAR_OFF:
         closurizedStatics.add(element);
-        useSet.addAll(usage.tearOff());
+        useSet.addAll(usage.read());
         break;
       case StaticUseKind.FIELD_GET:
       case StaticUseKind.FIELD_SET:
-      case StaticUseKind.CLOSURE:
-      case StaticUseKind.CLOSURE_CALL:
       case StaticUseKind.CALL_METHOD:
         // TODO(johnniwinther): Avoid this. Currently [FIELD_GET] and
         // [FIELD_SET] contains [BoxFieldElement]s which we cannot enqueue.
@@ -436,30 +386,39 @@
         break;
       case StaticUseKind.INVOKE:
         registerStaticInvocation(staticUse);
-        useSet.addAll(usage.normalUse());
+        // We don't track parameters in the codegen world builder, so we
+        // pass `null` instead of the concrete call structure.
+        useSet.addAll(usage.invoke(null));
         break;
       case StaticUseKind.SUPER_FIELD_SET:
-      case StaticUseKind.SUPER_TEAR_OFF:
-      case StaticUseKind.GET:
       case StaticUseKind.SET:
+        useSet.addAll(usage.write());
+        break;
+      case StaticUseKind.SUPER_TEAR_OFF:
+        methodsNeedingSuperGetter.add(element);
+        useSet.addAll(usage.read());
+        break;
+      case StaticUseKind.GET:
+        useSet.addAll(usage.read());
+        break;
       case StaticUseKind.FIELD_INIT:
+        useSet.addAll(usage.init());
+        break;
       case StaticUseKind.FIELD_CONSTANT_INIT:
-        useSet.addAll(usage.normalUse());
+        useSet.addAll(usage.constantInit(staticUse.constant));
         break;
       case StaticUseKind.CONSTRUCTOR_INVOKE:
       case StaticUseKind.CONST_CONSTRUCTOR_INVOKE:
-      case StaticUseKind.REDIRECTION:
-        useSet.addAll(usage.normalUse());
+        // We don't track parameters in the codegen world builder, so we
+        // pass `null` instead of the concrete call structure.
+        useSet.addAll(usage.invoke(null));
         break;
       case StaticUseKind.DIRECT_INVOKE:
         MemberEntity member = staticUse.element;
-        MemberUsage instanceUsage = _getMemberUsage(member, memberUsed);
         // We don't track parameters in the codegen world builder, so we
         // pass `null` instead of the concrete call structure.
-        memberUsed(instanceUsage.entity, instanceUsage.invoke(null));
-        _instanceMembersByName[instanceUsage.entity.name]
-            ?.remove(instanceUsage);
-        useSet.addAll(usage.normalUse());
+        useSet.addAll(usage.invoke(null));
+        _instanceMembersByName[usage.entity.name]?.remove(usage);
         if (staticUse.typeArguments?.isNotEmpty ?? false) {
           registerDynamicInvocation(
               new Selector.call(member.memberName, staticUse.callStructure),
@@ -469,6 +428,10 @@
       case StaticUseKind.INLINING:
         registerStaticInvocation(staticUse);
         break;
+      case StaticUseKind.CLOSURE:
+      case StaticUseKind.CLOSURE_CALL:
+        failedAt(CURRENT_ELEMENT_SPANNABLE,
+            "Static use ${staticUse.kind} is not supported during codegen.");
     }
     if (useSet.isNotEmpty) {
       memberUsed(usage.entity, useSet);
@@ -488,57 +451,76 @@
     });
   }
 
-  void _processInstantiatedClassMember(ClassEntity cls,
-      covariant MemberEntity member, MemberUsedCallback memberUsed,
+  void _processInstantiatedClassMember(
+      ClassEntity cls, MemberEntity member, MemberUsedCallback memberUsed,
       {bool dryRun: false}) {
     if (!member.isInstanceMember) return;
-    _getMemberUsage(member, memberUsed);
+    EnumSet<MemberUse> useSet = new EnumSet<MemberUse>();
+    _getMemberUsage(member, useSet);
+    if (useSet.isNotEmpty) {
+      memberUsed(member, useSet);
+    }
   }
 
-  MemberUsage _getMemberUsage(
-      covariant MemberEntity member, MemberUsedCallback memberUsed,
+  MemberUsage _getMemberUsage(MemberEntity member, EnumSet<MemberUse> useSet,
       {bool dryRun: false}) {
     // TODO(johnniwinther): Change [TypeMask] to not apply to a superclass
     // member unless the class has been instantiated. Similar to
     // [StrongModeConstraint].
-    MemberUsage usage = _instanceMemberUsage[member];
+    MemberUsage usage = _memberUsage[member];
     if (usage == null) {
-      String memberName = member.name;
-      ClassEntity cls = member.enclosingClass;
-      bool isNative = _nativeBasicData.isNativeClass(cls);
-      usage = new MemberUsage(member, isNative: isNative);
-      EnumSet<MemberUse> useSet = new EnumSet<MemberUse>();
-      useSet.addAll(usage.appliedUse);
-      if (!usage.hasRead && hasInvokedGetter(member)) {
-        useSet.addAll(usage.read());
-      }
-      if (!usage.hasWrite && hasInvokedSetter(member)) {
-        useSet.addAll(usage.write());
-      }
-      if (!usage.hasInvoke && hasInvocation(member)) {
-        // We don't track parameters in the codegen world builder, so we
-        // pass `null` instead of the concrete call structures.
-        useSet.addAll(usage.invoke(null));
-      }
+      if (member.isInstanceMember) {
+        String memberName = member.name;
+        ClassEntity cls = member.enclosingClass;
+        bool isNative = _nativeBasicData.isNativeClass(cls);
+        usage = new MemberUsage(member);
+        if (member.isField && !isNative) {
+          useSet.addAll(usage.init());
+        }
+        if (member is JSignatureMethod) {
+          // We mark signature methods as "always used" to prevent them from being
+          // optimized away.
+          // TODO(johnniwinther): Make this a part of the regular enqueueing.
+          useSet.addAll(usage.invoke(CallStructure.NO_ARGS));
+        }
 
-      if (!dryRun) {
-        if (usage.hasPendingClosurizationUse) {
-          // Store the member in [instanceFunctionsByName] to catch
-          // getters on the function.
-          _instanceFunctionsByName
-              .putIfAbsent(usage.entity.name, () => new Set<MemberUsage>())
-              .add(usage);
+        if (!usage.hasRead && hasInvokedGetter(member)) {
+          useSet.addAll(usage.read());
         }
-        if (usage.hasPendingNormalUse) {
-          // The element is not yet used. Add it to the list of instance
-          // members to still be processed.
-          _instanceMembersByName
-              .putIfAbsent(memberName, () => new Set<MemberUsage>())
-              .add(usage);
+        if (!usage.hasWrite && hasInvokedSetter(member)) {
+          useSet.addAll(usage.write());
         }
-        _instanceMemberUsage[member] = usage;
+        if (!usage.hasInvoke && hasInvocation(member)) {
+          // We don't track parameters in the codegen world builder, so we
+          // pass `null` instead of the concrete call structures.
+          useSet.addAll(usage.invoke(null));
+        }
+
+        if (!dryRun) {
+          if (usage.hasPendingClosurizationUse) {
+            // Store the member in [instanceFunctionsByName] to catch
+            // getters on the function.
+            _instanceFunctionsByName
+                .putIfAbsent(usage.entity.name, () => new Set<MemberUsage>())
+                .add(usage);
+          }
+          if (usage.hasPendingNormalUse) {
+            // The element is not yet used. Add it to the list of instance
+            // members to still be processed.
+            _instanceMembersByName
+                .putIfAbsent(memberName, () => new Set<MemberUsage>())
+                .add(usage);
+          }
+        }
+      } else {
+        usage = new MemberUsage(member);
+        if (member.isField) {
+          useSet.addAll(usage.init());
+        }
       }
-      memberUsed(member, useSet);
+      if (!dryRun) {
+        _memberUsage[member] = usage;
+      }
     } else {
       if (dryRun) {
         usage = usage.clone();
@@ -640,7 +622,7 @@
       }
     }
 
-    _instanceMemberUsage.forEach(processMemberUse);
+    _memberUsage.forEach(processMemberUse);
     return functions;
   }
 
@@ -658,7 +640,7 @@
       }
     }
 
-    _instanceMemberUsage.forEach(processMemberUse);
+    _memberUsage.forEach(processMemberUse);
     return functions;
   }
 
@@ -674,8 +656,7 @@
       }
     }
 
-    _instanceMemberUsage.forEach(processMemberUse);
-    _staticMemberUsage.forEach(processMemberUse);
+    _memberUsage.forEach(processMemberUse);
     return functions;
   }
 
diff --git a/pkg/compiler/lib/src/universe/member_usage.dart b/pkg/compiler/lib/src/universe/member_usage.dart
index e368cbb..f80d47c 100644
--- a/pkg/compiler/lib/src/universe/member_usage.dart
+++ b/pkg/compiler/lib/src/universe/member_usage.dart
@@ -7,7 +7,6 @@
 import '../common.dart';
 import '../constants/values.dart';
 import '../elements/entities.dart';
-import '../js_model/elements.dart' show JSignatureMethod;
 import '../util/enumset.dart';
 import 'call_structure.dart';
 
@@ -21,12 +20,12 @@
   }
 
   /// Returns the uses of [entity] that have been registered.
-  EnumSet<T> get appliedUse => _originalUse.minus(_pendingUse);
+  EnumSet<T> get _appliedUse => _originalUse.minus(_pendingUse);
 
   EnumSet<T> get _originalUse;
 
-  /// `true` if the [appliedUse] is non-empty.
-  bool get hasUse => appliedUse.isNotEmpty;
+  /// `true` if the [_appliedUse] is non-empty.
+  bool get hasUse => _appliedUse.isNotEmpty;
 
   /// Returns `true` if [other] has the same original and pending usage as this.
   bool hasSameUsage(AbstractUsage<T> other) {
@@ -45,13 +44,12 @@
   MemberUsage.cloned(this.entity, EnumSet<MemberUse> pendingUse)
       : super.cloned(pendingUse);
 
-  factory MemberUsage(MemberEntity member,
-      {bool isNative: false, bool trackParameters: false}) {
+  factory MemberUsage(MemberEntity member, {bool trackParameters: false}) {
     if (member.isField) {
       if (member.isAssignable) {
-        return new FieldUsage(member, isNative: isNative);
+        return new FieldUsage(member);
       } else {
-        return new FinalFieldUsage(member, isNative: isNative);
+        return new FinalFieldUsage(member);
       }
     } else if (member.isGetter) {
       return new GetterUsage(member);
@@ -63,8 +61,15 @@
       } else {
         return new ConstructorUsage(member);
       }
+    } else if (member.isConstructor) {
+      if (trackParameters) {
+        return new ParameterTrackingConstructorUsage(member);
+      } else {
+        return new ConstructorUsage(member);
+      }
     } else {
-      assert(member.isFunction, failedAt(member, "Unexpected member: $member"));
+      assert(member is FunctionEntity,
+          failedAt(member, "Unexpected member: $member"));
       if (trackParameters) {
         return new ParameterTrackingFunctionUsage(member);
       } else {
@@ -178,11 +183,11 @@
         fullyUsed == other.fullyUsed &&
         isFullyInvoked == other.isFullyInvoked &&
         _pendingUse == other._pendingUse &&
-        appliedUse == other.appliedUse;
+        _appliedUse == other._appliedUse;
   }
 
   @override
-  String toString() => '$entity:${appliedUse.iterable(MemberUse.values)}';
+  String toString() => '$entity:${_appliedUse.iterable(MemberUse.values)}';
 }
 
 class FieldUsage extends MemberUsage {
@@ -199,16 +204,11 @@
       {this.hasInit, this.hasRead, this.hasWrite})
       : super.cloned(field, pendingUse);
 
-  FieldUsage(FieldEntity field, {bool isNative: false})
+  FieldUsage(FieldEntity field)
       : hasInit = false,
         hasRead = false,
         hasWrite = false,
-        super.internal(field) {
-    // TODO(johnniwinther): Track native fields through member usage.
-    if (!isNative) {
-      init();
-    }
-  }
+        super.internal(field);
 
   @override
   Iterable<ConstantValue> get initialConstants => _initialConstants ?? const [];
@@ -301,14 +301,10 @@
       {this.hasInit, this.hasRead})
       : super.cloned(field, pendingUse);
 
-  FinalFieldUsage(FieldEntity field, {bool isNative: false})
+  FinalFieldUsage(FieldEntity field)
       : this.hasInit = false,
         this.hasRead = false,
-        super.internal(field) {
-    if (!isNative) {
-      init();
-    }
-  }
+        super.internal(field);
 
   @override
   Iterable<ConstantValue> get initialConstants => _initialConstants ?? const [];
@@ -389,14 +385,7 @@
   FunctionUsage(FunctionEntity function)
       : this.hasInvoke = false,
         this.hasRead = false,
-        super.internal(function) {
-    if (function is JSignatureMethod) {
-      // We mark signature methods as "always used" to prevent them from being
-      // optimized away.
-      // TODO(johnniwinther): Make this a part of the regular enqueueing.
-      invoke(function.parameterStructure.callStructure);
-    }
-  }
+        super.internal(function);
 
   @override
   FunctionEntity get entity => super.entity;
@@ -472,14 +461,7 @@
   ParameterTrackingFunctionUsage(FunctionEntity function)
       : hasRead = false,
         _parameterUsage = new ParameterUsage(function.parameterStructure),
-        super.internal(function) {
-    if (function is JSignatureMethod) {
-      // We mark signature methods as "always used" to prevent them from being
-      // optimized away.
-      // TODO(johnniwinther): Make this a part of the regular enqueueing.
-      invoke(CallStructure.NO_ARGS);
-    }
-  }
+        super.internal(function);
 
   @override
   bool get hasInvoke => _parameterUsage.hasInvoke;
@@ -804,7 +786,7 @@
   EnumSet<ClassUse> get _originalUse => ClassUses.ALL;
 
   @override
-  String toString() => '$cls:${appliedUse.iterable(ClassUse.values)}';
+  String toString() => '$cls:${_appliedUse.iterable(ClassUse.values)}';
 }
 
 /// Enum class for the possible kind of use of [ClassEntity] objects.
@@ -822,179 +804,6 @@
 
 typedef void ClassUsedCallback(ClassEntity cls, EnumSet<ClassUse> useSet);
 
-// TODO(johnniwinther): Merge this with [MemberUsage].
-abstract class StaticMemberUsage extends AbstractUsage<MemberUse>
-    implements MemberUsage {
-  @override
-  final MemberEntity entity;
-
-  bool hasNormalUse;
-  bool get hasClosurization => false;
-
-  StaticMemberUsage.cloned(this.entity, EnumSet<MemberUse> pendingUse,
-      {this.hasNormalUse: false})
-      : super.cloned(pendingUse);
-
-  StaticMemberUsage.internal(this.entity)
-      : this.hasNormalUse = false,
-        super();
-
-  EnumSet<MemberUse> normalUse() {
-    if (hasNormalUse) {
-      return MemberUses.NONE;
-    }
-    hasNormalUse = true;
-    return _pendingUse.removeAll(MemberUses.NORMAL_ONLY);
-  }
-
-  EnumSet<MemberUse> tearOff();
-
-  @override
-  EnumSet<MemberUse> init() => normalUse();
-
-  @override
-  EnumSet<MemberUse> constantInit(ConstantValue constant) => normalUse();
-
-  @override
-  EnumSet<MemberUse> read() => tearOff();
-
-  @override
-  EnumSet<MemberUse> write() => normalUse();
-
-  @override
-  EnumSet<MemberUse> invoke(CallStructure callStructure) => normalUse();
-
-  @override
-  EnumSet<MemberUse> fullyUse() => normalUse();
-
-  @override
-  Iterable<ConstantValue> get initialConstants => null;
-
-  @override
-  bool get hasPendingNormalUse => _pendingUse.contains(MemberUse.NORMAL);
-
-  @override
-  bool get isFullyInvoked => hasInvoke;
-
-  @override
-  EnumSet<MemberUse> get _originalUse => MemberUses.NORMAL_ONLY;
-
-  @override
-  bool dataEquals(MemberUsage other) {
-    assert(entity == other.entity);
-    return hasInit == other.hasInit &&
-        hasRead == other.hasRead &&
-        hasInvoke == other.hasInvoke &&
-        hasWrite == other.hasWrite &&
-        hasPendingClosurizationUse == other.hasPendingClosurizationUse &&
-        hasPendingNormalUse == other.hasPendingNormalUse &&
-        fullyUsed == other.fullyUsed &&
-        isFullyInvoked == other.isFullyInvoked &&
-        _pendingUse == other._pendingUse &&
-        appliedUse == other.appliedUse;
-  }
-
-  @override
-  String toString() => '$entity:${appliedUse.iterable(MemberUse.values)}';
-}
-
-class GeneralStaticMemberUsage extends StaticMemberUsage {
-  GeneralStaticMemberUsage(MemberEntity entity) : super.internal(entity);
-
-  GeneralStaticMemberUsage.cloned(
-      MemberEntity entity, EnumSet<MemberUse> pendingUse,
-      {bool hasNormalUse})
-      : super.cloned(entity, pendingUse, hasNormalUse: hasNormalUse);
-
-  @override
-  EnumSet<MemberUse> tearOff() => normalUse();
-
-  @override
-  bool get hasInit => true;
-
-  @override
-  bool get fullyUsed => hasNormalUse;
-
-  @override
-  bool get hasInvoke => hasNormalUse;
-
-  @override
-  bool get hasWrite => hasNormalUse;
-
-  @override
-  bool get hasRead => hasNormalUse;
-
-  @override
-  bool get hasPendingClosurizationUse => false;
-
-  @override
-  ParameterStructure get invokedParameters => null;
-
-  @override
-  MemberUsage clone() {
-    return new GeneralStaticMemberUsage.cloned(entity, _pendingUse.clone(),
-        hasNormalUse: hasNormalUse);
-  }
-}
-
-class StaticFunctionUsage extends StaticMemberUsage {
-  @override
-  bool hasClosurization;
-
-  StaticFunctionUsage(FunctionEntity entity)
-      : hasClosurization = false,
-        super.internal(entity);
-
-  StaticFunctionUsage.cloned(
-      FunctionEntity entity, EnumSet<MemberUse> pendingUse,
-      {bool hasNormalUse, this.hasClosurization})
-      : super.cloned(entity, pendingUse, hasNormalUse: hasNormalUse);
-
-  @override
-  FunctionEntity get entity => super.entity;
-
-  @override
-  bool get hasInit => true;
-
-  @override
-  EnumSet<MemberUse> tearOff() {
-    if (hasClosurization) {
-      return MemberUses.NONE;
-    }
-    hasNormalUse = hasClosurization = true;
-    return _pendingUse.removeAll(MemberUses.ALL_STATIC);
-  }
-
-  @override
-  EnumSet<MemberUse> get _originalUse => MemberUses.ALL_STATIC;
-
-  @override
-  bool get hasPendingClosurizationUse =>
-      _pendingUse.contains(MemberUse.CLOSURIZE_STATIC);
-
-  @override
-  bool get fullyUsed => hasNormalUse && hasClosurization;
-
-  @override
-  bool get hasInvoke => hasNormalUse;
-
-  @override
-  bool get hasWrite => hasNormalUse;
-
-  @override
-  bool get hasRead => hasClosurization;
-
-  @override
-  ParameterStructure get invokedParameters =>
-      hasInvoke ? entity.parameterStructure : null;
-
-  @override
-  MemberUsage clone() {
-    return new StaticFunctionUsage.cloned(entity, _pendingUse.clone(),
-        hasNormalUse: hasNormalUse, hasClosurization: hasClosurization);
-  }
-}
-
 /// Object used for tracking parameter use in constructor and method
 /// invocations.
 class ParameterUsage {
diff --git a/pkg/compiler/lib/src/universe/resolution_world_builder.dart b/pkg/compiler/lib/src/universe/resolution_world_builder.dart
index 7415ea2..09d2c6a 100644
--- a/pkg/compiler/lib/src/universe/resolution_world_builder.dart
+++ b/pkg/compiler/lib/src/universe/resolution_world_builder.dart
@@ -104,7 +104,7 @@
   // TODO(johnniwinther): Support unknown type arguments for generic types.
   void registerTypeInstantiation(
       InterfaceType type, ClassUsedCallback classUsed,
-      {ConstructorEntity constructor, bool isRedirection: false});
+      {ConstructorEntity constructor});
 
   /// Computes usage for all members declared by [cls]. Calls [membersUsed] with
   /// the usage changes for each member.
@@ -139,23 +139,19 @@
 class Instance {
   final InterfaceType type;
   final Instantiation kind;
-  final bool isRedirection;
 
-  Instance(this.type, this.kind, {this.isRedirection: false});
+  Instance(this.type, this.kind);
 
   @override
   int get hashCode {
-    return Hashing.objectHash(
-        type, Hashing.objectHash(kind, Hashing.objectHash(isRedirection)));
+    return Hashing.objectHash(type, Hashing.objectHash(kind));
   }
 
   @override
   bool operator ==(other) {
     if (identical(this, other)) return true;
     if (other is! Instance) return false;
-    return type == other.type &&
-        kind == other.kind &&
-        isRedirection == other.isRedirection;
+    return type == other.type && kind == other.kind;
   }
 
   @override
@@ -169,9 +165,6 @@
     } else if (kind == Instantiation.UNINSTANTIATED) {
       sb.write(' none');
     }
-    if (isRedirection) {
-      sb.write(' redirect');
-    }
     return sb.toString();
   }
 }
@@ -242,12 +235,11 @@
 
   /// Register [type] as the instantiation [kind] using [constructor].
   void addInstantiation(
-      ConstructorEntity constructor, InterfaceType type, Instantiation kind,
-      {bool isRedirection: false}) {
+      ConstructorEntity constructor, InterfaceType type, Instantiation kind) {
     instantiationMap ??= <ConstructorEntity, Set<Instance>>{};
     instantiationMap
         .putIfAbsent(constructor, () => new Set<Instance>())
-        .add(new Instance(type, kind, isRedirection: isRedirection));
+        .add(new Instance(type, kind));
     switch (kind) {
       case Instantiation.DIRECTLY_INSTANTIATED:
         isDirectlyInstantiated = true;
@@ -338,26 +330,6 @@
 
   Map<MemberEntity, MemberUsage> get memberUsageForTesting => _memberUsage;
 
-  Map<MemberEntity, MemberUsage> get staticMemberUsageForTesting {
-    Map<MemberEntity, MemberUsage> map = <MemberEntity, MemberUsage>{};
-    _memberUsage.forEach((MemberEntity member, MemberUsage usage) {
-      if (!member.isInstanceMember) {
-        map[member] = usage;
-      }
-    });
-    return map;
-  }
-
-  Map<MemberEntity, MemberUsage> get instanceMemberUsageForTesting {
-    Map<MemberEntity, MemberUsage> map = <MemberEntity, MemberUsage>{};
-    _memberUsage.forEach((MemberEntity member, MemberUsage usage) {
-      if (member.isInstanceMember) {
-        map[member] = usage;
-      }
-    });
-    return map;
-  }
-
   /// Map containing instance members of live classes that are not yet fully
   /// live themselves.
   final Map<String, Set<MemberUsage>> _instanceMembersByName =
@@ -546,7 +518,7 @@
   @override
   void registerTypeInstantiation(
       InterfaceType type, ClassUsedCallback classUsed,
-      {ConstructorEntity constructor, bool isRedirection: false}) {
+      {ConstructorEntity constructor}) {
     ClassEntity cls = type.element;
     InstantiationInfo info =
         _instantiationInfo.putIfAbsent(cls, () => new InstantiationInfo());
@@ -563,8 +535,7 @@
         kind = Instantiation.DIRECTLY_INSTANTIATED;
       }
     }
-    info.addInstantiation(constructor, type, kind,
-        isRedirection: isRedirection);
+    info.addInstantiation(constructor, type, kind);
     if (kind != Instantiation.UNINSTANTIATED) {
       _classHierarchyBuilder.updateClassHierarchyNodeForClass(cls,
           directlyInstantiated: info.isDirectlyInstantiated,
@@ -788,7 +759,6 @@
         break;
       case StaticUseKind.CONSTRUCTOR_INVOKE:
       case StaticUseKind.CONST_CONSTRUCTOR_INVOKE:
-      case StaticUseKind.REDIRECTION:
         useSet.addAll(usage.invoke(staticUse.callStructure));
         break;
       case StaticUseKind.DIRECT_INVOKE:
@@ -882,9 +852,10 @@
         // Note: this assumes that there are no non-native fields on native
         // classes, which may not be the case when a native class is subclassed.
         bool isNative = _nativeBasicData.isNativeClass(cls);
-        usage =
-            new MemberUsage(member, isNative: isNative, trackParameters: true);
-        useSet.addAll(usage.appliedUse);
+        usage = new MemberUsage(member, trackParameters: true);
+        if (member.isField && !isNative) {
+          useSet.addAll(usage.init());
+        }
         if (!dryRun) {
           if (member.isField && isNative) {
             registerUsedElement(member);
@@ -931,11 +902,13 @@
         }
       } else {
         usage = new MemberUsage(member, trackParameters: true);
-        useSet.addAll(usage.appliedUse);
+        if (member.isField) {
+          useSet.addAll(usage.init());
+        }
       }
-    }
-    if (!dryRun) {
-      _memberUsage[member] = usage;
+      if (!dryRun) {
+        _memberUsage[member] = usage;
+      }
     }
     return usage;
   }
diff --git a/pkg/compiler/lib/src/universe/use.dart b/pkg/compiler/lib/src/universe/use.dart
index 01b0d07..da78e72 100644
--- a/pkg/compiler/lib/src/universe/use.dart
+++ b/pkg/compiler/lib/src/universe/use.dart
@@ -154,7 +154,6 @@
   CALL_METHOD,
   CONSTRUCTOR_INVOKE,
   CONST_CONSTRUCTOR_INVOKE,
-  REDIRECTION,
   DIRECT_INVOKE,
   INLINING,
   INVOKE,
@@ -262,6 +261,8 @@
             element,
             "Static invoke element $element must be a top-level "
             "or static method."));
+    assert(element.isFunction,
+        failedAt(element, "Static get element $element must be a function."));
     assert(
         callStructure != null,
         failedAt(element,
@@ -280,6 +281,8 @@
             element,
             "Static tear-off element $element must be a top-level "
             "or static method."));
+    assert(element.isFunction,
+        failedAt(element, "Static get element $element must be a function."));
     return new StaticUse.internal(element, StaticUseKind.STATIC_TEAR_OFF,
         deferredImport: deferredImport);
   }
@@ -292,7 +295,7 @@
         failedAt(
             element,
             "Static get element $element must be a top-level "
-            "or static method."));
+            "or static field or getter."));
     assert(
         element.isField || element.isGetter,
         failedAt(element,
@@ -311,7 +314,7 @@
             "Static set element $element "
             "must be a top-level or static method."));
     assert(
-        element.isField || element.isSetter,
+        (element.isField && element.isAssignable) || element.isSetter,
         failedAt(element,
             "Static set element $element must be a field or a setter."));
     return new StaticUse.internal(element, StaticUseKind.SET,
@@ -530,19 +533,6 @@
         deferredImport: deferredImport);
   }
 
-  /// Constructor redirection to [element] on [type].
-  factory StaticUse.constructorRedirect(
-      ConstructorEntity element, InterfaceType type) {
-    assert(type != null,
-        failedAt(element, "No type provided for constructor redirection."));
-    assert(
-        element.isConstructor,
-        failedAt(element,
-            "Constructor redirection element $element must be a constructor."));
-    return new StaticUse.internal(element, StaticUseKind.REDIRECTION,
-        type: type);
-  }
-
   /// Initialization of an instance field [element].
   factory StaticUse.fieldInit(FieldEntity element) {
     assert(
@@ -654,7 +644,7 @@
             element,
             "Type argument count mismatch. Call structure has "
             "${callStructure?.typeArgumentCount ?? 0} but "
-            "${typeArguments?.length ?? 0} were passed."));
+            "${typeArguments?.length ?? 0} were passed in $this."));
   }
 
   GenericStaticUse.methodInlining(FunctionEntity entity, this.typeArguments)
diff --git a/tests/compiler/dart2js_extra/field_access_test.dart b/tests/compiler/dart2js_extra/field_access_test.dart
new file mode 100644
index 0000000..044baf4
--- /dev/null
+++ b/tests/compiler/dart2js_extra/field_access_test.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.
+
+// Test the all variants of field/property access/update are emitted.
+//
+// This is needed because getter/setters are now registered as read from and
+// written to, respectively, instead of being invoked.
+
+var field1a;
+
+var field1b;
+
+var field1c;
+
+@pragma('dart2js:noInline')
+get field2a => 42;
+
+@pragma('dart2js:noInline')
+set field2a(_) {}
+
+@pragma('dart2js:noInline')
+get field2b => 42;
+
+@pragma('dart2js:noInline')
+set field2b(_) {}
+
+@pragma('dart2js:noInline')
+get field2c => 42;
+
+@pragma('dart2js:noInline')
+set field2c(_) {}
+
+class Class {
+  @pragma('dart2js:noElision')
+  var field1a;
+
+  var field1b;
+
+  var field1c;
+
+  @pragma('dart2js:noInline')
+  get field2a => 42;
+
+  @pragma('dart2js:noInline')
+  set field2a(_) {}
+
+  @pragma('dart2js:noInline')
+  get field2b => 42;
+
+  @pragma('dart2js:noInline')
+  set field2b(_) {}
+
+  @pragma('dart2js:noInline')
+  get field2c => 42;
+
+  set field2c(_) {}
+
+  var field3a = 0;
+
+  var field3b;
+
+  @pragma('dart2js:noInline')
+  Class([this.field3b]);
+
+  @pragma('dart2js:noInline')
+  test() {
+    field1a;
+    field1b = 42;
+    field1c = field1c;
+
+    field2a;
+    field2b = 42;
+    field2c = field2c;
+  }
+}
+
+main() {
+  field1a;
+  field1b = 42;
+  field1c = field1c;
+
+  field2a;
+  field2b = 42;
+  field2c = field2c;
+
+  new Class().test();
+}