Version 2.10.0-93.0.dev

Merge commit '34052bf2bbe2477d22bc4ef96b774067a86f68e3' into 'dev'
diff --git a/pkg/dev_compiler/bin/dartdevc.dart b/pkg/dev_compiler/bin/dartdevc.dart
index a906e00..68246e3 100755
--- a/pkg/dev_compiler/bin/dartdevc.dart
+++ b/pkg/dev_compiler/bin/dartdevc.dart
@@ -12,6 +12,7 @@
 import 'dart:isolate';
 import 'package:bazel_worker/bazel_worker.dart';
 import 'package:dev_compiler/src/compiler/shared_command.dart';
+import 'package:dev_compiler/src/kernel/expression_compiler_worker.dart';
 
 /// The entry point for the Dart Dev Compiler.
 ///
@@ -28,6 +29,18 @@
     await _CompilerWorker(parsedArgs, workerConnection).run();
   } else if (parsedArgs.isBatch) {
     await runBatch(parsedArgs);
+  } else if (parsedArgs.isExpressionCompiler) {
+    ExpressionCompilerWorker worker;
+    if (sendPort != null) {
+      var receivePort = ReceivePort();
+      sendPort.send(receivePort.sendPort);
+      worker = await ExpressionCompilerWorker.createFromArgs(parsedArgs.rest,
+          requestStream: receivePort.cast<Map<String, dynamic>>(),
+          sendResponse: sendPort.send);
+    } else {
+      worker = await ExpressionCompilerWorker.createFromArgs(parsedArgs.rest);
+    }
+    await worker.start();
   } else {
     var result = await compile(parsedArgs);
     exitCode = result.exitCode;
diff --git a/pkg/dev_compiler/lib/dev_compiler.dart b/pkg/dev_compiler/lib/dev_compiler.dart
index dedfc72..ca0a8bd 100644
--- a/pkg/dev_compiler/lib/dev_compiler.dart
+++ b/pkg/dev_compiler/lib/dev_compiler.dart
@@ -8,4 +8,5 @@
 export 'src/compiler/shared_command.dart' show SharedCompilerOptions;
 export 'src/kernel/command.dart' show jsProgramToCode;
 export 'src/kernel/compiler.dart' show ProgramCompiler;
+export 'src/kernel/expression_compiler.dart' show ExpressionCompiler;
 export 'src/kernel/target.dart' show DevCompilerTarget;
diff --git a/pkg/dev_compiler/lib/src/compiler/shared_command.dart b/pkg/dev_compiler/lib/src/compiler/shared_command.dart
index 2761eb0..358f238 100644
--- a/pkg/dev_compiler/lib/src/compiler/shared_command.dart
+++ b/pkg/dev_compiler/lib/src/compiler/shared_command.dart
@@ -61,6 +61,14 @@
   /// runtime can enable synchronous stack trace deobsfuscation.
   final bool inlineSourceMap;
 
+  /// Whether to emit the full compiled kernel.
+  ///
+  /// This is used by expression compiler worker, launched from the debugger
+  /// in webdev and google3 scenarios, for expression evaluation features.
+  /// Full kernel for compiled files is needed to be able to compile
+  /// expressions on demand in the current scope of a breakpoint.
+  final bool emitFullCompiledKernel;
+
   /// Whether to emit a summary file containing API signatures.
   ///
   /// This is required for a modular build process.
@@ -113,6 +121,7 @@
       this.enableAsserts = true,
       this.replCompile = false,
       this.emitDebugMetadata = false,
+      this.emitFullCompiledKernel = false,
       this.summaryModules = const {},
       this.moduleFormats = const [],
       this.moduleName,
@@ -129,6 +138,8 @@
             enableAsserts: args['enable-asserts'] as bool,
             replCompile: args['repl-compile'] as bool,
             emitDebugMetadata: args['experimental-emit-debug-metadata'] as bool,
+            emitFullCompiledKernel:
+                args['experimental-output-compiled-kernel'] as bool,
             summaryModules:
                 _parseCustomSummaryModules(args['summary'] as List<String>),
             moduleFormats: parseModuleFormatOption(args),
@@ -181,6 +192,12 @@
           help: 'Experimental option for compiler development.\n'
               'Output a metadata file for debug tools next to the .js output.',
           defaultsTo: false,
+          hide: true)
+      ..addFlag('experimental-output-compiled-kernel',
+          help: 'Experimental option for compiler development.\n'
+              'Output a full kernel file for currently compiled module next to '
+              'the .js output.',
+          defaultsTo: false,
           hide: true);
   }
 
@@ -490,6 +507,17 @@
   /// See also [isBatchOrWorker].
   final bool isBatch;
 
+  /// Whether to run in `--experimental-expression-compiler` mode.
+  ///
+  /// This is a special mode that is optimized for only compiling expressions.
+  ///
+  /// All dependencies must come from precompiled dill files, and those must
+  /// be explicitly invalidated as needed between expression compile requests.
+  /// Invalidation of dill is performed using [updateDeps] from the client (i.e.
+  /// debugger) and should be done every time a dill file changes, for example,
+  /// on hot reload or rebuild.
+  final bool isExpressionCompiler;
+
   /// Whether to run in `--bazel_worker` mode, e.g. for Bazel builds.
   ///
   /// Similar to [isBatch] but with a different protocol.
@@ -507,11 +535,14 @@
   /// Note that this only makes sense when also reusing results.
   final bool useIncrementalCompiler;
 
-  ParsedArguments._(this.rest,
-      {this.isBatch = false,
-      this.isWorker = false,
-      this.reuseResult = false,
-      this.useIncrementalCompiler = false});
+  ParsedArguments._(
+    this.rest, {
+    this.isBatch = false,
+    this.isWorker = false,
+    this.reuseResult = false,
+    this.useIncrementalCompiler = false,
+    this.isExpressionCompiler = false,
+  });
 
   /// Preprocess arguments to determine whether DDK is used in batch mode or as a
   /// persistent worker.
@@ -530,6 +561,7 @@
     var isBatch = false;
     var reuseResult = false;
     var useIncrementalCompiler = false;
+    var isExpressionCompiler = false;
 
     Iterable<String> argsToParse = args;
 
@@ -548,6 +580,8 @@
         reuseResult = true;
       } else if (arg == '--use-incremental-compiler') {
         useIncrementalCompiler = true;
+      } else if (arg == '--experimental-expression-compiler') {
+        isExpressionCompiler = true;
       } else {
         newArgs.add(arg);
       }
@@ -556,7 +590,8 @@
         isWorker: isWorker,
         isBatch: isBatch,
         reuseResult: reuseResult,
-        useIncrementalCompiler: useIncrementalCompiler);
+        useIncrementalCompiler: useIncrementalCompiler,
+        isExpressionCompiler: isExpressionCompiler);
   }
 
   /// Whether the compiler is running in [isBatch] or [isWorker] mode.
diff --git a/pkg/dev_compiler/lib/src/kernel/command.dart b/pkg/dev_compiler/lib/src/kernel/command.dart
index ba80022..6598642 100644
--- a/pkg/dev_compiler/lib/src/kernel/command.dart
+++ b/pkg/dev_compiler/lib/src/kernel/command.dart
@@ -361,7 +361,7 @@
           'the --summarize option is not supported.');
       return CompilerResult(64);
     }
