Support in-memory CodegenResult serialization/deserialization
The serialization/deserialization is performed in test-mode.
Change-Id: I90f31a6a88dbdf2c38beb9b5409ce3c52557e227
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/102740
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
diff --git a/pkg/compiler/lib/src/backend_strategy.dart b/pkg/compiler/lib/src/backend_strategy.dart
index 7c14557..444937c 100644
--- a/pkg/compiler/lib/src/backend_strategy.dart
+++ b/pkg/compiler/lib/src/backend_strategy.dart
@@ -38,7 +38,7 @@
SelectorConstraintsStrategy selectorConstraintsStrategy);
/// Creates the [WorkItemBuilder] used by the codegen enqueuer.
- WorkItemBuilder createCodegenWorkItemBuilder();
+ WorkItemBuilder createCodegenWorkItemBuilder(JClosedWorld closedWorld);
/// Creates the [SsaBuilder] used for the element model.
SsaBuilder createSsaBuilder(
diff --git a/pkg/compiler/lib/src/common/codegen.dart b/pkg/compiler/lib/src/common/codegen.dart
index eaaa3f1..852152d 100644
--- a/pkg/compiler/lib/src/common/codegen.dart
+++ b/pkg/compiler/lib/src/common/codegen.dart
@@ -8,22 +8,35 @@
import '../common_elements.dart';
import '../constants/values.dart';
+import '../deferred_load.dart';
import '../elements/entities.dart';
import '../elements/types.dart' show DartType, InterfaceType;
+import '../inferrer/abstract_value_domain.dart';
+import '../io/source_information.dart';
import '../js/js.dart' as js;
-import '../js_backend/namer.dart' show Namer;
+import '../js_backend/namer.dart';
import '../js_emitter/code_emitter_task.dart' show Emitter;
import '../native/behavior.dart';
+import '../serialization/serialization.dart';
import '../universe/feature.dart';
+import '../universe/selector.dart';
import '../universe/use.dart' show ConstantUse, DynamicUse, StaticUse, TypeUse;
import '../universe/world_impact.dart'
show WorldImpact, WorldImpactBuilderImpl, WorldImpactVisitor;
import '../util/enumset.dart';
import '../util/util.dart';
+import '../world.dart';
class CodegenImpact extends WorldImpact {
const CodegenImpact();
+ factory CodegenImpact.readFromDataSource(DataSource source) =
+ _CodegenImpact.readFromDataSource;
+
+ void writeToDataSink(DataSink sink) {
+ throw new UnsupportedError('CodegenImpact.writeToDataSink');
+ }
+
Iterable<Pair<DartType, DartType>> get typeVariableBoundsSubtypeChecks {
return const <Pair<DartType, DartType>>[];
}
@@ -47,8 +60,10 @@
}
class _CodegenImpact extends WorldImpactBuilderImpl implements CodegenImpact {
- Setlet<Pair<DartType, DartType>> _typeVariableBoundsSubtypeChecks;
- Setlet<String> _constSymbols;
+ static const String tag = 'codegen-impact';
+
+ Set<Pair<DartType, DartType>> _typeVariableBoundsSubtypeChecks;
+ Set<String> _constSymbols;
List<Set<ClassEntity>> _specializedGetInterceptors;
bool _usesInterceptor = false;
EnumSet<AsyncMarker> _asyncMarkers;
@@ -58,6 +73,108 @@
_CodegenImpact();
+ _CodegenImpact.internal(
+ Set<DynamicUse> dynamicUses,
+ Set<StaticUse> staticUses,
+ Set<TypeUse> typeUses,
+ Set<ConstantUse> constantUses,
+ this._typeVariableBoundsSubtypeChecks,
+ this._constSymbols,
+ this._specializedGetInterceptors,
+ this._usesInterceptor,
+ this._asyncMarkers,
+ this._genericInstantiations,
+ this._nativeBehaviors,
+ this._nativeMethods)
+ : super.internal(dynamicUses, staticUses, typeUses, constantUses);
+
+ factory _CodegenImpact.readFromDataSource(DataSource source) {
+ source.begin(tag);
+ Set<DynamicUse> dynamicUses = source
+ .readList(() => DynamicUse.readFromDataSource(source),
+ emptyAsNull: true)
+ ?.toSet();
+ Set<StaticUse> staticUses = source
+ .readList(() => StaticUse.readFromDataSource(source), emptyAsNull: true)
+ ?.toSet();
+ Set<TypeUse> typeUses = source
+ .readList(() => TypeUse.readFromDataSource(source), emptyAsNull: true)
+ ?.toSet();
+ Set<ConstantUse> constantUses = source
+ .readList(() => ConstantUse.readFromDataSource(source),
+ emptyAsNull: true)
+ ?.toSet();
+ Set<Pair<DartType, DartType>> typeVariableBoundsSubtypeChecks =
+ source.readList(() {
+ return new Pair(source.readDartType(), source.readDartType());
+ }, emptyAsNull: true)?.toSet();
+ Set<String> constSymbols = source.readStrings(emptyAsNull: true)?.toSet();
+ List<Set<ClassEntity>> specializedGetInterceptors = source.readList(() {
+ return source.readClasses().toSet();
+ }, emptyAsNull: true);
+ bool usesInterceptor = source.readBool();
+ int asyncMarkersValue = source.readIntOrNull();
+ EnumSet<AsyncMarker> asyncMarkers = asyncMarkersValue != null
+ ? new EnumSet.fromValue(asyncMarkersValue)
+ : null;
+ Set<GenericInstantiation> genericInstantiations = source
+ .readList(() => GenericInstantiation.readFromDataSource(source),
+ emptyAsNull: true)
+ ?.toSet();
+ List<NativeBehavior> nativeBehaviors = source.readList(
+ () => NativeBehavior.readFromDataSource(source),
+ emptyAsNull: true);
+ Set<FunctionEntity> nativeMethods =
+ source.readMembers<FunctionEntity>(emptyAsNull: true)?.toSet();
+ source.end(tag);
+ return new _CodegenImpact.internal(
+ dynamicUses,
+ staticUses,
+ typeUses,
+ constantUses,
+ typeVariableBoundsSubtypeChecks,
+ constSymbols,
+ specializedGetInterceptors,
+ usesInterceptor,
+ asyncMarkers,
+ genericInstantiations,
+ nativeBehaviors,
+ nativeMethods);
+ }
+
+ @override
+ void writeToDataSink(DataSink sink) {
+ sink.begin(tag);
+ sink.writeList(dynamicUses, (DynamicUse use) => use.writeToDataSink(sink),
+ allowNull: true);
+ sink.writeList(staticUses, (StaticUse use) => use.writeToDataSink(sink),
+ allowNull: true);
+ sink.writeList(typeUses, (TypeUse use) => use.writeToDataSink(sink),
+ allowNull: true);
+ sink.writeList(constantUses, (ConstantUse use) => use.writeToDataSink(sink),
+ allowNull: true);
+ sink.writeList<Pair<DartType, DartType>>(_typeVariableBoundsSubtypeChecks,
+ (pair) {
+ sink.writeDartType(pair.a);
+ sink.writeDartType(pair.b);
+ }, allowNull: true);
+ sink.writeStrings(_constSymbols, allowNull: true);
+ sink.writeList(_specializedGetInterceptors, sink.writeClasses,
+ allowNull: true);
+ sink.writeBool(_usesInterceptor);
+ sink.writeIntOrNull(_asyncMarkers?.value);
+ sink.writeList(
+ _genericInstantiations,
+ (GenericInstantiation instantiation) =>
+ instantiation.writeToDataSink(sink),
+ allowNull: true);
+ sink.writeList(_nativeBehaviors,
+ (NativeBehavior behavior) => behavior.writeToDataSink(sink),
+ allowNull: true);
+ sink.writeMembers(_nativeMethods, allowNull: true);
+ sink.end(tag);
+ }
+
@override
void apply(WorldImpactVisitor visitor) {
staticUses.forEach(visitor.visitStaticUse);
@@ -67,7 +184,7 @@
void registerTypeVariableBoundsSubtypeCheck(
DartType subtype, DartType supertype) {
- _typeVariableBoundsSubtypeChecks ??= new Setlet<Pair<DartType, DartType>>();
+ _typeVariableBoundsSubtypeChecks ??= {};
_typeVariableBoundsSubtypeChecks
.add(new Pair<DartType, DartType>(subtype, supertype));
}
@@ -80,7 +197,7 @@
}
void registerConstSymbol(String name) {
- _constSymbols ??= new Setlet<String>();
+ _constSymbols ??= {};
_constSymbols.add(name);
}
@@ -121,7 +238,7 @@
}
void registerGenericInstantiation(GenericInstantiation instantiation) {
- _genericInstantiations ??= new Set<GenericInstantiation>();
+ _genericInstantiations ??= {};
_genericInstantiations.add(instantiation);
}
@@ -149,6 +266,33 @@
Iterable<FunctionEntity> get nativeMethods {
return _nativeMethods ?? const [];
}
+
+ @override
+ String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.write('CodegenImpact:');
+ WorldImpact.printOn(sb, this);
+
+ void add(String title, Iterable iterable) {
+ if (iterable.isNotEmpty) {
+ sb.write('\n $title:');
+ iterable.forEach((e) => sb.write('\n $e'));
+ }
+ }
+
+ add('typeVariableBoundsSubtypeChecks', typeVariableBoundsSubtypeChecks);
+ add('constSymbols', constSymbols);
+ add('specializedGetInterceptors', specializedGetInterceptors);
+ if (usesInterceptor) {
+ sb.write('\n usesInterceptor: true');
+ }
+ add('asyncMarkers', asyncMarkers);
+ add('genericInstantiations', genericInstantiations);
+ add('nativeBehaviors', nativeBehaviors);
+ add('nativeMethods', nativeMethods);
+
+ return sb.toString();
+ }
}
// TODO(johnniwinther): Split this class into interface and implementation.
@@ -245,6 +389,8 @@
}
class CodegenResult {
+ static const String tag = 'codegen-result';
+
final js.Fun code;
final CodegenImpact impact;
final Iterable<ModularName> modularNames;
@@ -252,6 +398,35 @@
CodegenResult(
this.code, this.impact, this.modularNames, this.modularExpressions);
+
+ /// Reads a [CodegenResult] object from [source].
+ ///
+ /// The [ModularName] and [ModularExpression] nodes read during
+ /// deserialization are collected in [modularNames] and [modularExpressions]
+ /// to avoid the need for visiting the [code] node post deserialization.
+ factory CodegenResult.readFromDataSource(
+ DataSource source,
+ List<ModularName> modularNames,
+ List<ModularExpression> modularExpressions) {
+ source.begin(tag);
+ js.Fun code = source.readJsNodeOrNull();
+ CodegenImpact impact = CodegenImpact.readFromDataSource(source);
+ source.end(tag);
+ return new CodegenResult(code, impact, modularNames, modularExpressions);
+ }
+
+ /// Writes the [CodegenResult] object to [sink].
+ ///
+ /// The [modularNames] and [modularExpressions] fields are not directly
+ /// serializes because these are embedded in the [code] node and collected
+ /// through this during deserialization.
+ void writeToDataSink(DataSink sink) {
+ sink.begin(tag);
+ sink.writeJsNodeOrNull(code);
+ impact.writeToDataSink(sink);
+ sink.end(tag);
+ }
+
void applyModularState(Namer namer, Emitter emitter) {
for (ModularName name in modularNames) {
switch (name.kind) {
@@ -386,6 +561,8 @@
}
class ModularName extends js.Name implements js.AstContainer {
+ static const String tag = 'modular-name';
+
final ModularNameKind kind;
js.Name _value;
final Object data;
@@ -393,6 +570,111 @@
ModularName(this.kind, {this.data, this.set});
+ factory ModularName.readFromDataSource(DataSource source) {
+ source.begin(tag);
+ ModularNameKind kind = source.readEnum(ModularNameKind.values);
+ Object data;
+ Set<ClassEntity> set;
+ switch (kind) {
+ case ModularNameKind.rtiField:
+ break;
+ case ModularNameKind.globalPropertyNameForType:
+ case ModularNameKind.runtimeTypeName:
+ bool dataIsClassEntity = source.readBool();
+ if (dataIsClassEntity) {
+ data = source.readClass();
+ } else {
+ data = source.readTypedef();
+ }
+ break;
+ case ModularNameKind.className:
+ case ModularNameKind.operatorIs:
+ case ModularNameKind.substitution:
+ case ModularNameKind.globalPropertyNameForClass:
+ data = source.readClass();
+ break;
+ case ModularNameKind.aliasedSuperMember:
+ case ModularNameKind.staticClosure:
+ case ModularNameKind.methodProperty:
+ case ModularNameKind.instanceField:
+ case ModularNameKind.instanceMethod:
+ case ModularNameKind.lazyInitializer:
+ case ModularNameKind.globalPropertyNameForMember:
+ data = source.readMember();
+ break;
+ case ModularNameKind.operatorIsType:
+ data = source.readDartType();
+ break;
+ case ModularNameKind.invocation:
+ data = Selector.readFromDataSource(source);
+ break;
+ case ModularNameKind.nameForGetInterceptor:
+ set = source.readClasses().toSet();
+ break;
+ case ModularNameKind.nameForGetOneShotInterceptor:
+ data = Selector.readFromDataSource(source);
+ set = source.readClasses().toSet();
+ break;
+ case ModularNameKind.asName:
+ data = source.readString();
+ break;
+ }
+ source.end(tag);
+ return new ModularName(kind, data: data, set: set);
+ }
+
+ void writeToDataSink(DataSink sink) {
+ sink.begin(tag);
+ sink.writeEnum(kind);
+ switch (kind) {
+ case ModularNameKind.rtiField:
+ break;
+ case ModularNameKind.globalPropertyNameForType:
+ case ModularNameKind.runtimeTypeName:
+ sink.writeBool(data is ClassEntity);
+ if (data is ClassEntity) {
+ sink.writeClass(data);
+ } else {
+ sink.writeTypedef(data);
+ }
+ break;
+ case ModularNameKind.className:
+ case ModularNameKind.operatorIs:
+ case ModularNameKind.substitution:
+ case ModularNameKind.globalPropertyNameForClass:
+ sink.writeClass(data);
+ break;
+ case ModularNameKind.aliasedSuperMember:
+ case ModularNameKind.staticClosure:
+ case ModularNameKind.methodProperty:
+ case ModularNameKind.instanceField:
+ case ModularNameKind.instanceMethod:
+ case ModularNameKind.lazyInitializer:
+ case ModularNameKind.globalPropertyNameForMember:
+ sink.writeMember(data);
+ break;
+ case ModularNameKind.operatorIsType:
+ sink.writeDartType(data);
+ break;
+ case ModularNameKind.invocation:
+ Selector selector = data;
+ selector.writeToDataSink(sink);
+ break;
+ case ModularNameKind.nameForGetInterceptor:
+ sink.writeClasses(set);
+ break;
+ case ModularNameKind.nameForGetOneShotInterceptor:
+ Selector selector = data;
+ selector.writeToDataSink(sink);
+ sink.writeClasses(set);
+ break;
+ case ModularNameKind.asName:
+ sink.writeString(data);
+ break;
+ }
+ sink.end(tag);
+ }
+
js.Name get value {
assert(_value != null);
return _value;
@@ -448,7 +730,7 @@
}
@override
- String toString() => 'ModularName(kind=$kind,data=$data,value=$value)';
+ String toString() => 'ModularName(kind=$kind,data=$data,value=${value?.key})';
}
enum ModularExpressionKind {
@@ -462,12 +744,78 @@
class ModularExpression extends js.DeferredExpression
implements js.AstContainer {
+ static const String tag = 'modular-expression';
+
final ModularExpressionKind kind;
final Object data;
js.Expression _value;
ModularExpression(this.kind, this.data);
+ factory ModularExpression.readFromDataSource(DataSource source) {
+ source.begin(tag);
+ ModularExpressionKind kind = source.readEnum(ModularExpressionKind.values);
+ Object data;
+ switch (kind) {
+ case ModularExpressionKind.globalObjectForLibrary:
+ data = source.readLibrary();
+ break;
+ case ModularExpressionKind.globalObjectForClass:
+ data = source.readClass();
+ break;
+ case ModularExpressionKind.globalObjectForType:
+ bool dataIsClassEntity = source.readBool();
+ if (dataIsClassEntity) {
+ data = source.readClass();
+ } else {
+ data = source.readTypedef();
+ }
+ break;
+ case ModularExpressionKind.globalObjectForMember:
+ data = source.readMember();
+ break;
+ case ModularExpressionKind.constant:
+ data = source.readConstant();
+ break;
+ case ModularExpressionKind.embeddedGlobalAccess:
+ data = source.readString();
+ break;
+ }
+ source.end(tag);
+ return new ModularExpression(kind, data);
+ }
+
+ void writeToDataSink(DataSink sink) {
+ sink.begin(tag);
+ sink.writeEnum(kind);
+ switch (kind) {
+ case ModularExpressionKind.globalObjectForLibrary:
+ sink.writeLibrary(data);
+ break;
+ case ModularExpressionKind.globalObjectForClass:
+ sink.writeClass(data);
+ break;
+ case ModularExpressionKind.globalObjectForType:
+ sink.writeBool(data is ClassEntity);
+ if (data is ClassEntity) {
+ sink.writeClass(data);
+ } else {
+ sink.writeTypedef(data);
+ }
+ break;
+ case ModularExpressionKind.globalObjectForMember:
+ sink.writeMember(data);
+ break;
+ case ModularExpressionKind.constant:
+ sink.writeConstant(data);
+ break;
+ case ModularExpressionKind.embeddedGlobalAccess:
+ sink.writeString(data);
+ break;
+ }
+ sink.end(tag);
+ }
+
@override
js.Expression get value {
assert(_value != null);
@@ -514,3 +862,1208 @@
return sb.toString();
}
}
+
+enum JsNodeKind {
+ comment,
+ await,
+ regExpLiteral,
+ property,
+ objectInitializer,
+ arrayHole,
+ arrayInitializer,
+ parentheses,
+ modularName,
+ asyncName,
+ stringBackedName,
+ stringConcatenation,
+ literalNull,
+ literalNumber,
+ literalString,
+ literalStringFromName,
+ literalBool,
+ modularExpression,
+ function,
+ namedFunction,
+ access,
+ parameter,
+ variableDeclaration,
+ thisExpression,
+ variableUse,
+ postfix,
+ prefix,
+ binary,
+ callExpression,
+ newExpression,
+ conditional,
+ variableInitialization,
+ assignment,
+ variableDeclarationList,
+ literalExpression,
+ dartYield,
+ literalStatement,
+ labeledStatement,
+ functionDeclaration,
+ switchDefault,
+ switchCase,
+ switchStatement,
+ catchClause,
+ tryStatement,
+ throwStatement,
+ returnStatement,
+ breakStatement,
+ continueStatement,
+ doStatement,
+ whileStatement,
+ forInStatement,
+ forStatement,
+ ifStatement,
+ emptyStatement,
+ expressionStatement,
+ block,
+ program,
+}
+
+/// Tags used for debugging serialization/deserialization boundary mismatches.
+class JsNodeTags {
+ static const String tag = 'js-node';
+ static const String comment = 'js-comment';
+ static const String await = 'js-await';
+ static const String regExpLiteral = 'js-regExpLiteral';
+ static const String property = 'js-property';
+ static const String objectInitializer = 'js-objectInitializer';
+ static const String arrayHole = 'js-arrayHole';
+ static const String arrayInitializer = 'js-arrayInitializer';
+ static const String parentheses = 'js-parentheses';
+ static const String modularName = 'js-modularName';
+ static const String asyncName = 'js-asyncName';
+ static const String stringBackedName = 'js-stringBackedName';
+ static const String stringConcatenation = 'js-stringConcatenation';
+ static const String literalNull = 'js-literalNull';
+ static const String literalNumber = 'js-literalNumber';
+ static const String literalString = 'js-literalString';
+ static const String literalStringFromName = 'js-literalStringFromName';
+ static const String literalBool = 'js-literalBool';
+ static const String modularExpression = 'js-modularExpression';
+ static const String function = 'js-function';
+ static const String namedFunction = 'js-namedFunction';
+ static const String access = 'js-access';
+ static const String parameter = 'js-parameter';
+ static const String variableDeclaration = 'js-variableDeclaration';
+ static const String thisExpression = 'js-thisExpression';
+ static const String variableUse = 'js-variableUse';
+ static const String postfix = 'js-postfix';
+ static const String prefix = 'js-prefix';
+ static const String binary = 'js-binary';
+ static const String callExpression = 'js-callExpression';
+ static const String newExpression = 'js-newExpression';
+ static const String conditional = 'js-conditional';
+ static const String variableInitialization = 'js-variableInitialization';
+ static const String assignment = 'js-assignment';
+ static const String variableDeclarationList = 'js-variableDeclarationList';
+ static const String literalExpression = 'js-literalExpression';
+ static const String dartYield = 'js-dartYield';
+ static const String literalStatement = 'js-literalStatement';
+ static const String labeledStatement = 'js-labeledStatement';
+ static const String functionDeclaration = 'js-functionDeclaration';
+ static const String switchDefault = 'js-switchDefault';
+ static const String switchCase = 'js-switchCase';
+ static const String switchStatement = 'js-switchStatement';
+ static const String catchClause = 'js-catchClause';
+ static const String tryStatement = 'js-tryStatement';
+ static const String throwStatement = 'js-throwStatement';
+ static const String returnStatement = 'js-returnStatement';
+ static const String breakStatement = 'js-breakStatement';
+ static const String continueStatement = 'js-continueStatement';
+ static const String doStatement = 'js-doStatement';
+ static const String whileStatement = 'js-whileStatement';
+ static const String forInStatement = 'js-forInStatement';
+ static const String forStatement = 'js-forStatement';
+ static const String ifStatement = 'js-ifStatement';
+ static const String emptyStatement = 'js-emptyStatement';
+ static const String expressionStatement = 'js-expressionStatement';
+ static const String block = 'js-block';
+ static const String program = 'js-program';
+}
+
+/// Visitor that serializes a [js.Node] into a [DataSink].
+class JsNodeSerializer implements js.NodeVisitor<void> {
+ final DataSink sink;
+
+ JsNodeSerializer._(this.sink);
+
+ static void writeToDataSink(DataSink sink, js.Node node) {
+ sink.begin(JsNodeTags.tag);
+ JsNodeSerializer serializer = new JsNodeSerializer._(sink);
+ serializer.visit(node);
+ sink.end(JsNodeTags.tag);
+ }
+
+ void visit(js.Node node, {bool allowNull: false}) {
+ if (allowNull) {
+ sink.writeBool(node != null);
+ if (node != null) {
+ node.accept(this);
+ }
+ } else {
+ node.accept(this);
+ }
+ }
+
+ void visitList(Iterable<js.Node> nodes) {
+ sink.writeList(nodes, visit);
+ }
+
+ void _writeInfo(js.Node node) {
+ SourceInformation.writeToDataSink(sink, node.sourceInformation);
+ }
+
+ @override
+ void visitInterpolatedDeclaration(js.InterpolatedDeclaration node) {
+ throw new UnsupportedError('JsNodeSerializer.visitInterpolatedDeclaration');
+ }
+
+ @override
+ void visitInterpolatedStatement(js.InterpolatedStatement node) {
+ throw new UnsupportedError('JsNodeSerializer.visitInterpolatedStatement');
+ }
+
+ @override
+ void visitInterpolatedSelector(js.InterpolatedSelector node) {
+ throw new UnsupportedError('JsNodeSerializer.visitInterpolatedDeclaration');
+ }
+
+ @override
+ void visitInterpolatedParameter(js.InterpolatedParameter node) {
+ throw new UnsupportedError('JsNodeSerializer.visitInterpolatedParameter');
+ }
+
+ @override
+ void visitInterpolatedLiteral(js.InterpolatedLiteral node) {
+ throw new UnsupportedError('JsNodeSerializer.visitInterpolatedLiteral');
+ }
+
+ @override
+ void visitInterpolatedExpression(js.InterpolatedExpression node) {
+ throw new UnsupportedError('JsNodeSerializer.visitInterpolatedExpression');
+ }
+
+ @override
+ void visitComment(js.Comment node) {
+ sink.writeEnum(JsNodeKind.comment);
+ sink.begin(JsNodeTags.comment);
+ sink.writeString(node.comment);
+ sink.end(JsNodeTags.comment);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitAwait(js.Await node) {
+ sink.writeEnum(JsNodeKind.await);
+ sink.begin(JsNodeTags.await);
+ visit(node.expression);
+ sink.end(JsNodeTags.await);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitRegExpLiteral(js.RegExpLiteral node) {
+ sink.writeEnum(JsNodeKind.regExpLiteral);
+ sink.begin(JsNodeTags.regExpLiteral);
+ sink.writeString(node.pattern);
+ sink.end(JsNodeTags.regExpLiteral);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitProperty(js.Property node) {
+ sink.writeEnum(JsNodeKind.property);
+ sink.begin(JsNodeTags.property);
+ visit(node.name);
+ visit(node.value);
+ sink.end(JsNodeTags.property);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitObjectInitializer(js.ObjectInitializer node) {
+ sink.writeEnum(JsNodeKind.objectInitializer);
+ sink.begin(JsNodeTags.objectInitializer);
+ visitList(node.properties);
+ sink.writeBool(node.isOneLiner);
+ sink.end(JsNodeTags.objectInitializer);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitArrayHole(js.ArrayHole node) {
+ sink.writeEnum(JsNodeKind.arrayHole);
+ sink.begin(JsNodeTags.arrayHole);
+ sink.end(JsNodeTags.arrayHole);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitArrayInitializer(js.ArrayInitializer node) {
+ sink.writeEnum(JsNodeKind.arrayInitializer);
+ sink.begin(JsNodeTags.arrayInitializer);
+ visitList(node.elements);
+ sink.end(JsNodeTags.arrayInitializer);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitParentheses(js.Parentheses node) {
+ sink.writeEnum(JsNodeKind.parentheses);
+ sink.begin(JsNodeTags.parentheses);
+ visit(node.enclosed);
+ sink.end(JsNodeTags.parentheses);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitName(js.Name node) {
+ if (node is ModularName) {
+ sink.writeEnum(JsNodeKind.modularName);
+ sink.begin(JsNodeTags.modularName);
+ node.writeToDataSink(sink);
+ sink.end(JsNodeTags.modularName);
+ _writeInfo(node);
+ } else if (node is AsyncName) {
+ sink.writeEnum(JsNodeKind.asyncName);
+ sink.begin(JsNodeTags.asyncName);
+ visit(node.prefix);
+ visit(node.base);
+ sink.end(JsNodeTags.asyncName);
+ _writeInfo(node);
+ } else if (node is StringBackedName) {
+ sink.writeEnum(JsNodeKind.stringBackedName);
+ sink.begin(JsNodeTags.stringBackedName);
+ sink.writeString(node.name);
+ sink.end(JsNodeTags.stringBackedName);
+ _writeInfo(node);
+ } else {
+ throw new UnsupportedError(
+ 'Unexpected deferred expression: ${node.runtimeType}.');
+ }
+ }
+
+ @override
+ void visitStringConcatenation(js.StringConcatenation node) {
+ sink.writeEnum(JsNodeKind.stringConcatenation);
+ sink.begin(JsNodeTags.stringConcatenation);
+ visitList(node.parts);
+ sink.end(JsNodeTags.stringConcatenation);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitLiteralNull(js.LiteralNull node) {
+ sink.writeEnum(JsNodeKind.literalNull);
+ sink.begin(JsNodeTags.literalNull);
+ sink.end(JsNodeTags.literalNull);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitLiteralNumber(js.LiteralNumber node) {
+ sink.writeEnum(JsNodeKind.literalNumber);
+ sink.begin(JsNodeTags.literalNumber);
+ sink.writeString(node.value);
+ sink.end(JsNodeTags.literalNumber);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitLiteralString(js.LiteralString node) {
+ if (node is js.LiteralStringFromName) {
+ sink.writeEnum(JsNodeKind.literalStringFromName);
+ sink.begin(JsNodeTags.literalStringFromName);
+ visit(node.name);
+ sink.end(JsNodeTags.literalStringFromName);
+ } else {
+ sink.writeEnum(JsNodeKind.literalString);
+ sink.begin(JsNodeTags.literalString);
+ sink.writeString(node.value);
+ sink.end(JsNodeTags.literalString);
+ }
+ _writeInfo(node);
+ }
+
+ @override
+ void visitLiteralBool(js.LiteralBool node) {
+ sink.writeEnum(JsNodeKind.literalBool);
+ sink.begin(JsNodeTags.literalBool);
+ sink.writeBool(node.value);
+ sink.end(JsNodeTags.literalBool);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitDeferredString(js.DeferredString node) {
+ throw new UnsupportedError('JsNodeSerializer.visitDeferredString');
+ }
+
+ @override
+ void visitDeferredNumber(js.DeferredNumber node) {
+ throw new UnsupportedError('JsNodeSerializer.visitDeferredNumber');
+ }
+
+ @override
+ void visitDeferredExpression(js.DeferredExpression node) {
+ if (node is ModularExpression) {
+ sink.writeEnum(JsNodeKind.modularExpression);
+ sink.begin(JsNodeTags.modularExpression);
+ node.writeToDataSink(sink);
+ sink.end(JsNodeTags.modularExpression);
+ _writeInfo(node);
+ } else {
+ throw new UnsupportedError(
+ 'Unexpected deferred expression: ${node.runtimeType}.');
+ }
+ }
+
+ @override
+ void visitFun(js.Fun node) {
+ sink.writeEnum(JsNodeKind.function);
+ sink.begin(JsNodeTags.function);
+ visitList(node.params);
+ visit(node.body);
+ sink.writeEnum(node.asyncModifier);
+ sink.end(JsNodeTags.function);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitNamedFunction(js.NamedFunction node) {
+ sink.writeEnum(JsNodeKind.namedFunction);
+ sink.begin(JsNodeTags.namedFunction);
+ visit(node.name);
+ visit(node.function);
+ sink.end(JsNodeTags.namedFunction);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitAccess(js.PropertyAccess node) {
+ sink.writeEnum(JsNodeKind.access);
+ sink.begin(JsNodeTags.access);
+ visit(node.receiver);
+ visit(node.selector);
+ sink.end(JsNodeTags.access);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitParameter(js.Parameter node) {
+ sink.writeEnum(JsNodeKind.parameter);
+ sink.begin(JsNodeTags.parameter);
+ sink.writeString(node.name);
+ sink.end(JsNodeTags.parameter);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitVariableDeclaration(js.VariableDeclaration node) {
+ sink.writeEnum(JsNodeKind.variableDeclaration);
+ sink.begin(JsNodeTags.variableDeclaration);
+ sink.writeString(node.name);
+ sink.writeBool(node.allowRename);
+ sink.end(JsNodeTags.variableDeclaration);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitThis(js.This node) {
+ sink.writeEnum(JsNodeKind.thisExpression);
+ sink.begin(JsNodeTags.thisExpression);
+ sink.end(JsNodeTags.thisExpression);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitVariableUse(js.VariableUse node) {
+ sink.writeEnum(JsNodeKind.variableUse);
+ sink.begin(JsNodeTags.variableUse);
+ sink.writeString(node.name);
+ sink.end(JsNodeTags.variableUse);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitPostfix(js.Postfix node) {
+ sink.writeEnum(JsNodeKind.postfix);
+ sink.begin(JsNodeTags.postfix);
+ sink.writeString(node.op);
+ visit(node.argument);
+ sink.end(JsNodeTags.postfix);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitPrefix(js.Prefix node) {
+ sink.writeEnum(JsNodeKind.prefix);
+ sink.begin(JsNodeTags.prefix);
+ sink.writeString(node.op);
+ visit(node.argument);
+ sink.end(JsNodeTags.prefix);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitBinary(js.Binary node) {
+ sink.writeEnum(JsNodeKind.binary);
+ sink.begin(JsNodeTags.binary);
+ sink.writeString(node.op);
+ visit(node.left);
+ visit(node.right);
+ sink.end(JsNodeTags.binary);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitCall(js.Call node) {
+ sink.writeEnum(JsNodeKind.callExpression);
+ sink.begin(JsNodeTags.callExpression);
+ visit(node.target);
+ visitList(node.arguments);
+ sink.end(JsNodeTags.callExpression);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitNew(js.New node) {
+ sink.writeEnum(JsNodeKind.newExpression);
+ sink.begin(JsNodeTags.newExpression);
+ visit(node.target);
+ visitList(node.arguments);
+ sink.end(JsNodeTags.newExpression);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitConditional(js.Conditional node) {
+ sink.writeEnum(JsNodeKind.conditional);
+ sink.begin(JsNodeTags.conditional);
+ visit(node.condition);
+ visit(node.then);
+ visit(node.otherwise);
+ sink.end(JsNodeTags.conditional);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitVariableInitialization(js.VariableInitialization node) {
+ sink.writeEnum(JsNodeKind.variableInitialization);
+ sink.begin(JsNodeTags.variableInitialization);
+ visit(node.declaration);
+ visit(node.value, allowNull: true);
+ sink.end(JsNodeTags.variableInitialization);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitAssignment(js.Assignment node) {
+ sink.writeEnum(JsNodeKind.assignment);
+ sink.begin(JsNodeTags.assignment);
+ visit(node.leftHandSide);
+ sink.writeStringOrNull(node.op);
+ visit(node.value);
+ sink.end(JsNodeTags.assignment);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitVariableDeclarationList(js.VariableDeclarationList node) {
+ sink.writeEnum(JsNodeKind.variableDeclarationList);
+ sink.begin(JsNodeTags.variableDeclarationList);
+ visitList(node.declarations);
+ sink.writeBool(node.indentSplits);
+ sink.end(JsNodeTags.variableDeclarationList);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitLiteralExpression(js.LiteralExpression node) {
+ sink.writeEnum(JsNodeKind.literalExpression);
+ sink.begin(JsNodeTags.literalExpression);
+ sink.writeString(node.template);
+ visitList(node.inputs);
+ sink.end(JsNodeTags.literalExpression);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitDartYield(js.DartYield node) {
+ sink.writeEnum(JsNodeKind.dartYield);
+ sink.begin(JsNodeTags.dartYield);
+ visit(node.expression);
+ sink.writeBool(node.hasStar);
+ sink.end(JsNodeTags.dartYield);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitLiteralStatement(js.LiteralStatement node) {
+ sink.writeEnum(JsNodeKind.literalStatement);
+ sink.begin(JsNodeTags.literalStatement);
+ sink.writeString(node.code);
+ sink.end(JsNodeTags.literalStatement);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitLabeledStatement(js.LabeledStatement node) {
+ sink.writeEnum(JsNodeKind.labeledStatement);
+ sink.begin(JsNodeTags.labeledStatement);
+ sink.writeString(node.label);
+ visit(node.body);
+ sink.end(JsNodeTags.labeledStatement);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitFunctionDeclaration(js.FunctionDeclaration node) {
+ sink.writeEnum(JsNodeKind.functionDeclaration);
+ sink.begin(JsNodeTags.functionDeclaration);
+ visit(node.name);
+ visit(node.function);
+ sink.end(JsNodeTags.functionDeclaration);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitDefault(js.Default node) {
+ sink.writeEnum(JsNodeKind.switchDefault);
+ sink.begin(JsNodeTags.switchDefault);
+ visit(node.body);
+ sink.end(JsNodeTags.switchDefault);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitCase(js.Case node) {
+ sink.writeEnum(JsNodeKind.switchCase);
+ sink.begin(JsNodeTags.switchCase);
+ visit(node.expression);
+ visit(node.body);
+ sink.end(JsNodeTags.switchCase);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitSwitch(js.Switch node) {
+ sink.writeEnum(JsNodeKind.switchStatement);
+ sink.begin(JsNodeTags.switchStatement);
+ visit(node.key);
+ visitList(node.cases);
+ sink.end(JsNodeTags.switchStatement);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitCatch(js.Catch node) {
+ sink.writeEnum(JsNodeKind.catchClause);
+ sink.begin(JsNodeTags.catchClause);
+ visit(node.declaration);
+ visit(node.body);
+ sink.end(JsNodeTags.catchClause);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitTry(js.Try node) {
+ sink.writeEnum(JsNodeKind.tryStatement);
+ sink.begin(JsNodeTags.tryStatement);
+ visit(node.body);
+ visit(node.catchPart, allowNull: true);
+ visit(node.finallyPart, allowNull: true);
+ sink.end(JsNodeTags.tryStatement);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitThrow(js.Throw node) {
+ sink.writeEnum(JsNodeKind.throwStatement);
+ sink.begin(JsNodeTags.throwStatement);
+ visit(node.expression);
+ sink.end(JsNodeTags.throwStatement);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitReturn(js.Return node) {
+ sink.writeEnum(JsNodeKind.returnStatement);
+ sink.begin(JsNodeTags.returnStatement);
+ visit(node.value, allowNull: true);
+ sink.end(JsNodeTags.returnStatement);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitBreak(js.Break node) {
+ sink.writeEnum(JsNodeKind.breakStatement);
+ sink.begin(JsNodeTags.breakStatement);
+ sink.writeStringOrNull(node.targetLabel);
+ sink.end(JsNodeTags.breakStatement);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitContinue(js.Continue node) {
+ sink.writeEnum(JsNodeKind.continueStatement);
+ sink.begin(JsNodeTags.continueStatement);
+ sink.writeStringOrNull(node.targetLabel);
+ sink.end(JsNodeTags.continueStatement);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitDo(js.Do node) {
+ sink.writeEnum(JsNodeKind.doStatement);
+ sink.begin(JsNodeTags.doStatement);
+ visit(node.body);
+ visit(node.condition);
+ sink.end(JsNodeTags.doStatement);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitWhile(js.While node) {
+ sink.writeEnum(JsNodeKind.whileStatement);
+ sink.begin(JsNodeTags.whileStatement);
+ visit(node.condition);
+ visit(node.body);
+ sink.end(JsNodeTags.whileStatement);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitForIn(js.ForIn node) {
+ sink.writeEnum(JsNodeKind.forInStatement);
+ sink.begin(JsNodeTags.forInStatement);
+ visit(node.leftHandSide);
+ visit(node.object);
+ visit(node.body);
+ sink.end(JsNodeTags.forInStatement);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitFor(js.For node) {
+ sink.writeEnum(JsNodeKind.forStatement);
+ sink.begin(JsNodeTags.forStatement);
+ visit(node.init, allowNull: true);
+ visit(node.condition, allowNull: true);
+ visit(node.update, allowNull: true);
+ visit(node.body);
+ sink.end(JsNodeTags.forStatement);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitIf(js.If node) {
+ sink.writeEnum(JsNodeKind.ifStatement);
+ sink.begin(JsNodeTags.ifStatement);
+ visit(node.condition);
+ visit(node.then);
+ visit(node.otherwise);
+ sink.end(JsNodeTags.ifStatement);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitEmptyStatement(js.EmptyStatement node) {
+ sink.writeEnum(JsNodeKind.emptyStatement);
+ sink.begin(JsNodeTags.emptyStatement);
+ sink.end(JsNodeTags.emptyStatement);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitExpressionStatement(js.ExpressionStatement node) {
+ sink.writeEnum(JsNodeKind.expressionStatement);
+ sink.begin(JsNodeTags.expressionStatement);
+ visit(node.expression);
+ sink.end(JsNodeTags.expressionStatement);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitBlock(js.Block node) {
+ sink.writeEnum(JsNodeKind.block);
+ sink.begin(JsNodeTags.block);
+ visitList(node.statements);
+ sink.end(JsNodeTags.block);
+ _writeInfo(node);
+ }
+
+ @override
+ void visitProgram(js.Program node) {
+ sink.writeEnum(JsNodeKind.program);
+ sink.begin(JsNodeTags.program);
+ visitList(node.body);
+ sink.end(JsNodeTags.program);
+ _writeInfo(node);
+ }
+}
+
+/// Helper class that deserializes a [js.Node] from [DataSource].
+///
+/// Deserialized [ModularName]s and [ModularExpression]s are collected in the
+/// [modularNames] and [modularExpressions] lists.
+class JsNodeDeserializer {
+ final DataSource source;
+ final List<ModularName> modularNames;
+ final List<ModularExpression> modularExpressions;
+
+ JsNodeDeserializer._(this.source, this.modularNames, this.modularExpressions);
+
+ static js.Node readFromDataSource(
+ DataSource source,
+ List<ModularName> modularNames,
+ List<ModularExpression> modularExpressions) {
+ source.begin(JsNodeTags.tag);
+ JsNodeDeserializer deserializer =
+ new JsNodeDeserializer._(source, modularNames, modularExpressions);
+ js.Node node = deserializer.read();
+ source.end(JsNodeTags.tag);
+ return node;
+ }
+
+ T read<T extends js.Node>({bool allowNull: false}) {
+ if (allowNull) {
+ bool hasValue = source.readBool();
+ if (!hasValue) return null;
+ }
+ JsNodeKind kind = source.readEnum(JsNodeKind.values);
+ js.Node node;
+ switch (kind) {
+ case JsNodeKind.comment:
+ source.begin(JsNodeTags.comment);
+ node = new js.Comment(source.readString());
+ source.end(JsNodeTags.comment);
+ break;
+ case JsNodeKind.await:
+ source.begin(JsNodeTags.await);
+ node = new js.Await(read());
+ source.end(JsNodeTags.await);
+ break;
+ case JsNodeKind.regExpLiteral:
+ source.begin(JsNodeTags.regExpLiteral);
+ node = new js.RegExpLiteral(source.readString());
+ source.end(JsNodeTags.regExpLiteral);
+ break;
+ case JsNodeKind.property:
+ source.begin(JsNodeTags.property);
+ js.Expression name = read();
+ js.Expression value = read();
+ node = new js.Property(name, value);
+ source.end(JsNodeTags.property);
+ break;
+ case JsNodeKind.objectInitializer:
+ source.begin(JsNodeTags.objectInitializer);
+ List<js.Property> properties = readList();
+ bool isOneLiner = source.readBool();
+ node = new js.ObjectInitializer(properties, isOneLiner: isOneLiner);
+ source.end(JsNodeTags.objectInitializer);
+ break;
+ case JsNodeKind.arrayHole:
+ source.begin(JsNodeTags.arrayHole);
+ node = new js.ArrayHole();
+ source.end(JsNodeTags.arrayHole);
+ break;
+ case JsNodeKind.arrayInitializer:
+ source.begin(JsNodeTags.arrayInitializer);
+ List<js.Expression> elements = readList();
+ node = new js.ArrayInitializer(elements);
+ source.end(JsNodeTags.arrayInitializer);
+ break;
+ case JsNodeKind.parentheses:
+ source.begin(JsNodeTags.parentheses);
+ node = new js.Parentheses(read());
+ source.end(JsNodeTags.parentheses);
+ break;
+ case JsNodeKind.modularName:
+ source.begin(JsNodeTags.modularName);
+ ModularName modularName = ModularName.readFromDataSource(source);
+ modularNames.add(modularName);
+ node = modularName;
+ source.end(JsNodeTags.modularName);
+ break;
+ case JsNodeKind.asyncName:
+ source.begin(JsNodeTags.asyncName);
+ js.Name prefix = read();
+ js.Name base = read();
+ node = new AsyncName(prefix, base);
+ source.end(JsNodeTags.asyncName);
+ break;
+ case JsNodeKind.stringBackedName:
+ source.begin(JsNodeTags.stringBackedName);
+ node = new StringBackedName(source.readString());
+ source.end(JsNodeTags.stringBackedName);
+ break;
+ case JsNodeKind.stringConcatenation:
+ source.begin(JsNodeTags.stringConcatenation);
+ List<js.Literal> parts = readList();
+ node = new js.StringConcatenation(parts);
+ source.end(JsNodeTags.stringConcatenation);
+ break;
+ case JsNodeKind.literalNull:
+ source.begin(JsNodeTags.literalNull);
+ node = new js.LiteralNull();
+ source.end(JsNodeTags.literalNull);
+ break;
+ case JsNodeKind.literalNumber:
+ source.begin(JsNodeTags.literalNumber);
+ node = new js.LiteralNumber(source.readString());
+ source.end(JsNodeTags.literalNumber);
+ break;
+ case JsNodeKind.literalString:
+ source.begin(JsNodeTags.literalString);
+ node = new js.LiteralString(source.readString());
+ source.end(JsNodeTags.literalString);
+ break;
+ case JsNodeKind.literalStringFromName:
+ source.begin(JsNodeTags.literalStringFromName);
+ js.Name name = read();
+ node = new js.LiteralStringFromName(name);
+ source.end(JsNodeTags.literalStringFromName);
+ break;
+ case JsNodeKind.literalBool:
+ source.begin(JsNodeTags.literalBool);
+ node = new js.LiteralBool(source.readBool());
+ source.end(JsNodeTags.literalBool);
+ break;
+ case JsNodeKind.modularExpression:
+ source.begin(JsNodeTags.modularExpression);
+ ModularExpression modularExpression =
+ ModularExpression.readFromDataSource(source);
+ modularExpressions.add(modularExpression);
+ node = modularExpression;
+ source.end(JsNodeTags.modularExpression);
+ break;
+ case JsNodeKind.function:
+ source.begin(JsNodeTags.function);
+ List<js.Parameter> params = readList();
+ js.Block body = read();
+ js.AsyncModifier asyncModifier =
+ source.readEnum(js.AsyncModifier.values);
+ node = new js.Fun(params, body, asyncModifier: asyncModifier);
+ source.end(JsNodeTags.function);
+ break;
+ case JsNodeKind.namedFunction:
+ source.begin(JsNodeTags.namedFunction);
+ js.Declaration name = read();
+ js.Fun function = read();
+ node = new js.NamedFunction(name, function);
+ source.end(JsNodeTags.namedFunction);
+ break;
+ case JsNodeKind.access:
+ source.begin(JsNodeTags.access);
+ js.Expression receiver = read();
+ js.Expression selector = read();
+ node = new js.PropertyAccess(receiver, selector);
+ source.end(JsNodeTags.access);
+ break;
+ case JsNodeKind.parameter:
+ source.begin(JsNodeTags.parameter);
+ node = new js.Parameter(source.readString());
+ source.end(JsNodeTags.parameter);
+ break;
+ case JsNodeKind.variableDeclaration:
+ source.begin(JsNodeTags.variableDeclaration);
+ String name = source.readString();
+ bool allowRename = source.readBool();
+ node = new js.VariableDeclaration(name, allowRename: allowRename);
+ source.end(JsNodeTags.variableDeclaration);
+ break;
+ case JsNodeKind.thisExpression:
+ source.begin(JsNodeTags.thisExpression);
+ node = new js.This();
+ source.end(JsNodeTags.thisExpression);
+ break;
+ case JsNodeKind.variableUse:
+ source.begin(JsNodeTags.variableUse);
+ node = new js.VariableUse(source.readString());
+ source.end(JsNodeTags.variableUse);
+ break;
+ case JsNodeKind.postfix:
+ source.begin(JsNodeTags.postfix);
+ String op = source.readString();
+ js.Expression argument = read();
+ node = new js.Postfix(op, argument);
+ source.end(JsNodeTags.postfix);
+ break;
+ case JsNodeKind.prefix:
+ source.begin(JsNodeTags.prefix);
+ String op = source.readString();
+ js.Expression argument = read();
+ node = new js.Prefix(op, argument);
+ source.end(JsNodeTags.prefix);
+ break;
+ case JsNodeKind.binary:
+ source.begin(JsNodeTags.binary);
+ String op = source.readString();
+ js.Expression left = read();
+ js.Expression right = read();
+ node = new js.Binary(op, left, right);
+ source.end(JsNodeTags.binary);
+ break;
+ case JsNodeKind.callExpression:
+ source.begin(JsNodeTags.callExpression);
+ js.Expression target = read();
+ List<js.Expression> arguments = readList();
+ node = new js.Call(target, arguments);
+ source.end(JsNodeTags.callExpression);
+ break;
+ case JsNodeKind.newExpression:
+ source.begin(JsNodeTags.newExpression);
+ js.Expression cls = read();
+ List<js.Expression> arguments = readList();
+ node = new js.New(cls, arguments);
+ source.end(JsNodeTags.newExpression);
+ break;
+ case JsNodeKind.conditional:
+ source.begin(JsNodeTags.conditional);
+ js.Expression condition = read();
+ js.Expression then = read();
+ js.Expression otherwise = read();
+ node = new js.Conditional(condition, then, otherwise);
+ source.end(JsNodeTags.conditional);
+ break;
+ case JsNodeKind.variableInitialization:
+ source.begin(JsNodeTags.variableInitialization);
+ js.Declaration declaration = read();
+ js.Expression value = source.readValueOrNull(read);
+ node = new js.VariableInitialization(declaration, value);
+ source.end(JsNodeTags.variableInitialization);
+ break;
+ case JsNodeKind.assignment:
+ source.begin(JsNodeTags.assignment);
+ js.Expression leftHandSide = read();
+ String op = source.readStringOrNull();
+ js.Expression value = read();
+ node = new js.Assignment.compound(leftHandSide, op, value);
+ source.end(JsNodeTags.assignment);
+ break;
+ case JsNodeKind.variableDeclarationList:
+ source.begin(JsNodeTags.variableDeclarationList);
+ List<js.VariableInitialization> declarations = readList();
+ bool indentSplits = source.readBool();
+ node = new js.VariableDeclarationList(declarations,
+ indentSplits: indentSplits);
+ source.end(JsNodeTags.variableDeclarationList);
+ break;
+ case JsNodeKind.literalExpression:
+ source.begin(JsNodeTags.literalExpression);
+ String template = source.readString();
+ List<js.Expression> inputs = readList();
+ node = new js.LiteralExpression.withData(template, inputs);
+ source.end(JsNodeTags.literalExpression);
+ break;
+ case JsNodeKind.dartYield:
+ source.begin(JsNodeTags.dartYield);
+ js.Expression expression = read();
+ bool hasStar = source.readBool();
+ node = new js.DartYield(expression, hasStar);
+ source.end(JsNodeTags.dartYield);
+ break;
+ case JsNodeKind.literalStatement:
+ source.begin(JsNodeTags.literalStatement);
+ node = new js.LiteralStatement(source.readString());
+ source.end(JsNodeTags.literalStatement);
+ break;
+ case JsNodeKind.labeledStatement:
+ source.begin(JsNodeTags.labeledStatement);
+ String label = source.readString();
+ js.Statement body = read();
+ node = new js.LabeledStatement(label, body);
+ source.end(JsNodeTags.labeledStatement);
+ break;
+ case JsNodeKind.functionDeclaration:
+ source.begin(JsNodeTags.functionDeclaration);
+ js.Declaration name = read();
+ js.Fun function = read();
+ node = new js.FunctionDeclaration(name, function);
+ source.end(JsNodeTags.functionDeclaration);
+ break;
+ case JsNodeKind.switchDefault:
+ source.begin(JsNodeTags.switchDefault);
+ js.Block body = read();
+ node = new js.Default(body);
+ source.end(JsNodeTags.switchDefault);
+ break;
+ case JsNodeKind.switchCase:
+ source.begin(JsNodeTags.switchCase);
+ js.Expression expression = read();
+ js.Block body = read();
+ node = new js.Case(expression, body);
+ source.end(JsNodeTags.switchCase);
+ break;
+ case JsNodeKind.switchStatement:
+ source.begin(JsNodeTags.switchStatement);
+ js.Expression key = read();
+ List<js.SwitchClause> cases = readList();
+ node = new js.Switch(key, cases);
+ source.end(JsNodeTags.switchStatement);
+ break;
+ case JsNodeKind.catchClause:
+ source.begin(JsNodeTags.catchClause);
+ js.Declaration declaration = read();
+ js.Block body = read();
+ node = new js.Catch(declaration, body);
+ source.end(JsNodeTags.catchClause);
+ break;
+ case JsNodeKind.tryStatement:
+ source.begin(JsNodeTags.tryStatement);
+ js.Block body = read();
+ js.Catch catchPart = source.readValueOrNull(read);
+ js.Block finallyPart = source.readValueOrNull(read);
+ node = new js.Try(body, catchPart, finallyPart);
+ source.end(JsNodeTags.tryStatement);
+ break;
+ case JsNodeKind.throwStatement:
+ source.begin(JsNodeTags.throwStatement);
+ js.Expression expression = read();
+ node = new js.Throw(expression);
+ source.end(JsNodeTags.throwStatement);
+ break;
+ case JsNodeKind.returnStatement:
+ source.begin(JsNodeTags.returnStatement);
+ js.Expression value = source.readValueOrNull(read);
+ node = new js.Return(value);
+ source.end(JsNodeTags.returnStatement);
+ break;
+ case JsNodeKind.breakStatement:
+ source.begin(JsNodeTags.breakStatement);
+ String targetLabel = source.readStringOrNull();
+ node = new js.Break(targetLabel);
+ source.end(JsNodeTags.breakStatement);
+ break;
+ case JsNodeKind.continueStatement:
+ source.begin(JsNodeTags.continueStatement);
+ String targetLabel = source.readStringOrNull();
+ node = new js.Continue(targetLabel);
+ source.end(JsNodeTags.continueStatement);
+ break;
+ case JsNodeKind.doStatement:
+ source.begin(JsNodeTags.doStatement);
+ js.Statement body = read();
+ js.Expression condition = read();
+ node = new js.Do(body, condition);
+ source.end(JsNodeTags.doStatement);
+ break;
+ case JsNodeKind.whileStatement:
+ source.begin(JsNodeTags.whileStatement);
+ js.Expression condition = read();
+ js.Statement body = read();
+ node = new js.While(condition, body);
+ source.end(JsNodeTags.whileStatement);
+ break;
+ case JsNodeKind.forInStatement:
+ source.begin(JsNodeTags.forInStatement);
+ js.Expression leftHandSide = read();
+ js.Expression object = read();
+ js.Statement body = read();
+ node = new js.ForIn(leftHandSide, object, body);
+ source.end(JsNodeTags.forInStatement);
+ break;
+ case JsNodeKind.forStatement:
+ source.begin(JsNodeTags.forStatement);
+ js.Expression init = read(allowNull: true);
+ js.Expression condition = read(allowNull: true);
+ js.Expression update = read(allowNull: true);
+ js.Statement body = read();
+ node = new js.For(init, condition, update, body);
+ source.end(JsNodeTags.forStatement);
+ break;
+ case JsNodeKind.ifStatement:
+ source.begin(JsNodeTags.ifStatement);
+ js.Expression condition = read();
+ js.Statement then = read();
+ js.Statement otherwise = read();
+ node = new js.If(condition, then, otherwise);
+ source.end(JsNodeTags.ifStatement);
+ break;
+ case JsNodeKind.emptyStatement:
+ source.begin(JsNodeTags.emptyStatement);
+ node = new js.EmptyStatement();
+ source.end(JsNodeTags.emptyStatement);
+ break;
+ case JsNodeKind.expressionStatement:
+ source.begin(JsNodeTags.expressionStatement);
+ node = new js.ExpressionStatement(read());
+ source.end(JsNodeTags.expressionStatement);
+ break;
+ case JsNodeKind.block:
+ source.begin(JsNodeTags.block);
+ List<js.Statement> statements = readList();
+ node = new js.Block(statements);
+ source.end(JsNodeTags.block);
+ break;
+ case JsNodeKind.program:
+ source.begin(JsNodeTags.program);
+ List<js.Statement> body = readList();
+ node = new js.Program(body);
+ source.end(JsNodeTags.program);
+ break;
+ }
+ SourceInformation sourceInformation =
+ SourceInformation.readFromDataSource(source);
+ if (sourceInformation != null) {
+ node = node.withSourceInformation(sourceInformation);
+ }
+ return node;
+ }
+
+ List<T> readList<T extends js.Node>({bool emptyAsNull: false}) {
+ return source.readList(read, emptyAsNull: emptyAsNull);
+ }
+}
+
+class CodegenReaderImpl implements CodegenReader {
+ final JClosedWorld closedWorld;
+ final List<ModularName> modularNames;
+ final List<ModularExpression> modularExpressions;
+
+ CodegenReaderImpl(
+ this.closedWorld, this.modularNames, this.modularExpressions);
+
+ @override
+ AbstractValue readAbstractValue(DataSource source) {
+ return closedWorld.abstractValueDomain
+ .readAbstractValueFromDataSource(source);
+ }
+
+ @override
+ js.Node readJsNode(DataSource source) {
+ return JsNodeDeserializer.readFromDataSource(
+ source, modularNames, modularExpressions);
+ }
+
+ @override
+ OutputUnit readOutputUnitReference(DataSource source) {
+ return closedWorld.outputUnitData.outputUnits[source.readInt()];
+ }
+}
+
+class CodegenWriterImpl implements CodegenWriter {
+ final JClosedWorld closedWorld;
+
+ CodegenWriterImpl(this.closedWorld);
+
+ @override
+ void writeAbstractValue(DataSink sink, AbstractValue value) {
+ closedWorld.abstractValueDomain.writeAbstractValueToDataSink(sink, value);
+ }
+
+ @override
+ void writeJsNode(DataSink sink, js.Node node) {
+ JsNodeSerializer.writeToDataSink(sink, node);
+ }
+
+ @override
+ void writeOutputUnitReference(DataSink sink, OutputUnit value) {
+ sink.writeInt(closedWorld.outputUnitData.outputUnits.indexOf(value));
+ }
+}
diff --git a/pkg/compiler/lib/src/constants/values.dart b/pkg/compiler/lib/src/constants/values.dart
index ffb7acf..c10956f 100644
--- a/pkg/compiler/lib/src/constants/values.dart
+++ b/pkg/compiler/lib/src/constants/values.dart
@@ -9,6 +9,8 @@
import '../elements/entities.dart';
import '../elements/types.dart';
import '../deferred_load.dart' show OutputUnit;
+import '../inferrer/abstract_value_domain.dart';
+import '../js/js.dart' as js;
import '../util/util.dart';
enum ConstantValueKind {
@@ -24,7 +26,8 @@
CONSTRUCTED,
TYPE,
INTERCEPTOR,
- SYNTHETIC,
+ JS_NAME,
+ ABSTRACT_VALUE,
INSTANTIATION,
DEFERRED_GLOBAL,
NON_CONSTANT,
@@ -47,7 +50,9 @@
R visitType(covariant TypeConstantValue constant, covariant A arg);
R visitInterceptor(
covariant InterceptorConstantValue constant, covariant A arg);
- R visitSynthetic(covariant SyntheticConstantValue constant, covariant A arg);
+ R visitAbstractValue(
+ covariant AbstractValueConstantValue constant, covariant A arg);
+ R visitJsName(covariant JsNameConstantValue constant, covariant A arg);
R visitDeferredGlobal(
covariant DeferredGlobalConstantValue constant, covariant A arg);
R visitNonConstant(covariant NonConstantValue constant, covariant A arg);
@@ -808,42 +813,81 @@
}
}
-class SyntheticConstantValue extends ConstantValue {
- final payload;
- final valueKind;
+class JsNameConstantValue extends ConstantValue {
+ final js.LiteralString name;
- SyntheticConstantValue(this.valueKind, this.payload);
+ JsNameConstantValue(this.name);
@override
bool get isDummy => true;
@override
bool operator ==(other) {
- return other is SyntheticConstantValue && payload == other.payload;
+ return other is JsNameConstantValue && name == other.name;
}
@override
- get hashCode => payload.hashCode * 17 + valueKind.hashCode;
+ get hashCode => name.hashCode * 17;
@override
List<ConstantValue> getDependencies() => const <ConstantValue>[];
@override
accept(ConstantValueVisitor visitor, arg) {
- return visitor.visitSynthetic(this, arg);
+ return visitor.visitJsName(this, arg);
}
@override
DartType getType(CommonElements types) => types.dynamicType;
@override
- ConstantValueKind get kind => ConstantValueKind.SYNTHETIC;
+ ConstantValueKind get kind => ConstantValueKind.JS_NAME;
@override
- String toDartText() => 'synthetic($valueKind, $payload)';
+ String toDartText() => 'js_name(${name})';
@override
- String toStructuredText() => 'SyntheticConstant($valueKind, $payload)';
+ String toStructuredText() => 'JsNameConstant(${name})';
+}
+
+/// An abstract value as a constant value. This is only used during code
+/// generation.
+class AbstractValueConstantValue extends ConstantValue {
+ final AbstractValue abstractValue;
+
+ AbstractValueConstantValue(this.abstractValue);
+
+ @override
+ bool get isDummy => true;
+
+ @override
+ bool operator ==(other) {
+ return other is AbstractValueConstantValue &&
+ abstractValue == other.abstractValue;
+ }
+
+ @override
+ get hashCode => abstractValue.hashCode * 17;
+
+ @override
+ List<ConstantValue> getDependencies() => const <ConstantValue>[];
+
+ @override
+ accept(ConstantValueVisitor visitor, arg) {
+ return visitor.visitAbstractValue(this, arg);
+ }
+
+ @override
+ DartType getType(CommonElements types) => types.dynamicType;
+
+ @override
+ ConstantValueKind get kind => ConstantValueKind.ABSTRACT_VALUE;
+
+ @override
+ String toDartText() => 'abstract_value($abstractValue)';
+
+ @override
+ String toStructuredText() => 'AbstractValueConstant($abstractValue)';
}
class ConstructedConstantValue extends ObjectConstantValue {
diff --git a/pkg/compiler/lib/src/deferred_load.dart b/pkg/compiler/lib/src/deferred_load.dart
index dd23b37..5c32df6 100644
--- a/pkg/compiler/lib/src/deferred_load.dart
+++ b/pkg/compiler/lib/src/deferred_load.dart
@@ -1235,7 +1235,7 @@
final Map<MemberEntity, OutputUnit> _memberToUnit;
final Map<Local, OutputUnit> _localFunctionToUnit;
final Map<ConstantValue, OutputUnit> _constantToUnit;
- final Iterable<OutputUnit> outputUnits;
+ final List<OutputUnit> outputUnits;
final Map<ImportEntity, String> _importDeferName;
/// A mapping from the name of a defer import to all the output units it
diff --git a/pkg/compiler/lib/src/inferrer/typemasks/constants.dart b/pkg/compiler/lib/src/inferrer/typemasks/constants.dart
index 9b5232f..a453382 100644
--- a/pkg/compiler/lib/src/inferrer/typemasks/constants.dart
+++ b/pkg/compiler/lib/src/inferrer/typemasks/constants.dart
@@ -4,10 +4,8 @@
library types.constants;
-import '../../common.dart';
import '../../constants/constant_system.dart' as constant_system;
import '../../constants/values.dart';
-import '../../js_backend/js_backend.dart' show SyntheticConstantKind;
import '../../world.dart' show JClosedWorld;
import 'masks.dart';
@@ -50,21 +48,14 @@
}
@override
- TypeMask visitSynthetic(
- SyntheticConstantValue constant, JClosedWorld closedWorld) {
- switch (constant.valueKind) {
- case SyntheticConstantKind.DUMMY_INTERCEPTOR:
- return constant.payload;
- case SyntheticConstantKind.EMPTY_VALUE:
- return constant.payload;
- case SyntheticConstantKind.TYPEVARIABLE_REFERENCE:
- return closedWorld.abstractValueDomain.intType;
- case SyntheticConstantKind.NAME:
- return closedWorld.abstractValueDomain.stringType;
- default:
- throw failedAt(CURRENT_ELEMENT_SPANNABLE,
- "Unexpected DummyConstantKind: ${constant.toStructuredText()}.");
- }
+ TypeMask visitAbstractValue(
+ AbstractValueConstantValue constant, JClosedWorld closedWorld) {
+ return constant.abstractValue;
+ }
+
+ @override
+ TypeMask visitJsName(JsNameConstantValue constant, JClosedWorld closedWorld) {
+ return closedWorld.abstractValueDomain.stringType;
}
@override
diff --git a/pkg/compiler/lib/src/io/position_information.dart b/pkg/compiler/lib/src/io/position_information.dart
index 63851e9..a92ab7e 100644
--- a/pkg/compiler/lib/src/io/position_information.dart
+++ b/pkg/compiler/lib/src/io/position_information.dart
@@ -11,12 +11,15 @@
import '../js/js.dart' as js;
import '../js/js_debug.dart';
import '../js/js_source_mapping.dart';
+import '../serialization/serialization.dart';
import 'code_output.dart' show BufferedCodeOutput;
import 'source_information.dart';
/// [SourceInformation] that consists of an offset position into the source
/// code.
class PositionSourceInformation extends SourceInformation {
+ static const String tag = 'source-information';
+
@override
final SourceLocation startPosition;
@@ -29,6 +32,28 @@
PositionSourceInformation(
this.startPosition, this.innerPosition, this.inliningContext);
+ factory PositionSourceInformation.readFromDataSource(DataSource source) {
+ source.begin(tag);
+ SourceLocation startPosition = SourceLocation.readFromDataSource(source);
+ SourceLocation innerPosition = SourceLocation.readFromDataSource(source);
+ List<FrameContext> inliningContext = source.readList(
+ () => FrameContext.readFromDataSource(source),
+ emptyAsNull: true);
+ source.end(tag);
+ return new PositionSourceInformation(
+ startPosition, innerPosition, inliningContext);
+ }
+
+ void writeToDataSinkInternal(DataSink sink) {
+ sink.begin(tag);
+ SourceLocation.writeToDataSink(sink, startPosition);
+ SourceLocation.writeToDataSink(sink, innerPosition);
+ sink.writeList(inliningContext,
+ (FrameContext context) => context.writeToDataSink(sink),
+ allowNull: true);
+ sink.end(tag);
+ }
+
@override
List<SourceLocation> get sourceLocations {
List<SourceLocation> list = <SourceLocation>[];
diff --git a/pkg/compiler/lib/src/io/source_information.dart b/pkg/compiler/lib/src/io/source_information.dart
index 64ca507..bc8f6e7 100644
--- a/pkg/compiler/lib/src/io/source_information.dart
+++ b/pkg/compiler/lib/src/io/source_information.dart
@@ -8,14 +8,41 @@
import '../common.dart';
import '../elements/entities.dart';
import '../js/js.dart' show JavaScriptNodeSourceInformation;
+import '../serialization/serialization.dart';
import '../universe/call_structure.dart';
import 'source_file.dart';
+import 'position_information.dart';
/// Interface for passing source information, for instance for use in source
/// maps, through the backend.
abstract class SourceInformation extends JavaScriptNodeSourceInformation {
const SourceInformation();
+ static SourceInformation readFromDataSource(DataSource source) {
+ int hasSourceInformation = source.readInt();
+ if (hasSourceInformation == 0) {
+ return null;
+ } else if (hasSourceInformation == 1) {
+ return const SourceMappedMarker();
+ } else {
+ assert(hasSourceInformation == 2);
+ return PositionSourceInformation.readFromDataSource(source);
+ }
+ }
+
+ static void writeToDataSink(
+ DataSink sink, SourceInformation sourceInformation) {
+ if (sourceInformation == null) {
+ sink.writeInt(0);
+ } else if (sourceInformation is SourceMappedMarker) {
+ sink.writeInt(1);
+ } else {
+ sink.writeInt(2);
+ PositionSourceInformation positionSourceInformation = sourceInformation;
+ positionSourceInformation.writeToDataSinkInternal(sink);
+ }
+ }
+
SourceSpan get sourceSpan;
/// The source location associated with the start of the JS node.
@@ -45,6 +72,8 @@
/// precise data about inlining that can then be used by defobuscation tools
/// when reconstructing a source stack from a production stack trace.
class FrameContext {
+ static const String tag = 'frame-context';
+
/// Location of the call that was inlined.
final SourceInformation callInformation;
@@ -53,6 +82,22 @@
FrameContext(this.callInformation, this.inlinedMethodName);
+ factory FrameContext.readFromDataSource(DataSource source) {
+ source.begin(tag);
+ SourceInformation callInformation =
+ SourceInformation.readFromDataSource(source);
+ String inlinedMethodName = source.readString();
+ source.end(tag);
+ return new FrameContext(callInformation, inlinedMethodName);
+ }
+
+ void writeToDataSink(DataSink sink) {
+ sink.begin(tag);
+ SourceInformation.writeToDataSink(sink, callInformation);
+ sink.writeString(inlinedMethodName);
+ sink.end(tag);
+ }
+
@override
String toString() => "(FrameContext: $callInformation, $inlinedMethodName)";
}
@@ -206,6 +251,8 @@
/// A location in a source file.
abstract class SourceLocation {
+ static const String tag = 'source-location';
+
const SourceLocation();
/// The absolute URI of the source file of this source location.
@@ -223,8 +270,42 @@
/// The name associated with this source location, if any.
String get sourceName;
- /// `true` if the offset within the length of the source file.
- bool get isValid;
+ static SourceLocation readFromDataSource(DataSource source) {
+ int hasSourceLocation = source.readInt();
+ if (hasSourceLocation == 0) {
+ return null;
+ } else if (hasSourceLocation == 1) {
+ return const NoSourceLocationMarker();
+ } else {
+ assert(hasSourceLocation == 2);
+ source.begin(tag);
+ Uri sourceUri = source.readUri();
+ int offset = source.readInt();
+ int line = source.readInt();
+ int column = source.readInt();
+ String sourceName = source.readString();
+ source.end(tag);
+ return new DirectSourceLocation(
+ sourceUri, offset, line, column, sourceName);
+ }
+ }
+
+ static void writeToDataSink(DataSink sink, SourceLocation sourceLocation) {
+ if (sourceLocation == null) {
+ sink.writeInt(0);
+ } else if (sourceLocation is NoSourceLocationMarker) {
+ sink.writeInt(1);
+ } else {
+ sink.writeInt(2);
+ sink.begin(tag);
+ sink.writeUri(sourceLocation.sourceUri);
+ sink.writeInt(sourceLocation.offset);
+ sink.writeInt(sourceLocation.line);
+ sink.writeInt(sourceLocation.column);
+ sink.writeString(sourceLocation.sourceName);
+ sink.end(tag);
+ }
+ }
@override
int get hashCode {
@@ -248,6 +329,26 @@
String toString() => '${sourceUri}:[${line},${column}]';
}
+class DirectSourceLocation extends SourceLocation {
+ @override
+ final Uri sourceUri;
+
+ @override
+ final int offset;
+
+ @override
+ final int line;
+
+ @override
+ final int column;
+
+ @override
+ final String sourceName;
+
+ DirectSourceLocation(
+ this.sourceUri, this.offset, this.line, this.column, this.sourceName);
+}
+
/// A location in a source file.
abstract class AbstractSourceLocation extends SourceLocation {
final SourceFile _sourceFile;
@@ -255,7 +356,7 @@
AbstractSourceLocation(this._sourceFile) {
assert(
- isValid,
+ offset < _sourceFile.length,
failedAt(
new SourceSpan(sourceUri, 0, 0),
"Invalid source location in ${sourceUri}: "
@@ -283,9 +384,6 @@
String get sourceName;
@override
- bool get isValid => offset < _sourceFile.length;
-
- @override
String get shortText => '${sourceUri.pathSegments.last}:[$line,$column]';
@override
@@ -359,9 +457,6 @@
Uri get sourceUri => null;
@override
- bool get isValid => true;
-
- @override
String get sourceName => null;
@override
diff --git a/pkg/compiler/lib/src/js_backend/backend.dart b/pkg/compiler/lib/src/js_backend/backend.dart
index 0b7b8b3..79a1ada 100644
--- a/pkg/compiler/lib/src/js_backend/backend.dart
+++ b/pkg/compiler/lib/src/js_backend/backend.dart
@@ -6,7 +6,7 @@
import '../common.dart';
import '../common/backend_api.dart' show ImpactTransformer;
-import '../common/codegen.dart' show CodegenResult;
+import '../common/codegen.dart';
import '../common/names.dart' show Uris;
import '../common/tasks.dart' show CompilerTask;
import '../common/work.dart';
@@ -24,6 +24,7 @@
import '../js_emitter/js_emitter.dart' show CodeEmitterTask;
import '../kernel/dart2js_target.dart';
import '../native/enqueue.dart';
+import '../serialization/serialization.dart';
import '../ssa/ssa.dart' show SsaFunctionCompiler;
import '../tracer.dart';
import '../universe/class_hierarchy.dart'
@@ -282,13 +283,6 @@
}
}
-enum SyntheticConstantKind {
- DUMMY_INTERCEPTOR,
- EMPTY_VALUE,
- TYPEVARIABLE_REFERENCE, // Reference to a type in reflection data.
- NAME
-}
-
class JavaScriptBackend {
static const String JS = 'JS';
static const String JS_BUILTIN = 'JS_BUILTIN';
@@ -556,7 +550,7 @@
closedWorld.nativeData,
closedWorld,
compiler.abstractValueStrategy.createSelectorStrategy()),
- compiler.backendStrategy.createCodegenWorkItemBuilder(),
+ compiler.backendStrategy.createCodegenWorkItemBuilder(closedWorld),
new CodegenEnqueuerListener(
elementEnvironment,
commonElements,
@@ -569,9 +563,26 @@
Map<MemberEntity, WorldImpact> codegenImpactsForTesting;
- WorldImpact generateCode(WorkItem work) {
+ WorldImpact generateCode(WorkItem work, JClosedWorld closedWorld,
+ EntityLookup entityLookup, ComponentLookup componentLookup) {
MemberEntity member = work.element;
CodegenResult result = functionCompiler.compile(member);
+ if (compiler.options.testMode) {
+ bool useDataKinds = true;
+ List<Object> data = [];
+ DataSink sink = new ObjectSink(data, useDataKinds: useDataKinds);
+ sink.registerCodegenWriter(new CodegenWriterImpl(closedWorld));
+ result.writeToDataSink(sink);
+ DataSource source = new ObjectSource(data, useDataKinds: useDataKinds);
+ List<ModularName> modularNames = [];
+ List<ModularExpression> modularExpression = [];
+ source.registerCodegenReader(
+ new CodegenReaderImpl(closedWorld, modularNames, modularExpression));
+ source.registerEntityLookup(entityLookup);
+ source.registerComponentLookup(componentLookup);
+ result = CodegenResult.readFromDataSource(
+ source, modularNames, modularExpression);
+ }
if (result.code != null) {
generatedCode[member] = result.code;
}
@@ -583,6 +594,7 @@
_codegenImpactTransformer.transformCodegenImpact(result.impact);
compiler.dumpInfoTask.registerImpact(member, worldImpact);
result.applyModularState(_namer, emitterTask.emitter);
+ //print('$member:$result');
return worldImpact;
}
diff --git a/pkg/compiler/lib/src/js_backend/constant_emitter.dart b/pkg/compiler/lib/src/js_backend/constant_emitter.dart
index 780b1bc..96bc202 100644
--- a/pkg/compiler/lib/src/js_backend/constant_emitter.dart
+++ b/pkg/compiler/lib/src/js_backend/constant_emitter.dart
@@ -15,7 +15,6 @@
import '../js_emitter/code_emitter_task.dart';
import '../options.dart';
import 'field_analysis.dart' show JFieldAnalysis;
-import 'js_backend.dart';
import 'runtime_types.dart';
typedef jsAst.Expression _ConstantReferenceGenerator(ConstantValue constant);
@@ -145,18 +144,14 @@
}
@override
- jsAst.Expression visitSynthetic(SyntheticConstantValue constant, [_]) {
- switch (constant.valueKind) {
- case SyntheticConstantKind.DUMMY_INTERCEPTOR:
- case SyntheticConstantKind.EMPTY_VALUE:
- return new jsAst.LiteralNumber('0');
- case SyntheticConstantKind.TYPEVARIABLE_REFERENCE:
- case SyntheticConstantKind.NAME:
- return constant.payload;
- default:
- throw failedAt(NO_LOCATION_SPANNABLE,
- "Unexpected DummyConstantKind ${constant.kind}");
- }
+ jsAst.Expression visitAbstractValue(AbstractValueConstantValue constant,
+ [_]) {
+ return new jsAst.LiteralNumber('0');
+ }
+
+ @override
+ jsAst.Expression visitJsName(JsNameConstantValue constant, [_]) {
+ return constant.name;
}
@override
diff --git a/pkg/compiler/lib/src/js_backend/namer.dart b/pkg/compiler/lib/src/js_backend/namer.dart
index b76662e..ee53198 100644
--- a/pkg/compiler/lib/src/js_backend/namer.dart
+++ b/pkg/compiler/lib/src/js_backend/namer.dart
@@ -34,7 +34,6 @@
import '../universe/selector.dart' show Selector, SelectorKind;
import '../util/util.dart';
import '../world.dart' show JClosedWorld;
-import 'backend.dart';
import 'native_data.dart';
part 'field_naming_mixin.dart';
@@ -1885,21 +1884,13 @@
}
@override
- void visitSynthetic(SyntheticConstantValue constant, [_]) {
- switch (constant.valueKind) {
- case SyntheticConstantKind.DUMMY_INTERCEPTOR:
- add('dummy_receiver');
- break;
- case SyntheticConstantKind.TYPEVARIABLE_REFERENCE:
- // Omit. These are opaque deferred indexes with nothing helpful to add.
- break;
- case SyntheticConstantKind.NAME:
- add('name');
- break;
- default:
- failedAt(
- CURRENT_ELEMENT_SPANNABLE, "Unexpected SyntheticConstantValue");
- }
+ void visitAbstractValue(AbstractValueConstantValue constant, [_]) {
+ add('dummy_receiver');
+ }
+
+ @override
+ void visitJsName(JsNameConstantValue constant, [_]) {
+ add('name');
}
@override
@@ -2021,19 +2012,19 @@
}
@override
- int visitSynthetic(SyntheticConstantValue constant, [_]) {
- switch (constant.valueKind) {
- case SyntheticConstantKind.TYPEVARIABLE_REFERENCE:
- // These contain a deferred opaque index into metadata. There is nothing
- // we can access that is stable between compiles. Luckily, since they
- // resolve to integer indexes, they're always part of a larger constant.
- return 0;
- default:
- throw failedAt(
- NO_LOCATION_SPANNABLE,
- 'SyntheticConstantValue should never be named and '
- 'never be subconstant');
- }
+ int visitAbstractValue(AbstractValueConstantValue constant, [_]) {
+ throw failedAt(
+ NO_LOCATION_SPANNABLE,
+ 'AbstractValueConstantValue should never be named and '
+ 'never be subconstant');
+ }
+
+ @override
+ int visitJsName(JsNameConstantValue constant, [_]) {
+ throw failedAt(
+ NO_LOCATION_SPANNABLE,
+ 'JsNameConstantValue should never be named and '
+ 'never be subconstant');
}
@override
@@ -2433,7 +2424,7 @@
/// Returns the name for the async body of the method with the [original]
/// name.
jsAst.Name deriveAsyncBodyName(jsAst.Name original) {
- return new _AsyncName(_literalAsyncPrefix, original);
+ return new AsyncName(_literalAsyncPrefix, original);
}
/// Returns the label name for [label] used as a break target.
diff --git a/pkg/compiler/lib/src/js_backend/namer_names.dart b/pkg/compiler/lib/src/js_backend/namer_names.dart
index 0604258..f74a30a 100644
--- a/pkg/compiler/lib/src/js_backend/namer_names.dart
+++ b/pkg/compiler/lib/src/js_backend/namer_names.dart
@@ -118,11 +118,11 @@
SetterName(jsAst.Name prefix, jsAst.Name base) : super(prefix, base);
}
-class _AsyncName extends _PrefixedName {
+class AsyncName extends _PrefixedName {
@override
int get _kind => _NamerNameKinds.Async.index;
- _AsyncName(jsAst.Name prefix, jsAst.Name base) : super(prefix, base);
+ AsyncName(jsAst.Name prefix, jsAst.Name base) : super(prefix, base);
@override
bool get allowRename => true;
diff --git a/pkg/compiler/lib/src/js_emitter/constant_ordering.dart b/pkg/compiler/lib/src/js_emitter/constant_ordering.dart
index 9306361..fb1a792 100644
--- a/pkg/compiler/lib/src/js_emitter/constant_ordering.dart
+++ b/pkg/compiler/lib/src/js_emitter/constant_ordering.dart
@@ -8,7 +8,6 @@
import '../elements/entities.dart'
show ClassEntity, FieldEntity, MemberEntity, TypedefEntity;
import '../elements/types.dart';
-import '../js_backend/js_backend.dart' show SyntheticConstantKind;
import 'sorter.dart' show Sorter;
/// A canonical but arbitrary ordering of constants. The ordering is 'stable'
@@ -169,32 +168,16 @@
}
@override
- int visitSynthetic(SyntheticConstantValue a, SyntheticConstantValue b) {
- // [SyntheticConstantValue]s have abstract fields that are set only by
- // convention. Lucky for us, they do not occur as top level constant, only
- // as elements of a few constants. If this becomes a source of instability,
- // we will need to add a total ordering on JavaScript ASTs including
- // deferred elements.
- SyntheticConstantKind aKind = a.valueKind;
- SyntheticConstantKind bKind = b.valueKind;
- int r = aKind.index - bKind.index;
- if (r != 0) return r;
- switch (aKind) {
- case SyntheticConstantKind.DUMMY_INTERCEPTOR:
- case SyntheticConstantKind.EMPTY_VALUE:
- // Never emitted.
- return 0;
+ int visitAbstractValue(
+ AbstractValueConstantValue a, AbstractValueConstantValue b) {
+ // Never emitted.
+ return 0;
+ }
- case SyntheticConstantKind.TYPEVARIABLE_REFERENCE:
- // An opaque deferred JS AST reference to a type in reflection data.
- return 0;
- case SyntheticConstantKind.NAME:
- // An opaque deferred JS AST reference to a name.
- return 0;
- default:
- // Should not happen.
- throw 'unexpected SyntheticConstantKind $aKind';
- }
+ @override
+ int visitJsName(JsNameConstantValue a, JsNameConstantValue b) {
+ // An opaque deferred JS AST reference to a name.
+ return 0;
}
@override
@@ -229,10 +212,11 @@
static const int CONSTRUCTED = 10;
static const int TYPE = 11;
static const int INTERCEPTOR = 12;
- static const int SYNTHETIC = 13;
- static const int DEFERRED_GLOBAL = 14;
- static const int NONCONSTANT = 15;
- static const int INSTANTIATION = 16;
+ static const int ABSTRACT_VALUE = 13;
+ static const int JS_NAME = 14;
+ static const int DEFERRED_GLOBAL = 15;
+ static const int NONCONSTANT = 16;
+ static const int INSTANTIATION = 17;
static int kind(ConstantValue constant) =>
constant.accept(const _KindVisitor(), null);
@@ -264,7 +248,9 @@
@override
int visitInterceptor(InterceptorConstantValue a, _) => INTERCEPTOR;
@override
- int visitSynthetic(SyntheticConstantValue a, _) => SYNTHETIC;
+ int visitAbstractValue(AbstractValueConstantValue a, _) => ABSTRACT_VALUE;
+ @override
+ int visitJsName(JsNameConstantValue a, _) => JS_NAME;
@override
int visitDeferredGlobal(DeferredGlobalConstantValue a, _) => DEFERRED_GLOBAL;
@override
diff --git a/pkg/compiler/lib/src/js_model/element_map_impl.dart b/pkg/compiler/lib/src/js_model/element_map_impl.dart
index 47f5b36..5356e84 100644
--- a/pkg/compiler/lib/src/js_model/element_map_impl.dart
+++ b/pkg/compiler/lib/src/js_model/element_map_impl.dart
@@ -2584,3 +2584,35 @@
return typeVariable;
}
}
+
+/// [EntityLookup] implementation for an fully built [JsKernelToElementMap].
+class ClosedEntityLookup implements EntityLookup {
+ final JsKernelToElementMap _elementMap;
+
+ ClosedEntityLookup(this._elementMap);
+
+ @override
+ IndexedTypeVariable getTypeVariableByIndex(int index) {
+ return _elementMap.typeVariables.getEntity(index);
+ }
+
+ @override
+ IndexedMember getMemberByIndex(int index) {
+ return _elementMap.members.getEntity(index);
+ }
+
+ @override
+ IndexedTypedef getTypedefByIndex(int index) {
+ return _elementMap.typedefs.getEntity(index);
+ }
+
+ @override
+ IndexedClass getClassByIndex(int index) {
+ return _elementMap.classes.getEntity(index);
+ }
+
+ @override
+ IndexedLibrary getLibraryByIndex(int index) {
+ return _elementMap.libraries.getEntity(index);
+ }
+}
diff --git a/pkg/compiler/lib/src/js_model/js_strategy.dart b/pkg/compiler/lib/src/js_model/js_strategy.dart
index 6eafe83..ffe37e3 100644
--- a/pkg/compiler/lib/src/js_model/js_strategy.dart
+++ b/pkg/compiler/lib/src/js_model/js_strategy.dart
@@ -31,6 +31,7 @@
import '../kernel/kernel_strategy.dart';
import '../native/behavior.dart';
import '../options.dart';
+import '../serialization/serialization.dart';
import '../ssa/builder_kernel.dart';
import '../ssa/nodes.dart';
import '../ssa/ssa.dart';
@@ -110,8 +111,17 @@
}
@override
- WorkItemBuilder createCodegenWorkItemBuilder() {
- return new KernelCodegenWorkItemBuilder(_compiler.backend);
+ WorkItemBuilder createCodegenWorkItemBuilder(JClosedWorld closedWorld) {
+ assert(_elementMap != null,
+ "JsBackendStrategy.elementMap has not been created yet.");
+ return new KernelCodegenWorkItemBuilder(
+ _compiler.backend,
+ closedWorld,
+ new ClosedEntityLookup(_elementMap),
+ // TODO(johnniwinther): Avoid the need for a [ComponentLookup]. This
+ // is caused by some type masks holding a kernel node for using in
+ // tracing.
+ new ComponentLookup(_elementMap.programEnv.mainComponent));
}
@override
@@ -137,26 +147,36 @@
class KernelCodegenWorkItemBuilder implements WorkItemBuilder {
final JavaScriptBackend _backend;
+ final JClosedWorld _closedWorld;
+ final EntityLookup _entityLookup;
+ final ComponentLookup _componentLookup;
- KernelCodegenWorkItemBuilder(this._backend);
+ KernelCodegenWorkItemBuilder(this._backend, this._closedWorld,
+ this._entityLookup, this._componentLookup);
@override
WorkItem createWorkItem(MemberEntity entity) {
if (entity.isAbstract) return null;
- return new KernelCodegenWorkItem(_backend, entity);
+ return new KernelCodegenWorkItem(
+ _backend, _closedWorld, _entityLookup, _componentLookup, entity);
}
}
class KernelCodegenWorkItem extends WorkItem {
final JavaScriptBackend _backend;
+ final JClosedWorld _closedWorld;
+ final EntityLookup _entityLookup;
+ final ComponentLookup _componentLookup;
@override
final MemberEntity element;
- KernelCodegenWorkItem(this._backend, this.element);
+ KernelCodegenWorkItem(this._backend, this._closedWorld, this._entityLookup,
+ this._componentLookup, this.element);
@override
WorldImpact run() {
- return _backend.generateCode(this);
+ return _backend.generateCode(
+ this, _closedWorld, _entityLookup, _componentLookup);
}
}
diff --git a/pkg/compiler/lib/src/js_model/js_world_builder.dart b/pkg/compiler/lib/src/js_model/js_world_builder.dart
index 1a9544f..b3a59d2 100644
--- a/pkg/compiler/lib/src/js_model/js_world_builder.dart
+++ b/pkg/compiler/lib/src/js_model/js_world_builder.dart
@@ -846,7 +846,10 @@
@override
ConstantValue visitString(StringConstantValue constant, _) => constant;
@override
- ConstantValue visitSynthetic(SyntheticConstantValue constant, _) => constant;
+ ConstantValue visitAbstractValue(AbstractValueConstantValue constant, _) =>
+ constant;
+ @override
+ ConstantValue visitJsName(JsNameConstantValue constant, _) => constant;
@override
ConstantValue visitNonConstant(NonConstantValue constant, _) => constant;
diff --git a/pkg/compiler/lib/src/serialization/abstract_sink.dart b/pkg/compiler/lib/src/serialization/abstract_sink.dart
index dfe4e9b..ac9d487 100644
--- a/pkg/compiler/lib/src/serialization/abstract_sink.dart
+++ b/pkg/compiler/lib/src/serialization/abstract_sink.dart
@@ -34,6 +34,8 @@
Map<Type, IndexedSink> _generalCaches = {};
+ CodegenWriter _codegenWriter;
+
AbstractDataSink({this.useDataKinds: false}) {
_dartTypeWriter = new DartTypeWriter(this);
_dartTypeNodeWriter = new DartTypeNodeWriter(this);
@@ -439,12 +441,23 @@
break;
case ConstantValueKind.NON_CONSTANT:
break;
- case ConstantValueKind.DEFERRED_GLOBAL:
case ConstantValueKind.INTERCEPTOR:
- case ConstantValueKind.SYNTHETIC:
- // These are only created in the SSA graph builder.
- throw new UnsupportedError(
- "Unsupported constant value kind ${value.kind}.");
+ InterceptorConstantValue constant = value;
+ writeClass(constant.cls);
+ break;
+ case ConstantValueKind.DEFERRED_GLOBAL:
+ DeferredGlobalConstantValue constant = value;
+ writeConstant(constant.referenced);
+ writeOutputUnitReference(constant.unit);
+ break;
+ case ConstantValueKind.ABSTRACT_VALUE:
+ AbstractValueConstantValue constant = value;
+ writeAbstractValue(constant.abstractValue);
+ break;
+ case ConstantValueKind.JS_NAME:
+ JsNameConstantValue constant = value;
+ writeJsNode(constant.name);
+ break;
}
}
@@ -474,6 +487,36 @@
_writeBool(value.isDeferred);
}
+ @override
+ void registerCodegenWriter(CodegenWriter writer) {
+ assert(writer != null);
+ assert(_codegenWriter == null);
+ _codegenWriter = writer;
+ }
+
+ @override
+ void writeOutputUnitReference(OutputUnit value) {
+ assert(
+ _codegenWriter != null,
+ "Can not serialize an OutputUnit reference "
+ "without a registered codegen writer.");
+ _codegenWriter.writeOutputUnitReference(this, value);
+ }
+
+ @override
+ void writeAbstractValue(AbstractValue value) {
+ assert(_codegenWriter != null,
+ "Can not serialize an AbstractValue without a registered codegen writer.");
+ _codegenWriter.writeAbstractValue(this, value);
+ }
+
+ @override
+ void writeJsNode(js.Node value) {
+ assert(_codegenWriter != null,
+ "Can not serialize a JS ndoe without a registered codegen writer.");
+ _codegenWriter.writeJsNode(this, value);
+ }
+
/// Actual serialization of a section begin tag, implemented by subclasses.
void _begin(String tag);
diff --git a/pkg/compiler/lib/src/serialization/abstract_source.dart b/pkg/compiler/lib/src/serialization/abstract_source.dart
index f4160ce..87cb765 100644
--- a/pkg/compiler/lib/src/serialization/abstract_source.dart
+++ b/pkg/compiler/lib/src/serialization/abstract_source.dart
@@ -12,6 +12,7 @@
ComponentLookup _componentLookup;
EntityLookup _entityLookup;
LocalLookup _localLookup;
+ CodegenReader _codegenReader;
IndexedSource<String> _stringIndex;
IndexedSource<Uri> _uriIndex;
@@ -71,6 +72,13 @@
}
@override
+ void registerCodegenReader(CodegenReader reader) {
+ assert(reader != null);
+ assert(_codegenReader == null);
+ _codegenReader = reader;
+ }
+
+ @override
E readCached<E>(E f()) {
IndexedSource source = _generalCaches[E] ??= new IndexedSource<E>(this);
return source.read(f);
@@ -495,11 +503,19 @@
return new InstantiationConstantValue(typeArguments, function);
case ConstantValueKind.NON_CONSTANT:
return new NonConstantValue();
- case ConstantValueKind.DEFERRED_GLOBAL:
case ConstantValueKind.INTERCEPTOR:
- case ConstantValueKind.SYNTHETIC:
- // These are only created in the SSA graph builder.
- throw new UnsupportedError("Unsupported constant value kind ${kind}.");
+ ClassEntity cls = readClass();
+ return new InterceptorConstantValue(cls);
+ case ConstantValueKind.DEFERRED_GLOBAL:
+ ConstantValue constant = readConstant();
+ OutputUnit unit = readOutputUnitReference();
+ return new DeferredGlobalConstantValue(constant, unit);
+ case ConstantValueKind.ABSTRACT_VALUE:
+ AbstractValue abstractValue = readAbstractValue();
+ return new AbstractValueConstantValue(abstractValue);
+ case ConstantValueKind.JS_NAME:
+ js.LiteralString name = readJsNode();
+ return new JsNameConstantValue(name);
}
throw new UnsupportedError("Unexpexted constant value kind ${kind}.");
}
@@ -626,6 +642,31 @@
return new ImportEntity(isDeferred, name, uri, enclosingLibraryUri);
}
+ @override
+ OutputUnit readOutputUnitReference() {
+ assert(
+ _codegenReader != null,
+ "Can not deserialize an OutputUnit reference "
+ "without a registered codegen reader.");
+ return _codegenReader.readOutputUnitReference(this);
+ }
+
+ @override
+ AbstractValue readAbstractValue() {
+ assert(
+ _codegenReader != null,
+ "Can not deserialize an AbstractValue "
+ "without a registered codegen reader.");
+ return _codegenReader.readAbstractValue(this);
+ }
+
+ @override
+ js.Node readJsNode() {
+ assert(_codegenReader != null,
+ "Can not deserialize a JS node without a registered codegen reader.");
+ return _codegenReader.readJsNode(this);
+ }
+
/// Actual deserialization of a section begin tag, implemented by subclasses.
void _begin(String tag);
diff --git a/pkg/compiler/lib/src/serialization/mixins.dart b/pkg/compiler/lib/src/serialization/mixins.dart
index 695d112..2de3e8a 100644
--- a/pkg/compiler/lib/src/serialization/mixins.dart
+++ b/pkg/compiler/lib/src/serialization/mixins.dart
@@ -314,6 +314,15 @@
}
@override
+ ImportEntity readImportOrNull() {
+ bool hasClass = readBool();
+ if (hasClass) {
+ return readImport();
+ }
+ return null;
+ }
+
+ @override
List<ImportEntity> readImports({bool emptyAsNull: false}) {
int count = readInt();
if (count == 0 && emptyAsNull) return null;
@@ -366,6 +375,15 @@
ir.LibraryDependency readLibraryDependencyNodeOrNull() {
return readValueOrNull(readLibraryDependencyNode);
}
+
+ @override
+ js.Node readJsNodeOrNull() {
+ bool hasValue = readBool();
+ if (hasValue) {
+ return readJsNode();
+ }
+ return null;
+ }
}
/// Mixin that implements all convenience methods of [DataSink].
@@ -693,6 +711,14 @@
}
@override
+ void writeImportOrNull(ImportEntity value) {
+ writeBool(value != null);
+ if (value != null) {
+ writeImport(value);
+ }
+ }
+
+ @override
void writeImports(Iterable<ImportEntity> values, {bool allowNull: false}) {
if (values == null) {
assert(allowNull);
@@ -751,4 +777,12 @@
void writeLibraryDependencyNodeOrNull(ir.LibraryDependency value) {
writeValueOrNull(value, writeLibraryDependencyNode);
}
+
+ @override
+ void writeJsNodeOrNull(js.Node value) {
+ writeBool(value != null);
+ if (value != null) {
+ writeJsNode(value);
+ }
+ }
}
diff --git a/pkg/compiler/lib/src/serialization/serialization.dart b/pkg/compiler/lib/src/serialization/serialization.dart
index 8623f87..d4f574c 100644
--- a/pkg/compiler/lib/src/serialization/serialization.dart
+++ b/pkg/compiler/lib/src/serialization/serialization.dart
@@ -11,12 +11,15 @@
import '../closure.dart';
import '../constants/constant_system.dart' as constant_system;
import '../constants/values.dart';
+import '../deferred_load.dart';
import '../diagnostics/source_span.dart';
import '../elements/entities.dart';
import '../elements/indexed.dart';
import '../elements/types.dart';
+import '../inferrer/abstract_value_domain.dart';
import '../ir/constants.dart';
import '../ir/static_type_base.dart';
+import '../js/js.dart' as js;
import '../js_model/closure.dart';
import '../js_model/locals.dart';
@@ -357,6 +360,9 @@
/// Writes the import [value] to this data sink.
void writeImport(ImportEntity value);
+ /// Writes the potentially `null` import [value] to this data sink.
+ void writeImportOrNull(ImportEntity value);
+
/// Writes import [values] to this data sink. If [allowNull] is `true`,
/// [values] is allowed to be `null`.
///
@@ -372,6 +378,30 @@
/// [DataSource.readImportMap].
void writeImportMap<V>(Map<ImportEntity, V> map, void f(V value),
{bool allowNull: false});
+
+ /// Writes an abstract [value] to this data sink.
+ ///
+ /// This feature is only available a [CodegenWriter] has been registered.
+ void writeAbstractValue(AbstractValue value);
+
+ /// Writes a reference to the output unit [value] to this data sink.
+ ///
+ /// This feature is only available a [CodegenWriter] has been registered.
+ void writeOutputUnitReference(OutputUnit value);
+
+ /// Writes a js node [value] to this data sink.
+ ///
+ /// This feature is only available a [CodegenWriter] has been registered.
+ void writeJsNode(js.Node value);
+
+ /// Writes a potentially `null` js node [value] to this data sink.
+ ///
+ /// This feature is only available a [CodegenWriter] has been registered.
+ void writeJsNodeOrNull(js.Node value);
+
+ /// Register a [CodegenWriter] with this data sink to support serialization
+ /// of codegen only data.
+ void registerCodegenWriter(CodegenWriter writer);
}
/// Interface for deserialization.
@@ -400,6 +430,10 @@
/// deserialization of references to locals.
void registerLocalLookup(LocalLookup localLookup);
+ /// Registers a [CodegenReader] with this data source to support
+ /// deserialization of codegen only data.
+ void registerCodegenReader(CodegenReader read);
+
/// Reads a reference to an [E] value from this data source. If the value has
/// not yet been deserialized, [f] is called to deserialize the value itself.
E readCached<E>(E f());
@@ -680,6 +714,9 @@
/// Reads a import from this data source.
ImportEntity readImport();
+ /// Reads a potentially `null` import from this data source.
+ ImportEntity readImportOrNull();
+
/// Reads a list of imports from this data source. If [emptyAsNull] is
/// `true`, `null` is returned instead of an empty list.
///
@@ -694,6 +731,26 @@
/// This is a convenience method to be used together with
/// [DataSink.writeImportMap].
Map<ImportEntity, V> readImportMap<V>(V f(), {bool emptyAsNull: false});
+
+ /// Reads an [AbstractValue] from this data source.
+ ///
+ /// This feature is only available a [CodegenReader] has been registered.
+ AbstractValue readAbstractValue();
+
+ /// Reads a reference to an [OutputUnit] from this data source.
+ ///
+ /// This feature is only available a [CodegenReader] has been registered.
+ OutputUnit readOutputUnitReference();
+
+ /// Reads a [js.Node] value from this data source.
+ ///
+ /// This feature is only available a [CodegenReader] has been registered.
+ js.Node readJsNode();
+
+ /// Reads a potentially `null` [js.Node] value from this data source.
+ ///
+ /// This feature is only available a [CodegenReader] has been registered.
+ js.Node readJsNodeOrNull();
}
/// Interface used for looking up entities by index during deserialization.
@@ -718,3 +775,17 @@
abstract class LocalLookup {
Local getLocalByIndex(MemberEntity memberContext, int index);
}
+
+/// Interface used for reading codegen only data during deserialization.
+abstract class CodegenReader {
+ AbstractValue readAbstractValue(DataSource source);
+ OutputUnit readOutputUnitReference(DataSource source);
+ js.Node readJsNode(DataSource source);
+}
+
+/// Interface used for writing codegen only data during serialization.
+abstract class CodegenWriter {
+ void writeAbstractValue(DataSink sink, AbstractValue value);
+ void writeOutputUnitReference(DataSink sink, OutputUnit value);
+ void writeJsNode(DataSink sink, js.Node node);
+}
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index e39180d..67cc980 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -4947,16 +4947,16 @@
var arguments = <HInstruction>[];
node.expression.accept(this);
arguments.add(pop());
- // TODO(johnniwinther): Use the static type of the expression.
+ StaticType expressionType = _getStaticType(node.expression);
bool typeArgumentsNeeded = _rtiNeed.instantiationNeedsTypeArguments(
- null, node.typeArguments.length);
+ expressionType.type, node.typeArguments.length);
List<DartType> typeArguments = node.typeArguments
.map((type) => typeArgumentsNeeded
? _elementMap.getDartType(type)
: _commonElements.dynamicType)
.toList();
registry.registerGenericInstantiation(
- new GenericInstantiation(null, typeArguments));
+ new GenericInstantiation(expressionType.type, typeArguments));
// TODO(johnniwinther): Can we avoid creating the instantiation object?
for (DartType type in typeArguments) {
HInstruction instruction =
diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart
index 502ab8e..47d0de0 100644
--- a/pkg/compiler/lib/src/ssa/codegen.dart
+++ b/pkg/compiler/lib/src/ssa/codegen.dart
@@ -67,13 +67,13 @@
if (needsAsyncRewrite) {
return finish(element.asyncMarker.isAsync
? (element.asyncMarker.isYielding
- ? const js.AsyncModifier.asyncStar()
- : const js.AsyncModifier.async())
+ ? js.AsyncModifier.asyncStar
+ : js.AsyncModifier.async)
: (element.asyncMarker.isYielding
- ? const js.AsyncModifier.syncStar()
- : const js.AsyncModifier.sync()));
+ ? js.AsyncModifier.syncStar
+ : js.AsyncModifier.sync));
} else {
- return finish(const js.AsyncModifier.sync());
+ return finish(js.AsyncModifier.sync);
}
}
diff --git a/pkg/compiler/lib/src/ssa/codegen_helpers.dart b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
index 7639921..bc388f2 100644
--- a/pkg/compiler/lib/src/ssa/codegen_helpers.dart
+++ b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
@@ -5,8 +5,7 @@
import '../constants/values.dart';
import '../elements/entities.dart';
import '../inferrer/abstract_value_domain.dart';
-import '../js_backend/js_backend.dart'
- show SuperMemberData, SyntheticConstantKind;
+import '../js_backend/js_backend.dart' show SuperMemberData;
import '../js_backend/interceptor_data.dart';
import '../options.dart';
import '../universe/selector.dart' show Selector;
@@ -191,9 +190,8 @@
if (_interceptorData.isInterceptedSelector(selector) &&
!_interceptorData.isInterceptedMixinSelector(
selector, mask, _closedWorld)) {
- ConstantValue constant = new SyntheticConstantValue(
- SyntheticConstantKind.DUMMY_INTERCEPTOR,
- receiverArgument.instructionType);
+ ConstantValue constant =
+ new AbstractValueConstantValue(receiverArgument.instructionType);
HConstant dummy = graph.addConstant(constant, _closedWorld);
receiverArgument.usedBy.remove(node);
node.inputs[1] = dummy;
diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart
index 8852a01..a893874 100644
--- a/pkg/compiler/lib/src/ssa/nodes.dart
+++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -15,7 +15,6 @@
import '../inferrer/abstract_value_domain.dart';
import '../io/source_information.dart';
import '../js/js.dart' as js;
-import '../js_backend/backend.dart' show SyntheticConstantKind;
import '../native/behavior.dart';
import '../universe/selector.dart' show Selector;
import '../universe/side_effects.dart' show SideEffects;
@@ -313,9 +312,7 @@
HConstant addConstantStringFromName(js.Name name, JClosedWorld closedWorld) {
return addConstant(
- new SyntheticConstantValue(
- SyntheticConstantKind.NAME, js.quoteName(name)),
- closedWorld);
+ new JsNameConstantValue(js.quoteName(name)), closedWorld);
}
HConstant addConstantBool(bool value, JClosedWorld closedWorld) {
@@ -330,7 +327,7 @@
// A constant with an empty type used as the HInstruction of an expression
// in an unreachable context.
return addConstant(
- new SyntheticConstantValue(SyntheticConstantKind.EMPTY_VALUE,
+ new AbstractValueConstantValue(
closedWorld.abstractValueDomain.emptyType),
closedWorld);
}
diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart
index b605866..91a06794f 100644
--- a/pkg/compiler/lib/src/ssa/optimize.dart
+++ b/pkg/compiler/lib/src/ssa/optimize.dart
@@ -15,7 +15,7 @@
import '../inferrer/types.dart';
import '../js_backend/field_analysis.dart'
show FieldAnalysisData, JFieldAnalysis;
-import '../js_backend/backend.dart' show CodegenInputs, SyntheticConstantKind;
+import '../js_backend/backend.dart' show CodegenInputs;
import '../js_backend/native_data.dart' show NativeData;
import '../js_backend/runtime_types.dart';
import '../native/behavior.dart';
@@ -1955,8 +1955,8 @@
HInstruction get zapInstruction {
if (zapInstructionCache == null) {
// A constant with no type does not pollute types at phi nodes.
- ConstantValue constant = new SyntheticConstantValue(
- SyntheticConstantKind.EMPTY_VALUE, _abstractValueDomain.emptyType);
+ ConstantValue constant =
+ new AbstractValueConstantValue(_abstractValueDomain.emptyType);
zapInstructionCache = analyzer.graph.addConstant(constant, closedWorld);
}
return zapInstructionCache;
diff --git a/pkg/compiler/lib/src/universe/feature.dart b/pkg/compiler/lib/src/universe/feature.dart
index f6386be..92ded95 100644
--- a/pkg/compiler/lib/src/universe/feature.dart
+++ b/pkg/compiler/lib/src/universe/feature.dart
@@ -10,6 +10,7 @@
import '../elements/types.dart';
import '../ir/runtime_type_analysis.dart';
+import '../serialization/serialization.dart';
import '../util/util.dart';
/// A language feature that may be seen in the program.
@@ -236,6 +237,8 @@
/// A generic instantiation of an expression of type [functionType] with the
/// given [typeArguments].
class GenericInstantiation {
+ static const String tag = 'generic-instantiation';
+
/// The static type of the instantiated expression.
final DartType functionType;
@@ -244,6 +247,21 @@
GenericInstantiation(this.functionType, this.typeArguments);
+ factory GenericInstantiation.readFromDataSource(DataSource source) {
+ source.begin(tag);
+ DartType functionType = source.readDartType();
+ List<DartType> typeArguments = source.readDartTypes();
+ source.end(tag);
+ return new GenericInstantiation(functionType, typeArguments);
+ }
+
+ void writeToDataSink(DataSink sink) {
+ sink.begin(tag);
+ sink.writeDartType(functionType);
+ sink.writeDartTypes(typeArguments);
+ sink.end(tag);
+ }
+
/// Short textual representation use for testing.
String get shortText => '<${typeArguments.join(',')}>';
diff --git a/pkg/compiler/lib/src/universe/use.dart b/pkg/compiler/lib/src/universe/use.dart
index 8b4c082..d062fc5 100644
--- a/pkg/compiler/lib/src/universe/use.dart
+++ b/pkg/compiler/lib/src/universe/use.dart
@@ -20,6 +20,8 @@
import '../constants/values.dart';
import '../elements/types.dart';
import '../elements/entities.dart';
+import '../inferrer/abstract_value_domain.dart';
+import '../serialization/serialization.dart';
import '../js_model/closure.dart';
import '../util/util.dart' show equalElements, Hashing;
import 'call_structure.dart' show CallStructure;
@@ -36,6 +38,8 @@
/// property and [receiverConstraint] defines the known constraint for the
/// object on which the property is accessed.
class DynamicUse {
+ static const String tag = 'dynamic-use';
+
final Selector selector;
final Object receiverConstraint;
final List<DartType> _typeArguments;
@@ -49,6 +53,35 @@
"${_typeArguments?.length ?? 0} were passed.");
}
+ factory DynamicUse.readFromDataSource(DataSource source) {
+ source.begin(tag);
+ Selector selector = Selector.readFromDataSource(source);
+ bool hasConstraint = source.readBool();
+ Object receiverConstraint;
+ if (hasConstraint) {
+ receiverConstraint = source.readAbstractValue();
+ }
+ List<DartType> typeArguments = source.readDartTypes(emptyAsNull: true);
+ source.end(tag);
+ return new DynamicUse(selector, receiverConstraint, typeArguments);
+ }
+
+ void writeToDataSink(DataSink sink) {
+ sink.begin(tag);
+ selector.writeToDataSink(sink);
+ sink.writeBool(receiverConstraint != null);
+ if (receiverConstraint != null) {
+ if (receiverConstraint is AbstractValue) {
+ sink.writeAbstractValue(receiverConstraint);
+ } else {
+ throw new UnsupportedError(
+ "Unsupported receiver constraint: ${receiverConstraint}");
+ }
+ }
+ sink.writeDartTypes(_typeArguments, allowNull: true);
+ sink.end(tag);
+ }
+
/// Short textual representation use for testing.
String get shortText {
StringBuffer sb = new StringBuffer();
@@ -136,6 +169,8 @@
// TODO(johnniwinther): Create backend-specific implementations with better
// invariants.
class StaticUse {
+ static const String tag = 'static-use';
+
final Entity element;
final StaticUseKind kind;
@override
@@ -174,6 +209,39 @@
return true;
}
+ factory StaticUse.readFromDataSource(DataSource source) {
+ source.begin(tag);
+ MemberEntity element = source.readMember();
+ StaticUseKind kind = source.readEnum(StaticUseKind.values);
+ InterfaceType type = source.readDartType(allowNull: true);
+ CallStructure callStructure =
+ source.readValueOrNull(() => CallStructure.readFromDataSource(source));
+ ImportEntity deferredImport = source.readImportOrNull();
+ ConstantValue constant = source.readConstantOrNull();
+ List<DartType> typeArguments = source.readDartTypes(emptyAsNull: true);
+ source.end(tag);
+ return new StaticUse.internal(element, kind,
+ type: type,
+ callStructure: callStructure,
+ deferredImport: deferredImport,
+ constant: constant,
+ typeArguments: typeArguments);
+ }
+
+ void writeToDataSink(DataSink sink) {
+ sink.begin(tag);
+ assert(element is MemberEntity, "Unsupported entity: $element");
+ sink.writeMember(element);
+ sink.writeEnum(kind);
+ sink.writeDartType(type, allowNull: true);
+ sink.writeValueOrNull(
+ callStructure, (CallStructure c) => c.writeToDataSink(sink));
+ sink.writeImportOrNull(deferredImport);
+ sink.writeConstantOrNull(constant);
+ sink.writeDartTypes(typeArguments, allowNull: true);
+ sink.end(tag);
+ }
+
/// Short textual representation use for testing.
String get shortText {
StringBuffer sb = new StringBuffer();
@@ -638,6 +706,8 @@
/// Use of a [DartType].
class TypeUse {
+ static const String tag = 'type-use';
+
final DartType type;
final TypeUseKind kind;
@override
@@ -649,6 +719,23 @@
this.kind = kind,
this.hashCode = Hashing.objectsHash(type, kind, deferredImport);
+ factory TypeUse.readFromDataSource(DataSource source) {
+ source.begin(tag);
+ DartType type = source.readDartType();
+ TypeUseKind kind = source.readEnum(TypeUseKind.values);
+ ImportEntity deferredImport = source.readImportOrNull();
+ source.end(tag);
+ return new TypeUse.internal(type, kind, deferredImport);
+ }
+
+ void writeToDataSink(DataSink sink) {
+ sink.begin(tag);
+ sink.writeDartType(type);
+ sink.writeEnum(kind);
+ sink.writeImportOrNull(deferredImport);
+ sink.end(tag);
+ }
+
/// Short textual representation use for testing.
String get shortText {
StringBuffer sb = new StringBuffer();
@@ -784,10 +871,25 @@
/// Use of a [ConstantValue].
class ConstantUse {
+ static const String tag = 'constant-use';
+
final ConstantValue value;
ConstantUse._(this.value);
+ factory ConstantUse.readFromDataSource(DataSource source) {
+ source.begin(tag);
+ ConstantValue value = source.readConstant();
+ source.end(tag);
+ return new ConstantUse._(value);
+ }
+
+ void writeToDataSink(DataSink sink) {
+ sink.begin(tag);
+ sink.writeConstant(value);
+ sink.end(tag);
+ }
+
/// Short textual representation use for testing.
String get shortText {
return value.toDartText();
diff --git a/pkg/compiler/lib/src/universe/world_impact.dart b/pkg/compiler/lib/src/universe/world_impact.dart
index cc9a92c..f3c8e21 100644
--- a/pkg/compiler/lib/src/universe/world_impact.dart
+++ b/pkg/compiler/lib/src/universe/world_impact.dart
@@ -82,6 +82,11 @@
Set<TypeUse> _typeUses;
Set<ConstantUse> _constantUses;
+ WorldImpactBuilderImpl();
+
+ WorldImpactBuilderImpl.internal(
+ this._dynamicUses, this._staticUses, this._typeUses, this._constantUses);
+
@override
bool get isEmpty =>
_dynamicUses == null &&
diff --git a/pkg/js_ast/lib/src/builder.dart b/pkg/js_ast/lib/src/builder.dart
index 635549b..bf9e94a 100644
--- a/pkg/js_ast/lib/src/builder.dart
+++ b/pkg/js_ast/lib/src/builder.dart
@@ -1029,15 +1029,15 @@
AsyncModifier asyncModifier;
if (acceptString('async')) {
if (acceptString('*')) {
- asyncModifier = const AsyncModifier.asyncStar();
+ asyncModifier = AsyncModifier.asyncStar;
} else {
- asyncModifier = const AsyncModifier.async();
+ asyncModifier = AsyncModifier.async;
}
} else if (acceptString('sync')) {
if (!acceptString('*')) error("Only sync* is valid - sync is implied");
- asyncModifier = const AsyncModifier.syncStar();
+ asyncModifier = AsyncModifier.syncStar;
} else {
- asyncModifier = const AsyncModifier.sync();
+ asyncModifier = AsyncModifier.sync;
}
expectCategory(LBRACE);
Block block = parseBlock();
diff --git a/pkg/js_ast/lib/src/nodes.dart b/pkg/js_ast/lib/src/nodes.dart
index 28923cf..d2970d4 100644
--- a/pkg/js_ast/lib/src/nodes.dart
+++ b/pkg/js_ast/lib/src/nodes.dart
@@ -1448,7 +1448,7 @@
final Block body;
final AsyncModifier asyncModifier;
- Fun(this.params, this.body, {this.asyncModifier: const AsyncModifier.sync()});
+ Fun(this.params, this.body, {this.asyncModifier: AsyncModifier.sync});
T accept<T>(NodeVisitor<T> visitor) => visitor.visitFun(this);
@@ -1471,27 +1471,26 @@
}
class AsyncModifier {
+ final int index;
final bool isAsync;
final bool isYielding;
final String description;
- const AsyncModifier.sync()
- : isAsync = false,
- isYielding = false,
- description = "sync";
- const AsyncModifier.async()
- : isAsync = true,
- isYielding = false,
- description = "async";
- const AsyncModifier.asyncStar()
- : isAsync = true,
- isYielding = true,
- description = "async*";
- const AsyncModifier.syncStar()
- : isAsync = false,
- isYielding = true,
- description = "sync*";
- toString() => description;
+ const AsyncModifier(this.index, this.description,
+ {this.isAsync, this.isYielding});
+
+ static const AsyncModifier sync =
+ const AsyncModifier(0, "sync", isAsync: false, isYielding: false);
+ static const AsyncModifier async =
+ const AsyncModifier(1, "async", isAsync: true, isYielding: false);
+ static const AsyncModifier asyncStar =
+ const AsyncModifier(2, "async*", isAsync: true, isYielding: true);
+ static const AsyncModifier syncStar =
+ const AsyncModifier(3, "sync*", isAsync: false, isYielding: true);
+
+ static const List<AsyncModifier> values = [sync, async, asyncStar, syncStar];
+
+ String toString() => description;
}
class PropertyAccess extends Expression {
diff --git a/pkg/js_ast/lib/src/printer.dart b/pkg/js_ast/lib/src/printer.dart
index d5eba7b..399ab56 100644
--- a/pkg/js_ast/lib/src/printer.dart
+++ b/pkg/js_ast/lib/src/printer.dart
@@ -620,17 +620,17 @@
}
out(")");
switch (fun.asyncModifier) {
- case const AsyncModifier.sync():
+ case AsyncModifier.sync:
break;
- case const AsyncModifier.async():
+ case AsyncModifier.async:
out(' ', isWhitespace: true);
out('async');
break;
- case const AsyncModifier.syncStar():
+ case AsyncModifier.syncStar:
out(' ', isWhitespace: true);
out('sync*');
break;
- case const AsyncModifier.asyncStar():
+ case AsyncModifier.asyncStar:
out(' ', isWhitespace: true);
out('async*');
break;
diff --git a/tests/compiler/dart2js/end_to_end/exit_code_test.dart b/tests/compiler/dart2js/end_to_end/exit_code_test.dart
index 082edeb..e424fad 100644
--- a/tests/compiler/dart2js/end_to_end/exit_code_test.dart
+++ b/tests/compiler/dart2js/end_to_end/exit_code_test.dart
@@ -22,8 +22,10 @@
import 'package:compiler/src/elements/entities.dart';
import 'package:compiler/src/js_backend/js_backend.dart';
import 'package:compiler/src/null_compiler_output.dart';
+import 'package:compiler/src/serialization/serialization.dart';
import 'package:compiler/src/options.dart' show CompilerOptions;
import 'package:compiler/src/universe/world_impact.dart';
+import 'package:compiler/src/world.dart';
import 'diagnostic_reporter_helper.dart';
class TestCompiler extends apiimpl.CompilerImpl {
@@ -108,9 +110,10 @@
useNewSourceInfo: compiler.options.useNewSourceInfo);
@override
- WorldImpact generateCode(WorkItem work) {
+ WorldImpact generateCode(WorkItem work, JClosedWorld closedWorld,
+ EntityLookup entityLookup, ComponentLookup componentLookup) {
compiler.test('Compiler.codegen');
- return super.generateCode(work);
+ return super.generateCode(work, closedWorld, entityLookup, componentLookup);
}
}