Refactor Namer and ConstantEmitter to prepare for modular compilation

Change-Id: I2fa8928d7c7d845df2507c9ad034107ec4bc21de
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/102364
Reviewed-by: Sigmund Cherem <sigmund@google.com>
diff --git a/pkg/compiler/lib/src/common/codegen.dart b/pkg/compiler/lib/src/common/codegen.dart
index e3f201f..19291da 100644
--- a/pkg/compiler/lib/src/common/codegen.dart
+++ b/pkg/compiler/lib/src/common/codegen.dart
@@ -4,17 +4,20 @@
 
 library dart2js.common.codegen;
 
+import 'package:js_ast/src/precedence.dart' as js show PRIMARY;
+
 import '../common_elements.dart';
+import '../constants/values.dart';
 import '../elements/entities.dart';
 import '../elements/types.dart' show DartType, InterfaceType;
-import '../native/behavior.dart';
 import '../js/js.dart' as js;
+import '../native/behavior.dart';
 import '../universe/feature.dart';
 import '../universe/use.dart' show ConstantUse, DynamicUse, StaticUse, TypeUse;
 import '../universe/world_impact.dart'
     show WorldImpact, WorldImpactBuilderImpl, WorldImpactVisitor;
 import '../util/enumset.dart';
-import '../util/util.dart' show Pair, Setlet;
+import '../util/util.dart';
 
 class CodegenImpact extends WorldImpact {
   const CodegenImpact();
@@ -150,16 +153,16 @@
 // TODO(johnniwinther): Move this implementation to the JS backend.
 class CodegenRegistry {
   final ElementEnvironment _elementEnvironment;
-  final MemberEntity currentElement;
-  final _CodegenImpact worldImpact;
+  final MemberEntity _currentElement;
+  final _CodegenImpact _worldImpact;
+  List<ModularName> _names;
+  List<ModularExpression> _expressions;
 
-  CodegenRegistry(this._elementEnvironment, this.currentElement)
-      : this.worldImpact = new _CodegenImpact();
-
-  bool get isForResolution => false;
+  CodegenRegistry(this._elementEnvironment, this._currentElement)
+      : this._worldImpact = new _CodegenImpact();
 
   @override
-  String toString() => 'CodegenRegistry for $currentElement';
+  String toString() => 'CodegenRegistry for $_currentElement';
 
   @deprecated
   void registerInstantiatedClass(ClassEntity element) {
@@ -167,40 +170,40 @@
   }
 
   void registerStaticUse(StaticUse staticUse) {
-    worldImpact.registerStaticUse(staticUse);
+    _worldImpact.registerStaticUse(staticUse);
   }
 
   void registerDynamicUse(DynamicUse dynamicUse) {
-    worldImpact.registerDynamicUse(dynamicUse);
+    _worldImpact.registerDynamicUse(dynamicUse);
   }
 
   void registerTypeUse(TypeUse typeUse) {
-    worldImpact.registerTypeUse(typeUse);
+    _worldImpact.registerTypeUse(typeUse);
   }
 
   void registerConstantUse(ConstantUse constantUse) {
-    worldImpact.registerConstantUse(constantUse);
+    _worldImpact.registerConstantUse(constantUse);
   }
 
   void registerTypeVariableBoundsSubtypeCheck(
       DartType subtype, DartType supertype) {
-    worldImpact.registerTypeVariableBoundsSubtypeCheck(subtype, supertype);
+    _worldImpact.registerTypeVariableBoundsSubtypeCheck(subtype, supertype);
   }
 
   void registerInstantiatedClosure(FunctionEntity element) {
-    worldImpact.registerStaticUse(new StaticUse.callMethod(element));
+    _worldImpact.registerStaticUse(new StaticUse.callMethod(element));
   }
 
   void registerConstSymbol(String name) {
-    worldImpact.registerConstSymbol(name);
+    _worldImpact.registerConstSymbol(name);
   }
 
   void registerSpecializedGetInterceptor(Set<ClassEntity> classes) {
-    worldImpact.registerSpecializedGetInterceptor(classes);
+    _worldImpact.registerSpecializedGetInterceptor(classes);
   }
 
   void registerUseInterceptor() {
-    worldImpact.registerUseInterceptor();
+    _worldImpact.registerUseInterceptor();
   }
 
   void registerInstantiation(InterfaceType type) {
@@ -208,25 +211,207 @@
   }
 
   void registerAsyncMarker(AsyncMarker asyncMarker) {
-    worldImpact.registerAsyncMarker(asyncMarker);
+    _worldImpact.registerAsyncMarker(asyncMarker);
   }
 
   void registerGenericInstantiation(GenericInstantiation instantiation) {
-    worldImpact.registerGenericInstantiation(instantiation);
+    _worldImpact.registerGenericInstantiation(instantiation);
   }
 
   void registerNativeBehavior(NativeBehavior nativeBehavior) {
-    worldImpact.registerNativeBehavior(nativeBehavior);
+    _worldImpact.registerNativeBehavior(nativeBehavior);
   }
 
   void registerNativeMethod(FunctionEntity function) {
-    worldImpact.registerNativeMethod(function);
+    _worldImpact.registerNativeMethod(function);
+  }
+
+  void registerModularName(ModularName name) {
+    _names ??= [];
+    _names.add(name);
+  }
+
+  void registerModularExpression(ModularExpression expression) {
+    _expressions ??= [];
+    _expressions.add(expression);
+  }
+
+  CodegenResult close(js.Fun code) {
+    return new CodegenResult(
+        code, _worldImpact, _names ?? const [], _expressions ?? const []);
   }
 }
 
 class CodegenResult {
-  js.Fun code;
-  CodegenImpact impact;
+  final js.Fun code;
+  final CodegenImpact impact;
+  final Iterable<ModularName> modularNames;
+  final Iterable<ModularExpression> modularExpressions;
 
-  CodegenResult(this.code, this.impact);
+  CodegenResult(
+      this.code, this.impact, this.modularNames, this.modularExpressions);
+
+  @override
+  String toString() {
+    StringBuffer sb = new StringBuffer();
+    sb.write('CodegenResult(code=');
+    sb.write(code != null ? js.DebugPrint(code) : '<null>,');
+    sb.write('impact=$impact,');
+    sb.write('modularNames=$modularNames,');
+    sb.write('modularExpressions=$modularExpressions');
+    sb.write(')');
+    return sb.toString();
+  }
+}
+
+enum ModularNameKind {
+  rtiField,
+  runtimeTypeName,
+  className,
+  aliasedSuperMember,
+  staticClosure,
+  methodProperty,
+  operatorIs,
+  operatorIsType,
+  substitution,
+  instanceMethod,
+  instanceField,
+  invocation,
+  lazyInitializer,
+  globalPropertyNameForClass,
+  globalPropertyNameForType,
+  globalPropertyNameForMember,
+  nameForGetInterceptor,
+  nameForGetOneShotInterceptor,
+  asName,
+}
+
+class ModularName extends js.Name implements js.AstContainer {
+  final ModularNameKind kind;
+  js.Name _value;
+  final Object data;
+  final Set<ClassEntity> set;
+
+  ModularName(this.kind, {this.data, this.set});
+
+  js.Name get value {
+    assert(_value != null);
+    return _value;
+  }
+
+  void set value(js.Name node) {
+    assert(_value == null);
+    assert(node != null);
+    _value = node.withSourceInformation(sourceInformation);
+  }
+
+  @override
+  String get key {
+    assert(_value != null);
+    return _value.key;
+  }
+
+  @override
+  String get name {
+    assert(_value != null);
+    return _value.name;
+  }
+
+  @override
+  bool get allowRename {
+    assert(_value != null);
+    return _value.allowRename;
+  }
+
+  @override
+  int compareTo(js.Name other) {
+    assert(_value != null);
+    return _value.compareTo(other);
+  }
+
+  @override
+  Iterable<js.Node> get containedNodes {
+    return _value != null ? [_value] : const [];
+  }
+
+  @override
+  int get hashCode {
+    return Hashing.setHash(set, Hashing.objectsHash(kind, data));
+  }
+
+  @override
+  bool operator ==(Object other) {
+    if (identical(this, other)) return true;
+    return other is ModularName &&
+        kind == other.kind &&
+        data == other.data &&
+        equalSets(set, other.set);
+  }
+
+  @override
+  String toString() => 'ModularName(kind=$kind,data=$data,value=$value)';
+}
+
+enum ModularExpressionKind {
+  globalObjectForLibrary,
+  globalObjectForClass,
+  globalObjectForType,
+  globalObjectForMember,
+  constant,
+  embeddedGlobalAccess,
+}
+
+class ModularExpression extends js.DeferredExpression
+    implements js.AstContainer {
+  final ModularExpressionKind kind;
+  final Object data;
+  js.Expression _value;
+
+  ModularExpression(this.kind, this.data);
+
+  @override
+  js.Expression get value {
+    assert(_value != null);
+    return _value;
+  }
+
+  void set value(js.Expression node) {
+    assert(_value == null);
+    assert(node != null);
+    _value = node.withSourceInformation(sourceInformation);
+  }
+
+  @override
+  int get precedenceLevel => _value?.precedenceLevel ?? js.PRIMARY;
+
+  @override
+  Iterable<js.Node> get containedNodes {
+    return _value != null ? [_value] : const [];
+  }
+
+  @override
+  int get hashCode {
+    return Hashing.objectsHash(kind, data);
+  }
+
+  @override
+  bool operator ==(Object other) {
+    if (identical(this, other)) return true;
+    return other is ModularExpression &&
+        kind == other.kind &&
+        data == other.data;
+  }
+
+  @override
+  String toString() {
+    StringBuffer sb = new StringBuffer();
+    sb.write('ModularExpression(kind=$kind,data=');
+    if (data is ConstantValue) {
+      sb.write((data as ConstantValue).toStructuredText());
+    } else {
+      sb.write(data);
+    }
+    sb.write(',value=$_value)');
+    return sb.toString();
+  }
 }
diff --git a/pkg/compiler/lib/src/js/js.dart b/pkg/compiler/lib/src/js/js.dart
index c62d6f3..c2f8403 100644
--- a/pkg/compiler/lib/src/js/js.dart
+++ b/pkg/compiler/lib/src/js/js.dart
@@ -119,7 +119,7 @@
 }
 
 abstract class ReferenceCountedAstNode implements Node {
-  markSeen(TokenCounter visitor);
+  void markSeen(TokenCounter visitor);
 }
 
 /// Represents the LiteralString resulting from unparsing [expression]. The
