Several dump-info fixes.
Includes:
* avoid prettyPrint in dump-info (which removes inconsistencies from shadowing
when using fast-startup), instead log start and end offsets and record the
emitted string.
* fix startup emitter to track a few more things (class names, inherit calls,
lazy initializers, etc)
Change-Id: I213edf526173212e918129440dc6dec996a9dd55
Reviewed-on: https://dart-review.googlesource.com/c/89084
Reviewed-by: Stephen Adams <sra@google.com>
Commit-Queue: Sigmund Cherem <sigmund@google.com>
diff --git a/pkg/compiler/lib/src/dump_info.dart b/pkg/compiler/lib/src/dump_info.dart
index e2722a0..d3f9c4d 100644
--- a/pkg/compiler/lib/src/dump_info.dart
+++ b/pkg/compiler/lib/src/dump_info.dart
@@ -32,6 +32,7 @@
final Compiler compiler;
final JClosedWorld closedWorld;
final GlobalTypeInferenceResults _globalInferenceResults;
+ final DumpInfoTask dumpInfoTask;
JElementEnvironment get environment => closedWorld.elementEnvironment;
CodegenWorldBuilder get codegenWorldBuilder => compiler.codegenWorldBuilder;
@@ -41,13 +42,13 @@
final Map<ConstantValue, Info> _constantToInfo = <ConstantValue, Info>{};
final Map<OutputUnit, OutputUnitInfo> _outputToInfo = {};
- ElementInfoCollector(
- this.compiler, this.closedWorld, this._globalInferenceResults);
+ ElementInfoCollector(this.compiler, this.dumpInfoTask, this.closedWorld,
+ this._globalInferenceResults);
void run() {
- compiler.dumpInfoTask._constantToNode.forEach((constant, node) {
+ dumpInfoTask._constantToNode.forEach((constant, node) {
// TODO(sigmund): add dependencies on other constants
- var size = compiler.dumpInfoTask._nodeToSize[node];
+ var size = dumpInfoTask._nodeData[node].length;
var code = jsAst.prettyPrint(node,
enableMinification: compiler.options.enableMinification);
var info = new ConstantInfo(
@@ -64,8 +65,8 @@
/// output size. Either because it is a function being emitted or inlined,
/// or because it is an entity that holds dependencies to other entities.
bool shouldKeep(Entity entity) {
- return compiler.dumpInfoTask.impacts.containsKey(entity) ||
- compiler.dumpInfoTask.inlineCount.containsKey(entity);
+ return dumpInfoTask.impacts.containsKey(entity) ||
+ dumpInfoTask.inlineCount.containsKey(entity);
}
LibraryInfo visitLibrary(LibraryEntity lib) {
@@ -73,7 +74,7 @@
if (libname.isEmpty) {
libname = '<unnamed>';
}
- int size = compiler.dumpInfoTask.sizeOf(lib);
+ int size = dumpInfoTask.sizeOf(lib);
LibraryInfo info = new LibraryInfo(libname, lib.canonicalUri, null, size);
_entityToInfo[lib] = info;
@@ -122,8 +123,8 @@
return null;
}
- int size = compiler.dumpInfoTask.sizeOf(field);
- String code = compiler.dumpInfoTask.codeOf(field);
+ int size = dumpInfoTask.sizeOf(field);
+ String code = dumpInfoTask.codeOf(field);
// TODO(het): Why doesn't `size` account for the code size already?
if (code != null) size += code.length;
@@ -162,7 +163,7 @@
outputUnit: _unitInfoForClass(clazz));
_entityToInfo[clazz] = classInfo;
- int size = compiler.dumpInfoTask.sizeOf(clazz);
+ int size = dumpInfoTask.sizeOf(clazz);
environment.forEachLocalClassMember(clazz, (member) {
if (member.isFunction || member.isGetter || member.isSetter) {
FunctionInfo functionInfo = visitFunction(member);
@@ -213,7 +214,7 @@
ClosureInfo closureInfo = new ClosureInfo(
name: element.name,
outputUnit: _unitInfoForClass(element),
- size: compiler.dumpInfoTask.sizeOf(element));
+ size: dumpInfoTask.sizeOf(element));
_entityToInfo[element] = closureInfo;
FunctionEntity callMethod = closedWorld.elementEnvironment
@@ -229,7 +230,7 @@
}
FunctionInfo visitFunction(FunctionEntity function) {
- int size = compiler.dumpInfoTask.sizeOf(function);
+ int size = dumpInfoTask.sizeOf(function);
// TODO(sigmund): consider adding a small info to represent unreachable
// code here.
if (size == 0 && !shouldKeep(function)) return null;
@@ -260,7 +261,7 @@
: false,
isExternal: function.isExternal,
);
- String code = compiler.dumpInfoTask.codeOf(function);
+ String code = dumpInfoTask.codeOf(function);
List<ParameterInfo> parameters = <ParameterInfo>[];
List<String> inferredParameterTypes = <String>[];
@@ -280,7 +281,7 @@
String sideEffects =
'${_globalInferenceResults.inferredData.getSideEffectsOfElement(function)}';
- int inlinedCount = compiler.dumpInfoTask.inlineCount[function];
+ int inlinedCount = dumpInfoTask.inlineCount[function];
if (inlinedCount == null) inlinedCount = 0;
FunctionInfo info = new FunctionInfo(
@@ -400,10 +401,10 @@
/// The size of the generated output.
int _programSize;
- // A set of javascript AST nodes that we care about the size of.
- // This set is automatically populated when registerEntityAst()
- // is called.
- final Set<jsAst.Node> _tracking = new Set<jsAst.Node>();
+ /// Data associated with javascript AST nodes. The map only contains keys for
+ /// nodes that we care about. Keys are automatically added when
+ /// [registerEntityAst] is called.
+ final Map<jsAst.Node, _CodeData> _nodeData = <jsAst.Node, _CodeData>{};
// A mapping from Dart Entities to Javascript AST Nodes.
final Map<Entity, List<jsAst.Node>> _entityToNodes =
@@ -411,10 +412,6 @@
final Map<ConstantValue, jsAst.Node> _constantToNode =
<ConstantValue, jsAst.Node>{};
- // A mapping from Javascript AST Nodes to the size of their
- // pretty-printed contents.
- final Map<jsAst.Node, int> _nodeToSize = <jsAst.Node, int>{};
-
final Map<Entity, int> inlineCount = <Entity, int>{};
// A mapping from an entity to a list of entities that are
@@ -470,24 +467,15 @@
return selections;
}
- // Returns true if we care about tracking the size of
- // this node.
- bool isTracking(jsAst.Node code) {
- if (compiler.options.dumpInfo) {
- return _tracking.contains(code);
- } else {
- return false;
- }
- }
-
/// Registers that a javascript AST node [code] was produced by the dart
/// Entity [entity].
- void registerEntityAst(Entity entity, jsAst.Node code) {
+ void registerEntityAst(Entity entity, jsAst.Node code,
+ {LibraryEntity library}) {
if (compiler.options.dumpInfo) {
_entityToNodes
.putIfAbsent(entity, () => new List<jsAst.Node>())
.add(code);
- _tracking.add(code);
+ _nodeData[code] ??= _CodeData();
}
}
@@ -496,19 +484,36 @@
assert(_constantToNode[constant] == null ||
_constantToNode[constant] == code);
_constantToNode[constant] = code;
- _tracking.add(code);
+ _nodeData[code] ??= _CodeData();
}
}
- /// Records the size of a dart AST node after it has been pretty-printed into
- /// the output buffer.
- void recordAstSize(jsAst.Node node, int size) {
- if (isTracking(node)) {
- //TODO: should I be incrementing here instead?
- _nodeToSize[node] = size;
+ // TODO(sigmund): delete the stack once we stop emitting the source text.
+ List<_CodeData> _stack = [];
+ void enterNode(jsAst.Node node, int start) {
+ var data = _nodeData[node];
+ if (data != null) {
+ _stack.add(data);
+ data.start = start;
}
}
+ void emit(String string) {
+ // Note: historically we emitted the full body of classes and methods, so
+ // instance methods ended up emitted twice. Once we use a different
+ // encoding of dump info, we also plan to remove this duplication.
+ _stack.forEach((f) => f.text.write(string));
+ }
+
+ void exitNode(jsAst.Node node, int start, int end, int closing) {
+ var data = _nodeData[node];
+ if (data == null) return;
+ var last = _stack.removeLast();
+ assert(data == last);
+ assert(data.start == start);
+ data.end = end;
+ }
+
/// Returns the size of the source code that was generated for an entity.
/// If no source code was produced, return 0.
int sizeOf(Entity entity) {
@@ -519,7 +524,7 @@
}
}
- int sizeOfNode(jsAst.Node node) => _nodeToSize[node] ?? 0;
+ int sizeOfNode(jsAst.Node node) => _nodeData[node].length ?? 0;
String codeOf(Entity entity) {
List<jsAst.Node> code = _entityToNodes[entity];
@@ -527,8 +532,7 @@
// Concatenate rendered ASTs.
StringBuffer sb = new StringBuffer();
for (jsAst.Node ast in code) {
- sb.writeln(jsAst.prettyPrint(ast,
- enableMinification: compiler.options.enableMinification));
+ sb.writeln(_nodeData[ast].text);
}
return sb.toString();
}
@@ -537,7 +541,7 @@
GlobalTypeInferenceResults globalInferenceResults) {
measure(() {
infoCollector = new ElementInfoCollector(
- compiler, closedWorld, globalInferenceResults)
+ compiler, this, closedWorld, globalInferenceResults)
..run();
StringBuffer jsonBuffer = new StringBuffer();
dumpInfoJson(jsonBuffer, closedWorld);
@@ -634,3 +638,15 @@
});
}
}
+
+/// Helper class to store what dump-info will show for a piece of code.
+///
+/// Currently we print out the actual text, in the future, we will only emit
+/// start and end offsets.
+class _CodeData {
+ int start;
+ int end;
+ StringBuffer text = new StringBuffer();
+
+ int get length => end - start;
+}
diff --git a/pkg/compiler/lib/src/js/js.dart b/pkg/compiler/lib/src/js/js.dart
index 9893f5b..523cd66 100644
--- a/pkg/compiler/lib/src/js/js.dart
+++ b/pkg/compiler/lib/src/js/js.dart
@@ -69,20 +69,20 @@
@override
void emit(String string) {
+ monitor?.emit(string);
outBuffer.add(string);
}
@override
void enterNode(Node node, int startPosition) {
+ monitor?.enterNode(node, startPosition);
codePositionListener.onStartPosition(node, startPosition);
}
@override
void exitNode(
Node node, int startPosition, int endPosition, int closingPosition) {
- if (monitor != null) {
- monitor.recordAstSize(node, endPosition - startPosition);
- }
+ monitor?.exitNode(node, startPosition, endPosition, closingPosition);
codePositionListener.onPositions(
node, startPosition, endPosition, closingPosition);
}
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
index 5ef3622..e5cabfe 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
@@ -597,6 +597,13 @@
return js.js('#.#', [cls.holder.name, cls.name]);
}
+ void registerEntityAst(Entity entity, js.Node code, {LibraryEntity library}) {
+ compiler.dumpInfoTask.registerEntityAst(entity, code);
+ // TODO(sigmund): stop recoding associations twice, dump-info already
+ // has library to element dependencies to recover this data.
+ if (library != null) compiler.dumpInfoTask.registerEntityAst(library, code);
+ }
+
js.Statement emitMainFragment(
Program program, DeferredLoadingState deferredLoadingState) {
MainFragment fragment = program.fragments.first;
@@ -792,30 +799,28 @@
.where((Holder holder) => !holder.isStaticStateHolder)
.toList(growable: false);
- Map<Holder, Map<js.Name, js.Expression>> holderCode = {};
+ Map<Holder, List<js.Property>> holderCode = {};
for (Holder holder in holders) {
- holderCode[holder] = <js.Name, js.Expression>{};
+ holderCode[holder] = <js.Property>[];
}
for (Library library in fragment.libraries) {
for (StaticMethod method in library.statics) {
assert(!method.holder.isStaticStateHolder);
var staticMethod = emitStaticMethod(method);
- if (compiler.options.dumpInfo) {
- for (var code in staticMethod.values) {
- compiler.dumpInfoTask.registerEntityAst(method.element, code);
- compiler.dumpInfoTask.registerEntityAst(library.element, code);
- }
- }
- holderCode[method.holder].addAll(staticMethod);
+ staticMethod.forEach((key, value) {
+ var property = new js.Property(js.quoteName(key), value);
+ holderCode[method.holder].add(property);
+ registerEntityAst(method.element, property, library: library.element);
+ });
}
for (Class cls in library.classes) {
assert(!cls.holder.isStaticStateHolder);
var constructor = emitConstructor(cls);
- compiler.dumpInfoTask.registerEntityAst(cls.element, constructor);
- compiler.dumpInfoTask.registerEntityAst(library.element, constructor);
- holderCode[cls.holder][cls.name] = constructor;
+ var property = new js.Property(js.quoteName(cls.name), constructor);
+ registerEntityAst(cls.element, property, library: library.element);
+ holderCode[cls.holder].add(property);
}
}
@@ -823,10 +828,7 @@
List<Holder> activeHolders = [];
for (Holder holder in holders) {
- List<js.Property> properties = [];
- holderCode[holder].forEach((js.Name key, js.Expression value) {
- properties.add(new js.Property(js.quoteName(key), value));
- });
+ List<js.Property> properties = holderCode[holder];
if (properties.isEmpty) {
holderInitializations.add(new js.VariableInitialization(
new js.VariableDeclaration(holder.name, allowRename: false),
@@ -1006,8 +1008,7 @@
var proto = js.js.statement(
'#.prototype = #;', [classReference(cls), emitPrototype(cls)]);
ClassEntity element = cls.element;
- compiler.dumpInfoTask.registerEntityAst(element, proto);
- compiler.dumpInfoTask.registerEntityAst(element.library, proto);
+ registerEntityAst(element, proto, library: element.library);
return proto;
}).toList(growable: false);
@@ -1050,7 +1051,7 @@
emitInstanceMethod(method)
.forEach((js.Expression name, js.Expression code) {
var prop = js.Property(name, code);
- compiler.dumpInfoTask.registerEntityAst(method.element, prop);
+ registerEntityAst(method.element, prop);
properties.add(prop);
});
});
@@ -1216,11 +1217,13 @@
if (cls.isSoftDeferred != softDeferred) continue;
collect(cls);
if (cls.mixinClass != null) {
- mixinCalls.add(js.js.statement('#(#, #)', [
+ js.Statement statement = js.js.statement('#(#, #)', [
locals.find('_mixin', 'hunkHelpers.mixin'),
classReference(cls),
classReference(cls.mixinClass),
- ]));
+ ]);
+ registerEntityAst(cls.element, statement, library: library.element);
+ mixinCalls.add(statement);
}
}
}
@@ -1231,13 +1234,27 @@
? new js.LiteralNull()
: classReference(superclass);
if (list.length == 1) {
- inheritCalls.add(js.js.statement('#(#, #)', [
+ Class cls = list.single;
+ var statement = js.js.statement('#(#, #)', [
locals.find('_inherit', 'hunkHelpers.inherit'),
- classReference(list.single),
+ classReference(cls),
superclassReference
- ]));
+ ]);
+ registerEntityAst(cls.element, statement, library: cls.element.library);
+ inheritCalls.add(statement);
} else {
- var listElements = list.map(classReference).toList();
+ List<js.Expression> listElements = [];
+ // Since inheritMany shares the superclass reference, we attribute it
+ // only to the first subclass.
+ ClassEntity firstClass = list.first.element;
+ registerEntityAst(firstClass, superclassReference,
+ library: firstClass.library);
+ for (Class cls in list) {
+ js.Expression reference = classReference(cls);
+ registerEntityAst(cls.element, reference,
+ library: cls.element.library);
+ listElements.add(reference);
+ }
inheritCalls.add(js.js.statement('#(#, #)', [
locals.find('_inheritMany', 'hunkHelpers.inheritMany'),
superclassReference,
@@ -1271,14 +1288,18 @@
if (method.aliasName != null) {
if (firstAlias) {
firstAlias = false;
- assignments.add(js.js.statement(
+ js.Statement statement = js.js.statement(
assignments.isEmpty
? 'var _ = #.prototype;'
: '_ = #.prototype',
- classReference(cls)));
+ classReference(cls));
+ registerEntityAst(method.element, statement);
+ assignments.add(statement);
}
- assignments.add(js.js.statement('_.# = _.#',
- [js.quoteName(method.aliasName), js.quoteName(method.name)]));
+ js.Statement statement = js.js.statement('_.# = _.#',
+ [js.quoteName(method.aliasName), js.quoteName(method.name)]);
+ registerEntityAst(method.element, statement);
+ assignments.add(statement);
}
}
}
@@ -1489,8 +1510,11 @@
if (method is StaticDartMethod) {
if (method.needsTearOff) {
Holder holder = method.holder;
- inits.add(
- emitInstallTearOff(new js.VariableUse(holder.name), method));
+ js.Statement statement =
+ emitInstallTearOff(new js.VariableUse(holder.name), method);
+ registerEntityAst(method.element, statement,
+ library: library.element);
+ inits.add(statement);
}
}
}
@@ -1508,7 +1532,9 @@
reference = js.js('# = #', [temp, container]);
}
for (InstanceMethod method in methods) {
- inits.add(emitInstallTearOff(reference, method));
+ js.Statement statement = emitInstallTearOff(reference, method);
+ registerEntityAst(method.element, statement);
+ inits.add(statement);
reference = temp; // Second and subsequent calls use temp.
}
}
@@ -1558,8 +1584,11 @@
//
Iterable<js.Statement> statements = fields.map((StaticField field) {
assert(field.holder.isStaticStateHolder);
- return js.js
+ js.Statement statement = js.js
.statement("#.# = #;", [field.holder.name, field.name, field.code]);
+ registerEntityAst(field.element, statement,
+ library: field.element.library);
+ return statement;
});
return wrapPhase('staticFields', statements.toList());
}
@@ -1574,13 +1603,17 @@
LocalAliases locals = LocalAliases();
for (StaticField field in fields) {
assert(field.holder.isStaticStateHolder);
- statements.add(js.js.statement("#(#, #, #, #);", [
+ js.Statement statement = js.js.statement("#(#, #, #, #);", [
locals.find('_lazy', 'hunkHelpers.lazy'),
field.holder.name,
js.quoteName(field.name),
js.quoteName(field.getterName),
field.code
- ]));
+ ]);
+
+ registerEntityAst(field.element, statement,
+ library: field.element.library);
+ statements.add(statement);
}
if (locals.isNotEmpty) {