blob: 5b1f4df07c21b9e81477207d028bf66299038e8f [file] [log] [blame]
// 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;
import '../common/metrics.dart' show Metric, Metrics, CountMetric;
import '../common/tasks.dart' show CompilerTask;
import '../compiler.dart' show Compiler;
import '../constants/values.dart';
import '../deferred_load/output_unit.dart' show OutputUnit;
import '../elements/entities.dart';
import '../js/js.dart' as js_ast;
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.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 'js_emitter.dart';
import 'model.dart';
/// 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 {
late final RuntimeTypesChecks _rtiChecks;
late final NativeEmitter nativeEmitter;
late final MetadataCollector metadataCollector;
late final Emitter emitter;
final Compiler _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.
late final Set<ClassEntity> neededClasses;
/// See [neededClasses] but for class types.
late final Set<ClassEntity> neededClassTypes;
EmitterMetrics? _emitterMetrics;
EmitterMetrics get emitterMetrics => _emitterMetrics ??= EmitterMetrics();
@override
Metrics get metrics => _emitterMetrics ?? Metrics.none();
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(
closedWorld,
_backendStrategy.nativeCodegenEnqueuer,
);
emitter = startup_js_emitter.EmitterImpl(
_compiler.options,
_compiler.reporter,
_compiler.outputProvider,
_compiler.dumpInfoRegistry,
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,
closedWorld.backendUsage,
closedWorld.nativeData,
closedWorld.rtiNeed,
closedWorld.interceptorData,
_rtiChecks,
codegenInputs.rtiRecipeEncoder,
codegenWorld.oneShotInterceptorData,
_backendStrategy.customElementsCodegenAnalysis,
_backendStrategy.recordsCodegen,
_backendStrategy.generatedCode,
namer,
this,
closedWorld,
closedWorld.fieldAnalysis,
closedWorld.recordData,
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 {
/// Returns the JS prototype of the given class [e].
js_ast.Expression prototypeAccess(ClassEntity e);
/// Returns the JS function representing the given function.
///
/// The function must be invoked and can not be used as closure.
js_ast.Expression staticFunctionAccess(FunctionEntity element);
js_ast.Expression staticFieldAccess(FieldEntity element);
/// Returns the JS function that must be invoked to get the value of the
/// lazily initialized static.
js_ast.Expression isolateLazyInitializerAccess(covariant FieldEntity element);
/// Returns the closure expression of a static function.
js_ast.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.
js_ast.Expression constructorAccess(ClassEntity e);
/// Returns the JS name representing the type [e].
js_ast.Name typeAccess(ClassEntity e);
/// Returns the JS name representing the type variable [e].
js_ast.Name typeVariableAccess(TypeVariableEntity e);
/// Returns the JS code for accessing the embedded [global].
js_ast.Expression generateEmbeddedGlobalAccess(String global);
/// Returns the JS code for accessing the given [constant].
js_ast.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 {
Program? get programForTesting;
List<PreFragment>? get preDeferredFragmentsForTesting;
/// The set of omitted [OutputUnits].
Set<OutputUnit> get omittedOutputUnits;
/// A map of loadId to list of [FinalizedFragments].
Map<String, List<FinalizedFragment>> get finalizedFragmentsToLoad;
/// The [FragmentMerger] itself.
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].
js_ast.Expression interceptorPrototypeAccess(ClassEntity e);
/// Returns the JS constructor of the given interceptor class [e].
js_ast.Expression interceptorClassAccess(ClassEntity e);
/// Returns the JS expression representing a function that returns 'null'
js_ast.Expression generateFunctionThatReturnsNull();
int compareConstants(ConstantValue a, ConstantValue b);
bool isConstantInlinedOrAlreadyEmitted(ConstantValue constant);
/// Returns the size of the code generated for a given output [unit].
Map<OutputUnit, int> get generatedSizes;
}
class EmitterMetrics implements Metrics {
@override
String get namespace => 'emitter';
CountMetric hunkListElements = CountMetric('hunkListElements');
@override
Iterable<Metric> get primary => [];
@override
Iterable<Metric> get secondary => [hunkListElements];
}