Introduce Codegen interface to decouple JavaScriptBackend from the codegen phase

Change-Id: I7effc20d34fa5d5da799c690182eb6f09948c6ff
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/101288
Reviewed-by: Sigmund Cherem <sigmund@google.com>
diff --git a/pkg/compiler/lib/src/backend_strategy.dart b/pkg/compiler/lib/src/backend_strategy.dart
index c5cfe60..a3461a8 100644
--- a/pkg/compiler/lib/src/backend_strategy.dart
+++ b/pkg/compiler/lib/src/backend_strategy.dart
@@ -43,7 +43,7 @@
       GlobalTypeInferenceResults globalInferenceResults);
 
   /// Creates the [SsaBuilder] used for the element model.
-  SsaBuilder createSsaBuilder(CompilerTask task, JavaScriptBackend backend,
+  SsaBuilder createSsaBuilder(CompilerTask task, CodegenInputs codegen,
       SourceInformationStrategy sourceInformationStrategy);
 
   /// Returns the [SourceInformationStrategy] use for the element model.
diff --git a/pkg/compiler/lib/src/common/codegen.dart b/pkg/compiler/lib/src/common/codegen.dart
index ec92751..b2dedea 100644
--- a/pkg/compiler/lib/src/common/codegen.dart
+++ b/pkg/compiler/lib/src/common/codegen.dart
@@ -7,6 +7,7 @@
 import '../common_elements.dart';
 import '../elements/entities.dart';
 import '../elements/types.dart' show DartType, InterfaceType;
+import '../native/behavior.dart';
 import '../universe/feature.dart';
 import '../universe/use.dart' show ConstantUse, DynamicUse, StaticUse, TypeUse;
 import '../universe/world_impact.dart'
@@ -34,6 +35,10 @@
 
   Iterable<GenericInstantiation> get genericInstantiations =>
       const <GenericInstantiation>[];
+
+  Iterable<NativeBehavior> get nativeBehaviors => const [];
+
+  Iterable<FunctionEntity> get nativeMethods => const [];
 }
 
 class _CodegenImpact extends WorldImpactBuilderImpl implements CodegenImpact {
@@ -43,6 +48,8 @@
   bool _usesInterceptor = false;
   EnumSet<AsyncMarker> _asyncMarkers;
   Set<GenericInstantiation> _genericInstantiations;
+  List<NativeBehavior> _nativeBehaviors;
+  Set<FunctionEntity> _nativeMethods;
 
   _CodegenImpact();
 
@@ -117,6 +124,26 @@
   Iterable<GenericInstantiation> get genericInstantiations {
     return _genericInstantiations ?? const <GenericInstantiation>[];
   }
+
+  void registerNativeBehavior(NativeBehavior nativeBehavior) {
+    _nativeBehaviors ??= [];
+    _nativeBehaviors.add(nativeBehavior);
+  }
+
+  @override
+  Iterable<NativeBehavior> get nativeBehaviors {
+    return _nativeBehaviors ?? const <NativeBehavior>[];
+  }
+
+  void registerNativeMethod(FunctionEntity function) {
+    _nativeMethods ??= {};
+    _nativeMethods.add(function);
+  }
+
+  @override
+  Iterable<FunctionEntity> get nativeMethods {
+    return _nativeMethods ?? const [];
+  }
 }
 
 // TODO(johnniwinther): Split this class into interface and implementation.
@@ -187,6 +214,14 @@
   void registerGenericInstantiation(GenericInstantiation instantiation) {
     worldImpact.registerGenericInstantiation(instantiation);
   }
+
+  void registerNativeBehavior(NativeBehavior nativeBehavior) {
+    worldImpact.registerNativeBehavior(nativeBehavior);
+  }
+
+  void registerNativeMethod(FunctionEntity function) {
+    worldImpact.registerNativeMethod(function);
+  }
 }
 
 /// [WorkItem] used exclusively by the [CodegenEnqueuer].
diff --git a/pkg/compiler/lib/src/dump_info.dart b/pkg/compiler/lib/src/dump_info.dart
index 3bfb460..eec68ab 100644
--- a/pkg/compiler/lib/src/dump_info.dart
+++ b/pkg/compiler/lib/src/dump_info.dart
@@ -204,7 +204,7 @@
 
     classInfo.size = size;
 
