| // 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, Uris; |
| 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 '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 'mirrors_analysis.dart'; |
| import 'mirrors_data.dart'; |
| import 'native_data.dart' show NativeBasicData; |
| import 'no_such_method_registry.dart'; |
| import 'type_variable_handler.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 MirrorsDataBuilder _mirrorsDataBuilder; |
| |
| final NoSuchMethodRegistry _noSuchMethodRegistry; |
| final CustomElementsResolutionAnalysis _customElementsAnalysis; |
| final MirrorsResolutionAnalysis _mirrorsAnalysis; |
| final TypeVariableResolutionAnalysis _typeVariableResolutionAnalysis; |
| |
| final NativeResolutionEnqueuer _nativeEnqueuer; |
| |
| /// 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._mirrorsDataBuilder, |
| this._noSuchMethodRegistry, |
| this._customElementsAnalysis, |
| this._mirrorsAnalysis, |
| this._typeVariableResolutionAnalysis, |
| this._nativeEnqueuer, |
| 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); |
| } |
| } |
| |
| /// Called to enable support for isolates. Any backend specific [WorldImpact] |
| /// of this is returned. |
| WorldImpact _enableIsolateSupport(FunctionEntity mainMethod) { |
| WorldImpactBuilderImpl impactBuilder = new WorldImpactBuilderImpl(); |
| // TODO(floitsch): We should also ensure that the class IsolateMessage is |
| // instantiated. Currently, just enabling isolate support works. |
| if (mainMethod != null) { |
| // The JavaScript backend implements [Isolate.spawn] by looking up |
| // top-level functions by name. So all top-level function tear-off |
| // closures have a private name field. |
| // |
| // The JavaScript backend of [Isolate.spawnUri] uses the same internal |
| // implementation as [Isolate.spawn], and fails if it cannot look main up |
| // by name. |
| impactBuilder.registerStaticUse(new StaticUse.staticTearOff(mainMethod)); |
| } |
| _impacts.isolateSupport.registerImpact(impactBuilder, _elementEnvironment); |
| _backendUsage.processBackendImpact(_impacts.isolateSupport); |
| _impacts.isolateSupportForResolution |
| .registerImpact(impactBuilder, _elementEnvironment); |
| _backendUsage.processBackendImpact(_impacts.isolateSupportForResolution); |
| return impactBuilder; |
| } |
| |
| /// 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)); |
| // If the main method takes arguments, this compilation could be the |
| // target of Isolate.spawnUri. Strictly speaking, that can happen also if |
| // main takes no arguments, but in this case the spawned isolate can't |
| // communicate with the spawning isolate. |
| mainImpact.addImpact(_enableIsolateSupport(mainMethod)); |
| } |
| 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); |
| enqueuer.applyImpact(_typeVariableResolutionAnalysis.flush(), |
| impactSource: _typeVariableResolutionAnalysis); |
| |
| 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; |
| |
| _mirrorsAnalysis.onQueueEmpty(enqueuer, recentClasses); |
| 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(); |
| _mirrorsDataBuilder.registerUsedMember(member); |
| _customElementsAnalysis.registerStaticUse(member); |
| |
| if (member.isFunction && member.isInstanceMember) { |
| FunctionEntity method = member; |
| ClassEntity cls = method.enclosingClass; |
| |
| if (method.name == Identifiers.call && |
| _elementEnvironment.isGenericClass(cls)) { |
| worldImpact.addImpact(_registerComputeSignature()); |
| } |
| } |
| _backendUsage.registerUsedMember(member); |
| |
| if (_elementEnvironment.isDeferredLoadLibraryGetter(member)) { |
| // TODO(sigurdm): Create a function registerLoadLibraryAccess. |
| if (!_isLoadLibraryFunctionResolved) { |
| _isLoadLibraryFunctionResolved = true; |
| _registerBackendImpact(worldImpact, _impacts.loadLibrary); |
| } |
| } |
| |
| // Enable isolate support if we start using something from the isolate |
| // library, or timers for the async library. We exclude constant fields, |
| // which are ending here because their initializing expression is |
| // compiled. |
| LibraryEntity library = member.library; |
| if (!_backendUsage.isIsolateInUse && !(member.isField && member.isConst)) { |
| Uri uri = library.canonicalUri; |
| if (uri == Uris.dart_isolate) { |
| _backendUsage.isIsolateInUse = true; |
| worldImpact |
| .addImpact(_enableIsolateSupport(_elementEnvironment.mainFunction)); |
| } else if (uri == Uris.dart_async) { |
| if (member.name == '_createTimer' || |
| member.name == '_createPeriodicTimer') { |
| // The [:Timer:] class uses the event queue of the isolate |
| // library, so we make sure that event queue is generated. |
| _backendUsage.isIsolateInUse = true; |
| worldImpact.addImpact( |
| _enableIsolateSupport(_elementEnvironment.mainFunction)); |
| } |
| } |
| } |
| |
| 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. |
| _backendUsage.isRuntimeTypeUsed = true; |
| // 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(); |
| if (_elementEnvironment.isGenericClass(cls)) { |
| _typeVariableResolutionAnalysis.registerClassWithTypeVariables(cls); |
| } |
| // 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) { |
| 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); |
| } |
| } |