3bc3a84fd0
- Support interface types without generic parameters in type tests. - Add '|' symbol to recipes to separate the library identifier from the type. This is used in the modular setting where types are not guaranteed to have a unique name. - Add a runtime method to strip library identifiers when printing type names. - Add a runtime method to retrieve original Dart type name from constructors. Issue: https://github.com/dart-lang/sdk/issues/48585 Change-Id: Ib2a69c29177ebb7c582bff3246ee985666cd5919 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/243362 Commit-Queue: Nicholas Shahan <nshahan@google.com> Reviewed-by: Mayank Patke <fishythefish@google.com> Reviewed-by: Anna Gringauze <annagrin@google.com> Reviewed-by: Mark Zhou <markzipan@google.com> Reviewed-by: Sigmund Cherem <sigmund@google.com>
423 lines
15 KiB
Dart
Executable File
423 lines
15 KiB
Dart
Executable File
#!/usr/bin/env dart
|
|
// Copyright (c) 2018, 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.
|
|
|
|
/// Compiles code with DDC and runs the resulting code with either node or
|
|
/// chrome.
|
|
///
|
|
/// The first script supplied should be the one with `main()`.
|
|
///
|
|
/// Saves the output in the same directory as the sources for convenient
|
|
/// inspection, modification or rerunning the code.
|
|
|
|
import 'dart:io';
|
|
|
|
import 'package:args/args.dart' show ArgParser;
|
|
import 'package:dev_compiler/src/compiler/js_names.dart'
|
|
as js_names;
|
|
import 'package:path/path.dart' as p;
|
|
|
|
enum NullSafety { strict, weak, disabled }
|
|
|
|
void main(List<String> args) async {
|
|
void printUsage() {
|
|
print('Usage: ddb [options] <dart-script-file>\n');
|
|
print('Compiles <dart-script-file> with the dev_compiler and runs it on a '
|
|
'JS platform.\n');
|
|
}
|
|
|
|
// Parse flags.
|
|
var parser = ArgParser(usageLineLength: 80)
|
|
..addOption('arch',
|
|
abbr: 'a',
|
|
help: "Target platform's machine architecture.",
|
|
allowed: ['x64', 'arm64'])
|
|
..addOption('binary', abbr: 'b', help: 'Runtime binary path.')
|
|
..addOption('compile-vm-options',
|
|
help: 'DART_VM_OPTIONS for the compilation VM.')
|
|
..addFlag('debug',
|
|
abbr: 'd',
|
|
help: 'Use current source instead of built SDK.',
|
|
defaultsTo: false)
|
|
..addMultiOption('enable-experiment',
|
|
help: 'Run with specified experiments enabled.')
|
|
..addFlag('help', abbr: 'h', help: 'Display this message.')
|
|
..addOption('mode',
|
|
help: 'Option to (compile|run|all). Default is all (compile and run).',
|
|
allowed: ['compile', 'run', 'all'],
|
|
defaultsTo: 'all')
|
|
..addFlag('sound-null-safety',
|
|
help: 'Compile for sound null safety at runtime. Passed through to the '
|
|
'DDC binary. Defaults to false.',
|
|
defaultsTo: false)
|
|
..addFlag('emit-debug-symbols',
|
|
help: 'Pass through flag for DDC, emits debug symbols file along with '
|
|
'the compiled module.',
|
|
defaultsTo: false)
|
|
..addFlag('null-assertions',
|
|
help: 'Run with assertions that values passed to non-nullable method '
|
|
'parameters are not null.',
|
|
defaultsTo: false)
|
|
..addFlag('native-null-assertions',
|
|
help: 'Run with assertions on non-nullable values returned from native '
|
|
'APIs.',
|
|
defaultsTo: true,
|
|
negatable: true)
|
|
..addFlag('weak-null-safety-errors',
|
|
help: 'Treat weak null safety warnings as errors.', defaultsTo: false)
|
|
..addFlag('canary',
|
|
help: 'Enable all compiler features under active development.',
|
|
defaultsTo: false)
|
|
..addFlag('observe',
|
|
help:
|
|
'Run the compiler in the Dart VM with --observe. Implies --debug.',
|
|
defaultsTo: false)
|
|
..addOption('out', help: 'Output file.')
|
|
..addOption('packages', help: 'Where to find a package spec file.')
|
|
..addOption('port',
|
|
abbr: 'p',
|
|
help: 'Run with the corresponding chrome/V8 debugging port open.',
|
|
defaultsTo: '9222')
|
|
..addOption('runtime',
|
|
abbr: 'r',
|
|
help: 'Platform to run on (node|d8|chrome). Default is node.',
|
|
allowed: ['node', 'd8', 'chrome'],
|
|
defaultsTo: 'node')
|
|
..addFlag('summarize-text',
|
|
help: 'Emit API summary in a .js.txt file.', defaultsTo: false)
|
|
..addMultiOption('summary',
|
|
abbr: 's',
|
|
help: 'summary file(s) of imported libraries, optionally with module '
|
|
'import path: -s path.sum=js/import/path')
|
|
..addFlag('verbose',
|
|
abbr: 'v',
|
|
help: 'Echos the commands, arguments, and environment this script is '
|
|
'running.',
|
|
defaultsTo: false)
|
|
..addOption('vm-service-port',
|
|
help: 'Specify the observatory port. Implied --observe.');
|
|
|
|
var options = parser.parse(args);
|
|
if (options['help'] as bool) {
|
|
printUsage();
|
|
print('Available options:');
|
|
print(parser.usage);
|
|
exit(0);
|
|
}
|
|
if (options.rest.length != 1) {
|
|
print('Dart script file required.\n');
|
|
printUsage();
|
|
exit(1);
|
|
}
|
|
var arch = options['arch'] as String?;
|
|
var debug = options['debug'] as bool ||
|
|
options['observe'] as bool ||
|
|
options.wasParsed('vm-service-port');
|
|
var summarizeText = options['summarize-text'] as bool;
|
|
var binary = options['binary'] as String?;
|
|
var experiments = options['enable-experiment'] as List<String>;
|
|
var summaries = options['summary'] as List<String>;
|
|
var port = int.parse(options['port'] as String);
|
|
var mode = options['mode'] as String;
|
|
var compile = mode == 'compile' || mode == 'all';
|
|
var run = mode == 'run' || mode == 'all';
|
|
var verbose = options['verbose'] as bool;
|
|
var soundNullSafety = options['sound-null-safety'] as bool;
|
|
var emitDebugSymbols = options['emit-debug-symbols'] as bool;
|
|
var nonNullAsserts = options['null-assertions'] as bool;
|
|
var nativeNonNullAsserts = options['native-null-assertions'] as bool;
|
|
var weakNullSafetyErrors = options['weak-null-safety-errors'] as bool;
|
|
var canaryFeatures = options['canary'] as bool;
|
|
var entry = p.canonicalize(options.rest.first);
|
|
var out = (options['out'] as String?) ?? p.setExtension(entry, '.js');
|
|
var libRoot = p.dirname(entry);
|
|
var basename = p.basenameWithoutExtension(entry);
|
|
var libname =
|
|
js_names.pathToJSIdentifier(p.relative(p.withoutExtension(entry)));
|
|
|
|
// By default (no `-d`), we use the `dartdevc` binary on the user's path to
|
|
// compute the SDK we use for execution. I.e., we assume that `dart` is
|
|
// under `$DART_SDK/bin/dart` and use that to find `dartdevc` and related
|
|
// artifacts. In this mode, this script can run against any installed SDK.
|
|
// If you want to run against a freshly built SDK, that must be first on
|
|
// your path.
|
|
var dartBinary = Platform.resolvedExecutable;
|
|
var dartSdk = p.dirname(p.dirname(dartBinary));
|
|
|
|
// In debug mode (`-d`), we run from the `pkg/dev_compiler` sources. We
|
|
// determine the location via this actual script (i.e., `-d` assumes
|
|
// this script remains under to `tool` sub-directory).
|
|
var toolPath =
|
|
Platform.script.normalizePath().toFilePath(windows: Platform.isWindows);
|
|
var ddcPath = p.dirname(p.dirname(toolPath));
|
|
var dartCheckoutPath = p.dirname(p.dirname(ddcPath));
|
|
|
|
/// Runs the [command] with [args] in [environment].
|
|
///
|
|
/// Will echo the commands to the console before running them when running in
|
|
/// `verbose` mode.
|
|
Future<Process> startProcess(String name, String command, List<String> args,
|
|
[Map<String, String> environment = const {}]) {
|
|
if (verbose) {
|
|
print('Running $name:\n$command ${args.join(' ')}\n');
|
|
if (environment.isNotEmpty) {
|
|
var environmentVariables =
|
|
environment.entries.map((e) => '${e.key}: ${e.value}').join('\n');
|
|
print('With Environment:\n$environmentVariables\n');
|
|
}
|
|
}
|
|
return Process.start(command, args,
|
|
mode: ProcessStartMode.inheritStdio, environment: environment);
|
|
}
|
|
|
|
Future<void> runDdc(List<String> ddcArgs) async {
|
|
var observe =
|
|
options.wasParsed('vm-service-port') || options['observe'] as bool;
|
|
var vmServicePort = options.wasParsed('vm-service-port')
|
|
? '=${options['vm-service-port']}'
|
|
: '';
|
|
var vmOptions = options['compile-vm-options'] as String?;
|
|
var args = <String>[
|
|
...?vmOptions?.split(' '),
|
|
if (debug) ...[
|
|
if (observe) ...[
|
|
'--enable-vm-service$vmServicePort',
|
|
'--pause-isolates-on-start',
|
|
],
|
|
'--enable-asserts',
|
|
// Use unbuilt script. This only works from a source checkout.
|
|
p.join(ddcPath, 'bin', 'dartdevc.dart'),
|
|
] else
|
|
// Use built snapshot.
|
|
p.join(dartSdk, 'bin', 'snapshots', 'dartdevc.dart.snapshot'),
|
|
...ddcArgs,
|
|
];
|
|
var process = await startProcess('DDC', dartBinary, args);
|
|
if (await process.exitCode != 0) exit(await process.exitCode);
|
|
}
|
|
|
|
String mod;
|
|
var chrome = false;
|
|
var node = false;
|
|
var d8 = false;
|
|
var runtime = options['runtime'] as String?;
|
|
switch (runtime) {
|
|
case 'node':
|
|
node = true;
|
|
mod = 'common';
|
|
break;
|
|
case 'd8':
|
|
d8 = true;
|
|
mod = 'legacy';
|
|
break;
|
|
case 'chrome':
|
|
chrome = true;
|
|
mod = 'amd';
|
|
break;
|
|
default:
|
|
throw Exception('Unexpected runtime: $runtime');
|
|
}
|
|
|
|
var sdkRoot = p.dirname(p.dirname(ddcPath));
|
|
var buildDir = resolveBuildDir(sdkRoot, arch);
|
|
var requirePath = p.join(sdkRoot, 'third_party', 'requirejs');
|
|
var sdkOutlineDill = p.join(buildDir,
|
|
soundNullSafety ? 'ddc_outline_sound.dill' : 'ddc_outline.dill');
|
|
var suffix = soundNullSafety ? p.join('sound', mod) : p.join('kernel', mod);
|
|
var sdkJsPath = p.join(buildDir, 'gen', 'utils', 'dartdevc', suffix);
|
|
|
|
// Print an initial empty line to separate the invocation from the output.
|
|
if (verbose) {
|
|
print('');
|
|
}
|
|
|
|
if (compile) {
|
|
var ddcArgs = [
|
|
if (summarizeText) '--summarize-text',
|
|
'--modules=$mod',
|
|
'--dart-sdk-summary=$sdkOutlineDill',
|
|
for (var summary in summaries) '--summary=$summary',
|
|
for (var experiment in experiments) '--enable-experiment=$experiment',
|
|
if (soundNullSafety) '--sound-null-safety',
|
|
if (options['packages'] != null) '--packages=${options['packages']}',
|
|
if (emitDebugSymbols) '--emit-debug-symbols',
|
|
if (canaryFeatures) '--canary',
|
|
'-o',
|
|
out,
|
|
entry
|
|
];
|
|
await runDdc(ddcArgs);
|
|
}
|
|
|
|
if (run) {
|
|
if (chrome) {
|
|
String chromeBinary;
|
|
if (binary != null) {
|
|
chromeBinary = binary;
|
|
} else if (Platform.isWindows) {
|
|
chromeBinary =
|
|
'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe';
|
|
} else if (Platform.isMacOS) {
|
|
chromeBinary =
|
|
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
|
|
} else {
|
|
// Assume Linux
|
|
chromeBinary = 'google-chrome';
|
|
}
|
|
|
|
var html = '''
|
|
<script src='$requirePath/require.js'></script>
|
|
<script>
|
|
require.config({
|
|
paths: {
|
|
'dart_sdk': '$sdkJsPath/dart_sdk',
|
|
},
|
|
waitSeconds: 15
|
|
});
|
|
require(['dart_sdk', '$basename'],
|
|
function(sdk, app) {
|
|
'use strict';
|
|
|
|
sdk.dart.weakNullSafetyWarnings(
|
|
!($weakNullSafetyErrors || $soundNullSafety));
|
|
sdk.dart.weakNullSafetyErrors($weakNullSafetyErrors);
|
|
sdk.dart.nonNullAsserts($nonNullAsserts);
|
|
sdk.dart.nativeNonNullAsserts($nativeNonNullAsserts);
|
|
sdk._debugger.registerDevtoolsFormatter();
|
|
app.$libname.main([]);
|
|
});
|
|
</script>
|
|
''';
|
|
var htmlFile = p.setExtension(out, '.html');
|
|
File(htmlFile).writeAsStringSync(html);
|
|
var tmp = p.join(Directory.systemTemp.path, 'ddc');
|
|
|
|
var process = await startProcess('Chrome', chromeBinary, [
|
|
'--auto-open-devtools-for-tabs',
|
|
'--allow-file-access-from-files',
|
|
'--remote-debugging-port=$port',
|
|
'--user-data-dir=$tmp',
|
|
htmlFile
|
|
]);
|
|
if (await process.exitCode != 0) exit(await process.exitCode);
|
|
} else if (node) {
|
|
var nodePath = '$sdkJsPath:$libRoot';
|
|
var runjs = '''
|
|
let source_maps;
|
|
try {
|
|
source_maps = require('source-map-support');
|
|
source_maps.install();
|
|
} catch(e) {
|
|
}
|
|
let sdk = require(\"dart_sdk\");
|
|
// Create a self reference for JS interop tests that set fields on self.
|
|
sdk.dart.global.self = sdk.dart.global;
|
|
let main = require(\"./$basename\").$libname.main;
|
|
try {
|
|
sdk.dart.weakNullSafetyWarnings(
|
|
!($weakNullSafetyErrors || $soundNullSafety));
|
|
sdk.dart.weakNullSafetyErrors($weakNullSafetyErrors);
|
|
sdk.dart.nonNullAsserts($nonNullAsserts);
|
|
sdk.dart.nativeNonNullAsserts($nativeNonNullAsserts);
|
|
sdk._isolate_helper.startRootIsolate(main, []);
|
|
} catch(e) {
|
|
if (!source_maps) {
|
|
console.log('For Dart source maps: npm install source-map-support');
|
|
}
|
|
sdk.core.print(sdk.dart.stackTrace(e));
|
|
process.exit(1);
|
|
}
|
|
''';
|
|
var nodeFile = p.setExtension(out, '.run.js');
|
|
File(nodeFile).writeAsStringSync(runjs);
|
|
var nodeBinary = binary ?? 'node';
|
|
var process = await startProcess('Node', nodeBinary,
|
|
['--inspect=localhost:$port', nodeFile], {'NODE_PATH': nodePath});
|
|
if (await process.exitCode != 0) exit(await process.exitCode);
|
|
} else if (d8) {
|
|
var runjs = '''
|
|
load("$ddcPath/lib/js/legacy/dart_library.js");
|
|
load("$sdkJsPath/dart_sdk.js");
|
|
load("$out");
|
|
|
|
let sdk = dart_library.import('dart_sdk');
|
|
// Create a self reference for JS interop tests that set fields on self.
|
|
sdk.dart.global.self = sdk.dart.global;
|
|
sdk.dart.weakNullSafetyWarnings(
|
|
!($weakNullSafetyErrors || $soundNullSafety));
|
|
sdk.dart.weakNullSafetyErrors($weakNullSafetyErrors);
|
|
sdk.dart.nonNullAsserts($nonNullAsserts);
|
|
sdk.dart.nativeNonNullAsserts($nativeNonNullAsserts);
|
|
|
|
// Invoke main through the d8 preamble to ensure the code is running
|
|
// within the fake event loop.
|
|
self.dartMainRunner(function () {
|
|
dart_library.start("$basename", "$libname");
|
|
});
|
|
''';
|
|
var dart2jsD8Preamble =
|
|
'$sdkRoot/sdk/lib/_internal/js_runtime/lib/preambles/d8.js';
|
|
var d8File = p.setExtension(out, '.d8.js');
|
|
File(d8File).writeAsStringSync(runjs);
|
|
var d8Binary = binary ?? p.join(dartCheckoutPath, _d8executable);
|
|
var process =
|
|
await startProcess('D8', d8Binary, [dart2jsD8Preamble, d8File]);
|
|
if (await process.exitCode != 0) exit(await process.exitCode);
|
|
}
|
|
}
|
|
}
|
|
|
|
String get _d8executable {
|
|
if (Platform.isWindows) {
|
|
return p.join('third_party', 'd8', 'windows', 'd8.exe');
|
|
} else if (Platform.isLinux) {
|
|
return p.join('third_party', 'd8', 'linux', 'd8');
|
|
} else if (Platform.isMacOS) {
|
|
return p.join('third_party', 'd8', 'macos', 'd8');
|
|
}
|
|
throw UnsupportedError('Unsupported platform.');
|
|
}
|
|
|
|
/// Maps supported unames to supported architectures.
|
|
final _resolvedUnames = {
|
|
'arm64': 'arm64',
|
|
'x86_64': 'x64',
|
|
'x64': 'x64',
|
|
};
|
|
|
|
/// Returns the location of the Dart SDK's build directory.
|
|
String resolveBuildDir(String sdkRoot, String? architecture) {
|
|
String platformString;
|
|
String archString;
|
|
if (Platform.isMacOS) {
|
|
platformString = 'xcodebuild';
|
|
var resolvedArchitecture = architecture;
|
|
if (architecture == null) {
|
|
var result = Process.runSync('uname', ['-m']).stdout as String;
|
|
final uname = result.trim();
|
|
resolvedArchitecture = _resolvedUnames[uname] ?? uname;
|
|
}
|
|
if (resolvedArchitecture == 'x64') {
|
|
archString = 'ReleaseX64';
|
|
} else if (resolvedArchitecture == 'arm64') {
|
|
archString = 'ReleaseARM64';
|
|
} else {
|
|
throw UnsupportedError(
|
|
'DDB does not currently support the architecture $architecture '
|
|
'on MacOS.');
|
|
}
|
|
} else if (Platform.isLinux || Platform.isWindows) {
|
|
platformString = 'out';
|
|
if (architecture != null && architecture != 'x64') {
|
|
throw UnsupportedError(
|
|
'DDB does not currently support the architecture $architecture '
|
|
'on Windows or Linux.');
|
|
}
|
|
archString = 'ReleaseX64';
|
|
} else {
|
|
throw UnsupportedError('Unsupported platform.');
|
|
}
|
|
return p.join(sdkRoot, platformString, archString);
|
|
}
|