-    // TODO(jmesserly): CFE mutates the Kernel tree, so we can't save the dill
+    // Note: CFE mutates the Kernel tree, so we can't save the dill
     // file if we successfully reused a cached library. If compiler state is
     // unchanged, it means we used the cache.
     //
@@ -376,6 +376,27 @@
     kernel.BinaryPrinter(sink).writeComponentFile(component);
     outFiles.add(sink.flush().then((_) => sink.close()));
   }
+  if (argResults['experimental-output-compiled-kernel'] as bool) {
+    if (outPaths.length > 1) {
+      print(
+          'If multiple output files (found ${outPaths.length}) are specified, '
+          'the --experimental-output-compiled-kernel option is not supported.');
+      return CompilerResult(64);
+    }
+    // Note: CFE mutates the Kernel tree, so we can't save the dill
+    // file if we successfully reused a cached library. If compiler state is
+    // unchanged, it means we used the cache.
+    //
+    // In that case, we need to unbind canonical names, because they could be
+    // bound already from the previous compile.
+    if (identical(compilerState, oldCompilerState)) {
+      compiledLibraries.unbindCanonicalNames();
+    }
+    var sink =
+        File(p.withoutExtension(outPaths.first) + '.full.dill').openWrite();
+    kernel.BinaryPrinter(sink).writeComponentFile(compiledLibraries);
+    outFiles.add(sink.flush().then((_) => sink.close()));
+  }
   if (argResults['summarize-text'] as bool) {
     if (outPaths.length > 1) {
       print(
diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart
index a040dc9..96575f1 100644
--- a/pkg/dev_compiler/lib/src/kernel/compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart
@@ -2930,11 +2930,16 @@
     _staticTypeContext.enterLibrary(_currentLibrary);
     _currentClass = cls;
 
-    // emit function with additional information,
-    // such as types that are used in the expression
+    // Emit function with additional information, such as types that are used
+    // in the expression. Note that typeTable can be null if this function is
+    // called from the expression compilation service, since we currently do
+    // not optimize for size of generated javascript in that scenario.
+    // TODO: figure whether or when optimizing for build time vs JavaScript
+    // size on expression evaluation is better.
+    // Issue: https://github.com/dart-lang/sdk/issues/43288
     var fun = _emitFunction(functionNode, name);
-    var items = _typeTable.discharge();
-    var body = js_ast.Block([...items, ...fun.body.statements]);
+    var items = _typeTable?.discharge();
+    var body = js_ast.Block([...?items, ...fun.body.statements]);
 
     return js_ast.Fun(fun.params, body);
   }
@@ -3450,7 +3455,7 @@
     if (fileUri == null) return null;
     try {
       var loc = _component.getLocation(fileUri, offset);
-      if (loc == null) return null;
+      if (loc == null || loc.line < 0) return null;
       return SourceLocation(offset,
           sourceUrl: fileUri, line: loc.line - 1, column: loc.column - 1);
     } on StateError catch (_) {
diff --git a/pkg/frontend_server/lib/src/expression_compiler.dart b/pkg/dev_compiler/lib/src/kernel/expression_compiler.dart
similarity index 94%
rename from pkg/frontend_server/lib/src/expression_compiler.dart
rename to pkg/dev_compiler/lib/src/kernel/expression_compiler.dart
index a64a904..2b26a3f 100644
--- a/pkg/frontend_server/lib/src/expression_compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/expression_compiler.dart
@@ -1,6 +1,6 @@
-// Copyright 2020 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.
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
 
 import 'dart:async';
 import 'dart:io';
@@ -210,6 +210,7 @@
   final IncrementalCompiler _compiler;
   final ProgramCompiler _kernel2jsCompiler;
   final Component _component;
+  final List<String> errors;
   DiagnosticMessageHandler onDiagnostic;
 
   void _log(String message) {
@@ -222,7 +223,7 @@
   }
 
   ExpressionCompiler(this._compiler, this._kernel2jsCompiler, this._component,
-      {this.verbose, this.onDiagnostic});
+      {this.verbose, this.onDiagnostic, this.errors});
 
   /// Compiles [expression] in [libraryUri] at [line]:[column] to JavaScript
   /// in [moduleName].
@@ -425,11 +426,15 @@
         scope.cls?.name,
         scope.procedure.isStatic);
 
-    // TODO(annagrin): The condition below seems to be always false.
-    // Errors are still correctly reported in the frontent_server,
-    // but we end up doing unnesessary work below.
-    // Add communication of error state from compiler here.
-    if (_compiler.context.errors.length > 0) {
+    // TODO: make this code clear and assumptions enforceable
+    // https://github.com/dart-lang/sdk/issues/43273
+    //
+    // We assume here that ExpressionCompiler is always created using
+    // onDisgnostic method that adds to the error list that is passed
+    // to the same invocation of the ExpressionCompiler constructor.
+    // We only use the error list once - below, to detect if the frontend
+    // compilation of the expression has failed.
+    if (errors.isNotEmpty) {
       return null;
     }
 
diff --git a/pkg/dev_compiler/lib/src/kernel/expression_compiler_worker.dart b/pkg/dev_compiler/lib/src/kernel/expression_compiler_worker.dart
new file mode 100644
index 0000000..9cb61dc
--- /dev/null
+++ b/pkg/dev_compiler/lib/src/kernel/expression_compiler_worker.dart
@@ -0,0 +1,473 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:_fe_analyzer_shared/src/messages/diagnostic_message.dart';
+import 'package:_fe_analyzer_shared/src/messages/severity.dart';
+import 'package:args/args.dart';
+import 'package:build_integration/file_system/multi_root.dart';
+import 'package:front_end/src/api_prototype/compiler_options.dart';
+import 'package:front_end/src/api_prototype/experimental_flags.dart';
+import 'package:front_end/src/api_prototype/file_system.dart';
+import 'package:front_end/src/api_unstable/ddc.dart';
+import 'package:kernel/ast.dart' show Component, Library;
+import 'package:kernel/target/targets.dart' show TargetFlags;
+import 'package:meta/meta.dart';
+import 'package:vm/http_filesystem.dart';
+
+import '../compiler/js_names.dart';
+import '../compiler/shared_command.dart';
+import 'command.dart';
+import 'compiler.dart';
+import 'expression_compiler.dart';
+import 'target.dart';
+
+/// A wrapper around asset server that redirects file read requests
+/// to http get requests to the asset server.
+class AssetFileSystem extends HttpAwareFileSystem {
+  final String server;
+  final String port;
+
+  AssetFileSystem(FileSystem original, this.server, this.port)
+      : super(original);
+
+  Uri resourceUri(Uri uri) =>
+      Uri.parse('http://$server:$port/getResource?uri=${uri.toString()}');
+
+  @override
+  FileSystemEntity entityForUri(Uri uri) {
+    if (uri.scheme == 'file') {
+      return super.entityForUri(uri);
+    }
+
+    // pass the uri to the asset server in the debugger
+    return HttpFileSystemEntity(this, resourceUri(uri));
+  }
+}
+
+/// The service that handles expression compilation requests from
+/// the debugger.
+///
+/// See design documentation and discussion in
+/// http://go/dart_expression_evaluation_webdev_google3
+///
+///
+/// [ExpressionCompilerWorker] listens to input stream of compile expression
+/// requests and outputs responses.
+///
+/// Debugger can run the service by running the dartdevc main in an isolate,
+/// which sets up the request stream and response callback using send ports.
+///
+/// Debugger also can pass an asset server's host and port so the service
+/// can read dill files from the [AssetFileSystem] that talks to the asset
+/// server behind the scenes over http.
+///
+/// Protocol:
+///
+///  - debugger and dartdevc expression evaluation service perform the initial
+///    handshake to establish two-way communication:
+///
+///    - debugger creates an isolate using dartdevc's main method with
+///      '--experimental-expression-compiler' flag and passes a send port
+///      to dartdevc for sending responces from the service to the debugger.
+///
+///    - dartdevc creates a new send port to receive requests on, and sends
+///      it back to the debugger, for sending requests to the service.
+///
+///  - debugger can now send two types of requests to the dartdevc service.
+///    The service handles the requests sequentially in a first come, first
+///    serve order.
+///
+///    - [UpdateDepsRequest]:
+///      This request is sent on (re-)build, making the dartdevc load all
+///      newly built full kernel files for given modules.
+///
+///    - [CompileExpressionRequest]:
+///      This request is sent any time an evaluateInFrame request is made to
+///      the debugger's VM service at a breakpoint - for example, on typing
+///      in an expression evaluation box, on hover over, evaluation of
+///      conditional breakpoints, evaluation of expressions in a watch window.
+///
+///  - Debugger closes the requests stream on exit, which effectively stops
+///    the service
+class ExpressionCompilerWorker {
+  final Stream<Map<String, dynamic>> requestStream;
+  final void Function(Map<String, dynamic>) sendResponse;
+
+  final _libraryForUri = <Uri, Library>{};
+  final _componentForLibrary = <Library, Component>{};
+  final _componentForModuleName = <String, Component>{};
+  final _componentModuleNames = <Component, String>{};
+  final ProcessedOptions _processedOptions;
+  final Component _sdkComponent;
+
+  ExpressionCompilerWorker._(this._processedOptions, this._sdkComponent,
+      this.requestStream, this.sendResponse);
+
+  static Future<ExpressionCompilerWorker> createFromArgs(
+    List<String> args, {
+    Stream<Map<String, dynamic>> requestStream,
+    void Function(Map<String, dynamic>) sendResponse,
+  }) {
+    // We are destructive on `args`, so make a copy.
+    args = args.toList();
+    var environmentDefines = parseAndRemoveDeclaredVariables(args);
+    var parsedArgs = argParser.parse(args);
+
+    FileSystem fileSystem = StandardFileSystem.instance;
+    var multiRoots = (parsedArgs['multi-root'] as Iterable<String>)
+        .map(Uri.base.resolve)
+        .toList();
+    var multiRootScheme = parsedArgs['multi-root-scheme'] as String;
+    if (multiRoots.isNotEmpty) {
+      fileSystem = MultiRootFileSystem(multiRootScheme, multiRoots, fileSystem);
+    }
+    var assetServerAddress = parsedArgs['asset-server-address'] as String;
+    if (assetServerAddress != null) {
+      var assetServerPort = parsedArgs['asset-server-port'] as String;
+      fileSystem = AssetFileSystem(
+          fileSystem, assetServerAddress, assetServerPort ?? '8080');
+    }
+    var experimentalFlags = parseExperimentalFlags(
+        parseExperimentalArguments(
+            parsedArgs['enable-experiment'] as List<String>),
+        onError: (e) => throw e);
+    return create(
+      librariesSpecificationUri:
+          _argToUri(parsedArgs['libraries-file'] as String),
+      packagesFile: _argToUri(parsedArgs['packages-file'] as String),
+      sdkSummary: _argToUri(parsedArgs['dart-sdk-summary'] as String),
+      fileSystem: fileSystem,
+      environmentDefines: environmentDefines,
+      experimentalFlags: experimentalFlags,
+      sdkRoot: _argToUri(parsedArgs['sdk-root'] as String),
+      trackWidgetCreation: parsedArgs['track-widget-creation'] as bool,
+      soundNullSafety: parsedArgs['sound-null-safety'] as bool,
+      verbose: parsedArgs['verbose'] as bool,
+      requestStream: requestStream,
+      sendResponse: sendResponse,
+    );
+  }
+
+  static List<String> errors = <String>[];
+  static List<String> warnings = <String>[];
+
+  /// Create the worker and load the sdk outlines.
+  static Future<ExpressionCompilerWorker> create({
+    @required Uri librariesSpecificationUri,
+    @required Uri sdkSummary,
+    @required FileSystem fileSystem,
+    Uri packagesFile,
+    Map<String, String> environmentDefines = const {},
+    Map<ExperimentalFlag, bool> experimentalFlags = const {},
+    Uri sdkRoot,
+    bool trackWidgetCreation = false,
+    bool soundNullSafety = false,
+    bool verbose = false,
+    Stream<Map<String, dynamic>> requestStream, // Defaults to read from stdin
+    void Function(Map<String, dynamic>)
+        sendResponse, // Defaults to write to stdout
+  }) async {
+    var options = CompilerOptions()
+      ..compileSdk = false
+      ..sdkRoot = sdkRoot
+      ..sdkSummary = sdkSummary
+      ..packagesFileUri = packagesFile
+      ..librariesSpecificationUri = librariesSpecificationUri
+      ..target = DevCompilerTarget(
+          TargetFlags(trackWidgetCreation: trackWidgetCreation))
+      ..fileSystem = fileSystem
+      ..omitPlatform = true
+      ..environmentDefines = environmentDefines
+      ..experimentalFlags = experimentalFlags
+      ..onDiagnostic = _onDiagnosticHandler(errors, warnings)
+      ..nnbdMode = soundNullSafety ? NnbdMode.Strong : NnbdMode.Weak
+      ..verbose = verbose;
+    requestStream ??= stdin
+        .transform(utf8.decoder.fuse(json.decoder))
+        .cast<Map<String, dynamic>>();
+    sendResponse ??= (Map<String, dynamic> response) =>
+        stdout.writeln(json.encode(response));
+    var processedOpts = ProcessedOptions(options: options);
+
+    var sdkComponent = await CompilerContext(processedOpts)
+        .runInContext<Component>((CompilerContext c) async {
+      return processedOpts.loadSdkSummary(null);
+    });
+
+    return ExpressionCompilerWorker._(
+        processedOpts, sdkComponent, requestStream, sendResponse)
+      .._update(sdkComponent, dartSdkModule);
+  }
+
+  /// Starts listening and responding to commands.
+  ///
+  /// Completes when the [requestStream] closes and we finish handling the
+  /// requests.
+  Future<void> start() async {
+    await for (var request in requestStream) {
+      try {
+        var command = request['command'] as String;
+        switch (command) {
+          case 'UpdateDeps':
+            sendResponse(
+                await _updateDeps(UpdateDepsRequest.fromJson(request)));
+            break;
+          case 'CompileExpression':
+            sendResponse(await _compileExpression(
+                CompileExpressionRequest.fromJson(request)));
+            break;
+          default:
+            throw ArgumentError(
+                'Unrecognized command `$command`, full request was `$request`');
+        }
+      } catch (e, s) {
+        sendResponse({
+          'exception': '$e',
+          'stackTrace': '$s',
+          'succeeded': false,
+        });
+      }
+    }
+  }
+
+  /// Handles a `CompileExpression` request.
+  Future<Map<String, dynamic>> _compileExpression(
+      CompileExpressionRequest request) async {
+    var libraryUri = Uri.parse(request.libraryUri);
+    if (libraryUri.scheme == 'dart') {
+      // compiling expressions inside the SDK currently fails because
+      // SDK kernel outlines do not contain information that is needed
+      // to detect the scope for expression evaluation - such as local
+      // symbols and source file line starts.
+      throw Exception('Expression compilation inside SDK is not supported yet');
+    }
+
+    var originalComponent = _componentForModuleName[request.moduleName];
+    if (originalComponent == null) {
+      throw ArgumentError(
+          'Unable to find library `$libraryUri`, it must be loaded first.');
+    }
+
+    var component = _sdkComponent;
+
+    if (libraryUri.scheme != 'dart') {
+      var libraries =
+          _collectTransitiveDependencies(originalComponent, _sdkComponent);
+      component = Component(
+        libraries: libraries,
+        nameRoot: originalComponent.root,
+        uriToSource: originalComponent.uriToSource,
+      );
+    }
+
+    errors.clear();
+    warnings.clear();
+
+    var incrementalCompiler = IncrementalCompiler.forExpressionCompilationOnly(
+        CompilerContext(_processedOptions), component);
+
+    var finalComponent =
+        await incrementalCompiler.computeDelta(entryPoints: [libraryUri]);
+    finalComponent.computeCanonicalNames();
+
+    if (errors.isNotEmpty) {
+      return {
+        'errors': errors,
+        'warnings': warnings,
+        'compiledProcedure': null,
+        'succeeded': errors.isEmpty,
+      };
+    }
+
+    var compiler = ProgramCompiler(
+      finalComponent,
+      incrementalCompiler.getClassHierarchy(),
+      SharedCompilerOptions(
+          sourceMap: true, summarizeApi: false, moduleName: request.moduleName),
+      _componentForLibrary,
+      _componentModuleNames,
+      coreTypes: incrementalCompiler.getCoreTypes(),
+    );
+
+    var expressionCompiler = ExpressionCompiler(
+      incrementalCompiler,
+      compiler,
+      finalComponent,
+      verbose: _processedOptions.verbose,
+      onDiagnostic: _onDiagnosticHandler(errors, warnings),
+      errors: errors,
+    );
+
+    var compiledProcedure = await expressionCompiler.compileExpressionToJs(
+        request.libraryUri,
+        request.line,
+        request.column,
+        request.jsModules,
+        request.jsScope,
+        request.moduleName,
+        request.expression);
+
+    return {
+      'errors': errors,
+      'warnings': warnings,
+      'compiledProcedure': compiledProcedure,
+      'succeeded': errors.isEmpty,
+    };
+  }
+
+  List<Library> _collectTransitiveDependencies(
+      Component component, Component sdk) {
+    var libraries = <Library>{};
+    libraries.addAll(sdk.libraries);
+
+    var toVisit = <Library>[];
+    toVisit.addAll(component.libraries);
+
+    while (toVisit.isNotEmpty) {
+      var lib = toVisit.removeLast();
+      if (!libraries.contains(lib)) {
+        libraries.add(lib);
+
+        for (var dep in lib.dependencies) {
+          var uri = dep.importedLibraryReference.asLibrary.importUri;
+          var library = _libraryForUri[uri];
+          assert(library == dep.importedLibraryReference.asLibrary);
+          toVisit.add(library);
+        }
+      }
+    }
+
+    return libraries.toList();
+  }
+
+  /// Loads in the specified dill files and invalidates any existing ones.
+  Future<Map<String, dynamic>> _updateDeps(UpdateDepsRequest request) async {
+    for (var input in request.inputs) {
+      var file =
+          _processedOptions.fileSystem.entityForUri(Uri.parse(input.path));
+      var bytes = await file.readAsBytes();
+      var component = await _processedOptions.loadComponent(
+          bytes, _sdkComponent.root,
+          alwaysCreateNewNamedNodes: true);
+      _update(component, input.moduleName);
+    }
+    return {'succeeded': true};
+  }
+
+  void _update(Component component, String moduleName) {
+    // do not update dart sdk
+    if (moduleName == dartSdkModule &&
+        _componentForModuleName.containsKey(moduleName)) {
+      return;
+    }
+
+    // cleanup old components and libraries
+    if (_componentForModuleName.containsKey(moduleName)) {
+      var oldComponent = _componentForModuleName[moduleName];
+      for (var lib in oldComponent.libraries) {
+        _componentForLibrary.remove(lib);
+        _libraryForUri.remove(lib.importUri);
+      }
+      _componentModuleNames.remove(oldComponent);
+      _componentForModuleName.remove(moduleName);
+    }
+
+    // add new components and libraries
+    _componentModuleNames[component] = moduleName;
+    _componentForModuleName[moduleName] = component;
+    for (var lib in component.libraries) {
+      _componentForLibrary[lib] = component;
+      _libraryForUri[lib.importUri] = lib;
+    }
+  }
+}
+
+class CompileExpressionRequest {
+  final int column;
+  final String expression;
+  final Map<String, String> jsModules;
+  final Map<String, String> jsScope;
+  final String libraryUri;
+  final int line;
+  final String moduleName;
+
+  CompileExpressionRequest({
+    @required this.expression,
+    @required this.column,
+    @required this.jsModules,
+    @required this.jsScope,
+    @required this.libraryUri,
+    @required this.line,
+    @required this.moduleName,
+  });
+
+  factory CompileExpressionRequest.fromJson(Map<String, dynamic> json) =>
+      CompileExpressionRequest(
+        expression: json['expression'] as String,
+        line: json['line'] as int,
+        column: json['column'] as int,
+        jsModules: Map<String, String>.from(json['jsModules'] as Map),
+        jsScope: Map<String, String>.from(json['jsScope'] as Map),
+        libraryUri: json['libraryUri'] as String,
+        moduleName: json['moduleName'] as String,
+      );
+}
+
+class UpdateDepsRequest {
+  final List<InputDill> inputs;
+
+  UpdateDepsRequest(this.inputs);
+
+  factory UpdateDepsRequest.fromJson(Map<String, dynamic> json) =>
+      UpdateDepsRequest([
+        for (var input in json['inputs'] as List)
+          InputDill(input['path'] as String, input['moduleName'] as String),
+      ]);
+}
+
+class InputDill {
+  final String moduleName;
+  final String path;
+
+  InputDill(this.path, this.moduleName);
+}
+
+void Function(DiagnosticMessage) _onDiagnosticHandler(
+        List<String> errors, List<String> warnings) =>
+    (DiagnosticMessage message) {
+      switch (message.severity) {
+        case Severity.error:
+        case Severity.internalProblem:
+          errors.add(message.plainTextFormatted.join('\n'));
+          break;
+        case Severity.warning:
+          warnings.add(message.plainTextFormatted.join('\n'));
+          break;
+        case Severity.context:
+        case Severity.ignored:
+          throw 'Unexpected severity: ${message.severity}';
+      }
+    };
+
+final argParser = ArgParser()
+  ..addOption('dart-sdk-summary')
+  ..addMultiOption('enable-experiment',
+      help: 'Enable a language experiment when invoking the CFE.')
+  ..addOption('libraries-file')
+  ..addMultiOption('multi-root')
+  ..addOption('multi-root-scheme', defaultsTo: 'org-dartlang-app')
+  ..addOption('packages-file')
+  ..addOption('sdk-root')
+  ..addOption('asset-server-address')
+  ..addOption('asset-server-port')
+  ..addFlag('track-widget-creation', defaultsTo: false)
+  ..addFlag('sound-null-safety', defaultsTo: false)
+  ..addFlag('verbose', defaultsTo: false);
+
+Uri _argToUri(String uriArg) =>
+    uriArg == null ? null : Uri.base.resolve(uriArg.replaceAll('\\', '/'));
diff --git a/pkg/dev_compiler/pubspec.yaml b/pkg/dev_compiler/pubspec.yaml
index 48ed14f..73cdcc9 100644
--- a/pkg/dev_compiler/pubspec.yaml
+++ b/pkg/dev_compiler/pubspec.yaml
@@ -23,6 +23,8 @@
   path: any
   source_maps: any
   source_span: any
+  vm:
+    path: ../vm
 
 dev_dependencies:
   analyzer: any
diff --git a/pkg/frontend_server/test/src/expression_compiler_test.dart b/pkg/dev_compiler/test/expression_compiler/expression_compiler_test.dart
similarity index 95%
rename from pkg/frontend_server/test/src/expression_compiler_test.dart
rename to pkg/dev_compiler/test/expression_compiler/expression_compiler_test.dart
index 2dabd91..7374f49 100644
--- a/pkg/frontend_server/test/src/expression_compiler_test.dart
+++ b/pkg/dev_compiler/test/expression_compiler/expression_compiler_test.dart
@@ -1,6 +1,6 @@
-// Copyright 2020 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:io' show Platform, File;
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
 
 import 'dart:io' show Directory, File;
 
@@ -11,7 +11,6 @@
     show CompilerOptions;
 import 'package:front_end/src/compute_platform_binaries_location.dart';
 import 'package:front_end/src/fasta/incremental_serializer.dart';
-import 'package:frontend_server/src/expression_compiler.dart';
 import 'package:kernel/ast.dart' show Component;
 import 'package:kernel/target/targets.dart';
 import 'package:path/path.dart' as p;
@@ -29,8 +28,8 @@
       bool outlineOnly,
       IncrementalSerializer incrementalSerializer])
       : super(
-            new CompilerContext(
-                new ProcessedOptions(options: options, inputs: [entryPoint])),
+            CompilerContext(
+                ProcessedOptions(options: options, inputs: [entryPoint])),
             initializeFrom,
             outlineOnly,
             incrementalSerializer);
