Version 2.7.1
* Cherry-pick a1b895a900dc519559f04951ae110e900aeb499c to stable
* Cherry-pick 7373347c7ce72e423ca42c3515caa3c66a46b024 to stable
* Cherry-pick aa56cd39a3 to stable
* Cherry-pick 1103d9fdb5 to stable
* Cherry-pick b050a59d3b to stable
* Cherry-pick a57a97703a to stable
* Cherry-pick 75fc15c7e1 to stable
* Cherry-pick b233061a90 to stable
* Cherry-pick 4bc9c8b52c to stable
* Cherry-pick 529913fd39 to stable
* Cherry-pick a92a9f1ba2 to stable
* Cherry-pick d202ee3b72ac2ce38d5588e5b384efed0070f7ca to stable
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dd63681..d316f79 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,14 @@
+## 2.7.1 - 2020-01-32
+
+This is a patch release that improves dart2js compile-time (issue [40217][]).
+
+[40217]: https://github.com/dart-lang/sdk/issues/40217
+
+**Breaking Change**:
+The Dart SDK for macOS is now only available for x64 (issue [39810][]).
+
+[39810]: https://github.com/dart-lang/sdk/issues/39810
+
## 2.7.0 - 2019-12-11
### Language
diff --git a/pkg/compiler/lib/src/inferrer/builder_kernel.dart b/pkg/compiler/lib/src/inferrer/builder_kernel.dart
index 48e3b92..9a003bd 100644
--- a/pkg/compiler/lib/src/inferrer/builder_kernel.dart
+++ b/pkg/compiler/lib/src/inferrer/builder_kernel.dart
@@ -339,9 +339,8 @@
ArgumentsTypes arguments = analyzeArguments(node.arguments);
Selector selector = new Selector(SelectorKind.CALL, constructor.memberName,
_elementMap.getCallStructure(node.arguments));
- AbstractValue mask = _memberData.typeOfSend(node);
handleConstructorInvoke(
- node, node.arguments, selector, mask, constructor, arguments);
+ node, node.arguments, selector, constructor, arguments);
_inferrer.analyze(constructor);
if (_inferrer.checkIfExposesThis(constructor)) {
@@ -356,9 +355,8 @@
ArgumentsTypes arguments = analyzeArguments(node.arguments);
Selector selector = new Selector(SelectorKind.CALL, constructor.memberName,
_elementMap.getCallStructure(node.arguments));
- AbstractValue mask = _memberData.typeOfSend(node);
handleConstructorInvoke(
- node, node.arguments, selector, mask, constructor, arguments);
+ node, node.arguments, selector, constructor, arguments);
_inferrer.analyze(constructor);
if (_inferrer.checkIfExposesThis(constructor)) {
@@ -391,7 +389,8 @@
if (_closedWorld.nativeData.isNativeMember(_analyzedMember)) {
// Native methods do not have a body, and we currently just say
- // they return dynamic.
+ // they return dynamic and may contain all side-effects.
+ _sideEffectsBuilder.setAllSideEffectsAndDependsOnSomething();
return _types.dynamicType;
}
@@ -850,10 +849,32 @@
return new ArgumentsTypes(positional, named);
}
+ AbstractValue _typeOfReceiver(ir.TreeNode node, ir.TreeNode receiver) {
+ KernelGlobalTypeInferenceElementData data = _memberData;
+ AbstractValue mask = data.typeOfReceiver(node);
+ if (mask != null) return mask;
+ // TODO(sigmund): ensure that this is only called once per node.
+ DartType staticType = _getStaticType(receiver);
+ // TODO(sigmund): this needs to be adjusted when we enable non-nullable
+ // types to handle legacy and nullable wrappers.
+ if (staticType is InterfaceType) {
+ ClassEntity cls = staticType.element;
+ if (receiver is ir.ThisExpression && !_closedWorld.isUsedAsMixin(cls)) {
+ mask = _closedWorld.abstractValueDomain.createNonNullSubclass(cls);
+ } else {
+ mask = _closedWorld.abstractValueDomain.createNullableSubtype(cls);
+ }
+ data.setReceiverTypeMask(node, mask);
+ return mask;
+ }
+ // TODO(sigmund): consider also extracting the bound of type parameters.
+ return null;
+ }
+
@override
TypeInformation visitMethodInvocation(ir.MethodInvocation node) {
Selector selector = _elementMap.getSelector(node);
- AbstractValue mask = _memberData.typeOfSend(node);
+ AbstractValue mask = _typeOfReceiver(node, node.receiver);
ir.TreeNode receiver = node.receiver;
if (receiver is ir.VariableGet &&
@@ -867,7 +888,7 @@
}
TypeInformation type =
- handleStaticInvoke(node, selector, mask, info.callMethod, arguments);
+ handleStaticInvoke(node, selector, info.callMethod, arguments);
FunctionType functionType =
_elementMap.elementEnvironment.getFunctionType(info.callMethod);
if (functionType.returnType.containsFreeTypeVariables) {
@@ -1028,8 +1049,8 @@
_closedWorld.commonElements.streamIteratorConstructor;
/// Synthesize a call to the [StreamIterator] constructor.
- iteratorType = handleStaticInvoke(node, null, null, constructor,
- new ArgumentsTypes([expressionType], null));
+ iteratorType = handleStaticInvoke(
+ node, null, constructor, new ArgumentsTypes([expressionType], null));
} else {
TypeInformation expressionType = visit(node.iterable);
Selector iteratorSelector = Selectors.iterator;
@@ -1118,9 +1139,8 @@
ConstructorEntity constructor = _elementMap.getConstructor(node.target);
ArgumentsTypes arguments = analyzeArguments(node.arguments);
Selector selector = _elementMap.getSelector(node);
- AbstractValue mask = _memberData.typeOfSend(node);
return handleConstructorInvoke(
- node, node.arguments, selector, mask, constructor, arguments);
+ node, node.arguments, selector, constructor, arguments);
}
/// Try to find the length given to a fixed array constructor call.
@@ -1176,11 +1196,10 @@
ir.Node node,
ir.Arguments arguments,
Selector selector,
- AbstractValue mask,
ConstructorEntity constructor,
ArgumentsTypes argumentsTypes) {
TypeInformation returnType =
- handleStaticInvoke(node, selector, mask, constructor, argumentsTypes);
+ handleStaticInvoke(node, selector, constructor, argumentsTypes);
if (_elementMap.commonElements.isUnnamedListConstructor(constructor)) {
// We have `new List(...)`.
if (arguments.positional.isEmpty && arguments.named.isEmpty) {
@@ -1227,17 +1246,16 @@
}
TypeInformation handleStaticInvoke(ir.Node node, Selector selector,
- AbstractValue mask, MemberEntity element, ArgumentsTypes arguments) {
- return _inferrer.registerCalledMember(node, selector, mask, _analyzedMember,
+ MemberEntity element, ArgumentsTypes arguments) {
+ return _inferrer.registerCalledMember(node, selector, _analyzedMember,
element, arguments, _sideEffectsBuilder, inLoop);
}
TypeInformation handleClosureCall(ir.Node node, Selector selector,
- AbstractValue mask, MemberEntity member, ArgumentsTypes arguments) {
+ MemberEntity member, ArgumentsTypes arguments) {
return _inferrer.registerCalledClosure(
node,
selector,
- mask,
_inferrer.typeOfMember(member),
_analyzedMember,
arguments,
@@ -1245,14 +1263,10 @@
inLoop: inLoop);
}
- TypeInformation handleForeignInvoke(
- ir.StaticInvocation node,
- FunctionEntity function,
- ArgumentsTypes arguments,
- Selector selector,
- AbstractValue mask) {
+ TypeInformation handleForeignInvoke(ir.StaticInvocation node,
+ FunctionEntity function, ArgumentsTypes arguments, Selector selector) {
String name = function.name;
- handleStaticInvoke(node, selector, mask, function, arguments);
+ handleStaticInvoke(node, selector, function, arguments);
if (name == Identifiers.JS) {
NativeBehavior nativeBehavior =
_elementMap.getNativeBehaviorForJsCall(node);
@@ -1281,16 +1295,15 @@
MemberEntity member = _elementMap.getMember(node.target);
ArgumentsTypes arguments = analyzeArguments(node.arguments);
Selector selector = _elementMap.getSelector(node);
- AbstractValue mask = _memberData.typeOfSend(node);
if (_closedWorld.commonElements.isForeign(member)) {
- return handleForeignInvoke(node, member, arguments, selector, mask);
+ return handleForeignInvoke(node, member, arguments, selector);
} else if (member.isConstructor) {
return handleConstructorInvoke(
- node, node.arguments, selector, mask, member, arguments);
+ node, node.arguments, selector, member, arguments);
} else {
assert(member.isFunction, "Unexpected static invocation target: $member");
TypeInformation type =
- handleStaticInvoke(node, selector, mask, member, arguments);
+ handleStaticInvoke(node, selector, member, arguments);
FunctionType functionType =
_elementMap.elementEnvironment.getFunctionType(member);
if (functionType.returnType.containsFreeTypeVariables) {
@@ -1310,16 +1323,14 @@
@override
TypeInformation visitStaticGet(ir.StaticGet node) {
- AbstractValue mask = _memberData.typeOfSend(node);
- assert(mask == null);
- return createStaticGetTypeInformation(node, node.target, mask: mask);
+ return createStaticGetTypeInformation(node, node.target);
}
- TypeInformation createStaticGetTypeInformation(ir.Node node, ir.Member target,
- {AbstractValue mask}) {
+ TypeInformation createStaticGetTypeInformation(
+ ir.Node node, ir.Member target) {
MemberEntity member = _elementMap.getMember(target);
return handleStaticInvoke(
- node, new Selector.getter(member.memberName), mask, member, null);
+ node, new Selector.getter(member.memberName), member, null);
}
@override
@@ -1329,17 +1340,16 @@
_state.markThisAsExposed();
}
MemberEntity member = _elementMap.getMember(node.target);
- AbstractValue mask = _memberData.typeOfSend(node);
- handleStaticInvoke(node, new Selector.setter(member.memberName), mask,
- member, new ArgumentsTypes([rhsType], null));
+ handleStaticInvoke(node, new Selector.setter(member.memberName), member,
+ new ArgumentsTypes([rhsType], null));
return rhsType;
}
- TypeInformation handlePropertyGet(
- ir.TreeNode node, TypeInformation receiverType, ir.Member interfaceTarget,
+ TypeInformation handlePropertyGet(ir.TreeNode node, ir.TreeNode receiver,
+ TypeInformation receiverType, ir.Member interfaceTarget,
{bool isThis}) {
Selector selector = _elementMap.getSelector(node);
- AbstractValue mask = _memberData.typeOfSend(node);
+ AbstractValue mask = _typeOfReceiver(node, receiver);
if (isThis) {
_checkIfExposesThis(
selector, _types.newTypedSelector(receiverType, mask));
@@ -1362,21 +1372,23 @@
@override
TypeInformation visitPropertyGet(ir.PropertyGet node) {
TypeInformation receiverType = visit(node.receiver);
- return handlePropertyGet(node, receiverType, node.interfaceTarget,
+ return handlePropertyGet(
+ node, node.receiver, receiverType, node.interfaceTarget,
isThis: node.receiver is ir.ThisExpression);
}
@override
TypeInformation visitDirectPropertyGet(ir.DirectPropertyGet node) {
TypeInformation receiverType = thisType;
- return handlePropertyGet(node, receiverType, node.target, isThis: true);
+ return handlePropertyGet(node, node.receiver, receiverType, node.target,
+ isThis: true);
}
@override
TypeInformation visitPropertySet(ir.PropertySet node) {
TypeInformation receiverType = visit(node.receiver);
Selector selector = _elementMap.getSelector(node);
- AbstractValue mask = _memberData.typeOfSend(node);
+ AbstractValue mask = _typeOfReceiver(node, node.receiver);
TypeInformation rhsType = visit(node.value);
if (node.value is ir.ThisExpression) {
@@ -1729,13 +1741,13 @@
return _types.nonNullEmpty();
}
- TypeInformation handleSuperNoSuchMethod(ir.Node node, Selector selector,
- AbstractValue mask, ArgumentsTypes arguments) {
+ TypeInformation handleSuperNoSuchMethod(
+ ir.Node node, Selector selector, ArgumentsTypes arguments) {
// Ensure we create a node, to make explicit the call to the
// `noSuchMethod` handler.
FunctionEntity noSuchMethod =
_elementMap.getSuperNoSuchMethod(_analyzedMember.enclosingClass);
- return handleStaticInvoke(node, selector, mask, noSuchMethod, arguments);
+ return handleStaticInvoke(node, selector, noSuchMethod, arguments);
}
@override
@@ -1747,10 +1759,8 @@
MemberEntity member =
_elementMap.getSuperMember(_analyzedMember, node.name);
assert(member != null, "No member found for super property get: $node");
- AbstractValue mask = _memberData.typeOfSend(node);
Selector selector = new Selector.getter(_elementMap.getName(node.name));
- TypeInformation type =
- handleStaticInvoke(node, selector, mask, member, null);
+ TypeInformation type = handleStaticInvoke(node, selector, member, null);
if (member.isGetter) {
FunctionType functionType =
_elementMap.elementEnvironment.getFunctionType(member);
@@ -1780,10 +1790,9 @@
MemberEntity member =
_elementMap.getSuperMember(_analyzedMember, node.name, setter: true);
assert(member != null, "No member found for super property set: $node");
- AbstractValue mask = _memberData.typeOfSend(node);
Selector selector = new Selector.setter(_elementMap.getName(node.name));
ArgumentsTypes arguments = new ArgumentsTypes([rhsType], null);
- handleStaticInvoke(node, selector, mask, member, arguments);
+ handleStaticInvoke(node, selector, member, arguments);
return rhsType;
}
@@ -1797,17 +1806,16 @@
_elementMap.getSuperMember(_analyzedMember, node.name);
ArgumentsTypes arguments = analyzeArguments(node.arguments);
Selector selector = _elementMap.getSelector(node);
- AbstractValue mask = _memberData.typeOfSend(node);
if (member == null) {
// TODO(johnniwinther): This shouldn't be necessary.
- return handleSuperNoSuchMethod(node, selector, mask, arguments);
+ return handleSuperNoSuchMethod(node, selector, arguments);
} else {
assert(member.isFunction, "Unexpected super invocation target: $member");
if (isIncompatibleInvoke(member, arguments)) {
- return handleSuperNoSuchMethod(node, selector, mask, arguments);
+ return handleSuperNoSuchMethod(node, selector, arguments);
} else {
TypeInformation type =
- handleStaticInvoke(node, selector, mask, member, arguments);
+ handleStaticInvoke(node, selector, member, arguments);
FunctionType functionType =
_elementMap.elementEnvironment.getFunctionType(member);
if (functionType.returnType.containsFreeTypeVariables) {
diff --git a/pkg/compiler/lib/src/inferrer/closure_tracer.dart b/pkg/compiler/lib/src/inferrer/closure_tracer.dart
index 3ee6a53..0a01c9b 100644
--- a/pkg/compiler/lib/src/inferrer/closure_tracer.dart
+++ b/pkg/compiler/lib/src/inferrer/closure_tracer.dart
@@ -7,7 +7,6 @@
import '../common/names.dart' show Identifiers, Names;
import '../elements/entities.dart';
import '../universe/selector.dart' show Selector;
-import 'abstract_value_domain.dart';
import 'debug.dart' as debug;
import 'inferrer_engine.dart';
import 'node_tracer.dart';
@@ -56,14 +55,13 @@
void _analyzeCall(CallSiteTypeInformation info) {
Selector selector = info.selector;
- AbstractValue mask = info.mask;
tracedElements.forEach((FunctionEntity functionElement) {
if (!selector.callStructure
.signatureApplies(functionElement.parameterStructure)) {
return;
}
- inferrer.updateParameterAssignments(
- info, functionElement, info.arguments, selector, mask,
+ inferrer.updateParameterInputs(
+ info, functionElement, info.arguments, selector,
remove: false, addToQueue: false);
});
}
diff --git a/pkg/compiler/lib/src/inferrer/inferrer_engine.dart b/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
index 98c43b2..a510d20 100644
--- a/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
+++ b/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
@@ -43,7 +43,7 @@
/// An inferencing engine that computes a call graph of [TypeInformation] nodes
/// by visiting the AST of the application, and then does the inferencing on the
/// graph.
-abstract class InferrerEngine {
+class InferrerEngine {
/// A set of selector names that [List] implements, that we know return their
/// element type.
final Set<Selector> returnsListElementTypeSet =
@@ -58,11 +58,45 @@
new Selector.call(const PublicName('removeLast'), CallStructure.NO_ARGS)
]);
- CompilerOptions get options;
-
/// The [JClosedWorld] on which inference reasoning is based.
- JClosedWorld get closedWorld;
- DiagnosticReporter get reporter;
+ final JsClosedWorld closedWorld;
+
+ final TypeSystem types;
+ final Map<ir.TreeNode, TypeInformation> concreteTypes = {};
+ final InferredDataBuilder inferredDataBuilder;
+
+ final FunctionEntity mainElement;
+
+ final Map<Local, TypeInformation> _defaultTypeOfParameter =
+ new Map<Local, TypeInformation>();
+
+ final WorkQueue _workQueue = new WorkQueue();
+
+ final Set<MemberEntity> _analyzedElements = new Set<MemberEntity>();
+
+ /// The maximum number of times we allow a node in the graph to
+ /// change types. If a node reaches that limit, we give up
+ /// inferencing on it and give it the dynamic type.
+ final int _MAX_CHANGE_COUNT = 6;
+
+ int _overallRefineCount = 0;
+ int _addedInGraph = 0;
+
+ final CompilerOptions _options;
+ final Progress _progress;
+ final DiagnosticReporter _reporter;
+ final CompilerOutput _compilerOutput;
+
+ final Set<ConstructorEntity> _generativeConstructorsExposingThis =
+ new Set<ConstructorEntity>();
+
+ /// Data computed internally within elements, like the type-mask of a send a
+ /// list allocation, or a for-in loop.
+ final Map<MemberEntity, GlobalTypeInferenceElementData> _memberData =
+ new Map<MemberEntity, GlobalTypeInferenceElementData>();
+
+ ElementEnvironment get _elementEnvironment => closedWorld.elementEnvironment;
+
AbstractValueDomain get abstractValueDomain =>
closedWorld.abstractValueDomain;
CommonElements get commonElements => closedWorld.commonElements;
@@ -71,255 +105,19 @@
// [ClosureWorldRefiner].
NoSuchMethodData get noSuchMethodData => closedWorld.noSuchMethodData;
- TypeSystem get types;
- Map<ir.TreeNode, TypeInformation> get concreteTypes;
- InferredDataBuilder get inferredDataBuilder;
-
- FunctionEntity get mainElement;
-
- void runOverAllElements();
-
- void analyze(MemberEntity member);
- void analyzeListAndEnqueue(ListTypeInformation info);
- void analyzeSetAndEnqueue(SetTypeInformation info);
- void analyzeMapAndEnqueue(MapTypeInformation info);
-
- /// Notifies to the inferrer that [analyzedElement] can have return type
- /// [newType]. [currentType] is the type the inference has currently found.
- ///
- /// Returns the new type for [analyzedElement].
- TypeInformation addReturnTypeForMethod(
- FunctionEntity element, TypeInformation unused, TypeInformation newType);
-
- /// Applies [f] to all elements in the universe that match [selector] and
- /// [mask]. If [f] returns false, aborts the iteration.
- void forEachElementMatching(
- Selector selector, AbstractValue mask, bool f(MemberEntity element));
-
- /// Returns the [TypeInformation] node for the default value of a parameter.
- /// If this is queried before it is set by [setDefaultTypeOfParameter], a
- /// [PlaceholderTypeInformation] is returned, which will later be replaced
- /// by the actual node when [setDefaultTypeOfParameter] is called.
- ///
- /// Invariant: After graph construction, no [PlaceholderTypeInformation] nodes
- /// should be present and a default type for each parameter should exist.
- TypeInformation getDefaultTypeOfParameter(Local parameter);
-
- /// This helper breaks abstractions but is currently required to work around
- /// the wrong modeling of default values of optional parameters of
- /// synthetic constructors.
- ///
- /// TODO(johnniwinther): Remove once default values of synthetic parameters
- /// are fixed.
- bool hasAlreadyComputedTypeOfParameterDefault(Local parameter);
-
- /// Sets the type of a parameter's default value to [type]. If the global
- /// mapping in [defaultTypeOfParameter] already contains a type, it must be
- /// a [PlaceholderTypeInformation], which will be replaced. All its uses are
- /// updated.
- void setDefaultTypeOfParameter(Local parameter, TypeInformation type,
- {bool isInstanceMember});
-
- Iterable<MemberEntity> getCallersOfForTesting(MemberEntity element);
-
- // TODO(johnniwinther): Make this private again.
- GlobalTypeInferenceElementData dataOfMember(MemberEntity element);
-
- bool checkIfExposesThis(ConstructorEntity element);
-
- void recordExposesThis(ConstructorEntity element, bool exposesThis);
-
- /// Records that the return type [element] is of type [type].
- void recordReturnType(FunctionEntity element, TypeInformation type);
-
- /// Records that [element] is of type [type].
- void recordTypeOfField(FieldEntity element, TypeInformation type);
-
- /// Registers a call to await with an expression of type [argumentType] as
- /// argument.
- TypeInformation registerAwait(ir.Node node, TypeInformation argument);
-
- /// Registers a call to yield with an expression of type [argumentType] as
- /// argument.
- TypeInformation registerYield(ir.Node node, TypeInformation argument);
-
- /// Registers that [caller] calls [closure] with [arguments].
- ///
- /// [sideEffectsBuilder] will be updated to incorporate the potential callees'
- /// side effects.
- ///
- /// [inLoop] tells whether the call happens in a loop.
- TypeInformation registerCalledClosure(
- ir.Node node,
- Selector selector,
- AbstractValue mask,
- TypeInformation closure,
- MemberEntity caller,
- ArgumentsTypes arguments,
- SideEffectsBuilder sideEffectsBuilder,
- {bool inLoop});
-
- /// Registers that [caller] calls [callee] at location [node], with
- /// [selector], and [arguments]. Note that [selector] is null for forwarding
- /// constructors.
- ///
- /// [sideEffectsBuilder] will be updated to incorporate [callee]'s side
- /// effects.
- ///
- /// [inLoop] tells whether the call happens in a loop.
- TypeInformation registerCalledMember(
- Object node,
- Selector selector,
- AbstractValue mask,
- MemberEntity caller,
- MemberEntity callee,
- ArgumentsTypes arguments,
- SideEffectsBuilder sideEffectsBuilder,
- bool inLoop);
-
- /// Registers that [caller] calls [selector] with [receiverType] as receiver,
- /// and [arguments].
- ///
- /// [sideEffectsBuilder] will be updated to incorporate the potential callees'
- /// side effects.
- ///
- /// [inLoop] tells whether the call happens in a loop.
- TypeInformation registerCalledSelector(
- CallType callType,
- ir.Node node,
- Selector selector,
- AbstractValue mask,
- TypeInformation receiverType,
- MemberEntity caller,
- ArgumentsTypes arguments,
- SideEffectsBuilder sideEffectsBuilder,
- {bool inLoop,
- bool isConditional});
-
- /// Update the assignments to parameters in the graph. [remove] tells whether
- /// assignments must be added or removed. If [init] is false, parameters are
- /// added to the work queue.
- void updateParameterAssignments(TypeInformation caller, MemberEntity callee,
- ArgumentsTypes arguments, Selector selector, AbstractValue mask,
- {bool remove, bool addToQueue: true});
-
- void updateSelectorInMember(MemberEntity owner, CallType callType,
- ir.Node node, Selector selector, AbstractValue mask);
-
- /// Returns the return type of [element].
- TypeInformation returnTypeOfMember(MemberEntity element);
-
- /// Returns the type of [element] when being called with [selector].
- TypeInformation typeOfMemberWithSelector(
- MemberEntity element, Selector selector);
-
- /// Returns the type of [element].
- TypeInformation typeOfMember(MemberEntity element);
-
- /// Returns the type of [element].
- TypeInformation typeOfParameter(Local element);
-
- /// Returns the type for [nativeBehavior]. See documentation on
- /// [NativeBehavior].
- TypeInformation typeOfNativeBehavior(NativeBehavior nativeBehavior);
-
- /// For a given selector, return a shared dynamic call site that will be used
- /// to combine the results of multiple dynamic calls in the program via
- /// [IndirectDynamicCallSiteTypeInformation].
- ///
- /// This is used only for scalability reasons: if there are too many targets
- /// and call sites, we may have a quadratic number of edges in the graph, so
- /// we add a level of indirection to merge the information and keep the graph
- /// smaller.
- DynamicCallSiteTypeInformation typeOfSharedDynamicCall(
- Selector selector, CallStructure structure);
-
- bool returnsListElementType(Selector selector, AbstractValue mask);
-
- bool returnsMapValueType(Selector selector, AbstractValue mask);
-
- void close();
-
- void clear();
-
- /// Returns true if global optimizations such as type inferencing can apply to
- /// the field [element].
- ///
- /// One category of elements that do not apply is runtime helpers that the
- /// backend calls, but the optimizations don't see those calls.
- bool canFieldBeUsedForGlobalOptimizations(FieldEntity element);
-
- /// Returns true if global optimizations such as type inferencing can apply to
- /// the parameter [element].
- ///
- /// One category of elements that do not apply is runtime helpers that the
- /// backend calls, but the optimizations don't see those calls.
- bool canFunctionParametersBeUsedForGlobalOptimizations(
- FunctionEntity function);
-
- /// Returns `true` if inference of parameter types is disabled for [member].
- bool assumeDynamic(MemberEntity member);
-}
-
-class InferrerEngineImpl extends InferrerEngine {
- final Map<Local, TypeInformation> defaultTypeOfParameter =
- new Map<Local, TypeInformation>();
- final WorkQueue workQueue = new WorkQueue();
- @override
- final FunctionEntity mainElement;
- final Set<MemberEntity> analyzedElements = new Set<MemberEntity>();
-
- /// The maximum number of times we allow a node in the graph to
- /// change types. If a node reaches that limit, we give up
- /// inferencing on it and give it the dynamic type.
- final int MAX_CHANGE_COUNT = 6;
-
- int overallRefineCount = 0;
- int addedInGraph = 0;
-
- @override
- final CompilerOptions options;
- final Progress progress;
- @override
- final DiagnosticReporter reporter;
- final CompilerOutput _compilerOutput;
-
- @override
- final JsClosedWorld closedWorld;
- @override
- final InferredDataBuilder inferredDataBuilder;
-
- @override
- final TypeSystem types;
- @override
- final Map<ir.TreeNode, TypeInformation> concreteTypes =
- new Map<ir.TreeNode, TypeInformation>();
-
- final Set<ConstructorEntity> generativeConstructorsExposingThis =
- new Set<ConstructorEntity>();
-
- /// Data computed internally within elements, like the type-mask of a send a
- /// list allocation, or a for-in loop.
- final Map<MemberEntity, GlobalTypeInferenceElementData> _memberData =
- new Map<MemberEntity, GlobalTypeInferenceElementData>();
-
- final NoSuchMethodRegistry noSuchMethodRegistry;
-
- InferrerEngineImpl(
- this.options,
- this.progress,
- this.reporter,
+ InferrerEngine(
+ this._options,
+ this._progress,
+ this._reporter,
this._compilerOutput,
this.closedWorld,
- this.noSuchMethodRegistry,
this.mainElement,
this.inferredDataBuilder)
: this.types = new TypeSystem(
closedWorld, new KernelTypeSystemStrategy(closedWorld));
- ElementEnvironment get _elementEnvironment => closedWorld.elementEnvironment;
-
- @override
+ /// Applies [f] to all elements in the universe that match [selector] and
+ /// [mask]. If [f] returns false, aborts the iteration.
void forEachElementMatching(
Selector selector, AbstractValue mask, bool f(MemberEntity element)) {
Iterable<MemberEntity> elements = closedWorld.locateMembers(selector, mask);
@@ -329,13 +127,12 @@
}
// TODO(johnniwinther): Make this private again.
- @override
GlobalTypeInferenceElementData dataOfMember(MemberEntity element) =>
_memberData[element] ??= new KernelGlobalTypeInferenceElementData();
/// Update [sideEffects] with the side effects of [callee] being
/// called with [selector].
- void updateSideEffects(SideEffectsBuilder sideEffectsBuilder,
+ void _updateSideEffects(SideEffectsBuilder sideEffectsBuilder,
Selector selector, MemberEntity callee) {
if (callee.isField) {
if (callee.isInstanceMember) {
@@ -363,7 +160,8 @@
}
}
- @override
+ /// Returns the type for [nativeBehavior]. See documentation on
+ /// [NativeBehavior].
TypeInformation typeOfNativeBehavior(NativeBehavior nativeBehavior) {
if (nativeBehavior == null) return types.dynamicType;
List typesReturned = nativeBehavior.typesReturned;
@@ -404,14 +202,13 @@
return returnType;
}
- @override
void updateSelectorInMember(MemberEntity owner, CallType callType,
ir.Node node, Selector selector, AbstractValue mask) {
KernelGlobalTypeInferenceElementData data = dataOfMember(owner);
assert(validCallType(callType, node, selector));
switch (callType) {
case CallType.access:
- data.setTypeMask(node, mask);
+ data.setReceiverTypeMask(node, mask);
break;
case CallType.indirectAccess:
// indirect access is not diretly recorded in the result data.
@@ -429,31 +226,26 @@
}
}
- @override
bool checkIfExposesThis(ConstructorEntity element) {
- return generativeConstructorsExposingThis.contains(element);
+ return _generativeConstructorsExposingThis.contains(element);
}
- @override
void recordExposesThis(ConstructorEntity element, bool exposesThis) {
if (exposesThis) {
- generativeConstructorsExposingThis.add(element);
+ _generativeConstructorsExposingThis.add(element);
}
}
- @override
bool returnsListElementType(Selector selector, AbstractValue mask) {
return mask != null &&
abstractValueDomain.isContainer(mask) &&
returnsListElementTypeSet.contains(selector);
}
- @override
bool returnsMapValueType(Selector selector, AbstractValue mask) {
return mask != null && abstractValueDomain.isMap(mask) && selector.isIndex;
}
- @override
void analyzeListAndEnqueue(ListTypeInformation info) {
if (info.analyzed) return;
info.analyzed = true;
@@ -468,13 +260,12 @@
info.originalType, abstractValueDomain.fixedListType)) {
info.checksGrowable = tracer.callsGrowableMethod;
}
- tracer.assignments.forEach(info.elementType.addAssignment);
+ tracer.inputs.forEach(info.elementType.addInput);
// Enqueue the list for later refinement
- workQueue.add(info);
- workQueue.add(info.elementType);
+ _workQueue.add(info);
+ _workQueue.add(info.elementType);
}
- @override
void analyzeSetAndEnqueue(SetTypeInformation info) {
if (info.analyzed) return;
info.analyzed = true;
@@ -486,13 +277,12 @@
info.bailedOut = false;
info.elementType.inferred = true;
- tracer.assignments.forEach(info.elementType.addAssignment);
+ tracer.inputs.forEach(info.elementType.addInput);
// Enqueue the set for later refinement.
- workQueue.add(info);
- workQueue.add(info.elementType);
+ _workQueue.add(info);
+ _workQueue.add(info.elementType);
}
- @override
void analyzeMapAndEnqueue(MapTypeInformation info) {
if (info.analyzed) return;
info.analyzed = true;
@@ -502,31 +292,30 @@
if (!succeeded) return;
info.bailedOut = false;
- for (int i = 0; i < tracer.keyAssignments.length; ++i) {
- TypeInformation newType = info.addEntryAssignment(abstractValueDomain,
- tracer.keyAssignments[i], tracer.valueAssignments[i]);
- if (newType != null) workQueue.add(newType);
+ for (int i = 0; i < tracer.keyInputs.length; ++i) {
+ TypeInformation newType = info.addEntryInput(
+ abstractValueDomain, tracer.keyInputs[i], tracer.valueInputs[i]);
+ if (newType != null) _workQueue.add(newType);
}
- for (TypeInformation map in tracer.mapAssignments) {
- workQueue.addAll(info.addMapAssignment(abstractValueDomain, map));
+ for (TypeInformation map in tracer.mapInputs) {
+ _workQueue.addAll(info.addMapInput(abstractValueDomain, map));
}
info.markAsInferred();
- workQueue.add(info.keyType);
- workQueue.add(info.valueType);
- workQueue.addAll(info.typeInfoMap.values);
- workQueue.add(info);
+ _workQueue.add(info.keyType);
+ _workQueue.add(info.valueType);
+ _workQueue.addAll(info.typeInfoMap.values);
+ _workQueue.add(info);
}
- @override
void runOverAllElements() {
- analyzeAllElements();
+ _analyzeAllElements();
TypeGraphDump dump =
debug.PRINT_GRAPH ? new TypeGraphDump(_compilerOutput, this) : null;
dump?.beforeAnalysis();
- buildWorkQueue();
- refine();
+ _buildWorkQueue();
+ _refine();
// Try to infer element types of lists and compute their escape information.
types.allocatedLists.values.forEach((TypeInformation info) {
@@ -560,7 +349,7 @@
types.strategy.forEachParameter(element, (Local parameter) {
types
.getInferredTypeOfParameter(parameter)
- .giveUp(this, clearAssignments: false);
+ .giveUp(this, clearInputs: false);
});
});
bailedOutOn.addAll(elements);
@@ -573,7 +362,7 @@
ParameterTypeInformation info =
types.getInferredTypeOfParameter(parameter);
info.maybeResume();
- workQueue.add(info);
+ _workQueue.add(info);
});
if (tracer.tracedType.mightBePassedToFunctionApply) {
inferredDataBuilder.registerMightBePassedToApply(element);
@@ -598,7 +387,7 @@
assert(calledElement is ConstructorEntity &&
calledElement.isGenerativeConstructor);
ClassEntity cls = calledElement.enclosingClass;
- FunctionEntity callMethod = lookupCallMethod(cls);
+ FunctionEntity callMethod = _lookupCallMethod(cls);
assert(callMethod != null, failedAt(cls));
Iterable<FunctionEntity> elements = [callMethod];
trace(elements, new ClosureTracerVisitor(elements, info, this));
@@ -625,17 +414,17 @@
// as nodes that use elements fetched from these lists/maps. The
// workset for a new run of the analysis will be these nodes.
Set<TypeInformation> seenTypes = new Set<TypeInformation>();
- while (!workQueue.isEmpty) {
- TypeInformation info = workQueue.remove();
+ while (!_workQueue.isEmpty) {
+ TypeInformation info = _workQueue.remove();
if (seenTypes.contains(info)) continue;
// If the node cannot be reset, we do not need to update its users either.
if (!info.reset(this)) continue;
seenTypes.add(info);
- workQueue.addAll(info.users);
+ _workQueue.addAll(info.users);
}
- workQueue.addAll(seenTypes);
- refine();
+ _workQueue.addAll(seenTypes);
+ _refine();
if (debug.PRINT_SUMMARY) {
types.allocatedLists.values.forEach((_info) {
@@ -681,42 +470,42 @@
}
} else if (info is StaticCallSiteTypeInformation) {
ClassEntity cls = info.calledElement.enclosingClass;
- FunctionEntity callMethod = lookupCallMethod(cls);
+ FunctionEntity callMethod = _lookupCallMethod(cls);
print('${types.getInferredSignatureOfMethod(callMethod)} for ${cls}');
} else {
print('${info.type} for some unknown kind of closure');
}
});
- analyzedElements.forEach((MemberEntity elem) {
+ _analyzedElements.forEach((MemberEntity elem) {
TypeInformation type = types.getInferredTypeOfMember(elem);
- print('${elem} :: ${type} from ${type.assignments} ');
+ print('${elem} :: ${type} from ${type.inputs} ');
});
}
dump?.afterAnalysis();
- reporter.log('Inferred $overallRefineCount types.');
+ _reporter.log('Inferred $_overallRefineCount types.');
- processLoopInformation();
+ _processLoopInformation();
}
/// Call [analyze] for all live members.
- void analyzeAllElements() {
+ void _analyzeAllElements() {
Iterable<MemberEntity> processedMembers = closedWorld.processedMembers
.where((MemberEntity member) => !member.isAbstract);
- progress.startPhase();
+ _progress.startPhase();
processedMembers.forEach((MemberEntity member) {
- progress.showProgress(
- 'Added ', addedInGraph, ' elements in inferencing graph.');
+ _progress.showProgress(
+ 'Added ', _addedInGraph, ' elements in inferencing graph.');
// This also forces the creation of the [ElementTypeInformation] to ensure
// it is in the graph.
types.withMember(member, () => analyze(member));
});
- reporter.log('Added $addedInGraph elements in inferencing graph.');
+ _reporter.log('Added $_addedInGraph elements in inferencing graph.');
}
/// Returns the body node for [member].
- ir.Node computeMemberBody(MemberEntity member) {
+ ir.Node _computeMemberBody(MemberEntity member) {
MemberDefinition definition =
closedWorld.elementMap.getMemberDefinition(member);
switch (definition.kind) {
@@ -752,7 +541,7 @@
/// Returns the `call` method on [cls] or the `noSuchMethod` if [cls] doesn't
/// implement `call`.
- FunctionEntity lookupCallMethod(ClassEntity cls) {
+ FunctionEntity _lookupCallMethod(ClassEntity cls) {
FunctionEntity function =
_elementEnvironment.lookupClassMember(cls, Identifiers.call);
if (function == null || function.isAbstract) {
@@ -762,18 +551,17 @@
return function;
}
- @override
void analyze(MemberEntity element) {
- if (analyzedElements.contains(element)) return;
- analyzedElements.add(element);
+ if (_analyzedElements.contains(element)) return;
+ _analyzedElements.add(element);
- ir.Node body = computeMemberBody(element);
+ ir.Node body = _computeMemberBody(element);
TypeInformation type;
- reporter.withCurrentElement(element, () {
- type = computeMemberTypeInformation(element, body);
+ _reporter.withCurrentElement(element, () {
+ type = _computeMemberTypeInformation(element, body);
});
- addedInGraph++;
+ _addedInGraph++;
if (element.isField) {
FieldEntity field = element;
@@ -784,7 +572,7 @@
if (type is! ListTypeInformation && type is! MapTypeInformation) {
// For non-container types, the constant handler does
// constant folding that could give more precise results.
- ConstantValue value = getFieldConstant(field);
+ ConstantValue value = _getFieldConstant(field);
if (value != null) {
if (value.isFunction) {
FunctionConstantValue functionConstant = value;
@@ -818,7 +606,7 @@
if ((element.isStatic || element.isTopLevel) &&
body != null &&
!element.isConst) {
- if (isFieldInitializerPotentiallyNull(element, body)) {
+ if (_isFieldInitializerPotentiallyNull(element, body)) {
recordTypeOfField(field, types.nullType);
}
}
@@ -829,10 +617,10 @@
}
/// Visits [body] to compute the [TypeInformation] node for [member].
- TypeInformation computeMemberTypeInformation(
+ TypeInformation _computeMemberTypeInformation(
MemberEntity member, ir.Node body) {
KernelTypeGraphBuilder visitor = new KernelTypeGraphBuilder(
- options,
+ _options,
closedWorld,
this,
member,
@@ -844,7 +632,7 @@
/// Returns `true` if the [initializer] of the non-const static or top-level
/// [field] is potentially `null`.
- bool isFieldInitializerPotentiallyNull(
+ bool _isFieldInitializerPotentiallyNull(
FieldEntity field, ir.Node initializer) {
// TODO(13429): We could do better here by using the
// constant handler to figure out if it's a lazy field or not.
@@ -866,18 +654,18 @@
/// Returns the [ConstantValue] for the initial value of [field], or
/// `null` if the initializer is not a constant value.
- ConstantValue getFieldConstant(FieldEntity field) {
+ ConstantValue _getFieldConstant(FieldEntity field) {
return closedWorld.fieldAnalysis.getFieldData(field).initialValue;
}
/// Returns `true` if [cls] has a 'call' method.
- bool hasCallType(ClassEntity cls) {
+ bool _hasCallType(ClassEntity cls) {
return closedWorld.elementMap.types
.getCallType(closedWorld.elementEnvironment.getThisType(cls)) !=
null;
}
- void processLoopInformation() {
+ void _processLoopInformation() {
types.allocatedCalls.forEach((CallSiteTypeInformation info) {
if (!info.inLoop) return;
// We can't compute the callees of closures, no new information to add.
@@ -887,7 +675,8 @@
if (info is StaticCallSiteTypeInformation) {
MemberEntity member = info.calledElement;
inferredDataBuilder.addFunctionCalledInLoop(member);
- } else if (info.mask != null &&
+ } else if (info is DynamicCallSiteTypeInformation &&
+ info.mask != null &&
abstractValueDomain.containsAll(info.mask).isDefinitelyFalse) {
// For instance methods, we only register a selector called in a
// loop if it is a typed selector, to avoid marking too many
@@ -900,20 +689,20 @@
});
}
- void refine() {
- progress.startPhase();
- while (!workQueue.isEmpty) {
- progress.showProgress('Inferred ', overallRefineCount, ' types.');
- TypeInformation info = workQueue.remove();
+ void _refine() {
+ _progress.startPhase();
+ while (!_workQueue.isEmpty) {
+ _progress.showProgress('Inferred ', _overallRefineCount, ' types.');
+ TypeInformation info = _workQueue.remove();
AbstractValue oldType = info.type;
AbstractValue newType = info.refine(this);
// Check that refinement has not accidentally changed the type.
assert(oldType == info.type);
if (info.abandonInferencing) info.doNotEnqueue = true;
if ((info.type = newType) != oldType) {
- overallRefineCount++;
+ _overallRefineCount++;
info.refineCount++;
- if (info.refineCount > MAX_CHANGE_COUNT) {
+ if (info.refineCount > _MAX_CHANGE_COUNT) {
if (debug.ANOMALY_WARN) {
print("ANOMALY WARNING: max refinement reached for $info");
}
@@ -921,7 +710,7 @@
info.type = info.refine(this);
info.doNotEnqueue = true;
}
- workQueue.addAll(info.users);
+ _workQueue.addAll(info.users);
if (info.hasStableType(this)) {
info.stabilize(this);
}
@@ -929,27 +718,29 @@
}
}
- void buildWorkQueue() {
- workQueue.addAll(types.orderedTypeInformations);
- workQueue.addAll(types.allocatedTypes);
- workQueue.addAll(types.allocatedClosures);
- workQueue.addAll(types.allocatedCalls);
+ void _buildWorkQueue() {
+ _workQueue.addAll(types.orderedTypeInformations);
+ _workQueue.addAll(types.allocatedTypes);
+ _workQueue.addAll(types.allocatedClosures);
+ _workQueue.addAll(types.allocatedCalls);
}
- @override
- void updateParameterAssignments(TypeInformation caller, MemberEntity callee,
- ArgumentsTypes arguments, Selector selector, AbstractValue mask,
+ /// Update the inputs to parameters in the graph. [remove] tells whether
+ /// inputs must be added or removed. If [init] is false, parameters are
+ /// added to the work queue.
+ void updateParameterInputs(TypeInformation caller, MemberEntity callee,
+ ArgumentsTypes arguments, Selector selector,
{bool remove, bool addToQueue: true}) {
if (callee.name == Identifiers.noSuchMethod_) return;
if (callee.isField) {
if (selector.isSetter) {
ElementTypeInformation info = types.getInferredTypeOfMember(callee);
if (remove) {
- info.removeAssignment(arguments.positional[0]);
+ info.removeInput(arguments.positional[0]);
} else {
- info.addAssignment(arguments.positional[0]);
+ info.addInput(arguments.positional[0]);
}
- if (addToQueue) workQueue.add(info);
+ if (addToQueue) _workQueue.add(info);
}
} else if (callee.isGetter) {
return;
@@ -972,7 +763,7 @@
ParameterTypeInformation info =
types.getInferredTypeOfParameter(parameter);
info.tagAsTearOffClosureParameter(this);
- if (addToQueue) workQueue.add(info);
+ if (addToQueue) _workQueue.add(info);
});
}
} else {
@@ -991,34 +782,37 @@
if (type == null) type = getDefaultTypeOfParameter(parameter);
TypeInformation info = types.getInferredTypeOfParameter(parameter);
if (remove) {
- info.removeAssignment(type);
+ info.removeInput(type);
} else {
- info.addAssignment(type);
+ info.addInput(type);
}
parameterIndex++;
- if (addToQueue) workQueue.add(info);
+ if (addToQueue) _workQueue.add(info);
});
}
}
- @override
+ /// Sets the type of a parameter's default value to [type]. If the global
+ /// mapping in `_defaultTypeOfParameter` already contains a type, it must be
+ /// a [PlaceholderTypeInformation], which will be replaced. All its uses are
+ /// updated.
void setDefaultTypeOfParameter(Local parameter, TypeInformation type,
{bool isInstanceMember}) {
assert(
type != null, failedAt(parameter, "No default type for $parameter."));
- TypeInformation existing = defaultTypeOfParameter[parameter];
- defaultTypeOfParameter[parameter] = type;
+ TypeInformation existing = _defaultTypeOfParameter[parameter];
+ _defaultTypeOfParameter[parameter] = type;
TypeInformation info = types.getInferredTypeOfParameter(parameter);
if (existing != null && existing is PlaceholderTypeInformation) {
// Replace references to [existing] to use [type] instead.
if (isInstanceMember) {
- ParameterAssignments assignments = info.assignments;
- assignments.replace(existing, type);
+ ParameterInputs inputs = info.inputs;
+ inputs.replace(existing, type);
} else {
- List<TypeInformation> assignments = info.assignments;
- for (int i = 0; i < assignments.length; i++) {
- if (assignments[i] == existing) {
- assignments[i] = type;
+ List<TypeInformation> inputs = info.inputs;
+ for (int i = 0; i < inputs.length; i++) {
+ if (inputs[i] == existing) {
+ inputs[i] = type;
}
}
}
@@ -1029,56 +823,59 @@
}
}
- @override
+ /// Returns the [TypeInformation] node for the default value of a parameter.
+ /// If this is queried before it is set by [setDefaultTypeOfParameter], a
+ /// [PlaceholderTypeInformation] is returned, which will later be replaced
+ /// by the actual node when [setDefaultTypeOfParameter] is called.
+ ///
+ /// Invariant: After graph construction, no [PlaceholderTypeInformation] nodes
+ /// should be present and a default type for each parameter should exist.
TypeInformation getDefaultTypeOfParameter(Local parameter) {
- return defaultTypeOfParameter.putIfAbsent(parameter, () {
+ return _defaultTypeOfParameter.putIfAbsent(parameter, () {
return new PlaceholderTypeInformation(
abstractValueDomain, types.currentMember);
});
}
- @override
- bool hasAlreadyComputedTypeOfParameterDefault(Local parameter) {
- TypeInformation seen = defaultTypeOfParameter[parameter];
- return (seen != null && seen is! PlaceholderTypeInformation);
- }
-
- @override
+ /// Returns the type of [element].
TypeInformation typeOfParameter(Local element) {
return types.getInferredTypeOfParameter(element);
}
- @override
+ /// Returns the type of [element].
TypeInformation typeOfMember(MemberEntity element) {
if (element is FunctionEntity) return types.functionType;
return types.getInferredTypeOfMember(element);
}
- @override
+ /// Returns the return type of [element].
TypeInformation returnTypeOfMember(MemberEntity element) {
if (element is! FunctionEntity) return types.dynamicType;
return types.getInferredTypeOfMember(element);
}
- @override
+ /// Records that [element] is of type [type].
void recordTypeOfField(FieldEntity element, TypeInformation type) {
- types.getInferredTypeOfMember(element).addAssignment(type);
+ types.getInferredTypeOfMember(element).addInput(type);
}
- @override
+ /// Records that the return type [element] is of type [type].
void recordReturnType(FunctionEntity element, TypeInformation type) {
TypeInformation info = types.getInferredTypeOfMember(element);
if (element.name == '==') {
// Even if x.== doesn't return a bool, 'x == null' evaluates to 'false'.
- info.addAssignment(types.boolType);
+ info.addInput(types.boolType);
}
// TODO(ngeoffray): Clean up. We do these checks because
// [SimpleTypesInferrer] deals with two different inferrers.
if (type == null) return;
- if (info.assignments.isEmpty) info.addAssignment(type);
+ if (info.inputs.isEmpty) info.addInput(type);
}
- @override
+ /// Notifies to the inferrer that [analyzedElement] can have return type
+ /// [newType]. [currentType] is the type the inference has currently found.
+ ///
+ /// Returns the new type for [analyzedElement].
TypeInformation addReturnTypeForMethod(
FunctionEntity element, TypeInformation unused, TypeInformation newType) {
TypeInformation type = types.getInferredTypeOfMember(element);
@@ -1087,15 +884,21 @@
if (element is ConstructorEntity && element.isGenerativeConstructor) {
return type;
}
- type.addAssignment(newType);
+ type.addInput(newType);
return type;
}
- @override
+ /// Registers that [caller] calls [callee] at location [node], with
+ /// [selector], and [arguments]. Note that [selector] is null for forwarding
+ /// constructors.
+ ///
+ /// [sideEffectsBuilder] will be updated to incorporate [callee]'s side
+ /// effects.
+ ///
+ /// [inLoop] tells whether the call happens in a loop.
TypeInformation registerCalledMember(
Object node,
Selector selector,
- AbstractValue mask,
MemberEntity caller,
MemberEntity callee,
ArgumentsTypes arguments,
@@ -1108,7 +911,6 @@
caller,
callee,
selector,
- mask,
arguments,
inLoop);
// If this class has a 'call' method then we have essentially created a
@@ -1121,17 +923,23 @@
callee is ConstructorEntity &&
callee.isGenerativeConstructor) {
ClassEntity cls = callee.enclosingClass;
- if (hasCallType(cls)) {
+ if (_hasCallType(cls)) {
types.allocatedClosures.add(info);
}
}
info.addToGraph(this);
types.allocatedCalls.add(info);
- updateSideEffects(sideEffectsBuilder, selector, callee);
+ _updateSideEffects(sideEffectsBuilder, selector, callee);
return info;
}
- @override
+ /// Registers that [caller] calls [selector] with [receiverType] as receiver,
+ /// and [arguments].
+ ///
+ /// [sideEffectsBuilder] will be updated to incorporate the potential callees'
+ /// side effects.
+ ///
+ /// [inLoop] tells whether the call happens in a loop.
TypeInformation registerCalledSelector(
CallType callType,
ir.Node node,
@@ -1144,80 +952,65 @@
{bool inLoop,
bool isConditional}) {
if (selector.isClosureCall) {
- return registerCalledClosure(node, selector, mask, receiverType, caller,
- arguments, sideEffectsBuilder,
+ return registerCalledClosure(
+ node, selector, receiverType, caller, arguments, sideEffectsBuilder,
inLoop: inLoop);
}
if (closedWorld.includesClosureCall(selector, mask)) {
sideEffectsBuilder.setAllSideEffectsAndDependsOnSomething();
}
+
closedWorld.locateMembers(selector, mask).forEach((callee) {
- updateSideEffects(sideEffectsBuilder, selector, callee);
+ _updateSideEffects(sideEffectsBuilder, selector, callee);
});
- CallSiteTypeInformation info;
-
- // We force using indirection for `==` because it is a very common dynamic
- // call site on many apps.
- // TODO(sigmund): it would be even better if we could automatically detect
- // when dynamic calls are growing too big and add the indirection at that
- // point.
- if (selector.name == '==') {
- info = new IndirectDynamicCallSiteTypeInformation(
- abstractValueDomain,
- types.currentMember,
- node,
- typeOfSharedDynamicCall(selector, CallStructure.ONE_ARG),
- caller,
- selector,
- mask,
- receiverType,
- arguments,
- inLoop,
- isConditional);
- } else {
- info = new DynamicCallSiteTypeInformation(
- abstractValueDomain,
- types.currentMember,
- callType,
- node,
- caller,
- selector,
- mask,
- receiverType,
- arguments,
- inLoop,
- isConditional);
- }
+ CallSiteTypeInformation info = new DynamicCallSiteTypeInformation(
+ abstractValueDomain,
+ types.currentMember,
+ callType,
+ node,
+ caller,
+ selector,
+ mask,
+ receiverType,
+ arguments,
+ inLoop,
+ isConditional);
info.addToGraph(this);
types.allocatedCalls.add(info);
return info;
}
- @override
+ /// Registers a call to await with an expression of type [argumentType] as
+ /// argument.
TypeInformation registerAwait(ir.Node node, TypeInformation argument) {
AwaitTypeInformation info = new AwaitTypeInformation(
abstractValueDomain, types.currentMember, node);
- info.addAssignment(argument);
+ info.addInput(argument);
types.allocatedTypes.add(info);
return info;
}
- @override
+ /// Registers a call to yield with an expression of type [argumentType] as
+ /// argument.
TypeInformation registerYield(ir.Node node, TypeInformation argument) {
YieldTypeInformation info = new YieldTypeInformation(
abstractValueDomain, types.currentMember, node);
- info.addAssignment(argument);
+ info.addInput(argument);
types.allocatedTypes.add(info);
return info;
}
- @override
+ /// Registers that [caller] calls [closure] with [arguments].
+ ///
+ /// [sideEffectsBuilder] will be updated to incorporate the potential callees'
+ /// side effects.
+ ///
+ /// [inLoop] tells whether the call happens in a loop.
TypeInformation registerCalledClosure(
ir.Node node,
Selector selector,
- AbstractValue mask,
TypeInformation closure,
MemberEntity caller,
ArgumentsTypes arguments,
@@ -1230,7 +1023,6 @@
node,
caller,
selector,
- mask,
closure,
arguments,
inLoop);
@@ -1239,7 +1031,6 @@
return info;
}
- @override
void close() {
for (MemberTypeInformation typeInformation
in types.memberTypeInformations.values) {
@@ -1247,7 +1038,6 @@
}
}
- @override
void clear() {
if (retainDataForTesting) return;
@@ -1258,7 +1048,7 @@
types.allocatedCalls.forEach(cleanup);
types.allocatedCalls.clear();
- defaultTypeOfParameter.clear();
+ _defaultTypeOfParameter.clear();
types.parameterTypeInformations.values.forEach(cleanup);
types.memberTypeInformations.values.forEach(cleanup);
@@ -1271,8 +1061,8 @@
types.allocatedClosures.forEach(cleanup);
types.allocatedClosures.clear();
- analyzedElements.clear();
- generativeConstructorsExposingThis.clear();
+ _analyzedElements.clear();
+ _generativeConstructorsExposingThis.clear();
types.allocatedMaps.values.forEach(cleanup);
types.allocatedSets.values.forEach(cleanup);
@@ -1281,13 +1071,12 @@
_memberData.clear();
}
- @override
Iterable<MemberEntity> getCallersOfForTesting(MemberEntity element) {
MemberTypeInformation info = types.getInferredTypeOfMember(element);
return info.callersForTesting;
}
- @override
+ /// Returns the type of [element] when being called with [selector].
TypeInformation typeOfMemberWithSelector(
MemberEntity element, Selector selector) {
if (element.name == Identifiers.noSuchMethod_ &&
@@ -1321,8 +1110,17 @@
/// cache holds that shared dynamic call node for a given selector.
Map<Selector, DynamicCallSiteTypeInformation> _sharedCalls = {};
- @override
- DynamicCallSiteTypeInformation typeOfSharedDynamicCall(
+ /// For a given selector, return a shared dynamic call site that will be used
+ /// to combine the results of multiple dynamic calls in the program via
+ /// [IndirectDynamicCallSiteTypeInformation].
+ ///
+ /// This is used only for scalability reasons: if there are too many targets
+ /// and call sites, we may have a quadratic number of edges in the graph, so
+ /// we add a level of indirection to merge the information and keep the graph
+ /// smaller.
+ // TODO(sigmund): start using or delete indirection logic.
+ // ignore: unused_element
+ DynamicCallSiteTypeInformation _typeOfSharedDynamicCall(
Selector selector, CallStructure structure) {
DynamicCallSiteTypeInformation info = _sharedCalls[selector];
if (info != null) return info;
@@ -1359,7 +1157,11 @@
return info;
}
- @override
+ /// Returns true if global optimizations such as type inferencing can apply to
+ /// the field [element].
+ ///
+ /// One category of elements that do not apply is runtime helpers that the
+ /// backend calls, but the optimizations don't see those calls.
bool canFieldBeUsedForGlobalOptimizations(FieldEntity element) {
if (closedWorld.backendUsage.isFieldUsedByBackend(element)) {
return false;
@@ -1370,13 +1172,17 @@
return true;
}
- @override
+ /// Returns true if global optimizations such as type inferencing can apply to
+ /// the parameter [element].
+ ///
+ /// One category of elements that do not apply is runtime helpers that the
+ /// backend calls, but the optimizations don't see those calls.
bool canFunctionParametersBeUsedForGlobalOptimizations(
FunctionEntity function) {
return !closedWorld.backendUsage.isFunctionUsedByBackend(function);
}
- @override
+ /// Returns `true` if inference of parameter types is disabled for [member].
bool assumeDynamic(MemberEntity member) {
return closedWorld.annotationsData.hasAssumeDynamic(member);
}
@@ -1451,7 +1257,7 @@
parameter,
type,
member,
- new ParameterAssignments());
+ new ParameterInputs());
} else {
return new ParameterTypeInformation.static(
abstractValueDomain, memberTypeInformation, parameter, type, member);
@@ -1496,8 +1302,7 @@
/// objects in a debugging data stream.
static const String tag = 'global-type-inference-element-data';
- // TODO(johnniwinther): Rename this together with [typeOfSend].
- Map<ir.TreeNode, AbstractValue> _sendMap;
+ Map<ir.TreeNode, AbstractValue> _receiverMap;
Map<ir.ForInStatement, AbstractValue> _iteratorMap;
Map<ir.ForInStatement, AbstractValue> _currentMap;
@@ -1505,8 +1310,8 @@
KernelGlobalTypeInferenceElementData();
- KernelGlobalTypeInferenceElementData.internal(
- this._sendMap, this._iteratorMap, this._currentMap, this._moveNextMap);
+ KernelGlobalTypeInferenceElementData.internal(this._receiverMap,
+ this._iteratorMap, this._currentMap, this._moveNextMap);
/// Deserializes a [GlobalTypeInferenceElementData] object from [source].
factory KernelGlobalTypeInferenceElementData.readFromDataSource(
@@ -1542,7 +1347,7 @@
sink.inMemberContext(context, () {
sink.begin(tag);
sink.writeTreeNodeMapInContext(
- _sendMap,
+ _receiverMap,
(AbstractValue value) =>
abstractValueDomain.writeAbstractValueToDataSink(sink, value),
allowNull: true);
@@ -1567,10 +1372,10 @@
@override
GlobalTypeInferenceElementData compress() {
- if (_sendMap != null) {
- _sendMap.removeWhere(_mapsToNull);
- if (_sendMap.isEmpty) {
- _sendMap = null;
+ if (_receiverMap != null) {
+ _receiverMap.removeWhere(_mapsToNull);
+ if (_receiverMap.isEmpty) {
+ _receiverMap = null;
}
}
if (_iteratorMap != null) {
@@ -1591,7 +1396,7 @@
_moveNextMap = null;
}
}
- if (_sendMap == null &&
+ if (_receiverMap == null &&
_iteratorMap == null &&
_currentMap == null &&
_moveNextMap == null) {
@@ -1601,9 +1406,9 @@
}
@override
- AbstractValue typeOfSend(ir.TreeNode node) {
- if (_sendMap == null) return null;
- return _sendMap[node];
+ AbstractValue typeOfReceiver(ir.TreeNode node) {
+ if (_receiverMap == null) return null;
+ return _receiverMap[node];
}
void setCurrentTypeMask(ir.ForInStatement node, AbstractValue mask) {
@@ -1639,15 +1444,9 @@
return _iteratorMap[node];
}
- void setTypeMask(ir.TreeNode node, AbstractValue mask) {
- _sendMap ??= <ir.TreeNode, AbstractValue>{};
- _sendMap[node] = mask;
- }
-
- @override
- AbstractValue typeOfGetter(ir.TreeNode node) {
- if (_sendMap == null) return null;
- return _sendMap[node];
+ void setReceiverTypeMask(ir.TreeNode node, AbstractValue mask) {
+ _receiverMap ??= <ir.TreeNode, AbstractValue>{};
+ _receiverMap[node] = mask;
}
}
diff --git a/pkg/compiler/lib/src/inferrer/list_tracer.dart b/pkg/compiler/lib/src/inferrer/list_tracer.dart
index aaa69e4..db86752 100644
--- a/pkg/compiler/lib/src/inferrer/list_tracer.dart
+++ b/pkg/compiler/lib/src/inferrer/list_tracer.dart
@@ -130,13 +130,13 @@
class ListTracerVisitor extends TracerVisitor {
// The [Set] of found assignments to the list.
- Set<TypeInformation> assignments = new Setlet<TypeInformation>();
+ Set<TypeInformation> inputs = new Setlet<TypeInformation>();
bool callsGrowableMethod = false;
ListTracerVisitor(tracedType, inferrer) : super(tracedType, inferrer);
/// Returns [true] if the analysis completed successfully, [false] if it
- /// bailed out. In the former case, [assignments] holds a list of
+ /// bailed out. In the former case, [inputs] holds a list of
/// [TypeInformation] nodes that flow into the element type of this list.
bool run() {
analyze();
@@ -149,7 +149,7 @@
return true;
} else {
callsGrowableMethod = true;
- assignments = null;
+ inputs = null;
return false;
}
}
@@ -180,18 +180,18 @@
int positionalLength = info.arguments.positional.length;
if (selectorName == 'add') {
if (positionalLength == 1) {
- assignments.add(info.arguments.positional[0]);
+ inputs.add(info.arguments.positional[0]);
}
} else if (selectorName == 'insert') {
if (positionalLength == 2) {
- assignments.add(info.arguments.positional[1]);
+ inputs.add(info.arguments.positional[1]);
}
} else {
bailout('Used in a not-ok selector');
return;
}
} else if (selector.isIndexSet) {
- assignments.add(info.arguments.positional[1]);
+ inputs.add(info.arguments.positional[1]);
} else if (!selector.isIndex) {
bailout('Used in a not-ok selector');
return;
@@ -202,7 +202,7 @@
}
if (selectorName == 'length' && selector.isSetter) {
callsGrowableMethod = true;
- assignments.add(inferrer.types.nullType);
+ inputs.add(inferrer.types.nullType);
}
} else if (selector.isCall &&
(info.hasClosureCallTargets ||
diff --git a/pkg/compiler/lib/src/inferrer/map_tracer.dart b/pkg/compiler/lib/src/inferrer/map_tracer.dart
index f21fd15..3f29c0c 100644
--- a/pkg/compiler/lib/src/inferrer/map_tracer.dart
+++ b/pkg/compiler/lib/src/inferrer/map_tracer.dart
@@ -35,17 +35,17 @@
// These lists are used to keep track of newly discovered assignments to
// the map. Note that elements at corresponding indices are expected to
// belong to the same assignment operation.
- List<TypeInformation> keyAssignments = <TypeInformation>[];
- List<TypeInformation> valueAssignments = <TypeInformation>[];
+ List<TypeInformation> keyInputs = <TypeInformation>[];
+ List<TypeInformation> valueInputs = <TypeInformation>[];
// This list is used to keep track of assignments of entire maps to
// this map.
- List<MapTypeInformation> mapAssignments = <MapTypeInformation>[];
+ List<MapTypeInformation> mapInputs = <MapTypeInformation>[];
MapTracerVisitor(tracedType, inferrer) : super(tracedType, inferrer);
/// Returns [true] if the analysis completed successfully, [false]
- /// if it bailed out. In the former case, [keyAssignments] and
- /// [valueAssignments] hold a list of [TypeInformation] nodes that
+ /// if it bailed out. In the former case, [keyInputs] and
+ /// [valueInputs] hold a list of [TypeInformation] nodes that
/// flow into the key and value types of this map.
bool run() {
analyze();
@@ -54,7 +54,7 @@
map.addFlowsIntoTargets(flowsInto);
return true;
}
- keyAssignments = valueAssignments = mapAssignments = null;
+ keyInputs = valueInputs = mapInputs = null;
return false;
}
@@ -87,7 +87,7 @@
TypeInformation map = info.arguments.positional[0];
if (map is MapTypeInformation) {
inferrer.analyzeMapAndEnqueue(map);
- mapAssignments.add(map);
+ mapInputs.add(map);
} else {
// If we could select a component from a [TypeInformation],
// like the keytype or valuetype in this case, we could
@@ -103,8 +103,8 @@
// to go to dynamic.
// TODO(herhut,16507): Use return type of closure in
// Map.putIfAbsent.
- keyAssignments.add(info.arguments.positional[0]);
- valueAssignments.add(inferrer.types.dynamicType);
+ keyInputs.add(info.arguments.positional[0]);
+ valueInputs.add(inferrer.types.dynamicType);
} else {
// It would be nice to handle [Map.keys] and [Map.values], too.
// However, currently those calls do not trigger the creation
@@ -115,8 +115,8 @@
return;
}
} else if (selector.isIndexSet) {
- keyAssignments.add(info.arguments.positional[0]);
- valueAssignments.add(info.arguments.positional[1]);
+ keyInputs.add(info.arguments.positional[0]);
+ valueInputs.add(info.arguments.positional[1]);
} else if (!selector.isIndex) {
bailout('Map used in a not-ok selector [$selectorName]');
return;
diff --git a/pkg/compiler/lib/src/inferrer/set_tracer.dart b/pkg/compiler/lib/src/inferrer/set_tracer.dart
index 72cf043..8a98719 100644
--- a/pkg/compiler/lib/src/inferrer/set_tracer.dart
+++ b/pkg/compiler/lib/src/inferrer/set_tracer.dart
@@ -65,12 +65,12 @@
]);
class SetTracerVisitor extends TracerVisitor {
- List<TypeInformation> assignments = <TypeInformation>[];
+ List<TypeInformation> inputs = <TypeInformation>[];
SetTracerVisitor(tracedType, inferrer) : super(tracedType, inferrer);
/// Returns [true] if the analysis completed successfully, [false] if it
- /// bailed out. In the former case, [assignments] holds a list of
+ /// bailed out. In the former case, [inputs] holds a list of
/// [TypeInformation] nodes that flow into the element type of this set.
bool run() {
analyze();
@@ -79,7 +79,7 @@
set.addFlowsIntoTargets(flowsInto);
return true;
}
- assignments = null;
+ inputs = null;
return false;
}
@@ -108,7 +108,7 @@
if (selector.isCall) {
switch (selectorName) {
case 'add':
- assignments.add(info.arguments.positional[0]);
+ inputs.add(info.arguments.positional[0]);
break;
case 'addAll':
// TODO(fishythefish): Extract type argument from type
diff --git a/pkg/compiler/lib/src/inferrer/type_graph_dump.dart b/pkg/compiler/lib/src/inferrer/type_graph_dump.dart
index a4ed3e3..c215ca1 100644
--- a/pkg/compiler/lib/src/inferrer/type_graph_dump.dart
+++ b/pkg/compiler/lib/src/inferrer/type_graph_dump.dart
@@ -44,7 +44,7 @@
/// during the analysis.
void beforeAnalysis() {
for (TypeInformation node in inferrer.types.allTypes) {
- Set<TypeInformation> copy = node.assignments.toSet();
+ Set<TypeInformation> copy = node.inputs.toSet();
if (!copy.isEmpty) {
assignmentsBeforeAnalysis[node] = copy;
}
@@ -54,7 +54,7 @@
/// Like [beforeAnalysis], takes a copy of the assignment sets.
void beforeTracing() {
for (TypeInformation node in inferrer.types.allTypes) {
- Set<TypeInformation> copy = node.assignments.toSet();
+ Set<TypeInformation> copy = node.inputs.toSet();
if (!copy.isEmpty) {
assignmentsBeforeTracing[node] = copy;
}
@@ -266,7 +266,7 @@
/// Creates a node for [node] displaying the given [text] in its box.
///
/// [inputs] specify named inputs to the node. If omitted, edges will be
- /// based on [node.assignments].
+ /// based on [node.inputs].
void addNode(TypeInformation node, String text,
{String color: defaultNodeColor, Map<String, TypeInformation> inputs}) {
seen.add(node);
@@ -290,7 +290,7 @@
// added, removed, temporary, or unchanged.
dynamic originalSet = global.assignmentsBeforeAnalysis[node] ?? const [];
var tracerSet = global.assignmentsBeforeTracing[node] ?? const [];
- var currentSet = node.assignments.toSet();
+ var currentSet = node.inputs.toSet();
for (TypeInformation assignment in currentSet) {
String color =
originalSet.contains(assignment) ? unchangedEdge : addedEdge;
diff --git a/pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart b/pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart
index a1e7c15..2de18dc 100644
--- a/pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart
+++ b/pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart
@@ -70,13 +70,12 @@
}
InferrerEngine createInferrerEngineFor(FunctionEntity main) {
- return new InferrerEngineImpl(
+ return InferrerEngine(
_compiler.options,
_compiler.progress,
_compiler.reporter,
_compiler.outputProvider,
closedWorld,
- _compiler.frontendStrategy.noSuchMethodRegistry,
main,
_inferredDataBuilder);
}
diff --git a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart
index af0410d..622b6fb 100644
--- a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart
+++ b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart
@@ -31,13 +31,13 @@
/// - Containers (for lists)
/// - Type of the element in a container
///
-/// A node has a set of assignments and users. Assignments are used to
+/// A node has a set of inputs and users. Inputs are used to
/// compute the type of the node ([TypeInformation.computeType]). Users are
/// added to the inferrer's work queue when the type of the node
/// changes.
abstract class TypeInformation {
Set<TypeInformation> users;
- var /* List|ParameterAssignments */ _assignments;
+ var /* List|ParameterInputs */ _inputs;
/// The type the inferrer has found for this [TypeInformation].
/// Initially empty.
@@ -49,15 +49,14 @@
/// The element this [TypeInformation] node belongs to.
MemberEntity get contextMember => context == null ? null : context.member;
- Iterable<TypeInformation> get assignments => _assignments;
+ Iterable<TypeInformation> get inputs => _inputs;
/// We abandon inference in certain cases (complex cyclic flow, native
/// behaviours, etc.). In some case, we might resume inference in the
- /// closure tracer, which is handled by checking whether [assignments] has
- /// been set to [STOP_TRACKING_ASSIGNMENTS_MARKER].
+ /// closure tracer, which is handled by checking whether [inputs] has
+ /// been set to [STOP_TRACKING_INPUTS_MARKER].
bool abandonInferencing = false;
- bool get mightResume =>
- !identical(assignments, STOP_TRACKING_ASSIGNMENTS_MARKER);
+ bool get mightResume => !identical(inputs, STOP_TRACKING_INPUTS_MARKER);
/// Number of times this [TypeInformation] has changed type.
int refineCount = 0;
@@ -85,19 +84,19 @@
bool get isConcrete => false;
TypeInformation(this.type, this.context)
- : _assignments = <TypeInformation>[],
+ : _inputs = <TypeInformation>[],
users = new Setlet<TypeInformation>();
- TypeInformation.noAssignments(this.type, this.context)
- : _assignments = const <TypeInformation>[],
+ TypeInformation.noInputs(this.type, this.context)
+ : _inputs = const <TypeInformation>[],
users = new Setlet<TypeInformation>();
TypeInformation.untracked(this.type)
- : _assignments = const <TypeInformation>[],
+ : _inputs = const <TypeInformation>[],
users = const ImmutableEmptySet(),
context = null;
- TypeInformation.withAssignments(this.type, this.context, this._assignments)
+ TypeInformation.withInputs(this.type, this.context, this._inputs)
: users = new Setlet<TypeInformation>();
void addUser(TypeInformation user) {
@@ -116,31 +115,31 @@
// The below is not a compile time constant to make it differentiable
// from other empty lists of [TypeInformation].
- static final STOP_TRACKING_ASSIGNMENTS_MARKER = new List<TypeInformation>(0);
+ static final STOP_TRACKING_INPUTS_MARKER = new List<TypeInformation>(0);
- bool areAssignmentsTracked() {
- return assignments != STOP_TRACKING_ASSIGNMENTS_MARKER;
+ bool areInputsTracked() {
+ return inputs != STOP_TRACKING_INPUTS_MARKER;
}
- void addAssignment(TypeInformation assignment) {
+ void addInput(TypeInformation input) {
// Cheap one-level cycle detection.
- if (assignment == this) return;
- if (areAssignmentsTracked()) {
- _assignments.add(assignment);
+ if (input == this) return;
+ if (areInputsTracked()) {
+ _inputs.add(input);
}
// Even if we abandon inferencing on this [TypeInformation] we
// need to collect the users, so that phases that track where
// elements flow in still work.
- assignment.addUser(this);
+ input.addUser(this);
}
- void removeAssignment(TypeInformation assignment) {
+ void removeInput(TypeInformation input) {
if (!abandonInferencing || mightResume) {
- _assignments.remove(assignment);
+ _inputs.remove(input);
}
- // We can have multiple assignments of the same [TypeInformation].
- if (!assignments.contains(assignment)) {
- assignment.removeUser(this);
+ // We can have multiple inputs of the same [TypeInformation].
+ if (!inputs.contains(input)) {
+ input.removeUser(this);
}
}
@@ -158,18 +157,18 @@
return inferrer.types.dynamicType.type;
}
- void giveUp(InferrerEngine inferrer, {bool clearAssignments: true}) {
+ void giveUp(InferrerEngine inferrer, {bool clearInputs: true}) {
abandonInferencing = true;
- // Do not remove [this] as a user of nodes in [assignments],
+ // Do not remove [this] as a user of nodes in [inputs],
// because our tracing analysis could be interested in tracing
// this node.
- if (clearAssignments) _assignments = STOP_TRACKING_ASSIGNMENTS_MARKER;
+ if (clearInputs) _inputs = STOP_TRACKING_INPUTS_MARKER;
// Do not remove users because our tracing analysis could be
// interested in tracing the users of this node.
}
void clear() {
- _assignments = STOP_TRACKING_ASSIGNMENTS_MARKER;
+ _inputs = STOP_TRACKING_INPUTS_MARKER;
users = const ImmutableEmptySet();
}
@@ -192,11 +191,11 @@
/// Returns whether the type cannot change after it has been
/// inferred.
bool hasStableType(InferrerEngine inferrer) {
- return !mightResume && assignments.every((e) => e.isStable);
+ return !mightResume && inputs.every((e) => e.isStable);
}
void removeAndClearReferences(InferrerEngine inferrer) {
- assignments.forEach((info) {
+ inputs.forEach((info) {
info.removeUser(this);
});
}
@@ -205,7 +204,7 @@
removeAndClearReferences(inferrer);
// Do not remove users because the tracing analysis could be interested
// in tracing the users of this node.
- _assignments = STOP_TRACKING_ASSIGNMENTS_MARKER;
+ _inputs = STOP_TRACKING_INPUTS_MARKER;
abandonInferencing = true;
isStable = true;
}
@@ -219,7 +218,7 @@
/// Destroys information not needed after type inference.
void cleanup() {
users = null;
- _assignments = null;
+ _inputs = null;
}
String toStructuredText(String indent) {
@@ -264,52 +263,52 @@
}
/// Parameters of instance functions behave differently than other
-/// elements because the inferrer may remove assignments. This happens
+/// elements because the inferrer may remove inputs. This happens
/// when the receiver of a dynamic call site can be refined
/// to a type where we know more about which instance method is being
/// called.
-class ParameterAssignments extends IterableBase<TypeInformation> {
- final Map<TypeInformation, int> assignments = new Map<TypeInformation, int>();
+class ParameterInputs extends IterableBase<TypeInformation> {
+ final Map<TypeInformation, int> _inputs = new Map<TypeInformation, int>();
void remove(TypeInformation info) {
- int existing = assignments[info];
+ int existing = _inputs[info];
if (existing == null) return;
if (existing == 1) {
- assignments.remove(info);
+ _inputs.remove(info);
} else {
- assignments[info] = existing - 1;
+ _inputs[info] = existing - 1;
}
}
void add(TypeInformation info) {
- int existing = assignments[info];
+ int existing = _inputs[info];
if (existing == null) {
- assignments[info] = 1;
+ _inputs[info] = 1;
} else {
- assignments[info] = existing + 1;
+ _inputs[info] = existing + 1;
}
}
void replace(TypeInformation old, TypeInformation replacement) {
- int existing = assignments[old];
+ int existing = _inputs[old];
if (existing != null) {
- int other = assignments[replacement];
+ int other = _inputs[replacement];
if (other != null) existing += other;
- assignments[replacement] = existing;
- assignments.remove(old);
+ _inputs[replacement] = existing;
+ _inputs.remove(old);
}
}
@override
- Iterator<TypeInformation> get iterator => assignments.keys.iterator;
+ Iterator<TypeInformation> get iterator => _inputs.keys.iterator;
@override
- Iterable<TypeInformation> where(Function f) => assignments.keys.where(f);
+ Iterable<TypeInformation> where(Function f) => _inputs.keys.where(f);
@override
- bool contains(Object info) => assignments.containsKey(info);
+ bool contains(Object info) => _inputs.containsKey(info);
@override
- String toString() => assignments.keys.toList().toString();
+ String toString() => _inputs.keys.toList().toString();
}
/// A node representing a resolved element of the component. The kind of
@@ -344,12 +343,9 @@
ElementTypeInformation._internal(
AbstractValueDomain abstractValueDomain, MemberTypeInformation context)
: super(abstractValueDomain.emptyType, context);
- ElementTypeInformation._withAssignments(
- AbstractValueDomain abstractValueDomain,
- MemberTypeInformation context,
- ParameterAssignments assignments)
- : super.withAssignments(
- abstractValueDomain.emptyType, context, assignments);
+ ElementTypeInformation._withInputs(AbstractValueDomain abstractValueDomain,
+ MemberTypeInformation context, ParameterInputs inputs)
+ : super.withInputs(abstractValueDomain.emptyType, context, inputs);
String getInferredSignature(TypeSystem types);
@@ -485,7 +481,7 @@
AbstractValue special = handleSpecialCases(inferrer);
if (special != null) return potentiallyNarrowType(special, inferrer);
return potentiallyNarrowType(
- inferrer.types.computeTypeMask(assignments), inferrer);
+ inferrer.types.computeTypeMask(inputs), inferrer);
}
@override
@@ -552,7 +548,7 @@
@override
bool hasStableType(InferrerEngine inferrer) {
- // The number of assignments of non-final fields is
+ // The number of inputs of non-final fields is
// not stable. Therefore such a field cannot be stable.
if (!_field.isAssignable) {
return false;
@@ -728,11 +724,11 @@
this._parameter,
this._type,
this._method,
- ParameterAssignments assignments)
+ ParameterInputs inputs)
: _isInstanceMemberParameter = true,
_isClosureParameter = false,
_isInitializingFormal = false,
- super._withAssignments(abstractValueDomain, context, assignments);
+ super._withInputs(abstractValueDomain, context, inputs);
FunctionEntity get method => _method;
@@ -770,9 +766,9 @@
if ((_isTearOffClosureParameter || _isClosureParameter) &&
disableInferenceForClosures) {
// Do not infer types for parameters of closures. We do not
- // clear the assignments in case the closure is successfully
+ // clear the inputs in case the closure is successfully
// traced.
- giveUp(inferrer, clearAssignments: false);
+ giveUp(inferrer, clearInputs: false);
return safeType(inferrer);
}
if (_isInstanceMemberParameter &&
@@ -845,7 +841,7 @@
AbstractValue special = handleSpecialCases(inferrer);
if (special != null) return special;
return potentiallyNarrowType(
- inferrer.types.computeTypeMask(assignments), inferrer);
+ inferrer.types.computeTypeMask(inputs), inferrer);
}
@override
@@ -855,7 +851,7 @@
@override
bool hasStableType(InferrerEngine inferrer) {
- // The number of assignments of parameters of instance methods is
+ // The number of inputs of parameters of instance methods is
// not stable. Therefore such a parameter cannot be stable.
if (_isInstanceMemberParameter) {
return false;
@@ -888,7 +884,7 @@
@override
AbstractValue computeType(InferrerEngine inferrer) =>
- inferrer.types.computeTypeMask(assignments);
+ inferrer.types.computeTypeMask(inputs);
@override
accept(TypeInformationVisitor visitor) {
@@ -930,7 +926,6 @@
final Object _call;
final MemberEntity caller;
final Selector selector;
- final AbstractValue mask;
final ArgumentsTypes arguments;
final bool inLoop;
@@ -940,10 +935,9 @@
this._call,
this.caller,
this.selector,
- this.mask,
this.arguments,
this.inLoop)
- : super.noAssignments(abstractValueDomain.emptyType, context) {
+ : super.noInputs(abstractValueDomain.emptyType, context) {
assert(_call is ir.Node || (_call == null && selector.name == '=='));
}
@@ -969,10 +963,9 @@
MemberEntity enclosing,
this.calledElement,
Selector selector,
- AbstractValue mask,
ArgumentsTypes arguments,
bool inLoop)
- : super(abstractValueDomain, context, call, enclosing, selector, mask,
+ : super(abstractValueDomain, context, call, enclosing, selector,
arguments, inLoop);
MemberTypeInformation _getCalledTypeInfo(InferrerEngine inferrer) {
@@ -987,8 +980,7 @@
if (arguments != null) {
arguments.forEach((info) => info.addUser(this));
}
- inferrer.updateParameterAssignments(
- this, calledElement, arguments, selector, mask,
+ inferrer.updateParameterInputs(this, calledElement, arguments, selector,
remove: false, addToQueue: false);
}
@@ -1056,6 +1048,7 @@
final DynamicCallSiteTypeInformation dynamicCall;
final bool isConditional;
final TypeInformation receiver;
+ final AbstractValue mask;
IndirectDynamicCallSiteTypeInformation(
AbstractValueDomain abstractValueDomain,
@@ -1064,25 +1057,25 @@
this.dynamicCall,
MemberEntity enclosing,
Selector selector,
- AbstractValue mask,
+ this.mask,
this.receiver,
ArgumentsTypes arguments,
bool inLoop,
this.isConditional)
- : super(abstractValueDomain, context, call, enclosing, selector, mask,
+ : super(abstractValueDomain, context, call, enclosing, selector,
arguments, inLoop);
@override
void addToGraph(InferrerEngine inferrer) {
receiver.addUser(this);
- dynamicCall.receiver.addAssignment(receiver);
+ dynamicCall.receiver.addInput(receiver);
List<TypeInformation> positional = arguments.positional;
for (int i = 0; i < positional.length; i++) {
positional[i].addUser(this);
- dynamicCall.arguments.positional[i].addAssignment(positional[i]);
+ dynamicCall.arguments.positional[i].addInput(positional[i]);
}
arguments.named.forEach((name, namedInfo) {
- dynamicCall.arguments.named[name].addAssignment(namedInfo);
+ dynamicCall.arguments.named[name].addInput(namedInfo);
});
dynamicCall.addUser(this);
}
@@ -1113,12 +1106,12 @@
}
@override
- void giveUp(InferrerEngine inferrer, {bool clearAssignments: true}) {
+ void giveUp(InferrerEngine inferrer, {bool clearInputs: true}) {
if (!abandonInferencing) {
inferrer.updateSelectorInMember(
caller, CallType.access, _call, selector, mask);
}
- super.giveUp(inferrer, clearAssignments: clearAssignments);
+ super.giveUp(inferrer, clearInputs: clearInputs);
}
@override
@@ -1147,6 +1140,7 @@
class DynamicCallSiteTypeInformation<T> extends CallSiteTypeInformation {
final CallType _callType;
final TypeInformation receiver;
+ final AbstractValue mask;
final bool isConditional;
bool _hasClosureCallTargets;
@@ -1160,12 +1154,12 @@
T call,
MemberEntity enclosing,
Selector selector,
- AbstractValue mask,
+ this.mask,
this.receiver,
ArgumentsTypes arguments,
bool inLoop,
this.isConditional)
- : super(abstractValueDomain, context, call, enclosing, selector, mask,
+ : super(abstractValueDomain, context, call, enclosing, selector,
arguments, inLoop) {
assert(validCallType(_callType, _call, selector));
}
@@ -1200,8 +1194,7 @@
inferrer.types.getInferredTypeOfMember(element);
_addCall(callee);
callee.addUser(this);
- inferrer.updateParameterAssignments(
- this, element, arguments, selector, typeMask,
+ inferrer.updateParameterInputs(this, element, arguments, selector,
remove: false, addToQueue: false);
}
}
@@ -1364,8 +1357,7 @@
inferrer.types.getInferredTypeOfMember(element);
_addCall(callee);
callee.addUser(this);
- inferrer.updateParameterAssignments(
- this, element, arguments, selector, typeMask,
+ inferrer.updateParameterInputs(this, element, arguments, selector,
remove: false, addToQueue: true);
});
@@ -1377,8 +1369,7 @@
inferrer.types.getInferredTypeOfMember(element);
_removeCall(callee);
callee.removeUser(this);
- inferrer.updateParameterAssignments(
- this, element, arguments, selector, typeMask,
+ inferrer.updateParameterInputs(this, element, arguments, selector,
remove: true, addToQueue: true);
});
}
@@ -1440,7 +1431,7 @@
}
@override
- void giveUp(InferrerEngine inferrer, {bool clearAssignments: true}) {
+ void giveUp(InferrerEngine inferrer, {bool clearInputs: true}) {
if (!abandonInferencing) {
inferrer.updateSelectorInMember(caller, _callType, _call, selector, mask);
Iterable<MemberEntity> oldTargets = _concreteTargets;
@@ -1452,13 +1443,12 @@
MemberTypeInformation callee =
inferrer.types.getInferredTypeOfMember(element);
callee.addCall(caller, _call);
- inferrer.updateParameterAssignments(
- this, element, arguments, selector, mask,
+ inferrer.updateParameterInputs(this, element, arguments, selector,
remove: false, addToQueue: true);
}
}
}
- super.giveUp(inferrer, clearAssignments: clearAssignments);
+ super.giveUp(inferrer, clearInputs: clearInputs);
}
@override
@@ -1501,11 +1491,10 @@
Object call,
MemberEntity enclosing,
Selector selector,
- AbstractValue mask,
this.closure,
ArgumentsTypes arguments,
bool inLoop)
- : super(abstractValueDomain, context, call, enclosing, selector, mask,
+ : super(abstractValueDomain, context, call, enclosing, selector,
arguments, inLoop);
@override
@@ -1574,12 +1563,12 @@
void removeUser(TypeInformation user) {}
@override
- void addAssignment(TypeInformation assignment) {
+ void addInput(TypeInformation assignment) {
throw "Not supported";
}
@override
- void removeAssignment(TypeInformation assignment) {
+ void removeInput(TypeInformation assignment) {
throw "Not supported";
}
@@ -1661,19 +1650,19 @@
NarrowTypeInformation(AbstractValueDomain abstractValueDomain,
TypeInformation narrowedType, this.typeAnnotation)
: super(abstractValueDomain.emptyType, narrowedType.context) {
- addAssignment(narrowedType);
+ addInput(narrowedType);
}
@override
- addAssignment(TypeInformation info) {
- super.addAssignment(info);
- assert(assignments.length == 1);
+ addInput(TypeInformation info) {
+ super.addInput(info);
+ assert(inputs.length == 1);
}
@override
AbstractValue computeType(InferrerEngine inferrer) {
AbstractValueDomain abstractValueDomain = inferrer.abstractValueDomain;
- AbstractValue input = assignments.first.type;
+ AbstractValue input = inputs.first.type;
AbstractValue intersection =
abstractValueDomain.intersection(input, typeAnnotation);
if (debug.ANOMALY_WARN) {
@@ -1702,7 +1691,7 @@
/// An [InferredTypeInformation] is a [TypeInformation] that
/// defaults to the dynamic type until it is marked as being
/// inferred, at which point it computes its type based on
-/// its assignments.
+/// its inputs.
abstract class InferredTypeInformation extends TypeInformation {
/// Whether the element type in that container has been inferred.
bool inferred = false;
@@ -1710,13 +1699,13 @@
InferredTypeInformation(AbstractValueDomain abstractValueDomain,
MemberTypeInformation context, TypeInformation parentType)
: super(abstractValueDomain.emptyType, context) {
- if (parentType != null) addAssignment(parentType);
+ if (parentType != null) addInput(parentType);
}
@override
AbstractValue computeType(InferrerEngine inferrer) {
if (!inferred) return safeType(inferrer);
- return inferrer.types.computeTypeMask(assignments);
+ return inferrer.types.computeTypeMask(inputs);
}
@override
@@ -1901,7 +1890,7 @@
valueType.addUser(this);
}
- TypeInformation addEntryAssignment(AbstractValueDomain abstractValueDomain,
+ TypeInformation addEntryInput(AbstractValueDomain abstractValueDomain,
TypeInformation key, TypeInformation value,
[bool nonNull = false]) {
TypeInformation newInfo = null;
@@ -1912,19 +1901,19 @@
abstractValueDomain, context, null, nonNull);
return newInfo;
});
- typeInfoMap[keyString].addAssignment(value);
+ typeInfoMap[keyString].addInput(value);
} else {
_allKeysAreStrings = false;
typeInfoMap.clear();
}
- keyType.addAssignment(key);
- valueType.addAssignment(value);
+ keyType.addInput(key);
+ valueType.addInput(value);
if (newInfo != null) newInfo.addUser(this);
return newInfo;
}
- List<TypeInformation> addMapAssignment(
+ List<TypeInformation> addMapInput(
AbstractValueDomain abstractValueDomain, MapTypeInformation other) {
List<TypeInformation> newInfos = <TypeInformation>[];
if (_allKeysAreStrings && other.inDictionaryMode) {
@@ -1935,14 +1924,14 @@
newInfos.add(newInfo);
return newInfo;
});
- typeInfoMap[keyString].addAssignment(value);
+ typeInfoMap[keyString].addInput(value);
});
} else {
_allKeysAreStrings = false;
typeInfoMap.clear();
}
- keyType.addAssignment(other.keyType);
- valueType.addAssignment(other.valueType);
+ keyType.addInput(other.keyType);
+ valueType.addInput(other.valueType);
return newInfos;
}
@@ -1953,7 +1942,7 @@
}
@override
- addAssignment(TypeInformation other) {
+ addInput(TypeInformation other) {
throw "not supported";
}
@@ -2103,7 +2092,7 @@
@override
AbstractValue computeType(InferrerEngine inferrer) {
- return inferrer.types.computeTypeMask(assignments);
+ return inferrer.types.computeTypeMask(inputs);
}
@override
@@ -2114,7 +2103,7 @@
StringBuffer sb, String indent, Set<TypeInformation> seen) {
if (seen.add(this)) {
sb.write('${toString()} [');
- for (TypeInformation assignment in assignments) {
+ for (TypeInformation assignment in inputs) {
sb.write('\n$indent ');
assignment._toStructuredText(sb, '$indent ', seen);
}
diff --git a/pkg/compiler/lib/src/inferrer/type_system.dart b/pkg/compiler/lib/src/inferrer/type_system.dart
index 2e05bac..6d5622e 100644
--- a/pkg/compiler/lib/src/inferrer/type_system.dart
+++ b/pkg/compiler/lib/src/inferrer/type_system.dart
@@ -536,7 +536,7 @@
new MapTypeInformation(currentMember, mask, keyTypeInfo, valueTypeInfo);
for (int i = 0; i < keyTypes.length; ++i) {
- TypeInformation newType = map.addEntryAssignment(
+ TypeInformation newType = map.addEntryInput(
_abstractValueDomain, keyTypes[i], valueTypes[i], true);
if (newType != null) allocatedTypes.add(newType);
}
@@ -562,8 +562,8 @@
PhiElementTypeInformation result = new PhiElementTypeInformation(
_abstractValueDomain, currentMember, null, null,
isTry: false);
- result.addAssignment(firstInput);
- result.addAssignment(secondInput);
+ result.addInput(firstInput);
+ result.addInput(secondInput);
allocatedTypes.add(result);
return result;
}
@@ -574,7 +574,7 @@
_abstractValueDomain, currentMember, node, variable,
isTry: isTry);
allocatedTypes.add(result);
- result.addAssignment(inputType);
+ result.addInput(inputType);
return result;
}
@@ -614,14 +614,14 @@
TypeInformation simplifyPhi(
ir.Node node, Local variable, PhiElementTypeInformation phiType) {
assert(phiType.branchNode == node);
- if (phiType.assignments.length == 1) return phiType.assignments.first;
+ if (phiType.inputs.length == 1) return phiType.inputs.first;
return phiType;
}
/// Adds [newType] as an input of [phiType].
PhiElementTypeInformation addPhiInput(Local variable,
PhiElementTypeInformation phiType, TypeInformation newType) {
- phiType.addAssignment(newType);
+ phiType.addInput(newType);
return phiType;
}
diff --git a/pkg/compiler/lib/src/inferrer/typemasks/masks.dart b/pkg/compiler/lib/src/inferrer/typemasks/masks.dart
index d7717bf..4f94658 100644
--- a/pkg/compiler/lib/src/inferrer/typemasks/masks.dart
+++ b/pkg/compiler/lib/src/inferrer/typemasks/masks.dart
@@ -756,10 +756,28 @@
@override
AbstractBool isInterceptor(TypeMask value) {
+ // TODO(39874): Remove cache when [TypeMask.isDisjoint] is faster.
+ var result = _isInterceptorCache[value];
+ if (result == null) {
+ result = _isInterceptorCacheSecondChance[value] ?? _isInterceptor(value);
+ if (_isInterceptorCache.length >= _kIsInterceptorCacheLimit) {
+ _isInterceptorCacheSecondChance = _isInterceptorCache;
+ _isInterceptorCache = {};
+ }
+ _isInterceptorCache[value] = result;
+ }
+ return result;
+ }
+
+ AbstractBool _isInterceptor(TypeMask value) {
return AbstractBool.maybeOrFalse(
!interceptorType.isDisjoint(value, _closedWorld));
}
+ static const _kIsInterceptorCacheLimit = 500;
+ Map<TypeMask, AbstractBool> _isInterceptorCache = {};
+ Map<TypeMask, AbstractBool> _isInterceptorCacheSecondChance = {};
+
@override
bool isMap(TypeMask value) {
return value.isMap;
diff --git a/pkg/compiler/lib/src/inferrer/types.dart b/pkg/compiler/lib/src/inferrer/types.dart
index 374f01a..5c5c4f1 100644
--- a/pkg/compiler/lib/src/inferrer/types.dart
+++ b/pkg/compiler/lib/src/inferrer/types.dart
@@ -52,13 +52,9 @@
/// The inferred return type when this result belongs to a function element.
AbstractValue get returnType;
- /// Returns the type of a send [node].
- // TODO(johnniwinther): Rename this.
- AbstractValue typeOfSend(ir.TreeNode node);
-
- /// Returns the type of the getter in a complex send-set [node], for example,
- /// the type of the `a.f` getter in `a.f += b`.
- AbstractValue typeOfGetter(ir.TreeNode node);
+ /// Returns the receiver type of a node that is a property get, set, or method
+ /// invocation.
+ AbstractValue typeOfReceiver(ir.TreeNode node);
/// Returns the type of the iterator in a [loop].
AbstractValue typeOfIterator(ir.TreeNode node);
@@ -88,8 +84,7 @@
GlobalTypeInferenceElementData compress();
// TODO(johnniwinther): Remove this. Maybe split by access/invoke.
- AbstractValue typeOfSend(ir.TreeNode node);
- AbstractValue typeOfGetter(ir.TreeNode node);
+ AbstractValue typeOfReceiver(ir.TreeNode node);
AbstractValue typeOfIterator(ir.TreeNode node);
@@ -443,9 +438,7 @@
}
@override
- AbstractValue typeOfSend(ir.Node node) => _data?.typeOfSend(node);
- @override
- AbstractValue typeOfGetter(ir.Node node) => _data?.typeOfGetter(node);
+ AbstractValue typeOfReceiver(ir.Node node) => _data?.typeOfReceiver(node);
@override
AbstractValue typeOfIterator(ir.Node node) => _data?.typeOfIterator(node);
@override
@@ -524,10 +517,7 @@
AbstractValue typeOfIterator(ir.Node node) => null;
@override
- AbstractValue typeOfGetter(ir.Node node) => null;
-
- @override
- AbstractValue typeOfSend(ir.Node node) => null;
+ AbstractValue typeOfReceiver(ir.Node node) => null;
@override
bool get isCalledOnce => false;
@@ -568,10 +558,7 @@
AbstractValue typeOfIterator(ir.Node node) => null;
@override
- AbstractValue typeOfGetter(ir.Node node) => null;
-
- @override
- AbstractValue typeOfSend(ir.Node node) => null;
+ AbstractValue typeOfReceiver(ir.Node node) => null;
@override
bool get isCalledOnce => false;
@@ -612,10 +599,7 @@
AbstractValue typeOfIterator(ir.Node node) => null;
@override
- AbstractValue typeOfGetter(ir.Node node) => null;
-
- @override
- AbstractValue typeOfSend(ir.Node node) => null;
+ AbstractValue typeOfReceiver(ir.Node node) => null;
@override
bool get isCalledOnce => false;
diff --git a/pkg/compiler/lib/src/js_model/js_strategy.dart b/pkg/compiler/lib/src/js_model/js_strategy.dart
index 822be8c..c313b19 100644
--- a/pkg/compiler/lib/src/js_model/js_strategy.dart
+++ b/pkg/compiler/lib/src/js_model/js_strategy.dart
@@ -539,23 +539,23 @@
@override
AbstractValue receiverTypeOfInvocation(
ir.MethodInvocation node, AbstractValueDomain abstractValueDomain) {
- return _targetResults.typeOfSend(node);
+ return _targetResults.typeOfReceiver(node);
}
@override
AbstractValue receiverTypeOfGet(ir.PropertyGet node) {
- return _targetResults.typeOfSend(node);
+ return _targetResults.typeOfReceiver(node);
}
@override
AbstractValue receiverTypeOfDirectGet(ir.DirectPropertyGet node) {
- return _targetResults.typeOfSend(node);
+ return _targetResults.typeOfReceiver(node);
}
@override
AbstractValue receiverTypeOfSet(
ir.PropertySet node, AbstractValueDomain abstractValueDomain) {
- return _targetResults.typeOfSend(node);
+ return _targetResults.typeOfReceiver(node);
}
@override
diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart
index edbcb67..32396ae 100644
--- a/pkg/compiler/lib/src/ssa/nodes.dart
+++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -2894,15 +2894,28 @@
}
class HParameterValue extends HLocalValue {
+ bool _potentiallyUsedAsVariable = true;
+
HParameterValue(Entity variable, AbstractValue type) : super(variable, type);
// [HParameterValue]s are either the value of the parameter (in fully SSA
// converted code), or the mutable variable containing the value (in
// incompletely SSA converted code, e.g. methods containing exceptions).
bool usedAsVariable() {
- for (HInstruction user in usedBy) {
- if (user is HLocalGet) return true;
- if (user is HLocalSet && user.local == this) return true;
+ if (_potentiallyUsedAsVariable) {
+ // If the HParameterValue is used as a variable, all of the uses should be
+ // HLocalGet or HLocalSet, so this loop exits fast.
+ for (HInstruction user in usedBy) {
+ if (user is HLocalGet) return true;
+ if (user is HLocalSet && user.local == this) return true;
+ }
+ // An 'ssa-conversion' optimization can make the HParameterValue change
+ // from a variable to a value, but there is no transformation that
+ // re-introduces the variable.
+ // TODO(sra): The builder knows that most parameters are not variables to
+ // begin with, so could initialize [_potentiallyUsedAsVariable] to
+ // `false`.
+ _potentiallyUsedAsVariable = false;
}
return false;
}
diff --git a/tests/compiler/dart2js/inference/data/closurization_instance_call.dart b/tests/compiler/dart2js/inference/data/closurization_instance_call.dart
index 4f785bf..d5eabac 100644
--- a/tests/compiler/dart2js/inference/data/closurization_instance_call.dart
+++ b/tests/compiler/dart2js/inference/data/closurization_instance_call.dart
@@ -24,7 +24,7 @@
/*member: closurizedCallToString:[exact=JSString]*/
closurizedCallToString() {
var c = new Class();
- c.call(); // Make `Class.call` live.
+ c. /*invoke: [null|exact=Class]*/ call(); // Make `Class.call` live.
var local = c. /*[exact=Class]*/ method;
local. /*invoke: [subclass=Closure]*/ toString();
local();
diff --git a/tests/compiler/dart2js/inference/data/closurization_local_call.dart b/tests/compiler/dart2js/inference/data/closurization_local_call.dart
index 65439e1..19c5758 100644
--- a/tests/compiler/dart2js/inference/data/closurization_local_call.dart
+++ b/tests/compiler/dart2js/inference/data/closurization_local_call.dart
@@ -21,7 +21,7 @@
/*member: closurizedCallToString:[exact=JSString]*/
closurizedCallToString() {
var c = new Class();
- c.call(); // Make `Class.call` live.
+ c. /*invoke: [null|exact=Class]*/ call(); // Make `Class.call` live.
var local = /*[exact=JSUInt31]*/ () => 42;
local. /*invoke: [subclass=Closure]*/ toString();
local();
diff --git a/tests/compiler/dart2js/inference/data/closurization_static_call.dart b/tests/compiler/dart2js/inference/data/closurization_static_call.dart
index a29e594..575f68d 100644
--- a/tests/compiler/dart2js/inference/data/closurization_static_call.dart
+++ b/tests/compiler/dart2js/inference/data/closurization_static_call.dart
@@ -24,7 +24,7 @@
/*member: closurizedCallToString:[exact=JSString]*/
closurizedCallToString() {
var c = new Class();
- c.call(); // Make `Class.call` live.
+ c. /*invoke: [null|exact=Class]*/ call(); // Make `Class.call` live.
var local = method;
local. /*invoke: [subclass=Closure]*/ toString();
local();
diff --git a/tests/compiler/dart2js/inference/data/do.dart b/tests/compiler/dart2js/inference/data/do.dart
index 549d826..d9dae617 100644
--- a/tests/compiler/dart2js/inference/data/do.dart
+++ b/tests/compiler/dart2js/inference/data/do.dart
@@ -47,7 +47,7 @@
var o = '';
do {
o = o. /*invoke: [exact=JSString]*/ toString();
- } while (o != null);
+ } while (o /*invoke: [null|exact=JSString]*/ != null);
return o;
}
@@ -60,7 +60,7 @@
var o = '';
do {
o = o. /*invoke: [exact=JSString]*/ toString();
- } while (o == null);
+ } while (o /*invoke: [null|exact=JSString]*/ == null);
return o;
}
diff --git a/tests/compiler/dart2js/inference/data/for.dart b/tests/compiler/dart2js/inference/data/for.dart
index b3829b7..9bc55db 100644
--- a/tests/compiler/dart2js/inference/data/for.dart
+++ b/tests/compiler/dart2js/inference/data/for.dart
@@ -47,7 +47,9 @@
/*member: forNotNull:[null|exact=JSString]*/
forNotNull() {
var local;
- for (var o = ''; o != null; o = o. /*invoke: [exact=JSString]*/ toString()) {
+ for (var o = '';
+ o /*invoke: [null|exact=JSString]*/ != null;
+ o = o. /*invoke: [exact=JSString]*/ toString()) {
local = o;
}
return local;
@@ -60,7 +62,9 @@
/*member: forNullFalse:[null]*/
forNullFalse() {
var local;
- for (var o = ''; o == null; o = o. /*invoke: [null]*/ toString()) {
+ for (var o = '';
+ o /*invoke: [null|exact=JSString]*/ == null;
+ o = o. /*invoke: [null]*/ toString()) {
local = o;
}
return local;
diff --git a/tests/compiler/dart2js/inference/data/general.dart b/tests/compiler/dart2js/inference/data/general.dart
index beb067d..f3d814f 100644
--- a/tests/compiler/dart2js/inference/data/general.dart
+++ b/tests/compiler/dart2js/inference/data/general.dart
@@ -675,7 +675,8 @@
A.generative();
/*member: A.==:[exact=JSBool]*/
- operator ==(/*[null|subclass=Object]*/ other) => 42 as dynamic;
+ operator ==(/*Union([exact=JSString], [exact=JSUInt31])*/ other) =>
+ 42 as dynamic;
/*member: A.myField:[exact=JSUInt31]*/
get myField => 42;
@@ -892,7 +893,7 @@
testDoWhile2();
testDoWhile3();
testDoWhile4();
- new A() == null;
+ new A() /*invoke: [null|subclass=A]*/ == null;
new A()
.. /*invoke: [exact=A]*/ returnInt1()
.. /*invoke: [exact=A]*/ returnInt2()
diff --git a/tests/compiler/dart2js/inference/data/narrowing.dart b/tests/compiler/dart2js/inference/data/narrowing.dart
index ba26c79..27c041d 100644
--- a/tests/compiler/dart2js/inference/data/narrowing.dart
+++ b/tests/compiler/dart2js/inference/data/narrowing.dart
@@ -13,7 +13,7 @@
/*member: nonNull1:[null]*/
void nonNull1() {
var x = 1;
- if (x == null) return;
+ if (x /*invoke: [null|subclass=JSInt]*/ == null) return;
argIsNonNull1(x);
}
@@ -25,7 +25,9 @@
/*member: nonNull2:[null]*/
void nonNull2() {
var x = 1;
- if ((x == null) /*invoke: [exact=JSBool]*/ == true) return;
+ if ((x /*invoke: [null|subclass=JSInt]*/ ==
+ null) /*invoke: [exact=JSBool]*/ ==
+ true) return;
argIsNonNull2(x);
}
@@ -37,7 +39,9 @@
/*member: nonNull3:[null]*/
void nonNull3() {
var x = 1;
- if ((x == null) /*invoke: [exact=JSBool]*/ != false) return;
+ if ((x /*invoke: [null|subclass=JSInt]*/ ==
+ null) /*invoke: [exact=JSBool]*/ !=
+ false) return;
argIsNonNull3(x);
}
@@ -52,7 +56,7 @@
/*member: nonNull4:[null]*/
void nonNull4() {
var x = 1;
- if (discard(x != null)) return;
+ if (discard(x /*invoke: [null|subclass=JSInt]*/ != null)) return;
argIsNonNull4(x);
}
@@ -64,7 +68,7 @@
/*member: nonNull5:[null]*/
void nonNull5() {
var x = 1;
- if (x != null ? false : false) return;
+ if (x /*invoke: [null|subclass=JSInt]*/ != null ? false : false) return;
argIsNonNull5(x);
}
@@ -76,8 +80,8 @@
/*member: nonNull6:[null]*/
void nonNull6() {
var x = 1;
- if ((/*[exact=JSBool]*/ (/*[exact=JSBool]*/ y) => y && false)(x != null))
- return;
+ if ((/*[exact=JSBool]*/ (/*[exact=JSBool]*/ y) =>
+ y && false)(x /*invoke: [null|subclass=JSInt]*/ != null)) return;
argIsNonNull6(x);
}
@@ -90,7 +94,7 @@
void nonNull7() {
var f = false;
var x = 1;
- if (f ? (throw x != null) : false) return;
+ if (f ? (throw x /*invoke: [null|subclass=JSInt]*/ != null) : false) return;
argIsNonNull7(x);
}
@@ -103,7 +107,7 @@
void nonNull8() {
var f = false;
var x = 1;
- if (f ?? (x != null)) return;
+ if (f ?? (x /*invoke: [null|subclass=JSInt]*/ != null)) return;
argIsNonNull8(x);
}
diff --git a/tests/compiler/dart2js/inference/data/null.dart b/tests/compiler/dart2js/inference/data/null.dart
index 9e53378..05fd76b 100644
--- a/tests/compiler/dart2js/inference/data/null.dart
+++ b/tests/compiler/dart2js/inference/data/null.dart
@@ -24,7 +24,7 @@
/*member: ifThenNullCheck:[exact=JSUInt31]*/
ifThenNullCheck(int /*[null|exact=JSUInt31]*/ value) {
- if (value == null) {
+ if (value /*invoke: [null|subclass=JSInt]*/ == null) {
return 0;
}
return value;
@@ -32,7 +32,7 @@
/*member: ifThenElseNullCheck:[exact=JSUInt31]*/
ifThenElseNullCheck(int /*[null|exact=JSUInt31]*/ value) {
- if (value == null) {
+ if (value /*invoke: [null|subclass=JSInt]*/ == null) {
return 0;
} else {
return value;
@@ -41,7 +41,7 @@
/*member: ifNotThenNullCheck:[exact=JSUInt31]*/
ifNotThenNullCheck(int /*[null|exact=JSUInt31]*/ value) {
- if (value != null) {
+ if (value /*invoke: [null|subclass=JSInt]*/ != null) {
return value;
}
return 0;
@@ -49,7 +49,7 @@
/*member: ifNotThenElseNullCheck:[exact=JSUInt31]*/
ifNotThenElseNullCheck(int /*[null|exact=JSUInt31]*/ value) {
- if (value != null) {
+ if (value /*invoke: [null|subclass=JSInt]*/ != null) {
return value;
} else {
return 0;
@@ -59,7 +59,8 @@
/*member: ifThenNotNullComplexCheck:[exact=JSUInt31]*/
ifThenNotNullComplexCheck(
int /*[null|exact=JSUInt31]*/ a, int /*[null|exact=JSUInt31]*/ b) {
- if (a != null && a /*invoke: [exact=JSUInt31]*/ != b) {
+ if (a /*invoke: [null|subclass=JSInt]*/ != null &&
+ a /*invoke: [exact=JSUInt31]*/ != b) {
return a;
}
return 0;
@@ -68,7 +69,8 @@
/*member: ifThenElseNotNullComplexCheck:[null|exact=JSUInt31]*/
ifThenElseNotNullComplexCheck(
int /*[null|exact=JSUInt31]*/ a, int /*[null|exact=JSUInt31]*/ b) {
- if (a != null && a /*invoke: [exact=JSUInt31]*/ != b) {
+ if (a /*invoke: [null|subclass=JSInt]*/ != null &&
+ a /*invoke: [exact=JSUInt31]*/ != b) {
return a;
}
return a;
@@ -78,7 +80,7 @@
ifThenNotNullGradualCheck1(
int /*[null|exact=JSUInt31]*/ a, int /*[exact=JSUInt31]*/ b) {
if (a /*invoke: [null|exact=JSUInt31]*/ != b) {
- if (a != null) {
+ if (a /*invoke: [null|subclass=JSInt]*/ != null) {
return a;
}
}
@@ -88,7 +90,7 @@
/*member: ifThenNotNullGradualCheck2:[exact=JSUInt31]*/
ifThenNotNullGradualCheck2(
int /*[null|exact=JSUInt31]*/ a, int /*[exact=JSUInt31]*/ b) {
- if (a != null) {
+ if (a /*invoke: [null|subclass=JSInt]*/ != null) {
if (a /*invoke: [exact=JSUInt31]*/ != b) {
return a;
}
diff --git a/tests/compiler/dart2js/inference/data/while.dart b/tests/compiler/dart2js/inference/data/while.dart
index a6f7373..ddafecf 100644
--- a/tests/compiler/dart2js/inference/data/while.dart
+++ b/tests/compiler/dart2js/inference/data/while.dart
@@ -49,7 +49,7 @@
/*member: whileNotNull:[exact=JSString]*/
whileNotNull() {
var o = '';
- while (o != null) {
+ while (o /*invoke: [null|exact=JSString]*/ != null) {
o = o. /*invoke: [exact=JSString]*/ toString();
}
return o;
@@ -62,7 +62,7 @@
/*member: whileNullUnreachable:[exact=JSString]*/
whileNullUnreachable() {
var o = '';
- while (o == null) {
+ while (o /*invoke: [null|exact=JSString]*/ == null) {
o = o. /*invoke: [null]*/ toString();
}
return o;
diff --git a/tests/compiler/dart2js/inference/inference_data/called_in_loop.dart b/tests/compiler/dart2js/inference/inference_data/called_in_loop.dart
index d984418..884773e 100644
--- a/tests/compiler/dart2js/inference/inference_data/called_in_loop.dart
+++ b/tests/compiler/dart2js/inference/inference_data/called_in_loop.dart
@@ -84,7 +84,7 @@
Class.constructorNotCalledInForLoop();
// TODO(johnniwinther): Should we track instance calls in loops?
- /*member: Class.instanceCalledInForLoop:*/
+ /*member: Class.instanceCalledInForLoop:loop*/
instanceCalledInForLoop() {}
/*member: Class.instanceNotCalledInForLoop:*/
diff --git a/tests/compiler/dart2js/inference/inference_data_test.dart b/tests/compiler/dart2js/inference/inference_data_test.dart
index 9041fc7..3aeb592 100644
--- a/tests/compiler/dart2js/inference/inference_data_test.dart
+++ b/tests/compiler/dart2js/inference/inference_data_test.dart
@@ -24,6 +24,7 @@
await checkTests(dataDir, const InferenceDataComputer(),
args: args,
testedConfigs: [strongConfig],
+ supportedMarkers: [strongMarker],
options: [stopAfterTypeInference]);
});
}
diff --git a/tests/compiler/dart2js/inference/inference_test_helper.dart b/tests/compiler/dart2js/inference/inference_test_helper.dart
index 06da02c..d0b6caa 100644
--- a/tests/compiler/dart2js/inference/inference_test_helper.dart
+++ b/tests/compiler/dart2js/inference/inference_test_helper.dart
@@ -146,11 +146,11 @@
ClosureRepresentationInfo info = _closureDataLookup.getClosureInfo(node);
return getMemberValue(info.callMethod);
} else if (node is ir.MethodInvocation) {
- return getTypeMaskValue(result.typeOfSend(node));
+ return getTypeMaskValue(result.typeOfReceiver(node));
} else if (node is ir.PropertyGet) {
- return getTypeMaskValue(result.typeOfGetter(node));
+ return getTypeMaskValue(result.typeOfReceiver(node));
} else if (node is ir.PropertySet) {
- return getTypeMaskValue(result.typeOfSend(node));
+ return getTypeMaskValue(result.typeOfReceiver(node));
} else if (node is ir.ForInStatement) {
if (id.kind == IdKind.iterator) {
return getTypeMaskValue(result.typeOfIterator(node));
diff --git a/tools/VERSION b/tools/VERSION
index 9c854ec..42b0140 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -32,7 +32,7 @@
CHANNEL stable
MAJOR 2
MINOR 7
-PATCH 0
+PATCH 1
PRERELEASE 0
PRERELEASE_PATCH 0
ABI_VERSION 24
diff --git a/tools/apps/update_homebrew/lib/src/impl.dart b/tools/apps/update_homebrew/lib/src/impl.dart
index 4d4cf86..3c1acab 100644
--- a/tools/apps/update_homebrew/lib/src/impl.dart
+++ b/tools/apps/update_homebrew/lib/src/impl.dart
@@ -12,7 +12,6 @@
'linux-arm': 'sdk/dartsdk-linux-arm64-release.zip',
};
const _ia32Files = {
- 'mac': 'sdk/dartsdk-macos-ia32-release.zip',
'linux': 'sdk/dartsdk-linux-ia32-release.zip',
'linux-arm': 'sdk/dartsdk-linux-arm-release.zip',
};
@@ -93,13 +92,8 @@
version "$stableVersion"
if OS.mac?
- if Hardware::CPU.is_64_bit?
- url "$_urlBase/stable/release/${revisions['stable']}/${_x64Files['mac']}"
- sha256 "${hashes['stable'][_x64Files['mac']]}"
- else
- url "$_urlBase/stable/release/${revisions['stable']}/${_ia32Files['mac']}"
- sha256 "${hashes['stable'][_ia32Files['mac']]}"
- end
+ url "$_urlBase/stable/release/${revisions['stable']}/${_x64Files['mac']}"
+ sha256 "${hashes['stable'][_x64Files['mac']]}"
elsif OS.linux? && Hardware::CPU.intel?
if Hardware::CPU.is_64_bit?
url "$_urlBase/stable/release/${revisions['stable']}/${_x64Files['linux']}"
@@ -121,13 +115,8 @@
devel do
version "$devVersion"
if OS.mac?
- if Hardware::CPU.is_64_bit?
- url "$_urlBase/dev/release/${revisions['dev']}/${_x64Files['mac']}"
- sha256 "${hashes['dev'][_x64Files['mac']]}"
- else
- url "$_urlBase/dev/release/${revisions['dev']}/${_ia32Files['mac']}"
- sha256 "${hashes['dev'][_ia32Files['mac']]}"
- end
+ url "$_urlBase/dev/release/${revisions['dev']}/${_x64Files['mac']}"
+ sha256 "${hashes['dev'][_x64Files['mac']]}"
elsif OS.linux? && Hardware::CPU.intel?
if Hardware::CPU.is_64_bit?
url "$_urlBase/dev/release/${revisions['dev']}/${_x64Files['linux']}"
diff --git a/tools/bots/dart_sdk.py b/tools/bots/dart_sdk.py
index a545a97..2bfc0b9 100755
--- a/tools/bots/dart_sdk.py
+++ b/tools/bots/dart_sdk.py
@@ -25,6 +25,8 @@
def BuildArchitectures():
if BUILD_OS == 'linux':
return ['ia32', 'x64', 'arm', 'arm64']
+ elif BUILD_OS == 'macos':
+ return ['x64']
else:
return ['ia32', 'x64']
diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json
index 8854609..0cc2796 100644
--- a/tools/bots/test_matrix.json
+++ b/tools/bots/test_matrix.json
@@ -2041,7 +2041,7 @@
{
"name": "build dart",
"script": "tools/build.py",
- "arguments": ["--arch=ia32,x64",
+ "arguments": ["--arch=x64",
"--mode=release", "create_sdk"]
},
{