| Index: sdk/lib/_internal/compiler/implementation/inferrer/container_tracer.dart
|
| ===================================================================
|
| --- sdk/lib/_internal/compiler/implementation/inferrer/container_tracer.dart (revision 31185)
|
| +++ sdk/lib/_internal/compiler/implementation/inferrer/container_tracer.dart (working copy)
|
| @@ -169,12 +169,17 @@
|
|
|
| bool _VERBOSE = false;
|
|
|
| -class ContainerTracerVisitor implements TypeInformationVisitor {
|
| - final ListTypeInformation container;
|
| +abstract class TracerVisitor implements TypeInformationVisitor {
|
| + final TypeInformation tracedType;
|
| final TypeGraphInferrerEngine inferrer;
|
| final Compiler compiler;
|
|
|
| + static const int MAX_ANALYSIS_COUNT = 16;
|
| + final Setlet<Element> analyzedElements = new Setlet<Element>();
|
|
|
| + TracerVisitor(this.tracedType, inferrer)
|
| + : this.inferrer = inferrer, this.compiler = inferrer.compiler;
|
| +
|
| // Work list that gets populated with [TypeInformation] that could
|
| // contain the container.
|
| final List<TypeInformation> workList = <TypeInformation>[];
|
| @@ -186,31 +191,22 @@
|
| final List<ListTypeInformation> containersToAnalyze =
|
| <ListTypeInformation>[];
|
|
|
| + final Setlet<TypeInformation> flowsInto = new Setlet<TypeInformation>();
|
| +
|
| // The current [TypeInformation] in the analysis.
|
| TypeInformation currentUser;
|
| -
|
| - // The list of found assignments to the container.
|
| - final List<TypeInformation> assignments = <TypeInformation>[];
|
| -
|
| - bool callsGrowableMethod = false;
|
| bool continueAnalyzing = true;
|
| -
|
| - static const int MAX_ANALYSIS_COUNT = 16;
|
| - final Setlet<Element> analyzedElements = new Setlet<Element>();
|
|
|
| - ContainerTracerVisitor(this.container, inferrer)
|
| - : this.inferrer = inferrer, this.compiler = inferrer.compiler;
|
| -
|
| void addNewEscapeInformation(TypeInformation info) {
|
| - if (container.flowsInto.contains(info)) return;
|
| - container.flowsInto.add(info);
|
| + if (flowsInto.contains(info)) return;
|
| + flowsInto.add(info);
|
| workList.add(info);
|
| }
|
|
|
| - List<TypeInformation> run() {
|
| + void analyze() {
|
| // Collect the [TypeInformation] where the container can flow in,
|
| // as well as the operations done on all these [TypeInformation]s.
|
| - addNewEscapeInformation(container);
|
| + addNewEscapeInformation(tracedType);
|
| while (!workList.isEmpty) {
|
| currentUser = workList.removeLast();
|
| currentUser.users.forEach((TypeInformation info) {
|
| @@ -226,35 +222,24 @@
|
| break;
|
| }
|
| }
|
| -
|
| - if (continueAnalyzing) {
|
| - if (!callsGrowableMethod && container.inferredLength == null) {
|
| - container.inferredLength = container.originalLength;
|
| - }
|
| - return assignments;
|
| - }
|
| - return null;
|
| }
|
|
|
| void bailout(String reason) {
|
| if (_VERBOSE) {
|
| - ContainerTypeMask mask = container.type;
|
| - print('Bailing out on ${mask.allocationNode} ${mask.allocationElement} '
|
| - 'because: $reason');
|
| + print('Bailing out on $tracedType because: $reason');
|
| }
|
| continueAnalyzing = false;
|
| - callsGrowableMethod = true;
|
| }
|
|
|
| - visitNarrowTypeInformation(NarrowTypeInformation info) {
|
| + void visitNarrowTypeInformation(NarrowTypeInformation info) {
|
| addNewEscapeInformation(info);
|
| }
|
|
|
| - visitPhiElementTypeInformation(PhiElementTypeInformation info) {
|
| + void visitPhiElementTypeInformation(PhiElementTypeInformation info) {
|
| addNewEscapeInformation(info);
|
| }
|
|
|
| - visitElementInContainerTypeInformation(
|
| + void visitElementInContainerTypeInformation(
|
| ElementInContainerTypeInformation info) {
|
| addNewEscapeInformation(info);
|
| }
|
| @@ -263,21 +248,15 @@
|
| containersToAnalyze.add(info);
|
| }
|
|
|
| - visitMapTypeInformation(MapTypeInformation info) {
|
| - bailout('Stored in a map');
|
| - }
|
| + void visitConcreteTypeInformation(ConcreteTypeInformation info) {}
|
|
|
| - visitConcreteTypeInformation(ConcreteTypeInformation info) {}
|
| + void visitClosureTypeInformation(ClosureTypeInformation info) {}
|
|
|
| - visitClosureCallSiteTypeInformation(ClosureCallSiteTypeInformation info) {
|
| - bailout('Passed to a closure');
|
| - }
|
| + void visitClosureCallSiteTypeInformation(
|
| + ClosureCallSiteTypeInformation info) {}
|
|
|
| visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) {
|
| Element called = info.calledElement;
|
| - if (called.isForeign(compiler) && called.name == 'JS') {
|
| - bailout('Used in JS ${info.call}');
|
| - }
|
| if (inferrer.types.getInferredTypeOf(called) == currentUser) {
|
| addNewEscapeInformation(info);
|
| }
|
| @@ -303,6 +282,7 @@
|
| }
|
|
|
| bool isAddedToContainer(DynamicCallSiteTypeInformation info) {
|
| + if (info.arguments == null) return false;
|
| var receiverType = info.receiver.type;
|
| if (!receiverType.isContainer) return false;
|
| String selectorName = info.selector.name;
|
| @@ -312,7 +292,111 @@
|
| || (selectorName == 'add' && currentUser == arguments[0]);
|
| }
|
|
|
| + void visitDynamicCallSiteTypeInformation(
|
| + DynamicCallSiteTypeInformation info) {
|
| + if (isAddedToContainer(info)) {
|
| + ContainerTypeMask mask = info.receiver.type;
|
| + if (mask.allocationNode != null) {
|
| + ListTypeInformation container =
|
| + inferrer.types.allocatedLists[mask.allocationNode];
|
| + containersToAnalyze.add(container);
|
| + } else {
|
| + // The [ContainerTypeMask] is a union of two containers, and
|
| + // we lose track of where these containers have been allocated
|
| + // at this point.
|
| + bailout('Stored in too many containers');
|
| + }
|
| + }
|
| +
|
| + Iterable<Element> inferredTargetTypes = info.targets.map((element) {
|
| + return inferrer.types.getInferredTypeOf(element);
|
| + });
|
| + if (inferredTargetTypes.any((user) => user == currentUser)) {
|
| + addNewEscapeInformation(info);
|
| + }
|
| + }
|
| +
|
| + bool isParameterOfListAddingMethod(Element element) {
|
| + if (!element.isParameter()) return false;
|
| + if (element.getEnclosingClass() != compiler.backend.listImplementation) {
|
| + return false;
|
| + }
|
| + Element method = element.enclosingElement;
|
| + return (method.name == '[]=')
|
| + || (method.name == 'add')
|
| + || (method.name == 'insert');
|
| + }
|
| +
|
| + bool isClosure(Element element) {
|
| + if (!element.isFunction()) return false;
|
| + Element outermost = element.getOutermostEnclosingMemberOrTopLevel();
|
| + return outermost.declaration != element.declaration;
|
| + }
|
| +
|
| + void visitElementTypeInformation(ElementTypeInformation info) {
|
| + Element element = info.element;
|
| + if (element.isParameter()
|
| + && inferrer.isNativeElement(element.enclosingElement)) {
|
| + bailout('Passed to a native method');
|
| + }
|
| + if (info.isClosurized()) {
|
| + bailout('Returned from a closurized method');
|
| + }
|
| + if (isClosure(info.element)) {
|
| + bailout('Returned from a closure');
|
| + }
|
| + if (compiler.backend.isNeededForReflection(info.element)) {
|
| + bailout('Escape in reflection');
|
| + }
|
| + if (isParameterOfListAddingMethod(info.element)) {
|
| + // These elements are being handled in
|
| + // [visitDynamicCallSiteTypeInformation].
|
| + return;
|
| + }
|
| + addNewEscapeInformation(info);
|
| + }
|
| +}
|
| +
|
| +class ContainerTracerVisitor extends TracerVisitor {
|
| + // The list of found assignments to the container.
|
| + final List<TypeInformation> assignments = <TypeInformation>[];
|
| + bool callsGrowableMethod = false;
|
| +
|
| + ContainerTracerVisitor(tracedType, inferrer) : super(tracedType, inferrer);
|
| +
|
| + List<TypeInformation> run() {
|
| + analyze();
|
| + ListTypeInformation container = tracedType;
|
| + if (continueAnalyzing) {
|
| + if (!callsGrowableMethod && container.inferredLength == null) {
|
| + container.inferredLength = container.originalLength;
|
| + }
|
| + container.flowsInto.addAll(flowsInto);
|
| + return assignments;
|
| + } else {
|
| + callsGrowableMethod = true;
|
| + return null;
|
| + }
|
| + }
|
| +
|
| + visitMapTypeInformation(MapTypeInformation info) {
|
| + bailout('Stored in a map');
|
| + }
|
| +
|
| + visitClosureCallSiteTypeInformation(ClosureCallSiteTypeInformation info) {
|
| + bailout('Passed to a closure');
|
| + }
|
| +
|
| + visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) {
|
| + super.visitStaticCallSiteTypeInformation(info);
|
| + Element called = info.calledElement;
|
| + if (called.isForeign(compiler) && called.name == 'JS') {
|
| + bailout('Used in JS ${info.call}');
|
| + }
|
| + }
|
| +
|
| visitDynamicCallSiteTypeInformation(DynamicCallSiteTypeInformation info) {
|
| + super.visitDynamicCallSiteTypeInformation(info);
|
| Selector selector = info.selector;
|
| String selectorName = selector.name;
|
| if (currentUser == info.receiver) {
|
| @@ -349,59 +433,75 @@
|
| && !info.targets.every((element) => element.isFunction())) {
|
| bailout('Passed to a closure');
|
| return;
|
| - } else if (isAddedToContainer(info)) {
|
| - ContainerTypeMask mask = info.receiver.type;
|
| - if (mask.allocationNode != null) {
|
| - ListTypeInformation container =
|
| - inferrer.types.allocatedLists[mask.allocationNode];
|
| - containersToAnalyze.add(container);
|
| + }
|
| + }
|
| +}
|
| +
|
| +class ClosureTracerVisitor extends TracerVisitor {
|
| + ClosureTracerVisitor(tracedType, inferrer) : super(tracedType, inferrer);
|
| +
|
| + void run() {
|
| + ClosureTypeInformation closure = tracedType;
|
| + FunctionElement element = closure.element;
|
| + element.functionSignature.forEachParameter((Element parameter) {
|
| + ElementTypeInformation info = inferrer.types.getInferredTypeOf(parameter);
|
| + info.abandonInferencing = false;
|
| + });
|
| + analyze();
|
| + element.functionSignature.forEachParameter((Element parameter) {
|
| + ElementTypeInformation info = inferrer.types.getInferredTypeOf(parameter);
|
| + if (continueAnalyzing) {
|
| + info.disableHandleSpecialCases = true;
|
| } else {
|
| - // The [ContainerTypeMask] is a union of two containers, and
|
| - // we lose track of where these containers have been allocated
|
| - // at this point.
|
| - bailout('Stored in too many containers');
|
| + info.giveUp(inferrer);
|
| }
|
| - }
|
| + });
|
| + }
|
|
|
| - if (info.targets
|
| - .map((element) => inferrer.types.getInferredTypeOf(element))
|
| - .any((other) => other == currentUser)) {
|
| - addNewEscapeInformation(info);
|
| - }
|
| + visitMapTypeInformation(MapTypeInformation info) {
|
| + bailout('Stored in a map');
|
| }
|
|
|
| - bool isClosure(Element element) {
|
| - if (!element.isFunction()) return false;
|
| - Element outermost = element.getOutermostEnclosingMemberOrTopLevel();
|
| - return outermost.declaration != element.declaration;
|
| + void analyzeCall(CallSiteTypeInformation info) {
|
| + ClosureTypeInformation closure = tracedType;
|
| + FunctionElement element = closure.element;
|
| + Selector selector = info.selector;
|
| + if (!selector.signatureApplies(element, compiler)) return;
|
| + inferrer.updateParameterAssignments(
|
| + info, element, info.arguments, selector, remove: false,
|
| + addToQueue: false);
|
| }
|
|
|
| - bool isParameterOfListAddingMethod(Element element) {
|
| - if (!element.isParameter()) return false;
|
| - if (element.getEnclosingClass() != compiler.backend.listImplementation) {
|
| - return false;
|
| + visitClosureCallSiteTypeInformation(ClosureCallSiteTypeInformation info) {
|
| + super.visitClosureCallSiteTypeInformation(info);
|
| + if (info.closure == currentUser) {
|
| + analyzeCall(info);
|
| + } else {
|
| + bailout('Passed to a closure');
|
| }
|
| - Element method = element.enclosingElement;
|
| - return (method.name == '[]=')
|
| - || (method.name == 'add')
|
| - || (method.name == 'insert');
|
| }
|
|
|
| - visitElementTypeInformation(ElementTypeInformation info) {
|
| - if (info.isClosurized()) {
|
| - bailout('Returned from a closurized method');
|
| + visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) {
|
| + super.visitStaticCallSiteTypeInformation(info);
|
| + Element called = info.calledElement;
|
| + if (called.isForeign(compiler) && called.name == 'JS') {
|
| + bailout('Used in JS ${info.call}');
|
| }
|
| - if (isClosure(info.element)) {
|
| - bailout('Returned from a closure');
|
| + }
|
| +
|
| + bool checkIfCurrentUser(element) {
|
| + return inferrer.types.getInferredTypeOf(element) == currentUser;
|
| + }
|
| +
|
| + visitDynamicCallSiteTypeInformation(DynamicCallSiteTypeInformation info) {
|
| + super.visitDynamicCallSiteTypeInformation(info);
|
| + if (info.selector.isCall()) {
|
| + if (info.arguments.contains(currentUser)
|
| + && !info.targets.every((element) => element.isFunction())) {
|
| + bailout('Passed to a closure');
|
| + } else if (info.targets.any((element) => checkIfCurrentUser(element))) {
|
| + analyzeCall(info);
|
| + }
|
| }
|
| - if (compiler.backend.isNeededForReflection(info.element)) {
|
| - bailout('Escape in reflection');
|
| - }
|
| - if (isParameterOfListAddingMethod(info.element)) {
|
| - // These elements are being handled in
|
| - // [visitDynamicCallSiteTypeInformation].
|
| - return;
|
| - }
|
| - addNewEscapeInformation(info);
|
| }
|
| }
|
|
|