@@ -39,8 +38,8 @@
       this.entryPoint, Component componentToInitializeFrom,
       [bool outlineOnly, IncrementalSerializer incrementalSerializer])
       : super.fromComponent(
-            new CompilerContext(
-                new ProcessedOptions(options: options, inputs: [entryPoint])),
+            CompilerContext(
+                ProcessedOptions(options: options, inputs: [entryPoint])),
             componentToInitializeFrom,
             outlineOnly,
             incrementalSerializer);
@@ -104,6 +103,7 @@
   String get package => importUri.toString();
   String get file => fileUri.path;
 
+  @override
   String toString() =>
       'Name: $name, File: $file, Package: $package, path: $path';
 }
@@ -140,13 +140,13 @@
     kernel2jsCompiler.emitModule(component);
 
     // create expression compiler
-    var evaluator = new ExpressionCompiler(
-        compiler, kernel2jsCompiler, component,
+    var evaluator = ExpressionCompiler(compiler, kernel2jsCompiler, component,
         verbose: setup.options.verbose,
-        onDiagnostic: setup.options.onDiagnostic);
+        onDiagnostic: setup.options.onDiagnostic,
+        errors: setup.errors);
 
     // collect all module names and paths
-    Map<Uri, Module> moduleInfo = _collectModules(component);
+    var moduleInfo = _collectModules(component);
 
     var modules =
         moduleInfo.map((k, v) => MapEntry<String, String>(v.name, v.path));
