[vm/kernel] Expression execution through Kernel.

Change-Id: Id90c91cb3b71f9ce703e21fe48147b86a7764d34
Reviewed-on: https://dart-review.googlesource.com/42562
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Alexander Aprelev <aam@google.com>
Commit-Queue: Samir Jindel <sjindel@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 76e4075..768cb71 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
@@ -61,6 +61,7 @@
       String expression,
       Map<String, DartType> definitions,
       List<TypeParameter> typeDefinitions,
+      String syntheticProcedureName,
       Uri libraryUri,
       [String className,
       bool isStatic = false]);
diff --git a/pkg/front_end/lib/src/fasta/incremental_compiler.dart b/pkg/front_end/lib/src/fasta/incremental_compiler.dart
index f23b510..bbd98f8 100644
--- a/pkg/front_end/lib/src/fasta/incremental_compiler.dart
+++ b/pkg/front_end/lib/src/fasta/incremental_compiler.dart
@@ -360,6 +360,7 @@
       String expression,
       Map<String, DartType> definitions,
       List<TypeParameter> typeDefinitions,
+      String syntheticProcedureName,
       Uri libraryUri,
       [String className,
       bool isStatic = false]) async {
@@ -440,7 +441,7 @@
           debugLibrary, className, className != null && !isStatic, parameters);
 
       Procedure procedure = new Procedure(
-          new Name("debugExpr"), ProcedureKind.Method, parameters,
+          new Name(syntheticProcedureName), ProcedureKind.Method, parameters,
           isStatic: isStatic);
 
       parameters.body = new ReturnStatement(compiledExpression)
diff --git a/pkg/front_end/lib/src/fasta/kernel/utils.dart b/pkg/front_end/lib/src/fasta/kernel/utils.dart
index 8bcda60..d95f852 100644
--- a/pkg/front_end/lib/src/fasta/kernel/utils.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/utils.dart
@@ -6,7 +6,10 @@
 
 import 'dart:io' show BytesBuilder, File, IOSink;
 
-import 'package:kernel/ast.dart' show Library, Component;
+import 'package:kernel/clone.dart' show CloneVisitor;
+
+import 'package:kernel/ast.dart'
+    show Library, Component, Procedure, Class, TypeParameter, Supertype;
 
 import 'package:kernel/binary/ast_to_binary.dart' show BinaryPrinter;
 
@@ -58,6 +61,39 @@
   return byteSink.builder.takeBytes();
 }
 
+List<int> serializeProcedure(Procedure procedure) {
+  Library fakeLibrary =
+      new Library(new Uri(scheme: 'evaluate', path: 'source'));
+
+  if (procedure.parent is Class) {
+    Class realClass = procedure.parent;
+
+    CloneVisitor cloner = new CloneVisitor();
+
+    Class fakeClass = new Class(name: realClass.name);
+    for (TypeParameter typeParam in realClass.typeParameters) {
+      fakeClass.typeParameters.add(typeParam.accept(cloner));
+    }
+
+    fakeClass.parent = fakeLibrary;
+    fakeClass.supertype = new Supertype.byReference(
+        realClass.supertype.className,
+        realClass.supertype.typeArguments.map(cloner.visitType).toList());
+
+    // Rebind the type parameters in the procedure.
+    procedure = procedure.accept(cloner);
+    procedure.parent = fakeClass;
+    fakeClass.procedures.add(procedure);
+    fakeLibrary.classes.add(fakeClass);
+  } else {
+    fakeLibrary.procedures.add(procedure);
+    procedure.parent = fakeLibrary;
+  }
+
+  Component program = new Component(libraries: [fakeLibrary]);
+  return serializeComponent(program);
+}
+
 /// A [Sink] that directly writes data into a byte builder.
 class ByteSink implements Sink<List<int>> {
   final BytesBuilder builder = new BytesBuilder();
diff --git a/pkg/front_end/test/fasta/expression_test.dart b/pkg/front_end/test/fasta/expression_test.dart
index 4a21e78..ee97b9d 100644
--- a/pkg/front_end/test/fasta/expression_test.dart
+++ b/pkg/front_end/test/fasta/expression_test.dart
@@ -325,6 +325,7 @@
             test.expression,
             definitions,
             typeParams,
+            "debugExpr",
             test.library,
             test.className,
             test.isStaticMethod);
diff --git a/pkg/status_file/test/data/vm.status b/pkg/status_file/test/data/vm.status
index e446356..726d64e 100644
--- a/pkg/status_file/test/data/vm.status
+++ b/pkg/status_file/test/data/vm.status
@@ -217,6 +217,7 @@
 cc/IsolateReload_KernelIncrementalCompile: Skip
 cc/IsolateReload_KernelIncrementalCompileAppAndLib: Skip
 cc/IsolateReload_KernelIncrementalCompileGenerics: Skip
+cc/IsolateReload_KernelIncrementalCompileExpression: Skip
 cc/Mixin_PrivateSuperResolution: Skip
 cc/Mixin_PrivateSuperResolutionCrossLibraryShouldFail: Skip
 
diff --git a/pkg/vm/bin/kernel_service.dart b/pkg/vm/bin/kernel_service.dart
index 329b554..0590e39 100644
--- a/pkg/vm/bin/kernel_service.dart
+++ b/pkg/vm/bin/kernel_service.dart
@@ -33,7 +33,7 @@
     show computePlatformBinariesLocation;
 import 'package:front_end/src/fasta/kernel/utils.dart';
 import 'package:front_end/src/fasta/hybrid_file_system.dart';
-import 'package:kernel/kernel.dart' show Component;
+import 'package:kernel/kernel.dart' show Component, Procedure;
 import 'package:kernel/target/targets.dart' show TargetFlags;
 import 'package:kernel/target/vm.dart' show VmTarget;
 import 'package:vm/incremental_compiler.dart';
@@ -49,10 +49,15 @@
 //   1 - Update in-memory file system with in-memory sources (used by tests).
 //   2 - Accept last compilation result.
 //   3 - APP JIT snapshot training run for kernel_service.
