| // 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); |
| } |
| } |