[dartdevc] fix #36253, kernel backend now emits trackLibraries call
Also fixes DDC's Kernel backend to emit source maps. There's also
some refactoring to move shared code to the shared_compiler, to unblock
hot reload implementation work and other fixes (such as exporting
private names from the module).
Change-Id: I872e221d9f266198fcc220900146c1c9c5503acb
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/97553
Commit-Queue: Jenny Messerly <jmesserly@google.com>
Reviewed-by: Vijay Menon <vsm@google.com>
diff --git a/pkg/dev_compiler/lib/src/analyzer/code_generator.dart b/pkg/dev_compiler/lib/src/analyzer/code_generator.dart
index 47c984c..f08de41 100644
--- a/pkg/dev_compiler/lib/src/analyzer/code_generator.dart
+++ b/pkg/dev_compiler/lib/src/analyzer/code_generator.dart
@@ -44,7 +44,7 @@
import 'extension_types.dart' show ExtensionTypeSet;
import 'js_interop.dart';
import 'js_typerep.dart';
-import 'module_compiler.dart' show CompilerOptions, JSModuleFile;
+import 'module_compiler.dart' show CompilerOptions;
import 'nullable_type_inference.dart' show NullableTypeInference;
import 'property_model.dart';
import 'reify_coercions.dart' show CoercionReifier;
@@ -84,16 +84,6 @@
JSTypeRep jsTypeRep;
- /// The set of libraries we are currently compiling, and the temporaries used
- /// to refer to them.
- ///
- /// We sometimes special case codegen for a single library, as it simplifies
- /// name scoping requirements.
- final _libraries = Map<LibraryElement, JS.Identifier>();
-
- /// Imported libraries, and the temporaries used to refer to them.
- final _imports = Map<LibraryElement, JS.TemporaryId>();
-
/// The list of dart:_runtime SDK functions; these are assumed by other code
/// in the SDK to be generated before anything else.
final _internalSdkFunctions = <JS.ModuleItem>[];
@@ -118,9 +108,6 @@
final _initializingFormalTemps = HashMap<ParameterElement, JS.TemporaryId>();
- JS.Identifier _extensionSymbolsModule;
- final _extensionSymbols = Map<String, JS.TemporaryId>();
-
/// The type provider from the current Analysis [context].
final TypeProvider types;
@@ -286,48 +273,17 @@
// Transform the AST to make coercions explicit.
compilationUnits = CoercionReifier.reify(compilationUnits);
- var items = <JS.ModuleItem>[];
- var root = JS.Identifier('_root');
- items.add(js.statement('const # = Object.create(null)', [root]));
+ var libraries = compilationUnits
+ .where((unit) {
+ var library = unit.declaredElement.library;
+ return unit.declaredElement == library.definingCompilationUnit;
+ })
+ .map((unit) => unit.declaredElement.library)
+ .toList();
- var isBuildingSdk = compilationUnits
- .any((u) => isSdkInternalRuntime(u.declaredElement.library));
- if (isBuildingSdk) {
- // Don't allow these to be renamed when we're building the SDK.
- // There is JS code in dart:* that depends on their names.
- runtimeModule = JS.Identifier('dart');
- _extensionSymbolsModule = JS.Identifier('dartx');
- } else {
- // Otherwise allow these to be renamed so users can write them.
- runtimeModule = JS.TemporaryId('dart');
- _extensionSymbolsModule = JS.TemporaryId('dartx');
- }
+ var items = startModule(libraries);
_typeTable = TypeTable(runtimeModule);
- // Initialize our library variables.
- var exports = <JS.NameSpecifier>[];
- void emitLibrary(JS.Identifier id) {
- items.add(js.statement('const # = Object.create(#)', [id, root]));
- exports.add(JS.NameSpecifier(id));
- }
-
- for (var unit in compilationUnits) {
- var library = unit.declaredElement.library;
- if (unit.declaredElement != library.definingCompilationUnit) continue;
-
- var libraryTemp = isSdkInternalRuntime(library)
- ? runtimeModule
- : JS.TemporaryId(jsLibraryName(_libraryRoot, library));
- _libraries[library] = libraryTemp;
- emitLibrary(libraryTemp);
- }
-
- // dart:_runtime has a magic module that holds extension method symbols.
- // TODO(jmesserly): find a cleaner design for this.
- if (isBuildingSdk) emitLibrary(_extensionSymbolsModule);
-
- items.add(JS.ExportDeclaration(JS.ExportClause(exports)));
-
// Collect all class/type Element -> Node mappings
// in case we need to forward declare any classes.
_declarationNodes = HashMap<TypeDefiningElement, AstNode>();
@@ -363,50 +319,15 @@
// Visit directives (for exports)
compilationUnits.forEach(_emitExportDirectives);
- // Declare imports
- _finishImports(items);
- // Initialize extension symbols
- _extensionSymbols.forEach((name, id) {
- JS.Expression value =
- JS.PropertyAccess(_extensionSymbolsModule, _propertyName(name));
- if (isBuildingSdk) {
- value = js.call('# = Symbol(#)', [value, js.string("dartx.$name")]);
- }
- items.add(js.statement('const # = #;', [id, value]));
- });
-
- _emitDebuggerExtensionInfo(options.moduleName);
+ // Declare imports and extension symbols
+ emitImportsAndExtensionSymbols(items);
// Discharge the type table cache variables and
// hoisted definitions.
items.addAll(_typeTable.discharge());
items.addAll(_internalSdkFunctions);
- // Add the module's code (produced by visiting compilation units, above)
- _copyAndFlattenBlocks(items, moduleItems);
-
- // Build the module.
- return JS.Program(items, name: options.moduleName);
- }
-
- void _emitDebuggerExtensionInfo(String name) {
- var properties = <JS.Property>[];
- _libraries.forEach((library, value) {
- // TODO(jacobr): we could specify a short library name instead of the
- // full library uri if we wanted to save space.
- properties.add(JS.Property(
- js.escapedString(jsLibraryDebuggerName(_libraryRoot, library)),
- value));
- });
-
- // Track the module name for each library in the module.
- // This data is only required for debugging.
- moduleItems.add(js
- .statement('#.trackLibraries(#, #, ${JSModuleFile.sourceMapHoleID});', [
- runtimeModule,
- js.string(name),
- JS.ObjectInitializer(properties, multiline: true)
- ]));
+ return finishModule(items, options.moduleName);
}
/// If [e] is a property accessor element, this returns the
@@ -494,22 +415,54 @@
return name;
}
- /// Flattens blocks in [items] to a single list.
+ /// Choose a canonical name from the [library] element.
///
- /// This will not flatten blocks that are marked as being scopes.
- void _copyAndFlattenBlocks(
- List<JS.ModuleItem> result, Iterable<JS.ModuleItem> items) {
- for (var item in items) {
- if (item is JS.Block && !item.isScope) {
- _copyAndFlattenBlocks(result, item.statements);
- } else if (item != null) {
- result.add(item);
- }
+ /// This never uses the library's name (the identifier in the `library`
+ /// declaration) as it doesn't have any meaningful rules enforced.
+ @override
+ String jsLibraryName(LibraryElement library) {
+ var uri = library.source.uri;
+ if (uri.scheme == 'dart') {
+ return uri.path;
}
+ // TODO(vsm): This is not necessarily unique if '__' appears in a file name.
+ var encodedSeparator = '__';
+ String qualifiedPath;
+ if (uri.scheme == 'package') {
+ // Strip the package name.
+ // TODO(vsm): This is not unique if an escaped '/'appears in a filename.
+ // E.g., "foo/bar.dart" and "foo$47bar.dart" would collide.
+ qualifiedPath = uri.pathSegments.skip(1).join(encodedSeparator);
+ } else {
+ qualifiedPath = path
+ .relative(uri.toFilePath(), from: _libraryRoot)
+ .replaceAll(path.separator, encodedSeparator)
+ .replaceAll('..', encodedSeparator);
+ }
+ return pathToJSIdentifier(qualifiedPath);
}
- String _libraryToModule(LibraryElement library) {
- assert(!_libraries.containsKey(library));
+ /// Debugger friendly name for a Dart Library.
+ @override
+ String jsLibraryDebuggerName(LibraryElement library) {
+ var uri = library.source.uri;
+ // For package: and dart: uris show the entire
+ if (uri.scheme == 'dart' || uri.scheme == 'package') return uri.toString();
+
+ var filePath = uri.toFilePath();
+ // Relative path to the library.
+ return path.relative(filePath, from: _libraryRoot);
+ }
+
+ /// Returns true if the library [l] is dart:_runtime.
+ @override
+ bool isSdkInternalRuntime(LibraryElement l) {
+ var uri = l.source.uri;
+ return uri.scheme == 'dart' && uri.path == '_runtime';
+ }
+
+ @override
+ String libraryToModule(LibraryElement library) {
var source = library.source;
// TODO(jmesserly): we need to split out HTML.
if (source.uri.scheme == 'dart') {
@@ -524,39 +477,6 @@
return moduleName;
}
- void _finishImports(List<JS.ModuleItem> items) {
- var modules = Map<String, List<LibraryElement>>();
-
- for (var import in _imports.keys) {
- modules.putIfAbsent(_libraryToModule(import), () => []).add(import);
- }
-
- String coreModuleName;
- if (!_libraries.containsKey(coreLibrary)) {
- coreModuleName = _libraryToModule(coreLibrary);
- }
- modules.forEach((module, libraries) {
- // Generate import directives.
- //
- // Our import variables are temps and can get renamed. Since our renaming
- // is integrated into js_ast, it is aware of this possibility and will
- // generate an "as" if needed. For example:
- //
- // import {foo} from 'foo'; // if no rename needed
- // import {foo as foo$} from 'foo'; // if rename was needed
- //
- var imports =
- libraries.map((l) => JS.NameSpecifier(_imports[l])).toList();
- if (module == coreModuleName) {
- imports.add(JS.NameSpecifier(runtimeModule));
- imports.add(JS.NameSpecifier(_extensionSymbolsModule));
- }
-
- items.add(JS.ImportDeclaration(
- namedImports: imports, from: js.string(module, "'")));
- });
- }
-
/// Called to emit class declarations.
///
/// During the course of emitting one item, we may emit another. For example
@@ -1506,7 +1426,7 @@
// Dart does not use ES6 constructors.
// Add an error to catch any invalid usage.
jsMethods
- .add(JS.Method(_propertyName('constructor'), js.fun(r'''function() {
+ .add(JS.Method(propertyName('constructor'), js.fun(r'''function() {
throw Error("use `new " + #.typeName(#.getReifiedType(this)) +
".new(...)` to create a Dart object");
}''', [runtimeModule, runtimeModule])));
@@ -1613,7 +1533,7 @@
if (param.kind == ParameterKind.NAMED) {
foundNamedParams = true;
- var name = _propertyName(param.name);
+ var name = propertyName(param.name);
body.add(js.statement('if (# in #) #;', [
name,
namedArgumentTemp,
@@ -1743,7 +1663,7 @@
// Sort the names to match dart2js order.
var sortedNames = (namedParameterTypes.keys.toList())..sort();
var named = sortedNames
- .map((n) => JS.Property(_propertyName(n), JS.Identifier(n)));
+ .map((n) => JS.Property(propertyName(n), JS.Identifier(n)));
addProperty('namedArguments', JS.ObjectInitializer(named.toList()));
positionalArgs.removeLast();
}
@@ -2085,7 +2005,7 @@
if (extensions.isEmpty) return;
var names = extensions
- .map((e) => _propertyName(JS.memberNameForDartMember(e)))
+ .map((e) => propertyName(JS.memberNameForDartMember(e)))
.toList();
body.add(js.statement('#.#(#, #);', [
runtimeModule,
@@ -2119,7 +2039,7 @@
var proto = classElem.type.isObject
? js.call('Object.create(null)')
: runtimeCall('get${name}s(#.__proto__)', [className]);
- elements.insert(0, JS.Property(_propertyName('__proto__'), proto));
+ elements.insert(0, JS.Property(propertyName('__proto__'), proto));
}
body.add(runtimeStatement('set${name}Signature(#, () => #)', [
className,
@@ -2333,7 +2253,7 @@
JS.Expression _constructorName(String name) {
if (name == '') {
// Default constructors (factory or not) use `new` as their name.
- return _propertyName('new');
+ return propertyName('new');
}
return _emitStaticMemberName(name);
}
@@ -2718,7 +2638,7 @@
JS.Method _emitTopLevelProperty(FunctionDeclaration node) {
var name = node.name.name;
return JS.Method(
- _propertyName(name), _emitFunctionExpression(node.functionExpression),
+ propertyName(name), _emitFunctionExpression(node.functionExpression),
isGetter: node.isGetter, isSetter: node.isSetter)
..sourceInformation = _functionEnd(node);
}
@@ -2890,7 +2810,7 @@
t = covariantParams.lookup(t) as TypeParameterElement;
if (t != null) {
body.add(runtimeStatement('checkTypeBound(#, #, #)',
- [_emitType(t.type), _emitType(t.bound), _propertyName(t.name)]));
+ [_emitType(t.type), _emitType(t.bound), propertyName(t.name)]));
}
}
}
@@ -3230,7 +3150,7 @@
{bool cacheType = true}) {
var properties = <JS.Property>[];
types.forEach((name, type) {
- var key = _propertyName(name);
+ var key = propertyName(name);
var value = _emitType(type, cacheType: cacheType);
properties.add(JS.Property(key, value));
});
@@ -3421,7 +3341,7 @@
/// function does not handle JS interop.
JS.Expression _emitTopLevelMemberName(Element e, {String suffix = ''}) {
var name = getJSExportName(e) ?? _getElementName(e);
- return _propertyName(name + suffix);
+ return propertyName(name + suffix);
}
@override
@@ -3686,7 +3606,7 @@
}
}
if (e.name == 'extensionSymbol' && firstArg is StringLiteral) {
- return _getExtensionSymbolInternal(firstArg.stringValue);
+ return getExtensionSymbolInternal(firstArg.stringValue);
}
}
@@ -4125,7 +4045,7 @@
JS.Property visitNamedExpression(NamedExpression node) {
assert(node.parent is ArgumentList);
return JS.Property(
- _propertyName(node.name.label.name), _visitExpression(node.expression));
+ propertyName(node.name.label.name), _visitExpression(node.expression));
}
List<JS.Parameter> _emitParametersForElement(ExecutableElement member) {
@@ -4526,7 +4446,7 @@
var args = ctor.positionalArguments.map(_emitDartObject).toList();
var named = <JS.Property>[];
ctor.namedArguments.forEach((name, value) {
- named.add(JS.Property(_propertyName(name), _emitDartObject(value)));
+ named.add(JS.Property(propertyName(name), _emitDartObject(value)));
});
if (named.isNotEmpty) args.add(JS.ObjectInitializer(named));
return _emitInstanceCreationExpression(ctor.constructor, type, args,
@@ -6113,11 +6033,11 @@
var runtimeName = getJSExportName(element);
if (runtimeName != null) {
var parts = runtimeName.split('.');
- if (parts.length < 2) return _propertyName(runtimeName);
+ if (parts.length < 2) return propertyName(runtimeName);
JS.Expression result = JS.Identifier(parts[0]);
for (int i = 1; i < parts.length; i++) {
- result = JS.PropertyAccess(result, _propertyName(parts[i]));
+ result = JS.PropertyAccess(result, propertyName(parts[i]));
}
return result;
}
@@ -6132,9 +6052,9 @@
// actually try to access those JS members via interop.
name = JS.memberNameForDartMember(name, _isExternal(element));
if (useExtension) {
- return _getExtensionSymbolInternal(name);
+ return getExtensionSymbolInternal(name);
}
- return _propertyName(name);
+ return propertyName(name);
}
/// Emits the name of a static member, suitable for use in a JS property
@@ -6173,20 +6093,7 @@
name += '_';
}
}
- return _propertyName(name);
- }
-
- /// This is an internal method used by [_emitMemberName] and the
- /// optimized `dart:_runtime extensionSymbol` builtin to get the symbol
- /// for `dartx.<name>`.
- ///
- /// Do not call this directly; you want [_emitMemberName], which knows how to
- /// handle the many details involved in naming.
- JS.TemporaryId _getExtensionSymbolInternal(String name) {
- return _extensionSymbols.putIfAbsent(
- name,
- () => JS.TemporaryId(
- '\$${JS.friendlyNameForDartOperator[name] ?? name}'));
+ return propertyName(name);
}
var _forwardingCache = HashMap<Element, Map<String, Element>>();
@@ -6253,14 +6160,6 @@
return false;
}
- /// Returns the canonical name to refer to the Dart library.
- JS.Identifier emitLibraryName(LibraryElement library) {
- // It's either one of the libraries in this module, or it's an import.
- return _libraries[library] ??
- _imports.putIfAbsent(library,
- () => JS.TemporaryId(jsLibraryName(_libraryRoot, library)));
- }
-
/// Return true if this is one of the methods/properties on all Dart Objects
/// (toString, hashCode, noSuchMethod, runtimeType, ==).
bool isObjectMember(String name) {
@@ -6668,49 +6567,6 @@
_unreachable(node);
}
-/// Choose a canonical name from the [library] element.
-///
-/// This never uses the library's name (the identifier in the `library`
-/// declaration) as it doesn't have any meaningful rules enforced.
-String jsLibraryName(String libraryRoot, LibraryElement library) {
- var uri = library.source.uri;
- if (uri.scheme == 'dart') {
- return uri.path;
- }
- // TODO(vsm): This is not necessarily unique if '__' appears in a file name.
- var encodedSeparator = '__';
- String qualifiedPath;
- if (uri.scheme == 'package') {
- // Strip the package name.
- // TODO(vsm): This is not unique if an escaped '/'appears in a filename.
- // E.g., "foo/bar.dart" and "foo$47bar.dart" would collide.
- qualifiedPath = uri.pathSegments.skip(1).join(encodedSeparator);
- } else {
- qualifiedPath = path
- .relative(uri.toFilePath(), from: libraryRoot)
- .replaceAll(path.separator, encodedSeparator)
- .replaceAll('..', encodedSeparator);
- }
- return pathToJSIdentifier(qualifiedPath);
-}
-
-/// Debugger friendly name for a Dart Library.
-String jsLibraryDebuggerName(String libraryRoot, LibraryElement library) {
- var uri = library.source.uri;
- // For package: and dart: uris show the entire
- if (uri.scheme == 'dart' || uri.scheme == 'package') return uri.toString();
-
- var filePath = uri.toFilePath();
- // Relative path to the library.
- return path.relative(filePath, from: libraryRoot);
-}
-
-/// Shorthand for identifier-like property names.
-/// For now, we emit them as strings and the printer restores them to
-/// identifiers if it can.
-// TODO(jmesserly): avoid the round tripping through quoted form.
-JS.LiteralString _propertyName(String name) => js.string(name, "'");
-
// TODO(jacobr): we would like to do something like the following
// but we don't have summary support yet.
// bool _supportJsExtensionMethod(AnnotatedNode node) =>
diff --git a/pkg/dev_compiler/lib/src/analyzer/module_compiler.dart b/pkg/dev_compiler/lib/src/analyzer/module_compiler.dart
index 7ad873d..3997597 100644
--- a/pkg/dev_compiler/lib/src/analyzer/module_compiler.dart
+++ b/pkg/dev_compiler/lib/src/analyzer/module_compiler.dart
@@ -19,6 +19,7 @@
import '../compiler/module_builder.dart'
show transformModuleFormat, ModuleFormat;
import '../compiler/shared_command.dart';
+import '../compiler/shared_compiler.dart';
import '../js_ast/js_ast.dart' as JS;
import '../js_ast/js_ast.dart' show js;
import '../js_ast/source_map_printer.dart' show SourceMapPrintingContext;
@@ -135,9 +136,6 @@
/// into the output JavaScript module.
final bool sourceMapComment;
- /// Whether to emit the source mapping file inline as a data url.
- final bool inlineSourceMap;
-
/// The file extension for summaries.
final String summaryExtension;
@@ -159,7 +157,6 @@
CompilerOptions(
{bool sourceMap = true,
this.sourceMapComment = true,
- this.inlineSourceMap = false,
bool summarizeApi = true,
this.summaryExtension = 'sum',
this.unsafeForceCompile = false,
@@ -182,7 +179,6 @@
CompilerOptions.fromArguments(ArgResults args)
: sourceMapComment = args['source-map-comment'] as bool,
- inlineSourceMap = args['inline-source-map'] as bool,
summaryExtension = args['summary-extension'] as String,
unsafeForceCompile = args['unsafe-force-compile'] as bool,
summaryOutPath = args['summary-out'] as String,
@@ -203,8 +199,6 @@
'disable if using X-SourceMap header',
defaultsTo: true,
hide: hide)
- ..addFlag('inline-source-map',
- help: 'emit source mapping inline', defaultsTo: false, hide: hide)
..addFlag('unsafe-force-compile',
help: 'Compile code even if it has errors. ಠ_ಠ\n'
'This has undefined behavior!',
@@ -242,13 +236,6 @@
/// the libraries in this module.
final List<int> summaryBytes;
- /// Unique identifier indicating hole to inline the source map.
- ///
- /// We cannot generate the source map before the script it is for is
- /// generated so we have generate the script including this id and then
- /// replace the ID once the source map is generated.
- static String sourceMapHoleID = 'SourceMap3G5a8h6JVhHfdGuDxZr1EF9GQC8y0e6u';
-
JSModuleFile(this.errors, this.options, this.moduleTree, this.summaryBytes);
JSModuleFile.invalid(this.errors, this.options)
@@ -303,7 +290,7 @@
var rawSourceMap = options.inlineSourceMap
? js.escapedString(json.encode(builtMap), "'").value
: 'null';
- text = text.replaceFirst(sourceMapHoleID, rawSourceMap);
+ text = text.replaceFirst(SharedCompiler.sourceMapLocationID, rawSourceMap);
return JSModuleCode(text, builtMap);
}
diff --git a/pkg/dev_compiler/lib/src/compiler/shared_command.dart b/pkg/dev_compiler/lib/src/compiler/shared_command.dart
index 0c7c246..50f1405 100644
--- a/pkg/dev_compiler/lib/src/compiler/shared_command.dart
+++ b/pkg/dev_compiler/lib/src/compiler/shared_command.dart
@@ -56,6 +56,10 @@
/// code.
final bool sourceMap;
+ /// Whether to emit the source mapping file in the program text, so the
+ /// runtime can enable synchronous stack trace deobsfuscation.
+ final bool inlineSourceMap;
+
/// Whether to emit a summary file containing API signatures.
///
/// This is required for a modular build process.
@@ -95,6 +99,7 @@
SharedCompilerOptions(
{this.sourceMap = true,
+ this.inlineSourceMap = false,
this.summarizeApi = true,
this.emitMetadata = false,
this.enableAsserts = true,
@@ -109,6 +114,7 @@
[String moduleRoot, String summaryExtension])
: this(
sourceMap: args['source-map'] as bool,
+ inlineSourceMap: args['inline-source-map'] as bool,
summarizeApi: args['summarize'] as bool,
emitMetadata: args['emit-metadata'] as bool,
enableAsserts: args['enable-asserts'] as bool,
@@ -137,6 +143,8 @@
help: 'emit an API summary file', defaultsTo: true, hide: hide)
..addFlag('source-map',
help: 'emit source mapping', defaultsTo: true, hide: hide)
+ ..addFlag('inline-source-map',
+ help: 'emit source mapping inline', defaultsTo: false, hide: hide)
..addFlag('emit-metadata',
help: 'emit metadata annotations queriable via mirrors', hide: hide)
..addFlag('enable-asserts',
diff --git a/pkg/dev_compiler/lib/src/compiler/shared_compiler.dart b/pkg/dev_compiler/lib/src/compiler/shared_compiler.dart
index 7b6aae5..1bfdecd 100644
--- a/pkg/dev_compiler/lib/src/compiler/shared_compiler.dart
+++ b/pkg/dev_compiler/lib/src/compiler/shared_compiler.dart
@@ -22,18 +22,46 @@
/// This lets DDC use the setter method's return value directly.
final List<JS.Identifier> _operatorSetResultStack = [];
+ /// Private member names in this module, organized by their library.
+ final _privateNames = HashMap<Library, HashMap<String, JS.TemporaryId>>();
+
+ /// Extension member symbols for adding Dart members to JS types.
+ ///
+ /// These are added to the [extensionSymbolsModule]; see that field for more
+ /// information.
+ final _extensionSymbols = <String, JS.TemporaryId>{};
+
+ /// The set of libraries we are currently compiling, and the temporaries used
+ /// to refer to them.
+ final _libraries = <Library, JS.Identifier>{};
+
+ /// Imported libraries, and the temporaries used to refer to them.
+ final _imports = <Library, JS.TemporaryId>{};
+
/// The identifier used to reference DDC's core "dart:_runtime" library from
/// generated JS code, typically called "dart" e.g. `dart.dcall`.
@protected
JS.Identifier runtimeModule;
+ /// The identifier used to reference DDC's "extension method" symbols, used to
+ /// safely add Dart-specific member names to JavaScript classes, such as
+ /// primitive types (e.g. String) or DOM types in "dart:html".
+ @protected
+ JS.Identifier extensionSymbolsModule;
+
+ /// Whether we're currently building the SDK, which may require special
+ /// bootstrapping logic.
+ ///
+ /// This is initialized by [startModule], which must be called before
+ /// accessing this field.
+ @protected
+ bool isBuildingSdk;
+
/// The temporary variable that stores named arguments (these are passed via a
/// JS object literal, to match JS conventions).
@protected
final namedArgumentTemp = JS.TemporaryId('opts');
- final _privateNames = HashMap<Library, HashMap<String, JS.TemporaryId>>();
-
/// The list of output module items, in the order they need to be emitted in.
@protected
final moduleItems = <JS.ModuleItem>[];
@@ -42,6 +70,7 @@
///
/// This is used for deferred supertypes of mutually recursive non-generic
/// classes.
+ @protected
final afterClassDefItems = <JS.ModuleItem>[];
/// The type used for private Dart [Symbol]s.
@@ -52,9 +81,14 @@
@protected
InterfaceType get internalSymbolType;
+ /// The current library being compiled.
@protected
Library get currentLibrary;
+ /// The library for dart:core in the SDK.
+ @protected
+ Library get coreLibrary;
+
/// The import URI of current library.
@protected
Uri get currentLibraryUri;
@@ -63,6 +97,25 @@
@protected
FunctionNode get currentFunction;
+ /// Choose a canonical name from the [library] element.
+ ///
+ /// This never uses the library's name (the identifier in the `library`
+ /// declaration) as it doesn't have any meaningful rules enforced.
+ @protected
+ String jsLibraryName(Library library);
+
+ /// Debugger friendly name for a Dart [library].
+ @protected
+ String jsLibraryDebuggerName(Library library);
+
+ /// Gets the module import URI that contains [library].
+ @protected
+ String libraryToModule(Library library);
+
+ /// Returns true if the library [l] is "dart:_runtime".
+ @protected
+ bool isSdkInternalRuntime(Library l);
+
/// Whether any superclass of [c] defines a static [name].
@protected
bool superclassHasStatic(Class c, String name);
@@ -289,6 +342,214 @@
@protected
JS.Expression canonicalizeConstObject(JS.Expression expr) =>
cacheConst(runtimeCall('const(#)', expr));
+
+ /// Emits preamble for the module containing [libraries], and returns the
+ /// list of module items for further items to be added.
+ ///
+ /// The preamble consists of initializing the identifiers for each library,
+ /// that will be used to store their members. It also generates the
+ /// appropriate ES6 `export` declaration to export them from this module.
+ ///
+ /// After the code for all of the library members is emitted,
+ /// [emitImportsAndExtensionSymbols] should be used to emit imports/extension
+ /// symbols into the list returned by this method. Finally, [finishModule]
+ /// can be called to complete the module and return the resulting JS AST.
+ ///
+ /// This also initializes several fields: [isBuildingSdk], [runtimeModule],
+ /// [extensionSymbolsModule], as well as the [_libraries] map needed by
+ /// [emitLibraryName].
+ @protected
+ List<JS.ModuleItem> startModule(Iterable<Library> libraries) {
+ isBuildingSdk = libraries.any(isSdkInternalRuntime);
+ if (isBuildingSdk) {
+ // Don't allow these to be renamed when we're building the SDK.
+ // There is JS code in dart:* that depends on their names.
+ runtimeModule = JS.Identifier('dart');
+ extensionSymbolsModule = JS.Identifier('dartx');
+ } else {
+ // Otherwise allow these to be renamed so users can write them.
+ runtimeModule = JS.TemporaryId('dart');
+ extensionSymbolsModule = JS.TemporaryId('dartx');
+ }
+
+ // Initialize our library variables.
+ var items = <JS.ModuleItem>[];
+ var exports = <JS.NameSpecifier>[];
+
+ if (isBuildingSdk) {
+ // Bootstrap the ability to create Dart library objects.
+ var libraryProto = JS.TemporaryId('_library');
+ items.add(js.statement('const # = Object.create(null)', libraryProto));
+ items.add(js.statement(
+ 'const # = Object.create(#)', [runtimeModule, libraryProto]));
+ items.add(js.statement('#.library = #', [runtimeModule, libraryProto]));
+ exports.add(JS.NameSpecifier(runtimeModule));
+ }
+
+ for (var library in libraries) {
+ if (isBuildingSdk && isSdkInternalRuntime(library)) {
+ _libraries[library] = runtimeModule;
+ continue;
+ }
+ var id = JS.TemporaryId(jsLibraryName(library));
+ _libraries[library] = id;
+
+ items.add(js.statement(
+ 'const # = Object.create(#.library)', [id, runtimeModule]));
+ exports.add(JS.NameSpecifier(id));
+ }
+
+ // dart:_runtime has a magic module that holds extension method symbols.
+ // TODO(jmesserly): find a cleaner design for this.
+ if (isBuildingSdk) {
+ var id = extensionSymbolsModule;
+ items.add(js.statement(
+ 'const # = Object.create(#.library)', [id, runtimeModule]));
+ exports.add(JS.NameSpecifier(id));
+ }
+
+ items.add(JS.ExportDeclaration(JS.ExportClause(exports)));
+ return items;
+ }
+
+ /// Returns the canonical name to refer to the Dart library.
+ @protected
+ JS.Identifier emitLibraryName(Library library) {
+ // It's either one of the libraries in this module, or it's an import.
+ return _libraries[library] ??
+ _imports.putIfAbsent(
+ library, () => JS.TemporaryId(jsLibraryName(library)));
+ }
+
+ /// Emits imports and extension methods into [items].
+ @protected
+ void emitImportsAndExtensionSymbols(List<JS.ModuleItem> items) {
+ var modules = Map<String, List<Library>>();
+
+ for (var import in _imports.keys) {
+ modules.putIfAbsent(libraryToModule(import), () => []).add(import);
+ }
+
+ String coreModuleName;
+ if (!_libraries.containsKey(coreLibrary)) {
+ coreModuleName = libraryToModule(coreLibrary);
+ }
+ modules.forEach((module, libraries) {
+ // Generate import directives.
+ //
+ // Our import variables are temps and can get renamed. Since our renaming
+ // is integrated into js_ast, it is aware of this possibility and will
+ // generate an "as" if needed. For example:
+ //
+ // import {foo} from 'foo'; // if no rename needed
+ // import {foo as foo$} from 'foo'; // if rename was needed
+ //
+ var imports =
+ libraries.map((l) => JS.NameSpecifier(_imports[l])).toList();
+ if (module == coreModuleName) {
+ imports.add(JS.NameSpecifier(runtimeModule));
+ imports.add(JS.NameSpecifier(extensionSymbolsModule));
+ }
+
+ items.add(JS.ImportDeclaration(
+ namedImports: imports, from: js.string(module, "'")));
+ });
+
+ // Initialize extension symbols
+ _extensionSymbols.forEach((name, id) {
+ JS.Expression value =
+ JS.PropertyAccess(extensionSymbolsModule, propertyName(name));
+ if (isBuildingSdk) {
+ value = js.call('# = Symbol(#)', [value, js.string("dartx.$name")]);
+ }
+ items.add(js.statement('const # = #;', [id, value]));
+ });
+ }
+
+ void _emitDebuggerExtensionInfo(String name) {
+ var properties = <JS.Property>[];
+ _libraries.forEach((library, value) {
+ // TODO(jacobr): we could specify a short library name instead of the
+ // full library uri if we wanted to save space.
+ properties.add(
+ JS.Property(js.escapedString(jsLibraryDebuggerName(library)), value));
+ });
+ var module = JS.ObjectInitializer(properties, multiline: true);
+
+ // Track the module name for each library in the module.
+ // This data is only required for debugging.
+ moduleItems.add(js.statement(
+ '#.trackLibraries(#, #, $sourceMapLocationID);',
+ [runtimeModule, js.string(name), module]));
+ }
+
+ /// Finishes the module created by [startModule], by combining the preable
+ /// [items] with the [moduleItems] that have been emitted.
+ ///
+ /// The [moduleName] should specify the module's name, and the items should
+ /// be the list resulting from startModule, with additional items added,
+ /// but not including the contents of moduleItems (which will be handled by
+ /// this method itself).
+ ///
+ /// Note, this function mutates the items list and returns it as the `body`
+ /// field of the result.
+ @protected
+ JS.Program finishModule(List<JS.ModuleItem> items, String moduleName) {
+ // TODO(jmesserly): there's probably further consolidation we can do
+ // between DDC's two backends, by moving more code into this method, as the
+ // code between `startModule` and `finishModule` is very similar in both.
+ _emitDebuggerExtensionInfo(moduleName);
+
+ // Add the module's code (produced by visiting compilation units, above)
+ _copyAndFlattenBlocks(items, moduleItems);
+ moduleItems.clear();
+
+ // Build the module.
+ return JS.Program(items, name: moduleName);
+ }
+
+ /// Flattens blocks in [items] to a single list.
+ ///
+ /// This will not flatten blocks that are marked as being scopes.
+ void _copyAndFlattenBlocks(
+ List<JS.ModuleItem> result, Iterable<JS.ModuleItem> items) {
+ for (var item in items) {
+ if (item is JS.Block && !item.isScope) {
+ _copyAndFlattenBlocks(result, item.statements);
+ } else if (item != null) {
+ result.add(item);
+ }
+ }
+ }
+
+ /// This is an internal method used by [_emitMemberName] and the
+ /// optimized `dart:_runtime extensionSymbol` builtin to get the symbol
+ /// for `dartx.<name>`.
+ ///
+ /// Do not call this directly; you want [_emitMemberName], which knows how to
+ /// handle the many details involved in naming.
+ @protected
+ JS.TemporaryId getExtensionSymbolInternal(String name) {
+ return _extensionSymbols.putIfAbsent(
+ name,
+ () => JS.TemporaryId(
+ '\$${JS.friendlyNameForDartOperator[name] ?? name}'));
+ }
+
+ /// Shorthand for identifier-like property names.
+ /// For now, we emit them as strings and the printer restores them to
+ /// identifiers if it can.
+ // TODO(jmesserly): avoid the round tripping through quoted form.
+ @protected
+ JS.LiteralString propertyName(String name) => js.string(name, "'");
+
+ /// Unique identifier indicating the location to inline the source map.
+ ///
+ /// We cannot generate the source map before the script it is for is
+ /// generated so we have generate the script including this identifier in the
+ /// JS AST, and then replace it once the source map is generated.
+ static const String sourceMapLocationID =
+ 'SourceMap3G5a8h6JVhHfdGuDxZr1EF9GQC8y0e6u';
}
/// Whether a variable with [name] is referenced in the [node].
diff --git a/pkg/dev_compiler/lib/src/kernel/command.dart b/pkg/dev_compiler/lib/src/kernel/command.dart
index e7a764c..317ee17 100644
--- a/pkg/dev_compiler/lib/src/kernel/command.dart
+++ b/pkg/dev_compiler/lib/src/kernel/command.dart
@@ -9,7 +9,6 @@
import 'package:args/args.dart';
import 'package:build_integration/file_system/multi_root.dart';
import 'package:cli_util/cli_util.dart' show getSdkPath;
-import 'package:dev_compiler/src/flutter/track_widget_constructor_locations.dart';
import 'package:front_end/src/api_unstable/ddc.dart' as fe;
import 'package:kernel/kernel.dart' hide MapEntry;
import 'package:kernel/text/ast_to_text.dart' as kernel show Printer;
@@ -20,7 +19,10 @@
import '../compiler/js_names.dart' as JS;
import '../compiler/module_builder.dart';
import '../compiler/shared_command.dart';
+import '../compiler/shared_compiler.dart';
+import '../flutter/track_widget_constructor_locations.dart';
import '../js_ast/js_ast.dart' as JS;
+import '../js_ast/js_ast.dart' show js;
import '../js_ast/source_map_printer.dart' show SourceMapPrintingContext;
import 'analyzer_to_kernel.dart';
@@ -275,7 +277,8 @@
// --single-out-file is used, but that option does not appear to be used by
// any of our build systems.
var jsCode = jsProgramToCode(jsModule, options.moduleFormats.first,
- buildSourceMap: argResults['source-map'] as bool,
+ buildSourceMap: options.sourceMap,
+ inlineSourceMap: options.inlineSourceMap,
jsUrl: path.toUri(output).toString(),
mapUrl: path.toUri(output + '.map').toString(),
bazelMapping: options.bazelMapping,
@@ -311,6 +314,7 @@
JSCode jsProgramToCode(JS.Program moduleTree, ModuleFormat format,
{bool buildSourceMap = false,
+ bool inlineSourceMap = false,
String jsUrl,
String mapUrl,
Map<String, String> bazelMapping,
@@ -344,6 +348,10 @@
}
var text = printer.getText();
+ var rawSourceMap = inlineSourceMap
+ ? js.escapedString(json.encode(builtMap), "'").value
+ : 'null';
+ text = text.replaceFirst(SharedCompiler.sourceMapLocationID, rawSourceMap);
return JSCode(text, builtMap);
}
diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart
index 36e9d0c..fdd1c6f 100644
--- a/pkg/dev_compiler/lib/src/kernel/compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart
@@ -40,13 +40,6 @@
ConstantVisitor<JS.Expression> {
final SharedCompilerOptions options;
- /// The set of libraries we are currently compiling, and the temporaries used
- /// to refer to them.
- ///
- /// We sometimes special case codegen for a single library, as it simplifies
- /// name scoping requirements.
- final _libraries = Map<Library, JS.Identifier>.identity();
-
/// Maps a library URI import, that is not in [_libraries], to the
/// corresponding Kernel summary module we imported it with.
final _importToSummary = Map<Library, Component>.identity();
@@ -54,18 +47,12 @@
/// Maps a summary to the JS import name for the module.
final _summaryToModule = Map<Component, String>.identity();
- /// Imported libraries, and the temporaries used to refer to them.
- final _imports = Map<Library, JS.TemporaryId>();
-
/// The variable for the current catch clause
VariableDeclaration _rethrowParameter;
/// In an async* function, this represents the stream controller parameter.
JS.TemporaryId _asyncStarController;
- JS.Identifier _extensionSymbolsModule;
- final _extensionSymbols = Map<String, JS.TemporaryId>();
-
Set<Class> _pendingClasses;
/// Temporary variables mapped to their corresponding JavaScript variable.
@@ -238,6 +225,9 @@
Library get currentLibrary => _currentLibrary;
@override
+ Library get coreLibrary => coreTypes.coreLibrary;
+
+ @override
FunctionNode get currentFunction => _currentFunction;
@override
@@ -266,47 +256,11 @@
}
var libraries = component.libraries.where((l) => !l.isExternal);
- var ddcRuntime =
- libraries.firstWhere(isSdkInternalRuntime, orElse: () => null);
- if (ddcRuntime != null) {
- // Don't allow these to be renamed when we're building the SDK.
- // There is JS code in dart:* that depends on their names.
- runtimeModule = JS.Identifier('dart');
- _extensionSymbolsModule = JS.Identifier('dartx');
- _nullableInference.allowNotNullDeclarations = true;
- } else {
- // Otherwise allow these to be renamed so users can write them.
- runtimeModule = JS.TemporaryId('dart');
- _extensionSymbolsModule = JS.TemporaryId('dartx');
- }
- _typeTable = TypeTable(runtimeModule);
// Initialize our library variables.
- var items = <JS.ModuleItem>[];
- var exports = <JS.NameSpecifier>[];
- // TODO(jmesserly): this is a performance optimization for V8 to prevent it
- // from treating our Dart library objects as JS Maps.
- var root = JS.Identifier('_root');
- items.add(js.statement('const # = Object.create(null)', [root]));
-
- void emitLibrary(JS.Identifier id) {
- items.add(js.statement('const # = Object.create(#)', [id, root]));
- exports.add(JS.NameSpecifier(id));
- }
-
- for (var library in libraries) {
- var libraryTemp = library == ddcRuntime
- ? runtimeModule
- : JS.TemporaryId(jsLibraryName(library));
- _libraries[library] = libraryTemp;
- emitLibrary(libraryTemp);
- }
-
- // dart:_runtime has a magic module that holds extension method symbols.
- // TODO(jmesserly): find a cleaner design for this.
- if (ddcRuntime != null) emitLibrary(_extensionSymbolsModule);
-
- items.add(JS.ExportDeclaration(JS.ExportClause(exports)));
+ var items = startModule(libraries);
+ _nullableInference.allowNotNullDeclarations = isBuildingSdk;
+ _typeTable = TypeTable(runtimeModule);
// Collect all class/type Element -> Node mappings
// in case we need to forward declare any classes.
@@ -331,53 +285,54 @@
// Visit directives (for exports)
libraries.forEach(_emitExports);
- // Declare imports
- _finishImports(items);
- // Initialize extension symbols
- _extensionSymbols.forEach((name, id) {
- JS.Expression value =
- JS.PropertyAccess(_extensionSymbolsModule, _propertyName(name));
- if (ddcRuntime != null) {
- value = js.call('# = Symbol(#)', [value, js.string("dartx.$name")]);
- }
- items.add(js.statement('const # = #;', [id, value]));
- });
+ // Declare imports and extension symbols
+ emitImportsAndExtensionSymbols(items);
// Discharge the type table cache variables and
// hoisted definitions.
items.addAll(_typeTable.discharge());
- // Add the module's code (produced by visiting compilation units, above)
- _copyAndFlattenBlocks(items, moduleItems);
-
- // Build the module.
- return JS.Program(items, name: options.moduleName);
+ return finishModule(items, options.moduleName);
}
- /// Flattens blocks in [items] to a single list.
- ///
- /// This will not flatten blocks that are marked as being scopes.
- void _copyAndFlattenBlocks(
- List<JS.ModuleItem> result, Iterable<JS.ModuleItem> items) {
- for (var item in items) {
- if (item is JS.Block && !item.isScope) {
- _copyAndFlattenBlocks(result, item.statements);
- } else if (item != null) {
- result.add(item);
- }
+ @override
+ String jsLibraryName(Library library) {
+ var uri = library.importUri;
+ if (uri.scheme == 'dart') return uri.path;
+
+ // TODO(vsm): This is not necessarily unique if '__' appears in a file name.
+ Iterable<String> segments;
+ if (uri.scheme == 'package') {
+ // Strip the package name.
+ // TODO(vsm): This is not unique if an escaped '/'appears in a filename.
+ // E.g., "foo/bar.dart" and "foo__bar.dart" would collide.
+ segments = uri.pathSegments.skip(1);
+ } else {
+ // TODO(jmesserly): this is not unique typically.
+ segments = [uri.pathSegments.last];
}
+
+ var qualifiedPath = segments.map((p) => p == '..' ? '' : p).join('__');
+ return pathToJSIdentifier(qualifiedPath);
}
- /// Returns the canonical name to refer to the Dart library.
- JS.Identifier emitLibraryName(Library library) {
- // It's either one of the libraries in this module, or it's an import.
- return _libraries[library] ??
- _imports.putIfAbsent(
- library, () => JS.TemporaryId(jsLibraryName(library)));
+ @override
+ String jsLibraryDebuggerName(Library library) {
+ var uri = library.importUri;
+ // For package: and dart: uris show the entire
+ if (uri.scheme == 'dart' || uri.scheme == 'package') return uri.toString();
+ // TODO(jmesserly): this is not unique typically.
+ return uri.pathSegments.last;
}
- String _libraryToModule(Library library) {
- assert(!_libraries.containsKey(library));
+ @override
+ bool isSdkInternalRuntime(Library l) {
+ var uri = l.importUri;
+ return uri.scheme == 'dart' && uri.path == '_runtime';
+ }
+
+ @override
+ String libraryToModule(Library library) {
if (library.importUri.scheme == 'dart') {
// TODO(jmesserly): we need to split out HTML.
return JS.dartSdkModule;
@@ -391,39 +346,6 @@
return moduleName;
}
- void _finishImports(List<JS.ModuleItem> items) {
- var modules = Map<String, List<Library>>();
-
- for (var import in _imports.keys) {
- modules.putIfAbsent(_libraryToModule(import), () => []).add(import);
- }
-
- String coreModuleName;
- if (!_libraries.containsKey(coreTypes.coreLibrary)) {
- coreModuleName = _libraryToModule(coreTypes.coreLibrary);
- }
- modules.forEach((module, libraries) {
- // Generate import directives.
- //
- // Our import variables are temps and can get renamed. Since our renaming
- // is integrated into js_ast, it is aware of this possibility and will
- // generate an "as" if needed. For example:
- //
- // import {foo} from 'foo'; // if no rename needed
- // import {foo as foo$} from 'foo'; // if rename was needed
- //
- var imports =
- libraries.map((l) => JS.NameSpecifier(_imports[l])).toList();
- if (module == coreModuleName) {
- imports.add(JS.NameSpecifier(runtimeModule));
- imports.add(JS.NameSpecifier(_extensionSymbolsModule));
- }
-
- items.add(JS.ImportDeclaration(
- namedImports: imports, from: js.string(module, "'")));
- });
- }
-
void _emitLibrary(Library library) {
// NOTE: this method isn't the right place to initialize per-library state.
// Classes can be visited out of order, so this is only to catch things that
@@ -1203,7 +1125,7 @@
if (extensions.isEmpty) return;
var names = extensions
- .map((e) => _propertyName(JS.memberNameForDartMember(e)))
+ .map((e) => propertyName(JS.memberNameForDartMember(e)))
.toList();
body.add(js.statement('#.#(#, #);', [
runtimeModule,
@@ -1239,7 +1161,7 @@
var proto = c == coreTypes.objectClass
? js.call('Object.create(null)')
: runtimeCall('get${name}s(#.__proto__)', [className]);
- elements.insert(0, JS.Property(_propertyName('__proto__'), proto));
+ elements.insert(0, JS.Property(propertyName('__proto__'), proto));
}
body.add(runtimeStatement('set${name}Signature(#, () => #)', [
className,
@@ -1480,7 +1402,7 @@
JS.Expression _constructorName(String name) {
if (name == '') {
// Default constructors (factory or not) use `new` as their name.
- return _propertyName('new');
+ return propertyName('new');
}
return _emitStaticMemberName(name);
}
@@ -1658,7 +1580,7 @@
// Dart does not use ES6 constructors.
// Add an error to catch any invalid usage.
jsMethods
- .add(JS.Method(_propertyName('constructor'), js.fun(r'''function() {
+ .add(JS.Method(propertyName('constructor'), js.fun(r'''function() {
throw Error("use `new " + #.typeName(#.getReifiedType(this)) +
".new(...)` to create a Dart object");
}''', [runtimeModule, runtimeModule])));
@@ -1864,7 +1786,7 @@
if (isCovariantParameter(param) &&
!isCovariantParameter(superMember.function.namedParameters
.firstWhere((n) => n.name == param.name))) {
- var name = _propertyName(param.name);
+ var name = propertyName(param.name);
var paramType = superMethodType.namedParameters
.firstWhere((n) => n.name == param.name);
body.add(js.statement('if (# in #) #;', [
@@ -2205,11 +2127,11 @@
var runtimeName = getJSExportName(member);
if (runtimeName != null) {
var parts = runtimeName.split('.');
- if (parts.length < 2) return _propertyName(runtimeName);
+ if (parts.length < 2) return propertyName(runtimeName);
JS.Expression result = JS.Identifier(parts[0]);
for (int i = 1; i < parts.length; i++) {
- result = JS.PropertyAccess(result, _propertyName(parts[i]));
+ result = JS.PropertyAccess(result, propertyName(parts[i]));
}
return result;
}
@@ -2228,22 +2150,9 @@
name = JS.memberNameForDartMember(
name, member is Procedure && member.isExternal);
if (useExtension) {
- return _getExtensionSymbolInternal(name);
+ return getExtensionSymbolInternal(name);
}
- return _propertyName(name);
- }
-
- /// This is an internal method used by [_emitMemberName] and the
- /// optimized `dart:_runtime extensionSymbol` builtin to get the symbol
- /// for `dartx.<name>`.
- ///
- /// Do not call this directly; you want [_emitMemberName], which knows how to
- /// handle the many details involved in naming.
- JS.TemporaryId _getExtensionSymbolInternal(String name) {
- return _extensionSymbols.putIfAbsent(
- name,
- () => JS.TemporaryId(
- '\$${JS.friendlyNameForDartOperator[name] ?? name}'));
+ return propertyName(name);
}
/// Don't symbolize native members that just forward to the underlying
@@ -2322,7 +2231,7 @@
name += '_';
}
}
- return _propertyName(name);
+ return propertyName(name);
}
JS.Expression _emitJSInteropStaticMemberName(NamedNode n) {
@@ -2352,7 +2261,7 @@
/// function does not handle JS interop.
JS.Expression _emitTopLevelMemberName(NamedNode n, {String suffix = ''}) {
var name = getJSExportName(n) ?? getTopLevelName(n);
- return _propertyName(name + suffix);
+ return propertyName(name + suffix);
}
String _getJSNameWithoutGlobal(NamedNode n) {
@@ -2405,7 +2314,7 @@
var name = node.name.name;
var result = JS.Method(
- _propertyName(name), _emitFunction(node.function, node.name.name),
+ propertyName(name), _emitFunction(node.function, node.name.name),
isGetter: node.isGetter, isSetter: node.isSetter)
..sourceInformation = _nodeEnd(node.fileEndOffset);
@@ -2700,7 +2609,7 @@
JS.ObjectInitializer _emitTypeProperties(Iterable<NamedType> types) {
return JS.ObjectInitializer(types
- .map((t) => JS.Property(_propertyName(t.name), _emitType(t.type)))
+ .map((t) => JS.Property(propertyName(t.name), _emitType(t.type)))
.toList());
}
@@ -3020,7 +2929,7 @@
body.add(runtimeStatement('checkTypeBound(#, #, #)', [
_emitType(TypeParameterType(t)),
_emitType(t.bound),
- _propertyName(t.name)
+ propertyName(t.name)
]));
}
}
@@ -4400,7 +4309,7 @@
return _emitType(firstArg.type);
}
if (name == 'extensionSymbol' && firstArg is StringLiteral) {
- return _getExtensionSymbolInternal(firstArg.value);
+ return getExtensionSymbolInternal(firstArg.value);
}
}
if (target == coreTypes.identicalProcedure) {
@@ -4504,7 +4413,7 @@
}
JS.Property _emitNamedExpression(NamedExpression arg) {
- return JS.Property(_propertyName(arg.name), _visitExpression(arg.value));
+ return JS.Property(propertyName(arg.name), _visitExpression(arg.value));
}
/// Emits code for the `JS(...)` macro.
@@ -5168,7 +5077,7 @@
var type = visitInterfaceType(node.getType(types) as InterfaceType);
var prototype = js.call("#.prototype", [type]);
- var properties = [JS.Property(_propertyName("__proto__"), prototype)]
+ var properties = [JS.Property(propertyName("__proto__"), prototype)]
..addAll(node.fieldValues.entries.map(entryToProperty));
return canonicalizeConstObject(
JS.ObjectInitializer(properties, multiline: true));
@@ -5201,39 +5110,6 @@
}
}
-bool isSdkInternalRuntime(Library l) =>
- l.importUri.toString() == 'dart:_runtime';
-
-/// Choose a canonical name from the [library] element.
-///
-/// This never uses the library's name (the identifier in the `library`
-/// declaration) as it doesn't have any meaningful rules enforced.
-String jsLibraryName(Library library) {
- var uri = library.importUri;
- if (uri.scheme == 'dart') return uri.path;
-
- // TODO(vsm): This is not necessarily unique if '__' appears in a file name.
- Iterable<String> segments;
- if (uri.scheme == 'package') {
- // Strip the package name.
- // TODO(vsm): This is not unique if an escaped '/'appears in a filename.
- // E.g., "foo/bar.dart" and "foo__bar.dart" would collide.
- segments = uri.pathSegments.skip(1);
- } else {
- // TODO(jmesserly): this is not unique typically.
- segments = [uri.pathSegments.last];
- }
-
- var qualifiedPath = segments.map((p) => p == '..' ? '' : p).join('__');
- return pathToJSIdentifier(qualifiedPath);
-}
-
-/// Shorthand for identifier-like property names.
-/// For now, we emit them as strings and the printer restores them to
-/// identifiers if it can.
-// TODO(jmesserly): avoid the round tripping through quoted form.
-JS.LiteralString _propertyName(String name) => js.string(name, "'");
-
bool _isInlineJSFunction(Statement body) {
var block = body;
if (block is Block) {
diff --git a/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/runtime.dart b/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/runtime.dart
index 7b161fb..665aae3 100644
--- a/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/runtime.dart
+++ b/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/runtime.dart
@@ -147,6 +147,22 @@
final JsSymbol = JS('', 'Symbol');
+/// The prototype used for all Dart libraries.
+///
+/// This makes it easy to identify Dart library objects, and also improves
+/// performance (JS engines such as V8 tend to assume `Object.create(null)` is
+/// used for a Map, so they don't optimize it as they normally would for
+/// class-like objects).
+///
+/// The `dart.library` field is set by the compiler during SDK bootstrapping
+/// (because it is needed for dart:_runtime itself), so we don't need to
+/// initialize it here. The name `dart.library` is used because it reads nicely,
+/// for example:
+///
+/// const my_library = Object.create(dart.library);
+///
+Object libraryPrototype = JS('', 'dart.library');
+
// TODO(vsm): Remove once this flag we've removed the ability to
// whitelist / fallback on the old behavior.
bool startAsyncSynchronously = true;