Added module information to metadata and use by frontend_sever

- created class representing module metadata, added metadata to JsCode
  class
- added module information to metadata, such as module name, load
  function name
- added import and file uris for libraries
- added experimental-emit-debug-metadata flag to frontend_server
- added frontend server tests to check for saved metadata

Related: https://github.com/dart-lang/sdk/issues/41852
Closes: https://github.com/dart-lang/sdk/issues/40774
Change-Id: Iecbbf1e4eea1919e01f002f45363d30707cb1590
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/150181
Commit-Queue: Anna Gringauze <annagrin@google.com>
Reviewed-by: Nicholas Shahan <nshahan@google.com>
Reviewed-by: Gary Roumanis <grouma@google.com>
This commit is contained in:
Anna Gringauze 2020-06-20 00:40:35 +00:00 committed by commit-bot@chromium.org
parent 40b95458a5
commit c909e16ee2
10 changed files with 568 additions and 49 deletions

View file

@ -191,7 +191,7 @@ class DdcModuleBuilder extends _ModuleBuilder {
}
var resultModule = NamedFunction(
loadFunctionName(module.name),
loadFunctionIdentifier(module.name),
js.fun("function(#) { 'use strict'; #; }", [parameters, statements]),
true);
@ -299,7 +299,7 @@ class AmdModuleBuilder extends _ModuleBuilder {
statements.add(Return(ObjectInitializer(exportedProps, multiline: true)));
}
var resultModule = NamedFunction(
loadFunctionName(module.name),
loadFunctionIdentifier(module.name),
js.fun("function(#) { 'use strict'; #; }", [fnParams, statements]),
true);
var block = js.statement(
@ -309,6 +309,17 @@ class AmdModuleBuilder extends _ModuleBuilder {
}
}
bool isSdkInternalRuntimeUri(Uri importUri) {
return importUri.scheme == 'dart' && importUri.path == '_runtime';
}
String libraryUriToJsIdentifier(Uri importUri) {
if (importUri.scheme == 'dart') {
return isSdkInternalRuntimeUri(importUri) ? 'dart' : importUri.path;
}
return pathToJSIdentifier(p.withoutExtension(importUri.pathSegments.last));
}
/// Converts an entire arbitrary path string into a string compatible with
/// JS identifier naming rules while conserving path information.
///
@ -327,9 +338,13 @@ String pathToJSIdentifier(String path) {
.replaceAll('-', '_'));
}
/// Creates function name given [moduleName].
String loadFunctionName(String moduleName) =>
'load__' + pathToJSIdentifier(moduleName.replaceAll('.', '_'));
/// Creates function name identifier given [moduleName].
Identifier loadFunctionName(String moduleName) =>
Identifier('load__' + pathToJSIdentifier(moduleName.replaceAll('.', '_')));
Identifier loadFunctionIdentifier(String moduleName) =>
Identifier(loadFunctionName(moduleName));
// Replacement string for path separators (i.e., '/', '\', '..').
final encodedSeparator = '__';

View file

