From 8b6f8969a6d3367a6ad2551c290048c61941ef77 Mon Sep 17 00:00:00 2001 From: Johnni Winther Date: Mon, 22 Oct 2018 14:37:04 +0000 Subject: [PATCH] Support post-inference serialization/deserialization Currently handling Swarm in-memory serialization/deserialization. Change-Id: I4b01f70d1e42d7462ed079ece227d6c2ed4de597 Reviewed-on: https://dart-review.googlesource.com/c/80445 Commit-Queue: Johnni Winther Reviewed-by: Sigmund Cherem Auto-Submit: Johnni Winther --- pkg/compiler/lib/src/backend_strategy.dart | 8 +- pkg/compiler/lib/src/closure.dart | 84 +++ pkg/compiler/lib/src/compiler.dart | 3 +- pkg/compiler/lib/src/deferred_load.dart | 87 +++ pkg/compiler/lib/src/elements/entities.dart | 27 + pkg/compiler/lib/src/elements/indexed.dart | 91 ++- .../lib/src/inferrer/inferrer_engine.dart | 91 ++- .../lib/src/inferrer/type_graph_inferrer.dart | 2 +- .../typemasks/container_type_mask.dart | 30 + .../typemasks/dictionary_type_mask.dart | 36 ++ .../inferrer/typemasks/flat_type_mask.dart | 33 +- .../src/inferrer/typemasks/map_type_mask.dart | 30 + .../lib/src/inferrer/typemasks/masks.dart | 13 +- .../lib/src/inferrer/typemasks/type_mask.dart | 34 ++ .../inferrer/typemasks/union_type_mask.dart | 25 +- .../inferrer/typemasks/value_type_mask.dart | 23 + .../src/js_backend/allocator_analysis.dart | 33 + .../lib/src/js_backend/annotations.dart | 45 ++ .../lib/src/js_backend/backend_usage.dart | 67 +++ .../lib/src/js_backend/inferred_data.dart | 58 ++ .../lib/src/js_backend/interceptor_data.dart | 48 ++ .../lib/src/js_backend/native_data.dart | 123 ++++ .../js_backend/no_such_method_registry.dart | 41 ++ .../lib/src/js_backend/runtime_types.dart | 62 ++ .../lib/src/js_emitter/code_emitter_task.dart | 2 +- pkg/compiler/lib/src/js_model/closure.dart | 452 +++++++++++++- .../lib/src/js_model/element_map.dart | 100 +++ .../lib/src/js_model/element_map_impl.dart | 318 +++++++++- pkg/compiler/lib/src/js_model/elements.dart | 532 +++++++++++++++- pkg/compiler/lib/src/js_model/env.dart | 430 ++++++++++++- .../lib/src/js_model/js_strategy.dart | 135 ++++- pkg/compiler/lib/src/js_model/locals.dart | 168 +++++- pkg/compiler/lib/src/kernel/env.dart | 2 +- pkg/compiler/lib/src/native/behavior.dart | 87 +++ pkg/compiler/lib/src/ordered_typeset.dart | 69 +++ .../lib/src/serialization/abstract_sink.dart | 332 ++++++++++ .../src/serialization/abstract_source.dart | 418 +++++++++++++ .../lib/src/serialization/binary_sink.dart | 60 ++ .../lib/src/serialization/binary_source.dart | 67 +++ .../lib/src/serialization/helpers.dart | 176 ++++++ .../lib/src/serialization/member_data.dart | 159 +++++ .../lib/src/serialization/mixins.dart | 569 ++++++++++++++++++ .../lib/src/serialization/node_indexer.dart | 142 +++++ .../lib/src/serialization/object_sink.dart | 53 ++ .../lib/src/serialization/object_source.dart | 71 +++ .../lib/src/serialization/serialization.dart | 560 +++++++++++++++++ .../lib/src/types/abstract_value_domain.dart | 7 + pkg/compiler/lib/src/types/types.dart | 142 ++++- .../lib/src/universe/call_structure.dart | 24 + .../lib/src/universe/class_hierarchy.dart | 51 ++ pkg/compiler/lib/src/universe/class_set.dart | 71 +++ pkg/compiler/lib/src/universe/selector.dart | 29 + .../lib/src/universe/side_effects.dart | 20 + pkg/compiler/lib/src/util/enumset.dart | 13 + pkg/compiler/lib/src/world.dart | 13 +- .../dart2js/analyses/dart2js_allowed.json | 38 +- .../dart2js/model/in_memory_split_test.dart | 90 ++- 57 files changed, 6421 insertions(+), 73 deletions(-) create mode 100644 pkg/compiler/lib/src/serialization/abstract_sink.dart create mode 100644 pkg/compiler/lib/src/serialization/abstract_source.dart create mode 100644 pkg/compiler/lib/src/serialization/binary_sink.dart create mode 100644 pkg/compiler/lib/src/serialization/binary_source.dart create mode 100644 pkg/compiler/lib/src/serialization/helpers.dart create mode 100644 pkg/compiler/lib/src/serialization/member_data.dart create mode 100644 pkg/compiler/lib/src/serialization/mixins.dart create mode 100644 pkg/compiler/lib/src/serialization/node_indexer.dart create mode 100644 pkg/compiler/lib/src/serialization/object_sink.dart create mode 100644 pkg/compiler/lib/src/serialization/object_source.dart create mode 100644 pkg/compiler/lib/src/serialization/serialization.dart diff --git a/pkg/compiler/lib/src/backend_strategy.dart b/pkg/compiler/lib/src/backend_strategy.dart index 6b2e8f0537736..c3aefb12d4d9c 100644 --- a/pkg/compiler/lib/src/backend_strategy.dart +++ b/pkg/compiler/lib/src/backend_strategy.dart @@ -13,7 +13,6 @@ import 'io/source_information.dart'; import 'js_backend/inferred_data.dart'; import 'js_backend/js_backend.dart'; import 'js_backend/native_data.dart'; -import 'js_emitter/sorter.dart'; import 'ssa/ssa.dart'; import 'types/types.dart'; import 'universe/world_builder.dart'; @@ -26,8 +25,11 @@ abstract class BackendStrategy { JClosedWorld createJClosedWorld( KClosedWorld closedWorld, OutputUnitData outputUnitData); - /// The [Sorter] used for sorting elements in the generated code. - Sorter get sorter; + /// Registers [closedWorld] as the current closed world used by this backend + /// strategy. + /// + /// This is used to support serialization after type inference. + void registerJClosedWorld(JClosedWorld closedWorld); /// Creates the [CodegenWorldBuilder] used by the codegen enqueuer. CodegenWorldBuilder createCodegenWorldBuilder( diff --git a/pkg/compiler/lib/src/closure.dart b/pkg/compiler/lib/src/closure.dart index 8006d88f0d381..22b3fc723851b 100644 --- a/pkg/compiler/lib/src/closure.dart +++ b/pkg/compiler/lib/src/closure.dart @@ -7,6 +7,9 @@ import 'common/tasks.dart' show CompilerTask, Measurer; import 'common.dart'; import 'elements/entities.dart'; import 'elements/types.dart'; +import 'js_model/closure.dart'; +import 'js_model/element_map.dart'; +import 'serialization/serialization.dart'; abstract class ClosureConversionTask extends CompilerTask { ClosureConversionTask(Measurer measurer) : super(measurer); @@ -17,6 +20,14 @@ abstract class ClosureConversionTask extends CompilerTask { /// node to look up, it returns a information about the internal representation /// of how closure conversion is implemented. T is an ir.Node or Node. abstract class ClosureData { + /// Deserializes a [ClosureData] object from [source]. + factory ClosureData.readFromDataSource( + JsToElementMap elementMap, DataSource source) = + ClosureDataImpl.readFromDataSource; + + /// Serializes this [ClosureData] to [sink]. + void writeToDataSink(DataSink sink); + /// Look up information about the variables that have been mutated and are /// used inside the scope of [node]. ScopeInfo getScopeInfo(MemberEntity member); @@ -32,6 +43,14 @@ abstract class ClosureData { CapturedScope getCapturedScope(MemberEntity entity); } +/// Enum used for identifying [ScopeInfo] subclasses in serialization. +enum ScopeInfoKind { + scopeInfo, + capturedScope, + capturedLoopScope, + closureRepresentationInfo, +} + /// Class that represents one level of scoping information, whether this scope /// is a closure or not. This is specifically used to store information /// about the usage of variables in try or sync blocks, because they need to be @@ -44,6 +63,27 @@ abstract class ClosureData { class ScopeInfo { const ScopeInfo(); + /// Deserializes a [ScopeInfo] object from [source]. + factory ScopeInfo.readFromDataSource(DataSource source) { + ScopeInfoKind kind = source.readEnum(ScopeInfoKind.values); + switch (kind) { + case ScopeInfoKind.scopeInfo: + return new JsScopeInfo.readFromDataSource(source); + case ScopeInfoKind.capturedScope: + return new JsCapturedScope.readFromDataSource(source); + case ScopeInfoKind.capturedLoopScope: + return new JsCapturedLoopScope.readFromDataSource(source); + case ScopeInfoKind.closureRepresentationInfo: + return new KernelClosureClassInfo.readFromDataSource(source); + } + throw new UnsupportedError('Unexpected ScopeInfoKind $kind'); + } + + /// Serializes this [ScopeInfo] to [sink]. + void writeToDataSink(DataSink sink) { + throw new UnsupportedError('${runtimeType}.writeToDataSink'); + } + /// Convenience reference pointer to the element representing `this`. /// If this scope is not in an instance member, it will be null. Local get thisLocal => null; @@ -81,6 +121,21 @@ class ScopeInfo { class CapturedScope extends ScopeInfo { const CapturedScope(); + /// Deserializes a [CapturedScope] object from [source]. + factory CapturedScope.readFromDataSource(DataSource source) { + ScopeInfoKind kind = source.readEnum(ScopeInfoKind.values); + switch (kind) { + case ScopeInfoKind.scopeInfo: + case ScopeInfoKind.closureRepresentationInfo: + throw new UnsupportedError('Unexpected CapturedScope kind $kind'); + case ScopeInfoKind.capturedScope: + return new JsCapturedScope.readFromDataSource(source); + case ScopeInfoKind.capturedLoopScope: + return new JsCapturedLoopScope.readFromDataSource(source); + } + throw new UnsupportedError('Unexpected ScopeInfoKind $kind'); + } + /// If true, this closure accesses a variable that was defined in an outside /// scope and this variable gets modified at some point (sometimes we say that /// variable has been "captured"). In this situation, access to this variable @@ -113,6 +168,20 @@ class CapturedScope extends ScopeInfo { class CapturedLoopScope extends CapturedScope { const CapturedLoopScope(); + /// Deserializes a [CapturedLoopScope] object from [source]. + factory CapturedLoopScope.readFromDataSource(DataSource source) { + ScopeInfoKind kind = source.readEnum(ScopeInfoKind.values); + switch (kind) { + case ScopeInfoKind.scopeInfo: + case ScopeInfoKind.closureRepresentationInfo: + case ScopeInfoKind.capturedScope: + throw new UnsupportedError('Unexpected CapturedLoopScope kind $kind'); + case ScopeInfoKind.capturedLoopScope: + return new JsCapturedLoopScope.readFromDataSource(source); + } + throw new UnsupportedError('Unexpected ScopeInfoKind $kind'); + } + /// True if this loop scope declares in the first part of the loop /// `for (;...;...)` any variables that need to be boxed. bool get hasBoxedLoopVariables => false; @@ -166,6 +235,21 @@ class CapturedLoopScope extends CapturedScope { class ClosureRepresentationInfo extends ScopeInfo { const ClosureRepresentationInfo(); + /// Deserializes a [ClosureRepresentationInfo] object from [source]. + factory ClosureRepresentationInfo.readFromDataSource(DataSource source) { + ScopeInfoKind kind = source.readEnum(ScopeInfoKind.values); + switch (kind) { + case ScopeInfoKind.scopeInfo: + case ScopeInfoKind.capturedScope: + case ScopeInfoKind.capturedLoopScope: + throw new UnsupportedError( + 'Unexpected ClosureRepresentationInfo kind $kind'); + case ScopeInfoKind.closureRepresentationInfo: + return new KernelClosureClassInfo.readFromDataSource(source); + } + throw new UnsupportedError('Unexpected ScopeInfoKind $kind'); + } + /// The original local function before any translation. /// /// Will be null for methods. diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart index 7f23c57cc3888..9140c0496d851 100644 --- a/pkg/compiler/lib/src/compiler.dart +++ b/pkg/compiler/lib/src/compiler.dart @@ -360,6 +360,7 @@ abstract class Compiler { void generateJavaScriptCode( GlobalTypeInferenceResults globalInferenceResults) { JClosedWorld closedWorld = globalInferenceResults.closedWorld; + backendStrategy.registerJClosedWorld(closedWorld); FunctionEntity mainFunction = closedWorld.elementEnvironment.mainFunction; reporter.log('Compiling...'); phase = PHASE_COMPILING; @@ -402,7 +403,7 @@ abstract class Compiler { enqueuer.createCodegenEnqueuer(closedWorld, globalInferenceResults); _codegenWorldBuilder = codegenEnqueuer.worldBuilder; codegenEnqueuer.applyImpact(backend.onCodegenStart( - closedWorld, _codegenWorldBuilder, backendStrategy.sorter)); + closedWorld, _codegenWorldBuilder, closedWorld.sorter)); return codegenEnqueuer; } diff --git a/pkg/compiler/lib/src/deferred_load.dart b/pkg/compiler/lib/src/deferred_load.dart index 95f65bf247f29..7243452452db6 100644 --- a/pkg/compiler/lib/src/deferred_load.dart +++ b/pkg/compiler/lib/src/deferred_load.dart @@ -22,6 +22,7 @@ import 'elements/types.dart'; import 'elements/entities.dart'; import 'kernel/kelements.dart' show KLocalFunction; import 'library_loader.dart'; +import 'serialization/serialization.dart'; import 'universe/use.dart'; import 'universe/world_impact.dart' show ImpactUseCase, WorldImpact, WorldImpactVisitorImpl; @@ -1271,6 +1272,10 @@ class ConstantWorkItem extends WorkItem { // TODO(sigmund): consider moving here every piece of data used as a result of // deferred loading (including hunksToLoad, etc). class OutputUnitData { + /// Tag used for identifying serialized [OutputUnitData] objects in a + /// debugging data stream. + static const String tag = 'output-unit-data'; + final bool isProgramSplit; final OutputUnit mainOutputUnit; final Map _classToUnit; @@ -1288,6 +1293,7 @@ class OutputUnitData { this._constantToUnit, this.outputUnits); + // Creates J-world data from the K-world data. OutputUnitData.from( OutputUnitData other, Map Function( @@ -1305,9 +1311,90 @@ class OutputUnitData { convertMemberMap(other._memberToUnit, other._localFunctionToUnit), _classToUnit = convertClassMap(other._classToUnit, other._localFunctionToUnit), + // Local functions only make sense in the K-world model. _localFunctionToUnit = const {}, _constantToUnit = convertConstantMap(other._constantToUnit); + /// Deserializes an [OutputUnitData] object from [source]. + factory OutputUnitData.readFromDataSource(DataSource source) { + source.begin(tag); + bool isProgramSplit = source.readBool(); + List imports = source.readList(() { + String name = source.readString(); + Uri uri = source.readUri(); + Uri enclosingLibraryUri = source.readUri(); + bool isDeferred = source.readBool(); + return new ImportEntity(isDeferred, name, uri, enclosingLibraryUri); + }); + List outputUnits = source.readList(() { + bool isMainOutput = source.readBool(); + String name = source.readString(); + Set importSet = source.readList(() { + return imports[source.readInt()]; + }).toSet(); + return new OutputUnit(isMainOutput, name, importSet); + }); + OutputUnit mainOutputUnit = outputUnits[source.readInt()]; + + Map classToUnit = source.readClassMap(() { + return outputUnits[source.readInt()]; + }); + Map memberToUnit = source.readMemberMap(() { + return outputUnits[source.readInt()]; + }); + Map constantToUnit = source.readConstantMap(() { + return outputUnits[source.readInt()]; + }); + source.end(tag); + return new OutputUnitData( + isProgramSplit, + mainOutputUnit, + classToUnit, + memberToUnit, + // Local functions only make sense in the K-world model. + const {}, + constantToUnit, + outputUnits); + } + + /// Serializes this [OutputUnitData] to [sink]. + void writeToDataSink(DataSink sink) { + sink.begin(tag); + sink.writeBool(isProgramSplit); + Map importIndex = {}; + for (OutputUnit outputUnit in outputUnits) { + for (ImportEntity import in outputUnit._imports) { + importIndex[import] ??= importIndex.length; + } + } + sink.writeList(importIndex.keys, (ImportEntity import) { + sink.writeString(import.name); + sink.writeUri(import.uri); + sink.writeUri(import.enclosingLibraryUri); + sink.writeBool(import.isDeferred); + }); + Map outputUnitIndices = {}; + sink.writeList(outputUnits, (OutputUnit outputUnit) { + outputUnitIndices[outputUnit] = outputUnitIndices.length; + sink.writeBool(outputUnit.isMainOutput); + sink.writeString(outputUnit.name); + sink.writeList(outputUnit._imports, (ImportEntity import) { + sink.writeInt(importIndex[import]); + }); + }); + sink.writeInt(outputUnitIndices[mainOutputUnit]); + sink.writeClassMap(_classToUnit, (OutputUnit outputUnit) { + sink.writeInt(outputUnitIndices[outputUnit]); + }); + sink.writeMemberMap(_memberToUnit, (OutputUnit outputUnit) { + sink.writeInt(outputUnitIndices[outputUnit]); + }); + sink.writeConstantMap(_constantToUnit, (OutputUnit outputUnit) { + sink.writeInt(outputUnitIndices[outputUnit]); + }); + sink.end(tag); + } + /// Returns the [OutputUnit] where [cls] belongs. OutputUnit outputUnitForClass(ClassEntity cls) { if (!isProgramSplit) return mainOutputUnit; diff --git a/pkg/compiler/lib/src/elements/entities.dart b/pkg/compiler/lib/src/elements/entities.dart index 2304b6212a0e8..0adff08cc18dc 100644 --- a/pkg/compiler/lib/src/elements/entities.dart +++ b/pkg/compiler/lib/src/elements/entities.dart @@ -7,6 +7,7 @@ library entities; import 'package:front_end/src/api_unstable/dart2js.dart' show AsyncModifier; import '../common.dart'; +import '../serialization/serialization.dart'; import '../universe/call_structure.dart' show CallStructure; import '../util/util.dart'; import 'names.dart'; @@ -258,6 +259,10 @@ abstract class Local extends Entity {} /// The structure of function parameters. class ParameterStructure { + /// Tag used for identifying serialized [ParameterStructure] objects in a + /// debugging data stream. + static const String tag = 'parameter-structure'; + /// The number of required (positional) parameters. final int requiredParameters; @@ -285,6 +290,28 @@ class ParameterStructure { type.typeVariables.length); } + /// Deserializes a [ParameterStructure] object from [source]. + factory ParameterStructure.readFromDataSource(DataSource source) { + source.begin(tag); + int requiredParameters = source.readInt(); + int positionalParameters = source.readInt(); + List namedParameters = source.readStrings(); + int typeParameters = source.readInt(); + source.end(tag); + return new ParameterStructure(requiredParameters, positionalParameters, + namedParameters, typeParameters); + } + + /// Serializes this [ParameterStructure] to [sink]. + void writeToDataSink(DataSink sink) { + sink.begin(tag); + sink.writeInt(requiredParameters); + sink.writeInt(positionalParameters); + sink.writeStrings(namedParameters); + sink.writeInt(typeParameters); + sink.end(tag); + } + /// The number of optional parameters (positional or named). int get optionalParameters => positionalParameters - requiredParameters + namedParameters.length; diff --git a/pkg/compiler/lib/src/elements/indexed.dart b/pkg/compiler/lib/src/elements/indexed.dart index f20a4ffabf419..56defb010664e 100644 --- a/pkg/compiler/lib/src/elements/indexed.dart +++ b/pkg/compiler/lib/src/elements/indexed.dart @@ -53,12 +53,16 @@ abstract class IndexedLocal extends _Indexed implements Local { /// Base implementation for an index based map of entities of type [E]. abstract class EntityMapBase { + int _size = 0; List _list = []; /// Returns the [index]th entity in the map. E getEntity(int index) => _list[index]; - /// Returns the number entities in the map. + /// Returns the number of non-null entities in the map. + int get size => _size; + + /// Returns the number (null and non-null) entities in the map. int get length => _list.length; } @@ -73,8 +77,26 @@ class EntityMap extends EntityMapBase { assert(entity._index == null); entity._index = _list.length; _list.add(entity); + _size++; return entity; } + + /// Registers a new [entity] by the given [index]. + E0 registerByIndex(int index, E0 entity) { + assert(index >= _list.length); + _list.length = index; + return register(entity); + } + + /// Calls [f] for each non-null entity. + void forEach(void f(E0 entity)) { + for (int index = 0; index < _list.length; index++) { + E entity = _list[index]; + if (entity != null) { + f(entity); + } + } + } } /// Base implementation of an index based map of entities of type [E] with a @@ -86,7 +108,7 @@ abstract class EntityDataMapBase /// Returns the data object stored for the [index]th entity. D getData(E entity) { int index = entity._index; - if (index < length && index >= _data.length) { + if (index < _list.length && index >= _data.length) { throw new StateError( 'Data is in the process of being created for ${_list[index]}.'); } @@ -111,12 +133,39 @@ class EntityDataMap extends EntityDataMapBase { E0 register(E0 entity, D0 data) { assert(entity != null); assert(entity._index == null); + assert( + _list.length == _data.length, + 'Data list length ${_data.length} inconsistent ' + 'with entity list length ${_list.length}.'); entity._index = _list.length; _list.add(entity); + _size++; assert(data != null); _data.add(data); return entity; } + + /// Registers a new [entity] with an associated [data] object by the given + /// [index]. + E0 registerByIndex( + int index, E0 entity, D0 data) { + assert(index >= _list.length); + _list.length = _data.length = index; + return register(entity, data); + } + + /// Calls [f] for each non-null entity with its corresponding data object. + void forEach(void f(E0 entity, D0 data)) { + if (_list.length != _data.length) { + throw new StateError('Data is in the process of being created.'); + } + for (int index = 0; index < _list.length; index++) { + E entity = _list[index]; + if (entity != null) { + f(entity, _data[index]); + } + } + } } /// Base implementation for an index based of entities of type [E] with a @@ -128,7 +177,7 @@ abstract class EntityDataEnvMapBase /// Returns the environment object stored for the [index]th entity. V getEnv(E entity) { int index = entity._index; - if (index < length && index >= _env.length) { + if (index < _list.length && index >= _env.length) { throw new StateError( 'Env is in the process of being created for ${_list[index]}.'); } @@ -149,12 +198,48 @@ class EntityDataEnvMap E0 entity, D0 data, V0 env) { assert(entity != null); assert(entity._index == null); + assert( + _list.length == _data.length, + 'Data list length ${_data.length} inconsistent ' + 'with entity list length ${_list.length}.'); + assert( + _list.length == _env.length, + 'Env list length ${_env.length} inconsistent ' + 'with entity list length ${_list.length}.'); entity._index = _list.length; _list.add(entity); + _size++; assert(data != null); _data.add(data); assert(env != null); _env.add(env); return entity; } + + /// Registers a new [entity] with an associated [data] object and environment + /// [env] by the given [index]. + E0 registerByIndex( + int index, E0 entity, D0 data, V0 env) { + assert(index >= _list.length); + _list.length = _data.length = _env.length = index; + return register(entity, data, env); + } + + /// Calls [f] for each non-null entity with its corresponding data object and + /// environment. + void forEach( + void f(E0 entity, D0 data, V0 env)) { + if (_list.length != _data.length) { + throw new StateError('Data is in the process of being created.'); + } + if (_list.length != _env.length) { + throw new StateError('Env is in the process of being created.'); + } + for (int index = 0; index < _list.length; index++) { + E entity = _list[index]; + if (entity != null) { + f(entity, _data[index], _env[index]); + } + } + } } diff --git a/pkg/compiler/lib/src/inferrer/inferrer_engine.dart b/pkg/compiler/lib/src/inferrer/inferrer_engine.dart index c60898325a6da..5ce0f07cd5b92 100644 --- a/pkg/compiler/lib/src/inferrer/inferrer_engine.dart +++ b/pkg/compiler/lib/src/inferrer/inferrer_engine.dart @@ -16,12 +16,12 @@ import '../elements/names.dart'; import '../elements/types.dart'; import '../js_backend/inferred_data.dart'; import '../js_backend/no_such_method_registry.dart'; -import '../js_emitter/sorter.dart'; import '../js_model/element_map.dart'; import '../js_model/js_strategy.dart'; import '../js_model/locals.dart'; import '../native/behavior.dart' as native; import '../options.dart'; +import '../serialization/serialization.dart'; import '../types/abstract_value_domain.dart'; import '../types/types.dart'; import '../universe/call_structure.dart'; @@ -288,8 +288,6 @@ class InferrerEngineImpl extends InferrerEngine { final NoSuchMethodRegistry noSuchMethodRegistry; - final Sorter sorter; - InferrerEngineImpl( this.options, this.progress, @@ -298,7 +296,6 @@ class InferrerEngineImpl extends InferrerEngine { this.closedWorld, this.noSuchMethodRegistry, this.mainElement, - this.sorter, this.inferredDataBuilder) : this.types = new TypeSystem( closedWorld, new KernelTypeSystemStrategy(closedWorld)); @@ -1365,6 +1362,10 @@ class KernelTypeSystemStrategy implements TypeSystemStrategy { class KernelGlobalTypeInferenceElementData implements GlobalTypeInferenceElementData { + /// Tag used for identifying serialized [GlobalTypeInferenceElementData] + /// objects in a debugging data stream. + static const String tag = 'global-type-inference-element-data'; + // TODO(johnniwinther): Rename this together with [typeOfSend]. Map _sendMap; @@ -1372,6 +1373,86 @@ class KernelGlobalTypeInferenceElementData Map _currentMap; Map _moveNextMap; + KernelGlobalTypeInferenceElementData(); + + KernelGlobalTypeInferenceElementData.internal( + this._sendMap, this._iteratorMap, this._currentMap, this._moveNextMap); + + /// Deserializes a [GlobalTypeInferenceElementData] object from [source]. + factory KernelGlobalTypeInferenceElementData.readFromDataSource( + DataSource source, AbstractValueDomain abstractValueDomain) { + source.begin(tag); + Map sendMap = source.readTreeNodeMap( + () => abstractValueDomain.readAbstractValueFromDataSource(source), + emptyAsNull: true); + Map iteratorMap = source.readTreeNodeMap( + () => abstractValueDomain.readAbstractValueFromDataSource(source), + emptyAsNull: true); + Map currentMap = source.readTreeNodeMap( + () => abstractValueDomain.readAbstractValueFromDataSource(source), + emptyAsNull: true); + Map moveNextMap = source.readTreeNodeMap( + () => abstractValueDomain.readAbstractValueFromDataSource(source), + emptyAsNull: true); + source.end(tag); + return new KernelGlobalTypeInferenceElementData.internal( + sendMap, iteratorMap, currentMap, moveNextMap); + } + + /// Serializes this [GlobalTypeInferenceElementData] to [sink]. + void writeToDataSink(DataSink sink, AbstractValueDomain abstractValueDomain) { + sink.begin(tag); + sink.writeTreeNodeMap( + _sendMap, + (AbstractValue value) => + abstractValueDomain.writeAbstractValueToDataSink(sink, value), + allowNull: true); + sink.writeTreeNodeMap( + _iteratorMap, + (AbstractValue value) => + abstractValueDomain.writeAbstractValueToDataSink(sink, value), + allowNull: true); + sink.writeTreeNodeMap( + _currentMap, + (AbstractValue value) => + abstractValueDomain.writeAbstractValueToDataSink(sink, value), + allowNull: true); + sink.writeTreeNodeMap( + _moveNextMap, + (AbstractValue value) => + abstractValueDomain.writeAbstractValueToDataSink(sink, value), + allowNull: true); + sink.end(tag); + } + + @override + void compress() { + if (_sendMap != null) { + _sendMap.removeWhere(_mapsToNull); + if (_sendMap.isEmpty) { + _sendMap = null; + } + } + if (_iteratorMap != null) { + _iteratorMap.removeWhere(_mapsToNull); + if (_iteratorMap.isEmpty) { + _iteratorMap = null; + } + } + if (_currentMap != null) { + _currentMap.removeWhere(_mapsToNull); + if (_currentMap.isEmpty) { + _currentMap = null; + } + } + if (_moveNextMap != null) { + _moveNextMap.removeWhere(_mapsToNull); + if (_moveNextMap.isEmpty) { + _moveNextMap = null; + } + } + } + @override AbstractValue typeOfSend(ir.TreeNode node) { if (_sendMap == null) return null; @@ -1429,3 +1510,5 @@ class KernelGlobalTypeInferenceElementData return _sendMap[node]; } } + +bool _mapsToNull(ir.TreeNode node, AbstractValue value) => value == null; diff --git a/pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart b/pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart index 827f0f4a7e98b..c8c20c2eaf3bf 100644 --- a/pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart +++ b/pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart @@ -79,7 +79,6 @@ class TypeGraphInferrer implements TypesInferrer { closedWorld, _compiler.backend.noSuchMethodRegistry, main, - _compiler.backendStrategy.sorter, _inferredDataBuilder); } @@ -109,6 +108,7 @@ class TypeGraphInferrer implements TypesInferrer { void createMemberResults( MemberEntity member, MemberTypeInformation typeInformation) { GlobalTypeInferenceElementData data = inferrer.dataOfMember(member); + data.compress(); bool isJsInterop = closedWorld.nativeData.isJsInteropMember(member); AbstractValue returnType; diff --git a/pkg/compiler/lib/src/inferrer/typemasks/container_type_mask.dart b/pkg/compiler/lib/src/inferrer/typemasks/container_type_mask.dart index a0280dc14ee38..4b27265402fef 100644 --- a/pkg/compiler/lib/src/inferrer/typemasks/container_type_mask.dart +++ b/pkg/compiler/lib/src/inferrer/typemasks/container_type_mask.dart @@ -8,6 +8,10 @@ part of masks; /// site of a container (currently only List) that will get specialized /// once the [TypeGraphInferrer] phase finds an element type for it. class ContainerTypeMask extends AllocationTypeMask { + /// Tag used for identifying serialized [ContainerTypeMask] objects in a + /// debugging data stream. + static const String tag = 'container-type-mask'; + final TypeMask forwardTo; // The [Node] where this type mask was created. @@ -25,6 +29,32 @@ class ContainerTypeMask extends AllocationTypeMask { ContainerTypeMask(this.forwardTo, this.allocationNode, this.allocationElement, this.elementType, this.length); + /// Deserializes a [ContainerTypeMask] object from [source]. + factory ContainerTypeMask.readFromDataSource( + DataSource source, JClosedWorld closedWorld) { + source.begin(tag); + TypeMask forwardTo = new TypeMask.readFromDataSource(source, closedWorld); + ir.TreeNode allocationNode = source.readTreeNodeOrNull(); + MemberEntity allocationElement = source.readMemberOrNull(); + TypeMask elementType = new TypeMask.readFromDataSource(source, closedWorld); + int length = source.readIntOrNull(); + source.end(tag); + return new ContainerTypeMask( + forwardTo, allocationNode, allocationElement, elementType, length); + } + + /// Serializes this [ContainerTypeMask] to [sink]. + void writeToDataSink(DataSink sink) { + sink.writeEnum(TypeMaskKind.container); + sink.begin(tag); + forwardTo.writeToDataSink(sink); + sink.writeTreeNodeOrNull(allocationNode); + sink.writeMemberOrNull(allocationElement); + elementType.writeToDataSink(sink); + sink.writeIntOrNull(length); + sink.end(tag); + } + TypeMask nullable() { return isNullable ? this diff --git a/pkg/compiler/lib/src/inferrer/typemasks/dictionary_type_mask.dart b/pkg/compiler/lib/src/inferrer/typemasks/dictionary_type_mask.dart index c6e3950273277..9a64845969fb6 100644 --- a/pkg/compiler/lib/src/inferrer/typemasks/dictionary_type_mask.dart +++ b/pkg/compiler/lib/src/inferrer/typemasks/dictionary_type_mask.dart @@ -13,6 +13,10 @@ part of masks; * the more general [MapTypeMask] is used. */ class DictionaryTypeMask extends MapTypeMask { + /// Tag used for identifying serialized [DictionaryTypeMask] objects in a + /// debugging data stream. + static const String tag = 'dictionary-type-mask'; + // The underlying key/value map of this dictionary. final Map _typeMap; @@ -25,6 +29,38 @@ class DictionaryTypeMask extends MapTypeMask { this._typeMap) : super(forwardTo, allocationNode, allocationElement, keyType, valueType); + /// Deserializes a [DictionaryTypeMask] object from [source]. + factory DictionaryTypeMask.readFromDataSource( + DataSource source, JClosedWorld closedWorld) { + source.begin(tag); + TypeMask forwardTo = new TypeMask.readFromDataSource(source, closedWorld); + ir.TreeNode allocationNode = source.readTreeNode(); + MemberEntity allocationElement = source.readMember(); + TypeMask keyType = new TypeMask.readFromDataSource(source, closedWorld); + TypeMask valueType = new TypeMask.readFromDataSource(source, closedWorld); + Map typeMap = source.readStringMap( + () => new TypeMask.readFromDataSource(source, closedWorld)); + source.end(tag); + return new DictionaryTypeMask(forwardTo, allocationNode, allocationElement, + keyType, valueType, typeMap); + } + + /// Serializes this [DictionaryTypeMask] to [sink]. + void writeToDataSink(DataSink sink) { + sink.writeEnum(TypeMaskKind.dictionary); + sink.begin(tag); + forwardTo.writeToDataSink(sink); + sink.writeTreeNode(allocationNode); + sink.writeMember(allocationElement); + valueType.writeToDataSink(sink); + keyType.writeToDataSink(sink); + sink.writeStringMap(_typeMap, (AbstractValue value) { + TypeMask typeMask = value; + typeMask.writeToDataSink(sink); + }); + sink.end(tag); + } + TypeMask nullable() { return isNullable ? this diff --git a/pkg/compiler/lib/src/inferrer/typemasks/flat_type_mask.dart b/pkg/compiler/lib/src/inferrer/typemasks/flat_type_mask.dart index 20150d5ea89f8..c74600a564a5a 100644 --- a/pkg/compiler/lib/src/inferrer/typemasks/flat_type_mask.dart +++ b/pkg/compiler/lib/src/inferrer/typemasks/flat_type_mask.dart @@ -9,6 +9,10 @@ part of masks; * base type. */ class FlatTypeMask implements TypeMask { + /// Tag used for identifying serialized [FlatTypeMask] objects in a + /// debugging data stream. + static const String tag = 'flat-type-mask'; + static const int EMPTY = 0; static const int EXACT = 1; static const int SUBCLASS = 2; @@ -39,10 +43,6 @@ class FlatTypeMask implements TypeMask { FlatTypeMask.nonNullSubtype(ClassEntity base) : this.internal(base, SUBTYPE << 1); - ClassQuery get _classQuery => isExact - ? ClassQuery.EXACT - : (isSubclass ? ClassQuery.SUBCLASS : ClassQuery.SUBTYPE); - FlatTypeMask.internal(this.base, this.flags); /** @@ -69,6 +69,31 @@ class FlatTypeMask implements TypeMask { base, flags, () => new FlatTypeMask.internal(base, flags)); } + /// Deserializes a [FlatTypeMask] object from [source]. + factory FlatTypeMask.readFromDataSource( + DataSource source, JClosedWorld closedWorld) { + source.begin(tag); + ClassEntity base = source.readClassOrNull(); + int flags = source.readInt(); + source.end(tag); + CommonMasks commonMasks = closedWorld.abstractValueDomain; + return commonMasks.getCachedMask( + base, flags, () => new FlatTypeMask.internal(base, flags)); + } + + /// Serializes this [FlatTypeMask] to [sink]. + void writeToDataSink(DataSink sink) { + sink.writeEnum(TypeMaskKind.flat); + sink.begin(tag); + sink.writeClassOrNull(base); + sink.writeInt(flags); + sink.end(tag); + } + + ClassQuery get _classQuery => isExact + ? ClassQuery.EXACT + : (isSubclass ? ClassQuery.SUBCLASS : ClassQuery.SUBTYPE); + bool get isEmpty => isEmptyOrNull && !isNullable; bool get isNull => isEmptyOrNull && isNullable; bool get isEmptyOrNull => (flags >> 1) == EMPTY; diff --git a/pkg/compiler/lib/src/inferrer/typemasks/map_type_mask.dart b/pkg/compiler/lib/src/inferrer/typemasks/map_type_mask.dart index f7b286674c461..5e6e2fb086056 100644 --- a/pkg/compiler/lib/src/inferrer/typemasks/map_type_mask.dart +++ b/pkg/compiler/lib/src/inferrer/typemasks/map_type_mask.dart @@ -10,6 +10,10 @@ part of masks; * once the [TypeGraphInferrer] phase finds a key and/or value type for it. */ class MapTypeMask extends AllocationTypeMask { + /// Tag used for identifying serialized [MapTypeMask] objects in a + /// debugging data stream. + static const String tag = 'map-type-mask'; + final TypeMask forwardTo; // The [Node] where this type mask was created. @@ -27,6 +31,32 @@ class MapTypeMask extends AllocationTypeMask { MapTypeMask(this.forwardTo, this.allocationNode, this.allocationElement, this.keyType, this.valueType); + /// Deserializes a [MapTypeMask] object from [source]. + factory MapTypeMask.readFromDataSource( + DataSource source, JClosedWorld closedWorld) { + source.begin(tag); + TypeMask forwardTo = new TypeMask.readFromDataSource(source, closedWorld); + ir.TreeNode allocationNode = source.readTreeNode(); + MemberEntity allocationElement = source.readMember(); + TypeMask keyType = new TypeMask.readFromDataSource(source, closedWorld); + TypeMask valueType = new TypeMask.readFromDataSource(source, closedWorld); + source.end(tag); + return new MapTypeMask( + forwardTo, allocationNode, allocationElement, keyType, valueType); + } + + /// Serializes this [MapTypeMask] to [sink]. + void writeToDataSink(DataSink sink) { + sink.writeEnum(TypeMaskKind.map); + sink.begin(tag); + forwardTo.writeToDataSink(sink); + sink.writeTreeNode(allocationNode); + sink.writeMember(allocationElement); + valueType.writeToDataSink(sink); + keyType.writeToDataSink(sink); + sink.end(tag); + } + TypeMask nullable() { return isNullable ? this diff --git a/pkg/compiler/lib/src/inferrer/typemasks/masks.dart b/pkg/compiler/lib/src/inferrer/typemasks/masks.dart index 5b0e9209d38e6..6987117953ec0 100644 --- a/pkg/compiler/lib/src/inferrer/typemasks/masks.dart +++ b/pkg/compiler/lib/src/inferrer/typemasks/masks.dart @@ -8,8 +8,9 @@ import 'package:kernel/ast.dart' as ir; import '../../common.dart'; import '../../common_elements.dart' show CommonElements; -import '../../constants/values.dart' show ConstantValue, PrimitiveConstantValue; +import '../../constants/values.dart'; import '../../elements/entities.dart'; +import '../../serialization/serialization.dart'; import '../../types/abstract_value_domain.dart'; import '../../universe/class_hierarchy.dart'; import '../../universe/selector.dart' show Selector; @@ -718,6 +719,16 @@ class CommonMasks implements AbstractValueDomain { String getCompactText(AbstractValue value) { return formatType(value); } + + @override + TypeMask readAbstractValueFromDataSource(DataSource source) { + return new TypeMask.readFromDataSource(source, _closedWorld); + } + + @override + void writeAbstractValueToDataSink(DataSink sink, covariant TypeMask value) { + value.writeToDataSink(sink); + } } /// Convert the given TypeMask to a compact string format. diff --git a/pkg/compiler/lib/src/inferrer/typemasks/type_mask.dart b/pkg/compiler/lib/src/inferrer/typemasks/type_mask.dart index 225f21c85bf9b..a08d32009b53c 100644 --- a/pkg/compiler/lib/src/inferrer/typemasks/type_mask.dart +++ b/pkg/compiler/lib/src/inferrer/typemasks/type_mask.dart @@ -91,6 +91,16 @@ class TypeMaskSelectorStrategy implements SelectorConstraintsStrategy { } } +/// Enum used for identifying [TypeMask] subclasses in serialization. +enum TypeMaskKind { + flat, + union, + container, + map, + dictionary, + value, +} + /** * A type mask represents a set of contained classes, but the * operations on it are not guaranteed to be precise and they may @@ -209,6 +219,30 @@ abstract class TypeMask implements AbstractValue { return UnionTypeMask.unionOf(masks, closedWorld); } + /// Deserializes a [TypeMask] object from [source]. + factory TypeMask.readFromDataSource( + DataSource source, JClosedWorld closedWorld) { + TypeMaskKind kind = source.readEnum(TypeMaskKind.values); + switch (kind) { + case TypeMaskKind.flat: + return new FlatTypeMask.readFromDataSource(source, closedWorld); + case TypeMaskKind.union: + return new UnionTypeMask.readFromDataSource(source, closedWorld); + case TypeMaskKind.container: + return new ContainerTypeMask.readFromDataSource(source, closedWorld); + case TypeMaskKind.map: + return new MapTypeMask.readFromDataSource(source, closedWorld); + case TypeMaskKind.dictionary: + return new DictionaryTypeMask.readFromDataSource(source, closedWorld); + case TypeMaskKind.value: + return new ValueTypeMask.readFromDataSource(source, closedWorld); + } + throw new UnsupportedError("Unexpected TypeMaskKind $kind."); + } + + /// Serializes this [TypeMask] to [sink]. + void writeToDataSink(DataSink sink); + /** * If [mask] is forwarding, returns the first non-forwarding [TypeMask] in * [mask]'s forwarding chain. diff --git a/pkg/compiler/lib/src/inferrer/typemasks/union_type_mask.dart b/pkg/compiler/lib/src/inferrer/typemasks/union_type_mask.dart index 0da519ce214e9..aba7ea6577ee1 100644 --- a/pkg/compiler/lib/src/inferrer/typemasks/union_type_mask.dart +++ b/pkg/compiler/lib/src/inferrer/typemasks/union_type_mask.dart @@ -5,7 +5,9 @@ part of masks; class UnionTypeMask implements TypeMask { - final Iterable disjointMasks; + /// Tag used for identifying serialized [UnionTypeMask] objects in a + /// debugging data stream. + static const String tag = 'union-type-mask'; static const int MAX_UNION_LENGTH = 4; @@ -14,11 +16,32 @@ class UnionTypeMask implements TypeMask { // helpful in debugging. static const bool PERFORM_EXTRA_CONTAINS_CHECK = false; + final Iterable disjointMasks; + UnionTypeMask._internal(this.disjointMasks) { assert(disjointMasks.length > 1); assert(disjointMasks.every((TypeMask mask) => !mask.isUnion)); } + /// Deserializes a [UnionTypeMask] object from [source]. + factory UnionTypeMask.readFromDataSource( + DataSource source, JClosedWorld closedWorld) { + source.begin(tag); + List disjointMasks = source + .readList(() => new TypeMask.readFromDataSource(source, closedWorld)); + source.end(tag); + return new UnionTypeMask._internal(disjointMasks); + } + + /// Serializes this [UnionTypeMask] to [sink]. + void writeToDataSink(DataSink sink) { + sink.writeEnum(TypeMaskKind.union); + sink.begin(tag); + sink.writeList( + disjointMasks, (FlatTypeMask mask) => mask.writeToDataSink(sink)); + sink.end(tag); + } + static TypeMask unionOf(Iterable masks, JClosedWorld closedWorld) { assert( masks.every((mask) => TypeMask.assertIsNormalized(mask, closedWorld))); diff --git a/pkg/compiler/lib/src/inferrer/typemasks/value_type_mask.dart b/pkg/compiler/lib/src/inferrer/typemasks/value_type_mask.dart index ebeda525f9c97..45c1300604cf3 100644 --- a/pkg/compiler/lib/src/inferrer/typemasks/value_type_mask.dart +++ b/pkg/compiler/lib/src/inferrer/typemasks/value_type_mask.dart @@ -5,11 +5,34 @@ part of masks; class ValueTypeMask extends ForwardingTypeMask { + /// Tag used for identifying serialized [ValueTypeMask] objects in a + /// debugging data stream. + static const String tag = 'value-type-mask'; + final TypeMask forwardTo; final PrimitiveConstantValue value; ValueTypeMask(this.forwardTo, this.value); + /// Deserializes a [ValueTypeMask] object from [source]. + factory ValueTypeMask.readFromDataSource( + DataSource source, JClosedWorld closedWorld) { + source.begin(tag); + TypeMask forwardTo = new TypeMask.readFromDataSource(source, closedWorld); + ConstantValue constant = source.readConstant(); + source.end(tag); + return new ValueTypeMask(forwardTo, constant); + } + + /// Serializes this [ValueTypeMask] to [sink]. + void writeToDataSink(DataSink sink) { + sink.writeEnum(TypeMaskKind.value); + sink.begin(tag); + forwardTo.writeToDataSink(sink); + sink.writeConstant(value); + sink.end(tag); + } + TypeMask nullable() { return isNullable ? this : new ValueTypeMask(forwardTo.nullable(), value); } diff --git a/pkg/compiler/lib/src/js_backend/allocator_analysis.dart b/pkg/compiler/lib/src/js_backend/allocator_analysis.dart index 74153222bd213..6e15c77393e46 100644 --- a/pkg/compiler/lib/src/js_backend/allocator_analysis.dart +++ b/pkg/compiler/lib/src/js_backend/allocator_analysis.dart @@ -10,6 +10,7 @@ import '../kernel/element_map.dart'; import '../kernel/kernel_strategy.dart'; import '../kernel/kelements.dart' show KClass, KField; import '../options.dart'; +import '../serialization/serialization.dart'; abstract class AllocatorAnalysis {} @@ -70,6 +71,10 @@ class KAllocatorAnalysis implements AllocatorAnalysis { } class JAllocatorAnalysis implements AllocatorAnalysis { + /// Tag used for identifying serialized [JAllocatorAnalysis] objects in a + /// debugging data stream. + static const String tag = 'allocator-analysis'; + // --csp and --fast-startup have different constraints to the generated code. final CompilerOptions _options; final Map _fixedInitializers = @@ -77,6 +82,34 @@ class JAllocatorAnalysis implements AllocatorAnalysis { JAllocatorAnalysis._(this._options); + /// Deserializes a [JAllocatorAnalysis] object from [source]. + factory JAllocatorAnalysis.readFromDataSource( + DataSource source, CompilerOptions options) { + source.begin(tag); + JAllocatorAnalysis analysis = new JAllocatorAnalysis._(options); + int fieldCount = source.readInt(); + for (int i = 0; i < fieldCount; i++) { + JField field = source.readMember(); + // TODO(sra): Deserialize constant, when non-null is supported. + ConstantValue value = const NullConstantValue(); + analysis._fixedInitializers[field] = value; + } + source.end(tag); + return analysis; + } + + /// Serializes this [JAllocatorAnalysis] to [sink]. + void writeToDataSink(DataSink sink) { + sink.begin(tag); + sink.writeInt(_fixedInitializers.length); + _fixedInitializers.forEach((JField field, ConstantValue value) { + sink.writeMember(field); + // TODO(sra): Serialize constant, when non-null is supported. + assert(value.isNull); + }); + sink.end(tag); + } + static JAllocatorAnalysis from(KAllocatorAnalysis kAnalysis, JsToFrontendMap map, CompilerOptions options) { var result = new JAllocatorAnalysis._(options); diff --git a/pkg/compiler/lib/src/js_backend/annotations.dart b/pkg/compiler/lib/src/js_backend/annotations.dart index 5f66d8f301385..025f6d8587615 100644 --- a/pkg/compiler/lib/src/js_backend/annotations.dart +++ b/pkg/compiler/lib/src/js_backend/annotations.dart @@ -10,6 +10,7 @@ import '../diagnostics/diagnostic_listener.dart'; import '../diagnostics/messages.dart'; import '../elements/entities.dart'; import '../native/native.dart' as native; +import '../serialization/serialization.dart'; /// Returns `true` if parameter and returns types should be trusted for /// [element]. @@ -179,6 +180,13 @@ AnnotationsData processAnnotations( } abstract class AnnotationsData { + /// Deserializes a [AnnotationsData] object from [source]. + factory AnnotationsData.readFromDataSource(DataSource source) = + AnnotationsDataImpl.readFromDataSource; + + /// Serializes this [AnnotationsData] to [sink]. + void writeToDataSink(DataSink sink); + /// Functions with a `@NoInline()` or `@noInline` annotation. Iterable get nonInlinableFunctions; @@ -199,6 +207,10 @@ abstract class AnnotationsData { } class AnnotationsDataImpl implements AnnotationsData { + /// Tag used for identifying serialized [AnnotationsData] objects in a + /// debugging data stream. + static const String tag = 'annotations-data'; + final Iterable nonInlinableFunctions; final Iterable tryInlineFunctions; final Iterable cannotThrowFunctions; @@ -213,6 +225,35 @@ class AnnotationsDataImpl implements AnnotationsData { this.sideEffectFreeFunctions, this.trustTypeAnnotationsMembers, this.assumeDynamicMembers); + + factory AnnotationsDataImpl.readFromDataSource(DataSource source) { + source.begin(tag); + Iterable nonInlinableFunctions = source.readMembers(); + Iterable tryInlineFunctions = source.readMembers(); + Iterable cannotThrowFunctions = source.readMembers(); + Iterable sideEffectFreeFunctions = source.readMembers(); + Iterable trustTypeAnnotationsMembers = source.readMembers(); + Iterable assumeDynamicMembers = source.readMembers(); + source.end(tag); + return new AnnotationsDataImpl( + nonInlinableFunctions, + tryInlineFunctions, + cannotThrowFunctions, + sideEffectFreeFunctions, + trustTypeAnnotationsMembers, + assumeDynamicMembers); + } + + void writeToDataSink(DataSink sink) { + sink.begin(tag); + sink.writeMembers(nonInlinableFunctions); + sink.writeMembers(tryInlineFunctions); + sink.writeMembers(cannotThrowFunctions); + sink.writeMembers(sideEffectFreeFunctions); + sink.writeMembers(trustTypeAnnotationsMembers); + sink.writeMembers(assumeDynamicMembers); + sink.end(tag); + } } class AnnotationsDataBuilder implements AnnotationsData { @@ -255,4 +296,8 @@ class AnnotationsDataBuilder implements AnnotationsData { Iterable get trustTypeAnnotationsMembers => _trustTypeAnnotationsMembers; Iterable get assumeDynamicMembers => _assumeDynamicMembers; + + void writeToDataSink(DataSink sink) { + throw new UnsupportedError('AnnotationsDataBuilder.writeToDataSink'); + } } diff --git a/pkg/compiler/lib/src/js_backend/backend_usage.dart b/pkg/compiler/lib/src/js_backend/backend_usage.dart index b3e02ace0dcec..efe00d97f8cb9 100644 --- a/pkg/compiler/lib/src/js_backend/backend_usage.dart +++ b/pkg/compiler/lib/src/js_backend/backend_usage.dart @@ -7,11 +7,19 @@ import '../common_elements.dart'; import '../elements/entities.dart'; import '../elements/types.dart'; import '../frontend_strategy.dart'; +import '../serialization/serialization.dart'; import '../universe/feature.dart'; import '../util/util.dart' show Setlet; import 'backend_impact.dart'; abstract class BackendUsage { + /// Deserializes a [BackendUsage] object from [source]. + factory BackendUsage.readFromDataSource(DataSource source) = + BackendUsageImpl.readFromDataSource; + + /// Serializes this [BackendUsage] to [sink]. + void writeToDataSink(DataSink sink); + bool needToInitializeIsolateAffinityTag; bool needToInitializeDispatchProperty; @@ -262,6 +270,10 @@ class BackendUsageBuilderImpl implements BackendUsageBuilder { } class BackendUsageImpl implements BackendUsage { + /// Tag used for identifying serialized [BackendUsage] objects in a + /// debugging data stream. + static const String tag = 'backend-usage'; + // TODO(johnniwinther): Remove the need for these. final Set _globalFunctionDependencies; final Set _globalClassDependencies; @@ -307,6 +319,61 @@ class BackendUsageImpl implements BackendUsage { this._helperClassesUsed = helperClassesUsed, this._runtimeTypeUses = runtimeTypeUses; + factory BackendUsageImpl.readFromDataSource(DataSource source) { + source.begin(tag); + Set globalFunctionDependencies = + source.readMembers().toSet(); + Set globalClassDependencies = source.readClasses().toSet(); + Set helperFunctionsUsed = + source.readMembers().toSet(); + Set helperClassesUsed = source.readClasses().toSet(); + Set runtimeTypeUses = source.readList(() { + RuntimeTypeUseKind kind = source.readEnum(RuntimeTypeUseKind.values); + DartType receiverType = source.readDartType(); + DartType argumentType = source.readDartType(allowNull: true); + return new RuntimeTypeUse(kind, receiverType, argumentType); + }).toSet(); + bool needToInitializeIsolateAffinityTag = source.readBool(); + bool needToInitializeDispatchProperty = source.readBool(); + bool requiresPreamble = source.readBool(); + bool isFunctionApplyUsed = source.readBool(); + bool isMirrorsUsed = source.readBool(); + bool isNoSuchMethodUsed = source.readBool(); + source.end(tag); + return new BackendUsageImpl( + globalFunctionDependencies: globalFunctionDependencies, + globalClassDependencies: globalClassDependencies, + helperFunctionsUsed: helperFunctionsUsed, + helperClassesUsed: helperClassesUsed, + runtimeTypeUses: runtimeTypeUses, + needToInitializeIsolateAffinityTag: needToInitializeIsolateAffinityTag, + needToInitializeDispatchProperty: needToInitializeDispatchProperty, + requiresPreamble: requiresPreamble, + isFunctionApplyUsed: isFunctionApplyUsed, + isMirrorsUsed: isMirrorsUsed, + isNoSuchMethodUsed: isNoSuchMethodUsed); + } + + void writeToDataSink(DataSink sink) { + sink.begin(tag); + sink.writeMembers(_globalFunctionDependencies); + sink.writeClasses(_globalClassDependencies); + sink.writeMembers(_helperFunctionsUsed); + sink.writeClasses(_helperClassesUsed); + sink.writeList(runtimeTypeUses, (RuntimeTypeUse runtimeTypeUse) { + sink.writeEnum(runtimeTypeUse.kind); + sink.writeDartType(runtimeTypeUse.receiverType); + sink.writeDartType(runtimeTypeUse.argumentType, allowNull: true); + }); + sink.writeBool(needToInitializeIsolateAffinityTag); + sink.writeBool(needToInitializeDispatchProperty); + sink.writeBool(requiresPreamble); + sink.writeBool(isFunctionApplyUsed); + sink.writeBool(isMirrorsUsed); + sink.writeBool(isNoSuchMethodUsed); + sink.end(tag); + } + @override bool isFunctionUsedByBackend(FunctionEntity element) { return _helperFunctionsUsed.contains(element); diff --git a/pkg/compiler/lib/src/js_backend/inferred_data.dart b/pkg/compiler/lib/src/js_backend/inferred_data.dart index 1f1f272b16e7a..73bb4932ff4a6 100644 --- a/pkg/compiler/lib/src/js_backend/inferred_data.dart +++ b/pkg/compiler/lib/src/js_backend/inferred_data.dart @@ -6,6 +6,7 @@ import 'dart:collection' show Queue; import '../common.dart'; import '../elements/entities.dart'; +import '../serialization/serialization.dart'; import '../types/abstract_value_domain.dart'; import '../universe/selector.dart'; import '../universe/side_effects.dart'; @@ -13,6 +14,20 @@ import '../world.dart'; import 'annotations.dart'; abstract class InferredData { + /// Deserializes a [InferredData] object from [source]. + factory InferredData.readFromDataSource( + DataSource source, JClosedWorld closedWorld) { + bool isTrivial = source.readBool(); + if (isTrivial) { + return new TrivialInferredData(); + } else { + return new InferredDataImpl.readFromDataSource(source, closedWorld); + } + } + + /// Serializes this [InferredData] to [sink]. + void writeToDataSink(DataSink sink); + /// Returns the side effects of executing [element]. SideEffects getSideEffectsOfElement(FunctionEntity element); @@ -62,6 +77,10 @@ abstract class InferredDataBuilder { } class InferredDataImpl implements InferredData { + /// Tag used for identifying serialized [InferredData] objects in a + /// debugging data stream. + static const String tag = 'inferred-data'; + final JClosedWorld _closedWorld; final Set _functionsCalledInLoop; final Map _sideEffects; @@ -80,6 +99,40 @@ class InferredDataImpl implements InferredData { this._elementsThatCannotThrow, this._functionsThatMightBePassedToApply); + factory InferredDataImpl.readFromDataSource( + DataSource source, JClosedWorld closedWorld) { + source.begin(tag); + Set functionsCalledInLoop = source.readMembers().toSet(); + Map sideEffects = + source.readMemberMap(() => new SideEffects.readFromDataSource(source)); + Set sideEffectsFreeElements = + source.readMembers().toSet(); + Set elementsThatCannotThrow = + source.readMembers().toSet(); + Set functionsThatMightBePassedToApply = + source.readMembers().toSet(); + source.end(tag); + return new InferredDataImpl( + closedWorld, + functionsCalledInLoop, + sideEffects, + sideEffectsFreeElements, + elementsThatCannotThrow, + functionsThatMightBePassedToApply); + } + + void writeToDataSink(DataSink sink) { + sink.writeBool(false); // Is _not_ trivial. + sink.begin(tag); + sink.writeMembers(_functionsCalledInLoop); + sink.writeMemberMap(_sideEffects, + (SideEffects sideEffects) => sideEffects.writeToDataSink(sink)); + sink.writeMembers(_sideEffectsFreeElements); + sink.writeMembers(_elementsThatCannotThrow); + sink.writeMembers(_functionsThatMightBePassedToApply); + sink.end(tag); + } + @override SideEffects getSideEffectsOfSelector( Selector selector, AbstractValue receiver) { @@ -250,6 +303,11 @@ class InferredDataBuilderImpl implements InferredDataBuilder { class TrivialInferredData implements InferredData { final SideEffects _allSideEffects = new SideEffects(); + @override + void writeToDataSink(DataSink sink) { + sink.writeBool(true); // Is trivial. + } + @override SideEffects getSideEffectsOfElement(FunctionEntity element) { return _allSideEffects; diff --git a/pkg/compiler/lib/src/js_backend/interceptor_data.dart b/pkg/compiler/lib/src/js_backend/interceptor_data.dart index f4db4d7e7d5f6..833c7c7561ef5 100644 --- a/pkg/compiler/lib/src/js_backend/interceptor_data.dart +++ b/pkg/compiler/lib/src/js_backend/interceptor_data.dart @@ -10,6 +10,7 @@ import '../common_elements.dart' import '../elements/entities.dart'; import '../elements/types.dart'; import '../js/js.dart' as jsAst; +import '../serialization/serialization.dart'; import '../types/abstract_value_domain.dart'; import '../universe/selector.dart'; import '../world.dart' show JClosedWorld; @@ -17,6 +18,15 @@ import 'namer.dart'; import 'native_data.dart'; abstract class InterceptorData { + /// Deserializes a [InterceptorData] object from [source]. + factory InterceptorData.readFromDataSource( + DataSource source, + NativeData nativeData, + CommonElements commonElements) = InterceptorDataImpl.readFromDataSource; + + /// Serializes this [InterceptorData] to [sink]. + void writeToDataSink(DataSink sink); + /// Returns `true` if [cls] is an intercepted class. bool isInterceptedClass(ClassEntity element); @@ -49,6 +59,10 @@ abstract class InterceptorDataBuilder { } class InterceptorDataImpl implements InterceptorData { + /// Tag used for identifying serialized [InterceptorData] objects in a + /// debugging data stream. + static const String tag = 'interceptor-data'; + final NativeBasicData _nativeData; final CommonElements _commonElements; @@ -89,6 +103,40 @@ class InterceptorDataImpl implements InterceptorData { this.interceptedClasses, this.classesMixedIntoInterceptedClasses); + factory InterceptorDataImpl.readFromDataSource( + DataSource source, NativeData nativeData, CommonElements commonElements) { + source.begin(tag); + int interceptedMembersCount = source.readInt(); + Map> interceptedMembers = {}; + for (int i = 0; i < interceptedMembersCount; i++) { + String name = source.readString(); + Set members = source.readMembers().toSet(); + interceptedMembers[name] = members; + } + Set interceptedClasses = source.readClasses().toSet(); + Set classesMixedIntoInterceptedClasses = + source.readClasses().toSet(); + source.end(tag); + return new InterceptorDataImpl( + nativeData, + commonElements, + interceptedMembers, + interceptedClasses, + classesMixedIntoInterceptedClasses); + } + + void writeToDataSink(DataSink sink) { + sink.begin(tag); + sink.writeInt(interceptedMembers.length); + interceptedMembers.forEach((String name, Set members) { + sink.writeString(name); + sink.writeMembers(members); + }); + sink.writeClasses(interceptedClasses); + sink.writeClasses(classesMixedIntoInterceptedClasses); + sink.end(tag); + } + bool isInterceptedMethod(MemberEntity element) { if (!element.isInstanceMember) return false; // TODO(johnniwinther): Avoid this hack. diff --git a/pkg/compiler/lib/src/js_backend/native_data.dart b/pkg/compiler/lib/src/js_backend/native_data.dart index 40c3d2895ad19..e41e541c054a1 100644 --- a/pkg/compiler/lib/src/js_backend/native_data.dart +++ b/pkg/compiler/lib/src/js_backend/native_data.dart @@ -8,12 +8,21 @@ import '../common.dart'; import '../common_elements.dart' show ElementEnvironment; import '../elements/entities.dart'; import '../native/behavior.dart' show NativeBehavior; +import '../serialization/serialization.dart'; import '../util/util.dart'; /// Basic information for native classes and js-interop libraries and classes. /// /// This information is computed during loading using [NativeBasicDataBuilder]. abstract class NativeBasicData { + /// Deserializes a [NativeBasicData] object from [source]. + factory NativeBasicData.readFromDataSource( + DataSource source, ElementEnvironment elementEnvironment) = + NativeBasicDataImpl.readFromDataSource; + + /// Serializes this [NativeBasicData] to [sink]. + void writeToDataSink(DataSink sink); + /// Returns `true` if [cls] corresponds to a native JavaScript class. /// /// A class is marked as native either through the `@Native(...)` annotation @@ -48,6 +57,14 @@ abstract class NativeBasicData { /// /// This information is computed during resolution using [NativeDataBuilder]. abstract class NativeData extends NativeBasicData { + /// Deserializes a [NativeData] object from [source]. + factory NativeData.readFromDataSource( + DataSource source, ElementEnvironment elementEnvironment) = + NativeDataImpl.readFromDataSource; + + /// Serializes this [NativeData] to [sink]. + void writeToDataSink(DataSink sink); + /// Returns `true` if [element] corresponds to a native JavaScript member. /// /// A member is marked as native either through the native mechanism @@ -267,6 +284,10 @@ class NativeBasicDataBuilderImpl implements NativeBasicDataBuilder { } class NativeBasicDataImpl implements NativeBasicData { + /// Tag used for identifying serialized [NativeBasicData] objects in a + /// debugging data stream. + static const String tag = 'native-basic-data'; + final ElementEnvironment _env; /// Tag info for native JavaScript classes names. See @@ -293,6 +314,45 @@ class NativeBasicDataImpl implements NativeBasicData { this.anonymousJsInteropClasses, this.jsInteropMembers); + factory NativeBasicDataImpl.readFromDataSource( + DataSource source, ElementEnvironment elementEnvironment) { + source.begin(tag); + Map nativeClassTagInfo = + source.readClassMap(() { + List names = source.readStrings(); + bool isNonLeaf = source.readBool(); + return new NativeClassTag.internal(names, isNonLeaf); + }); + Map jsInteropLibraries = + source.readLibraryMap(source.readString); + Map jsInteropClasses = + source.readLibraryMap(source.readString); + Set anonymousJsInteropClasses = source.readClasses().toSet(); + Map jsInteropMembers = + source.readLibraryMap(source.readString); + source.end(tag); + return new NativeBasicDataImpl( + elementEnvironment, + nativeClassTagInfo, + jsInteropLibraries, + jsInteropClasses, + anonymousJsInteropClasses, + jsInteropMembers); + } + + void writeToDataSink(DataSink sink) { + sink.begin(tag); + sink.writeClassMap(nativeClassTagInfo, (NativeClassTag tag) { + sink.writeStrings(tag.names); + sink.writeBool(tag.isNonLeaf); + }); + sink.writeLibraryMap(jsInteropLibraries, sink.writeString); + sink.writeClassMap(jsInteropClasses, sink.writeString); + sink.writeClasses(anonymousJsInteropClasses); + sink.writeMemberMap(jsInteropMembers, sink.writeString); + sink.end(tag); + } + @override bool isNativeClass(ClassEntity element) { if (isJsInteropClass(element)) return true; @@ -480,7 +540,13 @@ class NativeDataBuilderImpl implements NativeDataBuilder { jsInteropMembers); } +// TODO(johnniwinther): Remove fields that overlap with [NativeBasicData], like +// [anonymousJsInteropClasses]. class NativeDataImpl implements NativeData, NativeBasicDataImpl { + /// Tag used for identifying serialized [NativeData] objects in a + /// debugging data stream. + static const String tag = 'native-data'; + /// Prefix used to escape JS names that are not valid Dart names /// when using JSInterop. static const String _jsInteropEscapePrefix = r'JS$'; @@ -525,6 +591,63 @@ class NativeDataImpl implements NativeData, NativeBasicDataImpl { this.jsInteropClasses, this.jsInteropMembers); + factory NativeDataImpl.readFromDataSource( + DataSource source, ElementEnvironment elementEnvironment) { + source.begin(tag); + NativeBasicData nativeBasicData = + new NativeBasicData.readFromDataSource(source, elementEnvironment); + Map nativeMemberName = + source.readMemberMap(source.readString); + Map nativeMethodBehavior = source + .readMemberMap(() => new NativeBehavior.readFromDataSource(source)); + Map nativeFieldLoadBehavior = source + .readMemberMap(() => new NativeBehavior.readFromDataSource(source)); + Map nativeFieldStoreBehavior = source + .readMemberMap(() => new NativeBehavior.readFromDataSource(source)); + Map jsInteropLibraries = + source.readLibraryMap(source.readString); + Set anonymousJsInteropClasses = source.readClasses().toSet(); + Map jsInteropClasses = + source.readClassMap(source.readString); + Map jsInteropMembers = + source.readMemberMap(source.readString); + source.end(tag); + return new NativeDataImpl( + nativeBasicData, + nativeMemberName, + nativeMethodBehavior, + nativeFieldLoadBehavior, + nativeFieldStoreBehavior, + jsInteropLibraries, + anonymousJsInteropClasses, + jsInteropClasses, + jsInteropMembers); + } + + void writeToDataSink(DataSink sink) { + sink.begin(tag); + _nativeBasicData.writeToDataSink(sink); + + sink.writeMemberMap(nativeMemberName, sink.writeString); + + sink.writeMemberMap(nativeMethodBehavior, (NativeBehavior behavior) { + behavior.writeToDataSink(sink); + }); + + sink.writeMemberMap(nativeFieldLoadBehavior, (NativeBehavior behavior) { + behavior.writeToDataSink(sink); + }); + sink.writeMemberMap(nativeFieldStoreBehavior, (NativeBehavior behavior) { + behavior.writeToDataSink(sink); + }); + + sink.writeLibraryMap(jsInteropLibraries, sink.writeString); + sink.writeClasses(anonymousJsInteropClasses); + sink.writeClassMap(jsInteropClasses, sink.writeString); + sink.writeMemberMap(jsInteropMembers, sink.writeString); + sink.end(tag); + } + @override bool isAnonymousJsInteropClass(ClassEntity element) { return anonymousJsInteropClasses.contains(element); diff --git a/pkg/compiler/lib/src/js_backend/no_such_method_registry.dart b/pkg/compiler/lib/src/js_backend/no_such_method_registry.dart index 4c4739075dd3c..e997b3ebb61d8 100644 --- a/pkg/compiler/lib/src/js_backend/no_such_method_registry.dart +++ b/pkg/compiler/lib/src/js_backend/no_such_method_registry.dart @@ -6,6 +6,7 @@ import '../common.dart'; import '../common_elements.dart' show CommonElements; import '../common/names.dart' show Identifiers, Selectors; import '../elements/entities.dart'; +import '../serialization/serialization.dart'; import '../types/types.dart'; /// [NoSuchMethodRegistry] and [NoSuchMethodData] categorizes `noSuchMethod` @@ -171,6 +172,13 @@ class NoSuchMethodRegistryImpl implements NoSuchMethodRegistry { /// Post inference collected category `D` methods are into subcategories `D1` /// and `D2`. abstract class NoSuchMethodData { + /// Deserializes a [NoSuchMethodData] object from [source]. + factory NoSuchMethodData.readFromDataSource(DataSource source) = + NoSuchMethodDataImpl.readFromDataSource; + + /// Serializes this [NoSuchMethodData] to [sink]. + void writeToDataSink(DataSink sink); + /// Returns [true] if the given element is a complex [noSuchMethod] /// implementation. An implementation is complex if it falls into /// category D, as described above. @@ -186,6 +194,10 @@ abstract class NoSuchMethodData { } class NoSuchMethodDataImpl implements NoSuchMethodData { + /// Tag used for identifying serialized [NoSuchMethodData] objects in a + /// debugging data stream. + static const String tag = 'no-such-method-data'; + /// The implementations that fall into category B, described above. final Set throwingImpls; @@ -203,6 +215,35 @@ class NoSuchMethodDataImpl implements NoSuchMethodData { NoSuchMethodDataImpl( this.throwingImpls, this.otherImpls, this.forwardingSyntaxImpls); + factory NoSuchMethodDataImpl.readFromDataSource(DataSource source) { + source.begin(tag); + Set throwingImpls = + source.readMembers().toSet(); + Set otherImpls = + source.readMembers().toSet(); + Set forwardingSyntaxImpls = + source.readMembers().toSet(); + List complexNoReturnImpls = + source.readMembers(); + List complexReturningImpls = + source.readMembers(); + source.end(tag); + return new NoSuchMethodDataImpl( + throwingImpls, otherImpls, forwardingSyntaxImpls) + ..complexNoReturnImpls.addAll(complexNoReturnImpls) + ..complexReturningImpls.addAll(complexReturningImpls); + } + + void writeToDataSink(DataSink sink) { + sink.begin(tag); + sink.writeMembers(throwingImpls); + sink.writeMembers(otherImpls); + sink.writeMembers(forwardingSyntaxImpls); + sink.writeMembers(complexNoReturnImpls); + sink.writeMembers(complexReturningImpls); + sink.end(tag); + } + /// Now that type inference is complete, split category D into two /// subcategories: D1, those that have no return type, and D2, those /// that have a return type. diff --git a/pkg/compiler/lib/src/js_backend/runtime_types.dart b/pkg/compiler/lib/src/js_backend/runtime_types.dart index daa1915c75a44..3329424449e99 100644 --- a/pkg/compiler/lib/src/js_backend/runtime_types.dart +++ b/pkg/compiler/lib/src/js_backend/runtime_types.dart @@ -19,6 +19,7 @@ import '../js/js.dart' as jsAst; import '../js/js.dart' show js; import '../js_emitter/js_emitter.dart' show Emitter; import '../options.dart'; +import '../serialization/serialization.dart'; import '../universe/class_hierarchy.dart'; import '../universe/feature.dart'; import '../universe/selector.dart'; @@ -42,6 +43,20 @@ typedef bool ShouldEncodeTypedefCallback(TypedefType variable); /// Interface for the classes and methods that need runtime types. abstract class RuntimeTypesNeed { + /// Deserializes a [RuntimeTypesNeed] object from [source]. + factory RuntimeTypesNeed.readFromDataSource( + DataSource source, ElementEnvironment elementEnvironment) { + bool isTrivial = source.readBool(); + if (isTrivial) { + return const TrivialRuntimeTypesNeed(); + } + return new RuntimeTypesNeedImpl.readFromDataSource( + source, elementEnvironment); + } + + /// Serializes this [RuntimeTypesNeed] to [sink]. + void writeToDataSink(DataSink sink); + /// Returns `true` if [cls] needs type arguments at runtime type. /// /// This is for instance the case for generic classes used in a type test: @@ -106,6 +121,10 @@ abstract class RuntimeTypesNeed { class TrivialRuntimeTypesNeed implements RuntimeTypesNeed { const TrivialRuntimeTypesNeed(); + void writeToDataSink(DataSink sink) { + sink.writeBool(true); // Is trivial. + } + @override bool classNeedsTypeArguments(ClassEntity cls) => true; @@ -718,6 +737,10 @@ abstract class _RuntimeTypesBase { } class RuntimeTypesNeedImpl implements RuntimeTypesNeed { + /// Tag used for identifying serialized [RuntimeTypesNeed] objects in a + /// debugging data stream. + static const String tag = 'runtime-types-need'; + final ElementEnvironment _elementEnvironment; final Set classesNeedingTypeArguments; final Set methodsNeedingSignature; @@ -737,6 +760,45 @@ class RuntimeTypesNeedImpl implements RuntimeTypesNeed { this.selectorsNeedingTypeArguments, this.instantiationsNeedingTypeArguments); + factory RuntimeTypesNeedImpl.readFromDataSource( + DataSource source, ElementEnvironment elementEnvironment) { + source.begin(tag); + Set classesNeedingTypeArguments = + source.readClasses().toSet(); + Set methodsNeedingSignature = + source.readMembers().toSet(); + Set methodsNeedingTypeArguments = + source.readMembers().toSet(); + Set selectorsNeedingTypeArguments = + source.readList(() => new Selector.readFromDataSource(source)).toSet(); + Set instantiationsNeedingTypeArguments = + source.readList(source.readInt).toSet(); + source.end(tag); + return new RuntimeTypesNeedImpl( + elementEnvironment, + classesNeedingTypeArguments, + methodsNeedingSignature, + methodsNeedingTypeArguments, + null, + null, + selectorsNeedingTypeArguments, + instantiationsNeedingTypeArguments); + } + + void writeToDataSink(DataSink sink) { + sink.writeBool(false); // Is _not_ trivial. + sink.begin(tag); + sink.writeClasses(classesNeedingTypeArguments); + sink.writeMembers(methodsNeedingSignature); + sink.writeMembers(methodsNeedingTypeArguments); + assert(localFunctionsNeedingSignature == null); + assert(localFunctionsNeedingTypeArguments == null); + sink.writeList(selectorsNeedingTypeArguments, + (Selector selector) => selector.writeToDataSink(sink)); + sink.writeList(instantiationsNeedingTypeArguments, sink.writeInt); + sink.end(tag); + } + bool checkClass(covariant ClassEntity cls) => true; bool classNeedsTypeArguments(ClassEntity cls) { diff --git a/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart b/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart index cc9610343b65d..063d03f4467d5 100644 --- a/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart +++ b/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart @@ -201,7 +201,7 @@ class CodeEmitterTask extends CompilerTask { closedWorld.allocatorAnalysis, inferredData, backend.sourceInformationStrategy, - compiler.backendStrategy.sorter, + closedWorld.sorter, typeTestRegistry.rtiNeededClasses, closedWorld.elementEnvironment.mainFunction); int size = emitter.emitProgram(programBuilder); diff --git a/pkg/compiler/lib/src/js_model/closure.dart b/pkg/compiler/lib/src/js_model/closure.dart index 0a0c18415efdd..a500d9d42ecc8 100644 --- a/pkg/compiler/lib/src/js_model/closure.dart +++ b/pkg/compiler/lib/src/js_model/closure.dart @@ -18,6 +18,7 @@ import '../js_model/element_map.dart'; import '../js_model/env.dart'; import '../ordered_typeset.dart'; import '../options.dart'; +import '../serialization/serialization.dart'; import '../ssa/type_builder.dart'; import '../universe/selector.dart'; import 'elements.dart'; @@ -66,6 +67,8 @@ class KernelClosureConversionTask extends ClosureConversionTask { } class ClosureDataImpl implements ClosureData { + /// Tag used for identifying serialized [ClosureData] objects in a + /// debugging data stream. static const String tag = 'closure-data'; final JsToElementMap _elementMap; @@ -84,6 +87,42 @@ class ClosureDataImpl implements ClosureData { ClosureDataImpl(this._elementMap, this._scopeMap, this._capturedScopesMap, this._capturedScopeForSignatureMap, this._localClosureRepresentationMap); + /// Deserializes a [ClosureData] object from [source]. + factory ClosureDataImpl.readFromDataSource( + JsToElementMap elementMap, DataSource source) { + source.begin(tag); + // TODO(johnniwinther): Support shared [ScopeInfo]. + Map scopeMap = + source.readMemberMap(() => new ScopeInfo.readFromDataSource(source)); + Map capturedScopesMap = source + .readTreeNodeMap(() => new CapturedScope.readFromDataSource(source)); + Map capturedScopeForSignatureMap = source + .readMemberMap(() => new CapturedScope.readFromDataSource(source)); + Map localClosureRepresentationMap = + source.readTreeNodeMap( + () => new ClosureRepresentationInfo.readFromDataSource(source)); + source.end(tag); + return new ClosureDataImpl(elementMap, scopeMap, capturedScopesMap, + capturedScopeForSignatureMap, localClosureRepresentationMap); + } + + /// Serializes this [ClosureData] to [sink]. + void writeToDataSink(DataSink sink) { + sink.begin(tag); + sink.writeMemberMap( + _scopeMap, (ScopeInfo info) => info.writeToDataSink(sink)); + sink.writeTreeNodeMap(_capturedScopesMap, (CapturedScope scope) { + scope.writeToDataSink(sink); + }); + sink.writeMemberMap(_capturedScopeForSignatureMap, + (CapturedScope scope) => scope.writeToDataSink(sink)); + sink.writeTreeNodeMap(_localClosureRepresentationMap, + (ClosureRepresentationInfo info) { + info.writeToDataSink(sink); + }); + sink.end(tag); + } + @override ScopeInfo getScopeInfo(MemberEntity entity) { // TODO(johnniwinther): Remove this check when constructor bodies a created @@ -659,7 +698,11 @@ Local _getLocal( } class JsScopeInfo extends ScopeInfo { - final Set localsUsedInTryOrSync; + /// Tag used for identifying serialized [JsScopeInfo] objects in a + /// debugging data stream. + static const String tag = 'scope-info'; + + final Iterable localsUsedInTryOrSync; final Local thisLocal; final Map boxedVariables; @@ -702,6 +745,29 @@ class JsScopeInfo extends ScopeInfo { } bool isBoxed(Local variable) => boxedVariables.containsKey(variable); + + factory JsScopeInfo.readFromDataSource(DataSource source) { + source.begin(tag); + Iterable localsUsedInTryOrSync = source.readLocals(); + Local thisLocal = source.readLocalOrNull(); + Map boxedVariables = + source.readLocalMap(() => source.readMember()); + Set freeVariables = source.readLocals().toSet(); + source.end(tag); + return new JsScopeInfo.internal( + localsUsedInTryOrSync, thisLocal, boxedVariables, freeVariables); + } + + @override + void writeToDataSink(DataSink sink) { + sink.writeEnum(ScopeInfoKind.scopeInfo); + sink.begin(tag); + sink.writeLocals(localsUsedInTryOrSync); + sink.writeLocalOrNull(thisLocal); + sink.writeLocalMap(boxedVariables, sink.writeMember); + sink.writeLocals(freeVariables); + sink.end(tag); + } } class KernelCapturedScope extends KernelScopeInfo { @@ -746,8 +812,21 @@ class KernelCapturedScope extends KernelScopeInfo { } class JsCapturedScope extends JsScopeInfo implements CapturedScope { + /// Tag used for identifying serialized [JsCapturedScope] objects in a + /// debugging data stream. + static const String tag = 'captured-scope'; + final Local context; + JsCapturedScope.internal( + Iterable localsUsedInTryOrSync, + Local thisLocal, + Map boxedVariables, + Set freeVariables, + this.context) + : super.internal( + localsUsedInTryOrSync, thisLocal, boxedVariables, freeVariables); + JsCapturedScope.from( Map boxedVariables, KernelCapturedScope capturedScope, @@ -758,6 +837,31 @@ class JsCapturedScope extends JsScopeInfo implements CapturedScope { super.from(boxedVariables, capturedScope, localsMap, elementMap); bool get requiresContextBox => boxedVariables.isNotEmpty; + + factory JsCapturedScope.readFromDataSource(DataSource source) { + source.begin(tag); + Iterable localsUsedInTryOrSync = source.readLocals(); + Local thisLocal = source.readLocalOrNull(); + Map boxedVariables = + source.readLocalMap(() => source.readMember()); + Set freeVariables = source.readLocals().toSet(); + Local context = source.readLocalOrNull(); + source.end(tag); + return new JsCapturedScope.internal(localsUsedInTryOrSync, thisLocal, + boxedVariables, freeVariables, context); + } + + @override + void writeToDataSink(DataSink sink) { + sink.writeEnum(ScopeInfoKind.capturedScope); + sink.begin(tag); + sink.writeLocals(localsUsedInTryOrSync); + sink.writeLocalOrNull(thisLocal); + sink.writeLocalMap(boxedVariables, sink.writeMember); + sink.writeLocals(freeVariables); + sink.writeLocalOrNull(context); + sink.end(tag); + } } class KernelCapturedLoopScope extends KernelCapturedScope { @@ -788,8 +892,22 @@ class KernelCapturedLoopScope extends KernelCapturedScope { } class JsCapturedLoopScope extends JsCapturedScope implements CapturedLoopScope { + /// Tag used for identifying serialized [JsCapturedLoopScope] objects in a + /// debugging data stream. + static const String tag = 'captured-loop-scope'; + final List boxedLoopVariables; + JsCapturedLoopScope.internal( + Iterable localsUsedInTryOrSync, + Local thisLocal, + Map boxedVariables, + Set freeVariables, + Local context, + this.boxedLoopVariables) + : super.internal(localsUsedInTryOrSync, thisLocal, boxedVariables, + freeVariables, context); + JsCapturedLoopScope.from( Map boxedVariables, KernelCapturedLoopScope capturedScope, @@ -801,11 +919,43 @@ class JsCapturedLoopScope extends JsCapturedScope implements CapturedLoopScope { super.from(boxedVariables, capturedScope, localsMap, elementMap); bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; + + factory JsCapturedLoopScope.readFromDataSource(DataSource source) { + source.begin(tag); + Iterable localsUsedInTryOrSync = source.readLocals(); + Local thisLocal = source.readLocalOrNull(); + Map boxedVariables = + source.readLocalMap(() => source.readMember()); + Set freeVariables = source.readLocals().toSet(); + Local context = source.readLocalOrNull(); + List boxedLoopVariables = source.readLocals(); + source.end(tag); + return new JsCapturedLoopScope.internal(localsUsedInTryOrSync, thisLocal, + boxedVariables, freeVariables, context, boxedLoopVariables); + } + + @override + void writeToDataSink(DataSink sink) { + sink.writeEnum(ScopeInfoKind.capturedLoopScope); + sink.begin(tag); + sink.writeLocals(localsUsedInTryOrSync); + sink.writeLocalOrNull(thisLocal); + sink.writeLocalMap(boxedVariables, sink.writeMember); + sink.writeLocals(freeVariables); + sink.writeLocalOrNull(context); + sink.writeLocals(boxedLoopVariables); + sink.end(tag); + } } +// TODO(johnniwinther): Rename this class. // TODO(johnniwinther): Add unittest for the computed [ClosureClass]. class KernelClosureClassInfo extends JsScopeInfo implements ClosureRepresentationInfo { + /// Tag used for identifying serialized [KernelClosureClassInfo] objects in a + /// debugging data stream. + static const String tag = 'closure-representation-info'; + JFunction callMethod; JSignatureMethod signatureMethod; final Local closureEntity; @@ -839,6 +989,47 @@ class KernelClosureClassInfo extends JsScopeInfo : localToFieldMap = new Map(), super.from(boxedVariables, info, localsMap, elementMap); + factory KernelClosureClassInfo.readFromDataSource(DataSource source) { + source.begin(tag); + Iterable localsUsedInTryOrSync = source.readLocals(); + Local thisLocal = source.readLocalOrNull(); + Map boxedVariables = + source.readLocalMap(() => source.readMember()); + Set freeVariables = source.readLocals().toSet(); + JFunction callMethod = source.readMember(); + JSignatureMethod signatureMethod = source.readMemberOrNull(); + Local closureEntity = source.readLocalOrNull(); + JClass closureClassEntity = source.readClass(); + Map localToFieldMap = + source.readLocalMap(() => source.readMember()); + source.end(tag); + return new KernelClosureClassInfo.internal( + localsUsedInTryOrSync, + thisLocal, + boxedVariables, + freeVariables, + callMethod, + signatureMethod, + closureEntity, + closureClassEntity, + localToFieldMap); + } + + void writeToDataSink(DataSink sink) { + sink.writeEnum(ScopeInfoKind.closureRepresentationInfo); + sink.begin(tag); + sink.writeLocals(localsUsedInTryOrSync); + sink.writeLocalOrNull(thisLocal); + sink.writeLocalMap(boxedVariables, sink.writeMember); + sink.writeLocals(freeVariables); + sink.writeMember(callMethod); + sink.writeMemberOrNull(signatureMethod); + sink.writeLocalOrNull(closureEntity); + sink.writeClass(closureClassEntity); + sink.writeLocalMap(localToFieldMap, sink.writeMember); + sink.end(tag); + } + List get createdFieldEntities => localToFieldMap.keys.toList(); @override @@ -880,9 +1071,29 @@ class NodeBox { } class JClosureClass extends JClass { + /// Tag used for identifying serialized [JClosureClass] objects in a + /// debugging data stream. + static const String tag = 'closure-class'; + JClosureClass(JLibrary library, String name) : super(library, name, isAbstract: false); + factory JClosureClass.readFromDataSource(DataSource source) { + source.begin(tag); + JLibrary library = source.readLibrary(); + String name = source.readString(); + source.end(tag); + return new JClosureClass(library, name); + } + + void writeToDataSink(DataSink sink) { + sink.writeEnum(JClassKind.closure); + sink.begin(tag); + sink.writeLibrary(library); + sink.writeString(name); + sink.end(tag); + } + @override bool get isClosure => true; @@ -909,6 +1120,10 @@ class AnonymousClosureLocal implements Local { } class JClosureField extends JField implements PrivatelyNamedJSEntity { + /// Tag used for identifying serialized [JClosureClass] objects in a + /// debugging data stream. + static const String tag = 'closure-field'; + final String declaredName; JClosureField( @@ -927,12 +1142,40 @@ class JClosureField extends JField implements PrivatelyNamedJSEntity { : super(library, enclosingClass, memberName, isAssignable: isAssignable, isConst: isConst, isStatic: false); + factory JClosureField.readFromDataSource(DataSource source) { + source.begin(tag); + JClass cls = source.readClass(); + String name = source.readString(); + String declaredName = source.readString(); + bool isConst = source.readBool(); + bool isAssignable = source.readBool(); + source.end(tag); + return new JClosureField.internal( + cls.library, cls, new Name(name, cls.library), declaredName, + isAssignable: isAssignable, isConst: isConst); + } + @override + void writeToDataSink(DataSink sink) { + sink.writeEnum(JMemberKind.closureField); + sink.begin(tag); + sink.writeClass(enclosingClass); + sink.writeString(name); + sink.writeString(declaredName); + sink.writeBool(isConst); + sink.writeBool(isAssignable); + sink.end(tag); + } + @override Entity get rootOfScope => enclosingClass; } class RecordClassData implements JClassData { + /// Tag used for identifying serialized [RecordClassData] objects in a + /// debugging data stream. + static const String tag = 'record-class-data'; + @override final ClassDefinition definition; @@ -948,6 +1191,28 @@ class RecordClassData implements JClassData { RecordClassData( this.definition, this.thisType, this.supertype, this.orderedTypeSet); + factory RecordClassData.readFromDataSource(DataSource source) { + source.begin(tag); + ClassDefinition definition = new ClassDefinition.readFromDataSource(source); + InterfaceType thisType = source.readDartType(); + InterfaceType supertype = source.readDartType(); + OrderedTypeSet orderedTypeSet = + new OrderedTypeSet.readFromDataSource(source); + source.end(tag); + return new RecordClassData(definition, thisType, supertype, orderedTypeSet); + } + + @override + void writeToDataSink(DataSink sink) { + sink.writeEnum(JClassDataKind.record); + sink.begin(tag); + definition.writeToDataSink(sink); + sink.writeDartType(thisType); + sink.writeDartType(supertype); + orderedTypeSet.writeToDataSink(sink); + sink.end(tag); + } + @override bool get isMixinApplication => false; @@ -969,12 +1234,32 @@ class RecordClassData implements JClassData { /// A container for variables declared in a particular scope that are accessed /// elsewhere. -// TODO(efortuna, johnniwinther): Don't implement JClass. This isn't actually a +// TODO(johnniwinther): Don't implement JClass. This isn't actually a // class. class JRecord extends JClass { + /// Tag used for identifying serialized [JRecord] objects in a + /// debugging data stream. + static const String tag = 'record'; + JRecord(LibraryEntity library, String name) : super(library, name, isAbstract: false); + factory JRecord.readFromDataSource(DataSource source) { + source.begin(tag); + JLibrary library = source.readLibrary(); + String name = source.readString(); + source.end(tag); + return new JRecord(library, name); + } + + void writeToDataSink(DataSink sink) { + sink.writeEnum(JClassKind.record); + sink.begin(tag); + sink.writeLibrary(library); + sink.writeString(name); + sink.end(tag); + } + bool get isClosure => false; String toString() => '${jsElementPrefix}record_container($name)'; @@ -986,6 +1271,10 @@ class JRecord extends JClass { /// This corresponds to BoxFieldElement; we reuse BoxLocal from the original /// algorithm to correspond to the actual name of the variable. class JRecordField extends JField { + /// Tag used for identifying serialized [JRecordField] objects in a + /// debugging data stream. + static const String tag = 'record-field'; + final BoxLocal box; JRecordField(String name, this.box, {bool isConst}) @@ -993,24 +1282,92 @@ class JRecordField extends JField { new Name(name, box.container.library), isStatic: false, isAssignable: true, isConst: isConst); + factory JRecordField.readFromDataSource(DataSource source) { + source.begin(tag); + String name = source.readString(); + JClass enclosingClass = source.readClass(); + bool isConst = source.readBool(); + source.end(tag); + return new JRecordField(name, new BoxLocal(enclosingClass), + isConst: isConst); + } + + @override + void writeToDataSink(DataSink sink) { + sink.writeEnum(JMemberKind.recordField); + sink.begin(tag); + sink.writeString(name); + sink.writeClass(enclosingClass); + sink.writeBool(isConst); + sink.end(tag); + } + @override bool get isInstanceMember => false; } class ClosureClassData extends RecordClassData { + /// Tag used for identifying serialized [ClosureClassData] objects in a + /// debugging data stream. + static const String tag = 'closure-class-data'; + @override FunctionType callType; ClosureClassData(ClassDefinition definition, InterfaceType thisType, InterfaceType supertype, OrderedTypeSet orderedTypeSet) : super(definition, thisType, supertype, orderedTypeSet); + + factory ClosureClassData.readFromDataSource(DataSource source) { + source.begin(tag); + ClassDefinition definition = new ClassDefinition.readFromDataSource(source); + InterfaceType thisType = source.readDartType(); + InterfaceType supertype = source.readDartType(); + OrderedTypeSet orderedTypeSet = + new OrderedTypeSet.readFromDataSource(source); + FunctionType callType = source.readDartType(); + source.end(tag); + return new ClosureClassData(definition, thisType, supertype, orderedTypeSet) + ..callType = callType; + } + + @override + void writeToDataSink(DataSink sink) { + sink.writeEnum(JClassDataKind.closure); + sink.begin(tag); + definition.writeToDataSink(sink); + sink.writeDartType(thisType); + sink.writeDartType(supertype); + orderedTypeSet.writeToDataSink(sink); + sink.writeDartType(callType); + sink.end(tag); + } } class ClosureClassDefinition implements ClassDefinition { + /// Tag used for identifying serialized [ClosureClassDefinition] objects in a + /// debugging data stream. + static const String tag = 'closure-class-definition'; + final SourceSpan location; ClosureClassDefinition(this.location); + factory ClosureClassDefinition.readFromDataSource(DataSource source) { + source.begin(tag); + SourceSpan location = source.readSourceSpan(); + source.end(tag); + return new ClosureClassDefinition(location); + } + + @override + void writeToDataSink(DataSink sink) { + sink.writeEnum(ClassKind.closure); + sink.begin(tag); + sink.writeSourceSpan(location); + sink.end(tag); + } + ClassKind get kind => ClassKind.closure; ir.Node get node => @@ -1034,6 +1391,10 @@ abstract class ClosureMemberData implements JMemberData { class ClosureFunctionData extends ClosureMemberData with FunctionDataMixin implements FunctionData { + /// Tag used for identifying serialized [ClosureFunctionData] objects in a + /// debugging data stream. + static const String tag = 'closure-function-data'; + final FunctionType functionType; final ir.FunctionNode functionNode; final ClassTypeVariableAccess classTypeVariableAccess; @@ -1046,6 +1407,31 @@ class ClosureFunctionData extends ClosureMemberData this.classTypeVariableAccess) : super(definition, memberThisType); + factory ClosureFunctionData.readFromDataSource(DataSource source) { + source.begin(tag); + ClosureMemberDefinition definition = + new MemberDefinition.readFromDataSource(source); + InterfaceType memberThisType = source.readDartType(allowNull: true); + FunctionType functionType = source.readDartType(); + ir.FunctionNode functionNode = source.readTreeNode(); + ClassTypeVariableAccess classTypeVariableAccess = + source.readEnum(ClassTypeVariableAccess.values); + source.end(tag); + return new ClosureFunctionData(definition, memberThisType, functionType, + functionNode, classTypeVariableAccess); + } + + void writeToDataSink(DataSink sink) { + sink.writeEnum(JMemberDataKind.closureFunction); + sink.begin(tag); + definition.writeToDataSink(sink); + sink.writeDartType(memberThisType, allowNull: true); + sink.writeDartType(functionType); + sink.writeTreeNode(functionNode); + sink.writeEnum(classTypeVariableAccess); + sink.end(tag); + } + void forEachParameter(JsToElementMap elementMap, void f(DartType type, String name, ConstantValue defaultValue)) { void handleParameter(ir.VariableDeclaration node, {bool isOptional: true}) { @@ -1078,10 +1464,32 @@ class ClosureFunctionData extends ClosureMemberData } class ClosureFieldData extends ClosureMemberData implements JFieldData { + /// Tag used for identifying serialized [ClosureFieldData] objects in a + /// debugging data stream. + static const String tag = 'closure-field-data'; + DartType _type; + ClosureFieldData(MemberDefinition definition, InterfaceType memberThisType) : super(definition, memberThisType); + factory ClosureFieldData.readFromDataSource(DataSource source) { + source.begin(tag); + MemberDefinition definition = + new MemberDefinition.readFromDataSource(source); + InterfaceType memberThisType = source.readDartType(allowNull: true); + source.end(tag); + return new ClosureFieldData(definition, memberThisType); + } + + void writeToDataSink(DataSink sink) { + sink.writeEnum(JMemberDataKind.closureField); + sink.begin(tag); + definition.writeToDataSink(sink); + sink.writeDartType(memberThisType, allowNull: true); + sink.end(tag); + } + @override DartType getFieldType(IrToElementMap elementMap) { if (_type != null) return _type; @@ -1142,6 +1550,10 @@ class ClosureFieldData extends ClosureMemberData implements JFieldData { } class ClosureMemberDefinition implements MemberDefinition { + /// Tag used for identifying serialized [ClosureMemberDefinition] objects in a + /// debugging data stream. + static const String tag = 'closure-member-definition'; + final SourceSpan location; final MemberKind kind; final ir.TreeNode node; @@ -1150,14 +1562,50 @@ class ClosureMemberDefinition implements MemberDefinition { : assert( kind == MemberKind.closureCall || kind == MemberKind.closureField); + factory ClosureMemberDefinition.readFromDataSource( + DataSource source, MemberKind kind) { + source.begin(tag); + SourceSpan location = source.readSourceSpan(); + ir.TreeNode node = source.readTreeNode(); + source.end(tag); + return new ClosureMemberDefinition(location, kind, node); + } + + void writeToDataSink(DataSink sink) { + sink.writeEnum(kind); + sink.begin(tag); + sink.writeSourceSpan(location); + sink.writeTreeNode(node); + sink.end(tag); + } + String toString() => 'ClosureMemberDefinition(kind:$kind,location:$location)'; } class RecordContainerDefinition implements ClassDefinition { + /// Tag used for identifying serialized [RecordContainerDefinition] objects in + /// a debugging data stream. + static const String tag = 'record-definition'; + final SourceSpan location; RecordContainerDefinition(this.location); + factory RecordContainerDefinition.readFromDataSource(DataSource source) { + source.begin(tag); + SourceSpan location = source.readSourceSpan(); + source.end(tag); + return new RecordContainerDefinition(location); + } + + @override + void writeToDataSink(DataSink sink) { + sink.writeEnum(ClassKind.record); + sink.begin(tag); + sink.writeSourceSpan(location); + sink.end(tag); + } + ClassKind get kind => ClassKind.record; ir.Node get node => throw new UnsupportedError( diff --git a/pkg/compiler/lib/src/js_model/element_map.dart b/pkg/compiler/lib/src/js_model/element_map.dart index 13a6beae20559..22a306b01ba49 100644 --- a/pkg/compiler/lib/src/js_model/element_map.dart +++ b/pkg/compiler/lib/src/js_model/element_map.dart @@ -18,11 +18,13 @@ import '../js_emitter/code_emitter_task.dart'; import '../js_model/closure.dart' show JRecordField, KernelScopeInfo; import '../js_model/elements.dart' show JGeneratorBody; import '../native/native.dart' as native; +import '../serialization/serialization.dart'; import '../ssa/type_builder.dart'; import '../types/abstract_value_domain.dart'; import '../universe/call_structure.dart'; import '../universe/selector.dart'; import '../world.dart'; +import 'closure.dart'; /// Interface that translates between Kernel IR nodes and entities used for /// global type inference and building the SSA graph for members. @@ -273,6 +275,9 @@ abstract class KernelToLocalsMap { /// Returns the [JumpTarget] defined by the while statement [node] or `null` /// if [node] is not a jump target. JumpTarget getJumpTargetForWhile(ir.WhileStatement node); + + /// Serializes this [KernelToLocalsMap] to [sink]. + void writeToDataSink(DataSink sink); } /// Returns the [ir.FunctionNode] that defines [member] or `null` if [member] @@ -346,6 +351,27 @@ abstract class MemberDefinition { /// The canonical location of [member]. This is used for sorting the members /// in the emitted code. SourceSpan get location; + + /// Deserializes a [MemberDefinition] object from [source]. + factory MemberDefinition.readFromDataSource(DataSource source) { + MemberKind kind = source.readEnum(MemberKind.values); + switch (kind) { + case MemberKind.regular: + return new RegularMemberDefinition.readFromDataSource(source); + case MemberKind.constructor: + case MemberKind.constructorBody: + case MemberKind.signature: + case MemberKind.generatorBody: + return new SpecialMemberDefinition.readFromDataSource(source, kind); + case MemberKind.closureCall: + case MemberKind.closureField: + return new ClosureMemberDefinition.readFromDataSource(source, kind); + } + throw new UnsupportedError("Unexpected MemberKind $kind"); + } + + /// Serializes this [MemberDefinition] to [sink]. + void writeToDataSink(DataSink sink); } enum ClassKind { @@ -358,10 +384,29 @@ enum ClassKind { /// A member directly defined by its [ir.Member] node. class RegularMemberDefinition implements MemberDefinition { + /// Tag used for identifying serialized [RegularMemberDefinition] objects in a + /// debugging data stream. + static const String tag = 'regular-member-definition'; + final ir.Member node; RegularMemberDefinition(this.node); + factory RegularMemberDefinition.readFromDataSource(DataSource source) { + source.begin(tag); + ir.Member node = source.readMemberNode(); + source.end(tag); + return new RegularMemberDefinition(node); + } + + @override + void writeToDataSink(DataSink sink) { + sink.writeEnum(MemberKind.regular); + sink.begin(tag); + sink.writeMemberNode(node); + sink.end(tag); + } + SourceSpan get location => computeSourceSpanFromTreeNode(node); MemberKind get kind => MemberKind.regular; @@ -372,11 +417,31 @@ class RegularMemberDefinition implements MemberDefinition { /// The definition of a special kind of member class SpecialMemberDefinition implements MemberDefinition { + /// Tag used for identifying serialized [SpecialMemberDefinition] objects in a + /// debugging data stream. + static const String tag = 'special-member-definition'; + final ir.TreeNode node; final MemberKind kind; SpecialMemberDefinition(this.node, this.kind); + factory SpecialMemberDefinition.readFromDataSource( + DataSource source, MemberKind kind) { + source.begin(tag); + ir.TreeNode node = source.readTreeNode(); + source.end(tag); + return new SpecialMemberDefinition(node, kind); + } + + @override + void writeToDataSink(DataSink sink) { + sink.writeEnum(kind); + sink.begin(tag); + sink.writeTreeNode(node); + sink.end(tag); + } + SourceSpan get location => computeSourceSpanFromTreeNode(node); String toString() => 'SpecialMemberDefinition(kind:$kind,' @@ -394,14 +459,49 @@ abstract class ClassDefinition { /// The canonical location of [cls]. This is used for sorting the classes /// in the emitted code. SourceSpan get location; + + /// Deserializes a [ClassDefinition] object from [source]. + factory ClassDefinition.readFromDataSource(DataSource source) { + ClassKind kind = source.readEnum(ClassKind.values); + switch (kind) { + case ClassKind.regular: + return new RegularClassDefinition.readFromDataSource(source); + case ClassKind.closure: + return new ClosureClassDefinition.readFromDataSource(source); + case ClassKind.record: + return new RecordContainerDefinition.readFromDataSource(source); + } + throw new UnsupportedError("Unexpected ClassKind $kind"); + } + + /// Serializes this [ClassDefinition] to [sink]. + void writeToDataSink(DataSink sink); } /// A class directly defined by its [ir.Class] node. class RegularClassDefinition implements ClassDefinition { + /// Tag used for identifying serialized [RegularClassDefinition] objects in a + /// debugging data stream. + static const String tag = 'regular-class-definition'; + final ir.Class node; RegularClassDefinition(this.node); + factory RegularClassDefinition.readFromDataSource(DataSource source) { + source.begin(tag); + ir.Class node = source.readClassNode(); + source.end(tag); + return new RegularClassDefinition(node); + } + + void writeToDataSink(DataSink sink) { + sink.writeEnum(kind); + sink.begin(tag); + sink.writeClassNode(node); + sink.end(tag); + } + SourceSpan get location => computeSourceSpanFromTreeNode(node); ClassKind get kind => ClassKind.regular; 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 bc3122a868d41..dccda12a41d01 100644 --- a/pkg/compiler/lib/src/js_model/element_map_impl.dart +++ b/pkg/compiler/lib/src/js_model/element_map_impl.dart @@ -43,6 +43,7 @@ import '../kernel/kelements.dart'; import '../native/native.dart' as native; import '../options.dart'; import '../ordered_typeset.dart'; +import '../serialization/serialization.dart'; import '../ssa/type_builder.dart'; import '../universe/call_structure.dart'; import '../universe/selector.dart'; @@ -1442,6 +1443,23 @@ class JsEvaluationEnvironment extends EvaluationEnvironmentBase { class JsKernelToElementMap extends JsToElementMapBase with JsElementCreatorMixin implements JsToWorldBuilder, JsToElementMap { + /// Tag used for identifying serialized [JsKernelToElementMap] objects in a + /// debugging data stream. + static const String tag = 'js-kernel-to-element-map'; + + /// Tags used for identifying serialized subsections of a + /// [JsKernelToElementMap] object in a debugging data stream. + static const String libraryTag = 'libraries'; + static const String classTag = 'classes'; + static const String typedefTag = 'typedefs'; + static const String memberTag = 'members'; + static const String typeVariableTag = 'type-variables'; + static const String libraryDataTag = 'library-data'; + static const String classDataTag = 'class-data'; + static const String typedefDataTag = 'typedef-data'; + static const String memberDataTag = 'member-data'; + static const String typeVariableDataTag = 'type-variable-data'; + final Map libraryMap = {}; final Map classMap = {}; final Map typedefMap = {}; @@ -1574,7 +1592,6 @@ class JsKernelToElementMap extends JsToElementMapBase assert(newTypeVariable.typeVariableIndex == oldTypeVariable.typeVariableIndex); } - //typeVariableMap.keys.forEach((n) => print(n.parent)); // TODO(johnniwinther): We should close the environment in the beginning of // this constructor but currently we need the [MemberEntity] to query if the // member is live, thus potentially creating the [MemberEntity] in the @@ -1582,6 +1599,207 @@ class JsKernelToElementMap extends JsToElementMapBase _elementMap.envIsClosed = true; } + JsKernelToElementMap.readFromDataSource( + CompilerOptions options, + DiagnosticReporter reporter, + Environment environment, + ir.Component component, + DataSource source) + : super(options, reporter, environment) { + source.registerComponentLookup(new ComponentLookup(component)); + _EntityLookup entityLookup = new _EntityLookup(); + source.registerEntityLookup(entityLookup); + + source.begin(tag); + source.begin(libraryTag); + int libraryCount = source.readInt(); + for (int i = 0; i < libraryCount; i++) { + int index = source.readInt(); + JLibrary library = new JLibrary.readFromDataSource(source); + entityLookup.registerLibrary(index, library); + } + source.end(libraryTag); + + source.begin(classTag); + int classCount = source.readInt(); + for (int i = 0; i < classCount; i++) { + int index = source.readInt(); + JClass cls = new JClass.readFromDataSource(source); + entityLookup.registerClass(index, cls); + } + source.end(classTag); + + source.begin(typedefTag); + int typedefCount = source.readInt(); + for (int i = 0; i < typedefCount; i++) { + int index = source.readInt(); + JTypedef typedef = new JTypedef.readFromDataSource(source); + entityLookup.registerTypedef(index, typedef); + } + source.end(typedefTag); + + source.begin(memberTag); + int memberCount = source.readInt(); + for (int i = 0; i < memberCount; i++) { + int index = source.readInt(); + JMember member = new JMember.readFromDataSource(source); + entityLookup.registerMember(index, member); + } + source.end(memberTag); + + source.begin(typeVariableTag); + int typeVariableCount = source.readInt(); + for (int i = 0; i < typeVariableCount; i++) { + int index = source.readInt(); + JTypeVariable typeVariable = new JTypeVariable.readFromDataSource(source); + entityLookup.registerTypeVariable(index, typeVariable); + } + source.end(typeVariableTag); + + programEnv = new JProgramEnv([component]); + source.begin(libraryDataTag); + entityLookup.forEachLibrary((int index, JLibrary library) { + JLibraryEnv env = new JLibraryEnv.readFromDataSource(source); + JLibraryData data = new JLibraryData.readFromDataSource(source); + libraryMap[env.library] = + libraries.registerByIndex(index, library, data, env); + programEnv.registerLibrary(env); + assert(index == library.libraryIndex); + }); + source.end(libraryDataTag); + + source.begin(classDataTag); + entityLookup.forEachClass((int index, JClass cls) { + JClassEnv env = new JClassEnv.readFromDataSource(source); + JClassData data = new JClassData.readFromDataSource(source); + classMap[env.cls] = classes.registerByIndex(index, cls, data, env); + libraries.getEnv(cls.library).registerClass(cls.name, env); + assert(index == cls.classIndex); + }); + source.end(classDataTag); + + source.begin(typedefDataTag); + entityLookup.forEachTypedef((int index, JTypedef typedef) { + JTypedefData data = new JTypedefData.readFromDataSource(source); + typedefs.registerByIndex(index, typedef, data); + assert(index == typedef.typedefIndex); + }); + source.end(typedefDataTag); + + source.begin(memberDataTag); + entityLookup.forEachMember((int index, IndexedMember member) { + JMemberData data = new JMemberData.readFromDataSource(source); + members.registerByIndex(index, member, data); + switch (data.definition.kind) { + case MemberKind.regular: + case MemberKind.constructor: + ir.Member node = data.definition.node; + if (member.isField) { + fieldMap[node] = member; + } else if (member.isConstructor) { + constructorMap[node] = member; + } else { + methodMap[node] = member; + } + break; + default: + } + assert(index == member.memberIndex); + }); + source.end(memberDataTag); + + source.begin(typeVariableDataTag); + entityLookup.forEachTypeVariable((int index, JTypeVariable typeVariable) { + JTypeVariableData data = new JTypeVariableData.readFromDataSource(source); + typeVariableMap[data.node] = + typeVariables.registerByIndex(index, typeVariable, data); + assert(index == typeVariable.typeVariableIndex); + }); + source.end(typeVariableDataTag); + source.end(tag); + } + + /// Serializes this [JsToElementMap] to [sink]. + void writeToDataSink(DataSink sink) { + sink.begin(tag); + + // Serialize the entities before serializing the data. + sink.begin(libraryTag); + sink.writeInt(libraries.size); + libraries.forEach((JLibrary library, _, __) { + sink.writeInt(library.libraryIndex); + library.writeToDataSink(sink); + }); + sink.end(libraryTag); + + sink.begin(classTag); + sink.writeInt(classes.size); + classes.forEach((JClass cls, _, __) { + sink.writeInt(cls.classIndex); + cls.writeToDataSink(sink); + }); + sink.end(classTag); + + sink.begin(typedefTag); + sink.writeInt(typedefs.size); + typedefs.forEach((JTypedef typedef, _) { + sink.writeInt(typedef.typedefIndex); + typedef.writeToDataSink(sink); + }); + sink.end(typedefTag); + + sink.begin(memberTag); + sink.writeInt(members.size); + members.forEach((JMember member, _) { + sink.writeInt(member.memberIndex); + member.writeToDataSink(sink); + }); + sink.end(memberTag); + + sink.begin(typeVariableTag); + sink.writeInt(typeVariables.size); + typeVariables.forEach((JTypeVariable typeVariable, _) { + sink.writeInt(typeVariable.typeVariableIndex); + typeVariable.writeToDataSink(sink); + }); + sink.end(typeVariableTag); + + // Serialize the entity data after having serialized the entities. + sink.begin(libraryDataTag); + libraries.forEach((_, JLibraryData data, JLibraryEnv env) { + env.writeToDataSink(sink); + data.writeToDataSink(sink); + }); + sink.end(libraryDataTag); + + sink.begin(classDataTag); + classes.forEach((_, JClassData data, JClassEnv env) { + env.writeToDataSink(sink); + data.writeToDataSink(sink); + }); + sink.end(classDataTag); + + sink.begin(typedefDataTag); + typedefs.forEach((_, JTypedefData data) { + data.writeToDataSink(sink); + }); + sink.end(typedefDataTag); + + sink.begin(memberDataTag); + members.forEach((_, JMemberData data) { + data.writeToDataSink(sink); + }); + sink.end(memberDataTag); + + sink.begin(typeVariableDataTag); + typeVariables.forEach((_, JTypeVariableData data) { + data.writeToDataSink(sink); + }); + sink.end(typeVariableDataTag); + + sink.end(tag); + } + @override void forEachNestedClosure( MemberEntity member, void f(FunctionEntity closure)) { @@ -2223,3 +2441,101 @@ class JsToFrontendMapImpl extends JsToFrontendMapBase .getEntity(indexedTypeVariable.typeVariableIndex); } } + +/// [EntityLookup] implementation used to deserialize [JsKernelToElementMap]. +/// +/// Since data objects and environments are registered together with their +/// entity we need to have a separate lookup-by-index mechanism to allow for +/// index-based reference within data objects and environments. +class _EntityLookup implements EntityLookup { + final Map _libraries = {}; + final Map _classes = {}; + final Map _typedefs = {}; + final Map _members = {}; + final Map _typeVariables = {}; + + void registerLibrary(int index, JLibrary library) { + assert(!_libraries.containsKey(index), + "Library for index $index has already been defined."); + _libraries[index] = library; + } + + void registerClass(int index, JClass cls) { + assert(!_classes.containsKey(index), + "Class for index $index has already been defined."); + _classes[index] = cls; + } + + void registerTypedef(int index, JTypedef typedef) { + assert(!_typedefs.containsKey(index), + "Typedef for index $index has already been defined."); + _typedefs[index] = typedef; + } + + void registerMember(int index, JMember member) { + assert(!_members.containsKey(index), + "Member for index $index has already been defined."); + _members[index] = member; + } + + void registerTypeVariable(int index, JTypeVariable typeVariable) { + assert(!_typeVariables.containsKey(index), + "Type variable for index $index has already been defined."); + _typeVariables[index] = typeVariable; + } + + void forEachLibrary(void f(int index, JLibrary library)) { + _libraries.forEach(f); + } + + void forEachClass(void f(int index, JClass cls)) { + _classes.forEach(f); + } + + void forEachTypedef(void f(int index, JTypedef typedef)) { + _typedefs.forEach(f); + } + + void forEachMember(void f(int index, JMember member)) { + _members.forEach(f); + } + + void forEachTypeVariable(void f(int index, JTypeVariable typeVariable)) { + _typeVariables.forEach(f); + } + + @override + IndexedLibrary getLibraryByIndex(int index) { + IndexedLibrary library = _libraries[index]; + assert(library != null, "No library found for index $index"); + return library; + } + + @override + IndexedClass getClassByIndex(int index) { + IndexedClass cls = _classes[index]; + assert(cls != null, "No class found for index $index"); + return cls; + } + + @override + IndexedTypedef getTypedefByIndex(int index) { + IndexedTypedef typedef = _typedefs[index]; + assert(typedef != null, "No typedef found for index $index"); + return typedef; + } + + @override + IndexedMember getMemberByIndex(int index) { + IndexedMember member = _members[index]; + assert(member != null, "No member found for index $index"); + return member; + } + + @override + IndexedTypeVariable getTypeVariableByIndex(int index) { + IndexedTypeVariable typeVariable = _typeVariables[index]; + assert(typeVariable != null, "No type variable found for index $index"); + return typeVariable; + } +} diff --git a/pkg/compiler/lib/src/js_model/elements.dart b/pkg/compiler/lib/src/js_model/elements.dart index c3577022b6c21..f964ef927781b 100644 --- a/pkg/compiler/lib/src/js_model/elements.dart +++ b/pkg/compiler/lib/src/js_model/elements.dart @@ -9,7 +9,9 @@ import '../elements/entities.dart'; import '../elements/indexed.dart'; import '../elements/names.dart'; import '../elements/types.dart'; +import '../serialization/serialization.dart'; import '../universe/class_set.dart' show ClassHierarchyNodesMapKey; +import 'closure.dart'; /// Map from 'frontend' to 'backend' elements. /// @@ -318,15 +320,43 @@ class TypeConverter implements DartTypeVisitor { const String jsElementPrefix = 'j:'; class JLibrary extends IndexedLibrary { + /// Tag used for identifying serialized [JLibrary] objects in a + /// debugging data stream. + static const String tag = 'library'; + final String name; final Uri canonicalUri; JLibrary(this.name, this.canonicalUri); + /// Deserializes a [JLibrary] object from [source]. + factory JLibrary.readFromDataSource(DataSource source) { + source.begin(tag); + String name = source.readString(); + Uri canonicalUri = source.readUri(); + source.end(tag); + return new JLibrary(name, canonicalUri); + } + + /// Serializes this [JLibrary] to [sink]. + void writeToDataSink(DataSink sink) { + sink.begin(tag); + sink.writeString(name); + sink.writeUri(canonicalUri); + sink.end(tag); + } + String toString() => '${jsElementPrefix}library($name)'; } +/// Enum used for identifying [JClass] subclasses in serialization. +enum JClassKind { node, closure, record } + class JClass extends IndexedClass with ClassHierarchyNodesMapKey { + /// Tag used for identifying serialized [JClass] objects in a + /// debugging data stream. + static const String tag = 'class'; + final JLibrary library; final String name; @@ -334,6 +364,35 @@ class JClass extends IndexedClass with ClassHierarchyNodesMapKey { JClass(this.library, this.name, {this.isAbstract}); + /// Deserializes a [JClass] object from [source]. + factory JClass.readFromDataSource(DataSource source) { + JClassKind kind = source.readEnum(JClassKind.values); + switch (kind) { + case JClassKind.node: + source.begin(tag); + JLibrary library = source.readLibrary(); + String name = source.readString(); + bool isAbstract = source.readBool(); + source.end(tag); + return new JClass(library, name, isAbstract: isAbstract); + case JClassKind.closure: + return new JClosureClass.readFromDataSource(source); + case JClassKind.record: + return new JRecord.readFromDataSource(source); + } + throw new UnsupportedError("Unexpected ClassKind $kind"); + } + + /// Serializes this [JClass] to [sink]. + void writeToDataSink(DataSink sink) { + sink.writeEnum(JClassKind.node); + sink.begin(tag); + sink.writeLibrary(library); + sink.writeString(name); + sink.writeBool(isAbstract); + sink.end(tag); + } + @override bool get isClosure => false; @@ -341,15 +400,52 @@ class JClass extends IndexedClass with ClassHierarchyNodesMapKey { } class JTypedef extends IndexedTypedef { + /// Tag used for identifying serialized [JTypedef] objects in a + /// debugging data stream. + static const String tag = 'typedef'; + final JLibrary library; final String name; JTypedef(this.library, this.name); + /// Deserializes a [JTypedef] object from [source]. + factory JTypedef.readFromDataSource(DataSource source) { + source.begin(tag); + JLibrary library = source.readLibrary(); + String name = source.readString(); + source.end(tag); + return new JTypedef(library, name); + } + + /// Serializes this [JTypedef] to [sink]. + void writeToDataSink(DataSink sink) { + sink.begin(tag); + sink.writeLibrary(library); + sink.writeString(name); + sink.end(tag); + } + String toString() => '${jsElementPrefix}typedef($name)'; } +/// Enum used for identifying [JMember] subclasses in serialization. +enum JMemberKind { + generativeConstructor, + factoryConstructor, + constructorBody, + field, + getter, + setter, + method, + closureField, + closureCallMethod, + generatorBody, + signatureMethod, + recordField, +} + abstract class JMember extends IndexedMember { final JLibrary library; final JClass enclosingClass; @@ -359,6 +455,41 @@ abstract class JMember extends IndexedMember { JMember(this.library, this.enclosingClass, this._name, {bool isStatic: false}) : _isStatic = isStatic; + /// Deserializes a [JMember] object from [source]. + factory JMember.readFromDataSource(DataSource source) { + JMemberKind kind = source.readEnum(JMemberKind.values); + switch (kind) { + case JMemberKind.generativeConstructor: + return new JGenerativeConstructor.readFromDataSource(source); + case JMemberKind.factoryConstructor: + return new JFactoryConstructor.readFromDataSource(source); + case JMemberKind.constructorBody: + return new JConstructorBody.readFromDataSource(source); + case JMemberKind.field: + return new JField.readFromDataSource(source); + case JMemberKind.getter: + return new JGetter.readFromDataSource(source); + case JMemberKind.setter: + return new JSetter.readFromDataSource(source); + case JMemberKind.method: + return new JMethod.readFromDataSource(source); + case JMemberKind.closureField: + return new JClosureField.readFromDataSource(source); + case JMemberKind.closureCallMethod: + return new JClosureCallMethod.readFromDataSource(source); + case JMemberKind.generatorBody: + return new JGeneratorBody.readFromDataSource(source); + case JMemberKind.signatureMethod: + return new JSignatureMethod.readFromDataSource(source); + case JMemberKind.recordField: + return new JRecordField.readFromDataSource(source); + } + throw new UnsupportedError("Unexpected JMemberKind $kind"); + } + + /// Serializes this [JMember] to [sink]. + void writeToDataSink(DataSink sink); + String get name => _name.text; Name get memberName => _name; @@ -444,12 +575,42 @@ abstract class JConstructor extends JFunction } class JGenerativeConstructor extends JConstructor { + /// Tag used for identifying serialized [JGenerativeConstructor] objects in a + /// debugging data stream. + static const String tag = 'generative-constructor'; + JGenerativeConstructor( JClass enclosingClass, Name name, ParameterStructure parameterStructure, {bool isExternal, bool isConst}) : super(enclosingClass, name, parameterStructure, isExternal: isExternal, isConst: isConst); + factory JGenerativeConstructor.readFromDataSource(DataSource source) { + source.begin(tag); + JClass enclosingClass = source.readClass(); + String name = source.readString(); + ParameterStructure parameterStructure = + new ParameterStructure.readFromDataSource(source); + bool isExternal = source.readBool(); + bool isConst = source.readBool(); + source.end(tag); + return new JGenerativeConstructor(enclosingClass, + new Name(name, enclosingClass.library), parameterStructure, + isExternal: isExternal, isConst: isConst); + } + + @override + void writeToDataSink(DataSink sink) { + sink.writeEnum(JMemberKind.generativeConstructor); + sink.begin(tag); + sink.writeClass(enclosingClass); + sink.writeString(name); + parameterStructure.writeToDataSink(sink); + sink.writeBool(isExternal); + sink.writeBool(isConst); + sink.end(tag); + } + @override bool get isFactoryConstructor => false; @@ -458,6 +619,10 @@ class JGenerativeConstructor extends JConstructor { } class JFactoryConstructor extends JConstructor { + /// Tag used for identifying serialized [JFactoryConstructor] objects in a + /// debugging data stream. + static const String tag = 'factory-constructor'; + @override final bool isFromEnvironmentConstructor; @@ -467,6 +632,36 @@ class JFactoryConstructor extends JConstructor { : super(enclosingClass, name, parameterStructure, isExternal: isExternal, isConst: isConst); + factory JFactoryConstructor.readFromDataSource(DataSource source) { + source.begin(tag); + JClass enclosingClass = source.readClass(); + String name = source.readString(); + ParameterStructure parameterStructure = + new ParameterStructure.readFromDataSource(source); + bool isExternal = source.readBool(); + bool isConst = source.readBool(); + bool isFromEnvironmentConstructor = source.readBool(); + source.end(tag); + return new JFactoryConstructor(enclosingClass, + new Name(name, enclosingClass.library), parameterStructure, + isExternal: isExternal, + isConst: isConst, + isFromEnvironmentConstructor: isFromEnvironmentConstructor); + } + + @override + void writeToDataSink(DataSink sink) { + sink.writeEnum(JMemberKind.factoryConstructor); + sink.begin(tag); + sink.writeClass(enclosingClass); + sink.writeString(name); + parameterStructure.writeToDataSink(sink); + sink.writeBool(isExternal); + sink.writeBool(isConst); + sink.writeBool(isFromEnvironmentConstructor); + sink.end(tag); + } + @override bool get isFactoryConstructor => true; @@ -475,7 +670,11 @@ class JFactoryConstructor extends JConstructor { } class JConstructorBody extends JFunction implements ConstructorBodyEntity { - final ConstructorEntity constructor; + /// Tag used for identifying serialized [JConstructorBody] objects in a + /// debugging data stream. + static const String tag = 'constructor-body'; + + final JConstructor constructor; JConstructorBody(this.constructor) : super( @@ -487,10 +686,29 @@ class JConstructorBody extends JFunction implements ConstructorBodyEntity { isStatic: false, isExternal: false); + factory JConstructorBody.readFromDataSource(DataSource source) { + source.begin(tag); + JConstructor constructor = source.readMember(); + source.end(tag); + return new JConstructorBody(constructor); + } + + @override + void writeToDataSink(DataSink sink) { + sink.writeEnum(JMemberKind.constructorBody); + sink.begin(tag); + sink.writeMember(constructor); + sink.end(tag); + } + String get _kind => 'constructor_body'; } class JMethod extends JFunction { + /// Tag used for identifying serialized [JMethod] objects in a + /// debugging data stream. + static const String tag = 'method'; + final bool isAbstract; JMethod(JLibrary library, JClass enclosingClass, Name name, @@ -499,6 +717,53 @@ class JMethod extends JFunction { : super(library, enclosingClass, name, parameterStructure, asyncMarker, isStatic: isStatic, isExternal: isExternal); + factory JMethod.readFromDataSource(DataSource source) { + source.begin(tag); + MemberContextKind kind = source.readEnum(MemberContextKind.values); + JLibrary library; + JClass enclosingClass; + switch (kind) { + case MemberContextKind.library: + library = source.readLibrary(); + break; + case MemberContextKind.cls: + enclosingClass = source.readClass(); + library = enclosingClass.library; + break; + } + String name = source.readString(); + ParameterStructure parameterStructure = + new ParameterStructure.readFromDataSource(source); + AsyncMarker asyncMarker = source.readEnum(AsyncMarker.values); + bool isStatic = source.readBool(); + bool isExternal = source.readBool(); + bool isAbstract = source.readBool(); + source.end(tag); + return new JMethod(library, enclosingClass, new Name(name, library), + parameterStructure, asyncMarker, + isStatic: isStatic, isExternal: isExternal, isAbstract: isAbstract); + } + + @override + void writeToDataSink(DataSink sink) { + sink.writeEnum(JMemberKind.method); + sink.begin(tag); + if (enclosingClass != null) { + sink.writeEnum(MemberContextKind.cls); + sink.writeClass(enclosingClass); + } else { + sink.writeEnum(MemberContextKind.library); + sink.writeLibrary(library); + } + sink.writeString(name); + parameterStructure.writeToDataSink(sink); + sink.writeEnum(asyncMarker); + sink.writeBool(isStatic); + sink.writeBool(isExternal); + sink.writeBool(isAbstract); + sink.end(tag); + } + @override bool get isFunction => true; @@ -506,7 +771,11 @@ class JMethod extends JFunction { } class JGeneratorBody extends JFunction { - final FunctionEntity function; + /// Tag used for identifying serialized [JGeneratorBody] objects in a + /// debugging data stream. + static const String tag = 'generator-body'; + + final JFunction function; final DartType elementType; final int hashCode; @@ -516,10 +785,31 @@ class JGeneratorBody extends JFunction { function.parameterStructure, function.asyncMarker, isStatic: function.isStatic, isExternal: false); + factory JGeneratorBody.readFromDataSource(DataSource source) { + source.begin(tag); + JFunction function = source.readMember(); + DartType elementType = source.readDartType(); + source.end(tag); + return new JGeneratorBody(function, elementType); + } + + @override + void writeToDataSink(DataSink sink) { + sink.writeEnum(JMemberKind.generatorBody); + sink.begin(tag); + sink.writeMember(function); + sink.writeDartType(elementType); + sink.end(tag); + } + String get _kind => 'generator_body'; } class JGetter extends JFunction { + /// Tag used for identifying serialized [JGetter] objects in a + /// debugging data stream. + static const String tag = 'getter'; + final bool isAbstract; JGetter(JLibrary library, JClass enclosingClass, Name name, @@ -529,6 +819,50 @@ class JGetter extends JFunction { asyncMarker, isStatic: isStatic, isExternal: isExternal); + factory JGetter.readFromDataSource(DataSource source) { + source.begin(tag); + MemberContextKind kind = source.readEnum(MemberContextKind.values); + JLibrary library; + JClass enclosingClass; + switch (kind) { + case MemberContextKind.library: + library = source.readLibrary(); + break; + case MemberContextKind.cls: + enclosingClass = source.readClass(); + library = enclosingClass.library; + break; + } + String name = source.readString(); + AsyncMarker asyncMarker = source.readEnum(AsyncMarker.values); + bool isStatic = source.readBool(); + bool isExternal = source.readBool(); + bool isAbstract = source.readBool(); + source.end(tag); + return new JGetter( + library, enclosingClass, new Name(name, library), asyncMarker, + isStatic: isStatic, isExternal: isExternal, isAbstract: isAbstract); + } + + @override + void writeToDataSink(DataSink sink) { + sink.writeEnum(JMemberKind.getter); + sink.begin(tag); + if (enclosingClass != null) { + sink.writeEnum(MemberContextKind.cls); + sink.writeClass(enclosingClass); + } else { + sink.writeEnum(MemberContextKind.library); + sink.writeLibrary(library); + } + sink.writeString(name); + sink.writeEnum(asyncMarker); + sink.writeBool(isStatic); + sink.writeBool(isExternal); + sink.writeBool(isAbstract); + sink.end(tag); + } + @override bool get isGetter => true; @@ -536,6 +870,10 @@ class JGetter extends JFunction { } class JSetter extends JFunction { + /// Tag used for identifying serialized [JSetter] objects in a + /// debugging data stream. + static const String tag = 'setter'; + final bool isAbstract; JSetter(JLibrary library, JClass enclosingClass, Name name, @@ -544,6 +882,47 @@ class JSetter extends JFunction { AsyncMarker.SYNC, isStatic: isStatic, isExternal: isExternal); + factory JSetter.readFromDataSource(DataSource source) { + source.begin(tag); + MemberContextKind kind = source.readEnum(MemberContextKind.values); + JLibrary library; + JClass enclosingClass; + switch (kind) { + case MemberContextKind.library: + library = source.readLibrary(); + break; + case MemberContextKind.cls: + enclosingClass = source.readClass(); + library = enclosingClass.library; + break; + } + String name = source.readString(); + bool isStatic = source.readBool(); + bool isExternal = source.readBool(); + bool isAbstract = source.readBool(); + source.end(tag); + return new JSetter(library, enclosingClass, new Name(name, library), + isStatic: isStatic, isExternal: isExternal, isAbstract: isAbstract); + } + + @override + void writeToDataSink(DataSink sink) { + sink.writeEnum(JMemberKind.setter); + sink.begin(tag); + if (enclosingClass != null) { + sink.writeEnum(MemberContextKind.cls); + sink.writeClass(enclosingClass); + } else { + sink.writeEnum(MemberContextKind.library); + sink.writeLibrary(library); + } + sink.writeString(name); + sink.writeBool(isStatic); + sink.writeBool(isExternal); + sink.writeBool(isAbstract); + sink.end(tag); + } + @override bool get isAssignable => true; @@ -554,6 +933,10 @@ class JSetter extends JFunction { } class JField extends JMember implements FieldEntity, IndexedField { + /// Tag used for identifying serialized [JField] objects in a + /// debugging data stream. + static const String tag = 'field'; + final bool isAssignable; final bool isConst; @@ -561,6 +944,47 @@ class JField extends JMember implements FieldEntity, IndexedField { {bool isStatic, this.isAssignable, this.isConst}) : super(library, enclosingClass, name, isStatic: isStatic); + factory JField.readFromDataSource(DataSource source) { + source.begin(tag); + MemberContextKind kind = source.readEnum(MemberContextKind.values); + JLibrary library; + JClass enclosingClass; + switch (kind) { + case MemberContextKind.library: + library = source.readLibrary(); + break; + case MemberContextKind.cls: + enclosingClass = source.readClass(); + library = enclosingClass.library; + break; + } + String name = source.readString(); + bool isStatic = source.readBool(); + bool isAssignable = source.readBool(); + bool isConst = source.readBool(); + source.end(tag); + return new JField(library, enclosingClass, new Name(name, library), + isStatic: isStatic, isAssignable: isAssignable, isConst: isConst); + } + + @override + void writeToDataSink(DataSink sink) { + sink.writeEnum(JMemberKind.field); + sink.begin(tag); + if (enclosingClass != null) { + sink.writeEnum(MemberContextKind.cls); + sink.writeClass(enclosingClass); + } else { + sink.writeEnum(MemberContextKind.library); + sink.writeLibrary(library); + } + sink.writeString(name); + sink.writeBool(isStatic); + sink.writeBool(isAssignable); + sink.writeBool(isConst); + sink.end(tag); + } + @override bool get isField => true; @@ -568,33 +992,137 @@ class JField extends JMember implements FieldEntity, IndexedField { } class JClosureCallMethod extends JMethod { + /// Tag used for identifying serialized [JClosureCallMethod] objects in a + /// debugging data stream. + static const String tag = 'closure-call-method'; + JClosureCallMethod(ClassEntity enclosingClass, ParameterStructure parameterStructure, AsyncMarker asyncMarker) : super(enclosingClass.library, enclosingClass, Names.call, parameterStructure, asyncMarker, isStatic: false, isExternal: false, isAbstract: false); + factory JClosureCallMethod.readFromDataSource(DataSource source) { + source.begin(tag); + JClass enclosingClass = source.readClass(); + ParameterStructure parameterStructure = + new ParameterStructure.readFromDataSource(source); + AsyncMarker asyncMarker = source.readEnum(AsyncMarker.values); + source.end(tag); + return new JClosureCallMethod( + enclosingClass, parameterStructure, asyncMarker); + } + + @override + void writeToDataSink(DataSink sink) { + sink.writeEnum(JMemberKind.closureCallMethod); + sink.begin(tag); + sink.writeClass(enclosingClass); + parameterStructure.writeToDataSink(sink); + sink.writeEnum(asyncMarker); + sink.end(tag); + } + String get _kind => 'closure_call'; } /// A method that returns the signature of the Dart closure/tearoff that this /// method's parent class is representing. class JSignatureMethod extends JMethod { + /// Tag used for identifying serialized [JSignatureMethod] objects in a + /// debugging data stream. + static const String tag = 'signature-method'; + JSignatureMethod(ClassEntity enclosingClass) : super(enclosingClass.library, enclosingClass, Names.signature, const ParameterStructure(0, 0, const [], 0), AsyncMarker.SYNC, isStatic: false, isExternal: false, isAbstract: false); + factory JSignatureMethod.readFromDataSource(DataSource source) { + source.begin(tag); + JClass cls = source.readClass(); + source.end(tag); + return new JSignatureMethod(cls); + } + + void writeToDataSink(DataSink sink) { + sink.writeEnum(JMemberKind.signatureMethod); + sink.begin(tag); + sink.writeClass(enclosingClass); + sink.end(tag); + } + String get _kind => 'signature'; } +/// Enum used for identifying [JTypeVariable] variants in serialization. +enum JTypeVariableKind { cls, member, typedef, local } + class JTypeVariable extends IndexedTypeVariable { + /// Tag used for identifying serialized [JTypeVariable] objects in a + /// debugging data stream. + static const String tag = 'type-variable'; + final Entity typeDeclaration; final String name; final int index; JTypeVariable(this.typeDeclaration, this.name, this.index); + /// Deserializes a [JTypeVariable] object from [source]. + factory JTypeVariable.readFromDataSource(DataSource source) { + source.begin(tag); + JTypeVariableKind kind = source.readEnum(JTypeVariableKind.values); + Entity typeDeclaration; + switch (kind) { + case JTypeVariableKind.cls: + typeDeclaration = source.readClass(); + break; + case JTypeVariableKind.member: + typeDeclaration = source.readMember(); + break; + case JTypeVariableKind.typedef: + typeDeclaration = source.readTypedef(); + break; + case JTypeVariableKind.local: + // Type variables declared by local functions don't point to their + // declaration, since the corresponding closure call methods is created + // after the type variable. + // TODO(johnniwinther): Fix this. + break; + } + String name = source.readString(); + int index = source.readInt(); + source.end(tag); + return new JTypeVariable(typeDeclaration, name, index); + } + + /// Serializes this [JTypeVariable] to [sink]. + void writeToDataSink(DataSink sink) { + sink.begin(tag); + if (typeDeclaration is IndexedClass) { + IndexedClass cls = typeDeclaration; + sink.writeEnum(JTypeVariableKind.cls); + sink.writeClass(cls); + } else if (typeDeclaration is IndexedMember) { + IndexedMember member = typeDeclaration; + sink.writeEnum(JTypeVariableKind.member); + sink.writeMember(member); + } else if (typeDeclaration is IndexedTypedef) { + IndexedTypedef typedef = typeDeclaration; + sink.writeEnum(JTypeVariableKind.typedef); + sink.writeTypedef(typedef); + } else if (typeDeclaration == null) { + sink.writeEnum(JTypeVariableKind.local); + } else { + throw new UnsupportedError( + "Unexpected type variable declarer $typeDeclaration."); + } + sink.writeString(name); + sink.writeInt(index); + sink.end(tag); + } + String toString() => '${jsElementPrefix}type_variable(${typeDeclaration.name}.$name)'; } diff --git a/pkg/compiler/lib/src/js_model/env.dart b/pkg/compiler/lib/src/js_model/env.dart index d2e4f8ce8700e..876106a2446e2 100644 --- a/pkg/compiler/lib/src/js_model/env.dart +++ b/pkg/compiler/lib/src/js_model/env.dart @@ -18,14 +18,16 @@ import '../ir/visitors.dart'; import '../ir/util.dart'; import '../js_model/element_map.dart'; import '../ordered_typeset.dart'; +import '../serialization/serialization.dart'; import '../ssa/type_builder.dart'; +import 'closure.dart'; import 'element_map.dart'; import 'element_map_impl.dart'; import 'elements.dart'; /// Environment for fast lookup of component libraries. class JProgramEnv { - final Set _components; + final Iterable _components; final Map _libraryMap = {}; JProgramEnv(this._components); @@ -57,6 +59,10 @@ class JProgramEnv { /// Environment for fast lookup of library classes and members. class JLibraryEnv { + /// Tag used for identifying serialized [JLibraryEnv] objects in a + /// debugging data stream. + static const String tag = 'library-env'; + final ir.Library library; final Map _classMap = {}; final Map _memberMap; @@ -64,6 +70,27 @@ class JLibraryEnv { JLibraryEnv(this.library, this._memberMap, this._setterMap); + /// Deserializes a [JLibraryEnv] object from [source]. + factory JLibraryEnv.readFromDataSource(DataSource source) { + source.begin(tag); + ir.Library library = source.readLibraryNode(); + Map memberMap = + source.readStringMap(source.readMemberNode); + Map setterMap = + source.readStringMap(source.readMemberNode); + source.end(tag); + return new JLibraryEnv(library, memberMap, setterMap); + } + + /// Serializes this [JLibraryEnv] to [sink]. + void writeToDataSink(DataSink sink) { + sink.begin(tag); + sink.writeLibraryNode(library); + sink.writeStringMap(_memberMap, sink.writeMemberNode); + sink.writeStringMap(_setterMap, sink.writeMemberNode); + sink.end(tag); + } + void registerClass(String name, JClassEnv classEnv) { _classMap[name] = classEnv; } @@ -96,16 +123,64 @@ class JLibraryEnv { } class JLibraryData { + /// Tag used for identifying serialized [JLibraryData] objects in a + /// debugging data stream. + static const String tag = 'library-data'; + final ir.Library library; // TODO(johnniwinther): Avoid direct access to [imports]. It might be null if // it hasn't been computed for the corresponding [KLibraryData]. final Map imports; JLibraryData(this.library, this.imports); + + factory JLibraryData.readFromDataSource(DataSource source) { + source.begin(tag); + ir.Library library = source.readLibraryNode(); + int importCount = source.readInt(); + Map imports; + if (importCount > 0) { + // TODO(johnniwinther): Deserialize imports. + } + source.end(tag); + return new JLibraryData(library, imports); + } + + void writeToDataSink(DataSink sink) { + sink.begin(tag); + sink.writeLibraryNode(library); + if (imports == null) { + sink.writeInt(0); + } else { + sink.writeInt(imports.length); + // TODO(johnniwinther): Serialize imports. + } + sink.end(tag); + } } +/// Enum used for identifying [JClassEnv] subclasses in serialization. +enum JClassEnvKind { node, closure, record } + /// Member data for a class. abstract class JClassEnv { + /// Deserializes a [JClassEnv] object from [source]. + factory JClassEnv.readFromDataSource(DataSource source) { + JClassEnvKind kind = source.readEnum(JClassEnvKind.values); + switch (kind) { + case JClassEnvKind.node: + return new JClassEnvImpl.readFromDataSource(source); + case JClassEnvKind.closure: + return new ClosureClassEnv.readFromDataSource(source); + case JClassEnvKind.record: + return new RecordEnv.readFromDataSource(source); + } + throw new UnsupportedError("Unsupported JClassEnvKind $kind"); + } + + /// Serializes this [JClassEnv] to [sink]. + void writeToDataSink(DataSink sink); + /// The [ir.Class] that defined the class, if any. ir.Class get cls; @@ -139,6 +214,10 @@ abstract class JClassEnv { /// Environment for fast lookup of class members. class JClassEnvImpl implements JClassEnv { + /// Tag used for identifying serialized [JClassEnv] objects in a + /// debugging data stream. + static const String tag = 'class-env'; + final ir.Class cls; final Map _constructorMap; final Map _memberMap; @@ -152,6 +231,35 @@ class JClassEnvImpl implements JClassEnv { JClassEnvImpl(this.cls, this._constructorMap, this._memberMap, this._setterMap, this._members, this.isSuperMixinApplication); + factory JClassEnvImpl.readFromDataSource(DataSource source) { + source.begin(tag); + ir.Class cls = source.readClassNode(); + Map constructorMap = + source.readStringMap(source.readMemberNode); + Map memberMap = + source.readStringMap(source.readMemberNode); + Map setterMap = + source.readStringMap(source.readMemberNode); + List members = source.readMemberNodes(); + bool isSuperMixinApplication = source.readBool(); + source.end(tag); + return new JClassEnvImpl(cls, constructorMap, memberMap, setterMap, members, + isSuperMixinApplication); + } + + @override + void writeToDataSink(DataSink sink) { + sink.writeEnum(JClassEnvKind.node); + sink.begin(tag); + sink.writeClassNode(cls); + sink.writeStringMap(_constructorMap, sink.writeMemberNode); + sink.writeStringMap(_memberMap, sink.writeMemberNode); + sink.writeStringMap(_setterMap, sink.writeMemberNode); + sink.writeMemberNodes(_members); + sink.writeBool(isSuperMixinApplication); + sink.end(tag); + } + bool get isUnnamedMixinApplication => cls.isAnonymousMixin; /// Return the [MemberEntity] for the member [name] in [cls]. If [setter] is @@ -195,10 +303,31 @@ class JClassEnvImpl implements JClassEnv { } class RecordEnv implements JClassEnv { + /// Tag used for identifying serialized [RecordEnv] objects in a + /// debugging data stream. + static const String tag = 'record-env'; + final Map _memberMap; RecordEnv(this._memberMap); + factory RecordEnv.readFromDataSource(DataSource source) { + source.begin(tag); + Map _memberMap = + source.readStringMap(() => source.readMember()); + source.end(tag); + return new RecordEnv(_memberMap); + } + + @override + void writeToDataSink(DataSink sink) { + sink.writeEnum(JClassEnvKind.record); + sink.begin(tag); + sink.writeStringMap( + _memberMap, (IndexedMember member) => sink.writeMember(member)); + sink.end(tag); + } + @override void forEachConstructorBody(void f(ConstructorBodyEntity constructor)) { // We do not create constructor bodies for containers. @@ -238,8 +367,29 @@ class RecordEnv implements JClassEnv { } class ClosureClassEnv extends RecordEnv { + /// Tag used for identifying serialized [ClosureClassEnv] objects in a + /// debugging data stream. + static const String tag = 'closure-class-env'; + ClosureClassEnv(Map memberMap) : super(memberMap); + factory ClosureClassEnv.readFromDataSource(DataSource source) { + source.begin(tag); + Map _memberMap = + source.readStringMap(() => source.readMember()); + source.end(tag); + return new ClosureClassEnv(_memberMap); + } + + @override + void writeToDataSink(DataSink sink) { + sink.writeEnum(JClassEnvKind.closure); + sink.begin(tag); + sink.writeStringMap( + _memberMap, (IndexedMember member) => sink.writeMember(member)); + sink.end(tag); + } + @override MemberEntity lookupMember(IrToElementMap elementMap, String name, {bool setter: false}) { @@ -251,7 +401,27 @@ class ClosureClassEnv extends RecordEnv { } } +/// Enum used for identifying [JClassData] subclasses in serialization. +enum JClassDataKind { node, closure, record } + abstract class JClassData { + /// Deserializes a [JClassData] object from [source]. + factory JClassData.readFromDataSource(DataSource source) { + JClassDataKind kind = source.readEnum(JClassDataKind.values); + switch (kind) { + case JClassDataKind.node: + return new JClassDataImpl.readFromDataSource(source); + case JClassDataKind.closure: + return new ClosureClassData.readFromDataSource(source); + case JClassDataKind.record: + return new RecordClassData.readFromDataSource(source); + } + throw new UnsupportedError("Unexpected JClassDataKind $kind"); + } + + /// Serializes this [JClassData] to [sink]. + void writeToDataSink(DataSink sink); + ClassDefinition get definition; InterfaceType get thisType; @@ -267,6 +437,10 @@ abstract class JClassData { } class JClassDataImpl implements JClassData { + /// Tag used for identifying serialized [JClassDataImpl] objects in a + /// debugging data stream. + static const String tag = 'class-data'; + final ir.Class cls; final ClassDefinition definition; bool isMixinApplication; @@ -281,17 +455,75 @@ class JClassDataImpl implements JClassData { JClassDataImpl(this.cls, this.definition); + factory JClassDataImpl.readFromDataSource(DataSource source) { + source.begin(tag); + ir.Class cls = source.readClassNode(); + ClassDefinition definition = new ClassDefinition.readFromDataSource(source); + source.end(tag); + return new JClassDataImpl(cls, definition); + } + + @override + void writeToDataSink(DataSink sink) { + sink.writeEnum(JClassDataKind.node); + sink.begin(tag); + sink.writeClassNode(cls); + definition.writeToDataSink(sink); + sink.end(tag); + } + bool get isEnumClass => cls != null && cls.isEnum; DartType get callType => null; } +/// Enum used for identifying [JMemberData] subclasses in serialization. +enum JMemberDataKind { + function, + field, + constructor, + constructorBody, + signature, + generatorBody, + closureFunction, + closureField, +} + abstract class JMemberData { MemberDefinition get definition; InterfaceType getMemberThisType(JsToElementMap elementMap); ClassTypeVariableAccess get classTypeVariableAccess; + + JMemberData(); + + /// Deserializes a [JMemberData] object from [source]. + factory JMemberData.readFromDataSource(DataSource source) { + JMemberDataKind kind = source.readEnum(JMemberDataKind.values); + switch (kind) { + case JMemberDataKind.function: + return new FunctionDataImpl.readFromDataSource(source); + case JMemberDataKind.field: + return new JFieldDataImpl.readFromDataSource(source); + case JMemberDataKind.constructor: + return new JConstructorDataImpl.readFromDataSource(source); + case JMemberDataKind.constructorBody: + return new ConstructorBodyDataImpl.readFromDataSource(source); + case JMemberDataKind.signature: + return new SignatureFunctionData.readFromDataSource(source); + case JMemberDataKind.generatorBody: + return new GeneratorBodyFunctionData.readFromDataSource(source); + case JMemberDataKind.closureFunction: + return new ClosureFunctionData.readFromDataSource(source); + case JMemberDataKind.closureField: + return new ClosureFieldData.readFromDataSource(source); + } + throw new UnsupportedError("Unexpected JMemberDataKind $kind"); + } + + /// Serializes this [JMemberData] to [sink]. + void writeToDataSink(DataSink sink); } abstract class JMemberDataImpl implements JMemberData { @@ -351,6 +583,10 @@ abstract class FunctionDataMixin implements FunctionData { class FunctionDataImpl extends JMemberDataImpl with FunctionDataMixin implements FunctionData { + /// Tag used for identifying serialized [FunctionDataImpl] objects in a + /// debugging data stream. + static const String tag = 'function-data'; + final ir.FunctionNode functionNode; FunctionType _type; @@ -358,6 +594,32 @@ class FunctionDataImpl extends JMemberDataImpl ir.Member node, this.functionNode, MemberDefinition definition) : super(node, definition); + factory FunctionDataImpl.readFromDataSource(DataSource source) { + source.begin(tag); + ir.Member node = source.readMemberNode(); + ir.FunctionNode functionNode; + if (node is ir.Procedure) { + functionNode = node.function; + } else if (node is ir.Constructor) { + functionNode = node.function; + } else { + throw new UnsupportedError( + "Unexpected member node $node (${node.runtimeType})."); + } + MemberDefinition definition = + new MemberDefinition.readFromDataSource(source); + source.end(tag); + return new FunctionDataImpl(node, functionNode, definition); + } + + void writeToDataSink(DataSink sink) { + sink.writeEnum(JMemberDataKind.function); + sink.begin(tag); + sink.writeMemberNode(node); + definition.writeToDataSink(sink); + sink.end(tag); + } + FunctionType getFunctionType(covariant JsToElementMapBase elementMap) { return _type ??= elementMap.getFunctionType(functionNode); } @@ -395,6 +657,10 @@ class FunctionDataImpl extends JMemberDataImpl } class SignatureFunctionData implements FunctionData { + /// Tag used for identifying serialized [SignatureFunctionData] objects in a + /// debugging data stream. + static const String tag = 'signature-function-data'; + final MemberDefinition definition; final InterfaceType memberThisType; final ClassTypeVariableAccess classTypeVariableAccess; @@ -403,6 +669,29 @@ class SignatureFunctionData implements FunctionData { SignatureFunctionData(this.definition, this.memberThisType, this.typeParameters, this.classTypeVariableAccess); + factory SignatureFunctionData.readFromDataSource(DataSource source) { + source.begin(tag); + MemberDefinition definition = + new MemberDefinition.readFromDataSource(source); + InterfaceType memberThisType = source.readDartType(allowNull: true); + List typeParameters = source.readTypeParameterNodes(); + ClassTypeVariableAccess classTypeVariableAccess = + source.readEnum(ClassTypeVariableAccess.values); + source.end(tag); + return new SignatureFunctionData( + definition, memberThisType, typeParameters, classTypeVariableAccess); + } + + void writeToDataSink(DataSink sink) { + sink.writeEnum(JMemberDataKind.signature); + sink.begin(tag); + definition.writeToDataSink(sink); + sink.writeDartType(memberThisType, allowNull: true); + sink.writeTypeParameterNodes(typeParameters); + sink.writeEnum(classTypeVariableAccess); + sink.end(tag); + } + FunctionType getFunctionType(covariant JsToElementMapBase elementMap) { throw new UnsupportedError("SignatureFunctionData.getFunctionType"); } @@ -451,9 +740,32 @@ abstract class DelegatedFunctionData implements FunctionData { } class GeneratorBodyFunctionData extends DelegatedFunctionData { + /// Tag used for identifying serialized [GeneratorBodyFunctionData] objects in + /// a debugging data stream. + static const String tag = 'generator-body-data'; + final MemberDefinition definition; + GeneratorBodyFunctionData(FunctionData baseData, this.definition) : super(baseData); + + factory GeneratorBodyFunctionData.readFromDataSource(DataSource source) { + source.begin(tag); + // TODO(johnniwinther): Share the original base data on deserialization. + FunctionData baseData = new JMemberData.readFromDataSource(source); + MemberDefinition definition = + new MemberDefinition.readFromDataSource(source); + source.end(tag); + return new GeneratorBodyFunctionData(baseData, definition); + } + + void writeToDataSink(DataSink sink) { + sink.writeEnum(JMemberDataKind.generatorBody); + sink.begin(tag); + baseData.writeToDataSink(sink); + definition.writeToDataSink(sink); + sink.end(tag); + } } abstract class JConstructorData extends FunctionData { @@ -463,6 +775,10 @@ abstract class JConstructorData extends FunctionData { class JConstructorDataImpl extends FunctionDataImpl implements JConstructorData { + /// Tag used for identifying serialized [JConstructorDataImpl] objects in a + /// debugging data stream. + static const String tag = 'constructor-data'; + ConstantConstructor _constantConstructor; JConstructorBody constructorBody; @@ -470,6 +786,33 @@ class JConstructorDataImpl extends FunctionDataImpl ir.Member node, ir.FunctionNode functionNode, MemberDefinition definition) : super(node, functionNode, definition); + factory JConstructorDataImpl.readFromDataSource(DataSource source) { + source.begin(tag); + ir.Member node = source.readMemberNode(); + ir.FunctionNode functionNode; + if (node is ir.Procedure) { + functionNode = node.function; + } else if (node is ir.Constructor) { + functionNode = node.function; + } else { + throw new UnsupportedError( + "Unexpected member node $node (${node.runtimeType})."); + } + MemberDefinition definition = + new MemberDefinition.readFromDataSource(source); + source.end(tag); + return new JConstructorDataImpl(node, functionNode, definition); + } + + void writeToDataSink(DataSink sink) { + sink.writeEnum(JMemberDataKind.constructor); + sink.begin(tag); + sink.writeMemberNode(node); + definition.writeToDataSink(sink); + assert(constructorBody == null); + sink.end(tag); + } + ConstantConstructor getConstructorConstant( JsToElementMapBase elementMap, ConstructorEntity constructor) { if (_constantConstructor == null) { @@ -492,10 +835,40 @@ class JConstructorDataImpl extends FunctionDataImpl } class ConstructorBodyDataImpl extends FunctionDataImpl { + /// Tag used for identifying serialized [ConstructorBodyDataImpl] objects in + /// a debugging data stream. + static const String tag = 'constructor-body-data'; + ConstructorBodyDataImpl( ir.Member node, ir.FunctionNode functionNode, MemberDefinition definition) : super(node, functionNode, definition); + factory ConstructorBodyDataImpl.readFromDataSource(DataSource source) { + source.begin(tag); + ir.Member node = source.readMemberNode(); + ir.FunctionNode functionNode; + if (node is ir.Procedure) { + functionNode = node.function; + } else if (node is ir.Constructor) { + functionNode = node.function; + } else { + throw new UnsupportedError( + "Unexpected member node $node (${node.runtimeType})."); + } + MemberDefinition definition = + new MemberDefinition.readFromDataSource(source); + source.end(tag); + return new ConstructorBodyDataImpl(node, functionNode, definition); + } + + void writeToDataSink(DataSink sink) { + sink.writeEnum(JMemberDataKind.constructorBody); + sink.begin(tag); + sink.writeMemberNode(node); + definition.writeToDataSink(sink); + sink.end(tag); + } + // TODO(johnniwinther,sra): Constructor bodies should access type variables // through `this`. @override @@ -518,6 +891,10 @@ abstract class JFieldData extends JMemberData { } class JFieldDataImpl extends JMemberDataImpl implements JFieldData { + /// Tag used for identifying serialized [JFieldDataImpl] objects in + /// a debugging data stream. + static const String tag = 'field-data'; + DartType _type; bool _isConstantComputed = false; ConstantValue _constantValue; @@ -526,6 +903,23 @@ class JFieldDataImpl extends JMemberDataImpl implements JFieldData { JFieldDataImpl(ir.Field node, MemberDefinition definition) : super(node, definition); + factory JFieldDataImpl.readFromDataSource(DataSource source) { + source.begin(tag); + ir.Member node = source.readMemberNode(); + MemberDefinition definition = + new MemberDefinition.readFromDataSource(source); + source.end(tag); + return new JFieldDataImpl(node, definition); + } + + void writeToDataSink(DataSink sink) { + sink.writeEnum(JMemberDataKind.field); + sink.begin(tag); + sink.writeMemberNode(node); + definition.writeToDataSink(sink); + sink.end(tag); + } + ir.Field get node => super.node; DartType getFieldType(covariant JsToElementMapBase elementMap) { @@ -582,18 +976,52 @@ class JFieldDataImpl extends JMemberDataImpl implements JFieldData { } class JTypedefData { + /// Tag used for identifying serialized [JTypedefData] objects in + /// a debugging data stream. + static const String tag = 'typedef-data'; + final TypedefType rawType; JTypedefData(this.rawType); + + factory JTypedefData.readFromDataSource(DataSource source) { + source.begin(tag); + TypedefType rawType = source.readDartType(); + source.end(tag); + return new JTypedefData(rawType); + } + + void writeToDataSink(DataSink sink) { + sink.begin(tag); + sink.writeDartType(rawType); + sink.end(tag); + } } class JTypeVariableData { + /// Tag used for identifying serialized [JTypeVariableData] objects in + /// a debugging data stream. + static const String tag = 'type-variable-data'; + final ir.TypeParameter node; DartType _bound; DartType _defaultType; JTypeVariableData(this.node); + factory JTypeVariableData.readFromDataSource(DataSource source) { + source.begin(tag); + ir.TypeParameter node = source.readTypeParameterNode(); + source.end(tag); + return new JTypeVariableData(node); + } + + void writeToDataSink(DataSink sink) { + sink.begin(tag); + sink.writeTypeParameterNode(node); + sink.end(tag); + } + DartType getBound(IrToElementMap elementMap) { return _bound ??= elementMap.getDartType(node.bound); } diff --git a/pkg/compiler/lib/src/js_model/js_strategy.dart b/pkg/compiler/lib/src/js_model/js_strategy.dart index 7b9921ca7ce5b..3421f91ff4520 100644 --- a/pkg/compiler/lib/src/js_model/js_strategy.dart +++ b/pkg/compiler/lib/src/js_model/js_strategy.dart @@ -16,10 +16,12 @@ import '../compiler.dart'; import '../constants/constant_system.dart'; import '../constants/values.dart'; import '../deferred_load.dart'; +import '../diagnostics/diagnostic_listener.dart'; import '../elements/entities.dart'; import '../elements/names.dart'; import '../elements/types.dart'; import '../elements/entity_utils.dart' as utils; +import '../environment.dart'; import '../enqueue.dart'; import '../io/kernel_source_information.dart' show KernelSourceInformationStrategy; @@ -42,6 +44,7 @@ import '../kernel/kelements.dart'; import '../native/behavior.dart'; import '../ordered_typeset.dart'; import '../options.dart'; +import '../serialization/serialization.dart'; import '../ssa/builder_kernel.dart'; import '../ssa/nodes.dart'; import '../ssa/ssa.dart'; @@ -64,7 +67,6 @@ import 'locals.dart'; class JsBackendStrategy implements BackendStrategy { final Compiler _compiler; JsKernelToElementMap _elementMap; - Sorter _sorter; JsBackendStrategy(this._compiler); @@ -101,8 +103,8 @@ class JsBackendStrategy implements BackendStrategy { } @override - Sorter get sorter { - return _sorter ??= new KernelSorter(elementMap); + void registerJClosedWorld(covariant JsClosedWorld closedWorld) { + _elementMap = closedWorld.elementMap; } @override @@ -634,6 +636,8 @@ class JsClosedWorldBuilder { } class JsClosedWorld extends ClosedWorldBase { + static const String tag = 'closed-world'; + final JsKernelToElementMap elementMap; final RuntimeTypesNeed rtiNeed; AbstractValueDomain _abstractValueDomain; @@ -642,6 +646,7 @@ class JsClosedWorld extends ClosedWorldBase { final GlobalLocalsMap globalLocalsMap; final ClosureData closureDataLookup; final OutputUnitData outputUnitData; + Sorter _sorter; JsClosedWorld(this.elementMap, {ConstantSystem constantSystem, @@ -656,8 +661,8 @@ class JsClosedWorld extends ClosedWorldBase { Iterable liveInstanceMembers, Iterable assignedInstanceMembers, Iterable processedMembers, - Map> mixinUses, - Map> typesImplementedBySubclasses, + Map> mixinUses, + Map> typesImplementedBySubclasses, ClassHierarchy classHierarchy, AbstractValueStrategy abstractValueStrategy, this.annotationsData, @@ -684,6 +689,113 @@ class JsClosedWorld extends ClosedWorldBase { _abstractValueDomain = abstractValueStrategy.createDomain(this); } + /// Deserializes a [JsClosedWorld] object from [source]. + factory JsClosedWorld.readFromDataSource( + CompilerOptions options, + DiagnosticReporter reporter, + Environment environment, + AbstractValueStrategy abstractValueStrategy, + ir.Component component, + DataSource source) { + source.begin(tag); + + JsKernelToElementMap elementMap = + new JsKernelToElementMap.readFromDataSource( + options, reporter, environment, component, source); + GlobalLocalsMap globalLocalsMap = + new GlobalLocalsMap.readFromDataSource(source); + source.registerLocalLookup(new LocalLookupImpl(globalLocalsMap)); + ClassHierarchy classHierarchy = new ClassHierarchy.readFromDataSource( + source, elementMap.commonElements); + NativeData nativeData = new NativeData.readFromDataSource( + source, elementMap.elementEnvironment); + elementMap.nativeBasicData = nativeData; + InterceptorData interceptorData = new InterceptorData.readFromDataSource( + source, nativeData, elementMap.commonElements); + BackendUsage backendUsage = new BackendUsage.readFromDataSource(source); + RuntimeTypesNeed rtiNeed = new RuntimeTypesNeed.readFromDataSource( + source, elementMap.elementEnvironment); + JAllocatorAnalysis allocatorAnalysis = + new JAllocatorAnalysis.readFromDataSource(source, options); + NoSuchMethodData noSuchMethodData = + new NoSuchMethodData.readFromDataSource(source); + + Set implementedClasses = source.readClasses().toSet(); + Iterable liveNativeClasses = source.readClasses(); + Iterable liveInstanceMembers = source.readMembers(); + Iterable assignedInstanceMembers = source.readMembers(); + Iterable processedMembers = source.readMembers(); + Map> mixinUses = + source.readClassMap(() => source.readClasses().toSet()); + Map> typesImplementedBySubclasses = + source.readClassMap(() => source.readClasses().toSet()); + + AnnotationsData annotationsData = + new AnnotationsData.readFromDataSource(source); + + ClosureData closureData = + new ClosureData.readFromDataSource(elementMap, source); + + OutputUnitData outputUnitData = + new OutputUnitData.readFromDataSource(source); + + source.end(tag); + + return new JsClosedWorld(elementMap, + nativeData: nativeData, + interceptorData: interceptorData, + backendUsage: backendUsage, + rtiNeed: rtiNeed, + allocatorAnalysis: allocatorAnalysis, + noSuchMethodData: noSuchMethodData, + implementedClasses: implementedClasses, + liveNativeClasses: liveNativeClasses, + liveInstanceMembers: liveInstanceMembers, + assignedInstanceMembers: assignedInstanceMembers, + processedMembers: processedMembers, + mixinUses: mixinUses, + typesImplementedBySubclasses: typesImplementedBySubclasses, + classHierarchy: classHierarchy, + abstractValueStrategy: abstractValueStrategy, + annotationsData: annotationsData, + globalLocalsMap: globalLocalsMap, + closureDataLookup: closureData, + outputUnitData: outputUnitData); + } + + /// Serializes this [JsClosedWorld] to [sink]. + void writeToDataSink(DataSink sink) { + sink.begin(tag); + elementMap.writeToDataSink(sink); + globalLocalsMap.writeToDataSink(sink); + + classHierarchy.writeToDataSink(sink); + nativeData.writeToDataSink(sink); + interceptorData.writeToDataSink(sink); + backendUsage.writeToDataSink(sink); + rtiNeed.writeToDataSink(sink); + allocatorAnalysis.writeToDataSink(sink); + noSuchMethodData.writeToDataSink(sink); + sink.writeClasses(implementedClasses); + sink.writeClasses(liveNativeClasses); + sink.writeMembers(liveInstanceMembers); + sink.writeMembers(assignedInstanceMembers); + sink.writeMembers(processedMembers); + sink.writeClassMap( + mixinUses, (Set set) => sink.writeClasses(set)); + sink.writeClassMap(typesImplementedBySubclasses, + (Set set) => sink.writeClasses(set)); + annotationsData.writeToDataSink(sink); + closureDataLookup.writeToDataSink(sink); + outputUnitData.writeToDataSink(sink); + sink.end(tag); + } + + @override + Sorter get sorter { + return _sorter ??= new KernelSorter(elementMap); + } + @override AbstractValueDomain get abstractValueDomain { return _abstractValueDomain; @@ -1236,3 +1348,16 @@ class KernelSorter implements Sorter { a, definition1.location, b, definition2.location); } } + +/// [LocalLookup] implementation used to deserialize [JsClosedWorld]. +class LocalLookupImpl implements LocalLookup { + final GlobalLocalsMap _globalLocalsMap; + + LocalLookupImpl(this._globalLocalsMap); + + @override + Local getLocalByIndex(MemberEntity memberContext, int index) { + KernelToLocalsMapImpl map = _globalLocalsMap.getLocalsMap(memberContext); + return map.getLocalByIndex(index); + } +} diff --git a/pkg/compiler/lib/src/js_model/locals.dart b/pkg/compiler/lib/src/js_model/locals.dart index 14a933d5bcb0e..6f6692071972f 100644 --- a/pkg/compiler/lib/src/js_model/locals.dart +++ b/pkg/compiler/lib/src/js_model/locals.dart @@ -13,15 +13,60 @@ import '../elements/indexed.dart'; import '../elements/jumps.dart'; import '../elements/types.dart'; import '../ir/util.dart'; +import '../serialization/serialization.dart'; import 'element_map.dart'; import 'elements.dart' show JGeneratorBody; class GlobalLocalsMap { + /// Tag used for identifying serialized [GlobalLocalsMap] objects in a + /// debugging data stream. + static const String tag = 'global-locals-map'; + final Map _localsMaps; GlobalLocalsMap() : _localsMaps = {}; + GlobalLocalsMap.internal(this._localsMaps); + + /// Deserializes a [GlobalLocalsMap] object from [source]. + factory GlobalLocalsMap.readFromDataSource(DataSource source) { + source.begin(tag); + Map _localsMaps = {}; + int mapCount = source.readInt(); + for (int i = 0; i < mapCount; i++) { + KernelToLocalsMap localsMap = + new KernelToLocalsMapImpl.readFromDataSource(source); + List members = source.readMembers(); + for (MemberEntity member in members) { + _localsMaps[member] = localsMap; + } + } + source.end(tag); + return new GlobalLocalsMap.internal(_localsMaps); + } + + /// Serializes this [GlobalLocalsMap] to [sink]. + void writeToDataSink(DataSink sink) { + sink.begin(tag); + // [KernelToLocalsMap]s are shared between members and their nested + // closures, so we reverse [_localsMaps] to ensure that [KernelToLocalsMap]s + // are shared upon deserialization. The sharing is needed for correctness + // since captured variables will otherwise have distinct locals for their + // non-captured and captured uses. + Map> reverseMap = {}; + _localsMaps.forEach((MemberEntity member, KernelToLocalsMap localsMap) { + reverseMap.putIfAbsent(localsMap, () => []).add(member); + }); + sink.writeInt(reverseMap.length); + reverseMap + .forEach((KernelToLocalsMap localsMap, List members) { + localsMap.writeToDataSink(sink); + sink.writeMembers(members); + }); + sink.end(tag); + } + /// Returns the [KernelToLocalsMap] for [member]. KernelToLocalsMap getLocalsMap(MemberEntity member) { // If element is a ConstructorBodyEntity, its localsMap is the same as for @@ -48,6 +93,10 @@ class GlobalLocalsMap { } class KernelToLocalsMapImpl implements KernelToLocalsMap { + /// Tag used for identifying serialized [KernelToLocalsMapImpl] objects in a + /// debugging data stream. + static const String tag = 'locals-map'; + MemberEntity _currentMember; final EntityDataMap _locals = new EntityDataMap(); @@ -56,6 +105,73 @@ class KernelToLocalsMapImpl implements KernelToLocalsMap { Map _jumpTargetMap; Iterable _breaksAsContinue; + KernelToLocalsMapImpl(this._currentMember); + + /// Deserializes a [KernelToLocalsMapImpl] object from [source]. + KernelToLocalsMapImpl.readFromDataSource(DataSource source) { + source.begin(tag); + _currentMember = source.readMember(); + int localsCount = source.readInt(); + for (int i = 0; i < localsCount; i++) { + int index = source.readInt(); + String name = source.readStringOrNull(); + bool isRegularParameter = source.readBool(); + ir.VariableDeclaration node = source.readTreeNode(); + JLocal local = new JLocal(name, currentMember, + isRegularParameter: isRegularParameter); + LocalData data = new LocalData(node); + _locals.registerByIndex(index, local, data); + _variableMap[node] = local; + } + int jumpCount = source.readInt(); + if (jumpCount > 0) { + _jumpTargetMap = {}; + for (int i = 0; i < jumpCount; i++) { + JJumpTarget target = new JJumpTarget.readFromDataSource(source); + List nodes = source.readTreeNodes(); + for (ir.TreeNode node in nodes) { + _jumpTargetMap[node] = target; + } + } + } + _breaksAsContinue = source.readTreeNodes(); + source.end(tag); + } + + /// Serializes this [KernelToLocalsMapImpl] to [sink]. + void writeToDataSink(DataSink sink) { + sink.begin(tag); + sink.writeMember(currentMember); + sink.writeInt(_locals.size); + _locals.forEach((JLocal local, LocalData data) { + assert(local.memberContext == currentMember); + sink.writeInt(local.localIndex); + sink.writeStringOrNull(local.name); + sink.writeBool(local.isRegularParameter); + sink.writeTreeNode(data.node); + }); + if (_jumpTargetMap != null) { + // [JJumpTarget]s are shared between nodes, so we reverse + // [_jumpTargetMap] to ensure that [JJumpTarget]s are shared upon + // deserialization. This sharing is needed for correctness since for + // instance a label statement containing a for loop both constitutes the + // same jump target and the SSA graph builder dependents on this property. + Map> reversedMap = {}; + _jumpTargetMap.forEach((ir.TreeNode node, JJumpTarget target) { + reversedMap.putIfAbsent(target, () => []).add(node); + }); + sink.writeInt(reversedMap.length); + reversedMap.forEach((JJumpTarget target, List nodes) { + target.writeToDataSink(sink); + sink.writeTreeNodes(nodes); + }); + } else { + sink.writeInt(0); + } + sink.writeTreeNodes(_breaksAsContinue, allowNull: true); + sink.end(tag); + } + // TODO(johnniwinther): Compute this eagerly from the root of the member. void _ensureJumpMap(ir.TreeNode node) { if (_jumpTargetMap == null) { @@ -72,9 +188,12 @@ class KernelToLocalsMapImpl implements KernelToLocalsMap { } } - KernelToLocalsMapImpl(this._currentMember); - MemberEntity get currentMember => _currentMember; + + Local getLocalByIndex(int index) { + return _locals.getEntity(index); + } + @override JumpTarget getJumpTargetForBreak(ir.BreakStatement node) { _ensureJumpMap(node.target); @@ -328,6 +447,10 @@ class JumpVisitor extends ir.Visitor { } class JJumpTarget extends JumpTarget { + /// Tag used for identifying serialized [JJumpTarget] objects in a + /// debugging data stream. + static const String tag = 'jump-target'; + final MemberEntity memberContext; final int nestingLevel; List _labels; @@ -337,6 +460,47 @@ class JJumpTarget extends JumpTarget { JJumpTarget(this.memberContext, this.nestingLevel, {this.isSwitch: false, this.isSwitchCase: false}); + /// Deserializes a [JJumpTarget] object from [source]. + factory JJumpTarget.readFromDataSource(DataSource source) { + source.begin(tag); + MemberEntity memberContext = source.readMember(); + int nestingLevel = source.readInt(); + bool isSwitch = source.readBool(); + bool isSwitchCase = source.readBool(); + JJumpTarget target = new JJumpTarget(memberContext, nestingLevel, + isSwitch: isSwitch, isSwitchCase: isSwitchCase); + int labelCount = source.readInt(); + for (int i = 0; i < labelCount; i++) { + String labelName = source.readString(); + bool isBreakTarget = source.readBool(); + bool isContinueTarget = source.readBool(); + target.addLabel(labelName, + isBreakTarget: isBreakTarget, isContinueTarget: isContinueTarget); + } + source.end(tag); + return target; + } + + /// Serializes this [JJumpTarget] to [sink]. + void writeToDataSink(DataSink sink) { + sink.begin(tag); + sink.writeMember(memberContext); + sink.writeInt(nestingLevel); + sink.writeBool(isSwitch); + sink.writeBool(isSwitchCase); + if (_labels != null) { + sink.writeInt(_labels.length); + for (LabelDefinition definition in _labels) { + sink.writeString(definition.name); + sink.writeBool(definition.isBreakTarget); + sink.writeBool(definition.isContinueTarget); + } + } else { + sink.writeInt(0); + } + sink.end(tag); + } + bool isBreakTarget = false; bool isContinueTarget = false; diff --git a/pkg/compiler/lib/src/kernel/env.dart b/pkg/compiler/lib/src/kernel/env.dart index a343e9f2caead..e82ef91fb8a83 100644 --- a/pkg/compiler/lib/src/kernel/env.dart +++ b/pkg/compiler/lib/src/kernel/env.dart @@ -399,7 +399,7 @@ class KClassEnvImpl implements KClassEnv { } if (!includeStatic && member.isStatic) return; if (member.isNoSuchMethodForwarder) { - // TODO(sigmund): remove once #33665 is fixed. + // TODO(sigmund): remove once #33732 is fixed. if (!includeNoSuchMethodForwarders || member.name.isPrivate && member.name.libraryName != member.enclosingLibrary.reference) { diff --git a/pkg/compiler/lib/src/native/behavior.dart b/pkg/compiler/lib/src/native/behavior.dart index 5de83bd8c3a87..a437151c2d570 100644 --- a/pkg/compiler/lib/src/native/behavior.dart +++ b/pkg/compiler/lib/src/native/behavior.dart @@ -9,6 +9,7 @@ import '../elements/entities.dart'; import '../elements/types.dart'; import '../js/js.dart' as js; import '../js_backend/native_data.dart' show NativeBasicData; +import '../serialization/serialization.dart'; import '../universe/side_effects.dart' show SideEffects; import 'js.dart'; @@ -124,6 +125,10 @@ class NativeThrowBehavior { * `null` may be returned. */ class NativeBehavior { + /// Tag used for identifying serialized [NativeBehavior] objects in a + /// debugging data stream. + static const String tag = 'native-behavior'; + /// [DartType]s or [SpecialType]s returned or yielded by the native /// element. final List typesReturned = []; @@ -156,6 +161,88 @@ class NativeBehavior { NativeBehavior.internal(this.sideEffects); + /// Deserializes a [NativeBehavior] object from [source]. + factory NativeBehavior.readFromDataSource(DataSource source) { + source.begin(tag); + + List readTypes() { + List types = []; + types.addAll(source.readDartTypes()); + int specialCount = source.readInt(); + for (int i = 0; i < specialCount; i++) { + String name = source.readString(); + types.add(SpecialType.fromName(name)); + } + return types; + } + + List typesReturned = readTypes(); + List typesInstantiated = readTypes(); + String codeTemplateText = source.readStringOrNull(); + SideEffects sideEffects = new SideEffects.readFromDataSource(source); + int throwBehavior = source.readInt(); + bool isAllocation = source.readBool(); + bool useGvn = source.readBool(); + source.end(tag); + + NativeBehavior behavior = new NativeBehavior.internal(sideEffects); + behavior.typesReturned.addAll(typesReturned); + behavior.typesInstantiated.addAll(typesInstantiated); + if (codeTemplateText != null) { + behavior.codeTemplateText = codeTemplateText; + behavior.codeTemplate = js.js.parseForeignJS(codeTemplateText); + } + switch (throwBehavior) { + case 0: + behavior.throwBehavior = NativeThrowBehavior.NEVER; + break; + case 1: + behavior.throwBehavior = + NativeThrowBehavior.MAY_THROW_ONLY_ON_FIRST_ARGUMENT_ACCESS; + break; + case 2: + behavior.throwBehavior = NativeThrowBehavior.MAY; + break; + case 3: + behavior.throwBehavior = NativeThrowBehavior.MUST; + break; + } + behavior.isAllocation = isAllocation; + behavior.useGvn = useGvn; + return behavior; + } + + /// Serializes this [NativeBehavior] to [sink]. + void writeToDataSink(DataSink sink) { + sink.begin(tag); + + void writeTypes(List types) { + List dartTypes = []; + List specialTypes = []; + for (var type in types) { + if (type is DartType) { + dartTypes.add(type); + } else { + specialTypes.add(type); + } + } + sink.writeDartTypes(dartTypes); + sink.writeInt(specialTypes.length); + for (SpecialType type in specialTypes) { + sink.writeString(type.name); + } + } + + writeTypes(typesReturned); + writeTypes(typesInstantiated); + sink.writeStringOrNull(codeTemplateText); + sideEffects.writeToDataSink(sink); + sink.writeInt(throwBehavior._bits); + sink.writeBool(isAllocation); + sink.writeBool(useGvn); + sink.end(tag); + } + String toString() { return 'NativeBehavior(' 'returns: ${typesReturned}' diff --git a/pkg/compiler/lib/src/ordered_typeset.dart b/pkg/compiler/lib/src/ordered_typeset.dart index 4b587b6a44ac1..57ed4cb7199b0 100644 --- a/pkg/compiler/lib/src/ordered_typeset.dart +++ b/pkg/compiler/lib/src/ordered_typeset.dart @@ -11,6 +11,7 @@ import 'common.dart'; import 'diagnostics/diagnostic_listener.dart' show DiagnosticReporter; import 'elements/entities.dart'; import 'elements/types.dart'; +import 'serialization/serialization.dart'; /** * An ordered set of the supertypes of a class. The supertypes of a class are @@ -30,12 +31,80 @@ import 'elements/types.dart'; * C: [C, B, A, Object] */ class OrderedTypeSet { + /// Tag used for identifying serialized [OrderedTypeSet] objects in a + /// debugging data stream. + static const String tag = 'ordered-type-set'; + final List> _levels; final Link types; final Link _supertypes; OrderedTypeSet.internal(this._levels, this.types, this._supertypes); + /// Deserializes a [OrderedTypeSet] object from [source]. + factory OrderedTypeSet.readFromDataSource(DataSource source) { + // TODO(johnniwinther): Make the deserialized type sets share their + // internal links like the original type sets do? + source.begin(tag); + int typesCount = source.readInt(); + LinkBuilder typeLinkBuilder = + new LinkBuilder(); + List> links = []; + for (int i = 0; i < typesCount; i++) { + links.add(typeLinkBuilder.addLast(source.readDartType())); + } + Link types = + typeLinkBuilder.toLink(const Link()); + links.add(const Link()); + + int supertypesCount = source.readInt(); + LinkBuilder supertypeLinkBuilder = + new LinkBuilder(); + for (int i = 0; i < supertypesCount; i++) { + supertypeLinkBuilder.addLast(source.readDartType()); + } + Link supertypes = + supertypeLinkBuilder.toLink(const Link()); + + int levelCount = source.readInt(); + List> levels = + new List>(levelCount); + for (int i = 0; i < levelCount; i++) { + levels[i] = links[source.readInt()]; + } + source.end(tag); + return new OrderedTypeSet.internal(levels, types, supertypes); + } + + /// Serializes this [OrderedTypeSet] to [sink]. + void writeToDataSink(DataSink sink) { + sink.begin(tag); + List typeList = types.toList(); + sink.writeInt(typeList.length); + for (InterfaceType type in typeList) { + sink.writeDartType(type); + } + List supertypeList = _supertypes.toList(); + sink.writeInt(supertypeList.length); + for (InterfaceType supertype in supertypeList) { + sink.writeDartType(supertype); + } + List levelList = []; + Link link = types; + while (link != null) { + int index = _levels.indexOf(link); + if (index != -1) { + levelList.add(index); + } + link = link.tail; + } + sink.writeInt(levelList.length); + for (int level in levelList) { + sink.writeInt(level); + } + sink.end(tag); + } + factory OrderedTypeSet.singleton(InterfaceType type) { Link types = new LinkEntry(type, const Link()); diff --git a/pkg/compiler/lib/src/serialization/abstract_sink.dart b/pkg/compiler/lib/src/serialization/abstract_sink.dart new file mode 100644 index 0000000000000..b63b9a2c3c407 --- /dev/null +++ b/pkg/compiler/lib/src/serialization/abstract_sink.dart @@ -0,0 +1,332 @@ +// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +part of 'serialization.dart'; + +/// Base implementation of [DataSink] using [DataSinkMixin] to implement +/// convenience methods. +abstract class AbstractDataSink extends DataSinkMixin implements DataSink { + /// If `true`, serialization of every data kind is preceded by a [DataKind] + /// value. + /// + /// This is used for debugging data inconsistencies between serialization + /// and deserialization. + final bool useDataKinds; + + /// Visitor used for serializing [DartType]s. + DartTypeWriter _dartTypeWriter; + + /// Stack of tags used when [useDataKinds] is `true` to help debugging section + /// inconsistencies between serialization and deserialization. + List _tags; + + /// Map of [_MemberData] object for serialized kernel member nodes. + Map _memberData = {}; + + AbstractDataSink({this.useDataKinds: false}) { + _dartTypeWriter = new DartTypeWriter(this); + } + + void begin(String tag) { + if (useDataKinds) { + _tags ??= []; + _tags.add(tag); + _begin(tag); + } + } + + void end(Object tag) { + if (useDataKinds) { + _end(tag); + + String existingTag = _tags.removeLast(); + assert(existingTag == tag, + "Unexpected tag end. Expected $existingTag, found $tag."); + } + } + + @override + void writeSourceSpan(SourceSpan value) { + _writeDataKind(DataKind.sourceSpan); + _writeUri(value.uri); + _writeInt(value.begin); + _writeInt(value.end); + } + + @override + void writeDartType(DartType value, {bool allowNull: false}) { + _writeDataKind(DataKind.dartType); + _writeDartType(value, [], allowNull: allowNull); + } + + void _writeDartType( + DartType value, List functionTypeVariables, + {bool allowNull: false}) { + if (value == null) { + if (!allowNull) { + throw new UnsupportedError("Missing DartType is not allowed."); + } + writeEnum(DartTypeKind.none); + } else { + _dartTypeWriter.visit(value, functionTypeVariables); + } + } + + @override + void writeMemberNode(ir.Member value) { + _writeDataKind(DataKind.memberNode); + _writeMemberNode(value); + } + + void _writeMemberNode(ir.Member value) { + ir.Class cls = value.enclosingClass; + if (cls != null) { + _writeEnum(MemberContextKind.cls); + _writeClassNode(cls); + _writeString(_computeMemberName(value)); + } else { + _writeEnum(MemberContextKind.library); + _writeLibraryNode(value.enclosingLibrary); + _writeString(_computeMemberName(value)); + } + } + + @override + void writeClassNode(ir.Class value) { + _writeDataKind(DataKind.classNode); + _writeClassNode(value); + } + + void _writeClassNode(ir.Class value) { + _writeLibraryNode(value.enclosingLibrary); + _writeString(value.name); + } + + @override + void writeLibraryNode(ir.Library value) { + _writeDataKind(DataKind.libraryNode); + _writeLibraryNode(value); + } + + void _writeLibraryNode(ir.Library value) { + _writeUri(value.importUri); + } + + @override + void writeEnum(dynamic value) { + _writeDataKind(DataKind.enumValue); + _writeEnum(value); + } + + @override + void writeBool(bool value) { + assert(value != null); + _writeDataKind(DataKind.bool); + _writeInt(value ? 1 : 0); + } + + @override + void writeUri(Uri value) { + assert(value != null); + _writeDataKind(DataKind.uri); + _writeUri(value); + } + + @override + void writeString(String value) { + assert(value != null); + _writeDataKind(DataKind.string); + _writeString(value); + } + + @override + void writeInt(int value) { + assert(value != null); + assert(value >= 0 && value >> 30 == 0); + _writeDataKind(DataKind.int); + _writeInt(value); + } + + void writeTreeNode(ir.TreeNode value) { + _writeDataKind(DataKind.treeNode); + _writeTreeNode(value); + } + + void _writeTreeNode(ir.TreeNode value) { + if (value is ir.Class) { + _writeEnum(_TreeNodeKind.cls); + _writeClassNode(value); + } else if (value is ir.Member) { + _writeEnum(_TreeNodeKind.member); + _writeMemberNode(value); + } else if (value is ir.VariableDeclaration && + value.parent is ir.FunctionDeclaration) { + _writeEnum(_TreeNodeKind.functionDeclarationVariable); + _writeTreeNode(value.parent); + } else if (value is ir.FunctionNode) { + _writeEnum(_TreeNodeKind.functionNode); + _writeFunctionNode(value); + } else if (value is ir.TypeParameter) { + _writeEnum(_TreeNodeKind.typeParameter); + _writeTypeParameter(value); + } else { + _writeEnum(_TreeNodeKind.node); + ir.TreeNode member = value; + while (member is! ir.Member) { + if (member == null) { + throw new UnsupportedError("No enclosing member of TreeNode " + "$value (${value.runtimeType})"); + } + member = member.parent; + } + _writeMemberNode(member); + _MemberData memberData = _memberData[member] ??= new _MemberData(member); + int index = memberData.getIndexByTreeNode(value); + assert(index != null, "No index found for ${value.runtimeType}."); + _writeInt(index); + } + } + + void _writeFunctionNode(ir.FunctionNode value) { + ir.TreeNode parent = value.parent; + if (parent is ir.Procedure) { + _writeEnum(_FunctionNodeKind.procedure); + _writeMemberNode(parent); + } else if (parent is ir.Constructor) { + _writeEnum(_FunctionNodeKind.constructor); + _writeMemberNode(parent); + } else if (parent is ir.FunctionExpression) { + _writeEnum(_FunctionNodeKind.functionExpression); + _writeTreeNode(parent); + } else if (parent is ir.FunctionDeclaration) { + _writeEnum(_FunctionNodeKind.functionDeclaration); + _writeTreeNode(parent); + } else { + throw new UnsupportedError( + "Unsupported FunctionNode parent ${parent.runtimeType}"); + } + } + + @override + void writeTypeParameterNode(ir.TypeParameter value) { + _writeDataKind(DataKind.typeParameterNode); + _writeTypeParameter(value); + } + + void _writeTypeParameter(ir.TypeParameter value) { + ir.TreeNode parent = value.parent; + if (parent is ir.Class) { + _writeEnum(_TypeParameterKind.cls); + _writeClassNode(parent); + _writeInt(parent.typeParameters.indexOf(value)); + } else if (parent is ir.FunctionNode) { + _writeEnum(_TypeParameterKind.functionNode); + _writeFunctionNode(parent); + _writeInt(parent.typeParameters.indexOf(value)); + } else { + throw new UnsupportedError( + "Unsupported TypeParameter parent ${parent.runtimeType}"); + } + } + + void _writeDataKind(DataKind kind) { + if (useDataKinds) _writeEnum(kind); + } + + void writeLibrary(IndexedLibrary value) { + writeInt(value.libraryIndex); + } + + void writeClass(IndexedClass value) { + writeInt(value.classIndex); + } + + void writeTypedef(IndexedTypedef value) { + writeInt(value.typedefIndex); + } + + void writeMember(IndexedMember value) { + writeInt(value.memberIndex); + } + + void writeLocal(Local local) { + if (local is JLocal) { + writeEnum(LocalKind.jLocal); + writeMember(local.memberContext); + writeInt(local.localIndex); + } else if (local is ThisLocal) { + writeEnum(LocalKind.thisLocal); + writeClass(local.enclosingClass); + } else if (local is BoxLocal) { + writeEnum(LocalKind.boxLocal); + writeClass(local.container); + } else if (local is AnonymousClosureLocal) { + writeEnum(LocalKind.anonymousClosureLocal); + writeClass(local.closureClass); + } else if (local is TypeVariableLocal) { + writeEnum(LocalKind.typeVariableLocal); + writeDartType(local.typeVariable); + } else { + throw new UnsupportedError("Unsupported local ${local.runtimeType}"); + } + } + + @override + void writeConstant(ConstantValue value) { + _writeDataKind(DataKind.constant); + _writeConstant(value); + } + + void _writeConstant(ConstantValue value) { + _writeEnum(value.kind); + switch (value.kind) { + case ConstantValueKind.BOOL: + BoolConstantValue constant = value; + writeBool(constant.boolValue); + break; + case ConstantValueKind.INT: + IntConstantValue constant = value; + writeString(constant.intValue.toString()); + break; + case ConstantValueKind.DOUBLE: + DoubleConstantValue constant = value; + ByteData data = new ByteData(8); + data.setFloat64(0, constant.doubleValue); + writeInt(data.getUint16(0)); + writeInt(data.getUint16(2)); + writeInt(data.getUint16(4)); + writeInt(data.getUint16(6)); + break; + case ConstantValueKind.STRING: + StringConstantValue constant = value; + writeString(constant.stringValue); + break; + case ConstantValueKind.NULL: + break; + default: + // TODO(johnniwinther): Support remaining constant values. + throw new UnsupportedError( + "Unexpected constant value kind ${value.kind}."); + } + } + + /// Actual serialization of a section begin tag, implemented by subclasses. + void _begin(String tag); + + /// Actual serialization of a section end tag, implemented by subclasses. + void _end(String tag); + + /// Actual serialization of a URI value, implemented by subclasses. + void _writeUri(Uri value); + + /// Actual serialization of a String value, implemented by subclasses. + void _writeString(String value); + + /// Actual serialization of a non-negative integer value, implemented by + /// subclasses. + void _writeInt(int value); + + /// Actual serialization of an enum value, implemented by subclasses. + void _writeEnum(dynamic value); +} diff --git a/pkg/compiler/lib/src/serialization/abstract_source.dart b/pkg/compiler/lib/src/serialization/abstract_source.dart new file mode 100644 index 0000000000000..f258917f8fbfb --- /dev/null +++ b/pkg/compiler/lib/src/serialization/abstract_source.dart @@ -0,0 +1,418 @@ +// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +part of 'serialization.dart'; + +/// Base implementation of [DataSource] using [DataSourceMixin] to implement +/// convenience methods. +abstract class AbstractDataSource extends DataSourceMixin + implements DataSource { + final bool useDataKinds; + ComponentLookup _componentLookup; + EntityLookup _entityLookup; + LocalLookup _localLookup; + + AbstractDataSource({this.useDataKinds: false}); + + void begin(String tag) { + if (useDataKinds) _begin(tag); + } + + void end(String tag) { + if (useDataKinds) _end(tag); + } + + void registerComponentLookup(ComponentLookup componentLookup) { + assert(_componentLookup == null); + _componentLookup = componentLookup; + } + + ComponentLookup get componentLookup { + assert(_componentLookup != null); + return _componentLookup; + } + + void registerEntityLookup(EntityLookup entityLookup) { + assert(_entityLookup == null); + _entityLookup = entityLookup; + } + + EntityLookup get entityLookup { + assert(_entityLookup != null); + return _entityLookup; + } + + void registerLocalLookup(LocalLookup localLookup) { + assert(_localLookup == null); + _localLookup = localLookup; + } + + LocalLookup get localLookup { + assert(_localLookup != null); + return _localLookup; + } + + IndexedLibrary readLibrary() { + return getIndexedLibrary(readInt()); + } + + IndexedClass readClass() { + return getIndexedClass(readInt()); + } + + IndexedTypedef readTypedef() { + return getIndexedTypedef(readInt()); + } + + IndexedMember readMember() { + return getIndexedMember(readInt()); + } + + IndexedLibrary getIndexedLibrary(int libraryIndex) => + entityLookup.getLibraryByIndex(libraryIndex); + + IndexedClass getIndexedClass(int classIndex) => + entityLookup.getClassByIndex(classIndex); + + IndexedTypedef getIndexedTypedef(int typedefIndex) => + entityLookup.getTypedefByIndex(typedefIndex); + + IndexedMember getIndexedMember(int memberIndex) => + entityLookup.getMemberByIndex(memberIndex); + + IndexedTypeVariable getIndexedTypeVariable(int typeVariableIndex) => + entityLookup.getTypeVariableByIndex(typeVariableIndex); + + List _readDartTypes( + List functionTypeVariables) { + int count = readInt(); + List types = new List(count); + for (int index = 0; index < count; index++) { + types[index] = _readDartType(functionTypeVariables); + } + return types; + } + + @override + SourceSpan readSourceSpan() { + _checkDataKind(DataKind.sourceSpan); + Uri uri = _readUri(); + int begin = _readInt(); + int end = _readInt(); + return new SourceSpan(uri, begin, end); + } + + @override + DartType readDartType({bool allowNull: false}) { + _checkDataKind(DataKind.dartType); + DartType type = _readDartType([]); + assert(type != null || allowNull); + return type; + } + + DartType _readDartType(List functionTypeVariables) { + DartTypeKind kind = readEnum(DartTypeKind.values); + switch (kind) { + case DartTypeKind.none: + return null; + case DartTypeKind.voidType: + return const VoidType(); + case DartTypeKind.typeVariable: + return new TypeVariableType(getIndexedTypeVariable(readInt())); + case DartTypeKind.functionTypeVariable: + int index = readInt(); + assert(0 <= index && index < functionTypeVariables.length); + return functionTypeVariables[index]; + case DartTypeKind.functionType: + int typeVariableCount = readInt(); + List typeVariables = + new List.generate(typeVariableCount, + (int index) => new FunctionTypeVariable(index)); + functionTypeVariables = + new List.from(functionTypeVariables) + ..addAll(typeVariables); + for (int index = 0; index < typeVariableCount; index++) { + typeVariables[index].bound = _readDartType(functionTypeVariables); + } + DartType returnType = _readDartType(functionTypeVariables); + List parameterTypes = _readDartTypes(functionTypeVariables); + List optionalParameterTypes = + _readDartTypes(functionTypeVariables); + List namedParameterTypes = + _readDartTypes(functionTypeVariables); + List namedParameters = + new List(namedParameterTypes.length); + for (int i = 0; i < namedParameters.length; i++) { + namedParameters[i] = readString(); + } + return new FunctionType( + returnType, + parameterTypes, + optionalParameterTypes, + namedParameters, + namedParameterTypes, + typeVariables); + + case DartTypeKind.interfaceType: + IndexedClass cls = getIndexedClass(readInt()); + List typeArguments = _readDartTypes(functionTypeVariables); + return new InterfaceType(cls, typeArguments); + case DartTypeKind.typedef: + IndexedTypedef typedef = getIndexedTypedef(readInt()); + List typeArguments = _readDartTypes(functionTypeVariables); + DartType unaliased = _readDartType(functionTypeVariables); + return new TypedefType(typedef, typeArguments, unaliased); + case DartTypeKind.dynamicType: + return const DynamicType(); + case DartTypeKind.futureOr: + DartType typeArgument = _readDartType(functionTypeVariables); + return new FutureOrType(typeArgument); + } + throw new UnsupportedError("Unexpected DartTypeKind $kind"); + } + + _MemberData _readMemberData() { + MemberContextKind kind = _readEnum(MemberContextKind.values); + switch (kind) { + case MemberContextKind.cls: + _ClassData cls = _readClassData(); + String name = _readString(); + return cls.lookupMember(name); + case MemberContextKind.library: + _LibraryData library = _readLibraryData(); + String name = _readString(); + return library.lookupMember(name); + } + throw new UnsupportedError("Unsupported _MemberKind $kind"); + } + + @override + ir.Member readMemberNode() { + _checkDataKind(DataKind.memberNode); + return _readMemberData().node; + } + + _ClassData _readClassData() { + _LibraryData library = _readLibraryData(); + String name = _readString(); + return library.lookupClass(name); + } + + @override + ir.Class readClassNode() { + _checkDataKind(DataKind.classNode); + return _readClassData().node; + } + + _LibraryData _readLibraryData() { + Uri canonicalUri = _readUri(); + return componentLookup.getLibraryDataByUri(canonicalUri); + } + + @override + ir.Library readLibraryNode() { + _checkDataKind(DataKind.libraryNode); + return _readLibraryData().node; + } + + @override + E readEnum(List values) { + _checkDataKind(DataKind.enumValue); + return _readEnum(values); + } + + @override + Uri readUri() { + _checkDataKind(DataKind.uri); + return _readUri(); + } + + @override + bool readBool() { + _checkDataKind(DataKind.bool); + int value = _readInt(); + assert(value == 0 || value == 1); + return value == 1; + } + + @override + String readString() { + _checkDataKind(DataKind.string); + return _readString(); + } + + @override + int readInt() { + _checkDataKind(DataKind.int); + return _readInt(); + } + + @override + ir.TreeNode readTreeNode() { + _checkDataKind(DataKind.treeNode); + return _readTreeNode(); + } + + @override + ConstantValue readConstant() { + _checkDataKind(DataKind.constant); + return _readConstant(); + } + + ConstantValue _readConstant() { + ConstantValueKind kind = _readEnum(ConstantValueKind.values); + ConstantValue constant; + switch (kind) { + case ConstantValueKind.BOOL: + bool value = readBool(); + constant = new BoolConstantValue(value); + break; + case ConstantValueKind.INT: + BigInt value = BigInt.parse(readString()); + constant = new IntConstantValue(value); + break; + case ConstantValueKind.DOUBLE: + ByteData data = new ByteData(8); + data.setUint16(0, readInt()); + data.setUint16(2, readInt()); + data.setUint16(4, readInt()); + data.setUint16(6, readInt()); + double value = data.getFloat64(0); + constant = new DoubleConstantValue(value); + break; + case ConstantValueKind.STRING: + String value = readString(); + constant = new StringConstantValue(value); + break; + case ConstantValueKind.NULL: + constant = const NullConstantValue(); + break; + default: + // TODO(johnniwinther): Support remaining constant values. + throw new UnsupportedError("Unexpected constant value kind ${kind}."); + } + return constant; + } + + ir.TreeNode _readTreeNode() { + _TreeNodeKind kind = _readEnum(_TreeNodeKind.values); + switch (kind) { + case _TreeNodeKind.cls: + return _readClassData().node; + case _TreeNodeKind.member: + return _readMemberData().node; + case _TreeNodeKind.functionDeclarationVariable: + ir.FunctionDeclaration functionDeclaration = _readTreeNode(); + return functionDeclaration.variable; + case _TreeNodeKind.functionNode: + return _readFunctionNode(); + case _TreeNodeKind.typeParameter: + return _readTypeParameter(); + case _TreeNodeKind.node: + _MemberData data = _readMemberData(); + int index = _readInt(); + ir.TreeNode treeNode = data.getTreeNodeByIndex(index); + assert(treeNode != null, + "No TreeNode found for index $index in ${data.node}.$_errorContext"); + return treeNode; + } + throw new UnsupportedError("Unexpected _TreeNodeKind $kind"); + } + + ir.FunctionNode _readFunctionNode() { + _FunctionNodeKind kind = _readEnum(_FunctionNodeKind.values); + switch (kind) { + case _FunctionNodeKind.procedure: + ir.Procedure procedure = _readMemberData().node; + return procedure.function; + case _FunctionNodeKind.constructor: + ir.Constructor constructor = _readMemberData().node; + return constructor.function; + case _FunctionNodeKind.functionExpression: + ir.FunctionExpression functionExpression = _readTreeNode(); + return functionExpression.function; + case _FunctionNodeKind.functionDeclaration: + ir.FunctionDeclaration functionDeclaration = _readTreeNode(); + return functionDeclaration.function; + } + throw new UnsupportedError("Unexpected _FunctionNodeKind $kind"); + } + + @override + ir.TypeParameter readTypeParameterNode() { + _checkDataKind(DataKind.typeParameterNode); + return _readTypeParameter(); + } + + ir.TypeParameter _readTypeParameter() { + _TypeParameterKind kind = _readEnum(_TypeParameterKind.values); + switch (kind) { + case _TypeParameterKind.cls: + ir.Class cls = _readClassData().node; + return cls.typeParameters[_readInt()]; + case _TypeParameterKind.functionNode: + ir.FunctionNode functionNode = _readFunctionNode(); + return functionNode.typeParameters[_readInt()]; + } + throw new UnsupportedError("Unexpected _TypeParameterKind kind $kind"); + } + + void _checkDataKind(DataKind expectedKind) { + if (!useDataKinds) return; + DataKind actualKind = _readEnum(DataKind.values); + assert( + actualKind == expectedKind, + "Invalid data kind. " + "Expected $expectedKind, found $actualKind.$_errorContext"); + } + + @override + Local readLocal() { + LocalKind kind = readEnum(LocalKind.values); + switch (kind) { + case LocalKind.jLocal: + MemberEntity memberContext = readMember(); + int localIndex = readInt(); + return localLookup.getLocalByIndex(memberContext, localIndex); + case LocalKind.thisLocal: + ClassEntity cls = readClass(); + return new ThisLocal(cls); + case LocalKind.boxLocal: + ClassEntity cls = readClass(); + return new BoxLocal(cls); + case LocalKind.anonymousClosureLocal: + ClassEntity cls = readClass(); + return new AnonymousClosureLocal(cls); + case LocalKind.typeVariableLocal: + TypeVariableType typeVariable = readDartType(); + return new TypeVariableLocal(typeVariable); + } + throw new UnsupportedError("Unexpected local kind $kind"); + } + + /// Actual deserialization of a section begin tag, implemented by subclasses. + void _begin(String tag); + + /// Actual deserialization of a section end tag, implemented by subclasses. + void _end(String tag); + + /// Actual deserialization of a string value, implemented by subclasses. + String _readString(); + + /// Actual deserialization of a non-negative integer value, implemented by + /// subclasses. + int _readInt(); + + /// Actual deserialization of a URI value, implemented by subclasses. + Uri _readUri(); + + /// Actual deserialization of an enum value in [values], implemented by + /// subclasses. + E _readEnum(List values); + + /// Returns a string representation of the current state of the data source + /// useful for debugging in consistencies between serialization and + /// deserialization. + String get _errorContext; +} diff --git a/pkg/compiler/lib/src/serialization/binary_sink.dart b/pkg/compiler/lib/src/serialization/binary_sink.dart new file mode 100644 index 0000000000000..e7c211454c858 --- /dev/null +++ b/pkg/compiler/lib/src/serialization/binary_sink.dart @@ -0,0 +1,60 @@ +// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +part of 'serialization.dart'; + +/// [DataSink] that writes data as a sequence of bytes. +/// +/// This data sink works together with [BinarySource]. +class BinarySink extends AbstractDataSink { + final Sink> sink; + BufferedSink _bufferedSink; + + BinarySink(this.sink, {bool useDataKinds: false}) + : _bufferedSink = new BufferedSink(sink), + super(useDataKinds: useDataKinds); + + void _begin(String tag) { + // TODO(johnniwinther): Support tags in binary serialization? + } + void _end(String tag) { + // TODO(johnniwinther): Support tags in binary serialization? + } + + @override + void _writeUri(Uri value) { + _writeString(value.toString()); + } + + @override + void _writeString(String value) { + List bytes = utf8.encode(value); + _writeInt(bytes.length); + _bufferedSink.addBytes(bytes); + } + + @override + void _writeInt(int value) { + assert(value >= 0 && value >> 30 == 0); + if (value < 0x80) { + _bufferedSink.addByte(value); + } else if (value < 0x4000) { + _bufferedSink.addByte2((value >> 8) | 0x80, value & 0xFF); + } else { + _bufferedSink.addByte4((value >> 24) | 0xC0, (value >> 16) & 0xFF, + (value >> 8) & 0xFF, value & 0xFF); + } + } + + @override + void _writeEnum(dynamic value) { + _writeInt(value.index); + } + + void close() { + _bufferedSink.flushAndDestroy(); + _bufferedSink = null; + sink.close(); + } +} diff --git a/pkg/compiler/lib/src/serialization/binary_source.dart b/pkg/compiler/lib/src/serialization/binary_source.dart new file mode 100644 index 0000000000000..08362098a0051 --- /dev/null +++ b/pkg/compiler/lib/src/serialization/binary_source.dart @@ -0,0 +1,67 @@ +// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +part of 'serialization.dart'; + +/// [DataSource] that reads data from a sequence of bytes. +/// +/// This data source works together with [BinarySink]. +class BinarySourceImpl extends AbstractDataSource { + int _byteOffset = 0; + final List _bytes; + + BinarySourceImpl(this._bytes, {bool useDataKinds: false}) + : super(useDataKinds: useDataKinds); + + void _begin(String tag) {} + void _end(String tag) {} + + int _readByte() => _bytes[_byteOffset++]; + + @override + String _readString() { + int length = _readInt(); + List bytes = new Uint8List(length); + bytes.setRange(0, bytes.length, _bytes, _byteOffset); + _byteOffset += bytes.length; + return utf8.decode(bytes); + } + + @override + int _readInt() { + var byte = _readByte(); + if (byte & 0x80 == 0) { + // 0xxxxxxx + return byte; + } else if (byte & 0x40 == 0) { + // 10xxxxxx + return ((byte & 0x3F) << 8) | _readByte(); + } else { + // 11xxxxxx + return ((byte & 0x3F) << 24) | + (_readByte() << 16) | + (_readByte() << 8) | + _readByte(); + } + } + + @override + Uri _readUri() { + String text = _readString(); + return Uri.parse(text); + } + + @override + E _readEnum(List values) { + int index = _readInt(); + assert( + 0 <= index && index < values.length, + "Invalid data kind index. " + "Expected one of $values, found index $index."); + return values[index]; + } + + @override + String get _errorContext => ' Offset $_byteOffset in ${_bytes.length}.'; +} diff --git a/pkg/compiler/lib/src/serialization/helpers.dart b/pkg/compiler/lib/src/serialization/helpers.dart new file mode 100644 index 0000000000000..14446a1ff3ddc --- /dev/null +++ b/pkg/compiler/lib/src/serialization/helpers.dart @@ -0,0 +1,176 @@ +// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +part of 'serialization.dart'; + +/// Enum values used for identifying different kinds of serialized data. +/// +/// This is used to for debugging data inconsistencies between serialization +/// and deserialization. +enum DataKind { + bool, + int, + string, + enumValue, + uri, + libraryNode, + classNode, + memberNode, + treeNode, + typeParameterNode, + dartType, + sourceSpan, + constant, +} + +/// Enum used for identifying the enclosing entity of a member in serialization. +enum MemberContextKind { library, cls } + +/// Enum used for identifying [Local] subclasses in serialization. +enum LocalKind { + jLocal, + thisLocal, + boxLocal, + anonymousClosureLocal, + typeVariableLocal, +} + +/// Enum used for identifying [ir.TreeNode] subclasses in serialization. +enum _TreeNodeKind { + cls, + member, + node, + functionNode, + typeParameter, + functionDeclarationVariable +} + +/// Enum used for identifying [ir.FunctionNode] context in serialization. +enum _FunctionNodeKind { + procedure, + constructor, + functionExpression, + functionDeclaration, +} + +/// Enum used for identifying [ir.TypeParameter] context in serialization. +enum _TypeParameterKind { + cls, + functionNode, +} + +/// Class used for encoding tags in [ObjectSink] and [ObjectSource]. +class Tag { + final String value; + + Tag(this.value); + + int get hashCode => value.hashCode * 13; + + bool operator ==(other) { + if (identical(this, other)) return true; + if (other is! Tag) return false; + return value == other.value; + } + + String toString() => 'Tag($value)'; +} + +/// Enum used for identifying [DartType] subclasses in serialization. +enum DartTypeKind { + none, + voidType, + typeVariable, + functionTypeVariable, + functionType, + interfaceType, + typedef, + dynamicType, + futureOr +} + +/// Visitor that serializes [DartType] object together with [AbstractDataSink]. +class DartTypeWriter + implements DartTypeVisitor> { + final AbstractDataSink _sink; + + DartTypeWriter(this._sink); + + void visit(covariant DartType type, + List functionTypeVariables) => + type.accept(this, functionTypeVariables); + + void visitTypes( + List types, List functionTypeVariables) { + _sink.writeInt(types.length); + for (DartType type in types) { + _sink._writeDartType(type, functionTypeVariables); + } + } + + void visitVoidType(covariant VoidType type, + List functionTypeVariables) { + _sink.writeEnum(DartTypeKind.voidType); + } + + void visitTypeVariableType(covariant TypeVariableType type, + List functionTypeVariables) { + _sink.writeEnum(DartTypeKind.typeVariable); + IndexedTypeVariable typeVariable = type.element; + _sink.writeInt(typeVariable.typeVariableIndex); + } + + void visitFunctionTypeVariable(covariant FunctionTypeVariable type, + List functionTypeVariables) { + _sink.writeEnum(DartTypeKind.functionTypeVariable); + int index = functionTypeVariables.indexOf(type); + assert(index != -1); + _sink.writeInt(index); + } + + void visitFunctionType(covariant FunctionType type, + List functionTypeVariables) { + _sink.writeEnum(DartTypeKind.functionType); + functionTypeVariables = + new List.from(functionTypeVariables) + ..addAll(type.typeVariables); + _sink.writeInt(type.typeVariables.length); + for (FunctionTypeVariable variable in type.typeVariables) { + _sink._writeDartType(variable.bound, functionTypeVariables); + } + _sink._writeDartType(type.returnType, functionTypeVariables); + visitTypes(type.parameterTypes, functionTypeVariables); + visitTypes(type.optionalParameterTypes, functionTypeVariables); + visitTypes(type.namedParameterTypes, functionTypeVariables); + for (String namedParameter in type.namedParameters) { + _sink.writeString(namedParameter); + } + } + + void visitInterfaceType(covariant InterfaceType type, + List functionTypeVariables) { + _sink.writeEnum(DartTypeKind.interfaceType); + _sink.writeClass(type.element); + visitTypes(type.typeArguments, functionTypeVariables); + } + + void visitTypedefType(covariant TypedefType type, + List functionTypeVariables) { + _sink.writeEnum(DartTypeKind.typedef); + _sink.writeTypedef(type.element); + visitTypes(type.typeArguments, functionTypeVariables); + _sink._writeDartType(type.unaliased, functionTypeVariables); + } + + void visitDynamicType(covariant DynamicType type, + List functionTypeVariables) { + _sink.writeEnum(DartTypeKind.dynamicType); + } + + void visitFutureOrType(covariant FutureOrType type, + List functionTypeVariables) { + _sink.writeEnum(DartTypeKind.futureOr); + _sink._writeDartType(type.typeArgument, functionTypeVariables); + } +} diff --git a/pkg/compiler/lib/src/serialization/member_data.dart b/pkg/compiler/lib/src/serialization/member_data.dart new file mode 100644 index 0000000000000..08fa9a3280f84 --- /dev/null +++ b/pkg/compiler/lib/src/serialization/member_data.dart @@ -0,0 +1,159 @@ +// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +part of 'serialization.dart'; + +/// Helper for looking up object library data from an [ir.Component] node. +class ComponentLookup { + final ir.Component _component; + + /// Cache of [_LibraryData] for libraries in [_component]. + Map _libraryMap; + + ComponentLookup(this._component); + + /// Returns the [_LibraryData] object for the library with the [canonicalUri]. + _LibraryData getLibraryDataByUri(Uri canonicalUri) { + if (_libraryMap == null) { + _libraryMap = {}; + for (ir.Library library in _component.libraries) { + _libraryMap[library.importUri] = new _LibraryData(library); + } + } + return _libraryMap[canonicalUri]; + } +} + +/// Returns a name uniquely identifying a member within its enclosing library +/// or class. +String _computeMemberName(ir.Member member) { + if (member.name.isPrivate && + member.name.libraryName != member.enclosingLibrary.reference) { + // TODO(33732): Handle noSuchMethod forwarders for private members from + // other libraries. + return null; + } + String name = member.name.name; + if (member is ir.Procedure && member.kind == ir.ProcedureKind.Setter) { + name += "="; + } + return name; +} + +/// Helper for looking up classes and members from an [ir.Library] node. +class _LibraryData { + /// The [ir.Library] that defines the library. + final ir.Library node; + + /// Cache of [_ClassData] for classes in this library. + Map _classes; + + /// Cache of [_MemberData] for members in this library. + Map _members; + + _LibraryData(this.node); + + /// Returns the [_ClassData] for the class [name] in this library. + _ClassData lookupClass(String name) { + if (_classes == null) { + _classes = {}; + for (ir.Class cls in node.classes) { + assert(!_classes.containsKey(cls.name), + "Duplicate class '${cls.name}' in $_classes trying to add $cls."); + _classes[cls.name] = new _ClassData(cls); + } + } + return _classes[name]; + } + + /// Returns the [_MemberData] for the member uniquely identified by [name] in + /// this library. + _MemberData lookupMember(String name) { + if (_members == null) { + _members = {}; + for (ir.Member member in node.members) { + String name = _computeMemberName(member); + if (name == null) continue; + assert(!_members.containsKey(name), + "Duplicate member '$name' in $_members trying to add $member."); + _members[name] = new _MemberData(member); + } + } + return _members[name]; + } + + String toString() => '_LibraryData($node(${identityHashCode(node)}))'; +} + +/// Helper for looking up members from an [ir.Class] node. +class _ClassData { + /// The [ir.Class] that defines the class. + final ir.Class node; + + /// Cache of [_MemberData] for members in this class. + Map _members; + + _ClassData(this.node); + + /// Returns the [_MemberData] for the member uniquely identified by [name] in + /// this class. + _MemberData lookupMember(String name) { + if (_members == null) { + _members = {}; + for (ir.Member member in node.members) { + String name = _computeMemberName(member); + if (name == null) continue; + assert(!_members.containsKey(name), + "Duplicate member '$name' in $_members trying to add $member."); + _members[name] = new _MemberData(member); + } + } + return _members[name]; + } + + String toString() => '_ClassData($node(${identityHashCode(node)}))'; +} + +/// Helper for looking up child [ir.TreeNode]s of a [ir.Member] node. +class _MemberData { + /// The [ir.Member] that defines the member. + final ir.Member node; + + /// Cached index to [ir.TreeNode] map used for deserialization of + /// [ir.TreeNode]s. + Map _indexToNodeMap; + + /// Cached [ir.TreeNode] to index map used for serialization of + /// [ir.TreeNode]s. + Map _nodeToIndexMap; + + _MemberData(this.node); + + void _ensureMaps() { + if (_indexToNodeMap == null) { + _indexToNodeMap = {}; + _nodeToIndexMap = {}; + node.accept( + new _TreeNodeIndexerVisitor(_indexToNodeMap, _nodeToIndexMap)); + } + } + + /// Returns the [ir.TreeNode] corresponding to [index] in this member. + ir.TreeNode getTreeNodeByIndex(int index) { + _ensureMaps(); + ir.TreeNode treeNode = _indexToNodeMap[index]; + assert(treeNode != null, "No TreeNode found for index $index in $node."); + return treeNode; + } + + /// Returns the index corresponding to [ir.TreeNode] in this member. + int getIndexByTreeNode(ir.TreeNode node) { + _ensureMaps(); + int index = _nodeToIndexMap[node]; + assert(index != null, "No index found for ${node.runtimeType}."); + return index; + } + + String toString() => '_MemberData($node(${identityHashCode(node)}))'; +} diff --git a/pkg/compiler/lib/src/serialization/mixins.dart b/pkg/compiler/lib/src/serialization/mixins.dart new file mode 100644 index 0000000000000..e5b009fbb8442 --- /dev/null +++ b/pkg/compiler/lib/src/serialization/mixins.dart @@ -0,0 +1,569 @@ +// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +part of 'serialization.dart'; + +/// Mixin that implements all convenience methods of [DataSource]. +abstract class DataSourceMixin implements DataSource { + @override + E readValueOrNull(E f()) { + bool hasValue = readBool(); + if (hasValue) { + return f(); + } + return null; + } + + @override + List readList(E f(), {bool emptyAsNull: false}) { + int count = readInt(); + if (count == 0 && emptyAsNull) return null; + List list = new List(count); + for (int i = 0; i < count; i++) { + list[i] = f(); + } + return list; + } + + @override + int readIntOrNull() { + bool hasValue = readBool(); + if (hasValue) { + return readInt(); + } + return null; + } + + @override + String readStringOrNull() { + bool hasValue = readBool(); + if (hasValue) { + return readString(); + } + return null; + } + + @override + List readStrings({bool emptyAsNull: false}) { + int count = readInt(); + if (count == 0 && emptyAsNull) return null; + List list = new List(count); + for (int i = 0; i < count; i++) { + list[i] = readString(); + } + return list; + } + + @override + List readDartTypes({bool emptyAsNull: false}) { + int count = readInt(); + if (count == 0 && emptyAsNull) return null; + List list = new List(count); + for (int i = 0; i < count; i++) { + list[i] = readDartType(); + } + return list; + } + + @override + List readTypeParameterNodes({bool emptyAsNull: false}) { + int count = readInt(); + if (count == 0 && emptyAsNull) return null; + List list = new List(count); + for (int i = 0; i < count; i++) { + list[i] = readTypeParameterNode(); + } + return list; + } + + @override + List readMembers({bool emptyAsNull: false}) { + int count = readInt(); + if (count == 0 && emptyAsNull) return null; + List list = new List(count); + for (int i = 0; i < count; i++) { + MemberEntity member = readMember(); + list[i] = member; + } + return list; + } + + @override + List readMemberNodes({bool emptyAsNull: false}) { + int count = readInt(); + if (count == 0 && emptyAsNull) return null; + List list = new List(count); + for (int i = 0; i < count; i++) { + ir.Member value = readMemberNode(); + list[i] = value; + } + return list; + } + + @override + List readClasses({bool emptyAsNull: false}) { + int count = readInt(); + if (count == 0 && emptyAsNull) return null; + List list = new List(count); + for (int i = 0; i < count; i++) { + ClassEntity cls = readClass(); + list[i] = cls; + } + return list; + } + + @override + Map readLibraryMap(V f(), + {bool emptyAsNull: false}) { + int count = readInt(); + if (count == 0 && emptyAsNull) return null; + Map map = {}; + for (int i = 0; i < count; i++) { + LibraryEntity library = readLibrary(); + V value = f(); + map[library] = value; + } + return map; + } + + @override + Map readClassMap(V f(), + {bool emptyAsNull: false}) { + int count = readInt(); + if (count == 0 && emptyAsNull) return null; + Map map = {}; + for (int i = 0; i < count; i++) { + ClassEntity cls = readClass(); + V value = f(); + map[cls] = value; + } + return map; + } + + @override + Map readMemberMap(V f(), + {bool emptyAsNull: false}) { + int count = readInt(); + if (count == 0 && emptyAsNull) return null; + Map map = {}; + for (int i = 0; i < count; i++) { + MemberEntity member = readMember(); + V value = f(); + map[member] = value; + } + return map; + } + + @override + Map readTreeNodeMap(V f(), + {bool emptyAsNull: false}) { + int count = readInt(); + if (count == 0 && emptyAsNull) return null; + Map map = {}; + for (int i = 0; i < count; i++) { + ir.TreeNode node = readTreeNode(); + V value = f(); + map[node] = value; + } + return map; + } + + @override + List readLocals({bool emptyAsNull: false}) { + int count = readInt(); + if (count == 0 && emptyAsNull) return null; + List list = new List(count); + for (int i = 0; i < count; i++) { + Local local = readLocal(); + list[i] = local; + } + return list; + } + + @override + Map readLocalMap(V f(), {bool emptyAsNull: false}) { + int count = readInt(); + if (count == 0 && emptyAsNull) return null; + Map map = {}; + for (int i = 0; i < count; i++) { + Local local = readLocal(); + V value = f(); + map[local] = value; + } + return map; + } + + @override + List readTreeNodes({bool emptyAsNull: false}) { + int count = readInt(); + if (count == 0 && emptyAsNull) return null; + List list = new List(count); + for (int i = 0; i < count; i++) { + ir.TreeNode node = readTreeNode(); + list[i] = node; + } + return list; + } + + @override + Map readStringMap(V f(), {bool emptyAsNull: false}) { + int count = readInt(); + if (count == 0 && emptyAsNull) return null; + Map map = {}; + for (int i = 0; i < count; i++) { + String key = readString(); + V value = f(); + map[key] = value; + } + return map; + } + + @override + IndexedClass readClassOrNull() { + bool hasClass = readBool(); + if (hasClass) { + return readClass(); + } + return null; + } + + @override + ir.TreeNode readTreeNodeOrNull() { + bool hasValue = readBool(); + if (hasValue) { + return readTreeNode(); + } + return null; + } + + @override + IndexedMember readMemberOrNull() { + bool hasValue = readBool(); + if (hasValue) { + return readMember(); + } + return null; + } + + @override + Local readLocalOrNull() { + bool hasValue = readBool(); + if (hasValue) { + return readLocal(); + } + return null; + } + + @override + Map readConstantMap(V f(), + {bool emptyAsNull: false}) { + int count = readInt(); + if (count == 0 && emptyAsNull) return null; + Map map = {}; + for (int i = 0; i < count; i++) { + ConstantValue key = readConstant(); + V value = f(); + map[key] = value; + } + return map; + } + + @override + IndexedLibrary readLibraryOrNull() { + bool hasValue = readBool(); + if (hasValue) { + return readLibrary(); + } + return null; + } +} + +/// Mixin that implements all convenience methods of [DataSink]. +abstract class DataSinkMixin implements DataSink { + @override + void writeIntOrNull(int value) { + writeBool(value != null); + if (value != null) { + writeInt(value); + } + } + + @override + void writeStringOrNull(String value) { + writeBool(value != null); + if (value != null) { + writeString(value); + } + } + + @override + void writeClassOrNull(IndexedClass value) { + writeBool(value != null); + if (value != null) { + writeClass(value); + } + } + + @override + void writeLocalOrNull(Local value) { + writeBool(value != null); + if (value != null) { + writeLocal(value); + } + } + + @override + void writeClasses(Iterable values, {bool allowNull: false}) { + if (values == null) { + assert(allowNull); + writeInt(0); + } else { + writeInt(values.length); + for (IndexedClass value in values) { + writeClass(value); + } + } + } + + @override + void writeTreeNodes(Iterable values, {bool allowNull: false}) { + if (values == null) { + assert(allowNull); + writeInt(0); + } else { + writeInt(values.length); + for (ir.TreeNode value in values) { + writeTreeNode(value); + } + } + } + + @override + void writeStrings(Iterable values, {bool allowNull: false}) { + if (values == null) { + assert(allowNull); + writeInt(0); + } else { + writeInt(values.length); + for (String value in values) { + writeString(value); + } + } + } + + @override + void writeMemberNodes(Iterable values, {bool allowNull: false}) { + if (values == null) { + assert(allowNull); + writeInt(0); + } else { + writeInt(values.length); + for (ir.Member value in values) { + writeMemberNode(value); + } + } + } + + @override + void writeDartTypes(Iterable values, {bool allowNull: false}) { + if (values == null) { + assert(allowNull); + writeInt(0); + } else { + writeInt(values.length); + for (DartType value in values) { + writeDartType(value); + } + } + } + + @override + void writeLibraryMap(Map map, void f(V value), + {bool allowNull: false}) { + if (map == null) { + assert(allowNull); + writeInt(0); + } else { + writeInt(map.length); + map.forEach((LibraryEntity library, V value) { + writeLibrary(library); + f(value); + }); + } + } + + @override + void writeClassMap(Map map, void f(V value), + {bool allowNull: false}) { + if (map == null) { + assert(allowNull); + writeInt(0); + } else { + writeInt(map.length); + map.forEach((ClassEntity cls, V value) { + writeClass(cls); + f(value); + }); + } + } + + @override + void writeMemberMap(Map map, void f(V value), + {bool allowNull: false}) { + if (map == null) { + assert(allowNull); + writeInt(0); + } else { + writeInt(map.length); + map.forEach((MemberEntity member, V value) { + writeMember(member); + f(value); + }); + } + } + + @override + void writeStringMap(Map map, void f(V value), + {bool allowNull: false}) { + if (map == null) { + assert(allowNull); + writeInt(0); + } else { + writeInt(map.length); + map.forEach((String key, V value) { + writeString(key); + f(value); + }); + } + } + + @override + void writeLocals(Iterable values, {bool allowNull: false}) { + if (values == null) { + assert(allowNull); + writeInt(0); + } else { + writeInt(values.length); + for (Local value in values) { + writeLocal(value); + } + } + } + + @override + void writeLocalMap(Map map, void f(V value), + {bool allowNull: false}) { + if (map == null) { + assert(allowNull); + writeInt(0); + } else { + writeInt(map.length); + map.forEach((Local key, V value) { + writeLocal(key); + f(value); + }); + } + } + + @override + void writeTreeNodeMap(Map map, void f(V value), + {bool allowNull: false}) { + if (map == null) { + assert(allowNull); + writeInt(0); + } else { + writeInt(map.length); + map.forEach((ir.TreeNode key, V value) { + writeTreeNode(key); + f(value); + }); + } + } + + @override + void writeList(Iterable values, void f(E value), + {bool allowNull: false}) { + if (values == null) { + assert(allowNull); + writeInt(0); + } else { + writeInt(values.length); + values.forEach(f); + } + } + + @override + void writeTreeNodeOrNull(ir.TreeNode value) { + writeBool(value != null); + if (value != null) { + writeTreeNode(value); + } + } + + @override + void writeValueOrNull(E value, void f(E value)) { + writeBool(value != null); + if (value != null) { + f(value); + } + } + + @override + void writeMemberOrNull(IndexedMember value) { + writeBool(value != null); + if (value != null) { + writeMember(value); + } + } + + @override + void writeMembers(Iterable values, {bool allowNull: false}) { + if (values == null) { + assert(allowNull); + writeInt(0); + } else { + writeInt(values.length); + for (IndexedMember value in values) { + writeMember(value); + } + } + } + + @override + void writeTypeParameterNodes(Iterable values, + {bool allowNull: false}) { + if (values == null) { + assert(allowNull); + writeInt(0); + } else { + writeInt(values.length); + for (ir.TypeParameter value in values) { + writeTypeParameterNode(value); + } + } + } + + @override + void writeConstantMap(Map map, void f(V value), + {bool allowNull: false}) { + if (map == null) { + assert(allowNull); + writeInt(0); + } else { + writeInt(map.length); + map.forEach((ConstantValue key, V value) { + writeConstant(key); + f(value); + }); + } + } + + @override + void writeLibraryOrNull(IndexedLibrary value) { + writeBool(value != null); + if (value != null) { + writeLibrary(value); + } + } +} diff --git a/pkg/compiler/lib/src/serialization/node_indexer.dart b/pkg/compiler/lib/src/serialization/node_indexer.dart new file mode 100644 index 0000000000000..c9370f16f5008 --- /dev/null +++ b/pkg/compiler/lib/src/serialization/node_indexer.dart @@ -0,0 +1,142 @@ +// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +part of 'serialization.dart'; + +/// Visitor that ascribes an index to all [ir.TreeNode]s that potentially +/// needed for serialization and deserialization. +class _TreeNodeIndexerVisitor extends ir.Visitor { + int _currentIndex = 0; + final Map _indexToNodeMap; + final Map _nodeToIndexMap; + + _TreeNodeIndexerVisitor(this._indexToNodeMap, this._nodeToIndexMap); + + void registerNode(ir.TreeNode node) { + _indexToNodeMap[_currentIndex] = node; + _nodeToIndexMap[node] = _currentIndex; + _currentIndex++; + } + + @override + void defaultTreeNode(ir.TreeNode node) { + node.visitChildren(this); + } + + @override + void visitFunctionExpression(ir.FunctionExpression node) { + registerNode(node); + super.visitFunctionExpression(node); + } + + @override + void visitFunctionDeclaration(ir.FunctionDeclaration node) { + registerNode(node); + super.visitFunctionDeclaration(node); + } + + @override + void visitBlock(ir.Block node) { + registerNode(node); + super.visitBlock(node); + } + + @override + void visitVariableDeclaration(ir.VariableDeclaration node) { + if (node.parent is! ir.FunctionDeclaration) { + registerNode(node); + } + super.visitVariableDeclaration(node); + } + + @override + void visitSwitchStatement(ir.SwitchStatement node) { + registerNode(node); + super.visitSwitchStatement(node); + } + + @override + void visitForStatement(ir.ForStatement node) { + registerNode(node); + super.visitForStatement(node); + } + + @override + void visitForInStatement(ir.ForInStatement node) { + registerNode(node); + super.visitForInStatement(node); + } + + @override + void visitWhileStatement(ir.WhileStatement node) { + registerNode(node); + super.visitWhileStatement(node); + } + + @override + void visitDoStatement(ir.DoStatement node) { + registerNode(node); + super.visitDoStatement(node); + } + + @override + void visitBreakStatement(ir.BreakStatement node) { + registerNode(node); + super.visitBreakStatement(node); + } + + @override + void visitListLiteral(ir.ListLiteral node) { + registerNode(node); + super.visitListLiteral(node); + } + + @override + void visitMapLiteral(ir.MapLiteral node) { + registerNode(node); + super.visitMapLiteral(node); + } + + @override + void visitPropertyGet(ir.PropertyGet node) { + registerNode(node); + super.visitPropertyGet(node); + } + + @override + void visitPropertySet(ir.PropertySet node) { + registerNode(node); + super.visitPropertySet(node); + } + + @override + void visitMethodInvocation(ir.MethodInvocation node) { + registerNode(node); + super.visitMethodInvocation(node); + } + + @override + void visitDirectPropertyGet(ir.DirectPropertyGet node) { + registerNode(node); + super.visitDirectPropertyGet(node); + } + + @override + void visitDirectPropertySet(ir.DirectPropertySet node) { + registerNode(node); + super.visitDirectPropertySet(node); + } + + @override + void visitDirectMethodInvocation(ir.DirectMethodInvocation node) { + registerNode(node); + super.visitDirectMethodInvocation(node); + } + + @override + void visitStaticInvocation(ir.StaticInvocation node) { + registerNode(node); + super.visitStaticInvocation(node); + } +} diff --git a/pkg/compiler/lib/src/serialization/object_sink.dart b/pkg/compiler/lib/src/serialization/object_sink.dart new file mode 100644 index 0000000000000..195db2a818fc4 --- /dev/null +++ b/pkg/compiler/lib/src/serialization/object_sink.dart @@ -0,0 +1,53 @@ +// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +part of 'serialization.dart'; + +/// [DataSink] that writes to a list of objects, useful for debugging +/// inconsistencies between serialization and deserialization. +/// +/// This data sink works together with [ObjectSource]. +class ObjectSink extends AbstractDataSink { + List _data; + + ObjectSink(this._data, {bool useDataKinds}) + : super(useDataKinds: useDataKinds); + + void _begin(String tag) { + _data.add(new Tag('begin:$tag')); + } + + void _end(String tag) { + _data.add(new Tag('end:$tag')); + } + + @override + void _writeEnum(dynamic value) { + assert(value != null); + _data.add(value); + } + + @override + void _writeInt(int value) { + assert(value != null); + _data.add(value); + } + + @override + void _writeString(String value) { + assert(value != null); + _data.add(value); + } + + @override + void _writeUri(Uri value) { + assert(value != null); + _data.add(value); + } + + @override + void close() { + _data = null; + } +} diff --git a/pkg/compiler/lib/src/serialization/object_source.dart b/pkg/compiler/lib/src/serialization/object_source.dart new file mode 100644 index 0000000000000..cae965b664257 --- /dev/null +++ b/pkg/compiler/lib/src/serialization/object_source.dart @@ -0,0 +1,71 @@ +// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +part of 'serialization.dart'; + +/// [DataSource] that read from a list of objects, useful for debugging +/// inconsistencies between serialization and deserialization. +/// +/// This data source works together with [ObjectSink]. +class ObjectSource extends AbstractDataSource { + int _index = 0; + final List _data; + + ObjectSource(this._data, {bool useDataKinds}) + : super(useDataKinds: useDataKinds); + + T _read() { + dynamic value = _data[_index++]; + assert(value is T, "Expected $T value, found $value.$_errorContext"); + return value; + } + + void _begin(String tag) { + Tag expectedTag = new Tag('begin:$tag'); + Tag actualTag = _read(); + assert( + expectedTag == actualTag, + "Unexpected begin tag. " + "Expected $expectedTag, found $actualTag.$_errorContext"); + } + + void _end(String tag) { + Tag expectedTag = new Tag('end:$tag'); + Tag actualTag = _read(); + assert( + expectedTag == actualTag, + "Unexpected end tag. " + "Expected $expectedTag, found $actualTag.$_errorContext"); + } + + @override + String _readString() => _read(); + + @override + E _readEnum(List values) => _read(); + + @override + Uri _readUri() => _read(); + + @override + int _readInt() => _read(); + + @override + String get _errorContext { + StringBuffer sb = new StringBuffer(); + for (int i = _index - 50; i < _index + 10; i++) { + if (i >= 0 && i < _data.length) { + if (i == _index - 1) { + sb.write('\n> '); + } else { + sb.write('\n '); + } + sb.write(i); + sb.write(' '); + sb.write(_data[i]); + } + } + return sb.toString(); + } +} diff --git a/pkg/compiler/lib/src/serialization/serialization.dart b/pkg/compiler/lib/src/serialization/serialization.dart new file mode 100644 index 0000000000000..a5b738af7a802 --- /dev/null +++ b/pkg/compiler/lib/src/serialization/serialization.dart @@ -0,0 +1,560 @@ +// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:convert'; +import 'dart:typed_data'; +import 'package:kernel/ast.dart' as ir; +import 'package:kernel/binary/ast_to_binary.dart'; +import '../closure.dart'; +import '../constants/values.dart'; +import '../diagnostics/source_span.dart'; +import '../elements/entities.dart'; +import '../elements/indexed.dart'; +import '../elements/types.dart'; +import '../js_model/closure.dart'; +import '../js_model/locals.dart'; + +part 'abstract_sink.dart'; +part 'abstract_source.dart'; +part 'binary_sink.dart'; +part 'binary_source.dart'; +part 'helpers.dart'; +part 'member_data.dart'; +part 'mixins.dart'; +part 'node_indexer.dart'; +part 'object_sink.dart'; +part 'object_source.dart'; + +/// Interface for serialization. +abstract class DataSink { + /// Flushes any pending data and closes this data sink. + /// + /// The data sink can no longer be written to after closing. + void close(); + + /// Registers that the section [tag] starts. + /// + /// This is used for debugging to verify that sections are correctly aligned + /// between serialization and deserialization. + void begin(String tag); + + /// Registers that the section [tag] ends. + /// + /// This is used for debugging to verify that sections are correctly aligned + /// between serialization and deserialization. + void end(String tag); + + /// Writes the potentially `null` [value] to this data sink. If [value] is + /// non-null [f] is called to write the non-null value to the data sink. + /// + /// This is a convenience method to be used together with + /// [DataSource.readValueOrNull]. + void writeValueOrNull(E value, void f(E value)); + + /// Writes the [values] to this data sink calling [f] to write each value to + /// the data sink. If [allowNull] is `true`, [values] is allowed to be `null`. + /// + /// This is a convenience method to be used together with + /// [DataSource.readList]. + void writeList(Iterable values, void f(E value), + {bool allowNull: false}); + + /// Writes the boolean [value] to this data sink. + void writeBool(bool value); + + /// Writes the non-negative integer [value] to this data sink. + void writeInt(int value); + + /// Writes the potentially `null` non-negative [value] to this data sink. + /// + /// This is a convenience method to be used together with + /// [DataSource.readIntOrNull]. + void writeIntOrNull(int value); + + /// Writes the string [value] to this data sink. + void writeString(String value); + + /// Writes the potentially `null` string [value] to this data sink. + /// + /// This is a convenience method to be used together with + /// [DataSource.readStringOrNull]. + void writeStringOrNull(String value); + + /// Writes the string [values] to this data sink. If [allowNull] is `true`, + /// [values] is allowed to be `null`. + /// + /// This is a convenience method to be used together with + /// [DataSource.readStrings]. + void writeStrings(Iterable values, {bool allowNull: false}); + + /// Writes the [map] from string to [V] values to this data sink, calling [f] + /// to write each value to the data sink. If [allowNull] is `true`, [map] is + /// allowed to be `null`. + /// + /// This is a convenience method to be used together with + /// [DataSource.readStringMap]. + void writeStringMap(Map map, void f(V value), + {bool allowNull: false}); + + /// Writes the enum value [value] to this data sink. + // TODO(johnniwinther): Change the signature to + // `void writeEnum>(E value);` when an interface for enums + // is added to the language. + void writeEnum(dynamic value); + + /// Writes the URI [value] to this data sink. + void writeUri(Uri value); + + /// Writes a reference to the kernel library node [value] to this data sink. + void writeLibraryNode(ir.Library value); + + /// Writes a reference to the kernel class node [value] to this data sink. + void writeClassNode(ir.Class value); + + /// Writes a reference to the kernel member node [value] to this data sink. + void writeMemberNode(ir.Member value); + + /// Writes references to the kernel member node [values] to this data sink. + /// If [allowNull] is `true`, [values] is allowed to be `null`. + /// + /// This is a convenience method to be used together with + /// [DataSource.readMemberNodes]. + void writeMemberNodes(Iterable values, {bool allowNull: false}); + + /// Writes a reference to the kernel tree node [value] to this data sink. + void writeTreeNode(ir.TreeNode value); + + /// Writes a reference to the potentially `null` kernel tree node [value] + /// to this data sink. + /// + /// This is a convenience method to be used together with + /// [DataSource.readTreeNodeOrNull]. + void writeTreeNodeOrNull(ir.TreeNode value); + + /// Writes references to the kernel tree node [values] to this data sink. + /// If [allowNull] is `true`, [values] is allowed to be `null`. + /// + /// This is a convenience method to be used together with + /// [DataSource.readTreeNodes]. + void writeTreeNodes(Iterable values, {bool allowNull: false}); + + /// Writes the [map] from references to kernel tree nodes to [V] values to + /// this data sink, calling [f] to write each value to the data sink. If + /// [allowNull] is `true`, [map] is allowed to be `null`. + /// + /// This is a convenience method to be used together with + /// [DataSource.readTreeNodeMap]. + void writeTreeNodeMap(Map map, void f(V value), + {bool allowNull: false}); + + /// Writes a reference to the kernel type parameter node [value] to this data + /// sink. + void writeTypeParameterNode(ir.TypeParameter value); + + /// Writes references to the kernel type parameter node [values] to this data + /// sink. + /// If [allowNull] is `true`, [values] is allowed to be `null`. + /// + /// This is a convenience method to be used together with + /// [DataSource.readTypeParameterNodes]. + void writeTypeParameterNodes(Iterable values, + {bool allowNull: false}); + + /// Writes the type [value] to this data sink. If [allowNull] is `true`, + /// [value] is allowed to be `null`. + void writeDartType(DartType value, {bool allowNull: false}); + + /// Writes the type [values] to this data sink. If [allowNull] is `true`, + /// [values] is allowed to be `null`. + /// + /// This is a convenience method to be used together with + /// [DataSource.readDartTypes]. + void writeDartTypes(Iterable values, {bool allowNull: false}); + + /// Writes the source span [value] to this data sink. + void writeSourceSpan(SourceSpan value); + + /// Writes a reference to the indexed library [value] to this data sink. + void writeLibrary(IndexedLibrary value); + + /// Writes a reference to the potentially `null` indexed library [value] + /// to this data sink. + /// + /// This is a convenience method to be used together with + /// [DataSource.readLibraryOrNull]. + void writeLibraryOrNull(IndexedLibrary value); + + /// Writes the [map] from references to indexed libraries to [V] values to + /// this data sink, calling [f] to write each value to the data sink. If + /// [allowNull] is `true`, [map] is allowed to be `null`. + /// + /// This is a convenience method to be used together with + /// [DataSource.readLibraryMap]. + void writeLibraryMap(Map map, void f(V value), + {bool allowNull: false}); + + /// Writes a reference to the indexed class [value] to this data sink. + void writeClass(IndexedClass value); + + /// Writes a reference to the potentially `null` indexed class [value] + /// to this data sink. + /// + /// This is a convenience method to be used together with + /// [DataSource.readClassOrNull]. + void writeClassOrNull(IndexedClass value); + + /// Writes references to the indexed class [values] to this data sink. If + /// [allowNull] is `true`, [values] is allowed to be `null`. + /// + /// This is a convenience method to be used together with + /// [DataSource.readClasses]. + void writeClasses(Iterable values, {bool allowNull: false}); + + /// Writes the [map] from references to indexed classes to [V] values to this + /// data sink, calling [f] to write each value to the data sink. If + /// [allowNull] is `true`, [map] is allowed to be `null`. + /// + /// This is a convenience method to be used together with + /// [DataSource.readClassMap]. + void writeClassMap(Map map, void f(V value), + {bool allowNull: false}); + + /// Writes a reference to the indexed typedef [value] to this data sink. + void writeTypedef(IndexedTypedef value); + + /// Writes a reference to the indexed member [value] to this data sink. + void writeMember(IndexedMember value); + + /// Writes a reference to the potentially `null` indexed member [value] + /// to this data sink. + /// + /// This is a convenience method to be used together with + /// [DataSource.readMemberOrNull]. + void writeMemberOrNull(IndexedMember value); + + /// Writes references to the indexed member [values] to this data sink. If + /// [allowNull] is `true`, [values] is allowed to be `null`. + /// + /// This is a convenience method to be used together with + /// [DataSource.readMembers]. + void writeMembers(Iterable values, {bool allowNull: false}); + + /// Writes the [map] from references to indexed members to [V] values to this + /// data sink, calling [f] to write each value to the data sink. If + /// [allowNull] is `true`, [map] is allowed to be `null`. + /// + /// This is a convenience method to be used together with + /// [DataSource.readMemberMap]. + void writeMemberMap(Map map, void f(V value), + {bool allowNull: false}); + + /// Writes a reference to the local [value] to this data sink. + void writeLocal(Local local); + + /// Writes a reference to the potentially `null` local [value] + /// to this data sink. + /// + /// This is a convenience method to be used together with + /// [DataSource.readLocalOrNull]. + void writeLocalOrNull(Local local); + + /// Writes references to the local [values] to this data sink. If [allowNull] + /// is `true`, [values] is allowed to be `null`. + /// + /// This is a convenience method to be used together with + /// [DataSource.readLocals]. + void writeLocals(Iterable locals, {bool allowNull: false}); + + /// Writes the [map] from references to locals to [V] values to this data + /// sink, calling [f] to write each value to the data sink. If [allowNull] is + /// `true`, [map] is allowed to be `null`. + /// + /// This is a convenience method to be used together with + /// [DataSource.readLocalMap]. + void writeLocalMap(Map map, void f(V value), + {bool allowNull: false}); + + /// Writes the constant [value] to this data sink. + void writeConstant(ConstantValue value); + + /// Writes the [map] from constant values to [V] values to this data sink, + /// calling [f] to write each value to the data sink. If [allowNull] is + /// `true`, [map] is allowed to be `null`. + /// + /// This is a convenience method to be used together with + /// [DataSource.readConstantMap]. + void writeConstantMap(Map map, void f(V value), + {bool allowNull: false}); +} + +/// Interface for deserialization. +abstract class DataSource { + /// Registers that the section [tag] starts. + /// + /// This is used for debugging to verify that sections are correctly aligned + /// between serialization and deserialization. + void begin(String tag); + + /// Registers that the section [tag] ends. + /// + /// This is used for debugging to verify that sections are correctly aligned + /// between serialization and deserialization. + void end(String tag); + + /// Registers a [ComponentLookup] object with this data source to support + /// deserialization of references to kernel nodes. + void registerComponentLookup(ComponentLookup componentLookup); + + /// Registers an [EntityLookup] object with this data source to support + /// deserialization of references to indexed entities. + void registerEntityLookup(EntityLookup entityLookup); + + /// Registers a [LocalLookup] object with this data source to support + /// deserialization of references to locals. + void registerLocalLookup(LocalLookup localLookup); + + /// Reads a potentially `null` [E] value from this data source, calling [f] to + /// read the non-null value from the data source. + /// + /// This is a convenience method to be used together with + /// [DataSink.writeValueOrNull]. + E readValueOrNull(E f()); + + /// Reads a list of [E] values from this data source. If [emptyAsNull] is + /// `true`, `null` is returned instead of an empty list. + /// + /// This is a convenience method to be used together with + /// [DataSink.writeList]. + List readList(E f(), {bool emptyAsNull: false}); + + /// Reads a boolean value from this data source. + bool readBool(); + + /// Reads a non-negative integer value from this data source. + int readInt(); + + /// Reads a potentially `null` non-negative integer value from this data + /// source. + /// + /// This is a convenience method to be used together with + /// [DataSink.writeIntOrNull]. + int readIntOrNull(); + + /// Reads a string value from this data source. + String readString(); + + /// Reads a potentially `null` string value from this data source. + /// + /// This is a convenience method to be used together with + /// [DataSink.writeStringOrNull]. + String readStringOrNull(); + + /// Reads a list of string values from this data source. If [emptyAsNull] is + /// `true`, `null` is returned instead of an empty list. + /// + /// This is a convenience method to be used together with + /// [DataSink.writeStrings]. + List readStrings({bool emptyAsNull: false}); + + /// Reads a map from string values to [V] values from this data source, + /// calling [f] to read each value from the data source. If [emptyAsNull] is + /// `true`, `null` is returned instead of an empty map. + /// + /// This is a convenience method to be used together with + /// [DataSink.writeStringMap]. + Map readStringMap(V f(), {bool emptyAsNull: false}); + + /// Reads an enum value from the list of enum [values] from this data source. + /// + /// The [values] argument is intended to be the static `.values` field on + /// enum classes, for instance: + /// + /// enum Foo { bar, baz } + /// ... + /// Foo foo = source.readEnum(Foo.values); + /// + E readEnum(List values); + + /// Reads a URI value from this data source. + Uri readUri(); + + /// Reads a reference to a kernel library node from this data source. + ir.Library readLibraryNode(); + + /// Reads a reference to a kernel class node from this data source. + ir.Class readClassNode(); + + /// Reads a reference to a kernel member node from this data source. + ir.Member readMemberNode(); + + /// Reads a list of references to kernel member nodes from this data source. + /// If [emptyAsNull] is `true`, `null` is returned instead of an empty list. + /// + /// This is a convenience method to be used together with + /// [DataSink.writeMemberNodes]. + List readMemberNodes( + {bool emptyAsNull: false}); + + /// Reads a reference to a kernel tree node from this data source. + ir.TreeNode readTreeNode(); + + /// Reads a reference to a potentially `null` kernel tree node from this data + /// source. + ir.TreeNode readTreeNodeOrNull(); + + /// Reads a list of references to kernel tree nodes from this data source. + /// If [emptyAsNull] is `true`, `null` is returned instead of an empty list. + /// + /// This is a convenience method to be used together with + /// [DataSink.writeTreeNodes]. + List readTreeNodes({bool emptyAsNull: false}); + + /// Reads a map from kernel tree nodes to [V] values from this data source, + /// calling [f] to read each value from the data source. If [emptyAsNull] is + /// `true`, `null` is returned instead of an empty map. + /// + /// This is a convenience method to be used together with + /// [DataSink.writeTreeNodeMap]. + Map readTreeNodeMap(V f(), + {bool emptyAsNull: false}); + + /// Reads a reference to a kernel type parameter node from this data source. + ir.TypeParameter readTypeParameterNode(); + + /// Reads a list of references to kernel type parameter nodes from this data + /// source. If [emptyAsNull] is `true`, `null` is returned instead of an empty + /// list. + /// + /// This is a convenience method to be used together with + /// [DataSink.writeTypeParameterNodes]. + List readTypeParameterNodes({bool emptyAsNull: false}); + + /// Reads a type from this data source. If [allowNull], the returned type is + /// allowed to be `null`. + DartType readDartType({bool allowNull: false}); + + /// Reads a list of types from this data source. If [emptyAsNull] is `true`, + /// `null` is returned instead of an empty list. + /// + /// This is a convenience method to be used together with + /// [DataSink.writeDartTypes]. + List readDartTypes({bool emptyAsNull: false}); + + /// Reads a source span from this data source. + SourceSpan readSourceSpan(); + + /// Reads a reference to an indexed library from this data source. + IndexedLibrary readLibrary(); + + /// Reads a reference to a potentially `null` indexed library from this data + /// source. + IndexedLibrary readLibraryOrNull(); + Map readLibraryMap(V f(), + {bool emptyAsNull: false}); + + /// Reads a reference to an indexed class from this data source. + IndexedClass readClass(); + + /// Reads a reference to a potentially `null` indexed class from this data + /// source. + IndexedClass readClassOrNull(); + + /// Reads a list of references to indexed classes from this data source. + /// If [emptyAsNull] is `true`, `null` is returned instead of an empty list. + /// + /// This is a convenience method to be used together with + /// [DataSink.writeClasses]. + List readClasses({bool emptyAsNull: false}); + + /// Reads a map from indexed classes to [V] values from this data source, + /// calling [f] to read each value from the data source. If [emptyAsNull] is + /// `true`, `null` is returned instead of an empty map. + /// + /// This is a convenience method to be used together with + /// [DataSink.writeClassMap]. + Map readClassMap(V f(), + {bool emptyAsNull: false}); + + /// Reads a reference to an indexed typedef from this data source. + IndexedTypedef readTypedef(); + + /// Reads a reference to an indexed member from this data source. + IndexedMember readMember(); + + /// Reads a reference to a potentially `null` indexed member from this data + /// source. + IndexedMember readMemberOrNull(); + + /// Reads a list of references to indexed members from this data source. + /// If [emptyAsNull] is `true`, `null` is returned instead of an empty list. + /// + /// This is a convenience method to be used together with + /// [DataSink.writeMembers]. + List readMembers({bool emptyAsNull: false}); + + /// Reads a map from indexed members to [V] values from this data source, + /// calling [f] to read each value from the data source. If [emptyAsNull] is + /// `true`, `null` is returned instead of an empty map. + /// + /// This is a convenience method to be used together with + /// [DataSink.writeMemberMap]. + Map readMemberMap(V f(), + {bool emptyAsNull: false}); + + /// Reads a reference to a local from this data source. + Local readLocal(); + + /// Reads a reference to a potentially `null` local from this data source. + Local readLocalOrNull(); + + /// Reads a list of references to locals from this data source. If + /// [emptyAsNull] is `true`, `null` is returned instead of an empty list. + /// + /// This is a convenience method to be used together with + /// [DataSink.writeLocals]. + List readLocals({bool emptyAsNull: false}); + + /// Reads a map from locals to [V] values from this data source, calling [f] + /// to read each value from the data source. If [emptyAsNull] is `true`, + /// `null` is returned instead of an empty map. + /// + /// This is a convenience method to be used together with + /// [DataSink.writeLocalMap]. + Map readLocalMap(V f(), {bool emptyAsNull: false}); + + /// Reads a constant value from this data source. + ConstantValue readConstant(); + + /// Reads a map from constant values to [V] values from this data source, + /// calling [f] to read each value from the data source. If [emptyAsNull] is + /// `true`, `null` is returned instead of an empty map. + /// + /// This is a convenience method to be used together with + /// [DataSink.writeConstantMap]. + Map readConstantMap(V f(), + {bool emptyAsNull: false}); +} + +/// Interface used for looking up entities by index during deserialization. +abstract class EntityLookup { + /// Returns the indexed library corresponding to [index]. + IndexedLibrary getLibraryByIndex(int index); + + /// Returns the indexed class corresponding to [index]. + IndexedClass getClassByIndex(int index); + + /// Returns the indexed typedef corresponding to [index]. + IndexedTypedef getTypedefByIndex(int index); + + /// Returns the indexed member corresponding to [index]. + IndexedMember getMemberByIndex(int index); + + /// Returns the indexed type variable corresponding to [index]. + IndexedTypeVariable getTypeVariableByIndex(int index); +} + +/// Interface used for looking up locals by index during deserialization. +abstract class LocalLookup { + Local getLocalByIndex(MemberEntity memberContext, int index); +} diff --git a/pkg/compiler/lib/src/types/abstract_value_domain.dart b/pkg/compiler/lib/src/types/abstract_value_domain.dart index 37ff14d770ae9..d6cd5d03d7baa 100644 --- a/pkg/compiler/lib/src/types/abstract_value_domain.dart +++ b/pkg/compiler/lib/src/types/abstract_value_domain.dart @@ -6,6 +6,7 @@ library dart2js.abstract_value_domain; import '../constants/values.dart' show ConstantValue, PrimitiveConstantValue; import '../elements/entities.dart'; +import '../serialization/serialization.dart'; import '../universe/selector.dart'; import '../universe/world_builder.dart'; import '../world.dart'; @@ -456,4 +457,10 @@ abstract class AbstractValueDomain { /// Returns compact a textual representation for [value] used for debugging. String getCompactText(AbstractValue value); + + /// Deserializes an [AbstractValue] for this domain from [source]. + AbstractValue readAbstractValueFromDataSource(DataSource source); + + /// Serializes this [value] for this domain to [sink]. + void writeAbstractValueToDataSink(DataSink sink, AbstractValue value); } diff --git a/pkg/compiler/lib/src/types/types.dart b/pkg/compiler/lib/src/types/types.dart index 77d194c9bc690..03ed29e5add0e 100644 --- a/pkg/compiler/lib/src/types/types.dart +++ b/pkg/compiler/lib/src/types/types.dart @@ -12,9 +12,11 @@ import '../compiler.dart' show Compiler; import '../elements/entities.dart'; import '../js_backend/inferred_data.dart'; import '../inferrer/type_graph_inferrer.dart' show TypeGraphInferrer; +import '../serialization/serialization.dart'; import '../universe/selector.dart' show Selector; import '../world.dart' show JClosedWorld; import 'abstract_value_domain.dart'; +import '../inferrer/inferrer_engine.dart'; /// Results about a single element (e.g. a method, parameter, or field) /// produced by the global type-inference algorithm. @@ -27,6 +29,14 @@ import 'abstract_value_domain.dart'; /// guarantees) and the `subclass of Object or null` type mask for the type /// based queries (the runtime value could be anything). abstract class GlobalTypeInferenceMemberResult { + /// Deserializes a [GlobalTypeInferenceMemberResult] object from [source]. + factory GlobalTypeInferenceMemberResult.readFromDataSource( + DataSource source, AbstractValueDomain abstractValueDomain) = + GlobalTypeInferenceMemberResultImpl.readFromDataSource; + + /// Serializes this [GlobalTypeInferenceMemberResult] to [sink]. + void writeToDataSink(DataSink sink, AbstractValueDomain abstractValueDomain); + /// The inferred type when this result belongs to a field, null otherwise. AbstractValue get type; @@ -67,6 +77,18 @@ abstract class GlobalTypeInferenceMemberResult { /// Internal data used during type-inference to store intermediate results about /// a single element. abstract class GlobalTypeInferenceElementData { + /// Deserializes a [GlobalTypeInferenceElementData] object from [source]. + factory GlobalTypeInferenceElementData.readFromDataSource( + DataSource source, AbstractValueDomain abstractValueDomain) = + KernelGlobalTypeInferenceElementData.readFromDataSource; + + /// Serializes this [GlobalTypeInferenceElementData] to [sink]. + void writeToDataSink(DataSink sink, AbstractValueDomain abstractValueDomain); + + /// Compresses the inner representation by removing [AbstractValue] mappings + /// to `null`. + void compress(); + // TODO(johnniwinther): Remove this. Maybe split by access/invoke. AbstractValue typeOfSend(ir.TreeNode node); AbstractValue typeOfGetter(ir.TreeNode node); @@ -98,6 +120,20 @@ abstract class TypesInferrer { /// return was inferred to be a "guaranteed type", that means, it is a type that /// we can prove to be correct for all executions of the program. abstract class GlobalTypeInferenceResults { + /// Deserializes a [GlobalTypeInferenceResults] object from [source]. + factory GlobalTypeInferenceResults.readFromDataSource( + DataSource source, JClosedWorld closedWorld, InferredData inferredData) { + bool isTrivial = source.readBool(); + if (isTrivial) { + return new TrivialGlobalTypeInferenceResults(closedWorld); + } + return new GlobalTypeInferenceResultsImpl.readFromDataSource( + source, closedWorld, inferredData); + } + + /// Serializes this [GlobalTypeInferenceResults] to [sink]. + void writeToDataSink(DataSink sink); + JClosedWorld get closedWorld; InferredData get inferredData; @@ -154,6 +190,10 @@ class GlobalTypeInferenceTask extends CompilerTask { } class GlobalTypeInferenceResultsImpl implements GlobalTypeInferenceResults { + /// Tag used for identifying serialized [GlobalTypeInferenceResults] objects + /// in a debugging data stream. + static const String tag = 'global-type-inference-results'; + final JClosedWorld closedWorld; final InferredData inferredData; final GlobalTypeInferenceMemberResult _deadFieldResult; @@ -162,7 +202,7 @@ class GlobalTypeInferenceResultsImpl implements GlobalTypeInferenceResults { final Map memberResults; final Map parameterResults; - final Set checkedForGrowableLists; + final Set checkedForGrowableLists; final Set returnsListElementTypeSet; GlobalTypeInferenceResultsImpl( @@ -178,6 +218,46 @@ class GlobalTypeInferenceResultsImpl implements GlobalTypeInferenceResults { closedWorld.abstractValueDomain), _trivialParameterResult = closedWorld.abstractValueDomain.dynamicType; + factory GlobalTypeInferenceResultsImpl.readFromDataSource( + DataSource source, JClosedWorld closedWorld, InferredData inferredData) { + source.begin(tag); + Map memberResults = + source.readMemberMap(() => + new GlobalTypeInferenceMemberResult.readFromDataSource( + source, closedWorld.abstractValueDomain)); + Map parameterResults = source.readLocalMap(() => + closedWorld.abstractValueDomain + .readAbstractValueFromDataSource(source)); + Set checkedForGrowableLists = source.readTreeNodes().toSet(); + Set returnsListElementTypeSet = + source.readList(() => new Selector.readFromDataSource(source)).toSet(); + source.end(tag); + return new GlobalTypeInferenceResultsImpl( + closedWorld, + inferredData, + memberResults, + parameterResults, + checkedForGrowableLists, + returnsListElementTypeSet); + } + + void writeToDataSink(DataSink sink) { + sink.writeBool(false); // Is _not_ trivial. + sink.begin(tag); + sink.writeMemberMap( + memberResults, + (GlobalTypeInferenceMemberResult result) => + result.writeToDataSink(sink, closedWorld.abstractValueDomain)); + sink.writeLocalMap( + parameterResults, + (AbstractValue value) => closedWorld.abstractValueDomain + .writeAbstractValueToDataSink(sink, value)); + sink.writeTreeNodes(checkedForGrowableLists); + sink.writeList(returnsListElementTypeSet, + (Selector selector) => selector.writeToDataSink(sink)); + sink.end(tag); + } + @override GlobalTypeInferenceMemberResult resultOfMember(MemberEntity member) { assert( @@ -285,6 +365,10 @@ class GlobalTypeInferenceResultsImpl implements GlobalTypeInferenceResults { class GlobalTypeInferenceMemberResultImpl implements GlobalTypeInferenceMemberResult { + /// Tag used for identifying serialized [GlobalTypeInferenceMemberResult] + /// objects in a debugging data stream. + static const String tag = 'global-type-inference-mebmer-result'; + final GlobalTypeInferenceElementData _data; final Map _allocatedLists; final AbstractValue returnType; @@ -296,6 +380,43 @@ class GlobalTypeInferenceMemberResultImpl this._data, this._allocatedLists, this.returnType, this.type, {this.throwsAlways, this.isCalledOnce}); + factory GlobalTypeInferenceMemberResultImpl.readFromDataSource( + DataSource source, AbstractValueDomain abstractValueDomain) { + source.begin(tag); + GlobalTypeInferenceElementData data = source.readValueOrNull(() { + return new GlobalTypeInferenceElementData.readFromDataSource( + source, abstractValueDomain); + }); + Map allocatedLists = source.readTreeNodeMap( + () => abstractValueDomain.readAbstractValueFromDataSource(source)); + AbstractValue returnType = + abstractValueDomain.readAbstractValueFromDataSource(source); + AbstractValue type = + abstractValueDomain.readAbstractValueFromDataSource(source); + bool throwsAlways = source.readBool(); + bool isCalledOnce = source.readBool(); + source.end(tag); + return new GlobalTypeInferenceMemberResultImpl( + data, allocatedLists, returnType, type, + throwsAlways: throwsAlways, isCalledOnce: isCalledOnce); + } + + void writeToDataSink(DataSink sink, AbstractValueDomain abstractValueDomain) { + sink.begin(tag); + sink.writeValueOrNull(_data, (GlobalTypeInferenceElementData data) { + data.writeToDataSink(sink, abstractValueDomain); + }); + sink.writeTreeNodeMap( + _allocatedLists, + (AbstractValue value) => + abstractValueDomain.writeAbstractValueToDataSink(sink, value)); + abstractValueDomain.writeAbstractValueToDataSink(sink, returnType); + abstractValueDomain.writeAbstractValueToDataSink(sink, type); + sink.writeBool(throwsAlways); + sink.writeBool(isCalledOnce); + sink.end(tag); + } + AbstractValue typeOfSend(ir.Node node) => _data?.typeOfSend(node); AbstractValue typeOfGetter(ir.Node node) => _data?.typeOfGetter(node); AbstractValue typeOfIterator(ir.Node node) => _data?.typeOfIterator(node); @@ -320,6 +441,10 @@ class TrivialGlobalTypeInferenceResults implements GlobalTypeInferenceResults { closedWorld.abstractValueDomain.dynamicType), _trivialParameterResult = closedWorld.abstractValueDomain.dynamicType; + void writeToDataSink(DataSink sink) { + sink.writeBool(true); // Is trivial. + } + @override bool isFixedArrayCheckedForGrowable(ir.Node node) => false; @@ -377,6 +502,11 @@ class TrivialGlobalTypeInferenceMemberResult @override bool get isCalledOnce => false; + + void writeToDataSink(DataSink sink, AbstractValueDomain abstractValueDomain) { + throw new UnsupportedError( + "TrivialGlobalTypeInferenceMemberResult.writeToDataSink"); + } } class DeadFieldGlobalTypeInferenceResult @@ -420,6 +550,11 @@ class DeadFieldGlobalTypeInferenceResult @override bool get isCalledOnce => false; + + void writeToDataSink(DataSink sink, AbstractValueDomain abstractValueDomain) { + throw new UnsupportedError( + "DeadFieldGlobalTypeInferenceResult.writeToDataSink"); + } } class DeadMethodGlobalTypeInferenceResult @@ -463,4 +598,9 @@ class DeadMethodGlobalTypeInferenceResult @override bool get isCalledOnce => false; + + void writeToDataSink(DataSink sink, AbstractValueDomain abstractValueDomain) { + throw new UnsupportedError( + "DeadFieldGlobalTypeInferenceResult.writeToDataSink"); + } } diff --git a/pkg/compiler/lib/src/universe/call_structure.dart b/pkg/compiler/lib/src/universe/call_structure.dart index 0ca8c4d7d3ff2..c574ac8c577cf 100644 --- a/pkg/compiler/lib/src/universe/call_structure.dart +++ b/pkg/compiler/lib/src/universe/call_structure.dart @@ -6,6 +6,7 @@ library dart2js.call_structure; import '../common/names.dart' show Names; import '../elements/entities.dart' show ParameterStructure; +import '../serialization/serialization.dart'; import '../util/util.dart'; import 'selector.dart' show Selector; @@ -14,6 +15,10 @@ import 'selector.dart' show Selector; // TODO(johnniwinther): Should isGetter/isSetter be part of the call structure // instead of the selector? class CallStructure { + /// Tag used for identifying serialized [CallStructure] objects in a debugging + /// data stream. + static const String tag = 'call-structure'; + static const CallStructure NO_ARGS = const CallStructure.unnamed(0); static const CallStructure ONE_ARG = const CallStructure.unnamed(1); static const CallStructure TWO_ARGS = const CallStructure.unnamed(2); @@ -43,6 +48,25 @@ class CallStructure { argumentCount, namedArguments, typeArgumentCount); } + /// Deserializes a [CallStructure] object from [source]. + factory CallStructure.readFromDataSource(DataSource source) { + source.begin(tag); + int argumentCount = source.readInt(); + List namedArguments = source.readStrings(); + int typeArgumentCount = source.readInt(); + source.end(tag); + return new CallStructure(argumentCount, namedArguments, typeArgumentCount); + } + + /// Serializes this [CallStructure] to [sink]. + void writeToDataSink(DataSink sink) { + sink.begin(tag); + sink.writeInt(argumentCount); + sink.writeStrings(namedArguments); + sink.writeInt(typeArgumentCount); + sink.end(tag); + } + CallStructure withTypeArgumentCount(int typeArgumentCount) => new CallStructure(argumentCount, namedArguments, typeArgumentCount); diff --git a/pkg/compiler/lib/src/universe/class_hierarchy.dart b/pkg/compiler/lib/src/universe/class_hierarchy.dart index c351f1ecf984d..7032550530a0a 100644 --- a/pkg/compiler/lib/src/universe/class_hierarchy.dart +++ b/pkg/compiler/lib/src/universe/class_hierarchy.dart @@ -6,11 +6,20 @@ import '../common.dart'; import '../common_elements.dart'; import '../elements/entities.dart'; import '../elements/types.dart' show InterfaceType; +import '../serialization/serialization.dart'; import 'class_set.dart'; // TODO(johnniwinther): Move more methods from `JClosedWorld` to // `ClassHierarchy`. abstract class ClassHierarchy { + /// Deserializes a [ClassHierarchy] object from [source]. + factory ClassHierarchy.readFromDataSource( + DataSource source, CommonElements commonElements) = + ClassHierarchyImpl.readFromDataSource; + + /// Serializes this [ClassHierarchy] to [sink]. + void writeToDataSink(DataSink sink); + /// Returns `true` if [cls] is either directly or indirectly instantiated. bool isInstantiated(ClassEntity cls); @@ -141,6 +150,10 @@ abstract class ClassHierarchy { } class ClassHierarchyImpl implements ClassHierarchy { + /// Tag used for identifying serialized [ClassHierarchy] objects in a debugging + /// data stream. + static const String tag = 'class-hierarchy'; + final CommonElements _commonElements; final Map _classHierarchyNodes; final Map _classSets; @@ -148,6 +161,44 @@ class ClassHierarchyImpl implements ClassHierarchy { ClassHierarchyImpl( this._commonElements, this._classHierarchyNodes, this._classSets); + factory ClassHierarchyImpl.readFromDataSource( + DataSource source, CommonElements commonElements) { + source.begin(tag); + Map classHierarchyNodes = + new ClassHierarchyNodesMap(); + int classCount = source.readInt(); + for (int i = 0; i < classCount; i++) { + ClassHierarchyNode node = new ClassHierarchyNode.readFromDataSource( + source, classHierarchyNodes); + classHierarchyNodes[node.cls] = node; + } + Map classSets = {}; + for (int i = 0; i < classCount; i++) { + ClassSet classSet = + new ClassSet.readFromDataSource(source, classHierarchyNodes); + classSets[classSet.cls] = classSet; + } + + source.end(tag); + return new ClassHierarchyImpl( + commonElements, classHierarchyNodes, classSets); + } + + void writeToDataSink(DataSink sink) { + sink.begin(tag); + sink.writeInt(_classSets.length); + ClassHierarchyNode node = + getClassHierarchyNode(_commonElements.objectClass); + node.forEachSubclass((ClassEntity cls) { + getClassHierarchyNode(cls).writeToDataSink(sink); + }, ClassHierarchyNode.ALL); + ClassSet set = getClassSet(_commonElements.objectClass); + set.forEachSubclass((ClassEntity cls) { + getClassSet(cls).writeToDataSink(sink); + }, ClassHierarchyNode.ALL); + sink.end(tag); + } + @override bool isInstantiated(ClassEntity cls) { ClassHierarchyNode node = _classHierarchyNodes[cls]; diff --git a/pkg/compiler/lib/src/universe/class_set.dart b/pkg/compiler/lib/src/universe/class_set.dart index 04a9733f34d19..6431ab6137cdb 100644 --- a/pkg/compiler/lib/src/universe/class_set.dart +++ b/pkg/compiler/lib/src/universe/class_set.dart @@ -10,6 +10,7 @@ import 'package:front_end/src/api_unstable/dart2js.dart' show Link; import '../elements/entities.dart' show ClassEntity; import '../elements/indexed.dart' show IndexedClass; +import '../serialization/serialization.dart'; import '../util/enumset.dart' show EnumSet; /// Enum for the different kinds of instantiation of a class. @@ -46,6 +47,10 @@ enum Instantiation { /// E /// class ClassHierarchyNode { + /// Tag used for identifying serialized [ClassHierarchyNode] objects in a + /// debugging data stream. + static const String tag = 'class-hierarchy-node'; + /// Enum set for selecting instantiated classes in /// [ClassHierarchyNode.subclassesByMask], /// [ClassHierarchyNode.subclassesByMask] and [ClassSet.subtypesByMask]. @@ -209,6 +214,38 @@ class ClassHierarchyNode { } } + /// Deserializes a [ClassHierarchyNode] object from [source]. + factory ClassHierarchyNode.readFromDataSource( + DataSource source, Map nodeMap) { + source.begin(tag); + IndexedClass cls = source.readClass(); + ClassHierarchyNode parentNode; + IndexedClass superclass = source.readClassOrNull(); + if (superclass != null) { + parentNode = nodeMap[superclass]; + assert(parentNode != null, + "No ClassHierarchyNode for superclass $superclass."); + } + int maskValue = source.readInt(); + int hierarchyDepth = source.readInt(); + int instantiatedSubclassCount = source.readInt(); + source.end(tag); + return new ClassHierarchyNode(parentNode, cls, hierarchyDepth) + .._instantiatedSubclassCount = instantiatedSubclassCount + .._mask.value = maskValue; + } + + /// Serializes this [ClassHierarchyNode] to [sink]. + void writeToDataSink(DataSink sink) { + sink.begin(tag); + sink.writeClass(cls); + sink.writeClassOrNull(parentNode?.cls); + sink.writeInt(_mask.value); + sink.writeInt(hierarchyDepth); + sink.writeInt(_instantiatedSubclassCount); + sink.end(tag); + } + /// Adds [subclass] as a direct subclass of [cls]. void addDirectSubclass(ClassHierarchyNode subclass) { assert(!_directSubclasses.contains(subclass)); @@ -463,6 +500,10 @@ class ClassHierarchyNode { /// B E /// class ClassSet { + /// Tag used for identifying serialized [ClassSet] objects in a debugging + /// data stream. + static const String tag = 'class-set'; + final ClassHierarchyNode node; ClassEntity _leastUpperInstantiatedSubtype; @@ -502,6 +543,36 @@ class ClassSet { ClassSet(this.node); + /// Deserializes a [ClassSet] object from [source]. + factory ClassSet.readFromDataSource( + DataSource source, Map nodeMap) { + source.begin(tag); + ClassHierarchyNode node = nodeMap[source.readClass()]; + List subtypes = source.readList(() { + return nodeMap[source.readClass()]; + }, emptyAsNull: true); + List mixinApplications = source.readList(() { + return nodeMap[source.readClass()]; + }, emptyAsNull: true); + source.end(tag); + return new ClassSet(node) + .._subtypes = subtypes + .._mixinApplications = mixinApplications; + } + + /// Serializes this [ClassSet] to [sink]. + void writeToDataSink(DataSink sink) { + sink.begin(tag); + sink.writeClass(node.cls); + sink.writeList(_subtypes, (ClassHierarchyNode node) { + sink.writeClass(node.cls); + }, allowNull: true); + sink.writeList(_mixinApplications, (ClassHierarchyNode node) { + sink.writeClass(node.cls); + }, allowNull: true); + sink.end(tag); + } + ClassEntity get cls => node.cls; /// Returns `true` if `other.cls` is a subclass of [cls]. diff --git a/pkg/compiler/lib/src/universe/selector.dart b/pkg/compiler/lib/src/universe/selector.dart index 34aaf064949de..acc7100cd735a 100644 --- a/pkg/compiler/lib/src/universe/selector.dart +++ b/pkg/compiler/lib/src/universe/selector.dart @@ -10,6 +10,7 @@ import '../elements/entities.dart'; import '../elements/entity_utils.dart' as utils; import '../elements/names.dart'; import '../elements/operators.dart'; +import '../serialization/serialization.dart'; import '../util/util.dart' show Hashing; import 'call_structure.dart' show CallStructure; @@ -40,6 +41,10 @@ class SelectorKind { } class Selector { + /// Tag used for identifying serialized [Selector] objects in a debugging + /// data stream. + static const String tag = 'selector'; + final SelectorKind kind; final Name memberName; final CallStructure callStructure; @@ -192,6 +197,30 @@ class Selector { Names.genericInstantiation, new CallStructure(0, null, typeArguments)); + /// Deserializes a [Selector] object from [source]. + factory Selector.readFromDataSource(DataSource source) { + source.begin(tag); + SelectorKind kind = source.readEnum(SelectorKind.values); + bool isSetter = source.readBool(); + LibraryEntity library = source.readLibraryOrNull(); + String text = source.readString(); + CallStructure callStructure = new CallStructure.readFromDataSource(source); + source.end(tag); + return new Selector( + kind, new Name(text, library, isSetter: isSetter), callStructure); + } + + /// Serializes this [Selector] to [sink]. + void writeToDataSink(DataSink sink) { + sink.begin(tag); + sink.writeEnum(kind); + sink.writeBool(memberName.isSetter); + sink.writeLibraryOrNull(memberName.library); + sink.writeString(memberName.text); + callStructure.writeToDataSink(sink); + sink.end(tag); + } + bool get isGetter => kind == SelectorKind.GETTER; bool get isSetter => kind == SelectorKind.SETTER; bool get isCall => kind == SelectorKind.CALL; diff --git a/pkg/compiler/lib/src/universe/side_effects.dart b/pkg/compiler/lib/src/universe/side_effects.dart index 81d513f4a25df..aac589a9c4d9e 100644 --- a/pkg/compiler/lib/src/universe/side_effects.dart +++ b/pkg/compiler/lib/src/universe/side_effects.dart @@ -5,8 +5,13 @@ library universe.side_effects; import '../elements/entities.dart'; +import '../serialization/serialization.dart'; class SideEffects { + /// Tag used for identifying serialized [SideEffects] objects in a debugging + /// data stream. + static const String tag = 'side-effects'; + // Changes flags. static const int FLAG_CHANGES_INDEX = 0; static const int FLAG_CHANGES_INSTANCE_PROPERTY = FLAG_CHANGES_INDEX + 1; @@ -37,6 +42,21 @@ class SideEffects { SideEffects.fromFlags(this._flags); + /// Deserializes a [SideEffects] object from [source]. + factory SideEffects.readFromDataSource(DataSource source) { + source.begin(tag); + int flags = source.readInt(); + source.end(tag); + return new SideEffects.fromFlags(flags); + } + + /// Serializes this [SideEffects] to [sink]. + void writeToDataSink(DataSink sink) { + sink.begin(tag); + sink.writeInt(_flags); + sink.end(tag); + } + bool operator ==(other) => _flags == other._flags; int get hashCode => throw new UnsupportedError('SideEffects.hashCode'); diff --git a/pkg/compiler/lib/src/util/enumset.dart b/pkg/compiler/lib/src/util/enumset.dart index 31fef3c1ba00b..97df1a4cb1e04 100644 --- a/pkg/compiler/lib/src/util/enumset.dart +++ b/pkg/compiler/lib/src/util/enumset.dart @@ -32,6 +32,10 @@ abstract class EnumSet { /// The bit mask of the shifted indices for the enum values in this set. int get value; + /// Sets the enum values in this set through a bit mask of the shifted enum + /// value indices. + void set value(int mask); + /// Adds [enumValue] to this set. void add(E enumValue); @@ -127,6 +131,11 @@ class _EnumSet extends EnumSet { @override int get value => _value; + @override + void set value(int mask) { + _value = mask; + } + @override void add(E enumValue) { _value |= 1 << (enumValue as dynamic).index; @@ -173,6 +182,10 @@ class _ConstEnumSet extends EnumSet { return new _ConstEnumSet(value); } + void set value(int mask) { + throw new UnsupportedError('EnumSet.value='); + } + @override void add(E enumValue) { throw new UnsupportedError('EnumSet.add'); diff --git a/pkg/compiler/lib/src/world.dart b/pkg/compiler/lib/src/world.dart index e174ebcf529d1..cb83743e492af 100644 --- a/pkg/compiler/lib/src/world.dart +++ b/pkg/compiler/lib/src/world.dart @@ -32,6 +32,7 @@ import 'js_backend/runtime_types.dart' import 'js_model/locals.dart'; import 'ordered_typeset.dart'; import 'options.dart'; +import 'js_emitter/sorter.dart'; import 'types/abstract_value_domain.dart'; import 'universe/class_hierarchy.dart'; import 'universe/class_set.dart'; @@ -88,6 +89,9 @@ abstract class JClosedWorld implements World { OutputUnitData get outputUnitData; + /// The [Sorter] used for sorting elements in the generated code. + Sorter get sorter; + /// Returns `true` if [cls] is implemented by an instantiated class. bool isImplemented(ClassEntity cls); @@ -258,7 +262,7 @@ abstract class ClosedWorldBase implements JClosedWorld { final JCommonElements commonElements; // TODO(johnniwinther): Can this be derived from [ClassSet]s? - final Set _implementedClasses; + final Set implementedClasses; final Iterable liveInstanceMembers; @@ -280,15 +284,14 @@ abstract class ClosedWorldBase implements JClosedWorld { this.interceptorData, this.backendUsage, this.noSuchMethodData, - Set implementedClasses, + this.implementedClasses, this.liveNativeClasses, this.liveInstanceMembers, this.assignedInstanceMembers, this.processedMembers, this.mixinUses, this.typesImplementedBySubclasses, - this.classHierarchy) - : this._implementedClasses = implementedClasses; + this.classHierarchy); OrderedTypeSet getOrderedTypeSet(covariant ClassEntity cls); @@ -304,7 +307,7 @@ abstract class ClosedWorldBase implements JClosedWorld { /// Returns `true` if [cls] is implemented by an instantiated class. bool isImplemented(ClassEntity cls) { - return _implementedClasses.contains(cls); + return implementedClasses.contains(cls); } @override diff --git a/tests/compiler/dart2js/analyses/dart2js_allowed.json b/tests/compiler/dart2js/analyses/dart2js_allowed.json index 12771288dcfe1..c8f89bc6b0d93 100644 --- a/tests/compiler/dart2js/analyses/dart2js_allowed.json +++ b/tests/compiler/dart2js/analyses/dart2js_allowed.json @@ -217,6 +217,12 @@ "Dynamic access of 'isString'.": 1, "Dynamic access of 'stringValue'.": 1 }, + "pkg/compiler/lib/src/serialization/binary_sink.dart": { + "Dynamic access of 'index'.": 1 + }, + "pkg/compiler/lib/src/serialization/helpers.dart": { + "Dynamic access of 'value'.": 1 + }, "pkg/compiler/lib/src/universe/use.dart": { "Dynamic access of 'selector'.": 1, "Dynamic access of 'receiverConstraint'.": 1, @@ -298,6 +304,12 @@ "Dynamic access of 'usedBy'.": 2, "Dynamic access of 'inputs'.": 1 }, + "pkg/compiler/lib/src/inferrer/inferrer_engine.dart": { + "Dynamic access of 'isVoid'.": 1, + "Dynamic access of 'isDynamic'.": 1, + "Dynamic access of 'isInterfaceType'.": 1, + "Dynamic access of 'element'.": 1 + }, "pkg/compiler/lib/src/universe/function_set.dart": { "Dynamic access of 'selector'.": 1, "Dynamic access of 'receiver'.": 1 @@ -365,12 +377,6 @@ "Dynamic invocation of '[]'.": 9, "Dynamic invocation of 'toStatement'.": 3 }, - "pkg/compiler/lib/src/inferrer/inferrer_engine.dart": { - "Dynamic access of 'isVoid'.": 1, - "Dynamic access of 'isDynamic'.": 1, - "Dynamic access of 'isInterfaceType'.": 1, - "Dynamic access of 'element'.": 1 - }, "pkg/compiler/lib/src/inferrer/type_graph_nodes.dart": { "Dynamic invocation of 'add'.": 1, "Dynamic invocation of 'remove'.": 1, @@ -383,6 +389,16 @@ "Dynamic access of 'startPosition'.": 1, "Dynamic access of 'innerPosition'.": 1 }, + "pkg/compiler/lib/src/inferrer/locals_handler.dart": { + "Dynamic access of 'isEmpty'.": 1, + "Dynamic access of 'positional'.": 2, + "Dynamic access of 'length'.": 2, + "Dynamic access of 'named'.": 2, + "Dynamic invocation of '[]'.": 2 + }, + "pkg/compiler/lib/src/inferrer/type_graph_dump.dart": { + "Dynamic invocation of 'contains'.": 2 + }, "pkg/compiler/lib/src/ssa/variable_allocator.dart": { "Dynamic access of 'checkedInput'.": 1, "Dynamic access of 'usedBy'.": 1, @@ -412,16 +428,6 @@ "pkg/compiler/lib/src/ssa/value_set.dart": { "Dynamic invocation of 'add'.": 2 }, - "pkg/compiler/lib/src/inferrer/locals_handler.dart": { - "Dynamic access of 'isEmpty'.": 1, - "Dynamic access of 'positional'.": 2, - "Dynamic access of 'length'.": 2, - "Dynamic access of 'named'.": 2, - "Dynamic invocation of '[]'.": 2 - }, - "pkg/compiler/lib/src/inferrer/type_graph_dump.dart": { - "Dynamic invocation of 'contains'.": 2 - }, "pkg/compiler/lib/src/js_emitter/full_emitter/emitter.dart": { "Dynamic access of 'scheme'.": 1, "Dynamic invocation of 'finalizeTokens'.": 1 diff --git a/tests/compiler/dart2js/model/in_memory_split_test.dart b/tests/compiler/dart2js/model/in_memory_split_test.dart index 8c1a080072942..ab13b9052cf5d 100644 --- a/tests/compiler/dart2js/model/in_memory_split_test.dart +++ b/tests/compiler/dart2js/model/in_memory_split_test.dart @@ -2,41 +2,95 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +import 'package:args/args.dart'; import 'package:async_helper/async_helper.dart'; import 'package:compiler/src/compiler.dart'; +import 'package:compiler/src/filenames.dart'; import 'package:compiler/src/js_backend/inferred_data.dart'; +import 'package:compiler/src/js_model/js_strategy.dart'; +import 'package:compiler/src/serialization/serialization.dart'; import 'package:compiler/src/types/types.dart'; -import 'package:compiler/src/world.dart'; import 'package:expect/expect.dart'; +import 'package:front_end/src/fasta/kernel/utils.dart' as ir + show serializeComponent; +import 'package:front_end/src/fasta/kernel/utils.dart'; +import 'package:kernel/ast.dart' as ir; +import 'package:kernel/binary/ast_from_binary.dart' show BinaryBuilder; import '../helpers/memory_compiler.dart'; -String code = ''' -main() {} -'''; -main() { +main(List args) { + ArgParser argParser = new ArgParser(allowTrailingOptions: true); + argParser.addFlag('debug', abbr: 'd', defaultsTo: false); + argParser.addFlag('object', abbr: 'o', defaultsTo: false); + argParser.addFlag('kinds', abbr: 'k', defaultsTo: false); + ArgResults argResults = argParser.parse(args); + + bool useObjectSink = argResults['object'] || argResults['debug']; + bool useDataKinds = argResults['kinds'] || argResults['debug']; + asyncTest(() async { + Uri entryPoint; + if (argResults.rest.isEmpty) { + entryPoint = Uri.base.resolve('samples-dev/swarm/swarm.dart'); + } else { + entryPoint = Uri.base.resolve(nativeToUriPath(argResults.rest.last)); + } + CompilationResult result = await runCompiler( - memorySourceFiles: {'main.dart': code}, + entryPoint: entryPoint, beforeRun: (Compiler compiler) { compiler.stopAfterTypeInference = true; }); Expect.isTrue(result.isSuccess); Compiler compiler = result.compiler; - GlobalTypeInferenceResults globalInferenceResults = - cloneInferenceResults(compiler.globalInference.resultsForTesting); + GlobalTypeInferenceResults globalInferenceResults = cloneInferenceResults( + compiler, compiler.globalInference.resultsForTesting, + useObjectSink: useObjectSink, useDataKinds: useDataKinds); compiler.generateJavaScriptCode(globalInferenceResults); }); } GlobalTypeInferenceResults cloneInferenceResults( - GlobalTypeInferenceResultsImpl result) { - JClosedWorld closedWorld = result.closedWorld; - InferredData inferredData = result.inferredData; - return new GlobalTypeInferenceResultsImpl( - closedWorld, - inferredData, - result.memberResults, - result.parameterResults, - result.checkedForGrowableLists, - result.returnsListElementTypeSet); + Compiler compiler, GlobalTypeInferenceResults results, + {bool useObjectSink: false, bool useDataKinds}) { + JsClosedWorld closedWorld = results.closedWorld; + InferredData inferredData = results.inferredData; + ir.Component component = closedWorld.elementMap.programEnv.mainComponent; + List irData = ir.serializeComponent(component); + + Function() getData; + + DataSink sink; + if (useObjectSink) { + List data = []; + sink = new ObjectSink(data, useDataKinds: useDataKinds); + getData = () => data; + } else { + ByteSink byteSink = new ByteSink(); + sink = new BinarySink(byteSink, useDataKinds: useDataKinds); + getData = () => byteSink.builder.takeBytes(); + } + closedWorld.writeToDataSink(sink); + inferredData.writeToDataSink(sink); + results.writeToDataSink(sink); + sink.close(); + var worldData = getData(); + print('data size: ${worldData.length}'); + + ir.Component newComponent = new ir.Component(); + new BinaryBuilder(irData).readComponent(newComponent); + DataSource source = useObjectSink + ? new ObjectSource(worldData, useDataKinds: useDataKinds) + : new BinarySourceImpl(worldData, useDataKinds: useDataKinds); + closedWorld = new JsClosedWorld.readFromDataSource( + compiler.options, + compiler.reporter, + compiler.environment, + compiler.abstractValueStrategy, + newComponent, + source); + inferredData = new InferredData.readFromDataSource(source, closedWorld); + results = new GlobalTypeInferenceResults.readFromDataSource( + source, closedWorld, inferredData); + return results; }