blob: 7a6b935605d5a293a9c130b67bc5e69568fd1012 [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 'dart:async' show Future;
import 'package:js_runtime/shared/embedded_names.dart' as embeddedNames;
import '../common.dart';
import '../common/backend_api.dart'
show BackendClasses, ForeignResolver, NativeRegistry, ImpactTransformer;
import '../common/codegen.dart' show CodegenImpact, CodegenWorkItem;
import '../common/names.dart' show Uris;
import '../common/resolution.dart'
show Frontend, Resolution, ResolutionImpact, Target;
import '../common/tasks.dart' show CompilerTask;
import '../compiler.dart' show Compiler;
import '../constants/constant_system.dart';
import '../constants/expressions.dart';
import '../constants/values.dart';
import '../common_elements.dart' show CommonElements, ElementEnvironment;
import '../deferred_load.dart' show DeferredLoadTask;
import '../dump_info.dart' show DumpInfoTask;
import '../elements/elements.dart';
import '../elements/entities.dart';
import '../elements/resolution_types.dart';
import '../elements/types.dart';
import '../enqueue.dart'
show
DirectEnqueuerStrategy,
Enqueuer,
EnqueueTask,
ResolutionEnqueuer,
ResolutionWorkItemBuilder,
TreeShakingEnqueuerStrategy;
import '../io/multi_information.dart' show MultiSourceInformationStrategy;
import '../io/position_information.dart' show PositionSourceInformationStrategy;
import '../io/source_information.dart' show SourceInformationStrategy;
import '../io/start_end_information.dart'
show StartEndSourceInformationStrategy;
import '../js/js.dart' as jsAst;
import '../js/js.dart' show js;
import '../js/js_source_mapping.dart' show JavaScriptSourceInformationStrategy;
import '../js/rewrite_async.dart';
import '../js_emitter/js_emitter.dart' show CodeEmitterTask;
import '../kernel/task.dart';
import '../library_loader.dart' show LibraryLoader, LoadedLibraries;
import '../native/native.dart' as native;
import '../native/resolver.dart';
import '../ssa/ssa.dart' show SsaFunctionCompiler;
import '../tracer.dart';
import '../tree/tree.dart';
import '../types/types.dart';
import '../universe/call_structure.dart' show CallStructure;
import '../universe/selector.dart' show Selector;
import '../universe/world_builder.dart';
import '../universe/use.dart' show ConstantUse, StaticUse;
import '../universe/world_impact.dart'
show ImpactStrategy, ImpactUseCase, WorldImpact, WorldImpactVisitor;
import '../util/util.dart';
import '../world.dart' show ClosedWorld, ClosedWorldRefiner;
import 'annotations.dart';
import 'backend_helpers.dart';
import 'backend_impact.dart';
import 'backend_serialization.dart' show JavaScriptBackendSerialization;
import 'backend_usage.dart';
import 'checked_mode_helpers.dart';
import 'codegen_listener.dart';
import 'constant_handler_javascript.dart';
import 'custom_elements_analysis.dart';
import 'enqueuer.dart';
import 'impact_transformer.dart';
import 'interceptor_data.dart';
import 'js_interop_analysis.dart' show JsInteropAnalysis;
import 'lookup_map_analysis.dart'
show LookupMapResolutionAnalysis, LookupMapAnalysis;
import 'mirrors_analysis.dart';
import 'mirrors_data.dart';
import 'namer.dart';
import 'native_data.dart';
import 'no_such_method_registry.dart';
import 'patch_resolver.dart';
import 'resolution_listener.dart';
import 'type_variable_handler.dart';
part 'runtime_types.dart';
const VERBOSE_OPTIMIZER_HINTS = false;
abstract class FunctionCompiler {
/// Generates JavaScript code for `work.element`.
jsAst.Fun compile(CodegenWorkItem work, ClosedWorld closedWorld);
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;
static const int _mustInline = 5;
final Map<MethodElement, int> _cachedDecisions =
new Map<MethodElement, int>();
/// Returns the current cache decision. This should only be used for testing.
int getCurrentCacheDecisionForTesting(Element element) {
return _cachedDecisions[element];
}
// Returns `true`/`false` if we have a cached decision.
// Returns `null` otherwise.
bool canInline(MethodElement element, {bool insideLoop}) {
int decision = _cachedDecisions[element];
if (decision == null) {
// These synthetic elements are not yet present when we initially compute
// this cache from metadata annotations, so look for their parent.
if (element is ConstructorBodyElement) {
ConstructorBodyElement body = element;
decision = _cachedDecisions[body.constructor];
}
if (decision == null) {
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:
case _mustInline:
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:
case _mustInline:
return true;
}
}
// Quiet static checker.
return null;
}
void markAsInlinable(MethodElement element, {bool insideLoop}) {
int oldDecision = _cachedDecisions[element];
if (oldDecision == null) {
oldDecision = _unknown;
}
if (insideLoop) {
switch (oldDecision) {
case _mustNotInline:
throw new SpannableAssertionFailure(
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:
case _mustInline:
// Do nothing.
break;
}
} else {
switch (oldDecision) {
case _mustNotInline:
case _mayInlineInLoopMustNotOutside:
case _canInlineInLoopMustNotOutside:
throw new SpannableAssertionFailure(
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:
case _mustInline:
// Do nothing.
break;
}
}
}
void markAsNonInlinable(MethodElement element, {bool insideLoop: true}) {
int oldDecision = _cachedDecisions[element];
if (oldDecision == null) {
oldDecision = _unknown;
}
if (insideLoop) {
switch (oldDecision) {
case _canInlineInLoopMustNotOutside:
case _canInlineInLoopMayInlineOutside:
case _canInline:
case _mustInline:
throw new SpannableAssertionFailure(
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:
case _mustInline:
throw new SpannableAssertionFailure(
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 markAsMustInline(MethodElement element) {
_cachedDecisions[element] = _mustInline;
}
}
enum SyntheticConstantKind {
DUMMY_INTERCEPTOR,
EMPTY_VALUE,
TYPEVARIABLE_REFERENCE, // Reference to a type in reflection data.
NAME
}
class JavaScriptBackend {
final Compiler compiler;
String get patchVersion => emitter.patchVersion;
/// Returns true if the backend supports reflection.
bool get supportsReflection => emitter.supportsReflection;
final Annotations annotations;
/// Set of classes that need to be considered for reflection although not
/// otherwise visible during resolution.
Iterable<ClassElement> get classesRequiredForReflection {
// TODO(herhut): Clean this up when classes needed for rti are tracked.
return [helpers.closureClass, helpers.jsIndexableClass];
}
FunctionCompiler functionCompiler;
CodeEmitterTask emitter;
/**
* The generated code as a js AST for compiled methods.
*/
final Map<Element, jsAst.Expression> generatedCode =
<Element, jsAst.Expression>{};
FunctionInlineCache inlineCache = new FunctionInlineCache();
/// If [true], the compiler will emit code that logs whenever a method is
/// called. When TRACE_METHOD is 'console' this will be logged
/// directly in the JavaScript console. When TRACE_METHOD is 'post' the
/// information will be sent to a server via a POST request.
static const String TRACE_METHOD = const String.fromEnvironment('traceCalls');
static const bool TRACE_CALLS =
TRACE_METHOD == 'post' || TRACE_METHOD == 'console';
Namer _namer;
Namer get namer {
assert(invariant(NO_LOCATION_SPANNABLE, _namer != null,
message: "Namer has not been created yet."));
return _namer;
}
/**
* Set of classes whose `operator ==` methods handle `null` themselves.
*/
final Set<ClassElement> specialOperatorEqClasses = new Set<ClassElement>();
/**
* A set of members that are called from subclasses via `super`.
*/
final Set<MethodElement> aliasedSuperMembers = new Setlet<MethodElement>();
List<CompilerTask> get tasks {
List<CompilerTask> result = functionCompiler.tasks;
result.add(emitter);
result.add(patchResolverTask);
result.add(kernelTask);
return result;
}
final RuntimeTypesNeedBuilder _rtiNeedBuilder =
new _RuntimeTypesNeedBuilder();
RuntimeTypesNeed _rtiNeed;
final _RuntimeTypes _rti;
RuntimeTypesChecks _rtiChecks;
RuntimeTypesEncoder _rtiEncoder;
/// True if the html library has been loaded.
bool htmlLibraryIsLoaded = false;
/// Resolution analysis for tracking reflective access to type variables.
TypeVariableResolutionAnalysis _typeVariableResolutionAnalysis;
/// Codegen handler for reflective access to type variables.
TypeVariableCodegenAnalysis _typeVariableCodegenAnalysis;
/// 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;
/// Resolution support for tree-shaking entries of `LookupMap`.
LookupMapResolutionAnalysis lookupMapResolutionAnalysis;
/// Codegen support for tree-shaking entries of `LookupMap`.
LookupMapAnalysis _lookupMapAnalysis;
/// Codegen support for typed JavaScript interop.
JsInteropAnalysis jsInteropAnalysis;
/// Support for classifying `noSuchMethod` implementations.
NoSuchMethodRegistry noSuchMethodRegistry;
/// Resolution and codegen support for computing reflectable elements.
MirrorsAnalysis mirrorsAnalysis;
/// Builds kernel representation for the program.
KernelTask kernelTask;
/// The compiler task responsible for the compilation of constants for both
/// the frontend and the backend.
final JavaScriptConstantTask constantCompilerTask;
/// Backend transformation methods for the world impacts.
ImpactTransformer impactTransformer;
CodegenImpactTransformer _codegenImpactTransformer;
PatchResolverTask patchResolverTask;
/// The strategy used for collecting and emitting source information.
SourceInformationStrategy sourceInformationStrategy;
/// Interface for serialization of backend specific data.
JavaScriptBackendSerialization serialization;
NativeDataImpl _nativeData;
final NativeBasicDataImpl _nativeBaseData = new NativeBasicDataImpl();
NativeBasicData get nativeBaseData => _nativeBaseData;
NativeData get nativeData => _nativeData;
NativeBasicDataBuilder get nativeBaseDataBuilder => _nativeBaseData;
NativeDataBuilder get nativeDataBuilder => _nativeData;
final NativeDataResolver _nativeDataResolver;
InterceptorDataBuilder _interceptorDataBuilder;
InterceptorData _interceptorData;
OneShotInterceptorData _oneShotInterceptorData;
BackendUsage _backendUsage;
BackendUsageBuilder _backendUsageBuilder;
MirrorsDataImpl _mirrorsData;
CheckedModeHelpers _checkedModeHelpers;
native.NativeResolutionEnqueuer _nativeResolutionEnqueuer;
native.NativeCodegenEnqueuer _nativeCodegenEnqueuer;
BackendHelpers helpers;
BackendImpacts impacts;
/// Common classes used by the backend.
BackendClasses backendClasses;
/// Backend access to the front-end.
final JSFrontendAccess frontend;
Target _target;
Tracer tracer;
static SourceInformationStrategy createSourceInformationStrategy(
{bool generateSourceMap: false,
bool useMultiSourceInfo: false,
bool useNewSourceInfo: false}) {
if (!generateSourceMap) return const JavaScriptSourceInformationStrategy();
if (useMultiSourceInfo) {
if (useNewSourceInfo) {
return const MultiSourceInformationStrategy(const [
const PositionSourceInformationStrategy(),
const StartEndSourceInformationStrategy()
]);
} else {
return const MultiSourceInformationStrategy(const [
const StartEndSourceInformationStrategy(),
const PositionSourceInformationStrategy()
]);
}
} else if (useNewSourceInfo) {
return const PositionSourceInformationStrategy();
} else {
return const StartEndSourceInformationStrategy();
}
}
JavaScriptBackend(this.compiler,
{bool generateSourceMap: true,
bool useStartupEmitter: false,
bool useMultiSourceInfo: false,
bool useNewSourceInfo: false,
bool useKernel: false})
: _rti = new _RuntimeTypes(compiler),
annotations = new Annotations(compiler),
this.sourceInformationStrategy = createSourceInformationStrategy(
generateSourceMap: generateSourceMap,
useMultiSourceInfo: useMultiSourceInfo,
useNewSourceInfo: useNewSourceInfo),
frontend = new JSFrontendAccess(compiler),
constantCompilerTask = new JavaScriptConstantTask(compiler),
_nativeDataResolver = new NativeDataResolverImpl(compiler) {
_nativeData = new NativeDataImpl(nativeBaseData);
_target = new JavaScriptBackendTarget(this);
helpers = new BackendHelpers(compiler.elementEnvironment, commonElements);
impacts = new BackendImpacts(compiler.options, commonElements, helpers);
backendClasses = new JavaScriptBackendClasses(
compiler.elementEnvironment, helpers, nativeBaseData);
_mirrorsData = new MirrorsDataImpl(
compiler, compiler.options, commonElements, helpers, constants);
_backendUsageBuilder = new BackendUsageBuilderImpl(
compiler.elementEnvironment, commonElements, helpers);
_checkedModeHelpers = new CheckedModeHelpers(commonElements, helpers);
emitter =
new CodeEmitterTask(compiler, generateSourceMap, useStartupEmitter);
_nativeResolutionEnqueuer = new native.NativeResolutionEnqueuer(compiler);
_nativeCodegenEnqueuer = new native.NativeCodegenEnqueuer(
compiler, emitter, _nativeResolutionEnqueuer);
_typeVariableResolutionAnalysis = new TypeVariableResolutionAnalysis(
compiler.elementEnvironment, impacts, backendUsageBuilder);
customElementsResolutionAnalysis = new CustomElementsResolutionAnalysis(
compiler.resolution,
constantSystem,
commonElements,
backendClasses,
helpers,
nativeBaseData,
backendUsageBuilder);
customElementsCodegenAnalysis = new CustomElementsCodegenAnalysis(
compiler.resolution,
constantSystem,
commonElements,
backendClasses,
helpers,
nativeBaseData);
jsInteropAnalysis = new JsInteropAnalysis(this);
mirrorsAnalysis = new MirrorsAnalysis(this, compiler.resolution);
lookupMapResolutionAnalysis =
new LookupMapResolutionAnalysis(reporter, compiler.elementEnvironment);
noSuchMethodRegistry = new NoSuchMethodRegistry(this);
kernelTask = new KernelTask(compiler);
impactTransformer = new JavaScriptImpactTransformer(
compiler.options,
compiler.resolution,
compiler.elementEnvironment,
commonElements,
impacts,
nativeBaseData,
nativeResolutionEnqueuer,
backendUsageBuilder,
mirrorsDataBuilder,
customElementsResolutionAnalysis,
rtiNeedBuilder);
patchResolverTask = new PatchResolverTask(compiler);
functionCompiler =
new SsaFunctionCompiler(this, sourceInformationStrategy, useKernel);
serialization =
new JavaScriptBackendSerialization(nativeBaseData, nativeData);
_interceptorDataBuilder = new InterceptorDataBuilderImpl(
nativeBaseData, helpers, commonElements, compiler.resolution);
}
/// The [ConstantSystem] used to interpret compile-time constants for this
/// backend.
ConstantSystem get constantSystem => constants.constantSystem;
DiagnosticReporter get reporter => compiler.reporter;
CommonElements get commonElements => compiler.commonElements;
Resolution get resolution => compiler.resolution;
Target get target => _target;
/// Resolution analysis for tracking reflective access to type variables.
TypeVariableResolutionAnalysis get typeVariableResolutionAnalysis {
assert(invariant(NO_LOCATION_SPANNABLE, _typeVariableCodegenAnalysis == null,
message: "TypeVariableHandler has already been created."));
return _typeVariableResolutionAnalysis;
}
/// Codegen handler for reflective access to type variables.
TypeVariableCodegenAnalysis get typeVariableCodegenAnalysis {
assert(invariant(NO_LOCATION_SPANNABLE, _typeVariableCodegenAnalysis != null,
message: "TypeVariableHandler has not been created yet."));
return _typeVariableCodegenAnalysis;
}
MirrorsData get mirrorsData => _mirrorsData;
MirrorsDataBuilder get mirrorsDataBuilder => _mirrorsData;
/// Codegen support for tree-shaking entries of `LookupMap`.
LookupMapAnalysis get lookupMapAnalysis {
assert(invariant(NO_LOCATION_SPANNABLE, _lookupMapAnalysis != null,
message: "LookupMapAnalysis has not been created yet."));
return _lookupMapAnalysis;
}
InterceptorData get interceptorData {
assert(invariant(NO_LOCATION_SPANNABLE, _interceptorData != null,
message: "InterceptorData has not been computed yet."));
return _interceptorData;
}
InterceptorDataBuilder get interceptorDataBuilder {
assert(invariant(NO_LOCATION_SPANNABLE, _interceptorData == null,
message: "InterceptorData has already been computed."));
return _interceptorDataBuilder;
}
OneShotInterceptorData get oneShotInterceptorData {
assert(invariant(NO_LOCATION_SPANNABLE, _oneShotInterceptorData != null,
message: "OneShotInterceptorData has not been prepared yet."));
return _oneShotInterceptorData;
}
BackendUsage get backendUsage {
assert(invariant(NO_LOCATION_SPANNABLE, _backendUsage != null,
message: "BackendUsage has not been computed yet."));
return _backendUsage;
}
BackendUsageBuilder get backendUsageBuilder {
assert(invariant(NO_LOCATION_SPANNABLE, _backendUsage == null,
message: "BackendUsage has already been computed."));
return _backendUsageBuilder;
}
RuntimeTypesNeed get rtiNeed {
assert(invariant(NO_LOCATION_SPANNABLE, _rtiNeed != null,
message: "RuntimeTypesNeed has not been computed yet."));
return _rtiNeed;
}
RuntimeTypesNeedBuilder get rtiNeedBuilder {
assert(invariant(NO_LOCATION_SPANNABLE, _rtiNeed == null,
message: "RuntimeTypesNeed has already been computed."));
return _rtiNeedBuilder;
}
RuntimeTypesChecks get rtiChecks {
assert(invariant(NO_LOCATION_SPANNABLE, _rtiChecks != null,
message: "RuntimeTypesChecks has not been computed yet."));
return _rtiChecks;
}
RuntimeTypesChecksBuilder get rtiChecksBuilder {
assert(invariant(NO_LOCATION_SPANNABLE, _rtiChecks == null,
message: "RuntimeTypesChecks has already been computed."));
return _rti;
}
RuntimeTypesSubstitutions get rtiSubstitutions => _rti;
RuntimeTypesEncoder get rtiEncoder {
assert(invariant(NO_LOCATION_SPANNABLE, _rtiEncoder != null,
message: "RuntimeTypesEncoder has not been created."));
return _rtiEncoder;
}
CheckedModeHelpers get checkedModeHelpers => _checkedModeHelpers;
/// Returns constant environment for the JavaScript interpretation of the
/// constants.
JavaScriptConstantCompiler get constants {
return constantCompilerTask.jsConstantCompiler;
}
bool isDefaultNoSuchMethod(MethodElement element) {
return noSuchMethodRegistry.isDefaultNoSuchMethodImplementation(element);
}
MethodElement resolveExternalFunction(MethodElement element) {
if (isForeign(element)) {
return element;
}
if (_nativeDataResolver.isJsInteropMember(element)) {
if (element.memberName == const PublicName('[]') ||
element.memberName == const PublicName('[]=')) {
reporter.reportErrorMessage(
element, MessageKind.JS_INTEROP_INDEX_NOT_SUPPORTED);
}
return element;
}
return patchResolverTask.measure(() {
return patchResolverTask.resolveExternalFunction(element);
});
}
bool isForeign(Element element) => element.library == helpers.foreignLibrary;
bool isBackendLibrary(LibraryElement library) {
return library == helpers.interceptorsLibrary ||
library == helpers.jsHelperLibrary;
}
Namer determineNamer(
ClosedWorld closedWorld, CodegenWorldBuilder codegenWorldBuilder) {
return compiler.options.enableMinification
? compiler.options.useFrequencyNamer
? new FrequencyBasedNamer(
helpers, nativeData, closedWorld, codegenWorldBuilder)
: new MinifyNamer(
helpers, nativeData, closedWorld, codegenWorldBuilder)
: new Namer(helpers, nativeData, closedWorld, codegenWorldBuilder);
}
/// Returns true if global optimizations such as type inferencing can apply to
/// the field [element].
///
/// One category of elements that do not apply is runtime helpers that the
/// backend calls, but the optimizations don't see those calls.
bool canFieldBeUsedForGlobalOptimizations(FieldElement element) {
return !backendUsage.isFieldUsedByBackend(element) &&
!mirrorsData.invokedReflectively(element);
}
/// Returns true if global optimizations such as type inferencing can apply to
/// the parameter [element].
///
/// One category of elements that do not apply is runtime helpers that the
/// backend calls, but the optimizations don't see those calls.
bool canFunctionParametersBeUsedForGlobalOptimizations(
FunctionElement element) {
if (element.isLocal) return true;
MethodElement method = element;
return !backendUsage.isFunctionUsedByBackend(method) &&
!mirrorsData.invokedReflectively(method);
}
/**
* Record that [method] is called from a subclass via `super`.
*/
bool maybeRegisterAliasedSuperMember(
MemberElement member, Selector selector) {
if (!canUseAliasedSuperMember(member, selector)) {
// Invoking a super getter isn't supported, this would require changes to
// compact field descriptors in the emitter.
return false;
}
aliasedSuperMembers.add(member);
return true;
}
bool canUseAliasedSuperMember(Element member, Selector selector) {
return !selector.isGetter;
}
/**
* Returns `true` if [member] is called from a subclass via `super`.
*/
bool isAliasedSuperMember(FunctionElement member) {
return aliasedSuperMembers.contains(member);
}
/// Maps compile-time classes to their runtime class. The runtime class is
/// always a superclass or the class itself.
ClassElement getRuntimeClass(ClassElement class_) {
if (class_.isSubclassOf(helpers.jsIntClass)) return helpers.jsIntClass;
if (class_.isSubclassOf(helpers.jsArrayClass)) return helpers.jsArrayClass;
return class_;
}
bool operatorEqHandlesNullArgument(FunctionElement operatorEqfunction) {
return specialOperatorEqClasses.contains(operatorEqfunction.enclosingClass);
}
void validateInterceptorImplementsAllObjectMethods(
ClassElement interceptorClass) {
if (interceptorClass == null) return;
interceptorClass.ensureResolved(resolution);
ClassElement objectClass = commonElements.objectClass;
objectClass.forEachMember((_, Element member) {
if (member.isGenerativeConstructor) return;
Element interceptorMember = interceptorClass.lookupMember(member.name);
// Interceptors must override all Object methods due to calling convention
// differences.
assert(invariant(interceptorMember,
interceptorMember.enclosingClass == interceptorClass,
message:
"Member ${member.name} not overridden in ${interceptorClass}. "
"Found $interceptorMember from "
"${interceptorMember.enclosingClass}."));
});
}
void onResolutionStart(ResolutionEnqueuer enqueuer) {
helpers.onResolutionStart();
validateInterceptorImplementsAllObjectMethods(helpers.jsInterceptorClass);
// The null-interceptor must also implement *all* methods.
validateInterceptorImplementsAllObjectMethods(helpers.jsNullClass);
}
void onResolutionComplete(
ClosedWorld closedWorld, ClosedWorldRefiner closedWorldRefiner) {
for (Entity entity in compiler.enqueuer.resolution.processedEntities) {
processAnnotations(entity, closedWorldRefiner);
}
mirrorsDataBuilder.computeMembersNeededForReflection(
compiler.enqueuer.resolution.worldBuilder, closedWorld);
_backendUsage = _backendUsageBuilder.close();
_rtiNeed = rtiNeedBuilder.computeRuntimeTypesNeed(
compiler.enqueuer.resolution.worldBuilder,
closedWorld,
compiler.types,
commonElements,
helpers,
_backendUsage,
enableTypeAssertions: compiler.options.enableTypeAssertions);
_interceptorData =
_interceptorDataBuilder.onResolutionComplete(closedWorld);
_oneShotInterceptorData =
new OneShotInterceptorData(interceptorData, helpers);
mirrorsAnalysis.onResolutionComplete();
}
void onTypeInferenceComplete() {
noSuchMethodRegistry.onTypeInferenceComplete();
}
/// Register a runtime type variable bound tests between [typeArgument] and
/// [bound].
void registerTypeVariableBoundsSubtypeCheck(
ResolutionDartType typeArgument, ResolutionDartType bound) {
rtiChecksBuilder.registerTypeVariableBoundsSubtypeCheck(
typeArgument, bound);
}
/// Returns the [WorldImpact] of enabling deferred loading.
WorldImpact computeDeferredLoadingImpact() {
backendUsageBuilder.processBackendImpact(impacts.deferredLoading);
return impacts.deferredLoading.createImpact(compiler.elementEnvironment);
}
/// Called when resolving a call to a foreign function.
native.NativeBehavior resolveForeignCall(Send node, Element element,
CallStructure callStructure, ForeignResolver resolver) {
if (element.name == BackendHelpers.JS) {
return _nativeResolutionEnqueuer.resolveJsCall(node, resolver);
} else if (element.name == BackendHelpers.JS_EMBEDDED_GLOBAL) {
return _nativeResolutionEnqueuer.resolveJsEmbeddedGlobalCall(
node, resolver);
} else if (element.name == BackendHelpers.JS_BUILTIN) {
return _nativeResolutionEnqueuer.resolveJsBuiltinCall(node, resolver);
} else if (element.name == BackendHelpers.JS_INTERCEPTOR_CONSTANT) {
// The type constant that is an argument to JS_INTERCEPTOR_CONSTANT names
// a class that will be instantiated outside the program by attaching a
// native class dispatch record referencing the interceptor.
if (!node.argumentsNode.isEmpty) {
Node argument = node.argumentsNode.nodes.head;
ConstantExpression constant = resolver.getConstant(argument);
if (constant != null && constant.kind == ConstantExpressionKind.TYPE) {
TypeConstantExpression typeConstant = constant;
if (typeConstant.type is ResolutionInterfaceType) {
resolver.registerInstantiatedType(typeConstant.type);
// No native behavior for this call.
return null;
}
}
}
reporter.reportErrorMessage(
node, MessageKind.WRONG_ARGUMENT_FOR_JS_INTERCEPTOR_CONSTANT);
}
// No native behavior for this call.
return null;
}
bool isComplexNoSuchMethod(FunctionElement element) =>
noSuchMethodRegistry.isComplex(element);
ResolutionEnqueuer createResolutionEnqueuer(
CompilerTask task, Compiler compiler) {
return new ResolutionEnqueuer(
task,
compiler.options,
compiler.reporter,
compiler.options.analyzeOnly && compiler.options.analyzeMain
? const DirectEnqueuerStrategy()
: const TreeShakingEnqueuerStrategy(),
new ResolutionEnqueuerListener(
kernelTask,
compiler.options,
compiler.elementEnvironment,
commonElements,
helpers,
impacts,
backendClasses,
nativeBaseData,
_interceptorDataBuilder,
_backendUsageBuilder,
_rtiNeedBuilder,
mirrorsDataBuilder,
noSuchMethodRegistry,
customElementsResolutionAnalysis,
lookupMapResolutionAnalysis,
mirrorsAnalysis,
typeVariableResolutionAnalysis,
_nativeResolutionEnqueuer),
new ElementResolutionWorldBuilder(
this, compiler.resolution, const OpenWorldStrategy()),
new ResolutionWorkItemBuilder(compiler.resolution));
}
/// Creates an [Enqueuer] for code generation specific to this backend.
CodegenEnqueuer createCodegenEnqueuer(
CompilerTask task, Compiler compiler, ClosedWorld closedWorld) {
_typeVariableCodegenAnalysis = new TypeVariableCodegenAnalysis(this, helpers, mirrorsData);
_lookupMapAnalysis = new LookupMapAnalysis(
reporter,
constantSystem,
constants,
compiler.elementEnvironment,
commonElements,
helpers,
backendClasses,
lookupMapResolutionAnalysis);
return new CodegenEnqueuer(
task,
compiler.options,
const TreeShakingEnqueuerStrategy(),
new CodegenWorldBuilderImpl(
nativeBaseData, closedWorld, constants, const TypeMaskStrategy()),
new CodegenWorkItemBuilder(this, compiler.options),
new CodegenEnqueuerListener(
compiler.elementEnvironment,
commonElements,
helpers,
impacts,
backendClasses,
backendUsage,
rtiNeed,
customElementsCodegenAnalysis,
typeVariableCodegenAnalysis,
lookupMapAnalysis,
mirrorsAnalysis,
_nativeCodegenEnqueuer));
}
WorldImpact codegen(CodegenWorkItem work) {
Element element = work.element;
if (compiler.elementHasCompileTimeError(element)) {
DiagnosticMessage message =
// If there's more than one error, the first is probably most
// informative, as the following errors may be side-effects of the
// first error.
compiler.elementsWithCompileTimeErrors[element].first;
String messageText = message.message.computeMessage();
jsAst.LiteralString messageLiteral =
js.escapedString("Compile time error in $element: $messageText");
generatedCode[element] =
js("function () { throw new Error(#); }", [messageLiteral]);
return const CodegenImpact();
}
var kind = element.kind;
if (kind == ElementKind.TYPEDEF) {
return const WorldImpact();
}
if (element.isConstructor &&
element.enclosingClass == helpers.jsNullClass) {
// Work around a problem compiling JSNull's constructor.
return const CodegenImpact();
}
if (kind.category == ElementCategory.VARIABLE) {
VariableElement variableElement = element;
ConstantExpression constant = variableElement.constant;
if (constant != null) {
ConstantValue initialValue = constants.getConstantValue(constant);
if (initialValue != null) {
work.registry.worldImpact
.registerConstantUse(new ConstantUse.init(initialValue));
// We don't need to generate code for static or top-level
// variables. For instance variables, we may need to generate
// the checked setter.
if (Elements.isStaticOrTopLevel(element)) {
return _codegenImpactTransformer
.transformCodegenImpact(work.registry.worldImpact);
}
} else {
assert(invariant(
variableElement,
variableElement.isInstanceMember ||
constant.isImplicit ||
constant.isPotential,
message: "Constant expression without value: "
"${constant.toStructuredText()}."));
}
} else {
// If the constant-handler was not able to produce a result we have to
// go through the builder (below) to generate the lazy initializer for
// the static variable.
// We also need to register the use of the cyclic-error helper.
work.registry.worldImpact.registerStaticUse(new StaticUse.staticInvoke(
helpers.cyclicThrowHelper, CallStructure.ONE_ARG));
}
}
jsAst.Fun function = functionCompiler.compile(work, _closedWorld);
if (function.sourceInformation == null) {
function = function.withSourceInformation(
sourceInformationStrategy.buildSourceMappedMarker());
}
generatedCode[element] = function;
WorldImpact worldImpact = _codegenImpactTransformer
.transformCodegenImpact(work.registry.worldImpact);
compiler.dumpInfoTask.registerImpact(element, worldImpact);
return worldImpact;
}
native.NativeEnqueuer get nativeResolutionEnqueuer =>
_nativeResolutionEnqueuer;
native.NativeEnqueuer get nativeCodegenEnqueuer => _nativeCodegenEnqueuer;
ClassElement defaultSuperclass(ClassElement element) {
if (nativeBaseData.isJsInteropClass(element)) {
return helpers.jsJavaScriptObjectClass;
}
// Native classes inherit from Interceptor.
return nativeBaseData.isNativeClass(element)
? helpers.jsInterceptorClass
: commonElements.objectClass;
}
/**
* Unit test hook that returns code of an element as a String.
*
* Invariant: [element] must be a declaration element.
*/
String getGeneratedCode(Element element) {
assert(invariant(element, element.isDeclaration));
return jsAst.prettyPrint(generatedCode[element], compiler);
}
/// Called to finalize the [RuntimeTypesChecks] information.
void finalizeRti() {
_rtiChecks = rtiChecksBuilder.computeRequiredChecks();
}
/// Generates the output and returns the total size of the generated code.
int assembleProgram(ClosedWorld closedWorld) {
int programSize = emitter.assembleProgram(namer, closedWorld);
noSuchMethodRegistry.emitDiagnostic();
int totalMethodCount = generatedCode.length;
if (totalMethodCount != mirrorsAnalysis.preMirrorsMethodCount) {
int mirrorCount =
totalMethodCount - mirrorsAnalysis.preMirrorsMethodCount;
double percentage = (mirrorCount / totalMethodCount) * 100;
DiagnosticMessage hint =
reporter.createMessage(compiler.mainApp, MessageKind.MIRROR_BLOAT, {
'count': mirrorCount,
'total': totalMethodCount,
'percentage': percentage.round()
});
List<DiagnosticMessage> infos = <DiagnosticMessage>[];
for (LibraryElement library in compiler.libraryLoader.libraries) {
if (library.isInternalLibrary) continue;
for (ImportElement import in library.imports) {
LibraryElement importedLibrary = import.importedLibrary;
if (importedLibrary != compiler.commonElements.mirrorsLibrary)
continue;
MessageKind kind =
compiler.mirrorUsageAnalyzerTask.hasMirrorUsage(library)
? MessageKind.MIRROR_IMPORT
: MessageKind.MIRROR_IMPORT_NO_USAGE;
reporter.withCurrentElement(library, () {
infos.add(reporter.createMessage(import, kind));
});
}
}
reporter.reportHint(hint, infos);
}
return programSize;
}
/**
* Returns [:true:] if the checking of [type] is performed directly on the
* object and not on an interceptor.
*/
bool hasDirectCheckFor(ResolutionDartType type) {
Element element = type.element;
return element == commonElements.stringClass ||
element == commonElements.boolClass ||
element == commonElements.numClass ||
element == commonElements.intClass ||
element == commonElements.doubleClass ||
element == helpers.jsArrayClass ||
element == helpers.jsMutableArrayClass ||
element == helpers.jsExtendableArrayClass ||
element == helpers.jsFixedArrayClass ||
element == helpers.jsUnmodifiableArrayClass;
}
/// This method is called immediately after the [library] and its parts have
/// been scanned.
Future onLibraryScanned(LibraryElement library, LibraryLoader loader) {
if (!compiler.serialization.isDeserialized(library)) {
if (canLibraryUseNative(library)) {
library.forEachLocalMember((Element element) {
if (element.isClass) {
checkNativeAnnotation(compiler, element, nativeBaseDataBuilder);
}
});
}
checkJsInteropClassAnnotations(compiler, library, nativeBaseDataBuilder);
}
if (library.isPlatformLibrary &&
// Don't patch library currently disallowed.
!library.isSynthesized &&
!library.isPatched &&
// Don't patch deserialized libraries.
!compiler.serialization.isDeserialized(library)) {
// Apply patch, if any.
Uri patchUri = compiler.resolvePatchUri(library.canonicalUri.path);
if (patchUri != null) {
return compiler.patchParser.patchLibrary(loader, patchUri, library);
}
}
Uri uri = library.canonicalUri;
if (uri == Uris.dart_html) {
htmlLibraryIsLoaded = true;
} else if (uri == LookupMapResolutionAnalysis.PACKAGE_LOOKUP_MAP) {
lookupMapResolutionAnalysis.init(library);
}
annotations.onLibraryScanned(library);
return new Future.value();
}
/// This method is called when all new libraries loaded through
/// [LibraryLoader.loadLibrary] has been loaded and their imports/exports
/// have been computed.
Future onLibrariesLoaded(LoadedLibraries loadedLibraries) {
if (!loadedLibraries.containsLibrary(Uris.dart_core)) {
return new Future.value();
}
helpers.onLibrariesLoaded(loadedLibraries);
// These methods are overwritten with generated versions.
inlineCache.markAsNonInlinable(helpers.getInterceptorMethod,
insideLoop: true);
specialOperatorEqClasses
..add(commonElements.objectClass)
..add(helpers.jsInterceptorClass)
..add(helpers.jsNullClass);
return new Future.value();
}
jsAst.Call generateIsJsIndexableCall(
jsAst.Expression use1, jsAst.Expression use2) {
String dispatchPropertyName = embeddedNames.DISPATCH_PROPERTY_NAME;
jsAst.Expression dispatchProperty =
emitter.generateEmbeddedGlobalAccess(dispatchPropertyName);
// We pass the dispatch property record to the isJsIndexable
// helper rather than reading it inside the helper to increase the
// chance of making the dispatch record access monomorphic.
jsAst.PropertyAccess record =
new jsAst.PropertyAccess(use2, dispatchProperty);
List<jsAst.Expression> arguments = <jsAst.Expression>[use1, record];
MethodElement helper = helpers.isJsIndexable;
jsAst.Expression helperExpression = emitter.staticFunctionAccess(helper);
return new jsAst.Call(helperExpression, arguments);
}
/// Called after the queue is closed. [onQueueEmpty] may be called multiple
/// times, but [onQueueClosed] is only called once.
void onQueueClosed() {
jsInteropAnalysis.onQueueClosed();
}
// TODO(johnniwinther): Create a CodegenPhase object for the backend to hold
// data only available during code generation.
ClosedWorld _closedWorldCache;
ClosedWorld get _closedWorld {
assert(invariant(NO_LOCATION_SPANNABLE, _closedWorldCache != null,
message: "ClosedWorld has not be set yet."));
return _closedWorldCache;
}
void set _closedWorld(ClosedWorld value) {
_closedWorldCache = value;
}
/// Called when the compiler starts running the codegen enqueuer. The
/// [WorldImpact] of enabled backend features is returned.
WorldImpact onCodegenStart(
ClosedWorld closedWorld, CodegenWorldBuilder codegenWorldBuilder) {
_closedWorld = closedWorld;
_namer = determineNamer(closedWorld, codegenWorldBuilder);
tracer = new Tracer(closedWorld, namer, compiler);
emitter.createEmitter(namer, closedWorld);
_rtiEncoder =
_namer.rtiEncoder = new _RuntimeTypesEncoder(namer, emitter, helpers);
_codegenImpactTransformer = new CodegenImpactTransformer(
compiler.options,
compiler.elementEnvironment,
helpers,
impacts,
checkedModeHelpers,
nativeData,
backendUsage,
rtiNeed,
nativeCodegenEnqueuer,
namer,
oneShotInterceptorData,
lookupMapAnalysis,
rtiChecksBuilder);
return const WorldImpact();
}
/// Called when code generation has been completed.
void onCodegenEnd() {
sourceInformationStrategy.onComplete();
tracer.close();
}
// Does this element belong in the output
bool shouldOutput(Element element) => true;
/// Returns `true` if the `native` pseudo keyword is supported for [library].
bool canLibraryUseNative(LibraryElement library) {
return native.maybeEnableNative(compiler, library);
}
bool isTargetSpecificLibrary(LibraryElement library) {
Uri canonicalUri = library.canonicalUri;
if (canonicalUri == BackendHelpers.DART_JS_HELPER ||
canonicalUri == BackendHelpers.DART_INTERCEPTORS) {
return true;
}
return false;
}
/// Process backend specific annotations.
void processAnnotations(
Element element, ClosedWorldRefiner closedWorldRefiner) {
if (element.isMalformed) {
// Elements that are marked as malformed during parsing or resolution
// might be registered here. These should just be ignored.
return;
}
Element implementation = element.implementation;
if (element.isFunction || element.isConstructor) {
if (annotations.noInline(implementation)) {
inlineCache.markAsNonInlinable(implementation);
}
}
LibraryElement library = element.library;
if (!library.isPlatformLibrary && !canLibraryUseNative(library)) return;
bool hasNoInline = false;
bool hasForceInline = false;
bool hasNoThrows = false;
bool hasNoSideEffects = false;
for (MetadataAnnotation metadata in element.implementation.metadata) {
metadata.ensureResolved(resolution);
ConstantValue constantValue =
compiler.constants.getConstantValue(metadata.constant);
if (!constantValue.isConstructedObject) continue;
ObjectConstantValue value = constantValue;
ClassElement cls = value.type.element;
if (cls == helpers.forceInlineClass) {
hasForceInline = true;
if (VERBOSE_OPTIMIZER_HINTS) {
reporter.reportHintMessage(
element, MessageKind.GENERIC, {'text': "Must inline"});
}
inlineCache.markAsMustInline(element);
} else if (cls == helpers.noInlineClass) {
hasNoInline = true;
if (VERBOSE_OPTIMIZER_HINTS) {
reporter.reportHintMessage(
element, MessageKind.GENERIC, {'text': "Cannot inline"});
}
inlineCache.markAsNonInlinable(element);
} else if (cls == helpers.noThrowsClass) {
hasNoThrows = true;
if (!Elements.isStaticOrTopLevelFunction(element) &&
!element.isFactoryConstructor) {
reporter.internalError(
element,
"@NoThrows() is currently limited to top-level"
" or static functions and factory constructors.");
}
if (VERBOSE_OPTIMIZER_HINTS) {
reporter.reportHintMessage(
element, MessageKind.GENERIC, {'text': "Cannot throw"});
}
closedWorldRefiner.registerCannotThrow(element);
} else if (cls == helpers.noSideEffectsClass) {
hasNoSideEffects = true;
if (VERBOSE_OPTIMIZER_HINTS) {
reporter.reportHintMessage(
element, MessageKind.GENERIC, {'text': "Has no side effects"});
}
closedWorldRefiner.registerSideEffectsFree(element);
}
}
if (hasForceInline && hasNoInline) {
reporter.internalError(
element, "@ForceInline() must not be used with @NoInline.");
}
if (hasNoThrows && !hasNoInline) {
reporter.internalError(
element, "@NoThrows() should always be combined with @NoInline.");
}
if (hasNoSideEffects && !hasNoInline) {
reporter.internalError(element,
"@NoSideEffects() should always be combined with @NoInline.");
}
}
MethodElement helperForBadMain() => helpers.badMain;
MethodElement helperForMissingMain() => helpers.missingMain;
MethodElement helperForMainArity() => helpers.mainHasTooManyParameters;
/// Returns the filename for the output-unit named [name].
///
/// The filename is of the form "<main output file>_<name>.part.js".
/// If [addExtension] is false, the ".part.js" suffix is left out.
String deferredPartFileName(String name, {bool addExtension: true}) {
assert(name != "");
String outPath = compiler.options.outputUri != null
? compiler.options.outputUri.path
: "out";
String outName = outPath.substring(outPath.lastIndexOf('/') + 1);
String extension = addExtension ? ".part.js" : "";
return "${outName}_$name$extension";
}
/// Enable deferred loading. Returns `true` if the backend supports deferred
/// loading.
bool enableDeferredLoadingIfSupported(Spannable node) => true;
/// Enable compilation of code with compile time errors. Returns `true` if
/// supported by the backend.
bool enableCodegenWithErrorsIfSupported(Spannable node) => true;
jsAst.Expression rewriteAsync(
FunctionElement element, jsAst.Expression code) {
AsyncRewriterBase rewriter = null;
jsAst.Name name = namer.methodPropertyName(element);
switch (element.asyncMarker) {
case AsyncMarker.ASYNC:
rewriter = new AsyncRewriter(reporter, element,
asyncHelper: emitter.staticFunctionAccess(helpers.asyncHelper),
wrapBody: emitter.staticFunctionAccess(helpers.wrapBody),
newCompleter:
emitter.staticFunctionAccess(helpers.syncCompleterConstructor),
safeVariableName: namer.safeVariablePrefixForAsyncRewrite,
bodyName: namer.deriveAsyncBodyName(name));
break;
case AsyncMarker.SYNC_STAR:
rewriter = new SyncStarRewriter(reporter, element,
endOfIteration:
emitter.staticFunctionAccess(helpers.endOfIteration),
newIterable: emitter
.staticFunctionAccess(helpers.syncStarIterableConstructor),
yieldStarExpression:
emitter.staticFunctionAccess(helpers.yieldStar),
uncaughtErrorExpression:
emitter.staticFunctionAccess(helpers.syncStarUncaughtError),
safeVariableName: namer.safeVariablePrefixForAsyncRewrite,
bodyName: namer.deriveAsyncBodyName(name));
break;
case AsyncMarker.ASYNC_STAR:
rewriter = new AsyncStarRewriter(reporter, element,
asyncStarHelper:
emitter.staticFunctionAccess(helpers.asyncStarHelper),
streamOfController:
emitter.staticFunctionAccess(helpers.streamOfController),
wrapBody: emitter.staticFunctionAccess(helpers.wrapBody),
newController: emitter
.staticFunctionAccess(helpers.asyncStarControllerConstructor),
safeVariableName: namer.safeVariablePrefixForAsyncRewrite,
yieldExpression: emitter.staticFunctionAccess(helpers.yieldSingle),
yieldStarExpression:
emitter.staticFunctionAccess(helpers.yieldStar),
bodyName: namer.deriveAsyncBodyName(name));
break;
default:
assert(element.asyncMarker == AsyncMarker.SYNC);
return code;
}
return rewriter.rewrite(code);
}
/// The locations of js patch-files relative to the sdk-descriptors.
static const _patchLocations = const <String, String>{
"async": "_internal/js_runtime/lib/async_patch.dart",
"collection": "_internal/js_runtime/lib/collection_patch.dart",
"convert": "_internal/js_runtime/lib/convert_patch.dart",
"core": "_internal/js_runtime/lib/core_patch.dart",
"developer": "_internal/js_runtime/lib/developer_patch.dart",
"io": "_internal/js_runtime/lib/io_patch.dart",
"isolate": "_internal/js_runtime/lib/isolate_patch.dart",
"math": "_internal/js_runtime/lib/math_patch.dart",
"mirrors": "_internal/js_runtime/lib/mirrors_patch.dart",
"typed_data": "_internal/js_runtime/lib/typed_data_patch.dart",
"_internal": "_internal/js_runtime/lib/internal_patch.dart"
};
/// Returns the location of the patch-file associated with [libraryName]
/// resolved from [plaformConfigUri].
///
/// Returns null if there is none.
Uri resolvePatchUri(String libraryName, Uri platformConfigUri) {
String patchLocation = _patchLocations[libraryName];
if (patchLocation == null) return null;
return platformConfigUri.resolve(patchLocation);
}
/// Creates an impact strategy to use for compilation.
ImpactStrategy createImpactStrategy(
{bool supportDeferredLoad: true,
bool supportDumpInfo: true,
bool supportSerialization: true}) {
return new JavaScriptImpactStrategy(resolution, compiler.dumpInfoTask,
supportDeferredLoad: supportDeferredLoad,
supportDumpInfo: supportDumpInfo,
supportSerialization: supportSerialization);
}
EnqueueTask makeEnqueuer() => new EnqueueTask(compiler);
}
class JSFrontendAccess implements Frontend {
final Compiler compiler;
JSFrontendAccess(this.compiler);
Resolution get resolution => compiler.resolution;
@override
ResolutionImpact getResolutionImpact(Element element) {
return resolution.getResolutionImpact(element);
}
}
class JavaScriptImpactStrategy extends ImpactStrategy {
final Resolution resolution;
final DumpInfoTask dumpInfoTask;
final bool supportDeferredLoad;
final bool supportDumpInfo;
final bool supportSerialization;
JavaScriptImpactStrategy(this.resolution, this.dumpInfoTask,
{this.supportDeferredLoad,
this.supportDumpInfo,
this.supportSerialization});
@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 || supportSerialization) {
impact.apply(visitor);
} else {
impact.apply(visitor);
if (impactSource is Element) {
resolution.uncacheWorldImpact(impactSource);
}
}
} 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 && !supportSerialization) {
// TODO(johnniwinther): Allow emptying when serialization has been
// performed.
resolution.emptyCache();
}
}
}
class JavaScriptBackendClasses implements BackendClasses {
final ElementEnvironment _env;
final BackendHelpers helpers;
final NativeBasicData _nativeData;
JavaScriptBackendClasses(this._env, this.helpers, this._nativeData);
ClassElement get intClass => helpers.jsIntClass;
ClassElement get uint32Class => helpers.jsUInt32Class;
ClassElement get uint31Class => helpers.jsUInt31Class;
ClassElement get positiveIntClass => helpers.jsPositiveIntClass;
ClassElement get doubleClass => helpers.jsDoubleClass;
ClassElement get numClass => helpers.jsNumberClass;
ClassElement get stringClass => helpers.jsStringClass;
ClassElement get listClass => helpers.jsArrayClass;
ClassElement get mutableListClass => helpers.jsMutableArrayClass;
ClassElement get constListClass => helpers.jsUnmodifiableArrayClass;
ClassElement get fixedListClass => helpers.jsFixedArrayClass;
ClassElement get growableListClass => helpers.jsExtendableArrayClass;
ClassElement get mapClass => helpers.mapLiteralClass;
ClassElement get constMapClass => helpers.constMapLiteralClass;
ClassElement get typeClass => helpers.typeLiteralClass;
InterfaceType get typeType => _env.getRawType(typeClass);
ClassElement get boolClass => helpers.jsBoolClass;
ClassElement get nullClass => helpers.jsNullClass;
ClassElement get syncStarIterableClass => helpers.syncStarIterable;
ClassElement get asyncFutureClass => helpers.futureImplementation;
ClassElement get asyncStarStreamClass => helpers.controllerStream;
ClassElement get functionClass => helpers.commonElements.functionClass;
ClassElement get indexableClass => helpers.jsIndexableClass;
ClassElement get mutableIndexableClass => helpers.jsMutableIndexableClass;
ClassElement get indexingBehaviorClass => helpers.jsIndexingBehaviorInterface;
ClassElement get interceptorClass => helpers.jsInterceptorClass;
bool isDefaultEqualityImplementation(MemberElement element) {
assert(element.name == '==');
ClassElement classElement = element.enclosingClass;
return classElement == helpers.commonElements.objectClass ||
classElement == helpers.jsInterceptorClass ||
classElement == helpers.jsNullClass;
}
@override
bool isNativeClass(ClassEntity element) {
return _nativeData.isNativeClass(element);
}
InterfaceType getConstantMapTypeFor(InterfaceType sourceType,
{bool hasProtoKey: false, bool onlyStringKeys: false}) {
ClassElement classElement = onlyStringKeys
? (hasProtoKey
? helpers.constantProtoMapClass
: helpers.constantStringMapClass)
: helpers.generalConstantMapClass;
List<DartType> typeArgument = sourceType.typeArguments;
if (sourceType.treatAsRaw) {
return _env.getRawType(classElement);
} else {
return _env.createInterfaceType(classElement, typeArgument);
}
}
@override
FieldEntity get symbolField => helpers.symbolImplementationField;
@override
InterfaceType get symbolType {
return _env.getRawType(helpers.symbolImplementationClass);
}
}
class JavaScriptBackendTarget extends Target {
final JavaScriptBackend _backend;
JavaScriptBackendTarget(this._backend);
@override
bool isTargetSpecificLibrary(LibraryElement element) {
return _backend.isTargetSpecificLibrary(element);
}
@override
void resolveNativeMember(MemberElement element, NativeRegistry registry) {
return _backend._nativeDataResolver.resolveNativeMember(element, registry);
}
@override
MethodElement resolveExternalFunction(MethodElement element) {
return _backend.resolveExternalFunction(element);
}
@override
dynamic resolveForeignCall(Send node, Element element,
CallStructure callStructure, ForeignResolver resolver) {
return _backend.resolveForeignCall(node, element, callStructure, resolver);
}
@override
bool isDefaultNoSuchMethod(MethodElement element) {
return _backend.isDefaultNoSuchMethod(element);
}
@override
ClassElement defaultSuperclass(ClassElement element) {
return _backend.defaultSuperclass(element);
}
@override
bool isNativeClass(ClassEntity element) =>
_backend.nativeBaseData.isNativeClass(element);
@override
bool isForeign(Element element) => _backend.isForeign(element);
}