Skip to content

Commit 7b25ce8

Browse files
a-sivaCommit Queue
authored andcommitted
[dartdev] Use VmInteropHandler for invoking sub commands
Use VmInteropHandler for invoking sub commands instead of running them in an isolate. Running sub commands in an isolate causes an increased footprint. Changing this to use VmInteropHandler avoids the additional memory footprint. Commands that need to use an AOT runtime for execution now exec the AOT runtime and run the command. TEST=ci Change-Id: Ic96845b19951170effea3dd3619f798e2c72968a Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/402781 Reviewed-by: Ben Konyi <[email protected]> Commit-Queue: Siva Annamalai <[email protected]>
1 parent 6880ea8 commit 7b25ce8

File tree

11 files changed

+442
-57
lines changed

11 files changed

+442
-57
lines changed

pkg/dartdev/lib/src/commands/compile.dart

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import '../experiments.dart';
1717
import '../native_assets.dart';
1818
import '../sdk.dart';
1919
import '../utils.dart';
20+
import '../vm_interop_handler.dart';
2021

2122
const int genericErrorExitCode = 255;
2223
const int compileErrorExitCode = 254;
@@ -93,17 +94,18 @@ class CompileJSCommand extends CompileSubcommandCommand {
9394
final args = argResults!;
9495
var snapshot = sdk.dart2jsAotSnapshot;
9596
var runtime = sdk.dartAotRuntime;
97+
var useExecProcess = true;
9698
if (!Sdk.checkArtifactExists(snapshot, logError: false)) {
9799
// AOT snapshots cannot be generated on IA32, so we need this fallback
98100
// branch until support for IA32 is dropped (https://dartbug.com/49969).
99101
snapshot = sdk.dart2jsSnapshot;
100-
runtime = sdk.dart;
101102
if (!Sdk.checkArtifactExists(snapshot)) {
102103
return genericErrorExitCode;
103104
}
105+
runtime = sdk.dart;
106+
useExecProcess = false;
104107
}
105108
final dart2jsCommand = [
106-
runtime,
107109
snapshot,
108110
'--libraries-spec=${sdk.librariesJson}',
109111
'--cfe-invocation-modes=compile',
@@ -112,8 +114,13 @@ class CompileJSCommand extends CompileSubcommandCommand {
112114
if (args.rest.isNotEmpty) ...args.rest.sublist(0),
113115
];
114116
try {
115-
final exitCode = await runProcessInheritStdio(dart2jsCommand);
116-
return exitCode;
117+
VmInteropHandler.run(
118+
runtime,
119+
dart2jsCommand,
120+
packageConfigOverride: null,
121+
useExecProcess: useExecProcess,
122+
);
123+
return 0;
117124
} catch (e, st) {
118125
log.stderr('Error: JS compilation failed');
119126
log.stderr(e.toString());
@@ -135,8 +142,12 @@ class CompileDDCCommand extends CompileSubcommandCommand {
135142
// This command is an internal developer command used by tools and is
136143
// hidden in the help message.
137144
CompileDDCCommand({bool verbose = false})
138-
: super(cmdName, 'Compile Dart to JavaScript using ddc.', verbose,
139-
hidden:true,);
145+
: super(
146+
cmdName,
147+
'Compile Dart to JavaScript using ddc.',
148+
verbose,
149+
hidden: true,
150+
);
140151

141152
@override
142153
String get invocation => '${super.invocation} <dart entry point>';

pkg/dartdev/lib/src/vm_interop_handler.dart

Lines changed: 82 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import 'dart:io';
56
import 'dart:isolate';
67

78
/// Contains methods used to communicate DartDev results back to the VM.
@@ -18,6 +19,10 @@ abstract class VmInteropHandler {
1819
///
1920
/// If [markMainIsolateAsSystemIsolate] is given and set to true, the spawned
2021
/// isolate will run with `--mark-main-isolate-as-system-isolate` enabled.
22+
///
23+
/// If [useExecProcess] is given and set to true, the script is executed by
24+
/// execing it (On Linux/Mac the exec call is used, on Windows a new child
25+
/// process is started).
2126
static void run(
2227
String script,
2328
List<String> args, {
@@ -27,16 +32,33 @@ abstract class VmInteropHandler {
2732
//
2833
// See https://github.com/dart-lang/sdk/issues/53576
2934
bool markMainIsolateAsSystemIsolate = false,
35+
bool useExecProcess = false,
3036
}) {
37+
List<String> argsList;
38+
if (useExecProcess && Platform.isWindows) {
39+
// On Windows if a new process is used to execute the script we
40+
// need to escape the script path and all the arguments as we
41+
// construct a single command line string to be passed to the
42+
// Windows process create call.
43+
if (script.contains(' ') && !script.contains('"')) {
44+
// Escape paths that may contain spaces
45+
script = '"$script"';
46+
}
47+
argsList = [
48+
for (int i = 0; i < args.length; i++) _windowsArgumentEscape(args[i]),
49+
];
50+
} else {
51+
// Copy the list so it doesn't get GC'd underneath us.
52+
argsList = args.toList();
53+
}
3154
final port = _port;
3255
if (port == null) return;
3356
final message = <dynamic>[
34-
_kResultRun,
57+
useExecProcess? _kResultRunExec : _kResultRun,
3558
script,
3659
packageConfigOverride,
3760
markMainIsolateAsSystemIsolate,
38-
// Copy the list so it doesn't get GC'd underneath us.
39-
args.toList()
61+
argsList
4062
];
4163
port.send(message);
4264
}
@@ -50,9 +72,65 @@ abstract class VmInteropHandler {
5072
port.send(message);
5173
}
5274

75+
/// This code is identical to the one in process_patch.dart, please ensure
76+
/// changes made here are also done in process_patch.dart.
77+
/// TODO : figure out if this functionality can be abstracted out to a
78+
/// common place.
79+
static String _windowsArgumentEscape(String argument) {
80+
if (argument.isEmpty) {
81+
return '""';
82+
}
83+
var result = argument;
84+
if (argument.contains('\t') ||
85+
argument.contains(' ') ||
86+
argument.contains('"')) {
87+
// Produce something that the C runtime on Windows will parse
88+
// back as this string.
89+
90+
// Replace any number of '\' followed by '"' with
91+
// twice as many '\' followed by '\"'.
92+
var backslash = '\\'.codeUnitAt(0);
93+
var sb = StringBuffer();
94+
var nextPos = 0;
95+
var quotePos = argument.indexOf('"', nextPos);
96+
while (quotePos != -1) {
97+
var numBackslash = 0;
98+
var pos = quotePos - 1;
99+
while (pos >= 0 && argument.codeUnitAt(pos) == backslash) {
100+
numBackslash++;
101+
pos--;
102+
}
103+
sb.write(argument.substring(nextPos, quotePos - numBackslash));
104+
for (var i = 0; i < numBackslash; i++) {
105+
sb.write(r'\\');
106+
}
107+
sb.write(r'\"');
108+
nextPos = quotePos + 1;
109+
quotePos = argument.indexOf('"', nextPos);
110+
}
111+
sb.write(argument.substring(nextPos, argument.length));
112+
result = sb.toString();
113+
114+
// Add '"' at the beginning and end and replace all '\' at
115+
// the end with two '\'.
116+
sb = StringBuffer('"');
117+
sb.write(result);
118+
nextPos = argument.length - 1;
119+
while (argument.codeUnitAt(nextPos) == backslash) {
120+
sb.write('\\');
121+
nextPos--;
122+
}
123+
sb.write('"');
124+
result = sb.toString();
125+
}
126+
127+
return result;
128+
}
129+
53130
// Note: keep in sync with runtime/bin/dartdev_isolate.h
54131
static const int _kResultRun = 1;
55-
static const int _kResultExit = 2;
132+
static const int _kResultRunExec = 2;
133+
static const int _kResultExit = 3;
56134

57135
static SendPort? _port;
58136
}

runtime/bin/dartdev_isolate.cc

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ DartDevIsolate::DartDevRunner DartDevIsolate::runner_ =
3838
DartDevIsolate::DartDevRunner();
3939
bool DartDevIsolate::should_run_dart_dev_ = false;
4040
bool DartDevIsolate::print_usage_error_ = false;
41+
CommandLineOptions* DartDevIsolate::vm_options_ = nullptr;
4142
Monitor* DartDevIsolate::DartDevRunner::monitor_ = new Monitor();
4243
DartDevIsolate::DartDev_Result DartDevIsolate::DartDevRunner::result_ =
4344
DartDevIsolate::DartDev_Result_Unknown;
@@ -204,6 +205,86 @@ void DartDevIsolate::DartDevRunner::DartDevResultCallback(
204205
}
205206
break;
206207
}
208+
case DartDevIsolate::DartDev_Result_RunExec: {
209+
result_ = DartDevIsolate::DartDev_Result_RunExec;
210+
ASSERT(GetArrayItem(message, 1)->type == Dart_CObject_kString);
211+
auto item2 = GetArrayItem(message, 2);
212+
213+
ASSERT(item2->type == Dart_CObject_kString ||
214+
item2->type == Dart_CObject_kNull);
215+
216+
auto item3 = GetArrayItem(message, 3);
217+
218+
ASSERT(item3->type == Dart_CObject_kBool);
219+
const bool mark_main_isolate_as_system_isolate = item3->value.as_bool;
220+
if (mark_main_isolate_as_system_isolate) {
221+
Options::set_mark_main_isolate_as_system_isolate(true);
222+
}
223+
224+
if (*script_ != nullptr) {
225+
free(*script_);
226+
}
227+
if (*package_config_override_ != nullptr) {
228+
free(*package_config_override_);
229+
*package_config_override_ = nullptr;
230+
}
231+
*script_ = Utils::StrDup(GetArrayItem(message, 1)->value.as_string);
232+
233+
if (item2->type == Dart_CObject_kString) {
234+
*package_config_override_ = Utils::StrDup(item2->value.as_string);
235+
}
236+
237+
intptr_t num_vm_options = 0;
238+
const char** vm_options = nullptr;
239+
ASSERT(GetArrayItem(message, 4)->type == Dart_CObject_kArray);
240+
Dart_CObject* args = GetArrayItem(message, 4);
241+
intptr_t argc = args->value.as_array.length;
242+
Dart_CObject** dart_args = args->value.as_array.values;
243+
244+
if (vm_options_ != nullptr) {
245+
num_vm_options = vm_options_->count();
246+
vm_options = vm_options_->arguments();
247+
}
248+
auto deleter = [](char** args) {
249+
for (intptr_t i = 0; i < argc_; ++i) {
250+
free(args[i]);
251+
}
252+
delete[] args;
253+
};
254+
// Total count of arguments to be passed to the script being execed.
255+
argc_ = argc + num_vm_options + 1;
256+
257+
// Array of arguments to be passed to the script being execed.
258+
argv_ = std::unique_ptr<char*[], void (*)(char**)>(new char*[argc_ + 1],
259+
deleter);
260+
261+
intptr_t idx = 0;
262+
// Copy in name of the script to run (dartaotruntime).
263+
argv_[0] = Utils::StrDup(GetArrayItem(message, 1)->value.as_string);
264+
idx += 1;
265+
// Copy in any vm options that need to be passed to the execed process.
266+
for (intptr_t i = 0; i < num_vm_options; ++i) {
267+
argv_[i + idx] = Utils::StrDup(vm_options[i]);
268+
}
269+
idx += num_vm_options;
270+
// Copy in the dart options that need to be passed to the command.
271+
for (intptr_t i = 0; i < argc; ++i) {
272+
argv_[i + idx] = Utils::StrDup(dart_args[i]->value.as_string);
273+
}
274+
// Null terminate the argv array.
275+
argv_[argc + idx] = nullptr;
276+
277+
// Exec the script to be run and pass the arguments.
278+
char err_msg[256];
279+
err_msg[0] = '\0';
280+
int ret = Process::Exec(nullptr, *script_,
281+
const_cast<const char**>(argv_.get()), argc_,
282+
nullptr, err_msg, sizeof(err_msg));
283+
if (ret != 0) {
284+
ProcessError(err_msg, kErrorExitCode);
285+
}
286+
break;
287+
}
207288
case DartDevIsolate::DartDev_Result_Exit: {
208289
ASSERT(GetArrayItem(message, 1)->type == Dart_CObject_kInt32);
209290
int32_t dartdev_exit_code = GetArrayItem(message, 1)->value.as_int32;
@@ -314,7 +395,9 @@ DartDevIsolate::DartDev_Result DartDevIsolate::RunDartDev(
314395
Dart_IsolateGroupCreateCallback create_isolate,
315396
char** packages_file,
316397
char** script,
398+
CommandLineOptions* vm_options,
317399
CommandLineOptions* dart_options) {
400+
vm_options_ = vm_options;
318401
runner_.Run(create_isolate, packages_file, script, dart_options);
319402
return runner_.result();
320403
}

runtime/bin/dartdev_isolate.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ class DartDevIsolate {
2828
typedef enum {
2929
DartDev_Result_Unknown = -1,
3030
DartDev_Result_Run = 1,
31-
DartDev_Result_Exit = 2,
31+
DartDev_Result_RunExec = 2,
32+
DartDev_Result_Exit = 3,
3233
} DartDev_Result;
3334

3435
// Returns true if there does not exist a file at |script_uri| or the URI is
@@ -58,6 +59,7 @@ class DartDevIsolate {
5859
Dart_IsolateGroupCreateCallback create_isolate,
5960
char** packages_file,
6061
char** script,
62+
CommandLineOptions* vm_options,
6163
CommandLineOptions* dart_options);
6264

6365
protected:
@@ -83,11 +85,11 @@ class DartDevIsolate {
8385
static char** package_config_override_;
8486
static std::unique_ptr<char*[], void (*)(char**)> argv_;
8587
static intptr_t argc_;
88+
static Monitor* monitor_;
8689

8790
Dart_IsolateGroupCreateCallback create_isolate_;
8891
CommandLineOptions* dart_options_;
8992
const char* packages_file_;
90-
static Monitor* monitor_;
9193

9294
DISALLOW_ALLOCATION();
9395
};
@@ -98,6 +100,7 @@ class DartDevIsolate {
98100
static DartDevRunner runner_;
99101
static bool should_run_dart_dev_;
100102
static bool print_usage_error_;
103+
static CommandLineOptions* vm_options_;
101104

102105
DISALLOW_ALLOCATION();
103106
DISALLOW_IMPLICIT_CONSTRUCTORS(DartDevIsolate);

runtime/bin/main_impl.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1433,7 +1433,7 @@ void main(int argc, char** argv) {
14331433
Options::gen_snapshot_kind() == SnapshotKind::kNone) {
14341434
DartDevIsolate::DartDev_Result dartdev_result = DartDevIsolate::RunDartDev(
14351435
CreateIsolateGroupAndSetup, &package_config_override, &script_name,
1436-
&dart_options);
1436+
&vm_options, &dart_options);
14371437
ASSERT(dartdev_result != DartDevIsolate::DartDev_Result_Unknown);
14381438
ran_dart_dev = true;
14391439
should_run_user_program =

runtime/bin/process.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,9 @@ void FUNCTION_NAME(Process_Start)(Dart_NativeArguments args) {
114114
const char* path = DartUtils::GetStringValue(path_handle);
115115
Dart_Handle arguments = Dart_GetNativeArgument(args, 3);
116116
intptr_t args_length = 0;
117-
char** string_args =
117+
const char** string_args = const_cast<const char**>(
118118
ExtractCStringList(arguments, status_handle,
119-
"Arguments must be builtin strings", &args_length);
119+
"Arguments must be builtin strings", &args_length));
120120
if (string_args == nullptr) {
121121
Dart_SetBooleanReturnValue(args, false);
122122
return;

runtime/bin/process.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ class Process {
9494
// process exit streams.
9595
static int Start(Namespace* namespc,
9696
const char* path,
97-
char* arguments[],
97+
const char* arguments[],
9898
intptr_t arguments_length,
9999
const char* working_directory,
100100
char* environment[],
@@ -114,6 +114,23 @@ class Process {
114114
intptr_t exit_handler,
115115
ProcessResult* result);
116116

117+
// Exec process.
118+
// On systems that support 'exec' it will use it to replace
119+
// the current process image with the image corresponding to 'path'
120+
// On systems that do not support it (Windows) it will start in a
121+
// child process in the same group as the parent so that when the parent
122+
// is killed the child also dies.
123+
// Returns 0 if the process could be execed successfully
124+
// Returns -1 if the exec could not be done successfully and 'errmsg'
125+
// points to the error message
126+
static int Exec(Namespace* namespc,
127+
const char* path,
128+
const char* arguments[],
129+
intptr_t arguments_length,
130+
const char* working_directory,
131+
char* errmsg,
132+
intptr_t errmsg_len);
133+
117134
// Kill a process with a given pid.
118135
static bool Kill(intptr_t id, int signal);
119136

0 commit comments

Comments
 (0)