diff --git a/pkg/compiler/lib/src/js_backend/backend.dart b/pkg/compiler/lib/src/js_backend/backend.dart
index c05b885..213a74d 100644
--- a/pkg/compiler/lib/src/js_backend/backend.dart
+++ b/pkg/compiler/lib/src/js_backend/backend.dart
@@ -428,11 +428,14 @@
   }
 
   Namer determineNamer(JClosedWorld closedWorld, RuntimeTypeTags rtiTags) {
+    FixedNames fixedNames = compiler.options.enableMinification
+        ? const MinifiedFixedNames()
+        : const FixedNames();
     return compiler.options.enableMinification
         ? compiler.options.useFrequencyNamer
-            ? new FrequencyBasedNamer(closedWorld, rtiTags)
-            : new MinifyNamer(closedWorld, rtiTags)
-        : new Namer(closedWorld, rtiTags);
+            ? new FrequencyBasedNamer(closedWorld, rtiTags, fixedNames)
+            : new MinifyNamer(closedWorld, rtiTags, fixedNames)
+        : new Namer(closedWorld, rtiTags, fixedNames);
   }
 
   void validateInterceptorImplementsAllObjectMethods(
diff --git a/pkg/compiler/lib/src/js_backend/checked_mode_helpers.dart b/pkg/compiler/lib/src/js_backend/checked_mode_helpers.dart
index 751295e..c577600 100644
--- a/pkg/compiler/lib/src/js_backend/checked_mode_helpers.dart
+++ b/pkg/compiler/lib/src/js_backend/checked_mode_helpers.dart
@@ -12,7 +12,7 @@
 import '../ssa/nodes.dart' show HTypeConversion;
 import '../universe/call_structure.dart' show CallStructure;
 import '../universe/use.dart' show StaticUse;
-import 'namer.dart' show Namer;
+import 'namer.dart' show ModularNamer;
 
 class CheckedModeHelper {
   final String name;
@@ -28,7 +28,7 @@
 
   CallStructure get callStructure => CallStructure.ONE_ARG;
 
-  void generateAdditionalArguments(SsaCodeGenerator codegen, Namer namer,
+  void generateAdditionalArguments(SsaCodeGenerator codegen, ModularNamer namer,
       HTypeConversion node, List<jsAst.Expression> arguments) {
     // No additional arguments needed.
   }
@@ -41,7 +41,7 @@
   CallStructure get callStructure => CallStructure.TWO_ARGS;
 
   @override
-  void generateAdditionalArguments(SsaCodeGenerator codegen, Namer namer,
+  void generateAdditionalArguments(SsaCodeGenerator codegen, ModularNamer namer,
       HTypeConversion node, List<jsAst.Expression> arguments) {
     DartType type = node.typeExpression;
     jsAst.Name additionalArgument = namer.operatorIsType(type);
@@ -56,7 +56,7 @@
   CallStructure get callStructure => CallStructure.TWO_ARGS;
 
   @override
-  void generateAdditionalArguments(SsaCodeGenerator codegen, Namer namer,
+  void generateAdditionalArguments(SsaCodeGenerator codegen, ModularNamer namer,
       HTypeConversion node, List<jsAst.Expression> arguments) {
     assert(node.typeExpression.isTypeVariable);
     codegen.use(node.typeRepresentation);
@@ -71,7 +71,7 @@
   CallStructure get callStructure => CallStructure.TWO_ARGS;
 
   @override
-  void generateAdditionalArguments(SsaCodeGenerator codegen, Namer namer,
+  void generateAdditionalArguments(SsaCodeGenerator codegen, ModularNamer namer,
       HTypeConversion node, List<jsAst.Expression> arguments) {
     assert(node.typeExpression.isFunctionType);
     codegen.use(node.typeRepresentation);
@@ -86,7 +86,7 @@
   CallStructure get callStructure => CallStructure.TWO_ARGS;
 
   @override
-  void generateAdditionalArguments(SsaCodeGenerator codegen, Namer namer,
+  void generateAdditionalArguments(SsaCodeGenerator codegen, ModularNamer namer,
       HTypeConversion node, List<jsAst.Expression> arguments) {
     assert(node.typeExpression.isFutureOr);
     codegen.use(node.typeRepresentation);
@@ -101,7 +101,7 @@
   CallStructure get callStructure => const CallStructure.unnamed(4);
 
   @override
-  void generateAdditionalArguments(SsaCodeGenerator codegen, Namer namer,
+  void generateAdditionalArguments(SsaCodeGenerator codegen, ModularNamer namer,
       HTypeConversion node, List<jsAst.Expression> arguments) {
     // TODO(sra): Move these calls into the SSA graph so that the arguments can
     // be optimized, e,g, GVNed.
diff --git a/pkg/compiler/lib/src/js_backend/constant_emitter.dart b/pkg/compiler/lib/src/js_backend/constant_emitter.dart
index b596d65..780b1bc 100644
--- a/pkg/compiler/lib/src/js_backend/constant_emitter.dart
+++ b/pkg/compiler/lib/src/js_backend/constant_emitter.dart
@@ -22,40 +22,13 @@
 
 typedef jsAst.Expression _ConstantListGenerator(jsAst.Expression array);
 
-/// Generates the JavaScript expressions for constants.
-///
-/// It uses a given [_constantReferenceGenerator] to reference nested constants
-/// (if there are some). It is hence up to that function to decide which
-/// constants should be inlined or not.
-class ConstantEmitter implements ConstantValueVisitor<jsAst.Expression, Null> {
-  // Matches blank lines, comment lines and trailing comments that can't be part
-  // of a string.
-  static final RegExp COMMENT_RE =
-      new RegExp(r'''^ *(//.*)?\n|  *//[^''"\n]*$''', multiLine: true);
-
+/// Visitor that creates [jsAst.Expression]s for constants that are inlined
+/// and therefore can be created during modular code generation.
+class ModularConstantEmitter
+    implements ConstantValueVisitor<jsAst.Expression, Null> {
   final CompilerOptions _options;
-  final JCommonElements _commonElements;
-  final JElementEnvironment _elementEnvironment;
-  final RuntimeTypesNeed _rtiNeed;
-  final RuntimeTypesEncoder _rtiEncoder;
-  final JFieldAnalysis _fieldAnalysis;
-  final Emitter _emitter;
-  final _ConstantReferenceGenerator _constantReferenceGenerator;
-  final _ConstantListGenerator _makeConstantList;
 
-  /// The given [_constantReferenceGenerator] function must, when invoked with a
-  /// constant, either return a reference or return its literal expression if it
-  /// can be inlined.
-  ConstantEmitter(
-      this._options,
-      this._commonElements,
-      this._elementEnvironment,
-      this._rtiNeed,
-      this._rtiEncoder,
-      this._fieldAnalysis,
-      this._emitter,
-      this._constantReferenceGenerator,
-      this._makeConstantList);
+  ModularConstantEmitter(this._options);
 
   /// Constructs a literal expression that evaluates to the constant. Uses a
   /// canonical name unless the constant can be emitted multiple times (as for
@@ -172,6 +145,87 @@
   }
 
   @override
+  jsAst.Expression visitSynthetic(SyntheticConstantValue constant, [_]) {
+    switch (constant.valueKind) {
+      case SyntheticConstantKind.DUMMY_INTERCEPTOR:
+      case SyntheticConstantKind.EMPTY_VALUE:
+        return new jsAst.LiteralNumber('0');
+      case SyntheticConstantKind.TYPEVARIABLE_REFERENCE:
+      case SyntheticConstantKind.NAME:
+        return constant.payload;
+      default:
+        throw failedAt(NO_LOCATION_SPANNABLE,
+            "Unexpected DummyConstantKind ${constant.kind}");
+    }
+  }
+
+  @override
+  jsAst.Expression visitInstantiation(InstantiationConstantValue constant,
+          [_]) =>
+      null;
+
+  @override
+  jsAst.Expression visitDeferredGlobal(DeferredGlobalConstantValue constant,
+          [_]) =>
+      null;
+
+  @override
+  jsAst.Expression visitInterceptor(InterceptorConstantValue constant, [_]) =>
+      null;
+
+  @override
+  jsAst.Expression visitType(TypeConstantValue constant, [_]) => null;
+
+  @override
+  jsAst.Expression visitConstructed(ConstructedConstantValue constant, [_]) =>
+      null;
+
+  @override
+  jsAst.Expression visitMap(MapConstantValue constant, [_]) => null;
+
+  @override
+  jsAst.Expression visitSet(SetConstantValue constant, [_]) => null;
+
+  @override
+  jsAst.Expression visitList(ListConstantValue constant, [_]) => null;
+}
+
+/// Generates the JavaScript expressions for constants.
+///
+/// It uses a given [_constantReferenceGenerator] to reference nested constants
+/// (if there are some). It is hence up to that function to decide which
+/// constants should be inlined or not.
+class ConstantEmitter extends ModularConstantEmitter {
+  // Matches blank lines, comment lines and trailing comments that can't be part
+  // of a string.
+  static final RegExp COMMENT_RE =
+      new RegExp(r'''^ *(//.*)?\n|  *//[^''"\n]*$''', multiLine: true);
+
+  final JCommonElements _commonElements;
+  final JElementEnvironment _elementEnvironment;
+  final RuntimeTypesNeed _rtiNeed;
+  final RuntimeTypesEncoder _rtiEncoder;
+  final JFieldAnalysis _fieldAnalysis;
+  final Emitter _emitter;
+  final _ConstantReferenceGenerator _constantReferenceGenerator;
+  final _ConstantListGenerator _makeConstantList;
+
+  /// The given [_constantReferenceGenerator] function must, when invoked with a
+  /// constant, either return a reference or return its literal expression if it
+  /// can be inlined.
+  ConstantEmitter(
+      CompilerOptions options,
+      this._commonElements,
+      this._elementEnvironment,
+      this._rtiNeed,
+      this._rtiEncoder,
+      this._fieldAnalysis,
+      this._emitter,
+      this._constantReferenceGenerator,
+      this._makeConstantList)
+      : super(options);
+
+  @override
   jsAst.Expression visitList(ListConstantValue constant, [_]) {
     List<jsAst.Expression> elements = constant.entries
         .map(_constantReferenceGenerator)
@@ -322,21 +376,6 @@
   }
 
   @override
-  jsAst.Expression visitSynthetic(SyntheticConstantValue constant, [_]) {
-    switch (constant.valueKind) {
-      case SyntheticConstantKind.DUMMY_INTERCEPTOR:
-      case SyntheticConstantKind.EMPTY_VALUE:
-        return new jsAst.LiteralNumber('0');
-      case SyntheticConstantKind.TYPEVARIABLE_REFERENCE:
-      case SyntheticConstantKind.NAME:
-        return constant.payload;
-      default:
-        throw failedAt(NO_LOCATION_SPANNABLE,
-            "Unexpected DummyConstantKind ${constant.kind}");
-    }
-  }
-
-  @override
   jsAst.Expression visitConstructed(ConstructedConstantValue constant, [_]) {
     ClassEntity element = constant.type.element;
     if (element == _commonElements.jsConstClass) {
diff --git a/pkg/compiler/lib/src/js_backend/frequency_namer.dart b/pkg/compiler/lib/src/js_backend/frequency_namer.dart
index de45286..1396e15 100644
--- a/pkg/compiler/lib/src/js_backend/frequency_namer.dart
+++ b/pkg/compiler/lib/src/js_backend/frequency_namer.dart
@@ -23,32 +23,15 @@
   bool get shouldMinify => true;
 
   @override
-  final String getterPrefix = 'g';
-  @override
-  final String setterPrefix = 's';
-  @override
-  final String callPrefix = ''; // this will create function names $<n>
-  @override
-  String get operatorIsPrefix => r'$i';
-  @override
-  String get operatorAsPrefix => r'$a';
-  @override
-  String get callCatchAllName => r'$C';
-  @override
-  String get requiredParameterField => r'$R';
-  @override
-  String get defaultValuesField => r'$D';
-  @override
-  String get operatorSignature => r'$S';
-  @override
   String get genericInstantiationPrefix => r'$I';
 
   @override
   jsAst.Name get staticsPropertyName =>
       _staticsPropertyName ??= getFreshName(instanceScope, 'static');
 
-  FrequencyBasedNamer(JClosedWorld closedWorld, RuntimeTypeTags rtiTags)
-      : super(closedWorld, rtiTags) {
+  FrequencyBasedNamer(
+      JClosedWorld closedWorld, RuntimeTypeTags rtiTags, FixedNames fixedNames)
+      : super(closedWorld, rtiTags, fixedNames) {
     fieldRegistry = new _FieldNamingRegistry(this);
   }
 
diff --git a/pkg/compiler/lib/src/js_backend/minify_namer.dart b/pkg/compiler/lib/src/js_backend/minify_namer.dart
index b7751a1..c59f8f4 100644
--- a/pkg/compiler/lib/src/js_backend/minify_namer.dart
+++ b/pkg/compiler/lib/src/js_backend/minify_namer.dart
@@ -10,8 +10,9 @@
         _MinifiedFieldNamer,
         _MinifyConstructorBodyNamer,
         _MinifiedOneShotInterceptorNamer {
-  MinifyNamer(JClosedWorld closedWorld, RuntimeTypeTags rtiTags)
-      : super(closedWorld, rtiTags) {
+  MinifyNamer(
+      JClosedWorld closedWorld, RuntimeTypeTags rtiTags, FixedNames fixedNames)
+      : super(closedWorld, rtiTags, fixedNames) {
     reserveBackendNames();
     fieldRegistry = new _FieldNamingRegistry(this);
   }
@@ -25,25 +26,6 @@
   String get isolatePropertiesName => 'p';
   @override
   bool get shouldMinify => true;
-
-  @override
-  final String getterPrefix = 'g';
-  @override
-  final String setterPrefix = 's';
-  @override
-  final String callPrefix = ''; // this will create function names $<n>
-  @override
-  String get operatorIsPrefix => r'$i';
-  @override
-  String get operatorAsPrefix => r'$a';
-  @override
-  String get callCatchAllName => r'$C';
-  @override
-  String get requiredParameterField => r'$R';
-  @override
-  String get defaultValuesField => r'$D';
-  @override
-  String get operatorSignature => r'$S';
   @override
   String get genericInstantiationPrefix => r'$I';
 
diff --git a/pkg/compiler/lib/src/js_backend/namer.dart b/pkg/compiler/lib/src/js_backend/namer.dart
index dbbe8b7..b76662e 100644
--- a/pkg/compiler/lib/src/js_backend/namer.dart
+++ b/pkg/compiler/lib/src/js_backend/namer.dart
@@ -13,6 +13,7 @@
 
 import '../closure.dart';
 import '../common.dart';
+import '../common/codegen.dart';
 import '../common/names.dart' show Identifiers, Names, Selectors;
 import '../common_elements.dart' show JElementEnvironment;
 import '../constants/constant_system.dart' as constant_system;
@@ -421,46 +422,22 @@
     return _jsReserved;
   }
 
-  Set<String> _jsVariableReserved = null;
-
-  /// Names that cannot be used by local variables and parameters.
-  Set<String> get jsVariableReserved {
-    if (_jsVariableReserved == null) {
-      _jsVariableReserved = new Set<String>();
-      _jsVariableReserved.addAll(javaScriptKeywords);
-      _jsVariableReserved.addAll(reservedPropertySymbols);
-      _jsVariableReserved.addAll(reservedGlobalSymbols);
-      _jsVariableReserved.addAll(reservedGlobalObjectNames);
-      // 26 letters in the alphabet, 25 not counting I.
-      assert(reservedGlobalObjectNames.length == 25);
-      _jsVariableReserved.addAll(reservedGlobalHelperFunctions);
-    }
-    return _jsVariableReserved;
-  }
-
-  final String getterPrefix = r'get$';
   final String lazyGetterPrefix = r'$get$';
-  final String setterPrefix = r'set$';
   final String superPrefix = r'super$';
   final String metadataField = '@';
-  final String callPrefix = 'call';
-  String get callCatchAllName => r'call*';
-  final String callNameField = r'$callName';
   final String stubNameField = r'$stubName';
-  final String reflectableField = r'$reflectable';
   final String reflectionInfoField = r'$reflectionInfo';
   final String reflectionNameField = r'$reflectionName';
   final String metadataIndexField = r'$metadataIndex';
-  String get requiredParameterField => r'$requiredArgCount';
-  String get defaultValuesField => r'$defaultValues';
   final String methodsWithOptionalArgumentsField =
       r'$methodsWithOptionalArguments';
-  final String deferredAction = r'$deferredAction';
 
-  final String classDescriptorProperty = r'^';
-
+  @override
   final RuntimeTypeTags rtiTags;
 
+  @override
+  final FixedNames fixedNames;
+
   /// The non-minifying namer's [callPrefix] with a dollar after it.
   static const String _callPrefixDollar = r'call$';
 
@@ -477,13 +454,11 @@
   jsAst.Name get staticsPropertyName =>
       _staticsPropertyName ??= new StringBackedName('static');
 
-  final String rtiName = r'$ti';
-
   jsAst.Name _rtiFieldJsName;
 
   @override
   jsAst.Name get rtiFieldJsName =>
-      _rtiFieldJsName ??= new StringBackedName(rtiName);
+      _rtiFieldJsName ??= new StringBackedName(fixedNames.rtiName);
 
   // Name of property in a class description for the native dispatch metadata.
   final String nativeSpecProperty = '%';
@@ -582,14 +557,15 @@
   /// key into maps.
   final Map<LibraryEntity, String> _libraryKeys = HashMap();
 
-  Namer(this._closedWorld, this.rtiTags) {
-    _literalGetterPrefix = new StringBackedName(getterPrefix);
-    _literalSetterPrefix = new StringBackedName(setterPrefix);
+  Namer(this._closedWorld, this.rtiTags, this.fixedNames) {
+    _literalGetterPrefix = new StringBackedName(fixedNames.getterPrefix);
+    _literalSetterPrefix = new StringBackedName(fixedNames.setterPrefix);
   }
 
   JElementEnvironment get _elementEnvironment =>
       _closedWorld.elementEnvironment;
 
+  @override
   CommonElements get _commonElements => _closedWorld.commonElements;
 
   NativeData get _nativeData => _closedWorld.nativeData;
@@ -611,83 +587,6 @@
         entity.rootOfScope, () => new NamingScope());
   }
 
-  /// Returns the string that is to be used as the result of a call to
-  /// [JS_GET_NAME] at [node] with argument [name].
-  jsAst.Name getNameForJsGetName(Spannable spannable, JsGetName name) {
-    switch (name) {
-      case JsGetName.GETTER_PREFIX:
-        return asName(getterPrefix);
-      case JsGetName.SETTER_PREFIX:
-        return asName(setterPrefix);
-      case JsGetName.CALL_PREFIX:
-        return asName(callPrefix);
-      case JsGetName.CALL_PREFIX0:
-        return asName('${callPrefix}\$0');
-      case JsGetName.CALL_PREFIX1:
-        return asName('${callPrefix}\$1');
-      case JsGetName.CALL_PREFIX2:
-        return asName('${callPrefix}\$2');
-      case JsGetName.CALL_PREFIX3:
-        return asName('${callPrefix}\$3');
-      case JsGetName.CALL_PREFIX4:
-        return asName('${callPrefix}\$4');
-      case JsGetName.CALL_PREFIX5:
-        return asName('${callPrefix}\$5');
-      case JsGetName.CALL_CATCH_ALL:
-        return asName(callCatchAllName);
-      case JsGetName.REFLECTABLE:
-        return asName(reflectableField);
-      case JsGetName.CLASS_DESCRIPTOR_PROPERTY:
-        return asName(classDescriptorProperty);
-      case JsGetName.REQUIRED_PARAMETER_PROPERTY:
-        return asName(requiredParameterField);
-      case JsGetName.DEFAULT_VALUES_PROPERTY:
-        return asName(defaultValuesField);
-      case JsGetName.CALL_NAME_PROPERTY:
-        return asName(callNameField);
-      case JsGetName.DEFERRED_ACTION_PROPERTY:
-        return asName(deferredAction);
-      case JsGetName.OPERATOR_AS_PREFIX:
-        return asName(operatorAsPrefix);
-      case JsGetName.SIGNATURE_NAME:
-        return asName(operatorSignature);
-      case JsGetName.RTI_NAME:
-        return asName(rtiName);
-      case JsGetName.TYPEDEF_TAG:
-        return asName(rtiTags.typedefTag);
-      case JsGetName.FUNCTION_TYPE_TAG:
-        return asName(rtiTags.functionTypeTag);
-      case JsGetName.FUNCTION_TYPE_GENERIC_BOUNDS_TAG:
-        return asName(rtiTags.functionTypeGenericBoundsTag);
-      case JsGetName.FUNCTION_TYPE_VOID_RETURN_TAG:
-        return asName(rtiTags.functionTypeVoidReturnTag);
-      case JsGetName.FUNCTION_TYPE_RETURN_TYPE_TAG:
-        return asName(rtiTags.functionTypeReturnTypeTag);
-      case JsGetName.FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG:
-        return asName(rtiTags.functionTypeRequiredParametersTag);
-      case JsGetName.FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG:
-        return asName(rtiTags.functionTypeOptionalParametersTag);
-      case JsGetName.FUNCTION_TYPE_NAMED_PARAMETERS_TAG:
-        return asName(rtiTags.functionTypeNamedParametersTag);
-      case JsGetName.FUTURE_OR_TAG:
-        return asName(rtiTags.futureOrTag);
-      case JsGetName.FUTURE_OR_TYPE_ARGUMENT_TAG:
-        return asName(rtiTags.futureOrTypeTag);
-      case JsGetName.IS_INDEXABLE_FIELD_NAME:
-        return operatorIs(_commonElements.jsIndexingBehaviorInterface);
-      case JsGetName.NULL_CLASS_TYPE_NAME:
-        return runtimeTypeName(_commonElements.nullClass);
-      case JsGetName.OBJECT_CLASS_TYPE_NAME:
-        return runtimeTypeName(_commonElements.objectClass);
-      case JsGetName.FUNCTION_CLASS_TYPE_NAME:
-        return runtimeTypeName(_commonElements.functionClass);
-      case JsGetName.FUTURE_CLASS_TYPE_NAME:
-        return runtimeTypeName(_commonElements.futureClass);
-      default:
-        throw failedAt(spannable, 'Error: Namer has no name for "$name".');
-    }
-  }
-
   /// Return a reference to the given [name].
   ///
   /// This is used to ensure that every use site of a name has a unique node so
@@ -812,7 +711,8 @@
   /// concatenation at runtime, by applyFunction in js_helper.dart.
   jsAst.Name deriveCallMethodName(List<String> suffix) {
     // TODO(asgerf): Avoid clashes when named parameters contain $ symbols.
-    return new StringBackedName('$callPrefix\$${suffix.join(r'$')}');
+    return new StringBackedName(
+        '${fixedNames.callPrefix}\$${suffix.join(r'$')}');
   }
 
   /// The suffix list for the pattern:
@@ -1294,8 +1194,8 @@
     // or with one of the `call` stubs, such as `call$1`.
     assert(this is! MinifyNamer);
     if (name.startsWith(r'$') ||
-        name.startsWith(getterPrefix) ||
-        name.startsWith(setterPrefix) ||
+        name.startsWith(fixedNames.getterPrefix) ||
+        name.startsWith(fixedNames.setterPrefix) ||
         name.startsWith(_callPrefixDollar)) {
       name = '\$$name';
     }
@@ -1423,8 +1323,7 @@
     return _disambiguateInternalGlobal('getInterceptor\$$suffix');
   }
 
-  /// Property name used for the one-shot interceptor method for the given
-  /// [selector] and return-type specialization.
+  @override
   jsAst.Name nameForGetOneShotInterceptor(
       Selector selector, Iterable<ClassEntity> classes) {
     // The one-shot name is a global name derived from the invocation name.  To
@@ -1569,12 +1468,6 @@
 
   String globalObjectForConstant(ConstantValue constant) => 'C';
 
-  String get operatorIsPrefix => r'$is';
-
-  String get operatorAsPrefix => r'$as';
-
-  String get operatorSignature => r'$signature';
-
   String get genericInstantiationPrefix => r'$instantiate';
 
   // The name of the variable used to offset function signatures in deferred
@@ -1598,7 +1491,7 @@
     if (type.isFunctionType) {
       // TODO(erikcorry): Reduce from $isx to ix when we are minifying.
       return new CompoundName([
-        new StringBackedName(operatorIsPrefix),
+        new StringBackedName(fixedNames.operatorIsPrefix),
         _literalUnderscore,
         getFunctionTypeName(type)
       ]);
@@ -1610,8 +1503,10 @@
   @override
   jsAst.Name operatorIs(ClassEntity element) {
     // TODO(erikcorry): Reduce from $isx to ix when we are minifying.
-    return new CompoundName(
-        [new StringBackedName(operatorIsPrefix), runtimeTypeName(element)]);
+    return new CompoundName([
+      new StringBackedName(fixedNames.operatorIsPrefix),
+      runtimeTypeName(element)
+    ]);
   }
 
   /// Returns a name that does not clash with reserved JS keywords.
@@ -1625,37 +1520,28 @@
 
   @override
   jsAst.Name substitutionName(ClassEntity element) {
-    return new CompoundName(
-        [new StringBackedName(operatorAsPrefix), runtimeTypeName(element)]);
+    return new CompoundName([
+      new StringBackedName(fixedNames.operatorAsPrefix),
+      runtimeTypeName(element)
+    ]);
   }
 
   @override
   jsAst.Name asName(String name) {
-    if (name.startsWith(getterPrefix) && name.length > getterPrefix.length) {
+    if (name.startsWith(fixedNames.getterPrefix) &&
+        name.length > fixedNames.getterPrefix.length) {
       return new GetterName(_literalGetterPrefix,
-          new StringBackedName(name.substring(getterPrefix.length)));
+          new StringBackedName(name.substring(fixedNames.getterPrefix.length)));
     }
-    if (name.startsWith(setterPrefix) && name.length > setterPrefix.length) {
+    if (name.startsWith(fixedNames.setterPrefix) &&
+        name.length > fixedNames.setterPrefix.length) {
       return new GetterName(_literalSetterPrefix,
-          new StringBackedName(name.substring(setterPrefix.length)));
+          new StringBackedName(name.substring(fixedNames.setterPrefix.length)));
     }
 
     return new StringBackedName(name);
   }
 
-  /// Returns a variable name that cannot clash with a keyword, a global
-  /// variable, or any name starting with a single '$'.
-  ///
-  /// Furthermore, this function is injective, that is, it never returns the
-  /// same name for two different inputs.
-  String safeVariableName(String name) {
-    name = name.replaceAll('#', '_');
-    if (jsVariableReserved.contains(name) || name.startsWith(r'$')) {
-      return '\$$name';
-    }
-    return name;
-  }
-
   String operatorNameToIdentifier(String name) {
     if (name == null) return null;
     if (name == '==') {
@@ -1730,6 +1616,9 @@
   }
 }
 
+/// Returns a unique suffix for an intercepted accesses to [classes]. This is
+/// used as the suffix for emitted interceptor methods and as the unique key
+/// used to distinguish equivalences of sets of intercepted classes.
 String suffixForGetInterceptor(CommonElements commonElements,
     NativeData nativeData, Iterable<ClassEntity> classes) {
   String abbreviate(ClassEntity cls) {
@@ -2328,8 +2217,57 @@
   }
 }
 
+/// Fixed names usage by the namer.
+class FixedNames {
+  const FixedNames();
+
+  String get getterPrefix => r'get$';
+  String get setterPrefix => r'set$';
+  String get callPrefix => 'call';
+  String get callCatchAllName => r'call*';
+  String get callNameField => r'$callName';
+  String get reflectableField => r'$reflectable';
+  String get classDescriptorProperty => r'^';
+  String get defaultValuesField => r'$defaultValues';
+  String get deferredAction => r'$deferredAction';
+  String get operatorIsPrefix => r'$is';
+  String get operatorAsPrefix => r'$as';
+  String get operatorSignature => r'$signature';
+  String get requiredParameterField => r'$requiredArgCount';
+  String get rtiName => r'$ti';
+}
+
+/// Minified version of the fixed names usage by the namer.
+// TODO(johnniwinther): This should implement [FixedNames] and minify all fixed
+// names.
+class MinifiedFixedNames extends FixedNames {
+  const MinifiedFixedNames();
+
+  @override
+  String get getterPrefix => 'g';
+  @override
+  String get setterPrefix => 's';
+  @override
+  String get callPrefix => ''; // this will create function names $<n>
+  @override
+  String get operatorIsPrefix => r'$i';
+  @override
+  String get operatorAsPrefix => r'$a';
+  @override
+  String get callCatchAllName => r'$C';
+  @override
+  String get requiredParameterField => r'$R';
+  @override
+  String get defaultValuesField => r'$D';
+  @override
+  String get operatorSignature => r'$S';
+}
+
 /// Namer interface that can be used in modular code generation.
 abstract class ModularNamer {
+  FixedNames get fixedNames;
+  RuntimeTypeTags get rtiTags;
+
   /// Returns a variable use for accessing [library].
   ///
   /// This is one of the [reservedGlobalObjectNames]
@@ -2419,7 +2357,12 @@
   /// js_runtime contains a top-level `getInterceptor` method. The
   /// specializations have the same name, but with a suffix to avoid name
   /// collisions.
-  jsAst.Name nameForGetInterceptor(Iterable<ClassEntity> classes);
+  jsAst.Name nameForGetInterceptor(Set<ClassEntity> classes);
+
+  /// Property name used for the one-shot interceptor method for the given
+  /// [selector] and return-type specialization.
+  jsAst.Name nameForGetOneShotInterceptor(
+      Selector selector, Set<ClassEntity> classes);
 
   /// Returns the runtime name for [element].
   ///
@@ -2517,204 +2460,313 @@
   String implicitContinueLabelName(JumpTarget target) {
     return 'c\$${target.nestingLevel}';
   }
+
+  Set<String> _jsVariableReservedCache = null;
+
+  /// Names that cannot be used by local variables and parameters.
+  Set<String> get _jsVariableReserved {
+    if (_jsVariableReservedCache == null) {
+      _jsVariableReservedCache = new Set<String>();
+      _jsVariableReservedCache.addAll(Namer.javaScriptKeywords);
+      _jsVariableReservedCache.addAll(Namer.reservedPropertySymbols);
+      _jsVariableReservedCache.addAll(Namer.reservedGlobalSymbols);
+      _jsVariableReservedCache.addAll(Namer.reservedGlobalObjectNames);
+      // 26 letters in the alphabet, 25 not counting I.
+      assert(Namer.reservedGlobalObjectNames.length == 25);
+      _jsVariableReservedCache.addAll(Namer.reservedGlobalHelperFunctions);
+    }
+    return _jsVariableReservedCache;
+  }
+
+  /// Returns a variable name that cannot clash with a keyword, a global
+  /// variable, or any name starting with a single '$'.
+  ///
+  /// Furthermore, this function is injective, that is, it never returns the
+  /// same name for two different inputs.
+  String safeVariableName(String name) {
+    name = name.replaceAll('#', '_');
+    if (_jsVariableReserved.contains(name) || name.startsWith(r'$')) {
+      return '\$$name';
+    }
+    return name;
+  }
+
+  CommonElements get _commonElements;
+
+  /// Returns the string that is to be used as the result of a call to
+  /// [JS_GET_NAME] at [node] with argument [name].
+  jsAst.Name getNameForJsGetName(Spannable spannable, JsGetName name) {
+    switch (name) {
+      case JsGetName.GETTER_PREFIX:
+        return asName(fixedNames.getterPrefix);
+      case JsGetName.SETTER_PREFIX:
+        return asName(fixedNames.setterPrefix);
+      case JsGetName.CALL_PREFIX:
+        return asName(fixedNames.callPrefix);
+      case JsGetName.CALL_PREFIX0:
+        return asName('${fixedNames.callPrefix}\$0');
+      case JsGetName.CALL_PREFIX1:
+        return asName('${fixedNames.callPrefix}\$1');
+      case JsGetName.CALL_PREFIX2:
+        return asName('${fixedNames.callPrefix}\$2');
+      case JsGetName.CALL_PREFIX3:
+        return asName('${fixedNames.callPrefix}\$3');
+      case JsGetName.CALL_PREFIX4:
+        return asName('${fixedNames.callPrefix}\$4');
+      case JsGetName.CALL_PREFIX5:
+        return asName('${fixedNames.callPrefix}\$5');
+      case JsGetName.CALL_CATCH_ALL:
+        return asName(fixedNames.callCatchAllName);
+      case JsGetName.REFLECTABLE:
+        return asName(fixedNames.reflectableField);
+      case JsGetName.CLASS_DESCRIPTOR_PROPERTY:
+        return asName(fixedNames.classDescriptorProperty);
+      case JsGetName.REQUIRED_PARAMETER_PROPERTY:
+        return asName(fixedNames.requiredParameterField);
+      case JsGetName.DEFAULT_VALUES_PROPERTY:
+        return asName(fixedNames.defaultValuesField);
+      case JsGetName.CALL_NAME_PROPERTY:
+        return asName(fixedNames.callNameField);
+      case JsGetName.DEFERRED_ACTION_PROPERTY:
+        return asName(fixedNames.deferredAction);
+      case JsGetName.OPERATOR_AS_PREFIX:
+        return asName(fixedNames.operatorAsPrefix);
+      case JsGetName.SIGNATURE_NAME:
+        return asName(fixedNames.operatorSignature);
+      case JsGetName.RTI_NAME:
+        return asName(fixedNames.rtiName);
+      case JsGetName.TYPEDEF_TAG:
+        return asName(rtiTags.typedefTag);
+      case JsGetName.FUNCTION_TYPE_TAG:
+        return asName(rtiTags.functionTypeTag);
+      case JsGetName.FUNCTION_TYPE_GENERIC_BOUNDS_TAG:
+        return asName(rtiTags.functionTypeGenericBoundsTag);
+      case JsGetName.FUNCTION_TYPE_VOID_RETURN_TAG:
+        return asName(rtiTags.functionTypeVoidReturnTag);
+      case JsGetName.FUNCTION_TYPE_RETURN_TYPE_TAG:
+        return asName(rtiTags.functionTypeReturnTypeTag);
+      case JsGetName.FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG:
+        return asName(rtiTags.functionTypeRequiredParametersTag);
+      case JsGetName.FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG:
+        return asName(rtiTags.functionTypeOptionalParametersTag);
+      case JsGetName.FUNCTION_TYPE_NAMED_PARAMETERS_TAG:
+        return asName(rtiTags.functionTypeNamedParametersTag);
+      case JsGetName.FUTURE_OR_TAG:
+        return asName(rtiTags.futureOrTag);
+      case JsGetName.FUTURE_OR_TYPE_ARGUMENT_TAG:
+        return asName(rtiTags.futureOrTypeTag);
+      case JsGetName.IS_INDEXABLE_FIELD_NAME:
+        return operatorIs(_commonElements.jsIndexingBehaviorInterface);
+      case JsGetName.NULL_CLASS_TYPE_NAME:
+        return runtimeTypeName(_commonElements.nullClass);
+      case JsGetName.OBJECT_CLASS_TYPE_NAME:
+        return runtimeTypeName(_commonElements.objectClass);
+      case JsGetName.FUNCTION_CLASS_TYPE_NAME:
+        return runtimeTypeName(_commonElements.functionClass);
+      case JsGetName.FUTURE_CLASS_TYPE_NAME:
+        return runtimeTypeName(_commonElements.futureClass);
+      default:
+        throw failedAt(spannable, 'Error: Namer has no name for "$name".');
+    }
+  }
 }
 
 class ModularNamerImpl extends ModularNamer {
+  final CodegenRegistry _registry;
+  @override
+  final RuntimeTypeTags rtiTags;
+  @override
+  final FixedNames fixedNames;
+
+  @override
+  final CommonElements _commonElements;
+
+  ModularNamerImpl(
+      this._registry, this._commonElements, this.rtiTags, this.fixedNames);
+
   @override
   jsAst.Name get rtiFieldJsName {
-    return new ModularName(ModularNameKind.rtiField);
+    jsAst.Name name = new ModularName(ModularNameKind.rtiField);
+    _registry.registerModularName(name);
+    return name;
   }
 
   @override
   jsAst.Name runtimeTypeName(Entity element) {
-    return new ModularName(ModularNameKind.runtimeTypeName, element);
+    jsAst.Name name =
+        new ModularName(ModularNameKind.runtimeTypeName, data: element);
+    _registry.registerModularName(name);
+    return name;
   }
 
   @override
   jsAst.Name className(ClassEntity element) {
-    return new ModularName(ModularNameKind.className, element);
+    jsAst.Name name = new ModularName(ModularNameKind.className, data: element);
+    _registry.registerModularName(name);
+    return name;
   }
 
   @override
   jsAst.Expression readGlobalObjectForLibrary(LibraryEntity library) {
-    return new ModularVariableUse(
-        ModularVariableUseKind.globalObjectForLibrary, library);
+    jsAst.Expression expression = new ModularExpression(
+        ModularExpressionKind.globalObjectForLibrary, library);
+    _registry.registerModularExpression(expression);
+    return expression;
   }
 
   @override
   jsAst.Expression readGlobalObjectForClass(ClassEntity element) {
-    return new ModularVariableUse(
-        ModularVariableUseKind.globalObjectForClass, element);
+    jsAst.Expression expression = new ModularExpression(
+        ModularExpressionKind.globalObjectForClass, element);
+    _registry.registerModularExpression(expression);
+    return expression;
   }
 
   @override
   jsAst.Expression readGlobalObjectForType(Entity element) {
-    return new ModularVariableUse(
-        ModularVariableUseKind.globalObjectForType, element);
+    jsAst.Expression expression = new ModularExpression(
+        ModularExpressionKind.globalObjectForType, element);
+    _registry.registerModularExpression(expression);
+    return expression;
   }
 
   @override
   jsAst.Expression readGlobalObjectForMember(MemberEntity element) {
-    return new ModularVariableUse(
-        ModularVariableUseKind.globalObjectForMember, element);
+    jsAst.Expression expression = new ModularExpression(
+        ModularExpressionKind.globalObjectForMember, element);
+    _registry.registerModularExpression(expression);
+    return expression;
   }
 
   @override
   jsAst.Name aliasedSuperMemberPropertyName(MemberEntity member) {
-    return new ModularName(ModularNameKind.aliasedSuperMember, member);
+    jsAst.Name name =
+        new ModularName(ModularNameKind.aliasedSuperMember, data: member);
+    _registry.registerModularName(name);
+    return name;
   }
 
   @override
   jsAst.Name staticClosureName(FunctionEntity element) {
-    return new ModularName(ModularNameKind.staticClosure, element);
+    jsAst.Name name =
+        new ModularName(ModularNameKind.staticClosure, data: element);
+    _registry.registerModularName(name);
+    return name;
   }
 
   @override
   jsAst.Name methodPropertyName(FunctionEntity method) {
-    return new ModularName(ModularNameKind.methodProperty, method);
+    jsAst.Name name =
+        new ModularName(ModularNameKind.methodProperty, data: method);
+    _registry.registerModularName(name);
+    return name;
   }
 
   @override
   jsAst.Name instanceFieldPropertyName(FieldEntity element) {
-    return new ModularName(ModularNameKind.instanceField, element);
+    jsAst.Name name =
+        new ModularName(ModularNameKind.instanceField, data: element);
+    _registry.registerModularName(name);
+    return name;
   }
 
   @override
   jsAst.Name operatorIsType(DartType type) {
-    return new ModularName(ModularNameKind.operatorIsType, type);
+    jsAst.Name name =
+        new ModularName(ModularNameKind.operatorIsType, data: type);
+    _registry.registerModularName(name);
+    return name;
   }
 
   @override
   jsAst.Name instanceMethodName(FunctionEntity method) {
-    return new ModularName(ModularNameKind.instanceMethod, method);
+    jsAst.Name name =
+        new ModularName(ModularNameKind.instanceMethod, data: method);
+    _registry.registerModularName(name);
+    return name;
   }
 
   @override
   jsAst.Name invocationName(Selector selector) {
-    return new ModularName(ModularNameKind.invocation, selector);
+    jsAst.Name name =
+        new ModularName(ModularNameKind.invocation, data: selector);
+    _registry.registerModularName(name);
+    return name;
   }
 
   @override
   jsAst.Name lazyInitializerName(FieldEntity element) {
-    return new ModularName(ModularNameKind.lazyInitializer, element);
+    jsAst.Name name =
+        new ModularName(ModularNameKind.lazyInitializer, data: element);
+    _registry.registerModularName(name);
+    return name;
   }
 
   @override
   jsAst.Name operatorIs(ClassEntity element) {
-    return new ModularName(ModularNameKind.operatorIs, element);
+    jsAst.Name name =
+        new ModularName(ModularNameKind.operatorIs, data: element);
+    _registry.registerModularName(name);
+    return name;
   }
 
   @override
   jsAst.Name globalPropertyNameForType(Entity element) {
-    return new ModularName(ModularNameKind.globalPropertyNameForType, element);
+    jsAst.Name name = new ModularName(ModularNameKind.globalPropertyNameForType,
+        data: element);
+    _registry.registerModularName(name);
+    return name;
   }
 
   @override
   jsAst.Name globalPropertyNameForClass(ClassEntity element) {
-    return new ModularName(ModularNameKind.globalPropertyNameForClass, element);
+    jsAst.Name name = new ModularName(
+        ModularNameKind.globalPropertyNameForClass,
+        data: element);
+    _registry.registerModularName(name);
+    return name;
   }
 
   @override
   jsAst.Name globalPropertyNameForMember(MemberEntity element) {
-    return new ModularName(ModularNameKind.globalPropertyNameForClass, element);
+    jsAst.Name name = new ModularName(
+        ModularNameKind.globalPropertyNameForMember,
+        data: element);
+    _registry.registerModularName(name);
+    return name;
   }
 
   @override
-  jsAst.Name nameForGetInterceptor(Iterable<ClassEntity> classes) {
-    return new ModularName(ModularNameKind.nameForGetInterceptor, classes);
+  jsAst.Name nameForGetInterceptor(Set<ClassEntity> classes) {
+    jsAst.Name name =
+        new ModularName(ModularNameKind.nameForGetInterceptor, set: classes);
+    _registry.registerModularName(name);
+    return name;
   }
 
   @override
-  jsAst.Name asName(String name) {
-    return new ModularName(ModularNameKind.asName, name);
+  jsAst.Name nameForGetOneShotInterceptor(
+      Selector selector, Set<ClassEntity> classes) {
+    jsAst.Name name = new ModularName(
+        ModularNameKind.nameForGetOneShotInterceptor,
+        data: selector,
+        set: classes);
+    _registry.registerModularName(name);
+    return name;
+  }
+
+  @override
+  jsAst.Name asName(String text) {
+    jsAst.Name name = new ModularName(ModularNameKind.asName, data: text);
+    _registry.registerModularName(name);
+    return name;
   }
 
   @override
   jsAst.Name substitutionName(ClassEntity element) {
-    return new ModularName(ModularNameKind.substitution, element);
+    jsAst.Name name =
+        new ModularName(ModularNameKind.substitution, data: element);
+    _registry.registerModularName(name);
+    return name;
   }
 }
-
-enum ModularNameKind {
-  rtiField,
-  runtimeTypeName,
-  className,
-  aliasedSuperMember,
-  staticClosure,
-  methodProperty,
-  operatorIs,
-  operatorIsType,
-  substitution,
-  instanceMethod,
-  instanceField,
-  invocation,
-  lazyInitializer,
-  globalPropertyNameForClass,
-  globalPropertyNameForType,
-  globalPropertyNameForMember,
-  nameForGetInterceptor,
-  asName,
-}
-
-class ModularName extends jsAst.Name {
-  final ModularNameKind kind;
-  jsAst.Name deferred;
-  final Object data;
-
-  ModularName(this.kind, [this.data]);
-
-  @override
-  String get key {
-    assert(deferred != null);
-    return deferred.key;
-  }
-
-  @override
-  String get name {
-    assert(deferred != null);
-    return deferred.name;
-  }
-
-  @override
-  bool get allowRename {
-    assert(deferred != null);
-    return deferred.allowRename;
-  }
-
-  @override
-  int compareTo(covariant ModularName other) {
-    assert(deferred != null);
-    assert(other.deferred != null);
-    return deferred.compareTo(other.deferred);
-  }
-}
-
-enum ModularVariableUseKind {
-  globalObjectForLibrary,
-  globalObjectForClass,
-  globalObjectForType,
-  globalObjectForMember,
-}
-
-class ModularVariableUse extends jsAst.DeferredExpression {
-  final ModularVariableUseKind kind;
-  final Entity element;
-  jsAst.VariableUse _value;
-
-  ModularVariableUse(this.kind, this.element);
-
-  @override
-  jsAst.VariableUse get value {
-    assert(_value != null);
-    return _value;
-  }
-
-  void set value(jsAst.VariableUse value) {
-    assert(_value == null);
-    assert(value != null);
-    _value = value;
-  }
-
-  @override
-  int get precedenceLevel => value.precedenceLevel;
-}
diff --git a/pkg/compiler/lib/src/js_backend/namer_names.dart b/pkg/compiler/lib/src/js_backend/namer_names.dart
index db0819d..0604258 100644
--- a/pkg/compiler/lib/src/js_backend/namer_names.dart
+++ b/pkg/compiler/lib/src/js_backend/namer_names.dart
@@ -4,7 +4,6 @@
 
 part of js_backend.namer;
 
-// ignore: STRONG_MODE_INVALID_METHOD_OVERRIDE_FROM_BASE
 abstract class _NamerName extends jsAst.Name {
   int get _kind;
   _NamerName get _target => this;
@@ -20,7 +19,6 @@
 
 enum _NamerNameKinds { StringBacked, Getter, Setter, Async, Compound, Token }
 
-// ignore: STRONG_MODE_INVALID_METHOD_OVERRIDE_FROM_BASE
 class StringBackedName extends _NamerName {
   @override
   final String name;
@@ -43,14 +41,19 @@
   int get hashCode => name.hashCode;
 
   @override
-  int compareTo(covariant _NamerName other) {
-    other = other._target;
-    if (other._kind != _kind) return other._kind - _kind;
-    return name.compareTo(other.name);
+  int compareTo(jsAst.Name other) {
+    _NamerName otherNamerName;
+    if (other is ModularName) {
+      otherNamerName = other.value;
+    } else {
+      otherNamerName = other;
+    }
+    otherNamerName = otherNamerName._target;
+    if (otherNamerName._kind != _kind) return otherNamerName._kind - _kind;
+    return name.compareTo(otherNamerName.name);
   }
 }
 
-// ignore: STRONG_MODE_INVALID_METHOD_OVERRIDE_FROM_BASE
 abstract class _PrefixedName extends _NamerName implements jsAst.AstContainer {
   final jsAst.Name prefix;
   final jsAst.Name base;
@@ -80,10 +83,16 @@
   int get hashCode => base.hashCode * 13 + prefix.hashCode;
 
   @override
-  int compareTo(covariant _NamerName other) {
-    other = other._target;
-    if (other._kind != _kind) return other._kind - _kind;
-    _PrefixedName otherSameKind = other;
+  int compareTo(jsAst.Name other) {
+    _NamerName otherNamerName;
+    if (other is ModularName) {
+      otherNamerName = other.value;
+    } else {
+      otherNamerName = other;
+    }
+    otherNamerName = otherNamerName._target;
+    if (otherNamerName._kind != _kind) return otherNamerName._kind - _kind;
+    _PrefixedName otherSameKind = otherNamerName;
     int result = prefix.compareTo(otherSameKind.prefix);
     if (result == 0) {
       result = prefix.compareTo(otherSameKind.prefix);
@@ -95,7 +104,6 @@
   }
 }
 
-// ignore: STRONG_MODE_INVALID_METHOD_OVERRIDE_FROM_BASE
 class GetterName extends _PrefixedName {
   @override
   int get _kind => _NamerNameKinds.Getter.index;
@@ -103,7 +111,6 @@
   GetterName(jsAst.Name prefix, jsAst.Name base) : super(prefix, base);
 }
 
-// ignore: STRONG_MODE_INVALID_METHOD_OVERRIDE_FROM_BASE
 class SetterName extends _PrefixedName {
   @override
   int get _kind => _NamerNameKinds.Setter.index;
@@ -111,7 +118,6 @@
   SetterName(jsAst.Name prefix, jsAst.Name base) : super(prefix, base);
 }
 
-// ignore: STRONG_MODE_INVALID_METHOD_OVERRIDE_FROM_BASE
 class _AsyncName extends _PrefixedName {
   @override
   int get _kind => _NamerNameKinds.Async.index;
@@ -122,7 +128,6 @@
   bool get allowRename => true;
 }
 
-// ignore: STRONG_MODE_INVALID_METHOD_OVERRIDE_FROM_BASE
 class CompoundName extends _NamerName implements jsAst.AstContainer {
   final List<_NamerName> _parts;
   @override
@@ -170,10 +175,16 @@
   }
 
   @override
-  int compareTo(covariant _NamerName other) {
-    other = other._target;
-    if (other._kind != _kind) return other._kind - _kind;
-    CompoundName otherSameKind = other;
+  int compareTo(jsAst.Name other) {
+    _NamerName otherNamerName;
+    if (other is ModularName) {
+      otherNamerName = other.value;
+    } else {
+      otherNamerName = other;
+    }
+    otherNamerName = otherNamerName._target;
+    if (otherNamerName._kind != _kind) return otherNamerName._kind - _kind;
+    CompoundName otherSameKind = otherNamerName;
     if (otherSameKind._parts.length != _parts.length) {
       return otherSameKind._parts.length - _parts.length;
     }
@@ -185,7 +196,6 @@
   }
 }
 
-// ignore: STRONG_MODE_INVALID_METHOD_OVERRIDE_FROM_BASE
 class TokenName extends _NamerName implements jsAst.ReferenceCountedAstNode {
   @override
   int get _kind => _NamerNameKinds.Token.index;
@@ -201,8 +211,8 @@
 
   @override
   String get name {
-    assert(isFinalized);
-    return _name;
+    assert(isFinalized, "TokenName($key) has not been finalized.");
+    return _name ?? key;
   }
 
   @override
@@ -214,7 +224,7 @@
   }
 
   @override
-  markSeen(jsAst.TokenCounter counter) => _rc++;
+  void markSeen(jsAst.TokenCounter counter) => _rc++;
 
   @override
   bool operator ==(other) {
@@ -226,7 +236,7 @@
   @override
   int get hashCode => super.hashCode;
 
-  finalize() {
+  void finalize() {
     assert(
         !isFinalized,
         failedAt(NO_LOCATION_SPANNABLE,
@@ -235,7 +245,6 @@
   }
 }
 
-// ignore: STRONG_MODE_INVALID_METHOD_OVERRIDE_FROM_BASE
 class _NameReference extends _NamerName implements jsAst.AstContainer {
   @override
   _NamerName _target;
@@ -254,7 +263,15 @@
   String get name => _target.name;
 
   @override
-  int compareTo(covariant _NamerName other) => _target.compareTo(other);
+  int compareTo(jsAst.Name other) {
+    _NamerName otherNamerName;
+    if (other is ModularName) {
+      otherNamerName = other.value;
+    } else {
+      otherNamerName = other;
+    }
+    return _target.compareTo(otherNamerName);
+  }
 
   @override
   bool operator ==(other) => _target == other;
diff --git a/pkg/compiler/lib/src/js_emitter/class_stub_generator.dart b/pkg/compiler/lib/src/js_emitter/class_stub_generator.dart
index 9e49e92..b6f90d4 100644
--- a/pkg/compiler/lib/src/js_emitter/class_stub_generator.dart
+++ b/pkg/compiler/lib/src/js_emitter/class_stub_generator.dart
@@ -62,7 +62,7 @@
       typeParameters,
       fieldInitializers,
       typeInits,
-      _namer.deferredAction
+      _namer.fixedNames.deferredAction
     ]);
   }
 
diff --git a/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart b/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart
index 96e3219..35cca02 100644
--- a/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart
+++ b/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart
@@ -4,8 +4,6 @@
 
 library dart2js.js_emitter.code_emitter_task;
 
-import 'package:js_runtime/shared/embedded_names.dart' show JsBuiltin;
-
 import '../common.dart';
 import '../common/tasks.dart' show CompilerTask;
 import '../compiler.dart' show Compiler;
@@ -183,6 +181,9 @@
 
   /// Returns the JS code for accessing the given [constant].
   jsAst.Expression constantReference(ConstantValue constant);
+
+  /// Returns the JS code for accessing the global property [global].
+  String generateEmbeddedGlobalAccessString(String global);
 }
 
 /// Interface for the emitter that is used during the emission phase on the
@@ -208,9 +209,6 @@
   int compareConstants(ConstantValue a, ConstantValue b);
   bool isConstantInlinedOrAlreadyEmitted(ConstantValue constant);
 
-  /// Returns the JS template for the given [builtin].
-  jsAst.Template templateForBuiltin(JsBuiltin builtin);
-
   /// Returns the size of the code generated for a given output [unit].
   int generatedSize(OutputUnit unit);
 }
diff --git a/pkg/compiler/lib/src/js_emitter/instantiation_stub_generator.dart b/pkg/compiler/lib/src/js_emitter/instantiation_stub_generator.dart
index 5905955..7a13db6 100644
--- a/pkg/compiler/lib/src/js_emitter/instantiation_stub_generator.dart
+++ b/pkg/compiler/lib/src/js_emitter/instantiation_stub_generator.dart
@@ -106,7 +106,8 @@
   /// }
   /// ```
   ParameterStubMethod _generateSignatureStub(FieldEntity functionField) {
-    jsAst.Name operatorSignature = _namer.asName(_namer.operatorSignature);
+    jsAst.Name operatorSignature =
+        _namer.asName(_namer.fixedNames.operatorSignature);
 
     jsAst.Fun function = js('function() { return #(#(this.#), this.#); }', [
       _emitter.staticFunctionAccess(
diff --git a/pkg/compiler/lib/src/js_emitter/metadata_collector.dart b/pkg/compiler/lib/src/js_emitter/metadata_collector.dart
index 57ff772..0fa30eb 100644
--- a/pkg/compiler/lib/src/js_emitter/metadata_collector.dart
+++ b/pkg/compiler/lib/src/js_emitter/metadata_collector.dart
@@ -37,7 +37,7 @@
   // will be applied to the [entry] to also mark potential [_MetadataEntry]
   // instances in the [entry] as seen.
   @override
-  markSeen(jsAst.TokenCounter visitor);
+  void markSeen(jsAst.TokenCounter visitor);
 }
 
 class _BoundMetadataEntry extends _MetadataEntry {
@@ -65,7 +65,7 @@
   bool get isUsed => _rc > 0;
 
   @override
-  markSeen(jsAst.BaseVisitor visitor) {
+  void markSeen(jsAst.BaseVisitor visitor) {
     _rc++;
     if (_rc == 1) entry.accept(visitor);
   }
diff --git a/pkg/compiler/lib/src/js_emitter/runtime_type_generator.dart b/pkg/compiler/lib/src/js_emitter/runtime_type_generator.dart
index ad0cb3c..d82e130 100644
--- a/pkg/compiler/lib/src/js_emitter/runtime_type_generator.dart
+++ b/pkg/compiler/lib/src/js_emitter/runtime_type_generator.dart
@@ -173,7 +173,7 @@
         }
         if (encoding != null) {
           jsAst.Name operatorSignature =
-              _namer.asName(_namer.operatorSignature);
+              _namer.asName(_namer.fixedNames.operatorSignature);
           result.addSignature(classElement, operatorSignature, encoding);
         }
       }
@@ -203,7 +203,8 @@
         jsAst.Expression thisAccess = new jsAst.This();
         jsAst.Expression encoding = _rtiEncoder.getSignatureEncoding(
             _namer, emitterTask.emitter, type, thisAccess);
-        jsAst.Name operatorSignature = _namer.asName(_namer.operatorSignature);
+        jsAst.Name operatorSignature =
+            _namer.asName(_namer.fixedNames.operatorSignature);
         result.addSignature(classElement, operatorSignature, encoding);
       }
     }
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart
index 70f0219..ba22b6e 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart
@@ -4,19 +4,18 @@
 
 library dart2js.js_emitter.startup_emitter;
 
-import 'package:js_runtime/shared/embedded_names.dart'
-    show JsBuiltin, METADATA, TYPES;
-
 import '../../../compiler_new.dart';
 import '../../common.dart';
+import '../../common/codegen.dart';
 import '../../common/tasks.dart';
-import '../../constants/values.dart' show ConstantValue;
+import '../../constants/values.dart';
 import '../../deferred_load.dart' show OutputUnit;
 import '../../dump_info.dart';
 import '../../elements/entities.dart';
 import '../../io/source_information.dart';
 import '../../js/js.dart' as js;
-import '../../js_backend/js_backend.dart' show ModularNamer, Namer;
+import '../../js_backend/constant_emitter.dart';
+import '../../js_backend/namer.dart';
 import '../../js_backend/runtime_types.dart';
 import '../../options.dart';
 import '../../universe/codegen_world_builder.dart' show CodegenWorld;
@@ -26,10 +25,10 @@
 import '../program_builder/program_builder.dart' show ProgramBuilder;
 import 'model_emitter.dart';
 
-class ModularEmitterImpl implements ModularEmitter {
+abstract class ModularEmitterBase implements ModularEmitter {
   final ModularNamer _namer;
 
-  ModularEmitterImpl(this._namer);
+  ModularEmitterBase(this._namer);
 
   js.PropertyAccess globalPropertyAccessForClass(ClassEntity element) {
     js.Name name = _namer.globalPropertyNameForClass(element);
@@ -95,17 +94,47 @@
   }
 
   @override
+  String generateEmbeddedGlobalAccessString(String global) {
+    // TODO(floitsch): don't use 'init' as global embedder storage.
+    return 'init.$global';
+  }
+}
+
+class ModularEmitterImpl extends ModularEmitterBase {
+  final CodegenRegistry _registry;
+  final ModularConstantEmitter _constantEmitter;
+
+  ModularEmitterImpl(
+      ModularNamer namer, this._registry, CompilerOptions options)
+      : _constantEmitter = new ModularConstantEmitter(options),
+        super(namer);
+
+  @override
   js.Expression constantReference(ConstantValue constant) {
-    throw new UnimplementedError("ModularEmitter.constantReference");
+    if (constant.isFunction) {
+      FunctionConstantValue function = constant;
+      return staticClosureAccess(function.element);
+    }
+    js.Expression expression = _constantEmitter.generate(constant);
+    if (expression != null) {
+      return expression;
+    }
+    expression =
+        new ModularExpression(ModularExpressionKind.constant, constant);
+    _registry.registerModularExpression(expression);
+    return expression;
   }
 
   @override
   js.Expression generateEmbeddedGlobalAccess(String global) {
-    throw new UnimplementedError("ModularEmitter.generateEmbeddedGlobalAccess");
+    js.Expression expression = new ModularExpression(
+        ModularExpressionKind.embeddedGlobalAccess, global);
+    _registry.registerModularExpression(expression);
+    return expression;
   }
 }
 
-class EmitterImpl extends ModularEmitterImpl implements Emitter {
+class EmitterImpl extends ModularEmitterBase implements Emitter {
   final DiagnosticReporter _reporter;
   final JClosedWorld _closedWorld;
   final RuntimeTypesEncoder _rtiEncoder;
@@ -174,7 +203,7 @@
 
   @override
   js.Expression generateEmbeddedGlobalAccess(String global) {
-    return _emitter.generateEmbeddedGlobalAccess(global);
+    return js.js(generateEmbeddedGlobalAccessString(global));
   }
 
   @override
@@ -189,63 +218,6 @@
   }
 
   @override
-  js.Template templateForBuiltin(JsBuiltin builtin) {
-    switch (builtin) {
-      case JsBuiltin.dartObjectConstructor:
-        ClassEntity objectClass = _closedWorld.commonElements.objectClass;
-        return js.js.expressionTemplateYielding(typeAccess(objectClass));
-
-      case JsBuiltin.isCheckPropertyToJsConstructorName:
-        int isPrefixLength = _namer.operatorIsPrefix.length;
-        return js.js.expressionTemplateFor('#.substring($isPrefixLength)');
-
-      case JsBuiltin.isFunctionType:
-        return _rtiEncoder.templateForIsFunctionType;
-
-      case JsBuiltin.isFutureOrType:
-        return _rtiEncoder.templateForIsFutureOrType;
-
-      case JsBuiltin.isVoidType:
-        return _rtiEncoder.templateForIsVoidType;
-
-      case JsBuiltin.isDynamicType:
-        return _rtiEncoder.templateForIsDynamicType;
-
-      case JsBuiltin.isJsInteropTypeArgument:
-        return _rtiEncoder.templateForIsJsInteropTypeArgument;
-
-      case JsBuiltin.rawRtiToJsConstructorName:
-        return js.js.expressionTemplateFor("#.name");
-
-      case JsBuiltin.rawRuntimeType:
-        return js.js.expressionTemplateFor("#.constructor");
-
-      case JsBuiltin.isSubtype:
-        // TODO(floitsch): move this closer to where is-check properties are
-        // built.
-        String isPrefix = _namer.operatorIsPrefix;
-        return js.js.expressionTemplateFor("('$isPrefix' + #) in #.prototype");
-
-      case JsBuiltin.isGivenTypeRti:
-        return js.js.expressionTemplateFor('#.name === #');
-
-      case JsBuiltin.getMetadata:
-        String metadataAccess =
-            _emitter.generateEmbeddedGlobalAccessString(METADATA);
-        return js.js.expressionTemplateFor("$metadataAccess[#]");
-
-      case JsBuiltin.getType:
-        String typesAccess = _emitter.generateEmbeddedGlobalAccessString(TYPES);
-        return js.js.expressionTemplateFor("$typesAccess[#]");
-
-      default:
-        _reporter.internalError(
-            NO_LOCATION_SPANNABLE, "Unhandled Builtin: $builtin");
-        return null;
-    }
-  }
-
-  @override
   int generatedSize(OutputUnit unit) {
     if (_emitter.omittedFragments.any((f) => f.outputUnit == unit)) {
       return 0;
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 e8a9910..0e58794 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
@@ -597,7 +597,7 @@
       this._codegenWorld);
 
   js.Expression generateEmbeddedGlobalAccess(String global) =>
-      _modelEmitter.generateEmbeddedGlobalAccess(global);
+      _emitter.generateEmbeddedGlobalAccess(global);
 
   js.Expression generateConstantReference(ConstantValue value) =>
       _modelEmitter.generateConstantReference(value);
@@ -631,7 +631,7 @@
       'directAccessTestExpression': js.js(directAccessTestExpression),
       'cyclicThrow': _emitter
           .staticFunctionAccess(_closedWorld.commonElements.cyclicThrowHelper),
-      'operatorIsPrefix': js.string(_namer.operatorIsPrefix),
+      'operatorIsPrefix': js.string(_namer.fixedNames.operatorIsPrefix),
       'tearOffCode': new js.Block(buildTearOffCode(
           _options, _emitter, _namer, _closedWorld.commonElements)),
       'embeddedTypes': generateEmbeddedGlobalAccess(TYPES),
@@ -645,10 +645,10 @@
       'staticState': js.js('#', _namer.staticStateHolder),
       'constantHolderReference': buildConstantHolderReference(program),
       'holders': holderCode.statements,
-      'callName': js.string(_namer.callNameField),
+      'callName': js.string(_namer.fixedNames.callNameField),
       'stubName': js.string(_namer.stubNameField),
-      'argumentCount': js.string(_namer.requiredParameterField),
-      'defaultArgumentValues': js.string(_namer.defaultValuesField),
+      'argumentCount': js.string(_namer.fixedNames.requiredParameterField),
+      'defaultArgumentValues': js.string(_namer.fixedNames.defaultValuesField),
       'deferredGlobal': ModelEmitter.deferredInitializersGlobal,
       'hasSoftDeferredClasses': program.hasSoftDeferredClasses,
       'softId': js.string(softDeferredId),
@@ -817,8 +817,8 @@
     for (Library library in fragment.libraries) {
       for (StaticMethod method in library.statics) {
         assert(!method.holder.isStaticStateHolder);
-        var staticMethod = emitStaticMethod(method);
-        staticMethod.forEach((key, value) {
+        Map<js.Name, js.Expression> propertyMap = emitStaticMethod(method);
+        propertyMap.forEach((js.Name key, js.Expression value) {
           var property = new js.Property(js.quoteName(key), value);
           holderCode[method.holder].add(property);
           registerEntityAst(method.element, property, library: library.element);
@@ -826,7 +826,7 @@
       }
       for (Class cls in library.classes) {
         assert(!cls.holder.isStaticStateHolder);
-        var constructor = emitConstructor(cls);
+        js.Expression constructor = emitConstructor(cls);
         var property = new js.Property(js.quoteName(cls.name), constructor);
         registerEntityAst(cls.element, property, library: library.element);
         holderCode[cls.holder].add(property);
@@ -1072,14 +1072,15 @@
       // prototype for common values.
 
       // Closures taking exactly one argument are common.
+      properties.add(js.Property(js.string(_namer.fixedNames.callCatchAllName),
+          js.quoteName(call1Name)));
       properties.add(js.Property(
-          js.string(_namer.callCatchAllName), js.quoteName(call1Name)));
-      properties.add(
-          js.Property(js.string(_namer.requiredParameterField), js.number(1)));
+          js.string(_namer.fixedNames.requiredParameterField), js.number(1)));
 
       // Most closures have no optional arguments.
       properties.add(js.Property(
-          js.string(_namer.defaultValuesField), new js.LiteralNull()));
+          js.string(_namer.fixedNames.defaultValuesField),
+          new js.LiteralNull()));
     }
 
     return new js.ObjectInitializer(properties);
@@ -1189,12 +1190,12 @@
           js.Name applyName = method.applyIndex == 0
               ? method.name
               : method.parameterStubs[method.applyIndex - 1].name;
-          properties[js.string(_namer.callCatchAllName)] =
+          properties[js.string(_namer.fixedNames.callCatchAllName)] =
               js.quoteName(applyName);
         }
         // Common case of '1' is stored on the Closure class.
         if (method.requiredParameterCount != 1 || forceAdd) {
-          properties[js.string(_namer.requiredParameterField)] =
+          properties[js.string(_namer.fixedNames.requiredParameterField)] =
               js.number(method.requiredParameterCount);
         }
 
@@ -1203,7 +1204,8 @@
         // Default values property of `null` is stored on the common JS
         // superclass.
         if (defaultValues is! js.LiteralNull || forceAdd) {
-          properties[js.string(_namer.defaultValuesField)] = defaultValues;
+          properties[js.string(_namer.fixedNames.defaultValuesField)] =
+              defaultValues;
         }
       }
     }
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 5d3f455..064bd19 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
@@ -116,15 +116,6 @@
     return js.js('makeConstList(#)', [array]);
   }
 
-  js.Expression generateEmbeddedGlobalAccess(String global) {
-    return js.js(generateEmbeddedGlobalAccessString(global));
-  }
-
-  String generateEmbeddedGlobalAccessString(String global) {
-    // TODO(floitsch): don't use 'init' as global embedder storage.
-    return 'init.$global';
-  }
-
   bool isConstantInlinedOrAlreadyEmitted(ConstantValue constant) {
     if (constant.isFunction) return true; // Already emitted.
     if (constant.isPrimitive) return true; // Inlined.
diff --git a/pkg/compiler/lib/src/js_model/element_map.dart b/pkg/compiler/lib/src/js_model/element_map.dart
index 1c7f8f8..2b2d412 100644
--- a/pkg/compiler/lib/src/js_model/element_map.dart
+++ b/pkg/compiler/lib/src/js_model/element_map.dart
@@ -15,9 +15,6 @@
 import '../ir/closure.dart';
 import '../ir/static_type_provider.dart';
 import '../ir/util.dart';
-import '../js/js.dart' as js;
-import '../js_backend/namer.dart';
-import '../js_emitter/code_emitter_task.dart';
 import '../js_model/closure.dart' show JRecordField;
 import '../js_model/elements.dart' show JGeneratorBody;
 import '../native/behavior.dart';
@@ -104,9 +101,6 @@
   NativeBehavior getNativeBehaviorForJsEmbeddedGlobalCall(
       ir.StaticInvocation node);
 
-  /// Returns the [js.Name] for the `JsGetName` [constant] value.
-  js.Name getNameForJsGetName(ConstantValue constant, Namer namer);
-
   /// Computes the [ConstantValue] for the constant [expression].
   // TODO(johnniwinther): Move to [KernelToElementMapForBuilding]. This is only
   // used in impact builder for symbol constants.
@@ -140,9 +134,6 @@
   /// Returns the [LibraryEntity] corresponding to the library [node].
   LibraryEntity getLibrary(ir.Library node);
 
-  /// Returns the [js.Template] for the `JsBuiltin` [constant] value.
-  js.Template getJsBuiltinTemplate(ConstantValue constant, Emitter emitter);
-
   /// Returns a [Spannable] for a message pointing to the IR [node] in the
   /// context of [member].
   Spannable getSpannable(MemberEntity member, ir.Node node);
diff --git a/pkg/compiler/lib/src/js_model/element_map_impl.dart b/pkg/compiler/lib/src/js_model/element_map_impl.dart
index 30418af..47f5b36 100644
--- a/pkg/compiler/lib/src/js_model/element_map_impl.dart
+++ b/pkg/compiler/lib/src/js_model/element_map_impl.dart
@@ -4,7 +4,6 @@
 
 import 'package:front_end/src/api_unstable/dart2js.dart' show Link, LinkBuilder;
 
-import 'package:js_runtime/shared/embedded_names.dart';
 import 'package:kernel/ast.dart' as ir;
 import 'package:kernel/class_hierarchy.dart' as ir;
 import 'package:kernel/core_types.dart' as ir;
@@ -36,11 +35,8 @@
 import '../ir/static_type_cache.dart';
 import '../ir/static_type_provider.dart';
 import '../ir/util.dart';
-import '../js/js.dart' as js;
 import '../js_backend/annotations.dart';
-import '../js_backend/namer.dart';
 import '../js_backend/native_data.dart';
-import '../js_emitter/code_emitter_task.dart';
 import '../kernel/element_map_impl.dart';
 import '../kernel/env.dart';
 import '../kernel/kelements.dart';
@@ -1419,29 +1415,6 @@
   }
 
   @override
-  js.Name getNameForJsGetName(ConstantValue constant, Namer namer) {
-    int index = extractEnumIndexFromConstantValue(
-        constant, commonElements.jsGetNameEnum);
-    if (index == null) return null;
-    return namer.getNameForJsGetName(
-        CURRENT_ELEMENT_SPANNABLE, JsGetName.values[index]);
-  }
-
-  int extractEnumIndexFromConstantValue(
-      ConstantValue constant, ClassEntity classElement) {
-    if (constant is ConstructedConstantValue) {
-      if (constant.type.element == classElement) {
-        assert(constant.fields.length == 1 || constant.fields.length == 2);
-        ConstantValue indexConstant = constant.fields.values.first;
-        if (indexConstant is IntConstantValue) {
-          return indexConstant.intValue.toInt();
-        }
-      }
-    }
-    return null;
-  }
-
-  @override
   ConstantValue getConstantValue(ir.Expression node,
       {bool requireConstant: true, bool implicitNull: false}) {
     if (node is ir.ConstantExpression) {
@@ -2109,14 +2082,6 @@
     }
     return generatorBody;
   }
-
-  @override
-  js.Template getJsBuiltinTemplate(ConstantValue constant, Emitter emitter) {
-    int index = extractEnumIndexFromConstantValue(
-        constant, commonElements.jsBuiltinEnum);
-    if (index == null) return null;
-    return emitter.templateForBuiltin(JsBuiltin.values[index]);
-  }
 }
 
 class JsElementEnvironment extends ElementEnvironment
diff --git a/pkg/compiler/lib/src/js_model/js_strategy.dart b/pkg/compiler/lib/src/js_model/js_strategy.dart
index 8a3c2e0..1dc1c0b 100644
--- a/pkg/compiler/lib/src/js_model/js_strategy.dart
+++ b/pkg/compiler/lib/src/js_model/js_strategy.dart
@@ -27,6 +27,7 @@
 import '../js_backend/inferred_data.dart';
 import '../js_backend/namer.dart';
 import '../js_backend/native_data.dart';
+import '../js_backend/runtime_types.dart';
 import '../js_emitter/code_emitter_task.dart';
 import '../kernel/kernel_strategy.dart';
 import '../native/behavior.dart';
@@ -110,6 +111,7 @@
         codegen.namer,
         codegen.emitter,
         codegen.tracer,
+        codegen.rtiEncoder,
         sourceInformationStrategy);
   }
 
@@ -188,6 +190,7 @@
   final ModularNamer _namer;
   final ModularEmitter _emitter;
   final Tracer _tracer;
+  final RuntimeTypesEncoder _rtiEncoder;
   final SourceInformationStrategy _sourceInformationStrategy;
 
   // TODO(johnniwinther,sra): Inlining decisions should not be based on the
@@ -203,6 +206,7 @@
       this._namer,
       this._emitter,
       this._tracer,
+      this._rtiEncoder,
       this._sourceInformationStrategy);
 
   @override
@@ -223,6 +227,7 @@
           _namer,
           _emitter,
           _tracer,
+          _rtiEncoder,
           _sourceInformationStrategy,
           _inlineCache);
       return builder.build();
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index 6ac4d08..e39180d 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.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 'package:js_runtime/shared/embedded_names.dart';
 import 'package:kernel/ast.dart' as ir;
 
 import '../closure.dart';
@@ -10,12 +11,7 @@
 import '../common/names.dart';
 import '../common_elements.dart';
 import '../constants/constant_system.dart' as constant_system;
-import '../constants/values.dart'
-    show
-        ConstantValue,
-        InterceptorConstantValue,
-        StringConstantValue,
-        TypeConstantValue;
+import '../constants/values.dart';
 import '../dump_info.dart';
 import '../elements/entities.dart';
 import '../elements/jumps.dart';
@@ -121,6 +117,7 @@
   final CodegenRegistry registry;
   final ClosureData _closureDataLookup;
   final Tracer _tracer;
+  final RuntimeTypesEncoder _rtiEncoder;
 
   /// A stack of [InterfaceType]s that have been seen during inlining of
   /// factory constructors.  These types are preserved in [HInvokeStatic]s and
@@ -168,6 +165,7 @@
       this._namer,
       this._emitter,
       this._tracer,
+      this._rtiEncoder,
       this._sourceInformationStrategy,
       this._inlineCache)
       : this.targetElement = _effectiveTargetElementFor(_initialTargetElement),
@@ -452,7 +450,7 @@
                 closedWorld.fieldAnalysis.getFieldData(targetElement);
 
             if (fieldData.initialValue != null) {
-              registry.worldImpact.registerConstantUse(
+              registry.registerConstantUse(
                   new ConstantUse.init(fieldData.initialValue));
               if (targetElement.isStatic || targetElement.isTopLevel) {
                 /// No code is created for this field: All references inline the
@@ -462,7 +460,7 @@
             } else if (fieldData.isLazy) {
               // The generated initializer needs be wrapped in the cyclic-error
               // helper.
-              registry.worldImpact.registerStaticUse(new StaticUse.staticInvoke(
+              registry.registerStaticUse(new StaticUse.staticInvoke(
                   closedWorld.commonElements.cyclicThrowHelper,
                   CallStructure.ONE_ARG));
             }
@@ -4339,8 +4337,7 @@
     HInstruction instruction = pop();
 
     if (instruction is HConstant) {
-      js.Name name =
-          _elementMap.getNameForJsGetName(instruction.constant, _namer);
+      js.Name name = _getNameForJsGetName(instruction.constant, _namer);
       stack.add(graph.addConstantStringFromName(name, closedWorld));
       return;
     }
@@ -4353,6 +4350,29 @@
     stack.add(graph.addConstantNull(closedWorld));
   }
 
+  int _extractEnumIndexFromConstantValue(
+      ConstantValue constant, ClassEntity classElement) {
+    if (constant is ConstructedConstantValue) {
+      if (constant.type.element == classElement) {
+        assert(constant.fields.length == 1 || constant.fields.length == 2);
+        ConstantValue indexConstant = constant.fields.values.first;
+        if (indexConstant is IntConstantValue) {
+          return indexConstant.intValue.toInt();
+        }
+      }
+    }
+    return null;
+  }
+
+  /// Returns the [js.Name] for the `JsGetName` [constant] value.
+  js.Name _getNameForJsGetName(ConstantValue constant, ModularNamer namer) {
+    int index = _extractEnumIndexFromConstantValue(
+        constant, _commonElements.jsGetNameEnum);
+    if (index == null) return null;
+    return namer.getNameForJsGetName(
+        CURRENT_ELEMENT_SPANNABLE, JsGetName.values[index]);
+  }
+
   void _handleForeignJsEmbeddedGlobal(ir.StaticInvocation invocation) {
     if (_unexpectedForeignArguments(invocation,
         minPositional: 2, maxPositional: 2)) {
@@ -4393,8 +4413,7 @@
 
     js.Template template;
     if (instruction is HConstant) {
-      template =
-          _elementMap.getJsBuiltinTemplate(instruction.constant, _emitter);
+      template = _getJsBuiltinTemplate(instruction.constant, _emitter);
     }
     if (template == null) {
       reporter.reportErrorMessage(
@@ -4425,6 +4444,73 @@
         nativeBehavior: nativeBehavior));
   }
 
+  /// Returns the [js.Template] for the `JsBuiltin` [constant] value.
+  js.Template _getJsBuiltinTemplate(
+      ConstantValue constant, ModularEmitter emitter) {
+    int index = _extractEnumIndexFromConstantValue(
+        constant, _commonElements.jsBuiltinEnum);
+    if (index == null) return null;
+    return _templateForBuiltin(JsBuiltin.values[index]);
+  }
+
+  /// Returns the JS template for the given [builtin].
+  js.Template _templateForBuiltin(JsBuiltin builtin) {
+    switch (builtin) {
+      case JsBuiltin.dartObjectConstructor:
+        ClassEntity objectClass = closedWorld.commonElements.objectClass;
+        return js.js
+            .expressionTemplateYielding(_emitter.typeAccess(objectClass));
+
+      case JsBuiltin.isCheckPropertyToJsConstructorName:
+        int isPrefixLength = _namer.fixedNames.operatorIsPrefix.length;
+        return js.js.expressionTemplateFor('#.substring($isPrefixLength)');
+
+      case JsBuiltin.isFunctionType:
+        return _rtiEncoder.templateForIsFunctionType;
+
+      case JsBuiltin.isFutureOrType:
+        return _rtiEncoder.templateForIsFutureOrType;
+
+      case JsBuiltin.isVoidType:
+        return _rtiEncoder.templateForIsVoidType;
+
+      case JsBuiltin.isDynamicType:
+        return _rtiEncoder.templateForIsDynamicType;
+
+      case JsBuiltin.isJsInteropTypeArgument:
+        return _rtiEncoder.templateForIsJsInteropTypeArgument;
+
+      case JsBuiltin.rawRtiToJsConstructorName:
+        return js.js.expressionTemplateFor("#.name");
+
+      case JsBuiltin.rawRuntimeType:
+        return js.js.expressionTemplateFor("#.constructor");
+
+      case JsBuiltin.isSubtype:
+        // TODO(floitsch): move this closer to where is-check properties are
+        // built.
+        String isPrefix = _namer.fixedNames.operatorIsPrefix;
+        return js.js.expressionTemplateFor("('$isPrefix' + #) in #.prototype");
+
+      case JsBuiltin.isGivenTypeRti:
+        return js.js.expressionTemplateFor('#.name === #');
+
+      case JsBuiltin.getMetadata:
+        String metadataAccess =
+            _emitter.generateEmbeddedGlobalAccessString(METADATA);
+        return js.js.expressionTemplateFor("$metadataAccess[#]");
+
+      case JsBuiltin.getType:
+        String typesAccess = _emitter.generateEmbeddedGlobalAccessString(TYPES);
+        return js.js.expressionTemplateFor("$typesAccess[#]");
+
+      default:
+        reporter.internalError(
+            NO_LOCATION_SPANNABLE, "Unhandled Builtin: $builtin");
+        return null;
+    }
+  }
+
   void _handleForeignJsGetFlag(ir.StaticInvocation invocation) {
     if (_unexpectedForeignArguments(invocation,
         minPositional: 1, maxPositional: 1)) {
diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart
index 45aadc0..f4b339c 100644
--- a/pkg/compiler/lib/src/ssa/codegen.dart
+++ b/pkg/compiler/lib/src/ssa/codegen.dart
@@ -633,14 +633,15 @@
           op == '^' ||
           op == '&' ||
           op == '|') {
-        if (binary.left is js.VariableUse &&
-            (binary.left as js.VariableUse).name == variableName) {
+        js.Expression left = binary.left;
+        if (left is js.VariableUse && left.name == variableName) {
           // We know now, that we can shorten x = x + y into x += y.
           // Also check for the shortcut where y equals 1: x++ and x--.
+          js.Expression right = binary.right;
           if ((op == '+' || op == '-') &&
-              binary.right is js.LiteralNumber &&
-              (binary.right as js.LiteralNumber).value == "1") {
-            return new js.Prefix(op == '+' ? '++' : '--', binary.left);
+              right is js.LiteralNumber &&
+              right.value == "1") {
+            return new js.Prefix(op == '+' ? '++' : '--', left);
           }
           return new js.Assignment.compound(binary.left, op, binary.right);
         }
@@ -1821,7 +1822,7 @@
       assert(node.inputs.length == 1);
       _registry.registerSpecializedGetInterceptor(node.interceptedClasses);
       js.Name name = _namer.nameForGetInterceptor(node.interceptedClasses);
-      js.VariableUse isolate = _namer
+      js.Expression isolate = _namer
           .readGlobalObjectForLibrary(_commonElements.interceptorsLibrary);
       use(node.receiver);
       List<js.Expression> arguments = <js.Expression>[pop()];
@@ -1913,7 +1914,7 @@
   @override
   void visitOneShotInterceptor(HOneShotInterceptor node) {
     List<js.Expression> arguments = visitArguments(node.inputs);
-    js.VariableUse isolate =
+    js.Expression isolate =
         _namer.readGlobalObjectForLibrary(_commonElements.interceptorsLibrary);
     Selector selector = node.selector;
     js.Name methodName = _oneShotInterceptorData.registerOneShotInterceptor(
diff --git a/pkg/compiler/lib/src/ssa/ssa.dart b/pkg/compiler/lib/src/ssa/ssa.dart
index 4164617..59f4ff7 100644
--- a/pkg/compiler/lib/src/ssa/ssa.dart
+++ b/pkg/compiler/lib/src/ssa/ssa.dart
@@ -63,7 +63,7 @@
     HGraph graph =
         _builder.build(member, closedWorld, globalInferenceResults, registry);
     if (graph == null) {
-      return new CodegenResult(null, registry.worldImpact);
+      return registry.close(null);
     }
     optimizer.optimize(
         member, graph, codegen, closedWorld, globalInferenceResults, registry);
@@ -88,7 +88,7 @@
           sourceInformationStrategy.buildSourceMappedMarker());
     }
 
-    return new CodegenResult(result, registry.worldImpact);
+    return registry.close(result);
   }
 
   js.Expression _rewriteAsync(
diff --git a/tests/compiler/dart2js/deferred/emit_type_checks_test.dart b/tests/compiler/dart2js/deferred/emit_type_checks_test.dart
index 932e4df..2aea089 100644
--- a/tests/compiler/dart2js/deferred/emit_type_checks_test.dart
+++ b/tests/compiler/dart2js/deferred/emit_type_checks_test.dart
@@ -22,7 +22,7 @@
     String mainOutput = collector.getOutput('', OutputType.js);
     String deferredOutput = collector.getOutput('out_1', OutputType.jsPart);
     JavaScriptBackend backend = compiler.backend;
-    String isPrefix = backend.namerForTesting.operatorIsPrefix;
+    String isPrefix = backend.namerForTesting.fixedNames.operatorIsPrefix;
     Expect.isTrue(
         deferredOutput.contains('${isPrefix}A: 1'),
         "Deferred output doesn't contain '${isPrefix}A: 1':\n"
diff --git a/tests/compiler/dart2js/rti/factory_call_test.dart b/tests/compiler/dart2js/rti/factory_call_test.dart
index 5b28378..392c989 100644
--- a/tests/compiler/dart2js/rti/factory_call_test.dart
+++ b/tests/compiler/dart2js/rti/factory_call_test.dart
@@ -81,14 +81,17 @@
       js.Name selector = getName(targetName);
       bool callFound = false;
       forEachNode(fun, onCall: (js.Call node) {
-        js.Expression target = node.target;
-        if (target is js.PropertyAccess && target.selector == selector) {
-          callFound = true;
-          Expect.equals(
-              expectedTypeArguments,
-              node.arguments.length,
-              "Unexpected argument count in $function call to $targetName: "
-              "${js.nodeToString(fun)}");
+        js.Expression target = js.undefer(node.target);
+        if (target is js.PropertyAccess) {
+          js.Node targetSelector = js.undefer(target.selector);
+          if (targetSelector is js.Name && targetSelector.key == selector.key) {
+            callFound = true;
+            Expect.equals(
+                expectedTypeArguments,
+                node.arguments.length,
+                "Unexpected argument count in $function call to $targetName: "
+                "${js.nodeToString(fun)}");
+          }
         }
       });
       Expect.isTrue(
diff --git a/tests/compiler/dart2js/rti/instance_call_test.dart b/tests/compiler/dart2js/rti/instance_call_test.dart
index baff537..cdbbf3e 100644
--- a/tests/compiler/dart2js/rti/instance_call_test.dart
+++ b/tests/compiler/dart2js/rti/instance_call_test.dart
@@ -151,14 +151,17 @@
       js.Name selector = getName(targetName, expectedTypeArguments);
       bool callFound = false;
       forEachNode(fun, onCall: (js.Call node) {
-        js.Expression target = node.target;
-        if (target is js.PropertyAccess && target.selector == selector) {
-          callFound = true;
-          Expect.equals(
-              1 + expectedTypeArguments,
-              node.arguments.length,
-              "Unexpected argument count in $function call to $targetName: "
-              "${js.nodeToString(fun)}");
+        js.Expression target = js.undefer(node.target);
+        if (target is js.PropertyAccess) {
+          js.Node targetSelector = js.undefer(target.selector);
+          if (targetSelector is js.Name && targetSelector.key == selector.key) {
+            callFound = true;
+            Expect.equals(
+                1 + expectedTypeArguments,
+                node.arguments.length,
+                "Unexpected argument count in $function call to $targetName: "
+                "${js.nodeToString(fun)}");
+          }
         }
       });
       Expect.isTrue(callFound,