Add LocalState to KernelTypeGraphBuilder
Move LocalsHandler.seenBreakOrContinue/seenReturnOrThrow/_tryBlock to LocalState Move KernelTypeGraphBuilder._locals and KernelTypeGraphBuilder._fieldScope from visitor to LocalState Change-Id: I712558ab3d18c6196fb5b96a4f8084d44848b83b Reviewed-on: https://dart-review.googlesource.com/c/85444 Reviewed-by: Sigmund Cherem <sigmund@google.com> Commit-Queue: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
committed by
commit-bot@chromium.org
parent
be5d0cca5c
commit
b52182ee14
@@ -48,13 +48,12 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
final GlobalTypeInferenceElementData _memberData;
|
||||
final bool _inGenerativeConstructor;
|
||||
|
||||
LocalsHandler _locals;
|
||||
FieldInitializationScope _fieldScope;
|
||||
LocalState _state;
|
||||
final SideEffectsBuilder _sideEffectsBuilder;
|
||||
final Map<JumpTarget, List<LocalsHandler>> _breaksFor =
|
||||
<JumpTarget, List<LocalsHandler>>{};
|
||||
final Map<JumpTarget, List<LocalsHandler>> _continuesFor =
|
||||
<JumpTarget, List<LocalsHandler>>{};
|
||||
final Map<JumpTarget, List<LocalState>> _breaksFor =
|
||||
<JumpTarget, List<LocalState>>{};
|
||||
final Map<JumpTarget, List<LocalState>> _continuesFor =
|
||||
<JumpTarget, List<LocalState>>{};
|
||||
TypeInformation _returnType;
|
||||
final Set<Local> _capturedVariables = new Set<Local>();
|
||||
final Map<Local, FieldEntity> _capturedAndBoxed;
|
||||
@@ -71,9 +70,7 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
|
||||
KernelTypeGraphBuilder(this._options, this._closedWorld, this._inferrer,
|
||||
this._analyzedMember, this._analyzedNode, this._localsMap,
|
||||
[this._locals,
|
||||
this._fieldScope,
|
||||
Map<Local, FieldEntity> capturedAndBoxed])
|
||||
[this._state, Map<Local, FieldEntity> capturedAndBoxed])
|
||||
: this._types = _inferrer.types,
|
||||
this._memberData = _inferrer.dataOfMember(_analyzedMember),
|
||||
// TODO(johnniwinther): Should side effects also be tracked for field
|
||||
@@ -86,11 +83,10 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
this._capturedAndBoxed = capturedAndBoxed != null
|
||||
? new Map<Local, FieldEntity>.from(capturedAndBoxed)
|
||||
: <Local, FieldEntity>{} {
|
||||
if (_locals != null) return;
|
||||
if (_state != null) return;
|
||||
|
||||
_fieldScope =
|
||||
_inGenerativeConstructor ? new FieldInitializationScope() : null;
|
||||
_locals = new LocalsHandler(_analyzedNode);
|
||||
_state = new LocalState.initial(_analyzedNode,
|
||||
inGenerativeConstructor: _inGenerativeConstructor);
|
||||
}
|
||||
|
||||
JsToElementMap get _elementMap => _closedWorld.elementMap;
|
||||
@@ -101,16 +97,6 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
|
||||
bool get inLoop => _loopLevel > 0;
|
||||
|
||||
bool get _isThisExposed {
|
||||
return _inGenerativeConstructor ? _fieldScope.isThisExposed : true;
|
||||
}
|
||||
|
||||
void _markThisAsExposed() {
|
||||
if (_inGenerativeConstructor) {
|
||||
_fieldScope.isThisExposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if [member] is defined in a subclass of the current this
|
||||
/// type.
|
||||
bool _isInClassOrSubclass(MemberEntity member) {
|
||||
@@ -129,14 +115,14 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
/// field is considered to have been read before initialization and the field
|
||||
/// is assumed to be potentially `null`.
|
||||
void _checkIfExposesThis(Selector selector, AbstractValue mask) {
|
||||
if (_isThisExposed) {
|
||||
if (_state.isThisExposed) {
|
||||
// We already consider `this` to have been exposed.
|
||||
return;
|
||||
}
|
||||
if (_inferrer.closedWorld.includesClosureCall(selector, mask)) {
|
||||
// TODO(ngeoffray): We could do better here if we knew what we
|
||||
// are calling does not expose this.
|
||||
_markThisAsExposed();
|
||||
_state.markThisAsExposed();
|
||||
} else {
|
||||
_inferrer.forEachElementMatching(selector, mask, (MemberEntity element) {
|
||||
if (element != null && element.isField) {
|
||||
@@ -144,7 +130,7 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
if (!selector.isSetter &&
|
||||
_isInClassOrSubclass(field) &&
|
||||
field.isAssignable &&
|
||||
_fieldScope.readField(field) == null &&
|
||||
_state.readField(field) == null &&
|
||||
getFieldInitializer(_elementMap, field) == null) {
|
||||
// If the field is being used before this constructor
|
||||
// actually had a chance to initialize it, say it can be
|
||||
@@ -156,7 +142,7 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
}
|
||||
// TODO(ngeoffray): We could do better here if we knew what we
|
||||
// are calling does not expose this.
|
||||
_markThisAsExposed();
|
||||
_state.markThisAsExposed();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
@@ -199,12 +185,6 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
_inferrer.addReturnTypeForMethod(analyzedMethod, _returnType, type);
|
||||
}
|
||||
|
||||
void initializationIsIndefinite() {
|
||||
if (_inGenerativeConstructor) {
|
||||
_fieldScope.isIndefinite = true;
|
||||
}
|
||||
}
|
||||
|
||||
TypeInformation _thisType;
|
||||
TypeInformation get thisType {
|
||||
if (_thisType != null) return _thisType;
|
||||
@@ -232,7 +212,7 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
void handleParameter(ir.VariableDeclaration node, {bool isOptional}) {
|
||||
Local local = _localsMap.getLocalVariable(node);
|
||||
DartType type = _localsMap.getLocalType(_elementMap, local);
|
||||
_locals.update(_inferrer, _capturedAndBoxed, local,
|
||||
_state.updateLocal(_inferrer, _capturedAndBoxed, local,
|
||||
_inferrer.typeOfParameter(local), node, type);
|
||||
if (isOptional) {
|
||||
TypeInformation type;
|
||||
@@ -260,7 +240,7 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
_elementMap.elementEnvironment.forEachLocalClassMember(cls,
|
||||
(MemberEntity member) {
|
||||
if (member.isField && member.isInstanceMember && member.isAssignable) {
|
||||
TypeInformation type = _fieldScope.readField(member);
|
||||
TypeInformation type = _state.readField(member);
|
||||
MemberDefinition definition = _elementMap.getMemberDefinition(member);
|
||||
assert(definition.kind == MemberKind.regular);
|
||||
ir.Field node = definition.node;
|
||||
@@ -272,7 +252,7 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
}
|
||||
});
|
||||
}
|
||||
_inferrer.recordExposesThis(_analyzedMember, _isThisExposed);
|
||||
_inferrer.recordExposesThis(_analyzedMember, _state.isThisExposed);
|
||||
|
||||
if (cls.isAbstract) {
|
||||
if (_closedWorld.classHierarchy.isInstantiated(cls)) {
|
||||
@@ -294,7 +274,7 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
visitFieldInitializer(ir.FieldInitializer node) {
|
||||
TypeInformation rhsType = visit(node.value);
|
||||
FieldEntity field = _elementMap.getField(node.field);
|
||||
_fieldScope.updateField(field, rhsType);
|
||||
_state.updateField(field, rhsType);
|
||||
_inferrer.recordTypeOfField(field, rhsType);
|
||||
return null;
|
||||
}
|
||||
@@ -311,7 +291,7 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
|
||||
_inferrer.analyze(constructor);
|
||||
if (_inferrer.checkIfExposesThis(constructor)) {
|
||||
_markThisAsExposed();
|
||||
_state.markThisAsExposed();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -328,7 +308,7 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
|
||||
_inferrer.analyze(constructor);
|
||||
if (_inferrer.checkIfExposesThis(constructor)) {
|
||||
_markThisAsExposed();
|
||||
_state.markThisAsExposed();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -366,10 +346,10 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
case ir.AsyncMarker.Sync:
|
||||
if (_returnType == null) {
|
||||
// No return in the body.
|
||||
_returnType = _locals.seenReturnOrThrow
|
||||
_returnType = _state.seenReturnOrThrow
|
||||
? _types.nonNullEmpty() // Body always throws.
|
||||
: _types.nullType;
|
||||
} else if (!_locals.seenReturnOrThrow) {
|
||||
} else if (!_state.seenReturnOrThrow) {
|
||||
// We haven'TypeInformation seen returns on all branches. So the
|
||||
// method may also return null.
|
||||
recordReturnType(_types.nullType);
|
||||
@@ -429,7 +409,7 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
visitBlock(ir.Block block) {
|
||||
for (ir.Statement statement in block.statements) {
|
||||
visit(statement);
|
||||
if (_locals.aborts) break;
|
||||
if (_state.aborts) break;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -458,39 +438,33 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
List<IsCheck> negativeTests = <IsCheck>[];
|
||||
bool simpleCondition =
|
||||
handleCondition(node.condition, positiveTests, negativeTests);
|
||||
LocalsHandler localsBefore = _locals;
|
||||
FieldInitializationScope fieldScopeBefore = _fieldScope;
|
||||
LocalState stateBefore = _state;
|
||||
|
||||
_locals = new LocalsHandler.from(localsBefore, node);
|
||||
_state = new LocalState.childPath(stateBefore, node);
|
||||
_updateIsChecks(positiveTests, negativeTests);
|
||||
LocalsHandler localsAfterCondition = _locals;
|
||||
FieldInitializationScope fieldScopeAfterThen = _fieldScope;
|
||||
LocalState stateAfterCondition = _state;
|
||||
|
||||
_locals = new LocalsHandler.from(localsBefore, node);
|
||||
_fieldScope = new FieldInitializationScope.from(fieldScopeBefore);
|
||||
_state = new LocalState.childPath(stateBefore, node);
|
||||
if (simpleCondition) _updateIsChecks(negativeTests, positiveTests);
|
||||
visit(node.message);
|
||||
|
||||
LocalsHandler localsAfterMessage = _locals;
|
||||
FieldInitializationScope fieldScopeAfterMessage = _fieldScope;
|
||||
localsAfterMessage.seenReturnOrThrow = true;
|
||||
_locals = localsBefore.mergeDiamondFlow(
|
||||
_inferrer, localsAfterCondition, localsAfterMessage);
|
||||
_fieldScope = fieldScopeBefore?.mergeDiamondFlow(
|
||||
_inferrer, fieldScopeAfterThen, fieldScopeAfterMessage);
|
||||
LocalState stateAfterMessage = _state;
|
||||
stateAfterMessage.seenReturnOrThrow = true;
|
||||
_state = stateBefore.mergeDiamondFlow(
|
||||
_inferrer, stateAfterCondition, stateAfterMessage);
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
visitBreakStatement(ir.BreakStatement node) {
|
||||
JumpTarget target = _localsMap.getJumpTargetForBreak(node);
|
||||
_locals.seenBreakOrContinue = true;
|
||||
_state.seenBreakOrContinue = true;
|
||||
// Do a deep-copy of the locals, because the code following the
|
||||
// break will change them.
|
||||
if (_localsMap.generateContinueForBreak(node)) {
|
||||
_continuesFor[target].add(new LocalsHandler.deepCopyOf(_locals));
|
||||
_continuesFor[target].add(new LocalState.deepCopyOf(_state));
|
||||
} else {
|
||||
_breaksFor[target].add(new LocalsHandler.deepCopyOf(_locals));
|
||||
_breaksFor[target].add(new LocalState.deepCopyOf(_state));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -505,7 +479,7 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
JumpTarget jumpTarget = _localsMap.getJumpTargetForLabel(node);
|
||||
_setupBreaksAndContinues(jumpTarget);
|
||||
visit(body);
|
||||
_locals.mergeAfterBreaks(_inferrer, _getBreaks(jumpTarget));
|
||||
_state.mergeAfterBreaks(_inferrer, _getBreaks(jumpTarget));
|
||||
_clearBreaksAndContinues(jumpTarget);
|
||||
}
|
||||
return null;
|
||||
@@ -533,43 +507,38 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
// visit all cases and update [locals] until we have reached a
|
||||
// fixed point.
|
||||
bool changed;
|
||||
_locals.startLoop(_inferrer, node);
|
||||
_state.startLoop(_inferrer, node);
|
||||
do {
|
||||
changed = false;
|
||||
for (ir.SwitchCase switchCase in node.cases) {
|
||||
LocalsHandler localsBeforeCase = _locals;
|
||||
FieldInitializationScope fieldScopeBeforeCase = _fieldScope;
|
||||
_locals = new LocalsHandler.from(localsBeforeCase, switchCase);
|
||||
_fieldScope = new FieldInitializationScope.from(fieldScopeBeforeCase);
|
||||
LocalState stateBeforeCase = _state;
|
||||
_state = new LocalState.childPath(stateBeforeCase, switchCase);
|
||||
visit(switchCase);
|
||||
LocalsHandler localsAfterCase = _locals;
|
||||
changed = localsBeforeCase.mergeAll(_inferrer, [localsAfterCase]) ||
|
||||
changed;
|
||||
_locals = localsBeforeCase;
|
||||
_fieldScope = fieldScopeBeforeCase;
|
||||
LocalState stateAfterCase = _state;
|
||||
changed =
|
||||
stateBeforeCase.mergeAll(_inferrer, [stateAfterCase]) || changed;
|
||||
_state = stateBeforeCase;
|
||||
}
|
||||
} while (changed);
|
||||
_locals.endLoop(_inferrer, node);
|
||||
_state.endLoop(_inferrer, node);
|
||||
|
||||
continueTargets.forEach(_clearBreaksAndContinues);
|
||||
} else {
|
||||
LocalsHandler localsBeforeCase = _locals;
|
||||
FieldInitializationScope fieldScopeBeforeCase = _fieldScope;
|
||||
List<LocalsHandler> localsToMerge = <LocalsHandler>[];
|
||||
LocalState stateBeforeCase = _state;
|
||||
List<LocalState> statesToMerge = <LocalState>[];
|
||||
bool hasDefaultCase = false;
|
||||
|
||||
for (ir.SwitchCase switchCase in node.cases) {
|
||||
if (switchCase.isDefault) {
|
||||
hasDefaultCase = true;
|
||||
}
|
||||
_locals = new LocalsHandler.from(localsBeforeCase, switchCase);
|
||||
_state = new LocalState.childPath(stateBeforeCase, switchCase);
|
||||
visit(switchCase);
|
||||
localsToMerge.add(_locals);
|
||||
statesToMerge.add(_state);
|
||||
}
|
||||
localsBeforeCase.mergeAfterBreaks(_inferrer, localsToMerge,
|
||||
stateBeforeCase.mergeAfterBreaks(_inferrer, statesToMerge,
|
||||
keepOwnLocals: !hasDefaultCase);
|
||||
_locals = localsBeforeCase;
|
||||
_fieldScope = fieldScopeBeforeCase;
|
||||
_state = stateBeforeCase;
|
||||
}
|
||||
_clearBreaksAndContinues(jumpTarget);
|
||||
return null;
|
||||
@@ -584,10 +553,10 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
@override
|
||||
visitContinueSwitchStatement(ir.ContinueSwitchStatement node) {
|
||||
JumpTarget target = _localsMap.getJumpTargetForContinueSwitch(node);
|
||||
_locals.seenBreakOrContinue = true;
|
||||
_state.seenBreakOrContinue = true;
|
||||
// Do a deep-copy of the locals, because the code following the
|
||||
// break will change them.
|
||||
_continuesFor[target].add(new LocalsHandler.deepCopyOf(_locals));
|
||||
_continuesFor[target].add(new LocalState.deepCopyOf(_state));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -637,8 +606,8 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
TypeInformation visitReturnStatement(ir.ReturnStatement node) {
|
||||
ir.Node expression = node.expression;
|
||||
recordReturnType(expression == null ? _types.nullType : visit(expression));
|
||||
_locals.seenReturnOrThrow = true;
|
||||
initializationIsIndefinite();
|
||||
_state.seenReturnOrThrow = true;
|
||||
_state.markInitializationAsIndefinite();
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -706,14 +675,14 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
Local local = _localsMap.getLocalVariable(node);
|
||||
DartType type = _localsMap.getLocalType(_elementMap, local);
|
||||
if (node.initializer == null) {
|
||||
_locals.update(
|
||||
_state.updateLocal(
|
||||
_inferrer, _capturedAndBoxed, local, _types.nullType, node, type);
|
||||
} else {
|
||||
_locals.update(_inferrer, _capturedAndBoxed, local,
|
||||
_state.updateLocal(_inferrer, _capturedAndBoxed, local,
|
||||
visit(node.initializer), node, type);
|
||||
}
|
||||
if (node.initializer is ir.ThisExpression) {
|
||||
_markThisAsExposed();
|
||||
_state.markThisAsExposed();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -721,7 +690,8 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
@override
|
||||
TypeInformation visitVariableGet(ir.VariableGet node) {
|
||||
Local local = _localsMap.getLocalVariable(node.variable);
|
||||
TypeInformation type = _locals.use(_inferrer, _capturedAndBoxed, local);
|
||||
TypeInformation type =
|
||||
_state.readLocal(_inferrer, _capturedAndBoxed, local);
|
||||
assert(type != null, "Missing type information for $local.");
|
||||
return type;
|
||||
}
|
||||
@@ -730,11 +700,12 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
TypeInformation visitVariableSet(ir.VariableSet node) {
|
||||
TypeInformation rhsType = visit(node.value);
|
||||
if (node.value is ir.ThisExpression) {
|
||||
_markThisAsExposed();
|
||||
_state.markThisAsExposed();
|
||||
}
|
||||
Local local = _localsMap.getLocalVariable(node.variable);
|
||||
DartType type = _localsMap.getLocalType(_elementMap, local);
|
||||
_locals.update(_inferrer, _capturedAndBoxed, local, rhsType, node, type);
|
||||
_state.updateLocal(
|
||||
_inferrer, _capturedAndBoxed, local, rhsType, node, type);
|
||||
return rhsType;
|
||||
}
|
||||
|
||||
@@ -745,7 +716,7 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
// TODO(ngeoffray): We could do better here if we knew what we
|
||||
// are calling does not expose this.
|
||||
if (argument is ir.ThisExpression) {
|
||||
_markThisAsExposed();
|
||||
_state.markThisAsExposed();
|
||||
}
|
||||
positional.add(visit(argument));
|
||||
}
|
||||
@@ -755,7 +726,7 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
// TODO(ngeoffray): We could do better here if we knew what we
|
||||
// are calling does not expose this.
|
||||
if (value is ir.ThisExpression) {
|
||||
_markThisAsExposed();
|
||||
_state.markThisAsExposed();
|
||||
}
|
||||
named[argument.name] = visit(value);
|
||||
}
|
||||
@@ -846,7 +817,7 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
TypeInformation refinedType = _types
|
||||
.refineReceiver(selector, mask, receiverType, isConditional: false);
|
||||
DartType type = _localsMap.getLocalType(_elementMap, local);
|
||||
_locals.update(
|
||||
_state.updateLocal(
|
||||
_inferrer, _capturedAndBoxed, local, refinedType, node, type);
|
||||
List<Refinement> refinements = _localRefinementMap[variable];
|
||||
if (refinements != null) {
|
||||
@@ -931,12 +902,12 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
Local local = _localsMap.getLocalVariable(alias);
|
||||
DartType type = _localsMap.getLocalType(_elementMap, local);
|
||||
TypeInformation localType =
|
||||
_locals.use(_inferrer, _capturedAndBoxed, local);
|
||||
_state.readLocal(_inferrer, _capturedAndBoxed, local);
|
||||
for (Refinement refinement in refinements) {
|
||||
localType = _types.refineReceiver(
|
||||
refinement.selector, refinement.mask, localType,
|
||||
isConditional: true);
|
||||
_locals.update(
|
||||
_state.updateLocal(
|
||||
_inferrer, _capturedAndBoxed, local, localType, node, type);
|
||||
}
|
||||
}
|
||||
@@ -949,7 +920,7 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
if (node.iterable is ir.ThisExpression) {
|
||||
// Any reasonable implementation of an iterator would expose
|
||||
// this, so we play it safe and assume it will.
|
||||
_markThisAsExposed();
|
||||
_state.markThisAsExposed();
|
||||
}
|
||||
|
||||
AbstractValue currentMask;
|
||||
@@ -990,7 +961,7 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
|
||||
Local variable = _localsMap.getLocalVariable(node.variable);
|
||||
DartType variableType = _localsMap.getLocalType(_elementMap, variable);
|
||||
_locals.update(_inferrer, _capturedAndBoxed, variable, currentType,
|
||||
_state.updateLocal(_inferrer, _capturedAndBoxed, variable, currentType,
|
||||
node.variable, variableType);
|
||||
|
||||
JumpTarget target = _localsMap.getJumpTargetForForIn(node);
|
||||
@@ -1002,10 +973,10 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
void _setupBreaksAndContinues(JumpTarget target) {
|
||||
if (target == null) return;
|
||||
if (target.isContinueTarget) {
|
||||
_continuesFor[target] = <LocalsHandler>[];
|
||||
_continuesFor[target] = <LocalState>[];
|
||||
}
|
||||
if (target.isBreakTarget) {
|
||||
_breaksFor[target] = <LocalsHandler>[];
|
||||
_breaksFor[target] = <LocalState>[];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1014,15 +985,15 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
_breaksFor.remove(element);
|
||||
}
|
||||
|
||||
List<LocalsHandler> _getBreaks(JumpTarget target) {
|
||||
List<LocalsHandler> list = <LocalsHandler>[_locals];
|
||||
List<LocalState> _getBreaks(JumpTarget target) {
|
||||
List<LocalState> list = <LocalState>[_state];
|
||||
if (target == null) return list;
|
||||
if (!target.isBreakTarget) return list;
|
||||
return list..addAll(_breaksFor[target]);
|
||||
}
|
||||
|
||||
List<LocalsHandler> _getLoopBackEdges(JumpTarget target) {
|
||||
List<LocalsHandler> list = <LocalsHandler>[_locals];
|
||||
List<LocalState> _getLoopBackEdges(JumpTarget target) {
|
||||
List<LocalState> list = <LocalState>[_state];
|
||||
if (target == null) return list;
|
||||
if (!target.isContinueTarget) return list;
|
||||
return list..addAll(_continuesFor[target]);
|
||||
@@ -1031,24 +1002,21 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
TypeInformation handleLoop(ir.Node node, JumpTarget target, void logic()) {
|
||||
_loopLevel++;
|
||||
bool changed = false;
|
||||
LocalsHandler localsBefore = _locals;
|
||||
FieldInitializationScope fieldScopeBefore = _fieldScope;
|
||||
localsBefore.startLoop(_inferrer, node);
|
||||
LocalState stateBefore = _state;
|
||||
stateBefore.startLoop(_inferrer, node);
|
||||
do {
|
||||
// Setup (and clear in case of multiple iterations of the loop)
|
||||
// the lists of breaks and continues seen in the loop.
|
||||
_setupBreaksAndContinues(target);
|
||||
_locals = new LocalsHandler.from(localsBefore, node);
|
||||
_fieldScope = new FieldInitializationScope.from(fieldScopeBefore);
|
||||
_state = new LocalState.childPath(stateBefore, node);
|
||||
logic();
|
||||
changed = localsBefore.mergeAll(_inferrer, _getLoopBackEdges(target));
|
||||
changed = stateBefore.mergeAll(_inferrer, _getLoopBackEdges(target));
|
||||
} while (changed);
|
||||
_loopLevel--;
|
||||
localsBefore.endLoop(_inferrer, node);
|
||||
stateBefore.endLoop(_inferrer, node);
|
||||
bool keepOwnLocals = node is! ir.DoStatement;
|
||||
_locals = localsBefore.mergeAfterBreaks(_inferrer, _getBreaks(target),
|
||||
_state = stateBefore.mergeAfterBreaks(_inferrer, _getBreaks(target),
|
||||
keepOwnLocals: keepOwnLocals);
|
||||
_fieldScope = fieldScopeBefore;
|
||||
_clearBreaksAndContinues(target);
|
||||
return null;
|
||||
}
|
||||
@@ -1249,7 +1217,7 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
TypeInformation visitStaticSet(ir.StaticSet node) {
|
||||
TypeInformation rhsType = visit(node.value);
|
||||
if (node.value is ir.ThisExpression) {
|
||||
_markThisAsExposed();
|
||||
_state.markThisAsExposed();
|
||||
}
|
||||
MemberEntity member = _elementMap.getMember(node.target);
|
||||
AbstractValue mask = _memberData.typeOfSend(node);
|
||||
@@ -1299,7 +1267,7 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
|
||||
TypeInformation rhsType = visit(node.value);
|
||||
if (node.value is ir.ThisExpression) {
|
||||
_markThisAsExposed();
|
||||
_state.markThisAsExposed();
|
||||
}
|
||||
|
||||
if (_inGenerativeConstructor && node.receiver is ir.ThisExpression) {
|
||||
@@ -1314,7 +1282,7 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
MemberEntity single = targets.first;
|
||||
if (single.isField) {
|
||||
FieldEntity field = single;
|
||||
_fieldScope.updateField(field, rhsType);
|
||||
_state.updateField(field, rhsType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1375,11 +1343,11 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
List<IsCheck> positiveTests, List<IsCheck> negativeTests) {
|
||||
for (IsCheck check in positiveTests) {
|
||||
if (check.type != null) {
|
||||
_locals.narrow(
|
||||
_state.narrowLocal(
|
||||
_inferrer, _capturedAndBoxed, check.local, check.type, check.node);
|
||||
} else {
|
||||
DartType localType = _localsMap.getLocalType(_elementMap, check.local);
|
||||
_locals.update(_inferrer, _capturedAndBoxed, check.local,
|
||||
_state.updateLocal(_inferrer, _capturedAndBoxed, check.local,
|
||||
_types.nullType, check.node, localType);
|
||||
}
|
||||
}
|
||||
@@ -1387,7 +1355,7 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
if (check.type != null) {
|
||||
// TODO(johnniwinther): Use negative type knowledge.
|
||||
} else {
|
||||
_locals.narrow(_inferrer, _capturedAndBoxed, check.local,
|
||||
_state.narrowLocal(_inferrer, _capturedAndBoxed, check.local,
|
||||
_closedWorld.commonElements.objectType, check.node);
|
||||
}
|
||||
}
|
||||
@@ -1399,28 +1367,21 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
List<IsCheck> negativeTests = <IsCheck>[];
|
||||
bool simpleCondition =
|
||||
handleCondition(node.condition, positiveTests, negativeTests);
|
||||
LocalsHandler localsBefore = _locals;
|
||||
FieldInitializationScope fieldScopeBefore = _fieldScope;
|
||||
_locals = new LocalsHandler.from(localsBefore, node);
|
||||
_fieldScope = new FieldInitializationScope.from(fieldScopeBefore);
|
||||
LocalState stateBefore = _state;
|
||||
_state = new LocalState.childPath(stateBefore, node);
|
||||
_updateIsChecks(positiveTests, negativeTests);
|
||||
visit(node.then);
|
||||
|
||||
LocalsHandler localsAfterThen = _locals;
|
||||
FieldInitializationScope fieldScopeAfterThen = _fieldScope;
|
||||
_locals = new LocalsHandler.from(localsBefore, node);
|
||||
_fieldScope = new FieldInitializationScope.from(fieldScopeBefore);
|
||||
LocalState stateAfterThen = _state;
|
||||
_state = new LocalState.childPath(stateBefore, node);
|
||||
if (simpleCondition) {
|
||||
_updateIsChecks(negativeTests, positiveTests);
|
||||
}
|
||||
visit(node.otherwise);
|
||||
LocalsHandler localsAfterElse = _locals;
|
||||
FieldInitializationScope fieldScopeAfterElse = _fieldScope;
|
||||
LocalState stateAfterElse = _state;
|
||||
|
||||
_locals = localsBefore.mergeDiamondFlow(
|
||||
_inferrer, localsAfterThen, localsAfterElse);
|
||||
_fieldScope = fieldScopeBefore?.mergeDiamondFlow(
|
||||
_inferrer, fieldScopeAfterThen, fieldScopeAfterElse);
|
||||
_state =
|
||||
stateBefore.mergeDiamondFlow(_inferrer, stateAfterThen, stateAfterElse);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1456,15 +1417,13 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
_negativeIsChecks = <IsCheck>[];
|
||||
}
|
||||
visit(node.left, conditionContext: _accumulateIsChecks);
|
||||
LocalsHandler localsBefore = _locals;
|
||||
FieldInitializationScope fieldScopeBefore = _fieldScope;
|
||||
_locals = new LocalsHandler.from(localsBefore, node);
|
||||
_fieldScope = new FieldInitializationScope.from(fieldScopeBefore);
|
||||
LocalState stateBefore = _state;
|
||||
_state = new LocalState.childPath(stateBefore, node);
|
||||
_updateIsChecks(_positiveIsChecks, _negativeIsChecks);
|
||||
|
||||
LocalsHandler narrowed;
|
||||
LocalState narrowed;
|
||||
if (oldAccumulateIsChecks) {
|
||||
narrowed = new LocalsHandler.topLevelCopyOf(_locals);
|
||||
narrowed = new LocalState.deepCopyOf(_state);
|
||||
} else {
|
||||
_accumulateIsChecks = false;
|
||||
_positiveIsChecks = oldPositiveIsChecks;
|
||||
@@ -1474,17 +1433,16 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
if (oldAccumulateIsChecks) {
|
||||
bool invalidatedInRightHandSide(IsCheck check) {
|
||||
TypeInformation narrowedType =
|
||||
narrowed.use(_inferrer, _capturedAndBoxed, check.local);
|
||||
narrowed.readLocal(_inferrer, _capturedAndBoxed, check.local);
|
||||
TypeInformation currentType =
|
||||
_locals.use(_inferrer, _capturedAndBoxed, check.local);
|
||||
_state.readLocal(_inferrer, _capturedAndBoxed, check.local);
|
||||
return narrowedType != currentType;
|
||||
}
|
||||
|
||||
_positiveIsChecks.removeWhere(invalidatedInRightHandSide);
|
||||
_negativeIsChecks.removeWhere(invalidatedInRightHandSide);
|
||||
}
|
||||
_locals = localsBefore.mergeFlow(_inferrer, _locals);
|
||||
_fieldScope = fieldScopeBefore;
|
||||
_state = stateBefore.mergeFlow(_inferrer, _state);
|
||||
return _types.boolType;
|
||||
} else if (node.operator == '||') {
|
||||
_conditionIsSimple = false;
|
||||
@@ -1492,16 +1450,13 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
List<IsCheck> negativeIsChecks = <IsCheck>[];
|
||||
bool isSimple =
|
||||
handleCondition(node.left, positiveIsChecks, negativeIsChecks);
|
||||
LocalsHandler localsBefore = _locals;
|
||||
FieldInitializationScope fieldScopeBefore = _fieldScope;
|
||||
_locals = new LocalsHandler.from(_locals, node);
|
||||
_fieldScope = new FieldInitializationScope.from(fieldScopeBefore);
|
||||
LocalState stateBefore = _state;
|
||||
_state = new LocalState.childPath(stateBefore, node);
|
||||
if (isSimple) {
|
||||
_updateIsChecks(negativeIsChecks, positiveIsChecks);
|
||||
}
|
||||
visit(node.right, conditionContext: false);
|
||||
_locals = localsBefore.mergeFlow(_inferrer, _locals);
|
||||
_fieldScope = fieldScopeBefore;
|
||||
_state = stateBefore.mergeFlow(_inferrer, _state);
|
||||
return _types.boolType;
|
||||
}
|
||||
failedAt(CURRENT_ELEMENT_SPANNABLE,
|
||||
@@ -1515,24 +1470,17 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
List<IsCheck> negativeTests = <IsCheck>[];
|
||||
bool simpleCondition =
|
||||
handleCondition(node.condition, positiveTests, negativeTests);
|
||||
LocalsHandler localsBefore = _locals;
|
||||
_locals = new LocalsHandler.from(localsBefore, node);
|
||||
FieldInitializationScope fieldScopeBefore = _fieldScope;
|
||||
_fieldScope = new FieldInitializationScope.from(fieldScopeBefore);
|
||||
LocalState stateBefore = _state;
|
||||
_state = new LocalState.childPath(stateBefore, node);
|
||||
_updateIsChecks(positiveTests, negativeTests);
|
||||
TypeInformation firstType = visit(node.then);
|
||||
LocalsHandler localsAfterThen = _locals;
|
||||
FieldInitializationScope fieldScopeAfterThen = _fieldScope;
|
||||
_locals = new LocalsHandler.from(localsBefore, node);
|
||||
_fieldScope = new FieldInitializationScope.from(fieldScopeBefore);
|
||||
LocalState stateAfterThen = _state;
|
||||
_state = new LocalState.childPath(stateBefore, node);
|
||||
if (simpleCondition) _updateIsChecks(negativeTests, positiveTests);
|
||||
TypeInformation secondType = visit(node.otherwise);
|
||||
LocalsHandler localsAfterElse = _locals;
|
||||
FieldInitializationScope fieldScopeAfterElse = _fieldScope;
|
||||
_locals = localsBefore.mergeDiamondFlow(
|
||||
_inferrer, localsAfterThen, localsAfterElse);
|
||||
_fieldScope = fieldScopeBefore?.mergeDiamondFlow(
|
||||
_inferrer, fieldScopeAfterThen, fieldScopeAfterElse);
|
||||
LocalState stateAfterElse = _state;
|
||||
_state =
|
||||
stateBefore.mergeDiamondFlow(_inferrer, stateAfterThen, stateAfterElse);
|
||||
return _types.allocateDiamondPhi(firstType, secondType);
|
||||
}
|
||||
|
||||
@@ -1544,7 +1492,7 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
// analyzing the closure.
|
||||
// TODO(herhut): Analyze whether closure exposes this. Possibly using
|
||||
// whether the created closure as a `thisLocal`.
|
||||
_markThisAsExposed();
|
||||
_state.markThisAsExposed();
|
||||
|
||||
ClosureRepresentationInfo info = _closureDataLookup.getClosureInfo(node);
|
||||
|
||||
@@ -1557,7 +1505,7 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
_inferrer.recordTypeOfField(field, thisType);
|
||||
}
|
||||
TypeInformation localType =
|
||||
_locals.use(_inferrer, _capturedAndBoxed, variable);
|
||||
_state.readLocal(_inferrer, _capturedAndBoxed, variable);
|
||||
// The type is null for type parameters.
|
||||
if (localType != null) {
|
||||
_inferrer.recordTypeOfField(field, localType);
|
||||
@@ -1573,17 +1521,14 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
if (variable != null) {
|
||||
Local local = _localsMap.getLocalVariable(variable);
|
||||
DartType type = _localsMap.getLocalType(_elementMap, local);
|
||||
_locals.update(
|
||||
_state.updateLocal(
|
||||
_inferrer, _capturedAndBoxed, local, localFunctionType, node, type);
|
||||
}
|
||||
|
||||
// We don't put the closure in the work queue of the
|
||||
// inferrer, because it will share information with its enclosing
|
||||
// method, like for example the types of local variables.
|
||||
LocalsHandler closureLocals =
|
||||
new LocalsHandler.from(_locals, node, useOtherTryBlock: false);
|
||||
FieldInitializationScope closureFieldScope =
|
||||
new FieldInitializationScope.from(_fieldScope);
|
||||
LocalState closureState = new LocalState.closure(_state, node);
|
||||
KernelTypeGraphBuilder visitor = new KernelTypeGraphBuilder(
|
||||
_options,
|
||||
_closedWorld,
|
||||
@@ -1591,8 +1536,7 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
info.callMethod,
|
||||
functionNode,
|
||||
_localsMap,
|
||||
closureLocals,
|
||||
closureFieldScope,
|
||||
closureState,
|
||||
_capturedAndBoxed);
|
||||
visitor.run();
|
||||
_inferrer.recordReturnType(info.callMethod, visitor._returnType);
|
||||
@@ -1655,41 +1599,30 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
|
||||
@override
|
||||
visitTryCatch(ir.TryCatch node) {
|
||||
LocalsHandler localsBefore = _locals;
|
||||
FieldInitializationScope fieldScopeBefore = _fieldScope;
|
||||
_locals = new LocalsHandler.from(localsBefore, node,
|
||||
isTry: true, useOtherTryBlock: false);
|
||||
_fieldScope = new FieldInitializationScope.from(fieldScopeBefore);
|
||||
initializationIsIndefinite();
|
||||
LocalState stateBefore = _state;
|
||||
_state = new LocalState.tryBlock(stateBefore, node);
|
||||
_state.markInitializationAsIndefinite();
|
||||
visit(node.body);
|
||||
LocalsHandler localsAfterBody = _locals;
|
||||
_locals = localsBefore.mergeFlow(_inferrer, localsAfterBody);
|
||||
_fieldScope = fieldScopeBefore;
|
||||
LocalState stateAfterBody = _state;
|
||||
_state = stateBefore.mergeFlow(_inferrer, stateAfterBody);
|
||||
for (ir.Catch catchBlock in node.catches) {
|
||||
LocalsHandler localsBeforeCatch = _locals;
|
||||
FieldInitializationScope fieldScopeBeforeCatch = _fieldScope;
|
||||
_locals = new LocalsHandler.from(localsBeforeCatch, catchBlock);
|
||||
_fieldScope = new FieldInitializationScope.from(fieldScopeBeforeCatch);
|
||||
LocalState stateBeforeCatch = _state;
|
||||
_state = new LocalState.childPath(stateBeforeCatch, catchBlock);
|
||||
visit(catchBlock);
|
||||
LocalsHandler localsAfterCatch = _locals;
|
||||
_locals = localsBeforeCatch.mergeFlow(_inferrer, localsAfterCatch);
|
||||
_fieldScope = fieldScopeBeforeCatch;
|
||||
LocalState stateAfterCatch = _state;
|
||||
_state = stateBeforeCatch.mergeFlow(_inferrer, stateAfterCatch);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
visitTryFinally(ir.TryFinally node) {
|
||||
LocalsHandler localsBefore = _locals;
|
||||
FieldInitializationScope fieldScopeBefore = _fieldScope;
|
||||
_locals = new LocalsHandler.from(localsBefore, node,
|
||||
isTry: true, useOtherTryBlock: false);
|
||||
_fieldScope = new FieldInitializationScope.from(fieldScopeBefore);
|
||||
initializationIsIndefinite();
|
||||
LocalState stateBefore = _state;
|
||||
_state = new LocalState.tryBlock(stateBefore, node);
|
||||
_state.markInitializationAsIndefinite();
|
||||
visit(node.body);
|
||||
LocalsHandler localsAfterBody = _locals;
|
||||
_locals = localsBefore.mergeFlow(_inferrer, localsAfterBody);
|
||||
_fieldScope = fieldScopeBefore;
|
||||
LocalState stateAfterBody = _state;
|
||||
_state = stateBefore.mergeFlow(_inferrer, stateAfterBody);
|
||||
visit(node.finalizer);
|
||||
return null;
|
||||
}
|
||||
@@ -1709,15 +1642,15 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
mask = _types.dynamicType;
|
||||
}
|
||||
Local local = _localsMap.getLocalVariable(exception);
|
||||
_locals.update(
|
||||
_state.updateLocal(
|
||||
_inferrer, _capturedAndBoxed, local, mask, node, const DynamicType());
|
||||
}
|
||||
ir.VariableDeclaration stackTrace = node.stackTrace;
|
||||
if (stackTrace != null) {
|
||||
Local local = _localsMap.getLocalVariable(stackTrace);
|
||||
// TODO(johnniwinther): Use a mask based on [StackTrace].
|
||||
_locals.update(_inferrer, _capturedAndBoxed, local, _types.dynamicType,
|
||||
node, const DynamicType());
|
||||
_state.updateLocal(_inferrer, _capturedAndBoxed, local,
|
||||
_types.dynamicType, node, const DynamicType());
|
||||
}
|
||||
visit(node.body);
|
||||
return null;
|
||||
@@ -1726,13 +1659,13 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
@override
|
||||
TypeInformation visitThrow(ir.Throw node) {
|
||||
visit(node.expression);
|
||||
_locals.seenReturnOrThrow = true;
|
||||
_state.seenReturnOrThrow = true;
|
||||
return _types.nonNullEmpty();
|
||||
}
|
||||
|
||||
@override
|
||||
TypeInformation visitRethrow(ir.Rethrow node) {
|
||||
_locals.seenReturnOrThrow = true;
|
||||
_state.seenReturnOrThrow = true;
|
||||
return _types.nonNullEmpty();
|
||||
}
|
||||
|
||||
@@ -1749,7 +1682,7 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
TypeInformation visitSuperPropertyGet(ir.SuperPropertyGet node) {
|
||||
// TODO(herhut): We could do better here if we knew what we
|
||||
// are calling does not expose this.
|
||||
_markThisAsExposed();
|
||||
_state.markThisAsExposed();
|
||||
|
||||
MemberEntity member =
|
||||
_elementMap.getSuperMember(_analyzedMember, node.name);
|
||||
@@ -1771,7 +1704,7 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
TypeInformation visitSuperPropertySet(ir.SuperPropertySet node) {
|
||||
// TODO(herhut): We could do better here if we knew what we
|
||||
// are calling does not expose this.
|
||||
_markThisAsExposed();
|
||||
_state.markThisAsExposed();
|
||||
|
||||
TypeInformation rhsType = visit(node.value);
|
||||
MemberEntity member =
|
||||
@@ -1791,7 +1724,7 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
|
||||
TypeInformation visitSuperMethodInvocation(ir.SuperMethodInvocation node) {
|
||||
// TODO(herhut): We could do better here if we knew what we
|
||||
// are calling does not expose this.
|
||||
_markThisAsExposed();
|
||||
_state.markThisAsExposed();
|
||||
|
||||
MemberEntity member =
|
||||
_elementMap.getSuperMember(_analyzedMember, node.name);
|
||||
@@ -1867,3 +1800,188 @@ class Refinement {
|
||||
|
||||
Refinement(this.selector, this.mask);
|
||||
}
|
||||
|
||||
class LocalState {
|
||||
final LocalsHandler _locals;
|
||||
final FieldInitializationScope _fields;
|
||||
bool seenReturnOrThrow = false;
|
||||
bool seenBreakOrContinue = false;
|
||||
LocalsHandler _tryBlock;
|
||||
|
||||
LocalState.initial(ir.TreeNode node, {bool inGenerativeConstructor})
|
||||
: this.internal(
|
||||
new LocalsHandler(node),
|
||||
inGenerativeConstructor ? new FieldInitializationScope() : null,
|
||||
null,
|
||||
seenReturnOrThrow: false,
|
||||
seenBreakOrContinue: false);
|
||||
|
||||
LocalState.childPath(LocalState other, ir.TreeNode node)
|
||||
: this.internal(new LocalsHandler.from(other._locals, node, isTry: false),
|
||||
new FieldInitializationScope.from(other._fields), other._tryBlock,
|
||||
seenReturnOrThrow: false, seenBreakOrContinue: false);
|
||||
|
||||
LocalState.closure(LocalState other, ir.TreeNode node)
|
||||
: this.internal(new LocalsHandler.from(other._locals, node, isTry: false),
|
||||
new FieldInitializationScope.from(other._fields), null,
|
||||
seenReturnOrThrow: false, seenBreakOrContinue: false);
|
||||
|
||||
factory LocalState.tryBlock(LocalState other, ir.TreeNode node) {
|
||||
LocalsHandler locals =
|
||||
new LocalsHandler.from(other._locals, node, isTry: true);
|
||||
FieldInitializationScope fieldScope =
|
||||
new FieldInitializationScope.from(other._fields);
|
||||
LocalsHandler tryBlock = locals;
|
||||
return new LocalState.internal(locals, fieldScope, tryBlock,
|
||||
seenReturnOrThrow: false, seenBreakOrContinue: false);
|
||||
}
|
||||
|
||||
LocalState.deepCopyOf(LocalState other)
|
||||
: _locals = new LocalsHandler.deepCopyOf(other._locals),
|
||||
_tryBlock = other._tryBlock,
|
||||
_fields = other._fields;
|
||||
|
||||
LocalState.internal(this._locals, this._fields, this._tryBlock,
|
||||
{this.seenReturnOrThrow, this.seenBreakOrContinue});
|
||||
|
||||
bool get aborts {
|
||||
return seenReturnOrThrow || seenBreakOrContinue;
|
||||
}
|
||||
|
||||
bool get isThisExposed {
|
||||
return _fields == null || _fields.isThisExposed;
|
||||
}
|
||||
|
||||
void markThisAsExposed() {
|
||||
_fields?.isThisExposed = true;
|
||||
}
|
||||
|
||||
void markInitializationAsIndefinite() {
|
||||
_fields?.isIndefinite = true;
|
||||
}
|
||||
|
||||
TypeInformation readField(FieldEntity field) {
|
||||
return _fields.readField(field);
|
||||
}
|
||||
|
||||
void updateField(FieldEntity field, TypeInformation type) {
|
||||
_fields.updateField(field, type);
|
||||
}
|
||||
|
||||
TypeInformation readLocal(InferrerEngine inferrer,
|
||||
Map<Local, FieldEntity> capturedAndBoxed, Local local) {
|
||||
FieldEntity field = capturedAndBoxed[local];
|
||||
if (field != null) {
|
||||
return inferrer.typeOfMember(field);
|
||||
} else {
|
||||
return _locals.use(inferrer, local);
|
||||
}
|
||||
}
|
||||
|
||||
void updateLocal(
|
||||
InferrerEngine inferrer,
|
||||
Map<Local, FieldEntity> capturedAndBoxed,
|
||||
Local local,
|
||||
TypeInformation type,
|
||||
ir.Node node,
|
||||
DartType staticType) {
|
||||
assert(type != null);
|
||||
type = inferrer.types.narrowType(type, staticType);
|
||||
|
||||
FieldEntity field = capturedAndBoxed[local];
|
||||
if (field != null) {
|
||||
inferrer.recordTypeOfField(field, type);
|
||||
} else {
|
||||
_locals.update(inferrer, local, type, node, staticType, _tryBlock);
|
||||
}
|
||||
}
|
||||
|
||||
void narrowLocal(
|
||||
InferrerEngine inferrer,
|
||||
Map<Local, FieldEntity> capturedAndBoxed,
|
||||
Local local,
|
||||
DartType type,
|
||||
ir.Node node) {
|
||||
TypeInformation existing = readLocal(inferrer, capturedAndBoxed, local);
|
||||
TypeInformation newType =
|
||||
inferrer.types.narrowType(existing, type, isNullable: false);
|
||||
updateLocal(inferrer, capturedAndBoxed, local, newType, node, type);
|
||||
}
|
||||
|
||||
LocalState mergeFlow(InferrerEngine inferrer, LocalState other) {
|
||||
seenReturnOrThrow = false;
|
||||
seenBreakOrContinue = false;
|
||||
|
||||
if (other.aborts) {
|
||||
return this;
|
||||
}
|
||||
LocalsHandler locals = _locals.mergeFlow(inferrer, other._locals);
|
||||
return new LocalState.internal(locals, _fields, _tryBlock,
|
||||
seenReturnOrThrow: seenReturnOrThrow,
|
||||
seenBreakOrContinue: seenBreakOrContinue);
|
||||
}
|
||||
|
||||
LocalState mergeDiamondFlow(
|
||||
InferrerEngine inferrer, LocalState thenBranch, LocalState elseBranch) {
|
||||
seenReturnOrThrow =
|
||||
thenBranch.seenReturnOrThrow && elseBranch.seenReturnOrThrow;
|
||||
seenBreakOrContinue =
|
||||
thenBranch.seenBreakOrContinue && elseBranch.seenBreakOrContinue;
|
||||
|
||||
LocalsHandler locals;
|
||||
if (aborts) {
|
||||
locals = _locals;
|
||||
} else if (thenBranch.aborts) {
|
||||
locals = _locals.mergeFlow(inferrer, elseBranch._locals, inPlace: true);
|
||||
} else if (elseBranch.aborts) {
|
||||
locals = _locals.mergeFlow(inferrer, thenBranch._locals, inPlace: true);
|
||||
} else {
|
||||
locals = _locals.mergeDiamondFlow(
|
||||
inferrer, thenBranch._locals, elseBranch._locals);
|
||||
}
|
||||
|
||||
FieldInitializationScope fieldScope = _fields?.mergeDiamondFlow(
|
||||
inferrer, thenBranch._fields, elseBranch._fields);
|
||||
return new LocalState.internal(locals, fieldScope, _tryBlock,
|
||||
seenReturnOrThrow: seenReturnOrThrow,
|
||||
seenBreakOrContinue: seenBreakOrContinue);
|
||||
}
|
||||
|
||||
LocalState mergeAfterBreaks(InferrerEngine inferrer, List<LocalState> states,
|
||||
{bool keepOwnLocals: true}) {
|
||||
bool allBranchesAbort = true;
|
||||
for (LocalState state in states) {
|
||||
allBranchesAbort = allBranchesAbort && state.seenReturnOrThrow;
|
||||
}
|
||||
|
||||
keepOwnLocals = keepOwnLocals && !seenReturnOrThrow;
|
||||
|
||||
LocalsHandler locals = _locals.mergeAfterBreaks(
|
||||
inferrer,
|
||||
states
|
||||
.where((LocalState state) => !state.seenReturnOrThrow)
|
||||
.map((LocalState state) => state._locals),
|
||||
keepOwnLocals: keepOwnLocals);
|
||||
seenReturnOrThrow = allBranchesAbort && !keepOwnLocals;
|
||||
return new LocalState.internal(locals, _fields, _tryBlock,
|
||||
seenReturnOrThrow: seenReturnOrThrow,
|
||||
seenBreakOrContinue: seenBreakOrContinue);
|
||||
}
|
||||
|
||||
bool mergeAll(InferrerEngine inferrer, List<LocalState> states) {
|
||||
assert(!seenReturnOrThrow);
|
||||
return _locals.mergeAll(
|
||||
inferrer,
|
||||
states
|
||||
.where((LocalState state) => !state.seenReturnOrThrow)
|
||||
.map((LocalState state) => state._locals));
|
||||
}
|
||||
|
||||
void startLoop(InferrerEngine inferrer, ir.Node loop) {
|
||||
_locals.startLoop(inferrer, loop);
|
||||
}
|
||||
|
||||
void endLoop(InferrerEngine inferrer, ir.Node loop) {
|
||||
_locals.endLoop(inferrer, loop);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,69 +251,37 @@ class ArgumentsTypesIterator implements Iterator<TypeInformation> {
|
||||
*/
|
||||
class LocalsHandler {
|
||||
final VariableScope _locals;
|
||||
LocalsHandler _tryBlock;
|
||||
bool seenReturnOrThrow = false;
|
||||
bool seenBreakOrContinue = false;
|
||||
|
||||
bool get aborts {
|
||||
return seenReturnOrThrow || seenBreakOrContinue;
|
||||
}
|
||||
|
||||
bool get inTryBlock => _tryBlock != null;
|
||||
|
||||
LocalsHandler.internal(ir.Node block, this._locals, this._tryBlock);
|
||||
|
||||
LocalsHandler(ir.Node block)
|
||||
: _locals = new VariableScope(block, isTry: false),
|
||||
_tryBlock = null;
|
||||
: _locals = new VariableScope(block, isTry: false);
|
||||
|
||||
LocalsHandler.from(LocalsHandler other, ir.Node block,
|
||||
{bool isTry: false, bool useOtherTryBlock: true})
|
||||
: _locals =
|
||||
new VariableScope(block, isTry: isTry, parent: other._locals) {
|
||||
_tryBlock = useOtherTryBlock ? other._tryBlock : this;
|
||||
}
|
||||
: _locals = new VariableScope(block, isTry: isTry, parent: other._locals);
|
||||
|
||||
LocalsHandler.deepCopyOf(LocalsHandler other)
|
||||
: _locals = new VariableScope.deepCopyOf(other._locals),
|
||||
_tryBlock = other._tryBlock;
|
||||
: _locals = new VariableScope.deepCopyOf(other._locals);
|
||||
|
||||
LocalsHandler.topLevelCopyOf(LocalsHandler other)
|
||||
: _locals = new VariableScope.topLevelCopyOf(other._locals),
|
||||
_tryBlock = other._tryBlock;
|
||||
|
||||
TypeInformation use(InferrerEngine inferrer,
|
||||
Map<Local, FieldEntity> capturedAndBoxed, Local local) {
|
||||
FieldEntity field = capturedAndBoxed[local];
|
||||
if (field != null) {
|
||||
return inferrer.typeOfMember(field);
|
||||
} else {
|
||||
return _locals[local];
|
||||
}
|
||||
TypeInformation use(InferrerEngine inferrer, Local local) {
|
||||
return _locals[local];
|
||||
}
|
||||
|
||||
void update(InferrerEngine inferrer, Map<Local, FieldEntity> capturedAndBoxed,
|
||||
Local local, TypeInformation type, ir.Node node, DartType staticType) {
|
||||
assert(type != null);
|
||||
type = inferrer.types.narrowType(type, staticType);
|
||||
|
||||
FieldEntity field = capturedAndBoxed[local];
|
||||
if (field != null) {
|
||||
inferrer.recordTypeOfField(field, type);
|
||||
} else if (inTryBlock) {
|
||||
void update(InferrerEngine inferrer, Local local, TypeInformation type,
|
||||
ir.Node node, DartType staticType, LocalsHandler tryBlock) {
|
||||
if (tryBlock != null) {
|
||||
// We don't know if an assignment in a try block
|
||||
// will be executed, so all assignments in that block are
|
||||
// potential types after we have left it. We update the parent
|
||||
// of the try block so that, at exit of the try block, we get
|
||||
// the right phi for it.
|
||||
TypeInformation existing = _tryBlock._locals.parent[local];
|
||||
TypeInformation existing = tryBlock._locals.parent[local];
|
||||
if (existing != null) {
|
||||
TypeInformation phiType = inferrer.types.allocatePhi(
|
||||
_tryBlock._locals.block, local, existing,
|
||||
isTry: _tryBlock._locals.isTry);
|
||||
tryBlock._locals.block, local, existing,
|
||||
isTry: tryBlock._locals.isTry);
|
||||
TypeInformation inputType =
|
||||
inferrer.types.addPhiInput(local, phiType, type);
|
||||
_tryBlock._locals.parent[local] = inputType;
|
||||
tryBlock._locals.parent[local] = inputType;
|
||||
}
|
||||
// Update the current handler unconditionally with the new
|
||||
// type.
|
||||
@@ -323,30 +291,21 @@ class LocalsHandler {
|
||||
}
|
||||
}
|
||||
|
||||
void narrow(InferrerEngine inferrer, Map<Local, FieldEntity> capturedAndBoxed,
|
||||
Local local, DartType type, ir.Node node) {
|
||||
TypeInformation existing = use(inferrer, capturedAndBoxed, local);
|
||||
TypeInformation newType =
|
||||
inferrer.types.narrowType(existing, type, isNullable: false);
|
||||
update(inferrer, capturedAndBoxed, local, newType, node, type);
|
||||
}
|
||||
|
||||
/// Returns the join between this locals handler and [other] which models the
|
||||
/// flow through either this or [other].
|
||||
LocalsHandler mergeFlow(InferrerEngine inferrer, LocalsHandler other) {
|
||||
seenReturnOrThrow = false;
|
||||
seenBreakOrContinue = false;
|
||||
|
||||
if (other.aborts) {
|
||||
return this;
|
||||
} else {
|
||||
other._locals.forEachOwnLocal((Local local, TypeInformation type) {
|
||||
TypeInformation myType = _locals[local];
|
||||
if (myType == null) return; // Variable is only defined in [other].
|
||||
if (type == myType) return;
|
||||
_locals[local] = inferrer.types.allocateDiamondPhi(myType, type);
|
||||
});
|
||||
}
|
||||
///
|
||||
/// If [inPlace] is `true`, the variable types in this locals handler are
|
||||
/// replaced by the variables types in [other]. Otherwise the variable types
|
||||
/// from both are merged with a phi type.
|
||||
LocalsHandler mergeFlow(InferrerEngine inferrer, LocalsHandler other,
|
||||
{bool inPlace: false}) {
|
||||
other._locals.forEachOwnLocal((Local local, TypeInformation type) {
|
||||
TypeInformation myType = _locals[local];
|
||||
if (myType == null) return; // Variable is only defined in [other].
|
||||
if (type == myType) return;
|
||||
_locals[local] =
|
||||
inPlace ? type : inferrer.types.allocateDiamondPhi(myType, type);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -355,48 +314,27 @@ class LocalsHandler {
|
||||
LocalsHandler mergeDiamondFlow(InferrerEngine inferrer,
|
||||
LocalsHandler thenBranch, LocalsHandler elseBranch) {
|
||||
assert(elseBranch != null);
|
||||
seenReturnOrThrow =
|
||||
thenBranch.seenReturnOrThrow && elseBranch.seenReturnOrThrow;
|
||||
seenBreakOrContinue =
|
||||
thenBranch.seenBreakOrContinue && elseBranch.seenBreakOrContinue;
|
||||
if (aborts) return this;
|
||||
|
||||
void inPlaceUpdateOneBranch(LocalsHandler other) {
|
||||
other._locals.forEachOwnLocal((Local local, TypeInformation type) {
|
||||
TypeInformation myType = _locals[local];
|
||||
if (myType == null) return; // Variable is only defined in [other].
|
||||
if (type == myType) return;
|
||||
_locals[local] = type;
|
||||
});
|
||||
}
|
||||
|
||||
if (thenBranch.aborts) {
|
||||
inPlaceUpdateOneBranch(elseBranch);
|
||||
} else if (elseBranch.aborts) {
|
||||
inPlaceUpdateOneBranch(thenBranch);
|
||||
} else {
|
||||
void mergeLocal(Local local) {
|
||||
TypeInformation myType = _locals[local];
|
||||
if (myType == null) return;
|
||||
TypeInformation elseType = elseBranch._locals[local];
|
||||
TypeInformation thenType = thenBranch._locals[local];
|
||||
if (thenType == elseType) {
|
||||
_locals[local] = thenType;
|
||||
} else {
|
||||
_locals[local] =
|
||||
inferrer.types.allocateDiamondPhi(thenType, elseType);
|
||||
}
|
||||
void mergeLocal(Local local) {
|
||||
TypeInformation myType = _locals[local];
|
||||
if (myType == null) return;
|
||||
TypeInformation elseType = elseBranch._locals[local];
|
||||
TypeInformation thenType = thenBranch._locals[local];
|
||||
if (thenType == elseType) {
|
||||
_locals[local] = thenType;
|
||||
} else {
|
||||
_locals[local] = inferrer.types.allocateDiamondPhi(thenType, elseType);
|
||||
}
|
||||
|
||||
thenBranch._locals.forEachOwnLocal((Local local, _) {
|
||||
mergeLocal(local);
|
||||
});
|
||||
elseBranch._locals.forEachOwnLocal((Local local, _) {
|
||||
// Discard locals we already processed when iterating over
|
||||
// [thenBranch]'s locals.
|
||||
if (!thenBranch._locals.updates(local)) mergeLocal(local);
|
||||
});
|
||||
}
|
||||
|
||||
thenBranch._locals.forEachOwnLocal((Local local, _) {
|
||||
mergeLocal(local);
|
||||
});
|
||||
elseBranch._locals.forEachOwnLocal((Local local, _) {
|
||||
// Discard locals we already processed when iterating over
|
||||
// [thenBranch]'s locals.
|
||||
if (!thenBranch._locals.updates(local)) mergeLocal(local);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -431,7 +369,7 @@ class LocalsHandler {
|
||||
* labeled statement that do not break out.
|
||||
*/
|
||||
LocalsHandler mergeAfterBreaks(
|
||||
InferrerEngine inferrer, List<LocalsHandler> handlers,
|
||||
InferrerEngine inferrer, Iterable<LocalsHandler> handlers,
|
||||
{bool keepOwnLocals: true}) {
|
||||
ir.Node level = _locals.block;
|
||||
// Use a separate locals handler to perform the merge in, so that Phi
|
||||
@@ -440,15 +378,13 @@ class LocalsHandler {
|
||||
LocalsHandler merged =
|
||||
new LocalsHandler.from(this, level, isTry: _locals.isTry);
|
||||
Set<Local> seenLocals = new Setlet<Local>();
|
||||
bool allBranchesAbort = true;
|
||||
// Merge all other handlers.
|
||||
for (LocalsHandler handler in handlers) {
|
||||
allBranchesAbort = allBranchesAbort && handler.seenReturnOrThrow;
|
||||
merged._mergeHandler(inferrer, handler, seenLocals);
|
||||
}
|
||||
// If we want to keep own locals, we merge [seenLocals] from [this] into
|
||||
// [merged] to update the Phi nodes with original values.
|
||||
if (keepOwnLocals && !seenReturnOrThrow) {
|
||||
if (keepOwnLocals) {
|
||||
for (Local variable in seenLocals) {
|
||||
TypeInformation originalType = _locals[variable];
|
||||
if (originalType != null) {
|
||||
@@ -462,8 +398,6 @@ class LocalsHandler {
|
||||
merged._locals.forEachOwnLocal((Local variable, TypeInformation type) {
|
||||
_locals[variable] = inferrer.types.simplifyPhi(level, variable, type);
|
||||
});
|
||||
seenReturnOrThrow =
|
||||
allBranchesAbort && (!keepOwnLocals || seenReturnOrThrow);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -475,7 +409,6 @@ class LocalsHandler {
|
||||
*/
|
||||
bool _mergeHandler(InferrerEngine inferrer, LocalsHandler other,
|
||||
[Set<Local> seen]) {
|
||||
if (other.seenReturnOrThrow) return false;
|
||||
bool changed = false;
|
||||
other._locals.forEachLocalUntilNode(_locals.block, (local, otherType) {
|
||||
TypeInformation myType = _locals[local];
|
||||
@@ -500,9 +433,8 @@ class LocalsHandler {
|
||||
* Merge all [LocalsHandler] in [handlers] into this handler.
|
||||
* Returns whether a local in this handler has changed.
|
||||
*/
|
||||
bool mergeAll(InferrerEngine inferrer, List<LocalsHandler> handlers) {
|
||||
bool mergeAll(InferrerEngine inferrer, Iterable<LocalsHandler> handlers) {
|
||||
bool changed = false;
|
||||
assert(!seenReturnOrThrow);
|
||||
handlers.forEach((other) {
|
||||
changed = _mergeHandler(inferrer, other) || changed;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user