// Copyright (c) 2016, 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. #include "vm/kernel_isolate.h" #include "include/dart_native_api.h" #include "vm/compiler/jit/compiler.h" #include "vm/dart_api_impl.h" #include "vm/dart_entry.h" #include "vm/flags.h" #include "vm/isolate.h" #include "vm/lockers.h" #include "vm/message.h" #include "vm/message_handler.h" #include "vm/native_arguments.h" #include "vm/native_entry.h" #include "vm/native_message_handler.h" #include "vm/object.h" #include "vm/object_store.h" #include "vm/port.h" #include "vm/service.h" #include "vm/symbols.h" #include "vm/thread_pool.h" #include "vm/timeline.h" #if !defined(DART_PRECOMPILED_RUNTIME) namespace dart { #define Z (T->zone()) DEFINE_FLAG(bool, trace_kernel, false, "Trace Kernel service requests."); DEFINE_FLAG(charp, kernel_multiroot_filepaths, NULL, "Comma-separated list of file paths that should be treated as roots" " by frontend compiler."); DEFINE_FLAG(charp, kernel_multiroot_scheme, NULL, "URI scheme that replaces filepaths prefixes specified" " by kernel_multiroot_filepaths option"); // Tags used to indicate different requests to the dart frontend. // // Current tags include the following: // 0 - Perform normal compilation. // 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 expressions in context (used by expression evaluation). // 5 - Generate dependencies used to create a dependencies file. // 6 - Triggers shutdown of the kernel isolate. // 7 - Detects the nullability of a script based on it's opt-in status. const int KernelIsolate::kCompileTag = 0; const int KernelIsolate::kUpdateSourcesTag = 1; const int KernelIsolate::kAcceptTag = 2; const int KernelIsolate::kTrainTag = 3; const int KernelIsolate::kCompileExpressionTag = 4; const int KernelIsolate::kListDependenciesTag = 5; const int KernelIsolate::kNotifyIsolateShutdown = 6; const int KernelIsolate::kDetectNullabilityTag = 7; const char* KernelIsolate::kName = DART_KERNEL_ISOLATE_NAME; Dart_IsolateGroupCreateCallback KernelIsolate::create_group_callback_ = NULL; Monitor* KernelIsolate::monitor_ = new Monitor(); KernelIsolate::State KernelIsolate::state_ = KernelIsolate::kNotStarted; Isolate* KernelIsolate::isolate_ = NULL; Dart_Port KernelIsolate::kernel_port_ = ILLEGAL_PORT; class RunKernelTask : public ThreadPool::Task { public: virtual void Run() { ASSERT(Isolate::Current() == NULL); #ifdef SUPPORT_TIMELINE TimelineBeginEndScope tbes(Timeline::GetVMStream(), "KernelIsolateStartup"); #endif // SUPPORT_TIMELINE char* error = NULL; Isolate* isolate = NULL; Dart_IsolateGroupCreateCallback create_group_callback = KernelIsolate::create_group_callback(); ASSERT(create_group_callback != NULL); // Note: these flags must match those passed to the VM during // the app-jit training run (see //utils/kernel-service/BUILD.gn). Dart_IsolateFlags api_flags; Isolate::FlagsInitialize(&api_flags); api_flags.enable_asserts = false; api_flags.null_safety = true; api_flags.is_system_isolate = true; #if !defined(DART_PRECOMPILER) api_flags.use_field_guards = true; #endif #if !defined(DART_PRECOMPILER) api_flags.use_osr = true; #endif isolate = reinterpret_cast( create_group_callback(KernelIsolate::kName, KernelIsolate::kName, NULL, NULL, &api_flags, NULL, &error)); if (isolate == NULL) { if (FLAG_trace_kernel) { OS::PrintErr(DART_KERNEL_ISOLATE_NAME ": Isolate creation error: %s\n", error); } free(error); error = nullptr; KernelIsolate::SetKernelIsolate(NULL); KernelIsolate::InitializingFailed(); return; } bool got_unwind; { ASSERT(Isolate::Current() == NULL); StartIsolateScope start_scope(isolate); got_unwind = RunMain(isolate); } KernelIsolate::FinishedInitializing(); if (got_unwind) { ShutdownIsolate(reinterpret_cast(isolate)); return; } // isolate_ was set as side effect of create callback. ASSERT(KernelIsolate::IsKernelIsolate(isolate)); isolate->message_handler()->Run(isolate->group()->thread_pool(), NULL, ShutdownIsolate, reinterpret_cast(isolate)); } protected: static void ShutdownIsolate(uword parameter) { if (FLAG_trace_kernel) { OS::PrintErr(DART_KERNEL_ISOLATE_NAME ": ShutdownIsolate\n"); } KernelIsolate::SetLoadPort(ILLEGAL_PORT); Dart_EnterIsolate(reinterpret_cast(parameter)); { auto T = Thread::Current(); TransitionNativeToVM transition(T); StackZone zone(T); HandleScope handle_scope(T); auto I = T->isolate(); ASSERT(KernelIsolate::IsKernelIsolate(I)); // Print the error if there is one. This may execute dart code to // print the exception object, so we need to use a StartIsolateScope. Error& error = Error::Handle(Z); error = T->sticky_error(); if (!error.IsNull() && !error.IsUnwindError()) { OS::PrintErr(DART_KERNEL_ISOLATE_NAME ": Error: %s\n", error.ToErrorCString()); } error = I->sticky_error(); if (!error.IsNull() && !error.IsUnwindError()) { OS::PrintErr(DART_KERNEL_ISOLATE_NAME ": Error: %s\n", error.ToErrorCString()); } } Dart_ShutdownIsolate(); if (FLAG_trace_kernel) { OS::PrintErr(DART_KERNEL_ISOLATE_NAME ": Shutdown.\n"); } KernelIsolate::FinishedExiting(); } bool RunMain(Isolate* I) { Thread* T = Thread::Current(); ASSERT(I == T->isolate()); StackZone zone(T); // Invoke main which will return the port to which load requests are sent. const Library& root_library = Library::Handle(Z, I->group()->object_store()->root_library()); if (root_library.IsNull()) { OS::PrintErr(DART_KERNEL_ISOLATE_NAME ": Embedder did not install a script."); // Kernel isolate is not supported by embedder. return false; } ASSERT(!root_library.IsNull()); const String& entry_name = String::Handle(Z, String::New("main")); ASSERT(!entry_name.IsNull()); const Function& entry = Function::Handle( Z, root_library.LookupFunctionAllowPrivate(entry_name)); if (entry.IsNull()) { // Kernel isolate is not supported by embedder. OS::PrintErr(DART_KERNEL_ISOLATE_NAME ": Embedder did not provide a main function."); return false; } ASSERT(!entry.IsNull()); const Object& result = Object::Handle( Z, DartEntry::InvokeFunction(entry, Object::empty_array())); ASSERT(!result.IsNull()); if (result.IsError()) { // Kernel isolate did not initialize properly. if (FLAG_trace_kernel) { const Error& error = Error::Cast(result); OS::PrintErr(DART_KERNEL_ISOLATE_NAME ": Calling main resulted in an error: %s", error.ToErrorCString()); } if (result.IsUnwindError()) { return true; } return false; } ASSERT(result.IsReceivePort()); const ReceivePort& rp = ReceivePort::Cast(result); KernelIsolate::SetLoadPort(rp.Id()); return false; } }; void KernelIsolate::InitializeState() { // Grab the isolate create callback here to avoid race conditions with tests // that change this after Dart_Initialize returns. if (FLAG_trace_kernel) { OS::PrintErr(DART_KERNEL_ISOLATE_NAME ": InitializeState\n"); } create_group_callback_ = Isolate::CreateGroupCallback(); if (create_group_callback_ == NULL) { KernelIsolate::InitializingFailed(); return; } } bool KernelIsolate::Start() { if (create_group_callback_ == nullptr) { if (FLAG_trace_kernel) { OS::PrintErr(DART_KERNEL_ISOLATE_NAME ": Attempted to start kernel isolate without setting " "Dart_InitializeParams property 'start_kernel_isolate' " "to true\n"); } return false; } bool start_task = false; { MonitorLocker ml(monitor_); if (state_ == kNotStarted) { if (FLAG_trace_kernel) { OS::PrintErr(DART_KERNEL_ISOLATE_NAME ": Start\n"); } start_task = true; state_ = kStarting; ml.NotifyAll(); } } bool task_started = true; if (start_task) { task_started = Dart::thread_pool()->Run(); } return task_started; } void KernelIsolate::Shutdown() { MonitorLocker ml(monitor_); while (state_ == kStarting) { ml.Wait(); } if (state_ == kStopped || state_ == kNotStarted) { return; } ASSERT(state_ == kStarted); state_ = kStopping; ml.NotifyAll(); Isolate::KillIfExists(isolate_, Isolate::kInternalKillMsg); while (state_ != kStopped) { ml.Wait(); } } void KernelIsolate::InitCallback(Isolate* I) { Thread* T = Thread::Current(); ASSERT(I == T->isolate()); ASSERT(I != NULL); if (!NameEquals(I->name())) { // Not kernel isolate. return; } ASSERT(!Exists()); if (FLAG_trace_kernel) { OS::PrintErr(DART_KERNEL_ISOLATE_NAME ": InitCallback for %s.\n", I->name()); } SetKernelIsolate(I); } bool KernelIsolate::IsKernelIsolate(const Isolate* isolate) { MonitorLocker ml(monitor_); return isolate == isolate_; } bool KernelIsolate::IsRunning() { MonitorLocker ml(monitor_); return (kernel_port_ != ILLEGAL_PORT) && (isolate_ != NULL); } bool KernelIsolate::NameEquals(const char* name) { ASSERT(name != NULL); return (strcmp(name, DART_KERNEL_ISOLATE_NAME) == 0); } bool KernelIsolate::Exists() { MonitorLocker ml(monitor_); return isolate_ != NULL; } void KernelIsolate::SetKernelIsolate(Isolate* isolate) { MonitorLocker ml(monitor_); if (isolate != nullptr) { isolate->set_is_kernel_isolate(true); } isolate_ = isolate; ml.NotifyAll(); } void KernelIsolate::SetLoadPort(Dart_Port port) { MonitorLocker ml(monitor_); kernel_port_ = port; ml.NotifyAll(); } void KernelIsolate::FinishedExiting() { MonitorLocker ml(monitor_); ASSERT(state_ == kStarted || state_ == kStopping); state_ = kStopped; ml.NotifyAll(); } void KernelIsolate::FinishedInitializing() { MonitorLocker ml(monitor_); ASSERT(state_ == kStarting); state_ = kStarted; ml.NotifyAll(); } void KernelIsolate::InitializingFailed() { MonitorLocker ml(monitor_); ASSERT(state_ == kStarting); state_ = kStopped; ml.NotifyAll(); } Dart_Port KernelIsolate::WaitForKernelPort() { VMTagScope tagScope(Thread::Current(), VMTag::kLoadWaitTagId); MonitorLocker ml(monitor_); while (state_ == kStarting && (kernel_port_ == ILLEGAL_PORT)) { ml.Wait(); } 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(); if (source_files[i].source != NULL) { 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)); } else { source_code->type = Dart_CObject_kNull; } fileNamePairs[(i * 2) + 1] = source_code; } files.value.as_array.values = fileNamePairs; return files; } static void ReleaseFilesPairs(const Dart_CObject& files) { for (intptr_t i = 0; i < files.value.as_array.length; i++) { delete files.value.as_array.values[i]; } delete[] files.value.as_array.values; } static void PassThroughFinalizer(void* isolate_callback_data, void* peer) {} MallocGrowableArray* KernelIsolate::experimental_flags_ = new MallocGrowableArray(); void KernelIsolate::AddExperimentalFlag(const char* value) { char* save_ptr; // Needed for strtok_r. char* temp = Utils::StrDup(value); char* token = strtok_r(temp, ",", &save_ptr); while (token != NULL) { experimental_flags_->Add(Utils::StrDup(token)); token = strtok_r(NULL, ",", &save_ptr); } free(temp); } bool KernelIsolate::GetExperimentalFlag(ExperimentalFeature feature) { const char* value = GetExperimentalFeatureName(feature); for (const char* str : *experimental_flags_) { if (strcmp(str, value) == 0) { return true; } else if (strstr(str, "no-") == str && strcmp(str + 3, value) == 0) { return false; } } return GetExperimentalFeatureDefault(feature); } DEFINE_OPTION_HANDLER(KernelIsolate::AddExperimentalFlag, enable_experiment, "Comma separated list of experimental features."); class KernelCompilationRequest : public ValueObject { public: KernelCompilationRequest() : monitor_(), port_(Dart_NewNativePort("kernel-compilation-port", &HandleResponse, false)), next_(NULL), prev_(NULL) { RegisterRequest(this); result_.status = Dart_KernelCompilationStatus_Unknown; result_.error = NULL; result_.kernel = NULL; result_.kernel_size = 0; } ~KernelCompilationRequest() { UnregisterRequest(this); if (port_ != ILLEGAL_PORT) { Dart_CloseNativePort(port_); } } intptr_t setDillData(Dart_CObject** dills_array, intptr_t dill_num, const uint8_t* buffer, intptr_t buffer_size) { if (buffer != nullptr) { dills_array[dill_num] = new Dart_CObject; dills_array[dill_num]->type = Dart_CObject_kExternalTypedData; dills_array[dill_num]->value.as_external_typed_data.type = Dart_TypedData_kUint8; dills_array[dill_num]->value.as_external_typed_data.length = buffer_size; dills_array[dill_num]->value.as_external_typed_data.data = const_cast(buffer); dills_array[dill_num]->value.as_external_typed_data.peer = const_cast(buffer); dills_array[dill_num]->value.as_external_typed_data.callback = PassThroughFinalizer; dill_num++; } return dill_num; } Dart_KernelCompilationResult SendAndWaitForResponse( Dart_Port kernel_port, const uint8_t* platform_kernel, intptr_t platform_kernel_size, const char* expression, const Array& definitions, const Array& type_definitions, char const* library_uri, char const* klass, char const* method, bool is_static, const MallocGrowableArray* experimental_flags) { if (port_ == ILLEGAL_PORT) { Dart_KernelCompilationResult result = {}; result.status = Dart_KernelCompilationStatus_MsgFailed; result.error = Utils::StrDup("Error Kernel Isolate : unable to create reply port"); return result; } Thread* thread = Thread::Current(); TransitionNativeToVM transition(thread); 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 dart_platform_kernel; if (platform_kernel != NULL) { dart_platform_kernel.type = Dart_CObject_kExternalTypedData; dart_platform_kernel.value.as_external_typed_data.type = Dart_TypedData_kUint8; dart_platform_kernel.value.as_external_typed_data.length = platform_kernel_size; dart_platform_kernel.value.as_external_typed_data.data = const_cast(platform_kernel); dart_platform_kernel.value.as_external_typed_data.peer = const_cast(platform_kernel); dart_platform_kernel.value.as_external_typed_data.callback = PassThroughFinalizer; } else { // If NULL, the kernel service looks up the platform dill file // next to the executable. dart_platform_kernel.type = Dart_CObject_kNull; } Dart_CObject expression_object; expression_object.type = Dart_CObject_kString; expression_object.value.as_string = const_cast(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( String::CheckedHandle(thread->zone(), 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( String::CheckedHandle(thread->zone(), 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(library_uri); Dart_CObject class_object; if (klass != NULL) { class_object.type = Dart_CObject_kString; class_object.value.as_string = const_cast(klass); } else { class_object.type = Dart_CObject_kNull; } Dart_CObject method_object; if (method != NULL) { method_object.type = Dart_CObject_kString; method_object.value.as_string = const_cast(method); } else { method_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; auto isolate_group = thread->isolate_group(); auto source = isolate_group->source(); Dart_CObject isolate_id; isolate_id.type = Dart_CObject_kInt64; isolate_id.value.as_int64 = static_cast(isolate_group->id()); intptr_t num_dills = 0; if (source->kernel_buffer != nullptr) { num_dills++; } if (source->script_kernel_buffer != nullptr) { num_dills++; } Array& loaded_blobs = Array::Handle(); if (source->loaded_blobs_ != nullptr) { loaded_blobs = source->loaded_blobs_; WeakProperty& weak_property = WeakProperty::Handle(); for (intptr_t i = 0; i < loaded_blobs.Length(); i++) { weak_property ^= loaded_blobs.At(i); if (weak_property.key() != ExternalTypedData::null()) { num_dills++; } } } Dart_CObject dills_object; dills_object.type = Dart_CObject_kArray; dills_object.value.as_array.length = num_dills; Dart_CObject** dills_array = new Dart_CObject*[num_dills]; intptr_t dill_num = 0; dill_num = setDillData(dills_array, dill_num, source->kernel_buffer, source->kernel_buffer_size); dill_num = setDillData(dills_array, dill_num, source->script_kernel_buffer, source->script_kernel_size); if (!loaded_blobs.IsNull()) { WeakProperty& weak_property = WeakProperty::Handle(); for (intptr_t i = 0; i < loaded_blobs.Length(); i++) { weak_property ^= loaded_blobs.At(i); if (weak_property.key() != ExternalTypedData::null()) { ExternalTypedData& externalTypedData = ExternalTypedData::Handle( thread->zone(), ExternalTypedData::RawCast(weak_property.key())); NoSafepointScope no_safepoint(thread); const uint8_t* data = const_cast( reinterpret_cast(externalTypedData.DataAddr(0))); dill_num = setDillData(dills_array, dill_num, data, externalTypedData.Length()); } } } dills_object.value.as_array.values = dills_array; Dart_CObject num_blob_loads; num_blob_loads.type = Dart_CObject_kInt64; num_blob_loads.value.as_int64 = source->num_blob_loads_; Dart_CObject enable_asserts; enable_asserts.type = Dart_CObject_kBool; enable_asserts.value.as_bool = isolate_group->asserts(); intptr_t num_experimental_flags = experimental_flags->length(); Dart_CObject** experimental_flags_array = new Dart_CObject*[num_experimental_flags]; for (intptr_t i = 0; i < num_experimental_flags; ++i) { experimental_flags_array[i] = new Dart_CObject; experimental_flags_array[i]->type = Dart_CObject_kString; experimental_flags_array[i]->value.as_string = (*experimental_flags)[i]; } Dart_CObject experimental_flags_object; experimental_flags_object.type = Dart_CObject_kArray; experimental_flags_object.value.as_array.values = experimental_flags_array; experimental_flags_object.value.as_array.length = num_experimental_flags; Dart_CObject message; message.type = Dart_CObject_kArray; Dart_CObject* message_arr[] = {&tag, &send_port, &isolate_id, &dart_platform_kernel, &expression_object, &definitions_object, &type_definitions_object, &library_uri_object, &class_object, &method_object, &is_static_object, &dills_object, &num_blob_loads, &enable_asserts, &experimental_flags_object}; message.value.as_array.values = message_arr; message.value.as_array.length = ARRAY_SIZE(message_arr); { TransitionVMToNative transition(thread); // Send the message. Dart_PostCObject(kernel_port, &message); // Wait for reply to arrive. VMTagScope tagScope(thread, VMTag::kLoadWaitTagId); 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; for (intptr_t i = 0; i < num_dills; ++i) { delete dills_array[i]; } delete[] dills_array; for (intptr_t i = 0; i < num_experimental_flags; ++i) { delete experimental_flags_array[i]; } delete[] experimental_flags_array; return result_; } Dart_KernelCompilationResult SendAndWaitForResponse( int request_tag, Dart_Port kernel_port, const char* script_uri, const uint8_t* platform_kernel, intptr_t platform_kernel_size, int source_files_count, Dart_SourceFile source_files[], bool incremental_compile, bool snapshot_compile, const char* package_config, const char* multiroot_filepaths, const char* multiroot_scheme, const MallocGrowableArray* experimental_flags, const char* original_working_directory, Dart_KernelCompilationVerbosityLevel verbosity) { // Build the message for the Kernel isolate. // tag is used to specify which operation the frontend should perform. if (port_ == ILLEGAL_PORT) { Dart_KernelCompilationResult result = {}; result.status = Dart_KernelCompilationStatus_MsgFailed; result.error = Utils::StrDup("Error Kernel Isolate : unable to create reply port"); return result; } Dart_CObject tag; tag.type = Dart_CObject_kInt32; tag.value.as_int32 = request_tag; 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 uri; if (script_uri != NULL) { uri.type = Dart_CObject_kString; uri.value.as_string = const_cast(script_uri); } else { uri.type = Dart_CObject_kNull; } Dart_CObject dart_platform_kernel; if (platform_kernel != NULL) { dart_platform_kernel.type = Dart_CObject_kExternalTypedData; dart_platform_kernel.value.as_external_typed_data.type = Dart_TypedData_kUint8; dart_platform_kernel.value.as_external_typed_data.length = platform_kernel_size; dart_platform_kernel.value.as_external_typed_data.data = const_cast(platform_kernel); dart_platform_kernel.value.as_external_typed_data.peer = const_cast(platform_kernel); dart_platform_kernel.value.as_external_typed_data.callback = PassThroughFinalizer; } else { // If NULL, the kernel service looks up the platform dill file // next to the executable. dart_platform_kernel.type = Dart_CObject_kNull; } Dart_CObject dart_incremental; dart_incremental.type = Dart_CObject_kBool; dart_incremental.value.as_bool = incremental_compile; Dart_CObject dart_snapshot; dart_snapshot.type = Dart_CObject_kBool; dart_snapshot.value.as_bool = snapshot_compile; // TODO(aam): Assert that isolate exists once we move CompileAndReadScript // compilation logic out of CreateIsolateAndSetupHelper and into // IsolateSetupHelper in main.cc. auto thread = Thread::Current(); auto isolate_group = thread != nullptr ? thread->isolate_group() : nullptr; if (incremental_compile) { ASSERT(isolate_group != nullptr); } Dart_CObject isolate_id; isolate_id.type = Dart_CObject_kInt64; isolate_id.value.as_int64 = isolate_group != nullptr ? static_cast(isolate_group->id()) : 0; Dart_CObject message; message.type = Dart_CObject_kArray; Dart_CObject files = BuildFilesPairs(source_files_count, source_files); Dart_CObject enable_asserts; enable_asserts.type = Dart_CObject_kBool; enable_asserts.value.as_bool = isolate_group != nullptr ? isolate_group->asserts() : FLAG_enable_asserts; Dart_CObject null_safety; null_safety.type = Dart_CObject_kInt32; null_safety.value.as_int32 = (isolate_group != nullptr) ? (isolate_group->null_safety() ? kNullSafetyOptionStrong : kNullSafetyOptionWeak) : FLAG_sound_null_safety; intptr_t num_experimental_flags = experimental_flags->length(); Dart_CObject** experimental_flags_array = new Dart_CObject*[num_experimental_flags]; for (intptr_t i = 0; i < num_experimental_flags; ++i) { experimental_flags_array[i] = new Dart_CObject; experimental_flags_array[i]->type = Dart_CObject_kString; experimental_flags_array[i]->value.as_string = (*experimental_flags)[i]; } Dart_CObject experimental_flags_object; experimental_flags_object.type = Dart_CObject_kArray; experimental_flags_object.value.as_array.values = experimental_flags_array; experimental_flags_object.value.as_array.length = num_experimental_flags; Dart_CObject package_config_uri; if (package_config != NULL) { package_config_uri.type = Dart_CObject_kString; package_config_uri.value.as_string = const_cast(package_config); } else { package_config_uri.type = Dart_CObject_kNull; } Dart_CObject multiroot_filepaths_object; { const char* filepaths = multiroot_filepaths != NULL ? multiroot_filepaths : FLAG_kernel_multiroot_filepaths; if (filepaths != NULL) { multiroot_filepaths_object.type = Dart_CObject_kString; multiroot_filepaths_object.value.as_string = const_cast(filepaths); } else { multiroot_filepaths_object.type = Dart_CObject_kNull; } } Dart_CObject multiroot_scheme_object; { const char* scheme = multiroot_scheme != NULL ? multiroot_scheme : FLAG_kernel_multiroot_scheme; if (scheme != NULL) { multiroot_scheme_object.type = Dart_CObject_kString; multiroot_scheme_object.value.as_string = const_cast(scheme); } else { multiroot_scheme_object.type = Dart_CObject_kNull; } } Dart_CObject original_working_directory_object; { if (original_working_directory != NULL) { original_working_directory_object.type = Dart_CObject_kString; original_working_directory_object.value.as_string = const_cast(original_working_directory); } else { original_working_directory_object.type = Dart_CObject_kNull; } } Dart_CObject verbosity_str; verbosity_str.type = Dart_CObject_kString; verbosity_str.value.as_string = const_cast(KernelCompilationVerbosityLevelToString(verbosity)); Dart_CObject* message_arr[] = {&tag, &send_port, &uri, &dart_platform_kernel, &dart_incremental, &dart_snapshot, &null_safety, &isolate_id, &files, &enable_asserts, &experimental_flags_object, &package_config_uri, &multiroot_filepaths_object, &multiroot_scheme_object, &original_working_directory_object, &verbosity_str}; message.value.as_array.values = message_arr; message.value.as_array.length = ARRAY_SIZE(message_arr); // Send the message. Dart_PostCObject(kernel_port, &message); ReleaseFilesPairs(files); // Wait for reply to arrive. VMTagScope tagScope(Thread::Current(), VMTag::kLoadWaitTagId); MonitorLocker ml(&monitor_); while (result_.status == Dart_KernelCompilationStatus_Unknown) { ml.Wait(); } for (intptr_t i = 0; i < num_experimental_flags; ++i) { delete experimental_flags_array[i]; } delete[] experimental_flags_array; return result_; } private: void LoadKernelFromResponse(Dart_CObject* response) { ASSERT((response->type == Dart_CObject_kTypedData) || (response->type == Dart_CObject_kBool) || (response->type == Dart_CObject_kNull)); if (response->type == Dart_CObject_kNull) { return; } if (response->type == Dart_CObject_kBool) { result_.null_safety = response->value.as_bool; return; } ASSERT(response->value.as_typed_data.type == Dart_TypedData_kUint8); result_.kernel_size = response->value.as_typed_data.length; result_.kernel = static_cast(malloc(result_.kernel_size)); memmove(result_.kernel, response->value.as_typed_data.values, result_.kernel_size); } // Possible responses from the Kernel isolate: // // [Ok, Uint8List KernelBinary] // [Error, String error, Uint8List KernelBinary] // [Crash, String error] // void HandleResponseImpl(Dart_CObject* message) { ASSERT(message->type == Dart_CObject_kArray); ASSERT(message->value.as_array.length >= 1); Dart_CObject** response = message->value.as_array.values; MonitorLocker ml(&monitor_); ASSERT(response[0]->type == Dart_CObject_kInt32); result_.status = static_cast( message->value.as_array.values[0]->value.as_int32); if (result_.status == Dart_KernelCompilationStatus_Ok) { LoadKernelFromResponse(response[1]); } else { if (result_.status == Dart_KernelCompilationStatus_Error) { LoadKernelFromResponse(response[2]); } // This is an error. ASSERT(response[1]->type == Dart_CObject_kString); result_.error = Utils::StrDup(response[1]->value.as_string); } ml.Notify(); } static void HandleResponse(Dart_Port port, Dart_CObject* message) { MonitorLocker locker(requests_monitor_); KernelCompilationRequest* rq = FindRequestLocked(port); if (rq == NULL) { return; } rq->HandleResponseImpl(message); } static void RegisterRequest(KernelCompilationRequest* rq) { MonitorLocker locker(requests_monitor_); rq->next_ = requests_; if (requests_ != NULL) { requests_->prev_ = rq; } requests_ = rq; } static void UnregisterRequest(KernelCompilationRequest* rq) { MonitorLocker locker(requests_monitor_); if (rq->next_ != NULL) { rq->next_->prev_ = rq->prev_; } if (rq->prev_ != NULL) { rq->prev_->next_ = rq->next_; } else { requests_ = rq->next_; } } // Note: Caller must hold requests_monitor_. static KernelCompilationRequest* FindRequestLocked(Dart_Port port) { for (KernelCompilationRequest* rq = requests_; rq != NULL; rq = rq->next_) { if (rq->port_ == port) { return rq; } } return NULL; } static const char* KernelCompilationVerbosityLevelToString( Dart_KernelCompilationVerbosityLevel verbosity) { switch (verbosity) { case Dart_KernelCompilationVerbosityLevel_Error: return "error"; case Dart_KernelCompilationVerbosityLevel_Warning: return "warning"; case Dart_KernelCompilationVerbosityLevel_Info: return "info"; case Dart_KernelCompilationVerbosityLevel_All: return "all"; default: UNREACHABLE(); } } // This monitor must be held whenever linked list of requests is accessed. static Monitor* requests_monitor_; // Linked list of all active requests. Used to find a request by port number. // Guarded by requests_monitor_ lock. static KernelCompilationRequest* requests_; Monitor monitor_; Dart_Port port_; // Linked list of active requests. Guarded by requests_monitor_ lock. KernelCompilationRequest* next_; KernelCompilationRequest* prev_; Dart_KernelCompilationResult result_ = {}; }; Monitor* KernelCompilationRequest::requests_monitor_ = new Monitor(); KernelCompilationRequest* KernelCompilationRequest::requests_ = NULL; Dart_KernelCompilationResult KernelIsolate::CompileToKernel( const char* script_uri, const uint8_t* platform_kernel, intptr_t platform_kernel_size, int source_file_count, Dart_SourceFile source_files[], bool incremental_compile, bool snapshot_compile, const char* package_config, const char* multiroot_filepaths, const char* multiroot_scheme, Dart_KernelCompilationVerbosityLevel verbosity) { // Start the kernel Isolate if it is not already running. if (!Start()) { Dart_KernelCompilationResult result = {}; result.status = Dart_KernelCompilationStatus_Unknown; result.error = Utils::StrDup("Error while starting Kernel isolate task"); return result; } // This must be the main script to be loaded. Wait for Kernel isolate // to finish initialization. Dart_Port kernel_port = WaitForKernelPort(); if (kernel_port == ILLEGAL_PORT) { Dart_KernelCompilationResult result = {}; result.status = Dart_KernelCompilationStatus_MsgFailed; result.error = Utils::StrDup("Error while initializing Kernel isolate"); return result; } KernelCompilationRequest request; return request.SendAndWaitForResponse( kCompileTag, kernel_port, script_uri, platform_kernel, platform_kernel_size, source_file_count, source_files, incremental_compile, snapshot_compile, package_config, multiroot_filepaths, multiroot_scheme, experimental_flags_, NULL, verbosity); } bool KernelIsolate::DetectNullSafety(const char* script_uri, const char* package_config, const char* original_working_directory) { // Start the kernel Isolate if it is not already running. if (!Start()) { return false; } // Wait for Kernel isolate to finish initialization. Dart_Port kernel_port = WaitForKernelPort(); if (kernel_port == ILLEGAL_PORT) { return false; } KernelCompilationRequest request; Dart_KernelCompilationResult result = request.SendAndWaitForResponse( kDetectNullabilityTag, kernel_port, script_uri, nullptr, -1, 0, nullptr, false, false, package_config, nullptr, nullptr, experimental_flags_, original_working_directory, Dart_KernelCompilationVerbosityLevel_Error); return result.null_safety; } Dart_KernelCompilationResult KernelIsolate::ListDependencies() { Dart_Port kernel_port = WaitForKernelPort(); if (kernel_port == ILLEGAL_PORT) { Dart_KernelCompilationResult result = {}; result.status = Dart_KernelCompilationStatus_MsgFailed; result.error = Utils::StrDup("Error while initializing Kernel isolate"); return result; } KernelCompilationRequest request; return request.SendAndWaitForResponse( kListDependenciesTag, kernel_port, NULL, NULL, 0, 0, NULL, false, false, NULL, NULL, NULL, experimental_flags_, NULL, Dart_KernelCompilationVerbosityLevel_Error); } Dart_KernelCompilationResult KernelIsolate::AcceptCompilation() { // This must be the main script to be loaded. Wait for Kernel isolate // to finish initialization. Dart_Port kernel_port = WaitForKernelPort(); if (kernel_port == ILLEGAL_PORT) { Dart_KernelCompilationResult result = {}; result.status = Dart_KernelCompilationStatus_MsgFailed; result.error = Utils::StrDup("Error while initializing Kernel isolate"); return result; } KernelCompilationRequest request; return request.SendAndWaitForResponse( kAcceptTag, kernel_port, NULL, NULL, 0, 0, NULL, true, false, NULL, NULL, NULL, experimental_flags_, NULL, Dart_KernelCompilationVerbosityLevel_Error); } Dart_KernelCompilationResult KernelIsolate::CompileExpressionToKernel( const uint8_t* platform_kernel, intptr_t platform_kernel_size, const char* expression, const Array& definitions, const Array& type_definitions, const char* library_url, const char* klass, const char* method, bool is_static) { Dart_Port kernel_port = WaitForKernelPort(); if (kernel_port == ILLEGAL_PORT) { Dart_KernelCompilationResult result = {}; result.status = Dart_KernelCompilationStatus_MsgFailed; result.error = Utils::StrDup("Error while initializing Kernel isolate"); return result; } TransitionVMToNative transition(Thread::Current()); KernelCompilationRequest request; ASSERT(is_static || (klass != nullptr)); return request.SendAndWaitForResponse( kernel_port, platform_kernel, platform_kernel_size, expression, definitions, type_definitions, library_url, klass, method, is_static, experimental_flags_); } Dart_KernelCompilationResult KernelIsolate::UpdateInMemorySources( int source_files_count, Dart_SourceFile source_files[]) { // This must be the main script to be loaded. Wait for Kernel isolate // to finish initialization. Dart_Port kernel_port = WaitForKernelPort(); if (kernel_port == ILLEGAL_PORT) { Dart_KernelCompilationResult result = {}; result.status = Dart_KernelCompilationStatus_MsgFailed; result.error = Utils::StrDup("Error while initializing Kernel isolate"); return result; } KernelCompilationRequest request; return request.SendAndWaitForResponse( kUpdateSourcesTag, kernel_port, NULL, NULL, 0, source_files_count, source_files, true, false, NULL, NULL, NULL, experimental_flags_, NULL, Dart_KernelCompilationVerbosityLevel_Error); } void KernelIsolate::NotifyAboutIsolateGroupShutdown( const IsolateGroup* isolate_group) { if (!KernelIsolate::IsRunning()) { return; } Dart_Port kernel_port = WaitForKernelPort(); if (kernel_port == ILLEGAL_PORT) { return; } Dart_CObject tag; tag.type = Dart_CObject_kInt32; tag.value.as_int32 = KernelIsolate::kNotifyIsolateShutdown; Dart_CObject isolate_id; isolate_id.type = Dart_CObject_kInt64; isolate_id.value.as_int64 = static_cast(isolate_group->id()); Dart_CObject message; message.type = Dart_CObject_kArray; Dart_CObject* message_arr[] = {&tag, &isolate_id}; message.value.as_array.values = message_arr; message.value.as_array.length = ARRAY_SIZE(message_arr); // Send the message. Dart_PostCObject(kernel_port, &message); } } // namespace dart #endif // !defined(DART_PRECOMPILED_RUNTIME)