@ -61,6 +61,13 @@ class SharedCompilerOptions {
/// runtime can enable synchronous stack trace deobsfuscation.
final bool inlineSourceMap;
/// Whether to emit the debug metadata
///
/// Debugger uses this information about to construct mapping between
/// modules and libraries that otherwise requires expensive communication with
/// the browser.
final bool emitDebugMetadata;
/// Whether to emit a summary file containing API signatures.
///
/// This is required for a modular build process.
@ -99,6 +106,7 @@ class SharedCompilerOptions {
this.summarizeApi = true,
this.enableAsserts = true,
this.replCompile = false,
this.emitDebugMetadata = false,
this.summaryModules = const {},
this.moduleFormats = const [],
this.experiments = const {},
@ -119,7 +127,9 @@ class SharedCompilerOptions {
moduleFormats: parseModuleFormatOption(args),
moduleName: _getModuleName(args, moduleRoot),
replCompile: args['repl-compile'] as bool,
soundNullSafety: args['sound-null-safety'] as bool);
soundNullSafety: args['sound-null-safety'] as bool,
emitDebugMetadata:
args['experimental-emit-debug-metadata'] as bool);
static void addArguments(ArgParser parser, {bool hide = true}) {
addModuleFormatOptions(parser, hide: hide);
@ -152,7 +162,14 @@ class SharedCompilerOptions {
..addFlag('sound-null-safety',
help: 'Compile for sound null safety at runtime.',
negatable: true,
defaultsTo: false);
defaultsTo: false)
// TODO(41852) Define a process for breaking changes before graduating from
// experimental.
..addFlag('experimental-emit-debug-metadata',
help: 'Experimental option for compiler development.\n'
'Output a metadata file for debug tools next to the .js output.',
defaultsTo: false,
hide: true);
}
static String _getModuleName(ArgResults args, String moduleRoot) {

View file

@ -27,6 +27,7 @@ import '../js_ast/js_ast.dart' as js_ast;
import '../js_ast/js_ast.dart' show js;
import '../js_ast/source_map_printer.dart' show SourceMapPrintingContext;
import 'compiler.dart';
import 'module_metadata.dart';
import 'target.dart';
const _binaryName = 'dartdevc -k';
@ -109,13 +110,6 @@ Future<CompilerResult> _compile(List<String> args,
help: 'The path to the libraries.json file for the sdk.')
..addOption('used-inputs-file',
help: 'If set, the file to record inputs used.', hide: true)
// TODO(41852) Define a process for breaking changes before graduating from
// experimental.
..addFlag('experimental-emit-debug-metadata',
help: 'Experimental option for compiler development.\n'
'Output a metadata file for debug tools next to the .js output.',
defaultsTo: false,
hide: true)
..addFlag('kernel',
abbr: 'k',
help: 'Deprecated and ignored. To be removed in a future release.',
@ -425,6 +419,7 @@ Future<CompilerResult> _compile(List<String> args,
var jsCode = jsProgramToCode(jsModule, moduleFormat,
buildSourceMap: options.sourceMap,
inlineSourceMap: options.inlineSourceMap,
emitDebugMetadata: options.emitDebugMetadata,
jsUrl: p.toUri(output).toString(),
mapUrl: mapUrl,
customScheme: multiRootScheme,
@ -436,21 +431,9 @@ Future<CompilerResult> _compile(List<String> args,
outFiles.add(
File('$output.map').writeAsString(json.encode(jsCode.sourceMap)));
}
if (argResults['experimental-emit-debug-metadata'] as bool) {
var moduleMetadata = [
for (var lib in compiledLibraries.libraries)
{
'name': compiler.jsLibraryName(lib),
'sourceMapFileUri': mapUrl,
'dartFileUris': [
lib.fileUri.toString(),
...lib.parts.map((p) => p.partUri.toString())
],
}
];
if (jsCode.metadata != null) {
outFiles.add(
File('$output.metadata').writeAsString(json.encode(moduleMetadata)));
File('$output.metadata').writeAsString(json.encode(jsCode.metadata)));
}
}
@ -598,7 +581,14 @@ class JSCode {
/// using [placeSourceMap].
final Map sourceMap;
JSCode(this.code, this.sourceMap);
/// Module and library information
///
/// The [metadata] is a contract between compiler and the debugger,
/// helping the debugger map between libraries, modules, source paths.
/// see: https://goto.google.com/dart-web-debugger-metadata
final ModuleMetadata metadata;
JSCode(this.code, this.sourceMap, {this.metadata});
}
/// Converts [moduleTree] to [JSCode], using [format].
@ -608,6 +598,7 @@ class JSCode {
JSCode jsProgramToCode(js_ast.Program moduleTree, ModuleFormat format,
{bool buildSourceMap = false,
bool inlineSourceMap = false,
bool emitDebugMetadata = false,
String jsUrl,
String mapUrl,
String sourceMapBase,
@ -666,7 +657,27 @@ JSCode jsProgramToCode(js_ast.Program moduleTree, ModuleFormat format,
};
text = text.replaceFirst(
SharedCompiler.metricsLocationID, '$compileTimeStatistics');
return JSCode(text, builtMap);
var debugMetadata = emitDebugMetadata
? _emitMetadata(moduleTree, component, mapUrl, jsUrl)
: null;
return JSCode(text, builtMap, metadata: debugMetadata);
}
ModuleMetadata _emitMetadata(js_ast.Program program, Component component,
String sourceMapUri, String moduleUri) {
var metadata = ModuleMetadata(
program.name, loadFunctionName(program.name), sourceMapUri, moduleUri);
for (var lib in component.libraries) {
metadata.addLibrary(LibraryMetadata(
libraryUriToJsIdentifier(lib.importUri),
lib.importUri.toString(),
lib.fileUri.toString(),
[...lib.parts.map((p) => p.partUri)]));
}
return metadata;
}
/// Parses Dart's non-standard `-Dname=value` syntax for declared variables,

View file

@ -17,7 +17,8 @@ import 'package:path/path.dart' as p;
import '../compiler/js_names.dart' as js_ast;
import '../compiler/js_utils.dart' as js_ast;
import '../compiler/module_builder.dart' show pathToJSIdentifier;
import '../compiler/module_builder.dart'
show isSdkInternalRuntimeUri, libraryUriToJsIdentifier, pathToJSIdentifier;
import '../compiler/shared_command.dart' show SharedCompilerOptions;
import '../compiler/shared_compiler.dart';
import '../js_ast/js_ast.dart' as js_ast;
@ -371,11 +372,7 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
@override
String jsLibraryName(Library library) {
var uri = library.importUri;
if (uri.scheme == 'dart') {
return isSdkInternalRuntime(library) ? 'dart' : uri.path;
}
return pathToJSIdentifier(p.withoutExtension(uri.pathSegments.last));
return libraryUriToJsIdentifier(library.importUri);
}
@override
@ -405,8 +402,7 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
@override
bool isSdkInternalRuntime(Library l) {
var uri = l.importUri;
return uri.scheme == 'dart' && uri.path == '_runtime';
return isSdkInternalRuntimeUri(l.importUri);
}
@override

View file

@ -0,0 +1,173 @@
// 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.
/// Module metadata format version
///
/// Module reader always creates the current version but is able to read
/// metadata files with later versions as long as the changes are backward
/// compatible, i.e. only minor or patch versions have changed.
///
/// See: https://goto.google.com/dart-web-debugger-metadata
class ModuleMetadataVersion {
final int majorVersion;
final int minorVersion;
final int patchVersion;
const ModuleMetadataVersion(
this.majorVersion,
this.minorVersion,
this.patchVersion,
);
/// Current metadata version
///
/// Version follows simple semantic versioning format 'major.minor.patch'
/// See https://semver.org
///
/// TODO(annagrin): create metadata package, make version the same as the
/// metadata package version, automate updating with the package update
static const ModuleMetadataVersion current = ModuleMetadataVersion(1, 0, 0);
/// Current metadata version created by the reader
String get version => '$majorVersion.$minorVersion.$patchVersion';
/// Is this metadata version compatible with the given version
///
/// The minor and patch version changes never remove any fields that current
/// version supports, so the reader can create current metadata version from
/// any file created with a later reader, as long as the major version does
/// not change.
bool isCompatibleWith(String version) {
var parts = version.split('.');
if (parts.length != 3) {
throw FormatException('Version: $version'
'does not follow simple semantic versioning format');
}
var major = int.parse(parts[0]);
var minor = int.parse(parts[1]);
var patch = int.parse(parts[2]);
return major == majorVersion &&
minor >= minorVersion &&
patch >= patchVersion;
}
}
/// Library metadata
///
/// Represents library metadata used in the debugger,
/// supports reading from and writing to json
/// See: https://goto.google.com/dart-web-debugger-metadata
class LibraryMetadata {
/// Library name as defined in pubspec.yaml
final String name;
/// Library importUri
///
/// Example package:path/path.dart
final String importUri;
/// Library fileUri
///
/// Example file:///path/to/path/path.dart
final String fileUri;
/// All file uris from the library
///
/// Can be relative paths to the directory of the fileUri
final List<String> partUris;
LibraryMetadata(this.name, this.importUri, this.fileUri, this.partUris);
LibraryMetadata.fromJson(Map<String, dynamic> json)
: name = json['name'] as String,
importUri = json['importUri'] as String,
fileUri = json['fileUri'] as String,
partUris =
List.castFrom<dynamic, String>(json['partUris'] as List<dynamic>);
Map<String, dynamic> toJson() {
return {
'name': name,
'importUri': importUri,
'fileUri': fileUri,
'partUris': [...partUris]
};
}
}
/// Module metadata
///
/// Represents module metadata used in the debugger,
/// supports reading from and writing to json
/// See: https://goto.google.com/dart-web-debugger-metadata
class ModuleMetadata {
/// Metadata format version
String version;
/// Module name
///
/// Used as a name of the js module created by the compiler and
/// as key to store and load modules in the debugger and the browser
final String name;
/// Name of the function enclosing the module
///
/// Used by debugger to determine the top dart scope
final String closureName;
/// Source map uri
final String sourceMapUri;
/// Module uri
final String moduleUri;
final Map<String, LibraryMetadata> libraries = {};
ModuleMetadata(this.name, this.closureName, this.sourceMapUri, this.moduleUri,
{this.version}) {
version ??= ModuleMetadataVersion.current.version;
}
/// Add [library] to metadata
///
/// Used for filling the metadata in the compiler or for reading from
/// stored metadata files.
void addLibrary(LibraryMetadata library) {
if (!libraries.containsKey(library.importUri)) {
libraries[library.importUri] = library;
} else {
throw ('Metadata creation error: '
'Cannot add library $library with uri ${library.importUri}: '
'another library "${libraries[library.importUri]}" is found '
'with the same uri');
}
}
ModuleMetadata.fromJson(Map<String, dynamic> json)
: version = json['version'] as String,
name = json['name'] as String,
closureName = json['closureName'] as String,
sourceMapUri = json['sourceMapUri'] as String,
moduleUri = json['moduleUri'] as String {
var fileVersion = json['version'] as String;
if (!ModuleMetadataVersion.current.isCompatibleWith(version)) {
throw Exception('Unsupported metadata version $fileVersion');
}
for (var l in json['libraries'] as List<dynamic>) {
addLibrary(LibraryMetadata.fromJson(l as Map<String, dynamic>));
}
}
Map<String, dynamic> toJson() {
return {
'version': version,
'name': name,
'closureName': closureName,
'sourceMapUri': sourceMapUri,
'moduleUri': moduleUri,
'libraries': [for (var lib in libraries.values) lib.toJson()]
};
}
}

View file

@ -0,0 +1,135 @@
// 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:convert';
import 'dart:io';
import 'package:dev_compiler/src/kernel/module_metadata.dart';
import 'package:test/test.dart';
// Test creating, reading and writing debugger metadata
void main() {
group('Module metadata', () {
Directory tempDir;
File file;
setUpAll(() {
var systemTempDir = Directory.systemTemp;
tempDir = systemTempDir.createTempSync('foo bar');
var input = tempDir.uri.resolve('module.metadata');
file = File.fromUri(input)..createSync();
});
tearDownAll(() {
tempDir.delete(recursive: true);
});
test('create, write, and read', () async {
// create metadata
var version = ModuleMetadataVersion.current.version;
var module = createMetadata(version);
testMetadataFields(module, version);
// write metadata
file.writeAsBytesSync(utf8.encode(json.encode(module)));
expect(file.existsSync(), true);
// read metadata
var moduleJson = json.decode(utf8.decode(file.readAsBytesSync()));
var newModule =
ModuleMetadata.fromJson(moduleJson as Map<String, dynamic>);
testMetadataFields(newModule, version);
});
test('read later backward-compatible patch version', () async {
// create metadata with next patch version
var version = ModuleMetadataVersion(
ModuleMetadataVersion.current.majorVersion,
ModuleMetadataVersion.current.minorVersion,
ModuleMetadataVersion.current.patchVersion + 1)
.version;
var module = createMetadata(version);
// write metadata
file.writeAsBytesSync(utf8.encode(json.encode(module)));
expect(file.existsSync(), true);
// read metadata
var moduleJson = json.decode(utf8.decode(file.readAsBytesSync()));
var newModule =
ModuleMetadata.fromJson(moduleJson as Map<String, dynamic>);
testMetadataFields(newModule, version);
});
test('read later backward-compatible minor version', () async {
// create metadata with next minor version
var version = ModuleMetadataVersion(
ModuleMetadataVersion.current.majorVersion,
ModuleMetadataVersion.current.minorVersion + 1,
ModuleMetadataVersion.current.patchVersion + 1)
.version;
var module = createMetadata(version);
// write metadata
file.writeAsBytesSync(utf8.encode(json.encode(module)));
expect(file.existsSync(), true);
// read metadata
var moduleJson = json.decode(utf8.decode(file.readAsBytesSync()));
var newModule =
ModuleMetadata.fromJson(moduleJson as Map<String, dynamic>);
testMetadataFields(newModule, version);
});
test('fail to read later non-backward-compatible major version', () async {
// create metadata with next minor version
var version = ModuleMetadataVersion(
ModuleMetadataVersion.current.majorVersion + 1,
ModuleMetadataVersion.current.minorVersion + 1,
ModuleMetadataVersion.current.patchVersion + 1)
.version;
var module = createMetadata(version);
// write metadata
file.writeAsBytesSync(utf8.encode(json.encode(module)));
expect(file.existsSync(), true);
// try read metadata, expect to fail
var moduleJson = json.decode(utf8.decode(file.readAsBytesSync()));
ModuleMetadata newModule;
try {
newModule = ModuleMetadata.fromJson(moduleJson as Map<String, dynamic>);
} catch (e) {
expect(
e.toString(), 'Exception: Unsupported metadata version $version');
}
expect(newModule, null);
});
});
}
ModuleMetadata createMetadata(String version) => ModuleMetadata(
'module', 'closure', 'module.map', 'module.js', version: version)
..addLibrary(LibraryMetadata('library', 'package:library/test.dart',
'file:///source/library/lib/test.dart', ['src/test2.dart']));
void testMetadataFields(ModuleMetadata module, String version) {
// reader always creates current metadata version
expect(module.version, version);
expect(module.name, 'module');
expect(module.closureName, 'closure');
expect(module.sourceMapUri, 'module.map');
expect(module.moduleUri, 'module.js');
var libUri = module.libraries.keys.first;
var lib = module.libraries[libUri];
expect(libUri, 'package:library/test.dart');
expect(lib.name, 'library');
expect(lib.importUri, 'package:library/test.dart');
expect(lib.fileUri, 'file:///source/library/lib/test.dart');
expect(lib.partUris[0], 'src/test2.dart');
}

View file

@ -171,6 +171,9 @@ ArgParser argParser = ArgParser(allowTrailingOptions: true)
help: 'A path or uri to the libraries specification JSON file')
..addFlag('debugger-module-names',
help: 'Use debugger-friendly modules names', defaultsTo: false)
..addFlag('experimental-emit-debug-metadata',
help: 'Emit module and library metadata for the debugger',
defaultsTo: false)
..addOption('dartdevc-module-format',
help: 'The module format to use on for the dartdevc compiler',
defaultsTo: 'amd');
@ -320,7 +323,8 @@ class FrontendCompiler implements CompilerInterface {
this.transformer,
this.unsafePackageSerialization,
this.incrementalSerialization: true,
this.useDebuggerModuleNames: false}) {
this.useDebuggerModuleNames: false,
this.emitDebugMetadata: false}) {
_outputStream ??= stdout;
printerFactory ??= new BinaryPrinterFactory();
}
@ -330,6 +334,7 @@ class FrontendCompiler implements CompilerInterface {
bool unsafePackageSerialization;
bool incrementalSerialization;
bool useDebuggerModuleNames;
bool emitDebugMetadata;
CompilerOptions _compilerOptions;
BytecodeOptions _bytecodeOptions;
@ -636,27 +641,33 @@ class FrontendCompiler implements CompilerInterface {
final File sourceFile = File('$filename.sources');
final File manifestFile = File('$filename.json');
final File sourceMapsFile = File('$filename.map');
final File metadataFile = File('$filename.metadata');
if (!sourceFile.parent.existsSync()) {
sourceFile.parent.createSync(recursive: true);
}
_bundler = JavaScriptBundler(
component, strongComponents, fileSystemScheme, packageConfig,
useDebuggerModuleNames: useDebuggerModuleNames,
emitDebugMetadata: emitDebugMetadata,
moduleFormat: moduleFormat);
final sourceFileSink = sourceFile.openWrite();
final manifestFileSink = manifestFile.openWrite();
final sourceMapsFileSink = sourceMapsFile.openWrite();
final metadataFileSink =
emitDebugMetadata ? metadataFile.openWrite() : null;
await _bundler.compile(
results.classHierarchy,
results.coreTypes,
results.loadedLibraries,
sourceFileSink,
manifestFileSink,
sourceMapsFileSink);
sourceMapsFileSink,
metadataFileSink);
await Future.wait([
sourceFileSink.close(),
manifestFileSink.close(),
sourceMapsFileSink.close()
sourceMapsFileSink.close(),
if (metadataFileSink != null) metadataFileSink.close()
]);
}
@ -1409,7 +1420,8 @@ Future<int> starter(
printerFactory: binaryPrinterFactory,
unsafePackageSerialization: options["unsafe-package-serialization"],
incrementalSerialization: options["incremental-serialization"],
useDebuggerModuleNames: options['debugger-module-names']);
useDebuggerModuleNames: options['debugger-module-names'],
emitDebugMetadata: options['experimental-emit-debug-metadata']);
if (options.rest.isNotEmpty) {
return await compiler.compile(options.rest[0], options,

View file

@ -25,7 +25,9 @@ import 'strong_components.dart';
class JavaScriptBundler {
JavaScriptBundler(this._originalComponent, this._strongComponents,
this._fileSystemScheme, this._packageConfig,
{this.useDebuggerModuleNames = false, String moduleFormat})
{this.useDebuggerModuleNames = false,
this.emitDebugMetadata = false,
String moduleFormat})
: compilers = <String, ProgramCompiler>{},
_moduleFormat = parseModuleFormat(moduleFormat ?? 'amd') {
_summaries = <Component>[];
@ -58,6 +60,7 @@ class JavaScriptBundler {
final String _fileSystemScheme;
final PackageConfig _packageConfig;
final bool useDebuggerModuleNames;
final bool emitDebugMetadata;
final Map<String, ProgramCompiler> compilers;
final ModuleFormat _moduleFormat;
@ -74,9 +77,11 @@ class JavaScriptBundler {
Set<Library> loadedLibraries,
IOSink codeSink,
IOSink manifestSink,
IOSink sourceMapsSink) async {
IOSink sourceMapsSink,
IOSink metadataSink) async {
var codeOffset = 0;
var sourceMapOffset = 0;
var metadataOffset = 0;
final manifest = <String, Map<String, List<int>>>{};
final Set<Uri> visited = <Uri>{};
@ -124,7 +129,11 @@ class JavaScriptBundler {
_originalComponent,
classHierarchy,
SharedCompilerOptions(
sourceMap: true, summarizeApi: false, moduleName: moduleName),
sourceMap: true,
summarizeApi: false,
emitDebugMetadata: emitDebugMetadata,
moduleName: moduleName,
),
importToSummary,
summaryToModule,
coreTypes: coreTypes,
@ -149,11 +158,13 @@ class JavaScriptBundler {
sourceMapBase =
p.dirname((await _packageConfig.resolve(moduleUri)).path);
}
final code = jsProgramToCode(
jsModule,
_moduleFormat,
inlineSourceMap: true,
buildSourceMap: true,
emitDebugMetadata: emitDebugMetadata,
jsUrl: '$moduleUrl.lib.js',
mapUrl: '$moduleUrl.lib.js.map',
sourceMapBase: sourceMapBase,
@ -162,9 +173,14 @@ class JavaScriptBundler {
);
final codeBytes = utf8.encode(code.code);
final sourceMapBytes = utf8.encode(json.encode(code.sourceMap));
final metadataBytes =
emitDebugMetadata ? utf8.encode(json.encode(code.metadata)) : null;
codeSink.add(codeBytes);
sourceMapsSink.add(sourceMapBytes);
if (emitDebugMetadata) {
metadataSink.add(metadataBytes);
}
final String moduleKey = _moduleImportForSummary[moduleUri];
manifest[moduleKey] = {
'code': <int>[codeOffset, codeOffset += codeBytes.length],
@ -172,6 +188,11 @@ class JavaScriptBundler {
sourceMapOffset,
sourceMapOffset += sourceMapBytes.length
],
if (emitDebugMetadata)
'metadata': <int>[
metadataOffset,
metadataOffset += metadataBytes.length
],
};
}
manifestSink.add(utf8.encode(json.encode(manifest)));

View file

@ -1462,6 +1462,141 @@ true
expect(await starter(args), 0);
});
test('compile to JavaScript with no metadata', () async {
var file = File('${tempDir.path}/foo.dart')..createSync();
file.writeAsStringSync("main() {\n\n}\n");
File('${tempDir.path}/.packages')
..createSync()
..writeAsStringSync("hello:${tempDir.uri}\n");
var library = 'package:hello/foo.dart';
var dillFile = File('${tempDir.path}/app.dill');
var sourceFile = File('${dillFile.path}.sources');
var manifestFile = File('${dillFile.path}.json');
var sourceMapsFile = File('${dillFile.path}.map');
var metadataFile = File('${dillFile.path}.metadata');
expect(dillFile.existsSync(), false);
expect(sourceFile.existsSync(), false);
expect(manifestFile.existsSync(), false);
expect(sourceMapsFile.existsSync(), false);
expect(metadataFile.existsSync(), false);
final List<String> args = <String>[
'--sdk-root=${sdkRoot.toFilePath()}',
'--incremental',
'--platform=${ddcPlatformKernel.path}',
'--output-dill=${dillFile.path}',
'--target=dartdevc',
'--packages=${tempDir.path}/.packages',
'--debugger-module-names'
];
final StreamController<List<int>> streamController =
StreamController<List<int>>();
final StreamController<List<int>> stdoutStreamController =
StreamController<List<int>>();
final IOSink ioSink = IOSink(stdoutStreamController.sink);
StreamController<Result> receivedResults = StreamController<Result>();
final outputParser = OutputParser(receivedResults);
stdoutStreamController.stream
.transform(utf8.decoder)
.transform(const LineSplitter())
.listen(outputParser.listener);
Future<int> result =
starter(args, input: streamController.stream, output: ioSink);
streamController.add('compile $library\n'.codeUnits);
var count = 0;
receivedResults.stream.listen((Result compiledResult) {
CompilationResult result =
CompilationResult.parse(compiledResult.status);
count++;
// Request to 'compile', which results in full JavaScript and no metadata
expect(result.errorsCount, equals(0));
expect(sourceFile.existsSync(), equals(true));
expect(manifestFile.existsSync(), equals(true));
expect(sourceMapsFile.existsSync(), equals(true));
expect(metadataFile.existsSync(), equals(false));
expect(result.filename, dillFile.path);
streamController.add('accept\n'.codeUnits);
outputParser.expectSources = false;
streamController.add('quit\n'.codeUnits);
});
expect(await result, 0);
expect(count, 1);
});
test('compile to JavaScript with metadata', () async {
var file = File('${tempDir.path}/foo.dart')..createSync();
file.writeAsStringSync("main() {\n\n}\n");
File('${tempDir.path}/.packages')
..createSync()
..writeAsStringSync("hello:${tempDir.uri}\n");
var library = 'package:hello/foo.dart';
var dillFile = File('${tempDir.path}/app.dill');
var sourceFile = File('${dillFile.path}.sources');
var manifestFile = File('${dillFile.path}.json');
var sourceMapsFile = File('${dillFile.path}.map');
var metadataFile = File('${dillFile.path}.metadata');
expect(dillFile.existsSync(), false);
expect(sourceFile.existsSync(), false);
expect(manifestFile.existsSync(), false);
expect(sourceMapsFile.existsSync(), false);
expect(metadataFile.existsSync(), false);
final List<String> args = <String>[
'--sdk-root=${sdkRoot.toFilePath()}',
'--incremental',
'--platform=${ddcPlatformKernel.path}',
'--output-dill=${dillFile.path}',
'--target=dartdevc',
'--packages=${tempDir.path}/.packages',
'--debugger-module-names',
'--experimental-emit-debug-metadata'
];
final StreamController<List<int>> streamController =
StreamController<List<int>>();
final StreamController<List<int>> stdoutStreamController =
StreamController<List<int>>();
final IOSink ioSink = IOSink(stdoutStreamController.sink);
StreamController<Result> receivedResults = StreamController<Result>();
final outputParser = OutputParser(receivedResults);
stdoutStreamController.stream
.transform(utf8.decoder)
.transform(const LineSplitter())
.listen(outputParser.listener);
Future<int> result =
starter(args, input: streamController.stream, output: ioSink);
streamController.add('compile $library\n'.codeUnits);
int count = 0;
receivedResults.stream.listen((Result compiledResult) {
CompilationResult result =
CompilationResult.parse(compiledResult.status);
count++;
// Request to 'compile', which results in full JavaScript and no metadata
expect(result.errorsCount, equals(0));
expect(sourceFile.existsSync(), equals(true));
expect(manifestFile.existsSync(), equals(true));
expect(sourceMapsFile.existsSync(), equals(true));
expect(metadataFile.existsSync(), equals(true));
expect(result.filename, dillFile.path);
streamController.add('accept\n'.codeUnits);
outputParser.expectSources = false;
streamController.add('quit\n'.codeUnits);
});
expect(await result, 0);
expect(count, 1);
});
test('compile expression to Javascript', () async {
var file = File('${tempDir.path}/foo.dart')..createSync();
file.writeAsStringSync("main() {\n\n}\n");

View file

@ -103,10 +103,11 @@ void main() {
final manifestSink = _MemorySink();
final codeSink = _MemorySink();
final sourcemapSink = _MemorySink();
final metadataSink = _MemorySink();
final coreTypes = CoreTypes(testComponent);
await javaScriptBundler.compile(ClassHierarchy(testComponent, coreTypes),
coreTypes, {}, codeSink, manifestSink, sourcemapSink);
coreTypes, {}, codeSink, manifestSink, sourcemapSink, metadataSink);
final Map manifest = json.decode(utf8.decode(manifestSink.buffer));
final String code = utf8.decode(codeSink.buffer);
@ -143,10 +144,11 @@ void main() {
final manifestSink = _MemorySink();
final codeSink = _MemorySink();
final sourcemapSink = _MemorySink();
final metadataSink = _MemorySink();
final coreTypes = CoreTypes(testComponent);
await javaScriptBundler.compile(ClassHierarchy(testComponent, coreTypes),
coreTypes, {}, codeSink, manifestSink, sourcemapSink);
coreTypes, {}, codeSink, manifestSink, sourcemapSink, metadataSink);
final Map manifest = json.decode(utf8.decode(manifestSink.buffer));
final String code = utf8.decode(codeSink.buffer);
@ -183,10 +185,11 @@ void main() {
final manifestSink = _MemorySink();
final codeSink = _MemorySink();
final sourcemapSink = _MemorySink();
final metadataSink = _MemorySink();
final coreTypes = CoreTypes(testComponent);
await javaScriptBundler.compile(ClassHierarchy(testComponent, coreTypes),
coreTypes, {}, codeSink, manifestSink, sourcemapSink);
coreTypes, {}, codeSink, manifestSink, sourcemapSink, metadataSink);
final Map manifest = json.decode(utf8.decode(manifestSink.buffer));
final String code = utf8.decode(codeSink.buffer);
@ -232,10 +235,11 @@ void main() {
final manifestSink = _MemorySink();
final codeSink = _MemorySink();
final sourcemapSink = _MemorySink();
final metadataSink = _MemorySink();
final coreTypes = CoreTypes(testComponent);
javaScriptBundler.compile(ClassHierarchy(testComponent, coreTypes),
coreTypes, {}, codeSink, manifestSink, sourcemapSink);
coreTypes, {}, codeSink, manifestSink, sourcemapSink, metadataSink);
final code = utf8.decode(codeSink.buffer);
final manifest = json.decode(utf8.decode(manifestSink.buffer));