mirror of
https://github.com/dart-lang/sdk
synced 2024-09-15 22:51:29 +00:00
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:
parent
40b95458a5
commit
c909e16ee2
|
@ -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 = '__';
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
173
pkg/dev_compiler/lib/src/kernel/module_metadata.dart
Normal file
173
pkg/dev_compiler/lib/src/kernel/module_metadata.dart
Normal 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()]
|
||||
};
|
||||
}
|
||||
}
|
135
pkg/dev_compiler/test/module_metadata_test.dart
Normal file
135
pkg/dev_compiler/test/module_metadata_test.dart
Normal 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');
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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)));
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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));
|
||||
|
|
Loading…
Reference in a new issue