blob: e1f4868f5d88610e485e1840f01585560f474c1e [file] [log] [blame]
// 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,
],
);
}