Introduce `--preview-dart-2` option to run new frontend compiler in flutter tools. (#11741)

This adds --preview-dart-2 flag that enables use of Dart 2.0 Frontend in Flutter tools.
diff --git a/packages/flutter_tools/lib/src/android/android_device.dart b/packages/flutter_tools/lib/src/android/android_device.dart
index 6f2d0ec..a70a553 100644
--- a/packages/flutter_tools/lib/src/android/android_device.dart
+++ b/packages/flutter_tools/lib/src/android/android_device.dart
@@ -343,7 +343,6 @@
     String route,
     DebuggingOptions debuggingOptions,
     Map<String, dynamic> platformArgs,
-    String kernelPath,
     bool prebuiltApplication: false,
     bool applicationNeedsRebuild: false,
     bool usesTerminalUi: true,
@@ -361,7 +360,6 @@
       await buildApk(
           target: mainPath,
           buildInfo: debuggingOptions.buildInfo,
-          kernelPath: kernelPath,
       );
       // Package has been built, so we can get the updated application ID and
       // activity name from the .apk.
diff --git a/packages/flutter_tools/lib/src/android/gradle.dart b/packages/flutter_tools/lib/src/android/gradle.dart
index 6196ea6..e483f2c 100644
--- a/packages/flutter_tools/lib/src/android/gradle.dart
+++ b/packages/flutter_tools/lib/src/android/gradle.dart
@@ -192,7 +192,7 @@
     settings.writeContents(localProperties);
 }
 
-Future<Null> buildGradleProject(BuildInfo buildInfo, String target, String kernelPath) async {
+Future<Null> buildGradleProject(BuildInfo buildInfo, String target) async {
   // Update the local.properties file with the build mode.
   // FlutterPlugin v1 reads local.properties to determine build mode. Plugin v2
   // uses the standard Android way to determine what to build, but we still
@@ -211,7 +211,7 @@
     case FlutterPluginVersion.managed:
       // Fall through. Managed plugin builds the same way as plugin v2.
     case FlutterPluginVersion.v2:
-      return _buildGradleProjectV2(gradle, buildInfo, target, kernelPath);
+      return _buildGradleProjectV2(gradle, buildInfo, target);
   }
 }
 
@@ -233,7 +233,7 @@
   printStatus('Built $gradleAppOutV1 (${getSizeAsMB(apkFile.lengthSync())}).');
 }
 