@@ -161,7 +161,7 @@
     var jsExpression = await evaluator.compileExpressionToJs(
         module.package, line, column, modules, scope, module.name, expression);
 
-    if (setup.errors.length > 0) {
+    if (setup.errors.isNotEmpty) {
       jsExpression = setup.errors.toString().replaceAll(
           RegExp(
               r'org-dartlang-debug:synthetic_debug_expression:[0-9]*:[0-9]*:'),
@@ -174,7 +174,7 @@
   }
 
   Map<Uri, Module> _collectModules(Component component) {
-    Map<Uri, Module> modules = <Uri, Module>{};
+    var modules = <Uri, Module>{};
     for (var library in component.libraries) {
       modules[library.fileUri] = Module(library.importUri, library.fileUri);
     }
@@ -239,10 +239,10 @@
   }
 
   int _getEvaluationLine(String source) {
-    RegExp placeholderRegExp = RegExp(r'/\* evaluation placeholder \*/');
+    var placeholderRegExp = RegExp(r'/\* evaluation placeholder \*/');
 
     var lines = source.split('\n');
-    for (int line = 0; line < lines.length; line++) {
+    for (var line = 0; line < lines.length; line++) {
       var content = lines[line];
       if (placeholderRegExp.firstMatch(content) != null) {
         return line + 1;
@@ -253,10 +253,10 @@
 }
 
 void main() {
-  SetupCompilerOptions options = SetupCompilerOptions();
+  var options = SetupCompilerOptions();
 
   group('Expression compiler tests in extension method:', () {
-    const String source = '''
+    const source = '''
       extension NumberParsing on String {
         int parseInt() {
           var ret = int.parse(this);
@@ -306,7 +306,7 @@
   });
 
   group('Expression compiler tests in method:', () {
-    const String source = '''
+    const source = '''
       extension NumberParsing on String {
         int parseInt() {
           return int.parse(this);
@@ -559,7 +559,7 @@
   });
 
   group('Expression compiler tests in method with no field access:', () {
-    const String source = '''
+    const source = '''
       extension NumberParsing on String {
         int parseInt() {
           return int.parse(this);
@@ -720,7 +720,7 @@
   });
 
   group('Expression compiler tests in async method:', () {
-    const String source = '''
+    const source = '''
       class C {
         C(int this.field, int this._field);
 
@@ -780,7 +780,7 @@
   });
 
   group('Expression compiler tests in global function:', () {
-    const String source = '''
+    const source = '''
       extension NumberParsing on String {
         int parseInt() {
           return int.parse(this);
@@ -1038,7 +1038,7 @@
   });
 
   group('Expression compiler tests in closures:', () {
-    const String source = r'''
+    const source = r'''
       int globalFunction() {
       int x = 15;
       var c = C(1, 2);
@@ -1102,7 +1102,7 @@
   });
 
   group('Expression compiler tests in method with no type use', () {
-    const String source = '''
+    const source = '''
       abstract class Key {
         const factory Key(String value) = ValueKey;
         const Key.empty();
diff --git a/pkg/dev_compiler/test/expression_compiler/expression_compiler_worker_test.dart b/pkg/dev_compiler/test/expression_compiler/expression_compiler_worker_test.dart
new file mode 100644
index 0000000..18fa6ce
--- /dev/null
+++ b/pkg/dev_compiler/test/expression_compiler/expression_compiler_worker_test.dart
@@ -0,0 +1,647 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io' show Directory, File, Platform, Process, stderr, stdout;
+
+import 'package:build_integration/file_system/multi_root.dart';
+import 'package:front_end/src/api_prototype/standard_file_system.dart';
+import 'package:front_end/src/compute_platform_binaries_location.dart';
+import 'package:path/path.dart' as p;
+import 'package:pedantic/pedantic.dart';
+import 'package:test/test.dart';
+
+import 'package:dev_compiler/src/kernel/expression_compiler_worker.dart';
+
+/// Verbose mode for debugging
+bool get verbose => false;
+
+class ModuleConfiguration {
+  final String outputPath;
+  final String moduleName;
+  final String libraryUri;
+  final String jsFileName;
+  final String fullDillFileName;
+
+  ModuleConfiguration(
+      {this.outputPath,
+      this.moduleName,
+      this.libraryUri,
+      this.jsFileName,
+      this.fullDillFileName});
+
+  String get jsPath => p.join(outputPath, jsFileName);
+  String get fullDillPath => p.join(outputPath, fullDillFileName);
+}
+
+class TestProjectConfiguration {
+  final Directory rootDirectory;
+  final String outputDir = 'out';
+
+  TestProjectConfiguration(this.rootDirectory);
+
+  ModuleConfiguration get mainModule => ModuleConfiguration(
+      outputPath: outputPath,
+      moduleName: 'packages/_testPackage/main',
+      libraryUri: 'org-dartlang-app:/lib/main.dart',
+      jsFileName: 'main.js',
+      fullDillFileName: 'main.full.dill');
+
+  ModuleConfiguration get testModule => ModuleConfiguration(
+      outputPath: outputPath,
+      moduleName: 'packages/_testPackage/test_library',
+      libraryUri: 'package:_testPackage/test_library.dart',
+      jsFileName: 'test_library.js',
+      fullDillFileName: 'test_library.full.dill');
+
+  ModuleConfiguration get testModule2 => ModuleConfiguration(
+      outputPath: outputPath,
+      moduleName: 'packages/_testPackage/test_library2',
+      libraryUri: 'package:_testPackage/test_library2.dart',
+      jsFileName: 'test_library2.js',
+      fullDillFileName: 'test_library2.full.dill');
+
+  String get root => rootDirectory.path;
+  String get outputPath => p.join(root, outputDir);
+  String get packagesPath => p.join(root, '.packages');
+
+  String get sdkRoot => computePlatformBinariesLocation().path;
+  String get sdkSummaryPath => p.join(sdkRoot, 'ddc_sdk.dill');
+  String get librariesPath => p.join(sdkRoot, 'lib', 'libraries.json');
+
+  void createTestProject() {
+    var pubspec = rootDirectory.uri.resolve('pubspec.yaml');
+    File.fromUri(pubspec)
+      ..createSync()
+      ..writeAsStringSync('''
+name: _testPackage
+version: 1.0.0
+
+environment:
+  sdk: '>=2.8.0 <3.0.0'
+''');
+
+    var packages = rootDirectory.uri.resolve('.packages');
+    File.fromUri(packages)
+      ..createSync()
+      ..writeAsStringSync('''
+_testPackage:lib/
+''');
+
+    var main = rootDirectory.uri.resolve('lib/main.dart');
+    File.fromUri(main)
+      ..createSync(recursive: true)
+      ..writeAsStringSync('''
+import 'package:_testPackage/test_library.dart';
+
+var global = 0;
+
+void main() {
+  var count = 0;
+  // line 7
+  print('Global is: \${++global}');
+  print('Count is: \${++count}');
+
+  B b = new B();
+}
+''');
+
+    var testLibrary = rootDirectory.uri.resolve('lib/test_library.dart');
+    File.fromUri(testLibrary)
+      ..createSync()
+      ..writeAsStringSync('''
+import 'package:_testPackage/test_library2.dart';
+
+int testLibraryFunction(int formal) {
+  return formal; // line 4
+}
+
+int callLibraryFunction2(int formal) {
+  return testLibraryFunction2(formal); // line 8
+}
+
+class B {
+  C c() => new C();
+}
+''');
+
+    var testLibrary2 = rootDirectory.uri.resolve('lib/test_library2.dart');
+    File.fromUri(testLibrary2)
+      ..createSync()
+      ..writeAsStringSync('''
+int testLibraryFunction2(int formal) {
+  return formal; // line 2
+}
+
+class C {
+  int getNumber() => 42;
+}
+''');
+  }
+}
+
+void main() async {
+  group('Expression compiler worker (webdev simulation) - ', () {
+    ExpressionCompilerWorker worker;
+    Future workerDone;
+    StreamController<Map<String, dynamic>> requestController;
+    StreamController<Map<String, dynamic>> responseController;
+    Directory tempDir;
+    TestProjectConfiguration config;
+    List inputs;
+
+    setUpAll(() async {
+      tempDir = Directory.systemTemp.createTempSync('foo bar');
+      config = TestProjectConfiguration(tempDir);
+
+      // simulate webdev
+      config.createTestProject();
+      var kernelGenerator = DDCKernelGenerator(config);
+      await kernelGenerator.generate();
+
+      inputs = [
+        {
+          'path': config.mainModule.fullDillPath,
+          'moduleName': config.mainModule.moduleName
+        },
+        {
+          'path': config.testModule.fullDillPath,
+          'moduleName': config.testModule.moduleName
+        },
+        {
+          'path': config.testModule2.fullDillPath,
+          'moduleName': config.testModule2.moduleName
+        },
+      ];
+    });
+
+    tearDownAll(() async {
+      tempDir.deleteSync(recursive: true);
+    });
+
+    setUp(() async {
+      var fileSystem = MultiRootFileSystem(
+          'org-dartlang-app', [tempDir.uri], StandardFileSystem.instance);
+
+      requestController = StreamController<Map<String, dynamic>>();
+      responseController = StreamController<Map<String, dynamic>>();
+      worker = await ExpressionCompilerWorker.create(
+        librariesSpecificationUri: Uri.file(config.librariesPath),
+        // We should be able to load everything from dill and not require
+        // source parsing. Webdev and google3 integration currently rely on that.
+        // Make the test fail on source reading by not providing a packages.
+        packagesFile: null,
+        sdkSummary: Uri.file(config.sdkSummaryPath),
+        fileSystem: fileSystem,
+        requestStream: requestController.stream,
+        sendResponse: responseController.add,
+        verbose: verbose,
+      );
+      workerDone = worker.start();
+    });
+
+    tearDown(() async {
+      unawaited(requestController.close());
+      await workerDone;
+      unawaited(responseController.close());
+    });
+
+    test('can load dependencies and compile expressions in sdk', () async {
+      requestController.add({
+        'command': 'UpdateDeps',
+        'inputs': inputs,
+      });
+
+      requestController.add({
+        'command': 'CompileExpression',
+        'expression': 'other',
+        'line': 107,
+        'column': 1,
+        'jsModules': {},
+        'jsScope': {'other': 'other'},
+        'libraryUri': 'dart:collection',
+        'moduleName': 'dart_sdk',
+      });
+
+      expect(
+          responseController.stream,
+          emitsInOrder([
+            equals({
+              'succeeded': true,
+            }),
+            equals({
+              'succeeded': true,
+              'errors': isEmpty,
+              'warnings': isEmpty,
+              'compiledProcedure': contains('return other;'),
+            })
+          ]));
+    }, skip: 'Evaluating expressions in SDK is not supported yet');
+
+    test('can load dependencies and compile expressions in a library',
+        () async {
+      requestController.add({
+        'command': 'UpdateDeps',
+        'inputs': inputs,
+      });
+
+      requestController.add({
+        'command': 'CompileExpression',
+        'expression': 'formal',
+        'line': 4,
+        'column': 1,
+        'jsModules': {},
+        'jsScope': {'formal': 'formal'},
+        'libraryUri': config.testModule.libraryUri,
+        'moduleName': config.testModule.moduleName,
+      });
+
+      expect(
+          responseController.stream,
+          emitsInOrder([
+            equals({
+              'succeeded': true,
+            }),
+            equals({
+              'succeeded': true,
+              'errors': isEmpty,
+              'warnings': isEmpty,
+              'compiledProcedure': contains('return formal;'),
+            })
+          ]));
+    });
+
+    test('can load dependencies and compile expressions in main', () async {
+      requestController.add({
+        'command': 'UpdateDeps',
+        'inputs': inputs,
+      });
+
+      requestController.add({
+        'command': 'CompileExpression',
+        'expression': 'count',
+        'line': 7,
+        'column': 1,
+        'jsModules': {},
+        'jsScope': {'count': 'count'},
+        'libraryUri': config.mainModule.libraryUri,
+        'moduleName': config.mainModule.moduleName,
+      });
+
+      expect(
+          responseController.stream,
+          emitsInOrder([
+            equals({
+              'succeeded': true,
+            }),
+            equals({
+              'succeeded': true,
+              'errors': isEmpty,
+              'warnings': isEmpty,
+              'compiledProcedure': contains('return count;'),
+            })
+          ]));
+    });
+
+    test('can load dependencies and compile transitive expressions in main',
+        () async {
+      requestController.add({
+        'command': 'UpdateDeps',
+        'inputs': inputs,
+      });
+
+      requestController.add({
+        'command': 'CompileExpression',
+        'expression': 'B().c().getNumber()',
+        'line': 7,
+        'column': 1,
+        'jsModules': {},
+        'jsScope': {},
+        'libraryUri': config.mainModule.libraryUri,
+        'moduleName': config.mainModule.moduleName,
+      });
+
+      expect(
+          responseController.stream,
+          emitsInOrder([
+            equals({
+              'succeeded': true,
+            }),
+            equals({
+              'succeeded': true,
+              'errors': isEmpty,
+              'warnings': isEmpty,
+              'compiledProcedure':
+                  contains('return new test_library.B.new().c().getNumber()'),
+            })
+          ]));
+    });
+  });
+
+  group('Expression compiler worker (google3 simulation) - ', () {
+    ExpressionCompilerWorker worker;
+    Future workerDone;
+    StreamController<Map<String, dynamic>> requestController;
+    StreamController<Map<String, dynamic>> responseController;
+    Directory tempDir;
+    TestProjectConfiguration config;
+    List inputs;
+
+    setUpAll(() async {
+      tempDir = Directory.systemTemp.createTempSync('foo bar');
+      config = TestProjectConfiguration(tempDir);
+
+      // simulate google3
+      config.createTestProject();
+      var kernelGenerator = BazelKernelWorkerGenerator(config);
+      await kernelGenerator.generate();
+
+      inputs = [
+        {
+          'path': config.mainModule.fullDillPath,
+          'moduleName': config.mainModule.moduleName
+        },
+      ];
+    });
+
+    tearDownAll(() async {
+      tempDir.deleteSync(recursive: true);
+    });
+
+    setUp(() async {
+      var fileSystem = MultiRootFileSystem(
+          'org-dartlang-app', [tempDir.uri], StandardFileSystem.instance);
+
+      requestController = StreamController<Map<String, dynamic>>();
+      responseController = StreamController<Map<String, dynamic>>();
+      worker = await ExpressionCompilerWorker.create(
+        librariesSpecificationUri: Uri.file(config.librariesPath),
+        packagesFile: null,
+        sdkSummary: Uri.file(config.sdkSummaryPath),
+        fileSystem: fileSystem,
+        requestStream: requestController.stream,
+        sendResponse: responseController.add,
+        verbose: verbose,
+      );
+      workerDone = worker.start();
+    });
+
+    tearDown(() async {
+      unawaited(requestController.close());
+      await workerDone;
+      unawaited(responseController.close());
+    });
+
+    test('can load dependencies and compile expressions in sdk', () async {
+      requestController.add({
+        'command': 'UpdateDeps',
+        'inputs': inputs,
+      });
+
+      requestController.add({
+        'command': 'CompileExpression',
+        'expression': 'other',
+        'line': 107,
+        'column': 1,
+        'jsModules': {},
+        'jsScope': {'other': 'other'},
+        'libraryUri': 'dart:collection',
+        'moduleName': 'dart_sdk',
+      });
+
+      expect(
+          responseController.stream,
+          emitsInOrder([
+            equals({
+              'succeeded': true,
+            }),
+            equals({
+              'succeeded': true,
+              'errors': isEmpty,
+              'warnings': isEmpty,
+              'compiledProcedure': contains('return other;'),
+            })
+          ]));
+    }, skip: 'Evaluating expressions in SDK is not supported yet');
+
+    test('can load dependencies and compile expressions in a library',
+        () async {
+      requestController.add({
+        'command': 'UpdateDeps',
+        'inputs': inputs,
+      });
+
+      requestController.add({
+        'command': 'CompileExpression',
+        'expression': 'formal',
+        'line': 4,
+        'column': 1,
+        'jsModules': {},
+        'jsScope': {'formal': 'formal'},
+        'libraryUri': config.testModule.libraryUri,
+        'moduleName': config.mainModule.moduleName,
+      });
+
+      expect(
+          responseController.stream,
+          emitsInOrder([
+            equals({
+              'succeeded': true,
+            }),
+            equals({
+              'succeeded': true,
+              'errors': isEmpty,
+              'warnings': isEmpty,
+              'compiledProcedure': contains('return formal;'),
+            })
+          ]));
+    });
+
+    test('can load dependencies and compile expressions in main', () async {
+      requestController.add({
+        'command': 'UpdateDeps',
+        'inputs': inputs,
+      });
+
+      requestController.add({
+        'command': 'CompileExpression',
+        'expression': 'count',
+        'line': 7,
+        'column': 1,
+        'jsModules': {},
+        'jsScope': {'count': 'count'},
+        'libraryUri': config.mainModule.libraryUri,
+        'moduleName': config.mainModule.moduleName,
+      });
+
+      expect(
+          responseController.stream,
+          emitsInOrder([
+            equals({
+              'succeeded': true,
+            }),
+            equals({
+              'succeeded': true,
+              'errors': isEmpty,
+              'warnings': isEmpty,
+              'compiledProcedure': contains('return count;'),
+            })
+          ]));
+    });
+  }, skip: 'bazel kernel worker does not support full kernel generation yet');
+}
+
+/// Uses DDC to generate kernel from the test code
+/// in order to simulate webdev environment
+class DDCKernelGenerator {
+  final TestProjectConfiguration config;
+
+  DDCKernelGenerator(this.config);
+
+  Future<int> generate() async {
+    var dart = Platform.resolvedExecutable;
+    var dartdevc =
+        p.join(p.dirname(dart), 'snapshots', 'dartdevc.dart.snapshot');
+
+    Directory(config.outputPath)..createSync();
+
+    // generate test_library2.full.dill
+    var args = [
+      dartdevc,
+      config.testModule2.libraryUri,
+      '--no-summarize',
+      '-o',
+      config.testModule2.jsPath,
+      '--source-map',
+      '--experimental-emit-debug-metadata',
+      '--experimental-output-compiled-kernel',
+      '--dart-sdk-summary',
+      config.sdkSummaryPath,
+      '--multi-root',
+      config.root,
+      '--multi-root-scheme',
+      'org-dartlang-app',
+      '--packages',
+      config.packagesPath,
+    ];
+
+    var exitCode = await runProcess(dart, args, config.root);
+    if (exitCode != 0) {
+      return exitCode;
+    }
+
+    // generate test_library.full.dill
+    args = [
+      dartdevc,
+      config.testModule.libraryUri,
+      '--no-summarize',
+      '--summary',
+      '${config.testModule2.fullDillPath}=${config.testModule2.moduleName}',
+      '-o',
+      config.testModule.jsPath,
+      '--source-map',
+      '--experimental-emit-debug-metadata',
+      '--experimental-output-compiled-kernel',
+      '--dart-sdk-summary',
+      config.sdkSummaryPath,
+      '--multi-root',
+      config.root,
+      '--multi-root-scheme',
+      'org-dartlang-app',
+      '--packages',
+      config.packagesPath,
+    ];
+
+    exitCode = await runProcess(dart, args, config.root);
+    if (exitCode != 0) {
+      return exitCode;
+    }
+
+    // generate main.full.dill
+    args = [
+      dartdevc,
+      config.mainModule.libraryUri,
+      '--no-summarize',
+      '--summary',
+      '${config.testModule2.fullDillPath}=${config.testModule2.moduleName}',
+      '--summary',
+      '${config.testModule.fullDillPath}=${config.testModule.moduleName}',
+      '-o',
+      config.mainModule.jsPath,
+      '--source-map',
+      '--experimental-emit-debug-metadata',
+      '--experimental-output-compiled-kernel',
+      '--dart-sdk-summary',
+      config.sdkSummaryPath,
+      '--multi-root',
+      config.root,
+      '--multi-root-scheme',
+      'org-dartlang-app',
+      '--packages',
+      config.packagesPath,
+    ];
+
+    return await runProcess(dart, args, config.root);
+  }
+}
+
+/// Uses bazel kernel worker to generate kernel from test code
+/// in order to simulate google3 environment
+/// TODO: split into 3 modules
+class BazelKernelWorkerGenerator {
+  TestProjectConfiguration config;
+
+  BazelKernelWorkerGenerator(this.config);
+
+  Future<void> generate() async {
+    var dart = Platform.resolvedExecutable;
+    var kernelWorker =
+        p.join(p.dirname(dart), 'snapshots', 'kernel_worker.dart.snapshot');
+
+    var args = [
+      kernelWorker,
+      '--target',
+      'ddc',
+      '--output',
+      config.mainModule.fullDillPath,
+      '--dart-sdk-summary',
+      config.sdkSummaryPath,
+      '--exclude-non-sources',
+      '--source',
+      config.mainModule.libraryUri,
+      '--source',
+      config.testModule.libraryUri,
+      '--source',
+      config.testModule2.libraryUri,
+      '--multi-root',
+      config.root,
+      '--multi-root-scheme',
+      'org-dartlang-app',
+      '--packages-file',
+      '.packages',
+      '--verbose'
+    ];
+
+    return await runProcess(dart, args, config.root);
+  }
+}
+
+Future<int> runProcess(
+    String command, List<String> args, String workingDirectory) async {
+  if (verbose) {
+    print('Running command in $workingDirectory:'
+        '\n\t $command ${args.join(' ')}, ');
+  }
+  var process =
+      await Process.start(command, args, workingDirectory: workingDirectory)
+          .then((Process process) {
+    process
+      ..stdout.transform(utf8.decoder).listen(stdout.write)
+      ..stderr.transform(utf8.decoder).listen(stderr.write);
+    return process;
+  });
+
+  return await process.exitCode;
+}
diff --git a/pkg/frontend_server/lib/frontend_server.dart b/pkg/frontend_server/lib/frontend_server.dart
index 35f5631..af48251 100644
--- a/pkg/frontend_server/lib/frontend_server.dart
+++ b/pkg/frontend_server/lib/frontend_server.dart
@@ -9,7 +9,8 @@
 import 'dart:io' hide FileSystemEntity;
 
 import 'package:args/args.dart';
-import 'package:dev_compiler/dev_compiler.dart' show DevCompilerTarget;
+import 'package:dev_compiler/dev_compiler.dart'
+    show DevCompilerTarget, ExpressionCompiler;
 
 // front_end/src imports below that require lint `ignore_for_file`
 // are a temporary state of things until frontend team builds better api
@@ -39,7 +40,6 @@
 
 import 'src/javascript_bundle.dart';
 import 'src/strong_components.dart';
-import 'src/expression_compiler.dart';
 
 ArgParser argParser = ArgParser(allowTrailingOptions: true)
   ..addFlag('train',
@@ -968,7 +968,8 @@
     var evaluator = new ExpressionCompiler(
         _generator.generator, kernel2jsCompiler, component,
         verbose: _compilerOptions.verbose,
-        onDiagnostic: _compilerOptions.onDiagnostic);
+        onDiagnostic: _compilerOptions.onDiagnostic,
+        errors: errors);
 
     var procedure = await evaluator.compileExpressionToJs(libraryUri, line,
         column, jsModules, jsFrameValues, moduleName, expression);
diff --git a/pkg/frontend_server/pubspec.yaml b/pkg/frontend_server/pubspec.yaml
index 6f96f7e..7932dab 100644
--- a/pkg/frontend_server/pubspec.yaml
+++ b/pkg/frontend_server/pubspec.yaml
@@ -7,8 +7,6 @@
   sdk: "^2.7.0"
 
 dependencies:
-  _fe_analyzer_shared:
-    path: ../_fe_analyzer_shared
   args: ^1.4.4
   dev_compiler:
     path: ../dev_compiler
@@ -23,6 +21,5 @@
     path: ../vm
 
 dev_dependencies:
-  cli_util: any
   mockito: any
   test: any
diff --git a/tools/VERSION b/tools/VERSION
index b464eb3..fc575db 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 10
 PATCH 0
-PRERELEASE 92
+PRERELEASE 93
 PRERELEASE_PATCH 0
\ No newline at end of file