Support deserialized compilation of the empty program.

R=sigmund@google.com

Review URL: https://codereview.chromium.org/1892093003 .
This commit is contained in:
Johnni Winther 2016-04-18 10:36:34 +02:00
parent ea4a562517
commit 2ccfcf438a
15 changed files with 232 additions and 42 deletions

View file

@ -131,7 +131,7 @@ class ClosureFieldElement extends ElementX
bool get hasResolvedAst => hasTreeElements;
ResolvedAst get resolvedAst {
return new ParsedResolvedAst(this, null, treeElements);
return new ParsedResolvedAst(this, null, null, treeElements);
}
Expression get initializer {
@ -346,7 +346,7 @@ class SynthesizedCallMethodElementX extends BaseFunctionElementX
FunctionExpression parseNode(ParsingContext parsing) => node;
ResolvedAst get resolvedAst {
return new ParsedResolvedAst(this, node, treeElements);
return new ParsedResolvedAst(this, node, node.body, treeElements);
}
Element get analyzableElement => closureClass.methodElement.analyzableElement;
@ -379,6 +379,29 @@ class ClosureScope {
f(LocalVariableElement variable, BoxFieldElement boxField)) {
capturedVariables.forEach(f);
}
String toString() {
StringBuffer sb = new StringBuffer();
if (boxElement != null) {
sb.write('box=$boxElement');
}
if (boxedLoopVariables.isNotEmpty) {
if (sb.isNotEmpty) {
sb.write(',');
}
sb.write('boxedLoopVariables=${boxedLoopVariables}');
}
if (capturedVariables.isNotEmpty) {
if (sb.isNotEmpty) {
sb.write(',');
}
sb.write('capturedVariables=');
capturedVariables.forEach((Local local, BoxFieldElement field) {
sb.write('$local->${field} ');
});
}
return sb.toString();
}
}
class ClosureClassMap {

View file

@ -8,7 +8,7 @@ import 'dart:async' show Future;
import '../common.dart';
import '../common/codegen.dart' show CodegenImpact;
import '../common/resolution.dart' show ResolutionImpact;
import '../common/resolution.dart' show ResolutionImpact, Frontend;
import '../compile_time_constants.dart'
show BackendConstantEnvironment, ConstantCompilerTask;
import '../compiler.dart' show Compiler;
@ -393,6 +393,9 @@ abstract class Backend {
bool supportSerialization: true}) {
return const ImpactStrategy();
}
/// Backend access to the front-end.
Frontend get frontend => compiler.resolution;
}
/// Interface for resolving calls to foreign functions.

View file