-Future<Null> _buildGradleProjectV2(String gradle, BuildInfo buildInfo, String target, String kernelPath) async {
+Future<Null> _buildGradleProjectV2(String gradle, BuildInfo buildInfo, String target) async {
   final GradleProject project = await _gradleProject();
   final String assembleTask = project.assembleTaskFor(buildInfo);
   if (assembleTask == null) {
@@ -266,8 +266,8 @@
   if (target != null) {
     command.add('-Ptarget=$target');
   }
-  if (kernelPath != null)
-    command.add('-Pkernel=$kernelPath');
+  if (buildInfo.previewDart2)
+    command.add('-Ppreview-dart-2=true');
   command.add(assembleTask);
   final int exitCode = await runCommandAndStreamOutput(
       command,
diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart
index 9cdd234..c6afb1a 100644
--- a/packages/flutter_tools/lib/src/artifacts.dart
+++ b/packages/flutter_tools/lib/src/artifacts.dart
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'package:meta/meta.dart';
+
 import 'base/context.dart';
 import 'base/file_system.dart';
 import 'base/platform.dart';
@@ -16,7 +18,12 @@
   snapshotDart,
   flutterFramework,
   vmSnapshotData,
-  isolateSnapshotData
+  isolateSnapshotData,
+  platformKernelDill,
+  platformLibrariesJson,
+  flutterPatchedSdkPath,
+  frontendServerSnapshotForEngineDartSdk,
+  engineDartSdkPath,
 }
 
 String _artifactToFileName(Artifact artifact) {
@@ -37,17 +44,37 @@
       return 'vm_isolate_snapshot.bin';
     case Artifact.isolateSnapshotData:
       return 'isolate_snapshot.bin';
+    case Artifact.platformKernelDill:
+      return 'platform.dill';
+    case Artifact.platformLibrariesJson:
+      return 'libraries.json';
+    case Artifact.flutterPatchedSdkPath:
+      assert(false, 'No filename for sdk path, should not be invoked');
+      return null;
+    case Artifact.engineDartSdkPath:
+      return 'dart-sdk';
+    case Artifact.frontendServerSnapshotForEngineDartSdk:
+      return 'frontend_server.dart.snapshot';
   }
   assert(false, 'Invalid artifact $artifact.');
   return null;
 }
 
+class EngineBuildPaths {
+  const EngineBuildPaths({ @required this.targetEngine, @required this.hostEngine }):
+      assert(targetEngine != null),
+      assert(hostEngine != null);
+
+  final String targetEngine;
+  final String hostEngine;
+}
+
 // Manages the engine artifacts of Flutter.
 abstract class Artifacts {
   static Artifacts get instance => context[Artifacts];
 
-  static void useLocalEngine(String engineSrcPath, String engineOutPath) {
-    context.setVariable(Artifacts, new LocalEngineArtifacts(engineSrcPath, engineOutPath));
+  static void useLocalEngine(String engineSrcPath, EngineBuildPaths engineBuildPaths) {
+    context.setVariable(Artifacts, new LocalEngineArtifacts(engineSrcPath, engineBuildPaths.targetEngine, engineBuildPaths.hostEngine));
   }
 
   // Returns the requested [artifact] for the [platform] and [mode] combination.
@@ -91,6 +118,7 @@
     switch (artifact) {
       case Artifact.dartIoEntriesTxt:
       case Artifact.dartVmEntryPointsTxt:
+      case Artifact.frontendServerSnapshotForEngineDartSdk:
         assert(mode != BuildMode.debug, 'Artifact $artifact only available in non-debug mode.');
         return fs.path.join(engineDir, _artifactToFileName(artifact));
       case Artifact.genSnapshot:
@@ -111,6 +139,7 @@
       case Artifact.genSnapshot:
       case Artifact.snapshotDart:
       case Artifact.flutterFramework:
+      case Artifact.frontendServerSnapshotForEngineDartSdk:
         return fs.path.join(engineDir, _artifactToFileName(artifact));
       default:
         assert(false, 'Artifact $artifact not available for platform $platform.');
@@ -118,6 +147,11 @@
     }
   }
 
+  String _getFlutterPatchedSdkPath() {
+    final String engineArtifactsPath = cache.getArtifactDirectory('engine').path;
+    return fs.path.join(engineArtifactsPath, 'common', 'flutter_patched_sdk');
+  }
+
   String _getHostArtifactPath(Artifact artifact, TargetPlatform platform) {
     switch (artifact) {
       case Artifact.genSnapshot:
@@ -131,9 +165,17 @@
       fallThrough:
       case Artifact.vmSnapshotData:
       case Artifact.isolateSnapshotData:
+      case Artifact.frontendServerSnapshotForEngineDartSdk:
+      case Artifact.engineDartSdkPath:
         final String engineArtifactsPath = cache.getArtifactDirectory('engine').path;
         final String platformDirName = getNameForTargetPlatform(platform);
         return fs.path.join(engineArtifactsPath, platformDirName, _artifactToFileName(artifact));
+      case Artifact.platformKernelDill:
+        return fs.path.join(_getFlutterPatchedSdkPath(), _artifactToFileName(artifact));
+      case Artifact.platformLibrariesJson:
+        return fs.path.join(_getFlutterPatchedSdkPath(), 'lib', _artifactToFileName(artifact));
+      case Artifact.flutterPatchedSdkPath:
+        return _getFlutterPatchedSdkPath();
       default:
         assert(false, 'Artifact $artifact not available for platform $platform.');
         return null;
@@ -179,8 +221,9 @@
 class LocalEngineArtifacts extends Artifacts {
   final String _engineSrcPath;
   final String engineOutPath; // TODO(goderbauer): This should be private.
+  String _hostEngineOutPath;
 
-  LocalEngineArtifacts(this._engineSrcPath, this.engineOutPath);
+  LocalEngineArtifacts(this._engineSrcPath, this.engineOutPath, this._hostEngineOutPath);
 
   @override
   String getArtifactPath(Artifact artifact, [TargetPlatform platform, BuildMode mode]) {
@@ -198,8 +241,18 @@
       case Artifact.isolateSnapshotData:
       case Artifact.vmSnapshotData:
         return fs.path.join(engineOutPath, 'gen', 'flutter', 'lib', 'snapshot', _artifactToFileName(artifact));
+      case Artifact.platformKernelDill:
+        return fs.path.join(_getFlutterPatchedSdkPath(), _artifactToFileName(artifact));
+      case Artifact.platformLibrariesJson:
+        return fs.path.join(_getFlutterPatchedSdkPath(), 'lib', _artifactToFileName(artifact));
       case Artifact.flutterFramework:
         return fs.path.join(engineOutPath, _artifactToFileName(artifact));
+      case Artifact.flutterPatchedSdkPath:
+        return _getFlutterPatchedSdkPath();
+      case Artifact.frontendServerSnapshotForEngineDartSdk:
+        return fs.path.join(_hostEngineOutPath, 'gen', _artifactToFileName(artifact));
+      case Artifact.engineDartSdkPath:
+        return fs.path.join(_hostEngineOutPath, 'dart-sdk');
     }
     assert(false, 'Invalid artifact $artifact.');
     return null;
@@ -210,6 +263,10 @@
     return fs.path.basename(engineOutPath);
   }
 
+  String _getFlutterPatchedSdkPath() {
+    return fs.path.join(engineOutPath, 'flutter_patched_sdk');
+  }
+
   String _genSnapshotPath() {
     const List<String> clangDirs = const <String>['clang_x86', 'clang_x64', 'clang_i386'];
     final String genSnapshotName = _artifactToFileName(Artifact.genSnapshot);
diff --git a/packages/flutter_tools/lib/src/build_info.dart b/packages/flutter_tools/lib/src/build_info.dart
index c9a5662..b4be334 100644
--- a/packages/flutter_tools/lib/src/build_info.dart
+++ b/packages/flutter_tools/lib/src/build_info.dart
@@ -10,7 +10,7 @@
 
 /// Information about a build to be performed or used.
 class BuildInfo {
-  const BuildInfo(this.mode, this.flavor);
+  const BuildInfo(this.mode, this.flavor, { this.previewDart2 });
 
   final BuildMode mode;
   /// Represents a custom Android product flavor or an Xcode scheme, null for
@@ -21,6 +21,9 @@
   /// Mode-Flavor (e.g. Release-Paid).
   final String flavor;
 
+  // Whether build should be done using Dart2 Frontend parser.
+  final bool previewDart2;
+
   static const BuildInfo debug = const BuildInfo(BuildMode.debug, null);
   static const BuildInfo profile = const BuildInfo(BuildMode.profile, null);
   static const BuildInfo release = const BuildInfo(BuildMode.release, null);
diff --git a/packages/flutter_tools/lib/src/cache.dart b/packages/flutter_tools/lib/src/cache.dart
index 007e7a5..30ddaed 100644
--- a/packages/flutter_tools/lib/src/cache.dart
+++ b/packages/flutter_tools/lib/src/cache.dart
@@ -255,6 +255,8 @@
   List<List<String>> _getBinaryDirs() {
     final List<List<String>> binaryDirs = <List<String>>[];
 
+    binaryDirs.add(<String>['common', 'flutter_patched_sdk.zip']);
+
     if (cache.includeAllPlatforms)
       binaryDirs
         ..addAll(_osxBinaryDirs)
@@ -281,18 +283,21 @@
 
   List<List<String>> get _osxBinaryDirs => <List<String>>[
     <String>['darwin-x64', 'darwin-x64/artifacts.zip'],
+    <String>['darwin-x64', 'dart-sdk-darwin-x64.zip'],
     <String>['android-arm-profile/darwin-x64', 'android-arm-profile/darwin-x64.zip'],
     <String>['android-arm-release/darwin-x64', 'android-arm-release/darwin-x64.zip'],
   ];
 
   List<List<String>> get _linuxBinaryDirs => <List<String>>[
     <String>['linux-x64', 'linux-x64/artifacts.zip'],
+    <String>['linux-x64', 'dart-sdk-linux-x64.zip'],
     <String>['android-arm-profile/linux-x64', 'android-arm-profile/linux-x64.zip'],
     <String>['android-arm-release/linux-x64', 'android-arm-release/linux-x64.zip'],
   ];
 
   List<List<String>> get _windowsBinaryDirs => <List<String>>[
     <String>['windows-x64', 'windows-x64/artifacts.zip'],
+    <String>['windows-x64', 'dart-sdk-windows-x64.zip'],
     <String>['android-arm-profile/windows-x64', 'android-arm-profile/windows-x64.zip'],
     <String>['android-arm-release/windows-x64', 'android-arm-release/windows-x64.zip'],
   ];
diff --git a/packages/flutter_tools/lib/src/commands/build_aot.dart b/packages/flutter_tools/lib/src/commands/build_aot.dart
index 3ca4219..4409374 100644
--- a/packages/flutter_tools/lib/src/commands/build_aot.dart
+++ b/packages/flutter_tools/lib/src/commands/build_aot.dart
@@ -13,6 +13,7 @@
 import '../base/process_manager.dart';
 import '../base/utils.dart';
 import '../build_info.dart';
+import '../compile.dart';
 import '../dart/package_map.dart';
 import '../globals.dart';
 import '../resident_runner.dart';
@@ -35,7 +36,8 @@
         allowed: <String>['android-arm', 'ios']
       )
       ..addFlag('interpreter')
-      ..addFlag('quiet', defaultsTo: false);
+      ..addFlag('quiet', defaultsTo: false)
+      ..addFlag('preview-dart-2', negatable: false);
   }
 
   @override
@@ -63,7 +65,8 @@
       platform,
       getBuildMode(),
       outputPath: argResults['output-dir'],
-      interpreter: argResults['interpreter']
+      interpreter: argResults['interpreter'],
+      previewDart2: argResults['preview-dart-2'],
     );
     status?.stop();
 
@@ -90,7 +93,8 @@
   TargetPlatform platform,
   BuildMode buildMode, {
   String outputPath,
-  bool interpreter: false
+  bool interpreter: false,
+  bool previewDart2: false,
 }) async {
   outputPath ??= getAotBuildDirectory();
   try {
@@ -99,7 +103,8 @@
       platform,
       buildMode,
       outputPath: outputPath,
-      interpreter: interpreter
+      interpreter: interpreter,
+      previewDart2: previewDart2,
     );
   } on String catch (error) {
     // Catch the String exceptions thrown from the `runCheckedSync` methods below.
@@ -114,7 +119,8 @@
   TargetPlatform platform,
   BuildMode buildMode, {
   String outputPath,
-  bool interpreter: false
+  bool interpreter: false,
+  bool previewDart2: false,
 }) async {
   outputPath ??= getAotBuildDirectory();
   if (!isAotBuildMode(buildMode) && !interpreter) {
@@ -137,7 +143,11 @@
   final String isolateSnapshotInstructions = fs.path.join(outputDir.path, 'isolate_snapshot_instr');
   final String dependencies = fs.path.join(outputDir.path, 'snapshot.d');
 
-  final String vmEntryPoints = artifacts.getArtifactPath(Artifact.dartVmEntryPointsTxt, platform, buildMode);
+  final String vmEntryPoints = artifacts.getArtifactPath(
+    Artifact.dartVmEntryPointsTxt,
+    platform,
+    buildMode,
+  );
   final String ioEntryPoints = artifacts.getArtifactPath(Artifact.dartIoEntriesTxt, platform, buildMode);
 
   final PackageMap packageMap = new PackageMap(PackageMap.globalPackagesPath);
@@ -266,6 +276,13 @@
     ]);
   }
 
+  if (previewDart2) {
+    mainPath = await compile(
+      sdkRoot: artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath),
+      mainPath: mainPath,
+    );
+  }
+
   genSnapshotCmd.add(mainPath);
 
   final SnapshotType snapshotType = new SnapshotType(platform, buildMode);
diff --git a/packages/flutter_tools/lib/src/commands/build_apk.dart b/packages/flutter_tools/lib/src/commands/build_apk.dart
index 791b1cc..63c757d 100644
--- a/packages/flutter_tools/lib/src/commands/build_apk.dart
+++ b/packages/flutter_tools/lib/src/commands/build_apk.dart
@@ -35,6 +35,7 @@
   BuildApkCommand() {
     usesTargetOption();
     addBuildModeFlags();
+    argParser.addFlag('preview-dart-2', negatable: false);
     usesFlavorOption();
     usesPubOption();
   }
@@ -51,16 +52,13 @@
   @override
   Future<Null> runCommand() async {
     await super.runCommand();
-
-    final BuildInfo buildInfo = getBuildInfo();
-    await buildApk(buildInfo: buildInfo, target: targetFile);
+    await buildApk(buildInfo: getBuildInfo(), target: targetFile);
   }
 }
 
 Future<Null> buildApk({
   String target,
-  BuildInfo buildInfo: BuildInfo.debug,
-  String kernelPath,
+  BuildInfo buildInfo: BuildInfo.debug
 }) async {
   if (!isProjectUsingGradle()) {
     throwToolExit(
@@ -81,5 +79,5 @@
     throwToolExit('Try re-installing or updating your Android SDK.');
   }
 
-  return buildGradleProject(buildInfo, target, kernelPath);
+  return buildGradleProject(buildInfo, target);
 }
diff --git a/packages/flutter_tools/lib/src/commands/build_flx.dart b/packages/flutter_tools/lib/src/commands/build_flx.dart
index 5495a71..9188cd5 100644
--- a/packages/flutter_tools/lib/src/commands/build_flx.dart
+++ b/packages/flutter_tools/lib/src/commands/build_flx.dart
@@ -20,7 +20,7 @@
     argParser.addOption('output-file', abbr: 'o', defaultsTo: defaultFlxOutputPath);
     argParser.addOption('snapshot', defaultsTo: defaultSnapshotPath);
     argParser.addOption('depfile', defaultsTo: defaultDepfilePath);
-    argParser.addOption('kernel');
+    argParser.addFlag('preview-dart-2', negatable: false);
     argParser.addOption('working-dir', defaultsTo: getAssetBuildDirectory());
     argParser.addFlag('report-licensed-packages', help: 'Whether to report the names of all the packages that are included in the application\'s LICENSE file.', defaultsTo: false);
     usesPubOption();
@@ -49,7 +49,7 @@
       depfilePath: argResults['depfile'],
       privateKeyPath: argResults['private-key'],
       workingDirPath: argResults['working-dir'],
-      kernelPath: argResults['kernel'],
+      previewDart2: argResults['preview-dart-2'],
       precompiledSnapshot: argResults['precompiled'],
       reportLicensedPackages: argResults['report-licensed-packages']
     );
diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart
index 7ca63a0..2229185 100644
--- a/packages/flutter_tools/lib/src/commands/run.dart
+++ b/packages/flutter_tools/lib/src/commands/run.dart
@@ -110,11 +110,10 @@
     argParser.addOption('use-application-binary',
         hide: !verboseHelp,
         help: 'Specify a pre-built application binary to use when running.');
-    argParser.addOption('kernel',
+    argParser.addFlag('preview-dart-2',
         hide: !verboseHelp,
-        help: 'Path to a pre-built kernel blob to use when running.\n'
-              'This option only exists for testing new kernel code execution on devices\n'
-              'and is not needed during normal application development.');
+        defaultsTo: false,
+        help: 'Preview Dart 2.0 functionality.');
     argParser.addOption('packages',
         hide: !verboseHelp,
         help: 'Specify the path to the .packages file.');
@@ -177,7 +176,7 @@
   @override
   Future<Map<String, String>> get usageValues async {
     final bool isEmulator = await devices[0].isLocalEmulator;
-    final String deviceType = devices.length == 1 
+    final String deviceType = devices.length == 1
             ? getNameForTargetPlatform(await devices[0].targetPlatform)
             : 'multiple';
 
@@ -300,7 +299,7 @@
     }
 
     final List<FlutterDevice> flutterDevices = devices.map((Device device) {
-      return new FlutterDevice(device);
+      return new FlutterDevice(device, previewDart2: argResults['preview-dart-2']);
     }).toList();
 
     ResidentRunner runner;
@@ -311,7 +310,7 @@
         debuggingOptions: _createDebuggingOptions(),
         benchmarkMode: argResults['benchmark'],
         applicationBinary: argResults['use-application-binary'],
-        kernelFilePath: argResults['kernel'],
+        previewDart2: argResults['preview-dart-2'],
         projectRootPath: argResults['project-root'],
         packagesFilePath: argResults['packages'],
         projectAssets: argResults['project-assets'],
@@ -324,6 +323,7 @@
         debuggingOptions: _createDebuggingOptions(),
         traceStartup: traceStartup,
         applicationBinary: argResults['use-application-binary'],
+        previewDart2: argResults['preview-dart-2'],
         stayResident: stayResident,
       );
     }
