CFE support for expression evaluation, refactored.

Change-Id: I7eac1020b7aa11ea0276f909d7b11ee6388b1aa7
Reviewed-on: https://dart-review.googlesource.com/53209
Commit-Queue: Samir Jindel <sjindel@google.com>
Reviewed-by: Peter von der Ahé <ahe@google.com>
diff --git a/pkg/front_end/lib/src/api_prototype/incremental_kernel_generator.dart b/pkg/front_end/lib/src/api_prototype/incremental_kernel_generator.dart
index c60a782..76e4075 100644
--- a/pkg/front_end/lib/src/api_prototype/incremental_kernel_generator.dart
+++ b/pkg/front_end/lib/src/api_prototype/incremental_kernel_generator.dart
@@ -4,7 +4,8 @@
 
 import 'dart:async' show Future;
 
-import 'package:kernel/kernel.dart' show Component;
+import 'package:kernel/kernel.dart'
+    show Component, Procedure, DartType, TypeParameter;
 
 import '../base/processed_options.dart' show ProcessedOptions;
 
@@ -12,6 +13,8 @@
 
 import '../fasta/incremental_compiler.dart' show IncrementalCompiler;
 
+import '../fasta/scanner/string_scanner.dart' show StringScanner;
+
 import 'compiler_options.dart' show CompilerOptions;
 
 abstract class IncrementalKernelGenerator {
@@ -30,4 +33,39 @@
   /// valid files.  This guarantees that those files will be re-read on the
   /// next call to [computeDelta]).
   void invalidate(Uri uri);
+
+  /// Compile [expression] as an [Expression]. A function returning that
+  /// expression is compiled.
+  ///
+  /// [expression] may use the variables supplied in [definitions] as free
+  /// variables and [typeDefinitions] as free type variables. These will become
+  /// required parameters to the compiled function. All elements of
+  /// [definitions] and [typeDefinitions] will become parameters/type
+  /// parameters, whether or not they appear free in [expression]. The type
+  /// parameters should have a null parent pointer.
+  ///
+  /// [libraryUri] must refer to either a previously compiled library.
+  /// [className] may optionally refer to a class within such library to use for
+  /// the scope of the expression. In that case, [isStatic] indicates whether
+  /// the scope can access [this].
+  ///
+  /// It is illegal to use "await" in [expression] and the compiled function
+  /// will always be synchronous.
+  ///
+  /// [computeDelta] must have been called at least once prior.
+  ///
+  /// [compileExpression] will return [null] if the library or class for
+  /// [enclosingNode] could not be found. Otherwise, errors are reported in the
+  /// normal way.
+  Future<Procedure> compileExpression(
+      String expression,
+      Map<String, DartType> definitions,
+      List<TypeParameter> typeDefinitions,
+      Uri libraryUri,
+      [String className,
+      bool isStatic = false]);
+}
+
+bool isLegalIdentifier(String identifier) {
+  return StringScanner.isLegalIdentifier(identifier);
 }
diff --git a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
index 1513772..6a3bbab 100644
--- a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
+++ b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
@@ -1348,6 +1348,8 @@
 const Code<Message Function(String name)> codeDuplicatedNamePreviouslyUsed =
     const Code<Message Function(String name)>(
         "DuplicatedNamePreviouslyUsed", templateDuplicatedNamePreviouslyUsed,
+        analyzerCode: "REFERENCED_BEFORE_DECLARATION",
+        dart2jsCode: "*ignored*",
         severity: Severity.error);
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
@@ -1725,6 +1727,15 @@
 }
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeExpectedOneExpression = messageExpectedOneExpression;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageExpectedOneExpression = const MessageCode(
+    "ExpectedOneExpression",
+    severity: Severity.error,
+    message: r"""Expected one expression, but found additional input.""");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Code<Null> codeExpectedOpenParens = messageExpectedOpenParens;
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
diff --git a/pkg/front_end/lib/src/testing/hybrid_file_system.dart b/pkg/front_end/lib/src/fasta/hybrid_file_system.dart
similarity index 79%
rename from pkg/front_end/lib/src/testing/hybrid_file_system.dart
rename to pkg/front_end/lib/src/fasta/hybrid_file_system.dart
index ba2a2fe..4b388a3 100644
--- a/pkg/front_end/lib/src/testing/hybrid_file_system.dart
+++ b/pkg/front_end/lib/src/fasta/hybrid_file_system.dart
@@ -8,17 +8,18 @@
 
 import 'dart:async';
 