-    if (!compiler.backend.emitter.neededClasses.contains(clazz) &&
+    if (!compiler.backend.emitterTask.neededClasses.contains(clazz) &&
         classInfo.fields.isEmpty &&
         classInfo.functions.isEmpty) {
       return null;
@@ -350,7 +350,7 @@
           ? compiler.options.outputUri.pathSegments.last
           : deferredPartFileName(compiler.options, outputUnit.name);
       OutputUnitInfo info = new OutputUnitInfo(filename, outputUnit.name,
-          backend.emitter.emitter.generatedSize(outputUnit));
+          backend.emitterTask.emitter.generatedSize(outputUnit));
       info.imports
           .addAll(closedWorld.outputUnitData.getImportNames(outputUnit));
       result.outputUnits.add(info);
diff --git a/pkg/compiler/lib/src/js_backend/backend.dart b/pkg/compiler/lib/src/js_backend/backend.dart
index 026dd8c1..ffb0c40 100644
--- a/pkg/compiler/lib/src/js_backend/backend.dart
+++ b/pkg/compiler/lib/src/js_backend/backend.dart
@@ -6,36 +6,30 @@
 
 import '../common.dart';
 import '../common/backend_api.dart' show ImpactTransformer;
-import '../common/codegen.dart' show CodegenRegistry, CodegenWorkItem;
+import '../common/codegen.dart' show CodegenWorkItem;
 import '../common/names.dart' show Uris;
 import '../common/tasks.dart' show CompilerTask;
-import '../common_elements.dart'
-    show CommonElements, ElementEnvironment, JElementEnvironment;
+import '../common_elements.dart' show CommonElements, ElementEnvironment;
 import '../compiler.dart' show Compiler;
 import '../deferred_load.dart' show DeferredLoadTask;
 import '../dump_info.dart' show DumpInfoTask;
 import '../elements/entities.dart';
-import '../elements/types.dart';
 import '../enqueue.dart' show Enqueuer, EnqueueTask, ResolutionEnqueuer;
 import '../frontend_strategy.dart';
 import '../inferrer/types.dart';
-import '../io/source_information.dart'
-    show SourceInformation, SourceInformationStrategy;
+import '../io/source_information.dart' show SourceInformationStrategy;
 import '../js/js.dart' as jsAst;
 import '../js_model/elements.dart';
-import '../js/rewrite_async.dart';
-import '../js_emitter/js_emitter.dart' show CodeEmitterTask;
+import '../js_emitter/js_emitter.dart' show CodeEmitterTask, Emitter;
 import '../js_emitter/sorter.dart' show Sorter;
 import '../kernel/dart2js_target.dart';
 import '../native/enqueue.dart';
 import '../ssa/ssa.dart' show SsaFunctionCompiler;
 import '../tracer.dart';
-import '../universe/call_structure.dart' show CallStructure;
 import '../universe/class_hierarchy.dart'
     show ClassHierarchyBuilder, ClassQueries;
 import '../universe/codegen_world_builder.dart';
 import '../universe/selector.dart' show Selector;
-import '../universe/use.dart' show StaticUse;
 import '../universe/world_builder.dart';
 import '../universe/world_impact.dart'
     show ImpactStrategy, ImpactUseCase, WorldImpact, WorldImpactVisitor;
@@ -59,10 +53,13 @@
 import 'runtime_types.dart';
 
 abstract class FunctionCompiler {
-  void onCodegenStart();
+  void onCodegenStart(CodegenInputs codegen);
 
   /// Generates JavaScript code for `work.element`.
-  jsAst.Fun compile(CodegenWorkItem work, JClosedWorld closedWorld,
+  jsAst.Fun compile(
+      CodegenWorkItem work,
+      CodegenInputs codegen,
+      JClosedWorld closedWorld,
       GlobalTypeInferenceResults globalInferenceResults);
 
   Iterable get tasks;
@@ -295,7 +292,7 @@
   NAME
 }
 
-class JavaScriptBackend {
+class JavaScriptBackend implements CodegenInputs {
   static const String JS = 'JS';
   static const String JS_BUILTIN = 'JS_BUILTIN';
   static const String JS_EMBEDDED_GLOBAL = 'JS_EMBEDDED_GLOBAL';
@@ -308,7 +305,10 @@
 
   FunctionCompiler functionCompiler;
 
-  CodeEmitterTask emitter;
+  CodeEmitterTask emitterTask;
+
+  @override
+  Emitter get emitter => emitterTask.emitter;
 
   /// The generated code as a js AST for compiled methods.
   final Map<MemberEntity, jsAst.Expression> generatedCode =
@@ -316,6 +316,7 @@
 
   Namer _namer;
 
+  @override
   Namer get namer {
     assert(_namer != null,
         failedAt(NO_LOCATION_SPANNABLE, "Namer has not been created yet."));
@@ -327,7 +328,7 @@
 
   List<CompilerTask> get tasks {
     List<CompilerTask> result = functionCompiler.tasks;
-    result.add(emitter);
+    result.add(emitterTask);
     return result;
   }
 
@@ -368,11 +369,13 @@
 
   CheckedModeHelpers _checkedModeHelpers;
 
+  @override
   final SuperMemberData superMemberData = new SuperMemberData();
 
   NativeResolutionEnqueuer _nativeResolutionEnqueuer;
   NativeCodegenEnqueuer _nativeCodegenEnqueuer;
 
+  @override
   Tracer tracer;
 
   JavaScriptBackend(this.compiler,
@@ -385,11 +388,15 @@
     _backendUsageBuilder =
         new BackendUsageBuilderImpl(compiler.frontendStrategy);
     _checkedModeHelpers = new CheckedModeHelpers();
-    emitter = new CodeEmitterTask(compiler, generateSourceMap);
+    emitterTask = new CodeEmitterTask(compiler, generateSourceMap);
     noSuchMethodRegistry = new NoSuchMethodRegistryImpl(
         commonElements, compiler.frontendStrategy.createNoSuchMethodResolver());
     functionCompiler = new SsaFunctionCompiler(
-        this, compiler.measurer, sourceInformationStrategy);
+        compiler.options,
+        compiler.reporter,
+        compiler.backendStrategy,
+        compiler.measurer,
+        sourceInformationStrategy);
   }
 
   DiagnosticReporter get reporter => compiler.reporter;
@@ -418,6 +425,7 @@
     return _customElementsCodegenAnalysis;
   }
 
+  @override
   OneShotInterceptorData get oneShotInterceptorData {
     assert(
         _oneShotInterceptorData != null,
@@ -440,6 +448,7 @@
 
   RuntimeTypesChecksBuilder get rtiChecksBuilderForTesting => _rtiChecksBuilder;
 
+  @override
   RuntimeTypesSubstitutions get rtiSubstitutions {
     assert(
         _rtiSubstitutions != null,
@@ -448,6 +457,7 @@
     return _rtiSubstitutions;
   }
 
+  @override
   RuntimeTypesEncoder get rtiEncoder {
     assert(
         _rtiEncoder != null,
@@ -456,6 +466,7 @@
     return _rtiEncoder;
   }
 
+  @override
   CheckedModeHelpers get checkedModeHelpers => _checkedModeHelpers;
 
   Namer determineNamer(JClosedWorld closedWorld) {
@@ -605,7 +616,7 @@
         elementEnvironment,
         commonElements,
         closedWorld.dartTypes,
-        emitter,
+        emitterTask,
         closedWorld.liveNativeClasses,
         closedWorld.nativeData);
     return new CodegenEnqueuer(
@@ -638,8 +649,8 @@
       return const WorldImpact();
     }
 
-    jsAst.Fun function =
-        functionCompiler.compile(work, closedWorld, globalInferenceResults);
+    jsAst.Fun function = functionCompiler.compile(
+        work, this, closedWorld, globalInferenceResults);
     if (function != null) {
       if (function.sourceInformation == null) {
         function = function.withSourceInformation(
@@ -673,8 +684,8 @@
   /// Generates the output and returns the total size of the generated code.
   int assembleProgram(JClosedWorld closedWorld, InferredData inferredData,
       CodegenWorld codegenWorld) {
-    int programSize =
-        emitter.assembleProgram(namer, closedWorld, inferredData, codegenWorld);
+    int programSize = emitterTask.assembleProgram(
+        namer, closedWorld, inferredData, codegenWorld);
     closedWorld.noSuchMethodData.emitDiagnostic(reporter);
     return programSize;
   }
@@ -698,7 +709,6 @@
   /// [WorldImpact] of enabled backend features is returned.
   WorldImpact onCodegenStart(JClosedWorld closedWorld,
       CodegenWorldBuilder codegenWorldBuilder, Sorter sorter) {
-    functionCompiler.onCodegenStart();
     _oneShotInterceptorData = new OneShotInterceptorData(
         closedWorld.interceptorData, closedWorld.commonElements);
     _namer = determineNamer(closedWorld);
@@ -709,7 +719,7 @@
         closedWorld.elementEnvironment,
         closedWorld.commonElements,
         closedWorld.rtiNeed);
-    emitter.createEmitter(namer, closedWorld, codegenWorldBuilder, sorter);
+    emitterTask.createEmitter(namer, closedWorld, codegenWorldBuilder, sorter);
     // TODO(johnniwinther): Share the impact object created in
     // createCodegenEnqueuer.
     BackendImpacts impacts = new BackendImpacts(closedWorld.commonElements);
@@ -734,7 +744,10 @@
         nativeCodegenEnqueuer,
         namer,
         oneShotInterceptorData,
-        rtiChecksBuilder);
+        rtiChecksBuilder,
+        emitterTask.nativeEmitter);
+
+    functionCompiler.onCodegenStart(this);
     return const WorldImpact();
   }
 
@@ -744,117 +757,6 @@
     tracer.close();
   }
 
-  jsAst.Expression rewriteAsync(
-      CommonElements commonElements,
-      JElementEnvironment elementEnvironment,
-      CodegenRegistry registry,
-      FunctionEntity element,
-      jsAst.Expression code,
-      DartType asyncTypeParameter,
-      SourceInformation bodySourceInformation,
-      SourceInformation exitSourceInformation) {
-    if (element.asyncMarker == AsyncMarker.SYNC) return code;
-
-    AsyncRewriterBase rewriter = null;
-    jsAst.Name name = namer.methodPropertyName(
-        element is JGeneratorBody ? element.function : element);
-
-    switch (element.asyncMarker) {
-      case AsyncMarker.ASYNC:
-        rewriter = _makeAsyncRewriter(commonElements, elementEnvironment,
-            registry, element, code, asyncTypeParameter, name);
-        break;
-      case AsyncMarker.SYNC_STAR:
-        rewriter = new SyncStarRewriter(reporter, element,
-            endOfIteration:
-                emitter.staticFunctionAccess(commonElements.endOfIteration),
-            iterableFactory: emitter
-                .staticFunctionAccess(commonElements.syncStarIterableFactory),
-            iterableFactoryTypeArguments: _fetchItemType(asyncTypeParameter),
-            yieldStarExpression:
-                emitter.staticFunctionAccess(commonElements.yieldStar),
-            uncaughtErrorExpression: emitter
-                .staticFunctionAccess(commonElements.syncStarUncaughtError),
-            safeVariableName: namer.safeVariablePrefixForAsyncRewrite,
-            bodyName: namer.deriveAsyncBodyName(name));
-        registry.registerStaticUse(new StaticUse.staticInvoke(
-            commonElements.syncStarIterableFactory,
-            const CallStructure.unnamed(1, 1), [
-          elementEnvironment.getFunctionAsyncOrSyncStarElementType(element)
-        ]));
-        break;
-      case AsyncMarker.ASYNC_STAR:
-        rewriter = new AsyncStarRewriter(reporter, element,
-            asyncStarHelper:
-                emitter.staticFunctionAccess(commonElements.asyncStarHelper),
-            streamOfController:
-                emitter.staticFunctionAccess(commonElements.streamOfController),
-            wrapBody: emitter.staticFunctionAccess(commonElements.wrapBody),
-            newController: emitter.staticFunctionAccess(
-                commonElements.asyncStarStreamControllerFactory),
-            newControllerTypeArguments: _fetchItemType(asyncTypeParameter),
-            safeVariableName: namer.safeVariablePrefixForAsyncRewrite,
-            yieldExpression:
-                emitter.staticFunctionAccess(commonElements.yieldSingle),
-            yieldStarExpression:
-                emitter.staticFunctionAccess(commonElements.yieldStar),
-            bodyName: namer.deriveAsyncBodyName(name));
-        registry.registerStaticUse(new StaticUse.staticInvoke(
-            commonElements.asyncStarStreamControllerFactory,
-            const CallStructure.unnamed(1, 1), [
-          elementEnvironment.getFunctionAsyncOrSyncStarElementType(element)
-        ]));
-        break;
-    }
-    return rewriter.rewrite(code, bodySourceInformation, exitSourceInformation);
-  }
-
-  /// Returns an optional expression that evaluates [type].  Returns `null` if
-  /// the type expression is determined by the outside context and should be
-  /// added as a function parameter to the rewritten code.
-  // TODO(sra): We could also return an empty list if the generator takes no
-  // type (e.g. due to rtiNeed optimization).
-  List<jsAst.Expression> _fetchItemType(DartType type) {
-    if (type == null) return null;
-    var ast = rtiEncoder.getTypeRepresentation(emitter.emitter, type, null);
-    return <jsAst.Expression>[ast];
-  }
-
-  AsyncRewriter _makeAsyncRewriter(
-      CommonElements commonElements,
-      JElementEnvironment elementEnvironment,
-      CodegenRegistry registry,
-      FunctionEntity element,
-      jsAst.Expression code,
-      DartType elementType,
-      jsAst.Name name) {
-    var startFunction = commonElements.asyncHelperStartSync;
-    var completerFactory = commonElements.asyncAwaitCompleterFactory;
-
-    List<jsAst.Expression> itemTypeExpression = _fetchItemType(elementType);
-
-    var rewriter = new AsyncRewriter(reporter, element,
-        asyncStart: emitter.staticFunctionAccess(startFunction),
-        asyncAwait:
-            emitter.staticFunctionAccess(commonElements.asyncHelperAwait),
-        asyncReturn:
-            emitter.staticFunctionAccess(commonElements.asyncHelperReturn),
-        asyncRethrow:
-            emitter.staticFunctionAccess(commonElements.asyncHelperRethrow),
-        wrapBody: emitter.staticFunctionAccess(commonElements.wrapBody),
-        completerFactory: emitter.staticFunctionAccess(completerFactory),
-        completerFactoryTypeArguments: itemTypeExpression,
-        safeVariableName: namer.safeVariablePrefixForAsyncRewrite,
-        bodyName: namer.deriveAsyncBodyName(name));
-
-    registry.registerStaticUse(new StaticUse.staticInvoke(
-        completerFactory,
-        const CallStructure.unnamed(0, 1),
-        [elementEnvironment.getFunctionAsyncOrSyncStarElementType(element)]));
-
-    return rewriter;
-  }
-
   /// Creates an impact strategy to use for compilation.
   ImpactStrategy createImpactStrategy(
       {bool supportDeferredLoad: true, bool supportDumpInfo: true}) {
@@ -929,3 +831,20 @@
     return _aliasedSuperMembers.contains(member);
   }
 }
+
+/// Interface for resources only used during code generation.
+///
+/// This is currently implemented by the [JavaScriptBackend] but should be
+/// a separate object created when the codegen phase starts.
+abstract class CodegenInputs {
+  // TODO(johnniwinther): Should be the modular part of the emitter.
+  Emitter get emitter;
+  CheckedModeHelpers get checkedModeHelpers;
+  OneShotInterceptorData get oneShotInterceptorData;
+  RuntimeTypesSubstitutions get rtiSubstitutions;
+  RuntimeTypesEncoder get rtiEncoder;
+  // TODO(johnniwinther): Should be the modular part of the namer.
+  Namer get namer;
+  SuperMemberData get superMemberData;
+  Tracer get tracer;
+}
diff --git a/pkg/compiler/lib/src/js_backend/constant_emitter.dart b/pkg/compiler/lib/src/js_backend/constant_emitter.dart
index f214ed2..8976841 100644
--- a/pkg/compiler/lib/src/js_backend/constant_emitter.dart
+++ b/pkg/compiler/lib/src/js_backend/constant_emitter.dart
@@ -320,7 +320,7 @@
   @override
   jsAst.Expression visitInterceptor(InterceptorConstantValue constant, [_]) {
     ClassEntity interceptorClass = constant.cls;
-    return _task.interceptorPrototypeAccess(interceptorClass);
+    return _emitter.interceptorPrototypeAccess(interceptorClass);
   }
 
   @override
diff --git a/pkg/compiler/lib/src/js_backend/impact_transformer.dart b/pkg/compiler/lib/src/js_backend/impact_transformer.dart
index 1dbffb7..d4276ff 100644
--- a/pkg/compiler/lib/src/js_backend/impact_transformer.dart
+++ b/pkg/compiler/lib/src/js_backend/impact_transformer.dart
@@ -15,6 +15,7 @@
 import '../constants/expressions.dart';
 import '../elements/entities.dart';
 import '../elements/types.dart';
+import '../js_emitter/native_emitter.dart';
 import '../native/enqueue.dart';
 import '../native/behavior.dart';
 import '../options.dart';
@@ -351,6 +352,7 @@
   final Namer _namer;
   final OneShotInterceptorData _oneShotInterceptorData;
   final RuntimeTypesChecksBuilder _rtiChecksBuilder;
+  final NativeEmitter _nativeEmitter;
 
   CodegenImpactTransformer(
       this._options,
@@ -363,7 +365,8 @@
       this._nativeCodegenEnqueuer,
       this._namer,
       this._oneShotInterceptorData,
-      this._rtiChecksBuilder);
+      this._rtiChecksBuilder,
+      this._nativeEmitter);
 
   void onIsCheckForCodegen(DartType type, TransformedWorldImpact transformed) {
     if (type.isDynamic) return;
@@ -443,6 +446,15 @@
       _rtiChecksBuilder.registerGenericInstantiation(instantiation);
     }
 
+    for (NativeBehavior behavior in impact.nativeBehaviors) {
+      _nativeCodegenEnqueuer.registerNativeBehavior(
+          transformed, behavior, impact);
+    }
+
+    for (FunctionEntity function in impact.nativeMethods) {
+      _nativeEmitter.nativeMethods.add(function);
+    }
+
     // TODO(johnniwinther): Remove eager registration.
     return transformed;
   }
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 b59b9f2..5e7d7a5 100644
--- a/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart
+++ b/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart
@@ -73,73 +73,6 @@
   /// Returns true, if the emitter supports reflection.
   bool get supportsReflection => _emitterFactory.supportsReflection;
 
-  /// Returns the closure expression of a static function.
-  jsAst.Expression isolateStaticClosureAccess(FunctionEntity element) {
-    return emitter.isolateStaticClosureAccess(element);
-  }
-
-  /// Returns the JS function that must be invoked to get the value of the
-  /// lazily initialized static.
-  jsAst.Expression isolateLazyInitializerAccess(FieldEntity element) {
-    return emitter.isolateLazyInitializerAccess(element);
-  }
-
-  /// Returns the JS code for accessing the embedded [global].
-  jsAst.Expression generateEmbeddedGlobalAccess(String global) {
-    return emitter.generateEmbeddedGlobalAccess(global);
-  }
-
-  /// Returns the JS code for accessing the given [constant].
-  jsAst.Expression constantReference(ConstantValue constant) {
-    return emitter.constantReference(constant);
-  }
-
-  jsAst.Expression staticFieldAccess(FieldEntity e) {
-    return emitter.staticFieldAccess(e);
-  }
-
-  /// Returns the JS function representing the given function.
-  ///
-  /// The function must be invoked and can not be used as closure.
-  jsAst.Expression staticFunctionAccess(FunctionEntity e) {
-    return emitter.staticFunctionAccess(e);
-  }
-
-  /// Returns the JS constructor of the given element.
-  ///
-  /// The returned expression must only be used in a JS `new` expression.
-  jsAst.Expression constructorAccess(ClassEntity e) {
-    return emitter.constructorAccess(e);
-  }
-
-  /// Returns the JS prototype of the given class [e].
-  jsAst.Expression prototypeAccess(ClassEntity e,
-      {bool hasBeenInstantiated: false}) {
-    return emitter.prototypeAccess(e, hasBeenInstantiated);
-  }
-
-  /// Returns the JS prototype of the given interceptor class [e].
-  jsAst.Expression interceptorPrototypeAccess(ClassEntity e) {
-    return jsAst.js('#.prototype', interceptorClassAccess(e));
-  }
-
-  /// Returns the JS constructor of the given interceptor class [e].
-  jsAst.Expression interceptorClassAccess(ClassEntity e) {
-    return emitter.interceptorClassAccess(e);
-  }
-
-  /// Returns the JS expression representing the type [e].
-  ///
-  /// The given type [e] might be a Typedef.
-  jsAst.Expression typeAccess(Entity e) {
-    return emitter.typeAccess(e);
-  }
-
-  /// Returns the JS template for the given [builtin].
-  jsAst.Template builtinTemplateFor(JsBuiltin builtin) {
-    return emitter.templateForBuiltin(builtin);
-  }
-
   void _finalizeRti(CodegenWorld codegenWorld) {
     // Compute the required type checks to know which classes need a
     // 'is$' method.
@@ -247,8 +180,10 @@
   jsAst.Expression constructorAccess(ClassEntity e);
 
   /// Returns the JS prototype of the given class [e].
-  jsAst.Expression prototypeAccess(
-      covariant ClassEntity e, bool hasBeenInstantiated);
+  jsAst.Expression prototypeAccess(ClassEntity e, {bool hasBeenInstantiated});
+
+  /// Returns the JS prototype of the given interceptor class [e].
+  jsAst.Expression interceptorPrototypeAccess(ClassEntity e);
 
   /// Returns the JS constructor of the given interceptor class [e].
   jsAst.Expression interceptorClassAccess(ClassEntity e);
diff --git a/pkg/compiler/lib/src/js_emitter/interceptor_stub_generator.dart b/pkg/compiler/lib/src/js_emitter/interceptor_stub_generator.dart
index a826fe9..eeecd6ff 100644
--- a/pkg/compiler/lib/src/js_emitter/interceptor_stub_generator.dart
+++ b/pkg/compiler/lib/src/js_emitter/interceptor_stub_generator.dart
@@ -23,12 +23,12 @@
 import '../universe/selector.dart' show Selector;
 import '../world.dart' show JClosedWorld;
 
-import 'code_emitter_task.dart' show CodeEmitterTask, Emitter;
+import 'code_emitter_task.dart' show Emitter;
 
 class InterceptorStubGenerator {
   final CompilerOptions _options;
   final CommonElements _commonElements;
-  final CodeEmitterTask _emitterTask;
+  final Emitter _emitter;
   final NativeCodegenEnqueuer _nativeCodegenEnqueuer;
   final Namer _namer;
   final OneShotInterceptorData _oneShotInterceptorData;
@@ -39,7 +39,7 @@
   InterceptorStubGenerator(
       this._options,
       this._commonElements,
-      this._emitterTask,
+      this._emitter,
       this._nativeCodegenEnqueuer,
       this._namer,
       this._oneShotInterceptorData,
@@ -51,11 +51,9 @@
 
   InterceptorData get _interceptorData => _closedWorld.interceptorData;
 
-  Emitter get _emitter => _emitterTask.emitter;
-
   jsAst.Expression generateGetInterceptorMethod(Set<ClassEntity> classes) {
     jsAst.Expression interceptorFor(ClassEntity cls) {
-      return _emitterTask.interceptorPrototypeAccess(cls);
+      return _emitter.interceptorPrototypeAccess(cls);
     }
 
     /// Build a JavaScript AST node for doing a type check on
diff --git a/pkg/compiler/lib/src/js_emitter/native_emitter.dart b/pkg/compiler/lib/src/js_emitter/native_emitter.dart
index 5836b24..7be3f0d 100644
--- a/pkg/compiler/lib/src/js_emitter/native_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/native_emitter.dart
@@ -269,7 +269,7 @@
         if (stubParameter.name == name) {
           type = type.unaliased;
           if (type.isFunctionType) {
-            closureConverter ??= _emitterTask
+            closureConverter ??= _emitterTask.emitter
                 .staticFunctionAccess(_commonElements.closureConverter);
 
             // The parameter type is a function type either directly or through
diff --git a/pkg/compiler/lib/src/js_emitter/parameter_stub_generator.dart b/pkg/compiler/lib/src/js_emitter/parameter_stub_generator.dart
index e57d145..14ae576 100644
--- a/pkg/compiler/lib/src/js_emitter/parameter_stub_generator.dart
+++ b/pkg/compiler/lib/src/js_emitter/parameter_stub_generator.dart
@@ -23,12 +23,14 @@
 
 import 'model.dart';
 
-import 'code_emitter_task.dart' show CodeEmitterTask, Emitter;
+import 'code_emitter_task.dart' show Emitter;
+import 'native_emitter.dart';
 
 class ParameterStubGenerator {
   static final Set<Selector> emptySelectorSet = new Set<Selector>();
 
-  final CodeEmitterTask _emitterTask;
+  final Emitter _emitter;
+  final NativeEmitter _nativeEmitter;
   final Namer _namer;
   final RuntimeTypesEncoder _rtiEncoder;
   final NativeData _nativeData;
@@ -38,7 +40,8 @@
   final SourceInformationStrategy _sourceInformationStrategy;
 
   ParameterStubGenerator(
-      this._emitterTask,
+      this._emitter,
+      this._nativeEmitter,
       this._namer,
       this._rtiEncoder,
       this._nativeData,
@@ -47,8 +50,6 @@
       this._closedWorld,
       this._sourceInformationStrategy);
 
-  Emitter get _emitter => _emitterTask.emitter;
-
   JElementEnvironment get _elementEnvironment =>
       _closedWorld.elementEnvironment;
 
@@ -190,7 +191,7 @@
 
     var body; // List or jsAst.Statement.
     if (_nativeData.hasFixedBackendName(member)) {
-      body = _emitterTask.nativeEmitter.generateParameterStubStatements(
+      body = _nativeEmitter.generateParameterStubStatements(
           member,
           isInterceptedMethod,
           _namer.invocationName(selector),
@@ -206,7 +207,7 @@
         // Instead we need to call the statically resolved target.
         //   `<class>.prototype.bar$1.call(this, argument0, ...)`.
         body = js.statement('return #.#.call(this, #);', [
-          _emitterTask.prototypeAccess(superClass, hasBeenInstantiated: true),
+          _emitter.prototypeAccess(superClass, hasBeenInstantiated: true),
           methodName,
           targetArguments
         ]);
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 3eb330e..cd297c8 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
@@ -342,7 +342,7 @@
     InterceptorStubGenerator stubGenerator = new InterceptorStubGenerator(
         _options,
         _commonElements,
-        _task,
+        _task.emitter,
         _nativeCodegenEnqueuer,
         _namer,
         _oneShotInterceptorData,
@@ -969,7 +969,8 @@
     if (!_methodNeedsStubs(element)) return const <ParameterStubMethod>[];
 
     ParameterStubGenerator generator = new ParameterStubGenerator(
-        _task,
+        _task.emitter,
+        _task.nativeEmitter,
         _namer,
         _rtiEncoder,
         _nativeData,
@@ -1015,7 +1016,7 @@
     InterceptorStubGenerator stubGenerator = new InterceptorStubGenerator(
         _options,
         _commonElements,
-        _task,
+        _task.emitter,
         _nativeCodegenEnqueuer,
         _namer,
         _oneShotInterceptorData,
@@ -1111,7 +1112,7 @@
     InterceptorStubGenerator stubGenerator = new InterceptorStubGenerator(
         _options,
         _commonElements,
-        _task,
+        _task.emitter,
         _nativeCodegenEnqueuer,
         _namer,
         _oneShotInterceptorData,
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 546077f..6bf225e 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart
@@ -110,14 +110,19 @@
   }
 
   @override
-  js.PropertyAccess prototypeAccess(
-      ClassEntity element, bool hasBeenInstantiated) {
+  js.PropertyAccess prototypeAccess(ClassEntity element,
+      {bool hasBeenInstantiated}) {
     js.Expression constructor =
         hasBeenInstantiated ? constructorAccess(element) : typeAccess(element);
     return js.js('#.prototype', constructor);
   }
 
   @override
+  js.Expression interceptorPrototypeAccess(ClassEntity e) {
+    return js.js('#.prototype', interceptorClassAccess(e));
+  }
+
+  @override
   js.Template templateForBuiltin(JsBuiltin builtin) {
     switch (builtin) {
       case JsBuiltin.dartObjectConstructor:
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 633e642..1d5c23d 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
@@ -621,11 +621,14 @@
       // TODO(29455): 'hunkHelpers' displaces other names, so don't minify it.
       'hunkHelpers': js.VariableDeclaration('hunkHelpers', allowRename: false),
       'directAccessTestExpression': js.js(directAccessTestExpression),
-      'cyclicThrow': backend.emitter
+      'cyclicThrow': backend.emitterTask.emitter
           .staticFunctionAccess(_closedWorld.commonElements.cyclicThrowHelper),
       'operatorIsPrefix': js.string(namer.operatorIsPrefix),
-      'tearOffCode': new js.Block(buildTearOffCode(compiler.options,
-          backend.emitter.emitter, backend.namer, _closedWorld.commonElements)),
+      'tearOffCode': new js.Block(buildTearOffCode(
+          compiler.options,
+          backend.emitterTask.emitter,
+          backend.namer,
+          _closedWorld.commonElements)),
       'embeddedTypes': generateEmbeddedGlobalAccess(TYPES),
       'embeddedInterceptorTags':
           generateEmbeddedGlobalAccess(INTERCEPTORS_BY_TAG),
diff --git a/pkg/compiler/lib/src/js_model/element_map.dart b/pkg/compiler/lib/src/js_model/element_map.dart
index f435fbd..1c7f8f8 100644
--- a/pkg/compiler/lib/src/js_model/element_map.dart
+++ b/pkg/compiler/lib/src/js_model/element_map.dart
@@ -141,8 +141,7 @@
   LibraryEntity getLibrary(ir.Library node);
 
   /// Returns the [js.Template] for the `JsBuiltin` [constant] value.
-  js.Template getJsBuiltinTemplate(
-      ConstantValue constant, CodeEmitterTask emitter);
+  js.Template getJsBuiltinTemplate(ConstantValue constant, Emitter emitter);
 
   /// Returns a [Spannable] for a message pointing to the IR [node] in the
   /// context of [member].
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 be39539..30418af 100644
--- a/pkg/compiler/lib/src/js_model/element_map_impl.dart
+++ b/pkg/compiler/lib/src/js_model/element_map_impl.dart
@@ -2111,12 +2111,11 @@
   }
 
   @override
-  js.Template getJsBuiltinTemplate(
-      ConstantValue constant, CodeEmitterTask emitter) {
+  js.Template getJsBuiltinTemplate(ConstantValue constant, Emitter emitter) {
     int index = extractEnumIndexFromConstantValue(
         constant, commonElements.jsBuiltinEnum);
     if (index == null) return null;
-    return emitter.builtinTemplateFor(JsBuiltin.values[index]);
+    return emitter.templateForBuiltin(JsBuiltin.values[index]);
   }
 }
 
diff --git a/pkg/compiler/lib/src/js_model/js_strategy.dart b/pkg/compiler/lib/src/js_model/js_strategy.dart
index b57bd96..f07e522 100644
--- a/pkg/compiler/lib/src/js_model/js_strategy.dart
+++ b/pkg/compiler/lib/src/js_model/js_strategy.dart
@@ -12,6 +12,7 @@
 import '../common/tasks.dart';
 import '../compiler.dart';
 import '../deferred_load.dart';
+import '../dump_info.dart';
 import '../elements/entities.dart';
 import '../enqueue.dart';
 import '../io/kernel_source_information.dart'
@@ -23,13 +24,17 @@
 import '../js/js_source_mapping.dart';
 import '../js_backend/backend.dart';
 import '../js_backend/inferred_data.dart';
+import '../js_backend/namer.dart';
 import '../js_backend/native_data.dart';
+import '../js_emitter/code_emitter_task.dart';
 import '../kernel/kernel_strategy.dart';
 import '../native/behavior.dart';
+import '../options.dart';
 import '../ssa/builder_kernel.dart';
 import '../ssa/nodes.dart';
 import '../ssa/ssa.dart';
 import '../ssa/types.dart';
+import '../tracer.dart';
 import '../universe/codegen_world_builder.dart';
 import '../universe/selector.dart';
 import '../universe/world_builder.dart';
@@ -92,13 +97,19 @@
   }
 
   @override
-  SsaBuilder createSsaBuilder(CompilerTask task, JavaScriptBackend backend,
+  SsaBuilder createSsaBuilder(CompilerTask task, CodegenInputs codegen,
       SourceInformationStrategy sourceInformationStrategy) {
     return new KernelSsaBuilder(
         task,
-        backend.compiler,
+        _compiler.options,
+        _compiler.reporter,
+        _compiler.dumpInfoTask,
         // ignore:deprecated_member_use_from_same_package
-        elementMap);
+        elementMap,
+        codegen.namer,
+        codegen.emitter,
+        codegen.tracer,
+        sourceInformationStrategy);
   }
 
   @override
@@ -167,33 +178,50 @@
 
 /// Task for building SSA from kernel IR loaded from .dill.
 class KernelSsaBuilder implements SsaBuilder {
-  final CompilerTask task;
-  final Compiler _compiler;
+  final CompilerTask _task;
+  final CompilerOptions _options;
+  final DiagnosticReporter _reporter;
+  final DumpInfoTask _dumpInfoTask;
   final JsToElementMap _elementMap;
+  final Namer _namer;
+  final Emitter _emitter;
+  final Tracer _tracer;
+  final SourceInformationStrategy _sourceInformationStrategy;
+
+  // TODO(johnniwinther,sra): Inlining decisions should not be based on the
+  // order in which ssa graphs are built.
   FunctionInlineCache _inlineCache;
 
-  KernelSsaBuilder(this.task, this._compiler, this._elementMap);
+  KernelSsaBuilder(
+      this._task,
+      this._options,
+      this._reporter,
+      this._dumpInfoTask,
+      this._elementMap,
+      this._namer,
+      this._emitter,
+      this._tracer,
+      this._sourceInformationStrategy);
 
   @override
   HGraph build(CodegenWorkItem work, JClosedWorld closedWorld,
       GlobalTypeInferenceResults results) {
     _inlineCache ??= new FunctionInlineCache(closedWorld.annotationsData);
-    return task.measure(() {
+    return _task.measure(() {
       KernelSsaGraphBuilder builder = new KernelSsaGraphBuilder(
-          _compiler.options,
-          _compiler.reporter,
+          _options,
+          _reporter,
           work.element,
           _elementMap.getMemberThisType(work.element),
-          _compiler.dumpInfoTask,
+          _dumpInfoTask,
           _elementMap,
           results,
           closedWorld,
           work.registry,
-          _compiler.backend.namer,
-          _compiler.backend.emitter,
-          _compiler.backend.tracer,
-          _compiler.backend.emitter.nativeEmitter,
-          _compiler.backend.sourceInformationStrategy,
+          _namer,
+          _emitter,
+          _tracer,
+          _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 1811d96..2f3ace7a 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -37,7 +37,6 @@
 import '../js_backend/native_data.dart';
 import '../js_backend/runtime_types.dart';
 import '../js_emitter/code_emitter_task.dart';
-import '../js_emitter/js_emitter.dart' show NativeEmitter;
 import '../js_model/locals.dart' show JumpVisitor;
 import '../js_model/elements.dart' show JGeneratorBody;
 import '../js_model/element_map.dart';
@@ -114,7 +113,7 @@
 
   final CompilerOptions options;
   final DiagnosticReporter reporter;
-  final CodeEmitterTask _emitter;
+  final Emitter _emitter;
   final Namer _namer;
   final MemberEntity targetElement;
   final MemberEntity _initialTargetElement;
@@ -143,8 +142,6 @@
   LoopHandler _loopHandler;
   TypeBuilder _typeBuilder;
 
-  final NativeEmitter _nativeEmitter;
-
   /// True if we are visiting the expression of a throw statement; we assume
   /// this is a slow path.
   bool _inExpressionOfThrow = false;
@@ -171,7 +168,6 @@
       this._namer,
       this._emitter,
       this._tracer,
-      this._nativeEmitter,
       this._sourceInformationStrategy,
       this._inlineCache)
       : this.targetElement = _effectiveTargetElementFor(_initialTargetElement),
@@ -1491,7 +1487,7 @@
         parameterStructure: function.parameterStructure);
 
     if (closedWorld.nativeData.isNativeMember(targetElement)) {
-      _nativeEmitter.nativeMethods.add(targetElement);
+      registry.registerNativeMethod(targetElement);
       String nativeName;
       if (closedWorld.nativeData.hasFixedBackendName(targetElement)) {
         nativeName = closedWorld.nativeData.getFixedBackendName(targetElement);
@@ -4716,7 +4712,7 @@
   HForeignCode _invokeJsInteropFunction(
       FunctionEntity element, List<HInstruction> arguments) {
     assert(closedWorld.nativeData.isJsInteropMember(element));
-    _nativeEmitter.nativeMethods.add(element);
+    registry.registerNativeMethod(element);
 
     if (element is ConstructorEntity &&
         element.isFactoryConstructor &&
diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart
index 73e35e3..6bfaa83 100644
--- a/pkg/compiler/lib/src/ssa/codegen.dart
+++ b/pkg/compiler/lib/src/ssa/codegen.dart
@@ -10,7 +10,7 @@
 import '../common.dart';
 import '../common/names.dart';
 import '../common/codegen.dart' show CodegenRegistry, CodegenWorkItem;
-import '../common/tasks.dart' show CompilerTask;
+import '../common/tasks.dart' show Measurer, CompilerTask;
 import '../constants/constant_system.dart' as constant_system;
 import '../constants/values.dart';
 import '../common_elements.dart' show JCommonElements;
@@ -29,7 +29,6 @@
 import '../js_emitter/code_emitter_task.dart';
 import '../js_model/elements.dart' show JGeneratorBody;
 import '../native/behavior.dart';
-import '../native/enqueue.dart';
 import '../options.dart';
 import '../tracer.dart';
 import '../universe/call_structure.dart' show CallStructure;
@@ -47,12 +46,12 @@
 }
 
 class SsaCodeGeneratorTask extends CompilerTask {
-  final JavaScriptBackend backend;
-  final SourceInformationStrategy sourceInformationFactory;
+  final CompilerOptions _options;
+  final SourceInformationStrategy sourceInformationStrategy;
 
-  SsaCodeGeneratorTask(JavaScriptBackend backend, this.sourceInformationFactory)
-      : this.backend = backend,
-        super(backend.compiler.measurer);
+  SsaCodeGeneratorTask(
+      Measurer measurer, this._options, this.sourceInformationStrategy)
+      : super(measurer);
 
   @override
   String get name => 'SSA code generator';
@@ -61,7 +60,7 @@
       List<js.Parameter> parameters, js.Block body) {
     js.Fun finish(js.AsyncModifier asyncModifier) {
       return new js.Fun(parameters, body, asyncModifier: asyncModifier)
-          .withSourceInformation(sourceInformationFactory
+          .withSourceInformation(sourceInformationStrategy
               .createBuilderForContext(element)
               .buildDeclaration(element));
     }
@@ -79,67 +78,65 @@
     }
   }
 
-  js.Expression generateCode(
-      CodegenWorkItem work, HGraph graph, JClosedWorld closedWorld) {
+  js.Expression generateCode(CodegenWorkItem work, HGraph graph,
+      CodegenInputs codegen, JClosedWorld closedWorld) {
     if (work.element.isField) {
-      return generateLazyInitializer(work, graph, closedWorld);
+      return generateLazyInitializer(work, graph, codegen, closedWorld);
     } else {
-      return generateMethod(work, graph, closedWorld);
+      return generateMethod(work, graph, codegen, closedWorld);
     }
   }
 
-  js.Expression generateLazyInitializer(
-      CodegenWorkItem work, HGraph graph, JClosedWorld closedWorld) {
+  js.Expression generateLazyInitializer(CodegenWorkItem work, HGraph graph,
+      CodegenInputs codegen, JClosedWorld closedWorld) {
     return measure(() {
-      backend.tracer.traceGraph("codegen", graph);
-      SourceInformation sourceInformation = sourceInformationFactory
+      codegen.tracer.traceGraph("codegen", graph);
+      SourceInformation sourceInformation = sourceInformationStrategy
           .createBuilderForContext(work.element)
           .buildDeclaration(work.element);
-      SsaCodeGenerator codegen = new SsaCodeGenerator(
+      SsaCodeGenerator codeGenerator = new SsaCodeGenerator(
           this,
-          backend.compiler.options,
-          backend.emitter,
-          backend.nativeCodegenEnqueuer,
-          backend.checkedModeHelpers,
-          backend.oneShotInterceptorData,
-          backend.rtiSubstitutions,
-          backend.rtiEncoder,
-          backend.namer,
-          backend.superMemberData,
-          backend.tracer,
+          _options,
+          codegen.emitter,
+          codegen.checkedModeHelpers,
+          codegen.oneShotInterceptorData,
+          codegen.rtiSubstitutions,
+          codegen.rtiEncoder,
+          codegen.namer,
+          codegen.superMemberData,
+          codegen.tracer,
           closedWorld,
           work);
-      codegen.visitGraph(graph);
-      return new js.Fun(codegen.parameters, codegen.body)
+      codeGenerator.visitGraph(graph);
+      return new js.Fun(codeGenerator.parameters, codeGenerator.body)
           .withSourceInformation(sourceInformation);
     });
   }
 
-  js.Expression generateMethod(
-      CodegenWorkItem work, HGraph graph, JClosedWorld closedWorld) {
+  js.Expression generateMethod(CodegenWorkItem work, HGraph graph,
+      CodegenInputs codegen, JClosedWorld closedWorld) {
     return measure(() {
       FunctionEntity element = work.element;
       if (element.asyncMarker != AsyncMarker.SYNC) {
         work.registry.registerAsyncMarker(element.asyncMarker);
       }
-      SsaCodeGenerator codegen = new SsaCodeGenerator(
+      SsaCodeGenerator codeGenerator = new SsaCodeGenerator(
           this,
-          backend.compiler.options,
-          backend.emitter,
-          backend.nativeCodegenEnqueuer,
-          backend.checkedModeHelpers,
-          backend.oneShotInterceptorData,
-          backend.rtiSubstitutions,
-          backend.rtiEncoder,
-          backend.namer,
-          backend.superMemberData,
-          backend.tracer,
+          _options,
+          codegen.emitter,
+          codegen.checkedModeHelpers,
+          codegen.oneShotInterceptorData,
+          codegen.rtiSubstitutions,
+          codegen.rtiEncoder,
+          codegen.namer,
+          codegen.superMemberData,
+          codegen.tracer,
           closedWorld,
           work);
-      codegen.visitGraph(graph);
-      backend.tracer.traceGraph("codegen", graph);
+      codeGenerator.visitGraph(graph);
+      codegen.tracer.traceGraph("codegen", graph);
       return buildJavaScriptFunction(graph.needsAsyncRewrite, work.element,
-          codegen.parameters, codegen.body);
+          codeGenerator.parameters, codeGenerator.body);
     });
   }
 }
@@ -165,8 +162,7 @@
 
   final CompilerTask _codegenTask;
   final CompilerOptions _options;
-  final CodeEmitterTask _emitter;
-  final NativeCodegenEnqueuer _nativeEnqueuer;
+  final Emitter _emitter;
   final CheckedModeHelpers _checkedModeHelpers;
   final OneShotInterceptorData _oneShotInterceptorData;
   final RuntimeTypesSubstitutions _rtiSubstitutions;
@@ -223,7 +219,6 @@
       this._codegenTask,
       this._options,
       this._emitter,
-      this._nativeEnqueuer,
       this._checkedModeHelpers,
       this._oneShotInterceptorData,
       this._rtiSubstitutions,
@@ -2274,8 +2269,7 @@
   void registerForeignTypes(HForeign node) {
     NativeBehavior nativeBehavior = node.nativeBehavior;
     if (nativeBehavior == null) return;
-    _nativeEnqueuer.registerNativeBehavior(
-        _registry.worldImpact, nativeBehavior, node);
+    _registry.registerNativeBehavior(nativeBehavior);
   }
 
   @override
@@ -3279,9 +3273,7 @@
       case TypeInfoExpressionKind.COMPLETE:
         int index = 0;
         js.Expression result = _rtiEncoder.getTypeRepresentation(
-            _emitter.emitter,
-            type,
-            (TypeVariableType variable) => arguments[index++]);
+            _emitter, type, (TypeVariableType variable) => arguments[index++]);
         assert(
             index == node.inputs.length,
             "Not all input is read for type ${type}: "
diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart
index daf93c6..88f9b6c 100644
--- a/pkg/compiler/lib/src/ssa/optimize.dart
+++ b/pkg/compiler/lib/src/ssa/optimize.dart
@@ -5,8 +5,7 @@
 import '../common.dart';
 import '../common/codegen.dart' show CodegenRegistry, CodegenWorkItem;
 import '../common/names.dart' show Selectors;
-import '../common/tasks.dart' show CompilerTask;
-import '../compiler.dart' show Compiler;
+import '../common/tasks.dart' show Measurer, CompilerTask;
 import '../constants/constant_system.dart' as constant_system;
 import '../constants/values.dart';
 import '../common_elements.dart' show JCommonElements;
@@ -40,28 +39,26 @@
 }
 
 class SsaOptimizerTask extends CompilerTask {
-  final JavaScriptBackend _backend;
+  final CompilerOptions _options;
 
   Map<HInstruction, Range> ranges = <HInstruction, Range>{};
 
   Map<MemberEntity, OptimizationTestLog> loggersForTesting;
 
-  SsaOptimizerTask(this._backend) : super(_backend.compiler.measurer);
+  SsaOptimizerTask(Measurer measurer, this._options) : super(measurer);
 
   @override
   String get name => 'SSA optimizer';
 
-  Compiler get _compiler => _backend.compiler;
-
-  CompilerOptions get _options => _compiler.options;
-
-  RuntimeTypesSubstitutions get _rtiSubstitutions => _backend.rtiSubstitutions;
-
-  void optimize(CodegenWorkItem work, HGraph graph, JClosedWorld closedWorld,
+  void optimize(
+      CodegenWorkItem work,
+      HGraph graph,
+      CodegenInputs codegen,
+      JClosedWorld closedWorld,
       GlobalTypeInferenceResults globalInferenceResults) {
     void runPhase(OptimizationPhase phase) {
       measureSubtask(phase.name, () => phase.visitGraph(graph));
-      _backend.tracer.traceGraph(phase.name, graph);
+      codegen.tracer.traceGraph(phase.name, graph);
       assert(graph.isValid(), 'Graph not valid after ${phase.name}');
     }
 
@@ -82,7 +79,7 @@
         // Run trivial instruction simplification first to optimize
         // some patterns useful for type conversion.
         new SsaInstructionSimplifier(globalInferenceResults, _options,
-            _rtiSubstitutions, closedWorld, registry, log),
+            codegen.rtiSubstitutions, closedWorld, registry, log),
         new SsaTypeConversionInserter(closedWorld),
         new SsaRedundantPhiEliminator(),
         new SsaDeadPhiEliminator(),
@@ -91,10 +88,10 @@
         // After type propagation, more instructions can be
         // simplified.
         new SsaInstructionSimplifier(globalInferenceResults, _options,
-            _rtiSubstitutions, closedWorld, registry, log),
+            codegen.rtiSubstitutions, closedWorld, registry, log),
         new SsaCheckInserter(trustPrimitives, closedWorld, boundsChecked),
         new SsaInstructionSimplifier(globalInferenceResults, _options,
-            _rtiSubstitutions, closedWorld, registry, log),
+            codegen.rtiSubstitutions, closedWorld, registry, log),
         new SsaCheckInserter(trustPrimitives, closedWorld, boundsChecked),
         new SsaTypePropagator(globalInferenceResults, _options,
             closedWorld.commonElements, closedWorld, log),
@@ -107,7 +104,7 @@
         new SsaTypePropagator(globalInferenceResults, _options,
             closedWorld.commonElements, closedWorld, log),
         codeMotion = new SsaCodeMotion(closedWorld.abstractValueDomain),
-        loadElimination = new SsaLoadElimination(_compiler, closedWorld),
+        loadElimination = new SsaLoadElimination(closedWorld),
         new SsaRedundantPhiEliminator(),
         new SsaDeadPhiEliminator(),
         // After GVN and load elimination the same value may be used in code
@@ -120,7 +117,7 @@
         // Previous optimizations may have generated new
         // opportunities for instruction simplification.
         new SsaInstructionSimplifier(globalInferenceResults, _options,
-            _rtiSubstitutions, closedWorld, registry, log),
+            codegen.rtiSubstitutions, closedWorld, registry, log),
         new SsaCheckInserter(trustPrimitives, closedWorld, boundsChecked),
       ];
       phases.forEach(runPhase);
@@ -143,7 +140,7 @@
           new SsaCodeMotion(closedWorld.abstractValueDomain),
           new SsaValueRangeAnalyzer(closedWorld, this),
           new SsaInstructionSimplifier(globalInferenceResults, _options,
-              _rtiSubstitutions, closedWorld, registry, log),
+              codegen.rtiSubstitutions, closedWorld, registry, log),
           new SsaCheckInserter(trustPrimitives, closedWorld, boundsChecked),
           new SsaSimplifyInterceptors(closedWorld, work.element.enclosingClass),
           new SsaDeadCodeEliminator(closedWorld, this),
@@ -155,7 +152,7 @@
           // Run the simplifier to remove unneeded type checks inserted by
           // type propagation.
           new SsaInstructionSimplifier(globalInferenceResults, _options,
-              _rtiSubstitutions, closedWorld, registry, log),
+              codegen.rtiSubstitutions, closedWorld, registry, log),
         ];
       }
       phases.forEach(runPhase);
@@ -2946,7 +2943,6 @@
 /// [HFieldGet]), when it knows the value stored in that memory location, and
 /// stores that overwrite with the same value.
 class SsaLoadElimination extends HBaseVisitor implements OptimizationPhase {
-  final Compiler compiler;
   final JClosedWorld _closedWorld;
   final JFieldAnalysis _fieldAnalysis;
   @override
@@ -2956,7 +2952,7 @@
   bool newGvnCandidates = false;
   HGraph _graph;
 
-  SsaLoadElimination(this.compiler, this._closedWorld)
+  SsaLoadElimination(this._closedWorld)
       : _fieldAnalysis = _closedWorld.fieldAnalysis;
 
   AbstractValueDomain get _abstractValueDomain =>
diff --git a/pkg/compiler/lib/src/ssa/ssa.dart b/pkg/compiler/lib/src/ssa/ssa.dart
index ed857e1..c6e963e 100644
--- a/pkg/compiler/lib/src/ssa/ssa.dart
+++ b/pkg/compiler/lib/src/ssa/ssa.dart
@@ -4,13 +4,22 @@
 
 library ssa;
 
-import '../common/codegen.dart' show CodegenWorkItem;
+import '../backend_strategy.dart';
+import '../common.dart';
+import '../common_elements.dart' show CommonElements, JElementEnvironment;
+import '../common/codegen.dart' show CodegenRegistry, CodegenWorkItem;
 import '../common/tasks.dart' show CompilerTask, Measurer;
-import '../elements/entities.dart' show MemberEntity;
+import '../elements/entities.dart';
+import '../elements/types.dart';
 import '../inferrer/types.dart';
 import '../io/source_information.dart';
 import '../js/js.dart' as js;
-import '../js_backend/backend.dart' show JavaScriptBackend, FunctionCompiler;
+import '../js/rewrite_async.dart';
+import '../js_backend/backend.dart' show CodegenInputs, FunctionCompiler;
+import '../js_model/elements.dart';
+import '../options.dart';
+import '../universe/call_structure.dart' show CallStructure;
+import '../universe/use.dart' show StaticUse;
 import '../world.dart' show JClosedWorld;
 
 import 'codegen.dart';
@@ -18,37 +27,49 @@
 import 'optimize.dart';
 
 class SsaFunctionCompiler implements FunctionCompiler {
+  final DiagnosticReporter _reporter;
   final SsaCodeGeneratorTask generator;
   final SsaBuilderTask _builder;
   final SsaOptimizerTask optimizer;
-  final JavaScriptBackend backend;
+  final SourceInformationStrategy sourceInformationStrategy;
 
-  SsaFunctionCompiler(JavaScriptBackend backend, Measurer measurer,
-      SourceInformationStrategy sourceInformationFactory)
-      : generator = new SsaCodeGeneratorTask(backend, sourceInformationFactory),
-        _builder = new SsaBuilderTask(backend, sourceInformationFactory),
-        optimizer = new SsaOptimizerTask(backend),
-        backend = backend;
+  SsaFunctionCompiler(
+      CompilerOptions options,
+      this._reporter,
+      BackendStrategy backendStrategy,
+      Measurer measurer,
+      this.sourceInformationStrategy)
+      : generator = new SsaCodeGeneratorTask(
+            measurer, options, sourceInformationStrategy),
+        _builder = new SsaBuilderTask(
+            measurer, backendStrategy, sourceInformationStrategy),
+        optimizer = new SsaOptimizerTask(measurer, options);
 
   @override
-  void onCodegenStart() {
-    _builder.onCodegenStart();
+  void onCodegenStart(CodegenInputs codegen) {
+    _builder.onCodegenStart(codegen);
   }
 
   /// Generates JavaScript code for `work.element`.
   /// Using the ssa builder, optimizer and code generator.
   @override
-  js.Fun compile(CodegenWorkItem work, JClosedWorld closedWorld,
+  js.Fun compile(
+      CodegenWorkItem work,
+      CodegenInputs codegen,
+      JClosedWorld closedWorld,
       GlobalTypeInferenceResults globalInferenceResults) {
     HGraph graph = _builder.build(work, closedWorld, globalInferenceResults);
     if (graph == null) return null;
-    optimizer.optimize(work, graph, closedWorld, globalInferenceResults);
+    optimizer.optimize(
+        work, graph, codegen, closedWorld, globalInferenceResults);
     MemberEntity element = work.element;
-    js.Expression result = generator.generateCode(work, graph, closedWorld);
+    js.Expression result =
+        generator.generateCode(work, graph, codegen, closedWorld);
     if (graph.needsAsyncRewrite) {
       SourceInformationBuilder sourceInformationBuilder =
-          backend.sourceInformationStrategy.createBuilderForContext(element);
-      result = backend.rewriteAsync(
+          sourceInformationStrategy.createBuilderForContext(element);
+      result = _rewriteAsync(
+          codegen,
           closedWorld.commonElements,
           closedWorld.elementEnvironment,
           work.registry,
@@ -61,6 +82,132 @@
     return result;
   }
 
+  js.Expression _rewriteAsync(
+      CodegenInputs codegen,
+      CommonElements commonElements,
+      JElementEnvironment elementEnvironment,
+      CodegenRegistry registry,
+      FunctionEntity element,
+      js.Expression code,
+      DartType asyncTypeParameter,
+      SourceInformation bodySourceInformation,
+      SourceInformation exitSourceInformation) {
+    if (element.asyncMarker == AsyncMarker.SYNC) return code;
+
+    AsyncRewriterBase rewriter = null;
+    js.Name name = codegen.namer.methodPropertyName(
+        element is JGeneratorBody ? element.function : element);
+
+    switch (element.asyncMarker) {
+      case AsyncMarker.ASYNC:
+        rewriter = _makeAsyncRewriter(
+            codegen,
+            commonElements,
+            elementEnvironment,
+            registry,
+            element,
+            code,
+            asyncTypeParameter,
+            name);
+        break;
+      case AsyncMarker.SYNC_STAR:
+        rewriter = new SyncStarRewriter(_reporter, element,
+            endOfIteration: codegen.emitter
+                .staticFunctionAccess(commonElements.endOfIteration),
+            iterableFactory: codegen.emitter
+                .staticFunctionAccess(commonElements.syncStarIterableFactory),
+            iterableFactoryTypeArguments:
+                _fetchItemType(codegen, asyncTypeParameter),
+            yieldStarExpression:
+                codegen.emitter.staticFunctionAccess(commonElements.yieldStar),
+            uncaughtErrorExpression: codegen.emitter
+                .staticFunctionAccess(commonElements.syncStarUncaughtError),
+            safeVariableName: codegen.namer.safeVariablePrefixForAsyncRewrite,
+            bodyName: codegen.namer.deriveAsyncBodyName(name));
+        registry.registerStaticUse(new StaticUse.staticInvoke(
+            commonElements.syncStarIterableFactory,
+            const CallStructure.unnamed(1, 1), [
+          elementEnvironment.getFunctionAsyncOrSyncStarElementType(element)
+        ]));
+        break;
+      case AsyncMarker.ASYNC_STAR:
+        rewriter = new AsyncStarRewriter(_reporter, element,
+            asyncStarHelper: codegen.emitter
+                .staticFunctionAccess(commonElements.asyncStarHelper),
+            streamOfController: codegen.emitter
+                .staticFunctionAccess(commonElements.streamOfController),
+            wrapBody:
+                codegen.emitter.staticFunctionAccess(commonElements.wrapBody),
+            newController: codegen.emitter.staticFunctionAccess(
+                commonElements.asyncStarStreamControllerFactory),
+            newControllerTypeArguments:
+                _fetchItemType(codegen, asyncTypeParameter),
+            safeVariableName: codegen.namer.safeVariablePrefixForAsyncRewrite,
+            yieldExpression: codegen.emitter
+                .staticFunctionAccess(commonElements.yieldSingle),
+            yieldStarExpression:
+                codegen.emitter.staticFunctionAccess(commonElements.yieldStar),
+            bodyName: codegen.namer.deriveAsyncBodyName(name));
+        registry.registerStaticUse(new StaticUse.staticInvoke(
+            commonElements.asyncStarStreamControllerFactory,
+            const CallStructure.unnamed(1, 1), [
+          elementEnvironment.getFunctionAsyncOrSyncStarElementType(element)
+        ]));
+        break;
+    }
+    return rewriter.rewrite(code, bodySourceInformation, exitSourceInformation);
+  }
+
+  /// Returns an optional expression that evaluates [type].  Returns `null` if
+  /// the type expression is determined by the outside context and should be
+  /// added as a function parameter to the rewritten code.
+  // TODO(sra): We could also return an empty list if the generator takes no
+  // type (e.g. due to rtiNeed optimization).
+  List<js.Expression> _fetchItemType(CodegenInputs codegen, DartType type) {
+    if (type == null) return null;
+    var ast =
+        codegen.rtiEncoder.getTypeRepresentation(codegen.emitter, type, null);
+    return <js.Expression>[ast];
+  }
+
+  AsyncRewriter _makeAsyncRewriter(
+      CodegenInputs codegen,
+      CommonElements commonElements,
+      JElementEnvironment elementEnvironment,
+      CodegenRegistry registry,
+      FunctionEntity element,
+      js.Expression code,
+      DartType elementType,
+      js.Name name) {
+    FunctionEntity startFunction = commonElements.asyncHelperStartSync;
+    FunctionEntity completerFactory = commonElements.asyncAwaitCompleterFactory;
+
+    List<js.Expression> itemTypeExpression =
+        _fetchItemType(codegen, elementType);
+
+    AsyncRewriter rewriter = new AsyncRewriter(_reporter, element,
+        asyncStart: codegen.emitter.staticFunctionAccess(startFunction),
+        asyncAwait: codegen.emitter
+            .staticFunctionAccess(commonElements.asyncHelperAwait),
+        asyncReturn: codegen.emitter
+            .staticFunctionAccess(commonElements.asyncHelperReturn),
+        asyncRethrow: codegen.emitter
+            .staticFunctionAccess(commonElements.asyncHelperRethrow),
+        wrapBody: codegen.emitter.staticFunctionAccess(commonElements.wrapBody),
+        completerFactory:
+            codegen.emitter.staticFunctionAccess(completerFactory),
+        completerFactoryTypeArguments: itemTypeExpression,
+        safeVariableName: codegen.namer.safeVariablePrefixForAsyncRewrite,
+        bodyName: codegen.namer.deriveAsyncBodyName(name));
+
+    registry.registerStaticUse(new StaticUse.staticInvoke(
+        completerFactory,
+        const CallStructure.unnamed(0, 1),
+        [elementEnvironment.getFunctionAsyncOrSyncStarElementType(element)]));
+
+    return rewriter;
+  }
+
   @override
   Iterable<CompilerTask> get tasks {
     return <CompilerTask>[_builder, optimizer, generator];
@@ -75,19 +222,20 @@
 }
 
 class SsaBuilderTask extends CompilerTask {
-  final JavaScriptBackend _backend;
+  final BackendStrategy _backendStrategy;
   final SourceInformationStrategy _sourceInformationFactory;
   SsaBuilder _builder;
 
-  SsaBuilderTask(this._backend, this._sourceInformationFactory)
-      : super(_backend.compiler.measurer);
+  SsaBuilderTask(
+      Measurer measurer, this._backendStrategy, this._sourceInformationFactory)
+      : super(measurer);
 
   @override
   String get name => 'SSA builder';
 
-  void onCodegenStart() {
-    _builder = _backend.compiler.backendStrategy
-        .createSsaBuilder(this, _backend, _sourceInformationFactory);
+  void onCodegenStart(CodegenInputs codegen) {
+    _builder = _backendStrategy.createSsaBuilder(
+        this, codegen, _sourceInformationFactory);
   }
 
   /// Creates the [HGraph] for [work] or returns `null` if no code is needed
diff --git a/tests/compiler/dart2js/codegen/is_function_test.dart b/tests/compiler/dart2js/codegen/is_function_test.dart
index 53cbaf3..e0b3aea 100644
--- a/tests/compiler/dart2js/codegen/is_function_test.dart
+++ b/tests/compiler/dart2js/codegen/is_function_test.dart
@@ -24,7 +24,7 @@
         memorySourceFiles: {'main.dart': SOURCE}, options: options);
     Expect.isTrue(result.isSuccess);
     Compiler compiler = result.compiler;
-    Program program = compiler.backend.emitter.emitter.programForTesting;
+    Program program = compiler.backend.emitterTask.emitter.programForTesting;
     var name = compiler.backend.namer.operatorIs(
         compiler.backendClosedWorldForTesting.commonElements.functionClass);
     for (Fragment fragment in program.fragments) {
diff --git a/tests/compiler/dart2js/deferred/load_graph_segmentation_test.dart b/tests/compiler/dart2js/deferred/load_graph_segmentation_test.dart
index e79c1f8..45bd24b 100644
--- a/tests/compiler/dart2js/deferred/load_graph_segmentation_test.dart
+++ b/tests/compiler/dart2js/deferred/load_graph_segmentation_test.dart
@@ -29,7 +29,7 @@
 
     var mainOutputUnit = closedWorld.outputUnitData.mainOutputUnit;
     var backend = compiler.backend;
-    var classes = backend.emitter.neededClasses;
+    var classes = backend.emitterTask.neededClasses;
     var inputElement = classes.where((e) => e.name == 'InputElement').single;
     dynamic lib1 = lookupLibrary("memory:lib1.dart");
     var foo1 = env.lookupLibraryMember(lib1, "foo1");
diff --git a/tests/compiler/dart2js/helpers/program_lookup.dart b/tests/compiler/dart2js/helpers/program_lookup.dart
index f4ce6d9e..6aff382 100644
--- a/tests/compiler/dart2js/helpers/program_lookup.dart
+++ b/tests/compiler/dart2js/helpers/program_lookup.dart
@@ -43,7 +43,7 @@
   final Namer namer;
 
   ProgramLookup(Compiler compiler)
-      : this.program = compiler.backend.emitter.emitter.programForTesting,
+      : this.program = compiler.backend.emitterTask.emitter.programForTesting,
         this.namer = compiler.backend.namer;
 
   Fragment getFragment(OutputUnit outputUnit) {
diff --git a/tests/compiler/dart2js/rti/type_representation_test.dart b/tests/compiler/dart2js/rti/type_representation_test.dart
index 6e7eaef..97bd3d6 100644
--- a/tests/compiler/dart2js/rti/type_representation_test.dart
+++ b/tests/compiler/dart2js/rti/type_representation_test.dart
@@ -85,12 +85,18 @@
       [String expectedTypedefRepresentation]) {
     bool encodeTypedefName = false;
     Expression expression = typeRepresentation.getTypeRepresentation(
-        backend.emitter.emitter, type, onVariable, (x) => encodeTypedefName);
+        backend.emitterTask.emitter,
+        type,
+        onVariable,
+        (x) => encodeTypedefName);
     Expect.stringEquals(expectedRepresentation, stringify(expression));
 
     encodeTypedefName = true;
     expression = typeRepresentation.getTypeRepresentation(
-        backend.emitter.emitter, type, onVariable, (x) => encodeTypedefName);
+        backend.emitterTask.emitter,
+        type,
+        onVariable,
+        (x) => encodeTypedefName);
     if (expectedTypedefRepresentation == null) {
       expectedTypedefRepresentation = expectedRepresentation;
     }
@@ -98,8 +104,8 @@
   }
 
   String getJsName(Entity cls) {
-    Expression name =
-        typeRepresentation.getJavaScriptClassName(cls, backend.emitter.emitter);
+    Expression name = typeRepresentation.getJavaScriptClassName(
+        cls, backend.emitterTask.emitter);
     return stringify(name);
   }