| // 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:async'; | |
| import 'dart:typed_data'; | |
| import 'package:kernel/ast.dart' as ir; | |
| import 'package:kernel/binary/ast_from_binary.dart' as ir; | |
| import 'package:kernel/binary/ast_to_binary.dart' as ir; | |
| import '../../compiler_api.dart' as api; | |
| import '../common/codegen.dart'; | |
| import '../common/tasks.dart'; | |
| import '../deferred_load/output_unit.dart'; | |
| import '../diagnostics/diagnostic_listener.dart'; | |
| import '../dump_info.dart'; | |
| import '../elements/entities.dart'; | |
| import '../environment.dart'; | |
| import '../inferrer/abstract_value_domain.dart'; | |
| import '../inferrer/abstract_value_strategy.dart'; | |
| import '../inferrer/types.dart'; | |
| import '../io/source_information.dart'; | |
| import '../js_backend/codegen_inputs.dart'; | |
| import '../js_backend/inferred_data.dart'; | |
| import '../js_model/js_strategy.dart'; | |
| import '../js_model/js_world.dart'; | |
| import '../js_model/locals.dart'; | |
| import '../options.dart'; | |
| import 'deferrable.dart'; | |
| import 'serialization.dart'; | |
| class _StringInterner implements ir.StringInterner, StringInterner { | |
| final Map<String, String> _map = {}; | |
| @override | |
| String internString(String string) { | |
| return _map[string] ??= string; | |
| } | |
| } | |
| class SerializationTask extends CompilerTask { | |
| final CompilerOptions _options; | |
| final DiagnosticReporter _reporter; | |
| final api.CompilerInput _provider; | |
| final api.CompilerOutput _outputProvider; | |
| final _stringInterner = _StringInterner(); | |
| final ValueInterner? _valueInterner; | |
| SerializationTask( | |
| this._options, | |
| this._reporter, | |
| this._provider, | |
| this._outputProvider, | |
| Measurer measurer, | |
| ) : _valueInterner = _options.features.internValues.isEnabled | |
| ? ValueInterner() | |
| : null, | |
| super(measurer); | |
| @override | |
| String get name => 'Serialization'; | |
| void serializeComponent( | |
| ir.Component component, { | |
| bool includeSourceBytes = true, | |
| }) { | |
| measureSubtask('serialize dill', () { | |
| _reporter.log('Writing dill to ${_options.outputUri}'); | |
| api.BinaryOutputSink dillOutput = _outputProvider.createBinarySink( | |
| _options.outputUri!, | |
| ); | |
| ir.BinaryPrinter printer = ir.BinaryPrinter( | |
| dillOutput, | |
| includeSourceBytes: includeSourceBytes, | |
| ); | |
| printer.writeComponentFile(component); | |
| dillOutput.close(); | |
| }); | |
| } | |
| Future<ir.Component> deserializeComponent() async { | |
| return measureIoSubtask('deserialize dill', () async { | |
| _reporter.log('Reading dill from ${_options.inputDillUri}'); | |
| final dillInput = await _provider.readFromUri( | |
| _options.inputDillUri, | |
| inputKind: api.InputKind.binary, | |
| ); | |
| ir.Component component = ir.Component(); | |
| // Not using growable lists saves memory. | |
| ir.BinaryBuilder( | |
| dillInput.data, | |
| useGrowableLists: false, | |
| stringInterner: _stringInterner, | |
| ).readComponent(component); | |
| return component; | |
| }); | |
| } | |
| Future<ir.Component> deserializeComponentAndUpdateOptions() async { | |
| ir.Component component = await deserializeComponent(); | |
| return component; | |
| } | |
| void serializeClosedWorld( | |
| JClosedWorld closedWorld, | |
| SerializationIndices indices, | |
| ) { | |
| measureSubtask('serialize closed world', () { | |
| final outputUri = _options.dataUriForStage(CompilerStage.closedWorld); | |
| _reporter.log('Writing closed world to $outputUri'); | |
| api.BinaryOutputSink dataOutput = _outputProvider.createBinarySink( | |
| outputUri, | |
| ); | |
| DataSinkWriter sink = DataSinkWriter( | |
| BinaryDataSink(dataOutput), | |
| _options, | |
| indices, | |
| ); | |
| serializeClosedWorldToSink(closedWorld, sink); | |
| }); | |
| } | |
| Future<JClosedWorld> deserializeClosedWorld( | |
| AbstractValueStrategy abstractValueStrategy, | |
| ir.Component component, | |
| bool useDeferredSourceReads, | |
| SerializationIndices indices, | |
| ) async { | |
| return await measureIoSubtask('deserialize closed world', () async { | |
| final uri = _options.dataUriForStage(CompilerStage.closedWorld); | |
| _reporter.log('Reading data from $uri'); | |
| api.Input<Uint8List> dataInput = await _provider.readFromUri( | |
| uri, | |
| inputKind: api.InputKind.binary, | |
| ); | |
| DataSourceReader source = DataSourceReader( | |
| BinaryDataSource(dataInput.data, stringInterner: _stringInterner), | |
| _options, | |
| indices, | |
| interner: _valueInterner, | |
| useDeferredStrategy: useDeferredSourceReads, | |
| ); | |
| var closedWorld = deserializeClosedWorldFromSource( | |
| _options, | |
| _reporter, | |
| abstractValueStrategy, | |
| component, | |
| source, | |
| ); | |
| return closedWorld; | |
| }); | |
| } | |
| void serializeGlobalTypeInference( | |
| GlobalTypeInferenceResults results, | |
| SerializationIndices indices, | |
| ) { | |
| measureSubtask('serialize data', () { | |
| final outputUri = _options.dataUriForStage(CompilerStage.globalInference); | |
| _reporter.log('Writing data to $outputUri'); | |
| api.BinaryOutputSink dataOutput = _outputProvider.createBinarySink( | |
| outputUri, | |
| ); | |
| DataSinkWriter sink = DataSinkWriter( | |
| BinaryDataSink(dataOutput), | |
| _options, | |
| indices, | |
| ); | |
| serializeGlobalTypeInferenceResultsToSink(results, sink); | |
| }); | |
| } | |
| Future<GlobalTypeInferenceResults> deserializeGlobalTypeInferenceResults( | |
| Environment environment, | |
| AbstractValueStrategy abstractValueStrategy, | |
| ir.Component component, | |
| JClosedWorld closedWorld, | |
| bool useDeferredSourceReads, | |
| SerializationIndices indices, | |
| ) async { | |
| return await measureIoSubtask('deserialize data', () async { | |
| final uri = _options.dataUriForStage(CompilerStage.globalInference); | |
| _reporter.log('Reading data from $uri'); | |
| api.Input<Uint8List> dataInput = await _provider.readFromUri( | |
| uri, | |
| inputKind: api.InputKind.binary, | |
| ); | |
| DataSourceReader source = DataSourceReader( | |
| BinaryDataSource(dataInput.data, stringInterner: _stringInterner), | |
| _options, | |
| indices, | |
| interner: _valueInterner, | |
| useDeferredStrategy: useDeferredSourceReads, | |
| ); | |
| return deserializeGlobalTypeInferenceResultsFromSource( | |
| _options, | |
| _reporter, | |
| environment, | |
| abstractValueStrategy, | |
| component, | |
| closedWorld, | |
| source, | |
| ); | |
| }); | |
| } | |
| void serializeCodegen( | |
| JsBackendStrategy backendStrategy, | |
| AbstractValueDomain domain, | |
| CodegenResults codegenResults, | |
| SerializationIndices indices, | |
| ) { | |
| int shard = _options.codegenShard!; | |
| int shards = _options.codegenShards!; | |
| Map<MemberEntity, CodegenResult> results = {}; | |
| int index = 0; | |
| final lazyMemberBodies = backendStrategy.forEachCodegenMember(( | |
| MemberEntity member, | |
| ) { | |
| if (index % shards == shard) { | |
| final (result: codegenResult, isGenerated: _) = codegenResults | |
| .getCodegenResults(member); | |
| results[member] = codegenResult; | |
| } | |
| index++; | |
| }); | |
| measureSubtask('serialize codegen', () { | |
| final outputUri = _options.dataUriForStage(CompilerStage.codegenSharded); | |
| Uri uri = Uri.parse('$outputUri$shard'); | |
| api.BinaryOutputSink dataOutput = _outputProvider.createBinarySink(uri); | |
| DataSinkWriter sink = DataSinkWriter( | |
| BinaryDataSink(dataOutput), | |
| _options, | |
| indices, | |
| ); | |
| _reporter.log('Writing data to $uri'); | |
| sink.writeMembers(lazyMemberBodies); | |
| sink.registerAbstractValueDomain(domain); | |
| sink.writeMemberMap(results, (MemberEntity member, CodegenResult result) { | |
| sink.writeDeferrable(() => result.writeToDataSink(sink)); | |
| }); | |
| sink.close(); | |
| }); | |
| } | |
| Future<CodegenResults> deserializeCodegen( | |
| JsBackendStrategy backendStrategy, | |
| JClosedWorld closedWorld, | |
| CodegenInputs codegenInputs, | |
| bool useDeferredSourceReads, | |
| SourceLookup sourceLookup, | |
| SerializationIndices indices, | |
| ) async { | |
| int shards = _options.codegenShards!; | |
| Map<MemberEntity, Deferrable<CodegenResult>> results = {}; | |
| for (int shard = 0; shard < shards; shard++) { | |
| Uri uri = Uri.parse( | |
| '${_options.dataUriForStage(CompilerStage.codegenSharded)}$shard', | |
| ); | |
| await measureIoSubtask('deserialize codegen', () async { | |
| _reporter.log('Reading data from $uri'); | |
| api.Input<Uint8List> dataInput = await _provider.readFromUri( | |
| uri, | |
| inputKind: api.InputKind.binary, | |
| ); | |
| // TODO(36983): This code is extracted because there appeared to be a | |
| // memory leak for large buffer held by `source`. | |
| _deserializeCodegenInput( | |
| backendStrategy, | |
| closedWorld, | |
| uri, | |
| dataInput, | |
| results, | |
| useDeferredSourceReads, | |
| sourceLookup, | |
| indices, | |
| ); | |
| dataInput.release(); | |
| }); | |
| } | |
| return DeserializedCodegenResults( | |
| codegenInputs, | |
| DeferrableValueMap(results), | |
| backendStrategy.functionCompiler, | |
| ); | |
| } | |
| void _deserializeCodegenInput( | |
| JsBackendStrategy backendStrategy, | |
| JClosedWorld closedWorld, | |
| Uri uri, | |
| api.Input<Uint8List> dataInput, | |
| Map<MemberEntity, Deferrable<CodegenResult>> results, | |
| bool useDeferredSourceReads, | |
| SourceLookup sourceLookup, | |
| SerializationIndices indices, | |
| ) { | |
| DataSourceReader source = DataSourceReader( | |
| BinaryDataSource(dataInput.data, stringInterner: _stringInterner), | |
| _options, | |
| indices, | |
| interner: _valueInterner, | |
| useDeferredStrategy: useDeferredSourceReads, | |
| ); | |
| backendStrategy.prepareCodegenReader(source); | |
| source.registerSourceLookup(sourceLookup); | |
| final lazyMemberBodies = source.readMembers(); | |
| closedWorld.elementMap.registerLazyMemberBodies(lazyMemberBodies); | |
| source.registerAbstractValueDomain(closedWorld.abstractValueDomain); | |
| Map<MemberEntity, Deferrable<CodegenResult>> codegenResults = source | |
| .readMemberMap((MemberEntity member) { | |
| return source.readDeferrable( | |
| CodegenResult.readFromDataSource, | |
| cacheData: false, | |
| ); | |
| }); | |
| _reporter.log('Read ${codegenResults.length} members from $uri'); | |
| results.addAll(codegenResults); | |
| } | |
| DataSinkWriter dataSinkWriterForDumpInfo( | |
| AbstractValueDomain abstractValueDomain, | |
| SerializationIndices indices, | |
| ) { | |
| final outputUri = _options.dataUriForStage(CompilerStage.dumpInfo); | |
| api.BinaryOutputSink dataOutput = _outputProvider.createBinarySink( | |
| outputUri, | |
| ); | |
| final sink = DataSinkWriter(BinaryDataSink(dataOutput), _options, indices); | |
| sink.registerAbstractValueDomain(abstractValueDomain); | |
| return sink; | |
| } | |
| void serializeDumpInfoProgramData( | |
| DataSinkWriter sink, | |
| JsBackendStrategy backendStrategy, | |
| DumpInfoProgramData dumpInfoProgramData, | |
| DumpInfoJsAstRegistry dumpInfoRegistry, | |
| ) { | |
| dumpInfoProgramData.writeToDataSink(sink, dumpInfoRegistry); | |
| sink.close(); | |
| } | |
| Future<DumpInfoProgramData> deserializeDumpInfoProgramData( | |
| JsBackendStrategy backendStrategy, | |
| AbstractValueDomain abstractValueDomain, | |
| OutputUnitData outputUnitData, | |
| SerializationIndices indices, | |
| ) async { | |
| final inputUri = _options.dataUriForStage(CompilerStage.dumpInfo); | |
| final dataInput = await _provider.readFromUri( | |
| inputUri, | |
| inputKind: api.InputKind.binary, | |
| ); | |
| final source = DataSourceReader( | |
| BinaryDataSource(dataInput.data, stringInterner: _stringInterner), | |
| _options, | |
| indices, | |
| // This must use a deferred strategy so that we can delay reading the | |
| // registered impacts until we are able to read the count of them. | |
| useDeferredStrategy: true, | |
| ); | |
| backendStrategy.prepareCodegenReader(source); | |
| source.registerAbstractValueDomain(abstractValueDomain); | |
| return DumpInfoProgramData.readFromDataSource( | |
| source, | |
| includeCodeText: _options.dumpInfoFormat != DumpInfoFormat.binary, | |
| ); | |
| } | |
| } | |
| void serializeGlobalTypeInferenceResultsToSink( | |
| GlobalTypeInferenceResults results, | |
| DataSinkWriter sink, | |
| ) { | |
| final closedWorld = results.closedWorld; | |
| GlobalLocalsMap globalLocalsMap = results.globalLocalsMap; | |
| InferredData inferredData = results.inferredData; | |
| globalLocalsMap.writeToDataSink(sink); | |
| inferredData.writeToDataSink(sink); | |
| results.writeToDataSink(sink, closedWorld.elementMap); | |
| sink.close(); | |
| } | |
| GlobalTypeInferenceResults deserializeGlobalTypeInferenceResultsFromSource( | |
| CompilerOptions options, | |
| DiagnosticReporter reporter, | |
| Environment environment, | |
| AbstractValueStrategy abstractValueStrategy, | |
| ir.Component component, | |
| JClosedWorld closedWorld, | |
| DataSourceReader source, | |
| ) { | |
| source.registerComponentLookup(ComponentLookup(component)); | |
| GlobalLocalsMap globalLocalsMap = GlobalLocalsMap.readFromDataSource( | |
| closedWorld.closureDataLookup.getEnclosingMember, | |
| source, | |
| ); | |
| InferredData inferredData = InferredData.readFromDataSource( | |
| source, | |
| closedWorld, | |
| ); | |
| return GlobalTypeInferenceResults.readFromDataSource( | |
| source, | |
| closedWorld.elementMap, | |
| closedWorld, | |
| globalLocalsMap, | |
| inferredData, | |
| ); | |
| } | |
| void serializeClosedWorldToSink(JClosedWorld closedWorld, DataSinkWriter sink) { | |
| closedWorld.writeToDataSink(sink); | |
| sink.close(); | |
| } | |
| JClosedWorld deserializeClosedWorldFromSource( | |
| CompilerOptions options, | |
| DiagnosticReporter reporter, | |
| AbstractValueStrategy abstractValueStrategy, | |
| ir.Component component, | |
| DataSourceReader source, | |
| ) { | |
| return JClosedWorld.readFromDataSource( | |
| options, | |
| reporter, | |
| abstractValueStrategy, | |
| component, | |
| source, | |
| ); | |
| } |