-import 'package:front_end/src/api_prototype/file_system.dart';
-import 'package:front_end/src/api_prototype/memory_file_system.dart';
-import 'package:front_end/src/api_prototype/standard_file_system.dart';
+import '../api_prototype/file_system.dart';
+import '../api_prototype/memory_file_system.dart';
+import '../api_prototype/standard_file_system.dart';
 
 /// A file system that mixes files from memory and a physical file system. All
 /// memory entities take priotity over file system entities.
 class HybridFileSystem implements FileSystem {
   final MemoryFileSystem memory;
-  final StandardFileSystem physical = StandardFileSystem.instance;
+  final FileSystem physical;
 
-  HybridFileSystem(this.memory);
+  HybridFileSystem(this.memory, [FileSystem _physical])
+      : physical = _physical ?? StandardFileSystem.instance;
 
   @override
   FileSystemEntity entityForUri(Uri uri) =>
@@ -37,7 +38,8 @@
   Future<FileSystemEntity> get delegate async {
     if (_delegate != null) return _delegate;
     FileSystemEntity entity = _fs.memory.entityForUri(uri);
-    if ((uri.scheme != 'file' && uri.scheme != 'data') ||
+    if (((uri.scheme != 'file' && uri.scheme != 'data') &&
+            _fs.physical is StandardFileSystem) ||
         await entity.exists()) {
       _delegate = entity;
       return _delegate;
diff --git a/pkg/front_end/lib/src/fasta/import.dart b/pkg/front_end/lib/src/fasta/import.dart
index 1ae3b3b..d48f2a2 100644
--- a/pkg/front_end/lib/src/fasta/import.dart
+++ b/pkg/front_end/lib/src/fasta/import.dart
@@ -88,7 +88,7 @@
   }
 }
 
-createPrefixBuilder(
+KernelPrefixBuilder createPrefixBuilder(
     String prefix,
     LibraryBuilder importer,
     LibraryBuilder imported,
diff --git a/pkg/front_end/lib/src/fasta/incremental_compiler.dart b/pkg/front_end/lib/src/fasta/incremental_compiler.dart
index 17a2764..68033b6 100644
--- a/pkg/front_end/lib/src/fasta/incremental_compiler.dart
+++ b/pkg/front_end/lib/src/fasta/incremental_compiler.dart
@@ -8,13 +8,33 @@
 
 import 'package:kernel/binary/ast_from_binary.dart' show BinaryBuilder;
 
-import 'package:kernel/kernel.dart'
-    show Component, Library, LibraryPart, Procedure, Source;
-
 import '../api_prototype/file_system.dart' show FileSystemEntity;
 
+import 'package:kernel/kernel.dart'
+    show
+        Library,
+        Name,
+        ReturnStatement,
+        FunctionNode,
+        Class,
+        Expression,
+        DartType,
+        LibraryPart,
+        Component,
+        LibraryDependency,
+        Source,
+        Procedure,
+        TypeParameter,
+        ProcedureKind;
+
+import '../api_prototype/memory_file_system.dart' show MemoryFileSystem;
+
+import 'hybrid_file_system.dart' show HybridFileSystem;
+
+import 'package:kernel/kernel.dart' as kernel show Combinator;
+
 import '../api_prototype/incremental_kernel_generator.dart'
-    show IncrementalKernelGenerator;
+    show IncrementalKernelGenerator, isLegalIdentifier;
 
 import 'builder/builder.dart' show LibraryBuilder;
 
@@ -31,12 +51,17 @@
 
 import 'library_graph.dart' show LibraryGraph;
 
+import 'kernel/kernel_library_builder.dart' show KernelLibraryBuilder;
+import 'kernel/kernel_shadow_ast.dart' show ShadowVariableDeclaration;
+
 import 'source/source_library_builder.dart' show SourceLibraryBuilder;
 
 import 'ticker.dart' show Ticker;
 
 import 'uri_translator.dart' show UriTranslator;
 
+import 'combinator.dart' show Combinator;
+
 class IncrementalCompiler implements IncrementalKernelGenerator {
   final CompilerContext context;
 
@@ -132,11 +157,18 @@
         ticker.logMs("Decided to reuse ${reusedLibraries.length}"
             " of ${userCode.loader.builders.length} libraries");
       }
+
       reusedLibraries.addAll(platformBuilders);
 
       KernelIncrementalTarget userCodeOld = userCode;
       userCode = new KernelIncrementalTarget(
-          c.fileSystem, false, dillLoadedData, uriTranslator,
+          new HybridFileSystem(
+              new MemoryFileSystem(
+                  new Uri(scheme: "org-dartlang-debug", path: "/")),
+              c.fileSystem),
+          false,
+          dillLoadedData,
+          uriTranslator,
           uriToSource: c.uriToSource);
 
       for (LibraryBuilder library in reusedLibraries) {
@@ -168,8 +200,11 @@
         return new Component(
             libraries: compiledLibraries, uriToSource: <Uri, Source>{});
       }
-      userCodeOld?.loader?.builders?.clear();
-      userCodeOld = null;
+      if (componentWithDill != null) {
+        userCodeOld?.loader?.releaseAncillaryResources();
+        userCodeOld?.loader?.builders?.clear();
+        userCodeOld = null;
+      }
 
       List<Library> compiledLibraries =
           new List<Library>.from(userCode.loader.libraries);
@@ -188,8 +223,10 @@
         outputLibraries = compiledLibraries;
       }
 
-      // Clean up.
-      userCode.loader.releaseAncillaryResources();
+      if (componentWithDill == null) {
+        userCode.loader.builders.clear();
+        userCode = userCodeOld;
+      }
 
       // This is the incremental component.
       return new Component(libraries: outputLibraries, uriToSource: uriToSource)
@@ -318,6 +355,107 @@
     ticker.logMs("Appended libraries");
   }
 
+  @override
+  Future<Procedure> compileExpression(
+      String expression,
+      Map<String, DartType> definitions,
+      List<TypeParameter> typeDefinitions,
+      Uri libraryUri,
+      [String className,
+      bool isStatic = false]) async {
+    assert(dillLoadedData != null && userCode != null);
+
+    return await context.runInContext((_) async {
+      LibraryBuilder library = userCode.loader.read(libraryUri, -1);
+
+      Class kernelClass;
+      if (className != null) {
+        kernelClass = library.scopeBuilder[className]?.target;
+        if (kernelClass == null) return null;
+      }
+
+      userCode.loader.seenMessages.clear();
+
+      for (TypeParameter typeParam in typeDefinitions) {
+        if (!isLegalIdentifier(typeParam.name)) return null;
+      }
+      for (String name in definitions.keys) {
+        if (!isLegalIdentifier(name)) return null;
+      }
+
+      Uri debugExprUri = new Uri(
+          scheme: "org-dartlang-debug", path: "synthetic_debug_expression");
+
+      KernelLibraryBuilder debugLibrary = new KernelLibraryBuilder(
+          libraryUri,
+          debugExprUri,
+          userCode.loader,
+          null,
+          library.scope.createNestedScope("expression"),
+          library.target);
+
+      if (library is DillLibraryBuilder) {
+        for (LibraryDependency dependency in library.target.dependencies) {
+          if (!dependency.isImport) continue;
+
+          List<Combinator> combinators;
+
+          for (kernel.Combinator combinator in dependency.combinators) {
+            combinators ??= <Combinator>[];
+
+            combinators.add(combinator.isShow
+                ? new Combinator.show(
+                    combinator.names, combinator.fileOffset, library.fileUri)
+                : new Combinator.hide(
+                    combinator.names, combinator.fileOffset, library.fileUri));
+          }
+
+          debugLibrary.addImport(
+              null,
+              dependency.importedLibraryReference.canonicalName.name,
+              null,
+              dependency.name,
+              combinators,
+              dependency.isDeferred,
+              -1,
+              -1,
+              -1);
+        }
+
+        debugLibrary.addImportsToScope();
+      }
+
+      HybridFileSystem hfs = userCode.fileSystem;
+      MemoryFileSystem fs = hfs.memory;
+      fs.entityForUri(debugExprUri).writeAsStringSync(expression);
+
+      FunctionNode parameters = new FunctionNode(null,
+          typeParameters: typeDefinitions,
+          positionalParameters: definitions.keys
+              .map((name) => new ShadowVariableDeclaration(name, 0))
+              .toList());
+
+      debugLibrary.build(userCode.loader.coreLibrary, modifyTarget: false);
+      Expression compiledExpression = await userCode.loader.buildExpression(
+          debugLibrary, className, className != null && !isStatic, parameters);
+
+      Procedure procedure = new Procedure(
+          new Name("debugExpr"), ProcedureKind.Method, parameters,
+          isStatic: isStatic);
+
+      parameters.body = new ReturnStatement(compiledExpression)
+        ..parent = parameters;
+
+      procedure.fileUri = debugLibrary.fileUri;
+      procedure.parent = className != null ? kernelClass : library.target;
+
+      userCode.uriToSource.remove(debugExprUri);
+      userCode.loader.sourceBytes.remove(debugExprUri);
+
+      return procedure;
+    });
+  }
+
   List<LibraryBuilder> computeReusedLibraries(
       Set<Uri> invalidatedUris, UriTranslator uriTranslator) {
     if (userCode == null && userBuilders == null) {
diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
index e4b47b1..8273574 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -19,6 +19,7 @@
 import '../parser.dart'
     show
         Assert,
+        Parser,
         FormalParameterKind,
         IdentifierContext,
         MemberKind,
@@ -714,6 +715,42 @@
     return expressions;
   }
 
+  @override
+  Expression parseSingleExpression(
+      Parser parser, Token token, FunctionNode parameters) {
+    List<KernelTypeVariableBuilder> typeParameterBuilders;
+    for (TypeParameter typeParameter in parameters.typeParameters) {
+      typeParameterBuilders ??= <KernelTypeVariableBuilder>[];
+      typeParameterBuilders.add(
+          new KernelTypeVariableBuilder.fromKernel(typeParameter, library));
+    }
+    enterFunctionTypeScope(typeParameterBuilders);
+
+    enterLocalScope(
+        null,
+        new FormalParameters(parameters.positionalParameters, null, -1)
+            .computeFormalParameterScope(scope, member, this));
+
+    token = parser.parseExpression(parser.syntheticPreviousToken(token));
+
+    Expression expression = popForValue();
+    Token eof = token.next;
+
+    if (!eof.isEof) {
+      expression = wrapInLocatedCompileTimeError(
+          expression,
+          fasta.messageExpectedOneExpression
+              .withLocation(uri, eof.charOffset, eof.length));
+    }
+
+    ShadowReturnStatement fakeReturn = new ShadowReturnStatement(expression);
+
+    _typeInferrer.inferFunctionBody(
+        this, const DynamicType(), AsyncMarker.Sync, fakeReturn);
+
+    return fakeReturn.expression;
+  }
+
   void finishConstructor(
       KernelConstructorBuilder builder, AsyncMarker asyncModifier) {
     /// Quotes below are from [Dart Programming Language Specification, 4th
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart
index 08ce85d..463f5b4 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart
@@ -111,9 +111,11 @@
   /// the error message is the corresponding value in the map.
   Map<String, String> unserializableExports;
 
-  KernelLibraryBuilder(Uri uri, Uri fileUri, Loader loader, this.actualOrigin)
-      : library = actualOrigin?.library ?? new Library(uri, fileUri: fileUri),
-        super(loader, fileUri);
+  KernelLibraryBuilder(Uri uri, Uri fileUri, Loader loader, this.actualOrigin,
+      [Scope scope, Library target])
+      : library = target ??
+            (actualOrigin?.library ?? new Library(uri, fileUri: fileUri)),
+        super(loader, fileUri, scope);
 
   @override
   KernelLibraryBuilder get origin => actualOrigin ?? this;
@@ -828,13 +830,16 @@
   }
 
   @override
-  Library build(LibraryBuilder coreLibrary) {
+  Library build(LibraryBuilder coreLibrary, {bool modifyTarget}) {
     super.build(coreLibrary);
 
+    if (modifyTarget == false) return library;
+
     addDependencies(library, new Set<KernelLibraryBuilder>());
 
     loader.target.metadataCollector
         ?.setDocumentationComment(library, documentationComment);
+
     library.name = name;
     library.procedures.sort(compareProcedures);
 
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_type_variable_builder.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_type_variable_builder.dart
index b884fd5..1290f11 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_type_variable_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_type_variable_builder.dart
@@ -29,11 +29,19 @@
 
   KernelTypeVariableBuilder(
       String name, KernelLibraryBuilder compilationUnit, int charOffset,
-      [KernelTypeBuilder bound])
-      : actualParameter = new TypeParameter(name, null)
-          ..fileOffset = charOffset,
+      [KernelTypeBuilder bound, TypeParameter actual])
+      // TODO(32378): We would like to use '??' here instead, but in conjuction
+      // with '..', it crashes Dart2JS.
+      : actualParameter = actual != null
+            ? (actual..fileOffset = charOffset)
+            : (new TypeParameter(name, null)..fileOffset = charOffset),
         super(name, bound, compilationUnit, charOffset);
 
+  KernelTypeVariableBuilder.fromKernel(
+      TypeParameter parameter, KernelLibraryBuilder compilationUnit)
+      : actualParameter = parameter,
+        super(parameter.name, null, compilationUnit, parameter.fileOffset);
+
   @override
   KernelTypeVariableBuilder get origin => actualOrigin ?? this;
 
diff --git a/pkg/front_end/lib/src/fasta/scanner/string_scanner.dart b/pkg/front_end/lib/src/fasta/scanner/string_scanner.dart
index 236b31e..7de4ef6 100644
--- a/pkg/front_end/lib/src/fasta/scanner/string_scanner.dart
+++ b/pkg/front_end/lib/src/fasta/scanner/string_scanner.dart
@@ -4,7 +4,7 @@
 
 library dart2js.scanner.string_scanner;
 
-import '../../scanner/token.dart' show SyntheticStringToken, TokenType;
+import '../../scanner/token.dart' show Token, SyntheticStringToken, TokenType;
 
 import '../../scanner/token.dart' as analyzer show StringToken;
 
@@ -12,6 +12,8 @@
 
 import 'token.dart' show CommentToken, DartDocToken, StringToken;
 
+import 'error_token.dart' show ErrorToken;
+
 /**
  * Scanner that reads from a String and creates tokens that points to
  * substrings.
@@ -37,6 +39,12 @@
         : string;
   }
 
+  static bool isLegalIdentifier(String identifier) {
+    StringScanner scanner = new StringScanner(identifier);
+    Token startToken = scanner.tokenize();
+    return startToken is! ErrorToken && startToken.next.isEof;
+  }
+
   int advance() => string.codeUnitAt(++scanOffset);
   int peek() => string.codeUnitAt(scanOffset + 1);
 
diff --git a/pkg/front_end/lib/src/fasta/scope.dart b/pkg/front_end/lib/src/fasta/scope.dart
index cdf9a9b..f6b3309 100644
--- a/pkg/front_end/lib/src/fasta/scope.dart
+++ b/pkg/front_end/lib/src/fasta/scope.dart
@@ -66,6 +66,11 @@
       : this(<String, Builder>{}, null, parent, debugName,
             isModifiable: isModifiable);
 
+  Scope copyWithParent(Scope parent, String debugName) {
+    return new Scope(super.local, super.setters, parent, debugName,
+        isModifiable: isModifiable);
+  }
+
   /// Don't use this. Use [becomePartOf] instead.
   void set local(_) => unsupported("local=", -1, null);
 
diff --git a/pkg/front_end/lib/src/fasta/source/diet_listener.dart b/pkg/front_end/lib/src/fasta/source/diet_listener.dart
index 68b1941..3f9ee22 100644
--- a/pkg/front_end/lib/src/fasta/source/diet_listener.dart
+++ b/pkg/front_end/lib/src/fasta/source/diet_listener.dart
@@ -527,13 +527,9 @@
   StackListener createListener(
       ModifierBuilder builder, Scope memberScope, bool isInstanceMember,
       [Scope formalParameterScope]) {
-    InterfaceType thisType;
-    if (builder.isClassMember) {
-      // Note: we set thisType regardless of whether we are building a static
-      // member, since that provides better error recovery.
-      Class cls = builder.parent.target;
-      thisType = cls.thisType;
-    }
+    // Note: we set thisType regardless of whether we are building a static
+    // member, since that provides better error recovery.
+    InterfaceType thisType = currentClass?.target?.thisType;
     var typeInferrer = library.disableTypeInference
         ? typeInferenceEngine.createDisabledTypeInferrer()
         : typeInferenceEngine.createLocalTypeInferrer(uri, thisType, library);
diff --git a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
index 6c44178..25be339 100644
--- a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
@@ -109,9 +109,9 @@
 
   bool canAddImplementationBuilders = false;
 
-  SourceLibraryBuilder(SourceLoader loader, Uri fileUri)
+  SourceLibraryBuilder(SourceLoader loader, Uri fileUri, Scope scope)
       : this.fromScopes(loader, fileUri, new DeclarationBuilder<T>.library(),
-            new Scope.top());
+            scope ?? new Scope.top());
 
   SourceLibraryBuilder.fromScopes(
       this.loader, this.fileUri, this.libraryDeclaration, this.importScope)
diff --git a/pkg/front_end/lib/src/fasta/source/source_loader.dart b/pkg/front_end/lib/src/fasta/source/source_loader.dart
index 75ebc59..5419f26 100644
--- a/pkg/front_end/lib/src/fasta/source/source_loader.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_loader.dart
@@ -12,10 +12,12 @@
     show
         Arguments,
         Class,
+        Component,
         Expression,
+        FunctionNode,
         Library,
         LibraryDependency,
-        Component,
+        ProcedureKind,
         Supertype;
 
 import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
@@ -64,13 +66,17 @@
 import '../kernel/kernel_shadow_ast.dart'
     show ShadowClass, ShadowTypeInferenceEngine;
 
+import '../kernel/kernel_builder.dart' show KernelProcedureBuilder;
+
 import '../kernel/kernel_target.dart' show KernelTarget;
 
+import '../kernel/body_builder.dart' show BodyBuilder;
+
 import '../loader.dart' show Loader;
 
 import '../parser/class_member_parser.dart' show ClassMemberParser;
 
-import '../parser.dart' show lengthForToken, offsetForToken;
+import '../parser.dart' show Parser, lengthForToken, offsetForToken;
 
 import '../problems.dart' show internalProblem;
 
@@ -207,6 +213,37 @@
     }
   }
 
+  Future<Expression> buildExpression(
+      SourceLibraryBuilder library,
+      String enclosingClass,
+      bool isInstanceMember,
+      FunctionNode parameters) async {
+    Token token = await tokenize(library, suppressLexicalErrors: false);
+    if (token == null) return null;
+    DietListener dietListener = createDietListener(library);
+
+    Builder parent = library;
+    if (enclosingClass != null) {
+      Builder cls = dietListener.memberScope.lookup(enclosingClass, -1, null);
+      if (cls is ClassBuilder) {
+        parent = cls;
+        dietListener
+          ..currentClass = cls
+          ..memberScope = cls.scope.copyWithParent(
+              dietListener.memberScope.withTypeVariables(cls.typeVariables),
+              "debugExpression in $enclosingClass");
+      }
+    }
+    KernelProcedureBuilder builder = new KernelProcedureBuilder(null, 0, null,
+        "debugExpr", null, null, ProcedureKind.Method, library, 0, -1, -1)
+      ..parent = parent;
+    BodyBuilder listener = dietListener.createListener(
+        builder, dietListener.memberScope, isInstanceMember);
+
+    return listener.parseSingleExpression(
+        new Parser(listener), token, parameters);
+  }
+
   KernelTarget get target => super.target;
 
   DietListener createDietListener(LibraryBuilder library) {
diff --git a/pkg/front_end/lib/src/fasta/source/stack_listener.dart b/pkg/front_end/lib/src/fasta/source/stack_listener.dart
index d440748..938ad81 100644
--- a/pkg/front_end/lib/src/fasta/source/stack_listener.dart
+++ b/pkg/front_end/lib/src/fasta/source/stack_listener.dart
@@ -4,7 +4,7 @@
 
 library fasta.stack_listener;
 
-import 'package:kernel/ast.dart' show AsyncMarker, Expression;
+import 'package:kernel/ast.dart' show AsyncMarker, Expression, FunctionNode;
 
 import '../deprecated_problems.dart' show deprecated_inputError;
 
@@ -14,7 +14,7 @@
         messageNativeClauseShouldBeAnnotation,
         templateInternalProblemStackNotEmpty;
 
-import '../parser.dart' show Listener, MemberKind;
+import '../parser.dart' show Listener, MemberKind, Parser;
 
 import '../parser/identifier_context.dart' show IdentifierContext;
 
@@ -96,6 +96,12 @@
   // and ast_builder.dart.
   void exitLocalScope() => unsupported("exitLocalScope", -1, uri);
 
+  // TODO(ahe): This doesn't belong here. Only implemented by body_builder.dart.
+  dynamic parseSingleExpression(
+      Parser parser, Token token, FunctionNode parameters) {
+    return unsupported("finishSingleExpression", -1, uri);
+  }
+
   void push(Object node) {
     if (node == null) unhandled("null", "push", -1, uri);
     stack.push(node);
diff --git a/pkg/front_end/lib/src/testing/compiler_common.dart b/pkg/front_end/lib/src/testing/compiler_common.dart
index 0193e73..d09c4c6 100644
--- a/pkg/front_end/lib/src/testing/compiler_common.dart
+++ b/pkg/front_end/lib/src/testing/compiler_common.dart
@@ -17,7 +17,7 @@
 import '../compute_platform_binaries_location.dart'
     show computePlatformBinariesLocation;
 
-import '../testing/hybrid_file_system.dart' show HybridFileSystem;
+import '../fasta/hybrid_file_system.dart' show HybridFileSystem;
 
 /// Generate kernel for a script.
 ///
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index 01a8aa0..2bd2b36 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -118,6 +118,8 @@
 ExpectedClassMember/example: Fail
 ExpectedDeclaration/example: Fail
 ExpectedFunctionBody/example: Fail
+ExpectedOneExpression/analyzerCode: Fail
+ExpectedOneExpression/example: Fail
 ExpectedOpenParens/analyzerCode: Fail
 ExpectedOpenParens/example: Fail
 ExpectedStatement/statement: Fail
@@ -411,4 +413,3 @@
 UnterminatedToken/example: Fail
 YieldAsIdentifier/example: Fail
 YieldNotGenerator/example: Fail
-
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index bf341d7..59b79aa 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -2103,3 +2103,7 @@
   template: "Can't invoke the type '#type' because its declaration of `.call` is not a method."
   tip: "Change .call to a method or explicitly invoke .call."
   severity: ERROR
+
+ExpectedOneExpression:
+  template: "Expected one expression, but found additional input."
+  severity: ERROR
diff --git a/pkg/front_end/test/fasta/expression_test.dart b/pkg/front_end/test/fasta/expression_test.dart
index c0611b0..4a21e78 100644
--- a/pkg/front_end/test/fasta/expression_test.dart
+++ b/pkg/front_end/test/fasta/expression_test.dart
@@ -11,13 +11,7 @@
 import "dart:io" show File, IOSink;
 
 import "package:kernel/ast.dart"
-    show
-        Procedure,
-        Component,
-        CanonicalName,
-        DynamicType,
-        DartType,
-        TypeParameter;
+    show Procedure, Component, DynamicType, DartType, TypeParameter;
 
 import "package:testing/testing.dart"
     show Chain, ChainContext, Result, Step, TestDescription, runMe;
@@ -123,7 +117,9 @@
 
   final bool isStaticMethod;
 
-  final CanonicalName enclosingNode;
+  final Uri library;
+
+  final String className;
 
   String expression;
 
@@ -135,7 +131,8 @@
       this.definitions,
       this.typeDefinitions,
       this.isStaticMethod,
-      this.enclosingNode,
+      this.library,
+      this.className,
       this.expression);
 
   String toString() {
@@ -143,7 +140,8 @@
         "$entryPoint, "
         "$definitions, "
         "$typeDefinitions,"
-        "$enclosingNode, "
+        "$library, "
+        "$className, "
         "static = $isStaticMethod)";
   }
 
@@ -155,7 +153,7 @@
     if (!(new File.fromUri(entryPoint)).existsSync()) {
       return "Entry point $entryPoint doesn't exist.";
     }
-    if (enclosingNode == null || enclosingNode == "") {
+    if (library == null) {
       return "No enclosing node.";
     }
     if (expression == null) {
@@ -233,7 +231,8 @@
     List<String> definitions = <String>[];
     List<String> typeDefinitions = <String>[];
     bool isStaticMethod = false;
-    CanonicalName enclosingNode;
+    Uri library;
+    String className;
     String expression;
 
     dynamic maps = loadYamlNode(contents, sourceUrl: uri);
@@ -248,13 +247,10 @@
         if (key == "entry_point") {
           entryPoint = description.uri.resolveUri(Uri.parse(value as String));
         } else if (key == "position") {
-          Uri positionUri =
-              description.uri.resolveUri(Uri.parse(value as String));
-          enclosingNode = new CanonicalName.root();
-          enclosingNode =
-              enclosingNode.getChild("${positionUri.removeFragment()}");
-          if (positionUri.fragment != null && positionUri.fragment != '') {
-            enclosingNode = enclosingNode.getChild(positionUri.fragment);
+          Uri uri = description.uri.resolveUri(Uri.parse(value as String));
+          library = uri.removeFragment();
+          if (uri.fragment != null && uri.fragment != '') {
+            className = uri.fragment;
           }
         } else if (key == "definitions") {
           definitions = (value as YamlList).map((x) => x as String).toList();
@@ -268,7 +264,7 @@
         }
       }
       var test = new TestCase(description, entryPoint, definitions,
-          typeDefinitions, isStaticMethod, enclosingNode, expression);
+          typeDefinitions, isStaticMethod, library, className, expression);
       var result = test.validate();
       if (result != null) {
         return new Result.fail(tests, result);
@@ -325,9 +321,15 @@
       }
 
       for (var compiler in [sourceCompiler, dillCompiler]) {
-        // TODO: actually run the compiler
-        test.results
-            .add(new CompilationResult(compiler == null ? null : null, []));
+        Procedure compiledProcedure = await compiler.compileExpression(
+            test.expression,
+            definitions,
+            typeParams,
+            test.library,
+            test.className,
+            test.isStaticMethod);
+        var errors = context.takeErrors();
+        test.results.add(new CompilationResult(compiledProcedure, errors));
       }
     }
     return new Result.pass(tests);
@@ -357,7 +359,7 @@
   final List<CompilationMessage> errors = <CompilationMessage>[];
 
   final CompilerOptions optionBuilder = new CompilerOptions()
-    ..strongMode = false
+    ..strongMode = true
     ..reportMessages = true
     ..verbose = true
     ..fileSystem = fs
diff --git a/pkg/front_end/test/fasta/messages_test.dart b/pkg/front_end/test/fasta/messages_test.dart
index 279bc8a..16f96a5 100644
--- a/pkg/front_end/test/fasta/messages_test.dart
+++ b/pkg/front_end/test/fasta/messages_test.dart
@@ -29,7 +29,7 @@
 import 'package:front_end/src/fasta/severity.dart'
     show Severity, severityEnumValues;
 
-import 'package:front_end/src/testing/hybrid_file_system.dart'
+import 'package:front_end/src/fasta/hybrid_file_system.dart'
     show HybridFileSystem;
 
 import "../../tool/_fasta/entry_points.dart" show BatchCompiler;
diff --git a/pkg/front_end/test/src/incremental/hot_reload_e2e_test.dart b/pkg/front_end/test/src/incremental/hot_reload_e2e_test.dart
index 9ad0d40..023efa7 100644
--- a/pkg/front_end/test/src/incremental/hot_reload_e2e_test.dart
+++ b/pkg/front_end/test/src/incremental/hot_reload_e2e_test.dart
@@ -36,7 +36,7 @@
 import 'package:front_end/src/compute_platform_binaries_location.dart'
     show computePlatformBinariesLocation;
 
-import 'package:front_end/src/testing/hybrid_file_system.dart'
+import 'package:front_end/src/fasta/hybrid_file_system.dart'
     show HybridFileSystem;
 
 import '../../tool/reload.dart' show RemoteVm;
diff --git a/pkg/front_end/test/subpackage_relationships_test.dart b/pkg/front_end/test/subpackage_relationships_test.dart
index ddacabc..bcf7288 100644
--- a/pkg/front_end/test/subpackage_relationships_test.dart
+++ b/pkg/front_end/test/subpackage_relationships_test.dart
@@ -49,6 +49,7 @@
     'lib/src/base',
     'lib/src/byte_store',
     'lib/src/fasta',
+    'lib/src/fasta/scanner'
   ]),
   'lib/src/api_unstable': new SubpackageRules(allowedDependencies: [
     'lib/src',
@@ -160,6 +161,7 @@
   'lib/src/testing': new SubpackageRules(allowedDependencies: [
     'lib/src',
     'lib/src/api_prototype',
+    'lib/src/fasta',
   ]),
 };
 
diff --git a/pkg/front_end/testcases/expression.status b/pkg/front_end/testcases/expression.status
index 87a97a3..87869b0 100644
--- a/pkg/front_end/testcases/expression.status
+++ b/pkg/front_end/testcases/expression.status
@@ -1,47 +1,3 @@
 // Copyright (c) 2018, 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.
-
-class_capture.expression: Fail
-class_getter.expression: Fail
-class_invalid_static.expression: Fail
-class_invalid_static_capture.expression: Fail
-class_invalid_static_getter.expression: Fail
-class_invalid_static_setter.expression: Fail
-class_method.expression: Fail
-class_setter.expression: Fail
-class_static.expression: Fail
-class_static2.expression: Fail
-class_static3.expression: Fail
-class_type_param_bound.expression: Fail
-class_type_param_bound_illegal.expression: Fail
-class_type_param_reference.expression: Fail
-class_type_param_reference_arg.expression: Fail
-class_type_param_reference_arg_inferred.expression: Fail
-class_type_param_reference_ctor.expression: Fail
-class_type_param_reference_ctor_inferred.expression: Fail
-class_type_param_reference_var.expression: Fail
-core_lib_imported.expression: Fail
-core_lib_internal.expression: Fail
-eval.dart: Fail
-invalid.expression: Fail
-lib_ctor.expression: Fail
-lib_external_ctor.expression: Fail
-lib_nonctor.expression: Fail
-lib_nonreference.expression: Fail
-lib_nonshown_ctor.expression: Fail
-lib_reference.expression: Fail
-lib_simple.expression: Fail
-missing_variable_types.expression: Fail
-param_assign.expression: Fail
-param_capture.expression: Fail
-param_conflict.expression: Fail
-param_conflict_class.expression: Fail
-param_method.expression: Fail
-type_param_bound.expression: Fail
-type_param_shadow.expression: Fail
-type_param_shadow_arg.expression: Fail
-type_param_shadow_arg_ctor_inferred.expression: Fail
-type_param_shadow_arg_inferred.expression: Fail
-type_param_shadow_ctor.expression: Fail
-type_param_shadow_var.expression: Fail
diff --git a/pkg/front_end/testcases/expression/invalid.expression.yaml.expect b/pkg/front_end/testcases/expression/invalid.expression.yaml.expect
index 1ce824d..1e3cd80 100644
--- a/pkg/front_end/testcases/expression/invalid.expression.yaml.expect
+++ b/pkg/front_end/testcases/expression/invalid.expression.yaml.expect
@@ -1,4 +1,5 @@
 Errors: {
+  Can't find ')' to match '('. (@4)
   Expected an identifier, but got '*'. (@0)
   Method not found: 'foo'. (@1)
   Getter not found: ''. (@0)
diff --git a/pkg/front_end/testcases/expression/lib_nonctor.expression.yaml b/pkg/front_end/testcases/expression/lib_nonctor.expression.yaml
index bae7baa..637ef0c 100644
--- a/pkg/front_end/testcases/expression/lib_nonctor.expression.yaml
+++ b/pkg/front_end/testcases/expression/lib_nonctor.expression.yaml
@@ -6,4 +6,4 @@
 definitions: []
 position: "main.dart"
 expression: |
-  new Random();
+  new Random()
diff --git a/pkg/front_end/testcases/expression/main.dart b/pkg/front_end/testcases/expression/main.dart
index ba2d61d..d946d39 100644
--- a/pkg/front_end/testcases/expression/main.dart
+++ b/pkg/front_end/testcases/expression/main.dart
@@ -2,10 +2,6 @@
 // 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.
 
-// Copyright (c) 2018, 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.
-
 library main;
 
 import 'dart:io' show File, Process, exit;
diff --git a/pkg/front_end/testcases/expression/nolib.expression.yaml.expect b/pkg/front_end/testcases/expression/nolib.expression.yaml.expect
index ba1f145..c46c67f 100644
--- a/pkg/front_end/testcases/expression/nolib.expression.yaml.expect
+++ b/pkg/front_end/testcases/expression/nolib.expression.yaml.expect
@@ -1,3 +1,4 @@
 Errors: {
 }
-<no procedure>
+method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr() → dynamic
+  return 0;
diff --git a/pkg/front_end/testcases/expression/type_param_shadow_arg_ctor_inferred.expression.yaml.expect b/pkg/front_end/testcases/expression/type_param_shadow_arg_ctor_inferred.expression.yaml.expect
index 6368c6b..4802c5b 100644
--- a/pkg/front_end/testcases/expression/type_param_shadow_arg_ctor_inferred.expression.yaml.expect
+++ b/pkg/front_end/testcases/expression/type_param_shadow_arg_ctor_inferred.expression.yaml.expect
@@ -1,7 +1,7 @@
 Errors: {
-  A value of type 'main::A<dynamic>' can't be assigned to a variable of type 'main::A::debugExpr::T'. (@17)
+  A value of type 'main::A<dynamic>' can't be assigned to a variable of type 'T'. (@17)
 }
 method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr<T extends dynamic>() → dynamic
   return () → dart.core::Null {
-    main::A::debugExpr::T k = let final dynamic #t1 = new main::A::•<dynamic>() in let dynamic _ = null in invalid-expression "Error: A value of type 'main::A<dynamic>' can't be assigned to a variable of type 'main::A::debugExpr::T'.\nTry changing the type of the left hand side, or casting the right hand side to 'main::A::debugExpr::T'.";
+    main::A::debugExpr::T k = let final dynamic #t1 = let dynamic _ = null in invalid-expression "org-dartlang-debug:synthetic_debug_expression:2:13: Error: A value of type 'main::A<dynamic>' can't be assigned to a variable of type 'T'.\nTry changing the type of the left hand side, or casting the right hand side to 'T'.\n  T k = new A();\n            ^" in let final dynamic #t2 = new main::A::•<dynamic>() in null;
   };
diff --git a/pkg/vm/bin/kernel_service.dart b/pkg/vm/bin/kernel_service.dart
index ef4c8ef..329b554 100644
--- a/pkg/vm/bin/kernel_service.dart
+++ b/pkg/vm/bin/kernel_service.dart
@@ -32,7 +32,7 @@
 import 'package:front_end/src/compute_platform_binaries_location.dart'
     show computePlatformBinariesLocation;
 import 'package:front_end/src/fasta/kernel/utils.dart';
-import 'package:front_end/src/testing/hybrid_file_system.dart';
+import 'package:front_end/src/fasta/hybrid_file_system.dart';
 import 'package:kernel/kernel.dart' show Component;
 import 'package:kernel/target/targets.dart' show TargetFlags;
 import 'package:kernel/target/vm.dart' show VmTarget;