mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 09:22:12 +00:00
Split ast based parts from InferrerEngineImpl
R=sigmund@google.com Review-Url: https://codereview.chromium.org/2991313002 .
This commit is contained in:
parent
1df5b00122
commit
93d0a55534
530
pkg/compiler/lib/src/inferrer/ast_inferrer_engine.dart
Normal file
530
pkg/compiler/lib/src/inferrer/ast_inferrer_engine.dart
Normal file
|
@ -0,0 +1,530 @@
|
|||
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import '../common.dart';
|
||||
import '../common/names.dart';
|
||||
import '../compiler.dart';
|
||||
import '../constants/expressions.dart';
|
||||
import '../constants/values.dart';
|
||||
import '../elements/elements.dart';
|
||||
import '../elements/entities.dart';
|
||||
import '../resolution/tree_elements.dart';
|
||||
import '../tree/nodes.dart' as ast;
|
||||
import '../types/constants.dart';
|
||||
import '../types/types.dart';
|
||||
import '../universe/selector.dart';
|
||||
import '../util/util.dart';
|
||||
import '../world.dart';
|
||||
import 'closure_tracer.dart';
|
||||
import 'debug.dart' as debug;
|
||||
import 'locals_handler.dart';
|
||||
import 'builder.dart';
|
||||
import 'builder_kernel.dart';
|
||||
import 'inferrer_engine.dart';
|
||||
import 'type_graph_dump.dart';
|
||||
import 'type_graph_nodes.dart';
|
||||
import 'type_system.dart';
|
||||
|
||||
class AstInferrerEngine extends InferrerEngineImpl<ast.Node> {
|
||||
AstInferrerEngine(Compiler compiler, ClosedWorld closedWorld,
|
||||
ClosedWorldRefiner closedWorldRefiner, FunctionEntity mainElement)
|
||||
: super(compiler, closedWorld, closedWorldRefiner, mainElement,
|
||||
const TypeSystemStrategyImpl());
|
||||
|
||||
GlobalTypeInferenceElementData<ast.Node> createElementData() =>
|
||||
new AstGlobalTypeInferenceElementData();
|
||||
|
||||
void runOverAllElements() {
|
||||
if (compiler.disableTypeInference) return;
|
||||
if (compiler.options.verbose) {
|
||||
compiler.progress.reset();
|
||||
}
|
||||
sortResolvedAsts().forEach((ResolvedAst resolvedAst) {
|
||||
if (compiler.shouldPrintProgress) {
|
||||
reporter.log('Added $addedInGraph elements in inferencing graph.');
|
||||
compiler.progress.reset();
|
||||
}
|
||||
// This also forces the creation of the [ElementTypeInformation] to ensure
|
||||
// it is in the graph.
|
||||
MemberElement member = resolvedAst.element;
|
||||
ast.Node body;
|
||||
if (resolvedAst.kind == ResolvedAstKind.PARSED) {
|
||||
body = resolvedAst.body;
|
||||
}
|
||||
types.withMember(member, () => analyze(member, body, null));
|
||||
});
|
||||
reporter.log('Added $addedInGraph elements in inferencing graph.');
|
||||
|
||||
TypeGraphDump dump = debug.PRINT_GRAPH ? new TypeGraphDump(this) : null;
|
||||
|
||||
dump?.beforeAnalysis();
|
||||
buildWorkQueue();
|
||||
refine();
|
||||
|
||||
// Try to infer element types of lists and compute their escape information.
|
||||
types.allocatedLists.values.forEach((TypeInformation info) {
|
||||
analyzeListAndEnqueue(info);
|
||||
});
|
||||
|
||||
// Try to infer the key and value types for maps and compute the values'
|
||||
// escape information.
|
||||
types.allocatedMaps.values.forEach((TypeInformation info) {
|
||||
analyzeMapAndEnqueue(info);
|
||||
});
|
||||
|
||||
Set<FunctionEntity> bailedOutOn = new Set<FunctionEntity>();
|
||||
|
||||
// Trace closures to potentially infer argument types.
|
||||
types.allocatedClosures.forEach((dynamic info) {
|
||||
void trace(
|
||||
Iterable<FunctionEntity> elements, ClosureTracerVisitor tracer) {
|
||||
tracer.run();
|
||||
if (!tracer.continueAnalyzing) {
|
||||
elements.forEach((FunctionEntity _element) {
|
||||
MethodElement element = _element;
|
||||
MethodElement implementation = element.implementation;
|
||||
closedWorldRefiner.registerMightBePassedToApply(element);
|
||||
if (debug.VERBOSE) {
|
||||
print("traced closure $element as ${true} (bail)");
|
||||
}
|
||||
implementation.functionSignature
|
||||
.forEachParameter((FormalElement _parameter) {
|
||||
ParameterElement parameter = _parameter;
|
||||
types
|
||||
.getInferredTypeOfParameter(parameter)
|
||||
.giveUp(this, clearAssignments: false);
|
||||
});
|
||||
});
|
||||
bailedOutOn.addAll(elements);
|
||||
return;
|
||||
}
|
||||
elements
|
||||
.where((e) => !bailedOutOn.contains(e))
|
||||
.forEach((FunctionEntity _element) {
|
||||
MethodElement element = _element;
|
||||
MethodElement implementation = element.implementation;
|
||||
implementation.functionSignature
|
||||
.forEachParameter((FormalElement _parameter) {
|
||||
ParameterElement parameter = _parameter;
|
||||
ParameterTypeInformation info =
|
||||
types.getInferredTypeOfParameter(parameter);
|
||||
info.maybeResume();
|
||||
workQueue.add(info);
|
||||
});
|
||||
if (tracer.tracedType.mightBePassedToFunctionApply) {
|
||||
closedWorldRefiner.registerMightBePassedToApply(element);
|
||||
}
|
||||
if (debug.VERBOSE) {
|
||||
print("traced closure $element as "
|
||||
"${closedWorldRefiner
|
||||
.getCurrentlyKnownMightBePassedToApply(element)}");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (info is ClosureTypeInformation) {
|
||||
Iterable<FunctionEntity> elements = [info.closure];
|
||||
trace(elements, new ClosureTracerVisitor(elements, info, this));
|
||||
} else if (info is CallSiteTypeInformation) {
|
||||
if (info is StaticCallSiteTypeInformation &&
|
||||
info.selector != null &&
|
||||
info.selector.isCall) {
|
||||
// This is a constructor call to a class with a call method. So we
|
||||
// need to trace the call method here.
|
||||
MethodElement calledElement = info.calledElement;
|
||||
assert(calledElement.isGenerativeConstructor);
|
||||
ClassElement cls = calledElement.enclosingClass;
|
||||
MethodElement callMethod = cls.lookupMember(Identifiers.call);
|
||||
if (callMethod == null) {
|
||||
callMethod = cls.lookupMember(Identifiers.noSuchMethod_);
|
||||
}
|
||||
assert(callMethod != null, failedAt(cls));
|
||||
Iterable<FunctionEntity> elements = [callMethod];
|
||||
trace(elements, new ClosureTracerVisitor(elements, info, this));
|
||||
} else {
|
||||
// We only are interested in functions here, as other targets
|
||||
// of this closure call are not a root to trace but an intermediate
|
||||
// for some other function.
|
||||
Iterable<FunctionEntity> elements = new List<FunctionEntity>.from(
|
||||
info.callees.where((e) => e.isFunction));
|
||||
trace(elements, new ClosureTracerVisitor(elements, info, this));
|
||||
}
|
||||
} else if (info is MemberTypeInformation) {
|
||||
trace(<FunctionEntity>[info.member],
|
||||
new StaticTearOffClosureTracerVisitor(info.member, info, this));
|
||||
} else if (info is ParameterTypeInformation) {
|
||||
failedAt(
|
||||
NO_LOCATION_SPANNABLE, 'Unexpected closure allocation info $info');
|
||||
}
|
||||
});
|
||||
|
||||
dump?.beforeTracing();
|
||||
|
||||
// Reset all nodes that use lists/maps that have been inferred, as well
|
||||
// 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();
|
||||
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(seenTypes);
|
||||
refine();
|
||||
|
||||
if (debug.PRINT_SUMMARY) {
|
||||
types.allocatedLists.values.forEach((_info) {
|
||||
ListTypeInformation info = _info;
|
||||
print('${info.type} '
|
||||
'for ${info.originalType.allocationNode} '
|
||||
'at ${info.originalType.allocationElement} '
|
||||
'after ${info.refineCount}');
|
||||
});
|
||||
types.allocatedMaps.values.forEach((_info) {
|
||||
MapTypeInformation info = _info;
|
||||
print('${info.type} '
|
||||
'for ${info.originalType.allocationNode} '
|
||||
'at ${info.originalType.allocationElement} '
|
||||
'after ${info.refineCount}');
|
||||
});
|
||||
types.allocatedClosures.forEach((TypeInformation info) {
|
||||
if (info is ElementTypeInformation) {
|
||||
print('${info.getInferredSignature(types)} for '
|
||||
'${info.debugName}');
|
||||
} else if (info is ClosureTypeInformation) {
|
||||
print('${info.getInferredSignature(types)} for '
|
||||
'${info.debugName}');
|
||||
} else if (info is DynamicCallSiteTypeInformation) {
|
||||
for (MemberEntity target in info.targets) {
|
||||
if (target is FunctionEntity) {
|
||||
print(
|
||||
'${types.getInferredSignatureOfMethod(target)} for ${target}');
|
||||
} else {
|
||||
print(
|
||||
'${types.getInferredTypeOfMember(target).type} for ${target}');
|
||||
}
|
||||
}
|
||||
} else if (info is StaticCallSiteTypeInformation) {
|
||||
ClassElement cls = info.calledElement.enclosingClass;
|
||||
MethodElement callMethod = cls.lookupMember(Identifiers.call);
|
||||
print('${types.getInferredSignatureOfMethod(callMethod)} for ${cls}');
|
||||
} else {
|
||||
print('${info.type} for some unknown kind of closure');
|
||||
}
|
||||
});
|
||||
analyzedElements.forEach((MemberEntity elem) {
|
||||
TypeInformation type = types.getInferredTypeOfMember(elem);
|
||||
print('${elem} :: ${type} from ${type.assignments} ');
|
||||
});
|
||||
}
|
||||
dump?.afterAnalysis();
|
||||
|
||||
reporter.log('Inferred $overallRefineCount types.');
|
||||
|
||||
processLoopInformation();
|
||||
}
|
||||
|
||||
void analyze(MemberEntity element, ast.Node body, ArgumentsTypes arguments) {
|
||||
assert(!(element is MemberElement && !element.isDeclaration));
|
||||
if (analyzedElements.contains(element)) return;
|
||||
analyzedElements.add(element);
|
||||
|
||||
dynamic visitor = compiler.options.kernelGlobalInference
|
||||
? new KernelTypeGraphBuilder(element, compiler, this)
|
||||
: new ElementGraphBuilder(element, compiler, this);
|
||||
TypeInformation type;
|
||||
reporter.withCurrentElement(element, () {
|
||||
// ignore: UNDEFINED_METHOD
|
||||
type = visitor.run();
|
||||
});
|
||||
addedInGraph++;
|
||||
|
||||
if (element.isField) {
|
||||
FieldElement field = element;
|
||||
if (field.isFinal || field.isConst) {
|
||||
// If [element] is final and has an initializer, we record
|
||||
// the inferred type.
|
||||
if (body != null) {
|
||||
if (type is! ListTypeInformation && type is! MapTypeInformation) {
|
||||
// For non-container types, the constant handler does
|
||||
// constant folding that could give more precise results.
|
||||
ConstantExpression constant = field.constant;
|
||||
if (constant != null) {
|
||||
ConstantValue value =
|
||||
compiler.backend.constants.getConstantValue(constant);
|
||||
if (value != null) {
|
||||
if (value.isFunction) {
|
||||
FunctionConstantValue functionConstant = value;
|
||||
MethodElement function = functionConstant.element;
|
||||
type = types.allocateClosure(function);
|
||||
} else {
|
||||
// Although we might find a better type, we have to keep
|
||||
// the old type around to ensure that we get a complete view
|
||||
// of the type graph and do not drop any flow edges.
|
||||
TypeMask refinedType = computeTypeMask(closedWorld, value);
|
||||
assert(TypeMask.assertIsNormalized(refinedType, closedWorld));
|
||||
type = new NarrowTypeInformation(type, refinedType);
|
||||
types.allocatedTypes.add(type);
|
||||
}
|
||||
} else {
|
||||
assert(
|
||||
field.isInstanceMember ||
|
||||
constant.isImplicit ||
|
||||
constant.isPotential,
|
||||
failedAt(
|
||||
field,
|
||||
"Constant expression without value: "
|
||||
"${constant.toStructuredText()}."));
|
||||
}
|
||||
}
|
||||
}
|
||||
recordTypeOfField(field, type);
|
||||
} else if (!element.isInstanceMember) {
|
||||
recordTypeOfField(field, types.nullType);
|
||||
}
|
||||
} else if (body == null) {
|
||||
// Only update types of static fields if there is no
|
||||
// assignment. Instance fields are dealt with in the constructor.
|
||||
if (element.isStatic || element.isTopLevel) {
|
||||
recordTypeOfField(field, type);
|
||||
}
|
||||
} else {
|
||||
recordTypeOfField(field, type);
|
||||
}
|
||||
if ((element.isStatic || element.isTopLevel) &&
|
||||
body != null &&
|
||||
!element.isConst) {
|
||||
dynamic argument = body;
|
||||
// TODO(13429): We could do better here by using the
|
||||
// constant handler to figure out if it's a lazy field or not.
|
||||
if (argument.asSend() != null ||
|
||||
(argument.asNewExpression() != null && !argument.isConst)) {
|
||||
recordTypeOfField(field, types.nullType);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
FunctionEntity method = element;
|
||||
recordReturnType(method, type);
|
||||
}
|
||||
}
|
||||
|
||||
void updateParameterAssignments(TypeInformation caller, MemberEntity callee,
|
||||
ArgumentsTypes arguments, Selector selector, TypeMask mask,
|
||||
{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]);
|
||||
} else {
|
||||
info.addAssignment(arguments.positional[0]);
|
||||
}
|
||||
if (addToQueue) workQueue.add(info);
|
||||
}
|
||||
} else if (callee.isGetter) {
|
||||
return;
|
||||
} else if (selector != null && selector.isGetter) {
|
||||
// We are tearing a function off and thus create a closure.
|
||||
assert(callee.isFunction);
|
||||
MethodElement method = callee;
|
||||
MemberTypeInformation info = types.getInferredTypeOfMember(method);
|
||||
if (remove) {
|
||||
info.closurizedCount--;
|
||||
} else {
|
||||
info.closurizedCount++;
|
||||
if (Elements.isStaticOrTopLevel(method)) {
|
||||
types.allocatedClosures.add(info);
|
||||
} else {
|
||||
// We add the call-site type information here so that we
|
||||
// can benefit from further refinement of the selector.
|
||||
types.allocatedClosures.add(caller);
|
||||
}
|
||||
FunctionElement function = method.implementation;
|
||||
FunctionSignature signature = function.functionSignature;
|
||||
signature.forEachParameter((FormalElement _parameter) {
|
||||
ParameterElement parameter = _parameter;
|
||||
ParameterTypeInformation info =
|
||||
types.getInferredTypeOfParameter(parameter);
|
||||
info.tagAsTearOffClosureParameter(this);
|
||||
if (addToQueue) workQueue.add(info);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
MethodElement method = callee;
|
||||
FunctionElement function = method.implementation;
|
||||
FunctionSignature signature = function.functionSignature;
|
||||
int parameterIndex = 0;
|
||||
bool visitingRequiredParameter = true;
|
||||
signature.forEachParameter((FormalElement _parameter) {
|
||||
ParameterElement parameter = _parameter;
|
||||
if (signature.hasOptionalParameters &&
|
||||
parameter == signature.optionalParameters.first) {
|
||||
visitingRequiredParameter = false;
|
||||
}
|
||||
TypeInformation type = visitingRequiredParameter
|
||||
? arguments.positional[parameterIndex]
|
||||
: signature.optionalParametersAreNamed
|
||||
? arguments.named[parameter.name]
|
||||
: parameterIndex < arguments.positional.length
|
||||
? arguments.positional[parameterIndex]
|
||||
: null;
|
||||
if (type == null) type = getDefaultTypeOfParameter(parameter);
|
||||
TypeInformation info = types.getInferredTypeOfParameter(parameter);
|
||||
if (remove) {
|
||||
info.removeAssignment(type);
|
||||
} else {
|
||||
info.addAssignment(type);
|
||||
}
|
||||
parameterIndex++;
|
||||
if (addToQueue) workQueue.add(info);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Sorts the resolved elements by size. We do this for this inferrer
|
||||
// to get the same results for [ListTracer] compared to the
|
||||
// [SimpleTypesInferrer].
|
||||
Iterable<ResolvedAst> sortResolvedAsts() {
|
||||
int max = 0;
|
||||
Map<int, Setlet<ResolvedAst>> methodSizes = <int, Setlet<ResolvedAst>>{};
|
||||
compiler.enqueuer.resolution.processedEntities.forEach((_element) {
|
||||
MemberElement element = _element;
|
||||
ResolvedAst resolvedAst = element.resolvedAst;
|
||||
element = element.implementation;
|
||||
if (element.impliesType) return;
|
||||
assert(
|
||||
element.isField ||
|
||||
element.isFunction ||
|
||||
element.isConstructor ||
|
||||
element.isGetter ||
|
||||
element.isSetter,
|
||||
failedAt(element, 'Unexpected element kind: ${element.kind}'));
|
||||
if (element.isAbstract) return;
|
||||
// Put the other operators in buckets by length, later to be added in
|
||||
// length order.
|
||||
int length = 0;
|
||||
if (resolvedAst.kind == ResolvedAstKind.PARSED) {
|
||||
TreeElementMapping mapping = resolvedAst.elements;
|
||||
length = mapping.getSelectorCount();
|
||||
}
|
||||
max = length > max ? length : max;
|
||||
Setlet<ResolvedAst> set =
|
||||
methodSizes.putIfAbsent(length, () => new Setlet<ResolvedAst>());
|
||||
set.add(resolvedAst);
|
||||
});
|
||||
|
||||
List<ResolvedAst> result = <ResolvedAst>[];
|
||||
for (int i = 0; i <= max; i++) {
|
||||
Setlet<ResolvedAst> set = methodSizes[i];
|
||||
if (set != null) result.addAll(set);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
class TypeSystemStrategyImpl implements TypeSystemStrategy<ast.Node> {
|
||||
const TypeSystemStrategyImpl();
|
||||
|
||||
@override
|
||||
MemberTypeInformation createMemberTypeInformation(
|
||||
covariant MemberElement member) {
|
||||
assert(member.isDeclaration, failedAt(member));
|
||||
if (member.isField) {
|
||||
FieldElement field = member;
|
||||
return new FieldTypeInformation(field, field.type);
|
||||
} else if (member.isGetter) {
|
||||
GetterElement getter = member;
|
||||
return new GetterTypeInformation(getter, getter.type);
|
||||
} else if (member.isSetter) {
|
||||
SetterElement setter = member;
|
||||
return new SetterTypeInformation(setter);
|
||||
} else if (member.isFunction) {
|
||||
MethodElement method = member;
|
||||
return new MethodTypeInformation(method, method.type);
|
||||
} else {
|
||||
ConstructorElement constructor = member;
|
||||
if (constructor.isFactoryConstructor) {
|
||||
return new FactoryConstructorTypeInformation(
|
||||
constructor, constructor.type);
|
||||
} else {
|
||||
return new GenerativeConstructorTypeInformation(constructor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
ParameterTypeInformation createParameterTypeInformation(
|
||||
covariant ParameterElement parameter, TypeSystem<ast.Node> types) {
|
||||
assert(parameter.isImplementation, failedAt(parameter));
|
||||
FunctionTypedElement function = parameter.functionDeclaration.declaration;
|
||||
if (function.isLocal) {
|
||||
LocalFunctionElement localFunction = function;
|
||||
MethodElement callMethod = localFunction.callMethod;
|
||||
return new ParameterTypeInformation.localFunction(
|
||||
types.getInferredTypeOfMember(callMethod),
|
||||
parameter,
|
||||
parameter.type,
|
||||
callMethod);
|
||||
} else if (function.isInstanceMember) {
|
||||
MethodElement method = function;
|
||||
return new ParameterTypeInformation.instanceMember(
|
||||
types.getInferredTypeOfMember(method),
|
||||
parameter,
|
||||
parameter.type,
|
||||
method,
|
||||
new ParameterAssignments());
|
||||
} else {
|
||||
MethodElement method = function;
|
||||
return new ParameterTypeInformation.static(
|
||||
types.getInferredTypeOfMember(method),
|
||||
parameter,
|
||||
parameter.type,
|
||||
method,
|
||||
// TODO(johnniwinther): Is this still valid now that initializing
|
||||
// formals also introduce locals?
|
||||
isInitializingFormal: parameter.isInitializingFormal);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void forEachParameter(
|
||||
covariant MethodElement function, void f(Local parameter)) {
|
||||
MethodElement impl = function.implementation;
|
||||
FunctionSignature signature = impl.functionSignature;
|
||||
signature.forEachParameter((FormalElement _parameter) {
|
||||
ParameterElement parameter = _parameter;
|
||||
f(parameter);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
bool checkMapNode(ast.Node node) {
|
||||
return node is ast.LiteralMap;
|
||||
}
|
||||
|
||||
@override
|
||||
bool checkListNode(ast.Node node) {
|
||||
return node is ast.LiteralList || node is ast.Send;
|
||||
}
|
||||
|
||||
@override
|
||||
bool checkLoopPhiNode(ast.Node node) {
|
||||
return node is ast.Loop || node is ast.SwitchStatement;
|
||||
}
|
||||
|
||||
@override
|
||||
bool checkPhiNode(ast.Node node) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
bool checkClassEntity(covariant ClassElement cls) {
|
||||
return cls.isDeclaration;
|
||||
}
|
||||
}
|
|
@ -104,10 +104,11 @@ class ElementGraphBuilder extends ast.Visitor<TypeInformation>
|
|||
new LocalsHandler(inferrer, types, compiler.options, node, fieldScope);
|
||||
}
|
||||
|
||||
ElementGraphBuilder(MemberElement element, ResolvedAst resolvedAst,
|
||||
Compiler compiler, InferrerEngine inferrer, [LocalsHandler handler])
|
||||
: this.internal(element, resolvedAst, element.memberContext.declaration,
|
||||
inferrer, compiler, handler);
|
||||
ElementGraphBuilder(
|
||||
MemberElement element, Compiler compiler, InferrerEngine inferrer,
|
||||
[LocalsHandler handler])
|
||||
: this.internal(element, element.resolvedAst,
|
||||
element.memberContext.declaration, inferrer, compiler, handler);
|
||||
|
||||
TreeElements get elements => resolvedAst.elements;
|
||||
|
||||
|
@ -892,8 +893,13 @@ class ElementGraphBuilder extends ast.Visitor<TypeInformation>
|
|||
|
||||
void analyzeSuperConstructorCall(
|
||||
ConstructorElement target, ArgumentsTypes arguments) {
|
||||
assert(target.isDeclaration);
|
||||
ResolvedAst resolvedAst = target.resolvedAst;
|
||||
inferrer.analyze(resolvedAst, arguments);
|
||||
ast.Node body;
|
||||
if (resolvedAst.kind == ResolvedAstKind.PARSED) {
|
||||
body = resolvedAst.node;
|
||||
}
|
||||
inferrer.analyze(target, body, arguments);
|
||||
isThisExposed = isThisExposed || inferrer.checkIfExposesThis(target);
|
||||
}
|
||||
|
||||
|
@ -956,8 +962,8 @@ class ElementGraphBuilder extends ast.Visitor<TypeInformation>
|
|||
parameter,
|
||||
"Unexpected function declaration "
|
||||
"${declarationMethod}, expected ${analyzedElement}."));
|
||||
visitor = new ElementGraphBuilder(declarationMethod,
|
||||
parameter.functionDeclaration.resolvedAst, compiler, inferrer);
|
||||
visitor =
|
||||
new ElementGraphBuilder(declarationMethod, compiler, inferrer);
|
||||
}
|
||||
TypeInformation type =
|
||||
(defaultValue == null) ? types.nullType : visitor.visit(defaultValue);
|
||||
|
@ -1104,8 +1110,8 @@ class ElementGraphBuilder extends ast.Visitor<TypeInformation>
|
|||
// method, like for example the types of local variables.
|
||||
LocalsHandler closureLocals =
|
||||
new LocalsHandler.from(locals, node, useOtherTryBlock: false);
|
||||
ElementGraphBuilder visitor = new ElementGraphBuilder(element.callMethod,
|
||||
element.resolvedAst, compiler, inferrer, closureLocals);
|
||||
ElementGraphBuilder visitor = new ElementGraphBuilder(
|
||||
element.callMethod, compiler, inferrer, closureLocals);
|
||||
visitor.run();
|
||||
inferrer.recordReturnType(element.callMethod, visitor.returnType);
|
||||
|
||||
|
|
|
@ -65,14 +65,14 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
|||
new LocalsHandler(inferrer, types, compiler.options, node, fieldScope);
|
||||
}
|
||||
|
||||
factory KernelTypeGraphBuilder(MemberElement element, ResolvedAst resolvedAst,
|
||||
Compiler compiler, InferrerEngine inferrer,
|
||||
factory KernelTypeGraphBuilder(
|
||||
MemberElement element, Compiler compiler, InferrerEngine inferrer,
|
||||
[LocalsHandler handler]) {
|
||||
var adapter = _createKernelAdapter(compiler, resolvedAst);
|
||||
var adapter = _createKernelAdapter(compiler, element.resolvedAst);
|
||||
var node = adapter.getMemberNode(element);
|
||||
return new KernelTypeGraphBuilder.internal(
|
||||
element,
|
||||
resolvedAst,
|
||||
element.resolvedAst,
|
||||
element.outermostEnclosingMemberOrTopLevel.implementation,
|
||||
inferrer,
|
||||
compiler,
|
||||
|
|
|
@ -7,32 +7,29 @@ import 'package:kernel/ast.dart' as ir;
|
|||
import '../common.dart';
|
||||
import '../common/names.dart';
|
||||
import '../compiler.dart';
|
||||
import '../constants/expressions.dart';
|
||||
import '../constants/values.dart';
|
||||
import '../common_elements.dart';
|
||||
import '../elements/elements.dart';
|
||||
import '../elements/elements.dart'
|
||||
show
|
||||
ClassElement,
|
||||
ConstructorElement,
|
||||
Elements,
|
||||
MemberElement,
|
||||
ParameterElement;
|
||||
import '../elements/entities.dart';
|
||||
import '../elements/names.dart';
|
||||
import '../js_backend/annotations.dart';
|
||||
import '../js_backend/js_backend.dart';
|
||||
import '../native/behavior.dart' as native;
|
||||
import '../resolution/tree_elements.dart';
|
||||
import '../tree/nodes.dart' as ast;
|
||||
import '../types/constants.dart';
|
||||
import '../types/types.dart';
|
||||
import '../universe/call_structure.dart';
|
||||
import '../universe/selector.dart';
|
||||
import '../universe/side_effects.dart';
|
||||
import '../util/util.dart';
|
||||
import '../world.dart';
|
||||
import 'closure_tracer.dart';
|
||||
import 'debug.dart' as debug;
|
||||
import 'locals_handler.dart';
|
||||
import 'list_tracer.dart';
|
||||
import 'map_tracer.dart';
|
||||
import 'builder.dart';
|
||||
import 'builder_kernel.dart';
|
||||
import 'type_graph_dump.dart';
|
||||
import 'type_graph_inferrer.dart';
|
||||
import 'type_graph_nodes.dart';
|
||||
import 'type_system.dart';
|
||||
|
@ -40,7 +37,7 @@ import 'type_system.dart';
|
|||
/// 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 {
|
||||
abstract class InferrerEngine<T> {
|
||||
/// A set of selector names that [List] implements, that we know return their
|
||||
/// element type.
|
||||
final Set<Selector> returnsListElementTypeSet =
|
||||
|
@ -64,8 +61,8 @@ abstract class InferrerEngine {
|
|||
CommonMasks get commonMasks => closedWorld.commonMasks;
|
||||
CommonElements get commonElements => closedWorld.commonElements;
|
||||
|
||||
TypeSystem<ast.Node> get types;
|
||||
Map<ast.Node, TypeInformation> get concreteTypes;
|
||||
TypeSystem<T> get types;
|
||||
Map<T, TypeInformation> get concreteTypes;
|
||||
|
||||
/// Parallel structure for concreteTypes.
|
||||
// TODO(efortuna): Remove concreteTypes and/or parameterize InferrerEngine by
|
||||
|
@ -76,7 +73,7 @@ abstract class InferrerEngine {
|
|||
|
||||
void runOverAllElements();
|
||||
|
||||
void analyze(ResolvedAst resolvedAst, ArgumentsTypes arguments);
|
||||
void analyze(MemberEntity member, T node, ArgumentsTypes arguments);
|
||||
void analyzeListAndEnqueue(ListTypeInformation info);
|
||||
void analyzeMapAndEnqueue(MapTypeInformation info);
|
||||
|
||||
|
@ -136,11 +133,11 @@ abstract class InferrerEngine {
|
|||
|
||||
/// Registers a call to await with an expression of type [argumentType] as
|
||||
/// argument.
|
||||
TypeInformation registerAwait(ast.Node node, TypeInformation argument);
|
||||
TypeInformation registerAwait(T node, TypeInformation argument);
|
||||
|
||||
/// Registers a call to yield with an expression of type [argumentType] as
|
||||
/// argument.
|
||||
TypeInformation registerYield(ast.Node node, TypeInformation argument);
|
||||
TypeInformation registerYield(T node, TypeInformation argument);
|
||||
|
||||
/// Registers that [caller] calls [closure] with [arguments].
|
||||
///
|
||||
|
@ -149,7 +146,7 @@ abstract class InferrerEngine {
|
|||
///
|
||||
/// [inLoop] tells whether the call happens in a loop.
|
||||
TypeInformation registerCalledClosure(
|
||||
ast.Node node,
|
||||
T node,
|
||||
Selector selector,
|
||||
TypeMask mask,
|
||||
TypeInformation closure,
|
||||
|
@ -184,7 +181,7 @@ abstract class InferrerEngine {
|
|||
/// [inLoop] tells whether the call happens in a loop.
|
||||
TypeInformation registerCalledSelector(
|
||||
CallType callType,
|
||||
ast.Node node,
|
||||
T node,
|
||||
Selector selector,
|
||||
TypeMask mask,
|
||||
TypeInformation receiverType,
|
||||
|
@ -201,8 +198,8 @@ abstract class InferrerEngine {
|
|||
ArgumentsTypes arguments, Selector selector, TypeMask mask,
|
||||
{bool remove, bool addToQueue: true});
|
||||
|
||||
void updateSelectorInMember(MemberEntity owner, CallType callType,
|
||||
ast.Node node, Selector selector, TypeMask mask);
|
||||
void updateSelectorInMember(MemberEntity owner, CallType callType, T node,
|
||||
Selector selector, TypeMask mask);
|
||||
|
||||
/// Returns the return type of [element].
|
||||
TypeInformation returnTypeOfMember(MemberEntity element);
|
||||
|
@ -228,7 +225,7 @@ abstract class InferrerEngine {
|
|||
void clear();
|
||||
}
|
||||
|
||||
class InferrerEngineImpl extends InferrerEngine {
|
||||
abstract class InferrerEngineImpl<T> extends InferrerEngine<T> {
|
||||
final Map<Local, TypeInformation> defaultTypeOfParameter =
|
||||
new Map<Local, TypeInformation>();
|
||||
final WorkQueue workQueue = new WorkQueue();
|
||||
|
@ -249,9 +246,8 @@ class InferrerEngineImpl extends InferrerEngine {
|
|||
final ClosedWorld closedWorld;
|
||||
|
||||
final ClosedWorldRefiner closedWorldRefiner;
|
||||
final TypeSystem<ast.Node> types;
|
||||
final Map<ast.Node, TypeInformation> concreteTypes =
|
||||
new Map<ast.Node, TypeInformation>();
|
||||
final TypeSystem<T> types;
|
||||
final Map<T, TypeInformation> concreteTypes = new Map<T, TypeInformation>();
|
||||
|
||||
final Map<ir.Node, TypeInformation> concreteKernelTypes =
|
||||
new Map<ir.Node, TypeInformation>();
|
||||
|
@ -263,10 +259,13 @@ class InferrerEngineImpl extends InferrerEngine {
|
|||
final Map<MemberEntity, GlobalTypeInferenceElementData> _memberData =
|
||||
new Map<MemberEntity, GlobalTypeInferenceElementData>();
|
||||
|
||||
InferrerEngineImpl(this.compiler, ClosedWorld closedWorld,
|
||||
this.closedWorldRefiner, this.mainElement)
|
||||
: this.types = new TypeSystem<ast.Node>(
|
||||
closedWorld, const TypeSystemStrategyImpl()),
|
||||
InferrerEngineImpl(
|
||||
this.compiler,
|
||||
ClosedWorld closedWorld,
|
||||
this.closedWorldRefiner,
|
||||
this.mainElement,
|
||||
TypeSystemStrategy<T> typeSystemStrategy)
|
||||
: this.types = new TypeSystem<T>(closedWorld, typeSystemStrategy),
|
||||
this.closedWorld = closedWorld;
|
||||
|
||||
void forEachElementMatching(
|
||||
|
@ -277,12 +276,13 @@ class InferrerEngineImpl extends InferrerEngine {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO(johnniwinther): Make this private again.
|
||||
GlobalTypeInferenceElementData dataOfMember(MemberEntity element) =>
|
||||
_memberData.putIfAbsent(
|
||||
element, () => new GlobalTypeInferenceElementData());
|
||||
GlobalTypeInferenceElementData<T> createElementData();
|
||||
|
||||
GlobalTypeInferenceElementData lookupDataOfMember(MemberEntity element) =>
|
||||
// TODO(johnniwinther): Make this private again.
|
||||
GlobalTypeInferenceElementData<T> dataOfMember(MemberEntity element) =>
|
||||
_memberData.putIfAbsent(element, createElementData);
|
||||
|
||||
GlobalTypeInferenceElementData<T> lookupDataOfMember(MemberEntity element) =>
|
||||
_memberData[element];
|
||||
|
||||
/**
|
||||
|
@ -358,8 +358,8 @@ class InferrerEngineImpl extends InferrerEngine {
|
|||
return returnType;
|
||||
}
|
||||
|
||||
void updateSelectorInMember(MemberEntity owner, CallType callType,
|
||||
ast.Node node, Selector selector, TypeMask mask) {
|
||||
void updateSelectorInMember(MemberEntity owner, CallType callType, T node,
|
||||
Selector selector, TypeMask mask) {
|
||||
GlobalTypeInferenceElementData data = dataOfMember(owner);
|
||||
assert(validCallType(callType, node));
|
||||
switch (callType) {
|
||||
|
@ -456,280 +456,9 @@ class InferrerEngineImpl extends InferrerEngine {
|
|||
workQueue.add(info);
|
||||
}
|
||||
|
||||
void runOverAllElements() {
|
||||
if (compiler.disableTypeInference) return;
|
||||
if (compiler.options.verbose) {
|
||||
compiler.progress.reset();
|
||||
}
|
||||
sortResolvedAsts().forEach((ResolvedAst resolvedAst) {
|
||||
if (compiler.shouldPrintProgress) {
|
||||
reporter.log('Added $addedInGraph elements in inferencing graph.');
|
||||
compiler.progress.reset();
|
||||
}
|
||||
// This also forces the creation of the [ElementTypeInformation] to ensure
|
||||
// it is in the graph.
|
||||
MemberElement member = resolvedAst.element;
|
||||
types.withMember(member, () => analyze(resolvedAst, null));
|
||||
});
|
||||
reporter.log('Added $addedInGraph elements in inferencing graph.');
|
||||
void runOverAllElements();
|
||||
|
||||
TypeGraphDump dump = debug.PRINT_GRAPH ? new TypeGraphDump(this) : null;
|
||||
|
||||
dump?.beforeAnalysis();
|
||||
buildWorkQueue();
|
||||
refine();
|
||||
|
||||
// Try to infer element types of lists and compute their escape information.
|
||||
types.allocatedLists.values.forEach((TypeInformation info) {
|
||||
analyzeListAndEnqueue(info);
|
||||
});
|
||||
|
||||
// Try to infer the key and value types for maps and compute the values'
|
||||
// escape information.
|
||||
types.allocatedMaps.values.forEach((TypeInformation info) {
|
||||
analyzeMapAndEnqueue(info);
|
||||
});
|
||||
|
||||
Set<FunctionEntity> bailedOutOn = new Set<FunctionEntity>();
|
||||
|
||||
// Trace closures to potentially infer argument types.
|
||||
types.allocatedClosures.forEach((dynamic info) {
|
||||
void trace(
|
||||
Iterable<FunctionEntity> elements, ClosureTracerVisitor tracer) {
|
||||
tracer.run();
|
||||
if (!tracer.continueAnalyzing) {
|
||||
elements.forEach((FunctionEntity _element) {
|
||||
MethodElement element = _element;
|
||||
MethodElement implementation = element.implementation;
|
||||
closedWorldRefiner.registerMightBePassedToApply(element);
|
||||
if (debug.VERBOSE) {
|
||||
print("traced closure $element as ${true} (bail)");
|
||||
}
|
||||
implementation.functionSignature
|
||||
.forEachParameter((FormalElement _parameter) {
|
||||
ParameterElement parameter = _parameter;
|
||||
types
|
||||
.getInferredTypeOfParameter(parameter)
|
||||
.giveUp(this, clearAssignments: false);
|
||||
});
|
||||
});
|
||||
bailedOutOn.addAll(elements);
|
||||
return;
|
||||
}
|
||||
elements
|
||||
.where((e) => !bailedOutOn.contains(e))
|
||||
.forEach((FunctionEntity _element) {
|
||||
MethodElement element = _element;
|
||||
MethodElement implementation = element.implementation;
|
||||
implementation.functionSignature
|
||||
.forEachParameter((FormalElement _parameter) {
|
||||
ParameterElement parameter = _parameter;
|
||||
ParameterTypeInformation info =
|
||||
types.getInferredTypeOfParameter(parameter);
|
||||
info.maybeResume();
|
||||
workQueue.add(info);
|
||||
});
|
||||
if (tracer.tracedType.mightBePassedToFunctionApply) {
|
||||
closedWorldRefiner.registerMightBePassedToApply(element);
|
||||
}
|
||||
if (debug.VERBOSE) {
|
||||
print("traced closure $element as "
|
||||
"${closedWorldRefiner
|
||||
.getCurrentlyKnownMightBePassedToApply(element)}");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (info is ClosureTypeInformation) {
|
||||
Iterable<FunctionEntity> elements = [info.closure];
|
||||
trace(elements, new ClosureTracerVisitor(elements, info, this));
|
||||
} else if (info is CallSiteTypeInformation) {
|
||||
if (info is StaticCallSiteTypeInformation &&
|
||||
info.selector != null &&
|
||||
info.selector.isCall) {
|
||||
// This is a constructor call to a class with a call method. So we
|
||||
// need to trace the call method here.
|
||||
MethodElement calledElement = info.calledElement;
|
||||
assert(calledElement.isGenerativeConstructor);
|
||||
ClassElement cls = calledElement.enclosingClass;
|
||||
MethodElement callMethod = cls.lookupMember(Identifiers.call);
|
||||
if (callMethod == null) {
|
||||
callMethod = cls.lookupMember(Identifiers.noSuchMethod_);
|
||||
}
|
||||
assert(callMethod != null, failedAt(cls));
|
||||
Iterable<FunctionEntity> elements = [callMethod];
|
||||
trace(elements, new ClosureTracerVisitor(elements, info, this));
|
||||
} else {
|
||||
// We only are interested in functions here, as other targets
|
||||
// of this closure call are not a root to trace but an intermediate
|
||||
// for some other function.
|
||||
Iterable<FunctionEntity> elements = new List<FunctionEntity>.from(
|
||||
info.callees.where((e) => e.isFunction));
|
||||
trace(elements, new ClosureTracerVisitor(elements, info, this));
|
||||
}
|
||||
} else if (info is MemberTypeInformation) {
|
||||
trace(<FunctionEntity>[info.member],
|
||||
new StaticTearOffClosureTracerVisitor(info.member, info, this));
|
||||
} else if (info is ParameterTypeInformation) {
|
||||
failedAt(
|
||||
NO_LOCATION_SPANNABLE, 'Unexpected closure allocation info $info');
|
||||
}
|
||||
});
|
||||
|
||||
dump?.beforeTracing();
|
||||
|
||||
// Reset all nodes that use lists/maps that have been inferred, as well
|
||||
// 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();
|
||||
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(seenTypes);
|
||||
refine();
|
||||
|
||||
if (debug.PRINT_SUMMARY) {
|
||||
types.allocatedLists.values.forEach((_info) {
|
||||
ListTypeInformation info = _info;
|
||||
print('${info.type} '
|
||||
'for ${info.originalType.allocationNode} '
|
||||
'at ${info.originalType.allocationElement} '
|
||||
'after ${info.refineCount}');
|
||||
});
|
||||
types.allocatedMaps.values.forEach((_info) {
|
||||
MapTypeInformation info = _info;
|
||||
print('${info.type} '
|
||||
'for ${info.originalType.allocationNode} '
|
||||
'at ${info.originalType.allocationElement} '
|
||||
'after ${info.refineCount}');
|
||||
});
|
||||
types.allocatedClosures.forEach((TypeInformation info) {
|
||||
if (info is ElementTypeInformation) {
|
||||
print('${info.getInferredSignature(types)} for '
|
||||
'${info.debugName}');
|
||||
} else if (info is ClosureTypeInformation) {
|
||||
print('${info.getInferredSignature(types)} for '
|
||||
'${info.debugName}');
|
||||
} else if (info is DynamicCallSiteTypeInformation) {
|
||||
for (MemberEntity target in info.targets) {
|
||||
if (target is FunctionEntity) {
|
||||
print(
|
||||
'${types.getInferredSignatureOfMethod(target)} for ${target}');
|
||||
} else {
|
||||
print(
|
||||
'${types.getInferredTypeOfMember(target).type} for ${target}');
|
||||
}
|
||||
}
|
||||
} else if (info is StaticCallSiteTypeInformation) {
|
||||
ClassElement cls = info.calledElement.enclosingClass;
|
||||
MethodElement callMethod = cls.lookupMember(Identifiers.call);
|
||||
print('${types.getInferredSignatureOfMethod(callMethod)} for ${cls}');
|
||||
} else {
|
||||
print('${info.type} for some unknown kind of closure');
|
||||
}
|
||||
});
|
||||
analyzedElements.forEach((MemberEntity elem) {
|
||||
TypeInformation type = types.getInferredTypeOfMember(elem);
|
||||
print('${elem} :: ${type} from ${type.assignments} ');
|
||||
});
|
||||
}
|
||||
dump?.afterAnalysis();
|
||||
|
||||
reporter.log('Inferred $overallRefineCount types.');
|
||||
|
||||
processLoopInformation();
|
||||
}
|
||||
|
||||
void analyze(ResolvedAst resolvedAst, ArgumentsTypes arguments) {
|
||||
MemberElement element = resolvedAst.element;
|
||||
if (analyzedElements.contains(element)) return;
|
||||
analyzedElements.add(element);
|
||||
|
||||
dynamic visitor = compiler.options.kernelGlobalInference
|
||||
? new KernelTypeGraphBuilder(element, resolvedAst, compiler, this)
|
||||
: new ElementGraphBuilder(element, resolvedAst, compiler, this);
|
||||
TypeInformation type;
|
||||
reporter.withCurrentElement(element, () {
|
||||
// ignore: UNDEFINED_METHOD
|
||||
type = visitor.run();
|
||||
});
|
||||
addedInGraph++;
|
||||
|
||||
if (element.isField) {
|
||||
FieldElement field = element;
|
||||
ast.Node initializer = resolvedAst.body;
|
||||
if (field.isFinal || field.isConst) {
|
||||
// If [element] is final and has an initializer, we record
|
||||
// the inferred type.
|
||||
if (resolvedAst.body != null) {
|
||||
if (type is! ListTypeInformation && type is! MapTypeInformation) {
|
||||
// For non-container types, the constant handler does
|
||||
// constant folding that could give more precise results.
|
||||
ConstantExpression constant = field.constant;
|
||||
if (constant != null) {
|
||||
ConstantValue value =
|
||||
compiler.backend.constants.getConstantValue(constant);
|
||||
if (value != null) {
|
||||
if (value.isFunction) {
|
||||
FunctionConstantValue functionConstant = value;
|
||||
MethodElement function = functionConstant.element;
|
||||
type = types.allocateClosure(function);
|
||||
} else {
|
||||
// Although we might find a better type, we have to keep
|
||||
// the old type around to ensure that we get a complete view
|
||||
// of the type graph and do not drop any flow edges.
|
||||
TypeMask refinedType = computeTypeMask(closedWorld, value);
|
||||
assert(TypeMask.assertIsNormalized(refinedType, closedWorld));
|
||||
type = new NarrowTypeInformation(type, refinedType);
|
||||
types.allocatedTypes.add(type);
|
||||
}
|
||||
} else {
|
||||
assert(
|
||||
field.isInstanceMember ||
|
||||
constant.isImplicit ||
|
||||
constant.isPotential,
|
||||
failedAt(
|
||||
field,
|
||||
"Constant expression without value: "
|
||||
"${constant.toStructuredText()}."));
|
||||
}
|
||||
}
|
||||
}
|
||||
recordTypeOfField(field, type);
|
||||
} else if (!element.isInstanceMember) {
|
||||
recordTypeOfField(field, types.nullType);
|
||||
}
|
||||
} else if (initializer == null) {
|
||||
// Only update types of static fields if there is no
|
||||
// assignment. Instance fields are dealt with in the constructor.
|
||||
if (Elements.isStaticOrTopLevelField(element)) {
|
||||
recordTypeOfField(field, type);
|
||||
}
|
||||
} else {
|
||||
recordTypeOfField(field, type);
|
||||
}
|
||||
if (Elements.isStaticOrTopLevelField(field) &&
|
||||
resolvedAst.body != null &&
|
||||
!element.isConst) {
|
||||
dynamic argument = resolvedAst.body;
|
||||
// TODO(13429): We could do better here by using the
|
||||
// constant handler to figure out if it's a lazy field or not.
|
||||
if (argument.asSend() != null ||
|
||||
(argument.asNewExpression() != null && !argument.isConst)) {
|
||||
recordTypeOfField(field, types.nullType);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
MethodElement method = element;
|
||||
recordReturnType(method, type);
|
||||
}
|
||||
}
|
||||
void analyze(MemberEntity element, T body, ArgumentsTypes arguments);
|
||||
|
||||
void processLoopInformation() {
|
||||
types.allocatedCalls.forEach((dynamic info) {
|
||||
|
@ -789,77 +518,7 @@ class InferrerEngineImpl extends InferrerEngine {
|
|||
|
||||
void updateParameterAssignments(TypeInformation caller, MemberEntity callee,
|
||||
ArgumentsTypes arguments, Selector selector, TypeMask mask,
|
||||
{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]);
|
||||
} else {
|
||||
info.addAssignment(arguments.positional[0]);
|
||||
}
|
||||
if (addToQueue) workQueue.add(info);
|
||||
}
|
||||
} else if (callee.isGetter) {
|
||||
return;
|
||||
} else if (selector != null && selector.isGetter) {
|
||||
// We are tearing a function off and thus create a closure.
|
||||
assert(callee.isFunction);
|
||||
MethodElement method = callee;
|
||||
MemberTypeInformation info = types.getInferredTypeOfMember(method);
|
||||
if (remove) {
|
||||
info.closurizedCount--;
|
||||
} else {
|
||||
info.closurizedCount++;
|
||||
if (Elements.isStaticOrTopLevel(method)) {
|
||||
types.allocatedClosures.add(info);
|
||||
} else {
|
||||
// We add the call-site type information here so that we
|
||||
// can benefit from further refinement of the selector.
|
||||
types.allocatedClosures.add(caller);
|
||||
}
|
||||
FunctionElement function = method.implementation;
|
||||
FunctionSignature signature = function.functionSignature;
|
||||
signature.forEachParameter((FormalElement _parameter) {
|
||||
ParameterElement parameter = _parameter;
|
||||
ParameterTypeInformation info =
|
||||
types.getInferredTypeOfParameter(parameter);
|
||||
info.tagAsTearOffClosureParameter(this);
|
||||
if (addToQueue) workQueue.add(info);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
MethodElement method = callee;
|
||||
FunctionElement function = method.implementation;
|
||||
FunctionSignature signature = function.functionSignature;
|
||||
int parameterIndex = 0;
|
||||
bool visitingRequiredParameter = true;
|
||||
signature.forEachParameter((FormalElement _parameter) {
|
||||
ParameterElement parameter = _parameter;
|
||||
if (signature.hasOptionalParameters &&
|
||||
parameter == signature.optionalParameters.first) {
|
||||
visitingRequiredParameter = false;
|
||||
}
|
||||
TypeInformation type = visitingRequiredParameter
|
||||
? arguments.positional[parameterIndex]
|
||||
: signature.optionalParametersAreNamed
|
||||
? arguments.named[parameter.name]
|
||||
: parameterIndex < arguments.positional.length
|
||||
? arguments.positional[parameterIndex]
|
||||
: null;
|
||||
if (type == null) type = getDefaultTypeOfParameter(parameter);
|
||||
TypeInformation info = types.getInferredTypeOfParameter(parameter);
|
||||
if (remove) {
|
||||
info.removeAssignment(type);
|
||||
} else {
|
||||
info.addAssignment(type);
|
||||
}
|
||||
parameterIndex++;
|
||||
if (addToQueue) workQueue.add(info);
|
||||
});
|
||||
}
|
||||
}
|
||||
{bool remove, bool addToQueue: true});
|
||||
|
||||
void setDefaultTypeOfParameter(Local parameter, TypeInformation type,
|
||||
{bool isInstanceMember}) {
|
||||
|
@ -982,7 +641,7 @@ class InferrerEngineImpl extends InferrerEngine {
|
|||
|
||||
TypeInformation registerCalledSelector(
|
||||
CallType callType,
|
||||
ast.Node node,
|
||||
T node,
|
||||
Selector selector,
|
||||
TypeMask mask,
|
||||
TypeInformation receiverType,
|
||||
|
@ -1017,24 +676,24 @@ class InferrerEngineImpl extends InferrerEngine {
|
|||
return info;
|
||||
}
|
||||
|
||||
TypeInformation registerAwait(ast.Node node, TypeInformation argument) {
|
||||
TypeInformation registerAwait(T node, TypeInformation argument) {
|
||||
AwaitTypeInformation info =
|
||||
new AwaitTypeInformation<ast.Node>(types.currentMember, node);
|
||||
new AwaitTypeInformation<T>(types.currentMember, node);
|
||||
info.addAssignment(argument);
|
||||
types.allocatedTypes.add(info);
|
||||
return info;
|
||||
}
|
||||
|
||||
TypeInformation registerYield(ast.Node node, TypeInformation argument) {
|
||||
TypeInformation registerYield(T node, TypeInformation argument) {
|
||||
YieldTypeInformation info =
|
||||
new YieldTypeInformation<ast.Node>(types.currentMember, node);
|
||||
new YieldTypeInformation<T>(types.currentMember, node);
|
||||
info.addAssignment(argument);
|
||||
types.allocatedTypes.add(info);
|
||||
return info;
|
||||
}
|
||||
|
||||
TypeInformation registerCalledClosure(
|
||||
ast.Node node,
|
||||
T node,
|
||||
Selector selector,
|
||||
TypeMask mask,
|
||||
TypeInformation closure,
|
||||
|
@ -1058,46 +717,6 @@ class InferrerEngineImpl extends InferrerEngine {
|
|||
return info;
|
||||
}
|
||||
|
||||
// Sorts the resolved elements by size. We do this for this inferrer
|
||||
// to get the same results for [ListTracer] compared to the
|
||||
// [SimpleTypesInferrer].
|
||||
Iterable<ResolvedAst> sortResolvedAsts() {
|
||||
int max = 0;
|
||||
Map<int, Setlet<ResolvedAst>> methodSizes = <int, Setlet<ResolvedAst>>{};
|
||||
compiler.enqueuer.resolution.processedEntities.forEach((_element) {
|
||||
MemberElement element = _element;
|
||||
ResolvedAst resolvedAst = element.resolvedAst;
|
||||
element = element.implementation;
|
||||
if (element.impliesType) return;
|
||||
assert(
|
||||
element.isField ||
|
||||
element.isFunction ||
|
||||
element.isConstructor ||
|
||||
element.isGetter ||
|
||||
element.isSetter,
|
||||
failedAt(element, 'Unexpected element kind: ${element.kind}'));
|
||||
if (element.isAbstract) return;
|
||||
// Put the other operators in buckets by length, later to be added in
|
||||
// length order.
|
||||
int length = 0;
|
||||
if (resolvedAst.kind == ResolvedAstKind.PARSED) {
|
||||
TreeElementMapping mapping = resolvedAst.elements;
|
||||
length = mapping.getSelectorCount();
|
||||
}
|
||||
max = length > max ? length : max;
|
||||
Setlet<ResolvedAst> set =
|
||||
methodSizes.putIfAbsent(length, () => new Setlet<ResolvedAst>());
|
||||
set.add(resolvedAst);
|
||||
});
|
||||
|
||||
List<ResolvedAst> result = <ResolvedAst>[];
|
||||
for (int i = 0; i <= max; i++) {
|
||||
Setlet<ResolvedAst> set = methodSizes[i];
|
||||
if (set != null) result.addAll(set);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
void cleanup(TypeInformation info) => info.cleanup();
|
||||
|
||||
|
@ -1162,104 +781,3 @@ class InferrerEngineImpl extends InferrerEngine {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TypeSystemStrategyImpl implements TypeSystemStrategy<ast.Node> {
|
||||
const TypeSystemStrategyImpl();
|
||||
|
||||
@override
|
||||
MemberTypeInformation createMemberTypeInformation(
|
||||
covariant MemberElement member) {
|
||||
assert(member.isDeclaration, failedAt(member));
|
||||
if (member.isField) {
|
||||
FieldElement field = member;
|
||||
return new FieldTypeInformation(field, field.type);
|
||||
} else if (member.isGetter) {
|
||||
GetterElement getter = member;
|
||||
return new GetterTypeInformation(getter, getter.type);
|
||||
} else if (member.isSetter) {
|
||||
SetterElement setter = member;
|
||||
return new SetterTypeInformation(setter);
|
||||
} else if (member.isFunction) {
|
||||
MethodElement method = member;
|
||||
return new MethodTypeInformation(method, method.type);
|
||||
} else {
|
||||
ConstructorElement constructor = member;
|
||||
if (constructor.isFactoryConstructor) {
|
||||
return new FactoryConstructorTypeInformation(
|
||||
constructor, constructor.type);
|
||||
} else {
|
||||
return new GenerativeConstructorTypeInformation(constructor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
ParameterTypeInformation createParameterTypeInformation(
|
||||
covariant ParameterElement parameter, TypeSystem<ast.Node> types) {
|
||||
assert(parameter.isImplementation, failedAt(parameter));
|
||||
FunctionTypedElement function = parameter.functionDeclaration.declaration;
|
||||
if (function.isLocal) {
|
||||
LocalFunctionElement localFunction = function;
|
||||
MethodElement callMethod = localFunction.callMethod;
|
||||
return new ParameterTypeInformation.localFunction(
|
||||
types.getInferredTypeOfMember(callMethod),
|
||||
parameter,
|
||||
parameter.type,
|
||||
callMethod);
|
||||
} else if (function.isInstanceMember) {
|
||||
MethodElement method = function;
|
||||
return new ParameterTypeInformation.instanceMember(
|
||||
types.getInferredTypeOfMember(method),
|
||||
parameter,
|
||||
parameter.type,
|
||||
method,
|
||||
new ParameterAssignments());
|
||||
} else {
|
||||
MethodElement method = function;
|
||||
return new ParameterTypeInformation.static(
|
||||
types.getInferredTypeOfMember(method),
|
||||
parameter,
|
||||
parameter.type,
|
||||
method,
|
||||
// TODO(johnniwinther): Is this still valid now that initializing
|
||||
// formals also introduce locals?
|
||||
isInitializingFormal: parameter.isInitializingFormal);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void forEachParameter(
|
||||
covariant MethodElement function, void f(Local parameter)) {
|
||||
MethodElement impl = function.implementation;
|
||||
FunctionSignature signature = impl.functionSignature;
|
||||
signature.forEachParameter((FormalElement _parameter) {
|
||||
ParameterElement parameter = _parameter;
|
||||
f(parameter);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
bool checkMapNode(ast.Node node) {
|
||||
return node is ast.LiteralMap;
|
||||
}
|
||||
|
||||
@override
|
||||
bool checkListNode(ast.Node node) {
|
||||
return node is ast.LiteralList || node is ast.Send;
|
||||
}
|
||||
|
||||
@override
|
||||
bool checkLoopPhiNode(ast.Node node) {
|
||||
return node is ast.Loop || node is ast.SwitchStatement;
|
||||
}
|
||||
|
||||
@override
|
||||
bool checkPhiNode(ast.Node node) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
bool checkClassEntity(covariant ClassElement cls) {
|
||||
return cls.isDeclaration;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import '../types/masks.dart'
|
|||
import '../types/types.dart' show TypesInferrer;
|
||||
import '../universe/selector.dart' show Selector;
|
||||
import '../world.dart' show ClosedWorld, ClosedWorldRefiner;
|
||||
import 'ast_inferrer_engine.dart';
|
||||
import 'inferrer_engine.dart';
|
||||
import 'type_graph_nodes.dart';
|
||||
|
||||
|
@ -65,7 +66,7 @@ class TypeGraphInferrer implements TypesInferrer {
|
|||
|
||||
void analyzeMain(FunctionEntity main) {
|
||||
inferrer =
|
||||
new InferrerEngineImpl(compiler, closedWorld, closedWorldRefiner, main);
|
||||
new AstInferrerEngine(compiler, closedWorld, closedWorldRefiner, main);
|
||||
inferrer.runOverAllElements();
|
||||
}
|
||||
|
||||
|
|
|
@ -831,7 +831,7 @@ enum CallType {
|
|||
forIn,
|
||||
}
|
||||
|
||||
bool validCallType(CallType callType, Spannable call) {
|
||||
bool validCallType(CallType callType, Object call) {
|
||||
switch (callType) {
|
||||
case CallType.complex:
|
||||
return call is ast.SendSet;
|
||||
|
@ -855,7 +855,7 @@ bool validCallType(CallType callType, Spannable call) {
|
|||
*/
|
||||
abstract class CallSiteTypeInformation extends TypeInformation
|
||||
with ApplyableTypeInformation {
|
||||
final Spannable _call;
|
||||
final Object _call;
|
||||
final MemberEntity caller;
|
||||
final Selector selector;
|
||||
final TypeMask mask;
|
||||
|
@ -966,7 +966,7 @@ class StaticCallSiteTypeInformation extends CallSiteTypeInformation {
|
|||
}
|
||||
}
|
||||
|
||||
class DynamicCallSiteTypeInformation extends CallSiteTypeInformation {
|
||||
class DynamicCallSiteTypeInformation<T> extends CallSiteTypeInformation {
|
||||
final CallType _callType;
|
||||
final TypeInformation receiver;
|
||||
final bool isConditional;
|
||||
|
@ -977,7 +977,7 @@ class DynamicCallSiteTypeInformation extends CallSiteTypeInformation {
|
|||
DynamicCallSiteTypeInformation(
|
||||
MemberTypeInformation context,
|
||||
this._callType,
|
||||
ast.Node call,
|
||||
T call,
|
||||
MemberEntity enclosing,
|
||||
Selector selector,
|
||||
TypeMask mask,
|
||||
|
@ -1296,7 +1296,7 @@ class ClosureCallSiteTypeInformation extends CallSiteTypeInformation {
|
|||
|
||||
ClosureCallSiteTypeInformation(
|
||||
MemberTypeInformation context,
|
||||
Spannable call,
|
||||
Object call,
|
||||
MemberEntity enclosing,
|
||||
Selector selector,
|
||||
TypeMask mask,
|
||||
|
|
|
@ -145,7 +145,32 @@ class GlobalTypeInferenceParameterResult
|
|||
|
||||
/// Internal data used during type-inference to store intermediate results about
|
||||
/// a single element.
|
||||
class GlobalTypeInferenceElementData {
|
||||
abstract class GlobalTypeInferenceElementData<T> {
|
||||
TypeMask typeOfSend(T node);
|
||||
TypeMask typeOfGetter(T node);
|
||||
TypeMask typeOfOperator(T node);
|
||||
|
||||
void setTypeMask(T node, TypeMask mask);
|
||||
|
||||
void setGetterTypeMaskInComplexSendSet(T node, TypeMask mask);
|
||||
|
||||
void setOperatorTypeMaskInComplexSendSet(T node, TypeMask mask);
|
||||
|
||||
TypeMask typeOfIterator(T node);
|
||||
|
||||
TypeMask typeOfIteratorMoveNext(T node);
|
||||
|
||||
TypeMask typeOfIteratorCurrent(T node);
|
||||
|
||||
void setIteratorTypeMask(T node, TypeMask mask);
|
||||
|
||||
void setMoveNextTypeMask(T node, TypeMask mask);
|
||||
|
||||
void setCurrentTypeMask(T node, TypeMask mask);
|
||||
}
|
||||
|
||||
class AstGlobalTypeInferenceElementData
|
||||
extends GlobalTypeInferenceElementData<Node> {
|
||||
Map<Object, TypeMask> _typeMasks;
|
||||
|
||||
TypeMask _get(Object node) => _typeMasks != null ? _typeMasks[node] : null;
|
||||
|
@ -154,19 +179,22 @@ class GlobalTypeInferenceElementData {
|
|||
_typeMasks[node] = mask;
|
||||
}
|
||||
|
||||
TypeMask typeOfSend(Send node) => _get(node);
|
||||
TypeMask typeOfGetter(SendSet node) => _get(node.selector);
|
||||
TypeMask typeOfOperator(SendSet node) => _get(node.assignmentOperator);
|
||||
TypeMask typeOfSend(covariant Send node) => _get(node);
|
||||
TypeMask typeOfGetter(covariant SendSet node) => _get(node.selector);
|
||||
TypeMask typeOfOperator(covariant SendSet node) =>
|
||||
_get(node.assignmentOperator);
|
||||
|
||||
void setTypeMask(Send node, TypeMask mask) {
|
||||
void setTypeMask(covariant Send node, TypeMask mask) {
|
||||
_set(node, mask);
|
||||
}
|
||||
|
||||
void setGetterTypeMaskInComplexSendSet(SendSet node, TypeMask mask) {
|
||||
void setGetterTypeMaskInComplexSendSet(
|
||||
covariant SendSet node, TypeMask mask) {
|
||||
_set(node.selector, mask);
|
||||
}
|
||||
|
||||
void setOperatorTypeMaskInComplexSendSet(SendSet node, TypeMask mask) {
|
||||
void setOperatorTypeMaskInComplexSendSet(
|
||||
covariant SendSet node, TypeMask mask) {
|
||||
_set(node.assignmentOperator, mask);
|
||||
}
|
||||
|
||||
|
@ -176,21 +204,21 @@ class GlobalTypeInferenceElementData {
|
|||
// separate. The current implementation does this by using
|
||||
// children of the for-in node (these children were picked arbitrarily).
|
||||
|
||||
TypeMask typeOfIterator(ForIn node) => _get(node);
|
||||
TypeMask typeOfIterator(covariant ForIn node) => _get(node);
|
||||
|
||||
TypeMask typeOfIteratorMoveNext(ForIn node) => _get(node.forToken);
|
||||
TypeMask typeOfIteratorMoveNext(covariant ForIn node) => _get(node.forToken);
|
||||
|
||||
TypeMask typeOfIteratorCurrent(ForIn node) => _get(node.inToken);
|
||||
TypeMask typeOfIteratorCurrent(covariant ForIn node) => _get(node.inToken);
|
||||
|
||||
void setIteratorTypeMask(ForIn node, TypeMask mask) {
|
||||
void setIteratorTypeMask(covariant ForIn node, TypeMask mask) {
|
||||
_set(node, mask);
|
||||
}
|
||||
|
||||
void setMoveNextTypeMask(ForIn node, TypeMask mask) {
|
||||
void setMoveNextTypeMask(covariant ForIn node, TypeMask mask) {
|
||||
_set(node.forToken, mask);
|
||||
}
|
||||
|
||||
void setCurrentTypeMask(ForIn node, TypeMask mask) {
|
||||
void setCurrentTypeMask(covariant ForIn node, TypeMask mask) {
|
||||
_set(node.inToken, mask);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue