| // Copyright (c) 2015, 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 dart2js.js_helpers.impact; |
| |
| import '../common/elements.dart' show CommonElements, ElementEnvironment; |
| import '../common/names.dart'; |
| import '../elements/types.dart' show InterfaceType; |
| import '../elements/entities.dart'; |
| import '../universe/selector.dart'; |
| import '../universe/world_impact.dart' |
| show WorldImpact, WorldImpactBuilder, WorldImpactBuilderImpl; |
| import '../universe/use.dart'; |
| import '../util/enumset.dart'; |
| import '../options.dart'; |
| |
| /// Backend specific features required by a backend impact. |
| enum BackendFeature { |
| needToInitializeIsolateAffinityTag, |
| needToInitializeDispatchProperty, |
| } |
| |
| /// A set of JavaScript backend dependencies. |
| class BackendImpact { |
| final List<FunctionEntity> staticUses; |
| final List<FunctionEntity> globalUses; |
| final List<Selector> dynamicUses; |
| final List<InterfaceType> instantiatedTypes; |
| final List<ClassEntity> instantiatedClasses; |
| final List<ClassEntity> globalClasses; |
| final List<BackendImpact> otherImpacts; |
| final EnumSet<BackendFeature> _features; |
| |
| const BackendImpact( |
| {this.staticUses = const [], |
| this.globalUses = const [], |
| this.dynamicUses = const [], |
| this.instantiatedTypes = const [], |
| this.instantiatedClasses = const [], |
| this.globalClasses = const [], |
| this.otherImpacts = const [], |
| EnumSet<BackendFeature> features = const EnumSet.fixed(0)}) |
| : this._features = features; |
| |
| Iterable<BackendFeature> get features => |
| _features.iterable(BackendFeature.values); |
| |
| WorldImpact createImpact(ElementEnvironment elementEnvironment) { |
| WorldImpactBuilderImpl impactBuilder = WorldImpactBuilderImpl(); |
| registerImpact(impactBuilder, elementEnvironment); |
| return impactBuilder; |
| } |
| |
| /// Register this backend impact to the [worldImpactBuilder]. |
| void registerImpact(WorldImpactBuilder worldImpactBuilder, |
| ElementEnvironment elementEnvironment) { |
| for (FunctionEntity staticUse in staticUses) { |
| assert((staticUse as dynamic) != null); // TODO(48820): Remove when sound. |
| worldImpactBuilder.registerStaticUse(StaticUse.implicitInvoke(staticUse)); |
| } |
| for (FunctionEntity staticUse in globalUses) { |
| assert((staticUse as dynamic) != null); // TODO(48820): Remove when sound. |
| worldImpactBuilder.registerStaticUse(StaticUse.implicitInvoke(staticUse)); |
| } |
| for (Selector selector in dynamicUses) { |
| assert((selector as dynamic) != null); // TODO(48820): Remove when sound. |
| worldImpactBuilder |
| .registerDynamicUse(DynamicUse(selector, null, const [])); |
| } |
| for (InterfaceType instantiatedType in instantiatedTypes) { |
| worldImpactBuilder |
| .registerTypeUse(TypeUse.instantiation(instantiatedType)); |
| } |
| for (ClassEntity cls in instantiatedClasses) { |
| worldImpactBuilder.registerTypeUse( |
| TypeUse.instantiation(elementEnvironment.getRawType(cls))); |
| } |
| for (ClassEntity cls in globalClasses) { |
| worldImpactBuilder.registerTypeUse( |
| TypeUse.instantiation(elementEnvironment.getRawType(cls))); |
| } |
| for (BackendImpact otherImpact in otherImpacts) { |
| otherImpact.registerImpact(worldImpactBuilder, elementEnvironment); |
| } |
| } |
| } |
| |
| /// The JavaScript backend dependencies for various features. |
| class BackendImpacts { |
| final CommonElements _commonElements; |
| final CompilerOptions _options; |
| |
| BackendImpacts(this._commonElements, this._options); |
| |
| late final BackendImpact getRuntimeTypeArgument = BackendImpact( |
| globalUses: [], |
| otherImpacts: [newRtiImpact], |
| ); |
| |
| late final BackendImpact computeSignature = BackendImpact( |
| globalUses: [ |
| _commonElements.setArrayType, |
| ], |
| otherImpacts: [listValues], |
| ); |
| |
| late final BackendImpact mainWithArguments = BackendImpact( |
| globalUses: [_commonElements.convertMainArgumentList], |
| instantiatedClasses: [ |
| _commonElements.jsArrayClass, |
| _commonElements.jsStringClass |
| ], |
| ); |
| |
| late final BackendImpact asyncBody = BackendImpact( |
| staticUses: [ |
| _commonElements.asyncHelperAwait, |
| _commonElements.asyncHelperReturn, |
| _commonElements.asyncHelperRethrow, |
| _commonElements.streamIteratorConstructor, |
| _commonElements.wrapBody, |
| _commonElements.asyncHelperStartSync |
| ], |
| ); |
| |
| late final BackendImpact syncStarBody = BackendImpact( |
| staticUses: [ |
| _commonElements.endOfIteration, |
| _commonElements.yieldStar, |
| _commonElements.syncStarUncaughtError, |
| ], |
| ); |
| |
| late final BackendImpact asyncStarBody = BackendImpact( |
| staticUses: [ |
| _commonElements.asyncStarHelper, |
| _commonElements.streamOfController, |
| _commonElements.yieldSingle, |
| _commonElements.yieldStar, |
| _commonElements.streamIteratorConstructor, |
| _commonElements.wrapBody, |
| ], |
| ); |
| |
| late final BackendImpact typeVariableBoundCheck = BackendImpact( |
| staticUses: [ |
| _commonElements.checkTypeBound, |
| ], |
| ); |
| |
| late final BackendImpact asCheck = BackendImpact( |
| staticUses: [], |
| otherImpacts: [ |
| newRtiImpact, |
| ], |
| ); |
| |
| late final BackendImpact stringValues = BackendImpact( |
| instantiatedClasses: [_commonElements.jsStringClass], |
| ); |
| |
| late final BackendImpact numValues = BackendImpact( |
| instantiatedClasses: [ |
| _commonElements.jsIntClass, |
| _commonElements.jsPositiveIntClass, |
| _commonElements.jsUInt32Class, |
| _commonElements.jsUInt31Class, |
| _commonElements.jsNumberClass, |
| _commonElements.jsNumNotIntClass, |
| ], |
| ); |
| |
| BackendImpact get intValues => numValues; |
| |
| BackendImpact get doubleValues => numValues; |
| |
| late final BackendImpact boolValues = BackendImpact( |
| instantiatedClasses: [_commonElements.jsBoolClass], |
| ); |
| |
| late final BackendImpact nullValue = BackendImpact( |
| instantiatedClasses: [_commonElements.jsNullClass], |
| ); |
| |
| late final BackendImpact listValues = BackendImpact( |
| globalClasses: [ |
| _commonElements.jsArrayClass, |
| _commonElements.jsMutableArrayClass, |
| _commonElements.jsFixedArrayClass, |
| _commonElements.jsExtendableArrayClass, |
| _commonElements.jsUnmodifiableArrayClass |
| ], |
| ); |
| |
| late final BackendImpact throwUnsupportedError = BackendImpact( |
| staticUses: [_commonElements.throwUnsupportedError], |
| otherImpacts: [ |
| // Also register the types of the arguments passed to this method. |
| stringValues |
| ], |
| ); |
| |
| late final BackendImpact superNoSuchMethod = BackendImpact( |
| staticUses: [ |
| _commonElements.createInvocationMirror, |
| _commonElements.objectNoSuchMethod |
| ], |
| otherImpacts: [ |
| _needsInt('Needed to encode the invocation kind of super.noSuchMethod.'), |
| _needsList('Needed to encode the arguments of super.noSuchMethod.'), |
| _needsString('Needed to encode the name of super.noSuchMethod.') |
| ], |
| ); |
| |
| late final BackendImpact constantMapLiteral = BackendImpact( |
| instantiatedClasses: [ |
| _commonElements.constantMapClass, |
| _commonElements.constantStringMapClass, |
| _commonElements.generalConstantMapClass, |
| ], |
| ); |
| |
| late final BackendImpact constantSetLiteral = BackendImpact( |
| instantiatedClasses: [ |
| _commonElements.constSetLiteralClass, |
| ], |
| otherImpacts: [constantMapLiteral], |
| ); |
| |
| late final BackendImpact constSymbol = BackendImpact( |
| instantiatedClasses: [_commonElements.symbolImplementationClass], |
| staticUses: [_commonElements.symbolConstructorTarget], |
| ); |
| |
| /// Helper for registering that `int` is needed. |
| BackendImpact _needsInt(String reason) { |
| // TODO(johnniwinther): Register [reason] for use in dump-info. |
| return intValues; |
| } |
| |
| /// Helper for registering that `List` is needed. |
| BackendImpact _needsList(String reason) { |
| // TODO(johnniwinther): Register [reason] for use in dump-info. |
| return listValues; |
| } |
| |
| /// Helper for registering that `String` is needed. |
| BackendImpact _needsString(String reason) { |
| // TODO(johnniwinther): Register [reason] for use in dump-info. |
| return stringValues; |
| } |
| |
| late final BackendImpact assertWithoutMessage = BackendImpact( |
| staticUses: [_commonElements.assertHelper], |
| ); |
| |
| late final BackendImpact assertWithMessage = BackendImpact( |
| staticUses: [_commonElements.assertTest, _commonElements.assertThrow], |
| ); |
| |
| late final BackendImpact asyncForIn = BackendImpact( |
| staticUses: [_commonElements.streamIteratorConstructor], |
| ); |
| |
| late final BackendImpact stringInterpolation = BackendImpact( |
| dynamicUses: [Selectors.toString_], |
| staticUses: [_commonElements.stringInterpolationHelper], |
| otherImpacts: [_needsString('Strings are created.')], |
| ); |
| |
| late final BackendImpact stringJuxtaposition = |
| _needsString('String.concat is used.'); |
| |
| BackendImpact get nullLiteral => nullValue; |
| |
| BackendImpact get boolLiteral => boolValues; |
| |
| BackendImpact get intLiteral => intValues; |
| |
| BackendImpact get doubleLiteral => doubleValues; |
| |
| BackendImpact get stringLiteral => stringValues; |
| |
| late final BackendImpact catchStatement = BackendImpact( |
| staticUses: [_commonElements.exceptionUnwrapper], |
| instantiatedClasses: [ |
| _commonElements.jsPlainJavaScriptObjectClass, |
| _commonElements.jsUnknownJavaScriptObjectClass |
| ], |
| ); |
| |
| late final BackendImpact throwExpression = BackendImpact( |
| // We don't know ahead of time whether we will need the throw in a statement |
| // context or an expression context, so we register both here, even though |
| // we may not need the throwExpression helper. |
| staticUses: [ |
| _commonElements.wrapExceptionHelper, |
| _commonElements.throwExpressionHelper |
| ], |
| ); |
| |
| late final BackendImpact lazyField = BackendImpact( |
| staticUses: [ |
| _commonElements.cyclicThrowHelper, |
| _commonElements.throwLateFieldADI, |
| ], |
| ); |
| |
| late final BackendImpact typeLiteral = BackendImpact( |
| instantiatedClasses: [_commonElements.typeLiteralClass], |
| staticUses: [ |
| _commonElements.createRuntimeType, |
| _commonElements.typeLiteralMaker, |
| ], |
| ); |
| |
| late final BackendImpact stackTraceInCatch = BackendImpact( |
| instantiatedClasses: [_commonElements.stackTraceHelperClass], |
| staticUses: [_commonElements.traceFromException], |
| ); |
| |
| late final BackendImpact syncForIn = BackendImpact( |
| // The SSA builder recognizes certain for-in loops and can generate |
| // calls to throwConcurrentModificationError. |
| staticUses: [_commonElements.checkConcurrentModificationError], |
| ); |
| |
| late final BackendImpact typeVariableExpression = BackendImpact( |
| staticUses: [ |
| _commonElements.setArrayType, |
| _commonElements.createRuntimeType |
| ], |
| otherImpacts: [ |
| listValues, |
| getRuntimeTypeArgument, |
| _needsInt('Needed for accessing a type variable literal on this.') |
| ], |
| ); |
| |
| late final BackendImpact typeCheck = |
| BackendImpact(otherImpacts: [boolValues, newRtiImpact]); |
| |
| late final BackendImpact genericTypeCheck = BackendImpact( |
| staticUses: [ |
| // TODO(johnniwinther): Investigate why this is needed. |
| _commonElements.setArrayType, |
| ], |
| otherImpacts: [ |
| listValues, |
| getRuntimeTypeArgument, |
| newRtiImpact, |
| ], |
| ); |
| |
| late final BackendImpact genericIsCheck = |
| BackendImpact(otherImpacts: [intValues, newRtiImpact]); |
| |
| late final BackendImpact typeVariableTypeCheck = |
| BackendImpact(staticUses: [], otherImpacts: [newRtiImpact]); |
| |
| late final BackendImpact functionTypeCheck = BackendImpact( |
| staticUses: [/*helpers.functionTypeTestMetaHelper*/], |
| otherImpacts: [newRtiImpact], |
| ); |
| |
| late final BackendImpact futureOrTypeCheck = BackendImpact( |
| staticUses: [], |
| otherImpacts: [newRtiImpact], |
| ); |
| |
| late final BackendImpact nativeTypeCheck = BackendImpact( |
| staticUses: [ |
| // We will need to add the "$is" and "$as" properties on the |
| // JavaScript object prototype, so we make sure |
| // [:defineProperty:] is compiled. |
| _commonElements.defineProperty |
| ], |
| otherImpacts: [newRtiImpact], |
| ); |
| |
| late final BackendImpact closure = BackendImpact( |
| instantiatedClasses: [_commonElements.functionClass], |
| ); |
| |
| late final BackendImpact interceptorUse = BackendImpact( |
| staticUses: [_commonElements.getNativeInterceptorMethod], |
| instantiatedClasses: [ |
| _commonElements.jsJavaScriptObjectClass, |
| _commonElements.jsLegacyJavaScriptObjectClass, |
| _commonElements.jsPlainJavaScriptObjectClass, |
| _commonElements.jsJavaScriptFunctionClass |
| ], |
| features: EnumSet<BackendFeature>.fromValues([ |
| BackendFeature.needToInitializeDispatchProperty, |
| BackendFeature.needToInitializeIsolateAffinityTag |
| ], fixed: true), |
| ); |
| |
| late final BackendImpact allowInterop = BackendImpact( |
| staticUses: [ |
| _commonElements.jsAllowInterop1!, |
| _commonElements.jsAllowInterop2!, |
| ], |
| features: EnumSet<BackendFeature>.fromValues([ |
| BackendFeature.needToInitializeIsolateAffinityTag, |
| ], fixed: true), |
| ); |
| |
| late final BackendImpact numClasses = BackendImpact( |
| // The backend will try to optimize number operations and use the |
| // `iae` helper directly. |
| globalUses: [_commonElements.throwIllegalArgumentException], |
| ); |
| |
| late final BackendImpact listOrStringClasses = BackendImpact( |
| // The backend will try to optimize array and string access and use the |
| // `ioore` and `iae` _commonElements directly. |
| globalUses: [ |
| _commonElements.throwIndexOutOfRangeException, |
| _commonElements.throwIllegalArgumentException |
| ], |
| ); |
| |
| late final BackendImpact functionClass = BackendImpact( |
| globalClasses: [ |
| _commonElements.closureClass, |
| _commonElements.closureClass0Args, |
| _commonElements.closureClass2Args, |
| ], |
| ); |
| |
| late final BackendImpact mapClass = BackendImpact( |
| // The backend will use a literal list to initialize the entries of the map. |
| globalClasses: [_commonElements.listClass, _commonElements.mapLiteralClass], |
| ); |
| |
| late final BackendImpact setClass = BackendImpact( |
| globalClasses: [ |
| // The backend will use a literal list to initialize the entries of the |
| // set. |
| _commonElements.listClass, |
| _commonElements.setLiteralClass, |
| ], |
| ); |
| |
| late final BackendImpact boundClosureClass = BackendImpact( |
| globalClasses: [_commonElements.boundClosureClass], |
| ); |
| |
| late final BackendImpact nativeOrExtendsClass = BackendImpact( |
| globalUses: [_commonElements.getNativeInterceptorMethod], |
| globalClasses: [ |
| _commonElements.jsInterceptorClass, |
| _commonElements.jsJavaScriptObjectClass, |
| _commonElements.jsLegacyJavaScriptObjectClass, |
| _commonElements.jsPlainJavaScriptObjectClass, |
| _commonElements.jsJavaScriptFunctionClass |
| ], |
| ); |
| |
| late final BackendImpact mapLiteralClass = BackendImpact( |
| globalUses: [ |
| _commonElements.mapLiteralConstructor, |
| _commonElements.mapLiteralConstructorEmpty, |
| _commonElements.mapLiteralUntypedMaker, |
| _commonElements.mapLiteralUntypedEmptyMaker |
| ], |
| ); |
| |
| late final BackendImpact setLiteralClass = BackendImpact( |
| globalUses: [ |
| _commonElements.setLiteralConstructor, |
| _commonElements.setLiteralConstructorEmpty, |
| _commonElements.setLiteralUntypedMaker, |
| _commonElements.setLiteralUntypedEmptyMaker, |
| ], |
| ); |
| |
| late final BackendImpact closureClass = BackendImpact( |
| globalUses: [_commonElements.closureFromTearOff], |
| ); |
| |
| late final BackendImpact listClasses = BackendImpact( |
| // Literal lists can be translated into calls to these functions: |
| globalUses: [ |
| _commonElements.jsArrayTypedConstructor, |
| _commonElements.setArrayType, |
| ], |
| ); |
| |
| late final BackendImpact jsIndexingBehavior = BackendImpact( |
| // These two _commonElements are used by the emitter and the codegen. |
| // Because we cannot enqueue elements at the time of emission, |
| // we make sure they are always generated. |
| globalUses: [_commonElements.isJsIndexable], |
| ); |
| |
| late final BackendImpact traceHelper = BackendImpact( |
| globalUses: [_commonElements.traceHelper], |
| ); |
| |
| late final BackendImpact assertUnreachable = BackendImpact( |
| globalUses: [_commonElements.assertUnreachableMethod], |
| ); |
| |
| late final BackendImpact runtimeTypeSupport = BackendImpact( |
| globalClasses: [_commonElements.listClass], |
| globalUses: [ |
| _commonElements.setArrayType, |
| ], |
| otherImpacts: [getRuntimeTypeArgument, computeSignature], |
| ); |
| |
| late final BackendImpact deferredLoading = BackendImpact( |
| globalUses: [_commonElements.checkDeferredIsLoaded], |
| // Also register the types of the arguments passed to this method. |
| globalClasses: [_commonElements.stringClass], |
| ); |
| |
| late final BackendImpact noSuchMethodSupport = BackendImpact( |
| globalUses: [ |
| _commonElements.createInvocationMirror, |
| _commonElements.createUnmangledInvocationMirror |
| ], |
| dynamicUses: [Selectors.noSuchMethod_], |
| ); |
| |
| /// Backend impact for accessing a `loadLibrary` function on a deferred |
| /// prefix. |
| late final BackendImpact loadLibrary = BackendImpact( |
| globalUses: [ |
| _commonElements.loadDeferredLibrary, |
| ], |
| ); |
| |
| /// Backend impact for performing member closurization. |
| late final BackendImpact memberClosure = |
| BackendImpact(globalClasses: [_commonElements.boundClosureClass]); |
| |
| /// Backend impact for performing closurization of a top-level or static |
| /// function. |
| late final BackendImpact staticClosure = |
| BackendImpact(globalClasses: [_commonElements.closureClass]); |
| |
| final Map<int, BackendImpact> _genericInstantiation = {}; |
| |
| BackendImpact getGenericInstantiation(int typeArgumentCount) => |
| _genericInstantiation[typeArgumentCount] ??= BackendImpact( |
| staticUses: [ |
| _commonElements.getInstantiateFunction(typeArgumentCount), |
| _commonElements.instantiatedGenericFunctionTypeNewRti, |
| _commonElements.closureFunctionType, |
| ], |
| instantiatedClasses: [ |
| _commonElements.getInstantiationClass(typeArgumentCount), |
| ], |
| ); |
| |
| // TODO(sra): Split into refined impacts. |
| late final BackendImpact newRtiImpact = BackendImpact( |
| staticUses: [ |
| _commonElements.findType, |
| _commonElements.instanceType, |
| _commonElements.arrayInstanceType, |
| _commonElements.simpleInstanceType, |
| _commonElements.rtiEvalMethod, |
| _commonElements.rtiBindMethod, |
| _commonElements.installSpecializedIsTest, |
| _commonElements.generalIsTestImplementation, |
| _commonElements.generalAsCheckImplementation, |
| _commonElements.installSpecializedAsCheck, |
| _commonElements.generalNullableIsTestImplementation, |
| _commonElements.generalNullableAsCheckImplementation, |
| // Specialized checks. |
| _commonElements.specializedIsBool, |
| _commonElements.specializedAsBool, |
| _commonElements.specializedAsBoolLegacy, |
| _commonElements.specializedAsBoolNullable, |
| // no specializedIsDouble. |
| _commonElements.specializedAsDouble, |
| _commonElements.specializedAsDoubleLegacy, |
| _commonElements.specializedAsDoubleNullable, |
| _commonElements.specializedIsInt, |
| _commonElements.specializedAsInt, |
| _commonElements.specializedAsIntLegacy, |
| _commonElements.specializedAsIntNullable, |
| _commonElements.specializedIsNum, |
| _commonElements.specializedAsNum, |
| _commonElements.specializedAsNumLegacy, |
| _commonElements.specializedAsNumNullable, |
| _commonElements.specializedIsString, |
| _commonElements.specializedAsString, |
| _commonElements.specializedAsStringLegacy, |
| _commonElements.specializedAsStringNullable, |
| _commonElements.specializedIsTop, |
| _commonElements.specializedAsTop, |
| _commonElements.specializedIsObject, |
| _commonElements.specializedAsObject, |
| ], |
| globalClasses: [ |
| _commonElements.closureClass, // instanceOrFunctionType uses this. |
| ], |
| ); |
| |
| // TODO(fishythefish): Split into refined impacts. |
| late final BackendImpact rtiAddRules = BackendImpact( |
| globalUses: [ |
| _commonElements.rtiAddRulesMethod, |
| _commonElements.rtiAddErasedTypesMethod, |
| if (_options.enableVariance) |
| _commonElements.rtiAddTypeParameterVariancesMethod, |
| ], |
| otherImpacts: [_needsString('Needed to encode the new RTI ruleset.')], |
| ); |
| |
| late final BackendImpact lateFieldReadCheck = BackendImpact( |
| globalUses: [ |
| _commonElements.throwUnnamedLateFieldNI, |
| _commonElements.throwLateFieldNI, |
| ], |
| ); |
| |
| late final BackendImpact lateFieldWriteOnceCheck = BackendImpact( |
| globalUses: [ |
| _commonElements.throwUnnamedLateFieldAI, |
| _commonElements.throwLateFieldAI, |
| ], |
| ); |
| |
| late final BackendImpact lateFieldInitializeOnceCheck = BackendImpact( |
| globalUses: [ |
| _commonElements.throwUnnamedLateFieldADI, |
| _commonElements.throwLateFieldADI, |
| ], |
| ); |
| } |