First version of incremental DDC mode for expression compilation

- Support evaluate() calls from VM service in expression compiler
  - emit all accessed symbols, types, constants, extension symbols,
    and imports as part of synthetic evaluation function
    - Note: this fixes missing symbol issues in evaluateInFrame()
      as well
  - update expression evaluation tests
  - fix expression compilation broken after hot reload

See widget inspector layout explorer, a result of evaluate() call:
https://drive.google.com/file/d/16UdSE5_V1ZRXAf2KeBxNwYNHMfo1RbnT/view?usp=sharing&resourcekey=0-HZcPm68VbsVzrZ672CApvA

Closes: https://github.com/dart-lang/sdk/issues/41480
Closes: https://github.com/dart-lang/sdk/issues/44979
Closes: https://github.com/dart-lang/sdk/issues/44713
Closes: https://github.com/dart-lang/sdk/issues/44933
Closes: https://github.com/dart-lang/sdk/issues/44813
Closes: https://github.com/dart-lang/sdk/issues/44686
Change-Id: I96c74578c51503adbc4bfe6d6e6112319addc959
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/188400
Commit-Queue: Anna Gringauze <annagrin@google.com>
Reviewed-by: Nicholas Shahan <nshahan@google.com>
Reviewed-by: Mark Zhou <markzipan@google.com>
This commit is contained in:
Anna Gringauze
2021-03-09 17:23:05 +00:00
committed by commit-bot@chromium.org
parent 9f6e64a06f
commit 14eeb7507f
11 changed files with 1244 additions and 538 deletions
@@ -96,6 +96,27 @@ Program transformModuleFormat(ModuleFormat format, Program module) {
}
}
/// Transforms an ES6 [function] into a given module [format].
///
/// If the format is [ModuleFormat.es6] this will return [function] unchanged.
///
/// Because JS ASTs are immutable the resulting function will share as much
/// structure as possible with the original. The transformation is a shallow one
/// that affects the [ImportDeclaration]s from [items].
///
/// Returns a new function that combines all statements from tranformed imports
/// from [items] and the body of the [function].
Fun transformFunctionModuleFormat(
List<ModuleItem> items, Fun function, ModuleFormat format) {
switch (format) {
case ModuleFormat.amd:
return AmdModuleBuilder().buildFunctionWithImports(items, function);
default:
throw UnsupportedError(
'Incremental build does not support $format module format');
}
}
/// Base class for compiling ES6 modules into various ES5 module patterns.
///
/// This is a helper class for utilities and state that is shared by several
@@ -107,11 +128,16 @@ abstract class _ModuleBuilder {
final statements = <Statement>[];
/// Collect [imports], [exports] and [statements] from the ES6 [module].
void visitProgram(Program module) {
visitModuleItems(module.body);
}
/// Collect [imports], [exports] and [statements] from the ES6 [items].
///
/// For exports, this will also add their body to [statements] in the
/// appropriate position.
void visitProgram(Program module) {
for (var item in module.body) {
void visitModuleItems(List<ModuleItem> items) {
for (var item in items) {
if (item is ImportDeclaration) {
visitImportDeclaration(item);
} else if (item is ExportDeclaration) {
@@ -137,6 +163,12 @@ abstract class _ModuleBuilder {
void visitStatement(Statement node) {
statements.add(node);
}
void clear() {
imports.clear();
exports.clear();
statements.clear();
}
}
/// Generates modules for with our DDC `dart_library.js` loading mechanism.
@@ -260,31 +292,43 @@ class CommonJSModuleBuilder extends _ModuleBuilder {
class AmdModuleBuilder extends _ModuleBuilder {
AmdModuleBuilder();
Program build(Program module) {
var importStatements = <Statement>[];
/// Build a module variable definition for [import].
static Statement buildLoadModule(
Identifier moduleVar, ImportDeclaration import) =>
js.statement('const # = require(#);', [moduleVar, import.from]);
// Collect imports/exports/statements.
visitProgram(module);
/// Build library variable definitions for all libraries from [import].
static List<Statement> buildImports(
Identifier moduleVar, ImportDeclaration import) {
var items = <Statement>[];
var dependencies = <LiteralString>[];
var fnParams = <Parameter>[];
for (var importName in import.namedImports) {
// import * is not emitted by the compiler, so we don't handle it here.
assert(!importName.isStar);
var asName = importName.asName ?? importName.name;
items.add(js.statement(
'const # = #.#', [asName, moduleVar, importName.name.name]));
}
return items;
}
/// Group libraries from [imports] by modules.
static Map<Identifier, ImportDeclaration> _collectModuleImports(
List<ImportDeclaration> imports) {
var result = <Identifier, ImportDeclaration>{};
for (var import in imports) {
// TODO(jmesserly): we could use destructuring once Atom supports it.
var moduleVar =
TemporaryId(pathToJSIdentifier(import.from.valueWithoutQuotes));
fnParams.add(moduleVar);
dependencies.add(import.from);
// TODO(jmesserly): optimize for the common case of a single import.
for (var importName in import.namedImports) {
// import * is not emitted by the compiler, so we don't handle it here.
assert(!importName.isStar);
var asName = importName.asName ?? importName.name;
importStatements.add(js.statement(
'const # = #.#', [asName, moduleVar, importName.name.name]));
}
result[moduleVar] = import;
}
statements.insertAll(0, importStatements);
return result;
}
/// Build statements for [exports].
static List<Statement> buildExports(List<ExportDeclaration> exports) {
var items = <Statement>[];
if (exports.isNotEmpty) {
var exportedProps = <Property>[];
@@ -297,9 +341,59 @@ class AmdModuleBuilder extends _ModuleBuilder {
exportedProps.add(Property(js.string(alias.name), name.name));
}
}
statements.add(js.comment('Exports:'));
statements.add(Return(ObjectInitializer(exportedProps, multiline: true)));
items.add(js.comment('Exports:'));
items.add(Return(ObjectInitializer(exportedProps, multiline: true)));
}
return items;
}
/// Build function body with all necessary imports included.
///
/// Used for the top level syntetic function generated during expression
/// compilation, in order to include all the context needed for evaluation
/// inside it.
///
/// Returns a new function that combines all statements from tranformed
/// imports from [items] and the body of the [function].
Fun buildFunctionWithImports(List<ModuleItem> items, Fun function) {
clear();
visitModuleItems(items);
var moduleImports = _collectModuleImports(imports);
var importStatements = <Statement>[];
moduleImports.forEach((moduleVar, import) {
importStatements.add(buildLoadModule(moduleVar, import));
importStatements.addAll(buildImports(moduleVar, import));
});
return Fun(
function.params,
Block([...importStatements, ...statements, ...function.body.statements]),
);
}
Program build(Program module) {
// Collect imports/exports/statements.
visitProgram(module);
var moduleImports = _collectModuleImports(imports);
var importStatements = <Statement>[];
var fnParams = moduleImports.keys.toList();
var dependencies =
moduleImports.values.map((import) => import.from).toList();
moduleImports.forEach((moduleVar, import) {
importStatements.addAll(buildImports(moduleVar, import));
});
// Prepend import statetements.
statements.insertAll(0, importStatements);
// Append export statements.
statements.addAll(buildExports(exports));
var resultModule = NamedFunction(
loadFunctionIdentifier(module.name),
js.fun("function(#) { 'use strict'; #; }", [fnParams, statements]),
@@ -8,6 +8,9 @@ import '../compiler/js_names.dart' as js_ast;
import '../js_ast/js_ast.dart' as js_ast;
import '../js_ast/js_ast.dart' show js;
/// Defines how to emit a value of a table
typedef _emitValue<K> = js_ast.Expression Function(K, ModuleItemData);
/// Represents a top-level property hoisted to a top-level object.
class ModuleItemData {
/// The container that holds this module item in the emitted JS.
@@ -39,34 +42,39 @@ abstract class ModuleItemContainer<K> {
/// Name of the container in the emitted JS.
String name;
/// Indicates if this table is being used in an incremental context (such as
/// during expression evaluation).
///
/// Set by `emitFunctionIncremental` in kernel/compiler.dart.
bool incrementalMode = false;
/// Refers to the latest container if this container is sharded.
js_ast.Identifier containerId;
/// Refers to the aggregated entrypoint into this container.
///
/// Should only be accessed during expression evaluation since lookups are
/// deoptimized in V8..
js_ast.Identifier aggregatedContainerId;
final Map<K, ModuleItemData> moduleItems = {};
/// Incremental mode used for expression compilation
bool _incrementalMode = false;
/// Items accessed during incremental mode
final Set<K> incrementalModuleItems = {};
/// Indicates if this table is being used in an incremental context.
///
/// Used during expression evaluation.
/// Set by `emitFunctionIncremental` in kernel/compiler.dart.
bool get incrementalMode => _incrementalMode;
/// Sets the container to incremental mode.
///
/// Used during expression evaluating so only referenced items
/// will be emitted in a generated function.
///
/// Note: the container cannot revert to non-incremental mode.
void setIncrementalMode() {
incrementalModuleItems.clear();
_incrementalMode = true;
}
/// Holds keys that will not be emitted when calling [emit].
final Set<K> _noEmit = {};
/// Creates a container with a name, ID, and incremental ID used for
/// expression evaluation.
///
/// If [aggregatedId] is null, the container is not sharded, so the
/// containerId is safe to use during eval.
ModuleItemContainer._(
this.name, this.containerId, js_ast.Identifier aggregatedId)
: aggregatedContainerId = aggregatedId ?? containerId;
/// Creates a container with a name, ID
ModuleItemContainer._(this.name, this.containerId);
/// Creates an automatically sharding container backed by JS Objects.
factory ModuleItemContainer.asObject(String name,
@@ -107,15 +115,17 @@ abstract class ModuleItemContainer<K> {
_noEmit.add(key);
}
void setEmitIfIncremental(K key) {
if (incrementalMode) {
incrementalModuleItems.add(key);
}
}
/// Emit the container declaration/initializer, using multiple statements if
/// necessary.
List<js_ast.Statement> emit();
/// Emit the container declaration/initializer incrementally.
///
/// Used during expression evaluation. Appends all newly added types to the
/// aggregated container.
List<js_ast.Statement> emitIncremental();
/// Uses [emitValue] to emit the values in the table.
List<js_ast.Statement> emit({_emitValue<K> emitValue});
}
/// Associates a [K] with a container-unique JS key and arbitrary JS value.
@@ -142,8 +152,7 @@ class ModuleItemObjectContainer<K> extends ModuleItemContainer<K> {
String Function(K) keyToString;
ModuleItemObjectContainer(String name, this.keyToString)
: super._(
name, js_ast.TemporaryId(name), js_ast.Identifier('${name}\$Eval'));
: super._(name, js_ast.TemporaryId(name));
@override
void operator []=(K key, js_ast.Expression value) {
@@ -170,53 +179,33 @@ class ModuleItemObjectContainer<K> extends ModuleItemContainer<K> {
@override
js_ast.Expression access(K key) {
var id = incrementalMode ? aggregatedContainerId : moduleItems[key].id;
var id = moduleItems[key].id;
return js.call('#.#', [id, moduleItems[key].jsKey]);
}
@override
List<js_ast.Statement> emit() {
List<js_ast.Statement> emit({_emitValue<K> emitValue}) {
var containersToProperties = <js_ast.Identifier, List<js_ast.Property>>{};
moduleItems.forEach((k, v) {
if (_noEmit.contains(k)) return;
if (!incrementalMode && _noEmit.contains(k)) return;
if (incrementalMode && !incrementalModuleItems.contains(k)) return;
if (!containersToProperties.containsKey(v.id)) {
containersToProperties[v.id] = <js_ast.Property>[];
}
containersToProperties[v.id].add(js_ast.Property(v.jsKey, v.jsValue));
containersToProperties[v.id].add(js_ast.Property(
v.jsKey, emitValue == null ? v.jsValue : emitValue(k, v)));
});
// Emit a self-reference for the next container so V8 does not optimize it
// away. Required for expression evaluation.
if (containersToProperties[containerId] == null) {
containersToProperties[containerId] = [
js_ast.Property(
js_ast.LiteralString('_'), js.call('() => #', [containerId]))
];
}
if (containersToProperties.isEmpty) return [];
var statements = <js_ast.Statement>[];
var aggregatedContainers = <js_ast.Expression>[];
containersToProperties.forEach((containerId, properties) {
var containerObject = js_ast.ObjectInitializer(properties,
multiline: properties.length > 1);
statements.add(js.statement('var # = #', [containerId, containerObject]));
aggregatedContainers.add(js.call('#', [containerId]));
});
// Create an aggregated access point over all containers for eval.
statements.add(js.statement('var # = Object.assign({_ : () => #}, #)',
[aggregatedContainerId, aggregatedContainerId, aggregatedContainers]));
return statements;
}
/// Appends all newly added types to the most recent container.
@override
List<js_ast.Statement> emitIncremental() {
assert(incrementalMode);
var statements = <js_ast.Statement>[];
moduleItems.forEach((k, v) {
if (_noEmit.contains(k)) return;
statements.add(js
.statement('#[#] = #', [aggregatedContainerId, v.jsKey, v.jsValue]));
});
return statements;
}
}
@@ -232,7 +221,7 @@ class ModuleItemObjectContainer<K> extends ModuleItemContainer<K> {
/// ```
class ModuleItemArrayContainer<K> extends ModuleItemContainer<K> {
ModuleItemArrayContainer(String name)
: super._(name, js_ast.TemporaryId(name), null);
: super._(name, js_ast.TemporaryId(name));
@override
void operator []=(K key, js_ast.Expression value) {
@@ -246,23 +235,26 @@ class ModuleItemArrayContainer<K> extends ModuleItemContainer<K> {
@override
js_ast.Expression access(K key) {
var id = incrementalMode ? aggregatedContainerId : containerId;
var id = containerId;
return js.call('#[#]', [id, moduleItems[key].jsKey]);
}
@override
List<js_ast.Statement> emit() {
List<js_ast.Statement> emit({_emitValue<K> emitValue}) {
var properties = List<js_ast.Expression>.filled(length, null);
// If the entire array holds just one value, generate a short initializer.
var valueSet = <js_ast.Expression>{};
moduleItems.forEach((k, v) {
if (_noEmit.contains(k)) return;
if (!incrementalMode && _noEmit.contains(k)) return;
if (incrementalMode && !incrementalModuleItems.contains(k)) return;
valueSet.add(v.jsValue);
properties[int.parse((v.jsKey as js_ast.LiteralNumber).value)] =
v.jsValue;
emitValue == null ? v.jsValue : emitValue(k, v);
});
if (valueSet.isEmpty) return [];
if (valueSet.length == 1 && moduleItems.length > 1) {
return [
js.statement('var # = Array(#).fill(#)', [
@@ -281,16 +273,4 @@ class ModuleItemArrayContainer<K> extends ModuleItemContainer<K> {
])
];
}
@override
List<js_ast.Statement> emitIncremental() {
assert(incrementalMode);
var statements = <js_ast.Statement>[];
moduleItems.forEach((k, v) {
if (_noEmit.contains(k)) return;
statements.add(js
.statement('#[#] = #', [aggregatedContainerId, v.jsKey, v.jsValue]));
});
return statements;
}
}
@@ -30,6 +30,9 @@ abstract class SharedCompiler<Library, Class, InterfaceType, FunctionNode> {
final _symbolContainer = ModuleItemContainer<js_ast.Identifier>.asObject('S',
keyToString: (js_ast.Identifier i) => '${i.name}');
ModuleItemContainer<js_ast.Identifier> get symbolContainer =>
_symbolContainer;
/// Extension member symbols for adding Dart members to JS types.
///
/// These are added to the [extensionSymbolsModule]; see that field for more
@@ -43,6 +46,47 @@ abstract class SharedCompiler<Library, Class, InterfaceType, FunctionNode> {
/// Imported libraries, and the temporaries used to refer to them.
final _imports = <Library, js_ast.TemporaryId>{};
/// Incremental mode for expression compilation.
///
/// If set to true, triggers emitting tall used ypes, symbols, libraries,
/// constants, urs inside the generated function.
bool _incrementalMode = false;
@protected
bool get incrementalMode => _incrementalMode;
/// Set incremental mode for one expression compilation.
///
/// Sets all tables and internal structures to incremental mode so
/// only referenced items will be emitted in a generated function.
///
/// Note: the compiler cannot revert to non-incremental mode.
@protected
void setIncrementalMode() {
incrementalModules.clear();
_privateNames.clear();
symbolContainer.setIncrementalMode();
_incrementalMode = true;
}
/// Modules and libraries accessed during compilation in incremental mode.
@protected
final Map<String, Set<String>> incrementalModules = {};
@protected
void setEmitIfIncrementalLibrary(Library library) {
if (incrementalMode && library != null) {
setEmitIfIncremental(libraryToModule(library), jsLibraryName(library));
}
}
@protected
void setEmitIfIncremental(String module, String library) {
if (incrementalMode && library != null) {
incrementalModules.putIfAbsent(module, () => {}).add(library);
}
}
/// The identifier used to reference DDC's core "dart:_runtime" library from
/// generated JS code, typically called "dart" e.g. `dart.dcall`.
js_ast.Identifier runtimeModule;
@@ -220,8 +264,10 @@ abstract class SharedCompiler<Library, Class, InterfaceType, FunctionNode> {
/// dart.asInt(<expr>)
///
@protected
js_ast.Expression runtimeCall(String code, [List<Object> args]) =>
js.call('#.$code', <Object>[runtimeModule, ...?args]);
js_ast.Expression runtimeCall(String code, [List<Object> args]) {
setEmitIfIncremental(libraryToModule(coreLibrary), runtimeModule.name);
return js.call('#.$code', <Object>[runtimeModule, ...?args]);
}
/// Calls [runtimeCall] and uses `toStatement()` to convert the resulting
/// expression into a statement.
@@ -276,7 +322,13 @@ abstract class SharedCompiler<Library, Class, InterfaceType, FunctionNode> {
}
var privateNames = _privateNames.putIfAbsent(library, () => HashMap());
return privateNames.putIfAbsent(name, initPrivateNameSymbol);
var symbolId = privateNames.putIfAbsent(name, initPrivateNameSymbol);
setEmitIfIncrementalLibrary(library);
setEmitIfIncremental(libraryToModule(coreLibrary), runtimeModule.name);
_symbolContainer.setEmitIfIncremental(symbolId);
return symbolId;
}
/// Emits a private name JS Symbol for [memberName] unique to a Dart
@@ -445,7 +497,6 @@ abstract class SharedCompiler<Library, Class, InterfaceType, FunctionNode> {
.statement('var # = Object.create(#.library)', [id, runtimeModule]));
exports.add(js_ast.NameSpecifier(id));
}
items.add(js_ast.ExportDeclaration(js_ast.ExportClause(exports)));
if (isBuildingSdk) {
@@ -469,6 +520,8 @@ abstract class SharedCompiler<Library, Class, InterfaceType, FunctionNode> {
/// Returns the canonical name to refer to the Dart library.
js_ast.Identifier emitLibraryName(Library library) {
setEmitIfIncrementalLibrary(library);
// Avoid adding the dart:_runtime to _imports when our runtime unit tests
// import it explicitly. It will always be implicitly imported.
if (isSdkInternalRuntime(library)) return runtimeModule;
@@ -479,12 +532,10 @@ abstract class SharedCompiler<Library, Class, InterfaceType, FunctionNode> {
library, () => js_ast.TemporaryId(jsLibraryName(library)));
}
/// Emits imports and extension methods into [items].
/// Emits imports into [items].
@protected
void emitImportsAndExtensionSymbols(List<js_ast.ModuleItem> items,
{bool forceExtensionSymbols = false}) {
void emitImports(List<js_ast.ModuleItem> items) {
var modules = <String, List<Library>>{};
for (var import in _imports.keys) {
modules.putIfAbsent(libraryToModule(import), () => []).add(import);
}
@@ -493,33 +544,57 @@ abstract class SharedCompiler<Library, Class, InterfaceType, FunctionNode> {
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((library) {
var alias = jsLibraryAlias(library);
if (alias != null) {
var aliasId = js_ast.TemporaryId(alias);
return js_ast.NameSpecifier(aliasId, asName: _imports[library]);
if (!incrementalMode || incrementalModules.containsKey(module)) {
var usedLibraries = incrementalModules[module];
// 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 = <js_ast.NameSpecifier>[];
for (var library in libraries) {
if (!incrementalMode ||
usedLibraries.contains(jsLibraryName(library))) {
var alias = jsLibraryAlias(library);
if (alias != null) {
var aliasId = js_ast.TemporaryId(alias);
imports.add(
js_ast.NameSpecifier(aliasId, asName: _imports[library]));
} else {
imports.add(js_ast.NameSpecifier(_imports[library]));
}
}
}
if (module == coreModuleName) {
if (!incrementalMode || usedLibraries.contains(runtimeModule.name)) {
imports.add(js_ast.NameSpecifier(runtimeModule));
}
if (!incrementalMode ||
usedLibraries.contains(extensionSymbolsModule.name)) {
imports.add(js_ast.NameSpecifier(extensionSymbolsModule));
}
}
if (!incrementalMode || imports.isNotEmpty) {
items.add(js_ast.ImportDeclaration(
namedImports: imports, from: js.string(module, "'")));
}
return js_ast.NameSpecifier(_imports[library]);
}).toList();
if (module == coreModuleName) {
imports.add(js_ast.NameSpecifier(runtimeModule));
imports.add(js_ast.NameSpecifier(extensionSymbolsModule));
}
items.add(js_ast.ImportDeclaration(
namedImports: imports, from: js.string(module, "'")));
});
}
/// Emits extension methods into [items].
@protected
void emitExtensionSymbols(List<js_ast.ModuleItem> items,
{bool forceExtensionSymbols = false}) {
// Initialize extension symbols
_extensionSymbols.forEach((name, id) {
js_ast.Expression value =
@@ -530,16 +605,84 @@ abstract class SharedCompiler<Library, Class, InterfaceType, FunctionNode> {
value = js.call(
'# || (# = Symbol(#))', [value, value, js.string('dartx.$name')]);
}
if (!_symbolContainer.canEmit(id)) {
// Extension symbols marked with noEmit are managed manually.
// TODO(vsm): Change back to `const`.
// See https://github.com/dart-lang/sdk/issues/40380.
items.add(js.statement('var # = #;', [id, value]));
// Emit hoisted extension symbols that are marked as noEmit in regular as
// well as incremental mode (if needed) since they are going to be
// referenced as such in the generated expression.
if (!incrementalMode ||
_symbolContainer.incrementalModuleItems.contains(id)) {
if (!_symbolContainer.canEmit(id)) {
// Extension symbols marked with noEmit are managed manually.
// TODO(vsm): Change back to `const`.
// See https://github.com/dart-lang/sdk/issues/40380.
items.add(js.statement('var # = #;', [id, value]));
}
}
if (_symbolContainer.incrementalModuleItems.contains(id)) {
setEmitIfIncremental(
libraryToModule(coreLibrary), extensionSymbolsModule.name);
}
_symbolContainer[id] = value;
});
}
/// Emits exports as imports into [items].
///
/// Use information from exports to re-define library variables referenced
/// inside compiled expressions in incremental mode. That matches importing
/// a current module into the symbol used to represent the library during
/// original compilation in [ProgramCompiler.emitModule].
///
/// Example of exports emitted to JavaScript during emitModule:
///
/// ```
/// dart.trackLibraries("web/main", { ... });
/// // Exports:
/// return {
/// web__main: main
/// };
/// ```
///
/// The transformation to imports during expression compilation converts the
/// exports above to:
///
/// ```
/// const web__main = require('web/main');
/// const main = web__main.web__main;
/// ```
///
/// Where the compiled expression references `main`.
@protected
void emitExportsAsImports(List<js_ast.ModuleItem> items, Library current) {
var exports = <js_ast.NameSpecifier>[];
assert(incrementalMode);
assert(!isBuildingSdk);
var module = libraryToModule(current);
var usedLibraries = incrementalModules[module] ?? {};
if (usedLibraries.isNotEmpty) {
_libraries.forEach((library, libraryId) {
if (usedLibraries.contains(jsLibraryName(library))) {
var alias = jsLibraryAlias(library);
var aliasId = alias == null ? libraryId : js_ast.TemporaryId(alias);
var asName = alias == null ? null : libraryId;
exports.add(js_ast.NameSpecifier(aliasId, asName: asName));
}
});
items.add(js_ast.ImportDeclaration(
namedImports: exports, from: js.string(module, "'")));
}
}
/// Emits imports and extension methods into [items].
@protected
void emitImportsAndExtensionSymbols(List<js_ast.ModuleItem> items,
{bool forceExtensionSymbols = false}) {
emitImports(items);
emitExtensionSymbols(items, forceExtensionSymbols: forceExtensionSymbols);
}
void _emitDebuggerExtensionInfo(String name) {
var properties = <js_ast.Property>[];
var parts = <js_ast.Property>[];
@@ -568,27 +711,26 @@ abstract class SharedCompiler<Library, Class, InterfaceType, FunctionNode> {
///
/// A symbol lookup on an id marked no emit omits the symbol accessor.
js_ast.Expression getSymbol(js_ast.Identifier id) {
_symbolContainer.setEmitIfIncremental(id);
return _symbolContainer.canEmit(id) ? _symbolContainer.access(id) : id;
}
/// Returns the raw JS value associated with [id].
js_ast.Expression getSymbolValue(js_ast.Identifier id) {
_symbolContainer.setEmitIfIncremental(id);
return _symbolContainer[id];
}
/// Inserts a symbol into the symbol table.
js_ast.Expression addSymbol(js_ast.Identifier id, js_ast.Expression symbol) {
_symbolContainer[id] = symbol;
_symbolContainer.setEmitIfIncremental(id);
if (!containerizeSymbols) {
_symbolContainer.setNoEmit(id);
}
return _symbolContainer[id];
}
void setSymbolContainerIncrementalMode(bool setting) {
_symbolContainer.incrementalMode = setting;
}
/// Finishes the module created by [startModule], by combining the preable
/// [items] with the [moduleItems] that have been emitted.
///
@@ -646,7 +788,9 @@ abstract class SharedCompiler<Library, Class, InterfaceType, FunctionNode> {
_extensionSymbols[name] = id;
addSymbol(id, id);
}
return _extensionSymbols[name];
var symbolId = _extensionSymbols[name];
_symbolContainer.setEmitIfIncremental(symbolId);
return symbolId;
}
/// Shorthand for identifier-like property names.
+89 -50
View File
@@ -26,7 +26,7 @@ import '../compiler/module_containers.dart' show ModuleItemContainer;
import '../compiler/shared_command.dart' show SharedCompilerOptions;
import '../compiler/shared_compiler.dart';
import '../js_ast/js_ast.dart' as js_ast;
import '../js_ast/js_ast.dart' show js;
import '../js_ast/js_ast.dart' show ModuleItem, js;
import '../js_ast/source_map_printer.dart' show NodeEnd, NodeSpan, HoverComment;
import 'constants.dart';
import 'js_interop.dart';
@@ -77,7 +77,7 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
final _constLazyAccessors = <js_ast.Method>[];
/// Container for holding the results of lazily-evaluated constants.
final _constTableCache = ModuleItemContainer<String>.asArray('C');
var _constTableCache = ModuleItemContainer<String>.asArray('C');
/// Tracks the index in [moduleItems] where the const table must be inserted.
/// Required for SDK builds due to internal circular dependencies.
@@ -221,7 +221,7 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
final constAliasCache = HashMap<Constant, js_ast.Expression>();
/// Maps uri strings in asserts and elsewhere to hoisted identifiers.
final _uriContainer = ModuleItemContainer<String>.asArray('I');
var _uriContainer = ModuleItemContainer<String>.asArray('I');
final Class _jsArrayClass;
final Class _privateSymbolClass;
@@ -242,6 +242,8 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
final NullableInference _nullableInference;
bool _moduleEmitted = false;
factory ProgramCompiler(
Component component,
ClassHierarchy hierarchy,
@@ -320,8 +322,10 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
InterfaceType get internalSymbolType =>
_coreTypes.legacyRawType(_coreTypes.internalSymbolClass);
/// Module can be emitted only once, and the compiler can be reused after
/// only in incremental mode, for expression compilation only.
js_ast.Program emitModule(Component component) {
if (moduleItems.isNotEmpty) {
if (_moduleEmitted) {
throw StateError('Can only call emitModule once.');
}
_component = component;
@@ -443,7 +447,12 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
// Emit the hoisted type table cache variables
items.addAll(_typeTable.dischargeBoundTypes());
return finishModule(items, _options.moduleName);
var module = finishModule(items, _options.moduleName);
// Mark as finished for incremental mode, so it is safe to
// switch to the incremental mode for expression compilation.
_moduleEmitted = true;
return module;
}
@override
@@ -2530,8 +2539,14 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
return libraryJSName != null ? '$libraryJSName.$jsName' : jsName;
}
String _emitJsNameWithoutGlobal(NamedNode n) {
if (!usesJSInterop(n)) return null;
setEmitIfIncrementalLibrary(getLibrary(n));
return _jsNameWithoutGlobal(n);
}
js_ast.PropertyAccess _emitJSInterop(NamedNode n) {
var jsName = _jsNameWithoutGlobal(n);
var jsName = _emitJsNameWithoutGlobal(n);
if (jsName == null) return null;
return _emitJSInteropForGlobal(jsName);
}
@@ -2762,7 +2777,7 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
typeRep = runtimeCall(
'anonymousJSType(#)', [js.escapedString(getLocalClassName(c))]);
} else {
var jsName = _jsNameWithoutGlobal(c);
var jsName = _emitJsNameWithoutGlobal(c);
if (jsName != null) {
typeRep = runtimeCall('lazyJSType(() => #, #)',
[_emitJSInteropForGlobal(jsName), js.escapedString(jsName)]);
@@ -3072,6 +3087,30 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
js_ast.Expression visitTypedefType(TypedefType type) =>
visitFunctionType(type.unalias as FunctionType);
/// Set incremental mode for expression compilation.
///
/// Called for each expression compilation to set the intremental mode
/// and clear referenced items.
///
/// The compiler cannot revert to non-incremental mode, and requires the
/// original module to be already emitted by the same compiler instance.
@override
void setIncrementalMode() {
if (!_moduleEmitted) {
throw StateError(
'Cannot run in incremental mode before module completion');
}
super.setIncrementalMode();
_constTableCache = ModuleItemContainer<String>.asArray('C');
_constLazyAccessors.clear();
constAliasCache.clear();
_uriContainer = ModuleItemContainer<String>.asArray('I');
_typeTable.typeContainer.setIncrementalMode();
}
/// Emits function after initial compilation.
///
/// Emits function from kernel [functionNode] with name [name] in the context
@@ -3079,68 +3118,67 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
/// finished. For example, this happens in expression compilation during
/// expression evaluation initiated by the user from the IDE and coordinated
/// by the debugger.
js_ast.Fun emitFunctionIncremental(
Library library, Class cls, FunctionNode functionNode, String name) {
// setup context
/// Triggers incremental mode, which only emits symbols, types, constants,
/// libraries, and uris referenced in the expression compilation result.
js_ast.Fun emitFunctionIncremental(List<ModuleItem> items, Library library,
Class cls, FunctionNode functionNode, String name) {
// Setup context.
_currentLibrary = library;
_staticTypeContext.enterLibrary(_currentLibrary);
_currentClass = cls;
// Keep all symbols in containers.
containerizeSymbols = true;
// Set all tables to incremental mode, so we can only emit elements that
// were referenced the compiled code for the expression.
setIncrementalMode();
// Do not add formal parameter checks for the top-level synthetic function
// generated for expression evaluation, as those parameters are a set of
// variables from the current scope, and should alredy be checked in the
// variables from the current scope, and should already be checked in the
// original code.
_checkParameters = false;
// Set module item containers to incremental mode.
setSymbolContainerIncrementalMode(true);
_typeTable.typeContainer.incrementalMode = true;
_constTableCache.incrementalMode = true;
// Emit function with additional information, such as types that are used
// in the expression.
// Emit function while recoding elements accessed from tables.
var fun = _emitFunction(functionNode, name);
var types = _typeTable.dischargeBoundTypes();
var constants = _dischargeConstTable();
var extensionSymbols = <js_ast.Statement>[];
emitExtensionSymbols(extensionSymbols);
// Add all elements from tables accessed in the function
var body = js_ast.Block([
...extensionSymbols,
..._typeTable.dischargeBoundTypes(),
...symbolContainer.emit(),
..._emitConstTable(),
..._uriContainer.emit(),
...fun.body.statements
]);
// Import all necessary libraries, including libraries accessed from the
// current module and libraries accessed from the type table.
for (var library in _typeTable.incrementalLibraries()) {
setEmitIfIncrementalLibrary(library);
}
emitImports(items);
emitExportsAsImports(items, _currentLibrary);
var body = js_ast.Block([...?types, ...?constants, ...fun.body.statements]);
return js_ast.Fun(fun.params, body);
}
/// Emit all collected const symbols
///
/// This is similar to how constants are emitted during
/// initial compilation in emitModule
///
/// TODO: unify the code with emitModule.
List<js_ast.Statement> _dischargeConstTable() {
var items = <js_ast.Statement>[];
List<js_ast.Statement> _emitConstTable() {
var constTable = <js_ast.Statement>[];
if (_constLazyAccessors.isNotEmpty) {
var constTableBody = runtimeStatement(
'defineLazy(#, { # }, false)', [_constTable, _constLazyAccessors]);
items.add(constTableBody);
_constLazyAccessors.clear();
}
constTable
.add(js.statement('const # = Object.create(null);', [_constTable]));
_copyAndFlattenBlocks(items, moduleItems);
moduleItems.clear();
return items;
}
constTable.add(runtimeStatement(
'defineLazy(#, { # }, false)', [_constTable, _constLazyAccessors]));
/// Flattens blocks in [items] to a single list.
///
/// This will not flatten blocks that are marked as being scopes.
void _copyAndFlattenBlocks(
List<js_ast.Statement> result, Iterable<js_ast.ModuleItem> items) {
for (var item in items) {
if (item is js_ast.Block && !item.isScope) {
_copyAndFlattenBlocks(result, item.statements);
} else {
result.add(item as js_ast.Statement);
}
constTable.addAll(_constTableCache.emit());
}
return constTable;
}
js_ast.Fun _emitFunction(FunctionNode f, String name) {
@@ -3729,6 +3767,7 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
if (!_uriContainer.contains(uri)) {
_uriContainer[uri] = js_ast.LiteralString('"$uri"');
}
_uriContainer.setEmitIfIncremental(uri);
return _uriContainer.access(uri);
}
@@ -13,6 +13,8 @@ import 'package:_fe_analyzer_shared/src/messages/codes.dart'
show Code, Message, PlainAndColorizedString;
import 'package:dev_compiler/dev_compiler.dart';
import 'package:dev_compiler/src/compiler/js_names.dart' as js_ast;
import 'package:dev_compiler/src/compiler/module_builder.dart';
import 'package:dev_compiler/src/js_ast/js_ast.dart' as js_ast;
import 'package:dev_compiler/src/kernel/compiler.dart';
@@ -31,8 +33,6 @@ import 'package:kernel/ast.dart'
Member,
Node,
Procedure,
PropertyGet,
PropertySet,
RedirectingFactoryConstructor,
TreeNode,
TypeParameter,
@@ -114,7 +114,7 @@ class DartScopeBuilder extends Visitor<void> with VisitorVoidMixin {
}
DartScope build() {
if (_offset == null || _library == null || _member == null) return null;
if (_offset == null || _library == null) return null;
return DartScope(_library, _cls, _member, _definitions, _typeParameters);
}
@@ -127,7 +127,10 @@ class DartScopeBuilder extends Visitor<void> with VisitorVoidMixin {
@override
void visitLibrary(Library library) {
_library = library;
_offset = _component.getOffset(_library.fileUri, _line, _column);
_offset = 0;
if (_line > 0) {
_offset = _component.getOffset(_library.fileUri, _line, _column);
}
// Exit early if the evaluation offset is not found.
// Note: the complete scope is not found in this case,
@@ -261,53 +264,6 @@ class FileEndOffsetCalculator extends Visitor<int> with VisitorNullMixin<int> {
}
}
/// Collect private fields and libraries used in expression.
///
/// Used during expression evaluation to find symbols
/// for private fields. The symbols are used in the ddc
/// compilation of the expression, are not always avalable
/// in the JavaScript scope, so we need to redefine them.
///
/// See [_addSymbolDefinitions]
class PrivateFieldsVisitor extends Visitor<void> with VisitorVoidMixin {
final Map<String, Library> privateFields = {};
@override
void defaultNode(Node node) {
node.visitChildren(this);
}
@override
void visitFieldReference(Field node) {
if (node.name.isPrivate && !node.isStatic) {
privateFields[node.name.text] = node.enclosingLibrary;
}
}
@override
void visitField(Field node) {
if (node.name.isPrivate && !node.isStatic) {
privateFields[node.name.text] = node.enclosingLibrary;
}
}
@override
void visitPropertyGet(PropertyGet node) {
var member = node.interfaceTarget;
if (node.name.isPrivate && member != null && member.isInstanceMember) {
privateFields[node.name.text] = node.interfaceTarget?.enclosingLibrary;
}
}
@override
void visitPropertySet(PropertySet node) {
var member = node.interfaceTarget;
if (node.name.isPrivate && member != null && member.isInstanceMember) {
privateFields[node.name.text] = node.interfaceTarget?.enclosingLibrary;
}
}
}
class ExpressionCompiler {
static final String debugProcedureName = '\$dartEval';
@@ -317,6 +273,7 @@ class ExpressionCompiler {
final IncrementalCompiler _compiler;
final ProgramCompiler _kernel2jsCompiler;
final Component _component;
final ModuleFormat _moduleFormat;
DiagnosticMessageHandler onDiagnostic;
@@ -328,6 +285,7 @@ class ExpressionCompiler {
ExpressionCompiler(
this._options,
this._moduleFormat,
this.errors,
this._compiler,
this._kernel2jsCompiler,
@@ -494,81 +452,27 @@ class ExpressionCompiler {
// TODO: make this code clear and assumptions enforceable
// https://github.com/dart-lang/sdk/issues/43273
//
// We assume here that ExpressionCompiler is always created using
// onDisgnostic method that adds to the error list that is passed
// to the same invocation of the ExpressionCompiler constructor.
// We only use the error list once - below, to detect if the frontend
// compilation of the expression has failed.
if (errors.isNotEmpty) {
return null;
}
var jsFun = _kernel2jsCompiler.emitFunctionIncremental(
var imports = <js_ast.ModuleItem>[];
var jsFun = _kernel2jsCompiler.emitFunctionIncremental(imports,
scope.library, scope.cls, procedure.function, '$debugProcedureName');
_log('Generated JavaScript for expression');
var jsFunModified = _addSymbolDefinitions(procedure, jsFun, scope);
_log('Added symbol definitions to JavaScript');
// print JS ast to string for evaluation
var context = js_ast.SimpleJavaScriptPrintingContext();
var opts =
js_ast.JavaScriptPrintingOptions(allowKeywordsInProperties: true);
jsFunModified.accept(js_ast.Printer(opts, context));
_log('Performed JavaScript adjustments for expression');
var tree = transformFunctionModuleFormat(imports, jsFun, _moduleFormat);
tree.accept(
js_ast.Printer(opts, context, localNamer: js_ast.TemporaryNamer(tree)));
_log('Added imports and renamed variables for expression');
return context.getText();
}
/// Add symbol definitions for all symbols in compiled expression
///
/// Example:
///
/// compilation of this._field from library 'main'
///
/// Symbol definition:
///
/// let _f = dart.privateName(main, "_f");
///
/// Expression generated by ddc:
///
/// this[_f]
///
/// TODO: this is a temporary workaround to make JavaScript produced
/// by the ProgramCompiler self-contained.
/// Issue: https://github.com/dart-lang/sdk/issues/41480
js_ast.Fun _addSymbolDefinitions(
Procedure procedure, js_ast.Fun jsFun, DartScope scope) {
// get private fields accessed by the evaluated expression
var fieldsCollector = PrivateFieldsVisitor();
procedure.accept(fieldsCollector);
var privateFields = fieldsCollector.privateFields;
// collect library names where private symbols are defined
var libraryForField = privateFields.map((field, library) =>
MapEntry(field, _kernel2jsCompiler.emitLibraryName(library).name));
var body = js_ast.Block([
// re-create private field accessors
...libraryForField.keys.map(
(String field) => _createPrivateField(field, libraryForField[field])),
// statements generated by the FE
...jsFun.body.statements
]);
return js_ast.Fun(jsFun.params, body);
}
/// Creates a private symbol definition
///
/// example:
/// let _f = dart.privateName(main, "_f");
js_ast.Statement _createPrivateField(String field, String library) {
return js_ast.js.statement('let # = dart.privateName(#, #)',
[field, library, js_ast.js.string(field)]);
}
}
@@ -10,6 +10,8 @@ import 'dart:io';
import 'package:args/args.dart';
import 'package:build_integration/file_system/multi_root.dart';
import 'package:dev_compiler/dev_compiler.dart';
import 'package:dev_compiler/src/compiler/module_builder.dart';
import 'package:front_end/src/api_prototype/file_system.dart';
import 'package:front_end/src/api_unstable/ddc.dart';
import 'package:kernel/ast.dart' show Component, Library;
@@ -82,11 +84,13 @@ class ExpressionCompilerWorker {
final ProcessedOptions _processedOptions;
final CompilerOptions _compilerOptions;
final ModuleFormat _moduleFormat;
final Component _sdkComponent;
ExpressionCompilerWorker._(
this._processedOptions,
this._compilerOptions,
this._moduleFormat,
this._sdkComponent,
this.requestStream,
this.sendResponse,
@@ -120,6 +124,9 @@ class ExpressionCompilerWorker {
parseExperimentalArguments(
parsedArgs['enable-experiment'] as List<String>),
onError: (e) => throw e);
var moduleFormat = parseModuleFormat(parsedArgs['module-format'] as String);
return create(
librariesSpecificationUri:
_argToUri(parsedArgs['libraries-file'] as String),
@@ -131,6 +138,7 @@ class ExpressionCompilerWorker {
sdkRoot: _argToUri(parsedArgs['sdk-root'] as String),
trackWidgetCreation: parsedArgs['track-widget-creation'] as bool,
soundNullSafety: parsedArgs['sound-null-safety'] as bool,
moduleFormat: moduleFormat,
verbose: parsedArgs['verbose'] as bool,
requestStream: requestStream,
sendResponse: sendResponse,
@@ -152,6 +160,7 @@ class ExpressionCompilerWorker {
Uri sdkRoot,
bool trackWidgetCreation = false,
bool soundNullSafety = false,
ModuleFormat moduleFormat = ModuleFormat.amd,
bool verbose = false,
Stream<Map<String, dynamic>> requestStream, // Defaults to read from stdin
void Function(Map<String, dynamic>)
@@ -185,7 +194,7 @@ class ExpressionCompilerWorker {
});
return ExpressionCompilerWorker._(processedOptions, compilerOptions,
sdkComponent, requestStream, sendResponse)
moduleFormat, sdkComponent, requestStream, sendResponse)
.._updateCache(sdkComponent, dartSdkModule, true);
}
@@ -309,6 +318,7 @@ class ExpressionCompilerWorker {
sourceMap: true,
summarizeApi: false,
moduleName: moduleName,
soundNullSafety: _compilerOptions.nnbdMode == NnbdMode.Strong,
// Disable asserts due to failures to load source and
// locations on kernel loaded from dill files in DDC.
// https://github.com/dart-lang/sdk/issues/43986
@@ -342,6 +352,7 @@ class ExpressionCompilerWorker {
var expressionCompiler = ExpressionCompiler(
_compilerOptions,
_moduleFormat,
errors,
incrementalCompiler,
kernel2jsCompiler,
@@ -673,6 +684,7 @@ final argParser = ArgParser()
..addOption('sdk-root')
..addOption('asset-server-address')
..addOption('asset-server-port')
..addOption('module-format')
..addFlag('track-widget-creation', defaultsTo: false)
..addFlag('sound-null-safety', defaultsTo: false)
..addFlag('verbose', defaultsTo: false);
@@ -9,7 +9,8 @@ import 'dart:collection';
import 'package:kernel/kernel.dart';
import '../compiler/js_names.dart' as js_ast;
import '../compiler/module_containers.dart' show ModuleItemContainer;
import '../compiler/module_containers.dart'
show ModuleItemContainer, ModuleItemData;
import '../js_ast/js_ast.dart' as js_ast;
import '../js_ast/js_ast.dart' show js;
import 'kernel_helpers.dart';
@@ -119,16 +120,26 @@ class TypeTable {
bool _isNamed(DartType type) =>
typeContainer.contains(type) || _unboundTypeIds.containsKey(type);
Set<Library> incrementalLibraries() {
var libraries = <Library>{};
for (var t in typeContainer.incrementalModuleItems) {
if (t is InterfaceType) {
libraries.add(t.classNode.enclosingLibrary);
}
}
return libraries;
}
/// Emit the initializer statements for the type container, which contains
/// all named types with fully bound type parameters.
List<js_ast.Statement> dischargeBoundTypes() {
for (var t in typeContainer.keys) {
typeContainer[t] = js.call('() => ((# = #.constFn(#))())',
[typeContainer.access(t), _runtimeModule, typeContainer[t]]);
js_ast.Expression emitValue(DartType t, ModuleItemData data) {
var access = js.call('#.#', [data.id, data.jsKey]);
return js.call('() => ((# = #.constFn(#))())',
[access, _runtimeModule, data.jsValue]);
}
var boundTypes = typeContainer.incrementalMode
? typeContainer.emitIncremental()
: typeContainer.emit();
var boundTypes = typeContainer.emit(emitValue: emitValue);
// Bound types should only be emitted once (even across multiple evals).
for (var t in typeContainer.keys) {
typeContainer.setNoEmit(t);
@@ -172,6 +183,7 @@ class TypeTable {
if (!typeContainer.contains(type)) {
typeContainer[type] = typeRep;
}
typeContainer.setEmitIfIncremental(type);
return _unboundTypeIds[type] ?? typeContainer.access(type);
}
File diff suppressed because it is too large Load Diff
+5 -2
View File
@@ -10,7 +10,7 @@ import 'dart:io' hide FileSystemEntity;
import 'package:args/args.dart';
import 'package:dev_compiler/dev_compiler.dart'
show DevCompilerTarget, ExpressionCompiler;
show DevCompilerTarget, ExpressionCompiler, parseModuleFormat;
// front_end/src imports below that require lint `ignore_for_file`
// are a temporary state of things until frontend team builds better api
@@ -605,6 +605,7 @@ class FrontendCompiler implements CompilerInterface {
String filename, String fileSystemScheme, String moduleFormat) async {
var packageConfig = await loadPackageConfigUri(
_compilerOptions.packagesFileUri ?? File('.packages').absolute.uri);
var soundNullSafety = _compilerOptions.nnbdMode == NnbdMode.Strong;
final Component component = results.component;
// Compute strongly connected components.
final strongComponents = StrongComponents(component,
@@ -623,7 +624,8 @@ class FrontendCompiler implements CompilerInterface {
component, strongComponents, fileSystemScheme, packageConfig,
useDebuggerModuleNames: useDebuggerModuleNames,
emitDebugMetadata: emitDebugMetadata,
moduleFormat: moduleFormat);
moduleFormat: moduleFormat,
soundNullSafety: soundNullSafety);
final sourceFileSink = sourceFile.openWrite();
final manifestFileSink = manifestFile.openWrite();
final sourceMapsFileSink = sourceMapsFile.openWrite();
@@ -839,6 +841,7 @@ class FrontendCompiler implements CompilerInterface {
var expressionCompiler = new ExpressionCompiler(
_compilerOptions,
parseModuleFormat(_options['dartdevc-module-format'] as String),
errors,
_generator.generator,
kernel2jsCompiler,
@@ -27,6 +27,7 @@ class JavaScriptBundler {
this._fileSystemScheme, this._packageConfig,
{this.useDebuggerModuleNames = false,
this.emitDebugMetadata = false,
this.soundNullSafety = false,
String moduleFormat})
: compilers = <String, ProgramCompiler>{},
_moduleFormat = parseModuleFormat(moduleFormat ?? 'amd') {
@@ -65,6 +66,7 @@ class JavaScriptBundler {
final bool emitDebugMetadata;
final Map<String, ProgramCompiler> compilers;
final ModuleFormat _moduleFormat;
final bool soundNullSafety;
List<Component> _summaries;
List<Uri> _summaryUris;
@@ -135,6 +137,7 @@ class JavaScriptBundler {
summarizeApi: false,
emitDebugMetadata: emitDebugMetadata,
moduleName: moduleName,
soundNullSafety: soundNullSafety,
),
importToSummary,
summaryToModule,
-38
View File
@@ -1,38 +0,0 @@
// Copyright (c) 2019, 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.
// Tests that evaluation containers aren't renamed by DDC.
import 'dart:_foreign_helper' as helper show JS;
import 'package:expect/expect.dart';
class T {
final int T$Eval = 0;
final int S$Eval = 0;
String get realT$Eval => helper.JS<String>('', 'T\$Eval.toString()');
String get realS$Eval => helper.JS<String>('', 'S\$Eval.toString()');
}
class T$Eval {}
void main() {
var T$Eval = T();
var S$Eval = T$Eval;
var container1 = helper.JS<String>('', 'T\$Eval.toString()');
var container2 = helper.JS<String>('', 'S\$Eval.toString()');
// Evaluation containers are JS Objects. Ensure they aren't shadowed by JS
// symbols or Dart constructs.
Expect.equals('[object Object]', '$container1');
Expect.equals('[object Object]', '$container2');
Expect.equals("Instance of 'T'", T$Eval.toString());
Expect.equals(T$Eval.T$Eval, 0);
Expect.equals(T$Eval.S$Eval, 0);
Expect.notEquals(T$Eval.toString(), container1);
Expect.equals(T$Eval.realT$Eval, container1);
Expect.equals(T$Eval.realS$Eval, container2);
}