| // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| library dart2js.js_emitter.code_emitter_task; |
| |
| import 'package:compiler/src/dump_info.dart'; |
| import 'package:compiler/src/native/enqueue.dart'; |
| |
| import '../common/metrics.dart' show Metric, Metrics, CountMetric; |
| import '../common/tasks.dart' show CompilerTask; |
| import '../compiler_interfaces.dart' show CompilerEmitterFacade; |
| import '../constants/values.dart'; |
| import '../deferred_load/output_unit.dart' show OutputUnit; |
| import '../elements/entities.dart'; |
| import '../js/js.dart' as jsAst; |
| import '../js_backend/codegen_inputs.dart' show CodegenInputs; |
| import '../js_backend/inferred_data.dart'; |
| import '../js_backend/namer.dart' show Namer; |
| import '../js_backend/runtime_types.dart' show RuntimeTypesChecks; |
| import '../js_model/js_strategy_interfaces.dart'; |
| import '../js_model/js_world.dart' show JClosedWorld; |
| import '../options.dart'; |
| import '../universe/codegen_world_builder.dart'; |
| import 'program_builder/program_builder.dart'; |
| import 'startup_emitter/emitter.dart' as startup_js_emitter; |
| import 'startup_emitter/fragment_merger.dart'; |
| |
| import 'metadata_collector.dart' show MetadataCollector; |
| import 'model.dart'; |
| import 'native_emitter.dart' show NativeEmitter; |
| import 'interfaces.dart' as interfaces; |
| |
| /// Generates the code for all used classes in the program. Static fields (even |
| /// in classes) are ignored, since they can be treated as non-class elements. |
| /// |
| /// The code for the containing (used) methods must exist in the `universe`. |
| class CodeEmitterTask extends CompilerTask |
| implements interfaces.CodeEmitterTask { |
| late final RuntimeTypesChecks _rtiChecks; |
| @override |
| late final NativeEmitter nativeEmitter; |
| @override |
| late final MetadataCollector metadataCollector; |
| @override |
| late final Emitter emitter; |
| final CompilerEmitterFacade _compiler; |
| final bool _generateSourceMap; |
| |
| JsBackendStrategy get _backendStrategy => _compiler.backendStrategy; |
| |
| CompilerOptions get options => _compiler.options; |
| |
| /// The field is set after the program has been emitted. |
| /// Contains a list of all classes that are emitted. |
| /// Currently used for testing and dump-info. |
| @override |
| late final Set<ClassEntity> neededClasses; |
| |
| /// See [neededClasses] but for class types. |
| @override |
| late final Set<ClassEntity> neededClassTypes; |
| |
| @override |
| final _EmitterMetrics metrics = _EmitterMetrics(); |
| |
| CodeEmitterTask(this._compiler, this._generateSourceMap) |
| : super(_compiler.measurer); |
| |
| @override |
| String get name => 'Code emitter'; |
| |
| void _finalizeRti(CodegenInputs codegen, CodegenWorld codegenWorld) { |
| // Compute the required type checks to know which classes need a |
| // 'is$' method. |
| _rtiChecks = _backendStrategy.rtiChecksBuilder |
| .computeRequiredChecks(codegenWorld, options); |
| } |
| |
| /// Creates the [Emitter] for this task. |
| void createEmitter( |
| Namer namer, CodegenInputs codegen, JClosedWorld closedWorld) { |
| measure(() { |
| nativeEmitter = NativeEmitter(this, closedWorld, |
| _backendStrategy.nativeCodegenEnqueuer as NativeCodegenEnqueuer); |
| emitter = startup_js_emitter.EmitterImpl( |
| _compiler.options, |
| _compiler.reporter, |
| _compiler.outputProvider, |
| _compiler.dumpInfoTask as DumpInfoTask, |
| namer, |
| closedWorld, |
| codegen.rtiRecipeEncoder, |
| nativeEmitter, |
| _backendStrategy.sourceInformationStrategy, |
| this, |
| _generateSourceMap); |
| metadataCollector = MetadataCollector( |
| _compiler.reporter, emitter, codegen.rtiRecipeEncoder); |
| }); |
| } |
| |
| int assembleProgram( |
| Namer namer, |
| JClosedWorld closedWorld, |
| InferredData inferredData, |
| CodegenInputs codegenInputs, |
| CodegenWorld codegenWorld) { |
| return measure(() { |
| measureSubtask('finalize rti', () { |
| _finalizeRti(codegenInputs, codegenWorld); |
| }); |
| ProgramBuilder programBuilder = ProgramBuilder( |
| _compiler.options, |
| closedWorld.elementEnvironment, |
| closedWorld.commonElements, |
| closedWorld.outputUnitData, |
| codegenWorld, |
| _backendStrategy.nativeCodegenEnqueuer as NativeCodegenEnqueuer, |
| closedWorld.backendUsage, |
| closedWorld.nativeData, |
| closedWorld.rtiNeed, |
| closedWorld.interceptorData, |
| _rtiChecks, |
| codegenInputs.rtiRecipeEncoder, |
| codegenWorld.oneShotInterceptorData, |
| _backendStrategy.customElementsCodegenAnalysis, |
| _backendStrategy.generatedCode, |
| namer, |
| this, |
| closedWorld, |
| closedWorld.fieldAnalysis, |
| inferredData, |
| _backendStrategy.sourceInformationStrategy, |
| closedWorld.sorter, |
| _rtiChecks.requiredClasses, |
| closedWorld.elementEnvironment.mainFunction!); |
| int size = emitter.emitProgram(programBuilder, codegenWorld); |
| neededClasses = programBuilder.collector.neededClasses; |
| neededClassTypes = programBuilder.collector.neededClassTypes; |
| return size; |
| }); |
| } |
| } |
| |
| /// Interface for the subset of the [Emitter] that can be used during modular |
| /// code generation. |
| /// |
| /// Note that the emission phase is not itself modular but performed on |
| /// the closed world computed by the codegen enqueuer. |
| abstract class ModularEmitter implements interfaces.ModularEmitter { |
| /// Returns the JS prototype of the given class [e]. |
| @override |
| jsAst.Expression prototypeAccess(ClassEntity e); |
| |
| /// Returns the JS function representing the given function. |
| /// |
| /// The function must be invoked and can not be used as closure. |
| @override |
| jsAst.Expression staticFunctionAccess(FunctionEntity element); |
| |
| @override |
| jsAst.Expression staticFieldAccess(FieldEntity element); |
| |
| /// Returns the JS function that must be invoked to get the value of the |
| /// lazily initialized static. |
| @override |
| jsAst.Expression isolateLazyInitializerAccess(covariant FieldEntity element); |
| |
| /// Returns the closure expression of a static function. |
| @override |
| jsAst.Expression staticClosureAccess(covariant FunctionEntity element); |
| |
| /// Returns the JS constructor of the given element. |
| /// |
| /// The returned expression must only be used in a JS `new` expression. |
| @override |
| jsAst.Expression constructorAccess(ClassEntity e); |
| |
| /// Returns the JS name representing the type [e]. |
| @override |
| jsAst.Name typeAccessNewRti(ClassEntity e); |
| |
| /// Returns the JS name representing the type variable [e]. |
| @override |
| jsAst.Name typeVariableAccessNewRti(TypeVariableEntity e); |
| |
| /// Returns the JS code for accessing the embedded [global]. |
| jsAst.Expression generateEmbeddedGlobalAccess(String global); |
| |
| /// Returns the JS code for accessing the given [constant]. |
| @override |
| jsAst.Expression constantReference(ConstantValue constant); |
| |
| /// Returns the JS code for accessing the global property [global]. |
| String generateEmbeddedGlobalAccessString(String global); |
| } |
| |
| /// Interface for the emitter that is used during the emission phase on the |
| /// closed world computed by the codegen enqueuer. |
| /// |
| /// These methods are _not_ available during modular code generation. |
| abstract class Emitter implements ModularEmitter, interfaces.Emitter { |
| Program? get programForTesting; |
| |
| List<PreFragment>? get preDeferredFragmentsForTesting; |
| |
| /// The set of omitted [OutputUnits]. |
| Set<OutputUnit> get omittedOutputUnits; |
| |
| /// A map of loadId to list of [FinalizedFragments]. |
| @override |
| Map<String, List<FinalizedFragment>> get finalizedFragmentsToLoad; |
| |
| /// The [FragmentMerger] itself. |
| @override |
| FragmentMerger get fragmentMerger; |
| |
| /// Uses the [programBuilder] to generate a model of the program, emits |
| /// the program, and returns the size of the generated output. |
| int emitProgram(ProgramBuilder programBuilder, CodegenWorld codegenWorld); |
| |
| /// Returns the JS prototype of the given interceptor class [e]. |
| @override |
| jsAst.Expression interceptorPrototypeAccess(ClassEntity e); |
| |
| /// Returns the JS constructor of the given interceptor class [e]. |
| @override |
| jsAst.Expression interceptorClassAccess(ClassEntity e); |
| |
| /// Returns the JS expression representing a function that returns 'null' |
| jsAst.Expression generateFunctionThatReturnsNull(); |
| |
| @override |
| int compareConstants(ConstantValue a, ConstantValue b); |
| @override |
| bool isConstantInlinedOrAlreadyEmitted(ConstantValue constant); |
| |
| /// Returns the size of the code generated for a given output [unit]. |
| @override |
| int generatedSize(OutputUnit unit); |
| } |
| |
| class _EmitterMetrics implements Metrics { |
| @override |
| String get namespace => 'emitter'; |
| |
| CountMetric hunkListElements = CountMetric('hunkListElements'); |
| |
| @override |
| Iterable<Metric> get primary => []; |
| |
| @override |
| Iterable<Metric> get secondary => [hunkListElements]; |
| } |