1// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#include "bin/dartdev_isolate.h"
6
7#if !defined(DART_PRECOMPILED_RUNTIME)
8
9#include <functional>
10#include <memory>
11
12#include "bin/directory.h"
13#include "bin/error_exit.h"
14#include "bin/exe_utils.h"
15#include "bin/file.h"
16#include "bin/lockers.h"
17#include "bin/platform.h"
18#include "bin/process.h"
19#include "include/dart_embedder_api.h"
20#include "platform/utils.h"
21
22#define CHECK_RESULT(result) \
23 if (Dart_IsError(result)) { \
24 ProcessError(Dart_GetError(result), kErrorExitCode); \
25 if (send_port_id != ILLEGAL_PORT) { \
26 Dart_CloseNativePort(send_port_id); \
27 } \
28 Dart_ExitScope(); \
29 Dart_ExitIsolate(); \
30 return; \
31 }
32
33namespace dart {
34namespace bin {
35
36DartDevIsolate::DartDevRunner DartDevIsolate::runner_ =
37 DartDevIsolate::DartDevRunner();
38bool DartDevIsolate::should_run_dart_dev_ = false;
39Monitor* DartDevIsolate::DartDevRunner::monitor_ = new Monitor();
40DartDevIsolate::DartDev_Result DartDevIsolate::DartDevRunner::result_ =
41 DartDevIsolate::DartDev_Result_Unknown;
42char** DartDevIsolate::DartDevRunner::script_ = nullptr;
43std::unique_ptr<char*[], void (*)(char*[])>
44 DartDevIsolate::DartDevRunner::argv_ =
45 std::unique_ptr<char*[], void (*)(char**)>(nullptr, [](char**) {});
46intptr_t DartDevIsolate::DartDevRunner::argc_ = 0;
47
48bool DartDevIsolate::ShouldParseCommand(const char* script_uri) {
49 // If script_uri is not a file path or of a known URI scheme, we can assume
50 // that this is a DartDev command.
51 return (!File::ExistsUri(nullptr, script_uri) &&
52 (strncmp(script_uri, "http://", 7) != 0) &&
53 (strncmp(script_uri, "https://", 8) != 0) &&
54 (strncmp(script_uri, "file://", 7) != 0) &&
55 (strncmp(script_uri, "package:", 8) != 0) &&
56 (strncmp(script_uri, "google3://", 10) != 0));
57}
58
59Utils::CStringUniquePtr DartDevIsolate::TryResolveDartDevSnapshotPath() {
60 // |dir_prefix| includes the last path seperator.
61 auto dir_prefix = EXEUtils::GetDirectoryPrefixFromExeName();
62
63 // First assume we're in dart-sdk/bin.
64 char* snapshot_path =
65 Utils::SCreate("%s../lib/_internal/dartdev.dill", dir_prefix.get());
66 if (File::Exists(nullptr, snapshot_path)) {
67 return Utils::CreateCStringUniquePtr(snapshot_path);
68 }
69 free(snapshot_path);
70
71 // If we're not in dart-sdk/bin, we might be in one of the $SDK/out/*
72 // directories. Try to use a snapshot rom a previously built SDK.
73 snapshot_path = Utils::SCreate("%sdartdev.dill", dir_prefix.get());
74 if (File::Exists(nullptr, snapshot_path)) {
75 return Utils::CreateCStringUniquePtr(snapshot_path);
76 }
77 free(snapshot_path);
78
79 Syslog::PrintErr("Could not find DartDev snapshot.\n");
80 return Utils::CreateCStringUniquePtr(nullptr);
81}
82
83void DartDevIsolate::DartDevRunner::Run(
84 Dart_IsolateGroupCreateCallback create_isolate,
85 const char* packages_file,
86 char** script,
87 CommandLineOptions* dart_options) {
88 create_isolate_ = create_isolate;
89 dart_options_ = dart_options;
90 packages_file_ = packages_file;
91 script_ = script;
92
93 MonitorLocker locker(monitor_);
94 int result = Thread::Start("DartDev Runner", RunCallback,
95 reinterpret_cast<uword>(this));
96 if (result != 0) {
97 FATAL1("Failed to start DartDev thread: %d", result);
98 }
99 monitor_->WaitMicros(Monitor::kNoTimeout);
100
101 if (result_ == DartDevIsolate::DartDev_Result_Run) {
102 // Clear the DartDev dart_options and replace them with the processed
103 // options provided by DartDev.
104 dart_options_->Reset();
105 dart_options_->AddArguments(const_cast<const char**>(argv_.get()), argc_);
106 }
107}
108
109static Dart_CObject* GetArrayItem(Dart_CObject* message, intptr_t index) {
110 return message->value.as_array.values[index];
111}
112
113void DartDevIsolate::DartDevRunner::DartDevResultCallback(
114 Dart_Port dest_port_id,
115 Dart_CObject* message) {
116 ASSERT(message->type == Dart_CObject_kArray);
117 int32_t type = GetArrayItem(message, 0)->value.as_int32;
118 switch (type) {
119 case DartDevIsolate::DartDev_Result_Run: {
120 result_ = DartDevIsolate::DartDev_Result_Run;
121 ASSERT(GetArrayItem(message, 1)->type == Dart_CObject_kString);
122 if (*script_ != nullptr) {
123 free(*script_);
124 }
125 *script_ = Utils::StrDup(GetArrayItem(message, 1)->value.as_string);
126 ASSERT(GetArrayItem(message, 2)->type == Dart_CObject_kArray);
127 Dart_CObject* args = GetArrayItem(message, 2);
128 argc_ = args->value.as_array.length;
129 Dart_CObject** dart_args = args->value.as_array.values;
130
131 auto deleter = [](char** args) {
132 for (intptr_t i = 0; i < argc_; ++i) {
133 free(args[i]);
134 }
135 delete[] args;
136 };
137 argv_ =
138 std::unique_ptr<char*[], void (*)(char**)>(new char*[argc_], deleter);
139 for (intptr_t i = 0; i < argc_; ++i) {
140 argv_[i] = Utils::StrDup(dart_args[i]->value.as_string);
141 }
142 break;
143 }
144 case DartDevIsolate::DartDev_Result_Exit: {
145 ASSERT(GetArrayItem(message, 1)->type == Dart_CObject_kInt32);
146 int32_t dartdev_exit_code = GetArrayItem(message, 1)->value.as_int32;
147
148 // If we're given a non-zero exit code, DartDev is signaling for us to
149 // shutdown.
150 if (dartdev_exit_code != 0) {
151 Process::SetGlobalExitCode(dartdev_exit_code);
152 }
153
154 // If DartDev hasn't signaled for us to do anything else, we can assume
155 // there's nothing else for the VM to run and that we can exit.
156 if (result_ == DartDevIsolate::DartDev_Result_Unknown) {
157 result_ = DartDevIsolate::DartDev_Result_Exit;
158 }
159
160 // DartDev is done processing the command. Unblock the main thread and
161 // continue the launch procedure.
162 DartDevRunner::monitor_->Notify();
163 break;
164 }
165 default:
166 UNREACHABLE();
167 }
168}
169
170void DartDevIsolate::DartDevRunner::RunCallback(uword args) {
171 MonitorLocker locker_(DartDevRunner::monitor_);
172 DartDevRunner* runner = reinterpret_cast<DartDevRunner*>(args);
173
174 // TODO(bkonyi): bring up DartDev from kernel instead of a app-jit snapshot.
175 // See https://github.com/dart-lang/sdk/issues/42804
176 auto dartdev_path = DartDevIsolate::TryResolveDartDevSnapshotPath();
177 if (dartdev_path == nullptr) {
178 ProcessError("Failed to find DartDev snapshot.", kErrorExitCode);
179 return;
180 }
181
182 // Hardcode flags to match those used to generate the DartDev snapshot.
183 Dart_IsolateFlags flags;
184 Dart_IsolateFlagsInitialize(&flags);
185 flags.enable_asserts = false;
186 flags.null_safety = false;
187 flags.use_field_guards = true;
188 flags.use_osr = true;
189
190 char* error;
191 Dart_Isolate dartdev_isolate = runner->create_isolate_(
192 dartdev_path.get(), "dartdev", nullptr, runner->packages_file_, &flags,
193 NULL /* callback_data */, const_cast<char**>(&error));
194
195 if (dartdev_isolate == nullptr) {
196 ProcessError(error, kErrorExitCode);
197 return;
198 }
199
200 Dart_EnterIsolate(dartdev_isolate);
201 Dart_EnterScope();
202
203 // Retrieve the DartDev entrypoint.
204 Dart_Port send_port_id = ILLEGAL_PORT;
205 Dart_Handle root_lib = Dart_RootLibrary();
206 Dart_Handle main_closure =
207 Dart_GetField(root_lib, Dart_NewStringFromCString("main"));
208 CHECK_RESULT(main_closure);
209
210 if (!Dart_IsClosure(main_closure)) {
211 ProcessError("Unable to find 'main' in root library 'dartdev'",
212 kErrorExitCode);
213 Dart_ExitScope();
214 Dart_ExitIsolate();
215 return;
216 }
217
218 // Create a SendPort that DartDev can use to communicate its results over.
219 send_port_id = Dart_NewNativePort("dartdev", DartDevResultCallback, false);
220 ASSERT(send_port_id != ILLEGAL_PORT);
221 Dart_Handle send_port = Dart_NewSendPort(send_port_id);
222 CHECK_RESULT(send_port);
223
224 const intptr_t kNumIsolateArgs = 7;
225 Dart_Handle isolate_args[kNumIsolateArgs];
226 isolate_args[0] = Dart_Null(); // parentPort
227 isolate_args[1] = main_closure; // entryPoint
228 isolate_args[2] = runner->dart_options_->CreateRuntimeOptions(); // args
229 isolate_args[3] = send_port; // message
230 isolate_args[4] = Dart_True(); // isSpawnUri
231 isolate_args[5] = Dart_Null(); // controlPort
232 isolate_args[6] = Dart_Null(); // capabilities
233
234 Dart_Handle isolate_lib =
235 Dart_LookupLibrary(Dart_NewStringFromCString("dart:isolate"));
236 Dart_Handle result =
237 Dart_Invoke(isolate_lib, Dart_NewStringFromCString("_startIsolate"),
238 kNumIsolateArgs, isolate_args);
239 CHECK_RESULT(result);
240 CHECK_RESULT(Dart_RunLoop());
241
242 Dart_CloseNativePort(send_port_id);
243
244 Dart_ExitScope();
245 Dart_ShutdownIsolate();
246}
247
248void DartDevIsolate::DartDevRunner::ProcessError(const char* msg,
249 int32_t exit_code) {
250 Syslog::PrintErr("%s\n", msg);
251 Process::SetGlobalExitCode(exit_code);
252 result_ = DartDevIsolate::DartDev_Result_Exit;
253 DartDevRunner::monitor_->Notify();
254}
255
256DartDevIsolate::DartDev_Result DartDevIsolate::RunDartDev(
257 Dart_IsolateGroupCreateCallback create_isolate,
258 const char* packages_file,
259 char** script,
260 CommandLineOptions* dart_options) {
261 runner_.Run(create_isolate, packages_file, script, dart_options);
262 return runner_.result();
263}
264
265#endif // if !defined(DART_PRECOMPILED_RUNTIME)
266
267} // namespace bin
268} // namespace dart
269