blob: c1b2cac3cca9c0515e29e3449776909951c26c23 [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.
library js_backend.backend.resolution_listener;
import '../common/names.dart' show Identifiers;
import '../common_elements.dart' show CommonElements, ElementEnvironment;
import '../constants/values.dart';
import '../deferred_load.dart';
import '../elements/entities.dart';
import '../elements/types.dart';
import '../enqueue.dart' show Enqueuer, EnqueuerListener;
import '../native/enqueue.dart';
import '../options.dart' show CompilerOptions;
import '../universe/call_structure.dart' show CallStructure;
import '../universe/use.dart' show StaticUse, TypeUse;
import '../universe/world_impact.dart'
show WorldImpact, WorldImpactBuilder, WorldImpactBuilderImpl;
import 'allocator_analysis.dart';
import 'backend.dart';
import 'backend_impact.dart';
import 'backend_usage.dart';
import 'checked_mode_helpers.dart';
import 'custom_elements_analysis.dart';
import 'interceptor_data.dart';
import 'native_data.dart' show NativeBasicData;
import 'no_such_method_registry.dart';
class ResolutionEnqueuerListener extends EnqueuerListener {
// TODO(johnniwinther): Avoid the need for this.
final DeferredLoadTask _deferredLoadTask;
final CompilerOptions _options;
final ElementEnvironment _elementEnvironment;
final CommonElements _commonElements;
final BackendImpacts _impacts;
final NativeBasicData _nativeData;
final InterceptorDataBuilder _interceptorData;
final BackendUsageBuilder _backendUsage;
final NoSuchMethodRegistry _noSuchMethodRegistry;
final CustomElementsResolutionAnalysis _customElementsAnalysis;
final NativeResolutionEnqueuer _nativeEnqueuer;
final KAllocatorAnalysis _allocatorAnalysis;
/// True when we enqueue the loadLibrary code.
bool _isLoadLibraryFunctionResolved = false;
ResolutionEnqueuerListener(
this._options,
this._elementEnvironment,
this._commonElements,
this._impacts,
this._nativeData,
this._interceptorData,
this._backendUsage,
this._noSuchMethodRegistry,
this._customElementsAnalysis,
this._nativeEnqueuer,
this._allocatorAnalysis,
this._deferredLoadTask);
void _registerBackendImpact(
WorldImpactBuilder builder, BackendImpact impact) {
impact.registerImpact(builder, _elementEnvironment);
_backendUsage.processBackendImpact(impact);
}
void _addInterceptors(ClassEntity cls, WorldImpactBuilder impactBuilder) {
_interceptorData.addInterceptors(cls);
impactBuilder.registerTypeUse(
new TypeUse.instantiation(_elementEnvironment.getRawType(cls)));
_backendUsage.registerBackendClassUse(cls);
}
@override
WorldImpact registerClosurizedMember(FunctionEntity element) {
WorldImpactBuilderImpl impactBuilder = new WorldImpactBuilderImpl();
_backendUsage.processBackendImpact(_impacts.memberClosure);
impactBuilder
.addImpact(_impacts.memberClosure.createImpact(_elementEnvironment));
FunctionType type = _elementEnvironment.getFunctionType(element);
if (type.containsTypeVariables) {
impactBuilder.addImpact(_registerComputeSignature());
}
return impactBuilder;
}
@override
WorldImpact registerGetOfStaticFunction() {
_backendUsage.processBackendImpact(_impacts.staticClosure);
return _impacts.staticClosure.createImpact(_elementEnvironment);
}
WorldImpact _registerComputeSignature() {
_backendUsage.processBackendImpact(_impacts.computeSignature);
return _impacts.computeSignature.createImpact(_elementEnvironment);
}
@override
void registerInstantiatedType(InterfaceType type,
{bool isGlobal: false, bool nativeUsage: false}) {
if (isGlobal) {
_backendUsage.registerGlobalClassDependency(type.element);
}
if (nativeUsage) {
_nativeEnqueuer.onInstantiatedType(type);
}
}
/// Computes the [WorldImpact] of calling [mainMethod] as the entry point.
WorldImpact _computeMainImpact(FunctionEntity mainMethod) {
WorldImpactBuilderImpl mainImpact = new WorldImpactBuilderImpl();
CallStructure callStructure = mainMethod.parameterStructure.callStructure;
if (callStructure.argumentCount > 0) {
_impacts.mainWithArguments
.registerImpact(mainImpact, _elementEnvironment);
_backendUsage.processBackendImpact(_impacts.mainWithArguments);
mainImpact.registerStaticUse(
new StaticUse.staticInvoke(mainMethod, callStructure));
}
mainImpact.registerStaticUse(
new StaticUse.staticInvoke(mainMethod, CallStructure.NO_ARGS));
return mainImpact;
}
/// Returns the [WorldImpact] of enabling deferred loading.
WorldImpact _computeDeferredLoadingImpact() {
_backendUsage.processBackendImpact(_impacts.deferredLoading);
return _impacts.deferredLoading.createImpact(_elementEnvironment);
}
@override
void onQueueOpen(Enqueuer enqueuer, FunctionEntity mainMethod,
Iterable<LibraryEntity> libraries) {
if (_deferredLoadTask.isProgramSplit) {
enqueuer.applyImpact(_computeDeferredLoadingImpact(),
impactSource: 'deferred load');
}
enqueuer.applyImpact(_nativeEnqueuer.processNativeClasses(libraries),
impactSource: 'native classes');
if (mainMethod != null) {
enqueuer.applyImpact(_computeMainImpact(mainMethod),
impactSource: 'main impact');
}
// Elements required by enqueueHelpers are global dependencies
// that are not pulled in by a particular element.
enqueuer.applyImpact(computeHelpersImpact(), impactSource: 'helpers');
}
@override
bool onQueueEmpty(Enqueuer enqueuer, Iterable<ClassEntity> recentClasses) {
// Add elements used synthetically, that is, through features rather than
// syntax, for instance custom elements.
//
// Return early if any elements are added to avoid counting the elements as
// due to mirrors.
enqueuer.applyImpact(_customElementsAnalysis.flush(),
impactSource: _customElementsAnalysis);
for (ClassEntity cls in recentClasses) {
MemberEntity element = _elementEnvironment.lookupLocalClassMember(
cls, Identifiers.noSuchMethod_);
if (element != null &&
element.isInstanceMember &&
element.isFunction &&
!element.isAbstract) {
_noSuchMethodRegistry.registerNoSuchMethod(element);
}
}
_noSuchMethodRegistry.onQueueEmpty();
if (!_backendUsage.isNoSuchMethodUsed &&
(_noSuchMethodRegistry.hasThrowingNoSuchMethod ||
_noSuchMethodRegistry.hasComplexNoSuchMethod)) {
_backendUsage.processBackendImpact(_impacts.noSuchMethodSupport);
enqueuer.applyImpact(
_impacts.noSuchMethodSupport.createImpact(_elementEnvironment));
_backendUsage.isNoSuchMethodUsed = true;
}
if (!enqueuer.queueIsEmpty) return false;
return true;
}
@override
void onQueueClosed() {}
/// Adds the impact of [constant] to [impactBuilder].
void _computeImpactForCompileTimeConstant(
ConstantValue constant, WorldImpactBuilder impactBuilder) {
_computeImpactForCompileTimeConstantInternal(constant, impactBuilder);
for (ConstantValue dependency in constant.getDependencies()) {
_computeImpactForCompileTimeConstant(dependency, impactBuilder);
}
}
void _computeImpactForCompileTimeConstantInternal(
ConstantValue constant, WorldImpactBuilder impactBuilder) {
DartType type = constant.getType(_commonElements);
_computeImpactForInstantiatedConstantType(type, impactBuilder);
if (constant.isFunction) {
FunctionConstantValue function = constant;
impactBuilder
.registerStaticUse(new StaticUse.staticTearOff(function.element));
} else if (constant.isInterceptor) {
// An interceptor constant references the class's prototype chain.
InterceptorConstantValue interceptor = constant;
InterfaceType type = _elementEnvironment.getThisType(interceptor.cls);
_computeImpactForInstantiatedConstantType(type, impactBuilder);
} else if (constant.isType) {
FunctionEntity helper = _commonElements.createRuntimeType;
impactBuilder.registerStaticUse(new StaticUse.staticInvoke(
// TODO(johnniwinther): Find the right [CallStructure].
helper,
null));
_backendUsage.registerBackendFunctionUse(helper);
impactBuilder
.registerTypeUse(new TypeUse.instantiation(_commonElements.typeType));
}
}
void _computeImpactForInstantiatedConstantType(
DartType type, WorldImpactBuilder impactBuilder) {
if (type is InterfaceType) {
impactBuilder.registerTypeUse(new TypeUse.instantiation(type));
if (type.element == _commonElements.typeLiteralClass) {
// If we use a type literal in a constant, the compile time
// constant emitter will generate a call to the createRuntimeType
// helper so we register a use of that.
impactBuilder.registerStaticUse(new StaticUse.staticInvoke(
// TODO(johnniwinther): Find the right [CallStructure].
_commonElements.createRuntimeType,
null));
}
}
}
@override
WorldImpact registerUsedConstant(ConstantValue constant) {
WorldImpactBuilderImpl impactBuilder = new WorldImpactBuilderImpl();
_computeImpactForCompileTimeConstant(constant, impactBuilder);
return impactBuilder;
}
@override
WorldImpact registerUsedElement(MemberEntity member) {
WorldImpactBuilderImpl worldImpact = new WorldImpactBuilderImpl();
_customElementsAnalysis.registerStaticUse(member);
if (member.isFunction) {
FunctionEntity function = member;
if (function.isExternal) {
FunctionType functionType =
_elementEnvironment.getFunctionType(function);
var allParameterTypes = <DartType>[]
..addAll(functionType.parameterTypes)
..addAll(functionType.optionalParameterTypes)
..addAll(functionType.namedParameterTypes);
for (var type in allParameterTypes) {
if (type.isFunctionType || type.isTypedef) {
var closureConverter = _commonElements.closureConverter;
worldImpact.registerStaticUse(
new StaticUse.implicitInvoke(closureConverter));
_backendUsage.registerBackendFunctionUse(closureConverter);
_backendUsage.registerGlobalFunctionDependency(closureConverter);
break;
}
}
}
if (function.isInstanceMember) {
ClassEntity cls = function.enclosingClass;
if (function.name == Identifiers.call &&
_elementEnvironment.isGenericClass(cls)) {
worldImpact.addImpact(_registerComputeSignature());
}
}
}
_backendUsage.registerUsedMember(member);
if (_commonElements.isCreateInvocationMirrorHelper(member)) {
_registerBackendImpact(worldImpact, _impacts.noSuchMethodSupport);
}
if (_elementEnvironment.isDeferredLoadLibraryGetter(member)) {
// TODO(sigurdm): Create a function registerLoadLibraryAccess.
if (!_isLoadLibraryFunctionResolved) {
_isLoadLibraryFunctionResolved = true;
_registerBackendImpact(worldImpact, _impacts.loadLibrary);
}
}
if (member.isGetter && member.name == Identifiers.runtimeType_) {
// Enable runtime type support if we discover a getter called
// runtimeType. We have to enable runtime type before hitting the
// codegen, so that constructors know whether they need to generate code
// for runtime type.
// TODO(ahe): Record precise dependency here.
worldImpact.addImpact(_registerRuntimeType());
}
return worldImpact;
}
/// Called to register that the `runtimeType` property has been accessed. Any
/// backend specific [WorldImpact] of this is returned.
WorldImpact _registerRuntimeType() {
_backendUsage.processBackendImpact(_impacts.runtimeTypeSupport);
return _impacts.runtimeTypeSupport.createImpact(_elementEnvironment);
}
WorldImpact _processClass(ClassEntity cls) {
WorldImpactBuilderImpl impactBuilder = new WorldImpactBuilderImpl();
// TODO(johnniwinther): Extract an `implementationClassesOf(...)` function
// for these into [CommonElements] or [BackendImpacts].
// Register any helper that will be needed by the backend.
if (cls == _commonElements.intClass ||
cls == _commonElements.doubleClass ||
cls == _commonElements.numClass) {
_registerBackendImpact(impactBuilder, _impacts.numClasses);
} else if (cls == _commonElements.listClass ||
cls == _commonElements.stringClass) {
_registerBackendImpact(impactBuilder, _impacts.listOrStringClasses);
} else if (cls == _commonElements.functionClass) {
_registerBackendImpact(impactBuilder, _impacts.functionClass);
} else if (cls == _commonElements.mapClass) {
_registerBackendImpact(impactBuilder, _impacts.mapClass);
} else if (cls == _commonElements.boundClosureClass) {
_registerBackendImpact(impactBuilder, _impacts.boundClosureClass);
} else if (_nativeData.isNativeOrExtendsNative(cls)) {
_registerBackendImpact(impactBuilder, _impacts.nativeOrExtendsClass);
} else if (cls == _commonElements.mapLiteralClass) {
_registerBackendImpact(impactBuilder, _impacts.mapLiteralClass);
}
if (cls == _commonElements.closureClass) {
_registerBackendImpact(impactBuilder, _impacts.closureClass);
}
if (cls == _commonElements.stringClass ||
cls == _commonElements.jsStringClass) {
_addInterceptors(_commonElements.jsStringClass, impactBuilder);
} else if (cls == _commonElements.listClass ||
cls == _commonElements.jsArrayClass ||
cls == _commonElements.jsFixedArrayClass ||
cls == _commonElements.jsExtendableArrayClass ||
cls == _commonElements.jsUnmodifiableArrayClass) {
_addInterceptors(_commonElements.jsArrayClass, impactBuilder);
_addInterceptors(_commonElements.jsMutableArrayClass, impactBuilder);
_addInterceptors(_commonElements.jsFixedArrayClass, impactBuilder);
_addInterceptors(_commonElements.jsExtendableArrayClass, impactBuilder);
_addInterceptors(_commonElements.jsUnmodifiableArrayClass, impactBuilder);
_registerBackendImpact(impactBuilder, _impacts.listClasses);
} else if (cls == _commonElements.intClass ||
cls == _commonElements.jsIntClass) {
_addInterceptors(_commonElements.jsIntClass, impactBuilder);
_addInterceptors(_commonElements.jsPositiveIntClass, impactBuilder);
_addInterceptors(_commonElements.jsUInt32Class, impactBuilder);
_addInterceptors(_commonElements.jsUInt31Class, impactBuilder);
_addInterceptors(_commonElements.jsNumberClass, impactBuilder);
} else if (cls == _commonElements.doubleClass ||
cls == _commonElements.jsDoubleClass) {
_addInterceptors(_commonElements.jsDoubleClass, impactBuilder);
_addInterceptors(_commonElements.jsNumberClass, impactBuilder);
} else if (cls == _commonElements.boolClass ||
cls == _commonElements.jsBoolClass) {
_addInterceptors(_commonElements.jsBoolClass, impactBuilder);
} else if (cls == _commonElements.nullClass ||
cls == _commonElements.jsNullClass) {
_addInterceptors(_commonElements.jsNullClass, impactBuilder);
} else if (cls == _commonElements.numClass ||
cls == _commonElements.jsNumberClass) {
_addInterceptors(_commonElements.jsIntClass, impactBuilder);
_addInterceptors(_commonElements.jsPositiveIntClass, impactBuilder);
_addInterceptors(_commonElements.jsUInt32Class, impactBuilder);
_addInterceptors(_commonElements.jsUInt31Class, impactBuilder);
_addInterceptors(_commonElements.jsDoubleClass, impactBuilder);
_addInterceptors(_commonElements.jsNumberClass, impactBuilder);
} else if (cls == _commonElements.jsJavaScriptObjectClass) {
_addInterceptors(_commonElements.jsJavaScriptObjectClass, impactBuilder);
} else if (cls == _commonElements.jsPlainJavaScriptObjectClass) {
_addInterceptors(
_commonElements.jsPlainJavaScriptObjectClass, impactBuilder);
} else if (cls == _commonElements.jsUnknownJavaScriptObjectClass) {
_addInterceptors(
_commonElements.jsUnknownJavaScriptObjectClass, impactBuilder);
} else if (cls == _commonElements.jsJavaScriptFunctionClass) {
_addInterceptors(
_commonElements.jsJavaScriptFunctionClass, impactBuilder);
} else if (_nativeData.isNativeOrExtendsNative(cls)) {
_interceptorData.addInterceptorsForNativeClassMembers(cls);
} else if (cls == _commonElements.jsIndexingBehaviorInterface) {
_registerBackendImpact(impactBuilder, _impacts.jsIndexingBehavior);
}
_customElementsAnalysis.registerInstantiatedClass(cls);
return impactBuilder;
}
@override
WorldImpact registerImplementedClass(ClassEntity cls) {
return _processClass(cls);
}
@override
WorldImpact registerInstantiatedClass(ClassEntity cls) {
_allocatorAnalysis.registerInstantiatedClass(cls);
return _processClass(cls);
}
/// Compute the [WorldImpact] for backend helper methods.
WorldImpact computeHelpersImpact() {
assert(_commonElements.interceptorsLibrary != null);
WorldImpactBuilderImpl impactBuilder = new WorldImpactBuilderImpl();
// TODO(ngeoffray): Not enqueuing those two classes currently make
// the compiler potentially crash. However, any reasonable program
// will instantiate those two classes.
_addInterceptors(_commonElements.jsBoolClass, impactBuilder);
_addInterceptors(_commonElements.jsNullClass, impactBuilder);
if (_options.enableTypeAssertions) {
_registerBackendImpact(impactBuilder, _impacts.enableTypeAssertions);
}
if (_options.disableRtiOptimization) {
// When RTI optimization is disabled we always need all RTI helpers, so
// register these here.
_registerBackendImpact(impactBuilder, _impacts.computeSignature);
_registerBackendImpact(impactBuilder, _impacts.getRuntimeTypeArgument);
}
if (JavaScriptBackend.TRACE_CALLS) {
_registerBackendImpact(impactBuilder, _impacts.traceHelper);
}
_registerBackendImpact(impactBuilder, _impacts.assertUnreachable);
_registerCheckedModeHelpers(impactBuilder);
return impactBuilder;
}
void _registerCheckedModeHelpers(WorldImpactBuilder impactBuilder) {
// We register all the _commonElements in the resolution queue.
// TODO(13155): Find a way to register fewer _commonElements.
List<FunctionEntity> staticUses = <FunctionEntity>[];
for (CheckedModeHelper helper in CheckedModeHelpers.helpers) {
staticUses.add(helper.getStaticUse(_commonElements).element);
}
_registerBackendImpact(
impactBuilder, new BackendImpact(globalUses: staticUses));
}
@override
void logSummary(void log(String message)) {
_nativeEnqueuer.logSummary(log);
}
}