Merge analyzer branch into master
Some work on the Dart analyzer was done on a branch, to avoid disrupting
others, and is landed with this commit. It is a merge, in order to
preserve the history of work on that branch.
diff --git a/DEPS b/DEPS
index f8de26e..f05a631 100644
--- a/DEPS
+++ b/DEPS
@@ -81,7 +81,7 @@
# For more details, see https://github.com/dart-lang/sdk/issues/30164
"dart_style_tag": "1.2.1", # Please see the note above before updating.
- "dartdoc_tag" : "v0.24.1",
+ "dartdoc_tag" : "v0.26.0",
"file_rev": "515ed1dd48740ab14b625de1be464cb2bca4fefd", # 5.0.6
"fixnum_tag": "0.10.9",
"func_rev": "25eec48146a58967d75330075ab376b3838b18a8",
diff --git a/pkg/compiler/lib/src/inferrer/builder_kernel.dart b/pkg/compiler/lib/src/inferrer/builder_kernel.dart
index aea3904..add6849 100644
--- a/pkg/compiler/lib/src/inferrer/builder_kernel.dart
+++ b/pkg/compiler/lib/src/inferrer/builder_kernel.dart
@@ -126,7 +126,7 @@
: <Local, FieldEntity>{} {
if (_state != null) return;
- _state = new LocalState.initial(_analyzedNode,
+ _state = new LocalState.initial(
inGenerativeConstructor: _inGenerativeConstructor);
}
@@ -479,7 +479,7 @@
handleCondition(node.condition);
LocalState afterConditionWhenTrue = _stateAfterWhenTrue;
LocalState afterConditionWhenFalse = _stateAfterWhenFalse;
- _state = new LocalState.childPath(afterConditionWhenFalse, node.message);
+ _state = new LocalState.childPath(afterConditionWhenFalse);
visit(node.message);
LocalState stateAfterMessage = _state;
stateAfterMessage.seenReturnOrThrow = true;
@@ -545,7 +545,7 @@
changed = false;
for (ir.SwitchCase switchCase in node.cases) {
LocalState stateBeforeCase = _state;
- _state = new LocalState.childPath(stateBeforeCase, switchCase);
+ _state = new LocalState.childPath(stateBeforeCase);
visit(switchCase);
LocalState stateAfterCase = _state;
changed =
@@ -565,7 +565,7 @@
if (switchCase.isDefault) {
hasDefaultCase = true;
}
- _state = new LocalState.childPath(stateBeforeCase, switchCase);
+ _state = new LocalState.childPath(stateBeforeCase);
visit(switchCase);
statesToMerge.add(_state);
}
@@ -1041,7 +1041,7 @@
// Setup (and clear in case of multiple iterations of the loop)
// the lists of breaks and continues seen in the loop.
_setupBreaksAndContinues(target);
- _state = new LocalState.childPath(stateBefore, node);
+ _state = new LocalState.childPath(stateBefore);
logic();
changed = stateBefore.mergeAll(_inferrer, _getLoopBackEdges(target));
} while (changed);
@@ -1346,10 +1346,8 @@
if (operand is ir.VariableGet) {
Local local = _localsMap.getLocalVariable(operand.variable);
DartType type = _elementMap.getDartType(node.type);
- LocalState stateAfterCheckWhenTrue =
- new LocalState.childPath(_state, node);
- LocalState stateAfterCheckWhenFalse =
- new LocalState.childPath(_state, node);
+ LocalState stateAfterCheckWhenTrue = new LocalState.childPath(_state);
+ LocalState stateAfterCheckWhenFalse = new LocalState.childPath(_state);
stateAfterCheckWhenTrue.narrowLocal(
_inferrer, _capturedAndBoxed, local, type, node);
_setStateAfter(_state, stateAfterCheckWhenTrue, stateAfterCheckWhenFalse);
@@ -1362,10 +1360,8 @@
if (receiver is ir.VariableGet) {
Local local = _localsMap.getLocalVariable(receiver.variable);
DartType localType = _localsMap.getLocalType(_elementMap, local);
- LocalState stateAfterCheckWhenTrue =
- new LocalState.childPath(_state, node);
- LocalState stateAfterCheckWhenFalse =
- new LocalState.childPath(_state, node);
+ LocalState stateAfterCheckWhenTrue = new LocalState.childPath(_state);
+ LocalState stateAfterCheckWhenFalse = new LocalState.childPath(_state);
stateAfterCheckWhenTrue.updateLocal(_inferrer, _capturedAndBoxed, local,
_types.nullType, node, localType);
stateAfterCheckWhenFalse.narrowLocal(_inferrer, _capturedAndBoxed, local,
@@ -1380,11 +1376,10 @@
handleCondition(node.condition);
LocalState stateAfterConditionWhenTrue = _stateAfterWhenTrue;
LocalState stateAfterConditionWhenFalse = _stateAfterWhenFalse;
- _state = new LocalState.childPath(stateAfterConditionWhenTrue, node.then);
+ _state = new LocalState.childPath(stateAfterConditionWhenTrue);
visit(node.then);
LocalState stateAfterThen = _state;
- _state =
- new LocalState.childPath(stateAfterConditionWhenFalse, node.otherwise);
+ _state = new LocalState.childPath(stateAfterConditionWhenFalse);
visit(node.otherwise);
LocalState stateAfterElse = _state;
_state =
@@ -1413,17 +1408,17 @@
TypeInformation visitLogicalExpression(ir.LogicalExpression node) {
if (node.operator == '&&') {
LocalState stateBefore = _state;
- _state = new LocalState.childPath(stateBefore, node.left);
+ _state = new LocalState.childPath(stateBefore);
handleCondition(node.left);
LocalState stateAfterLeftWhenTrue = _stateAfterWhenTrue;
LocalState stateAfterLeftWhenFalse = _stateAfterWhenFalse;
- _state = new LocalState.childPath(stateAfterLeftWhenTrue, node.right);
+ _state = new LocalState.childPath(stateAfterLeftWhenTrue);
handleCondition(node.right);
LocalState stateAfterRightWhenTrue = _stateAfterWhenTrue;
LocalState stateAfterRightWhenFalse = _stateAfterWhenFalse;
LocalState stateAfterWhenTrue = stateAfterRightWhenTrue;
- LocalState stateAfterWhenFalse =
- new LocalState.childPath(stateBefore, node).mergeDiamondFlow(
+ LocalState stateAfterWhenFalse = new LocalState.childPath(stateBefore)
+ .mergeDiamondFlow(
_inferrer, stateAfterLeftWhenFalse, stateAfterRightWhenFalse);
LocalState after = stateBefore.mergeDiamondFlow(
_inferrer, stateAfterWhenTrue, stateAfterWhenFalse);
@@ -1431,16 +1426,16 @@
return _types.boolType;
} else if (node.operator == '||') {
LocalState stateBefore = _state;
- _state = new LocalState.childPath(stateBefore, node.left);
+ _state = new LocalState.childPath(stateBefore);
handleCondition(node.left);
LocalState stateAfterLeftWhenTrue = _stateAfterWhenTrue;
LocalState stateAfterLeftWhenFalse = _stateAfterWhenFalse;
- _state = new LocalState.childPath(stateAfterLeftWhenFalse, node.right);
+ _state = new LocalState.childPath(stateAfterLeftWhenFalse);
handleCondition(node.right);
LocalState stateAfterRightWhenTrue = _stateAfterWhenTrue;
LocalState stateAfterRightWhenFalse = _stateAfterWhenFalse;
- LocalState stateAfterWhenTrue =
- new LocalState.childPath(stateBefore, node).mergeDiamondFlow(
+ LocalState stateAfterWhenTrue = new LocalState.childPath(stateBefore)
+ .mergeDiamondFlow(
_inferrer, stateAfterLeftWhenTrue, stateAfterRightWhenTrue);
LocalState stateAfterWhenFalse = stateAfterRightWhenFalse;
LocalState stateAfter = stateBefore.mergeDiamondFlow(
@@ -1459,10 +1454,10 @@
handleCondition(node.condition);
LocalState stateAfterWhenTrue = _stateAfterWhenTrue;
LocalState stateAfterWhenFalse = _stateAfterWhenFalse;
- _state = new LocalState.childPath(stateAfterWhenTrue, node.then);
+ _state = new LocalState.childPath(stateAfterWhenTrue);
TypeInformation firstType = visit(node.then);
LocalState stateAfterThen = _state;
- _state = new LocalState.childPath(stateAfterWhenFalse, node.otherwise);
+ _state = new LocalState.childPath(stateAfterWhenFalse);
TypeInformation secondType = visit(node.otherwise);
LocalState stateAfterElse = _state;
_state =
@@ -1514,7 +1509,7 @@
// 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.
- LocalState closureState = new LocalState.closure(_state, node);
+ LocalState closureState = new LocalState.closure(_state);
KernelTypeGraphBuilder visitor = new KernelTypeGraphBuilder(
_options,
_closedWorld,
@@ -1544,7 +1539,7 @@
visitWhileStatement(ir.WhileStatement node) {
return handleLoop(node, _localsMap.getJumpTargetForWhile(node), () {
handleCondition(node.condition);
- _state = new LocalState.childPath(_stateAfterWhenTrue, node.body);
+ _state = new LocalState.childPath(_stateAfterWhenTrue);
visit(node.body);
});
}
@@ -1569,7 +1564,7 @@
}
return handleLoop(node, _localsMap.getJumpTargetForFor(node), () {
handleCondition(node.condition);
- _state = new LocalState.childPath(_stateAfterWhenTrue, node.body);
+ _state = new LocalState.childPath(_stateAfterWhenTrue);
visit(node.body);
for (ir.Expression update in node.updates) {
visit(update);
@@ -1587,7 +1582,7 @@
_state = stateBefore.mergeFlow(_inferrer, stateAfterBody);
for (ir.Catch catchBlock in node.catches) {
LocalState stateBeforeCatch = _state;
- _state = new LocalState.childPath(stateBeforeCatch, catchBlock);
+ _state = new LocalState.childPath(stateBeforeCatch);
visit(catchBlock);
LocalState stateAfterCatch = _state;
_state = stateBeforeCatch.mergeFlow(_inferrer, stateAfterCatch);
@@ -1778,27 +1773,26 @@
bool seenBreakOrContinue = false;
LocalsHandler _tryBlock;
- LocalState.initial(ir.TreeNode node, {bool inGenerativeConstructor})
+ LocalState.initial({bool inGenerativeConstructor})
: this.internal(
- new LocalsHandler(node),
+ new LocalsHandler(),
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),
+ LocalState.childPath(LocalState other)
+ : this.internal(new LocalsHandler.from(other._locals),
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),
+ LocalState.closure(LocalState other)
+ : this.internal(new LocalsHandler.from(other._locals),
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);
+ LocalsHandler locals = new LocalsHandler.tryBlock(other._locals, node);
FieldInitializationScope fieldScope =
new FieldInitializationScope.from(other._fields);
LocalsHandler tryBlock = locals;
diff --git a/pkg/compiler/lib/src/inferrer/inferrer_engine.dart b/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
index 896f862..8045775 100644
--- a/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
+++ b/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
@@ -1278,7 +1278,8 @@
bool checkLoopPhiNode(ir.Node node) => true;
@override
- bool checkPhiNode(ir.Node node) => true;
+ bool checkPhiNode(ir.Node node) =>
+ node == null || node is ir.TryCatch || node is ir.TryFinally;
@override
void forEachParameter(FunctionEntity function, void f(Local parameter)) {
diff --git a/pkg/compiler/lib/src/inferrer/locals_handler.dart b/pkg/compiler/lib/src/inferrer/locals_handler.dart
index 7b58f76..cfe7f1c 100644
--- a/pkg/compiler/lib/src/inferrer/locals_handler.dart
+++ b/pkg/compiler/lib/src/inferrer/locals_handler.dart
@@ -8,6 +8,7 @@
import 'package:kernel/ast.dart' as ir;
import '../elements/entities.dart';
import '../elements/types.dart';
+import '../ir/util.dart';
import '../util/util.dart';
import 'inferrer_engine.dart';
import 'type_graph_nodes.dart';
@@ -20,41 +21,69 @@
* once the control flow block has been visited.
*/
class VariableScope {
+ /// The number of parent scopes of this scope.
+ ///
+ /// This is used for computing common parents efficiently.
+ final int _level;
+
Map<Local, TypeInformation> variables;
/// The parent of this scope. Null for the root scope.
final VariableScope parent;
/// The [ir.Node] that created this scope.
- final ir.Node block;
+ final ir.Node tryBlock;
- /// `true` if this scope is for a try block.
- final bool isTry;
+ final VariableScope copyOf;
- VariableScope(this.block, {VariableScope parent, this.isTry})
+ VariableScope({this.parent})
: this.variables = null,
- this.parent = parent {
- assert(isTry == (block is ir.TryCatch || block is ir.TryFinally),
- "Unexpected block $block for isTry=$isTry");
+ this.copyOf = null,
+ this.tryBlock = null,
+ _level = (parent?._level ?? -1) + 1;
+
+ VariableScope.tryBlock(this.tryBlock, {this.parent})
+ : this.variables = null,
+ this.copyOf = null,
+ _level = (parent?._level ?? -1) + 1 {
+ assert(tryBlock is ir.TryCatch || tryBlock is ir.TryFinally,
+ "Unexpected block $tryBlock for VariableScope.tryBlock");
}
VariableScope.deepCopyOf(VariableScope other)
: variables = other.variables == null
? null
: new Map<Local, TypeInformation>.from(other.variables),
- block = other.block,
- isTry = other.isTry,
+ tryBlock = other.tryBlock,
+ copyOf = other.copyOf ?? other,
+ _level = other._level,
parent = other.parent == null
? null
: new VariableScope.deepCopyOf(other.parent);
- VariableScope.topLevelCopyOf(VariableScope other)
- : variables = other.variables == null
- ? null
- : new Map<Local, TypeInformation>.from(other.variables),
- block = other.block,
- isTry = other.isTry,
- parent = other.parent;
+ /// `true` if this scope is for a try block.
+ bool get isTry => tryBlock != null;
+
+ /// Returns the [VariableScope] that defines the identity of this scope.
+ ///
+ /// If this scope is a copy of another scope, the identity is the identity
+ /// of the other scope, otherwise the identity is the scope itself.
+ VariableScope get identity => copyOf ?? this;
+
+ /// Returns the common parent between this and [other] based on [identity].
+ VariableScope commonParent(VariableScope other) {
+ if (identity == other.identity) {
+ return identity;
+ } else if (_level > other._level) {
+ return parent.commonParent(other);
+ } else if (_level < other._level) {
+ return commonParent(other.parent);
+ } else if (_level > 0) {
+ return parent.commonParent(other.parent);
+ } else {
+ return null;
+ }
+ }
TypeInformation operator [](Local variable) {
TypeInformation result;
@@ -72,15 +101,18 @@
variables[variable] = mask;
}
- void forEachOwnLocal(void f(Local variable, TypeInformation type)) {
- if (variables == null) return;
- variables.forEach(f);
+ /// Calls [f] for all variables in this and parent scopes until and including
+ /// [scope]. [f] is called at most once for each variable.
+ void forEachLocalUntilScope(
+ VariableScope scope, void f(Local variable, TypeInformation type)) {
+ _forEachLocalUntilScope(scope, f, new Setlet<Local>(), this);
}
- void forEachLocalUntilNode(
- ir.Node node, void f(Local variable, TypeInformation type),
- [Setlet<Local> seenLocals]) {
- if (seenLocals == null) seenLocals = new Setlet<Local>();
+ void _forEachLocalUntilScope(
+ VariableScope scope,
+ void f(Local variable, TypeInformation type),
+ Setlet<Local> seenLocals,
+ VariableScope origin) {
if (variables != null) {
variables.forEach((variable, type) {
if (seenLocals.contains(variable)) return;
@@ -88,12 +120,22 @@
f(variable, type);
});
}
- if (node != null && block == node) return;
- if (parent != null) parent.forEachLocalUntilNode(node, f, seenLocals);
+ if (scope?.identity == identity) {
+ return;
+ }
+ if (parent != null) {
+ parent._forEachLocalUntilScope(scope, f, seenLocals, origin);
+ } else {
+ assert(
+ scope == null,
+ "Scope not found: \n"
+ "origin=${origin.toStructuredText('')}\n"
+ "scope=${scope.toStructuredText('')}");
+ }
}
void forEachLocal(void f(Local variable, TypeInformation type)) {
- forEachLocalUntilNode(null, f);
+ forEachLocalUntilScope(null, f);
}
bool updates(Local variable) {
@@ -109,12 +151,13 @@
void _toStructuredText(StringBuffer sb, String indent) {
sb.write('VariableScope($hashCode) [');
- String blockText = block.toString().replaceAll('\n', ' ');
- if (blockText.length > 20) {
- blockText = blockText.substring(0, 17) + '...';
+ sb.write('\n${indent} level:$_level');
+ if (copyOf != null) {
+ sb.write('\n${indent} copyOf:VariableScope(${copyOf.hashCode})');
}
- sb.write('\n${indent} block: '
- '(${block.runtimeType}:${block.hashCode})${blockText}');
+ if (tryBlock != null) {
+ sb.write('\n${indent} tryBlock: ${nodeToDebugString(tryBlock)}');
+ }
if (variables != null) {
sb.write('\n${indent} variables:');
variables.forEach((Local local, TypeInformation type) {
@@ -280,11 +323,13 @@
class LocalsHandler {
final VariableScope _locals;
- LocalsHandler(ir.Node block)
- : _locals = new VariableScope(block, isTry: false);
+ LocalsHandler() : _locals = new VariableScope();
- LocalsHandler.from(LocalsHandler other, ir.Node block, {bool isTry: false})
- : _locals = new VariableScope(block, isTry: isTry, parent: other._locals);
+ LocalsHandler.from(LocalsHandler other)
+ : _locals = new VariableScope(parent: other._locals);
+
+ LocalsHandler.tryBlock(LocalsHandler other, ir.TreeNode block)
+ : _locals = new VariableScope.tryBlock(block, parent: other._locals);
LocalsHandler.deepCopyOf(LocalsHandler other)
: _locals = new VariableScope.deepCopyOf(other._locals);
@@ -304,7 +349,7 @@
TypeInformation existing = tryBlock._locals.parent[local];
if (existing != null) {
TypeInformation phiType = inferrer.types.allocatePhi(
- tryBlock._locals.block, local, existing,
+ tryBlock._locals.tryBlock, local, existing,
isTry: tryBlock._locals.isTry);
TypeInformation inputType =
inferrer.types.addPhiInput(local, phiType, type);
@@ -326,7 +371,18 @@
/// from both are merged with a phi type.
LocalsHandler mergeFlow(InferrerEngine inferrer, LocalsHandler other,
{bool inPlace: false}) {
- other._locals.forEachLocalUntilNode(_locals.block,
+ VariableScope common = _locals.commonParent(other._locals);
+ assert(
+ common != null,
+ "No common parent for\n"
+ "1:${_locals.toStructuredText(' ')}\n"
+ "2:${other._locals.toStructuredText(' ')}");
+ assert(
+ common == _locals || _locals.variables == null,
+ "Non-empty common parent for\n"
+ "1:${common.toStructuredText(' ')}\n"
+ "2:${_locals.toStructuredText(' ')}");
+ other._locals.forEachLocalUntilScope(common,
(Local local, TypeInformation type) {
TypeInformation myType = _locals[local];
if (myType == null) return; // Variable is only defined in [other].
@@ -355,10 +411,27 @@
}
}
- thenBranch._locals.forEachLocalUntilNode(_locals.block, (Local local, _) {
+ VariableScope common = _locals.commonParent(thenBranch._locals);
+ assert(
+ common != null,
+ "No common parent for\n"
+ "1:${_locals.toStructuredText(' ')}\n"
+ "2:${thenBranch._locals.toStructuredText(' ')}");
+ assert(
+ _locals.commonParent(elseBranch._locals) == common,
+ "Diff common parent for\n"
+ "1:${common.toStructuredText(' ')}\n2:"
+ "${_locals.commonParent(elseBranch._locals)?.toStructuredText(' ')}");
+ assert(
+ common == _locals || _locals.variables == null,
+ "Non-empty common parent for\n"
+ "common:${common.toStructuredText(' ')}\n"
+ "1:${_locals.toStructuredText(' ')}\n"
+ "2:${thenBranch._locals.toStructuredText(' ')}");
+ thenBranch._locals.forEachLocalUntilScope(common, (Local local, _) {
mergeLocal(local);
});
- elseBranch._locals.forEachLocalUntilNode(_locals.block, (Local local, _) {
+ elseBranch._locals.forEachLocalUntilScope(common, (Local local, _) {
// Discard locals we already processed when iterating over
// [thenBranch]'s locals.
if (!thenBranch._locals.updates(local)) mergeLocal(local);
@@ -399,16 +472,44 @@
LocalsHandler mergeAfterBreaks(
InferrerEngine inferrer, Iterable<LocalsHandler> handlers,
{bool keepOwnLocals: true}) {
- ir.Node level = _locals.block;
+ ir.Node tryBlock = _locals.tryBlock;
// Use a separate locals handler to perform the merge in, so that Phi
// creation does not invalidate previous type knowledge while we might
// still look it up.
- LocalsHandler merged =
- new LocalsHandler.from(this, level, isTry: _locals.isTry);
+ VariableScope merged = tryBlock != null
+ ? new VariableScope.tryBlock(tryBlock, parent: _locals)
+ : new VariableScope(parent: _locals);
Set<Local> seenLocals = new Setlet<Local>();
// Merge all other handlers.
for (LocalsHandler handler in handlers) {
- merged._mergeHandler(inferrer, handler, seenLocals);
+ VariableScope common = _locals.commonParent(handler._locals);
+ assert(
+ common != null,
+ "No common parent for\n"
+ "1:${_locals.toStructuredText(' ')}\n"
+ "2:${handler._locals.toStructuredText(' ')}");
+ assert(
+ common == _locals || _locals.variables == null,
+ "Non-empty common parent for\n"
+ "common:${common.toStructuredText(' ')}\n"
+ "1:${_locals.toStructuredText(' ')}\n"
+ "2:${handler._locals.toStructuredText(' ')}");
+ handler._locals.forEachLocalUntilScope(common, (local, otherType) {
+ TypeInformation myType = merged[local];
+ if (myType == null) return;
+ TypeInformation newType;
+ if (!seenLocals.contains(local)) {
+ newType = inferrer.types.allocatePhi(
+ merged.tryBlock, local, otherType,
+ isTry: merged.isTry);
+ seenLocals.add(local);
+ } else {
+ newType = inferrer.types.addPhiInput(local, myType, otherType);
+ }
+ if (newType != myType) {
+ merged[local] = newType;
+ }
+ });
}
// If we want to keep own locals, we merge [seenLocals] from [this] into
// [merged] to update the Phi nodes with original values.
@@ -416,56 +517,47 @@
for (Local variable in seenLocals) {
TypeInformation originalType = _locals[variable];
if (originalType != null) {
- merged._locals[variable] = inferrer.types
- .addPhiInput(variable, merged._locals[variable], originalType);
+ merged[variable] = inferrer.types
+ .addPhiInput(variable, merged[variable], originalType);
}
}
}
// Clean up Phi nodes with single input and store back result into
// actual locals handler.
- merged._locals.forEachLocalUntilNode(_locals.block,
+ merged.forEachLocalUntilScope(merged,
(Local variable, TypeInformation type) {
- _locals[variable] = inferrer.types.simplifyPhi(level, variable, type);
+ _locals[variable] = inferrer.types.simplifyPhi(tryBlock, variable, type);
});
return this;
}
- /**
- * Merge [other] into this handler. Returns whether a local in this
- * has changed. If [seen] is not null, we allocate new Phi nodes
- * unless the local is already present in the set [seen]. This effectively
- * overwrites the current type knowledge in this handler.
- */
- bool _mergeHandler(InferrerEngine inferrer, LocalsHandler other,
- [Set<Local> seen]) {
- bool changed = false;
- other._locals.forEachLocalUntilNode(_locals.block, (local, otherType) {
- TypeInformation myType = _locals[local];
- if (myType == null) return;
- TypeInformation newType;
- if (seen != null && !seen.contains(local)) {
- newType = inferrer.types
- .allocatePhi(_locals.block, local, otherType, isTry: _locals.isTry);
- seen.add(local);
- } else {
- newType = inferrer.types.addPhiInput(local, myType, otherType);
- }
- if (newType != myType) {
- changed = true;
- _locals[local] = newType;
- }
- });
- return changed;
- }
-
- /**
- * Merge all [LocalsHandler] in [handlers] into this handler.
- * Returns whether a local in this handler has changed.
- */
+ /// Merge all [LocalsHandler] in [handlers] into this handler.
+ /// Returns whether a local in this handler has changed.
bool mergeAll(InferrerEngine inferrer, Iterable<LocalsHandler> handlers) {
bool changed = false;
- handlers.forEach((other) {
- changed = _mergeHandler(inferrer, other) || changed;
+ handlers.forEach((LocalsHandler other) {
+ VariableScope common = _locals.commonParent(other._locals);
+ assert(
+ common != null,
+ "No common parent for\n"
+ "1:${_locals.toStructuredText(' ')}\n"
+ "2:${other._locals.toStructuredText(' ')}");
+ assert(
+ common == _locals || _locals.variables == null,
+ "Non-empty common parent for\n"
+ "common:${common.toStructuredText(' ')}\n"
+ "1:${_locals.toStructuredText(' ')}\n"
+ "2:${other._locals.toStructuredText(' ')}");
+ other._locals.forEachLocalUntilScope(common, (local, otherType) {
+ TypeInformation myType = _locals[local];
+ if (myType == null) return;
+ TypeInformation newType =
+ inferrer.types.addPhiInput(local, myType, otherType);
+ if (newType != myType) {
+ changed = true;
+ _locals[local] = newType;
+ }
+ });
});
return changed;
}
diff --git a/pkg/compiler/lib/src/ir/util.dart b/pkg/compiler/lib/src/ir/util.dart
index 8a4a0c4..fb6ba1e 100644
--- a/pkg/compiler/lib/src/ir/util.dart
+++ b/pkg/compiler/lib/src/ir/util.dart
@@ -10,6 +10,16 @@
import '../common.dart';
import '../elements/entities.dart';
+/// Returns a textual representation of [node] that include the runtime type and
+/// hash code of the node and a one line prefix of the node toString text.
+String nodeToDebugString(ir.Node node, [int textLength = 40]) {
+ String blockText = node.toString().replaceAll('\n', ' ');
+ if (blockText.length > textLength) {
+ blockText = blockText.substring(0, textLength - 3) + '...';
+ }
+ return '(${node.runtimeType}:${node.hashCode})${blockText}';
+}
+
/// Comparator for the canonical order or named arguments.
// TODO(johnniwinther): Remove this when named parameters are sorted in dill.
int namedOrdering(ir.VariableDeclaration a, ir.VariableDeclaration b) {
diff --git a/runtime/vm/class_finalizer.cc b/runtime/vm/class_finalizer.cc
index f578339..c633ce0 100644
--- a/runtime/vm/class_finalizer.cc
+++ b/runtime/vm/class_finalizer.cc
@@ -2245,6 +2245,9 @@
// Top level classes are always fully loaded.
if (!cls.IsTopLevel() && cls.kernel_offset() > 0) {
kernel::KernelLoader::FinishLoading(cls);
+ if (cls.is_finalized()) {
+ return;
+ }
}
#endif // !defined(DART_PRECOMPILED_RUNTIME)
diff --git a/runtime/vm/simulator_dbc.cc b/runtime/vm/simulator_dbc.cc
index 896379e..2c7f573 100644
--- a/runtime/vm/simulator_dbc.cc
+++ b/runtime/vm/simulator_dbc.cc
@@ -115,6 +115,10 @@
: static_cast<intptr_t>(kSmiCid);
}
+ DART_FORCE_INLINE static void IncrementUsageCounter(RawFunction* f) {
+ f->ptr()->usage_counter_++;
+ }
+
DART_FORCE_INLINE static void IncrementICUsageCount(RawObject** entries,
intptr_t offset,
intptr_t args_tested) {
@@ -1699,6 +1703,8 @@
RawObject** call_top = SP + 1;
RawICData* icdata = RAW_CAST(ICData, LOAD_CONSTANT(kidx));
+ SimulatorHelpers::IncrementUsageCounter(
+ RAW_CAST(Function, icdata->ptr()->owner_));
InstanceCall1(thread, icdata, call_base, call_top, &pc, &FP, &SP,
false /* optimized */);
}
@@ -1722,6 +1728,8 @@
RawObject** call_top = SP + 1;
RawICData* icdata = RAW_CAST(ICData, LOAD_CONSTANT(kidx));
+ SimulatorHelpers::IncrementUsageCounter(
+ RAW_CAST(Function, icdata->ptr()->owner_));
InstanceCall2(thread, icdata, call_base, call_top, &pc, &FP, &SP,
false /* optimized */);
}
@@ -1740,6 +1748,7 @@
RawObject** call_top = SP + 1;
RawICData* icdata = RAW_CAST(ICData, LOAD_CONSTANT(kidx));
+ SimulatorHelpers::IncrementUsageCounter(FrameFunction(FP));
InstanceCall1(thread, icdata, call_base, call_top, &pc, &FP, &SP,
true /* optimized */);
}
@@ -1758,6 +1767,7 @@
RawObject** call_top = SP + 1;
RawICData* icdata = RAW_CAST(ICData, LOAD_CONSTANT(kidx));
+ SimulatorHelpers::IncrementUsageCounter(FrameFunction(FP));
InstanceCall2(thread, icdata, call_base, call_top, &pc, &FP, &SP,
true /* optimized */);
}
diff --git a/tests/co19_2/co19_2-kernel.status b/tests/co19_2/co19_2-kernel.status
index 380c44a..5e23d5f 100644
--- a/tests/co19_2/co19_2-kernel.status
+++ b/tests/co19_2/co19_2-kernel.status
@@ -8,6 +8,7 @@
LibTest/core/List/List_class_A01_t02: Pass, Slow # Does many calls
LibTest/io/FileSystemCreateEvent/isDirectory_A01_t06: Pass, RuntimeError # https://github.com/dart-lang/co19/issues/186
LibTest/io/RawDatagramSocket/close_A01_t01: Pass, RuntimeError # https://github.com/dart-lang/co19/issues/195
+LibTest/io/RawDatagramSocket/every_A01_t02: Pass, RuntimeError # https://github.com/dart-lang/co19/issues/195
LibTest/io/RawDatagramSocket/singleWhere_A03_t01: Pass, RuntimeError # https://github.com/dart-lang/co19/issues/195
[ $compiler == dartkp ]