| #!/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. | 
 |  | 
 | // @dart = 2.9 | 
 |  | 
 | // | 
 | // 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:path/path.dart' as p; | 
 | import 'package:dev_compiler/src/compiler/module_builder.dart' | 
 |     as module_builder; | 
 |  | 
 | 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; | 
 |   var summaries = options['summary'] as List; | 
 |   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 = | 
 |       module_builder.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(String command, List<String> args) async { | 
 |     if (debug) { | 
 |       // Use unbuilt script.  This only works from a source checkout. | 
 |       var vmServicePort = options.wasParsed('vm-service-port') | 
 |           ? '=${options['vm-service-port']}' | 
 |           : ''; | 
 |       var observe = | 
 |           options.wasParsed('vm-service-port') || options['observe'] as bool; | 
 |       args.insertAll(0, [ | 
 |         if (observe) ...[ | 
 |           '--enable-vm-service$vmServicePort', | 
 |           '--pause-isolates-on-start', | 
 |         ], | 
 |         '--enable-asserts', | 
 |         p.join(ddcPath, 'bin', '$command.dart') | 
 |       ]); | 
 |       command = dartBinary; | 
 |     } else { | 
 |       // Use built snapshot. | 
 |       command = p.join(dartSdk, 'bin', command); | 
 |     } | 
 |     var process = await startProcess('DDC', command, args, <String, String>{ | 
 |       if (options['compile-vm-options'] != null) | 
 |         'DART_VM_OPTIONS': options['compile-vm-options'] as String | 
 |     }); | 
 |     if (await process.exitCode != 0) exit(await process.exitCode); | 
 |   } | 
 |  | 
 |   String mod; | 
 |   bool chrome = false; | 
 |   bool node = false; | 
 |   bool d8 = false; | 
 |   switch (options['runtime'] as String) { | 
 |     case 'node': | 
 |       node = true; | 
 |       mod = 'common'; | 
 |       break; | 
 |     case 'd8': | 
 |       d8 = true; | 
 |       mod = 'legacy'; | 
 |       break; | 
 |     case 'chrome': | 
 |       chrome = true; | 
 |       mod = 'amd'; | 
 |       break; | 
 |   } | 
 |  | 
 |   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('dartdevc', 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'; | 
 |     String resolvedArchitecture = architecture; | 
 |     if (architecture == null) { | 
 |       final uname = Process.runSync('uname', ['-m']).stdout.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); | 
 | } |