+//   4 - Compile an individual expression in some context (for debugging
+//       purposes).
 const int kCompileTag = 0;
 const int kUpdateSourcesTag = 1;
 const int kAcceptTag = 2;
 const int kTrainTag = 3;
+const int kCompileExpressionTag = 4;
+
+bool allowDartInternalImport = false;
 
 abstract class Compiler {
   final FileSystem fileSystem;
@@ -177,9 +182,10 @@
   }
 }
 
-final Map<int, Compiler> isolateCompilers = new Map<int, Compiler>();
+final Map<int, IncrementalCompilerWrapper> isolateCompilers =
+    new Map<int, IncrementalCompilerWrapper>();
 
-Compiler lookupIncrementalCompiler(int isolateId) {
+IncrementalCompilerWrapper lookupIncrementalCompiler(int isolateId) {
   return isolateCompilers[isolateId];
 }
 
@@ -242,10 +248,60 @@
 
 // Process a request from the runtime. See KernelIsolate::CompileToKernel in
 // kernel_isolate.cc and Loader::SendKernelRequest in loader.cc.
+Future _processExpressionCompilationRequest(request) async {
+  final SendPort port = request[1];
+  final int isolateId = request[2];
+  final String expression = request[3];
+  final List definitions = request[4];
+  final List typeDefinitions = request[5];
+  final String libraryUri = request[6];
+  final String klass = request[7]; // might be null
+  final bool isStatic = request[8];
+
+  IncrementalCompilerWrapper compiler = isolateCompilers[isolateId];
+
+  if (compiler == null) {
+    port.send(new CompilationResult.errors(
+        ["No incremental compiler available for this isolate."]).toResponse());
+    return;
+  }
+
+  compiler.errors.clear();
+
+  CompilationResult result;
+  try {
+    Procedure procedure = await compiler.generator.compileExpression(
+        expression, definitions, typeDefinitions, libraryUri, klass, isStatic);
+
+    if (procedure == null) {
+      port.send(new CompilationResult.errors(["Invalid scope."]).toResponse());
+      return;
+    }
+
+    if (compiler.errors.isNotEmpty) {
+      // TODO(sigmund): the compiler prints errors to the console, so we
+      // shouldn't print those messages again here.
+      result = new CompilationResult.errors(compiler.errors);
+    } else {
+      result = new CompilationResult.ok(serializeProcedure(procedure));
+    }
+  } catch (error, stack) {
+    result = new CompilationResult.crash(error, stack);
+  }
+
+  port.send(result.toResponse());
+}
+
 Future _processLoadRequest(request) async {
   if (verbose) print("DFE: request: $request");
 
   int tag = request[0];
+
+  if (tag == kCompileExpressionTag) {
+    await _processExpressionCompilationRequest(request);
+    return;
+  }
+
   final SendPort port = request[1];
   final String inputFileUri = request[2];
   final Uri script =
diff --git a/pkg/vm/lib/incremental_compiler.dart b/pkg/vm/lib/incremental_compiler.dart
index 91d9426..3541bc2 100644
--- a/pkg/vm/lib/incremental_compiler.dart
+++ b/pkg/vm/lib/incremental_compiler.dart
@@ -10,6 +10,8 @@
 import 'package:front_end/src/api_prototype/incremental_kernel_generator.dart';
 import 'package:kernel/kernel.dart';
 
+const String kDebugProcedureName = ":Eval";
+
 /// Wrapper around [IncrementalKernelGenerator] that keeps track of rejected
 /// deltas and combines them together into resultant program until it is
 /// accepted.
@@ -74,4 +76,30 @@
     _pendingDeltas.clear();
     fullComponent = true;
   }
+
+  Future<Procedure> compileExpression(
+      String expression,
+      List<String> definitions,
+      List<String> typeDefinitions,
+      String libraryUri,
+      String klass,
+      bool isStatic) {
+    Map<String, DartType> completeDefinitions = {};
+    for (String name in definitions) {
+      if (!isLegalIdentifier(name)) continue;
+      completeDefinitions[name] = new DynamicType();
+    }
+
+    List<TypeParameter> typeParameters = [];
+    for (String name in typeDefinitions) {
+      if (!isLegalIdentifier(name)) continue;
+      typeParameters.add(new TypeParameter(name, new DynamicType()));
+    }
+
+    Uri library = Uri.parse(libraryUri);
+    if (library == null) return null;
+
+    return _generator.compileExpression(expression, completeDefinitions,
+        typeParameters, kDebugProcedureName, library, klass, isStatic);
+  }
 }
diff --git a/runtime/observatory/tests/service/async_generator_breakpoint_test.dart b/runtime/observatory/tests/service/async_generator_breakpoint_test.dart
index 6781af7..79c6be7 100644
--- a/runtime/observatory/tests/service/async_generator_breakpoint_test.dart
+++ b/runtime/observatory/tests/service/async_generator_breakpoint_test.dart
@@ -69,7 +69,8 @@
 
   var hits = [];
 
-  isolate.rootLibrary.evaluate('testerReady = true;').then((result) {
+  isolate.rootLibrary.evaluate('testerReady = true').then((result) {
+    print(result);
     expect((result as Instance).valueAsString, equals('true'));
   });
 
diff --git a/runtime/observatory/tests/service/bad_reload_test.dart b/runtime/observatory/tests/service/bad_reload_test.dart
index 8e9aed7..23927e9 100644
--- a/runtime/observatory/tests/service/bad_reload_test.dart
+++ b/runtime/observatory/tests/service/bad_reload_test.dart
@@ -72,9 +72,11 @@
     expect(reasonForCancelling['type'], equals('ReasonForCancelling'));
     expect(reasonForCancelling['message'], contains('library_isnt_here_man'));
 
-    // Invoke test in v2.
-    String v2 = await invokeTest(spawnedIsolate);
-    expect(v2, 'apple');
+    // TODO(32341): enable in Dart 2
+    if (!Platform.executableArguments.contains("--preview_dart_2")) {
+      String v2 = await invokeTest(spawnedIsolate);
+      expect(v2, 'apple');
+    }
   }
 ];
 
