diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h index f20475b64e52..8ccbe7a53801 100644 --- a/runtime/include/dart_api.h +++ b/runtime/include/dart_api.h @@ -3139,7 +3139,8 @@ typedef struct { DART_EXPORT Dart_KernelCompilationResult Dart_CompileSourcesToKernel(const char* script_uri, int source_files_count, - Dart_SourceFile source_files[]); + Dart_SourceFile source_files[], + bool incremental_compile); #define DART_KERNEL_ISOLATE_NAME "kernel-service" diff --git a/runtime/tests/vm/vm.status b/runtime/tests/vm/vm.status index acb308d2d639..689ec8aeab8e 100644 --- a/runtime/tests/vm/vm.status +++ b/runtime/tests/vm/vm.status @@ -249,6 +249,11 @@ dart/redirection_type_shuffling_test/none: RuntimeError # Issue 29201 dart/regress29620_test: RuntimeError dart/spawn_shutdown_test: SkipSlow +[ ($compiler != dartk) ] +cc/IsolateReload_KernelIncrementalCompile: Skip +cc/IsolateReload_KernelIncrementalCompileAppAndLib: Skip +cc/IsolateReload_KernelIncrementalCompileGenerics: Skip + [ ($compiler == dartk) && ($runtime == vm) ] cc/CanonicalizationInScriptSnapshots: Fail cc/Class_ComputeEndTokenPos: Crash @@ -323,6 +328,7 @@ cc/IsolateReload_EnumToNotEnum: Skip cc/IsolateReload_ExportedLibModified: Crash cc/IsolateReload_ImportedLibModified: Crash cc/IsolateReload_ImportedMixinFunction: Crash +cc/IsolateReload_KernelIncrementalCompileGenerics: Crash # Issue 30322 cc/IsolateReload_LibraryHide: Crash cc/IsolateReload_LibraryLookup: Fail cc/IsolateReload_LibraryShow: Crash @@ -415,6 +421,7 @@ cc/Service_TokenStream: Fail cc/InjectNativeFields1: Crash cc/InjectNativeFields3: Crash cc/Service_TokenStream: Crash +cc/IsolateReload_KernelIncrementalCompileAppAndLib: Crash # Issue 30420 [ ($compiler == dartk) && ($runtime == vm) && ($system == macos) ] cc/IsolateReload_DanglingGetter_Class: Crash diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc index 4f37d4cf5432..a868878a7dd4 100644 --- a/runtime/vm/dart_api_impl.cc +++ b/runtime/vm/dart_api_impl.cc @@ -5889,15 +5889,16 @@ Dart_CompileToKernel(const char* script_uri) { DART_EXPORT Dart_KernelCompilationResult Dart_CompileSourcesToKernel(const char* script_uri, int source_files_count, - Dart_SourceFile sources[]) { + Dart_SourceFile sources[], + bool incremental_compile) { #ifdef DART_PRECOMPILED_RUNTIME Dart_KernelCompilationResult result; result.status = Dart_KernelCompilationStatus_Unknown; result.error = strdup("Dart_CompileSourcesToKernel is unsupported."); return result; #else - return KernelIsolate::CompileToKernel(script_uri, source_files_count, - sources); + return KernelIsolate::CompileToKernel(script_uri, source_files_count, sources, + incremental_compile); #endif } diff --git a/runtime/vm/isolate_reload_test.cc b/runtime/vm/isolate_reload_test.cc index ea63190baed9..cbe991bd7d58 100644 --- a/runtime/vm/isolate_reload_test.cc +++ b/runtime/vm/isolate_reload_test.cc @@ -67,6 +67,217 @@ TEST_CASE(IsolateReload_FunctionReplacement) { EXPECT_EQ(10, SimpleInvoke(lib, "main")); } +TEST_CASE(IsolateReload_IncrementalCompile) { + const char* kScriptChars = + "main() {\n" + " return 42;\n" + "}\n"; + Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL); + EXPECT_VALID(lib); + Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL); + int64_t value = 0; + result = Dart_IntegerToInt64(result, &value); + EXPECT_VALID(result); + EXPECT_EQ(42, value); + + const char* kUpdatedScriptChars = + "main() {\n" + " return 24;\n" + "}\n" + ""; + lib = TestCase::ReloadTestScript(kUpdatedScriptChars); + EXPECT_VALID(lib); + result = Dart_Invoke(lib, NewString("main"), 0, NULL); + result = Dart_IntegerToInt64(result, &value); + EXPECT_VALID(result); + EXPECT_EQ(24, value); +} + +TEST_CASE(IsolateReload_KernelIncrementalCompile) { + // clang-format off + Dart_SourceFile sourcefiles[] = { + { + "file:///test-app", + "main() {\n" + " return 42;\n" + "}\n", + }, + { + "file:///.packages", "untitled:/" + }}; + // clang-format on + + Dart_Handle lib = TestCase::LoadTestScriptWithDFE( + sizeof(sourcefiles) / sizeof(Dart_SourceFile), sourcefiles, + NULL /* resolver */, true /* finalize */, true /* incrementally */); + Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL); + int64_t value = 0; + result = Dart_IntegerToInt64(result, &value); + EXPECT_VALID(result); + EXPECT_EQ(42, value); + + // clang-format off + Dart_SourceFile updated_sourcefiles[] = { + { + "file:///test-app", + "main() {\n" + " return 24;\n" + "}\n" + "" + }}; + // clang-format on + { + void* kernel_pgm = NULL; + char* error = TestCase::CompileTestScriptWithDFE( + "file:///test-app", + sizeof(updated_sourcefiles) / sizeof(Dart_SourceFile), + updated_sourcefiles, &kernel_pgm, true /* incrementally */); + EXPECT(error == NULL); + EXPECT_NOTNULL(kernel_pgm); + + lib = TestCase::ReloadTestKernel(kernel_pgm); + EXPECT_VALID(lib); + } + result = Dart_Invoke(lib, NewString("main"), 0, NULL); + result = Dart_IntegerToInt64(result, &value); + EXPECT_VALID(result); + EXPECT_EQ(24, value); +} + +TEST_CASE(IsolateReload_KernelIncrementalCompileAppAndLib) { + // clang-format off + Dart_SourceFile sourcefiles[] = { + { + "file:///test-app.dart", + "import 'test-lib.dart';\n" + "main() {\n" + " return WhatsTheMeaningOfAllThis();\n" + "}\n", + }, + { + "file:///test-lib.dart", + "WhatsTheMeaningOfAllThis() {\n" + " return 42;\n" + "}\n", + }, + { + "file:///.packages", "untitled:/" + }}; + // clang-format on + + Dart_Handle lib = TestCase::LoadTestScriptWithDFE( + sizeof(sourcefiles) / sizeof(Dart_SourceFile), sourcefiles, + NULL /* resolver */, true /* finalize */, true /* incrementally */); + EXPECT_VALID(lib); + Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL); + int64_t value = 0; + result = Dart_IntegerToInt64(result, &value); + EXPECT_VALID(result); + EXPECT_EQ(42, value); + + // clang-format off + Dart_SourceFile updated_sourcefiles[] = { + { + "file:///test-lib.dart", + "WhatsTheMeaningOfAllThis() {\n" + " return 24;\n" + "}\n" + "" + }}; + // clang-format on + { + void* kernel_pgm = NULL; + char* error = TestCase::CompileTestScriptWithDFE( + "file:///test-app", + sizeof(updated_sourcefiles) / sizeof(Dart_SourceFile), + updated_sourcefiles, &kernel_pgm, true /* incrementally */); + EXPECT(error == NULL); + EXPECT_NOTNULL(kernel_pgm); + + lib = TestCase::ReloadTestKernel(kernel_pgm); + EXPECT_VALID(lib); + } + result = Dart_Invoke(lib, NewString("main"), 0, NULL); + result = Dart_IntegerToInt64(result, &value); + EXPECT_VALID(result); + EXPECT_EQ(24, value); +} + +TEST_CASE(IsolateReload_KernelIncrementalCompileGenerics) { + // clang-format off + Dart_SourceFile sourcefiles[] = { + { + "file:///test-app.dart", + "import 'test-lib.dart';\n" + "class Account {\n" + " int balance() => 42;\n" + "}\n" + "class MyAccountState extends State {\n" + " MyAccountState(Account a): super(a) {}\n" + "}\n" + "main() {\n" + " return (new MyAccountState(new Account()))\n" + " .howAreTheThings().balance();\n" + "}\n", + }, + { + "file:///test-lib.dart", + "class State {\n" + " T t;" + " State(this.t);\n" + " T howAreTheThings() => t;\n" + "}\n", + }, + { + "file:///.packages", "untitled:/" + }}; + // clang-format on + + Dart_Handle lib = TestCase::LoadTestScriptWithDFE( + sizeof(sourcefiles) / sizeof(Dart_SourceFile), sourcefiles, + NULL /* resolver */, true /* finalize */, true /* incrementally */); + EXPECT_VALID(lib); + Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL); + int64_t value = 0; + result = Dart_IntegerToInt64(result, &value); + EXPECT_VALID(result); + EXPECT_EQ(42, value); + + // clang-format off + Dart_SourceFile updated_sourcefiles[] = { + { + "file:///test-app.dart", + "import 'test-lib.dart';\n" + "class Account {\n" + " int balance() => 24;\n" + "}\n" + "class MyAccountState extends State {\n" + " MyAccountState(Account a): super(a) {}\n" + "}\n" + "main() {\n" + " return (new MyAccountState(new Account()))\n" + " .howAreTheThings().balance();\n" + "}\n", + }}; + // clang-format on + { + void* kernel_pgm = NULL; + char* error = TestCase::CompileTestScriptWithDFE( + "file:///test-app", + sizeof(updated_sourcefiles) / sizeof(Dart_SourceFile), + updated_sourcefiles, &kernel_pgm, true /* incrementally */); + EXPECT(error == NULL); + EXPECT_NOTNULL(kernel_pgm); + + lib = TestCase::ReloadTestKernel(kernel_pgm); + EXPECT_VALID(lib); + } + result = Dart_Invoke(lib, NewString("main"), 0, NULL); + result = Dart_IntegerToInt64(result, &value); + EXPECT_VALID(result); + EXPECT_EQ(24, value); +} + TEST_CASE(IsolateReload_BadClass) { const char* kScript = "class Foo {\n" diff --git a/runtime/vm/kernel_isolate.cc b/runtime/vm/kernel_isolate.cc index 6020a27f8b75..75d52b9b5708 100644 --- a/runtime/vm/kernel_isolate.cc +++ b/runtime/vm/kernel_isolate.cc @@ -248,6 +248,31 @@ Dart_Port KernelIsolate::WaitForKernelPort() { return kernel_port_; } +static Dart_CObject BuildFilesPairs(int source_files_count, + Dart_SourceFile source_files[]) { + Dart_CObject files; + files.type = Dart_CObject_kArray; + files.value.as_array.length = source_files_count * 2; + // typedef Dart_CObject* Dart_CObjectPtr; + Dart_CObject** fileNamePairs = new Dart_CObject*[source_files_count * 2]; + for (int i = 0; i < source_files_count; i++) { + Dart_CObject* source_uri = new Dart_CObject(); + source_uri->type = Dart_CObject_kString; + source_uri->value.as_string = const_cast(source_files[i].uri); + fileNamePairs[i * 2] = source_uri; + + Dart_CObject* source_code = new Dart_CObject(); + source_code->type = Dart_CObject_kTypedData; + source_code->value.as_typed_data.type = Dart_TypedData_kUint8; + source_code->value.as_typed_data.length = strlen(source_files[i].source); + source_code->value.as_typed_data.values = + reinterpret_cast(const_cast(source_files[i].source)); + fileNamePairs[(i * 2) + 1] = source_code; + } + files.value.as_array.values = fileNamePairs; + return files; +} + class KernelCompilationRequest : public ValueObject { public: KernelCompilationRequest() @@ -275,10 +300,12 @@ class KernelCompilationRequest : public ValueObject { Dart_Port kernel_port, const char* script_uri, int source_files_count, - Dart_SourceFile source_files[]) { - // Build the [null, send_port, script_uri] message for the Kernel isolate: - // null tag tells it that request came from this code, instead of Loader - // so that it can given a more informative response. + Dart_SourceFile source_files[], + bool incremental_compile) { + // Build the [null, send_port, script_uri, incremental_compile, isolate_id, + // [files]] message for the Kernel isolate: null tag tells it that request + // came from this code, instead of Loader so that it can given a more + // informative response. Dart_CObject tag; tag.type = Dart_CObject_kNull; @@ -291,44 +318,38 @@ class KernelCompilationRequest : public ValueObject { uri.type = Dart_CObject_kString; uri.value.as_string = const_cast(script_uri); + Dart_CObject dart_incremental; + dart_incremental.type = Dart_CObject_kBool; + dart_incremental.value.as_bool = incremental_compile; + + // TODO(aam): Assert that isolate exists once we move CompileAndReadScript + // compilation logic out of CreateIsolateAndSetupHelper and into + // IsolateSetupHelper in main.cc. + Isolate* isolate = + Thread::Current() != NULL ? Thread::Current()->isolate() : NULL; + if (incremental_compile) { + ASSERT(isolate != NULL); + } + Dart_CObject isolate_id; + isolate_id.type = Dart_CObject_kInt64; + isolate_id.value.as_int64 = + isolate != NULL ? static_cast(isolate->main_port()) : 0; + Dart_CObject message; message.type = Dart_CObject_kArray; - if (source_files_count == 0) { - static const intptr_t message_len = 3; - Dart_CObject* message_arr[] = {&tag, &send_port, &uri}; - message.value.as_array.values = message_arr; - message.value.as_array.length = message_len; - // Send the message. - Dart_PostCObject(kernel_port, &message); - } else { - Dart_CObject files; - files.type = Dart_CObject_kArray; - files.value.as_array.length = source_files_count * 2; - // typedef Dart_CObject* Dart_CObjectPtr; - Dart_CObject** fileNamePairs = new Dart_CObject*[source_files_count * 2]; - for (int i = 0; i < source_files_count; i++) { - Dart_CObject* source_uri = new Dart_CObject(); - source_uri->type = Dart_CObject_kString; - source_uri->value.as_string = const_cast(source_files[i].uri); - fileNamePairs[i * 2] = source_uri; - - Dart_CObject* source_code = new Dart_CObject(); - source_code->type = Dart_CObject_kTypedData; - source_code->value.as_typed_data.type = Dart_TypedData_kUint8; - source_code->value.as_typed_data.length = - strlen(source_files[i].source); - source_code->value.as_typed_data.values = reinterpret_cast( - const_cast(source_files[i].source)); - fileNamePairs[(i * 2) + 1] = source_code; - } - files.value.as_array.values = fileNamePairs; - static const intptr_t message_len = 4; - Dart_CObject* message_arr[] = {&tag, &send_port, &uri, &files}; - message.value.as_array.values = message_arr; - message.value.as_array.length = message_len; - Dart_PostCObject(kernel_port, &message); + intptr_t message_len = 5; + Dart_CObject files; + if (source_files_count != 0) { + files = BuildFilesPairs(source_files_count, source_files); + message_len++; } + Dart_CObject* message_arr[] = { + &tag, &send_port, &uri, &dart_incremental, &isolate_id, &files}; + message.value.as_array.values = message_arr; + message.value.as_array.length = message_len; + // Send the message. + Dart_PostCObject(kernel_port, &message); // Wait for reply to arrive. MonitorLocker ml(monitor_); @@ -439,7 +460,8 @@ KernelCompilationRequest* KernelCompilationRequest::requests_ = NULL; Dart_KernelCompilationResult KernelIsolate::CompileToKernel( const char* script_uri, int source_file_count, - Dart_SourceFile source_files[]) { + Dart_SourceFile source_files[], + bool incremental_compile) { // This must be the main script to be loaded. Wait for Kernel isolate // to finish initialization. Dart_Port kernel_port = WaitForKernelPort(); @@ -452,7 +474,8 @@ Dart_KernelCompilationResult KernelIsolate::CompileToKernel( KernelCompilationRequest request; return request.SendAndWaitForResponse(kernel_port, script_uri, - source_file_count, source_files); + source_file_count, source_files, + incremental_compile); } #endif // DART_PRECOMPILED_RUNTIME diff --git a/runtime/vm/kernel_isolate.h b/runtime/vm/kernel_isolate.h index 7cb9f782b6c2..c87aea02e6f2 100644 --- a/runtime/vm/kernel_isolate.h +++ b/runtime/vm/kernel_isolate.h @@ -30,7 +30,8 @@ class KernelIsolate : public AllStatic { static Dart_KernelCompilationResult CompileToKernel( const char* script_uri, int source_files_count = 0, - Dart_SourceFile source_files[] = NULL); + Dart_SourceFile source_files[] = NULL, + bool incremental_compile = false); protected: static Monitor* monitor_; diff --git a/runtime/vm/unit_test.cc b/runtime/vm/unit_test.cc index e49a3d3570d9..179e53762c10 100644 --- a/runtime/vm/unit_test.cc +++ b/runtime/vm/unit_test.cc @@ -164,11 +164,12 @@ static Dart_Handle ResolvePackageUri(const char* uri_chars) { } static ThreadLocalKey script_reload_key = kUnsetThreadLocalKey; +static ThreadLocalKey kernel_reload_key = kUnsetThreadLocalKey; char* TestCase::CompileTestScriptWithDFE(const char* url, const char* source, - void** kernel_pgm) { - Zone* zone = Thread::Current()->zone(); + void** kernel_pgm, + bool incrementally) { // clang-format off Dart_SourceFile sourcefiles[] = { { @@ -178,9 +179,19 @@ char* TestCase::CompileTestScriptWithDFE(const char* url, "file:///.packages", "untitled:/" }}; // clang-format on - int sourcefiles_count = sizeof(sourcefiles) / sizeof(Dart_SourceFile); - Dart_KernelCompilationResult compilation_result = - Dart_CompileSourcesToKernel(url, sourcefiles_count, sourcefiles); + return CompileTestScriptWithDFE(url, + sizeof(sourcefiles) / sizeof(Dart_SourceFile), + sourcefiles, kernel_pgm, incrementally); +} + +char* TestCase::CompileTestScriptWithDFE(const char* url, + int sourcefiles_count, + Dart_SourceFile sourcefiles[], + void** kernel_pgm, + bool incrementally) { + Zone* zone = Thread::Current()->zone(); + Dart_KernelCompilationResult compilation_result = Dart_CompileSourcesToKernel( + url, sourcefiles_count, sourcefiles, incrementally); if (compilation_result.status != Dart_KernelCompilationStatus_Ok) { return OS::SCreate(zone, "Compilation failed %s", compilation_result.error); @@ -202,25 +213,35 @@ static Dart_Handle LibraryTagHandler(Dart_LibraryTag tag, Dart_Handle url) { if (FLAG_use_dart_frontend) { // Reload request. - ASSERT(script_reload_key != kUnsetThreadLocalKey); - const char* script_source = reinterpret_cast( - OSThread::GetThreadLocal(script_reload_key)); - ASSERT(script_source != NULL); - OSThread::SetThreadLocal(script_reload_key, 0); + const char* urlstr = NULL; Dart_Handle result = Dart_StringToCString(url, &urlstr); if (Dart_IsError(result)) { return Dart_NewApiError("accessing url characters failed"); } + + // Updated library either arrives as dart source or as + // a precompiled kernel binary. void* kernel_pgm; - char* error = - TestCase::CompileTestScriptWithDFE(urlstr, script_source, &kernel_pgm); - if (error == NULL) { - return Dart_LoadScript(url, Dart_Null(), - reinterpret_cast(kernel_pgm), 0, 0); + if (script_reload_key != kUnsetThreadLocalKey) { + const char* script_source = reinterpret_cast( + OSThread::GetThreadLocal(script_reload_key)); + ASSERT(script_source != NULL); + OSThread::SetThreadLocal(script_reload_key, 0); + char* error = TestCase::CompileTestScriptWithDFE(urlstr, script_source, + &kernel_pgm); + if (error != NULL) { + return Dart_NewApiError(error); + } } else { - return Dart_NewApiError(error); + ASSERT(kernel_reload_key != kUnsetThreadLocalKey); + kernel_pgm = + reinterpret_cast(OSThread::GetThreadLocal(kernel_reload_key)); + ASSERT(kernel_pgm != NULL); + OSThread::SetThreadLocal(kernel_reload_key, 0); } + return Dart_LoadScript(url, Dart_Null(), + reinterpret_cast(kernel_pgm), 0, 0); } if (tag == Dart_kCanonicalizeUrl) { Dart_Handle library_url = Dart_LibraryUrl(library); @@ -328,47 +349,50 @@ static Dart_Handle LoadTestScriptWithVMParser(const char* script, return lib; } -static Dart_Handle LoadTestScriptWithDFE(const char* script, - Dart_NativeEntryResolver resolver, - const char* lib_url, - bool finalize_classes) { - Dart_Handle url = NewString(lib_url); - Dart_Handle result = Dart_SetLibraryTagHandler(LibraryTagHandler); - EXPECT_VALID(result); - void* kernel_pgm = NULL; - char* error = - TestCase::CompileTestScriptWithDFE(lib_url, script, &kernel_pgm); - if (error == NULL) { - Dart_Handle lib = Dart_LoadScript( - url, Dart_Null(), reinterpret_cast(kernel_pgm), 0, 0); - DART_CHECK_VALID(lib); - result = Dart_SetNativeResolver(lib, resolver, NULL); - DART_CHECK_VALID(result); - if (finalize_classes) { - result = Dart_FinalizeLoading(false); - DART_CHECK_VALID(result); - } - return lib; - } else { - return Dart_NewApiError(error); - } -} - Dart_Handle TestCase::LoadTestScript(const char* script, Dart_NativeEntryResolver resolver, const char* lib_url, bool finalize_classes) { - if (!FLAG_use_dart_frontend) { + if (FLAG_use_dart_frontend) { + Dart_SourceFile sourcefiles[] = { + {OS::SCreate(Thread::Current()->zone(), "file:///%s", lib_url), + script}}; + return LoadTestScriptWithDFE(sizeof(sourcefiles) / sizeof(Dart_SourceFile), + sourcefiles, resolver, finalize_classes); + } else { return LoadTestScriptWithVMParser(script, resolver, lib_url, finalize_classes); - } else { - Zone* zone = Thread::Current()->zone(); - char* resolved_lib_url = OS::SCreate(zone, "file:///%s", lib_url); - return LoadTestScriptWithDFE(script, resolver, resolved_lib_url, - finalize_classes); } } +Dart_Handle TestCase::LoadTestScriptWithDFE(int sourcefiles_count, + Dart_SourceFile sourcefiles[], + Dart_NativeEntryResolver resolver, + bool finalize, + bool incrementally) { + // First script is the main script. + Dart_Handle url = NewString(sourcefiles[0].uri); + Dart_Handle result = Dart_SetLibraryTagHandler(LibraryTagHandler); + EXPECT_VALID(result); + void* kernel_pgm = NULL; + char* error = TestCase::CompileTestScriptWithDFE( + sourcefiles[0].uri, sourcefiles_count, sourcefiles, &kernel_pgm, + incrementally); + if (error != NULL) { + return Dart_NewApiError(error); + } + Dart_Handle lib = Dart_LoadScript( + url, Dart_Null(), reinterpret_cast(kernel_pgm), 0, 0); + DART_CHECK_VALID(lib); + result = Dart_SetNativeResolver(lib, resolver, NULL); + DART_CHECK_VALID(result); + if (finalize) { + result = Dart_FinalizeLoading(false); + DART_CHECK_VALID(result); + } + return lib; +} + #ifndef PRODUCT void TestCase::SetReloadTestScript(const char* script) { @@ -381,6 +405,16 @@ void TestCase::SetReloadTestScript(const char* script) { OSThread::SetThreadLocal(script_reload_key, reinterpret_cast(script)); } +void TestCase::SetReloadTestKernel(const void* kernel) { + if (kernel_reload_key == kUnsetThreadLocalKey) { + kernel_reload_key = OSThread::CreateThreadLocal(); + } + ASSERT(kernel_reload_key != kUnsetThreadLocalKey); + ASSERT(OSThread::GetThreadLocal(kernel_reload_key) == 0); + // Store the new script in TLS. + OSThread::SetThreadLocal(kernel_reload_key, reinterpret_cast(kernel)); +} + Dart_Handle TestCase::TriggerReload() { Isolate* isolate = Isolate::Current(); JSONStream js; @@ -435,6 +469,28 @@ Dart_Handle TestCase::ReloadTestScript(const char* script) { return result; } +Dart_Handle TestCase::ReloadTestKernel(const void* kernel) { + SetReloadTestKernel(kernel); + + Dart_Handle result = TriggerReload(); + if (Dart_IsError(result)) { + return result; + } + + result = GetReloadErrorOrRootLibrary(); + + { + Thread* thread = Thread::Current(); + TransitionNativeToVM transition(thread); + Isolate* isolate = thread->isolate(); + if (isolate->reload_context() != NULL) { + isolate->DeleteReloadContext(); + } + } + + return result; +} + #endif // !PRODUCT Dart_Handle TestCase::LoadCoreTestScript(const char* script, diff --git a/runtime/vm/unit_test.h b/runtime/vm/unit_test.h index 16c6303af50c..1e2889b837af 100644 --- a/runtime/vm/unit_test.h +++ b/runtime/vm/unit_test.h @@ -289,11 +289,23 @@ class TestCase : TestCaseBase { static char* CompileTestScriptWithDFE(const char* url, const char* source, - void** kernel_pgm); + void** kernel_pgm, + bool incrementally = false); + static char* CompileTestScriptWithDFE(const char* url, + int sourcefiles_count, + Dart_SourceFile sourcefiles[], + void** kernel_pgm, + bool incrementally = false); static Dart_Handle LoadTestScript(const char* script, Dart_NativeEntryResolver resolver, const char* lib_uri = USER_TEST_URI, bool finalize = true); + static Dart_Handle LoadTestScriptWithDFE( + int sourcefiles_count, + Dart_SourceFile sourcefiles[], + Dart_NativeEntryResolver resolver = NULL, + bool finalize = true, + bool incrementally = false); static Dart_Handle LoadCoreTestScript(const char* script, Dart_NativeEntryResolver resolver); static Dart_Handle lib(); @@ -314,6 +326,8 @@ class TestCase : TestCaseBase { // Sets |script| to be the source used at next reload. static void SetReloadTestScript(const char* script); + static void SetReloadTestKernel(const void* kernel); + // Initiates the reload. static Dart_Handle TriggerReload(); // Gets the result of a reload. @@ -321,6 +335,7 @@ class TestCase : TestCaseBase { // Helper function which reloads the current isolate using |script|. static Dart_Handle ReloadTestScript(const char* script); + static Dart_Handle ReloadTestKernel(const void* kernel); static void AddTestLib(const char* url, const char* source); static const char* GetTestLib(const char* url); diff --git a/utils/kernel-service/kernel-service.dart b/utils/kernel-service/kernel-service.dart index 92cb34eb2da6..cd5ae0ca70a7 100644 --- a/utils/kernel-service/kernel-service.dart +++ b/utils/kernel-service/kernel-service.dart @@ -21,12 +21,14 @@ library runtime.tools.kernel_service; import 'dart:async' show Future; +import 'dart:convert' show JSON; import 'dart:io' show Platform hide FileSystemEntity; import 'dart:isolate'; import 'dart:typed_data' show Uint8List; import 'package:front_end/file_system.dart'; import 'package:front_end/front_end.dart'; +import 'package:front_end/incremental_kernel_generator.dart'; import 'package:front_end/memory_file_system.dart'; import 'package:front_end/physical_file_system.dart'; import 'package:front_end/src/fasta/kernel/utils.dart'; @@ -38,6 +40,119 @@ import 'package:kernel/target/vm_fasta.dart' show VmFastaTarget; const bool verbose = const bool.fromEnvironment('DFE_VERBOSE'); const bool strongMode = const bool.fromEnvironment('DFE_STRONG_MODE'); +abstract class Compiler { + final FileSystem fileSystem; + final List errors = new List(); + + CompilerOptions options; + + Compiler(this.fileSystem) { + Uri packagesUri = (Platform.packageConfig != null) + ? Uri.parse(Platform.packageConfig) + : null; + + Uri sdkSummary = Uri.base + .resolveUri(new Uri.file(Platform.resolvedExecutable)) + .resolveUri(new Uri.directory("patched_sdk")) + // TODO(sigmund): use outline.dill when the mixin transformer is + // modular. + .resolve('platform.dill'); + + if (verbose) { + print("DFE: Platform.packageConfig: ${Platform.packageConfig}"); + print("DFE: packagesUri: ${packagesUri}"); + print("DFE: Platform.resolvedExecutable: ${Platform.resolvedExecutable}"); + print("DFE: sdkSummary: ${sdkSummary}"); + } + + options = new CompilerOptions() + ..strongMode = strongMode + ..fileSystem = fileSystem + ..target = new VmFastaTarget(new TargetFlags(strongMode: strongMode)) + ..packagesFileUri = packagesUri + ..sdkSummary = sdkSummary + ..verbose = verbose + ..throwOnErrors = false + ..reportMessages = true + ..onError = (CompilationMessage e) { + if (e.severity == Severity.error) { + // TODO(sigmund): support emitting code with errors as long as they + // are handled in the generated code (issue #30194). + errors.add(e.message); + } + }; + } + + Future compile(Uri script); +} + +class IncrementalCompiler extends Compiler { + IncrementalKernelGenerator generator; + + IncrementalCompiler(FileSystem fileSystem) : super(fileSystem); + + @override + Future compile(Uri script) async { + if (generator == null) { + generator = await IncrementalKernelGenerator.newInstance(options, script); + } + DeltaProgram deltaProgram = await generator.computeDelta(); + // TODO(aam): Accepting/rejecting should be done based on VM response. + generator.acceptLastDelta(); + return deltaProgram.newProgram; + } + + void invalidate(Uri uri) { + generator.invalidate(uri); + } +} + +class SingleShotCompiler extends Compiler { + final bool requireMain; + + SingleShotCompiler(FileSystem fileSystem, this.requireMain) + : super(fileSystem); + + @override + Future compile(Uri script) async { + return requireMain + ? kernelForProgram(script, options) + : kernelForBuildUnit([script], options..chaseDependencies = true); + } +} + +final Map isolateCompilers = new Map(); + +Future lookupOrBuildNewIncrementalCompiler( + int isolateId, List sourceFiles) async { + IncrementalCompiler compiler; + if (isolateCompilers.containsKey(isolateId)) { + compiler = isolateCompilers[isolateId]; + final HybridFileSystem fileSystem = compiler.fileSystem; + if (sourceFiles != null) { + for (int i = 0; i < sourceFiles.length ~/ 2; i++) { + Uri uri = Uri.parse(sourceFiles[i * 2]); + fileSystem.memory + .entityForUri(uri) + .writeAsBytesSync(sourceFiles[i * 2 + 1]); + compiler.invalidate(uri); + } + } + } else { + final FileSystem fileSystem = sourceFiles == null + ? PhysicalFileSystem.instance + : _buildFileSystem(sourceFiles); + + // TODO(aam): IncrementalCompiler instance created below have to be + // destroyed when corresponding isolate is shut down. To achieve that kernel + // isolate needs to receive a message indicating that particular + // isolate was shut down. Message should be handled here in this script. + compiler = new IncrementalCompiler(fileSystem); + isolateCompilers[isolateId] = compiler; + } + return compiler; +} + // Process a request from the runtime. See KernelIsolate::CompileToKernel in // kernel_isolate.cc and Loader::SendKernelRequest in loader.cc. Future _processLoadRequest(request) async { @@ -47,66 +162,44 @@ Future _processLoadRequest(request) async { final SendPort port = request[1]; final String inputFileUri = request[2]; final Uri script = Uri.base.resolve(inputFileUri); - - FileSystem fileSystem = PhysicalFileSystem.instance; - bool requireMain = true; - - if (request.length > 3) { - fileSystem = _buildFileSystem(request[3]); - requireMain = false; - } - - Uri packagesUri = (Platform.packageConfig != null) - ? Uri.parse(Platform.packageConfig) - : null; - - Uri sdkSummary = Uri.base - .resolveUri(new Uri.file(Platform.resolvedExecutable)) - .resolveUri(new Uri.directory("patched_sdk")) - // TODO(sigmund): use outline.dill when the mixin transformer is modular. - .resolve('platform.dill'); - - if (verbose) { - print("DFE: scriptUri: ${script}"); - print("DFE: Platform.packageConfig: ${Platform.packageConfig}"); - print("DFE: packagesUri: ${packagesUri}"); - print("DFE: Platform.resolvedExecutable: ${Platform.resolvedExecutable}"); - print("DFE: sdkSummary: ${sdkSummary}"); + final bool incremental = request[3]; + + final List sourceFiles = request.length > 5 ? request[5] : null; + + Compiler compiler; + // TODO(aam): There should be no need to have an option to choose + // one compiler or another. We should always use an incremental + // compiler as its functionality is a super set of the other one. We need to + // watch the performance though. + if (incremental) { + final int isolateId = request[4]; + compiler = + await lookupOrBuildNewIncrementalCompiler(isolateId, sourceFiles); + } else { + final FileSystem fileSystem = sourceFiles == null + ? PhysicalFileSystem.instance + : _buildFileSystem(sourceFiles); + compiler = new SingleShotCompiler( + fileSystem, sourceFiles == null /* requireMain */); } - var errors = []; - var options = new CompilerOptions() - ..strongMode = strongMode - ..fileSystem = fileSystem - ..target = new VmFastaTarget(new TargetFlags(strongMode: strongMode)) - ..packagesFileUri = packagesUri - ..sdkSummary = sdkSummary - ..verbose = verbose - ..throwOnErrors = false - ..reportMessages = true - ..onError = (CompilationMessage e) { - if (e.severity == Severity.error) { - // TODO(sigmund): support emitting code with errors as long as they are - // handled in the generated code (issue #30194). - errors.add(e.message); - } - }; - CompilationResult result; try { - Program program = requireMain - ? await kernelForProgram(script, options) - : await kernelForBuildUnit([script], options..chaseDependencies = true); + if (verbose) { + print("DFE: scriptUri: ${script}"); + } + + Program program = await compiler.compile(script); - if (errors.isNotEmpty) { + 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(errors); + result = new CompilationResult.errors(compiler.errors); } else { // We serialize the program excluding platform.dill because the VM has // these sources built-in. Everything loaded as a summary in // [kernelForProgram] is marked `external`, so we can use that bit to - // decide what to excluce. + // decide what to exclude. // TODO(sigmund): remove the following line (Issue #30111) program.libraries.forEach((e) => e.isExternal = false); result = new CompilationResult.ok( @@ -166,7 +259,13 @@ train(String scriptUri) { throw "Unexpected response: $response"; } }; - var request = [tag, responsePort.sendPort, scriptUri]; + var request = [ + tag, + responsePort.sendPort, + scriptUri, + 1 /* isolateId chosen randomly */, + false /* incremental */ + ]; _processLoadRequest(request); }