mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 01:21:07 +00:00
[dartdevc] Creating container entrypoints for expression eval.
The expression evaluator should now index/read object containers (types and symbols right now) via accessors like 'A$Eval'. Fixes #44494 Change-Id: I31d203cfe59a6e2c03eeb22e8c86270c28b5e2fa Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/176923 Reviewed-by: Nicholas Shahan <nshahan@google.com> Commit-Queue: Mark Zhou <markzipan@google.com>
This commit is contained in:
parent
daba9f3626
commit
bd30532d82
|
@ -39,15 +39,34 @@ 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 = {};
|
||||
|
||||
/// Holds keys that will not be emitted when calling [emit].
|
||||
final Set<K> _noEmit = {};
|
||||
|
||||
ModuleItemContainer._(this.name, this.containerId);
|
||||
/// 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)
|
||||
: this.aggregatedContainerId = aggregatedId ?? containerId;
|
||||
|
||||
/// Creates an automatically sharding container backed by JS Objects.
|
||||
factory ModuleItemContainer.asObject(String name,
|
||||
|
@ -95,7 +114,7 @@ abstract class ModuleItemContainer<K> {
|
|||
/// Emit the container declaration/initializer incrementally.
|
||||
///
|
||||
/// Used during expression evaluation. Appends all newly added types to the
|
||||
/// most recent container.
|
||||
/// aggregated container.
|
||||
List<js_ast.Statement> emitIncremental();
|
||||
}
|
||||
|
||||
|
@ -123,7 +142,8 @@ class ModuleItemObjectContainer<K> extends ModuleItemContainer<K> {
|
|||
String Function(K) keyToString;
|
||||
|
||||
ModuleItemObjectContainer(String name, this.keyToString)
|
||||
: super._(name, js_ast.TemporaryId(name));
|
||||
: super._(
|
||||
name, js_ast.TemporaryId(name), js_ast.Identifier('${name}\$Eval'));
|
||||
|
||||
@override
|
||||
void operator []=(K key, js_ast.Expression value) {
|
||||
|
@ -150,7 +170,8 @@ class ModuleItemObjectContainer<K> extends ModuleItemContainer<K> {
|
|||
|
||||
@override
|
||||
js_ast.Expression access(K key) {
|
||||
return js.call('#.#', [moduleItems[key].id, moduleItems[key].jsKey]);
|
||||
var id = incrementalMode ? aggregatedContainerId : moduleItems[key].id;
|
||||
return js.call('#.#', [id, moduleItems[key].jsKey]);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -173,23 +194,29 @@ class ModuleItemObjectContainer<K> extends ModuleItemContainer<K> {
|
|||
];
|
||||
}
|
||||
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 # = Object.create(#)', [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('#[#] = #', [containerId, v.jsKey, v.jsValue]));
|
||||
statements.add(js
|
||||
.statement('#[#] = #', [aggregatedContainerId, v.jsKey, v.jsValue]));
|
||||
});
|
||||
return statements;
|
||||
}
|
||||
|
@ -206,7 +233,7 @@ class ModuleItemObjectContainer<K> extends ModuleItemContainer<K> {
|
|||
/// ```
|
||||
class ModuleItemArrayContainer<K> extends ModuleItemContainer<K> {
|
||||
ModuleItemArrayContainer(String name)
|
||||
: super._(name, js_ast.TemporaryId(name));
|
||||
: super._(name, js_ast.TemporaryId(name), null);
|
||||
|
||||
@override
|
||||
void operator []=(K key, js_ast.Expression value) {
|
||||
|
@ -220,7 +247,8 @@ class ModuleItemArrayContainer<K> extends ModuleItemContainer<K> {
|
|||
|
||||
@override
|
||||
js_ast.Expression access(K key) {
|
||||
return js.call('#[#]', [containerId, moduleItems[key].jsKey]);
|
||||
var id = incrementalMode ? aggregatedContainerId : containerId;
|
||||
return js.call('#[#]', [id, moduleItems[key].jsKey]);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -257,11 +285,12 @@ 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('#[#] = #', [containerId, v.jsKey, v.jsValue]));
|
||||
statements.add(js
|
||||
.statement('#[#] = #', [aggregatedContainerId, v.jsKey, v.jsValue]));
|
||||
});
|
||||
return statements;
|
||||
}
|
||||
|
|
|
@ -581,6 +581,10 @@ abstract class SharedCompiler<Library, Class, InterfaceType, FunctionNode> {
|
|||
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.
|
||||
///
|
||||
|
|
|
@ -70,7 +70,7 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
|||
/// Let variables collected for the given function.
|
||||
List<js_ast.TemporaryId> _letVariables;
|
||||
|
||||
final _constTable = js_ast.TemporaryId('CT');
|
||||
final _constTable = js_ast.Identifier('CT');
|
||||
|
||||
/// Constant getters used to populate the constant table.
|
||||
final _constLazyAccessors = <js_ast.Method>[];
|
||||
|
@ -3088,6 +3088,10 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
|||
// original code.
|
||||
_checkParameters = false;
|
||||
|
||||
// Set module item containers to incremental mode.
|
||||
setSymbolContainerIncrementalMode(true);
|
||||
_typeTable.typeContainer.incrementalMode = true;
|
||||
|
||||
// Emit function with additional information, such as types that are used
|
||||
// in the expression. Note that typeTable can be null if this function is
|
||||
// called from the expression compilation service, since we currently do
|
||||
|
@ -3097,7 +3101,7 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
|||
// Issue: https://github.com/dart-lang/sdk/issues/43288
|
||||
var fun = _emitFunction(functionNode, name);
|
||||
|
||||
var types = _typeTable?.dischargeBoundTypes(incremental: true);
|
||||
var types = _typeTable?.dischargeBoundTypes();
|
||||
var constants = _dischargeConstTable();
|
||||
|
||||
var body = js_ast.Block([...?types, ...?constants, ...fun.body.statements]);
|
||||
|
|
|
@ -122,15 +122,14 @@ class TypeTable {
|
|||
|
||||
/// Emit the initializer statements for the type container, which contains
|
||||
/// all named types with fully bound type parameters.
|
||||
///
|
||||
/// [incremental] is only used for expression evaluation.
|
||||
List<js_ast.Statement> dischargeBoundTypes({bool incremental = false}) {
|
||||
List<js_ast.Statement> dischargeBoundTypes() {
|
||||
for (var t in typeContainer.keys) {
|
||||
typeContainer[t] = js.call('() => ((# = #.constFn(#))())',
|
||||
[typeContainer.access(t), _runtimeModule, typeContainer[t]]);
|
||||
}
|
||||
var boundTypes =
|
||||
incremental ? typeContainer.emitIncremental() : typeContainer.emit();
|
||||
var boundTypes = typeContainer.incrementalMode
|
||||
? typeContainer.emitIncremental()
|
||||
: typeContainer.emit();
|
||||
// Bound types should only be emitted once (even across multiple evals).
|
||||
for (var t in typeContainer.keys) {
|
||||
typeContainer.setNoEmit(t);
|
||||
|
|
|
@ -450,10 +450,10 @@ void main() {
|
|||
expression: 'main',
|
||||
expectedResult: '''
|
||||
(function(x, y, z) {
|
||||
T.VoidTodynamic = () => (T.VoidTodynamic = dart.constFn(dart.fnType(dart.dynamic, [])))();
|
||||
T\$Eval.VoidTodynamic = () => (T\$Eval.VoidTodynamic = dart.constFn(dart.fnType(dart.dynamic, [])))();
|
||||
dart.defineLazy(CT, {
|
||||
get C0() {
|
||||
return C[0] = dart.fn(foo.main, T.VoidTodynamic());
|
||||
return C[0] = dart.fn(foo.main, T\$Eval.VoidTodynamic());
|
||||
}
|
||||
}, false);
|
||||
return C[0] || CT.C0;
|
||||
|
@ -1339,8 +1339,8 @@ void main() {
|
|||
expression: 'baz(p as String)',
|
||||
expectedResult: '''
|
||||
(function(p) {
|
||||
T.StringL = () => (T.StringL = dart.constFn(dart.legacy(core.String)))();
|
||||
return foo.baz(T.StringL().as(p));
|
||||
T\$Eval.StringL = () => (T\$Eval.StringL = dart.constFn(dart.legacy(core.String)))();
|
||||
return foo.baz(T\$Eval.StringL().as(p));
|
||||
}(
|
||||
0
|
||||
))
|
||||
|
@ -1959,8 +1959,8 @@ void main() {
|
|||
expression: 'a is String',
|
||||
expectedResult: '''
|
||||
(function(a, check) {
|
||||
T.StringL = () => (T.StringL = dart.constFn(dart.legacy(core.String)))();
|
||||
return T.StringL().is(a);
|
||||
T\$Eval.StringL = () => (T\$Eval.StringL = dart.constFn(dart.legacy(core.String)))();
|
||||
return T\$Eval.StringL().is(a);
|
||||
}(
|
||||
null,
|
||||
null
|
||||
|
@ -1974,7 +1974,7 @@ void main() {
|
|||
expression: 'a is int',
|
||||
expectedResult: '''
|
||||
(function(a, check) {
|
||||
return T.intL().is(a);
|
||||
return T\$Eval.intL().is(a);
|
||||
}(
|
||||
null,
|
||||
null
|
||||
|
@ -2182,10 +2182,10 @@ void main() {
|
|||
expression: 'main',
|
||||
expectedResult: '''
|
||||
(function(x, y, z) {
|
||||
T.VoidTodynamic = () => (T.VoidTodynamic = dart.constFn(dart.fnType(dart.dynamic, [])))();
|
||||
T\$Eval.VoidTodynamic = () => (T\$Eval.VoidTodynamic = dart.constFn(dart.fnType(dart.dynamic, [])))();
|
||||
dart.defineLazy(CT, {
|
||||
get C0() {
|
||||
return C[0] = dart.fn(foo.main, T.VoidTodynamic());
|
||||
return C[0] = dart.fn(foo.main, T\$Eval.VoidTodynamic());
|
||||
}
|
||||
}, false);
|
||||
return C[0] || CT.C0;
|
||||
|
|
38
tests/dartdevc/container_test.dart
Normal file
38
tests/dartdevc/container_test.dart
Normal file
|
@ -0,0 +1,38 @@
|
|||
// 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);
|
||||
}
|
Loading…
Reference in a new issue