diff --git a/packages/flutter_tools/lib/src/compile.dart b/packages/flutter_tools/lib/src/compile.dart
new file mode 100644
index 0000000..9c11d37
--- /dev/null
+++ b/packages/flutter_tools/lib/src/compile.dart
@@ -0,0 +1,149 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:convert';
+
+import 'package:flutter_tools/src/base/process_manager.dart';
+import 'package:usage/uuid/uuid.dart';
+
+import 'artifacts.dart';
+import 'base/file_system.dart';
+import 'base/io.dart';
+import 'base/process_manager.dart';
+import 'globals.dart';
+
+String _dartExecutable() {
+  final String engineDartSdkPath = artifacts.getArtifactPath(
+    Artifact.engineDartSdkPath
+  );
+  return fs.path.join(engineDartSdkPath, 'bin', 'dart');
+}
+
+class _StdoutHandler {
+  String boundaryKey;
+  Completer<String> outputFilename = new Completer<String>();
+
+  void handler(String string) {
+    const String kResultPrefix = 'result ';
+    if (boundaryKey == null) {
+      if (string.startsWith(kResultPrefix))
+        boundaryKey = string.substring(kResultPrefix.length);
+    } else if (string.startsWith(boundaryKey))
+      outputFilename.complete(string.length > boundaryKey.length
+        ? string.substring(boundaryKey.length + 1)
+        : null);
+    else
+      printTrace('compile debug message: $string');
+  }
+}
+
+Future<String> compile({String sdkRoot, String mainPath}) async {
+  final String frontendServer = artifacts.getArtifactPath(
+    Artifact.frontendServerSnapshotForEngineDartSdk
+  );
+
+  // This is a URI, not a file path, so the forward slash is correct even on Windows.
+  if (!sdkRoot.endsWith('/'))
+    sdkRoot = '$sdkRoot/';
+  final Process server = await processManager.start(<String>[
+    _dartExecutable(),
+    frontendServer,
+    '--sdk-root',
+    sdkRoot,
+    mainPath
+  ]);
+
+  final _StdoutHandler stdoutHandler = new _StdoutHandler();
+  server.stderr
+    .transform(UTF8.decoder)
+    .listen((String s) { printTrace('compile debug message: $s'); });
+  server.stdout
+    .transform(UTF8.decoder)
+    .transform(const LineSplitter())
+    .listen(stdoutHandler.handler);
+  await server.exitCode;
+  return stdoutHandler.outputFilename.future;
+}
+
+/// Wrapper around incremental frontend server compiler, that communicates with
+/// server via stdin/stdout.
+///
+/// The wrapper is intended to stay resident in memory as user changes, reloads,
+/// restarts the Flutter app.
+class ResidentCompiler {
+  ResidentCompiler(this._sdkRoot) {
+    assert(_sdkRoot != null);
+    // This is a URI, not a file path, so the forward slash is correct even on Windows.
+    if (!_sdkRoot.endsWith('/'))
+      _sdkRoot = '$_sdkRoot/';
+  }
+
+  String _sdkRoot;
+  Process _server;
+  final _StdoutHandler stdoutHandler = new _StdoutHandler();
+
+  /// If invoked for the first time, it compiles Dart script identified by
+  /// [mainPath], [invalidatedFiles] list is ignored.
+  /// Otherwise, [mainPath] is ignored, but [invalidatedFiles] is recompiled
+  /// into new binary.
+  /// Binary file name is returned if compilation was successful, otherwise
+  /// `null` is returned.
+  Future<String> recompile(String mainPath, List<String> invalidatedFiles) async {
+    // First time recompile is called we actually have to compile the app from
+    // scratch ignoring list of invalidated files.
+    if (_server == null)
+      return _compile(mainPath);
+
+    final String inputKey = new Uuid().generateV4();
+    _server.stdin.writeln('recompile $inputKey');
+    for (String invalidatedFile in invalidatedFiles)
+      _server.stdin.writeln(invalidatedFile);
+    _server.stdin.writeln(inputKey);
+
+    return stdoutHandler.outputFilename.future;
+  }
+
+  Future<String> _compile(String scriptFilename) async {
+    if (_server == null) {
+      final String frontendServer = artifacts.getArtifactPath(
+        Artifact.frontendServerSnapshotForEngineDartSdk
+      );
+      _server = await processManager.start(<String>[
+        _dartExecutable(),
+        frontendServer,
+        '--sdk-root',
+        _sdkRoot,
+        '--incremental'
+      ]);
+    }
+    _server.stdout
+      .transform(UTF8.decoder)
+      .transform(const LineSplitter())
+      .listen(stdoutHandler.handler);
+    _server.stderr
+      .transform(UTF8.decoder)
+      .transform(const LineSplitter())
+      .listen((String s) { printTrace('compile debug message: $s'); });
+
+    _server.stdin.writeln('compile $scriptFilename');
+
+    return stdoutHandler.outputFilename.future;
+  }
+
+
+  /// Should be invoked when results of compilation are accepted by the client.
+  ///
+  /// Either [accept] or [reject] should be called after every [recompile] call.
+  void accept() {
+    _server.stdin.writeln('accept');
+  }
+
+  /// Should be invoked when results of compilation are rejected by the client.
+  ///
+  /// Either [accept] or [reject] should be called after every [recompile] call.
+  void reject() {
+    _server.stdin.writeln('reject');
+  }
+}
diff --git a/packages/flutter_tools/lib/src/devfs.dart b/packages/flutter_tools/lib/src/devfs.dart
index 72c0369..5a0e550 100644
--- a/packages/flutter_tools/lib/src/devfs.dart
+++ b/packages/flutter_tools/lib/src/devfs.dart
@@ -12,6 +12,7 @@
 import 'base/file_system.dart';
 import 'base/io.dart';
 import 'build_info.dart';
