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:
Johnni Winther
2018-11-29 07:33:18 +00:00
committed by commit-bot@chromium.org
parent be5d0cca5c
commit b52182ee14
2 changed files with 367 additions and 317 deletions
+321 -203
View File
@@ -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);
}
}
+46 -114
View File
@@ -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;
});