blob: a509e8a11a0f748f554afaaaf8b3712426b2604e [file] [log] [blame]
// Copyright (c) 2017, 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.
// @dart = 2.10
library dart2js.js_model.strategy;
import 'package:kernel/ast.dart' as ir;
import '../common.dart';
import '../common/codegen.dart';
import '../common/elements.dart' show CommonElements, ElementEnvironment;
import '../common/tasks.dart';
import '../common/work.dart';
import '../compiler.dart';
import '../deferred_load/output_unit.dart'
show LateOutputUnitDataBuilder, OutputUnitData;
import '../dump_info.dart';
import '../elements/entities.dart';
import '../enqueue.dart';
import '../io/kernel_source_information.dart'
show KernelSourceInformationStrategy;
import '../io/source_information.dart';
import '../inferrer/abstract_value_domain.dart';
import '../inferrer/type_graph_inferrer.dart';
import '../inferrer/types.dart';
import '../js/js_source_mapping.dart';
import '../js_backend/backend.dart';
import '../js_backend/backend_impact.dart';
import '../js_backend/codegen_listener.dart';
import '../js_backend/custom_elements_analysis.dart';
import '../js_backend/enqueuer.dart';
import '../js_backend/impact_transformer.dart';
import '../js_backend/inferred_data.dart';
import '../js_backend/interceptor_data.dart';
import '../js_backend/namer.dart';
import '../js_backend/runtime_types.dart';
import '../js_backend/runtime_types_codegen.dart';
import '../js_backend/runtime_types_new.dart'
show RecipeEncoder, RecipeEncoderImpl;
import '../js_emitter/code_emitter_task.dart' show ModularEmitter;
import '../js_emitter/js_emitter.dart' show CodeEmitterTask;
import '../js/js.dart' as js;
import '../kernel/kernel_strategy.dart';
import '../kernel/kernel_world.dart';
import '../native/behavior.dart';
import '../native/enqueue.dart';
import '../options.dart';
import '../serialization/serialization.dart';
import '../ssa/builder.dart';
import '../ssa/metrics.dart';
import '../ssa/nodes.dart';
import '../ssa/ssa.dart';
import '../ssa/types.dart';
import '../tracer.dart';
import '../universe/codegen_world_builder.dart';
import '../universe/selector.dart';
import '../universe/world_impact.dart';
import '../world.dart';
import 'closure.dart';
import 'element_map.dart';
import 'element_map_impl.dart';
import 'js_world.dart';
import 'js_world_builder.dart' show JsClosedWorldBuilder;
import 'locals.dart';
/// JS Strategy pattern that defines the element model used in type inference
/// and code generation.
class JsBackendStrategy {
final Compiler _compiler;
JsKernelToElementMap _elementMap;
/// Codegen support for generating table of interceptors and
/// constructors for custom elements.
CustomElementsCodegenAnalysis _customElementsCodegenAnalysis;
NativeCodegenEnqueuer _nativeCodegenEnqueuer;
Namer _namer;
CodegenImpactTransformer _codegenImpactTransformer;
CodeEmitterTask _emitterTask;
RuntimeTypesChecksBuilder _rtiChecksBuilder;
FunctionCompiler _functionCompiler;
SourceInformationStrategy sourceInformationStrategy;
final SsaMetrics _ssaMetrics = SsaMetrics();
/// The generated code as a js AST for compiled methods.
final Map<MemberEntity, js.Expression> generatedCode = {};
JsBackendStrategy(this._compiler) {
bool generateSourceMap = _compiler.options.generateSourceMap;
if (!generateSourceMap) {
sourceInformationStrategy = const JavaScriptSourceInformationStrategy();
} else {
sourceInformationStrategy = KernelSourceInformationStrategy(this);
}
_emitterTask = CodeEmitterTask(_compiler, generateSourceMap);
_functionCompiler = SsaFunctionCompiler(
_compiler.options,
_compiler.reporter,
_ssaMetrics,
this,
_compiler.measurer,
sourceInformationStrategy);
}
List<CompilerTask> get tasks {
List<CompilerTask> result = functionCompiler.tasks;
result.add(emitterTask);
return result;
}
FunctionCompiler get functionCompiler => _functionCompiler;
CodeEmitterTask get emitterTask => _emitterTask;
Namer get namerForTesting => _namer;
NativeEnqueuer get nativeCodegenEnqueuer => _nativeCodegenEnqueuer;
RuntimeTypesChecksBuilder get rtiChecksBuilderForTesting => _rtiChecksBuilder;
Map<MemberEntity, WorldImpact> codegenImpactsForTesting;
String getGeneratedCodeForTesting(MemberEntity element) {
if (generatedCode[element] == null) return null;
return js.prettyPrint(generatedCode[element],
enableMinification: _compiler.options.enableMinification);
}
@deprecated
JsToElementMap get elementMap {
assert(_elementMap != null,
"JsBackendStrategy.elementMap has not been created yet.");
return _elementMap;
}
/// Codegen support for generating table of interceptors and
/// constructors for custom elements.
CustomElementsCodegenAnalysis get customElementsCodegenAnalysis {
assert(
_customElementsCodegenAnalysis != null,
failedAt(NO_LOCATION_SPANNABLE,
"CustomElementsCodegenAnalysis has not been created yet."));
return _customElementsCodegenAnalysis;
}
RuntimeTypesChecksBuilder get rtiChecksBuilder {
assert(
_rtiChecksBuilder != null,
failedAt(NO_LOCATION_SPANNABLE,
"RuntimeTypesChecksBuilder has not been created yet."));
assert(
!_rtiChecksBuilder.rtiChecksBuilderClosed,
failedAt(NO_LOCATION_SPANNABLE,
"RuntimeTypesChecks has already been computed."));
return _rtiChecksBuilder;
}
/// Create the [JClosedWorld] from [closedWorld].
JClosedWorld createJClosedWorld(
KClosedWorld closedWorld, OutputUnitData outputUnitData) {
KernelFrontendStrategy strategy = _compiler.frontendStrategy;
_elementMap = JsKernelToElementMap(
_compiler.reporter,
_compiler.environment,
strategy.elementMap,
closedWorld.liveMemberUsage,
closedWorld.annotationsData);
ClosureDataBuilder closureDataBuilder = ClosureDataBuilder(
_compiler.reporter, _elementMap, closedWorld.annotationsData);
JsClosedWorldBuilder closedWorldBuilder = JsClosedWorldBuilder(
_elementMap,
closureDataBuilder,
_compiler.options,
_compiler.reporter,
_compiler.abstractValueStrategy);
JClosedWorld jClosedWorld = closedWorldBuilder.convertClosedWorld(
closedWorld, strategy.closureModels, outputUnitData);
_elementMap.lateOutputUnitDataBuilder =
LateOutputUnitDataBuilder(jClosedWorld.outputUnitData);
return jClosedWorld;
}
/// Registers [closedWorld] as the current closed world used by this backend
/// strategy.
///
/// This is used to support serialization after type inference.
void registerJClosedWorld(covariant JsClosedWorld closedWorld) {
_elementMap = closedWorld.elementMap;
}
/// Called when the compiler starts running the codegen.
///
/// Returns the [CodegenInputs] objects with the needed data.
CodegenInputs onCodegenStart(
GlobalTypeInferenceResults globalTypeInferenceResults) {
JClosedWorld closedWorld = globalTypeInferenceResults.closedWorld;
FixedNames fixedNames = _compiler.options.enableMinification
? const MinifiedFixedNames()
: const FixedNames();
Tracer tracer =
Tracer(closedWorld, _compiler.options, _compiler.outputProvider);
RuntimeTypesSubstitutions rtiSubstitutions;
if (_compiler.options.disableRtiOptimization) {
rtiSubstitutions = TrivialRuntimeTypesSubstitutions(closedWorld);
_rtiChecksBuilder =
TrivialRuntimeTypesChecksBuilder(closedWorld, rtiSubstitutions);
} else {
RuntimeTypesImpl runtimeTypesImpl = RuntimeTypesImpl(closedWorld);
_rtiChecksBuilder = runtimeTypesImpl;
rtiSubstitutions = runtimeTypesImpl;
}
RecipeEncoder rtiRecipeEncoder = RecipeEncoderImpl(closedWorld,
rtiSubstitutions, closedWorld.nativeData, closedWorld.commonElements);
CodegenInputs codegen =
CodegenInputs(rtiSubstitutions, rtiRecipeEncoder, tracer, fixedNames);
functionCompiler.initialize(globalTypeInferenceResults, codegen);
return codegen;
}
/// Creates an [Enqueuer] for code generation specific to this backend.
CodegenEnqueuer createCodegenEnqueuer(
CompilerTask task,
JClosedWorld closedWorld,
GlobalTypeInferenceResults globalInferenceResults,
CodegenInputs codegen,
CodegenResults codegenResults) {
assert(_elementMap != null,
"JsBackendStrategy.elementMap has not been created yet.");
OneShotInterceptorData oneShotInterceptorData = OneShotInterceptorData(
closedWorld.interceptorData,
closedWorld.commonElements,
closedWorld.nativeData);
_onCodegenEnqueuerStart(
globalInferenceResults, codegen, oneShotInterceptorData);
ElementEnvironment elementEnvironment = closedWorld.elementEnvironment;
CommonElements commonElements = closedWorld.commonElements;
BackendImpacts impacts = BackendImpacts(commonElements, _compiler.options);
_customElementsCodegenAnalysis = CustomElementsCodegenAnalysis(
commonElements, elementEnvironment, closedWorld.nativeData);
return CodegenEnqueuer(
task,
CodegenWorldBuilderImpl(
closedWorld,
_compiler.abstractValueStrategy.createSelectorStrategy(),
oneShotInterceptorData),
KernelCodegenWorkItemBuilder(
this,
closedWorld,
codegenResults,
ClosedEntityLookup(_elementMap),
// TODO(johnniwinther): Avoid the need for a [ComponentLookup]. This
// is caused by some type masks holding a kernel node for using in
// tracing.
ComponentLookup(_elementMap.programEnv.mainComponent)),
CodegenEnqueuerListener(
_compiler.options,
elementEnvironment,
commonElements,
impacts,
closedWorld.backendUsage,
closedWorld.rtiNeed,
customElementsCodegenAnalysis,
nativeCodegenEnqueuer),
closedWorld.annotationsData);
}
/// Called before the compiler starts running the codegen enqueuer.
void _onCodegenEnqueuerStart(
GlobalTypeInferenceResults globalTypeInferenceResults,
CodegenInputs codegen,
OneShotInterceptorData oneShotInterceptorData) {
JClosedWorld closedWorld = globalTypeInferenceResults.closedWorld;
FixedNames fixedNames = codegen.fixedNames;
_namer = _compiler.options.enableMinification
? _compiler.options.useFrequencyNamer
? FrequencyBasedNamer(closedWorld, fixedNames)
: MinifyNamer(closedWorld, fixedNames)
: Namer(closedWorld, fixedNames);
_nativeCodegenEnqueuer = NativeCodegenEnqueuer(
_compiler.options,
closedWorld.elementEnvironment,
closedWorld.commonElements,
closedWorld.dartTypes,
emitterTask,
closedWorld.liveNativeClasses,
closedWorld.nativeData);
emitterTask.createEmitter(_namer, codegen, closedWorld);
// TODO(johnniwinther): Share the impact object created in
// createCodegenEnqueuer.
BackendImpacts impacts =
BackendImpacts(closedWorld.commonElements, _compiler.options);
_codegenImpactTransformer = CodegenImpactTransformer(
closedWorld,
closedWorld.elementEnvironment,
impacts,
closedWorld.nativeData,
closedWorld.backendUsage,
closedWorld.rtiNeed,
nativeCodegenEnqueuer,
_namer,
oneShotInterceptorData,
rtiChecksBuilder,
emitterTask.nativeEmitter);
}
WorldImpact generateCode(
WorkItem work,
JClosedWorld closedWorld,
CodegenResults codegenResults,
EntityLookup entityLookup,
ComponentLookup componentLookup) {
MemberEntity member = work.element;
CodegenResult result = codegenResults.getCodegenResults(member);
if (_compiler.options.testMode) {
bool useDataKinds = true;
List<Object> data = [];
DataSinkWriter sink = DataSinkWriter(
ObjectDataSink(data), _compiler.options,
useDataKinds: useDataKinds);
sink.registerCodegenWriter(CodegenWriterImpl(closedWorld));
result.writeToDataSink(sink);
sink.close();
DataSourceReader source = DataSourceReader(
ObjectDataSource(data), _compiler.options,
useDataKinds: useDataKinds);
List<ModularName> modularNames = [];
List<ModularExpression> modularExpression = [];
source.registerCodegenReader(
CodegenReaderImpl(closedWorld, modularNames, modularExpression));
source.registerEntityLookup(entityLookup);
source.registerComponentLookup(componentLookup);
result = CodegenResult.readFromDataSource(
source, modularNames, modularExpression);
}
if (result.code != null) {
generatedCode[member] = result.code;
}
if (retainDataForTesting) {
codegenImpactsForTesting ??= {};
codegenImpactsForTesting[member] = result.impact;
}
WorldImpact worldImpact =
_codegenImpactTransformer.transformCodegenImpact(result.impact);
_compiler.dumpInfoTask.registerImpact(member, worldImpact);
result.applyModularState(_namer, emitterTask.emitter);
return worldImpact;
}
/// Called when code generation has been completed.
void onCodegenEnd(CodegenInputs codegen) {
sourceInformationStrategy.onComplete();
codegen.tracer.close();
}
/// Generates the output and returns the total size of the generated code.
int assembleProgram(JClosedWorld closedWorld, InferredData inferredData,
CodegenInputs codegenInputs, CodegenWorld codegenWorld) {
int programSize = emitterTask.assembleProgram(
_namer, closedWorld, inferredData, codegenInputs, codegenWorld);
closedWorld.noSuchMethodData.emitDiagnostic(_compiler.reporter);
return programSize;
}
/// Creates the [SsaBuilder] used for the element model.
SsaBuilder createSsaBuilder(
CompilerTask task, SourceInformationStrategy sourceInformationStrategy) {
return KernelSsaBuilder(
task,
_compiler.options,
_compiler.reporter,
_compiler.dumpInfoTask,
_ssaMetrics,
// ignore:deprecated_member_use_from_same_package
elementMap,
sourceInformationStrategy);
}
/// Creates a [SourceSpan] from [spannable] in context of [currentElement].
SourceSpan spanFromSpannable(Spannable spannable, Entity currentElement) {
return _elementMap.getSourceSpan(spannable, currentElement);
}
/// Creates the [TypesInferrer] used by this strategy.
TypesInferrer createTypesInferrer(
JClosedWorld closedWorld,
GlobalLocalsMap globalLocalsMap,
InferredDataBuilder inferredDataBuilder) {
return TypeGraphInferrer(
_compiler, closedWorld, globalLocalsMap, inferredDataBuilder);
}
/// Prepare [source] to deserialize modular code generation data.
void prepareCodegenReader(DataSourceReader source) {
source.registerEntityReader(ClosedEntityReader(_elementMap));
source.registerEntityLookup(ClosedEntityLookup(_elementMap));
source.registerComponentLookup(
ComponentLookup(_elementMap.programEnv.mainComponent));
}
/// Calls [f] for every member that needs to be serialized for modular code
/// generation and returns an [EntityWriter] for encoding these members in
/// the serialized data.
///
/// The needed members include members computed on demand during non-modular
/// code generation, such as constructor bodies and and generator bodies.
EntityWriter forEachCodegenMember(void Function(MemberEntity member) f) {
int earlyMemberIndexLimit = _elementMap.prepareForCodegenSerialization();
ClosedEntityWriter entityWriter = ClosedEntityWriter(earlyMemberIndexLimit);
for (int memberIndex = 0;
memberIndex < _elementMap.members.length;
memberIndex++) {
MemberEntity member = _elementMap.members.getEntity(memberIndex);
if (member == null || member.isAbstract) continue;
f(member);
}
return entityWriter;
}
}
class KernelCodegenWorkItemBuilder implements WorkItemBuilder {
final JsBackendStrategy _backendStrategy;
final JClosedWorld _closedWorld;
final CodegenResults _codegenResults;
final EntityLookup _entityLookup;
final ComponentLookup _componentLookup;
KernelCodegenWorkItemBuilder(this._backendStrategy, this._closedWorld,
this._codegenResults, this._entityLookup, this._componentLookup);
@override
WorkItem createWorkItem(MemberEntity entity) {
if (entity.isAbstract) return null;
return KernelCodegenWorkItem(_backendStrategy, _closedWorld,
_codegenResults, _entityLookup, _componentLookup, entity);
}
}
class KernelCodegenWorkItem extends WorkItem {
final JsBackendStrategy _backendStrategy;
final JClosedWorld _closedWorld;
final CodegenResults _codegenResults;
final EntityLookup _entityLookup;
final ComponentLookup _componentLookup;
@override
final MemberEntity element;
KernelCodegenWorkItem(
this._backendStrategy,
this._closedWorld,
this._codegenResults,
this._entityLookup,
this._componentLookup,
this.element);
@override
WorldImpact run() {
return _backendStrategy.generateCode(
this, _closedWorld, _codegenResults, _entityLookup, _componentLookup);
}
}
/// Task for building SSA from kernel IR loaded from .dill.
class KernelSsaBuilder implements SsaBuilder {
final CompilerTask _task;
final CompilerOptions _options;
final DiagnosticReporter _reporter;
final DumpInfoTask _dumpInfoTask;
final SsaMetrics _metrics;
final JsToElementMap _elementMap;
final SourceInformationStrategy _sourceInformationStrategy;
FunctionInlineCache _inlineCache;
InlineDataCache _inlineDataCache;
KernelSsaBuilder(
this._task,
this._options,
this._reporter,
this._dumpInfoTask,
this._metrics,
this._elementMap,
this._sourceInformationStrategy);
@override
HGraph build(
MemberEntity member,
JClosedWorld closedWorld,
GlobalTypeInferenceResults results,
CodegenInputs codegen,
CodegenRegistry registry,
ModularNamer namer,
ModularEmitter emitter) {
_inlineCache ??= FunctionInlineCache(closedWorld.annotationsData);
_inlineDataCache ??= InlineDataCache(
enableUserAssertions: _options.enableUserAssertions,
omitImplicitCasts: _options.omitImplicitChecks);
return _task.measure(() {
KernelSsaGraphBuilder builder = KernelSsaGraphBuilder(
_options,
_reporter,
member,
_elementMap.getMemberThisType(member),
_dumpInfoTask,
_metrics,
_elementMap,
results,
closedWorld,
registry,
namer,
emitter,
codegen.tracer,
_sourceInformationStrategy,
_inlineCache,
_inlineDataCache);
return builder.build();
});
}
}
class KernelToTypeInferenceMapImpl implements KernelToTypeInferenceMap {
final GlobalTypeInferenceResults _globalInferenceResults;
GlobalTypeInferenceMemberResult _targetResults;
KernelToTypeInferenceMapImpl(
MemberEntity target, this._globalInferenceResults) {
_targetResults = _resultOf(target);
}
GlobalTypeInferenceMemberResult _resultOf(MemberEntity e) =>
_globalInferenceResults
.resultOfMember(e is ConstructorBodyEntity ? e.constructor : e);
@override
AbstractValue getReturnTypeOf(FunctionEntity function) {
return AbstractValueFactory.inferredReturnTypeForElement(
function, _globalInferenceResults);
}
@override
AbstractValue receiverTypeOfInvocation(
ir.Expression node, AbstractValueDomain abstractValueDomain) {
return _targetResults.typeOfReceiver(node);
}
@override
AbstractValue receiverTypeOfGet(ir.Expression node) {
return _targetResults.typeOfReceiver(node);
}
@override
AbstractValue receiverTypeOfSet(
ir.Expression node, AbstractValueDomain abstractValueDomain) {
return _targetResults.typeOfReceiver(node);
}
@override
AbstractValue typeOfListLiteral(
ir.ListLiteral listLiteral, AbstractValueDomain abstractValueDomain) {
return _globalInferenceResults.typeOfListLiteral(listLiteral) ??
abstractValueDomain.dynamicType;
}
@override
AbstractValue typeOfIterator(ir.ForInStatement node) {
return _targetResults.typeOfIterator(node);
}
@override
AbstractValue typeOfIteratorCurrent(ir.ForInStatement node) {
return _targetResults.typeOfIteratorCurrent(node);
}
@override
AbstractValue typeOfIteratorMoveNext(ir.ForInStatement node) {
return _targetResults.typeOfIteratorMoveNext(node);
}
@override
bool isJsIndexableIterator(
ir.ForInStatement node, AbstractValueDomain abstractValueDomain) {
AbstractValue mask = typeOfIterator(node);
// TODO(sra): Investigate why mask is sometimes null.
if (mask == null) return false;
return abstractValueDomain.isJsIndexableAndIterable(mask).isDefinitelyTrue;
}
@override
AbstractValue inferredIndexType(ir.ForInStatement node) {
return AbstractValueFactory.inferredResultTypeForSelector(
Selector.index(), typeOfIterator(node), _globalInferenceResults);
}
@override
AbstractValue getInferredTypeOf(MemberEntity member) {
return AbstractValueFactory.inferredTypeForMember(
member, _globalInferenceResults);
}
@override
AbstractValue getInferredTypeOfParameter(Local parameter) {
return AbstractValueFactory.inferredTypeForParameter(
parameter, _globalInferenceResults);
}
@override
AbstractValue resultTypeOfSelector(Selector selector, AbstractValue mask) {
return AbstractValueFactory.inferredResultTypeForSelector(
selector, mask, _globalInferenceResults);
}
@override
AbstractValue typeFromNativeBehavior(
NativeBehavior nativeBehavior, JClosedWorld closedWorld) {
return AbstractValueFactory.fromNativeBehavior(nativeBehavior, closedWorld);
}
}