blob: 3ced9a0264ab8abc9f31b4146c0ab976f37dcc79 [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 js_backend.backend;
import '../common.dart';
import '../common/backend_api.dart' show ImpactTransformer;
import '../common/codegen.dart';
import '../common/names.dart' show Uris;
import '../common/tasks.dart' show CompilerTask;
import '../common/work.dart';
import '../common_elements.dart' show CommonElements, ElementEnvironment;
import '../compiler.dart' show Compiler;
import '../deferred_load.dart' show DeferredLoadTask;
import '../dump_info.dart' show DumpInfoTask;
import '../elements/entities.dart';
import '../enqueue.dart' show Enqueuer, EnqueueTask, ResolutionEnqueuer;
import '../frontend_strategy.dart';
import '../inferrer/types.dart';
import '../io/source_information.dart' show SourceInformationStrategy;
import '../js/js.dart' as jsAst;
import '../js_model/elements.dart';
import '../js_emitter/js_emitter.dart' show CodeEmitterTask;
import '../kernel/dart2js_target.dart';
import '../native/enqueue.dart';
import '../serialization/serialization.dart';
import '../ssa/ssa.dart' show SsaFunctionCompiler;
import '../tracer.dart';
import '../universe/class_hierarchy.dart'
show ClassHierarchyBuilder, ClassQueries;
import '../universe/codegen_world_builder.dart';
import '../universe/world_builder.dart';
import '../universe/world_impact.dart'
show ImpactStrategy, ImpactUseCase, WorldImpact, WorldImpactVisitor;
import '../world.dart' show JClosedWorld;
import 'field_analysis.dart';
import 'annotations.dart';
import 'backend_impact.dart';
import 'backend_usage.dart';
import 'checked_mode_helpers.dart';
import 'codegen_listener.dart';
import 'custom_elements_analysis.dart';
import 'enqueuer.dart';
import 'impact_transformer.dart';
import 'inferred_data.dart';
import 'interceptor_data.dart';
import 'namer.dart';
import 'native_data.dart';
import 'no_such_method_registry.dart';
import 'resolution_listener.dart';
import 'runtime_types.dart';
abstract class FunctionCompiler {
void initialize(
GlobalTypeInferenceResults globalInferenceResults, CodegenInputs codegen);
/// Generates JavaScript code for [member].
CodegenResult compile(MemberEntity member);
Iterable get tasks;
}
/*
* Invariants:
* canInline(function) implies canInline(function, insideLoop:true)
* !canInline(function, insideLoop: true) implies !canInline(function)
*/
class FunctionInlineCache {
static const int _unknown = -1;
static const int _mustNotInline = 0;
// May-inline-in-loop means that the function may not be inlined outside loops
// but may be inlined in a loop.
static const int _mayInlineInLoopMustNotOutside = 1;
// The function can be inlined in a loop, but not outside.
static const int _canInlineInLoopMustNotOutside = 2;
// May-inline means that we know that it can be inlined inside a loop, but
// don't know about the general case yet.
static const int _canInlineInLoopMayInlineOutside = 3;
static const int _canInline = 4;
final Map<FunctionEntity, int> _cachedDecisions =
new Map<FunctionEntity, int>();
final Set<FunctionEntity> _tryInlineFunctions = new Set<FunctionEntity>();
FunctionInlineCache(AnnotationsData annotationsData) {
annotationsData.forEachNoInline((FunctionEntity function) {
markAsNonInlinable(function);
});
annotationsData.forEachTryInline((FunctionEntity function) {
markAsTryInline(function);
});
}
/// Checks that [method] is the canonical representative for this method.
///
/// For a [MethodElement] this means it must be the declaration element.
bool checkFunction(FunctionEntity method) {
return '$method'.startsWith(jsElementPrefix);
}
/// Returns the current cache decision. This should only be used for testing.
int getCurrentCacheDecisionForTesting(FunctionEntity element) {
assert(checkFunction(element), failedAt(element));
return _cachedDecisions[element];
}
// Returns `true`/`false` if we have a cached decision.
// Returns `null` otherwise.
bool canInline(FunctionEntity element, {bool insideLoop}) {
assert(checkFunction(element), failedAt(element));
int decision = _cachedDecisions[element];
if (decision == null) {
// TODO(sra): Have annotations for mustInline / noInline for constructor
// bodies. (There used to be some logic here to have constructor bodies,
// inherit the settings from annotations on the generative
// constructor. This was conflated with the heuristic decisions, leading
// to lack of inlining where it was beneficial.)
decision = _unknown;
}
if (insideLoop) {
switch (decision) {
case _mustNotInline:
return false;
case _unknown:
case _mayInlineInLoopMustNotOutside:
// We know we can't inline outside a loop, but don't know for the
// loop case. Return `null` to indicate that we don't know yet.
return null;
case _canInlineInLoopMustNotOutside:
case _canInlineInLoopMayInlineOutside:
case _canInline:
return true;
}
} else {
switch (decision) {
case _mustNotInline:
case _mayInlineInLoopMustNotOutside:
case _canInlineInLoopMustNotOutside:
return false;
case _unknown:
case _canInlineInLoopMayInlineOutside:
// We know we can inline inside a loop, but don't know for the
// non-loop case. Return `null` to indicate that we don't know yet.
return null;
case _canInline:
return true;
}
}
// Quiet static checker.
return null;
}
void markAsInlinable(FunctionEntity element, {bool insideLoop}) {
assert(checkFunction(element), failedAt(element));
int oldDecision = _cachedDecisions[element];
if (oldDecision == null) {
oldDecision = _unknown;
}
if (insideLoop) {
switch (oldDecision) {
case _mustNotInline:
throw failedAt(
element,
"Can't mark a function as non-inlinable and inlinable at the "
"same time.");
case _unknown:
// We know that it can be inlined in a loop, but don't know about the
// non-loop case yet.
_cachedDecisions[element] = _canInlineInLoopMayInlineOutside;
break;
case _mayInlineInLoopMustNotOutside:
_cachedDecisions[element] = _canInlineInLoopMustNotOutside;
break;
case _canInlineInLoopMustNotOutside:
case _canInlineInLoopMayInlineOutside:
case _canInline:
// Do nothing.
break;
}
} else {
switch (oldDecision) {
case _mustNotInline:
case _mayInlineInLoopMustNotOutside:
case _canInlineInLoopMustNotOutside:
throw failedAt(
element,
"Can't mark a function as non-inlinable and inlinable at the "
"same time.");
case _unknown:
case _canInlineInLoopMayInlineOutside:
_cachedDecisions[element] = _canInline;
break;
case _canInline:
// Do nothing.
break;
}
}
}
void markAsNonInlinable(FunctionEntity element, {bool insideLoop: true}) {
assert(checkFunction(element), failedAt(element));
int oldDecision = _cachedDecisions[element];
if (oldDecision == null) {
oldDecision = _unknown;
}
if (insideLoop) {
switch (oldDecision) {
case _canInlineInLoopMustNotOutside:
case _canInlineInLoopMayInlineOutside:
case _canInline:
throw failedAt(
element,
"Can't mark a function as non-inlinable and inlinable at the "
"same time.");
case _mayInlineInLoopMustNotOutside:
case _unknown:
_cachedDecisions[element] = _mustNotInline;
break;
case _mustNotInline:
// Do nothing.
break;
}
} else {
switch (oldDecision) {
case _canInline:
throw failedAt(
element,
"Can't mark a function as non-inlinable and inlinable at the "
"same time.");
case _unknown:
// We can't inline outside a loop, but we might still be allowed to do
// so outside.
_cachedDecisions[element] = _mayInlineInLoopMustNotOutside;
break;
case _canInlineInLoopMayInlineOutside:
// We already knew that the function could be inlined inside a loop,
// but didn't have information about the non-loop case. Now we know
// that it can't be inlined outside a loop.
_cachedDecisions[element] = _canInlineInLoopMustNotOutside;
break;
case _mayInlineInLoopMustNotOutside:
case _canInlineInLoopMustNotOutside:
case _mustNotInline:
// Do nothing.
break;
}
}
}
void markAsTryInline(FunctionEntity element) {
assert(checkFunction(element), failedAt(element));
_tryInlineFunctions.add(element);
}
bool markedAsTryInline(FunctionEntity element) {
assert(checkFunction(element), failedAt(element));
return _tryInlineFunctions.contains(element);
}
}
class JavaScriptBackend {
static const String JS = 'JS';
static const String JS_BUILTIN = 'JS_BUILTIN';
static const String JS_EMBEDDED_GLOBAL = 'JS_EMBEDDED_GLOBAL';
static const String JS_INTERCEPTOR_CONSTANT = 'JS_INTERCEPTOR_CONSTANT';
static const String JS_STRING_CONCAT = 'JS_STRING_CONCAT';
final Compiler compiler;
FrontendStrategy get frontendStrategy => compiler.frontendStrategy;
FunctionCompiler functionCompiler;
CodeEmitterTask emitterTask;
/// The generated code as a js AST for compiled methods.
final Map<MemberEntity, jsAst.Expression> generatedCode =
<MemberEntity, jsAst.Expression>{};
Namer _namer;
Namer get namerForTesting => _namer;
/// Set of classes whose `operator ==` methods handle `null` themselves.
final Set<ClassEntity> specialOperatorEqClasses = new Set<ClassEntity>();
List<CompilerTask> get tasks {
List<CompilerTask> result = functionCompiler.tasks;
result.add(emitterTask);
return result;
}
RuntimeTypesChecksBuilder _rtiChecksBuilder;
/// True if the html library has been loaded.
bool htmlLibraryIsLoaded = false;
/// Resolution support for generating table of interceptors and
/// constructors for custom elements.
CustomElementsResolutionAnalysis _customElementsResolutionAnalysis;
/// Codegen support for generating table of interceptors and
/// constructors for custom elements.
CustomElementsCodegenAnalysis _customElementsCodegenAnalysis;
KFieldAnalysis _fieldAnalysis;
/// Support for classifying `noSuchMethod` implementations.
NoSuchMethodRegistry noSuchMethodRegistry;
/// Backend transformation methods for the world impacts.
ImpactTransformer impactTransformer;
CodegenImpactTransformer _codegenImpactTransformer;
/// The strategy used for collecting and emitting source information.
SourceInformationStrategy sourceInformationStrategy;
NativeDataBuilderImpl _nativeDataBuilder;
NativeDataBuilder get nativeDataBuilder => _nativeDataBuilder;
BackendUsageBuilder _backendUsageBuilder;
NativeResolutionEnqueuer _nativeResolutionEnqueuer;
NativeCodegenEnqueuer _nativeCodegenEnqueuer;
JavaScriptBackend(this.compiler,
{bool generateSourceMap: true,
bool useMultiSourceInfo: false,
bool useNewSourceInfo: false})
: this.sourceInformationStrategy =
compiler.backendStrategy.sourceInformationStrategy {
CommonElements commonElements = compiler.frontendStrategy.commonElements;
_backendUsageBuilder =
new BackendUsageBuilderImpl(compiler.frontendStrategy);
emitterTask = new CodeEmitterTask(compiler, generateSourceMap);
noSuchMethodRegistry = new NoSuchMethodRegistryImpl(
commonElements, compiler.frontendStrategy.createNoSuchMethodResolver());
functionCompiler = new SsaFunctionCompiler(
compiler.options,
compiler.reporter,
compiler.backendStrategy,
compiler.measurer,
sourceInformationStrategy);
}
DiagnosticReporter get reporter => compiler.reporter;
ImpactCacheDeleter get impactCacheDeleter => compiler.impactCacheDeleter;
KFieldAnalysis get fieldAnalysisForTesting => _fieldAnalysis;
/// Resolution support for generating table of interceptors and
/// constructors for custom elements.
CustomElementsResolutionAnalysis get customElementsResolutionAnalysis {
assert(
_customElementsResolutionAnalysis != null,
failedAt(NO_LOCATION_SPANNABLE,
"CustomElementsResolutionAnalysis has not been created yet."));
return _customElementsResolutionAnalysis;
}
/// 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;
}
RuntimeTypesChecksBuilder get rtiChecksBuilderForTesting => _rtiChecksBuilder;
void validateInterceptorImplementsAllObjectMethods(
ClassEntity interceptorClass) {
if (interceptorClass == null) return;
ClassEntity objectClass = frontendStrategy.commonElements.objectClass;
frontendStrategy.elementEnvironment.forEachClassMember(objectClass,
(_, MemberEntity member) {
MemberEntity interceptorMember = frontendStrategy.elementEnvironment
.lookupLocalClassMember(interceptorClass, member.name);
// Interceptors must override all Object methods due to calling convention
// differences.
assert(
interceptorMember.enclosingClass == interceptorClass,
failedAt(
interceptorMember,
"Member ${member.name} not overridden in ${interceptorClass}. "
"Found $interceptorMember from "
"${interceptorMember.enclosingClass}."));
});
}
/// Called before processing of the resolution queue is started.
void onResolutionStart() {
// TODO(johnniwinther): Avoid the compiler.elementEnvironment.getThisType
// calls. Currently needed to ensure resolution of the classes for various
// queries in native behavior computation, inference and codegen.
frontendStrategy.elementEnvironment
.getThisType(frontendStrategy.commonElements.jsArrayClass);
frontendStrategy.elementEnvironment
.getThisType(frontendStrategy.commonElements.jsExtendableArrayClass);
validateInterceptorImplementsAllObjectMethods(
frontendStrategy.commonElements.jsInterceptorClass);
// The null-interceptor must also implement *all* methods.
validateInterceptorImplementsAllObjectMethods(
frontendStrategy.commonElements.jsNullClass);
}
/// Called when the resolution queue has been closed.
void onResolutionEnd() {
frontendStrategy.annotationProcessor.processJsInteropAnnotations(
frontendStrategy.nativeBasicData, nativeDataBuilder);
}
ResolutionEnqueuer createResolutionEnqueuer(
CompilerTask task, Compiler compiler) {
ElementEnvironment elementEnvironment =
compiler.frontendStrategy.elementEnvironment;
CommonElements commonElements = compiler.frontendStrategy.commonElements;
NativeBasicData nativeBasicData = compiler.frontendStrategy.nativeBasicData;
RuntimeTypesNeedBuilder rtiNeedBuilder =
compiler.frontendStrategy.createRuntimeTypesNeedBuilder();
BackendImpacts impacts = new BackendImpacts(commonElements);
_nativeResolutionEnqueuer = new NativeResolutionEnqueuer(
compiler.options,
elementEnvironment,
commonElements,
compiler.frontendStrategy.dartTypes,
compiler.frontendStrategy.createNativeClassFinder(nativeBasicData));
_nativeDataBuilder = new NativeDataBuilderImpl(nativeBasicData);
_customElementsResolutionAnalysis = new CustomElementsResolutionAnalysis(
elementEnvironment,
commonElements,
nativeBasicData,
_backendUsageBuilder);
_fieldAnalysis = new KFieldAnalysis(compiler.frontendStrategy);
ClassQueries classQueries = compiler.frontendStrategy.createClassQueries();
ClassHierarchyBuilder classHierarchyBuilder =
new ClassHierarchyBuilder(commonElements, classQueries);
impactTransformer = new JavaScriptImpactTransformer(
compiler.options,
elementEnvironment,
commonElements,
impacts,
nativeBasicData,
_nativeResolutionEnqueuer,
_backendUsageBuilder,
customElementsResolutionAnalysis,
rtiNeedBuilder,
classHierarchyBuilder);
InterceptorDataBuilder interceptorDataBuilder =
new InterceptorDataBuilderImpl(
nativeBasicData, elementEnvironment, commonElements);
AnnotationsDataBuilder annotationsDataBuilder =
new AnnotationsDataBuilder();
return new ResolutionEnqueuer(
task,
compiler.options,
compiler.reporter,
new ResolutionEnqueuerListener(
compiler.options,
elementEnvironment,
commonElements,
impacts,
nativeBasicData,
interceptorDataBuilder,
_backendUsageBuilder,
noSuchMethodRegistry,
customElementsResolutionAnalysis,
_nativeResolutionEnqueuer,
_fieldAnalysis,
compiler.deferredLoadTask),
compiler.frontendStrategy.createResolutionWorldBuilder(
nativeBasicData,
_nativeDataBuilder,
interceptorDataBuilder,
_backendUsageBuilder,
rtiNeedBuilder,
_fieldAnalysis,
_nativeResolutionEnqueuer,
noSuchMethodRegistry,
annotationsDataBuilder,
const StrongModeWorldStrategy(),
classHierarchyBuilder,
classQueries),
compiler.frontendStrategy.createResolutionWorkItemBuilder(
nativeBasicData,
_nativeDataBuilder,
annotationsDataBuilder,
impactTransformer,
compiler.impactCache,
_fieldAnalysis));
}
/// Creates an [Enqueuer] for code generation specific to this backend.
CodegenEnqueuer createCodegenEnqueuer(
CompilerTask task,
Compiler compiler,
JClosedWorld closedWorld,
GlobalTypeInferenceResults globalInferenceResults,
CodegenInputs codegen,
CodegenResults codegenResults) {
OneShotInterceptorData oneShotInterceptorData = new OneShotInterceptorData(
closedWorld.interceptorData,
closedWorld.commonElements,
closedWorld.nativeData);
_onCodegenEnqueuerStart(
globalInferenceResults, codegen, oneShotInterceptorData);
ElementEnvironment elementEnvironment = closedWorld.elementEnvironment;
CommonElements commonElements = closedWorld.commonElements;
BackendImpacts impacts = new BackendImpacts(commonElements);
_customElementsCodegenAnalysis = new CustomElementsCodegenAnalysis(
commonElements, elementEnvironment, closedWorld.nativeData);
return new CodegenEnqueuer(
task,
compiler.options,
compiler.backendStrategy.createCodegenWorldBuilder(
closedWorld.nativeData,
closedWorld,
compiler.abstractValueStrategy.createSelectorStrategy(),
oneShotInterceptorData),
compiler.backendStrategy
.createCodegenWorkItemBuilder(closedWorld, codegenResults),
new CodegenEnqueuerListener(
elementEnvironment,
commonElements,
impacts,
closedWorld.backendUsage,
closedWorld.rtiNeed,
customElementsCodegenAnalysis,
nativeCodegenEnqueuer));
}
Map<MemberEntity, WorldImpact> codegenImpactsForTesting;
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 = [];
DataSink sink = new ObjectSink(data, useDataKinds: useDataKinds);
sink.registerCodegenWriter(new CodegenWriterImpl(closedWorld));
result.writeToDataSink(sink);
DataSource source = new ObjectSource(data, useDataKinds: useDataKinds);
List<ModularName> modularNames = [];
List<ModularExpression> modularExpression = [];
source.registerCodegenReader(
new 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 ??= <MemberEntity, WorldImpact>{};
codegenImpactsForTesting[member] = result.impact;
}
WorldImpact worldImpact =
_codegenImpactTransformer.transformCodegenImpact(result.impact);
compiler.dumpInfoTask.registerImpact(member, worldImpact);
result.applyModularState(_namer, emitterTask.emitter);
return worldImpact;
}
NativeResolutionEnqueuer get nativeResolutionEnqueuerForTesting =>
_nativeResolutionEnqueuer;
NativeEnqueuer get nativeCodegenEnqueuer => _nativeCodegenEnqueuer;
/// Unit test hook that returns code of an element as a String.
///
/// Invariant: [element] must be a declaration element.
String getGeneratedCode(MemberEntity element) {
return jsAst.prettyPrint(generatedCode[element],
enableMinification: compiler.options.enableMinification);
}
/// 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(reporter);
return programSize;
}
/// This method is called immediately after the [library] and its parts have
/// been loaded.
void setAnnotations(LibraryEntity library) {
AnnotationProcessor processor =
compiler.frontendStrategy.annotationProcessor;
if (maybeEnableNative(library.canonicalUri)) {
processor.extractNativeAnnotations(library);
}
processor.extractJsInteropAnnotations(library);
Uri uri = library.canonicalUri;
if (uri == Uris.dart_html) {
_backendUsageBuilder.registerHtmlIsLoaded();
}
}
/// Called when the compiler starts running the codegen.
///
/// Returns the [CodegenInputs] objects with the needed data.
CodegenInputs onCodegenStart(
GlobalTypeInferenceResults globalTypeInferenceResults) {
JClosedWorld closedWorld = globalTypeInferenceResults.closedWorld;
RuntimeTypeTags rtiTags = const RuntimeTypeTags();
FixedNames fixedNames = compiler.options.enableMinification
? const MinifiedFixedNames()
: const FixedNames();
Tracer tracer = new Tracer(closedWorld, compiler.outputProvider);
RuntimeTypesEncoder rtiEncoder = new RuntimeTypesEncoderImpl(
rtiTags,
closedWorld.nativeData,
closedWorld.elementEnvironment,
closedWorld.commonElements,
closedWorld.rtiNeed);
RuntimeTypesSubstitutions rtiSubstitutions;
if (compiler.options.disableRtiOptimization) {
rtiSubstitutions = new TrivialRuntimeTypesSubstitutions(closedWorld);
_rtiChecksBuilder =
new TrivialRuntimeTypesChecksBuilder(closedWorld, rtiSubstitutions);
} else {
RuntimeTypesImpl runtimeTypesImpl = new RuntimeTypesImpl(closedWorld);
_rtiChecksBuilder = runtimeTypesImpl;
rtiSubstitutions = runtimeTypesImpl;
}
CodegenInputs codegen = new CodegenInputsImpl(
rtiSubstitutions, rtiEncoder, tracer, rtiTags, fixedNames);
functionCompiler.initialize(globalTypeInferenceResults, codegen);
return codegen;
}
/// Called before the compiler starts running the codegen enqueuer.
void _onCodegenEnqueuerStart(
GlobalTypeInferenceResults globalTypeInferenceResults,
CodegenInputs codegen,
OneShotInterceptorData oneShotInterceptorData) {
JClosedWorld closedWorld = globalTypeInferenceResults.closedWorld;
RuntimeTypeTags rtiTags = codegen.rtiTags;
FixedNames fixedNames = codegen.fixedNames;
_namer = compiler.options.enableMinification
? compiler.options.useFrequencyNamer
? new FrequencyBasedNamer(closedWorld, rtiTags, fixedNames)
: new MinifyNamer(closedWorld, rtiTags, fixedNames)
: new Namer(closedWorld, rtiTags, fixedNames);
_nativeCodegenEnqueuer = new 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 = new BackendImpacts(closedWorld.commonElements);
_codegenImpactTransformer = new CodegenImpactTransformer(
compiler.options,
closedWorld,
closedWorld.elementEnvironment,
closedWorld.commonElements,
impacts,
closedWorld.nativeData,
closedWorld.backendUsage,
closedWorld.rtiNeed,
nativeCodegenEnqueuer,
_namer,
oneShotInterceptorData,
rtiChecksBuilder,
emitterTask.nativeEmitter);
}
/// Called when code generation has been completed.
void onCodegenEnd(CodegenInputs codegen) {
sourceInformationStrategy.onComplete();
codegen.tracer.close();
}
/// Creates an impact strategy to use for compilation.
ImpactStrategy createImpactStrategy(
{bool supportDeferredLoad: true, bool supportDumpInfo: true}) {
return new JavaScriptImpactStrategy(
impactCacheDeleter, compiler.dumpInfoTask,
supportDeferredLoad: supportDeferredLoad,
supportDumpInfo: supportDumpInfo);
}
EnqueueTask makeEnqueuer() => new EnqueueTask(compiler);
}
class JavaScriptImpactStrategy extends ImpactStrategy {
final ImpactCacheDeleter impactCacheDeleter;
final DumpInfoTask dumpInfoTask;
final bool supportDeferredLoad;
final bool supportDumpInfo;
JavaScriptImpactStrategy(this.impactCacheDeleter, this.dumpInfoTask,
{this.supportDeferredLoad, this.supportDumpInfo});
@override
void visitImpact(var impactSource, WorldImpact impact,
WorldImpactVisitor visitor, ImpactUseCase impactUse) {
// TODO(johnniwinther): Compute the application strategy once for each use.
if (impactUse == ResolutionEnqueuer.IMPACT_USE) {
if (supportDeferredLoad) {
impact.apply(visitor);
} else {
impact.apply(visitor);
}
} else if (impactUse == DeferredLoadTask.IMPACT_USE) {
impact.apply(visitor);
// Impacts are uncached globally in [onImpactUsed].
} else if (impactUse == DumpInfoTask.IMPACT_USE) {
impact.apply(visitor);
dumpInfoTask.unregisterImpact(impactSource);
} else {
impact.apply(visitor);
}
}
@override
void onImpactUsed(ImpactUseCase impactUse) {
if (impactUse == DeferredLoadTask.IMPACT_USE) {
impactCacheDeleter.emptyCache();
}
}
}
/// Interface for resources only used during code generation.
abstract class CodegenInputs {
CheckedModeHelpers get checkedModeHelpers;
RuntimeTypesSubstitutions get rtiSubstitutions;
RuntimeTypesEncoder get rtiEncoder;
Tracer get tracer;
RuntimeTypeTags get rtiTags;
FixedNames get fixedNames;
}
class CodegenInputsImpl implements CodegenInputs {
@override
final CheckedModeHelpers checkedModeHelpers = new CheckedModeHelpers();
@override
final RuntimeTypesSubstitutions rtiSubstitutions;
@override
final RuntimeTypesEncoder rtiEncoder;
@override
final Tracer tracer;
@override
final RuntimeTypeTags rtiTags;
@override
final FixedNames fixedNames;
CodegenInputsImpl(this.rtiSubstitutions, this.rtiEncoder, this.tracer,
this.rtiTags, this.fixedNames);
}