diff --git a/runtime/observatory/tests/service/debugger_inspect_test.dart b/runtime/observatory/tests/service/debugger_inspect_test.dart
index c52c42f..4f8f680 100644
--- a/runtime/observatory/tests/service/debugger_inspect_test.dart
+++ b/runtime/observatory/tests/service/debugger_inspect_test.dart
@@ -32,7 +32,7 @@
     });
 
     // Start listening for events first.
-    await isolate.rootLibrary.evaluate('testeeDo();');
+    await isolate.rootLibrary.evaluate('testeeDo()');
     return completer.future;
   },
 ];
diff --git a/runtime/observatory/tests/service/evaluate_activation_test.dart b/runtime/observatory/tests/service/evaluate_activation_test.dart
index fc87190..cb352cd 100644
--- a/runtime/observatory/tests/service/evaluate_activation_test.dart
+++ b/runtime/observatory/tests/service/evaluate_activation_test.dart
@@ -114,7 +114,7 @@
     }
   });
 
-  var result = await rootLib.evaluate('new C().method(3);');
+  var result = await rootLib.evaluate('new C().method(3)');
   print("Result $result");
   expect(hitBreakpoint, isTrue);
 }
@@ -163,7 +163,7 @@
     }
   });
 
-  var result = await rootLib.evaluate('C.method2(3);');
+  var result = await rootLib.evaluate('C.method2(3)');
   print("Result $result");
   expect(hitBreakpoint, isTrue);
 }
@@ -204,7 +204,7 @@
     }
   });
 
-  var result = await rootLib.evaluate('new C().method3(3);');
+  var result = await rootLib.evaluate('new C().method3(3)');
   print("Result $result");
   expect(hitBreakpoint, isTrue);
 }
