Handle local invoke in inferrer
Reviewed in https://codereview.chromium.org/3008133002/ Change-Id: I65595edf34bf4774a9cadc4cb0dc9bf0572dd4df Reviewed-on: https://dart-review.googlesource.com/3883 Reviewed-by: Morten Krogh-jespersen <mkroghj@google.com>
This commit is contained in:
@@ -2798,17 +2798,9 @@ class ElementGraphBuilder extends ast.Visitor<TypeInformation>
|
||||
}
|
||||
}
|
||||
|
||||
return inferrer.registerCalledSelector(
|
||||
callType,
|
||||
node,
|
||||
selector,
|
||||
mask,
|
||||
receiverType,
|
||||
outermostElement,
|
||||
arguments,
|
||||
sideEffects,
|
||||
inLoop,
|
||||
isConditional);
|
||||
return inferrer.registerCalledSelector(callType, node, selector, mask,
|
||||
receiverType, outermostElement, arguments, sideEffects,
|
||||
inLoop: inLoop, isConditional: isConditional);
|
||||
}
|
||||
|
||||
TypeInformation handleDynamicInvoke(ast.Send node) {
|
||||
|
||||
@@ -12,6 +12,9 @@ import '../elements/types.dart';
|
||||
import '../kernel/element_map.dart';
|
||||
import '../options.dart';
|
||||
import '../types/constants.dart';
|
||||
import '../types/types.dart';
|
||||
import '../universe/selector.dart';
|
||||
import '../universe/side_effects.dart';
|
||||
import '../world.dart';
|
||||
import 'inferrer_engine.dart';
|
||||
import 'locals_handler.dart';
|
||||
@@ -35,6 +38,8 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
final KernelToElementMapForBuilding _elementMap;
|
||||
final KernelToLocalsMap _localsMap;
|
||||
LocalsHandler _locals;
|
||||
final GlobalTypeInferenceElementData<ir.Node> _memberData;
|
||||
SideEffects _sideEffects = new SideEffects.empty();
|
||||
|
||||
TypeInformation _returnType;
|
||||
|
||||
@@ -48,7 +53,8 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
this._elementMap,
|
||||
this._localsMap,
|
||||
[this._locals])
|
||||
: this._types = _inferrer.types {
|
||||
: this._types = _inferrer.types,
|
||||
this._memberData = _inferrer.dataOfMember(_analyzedMember) {
|
||||
if (_locals != null) return;
|
||||
|
||||
FieldInitializationScope<ir.Node> fieldScope =
|
||||
@@ -59,6 +65,10 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
_inferrer, _types, _options, _analyzedNode, fieldScope);
|
||||
}
|
||||
|
||||
int _loopLevel = 0;
|
||||
|
||||
bool get inLoop => _loopLevel > 0;
|
||||
|
||||
TypeInformation run() {
|
||||
if (_analyzedMember.isField) {
|
||||
if (_analyzedNode == null || _analyzedNode is ir.NullLiteral) {
|
||||
@@ -254,4 +264,62 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
_locals.update(local, rhsType, node, type);
|
||||
return rhsType;
|
||||
}
|
||||
|
||||
ArgumentsTypes analyzeArguments(ir.Arguments arguments) {
|
||||
List<TypeInformation> positional = <TypeInformation>[];
|
||||
Map<String, TypeInformation> named;
|
||||
for (ir.Expression argument in arguments.positional) {
|
||||
positional.add(argument.accept(this));
|
||||
}
|
||||
for (ir.NamedExpression argument in arguments.named) {
|
||||
named ??= <String, TypeInformation>{};
|
||||
named[argument.name] = argument.value.accept(this);
|
||||
}
|
||||
|
||||
/// TODO(johnniwinther): Track `isThisExposed`.
|
||||
return new ArgumentsTypes(positional, named);
|
||||
}
|
||||
|
||||
@override
|
||||
TypeInformation visitMethodInvocation(ir.MethodInvocation node) {
|
||||
TypeInformation receiverType = visit(node.receiver);
|
||||
Selector selector = _elementMap.getSelector(node);
|
||||
TypeMask mask = _memberData.typeOfSend(node);
|
||||
|
||||
ArgumentsTypes arguments = analyzeArguments(node.arguments);
|
||||
if (selector.name == '==' || selector.name == '!=') {
|
||||
if (_types.isNull(receiverType)) {
|
||||
// TODO(johnniwinther): Add null check.
|
||||
return _types.boolType;
|
||||
} else if (_types.isNull(arguments.positional[0])) {
|
||||
// TODO(johnniwinther): Add null check.
|
||||
return _types.boolType;
|
||||
}
|
||||
}
|
||||
return handleDynamicInvoke(
|
||||
CallType.access, node, selector, mask, receiverType, arguments);
|
||||
}
|
||||
|
||||
TypeInformation handleDynamicInvoke(
|
||||
CallType callType,
|
||||
ir.Node node,
|
||||
Selector selector,
|
||||
TypeMask mask,
|
||||
TypeInformation receiverType,
|
||||
ArgumentsTypes arguments) {
|
||||
assert(receiverType != null);
|
||||
if (_types.selectorNeedsUpdate(receiverType, mask)) {
|
||||
mask = receiverType == _types.dynamicType
|
||||
? null
|
||||
: _types.newTypedSelector(receiverType, mask);
|
||||
_inferrer.updateSelectorInMember(
|
||||
_analyzedMember, callType, node, selector, mask);
|
||||
}
|
||||
|
||||
// TODO(johnniwinther): Refine receiver on non-captured locals.
|
||||
|
||||
return _inferrer.registerCalledSelector(callType, node, selector, mask,
|
||||
receiverType, _analyzedMember, arguments, _sideEffects,
|
||||
inLoop: inLoop, isConditional: false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,9 +123,9 @@ abstract class InferrerEngine<T> {
|
||||
Iterable<MemberEntity> getCallersOf(MemberEntity element);
|
||||
|
||||
// TODO(johnniwinther): Make this private again.
|
||||
GlobalTypeInferenceElementData dataOfMember(MemberEntity element);
|
||||
GlobalTypeInferenceElementData<T> dataOfMember(MemberEntity element);
|
||||
|
||||
GlobalTypeInferenceElementData lookupDataOfMember(MemberEntity element);
|
||||
GlobalTypeInferenceElementData<T> lookupDataOfMember(MemberEntity element);
|
||||
|
||||
bool checkIfExposesThis(ConstructorEntity element);
|
||||
|
||||
@@ -194,8 +194,8 @@ abstract class InferrerEngine<T> {
|
||||
MemberEntity caller,
|
||||
ArgumentsTypes arguments,
|
||||
SideEffects sideEffects,
|
||||
bool inLoop,
|
||||
bool isConditional);
|
||||
{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
|
||||
@@ -1004,8 +1004,8 @@ abstract class InferrerEngineImpl<T> extends InferrerEngine<T> {
|
||||
MemberEntity caller,
|
||||
ArgumentsTypes arguments,
|
||||
SideEffects sideEffects,
|
||||
bool inLoop,
|
||||
bool isConditional) {
|
||||
{bool inLoop,
|
||||
bool isConditional}) {
|
||||
if (selector.isClosureCall) {
|
||||
return registerCalledClosure(node, selector, mask, receiverType, caller,
|
||||
arguments, sideEffects, inLoop);
|
||||
@@ -1105,7 +1105,7 @@ abstract class InferrerEngineImpl<T> extends InferrerEngine<T> {
|
||||
}
|
||||
|
||||
TypeInformation typeOfMemberWithSelector(
|
||||
covariant MemberElement element, Selector selector) {
|
||||
MemberEntity element, Selector selector) {
|
||||
if (element.name == Identifiers.noSuchMethod_ &&
|
||||
selector.name != element.name) {
|
||||
// An invocation can resolve to a [noSuchMethod], in which case
|
||||
@@ -1119,11 +1119,11 @@ abstract class InferrerEngineImpl<T> extends InferrerEngine<T> {
|
||||
: types.functionType;
|
||||
} else if (element.isField) {
|
||||
return typeOfMember(element);
|
||||
} else if (Elements.isUnresolved(element)) {
|
||||
return types.dynamicType;
|
||||
} else {
|
||||
assert(element.isGetter);
|
||||
} else if (element.isGetter) {
|
||||
return returnTypeOfMember(element);
|
||||
} else {
|
||||
assert(element is MemberElement && Elements.isUnresolved(element));
|
||||
return types.dynamicType;
|
||||
}
|
||||
} else if (element.isGetter || element.isField) {
|
||||
assert(selector.isCall || selector.isSetter);
|
||||
|
||||
@@ -205,7 +205,7 @@ class KernelInferrerEngine extends InferrerEngineImpl<ir.Node> {
|
||||
|
||||
@override
|
||||
GlobalTypeInferenceElementData<ir.Node> createElementData() {
|
||||
throw new UnimplementedError('KernelInferrerEngine.createElementData');
|
||||
return new KernelGlobalTypeInferenceElementData();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,3 +343,81 @@ class KernelTypeSystemStrategy implements TypeSystemStrategy<ir.Node> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class KernelGlobalTypeInferenceElementData
|
||||
extends GlobalTypeInferenceElementData<ir.Node> {
|
||||
// TODO(johnniwinther): Rename this together with [typeOfSend].
|
||||
Map<ir.Node, TypeMask> _sendMap;
|
||||
|
||||
@override
|
||||
TypeMask typeOfSend(ir.Node node) {
|
||||
if (_sendMap == null) return null;
|
||||
return _sendMap[node];
|
||||
}
|
||||
|
||||
@override
|
||||
void setCurrentTypeMask(ir.Node node, TypeMask mask) {
|
||||
throw new UnsupportedError(
|
||||
'KernelGlobalTypeInferenceElementData.setCurrentTypeMask');
|
||||
}
|
||||
|
||||
@override
|
||||
void setMoveNextTypeMask(ir.Node node, TypeMask mask) {
|
||||
throw new UnsupportedError(
|
||||
'KernelGlobalTypeInferenceElementData.setMoveNextTypeMask');
|
||||
}
|
||||
|
||||
@override
|
||||
void setIteratorTypeMask(ir.Node node, TypeMask mask) {
|
||||
throw new UnsupportedError(
|
||||
'KernelGlobalTypeInferenceElementData.setIteratorTypeMask');
|
||||
}
|
||||
|
||||
@override
|
||||
TypeMask typeOfIteratorCurrent(ir.Node node) {
|
||||
throw new UnsupportedError(
|
||||
'KernelGlobalTypeInferenceElementData.typeOfIteratorCurrent');
|
||||
}
|
||||
|
||||
@override
|
||||
TypeMask typeOfIteratorMoveNext(ir.Node node) {
|
||||
throw new UnsupportedError(
|
||||
'KernelGlobalTypeInferenceElementData.typeOfIteratorMoveNext');
|
||||
}
|
||||
|
||||
@override
|
||||
TypeMask typeOfIterator(ir.Node node) {
|
||||
throw new UnsupportedError(
|
||||
'KernelGlobalTypeInferenceElementData.typeOfIterator');
|
||||
}
|
||||
|
||||
@override
|
||||
void setOperatorTypeMaskInComplexSendSet(ir.Node node, TypeMask mask) {
|
||||
throw new UnsupportedError(
|
||||
'KernelGlobalTypeInferenceElementData.setOperatorTypeMaskInComplexSendSet');
|
||||
}
|
||||
|
||||
@override
|
||||
void setGetterTypeMaskInComplexSendSet(ir.Node node, TypeMask mask) {
|
||||
throw new UnsupportedError(
|
||||
'KernelGlobalTypeInferenceElementData.setGetterTypeMaskInComplexSendSet');
|
||||
}
|
||||
|
||||
@override
|
||||
void setTypeMask(ir.Node node, TypeMask mask) {
|
||||
_sendMap ??= <ir.Node, TypeMask>{};
|
||||
_sendMap[node] = mask;
|
||||
}
|
||||
|
||||
@override
|
||||
TypeMask typeOfOperator(ir.Node node) {
|
||||
throw new UnsupportedError(
|
||||
'KernelGlobalTypeInferenceElementData.typeOfOperator');
|
||||
}
|
||||
|
||||
@override
|
||||
TypeMask typeOfGetter(ir.Node node) {
|
||||
throw new UnsupportedError(
|
||||
'KernelGlobalTypeInferenceElementData.typeOfGetter');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ library compiler.src.inferrer.type_graph_nodes;
|
||||
|
||||
import 'dart:collection' show IterableBase;
|
||||
|
||||
import 'package:kernel/ast.dart' as ir;
|
||||
|
||||
import '../common.dart';
|
||||
import '../common/names.dart' show Identifiers;
|
||||
import '../constants/values.dart';
|
||||
@@ -382,12 +384,13 @@ abstract class MemberTypeInformation extends ElementTypeInformation
|
||||
* to enable counting the global number of call sites of [element].
|
||||
*
|
||||
* A call site is either an AST [ast.Node], an [Element] (see uses of
|
||||
* [synthesizeForwardingCall] in [SimpleTypeInferrerVisitor]).
|
||||
* [synthesizeForwardingCall] in [SimpleTypeInferrerVisitor]) or an IR
|
||||
* [ir.Node].
|
||||
*
|
||||
* The global information is summarized in [cleanup], after which [_callers]
|
||||
* is set to `null`.
|
||||
*/
|
||||
Map<MemberEntity, Setlet<Spannable>> _callers;
|
||||
Map<MemberEntity, Setlet<Object>> _callers;
|
||||
|
||||
MemberTypeInformation._internal(this._member) : super._internal(null) {
|
||||
assert(_checkMember(_member));
|
||||
@@ -401,12 +404,12 @@ abstract class MemberTypeInformation extends ElementTypeInformation
|
||||
|
||||
String get debugName => '$member';
|
||||
|
||||
void addCall(MemberEntity caller, Spannable node) {
|
||||
_callers ??= <MemberEntity, Setlet<Spannable>>{};
|
||||
void addCall(MemberEntity caller, Object node) {
|
||||
_callers ??= <MemberEntity, Setlet<Object>>{};
|
||||
_callers.putIfAbsent(caller, () => new Setlet()).add(node);
|
||||
}
|
||||
|
||||
void removeCall(MemberEntity caller, node) {
|
||||
void removeCall(MemberEntity caller, Object node) {
|
||||
if (_callers == null) return;
|
||||
Setlet calls = _callers[caller];
|
||||
if (calls == null) return;
|
||||
@@ -831,7 +834,7 @@ bool validCallType(CallType callType, Object call) {
|
||||
case CallType.complex:
|
||||
return call is ast.SendSet;
|
||||
case CallType.access:
|
||||
return call is ast.Send;
|
||||
return call is ast.Send || call is ir.Node;
|
||||
case CallType.forIn:
|
||||
return call is ast.ForIn;
|
||||
}
|
||||
@@ -863,7 +866,8 @@ abstract class CallSiteTypeInformation extends TypeInformation
|
||||
assert(_checkCaller(caller));
|
||||
// [_call] is either an AST node or a constructor element in case of a
|
||||
// a forwarding constructor _call.
|
||||
assert(_call is ast.Node || _call is ConstructorElement);
|
||||
assert(
|
||||
_call is ast.Node || _call is ConstructorElement || _call is ir.Node);
|
||||
}
|
||||
|
||||
bool _checkCaller(MemberEntity caller) {
|
||||
|
||||
@@ -152,6 +152,7 @@ class GlobalTypeInferenceParameterResultImpl<T>
|
||||
/// Internal data used during type-inference to store intermediate results about
|
||||
/// a single element.
|
||||
abstract class GlobalTypeInferenceElementData<T> {
|
||||
// TODO(johnniwinther): Remove this. Maybe split by access/invoke.
|
||||
TypeMask typeOfSend(T node);
|
||||
TypeMask typeOfGetter(T node);
|
||||
TypeMask typeOfOperator(T node);
|
||||
|
||||
@@ -133,17 +133,15 @@ class IdData {
|
||||
IdValue value = expectedMap[id];
|
||||
if (data.value != value || value == null && data.value.value != '') {
|
||||
String expected = value?.toString() ?? '';
|
||||
int offset = getOffsetFromId(id);
|
||||
annotations
|
||||
.putIfAbsent(data.sourceSpan.begin, () => [])
|
||||
.putIfAbsent(offset, () => [])
|
||||
.add('${expected} | ${data.value}');
|
||||
}
|
||||
});
|
||||
expectedMap.forEach((Id id, IdValue expected) {
|
||||
if (!actualMap.containsKey(id)) {
|
||||
int offset = compiler.reporter
|
||||
.spanFromSpannable(
|
||||
computeSpannable(elementEnvironment, mainUri, id))
|
||||
.begin;
|
||||
int offset = getOffsetFromId(id);
|
||||
annotations.putIfAbsent(offset, () => []).add('${expected} | ---');
|
||||
}
|
||||
});
|
||||
@@ -171,6 +169,12 @@ class IdData {
|
||||
});
|
||||
return withAnnotations(annotations);
|
||||
}
|
||||
|
||||
int getOffsetFromId(Id id) {
|
||||
return compiler.reporter
|
||||
.spanFromSpannable(computeSpannable(elementEnvironment, mainUri, id))
|
||||
.begin;
|
||||
}
|
||||
}
|
||||
|
||||
/// Check code for all test files int [data] using [computeFromAst] and
|
||||
|
||||
@@ -7,6 +7,8 @@ main() {
|
||||
uninitializedLocal();
|
||||
initializedLocal();
|
||||
updatedLocal();
|
||||
invokeLocal();
|
||||
postfixLocal();
|
||||
}
|
||||
|
||||
/*element: uninitializedLocal:[null]*/
|
||||
@@ -27,3 +29,18 @@ updatedLocal() {
|
||||
local2 = 0;
|
||||
return local2;
|
||||
}
|
||||
|
||||
/*element: invokeLocal:[null]*/
|
||||
invokeLocal() {
|
||||
var local2 = 0;
|
||||
local2. /*invoke: [exact=JSUInt31]*/ toString();
|
||||
return null;
|
||||
}
|
||||
|
||||
/*element: postfixLocal:[null]*/
|
||||
postfixLocal() {
|
||||
// ignore: UNUSED_LOCAL_VARIABLE
|
||||
var local2 = 0;
|
||||
local2 /*invoke: [exact=JSUInt31]*/ ++;
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user