blob: 169c62adc0fce44c59a543debca990845edd33e5 [file] [log] [blame] [edit]
// Copyright (c) 2020, 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.
import 'dart:io';
import 'dart:isolate';
/// Contains methods used to communicate DartDev results back to the VM.
///
/// Messages are received in runtime/bin/dartdev_isolate.cc.
abstract class VmInteropHandler {
/// Initializes [VmInteropHandler] to utilize [port] to communicate with the
/// VM.
static void initialize(SendPort? port) => _port = port;
/// Notifies the VM to run [script] with [args] upon DartDev exit.
///
/// If [packageConfigOverride] is given, that is where the packageConfig is found.
///
/// If [markMainIsolateAsSystemIsolate] is given and set to true, the spawned
/// isolate will run with `--mark-main-isolate-as-system-isolate` enabled.
///
/// If [useExecProcess] is given and set to true, the script is executed by
/// execing it (On Linux/Mac the exec call is used, on Windows a new child
/// process is started).
static void run(
String script,
List<String> args, {
String? packageConfigOverride,
// TODO(bkonyi): remove once DartDev moves to AOT and this flag can be
// provided directly to the process spawned by `dart run` and `dart test`.
//
// See https://github.com/dart-lang/sdk/issues/53576
bool markMainIsolateAsSystemIsolate = false,
bool useExecProcess = false,
}) {
List<String> argsList;
if (useExecProcess && Platform.isWindows) {
// On Windows if a new process is used to execute the script we
// need to escape the script path and all the arguments as we
// construct a single command line string to be passed to the
// Windows process create call.
if (script.contains(' ') && !script.contains('"')) {
// Escape paths that may contain spaces
script = '"$script"';
}
argsList = [
for (int i = 0; i < args.length; i++) _windowsArgumentEscape(args[i]),
];
} else {
// Copy the list so it doesn't get GC'd underneath us.
argsList = args.toList();
}
final port = _port;
if (port == null) return;
final message = <dynamic>[
useExecProcess ? _kResultRunExec : _kResultRun,
script,
packageConfigOverride,
markMainIsolateAsSystemIsolate,
argsList
];
port.send(message);
}
/// Notifies the VM that DartDev has completed running. If provided a
/// non-zero [exitCode], the VM will terminate with the given exit code.
static void exit(int? exitCode) {
final port = _port;
if (port == null) return;
final message = <dynamic>[_kResultExit, exitCode];
port.send(message);
}
/// This code is identical to the one in process_patch.dart, please ensure
/// changes made here are also done in process_patch.dart.
/// TODO : figure out if this functionality can be abstracted out to a
/// common place.
static String _windowsArgumentEscape(String argument) {
if (argument.isEmpty) {
return '""';
}
var result = argument;
if (argument.contains('\t') ||
argument.contains(' ') ||
argument.contains('"')) {
// Produce something that the C runtime on Windows will parse
// back as this string.
// Replace any number of '\' followed by '"' with
// twice as many '\' followed by '\"'.
var backslash = '\\'.codeUnitAt(0);
var sb = StringBuffer();
var nextPos = 0;
var quotePos = argument.indexOf('"', nextPos);
while (quotePos != -1) {
var numBackslash = 0;
var pos = quotePos - 1;
while (pos >= 0 && argument.codeUnitAt(pos) == backslash) {
numBackslash++;
pos--;
}
sb.write(argument.substring(nextPos, quotePos - numBackslash));
for (var i = 0; i < numBackslash; i++) {
sb.write(r'\\');
}
sb.write(r'\"');
nextPos = quotePos + 1;
quotePos = argument.indexOf('"', nextPos);
}
sb.write(argument.substring(nextPos, argument.length));
result = sb.toString();
// Add '"' at the beginning and end and replace all '\' at
// the end with two '\'.
sb = StringBuffer('"');
sb.write(result);
nextPos = argument.length - 1;
while (argument.codeUnitAt(nextPos) == backslash) {
sb.write('\\');
nextPos--;
}
sb.write('"');
result = sb.toString();
}
return result;
}
// Note: keep in sync with runtime/bin/dartdev_isolate.h
static const int _kResultRun = 1;
static const int _kResultRunExec = 2;
static const int _kResultExit = 3;
static SendPort? _port;
}