Reland "[ VM / DartDev ] Launch DartDev in an isolate within a single main Dart process"

This reverts commit 7e373831ce.

Change-Id: Ib94d793a5753ec6352cd9a65e35f726a63336368
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/154830
Reviewed-by: Siva Annamalai <asiva@google.com>
This commit is contained in:
Ben Konyi 2020-07-21 16:23:18 +00:00
parent 204d22467b
commit 157ebfe50f
37 changed files with 838 additions and 518 deletions

View file

@ -61,7 +61,10 @@ group("runtime") {
# TODO(bkonyi): this dep causes a segfault on Android XARM_X64 builds.
# See issue #41776.
if (dart_target_arch != "arm") {
deps += [ "utils/dartdev:dartdev" ]
deps += [
"utils/dartdev:dartdev",
"utils/dds:dds",
]
}
}
@ -159,9 +162,7 @@ if (is_fuchsia) {
data_deps = [ "runtime/bin:dart" ]
manifest = "build/fuchsia/dart.cmx"
resource_files = [
".packages",
]
resource_files = [ ".packages" ]
resource_dirs = [
"tests/standalone",
"pkg/async_helper",
@ -178,13 +179,15 @@ if (is_fuchsia) {
resources = []
foreach(file, resource_files) {
resources += [{
path = file
dest = "data/" + file
}]
resources += [
{
path = file
dest = "data/" + file
},
]
}
resources += exec_script(
"tools/fuchsia/find_resources.py", resource_dirs, "json")
resources +=
exec_script("tools/fuchsia/find_resources.py", resource_dirs, "json")
}
fuchsia_package("dart_sdk_fuchsia_test_package") {

View file

@ -2,18 +2,12 @@
// 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:async';
import 'dart:isolate';
import 'package:dartdev/dartdev.dart';
import 'package:pedantic/pedantic.dart' show unawaited;
/// The entry point for dartdev.
Future<void> main(List<String> args) async {
// Ignore SIGINT to ensure DartDev doesn't exit before any of its
// spawned children. Draining the stream returned by watch() effectively
// sends the signals to the void.
//
// See https://github.com/dart-lang/sdk/issues/42092 for context.
unawaited(ProcessSignal.sigint.watch().drain());
await runDartdev(args);
Future<void> main(List<String> args, SendPort port) async {
await runDartdev(args, port);
}

View file

@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'dart:io' as io;
import 'dart:isolate';
import 'package:analyzer/src/dart/analysis/experiments.dart';
import 'package:args/args.dart';
@ -21,11 +22,13 @@ import 'src/commands/run.dart';
import 'src/commands/test.dart';
import 'src/core.dart';
import 'src/experiments.dart';
import 'src/vm_interop_handler.dart';
/// This is typically called from bin/, but given the length of the method and
/// analytics logic, it has been moved here. Also note that this method calls
/// [io.exit(code)] directly.
Future<void> runDartdev(List<String> args) async {
Future<void> runDartdev(List<String> args, SendPort port) async {
VmInteropHandler.initialize(port);
final stopwatch = Stopwatch();
int result;
@ -68,6 +71,12 @@ Future<void> runDartdev(List<String> args) async {
io.exit(0);
}
// --launch-dds is provided by the VM if the VM service is to be enabled. In
// that case, we need to launch DDS as well.
// TODO(bkonyi): add support for pub run (#42726)
if (args.contains('--launch-dds')) {
RunCommand.launchDds = true;
}
String commandName;
try {
@ -79,6 +88,12 @@ Future<void> runDartdev(List<String> args) async {
args = List.from(args)..remove('--disable-dartdev-analytics');
}
// Run also can't be called with '--launch-dds', remove it if it's
// contained in args.
if (args.contains('--launch-dds')) {
args = List.from(args)..remove('--launch-dds');
}
// Before calling to run, send the first ping to analytics to have the first
// ping, as well as the command itself, running in parallel.
if (analytics.enabled) {
@ -133,7 +148,7 @@ Future<void> runDartdev(List<String> args) async {
analytics.enabled = true;
}
analytics.close();
io.exit(exitCode);
VmInteropHandler.exit(exitCode);
}
}
@ -166,6 +181,11 @@ class DartdevRunner<int> extends CommandRunner {
help: 'Disable anonymous analytics for this `dart *` run',
hide: true);
// Another hidden flag used by the VM to indicate that DDS should be
// launched. Should be removed for all commands other than `run`.
argParser.addFlag('launch-dds',
negatable: false, hide: true, help: 'Launch DDS.');
addCommand(AnalyzeCommand());
addCommand(CreateCommand(verbose: verbose));
addCommand(CompileCommand());
@ -192,7 +212,7 @@ class DartdevRunner<int> extends CommandRunner {
io.stderr.writeln(
"Error when reading '$firstArg': No such file or directory.");
// This is the exit code used by the frontend.
io.exit(254);
VmInteropHandler.exit(254);
}
}

View file

@ -73,7 +73,7 @@ class AnalyzeCommand extends DartdevCommand<int> {
});
await analysisCompleter.future;
await server.dispose();
progress.finish(showTiming: true);
errors.sort();

View file

@ -4,18 +4,20 @@
import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'package:args/args.dart';
import 'package:dds/dds.dart';
import 'package:path/path.dart';
import '../core.dart';
import '../experiments.dart';
import '../sdk.dart';
import '../utils.dart';
import '../vm_interop_handler.dart';
class RunCommand extends DartdevCommand<int> {
static bool launchDds = false;
@override
final ArgParser argParser = ArgParser.allowAnything();
@ -60,21 +62,19 @@ Run a Dart file.''');
// The command line arguments after 'run'
var args = argResults.arguments.toList();
var argsContainFileOrHelp = false;
var argsContainFile = false;
for (var arg in args) {
// The arg.contains('.') matches a file name pattern, i.e. some 'foo.dart'
if (arg.contains('.') ||
arg == '--help' ||
arg == '-h' ||
arg == 'help') {
argsContainFileOrHelp = true;
break;
if (arg.contains('.')) {
argsContainFile = true;
} else if (arg == '--help' || arg == '-h' || arg == 'help') {
printUsage();
return 0;
}
}
final cwd = Directory.current;
if (!argsContainFileOrHelp && cwd.existsSync()) {
if (!argsContainFile && cwd.existsSync()) {
var foundImplicitFileToRun = false;
var cwdName = cwd.name;
for (var entity in cwd.listSync(followLinks: false)) {
@ -100,6 +100,8 @@ Run a Dart file.''');
'Could not find the implicit file to run: '
'bin$separator$cwdName.dart.',
);
// Error exit code, as defined in runtime/bin/error_exit.h
return 255;
}
}
@ -117,213 +119,43 @@ Run a Dart file.''');
// service intermediary which implements the VM service protocol and
// provides non-VM specific extensions (e.g., log caching, client
// synchronization).
if (args.any((element) =>
element.startsWith('--observe') ||
element.startsWith('--enable-vm-service'))) {
return await _DebuggingSession(this, args).start();
} else {
// Starting in ProcessStartMode.inheritStdio mode means the child process
// can detect support for ansi chars.
final process = await Process.start(
sdk.dart, ['--disable-dart-dev', ...args],
mode: ProcessStartMode.inheritStdio);
return process.exitCode;
_DebuggingSession debugSession;
if (launchDds) {
debugSession = _DebuggingSession();
await debugSession.start();
}
final script = Directory.current.uri
.resolve(args.firstWhere((e) => !e.startsWith('-')))
.toFilePath();
final runArgs = args.length == 1 ? <String>[] : args.sublist(1);
VmInteropHandler.run(script, runArgs);
return 0;
}
}
class _DebuggingSession {
_DebuggingSession(this._runCommand, List<String> args)
: _args = args.toList() {
// Process flags that are meant to configure the VM service HTTP server or
// dump VM service connection information to a file. Since the VM service
// clients won't actually be connecting directly to the service, we'll make
// DDS appear as if it is the actual VM service.
for (final arg in _args) {
final isObserve = arg.startsWith('--observe');
if (isObserve || arg.startsWith('--enable-vm-service')) {
if (isObserve) {
_observe = true;
}
if (arg.contains('=') || arg.contains(':')) {
// These flags can be provided by the embedder so we need to check for
// both `=` and `:` separators.
final observatoryBindInfo =
(arg.contains('=') ? arg.split('=') : arg.split(':'))[1]
.split('/');
_port = int.tryParse(observatoryBindInfo.first) ?? 0;
if (observatoryBindInfo.length > 1) {
try {
_bindAddress = Uri.http(observatoryBindInfo[1], '');
} on FormatException {
// TODO(bkonyi): log invalid parse? The VM service just ignores
// bad input flags.
// Ignore.
}
}
}
} else if (arg.startsWith('--write-service-info=')) {
try {
final split = arg.split('=');
if (split[1].isNotEmpty) {
_serviceInfoUri = Uri.parse(split[1]);
} else {
_runCommand.usageException(
'Invalid URI argument to --write-service-info: "${split[1]}"');
}
} on FormatException {
// TODO(bkonyi): log invalid parse? The VM service just ignores bad
// input flags.
// Ignore.
}
} else if (arg == '--disable-service-auth-codes') {
_disableServiceAuthCodes = true;
Future<void> start() async {
final serviceInfo = await Service.getInfo();
final process = await Process.start(
sdk.dart,
[
if (dirname(sdk.dart).endsWith('bin'))
sdk.ddsSnapshot
else
absolute(dirname(sdk.dart), 'gen', 'dds.dart.snapshot'),
serviceInfo.serverUri.toString()
],
mode: ProcessStartMode.detachedWithStdio);
final completer = Completer<void>();
StreamSubscription sub;
sub = process.stderr.transform(utf8.decoder).listen((event) {
if (event == 'DDS started') {
sub.cancel();
completer.complete();
}
}
// Strip --observe and --write-service-info from the arguments as we'll be
// providing our own.
_args.removeWhere(
(arg) =>
arg.startsWith('--observe') ||
arg.startsWith('--enable-vm-service') ||
arg.startsWith('--write-service-info'),
);
}
FutureOr<int> start() async {
// Output the service information for the target process to a temporary
// file so we can avoid scraping stderr for the service URI.
final serviceInfoDir =
await Directory.systemTemp.createTemp('dart_service');
final serviceInfoUri = serviceInfoDir.uri.resolve('service_info.json');
final serviceInfoFile = await File.fromUri(serviceInfoUri).create();
// Start using ProcessStartMode.normal and forward stdio manually as we
// need to filter the true VM service URI and replace it with the DDS URI.
_process = await Process.start(
sdk.dart,
[
'--disable-dart-dev',
// We don't care which port the VM service binds to.
_observe ? '--observe=0' : '--enable-vm-service=0',
'--write-service-info=$serviceInfoUri',
..._args,
],
);
_forwardAndFilterStdio(_process);
// Start DDS once the VM service has finished starting up.
await Future.any([
_waitForRemoteServiceUri(serviceInfoFile).then(_startDDS),
_process.exitCode,
]);
return _process.exitCode.then((exitCode) async {
// Shutdown DDS if it was started and wait for the process' stdio streams
// to close so we don't truncate program output.
await Future.wait([
if (_dds != null) _dds.shutdown(),
_stderrDone,
_stdoutDone,
]);
return exitCode;
});
await completer.future;
}
Future<Uri> _waitForRemoteServiceUri(File serviceInfoFile) async {
// Wait for VM service to write its connection info to disk.
while (await serviceInfoFile.length() <= 5) {
await Future.delayed(const Duration(milliseconds: 50));
}
final serviceInfoStr = await serviceInfoFile.readAsString();
return Uri.parse(jsonDecode(serviceInfoStr)['uri']);
}
Future<void> _startDDS(Uri remoteVmServiceUri) async {
_dds = await DartDevelopmentService.startDartDevelopmentService(
remoteVmServiceUri,
serviceUri: _bindAddress.replace(port: _port),
enableAuthCodes: !_disableServiceAuthCodes,
);
if (_serviceInfoUri != null) {
// Output the service connection information.
await File.fromUri(_serviceInfoUri).writeAsString(
json.encode({
'uri': _dds.uri.toString(),
}),
);
}
_ddsCompleter.complete();
}
void _forwardAndFilterStdio(Process process) {
// Since VM service clients cannot connect to the real VM service once DDS
// has started, replace all instances of the real VM service's URI with the
// DDS URI. Clients should only know that they are connected to DDS if they
// explicitly request that information via the protocol.
String filterObservatoryUri(String msg) {
if (_dds == null) {
return msg;
}
if (msg.contains('Observatory listening on') ||
msg.contains('Connect to Observatory at')) {
// Search for the VM service URI in the message and replace it.
msg = msg.replaceFirst(
RegExp(r'https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.'
r'[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)'),
_dds.uri.toString(),
);
}
return msg;
}
// Wait for DDS to start before handling any stdio events from the target
// to ensure we don't let any unfiltered messages slip through.
// TODO(bkonyi): consider filtering on bytes rather than decoding the UTF8.
_stderrDone = process.stderr
.transform(const Utf8Decoder(allowMalformed: true))
.listen((event) async {
await _waitForDDS();
stderr.write(filterObservatoryUri(event));
}).asFuture();
_stdoutDone = process.stdout
.transform(const Utf8Decoder(allowMalformed: true))
.listen((event) async {
await _waitForDDS();
stdout.write(filterObservatoryUri(event));
}).asFuture();
stdin.listen(
(event) async {
await _waitForDDS();
process.stdin.add(event);
},
);
}
Future<void> _waitForDDS() async {
if (!_ddsCompleter.isCompleted) {
// No need to wait for DDS if the process has already exited.
await Future.any([
_ddsCompleter.future,
_process.exitCode,
]);
}
}
Uri _bindAddress = Uri.http('127.0.0.1', '');
bool _disableServiceAuthCodes = false;
DartDevelopmentService _dds;
bool _observe = false;
int _port = 8181;
Process _process;
Uri _serviceInfoUri;
Future _stderrDone;
Future _stdoutDone;
final List<String> _args;
final Completer<void> _ddsCompleter = Completer();
final RunCommand _runCommand;
}

View file

@ -52,6 +52,9 @@ class Sdk {
String get dartfmt => path.absolute(sdkPath, 'bin', _binName('dartfmt'));
String get ddsSnapshot =>
path.absolute(sdkPath, 'bin', 'snapshots', 'dds.dart.snapshot');
String get pub => path.absolute(sdkPath, 'bin', _binName('pub'));
static String _binName(String base) =>

View file

@ -0,0 +1,40 @@
// 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:isolate';
/// Contains methods used to communicate DartDev results back to the VM.
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.
static void run(String script, List<String> args) {
assert(_port != null);
if (_port == null) return;
final message = List<dynamic>.filled(3, null)
..[0] = _kResultRun
..[1] = script
..[2] = args;
_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) {
assert(_port != null);
if (_port == null) return;
final message = List<dynamic>.filled(2, null)
..[0] = _kResultExit
..[1] = exitCode;
_port.send(message);
}
// Note: keep in sync with runtime/bin/dartdev_isolate.h
static const int _kResultRun = 1;
static const int _kResultExit = 2;
static SendPort _port;
}

View file

@ -79,13 +79,51 @@ void help() {
var result = p.runSync('--help', ['--verbose']);
expect(result.exitCode, 0);
expect(result.stdout, contains('migrate '));
expect(result.stdout, isEmpty);
expect(result.stderr,
contains('The following options are only used for VM development'));
});
test('--help -v', () {
p = project();
var result = p.runSync('--help', ['-v']);
expect(result.exitCode, 0);
expect(result.stdout, isEmpty);
expect(result.stderr,
contains('The following options are only used for VM development'));
});
test('help', () {
p = project();
var result = p.runSync('help', []);
expect(result.exitCode, 0);
expect(result.stderr, isEmpty);
expect(result.stdout, contains(DartdevRunner.dartdevDescription));
expect(result.stdout,
contains('Usage: dart [<vm-flags>] <command|dart-file> [<arguments>]'));
expect(result.stdout, contains('Global options:'));
expect(result.stdout, contains('Available commands:'));
expect(result.stdout, contains('analyze '));
expect(result.stdout, contains('create '));
expect(result.stdout, contains('compile '));
expect(result.stdout, contains('format '));
expect(result.stdout, contains('migrate '));
});
test('help --verbose', () {
p = project();
var result = p.runSync('help', ['--verbose']);
expect(result.exitCode, 0);
expect(result.stdout, contains('migrate '));
});
test('help -v', () {
p = project();
var result = p.runSync('help', ['-v']);
expect(result.exitCode, 0);
expect(result.stdout, contains('migrate '));
});

View file

@ -55,7 +55,6 @@ class TestProject {
String workingDir,
}) {
var arguments = [
absolutePathToDartdevFile,
command,
if (command == 'migrate')
// TODO(srawlins): Enable `pub outdated` in tests.
@ -64,7 +63,6 @@ class TestProject {
];
arguments.add('--disable-dartdev-analytics');
return Process.runSync(
Platform.resolvedExecutable,
arguments,

18
pkg/dds/bin/dds.dart Normal file
View file

@ -0,0 +1,18 @@
// 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 'package:dds/dds.dart';
/// A simple program which starts a [DartDevelopmentService] instance with a
/// basic configuration.
///
/// Takes the VM service URI as its single argument.
Future<void> main(List<String> args) async {
if (args.isEmpty) return;
final remoteVmServiceUri = Uri.parse(args.first);
await DartDevelopmentService.startDartDevelopmentService(remoteVmServiceUri);
stderr.write('DDS started');
}

View file

@ -30,12 +30,26 @@ class _ClientManager {
);
clients.add(client);
client.listen().then((_) => removeClient(client));
if (clients.length == 1) {
dds.isolateManager.initialize().then((_) {
dds.streamManager.streamListen(
null,
_StreamManager.kDebugStream,
);
});
}
}
/// Cleanup state for a disconnected client.
void removeClient(_DartDevelopmentServiceClient client) {
_clearClientName(client);
clients.remove(client);
if (clients.isEmpty) {
dds.streamManager.streamCancel(
null,
_StreamManager.kDebugStream,
);
}
}
/// Cleanup clients on DDS shutdown.

View file

@ -85,7 +85,6 @@ class _RunningIsolate {
if (_resumeApprovalsByName.isEmpty) {
return false;
}
// If all the required approvals are present, we should resume.
return _resumeApprovalsByName.containsAll(requiredClientApprovals);
}

View file

@ -117,12 +117,15 @@ class _StreamManager {
) async {
assert(stream != null && stream.isNotEmpty);
if (!streamListeners.containsKey(stream)) {
// This will return an RPC exception if the stream doesn't exist. This
// will throw and the exception will be forwarded to the client.
final result = await dds._vmServiceClient.sendRequest('streamListen', {
'streamId': stream,
});
assert(result['type'] == 'Success');
if ((stream == kDebugStream && client == null) ||
stream != kDebugStream) {
// This will return an RPC exception if the stream doesn't exist. This
// will throw and the exception will be forwarded to the client.
final result = await dds._vmServiceClient.sendRequest('streamListen', {
'streamId': stream,
});
assert(result['type'] == 'Success');
}
streamListeners[stream] = <_DartDevelopmentServiceClient>[];
}
if (streamListeners[stream].contains(client)) {
@ -142,16 +145,18 @@ class _StreamManager {
/// send a `streamCancel` request for `stream` to the VM service.
Future<void> streamCancel(
_DartDevelopmentServiceClient client,
String stream,
) async {
String stream, {
bool cancelCoreStream = false,
}) async {
assert(stream != null && stream.isNotEmpty);
final listeners = streamListeners[stream];
if (listeners == null || !listeners.contains(client)) {
if (client != null && (listeners == null || !listeners.contains(client))) {
throw kStreamNotSubscribedException;
}
listeners.remove(client);
// Don't cancel streams DDS needs to function.
if (listeners.isEmpty && !ddsCoreStreams.contains(stream)) {
if (listeners.isEmpty &&
(!ddsCoreStreams.contains(stream) || cancelCoreStream)) {
streamListeners.remove(stream);
// Ensure the VM service hasn't shutdown.
if (dds._vmServiceClient.isClosed) {

View file

@ -103,6 +103,10 @@ abstract class TestCase {
'--enable-vm-service=0', // Note: use 0 to avoid port collisions.
'--pause_isolates_on_start',
'--disable-service-auth-codes',
// TODO(bkonyi): The service isolate starts before DartDev has a chance
// to spawn DDS. We should suppress the Observatory message until DDS
// starts (#42727).
'--disable-dart-dev',
outputUri.toFilePath()
];
vmArgs.add('$reloadCount');

View file

@ -13,6 +13,7 @@ main(List<String> args) async {
LeakFinder heapHelper = new LeakFinder();
heapHelper.start([
"--disable-dart-dev",
"--enable-asserts",
Platform.script.resolve("incremental_dart2js_tester.dart").toString(),
"--addDebugBreaks",

View file

@ -56,6 +56,7 @@ benchmark
besides
beta
bigger
bkonyi
blah
blindly
blocked
@ -129,6 +130,7 @@ dash
dashes
day
db
dds
debugger
decrease
decrements
@ -502,6 +504,7 @@ slight
smoke
somehow
spans
spawn
spell
spellcheck
spelled

View file

@ -453,6 +453,7 @@ main() {
"--pause-isolates-on-exit",
"--enable-vm-service:0",
"--disable-service-auth-codes",
"--disable-dart-dev",
list.path
]);
@ -579,6 +580,7 @@ main() {
"--pause-isolates-on-exit",
"--enable-vm-service:0",
"--disable-service-auth-codes",
"--disable-dart-dev",
list.path
]);
@ -658,6 +660,7 @@ main() {
'--enable-vm-service=0', // Note: use 0 to avoid port collisions.
'--pause_isolates_on_start',
'--disable-service-auth-codes',
'--disable-dart-dev',
outputFile.path
];
final vm = await Process.start(Platform.resolvedExecutable, vmArgs);
@ -917,6 +920,7 @@ main() {
"--pause-isolates-on-start",
"--enable-vm-service:0",
"--disable-service-auth-codes",
"--disable-dart-dev",
scriptOrDill.path
]);

View file

@ -878,6 +878,8 @@ dart_executable("dart") {
}
extra_sources = [
"builtin.cc",
"dartdev_isolate.cc",
"dartdev_isolate.h",
"dfe.cc",
"dfe.h",
"gzip.cc",

View file

@ -15,8 +15,6 @@ builtin_impl_sources = [
"crypto_linux.cc",
"crypto_macos.cc",
"crypto_win.cc",
"dartdev_utils.cc",
"dartdev_utils.h",
"dartutils.cc",
"dartutils.h",
"directory.cc",

View file

@ -0,0 +1,262 @@
// 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.
#include "bin/dartdev_isolate.h"
#if !defined(DART_PRECOMPILED_RUNTIME)
#include <functional>
#include <memory>
#include "bin/directory.h"
#include "bin/error_exit.h"
#include "bin/exe_utils.h"
#include "bin/file.h"
#include "bin/lockers.h"
#include "bin/platform.h"
#include "bin/process.h"
#include "include/dart_embedder_api.h"
#include "platform/utils.h"
#define CHECK_RESULT(result) \
if (Dart_IsError(result)) { \
ProcessError(Dart_GetError(result), kErrorExitCode); \
if (send_port_id != ILLEGAL_PORT) { \
Dart_CloseNativePort(send_port_id); \
} \
Dart_ExitScope(); \
Dart_ExitIsolate(); \
return; \
}
namespace dart {
namespace bin {
DartDevIsolate::DartDevRunner DartDevIsolate::runner_ =
DartDevIsolate::DartDevRunner();
bool DartDevIsolate::should_run_dart_dev_ = false;
Monitor* DartDevIsolate::DartDevRunner::monitor_ = new Monitor();
DartDevIsolate::DartDev_Result DartDevIsolate::DartDevRunner::result_ =
DartDevIsolate::DartDev_Result_Unknown;
char** DartDevIsolate::DartDevRunner::script_ = nullptr;
std::unique_ptr<char*[], void (*)(char*[])>
DartDevIsolate::DartDevRunner::argv_ =
std::unique_ptr<char*[], void (*)(char**)>(nullptr, [](char**) {});
intptr_t DartDevIsolate::DartDevRunner::argc_ = 0;
bool DartDevIsolate::ShouldParseCommand(const char* script_uri) {
// If script_uri is not a file path or of a known URI scheme, we can assume
// that this is a DartDev command.
return (!File::ExistsUri(nullptr, script_uri) &&
(strncmp(script_uri, "http://", 7) != 0) &&
(strncmp(script_uri, "https://", 8) != 0) &&
(strncmp(script_uri, "file://", 7) != 0) &&
(strncmp(script_uri, "package:", 8) != 0) &&
(strncmp(script_uri, "google3://", 10) != 0));
}
const char* DartDevIsolate::TryResolveDartDevSnapshotPath() {
// |dir_prefix| includes the last path seperator.
auto dir_prefix = EXEUtils::GetDirectoryPrefixFromExeName();
// First assume we're in dart-sdk/bin.
char* snapshot_path =
Utils::SCreate("%ssnapshots/dartdev.dart.snapshot", dir_prefix.get());
if (File::Exists(nullptr, snapshot_path)) {
return snapshot_path;
}
free(snapshot_path);
// If we're not in dart-sdk/bin, we might be in one of the $SDK/out/*
// directories. Try to use a snapshot rom a previously built SDK.
snapshot_path = Utils::SCreate("%sdartdev.dart.snapshot", dir_prefix.get());
if (File::Exists(nullptr, snapshot_path)) {
return snapshot_path;
}
free(snapshot_path);
Syslog::PrintErr("Could not find DartDev snapshot.\n");
return nullptr;
}
void DartDevIsolate::DartDevRunner::Run(
Dart_IsolateGroupCreateCallback create_isolate,
const char* packages_file,
char** script,
CommandLineOptions* dart_options) {
create_isolate_ = create_isolate;
dart_options_ = dart_options;
packages_file_ = packages_file;
script_ = script;
MonitorLocker locker(monitor_);
int result = Thread::Start("DartDev Runner", RunCallback,
reinterpret_cast<uword>(this));
if (result != 0) {
FATAL1("Failed to start DartDev thread: %d", result);
}
monitor_->WaitMicros(Monitor::kNoTimeout);
if (result_ == DartDevIsolate::DartDev_Result_Run) {
// Clear the DartDev dart_options and replace them with the processed
// options provided by DartDev.
dart_options_->Reset();
for (intptr_t i = 0; i < argc_; ++i) {
dart_options_->AddArguments(const_cast<const char**>(argv_.get()), argc_);
}
}
}
static Dart_CObject* GetArrayItem(Dart_CObject* message, intptr_t index) {
return message->value.as_array.values[index];
}
void DartDevIsolate::DartDevRunner::DartDevResultCallback(
Dart_Port dest_port_id,
Dart_CObject* message) {
ASSERT(message->type == Dart_CObject_kArray);
int32_t type = GetArrayItem(message, 0)->value.as_int32;
switch (type) {
case DartDevIsolate::DartDev_Result_Run: {
result_ = DartDevIsolate::DartDev_Result_Run;
ASSERT(GetArrayItem(message, 1)->type == Dart_CObject_kString);
if (*script_ != nullptr) {
free(*script_);
}
*script_ = Utils::StrDup(GetArrayItem(message, 1)->value.as_string);
ASSERT(GetArrayItem(message, 2)->type == Dart_CObject_kArray);
Dart_CObject* args = GetArrayItem(message, 2);
argc_ = args->value.as_array.length;
Dart_CObject** dart_args = args->value.as_array.values;
auto deleter = [](char** args) {
for (intptr_t i = 0; i < argc_; ++i) {
free(args[i]);
}
delete[] args;
};
argv_ =
std::unique_ptr<char*[], void (*)(char**)>(new char*[argc_], deleter);
for (intptr_t i = 0; i < argc_; ++i) {
argv_[i] = Utils::StrDup(dart_args[i]->value.as_string);
}
break;
}
case DartDevIsolate::DartDev_Result_Exit: {
ASSERT(GetArrayItem(message, 1)->type == Dart_CObject_kInt32);
int32_t dartdev_exit_code = GetArrayItem(message, 1)->value.as_int32;
// If we're given a non-zero exit code, DartDev is signaling for us to
// shutdown.
if (dartdev_exit_code != 0) {
Process::SetGlobalExitCode(dartdev_exit_code);
}
// If DartDev hasn't signaled for us to do anything else, we can assume
// there's nothing else for the VM to run and that we can exit.
if (result_ == DartDevIsolate::DartDev_Result_Unknown) {
result_ = DartDevIsolate::DartDev_Result_Exit;
}
// DartDev is done processing the command. Unblock the main thread and
// continue the launch procedure.
DartDevRunner::monitor_->Notify();
break;
}
default:
UNREACHABLE();
}
}
void DartDevIsolate::DartDevRunner::RunCallback(uword args) {
MonitorLocker locker_(DartDevRunner::monitor_);
DartDevRunner* runner = reinterpret_cast<DartDevRunner*>(args);
const char* dartdev_path = DartDevIsolate::TryResolveDartDevSnapshotPath();
if (dartdev_path == nullptr) {
ProcessError("Failed to find DartDev snapshot.", kErrorExitCode);
return;
}
Dart_IsolateFlags flags;
Dart_IsolateFlagsInitialize(&flags);
char* error;
Dart_Isolate dartdev_isolate = runner->create_isolate_(
dartdev_path, "dartdev", nullptr, runner->packages_file_, &flags,
NULL /* callback_data */, const_cast<char**>(&error));
free(const_cast<char*>(dartdev_path));
if (dartdev_isolate == nullptr) {
ProcessError(error, kErrorExitCode);
return;
}
Dart_EnterIsolate(dartdev_isolate);
Dart_EnterScope();
// Retrieve the DartDev entrypoint.
Dart_Port send_port_id = ILLEGAL_PORT;
Dart_Handle root_lib = Dart_RootLibrary();
Dart_Handle main_closure =
Dart_GetField(root_lib, Dart_NewStringFromCString("main"));
CHECK_RESULT(main_closure);
if (!Dart_IsClosure(main_closure)) {
ProcessError("Unable to find 'main' in root library 'dartdev'",
kErrorExitCode);
Dart_ExitScope();
Dart_ExitIsolate();
return;
}
// Create a SendPort that DartDev can use to communicate its results over.
send_port_id = Dart_NewNativePort("dartdev", DartDevResultCallback, false);
ASSERT(send_port_id != ILLEGAL_PORT);
Dart_Handle send_port = Dart_NewSendPort(send_port_id);
CHECK_RESULT(send_port);
const intptr_t kNumIsolateArgs = 7;
Dart_Handle isolate_args[kNumIsolateArgs];
isolate_args[0] = Dart_Null(); // parentPort
isolate_args[1] = main_closure; // entryPoint
isolate_args[2] = runner->dart_options_->CreateRuntimeOptions(); // args
isolate_args[3] = send_port; // message
isolate_args[4] = Dart_True(); // isSpawnUri
isolate_args[5] = Dart_Null(); // controlPort
isolate_args[6] = Dart_Null(); // capabilities
Dart_Handle isolate_lib =
Dart_LookupLibrary(Dart_NewStringFromCString("dart:isolate"));
Dart_Handle result =
Dart_Invoke(isolate_lib, Dart_NewStringFromCString("_startIsolate"),
kNumIsolateArgs, isolate_args);
CHECK_RESULT(result);
CHECK_RESULT(Dart_RunLoop());
Dart_CloseNativePort(send_port_id);
Dart_ExitScope();
Dart_ShutdownIsolate();
}
void DartDevIsolate::DartDevRunner::ProcessError(const char* msg,
int32_t exit_code) {
Syslog::PrintErr("%s\n", msg);
Process::SetGlobalExitCode(exit_code);
result_ = DartDevIsolate::DartDev_Result_Exit;
DartDevRunner::monitor_->Notify();
}
DartDevIsolate::DartDev_Result DartDevIsolate::RunDartDev(
Dart_IsolateGroupCreateCallback create_isolate,
const char* packages_file,
char** script,
CommandLineOptions* dart_options) {
runner_.Run(create_isolate, packages_file, script, dart_options);
return runner_.result();
}
#endif // if !defined(DART_PRECOMPILED_RUNTIME)
} // namespace bin
} // namespace dart

View file

@ -0,0 +1,99 @@
// 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.
#ifndef RUNTIME_BIN_DARTDEV_ISOLATE_H_
#define RUNTIME_BIN_DARTDEV_ISOLATE_H_
#if !defined(DART_PRECOMPILED_RUNTIME)
#include <memory>
#include "bin/thread.h"
#include "include/dart_api.h"
#include "include/dart_native_api.h"
#include "platform/globals.h"
namespace dart {
namespace bin {
class CommandLineOptions;
class DartDevIsolate {
public:
// Note: keep in sync with pkg/dartdev/lib/vm_interop_handler.dart
typedef enum {
DartDev_Result_Unknown = -1,
DartDev_Result_Run = 1,
DartDev_Result_Exit = 2,
} DartDev_Result;
// Returns true if there does not exist a file at |script_uri| or the URI is
// not an HTTP resource.
static bool ShouldParseCommand(const char* script_uri);
static void set_should_run_dart_dev(bool enable) {
should_run_dart_dev_ = enable;
}
static bool should_run_dart_dev() { return should_run_dart_dev_; }
// Starts a DartDev instance in a new isolate and runs it to completion.
//
// Returns true if the VM should run the result in `script`, in which case
// `script` and `dart_options` will have been repopulated with the correct
// values.
static DartDev_Result RunDartDev(
Dart_IsolateGroupCreateCallback create_isolate,
const char* packages_file,
char** script,
CommandLineOptions* dart_options);
protected:
class DartDevRunner {
public:
DartDevRunner() {}
void Run(Dart_IsolateGroupCreateCallback create_isolate,
const char* packages_file,
char** script,
CommandLineOptions* dart_options);
DartDev_Result result() const { return result_; }
private:
static void DartDevResultCallback(Dart_Port dest_port_id,
Dart_CObject* message);
static void RunCallback(uword arg);
static void ProcessError(const char* msg, int32_t exit_code);
static DartDev_Result result_;
static char** script_;
static std::unique_ptr<char*[], void (*)(char**)> argv_;
static intptr_t argc_;
Dart_IsolateGroupCreateCallback create_isolate_;
CommandLineOptions* dart_options_;
const char* packages_file_;
static Monitor* monitor_;
DISALLOW_ALLOCATION();
};
private:
// Attempts to find the DartDev snapshot. If the snapshot cannot be found,
// the VM will shutdown.
static const char* TryResolveDartDevSnapshotPath();
static DartDevRunner runner_;
static bool should_run_dart_dev_;
DISALLOW_ALLOCATION();
DISALLOW_IMPLICIT_CONSTRUCTORS(DartDevIsolate);
};
} // namespace bin
} // namespace dart
#endif // !defined(DART_PRECOMPILED_RUNTIME)
#endif // RUNTIME_BIN_DARTDEV_ISOLATE_H_

View file

@ -1,53 +0,0 @@
// 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.
#include "bin/dartdev_utils.h"
#include <memory>
#include "bin/directory.h"
#include "bin/exe_utils.h"
#include "bin/file.h"
#include "platform/utils.h"
namespace dart {
namespace bin {
bool DartDevUtils::ShouldParseCommand(const char* script_uri) {
// If script_uri is not a file path or of a known URI scheme, we can assume
// that this is a DartDev command.
return (!File::ExistsUri(nullptr, script_uri) &&
(strncmp(script_uri, "http://", 7) != 0) &&
(strncmp(script_uri, "https://", 8) != 0) &&
(strncmp(script_uri, "file://", 7) != 0) &&
(strncmp(script_uri, "package:", 8) != 0) &&
(strncmp(script_uri, "google3://", 10) != 0));
}
bool DartDevUtils::TryResolveDartDevSnapshotPath(char** script_name) {
// |dir_prefix| includes the last path seperator.
auto dir_prefix = EXEUtils::GetDirectoryPrefixFromExeName();
// First assume we're in dart-sdk/bin.
char* snapshot_path =
Utils::SCreate("%ssnapshots/dartdev.dart.snapshot", dir_prefix.get());
if (File::Exists(nullptr, snapshot_path)) {
*script_name = snapshot_path;
return true;
}
free(snapshot_path);
// If we're not in dart-sdk/bin, we might be in one of the $SDK/out/*
// directories. Try to use a snapshot from a previously built SDK.
snapshot_path = Utils::SCreate("%sdartdev.dart.snapshot", dir_prefix.get());
if (File::Exists(nullptr, snapshot_path)) {
*script_name = snapshot_path;
return true;
}
free(snapshot_path);
return false;
}
} // namespace bin
} // namespace dart

View file

@ -1,32 +0,0 @@
// 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.
#ifndef RUNTIME_BIN_DARTDEV_UTILS_H_
#define RUNTIME_BIN_DARTDEV_UTILS_H_
#include "platform/globals.h"
namespace dart {
namespace bin {
class DartDevUtils {
public:
// Returns true if there does not exist a file at |script_uri| or the URI is
// not an HTTP resource.
static bool ShouldParseCommand(const char* script_uri);
// Returns true if we were successfully able to find the DartDev snapshot.
// Returns false if we were unable to find the snapshot, in which case the
// VM should exit.
static bool TryResolveDartDevSnapshotPath(char** script_name);
private:
DISALLOW_ALLOCATION();
DISALLOW_IMPLICIT_CONSTRUCTORS(DartDevUtils);
};
} // namespace bin
} // namespace dart
#endif // RUNTIME_BIN_DARTDEV_UTILS_H_

View file

@ -12,11 +12,9 @@
#include "bin/namespace.h"
#include "bin/platform.h"
#include "bin/utils.h"
#include "include/dart_api.h"
#include "include/dart_native_api.h"
#include "include/dart_tools_api.h"
#include "platform/assert.h"
#include "platform/globals.h"
#include "platform/memory_sanitizer.h"
@ -68,6 +66,29 @@ static bool IsWindowsHost() {
#endif // defined(HOST_OS_WINDOWS)
}
Dart_Handle CommandLineOptions::CreateRuntimeOptions() {
Dart_Handle string_type = DartUtils::GetDartType("dart:core", "String");
if (Dart_IsError(string_type)) {
return string_type;
}
Dart_Handle dart_arguments =
Dart_NewListOfTypeFilled(string_type, Dart_EmptyString(), count_);
if (Dart_IsError(dart_arguments)) {
return dart_arguments;
}
for (int i = 0; i < count_; i++) {
Dart_Handle argument_value = DartUtils::NewString(GetArgument(i));
if (Dart_IsError(argument_value)) {
return argument_value;
}
Dart_Handle result = Dart_ListSetAt(dart_arguments, i, argument_value);
if (Dart_IsError(result)) {
return result;
}
}
return dart_arguments;
}
int64_t DartUtils::GetIntegerValue(Dart_Handle value_obj) {
int64_t value = 0;
Dart_Handle result = Dart_IntegerToInt64(value_obj, &value);

View file

@ -55,6 +55,8 @@ class CommandLineOptions {
arguments_ = NULL;
}
void Reset() { count_ = 0; }
int count() const { return count_; }
int max_count() const { return max_count_; }
const char** arguments() const { return arguments_; }
@ -80,6 +82,8 @@ class CommandLineOptions {
}
}
Dart_Handle CreateRuntimeOptions();
void operator delete(void* pointer) { abort(); }
private:

View file

@ -5,16 +5,13 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory>
#include "include/bin/dart_io_api.h"
#include "include/dart_api.h"
#include "include/dart_embedder_api.h"
#include "include/dart_tools_api.h"
#include <memory>
#include "bin/builtin.h"
#include "bin/console.h"
#include "bin/crashpad.h"
#include "bin/dartdev_isolate.h"
#include "bin/dartutils.h"
#include "bin/dfe.h"
#include "bin/error_exit.h"
@ -31,6 +28,10 @@
#include "bin/thread.h"
#include "bin/utils.h"
#include "bin/vmservice_impl.h"
#include "include/bin/dart_io_api.h"
#include "include/dart_api.h"
#include "include/dart_embedder_api.h"
#include "include/dart_tools_api.h"
#include "platform/globals.h"
#include "platform/growable_array.h"
#include "platform/hashmap.h"
@ -74,30 +75,6 @@ static Dart_Isolate main_isolate = NULL;
static void ReadFile(const char* filename, uint8_t** buffer, intptr_t* size);
static Dart_Handle CreateRuntimeOptions(CommandLineOptions* options) {
int options_count = options->count();
Dart_Handle string_type = DartUtils::GetDartType("dart:core", "String");
if (Dart_IsError(string_type)) {
return string_type;
}
Dart_Handle dart_arguments =
Dart_NewListOfTypeFilled(string_type, Dart_EmptyString(), options_count);
if (Dart_IsError(dart_arguments)) {
return dart_arguments;
}
for (int i = 0; i < options_count; i++) {
Dart_Handle argument_value = DartUtils::NewString(options->GetArgument(i));
if (Dart_IsError(argument_value)) {
return argument_value;
}
Dart_Handle result = Dart_ListSetAt(dart_arguments, i, argument_value);
if (Dart_IsError(result)) {
return result;
}
}
return dart_arguments;
}
#define SAVE_ERROR_AND_EXIT(result) \
*error = Utils::StrDup(Dart_GetError(result)); \
if (Dart_IsCompilationError(result)) { \
@ -405,6 +382,15 @@ static Dart_Isolate IsolateSetupHelper(Dart_Isolate isolate,
CHECK_RESULT(result);
}
// Disable pausing the DartDev isolate on start and exit.
const char* isolate_name = nullptr;
result = Dart_StringToCString(Dart_DebugName(), &isolate_name);
CHECK_RESULT(result);
if (strstr(isolate_name, "dartdev") != nullptr) {
Dart_SetShouldPauseOnStart(false);
Dart_SetShouldPauseOnExit(false);
}
// Make the isolate runnable so that it is ready to handle messages.
Dart_ExitScope();
Dart_ExitIsolate();
@ -941,8 +927,8 @@ bool RunMainIsolate(const char* script_name, CommandLineOptions* dart_options) {
// initial startup message.
const intptr_t kNumIsolateArgs = 2;
Dart_Handle isolate_args[kNumIsolateArgs];
isolate_args[0] = main_closure; // entryPoint
isolate_args[1] = CreateRuntimeOptions(dart_options); // args
isolate_args[0] = main_closure; // entryPoint
isolate_args[1] = dart_options->CreateRuntimeOptions(); // args
Dart_Handle isolate_lib =
Dart_LookupLibrary(Dart_NewStringFromCString("dart:isolate"));
@ -1011,7 +997,7 @@ static Dart_GetVMServiceAssetsArchive GetVMServiceAssetsArchiveCallback = NULL;
#endif // !defined(PRODUCT)
void main(int argc, char** argv) {
char* script_name;
char* script_name = nullptr;
const int EXTRA_VM_ARGUMENTS = 10;
CommandLineOptions vm_options(argc + EXTRA_VM_ARGUMENTS);
CommandLineOptions dart_options(argc + EXTRA_VM_ARGUMENTS);
@ -1101,9 +1087,6 @@ void main(int argc, char** argv) {
}
}
// At this point, script_name now points to either a script or a snapshot
// determined by DartDevUtils above.
DartUtils::SetEnvironment(Options::environment());
if (Options::suppress_core_dump()) {
@ -1114,19 +1097,28 @@ void main(int argc, char** argv) {
Loader::InitOnce();
if (app_snapshot == nullptr) {
// For testing purposes we add a flag to debug-mode to use the
// in-memory ELF loader.
const bool force_load_elf_from_memory =
false DEBUG_ONLY(|| Options::force_load_elf_from_memory());
app_snapshot =
Snapshot::TryReadAppSnapshot(script_name, force_load_elf_from_memory);
}
if (app_snapshot != nullptr) {
vm_run_app_snapshot = true;
app_snapshot->SetBuffers(&vm_snapshot_data, &vm_snapshot_instructions,
&app_isolate_snapshot_data,
&app_isolate_snapshot_instructions);
auto try_load_snapshots_lambda = [&](void) -> void {
if (app_snapshot == nullptr) {
// For testing purposes we add a flag to debug-mode to use the
// in-memory ELF loader.
const bool force_load_elf_from_memory =
false DEBUG_ONLY(|| Options::force_load_elf_from_memory());
app_snapshot =
Snapshot::TryReadAppSnapshot(script_name, force_load_elf_from_memory);
}
if (app_snapshot != nullptr) {
vm_run_app_snapshot = true;
app_snapshot->SetBuffers(&vm_snapshot_data, &vm_snapshot_instructions,
&app_isolate_snapshot_data,
&app_isolate_snapshot_instructions);
}
};
// At this point, script_name now points to a script if DartDev is disabled
// or a valid file path was provided as the first non-flag argument.
// Otherwise, script_name can be NULL if DartDev should be run.
if (script_name != nullptr) {
try_load_snapshots_lambda();
}
if (Options::gen_snapshot_kind() == kAppJIT) {
@ -1159,16 +1151,18 @@ void main(int argc, char** argv) {
// Note: must read platform only *after* VM flags are parsed because
// they might affect how the platform is loaded.
#if !defined(DART_PRECOMPILED_RUNTIME)
dfe.Init(Options::target_abi_version());
uint8_t* application_kernel_buffer = NULL;
intptr_t application_kernel_buffer_size = 0;
dfe.ReadScript(script_name, &application_kernel_buffer,
&application_kernel_buffer_size);
if (application_kernel_buffer != NULL) {
// Since we loaded the script anyway, save it.
dfe.set_application_kernel_buffer(application_kernel_buffer,
application_kernel_buffer_size);
Options::dfe()->set_use_dfe();
if (script_name != nullptr) {
dfe.Init(Options::target_abi_version());
uint8_t* application_kernel_buffer = NULL;
intptr_t application_kernel_buffer_size = 0;
dfe.ReadScript(script_name, &application_kernel_buffer,
&application_kernel_buffer_size);
if (application_kernel_buffer != NULL) {
// Since we loaded the script anyway, save it.
dfe.set_application_kernel_buffer(application_kernel_buffer,
application_kernel_buffer_size);
Options::dfe()->set_use_dfe();
}
}
#endif
@ -1208,10 +1202,29 @@ void main(int argc, char** argv) {
&ServiceStreamCancelCallback);
Dart_SetFileModifiedCallback(&FileModifiedCallback);
Dart_SetEmbedderInformationCallback(&EmbedderInformationCallback);
bool ran_dart_dev = false;
bool should_run_user_program = true;
#if !defined(DART_PRECOMPILED_RUNTIME)
if (DartDevIsolate::should_run_dart_dev() && !Options::disable_dart_dev() &&
Options::gen_snapshot_kind() == SnapshotKind::kNone) {
DartDevIsolate::DartDev_Result dartdev_result = DartDevIsolate::RunDartDev(
CreateIsolateGroupAndSetup, Options::packages_file(), &script_name,
&dart_options);
ASSERT(dartdev_result != DartDevIsolate::DartDev_Result_Unknown);
ran_dart_dev = true;
should_run_user_program =
(dartdev_result == DartDevIsolate::DartDev_Result_Run);
if (should_run_user_program) {
try_load_snapshots_lambda();
}
}
#endif // !defined(DART_PRECOMPILED_RUNTIME)
// Run the main isolate until we aren't told to restart.
while (RunMainIsolate(script_name, &dart_options)) {
Syslog::PrintErr("Restarting VM\n");
if (should_run_user_program) {
// Run the main isolate until we aren't told to restart.
while (RunMainIsolate(script_name, &dart_options)) {
Syslog::PrintErr("Restarting VM\n");
}
}
// Terminate process exit-code handler.
@ -1227,6 +1240,9 @@ void main(int argc, char** argv) {
delete app_snapshot;
free(app_script_uri);
if (ran_dart_dev && script_name != nullptr) {
free(script_name);
}
// Free copied argument strings if converted.
if (argv_converted) {

View file

@ -9,7 +9,7 @@
#include <string.h>
#include "bin/abi_version.h"
#include "bin/dartdev_utils.h"
#include "bin/dartdev_isolate.h"
#include "bin/error_exit.h"
#include "bin/options.h"
#include "bin/platform.h"
@ -35,9 +35,6 @@ static const char* kSnapshotKindNames[] = {
NULL,
};
static const char* kEnableExperiment1 = "--enable-experiment";
static const char* kEnableExperiment2 = "--enable_experiment";
SnapshotKind Options::gen_snapshot_kind_ = kNone;
bool Options::enable_vm_service_ = false;
MallocGrowableArray<const char*> Options::enabled_experiments_ =
@ -381,27 +378,17 @@ bool Options::ProcessEnableExperimentOption(const char* arg,
return false;
}
vm_options->AddArgument(arg);
Utils::CStringUniquePtr tmp = Utils::CreateCStringUniquePtr(
Utils::StrDup(value));
Utils::CStringUniquePtr tmp =
Utils::CreateCStringUniquePtr(Utils::StrDup(value));
char* save_ptr; // Needed for strtok_r.
char* token = strtok_r(const_cast<char*>(tmp.get()), ",", &save_ptr);
while (token != NULL) {
enabled_experiments_.Add(token);
enabled_experiments_.Add(Utils::StrDup(token));
token = strtok_r(NULL, ",", &save_ptr);
}
return true;
}
static void ResolveDartDevSnapshotPath(const char* script,
char** snapshot_path) {
if (!DartDevUtils::TryResolveDartDevSnapshotPath(snapshot_path)) {
Syslog::PrintErr(
"Could not find DartDev snapshot and '%s' is not a valid script.\n",
script);
Platform::Exit(kErrorExitCode);
}
}
int Options::ParseArguments(int argc,
char** argv,
bool vm_run_app_snapshot,
@ -476,17 +463,11 @@ int Options::ParseArguments(int argc,
}
}
if (!Options::disable_dart_dev()) {
// Don't start the VM service for the DartDev process. Without doing a
// second pass over the argument list to explicitly check for
// --disable-dart-dev, this is the earliest we can assume we know whether
// or not we're running with DartDev enabled.
vm_service_server_port_ = INVALID_VM_SERVICE_SERVER_PORT;
vm_service_server_ip_ = DEFAULT_VM_SERVICE_SERVER_IP;
}
#if !defined(DART_PRECOMPILED_RUNTIME)
Options::dfe()->set_use_dfe();
#else
// DartDev is not supported in AOT.
Options::disable_dart_dev_ = true;
#endif // !defined(DART_PRECOMPILED_RUNTIME)
if (Options::deterministic()) {
// Both an embedder and VM flag.
@ -503,24 +484,31 @@ int Options::ParseArguments(int argc,
// The arguments to the VM are at positions 1 through i-1 in argv.
Platform::SetExecutableArguments(i, argv);
bool implicitly_use_dart_dev = false;
bool run_script = false;
int script_or_cmd_index = -1;
// Get the script name.
if (i < argc) {
#if !defined(DART_PRECOMPILED_RUNTIME)
// If the script name is a valid file or a URL, we'll run the script
// directly. Otherwise, this might be a DartDev command and we need to try
// to find the DartDev snapshot so we can forward the command and its
// arguments.
bool is_potential_file_path = !DartDevUtils::ShouldParseCommand(argv[i]);
bool is_potential_file_path = !DartDevIsolate::ShouldParseCommand(argv[i]);
bool implicitly_use_dart_dev = false;
#else
bool is_potential_file_path = true;
#endif // !defined(DART_PRECOMPILED_RUNTIME)
script_or_cmd_index = i;
if (Options::disable_dart_dev() ||
(is_potential_file_path && !enable_vm_service_)) {
*script_name = Utils::StrDup(argv[i]);
run_script = true;
i++;
} else {
ResolveDartDevSnapshotPath(argv[i], script_name);
}
#if !defined(DART_PRECOMPILED_RUNTIME)
else { // NOLINT
DartDevIsolate::set_should_run_dart_dev(true);
}
// Handle the special case where the user is running a Dart program without
// using a DartDev command and wants to use the VM service. Here we'll run
@ -530,10 +518,13 @@ int Options::ParseArguments(int argc,
implicitly_use_dart_dev = true;
dart_options->AddArgument("run");
}
} else if (!Options::disable_dart_dev() &&
((Options::help_option() && !Options::verbose_option()) ||
(argc == 1)) &&
DartDevUtils::TryResolveDartDevSnapshotPath(script_name)) {
#endif // !defined(DART_PRECOMPILED_RUNTIME)
}
#if !defined(DART_PRECOMPILED_RUNTIME)
else if (!Options::disable_dart_dev() && // NOLINT
((Options::help_option() && !Options::verbose_option()) ||
(argc == 1))) {
DartDevIsolate::set_should_run_dart_dev(true);
// Let DartDev handle the default help message.
dart_options->AddArgument("help");
return 0;
@ -541,7 +532,7 @@ int Options::ParseArguments(int argc,
(enable_dartdev_analytics || disable_dartdev_analytics)) {
// The analytics flags are a special case as we don't have a target script
// or DartDev command but we still want to launch DartDev.
ResolveDartDevSnapshotPath(argv[i], script_name);
DartDevIsolate::set_should_run_dart_dev(true);
if (enable_dartdev_analytics) {
dart_options->AddArgument("--enable-analytics");
@ -550,89 +541,68 @@ int Options::ParseArguments(int argc,
dart_options->AddArgument("--disable-analytics");
}
return 0;
} else {
return -1;
}
#endif // !defined(DART_PRECOMPILED_RUNTIME)
else { // NOLINT
return -1;
}
const char** vm_argv = temp_vm_options.arguments();
int vm_argc = temp_vm_options.count();
if (Options::disable_dart_dev() || run_script) {
// Only populate the VM options if we're not running with DartDev.
vm_options->AddArguments(vm_argv, vm_argc);
} else if (implicitly_use_dart_dev) {
// If we're using DartDev implicitly (e.g., dart --observe foo.dart), we
// want to forward all the VM arguments to the spawned process to ensure
// the program behaves as the user expects even though we're running
// through DartDev without their knowledge.
dart_options->AddArguments(const_cast<const char**>(argv + 1),
script_or_cmd_index - 1);
} else if (i > 1) {
// If we're running with DartDev, we're going to ignore the VM options for
// this VM instance and print a warning.
vm_options->AddArguments(vm_argv, vm_argc);
int num_experiment_flags = 0;
if (!enabled_experiments_.is_empty()) {
for (intptr_t j = 1; j < script_or_cmd_index; ++j) {
if ((strstr(argv[j], kEnableExperiment1) != nullptr) ||
(strstr(argv[j], kEnableExperiment2) != nullptr)) {
++num_experiment_flags;
}
}
}
if (num_experiment_flags + 1 != script_or_cmd_index) {
Syslog::PrintErr(
"Warning: The following flags were passed as VM options and are "
"being "
"ignored:\n\n");
for (int j = 1; j < script_or_cmd_index; ++j) {
if ((strstr(argv[j], kEnableExperiment1) == nullptr) &&
(strstr(argv[j], kEnableExperiment2) == nullptr)) {
Syslog::PrintErr(" %s\n", argv[j]);
}
}
Syslog::PrintErr(
"\nThese flags should be passed after the dart command (e.g., 'dart "
"run --enable-asserts foo.dart' instead of 'dart --enable-asserts "
"run "
"foo.dart').\n\n");
}
}
if (!enabled_experiments_.is_empty() &&
!(Options::disable_dart_dev() || run_script)) {
#if !defined(DART_PRECOMPILED_RUNTIME)
if (!enabled_experiments_.is_empty()) {
intptr_t num_experiments = enabled_experiments_.length();
int option_size = strlen(kEnableExperiment1) + 1;
for (intptr_t i = 0; i < num_experiments; ++i) {
const char* flag = enabled_experiments_.At(i);
option_size += strlen(flag);
if (i + 1 != num_experiments) {
// Account for comma if there's more experiments to add.
++option_size;
if (!(Options::disable_dart_dev() || run_script)) {
const char* kEnableExperiment = "--enable-experiment=";
int option_size = strlen(kEnableExperiment);
for (intptr_t i = 0; i < num_experiments; ++i) {
const char* flag = enabled_experiments_.At(i);
option_size += strlen(flag);
if (i + 1 != num_experiments) {
// Account for comma if there's more experiments to add.
++option_size;
}
}
// Make room for null terminator
++option_size;
char* enabled_experiments_arg = new char[option_size];
int offset = snprintf(enabled_experiments_arg, option_size, "%s",
kEnableExperiment);
for (intptr_t i = 0; i < num_experiments; ++i) {
const char* flag = enabled_experiments_.At(i);
const char* kFormat = (i + 1 != num_experiments) ? "%s," : "%s";
offset += snprintf(enabled_experiments_arg + offset,
option_size - offset, kFormat, flag);
free(const_cast<char*>(flag));
ASSERT(offset < option_size);
}
DartDevIsolate::set_should_run_dart_dev(true);
dart_options->AddArgument(enabled_experiments_arg);
} else {
for (intptr_t i = 0; i < num_experiments; ++i) {
free(const_cast<char*>(enabled_experiments_.At(i)));
}
}
// Make room for null terminator
++option_size;
char* enabled_experiments_arg = new char[option_size];
int offset = snprintf(enabled_experiments_arg, option_size,
"%s=", kEnableExperiment1);
for (intptr_t i = 0; i < num_experiments; ++i) {
const char* flag = enabled_experiments_.At(i);
const char* kFormat = (i + 1 != num_experiments) ? "%s," : "%s";
offset += snprintf(enabled_experiments_arg + offset, option_size - offset,
kFormat, flag);
ASSERT(offset < option_size);
}
dart_options->AddArgument(enabled_experiments_arg);
}
#endif // !defined(DART_PRECOMPILED_RUNTIME)
// Parse out options to be passed to dart main.
while (i < argc) {
if (!run_script) {
OptionProcessor::TryProcess(argv[i], vm_options);
}
dart_options->AddArgument(argv[i]);
i++;
}
if (!Options::disable_dart_dev() && enable_vm_service_) {
dart_options->AddArgument("--launch-dds");
}
// Verify consistency of arguments.
// snapshot_depfile is an alias for depfile. Passing them both is an error.

View file

@ -18,7 +18,11 @@ class _HtmlWebSocket implements CommonWebSocket {
void connect(String address, void onOpen(), void onMessage(dynamic data),
void onError(), void onClose()) {
_webSocket = new WebSocket(address);
// The VM service will attempt to redirect our websocket connection request
// to DDS, but the dart:html WebSocket doesn't follow redirects. If the
// 'implicit-redirect' protocol is provided, the VM service will manually
// forward traffic to DDS.
_webSocket = new WebSocket(address, ['implicit-redirect']);
_webSocket.onClose.listen((CloseEvent) => onClose());
_webSocket.onError.listen((Event) => onError());
_webSocket.onOpen.listen((Event) => onOpen());

View file

@ -1117,6 +1117,9 @@ void Service::HandleEvent(ServiceEvent* event) {
}
// Ignore events when no one is listening to the event stream.
return;
} else if (event->stream_info() != NULL &&
FLAG_warn_on_pause_with_no_debugger && event->IsPause()) {
ReportPauseOnConsole(event);
}
if (!ServiceIsolate::IsRunning()) {
return;

View file

@ -47,6 +47,7 @@ declare_args() {
# ........analysis_server.dart.snapshot
# ........dart2js.dart.snapshot
# ........dartanalyzer.dart.snapshot
# ........dds.dart.snapshot
# ........dartdev.dart.snapshot
# ........dartdoc.dart.snapshot
# ........dartfmt.dart.snapshot
@ -133,6 +134,10 @@ _platform_sdk_snapshots = [
"dartfmt",
"../utils/dartfmt",
],
[
"dds",
"../utils/dds:generate_dds_snapshot",
],
[
"pub",
"../utils/pub",
@ -182,6 +187,10 @@ _full_sdk_snapshots = [
"dartfmt",
"../utils/dartfmt",
],
[
"dds",
"../utils/dds:generate_dds_snapshot",
],
[
"kernel_worker",
"../utils/bazel:kernel_worker",

View file

@ -354,14 +354,41 @@ class Server {
final String path = result;
if (path == WEBSOCKET_PATH) {
final subprotocols = request.headers['sec-websocket-protocol'];
if (acceptNewWebSocketConnections) {
WebSocketTransformer.upgrade(request,
protocolSelector:
subprotocols == null ? null : (_) => 'implicit-redirect',
compression: CompressionOptions.compressionOff)
.then((WebSocket webSocket) {
WebSocketClient(webSocket, _service);
});
} else {
// Forward the websocket connection request to DDS.
// The Javascript WebSocket implementation doesn't like websocket
// connection requests being redirected. Instead of redirecting, we'll
// just forward the connection manually if 'implicit-redirect' is
// provided as a protocol.
if (subprotocols != null) {
if (subprotocols.contains('implicit-redirect')) {
WebSocketTransformer.upgrade(request,
protocolSelector: (_) => 'implicit-redirect',
compression: CompressionOptions.compressionOff)
.then((WebSocket webSocket) async {
final ddsWs = await WebSocket.connect(
_service.ddsUri!.replace(scheme: 'ws').toString());
ddsWs.addStream(webSocket);
webSocket.addStream(ddsWs);
webSocket.done.then((_) {
ddsWs.close();
});
ddsWs.done.then((_) {
webSocket.close();
});
});
return;
}
}
request.response.redirect(_service.ddsUri!);
}
return;

View file

@ -4,8 +4,6 @@
import 'dart:io';
import 'package:async_helper/async_helper.dart';
import 'package:path/path.dart' as p;
import 'package:expect/expect.dart';
Future<void> main() async {

View file

@ -4,8 +4,6 @@
import 'dart:io';
import 'package:async_helper/async_helper.dart';
import 'package:path/path.dart' as p;
import 'package:expect/expect.dart';
Future<void> main() async {

View file

@ -4,8 +4,6 @@
import 'dart:io';
import 'package:async_helper/async_helper.dart';
import 'package:path/path.dart' as p;
import 'package:expect/expect.dart';
Future<void> main() async {

View file

@ -4,8 +4,6 @@
import 'dart:io';
import 'package:async_helper/async_helper.dart';
import 'package:path/path.dart' as p;
import 'package:expect/expect.dart';
Future<void> main() async {

View file

@ -17,14 +17,6 @@ dartfix_files = exec_script("../../tools/list_dart_files.py",
rebase_path("../../pkg/dartfix"),
],
"list lines")
dds_files = exec_script("../../tools/list_dart_files.py",
[
"absolute",
rebase_path("../../pkg/dds"),
],
"list lines")
group("dartdev") {
deps = [ ":copy_dartdev_snapshot" ]
}
@ -39,6 +31,6 @@ copy("copy_dartdev_snapshot") {
application_snapshot("generate_dartdev_snapshot") {
main_dart = "../../pkg/dartdev/bin/dartdev.dart"
training_args = [ "--help" ]
inputs = dartdev_files + dartfix_files + dds_files
inputs = dartdev_files + dartfix_files
output = "$root_gen_dir/dartdev.dart.snapshot"
}

30
utils/dds/BUILD.gn Normal file
View file

@ -0,0 +1,30 @@
# 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("../application_snapshot.gni")
dds_files = exec_script("../../tools/list_dart_files.py",
[
"absolute",
rebase_path("../../pkg/dds"),
],
"list lines")
group("dds") {
deps = [ ":copy_dds_snapshot" ]
}
copy("copy_dds_snapshot") {
visibility = [ ":dds" ]
deps = [ ":generate_dds_snapshot" ]
sources = [ "$root_gen_dir/dds.dart.snapshot" ]
outputs = [ "$root_out_dir/dds.dart.snapshot" ]
}
application_snapshot("generate_dds_snapshot") {
main_dart = "../../pkg/dds/bin/dds.dart"
training_args = []
inputs = dds_files
output = "$root_gen_dir/dds.dart.snapshot"
}