Decouple Namer from RuntimeTypesEncoder and Tracer

Change-Id: I1c52b6740b590b1089201c3d0d8286d8e0912c48
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/101824
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/compiler/lib/src/js_backend/backend.dart b/pkg/compiler/lib/src/js_backend/backend.dart
index 23d1f20..c05b885 100644
--- a/pkg/compiler/lib/src/js_backend/backend.dart
+++ b/pkg/compiler/lib/src/js_backend/backend.dart
@@ -313,11 +313,7 @@
 
   Namer _namer;
 
-  Namer get namer {
-    assert(_namer != null,
-        failedAt(NO_LOCATION_SPANNABLE, "Namer has not been created yet."));
-    return _namer;
-  }
+  Namer get namerForTesting => _namer;
 
   /// Set of classes whose `operator ==` methods handle `null` themselves.
   final Set<ClassEntity> specialOperatorEqClasses = new Set<ClassEntity>();
@@ -431,12 +427,12 @@
     return _rtiEncoder;
   }
 
-  Namer determineNamer(JClosedWorld closedWorld) {
+  Namer determineNamer(JClosedWorld closedWorld, RuntimeTypeTags rtiTags) {
     return compiler.options.enableMinification
         ? compiler.options.useFrequencyNamer
-            ? new FrequencyBasedNamer(closedWorld)
-            : new MinifyNamer(closedWorld)
-        : new Namer(closedWorld);
+            ? new FrequencyBasedNamer(closedWorld, rtiTags)
+            : new MinifyNamer(closedWorld, rtiTags)
+        : new Namer(closedWorld, rtiTags);
   }
 
   void validateInterceptorImplementsAllObjectMethods(
@@ -639,7 +635,7 @@
   int assembleProgram(JClosedWorld closedWorld, InferredData inferredData,
       CodegenInputs codegen, CodegenWorld codegenWorld) {
     int programSize = emitterTask.assembleProgram(
-        namer, closedWorld, inferredData, codegen, codegenWorld);
+        _namer, closedWorld, inferredData, codegen, codegenWorld);
     closedWorld.noSuchMethodData.emitDiagnostic(reporter);
     return programSize;
   }
@@ -662,12 +658,12 @@
   /// Called when the compiler starts running the codegen enqueuer. The
   /// [WorldImpact] of enabled backend features is returned.
   CodegenInputs onCodegenStart(JClosedWorld closedWorld) {
-    _namer = determineNamer(closedWorld);
+    RuntimeTypeTags rtiTags = const RuntimeTypeTags();
     OneShotInterceptorData oneShotInterceptorData = new OneShotInterceptorData(
         closedWorld.interceptorData, closedWorld.commonElements);
-    Tracer tracer = new Tracer(closedWorld, namer, compiler.outputProvider);
-    _rtiEncoder = _namer.rtiEncoder = new RuntimeTypesEncoderImpl(
-        namer,
+    Tracer tracer = new Tracer(closedWorld, compiler.outputProvider);
+    _rtiEncoder = new RuntimeTypesEncoderImpl(
+        rtiTags,
         closedWorld.nativeData,
         closedWorld.elementEnvironment,
         closedWorld.commonElements,
@@ -680,7 +676,8 @@
         emitterTask,
         closedWorld.liveNativeClasses,
         closedWorld.nativeData);
-    emitterTask.createEmitter(namer, closedWorld);
+    _namer = determineNamer(closedWorld, rtiTags);
+    emitterTask.createEmitter(_namer, closedWorld);
     // TODO(johnniwinther): Share the impact object created in
     // createCodegenEnqueuer.
     BackendImpacts impacts = new BackendImpacts(closedWorld.commonElements);
@@ -704,13 +701,13 @@
         closedWorld.backendUsage,
         closedWorld.rtiNeed,
         nativeCodegenEnqueuer,
-        namer,
+        _namer,
         oneShotInterceptorData,
         rtiChecksBuilder,
         emitterTask.nativeEmitter);
 
     CodegenInputs codegen = new CodegenInputsImpl(emitterTask.emitter,
-        oneShotInterceptorData, rtiSubstitutions, rtiEncoder, namer, tracer);
+        oneShotInterceptorData, rtiSubstitutions, rtiEncoder, _namer, tracer);
 
     functionCompiler.onCodegenStart(codegen);
     return codegen;
diff --git a/pkg/compiler/lib/src/js_backend/frequency_namer.dart b/pkg/compiler/lib/src/js_backend/frequency_namer.dart
index 7bb0830..de45286 100644
--- a/pkg/compiler/lib/src/js_backend/frequency_namer.dart
+++ b/pkg/compiler/lib/src/js_backend/frequency_namer.dart
@@ -47,7 +47,8 @@
   jsAst.Name get staticsPropertyName =>
       _staticsPropertyName ??= getFreshName(instanceScope, 'static');
 
-  FrequencyBasedNamer(JClosedWorld closedWorld) : super(closedWorld) {
+  FrequencyBasedNamer(JClosedWorld closedWorld, RuntimeTypeTags rtiTags)
+      : super(closedWorld, rtiTags) {
     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 7ae3818..b7751a1 100644
--- a/pkg/compiler/lib/src/js_backend/minify_namer.dart
+++ b/pkg/compiler/lib/src/js_backend/minify_namer.dart
@@ -10,7 +10,8 @@
         _MinifiedFieldNamer,
         _MinifyConstructorBodyNamer,
         _MinifiedOneShotInterceptorNamer {
-  MinifyNamer(JClosedWorld closedWorld) : super(closedWorld) {
+  MinifyNamer(JClosedWorld closedWorld, RuntimeTypeTags rtiTags)
+      : super(closedWorld, rtiTags) {
     reserveBackendNames();
     fieldRegistry = new _FieldNamingRegistry(this);
   }
@@ -412,7 +413,8 @@
     String callSuffix = selector.isCall
         ? Namer.callSuffixForStructure(selector.callStructure).join()
         : "";
-    String suffix = suffixForGetInterceptor(classes);
+    String suffix =
+        suffixForGetInterceptor(_commonElements, _nativeData, classes);
     String fullName = "\$intercepted$prefix\$$root$callSuffix\$$suffix";
     return _disambiguateInternalGlobal(fullName);
   }
diff --git a/pkg/compiler/lib/src/js_backend/namer.dart b/pkg/compiler/lib/src/js_backend/namer.dart
index 138e4cd..dbbe8b7 100644
--- a/pkg/compiler/lib/src/js_backend/namer.dart
+++ b/pkg/compiler/lib/src/js_backend/namer.dart
@@ -26,6 +26,7 @@
 import '../elements/types.dart';
 import '../js/js.dart' as jsAst;
 import '../js_backend/field_analysis.dart';
+import '../js_backend/runtime_types.dart' show RuntimeTypeTags;
 import '../js_model/closure.dart';
 import '../js_model/elements.dart' show JGeneratorBody;
 import '../universe/call_structure.dart' show CallStructure;
@@ -34,7 +35,6 @@
 import '../world.dart' show JClosedWorld;
 import 'backend.dart';
 import 'native_data.dart';
-import 'runtime_types.dart';
 
 part 'field_naming_mixin.dart';
 part 'frequency_namer.dart';
@@ -459,6 +459,8 @@
 
   final String classDescriptorProperty = r'^';
 
+  final RuntimeTypeTags rtiTags;
+
   /// The non-minifying namer's [callPrefix] with a dollar after it.
   static const String _callPrefixDollar = r'call$';
 
@@ -491,21 +493,6 @@
 
   final JClosedWorld _closedWorld;
 
-  RuntimeTypesEncoder _rtiEncoder;
-  RuntimeTypesEncoder get rtiEncoder {
-    assert(_rtiEncoder != null,
-        failedAt(NO_LOCATION_SPANNABLE, "Namer.rtiEncoder has not been set."));
-    return _rtiEncoder;
-  }
-
-  void set rtiEncoder(RuntimeTypesEncoder value) {
-    assert(
-        _rtiEncoder == null,
-        failedAt(
-            NO_LOCATION_SPANNABLE, "Namer.rtiEncoder has already been set."));
-    _rtiEncoder = value;
-  }
-
   /// Used disambiguated names in the global namespace, issued by
   /// [_disambiguateGlobal], and [_disambiguateInternalGlobal].
   ///
@@ -595,7 +582,7 @@
   /// key into maps.
   final Map<LibraryEntity, String> _libraryKeys = HashMap();
 
-  Namer(this._closedWorld) {
+  Namer(this._closedWorld, this.rtiTags) {
     _literalGetterPrefix = new StringBackedName(getterPrefix);
     _literalSetterPrefix = new StringBackedName(setterPrefix);
   }
@@ -667,25 +654,25 @@
       case JsGetName.RTI_NAME:
         return asName(rtiName);
       case JsGetName.TYPEDEF_TAG:
-        return asName(typedefTag);
+        return asName(rtiTags.typedefTag);
       case JsGetName.FUNCTION_TYPE_TAG:
-        return asName(functionTypeTag);
+        return asName(rtiTags.functionTypeTag);
       case JsGetName.FUNCTION_TYPE_GENERIC_BOUNDS_TAG:
-        return asName(functionTypeGenericBoundsTag);
+        return asName(rtiTags.functionTypeGenericBoundsTag);
       case JsGetName.FUNCTION_TYPE_VOID_RETURN_TAG:
-        return asName(functionTypeVoidReturnTag);
+        return asName(rtiTags.functionTypeVoidReturnTag);
       case JsGetName.FUNCTION_TYPE_RETURN_TYPE_TAG:
-        return asName(functionTypeReturnTypeTag);
+        return asName(rtiTags.functionTypeReturnTypeTag);
       case JsGetName.FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG:
-        return asName(functionTypeRequiredParametersTag);
+        return asName(rtiTags.functionTypeRequiredParametersTag);
       case JsGetName.FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG:
-        return asName(functionTypeOptionalParametersTag);
+        return asName(rtiTags.functionTypeOptionalParametersTag);
       case JsGetName.FUNCTION_TYPE_NAMED_PARAMETERS_TAG:
-        return asName(functionTypeNamedParametersTag);
+        return asName(rtiTags.functionTypeNamedParametersTag);
       case JsGetName.FUTURE_OR_TAG:
-        return asName(futureOrTag);
+        return asName(rtiTags.futureOrTag);
       case JsGetName.FUTURE_OR_TYPE_ARGUMENT_TAG:
-        return asName(futureOrTypeTag);
+        return asName(rtiTags.futureOrTypeTag);
       case JsGetName.IS_INDEXABLE_FIELD_NAME:
         return operatorIs(_commonElements.jsIndexingBehaviorInterface);
       case JsGetName.NULL_CLASS_TYPE_NAME:
@@ -730,10 +717,9 @@
   String constantLongName(ConstantValue constant) {
     String longName = _constantLongNames[constant];
     if (longName == null) {
-      _constantHasher ??= new ConstantCanonicalHasher(rtiEncoder, _closedWorld);
-      longName =
-          new ConstantNamingVisitor(rtiEncoder, _closedWorld, _constantHasher)
-              .getName(constant);
+      _constantHasher ??= new ConstantCanonicalHasher(this, _closedWorld);
+      longName = new ConstantNamingVisitor(this, _closedWorld, _constantHasher)
+          .getName(constant);
       _constantLongNames[constant] = longName;
     }
     return longName;
@@ -1407,34 +1393,6 @@
     return disambiguated;
   }
 
-  String suffixForGetInterceptor(Iterable<ClassEntity> classes) {
-    String abbreviate(ClassEntity cls) {
-      if (cls == _commonElements.objectClass) return "o";
-      if (cls == _commonElements.jsStringClass) return "s";
-      if (cls == _commonElements.jsArrayClass) return "a";
-      if (cls == _commonElements.jsDoubleClass) return "d";
-      if (cls == _commonElements.jsIntClass) return "i";
-      if (cls == _commonElements.jsNumberClass) return "n";
-      if (cls == _commonElements.jsNullClass) return "u";
-      if (cls == _commonElements.jsBoolClass) return "b";
-      if (cls == _commonElements.jsInterceptorClass) return "I";
-      return cls.name;
-    }
-
-    List<String> names = classes
-        .where((cls) => !_nativeData.isNativeOrExtendsNative(cls))
-        .map(abbreviate)
-        .toList();
-    // There is one dispatch mechanism for all native classes.
-    if (classes.any((cls) => _nativeData.isNativeOrExtendsNative(cls))) {
-      names.add("x");
-    }
-    // Sort the names of the classes after abbreviating them to ensure
-    // the suffix is stable and predictable for the suggested names.
-    names.sort();
-    return names.join();
-  }
-
   String _getSuffixForInterceptedClasses(Iterable<ClassEntity> classes) {
     if (classes.isEmpty) {
       // TODO(johnniwinther,sra): If [classes] is empty it should either have
@@ -1452,7 +1410,7 @@
       // set of classes for most general variant, e.g. "$lt$n" could be "$lt".
       return '';
     } else {
-      return suffixForGetInterceptor(classes);
+      return suffixForGetInterceptor(_commonElements, _nativeData, classes);
     }
   }
 
@@ -1496,10 +1454,7 @@
     return _disambiguateGlobalType(element);
   }
 
-  /// Returns the disambiguated name of [class_].
-  ///
-  /// This is both the *runtime type* of the class (see [runtimeTypeName])
-  /// and a global property name in which to store its JS constructor.
+  @override
   jsAst.Name className(ClassEntity class_) => _disambiguateGlobalType(class_);
 
   @override
@@ -1622,26 +1577,6 @@
 
   String get genericInstantiationPrefix => r'$instantiate';
 
-  String get typedefTag => r'typedef';
-
-  String get functionTypeTag => r'func';
-
-  String get functionTypeVoidReturnTag => r'v';
-
-  String get functionTypeReturnTypeTag => r'ret';
-
-  String get functionTypeRequiredParametersTag => r'args';
-
-  String get functionTypeOptionalParametersTag => r'opt';
-
-  String get functionTypeNamedParametersTag => r'named';
-
-  String get functionTypeGenericBoundsTag => r'bounds';
-
-  String get futureOrTag => r'futureOr';
-
-  String get futureOrTypeTag => r'type';
-
   // The name of the variable used to offset function signatures in deferred
   // parts with the fast-startup emitter.
   String get typesOffsetName => r'typesOffset';
@@ -1652,7 +1587,7 @@
 
   jsAst.Name getFunctionTypeName(FunctionType functionType) {
     return functionTypeNameMap.putIfAbsent(functionType, () {
-      _functionTypeNamer ??= new FunctionTypeNamer(rtiEncoder);
+      _functionTypeNamer ??= new FunctionTypeNamer();
       String proposedName = _functionTypeNamer.computeName(functionType);
       return getFreshName(instanceScope, proposedName);
     });
@@ -1767,6 +1702,61 @@
       return name;
     }
   }
+
+  String getTypeRepresentationForTypeConstant(DartType type) {
+    if (type.isDynamic) return "dynamic";
+    if (type is TypedefType) {
+      return uniqueNameForTypeConstantElement(
+          type.element.library, type.element);
+    }
+    if (type is FunctionType) {
+      // TODO(johnniwinther): Add naming scheme for function type literals.
+      // These currently only occur from kernel.
+      return '()->';
+    }
+    InterfaceType interface = type;
+    String name = uniqueNameForTypeConstantElement(
+        interface.element.library, interface.element);
+
+    // Type constants can currently only be raw types, so there is no point
+    // adding ground-term type parameters, as they would just be 'dynamic'.
+    // TODO(sra): Since the result string is used only in constructing constant
+    // names, it would result in more readable names if the final string was a
+    // legal JavaScript identifier.
+    if (interface.typeArguments.isEmpty) return name;
+    String arguments =
+        new List.filled(interface.typeArguments.length, 'dynamic').join(', ');
+    return '$name<$arguments>';
+  }
+}
+
+String suffixForGetInterceptor(CommonElements commonElements,
+    NativeData nativeData, Iterable<ClassEntity> classes) {
+  String abbreviate(ClassEntity cls) {
+    if (cls == commonElements.objectClass) return "o";
+    if (cls == commonElements.jsStringClass) return "s";
+    if (cls == commonElements.jsArrayClass) return "a";
+    if (cls == commonElements.jsDoubleClass) return "d";
+    if (cls == commonElements.jsIntClass) return "i";
+    if (cls == commonElements.jsNumberClass) return "n";
+    if (cls == commonElements.jsNullClass) return "u";
+    if (cls == commonElements.jsBoolClass) return "b";
+    if (cls == commonElements.jsInterceptorClass) return "I";
+    return cls.name;
+  }
+
+  List<String> names = classes
+      .where((cls) => !nativeData.isNativeOrExtendsNative(cls))
+      .map(abbreviate)
+      .toList();
+  // There is one dispatch mechanism for all native classes.
+  if (classes.any((cls) => nativeData.isNativeOrExtendsNative(cls))) {
+    names.add("x");
+  }
+  // Sort the names of the classes after abbreviating them to ensure
+  // the suffix is stable and predictable for the suggested names.
+  names.sort();
+  return names.join();
 }
 
 /// Generator of names for [ConstantValue] values.
@@ -1788,7 +1778,7 @@
   static const MAX_EXTRA_LENGTH = 30;
   static const DEFAULT_TAG_LENGTH = 3;
 
-  final RuntimeTypesEncoder _rtiEncoder;
+  final Namer _namer;
   final JClosedWorld _closedWorld;
   final ConstantCanonicalHasher _hasher;
 
@@ -1797,7 +1787,7 @@
   List<String> fragments = <String>[];
   int length = 0;
 
-  ConstantNamingVisitor(this._rtiEncoder, this._closedWorld, this._hasher);
+  ConstantNamingVisitor(this._namer, this._closedWorld, this._hasher);
 
   JElementEnvironment get _elementEnvironment =>
       _closedWorld.elementEnvironment;
@@ -1992,7 +1982,7 @@
     }
     if (name == null) {
       // e.g. DartType 'dynamic' has no element.
-      name = _rtiEncoder.getTypeRepresentationForTypeConstant(type);
+      name = _namer.getTypeRepresentationForTypeConstant(type);
     }
     addIdentifier(name);
     add(getHashTag(constant, 3));
@@ -2039,11 +2029,11 @@
   static const _MASK = 0x1fffffff;
   static const _UINT32_LIMIT = 4 * 1024 * 1024 * 1024;
 
-  final RuntimeTypesEncoder _rtiEncoder;
+  final Namer _namer;
   final JClosedWorld _closedWorld;
   final Map<ConstantValue, int> _hashes = {};
 
-  ConstantCanonicalHasher(this._rtiEncoder, this._closedWorld);
+  ConstantCanonicalHasher(this._namer, this._closedWorld);
 
   JElementEnvironment get _elementEnvironment =>
       _closedWorld.elementEnvironment;
@@ -2131,7 +2121,7 @@
   int visitType(TypeConstantValue constant, [_]) {
     DartType type = constant.representedType;
     // This name includes the library name and type parameters.
-    String name = _rtiEncoder.getTypeRepresentationForTypeConstant(type);
+    String name = _namer.getTypeRepresentationForTypeConstant(type);
     return _hashString(4, name);
   }
 
@@ -2233,10 +2223,9 @@
 }
 
 class FunctionTypeNamer extends BaseDartTypeVisitor {
-  final RuntimeTypesEncoder rtiEncoder;
   StringBuffer sb;
 
-  FunctionTypeNamer(this.rtiEncoder);
+  FunctionTypeNamer();
 
   String computeName(DartType type) {
     sb = new StringBuffer();
@@ -2267,9 +2256,19 @@
     sb.write(type.element.name);
   }
 
+  bool _isSimpleFunctionType(FunctionType type) {
+    if (!type.returnType.isDynamic) return false;
+    if (!type.optionalParameterTypes.isEmpty) return false;
+    if (!type.namedParameterTypes.isEmpty) return false;
+    for (DartType parameter in type.parameterTypes) {
+      if (!parameter.isDynamic) return false;
+    }
+    return true;
+  }
+
   @override
   visitFunctionType(FunctionType type, _) {
-    if (rtiEncoder.isSimpleFunctionType(type)) {
+    if (_isSimpleFunctionType(type)) {
       sb.write('args${type.parameterTypes.length}');
       return;
     }
@@ -2458,6 +2457,12 @@
   /// Returns the name of the closure of the static method [element].
   jsAst.Name staticClosureName(FunctionEntity element);
 
+  /// Returns the disambiguated name of [class_].
+  ///
+  /// This is both the *runtime type* of the class (see [runtimeTypeName])
+  /// and a global property name in which to store its JS constructor.
+  jsAst.Name className(ClassEntity class_);
+
   /// The prefix used for encoding async properties.
   final String asyncPrefix = r"$async$";
 
@@ -2526,6 +2531,11 @@
   }
 
   @override
+  jsAst.Name className(ClassEntity element) {
+    return new ModularName(ModularNameKind.className, element);
+  }
+
+  @override
   jsAst.Expression readGlobalObjectForLibrary(LibraryEntity library) {
     return new ModularVariableUse(
         ModularVariableUseKind.globalObjectForLibrary, library);
@@ -2628,6 +2638,7 @@
 enum ModularNameKind {
   rtiField,
   runtimeTypeName,
+  className,
   aliasedSuperMember,
   staticClosure,
   methodProperty,
diff --git a/pkg/compiler/lib/src/js_backend/runtime_types.dart b/pkg/compiler/lib/src/js_backend/runtime_types.dart
index 87c984e..b4893ae5 100644
--- a/pkg/compiler/lib/src/js_backend/runtime_types.dart
+++ b/pkg/compiler/lib/src/js_backend/runtime_types.dart
@@ -18,7 +18,7 @@
 import '../ir/runtime_type_analysis.dart';
 import '../js/js.dart' as jsAst;
 import '../js/js.dart' show js;
-import '../js_emitter/js_emitter.dart' show Emitter;
+import '../js_emitter/js_emitter.dart' show ModularEmitter;
 import '../options.dart';
 import '../serialization/serialization.dart';
 import '../universe/class_hierarchy.dart';
@@ -656,15 +656,8 @@
 }
 
 abstract class RuntimeTypesEncoder {
-  bool isSimpleFunctionType(FunctionType type);
-
-  jsAst.Expression getSignatureEncoding(
-      Emitter emitter, DartType type, jsAst.Expression this_);
-
-  jsAst.Expression getSubstitutionRepresentation(
-      Emitter emitter, List<DartType> types, OnVariableCallback onVariable);
-  jsAst.Expression getSubstitutionCode(
-      Emitter emitter, Substitution substitution);
+  jsAst.Expression getSignatureEncoding(ModularNamer namer,
+      ModularEmitter emitter, DartType type, jsAst.Expression this_);
 
   /// Returns the JavaScript template to determine at runtime if a type object
   /// is a function type.
@@ -686,15 +679,13 @@
   /// is a type argument of js-interop class.
   jsAst.Template get templateForIsJsInteropTypeArgument;
 
-  jsAst.Name get getFunctionThatReturnsNullName;
-
   /// Returns a [jsAst.Expression] representing the given [type]. Type variables
   /// are replaced by the [jsAst.Expression] returned by [onVariable].
   jsAst.Expression getTypeRepresentation(
-      Emitter emitter, DartType type, OnVariableCallback onVariable,
+      ModularEmitter emitter, DartType type, OnVariableCallback onVariable,
       [ShouldEncodeTypedefCallback shouldEncodeTypedef]);
 
-  String getTypeRepresentationForTypeConstant(DartType type);
+  jsAst.Expression getJsInteropTypeArguments(int count);
 }
 
 /// Common functionality for [_RuntimeTypesNeedBuilder] and [_RuntimeTypes].
@@ -2206,27 +2197,15 @@
 }
 
 class RuntimeTypesEncoderImpl implements RuntimeTypesEncoder {
-  final Namer namer;
   final ElementEnvironment _elementEnvironment;
   final CommonElements commonElements;
   final TypeRepresentationGenerator _representationGenerator;
   final RuntimeTypesNeed _rtiNeed;
 
-  RuntimeTypesEncoderImpl(this.namer, NativeBasicData nativeData,
+  RuntimeTypesEncoderImpl(RuntimeTypeTags rtiTags, NativeBasicData nativeData,
       this._elementEnvironment, this.commonElements, this._rtiNeed)
       : _representationGenerator =
-            new TypeRepresentationGenerator(namer, nativeData);
-
-  @override
-  bool isSimpleFunctionType(FunctionType type) {
-    if (!type.returnType.isDynamic) return false;
-    if (!type.optionalParameterTypes.isEmpty) return false;
-    if (!type.namedParameterTypes.isEmpty) return false;
-    for (DartType parameter in type.parameterTypes) {
-      if (!parameter.isDynamic) return false;
-    }
-    return true;
-  }
+            new TypeRepresentationGenerator(rtiTags, nativeData);
 
   /// Returns the JavaScript template to determine at runtime if a type object
   /// is a function type.
@@ -2263,28 +2242,18 @@
 
   @override
   jsAst.Expression getTypeRepresentation(
-      Emitter emitter, DartType type, OnVariableCallback onVariable,
+      ModularEmitter emitter, DartType type, OnVariableCallback onVariable,
       [ShouldEncodeTypedefCallback shouldEncodeTypedef]) {
     return _representationGenerator.getTypeRepresentation(
         emitter, type, onVariable, shouldEncodeTypedef);
   }
 
-  @override
-  jsAst.Expression getSubstitutionRepresentation(
-      Emitter emitter, List<DartType> types, OnVariableCallback onVariable) {
-    List<jsAst.Expression> elements = types
-        .map(
-            (DartType type) => getTypeRepresentation(emitter, type, onVariable))
-        .toList(growable: false);
-    return new jsAst.ArrayInitializer(elements);
-  }
-
   String getTypeVariableName(TypeVariableType type) {
     String name = type.element.name;
     return name.replaceAll('#', '_');
   }
 
-  jsAst.Expression getTypeEncoding(Emitter emitter, DartType type,
+  jsAst.Expression getTypeEncoding(ModularEmitter emitter, DartType type,
       {bool alwaysGenerateFunction: false}) {
     ClassEntity contextClass = DartTypes.getClassContext(type);
     jsAst.Expression onVariable(TypeVariableType v) {
@@ -2311,8 +2280,8 @@
   }
 
   @override
-  jsAst.Expression getSignatureEncoding(
-      Emitter emitter, DartType type, jsAst.Expression this_) {
+  jsAst.Expression getSignatureEncoding(ModularNamer namer,
+      ModularEmitter emitter, DartType type, jsAst.Expression this_) {
     ClassEntity contextClass = DartTypes.getClassContext(type);
     jsAst.Expression encoding =
         getTypeEncoding(emitter, type, alwaysGenerateFunction: true);
@@ -2338,100 +2307,41 @@
     }
   }
 
-  /// Compute a JavaScript expression that describes the necessary substitution
-  /// for type arguments in a subtype test.
-  ///
-  /// The result can be:
-  ///  1) `null`, if no substituted check is necessary, because the type
-  ///     variables are the same or there are no type variables in the class
-  ///     that is checked for.
-  ///  2) A list expression describing the type arguments to be used in the
-  ///     subtype check, if the type arguments to be used in the check do not
-  ///     depend on the type arguments of the object.
-  ///  3) A function mapping the type variables of the object to be checked to
-  ///     a list expression.
   @override
-  jsAst.Expression getSubstitutionCode(
-      Emitter emitter, Substitution substitution) {
-    if (substitution.isTrivial) {
-      return new jsAst.LiteralNull();
-    }
-
-    if (substitution.isJsInterop) {
-      return js(
-          'function() { return # }',
-          _representationGenerator
-              .getJsInteropTypeArguments(substitution.length));
-    }
-
-    jsAst.Expression declaration(TypeVariableType variable) {
-      return new jsAst.Parameter(getVariableName(variable.element.name));
-    }
-
-    jsAst.Expression use(TypeVariableType variable) {
-      return new jsAst.VariableUse(getVariableName(variable.element.name));
-    }
-
-    if (substitution.arguments.every((DartType type) => type.isDynamic)) {
-      return emitter.generateFunctionThatReturnsNull();
-    } else {
-      jsAst.Expression value =
-          getSubstitutionRepresentation(emitter, substitution.arguments, use);
-      if (substitution.isFunction) {
-        Iterable<jsAst.Expression> formals =
-            // TODO(johnniwinther): Pass [declaration] directly to `map` when
-            // `substitution.parameters` can no longer be a
-            // `List<ResolutionDartType>`.
-            substitution.parameters.map((type) => declaration(type));
-        return js('function(#) { return # }', [formals, value]);
-      } else {
-        return js('function() { return # }', value);
-      }
-    }
-  }
-
-  String getVariableName(String name) {
-    // Kernel type variable names for anonymous mixin applications have names
-    // canonicalized to a non-identified, e.g. '#U0'.
-    name = name.replaceAll('#', '_');
-    return namer.safeVariableName(name);
-  }
-
-  @override
-  jsAst.Name get getFunctionThatReturnsNullName =>
-      namer.internalGlobal('functionThatReturnsNull');
-
-  @override
-  String getTypeRepresentationForTypeConstant(DartType type) {
-    if (type.isDynamic) return "dynamic";
-    if (type is TypedefType) {
-      return namer.uniqueNameForTypeConstantElement(
-          type.element.library, type.element);
-    }
-    if (type is FunctionType) {
-      // TODO(johnniwinther): Add naming scheme for function type literals.
-      // These currently only occur from kernel.
-      return '()->';
-    }
-    InterfaceType interface = type;
-    String name = namer.uniqueNameForTypeConstantElement(
-        interface.element.library, interface.element);
-
-    // Type constants can currently only be raw types, so there is no point
-    // adding ground-term type parameters, as they would just be 'dynamic'.
-    // TODO(sra): Since the result string is used only in constructing constant
-    // names, it would result in more readable names if the final string was a
-    // legal JavaScript identifier.
-    if (interface.typeArguments.isEmpty) return name;
-    String arguments =
-        new List.filled(interface.typeArguments.length, 'dynamic').join(', ');
-    return '$name<$arguments>';
+  jsAst.Expression getJsInteropTypeArguments(int count) {
+    return _representationGenerator.getJsInteropTypeArguments(count);
   }
 }
 
+/// Fixed strings used for runtime types encoding.
+// TODO(johnniwinther): Use different names for minified code?
+class RuntimeTypeTags {
+  const RuntimeTypeTags();
+
+  String get typedefTag => r'typedef';
+
+  String get functionTypeTag => r'func';
+
+  String get functionTypeVoidReturnTag => r'v';
+
+  String get functionTypeReturnTypeTag => r'ret';
+
+  String get functionTypeRequiredParametersTag => r'args';
+
+  String get functionTypeOptionalParametersTag => r'opt';
+
+  String get functionTypeNamedParametersTag => r'named';
+
+  String get functionTypeGenericBoundsTag => r'bounds';
+
+  String get futureOrTag => r'futureOr';
+
+  String get futureOrTypeTag => r'type';
+}
+
 class TypeRepresentationGenerator
-    implements DartTypeVisitor<jsAst.Expression, Emitter> {
-  final Namer namer;
+    implements DartTypeVisitor<jsAst.Expression, ModularEmitter> {
+  final RuntimeTypeTags _rtiTags;
   final NativeBasicData _nativeData;
 
   OnVariableCallback onVariable;
@@ -2439,12 +2349,12 @@
   Map<TypeVariableType, jsAst.Expression> typedefBindings;
   List<FunctionTypeVariable> functionTypeVariables = <FunctionTypeVariable>[];
 
-  TypeRepresentationGenerator(this.namer, this._nativeData);
+  TypeRepresentationGenerator(this._rtiTags, this._nativeData);
 
   /// Creates a type representation for [type]. [onVariable] is called to
   /// provide the type representation for type variables.
   jsAst.Expression getTypeRepresentation(
-      Emitter emitter,
+      ModularEmitter emitter,
       DartType type,
       OnVariableCallback onVariable,
       ShouldEncodeTypedefCallback encodeTypedef) {
@@ -2459,7 +2369,8 @@
     return representation;
   }
 
-  jsAst.Expression getJavaScriptClassName(Entity element, Emitter emitter) {
+  jsAst.Expression getJavaScriptClassName(
+      Entity element, ModularEmitter emitter) {
     return emitter.typeAccess(element);
   }
 
@@ -2469,12 +2380,12 @@
 
   jsAst.Expression getJsInteropTypeArgumentValue() => js('-2');
   @override
-  jsAst.Expression visit(DartType type, Emitter emitter) =>
+  jsAst.Expression visit(DartType type, ModularEmitter emitter) =>
       type.accept(this, emitter);
 
   @override
   jsAst.Expression visitTypeVariableType(
-      TypeVariableType type, Emitter emitter) {
+      TypeVariableType type, ModularEmitter emitter) {
     if (typedefBindings != null) {
       assert(typedefBindings[type] != null);
       return typedefBindings[type];
@@ -2484,14 +2395,14 @@
 
   @override
   jsAst.Expression visitFunctionTypeVariable(
-      FunctionTypeVariable type, Emitter emitter) {
+      FunctionTypeVariable type, ModularEmitter emitter) {
     int position = functionTypeVariables.indexOf(type);
     assert(position >= 0);
     return js.number(functionTypeVariables.length - position - 1);
   }
 
   @override
-  jsAst.Expression visitDynamicType(DynamicType type, Emitter emitter) {
+  jsAst.Expression visitDynamicType(DynamicType type, ModularEmitter emitter) {
     return getDynamicValue();
   }
 
@@ -2508,7 +2419,8 @@
   }
 
   @override
-  jsAst.Expression visitInterfaceType(InterfaceType type, Emitter emitter) {
+  jsAst.Expression visitInterfaceType(
+      InterfaceType type, ModularEmitter emitter) {
     jsAst.Expression name = getJavaScriptClassName(type.element, emitter);
     jsAst.Expression result;
     if (type.typeArguments.isEmpty) {
@@ -2527,7 +2439,7 @@
     return result;
   }
 
-  jsAst.Expression visitList(List<DartType> types, Emitter emitter,
+  jsAst.Expression visitList(List<DartType> types, ModularEmitter emitter,
       {jsAst.Expression head}) {
     List<jsAst.Expression> elements = <jsAst.Expression>[];
     if (head != null) {
@@ -2547,13 +2459,13 @@
   /// Returns the JavaScript template to determine at runtime if a type object
   /// is a function type.
   jsAst.Template get templateForIsFunctionType {
-    return jsAst.js.expressionTemplateFor("'${namer.functionTypeTag}' in #");
+    return jsAst.js.expressionTemplateFor("'${_rtiTags.functionTypeTag}' in #");
   }
 
   /// Returns the JavaScript template to determine at runtime if a type object
   /// is a FutureOr type.
   jsAst.Template get templateForIsFutureOrType {
-    return jsAst.js.expressionTemplateFor("'${namer.futureOrTag}' in #");
+    return jsAst.js.expressionTemplateFor("'${_rtiTags.futureOrTag}' in #");
   }
 
   /// Returns the JavaScript template to determine at runtime if a type object
@@ -2573,7 +2485,8 @@
   }
 
   @override
-  jsAst.Expression visitFunctionType(FunctionType type, Emitter emitter) {
+  jsAst.Expression visitFunctionType(
+      FunctionType type, ModularEmitter emitter) {
     List<jsAst.Property> properties = <jsAst.Property>[];
 
     void addProperty(String name, jsAst.Expression value) {
@@ -2582,7 +2495,7 @@
 
     // Type representations for functions have a property which is a tag marking
     // them as function types. The value is not used, so '1' is just a dummy.
-    addProperty(namer.functionTypeTag, js.number(1));
+    addProperty(_rtiTags.functionTypeTag, js.number(1));
 
     if (type.typeVariables.isNotEmpty) {
       // Generic function types have type parameters which are reduced to de
@@ -2593,20 +2506,20 @@
       // TODO(sra): This emits `P.Object` for the common unbounded case. We
       // could replace the Object bounds with an array hole for a compact `[,,]`
       // representation.
-      addProperty(namer.functionTypeGenericBoundsTag,
+      addProperty(_rtiTags.functionTypeGenericBoundsTag,
           visitList(type.typeVariables.map((v) => v.bound).toList(), emitter));
     }
 
     if (!type.returnType.treatAsDynamic) {
       addProperty(
-          namer.functionTypeReturnTypeTag, visit(type.returnType, emitter));
+          _rtiTags.functionTypeReturnTypeTag, visit(type.returnType, emitter));
     }
     if (!type.parameterTypes.isEmpty) {
-      addProperty(namer.functionTypeRequiredParametersTag,
+      addProperty(_rtiTags.functionTypeRequiredParametersTag,
           visitList(type.parameterTypes, emitter));
     }
     if (!type.optionalParameterTypes.isEmpty) {
-      addProperty(namer.functionTypeOptionalParametersTag,
+      addProperty(_rtiTags.functionTypeOptionalParametersTag,
           visitList(type.optionalParameterTypes, emitter));
     }
     if (!type.namedParameterTypes.isEmpty) {
@@ -2619,7 +2532,7 @@
         namedArguments
             .add(new jsAst.Property(name, visit(types[index], emitter)));
       }
-      addProperty(namer.functionTypeNamedParametersTag,
+      addProperty(_rtiTags.functionTypeNamedParametersTag,
           new jsAst.ObjectInitializer(namedArguments));
     }
 
@@ -2632,12 +2545,12 @@
   }
 
   @override
-  jsAst.Expression visitVoidType(VoidType type, Emitter emitter) {
+  jsAst.Expression visitVoidType(VoidType type, ModularEmitter emitter) {
     return getVoidValue();
   }
 
   @override
-  jsAst.Expression visitTypedefType(TypedefType type, Emitter emitter) {
+  jsAst.Expression visitTypedefType(TypedefType type, ModularEmitter emitter) {
     bool shouldEncode = shouldEncodeTypedef(type);
     DartType unaliasedType = type.unaliased;
 
@@ -2683,7 +2596,7 @@
           : visitList(type.typeArguments, emitter, head: name);
 
       // Add it to the function-type object.
-      jsAst.LiteralString tag = js.string(namer.typedefTag);
+      jsAst.LiteralString tag = js.string(_rtiTags.typedefTag);
       initializer.properties.add(new jsAst.Property(tag, encodedTypedef));
       return finish(initializer);
     } else {
@@ -2692,7 +2605,8 @@
   }
 
   @override
-  jsAst.Expression visitFutureOrType(FutureOrType type, Emitter emitter) {
+  jsAst.Expression visitFutureOrType(
+      FutureOrType type, ModularEmitter emitter) {
     List<jsAst.Property> properties = <jsAst.Property>[];
 
     void addProperty(String name, jsAst.Expression value) {
@@ -2701,9 +2615,9 @@
 
     // Type representations for FutureOr have a property which is a tag marking
     // them as FutureOr types. The value is not used, so '1' is just a dummy.
-    addProperty(namer.futureOrTag, js.number(1));
+    addProperty(_rtiTags.futureOrTag, js.number(1));
     if (!type.typeArgument.treatAsDynamic) {
-      addProperty(namer.futureOrTypeTag, visit(type.typeArgument, emitter));
+      addProperty(_rtiTags.futureOrTypeTag, visit(type.typeArgument, emitter));
     }
 
     return new jsAst.ObjectInitializer(properties);
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 99a79d1..96e3219 100644
--- a/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart
+++ b/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart
@@ -151,7 +151,7 @@
 /// code generation.
 ///
 /// Note that the emission phase is not itself modular but performed on
-/// performed on the closed world computed by the codegen enqueuer.
+/// the closed world computed by the codegen enqueuer.
 abstract class ModularEmitter {
   /// Returns the JS prototype of the given class [e].
   jsAst.Expression prototypeAccess(ClassEntity e, {bool hasBeenInstantiated});
@@ -175,6 +175,9 @@
   /// The returned expression must only be used in a JS `new` expression.
   jsAst.Expression constructorAccess(ClassEntity e);
 
+  /// Returns the JS expression representing the type [e].
+  jsAst.Expression typeAccess(Entity e);
+
   /// Returns the JS code for accessing the embedded [global].
   jsAst.Expression generateEmbeddedGlobalAccess(String global);
 
@@ -199,9 +202,6 @@
   /// Returns the JS constructor of the given interceptor class [e].
   jsAst.Expression interceptorClassAccess(ClassEntity e);
 
-  /// Returns the JS expression representing the type [e].
-  jsAst.Expression typeAccess(Entity e);
-
   /// Returns the JS expression representing a function that returns 'null'
   jsAst.Expression generateFunctionThatReturnsNull();
 
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 cd297c8..0ce8bde 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
@@ -958,7 +958,8 @@
       FunctionType type, OutputUnit outputUnit) {
     if (type.containsTypeVariables) {
       js.Expression thisAccess = js.js(r'this.$receiver');
-      return _rtiEncoder.getSignatureEncoding(_task.emitter, type, thisAccess);
+      return _rtiEncoder.getSignatureEncoding(
+          _namer, _task.emitter, type, thisAccess);
     } else {
       return _task.metadataCollector.reifyType(type, outputUnit);
     }
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 02cdf45..ad0cb3c 100644
--- a/pkg/compiler/lib/src/js_emitter/runtime_type_generator.dart
+++ b/pkg/compiler/lib/src/js_emitter/runtime_type_generator.dart
@@ -16,6 +16,7 @@
     show
         ClassChecks,
         ClassFunctionType,
+        OnVariableCallback,
         RuntimeTypesChecks,
         RuntimeTypesEncoder,
         Substitution,
@@ -23,7 +24,7 @@
 import '../js_emitter/sorter.dart';
 import '../util/util.dart' show Setlet;
 
-import 'code_emitter_task.dart' show CodeEmitterTask;
+import 'code_emitter_task.dart' show CodeEmitterTask, Emitter;
 
 // Function signatures used in the generation of runtime type information.
 typedef void FunctionTypeSignatureEmitter(ClassFunctionType classFunctionType);
@@ -187,7 +188,7 @@
       Substitution substitution = check.substitution;
       if (substitution != null) {
         jsAst.Expression body =
-            _rtiEncoder.getSubstitutionCode(emitterTask.emitter, substitution);
+            _getSubstitutionCode(emitterTask.emitter, substitution);
         result.addSubstitution(
             checkedClass, _namer.substitutionName(checkedClass), body);
       }
@@ -201,7 +202,7 @@
       if (type != null) {
         jsAst.Expression thisAccess = new jsAst.This();
         jsAst.Expression encoding = _rtiEncoder.getSignatureEncoding(
-            emitterTask.emitter, type, thisAccess);
+            _namer, emitterTask.emitter, type, thisAccess);
         jsAst.Name operatorSignature = _namer.asName(_namer.operatorSignature);
         result.addSignature(classElement, operatorSignature, encoding);
       }
@@ -209,6 +210,71 @@
     return result;
   }
 
+  /// Compute a JavaScript expression that describes the necessary substitution
+  /// for type arguments in a subtype test.
+  ///
+  /// The result can be:
+  ///  1) `null`, if no substituted check is necessary, because the type
+  ///     variables are the same or there are no type variables in the class
+  ///     that is checked for.
+  ///  2) A list expression describing the type arguments to be used in the
+  ///     subtype check, if the type arguments to be used in the check do not
+  ///     depend on the type arguments of the object.
+  ///  3) A function mapping the type variables of the object to be checked to
+  ///     a list expression.
+  jsAst.Expression _getSubstitutionCode(
+      Emitter emitter, Substitution substitution) {
+    if (substitution.isTrivial) {
+      return new jsAst.LiteralNull();
+    }
+
+    if (substitution.isJsInterop) {
+      return js('function() { return # }',
+          _rtiEncoder.getJsInteropTypeArguments(substitution.length));
+    }
+
+    jsAst.Expression declaration(TypeVariableType variable) {
+      return new jsAst.Parameter(_getVariableName(variable.element.name));
+    }
+
+    jsAst.Expression use(TypeVariableType variable) {
+      return new jsAst.VariableUse(_getVariableName(variable.element.name));
+    }
+
+    if (substitution.arguments.every((DartType type) => type.isDynamic)) {
+      return emitter.generateFunctionThatReturnsNull();
+    } else {
+      jsAst.Expression value =
+          _getSubstitutionRepresentation(emitter, substitution.arguments, use);
+      if (substitution.isFunction) {
+        Iterable<jsAst.Expression> formals =
+            // TODO(johnniwinther): Pass [declaration] directly to `map` when
+            // `substitution.parameters` can no longer be a
+            // `List<ResolutionDartType>`.
+            substitution.parameters.map((type) => declaration(type));
+        return js('function(#) { return # }', [formals, value]);
+      } else {
+        return js('function() { return # }', value);
+      }
+    }
+  }
+
+  jsAst.Expression _getSubstitutionRepresentation(
+      Emitter emitter, List<DartType> types, OnVariableCallback onVariable) {
+    List<jsAst.Expression> elements = types
+        .map((DartType type) =>
+            _rtiEncoder.getTypeRepresentation(emitter, type, onVariable))
+        .toList(growable: false);
+    return new jsAst.ArrayInitializer(elements);
+  }
+
+  String _getVariableName(String name) {
+    // Kernel type variable names for anonymous mixin applications have names
+    // canonicalized to a non-identified, e.g. '#U0'.
+    name = name.replaceAll('#', '_');
+    return _namer.safeVariableName(name);
+  }
+
   void _generateIsTestsOn(
       ClassEntity cls,
       FunctionTypeSignatureEmitter generateFunctionTypeSignature,
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 a2095b5..70f0219 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart
@@ -81,6 +81,7 @@
     return js.js('#.prototype', constructor);
   }
 
+  @override
   js.Expression typeAccess(Entity element) {
     return globalPropertyAccessForType(element);
   }
diff --git a/pkg/compiler/lib/src/ssa/ssa_tracer.dart b/pkg/compiler/lib/src/ssa/ssa_tracer.dart
index b4fe2d7..9a5b4ca 100644
--- a/pkg/compiler/lib/src/ssa/ssa_tracer.dart
+++ b/pkg/compiler/lib/src/ssa/ssa_tracer.dart
@@ -7,7 +7,7 @@
 import '../../compiler_new.dart' show OutputSink;
 import '../diagnostics/invariant.dart' show DEBUG_MODE;
 import '../inferrer/abstract_value_domain.dart';
-import '../js_backend/namer.dart' show Namer;
+import '../js_backend/namer.dart' show suffixForGetInterceptor;
 import '../tracer.dart';
 import '../world.dart' show JClosedWorld;
 import 'nodes.dart';
@@ -17,11 +17,10 @@
 /// to enable it.
 class HTracer extends HGraphVisitor with TracerUtil {
   final JClosedWorld closedWorld;
-  final Namer namer;
   @override
   final OutputSink output;
 
-  HTracer(this.output, this.closedWorld, this.namer);
+  HTracer(this.output, this.closedWorld);
 
   void traceGraph(String name, HGraph graph) {
     DEBUG_MODE = true;
@@ -76,7 +75,7 @@
   @override
   void visitBasicBlock(HBasicBlock block) {
     HInstructionStringifier stringifier =
-        new HInstructionStringifier(block, closedWorld, namer);
+        new HInstructionStringifier(block, closedWorld);
     assert(block.id != null);
     tag("block", () {
       printProperty("name", "B${block.id}");
@@ -114,10 +113,9 @@
 
 class HInstructionStringifier implements HVisitor<String> {
   final JClosedWorld closedWorld;
-  final Namer namer;
   final HBasicBlock currentBlock;
 
-  HInstructionStringifier(this.currentBlock, this.closedWorld, this.namer);
+  HInstructionStringifier(this.currentBlock, this.closedWorld);
 
   AbstractValueDomain get _abstractValueDomain =>
       closedWorld.abstractValueDomain;
@@ -356,8 +354,9 @@
   String visitInterceptor(HInterceptor node) {
     String value = temporaryId(node.inputs[0]);
     if (node.interceptedClasses != null) {
-      String cls = namer.suffixForGetInterceptor(node.interceptedClasses);
-      return "Interceptor ($cls): $value";
+      String cls = suffixForGetInterceptor(closedWorld.commonElements,
+          closedWorld.nativeData, node.interceptedClasses);
+      return "Interceptor (${cls}): $value";
     }
     return "Interceptor: $value";
   }
diff --git a/pkg/compiler/lib/src/tracer.dart b/pkg/compiler/lib/src/tracer.dart
index 2c687bf..c67cafc 100644
--- a/pkg/compiler/lib/src/tracer.dart
+++ b/pkg/compiler/lib/src/tracer.dart
@@ -5,7 +5,6 @@
 library tracer;
 
 import '../compiler_new.dart' as api;
-import 'js_backend/namer.dart' show Namer;
 import 'ssa/nodes.dart' as ssa show HGraph;
 import 'ssa/ssa_tracer.dart' show HTracer;
 import 'util/util.dart' show Indentation;
@@ -24,13 +23,12 @@
 /// readable by IR Hydra.
 class Tracer extends TracerUtil {
   final JClosedWorld closedWorld;
-  final Namer namer;
   bool traceActive = false;
   @override
   final api.OutputSink output;
   final RegExp traceFilter;
 
-  Tracer(this.closedWorld, this.namer, api.CompilerOutput compilerOutput)
+  Tracer(this.closedWorld, api.CompilerOutput compilerOutput)
       : traceFilter = TRACE_FILTER_PATTERN == null
             ? null
             : new RegExp(TRACE_FILTER_PATTERN),
@@ -55,7 +53,7 @@
   void traceGraph(String name, var irObject) {
     if (!traceActive) return;
     if (irObject is ssa.HGraph) {
-      new HTracer(output, closedWorld, namer).traceGraph(name, irObject);
+      new HTracer(output, closedWorld).traceGraph(name, irObject);
     }
   }
 
diff --git a/tests/compiler/dart2js/codegen/is_function_test.dart b/tests/compiler/dart2js/codegen/is_function_test.dart
index e0b3aea..9965040 100644
--- a/tests/compiler/dart2js/codegen/is_function_test.dart
+++ b/tests/compiler/dart2js/codegen/is_function_test.dart
@@ -25,7 +25,7 @@
     Expect.isTrue(result.isSuccess);
     Compiler compiler = result.compiler;
     Program program = compiler.backend.emitterTask.emitter.programForTesting;
-    var name = compiler.backend.namer.operatorIs(
+    var name = compiler.backend.namerForTesting.operatorIs(
         compiler.backendClosedWorldForTesting.commonElements.functionClass);
     for (Fragment fragment in program.fragments) {
       for (Library library in fragment.libraries) {
diff --git a/tests/compiler/dart2js/codegen/jsarray_indexof_test.dart b/tests/compiler/dart2js/codegen/jsarray_indexof_test.dart
index 8b98f7d..d5df20f 100644
--- a/tests/compiler/dart2js/codegen/jsarray_indexof_test.dart
+++ b/tests/compiler/dart2js/codegen/jsarray_indexof_test.dart
@@ -51,7 +51,7 @@
 
   Selector getLengthSelector = new Selector.getter(const PublicName('length'));
   js.Name getLengthName =
-      compiler.backend.namer.invocationName(getLengthSelector);
+      compiler.backend.namerForTesting.invocationName(getLengthSelector);
 
   Method method = programLookup.getMethod(jsArrayIndexOf);
   int lengthCount = 0;
diff --git a/tests/compiler/dart2js/deferred/emit_type_checks_test.dart b/tests/compiler/dart2js/deferred/emit_type_checks_test.dart
index a6cf80c..932e4df 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.namer.operatorIsPrefix;
+    String isPrefix = backend.namerForTesting.operatorIsPrefix;
     Expect.isTrue(
         deferredOutput.contains('${isPrefix}A: 1'),
         "Deferred output doesn't contain '${isPrefix}A: 1':\n"
diff --git a/tests/compiler/dart2js/helpers/program_lookup.dart b/tests/compiler/dart2js/helpers/program_lookup.dart
index 6aff382..4f4d052 100644
--- a/tests/compiler/dart2js/helpers/program_lookup.dart
+++ b/tests/compiler/dart2js/helpers/program_lookup.dart
@@ -44,7 +44,7 @@
 
   ProgramLookup(Compiler compiler)
       : this.program = compiler.backend.emitterTask.emitter.programForTesting,
-        this.namer = compiler.backend.namer;
+        this.namer = compiler.backend.namerForTesting;
 
   Fragment getFragment(OutputUnit outputUnit) {
     for (Fragment fragment in program.fragments) {
diff --git a/tests/compiler/dart2js/rti/factory_call_test.dart b/tests/compiler/dart2js/rti/factory_call_test.dart
index 5526cc0..5b28378 100644
--- a/tests/compiler/dart2js/rti/factory_call_test.dart
+++ b/tests/compiler/dart2js/rti/factory_call_test.dart
@@ -46,7 +46,7 @@
     ProgramLookup programLookup = new ProgramLookup(compiler);
 
     js.Name getName(String name) {
-      return compiler.backend.namer
+      return compiler.backend.namerForTesting
           .globalPropertyNameForMember(lookupMember(elementEnvironment, name));
     }
 
diff --git a/tests/compiler/dart2js/rti/instance_call_test.dart b/tests/compiler/dart2js/rti/instance_call_test.dart
index 3b0c102..baff537 100644
--- a/tests/compiler/dart2js/rti/instance_call_test.dart
+++ b/tests/compiler/dart2js/rti/instance_call_test.dart
@@ -107,7 +107,7 @@
     ProgramLookup programLookup = new ProgramLookup(compiler);
 
     js.Name getName(String name, int typeArguments) {
-      return compiler.backend.namer.invocationName(new Selector.call(
+      return compiler.backend.namerForTesting.invocationName(new Selector.call(
           new PublicName(name),
           new CallStructure(1, const <String>[], typeArguments)));
     }
diff --git a/tests/compiler/dart2js/rti/type_representation_test.dart b/tests/compiler/dart2js/rti/type_representation_test.dart
index 97bd3d6..0729ef9 100644
--- a/tests/compiler/dart2js/rti/type_representation_test.dart
+++ b/tests/compiler/dart2js/rti/type_representation_test.dart
@@ -14,7 +14,7 @@
 import 'package:compiler/src/elements/entities.dart';
 import 'package:compiler/src/js_backend/backend.dart' show JavaScriptBackend;
 import 'package:compiler/src/js_backend/runtime_types.dart'
-    show TypeRepresentationGenerator;
+    show RuntimeTypeTags, TypeRepresentationGenerator;
 import 'package:compiler/src/world.dart';
 import 'package:expect/expect.dart';
 import '../helpers/element_lookup.dart';
@@ -67,9 +67,10 @@
   Compiler compiler = result.compiler;
   JavaScriptBackend backend = compiler.backend;
 
+  RuntimeTypeTags rtiTags = const RuntimeTypeTags();
   TypeRepresentationGenerator typeRepresentation =
       new TypeRepresentationGenerator(
-          backend.namer, compiler.backendClosedWorldForTesting.nativeData);
+          rtiTags, compiler.backendClosedWorldForTesting.nativeData);
 
   Expression onVariable(TypeVariableType _variable) {
     TypeVariableType variable = _variable;
@@ -111,15 +112,15 @@
 
   JClosedWorld closedWorld = compiler.backendClosedWorldForTesting;
   ElementEnvironment elementEnvironment = closedWorld.elementEnvironment;
-  String func = backend.namer.functionTypeTag;
-  String ret = backend.namer.functionTypeReturnTypeTag;
+  String func = rtiTags.functionTypeTag;
+  String ret = rtiTags.functionTypeReturnTypeTag;
   String retvoid = '$ret: -1';
-  String args = backend.namer.functionTypeRequiredParametersTag;
-  String opt = backend.namer.functionTypeOptionalParametersTag;
-  String named = backend.namer.functionTypeNamedParametersTag;
-  String bounds = backend.namer.functionTypeGenericBoundsTag;
-  String futureOr = backend.namer.futureOrTag;
-  String futureOrType = backend.namer.futureOrTypeTag;
+  String args = rtiTags.functionTypeRequiredParametersTag;
+  String opt = rtiTags.functionTypeOptionalParametersTag;
+  String named = rtiTags.functionTypeNamedParametersTag;
+  String bounds = rtiTags.functionTypeGenericBoundsTag;
+  String futureOr = rtiTags.futureOrTag;
+  String futureOrType = rtiTags.futureOrTypeTag;
 
   ClassEntity List_ = findClass(closedWorld, 'List');
   TypeVariableType List_E =