@ -4,6 +4,7 @@
library dart2js.common.codegen;
import '../closure.dart' show SynthesizedCallMethodElementX;
import '../common.dart';
import '../compiler.dart' show Compiler;
import '../constants/values.dart' show ConstantValue;
@ -225,6 +226,7 @@ class CodegenRegistry extends Registry {
/// [WorkItem] used exclusively by the [CodegenEnqueuer].
class CodegenWorkItem extends WorkItem {
CodegenRegistry registry;
final ResolvedAst resolvedAst;
factory CodegenWorkItem(Compiler compiler, AstElement element,
ItemCompilationContext compilationContext) {
@ -235,16 +237,14 @@ class CodegenWorkItem extends WorkItem {
assert(invariant(
element, compiler.enqueuer.resolution.hasBeenProcessed(element),
message: "$element has not been resolved."));
assert(invariant(element, element.hasResolvedAst,
message: 'Resolution tree is null for $element in codegen work item'));
return new CodegenWorkItem.internal(element, compilationContext);
ResolvedAst resolvedAst = compiler.backend.frontend.getResolvedAst(element);
return new CodegenWorkItem.internal(resolvedAst, compilationContext);
}
CodegenWorkItem.internal(
AstElement element, ItemCompilationContext compilationContext)
: super(element, compilationContext);
ResolvedAst get resolvedAst => element.resolvedAst;
ResolvedAst resolvedAst, ItemCompilationContext compilationContext)
: this.resolvedAst = resolvedAst,
super(resolvedAst.element, compilationContext);
WorldImpact run(Compiler compiler, CodegenEnqueuer world) {
if (world.isProcessed(element)) return const WorldImpact();

View file

@ -185,8 +185,18 @@ class ListLiteralUse {
}
}
/// Interface for the accessing the front-end analysis.
// TODO(johnniwinther): Find a better name for this.
abstract class Frontend {
/// Returns the `ResolvedAst` for the [element].
ResolvedAst getResolvedAst(Element element);
/// Returns the [ResolutionImpact] for [element].
ResolutionImpact getResolutionImpact(Element element);
}
// TODO(johnniwinther): Rename to `Resolver` or `ResolverContext`.
abstract class Resolution {
abstract class Resolution implements Frontend {
ParsingContext get parsingContext;
DiagnosticReporter get reporter;
CoreTypes get coreTypes;

View file

@ -1850,6 +1850,9 @@ class _CompilerResolution implements Resolution {
bool hasResolvedAst(Element element) {
assert(invariant(element, element.isDeclaration,
message: "Element $element must be the declaration."));
if (compiler.serialization.isDeserialized(element)) {
return compiler.serialization.hasResolvedAst(element);
}
return element is AstElement &&
hasBeenResolved(element) &&
element.hasResolvedAst;
@ -1860,6 +1863,9 @@ class _CompilerResolution implements Resolution {
assert(invariant(element, element.isDeclaration,
message: "Element $element must be the declaration."));
if (hasResolvedAst(element)) {
if (compiler.serialization.isDeserialized(element)) {
return compiler.serialization.getResolvedAst(element);
}
AstElement astElement = element;
return astElement.resolvedAst;
}

View file

@ -1199,9 +1199,12 @@ class AsyncMarker {
/// Canonical list of marker values.
///
/// Added to make [AsyncMarker] enum-like.
static const List<AsyncMarker> values =
const <AsyncMarker>[SYNC, SYNC_STAR, ASYNC, ASYNC_STAR];
static const List<AsyncMarker> values = const <AsyncMarker>[
SYNC,
SYNC_STAR,
ASYNC,
ASYNC_STAR
];
/// Index to this marker within [values].
///
@ -1660,10 +1663,19 @@ abstract class ResolvedAst {
/// The kind of semantics definition used for this object.
ResolvedAstKind get kind;
/// The AST node for [element]. This only available of [kind] is
/// `ResolvedAstKind.PARSED`.
/// The root AST node for the declaration of [element]. This only available if
/// [kind] is `ResolvedAstKind.PARSED`.
Node get node;
/// The AST node for the 'body' of [element].
///
/// For functions and constructors this is the root AST node of the method
/// body, and for variables this is the root AST node of the initializer, if
/// available.
///
/// This only available if [kind] is `ResolvedAstKind.PARSED`.
Node get body;
/// The [TreeElements] containing the resolution data for [node]. This only
/// available of [kind] is `ResolvedAstKind.PARSED`.
TreeElements get elements;
@ -1674,9 +1686,10 @@ abstract class ResolvedAst {
class ParsedResolvedAst implements ResolvedAst {
final Element element;
final Node node;
final Node body;
final TreeElements elements;
ParsedResolvedAst(this.element, this.node, this.elements);
ParsedResolvedAst(this.element, this.node, this.body, this.elements);
ResolvedAstKind get kind => ResolvedAstKind.PARSED;
@ -1698,7 +1711,12 @@ class SynthesizedResolvedAst implements ResolvedAst {
@override
Node get node {
throw new UnsupportedError('$this does not have an AST');
throw new UnsupportedError('$this does not have a root AST node');
}
@override
Node get body {
throw new UnsupportedError('$this does not have a body AST node');
}
String toString() => '$kind:$element';

View file

@ -3269,7 +3269,15 @@ abstract class AstElementMixin implements AstElement {
}
ResolvedAst get resolvedAst {
Node node = definingElement.node;
Node body;
if (definingElement.isField) {
FieldElement field = definingElement;
body = field.initializer;
} else if (node != null && node.asFunctionExpression() != null) {
body = node.asFunctionExpression().body;
}
return new ParsedResolvedAst(
declaration, definingElement.node, definingElement.treeElements);
declaration, node, body, definingElement.treeElements);
}
}

View file

@ -511,6 +511,8 @@ class JavaScriptBackend extends Backend {
final BackendHelpers helpers;
final BackendImpacts impacts;
final JSFrontendAccess frontend;
JavaScriptBackend(Compiler compiler,
{bool generateSourceMap: true,
bool useStartupEmitter: false,
@ -529,6 +531,7 @@ class JavaScriptBackend extends Backend {
: const JavaScriptSourceInformationStrategy(),
helpers = new BackendHelpers(compiler),
impacts = new BackendImpacts(compiler),
frontend = new JSFrontendAccess(compiler),
super(compiler) {
emitter = new CodeEmitterTask(
compiler, namer, generateSourceMap, useStartupEmitter);
@ -2475,6 +2478,32 @@ class JavaScriptBackend extends Backend {
}
}
class JSFrontendAccess implements Frontend {
final Compiler compiler;
JSFrontendAccess(this.compiler);
Resolution get resolution => compiler.resolution;
@override
ResolutionImpact getResolutionImpact(Element element) {
return resolution.getResolutionImpact(element);
}
@override
ResolvedAst getResolvedAst(Element element) {
if (element is SynthesizedCallMethodElementX) {
return element.resolvedAst;
} else if (element is ConstructorBodyElementX) {
return element.resolvedAst;
} else {
assert(invariant(element, resolution.hasResolvedAst(element.declaration),
message: 'No ResolvedAst for $element'));
return resolution.getResolvedAst(element.declaration);
}
}
}
/// Handling of special annotations for tests.
class Annotations {
static final Uri PACKAGE_EXPECT =

View file

@ -18,7 +18,13 @@ import '../common/codegen.dart' show CodegenImpact, CodegenWorkItem;
import '../common/names.dart' show Identifiers, Names, Selectors, Uris;
import '../common/registry.dart' show EagerRegistry, Registry;
import '../common/resolution.dart'
show Feature, ListLiteralUse, MapLiteralUse, Resolution, ResolutionImpact;
show
Feature,
Frontend,
ListLiteralUse,
MapLiteralUse,
Resolution,
ResolutionImpact;
import '../common/tasks.dart' show CompilerTask;
import '../common/work.dart' show ItemCompilationContext;
import '../compile_time_constants.dart';
@ -32,6 +38,7 @@ import '../deferred_load.dart' show DeferredLoadTask;
import '../diagnostics/invariant.dart' show DEBUG_MODE;
import '../dump_info.dart' show DumpInfoTask;
import '../elements/elements.dart';
import '../elements/modelx.dart' show ConstructorBodyElementX;
import '../elements/visitor.dart' show BaseElementVisitor;
import '../enqueue.dart' show Enqueuer, ResolutionEnqueuer;
import '../io/code_output.dart';

View file

@ -757,6 +757,8 @@ bool testResolvedAstEquivalence(
resolvedAst1.element, resolvedAst2.element) &&
new NodeEquivalenceVisitor(strategy).testNodes(resolvedAst1, resolvedAst2,
'node', resolvedAst1.node, resolvedAst2.node) &&
new NodeEquivalenceVisitor(strategy).testNodes(resolvedAst1, resolvedAst2,
'body', resolvedAst1.body, resolvedAst2.body) &&
testTreeElementsEquivalence(resolvedAst1, resolvedAst2, strategy);
}

View file

@ -9,6 +9,7 @@ class Key {
static const Key ALIAS = const Key('alias');
static const Key ARGUMENTS = const Key('arguments');
static const Key ASYNC_MARKER = const Key('asyncMarker');
static const Key BODY = const Key('body');
static const Key BOUND = const Key('bound');
static const Key CACHED_TYPE = const Key('cachedType');
static const Key CALL_STRUCTURE = const Key('callStructure');

View file

@ -108,6 +108,9 @@ class ResolvedAstSerializer extends Visitor {
Key.URI,
elements.analyzedElement.compilationUnit.script.resourceUri,
elements.analyzedElement.compilationUnit.script.resourceUri);
if (resolvedAst.body != null) {
objectEncoder.setInt(Key.BODY, nodeIndices[resolvedAst.body]);
}
AstKind kind;
if (element.enclosingClass is EnumClassElement) {
if (element.name == 'index') {
@ -479,6 +482,11 @@ class ResolvedAstDeserializer {
AstIndexComputer indexComputer = new AstIndexComputer();
Map<Node, int> nodeIndices = indexComputer.nodeIndices;
List<Node> nodeList = indexComputer.nodeList;
Node body;
int bodyNodeIndex = objectDecoder.getInt(Key.BODY, isOptional: true);
if (bodyNodeIndex != null) {
body = nodeList[bodyNodeIndex];
}
root.accept(indexComputer);
List<JumpTarget> jumpTargets = <JumpTarget>[];
@ -594,6 +602,6 @@ class ResolvedAstDeserializer {
}
}
}
return new ParsedResolvedAst(element, root, elements);
return new ParsedResolvedAst(element, root, body, elements);
}
}

View file

@ -54,6 +54,14 @@ class SerializationTask extends CompilerTask implements LibraryDeserializer {
return new DeserializedResolutionWorkItem(
element, context, deserializer.computeWorldImpact(element));
}
bool hasResolvedAst(Element element) {
return deserializer != null ? deserializer.hasResolvedAst(element) : false;
}
ResolvedAst getResolvedAst(Element element) {
return deserializer != null ? deserializer.getResolvedAst(element) : null;
}
}
/// A [ResolutionWorkItem] for a deserialized element.
@ -84,6 +92,7 @@ class DeserializedResolutionWorkItem implements ResolutionWorkItem {
abstract class DeserializerSystem {
Future<LibraryElement> readLibrary(Uri resolvedUri);
bool isDeserialized(Element element);
bool hasResolvedAst(Element element);
ResolvedAst getResolvedAst(Element element);
ResolutionImpact getResolutionImpact(Element element);
WorldImpact computeWorldImpact(Element element);

View file

@ -327,8 +327,8 @@ class LocalsHandler {
void startFunction(AstElement element, ast.Node node) {
assert(invariant(element, element.isImplementation));
Compiler compiler = builder.compiler;
closureData = compiler.closureToClassMapper
.computeClosureToClassMapping(element.resolvedAst);
closureData = compiler.closureToClassMapper.computeClosureToClassMapping(
compiler.backend.frontend.getResolvedAst(element.declaration));
if (element is FunctionElement) {
FunctionElement functionElement = element;
@ -457,7 +457,8 @@ class LocalsHandler {
builder.reporter.internalError(builder.compiler.currentElement,
"Runtime type information not available for $local.");
} else {
builder.reporter.internalError(local, "Cannot find value $local.");
builder.reporter.internalError(
local, "Cannot find value $local in ${directLocals.keys}.");
}
}
HInstruction value = directLocals[local];
@ -1377,6 +1378,7 @@ class SsaBuilder extends ast.Visitor
if (compiler.elementHasCompileTimeError(element)) return false;
FunctionElement function = element;
ResolvedAst functionResolvedAst = backend.frontend.getResolvedAst(function);
bool insideLoop = loopNesting > 0 || graph.calledInLoop;
// Bail out early if the inlining decision is in the cache and we can't
@ -1436,7 +1438,7 @@ class SsaBuilder extends ast.Visitor
bool doesNotContainCode() {
// A function with size 1 does not contain any code.
return InlineWeeder.canBeInlined(function, 1, true,
return InlineWeeder.canBeInlined(functionResolvedAst, 1, true,
enableUserAssertions: compiler.options.enableUserAssertions);
}
@ -1444,7 +1446,7 @@ class SsaBuilder extends ast.Visitor
// The call is on a path which is executed rarely, so inline only if it
// does not make the program larger.
if (isCalledOnce(element)) {
return InlineWeeder.canBeInlined(function, -1, false,
return InlineWeeder.canBeInlined(functionResolvedAst, -1, false,
enableUserAssertions: compiler.options.enableUserAssertions);
}
// TODO(sra): Measure if inlining would 'reduce' the size. One desirable
@ -1482,7 +1484,7 @@ class SsaBuilder extends ast.Visitor
if (cachedCanBeInlined == true) {
// We may have forced the inlining of some methods. Therefore check
// if we can inline this method regardless of size.
assert(InlineWeeder.canBeInlined(function, -1, false,
assert(InlineWeeder.canBeInlined(functionResolvedAst, -1, false,
allowLoops: true,
enableUserAssertions: compiler.options.enableUserAssertions));
return true;
@ -1507,7 +1509,7 @@ class SsaBuilder extends ast.Visitor
}
bool canInline;
canInline = InlineWeeder.canBeInlined(
function, maxInliningNodes, useMaxInliningNodes,
functionResolvedAst, maxInliningNodes, useMaxInliningNodes,
enableUserAssertions: compiler.options.enableUserAssertions);
if (canInline) {
backend.inlineCache.markAsInlinable(element, insideLoop: insideLoop);
@ -1531,7 +1533,7 @@ class SsaBuilder extends ast.Visitor
}
List<HInstruction> compiledArguments = completeSendArgumentsList(
function, selector, providedArguments, currentNode);
enterInlinedMethod(function, currentNode, compiledArguments,
enterInlinedMethod(function, functionResolvedAst, compiledArguments,
instanceType: instanceType);
inlinedFrom(function, () {
if (!isReachable) {
@ -1678,7 +1680,7 @@ class SsaBuilder extends ast.Visitor
HGraph buildMethod(FunctionElement functionElement) {
assert(invariant(functionElement, functionElement.isImplementation));
graph.calledInLoop = compiler.world.isCalledInLoop(functionElement);
ast.FunctionExpression function = functionElement.node;
ast.FunctionExpression function = resolvedAst.node;
assert(function != null);
assert(elements.getFunctionDefinition(function) != null);
openFunction(functionElement, function);
@ -1832,9 +1834,12 @@ class SsaBuilder extends ast.Visitor
void setupStateForInlining(
FunctionElement function, List<HInstruction> compiledArguments,
{InterfaceType instanceType}) {
ResolvedAst resolvedAst =
compiler.backend.frontend.getResolvedAst(function.declaration);
assert(resolvedAst != null);
localsHandler = new LocalsHandler(this, function, instanceType);
localsHandler.closureData = compiler.closureToClassMapper
.computeClosureToClassMapping(function.resolvedAst);
localsHandler.closureData =
compiler.closureToClassMapper.computeClosureToClassMapping(resolvedAst);
returnLocal = new SyntheticLocal("result", function);
localsHandler.updateLocal(returnLocal, graph.addConstantNull(compiler));
@ -1863,8 +1868,6 @@ class SsaBuilder extends ast.Visitor
}
assert(argumentIndex == compiledArguments.length);
resolvedAst = function.resolvedAst;
assert(resolvedAst != null);
returnType = signature.type.returnType;
stack = <HInstruction>[];
@ -2008,7 +2011,7 @@ class SsaBuilder extends ast.Visitor
// Build the initializers in the context of the new constructor.
ResolvedAst oldResolvedAst = resolvedAst;
resolvedAst = callee.resolvedAst;
resolvedAst = backend.frontend.getResolvedAst(callee);
ClosureClassMap oldClosureData = localsHandler.closureData;
ClosureClassMap newClosureData = compiler.closureToClassMapper
.computeClosureToClassMapping(resolvedAst);
@ -2175,7 +2178,7 @@ class SsaBuilder extends ast.Visitor
} else {
ast.Node right = initializer;
ResolvedAst savedResolvedAst = resolvedAst;
resolvedAst = member.resolvedAst;
resolvedAst = backend.frontend.getResolvedAst(member);
// In case the field initializer uses closures, run the
// closure to class mapper.
compiler.closureToClassMapper
@ -2382,7 +2385,7 @@ class SsaBuilder extends ast.Visitor
bodyCallInputs.add(interceptor);
}
bodyCallInputs.add(newObject);
ResolvedAst resolvedAst = constructor.resolvedAst;
ResolvedAst resolvedAst = backend.frontend.getResolvedAst(constructor);
ast.Node node = resolvedAst.node;
ClosureClassMap parameterClosureData =
compiler.closureToClassMapper.getMappingForNestedFunction(node);
@ -7879,8 +7882,8 @@ class SsaBuilder extends ast.Visitor
* This method is invoked before inlining the body of [function] into this
* [SsaBuilder].
*/
void enterInlinedMethod(FunctionElement function, ast.Node _,
List<HInstruction> compiledArguments,
void enterInlinedMethod(FunctionElement function,
ResolvedAst functionResolvedAst, List<HInstruction> compiledArguments,
{InterfaceType instanceType}) {
AstInliningState state = new AstInliningState(
function,
@ -7891,6 +7894,7 @@ class SsaBuilder extends ast.Visitor
localsHandler,
inTryStatement,
allInlinedFunctionsCalledOnce && isFunctionCalledOnce(function));
resolvedAst = functionResolvedAst;
inliningStack.add(state);
// Setting up the state of the (AST) builder is performed even when the
@ -8087,14 +8091,14 @@ class InlineWeeder extends ast.Visitor {
this.enableUserAssertions);
static bool canBeInlined(
FunctionElement function, int maxInliningNodes, bool useMaxInliningNodes,
ResolvedAst resolvedAst, int maxInliningNodes, bool useMaxInliningNodes,
{bool allowLoops: false, bool enableUserAssertions: null}) {
assert(enableUserAssertions is bool); // Ensure we passed it.
if (function.resolvedAst.elements.containsTryStatement) return false;
if (resolvedAst.elements.containsTryStatement) return false;
InlineWeeder weeder = new InlineWeeder(maxInliningNodes,
useMaxInliningNodes, allowLoops, enableUserAssertions);
ast.FunctionExpression functionExpression = function.node;
ast.FunctionExpression functionExpression = resolvedAst.node;
weeder.visit(functionExpression.initializers);
weeder.visit(functionExpression.body);
weeder.visit(functionExpression.asyncModifier);

View file

@ -0,0 +1,62 @@
// Copyright (c) 2016, 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.
library dart2js.serialization_compilation_test;
import 'dart:async';
import 'package:async_helper/async_helper.dart';
import 'package:expect/expect.dart';
import 'package:compiler/src/commandline_options.dart';
import 'package:compiler/src/common/backend_api.dart';
import 'package:compiler/src/common/names.dart';
import 'package:compiler/src/compiler.dart';
import 'package:compiler/src/filenames.dart';
import 'memory_compiler.dart';
import 'serialization_helper.dart';
import 'serialization_test_data.dart';
import 'output_collector.dart';
main(List<String> args) {
asyncTest(() async {
Arguments arguments = new Arguments.from(args);
String serializedData = await serializeDartCore(
arguments: arguments,
serializeResolvedAst: true);
if (arguments.filename != null) {
Uri entryPoint = Uri.base.resolve(nativeToUriPath(arguments.filename));
await compile(serializedData, entryPoint, null);
} else {
Uri entryPoint = Uri.parse('memory:main.dart');
// TODO(johnniwinther): Handle the remaining tests.
for (Test test in TESTS.sublist(0, 1)) {
await compile(serializedData, entryPoint, test,
verbose: arguments.verbose);
}
}
});
}
Future compile(String serializedData, Uri entryPoint, Test test,
{bool verbose: false}) async {
String testDescription =
test != null ? test.sourceFiles[entryPoint.path] : '${entryPoint}';
print('------------------------------------------------------------------');
print('compile ${testDescription}');
print('------------------------------------------------------------------');
OutputCollector outputCollector = new OutputCollector();
await runCompiler(
entryPoint: entryPoint,
memorySourceFiles: test != null ? test.sourceFiles : const {},
options: [Flags.disableTypeInference,
Flags.disableInlining,
Flags.noSourceMaps],
outputProvider: outputCollector,
beforeRun: (Compiler compiler) {
deserialize(compiler, serializedData, deserializeResolvedAst: true);
});
if (verbose) {
print(outputCollector.getOutput('', 'js'));
}
}