+import 'compile.dart';
 import 'dart/package_map.dart';
 import 'globals.dart';
 import 'vmservice.dart';
@@ -361,9 +362,12 @@
 
   /// Update files on the device and return the number of bytes sync'd
   Future<int> update({
+    String mainPath,
+    String target,
     AssetBundle bundle,
     bool bundleDirty: false,
     Set<String> fileFilter,
+    ResidentCompiler generator,
   }) async {
     // Mark all entries as possibly deleted.
     for (DevFSContent content in _entries.values) {
@@ -427,6 +431,18 @@
     });
     if (dirtyEntries.isNotEmpty) {
       printTrace('Updating files');
+      if (generator != null) {
+        final List<String> invalidatedFiles = <String>[];
+        dirtyEntries.forEach((Uri deviceUri, DevFSContent content) {
+          if (content is DevFSFileContent)
+            invalidatedFiles.add(content.file.uri.toString());
+        });
+        final String compiledBinary = await generator.recompile(mainPath, invalidatedFiles);
+        if (compiledBinary != null && compiledBinary.isNotEmpty)
+          dirtyEntries.putIfAbsent(Uri.parse(target + '.dill'),
+                  () => new DevFSFileContent(fs.file(compiledBinary)));
+      }
+
       if (_httpWriter != null) {
         try {
           await _httpWriter.write(dirtyEntries);
diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart
index 52434aa..9bc0634 100644
--- a/packages/flutter_tools/lib/src/device.dart
+++ b/packages/flutter_tools/lib/src/device.dart
@@ -240,7 +240,6 @@
     String route,
     DebuggingOptions debuggingOptions,
     Map<String, dynamic> platformArgs,
-    String kernelPath,
     bool prebuiltApplication: false,
     bool applicationNeedsRebuild: false,
     bool usesTerminalUi: true,
diff --git a/packages/flutter_tools/lib/src/flx.dart b/packages/flutter_tools/lib/src/flx.dart
index 2d9abf2..f51ff1e 100644
--- a/packages/flutter_tools/lib/src/flx.dart
+++ b/packages/flutter_tools/lib/src/flx.dart
@@ -4,11 +4,13 @@
 
 import 'dart:async';
 
+import 'artifacts.dart';
 import 'asset.dart';
 import 'base/build.dart';
 import 'base/common.dart';
 import 'base/file_system.dart';
 import 'build_info.dart';
+import 'compile.dart';
 import 'dart/package_map.dart';
 import 'devfs.dart';
 import 'globals.dart';
@@ -25,6 +27,7 @@
 const String _kKernelKey = 'kernel_blob.bin';
 const String _kSnapshotKey = 'snapshot_blob.bin';
 const String _kDylibKey = 'libapp.so';
+const String _kPlatformKernelKey = 'platform.dill';
 
 Future<Null> build({
   String mainPath: defaultMainPath,
@@ -35,7 +38,7 @@
   String privateKeyPath: defaultPrivateKeyPath,
   String workingDirPath,
   String packagesPath,
-  String kernelPath,
+  bool previewDart2 : false,
   bool precompiledSnapshot: false,
   bool reportLicensedPackages: false
 }) async {
@@ -46,7 +49,7 @@
   packagesPath ??= fs.path.absolute(PackageMap.globalPackagesPath);
   File snapshotFile;
 
-  if (!precompiledSnapshot) {
+  if (!precompiledSnapshot && !previewDart2) {
     ensureDirectoryExists(snapshotPath);
 
     // In a precompiled snapshot, the instruction buffer contains script
@@ -65,8 +68,13 @@
   }
 
   DevFSContent kernelContent;
-  if (kernelPath != null)
-    kernelContent = new DevFSFileContent(fs.file(kernelPath));
+  if (!precompiledSnapshot && previewDart2) {
+    final String kernelBinaryFilename = await compile(
+      sdkRoot: artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath),
+      mainPath: fs.file(mainPath).absolute.path
+    );
+    kernelContent = new DevFSFileContent(fs.file(kernelBinaryFilename));
+  }
 
   return assemble(
     manifestPath: manifestPath,
@@ -118,8 +126,11 @@
       .expand((DevFSContent content) => content.fileDependencies)
       .toList();
 
-  if (kernelContent != null)
+  if (kernelContent != null) {
+    final String platformKernelDill = artifacts.getArtifactPath(Artifact.platformKernelDill);
     zipBuilder.entries[_kKernelKey] = kernelContent;
+    zipBuilder.entries[_kPlatformKernelKey] = new DevFSFileContent(fs.file(platformKernelDill));
+  }
   if (snapshotFile != null)
     zipBuilder.entries[_kSnapshotKey] = new DevFSFileContent(snapshotFile);
   if (dylibFile != null)
diff --git a/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart b/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart
index 1587577..08af8c7 100644
--- a/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart
+++ b/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart
@@ -65,7 +65,7 @@
     DebuggingOptions debuggingOptions,
     Map<String, dynamic> platformArgs,
     bool prebuiltApplication: false,
-    String kernelPath,
+    bool previewDart2: false,
     bool applicationNeedsRebuild: false,
     bool usesTerminalUi: false,
   }) => new Future<Null>.error('unimplemented');
diff --git a/packages/flutter_tools/lib/src/ios/devices.dart b/packages/flutter_tools/lib/src/ios/devices.dart
index 1ad358c..5d311d1 100644
--- a/packages/flutter_tools/lib/src/ios/devices.dart
+++ b/packages/flutter_tools/lib/src/ios/devices.dart
@@ -170,7 +170,7 @@
     DebuggingOptions debuggingOptions,
     Map<String, dynamic> platformArgs,
     bool prebuiltApplication: false,
-    String kernelPath,
+    bool previewDart2: false,
     bool applicationNeedsRebuild: false,
     bool usesTerminalUi: true,
   }) async {
diff --git a/packages/flutter_tools/lib/src/ios/simulators.dart b/packages/flutter_tools/lib/src/ios/simulators.dart
index 03c73e4..ce98c7c 100644
--- a/packages/flutter_tools/lib/src/ios/simulators.dart
+++ b/packages/flutter_tools/lib/src/ios/simulators.dart
@@ -311,7 +311,7 @@
     String route,
     DebuggingOptions debuggingOptions,
     Map<String, dynamic> platformArgs,
-    String kernelPath,
+    bool previewDart2: false,
     bool prebuiltApplication: false,
     bool applicationNeedsRebuild: false,
     bool usesTerminalUi: true,
diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart
index 003531d..3e1db2a 100644
--- a/packages/flutter_tools/lib/src/resident_runner.dart
+++ b/packages/flutter_tools/lib/src/resident_runner.dart
@@ -8,6 +8,7 @@
 
 import 'android/gradle.dart';
 import 'application_package.dart';
+import 'artifacts.dart';
 import 'asset.dart';
 import 'base/common.dart';
 import 'base/file_system.dart';
@@ -16,6 +17,7 @@
 import 'base/terminal.dart';
 import 'base/utils.dart';
 import 'build_info.dart';
+import 'compile.dart';
 import 'dart/dependencies.dart';
 import 'dart/package_map.dart';
 import 'dependency_checker.dart';
@@ -32,10 +34,15 @@
   List<VMService> vmServices;
   DevFS devFS;
   ApplicationPackage package;
+  ResidentCompiler generator;
 
   StreamSubscription<String> _loggingSubscription;
 
-  FlutterDevice(this.device);
+  FlutterDevice(this.device, { bool previewDart2 : false }) {
+    if (previewDart2)
+      generator = new ResidentCompiler(
+        artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath));
+  }
 
   String viewFilter;
 
@@ -244,7 +251,6 @@
       platformArgs: platformArgs,
       route: route,
       prebuiltApplication: prebuiltMode,
-      kernelPath: hotRunner.kernelFilePath,
       applicationNeedsRebuild: shouldBuild || hasDirtyDependencies,
       usesTerminalUi: hotRunner.usesTerminalUI,
     );