diff --git a/runtime/observatory/tests/service/get_user_level_retaining_path_rpc_test.dart b/runtime/observatory/tests/service/get_user_level_retaining_path_rpc_test.dart
index c458396..6211574 100644
--- a/runtime/observatory/tests/service/get_user_level_retaining_path_rpc_test.dart
+++ b/runtime/observatory/tests/service/get_user_level_retaining_path_rpc_test.dart
@@ -40,7 +40,7 @@
   // Expect a simple path through variable x instead of long path filled
   // with VM objects
   (Isolate isolate) async {
-    var target1 = await eval(isolate, 'x;');
+    var target1 = await eval(isolate, 'x');
     var params = {
       'targetId': target1['id'],
       'limit': 100,
@@ -56,7 +56,7 @@
   // Expect a simple path through variable fn instead of long path filled
   // with VM objects
   (Isolate isolate) async {
-    var target2 = await eval(isolate, 'fn;');
+    var target2 = await eval(isolate, 'fn');
     var params = {
       'targetId': target2['id'],
       'limit': 100,
diff --git a/runtime/observatory/tests/service/service_kernel.status b/runtime/observatory/tests/service/service_kernel.status
index c9b5f1d..8a8fd82 100644
--- a/runtime/observatory/tests/service/service_kernel.status
+++ b/runtime/observatory/tests/service/service_kernel.status
@@ -11,7 +11,6 @@
 async_star_step_out_test: RuntimeError # Issue 29158, Async debugging
 async_step_out_test: RuntimeError # Issue 29158, Async debugging
 awaiter_async_stack_contents_test: RuntimeError # Issue 29158, Async debugging
-eval_internal_class_test: RuntimeError
 evaluate_activation_in_method_class_test: RuntimeError
 evaluate_activation_test/instance: RuntimeError
 evaluate_activation_test/scope: RuntimeError
diff --git a/runtime/tests/vm/vm.status b/runtime/tests/vm/vm.status
index 1c25d3c..b097d1e 100644
--- a/runtime/tests/vm/vm.status
+++ b/runtime/tests/vm/vm.status
@@ -55,6 +55,7 @@
 [ $compiler != dartk ]
 cc/IsolateReload_KernelIncrementalCompile: SkipByDesign
 cc/IsolateReload_KernelIncrementalCompileAppAndLib: SkipByDesign
+cc/IsolateReload_KernelIncrementalCompileExpression: SkipByDesign
 cc/IsolateReload_KernelIncrementalCompileGenerics: SkipByDesign
 cc/Mixin_PrivateSuperResolution: Skip
 cc/Mixin_PrivateSuperResolutionCrossLibraryShouldFail: Skip
diff --git a/runtime/vm/bootstrap.cc b/runtime/vm/bootstrap.cc
index 2bd04fa..66037a1 100644
--- a/runtime/vm/bootstrap.cc
+++ b/runtime/vm/bootstrap.cc
@@ -336,7 +336,7 @@
 
   // The platform binary may contain other libraries (e.g., dart:_builtin or
   // dart:io) that will not be bundled with application.  Load them now.
-  const Object& result = loader.LoadProgram();
+  const Object& result = Object::Handle(zone, loader.LoadProgram());
   if (result.IsError()) {
     return Error::Cast(result).raw();
   }
diff --git a/runtime/vm/bootstrap_nocore.cc b/runtime/vm/bootstrap_nocore.cc
index 679c41f..b311558 100644
--- a/runtime/vm/bootstrap_nocore.cc
+++ b/runtime/vm/bootstrap_nocore.cc
@@ -96,7 +96,7 @@
 
   // The platform binary may contain other libraries (e.g., dart:_builtin or
   // dart:io) that will not be bundled with application.  Load them now.
-  const Object& result = loader.LoadProgram();
+  const Object& result = Object::Handle(loader.LoadProgram());
   if (result.IsError()) {
     return Error::Cast(result).raw();
   }
diff --git a/runtime/vm/compiler_test.cc b/runtime/vm/compiler_test.cc
index 091dd5d..bbc4d16 100644
--- a/runtime/vm/compiler_test.cc
+++ b/runtime/vm/compiler_test.cc
@@ -179,6 +179,10 @@
 }
 
 ISOLATE_UNIT_TEST_CASE(EvalExpressionWithLazyCompile) {
+  {  // Initialize an incremental compiler in DFE mode.
+    TransitionVMToNative transition(thread);
+    TestCase::LoadTestScript("", NULL);
+  }
   Library& lib = Library::Handle(Library::CoreLibrary());
 
   const String& expression = String::Handle(
@@ -193,6 +197,10 @@
 }
 
 ISOLATE_UNIT_TEST_CASE(EvalExpressionExhaustCIDs) {
+  {  // Initialize an incremental compiler in DFE mode.
+    TransitionVMToNative transition(thread);
+    TestCase::LoadTestScript("", NULL);
+  }
   Library& lib = Library::Handle(Library::CoreLibrary());
 
   const String& expression = String::Handle(String::New("3 + 4"));
diff --git a/runtime/vm/isolate_reload_test.cc b/runtime/vm/isolate_reload_test.cc
index 1362438..3c30629 100644
--- a/runtime/vm/isolate_reload_test.cc
+++ b/runtime/vm/isolate_reload_test.cc
@@ -8,6 +8,7 @@
 #include "vm/debugger_api_impl_test.h"
 #include "vm/globals.h"
 #include "vm/isolate.h"
+#include "vm/kernel_loader.h"
 #include "vm/lockers.h"
 #include "vm/thread_barrier.h"
 #include "vm/thread_pool.h"
diff --git a/runtime/vm/kernel_isolate.cc b/runtime/vm/kernel_isolate.cc
index cf12542..90325c2 100644
--- a/runtime/vm/kernel_isolate.cc
+++ b/runtime/vm/kernel_isolate.cc
@@ -52,6 +52,7 @@
 const int KernelIsolate::kUpdateSourcesTag = 1;
 const int KernelIsolate::kAcceptTag = 2;
 const int KernelIsolate::kTrainTag = 3;
+const int KernelIsolate::kCompileExpressionTag = 4;
 
 Dart_IsolateCreateCallback KernelIsolate::create_callback_ = NULL;
 Monitor* KernelIsolate::monitor_ = new Monitor();
@@ -341,6 +342,125 @@
   }
 
   Dart_KernelCompilationResult SendAndWaitForResponse(
+      Dart_Port kernel_port,
+      const char* expression,
+      const Array& definitions,
+      const Array& type_definitions,
+      char const* library_uri,
+      char const* klass,
+      bool is_static) {
+    Dart_CObject tag;
+    tag.type = Dart_CObject_kInt32;
+    tag.value.as_int32 = KernelIsolate::kCompileExpressionTag;
+
+    Dart_CObject send_port;
+    send_port.type = Dart_CObject_kSendPort;
+    send_port.value.as_send_port.id = port_;
+    send_port.value.as_send_port.origin_id = ILLEGAL_PORT;
+
+    Dart_CObject expression_object;
+    expression_object.type = Dart_CObject_kString;
+    expression_object.value.as_string = const_cast<char*>(expression);
+
+    Dart_CObject definitions_object;
+    intptr_t num_definitions = definitions.Length();
+    definitions_object.type = Dart_CObject_kArray;
+    definitions_object.value.as_array.length = num_definitions;
+
+    Dart_CObject** definitions_array = new Dart_CObject*[num_definitions];
+    for (intptr_t i = 0; i < num_definitions; ++i) {
+      definitions_array[i] = new Dart_CObject;
+      definitions_array[i]->type = Dart_CObject_kString;
+      definitions_array[i]->value.as_string = const_cast<char*>(
+          String::CheckedHandle(definitions.At(i)).ToCString());
+    }
+    definitions_object.value.as_array.values = definitions_array;
+
+    Dart_CObject type_definitions_object;
+    intptr_t num_type_definitions = type_definitions.Length();
+    type_definitions_object.type = Dart_CObject_kArray;
+    type_definitions_object.value.as_array.length = num_type_definitions;
+
+    Dart_CObject** type_definitions_array =
+        new Dart_CObject*[num_type_definitions];
+    for (intptr_t i = 0; i < num_type_definitions; ++i) {
+      type_definitions_array[i] = new Dart_CObject;
+      type_definitions_array[i]->type = Dart_CObject_kString;
+      type_definitions_array[i]->value.as_string = const_cast<char*>(
+          String::CheckedHandle(type_definitions.At(i)).ToCString());
+    }
+    type_definitions_object.value.as_array.values = type_definitions_array;
+
+    Dart_CObject library_uri_object;
+    library_uri_object.type = Dart_CObject_kString;
+    library_uri_object.value.as_string = const_cast<char*>(library_uri);
+
+    Dart_CObject class_object;
+    if (klass != NULL) {
+      class_object.type = Dart_CObject_kString;
+      class_object.value.as_string = const_cast<char*>(klass);
+    } else {
+      class_object.type = Dart_CObject_kNull;
+    }
+
+    Dart_CObject is_static_object;
+    is_static_object.type = Dart_CObject_kBool;
+    is_static_object.value.as_bool = is_static;
+
+    Isolate* isolate =
+        Thread::Current() != NULL ? Thread::Current()->isolate() : NULL;
+    ASSERT(isolate != NULL);
+    Dart_CObject isolate_id;
+    isolate_id.type = Dart_CObject_kInt64;
+    isolate_id.value.as_int64 =
+        isolate != NULL ? static_cast<int64_t>(isolate->main_port()) : 0;
+
+    Dart_CObject message;
+    message.type = Dart_CObject_kArray;
+    Dart_CObject suppress_warnings;
+    suppress_warnings.type = Dart_CObject_kBool;
+    suppress_warnings.value.as_bool = FLAG_suppress_fe_warnings;
+
+    Dart_CObject dart_sync_async;
+    dart_sync_async.type = Dart_CObject_kBool;
+    dart_sync_async.value.as_bool = FLAG_sync_async;
+
+    Dart_CObject* message_arr[] = {&tag,
+                                   &send_port,
+                                   &isolate_id,
+                                   &expression_object,
+                                   &definitions_object,
+                                   &type_definitions_object,
+                                   &library_uri_object,
+                                   &class_object,
+                                   &is_static_object,
+                                   &suppress_warnings,
+                                   &dart_sync_async};
+    message.value.as_array.values = message_arr;
+    message.value.as_array.length = ARRAY_SIZE(message_arr);
+    // Send the message.
+    Dart_PostCObject(kernel_port, &message);
+
+    // Wait for reply to arrive.
+    MonitorLocker ml(monitor_);
+    while (result_.status == Dart_KernelCompilationStatus_Unknown) {
+      ml.Wait();
+    }
+
+    for (intptr_t i = 0; i < num_definitions; ++i) {
+      delete definitions_array[i];
+    }
+    delete[] definitions_array;
+
+    for (intptr_t i = 0; i < num_type_definitions; ++i) {
+      delete type_definitions_array[i];
+    }
+    delete[] type_definitions_array;
+
+    return result_;
+  }
+
+  Dart_KernelCompilationResult SendAndWaitForResponse(
       int request_tag,
       Dart_Port kernel_port,
       const char* script_uri,
@@ -602,6 +722,27 @@
                                         0, NULL, true, NULL);
 }
 
+Dart_KernelCompilationResult KernelIsolate::CompileExpressionToKernel(
+    const char* expression,
+    const Array& definitions,
+    const Array& type_definitions,
+    const char* library_url,
+    const char* klass,
+    bool is_static) {
+  Dart_Port kernel_port = WaitForKernelPort();
+  if (kernel_port == ILLEGAL_PORT) {
+    Dart_KernelCompilationResult result;
+    result.status = Dart_KernelCompilationStatus_Unknown;
+    result.error = strdup("Error while initializing Kernel isolate");
+    return result;
+  }
+
+  KernelCompilationRequest request;
+  return request.SendAndWaitForResponse(kernel_port, expression, definitions,
+                                        type_definitions, library_url, klass,
+                                        is_static);
+}
+
 Dart_KernelCompilationResult KernelIsolate::UpdateInMemorySources(
     int source_files_count,
     Dart_SourceFile source_files[]) {
diff --git a/runtime/vm/kernel_isolate.h b/runtime/vm/kernel_isolate.h
index f9084cf..2e45b52 100644
--- a/runtime/vm/kernel_isolate.h
+++ b/runtime/vm/kernel_isolate.h
@@ -8,6 +8,7 @@
 #if !defined(DART_PRECOMPILED_RUNTIME)
 
 #include "include/dart_api.h"
+#include "include/dart_native_api.h"
 
 #include "vm/allocation.h"
 #include "vm/dart.h"
@@ -22,6 +23,7 @@
   static const int kUpdateSourcesTag;
   static const int kAcceptTag;
   static const int kTrainTag;
+  static const int kCompileExpressionTag;
 
   static void Run();
 
@@ -46,6 +48,14 @@
       int source_files_count,
       Dart_SourceFile source_files[]);
 
+  static Dart_KernelCompilationResult CompileExpressionToKernel(
+      const char* expression,
+      const Array& definitions,
+      const Array& type_definitions,
+      const char* library_url,
+      const char* klass,
+      bool is_static);
+
  protected:
   static Monitor* monitor_;
   static Dart_IsolateCreateCallback create_callback_;
diff --git a/runtime/vm/kernel_loader.cc b/runtime/vm/kernel_loader.cc
index 6a3358c..d151f4e 100644
--- a/runtime/vm/kernel_loader.cc
+++ b/runtime/vm/kernel_loader.cc
@@ -195,7 +195,7 @@
                                         bool process_pending_classes) {
   if (program->is_single_program()) {
     KernelLoader loader(program);
-    return loader.LoadProgram(process_pending_classes);
+    return Object::Handle(loader.LoadProgram(process_pending_classes));
   }
 
   kernel::Reader reader(program->kernel_data(), program->kernel_data_size());
@@ -216,7 +216,7 @@
     Program* subprogram = Program::ReadFrom(&reader, false);
     ASSERT(subprogram->is_single_program());
     KernelLoader loader(subprogram);
-    Object& load_result = loader.LoadProgram(false);
+    Object& load_result = Object::Handle(loader.LoadProgram(false));
     if (load_result.IsError()) return load_result;
 
     if (library.IsNull() && load_result.IsLibrary()) {
@@ -545,7 +545,7 @@
   potential_extension_libraries_ = GrowableObjectArray::null();
 }
 
-Object& KernelLoader::LoadProgram(bool process_pending_classes) {
+RawObject* KernelLoader::LoadProgram(bool process_pending_classes) {
   ASSERT(kernel_program_info_.constants() == Array::null());
 
   if (!program_->is_single_program()) {
@@ -557,15 +557,15 @@
   LongJumpScope jump;
   if (setjmp(*jump.Set()) == 0) {
     const intptr_t length = program_->library_count();
+    Object& last_library = Library::Handle(Z);
     for (intptr_t i = 0; i < length; i++) {
-      LoadLibrary(i);
+      last_library = LoadLibrary(i);
     }
 
     if (process_pending_classes) {
       if (!ClassFinalizer::ProcessPendingClasses()) {
         // Class finalization failed -> sticky error would be set.
-        Error& error = Error::Handle(Z);
-        error = H.thread()->sticky_error();
+        RawError* error = H.thread()->sticky_error();
         H.thread()->clear_sticky_error();
         return error;
       }
@@ -586,19 +586,18 @@
 
     NameIndex main = program_->main_method();
     if (main == -1) {
-      return Library::Handle(Z);
+      return Library::null();
     }
 
     NameIndex main_library = H.EnclosingName(main);
     Library& library = LookupLibrary(main_library);
 
-    return library;
+    return library.raw();
   }
 
   // Either class finalization failed or we caught a compile error.
   // In both cases sticky error would be set.
-  Error& error = Error::Handle(Z);
-  error = thread_->sticky_error();
+  RawError* error = thread_->sticky_error();
   thread_->clear_sticky_error();
   return error;
 }
@@ -683,7 +682,7 @@
   field.set_has_initializer(false);
 }
 
-void KernelLoader::LoadLibrary(intptr_t index) {
+RawLibrary* KernelLoader::LoadLibrary(intptr_t index) {
   if (!program_->is_single_program()) {
     FATAL(
         "Trying to load a concatenated dill file at a time where that is "
@@ -713,15 +712,16 @@
       // snapshot so we skip loading 'dart:vmservice_io'.
       skip_vmservice_library_ = library_helper.canonical_name_;
       ASSERT(H.IsLibrary(skip_vmservice_library_));
-      return;
+      return Library::null();
     }
   }
 
-  Library& library = LookupLibrary(library_helper.canonical_name_);
+  Library& library =
+      Library::Handle(Z, LookupLibrary(library_helper.canonical_name_).raw());
 
   // The Kernel library is external implies that it is already loaded.
   ASSERT(!library_helper.IsExternal() || library.Loaded());
-  if (library.Loaded()) return;
+  if (library.Loaded()) return library.raw();
 
   library_kernel_data_ =
       TypedData::New(kTypedDataUint8ArrayCid, library_size, Heap::kOld);
@@ -846,6 +846,8 @@
   toplevel_class.SetFunctions(Array::Handle(MakeFunctionsArray()));
   classes.Add(toplevel_class, Heap::kOld);
   if (!library.Loaded()) library.SetLoaded();
+
+  return library.raw();
 }
 
 void KernelLoader::LoadLibraryImportsAndExports(Library* library) {
diff --git a/runtime/vm/kernel_loader.h b/runtime/vm/kernel_loader.h
index ed47893..21f4bb2 100644
--- a/runtime/vm/kernel_loader.h
+++ b/runtime/vm/kernel_loader.h
@@ -128,7 +128,7 @@
 
   // Returns the library containing the main procedure, null if there
   // was no main procedure, or a failure object if there was an error.
-  Object& LoadProgram(bool process_pending_classes = true);
+  RawObject* LoadProgram(bool process_pending_classes = true);
 
   // Finds all libraries that have been modified in this incremental
   // version of the kernel program file.
@@ -137,7 +137,7 @@
                                     BitVector* modified_libs,
                                     bool force_reload);
 
-  void LoadLibrary(intptr_t index);
+  RawLibrary* LoadLibrary(intptr_t index);
 
   static void FinishLoading(const Class& klass);
 
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 1e84c31..4923104 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -34,6 +34,8 @@
 #include "vm/heap.h"
 #include "vm/isolate_reload.h"
 #include "vm/kernel.h"
+#include "vm/kernel_isolate.h"
+#include "vm/kernel_loader.h"
 #include "vm/native_symbol.h"
 #include "vm/object_store.h"
 #include "vm/parser.h"
@@ -3195,6 +3197,14 @@
   return String::ConcatAll(Array::Handle(Array::MakeFixedLength(src_pieces)));
 }
 
+static RawObject* EvaluateWithDFEHelper(const String& expression,
+                                        const Array& definitions,
+                                        const Array& type_definitions,
+                                        const String& library_url,
+                                        const String& klass,
+                                        bool is_static,
+                                        const Array& arguments);
+
 RawFunction* Function::EvaluateHelper(const Class& cls,
                                       const String& expr,
                                       const Array& param_names,
@@ -3221,6 +3231,12 @@
   return func.raw();
 }
 
+#if !defined(DART_PRECOMPILED_RUNTIME)
+static void ReleaseFetchedBytes(uint8_t* buffer) {
+  free(buffer);
+}
+#endif
+
 RawObject* Class::Evaluate(const String& expr,
                            const Array& param_names,
                            const Array& param_values) const {
@@ -3232,11 +3248,17 @@
     return UnhandledException::New(exception, stacktrace);
   }
 
-  const Function& eval_func = Function::Handle(
-      Function::EvaluateHelper(*this, expr, param_names, true));
-  const Object& result =
-      Object::Handle(DartEntry::InvokeFunction(eval_func, param_values));
-  return result.raw();
+  if (Library::Handle(library()).kernel_data() == TypedData::null()) {
+    const Function& eval_func = Function::Handle(
+        Function::EvaluateHelper(*this, expr, param_names, true));
+    return DartEntry::InvokeFunction(eval_func, param_values);
+  }
+
+  return EvaluateWithDFEHelper(
+      expr, param_names, Array::Handle(Array::New(0)),
+      String::Handle(Library::Handle(library()).url()),
+      IsTopLevel() ? String::Handle() : String::Handle(UserVisibleName()),
+      !IsTopLevel(), param_values);
 }
 
 // Ensure that top level parsing of the class has been done.
@@ -6033,6 +6055,9 @@
 
 // This field is heavily overloaded:
 //   eval function:           Script expression source
+//   kernel eval function:    Array[0] = Script
+//                            Array[1] = Kernel data
+//                            Array[2] = Kernel offset of enclosing library
 //   signature function:      SignatureData
 //   method extractor:        Function extracted closure function
 //   noSuchMethod dispatcher: Array arguments descriptor
@@ -7505,9 +7530,26 @@
   return PatchClass::Cast(obj).origin_class();
 }
 
+void Function::SetKernelDataAndScript(const Script& script,
+                                      const TypedData& data,
+                                      intptr_t offset) {
+  Array& data_field = Array::Handle(Array::New(3));
+  data_field.SetAt(0, script);
+  data_field.SetAt(1, data);
+  data_field.SetAt(2, Smi::Handle(Smi::New(offset)));
+  set_data(data_field);
+}
+
 RawScript* Function::script() const {
   // NOTE(turnidge): If you update this function, you probably want to
   // update Class::PatchFieldsAndFunctions() at the same time.
+  Object& data = Object::Handle(raw_ptr()->data_);
+  if (data.IsArray()) {
+    Object& script = Object::Handle(Array::Cast(data).At(0));
+    if (script.IsScript()) {
+      return Script::Cast(script).raw();
+    }
+  }
   if (token_pos() == TokenPosition::kMinSource) {
     // Testing for position 0 is an optimization that relies on temporary
     // eval functions having token position 0.
@@ -7532,6 +7574,13 @@
 }
 
 RawTypedData* Function::KernelData() const {
+  Object& data = Object::Handle(raw_ptr()->data_);
+  if (data.IsArray()) {
+    Object& script = Object::Handle(Array::Cast(data).At(0));
+    if (script.IsScript()) {
+      return TypedData::RawCast(Array::Cast(data).At(1));
+    }
+  }
   if (IsClosureFunction()) {
     Function& parent = Function::Handle(parent_function());
     ASSERT(!parent.IsNull());
@@ -7548,6 +7597,13 @@
 }
 
 intptr_t Function::KernelDataProgramOffset() const {
+  Object& data = Object::Handle(raw_ptr()->data_);
+  if (data.IsArray()) {
+    Object& script = Object::Handle(Array::Cast(data).At(0));
+    if (script.IsScript()) {
+      return Smi::Value(Smi::RawCast(Array::Cast(data).At(2)));
+    }
+  }
   if (IsClosureFunction()) {
     Function& parent = Function::Handle(parent_function());
     ASSERT(!parent.IsNull());
@@ -11310,10 +11366,15 @@
 RawObject* Library::Evaluate(const String& expr,
                              const Array& param_names,
                              const Array& param_values) const {
-  // Evaluate the expression as a static function of the toplevel class.
-  Class& top_level_class = Class::Handle(toplevel_class());
-  ASSERT(top_level_class.is_finalized());
-  return top_level_class.Evaluate(expr, param_names, param_values);
+  if (kernel_data() == TypedData::null()) {
+    // Evaluate the expression as a static function of the toplevel class.
+    Class& top_level_class = Class::Handle(toplevel_class());
+    ASSERT(top_level_class.is_finalized());
+    return top_level_class.Evaluate(expr, param_names, param_values);
+  }
+  return EvaluateWithDFEHelper(expr, param_names, Array::Handle(Array::New(0)),
+                               String::Handle(url()), String::Handle(), false,
+                               param_values);
 }
 
 void Library::InitNativeWrappersLibrary(Isolate* isolate, bool is_kernel) {
@@ -11370,6 +11431,118 @@
 };
 typedef UnorderedHashMap<LibraryLookupTraits> LibraryLookupMap;
 
+static RawObject* EvaluateWithDFEHelper(const String& expression,
+                                        const Array& definitions,
+                                        const Array& type_definitions,
+                                        const String& library_url,
+                                        const String& klass,
+                                        bool is_static,
+                                        const Array& arguments) {
+#if defined(DART_PRECOMPILED_RUNTIME)
+  const String& error_str = String::Handle(
+      String::New("Kernel service isolate not available in precompiled mode."));
+  return ApiError::New(error_str);
+#else
+  Isolate* I = Isolate::Current();
+  Thread* T = Thread::Current();
+
+  Dart_KernelCompilationResult compilation_result;
+  {
+    TransitionVMToNative transition(T);
+    compilation_result = KernelIsolate::CompileExpressionToKernel(
+        expression.ToCString(), definitions, type_definitions,
+        library_url.ToCString(), klass.IsNull() ? NULL : klass.ToCString(),
+        is_static);
+  }
+
+  Function& callee = Function::Handle();
+  intptr_t num_cids = I->class_table()->NumCids();
+  intptr_t num_libs =
+      GrowableObjectArray::Handle(I->object_store()->libraries()).Length();
+
+  void* kernel_pgm = NULL;
+  if (compilation_result.status != Dart_KernelCompilationStatus_Ok) {
+    const String& prefix =
+        String::Handle(String::New("Kernel isolate rejected this request:\n"));
+    const String& error_str = String::Handle(String::Concat(
+        prefix, String::Handle(String::New(compilation_result.error))));
+    free(compilation_result.error);
+    return ApiError::New(error_str);
+  }
+
+  const uint8_t* kernel_file = compilation_result.kernel;
+  intptr_t kernel_length = compilation_result.kernel_size;
+  ASSERT(kernel_file != NULL);
+  kernel_pgm =
+      Dart_ReadKernelBinary(kernel_file, kernel_length, ReleaseFetchedBytes);
+  if (kernel_pgm == NULL) {
+    return ApiError::New(String::Handle(
+        String::New("Kernel isolate returned ill-formed kernel.")));
+  }
+
+  // Load the program with the debug procedure as a regular, independent
+  // program.
+  kernel::KernelLoader loader(reinterpret_cast<kernel::Program*>(kernel_pgm));
+  loader.LoadProgram();
+  ASSERT(I->class_table()->NumCids() > num_cids &&
+         GrowableObjectArray::Handle(I->object_store()->libraries()).Length() ==
+             num_libs + 1);
+  const String& fake_library_url =
+      String::Handle(String::New("evaluate:source"));
+  const Library& loaded =
+      Library::Handle(Library::LookupLibrary(T, fake_library_url));
+  ASSERT(!loaded.IsNull());
+
+  String& debug_name = String::Handle(
+      String::New(Symbols::Symbol(Symbols::kDebugProcedureNameId)));
+  Class& fake_class = Class::Handle();
+  if (!klass.IsNull()) {
+    fake_class = loaded.LookupClass(String::Handle(String::New(klass)));
+    ASSERT(!fake_class.IsNull());
+    callee = fake_class.LookupFunctionAllowPrivate(debug_name);
+  } else {
+    callee = loaded.LookupFunctionAllowPrivate(debug_name);
+  }
+  ASSERT(!callee.IsNull());
+
+  // Save the loaded library's kernel data to the generic "data" field of the
+  // callee, so it doesn't require access it's parent library during
+  // compilation.
+  callee.SetKernelDataAndScript(Script::Handle(callee.script()),
+                                TypedData::Handle(loaded.kernel_data()),
+                                loaded.kernel_offset());
+
+  // Reparent the callee to the real enclosing class so we can remove the fake
+  // class and library from the object store.
+  const Library& real_library =
+      Library::Handle(Library::LookupLibrary(T, library_url));
+  ASSERT(!real_library.IsNull());
+  Class& real_class = Class::Handle();
+  if (!klass.IsNull()) {
+    real_class = real_library.LookupClass(String::Handle(String::New(klass)));
+  } else {
+    real_class = real_library.toplevel_class();
+  }
+  ASSERT(!real_class.IsNull());
+
+  callee.set_owner(real_class);
+
+  // Unlink the fake library and class from the object store.
+  GrowableObjectArray::Handle(I->object_store()->libraries())
+      .SetLength(num_libs);
+  I->class_table()->SetNumCids(num_cids);
+  if (!fake_class.IsNull()) {
+    fake_class.set_id(kIllegalCid);
+  }
+  LibraryLookupMap libraries_map(I->object_store()->libraries_map());
+  bool removed = libraries_map.Remove(fake_library_url);
+  ASSERT(removed);
+  I->object_store()->set_libraries_map(libraries_map.Release());
+
+  return DartEntry::InvokeFunction(callee, arguments);
+#endif
+}
+
 // Returns library with given url in current isolate, or NULL.
 RawLibrary* Library::LookupLibrary(Thread* thread, const String& url) {
   Zone* zone = thread->zone();
@@ -15522,8 +15695,6 @@
                               const String& expr,
                               const Array& param_names,
                               const Array& param_values) const {
-  const Function& eval_func = Function::Handle(
-      Function::EvaluateHelper(method_cls, expr, param_names, false));
   const Array& args = Array::Handle(Array::New(1 + param_values.Length()));
   PassiveObject& param = PassiveObject::Handle();
   args.SetAt(0, *this);
@@ -15531,7 +15702,18 @@
     param = param_values.At(i);
     args.SetAt(i + 1, param);
   }
-  return DartEntry::InvokeFunction(eval_func, args);
+
+  const Library& library = Library::Handle(method_cls.library());
+  if (library.kernel_data() == TypedData::null()) {
+    const Function& eval_func = Function::Handle(
+        Function::EvaluateHelper(method_cls, expr, param_names, false));
+    return DartEntry::InvokeFunction(eval_func, args);
+  } else {
+    return EvaluateWithDFEHelper(
+        expr, param_names, Array::Handle(Array::New(0)),
+        String::Handle(Library::Handle(method_cls.library()).url()),
+        String::Handle(method_cls.UserVisibleName()), false, args);
+  }
 }
 
 RawObject* Instance::HashCode() const {
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 6a99622..7d373a9 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -2130,6 +2130,7 @@
   void ZeroEdgeCounters() const;
 
   RawClass* Owner() const;
+  void set_owner(const Object& value) const;
   RawClass* origin() const;
   RawScript* script() const;
   RawObject* RawOwner() const { return raw_ptr()->owner_; }
@@ -2488,6 +2489,10 @@
     set_optimized_call_site_count(value);
   }
 
+  void SetKernelDataAndScript(const Script& script,
+                              const TypedData& data,
+                              intptr_t offset);
+
   intptr_t KernelDataProgramOffset() const;
 
   RawTypedData* KernelData() const;
@@ -2888,7 +2893,6 @@
   void set_name(const String& value) const;
   void set_kind(RawFunction::Kind value) const;
   void set_parent_function(const Function& value) const;
-  void set_owner(const Object& value) const;
   RawFunction* implicit_closure_function() const;
   void set_implicit_closure_function(const Function& value) const;
   RawInstance* implicit_static_closure() const;
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index 4890d04..0fc1385 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -4495,6 +4495,7 @@
   LinkedHashMap& cc_map = LinkedHashMap::Handle(LinkedHashMap::NewDefault());
 
   // 3. Expect them to have identical structure.
+  TransitionNativeToVM transition(thread);
   CheckIdenticalHashStructure(dart_map, cc_map);
 }
 
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index 4b3f6bc..5ca98a9 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -450,7 +450,8 @@
   V(PrependTypeArguments, "_prependTypeArguments")                             \
   V(DartDeveloperCausalAsyncStacks, "dart.developer.causal_async_stacks")      \
   V(_AsyncStarListenHelper, "_asyncStarListenHelper")                          \
-  V(GrowRegExpStack, "_growRegExpStack")
+  V(GrowRegExpStack, "_growRegExpStack")                                       \
+  V(DebugProcedureName, ":Eval")
 
 // Contains a list of frequently used strings in a canonicalized form. This
 // list is kept in the vm_isolate in order to share the copy across isolates
diff --git a/runtime/vm/unit_test.cc b/runtime/vm/unit_test.cc
index 10795eb..7dd78a0 100644
--- a/runtime/vm/unit_test.cc
+++ b/runtime/vm/unit_test.cc
@@ -267,7 +267,13 @@
       url, FLAG_strong ? platform_strong_dill : platform_dill,
       FLAG_strong ? platform_strong_dill_size : platform_dill_size,
       sourcefiles_count, sourcefiles, incrementally, NULL);
+  return ValidateCompilationResult(zone, compilation_result, kernel_pgm);
+}
 
+char* TestCase::ValidateCompilationResult(
+    Zone* zone,
+    Dart_KernelCompilationResult compilation_result,
+    void** kernel_pgm) {
   if (compilation_result.status != Dart_KernelCompilationStatus_Ok) {
     char* result =
         OS::SCreate(zone, "Compilation failed %s", compilation_result.error);
diff --git a/runtime/vm/unit_test.h b/runtime/vm/unit_test.h
index 5aa1d6a..7a500d4 100644
--- a/runtime/vm/unit_test.h
+++ b/runtime/vm/unit_test.h
@@ -379,6 +379,10 @@
                                     const char* name,
                                     void* data = NULL);
 
+  static char* ValidateCompilationResult(Zone* zone,
+                                         Dart_KernelCompilationResult result,
+                                         void** kernel_pgm);
+
   RunEntry* const run_;
 };