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 ]