@@ -319,6 +325,8 @@
   }
 
   Future<bool> updateDevFS({
+    String mainPath,
+    String target,
     AssetBundle bundle,
     bool bundleDirty: false,
     Set<String> fileFilter
@@ -330,9 +338,12 @@
     int bytes = 0;
     try {
       bytes = await devFS.update(
+        mainPath: mainPath,
+        target: target,
         bundle: bundle,
         bundleDirty: bundleDirty,
-        fileFilter: fileFilter
+        fileFilter: fileFilter,
+        generator: generator
       );
     } on DevFSException {
       devFSStatus.cancel();
@@ -342,6 +353,13 @@
     printTrace('Synced ${getSizeAsMB(bytes)}.');
     return true;
   }
+
+  void updateReloadStatus(bool wasReloadSuccessful) {
+    if (wasReloadSuccessful)
+      generator?.accept();
+    else
+      generator?.reject();
+  }
 }
 
 // Shared code between different resident application runners.
diff --git a/packages/flutter_tools/lib/src/run_cold.dart b/packages/flutter_tools/lib/src/run_cold.dart
index 95894b53..76d8bd6 100644
--- a/packages/flutter_tools/lib/src/run_cold.dart
+++ b/packages/flutter_tools/lib/src/run_cold.dart
@@ -20,6 +20,7 @@
     bool usesTerminalUI: true,
     this.traceStartup: false,
     this.applicationBinary,
+    this.previewDart2 : false,
     bool stayResident: true,
   }) : super(devices,
              target: target,
@@ -29,6 +30,7 @@
 
   final bool traceStartup;
   final String applicationBinary;
+  final bool previewDart2;
 
   @override
   Future<int> run({
diff --git a/packages/flutter_tools/lib/src/run_hot.dart b/packages/flutter_tools/lib/src/run_hot.dart
index a4faac3..464ded4 100644
--- a/packages/flutter_tools/lib/src/run_hot.dart
+++ b/packages/flutter_tools/lib/src/run_hot.dart
@@ -4,9 +4,9 @@
 
 import 'dart:async';
 
-import 'package:meta/meta.dart';
 import 'package:json_rpc_2/error_code.dart' as rpc_error_code;
 import 'package:json_rpc_2/json_rpc_2.dart' as rpc;
+import 'package:meta/meta.dart';
 
 import 'base/context.dart';
 import 'base/file_system.dart';
@@ -39,7 +39,7 @@
     bool usesTerminalUI: true,
     this.benchmarkMode: false,
     this.applicationBinary,
-    this.kernelFilePath,
+    this.previewDart2: false,
     String projectRootPath,
     String packagesFilePath,
     String projectAssets,
@@ -60,7 +60,7 @@
   final Map<String, int> benchmarkData = <String, int>{};
   // The initial launch is from a snapshot.
   bool _runningFromSnapshot = true;
-  String kernelFilePath;
+  bool previewDart2 = false;
 
   bool _refreshDartDependencies() {
     if (!hotRunnerConfig.computeDartDependencies) {
@@ -112,7 +112,6 @@
 
     for (FlutterDevice device in flutterDevices)
       device.initLogReader();
-
     try {
       final List<Uri> baseUris = await _initDevFS();
       if (connectionInfoCompleter != null) {
@@ -251,6 +250,8 @@
 
     for (FlutterDevice device in flutterDevices) {
       final bool result = await device.updateDevFS(
+        mainPath: mainPath,
+        target: target,
         bundle: assetBundle,
         bundleDirty: rebuildBundle,
         fileFilter: _dartDependencies,
@@ -363,15 +364,20 @@
   }
 
   /// Returns [true] if the reload was successful.
-  static bool validateReloadReport(Map<String, dynamic> reloadReport) {
+  /// Prints errors if [printErrors] is [true].
+  static bool validateReloadReport(Map<String, dynamic> reloadReport,
+      { bool printErrors: true }) {
     if (reloadReport['type'] != 'ReloadReport') {
-      printError('Hot reload received invalid response: $reloadReport');
+      if (printErrors)
+        printError('Hot reload received invalid response: $reloadReport');
       return false;
     }
     if (!reloadReport['success']) {
-      printError('Hot reload was rejected:');
-      for (Map<String, dynamic> notice in reloadReport['details']['notices'])
-        printError('${notice['message']}');
+      if (printErrors) {
+        printError('Hot reload was rejected:');
+        for (Map<String, dynamic> notice in reloadReport['details']['notices'])
+          printError('${notice['message']}');
+      }
       return false;
     }
     return true;
@@ -464,23 +470,39 @@
       return new OperationResult(1, 'Dart source error');
     String reloadMessage;
     try {
-      final String entryPath = fs.path.relative(mainPath, from: projectRootPath);
+      final String entryPath = fs.path.relative(
+        previewDart2 ? mainPath + '.dill' : mainPath,
+        from: projectRootPath
+      );
       if (benchmarkMode)
         vmReloadTimer.start();
-      final List<Future<Map<String, dynamic>>> reloadReportFutures = <Future<Map<String, dynamic>>>[];
+      final Completer<Map<String, dynamic>> retrieveFirstReloadReport = new Completer<Map<String, dynamic>>();
+
+      int countExpectedReports = 0;
       for (FlutterDevice device in flutterDevices) {
+        // List has one report per Flutter view.
         final List<Future<Map<String, dynamic>>> reports = device.reloadSources(
           entryPath,
           pause: pause
         );
-        reloadReportFutures.addAll(reports);
+        countExpectedReports += reports.length;
+        Future.wait(reports).then((List<Map<String, dynamic>> list) {
+          // TODO(aam): Investigate why we are validating only first reload report,
+          // which seems to be current behavior
+          final Map<String, dynamic> firstReport = list.first;
+          // Don't print errors because they will be printed further down when
+          // `validateReloadReport` is called again.
+          device.updateReloadStatus(validateReloadReport(firstReport,
+            printErrors: false));
+          retrieveFirstReloadReport.complete(firstReport);
+        });
       }
-      if (reloadReportFutures.isEmpty) {
+
+      if (countExpectedReports == 0) {
         printError('Unable to hot reload. No instance of Flutter is currently running.');
         return new OperationResult(1, 'No instances running');
       }
-      final Map<String, dynamic> reloadReport = (await Future.wait(reloadReportFutures)).first;
-
+      final Map<String, dynamic> reloadReport = await retrieveFirstReloadReport.future;
       if (!validateReloadReport(reloadReport)) {
         // Reload failed.
         flutterUsage.sendEvent('hot', 'reload-reject');
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart
index 18f33df..8a2d30f 100644
--- a/packages/flutter_tools/lib/src/runner/flutter_command.dart
+++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart
@@ -143,10 +143,13 @@
   }
 
   BuildInfo getBuildInfo() {
-    if (argParser.options.containsKey('flavor'))
-      return new BuildInfo(getBuildMode(), argResults['flavor']);
-    else
-      return new BuildInfo(getBuildMode(), null);
+    return new BuildInfo(getBuildMode(),
+      argParser.options.containsKey('flavor')
+        ? argResults['flavor']
+        : null,
+      previewDart2: argParser.options.containsKey('preview-dart-2')
+        ? argResults['preview-dart-2']
+        : false);
   }
 
   void setupApplicationPackages() {
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart b/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart
index 408db71..dbdcb11 100644
--- a/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart
+++ b/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart
@@ -270,8 +270,7 @@
     }
 
     if (globalResults['machine']) {
-      printError('The --machine flag is only valid with the --version flag.');
-      throw new ProcessExit(2);
+      throwToolExit('The --machine flag is only valid with the --version flag.', exitCode: 2);
     }
 
     await super.runCommand(globalResults);
@@ -304,40 +303,44 @@
       engineSourcePath ??= _tryEnginePath(fs.path.join(Cache.flutterRoot, '../engine/src'));
 
       if (engineSourcePath == null) {
-        printError('Unable to detect local Flutter engine build directory.\n'
-            'Either specify a dependency_override for the $kFlutterEnginePackageName package in your pubspec.yaml and\n'
-            'ensure --package-root is set if necessary, or set the \$$kFlutterEngineEnvironmentVariableName environment variable, or\n'
-            'use --local-engine-src-path to specify the path to the root of your flutter/engine repository.');
-        throw new ProcessExit(2);
+        throwToolExit('Unable to detect local Flutter engine build directory.\n'
+          'Either specify a dependency_override for the $kFlutterEnginePackageName package in your pubspec.yaml and\n'
+          'ensure --package-root is set if necessary, or set the \$$kFlutterEngineEnvironmentVariableName environment variable, or\n'
+          'use --local-engine-src-path to specify the path to the root of your flutter/engine repository.',
+          exitCode: 2);
       }
     }
 
     if (engineSourcePath != null && _tryEnginePath(engineSourcePath) == null) {
-      printError('Unable to detect a Flutter engine build directory in $engineSourcePath.\n'
-          'Please ensure that $engineSourcePath is a Flutter engine \'src\' directory and that\n'
-          'you have compiled the engine in that directory, which should produce an \'out\' directory');
-      throw new ProcessExit(2);
+      throwToolExit('Unable to detect a Flutter engine build directory in $engineSourcePath.\n'
+        'Please ensure that $engineSourcePath is a Flutter engine \'src\' directory and that\n'
+        'you have compiled the engine in that directory, which should produce an \'out\' directory',
+        exitCode: 2);
     }
 
     return engineSourcePath;
   }
 
-  String _findEngineBuildPath(ArgResults globalResults, String enginePath) {
+  EngineBuildPaths _findEngineBuildPath(ArgResults globalResults, String enginePath) {
     String localEngine;
     if (globalResults['local-engine'] != null) {
       localEngine = globalResults['local-engine'];
     } else {
-      printError('You must specify --local-engine if you are using a locally built engine.');
-      throw new ProcessExit(2);
+      throwToolExit('You must specify --local-engine if you are using a locally built engine.', exitCode: 2);
     }
 
     final String engineBuildPath = fs.path.normalize(fs.path.join(enginePath, 'out', localEngine));
     if (!fs.isDirectorySync(engineBuildPath)) {
-      printError('No Flutter engine build found at $engineBuildPath.');
-      throw new ProcessExit(2);
+      throwToolExit('No Flutter engine build found at $engineBuildPath.', exitCode: 2);
     }
 
-    return engineBuildPath;
+    final String hostLocalEngine = 'host_' + localEngine.substring(localEngine.indexOf('_') + 1);
+    final String engineHostBuildPath = fs.path.normalize(fs.path.join(enginePath, 'out', hostLocalEngine));
+    if (!fs.isDirectorySync(engineHostBuildPath)) {
+      throwToolExit('No Flutter host engine build found at $engineHostBuildPath.', exitCode: 2);
+    }
+
+    return new EngineBuildPaths(targetEngine: engineBuildPath, hostEngine: engineHostBuildPath);
   }
 
   static void initFlutterRoot() {