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:
Johnni Winther
2017-09-07 10:23:54 +02:00
parent 1cafe28af5
commit 68b3e9b494
8 changed files with 200 additions and 36 deletions
+3 -11
View File
@@ -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) {
+1
View File
@@ -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;
}