Version 2.18.0-275.0.dev
Merge commit '33295f8247d4dfc259f1a275598434316db34a16' into 'dev'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cfad1f4..acadd9a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -57,6 +57,27 @@
collected earlier than they were before
(see issue [#36983](https://github.com/dart-lang/sdk/issues/36983) for details).
+
+### Tools
+
+#### Dart command line
+
+#### Linter
+
+Updated the Linter to `1.26.0`, which includes changes that
+
+- add new lint: `combinators_ordering`.
+- fix `use_colored_box` and `use_decorated_box` to not over-report on containers without
+ a child.
+- fix `unnecessary_parenthesis` false positives on a map-or-set literal at the start of
+ an expression statement.
+- fix `prefer_final_locals` false positives reporting on fields.
+- fix `unnecessary_overrides` to allow overrides on `@Protected`members.
+- fix `avoid_multiple_declarations_per_line` false positives in `for` statements.
+- fix `prefer_final_locals` false positives on declaration lists with at least one
+ non-final variable.
+- fix`use_build_context_synchronously` to handle `await`s in `if` conditions.
+
## 2.18.0
### Language
diff --git a/DEPS b/DEPS
index 4a6f2e9..f7fc977 100644
--- a/DEPS
+++ b/DEPS
@@ -122,7 +122,7 @@
"intl_rev": "e9b573679de5e703d89a242b9dca331c772979ef",
"jinja2_rev": "2222b31554f03e62600cd7e383376a7c187967a1",
"json_rpc_2_rev": "805e6536dd961d66f6b8cd46d8f3e61774f957c9",
- "linter_rev": "1ddc70948d94f2449fec69a95e3ceb7b6b6c8348", # manually rev'd
+ "linter_rev": "b4afc10055f3009478da1eac1827f9fef42c3759", # 1.26.0
"lints_rev": "8294e5648ab49474541527e2911e72e4c5aefe55",
"logging_rev": "f6979e3bc3b6e1847a08335b7eb6304e18986195",
"markdown_rev": "e3f4bd28c9e61b522f75f291d4d6cfcfeccd83ee", # b/236358256
diff --git a/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart b/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
index fbcedf8..ced5a9b 100644
--- a/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
@@ -26,27 +26,29 @@
/// closure.
class AssignedVariables<Node extends Object, Variable extends Object> {
/// Mapping from a node to the info for that node.
- final Map<Node, AssignedVariablesNodeInfo<Variable>> _info =
- new Map<Node, AssignedVariablesNodeInfo<Variable>>.identity();
+ final Map<Node, AssignedVariablesNodeInfo> _info =
+ new Map<Node, AssignedVariablesNodeInfo>.identity();
/// Info for the variables written or captured anywhere in the code being
/// analyzed.
- final AssignedVariablesNodeInfo<Variable> _anywhere =
- new AssignedVariablesNodeInfo<Variable>();
+ final AssignedVariablesNodeInfo _anywhere = new AssignedVariablesNodeInfo();
/// Stack of info for nodes that have been entered but not yet left.
- final List<AssignedVariablesNodeInfo<Variable>> _stack = [
- new AssignedVariablesNodeInfo<Variable>()
+ final List<AssignedVariablesNodeInfo> _stack = [
+ new AssignedVariablesNodeInfo()
];
/// When assertions are enabled, the set of info objects that have been
/// retrieved by [deferNode] but not yet sent to [storeNode].
- final Set<AssignedVariablesNodeInfo<Variable>> _deferredInfos =
- new Set<AssignedVariablesNodeInfo<Variable>>.identity();
+ final Set<AssignedVariablesNodeInfo> _deferredInfos =
+ new Set<AssignedVariablesNodeInfo>.identity();
/// Keeps track of whether [finish] has been called.
bool _isFinished = false;
+ final PromotionKeyStore<Variable> _promotionKeyStore =
+ new PromotionKeyStore<Variable>();
+
/// This method should be called during pre-traversal, to mark the start of a
/// loop statement, switch statement, try statement, loop collection element,
/// local function, closure, or late variable initializer which might need to
@@ -60,7 +62,7 @@
/// switch expression should not.
void beginNode() {
assert(!_isFinished);
- _stack.add(new AssignedVariablesNodeInfo<Variable>());
+ _stack.add(new AssignedVariablesNodeInfo());
}
/// This method should be called during pre-traversal, to indicate that the
@@ -70,8 +72,9 @@
/// is to allow for error recovery in the analyzer).
void declare(Variable variable) {
assert(!_isFinished);
- _stack.last._declared.add(variable);
- _anywhere._declared.add(variable);
+ int variableKey = _promotionKeyStore.keyForVariable(variable);
+ _stack.last._declared.add(variableKey);
+ _anywhere._declared.add(variableKey);
}
/// This method may be called during pre-traversal, to mark the end of a
@@ -87,15 +90,15 @@
/// time, the caller should pass the returned data to [storeNodeInfo].
///
/// See [beginNode] for more details.
- AssignedVariablesNodeInfo<Variable> deferNode(
+ AssignedVariablesNodeInfo deferNode(
{bool isClosureOrLateVariableInitializer: false}) {
assert(!_isFinished);
- AssignedVariablesNodeInfo<Variable> info = _stack.removeLast();
+ AssignedVariablesNodeInfo info = _stack.removeLast();
info._read.removeAll(info._declared);
info._written.removeAll(info._declared);
info._readCaptured.removeAll(info._declared);
info._captured.removeAll(info._declared);
- AssignedVariablesNodeInfo<Variable> last = _stack.last;
+ AssignedVariablesNodeInfo last = _stack.last;
last._read.addAll(info._read);
last._written.addAll(info._written);
last._readCaptured.addAll(info._readCaptured);
@@ -124,8 +127,8 @@
/// calls.
void discardNode() {
assert(!_isFinished);
- AssignedVariablesNodeInfo<Variable> discarded = _stack.removeLast();
- AssignedVariablesNodeInfo<Variable> last = _stack.last;
+ AssignedVariablesNodeInfo discarded = _stack.removeLast();
+ AssignedVariablesNodeInfo last = _stack.last;
last._declared.addAll(discarded._declared);
last._read.addAll(discarded._read);
last._written.addAll(discarded._written);
@@ -161,15 +164,14 @@
assert(
_deferredInfos.isEmpty, "Deferred infos not stored: $_deferredInfos");
assert(_stack.length == 1, "Unexpected stack: $_stack");
- AssignedVariablesNodeInfo<Variable> last = _stack.last;
- Set<Variable> undeclaredReads = last._read.difference(last._declared);
+ AssignedVariablesNodeInfo last = _stack.last;
+ Set<int> undeclaredReads = last._read.difference(last._declared);
assert(undeclaredReads.isEmpty,
'Variables read from but not declared: $undeclaredReads');
- Set<Variable> undeclaredWrites = last._written.difference(last._declared);
+ Set<int> undeclaredWrites = last._written.difference(last._declared);
assert(undeclaredWrites.isEmpty,
'Variables written to but not declared: $undeclaredWrites');
- Set<Variable> undeclaredCaptures =
- last._captured.difference(last._declared);
+ Set<int> undeclaredCaptures = last._captured.difference(last._declared);
assert(undeclaredCaptures.isEmpty,
'Variables captured but not declared: $undeclaredCaptures');
return true;
@@ -184,21 +186,22 @@
/// This is used by the front end when building for-elements in lists, maps,
/// and sets; their initializers are partially built after building their
/// loop conditions but before completely building their bodies.
- AssignedVariablesNodeInfo<Variable> popNode() {
+ AssignedVariablesNodeInfo popNode() {
assert(!_isFinished);
return _stack.removeLast();
}
/// Call this method to un-do the effect of [popNode].
- void pushNode(AssignedVariablesNodeInfo<Variable> node) {
+ void pushNode(AssignedVariablesNodeInfo node) {
assert(!_isFinished);
_stack.add(node);
}
void read(Variable variable) {
assert(!_isFinished);
- _stack.last._read.add(variable);
- _anywhere._read.add(variable);
+ int variableKey = _promotionKeyStore.keyForVariable(variable);
+ _stack.last._read.add(variableKey);
+ _anywhere._read.add(variableKey);
}
/// Call this method to register that the node [from] for which information
@@ -207,7 +210,7 @@
// general elements in the front-end.
void reassignInfo(Node from, Node to) {
assert(!_info.containsKey(to), "Node $to already has info: ${_info[to]}");
- AssignedVariablesNodeInfo<Variable>? info = _info.remove(from);
+ AssignedVariablesNodeInfo? info = _info.remove(from);
assert(
info != null,
'No information for $from (${from.hashCode}) in '
@@ -218,7 +221,7 @@
/// This method may be called at any time between a call to [deferNode] and
/// the call to [finish], to store assigned variable info for the node.
- void storeInfo(Node node, AssignedVariablesNodeInfo<Variable> info) {
+ void storeInfo(Node node, AssignedVariablesNodeInfo info) {
assert(!_isFinished);
// Caller should not try to store the same piece of info more than once.
assert(_deferredInfos.remove(info));
@@ -237,12 +240,13 @@
/// variable.
void write(Variable variable) {
assert(!_isFinished);
- _stack.last._written.add(variable);
- _anywhere._written.add(variable);
+ int variableKey = _promotionKeyStore.keyForVariable(variable);
+ _stack.last._written.add(variableKey);
+ _anywhere._written.add(variableKey);
}
/// Queries the information stored for the given [node].
- AssignedVariablesNodeInfo<Variable> _getInfoForNode(Node node) {
+ AssignedVariablesNodeInfo _getInfoForNode(Node node) {
return _info[node] ??
(throw new StateError('No information for $node (${node.hashCode}) in '
'{${_info.keys.map((k) => '$k (${k.hashCode})').join(',')}}'));
@@ -260,26 +264,28 @@
/// Not intended to be used by clients of flow analysis.
class AssignedVariablesForTesting<Node extends Object, Variable extends Object>
extends AssignedVariables<Node, Variable> {
- Set<Variable> get capturedAnywhere => _anywhere._captured;
+ Set<int> get capturedAnywhere => _anywhere._captured;
- Set<Variable> get declaredAtTopLevel => _stack.first._declared;
+ Set<int> get declaredAtTopLevel => _stack.first._declared;
- Set<Variable> get readAnywhere => _anywhere._read;
+ Set<int> get readAnywhere => _anywhere._read;
- Set<Variable> get readCapturedAnywhere => _anywhere._readCaptured;
+ Set<int> get readCapturedAnywhere => _anywhere._readCaptured;
- Set<Variable> get writtenAnywhere => _anywhere._written;
+ Set<int> get writtenAnywhere => _anywhere._written;
- Set<Variable> capturedInNode(Node node) => _getInfoForNode(node)._captured;
+ Set<int> capturedInNode(Node node) => _getInfoForNode(node)._captured;
- Set<Variable> declaredInNode(Node node) => _getInfoForNode(node)._declared;
+ Set<int> declaredInNode(Node node) => _getInfoForNode(node)._declared;
bool isTracked(Node node) => _info.containsKey(node);
- Set<Variable> readCapturedInNode(Node node) =>
- _getInfoForNode(node)._readCaptured;
+ int keyForVariable(Variable variable) =>
+ _promotionKeyStore.keyForVariable(variable);
- Set<Variable> readInNode(Node node) => _getInfoForNode(node)._read;
+ Set<int> readCapturedInNode(Node node) => _getInfoForNode(node)._readCaptured;
+
+ Set<int> readInNode(Node node) => _getInfoForNode(node)._read;
String toString() {
StringBuffer sb = new StringBuffer();
@@ -289,24 +295,26 @@
return sb.toString();
}
- Set<Variable> writtenInNode(Node node) => _getInfoForNode(node)._written;
+ Variable variableForKey(int key) => _promotionKeyStore.variableForKey(key);
+
+ Set<int> writtenInNode(Node node) => _getInfoForNode(node)._written;
}
/// Information tracked by [AssignedVariables] for a single node.
-class AssignedVariablesNodeInfo<Variable extends Object> {
- final Set<Variable> _read = new Set<Variable>.identity();
+class AssignedVariablesNodeInfo {
+ final Set<int> _read = {};
/// The set of local variables that are potentially written in the node.
- final Set<Variable> _written = new Set<Variable>.identity();
+ final Set<int> _written = {};
- final Set<Variable> _readCaptured = new Set<Variable>.identity();
+ final Set<int> _readCaptured = {};
/// The set of local variables for which a potential write is captured by a
/// local function or closure inside the node.
- final Set<Variable> _captured = new Set<Variable>.identity();
+ final Set<int> _captured = {};
/// The set of local variables that are declared in the node.
- final Set<Variable> _declared = new Set<Variable>.identity();
+ final Set<int> _declared = {};
String toString() =>
'AssignedVariablesNodeInfo(_written=$_written, _captured=$_captured, '
@@ -346,17 +354,17 @@
/// Information gathered by flow analysis about an argument to either
/// `identical` or `operator ==`.
-class EqualityInfo<Variable extends Object, Type extends Object> {
+class EqualityInfo<Type extends Object> {
/// The [ExpressionInfo] for the expression. This is used to determine
/// whether the expression is a `null` literal.
- final ExpressionInfo<Variable, Type>? _expressionInfo;
+ final ExpressionInfo<Type>? _expressionInfo;
/// The type of the expression on the LHS of `==` or `!=`.
final Type _type;
/// If the LHS of `==` or `!=` is a reference, the thing being referred to.
/// Otherwise `null`.
- final ReferenceWithType<Variable, Type>? _reference;
+ final ReferenceWithType<Type>? _reference;
EqualityInfo._(this._expressionInfo, this._type, this._reference);
@@ -368,27 +376,26 @@
/// A collection of flow models representing the possible outcomes of evaluating
/// an expression that are relevant to flow analysis.
-class ExpressionInfo<Variable extends Object, Type extends Object> {
+class ExpressionInfo<Type extends Object> {
/// The state after the expression evaluates, if we don't care what it
/// evaluates to.
- final FlowModel<Variable, Type> after;
+ final FlowModel<Type> after;
/// The state after the expression evaluates, if it evaluates to `true`.
- final FlowModel<Variable, Type> ifTrue;
+ final FlowModel<Type> ifTrue;
/// The state after the expression evaluates, if it evaluates to `false`.
- final FlowModel<Variable, Type> ifFalse;
+ final FlowModel<Type> ifFalse;
ExpressionInfo(this.after, this.ifTrue, this.ifFalse);
/// Computes a new [ExpressionInfo] based on this one, but with the roles of
/// [ifTrue] and [ifFalse] reversed.
- ExpressionInfo<Variable, Type> invert() =>
- new ExpressionInfo<Variable, Type>(after, ifFalse, ifTrue);
+ ExpressionInfo<Type> invert() =>
+ new ExpressionInfo<Type>(after, ifFalse, ifTrue);
- ExpressionInfo<Variable, Type>? rebaseForward(
- TypeOperations<Type> typeOperations,
- FlowModel<Variable, Type> base) =>
+ ExpressionInfo<Type>? rebaseForward(
+ TypeOperations<Type> typeOperations, FlowModel<Type> base) =>
new ExpressionInfo(base, ifTrue.rebaseForward(typeOperations, base),
ifFalse.rebaseForward(typeOperations, base));
@@ -509,25 +516,22 @@
///
/// Note: the return type is nullable because legacy type promotion doesn't
/// need to record information about equality operands.
- EqualityInfo<Variable, Type>? equalityOperand_end(
- Expression operand, Type type);
+ EqualityInfo<Type>? equalityOperand_end(Expression operand, Type type);
/// Call this method just after visiting the operands of a binary `==` or `!=`
/// expression, or an invocation of `identical`.
///
/// [leftOperandInfo] and [rightOperandInfo] should be the values returned by
/// [equalityOperand_end].
- void equalityOperation_end(
- Expression wholeExpression,
- EqualityInfo<Variable, Type>? leftOperandInfo,
- EqualityInfo<Variable, Type>? rightOperandInfo,
+ void equalityOperation_end(Expression wholeExpression,
+ EqualityInfo<Type>? leftOperandInfo, EqualityInfo<Type>? rightOperandInfo,
{bool notEqual = false});
/// Retrieves the [ExpressionInfo] associated with [target], if known. Will
/// return `null` if (a) no info is associated with [target], or (b) another
/// expression with info has been visited more recently than [target]. For
/// testing only.
- ExpressionInfo<Variable, Type>? expressionInfoForTesting(Expression target);
+ ExpressionInfo<Type>? expressionInfoForTesting(Expression target);
/// This method should be called at the conclusion of flow analysis for a top
/// level function or method. Performs assertion checks.
@@ -801,7 +805,7 @@
/// is not associated with an SSA node because it is write captured. For
/// testing only.
@visibleForTesting
- SsaNode<Variable, Type>? ssaNodeForTesting(Variable variable);
+ SsaNode<Type>? ssaNodeForTesting(Variable variable);
/// Call this method just before visiting one of the cases in the body of a
/// switch statement. See [switchStatement_expressionEnd] for details.
@@ -1144,17 +1148,14 @@
}
@override
- EqualityInfo<Variable, Type>? equalityOperand_end(
- Expression operand, Type type) =>
+ EqualityInfo<Type>? equalityOperand_end(Expression operand, Type type) =>
_wrap('equalityOperand_end($operand, $type)',
() => _wrapped.equalityOperand_end(operand, type),
isQuery: true);
@override
- void equalityOperation_end(
- Expression wholeExpression,
- EqualityInfo<Variable, Type>? leftOperandInfo,
- EqualityInfo<Variable, Type>? rightOperandInfo,
+ void equalityOperation_end(Expression wholeExpression,
+ EqualityInfo<Type>? leftOperandInfo, EqualityInfo<Type>? rightOperandInfo,
{bool notEqual = false}) {
_wrap(
'equalityOperation_end($wholeExpression, $leftOperandInfo, '
@@ -1165,7 +1166,7 @@
}
@override
- ExpressionInfo<Variable, Type>? expressionInfoForTesting(Expression target) {
+ ExpressionInfo<Type>? expressionInfoForTesting(Expression target) {
return _wrap('expressionInfoForTesting($target)',
() => _wrapped.expressionInfoForTesting(target),
isQuery: true);
@@ -1423,7 +1424,7 @@
}
@override
- SsaNode<Variable, Type>? ssaNodeForTesting(Variable variable) {
+ SsaNode<Type>? ssaNodeForTesting(Variable variable) {
return _wrap('ssaNodeForTesting($variable)',
() => _wrapped.ssaNodeForTesting(variable),
isQuery: true);
@@ -1617,10 +1618,11 @@
/// Instances of this class are immutable, so the methods below that "update"
/// the state actually leave `this` unchanged and return a new state object.
@visibleForTesting
-class FlowModel<Variable extends Object, Type extends Object> {
+class FlowModel<Type extends Object> {
final Reachability reachable;
- /// For each variable being tracked by flow analysis, the variable's model.
+ /// For each promotable thing being tracked by flow analysis, the
+ /// corresponding model.
///
/// Flow analysis has no awareness of scope, so variables that are out of
/// scope are retained in the map until such time as their declaration no
@@ -1632,13 +1634,12 @@
/// analysis results for error-free code, because it is an error to refer to a
/// variable that is no longer in scope.
///
- /// `null` is allowed as a special key; it represents the pseudo-variable
- /// `this`. (This is needed so that we can explain why `this` is not
- /// promoted, and why properties of `this` are not promoted).
- final Map<Variable?, VariableModel<Variable, Type> /*!*/ > variableInfo;
+ /// Keys are the unique integers assigned by
+ /// [_FlowAnalysisImpl._promotionKeyStore].
+ final Map<int, VariableModel<Type> /*!*/ > variableInfo;
/// The empty map, used to [join] variables.
- final Map<Variable?, VariableModel<Variable, Type>> _emptyVariableMap = {};
+ final Map<int, VariableModel<Type>> _emptyVariableMap = {};
/// Creates a state object with the given [reachable] status. All variables
/// are assumed to be unpromoted and already assigned, so joining another
@@ -1654,7 +1655,7 @@
// ignore:unnecessary_null_comparison
assert(reachable != null);
assert(() {
- for (VariableModel<Variable, Type> value in variableInfo.values) {
+ for (VariableModel<Type> value in variableInfo.values) {
// ignore:unnecessary_null_comparison
assert(value != null);
}
@@ -1678,27 +1679,24 @@
/// an exception, so we want to reinstate the results of any promotions and
/// assignments that occurred during the `try` block, to the extent that they
/// weren't invalidated by later assignments in the `finally` block.
- FlowModel<Variable, Type> attachFinally(
- TypeOperations<Type> typeOperations,
- FlowModel<Variable, Type> beforeFinally,
- FlowModel<Variable, Type> afterFinally) {
+ FlowModel<Type> attachFinally(TypeOperations<Type> typeOperations,
+ FlowModel<Type> beforeFinally, FlowModel<Type> afterFinally) {
// Code that follows the `try/finally` is reachable iff the end of the `try`
// block is reachable _and_ the end of the `finally` block is reachable.
Reachability newReachable = afterFinally.reachable.rebaseForward(reachable);
// Consider each variable that is common to all three models.
- Map<Variable?, VariableModel<Variable, Type>> newVariableInfo =
- <Variable?, VariableModel<Variable, Type>>{};
+ Map<int, VariableModel<Type>> newVariableInfo =
+ <int, VariableModel<Type>>{};
bool variableInfoMatchesThis = true;
bool variableInfoMatchesAfterFinally = true;
- for (MapEntry<Variable?, VariableModel<Variable, Type>> entry
- in variableInfo.entries) {
- Variable? variable = entry.key;
- VariableModel<Variable, Type> thisModel = entry.value;
- VariableModel<Variable, Type>? beforeFinallyModel =
- beforeFinally.variableInfo[variable];
- VariableModel<Variable, Type>? afterFinallyModel =
- afterFinally.variableInfo[variable];
+ for (MapEntry<int, VariableModel<Type>> entry in variableInfo.entries) {
+ int promotionKey = entry.key;
+ VariableModel<Type> thisModel = entry.value;
+ VariableModel<Type>? beforeFinallyModel =
+ beforeFinally.variableInfo[promotionKey];
+ VariableModel<Type>? afterFinallyModel =
+ afterFinally.variableInfo[promotionKey];
if (beforeFinallyModel == null || afterFinallyModel == null) {
// The variable is in `this` model but not in one of the `finally`
// models. This happens when the variable is declared inside the `try`
@@ -1711,7 +1709,7 @@
// because any write captures in the `try` block are conservatively
// considered to take effect in the `finally` block too.
List<Type>? newPromotedTypes;
- SsaNode<Variable, Type>? newSsaNode;
+ SsaNode<Type>? newSsaNode;
if (beforeFinallyModel.ssaNode == afterFinallyModel.ssaNode) {
// The finally clause doesn't write to the variable, so we want to keep
// all promotions that were done to it in both the try and finally
@@ -1737,7 +1735,7 @@
// The `finally` block inherited the "unassigned" state from the `try`
// block so we can just inherit from it.
bool newUnassigned = afterFinallyModel.unassigned;
- VariableModel<Variable, Type> newModel = VariableModel._identicalOrNew(
+ VariableModel<Type> newModel = VariableModel._identicalOrNew(
thisModel,
afterFinallyModel,
newPromotedTypes,
@@ -1745,7 +1743,7 @@
newAssigned,
newUnassigned,
newSsaNode);
- newVariableInfo[variable] = newModel;
+ newVariableInfo[promotionKey] = newModel;
if (!identical(newModel, thisModel)) variableInfoMatchesThis = false;
if (!identical(newModel, afterFinallyModel)) {
variableInfoMatchesAfterFinally = false;
@@ -1756,8 +1754,8 @@
// erroneously think that `newVariableInfo` matches `afterFinally`. If so,
// correct that.
if (variableInfoMatchesAfterFinally) {
- for (Variable? variable in afterFinally.variableInfo.keys) {
- if (!variableInfo.containsKey(variable)) {
+ for (int promotionKey in afterFinally.variableInfo.keys) {
+ if (!variableInfo.containsKey(promotionKey)) {
variableInfoMatchesAfterFinally = false;
break;
}
@@ -1796,55 +1794,55 @@
/// and only remove promotions if it can be shown that they aren't restored
/// later in the loop body. If we switch to a fixed point analysis, we should
/// be able to remove this method.
- FlowModel<Variable, Type> conservativeJoin(
- Iterable<Variable> writtenVariables,
- Iterable<Variable> capturedVariables) {
- Map<Variable?, VariableModel<Variable, Type>>? newVariableInfo;
+ FlowModel<Type> conservativeJoin(
+ Iterable<int> writtenVariables, Iterable<int> capturedVariables) {
+ Map<int, VariableModel<Type>>? newVariableInfo;
- for (Variable variable in writtenVariables) {
- VariableModel<Variable, Type>? info = variableInfo[variable];
+ for (int variableKey in writtenVariables) {
+ VariableModel<Type>? info = variableInfo[variableKey];
if (info == null) continue;
- VariableModel<Variable, Type> newInfo =
+ VariableModel<Type> newInfo =
info.discardPromotionsAndMarkNotUnassigned();
if (!identical(info, newInfo)) {
- (newVariableInfo ??=
- new Map<Variable?, VariableModel<Variable, Type>>.of(
- variableInfo))[variable] = newInfo;
+ (newVariableInfo ??= new Map<int, VariableModel<Type>>.of(
+ variableInfo))[variableKey] = newInfo;
}
}
- for (Variable variable in capturedVariables) {
- VariableModel<Variable, Type>? info = variableInfo[variable];
+ for (int variableKey in capturedVariables) {
+ VariableModel<Type>? info = variableInfo[variableKey];
if (info == null) continue;
if (!info.writeCaptured) {
- (newVariableInfo ??=
- new Map<Variable?, VariableModel<Variable, Type>>.of(
- variableInfo))[variable] = info.writeCapture();
+ (newVariableInfo ??= new Map<int, VariableModel<Type>>.of(
+ variableInfo))[variableKey] = info.writeCapture();
}
}
- FlowModel<Variable, Type> result = newVariableInfo == null
+ FlowModel<Type> result = newVariableInfo == null
? this
- : new FlowModel<Variable, Type>.withInfo(reachable, newVariableInfo);
+ : new FlowModel<Type>.withInfo(reachable, newVariableInfo);
return result;
}
- /// Register a declaration of the [variable].
+ /// Register a declaration of the variable whose key is [variableKey].
/// Should also be called for function parameters.
///
/// A local variable is [initialized] if its declaration has an initializer.
/// A function parameter is always initialized, so [initialized] is `true`.
- FlowModel<Variable, Type> declare(Variable variable, bool initialized) {
- VariableModel<Variable, Type> newInfoForVar =
+ FlowModel<Type> declare<Variable extends Object>(
+ Variable variable, int variableKey, bool initialized) {
+ VariableModel<Type> newInfoForVar =
new VariableModel.fresh(assigned: initialized);
- return _updateVariableInfo(new VariableReference(variable), newInfoForVar);
+ return _updateVariableInfo(
+ new VariableReference(variable, variableKey), newInfoForVar);
}
- /// Gets the info for the given [variable], creating it if it doesn't exist.
- VariableModel<Variable, Type> infoFor(Variable variable) =>
- variableInfo[variable] ?? new VariableModel.fresh();
+ /// Gets the info for the given [promotionKey], creating it if it doesn't
+ /// exist.
+ VariableModel<Type> infoFor(int promotionKey) =>
+ variableInfo[promotionKey] ?? new VariableModel.fresh();
/// Builds a [FlowModel] based on `this`, but extending the `tested` set to
/// include types from [other]. This is used at the bottom of certain kinds
@@ -1852,29 +1850,25 @@
/// consistently treated as "of interest" in code that follows the loop,
/// regardless of the type of loop.
@visibleForTesting
- FlowModel<Variable, Type> inheritTested(
- TypeOperations<Type> typeOperations, FlowModel<Variable, Type> other) {
- Map<Variable?, VariableModel<Variable, Type>> newVariableInfo =
- <Variable?, VariableModel<Variable, Type>>{};
- Map<Variable?, VariableModel<Variable, Type>> otherVariableInfo =
- other.variableInfo;
+ FlowModel<Type> inheritTested(
+ TypeOperations<Type> typeOperations, FlowModel<Type> other) {
+ Map<int, VariableModel<Type>> newVariableInfo =
+ <int, VariableModel<Type>>{};
+ Map<int, VariableModel<Type>> otherVariableInfo = other.variableInfo;
bool changed = false;
- for (MapEntry<Variable?, VariableModel<Variable, Type>> entry
- in variableInfo.entries) {
- Variable? variable = entry.key;
- VariableModel<Variable, Type> variableModel = entry.value;
- VariableModel<Variable, Type>? otherVariableModel =
- otherVariableInfo[variable];
- VariableModel<Variable, Type> newVariableModel =
- otherVariableModel == null
- ? variableModel
- : VariableModel.inheritTested(
- typeOperations, variableModel, otherVariableModel.tested);
- newVariableInfo[variable] = newVariableModel;
+ for (MapEntry<int, VariableModel<Type>> entry in variableInfo.entries) {
+ int promotionKey = entry.key;
+ VariableModel<Type> variableModel = entry.value;
+ VariableModel<Type>? otherVariableModel = otherVariableInfo[promotionKey];
+ VariableModel<Type> newVariableModel = otherVariableModel == null
+ ? variableModel
+ : VariableModel.inheritTested(
+ typeOperations, variableModel, otherVariableModel.tested);
+ newVariableInfo[promotionKey] = newVariableModel;
if (!identical(newVariableModel, variableModel)) changed = true;
}
if (changed) {
- return new FlowModel<Variable, Type>.withInfo(reachable, newVariableInfo);
+ return new FlowModel<Type>.withInfo(reachable, newVariableInfo);
} else {
return this;
}
@@ -1890,26 +1884,26 @@
/// example, if a variable is promoted in `this` but not in [base], then it
/// will be promoted in the output model, provided that hasn't been reassigned
/// since then (which would make the promotion unsound).
- FlowModel<Variable, Type> rebaseForward(
- TypeOperations<Type> typeOperations, FlowModel<Variable, Type> base) {
+ FlowModel<Type> rebaseForward(
+ TypeOperations<Type> typeOperations, FlowModel<Type> base) {
// The rebased model is reachable iff both `this` and the new base are
// reachable.
Reachability newReachable = reachable.rebaseForward(base.reachable);
// Consider each variable in the new base model.
- Map<Variable?, VariableModel<Variable, Type>> newVariableInfo =
- <Variable?, VariableModel<Variable, Type>>{};
+ Map<int, VariableModel<Type>> newVariableInfo =
+ <int, VariableModel<Type>>{};
bool variableInfoMatchesThis = true;
bool variableInfoMatchesBase = true;
- for (MapEntry<Variable?, VariableModel<Variable, Type>> entry
+ for (MapEntry<int, VariableModel<Type>> entry
in base.variableInfo.entries) {
- Variable? variable = entry.key;
- VariableModel<Variable, Type> baseModel = entry.value;
- VariableModel<Variable, Type>? thisModel = variableInfo[variable];
+ int promotionKey = entry.key;
+ VariableModel<Type> baseModel = entry.value;
+ VariableModel<Type>? thisModel = variableInfo[promotionKey];
if (thisModel == null) {
// The variable has newly came into scope since `thisModel`, so the
// information in `baseModel` is up to date.
- newVariableInfo[variable] = baseModel;
+ newVariableInfo[promotionKey] = baseModel;
variableInfoMatchesThis = false;
continue;
}
@@ -1943,7 +1937,7 @@
// The variable is definitely unassigned if it was definitely unassigned
// in both `this` model and the new base model.
bool newUnassigned = thisModel.unassigned && baseModel.unassigned;
- VariableModel<Variable, Type> newModel = VariableModel._identicalOrNew(
+ VariableModel<Type> newModel = VariableModel._identicalOrNew(
thisModel,
baseModel,
newPromotedTypes,
@@ -1951,7 +1945,7 @@
newAssigned,
newUnassigned,
newWriteCaptured ? null : baseModel.ssaNode);
- newVariableInfo[variable] = newModel;
+ newVariableInfo[promotionKey] = newModel;
if (!identical(newModel, thisModel)) variableInfoMatchesThis = false;
if (!identical(newModel, baseModel)) variableInfoMatchesBase = false;
}
@@ -1959,8 +1953,8 @@
// present in `this` that aren't present in `base`, we may erroneously think
// that `newVariableInfo` matches `this`. If so, correct that.
if (variableInfoMatchesThis) {
- for (Variable? variable in variableInfo.keys) {
- if (!base.variableInfo.containsKey(variable)) {
+ for (int promotionKey in variableInfo.keys) {
+ if (!base.variableInfo.containsKey(promotionKey)) {
variableInfoMatchesThis = false;
break;
}
@@ -1980,17 +1974,17 @@
}
/// Updates the state to indicate that the control flow path is unreachable.
- FlowModel<Variable, Type> setUnreachable() {
+ FlowModel<Type> setUnreachable() {
if (!reachable.locallyReachable) return this;
- return new FlowModel<Variable, Type>.withInfo(
+ return new FlowModel<Type>.withInfo(
reachable.setUnreachable(), variableInfo);
}
/// Returns a [FlowModel] indicating the result of creating a control flow
/// split. See [Reachability.split] for more information.
- FlowModel<Variable, Type> split() =>
- new FlowModel<Variable, Type>.withInfo(reachable.split(), variableInfo);
+ FlowModel<Type> split() =>
+ new FlowModel<Type>.withInfo(reachable.split(), variableInfo);
@override
String toString() => '($reachable, $variableInfo)';
@@ -2000,29 +1994,27 @@
///
/// Note that the state is only changed if the previous type of [variable] was
/// potentially nullable.
- ExpressionInfo<Variable, Type> tryMarkNonNullable(
- TypeOperations<Type> typeOperations,
- ReferenceWithType<Variable, Type> referenceWithType) {
- VariableModel<Variable, Type> info =
+ ExpressionInfo<Type> tryMarkNonNullable(TypeOperations<Type> typeOperations,
+ ReferenceWithType<Type> referenceWithType) {
+ VariableModel<Type> info =
referenceWithType.reference.getInfo(variableInfo);
if (info.writeCaptured) {
- return new _TrivialExpressionInfo<Variable, Type>(this);
+ return new _TrivialExpressionInfo<Type>(this);
}
Type previousType = referenceWithType.type;
Type newType = typeOperations.promoteToNonNull(previousType);
if (typeOperations.isSameType(newType, previousType)) {
- return new _TrivialExpressionInfo<Variable, Type>(this);
+ return new _TrivialExpressionInfo<Type>(this);
}
assert(typeOperations.isSubtypeOf(newType, previousType));
- FlowModel<Variable, Type> modelIfSuccessful = _finishTypeTest(
+ FlowModel<Type> modelIfSuccessful = _finishTypeTest(
typeOperations, referenceWithType.reference, info, null, newType);
- FlowModel<Variable, Type> modelIfFailed = this;
+ FlowModel<Type> modelIfFailed = this;
- return new ExpressionInfo<Variable, Type>(
- this, modelIfSuccessful, modelIfFailed);
+ return new ExpressionInfo<Type>(this, modelIfSuccessful, modelIfFailed);
}
/// Returns an [ExpressionInfo] indicating the result of casting the given
@@ -2034,11 +2026,9 @@
///
/// TODO(paulberry): if the type is non-nullable, should this method mark the
/// variable as definitely assigned? Does it matter?
- FlowModel<Variable, Type> tryPromoteForTypeCast(
- TypeOperations<Type> typeOperations,
- ReferenceWithType<Variable, Type> referenceWithType,
- Type type) {
- VariableModel<Variable, Type> info =
+ FlowModel<Type> tryPromoteForTypeCast(TypeOperations<Type> typeOperations,
+ ReferenceWithType<Type> referenceWithType, Type type) {
+ VariableModel<Type> info =
referenceWithType.reference.getInfo(variableInfo);
if (info.writeCaptured) {
return this;
@@ -2065,18 +2055,18 @@
///
/// TODO(paulberry): if the type is non-nullable, should this method mark the
/// variable as definitely assigned? Does it matter?
- ExpressionInfo<Variable, Type> tryPromoteForTypeCheck(
+ ExpressionInfo<Type> tryPromoteForTypeCheck(
TypeOperations<Type> typeOperations,
- ReferenceWithType<Variable, Type> referenceWithType,
+ ReferenceWithType<Type> referenceWithType,
Type type) {
- VariableModel<Variable, Type> info =
+ VariableModel<Type> info =
referenceWithType.reference.getInfo(variableInfo);
if (info.writeCaptured) {
- return new _TrivialExpressionInfo<Variable, Type>(this);
+ return new _TrivialExpressionInfo<Type>(this);
}
Type previousType = referenceWithType.type;
- FlowModel<Variable, Type> modelIfSuccessful = this;
+ FlowModel<Type> modelIfSuccessful = this;
Type? typeIfSuccess = typeOperations.tryPromoteToType(type, previousType);
if (typeIfSuccess != null &&
!typeOperations.isSameType(typeIfSuccess, previousType)) {
@@ -2098,52 +2088,53 @@
} else {
typeIfFailed = factoredType;
}
- FlowModel<Variable, Type> modelIfFailed = _finishTypeTest(
+ FlowModel<Type> modelIfFailed = _finishTypeTest(
typeOperations, referenceWithType.reference, info, type, typeIfFailed);
- return new ExpressionInfo<Variable, Type>(
- this, modelIfSuccessful, modelIfFailed);
+ return new ExpressionInfo<Type>(this, modelIfSuccessful, modelIfFailed);
}
/// Returns a [FlowModel] indicating the result of removing a control flow
/// split. See [Reachability.unsplit] for more information.
- FlowModel<Variable, Type> unsplit() =>
- new FlowModel<Variable, Type>.withInfo(reachable.unsplit(), variableInfo);
+ FlowModel<Type> unsplit() =>
+ new FlowModel<Type>.withInfo(reachable.unsplit(), variableInfo);
/// Removes control flow splits until a [FlowModel] is obtained whose
/// reachability has the given [parent].
- FlowModel<Variable, Type> unsplitTo(Reachability parent) {
+ FlowModel<Type> unsplitTo(Reachability parent) {
if (identical(this.reachable.parent, parent)) return this;
Reachability reachable = this.reachable.unsplit();
while (!identical(reachable.parent, parent)) {
reachable = reachable.unsplit();
}
- return new FlowModel<Variable, Type>.withInfo(reachable, variableInfo);
+ return new FlowModel<Type>.withInfo(reachable, variableInfo);
}
- /// Updates the state to indicate that an assignment was made to the given
- /// [variable]. The variable is marked as definitely assigned, and any
- /// previous type promotion is removed.
+ /// Updates the state to indicate that an assignment was made to [variable],
+ /// whose key is [variableKey]. The variable is marked as definitely
+ /// assigned, and any previous type promotion is removed.
///
/// If there is any chance that the write will cause a demotion, the caller
/// must pass in a non-null value for [nonPromotionReason] describing the
/// reason for any potential demotion.
- FlowModel<Variable, Type> write(
+ FlowModel<Type> write<Variable extends Object>(
NonPromotionReason? nonPromotionReason,
Variable variable,
+ int variableKey,
Type writtenType,
- SsaNode<Variable, Type> newSsaNode,
+ SsaNode<Type> newSsaNode,
Operations<Variable, Type> operations,
{bool promoteToTypeOfInterest = true}) {
- VariableModel<Variable, Type>? infoForVar = variableInfo[variable];
+ VariableModel<Type>? infoForVar = variableInfo[variableKey];
if (infoForVar == null) return this;
- VariableModel<Variable, Type> newInfoForVar = infoForVar.write(
- nonPromotionReason, variable, writtenType, operations, newSsaNode,
+ VariableModel<Type> newInfoForVar = infoForVar.write(nonPromotionReason,
+ variable, variableKey, writtenType, operations, newSsaNode,
promoteToTypeOfInterest: promoteToTypeOfInterest);
if (identical(newInfoForVar, infoForVar)) return this;
- return _updateVariableInfo(new VariableReference(variable), newInfoForVar);
+ return _updateVariableInfo(
+ new VariableReference(variable, variableKey), newInfoForVar);
}
/// Common algorithm for [tryMarkNonNullable], [tryPromoteForTypeCast],
@@ -2157,10 +2148,10 @@
/// - [promotedType] should be a subtype of the currently-promoted type (i.e.
/// no redundant or side-promotions)
/// - The variable should not be write-captured.
- FlowModel<Variable, Type> _finishTypeTest(
+ FlowModel<Type> _finishTypeTest(
TypeOperations<Type> typeOperations,
- Reference<Variable, Type> reference,
- VariableModel<Variable, Type> info,
+ Reference<Type> reference,
+ VariableModel<Type> info,
Type? testedType,
Type? promotedType,
) {
@@ -2187,7 +2178,7 @@
? this
: _updateVariableInfo(
reference,
- new VariableModel<Variable, Type>(
+ new VariableModel<Type>(
promotedTypes: newPromotedTypes,
tested: newTested,
assigned: info.assigned,
@@ -2199,14 +2190,14 @@
/// Returns a new [FlowModel] where the information for [reference] is
/// replaced with [model].
- FlowModel<Variable, Type> _updateVariableInfo(
- Reference<Variable, Type> reference, VariableModel<Variable, Type> model,
+ FlowModel<Type> _updateVariableInfo(
+ Reference<Type> reference, VariableModel<Type> model,
{Reachability? reachable}) {
reachable ??= this.reachable;
- Map<Variable?, VariableModel<Variable, Type>> newVariableInfo =
- new Map<Variable?, VariableModel<Variable, Type>>.of(variableInfo);
+ Map<int, VariableModel<Type>> newVariableInfo =
+ new Map<int, VariableModel<Type>>.of(variableInfo);
reference.storeInfo(newVariableInfo, model);
- return new FlowModel<Variable, Type>.withInfo(reachable, newVariableInfo);
+ return new FlowModel<Type>.withInfo(reachable, newVariableInfo);
}
/// Forms a new state to reflect a control flow path that might have come from
@@ -2218,12 +2209,11 @@
/// are kept only if they are common to both input states; if a variable is
/// promoted to one type in one state and a subtype in the other state, the
/// less specific type promotion is kept.
- static FlowModel<Variable, Type>
- join<Variable extends Object, Type extends Object>(
+ static FlowModel<Type> join<Type extends Object>(
TypeOperations<Type> typeOperations,
- FlowModel<Variable, Type>? first,
- FlowModel<Variable, Type>? second,
- Map<Variable?, VariableModel<Variable, Type>> emptyVariableMap,
+ FlowModel<Type>? first,
+ FlowModel<Type>? second,
+ Map<int, VariableModel<Type>> emptyVariableMap,
) {
if (first == null) return second!;
if (second == null) return first;
@@ -2240,9 +2230,11 @@
Reachability newReachable =
Reachability.join(first.reachable, second.reachable);
- Map<Variable?, VariableModel<Variable, Type>> newVariableInfo =
- FlowModel.joinVariableInfo(typeOperations, first.variableInfo,
- second.variableInfo, emptyVariableMap);
+ Map<int, VariableModel<Type>> newVariableInfo = FlowModel.joinVariableInfo(
+ typeOperations,
+ first.variableInfo,
+ second.variableInfo,
+ emptyVariableMap);
return FlowModel._identicalOrNew(
first, second, newReachable, newVariableInfo);
@@ -2250,33 +2242,29 @@
/// Joins two "variable info" maps. See [join] for details.
@visibleForTesting
- static Map<Variable?, VariableModel<Variable, Type>>
- joinVariableInfo<Variable extends Object, Type extends Object>(
+ static Map<int, VariableModel<Type>> joinVariableInfo<Type extends Object>(
TypeOperations<Type> typeOperations,
- Map<Variable?, VariableModel<Variable, Type>> first,
- Map<Variable?, VariableModel<Variable, Type>> second,
- Map<Variable?, VariableModel<Variable, Type>> emptyMap,
+ Map<int, VariableModel<Type>> first,
+ Map<int, VariableModel<Type>> second,
+ Map<int, VariableModel<Type>> emptyMap,
) {
if (identical(first, second)) return first;
if (first.isEmpty || second.isEmpty) {
return emptyMap;
}
- Map<Variable?, VariableModel<Variable, Type>> result =
- <Variable?, VariableModel<Variable, Type>>{};
+ Map<int, VariableModel<Type>> result = <int, VariableModel<Type>>{};
bool alwaysFirst = true;
bool alwaysSecond = true;
- for (MapEntry<Variable?, VariableModel<Variable, Type>> entry
- in first.entries) {
- Variable? variable = entry.key;
- VariableModel<Variable, Type>? secondModel = second[variable];
+ for (MapEntry<int, VariableModel<Type>> entry in first.entries) {
+ int promotionKey = entry.key;
+ VariableModel<Type>? secondModel = second[promotionKey];
if (secondModel == null) {
alwaysFirst = false;
} else {
- VariableModel<Variable, Type> joined =
- VariableModel.join<Variable, Type>(
- typeOperations, entry.value, secondModel);
- result[variable] = joined;
+ VariableModel<Type> joined =
+ VariableModel.join<Type>(typeOperations, entry.value, secondModel);
+ result[promotionKey] = joined;
if (!identical(joined, entry.value)) alwaysFirst = false;
if (!identical(joined, secondModel)) alwaysSecond = false;
}
@@ -2290,12 +2278,11 @@
/// Models the result of joining the flow models [first] and [second] at the
/// merge of two control flow paths.
- static FlowModel<Variable, Type>
- merge<Variable extends Object, Type extends Object>(
+ static FlowModel<Type> merge<Type extends Object>(
TypeOperations<Type> typeOperations,
- FlowModel<Variable, Type>? first,
- FlowModel<Variable, Type>? second,
- Map<Variable?, VariableModel<Variable, Type>> emptyVariableMap,
+ FlowModel<Type>? first,
+ FlowModel<Type>? second,
+ Map<int, VariableModel<Type>> emptyVariableMap,
) {
if (first == null) return second!.unsplit();
if (second == null) return first.unsplit();
@@ -2312,9 +2299,11 @@
Reachability newReachable =
Reachability.join(first.reachable, second.reachable).unsplit();
- Map<Variable?, VariableModel<Variable, Type>> newVariableInfo =
- FlowModel.joinVariableInfo(typeOperations, first.variableInfo,
- second.variableInfo, emptyVariableMap);
+ Map<int, VariableModel<Type>> newVariableInfo = FlowModel.joinVariableInfo(
+ typeOperations,
+ first.variableInfo,
+ second.variableInfo,
+ emptyVariableMap);
return FlowModel._identicalOrNew(
first, second, newReachable, newVariableInfo);
@@ -2322,12 +2311,11 @@
/// Creates a new [FlowModel] object, unless it is equivalent to either
/// [first] or [second], in which case one of those objects is re-used.
- static FlowModel<Variable, Type>
- _identicalOrNew<Variable extends Object, Type extends Object>(
- FlowModel<Variable, Type> first,
- FlowModel<Variable, Type> second,
- Reachability newReachable,
- Map<Variable?, VariableModel<Variable, Type>> newVariableInfo) {
+ static FlowModel<Type> _identicalOrNew<Type extends Object>(
+ FlowModel<Type> first,
+ FlowModel<Type> second,
+ Reachability newReachable,
+ Map<int, VariableModel<Type>> newVariableInfo) {
if (first.reachable == newReachable &&
identical(first.variableInfo, newVariableInfo)) {
return first;
@@ -2337,23 +2325,20 @@
return second;
}
- return new FlowModel<Variable, Type>.withInfo(
- newReachable, newVariableInfo);
+ return new FlowModel<Type>.withInfo(newReachable, newVariableInfo);
}
/// Determines whether the given "variableInfo" maps are equivalent.
///
/// The equivalence check is shallow; if two variables' models are not
/// identical, we return `false`.
- static bool _variableInfosEqual<Variable extends Object, Type extends Object>(
- Map<Variable?, VariableModel<Variable, Type>> p1,
- Map<Variable?, VariableModel<Variable, Type>> p2) {
+ static bool _variableInfosEqual<Type extends Object>(
+ Map<int, VariableModel<Type>> p1, Map<int, VariableModel<Type>> p2) {
if (p1.length != p2.length) return false;
if (!p1.keys.toSet().containsAll(p2.keys)) return false;
- for (MapEntry<Variable?, VariableModel<Variable, Type>> entry
- in p1.entries) {
- VariableModel<Variable, Type> p1Value = entry.value;
- VariableModel<Variable, Type>? p2Value = p2[entry.key];
+ for (MapEntry<int, VariableModel<Type>> entry in p1.entries) {
+ VariableModel<Type> p1Value = entry.value;
+ VariableModel<Type>? p2Value = p2[entry.key];
if (!identical(p1Value, p2Value)) {
return false;
}
@@ -2426,6 +2411,48 @@
extends TypeOperations<Type> implements VariableOperations<Variable, Type> {
}
+/// This data structure assigns a unique integer identifier to everything that
+/// might undergo promotion in the user's code (local variables and properties).
+/// An integer identifier is also assigned to `this` (even though `this` is not
+/// promotable), because promotable properties can be reached using `this` as a
+/// starting point.
+@visibleForTesting
+class PromotionKeyStore<Variable extends Object> {
+ @visibleForTesting
+
+ /// Special promotion key to represent `this`.
+ late final int thisPromotionKey = _makeNewKey(null);
+
+ final Map<Variable, int> _variableKeys = new Map<Variable, int>.identity();
+
+ final List<Variable?> _keyToVariable = [];
+
+ /// List of maps indicating the set of properties of each promotable entity
+ /// being tracked by flow analysis. The list is indexed by the promotion key
+ /// of the target, and the map is indexed by the property name.
+ ///
+ /// Null list elements are considered equivalent to an empty map (this allows
+ /// us so save memory due to the fact that most entries will not be accessed).
+ final List<Map<String, int>?> _properties = [];
+
+ int getProperty(int targetKey, String propertyName) =>
+ (_properties[targetKey] ??= {})[propertyName] ??= _makeNewKey(null);
+
+ @visibleForTesting
+ int keyForVariable(Variable variable) =>
+ _variableKeys[variable] ??= _makeNewKey(variable);
+
+ @visibleForTesting
+ Variable variableForKey(int variableKey) => _keyToVariable[variableKey]!;
+
+ int _makeNewKey(Variable? variable) {
+ int key = _keyToVariable.length;
+ _keyToVariable.add(variable);
+ _properties.add(null);
+ return key;
+ }
+}
+
/// Non-promotion reason describing the situation where an expression was not
/// promoted due to the fact that it's a property get.
class PropertyNotPromoted<Type extends Object> extends NonPromotionReason {
@@ -2605,39 +2632,45 @@
/// other references in order to give useful error messages to the user about
/// why promotion did not occur.
@visibleForTesting
-abstract class Reference<Variable extends Object, Type extends Object> {
+abstract class Reference<Type extends Object> {
+ /// The reference's corresponding key, as assigned by [PromotionKeyStore].
+ final int promotionKey;
+
+ Reference(this.promotionKey);
+
/// Gets the info for this reference, creating it if it doesn't exist.
- VariableModel<Variable, Type> getInfo(
- Map<Variable?, VariableModel<Variable, Type>> variableInfo) =>
- _getInfo(variableInfo) ?? new VariableModel<Variable, Type>.fresh();
+ VariableModel<Type> getInfo(Map<int, VariableModel<Type>> variableInfo) =>
+ _getInfo(variableInfo) ?? new VariableModel<Type>.fresh();
/// Gets a map of non-promotion reasons associated with this reference. This
/// is the map that will be returned from [FlowAnalysis.whyNotPromoted].
Map<Type, NonPromotionReason> Function() getNonPromotionReasons(
- Map<Variable?, VariableModel<Variable, Type>> variableInfo,
+ Map<int, VariableModel<Type>> variableInfo,
Type staticType,
- Operations<Variable, Type> operations);
+ TypeOperations<Type> operations);
/// Creates a reference representing a get of a property called [propertyName]
/// on the reference represented by `this`.
- Reference<Variable, Type> propertyGet(
+ Reference<Type> propertyGet(PromotionKeyStore promotionKeyStore,
String propertyName, Object? propertyMember) =>
- new _PropertyGetReference<Variable, Type>(
- this, propertyName, propertyMember);
+ new _PropertyGetReference<Type>(propertyName, propertyMember,
+ promotionKeyStore.getProperty(promotionKey, propertyName));
/// Stores info for this reference in [variableInfo].
- void storeInfo(Map<Variable?, VariableModel<Variable, Type>> variableInfo,
- VariableModel<Variable, Type> variableModel);
+ void storeInfo(Map<int, VariableModel<Type>> variableInfo,
+ VariableModel<Type> variableModel) {
+ variableInfo[promotionKey] = variableModel;
+ }
/// Gets the info for this reference, or `null` if it doesn't exist.
- VariableModel<Variable, Type>? _getInfo(
- Map<Variable?, VariableModel<Variable, Type>> variableInfo);
+ VariableModel<Type>? _getInfo(Map<int, VariableModel<Type>> variableInfo) =>
+ variableInfo[promotionKey];
}
/// Container object combining a [Reference] object with its static type.
@visibleForTesting
-class ReferenceWithType<Variable extends Object, Type extends Object> {
- final Reference<Variable, Type> reference;
+class ReferenceWithType<Type extends Object> {
+ final Reference<Type> reference;
final Type type;
@@ -2656,7 +2689,7 @@
/// (https://en.wikipedia.org/wiki/Static_single_assignment_form) except that it
/// does not store a complete IR of the code being analyzed.
@visibleForTesting
-class SsaNode<Variable extends Object, Type extends Object> {
+class SsaNode<Type extends Object> {
/// Expando mapping SSA nodes to debug ids. Only used by `toString`.
static final Expando<int> _debugIds = new Expando<int>();
@@ -2671,7 +2704,7 @@
/// [_TrivialExpressionInfo]) because such information does not lead to
/// promotions.
@visibleForTesting
- final ExpressionInfo<Variable, Type>? expressionInfo;
+ final ExpressionInfo<Type>? expressionInfo;
SsaNode(this.expressionInfo);
@@ -2776,7 +2809,7 @@
/// Instances of this class are immutable, so the methods below that "update"
/// the state actually leave `this` unchanged and return a new state object.
@visibleForTesting
-class VariableModel<Variable extends Object, Type extends Object> {
+class VariableModel<Type extends Object> {
/// Sequence of types that the variable has been promoted to, where each
/// element of the sequence is a subtype of the previous. Null if the
/// variable hasn't been promoted.
@@ -2799,24 +2832,18 @@
/// value has changed since a time in the past.
///
/// `null` if the variable has been write captured.
- final SsaNode<Variable, Type>? ssaNode;
+ final SsaNode<Type>? ssaNode;
/// Non-promotion history of this variable.
final NonPromotionHistory<Type>? nonPromotionHistory;
- /// Promotion information for properties of this variable. We don't actually
- /// promote properties, but we track the promotions that would occur if we
- /// did, so that we can report those as non-promotion reasons.
- final Map<String, VariableModel<Variable, Type>> properties;
-
VariableModel(
{required this.promotedTypes,
required this.tested,
required this.assigned,
required this.unassigned,
required this.ssaNode,
- this.nonPromotionHistory,
- this.properties = const {}}) {
+ this.nonPromotionHistory}) {
assert(!(assigned && unassigned),
"Can't be both definitely assigned and unassigned");
assert(promotedTypes == null || promotedTypes!.isNotEmpty);
@@ -2834,9 +2861,8 @@
: promotedTypes = null,
tested = const [],
unassigned = !assigned,
- ssaNode = new SsaNode<Variable, Type>(null),
- nonPromotionHistory = null,
- properties = const {};
+ ssaNode = new SsaNode<Type>(null),
+ nonPromotionHistory = null;
/// Indicates whether the variable has been write captured.
bool get writeCaptured => ssaNode == null;
@@ -2846,27 +2872,15 @@
///
/// Used by [conservativeJoin] to update the state of variables at the top of
/// loops whose bodies write to them.
- VariableModel<Variable, Type> discardPromotionsAndMarkNotUnassigned() {
- return new VariableModel<Variable, Type>(
+ VariableModel<Type> discardPromotionsAndMarkNotUnassigned() {
+ return new VariableModel<Type>(
promotedTypes: null,
tested: tested,
assigned: assigned,
unassigned: false,
- ssaNode: writeCaptured ? null : new SsaNode<Variable, Type>(null));
+ ssaNode: writeCaptured ? null : new SsaNode<Type>(null));
}
- /// Updates `this` with a new set of properties.
- VariableModel<Variable, Type> setProperties(
- Map<String, VariableModel<Variable, Type>> newProperties) =>
- new VariableModel<Variable, Type>(
- promotedTypes: promotedTypes,
- tested: tested,
- unassigned: unassigned,
- assigned: assigned,
- ssaNode: ssaNode,
- nonPromotionHistory: nonPromotionHistory,
- properties: newProperties);
-
@override
String toString() {
List<String> parts = [ssaNode.toString()];
@@ -2888,9 +2902,6 @@
if (nonPromotionHistory != null) {
parts.add('nonPromotionHistory: $nonPromotionHistory');
}
- if (properties.isNotEmpty) {
- parts.add('properties: $properties');
- }
return 'VariableModel(${parts.join(', ')})';
}
@@ -2900,15 +2911,16 @@
/// If there is any chance that the write will cause a demotion, the caller
/// must pass in a non-null value for [nonPromotionReason] describing the
/// reason for any potential demotion.
- VariableModel<Variable, Type> write(
+ VariableModel<Type> write<Variable extends Object>(
NonPromotionReason? nonPromotionReason,
Variable variable,
+ int variableKey,
Type writtenType,
Operations<Variable, Type> operations,
- SsaNode<Variable, Type> newSsaNode,
+ SsaNode<Type> newSsaNode,
{required bool promoteToTypeOfInterest}) {
if (writeCaptured) {
- return new VariableModel<Variable, Type>(
+ return new VariableModel<Type>(
promotedTypes: promotedTypes,
tested: tested,
assigned: true,
@@ -2928,7 +2940,7 @@
// TODO(paulberry): remove demotions from demotionResult.nonPromotionHistory
// that are no longer in effect due to re-promotion.
if (identical(promotedTypes, newPromotedTypes) && assigned) {
- return new VariableModel<Variable, Type>(
+ return new VariableModel<Type>(
promotedTypes: promotedTypes,
tested: tested,
assigned: assigned,
@@ -2943,7 +2955,7 @@
newTested = tested;
}
- return new VariableModel<Variable, Type>(
+ return new VariableModel<Type>(
promotedTypes: newPromotedTypes,
tested: newTested,
assigned: true,
@@ -2954,8 +2966,8 @@
/// Returns a new [VariableModel] reflecting the fact that the variable has
/// been write-captured.
- VariableModel<Variable, Type> writeCapture() {
- return new VariableModel<Variable, Type>(
+ VariableModel<Type> writeCapture() {
+ return new VariableModel<Type>(
promotedTypes: null,
tested: const [],
assigned: assigned,
@@ -3123,14 +3135,13 @@
/// are consistently treated as "of interest" in code that follows the loop,
/// regardless of the type of loop.
@visibleForTesting
- static VariableModel<Variable, Type>
- inheritTested<Variable extends Object, Type extends Object>(
- TypeOperations<Type> typeOperations,
- VariableModel<Variable, Type> model,
- List<Type> tested) {
+ static VariableModel<Type> inheritTested<Type extends Object>(
+ TypeOperations<Type> typeOperations,
+ VariableModel<Type> model,
+ List<Type> tested) {
List<Type> newTested = joinTested(tested, model.tested, typeOperations);
if (identical(newTested, model.tested)) return model;
- return new VariableModel<Variable, Type>(
+ return new VariableModel<Type>(
promotedTypes: model.promotedTypes,
tested: newTested,
assigned: model.assigned,
@@ -3139,11 +3150,10 @@
}
/// Joins two variable models. See [FlowModel.join] for details.
- static VariableModel<Variable, Type>
- join<Variable extends Object, Type extends Object>(
- TypeOperations<Type> typeOperations,
- VariableModel<Variable, Type> first,
- VariableModel<Variable, Type> second) {
+ static VariableModel<Type> join<Type extends Object>(
+ TypeOperations<Type> typeOperations,
+ VariableModel<Type> first,
+ VariableModel<Type> second) {
List<Type>? newPromotedTypes = joinPromotedTypes(
first.promotedTypes, second.promotedTypes, typeOperations);
newPromotedTypes = typeOperations.refinePromotedTypes(
@@ -3154,11 +3164,11 @@
List<Type> newTested = newWriteCaptured
? const []
: joinTested(first.tested, second.tested, typeOperations);
- SsaNode<Variable, Type>? newSsaNode = newWriteCaptured
+ SsaNode<Type>? newSsaNode = newWriteCaptured
? null
: first.ssaNode == second.ssaNode
? first.ssaNode
- : new SsaNode<Variable, Type>(null);
+ : new SsaNode<Type>(null);
return _identicalOrNew(first, second, newPromotedTypes, newTested,
newAssigned, newUnassigned, newWriteCaptured ? null : newSsaNode);
}
@@ -3167,11 +3177,8 @@
/// chains. Briefly, we intersect given chains. The chains are totally
/// ordered subsets of a global partial order. Their intersection is a
/// subset of each, and as such is also totally ordered.
- static List<Type>?
- joinPromotedTypes<Variable extends Object, Type extends Object>(
- List<Type>? chain1,
- List<Type>? chain2,
- TypeOperations<Type> typeOperations) {
+ static List<Type>? joinPromotedTypes<Type extends Object>(List<Type>? chain1,
+ List<Type>? chain2, TypeOperations<Type> typeOperations) {
if (chain1 == null) return chain1;
if (chain2 == null) return chain2;
@@ -3214,10 +3221,8 @@
/// - The sense of equality for the union operation is determined by
/// [TypeOperations.isSameType].
/// - The types of interests lists are considered immutable.
- static List<Type> joinTested<Variable extends Object, Type extends Object>(
- List<Type> types1,
- List<Type> types2,
- TypeOperations<Type> typeOperations) {
+ static List<Type> joinTested<Type extends Object>(List<Type> types1,
+ List<Type> types2, TypeOperations<Type> typeOperations) {
// Ensure that types1 is the shorter list.
if (types1.length > types2.length) {
List<Type> tmp = types1;
@@ -3291,24 +3296,22 @@
? [promoted]
: (promotedTypes.toList()..add(promoted));
- static List<Type>
- _addTypeToUniqueList<Variable extends Object, Type extends Object>(
- List<Type> types, Type newType, TypeOperations<Type> typeOperations) {
+ static List<Type> _addTypeToUniqueList<Type extends Object>(
+ List<Type> types, Type newType, TypeOperations<Type> typeOperations) {
if (_typeListContains(typeOperations, types, newType)) return types;
return new List<Type>.of(types)..add(newType);
}
/// Creates a new [VariableModel] object, unless it is equivalent to either
/// [first] or [second], in which case one of those objects is re-used.
- static VariableModel<Variable, Type>
- _identicalOrNew<Variable extends Object, Type extends Object>(
- VariableModel<Variable, Type> first,
- VariableModel<Variable, Type> second,
- List<Type>? newPromotedTypes,
- List<Type> newTested,
- bool newAssigned,
- bool newUnassigned,
- SsaNode<Variable, Type>? newSsaNode) {
+ static VariableModel<Type> _identicalOrNew<Type extends Object>(
+ VariableModel<Type> first,
+ VariableModel<Type> second,
+ List<Type>? newPromotedTypes,
+ List<Type> newTested,
+ bool newAssigned,
+ bool newUnassigned,
+ SsaNode<Type>? newSsaNode) {
if (identical(first.promotedTypes, newPromotedTypes) &&
identical(first.tested, newTested) &&
first.assigned == newAssigned &&
@@ -3322,7 +3325,7 @@
second.ssaNode == newSsaNode) {
return second;
} else {
- return new VariableModel<Variable, Type>(
+ return new VariableModel<Type>(
promotedTypes: newPromotedTypes,
tested: newTested,
assigned: newAssigned,
@@ -3331,7 +3334,7 @@
}
}
- static bool _typeListContains<Variable extends Object, Type extends Object>(
+ static bool _typeListContains<Type extends Object>(
TypeOperations<Type> typeOperations, List<Type> list, Type searchType) {
for (Type type in list) {
if (typeOperations.isSameType(type, searchType)) return true;
@@ -3351,18 +3354,18 @@
/// (or function parameter).
@visibleForTesting
class VariableReference<Variable extends Object, Type extends Object>
- extends Reference<Variable, Type> {
+ extends Reference<Type> {
/// The variable being referred to.
final Variable variable;
- VariableReference(this.variable);
+ VariableReference(this.variable, super.promotionKey);
@override
Map<Type, NonPromotionReason> Function() getNonPromotionReasons(
- Map<Variable?, VariableModel<Variable, Type>> variableInfo,
+ Map<int, VariableModel<Type>> variableInfo,
Type staticType,
- Operations<Variable, Type> operations) {
- VariableModel<Variable, Type>? currentVariableInfo = variableInfo[variable];
+ covariant Operations<Variable, Type> operations) {
+ VariableModel<Type>? currentVariableInfo = variableInfo[promotionKey];
if (currentVariableInfo == null) {
return () => {};
}
@@ -3384,27 +3387,15 @@
}
@override
- void storeInfo(Map<Variable?, VariableModel<Variable, Type>> variableInfo,
- VariableModel<Variable, Type> variableModel) {
- variableInfo[variable] = variableModel;
- }
-
- @override
- String toString() => 'VariableReference($variable)';
-
- @override
- VariableModel<Variable, Type>? _getInfo(
- Map<Variable?, VariableModel<Variable, Type>> variableInfo) =>
- variableInfo[variable];
+ String toString() => 'VariableReference($variable, $promotionKey)';
}
class WhyNotPromotedInfo {}
/// [_FlowContext] representing an assert statement or assert initializer.
-class _AssertContext<Variable extends Object, Type extends Object>
- extends _SimpleContext<Variable, Type> {
+class _AssertContext<Type extends Object> extends _SimpleContext<Type> {
/// Flow models associated with the condition being asserted.
- ExpressionInfo<Variable, Type>? _conditionInfo;
+ ExpressionInfo<Type>? _conditionInfo;
_AssertContext(super.previous);
@@ -3416,10 +3407,9 @@
/// [_FlowContext] representing a language construct that branches on a boolean
/// condition, such as an `if` statement, conditional expression, or a logical
/// binary operator.
-class _BranchContext<Variable extends Object, Type extends Object>
- extends _FlowContext {
+class _BranchContext<Type extends Object> extends _FlowContext {
/// Flow models associated with the condition being branched on.
- final ExpressionInfo<Variable, Type>? _conditionInfo;
+ final ExpressionInfo<Type>? _conditionInfo;
_BranchContext(this._conditionInfo);
@@ -3429,15 +3419,14 @@
/// [_FlowContext] representing a language construct that can be targeted by
/// `break` or `continue` statements, such as a loop or switch statement.
-class _BranchTargetContext<Variable extends Object, Type extends Object>
- extends _FlowContext {
+class _BranchTargetContext<Type extends Object> extends _FlowContext {
/// Accumulated flow model for all `break` statements seen so far, or `null`
/// if no `break` statements have been seen yet.
- FlowModel<Variable, Type>? _breakModel;
+ FlowModel<Type>? _breakModel;
/// Accumulated flow model for all `continue` statements seen so far, or
/// `null` if no `continue` statements have been seen yet.
- FlowModel<Variable, Type>? _continueModel;
+ FlowModel<Type>? _continueModel;
/// The reachability checkpoint associated with this loop or switch statement.
/// When analyzing deeply nested `break` and `continue` statements, their flow
@@ -3453,13 +3442,12 @@
}
/// [_FlowContext] representing a conditional expression.
-class _ConditionalContext<Variable extends Object, Type extends Object>
- extends _BranchContext<Variable, Type> {
+class _ConditionalContext<Type extends Object> extends _BranchContext<Type> {
/// Flow models associated with the value of the conditional expression in the
/// circumstance where the "then" branch is taken.
- ExpressionInfo<Variable, Type>? _thenInfo;
+ ExpressionInfo<Type>? _thenInfo;
- _ConditionalContext(ExpressionInfo<Variable, Type> super.conditionInfo);
+ _ConditionalContext(ExpressionInfo<Type> super.conditionInfo);
@override
String toString() => '_ConditionalContext(conditionInfo: $_conditionInfo, '
@@ -3494,11 +3482,9 @@
/// The mapping from [Statement]s that can act as targets for `break` and
/// `continue` statements (i.e. loops and switch statements) to the to their
/// context information.
- final Map<Statement, _BranchTargetContext<Variable, Type>>
- _statementToContext = {};
+ final Map<Statement, _BranchTargetContext<Type>> _statementToContext = {};
- FlowModel<Variable, Type> _current =
- new FlowModel<Variable, Type>(Reachability.initial);
+ FlowModel<Type> _current = new FlowModel<Type>(Reachability.initial);
/// The most recently visited expression for which an [ExpressionInfo] object
/// exists, or `null` if no expression has been visited that has a
@@ -3507,7 +3493,7 @@
/// If [_expressionWithInfo] is not `null`, the [ExpressionInfo] object
/// corresponding to it. Otherwise `null`.
- ExpressionInfo<Variable, Type>? _expressionInfo;
+ ExpressionInfo<Type>? _expressionInfo;
/// The most recently visited expression which was a reference, or `null` if
/// no such expression has been visited.
@@ -3515,7 +3501,7 @@
/// If [_expressionVariable] is not `null`, the reference corresponding to it.
/// Otherwise `null`.
- ReferenceWithType<Variable, Type>? _expressionReference;
+ ReferenceWithType<Type>? _expressionReference;
final AssignedVariables<Node, Variable> _assignedVariables;
@@ -3526,19 +3512,20 @@
/// analyzing old language versions).
final bool respectImplicitlyTypedVarInitializers;
+ final PromotionKeyStore<Variable> _promotionKeyStore;
+
_FlowAnalysisImpl(this.operations, this._assignedVariables,
- {required this.respectImplicitlyTypedVarInitializers}) {
+ {required this.respectImplicitlyTypedVarInitializers})
+ : _promotionKeyStore = _assignedVariables._promotionKeyStore {
if (!_assignedVariables._isFinished) {
_assignedVariables.finish();
}
- AssignedVariablesNodeInfo<Variable> anywhere = _assignedVariables._anywhere;
- Set<Variable> implicitlyDeclaredVars = {
- ...anywhere._read,
- ...anywhere._written
- };
+ AssignedVariablesNodeInfo anywhere = _assignedVariables._anywhere;
+ Set<int> implicitlyDeclaredVars = {...anywhere._read, ...anywhere._written};
implicitlyDeclaredVars.removeAll(anywhere._declared);
- for (Variable variable in implicitlyDeclaredVars) {
- declare(variable, true);
+ for (int variableKey in implicitlyDeclaredVars) {
+ _current = _current.declare(
+ _promotionKeyStore.variableForKey(variableKey), variableKey, true);
}
}
@@ -3547,7 +3534,7 @@
@override
void asExpression_end(Expression subExpression, Type type) {
- ReferenceWithType<Variable, Type>? referenceWithType =
+ ReferenceWithType<Type>? referenceWithType =
_getExpressionReference(subExpression);
if (referenceWithType == null) return;
_current =
@@ -3556,9 +3543,8 @@
@override
void assert_afterCondition(Expression condition) {
- _AssertContext<Variable, Type> context =
- _stack.last as _AssertContext<Variable, Type>;
- ExpressionInfo<Variable, Type> conditionInfo = _expressionEnd(condition);
+ _AssertContext<Type> context = _stack.last as _AssertContext<Type>;
+ ExpressionInfo<Type> conditionInfo = _expressionEnd(condition);
context._conditionInfo = conditionInfo;
_current = conditionInfo.ifFalse;
}
@@ -3566,19 +3552,18 @@
@override
void assert_begin() {
_current = _current.split();
- _stack.add(new _AssertContext<Variable, Type>(_current));
+ _stack.add(new _AssertContext<Type>(_current));
}
@override
void assert_end() {
- _AssertContext<Variable, Type> context =
- _stack.removeLast() as _AssertContext<Variable, Type>;
+ _AssertContext<Type> context = _stack.removeLast() as _AssertContext<Type>;
_current = _merge(context._previous, context._conditionInfo!.ifTrue);
}
@override
void booleanLiteral(Expression expression, bool value) {
- FlowModel<Variable, Type> unreachable = _current.setUnreachable();
+ FlowModel<Type> unreachable = _current.setUnreachable();
_storeExpressionInfo(
expression,
value
@@ -3593,8 +3578,8 @@
@override
void conditional_elseBegin(Expression thenExpression) {
- _ConditionalContext<Variable, Type> context =
- _stack.last as _ConditionalContext<Variable, Type>;
+ _ConditionalContext<Type> context =
+ _stack.last as _ConditionalContext<Type>;
context._thenInfo = _expressionEnd(thenExpression);
_current = context._conditionInfo!.ifFalse;
}
@@ -3602,10 +3587,10 @@
@override
void conditional_end(
Expression conditionalExpression, Expression elseExpression) {
- _ConditionalContext<Variable, Type> context =
- _stack.removeLast() as _ConditionalContext<Variable, Type>;
- ExpressionInfo<Variable, Type> thenInfo = context._thenInfo!;
- ExpressionInfo<Variable, Type> elseInfo = _expressionEnd(elseExpression);
+ _ConditionalContext<Type> context =
+ _stack.removeLast() as _ConditionalContext<Type>;
+ ExpressionInfo<Type> thenInfo = context._thenInfo!;
+ ExpressionInfo<Type> elseInfo = _expressionEnd(elseExpression);
_storeExpressionInfo(
conditionalExpression,
new ExpressionInfo(
@@ -3616,22 +3601,23 @@
@override
void conditional_thenBegin(Expression condition, Node conditionalExpression) {
- ExpressionInfo<Variable, Type> conditionInfo = _expressionEnd(condition);
+ ExpressionInfo<Type> conditionInfo = _expressionEnd(condition);
_stack.add(new _ConditionalContext(conditionInfo));
_current = conditionInfo.ifTrue;
}
@override
void declare(Variable variable, bool initialized) {
- _current = _current.declare(variable, initialized);
+ _current = _current.declare(
+ variable, _promotionKeyStore.keyForVariable(variable), initialized);
}
@override
void doStatement_bodyBegin(Statement doStatement) {
- AssignedVariablesNodeInfo<Variable> info =
+ AssignedVariablesNodeInfo info =
_assignedVariables._getInfoForNode(doStatement);
- _BranchTargetContext<Variable, Type> context =
- new _BranchTargetContext<Variable, Type>(_current.reachable);
+ _BranchTargetContext<Type> context =
+ new _BranchTargetContext<Type>(_current.reachable);
_stack.add(context);
_current = _current.conservativeJoin(info._written, info._captured).split();
_statementToContext[doStatement] = context;
@@ -3639,39 +3625,34 @@
@override
void doStatement_conditionBegin() {
- _BranchTargetContext<Variable, Type> context =
- _stack.last as _BranchTargetContext<Variable, Type>;
+ _BranchTargetContext<Type> context =
+ _stack.last as _BranchTargetContext<Type>;
_current = _join(_current, context._continueModel);
}
@override
void doStatement_end(Expression condition) {
- _BranchTargetContext<Variable, Type> context =
- _stack.removeLast() as _BranchTargetContext<Variable, Type>;
+ _BranchTargetContext<Type> context =
+ _stack.removeLast() as _BranchTargetContext<Type>;
_current = _merge(_expressionEnd(condition).ifFalse, context._breakModel);
}
@override
- EqualityInfo<Variable, Type> equalityOperand_end(
- Expression operand, Type type) =>
- new EqualityInfo<Variable, Type>._(
+ EqualityInfo<Type> equalityOperand_end(Expression operand, Type type) =>
+ new EqualityInfo<Type>._(
_getExpressionInfo(operand), type, _getExpressionReference(operand));
@override
- void equalityOperation_end(
- Expression wholeExpression,
- EqualityInfo<Variable, Type>? leftOperandInfo,
- EqualityInfo<Variable, Type>? rightOperandInfo,
+ void equalityOperation_end(Expression wholeExpression,
+ EqualityInfo<Type>? leftOperandInfo, EqualityInfo<Type>? rightOperandInfo,
{bool notEqual = false}) {
// Note: leftOperandInfo and rightOperandInfo are nullable in the base class
// to account for the fact that legacy type promotion doesn't record
// information about legacy operands. But since we are currently in full
// (post null safety) flow analysis logic, we can safely assume that they
// are not null.
- ReferenceWithType<Variable, Type>? lhsReference =
- leftOperandInfo!._reference;
- ReferenceWithType<Variable, Type>? rhsReference =
- rightOperandInfo!._reference;
+ ReferenceWithType<Type>? lhsReference = leftOperandInfo!._reference;
+ ReferenceWithType<Type>? rhsReference = rightOperandInfo!._reference;
TypeClassification leftOperandTypeClassification =
operations.classifyType(leftOperandInfo._type);
TypeClassification rightOperandTypeClassification =
@@ -3689,15 +3670,15 @@
// but weak mode it might produce an "equal" result. We don't want flow
// analysis behavior to depend on mode, so we conservatively assume that
// either result is possible.
- } else if (leftOperandInfo._expressionInfo is _NullInfo<Variable, Type> &&
+ } else if (leftOperandInfo._expressionInfo is _NullInfo<Type> &&
rhsReference != null) {
- ExpressionInfo<Variable, Type> equalityInfo =
+ ExpressionInfo<Type> equalityInfo =
_current.tryMarkNonNullable(operations, rhsReference);
_storeExpressionInfo(
wholeExpression, notEqual ? equalityInfo : equalityInfo.invert());
- } else if (rightOperandInfo._expressionInfo is _NullInfo<Variable, Type> &&
+ } else if (rightOperandInfo._expressionInfo is _NullInfo<Type> &&
lhsReference != null) {
- ExpressionInfo<Variable, Type> equalityInfo =
+ ExpressionInfo<Type> equalityInfo =
_current.tryMarkNonNullable(operations, lhsReference);
_storeExpressionInfo(
wholeExpression, notEqual ? equalityInfo : equalityInfo.invert());
@@ -3705,7 +3686,7 @@
}
@override
- ExpressionInfo<Variable, Type>? expressionInfoForTesting(Expression target) =>
+ ExpressionInfo<Type>? expressionInfoForTesting(Expression target) =>
identical(target, _expressionWithInfo) ? _expressionInfo : null;
@override
@@ -3716,11 +3697,11 @@
@override
void for_bodyBegin(Statement? node, Expression? condition) {
- ExpressionInfo<Variable, Type> conditionInfo = condition == null
+ ExpressionInfo<Type> conditionInfo = condition == null
? new ExpressionInfo(_current, _current, _current.setUnreachable())
: _expressionEnd(condition);
- _WhileContext<Variable, Type> context = new _WhileContext<Variable, Type>(
- _current.reachable.parent!, conditionInfo);
+ _WhileContext<Type> context =
+ new _WhileContext<Type>(_current.reachable.parent!, conditionInfo);
_stack.add(context);
if (node != null) {
_statementToContext[node] = context;
@@ -3730,18 +3711,16 @@
@override
void for_conditionBegin(Node node) {
- AssignedVariablesNodeInfo<Variable> info =
- _assignedVariables._getInfoForNode(node);
+ AssignedVariablesNodeInfo info = _assignedVariables._getInfoForNode(node);
_current = _current.conservativeJoin(info._written, info._captured).split();
}
@override
void for_end() {
- _WhileContext<Variable, Type> context =
- _stack.removeLast() as _WhileContext<Variable, Type>;
+ _WhileContext<Type> context = _stack.removeLast() as _WhileContext<Type>;
// Tail of the stack: falseCondition, break
- FlowModel<Variable, Type>? breakState = context._breakModel;
- FlowModel<Variable, Type> falseCondition = context._conditionInfo.ifFalse;
+ FlowModel<Type>? breakState = context._breakModel;
+ FlowModel<Type> falseCondition = context._conditionInfo.ifFalse;
_current =
_merge(falseCondition, breakState).inheritTested(operations, _current);
@@ -3749,26 +3728,23 @@
@override
void for_updaterBegin() {
- _WhileContext<Variable, Type> context =
- _stack.last as _WhileContext<Variable, Type>;
+ _WhileContext<Type> context = _stack.last as _WhileContext<Type>;
_current = _join(_current, context._continueModel);
}
@override
void forEach_bodyBegin(Node node) {
- AssignedVariablesNodeInfo<Variable> info =
- _assignedVariables._getInfoForNode(node);
+ AssignedVariablesNodeInfo info = _assignedVariables._getInfoForNode(node);
_current = _current.conservativeJoin(info._written, info._captured).split();
- _SimpleStatementContext<Variable, Type> context =
- new _SimpleStatementContext<Variable, Type>(
- _current.reachable.parent!, _current);
+ _SimpleStatementContext<Type> context =
+ new _SimpleStatementContext<Type>(_current.reachable.parent!, _current);
_stack.add(context);
}
@override
void forEach_end() {
- _SimpleStatementContext<Variable, Type> context =
- _stack.removeLast() as _SimpleStatementContext<Variable, Type>;
+ _SimpleStatementContext<Type> context =
+ _stack.removeLast() as _SimpleStatementContext<Type>;
_current = _merge(_current, context._previous);
}
@@ -3784,8 +3760,7 @@
@override
void functionExpression_begin(Node node) {
- AssignedVariablesNodeInfo<Variable> info =
- _assignedVariables._getInfoForNode(node);
+ AssignedVariablesNodeInfo info = _assignedVariables._getInfoForNode(node);
_current = _current.conservativeJoin(const [], info._written);
_stack.add(new _FunctionExpressionContext(_current));
_current = _current.conservativeJoin(_assignedVariables._anywhere._written,
@@ -3794,14 +3769,14 @@
@override
void functionExpression_end() {
- _SimpleContext<Variable, Type> context =
- _stack.removeLast() as _FunctionExpressionContext<Variable, Type>;
+ _SimpleContext<Type> context =
+ _stack.removeLast() as _FunctionExpressionContext<Type>;
_current = context._previous;
}
@override
void handleBreak(Statement target) {
- _BranchTargetContext<Variable, Type>? context = _statementToContext[target];
+ _BranchTargetContext<Type>? context = _statementToContext[target];
if (context != null) {
context._breakModel =
_join(context._breakModel, _current.unsplitTo(context._checkpoint));
@@ -3811,7 +3786,7 @@
@override
void handleContinue(Statement target) {
- _BranchTargetContext<Variable, Type>? context = _statementToContext[target];
+ _BranchTargetContext<Type>? context = _statementToContext[target];
if (context != null) {
context._continueModel = _join(
context._continueModel, _current.unsplitTo(context._checkpoint));
@@ -3826,27 +3801,27 @@
@override
void ifNullExpression_end() {
- _IfNullExpressionContext<Variable, Type> context =
- _stack.removeLast() as _IfNullExpressionContext<Variable, Type>;
+ _IfNullExpressionContext<Type> context =
+ _stack.removeLast() as _IfNullExpressionContext<Type>;
_current = _merge(_current, context._previous);
}
@override
void ifNullExpression_rightBegin(
Expression leftHandSide, Type leftHandSideType) {
- ReferenceWithType<Variable, Type>? lhsReference =
+ ReferenceWithType<Type>? lhsReference =
_getExpressionReference(leftHandSide);
- FlowModel<Variable, Type> promoted;
+ FlowModel<Type> promoted;
_current = _current.split();
if (lhsReference != null) {
- ExpressionInfo<Variable, Type> promotionInfo =
+ ExpressionInfo<Type> promotionInfo =
_current.tryMarkNonNullable(operations, lhsReference);
_current = promotionInfo.ifFalse;
promoted = promotionInfo.ifTrue;
} else {
promoted = _current;
}
- _stack.add(new _IfNullExpressionContext<Variable, Type>(promoted));
+ _stack.add(new _IfNullExpressionContext<Type>(promoted));
}
@override
@@ -3856,18 +3831,16 @@
@override
void ifStatement_elseBegin() {
- _IfContext<Variable, Type> context =
- _stack.last as _IfContext<Variable, Type>;
+ _IfContext<Type> context = _stack.last as _IfContext<Type>;
context._afterThen = _current;
_current = context._conditionInfo!.ifFalse;
}
@override
void ifStatement_end(bool hasElse) {
- _IfContext<Variable, Type> context =
- _stack.removeLast() as _IfContext<Variable, Type>;
- FlowModel<Variable, Type> afterThen;
- FlowModel<Variable, Type> afterElse;
+ _IfContext<Type> context = _stack.removeLast() as _IfContext<Type>;
+ FlowModel<Type> afterThen;
+ FlowModel<Type> afterElse;
if (hasElse) {
afterThen = context._afterThen!;
afterElse = _current;
@@ -3880,7 +3853,7 @@
@override
void ifStatement_thenBegin(Expression condition, Node ifNode) {
- ExpressionInfo<Variable, Type> conditionInfo = _expressionEnd(condition);
+ ExpressionInfo<Type> conditionInfo = _expressionEnd(condition);
_stack.add(new _IfContext(conditionInfo));
_current = conditionInfo.ifTrue;
}
@@ -3891,7 +3864,8 @@
{required bool isFinal,
required bool isLate,
required bool isImplicitlyTyped}) {
- ExpressionInfo<Variable, Type>? expressionInfo;
+ int variableKey = _promotionKeyStore.keyForVariable(variable);
+ ExpressionInfo<Type>? expressionInfo;
if (isLate) {
// Don't get expression info for late variables, since we don't know when
// they'll be initialized.
@@ -3903,17 +3877,17 @@
} else {
expressionInfo = _getExpressionInfo(initializerExpression);
}
- SsaNode<Variable, Type> newSsaNode = new SsaNode<Variable, Type>(
+ SsaNode<Type> newSsaNode = new SsaNode<Type>(
expressionInfo is _TrivialExpressionInfo ? null : expressionInfo);
_current = _current.write(
- null, variable, initializerType, newSsaNode, operations,
+ null, variable, variableKey, initializerType, newSsaNode, operations,
promoteToTypeOfInterest: !isImplicitlyTyped && !isFinal);
if (isImplicitlyTyped && operations.isTypeParameterType(initializerType)) {
_current = _current
.tryPromoteForTypeCheck(
operations,
- new ReferenceWithType<Variable, Type>(
- new VariableReference<Variable, Type>(variable),
+ new ReferenceWithType<Type>(
+ new VariableReference<Variable, Type>(variable, variableKey),
promotedType(variable) ?? operations.variableType(variable)),
initializerType)
.ifTrue;
@@ -3922,17 +3896,19 @@
@override
bool isAssigned(Variable variable) {
- return _current.infoFor(variable).assigned;
+ return _current
+ .infoFor(_promotionKeyStore.keyForVariable(variable))
+ .assigned;
}
@override
void isExpression_end(Expression isExpression, Expression subExpression,
bool isNot, Type type) {
- ReferenceWithType<Variable, Type>? subExpressionReference =
+ ReferenceWithType<Type>? subExpressionReference =
_getExpressionReference(subExpression);
if (subExpressionReference != null) {
- ExpressionInfo<Variable, Type> expressionInfo = _current
- .tryPromoteForTypeCheck(operations, subExpressionReference, type);
+ ExpressionInfo<Type> expressionInfo = _current.tryPromoteForTypeCheck(
+ operations, subExpressionReference, type);
_storeExpressionInfo(
isExpression, isNot ? expressionInfo.invert() : expressionInfo);
}
@@ -3940,22 +3916,24 @@
@override
bool isUnassigned(Variable variable) {
- return _current.infoFor(variable).unassigned;
+ return _current
+ .infoFor(_promotionKeyStore.keyForVariable(variable))
+ .unassigned;
}
@override
void labeledStatement_begin(Statement node) {
_current = _current.split();
- _BranchTargetContext<Variable, Type> context =
- new _BranchTargetContext<Variable, Type>(_current.reachable.parent!);
+ _BranchTargetContext<Type> context =
+ new _BranchTargetContext<Type>(_current.reachable.parent!);
_stack.add(context);
_statementToContext[node] = context;
}
@override
void labeledStatement_end() {
- _BranchTargetContext<Variable, Type> context =
- _stack.removeLast() as _BranchTargetContext<Variable, Type>;
+ _BranchTargetContext<Type> context =
+ _stack.removeLast() as _BranchTargetContext<Type>;
_current = _merge(_current, context._breakModel);
}
@@ -3987,12 +3965,11 @@
@override
void logicalBinaryOp_end(Expression wholeExpression, Expression rightOperand,
{required bool isAnd}) {
- _BranchContext<Variable, Type> context =
- _stack.removeLast() as _BranchContext<Variable, Type>;
- ExpressionInfo<Variable, Type> rhsInfo = _expressionEnd(rightOperand);
+ _BranchContext<Type> context = _stack.removeLast() as _BranchContext<Type>;
+ ExpressionInfo<Type> rhsInfo = _expressionEnd(rightOperand);
- FlowModel<Variable, Type> trueResult;
- FlowModel<Variable, Type> falseResult;
+ FlowModel<Type> trueResult;
+ FlowModel<Type> falseResult;
if (isAnd) {
trueResult = rhsInfo.ifTrue;
falseResult = _join(context._conditionInfo!.ifFalse, rhsInfo.ifFalse);
@@ -4009,20 +3986,20 @@
@override
void logicalBinaryOp_rightBegin(Expression leftOperand, Node wholeExpression,
{required bool isAnd}) {
- ExpressionInfo<Variable, Type> conditionInfo = _expressionEnd(leftOperand);
- _stack.add(new _BranchContext<Variable, Type>(conditionInfo));
+ ExpressionInfo<Type> conditionInfo = _expressionEnd(leftOperand);
+ _stack.add(new _BranchContext<Type>(conditionInfo));
_current = isAnd ? conditionInfo.ifTrue : conditionInfo.ifFalse;
}
@override
void logicalNot_end(Expression notExpression, Expression operand) {
- ExpressionInfo<Variable, Type> conditionInfo = _expressionEnd(operand);
+ ExpressionInfo<Type> conditionInfo = _expressionEnd(operand);
_storeExpressionInfo(notExpression, conditionInfo.invert());
}
@override
void nonNullAssert_end(Expression operand) {
- ReferenceWithType<Variable, Type>? operandReference =
+ ReferenceWithType<Type>? operandReference =
_getExpressionReference(operand);
if (operandReference != null) {
_current =
@@ -4032,8 +4009,8 @@
@override
void nullAwareAccess_end() {
- _NullAwareAccessContext<Variable, Type> context =
- _stack.removeLast() as _NullAwareAccessContext<Variable, Type>;
+ _NullAwareAccessContext<Type> context =
+ _stack.removeLast() as _NullAwareAccessContext<Type>;
_current = _merge(_current, context._previous);
}
@@ -4042,9 +4019,8 @@
// ignore:unnecessary_null_comparison
assert(targetType != null);
_current = _current.split();
- _stack.add(new _NullAwareAccessContext<Variable, Type>(_current));
- ReferenceWithType<Variable, Type>? targetReference =
- _getExpressionReference(target);
+ _stack.add(new _NullAwareAccessContext<Type>(_current));
+ ReferenceWithType<Type>? targetReference = _getExpressionReference(target);
if (targetReference != null) {
_current =
_current.tryMarkNonNullable(operations, targetReference).ifTrue;
@@ -4064,32 +4040,35 @@
@override
Type? promotedType(Variable variable) {
- return _current.infoFor(variable).promotedTypes?.last;
+ return _current
+ .infoFor(_promotionKeyStore.keyForVariable(variable))
+ .promotedTypes
+ ?.last;
}
@override
void propertyGet(Expression wholeExpression, Expression target,
String propertyName, Object? propertyMember, Type staticType) {
- Reference<Variable, Type>? reference =
- _getExpressionReference(target)?.reference;
+ Reference<Type>? reference = _getExpressionReference(target)?.reference;
if (reference != null) {
_storeExpressionReference(
wholeExpression,
- new ReferenceWithType<Variable, Type>(
- reference.propertyGet(propertyName, propertyMember), staticType));
+ new ReferenceWithType<Type>(
+ reference.propertyGet(
+ _promotionKeyStore, propertyName, propertyMember),
+ staticType));
}
}
@override
- SsaNode<Variable, Type>? ssaNodeForTesting(Variable variable) =>
- _current.variableInfo[variable]?.ssaNode;
+ SsaNode<Type>? ssaNodeForTesting(Variable variable) => _current
+ .variableInfo[_promotionKeyStore.keyForVariable(variable)]?.ssaNode;
@override
void switchStatement_beginCase(bool hasLabel, Node node) {
- AssignedVariablesNodeInfo<Variable> info =
- _assignedVariables._getInfoForNode(node);
- _SimpleStatementContext<Variable, Type> context =
- _stack.last as _SimpleStatementContext<Variable, Type>;
+ AssignedVariablesNodeInfo info = _assignedVariables._getInfoForNode(node);
+ _SimpleStatementContext<Type> context =
+ _stack.last as _SimpleStatementContext<Type>;
if (hasLabel) {
_current =
context._previous.conservativeJoin(info._written, info._captured);
@@ -4100,9 +4079,9 @@
@override
void switchStatement_end(bool isExhaustive) {
- _SimpleStatementContext<Variable, Type> context =
- _stack.removeLast() as _SimpleStatementContext<Variable, Type>;
- FlowModel<Variable, Type>? breakState = context._breakModel;
+ _SimpleStatementContext<Type> context =
+ _stack.removeLast() as _SimpleStatementContext<Type>;
+ FlowModel<Type>? breakState = context._breakModel;
// It is allowed to "fall off" the end of a switch statement, so join the
// current state to any breaks that were found previously.
@@ -4117,9 +4096,8 @@
@override
void switchStatement_expressionEnd(Statement switchStatement) {
_current = _current.split();
- _SimpleStatementContext<Variable, Type> context =
- new _SimpleStatementContext<Variable, Type>(
- _current.reachable.parent!, _current);
+ _SimpleStatementContext<Type> context =
+ new _SimpleStatementContext<Type>(_current.reachable.parent!, _current);
_stack.add(context);
_statementToContext[switchStatement] = context;
}
@@ -4128,8 +4106,9 @@
void thisOrSuper(Expression expression, Type staticType) {
_storeExpressionReference(
expression,
- new ReferenceWithType<Variable, Type>(
- new _ThisReference<Variable, Type>(), staticType));
+ new ReferenceWithType<Type>(
+ new _ThisReference<Type>(_promotionKeyStore.thisPromotionKey),
+ staticType));
}
@override
@@ -4137,29 +4116,27 @@
Object? propertyMember, Type staticType) {
_storeExpressionReference(
expression,
- new ReferenceWithType<Variable, Type>(
- new _ThisReference<Variable, Type>()
- .propertyGet(propertyName, propertyMember),
+ new ReferenceWithType<Type>(
+ new _ThisReference<Type>(_promotionKeyStore.thisPromotionKey)
+ .propertyGet(_promotionKeyStore, propertyName, propertyMember),
staticType));
}
@override
void tryCatchStatement_bodyBegin() {
_current = _current.split();
- _stack.add(new _TryContext<Variable, Type>(_current));
+ _stack.add(new _TryContext<Type>(_current));
}
@override
void tryCatchStatement_bodyEnd(Node body) {
- FlowModel<Variable, Type> afterBody = _current;
+ FlowModel<Type> afterBody = _current;
- _TryContext<Variable, Type> context =
- _stack.last as _TryContext<Variable, Type>;
- FlowModel<Variable, Type> beforeBody = context._previous;
+ _TryContext<Type> context = _stack.last as _TryContext<Type>;
+ FlowModel<Type> beforeBody = context._previous;
- AssignedVariablesNodeInfo<Variable> info =
- _assignedVariables._getInfoForNode(body);
- FlowModel<Variable, Type> beforeCatch =
+ AssignedVariablesNodeInfo info = _assignedVariables._getInfoForNode(body);
+ FlowModel<Type> beforeCatch =
beforeBody.conservativeJoin(info._written, info._captured);
context._beforeCatch = beforeCatch;
@@ -4169,51 +4146,52 @@
@override
void tryCatchStatement_catchBegin(
Variable? exceptionVariable, Variable? stackTraceVariable) {
- _TryContext<Variable, Type> context =
- _stack.last as _TryContext<Variable, Type>;
+ _TryContext<Type> context = _stack.last as _TryContext<Type>;
_current = context._beforeCatch!;
if (exceptionVariable != null) {
- _current = _current.declare(exceptionVariable, true);
+ int exceptionVariableKey =
+ _promotionKeyStore.keyForVariable(exceptionVariable);
+ _current =
+ _current.declare(exceptionVariable, exceptionVariableKey, true);
}
if (stackTraceVariable != null) {
- _current = _current.declare(stackTraceVariable, true);
+ int stackTraceVariableKey =
+ _promotionKeyStore.keyForVariable(stackTraceVariable);
+ _current =
+ _current.declare(stackTraceVariable, stackTraceVariableKey, true);
}
}
@override
void tryCatchStatement_catchEnd() {
- _TryContext<Variable, Type> context =
- _stack.last as _TryContext<Variable, Type>;
+ _TryContext<Type> context = _stack.last as _TryContext<Type>;
context._afterBodyAndCatches =
_join(context._afterBodyAndCatches, _current);
}
@override
void tryCatchStatement_end() {
- _TryContext<Variable, Type> context =
- _stack.removeLast() as _TryContext<Variable, Type>;
+ _TryContext<Type> context = _stack.removeLast() as _TryContext<Type>;
_current = context._afterBodyAndCatches!.unsplit();
}
@override
void tryFinallyStatement_bodyBegin() {
- _stack.add(new _TryFinallyContext<Variable, Type>(_current));
+ _stack.add(new _TryFinallyContext<Type>(_current));
}
@override
void tryFinallyStatement_end() {
- _TryFinallyContext<Variable, Type> context =
- _stack.removeLast() as _TryFinallyContext<Variable, Type>;
+ _TryFinallyContext<Type> context =
+ _stack.removeLast() as _TryFinallyContext<Type>;
_current = context._afterBodyAndCatches!
.attachFinally(operations, context._beforeFinally, _current);
}
@override
void tryFinallyStatement_finallyBegin(Node body) {
- AssignedVariablesNodeInfo<Variable> info =
- _assignedVariables._getInfoForNode(body);
- _TryFinallyContext<Variable, Type> context =
- _stack.last as _TryFinallyContext<Variable, Type>;
+ AssignedVariablesNodeInfo info = _assignedVariables._getInfoForNode(body);
+ _TryFinallyContext<Type> context = _stack.last as _TryFinallyContext<Type>;
context._afterBodyAndCatches = _current;
_current = _join(_current,
context._previous.conservativeJoin(info._written, info._captured));
@@ -4222,16 +4200,16 @@
@override
Type? variableRead(Expression expression, Variable variable) {
+ int variableKey = _promotionKeyStore.keyForVariable(variable);
VariableReference<Variable, Type> variableReference =
- new VariableReference<Variable, Type>(variable);
- VariableModel<Variable, Type> variableModel =
+ new VariableReference<Variable, Type>(variable, variableKey);
+ VariableModel<Type> variableModel =
variableReference.getInfo(_current.variableInfo);
Type? promotedType = variableModel.promotedTypes?.last;
Type currentType = promotedType ?? operations.variableType(variable);
_storeExpressionReference(expression,
- new ReferenceWithType<Variable, Type>(variableReference, currentType));
- ExpressionInfo<Variable, Type>? expressionInfo = variableModel
- .ssaNode?.expressionInfo
+ new ReferenceWithType<Type>(variableReference, currentType));
+ ExpressionInfo<Type>? expressionInfo = variableModel.ssaNode?.expressionInfo
?.rebaseForward(operations, _current);
if (expressionInfo != null) {
_storeExpressionInfo(expression, expressionInfo);
@@ -4242,9 +4220,9 @@
@override
void whileStatement_bodyBegin(
Statement whileStatement, Expression condition) {
- ExpressionInfo<Variable, Type> conditionInfo = _expressionEnd(condition);
- _WhileContext<Variable, Type> context = new _WhileContext<Variable, Type>(
- _current.reachable.parent!, conditionInfo);
+ ExpressionInfo<Type> conditionInfo = _expressionEnd(condition);
+ _WhileContext<Type> context =
+ new _WhileContext<Type>(_current.reachable.parent!, conditionInfo);
_stack.add(context);
_statementToContext[whileStatement] = context;
_current = conditionInfo.ifTrue;
@@ -4253,15 +4231,13 @@
@override
void whileStatement_conditionBegin(Node node) {
_current = _current.split();
- AssignedVariablesNodeInfo<Variable> info =
- _assignedVariables._getInfoForNode(node);
+ AssignedVariablesNodeInfo info = _assignedVariables._getInfoForNode(node);
_current = _current.conservativeJoin(info._written, info._captured);
}
@override
void whileStatement_end() {
- _WhileContext<Variable, Type> context =
- _stack.removeLast() as _WhileContext<Variable, Type>;
+ _WhileContext<Type> context = _stack.removeLast() as _WhileContext<Type>;
_current = _merge(context._conditionInfo.ifFalse, context._breakModel)
.inheritTested(operations, _current);
}
@@ -4269,8 +4245,7 @@
@override
Map<Type, NonPromotionReason> Function() whyNotPromoted(Expression target) {
if (identical(target, _expressionWithReference)) {
- ReferenceWithType<Variable, Type>? referenceWithType =
- _expressionReference;
+ ReferenceWithType<Type>? referenceWithType = _expressionReference;
if (referenceWithType != null) {
return referenceWithType.reference.getNonPromotionReasons(
_current.variableInfo, referenceWithType.type, operations);
@@ -4282,21 +4257,23 @@
@override
Map<Type, NonPromotionReason> Function() whyNotPromotedImplicitThis(
Type staticType) {
- return new _ThisReference<Variable, Type>()
+ return new _ThisReference<Type>(_promotionKeyStore.thisPromotionKey)
.getNonPromotionReasons(_current.variableInfo, staticType, operations);
}
@override
void write(Node node, Variable variable, Type writtenType,
Expression? writtenExpression) {
- ExpressionInfo<Variable, Type>? expressionInfo = writtenExpression == null
+ int variableKey = _promotionKeyStore.keyForVariable(variable);
+ ExpressionInfo<Type>? expressionInfo = writtenExpression == null
? null
: _getExpressionInfo(writtenExpression);
- SsaNode<Variable, Type> newSsaNode = new SsaNode<Variable, Type>(
+ SsaNode<Type> newSsaNode = new SsaNode<Type>(
expressionInfo is _TrivialExpressionInfo ? null : expressionInfo);
_current = _current.write(
new DemoteViaExplicitWrite<Variable>(variable, node),
variable,
+ variableKey,
writtenType,
newSsaNode,
operations);
@@ -4319,16 +4296,16 @@
/// be the last expression that was traversed). If there is no
/// [ExpressionInfo] associated with the [expression], then a fresh
/// [ExpressionInfo] is created recording the current flow analysis state.
- ExpressionInfo<Variable, Type> _expressionEnd(Expression expression) =>
+ ExpressionInfo<Type> _expressionEnd(Expression expression) =>
_getExpressionInfo(expression) ?? new _TrivialExpressionInfo(_current);
/// Gets the [ExpressionInfo] associated with the [expression] (which should
/// be the last expression that was traversed). If there is no
/// [ExpressionInfo] associated with the [expression], then `null` is
/// returned.
- ExpressionInfo<Variable, Type>? _getExpressionInfo(Expression expression) {
+ ExpressionInfo<Type>? _getExpressionInfo(Expression expression) {
if (identical(expression, _expressionWithInfo)) {
- ExpressionInfo<Variable, Type>? expressionInfo = _expressionInfo;
+ ExpressionInfo<Type>? expressionInfo = _expressionInfo;
_expressionInfo = null;
return expressionInfo;
} else {
@@ -4339,11 +4316,9 @@
/// Gets the [Reference] associated with the [expression] (which should be the
/// last expression that was traversed). If there is no [Reference]
/// associated with the [expression], then `null` is returned.
- ReferenceWithType<Variable, Type>? _getExpressionReference(
- Expression? expression) {
+ ReferenceWithType<Type>? _getExpressionReference(Expression? expression) {
if (identical(expression, _expressionWithReference)) {
- ReferenceWithType<Variable, Type>? expressionReference =
- _expressionReference;
+ ReferenceWithType<Type>? expressionReference = _expressionReference;
_expressionReference = null;
return expressionReference;
} else {
@@ -4351,19 +4326,17 @@
}
}
- FlowModel<Variable, Type> _join(FlowModel<Variable, Type>? first,
- FlowModel<Variable, Type>? second) =>
+ FlowModel<Type> _join(FlowModel<Type>? first, FlowModel<Type>? second) =>
FlowModel.join(operations, first, second, _current._emptyVariableMap);
- FlowModel<Variable, Type> _merge(
- FlowModel<Variable, Type> first, FlowModel<Variable, Type>? second) =>
+ FlowModel<Type> _merge(FlowModel<Type> first, FlowModel<Type>? second) =>
FlowModel.merge(operations, first, second, _current._emptyVariableMap);
/// Associates [expression], which should be the most recently visited
/// expression, with the given [expressionInfo] object, and updates the
/// current flow model state to correspond to it.
void _storeExpressionInfo(
- Expression expression, ExpressionInfo<Variable, Type> expressionInfo) {
+ Expression expression, ExpressionInfo<Type> expressionInfo) {
_expressionWithInfo = expression;
_expressionInfo = expressionInfo;
_current = expressionInfo.after;
@@ -4371,8 +4344,8 @@
/// Associates [expression], which should be the most recently visited
/// expression, with the given [Reference] object.
- void _storeExpressionReference(Expression expression,
- ReferenceWithType<Variable, Type> expressionReference) {
+ void _storeExpressionReference(
+ Expression expression, ReferenceWithType<Type> expressionReference) {
_expressionWithReference = expression;
_expressionReference = expressionReference;
}
@@ -4383,8 +4356,8 @@
abstract class _FlowContext {}
/// [_FlowContext] representing a function expression.
-class _FunctionExpressionContext<Variable extends Object, Type extends Object>
- extends _SimpleContext<Variable, Type> {
+class _FunctionExpressionContext<Type extends Object>
+ extends _SimpleContext<Type> {
_FunctionExpressionContext(super.previous);
@override
@@ -4392,13 +4365,12 @@
}
/// [_FlowContext] representing an `if` statement.
-class _IfContext<Variable extends Object, Type extends Object>
- extends _BranchContext<Variable, Type> {
+class _IfContext<Type extends Object> extends _BranchContext<Type> {
/// Flow model associated with the state of program execution after the `if`
/// statement executes, in the circumstance where the "then" branch is taken.
- FlowModel<Variable, Type>? _afterThen;
+ FlowModel<Type>? _afterThen;
- _IfContext(ExpressionInfo<Variable, Type> super.conditionInfo);
+ _IfContext(ExpressionInfo<Type> super.conditionInfo);
@override
String toString() =>
@@ -4406,8 +4378,8 @@
}
/// [_FlowContext] representing an "if-null" (`??`) expression.
-class _IfNullExpressionContext<Variable extends Object, Type extends Object>
- extends _SimpleContext<Variable, Type> {
+class _IfNullExpressionContext<Type extends Object>
+ extends _SimpleContext<Type> {
_IfNullExpressionContext(super.previous);
@override
@@ -4416,14 +4388,14 @@
/// Contextual information tracked by legacy type promotion about a binary "and"
/// expression (`&&`).
-class _LegacyBinaryAndContext<Variable extends Object, Type extends Object>
- extends _LegacyContext<Variable, Type> {
+class _LegacyBinaryAndContext<Type extends Object>
+ extends _LegacyContext<Type> {
/// Types that were shown by the LHS of the "and" expression.
- final Map<Variable, Type> _lhsShownTypes;
+ final Map<int, Type> _lhsShownTypes;
/// Information about variables that might be assigned by the RHS of the "and"
/// expression.
- final AssignedVariablesNodeInfo<Variable> _assignedVariablesInfoForRhs;
+ final AssignedVariablesNodeInfo _assignedVariablesInfoForRhs;
_LegacyBinaryAndContext(super.previousKnownTypes, this._lhsShownTypes,
this._assignedVariablesInfoForRhs);
@@ -4431,22 +4403,22 @@
/// Contextual information tracked by legacy type promotion about a statement or
/// expression.
-class _LegacyContext<Variable, Type> {
+class _LegacyContext<Type> {
/// The set of known types in effect before the statement or expression in
/// question was encountered.
- final Map<Variable, Type> _previousKnownTypes;
+ final Map<int, Type> _previousKnownTypes;
_LegacyContext(this._previousKnownTypes);
}
/// Data tracked by legacy type promotion about an expression.
-class _LegacyExpressionInfo<Variable, Type> {
+class _LegacyExpressionInfo<Type> {
/// Variables whose types are "shown" by the expression in question.
///
/// For example, the spec says that the expression `x is T` "shows" `x` to
/// have type `T`, so accordingly, the [_LegacyExpressionInfo] for `x is T`
/// will have an entry in this map that maps `x` to `T`.
- final Map<Variable, Type> _shownTypes;
+ final Map<int, Type> _shownTypes;
_LegacyExpressionInfo(this._shownTypes);
@@ -4474,14 +4446,14 @@
/// If [_expressionWithInfo] is not `null`, the [_LegacyExpressionInfo] object
/// corresponding to it. Otherwise `null`.
- _LegacyExpressionInfo<Variable, Type>? _expressionInfo;
+ _LegacyExpressionInfo<Type>? _expressionInfo;
/// The set of type promotions currently in effect.
- Map<Variable, Type> _knownTypes = {};
+ Map<int, Type> _knownTypes = {};
/// Stack of [_LegacyContext] objects representing the statements and
/// expressions that are currently being visited.
- final List<_LegacyContext<Variable, Type>> _contextStack = [];
+ final List<_LegacyContext<Type>> _contextStack = [];
/// Stack for tracking writes occurring on the LHS of a binary "and" (`&&`)
/// operation. Whenever we visit a write, we update the top entry in this
@@ -4489,9 +4461,12 @@
/// a fresh empty entry onto this stack; accordingly, upon reaching the RHS of
/// the binary "and", the top entry of the stack contains the set of variables
/// written to during the LHS of the "and".
- final List<Set<Variable>> _writeStackForAnd = [{}];
+ final List<Set<int>> _writeStackForAnd = [{}];
- _LegacyTypePromotion(this._operations, this._assignedVariables);
+ final PromotionKeyStore<Variable> _promotionKeyStore;
+
+ _LegacyTypePromotion(this._operations, this._assignedVariables)
+ : _promotionKeyStore = _assignedVariables._promotionKeyStore;
@override
bool get isReachable => true;
@@ -4544,19 +4519,16 @@
void doStatement_end(Expression condition) {}
@override
- EqualityInfo<Variable, Type>? equalityOperand_end(
- Expression operand, Type type) =>
+ EqualityInfo<Type>? equalityOperand_end(Expression operand, Type type) =>
null;
@override
- void equalityOperation_end(
- Expression wholeExpression,
- EqualityInfo<Variable, Type>? leftOperandInfo,
- EqualityInfo<Variable, Type>? rightOperandInfo,
+ void equalityOperation_end(Expression wholeExpression,
+ EqualityInfo<Type>? leftOperandInfo, EqualityInfo<Type>? rightOperandInfo,
{bool notEqual = false}) {}
@override
- ExpressionInfo<Variable, Type>? expressionInfoForTesting(Expression target) {
+ ExpressionInfo<Type>? expressionInfoForTesting(Expression target) {
throw new StateError(
'expressionInfoForTesting requires null-aware flow analysis');
}
@@ -4648,19 +4620,18 @@
@override
void isExpression_end(Expression isExpression, Expression subExpression,
bool isNot, Type type) {
- _LegacyExpressionInfo<Variable, Type>? expressionInfo =
+ _LegacyExpressionInfo<Type>? expressionInfo =
_getExpressionInfo(subExpression);
if (!isNot && expressionInfo is _LegacyVariableReadInfo<Variable, Type>) {
Variable variable = expressionInfo._variable;
+ int variableKey = expressionInfo._variableKey;
Type currentType =
- _knownTypes[variable] ?? _operations.variableType(variable);
+ _knownTypes[variableKey] ?? _operations.variableType(variable);
Type? promotedType = _operations.tryPromoteToType(type, currentType);
if (promotedType != null &&
!_operations.isSameType(currentType, promotedType)) {
- _storeExpressionInfo(
- isExpression,
- new _LegacyExpressionInfo<Variable, Type>(
- {variable: promotedType}));
+ _storeExpressionInfo(isExpression,
+ new _LegacyExpressionInfo<Type>({variableKey: promotedType}));
}
}
}
@@ -4691,13 +4662,13 @@
void logicalBinaryOp_end(Expression wholeExpression, Expression rightOperand,
{required bool isAnd}) {
if (!isAnd) return;
- _LegacyBinaryAndContext<Variable, Type> context =
- _contextStack.removeLast() as _LegacyBinaryAndContext<Variable, Type>;
+ _LegacyBinaryAndContext<Type> context =
+ _contextStack.removeLast() as _LegacyBinaryAndContext<Type>;
_knownTypes = context._previousKnownTypes;
- AssignedVariablesNodeInfo<Variable> assignedVariablesInfoForRhs =
+ AssignedVariablesNodeInfo assignedVariablesInfoForRhs =
context._assignedVariablesInfoForRhs;
- Map<Variable, Type> lhsShownTypes = context._lhsShownTypes;
- Map<Variable, Type> rhsShownTypes =
+ Map<int, Type> lhsShownTypes = context._lhsShownTypes;
+ Map<int, Type> rhsShownTypes =
_getExpressionInfo(rightOperand)?._shownTypes ?? {};
// A logical boolean expression b of the form `e1 && e2` shows that a local
// variable v has type T if both of the following conditions hold:
@@ -4715,12 +4686,12 @@
// type T2? The de facto behavior we have had for a long time is to combine
// the two types in the same way we would combine it if c were first
// promoted to T1 and then had a successful `is T2` check.
- Map<Variable, Type> newShownTypes = {};
- for (MapEntry<Variable, Type> entry in lhsShownTypes.entries) {
+ Map<int, Type> newShownTypes = {};
+ for (MapEntry<int, Type> entry in lhsShownTypes.entries) {
if (assignedVariablesInfoForRhs._written.contains(entry.key)) continue;
newShownTypes[entry.key] = entry.value;
}
- for (MapEntry<Variable, Type> entry in rhsShownTypes.entries) {
+ for (MapEntry<int, Type> entry in rhsShownTypes.entries) {
if (assignedVariablesInfoForRhs._written.contains(entry.key)) continue;
Type? previouslyShownType = newShownTypes[entry.key];
if (previouslyShownType == null) {
@@ -4734,24 +4705,24 @@
}
}
}
- _storeExpressionInfo(wholeExpression,
- new _LegacyExpressionInfo<Variable, Type>(newShownTypes));
+ _storeExpressionInfo(
+ wholeExpression, new _LegacyExpressionInfo<Type>(newShownTypes));
}
@override
void logicalBinaryOp_rightBegin(Expression leftOperand, Node wholeExpression,
{required bool isAnd}) {
- Set<Variable> variablesWrittenOnLhs = _writeStackForAnd.removeLast();
+ Set<int> variablesWrittenOnLhs = _writeStackForAnd.removeLast();
_writeStackForAnd.last.addAll(variablesWrittenOnLhs);
if (!isAnd) return;
- AssignedVariablesNodeInfo<Variable> info =
+ AssignedVariablesNodeInfo info =
_assignedVariables._getInfoForNode(wholeExpression);
- Map<Variable, Type> lhsShownTypes =
+ Map<int, Type> lhsShownTypes =
_getExpressionInfo(leftOperand)?._shownTypes ?? {};
- _contextStack.add(new _LegacyBinaryAndContext<Variable, Type>(
- _knownTypes, lhsShownTypes, info));
- Map<Variable, Type>? newKnownTypes;
- for (MapEntry<Variable, Type> entry in lhsShownTypes.entries) {
+ _contextStack.add(
+ new _LegacyBinaryAndContext<Type>(_knownTypes, lhsShownTypes, info));
+ Map<int, Type>? newKnownTypes;
+ for (MapEntry<int, Type> entry in lhsShownTypes.entries) {
// Given a statement of the form `e1 && e2`, if e1 shows that a
// local variable v has type T, then the type of v is known to be T in
// e2, unless any of the following are true:
@@ -4770,7 +4741,7 @@
_assignedVariables._anywhere._written.contains(entry.key)) {
continue;
}
- (newKnownTypes ??= new Map<Variable, Type>.of(_knownTypes))[entry.key] =
+ (newKnownTypes ??= new Map<int, Type>.of(_knownTypes))[entry.key] =
entry.value;
}
if (newKnownTypes != null) _knownTypes = newKnownTypes;
@@ -4799,7 +4770,8 @@
@override
Type? promotedType(Variable variable) {
- return _knownTypes[variable];
+ int variableKey = _promotionKeyStore.keyForVariable(variable);
+ return _knownTypes[variableKey];
}
@override
@@ -4807,7 +4779,7 @@
String propertyName, Object? propertyMember, Type staticType) {}
@override
- SsaNode<Variable, Type>? ssaNodeForTesting(Variable variable) {
+ SsaNode<Type>? ssaNodeForTesting(Variable variable) {
throw new StateError('ssaNodeForTesting requires null-aware flow analysis');
}
@@ -4854,9 +4826,10 @@
@override
Type? variableRead(Expression expression, Variable variable) {
- _storeExpressionInfo(
- expression, new _LegacyVariableReadInfo<Variable, Type>(variable));
- return _knownTypes[variable];
+ int variableKey = _promotionKeyStore.keyForVariable(variable);
+ _storeExpressionInfo(expression,
+ new _LegacyVariableReadInfo<Variable, Type>(variable, variableKey));
+ return _knownTypes[variableKey];
}
@override
@@ -4883,19 +4856,17 @@
@override
void write(Node node, Variable variable, Type writtenType,
Expression? writtenExpression) {
- _writeStackForAnd.last.add(variable);
+ int variableKey = _promotionKeyStore.keyForVariable(variable);
+ _writeStackForAnd.last.add(variableKey);
}
void _conditionalOrIf_thenBegin(Expression condition, Node node) {
- _contextStack.add(new _LegacyContext<Variable, Type>(_knownTypes));
- AssignedVariablesNodeInfo<Variable> info =
- _assignedVariables._getInfoForNode(node);
- Map<Variable, Type>? newKnownTypes;
- _LegacyExpressionInfo<Variable, Type>? expressionInfo =
- _getExpressionInfo(condition);
+ _contextStack.add(new _LegacyContext<Type>(_knownTypes));
+ AssignedVariablesNodeInfo info = _assignedVariables._getInfoForNode(node);
+ Map<int, Type>? newKnownTypes;
+ _LegacyExpressionInfo<Type>? expressionInfo = _getExpressionInfo(condition);
if (expressionInfo != null) {
- for (MapEntry<Variable, Type> entry
- in expressionInfo._shownTypes.entries) {
+ for (MapEntry<int, Type> entry in expressionInfo._shownTypes.entries) {
// Given an expression of the form n1?n2:n3 or a statement of the form
// `if (n1) n2 else n3`, if n1 shows that a local variable v has type T,
// then the type of v is known to be T in n2, unless any of the
@@ -4913,7 +4884,7 @@
_assignedVariables._anywhere._written.contains(entry.key)) {
continue;
}
- (newKnownTypes ??= new Map<Variable, Type>.of(_knownTypes))[entry.key] =
+ (newKnownTypes ??= new Map<int, Type>.of(_knownTypes))[entry.key] =
entry.value;
}
if (newKnownTypes != null) _knownTypes = newKnownTypes;
@@ -4926,21 +4897,20 @@
print(' expressionWithInfo: $_expressionWithInfo');
print(' expressionInfo: $_expressionInfo');
print(' contextStack:');
- for (_LegacyContext<Variable, Type> stackEntry in _contextStack.reversed) {
+ for (_LegacyContext<Type> stackEntry in _contextStack.reversed) {
print(' $stackEntry');
}
print(' writeStackForAnd:');
- for (Set<Variable> stackEntry in _writeStackForAnd.reversed) {
+ for (Set<int> stackEntry in _writeStackForAnd.reversed) {
print(' $stackEntry');
}
}
/// Gets the [_LegacyExpressionInfo] associated with [expression], if any;
/// otherwise returns `null`.
- _LegacyExpressionInfo<Variable, Type>? _getExpressionInfo(
- Expression expression) {
+ _LegacyExpressionInfo<Type>? _getExpressionInfo(Expression expression) {
if (identical(expression, _expressionWithInfo)) {
- _LegacyExpressionInfo<Variable, Type>? expressionInfo = _expressionInfo;
+ _LegacyExpressionInfo<Type>? expressionInfo = _expressionInfo;
_expressionInfo = null;
return expressionInfo;
} else {
@@ -4950,8 +4920,8 @@
/// Associates [expressionInfo] with [expression] for use by a future call to
/// [_getExpressionInfo].
- void _storeExpressionInfo(Expression expression,
- _LegacyExpressionInfo<Variable, Type> expressionInfo) {
+ void _storeExpressionInfo(
+ Expression expression, _LegacyExpressionInfo<Type> expressionInfo) {
_expressionWithInfo = expression;
_expressionInfo = expressionInfo;
}
@@ -4960,22 +4930,25 @@
/// Data tracked by legacy type promotion about an expression that reads the
/// value of a local variable.
class _LegacyVariableReadInfo<Variable, Type>
- implements _LegacyExpressionInfo<Variable, Type> {
+ implements _LegacyExpressionInfo<Type> {
/// The variable being referred to.
final Variable _variable;
- _LegacyVariableReadInfo(this._variable);
+ /// The variable's corresponding key, as assigned by [PromotionKeyStore].
+ final int _variableKey;
+
+ _LegacyVariableReadInfo(this._variable, this._variableKey);
@override
- Map<Variable, Type> get _shownTypes => {};
+ Map<int, Type> get _shownTypes => {};
@override
String toString() => 'LegacyVariableReadInfo($_variable, $_shownTypes)';
}
/// [_FlowContext] representing a null aware access (`?.`).
-class _NullAwareAccessContext<Variable extends Object, Type extends Object>
- extends _SimpleContext<Variable, Type> {
+class _NullAwareAccessContext<Type extends Object>
+ extends _SimpleContext<Type> {
_NullAwareAccessContext(super.previous);
@override
@@ -4983,21 +4956,20 @@
}
/// [ExpressionInfo] representing a `null` literal.
-class _NullInfo<Variable extends Object, Type extends Object>
- implements ExpressionInfo<Variable, Type> {
+class _NullInfo<Type extends Object> implements ExpressionInfo<Type> {
@override
- final FlowModel<Variable, Type> after;
+ final FlowModel<Type> after;
_NullInfo(this.after);
@override
- FlowModel<Variable, Type> get ifFalse => after;
+ FlowModel<Type> get ifFalse => after;
@override
- FlowModel<Variable, Type> get ifTrue => after;
+ FlowModel<Type> get ifTrue => after;
@override
- ExpressionInfo<Variable, Type> invert() {
+ ExpressionInfo<Type> invert() {
// This should only happen if `!null` is encountered. That should never
// happen for a properly typed program, but we need to handle it so we can
// give reasonable errors for an improperly typed program.
@@ -5005,20 +4977,13 @@
}
@override
- ExpressionInfo<Variable, Type>? rebaseForward(
- TypeOperations<Type> typeOperations,
- FlowModel<Variable, Type> base) =>
+ ExpressionInfo<Type>? rebaseForward(
+ TypeOperations<Type> typeOperations, FlowModel<Type> base) =>
null;
}
/// [Reference] object representing a property get applied to another reference.
-class _PropertyGetReference<Variable extends Object, Type extends Object>
- extends Reference<Variable, Type> {
- /// The target of the property get. For example a property get of the form
- /// `a.b`, where `a` is a local variable, has a target which is a reference to
- /// `a`.
- final Reference<Variable, Type> target;
-
+class _PropertyGetReference<Type extends Object> extends Reference<Type> {
/// The name of the property.
final String propertyName;
@@ -5027,11 +4992,12 @@
/// [FlowAnalysis.thisOrSuperPropertyGet].
final Object? propertyMember;
- _PropertyGetReference(this.target, this.propertyName, this.propertyMember);
+ _PropertyGetReference(
+ this.propertyName, this.propertyMember, super.promotionKey);
@override
Map<Type, NonPromotionReason> Function() getNonPromotionReasons(
- Map<Variable?, VariableModel<Variable, Type>> variableInfo,
+ Map<int, VariableModel<Type>> variableInfo,
Type staticType,
TypeOperations<Type> typeOperations) {
List<Type>? promotedTypes = _getInfo(variableInfo)?.promotedTypes;
@@ -5049,39 +5015,20 @@
}
@override
- void storeInfo(Map<Variable?, VariableModel<Variable, Type>> variableInfo,
- VariableModel<Variable, Type> variableModel) {
- VariableModel<Variable, Type> targetInfo = target.getInfo(variableInfo);
- Map<String, VariableModel<Variable, Type>> newProperties =
- new Map<String, VariableModel<Variable, Type>>.of(
- targetInfo.properties);
- newProperties[propertyName] = variableModel;
- target.storeInfo(variableInfo, targetInfo.setProperties(newProperties));
- }
-
- @override
String toString() =>
- '_PropertyGetReference($target, $propertyName, $propertyMember)';
-
- @override
- VariableModel<Variable, Type>? _getInfo(
- Map<Variable?, VariableModel<Variable, Type>> variableInfo) {
- VariableModel<Variable, Type> targetInfo = target.getInfo(variableInfo);
- return targetInfo.properties[propertyName];
- }
+ '_PropertyGetReference($propertyName, $propertyMember, $promotionKey)';
}
/// [_FlowContext] representing a language construct for which flow analysis
/// must store a flow model state to be retrieved later, such as a `try`
/// statement, function expression, or "if-null" (`??`) expression.
-abstract class _SimpleContext<Variable extends Object, Type extends Object>
- extends _FlowContext {
+abstract class _SimpleContext<Type extends Object> extends _FlowContext {
/// The stored state. For a `try` statement, this is the state from the
/// beginning of the `try` block. For a function expression, this is the
/// state at the point the function expression was created. For an "if-null"
/// expression, this is the state after execution of the expression before the
/// `??`.
- final FlowModel<Variable, Type> _previous;
+ final FlowModel<Type> _previous;
_SimpleContext(this._previous);
}
@@ -5090,12 +5037,12 @@
/// `break` or `continue` statements, and for which flow analysis must store a
/// flow model state to be retrieved later. Examples include "for each" and
/// `switch` statements.
-class _SimpleStatementContext<Variable extends Object, Type extends Object>
- extends _BranchTargetContext<Variable, Type> {
+class _SimpleStatementContext<Type extends Object>
+ extends _BranchTargetContext<Type> {
/// The stored state. For a "for each" statement, this is the state after
/// evaluation of the iterable. For a `switch` statement, this is the state
/// after evaluation of the switch expression.
- final FlowModel<Variable, Type> _previous;
+ final FlowModel<Type> _previous;
_SimpleStatementContext(super.checkpoint, this._previous);
@@ -5106,11 +5053,12 @@
}
/// [Reference] object representing an implicit or explicit reference to `this`.
-class _ThisReference<Variable extends Object, Type extends Object>
- extends Reference<Variable, Type> {
+class _ThisReference<Type extends Object> extends Reference<Type> {
+ _ThisReference(super.promotionKey);
+
@override
Map<Type, NonPromotionReason> Function() getNonPromotionReasons(
- Map<Variable?, VariableModel<Variable, Type>> variableInfo,
+ Map<int, VariableModel<Type>> variableInfo,
Type staticType,
TypeOperations<Type> typeOperations) {
List<Type>? promotedTypes = _getInfo(variableInfo)?.promotedTypes;
@@ -5125,58 +5073,45 @@
}
return () => {};
}
-
- @override
- void storeInfo(Map<Variable?, VariableModel<Variable, Type>> variableInfo,
- VariableModel<Variable, Type> variableModel) {
- variableInfo[null] = variableModel;
- }
-
- @override
- VariableModel<Variable, Type>? _getInfo(
- Map<Variable?, VariableModel<Variable, Type>> variableInfo) =>
- variableInfo[null];
}
/// Specialization of [ExpressionInfo] for the case where the information we
/// have about the expression is trivial (meaning we know by construction that
/// the expression's [after], [ifTrue], and [ifFalse] models are all the same).
-class _TrivialExpressionInfo<Variable extends Object, Type extends Object>
- implements ExpressionInfo<Variable, Type> {
+class _TrivialExpressionInfo<Type extends Object>
+ implements ExpressionInfo<Type> {
@override
- final FlowModel<Variable, Type> after;
+ final FlowModel<Type> after;
_TrivialExpressionInfo(this.after);
@override
- FlowModel<Variable, Type> get ifFalse => after;
+ FlowModel<Type> get ifFalse => after;
@override
- FlowModel<Variable, Type> get ifTrue => after;
+ FlowModel<Type> get ifTrue => after;
@override
- ExpressionInfo<Variable, Type> invert() => this;
+ ExpressionInfo<Type> invert() => this;
@override
- ExpressionInfo<Variable, Type> rebaseForward(
- TypeOperations<Type> typeOperations,
- FlowModel<Variable, Type> base) =>
+ ExpressionInfo<Type> rebaseForward(
+ TypeOperations<Type> typeOperations, FlowModel<Type> base) =>
new _TrivialExpressionInfo(base);
}
/// [_FlowContext] representing a try statement.
-class _TryContext<Variable extends Object, Type extends Object>
- extends _SimpleContext<Variable, Type> {
+class _TryContext<Type extends Object> extends _SimpleContext<Type> {
/// If the statement is a "try/catch" statement, the flow model representing
/// program state at the top of any `catch` block.
- FlowModel<Variable, Type>? _beforeCatch;
+ FlowModel<Type>? _beforeCatch;
/// If the statement is a "try/catch" statement, the accumulated flow model
/// representing program state after the `try` block or one of the `catch`
/// blocks has finished executing. If the statement is a "try/finally"
/// statement, the flow model representing program state after the `try` block
/// has finished executing.
- FlowModel<Variable, Type>? _afterBodyAndCatches;
+ FlowModel<Type>? _afterBodyAndCatches;
_TryContext(super.previous);
@@ -5186,21 +5121,19 @@
'afterBodyAndCatches: $_afterBodyAndCatches)';
}
-class _TryFinallyContext<Variable extends Object, Type extends Object>
- extends _TryContext<Variable, Type> {
+class _TryFinallyContext<Type extends Object> extends _TryContext<Type> {
/// The flow model representing program state at the top of the `finally`
/// block.
- late final FlowModel<Variable, Type> _beforeFinally;
+ late final FlowModel<Type> _beforeFinally;
_TryFinallyContext(super.previous);
}
/// [_FlowContext] representing a `while` loop (or a C-style `for` loop, which
/// is functionally similar).
-class _WhileContext<Variable extends Object, Type extends Object>
- extends _BranchTargetContext<Variable, Type> {
+class _WhileContext<Type extends Object> extends _BranchTargetContext<Type> {
/// Flow models associated with the loop condition.
- final ExpressionInfo<Variable, Type> _conditionInfo;
+ final ExpressionInfo<Type> _conditionInfo;
_WhileContext(super.checkpoint, this._conditionInfo);
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables_test.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables_test.dart
index 8676f23..f3ab112 100644
--- a/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables_test.dart
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables_test.dart
@@ -21,7 +21,8 @@
isClosureOrLateVariableInitializer: true);
assignedVariables.read(v3);
assignedVariables.finish();
- expect(assignedVariables.readCapturedAnywhere, {v2});
+ expect(assignedVariables.readCapturedAnywhere,
+ {assignedVariables.keyForVariable(v2)});
});
test('readCapturedAnywhere does not record variables local to a closure', () {
@@ -36,7 +37,8 @@
assignedVariables.endNode(_Node(),
isClosureOrLateVariableInitializer: true);
assignedVariables.finish();
- expect(assignedVariables.readCapturedAnywhere, {v1});
+ expect(assignedVariables.readCapturedAnywhere,
+ {assignedVariables.keyForVariable(v1)});
});
test('capturedAnywhere records assignments in closures', () {
@@ -54,7 +56,8 @@
isClosureOrLateVariableInitializer: true);
assignedVariables.write(v3);
assignedVariables.finish();
- expect(assignedVariables.capturedAnywhere, {v2});
+ expect(assignedVariables.capturedAnywhere,
+ {assignedVariables.keyForVariable(v2)});
});
test('capturedAnywhere does not record variables local to a closure', () {
@@ -69,7 +72,8 @@
assignedVariables.endNode(_Node(),
isClosureOrLateVariableInitializer: true);
assignedVariables.finish();
- expect(assignedVariables.capturedAnywhere, {v1});
+ expect(assignedVariables.capturedAnywhere,
+ {assignedVariables.keyForVariable(v1)});
});
test('readAnywhere records all reads', () {
@@ -87,7 +91,11 @@
isClosureOrLateVariableInitializer: true);
assignedVariables.read(v3);
assignedVariables.finish();
- expect(assignedVariables.readAnywhere, {v1, v2, v3});
+ expect(assignedVariables.readAnywhere, {
+ assignedVariables.keyForVariable(v1),
+ assignedVariables.keyForVariable(v2),
+ assignedVariables.keyForVariable(v3)
+ });
});
test('writtenAnywhere records all assignments', () {
@@ -105,7 +113,11 @@
isClosureOrLateVariableInitializer: true);
assignedVariables.write(v3);
assignedVariables.finish();
- expect(assignedVariables.writtenAnywhere, {v1, v2, v3});
+ expect(assignedVariables.writtenAnywhere, {
+ assignedVariables.keyForVariable(v1),
+ assignedVariables.keyForVariable(v2),
+ assignedVariables.keyForVariable(v3)
+ });
});
test('readInNode ignores reads outside the node', () {
@@ -132,7 +144,8 @@
var node = _Node();
assignedVariables.endNode(node);
assignedVariables.finish();
- expect(assignedVariables.readInNode(node), {v1});
+ expect(assignedVariables.readInNode(node),
+ {assignedVariables.keyForVariable(v1)});
});
test('readInNode records reads in a nested node', () {
@@ -146,7 +159,8 @@
var node = _Node();
assignedVariables.endNode(node);
assignedVariables.finish();
- expect(assignedVariables.readInNode(node), {v1});
+ expect(assignedVariables.readInNode(node),
+ {assignedVariables.keyForVariable(v1)});
});
test('readInNode records reads in a closure', () {
@@ -158,7 +172,8 @@
var node = _Node();
assignedVariables.endNode(node, isClosureOrLateVariableInitializer: true);
assignedVariables.finish();
- expect(assignedVariables.readInNode(node), {v1});
+ expect(assignedVariables.readInNode(node),
+ {assignedVariables.keyForVariable(v1)});
});
test('writtenInNode ignores assignments outside the node', () {
@@ -185,7 +200,8 @@
var node = _Node();
assignedVariables.endNode(node);
assignedVariables.finish();
- expect(assignedVariables.writtenInNode(node), {v1});
+ expect(assignedVariables.writtenInNode(node),
+ {assignedVariables.keyForVariable(v1)});
});
test('writtenInNode records assignments in a nested node', () {
@@ -199,7 +215,8 @@
var node = _Node();
assignedVariables.endNode(node);
assignedVariables.finish();
- expect(assignedVariables.writtenInNode(node), {v1});
+ expect(assignedVariables.writtenInNode(node),
+ {assignedVariables.keyForVariable(v1)});
});
test('writtenInNode records assignments in a closure', () {
@@ -211,7 +228,8 @@
var node = _Node();
assignedVariables.endNode(node, isClosureOrLateVariableInitializer: true);
assignedVariables.finish();
- expect(assignedVariables.writtenInNode(node), {v1});
+ expect(assignedVariables.writtenInNode(node),
+ {assignedVariables.keyForVariable(v1)});
});
test('readCapturedInNode ignores reads in non-nested closures', () {
@@ -250,8 +268,10 @@
var outerNode = _Node();
assignedVariables.endNode(outerNode);
assignedVariables.finish();
- expect(assignedVariables.readCapturedInNode(innerNode), {v1});
- expect(assignedVariables.readCapturedInNode(outerNode), {v1});
+ expect(assignedVariables.readCapturedInNode(innerNode),
+ {assignedVariables.keyForVariable(v1)});
+ expect(assignedVariables.readCapturedInNode(outerNode),
+ {assignedVariables.keyForVariable(v1)});
});
test('capturedInNode ignores assignments in non-nested closures', () {
@@ -290,8 +310,10 @@
var outerNode = _Node();
assignedVariables.endNode(outerNode);
assignedVariables.finish();
- expect(assignedVariables.capturedInNode(innerNode), {v1});
- expect(assignedVariables.capturedInNode(outerNode), {v1});
+ expect(assignedVariables.capturedInNode(innerNode),
+ {assignedVariables.keyForVariable(v1)});
+ expect(assignedVariables.capturedInNode(outerNode),
+ {assignedVariables.keyForVariable(v1)});
});
group('Variables do not percolate beyond the scope they were declared in',
@@ -371,7 +393,12 @@
assignedVariables.discardNode();
assignedVariables.endNode(node);
assignedVariables.finish();
- expect(assignedVariables.declaredInNode(node), unorderedEquals([v1, v2]));
+ expect(
+ assignedVariables.declaredInNode(node),
+ unorderedEquals([
+ assignedVariables.keyForVariable(v1),
+ assignedVariables.keyForVariable(v2)
+ ]));
});
test('discardNode percolates reads to enclosing node', () {
@@ -388,7 +415,12 @@
assignedVariables.discardNode();
assignedVariables.endNode(node);
assignedVariables.finish();
- expect(assignedVariables.readInNode(node), unorderedEquals([v1, v2]));
+ expect(
+ assignedVariables.readInNode(node),
+ unorderedEquals([
+ assignedVariables.keyForVariable(v1),
+ assignedVariables.keyForVariable(v2)
+ ]));
});
test('discardNode percolates writes to enclosing node', () {
@@ -405,7 +437,12 @@
assignedVariables.discardNode();
assignedVariables.endNode(node);
assignedVariables.finish();
- expect(assignedVariables.writtenInNode(node), unorderedEquals([v1, v2]));
+ expect(
+ assignedVariables.writtenInNode(node),
+ unorderedEquals([
+ assignedVariables.keyForVariable(v1),
+ assignedVariables.keyForVariable(v2)
+ ]));
});
test('discardNode percolates read captures to enclosing node', () {
@@ -426,7 +463,11 @@
assignedVariables.endNode(node);
assignedVariables.finish();
expect(
- assignedVariables.readCapturedInNode(node), unorderedEquals([v1, v2]));
+ assignedVariables.readCapturedInNode(node),
+ unorderedEquals([
+ assignedVariables.keyForVariable(v1),
+ assignedVariables.keyForVariable(v2)
+ ]));
});
test('discardNode percolates write captures to enclosing node', () {
@@ -446,7 +487,12 @@
assignedVariables.discardNode();
assignedVariables.endNode(node);
assignedVariables.finish();
- expect(assignedVariables.capturedInNode(node), unorderedEquals([v1, v2]));
+ expect(
+ assignedVariables.capturedInNode(node),
+ unorderedEquals([
+ assignedVariables.keyForVariable(v1),
+ assignedVariables.keyForVariable(v2)
+ ]));
});
test('deferNode allows deferring of node info', () {
@@ -472,9 +518,16 @@
var node = _Node();
assignedVariables.storeInfo(node, info);
assignedVariables.finish();
- expect(assignedVariables.declaredInNode(node), [v3]);
- expect(assignedVariables.writtenInNode(node), unorderedEquals([v1, v2]));
- expect(assignedVariables.capturedInNode(node), [v2]);
+ expect(assignedVariables.declaredInNode(node),
+ [assignedVariables.keyForVariable(v3)]);
+ expect(
+ assignedVariables.writtenInNode(node),
+ unorderedEquals([
+ assignedVariables.keyForVariable(v1),
+ assignedVariables.keyForVariable(v2)
+ ]));
+ expect(assignedVariables.capturedInNode(node),
+ [assignedVariables.keyForVariable(v2)]);
});
}
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart
index a3700cd..d2195c0 100644
--- a/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart
@@ -13,7 +13,7 @@
test('asExpression_end promotes variables', () {
var h = Harness();
var x = Var('x', 'int?');
- late SsaNode<Var, Type> ssaBeforePromotion;
+ late SsaNode<Type> ssaBeforePromotion;
h.run([
declare(x, initialized: true),
getSsaNodes((nodes) => ssaBeforePromotion = nodes[x]!),
@@ -186,7 +186,7 @@
test('equalityOp(x != null) promotes true branch', () {
var h = Harness();
var x = Var('x', 'int?');
- late SsaNode<Var, Type> ssaBeforePromotion;
+ late SsaNode<Type> ssaBeforePromotion;
h.run([
declare(x, initialized: true),
getSsaNodes((nodes) => ssaBeforePromotion = nodes[x]!),
@@ -255,7 +255,7 @@
test('equalityOp(x == null) promotes false branch', () {
var h = Harness();
var x = Var('x', 'int?');
- late SsaNode<Var, Type> ssaBeforePromotion;
+ late SsaNode<Type> ssaBeforePromotion;
h.run([
declare(x, initialized: true),
getSsaNodes((nodes) => ssaBeforePromotion = nodes[x]!),
@@ -289,7 +289,7 @@
test('equalityOp(null != x) promotes true branch', () {
var h = Harness();
var x = Var('x', 'int?');
- late SsaNode<Var, Type> ssaBeforePromotion;
+ late SsaNode<Type> ssaBeforePromotion;
h.run([
declare(x, initialized: true),
getSsaNodes((nodes) => ssaBeforePromotion = nodes[x]!),
@@ -319,7 +319,7 @@
test('equalityOp(null == x) promotes false branch', () {
var h = Harness();
var x = Var('x', 'int?');
- late SsaNode<Var, Type> ssaBeforePromotion;
+ late SsaNode<Type> ssaBeforePromotion;
h.run([
declare(x, initialized: true),
getSsaNodes((nodes) => ssaBeforePromotion = nodes[x]!),
@@ -441,7 +441,7 @@
test('doStatement_bodyBegin() un-promotes', () {
var h = Harness();
var x = Var('x', 'int?');
- late SsaNode<Var, Type> ssaBeforeLoop;
+ late SsaNode<Type> ssaBeforeLoop;
h.run([
declare(x, initialized: true),
x.expr.as_('int').stmt,
@@ -608,7 +608,7 @@
test('for_conditionBegin() un-promotes', () {
var h = Harness();
var x = Var('x', 'int?');
- late SsaNode<Var, Type> ssaBeforeLoop;
+ late SsaNode<Type> ssaBeforeLoop;
h.run([
declare(x, initialized: true),
x.expr.as_('int').stmt,
@@ -750,8 +750,8 @@
var h = Harness();
var x = Var('x', 'int?');
var y = Var('x', 'int?');
- late SsaNode<Var, Type> xSsaInsideLoop;
- late SsaNode<Var, Type> ySsaInsideLoop;
+ late SsaNode<Type> xSsaInsideLoop;
+ late SsaNode<Type> ySsaInsideLoop;
h.run([
declare(x, initialized: true),
declare(y, initialized: true),
@@ -779,8 +779,8 @@
var h = Harness();
var x = Var('x', 'int?');
var y = Var('x', 'int?');
- late SsaNode<Var, Type> xSsaInsideLoop;
- late SsaNode<Var, Type> ySsaInsideLoop;
+ late SsaNode<Type> xSsaInsideLoop;
+ late SsaNode<Type> ySsaInsideLoop;
h.run([
declare(x, initialized: true),
declare(y, initialized: true),
@@ -806,7 +806,7 @@
test('forEach_bodyBegin() un-promotes', () {
var h = Harness();
var x = Var('x', 'int?');
- late SsaNode<Var, Type> ssaBeforeLoop;
+ late SsaNode<Type> ssaBeforeLoop;
h.run([
declare(x, initialized: true),
x.expr.as_('int').stmt,
@@ -966,7 +966,7 @@
var h = Harness();
var x = Var('x', 'int?');
var y = Var('y', 'int?');
- late SsaNode<Var, Type> ssaBeforeFunction;
+ late SsaNode<Type> ssaBeforeFunction;
h.run([
declare(x, initialized: true), declare(y, initialized: true),
x.expr.as_('int').stmt, y.expr.as_('int').stmt,
@@ -1229,7 +1229,7 @@
var x = Var('x', 'bool');
var y = Var('y', 'bool');
var z = Var('z', 'bool');
- late SsaNode<Var, Type> xSsaNodeBeforeIf;
+ late SsaNode<Type> xSsaNodeBeforeIf;
h.run([
declare(w, initialized: true),
declare(x, initialized: true),
@@ -1258,7 +1258,7 @@
'unreachable', () {
var h = Harness();
var x = Var('x', 'Object');
- late SsaNode<Var, Type> xSsaNodeBeforeIf;
+ late SsaNode<Type> xSsaNodeBeforeIf;
h.run([
declare(x, initialized: true),
getSsaNodes((nodes) {
@@ -1279,7 +1279,7 @@
'unreachable', () {
var h = Harness();
var x = Var('x', 'Object');
- late SsaNode<Var, Type> xSsaNodeBeforeIf;
+ late SsaNode<Type> xSsaNodeBeforeIf;
h.run([
declare(x, initialized: true),
getSsaNodes((nodes) {
@@ -1386,7 +1386,7 @@
var h = Harness();
var x = Var('x', 'Object');
var y = Var('y', 'int?');
- late ExpressionInfo<Var, Type> writtenValueInfo;
+ late ExpressionInfo<Type> writtenValueInfo;
h.run([
declare(y, initialized: true),
declareInitialized(
@@ -1487,7 +1487,7 @@
{bool inverted = false}) {
var h = Harness();
var x = Var('x', declaredType);
- late SsaNode<Var, Type> ssaBeforePromotion;
+ late SsaNode<Type> ssaBeforePromotion;
h.run([
declare(x, initialized: true),
getSsaNodes((nodes) => ssaBeforePromotion = nodes[x]!),
@@ -1778,7 +1778,7 @@
test('nonNullAssert_end(x) promotes', () {
var h = Harness();
var x = Var('x', 'int?');
- late SsaNode<Var, Type> ssaBeforePromotion;
+ late SsaNode<Type> ssaBeforePromotion;
h.run([
declare(x, initialized: true),
getSsaNodes((nodes) => ssaBeforePromotion = nodes[x]!),
@@ -1791,7 +1791,7 @@
test('nullAwareAccess temporarily promotes', () {
var h = Harness();
var x = Var('x', 'int?');
- late SsaNode<Var, Type> ssaBeforePromotion;
+ late SsaNode<Type> ssaBeforePromotion;
h.run([
declare(x, initialized: true),
getSsaNodes((nodes) => ssaBeforePromotion = nodes[x]!),
@@ -1991,7 +1991,7 @@
test('switchStatement_beginCase(true) un-promotes', () {
var h = Harness();
var x = Var('x', 'int?');
- late SsaNode<Var, Type> ssaBeforeSwitch;
+ late SsaNode<Type> ssaBeforeSwitch;
h.run([
declare(x, initialized: true),
x.expr.as_('int').stmt,
@@ -2142,7 +2142,7 @@
() {
var h = Harness();
var x = Var('x', 'int?');
- late SsaNode<Var, Type> ssaAfterTry;
+ late SsaNode<Type> ssaAfterTry;
h.run([
declare(x, initialized: true),
x.expr.as_('int').stmt,
@@ -2277,8 +2277,8 @@
'body', () {
var h = Harness();
var x = Var('x', 'int?');
- late SsaNode<Var, Type> ssaAtStartOfTry;
- late SsaNode<Var, Type> ssaAfterTry;
+ late SsaNode<Type> ssaAtStartOfTry;
+ late SsaNode<Type> ssaAfterTry;
h.run([
declare(x, initialized: true),
x.expr.as_('int').stmt,
@@ -2348,8 +2348,8 @@
var h = Harness();
var x = Var('x', 'int?');
var y = Var('y', 'int?');
- late SsaNode<Var, Type> xSsaAtEndOfFinally;
- late SsaNode<Var, Type> ySsaAtEndOfFinally;
+ late SsaNode<Type> xSsaAtEndOfFinally;
+ late SsaNode<Type> ySsaAtEndOfFinally;
h.run([
declare(x, initialized: true), declare(y, initialized: true),
try_([
@@ -2387,10 +2387,10 @@
var h = Harness();
var x = Var('x', 'int?');
var y = Var('y', 'int?');
- late SsaNode<Var, Type> xSsaAtEndOfTry;
- late SsaNode<Var, Type> ySsaAtEndOfTry;
- late SsaNode<Var, Type> xSsaAtEndOfFinally;
- late SsaNode<Var, Type> ySsaAtEndOfFinally;
+ late SsaNode<Type> xSsaAtEndOfTry;
+ late SsaNode<Type> ySsaAtEndOfTry;
+ late SsaNode<Type> xSsaAtEndOfFinally;
+ late SsaNode<Type> ySsaAtEndOfFinally;
h.run([
declare(x, initialized: true), declare(y, initialized: true),
try_([
@@ -2872,7 +2872,7 @@
test('whileStatement_conditionBegin() un-promotes', () {
var h = Harness();
var x = Var('x', 'int?');
- late SsaNode<Var, Type> ssaBeforeLoop;
+ late SsaNode<Type> ssaBeforeLoop;
h.run([
declare(x, initialized: true),
x.expr.as_('int').stmt,
@@ -2962,8 +2962,8 @@
var h = Harness();
var x = Var('x', 'int?');
var y = Var('x', 'int?');
- late SsaNode<Var, Type> xSsaInsideLoop;
- late SsaNode<Var, Type> ySsaInsideLoop;
+ late SsaNode<Type> xSsaInsideLoop;
+ late SsaNode<Type> ySsaInsideLoop;
h.run([
declare(x, initialized: true),
declare(y, initialized: true),
@@ -2991,8 +2991,8 @@
var h = Harness();
var x = Var('x', 'int?');
var y = Var('x', 'int?');
- late SsaNode<Var, Type> xSsaInsideLoop;
- late SsaNode<Var, Type> ySsaInsideLoop;
+ late SsaNode<Type> xSsaInsideLoop;
+ late SsaNode<Type> ySsaInsideLoop;
h.run([
declare(x, initialized: true),
declare(y, initialized: true),
@@ -3019,8 +3019,8 @@
var h = Harness();
var x = Var('x', 'Object');
var y = Var('y', 'int?');
- late SsaNode<Var, Type> ssaBeforeWrite;
- late ExpressionInfo<Var, Type> writtenValueInfo;
+ late SsaNode<Type> ssaBeforeWrite;
+ late ExpressionInfo<Type> writtenValueInfo;
h.run([
declare(x, initialized: true),
declare(y, initialized: true),
@@ -3045,8 +3045,8 @@
var h = Harness();
var x = Var('x', 'Object');
var y = Var('y', 'int?');
- late SsaNode<Var, Type> ssaBeforeWrite;
- late ExpressionInfo<Var, Type> writtenValueInfo;
+ late SsaNode<Type> ssaBeforeWrite;
+ late ExpressionInfo<Type> writtenValueInfo;
h.run([
declare(x, initialized: true),
declare(y, initialized: true),
@@ -3079,8 +3079,8 @@
var h = Harness();
var x = Var('x', 'int?');
var y = Var('y', 'int?');
- late SsaNode<Var, Type> xSsaBeforeWrite;
- late SsaNode<Var, Type> ySsa;
+ late SsaNode<Type> xSsaBeforeWrite;
+ late SsaNode<Type> ySsa;
h.run([
declare(x, initialized: true),
declare(y, initialized: true),
@@ -3100,7 +3100,7 @@
var h = Harness();
var x = Var('x', 'Object');
var y = Var('y', 'int?');
- late SsaNode<Var, Type> ssaBeforeWrite;
+ late SsaNode<Type> ssaBeforeWrite;
h.run([
declare(x, initialized: true),
declare(y, initialized: true),
@@ -3125,7 +3125,7 @@
test('write() permits expression to be null', () {
var h = Harness();
var x = Var('x', 'Object');
- late SsaNode<Var, Type> ssaBeforeWrite;
+ late SsaNode<Type> ssaBeforeWrite;
h.run([
declare(x, initialized: true),
getSsaNodes((nodes) => ssaBeforeWrite = nodes[x]!),
@@ -3363,15 +3363,14 @@
var objectQVar = Var('x', 'Object?');
var nullVar = Var('x', 'Null');
group('setUnreachable', () {
- var unreachable =
- FlowModel<Var, Type>(Reachability.initial.setUnreachable());
- var reachable = FlowModel<Var, Type>(Reachability.initial);
+ var unreachable = FlowModel<Type>(Reachability.initial.setUnreachable());
+ var reachable = FlowModel<Type>(Reachability.initial);
test('unchanged', () {
expect(unreachable.setUnreachable(), same(unreachable));
});
test('changed', () {
- void _check(FlowModel<Var, Type> initial) {
+ void _check(FlowModel<Type> initial) {
var s = initial.setUnreachable();
expect(s, isNot(same(initial)));
expect(s.reachable.overallReachable, false);
@@ -3383,33 +3382,33 @@
});
test('split', () {
- var s1 = FlowModel<Var, Type>(Reachability.initial);
+ var s1 = FlowModel<Type>(Reachability.initial);
var s2 = s1.split();
expect(s2.reachable.parent, same(s1.reachable));
});
test('unsplit', () {
- var s1 = FlowModel<Var, Type>(Reachability.initial.split());
+ var s1 = FlowModel<Type>(Reachability.initial.split());
var s2 = s1.unsplit();
expect(s2.reachable, same(Reachability.initial));
});
group('unsplitTo', () {
test('no change', () {
- var s1 = FlowModel<Var, Type>(Reachability.initial.split());
+ var s1 = FlowModel<Type>(Reachability.initial.split());
var result = s1.unsplitTo(s1.reachable.parent!);
expect(result, same(s1));
});
test('unsplit once, reachable', () {
- var s1 = FlowModel<Var, Type>(Reachability.initial.split());
+ var s1 = FlowModel<Type>(Reachability.initial.split());
var s2 = s1.split();
var result = s2.unsplitTo(s1.reachable.parent!);
expect(result.reachable, same(s1.reachable));
});
test('unsplit once, unreachable', () {
- var s1 = FlowModel<Var, Type>(Reachability.initial.split());
+ var s1 = FlowModel<Type>(Reachability.initial.split());
var s2 = s1.split().setUnreachable();
var result = s2.unsplitTo(s1.reachable.parent!);
expect(result.reachable.locallyReachable, false);
@@ -3417,7 +3416,7 @@
});
test('unsplit twice, reachable', () {
- var s1 = FlowModel<Var, Type>(Reachability.initial.split());
+ var s1 = FlowModel<Type>(Reachability.initial.split());
var s2 = s1.split();
var s3 = s2.split();
var result = s3.unsplitTo(s1.reachable.parent!);
@@ -3425,7 +3424,7 @@
});
test('unsplit twice, top unreachable', () {
- var s1 = FlowModel<Var, Type>(Reachability.initial.split());
+ var s1 = FlowModel<Type>(Reachability.initial.split());
var s2 = s1.split();
var s3 = s2.split().setUnreachable();
var result = s3.unsplitTo(s1.reachable.parent!);
@@ -3434,7 +3433,7 @@
});
test('unsplit twice, previous unreachable', () {
- var s1 = FlowModel<Var, Type>(Reachability.initial.split());
+ var s1 = FlowModel<Type>(Reachability.initial.split());
var s2 = s1.split().setUnreachable();
var s3 = s2.split();
var result = s3.unsplitTo(s1.reachable.parent!);
@@ -3446,38 +3445,39 @@
group('tryPromoteForTypeCheck', () {
test('unpromoted -> unchanged (same)', () {
var h = Harness();
- var s1 = FlowModel<Var, Type>(Reachability.initial);
+ var s1 = FlowModel<Type>(Reachability.initial);
var s2 = s1._tryPromoteForTypeCheck(h, intVar, 'int').ifTrue;
expect(s2, same(s1));
});
test('unpromoted -> unchanged (supertype)', () {
var h = Harness();
- var s1 = FlowModel<Var, Type>(Reachability.initial);
+ var s1 = FlowModel<Type>(Reachability.initial);
var s2 = s1._tryPromoteForTypeCheck(h, intVar, 'Object').ifTrue;
expect(s2, same(s1));
});
test('unpromoted -> unchanged (unrelated)', () {
var h = Harness();
- var s1 = FlowModel<Var, Type>(Reachability.initial);
+ var s1 = FlowModel<Type>(Reachability.initial);
var s2 = s1._tryPromoteForTypeCheck(h, intVar, 'String').ifTrue;
expect(s2, same(s1));
});
test('unpromoted -> subtype', () {
var h = Harness();
- var s1 = FlowModel<Var, Type>(Reachability.initial);
+ var s1 = FlowModel<Type>(Reachability.initial);
var s2 = s1._tryPromoteForTypeCheck(h, intQVar, 'int').ifTrue;
expect(s2.reachable.overallReachable, true);
expect(s2.variableInfo, {
- intQVar: _matchVariableModel(chain: ['int'], ofInterest: ['int'])
+ _promotionKeyStore.keyForVariable(intQVar):
+ _matchVariableModel(chain: ['int'], ofInterest: ['int'])
});
});
test('promoted -> unchanged (same)', () {
var h = Harness();
- var s1 = FlowModel<Var, Type>(Reachability.initial)
+ var s1 = FlowModel<Type>(Reachability.initial)
._tryPromoteForTypeCheck(h, objectQVar, 'int')
.ifTrue;
var s2 = s1._tryPromoteForTypeCheck(h, objectQVar, 'int').ifTrue;
@@ -3486,7 +3486,7 @@
test('promoted -> unchanged (supertype)', () {
var h = Harness();
- var s1 = FlowModel<Var, Type>(Reachability.initial)
+ var s1 = FlowModel<Type>(Reachability.initial)
._tryPromoteForTypeCheck(h, objectQVar, 'int')
.ifTrue;
var s2 = s1._tryPromoteForTypeCheck(h, objectQVar, 'Object').ifTrue;
@@ -3495,7 +3495,7 @@
test('promoted -> unchanged (unrelated)', () {
var h = Harness();
- var s1 = FlowModel<Var, Type>(Reachability.initial)
+ var s1 = FlowModel<Type>(Reachability.initial)
._tryPromoteForTypeCheck(h, objectQVar, 'int')
.ifTrue;
var s2 = s1._tryPromoteForTypeCheck(h, objectQVar, 'String').ifTrue;
@@ -3504,13 +3504,13 @@
test('promoted -> subtype', () {
var h = Harness();
- var s1 = FlowModel<Var, Type>(Reachability.initial)
+ var s1 = FlowModel<Type>(Reachability.initial)
._tryPromoteForTypeCheck(h, objectQVar, 'int?')
.ifTrue;
var s2 = s1._tryPromoteForTypeCheck(h, objectQVar, 'int').ifTrue;
expect(s2.reachable.overallReachable, true);
expect(s2.variableInfo, {
- objectQVar: _matchVariableModel(
+ _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
chain: ['int?', 'int'], ofInterest: ['int?', 'int'])
});
});
@@ -3522,21 +3522,21 @@
test('without declaration', () {
// This should not happen in valid code, but test that we don't crash.
var h = Harness();
- var s = FlowModel<Var, Type>(Reachability.initial).write(
- null, objectQVar, Type('Object?'), new SsaNode<Var, Type>(null), h);
+ var s = FlowModel<Type>(Reachability.initial)._write(
+ null, objectQVar, Type('Object?'), new SsaNode<Type>(null), h);
expect(s.variableInfo[objectQVar], isNull);
});
test('unchanged', () {
var h = Harness();
- var s1 = FlowModel<Var, Type>(Reachability.initial)
- .declare(objectQVar, true);
- var s2 = s1.write(
- null, objectQVar, Type('Object?'), new SsaNode<Var, Type>(null), h);
+ var s1 =
+ FlowModel<Type>(Reachability.initial)._declare(objectQVar, true);
+ var s2 = s1._write(
+ null, objectQVar, Type('Object?'), new SsaNode<Type>(null), h);
expect(s2, isNot(same(s1)));
expect(s2.reachable, same(s1.reachable));
expect(
- s2.infoFor(objectQVar),
+ s2._infoFor(objectQVar),
_matchVariableModel(
chain: null,
ofInterest: isEmpty,
@@ -3546,13 +3546,13 @@
test('marks as assigned', () {
var h = Harness();
- var s1 = FlowModel<Var, Type>(Reachability.initial)
- .declare(objectQVar, false);
- var s2 = s1.write(
- null, objectQVar, Type('int?'), new SsaNode<Var, Type>(null), h);
+ var s1 =
+ FlowModel<Type>(Reachability.initial)._declare(objectQVar, false);
+ var s2 = s1._write(
+ null, objectQVar, Type('int?'), new SsaNode<Type>(null), h);
expect(s2.reachable.overallReachable, true);
expect(
- s2.infoFor(objectQVar),
+ s2._infoFor(objectQVar),
_matchVariableModel(
chain: null,
ofInterest: isEmpty,
@@ -3562,16 +3562,17 @@
test('un-promotes fully', () {
var h = Harness();
- var s1 = FlowModel<Var, Type>(Reachability.initial)
- .declare(objectQVar, true)
+ var s1 = FlowModel<Type>(Reachability.initial)
+ ._declare(objectQVar, true)
._tryPromoteForTypeCheck(h, objectQVar, 'int')
.ifTrue;
- expect(s1.variableInfo, contains(objectQVar));
- var s2 = s1.write(_MockNonPromotionReason(), objectQVar, Type('int?'),
- new SsaNode<Var, Type>(null), h);
+ expect(s1.variableInfo,
+ contains(_promotionKeyStore.keyForVariable(objectQVar)));
+ var s2 = s1._write(_MockNonPromotionReason(), objectQVar, Type('int?'),
+ new SsaNode<Type>(null), h);
expect(s2.reachable.overallReachable, true);
expect(s2.variableInfo, {
- objectQVar: _matchVariableModel(
+ _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
chain: null,
ofInterest: isEmpty,
assigned: true,
@@ -3581,24 +3582,24 @@
test('un-promotes partially, when no exact match', () {
var h = Harness();
- var s1 = FlowModel<Var, Type>(Reachability.initial)
- .declare(objectQVar, true)
+ var s1 = FlowModel<Type>(Reachability.initial)
+ ._declare(objectQVar, true)
._tryPromoteForTypeCheck(h, objectQVar, 'num?')
.ifTrue
._tryPromoteForTypeCheck(h, objectQVar, 'int')
.ifTrue;
expect(s1.variableInfo, {
- objectQVar: _matchVariableModel(
+ _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
chain: ['num?', 'int'],
ofInterest: ['num?', 'int'],
assigned: true,
unassigned: false)
});
- var s2 = s1.write(_MockNonPromotionReason(), objectQVar, Type('num'),
- new SsaNode<Var, Type>(null), h);
+ var s2 = s1._write(_MockNonPromotionReason(), objectQVar, Type('num'),
+ new SsaNode<Type>(null), h);
expect(s2.reachable.overallReachable, true);
expect(s2.variableInfo, {
- objectQVar: _matchVariableModel(
+ _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
chain: ['num?', 'num'],
ofInterest: ['num?', 'int'],
assigned: true,
@@ -3608,8 +3609,8 @@
test('un-promotes partially, when exact match', () {
var h = Harness();
- var s1 = FlowModel<Var, Type>(Reachability.initial)
- .declare(objectQVar, true)
+ var s1 = FlowModel<Type>(Reachability.initial)
+ ._declare(objectQVar, true)
._tryPromoteForTypeCheck(h, objectQVar, 'num?')
.ifTrue
._tryPromoteForTypeCheck(h, objectQVar, 'num')
@@ -3617,17 +3618,17 @@
._tryPromoteForTypeCheck(h, objectQVar, 'int')
.ifTrue;
expect(s1.variableInfo, {
- objectQVar: _matchVariableModel(
+ _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
chain: ['num?', 'num', 'int'],
ofInterest: ['num?', 'num', 'int'],
assigned: true,
unassigned: false)
});
- var s2 = s1.write(_MockNonPromotionReason(), objectQVar, Type('num'),
- new SsaNode<Var, Type>(null), h);
+ var s2 = s1._write(_MockNonPromotionReason(), objectQVar, Type('num'),
+ new SsaNode<Type>(null), h);
expect(s2.reachable.overallReachable, true);
expect(s2.variableInfo, {
- objectQVar: _matchVariableModel(
+ _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
chain: ['num?', 'num'],
ofInterest: ['num?', 'num', 'int'],
assigned: true,
@@ -3637,25 +3638,25 @@
test('leaves promoted, when exact match', () {
var h = Harness();
- var s1 = FlowModel<Var, Type>(Reachability.initial)
- .declare(objectQVar, true)
+ var s1 = FlowModel<Type>(Reachability.initial)
+ ._declare(objectQVar, true)
._tryPromoteForTypeCheck(h, objectQVar, 'num?')
.ifTrue
._tryPromoteForTypeCheck(h, objectQVar, 'num')
.ifTrue;
expect(s1.variableInfo, {
- objectQVar: _matchVariableModel(
+ _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
chain: ['num?', 'num'],
ofInterest: ['num?', 'num'],
assigned: true,
unassigned: false)
});
- var s2 = s1.write(
- null, objectQVar, Type('num'), new SsaNode<Var, Type>(null), h);
+ var s2 = s1._write(
+ null, objectQVar, Type('num'), new SsaNode<Type>(null), h);
expect(s2.reachable.overallReachable, true);
expect(s2.variableInfo, isNot(same(s1.variableInfo)));
expect(s2.variableInfo, {
- objectQVar: _matchVariableModel(
+ _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
chain: ['num?', 'num'],
ofInterest: ['num?', 'num'],
assigned: true,
@@ -3665,25 +3666,25 @@
test('leaves promoted, when writing a subtype', () {
var h = Harness();
- var s1 = FlowModel<Var, Type>(Reachability.initial)
- .declare(objectQVar, true)
+ var s1 = FlowModel<Type>(Reachability.initial)
+ ._declare(objectQVar, true)
._tryPromoteForTypeCheck(h, objectQVar, 'num?')
.ifTrue
._tryPromoteForTypeCheck(h, objectQVar, 'num')
.ifTrue;
expect(s1.variableInfo, {
- objectQVar: _matchVariableModel(
+ _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
chain: ['num?', 'num'],
ofInterest: ['num?', 'num'],
assigned: true,
unassigned: false)
});
- var s2 = s1.write(
- null, objectQVar, Type('int'), new SsaNode<Var, Type>(null), h);
+ var s2 = s1._write(
+ null, objectQVar, Type('int'), new SsaNode<Type>(null), h);
expect(s2.reachable.overallReachable, true);
expect(s2.variableInfo, isNot(same(s1.variableInfo)));
expect(s2.variableInfo, {
- objectQVar: _matchVariableModel(
+ _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
chain: ['num?', 'num'],
ofInterest: ['num?', 'num'],
assigned: true,
@@ -3696,15 +3697,16 @@
var h = Harness();
var x = Var('x', 'int?');
- var s1 = FlowModel<Var, Type>(Reachability.initial).declare(x, true);
+ var s1 = FlowModel<Type>(Reachability.initial)._declare(x, true);
expect(s1.variableInfo, {
- x: _matchVariableModel(chain: null),
+ _promotionKeyStore.keyForVariable(x):
+ _matchVariableModel(chain: null),
});
- var s2 =
- s1.write(null, x, Type('int'), new SsaNode<Var, Type>(null), h);
+ var s2 = s1._write(null, x, Type('int'), new SsaNode<Type>(null), h);
expect(s2.variableInfo, {
- x: _matchVariableModel(chain: ['int']),
+ _promotionKeyStore.keyForVariable(x):
+ _matchVariableModel(chain: ['int']),
});
});
@@ -3712,40 +3714,42 @@
var h = Harness();
var x = Var('x', 'int?');
- var s1 = FlowModel<Var, Type>(Reachability.initial).declare(x, true);
+ var s1 = FlowModel<Type>(Reachability.initial)._declare(x, true);
expect(s1.variableInfo, {
- x: _matchVariableModel(chain: null),
+ _promotionKeyStore.keyForVariable(x):
+ _matchVariableModel(chain: null),
});
- var s2 = s1.conservativeJoin([], [x]);
+ var s2 = s1._conservativeJoin([], [x]);
expect(s2.variableInfo, {
- x: _matchVariableModel(chain: null, writeCaptured: true),
+ _promotionKeyStore.keyForVariable(x):
+ _matchVariableModel(chain: null, writeCaptured: true),
});
// 'x' is write-captured, so not promoted
- var s3 =
- s2.write(null, x, Type('int'), new SsaNode<Var, Type>(null), h);
+ var s3 = s2._write(null, x, Type('int'), new SsaNode<Type>(null), h);
expect(s3.variableInfo, {
- x: _matchVariableModel(chain: null, writeCaptured: true),
+ _promotionKeyStore.keyForVariable(x):
+ _matchVariableModel(chain: null, writeCaptured: true),
});
});
test('when promoted', () {
var h = Harness();
- var s1 = FlowModel<Var, Type>(Reachability.initial)
- .declare(objectQVar, true)
+ var s1 = FlowModel<Type>(Reachability.initial)
+ ._declare(objectQVar, true)
._tryPromoteForTypeCheck(h, objectQVar, 'int?')
.ifTrue;
expect(s1.variableInfo, {
- objectQVar: _matchVariableModel(
+ _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
chain: ['int?'],
ofInterest: ['int?'],
),
});
- var s2 = s1.write(
- null, objectQVar, Type('int'), new SsaNode<Var, Type>(null), h);
+ var s2 = s1._write(
+ null, objectQVar, Type('int'), new SsaNode<Type>(null), h);
expect(s2.variableInfo, {
- objectQVar: _matchVariableModel(
+ _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
chain: ['int?', 'int'],
ofInterest: ['int?'],
),
@@ -3754,20 +3758,20 @@
test('when not promoted', () {
var h = Harness();
- var s1 = FlowModel<Var, Type>(Reachability.initial)
- .declare(objectQVar, true)
+ var s1 = FlowModel<Type>(Reachability.initial)
+ ._declare(objectQVar, true)
._tryPromoteForTypeCheck(h, objectQVar, 'int?')
.ifFalse;
expect(s1.variableInfo, {
- objectQVar: _matchVariableModel(
+ _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
chain: ['Object'],
ofInterest: ['int?'],
),
});
- var s2 = s1.write(
- null, objectQVar, Type('int'), new SsaNode<Var, Type>(null), h);
+ var s2 = s1._write(
+ null, objectQVar, Type('int'), new SsaNode<Type>(null), h);
expect(s2.variableInfo, {
- objectQVar: _matchVariableModel(
+ _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
chain: ['Object', 'int'],
ofInterest: ['int?'],
),
@@ -3777,20 +3781,20 @@
test('Promotes to type of interest when not previously promoted', () {
var h = Harness();
- var s1 = FlowModel<Var, Type>(Reachability.initial)
- .declare(objectQVar, true)
+ var s1 = FlowModel<Type>(Reachability.initial)
+ ._declare(objectQVar, true)
._tryPromoteForTypeCheck(h, objectQVar, 'num?')
.ifFalse;
expect(s1.variableInfo, {
- objectQVar: _matchVariableModel(
+ _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
chain: ['Object'],
ofInterest: ['num?'],
),
});
- var s2 = s1.write(_MockNonPromotionReason(), objectQVar, Type('num?'),
- new SsaNode<Var, Type>(null), h);
+ var s2 = s1._write(_MockNonPromotionReason(), objectQVar, Type('num?'),
+ new SsaNode<Type>(null), h);
expect(s2.variableInfo, {
- objectQVar: _matchVariableModel(
+ _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
chain: ['num?'],
ofInterest: ['num?'],
),
@@ -3799,22 +3803,22 @@
test('Promotes to type of interest when previously promoted', () {
var h = Harness();
- var s1 = FlowModel<Var, Type>(Reachability.initial)
- .declare(objectQVar, true)
+ var s1 = FlowModel<Type>(Reachability.initial)
+ ._declare(objectQVar, true)
._tryPromoteForTypeCheck(h, objectQVar, 'num?')
.ifTrue
._tryPromoteForTypeCheck(h, objectQVar, 'int?')
.ifFalse;
expect(s1.variableInfo, {
- objectQVar: _matchVariableModel(
+ _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
chain: ['num?', 'num'],
ofInterest: ['num?', 'int?'],
),
});
- var s2 = s1.write(_MockNonPromotionReason(), objectQVar, Type('int?'),
- new SsaNode<Var, Type>(null), h);
+ var s2 = s1._write(_MockNonPromotionReason(), objectQVar, Type('int?'),
+ new SsaNode<Type>(null), h);
expect(s2.variableInfo, {
- objectQVar: _matchVariableModel(
+ _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
chain: ['num?', 'int?'],
ofInterest: ['num?', 'int?'],
),
@@ -3868,23 +3872,22 @@
test('; first', () {
var x = Var('x', 'Object?');
- var s1 = FlowModel<Var, Type>(Reachability.initial)
- .declare(x, true)
+ var s1 = FlowModel<Type>(Reachability.initial)
+ ._declare(x, true)
._tryPromoteForTypeCheck(h, x, 'B?')
.ifFalse
._tryPromoteForTypeCheck(h, x, 'A?')
.ifFalse;
expect(s1.variableInfo, {
- x: _matchVariableModel(
+ _promotionKeyStore.keyForVariable(x): _matchVariableModel(
chain: ['Object'],
ofInterest: ['A?', 'B?'],
),
});
- var s2 =
- s1.write(null, x, Type('C'), new SsaNode<Var, Type>(null), h);
+ var s2 = s1._write(null, x, Type('C'), new SsaNode<Type>(null), h);
expect(s2.variableInfo, {
- x: _matchVariableModel(
+ _promotionKeyStore.keyForVariable(x): _matchVariableModel(
chain: ['Object', 'B'],
ofInterest: ['A?', 'B?'],
),
@@ -3894,23 +3897,22 @@
test('; second', () {
var x = Var('x', 'Object?');
- var s1 = FlowModel<Var, Type>(Reachability.initial)
- .declare(x, true)
+ var s1 = FlowModel<Type>(Reachability.initial)
+ ._declare(x, true)
._tryPromoteForTypeCheck(h, x, 'A?')
.ifFalse
._tryPromoteForTypeCheck(h, x, 'B?')
.ifFalse;
expect(s1.variableInfo, {
- x: _matchVariableModel(
+ _promotionKeyStore.keyForVariable(x): _matchVariableModel(
chain: ['Object'],
ofInterest: ['A?', 'B?'],
),
});
- var s2 =
- s1.write(null, x, Type('C'), new SsaNode<Var, Type>(null), h);
+ var s2 = s1._write(null, x, Type('C'), new SsaNode<Type>(null), h);
expect(s2.variableInfo, {
- x: _matchVariableModel(
+ _promotionKeyStore.keyForVariable(x): _matchVariableModel(
chain: ['Object', 'B'],
ofInterest: ['A?', 'B?'],
),
@@ -3920,23 +3922,22 @@
test('; nullable and non-nullable', () {
var x = Var('x', 'Object?');
- var s1 = FlowModel<Var, Type>(Reachability.initial)
- .declare(x, true)
+ var s1 = FlowModel<Type>(Reachability.initial)
+ ._declare(x, true)
._tryPromoteForTypeCheck(h, x, 'A')
.ifFalse
._tryPromoteForTypeCheck(h, x, 'A?')
.ifFalse;
expect(s1.variableInfo, {
- x: _matchVariableModel(
+ _promotionKeyStore.keyForVariable(x): _matchVariableModel(
chain: ['Object'],
ofInterest: ['A', 'A?'],
),
});
- var s2 =
- s1.write(null, x, Type('B'), new SsaNode<Var, Type>(null), h);
+ var s2 = s1._write(null, x, Type('B'), new SsaNode<Type>(null), h);
expect(s2.variableInfo, {
- x: _matchVariableModel(
+ _promotionKeyStore.keyForVariable(x): _matchVariableModel(
chain: ['Object', 'A'],
ofInterest: ['A', 'A?'],
),
@@ -3947,25 +3948,27 @@
group('; ambiguous', () {
test('; no promotion', () {
var h = Harness();
- var s1 = FlowModel<Var, Type>(Reachability.initial)
- .declare(objectQVar, true)
+ var s1 = FlowModel<Type>(Reachability.initial)
+ ._declare(objectQVar, true)
._tryPromoteForTypeCheck(h, objectQVar, 'num?')
.ifFalse
._tryPromoteForTypeCheck(h, objectQVar, 'num*')
.ifFalse;
expect(s1.variableInfo, {
- objectQVar: _matchVariableModel(
+ _promotionKeyStore.keyForVariable(objectQVar):
+ _matchVariableModel(
chain: ['Object'],
ofInterest: ['num?', 'num*'],
),
});
- var s2 = s1.write(
- null, objectQVar, Type('int'), new SsaNode<Var, Type>(null), h);
+ var s2 = s1._write(
+ null, objectQVar, Type('int'), new SsaNode<Type>(null), h);
// It's ambiguous whether to promote to num? or num*, so we don't
// promote.
expect(s2, isNot(same(s1)));
expect(s2.variableInfo, {
- objectQVar: _matchVariableModel(
+ _promotionKeyStore.keyForVariable(objectQVar):
+ _matchVariableModel(
chain: ['Object'],
ofInterest: ['num?', 'num*'],
),
@@ -3975,24 +3978,24 @@
test('exact match', () {
var h = Harness();
- var s1 = FlowModel<Var, Type>(Reachability.initial)
- .declare(objectQVar, true)
+ var s1 = FlowModel<Type>(Reachability.initial)
+ ._declare(objectQVar, true)
._tryPromoteForTypeCheck(h, objectQVar, 'num?')
.ifFalse
._tryPromoteForTypeCheck(h, objectQVar, 'num*')
.ifFalse;
expect(s1.variableInfo, {
- objectQVar: _matchVariableModel(
+ _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
chain: ['Object'],
ofInterest: ['num?', 'num*'],
),
});
- var s2 = s1.write(_MockNonPromotionReason(), objectQVar, Type('num?'),
- new SsaNode<Var, Type>(null), h);
+ var s2 = s1._write(_MockNonPromotionReason(), objectQVar,
+ Type('num?'), new SsaNode<Type>(null), h);
// It's ambiguous whether to promote to num? or num*, but since the
// written type is exactly num?, we use that.
expect(s2.variableInfo, {
- objectQVar: _matchVariableModel(
+ _promotionKeyStore.keyForVariable(objectQVar): _matchVariableModel(
chain: ['num?'],
ofInterest: ['num?', 'num*'],
),
@@ -4007,23 +4010,23 @@
var h = Harness();
- var s1 = FlowModel<Var, Type>(Reachability.initial)
- .declare(x, true)
+ var s1 = FlowModel<Type>(Reachability.initial)
+ ._declare(x, true)
._tryPromoteForTypeCheck(h, x, 'num?')
.ifTrue
._tryPromoteForTypeCheck(h, x, 'int?')
.ifTrue;
expect(s1.variableInfo, {
- x: _matchVariableModel(
+ _promotionKeyStore.keyForVariable(x): _matchVariableModel(
chain: ['num?', 'int?'],
ofInterest: ['num?', 'int?'],
),
});
- var s2 = s1.write(_MockNonPromotionReason(), x, Type('double'),
- new SsaNode<Var, Type>(null), h);
+ var s2 = s1._write(_MockNonPromotionReason(), x, Type('double'),
+ new SsaNode<Type>(null), h);
expect(s2.variableInfo, {
- x: _matchVariableModel(
+ _promotionKeyStore.keyForVariable(x): _matchVariableModel(
chain: ['num?', 'num'],
ofInterest: ['num?', 'int?'],
),
@@ -4035,18 +4038,20 @@
var objectQVar = Var('x', 'Object?');
test('initialized', () {
- var s = FlowModel<Var, Type>(Reachability.initial)
- .declare(objectQVar, true);
+ var s =
+ FlowModel<Type>(Reachability.initial)._declare(objectQVar, true);
expect(s.variableInfo, {
- objectQVar: _matchVariableModel(assigned: true, unassigned: false),
+ _promotionKeyStore.keyForVariable(objectQVar):
+ _matchVariableModel(assigned: true, unassigned: false),
});
});
test('not initialized', () {
- var s = FlowModel<Var, Type>(Reachability.initial)
- .declare(objectQVar, false);
+ var s =
+ FlowModel<Type>(Reachability.initial)._declare(objectQVar, false);
expect(s.variableInfo, {
- objectQVar: _matchVariableModel(assigned: false, unassigned: true),
+ _promotionKeyStore.keyForVariable(objectQVar):
+ _matchVariableModel(assigned: false, unassigned: true),
});
});
});
@@ -4054,23 +4059,23 @@
group('markNonNullable', () {
test('unpromoted -> unchanged', () {
var h = Harness();
- var s1 = FlowModel<Var, Type>(Reachability.initial);
+ var s1 = FlowModel<Type>(Reachability.initial);
var s2 = s1._tryMarkNonNullable(h, intVar).ifTrue;
expect(s2, same(s1));
});
test('unpromoted -> promoted', () {
var h = Harness();
- var s1 = FlowModel<Var, Type>(Reachability.initial);
+ var s1 = FlowModel<Type>(Reachability.initial);
var s2 = s1._tryMarkNonNullable(h, intQVar).ifTrue;
expect(s2.reachable.overallReachable, true);
- expect(s2.infoFor(intQVar),
+ expect(s2._infoFor(intQVar),
_matchVariableModel(chain: ['int'], ofInterest: []));
});
test('promoted -> unchanged', () {
var h = Harness();
- var s1 = FlowModel<Var, Type>(Reachability.initial)
+ var s1 = FlowModel<Type>(Reachability.initial)
._tryPromoteForTypeCheck(h, objectQVar, 'int')
.ifTrue;
var s2 = s1._tryMarkNonNullable(h, objectQVar).ifTrue;
@@ -4079,23 +4084,23 @@
test('promoted -> re-promoted', () {
var h = Harness();
- var s1 = FlowModel<Var, Type>(Reachability.initial)
+ var s1 = FlowModel<Type>(Reachability.initial)
._tryPromoteForTypeCheck(h, objectQVar, 'int?')
.ifTrue;
var s2 = s1._tryMarkNonNullable(h, objectQVar).ifTrue;
expect(s2.reachable.overallReachable, true);
expect(s2.variableInfo, {
- objectQVar:
+ _promotionKeyStore.keyForVariable(objectQVar):
_matchVariableModel(chain: ['int?', 'int'], ofInterest: ['int?'])
});
});
test('promote to Never', () {
var h = Harness();
- var s1 = FlowModel<Var, Type>(Reachability.initial);
+ var s1 = FlowModel<Type>(Reachability.initial);
var s2 = s1._tryMarkNonNullable(h, nullVar).ifTrue;
expect(s2.reachable.overallReachable, false);
- expect(s2.infoFor(nullVar),
+ expect(s2._infoFor(nullVar),
_matchVariableModel(chain: ['Never'], ofInterest: []));
});
});
@@ -4103,46 +4108,51 @@
group('conservativeJoin', () {
test('unchanged', () {
var h = Harness();
- var s1 = FlowModel<Var, Type>(Reachability.initial)
- .declare(intQVar, true)
+ var s1 = FlowModel<Type>(Reachability.initial)
+ ._declare(intQVar, true)
._tryPromoteForTypeCheck(h, objectQVar, 'int')
.ifTrue;
- var s2 = s1.conservativeJoin([intQVar], []);
+ var s2 = s1._conservativeJoin([intQVar], []);
expect(s2, isNot(same(s1)));
expect(s2.reachable, same(s1.reachable));
expect(s2.variableInfo, {
- objectQVar: _matchVariableModel(chain: ['int'], ofInterest: ['int']),
- intQVar: _matchVariableModel(chain: null, ofInterest: [])
+ _promotionKeyStore.keyForVariable(objectQVar):
+ _matchVariableModel(chain: ['int'], ofInterest: ['int']),
+ _promotionKeyStore.keyForVariable(intQVar):
+ _matchVariableModel(chain: null, ofInterest: [])
});
});
test('written', () {
var h = Harness();
- var s1 = FlowModel<Var, Type>(Reachability.initial)
+ var s1 = FlowModel<Type>(Reachability.initial)
._tryPromoteForTypeCheck(h, objectQVar, 'int')
.ifTrue
._tryPromoteForTypeCheck(h, intQVar, 'int')
.ifTrue;
- var s2 = s1.conservativeJoin([intQVar], []);
+ var s2 = s1._conservativeJoin([intQVar], []);
expect(s2.reachable.overallReachable, true);
expect(s2.variableInfo, {
- objectQVar: _matchVariableModel(chain: ['int'], ofInterest: ['int']),
- intQVar: _matchVariableModel(chain: null, ofInterest: ['int'])
+ _promotionKeyStore.keyForVariable(objectQVar):
+ _matchVariableModel(chain: ['int'], ofInterest: ['int']),
+ _promotionKeyStore.keyForVariable(intQVar):
+ _matchVariableModel(chain: null, ofInterest: ['int'])
});
});
test('write captured', () {
var h = Harness();
- var s1 = FlowModel<Var, Type>(Reachability.initial)
+ var s1 = FlowModel<Type>(Reachability.initial)
._tryPromoteForTypeCheck(h, objectQVar, 'int')
.ifTrue
._tryPromoteForTypeCheck(h, intQVar, 'int')
.ifTrue;
- var s2 = s1.conservativeJoin([], [intQVar]);
+ var s2 = s1._conservativeJoin([], [intQVar]);
expect(s2.reachable.overallReachable, true);
expect(s2.variableInfo, {
- objectQVar: _matchVariableModel(chain: ['int'], ofInterest: ['int']),
- intQVar: _matchVariableModel(
+ _promotionKeyStore.keyForVariable(objectQVar):
+ _matchVariableModel(chain: ['int'], ofInterest: ['int']),
+ _promotionKeyStore.keyForVariable(intQVar): _matchVariableModel(
chain: null, ofInterest: isEmpty, unassigned: false)
});
});
@@ -4151,7 +4161,7 @@
group('rebaseForward', () {
test('reachability', () {
var h = Harness();
- var reachable = FlowModel<Var, Type>(Reachability.initial);
+ var reachable = FlowModel<Type>(Reachability.initial);
var unreachable = reachable.setUnreachable();
expect(reachable.rebaseForward(h, reachable), same(reachable));
expect(reachable.rebaseForward(h, unreachable), same(unreachable));
@@ -4169,22 +4179,22 @@
var b = Var('b', 'int');
var c = Var('c', 'int');
var d = Var('d', 'int');
- var s0 = FlowModel<Var, Type>(Reachability.initial)
- .declare(a, false)
- .declare(b, false)
- .declare(c, false)
- .declare(d, false);
+ var s0 = FlowModel<Type>(Reachability.initial)
+ ._declare(a, false)
+ ._declare(b, false)
+ ._declare(c, false)
+ ._declare(d, false);
var s1 = s0
- .write(null, a, Type('int'), new SsaNode<Var, Type>(null), h)
- .write(null, b, Type('int'), new SsaNode<Var, Type>(null), h);
+ ._write(null, a, Type('int'), new SsaNode<Type>(null), h)
+ ._write(null, b, Type('int'), new SsaNode<Type>(null), h);
var s2 = s0
- .write(null, a, Type('int'), new SsaNode<Var, Type>(null), h)
- .write(null, c, Type('int'), new SsaNode<Var, Type>(null), h);
+ ._write(null, a, Type('int'), new SsaNode<Type>(null), h)
+ ._write(null, c, Type('int'), new SsaNode<Type>(null), h);
var result = s1.rebaseForward(h, s2);
- expect(result.infoFor(a).assigned, true);
- expect(result.infoFor(b).assigned, true);
- expect(result.infoFor(c).assigned, true);
- expect(result.infoFor(d).assigned, false);
+ expect(result._infoFor(a).assigned, true);
+ expect(result._infoFor(b).assigned, true);
+ expect(result._infoFor(c).assigned, true);
+ expect(result._infoFor(d).assigned, false);
});
test('write captured', () {
@@ -4193,29 +4203,29 @@
var b = Var('b', 'int');
var c = Var('c', 'int');
var d = Var('d', 'int');
- var s0 = FlowModel<Var, Type>(Reachability.initial)
- .declare(a, false)
- .declare(b, false)
- .declare(c, false)
- .declare(d, false);
+ var s0 = FlowModel<Type>(Reachability.initial)
+ ._declare(a, false)
+ ._declare(b, false)
+ ._declare(c, false)
+ ._declare(d, false);
// In s1, a and b are write captured. In s2, a and c are.
- var s1 = s0.conservativeJoin([a, b], [a, b]);
- var s2 = s1.conservativeJoin([a, c], [a, c]);
+ var s1 = s0._conservativeJoin([a, b], [a, b]);
+ var s2 = s1._conservativeJoin([a, c], [a, c]);
var result = s1.rebaseForward(h, s2);
expect(
- result.infoFor(a),
+ result._infoFor(a),
_matchVariableModel(writeCaptured: true, unassigned: false),
);
expect(
- result.infoFor(b),
+ result._infoFor(b),
_matchVariableModel(writeCaptured: true, unassigned: false),
);
expect(
- result.infoFor(c),
+ result._infoFor(c),
_matchVariableModel(writeCaptured: true, unassigned: false),
);
expect(
- result.infoFor(d),
+ result._infoFor(d),
_matchVariableModel(writeCaptured: false, unassigned: true),
);
});
@@ -4223,16 +4233,16 @@
test('write captured and promoted', () {
var h = Harness();
var a = Var('a', 'num');
- var s0 = FlowModel<Var, Type>(Reachability.initial).declare(a, false);
+ var s0 = FlowModel<Type>(Reachability.initial)._declare(a, false);
// In s1, a is write captured. In s2 it's promoted.
- var s1 = s0.conservativeJoin([a], [a]);
+ var s1 = s0._conservativeJoin([a], [a]);
var s2 = s0._tryPromoteForTypeCheck(h, a, 'int').ifTrue;
expect(
- s1.rebaseForward(h, s2).infoFor(a),
+ s1.rebaseForward(h, s2)._infoFor(a),
_matchVariableModel(writeCaptured: true, chain: isNull),
);
expect(
- s2.rebaseForward(h, s1).infoFor(a),
+ s2.rebaseForward(h, s1)._infoFor(a),
_matchVariableModel(writeCaptured: true, chain: isNull),
);
});
@@ -4242,11 +4252,11 @@
List<String>? expectedChain) {
var h = Harness();
var x = Var('x', 'Object?');
- var s0 = FlowModel<Var, Type>(Reachability.initial).declare(x, true);
+ var s0 = FlowModel<Type>(Reachability.initial)._declare(x, true);
var s1 = s0;
if (unsafe) {
- s1 = s1.write(
- null, x, Type('Object?'), new SsaNode<Var, Type>(null), h);
+ s1 =
+ s1._write(null, x, Type('Object?'), new SsaNode<Type>(null), h);
}
if (thisType != null) {
s1 = s1._tryPromoteForTypeCheck(h, x, thisType).ifTrue;
@@ -4256,10 +4266,12 @@
: s0._tryPromoteForTypeCheck(h, x, otherType).ifTrue;
var result = s2.rebaseForward(h, s1);
if (expectedChain == null) {
- expect(result.variableInfo, contains(x));
- expect(result.infoFor(x).promotedTypes, isNull);
+ expect(result.variableInfo,
+ contains(_promotionKeyStore.keyForVariable(x)));
+ expect(result._infoFor(x).promotedTypes, isNull);
} else {
- expect(result.infoFor(x).promotedTypes!.map((t) => t.type).toList(),
+ expect(
+ result._infoFor(x).promotedTypes!.map((t) => t.type).toList(),
expectedChain);
}
}
@@ -4300,31 +4312,31 @@
var h = Harness();
var x = Var('x', 'Object?');
var initialModel =
- FlowModel<Var, Type>(Reachability.initial).declare(x, true);
+ FlowModel<Type>(Reachability.initial)._declare(x, true);
for (var t in before) {
initialModel = initialModel._tryPromoteForTypeCheck(h, x, t).ifTrue;
}
- _checkChain(initialModel.infoFor(x).promotedTypes, before);
+ _checkChain(initialModel._infoFor(x).promotedTypes, before);
var tryModel = initialModel;
for (var t in inTry) {
tryModel = tryModel._tryPromoteForTypeCheck(h, x, t).ifTrue;
}
var expectedTryChain = before.toList()..addAll(inTry);
- _checkChain(tryModel.infoFor(x).promotedTypes, expectedTryChain);
+ _checkChain(tryModel._infoFor(x).promotedTypes, expectedTryChain);
var finallyModel = initialModel;
for (var t in inFinally) {
finallyModel = finallyModel._tryPromoteForTypeCheck(h, x, t).ifTrue;
}
var expectedFinallyChain = before.toList()..addAll(inFinally);
_checkChain(
- finallyModel.infoFor(x).promotedTypes, expectedFinallyChain);
+ finallyModel._infoFor(x).promotedTypes, expectedFinallyChain);
var result = tryModel.rebaseForward(h, finallyModel);
- _checkChain(result.infoFor(x).promotedTypes, expectedResult);
+ _checkChain(result._infoFor(x).promotedTypes, expectedResult);
// And verify that the inputs are unchanged.
- _checkChain(initialModel.infoFor(x).promotedTypes, before);
- _checkChain(tryModel.infoFor(x).promotedTypes, expectedTryChain);
+ _checkChain(initialModel._infoFor(x).promotedTypes, before);
+ _checkChain(tryModel._infoFor(x).promotedTypes, expectedTryChain);
_checkChain(
- finallyModel.infoFor(x).promotedTypes, expectedFinallyChain);
+ finallyModel._infoFor(x).promotedTypes, expectedFinallyChain);
}
_check(['Object'], ['num', 'int'], ['Iterable', 'List'],
@@ -4351,15 +4363,15 @@
test('types of interest', () {
var h = Harness();
var a = Var('a', 'Object');
- var s0 = FlowModel<Var, Type>(Reachability.initial).declare(a, false);
+ var s0 = FlowModel<Type>(Reachability.initial)._declare(a, false);
var s1 = s0._tryPromoteForTypeCheck(h, a, 'int').ifFalse;
var s2 = s0._tryPromoteForTypeCheck(h, a, 'String').ifFalse;
expect(
- s1.rebaseForward(h, s2).infoFor(a),
+ s1.rebaseForward(h, s2)._infoFor(a),
_matchVariableModel(ofInterest: ['int', 'String']),
);
expect(
- s2.rebaseForward(h, s1).infoFor(a),
+ s2.rebaseForward(h, s1)._infoFor(a),
_matchVariableModel(ofInterest: ['int', 'String']),
);
});
@@ -4367,8 +4379,8 @@
test('variable present in one state but not the other', () {
var h = Harness();
var x = Var('x', 'Object?');
- var s0 = FlowModel<Var, Type>(Reachability.initial);
- var s1 = s0.declare(x, true);
+ var s0 = FlowModel<Type>(Reachability.initial);
+ var s1 = s0._declare(x, true);
expect(s1.rebaseForward(h, s0), same(s0));
expect(s0.rebaseForward(h, s1), same(s1));
});
@@ -4536,23 +4548,23 @@
});
group('join', () {
- var x = Var('x', 'Object?');
- var y = Var('y', 'Object?');
- var z = Var('z', 'Object?');
- var w = Var('w', 'Object?');
+ var x = _promotionKeyStore.keyForVariable(Var('x', 'Object?'));
+ var y = _promotionKeyStore.keyForVariable(Var('y', 'Object?'));
+ var z = _promotionKeyStore.keyForVariable(Var('z', 'Object?'));
+ var w = _promotionKeyStore.keyForVariable(Var('w', 'Object?'));
var intType = Type('int');
var intQType = Type('int?');
var stringType = Type('String');
- const emptyMap = const <Var, VariableModel<Var, Type>>{};
+ const emptyMap = const <int, VariableModel<Type>>{};
- VariableModel<Var, Type> model(List<Type>? promotionChain,
+ VariableModel<Type> model(List<Type>? promotionChain,
{List<Type>? typesOfInterest, bool assigned = false}) =>
- VariableModel<Var, Type>(
+ VariableModel<Type>(
promotedTypes: promotionChain,
tested: typesOfInterest ?? promotionChain ?? [],
assigned: assigned,
unassigned: !assigned,
- ssaNode: new SsaNode<Var, Type>(null));
+ ssaNode: new SsaNode<Type>(null));
group('without input reuse', () {
test('promoted with unpromoted', () {
@@ -4587,7 +4599,7 @@
x: model([intType]),
y: model([stringType])
};
- var p2 = <Var, VariableModel<Var, Type>>{};
+ var p2 = <int, VariableModel<Type>>{};
expect(FlowModel.joinVariableInfo(h, p1, p2, emptyMap), same(emptyMap));
expect(FlowModel.joinVariableInfo(h, p2, p1, emptyMap), same(emptyMap));
});
@@ -4724,19 +4736,19 @@
});
group('merge', () {
- var x = Var('x', 'Object?');
+ var x = _promotionKeyStore.keyForVariable(Var('x', 'Object?'));
var intType = Type('int');
var stringType = Type('String');
- const emptyMap = const <Var, VariableModel<Var, Type>>{};
+ const emptyMap = const <int, VariableModel<Type>>{};
- VariableModel<Var, Type> varModel(List<Type>? promotionChain,
+ VariableModel<Type> varModel(List<Type>? promotionChain,
{bool assigned = false}) =>
- VariableModel<Var, Type>(
+ VariableModel<Type>(
promotedTypes: promotionChain,
tested: promotionChain ?? [],
assigned: assigned,
unassigned: !assigned,
- ssaNode: new SsaNode<Var, Type>(null));
+ ssaNode: new SsaNode<Type>(null));
test('first is null', () {
var h = Harness();
@@ -4817,18 +4829,18 @@
});
group('inheritTested', () {
- var x = Var('x', 'Object?');
+ var x = _promotionKeyStore.keyForVariable(Var('x', 'Object?'));
var intType = Type('int');
var stringType = Type('String');
- const emptyMap = const <Var, VariableModel<Var, Type>>{};
+ const emptyMap = const <int, VariableModel<Type>>{};
- VariableModel<Var, Type> model(List<Type> typesOfInterest) =>
- VariableModel<Var, Type>(
+ VariableModel<Type> model(List<Type> typesOfInterest) =>
+ VariableModel<Type>(
promotedTypes: null,
tested: typesOfInterest,
assigned: true,
unassigned: false,
- ssaNode: new SsaNode<Var, Type>(null));
+ ssaNode: new SsaNode<Type>(null));
test('inherits types of interest from other', () {
var h = Harness();
@@ -5821,6 +5833,8 @@
});
}
+final _promotionKeyStore = PromotionKeyStore();
+
/// Returns the appropriate matcher for expecting an assertion error to be
/// thrown or not, based on whether assertions are enabled.
Matcher get _asserts {
@@ -5873,7 +5887,7 @@
Matcher assignedMatcher = wrapMatcher(assigned);
Matcher unassignedMatcher = wrapMatcher(unassigned);
Matcher writeCapturedMatcher = wrapMatcher(writeCaptured);
- return predicate((VariableModel<Var, Type> model) {
+ return predicate((VariableModel<Type> model) {
if (!chainMatcher.matches(model.promotedTypes, {})) return false;
if (!ofInterestMatcher.matches(model.tested, {})) return false;
if (!assignedMatcher.matches(model.assigned, {})) return false;
@@ -5900,18 +5914,50 @@
fail('Unexpected call to accept');
}
-extension on FlowModel<Var, Type> {
- ExpressionInfo<Var, Type> _tryMarkNonNullable(Harness h, Var variable) =>
+extension on FlowModel<Type> {
+ FlowModel<Type> _conservativeJoin(
+ Iterable<Var> writtenVariables, Iterable<Var> capturedVariables) =>
+ conservativeJoin([
+ for (Var v in writtenVariables) _promotionKeyStore.keyForVariable(v)
+ ], [
+ for (Var v in capturedVariables) _promotionKeyStore.keyForVariable(v)
+ ]);
+
+ FlowModel<Type> _declare(Var variable, bool initialized) => this.declare(
+ variable, _promotionKeyStore.keyForVariable(variable), initialized);
+
+ VariableModel<Type> _infoFor(Var variable) =>
+ infoFor(_promotionKeyStore.keyForVariable(variable));
+
+ ExpressionInfo<Type> _tryMarkNonNullable(Harness h, Var variable) =>
tryMarkNonNullable(h, _varRefWithType(variable));
- ExpressionInfo<Var, Type> _tryPromoteForTypeCheck(
+ ExpressionInfo<Type> _tryPromoteForTypeCheck(
Harness h, Var variable, String type) =>
tryPromoteForTypeCheck(h, _varRefWithType(variable), Type(type));
- Reference<Var, Type> _varRef(Var variable) =>
- new VariableReference<Var, Type>(variable);
+ Reference<Type> _varRef(Var variable) => new VariableReference<Var, Type>(
+ variable, _promotionKeyStore.keyForVariable(variable));
- ReferenceWithType<Var, Type> _varRefWithType(Var variable) =>
- new ReferenceWithType<Var, Type>(_varRef(variable),
- variableInfo[variable]?.promotedTypes?.last ?? variable.type);
+ ReferenceWithType<Type> _varRefWithType(Var variable) =>
+ new ReferenceWithType<Type>(
+ _varRef(variable),
+ variableInfo[_promotionKeyStore.keyForVariable(variable)]
+ ?.promotedTypes
+ ?.last ??
+ variable.type);
+
+ FlowModel<Type> _write(
+ NonPromotionReason? nonPromotionReason,
+ Var variable,
+ Type writtenType,
+ SsaNode<Type> newSsaNode,
+ Operations<Var, Type> operations) =>
+ write(
+ nonPromotionReason,
+ variable,
+ _promotionKeyStore.keyForVariable(variable),
+ writtenType,
+ newSsaNode,
+ operations);
}
diff --git a/pkg/_fe_analyzer_shared/test/mini_ast.dart b/pkg/_fe_analyzer_shared/test/mini_ast.dart
index 2267aaa..4d4c3ed 100644
--- a/pkg/_fe_analyzer_shared/test/mini_ast.dart
+++ b/pkg/_fe_analyzer_shared/test/mini_ast.dart
@@ -193,8 +193,7 @@
/// [ExpressionInfo] associated with it. If the expression has no flow
/// analysis information associated with it, `null` will be passed to
/// [callback].
- Expression getExpressionInfo(
- void Function(ExpressionInfo<Var, Type>?) callback) =>
+ Expression getExpressionInfo(void Function(ExpressionInfo<Type>?) callback) =>
new _GetExpressionInfo(this, callback);
/// If `this` is an expression `x`, creates the expression `x ?? other`.
@@ -604,8 +603,7 @@
/// Gets the SSA node associated with [variable] at the current point in
/// control flow, or `null` if the variable has been write captured.
- SsaNode<Var, Type>? operator [](Var variable) =>
- _flow.ssaNodeForTesting(variable);
+ SsaNode<Type>? operator [](Var variable) => _flow.ssaNodeForTesting(variable);
}
/// Representation of a statement in the pseudo-Dart language used for flow
@@ -1171,7 +1169,7 @@
class _GetExpressionInfo extends Expression {
final Expression target;
- final void Function(ExpressionInfo<Var, Type>?) callback;
+ final void Function(ExpressionInfo<Type>?) callback;
_GetExpressionInfo(this.target, this.callback);
@@ -1413,7 +1411,7 @@
flow.logicalBinaryOp_begin();
}
var leftType = analyzeExpression(lhs);
- EqualityInfo<Var, Type>? leftInfo;
+ EqualityInfo<Type>? leftInfo;
if (isEquals) {
leftInfo = flow.equalityOperand_end(lhs, leftType);
} else if (isLogical) {
diff --git a/pkg/analysis_server/benchmark/benchmarks.dart b/pkg/analysis_server/benchmark/benchmarks.dart
index 9dea223..7257714 100644
--- a/pkg/analysis_server/benchmark/benchmarks.dart
+++ b/pkg/analysis_server/benchmark/benchmarks.dart
@@ -33,7 +33,7 @@
);
runner.addCommand(ListCommand(benchmarks));
runner.addCommand(RunCommand(benchmarks));
- runner.run(args);
+ await runner.run(args);
}
String get analysisServerSrcPath {
diff --git a/pkg/analysis_server/benchmark/integration/driver.dart b/pkg/analysis_server/benchmark/integration/driver.dart
index b52e80a..5c16d36 100644
--- a/pkg/analysis_server/benchmark/integration/driver.dart
+++ b/pkg/analysis_server/benchmark/integration/driver.dart
@@ -112,7 +112,7 @@
logger.log(Level.FINE, 'requesting server shutdown');
// Give the server a short time to comply with the shutdown request; if it
// doesn't exit, then forcibly terminate it.
- sendServerShutdown();
+ unawaited(sendServerShutdown());
await server.exitCode.timeout(timeout, onTimeout: () {
return server.kill('server failed to exit');
});
diff --git a/pkg/analysis_server/benchmark/perf/memory_tests.dart b/pkg/analysis_server/benchmark/perf/memory_tests.dart
index 488c61c..6bc4b74 100644
--- a/pkg/analysis_server/benchmark/perf/memory_tests.dart
+++ b/pkg/analysis_server/benchmark/perf/memory_tests.dart
@@ -195,7 +195,7 @@
@override
Future<void> shutdown() async {
_test.tearDown();
- _logger.shutdown();
+ await _logger.shutdown();
}
@override
@@ -254,7 +254,7 @@
total += heapUsage['heapUsage'] + heapUsage['externalUsage'] as int;
}
- service.dispose();
+ await service.dispose();
return total;
}
diff --git a/pkg/analysis_server/doc/tutorial/quick_assist.md b/pkg/analysis_server/doc/tutorial/quick_assist.md
new file mode 100644
index 0000000..b42dab9
--- /dev/null
+++ b/pkg/analysis_server/doc/tutorial/quick_assist.md
@@ -0,0 +1,337 @@
+# Writing a quick assist
+
+This document describes what a quick assist is and outlines the basic steps for
+writing a quick assist.
+
+## Overview
+
+A quick assist is an automated code edit that is both local in scope and doesn't
+require any user input. By 'local in scope' we mean that the assist only needs
+information from the local library (the library in which it is invoked) and will
+only make changes to the local library. If a code edit requires user input,
+might depend on knowledge from outside the local library, or could make changes
+outside the local library, then it should probably be implemented using a
+refactoring.
+
+Unlike quick fixes, quick assists are displayed even when there are no
+diagnostics being reported against the code. They are not intended to fix
+problems that are being reported, but are used to perform common and
+straightforward code transformations.
+
+In this document we'll use a simple example of writing an assist to convert a
+decimal representation of an integer into a hexadecimal representation. While
+this wouldn't be a good assist to offer, it's simple enough that the details of
+the implementation won't mask the general process used to implement an assist.
+
+## Design considerations
+
+Because quick assists are computed on demand, they need to be computed quickly.
+(Some clients request quick assists every time the user repositions the cursor.)
+That places a performance requirement on quick assists, one that requires that
+the code to compute a quick assist can't perform any potentially lengthy
+computations such as searching all of the user's code or accessing the network.
+That, in turn, generally means that assists can only support localized changes.
+They can add or remove text in the local library, but generally can't do more
+than that.
+
+Unlike quick fixes, there is no signal to indicate which assists might apply at
+a given location in the code. That means that we have to test every assist to
+see whether it's appropriate, which puts a practical limit on the number of
+assists that we can implement. (Even if each assist only takes 100 milliseconds
+to determine whether it applies, if we have 100 assists it will take 10 seconds
+to return the list of assists to the user, which is too slow.) That means that
+we need to be discerning about which assists are implemented and work to make
+assists return quickly if they are not appropriate.
+
+## Describing the assist
+
+Each assist has an instance of the class `AssistKind` associated with it. The
+existing assists for Dart are defined in the class `DartAssistKind`. An
+assist kind has an identifier, a priority, and a message.
+
+The identifier is used by some LSP-based clients to provide user-defined
+shortcuts. It's a hierarchical dot-separated identifier and should follow the
+pattern seen in the existing assist kinds.
+
+The priority is used to order the list of assists when presented to the user.
+The larger the value the closer to the top of the list it will appear. If you're
+implementing an assist for Dart files, you should use one of the constants
+defined in `DartAssistKindPriority` (typically
+`DartAssistKindPriority.DEFAULT`), or add a new constant if there's a need for
+it.
+
+The message is what will be displayed to the user by the client. This should be
+a sentence fragment (no terminating period) that would be appropriate as a label
+in a menu or on a button. It should describe the change that will be made to the
+user's code.
+
+Create a static field whose value is an `AssistKind` describing the assist
+you're implementing. For our example you might define a new constant in
+`DartAssistKind` like this:
+
+```dart
+static const CONVERT_TO_HEX = AssistKind(
+ 'dart.assist.convert.toHex',
+ DartAssistKindPriority.DEFAULT,
+ "Convert to hexadecimal",
+);
+```
+
+### Implementing the assist, part 1
+
+To implement the assist you'll create a subclass of `CorrectionProducer`. Most
+of the existing correction producers are in the directory
+`analysis_server/lib/src/services/correction/dart`, so we'll start by creating
+a file named `convert_to_hex.dart` in that directory that contains the following
+(with the year updated appropriately):
+
+```dart
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/correction/assist.dart';
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class ConvertToHex extends CorrectionProducer {
+ @override
+ AssistKind get assistKind => DartAssistKind.CONVERT_TO_HEX;
+
+ @override
+ Future<void> compute(ChangeBuilder builder) async {
+ }
+}
+```
+
+The `compute` method is where the assist will be built. We'll come back to it in
+"Implementing the assist, part 2".
+
+The `assistKind` getter is how you associate the assist kind we created earlier
+with the assist produced by the `compute` method.
+
+There's another getter you might need to override. The message associated with
+the assist kind is actually a template that can be filled in at runtime. The
+placeholders in the message are denoted by integers inside curly braces (such as
+`{0}`). The integers are indexes into a list of replacement values (hence, zero
+based), and the getter `assistArguments` returns the list of replacement values.
+The message we used above doesn't have any placeholders, but if we'd written the
+message as `"Convert to '{0}'"`, then we could return the replacement values by
+implementing:
+
+```dart
+String _hexRepresentation = '';
+
+@override
+List<Object> get assistArguments => [_hexRepresentation];
+```
+
+and assigning the replacement string to the field inside the `compute` method.
+
+If you don't implement this getter, then the inherited getter will return an
+empty list. The number of elements in the list must match the largest index used
+in the message. If it doesn't, an exception will be thrown at runtime.
+
+### Testing the assist
+
+Before we look at implementing the `compute` method, we should probably write
+some tests. Even if you don't normally use a test-driven approach to coding, we
+recommend it in this case because writing the tests can help you think of corner
+cases that the implementation will need to handle. The corresponding tests are
+in the directory `analysis_server/test/src/services/correction/assist`, so we'll
+create a file named `convert_to_hex_test.dart` that contains the following:
+
+```dart
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/correction/assist.dart';
+import 'package:analyzer_plugin/utilities/assist/assist.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'assist_processor.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(ConvertToHexTest);
+ });
+}
+
+@reflectiveTest
+class ConvertToHexTest extends AssistProcessorTest {
+ @override
+ AssistKind get kind => DartAssistKind.CONVERT_TO_HEX;
+}
+```
+
+This getter tells the test framework to expect an assist of the returned kind.
+
+The test can then be written in a method that looks something like this:
+
+```dart
+Future<void> test_positive() async {
+ await resolveTestCode('''
+var c = /*caret*/42;
+''');
+ await assertHasAssist('''
+var c = 0x2a;
+''');
+}
+```
+
+The test framework will look for the marker `/*caret*/`, remember it's offset,
+create a file containing the first piece of code with the marker removed, run
+the assist over the code, use our correction producer to build an edit, apply
+the edit to the file, and textually compare the results with the second piece of
+code.
+
+### Registering the assist
+
+Before we can run the test, we need to register the correction producer so that
+it can be run.
+
+The list of assists is computed by the `AssistProcessor`, which has two static
+lists named `generators` and `multiGenerators` that contain the correction
+producers used to compute the assists.
+
+Actually, the lists contain functions used to create the producers. We do that
+so that producers can't accidentally carry state over from one use to the next.
+These functions are usually a tear-off of the correction producer's constructor.
+
+The last step is to add your correction producer to the list named `generators`.
+We'll talk about the other list in "Multi-assist producers".
+
+At this point you should be able to run the test and see it failing.
+
+### Implementing the assist, part 2
+
+We're now at a point where we can finish the implementation of the assist by
+implementing the `compute` method.
+
+The correction producer has access to most of the information you should need in
+order to write the assist. The change builder passed to `compute` is how you
+construct the edit that will be sent back to the client.
+
+The first step in the implementation of any assist is to find the location in
+the AST where the cursor is positioned and verify that all the conditions on
+which the assist is predicated are valid. For example, for our assist we'll need
+to ensure that the cursor is on an integer literal, that the literal consists of
+decimal digits, and that the value is a valid integer.
+
+Finding the AST node is easy because it's done for you by the assist processor
+before `compute` is invoked. All you have to do is use the getter `node` to find
+the node at the cursor.
+
+```dart
+@override
+Future<void> compute(ChangeBuilder builder) async {
+ var node = this.node;
+}
+```
+
+Then we need to verify that this node is an integer literal:
+
+```dart
+@override
+Future<void> compute(ChangeBuilder builder) async {
+ var node = this.node;
+ if (node is! IntegerLiteral) {
+ return;
+ }
+}
+```
+
+If it isn't, then we'll return. Because we haven't used the builder to create a
+assist, returning now means that no assist from this producer will be sent to
+the client.
+
+We'll also check that the integer has the right form and is valid:
+
+```dart
+@override
+Future<void> compute(ChangeBuilder builder) async {
+ var node = this.node;
+ if (node is! IntegerLiteral) {
+ return;
+ }
+ var value = node.value;
+ if (value == null) {
+ return;
+ }
+ if (node.literal.lexeme.contains(RegExp('[^0-9]'))) {
+ return;
+ }
+}
+```
+
+After all those checks we now know that we have a decimal integer that we can
+convert. Note that we check for a `null` value before checking for non-decimal
+digits because it's a faster check and we want the assist to fail as quickly as
+possible.
+
+We're now ready to create the edit. To do that we're going to use the
+`ChangeBuilder` passed to the `compute` method. In the example below we'll
+introduce a couple of the methods on `ChangeBuilder`, but for more information
+you can read [Creating `SourceChange`s](https://github.com/dart-lang/sdk/blob/main/pkg/analyzer_plugin/doc/tutorial/creating_edits.md).
+
+```dart
+@override
+Future<void> compute(ChangeBuilder builder) async {
+ var node = this.node;
+ if (node is! IntegerLiteral) {
+ return;
+ }
+ var value = node.value;
+ if (value == null) {
+ return;
+ }
+ if (node.literal.lexeme.contains(RegExp('[^0-9]'))) {
+ return;
+ }
+ await builder.addDartFileEdit(file, (builder) {
+ var hexDigits = value.toRadixString(16);
+ builder.addSimpleReplacement(range.node(node), '0x$hexDigits');
+ });
+}
+```
+
+We're using `addDartFileEdit` to create an edit in a `.dart` file. In this case
+the edit is simple: we're just replacing one representation of the integer
+literal with a representation of the same value in a different base. The getter
+`range` returns a `RangeFactory`, a utility class with lots of methods to make
+it easier to create `SourceRange`s.
+
+We're missing several test cases. Minimally we should test that the assist works
+correctly with negative values (it doesn't), and that it will not produce an
+edit if the integer literal isn't valid. We'll leave adding such tests as an
+exercise for the reader.
+
+In this example we're just making a simple replacement, so we're avoiding any
+need to worry about formatting. As a general principle we don't attempt to
+format the code after it's been modified, but we do make an effort to leave the
+code in a reasonably readable state. There's a getter (`eol`) that you can use
+to get the end-of-line marker that should be used in the file, and there's
+another getter (`utils`) that will return an object with several utility methods
+that help with things like getting the right indentation for nested code.
+
+### Multi-assist producers
+
+We skipped over the list named `multiGenerators` earlier, promising that we'd
+return to it later. You'll probably never have a need for it, but in case you do
+this section will hopefully tell you what you need to know.
+
+There's a subclass of `CorrectionProducer` named `MultiCorrectionProducer` and
+this list is how you register one of them. That class exists for rare cases
+where you need to use a single correction producer to produce multiple assists.
+This is generally only needed when you can't know in advance the maximum number
+of assists that might need to be produced. For example, there's a set of assists
+to wrap a Flutter `Widget` in another widget, but the set of widgets that can
+wrap a given widget depends on the widget being wrapped.
+
+If you are able to enumerate the possible assists ahead of time, then you're
+probably better off to create one subclass of `CorrectionProducer` for each of
+the assists.
diff --git a/pkg/analysis_server/lib/src/analysis_server.dart b/pkg/analysis_server/lib/src/analysis_server.dart
index f553073..64db45c 100644
--- a/pkg/analysis_server/lib/src/analysis_server.dart
+++ b/pkg/analysis_server/lib/src/analysis_server.dart
@@ -805,7 +805,7 @@
_refactoringManager = RefactoringManager(this, refactoringWorkspace);
}
- Future<void> _scheduleAnalysisImplementedNotification() async {
+ void _scheduleAnalysisImplementedNotification() {
var files = analysisServices[AnalysisService.IMPLEMENTED];
if (files != null) {
scheduleImplementedNotification(this, files);
diff --git a/pkg/analysis_server/lib/src/cider/rename.dart b/pkg/analysis_server/lib/src/cider/rename.dart
index 8d967e5..d1b71bd 100644
--- a/pkg/analysis_server/lib/src/cider/rename.dart
+++ b/pkg/analysis_server/lib/src/cider/rename.dart
@@ -53,7 +53,7 @@
} else if (element is ConstructorElement) {
status = validateConstructorName(name);
_analyzePossibleConflicts(element, status, name);
- } else if (element is ImportElement) {
+ } else if (element is ImportElement2) {
status = validateImportPrefixName(name);
}
@@ -156,7 +156,7 @@
replaceMatches.addMatch(result.path, result.matches.toList());
}
}
- } else if (element is ImportElement) {
+ } else if (element is ImportElement2) {
var replaceInfo = <ReplaceInfo>[];
for (var match in matches) {
for (var ref in match.references) {
@@ -214,29 +214,28 @@
lineInfo.getLocation(element.setter!.nameOffset),
element.setter!.nameLength));
}
- } else if (element is ImportElement) {
- var prefix = element.prefix;
+ } else if (element is ImportElement2) {
var unit =
(await canRename._fileResolver.resolve2(path: sourcePath)).unit;
- var index = element.library.imports.indexOf(element);
+ var index = element.library.imports2.indexOf(element);
var node = unit.directives.whereType<ImportDirective>().elementAt(index);
+ final prefixNode = node.prefix;
if (newName.isEmpty) {
// We should not get `prefix == null` because we check in
// `checkNewName` that the new name is different.
- if (prefix == null) {
- return infos;
+ if (prefixNode != null) {
+ var prefixEnd = prefixNode.end;
+ infos.add(ReplaceInfo(newName, lineInfo.getLocation(node.uri.end),
+ prefixEnd - node.uri.end));
}
- var prefixEnd = prefix.nameOffset + prefix.nameLength;
- infos.add(ReplaceInfo(newName, lineInfo.getLocation(node.uri.end),
- prefixEnd - node.uri.end));
} else {
- if (prefix == null) {
+ if (prefixNode == null) {
var uriEnd = node.uri.end;
infos.add(
ReplaceInfo(' as $newName', lineInfo.getLocation(uriEnd), 0));
} else {
- var offset = prefix.nameOffset;
- var length = prefix.nameLength;
+ var offset = prefixNode.offset;
+ var length = prefixNode.length;
infos.add(ReplaceInfo(newName, lineInfo.getLocation(offset), length));
}
}
@@ -387,7 +386,7 @@
if (element is ConstructorElement) {
return true;
}
- if (element is ImportElement) {
+ if (element is ImportElement2) {
return true;
}
if (element is LabelElement || element is LocalElement) {
diff --git a/pkg/analysis_server/lib/src/computer/computer_call_hierarchy.dart b/pkg/analysis_server/lib/src/computer/computer_call_hierarchy.dart
new file mode 100644
index 0000000..5218d75
--- /dev/null
+++ b/pkg/analysis_server/lib/src/computer/computer_call_hierarchy.dart
@@ -0,0 +1,477 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/protocol_server.dart' as protocol;
+import 'package:analysis_server/src/search/element_references.dart';
+import 'package:analysis_server/src/services/search/search_engine.dart';
+import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/visitor.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/source/source_range.dart';
+import 'package:analyzer/src/dart/ast/element_locator.dart';
+import 'package:analyzer/src/dart/ast/utilities.dart';
+
+/// A [CallHierarchyItem] and a set of ranges that call to or from it.
+class CallHierarchyCalls {
+ final CallHierarchyItem item;
+ final List<SourceRange> ranges;
+
+ CallHierarchyCalls(this.item, [List<SourceRange>? ranges])
+ : ranges = ranges ?? [];
+}
+
+/// An item that can appear in a Call Hierarchy.
+///
+/// Items are the containers that can contain calls to other items. This
+/// includes not only functions and methods, but classes (because they can call
+/// functions in class field initializers) and files (because they can call
+/// functions in top level variable initializers).
+///
+/// Implicit constructors are represented using the location of their classes
+/// (since do not themselves have a range) but a [kind] of
+/// [CallHierarchyKind.constructor].
+class CallHierarchyItem {
+ /// The kind of this item in the Call Hierarchy.
+ ///
+ /// This may be used to select an icon to show in the Call Hierarchy tree.
+ final CallHierarchyKind kind;
+
+ // The user-visible name for this item in the Call Hierarchy.
+ final String displayName;
+
+ /// The file that contains the declaration of this item.
+ final String file;
+
+ /// The range of the name at the declaration of this item.
+ final SourceRange range;
+
+ // TODO(dantup): This class will need to contain two ranges (codeRange +
+ // selectionRange) for LSP.
+
+ CallHierarchyItem.forElement(
+ Element element, {
+ required this.range,
+ }) : displayName = element is CompilationUnitElement
+ ? element.source.shortName
+ : element is PropertyAccessorElement
+ ? element.isGetter
+ ? 'get ${element.displayName}'
+ : 'set ${element.displayName}'
+ : element.displayName,
+ file = element.source!.fullName,
+ kind = CallHierarchyKind.forElement(element);
+
+ CallHierarchyItem.forProtocolElement(
+ protocol.Element element, {
+ required this.range,
+ }) : displayName = element.name,
+ file = element.location!.file,
+ kind = CallHierarchyKind.forProtocolElement(element);
+}
+
+/// Kinds of [CallHierarchyItem] that can make calls to other
+/// [CallHierarchyItem]s.
+enum CallHierarchyKind {
+ class_,
+ constructor,
+ extension,
+ file,
+ function,
+ method,
+ mixin,
+ property,
+ unknown;
+
+ static const _elementMapping = {
+ ElementKind.CLASS: class_,
+ ElementKind.COMPILATION_UNIT: file,
+ ElementKind.CONSTRUCTOR: constructor,
+ ElementKind.EXTENSION: extension,
+ ElementKind.FUNCTION: function,
+ ElementKind.GETTER: property,
+ ElementKind.METHOD: method,
+ ElementKind.SETTER: property,
+ };
+
+ static const _protocolElementMapping = {
+ protocol.ElementKind.CLASS: class_,
+ protocol.ElementKind.COMPILATION_UNIT: file,
+ protocol.ElementKind.CONSTRUCTOR: constructor,
+ protocol.ElementKind.EXTENSION: extension,
+ protocol.ElementKind.FUNCTION: function,
+ protocol.ElementKind.GETTER: property,
+ protocol.ElementKind.METHOD: method,
+ protocol.ElementKind.MIXIN: mixin,
+ protocol.ElementKind.SETTER: property,
+ };
+
+ static CallHierarchyKind forElement(Element element) =>
+ _elementMapping[element.kind] ?? unknown;
+
+ static CallHierarchyKind forProtocolElement(protocol.Element element) =>
+ _protocolElementMapping[element.kind] ?? unknown;
+}
+
+/// A computer for Call Hierarchies.
+///
+/// Call Hierarchies are computed lazily and roughly follow the LSP model, which
+/// is:
+///
+/// 1. Client calls `findTarget(int offset)` to locate a starting point for
+/// navigation. The starting point is always based on the declaration of
+/// the executable code even if invoked at a call site.
+/// 2. Client passes the returned target into `findIncomingCalls()` or
+/// `findOutgoingCalls()` to find relevant calls in to/out of that item.
+/// The result will contain a set of other items, along with the ranges
+/// of the calls in or out.
+///
+/// It is expected that clients will call the methods above at different times
+/// (as the user expands items in a tree). It is up to the caller to handle
+/// cases where a target may no longer be valid (due to file modifications) that
+/// may result in inconsistent results.
+class DartCallHierarchyComputer {
+ final ResolvedUnitResult _result;
+
+ DartCallHierarchyComputer(this._result);
+
+ /// Finds incoming calls to [target], returning the elements that call them
+ /// and ranges of those calls within.
+ Future<List<CallHierarchyCalls>> findIncomingCalls(
+ CallHierarchyItem target,
+ SearchEngine searchEngine,
+ ) async {
+ assert(target.file == _result.path);
+ final node = _findTargetNode(target.range.offset);
+ var element = _getElementOfNode(node);
+ if (element == null) {
+ return [];
+ }
+
+ // Implicit constructors are handled using the Class element and a kind of
+ // `constructor`, because we need to locate them using an `offset`, which
+ // implicit constructors do not have.
+ // Here, we map them back to the synthetic constructor element.
+ final isImplicitConstructor =
+ element is ClassElement && target.kind == CallHierarchyKind.constructor;
+ if (isImplicitConstructor) {
+ element = element.unnamedConstructor;
+ }
+
+ // We only find incoming calls to executable elements.
+ if (element is! ExecutableElement) {
+ return [];
+ }
+
+ final computer = ElementReferencesComputer(searchEngine);
+ final references = await computer.compute(element, false);
+
+ // Group results by their container, since we only want to return a single
+ // entry for a body, with a set of ranges within.
+ final resultsByContainer = <protocol.Element, CallHierarchyCalls>{};
+ // We may need to fetch parsed results for the other files, reuse them
+ // across calls.
+ final parsedUnits = <String, SomeParsedUnitResult?>{};
+ for (final reference in references) {
+ final container = _getContainer(reference.path);
+ if (container == null) {
+ continue;
+ }
+
+ // Create an item for a container the first time we see it.
+ final containerCalls = resultsByContainer.putIfAbsent(
+ container,
+ () => CallHierarchyCalls(CallHierarchyItem.forProtocolElement(
+ container,
+ range: _rangeForProtocolElement(container),
+ )),
+ );
+
+ // Add this match to the containers results.
+ final range = _rangeForSearchResult(reference, parsedUnits);
+ containerCalls.ranges.add(range);
+ }
+
+ return resultsByContainer.values.toList();
+ }
+
+ /// Finds outgoing calls from [target], returning the elements that are called
+ /// and the ranges that call them.
+ Future<List<CallHierarchyCalls>> findOutgoingCalls(
+ CallHierarchyItem target,
+ ) async {
+ assert(target.file == _result.path);
+ final node = _findTargetNode(target.range.offset);
+ if (node == null) {
+ return [];
+ }
+
+ // Don't look for outbound calls in things that aren't functions.
+ if (!(node is FunctionDeclaration ||
+ node is ConstructorDeclaration ||
+ node is MethodDeclaration)) {
+ return [];
+ }
+
+ final referenceNodes = <AstNode>{};
+ node.accept(_OutboundCallVisitor(node, referenceNodes.add));
+
+ // Group results by their target, since we only want to return a single
+ // entry for each target, with a set of ranges that call it.
+ final resultsByTarget = <Element, CallHierarchyCalls>{};
+ for (final referenceNode in referenceNodes) {
+ final target = _getElementOfNode(referenceNode);
+ if (target == null) {
+ continue;
+ }
+
+ // Create an item for a target the first time we see it.
+ final calls = resultsByTarget.putIfAbsent(
+ target,
+ () => CallHierarchyCalls(CallHierarchyItem.forElement(
+ target,
+ range: _rangeForElement(target),
+ )),
+ );
+
+ // Add this call to the targets results.
+ final range = _rangeForNode(referenceNode);
+ calls.ranges.add(range);
+ }
+
+ return resultsByTarget.values.toList();
+ }
+
+ /// Finds a target for starting call hierarchy navigation at [offset].
+ ///
+ /// If [offset] is an invocation, returns information about the [Element] it
+ /// refers to.
+ CallHierarchyItem? findTarget(int offset) {
+ final node = _findTargetNode(offset);
+ final element = _getElementOfNode(node);
+
+ // We only return targets that are executable elements.
+ return element is ExecutableElement
+ ? CallHierarchyItem.forElement(element,
+ range: _rangeForElement(element))
+ : null;
+ }
+
+ /// Finds a target node for call hierarchy navigation at [offset].
+ AstNode? _findTargetNode(int offset) {
+ var node = NodeLocator(offset).searchWithin(_result.unit);
+ if (node is SimpleIdentifier &&
+ node.parent != null &&
+ node.parent is! VariableDeclaration &&
+ node.parent is! AssignmentExpression) {
+ node = node.parent;
+ }
+
+ // For consistency with other places, we only treat the type name as the
+ // constructor for unnamed constructor (since we use the constructors name
+ // as the target).
+ if (node is NamedType) {
+ final parent = node.parent;
+ if (parent is ConstructorName) {
+ final name = parent.name;
+ if (name != null && offset < name.offset) {
+ return null;
+ }
+ }
+ } else if (node is ConstructorDeclaration) {
+ final name = node.name;
+ if (name != null && offset < name.offset) {
+ return null;
+ }
+ }
+
+ return node;
+ }
+
+ /// Returns the enclosing element (class, function body, compliation unit) for
+ /// the reference [element].
+ ///
+ /// This is used to construct (and group calls by) a [CallHierarchyItem] that
+ /// contains calls.
+ protocol.Element? _getContainer(List<protocol.Element> elements) {
+ return elements.firstWhere((parent) =>
+ parent.kind == protocol.ElementKind.CLASS ||
+ parent.kind == protocol.ElementKind.COMPILATION_UNIT ||
+ parent.kind == protocol.ElementKind.CONSTRUCTOR ||
+ parent.kind == protocol.ElementKind.ENUM ||
+ parent.kind == protocol.ElementKind.EXTENSION ||
+ parent.kind == protocol.ElementKind.FUNCTION ||
+ parent.kind == protocol.ElementKind.GETTER ||
+ parent.kind == protocol.ElementKind.METHOD ||
+ parent.kind == protocol.ElementKind.MIXIN ||
+ parent.kind == protocol.ElementKind.SETTER);
+ }
+
+ /// Return the [Element] of the given [node], or `null` if [node] is `null`,
+ /// does not have an element, or the element is not a valid target for call
+ /// hierarchy.
+ Element? _getElementOfNode(AstNode? node) {
+ if (node == null) {
+ return null;
+ }
+ final parent = node.parent;
+
+ // ElementLocator returns the class for default constructor calls and null
+ // for constructor names.
+ if (node is NamedType && parent is ConstructorName) {
+ return parent.staticElement;
+ } else if (node is ConstructorName) {
+ return node.staticElement;
+ } else if (node is PropertyAccess) {
+ node = node.propertyName;
+ }
+
+ final element = ElementLocator.locate(node);
+
+ // Don't consider synthetic getter/setter for a field to be executable
+ // since they don't contain any executable code.
+ if (element is PropertyAccessorElement && element.isSynthetic) {
+ return null;
+ }
+
+ return element;
+ }
+
+ /// Returns the [SourceRange] to use for [element].
+ ///
+ /// The returned range covers only the name of the target and does not include
+ /// any parameter list.
+ SourceRange _rangeForElement(Element element) {
+ // For synthetic items (like implicit constructors), use the nonSynthetic
+ // element for the location.
+ element = element.nonSynthetic;
+ return SourceRange(element.nameOffset, element.nameLength);
+ }
+
+ /// Returns the [SourceRange] to use for [node].
+ ///
+ /// The returned range covers only the name of the target and does not include
+ /// the argument list.
+ SourceRange _rangeForNode(AstNode node) {
+ if (node is MethodInvocation) {
+ return SourceRange(
+ node.methodName.offset,
+ node.methodName.length,
+ );
+ } else if (node is InstanceCreationExpression) {
+ return SourceRange(
+ node.constructorName.offset,
+ node.constructorName.length,
+ );
+ } else if (node is PropertyAccess) {
+ return SourceRange(
+ node.propertyName.offset,
+ node.propertyName.length,
+ );
+ }
+ return SourceRange(node.offset, node.length);
+ }
+
+ /// Returns the [SourceRange] to use for [element].
+ ///
+ /// The returned range covers only the name of the target and does not include
+ /// any parameter list.
+ SourceRange _rangeForProtocolElement(protocol.Element element) {
+ return SourceRange(element.location!.offset, element.location!.length);
+ }
+
+ /// Returns the [SourceRange] for [match].
+ ///
+ /// Usually this is the range returned from the search index, but sometimes it
+ /// will be adjusted to reflect the source range that should be highlighted
+ /// for this call. For example, an unnamed constructor may be given a range
+ /// covering the type name (whereas the index has a zero-width range after the
+ /// type name).
+ SourceRange _rangeForSearchResult(
+ protocol.SearchResult match,
+ Map<String, SomeParsedUnitResult?> parsedUnits,
+ ) {
+ var offset = match.location.offset;
+ var length = match.location.length;
+ final file = match.location.file;
+ final result = parsedUnits.putIfAbsent(
+ file,
+ () => _result.session.getParsedUnit(file),
+ );
+
+ if (result is ParsedUnitResult) {
+ final node = NodeLocator(offset).searchWithin(result.unit);
+ if (node != null && !node.isSynthetic) {
+ final parent = node.parent;
+ // Handle named constructors which have their `.` included in the
+ // search index range, but we just want to highlight the name.
+ if (node is SimpleIdentifier && parent is MethodInvocation) {
+ offset = parent.methodName.offset;
+ length = parent.methodName.length;
+ } else if (length == 0) {
+ // If the search index length was 0, prefer the nodes range.
+ offset = node.offset;
+ length = node.length;
+ }
+ }
+ }
+
+ return SourceRange(offset, length);
+ }
+}
+
+/// Collects outbound calls from a node.
+class _OutboundCallVisitor extends RecursiveAstVisitor<void> {
+ final AstNode root;
+ final void Function(AstNode) collect;
+
+ _OutboundCallVisitor(this.root, this.collect);
+
+ @override
+ void visitConstructorName(ConstructorName node) {
+ collect(node.name ?? node);
+ super.visitConstructorName(node);
+ }
+
+ @override
+ void visitFunctionDeclaration(FunctionDeclaration node) {
+ // Only descend into function declarations if they are the target/root
+ // function.
+ if (node == root) {
+ super.visitFunctionDeclaration(node);
+ }
+ }
+
+ @override
+ void visitFunctionReference(FunctionReference node) {
+ collect(node);
+ super.visitFunctionReference(node);
+ }
+
+ @override
+ void visitMethodInvocation(MethodInvocation node) {
+ collect(node.methodName);
+ super.visitMethodInvocation(node);
+ }
+
+ @override
+ void visitPrefixedIdentifier(PrefixedIdentifier node) {
+ collect(node.identifier);
+ super.visitPrefixedIdentifier(node);
+ }
+
+ @override
+ void visitPropertyAccess(PropertyAccess node) {
+ collect(node.propertyName);
+ super.visitPropertyAccess(node);
+ }
+
+ @override
+ void visitSimpleIdentifier(SimpleIdentifier node) {
+ if (node.staticElement is FunctionElement && !node.inDeclarationContext()) {
+ collect(node);
+ }
+ super.visitSimpleIdentifier(node);
+ }
+}
diff --git a/pkg/analysis_server/lib/src/computer/import_elements_computer.dart b/pkg/analysis_server/lib/src/computer/import_elements_computer.dart
index a29230f..e1a152f 100644
--- a/pkg/analysis_server/lib/src/computer/import_elements_computer.dart
+++ b/pkg/analysis_server/lib/src/computer/import_elements_computer.dart
@@ -339,7 +339,7 @@
/// [importedElements]. They will match if they import the same library using
/// the same prefix.
bool _matches(ImportDirective import, ImportedElements importedElements) {
- var importElement = import.element;
+ var importElement = import.element2;
if (importElement == null) {
return false;
}
diff --git a/pkg/analysis_server/lib/src/domains/completion/available_suggestions.dart b/pkg/analysis_server/lib/src/domains/completion/available_suggestions.dart
index c7ec048..e98d0fa 100644
--- a/pkg/analysis_server/lib/src/domains/completion/available_suggestions.dart
+++ b/pkg/analysis_server/lib/src/domains/completion/available_suggestions.dart
@@ -97,7 +97,7 @@
var uniqueElements = _UniqueImportedElements();
var existingImports = <protocol.ExistingImport>[];
- var importElementList = resolvedUnit.libraryElement.imports;
+ var importElementList = resolvedUnit.libraryElement.imports2;
for (var import in importElementList) {
var importedLibrary = import.importedLibrary;
if (importedLibrary == null) continue;
diff --git a/pkg/analysis_server/lib/src/handler/legacy/analysis_reanalyze.dart b/pkg/analysis_server/lib/src/handler/legacy/analysis_reanalyze.dart
index 988d2e9..cbf090a 100644
--- a/pkg/analysis_server/lib/src/handler/legacy/analysis_reanalyze.dart
+++ b/pkg/analysis_server/lib/src/handler/legacy/analysis_reanalyze.dart
@@ -16,14 +16,14 @@
@override
Future<void> handle() async {
- server.options.analytics?.sendEvent('analysis', 'reanalyze');
+ unawaited(server.options.analytics?.sendEvent('analysis', 'reanalyze'));
await server.reanalyze();
//
// Restart all of the plugins. This is an async operation that will happen
// in the background.
//
- server.pluginManager.restartPlugins();
+ unawaited(server.pluginManager.restartPlugins());
sendResult(AnalysisReanalyzeResult());
}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/analysis_set_analysis_roots.dart b/pkg/analysis_server/lib/src/handler/legacy/analysis_set_analysis_roots.dart
index 6f2c5e2..d01a068 100644
--- a/pkg/analysis_server/lib/src/handler/legacy/analysis_set_analysis_roots.dart
+++ b/pkg/analysis_server/lib/src/handler/legacy/analysis_set_analysis_roots.dart
@@ -21,8 +21,11 @@
var includedPathList = params.included;
var excludedPathList = params.excluded;
- server.options.analytics?.sendEvent('analysis', 'setAnalysisRoots',
- value: includedPathList.length);
+ unawaited(server.options.analytics?.sendEvent(
+ 'analysis',
+ 'setAnalysisRoots',
+ value: includedPathList.length,
+ ));
server.analyticsManager.startedSetAnalysisRoots(params);
// validate
diff --git a/pkg/analysis_server/lib/src/handler/legacy/analytics_send_event.dart b/pkg/analysis_server/lib/src/handler/legacy/analytics_send_event.dart
index afb36ff..2051bd6 100644
--- a/pkg/analysis_server/lib/src/handler/legacy/analytics_send_event.dart
+++ b/pkg/analysis_server/lib/src/handler/legacy/analytics_send_event.dart
@@ -25,7 +25,7 @@
}
var params = AnalyticsSendEventParams.fromRequest(request);
- analytics.sendEvent(_clientId, params.action);
+ unawaited(analytics.sendEvent(_clientId, params.action));
sendResult(AnalyticsSendEventResult());
}
}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/analytics_send_timing.dart b/pkg/analysis_server/lib/src/handler/legacy/analytics_send_timing.dart
index bddc7d0..476abfa 100644
--- a/pkg/analysis_server/lib/src/handler/legacy/analytics_send_timing.dart
+++ b/pkg/analysis_server/lib/src/handler/legacy/analytics_send_timing.dart
@@ -25,7 +25,8 @@
}
var params = AnalyticsSendTimingParams.fromRequest(request);
- analytics.sendTiming(params.event, params.millis, category: _clientId);
+ unawaited(
+ analytics.sendTiming(params.event, params.millis, category: _clientId));
sendResult(AnalyticsSendTimingResult());
}
}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/completion_get_suggestions.dart b/pkg/analysis_server/lib/src/handler/legacy/completion_get_suggestions.dart
index 62907af..c29308e 100644
--- a/pkg/analysis_server/lib/src/handler/legacy/completion_get_suggestions.dart
+++ b/pkg/analysis_server/lib/src/handler/legacy/completion_get_suggestions.dart
@@ -41,7 +41,7 @@
}
var performance = OperationPerformanceImpl('<root>');
- performance.runAsync(
+ await performance.runAsync(
'request',
(performance) async {
if (file.endsWith('.yaml')) {
diff --git a/pkg/analysis_server/lib/src/handler/legacy/completion_get_suggestions2.dart b/pkg/analysis_server/lib/src/handler/legacy/completion_get_suggestions2.dart
index d889bbd..e96ab6a 100644
--- a/pkg/analysis_server/lib/src/handler/legacy/completion_get_suggestions2.dart
+++ b/pkg/analysis_server/lib/src/handler/legacy/completion_get_suggestions2.dart
@@ -147,7 +147,7 @@
}
var performance = OperationPerformanceImpl('<root>');
- performance.runAsync(
+ await performance.runAsync(
'request',
(performance) async {
var resolvedUnit = await performance.runAsync(
diff --git a/pkg/analysis_server/lib/src/handler/legacy/edit_format.dart b/pkg/analysis_server/lib/src/handler/legacy/edit_format.dart
index bf77274..d09c2ee 100644
--- a/pkg/analysis_server/lib/src/handler/legacy/edit_format.dart
+++ b/pkg/analysis_server/lib/src/handler/legacy/edit_format.dart
@@ -20,7 +20,7 @@
@override
Future<void> handle() async {
- server.options.analytics?.sendEvent('edit', 'format');
+ unawaited(server.options.analytics?.sendEvent('edit', 'format'));
var params = EditFormatParams.fromRequest(request);
var file = params.file;
diff --git a/pkg/analysis_server/lib/src/handler/legacy/edit_get_postfix_completion.dart b/pkg/analysis_server/lib/src/handler/legacy/edit_get_postfix_completion.dart
index ba79981..db908dd 100644
--- a/pkg/analysis_server/lib/src/handler/legacy/edit_get_postfix_completion.dart
+++ b/pkg/analysis_server/lib/src/handler/legacy/edit_get_postfix_completion.dart
@@ -18,7 +18,10 @@
@override
Future<void> handle() async {
- server.options.analytics?.sendEvent('edit', 'getPostfixCompletion');
+ unawaited(server.options.analytics?.sendEvent(
+ 'edit',
+ 'getPostfixCompletion',
+ ));
var params = EditGetPostfixCompletionParams.fromRequest(request);
var file = params.file;
diff --git a/pkg/analysis_server/lib/src/handler/legacy/edit_organize_directives.dart b/pkg/analysis_server/lib/src/handler/legacy/edit_organize_directives.dart
index 99967a5..2f06e89 100644
--- a/pkg/analysis_server/lib/src/handler/legacy/edit_organize_directives.dart
+++ b/pkg/analysis_server/lib/src/handler/legacy/edit_organize_directives.dart
@@ -21,7 +21,10 @@
@override
Future<void> handle() async {
// TODO(brianwilkerson) Move analytics tracking out of [handleRequest].
- server.options.analytics?.sendEvent('edit', 'organizeDirectives');
+ unawaited(server.options.analytics?.sendEvent(
+ 'edit',
+ 'organizeDirectives',
+ ));
var params = EditOrganizeDirectivesParams.fromRequest(request);
var file = params.file;
diff --git a/pkg/analysis_server/lib/src/handler/legacy/search_find_element_references.dart b/pkg/analysis_server/lib/src/handler/legacy/search_find_element_references.dart
index ccdbbaf..a0bdfbb 100644
--- a/pkg/analysis_server/lib/src/handler/legacy/search_find_element_references.dart
+++ b/pkg/analysis_server/lib/src/handler/legacy/search_find_element_references.dart
@@ -25,8 +25,8 @@
var file = params.file;
// prepare element
var element = await server.getElementAtOffset(file, params.offset);
- if (element is ImportElement) {
- element = element.prefix;
+ if (element is ImportElement2) {
+ element = element.prefix?.element;
}
if (element is FieldFormalParameterElement) {
element = element.field;
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
index 5cd9ec2..5d42f49 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
@@ -223,7 +223,7 @@
Map<String, Set<String>> _buildLookupOfImportedSymbols(
ResolvedUnitResult unit) {
final alreadyImportedSymbols = <String, Set<String>>{};
- final importElementList = unit.libraryElement.imports;
+ final importElementList = unit.libraryElement.imports2;
for (var import in importElementList) {
final importedLibrary = import.importedLibrary;
if (importedLibrary == null) continue;
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_execute_command.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_execute_command.dart
index e75e0f1..2454202 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_execute_command.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_execute_command.dart
@@ -52,11 +52,11 @@
? ProgressReporter.serverCreated(server)
: ProgressReporter.noop;
- // To make parsing arguments easier in commands, instead of a
+ // To make passing arguments easier in commands, instead of a
// `List<Object?>` we now use `Map<String, Object?>`.
//
// However, some handlers still support the list for compatibility so we
- // must allow the to convert a `List` to a `Map`.
+ // must allow them to convert a `List` to a `Map`.
final arguments = params.arguments ?? const [];
Map<String, Object?> commandParams;
if (arguments.length == 1 && arguments[0] is Map<String, Object?>) {
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_references.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_references.dart
index 0f72ab1..07fd4ff 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_references.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_references.dart
@@ -56,8 +56,8 @@
Future<ErrorOr<List<Location>?>> _getReferences(String path, int offset,
ReferenceParams params, ResolvedUnitResult unit) async {
var element = await server.getElementAtOffset(path, offset);
- if (element is ImportElement) {
- element = element.prefix;
+ if (element is ImportElement2) {
+ element = element.prefix?.element;
}
if (element is FieldFormalParameterElement) {
element = element.field;
diff --git a/pkg/analysis_server/lib/src/plugin/plugin_manager.dart b/pkg/analysis_server/lib/src/plugin/plugin_manager.dart
index 9c89392..df45853 100644
--- a/pkg/analysis_server/lib/src/plugin/plugin_manager.dart
+++ b/pkg/analysis_server/lib/src/plugin/plugin_manager.dart
@@ -343,12 +343,12 @@
_pluginMap[path] = plugin;
try {
var session = await plugin.start(byteStorePath, sdkPath);
- session?.onDone.then((_) {
+ unawaited(session?.onDone.then((_) {
if (_pluginMap[path] == plugin) {
_pluginMap.remove(path);
_notifyPluginsChanged();
}
- });
+ }));
} catch (exception, stackTrace) {
// Record the exception (for debugging purposes) and record the fact
// that we should not try to communicate with the plugin.
@@ -534,9 +534,9 @@
//
_pluginMap[path] = plugin;
var session = await plugin.start(byteStorePath, sdkPath);
- session?.onDone.then((_) {
+ unawaited(session?.onDone.then((_) {
_pluginMap.remove(path);
- });
+ }));
//
// Re-initialize the plugin.
//
@@ -993,7 +993,7 @@
name = result.name;
version = result.version;
if (!isCompatible) {
- sendRequest(PluginShutdownParams());
+ unawaited(sendRequest(PluginShutdownParams()));
info.reportException(CaughtException(
PluginException('Plugin is not compatible.'), StackTrace.current));
return false;
diff --git a/pkg/analysis_server/lib/src/server/dev_server.dart b/pkg/analysis_server/lib/src/server/dev_server.dart
index d59e2fa..863667e 100644
--- a/pkg/analysis_server/lib/src/server/dev_server.dart
+++ b/pkg/analysis_server/lib/src/server/dev_server.dart
@@ -129,14 +129,15 @@
}
});
- _channel.sendRequest(Request('${_nextId++}', 'server.setSubscriptions', {
+ await _channel
+ .sendRequest(Request('${_nextId++}', 'server.setSubscriptions', {
'subscriptions': ['STATUS'],
}));
directories =
directories.map((dir) => path.normalize(path.absolute(dir))).toList();
- _channel.sendRequest(Request(
+ await _channel.sendRequest(Request(
'${_nextId++}',
'analysis.setAnalysisRoots',
{'included': directories, 'excluded': []},
diff --git a/pkg/analysis_server/lib/src/server/driver.dart b/pkg/analysis_server/lib/src/server/driver.dart
index 7234c5b..f553451 100644
--- a/pkg/analysis_server/lib/src/server/driver.dart
+++ b/pkg/analysis_server/lib/src/server/driver.dart
@@ -428,7 +428,7 @@
}
await instrumentationService.shutdown();
- socketServer.analysisServer!.shutdown();
+ unawaited(socketServer.analysisServer!.shutdown());
try {
tempDriverDir.deleteSync(recursive: true);
@@ -454,7 +454,7 @@
httpServer.close();
}
await instrumentationService.shutdown();
- socketServer.analysisServer!.shutdown();
+ unawaited(socketServer.analysisServer!.shutdown());
if (sendPort == null) exit(0);
});
},
@@ -502,7 +502,7 @@
// Only shutdown the server and exit if the server is not already
// handling the shutdown.
if (!socketServer.analysisServer!.willExit) {
- socketServer.analysisServer!.shutdown();
+ unawaited(socketServer.analysisServer!.shutdown());
exit(0);
}
});
diff --git a/pkg/analysis_server/lib/src/server/http_server.dart b/pkg/analysis_server/lib/src/server/http_server.dart
index 9826d89..eb8835a 100644
--- a/pkg/analysis_server/lib/src/server/http_server.dart
+++ b/pkg/analysis_server/lib/src/server/http_server.dart
@@ -2,6 +2,7 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
+import 'dart:async';
import 'dart:io';
import 'package:analysis_server/src/socket_server.dart';
@@ -102,7 +103,7 @@
response.headers.contentType = ContentType.text;
response.write(
'WebSocket connections not supported (${request.uri.path}).');
- response.close();
+ unawaited(response.close());
} else {
_returnUnknownRequest(request);
}
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/imported_reference_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/imported_reference_contributor.dart
index 56c5db2..1296ec4 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/imported_reference_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/imported_reference_contributor.dart
@@ -18,14 +18,14 @@
}
// Traverse imports including dart:core
- var imports = request.libraryElement.imports;
+ var imports = request.libraryElement.imports2;
for (var importElement in imports) {
var libraryElement = importElement.importedLibrary;
if (libraryElement != null) {
_buildSuggestions(
libraryElement: libraryElement,
namespace: importElement.namespace,
- prefix: importElement.prefix?.name,
+ prefix: importElement.prefix?.element.name,
);
if (libraryElement.isDartCore &&
request.opType.includeTypeNameSuggestions) {
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/library_member_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/library_member_contributor.dart
index 86e8275..2aced27 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/library_member_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/library_member_contributor.dart
@@ -22,18 +22,18 @@
if (targetId is SimpleIdentifier && !request.target.isCascade) {
var elem = targetId.staticElement;
if (elem is PrefixElement && !elem.isSynthetic) {
- var imports = request.libraryElement.imports;
+ var imports = request.libraryElement.imports2;
_buildSuggestions(elem, imports);
}
}
}
- void _buildSuggestions(PrefixElement elem, List<ImportElement> imports) {
+ void _buildSuggestions(PrefixElement elem, List<ImportElement2> imports) {
var parent = request.target.containingNode.parent;
var typesOnly = parent is NamedType;
var isConstructor = parent?.parent is ConstructorName;
for (var importElem in imports) {
- if (importElem.prefix?.name == elem.name) {
+ if (importElem.prefix?.element.name == elem.name) {
var library = importElem.importedLibrary;
if (library != null) {
builder.libraryUriStr = library.source.uri.toString();
@@ -65,7 +65,7 @@
}
}
// If the import is `deferred` then suggest `loadLibrary`.
- if (!typesOnly && importElem.isDeferred) {
+ if (!typesOnly && importElem.prefix is DeferredImportElementPrefix) {
builder.suggestLoadLibraryFunction(library.loadLibraryFunction);
}
builder.libraryUriStr = null;
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/library_prefix_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/library_prefix_contributor.dart
index 553c1ec..4e025b9 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/library_prefix_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/library_prefix_contributor.dart
@@ -15,9 +15,9 @@
return;
}
- var imports = request.libraryElement.imports;
+ var imports = request.libraryElement.imports2;
for (var element in imports) {
- var prefix = element.prefix?.name;
+ var prefix = element.prefix?.element.name;
if (prefix != null && prefix.isNotEmpty) {
var libraryElement = element.importedLibrary;
if (libraryElement != null) {
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/not_imported_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/not_imported_contributor.dart
index 9949e2b..39c8edd 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/not_imported_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/not_imported_contributor.dart
@@ -46,7 +46,7 @@
}
var importedLibraries = Set<LibraryElement>.identity();
- for (var import in request.libraryElement.imports) {
+ for (var import in request.libraryElement.imports2) {
var importedLibrary = import.importedLibrary;
if (importedLibrary != null) {
if (import.combinators.isEmpty) {
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/change_to.dart b/pkg/analysis_server/lib/src/services/correction/dart/change_to.dart
index 67c2ff4..eb77c8c 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/change_to.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/change_to.dart
@@ -112,8 +112,8 @@
}
}
// Check elements from imports.
- for (var importElement in resolvedResult.libraryElement.imports) {
- if (importElement.prefix?.name == prefixName) {
+ for (var importElement in resolvedResult.libraryElement.imports2) {
+ if (importElement.prefix?.element.name == prefixName) {
var namespace = getImportNamespace(importElement);
finder._updateList(namespace.values);
}
@@ -220,8 +220,8 @@
}
}
// Check unprefixed imports.
- for (var importElement in resolvedResult.libraryElement.imports) {
- if (importElement.prefix?.name == prefixName) {
+ for (var importElement in resolvedResult.libraryElement.imports2) {
+ if (importElement.prefix?.element.name == prefixName) {
var namespace = getImportNamespace(importElement);
finder._updateList(namespace.values);
}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/create_class.dart b/pkg/analysis_server/lib/src/services/correction/dart/create_class.dart
index 00cbb7d..1107023 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/create_class.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/create_class.dart
@@ -72,8 +72,9 @@
filePath = file;
prefix = '$eol$eol';
} else {
- for (var import in libraryElement.imports) {
- if (prefixElement is PrefixElement && import.prefix == prefixElement) {
+ for (var import in libraryElement.imports2) {
+ if (prefixElement is PrefixElement &&
+ import.prefix?.element == prefixElement) {
var library = import.importedLibrary;
if (library != null) {
targetUnit = library.definingCompilationUnit;
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/create_mixin.dart b/pkg/analysis_server/lib/src/services/correction/dart/create_mixin.dart
index 01698d0..d66cd28 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/create_mixin.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/create_mixin.dart
@@ -69,8 +69,9 @@
filePath = file;
prefix = '$eol$eol';
} else {
- for (var import in libraryElement.imports) {
- if (prefixElement is PrefixElement && import.prefix == prefixElement) {
+ for (var import in libraryElement.imports2) {
+ if (prefixElement is PrefixElement &&
+ import.prefix?.element == prefixElement) {
var library = import.importedLibrary;
if (library != null) {
targetUnit = library.definingCompilationUnit;
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/data_driven.dart b/pkg/analysis_server/lib/src/services/correction/dart/data_driven.dart
index ba96a6c..41ed6d7 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/data_driven.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/data_driven.dart
@@ -10,7 +10,8 @@
import 'package:analysis_server/src/services/correction/fix/data_driven/transform.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set_manager.dart';
-import 'package:analyzer/dart/element/element.dart' show LibraryElement;
+import 'package:analyzer/dart/element/element.dart'
+ show DirectiveUriWithRelativeUri, LibraryElement;
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
import 'package:meta/meta.dart';
@@ -24,13 +25,13 @@
Stream<CorrectionProducer> get producers async* {
var importedUris = <Uri>[];
var library = resolvedResult.libraryElement;
- for (var importElement in library.imports) {
+ for (var importElement in library.imports2) {
// TODO(brianwilkerson) Filter based on combinators to help avoid making
// invalid suggestions.
var uri = importElement.uri;
- if (uri != null) {
+ if (uri is DirectiveUriWithRelativeUri) {
// The [uri] is `null` if the literal string is not a valid URI.
- importedUris.add(Uri.parse(uri));
+ importedUris.add(uri.relativeUri);
}
}
var matchers = ElementMatcher.matchersForNode(node);
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/import_add_show.dart b/pkg/analysis_server/lib/src/services/correction/dart/import_add_show.dart
index 20fa0ec..c2d0ee8 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/import_add_show.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/import_add_show.dart
@@ -30,7 +30,7 @@
return;
}
// prepare whole import namespace
- var importElement = importDirective.element;
+ var importElement = importDirective.element2;
if (importElement == null) {
return;
}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/import_library.dart b/pkg/analysis_server/lib/src/services/correction/dart/import_library.dart
index e6b8f80..ab3f3b4 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/import_library.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/import_library.dart
@@ -164,7 +164,7 @@
// is, then we can check the extension elements without needing to perform
// additional analysis.
var foundImport = false;
- for (var imp in libraryElement.imports) {
+ for (var imp in libraryElement.imports2) {
// prepare element
var importedLibrary = imp.importedLibrary;
if (importedLibrary == null || importedLibrary != libraryToImport) {
@@ -244,7 +244,7 @@
// may be there is an existing import,
// but it is with prefix and we don't use this prefix
var alreadyImportedWithPrefix = <LibraryElement>{};
- for (var imp in libraryElement.imports) {
+ for (var imp in libraryElement.imports2) {
// prepare element
var libraryElement = imp.importedLibrary;
if (libraryElement == null) {
@@ -261,7 +261,7 @@
continue;
}
// may be apply prefix
- var prefix = imp.prefix;
+ var prefix = imp.prefix?.element;
if (prefix != null) {
yield _ImportLibraryPrefix(libraryElement, prefix);
continue;
diff --git a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
index b1fe5b7..3549471 100644
--- a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
+++ b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
@@ -1628,6 +1628,8 @@
status: needsEvaluation
LintCode.close_sinks:
status: needsEvaluation
+LintCode.combinators_ordering:
+ status: needsEvaluation
LintCode.comment_references:
status: needsEvaluation
LintCode.conditional_uri_does_not_exist:
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/element_matcher.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/element_matcher.dart
index 0c23da3..f87525e 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/element_matcher.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/element_matcher.dart
@@ -122,7 +122,7 @@
if (library == null) {
return null;
}
- for (var importElement in library.imports) {
+ for (var importElement in library.imports2) {
// TODO(brianwilkerson) Filter based on combinators to help avoid making
// invalid suggestions.
var uri = importElement.importedLibrary?.source.uri;
diff --git a/pkg/analysis_server/lib/src/services/correction/namespace.dart b/pkg/analysis_server/lib/src/services/correction/namespace.dart
index affc53d..c97452d 100644
--- a/pkg/analysis_server/lib/src/services/correction/namespace.dart
+++ b/pkg/analysis_server/lib/src/services/correction/namespace.dart
@@ -14,13 +14,13 @@
return _getExportNamespaceForLibrary(library)[name];
}
-/// Return the [ImportElement] that is referenced by [prefixNode], or `null` if
+/// Return the [ImportElement2] that is referenced by [prefixNode], or `null` if
/// the node does not reference a prefix or if we cannot determine which import
/// is being referenced.
-ImportElement? getImportElement(SimpleIdentifier prefixNode) {
+ImportElement2? getImportElement(SimpleIdentifier prefixNode) {
var parent = prefixNode.parent;
if (parent is ImportDirective) {
- return parent.element;
+ return parent.element2;
}
return _getImportElementInfo(prefixNode);
}
@@ -31,27 +31,27 @@
return namespace.definedNames;
}
-/// Return the [ImportElement] that declared [prefix] and imports [element].
+/// Return the [ImportElement2] that declared [prefix] and imports [element].
///
/// [libraryElement] - the [LibraryElement] where reference is.
/// [prefix] - the import prefix, maybe `null`.
/// [element] - the referenced element.
-/// [importElementsMap] - the cache of [Element]s imported by [ImportElement]s.
-ImportElement? _getImportElement(LibraryElement libraryElement, String prefix,
- Element element, Map<ImportElement, Set<Element>> importElementsMap) {
+/// [importElementsMap] - the cache of [Element]s imported by [ImportElement2]s.
+ImportElement2? _getImportElement(LibraryElement libraryElement, String prefix,
+ Element element, Map<ImportElement2, Set<Element>> importElementsMap) {
if (element.enclosingElement is! CompilationUnitElement) {
return null;
}
var usedLibrary = element.library;
// find ImportElement that imports used library with used prefix
- List<ImportElement>? candidates;
- for (var importElement in libraryElement.imports) {
+ List<ImportElement2>? candidates;
+ for (var importElement in libraryElement.imports2) {
// required library
if (importElement.importedLibrary != usedLibrary) {
continue;
}
// required prefix
- var prefixElement = importElement.prefix;
+ var prefixElement = importElement.prefix?.element;
if (prefixElement == null) {
continue;
}
@@ -95,9 +95,9 @@
return null;
}
-/// Returns the [ImportElement] that is referenced by [prefixNode] with a
+/// Returns the [ImportElement2] that is referenced by [prefixNode] with a
/// [PrefixElement], maybe `null`.
-ImportElement? _getImportElementInfo(SimpleIdentifier prefixNode) {
+ImportElement2? _getImportElementInfo(SimpleIdentifier prefixNode) {
// prepare environment
var parent = prefixNode.parent;
var unit = prefixNode.thisOrAncestorOfType<CompilationUnit>();
@@ -124,7 +124,7 @@
}
// find ImportElement
var prefix = prefixNode.name;
- var importElementsMap = <ImportElement, Set<Element>>{};
+ var importElementsMap = <ImportElement2, Set<Element>>{};
return _getImportElement(
libraryElement, prefix, usedElement, importElementsMap);
}
diff --git a/pkg/analysis_server/lib/src/services/correction/util.dart b/pkg/analysis_server/lib/src/services/correction/util.dart
index 3c1c745..603fb4e 100644
--- a/pkg/analysis_server/lib/src/services/correction/util.dart
+++ b/pkg/analysis_server/lib/src/services/correction/util.dart
@@ -326,8 +326,8 @@
return Precedence.none;
}
-/// Returns the namespace of the given [ImportElement].
-Map<String, Element> getImportNamespace(ImportElement imp) {
+/// Returns the namespace of the given [ImportElement2].
+Map<String, Element> getImportNamespace(ImportElement2 imp) {
return imp.namespace.definedNames;
}
@@ -1215,8 +1215,8 @@
/// Return the import element used to import given [element] into the library.
/// May be `null` if was not imported, i.e. declared in the same library.
- ImportElement? _getImportElement(Element element) {
- for (var imp in _library.imports) {
+ ImportElement2? _getImportElement(Element element) {
+ for (var imp in _library.imports2) {
var definedNames = getImportNamespace(imp);
if (definedNames.containsValue(element)) {
return imp;
@@ -1270,7 +1270,7 @@
// ensure import
var importElement = _getImportElement(element);
if (importElement != null) {
- var prefix = importElement.prefix;
+ var prefix = importElement.prefix?.element;
if (prefix != null) {
sb.write(prefix.displayName);
sb.write('.');
diff --git a/pkg/analysis_server/lib/src/services/refactoring/refactoring.dart b/pkg/analysis_server/lib/src/services/refactoring/refactoring.dart
index 7d76a4a..76dd47b 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/refactoring.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/refactoring.dart
@@ -413,7 +413,7 @@
if (element is ConstructorElement) {
return RenameConstructorRefactoringImpl(workspace, session, element);
}
- if (element is ImportElement) {
+ if (element is ImportElement2) {
return RenameImportRefactoringImpl(workspace, session, element);
}
if (element is LabelElement) {
@@ -452,7 +452,7 @@
}
// Use the prefix offset/length when renaming an import directive.
- if (node is ImportDirective && element is ImportElement) {
+ if (node is ImportDirective && element is ImportElement2) {
var prefix = node.prefix;
if (prefix != null) {
offset = prefix.offset;
diff --git a/pkg/analysis_server/lib/src/services/refactoring/rename.dart b/pkg/analysis_server/lib/src/services/refactoring/rename.dart
index 7f93bf1..66c74f0 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/rename.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/rename.dart
@@ -114,8 +114,8 @@
static String _getOldName(Element element) {
if (element is ConstructorElement) {
return element.name;
- } else if (element is ImportElement) {
- var prefix = element.prefix;
+ } else if (element is ImportElement2) {
+ var prefix = element.prefix?.element;
if (prefix != null) {
return prefix.displayName;
}
diff --git a/pkg/analysis_server/lib/src/services/refactoring/rename_import.dart b/pkg/analysis_server/lib/src/services/refactoring/rename_import.dart
index 2a0bf1f..94f6500 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/rename_import.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/rename_import.dart
@@ -16,16 +16,16 @@
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer_plugin/utilities/range_factory.dart';
-/// A [Refactoring] for renaming [ImportElement]s.
+/// A [Refactoring] for renaming [ImportElement2]s.
class RenameImportRefactoringImpl extends RenameRefactoringImpl {
final AnalysisSession session;
RenameImportRefactoringImpl(
- RefactoringWorkspace workspace, this.session, ImportElement element)
+ RefactoringWorkspace workspace, this.session, ImportElement2 element)
: super(workspace, element);
@override
- ImportElement get element => super.element as ImportElement;
+ ImportElement2 get element => super.element as ImportElement2;
@override
String get refactoringName {
@@ -49,32 +49,28 @@
Future<void> fillChange() async {
// update declaration
{
- var prefix = element.prefix;
+ final node = await _findNode();
+ if (node == null) {
+ return;
+ }
+
+ final prefixNode = node.prefix;
SourceEdit? edit;
if (newName.isEmpty) {
// We should not get `prefix == null` because we check in
// `checkNewName` that the new name is different.
- if (prefix == null) {
- return;
- }
- var node = await _findNode();
- if (node != null) {
+ if (prefixNode != null) {
var uriEnd = node.uri.end;
- var prefixEnd = prefix.nameOffset + prefix.nameLength;
+ var prefixEnd = prefixNode.end;
edit = newSourceEdit_range(
range.startOffsetEndOffset(uriEnd, prefixEnd), '');
}
} else {
- if (prefix == null) {
- var node = await _findNode();
- if (node != null) {
- var uriEnd = node.uri.end;
- edit = newSourceEdit_range(SourceRange(uriEnd, 0), ' as $newName');
- }
+ if (prefixNode == null) {
+ var uriEnd = node.uri.end;
+ edit = newSourceEdit_range(SourceRange(uriEnd, 0), ' as $newName');
} else {
- var offset = prefix.nameOffset;
- var length = prefix.nameLength;
- edit = newSourceEdit_range(SourceRange(offset, length), newName);
+ edit = newSourceEdit_range(range.node(prefixNode), newName);
}
}
if (edit != null) {
@@ -111,7 +107,7 @@
return null;
}
var unit = unitResult.unit;
- var index = library.imports.indexOf(element);
+ var index = library.imports2.indexOf(element);
return unit.directives.whereType<ImportDirective>().elementAt(index);
}
diff --git a/pkg/analysis_server/lib/src/services/refactoring/rename_unit_member.dart b/pkg/analysis_server/lib/src/services/refactoring/rename_unit_member.dart
index 0479329..4f25f31 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/rename_unit_member.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/rename_unit_member.dart
@@ -183,7 +183,7 @@
return true;
}
// check imports
- for (var importElement in atLibrary.imports) {
+ for (var importElement in atLibrary.imports2) {
// ignore if imported with prefix
if (importElement.prefix != null) {
continue;
diff --git a/pkg/analysis_server/lib/src/services/search/search_engine.dart b/pkg/analysis_server/lib/src/services/search/search_engine.dart
index 09df9a4..0b88104 100644
--- a/pkg/analysis_server/lib/src/services/search/search_engine.dart
+++ b/pkg/analysis_server/lib/src/services/search/search_engine.dart
@@ -87,9 +87,7 @@
/// Instances of the class [SearchMatch] represent a match found by
/// [SearchEngine].
abstract class SearchMatch {
- /// Return the [Element] containing the match. Can return `null` if the unit
- /// does not exist, or its element was invalidated, or the element cannot be
- /// found, etc.
+ /// Return the [Element] containing the match.
Element get element;
/// The absolute path of the file containing the match.
diff --git a/pkg/analysis_server/lib/src/status/ast_writer.dart b/pkg/analysis_server/lib/src/status/ast_writer.dart
index b9f610a..b98c3d6 100644
--- a/pkg/analysis_server/lib/src/status/ast_writer.dart
+++ b/pkg/analysis_server/lib/src/status/ast_writer.dart
@@ -87,7 +87,7 @@
} else if (node is GenericFunctionType) {
properties['type'] = node.type;
} else if (node is ImportDirective) {
- properties['element'] = node.element;
+ properties['element'] = node.element2;
properties['selectedSource'] = node.selectedSource;
properties['uriSource'] = node.uriSource;
} else if (node is IndexExpression) {
diff --git a/pkg/analysis_server/lib/src/status/element_writer.dart b/pkg/analysis_server/lib/src/status/element_writer.dart
index bc09cd5..560fc23 100644
--- a/pkg/analysis_server/lib/src/status/element_writer.dart
+++ b/pkg/analysis_server/lib/src/status/element_writer.dart
@@ -99,9 +99,9 @@
properties['returnType'] = element.returnType;
properties['type'] = element.type;
}
- if (element is ImportElement) {
+ if (element is ImportElement2) {
properties['combinators'] = element.combinators;
- properties['isDeferred'] = element.isDeferred;
+ properties['isDeferred'] = element.prefix is DeferredImportElementPrefix;
properties['library'] = element.library;
}
if (element is LibraryElement) {
diff --git a/pkg/analysis_server/lib/src/status/pages.dart b/pkg/analysis_server/lib/src/status/pages.dart
index 66538d6..130a190 100644
--- a/pkg/analysis_server/lib/src/status/pages.dart
+++ b/pkg/analysis_server/lib/src/status/pages.dart
@@ -2,6 +2,7 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
+import 'dart:async';
import 'dart:convert';
import 'dart:io';
@@ -192,7 +193,7 @@
var path = request.uri.path;
if (path == '/') {
- respondRedirect(request, pages.first.path);
+ unawaited(respondRedirect(request, pages.first.path));
return;
}
@@ -201,7 +202,7 @@
var response = request.response;
response.headers.contentType = ContentType.html;
response.write(await page.generate(request.uri.queryParameters));
- response.close();
+ unawaited(response.close());
return;
}
}
@@ -216,7 +217,7 @@
response.statusCode = HttpStatus.internalServerError;
response.headers.contentType = ContentType.text;
response.write('$e\n\n$st');
- response.close();
+ unawaited(response.close());
}
}
}
diff --git a/pkg/analysis_server/test/analysis/notification_errors_test.dart b/pkg/analysis_server/test/analysis/notification_errors_test.dart
index 4c93ced..fcec6cf 100644
--- a/pkg/analysis_server/test/analysis/notification_errors_test.dart
+++ b/pkg/analysis_server/test/analysis/notification_errors_test.dart
@@ -339,7 +339,6 @@
expect(error.location.file, testFile.path);
expect(error.severity, AnalysisErrorSeverity.INFO);
expect(error.type, AnalysisErrorType.LINT);
- expect(error.message, lint.description);
}
Future<void> test_notInAnalysisRoot() async {
diff --git a/pkg/analysis_server/test/completion_test_support.dart b/pkg/analysis_server/test/completion_test_support.dart
index cdf6b9b..a0ebd67 100644
--- a/pkg/analysis_server/test/completion_test_support.dart
+++ b/pkg/analysis_server/test/completion_test_support.dart
@@ -79,7 +79,8 @@
.toList();
}
- Future runTest(LocationSpec spec, [Map<String, String>? extraFiles]) async {
+ Future<void> runTest(LocationSpec spec,
+ [Map<String, String>? extraFiles]) async {
await super.setUp();
try {
@@ -102,7 +103,7 @@
assertHasNoCompletion(result);
}
} finally {
- super.tearDown();
+ await super.tearDown();
}
}
}
diff --git a/pkg/analysis_server/test/domain_analysis_test.dart b/pkg/analysis_server/test/domain_analysis_test.dart
index b3f9c9b..1c94ed1 100644
--- a/pkg/analysis_server/test/domain_analysis_test.dart
+++ b/pkg/analysis_server/test/domain_analysis_test.dart
@@ -1458,7 +1458,7 @@
/// Repeat a few times, eventually there will be no work to do.
Future<void> _waitAnalysisComplete() async {
for (var i = 0; i < 128; i++) {
- pumpEventQueue();
+ await pumpEventQueue();
await server.onAnalysisComplete;
}
}
diff --git a/pkg/analysis_server/test/domain_execution_test.dart b/pkg/analysis_server/test/domain_execution_test.dart
index a9f288c..8532002 100644
--- a/pkg/analysis_server/test/domain_execution_test.dart
+++ b/pkg/analysis_server/test/domain_execution_test.dart
@@ -130,7 +130,7 @@
@override
Future<void> tearDown() async {
await _disposeExecutionContext();
- super.tearDown();
+ await super.tearDown();
}
Future<void> test_createAndDeleteMultipleContexts() async {
diff --git a/pkg/analysis_server/test/edit/refactoring_test.dart b/pkg/analysis_server/test/edit/refactoring_test.dart
index f90d1e6..e397c48 100644
--- a/pkg/analysis_server/test/edit/refactoring_test.dart
+++ b/pkg/analysis_server/test/edit/refactoring_test.dart
@@ -2,6 +2,8 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
+import 'dart:async';
+
import 'package:analysis_server/protocol/protocol.dart';
import 'package:analysis_server/protocol/protocol_generated.dart';
import 'package:analysis_server/src/services/refactoring/refactoring_manager.dart';
@@ -877,8 +879,7 @@
var request =
EditGetAvailableRefactoringsParams(testFile.path, offset, length)
.toRequest('0');
- serverChannel.sendRequest(request);
- var response = await serverChannel.waitForResponse(request);
+ var response = await serverChannel.sendRequest(request);
var result = EditGetAvailableRefactoringsResult.fromResponse(response);
kinds = result.kinds;
}
@@ -2250,7 +2251,8 @@
print(otherName);
}
''');
- server.getAnalysisDriver(testFile.path)!.getResult(testFile.path);
+ unawaited(
+ server.getAnalysisDriver(testFile.path)!.getResult(testFile.path));
// send the second request, with the same kind, file and offset
await waitForTasksFinished();
result = await getRefactoringResult(() {
diff --git a/pkg/analysis_server/test/integration/analysis/update_content_list_test.dart b/pkg/analysis_server/test/integration/analysis/update_content_list_test.dart
index b41a1df..1b7f70e 100644
--- a/pkg/analysis_server/test/integration/analysis/update_content_list_test.dart
+++ b/pkg/analysis_server/test/integration/analysis/update_content_list_test.dart
@@ -27,6 +27,7 @@
// Create a dummy file
writeFile(pathname, '// dummy text');
await standardAnalysisSetup();
+ await analysisFinished;
// Override file contents with badText.
await sendAnalysisUpdateContent({pathname: AddContentOverlay(badText)});
await analysisFinished;
diff --git a/pkg/analysis_server/test/lsp/definition_test.dart b/pkg/analysis_server/test/lsp/definition_test.dart
index 1577e53..8c6c351 100644
--- a/pkg/analysis_server/test/lsp/definition_test.dart
+++ b/pkg/analysis_server/test/lsp/definition_test.dart
@@ -173,6 +173,17 @@
await testContents(contents);
}
+ Future<void> test_fieldFormalParam() async {
+ final contents = '''
+class A {
+ final String [[a]];
+ A(this.^a});
+}
+''';
+
+ await testContents(contents);
+ }
+
Future<void> test_fromPlugins() async {
final pluginAnalyzedFilePath = join(projectFolderPath, 'lib', 'foo.foo');
final pluginAnalyzedFileUri = Uri.file(pluginAnalyzedFilePath);
@@ -402,6 +413,19 @@
await testContents(contents);
}
+ Future<void> test_superFormalParam() async {
+ final contents = '''
+class A {
+ A({required int [[a]]});
+}
+class B extends A {
+ B({required super.^a}) : assert(a > 0);
+}
+''';
+
+ await testContents(contents);
+ }
+
Future<void> test_type() async {
final contents = '''
f() {
diff --git a/pkg/analysis_server/test/services/completion/dart/completion_manager_test.dart b/pkg/analysis_server/test/services/completion/dart/completion_manager_test.dart
index fb80d3e..62bd4a5 100644
--- a/pkg/analysis_server/test/services/completion/dart/completion_manager_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/completion_manager_test.dart
@@ -59,10 +59,10 @@
var directives = resolvedUnit.unit.directives;
- var imports = request.libraryElement.imports;
+ var imports = request.libraryElement.imports2;
expect(imports, hasLength(directives.length + 1));
- ImportElement importNamed(String expectedUri) {
+ ImportElement2 importNamed(String expectedUri) {
var uriList = <String>[];
for (var importElement in imports) {
var uri = importElement.importedLibrary!.source.uri.toString();
diff --git a/pkg/analysis_server/test/services/completion/dart/location/field_formal_parameter_test.dart b/pkg/analysis_server/test/services/completion/dart/location/field_formal_parameter_test.dart
index 68b4c68..556b765 100644
--- a/pkg/analysis_server/test/services/completion/dart/location/field_formal_parameter_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/location/field_formal_parameter_test.dart
@@ -31,7 +31,7 @@
mixin SuperFormalParameterTestCases on AbstractCompletionDriverTest {
Future<void> test_class_replacement_left() async {
- _checkContainers(
+ await _checkContainers(
declarations: 'var field = 0;',
constructorParameters: 'this.f^',
validator: (response) {
@@ -48,7 +48,7 @@
}
Future<void> test_class_replacement_right() async {
- _checkContainers(
+ await _checkContainers(
declarations: 'var field = 0;',
constructorParameters: 'this.^f',
validator: (response) {
@@ -105,7 +105,7 @@
}
Future<void> test_class_suggestions_onlyNotSpecified_optionalNamed() async {
- _checkContainers(
+ await _checkContainers(
declarations: 'final int x; final int y;',
constructorParameters: '{this.x, this.^}',
validator: (response) {
@@ -123,7 +123,7 @@
Future<void>
test_class_suggestions_onlyNotSpecified_requiredPositional() async {
- _checkContainers(
+ await _checkContainers(
declarations: 'final int x; final int y;',
constructorParameters: 'this.x, this.^',
validator: (response) {
diff --git a/pkg/analysis_server/test/services/refactoring/rename_import_test.dart b/pkg/analysis_server/test/services/refactoring/rename_import_test.dart
index 8ad640e..d8a7f3c 100644
--- a/pkg/analysis_server/test/services/refactoring/rename_import_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/rename_import_test.dart
@@ -236,6 +236,6 @@
void _createRefactoring(String search) {
var directive = findNode.import(search);
- createRenameRefactoringForElement(directive.element);
+ createRenameRefactoringForElement(directive.element2);
}
}
diff --git a/pkg/analysis_server/test/src/computer/call_hierarchy_computer_test.dart b/pkg/analysis_server/test/src/computer/call_hierarchy_computer_test.dart
new file mode 100644
index 0000000..6bc39cc
--- /dev/null
+++ b/pkg/analysis_server/test/src/computer/call_hierarchy_computer_test.dart
@@ -0,0 +1,1312 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/computer/computer_call_hierarchy.dart';
+import 'package:analysis_server/src/services/search/search_engine.dart';
+import 'package:analysis_server/src/services/search/search_engine_internal.dart';
+import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/source/source_range.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../../abstract_single_unit.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(CallHierarchyComputerFindTargetTest);
+ defineReflectiveTests(CallHierarchyComputerIncomingCallsTest);
+ defineReflectiveTests(CallHierarchyComputerOutgoingCallsTest);
+ });
+}
+
+/// Matches a [CallHierarchyItem] with the given name/kind/file.
+Matcher _isItem(CallHierarchyKind kind, String displayName, String file,
+ SourceRange range) =>
+ TypeMatcher<CallHierarchyItem>()
+ .having((e) => e.kind, 'kind', kind)
+ .having((e) => e.displayName, 'displayName', displayName)
+ .having((e) => e.file, 'file', file)
+ .having((e) => e.range, 'range', range);
+
+/// Matches a [CallHierarchyCalls] result with the given element/ranges.
+Matcher _isResult(
+ CallHierarchyKind kind,
+ String displayName,
+ String file,
+ SourceRange range, {
+ List<SourceRange>? ranges,
+}) {
+ var matcher = TypeMatcher<CallHierarchyCalls>()
+ .having((c) => c.item, 'item', _isItem(kind, displayName, file, range));
+
+ if (ranges != null) {
+ matcher = matcher.having((c) => c.ranges, 'ranges', ranges);
+ }
+
+ return matcher;
+}
+
+abstract class AbstractCallHierarchyTest extends AbstractSingleUnitTest {
+ final startOfFile = SourceRange(0, 0);
+
+ // Gets the expected range that follows the string [prefix] in [code] with a
+ // length of [match.length].
+ SourceRange rangeAfterPrefix(String prefix, String code, String match) =>
+ SourceRange(
+ withoutMarkers(code).indexOf(prefix) + prefix.length, match.length);
+
+ // Gets the expected range that starts at [search] in [code] with a
+ // length of [match.length].
+ SourceRange rangeAtSearch(String search, String code, [String? match]) {
+ final index = withoutMarkers(code).indexOf(search);
+ expect(index, greaterThanOrEqualTo(0));
+ return SourceRange(index, (match ?? search).length);
+ }
+
+ String withoutMarkers(String code) => code.replaceAll('^', '');
+}
+
+@reflectiveTest
+class CallHierarchyComputerFindTargetTest extends AbstractCallHierarchyTest {
+ late String otherFile;
+
+ Future<void> expectNoTarget(String content) async {
+ await expectTarget(content, isNull);
+ }
+
+ Future<void> expectTarget(String content, Matcher matcher) async {
+ final target = await findTarget(content);
+ expect(target, matcher);
+ }
+
+ Future<CallHierarchyItem?> findTarget(String code) async {
+ final marker = code.indexOf('^');
+ expect(marker, greaterThanOrEqualTo(0));
+ addTestSource(withoutMarkers(code));
+ final result =
+ await (await session).getResolvedUnit(testFile) as ResolvedUnitResult;
+
+ return DartCallHierarchyComputer(result).findTarget(marker);
+ }
+
+ @override
+ void setUp() {
+ super.setUp();
+ otherFile = convertPath('$testPackageLibPath/other.dart');
+ }
+
+ Future<void> test_args() async {
+ await expectNoTarget('main(int ^a) {}');
+ }
+
+ Future<void> test_block() async {
+ await expectNoTarget('f() {^}');
+ }
+
+ Future<void> test_comment() async {
+ await expectNoTarget('f() {} // this is a ^comment');
+ }
+
+ Future<void> test_constructor() async {
+ final contents = '''
+class Foo {
+ Fo^o(String a) {}
+}
+ ''';
+
+ final target = await findTarget(contents);
+ expect(
+ target,
+ _isItem(
+ CallHierarchyKind.constructor,
+ 'Foo',
+ testFile,
+ rangeAtSearch('Foo(', contents, 'Foo'),
+ ),
+ );
+ }
+
+ Future<void> test_constructorCall() async {
+ final contents = '''
+import 'other.dart';
+
+f() {
+ final foo = Fo^o();
+}
+ ''';
+
+ final otherContents = '''
+class Foo {
+ Foo();
+}
+ ''';
+
+ addSource(otherFile, otherContents);
+ await expectTarget(
+ contents,
+ _isItem(
+ CallHierarchyKind.constructor,
+ 'Foo',
+ otherFile,
+ rangeAtSearch('Foo(', otherContents, 'Foo'),
+ ));
+ }
+
+ Future<void> test_extension_method() async {
+ final contents = '''
+extension StringExtension on String {
+ void myMet^hod() {}
+}
+ ''';
+
+ await expectTarget(
+ contents,
+ _isItem(
+ CallHierarchyKind.method,
+ 'myMethod',
+ testFile,
+ rangeAtSearch('myMethod', contents),
+ ));
+ }
+
+ Future<void> test_extension_methodCall() async {
+ final contents = '''
+import 'other.dart';
+
+f() {
+ ''.myMet^hod();
+}
+ ''';
+
+ final otherContents = '''
+extension StringExtension on String {
+ void myMethod() {}
+}
+ ''';
+
+ addSource(otherFile, otherContents);
+ await expectTarget(
+ contents,
+ _isItem(
+ CallHierarchyKind.method,
+ 'myMethod',
+ otherFile,
+ rangeAtSearch('myMethod', otherContents),
+ ));
+ }
+
+ Future<void> test_function() async {
+ final contents = '''
+void myFun^ction() {}
+ ''';
+
+ await expectTarget(
+ contents,
+ _isItem(
+ CallHierarchyKind.function,
+ 'myFunction',
+ testFile,
+ rangeAtSearch('myFunction', contents),
+ ));
+ }
+
+ Future<void> test_functionCall() async {
+ final contents = '''
+import 'other.dart' as f;
+
+f() {
+ f.myFun^ction();
+}
+ ''';
+
+ final otherContents = '''
+void myFunction() {}
+ ''';
+
+ addSource(otherFile, otherContents);
+ await expectTarget(
+ contents,
+ _isItem(
+ CallHierarchyKind.function,
+ 'myFunction',
+ otherFile,
+ rangeAtSearch('myFunction', otherContents),
+ ));
+ }
+
+ Future<void> test_getter() async {
+ final contents = '''
+class Foo {
+ String get fo^o => '';
+}
+ ''';
+
+ await expectTarget(
+ contents,
+ _isItem(
+ CallHierarchyKind.property,
+ 'get foo',
+ testFile,
+ rangeAtSearch('foo', contents),
+ ));
+ }
+
+ Future<void> test_getterCall() async {
+ final contents = '''
+import 'other.dart';
+
+f() {
+ final foo = ba^r;
+}
+ ''';
+
+ final otherContents = '''
+String get bar => '';
+ ''';
+
+ addSource(otherFile, otherContents);
+ await expectTarget(
+ contents,
+ _isItem(
+ CallHierarchyKind.property,
+ 'get bar',
+ otherFile,
+ rangeAtSearch('bar', otherContents),
+ ));
+ }
+
+ Future<void> test_implicitConstructorCall() async {
+ // Even if a constructor is implicit, we might want to be able to get the
+ // incoming calls, so we should return the class location as a stand-in
+ // (although with the Kind still set to constructor).
+ final contents = '''
+import 'other.dart';
+
+f() {
+ final foo = Fo^o();
+}
+ ''';
+
+ final otherContents = '''
+class Foo {}
+ ''';
+
+ addSource(otherFile, otherContents);
+ await expectTarget(
+ contents,
+ _isItem(
+ CallHierarchyKind.constructor,
+ 'Foo',
+ otherFile,
+ rangeAtSearch('Foo {', otherContents, 'Foo'),
+ ));
+ }
+
+ Future<void> test_method() async {
+ final contents = '''
+class Foo {
+ void myMet^hod() {}
+}
+ ''';
+
+ await expectTarget(
+ contents,
+ _isItem(
+ CallHierarchyKind.method,
+ 'myMethod',
+ testFile,
+ rangeAtSearch('myMethod', contents),
+ ));
+ }
+
+ Future<void> test_methodCall() async {
+ final contents = '''
+import 'other.dart';
+
+f() {
+ Foo().myMet^hod();
+}
+ ''';
+
+ final otherContents = '''
+class Foo {
+ void myMethod() {}
+}
+ ''';
+
+ addSource(otherFile, otherContents);
+ await expectTarget(
+ contents,
+ _isItem(
+ CallHierarchyKind.method,
+ 'myMethod',
+ otherFile,
+ rangeAtSearch('myMethod', otherContents),
+ ));
+ }
+
+ Future<void> test_mixin_method() async {
+ final contents = '''
+mixin Bar {
+ void myMet^hod() {}
+}
+ ''';
+
+ await expectTarget(
+ contents,
+ _isItem(
+ CallHierarchyKind.method,
+ 'myMethod',
+ testFile,
+ rangeAtSearch('myMethod', contents),
+ ));
+ }
+
+ Future<void> test_mixin_methodCall() async {
+ final contents = '''
+import 'other.dart';
+
+f() {
+ Foo().myMet^hod();
+}
+ ''';
+
+ final otherContents = '''
+class Bar {
+ void myMethod() {}
+}
+
+class Foo with Bar {}
+ ''';
+
+ addSource(otherFile, otherContents);
+ await expectTarget(
+ contents,
+ _isItem(
+ CallHierarchyKind.method,
+ 'myMethod',
+ otherFile,
+ rangeAtSearch('myMethod', otherContents),
+ ));
+ }
+
+ Future<void> test_namedConstructor() async {
+ final contents = '''
+class Foo {
+ Foo.Ba^r(String a) {}
+}
+ ''';
+
+ await expectTarget(
+ contents,
+ _isItem(
+ CallHierarchyKind.constructor,
+ 'Foo.Bar',
+ testFile,
+ rangeAtSearch('Bar', contents),
+ ));
+ }
+
+ Future<void> test_namedConstructor_typeName() async {
+ final contents = '''
+class Foo {
+ Fo^o.Bar(String a) {}
+}
+ ''';
+
+ await expectNoTarget(contents);
+ }
+
+ Future<void> test_namedConstructorCall() async {
+ final contents = '''
+import 'other.dart';
+
+f() {
+ final foo = Foo.Ba^r();
+}
+ ''';
+
+ final otherContents = '''
+class Foo {
+ Foo.Bar();
+}
+ ''';
+
+ addSource(otherFile, otherContents);
+ await expectTarget(
+ contents,
+ _isItem(
+ CallHierarchyKind.constructor,
+ 'Foo.Bar',
+ otherFile,
+ rangeAtSearch('Bar', otherContents),
+ ));
+ }
+
+ Future<void> test_namedConstructorCall_typeName() async {
+ final contents = '''
+import 'other.dart';
+
+f() {
+ final foo = Fo^o.Bar();
+}
+ ''';
+
+ final otherContents = '''
+class Foo {
+ Foo.Bar();
+}
+ ''';
+
+ addSource(otherFile, otherContents);
+ await expectNoTarget(contents);
+ }
+
+ Future<void> test_setter() async {
+ final contents = '''
+class Foo {
+ set fo^o(Strin value) {};
+}
+ ''';
+
+ await expectTarget(
+ contents,
+ _isItem(
+ CallHierarchyKind.property,
+ 'set foo',
+ testFile,
+ rangeAtSearch('foo', contents),
+ ));
+ }
+
+ Future<void> test_setterCall() async {
+ final contents = '''
+import 'other.dart';
+
+f() {
+ ba^r = '';
+}
+ ''';
+
+ final otherContents = '''
+set bar(String value) {}
+ ''';
+
+ addSource(otherFile, otherContents);
+ await expectTarget(
+ contents,
+ _isItem(
+ CallHierarchyKind.property,
+ 'set bar',
+ otherFile,
+ rangeAtSearch('bar', otherContents),
+ ));
+ }
+
+ Future<void> test_whitespace() async {
+ await expectNoTarget(' ^ f() {}');
+ }
+}
+
+@reflectiveTest
+class CallHierarchyComputerIncomingCallsTest extends AbstractCallHierarchyTest {
+ late String otherFile;
+ late SearchEngine searchEngine;
+
+ Future<List<CallHierarchyCalls>> findIncomingCalls(String code) async {
+ final marker = code.indexOf('^');
+ expect(marker, greaterThanOrEqualTo(0));
+ addTestSource(withoutMarkers(code));
+ final session_ = await session;
+
+ var result = await session_.getResolvedUnit(testFile) as ResolvedUnitResult;
+ expect(result.errors, isEmpty);
+ var computer = DartCallHierarchyComputer(result);
+ // It's possible that the target is in a different file (because we were
+ // invoked on a call and not a declaration), so fetch the resolved unit
+ // for the target.
+ final target = computer.findTarget(marker)!;
+ if (target.file != testFile) {
+ result =
+ await session_.getResolvedUnit(target.file) as ResolvedUnitResult;
+ expect(result.errors, isEmpty);
+ computer = DartCallHierarchyComputer(result);
+ }
+ return computer.findIncomingCalls(target, searchEngine);
+ }
+
+ @override
+ void setUp() {
+ super.setUp();
+ otherFile = convertPath('$testPackageLibPath/other.dart');
+ searchEngine = SearchEngineImpl([
+ driverFor(testPackageRootPath),
+ ]);
+ }
+
+ Future<void> test_constructor() async {
+ final contents = '''
+class Foo {
+ Fo^o();
+}
+ ''';
+
+ final otherContents = '''
+import 'test.dart';
+
+final foo1 = Foo();
+class Bar {
+ final foo2 = Foo();
+ Foo get foo3 => Foo();
+ Bar() {
+ final foo4 = Foo();
+ }
+ void bar() {
+ final foo5 = Foo();
+ final foo6 = Foo();
+ }
+}
+ ''';
+
+ // Gets the expected range that follows the string [prefix].
+ SourceRange rangeAfter(String prefix) =>
+ rangeAfterPrefix(prefix, otherContents, 'Foo');
+
+ addSource(otherFile, otherContents);
+ final calls = await findIncomingCalls(contents);
+ expect(
+ calls,
+ unorderedEquals([
+ _isResult(CallHierarchyKind.file, 'other.dart', otherFile, startOfFile,
+ ranges: [
+ rangeAfter('foo1 = '),
+ ]),
+ _isResult(CallHierarchyKind.class_, 'Bar', otherFile,
+ rangeAtSearch('Bar {', otherContents, 'Bar'),
+ ranges: [
+ rangeAfter('foo2 = '),
+ ]),
+ _isResult(CallHierarchyKind.property, 'foo3', otherFile,
+ rangeAtSearch('foo3', otherContents),
+ ranges: [
+ rangeAfter('foo3 => '),
+ ]),
+ _isResult(CallHierarchyKind.constructor, 'Bar', otherFile,
+ rangeAtSearch('Bar() {', otherContents, 'Bar'),
+ ranges: [
+ rangeAfter('foo4 = '),
+ ]),
+ _isResult(CallHierarchyKind.method, 'bar', otherFile,
+ rangeAtSearch('bar() {', otherContents, 'bar'),
+ ranges: [
+ rangeAfter('foo5 = '),
+ rangeAfter('foo6 = '),
+ ]),
+ ]),
+ );
+ }
+
+ Future<void> test_extension_method() async {
+ final contents = '''
+extension StringExtension on String {
+ void myMet^hod() {}
+}
+ ''';
+
+ final otherContents = '''
+import 'test.dart';
+
+void f() {
+ ''.myMethod();
+}
+ ''';
+
+ // Gets the expected range that follows the string [prefix].
+ SourceRange rangeAfter(String prefix) =>
+ rangeAfterPrefix(prefix, otherContents, 'myMethod');
+
+ addSource(otherFile, otherContents);
+ final calls = await findIncomingCalls(contents);
+ expect(
+ calls,
+ unorderedEquals([
+ _isResult(CallHierarchyKind.function, 'f', otherFile,
+ rangeAtSearch('f() {', otherContents, 'f'),
+ ranges: [
+ rangeAfter("''."),
+ ]),
+ ]),
+ );
+ }
+
+ Future<void> test_function() async {
+ final contents = '''
+String myFun^ction() => '';
+ ''';
+
+ final otherContents = '''
+import 'test.dart';
+
+final foo1 = myFunction();
+
+class Bar {
+ final foo2 = myFunction();
+ String get foo3 => myFunction();
+ Bar() {
+ final foo4 = myFunction();
+ }
+ void bar() {
+ final foo5 = myFunction();
+ final foo6 = myFunction();
+ }
+}
+ ''';
+
+ // Gets the expected range that follows the string [prefix].
+ SourceRange rangeAfter(String prefix) =>
+ rangeAfterPrefix(prefix, otherContents, 'myFunction');
+
+ addSource(otherFile, otherContents);
+ final calls = await findIncomingCalls(contents);
+ expect(
+ calls,
+ unorderedEquals([
+ _isResult(CallHierarchyKind.file, 'other.dart', otherFile, startOfFile,
+ ranges: [
+ rangeAfter('foo1 = '),
+ ]),
+ _isResult(CallHierarchyKind.class_, 'Bar', otherFile,
+ rangeAtSearch('Bar {', otherContents, 'Bar'),
+ ranges: [
+ rangeAfter('foo2 = '),
+ ]),
+ _isResult(CallHierarchyKind.property, 'foo3', otherFile,
+ rangeAtSearch('foo3', otherContents),
+ ranges: [
+ rangeAfter('foo3 => '),
+ ]),
+ _isResult(CallHierarchyKind.constructor, 'Bar', otherFile,
+ rangeAtSearch('Bar() {', otherContents, 'Bar'),
+ ranges: [
+ rangeAfter('foo4 = '),
+ ]),
+ _isResult(CallHierarchyKind.method, 'bar', otherFile,
+ rangeAtSearch('bar() {', otherContents, 'bar'),
+ ranges: [
+ rangeAfter('foo5 = '),
+ rangeAfter('foo6 = '),
+ ]),
+ ]),
+ );
+ }
+
+ Future<void> test_getter() async {
+ final contents = '''
+String get f^oo => '';
+ ''';
+
+ final otherContents = '''
+import 'test.dart';
+
+final foo1 = foo;
+class Bar {
+ final foo2 = foo;
+ Foo get foo3 => foo;
+ Bar() {
+ final foo4 = foo;
+ }
+ void bar() {
+ final foo5 = foo;
+ final foo6 = foo;
+ }
+}
+ ''';
+
+ // Gets the expected range that follows the string [prefix].
+ SourceRange rangeAfter(String prefix) =>
+ rangeAfterPrefix(prefix, otherContents, 'foo');
+
+ addSource(otherFile, otherContents);
+ final calls = await findIncomingCalls(contents);
+ expect(
+ calls,
+ unorderedEquals([
+ _isResult(CallHierarchyKind.file, 'other.dart', otherFile, startOfFile,
+ ranges: [
+ rangeAfter('foo1 = '),
+ ]),
+ _isResult(CallHierarchyKind.class_, 'Bar', otherFile,
+ rangeAtSearch('Bar {', otherContents, 'Bar'),
+ ranges: [
+ rangeAfter('foo2 = '),
+ ]),
+ _isResult(CallHierarchyKind.property, 'foo3', otherFile,
+ rangeAtSearch('foo3', otherContents),
+ ranges: [
+ rangeAfter('foo3 => '),
+ ]),
+ _isResult(CallHierarchyKind.constructor, 'Bar', otherFile,
+ rangeAtSearch('Bar() {', otherContents, 'Bar'),
+ ranges: [
+ rangeAfter('foo4 = '),
+ ]),
+ _isResult(CallHierarchyKind.method, 'bar', otherFile,
+ rangeAtSearch('bar() {', otherContents, 'bar'),
+ ranges: [
+ rangeAfter('foo5 = '),
+ rangeAfter('foo6 = '),
+ ]),
+ ]),
+ );
+ }
+
+ Future<void> test_implicitConstructor() async {
+ // We still expect to be able to navigate with implicit constructors. This
+ // is done by the target being the class, but with a kind of Constructor.
+ final contents = '''
+// ignore_for_file: unused_local_variable
+import 'other.dart';
+
+f() {
+ final foo1 = Fo^o();
+}
+ ''';
+
+ final otherContents = '''
+class Foo {}
+
+final foo2 = Foo();
+ ''';
+
+ // Gets the expected range that follows the string [prefix].
+ SourceRange rangeAfter(String prefix, String code) =>
+ rangeAfterPrefix(prefix, code, 'Foo');
+
+ addSource(otherFile, otherContents);
+ final calls = await findIncomingCalls(contents);
+ expect(
+ calls,
+ unorderedEquals([
+ _isResult(CallHierarchyKind.function, 'f', testFile,
+ rangeAtSearch('f() {', contents, 'f'),
+ ranges: [
+ rangeAfter('foo1 = ', contents),
+ ]),
+ _isResult(CallHierarchyKind.file, 'other.dart', otherFile, startOfFile,
+ ranges: [
+ rangeAfter('foo2 = ', otherContents),
+ ]),
+ ]),
+ );
+ }
+
+ Future<void> test_method() async {
+ final contents = '''
+class Foo {
+ void myMet^hod() {}
+}
+ ''';
+
+ final otherContents = '''
+import 'test.dart';
+
+void f() {
+ Foo().myMethod();
+ final tearoff = Foo().myMethod;
+}
+ ''';
+
+ // Gets the expected range that follows the string [prefix].
+ SourceRange rangeAfter(String prefix) =>
+ rangeAfterPrefix(prefix, otherContents, 'myMethod');
+
+ addSource(otherFile, otherContents);
+ final calls = await findIncomingCalls(contents);
+ expect(
+ calls,
+ unorderedEquals([
+ _isResult(CallHierarchyKind.function, 'f', otherFile,
+ rangeAtSearch('f() {', otherContents, 'f'),
+ ranges: [
+ rangeAfter('Foo().'),
+ rangeAfter('tearoff = Foo().'),
+ ]),
+ ]),
+ );
+ }
+
+ Future<void> test_mixin_method() async {
+ final contents = '''
+mixin Bar {
+ void myMet^hod() {}
+}
+
+class Foo with Bar {}
+ ''';
+
+ final otherContents = '''
+import 'test.dart';
+
+void f() {
+ Foo().myMethod();
+}
+ ''';
+
+ // Gets the expected range that follows the string [prefix].
+ SourceRange rangeAfter(String prefix) =>
+ rangeAfterPrefix(prefix, otherContents, 'myMethod');
+
+ addSource(otherFile, otherContents);
+ final calls = await findIncomingCalls(contents);
+ expect(
+ calls,
+ unorderedEquals([
+ _isResult(CallHierarchyKind.function, 'f', otherFile,
+ rangeAtSearch('f() {', otherContents, 'f'),
+ ranges: [
+ rangeAfter('Foo().'),
+ ]),
+ ]),
+ );
+ }
+
+ Future<void> test_namedConstructor() async {
+ final contents = '''
+class Foo {
+ Foo.B^ar();
+}
+ ''';
+
+ final otherContents = '''
+import 'test.dart';
+
+f() {
+ final foo = Foo.Bar();
+}
+ ''';
+
+ // Gets the expected range that follows the string [prefix].
+ SourceRange rangeAfter(String prefix) =>
+ rangeAfterPrefix(prefix, otherContents, 'Bar');
+
+ addSource(otherFile, otherContents);
+ final calls = await findIncomingCalls(contents);
+ expect(
+ calls,
+ unorderedEquals([
+ _isResult(CallHierarchyKind.function, 'f', otherFile,
+ rangeAtSearch('f() {', otherContents, 'f'),
+ ranges: [
+ rangeAfter('foo = Foo.'),
+ ]),
+ ]),
+ );
+ }
+
+ Future<void> test_setter() async {
+ final contents = '''
+set fo^o(String value) {}
+ ''';
+
+ final otherContents = '''
+import 'test.dart';
+
+class Bar {
+ Bar() {
+ /*1*/foo = '';
+ }
+ void bar() {
+ /*2*/foo = '';
+ /*3*/foo = '';
+ }
+}
+ ''';
+
+ // Gets the expected range that follows the string [prefix].
+ SourceRange rangeAfter(String prefix) =>
+ rangeAfterPrefix(prefix, otherContents, 'foo');
+
+ addSource(otherFile, otherContents);
+ final calls = await findIncomingCalls(contents);
+ expect(
+ calls,
+ unorderedEquals([
+ _isResult(CallHierarchyKind.constructor, 'Bar', otherFile,
+ rangeAtSearch('Bar() {', otherContents, 'Bar'),
+ ranges: [
+ rangeAfter('/*1*/'),
+ ]),
+ _isResult(CallHierarchyKind.method, 'bar', otherFile,
+ rangeAtSearch('bar() {', otherContents, 'bar'),
+ ranges: [
+ rangeAfter('/*2*/'),
+ rangeAfter('/*3*/'),
+ ]),
+ ]),
+ );
+ }
+}
+
+@reflectiveTest
+class CallHierarchyComputerOutgoingCallsTest extends AbstractCallHierarchyTest {
+ late String otherFile;
+
+ Future<List<CallHierarchyCalls>> findOutgoingCalls(String code) async {
+ final marker = code.indexOf('^');
+ expect(marker, greaterThanOrEqualTo(0));
+ addTestSource(withoutMarkers(code));
+ final session_ = await session;
+
+ var result = await session_.getResolvedUnit(testFile) as ResolvedUnitResult;
+ expect(result.errors, isEmpty);
+ var computer = DartCallHierarchyComputer(result);
+ // It's possible that the target is in a different file (because we were
+ // invoked on a call and not a declaration), so fetch the resolved unit
+ // for the target.
+ final target = computer.findTarget(marker)!;
+ if (target.file != testFile) {
+ result =
+ await session_.getResolvedUnit(target.file) as ResolvedUnitResult;
+ expect(result.errors, isEmpty);
+ computer = DartCallHierarchyComputer(result);
+ }
+ return computer.findOutgoingCalls(target);
+ }
+
+ @override
+ void setUp() {
+ super.setUp();
+ otherFile = convertPath('$testPackageLibPath/other.dart');
+ }
+
+ Future<void> test_constructor() async {
+ final contents = '''
+// ignore_for_file: unused_local_variable
+import 'other.dart';
+
+class Foo {
+ Fo^o() {
+ final a = A();
+ final constructorTearoffA = A.new;
+ final b = B();
+ final constructorTearoffB = B.new;
+ }
+}
+ ''';
+
+ final otherContents = '''
+class A {
+ A();
+}
+
+class B {
+}
+ ''';
+
+ addSource(otherFile, otherContents);
+ final calls = await findOutgoingCalls(contents);
+ expect(
+ calls,
+ unorderedEquals([
+ _isResult(CallHierarchyKind.constructor, 'A', otherFile,
+ rangeAtSearch('A();', otherContents, 'A'),
+ ranges: [
+ rangeAtSearch('A()', contents, 'A'),
+ rangeAfterPrefix('constructorTearoffA = A.', contents, 'new'),
+ ]),
+ _isResult(CallHierarchyKind.constructor, 'B', otherFile,
+ rangeAtSearch('B {', otherContents, 'B'),
+ ranges: [
+ rangeAtSearch('B()', contents, 'B'),
+ rangeAfterPrefix('constructorTearoffB = B.', contents, 'new'),
+ ]),
+ ]),
+ );
+ }
+
+ Future<void> test_extension_method() async {
+ final contents = '''
+// ignore_for_file: unused_local_variable
+import 'other.dart';
+
+extension StringExtension on String {
+ void fo^o() {
+ ''.bar();
+ final tearoff = ''.bar;
+ }
+}
+ ''';
+
+ final otherContents = '''
+extension StringExtension on String {
+ bar() {}
+}
+ ''';
+
+ addSource(otherFile, otherContents);
+ final calls = await findOutgoingCalls(contents);
+ expect(
+ calls,
+ unorderedEquals([
+ _isResult(CallHierarchyKind.method, 'bar', otherFile,
+ rangeAtSearch('bar() {', otherContents, 'bar'),
+ ranges: [
+ rangeAtSearch('bar();', contents, 'bar'),
+ rangeAtSearch('bar;', contents, 'bar'),
+ ]),
+ ]),
+ );
+ }
+
+ Future<void> test_function() async {
+ final contents = '''
+// ignore_for_file: unused_local_variable
+import 'other.dart';
+
+void fo^o() {
+ void nested() {
+ f(); // not a call of 'foo'
+ }
+ f(); // 1
+ final tearoff = f;
+ nested();
+ final nestedTearoff = nested;
+}
+ ''';
+
+ final otherContents = '''
+void f() {}
+ ''';
+
+ addSource(otherFile, otherContents);
+ final calls = await findOutgoingCalls(contents);
+ expect(
+ calls,
+ unorderedEquals([
+ _isResult(CallHierarchyKind.function, 'f', otherFile,
+ rangeAtSearch('f() {', otherContents, 'f'),
+ ranges: [
+ rangeAtSearch('f(); // 1', contents, 'f'),
+ rangeAfterPrefix('tearoff = ', contents, 'f'),
+ ]),
+ _isResult(CallHierarchyKind.function, 'nested', testFile,
+ rangeAtSearch('nested() {', contents, 'nested'),
+ ranges: [
+ rangeAtSearch('nested();', contents, 'nested'),
+ rangeAfterPrefix('nestedTearoff = ', contents, 'nested'),
+ ]),
+ ]),
+ );
+ }
+
+ Future<void> test_getter() async {
+ final contents = '''
+// ignore_for_file: unused_local_variable
+import 'other.dart';
+
+String get fo^o {
+ final a = A();
+ final b = a.b;
+ final c = A().b;
+ return '';
+}
+ ''';
+
+ final otherContents = '''
+class A {
+ String get b => '';
+}
+ ''';
+
+ addSource(otherFile, otherContents);
+ final calls = await findOutgoingCalls(contents);
+ expect(
+ calls,
+ unorderedEquals([
+ _isResult(CallHierarchyKind.constructor, 'A', otherFile,
+ rangeAtSearch('A {', otherContents, 'A')),
+ _isResult(CallHierarchyKind.property, 'get b', otherFile,
+ rangeAtSearch('b => ', otherContents, 'b'),
+ ranges: [
+ rangeAfterPrefix('a.', contents, 'b'),
+ rangeAfterPrefix('A().', contents, 'b'),
+ ]),
+ ]),
+ );
+ }
+
+ Future<void> test_implicitConstructor() async {
+ // We can still begin navigating from an implicit constructor (so we can
+ // search for inbound calls), so we should ensure that trying to fetch
+ // outbound calls returns empty (and doesn't fail).
+ final contents = '''
+// ignore_for_file: unused_local_variable
+import 'other.dart';
+
+f() {
+ final foo1 = Fo^o();
+}
+ ''';
+
+ final otherContents = '''
+class Foo {}
+ ''';
+
+ addSource(otherFile, otherContents);
+ final calls = await findOutgoingCalls(contents);
+ expect(calls, isEmpty);
+ }
+
+ Future<void> test_method() async {
+ final contents = '''
+// ignore_for_file: unused_local_variable
+import 'other.dart';
+
+class Foo {
+ void fo^o() {
+ final a = A();
+ a.bar();
+ final tearoff = a.bar;
+ // non-calls
+ var x = 1;
+ var y = x;
+ a.field;
+ }
+}
+ ''';
+
+ final otherContents = '''
+class A {
+ String field;
+ void bar() {}
+}
+ ''';
+
+ addSource(otherFile, otherContents);
+ final calls = await findOutgoingCalls(contents);
+ expect(
+ calls,
+ unorderedEquals([
+ _isResult(CallHierarchyKind.constructor, 'A', otherFile,
+ rangeAtSearch('A {', otherContents, 'A')),
+ _isResult(CallHierarchyKind.method, 'bar', otherFile,
+ rangeAtSearch('bar() {', otherContents, 'bar'),
+ ranges: [
+ rangeAfterPrefix('a.', contents, 'bar'),
+ rangeAfterPrefix('tearoff = a.', contents, 'bar'),
+ ]),
+ ]),
+ );
+ }
+
+ Future<void> test_mixin_method() async {
+ final contents = '''
+// ignore_for_file: unused_local_variable
+import 'other.dart';
+
+mixin MyMixin {
+ void f^() {
+ final a = A();
+ a.foo();
+ A().foo();
+ final tearoff = a.foo;
+ }
+}
+ ''';
+
+ final otherContents = '''
+mixin OtherMixin {
+ void foo() {}
+}
+
+class A with OtherMixin {}
+ ''';
+
+ addSource(otherFile, otherContents);
+ final calls = await findOutgoingCalls(contents);
+ expect(
+ calls,
+ unorderedEquals([
+ _isResult(CallHierarchyKind.constructor, 'A', otherFile,
+ rangeAtSearch('A with', otherContents, 'A')),
+ _isResult(CallHierarchyKind.method, 'foo', otherFile,
+ rangeAtSearch('foo() {', otherContents, 'foo'),
+ ranges: [
+ rangeAfterPrefix('a.', contents, 'foo'),
+ rangeAfterPrefix('A().', contents, 'foo'),
+ rangeAfterPrefix('tearoff = a.', contents, 'foo'),
+ ]),
+ ]),
+ );
+ }
+
+ Future<void> test_namedConstructor() async {
+ final contents = '''
+// ignore_for_file: unused_local_variable
+import 'other.dart';
+
+class Foo {
+ Foo.B^ar() {
+ final a = A.named();
+ final constructorTearoff = A.named;
+ }
+}
+ ''';
+
+ final otherContents = '''
+void f() {}
+class A {
+ A.named();
+}
+ ''';
+
+ addSource(otherFile, otherContents);
+ final calls = await findOutgoingCalls(contents);
+ expect(
+ calls,
+ unorderedEquals([
+ _isResult(CallHierarchyKind.constructor, 'A.named', otherFile,
+ rangeAtSearch('named', otherContents),
+ ranges: [
+ rangeAfterPrefix('a = A.', contents, 'named'),
+ rangeAfterPrefix('constructorTearoff = A.', contents, 'named'),
+ ]),
+ ]),
+ );
+ }
+
+ Future<void> test_setter() async {
+ final contents = '''
+import 'other.dart';
+
+set fo^o(String value) {
+ final a = A();
+ a.b = '';
+ A().b = '';
+}
+ ''';
+
+ final otherContents = '''
+class A {
+ set b(String value) {}
+}
+ ''';
+
+ addSource(otherFile, otherContents);
+ final calls = await findOutgoingCalls(contents);
+ expect(
+ calls,
+ unorderedEquals([
+ _isResult(CallHierarchyKind.constructor, 'A', otherFile,
+ rangeAtSearch('A {', otherContents, 'A')),
+ _isResult(CallHierarchyKind.property, 'set b', otherFile,
+ rangeAtSearch('b(String ', otherContents, 'b'),
+ ranges: [
+ rangeAfterPrefix('a.', contents, 'b'),
+ rangeAfterPrefix('A().', contents, 'b'),
+ ]),
+ ]),
+ );
+ }
+}
diff --git a/pkg/analysis_server/test/src/computer/test_all.dart b/pkg/analysis_server/test/src/computer/test_all.dart
index af15c51..65087cc 100644
--- a/pkg/analysis_server/test/src/computer/test_all.dart
+++ b/pkg/analysis_server/test/src/computer/test_all.dart
@@ -4,6 +4,7 @@
import 'package:test_reflective_loader/test_reflective_loader.dart';
+import 'call_hierarchy_computer_test.dart' as call_hierarchy_computer;
import 'closing_labels_computer_test.dart' as closing_labels_computer;
import 'color_computer_test.dart' as color_computer;
import 'folding_computer_test.dart' as folding_computer;
@@ -15,6 +16,7 @@
void main() {
defineReflectiveSuite(() {
+ call_hierarchy_computer.main();
closing_labels_computer.main();
color_computer.main();
folding_computer.main();
diff --git a/pkg/analysis_server/test/src/domains/completion/available_suggestion_sets_test.dart b/pkg/analysis_server/test/src/domains/completion/available_suggestion_sets_test.dart
index 15124db..3ab015e 100644
--- a/pkg/analysis_server/test/src/domains/completion/available_suggestion_sets_test.dart
+++ b/pkg/analysis_server/test/src/domains/completion/available_suggestion_sets_test.dart
@@ -45,7 +45,7 @@
// Delete the file, the set should be removed.
deleteFile(path);
- waitForSetWithUriRemoved(uriStr);
+ await waitForSetWithUriRemoved(uriStr);
}
Future<void> test_suggestion_class() async {
diff --git a/pkg/analysis_server/test/src/services/correction/assist/assign_to_local_variable_test.dart b/pkg/analysis_server/test/src/services/correction/assist/assign_to_local_variable_test.dart
index 3751d7f..c544b19 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/assign_to_local_variable_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/assign_to_local_variable_test.dart
@@ -102,7 +102,7 @@
final data['table'][] //marker
}
''');
- assertNoAssistAt('] //marker');
+ await assertNoAssistAt('] //marker');
}
Future<void> test_throw() async {
diff --git a/pkg/analysis_server/test/src/services/correction/fix/create_field_test.dart b/pkg/analysis_server/test/src/services/correction/fix/create_field_test.dart
index af26bc4..6f64621 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/create_field_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/create_field_test.dart
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:analysis_server/src/services/correction/fix.dart';
-import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -364,25 +363,6 @@
await assertNoFix();
}
- Future<void> test_inPart_imported() async {
- addSource('$testPackageLibPath/a.dart', '''
-part of lib;
-class A {}
-''');
-
- await resolveTestCode('''
-import 'package:test/a.dart';
-
-void f(A a) {
- int v = a.test;
- print(v);
-}
-''');
- await assertNoFix(errorFilter: (e) {
- return e.errorCode == CompileTimeErrorCode.UNDEFINED_GETTER;
- });
- }
-
Future<void> test_inPart_self() async {
await resolveTestCode('''
part of lib;
diff --git a/pkg/analysis_server/test/src/services/correction/fix/create_getter_test.dart b/pkg/analysis_server/test/src/services/correction/fix/create_getter_test.dart
index 7566168..ed0884c 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/create_getter_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/create_getter_test.dart
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:analysis_server/src/services/correction/fix.dart';
-import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -306,26 +305,6 @@
''');
}
- Future<void> test_qualified_instance_inPart_imported() async {
- addSource('$testPackageLibPath/a.dart', '''
-part of lib;
-
-class A {}
-''');
-
- await resolveTestCode('''
-import 'package:test/a.dart';
-
-void f(A a) {
- int v = a.test;
- print(v);
-}
-''');
- await assertNoFix(errorFilter: (e) {
- return e.errorCode == CompileTimeErrorCode.UNDEFINED_GETTER;
- });
- }
-
Future<void> test_qualified_instance_inPart_self() async {
await resolveTestCode('''
part of lib;
diff --git a/pkg/analysis_server/test/src/services/correction/fix/create_setter_test.dart b/pkg/analysis_server/test/src/services/correction/fix/create_setter_test.dart
index fea7674..930c68e 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/create_setter_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/create_setter_test.dart
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:analysis_server/src/services/correction/fix.dart';
-import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -301,25 +300,6 @@
''');
}
- Future<void> test_qualified_instance_inPart_imported() async {
- addSource('$testPackageLibPath/a.dart', '''
-part of lib;
-
-class A {}
-''');
-
- await resolveTestCode('''
-import 'package:test/a.dart';
-
-void f(A a) {
- a.test = 0;
-}
-''');
- await assertNoFix(errorFilter: (e) {
- return e.errorCode == CompileTimeErrorCode.UNDEFINED_SETTER;
- });
- }
-
Future<void> test_qualified_instance_inPart_self() async {
await resolveTestCode('''
part of lib;
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/sdk_fix_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/sdk_fix_test.dart
index 181cf6d..fb75558 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/sdk_fix_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/sdk_fix_test.dart
@@ -87,6 +87,6 @@
await resolveTestCode('''
var x = '';
''');
- assertNoExceptions();
+ await assertNoExceptions();
}
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_empty_catch_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_empty_catch_test.dart
index 1e5bf88..a7b3898 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/remove_empty_catch_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_empty_catch_test.dart
@@ -67,7 +67,7 @@
} catch 2;
}
''');
- assertNoExceptions();
+ await assertNoExceptions();
}
Future<void> test_singleCatch_finally_newLine() async {
diff --git a/pkg/analysis_server/test/stress/utilities/server.dart b/pkg/analysis_server/test/stress/utilities/server.dart
index 93daac0..1e0a635 100644
--- a/pkg/analysis_server/test/stress/utilities/server.dart
+++ b/pkg/analysis_server/test/stress/utilities/server.dart
@@ -572,11 +572,11 @@
// stdout.writeln('Launching $serverPath');
// stdout.writeln('$dartBinary ${arguments.join(' ')}');
_process = await Process.start(dartBinary, arguments);
- _process!.exitCode.then((int code) {
+ unawaited(_process!.exitCode.then((int code) {
if (code != 0) {
throw StateError('Server terminated with exit code $code');
}
- });
+ }));
_listenToOutput();
var completer = Completer<void>();
_serverConnectedCompleter = completer;
diff --git a/pkg/analysis_server/tool/code_completion/completion_metrics_client.dart b/pkg/analysis_server/tool/code_completion/completion_metrics_client.dart
index 0fa0e39..dc94890 100644
--- a/pkg/analysis_server/tool/code_completion/completion_metrics_client.dart
+++ b/pkg/analysis_server/tool/code_completion/completion_metrics_client.dart
@@ -38,7 +38,8 @@
var options = CompletionMetricsOptions(result);
var stopwatch = Stopwatch()..start();
var client = _AnalysisServerClient(Directory(_sdk.sdkPath), targets);
- _CompletionClientMetricsComputer(rootPath, options, client).computeMetrics();
+ await _CompletionClientMetricsComputer(rootPath, options, client)
+ .computeMetrics();
stopwatch.stop();
var duration = Duration(milliseconds: stopwatch.elapsedMilliseconds);
@@ -289,7 +290,7 @@
_process = process;
_shutdownResponseReceived = false;
// This callback hookup can't throw.
- process.exitCode.whenComplete(() {
+ unawaited(process.exitCode.whenComplete(() {
_process = null;
if (!_shutdownResponseReceived) {
@@ -312,7 +313,7 @@
_onCrash.complete();
}
- });
+ }));
final errorStream = process.stderr
.transform<String>(utf8.decoder)
@@ -326,7 +327,7 @@
_streamController('server.error').stream.listen(_handleServerError);
- _sendCommand('server.setSubscriptions', params: <String, dynamic>{
+ await _sendCommand('server.setSubscriptions', params: <String, dynamic>{
'subscriptions': <String>['STATUS'],
});
diff --git a/pkg/analyzer/lib/dart/ast/ast.dart b/pkg/analyzer/lib/dart/ast/ast.dart
index 070e789..5b3cd79 100644
--- a/pkg/analyzer/lib/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/dart/ast/ast.dart
@@ -2877,9 +2877,14 @@
/// imported URI is not deferred.
Token? get deferredKeyword;
+ @Deprecated('Use element2 instead')
@override
ImportElement? get element;
+ /// Return the element associated with this directive, or `null` if the AST
+ /// structure has not been resolved.
+ ImportElement2? get element2;
+
/// The token representing the 'import' keyword.
Token get importKeyword;
diff --git a/pkg/analyzer/lib/dart/element/element.dart b/pkg/analyzer/lib/dart/element/element.dart
index 22fae35..8d6f524 100644
--- a/pkg/analyzer/lib/dart/element/element.dart
+++ b/pkg/analyzer/lib/dart/element/element.dart
@@ -504,6 +504,11 @@
InterfaceType get returnType;
}
+/// [ImportElementPrefix] that is used together with `deferred`.
+///
+/// Clients may not extend, implement or mix-in this class.
+abstract class DeferredImportElementPrefix implements ImportElementPrefix {}
+
/// Meaning of a URI referenced in a directive.
///
/// Clients may not extend, implement or mix-in this class.
@@ -1099,8 +1104,11 @@
R? visitGenericFunctionTypeElement(GenericFunctionTypeElement element);
+ @Deprecated('Override visitImportElement2() instead')
R? visitImportElement(ImportElement element);
+ R? visitImportElement2(ImportElement2 element);
+
R? visitLabelElement(LabelElement element);
R? visitLibraryAugmentationElement(LibraryAugmentationElement element);
@@ -1342,6 +1350,7 @@
/// A single import directive within a library.
///
/// Clients may not extend, implement or mix-in this class.
+@Deprecated('Use ImportElement2 instead')
abstract class ImportElement implements UriReferencedElement {
/// Return a list containing the combinators that were specified as part of
/// the import directive in the order in which they were specified.
@@ -1362,6 +1371,40 @@
PrefixElement? get prefix;
}
+/// A single import directive within a library.
+///
+/// Clients may not extend, implement or mix-in this class.
+abstract class ImportElement2 implements _ExistingElement {
+ /// Return a list containing the combinators that were specified as part of
+ /// the import directive in the order in which they were specified.
+ List<NamespaceCombinator> get combinators;
+
+ /// Returns the [LibraryElement], if [uri] is a [DirectiveUriWithLibrary].
+ LibraryElement? get importedLibrary;
+
+ /// The offset of the `import` keyword.
+ int get importKeywordOffset;
+
+ /// The [Namespace] that this directive contributes to the containing library.
+ Namespace get namespace;
+
+ /// Return the prefix that was specified as part of the import directive, or
+ /// `null` if there was no prefix specified.
+ ImportElementPrefix? get prefix;
+
+ /// The interpretation of the URI specified in the directive.
+ DirectiveUri get uri;
+}
+
+/// Usage of a [PrefixElement] in an `import` directive.
+///
+/// Clients may not extend, implement or mix-in this class.
+abstract class ImportElementPrefix {
+ /// Return the prefix that was specified as part of the import directive, or
+ /// `null` if there was no prefix specified.
+ PrefixElement get element;
+}
+
/// A label associated with a statement.
///
/// Clients may not extend, implement or mix-in this class.
@@ -1516,8 +1559,12 @@
FeatureSet get featureSet;
/// Return a list containing all of the imports defined in this library.
+ @Deprecated('Use imports2 instead')
List<ImportElement> get imports;
+ /// Return a list containing all of the imports defined in this library.
+ List<ImportElement2> get imports2;
+
bool get isNonNullableByDefault;
/// The language version for this library.
@@ -1726,8 +1773,12 @@
LibraryOrAugmentationElement get enclosingElement2;
/// Return the imports that share this prefix.
+ @Deprecated('Use imports2 instead')
List<ImportElement> get imports;
+ /// Return the imports that share this prefix.
+ List<ImportElement2> get imports2;
+
@override
String get name;
diff --git a/pkg/analyzer/lib/dart/element/visitor.dart b/pkg/analyzer/lib/dart/element/visitor.dart
index 86e4839..4735d6f 100644
--- a/pkg/analyzer/lib/dart/element/visitor.dart
+++ b/pkg/analyzer/lib/dart/element/visitor.dart
@@ -126,10 +126,14 @@
R? visitGenericFunctionTypeElement(GenericFunctionTypeElement element) =>
visitElement(element);
+ @Deprecated('Override visitImportElement2() instead')
@override
R? visitImportElement(ImportElement element) => visitElement(element);
@override
+ R? visitImportElement2(ImportElement2 element) => visitElement(element);
+
+ @override
R? visitLabelElement(LabelElement element) => visitElement(element);
@override
@@ -272,6 +276,7 @@
return null;
}
+ @Deprecated('Override visitImportElement2() instead')
@override
R? visitImportElement(ImportElement element) {
element.visitChildren(this);
@@ -279,6 +284,12 @@
}
@override
+ R? visitImportElement2(ImportElement2 element) {
+ element.visitChildren(this);
+ return null;
+ }
+
+ @override
R? visitLabelElement(LabelElement element) {
element.visitChildren(this);
return null;
@@ -405,10 +416,14 @@
R? visitGenericFunctionTypeElement(GenericFunctionTypeElement element) =>
null;
+ @Deprecated('Override visitImportElement2() instead')
@override
R? visitImportElement(ImportElement element) => null;
@override
+ R? visitImportElement2(ImportElement2 element) => null;
+
+ @override
R? visitLabelElement(LabelElement element) => null;
@override
@@ -498,10 +513,14 @@
R? visitGenericFunctionTypeElement(GenericFunctionTypeElement element) =>
_throw(element);
+ @Deprecated('Override visitImportElement2() instead')
@override
R? visitImportElement(ImportElement element) => _throw(element);
@override
+ R? visitImportElement2(ImportElement2 element) => _throw(element);
+
+ @override
R? visitLabelElement(LabelElement element) => _throw(element);
@override
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index a55028c..3058d67 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -85,7 +85,7 @@
/// TODO(scheglov) Clean up the list of implicitly analyzed files.
class AnalysisDriver implements AnalysisDriverGeneric {
/// The version of data format, should be incremented on every format change.
- static const int DATA_VERSION = 229;
+ static const int DATA_VERSION = 230;
/// The number of exception contexts allowed to write. Once this field is
/// zero, we stop writing any new exception contexts in this process.
diff --git a/pkg/analyzer/lib/src/dart/analysis/file_state.dart b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
index 4675824..8ec05f7 100644
--- a/pkg/analyzer/lib/src/dart/analysis/file_state.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
@@ -212,7 +212,7 @@
/// Information about a single `export` directive.
class ExportDirectiveState<U extends DirectiveUri> extends DirectiveState {
- final UnlinkedNamespaceDirective directive;
+ final UnlinkedExportDirective directive;
final U selectedUri;
final NamespaceDirectiveUris uris;
@@ -927,14 +927,14 @@
UnlinkedPartOfNameDirective? partOfNameDirective;
UnlinkedPartOfUriDirective? partOfUriDirective;
var augmentations = <UnlinkedImportAugmentationDirective>[];
- var exports = <UnlinkedNamespaceDirective>[];
- var imports = <UnlinkedNamespaceDirective>[];
+ var exports = <UnlinkedExportDirective>[];
+ var imports = <UnlinkedImportDirective>[];
var parts = <UnlinkedPartDirective>[];
var macroClasses = <MacroClass>[];
var hasDartCoreImport = false;
for (var directive in unit.directives) {
if (directive is ExportDirective) {
- var builder = _serializeNamespaceDirective(directive);
+ var builder = _serializeExport(directive);
exports.add(builder);
} else if (directive is AugmentationImportDirectiveImpl) {
augmentations.add(
@@ -943,7 +943,7 @@
),
);
} else if (directive is ImportDirectiveImpl) {
- var builder = _serializeNamespaceDirective(directive);
+ var builder = _serializeImport(directive);
imports.add(builder);
if (builder.uri == 'dart:core') {
hasDartCoreImport = true;
@@ -1016,9 +1016,12 @@
}
if (!isDartCore && !hasDartCoreImport) {
imports.add(
- UnlinkedNamespaceDirective(
+ UnlinkedImportDirective(
+ combinators: [],
configurations: [],
- isSyntheticDartCoreImport: true,
+ importKeywordOffset: -1,
+ isSyntheticDartCore: true,
+ prefix: null,
uri: 'dart:core',
),
);
@@ -1081,19 +1084,69 @@
return true;
}
- static UnlinkedNamespaceDirective _serializeNamespaceDirective(
- NamespaceDirective directive) {
- return UnlinkedNamespaceDirective(
- configurations: directive.configurations.map((configuration) {
- var name = configuration.name.components.join('.');
- var value = configuration.value?.stringValue ?? '';
- return UnlinkedNamespaceDirectiveConfiguration(
- name: name,
- value: value,
- uri: configuration.uri.stringValue,
+ static List<UnlinkedCombinator> _serializeCombinators(
+ List<Combinator> combinators,
+ ) {
+ return combinators.map((combinator) {
+ if (combinator is ShowCombinator) {
+ return UnlinkedCombinator(
+ keywordOffset: combinator.keyword.offset,
+ endOffset: combinator.end,
+ isShow: true,
+ names: combinator.shownNames.map((e) => e.name).toList(),
);
- }).toList(),
- uri: directive.uri.stringValue,
+ } else {
+ combinator as HideCombinator;
+ return UnlinkedCombinator(
+ keywordOffset: combinator.keyword.offset,
+ endOffset: combinator.end,
+ isShow: false,
+ names: combinator.hiddenNames.map((e) => e.name).toList(),
+ );
+ }
+ }).toList();
+ }
+
+ static List<UnlinkedNamespaceDirectiveConfiguration> _serializeConfigurations(
+ List<Configuration> configurations,
+ ) {
+ return configurations.map((configuration) {
+ var name = configuration.name.components.join('.');
+ var value = configuration.value?.stringValue ?? '';
+ return UnlinkedNamespaceDirectiveConfiguration(
+ name: name,
+ value: value,
+ uri: configuration.uri.stringValue,
+ );
+ }).toList();
+ }
+
+ static UnlinkedExportDirective _serializeExport(ExportDirective node) {
+ return UnlinkedExportDirective(
+ combinators: _serializeCombinators(node.combinators),
+ configurations: _serializeConfigurations(node.configurations),
+ uri: node.uri.stringValue,
+ );
+ }
+
+ static UnlinkedImportDirective _serializeImport(ImportDirective node) {
+ UnlinkedImportDirectivePrefix? unlinkedPrefix;
+ final prefix = node.prefix;
+ if (prefix != null) {
+ unlinkedPrefix = UnlinkedImportDirectivePrefix(
+ deferredOffset: node.deferredKeyword?.offset,
+ asOffset: node.asKeyword!.offset,
+ name: prefix.name,
+ nameOffset: prefix.offset,
+ );
+ }
+
+ return UnlinkedImportDirective(
+ combinators: _serializeCombinators(node.combinators),
+ configurations: _serializeConfigurations(node.configurations),
+ importKeywordOffset: node.importKeyword.offset,
+ uri: node.uri.stringValue,
+ prefix: unlinkedPrefix,
);
}
}
@@ -1651,7 +1704,7 @@
/// Information about a single `import` directive.
class ImportDirectiveState<U extends DirectiveUri> extends DirectiveState {
- final UnlinkedNamespaceDirective directive;
+ final UnlinkedImportDirective directive;
final U selectedUri;
final NamespaceDirectiveUris uris;
@@ -1671,7 +1724,7 @@
/// into a [Source].
Source? get importedSource => null;
- bool get isSyntheticDartCoreImport => directive.isSyntheticDartCoreImport;
+ bool get isSyntheticDartCore => directive.isSyntheticDartCore;
}
/// [ImportDirectiveWithUri] that has a valid URI that references a file.
diff --git a/pkg/analyzer/lib/src/dart/analysis/index.dart b/pkg/analyzer/lib/src/dart/analysis/index.dart
index 1a47eb1..695b5cd 100644
--- a/pkg/analyzer/lib/src/dart/analysis/index.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/index.dart
@@ -730,7 +730,7 @@
@override
void visitImportDirective(ImportDirective node) {
- ImportElement? element = node.element;
+ final element = node.element2;
recordUriReference(element?.importedLibrary, node.uri);
super.visitImportDirective(node);
}
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
index 2fc3001..fbbea18 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
@@ -582,7 +582,7 @@
} else if (directive is ImportDirectiveImpl) {
_resolveImportDirective(
directive: directive,
- importElement: _libraryElement.imports[importIndex],
+ importElement: _libraryElement.imports2[importIndex],
importState: _library.imports[importIndex],
libraryErrorReporter: libraryErrorReporter,
);
@@ -715,12 +715,12 @@
void _resolveImportDirective({
required ImportDirectiveImpl directive,
- required ImportElement importElement,
+ required ImportElement2 importElement,
required ImportDirectiveState importState,
required ErrorReporter libraryErrorReporter,
}) {
directive.element = importElement;
- directive.prefix?.staticElement = importElement.prefix;
+ directive.prefix?.staticElement = importElement.prefix?.element;
_resolveNamespaceDirective(
directive: directive,
primaryUriNode: directive.uri,
diff --git a/pkg/analyzer/lib/src/dart/analysis/search.dart b/pkg/analyzer/lib/src/dart/analysis/search.dart
index 657f117..51c397e 100644
--- a/pkg/analyzer/lib/src/dart/analysis/search.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/search.dart
@@ -130,13 +130,13 @@
class ImportElementReferencesVisitor extends RecursiveAstVisitor<void> {
final List<SearchResult> results = <SearchResult>[];
- final ImportElement importElement;
+ final ImportElement2 importElement;
final CompilationUnitElement enclosingUnitElement;
late final Set<Element> importedElements;
ImportElementReferencesVisitor(
- ImportElement element, this.enclosingUnitElement)
+ ImportElement2 element, this.enclosingUnitElement)
: importElement = element {
importedElements = element.namespace.definedNames.values.toSet();
}
@@ -153,7 +153,7 @@
return;
}
if (importElement.prefix != null) {
- if (node.staticElement == importElement.prefix) {
+ if (node.staticElement == importElement.prefix?.element) {
var parent = node.parent;
if (parent is PrefixedIdentifier && parent.prefix == node) {
var element = parent.writeOrReadElement?.declaration;
@@ -262,7 +262,7 @@
element, (n) => n is Block, searchedFiles);
}
return _searchReferences_Function(element, searchedFiles);
- } else if (element is ImportElement) {
+ } else if (element is ImportElement2) {
return _searchReferences_Import(element, searchedFiles);
} else if (kind == ElementKind.LABEL ||
kind == ElementKind.LOCAL_VARIABLE) {
@@ -556,7 +556,7 @@
}
Future<List<SearchResult>> _searchReferences_Import(
- ImportElement element, SearchedFiles searchedFiles) async {
+ ImportElement2 element, SearchedFiles searchedFiles) async {
String path = element.source.fullName;
if (!searchedFiles.add(path, this)) {
return const <SearchResult>[];
diff --git a/pkg/analyzer/lib/src/dart/analysis/unlinked_data.dart b/pkg/analyzer/lib/src/dart/analysis/unlinked_data.dart
index 116cd36..43877b2 100644
--- a/pkg/analyzer/lib/src/dart/analysis/unlinked_data.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/unlinked_data.dart
@@ -90,6 +90,71 @@
}
}
+class UnlinkedCombinator {
+ final int keywordOffset;
+ final int endOffset;
+ final bool isShow;
+ final List<String> names;
+
+ UnlinkedCombinator({
+ required this.keywordOffset,
+ required this.endOffset,
+ required this.isShow,
+ required this.names,
+ });
+
+ factory UnlinkedCombinator.read(SummaryDataReader reader) {
+ return UnlinkedCombinator(
+ keywordOffset: reader.readUInt30(),
+ endOffset: reader.readUInt30(),
+ isShow: reader.readBool(),
+ names: reader.readStringUtf8List(),
+ );
+ }
+
+ void write(BufferedSink sink) {
+ sink.writeUInt30(keywordOffset);
+ sink.writeUInt30(endOffset);
+ sink.writeBool(isShow);
+ sink.writeStringUtf8Iterable(names);
+ }
+}
+
+/// Unlinked information about an `export` directive.
+class UnlinkedExportDirective extends UnlinkedNamespaceDirective {
+ UnlinkedExportDirective({
+ required super.combinators,
+ required super.configurations,
+ required super.uri,
+ });
+
+ factory UnlinkedExportDirective.read(SummaryDataReader reader) {
+ return UnlinkedExportDirective(
+ combinators: reader.readTypedList(
+ () => UnlinkedCombinator.read(reader),
+ ),
+ configurations: reader.readTypedList(
+ () => UnlinkedNamespaceDirectiveConfiguration.read(reader),
+ ),
+ uri: reader.readOptionalStringUtf8(),
+ );
+ }
+
+ void write(BufferedSink sink) {
+ sink.writeList<UnlinkedCombinator>(
+ combinators,
+ (x) => x.write(sink),
+ );
+ sink.writeList<UnlinkedNamespaceDirectiveConfiguration>(
+ configurations,
+ (x) {
+ x.write(sink);
+ },
+ );
+ sink.writeOptionalStringUtf8(uri);
+ }
+}
+
class UnlinkedImportAugmentationDirective {
final String? uri;
@@ -110,6 +175,89 @@
}
}
+/// Unlinked information about an 'import' directive.
+class UnlinkedImportDirective extends UnlinkedNamespaceDirective {
+ final int importKeywordOffset;
+ final bool isSyntheticDartCore;
+ final UnlinkedImportDirectivePrefix? prefix;
+
+ UnlinkedImportDirective({
+ required super.combinators,
+ required super.configurations,
+ required this.importKeywordOffset,
+ this.isSyntheticDartCore = false,
+ required this.prefix,
+ required super.uri,
+ });
+
+ factory UnlinkedImportDirective.read(SummaryDataReader reader) {
+ return UnlinkedImportDirective(
+ combinators: reader.readTypedList(
+ () => UnlinkedCombinator.read(reader),
+ ),
+ configurations: reader.readTypedList(
+ () => UnlinkedNamespaceDirectiveConfiguration.read(reader),
+ ),
+ importKeywordOffset: reader.readUInt30() - 1,
+ isSyntheticDartCore: reader.readBool(),
+ prefix: reader.readOptionalObject(
+ UnlinkedImportDirectivePrefix.read,
+ ),
+ uri: reader.readOptionalStringUtf8(),
+ );
+ }
+
+ void write(BufferedSink sink) {
+ sink.writeList<UnlinkedCombinator>(
+ combinators,
+ (x) => x.write(sink),
+ );
+ sink.writeList<UnlinkedNamespaceDirectiveConfiguration>(
+ configurations,
+ (x) {
+ x.write(sink);
+ },
+ );
+ sink.writeUInt30(1 + importKeywordOffset);
+ sink.writeBool(isSyntheticDartCore);
+ sink.writeOptionalObject<UnlinkedImportDirectivePrefix>(
+ prefix,
+ (x) => x.write(sink),
+ );
+ sink.writeOptionalStringUtf8(uri);
+ }
+}
+
+class UnlinkedImportDirectivePrefix {
+ final int? deferredOffset;
+ final int asOffset;
+ final String name;
+ final int nameOffset;
+
+ UnlinkedImportDirectivePrefix({
+ required this.deferredOffset,
+ required this.asOffset,
+ required this.name,
+ required this.nameOffset,
+ });
+
+ factory UnlinkedImportDirectivePrefix.read(SummaryDataReader reader) {
+ return UnlinkedImportDirectivePrefix(
+ deferredOffset: reader.readOptionalUInt30(),
+ asOffset: reader.readUInt30(),
+ name: reader.readStringUtf8(),
+ nameOffset: reader.readUInt30(),
+ );
+ }
+
+ void write(BufferedSink sink) {
+ sink.writeOptionalUInt30(deferredOffset);
+ sink.writeUInt30(asOffset);
+ sink.writeStringUtf8(name);
+ sink.writeUInt30(nameOffset);
+ }
+}
+
class UnlinkedLibraryAugmentationDirective {
final String? uri;
final UnlinkedSourceRange uriRange;
@@ -154,43 +302,16 @@
}
}
-/// Unlinked information about a namespace directive.
-class UnlinkedNamespaceDirective {
- /// The configurations that control which library will actually be used.
+abstract class UnlinkedNamespaceDirective {
+ final List<UnlinkedCombinator> combinators;
final List<UnlinkedNamespaceDirectiveConfiguration> configurations;
-
- final bool isSyntheticDartCoreImport;
-
- /// The URI referenced by this directive, nad used by default when none
- /// of the [configurations] matches.
final String? uri;
UnlinkedNamespaceDirective({
+ required this.combinators,
required this.configurations,
- this.isSyntheticDartCoreImport = false,
required this.uri,
});
-
- factory UnlinkedNamespaceDirective.read(SummaryDataReader reader) {
- return UnlinkedNamespaceDirective(
- configurations: reader.readTypedList(
- () => UnlinkedNamespaceDirectiveConfiguration.read(reader),
- ),
- uri: reader.readOptionalStringUtf8(),
- isSyntheticDartCoreImport: reader.readBool(),
- );
- }
-
- void write(BufferedSink sink) {
- sink.writeList<UnlinkedNamespaceDirectiveConfiguration>(
- configurations,
- (x) {
- x.write(sink);
- },
- );
- sink.writeOptionalStringUtf8(uri);
- sink.writeBool(isSyntheticDartCoreImport);
- }
}
/// Unlinked information about a namespace directive configuration.
@@ -342,10 +463,10 @@
final List<UnlinkedImportAugmentationDirective> augmentations;
/// `export` directives.
- final List<UnlinkedNamespaceDirective> exports;
+ final List<UnlinkedExportDirective> exports;
/// `import` directives.
- final List<UnlinkedNamespaceDirective> imports;
+ final List<UnlinkedImportDirective> imports;
/// Encoded informative data.
final Uint8List informativeBytes;
@@ -397,10 +518,10 @@
() => UnlinkedImportAugmentationDirective.read(reader),
),
exports: reader.readTypedList(
- () => UnlinkedNamespaceDirective.read(reader),
+ () => UnlinkedExportDirective.read(reader),
),
imports: reader.readTypedList(
- () => UnlinkedNamespaceDirective.read(reader),
+ () => UnlinkedImportDirective.read(reader),
),
informativeBytes: reader.readUint8List(),
libraryAugmentationDirective: reader.readOptionalObject(
@@ -431,10 +552,10 @@
sink.writeList<UnlinkedImportAugmentationDirective>(augmentations, (x) {
x.write(sink);
});
- sink.writeList<UnlinkedNamespaceDirective>(exports, (x) {
+ sink.writeList<UnlinkedExportDirective>(exports, (x) {
x.write(sink);
});
- sink.writeList<UnlinkedNamespaceDirective>(imports, (x) {
+ sink.writeList<UnlinkedImportDirective>(imports, (x) {
x.write(sink);
});
sink.writeUint8List(informativeBytes);
diff --git a/pkg/analyzer/lib/src/dart/ast/ast.dart b/pkg/analyzer/lib/src/dart/ast/ast.dart
index 60e5845..b8d68c1 100644
--- a/pkg/analyzer/lib/src/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/src/dart/ast/ast.dart
@@ -15,6 +15,7 @@
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/ast/to_source_visitor.dart';
import 'package:analyzer/src/dart/ast/token.dart';
+import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/resolver/typed_literal_resolver.dart';
import 'package:analyzer/src/fasta/token_utils.dart' as util show findPrevious;
import 'package:analyzer/src/generated/resolver.dart';
@@ -6420,8 +6421,18 @@
_becomeParentOf(_prefix);
}
+ @Deprecated('Use element2 instead')
@override
- ImportElement? get element => super.element as ImportElement?;
+ ImportElement? get element {
+ final element2 = this.element2;
+ if (element2 != null) {
+ return ImportElementImpl(element2);
+ }
+ return null;
+ }
+
+ @override
+ ImportElement2? get element2 => super.element as ImportElement2?;
@override
Token get firstTokenAfterCommentAndMetadata => importKeyword;
@@ -6439,7 +6450,7 @@
@override
LibraryElement? get uriElement {
- return element?.importedLibrary;
+ return element2?.importedLibrary;
}
@override
@@ -8995,11 +9006,11 @@
bool get isDeferred {
Element? element = _prefix.staticElement;
if (element is PrefixElement) {
- List<ImportElement> imports = element.imports;
+ final imports = element.imports2;
if (imports.length != 1) {
return false;
}
- return imports[0].isDeferred;
+ return imports[0].prefix is DeferredImportElementPrefix;
}
return false;
}
diff --git a/pkg/analyzer/lib/src/dart/ast/element_locator.dart b/pkg/analyzer/lib/src/dart/ast/element_locator.dart
index e0bfc35..dd37049 100644
--- a/pkg/analyzer/lib/src/dart/ast/element_locator.dart
+++ b/pkg/analyzer/lib/src/dart/ast/element_locator.dart
@@ -111,7 +111,7 @@
@override
Element? visitImportDirective(ImportDirective node) {
- return node.element;
+ return node.element2;
}
@override
diff --git a/pkg/analyzer/lib/src/dart/element/display_string_builder.dart b/pkg/analyzer/lib/src/dart/element/display_string_builder.dart
index e9ff526..c3e6e47 100644
--- a/pkg/analyzer/lib/src/dart/element/display_string_builder.dart
+++ b/pkg/analyzer/lib/src/dart/element/display_string_builder.dart
@@ -138,9 +138,9 @@
_writeFormalParameters(element.parameters, forElement: true);
}
- void writeImportElement(ImportElementImpl element) {
+ void writeImportElement(ImportElement2Impl element) {
_write('import ');
- (element.importedLibrary as LibraryElementImpl).appendTo(this);
+ _writeDirectiveUri(element.uri);
}
void writeInterfaceType(InterfaceType type) {
@@ -164,15 +164,7 @@
void writePartElement(PartElementImpl element) {
_write('part ');
-
- final uri = element.uri;
- if (uri is DirectiveUriWithUnitImpl) {
- _write('unit ${uri.unit.source.uri}');
- } else if (uri is DirectiveUriWithSourceImpl) {
- _write('source ${uri.source}');
- } else {
- _write('<unknown>');
- }
+ _writeDirectiveUri(element.uri);
}
void writePrefixElement(PrefixElementImpl element) {
@@ -241,6 +233,16 @@
_buffer.write(str);
}
+ void _writeDirectiveUri(DirectiveUri uri) {
+ if (uri is DirectiveUriWithUnitImpl) {
+ _write('unit ${uri.unit.source.uri}');
+ } else if (uri is DirectiveUriWithSourceImpl) {
+ _write('source ${uri.source}');
+ } else {
+ _write('<unknown>');
+ }
+ }
+
void _writeFormalParameters(
List<ParameterElement> parameters, {
required bool forElement,
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index 8d2924b8..4215c02 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -1662,21 +1662,32 @@
}
}
+class DeferredImportElementPrefixImpl extends ImportElementPrefixImpl
+ implements DeferredImportElementPrefix {
+ DeferredImportElementPrefixImpl({
+ required super.element,
+ });
+}
+
class DirectiveUriImpl implements DirectiveUri {}
-class DirectiveUriWithLibraryImpl extends DirectiveUriWithRelativeUriImpl
+class DirectiveUriWithLibraryImpl extends DirectiveUriWithSourceImpl
implements DirectiveUriWithLibrary {
@override
- final LibraryElementImpl library;
+ late LibraryElementImpl library;
DirectiveUriWithLibraryImpl({
required super.relativeUriString,
required super.relativeUri,
+ required super.source,
required this.library,
});
- @override
- Source get source => library.source;
+ DirectiveUriWithLibraryImpl.read({
+ required super.relativeUriString,
+ required super.relativeUri,
+ required super.source,
+ });
}
class DirectiveUriWithRelativeUriImpl
@@ -3613,42 +3624,38 @@
}
}
-/// A concrete implementation of an [ImportElement].
-class ImportElementImpl extends UriReferencedElementImpl
- implements ImportElement {
- @override
- LibraryElement? importedLibrary;
-
- @override
- PrefixElement? prefix;
-
+class ImportElement2Impl extends _ExistingElementImpl
+ implements ImportElement2 {
@override
List<NamespaceCombinator> combinators = const [];
- /// The cached value of [namespace].
+ @override
+ final int importKeywordOffset;
+
+ @override
+ final ImportElementPrefix? prefix;
+
+ @override
+ final DirectiveUri uri;
+
Namespace? _namespace;
- /// Initialize a newly created import element at the given [offset].
- /// The offset may be `-1` if the import is synthetic.
- ImportElementImpl(int offset) : super(null, offset);
+ ImportElement2Impl({
+ required this.importKeywordOffset,
+ required this.prefix,
+ required this.uri,
+ }) : super(null, importKeywordOffset);
@override
- CompilationUnitElementImpl get enclosingUnit {
- var enclosingLibrary = enclosingElement as LibraryElementImpl;
- return enclosingLibrary._definingCompilationUnit;
- }
+ int get hashCode => identityHashCode(this);
@override
- String get identifier => "${importedLibrary?.identifier}@$nameOffset";
-
- @override
- bool get isDeferred {
- return hasModifier(Modifier.DEFERRED);
- }
-
- /// Set whether this import is for a deferred library.
- set isDeferred(bool isDeferred) {
- setModifier(Modifier.DEFERRED, isDeferred);
+ LibraryElement? get importedLibrary {
+ final uri = this.uri;
+ if (uri is DirectiveUriWithLibrary) {
+ return uri.library;
+ }
+ return null;
}
@override
@@ -3656,25 +3663,264 @@
@override
Namespace get namespace {
- return _namespace ??=
- NamespaceBuilder().createImportNamespaceForDirective(this);
+ final uri = this.uri;
+ if (uri is DirectiveUriWithLibrary) {
+ return _namespace ??=
+ NamespaceBuilder().createImportNamespaceForDirective(
+ importedLibrary: uri.library,
+ combinators: combinators,
+ prefix: prefix?.element,
+ );
+ }
+ return Namespace.EMPTY;
}
@override
- T? accept<T>(ElementVisitor<T> visitor) => visitor.visitImportElement(this);
+ bool operator ==(Object other) {
+ return identical(this, other);
+ }
+
+ @override
+ T? accept<T>(ElementVisitor<T> visitor) {
+ return visitor.visitImportElement2(this);
+ }
@override
void appendTo(ElementDisplayStringBuilder builder) {
builder.writeImportElement(this);
}
+}
+
+/// A concrete implementation of an [ImportElement].
+@Deprecated('Use ImportElement2 instead')
+class ImportElementImpl extends ElementImpl implements ImportElement {
+ final ImportElement2 base;
+
+ ImportElementImpl(this.base) : super(base.name, base.nameOffset);
+
+ @override
+ List<NamespaceCombinator> get combinators => base.combinators;
+
+ @override
+ AnalysisContext get context => base.context;
+
+ @override
+ Element get declaration => base.declaration;
+
+ @override
+ String get displayName => base.displayName;
+
+ @override
+ String? get documentationComment => base.documentationComment;
+
+ @override
+ Element? get enclosingElement => base.enclosingElement;
+
+ @override
+ bool get hasAlwaysThrows => base.hasAlwaysThrows;
+
+ @override
+ bool get hasDeprecated => base.hasDeprecated;
+
+ @override
+ bool get hasDoNotStore => base.hasDoNotStore;
+
+ @override
+ bool get hasFactory => base.hasFactory;
+
+ @override
+ bool get hasInternal => base.hasInternal;
+
+ @override
+ bool get hasIsTest => base.hasIsTest;
+
+ @override
+ bool get hasIsTestGroup => base.hasIsTestGroup;
+
+ @override
+ bool get hasJS => base.hasJS;
+
+ @override
+ bool get hasLiteral => base.hasLiteral;
+
+ @override
+ bool get hasMustCallSuper => base.hasMustCallSuper;
+
+ @override
+ bool get hasNonVirtual => base.hasNonVirtual;
+
+ @override
+ bool get hasOptionalTypeArgs => base.hasOptionalTypeArgs;
+
+ @override
+ bool get hasOverride => base.hasOverride;
+
+ @override
+ bool get hasProtected => base.hasProtected;
+
+ @override
+ bool get hasRequired => base.hasRequired;
+
+ @override
+ bool get hasSealed => base.hasSealed;
+
+ @override
+ bool get hasUseResult => base.hasUseResult;
+
+ @override
+ bool get hasVisibleForOverriding => base.hasVisibleForOverriding;
+
+ @override
+ bool get hasVisibleForTemplate => base.hasVisibleForTemplate;
+
+ @override
+ bool get hasVisibleForTesting => base.hasVisibleForTesting;
+
+ @override
+ int get id => base.id;
+
+ @override
+ String get identifier => 'import@$nameOffset';
+
+ @override
+ LibraryElement? get importedLibrary {
+ final uri = base.uri;
+ if (uri is DirectiveUriWithLibrary) {
+ return uri.library;
+ }
+ return null;
+ }
+
+ @override
+ bool get isDeferred => base.prefix is DeferredImportElementPrefix;
+
+ @override
+ bool get isPrivate => base.isPrivate;
+
+ @override
+ bool get isPublic => base.isPublic;
+
+ @override
+ bool get isSynthetic => base.isSynthetic;
+
+ @override
+ ElementKind get kind => base.kind;
+
+ @override
+ LibraryElementImpl get library => base.library as LibraryElementImpl;
+
+ @override
+ Source get librarySource => base.librarySource;
+
+ @override
+ ElementLocation get location => base.location!;
+
+ @override
+ List<ElementAnnotation> get metadata => base.metadata;
+
+ @override
+ String? get name => base.name;
+
+ @override
+ int get nameLength => base.nameLength;
+
+ @override
+ int get nameOffset => base.nameOffset;
+
+ @override
+ Namespace get namespace => base.namespace;
+
+ @override
+ Element get nonSynthetic => this;
+
+ @override
+ PrefixElement? get prefix => base.prefix?.element;
+
+ @override
+ AnalysisSession? get session => base.session;
+
+ @override
+ Source get source => base.source;
+
+ @override
+ String? get uri {
+ final uri = base.uri;
+ if (uri is DirectiveUriWithRelativeUriString) {
+ return uri.relativeUriString;
+ } else {
+ return null;
+ }
+ }
+
+ @override
+ // TODO: implement uriEnd
+ int get uriEnd => throw UnimplementedError();
+
+ @override
+ // TODO: implement uriOffset
+ int get uriOffset => throw UnimplementedError();
+
+ @override
+ bool operator ==(Object other) {
+ return identical(this, other);
+ }
+
+ @override
+ T? accept<T>(ElementVisitor<T> visitor) {
+ return visitor.visitImportElement(this);
+ }
+
+ @override
+ String getDisplayString(
+ {required bool withNullability, bool multiline = false}) {
+ return base.getDisplayString(
+ withNullability: withNullability,
+ multiline: multiline,
+ );
+ }
+
+ @override
+ String getExtendedDisplayName(String? shortName) {
+ return base.getExtendedDisplayName(shortName);
+ }
+
+ @Deprecated('Use isAccessibleIn2() instead')
+ @override
+ bool isAccessibleIn(LibraryElement? library) {
+ return base.isAccessibleIn(library);
+ }
+
+ @override
+ bool isAccessibleIn2(LibraryElement library) {
+ return base.isAccessibleIn2(library);
+ }
+
+ @override
+ E thisOrAncestorMatching<E extends Element>(
+ bool Function(Element p1) predicate) {
+ return base.thisOrAncestorMatching(predicate) as E;
+ }
+
+ @override
+ E? thisOrAncestorOfType<E extends Element>() {
+ return base.thisOrAncestorOfType();
+ }
@override
void visitChildren(ElementVisitor visitor) {
- super.visitChildren(visitor);
- prefix?.accept(visitor);
+ base.visitChildren(visitor);
}
}
+class ImportElementPrefixImpl implements ImportElementPrefix {
+ @override
+ final PrefixElement element;
+
+ ImportElementPrefixImpl({
+ required this.element,
+ });
+}
+
/// A concrete implementation of a [LabelElement].
class LabelElementImpl extends ElementImpl implements LabelElement {
/// A flag indicating whether this label is associated with a `switch`
@@ -3736,6 +3982,10 @@
FeatureSet get featureSet => augmented.featureSet;
@override
+ // TODO: implement imports2
+ List<ImportElement2> get imports2 => throw UnimplementedError();
+
+ @override
bool get isNonNullableByDefault => augmented.isNonNullableByDefault;
@override
@@ -3897,20 +4147,24 @@
@override
List<LibraryElement> get importedLibraries {
- HashSet<LibraryElement> libraries = HashSet<LibraryElement>();
- for (ImportElement element in imports) {
- var library = element.importedLibrary;
- if (library != null) {
- libraries.add(library);
- }
- }
- return libraries.toList(growable: false);
+ return imports2
+ .map((import) => import.uri)
+ .whereType<DirectiveUriWithLibrary>()
+ .map((uri) => uri.library)
+ .toSet()
+ .toList();
+ }
+
+ @Deprecated('Use imports2 instead')
+ @override
+ List<ImportElement> get imports {
+ return imports2.map(ImportElementImpl.new).toList();
}
@override
- List<ImportElement> get imports {
+ List<ImportElement2> get imports2 {
linkedData?.read(this);
- return _imports;
+ return _imports2;
}
@override
@@ -4018,7 +4272,7 @@
@override
List<PrefixElement> get prefixes =>
- _prefixes ??= buildPrefixesFromImports(imports);
+ _prefixes ??= buildPrefixesFromImports(imports2);
@override
Namespace get publicNamespace {
@@ -4124,8 +4378,8 @@
required String? prefix,
required String name,
}) {
- for (var importElement in imports) {
- if (importElement.prefix?.name == prefix &&
+ for (var importElement in imports2) {
+ if (importElement.prefix?.element.name == prefix &&
importElement.importedLibrary?.isSynthetic != false) {
var showCombinators = importElement.combinators
.whereType<ShowElementCombinator>()
@@ -4193,10 +4447,10 @@
}
static List<PrefixElement> buildPrefixesFromImports(
- List<ImportElement> imports) {
+ List<ImportElement2> imports) {
HashSet<PrefixElement> prefixes = HashSet<PrefixElement>();
- for (ImportElement element in imports) {
- var prefix = element.prefix;
+ for (ImportElement2 element in imports) {
+ var prefix = element.prefix?.element;
if (prefix != null) {
prefixes.add(prefix);
}
@@ -4233,7 +4487,7 @@
/// A list containing specifications of all of the imports defined in this
/// library.
- List<ImportElement> _imports = _Sentinel.importElement;
+ List<ImportElement2> _imports2 = _Sentinel.importElement2;
/// A list containing specifications of all of the exports defined in this
/// library.
@@ -4297,32 +4551,29 @@
@override
String get identifier => '${_definingCompilationUnit.source.uri}';
+ @Deprecated('Use imports2 instead')
@override
List<ImportElement> get imports {
- return _imports;
+ return _imports2.map(ImportElementImpl.new).toList();
}
/// Set the specifications of all of the imports defined in this library to
/// the given list of [imports].
- set imports(List<ImportElement> imports) {
- for (ImportElement importElement in imports) {
- (importElement as ImportElementImpl).enclosingElement = this;
- var prefix = importElement.prefix as PrefixElementImpl?;
- if (prefix != null) {
- prefix.enclosingElement = this;
- }
+ set imports2(List<ImportElement2> imports) {
+ for (final importElement in imports) {
+ (importElement as ImportElement2Impl).enclosingElement = this;
}
- _imports = imports;
+ _imports2 = imports;
_prefixes = null;
}
- List<ImportElement> get imports_unresolved {
- return _imports;
+ List<ImportElement2> get imports_unresolved {
+ return _imports2;
}
@override
List<PrefixElement> get prefixes =>
- _prefixes ??= buildPrefixesFromImports(imports);
+ _prefixes ??= buildPrefixesFromImports(imports2);
@override
AnalysisSessionImpl get session;
@@ -4337,16 +4588,18 @@
super.visitChildren(visitor);
_definingCompilationUnit.accept(visitor);
safelyVisitChildren(exports, visitor);
+ // ignore: deprecated_member_use_from_same_package
safelyVisitChildren(imports, visitor);
+ safelyVisitChildren(imports2, visitor);
}
static List<PrefixElement> buildPrefixesFromImports(
- List<ImportElement> imports) {
+ List<ImportElement2> imports) {
HashSet<PrefixElement> prefixes = HashSet<PrefixElement>();
- for (ImportElement element in imports) {
- var prefix = element.prefix;
+ for (final import in imports) {
+ var prefix = import.prefix;
if (prefix != null) {
- prefixes.add(prefix);
+ prefixes.add(prefix.element);
}
}
return prefixes.toList(growable: false);
@@ -5227,6 +5480,7 @@
LibraryOrAugmentationElementImpl get enclosingElement2 =>
super.enclosingElement as LibraryOrAugmentationElementImpl;
+ @Deprecated('Use imports2 instead')
@override
List<ImportElement> get imports {
return enclosingElement2.imports
@@ -5235,6 +5489,13 @@
}
@override
+ List<ImportElement2> get imports2 {
+ return enclosingElement2.imports2
+ .where((import) => import.prefix?.element == this)
+ .toList();
+ }
+
+ @override
ElementKind get kind => ElementKind.PREFIX;
@override
@@ -6271,7 +6532,7 @@
static final List<FieldElement> fieldElement = List.unmodifiable([]);
static final List<AugmentationImportElement> augmentationImportElement =
List.unmodifiable([]);
- static final List<ImportElement> importElement = List.unmodifiable([]);
+ static final List<ImportElement2> importElement2 = List.unmodifiable([]);
static final List<MethodElement> methodElement = List.unmodifiable([]);
static final List<PropertyAccessorElement> propertyAccessorElement =
List.unmodifiable([]);
diff --git a/pkg/analyzer/lib/src/dart/element/scope.dart b/pkg/analyzer/lib/src/dart/element/scope.dart
index f57b6e0..1fab302 100644
--- a/pkg/analyzer/lib/src/dart/element/scope.dart
+++ b/pkg/analyzer/lib/src/dart/element/scope.dart
@@ -118,6 +118,16 @@
_element.prefixes.forEach(_addGetter);
_element.units.forEach(_addUnitElements);
+
+ // TODO(scheglov) I don't understand why it used to work, but broke now.
+ // Now: when I'm adding `ImportElement2`.
+ // We used to get it from `exportedReference`, but this is wrong.
+ // These elements are declared in dart:core itself.
+ final reference = _element.reference!;
+ if (reference.name == 'dart:core') {
+ _addGetter(DynamicElementImpl.instance);
+ _addGetter(NeverElementImpl.instance);
+ }
}
void _addExtension(ExtensionElement element) {
@@ -155,9 +165,11 @@
PrefixScope(this._library, PrefixElement? prefix) {
final elementFactory = _library.session.elementFactory;
- for (final import in _library.imports) {
- if (import.prefix == prefix) {
- final importedLibrary = import.importedLibrary;
+ for (final import in _library.imports2) {
+ final importedUri = import.uri;
+ if (importedUri is DirectiveUriWithLibrary &&
+ import.prefix?.element == prefix) {
+ final importedLibrary = importedUri.library;
if (importedLibrary is LibraryElementImpl) {
final combinators = import.combinators.build();
for (final exportedReference in importedLibrary.exportedReferences) {
@@ -172,7 +184,7 @@
_add(importedElement);
}
}
- if (import.isDeferred) {
+ if (import.prefix is DeferredImportElementPrefix) {
_deferredLibrary ??= importedLibrary;
}
}
diff --git a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
index 055b894..0e87bbf 100644
--- a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
+++ b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
@@ -288,7 +288,7 @@
if (element is LocalVariableElement ||
(element is ParameterElement && !element.isNamed)) {
await collectReferences2(element.source!.fullName, performance!);
- } else if (element is ImportElement) {
+ } else if (element is ImportElement2) {
return await _searchReferences_Import(element);
} else {
var result = performance!.run('getFilesContaining', (performance) {
@@ -842,7 +842,7 @@
}
Future<List<CiderSearchMatch>> _searchReferences_Import(
- ImportElement element) async {
+ ImportElement2 element) async {
var results = <CiderSearchMatch>[];
LibraryElement libraryElement = element.library;
for (CompilationUnitElement unitElement in libraryElement.units) {
diff --git a/pkg/analyzer/lib/src/dart/micro/utils.dart b/pkg/analyzer/lib/src/dart/micro/utils.dart
index fc88c98..a4e9419 100644
--- a/pkg/analyzer/lib/src/dart/micro/utils.dart
+++ b/pkg/analyzer/lib/src/dart/micro/utils.dart
@@ -28,7 +28,7 @@
if (node is SimpleIdentifier && element is PrefixElement) {
var parent = node.parent;
if (parent is ImportDirective) {
- element = parent.element;
+ element = parent.element2;
} else {
element = _getImportElementInfo(node);
}
@@ -62,27 +62,27 @@
return constructor;
}
-/// Return the [ImportElement] that declared [prefix] and imports [element].
+/// Return the [ImportElement2] that declared [prefix] and imports [element].
///
/// [libraryElement] - the [LibraryElement] where reference is.
/// [prefix] - the import prefix, maybe `null`.
/// [element] - the referenced element.
-/// [importElementsMap] - the cache of [Element]s imported by [ImportElement]s.
-ImportElement? _getImportElement(LibraryElement libraryElement, String prefix,
- Element element, Map<ImportElement, Set<Element>> importElementsMap) {
+/// [importElementsMap] - the cache of [Element]s imported by [ImportElement2]s.
+ImportElement2? _getImportElement(LibraryElement libraryElement, String prefix,
+ Element element, Map<ImportElement2, Set<Element>> importElementsMap) {
if (element.enclosingElement is! CompilationUnitElement) {
return null;
}
var usedLibrary = element.library;
// find ImportElement that imports used library with used prefix
- List<ImportElement>? candidates;
- for (var importElement in libraryElement.imports) {
+ List<ImportElement2>? candidates;
+ for (var importElement in libraryElement.imports2) {
// required library
if (importElement.importedLibrary != usedLibrary) {
continue;
}
// required prefix
- var prefixElement = importElement.prefix;
+ var prefixElement = importElement.prefix?.element;
if (prefixElement == null) {
continue;
}
@@ -126,9 +126,9 @@
return null;
}
-/// Returns the [ImportElement] that is referenced by [prefixNode] with a
+/// Returns the [ImportElement2] that is referenced by [prefixNode] with a
/// [PrefixElement], maybe `null`.
-ImportElement? _getImportElementInfo(SimpleIdentifier prefixNode) {
+ImportElement2? _getImportElementInfo(SimpleIdentifier prefixNode) {
// prepare environment
var parent = prefixNode.parent;
var unit = prefixNode.thisOrAncestorOfType<CompilationUnit>();
@@ -155,7 +155,7 @@
}
// find ImportElement
var prefix = prefixNode.name;
- var importElementsMap = <ImportElement, Set<Element>>{};
+ var importElementsMap = <ImportElement2, Set<Element>>{};
return _getImportElement(
libraryElement, prefix, usedElement, importElementsMap);
}
diff --git a/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart
index 0906db3..79d5949 100644
--- a/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart
@@ -111,7 +111,7 @@
left = node.leftOperand;
var flow = _resolver.flowAnalysis.flow;
- EqualityInfo<PromotableElement, DartType>? leftInfo;
+ EqualityInfo<DartType>? leftInfo;
var leftExtensionOverride = left is ExtensionOverride;
if (!leftExtensionOverride) {
leftInfo = flow?.equalityOperand_end(left, left.typeOrThrow);
diff --git a/pkg/analyzer/lib/src/dart/resolver/invocation_inferrer.dart b/pkg/analyzer/lib/src/dart/resolver/invocation_inferrer.dart
index b9fcb2c..729d28a 100644
--- a/pkg/analyzer/lib/src/dart/resolver/invocation_inferrer.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/invocation_inferrer.dart
@@ -203,8 +203,7 @@
Substitution.fromPairs(rawType.typeFormals, inferrer.partialInfer());
}
- List<EqualityInfo<PromotableElement, DartType>?>? identicalInfo =
- _isIdentical ? [] : null;
+ List<EqualityInfo<DartType>?>? identicalInfo = _isIdentical ? [] : null;
var parameterMap = _computeParameterMap(rawType?.parameters ?? const []);
var deferredFunctionLiterals = _visitArguments(
parameterMap: parameterMap,
@@ -434,8 +433,7 @@
/// If the invocation being processed is a call to `identical`, informs flow
/// analysis about it, so that it can do appropriate promotions.
- void _recordIdenticalInfo(
- List<EqualityInfo<PromotableElement, DartType>?>? identicalInfo) {
+ void _recordIdenticalInfo(List<EqualityInfo<DartType>?>? identicalInfo) {
var flow = resolver.flowAnalysis.flow;
if (identicalInfo != null) {
flow?.equalityOperation_end(argumentList.parent as Expression,
@@ -446,7 +444,7 @@
/// Resolves any function literals that were deferred by [_visitArguments].
void _resolveDeferredFunctionLiterals(
{required List<_DeferredParamInfo> deferredFunctionLiterals,
- List<EqualityInfo<PromotableElement, DartType>?>? identicalInfo,
+ List<EqualityInfo<DartType>?>? identicalInfo,
Substitution? substitution,
GenericInferrer? inferrer}) {
var flow = resolver.flowAnalysis.flow;
@@ -481,7 +479,7 @@
/// returned.
List<_DeferredParamInfo>? _visitArguments(
{required Map<Object, ParameterElement> parameterMap,
- List<EqualityInfo<PromotableElement, DartType>?>? identicalInfo,
+ List<EqualityInfo<DartType>?>? identicalInfo,
Substitution? substitution,
GenericInferrer? inferrer}) {
assert(whyNotPromotedList.isEmpty);
diff --git a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
index 6d951b3..13368ab 100644
--- a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
@@ -665,8 +665,9 @@
// Note: prefix?.bar is reported as an error in ElementResolver.
if (name == FunctionElement.LOAD_LIBRARY_NAME) {
- var imports = prefix.imports;
- if (imports.length == 1 && imports[0].isDeferred) {
+ var imports = prefix.imports2;
+ if (imports.length == 1 &&
+ imports[0].prefix is DeferredImportElementPrefix) {
var importedLibrary = imports[0].importedLibrary;
var element = importedLibrary?.loadLibraryFunction;
element = _resolver.toLegacyElement(element);
diff --git a/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart b/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart
index 781f48e..5f94025 100644
--- a/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart
@@ -757,8 +757,8 @@
@override
void visitImportDirective(ImportDirective node) {
- var element = node.element;
- if (element is ImportElementImpl) {
+ var element = node.element2;
+ if (element is ImportElement2Impl) {
_setOrCreateMetadataElements(element, node.metadata);
}
diff --git a/pkg/analyzer/lib/src/dart/resolver/scope.dart b/pkg/analyzer/lib/src/dart/resolver/scope.dart
index 84552fb..79865a7 100644
--- a/pkg/analyzer/lib/src/dart/resolver/scope.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/scope.dart
@@ -158,18 +158,13 @@
/// Create a namespace representing the import namespace of the given
/// [element].
- Namespace createImportNamespaceForDirective(ImportElement element) {
- var importedLibrary = element.importedLibrary;
- if (importedLibrary == null) {
- //
- // The imported library will be null if the URI does not reference a valid
- // library.
- //
- return Namespace.EMPTY;
- }
+ Namespace createImportNamespaceForDirective({
+ required LibraryElement importedLibrary,
+ required List<NamespaceCombinator> combinators,
+ required PrefixElement? prefix,
+ }) {
Map<String, Element> exportedNames = _getExportMapping(importedLibrary);
- exportedNames = _applyCombinators(exportedNames, element.combinators);
- var prefix = element.prefix;
+ exportedNames = _applyCombinators(exportedNames, combinators);
if (prefix != null) {
return PrefixedNamespace(prefix.name, exportedNames);
}
diff --git a/pkg/analyzer/lib/src/error/best_practices_verifier.dart b/pkg/analyzer/lib/src/error/best_practices_verifier.dart
index 4f5dc3e..bd89227 100644
--- a/pkg/analyzer/lib/src/error/best_practices_verifier.dart
+++ b/pkg/analyzer/lib/src/error/best_practices_verifier.dart
@@ -584,8 +584,9 @@
@override
void visitImportDirective(ImportDirective node) {
_deprecatedVerifier.importDirective(node);
- var importElement = node.element;
- if (importElement != null && importElement.isDeferred) {
+ var importElement = node.element2;
+ if (importElement != null &&
+ importElement.prefix is DeferredImportElementPrefix) {
_checkForLoadLibraryFunction(node, importElement);
}
_invalidAccessVerifier.verifyImport(node);
@@ -1039,7 +1040,7 @@
return;
}
- var importElement = node.element;
+ var importElement = node.element2;
if (importElement == null) {
return;
}
@@ -1234,14 +1235,14 @@
/// The import has already been determined to be deferred when this is called.
///
/// @param node the import directive to evaluate
- /// @param importElement the [ImportElement] retrieved from the node
+ /// @param importElement the [ImportElement2] retrieved from the node
/// @return `true` if and only if an error code is generated on the passed
/// node
/// See [CompileTimeErrorCode.IMPORT_DEFERRED_LIBRARY_WITH_LOAD_FUNCTION].
bool _checkForLoadLibraryFunction(
- ImportDirective node, ImportElement importElement) {
+ ImportDirective node, ImportElement2 importElement) {
var importedLibrary = importElement.importedLibrary;
- var prefix = importElement.prefix;
+ var prefix = importElement.prefix?.element;
if (importedLibrary == null || prefix == null) {
return false;
}
diff --git a/pkg/analyzer/lib/src/error/dead_code_verifier.dart b/pkg/analyzer/lib/src/error/dead_code_verifier.dart
index 1890e65..9339c45 100644
--- a/pkg/analyzer/lib/src/error/dead_code_verifier.dart
+++ b/pkg/analyzer/lib/src/error/dead_code_verifier.dart
@@ -62,7 +62,7 @@
@override
void visitImportDirective(ImportDirective node) {
- ImportElement? importElement = node.element;
+ final importElement = node.element2;
if (importElement != null) {
// The element is null when the URI is invalid, but not when the URI is
// valid but refers to a non-existent file.
diff --git a/pkg/analyzer/lib/src/error/duplicate_definition_verifier.dart b/pkg/analyzer/lib/src/error/duplicate_definition_verifier.dart
index 55cd57b4..9e725a5 100644
--- a/pkg/analyzer/lib/src/error/duplicate_definition_verifier.dart
+++ b/pkg/analyzer/lib/src/error/duplicate_definition_verifier.dart
@@ -318,8 +318,8 @@
}
}
- for (ImportElement importElement in _currentLibrary.imports) {
- var prefix = importElement.prefix;
+ for (final importElement in _currentLibrary.imports2) {
+ var prefix = importElement.prefix?.element;
if (prefix != null) {
definedGetters[prefix.name] = prefix;
}
diff --git a/pkg/analyzer/lib/src/error/imports_verifier.dart b/pkg/analyzer/lib/src/error/imports_verifier.dart
index 52781b7..4c990ff 100644
--- a/pkg/analyzer/lib/src/error/imports_verifier.dart
+++ b/pkg/analyzer/lib/src/error/imports_verifier.dart
@@ -390,7 +390,7 @@
for (int i = 0; i < length; i++) {
ImportDirective unusedImport = _unusedImports[i];
// Check that the imported URI exists and isn't dart:core
- var importElement = unusedImport.element;
+ var importElement = unusedImport.element2;
if (importElement != null) {
var libraryElement = importElement.importedLibrary;
if (libraryElement == null ||
@@ -776,7 +776,7 @@
Namespace? computeNamespace(ImportDirective importDirective) {
var namespace = this[importDirective];
if (namespace == null) {
- var importElement = importDirective.element;
+ var importElement = importDirective.element2;
if (importElement != null) {
namespace = importElement.namespace;
this[importDirective] = namespace;
diff --git a/pkg/analyzer/lib/src/generated/element_resolver.dart b/pkg/analyzer/lib/src/generated/element_resolver.dart
index 9ae6c75..26fca88 100644
--- a/pkg/analyzer/lib/src/generated/element_resolver.dart
+++ b/pkg/analyzer/lib/src/generated/element_resolver.dart
@@ -26,7 +26,7 @@
/// * An identifier within the declaration of that name should resolve to the
/// element being declared.
/// * An identifier denoting a prefix should resolve to the element
-/// representing the import that defines the prefix (an [ImportElement]).
+/// representing the import that defines the prefix (an [ImportElement2]).
/// * An identifier denoting a variable should resolve to the element
/// representing the variable (a [VariableElement]).
/// * An identifier denoting a parameter should resolve to the element
@@ -56,7 +56,7 @@
/// specified library does not exist.
/// 5. Every [ImportDirective] and [ExportDirective] should resolve to the
/// element representing the library being specified by the directive unless
-/// the specified library does not exist (an [ImportElement] or
+/// the specified library does not exist (an [ImportElement2] or
/// [ExportElement]).
/// 6. The identifier representing the prefix in an [ImportDirective] should
/// resolve to the element representing the prefix (a [PrefixElement]).
@@ -240,7 +240,7 @@
}
}
}
- var importElement = node.element;
+ var importElement = node.element2;
if (importElement != null) {
// The element is null when the URI is invalid
var library = importElement.importedLibrary;
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index 01f79da..28d5b16 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -857,14 +857,14 @@
@override
void visitImportDirective(ImportDirective node) {
- var importElement = node.element;
+ var importElement = node.element2;
if (node.prefix != null) {
_checkForBuiltInIdentifierAsName(node.prefix!,
CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_PREFIX_NAME);
}
if (importElement != null) {
_checkForImportInternalLibrary(node, importElement);
- if (importElement.isDeferred) {
+ if (importElement.prefix is DeferredImportElementPrefix) {
_checkForDeferredImportOfExtensions(node, importElement);
}
}
@@ -2299,7 +2299,7 @@
/// Report a diagnostic if there are any extensions in the imported library
/// that are not hidden.
void _checkForDeferredImportOfExtensions(
- ImportDirective directive, ImportElement importElement) {
+ ImportDirective directive, ImportElement2 importElement) {
for (var element in importElement.namespace.definedNames.values) {
if (element is ExtensionElement) {
errorReporter.reportErrorForNode(
@@ -2872,12 +2872,12 @@
/// Check that if the visiting library is not system, then any given library
/// should not be SDK internal library. The [importElement] is the
- /// [ImportElement] retrieved from the node, if the element in the node was
+ /// [ImportElement2] retrieved from the node, if the element in the node was
/// `null`, then this method is not called
///
/// See [CompileTimeErrorCode.IMPORT_INTERNAL_LIBRARY].
void _checkForImportInternalLibrary(
- ImportDirective directive, ImportElement importElement) {
+ ImportDirective directive, ImportElement2 importElement) {
if (_isInSystemLibrary) {
return;
}
@@ -5096,7 +5096,7 @@
if (library == null) {
return '';
}
- List<ImportElement> imports = _currentLibrary.imports;
+ final imports = _currentLibrary.imports2;
int count = imports.length;
for (int i = 0; i < count; i++) {
if (identical(imports[i].importedLibrary, library)) {
diff --git a/pkg/analyzer/lib/src/generated/testing/element_factory.dart b/pkg/analyzer/lib/src/generated/testing/element_factory.dart
index 68896c6..49121ab4 100644
--- a/pkg/analyzer/lib/src/generated/testing/element_factory.dart
+++ b/pkg/analyzer/lib/src/generated/testing/element_factory.dart
@@ -195,16 +195,6 @@
return getter;
}
- static ImportElementImpl importFor(
- LibraryElement importedLibrary, PrefixElement? prefix,
- [List<NamespaceCombinator> combinators = const <NamespaceCombinator>[]]) {
- ImportElementImpl spec = ImportElementImpl(0);
- spec.importedLibrary = importedLibrary;
- spec.prefix = prefix;
- spec.combinators = combinators;
- return spec;
- }
-
static LibraryElementImpl library(
AnalysisContext context, String libraryName) {
String fileName = "/$libraryName.dart";
diff --git a/pkg/analyzer/lib/src/summary2/ast_binary_tag.dart b/pkg/analyzer/lib/src/summary2/ast_binary_tag.dart
index 83c6aea..a59dff8 100644
--- a/pkg/analyzer/lib/src/summary2/ast_binary_tag.dart
+++ b/pkg/analyzer/lib/src/summary2/ast_binary_tag.dart
@@ -16,6 +16,12 @@
withNothing,
}
+enum ImportElementPrefixKind {
+ isDeferred,
+ isNotDeferred,
+ isNull,
+}
+
class Tag {
static const int Nothing = 0;
static const int Something = 1;
@@ -103,6 +109,7 @@
static const int MemberLegacyWithoutTypeArguments = 1;
static const int MemberLegacyWithTypeArguments = 2;
static const int MemberWithTypeArguments = 3;
+ static const int ImportPrefixElement = 4;
static const int ParameterKindRequiredPositional = 1;
static const int ParameterKindOptionalPositional = 2;
diff --git a/pkg/analyzer/lib/src/summary2/bundle_reader.dart b/pkg/analyzer/lib/src/summary2/bundle_reader.dart
index aa14833f..268706f 100644
--- a/pkg/analyzer/lib/src/summary2/bundle_reader.dart
+++ b/pkg/analyzer/lib/src/summary2/bundle_reader.dart
@@ -370,12 +370,15 @@
unitElement: unitElement,
);
- for (var import in element.imports) {
- import as ImportElementImpl;
+ for (var import in element.imports2) {
+ import as ImportElement2Impl;
import.metadata = reader._readAnnotationList(
unitElement: unitElement,
);
- import.importedLibrary = reader.readElement() as LibraryElementImpl?;
+ final uri = import.uri;
+ if (uri is DirectiveUriWithLibraryImpl) {
+ uri.library = reader.libraryOfUri(uri.source.uri);
+ }
}
for (var export in element.exports) {
@@ -445,14 +448,22 @@
libraryElement.reference = _reference;
libraryElement.languageVersion = _readLanguageVersion();
- libraryElement.imports = _reader.readTypedList(_readImportElement);
+ libraryElement.imports2 = _reader.readTypedList(() {
+ return _readImportElement(
+ libraryElement: libraryElement,
+ );
+ });
libraryElement.exports = _reader.readTypedList(_readExportElement);
LibraryElementFlags.read(_reader, libraryElement);
- var unitContainerRef = _reference.getChild('@unit');
+ for (final import in libraryElement.imports2) {
+ final prefixElement = import.prefix?.element;
+ if (prefixElement is PrefixElementImpl) {
+ libraryElement.encloseElement(prefixElement);
+ }
+ }
libraryElement.definingCompilationUnit = _readUnitElement(
- unitContainerRef: unitContainerRef,
libraryElement: libraryElement,
librarySource: librarySource,
unitSource: librarySource,
@@ -460,7 +471,6 @@
libraryElement.parts2 = _reader.readTypedList(() {
return _readPartElement(
- unitContainerRef: unitContainerRef,
libraryElement: libraryElement,
);
});
@@ -581,7 +591,6 @@
}
DirectiveUri _readDirectiveUri({
- required Reference unitContainerRef,
required LibraryElementImpl libraryElement,
}) {
DirectiveUriWithRelativeUriStringImpl readWithRelativeUriString() {
@@ -608,12 +617,15 @@
final sourceUriStr = _reader.readStringReference();
final sourceUri = Uri.parse(sourceUriStr);
- final source = sourceFactory.forUri2(sourceUri)!;
+ final source = sourceFactory.forUri2(sourceUri);
+
+ // TODO(scheglov) https://github.com/dart-lang/sdk/issues/49431
+ final fixedSource = source ?? sourceFactory.forUri('dart:math')!;
return DirectiveUriWithSourceImpl(
relativeUriString: parent.relativeUriString,
relativeUri: parent.relativeUri,
- source: source,
+ source: fixedSource,
);
}
@@ -623,7 +635,6 @@
case DirectiveUriKind.withUnit:
final parent = readWithSource();
final unitElement = _readUnitElement(
- unitContainerRef: unitContainerRef,
libraryElement: libraryElement,
librarySource: libraryElement.source,
unitSource: parent.source,
@@ -634,8 +645,12 @@
unit: unitElement,
);
case DirectiveUriKind.withLibrary:
- // TODO: Handle this case.
- throw UnimplementedError();
+ final parent = readWithSource();
+ return DirectiveUriWithLibraryImpl.read(
+ relativeUriString: parent.relativeUriString,
+ relativeUri: parent.relativeUri,
+ source: parent.source,
+ );
case DirectiveUriKind.withSource:
return readWithSource();
case DirectiveUriKind.withRelativeUri:
@@ -859,21 +874,53 @@
});
}
- ImportElementImpl _readImportElement() {
- var element = ImportElementImpl(-1);
+ ImportElement2Impl _readImportElement({
+ required LibraryElementImpl libraryElement,
+ }) {
+ final uri = _readDirectiveUri(
+ libraryElement: libraryElement,
+ );
+ final prefix = _readImportElementPrefix();
+ final combinators = _reader.readTypedList(_readNamespaceCombinator);
+
+ final element = ImportElement2Impl(
+ importKeywordOffset: -1,
+ uri: uri,
+ prefix: prefix,
+ )..combinators = combinators;
ImportElementFlags.read(_reader, element);
- element.uri = _reader.readOptionalStringReference();
- var prefixName = _reader.readOptionalStringReference();
- if (prefixName != null) {
- var reference = _reference.getChild('@prefix').getChild(prefixName);
- var prefixElement =
- PrefixElementImpl(prefixName, -1, reference: reference);
- element.prefix = prefixElement;
- }
- element.combinators = _reader.readTypedList(_readNamespaceCombinator);
return element;
}
+ ImportElementPrefixImpl? _readImportElementPrefix() {
+ PrefixElementImpl buildElement(String name) {
+ final reference = _reference.getChild('@prefix').getChild(name);
+ final existing = reference.element;
+ if (existing is PrefixElementImpl) {
+ return existing;
+ } else {
+ return PrefixElementImpl(name, -1, reference: reference);
+ }
+ }
+
+ final kindIndex = _reader.readByte();
+ final kind = ImportElementPrefixKind.values[kindIndex];
+ switch (kind) {
+ case ImportElementPrefixKind.isDeferred:
+ final name = _reader.readStringReference();
+ return DeferredImportElementPrefixImpl(
+ element: buildElement(name),
+ );
+ case ImportElementPrefixKind.isNotDeferred:
+ final name = _reader.readStringReference();
+ return ImportElementPrefixImpl(
+ element: buildElement(name),
+ );
+ case ImportElementPrefixKind.isNull:
+ return null;
+ }
+ }
+
LibraryLanguageVersion _readLanguageVersion() {
var packageMajor = _reader.readUInt30();
var packageMinor = _reader.readUInt30();
@@ -1044,11 +1091,9 @@
}
PartElement _readPartElement({
- required Reference unitContainerRef,
required LibraryElementImpl libraryElement,
}) {
final uri = _readDirectiveUri(
- unitContainerRef: unitContainerRef,
libraryElement: libraryElement,
);
@@ -1272,7 +1317,6 @@
}
CompilationUnitElementImpl _readUnitElement({
- required Reference unitContainerRef,
required LibraryElementImpl libraryElement,
required Source librarySource,
required Source unitSource,
@@ -1285,7 +1329,8 @@
lineInfo: LineInfo([0]),
);
- var unitReference = unitContainerRef.getChild('${unitSource.uri}');
+ final unitContainerRef = _reference.getChild('@unit');
+ final unitReference = unitContainerRef.getChild('${unitSource.uri}');
unitElement.setLinkedData(
unitReference,
CompilationUnitElementLinkedData(
@@ -1423,6 +1468,10 @@
this._reader,
);
+ LibraryElementImpl libraryOfUri(Uri uri) {
+ return _elementFactory.libraryOfUri2(uri);
+ }
+
int readByte() {
return _reader.readByte();
}
diff --git a/pkg/analyzer/lib/src/summary2/bundle_writer.dart b/pkg/analyzer/lib/src/summary2/bundle_writer.dart
index 1008851..1d6844a 100644
--- a/pkg/analyzer/lib/src/summary2/bundle_writer.dart
+++ b/pkg/analyzer/lib/src/summary2/bundle_writer.dart
@@ -102,7 +102,7 @@
_writeFeatureSet(libraryElement.featureSet);
_writeLanguageVersion(libraryElement.languageVersion);
_resolutionSink._writeAnnotationList(libraryElement.metadata);
- _writeList(libraryElement.imports, _writeImportElement);
+ _writeList(libraryElement.imports2, _writeImportElement);
_writeList(libraryElement.exports, _writeExportElement);
for (final partElement in libraryElement.parts2) {
_resolutionSink._writeAnnotationList(partElement.metadata);
@@ -190,8 +190,8 @@
}
if (element is DirectiveUriWithLibrary) {
- // TODO(scheglov) implement
- throw UnimplementedError();
+ _sink.writeByte(DirectiveUriKind.withLibrary.index);
+ writeWithSource(element);
} else if (element is DirectiveUriWithUnit) {
_sink.writeByte(DirectiveUriKind.withUnit.index);
writeWithSource(element);
@@ -317,14 +317,25 @@
});
}
- void _writeImportElement(ImportElement element) {
- element as ImportElementImpl;
- ImportElementFlags.write(_sink, element);
- _sink._writeOptionalStringReference(element.uri);
- _sink._writeOptionalStringReference(element.prefix?.name);
- _sink.writeList(element.combinators, _writeNamespaceCombinator);
+ void _writeImportElement(ImportElement2 element) {
+ element as ImportElement2Impl;
_resolutionSink._writeAnnotationList(element.metadata);
- _resolutionSink.writeElement(element.importedLibrary);
+ _writeDirectiveUri(element.uri);
+ _writeImportElementPrefix(element.prefix);
+ _sink.writeList(element.combinators, _writeNamespaceCombinator);
+ ImportElementFlags.write(_sink, element);
+ }
+
+ void _writeImportElementPrefix(ImportElementPrefix? prefix) {
+ if (prefix is DeferredImportElementPrefix) {
+ _sink.writeByte(ImportElementPrefixKind.isDeferred.index);
+ _sink._writeStringReference(prefix.element.name);
+ } else if (prefix is ImportElementPrefix) {
+ _sink.writeByte(ImportElementPrefixKind.isNotDeferred.index);
+ _sink._writeStringReference(prefix.element.name);
+ } else {
+ _sink.writeByte(ImportElementPrefixKind.isNull.index);
+ }
}
void _writeLanguageVersion(LibraryLanguageVersion version) {
diff --git a/pkg/analyzer/lib/src/summary2/element_builder.dart b/pkg/analyzer/lib/src/summary2/element_builder.dart
index e53f62f..da83e3f 100644
--- a/pkg/analyzer/lib/src/summary2/element_builder.dart
+++ b/pkg/analyzer/lib/src/summary2/element_builder.dart
@@ -26,9 +26,8 @@
final CompilationUnitElementImpl _unitElement;
final _exports = <ExportElement>[];
- final _imports = <ImportElement>[];
var _isFirstLibraryDirective = true;
- var _hasCoreImport = false;
+ var _importDirectiveIndex = 0;
var _partDirectiveIndex = 0;
_EnclosingContext _enclosingContext;
@@ -66,17 +65,6 @@
_libraryElement.exports = _exports;
- if (!_hasCoreImport) {
- final dartCore = _linker.elementFactory.dartCoreElement;
- _imports.add(
- ImportElementImpl(-1)
- ..importedLibrary = dartCore
- ..isSynthetic = true
- ..uri = 'dart:core',
- );
- }
- _libraryElement.imports = _imports;
-
if (_isFirstLibraryDirective) {
_isFirstLibraryDirective = false;
var firstDirective = unit.directives.firstOrNull;
@@ -757,39 +745,11 @@
@override
void visitImportDirective(covariant ImportDirectiveImpl node) {
- var uriStr = node.uri.stringValue;
-
- var element = ImportElementImpl(node.importKeyword.offset);
- element.combinators = _buildCombinators(node.combinators);
-
- try {
- element.importedLibrary = _selectLibrary(node);
- } on ArgumentError {
- // TODO(scheglov) Remove this when using `ImportDirectiveState`.
- }
-
- element.isDeferred = node.deferredKeyword != null;
- element.metadata = _buildAnnotations(node.metadata);
- element.uri = uriStr;
-
- var prefixNode = node.prefix;
- if (prefixNode != null) {
- element.prefix = PrefixElementImpl(
- prefixNode.name,
- prefixNode.offset,
- reference: _libraryBuilder.reference
- .getChild('@prefix')
- .getChild(prefixNode.name),
- );
- }
-
- node.element = element;
-
- _imports.add(element);
-
- if (uriStr == 'dart:core') {
- _hasCoreImport = true;
- }
+ final index = _importDirectiveIndex++;
+ final importElement = _libraryElement.imports2[index];
+ importElement as ImportElement2Impl;
+ importElement.metadata = _buildAnnotations(node.metadata);
+ node.element = importElement;
}
@override
diff --git a/pkg/analyzer/lib/src/summary2/element_flags.dart b/pkg/analyzer/lib/src/summary2/element_flags.dart
index a8ba866..eae765c 100644
--- a/pkg/analyzer/lib/src/summary2/element_flags.dart
+++ b/pkg/analyzer/lib/src/summary2/element_flags.dart
@@ -151,18 +151,15 @@
}
class ImportElementFlags {
- static const int _isDeferred = 1 << 0;
- static const int _isSynthetic = 1 << 1;
+ static const int _isSynthetic = 1 << 0;
- static void read(SummaryDataReader reader, ImportElementImpl element) {
+ static void read(SummaryDataReader reader, ImportElement2Impl element) {
var byte = reader.readByte();
- element.isDeferred = (byte & _isDeferred) != 0;
element.isSynthetic = (byte & _isSynthetic) != 0;
}
- static void write(BufferedSink sink, ImportElementImpl element) {
+ static void write(BufferedSink sink, ImportElement2Impl element) {
var result = 0;
- result |= element.isDeferred ? _isDeferred : 0;
result |= element.isSynthetic ? _isSynthetic : 0;
sink.writeByte(result);
}
diff --git a/pkg/analyzer/lib/src/summary2/informative_data.dart b/pkg/analyzer/lib/src/summary2/informative_data.dart
index 086c90d..b684e30 100644
--- a/pkg/analyzer/lib/src/summary2/informative_data.dart
+++ b/pkg/analyzer/lib/src/summary2/informative_data.dart
@@ -448,16 +448,16 @@
element.documentationComment = info.docComment;
}
- forCorrespondingPairs<ImportElement, _InfoImport>(
+ forCorrespondingPairs<ImportElement2, _InfoImport>(
element.imports_unresolved,
info.imports,
(element, info) {
- element as ImportElementImpl;
+ element as ImportElement2Impl;
element.nameOffset = info.nameOffset;
- var prefix = element.prefix;
- if (prefix is PrefixElementImpl) {
- prefix.nameOffset = info.prefixOffset;
+ final prefixElement = element.prefix?.element;
+ if (prefixElement is PrefixElementImpl) {
+ prefixElement.nameOffset = info.prefixOffset;
}
_applyToCombinators(element.combinators, info.combinators);
@@ -487,7 +487,7 @@
info.libraryConstantOffsets,
(applier) {
applier.applyToMetadata(element);
- applier.applyToDirectives(element.imports);
+ applier.applyToImports(element.imports2);
applier.applyToDirectives(element.exports);
applier.applyToPartDirectives(element.parts2);
},
@@ -1691,6 +1691,12 @@
}
}
+ void applyToImports(List<ImportElement2> elements) {
+ for (var element in elements) {
+ applyToMetadata(element);
+ }
+ }
+
void applyToMetadata(Element element) {
for (var annotation in element.metadata) {
var node = (annotation as ElementAnnotationImpl).annotationAst;
diff --git a/pkg/analyzer/lib/src/summary2/library_builder.dart b/pkg/analyzer/lib/src/summary2/library_builder.dart
index 83db6a1..2e9b765 100644
--- a/pkg/analyzer/lib/src/summary2/library_builder.dart
+++ b/pkg/analyzer/lib/src/summary2/library_builder.dart
@@ -132,6 +132,8 @@
/// Build elements for declarations in the library units, add top-level
/// declarations to the local scope, for combining into export scopes.
void buildElements() {
+ element.imports2 = kind.imports.map(_buildImport).toList();
+
for (var linkingUnit in units) {
var elementBuilder = ElementBuilder(
libraryBuilder: this,
@@ -402,6 +404,121 @@
}
}
+ ImportElement2Impl _buildImport(ImportDirectiveState state) {
+ final importPrefix = state.directive.prefix.mapOrNull((unlinked) {
+ if (unlinked.deferredOffset != null) {
+ return DeferredImportElementPrefixImpl(
+ element: _buildPrefix(
+ name: unlinked.name,
+ nameOffset: unlinked.nameOffset,
+ ),
+ );
+ } else {
+ return ImportElementPrefixImpl(
+ element: _buildPrefix(
+ name: unlinked.name,
+ nameOffset: unlinked.nameOffset,
+ ),
+ );
+ }
+ });
+
+ final combinators = state.directive.combinators.map((unlinked) {
+ if (unlinked.isShow) {
+ return ShowElementCombinatorImpl()
+ ..offset = unlinked.keywordOffset
+ ..end = unlinked.endOffset
+ ..shownNames = unlinked.names;
+ } else {
+ // TODO(scheglov) Why no offsets?
+ return HideElementCombinatorImpl()..hiddenNames = unlinked.names;
+ }
+ }).toList();
+
+ final DirectiveUri uri;
+ if (state is ImportDirectiveWithFile) {
+ final importedLibraryKind = state.importedLibrary;
+ if (importedLibraryKind != null) {
+ final importedFile = importedLibraryKind.file;
+ final importedUri = importedFile.uri;
+ final elementFactory = linker.elementFactory;
+ final importedLibrary = elementFactory.libraryOfUri2(importedUri);
+ uri = DirectiveUriWithLibraryImpl(
+ relativeUriString: state.selectedUri.relativeUriStr,
+ relativeUri: state.selectedUri.relativeUri,
+ source: importedLibrary.source,
+ library: importedLibrary,
+ );
+ } else {
+ uri = DirectiveUriWithSourceImpl(
+ relativeUriString: state.selectedUri.relativeUriStr,
+ relativeUri: state.selectedUri.relativeUri,
+ source: state.importedSource,
+ );
+ }
+ } else if (state is ImportDirectiveWithInSummarySource) {
+ final importedLibrarySource = state.importedLibrarySource;
+ if (importedLibrarySource != null) {
+ final importedUri = importedLibrarySource.uri;
+ final elementFactory = linker.elementFactory;
+ final importedLibrary = elementFactory.libraryOfUri2(importedUri);
+ uri = DirectiveUriWithLibraryImpl(
+ relativeUriString: state.selectedUri.relativeUriStr,
+ relativeUri: state.selectedUri.relativeUri,
+ source: importedLibrary.source,
+ library: importedLibrary,
+ );
+ } else {
+ uri = DirectiveUriWithSourceImpl(
+ relativeUriString: state.selectedUri.relativeUriStr,
+ relativeUri: state.selectedUri.relativeUri,
+ source: state.importedSource,
+ );
+ }
+ } else {
+ final selectedUri = state.selectedUri;
+ if (selectedUri is file_state.DirectiveUriWithUri) {
+ uri = DirectiveUriWithRelativeUriImpl(
+ relativeUriString: selectedUri.relativeUriStr,
+ relativeUri: selectedUri.relativeUri,
+ );
+ } else if (selectedUri is file_state.DirectiveUriWithString) {
+ uri = DirectiveUriWithRelativeUriStringImpl(
+ relativeUriString: selectedUri.relativeUriStr,
+ );
+ } else {
+ uri = DirectiveUriImpl();
+ }
+ }
+
+ return ImportElement2Impl(
+ importKeywordOffset: state.directive.importKeywordOffset,
+ uri: uri,
+ prefix: importPrefix,
+ )
+ ..combinators = combinators
+ ..isSynthetic = state.isSyntheticDartCore;
+ }
+
+ PrefixElementImpl _buildPrefix({
+ required String name,
+ required int nameOffset,
+ }) {
+ final reference = this.reference.getChild('@prefix').getChild(name);
+ final existing = reference.element;
+ if (existing is PrefixElementImpl) {
+ return existing;
+ } else {
+ final result = PrefixElementImpl(
+ name,
+ nameOffset,
+ reference: reference,
+ );
+ element.encloseElement(result);
+ return result;
+ }
+ }
+
/// These elements are implicitly declared in `dart:core`.
void _declareDartCoreDynamicNever() {
if (reference.name == 'dart:core') {
@@ -592,3 +709,10 @@
super.visitElement(element);
}
}
+
+extension<T> on T? {
+ R? mapOrNull<R>(R Function(T) mapper) {
+ final self = this;
+ return self != null ? mapper(self) : null;
+ }
+}
diff --git a/pkg/analyzer/lib/src/summary2/macro_application.dart b/pkg/analyzer/lib/src/summary2/macro_application.dart
index 8260593..8fcd68c 100644
--- a/pkg/analyzer/lib/src/summary2/macro_application.dart
+++ b/pkg/analyzer/lib/src/summary2/macro_application.dart
@@ -258,8 +258,8 @@
constructorName = annotation.constructorName?.name;
} else if (nameNode is PrefixedIdentifier) {
final importPrefixCandidate = nameNode.prefix.name;
- final hasImportPrefix = libraryBuilder.element.imports
- .any((import) => import.prefix?.name == importPrefixCandidate);
+ final hasImportPrefix = libraryBuilder.element.imports2.any(
+ (import) => import.prefix?.element.name == importPrefixCandidate);
if (hasImportPrefix) {
prefix = importPrefixCandidate;
name = nameNode.identifier.name;
@@ -273,8 +273,8 @@
throw StateError('${nameNode.runtimeType} $nameNode');
}
- for (final import in libraryBuilder.element.imports) {
- if (import.prefix?.name != prefix) {
+ for (final import in libraryBuilder.element.imports2) {
+ if (import.prefix?.element.name != prefix) {
continue;
}
diff --git a/pkg/analyzer/lib/src/test_utilities/find_element.dart b/pkg/analyzer/lib/src/test_utilities/find_element.dart
index d2923f5..3d7b0c3 100644
--- a/pkg/analyzer/lib/src/test_utilities/find_element.dart
+++ b/pkg/analyzer/lib/src/test_utilities/find_element.dart
@@ -50,10 +50,10 @@
throw StateError('Not found: $name');
}
- ImportElement import(String targetUri, {bool mustBeUnique = true}) {
- ImportElement? importElement;
+ ImportElement2 import(String targetUri, {bool mustBeUnique = true}) {
+ ImportElement2? importElement;
- for (var import in libraryElement.imports) {
+ for (var import in libraryElement.imports2) {
var importedUri = import.importedLibrary?.source.uri.toString();
if (importedUri == targetUri) {
if (importElement == null) {
@@ -243,10 +243,10 @@
}
PrefixElement prefix(String name) {
- for (var import_ in libraryElement.imports) {
- var prefix = import_.prefix;
- if (prefix?.name == name) {
- return prefix!;
+ for (var import_ in libraryElement.imports2) {
+ var prefix = import_.prefix?.element;
+ if (prefix != null && prefix.name == name) {
+ return prefix;
}
}
throw StateError('Not found: $name');
@@ -311,13 +311,13 @@
/// Helper for searching imported elements.
class ImportFindElement extends _FindElementBase {
- final ImportElement import;
+ final ImportElement2 import;
ImportFindElement(this.import);
LibraryElement get importedLibrary => import.importedLibrary!;
- PrefixElement? get prefix => import.prefix;
+ PrefixElement? get prefix => import.prefix?.element;
@override
CompilationUnitElement get unitElement {
diff --git a/pkg/analyzer/test/generated/element_resolver_test.dart b/pkg/analyzer/test/generated/element_resolver_test.dart
index 7cac99c..af43c44 100644
--- a/pkg/analyzer/test/generated/element_resolver_test.dart
+++ b/pkg/analyzer/test/generated/element_resolver_test.dart
@@ -422,7 +422,7 @@
''', [
error(HintCode.UNUSED_IMPORT, 7, 11),
]);
- expect(findNode.import('dart:math').element!.importedLibrary!.name,
+ expect(findNode.import('dart:math').element2!.importedLibrary!.name,
'dart.math');
}
@@ -432,7 +432,7 @@
''', [
error(HintCode.UNUSED_IMPORT, 7, 11),
]);
- expect(findNode.import('dart:math').element!.importedLibrary!.name,
+ expect(findNode.import('dart:math').element2!.importedLibrary!.name,
'dart.math');
}
@@ -448,7 +448,7 @@
]);
var importedVariables = findNode
.import('lib1.dart')
- .element!
+ .element2!
.importedLibrary!
.definingCompilationUnit
.topLevelVariables;
diff --git a/pkg/analyzer/test/generated/resolver_test_case.dart b/pkg/analyzer/test/generated/resolver_test_case.dart
index 704b831..d0f7199 100644
--- a/pkg/analyzer/test/generated/resolver_test_case.dart
+++ b/pkg/analyzer/test/generated/resolver_test_case.dart
@@ -110,7 +110,7 @@
void visitImportDirective(ImportDirective node) {
// Not sure how to test the combinators given that it isn't an error if the
// names are not defined.
- _checkResolved(node, node.element, (node) => node is ImportElement);
+ _checkResolved(node, node.element2, (node) => node is ImportElement2);
var prefix = node.prefix;
if (prefix == null) {
return;
diff --git a/pkg/analyzer/test/id_tests/assigned_variables_test.dart b/pkg/analyzer/test/id_tests/assigned_variables_test.dart
index 5107df9..309c55e 100644
--- a/pkg/analyzer/test/id_tests/assigned_variables_test.dart
+++ b/pkg/analyzer/test/id_tests/assigned_variables_test.dart
@@ -97,8 +97,8 @@
node, () => super.visitVariableDeclaration(node));
}
- Set<String> _convertVars(Iterable<PromotableElement> x) =>
- x.map((e) => e.name).toSet();
+ Set<String> _convertVars(Iterable<int> x) =>
+ x.map((e) => _currentAssignedVariables!.variableForKey(e).name).toSet();
void _handlePossibleTopLevelDeclaration(
AstNode node, void Function() callback) {
diff --git a/pkg/analyzer/test/src/dart/analysis/analyzer_state_printer.dart b/pkg/analyzer/test/src/dart/analysis/analyzer_state_printer.dart
index f55b678..89af3ef 100644
--- a/pkg/analyzer/test/src/dart/analysis/analyzer_state_printer.dart
+++ b/pkg/analyzer/test/src/dart/analysis/analyzer_state_printer.dart
@@ -235,7 +235,7 @@
sink.write(' ${file.uri}');
}
- if (import.isSyntheticDartCoreImport) {
+ if (import.isSyntheticDartCore) {
sink.write(' synthetic');
}
sink.writeln();
@@ -250,7 +250,7 @@
sink.write(' notLibrary');
}
- if (import.isSyntheticDartCoreImport) {
+ if (import.isSyntheticDartCore) {
sink.write(' synthetic');
}
sink.writeln();
@@ -258,7 +258,7 @@
final uriStr = _stringOfUriStr(import.selectedUri.relativeUriStr);
sink.write(_indent);
sink.write('uri: $uriStr');
- if (import.isSyntheticDartCoreImport) {
+ if (import.isSyntheticDartCore) {
sink.write(' synthetic');
}
sink.writeln();
diff --git a/pkg/analyzer/test/src/dart/analysis/driver_resolution_test.dart b/pkg/analyzer/test/src/dart/analysis/driver_resolution_test.dart
index 1901bb3..b2fac8c 100644
--- a/pkg/analyzer/test/src/dart/analysis/driver_resolution_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/driver_resolution_test.dart
@@ -412,8 +412,8 @@
await resolveTestFile();
CompilationUnit unit = result.unit;
- ImportElement aImport = unit.declaredElement!.library.imports[0];
- PrefixElement aPrefix = aImport.prefix!;
+ final aImport = unit.declaredElement!.library.imports2[0];
+ PrefixElement aPrefix = aImport.prefix!.element;
LibraryElement aLibrary = aImport.importedLibrary!;
CompilationUnitElement aUnitElement = aLibrary.definingCompilationUnit;
@@ -451,8 +451,8 @@
await resolveTestFile();
CompilationUnit unit = result.unit;
- ImportElement aImport = unit.declaredElement!.library.imports[0];
- PrefixElement aPrefix = aImport.prefix!;
+ final aImport = unit.declaredElement!.library.imports2[0];
+ PrefixElement aPrefix = aImport.prefix!.element;
LibraryElement aLibrary = aImport.importedLibrary!;
CompilationUnitElement aUnitElement = aLibrary.definingCompilationUnit;
@@ -492,8 +492,8 @@
await resolveTestFile();
CompilationUnit unit = result.unit;
- ImportElement aImport = unit.declaredElement!.library.imports[0];
- PrefixElement aPrefix = aImport.prefix!;
+ final aImport = unit.declaredElement!.library.imports2[0];
+ PrefixElement aPrefix = aImport.prefix!.element;
LibraryElement aLibrary = aImport.importedLibrary!;
CompilationUnitElement aUnitElement = aLibrary.definingCompilationUnit;
@@ -533,8 +533,8 @@
await resolveTestFile();
CompilationUnit unit = result.unit;
- ImportElement aImport = unit.declaredElement!.library.imports[0];
- PrefixElement aPrefix = aImport.prefix!;
+ final aImport = unit.declaredElement!.library.imports2[0];
+ PrefixElement aPrefix = aImport.prefix!.element;
LibraryElement aLibrary = aImport.importedLibrary!;
CompilationUnitElement aUnitElement = aLibrary.definingCompilationUnit;
@@ -1443,7 +1443,7 @@
assertInvokeType(invocation, 'Future<dynamic> Function()');
var target = invocation.target as SimpleIdentifier;
- assertElement(target, import.prefix);
+ assertElement(target, import.prefix?.element);
assertType(target, null);
var name = invocation.methodName;
@@ -1469,7 +1469,7 @@
assertInvokeType(invocation, 'Future<dynamic> Function()');
var target = invocation.target as SimpleIdentifier;
- assertElement(target, import.prefix);
+ assertElement(target, import.prefix?.element);
assertType(target, null);
var name = invocation.methodName;
@@ -1500,7 +1500,7 @@
assertType(prefixed, 'Future<dynamic> Function()');
var prefix = prefixed.prefix;
- assertElement(prefix, import.prefix);
+ assertElement(prefix, import.prefix?.element);
assertType(prefix, null);
var identifier = prefixed.identifier;
@@ -1528,7 +1528,7 @@
assertElement(prefixed, v.getter);
assertType(prefixed, 'int');
- assertElement(prefixed.prefix, import.prefix);
+ assertElement(prefixed.prefix, import.prefix?.element);
assertType(prefixed.prefix, null);
assertElement(prefixed.identifier, v.getter);
@@ -1540,7 +1540,7 @@
assertElementNull(prefixed);
assertTypeNull(prefixed);
- assertElement(prefixed.prefix, import.prefix);
+ assertElement(prefixed.prefix, import.prefix?.element);
assertType(prefixed.prefix, null);
assertUnresolvedSimpleIdentifier(prefixed.identifier);
@@ -2210,7 +2210,7 @@
await resolveTestFile();
CompilationUnit unit = result.unit;
- ImportElement aImport = unit.declaredElement!.library.imports[0];
+ final aImport = unit.declaredElement!.library.imports2[0];
LibraryElement aLibrary = aImport.importedLibrary!;
ClassElement cElement = aLibrary.getType('C')!;
@@ -2241,7 +2241,7 @@
SimpleIdentifier typePrefix = typeIdentifier.prefix;
expect(typePrefix.name, 'p');
- expect(typePrefix.staticElement, same(aImport.prefix));
+ expect(typePrefix.staticElement, same(aImport.prefix?.element));
expect(typePrefix.staticType, isNull);
expect(typeIdentifier.identifier.staticElement, same(cElement));
@@ -2271,7 +2271,7 @@
SimpleIdentifier typePrefix = typeIdentifier.prefix;
expect(typePrefix.name, 'p');
- expect(typePrefix.staticElement, same(aImport.prefix));
+ expect(typePrefix.staticElement, same(aImport.prefix?.element));
expect(typePrefix.staticType, isNull);
expect(typeIdentifier.identifier.staticElement, same(cElement));
@@ -2303,7 +2303,7 @@
SimpleIdentifier typePrefix = typeIdentifier.prefix;
expect(typePrefix.name, 'p');
- expect(typePrefix.staticElement, same(aImport.prefix));
+ expect(typePrefix.staticElement, same(aImport.prefix?.element));
expect(typePrefix.staticType, isNull);
expect(typeIdentifier.identifier.staticElement, same(cElement));
@@ -2898,7 +2898,7 @@
findNode.namedType('a.Future'),
futureElement,
'Future<int>',
- expectedPrefix: findElement.import('dart:async').prefix,
+ expectedPrefix: findElement.import('dart:async').prefix?.element,
);
assertNamedType(findNode.namedType('int>'), intElement, 'int');
}
@@ -3231,7 +3231,7 @@
await resolveTestFile();
expect(result.errors, isNotEmpty);
- ImportElement import = findNode.import('dart:math').element!;
+ final import = findNode.import('dart:math').element2!;
var pRef = findNode.simple('p(a)');
assertElement(pRef, import.prefix);
@@ -3440,14 +3440,14 @@
await resolveTestFile();
expect(result.errors, isNotEmpty);
- ImportElement import = findNode.import('dart:math').element!;
+ final import = findNode.import('dart:math').element2!;
var invocation = findNode.methodInvocation('p(a)');
expect(invocation.staticType, isDynamicType);
assertUnresolvedInvokeType(invocation.staticInvokeType!);
var pRef = invocation.methodName;
- assertElement(pRef, import.prefix);
+ assertElement(pRef, import.prefix?.element);
assertTypeDynamic(pRef);
var aRef = findNode.simple('a);');
@@ -3613,7 +3613,7 @@
await resolveTestFile();
expect(result.errors, isNotEmpty);
- ImportElement import = findNode.import('a.dart').element!;
+ final import = findNode.import('a.dart').element2!;
var tElement = import.importedLibrary!.publicNamespace.get('T');
var prefixedName = findNode.prefixed('p.T');
@@ -3681,7 +3681,7 @@
await resolveTestFile();
expect(result.errors, isNotEmpty);
- ImportElement import = findNode.import('a.dart').element!;
+ final import = findNode.import('a.dart').element2!;
var tElement = import.importedLibrary!.publicNamespace.get('T');
var prefixedName = findNode.prefixed('p.T');
@@ -5646,7 +5646,7 @@
assertType(creation, 'C');
assertNamedType(creation.constructorName.type, c, 'C',
- expectedPrefix: import.prefix);
+ expectedPrefix: import.prefix?.element);
}
{
@@ -5656,7 +5656,7 @@
assertType(creation, 'C');
assertNamedType(creation.constructorName.type, c, 'C',
- expectedPrefix: import.prefix);
+ expectedPrefix: import.prefix?.element);
assertElement(creation.constructorName.name, namedConstructor);
}
}
@@ -5907,14 +5907,14 @@
my.Future<int> a;
''');
await resolveTestFile();
- ImportElement myImport = result.libraryElement.imports[0];
+ final myImport = result.libraryElement.imports2[0];
var intRef = findNode.namedType('int> a');
assertNamedType(intRef, intElement, 'int');
var futureRef = findNode.namedType('my.Future<int> a');
assertNamedType(futureRef, futureElement, 'Future<int>',
- expectedPrefix: myImport.prefix);
+ expectedPrefix: myImport.prefix?.element);
}
test_postfix_increment_of_non_generator() async {
@@ -6163,8 +6163,8 @@
// expect(result.errors, isEmpty);
var unitElement = result.unit.declaredElement!;
- ImportElement myImport = unitElement.library.imports[0];
- PrefixElement myPrefix = myImport.prefix!;
+ final myImport = unitElement.library.imports2[0];
+ PrefixElement myPrefix = myImport.prefix!.element;
var myLibrary = myImport.importedLibrary!;
var myUnit = myLibrary.definingCompilationUnit;
@@ -7751,8 +7751,8 @@
await resolveTestFile();
CompilationUnit unit = result.unit;
- ImportElement bImport = unit.declaredElement!.library.imports[0];
- ImportElement cImport = unit.declaredElement!.library.imports[1];
+ final bImport = unit.declaredElement!.library.imports2[0];
+ final cImport = unit.declaredElement!.library.imports2[1];
LibraryElement bLibrary = bImport.importedLibrary!;
LibraryElement aLibrary = bLibrary.exports[0].exportedLibrary!;
@@ -7766,7 +7766,10 @@
expect(typeIdentifier.staticElement, aClass);
expect(typeIdentifier.prefix.name, 'b');
- expect(typeIdentifier.prefix.staticElement, same(bImport.prefix));
+ expect(
+ typeIdentifier.prefix.staticElement,
+ same(bImport.prefix?.element),
+ );
expect(typeIdentifier.identifier.staticElement, aClass);
}
@@ -7779,7 +7782,10 @@
expect(typeIdentifier.staticElement, aClass);
expect(typeIdentifier.prefix.name, 'c');
- expect(typeIdentifier.prefix.staticElement, same(cImport.prefix));
+ expect(
+ typeIdentifier.prefix.staticElement,
+ same(cImport.prefix?.element),
+ );
expect(typeIdentifier.identifier.staticElement, aClass);
}
@@ -7913,7 +7919,7 @@
expect(result.errors, isNotEmpty);
var unitElement = result.unit.declaredElement!;
- var foo = unitElement.library.imports[0].prefix;
+ var foo = unitElement.library.imports2[0].prefix?.element;
List<Statement> statements = _getMainStatements(result);
var statement = statements[0] as ExpressionStatement;
@@ -7997,8 +8003,8 @@
expect(result.errors, isNotEmpty);
var unitElement = result.unit.declaredElement!;
- var mathImport = unitElement.library.imports[0];
- var foo = mathImport.prefix;
+ var mathImport = unitElement.library.imports2[0];
+ var foo = mathImport.prefix?.element;
List<Statement> statements = _getMainStatements(result);
var statement = statements[0] as ExpressionStatement;
@@ -8043,8 +8049,8 @@
expect(result.errors, isNotEmpty);
var unitElement = result.unit.declaredElement!;
- var mathImport = unitElement.library.imports[0];
- var foo = mathImport.prefix;
+ var mathImport = unitElement.library.imports2[0];
+ var foo = mathImport.prefix?.element;
var randomElement = mathImport.importedLibrary!.getType('Random')!;
List<Statement> statements = _getMainStatements(result);
diff --git a/pkg/analyzer/test/src/dart/analysis/driver_test.dart b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
index 0149bed..5226986 100644
--- a/pkg/analyzer/test/src/dart/analysis/driver_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
@@ -2107,7 +2107,7 @@
ResolvedUnitResult result = await driver.getResultValid(testFile);
expect(result.path, testFile);
// Has only imports for valid URIs.
- List<ImportElement> imports = result.libraryElement.imports;
+ final imports = result.libraryElement.imports2;
expect(imports.map((import) {
return import.importedLibrary?.source.uri.toString();
}), ['dart:async', null, 'dart:math', 'dart:core']);
@@ -3568,9 +3568,9 @@
'$unit');
}
- ImportElement _getImportElement(CompilationUnit unit, int directiveIndex) {
+ ImportElement2 _getImportElement(CompilationUnit unit, int directiveIndex) {
var import = unit.directives[directiveIndex] as ImportDirective;
- return import.element!;
+ return import.element2!;
}
Source _getImportSource(CompilationUnit unit, int directiveIndex) {
diff --git a/pkg/analyzer/test/src/dart/analysis/file_state_test.dart b/pkg/analyzer/test/src/dart/analysis/file_state_test.dart
index 1336d79..29bb1a8 100644
--- a/pkg/analyzer/test/src/dart/analysis/file_state_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/file_state_test.dart
@@ -1562,7 +1562,7 @@
final coreKind = core.t1!.kind as LibraryFileStateKind;
for (final import in coreKind.imports) {
- if (import.isSyntheticDartCoreImport) {
+ if (import.isSyntheticDartCore) {
fail('dart:core should not import itself');
}
}
diff --git a/pkg/analyzer/test/src/dart/analysis/search_test.dart b/pkg/analyzer/test/src/dart/analysis/search_test.dart
index c59203e..c95f1a0 100644
--- a/pkg/analyzer/test/src/dart/analysis/search_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/search_test.dart
@@ -1131,7 +1131,7 @@
Random bar() => null;
''');
- ImportElement element = findElement.import('dart:math');
+ final element = findElement.import('dart:math');
var main = findElement.function('main');
var bar = findElement.function('bar');
var kind = SearchResultKind.REFERENCE;
@@ -1163,7 +1163,7 @@
N4 = 0;
}
''');
- ImportElement element = findElement.import('package:test/a.dart');
+ final element = findElement.import('package:test/a.dart');
var main = findElement.function('main');
var kind = SearchResultKind.REFERENCE;
var expected = [
@@ -1213,14 +1213,14 @@
var kind = SearchResultKind.REFERENCE;
var length = 'p.'.length;
{
- ImportElement element = findElement.import('dart:async');
+ final element = findElement.import('dart:async');
var expected = [
_expectId(main, kind, 'p.Future;', length: length),
];
await _verifyReferences(element, expected);
}
{
- ImportElement element = findElement.import('dart:math');
+ final element = findElement.import('dart:math');
var expected = [
_expectId(main, kind, 'p.Random', length: length),
];
@@ -1247,7 +1247,7 @@
a.N4 = 0;
}
''');
- ImportElement element = findElement.import('package:test/a.dart');
+ final element = findElement.import('package:test/a.dart');
var main = findElement.function('main');
var kind = SearchResultKind.REFERENCE;
var length = 'a.'.length;
@@ -2125,7 +2125,7 @@
V(); // nq
}
''');
- ImportElement importElement = findNode.import('show V').element!;
+ final importElement = findNode.import('show V').element2!;
CompilationUnitElement impUnit =
importElement.importedLibrary!.definingCompilationUnit;
TopLevelVariableElement variable = impUnit.topLevelVariables[0];
diff --git a/pkg/analyzer/test/src/dart/element/element_test.dart b/pkg/analyzer/test/src/dart/element/element_test.dart
index b69f6c9..3fa8eab 100644
--- a/pkg/analyzer/test/src/dart/element/element_test.dart
+++ b/pkg/analyzer/test/src/dart/element/element_test.dart
@@ -2,21 +2,17 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/constant/value.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
-import 'package:analyzer/src/dart/analysis/session.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/type.dart';
-import 'package:analyzer/src/generated/engine.dart' show AnalysisContext;
import 'package:analyzer/src/generated/testing/element_factory.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
-import '../../../generated/test_analysis_context.dart';
import '../../../generated/test_support.dart';
import '../../../generated/type_system_test.dart';
import '../resolution/context_collection_resolution.dart';
@@ -33,7 +29,6 @@
defineReflectiveTests(CompilationUnitElementImplTest);
defineReflectiveTests(ElementLocationImplTest);
defineReflectiveTests(ElementImplTest);
- defineReflectiveTests(LibraryElementImplTest);
defineReflectiveTests(TopLevelVariableElementImplTest);
defineReflectiveTests(UniqueLocationTest);
});
@@ -1246,75 +1241,6 @@
}
@reflectiveTest
-class LibraryElementImplTest {
- void test_getImportedLibraries() {
- AnalysisContext context = TestAnalysisContext();
- LibraryElementImpl library1 = ElementFactory.library(context, "l1");
- LibraryElementImpl library2 = ElementFactory.library(context, "l2");
- LibraryElementImpl library3 = ElementFactory.library(context, "l3");
- LibraryElementImpl library4 = ElementFactory.library(context, "l4");
- PrefixElement prefixA = PrefixElementImpl('a', -1);
- PrefixElement prefixB = PrefixElementImpl('b', -1);
- List<ImportElementImpl> imports = [
- ElementFactory.importFor(library2, null),
- ElementFactory.importFor(library2, prefixB),
- ElementFactory.importFor(library3, null),
- ElementFactory.importFor(library3, prefixA),
- ElementFactory.importFor(library3, prefixB),
- ElementFactory.importFor(library4, prefixA)
- ];
- library1.imports = imports;
- List<LibraryElement> libraries = library1.importedLibraries;
- expect(libraries,
- unorderedEquals(<LibraryElement>[library2, library3, library4]));
- }
-
- void test_getPrefixes() {
- AnalysisContext context = TestAnalysisContext();
- LibraryElementImpl library = ElementFactory.library(context, "l1");
- PrefixElement prefixA = PrefixElementImpl('a', -1);
- PrefixElement prefixB = PrefixElementImpl('b', -1);
- List<ImportElementImpl> imports = [
- ElementFactory.importFor(ElementFactory.library(context, "l2"), null),
- ElementFactory.importFor(ElementFactory.library(context, "l3"), null),
- ElementFactory.importFor(ElementFactory.library(context, "l4"), prefixA),
- ElementFactory.importFor(ElementFactory.library(context, "l5"), prefixA),
- ElementFactory.importFor(ElementFactory.library(context, "l6"), prefixB)
- ];
- library.imports = imports;
- List<PrefixElement> prefixes = library.prefixes;
- expect(prefixes, hasLength(2));
- if (identical(prefixA, prefixes[0])) {
- expect(prefixes[1], same(prefixB));
- } else {
- expect(prefixes[0], same(prefixB));
- expect(prefixes[1], same(prefixA));
- }
- }
-
- void test_setImports() {
- AnalysisContext context = TestAnalysisContext();
- LibraryElementImpl library = LibraryElementImpl(
- context,
- _AnalysisSessionMock(),
- 'l1',
- -1,
- 0,
- FeatureSet.latestLanguageVersion());
- List<ImportElementImpl> expectedImports = [
- ElementFactory.importFor(ElementFactory.library(context, "l2"), null),
- ElementFactory.importFor(ElementFactory.library(context, "l3"), null)
- ];
- library.imports = expectedImports;
- List<ImportElement> actualImports = library.imports;
- expect(actualImports, hasLength(expectedImports.length));
- for (int i = 0; i < actualImports.length; i++) {
- expect(actualImports[i], same(expectedImports[i]));
- }
- }
-}
-
-@reflectiveTest
class TopLevelVariableElementImplTest extends PubPackageResolutionTest {
test_computeConstantValue() async {
newFile('$testPackageLibPath/a.dart', r'''
@@ -1600,8 +1526,3 @@
expect(_voidType.resolveToBound(objectNone), same(_voidType));
}
}
-
-class _AnalysisSessionMock implements AnalysisSessionImpl {
- @override
- noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
-}
diff --git a/pkg/analyzer/test/src/dart/resolution/type_literal_test.dart b/pkg/analyzer/test/src/dart/resolution/type_literal_test.dart
index 5f4fe3c..c43750d 100644
--- a/pkg/analyzer/test/src/dart/resolution/type_literal_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/type_literal_test.dart
@@ -41,7 +41,7 @@
typeLiteral,
findElement.importFind('package:test/a.dart').class_('C'),
'C<int>',
- expectedPrefix: findElement.import('package:test/a.dart').prefix,
+ expectedPrefix: findElement.import('package:test/a.dart').prefix?.element,
);
}
@@ -133,7 +133,7 @@
typeLiteral,
findElement.importFind('package:test/a.dart').typeAlias('CA'),
'C<int>',
- expectedPrefix: findElement.import('package:test/a.dart').prefix,
+ expectedPrefix: findElement.import('package:test/a.dart').prefix?.element,
);
}
diff --git a/pkg/analyzer/test/src/dart/resolution/type_name_test.dart b/pkg/analyzer/test/src/dart/resolution/type_name_test.dart
index 33d81f0..5c3ff04 100644
--- a/pkg/analyzer/test/src/dart/resolution/type_name_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/type_name_test.dart
@@ -666,7 +666,7 @@
findNode.namedType('mycore.dynamic a;'),
dynamicElement,
'dynamic',
- expectedPrefix: findElement.import('dart:core').prefix,
+ expectedPrefix: findElement.import('dart:core').prefix?.element,
);
}
diff --git a/pkg/analyzer/test/src/diagnostics/implements_deferred_class_test.dart b/pkg/analyzer/test/src/diagnostics/implements_deferred_class_test.dart
index d9e36f7..ac8fe41 100644
--- a/pkg/analyzer/test/src/diagnostics/implements_deferred_class_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/implements_deferred_class_test.dart
@@ -75,6 +75,6 @@
var typeRef = findNode.namedType('Random {}');
assertNamedType(typeRef, randomElement, 'Random',
- expectedPrefix: mathImport.prefix);
+ expectedPrefix: mathImport.prefix?.element);
}
}
diff --git a/pkg/analyzer/test/src/diagnostics/mixin_super_class_constraint_deferred_class_test.dart b/pkg/analyzer/test/src/diagnostics/mixin_super_class_constraint_deferred_class_test.dart
index 4985163..21d8902 100644
--- a/pkg/analyzer/test/src/diagnostics/mixin_super_class_constraint_deferred_class_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/mixin_super_class_constraint_deferred_class_test.dart
@@ -32,6 +32,6 @@
var typeRef = findNode.namedType('Random {}');
assertNamedType(typeRef, randomElement, 'Random',
- expectedPrefix: mathImport.prefix);
+ expectedPrefix: mathImport.prefix?.element);
}
}
diff --git a/pkg/analyzer/test/src/diagnostics/use_of_nullable_value_test.dart b/pkg/analyzer/test/src/diagnostics/use_of_nullable_value_test.dart
index b6cbd94..b4cce46 100644
--- a/pkg/analyzer/test/src/diagnostics/use_of_nullable_value_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/use_of_nullable_value_test.dart
@@ -407,8 +407,7 @@
}
''', [
error(CompileTimeErrorCode.UNCHECKED_PROPERTY_ACCESS_OF_NULLABLE_VALUE,
- 104, 1,
- contextMessages: [message('/home/test/lib/test.dart', 56, 1)]),
+ 104, 1),
]);
if (isNullSafetyEnabled) {
@@ -660,8 +659,7 @@
}
''', [
error(CompileTimeErrorCode.UNCHECKED_PROPERTY_ACCESS_OF_NULLABLE_VALUE,
- 105, 1,
- contextMessages: [message('/home/test/lib/test.dart', 56, 1)]),
+ 105, 1),
]);
assertResolvedNodeText(findNode.assignment('x += 1'), r'''
@@ -1594,8 +1592,7 @@
}
''', [
error(CompileTimeErrorCode.UNCHECKED_PROPERTY_ACCESS_OF_NULLABLE_VALUE,
- 105, 1,
- contextMessages: [message('/home/test/lib/test.dart', 56, 1)]),
+ 105, 1),
]);
var propertyAccess1 = findNode.propertyAccess('b.a?.x; // 1');
var propertyAccess2 = findNode.propertyAccess('b.a.x; // 2');
@@ -1664,8 +1661,7 @@
}
''', [
error(CompileTimeErrorCode.UNCHECKED_PROPERTY_ACCESS_OF_NULLABLE_VALUE,
- 148, 1,
- contextMessages: [message('/home/test/lib/test.dart', 56, 1)]),
+ 148, 1),
]);
var propertyAccess1 = findNode.propertyAccess('x; // 1');
var propertyAccess2 = findNode.propertyAccess('x; // 2');
@@ -1702,8 +1698,7 @@
}
''', [
error(CompileTimeErrorCode.UNCHECKED_PROPERTY_ACCESS_OF_NULLABLE_VALUE,
- 152, 1,
- contextMessages: [message('/home/test/lib/test.dart', 101, 1)]),
+ 152, 1),
]);
var propertyAccess1 = findNode.propertyAccess('x; // 1');
var propertyAccess2 = findNode.propertyAccess('x; // 2');
diff --git a/pkg/analyzer/test/src/summary/element_text.dart b/pkg/analyzer/test/src/summary/element_text.dart
index 2c955c4..1177e37 100644
--- a/pkg/analyzer/test/src/summary/element_text.dart
+++ b/pkg/analyzer/test/src/summary/element_text.dart
@@ -52,6 +52,7 @@
bool withDisplayName = false,
bool withExportScope = false,
bool withNonSynthetic = false,
+ bool withSyntheticDartCoreImport = false,
}) {
var writer = _ElementWriter(
selfUriStr: '${library.source.uri}',
@@ -59,6 +60,7 @@
withDisplayName: withDisplayName,
withExportScope: withExportScope,
withNonSynthetic: withNonSynthetic,
+ withSyntheticDartCoreImport: withSyntheticDartCoreImport,
);
writer.writeLibraryElement(library);
@@ -124,6 +126,7 @@
final bool withDisplayName;
final bool withExportScope;
final bool withNonSynthetic;
+ final bool withSyntheticDartCoreImport;
final StringBuffer buffer = StringBuffer();
String indent = '';
@@ -134,6 +137,7 @@
required this.withDisplayName,
required this.withExportScope,
required this.withNonSynthetic,
+ required this.withSyntheticDartCoreImport,
});
void writeLibraryElement(LibraryElement e) {
@@ -154,7 +158,9 @@
_writeDocumentation(e);
_writeMetadata(e);
- var imports = e.imports.where((import) => !import.isSynthetic).toList();
+ var imports = e.imports2
+ .where((import) => withSyntheticDartCoreImport || !import.isSynthetic)
+ .toList();
_writeElements('imports', imports, _writeImportElement);
_writeElements('exports', e.exports, _writeExportElement);
@@ -426,16 +432,18 @@
}
void _writeDirectiveUri(DirectiveUri uri) {
- if (uri is DirectiveUriWithUnit) {
- _writelnWithIndent('${uri.unit.source.uri}');
+ if (uri is DirectiveUriWithLibraryImpl) {
+ buffer.write('${uri.library.source.uri}');
+ } else if (uri is DirectiveUriWithUnit) {
+ buffer.write('${uri.unit.source.uri}');
} else if (uri is DirectiveUriWithSource) {
- _writelnWithIndent("source '${uri.source.uri}'");
+ buffer.write("source '${uri.source.uri}'");
} else if (uri is DirectiveUriWithRelativeUri) {
- _writelnWithIndent("relativeUri '${uri.relativeUri}'");
+ buffer.write("relativeUri '${uri.relativeUri}'");
} else if (uri is DirectiveUriWithRelativeUriString) {
- _writelnWithIndent("relativeUriString '${uri.relativeUriString}'");
+ buffer.write("relativeUriString '${uri.relativeUriString}'");
} else {
- _writelnWithIndent('noRelativeUriString');
+ buffer.write('noRelativeUriString');
}
}
@@ -571,24 +579,25 @@
}
}
- void _writeImportElement(ImportElement e) {
+ void _writeImportElement(ImportElement2 e) {
_writeIndentedLine(() {
- _writeUri(e.importedLibrary?.source);
- _writeIf(e.isDeferred, ' deferred');
-
- var prefix = e.prefix;
- if (prefix != null) {
- buffer.write(' as ');
- _writeName(prefix);
- }
+ _writeDirectiveUri(e.uri);
+ _writeIf(e.isSynthetic, ' synthetic');
+ _writeImportElementPrefix(e.prefix);
});
_withIndent(() {
_writeMetadata(e);
_writeNamespaceCombinators(e.combinators);
});
+ }
- _assertNonSyntheticElementSelf(e);
+ void _writeImportElementPrefix(ImportElementPrefix? prefix) {
+ if (prefix != null) {
+ _writeIf(prefix is DeferredImportElementPrefix, ' deferred');
+ buffer.write(' as ');
+ _writeName(prefix.element);
+ }
}
void _writeIndentedLine(void Function() f) {
@@ -727,7 +736,9 @@
void _writePartElement(PartElement e) {
final uri = e.uri;
- _writeDirectiveUri(uri);
+ _writeIndentedLine(() {
+ _writeDirectiveUri(e.uri);
+ });
_withIndent(() {
_writeMetadata(e);
diff --git a/pkg/analyzer/test/src/summary/elements_test.dart b/pkg/analyzer/test/src/summary/elements_test.dart
index 0f4a224..91fbd41 100644
--- a/pkg/analyzer/test/src/summary/elements_test.dart
+++ b/pkg/analyzer/test/src/summary/elements_test.dart
@@ -22788,7 +22788,7 @@
staticType: null
element: <null>
imports
- <unresolved>
+ relativeUri 'ht:'
metadata
Annotation
atSign: @ @0
@@ -22832,8 +22832,16 @@
addSource('$testPackageLibPath/a.dart', 'library a; class C {}');
var library = await buildLibrary('import "a.dart" as a; a.C c;');
- expect(library.imports[0].prefix!.nameOffset, 19);
- expect(library.imports[0].prefix!.nameLength, 1);
+ // TODO(scheglov) Remove when removing `imports`.
+ {
+ // ignore: deprecated_member_use_from_same_package
+ final prefixElement = library.imports[0].prefix!;
+ expect(prefixElement.nameOffset, 19);
+ expect(prefixElement.nameLength, 1);
+ }
+
+ final prefixElement = library.imports2[0].prefix!.element;
+ expect(prefixElement.nameOffset, 19);
checkElementText(library, r'''
library
@@ -22860,9 +22868,9 @@
class C {}
class D extends p.C {} // Prevent "unused import" warning
''');
- expect(library.imports, hasLength(2));
- expect(library.imports[0].importedLibrary!.location, library.location);
- expect(library.imports[1].importedLibrary!.isDartCore, true);
+ expect(library.imports2, hasLength(2));
+ expect(library.imports2[0].importedLibrary!.location, library.location);
+ expect(library.imports2[1].importedLibrary!.isDartCore, true);
checkElementText(library, r'''
library
imports
@@ -22920,7 +22928,7 @@
var library = await buildLibrary('''
import "dart:math" show e, pi;
''');
- var import = library.imports[0];
+ var import = library.imports2[0];
var combinator = import.combinators[0] as ShowElementCombinator;
expect(combinator.offset, 19);
expect(combinator.end, 29);
@@ -22930,7 +22938,9 @@
var library = await buildLibrary('''
import 'foo.dart';
''');
- expect(library.imports[0].uri, 'foo.dart');
+
+ final uri = library.imports2[0].uri as DirectiveUriWithLibrary;
+ expect(uri.relativeUriString, 'foo.dart');
}
test_imports() async {
@@ -25366,11 +25376,11 @@
checkElementText(library, r'''
library
imports
- <unresolved>
- <unresolved>
+ relativeUriString ':[invaliduri]'
+ relativeUriString ':[invaliduri]:foo.dart'
package:test/a1.dart
- <unresolved>
- <unresolved>
+ relativeUriString ':[invaliduri]'
+ relativeUriString ':[invaliduri]:foo.dart'
exports
<unresolved>
<unresolved>
@@ -25424,6 +25434,121 @@
''');
}
+ test_library_imports_noRelativeUriStr() async {
+ final library = await buildLibrary(r'''
+import '${'foo'}.dart';
+''');
+ checkElementText(library, r'''
+library
+ imports
+ noRelativeUriString
+ definingUnit
+''');
+ }
+
+ test_library_imports_prefix_importedLibraries() async {
+ final library = await buildLibrary(r'''
+import 'dart:async' as p1;
+import 'dart:collection' as p2;
+import 'dart:math' as p1;
+''');
+ final p1 = library.prefixes.singleWhere((prefix) => prefix.name == 'p1');
+ final import_async = library.imports2[0];
+ final import_math = library.imports2[2];
+ expect(p1.imports2, unorderedEquals([import_async, import_math]));
+ }
+
+ test_library_imports_syntheticDartCore() async {
+ final library = await buildLibrary('');
+ checkElementText(
+ library,
+ r'''
+library
+ imports
+ dart:core synthetic
+ definingUnit
+''',
+ withSyntheticDartCoreImport: true);
+ }
+
+ test_library_imports_withRelativeUri_emptyUriSelf() async {
+ final library = await buildLibrary(r'''
+import '';
+''');
+ checkElementText(library, r'''
+library
+ imports
+ package:test/test.dart
+ definingUnit
+''');
+ }
+
+ test_library_imports_withRelativeUri_noSource() async {
+ final library = await buildLibrary(r'''
+import 'foo:bar';
+''');
+ checkElementText(library, r'''
+library
+ imports
+ relativeUri 'foo:bar'
+ definingUnit
+''');
+ }
+
+ test_library_imports_withRelativeUri_notExists() async {
+ final library = await buildLibrary(r'''
+import 'a.dart';
+''');
+ checkElementText(library, r'''
+library
+ imports
+ package:test/a.dart
+ definingUnit
+''');
+ }
+
+ test_library_imports_withRelativeUri_notLibrary_augmentation() async {
+ newFile('$testPackageLibPath/a.dart', r'''
+library augment 'test.dart';
+''');
+ final library = await buildLibrary(r'''
+import 'a.dart';
+''');
+ checkElementText(library, r'''
+library
+ imports
+ source 'package:test/a.dart'
+ definingUnit
+''');
+ }
+
+ test_library_imports_withRelativeUri_notLibrary_part() async {
+ newFile('$testPackageLibPath/a.dart', r'''
+part of other.lib;
+''');
+ final library = await buildLibrary(r'''
+import 'a.dart';
+''');
+ checkElementText(library, r'''
+library
+ imports
+ source 'package:test/a.dart'
+ definingUnit
+''');
+ }
+
+ test_library_imports_withRelativeUriString() async {
+ final library = await buildLibrary(r'''
+import ':';
+''');
+ checkElementText(library, r'''
+library
+ imports
+ relativeUriString ':'
+ definingUnit
+''');
+ }
+
test_library_name_with_spaces() async {
var library = await buildLibrary('library foo . bar ;');
checkElementText(library, r'''
@@ -25526,7 +25651,6 @@
}
test_library_parts_withRelativeUri_noSource() async {
- newFile('$testPackageLibPath/a.dart', '');
final library = await buildLibrary(r'''
part 'foo:bar';
''');
@@ -25587,6 +25711,16 @@
''');
}
+ test_library_prefixes() async {
+ final library = await buildLibrary(r'''
+import 'dart:async' as p1;
+import 'dart:collection' as p2;
+import 'dart:math' as p1;
+''');
+ final prefixNames = library.prefixes.map((e) => e.name).toList();
+ expect(prefixNames, unorderedEquals(['p1', 'p2']));
+ }
+
test_localFunctions() async {
var library = await buildLibrary(r'''
f() {
@@ -37947,7 +38081,7 @@
test_unresolved_import() async {
var library = await buildLibrary("import 'foo.dart';", allowErrors: true);
- var importedLibrary = library.imports[0].importedLibrary!;
+ var importedLibrary = library.imports2[0].importedLibrary!;
expect(importedLibrary.loadLibraryFunction, isNotNull);
expect(importedLibrary.publicNamespace, isNotNull);
expect(importedLibrary.exportNamespace, isNotNull);
diff --git a/pkg/analyzer/test/src/summary/resolved_ast_printer.dart b/pkg/analyzer/test/src/summary/resolved_ast_printer.dart
index 44ad036..c88cc6a 100644
--- a/pkg/analyzer/test/src/summary/resolved_ast_printer.dart
+++ b/pkg/analyzer/test/src/summary/resolved_ast_printer.dart
@@ -667,7 +667,7 @@
_writeln('ImportDirective');
_withIndent(() {
_writeNamedChildEntities(node);
- _writeElement('element', node.element);
+ _writeElement('element', node.element2);
_writeSource('selectedSource', node.selectedSource);
_writeRaw('selectedUriContent', node.selectedUriContent);
_writeRaw('uriContent', node.uriContent);
diff --git a/pkg/analyzer/test/util/element_type_matchers.dart b/pkg/analyzer/test/util/element_type_matchers.dart
index f286b3f..f05ab87 100644
--- a/pkg/analyzer/test/util/element_type_matchers.dart
+++ b/pkg/analyzer/test/util/element_type_matchers.dart
@@ -30,7 +30,7 @@
const isHideElementCombinator = TypeMatcher<HideElementCombinator>();
-const isImportElement = TypeMatcher<ImportElement>();
+const isImportElement = TypeMatcher<ImportElement2>();
const isLabelElement = TypeMatcher<LabelElement>();
diff --git a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
index 9ad3e64..b7bb1be 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
@@ -286,7 +286,7 @@
@override
void writeImportedName(List<Uri> uris, String name) {
assert(uris.isNotEmpty);
- var imports = <ImportElement>[];
+ var imports = <ImportElement2>[];
for (var uri in uris) {
imports.addAll(dartFileEditBuilder._getImportsForUri(uri));
}
@@ -301,7 +301,7 @@
} else {
var prefix = import.prefix;
if (prefix != null) {
- write(prefix.displayName);
+ write(prefix.element.displayName);
write('.');
}
}
@@ -1005,8 +1005,8 @@
/// Given a list of [imports] that do, or can, make the [name] visible in
/// scope, return the one that will lead to the cleanest code.
- ImportElement? _getBestImportForName(
- List<ImportElement> imports, String name) {
+ ImportElement2? _getBestImportForName(
+ List<ImportElement2> imports, String name) {
if (imports.isEmpty) {
return null;
} else if (imports.length == 1) {
@@ -1160,7 +1160,7 @@
if (import != null) {
var prefix = import.prefix;
if (prefix != null) {
- write(prefix.displayName);
+ write(prefix.element.displayName);
write('.');
}
} else {
@@ -1464,10 +1464,10 @@
return ImportLibraryElementResultImpl(null);
}
- for (var import in resolvedUnit.libraryElement.imports) {
+ for (var import in resolvedUnit.libraryElement.imports2) {
var importedLibrary = import.importedLibrary;
if (importedLibrary != null && importedLibrary.source.uri == uri) {
- return ImportLibraryElementResultImpl(import.prefix?.name);
+ return ImportLibraryElementResultImpl(import.prefix?.element.name);
}
}
@@ -1489,7 +1489,7 @@
if (resolvedUnit.libraryElement.source.uri == uri) return false;
// Existing import.
- for (var import in resolvedUnit.libraryElement.imports) {
+ for (var import in resolvedUnit.libraryElement.imports2) {
var importedLibrary = import.importedLibrary;
if (importedLibrary != null && importedLibrary.source.uri == uri) {
return true;
@@ -1752,8 +1752,8 @@
/// Return the import element used to import the given [element] into the
/// target library, or `null` if the element was not imported, such as when
/// the element is declared in the same library.
- ImportElement? _getImportElement(Element element) {
- for (var import in resolvedUnit.libraryElement.imports) {
+ ImportElement2? _getImportElement(Element element) {
+ for (var import in resolvedUnit.libraryElement.imports2) {
var definedNames = import.namespace.definedNames;
if (definedNames.containsValue(element)) {
return import;
@@ -1762,8 +1762,8 @@
return null;
}
- Iterable<ImportElement> _getImportsForUri(Uri uri) sync* {
- for (var import in resolvedUnit.libraryElement.imports) {
+ Iterable<ImportElement2> _getImportsForUri(Uri uri) sync* {
+ for (var import in resolvedUnit.libraryElement.imports2) {
var importUri = import.importedLibrary?.source.uri;
if (importUri == uri) {
yield import;
diff --git a/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart b/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart
index dada67b..23df1dd 100644
--- a/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart
+++ b/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart
@@ -26,17 +26,39 @@
unit.accept(visitor);
} else {
var node = _getNodeForRange(unit, offset, length);
- // Take the outer-most node that shares this offset/length so that we get
- // things like ConstructorName instead of SimpleIdentifier.
- // https://github.com/dart-lang/sdk/issues/46725
if (node != null) {
- node = _getOutermostNode(node);
+ node = _getNavigationTargetNode(node);
}
node?.accept(visitor);
}
return collector;
}
+/// Gets the nearest node that should be used for navigation.
+///
+/// This is usually the outermost node with the same offset as node but in some
+/// cases may be a different ancestor where required to produce the correct
+/// result.
+AstNode _getNavigationTargetNode(AstNode node) {
+ AstNode? current = node;
+ while (current != null &&
+ current.parent != null &&
+ current.offset == current.parent!.offset) {
+ current = current.parent;
+ }
+ current ??= node;
+
+ // To navigate to formal params, we need to visit the parameter and not just
+ // the identifier but they don't start at the same offset as they have a
+ // prefix.
+ final parent = current.parent;
+ if (parent is FormalParameter) {
+ current = parent;
+ }
+
+ return current;
+}
+
AstNode? _getNodeForRange(CompilationUnit unit, int offset, int length) {
var node = NodeLocator(offset, offset + length).searchWithin(unit);
for (var n = node; n != null; n = n.parent) {
@@ -47,21 +69,6 @@
return node;
}
-/// Gets the outer-most node with the same offset as node.
-///
-/// This reduces the number of nodes the visitor needs to walk when collecting
-/// navigation for a specific location in the file.
-AstNode _getOutermostNode(AstNode node) {
- AstNode? current = node;
- while (current != null &&
- current.parent != null &&
- current != current.parent &&
- current.offset == current.parent!.offset) {
- current = current.parent;
- }
- return current ?? node;
-}
-
/// A Dart specific wrapper around [NavigationCollector].
class _DartNavigationCollector {
final NavigationCollector collector;
@@ -360,7 +367,7 @@
@override
void visitImportDirective(ImportDirective node) {
- var importElement = node.element;
+ var importElement = node.element2;
if (importElement != null) {
Element? libraryElement = importElement.importedLibrary;
_addUriDirectiveRegion(node, libraryElement);
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index 5fdd14b..2c08f76 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -685,11 +685,12 @@
// Dump Info.
if (options.dumpInfo) {
- runDumpInfo(codegenResults, programSize);
+ await runDumpInfo(codegenResults, programSize);
}
}
- void runDumpInfo(CodegenResults codegenResults, int programSize) {
+ Future<void> runDumpInfo(
+ CodegenResults codegenResults, int programSize) async {
GlobalTypeInferenceResults globalTypeInferenceResults =
codegenResults.globalTypeInferenceResults;
JClosedWorld closedWorld = globalTypeInferenceResults.closedWorld;
@@ -698,11 +699,13 @@
dumpInfoTask.reportSize(programSize);
if (options.features.newDumpInfo.isEnabled) {
assert(untrimmedComponentForDumpInfo != null);
- dumpInfoState = dumpInfoTask.dumpInfoNew(untrimmedComponentForDumpInfo,
- closedWorld, globalTypeInferenceResults);
+ dumpInfoState = await dumpInfoTask.dumpInfoNew(
+ untrimmedComponentForDumpInfo,
+ closedWorld,
+ globalTypeInferenceResults);
} else {
dumpInfoState =
- dumpInfoTask.dumpInfo(closedWorld, globalTypeInferenceResults);
+ await dumpInfoTask.dumpInfo(closedWorld, globalTypeInferenceResults);
}
if (retainDataForTesting) {
dumpInfoStateForTesting = dumpInfoState;
diff --git a/pkg/compiler/lib/src/dump_info.dart b/pkg/compiler/lib/src/dump_info.dart
index 18cba93..e5d7229 100644
--- a/pkg/compiler/lib/src/dump_info.dart
+++ b/pkg/compiler/lib/src/dump_info.dart
@@ -20,6 +20,7 @@
import 'common/elements.dart' show JElementEnvironment;
import 'common/names.dart';
import 'common/tasks.dart' show CompilerTask;
+import 'common/ram_usage.dart';
import 'compiler.dart' show Compiler;
import 'constants/values.dart' show ConstantValue, InterceptorConstantValue;
import 'deferred_load/output_unit.dart' show OutputUnit, deferredPartFileName;
@@ -1234,30 +1235,32 @@
return code.map((ast) => _nodeData[ast]).toList();
}
- DumpInfoStateData dumpInfo(JClosedWorld closedWorld,
- GlobalTypeInferenceResults globalInferenceResults) {
+ Future<DumpInfoStateData> dumpInfo(JClosedWorld closedWorld,
+ GlobalTypeInferenceResults globalInferenceResults) async {
DumpInfoStateData dumpInfoState;
- measure(() {
+ await measure(() async {
ElementInfoCollector elementInfoCollector = ElementInfoCollector(
compiler, this, closedWorld, globalInferenceResults)
..run();
- dumpInfoState = buildDumpInfoData(closedWorld, elementInfoCollector);
+ dumpInfoState =
+ await buildDumpInfoData(closedWorld, elementInfoCollector);
if (useBinaryFormat) {
dumpInfoBinary(dumpInfoState.info);
} else {
dumpInfoJson(dumpInfoState.info);
}
+ return;
});
return dumpInfoState;
}
- DumpInfoStateData dumpInfoNew(
+ Future<DumpInfoStateData> dumpInfoNew(
ir.Component component,
JClosedWorld closedWorld,
- GlobalTypeInferenceResults globalInferenceResults) {
+ GlobalTypeInferenceResults globalInferenceResults) async {
DumpInfoStateData dumpInfoState;
- measure(() {
+ await measure(() async {
KernelInfoCollector kernelInfoCollector =
KernelInfoCollector(component, compiler, this, closedWorld)..run();
@@ -1265,7 +1268,8 @@
globalInferenceResults)
..run();
- dumpInfoState = buildDumpInfoDataNew(closedWorld, kernelInfoCollector);
+ dumpInfoState =
+ await buildDumpInfoDataNew(closedWorld, kernelInfoCollector);
TreeShakingInfoVisitor().filter(dumpInfoState.info);
if (useBinaryFormat) {
@@ -1308,8 +1312,8 @@
});
}
- DumpInfoStateData buildDumpInfoData(
- JClosedWorld closedWorld, ElementInfoCollector infoCollector) {
+ Future<DumpInfoStateData> buildDumpInfoData(
+ JClosedWorld closedWorld, ElementInfoCollector infoCollector) async {
Stopwatch stopwatch = Stopwatch();
stopwatch.start();
@@ -1371,6 +1375,7 @@
entrypoint: infoCollector
.state.entityToInfo[closedWorld.elementEnvironment.mainFunction],
size: _programSize,
+ ramUsage: await currentHeapCapacityInMb(),
dart2jsVersion:
compiler.options.hasBuildId ? compiler.options.buildId : null,
compilationMoment: DateTime.now(),
@@ -1387,8 +1392,8 @@
return result;
}
- DumpInfoStateData buildDumpInfoDataNew(
- JClosedWorld closedWorld, KernelInfoCollector infoCollector) {
+ Future<DumpInfoStateData> buildDumpInfoDataNew(
+ JClosedWorld closedWorld, KernelInfoCollector infoCollector) async {
Stopwatch stopwatch = Stopwatch();
stopwatch.start();
@@ -1453,6 +1458,7 @@
entrypoint: infoCollector
.state.entityToInfo[closedWorld.elementEnvironment.mainFunction],
size: _programSize,
+ ramUsage: await currentHeapCapacityInMb(),
dart2jsVersion:
compiler.options.hasBuildId ? compiler.options.buildId : null,
compilationMoment: DateTime.now(),
diff --git a/pkg/compiler/lib/src/inferrer/builder.dart b/pkg/compiler/lib/src/inferrer/builder.dart
index 6eb60d8..6a4a5f2 100644
--- a/pkg/compiler/lib/src/inferrer/builder.dart
+++ b/pkg/compiler/lib/src/inferrer/builder.dart
@@ -434,10 +434,6 @@
case ir.AsyncMarker.AsyncStar:
recordReturnType(_types.asyncStarStreamType);
break;
- case ir.AsyncMarker.SyncYielding:
- failedAt(
- _analyzedMember, "Unexpected async marker: ${node.asyncMarker}");
- break;
}
assert(_breaksFor.isEmpty);
assert(_continuesFor.isEmpty);
diff --git a/pkg/compiler/lib/src/inferrer/engine.dart b/pkg/compiler/lib/src/inferrer/engine.dart
index 6cae1c6..f088516 100644
--- a/pkg/compiler/lib/src/inferrer/engine.dart
+++ b/pkg/compiler/lib/src/inferrer/engine.dart
@@ -65,6 +65,7 @@
@override
final JsClosedWorld closedWorld;
+ @override
final TypeSystem types;
final Map<ir.TreeNode, TypeInformation> concreteTypes = {};
final GlobalLocalsMap globalLocalsMap;
@@ -174,6 +175,7 @@
/// Returns the type for [nativeBehavior]. See documentation on
/// [NativeBehavior].
+ @override
TypeInformation typeOfNativeBehavior(NativeBehavior nativeBehavior) {
if (nativeBehavior == null) return types.dynamicType;
List<Object> typesReturned = nativeBehavior.typesReturned;
@@ -214,6 +216,7 @@
return returnType;
}
+ @override
void updateSelectorInMember(MemberEntity owner, CallType callType,
ir.Node node, Selector selector, AbstractValue mask) {
KernelGlobalTypeInferenceElementData data = dataOfMember(owner);
@@ -755,6 +758,7 @@
/// Update the inputs to parameters in the graph. [remove] tells whether
/// inputs must be added or removed. If [init] is false, parameters are
/// added to the work queue.
+ @override
void updateParameterInputs(TypeInformation caller, MemberEntity callee,
ArgumentsTypes arguments, Selector selector,
{bool remove, bool addToQueue = true}) {
@@ -846,6 +850,7 @@
///
/// Invariant: After graph construction, no [PlaceholderTypeInformation] nodes
/// should be present and a default type for each parameter should exist.
+ @override
TypeInformation getDefaultTypeOfParameter(Local parameter) {
return _defaultTypeOfParameter.putIfAbsent(parameter, () {
return PlaceholderTypeInformation(
@@ -1093,6 +1098,7 @@
}
/// Returns the type of [element] when being called with [selector].
+ @override
TypeInformation typeOfMemberWithSelector(
MemberEntity element, Selector selector) {
if (element.name == Identifiers.noSuchMethod_ &&
@@ -1176,6 +1182,7 @@
///
/// One category of elements that do not apply is runtime helpers that the
/// backend calls, but the optimizations don't see those calls.
+ @override
bool canFieldBeUsedForGlobalOptimizations(FieldEntity element) {
if (closedWorld.backendUsage.isFieldUsedByBackend(element)) {
return false;
@@ -1191,12 +1198,14 @@
///
/// One category of elements that do not apply is runtime helpers that the
/// backend calls, but the optimizations don't see those calls.
+ @override
bool canFunctionParametersBeUsedForGlobalOptimizations(
FunctionEntity function) {
return !closedWorld.backendUsage.isFunctionUsedByBackend(function);
}
/// Returns `true` if inference of parameter types is disabled for [member].
+ @override
bool assumeDynamic(MemberEntity member) {
return closedWorld.annotationsData.hasAssumeDynamic(member);
}
diff --git a/pkg/compiler/lib/src/inferrer/engine_interfaces.dart b/pkg/compiler/lib/src/inferrer/engine_interfaces.dart
index 80c7d3e..ce45629 100644
--- a/pkg/compiler/lib/src/inferrer/engine_interfaces.dart
+++ b/pkg/compiler/lib/src/inferrer/engine_interfaces.dart
@@ -2,22 +2,42 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
+import 'package:kernel/ast.dart' as ir;
+
import '../common/elements.dart';
import '../elements/entities.dart';
import '../js_backend/inferred_data.dart';
import '../js_backend/no_such_method_registry_interfaces.dart';
+import '../native/behavior.dart';
import '../universe/selector.dart';
import '../world_interfaces.dart';
import 'abstract_value_domain.dart';
+import 'locals_handler.dart';
+import 'type_graph_nodes.dart';
+import 'type_system.dart';
abstract class InferrerEngine {
AbstractValueDomain get abstractValueDomain;
+ TypeSystem get types;
JClosedWorld get closedWorld;
CommonElements get commonElements;
InferredDataBuilder get inferredDataBuilder;
FunctionEntity get mainElement;
NoSuchMethodData get noSuchMethodData;
+ TypeInformation typeOfNativeBehavior(NativeBehavior nativeBehavior);
+ bool canFieldBeUsedForGlobalOptimizations(FieldEntity element);
+ bool assumeDynamic(MemberEntity member);
+ TypeInformation getDefaultTypeOfParameter(Local parameter);
+ bool canFunctionParametersBeUsedForGlobalOptimizations(
+ FunctionEntity function);
+ TypeInformation typeOfMemberWithSelector(
+ MemberEntity element, Selector? selector);
+ void updateSelectorInMember(MemberEntity owner, CallType callType,
+ ir.Node? node, Selector? selector, AbstractValue? mask);
+ void updateParameterInputs(TypeInformation caller, MemberEntity callee,
+ ArgumentsTypes? arguments, Selector? selector,
+ {required bool remove, bool addToQueue = true});
bool returnsListElementType(Selector selector, AbstractValue mask);
bool returnsMapValueType(Selector selector, AbstractValue mask);
}
diff --git a/pkg/compiler/lib/src/inferrer/locals_handler.dart b/pkg/compiler/lib/src/inferrer/locals_handler.dart
index 69f77f5..a9c0265 100644
--- a/pkg/compiler/lib/src/inferrer/locals_handler.dart
+++ b/pkg/compiler/lib/src/inferrer/locals_handler.dart
@@ -2,8 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-// @dart = 2.10
-
library locals_handler;
import 'dart:collection' show IterableMixin;
@@ -11,7 +9,7 @@
import '../elements/entities.dart';
import '../ir/util.dart';
import '../util/util.dart';
-import 'engine.dart';
+import 'engine_interfaces.dart';
import 'type_graph_nodes.dart';
/// A variable scope holds types for variables. It has a link to a
@@ -25,15 +23,15 @@
/// This is used for computing common parents efficiently.
final int _level;
- Map<Local, TypeInformation> variables;
+ Map<Local, TypeInformation>? variables;
/// The parent of this scope. Null for the root scope.
- final VariableScope parent;
+ final VariableScope? parent;
/// The [ir.Node] that created this scope.
- final ir.Node tryBlock;
+ final ir.Node? tryBlock;
- final VariableScope copyOf;
+ final VariableScope? copyOf;
VariableScope({this.parent})
: this.variables = null,
@@ -52,13 +50,13 @@
VariableScope.deepCopyOf(VariableScope other)
: variables = other.variables == null
? null
- : Map<Local, TypeInformation>.from(other.variables),
+ : Map<Local, TypeInformation>.from(other.variables!),
tryBlock = other.tryBlock,
copyOf = other.copyOf ?? other,
_level = other._level,
parent = other.parent == null
? null
- : VariableScope.deepCopyOf(other.parent);
+ : VariableScope.deepCopyOf(other.parent!);
/// `true` if this scope is for a try block.
bool get isTry => tryBlock != null;
@@ -70,50 +68,47 @@
VariableScope get identity => copyOf ?? this;
/// Returns the common parent between this and [other] based on [identity].
- VariableScope commonParent(VariableScope other) {
+ VariableScope? commonParent(VariableScope other) {
if (identity == other.identity) {
return identity;
} else if (_level > other._level) {
- return parent.commonParent(other);
+ return parent!.commonParent(other);
} else if (_level < other._level) {
- return commonParent(other.parent);
+ return commonParent(other.parent!);
} else if (_level > 0) {
- return parent.commonParent(other.parent);
+ return parent!.commonParent(other.parent!);
} else {
return null;
}
}
- TypeInformation operator [](Local variable) {
- TypeInformation result;
- if (variables == null || (result = variables[variable]) == null) {
- return parent == null ? null : parent[variable];
+ TypeInformation? operator [](Local variable) {
+ TypeInformation? result;
+ if (variables == null || (result = variables![variable]) == null) {
+ return parent == null ? null : parent![variable];
}
return result;
}
void operator []=(Local variable, TypeInformation mask) {
- assert(mask != null);
- if (variables == null) {
- variables = Map<Local, TypeInformation>();
- }
- variables[variable] = mask;
+ assert((mask as dynamic) != null); // TODO(48820): Remove when sound.
+ (variables ??= <Local, TypeInformation>{})[variable] = mask;
}
/// 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)) {
+ VariableScope? scope, void f(Local variable, TypeInformation type)) {
_forEachLocalUntilScope(scope, f, Setlet<Local>(), this);
}
void _forEachLocalUntilScope(
- VariableScope scope,
+ VariableScope? scope,
void f(Local variable, TypeInformation type),
Setlet<Local> seenLocals,
VariableScope origin) {
if (variables != null) {
- variables.forEach((variable, type) {
+ variables!.forEach((variable, type) {
if (seenLocals.contains(variable)) return;
seenLocals.add(variable);
f(variable, type);
@@ -123,7 +118,7 @@
return;
}
if (parent != null) {
- parent._forEachLocalUntilScope(scope, f, seenLocals, origin);
+ parent!._forEachLocalUntilScope(scope, f, seenLocals, origin);
} else {
assert(
scope == null,
@@ -139,7 +134,7 @@
bool updates(Local variable) {
if (variables == null) return false;
- return variables.containsKey(variable);
+ return variables!.containsKey(variable);
}
String toStructuredText(String indent) {
@@ -155,18 +150,18 @@
sb.write('\n${indent} copyOf:VariableScope(${copyOf.hashCode})');
}
if (tryBlock != null) {
- sb.write('\n${indent} tryBlock: ${nodeToDebugString(tryBlock)}');
+ sb.write('\n${indent} tryBlock: ${nodeToDebugString(tryBlock!)}');
}
if (variables != null) {
sb.write('\n${indent} variables:');
- variables.forEach((Local local, TypeInformation type) {
+ variables!.forEach((Local local, TypeInformation type) {
sb.write('\n${indent} $local: ');
sb.write(type.toStructuredText('${indent} '));
});
}
if (parent != null) {
sb.write('\n${indent} parent:');
- parent._toStructuredText(sb, '${indent} ');
+ parent!._toStructuredText(sb, '${indent} ');
}
sb.write(']');
}
@@ -180,7 +175,7 @@
/// Tracks initializers via initializations and assignments.
class FieldInitializationScope {
- Map<FieldEntity, TypeInformation> fields;
+ Map<FieldEntity, TypeInformation>? fields;
bool isThisExposed;
/// `true` when control flow prevents accumulating definite assignments,
@@ -195,7 +190,7 @@
: isThisExposed = other.isThisExposed,
isIndefinite = other.isIndefinite;
- factory FieldInitializationScope.from(FieldInitializationScope other) {
+ static FieldInitializationScope? from(FieldInitializationScope? other) {
if (other == null) return null;
return FieldInitializationScope.internalFrom(other);
}
@@ -203,12 +198,11 @@
void updateField(FieldEntity field, TypeInformation type) {
if (isThisExposed) return;
if (isIndefinite) return;
- fields ??= Map<FieldEntity, TypeInformation>();
- fields[field] = type;
+ (fields ??= <FieldEntity, TypeInformation>{})[field] = type;
}
- TypeInformation readField(FieldEntity field) {
- return fields == null ? null : fields[field];
+ TypeInformation? readField(FieldEntity field) {
+ return fields == null ? null : fields![field];
}
void forEach(void f(FieldEntity element, TypeInformation type)) {
@@ -219,7 +213,7 @@
/// flow through either [thenScope] or [elseScope].
FieldInitializationScope mergeDiamondFlow(InferrerEngine inferrer,
FieldInitializationScope thenScope, FieldInitializationScope elseScope) {
- assert(elseScope != null);
+ assert((elseScope as dynamic) != null); // TODO(48820): Remove when sound.
// Quick bailout check. If [isThisExposed] or [isIndefinite] is true, we
// know the code following won't do anything.
@@ -230,7 +224,7 @@
elseScope.fields == null ? this : elseScope;
thenScope.forEach((FieldEntity field, TypeInformation type) {
- TypeInformation otherType = otherScope.readField(field);
+ final otherType = otherScope.readField(field);
if (otherType == null) return;
updateField(field, inferrer.types.allocateDiamondPhi(type, otherType));
});
@@ -246,10 +240,10 @@
final List<TypeInformation> positional;
final Map<String, TypeInformation> named;
- ArgumentsTypes(this.positional, Map<String, TypeInformation> named)
+ ArgumentsTypes(this.positional, Map<String, TypeInformation>? named)
: this.named = (named == null || named.isEmpty) ? const {} : named {
- assert(this.positional.every((TypeInformation type) => type != null));
- assert(this.named.values.every((TypeInformation type) => type != null));
+ assert(this.positional.every((TypeInformation? type) => type != null));
+ assert(this.named.values.every((TypeInformation? type) => type != null));
}
ArgumentsTypes.empty()
@@ -266,7 +260,8 @@
String toString() => "{ positional = $positional, named = $named }";
@override
- bool operator ==(other) {
+ bool operator ==(Object? other) {
+ if (other is! ArgumentsTypes) return false;
if (positional.length != other.positional.length) return false;
if (named.length != other.named.length) return false;
for (int i = 0; i < positional.length; i++) {
@@ -296,7 +291,7 @@
}
@override
- bool contains(Object type) {
+ bool contains(Object? type) {
return positional.contains(type) || named.containsValue(type);
}
}
@@ -341,26 +336,25 @@
LocalsHandler.deepCopyOf(LocalsHandler other)
: _locals = VariableScope.deepCopyOf(other._locals);
- TypeInformation use(Local local) {
+ TypeInformation? use(Local local) {
return _locals[local];
}
void update(InferrerEngine inferrer, Local local, TypeInformation type,
- LocalsHandler tryBlock) {
+ 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];
+ final existing = tryBlock._locals.parent![local];
if (existing != null) {
- TypeInformation phiType = inferrer.types.allocatePhi(
+ final phiType = inferrer.types.allocatePhi(
tryBlock._locals.tryBlock, local, existing,
isTry: tryBlock._locals.isTry);
- TypeInformation inputType =
- inferrer.types.addPhiInput(local, phiType, type);
- tryBlock._locals.parent[local] = inputType;
+ final inputType = inferrer.types.addPhiInput(local, phiType, type);
+ tryBlock._locals.parent![local] = inputType;
}
// Update the current handler unconditionally with the new
// type.
@@ -378,7 +372,7 @@
/// from both are merged with a phi type.
LocalsHandler mergeFlow(InferrerEngine inferrer, LocalsHandler other,
{bool inPlace = false}) {
- VariableScope common = _locals.commonParent(other._locals);
+ final common = _locals.commonParent(other._locals);
assert(
common != null,
"No common parent for\n"
@@ -387,11 +381,11 @@
assert(
common == _locals || _locals.variables == null,
"Non-empty common parent for\n"
- "1:${common.toStructuredText(' ')}\n"
+ "1:${common?.toStructuredText(' ')}\n"
"2:${_locals.toStructuredText(' ')}");
other._locals.forEachLocalUntilScope(common,
(Local local, TypeInformation type) {
- TypeInformation myType = _locals[local];
+ final myType = _locals[local];
if (myType == null) return; // Variable is only defined in [other].
if (type == myType) return;
_locals[local] =
@@ -404,13 +398,13 @@
/// flow through either [thenBranch] or [elseBranch].
LocalsHandler mergeDiamondFlow(InferrerEngine inferrer,
LocalsHandler thenBranch, LocalsHandler elseBranch) {
- assert(elseBranch != null);
+ assert((elseBranch as dynamic) != null); // TODO(48820): Remove when sound.
void mergeLocal(Local local) {
- TypeInformation myType = _locals[local];
+ final myType = _locals[local];
if (myType == null) return;
- TypeInformation elseType = elseBranch._locals[local];
- TypeInformation thenType = thenBranch._locals[local];
+ final elseType = elseBranch._locals[local]!;
+ final thenType = thenBranch._locals[local]!;
if (thenType == elseType) {
_locals[local] = thenType;
} else {
@@ -418,7 +412,7 @@
}
}
- VariableScope common = _locals.commonParent(thenBranch._locals);
+ final common = _locals.commonParent(thenBranch._locals);
assert(
common != null,
"No common parent for\n"
@@ -427,12 +421,12 @@
assert(
_locals.commonParent(elseBranch._locals) == common,
"Diff common parent for\n"
- "1:${common.toStructuredText(' ')}\n2:"
+ "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"
+ "common:${common?.toStructuredText(' ')}\n"
"1:${_locals.toStructuredText(' ')}\n"
"2:${thenBranch._locals.toStructuredText(' ')}");
thenBranch._locals.forEachLocalUntilScope(common, (Local local, _) {
@@ -477,7 +471,7 @@
LocalsHandler mergeAfterBreaks(
InferrerEngine inferrer, Iterable<LocalsHandler> handlers,
{bool keepOwnLocals = true}) {
- ir.Node tryBlock = _locals.tryBlock;
+ final 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.
@@ -487,7 +481,7 @@
Set<Local> seenLocals = Setlet<Local>();
// Merge all other handlers.
for (LocalsHandler handler in handlers) {
- VariableScope common = _locals.commonParent(handler._locals);
+ final common = _locals.commonParent(handler._locals);
assert(
common != null,
"No common parent for\n"
@@ -496,11 +490,11 @@
assert(
common == _locals || _locals.variables == null,
"Non-empty common parent for\n"
- "common:${common.toStructuredText(' ')}\n"
+ "common:${common?.toStructuredText(' ')}\n"
"1:${_locals.toStructuredText(' ')}\n"
"2:${handler._locals.toStructuredText(' ')}");
handler._locals.forEachLocalUntilScope(common, (local, otherType) {
- TypeInformation myType = merged[local];
+ final myType = merged[local];
if (myType == null) return;
TypeInformation newType;
if (!seenLocals.contains(local)) {
@@ -509,7 +503,8 @@
isTry: merged.isTry);
seenLocals.add(local);
} else {
- newType = inferrer.types.addPhiInput(local, myType, otherType);
+ newType = inferrer.types.addPhiInput(
+ local, myType as PhiElementTypeInformation, otherType);
}
if (newType != myType) {
merged[local] = newType;
@@ -520,10 +515,10 @@
// [merged] to update the Phi nodes with original values.
if (keepOwnLocals) {
for (Local variable in seenLocals) {
- TypeInformation originalType = _locals[variable];
+ final originalType = _locals[variable];
if (originalType != null) {
- merged[variable] = inferrer.types
- .addPhiInput(variable, merged[variable], originalType);
+ merged[variable] = inferrer.types.addPhiInput(variable,
+ merged[variable] as PhiElementTypeInformation, originalType);
}
}
}
@@ -531,7 +526,8 @@
// actual locals handler.
merged.forEachLocalUntilScope(merged,
(Local variable, TypeInformation type) {
- _locals[variable] = inferrer.types.simplifyPhi(tryBlock, variable, type);
+ _locals[variable] = inferrer.types
+ .simplifyPhi(tryBlock, variable, type as PhiElementTypeInformation);
});
return this;
}
@@ -541,7 +537,7 @@
bool mergeAll(InferrerEngine inferrer, Iterable<LocalsHandler> handlers) {
bool changed = false;
handlers.forEach((LocalsHandler other) {
- VariableScope common = _locals.commonParent(other._locals);
+ final common = _locals.commonParent(other._locals);
assert(
common != null,
"No common parent for\n"
@@ -550,14 +546,14 @@
assert(
common == _locals || _locals.variables == null,
"Non-empty common parent for\n"
- "common:${common.toStructuredText(' ')}\n"
+ "common:${common?.toStructuredText(' ')}\n"
"1:${_locals.toStructuredText(' ')}\n"
"2:${other._locals.toStructuredText(' ')}");
other._locals.forEachLocalUntilScope(common, (local, otherType) {
- TypeInformation myType = _locals[local];
+ final myType = _locals[local];
if (myType == null) return;
- TypeInformation newType =
- inferrer.types.addPhiInput(local, myType, otherType);
+ TypeInformation newType = inferrer.types
+ .addPhiInput(local, myType as PhiElementTypeInformation, otherType);
if (newType != myType) {
changed = true;
_locals[local] = newType;
@@ -579,8 +575,8 @@
void endLoop(InferrerEngine inferrer, ir.Node loop) {
_locals.forEachLocal((Local variable, TypeInformation type) {
- TypeInformation newType =
- inferrer.types.simplifyPhi(loop, variable, type);
+ final newType = inferrer.types
+ .simplifyPhi(loop, variable, type as PhiElementTypeInformation);
if (newType != type) {
_locals[variable] = newType;
}
diff --git a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart
index 8f28c04..6a698dc 100644
--- a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart
+++ b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart
@@ -2,8 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-// @dart = 2.10
-
library compiler.src.inferrer.type_graph_nodes;
import 'dart:collection' show IterableBase;
@@ -16,11 +14,11 @@
import '../elements/types.dart';
import '../universe/selector.dart' show Selector;
import '../util/util.dart' show Setlet;
-import '../world.dart' show JClosedWorld;
+import '../world_interfaces.dart' show JClosedWorld;
import 'abstract_value_domain.dart';
import 'debug.dart' as debug;
import 'locals_handler.dart' show ArgumentsTypes;
-import 'engine.dart';
+import 'engine_interfaces.dart';
import 'type_system.dart';
/// Common class for all nodes in the graph. The current nodes are:
@@ -46,10 +44,10 @@
AbstractValue type;
/// The graph node of the member this [TypeInformation] node belongs to.
- final MemberTypeInformation context;
+ final MemberTypeInformation? context;
/// The element this [TypeInformation] node belongs to.
- MemberEntity get contextMember => context?.member;
+ MemberEntity? get contextMember => context?.member;
ParameterInputs get inputs => _inputs;
@@ -189,7 +187,7 @@
/// The [Element] where this [TypeInformation] was created. May be `null`
/// for some [TypeInformation] nodes, where we do not need to store
/// the information.
- MemberEntity get owner => (context != null) ? context.member : null;
+ MemberEntity? get owner => (context != null) ? context?.member : null;
/// Returns whether the type cannot change after it has been
/// inferred.
@@ -341,10 +339,11 @@
@override
Iterator<TypeInformation> get iterator => _inputs.keys.iterator;
@override
- Iterable<TypeInformation> where(Function f) => _inputs.keys.where(f);
+ Iterable<TypeInformation> where(bool Function(TypeInformation) f) =>
+ _inputs.keys.where(f);
@override
- bool contains(Object info) => _inputs.containsKey(info);
+ bool contains(Object? info) => _inputs.containsKey(info);
@override
String toString() => _inputs.keys.toList().toString();
@@ -380,10 +379,10 @@
bool disableInferenceForClosures = true;
ElementTypeInformation._internal(
- AbstractValueDomain abstractValueDomain, MemberTypeInformation context)
+ AbstractValueDomain abstractValueDomain, MemberTypeInformation? context)
: super(abstractValueDomain.emptyType, context);
ElementTypeInformation._withInputs(AbstractValueDomain abstractValueDomain,
- MemberTypeInformation context, ParameterInputs inputs)
+ MemberTypeInformation? context, ParameterInputs inputs)
: super.withInputs(abstractValueDomain.emptyType, context, inputs);
String getInferredSignature(TypeSystem types);
@@ -410,7 +409,7 @@
// Strict `bool` value is computed in cleanup(). Also used as a flag to see if
// cleanup has been called.
- bool _isCalledOnce = null;
+ bool? _isCalledOnce = null;
/// Whether this member is invoked via indirect dynamic calls. In that case
/// the exact number of call sites cannot be computed precisely.
@@ -423,7 +422,7 @@
///
/// The global information is summarized in [cleanup], after which [_callers]
/// is set to `null`.
- Map<MemberEntity, Setlet<ir.Node /*?*/ >> _callers;
+ Map<MemberEntity, Setlet<ir.Node?>>? _callers;
MemberTypeInformation._internal(
AbstractValueDomain abstractValueDomain, this._member)
@@ -434,22 +433,24 @@
@override
String get debugName => '$member';
- void addCall(MemberEntity caller, ir.Node /*?*/ node) {
- _callers ??= <MemberEntity, Setlet<ir.Node /*?*/ >>{};
- _callers.putIfAbsent(caller, () => Setlet()).add(node);
+ void addCall(MemberEntity caller, ir.Node? node) {
+ (_callers ??= <MemberEntity, Setlet<ir.Node?>>{})
+ .putIfAbsent(caller, () => Setlet())
+ .add(node);
}
- void removeCall(MemberEntity caller, ir.Node /*?*/ node) {
- if (_callers == null) return;
- Setlet calls = _callers[caller];
+ void removeCall(MemberEntity caller, Object node) {
+ final callers = _callers;
+ if (callers == null) return;
+ final calls = callers[caller];
if (calls == null) return;
calls.remove(node);
if (calls.isEmpty) {
- _callers.remove(caller);
+ callers.remove(caller);
}
}
- Iterable<MemberEntity> get callersForTesting {
+ Iterable<MemberEntity>? get callersForTesting {
return _callers?.keys;
}
@@ -464,10 +465,11 @@
}
bool _computeIsCalledOnce() {
+ final callers = _callers;
if (_calledIndirectly) return false;
- if (_callers == null) return false;
+ if (callers == null) return false;
int count = 0;
- for (var set in _callers.values) {
+ for (var set in callers.values) {
count += set.length;
if (count > 1) return false;
}
@@ -488,9 +490,9 @@
@override
bool get isStable => super.isStable && !isClosurized;
- AbstractValue handleSpecialCases(InferrerEngine inferrer);
+ AbstractValue? handleSpecialCases(InferrerEngine inferrer);
- AbstractValue _handleFunctionCase(
+ AbstractValue? _handleFunctionCase(
FunctionEntity function, InferrerEngine inferrer) {
if (inferrer.closedWorld.nativeData.isNativeMember(function)) {
// Use the type annotation as the type for native elements. We
@@ -527,7 +529,7 @@
@override
AbstractValue computeType(InferrerEngine inferrer) {
- AbstractValue special = handleSpecialCases(inferrer);
+ final special = handleSpecialCases(inferrer);
if (special != null) return potentiallyNarrowType(special, inferrer);
return potentiallyNarrowType(
inferrer.types.computeTypeMask(inputs), inferrer);
@@ -554,36 +556,37 @@
@override
String getInferredSignature(TypeSystem types) {
- return types.getInferredSignatureOfMethod(_member);
+ return types.getInferredSignatureOfMethod(_member as FunctionEntity);
}
}
class FieldTypeInformation extends MemberTypeInformation {
- FieldEntity get _field => _member;
+ @override
+ final FieldEntity _member;
final DartType _type;
FieldTypeInformation(
- AbstractValueDomain abstractValueDomain, FieldEntity element, this._type)
- : super._internal(abstractValueDomain, element);
+ AbstractValueDomain abstractValueDomain, this._member, this._type)
+ : super._internal(abstractValueDomain, _member);
@override
- AbstractValue handleSpecialCases(InferrerEngine inferrer) {
- if (!inferrer.canFieldBeUsedForGlobalOptimizations(_field) ||
- inferrer.assumeDynamic(_field)) {
+ AbstractValue? handleSpecialCases(InferrerEngine inferrer) {
+ if (!inferrer.canFieldBeUsedForGlobalOptimizations(_member) ||
+ inferrer.assumeDynamic(_member)) {
// Do not infer types for fields that have a corresponding annotation or
// are assigned by synthesized calls
giveUp(inferrer);
return safeType(inferrer);
}
- if (inferrer.closedWorld.nativeData.isNativeMember(_field)) {
+ if (inferrer.closedWorld.nativeData.isNativeMember(_member)) {
// Use the type annotation as the type for native elements. We
// also give up on inferring to make sure this element never
// goes in the work queue.
giveUp(inferrer);
return inferrer
.typeOfNativeBehavior(inferrer.closedWorld.nativeData
- .getNativeFieldLoadBehavior(_field))
+ .getNativeFieldLoadBehavior(_member))
.type;
}
return null;
@@ -599,7 +602,7 @@
bool hasStableType(InferrerEngine inferrer) {
// The number of inputs of non-final fields is
// not stable. Therefore such a field cannot be stable.
- if (!_field.isAssignable) {
+ if (!_member.isAssignable) {
return false;
}
return super.hasStableType(inferrer);
@@ -607,16 +610,17 @@
}
class GetterTypeInformation extends MemberTypeInformation {
- FunctionEntity get _getter => _member;
+ @override
+ final FunctionEntity _member;
final FunctionType _type;
- GetterTypeInformation(AbstractValueDomain abstractValueDomain,
- FunctionEntity element, this._type)
- : super._internal(abstractValueDomain, element);
+ GetterTypeInformation(
+ AbstractValueDomain abstractValueDomain, this._member, this._type)
+ : super._internal(abstractValueDomain, _member);
@override
- AbstractValue handleSpecialCases(InferrerEngine inferrer) {
- return _handleFunctionCase(_getter, inferrer);
+ AbstractValue? handleSpecialCases(InferrerEngine inferrer) {
+ return _handleFunctionCase(_member, inferrer);
}
@override
@@ -627,15 +631,15 @@
}
class SetterTypeInformation extends MemberTypeInformation {
- FunctionEntity get _setter => _member;
+ @override
+ final FunctionEntity _member;
- SetterTypeInformation(
- AbstractValueDomain abstractValueDomain, FunctionEntity element)
- : super._internal(abstractValueDomain, element);
+ SetterTypeInformation(AbstractValueDomain abstractValueDomain, this._member)
+ : super._internal(abstractValueDomain, _member);
@override
- AbstractValue handleSpecialCases(InferrerEngine inferrer) {
- return _handleFunctionCase(_setter, inferrer);
+ AbstractValue? handleSpecialCases(InferrerEngine inferrer) {
+ return _handleFunctionCase(_member, inferrer);
}
@override
@@ -646,22 +650,23 @@
}
class MethodTypeInformation extends MemberTypeInformation {
- FunctionEntity get _method => _member;
+ @override
+ final FunctionEntity _member;
final FunctionType _type;
- MethodTypeInformation(AbstractValueDomain abstractValueDomain,
- FunctionEntity element, this._type)
- : super._internal(abstractValueDomain, element);
+ MethodTypeInformation(
+ AbstractValueDomain abstractValueDomain, this._member, this._type)
+ : super._internal(abstractValueDomain, _member);
@override
- AbstractValue handleSpecialCases(InferrerEngine inferrer) {
- return _handleFunctionCase(_method, inferrer);
+ AbstractValue? handleSpecialCases(InferrerEngine inferrer) {
+ return _handleFunctionCase(_member, inferrer);
}
@override
AbstractValue _potentiallyNarrowType(
AbstractValue mask, InferrerEngine inferrer) {
- if (inferrer.commonElements.isLateReadCheck(_method)) {
+ if (inferrer.commonElements.isLateReadCheck(_member)) {
mask = inferrer.abstractValueDomain.excludeLateSentinel(mask);
}
return _narrowType(inferrer.closedWorld, mask, _type.returnType);
@@ -672,31 +677,31 @@
}
class FactoryConstructorTypeInformation extends MemberTypeInformation {
- ConstructorEntity get _constructor => _member;
+ @override
+ final ConstructorEntity _member;
final FunctionType _type;
- FactoryConstructorTypeInformation(AbstractValueDomain abstractValueDomain,
- ConstructorEntity element, this._type)
- : super._internal(abstractValueDomain, element);
+ FactoryConstructorTypeInformation(
+ AbstractValueDomain abstractValueDomain, this._member, this._type)
+ : super._internal(abstractValueDomain, _member);
@override
- AbstractValue handleSpecialCases(InferrerEngine inferrer) {
+ AbstractValue? handleSpecialCases(InferrerEngine inferrer) {
AbstractValueDomain abstractValueDomain = inferrer.abstractValueDomain;
- if (_constructor.isFromEnvironmentConstructor) {
- if (_constructor.enclosingClass == inferrer.commonElements.intClass) {
+ if (_member.isFromEnvironmentConstructor) {
+ if (_member.enclosingClass == inferrer.commonElements.intClass) {
giveUp(inferrer);
return abstractValueDomain.includeNull(abstractValueDomain.intType);
- } else if (_constructor.enclosingClass ==
- inferrer.commonElements.boolClass) {
+ } else if (_member.enclosingClass == inferrer.commonElements.boolClass) {
giveUp(inferrer);
return abstractValueDomain.includeNull(abstractValueDomain.boolType);
- } else if (_constructor.enclosingClass ==
+ } else if (_member.enclosingClass ==
inferrer.commonElements.stringClass) {
giveUp(inferrer);
return abstractValueDomain.includeNull(abstractValueDomain.stringType);
}
}
- return _handleFunctionCase(_constructor, inferrer);
+ return _handleFunctionCase(_member, inferrer);
}
@override
@@ -712,15 +717,16 @@
}
class GenerativeConstructorTypeInformation extends MemberTypeInformation {
- ConstructorEntity get _constructor => _member;
+ @override
+ final FunctionEntity _member;
GenerativeConstructorTypeInformation(
- AbstractValueDomain abstractValueDomain, ConstructorEntity element)
- : super._internal(abstractValueDomain, element);
+ AbstractValueDomain abstractValueDomain, this._member)
+ : super._internal(abstractValueDomain, _member);
@override
- AbstractValue handleSpecialCases(InferrerEngine inferrer) {
- return _handleFunctionCase(_constructor, inferrer);
+ AbstractValue? handleSpecialCases(InferrerEngine inferrer) {
+ return _handleFunctionCase(_member, inferrer);
}
@override
@@ -796,13 +802,12 @@
_isTearOffClosureParameter = true;
// We have to add a flow-edge for the default value (if it exists), as we
// might not see all call-sites and thus miss the use of it.
- TypeInformation defaultType =
- inferrer.getDefaultTypeOfParameter(_parameter);
- if (defaultType != null) defaultType.addUser(this);
+ final defaultType = inferrer.getDefaultTypeOfParameter(_parameter);
+ defaultType.addUser(this);
}
// TODO(herhut): Cleanup into one conditional.
- AbstractValue handleSpecialCases(InferrerEngine inferrer) {
+ AbstractValue? handleSpecialCases(InferrerEngine inferrer) {
if (!inferrer.canFunctionParametersBeUsedForGlobalOptimizations(_method) ||
inferrer.assumeDynamic(_method)) {
// Do not infer types for parameters that have a corresponding annotation
@@ -890,7 +895,7 @@
@override
AbstractValue computeType(InferrerEngine inferrer) {
- AbstractValue special = handleSpecialCases(inferrer);
+ final special = handleSpecialCases(inferrer);
if (special != null) return special;
return potentiallyNarrowType(
inferrer.types.computeTypeMask(inputs), inferrer);
@@ -953,7 +958,7 @@
forIn,
}
-bool validCallType(CallType callType, ir.Node /*?*/ call, Selector selector) {
+bool validCallType(CallType callType, ir.Node? call, Selector selector) {
switch (callType) {
case CallType.access:
return call is ir.Node;
@@ -962,7 +967,6 @@
case CallType.forIn:
return call is ir.ForInStatement;
}
- throw StateError('Unexpected call type $callType.');
}
/// A [CallSiteTypeInformation] is a call found in the AST, or a
@@ -975,10 +979,10 @@
/// and [selector] and [receiver] fields for dynamic calls.
abstract class CallSiteTypeInformation extends TypeInformation
with ApplyableTypeInformation {
- final ir.Node /*?*/ _call;
+ final ir.Node? _call;
final MemberEntity caller;
- final Selector selector;
- final ArgumentsTypes arguments;
+ final Selector? selector;
+ final ArgumentsTypes? arguments;
final bool inLoop;
CallSiteTypeInformation(
@@ -990,7 +994,7 @@
this.arguments,
this.inLoop)
: super.noInputs(abstractValueDomain.emptyType, context) {
- assert(_call is ir.Node || (_call == null && selector.name == '=='));
+ assert(_call is ir.Node || (_call == null && selector?.name == '=='));
}
@override
@@ -1011,7 +1015,7 @@
StaticCallSiteTypeInformation(
AbstractValueDomain abstractValueDomain,
MemberTypeInformation context,
- ir.Node /*?*/ call,
+ ir.Node? call,
MemberEntity enclosing,
this.calledElement,
Selector selector,
@@ -1029,10 +1033,10 @@
@override
void addToGraph(InferrerEngine inferrer) {
MemberTypeInformation callee = _getCalledTypeInfo(inferrer);
- callee.addCall(caller, _call);
+ callee.addCall(caller, _call!);
callee.addUser(this);
if (arguments != null) {
- arguments.forEach((info) => info.addUser(this));
+ arguments!.forEach((info) => info.addUser(this));
}
inferrer.updateParameterInputs(this, calledElement, arguments, selector,
remove: false, addToQueue: false);
@@ -1072,7 +1076,7 @@
bool hasStableType(InferrerEngine inferrer) {
bool isStable = _getCalledTypeInfo(inferrer).isStable;
return isStable &&
- (arguments == null || arguments.every((info) => info.isStable)) &&
+ (arguments == null || arguments!.every((info) => info.isStable)) &&
super.hasStableType(inferrer);
}
@@ -1081,7 +1085,7 @@
ElementTypeInformation callee = _getCalledTypeInfo(inferrer);
callee.removeUser(this);
if (arguments != null) {
- arguments.forEach((info) => info.removeUser(this));
+ arguments!.forEach((info) => info.removeUser(this));
}
super.removeAndClearReferences(inferrer);
}
@@ -1107,7 +1111,7 @@
IndirectDynamicCallSiteTypeInformation(
AbstractValueDomain abstractValueDomain,
MemberTypeInformation context,
- ir.Node /*?*/ call,
+ ir.Node? call,
this.dynamicCall,
MemberEntity enclosing,
Selector selector,
@@ -1123,20 +1127,22 @@
void addToGraph(InferrerEngine inferrer) {
receiver.addUser(this);
dynamicCall.receiver.addInput(receiver);
- List<TypeInformation> positional = arguments.positional;
+ final args = arguments!;
+ List<TypeInformation> positional = args.positional;
+ final dynamicCallArgs = dynamicCall.arguments!;
for (int i = 0; i < positional.length; i++) {
positional[i].addUser(this);
- dynamicCall.arguments.positional[i].addInput(positional[i]);
+ dynamicCallArgs.positional[i].addInput(positional[i]);
}
- arguments.named.forEach((name, namedInfo) {
- dynamicCall.arguments.named[name].addInput(namedInfo);
+ args.named.forEach((name, namedInfo) {
+ dynamicCallArgs.named[name]!.addInput(namedInfo);
});
dynamicCall.addUser(this);
}
@override
AbstractValue computeType(InferrerEngine inferrer) {
- AbstractValue typeMask = _computeTypedSelector(inferrer);
+ final typeMask = _computeTypedSelector(inferrer);
inferrer.updateSelectorInMember(
caller, CallType.access, _call, selector, typeMask);
@@ -1151,7 +1157,7 @@
return result;
}
- AbstractValue _computeTypedSelector(InferrerEngine inferrer) {
+ AbstractValue? _computeTypedSelector(InferrerEngine inferrer) {
AbstractValue receiverType = receiver.type;
if (mask == receiverType) return mask;
return receiverType == inferrer.abstractValueDomain.dynamicType
@@ -1185,7 +1191,7 @@
dynamicCall.removeUser(this);
receiver.removeUser(this);
if (arguments != null) {
- arguments.forEach((info) => info.removeUser(this));
+ arguments!.forEach((info) => info.removeUser(this));
}
super.removeAndClearReferences(inferrer);
}
@@ -1197,16 +1203,16 @@
final TypeInformation receiver;
final AbstractValue mask;
final bool isConditional;
- bool _hasClosureCallTargets;
+ bool? _hasClosureCallTargets;
/// Cached concrete targets of this call.
- Iterable<MemberEntity> _concreteTargets;
+ Iterable<MemberEntity>? _concreteTargets;
DynamicCallSiteTypeInformation(
AbstractValueDomain abstractValueDomain,
MemberTypeInformation context,
this._callType,
- T call,
+ T? call,
MemberEntity enclosing,
Selector selector,
this.mask,
@@ -1223,28 +1229,29 @@
if (_callType == CallType.indirectAccess) {
callee._calledIndirectly = true;
} else {
- callee.addCall(caller, _call);
+ callee.addCall(caller, _call!);
}
}
void _removeCall(MemberTypeInformation callee) {
if (_callType != CallType.indirectAccess) {
- callee.removeCall(caller, _call);
+ callee.removeCall(caller, _call!);
}
}
@override
void addToGraph(InferrerEngine inferrer) {
- assert(receiver != null);
- AbstractValue typeMask = computeTypedSelector(inferrer);
+ assert((receiver as dynamic) != null); // TODO(48820): Remove when sound.
+ final typeMask = computeTypedSelector(inferrer);
_hasClosureCallTargets =
- inferrer.closedWorld.includesClosureCall(selector, typeMask);
- _concreteTargets = inferrer.closedWorld.locateMembers(selector, typeMask);
+ inferrer.closedWorld.includesClosureCall(selector!, typeMask);
+ final concreteTargets = _concreteTargets =
+ inferrer.closedWorld.locateMembers(selector!, typeMask);
receiver.addUser(this);
if (arguments != null) {
- arguments.forEach((info) => info.addUser(this));
+ arguments!.forEach((info) => info.addUser(this));
}
- for (MemberEntity element in _concreteTargets) {
+ for (MemberEntity element in concreteTargets) {
MemberTypeInformation callee =
inferrer.types.getInferredTypeOfMember(element);
_addCall(callee);
@@ -1255,17 +1262,17 @@
}
/// `true` if this invocation can hit a 'call' method on a closure.
- bool get hasClosureCallTargets => _hasClosureCallTargets;
+ bool get hasClosureCallTargets => _hasClosureCallTargets!;
/// All concrete targets of this invocation. If [hasClosureCallTargets] is
/// `true` the invocation can additional target an unknown set of 'call'
/// methods on closures.
- Iterable<MemberEntity> get concreteTargets => _concreteTargets;
+ Iterable<MemberEntity> get concreteTargets => _concreteTargets!;
@override
- Iterable<MemberEntity> get callees => _concreteTargets;
+ Iterable<MemberEntity> get callees => _concreteTargets!;
- AbstractValue computeTypedSelector(InferrerEngine inferrer) {
+ AbstractValue? computeTypedSelector(InferrerEngine inferrer) {
AbstractValue receiverType = receiver.type;
if (mask != receiverType) {
return receiverType == inferrer.abstractValueDomain.dynamicType
@@ -1277,11 +1284,11 @@
}
bool targetsIncludeComplexNoSuchMethod(InferrerEngine inferrer) {
- return _concreteTargets.any((MemberEntity e) {
+ return _concreteTargets!.any((MemberEntity e) {
return e.isFunction &&
e.isInstanceMember &&
e.name == Identifiers.noSuchMethod_ &&
- inferrer.noSuchMethodData.isComplex(e);
+ inferrer.noSuchMethodData.isComplex(e as FunctionEntity);
});
}
@@ -1292,31 +1299,31 @@
///
/// Returns the more precise TypeInformation, or `null` to defer to the
/// library code.
- TypeInformation handleIntrisifiedSelector(
- Selector selector, AbstractValue mask, InferrerEngine inferrer) {
+ TypeInformation? handleIntrisifiedSelector(
+ Selector selector, AbstractValue? mask, InferrerEngine inferrer) {
AbstractValueDomain abstractValueDomain = inferrer.abstractValueDomain;
if (mask == null) return null;
if (abstractValueDomain.isIntegerOrNull(mask).isPotentiallyFalse) {
return null;
}
if (!selector.isCall && !selector.isOperator) return null;
- if (!arguments.named.isEmpty) return null;
- if (arguments.positional.length > 1) return null;
+ final args = arguments!;
+ if (!args.named.isEmpty) return null;
+ if (args.positional.length > 1) return null;
- bool isInt(info) =>
+ bool isInt(TypeInformation info) =>
abstractValueDomain.isIntegerOrNull(info.type).isDefinitelyTrue;
- bool isEmpty(info) =>
+ bool isEmpty(TypeInformation info) =>
abstractValueDomain.isEmpty(info.type).isDefinitelyTrue;
- bool isUInt31(info) => abstractValueDomain
+ bool isUInt31(TypeInformation info) => abstractValueDomain
.isUInt31(abstractValueDomain.excludeNull(info.type))
.isDefinitelyTrue;
- bool isPositiveInt(info) =>
+ bool isPositiveInt(TypeInformation info) =>
abstractValueDomain.isPositiveIntegerOrNull(info.type).isDefinitelyTrue;
TypeInformation tryLater() => inferrer.types.nonNullEmptyType;
- TypeInformation argument =
- arguments.isEmpty ? null : arguments.positional.first;
+ final argument = args.isEmpty ? null : args.positional.first;
String name = selector.name;
// These are type inference rules only for useful cases that are not
@@ -1331,7 +1338,7 @@
case '%':
case 'remainder':
case '~/':
- if (isEmpty(argument)) return tryLater();
+ if (isEmpty(argument!)) return tryLater();
if (isPositiveInt(receiver) && isPositiveInt(argument)) {
// uint31 + uint31 -> uint32
if (name == '+' && isUInt31(receiver) && isUInt31(argument)) {
@@ -1346,35 +1353,35 @@
case '|':
case '^':
- if (isEmpty(argument)) return tryLater();
+ if (isEmpty(argument!)) return tryLater();
if (isUInt31(receiver) && isUInt31(argument)) {
return inferrer.types.uint31Type;
}
return null;
case '>>':
- if (isEmpty(argument)) return tryLater();
+ if (isEmpty(argument!)) return tryLater();
if (isUInt31(receiver)) {
return inferrer.types.uint31Type;
}
return null;
case '>>>':
- if (isEmpty(argument)) return tryLater();
+ if (isEmpty(argument!)) return tryLater();
if (isUInt31(receiver)) {
return inferrer.types.uint31Type;
}
return null;
case '&':
- if (isEmpty(argument)) return tryLater();
+ if (isEmpty(argument!)) return tryLater();
if (isUInt31(receiver) || isUInt31(argument)) {
return inferrer.types.uint31Type;
}
return null;
case '-':
- if (isEmpty(argument)) return tryLater();
+ if (isEmpty(argument!)) return tryLater();
if (isInt(argument)) {
return inferrer.types.intType;
}
@@ -1385,9 +1392,7 @@
return inferrer.types.intType;
case 'abs':
- return arguments.hasNoArguments()
- ? inferrer.types.positiveIntType
- : null;
+ return args.hasNoArguments() ? inferrer.types.positiveIntType : null;
default:
return null;
@@ -1398,19 +1403,21 @@
AbstractValue computeType(InferrerEngine inferrer) {
JClosedWorld closedWorld = inferrer.closedWorld;
AbstractValueDomain abstractValueDomain = closedWorld.abstractValueDomain;
- Iterable<MemberEntity> oldTargets = _concreteTargets;
- AbstractValue typeMask = computeTypedSelector(inferrer);
+ final oldTargets = _concreteTargets!;
+ final typeMask = computeTypedSelector(inferrer);
+ final localSelector = selector!;
inferrer.updateSelectorInMember(
- caller, _callType, _call, selector, typeMask);
+ caller, _callType, _call, localSelector, typeMask);
_hasClosureCallTargets =
- closedWorld.includesClosureCall(selector, typeMask);
- _concreteTargets = closedWorld.locateMembers(selector, typeMask);
+ closedWorld.includesClosureCall(localSelector, typeMask);
+ final concreteTargets =
+ _concreteTargets = closedWorld.locateMembers(localSelector, typeMask);
// Update the call graph if the targets could have changed.
- if (!identical(_concreteTargets, oldTargets)) {
+ if (!identical(concreteTargets, oldTargets)) {
// Add calls to new targets to the graph.
- _concreteTargets
+ concreteTargets
.where((target) => !oldTargets.contains(target))
.forEach((MemberEntity element) {
MemberTypeInformation callee =
@@ -1423,7 +1430,7 @@
// Walk over the old targets, and remove calls that cannot happen anymore.
oldTargets
- .where((target) => !_concreteTargets.contains(target))
+ .where((target) => !concreteTargets.contains(target))
.forEach((MemberEntity element) {
MemberTypeInformation callee =
inferrer.types.getInferredTypeOfMember(element);
@@ -1437,18 +1444,18 @@
// Walk over the found targets, and compute the joined union type mask
// for all these targets.
AbstractValue result;
- if (_hasClosureCallTargets) {
+ if (_hasClosureCallTargets!) {
result = abstractValueDomain.dynamicType;
} else {
result = inferrer.types
- .joinTypeMasks(_concreteTargets.map((MemberEntity element) {
+ .joinTypeMasks(concreteTargets.map((MemberEntity element) {
if (typeMask != null &&
- inferrer.returnsListElementType(selector, typeMask)) {
+ inferrer.returnsListElementType(localSelector, typeMask)) {
return abstractValueDomain.getContainerElementType(receiver.type);
} else if (typeMask != null &&
- inferrer.returnsMapValueType(selector, typeMask)) {
+ inferrer.returnsMapValueType(localSelector, typeMask)) {
if (abstractValueDomain.isDictionary(typeMask)) {
- AbstractValue arg = arguments.positional[0].type;
+ AbstractValue arg = arguments!.positional[0].type;
ConstantValue value = abstractValueDomain.getPrimitiveValue(arg);
if (value is StringConstantValue) {
String key = value.stringValue;
@@ -1476,8 +1483,8 @@
}
return abstractValueDomain.getMapValueType(typeMask);
} else {
- TypeInformation info =
- handleIntrisifiedSelector(selector, typeMask, inferrer);
+ final info =
+ handleIntrisifiedSelector(localSelector, typeMask, inferrer);
if (info != null) return info.type;
return inferrer.typeOfMemberWithSelector(element, selector).type;
}
@@ -1495,16 +1502,19 @@
@override
void giveUp(InferrerEngine inferrer, {bool clearInputs = true}) {
if (!abandonInferencing) {
- inferrer.updateSelectorInMember(caller, _callType, _call, selector, mask);
- Iterable<MemberEntity> oldTargets = _concreteTargets;
+ final call = _call!;
+ inferrer.updateSelectorInMember(caller, _callType, call, selector, mask);
+ final oldTargets = concreteTargets;
+ final localSelector = selector!;
_hasClosureCallTargets =
- inferrer.closedWorld.includesClosureCall(selector, mask);
- _concreteTargets = inferrer.closedWorld.locateMembers(selector, mask);
- for (MemberEntity element in _concreteTargets) {
+ inferrer.closedWorld.includesClosureCall(localSelector, mask);
+ final newConcreteTargets = _concreteTargets =
+ inferrer.closedWorld.locateMembers(localSelector, mask);
+ for (MemberEntity element in newConcreteTargets) {
if (!oldTargets.contains(element)) {
MemberTypeInformation callee =
inferrer.types.getInferredTypeOfMember(element);
- callee.addCall(caller, _call);
+ callee.addCall(caller, call);
inferrer.updateParameterInputs(this, element, arguments, selector,
remove: false, addToQueue: true);
}
@@ -1515,13 +1525,13 @@
@override
void removeAndClearReferences(InferrerEngine inferrer) {
- for (MemberEntity element in _concreteTargets) {
+ for (MemberEntity element in concreteTargets) {
MemberTypeInformation callee =
inferrer.types.getInferredTypeOfMember(element);
callee.removeUser(this);
}
if (arguments != null) {
- arguments.forEach((info) => info.removeUser(this));
+ arguments!.forEach((info) => info.removeUser(this));
}
super.removeAndClearReferences(inferrer);
}
@@ -1537,9 +1547,9 @@
@override
bool hasStableType(InferrerEngine inferrer) {
return receiver.isStable &&
- _concreteTargets.every((MemberEntity element) =>
+ concreteTargets.every((MemberEntity element) =>
inferrer.types.getInferredTypeOfMember(element).isStable) &&
- (arguments == null || arguments.every((info) => info.isStable)) &&
+ (arguments == null || arguments!.every((info) => info.isStable)) &&
super.hasStableType(inferrer);
}
}
@@ -1550,7 +1560,7 @@
ClosureCallSiteTypeInformation(
AbstractValueDomain abstractValueDomain,
MemberTypeInformation context,
- ir.Node /*?*/ call,
+ ir.Node? call,
MemberEntity enclosing,
Selector selector,
this.closure,
@@ -1561,7 +1571,7 @@
@override
void addToGraph(InferrerEngine inferrer) {
- arguments.forEach((info) => info.addUser(this));
+ arguments!.forEach((info) => info.addUser(this));
closure.addUser(this);
}
@@ -1763,7 +1773,7 @@
bool inferred = false;
InferredTypeInformation(AbstractValueDomain abstractValueDomain,
- MemberTypeInformation context, TypeInformation parentType)
+ MemberTypeInformation? context, TypeInformation? parentType)
: super(abstractValueDomain.emptyType, context) {
if (parentType != null) addInput(parentType);
}
@@ -1789,14 +1799,14 @@
final AbstractValue originalType;
/// The length at the allocation site.
- final int originalLength;
+ final int? originalLength;
/// The length after the container has been traced.
- int inferredLength;
+ late int inferredLength;
ListTypeInformation(
AbstractValueDomain abstractValueDomain,
- MemberTypeInformation context,
+ MemberTypeInformation? context,
this.originalType,
this.elementType,
this.originalLength)
@@ -1850,7 +1860,7 @@
/// elements in a [ListTypeInformation].
class ElementInContainerTypeInformation extends InferredTypeInformation {
ElementInContainerTypeInformation(AbstractValueDomain abstractValueDomain,
- MemberTypeInformation context, elementType)
+ MemberTypeInformation? context, elementType)
: super(abstractValueDomain, context, elementType);
@override
@@ -1869,7 +1879,7 @@
final AbstractValue originalType;
SetTypeInformation(
- MemberTypeInformation context, this.originalType, this.elementType)
+ MemberTypeInformation? context, this.originalType, this.elementType)
: super(originalType, context) {
elementType.addUser(this);
}
@@ -1917,7 +1927,7 @@
/// [SetTypeInformation].
class ElementInSetTypeInformation extends InferredTypeInformation {
ElementInSetTypeInformation(AbstractValueDomain abstractValueDomain,
- MemberTypeInformation context, elementType)
+ MemberTypeInformation? context, elementType)
: super(abstractValueDomain, context, elementType);
@override
@@ -1945,32 +1955,32 @@
bool get inDictionaryMode => !bailedOut && _allKeysAreStrings;
- MapTypeInformation(MemberTypeInformation context, this.originalType,
+ MapTypeInformation(MemberTypeInformation? context, this.originalType,
this.keyType, this.valueType)
: super(originalType, context) {
keyType.addUser(this);
valueType.addUser(this);
}
- TypeInformation addEntryInput(AbstractValueDomain abstractValueDomain,
+ TypeInformation? addEntryInput(AbstractValueDomain abstractValueDomain,
TypeInformation key, TypeInformation value,
[bool nonNull = false]) {
- TypeInformation newInfo = null;
+ ValueInMapTypeInformation? newInfo = null;
if (_allKeysAreStrings && key is StringLiteralTypeInformation) {
String keyString = key.asString();
typeInfoMap.putIfAbsent(keyString, () {
newInfo = ValueInMapTypeInformation(
abstractValueDomain, context, null, nonNull);
- return newInfo;
+ return newInfo!;
});
- typeInfoMap[keyString].addInput(value);
+ typeInfoMap[keyString]!.addInput(value);
} else {
_allKeysAreStrings = false;
typeInfoMap.clear();
}
keyType.addInput(key);
valueType.addInput(value);
- if (newInfo != null) newInfo.addUser(this);
+ newInfo?.addUser(this);
return newInfo;
}
@@ -1981,12 +1991,12 @@
if (_allKeysAreStrings && other.inDictionaryMode) {
other.typeInfoMap.forEach((keyString, value) {
typeInfoMap.putIfAbsent(keyString, () {
- TypeInformation newInfo = ValueInMapTypeInformation(
+ final newInfo = ValueInMapTypeInformation(
abstractValueDomain, context, null, false);
newInfos.add(newInfo);
return newInfo;
});
- typeInfoMap[keyString].addInput(value);
+ typeInfoMap[keyString]!.addInput(value);
});
} else {
_allKeysAreStrings = false;
@@ -2018,7 +2028,7 @@
if (inDictionaryMode) {
Map<String, AbstractValue> mappings = Map<String, AbstractValue>();
for (var key in typeInfoMap.keys) {
- mappings[key] = typeInfoMap[key].type;
+ mappings[key] = typeInfoMap[key]!.type;
}
return inferrer.abstractValueDomain.createDictionaryValue(
abstractValueDomain.getGeneralization(originalType),
@@ -2045,14 +2055,14 @@
} else if (abstractValueDomain.isDictionary(type)) {
assert(inDictionaryMode);
for (String key in typeInfoMap.keys) {
- TypeInformation value = typeInfoMap[key];
+ final value = typeInfoMap[key]!;
if (!abstractValueDomain.containsDictionaryKey(type, key) &&
abstractValueDomain.containsAll(value.type).isDefinitelyFalse &&
abstractValueDomain.isNull(value.type).isDefinitelyFalse) {
return toTypeMask(inferrer);
}
if (abstractValueDomain.getDictionaryValueForKey(type, key) !=
- typeInfoMap[key].type) {
+ typeInfoMap[key]!.type) {
return toTypeMask(inferrer);
}
}
@@ -2099,7 +2109,7 @@
/// for the keys in a [MapTypeInformation]
class KeyInMapTypeInformation extends InferredTypeInformation {
KeyInMapTypeInformation(AbstractValueDomain abstractValueDomain,
- MemberTypeInformation context, TypeInformation keyType)
+ MemberTypeInformation? context, TypeInformation keyType)
: super(abstractValueDomain, context, keyType);
@override
@@ -2120,7 +2130,7 @@
final bool nonNull;
ValueInMapTypeInformation(AbstractValueDomain abstractValueDomain,
- MemberTypeInformation context, TypeInformation valueType,
+ MemberTypeInformation? context, TypeInformation? valueType,
[this.nonNull = false])
: super(abstractValueDomain, context, valueType);
@@ -2143,13 +2153,13 @@
/// A [PhiElementTypeInformation] is an union of
/// [ElementTypeInformation], that is local to a method.
class PhiElementTypeInformation extends TypeInformation {
- final ir.Node branchNode;
- final Local variable;
+ final ir.Node? branchNode;
+ final Local? variable;
final bool isTry;
PhiElementTypeInformation(AbstractValueDomain abstractValueDomain,
- MemberTypeInformation context, this.branchNode, this.variable,
- {this.isTry})
+ MemberTypeInformation? context, this.branchNode, this.variable,
+ {required this.isTry})
: super(abstractValueDomain.emptyType, context);
@override
@@ -2186,7 +2196,7 @@
final FunctionEntity _element;
ClosureTypeInformation(AbstractValueDomain abstractValueDomain,
- MemberTypeInformation context, this._element)
+ MemberTypeInformation? context, this._element)
: super(abstractValueDomain.emptyType, context);
FunctionEntity get closure => _element;
@@ -2227,7 +2237,7 @@
/// Set to true once analysis is completed.
bool analyzed = false;
- Set<TypeInformation> _flowsInto;
+ Set<TypeInformation>? _flowsInto;
/// The set of [TypeInformation] nodes where values from the traced node could
/// flow in.
@@ -2241,7 +2251,7 @@
if (_flowsInto == null) {
_flowsInto = nodes.toSet();
} else {
- _flowsInto.addAll(nodes);
+ _flowsInto!.addAll(nodes);
}
}
}
@@ -2326,7 +2336,7 @@
if (isNullable) {
otherType = abstractValueDomain.includeNull(otherType);
}
- if (type == null) return otherType;
+ assert((type as dynamic) != null); // TODO(48820): Remove when sound.
AbstractValue newType = abstractValueDomain.intersection(type, otherType);
return abstractValueDomain.isLateSentinel(type).isPotentiallyTrue
? abstractValueDomain.includeLateSentinel(newType)
diff --git a/pkg/compiler/lib/src/inferrer/type_system.dart b/pkg/compiler/lib/src/inferrer/type_system.dart
index 9a90a88..34abce3 100644
--- a/pkg/compiler/lib/src/inferrer/type_system.dart
+++ b/pkg/compiler/lib/src/inferrer/type_system.dart
@@ -2,14 +2,12 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-// @dart = 2.10
-
import 'package:kernel/ast.dart' as ir;
import '../common.dart';
import '../constants/values.dart' show BoolConstantValue;
import '../elements/entities.dart';
import '../elements/types.dart';
-import '../world.dart';
+import '../world_interfaces.dart';
import 'abstract_value_domain.dart';
import 'type_graph_nodes.dart';
@@ -30,7 +28,7 @@
void forEachParameter(FunctionEntity function, void f(Local parameter));
/// Returns whether [node] is valid as a general phi node.
- bool checkPhiNode(ir.Node node);
+ bool checkPhiNode(ir.Node? node);
/// Returns whether [node] is valid as a loop phi node.
bool checkLoopPhiNode(ir.Node node);
@@ -81,8 +79,7 @@
final Set<TypeInformation> allocatedClosures = Set<TypeInformation>();
/// Cache of [ConcreteTypeInformation].
- final Map<AbstractValue, TypeInformation> concreteTypes =
- Map<AbstractValue, TypeInformation>();
+ final Map<AbstractValue, ConcreteTypeInformation> concreteTypes = {};
/// Cache of some primitive constant types.
final Map<Object, TypeInformation> primitiveConstantTypes = {};
@@ -113,17 +110,15 @@
allocatedTypes,
].expand((x) => x);
- TypeSystem(this._closedWorld, this.strategy) {
- nonNullEmptyType = getConcreteTypeFor(_abstractValueDomain.emptyType);
- }
+ TypeSystem(this._closedWorld, this.strategy);
AbstractValueDomain get _abstractValueDomain =>
_closedWorld.abstractValueDomain;
/// Used to group [TypeInformation] nodes by the element that triggered their
/// creation.
- MemberTypeInformation _currentMember = null;
- MemberTypeInformation get currentMember => _currentMember;
+ MemberTypeInformation? _currentMember = null;
+ MemberTypeInformation? get currentMember => _currentMember;
void withMember(MemberEntity element, void action()) {
assert(_currentMember == null,
@@ -133,157 +128,81 @@
_currentMember = null;
}
- TypeInformation nullTypeCache;
- TypeInformation get nullType {
- if (nullTypeCache != null) return nullTypeCache;
- return nullTypeCache = getConcreteTypeFor(_abstractValueDomain.nullType);
- }
+ late final TypeInformation nullType =
+ getConcreteTypeFor(_abstractValueDomain.nullType);
- TypeInformation intTypeCache;
- TypeInformation get intType {
- if (intTypeCache != null) return intTypeCache;
- return intTypeCache = getConcreteTypeFor(_abstractValueDomain.intType);
- }
+ late final TypeInformation intType =
+ getConcreteTypeFor(_abstractValueDomain.intType);
- TypeInformation uint32TypeCache;
- TypeInformation get uint32Type {
- if (uint32TypeCache != null) return uint32TypeCache;
- return uint32TypeCache =
- getConcreteTypeFor(_abstractValueDomain.uint32Type);
- }
+ late final TypeInformation uint32Type =
+ getConcreteTypeFor(_abstractValueDomain.uint32Type);
- TypeInformation uint31TypeCache;
- TypeInformation get uint31Type {
- if (uint31TypeCache != null) return uint31TypeCache;
- return uint31TypeCache =
- getConcreteTypeFor(_abstractValueDomain.uint31Type);
- }
+ late final TypeInformation uint31Type =
+ getConcreteTypeFor(_abstractValueDomain.uint31Type);
- TypeInformation positiveIntTypeCache;
- TypeInformation get positiveIntType {
- if (positiveIntTypeCache != null) return positiveIntTypeCache;
- return positiveIntTypeCache =
- getConcreteTypeFor(_abstractValueDomain.positiveIntType);
- }
+ late final TypeInformation positiveIntType =
+ getConcreteTypeFor(_abstractValueDomain.positiveIntType);
- TypeInformation numTypeCache;
- TypeInformation get numType {
- if (numTypeCache != null) return numTypeCache;
- return numTypeCache = getConcreteTypeFor(_abstractValueDomain.numType);
- }
+ late final TypeInformation numType =
+ getConcreteTypeFor(_abstractValueDomain.numType);
- TypeInformation boolTypeCache;
- TypeInformation get boolType {
- if (boolTypeCache != null) return boolTypeCache;
- return boolTypeCache = getConcreteTypeFor(_abstractValueDomain.boolType);
- }
+ late final TypeInformation boolType =
+ getConcreteTypeFor(_abstractValueDomain.boolType);
- TypeInformation functionTypeCache;
- TypeInformation get functionType {
- if (functionTypeCache != null) return functionTypeCache;
- return functionTypeCache =
- getConcreteTypeFor(_abstractValueDomain.functionType);
- }
+ late final TypeInformation functionType =
+ getConcreteTypeFor(_abstractValueDomain.functionType);
- TypeInformation listTypeCache;
- TypeInformation get listType {
- if (listTypeCache != null) return listTypeCache;
- return listTypeCache = getConcreteTypeFor(_abstractValueDomain.listType);
- }
+ late final TypeInformation listType =
+ getConcreteTypeFor(_abstractValueDomain.listType);
- TypeInformation constListTypeCache;
- TypeInformation get constListType {
- if (constListTypeCache != null) return constListTypeCache;
- return constListTypeCache =
- getConcreteTypeFor(_abstractValueDomain.constListType);
- }
+ late final TypeInformation constListType =
+ getConcreteTypeFor(_abstractValueDomain.constListType);
- TypeInformation fixedListTypeCache;
- TypeInformation get fixedListType {
- if (fixedListTypeCache != null) return fixedListTypeCache;
- return fixedListTypeCache =
- getConcreteTypeFor(_abstractValueDomain.fixedListType);
- }
+ late final TypeInformation fixedListType =
+ getConcreteTypeFor(_abstractValueDomain.fixedListType);
- TypeInformation growableListTypeCache;
- TypeInformation get growableListType {
- if (growableListTypeCache != null) return growableListTypeCache;
- return growableListTypeCache =
- getConcreteTypeFor(_abstractValueDomain.growableListType);
- }
+ late final TypeInformation growableListType =
+ getConcreteTypeFor(_abstractValueDomain.growableListType);
- TypeInformation _mutableArrayType;
- TypeInformation get mutableArrayType => _mutableArrayType ??=
+ late final TypeInformation mutableArrayType =
getConcreteTypeFor(_abstractValueDomain.mutableArrayType);
- TypeInformation setTypeCache;
- TypeInformation get setType =>
- setTypeCache ??= getConcreteTypeFor(_abstractValueDomain.setType);
+ late final TypeInformation setType =
+ getConcreteTypeFor(_abstractValueDomain.setType);
- TypeInformation constSetTypeCache;
- TypeInformation get constSetType => constSetTypeCache ??=
+ late final TypeInformation constSetType =
getConcreteTypeFor(_abstractValueDomain.constSetType);
- TypeInformation mapTypeCache;
- TypeInformation get mapType {
- if (mapTypeCache != null) return mapTypeCache;
- return mapTypeCache = getConcreteTypeFor(_abstractValueDomain.mapType);
- }
+ late final TypeInformation mapType =
+ getConcreteTypeFor(_abstractValueDomain.mapType);
- TypeInformation constMapTypeCache;
- TypeInformation get constMapType {
- if (constMapTypeCache != null) return constMapTypeCache;
- return constMapTypeCache =
- getConcreteTypeFor(_abstractValueDomain.constMapType);
- }
+ late final TypeInformation constMapType =
+ getConcreteTypeFor(_abstractValueDomain.constMapType);
- TypeInformation stringTypeCache;
- TypeInformation get stringType {
- if (stringTypeCache != null) return stringTypeCache;
- return stringTypeCache =
- getConcreteTypeFor(_abstractValueDomain.stringType);
- }
+ late final TypeInformation stringType =
+ getConcreteTypeFor(_abstractValueDomain.stringType);
- TypeInformation typeTypeCache;
- TypeInformation get typeType {
- if (typeTypeCache != null) return typeTypeCache;
- return typeTypeCache = getConcreteTypeFor(_abstractValueDomain.typeType);
- }
+ late final TypeInformation typeType =
+ getConcreteTypeFor(_abstractValueDomain.typeType);
- TypeInformation dynamicTypeCache;
- TypeInformation get dynamicType {
- if (dynamicTypeCache != null) return dynamicTypeCache;
- return dynamicTypeCache =
- getConcreteTypeFor(_abstractValueDomain.dynamicType);
- }
+ late final TypeInformation dynamicType =
+ getConcreteTypeFor(_abstractValueDomain.dynamicType);
- TypeInformation asyncFutureTypeCache;
// Subtype of Future returned by async methods.
- TypeInformation get asyncFutureType {
- if (asyncFutureTypeCache != null) return asyncFutureTypeCache;
- return asyncFutureTypeCache =
- getConcreteTypeFor(_abstractValueDomain.asyncFutureType);
- }
+ late final TypeInformation asyncFutureType =
+ getConcreteTypeFor(_abstractValueDomain.asyncFutureType);
- TypeInformation syncStarIterableTypeCache;
- TypeInformation get syncStarIterableType {
- if (syncStarIterableTypeCache != null) return syncStarIterableTypeCache;
- return syncStarIterableTypeCache =
- getConcreteTypeFor(_abstractValueDomain.syncStarIterableType);
- }
+ late final TypeInformation syncStarIterableType =
+ getConcreteTypeFor(_abstractValueDomain.syncStarIterableType);
- TypeInformation asyncStarStreamTypeCache;
- TypeInformation get asyncStarStreamType {
- if (asyncStarStreamTypeCache != null) return asyncStarStreamTypeCache;
- return asyncStarStreamTypeCache =
- getConcreteTypeFor(_abstractValueDomain.asyncStarStreamType);
- }
+ late final TypeInformation asyncStarStreamType =
+ getConcreteTypeFor(_abstractValueDomain.asyncStarStreamType);
- TypeInformation _lateSentinelType;
- TypeInformation get lateSentinelType => _lateSentinelType ??=
+ late final TypeInformation lateSentinelType =
getConcreteTypeFor(_abstractValueDomain.lateSentinelType);
- TypeInformation nonNullEmptyType;
+ late final TypeInformation nonNullEmptyType =
+ getConcreteTypeFor(_abstractValueDomain.emptyType);
TypeInformation stringLiteralType(String value) {
return StringLiteralTypeInformation(
@@ -312,7 +231,7 @@
/// Returns the least upper bound between [firstType] and
/// [secondType].
TypeInformation computeLUB(
- TypeInformation firstType, TypeInformation secondType) {
+ TypeInformation? firstType, TypeInformation secondType) {
if (firstType == null) return secondType;
if (firstType == secondType) return firstType;
if (firstType == nonNullEmptyType) return secondType;
@@ -431,7 +350,7 @@
/// Returns the internal inferrer representation for [mask].
ConcreteTypeInformation getConcreteTypeFor(AbstractValue mask) {
- assert(mask != null);
+ assert((mask as dynamic) != null); // TODO(48820): Remove when sound.
return concreteTypes.putIfAbsent(mask, () {
return ConcreteTypeInformation(mask);
});
@@ -471,24 +390,24 @@
return type == nullType;
}
- TypeInformation allocateList(
- TypeInformation type, ir.TreeNode node, MemberEntity enclosing,
- [TypeInformation elementType, int length]) {
+ TypeInformation allocateList(TypeInformation type, ir.TreeNode node,
+ MemberEntity enclosing, TypeInformation elementType,
+ [int? length]) {
assert(strategy.checkListNode(node));
- ClassEntity typedDataClass = _closedWorld.commonElements.typedDataClass;
- bool isTypedArray = typedDataClass != null &&
+ final typedDataClass = _closedWorld.commonElements.typedDataClass;
+ bool isTypedArray =
_closedWorld.classHierarchy.isInstantiated(typedDataClass) &&
- _abstractValueDomain
- .isInstanceOfOrNull(type.type, typedDataClass)
- .isDefinitelyTrue;
+ _abstractValueDomain
+ .isInstanceOfOrNull(type.type, typedDataClass)
+ .isDefinitelyTrue;
bool isConst = (type.type == _abstractValueDomain.constListType);
bool isFixed = (type.type == _abstractValueDomain.fixedListType) ||
isConst ||
isTypedArray;
bool isElementInferred = isConst || isTypedArray;
- int inferredLength = isFixed ? length : null;
- AbstractValue elementTypeMask =
+ final inferredLength = isFixed ? length : null;
+ final elementTypeMask =
isElementInferred ? elementType.type : dynamicType.type;
AbstractValue mask = _abstractValueDomain.createContainerValue(
type.type, node, enclosing, elementTypeMask, inferredLength);
@@ -512,9 +431,8 @@
return result;
}
- TypeInformation allocateSet(
- TypeInformation type, ir.TreeNode node, MemberEntity enclosing,
- [TypeInformation elementType]) {
+ TypeInformation allocateSet(TypeInformation type, ir.TreeNode node,
+ MemberEntity enclosing, TypeInformation elementType) {
assert(strategy.checkSetNode(node));
bool isConst = type.type == _abstractValueDomain.constSetType;
@@ -532,44 +450,47 @@
}
TypeInformation allocateMap(
- ConcreteTypeInformation type, ir.TreeNode node, MemberEntity element,
- [List<TypeInformation> keyTypes, List<TypeInformation> valueTypes]) {
+ ConcreteTypeInformation type,
+ ir.TreeNode node,
+ MemberEntity element,
+ List<TypeInformation> keyTypes,
+ List<TypeInformation> valueTypes) {
assert(strategy.checkMapNode(node));
assert(keyTypes.length == valueTypes.length);
bool isFixed = (type.type == _abstractValueDomain.constMapType);
- TypeInformation keyType, valueType;
+ PhiElementTypeInformation? keyType, valueType;
for (int i = 0; i < keyTypes.length; ++i) {
- TypeInformation type = keyTypes[i];
+ final typeForKey = keyTypes[i];
keyType = keyType == null
- ? allocatePhi(null, null, type, isTry: false)
- : addPhiInput(null, keyType, type);
+ ? allocatePhi(null, null, typeForKey, isTry: false)
+ : addPhiInput(null, keyType, typeForKey);
- type = valueTypes[i];
+ final typeForValue = valueTypes[i];
valueType = valueType == null
- ? allocatePhi(null, null, type, isTry: false)
- : addPhiInput(null, valueType, type);
+ ? allocatePhi(null, null, typeForValue, isTry: false)
+ : addPhiInput(null, valueType, typeForValue);
}
- keyType =
+ final simplifiedKeyType =
keyType == null ? nonNullEmpty() : simplifyPhi(null, null, keyType);
- valueType =
+ final simplifiedValueType =
valueType == null ? nonNullEmpty() : simplifyPhi(null, null, valueType);
AbstractValue keyTypeMask, valueTypeMask;
if (isFixed) {
- keyTypeMask = keyType.type;
- valueTypeMask = valueType.type;
+ keyTypeMask = simplifiedKeyType.type;
+ valueTypeMask = simplifiedValueType.type;
} else {
keyTypeMask = valueTypeMask = dynamicType.type;
}
AbstractValue mask = _abstractValueDomain.createMapValue(
type.type, node, element, keyTypeMask, valueTypeMask);
- TypeInformation keyTypeInfo =
- KeyInMapTypeInformation(_abstractValueDomain, currentMember, keyType);
- TypeInformation valueTypeInfo = ValueInMapTypeInformation(
- _abstractValueDomain, currentMember, valueType);
+ final keyTypeInfo = KeyInMapTypeInformation(
+ _abstractValueDomain, currentMember, simplifiedKeyType);
+ final valueTypeInfo = ValueInMapTypeInformation(
+ _abstractValueDomain, currentMember, simplifiedValueType);
allocatedTypes.add(keyTypeInfo);
allocatedTypes.add(valueTypeInfo);
@@ -577,7 +498,7 @@
MapTypeInformation(currentMember, mask, keyTypeInfo, valueTypeInfo);
for (int i = 0; i < keyTypes.length; ++i) {
- TypeInformation newType = map.addEntryInput(
+ final newType = map.addEntryInput(
_abstractValueDomain, keyTypes[i], valueTypes[i], true);
if (newType != null) allocatedTypes.add(newType);
}
@@ -610,7 +531,7 @@
}
PhiElementTypeInformation _addPhi(
- ir.Node node, Local variable, TypeInformation inputType, bool isTry) {
+ ir.Node? node, Local? variable, TypeInformation inputType, bool isTry) {
PhiElementTypeInformation result = PhiElementTypeInformation(
_abstractValueDomain, currentMember, node, variable,
isTry: isTry);
@@ -622,8 +543,8 @@
/// Returns a new type for holding the potential types of [element].
/// [inputType] is the first incoming type of the phi.
PhiElementTypeInformation allocatePhi(
- ir.Node node, Local variable, TypeInformation inputType,
- {bool isTry}) {
+ ir.Node? node, Local? variable, TypeInformation inputType,
+ {required bool isTry}) {
assert(strategy.checkPhiNode(node));
// Check if [inputType] is a phi for a local updated in
// the try/catch block [node]. If it is, no need to allocate a new
@@ -643,7 +564,7 @@
/// from other merging uses.
PhiElementTypeInformation allocateLoopPhi(
ir.Node node, Local variable, TypeInformation inputType,
- {bool isTry}) {
+ {required bool isTry}) {
assert(strategy.checkLoopPhiNode(node));
return _addPhi(node, variable, inputType, isTry);
}
@@ -653,14 +574,14 @@
/// implementation of this method could just return that incoming
/// input type.
TypeInformation simplifyPhi(
- ir.Node node, Local variable, PhiElementTypeInformation phiType) {
+ ir.Node? node, Local? variable, PhiElementTypeInformation phiType) {
assert(phiType.branchNode == node);
if (phiType.inputs.length == 1) return phiType.inputs.first;
return phiType;
}
/// Adds [newType] as an input of [phiType].
- PhiElementTypeInformation addPhiInput(Local variable,
+ PhiElementTypeInformation addPhiInput(Local? variable,
PhiElementTypeInformation phiType, TypeInformation newType) {
phiType.addInput(newType);
return phiType;
@@ -697,7 +618,7 @@
list.add(mask);
}
- AbstractValue newType = null;
+ AbstractValue? newType;
for (AbstractValue mask in list) {
newType =
newType == null ? mask : _abstractValueDomain.union(newType, mask);
diff --git a/pkg/compiler/lib/src/ir/impact_data.dart b/pkg/compiler/lib/src/ir/impact_data.dart
index fb1f3b2..2a65114 100644
--- a/pkg/compiler/lib/src/ir/impact_data.dart
+++ b/pkg/compiler/lib/src/ir/impact_data.dart
@@ -192,10 +192,6 @@
}
registerAsyncStar(elementType);
break;
-
- case ir.AsyncMarker.SyncYielding:
- failedAt(CURRENT_ELEMENT_SPANNABLE,
- "Unexpected async marker: ${asyncMarker}");
}
}
diff --git a/pkg/compiler/lib/src/ir/util.dart b/pkg/compiler/lib/src/ir/util.dart
index c9fa392..d96ad3d 100644
--- a/pkg/compiler/lib/src/ir/util.dart
+++ b/pkg/compiler/lib/src/ir/util.dart
@@ -58,7 +58,6 @@
return AsyncMarker.SYNC;
case ir.AsyncMarker.SyncStar:
return AsyncMarker.SYNC_STAR;
- case ir.AsyncMarker.SyncYielding:
default:
throw UnsupportedError(
"Async marker ${node.asyncMarker} is not supported.");
diff --git a/pkg/compiler/lib/src/js_backend/custom_elements_analysis.dart b/pkg/compiler/lib/src/js_backend/custom_elements_analysis.dart
index a542b6c..a5a1606 100644
--- a/pkg/compiler/lib/src/js_backend/custom_elements_analysis.dart
+++ b/pkg/compiler/lib/src/js_backend/custom_elements_analysis.dart
@@ -2,8 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-// @dart = 2.10
-
import '../common/elements.dart';
import '../constants/constant_system.dart' as constant_system;
import '../constants/values.dart';
@@ -71,7 +69,7 @@
}
void registerStaticUse(MemberEntity element) {
- assert(element != null);
+ assert((element as dynamic) != null); // TODO(48820): Remove.
if (_commonElements.isFindIndexForNativeSubclassType(element)) {
join.demanded = true;
}
@@ -151,9 +149,7 @@
final ElementEnvironment _elementEnvironment;
final CommonElements _commonElements;
final NativeBasicData _nativeData;
- final BackendUsageBuilder _backendUsageBuilder;
-
- final bool forResolution;
+ final BackendUsageBuilder? _backendUsageBuilder;
// Classes that are candidates for needing constructors. Classes are moved to
// [activeClasses] when we know they need constructors.
@@ -173,9 +169,8 @@
CustomElementsAnalysisJoin(
this._elementEnvironment, this._commonElements, this._nativeData,
- {BackendUsageBuilder backendUsageBuilder})
- : this._backendUsageBuilder = backendUsageBuilder,
- this.forResolution = backendUsageBuilder != null;
+ {BackendUsageBuilder? backendUsageBuilder})
+ : this._backendUsageBuilder = backendUsageBuilder;
WorldImpact flush() {
if (!demanded) return const WorldImpact();
@@ -196,13 +191,13 @@
impactBuilder.registerStaticUse(
StaticUse.constructorInvoke(constructor, CallStructure.NO_ARGS));
}
- if (forResolution) {
+ if (_backendUsageBuilder != null) {
escapingConstructors
- .forEach(_backendUsageBuilder.registerGlobalFunctionDependency);
+ .forEach(_backendUsageBuilder!.registerGlobalFunctionDependency);
}
// Force the generation of the type constant that is the key to an entry
// in the generated table.
- ConstantValue constant = _makeTypeConstant(cls);
+ final constant = _makeTypeConstant(cls);
impactBuilder.registerConstantUse(ConstantUse.customElements(constant));
}
}
diff --git a/pkg/compiler/lib/src/js_emitter/native_generator.dart b/pkg/compiler/lib/src/js_emitter/native_generator.dart
index a47b6be..ab7d73a 100644
--- a/pkg/compiler/lib/src/js_emitter/native_generator.dart
+++ b/pkg/compiler/lib/src/js_emitter/native_generator.dart
@@ -2,8 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-// @dart = 2.10
-
library dart2js.js_emitter.native_generator;
import 'package:js_runtime/synced/embedded_names.dart' as embeddedNames;
diff --git a/pkg/compiler/lib/src/source_file_provider.dart b/pkg/compiler/lib/src/source_file_provider.dart
index df9763b..ee9dea5 100644
--- a/pkg/compiler/lib/src/source_file_provider.dart
+++ b/pkg/compiler/lib/src/source_file_provider.dart
@@ -559,13 +559,18 @@
}
api.Input<List<int>> result =
await readBytesFromUri(resolvedUri, inputKind);
- switch (inputKind) {
- case api.InputKind.UTF8:
- utf8SourceFiles[uri] = utf8SourceFiles[resolvedUri];
- break;
- case api.InputKind.binary:
- binarySourceFiles[uri] = binarySourceFiles[resolvedUri];
- break;
+ if (uri != resolvedUri) {
+ if (!resolvedUri.isAbsolute) {
+ resolvedUri = cwd.resolveUri(resolvedUri);
+ }
+ switch (inputKind) {
+ case api.InputKind.UTF8:
+ utf8SourceFiles[uri] = utf8SourceFiles[resolvedUri];
+ break;
+ case api.InputKind.binary:
+ binarySourceFiles[uri] = binarySourceFiles[resolvedUri];
+ break;
+ }
}
return result;
}
diff --git a/pkg/compiler/test/analyses/dart2js_allowed.json b/pkg/compiler/test/analyses/dart2js_allowed.json
index 107ef15..746d0ef 100644
--- a/pkg/compiler/test/analyses/dart2js_allowed.json
+++ b/pkg/compiler/test/analyses/dart2js_allowed.json
@@ -27,15 +27,6 @@
"Dynamic access of 'memberContext'.": 1,
"Dynamic access of 'name'.": 1
},
- "pkg/compiler/lib/src/inferrer/locals_handler.dart": {
- "Dynamic access of 'length'.": 2,
- "Dynamic access of 'named'.": 2,
- "Dynamic access of 'positional'.": 2,
- "Dynamic invocation of '[]'.": 2
- },
- "pkg/compiler/lib/src/inferrer/type_graph_nodes.dart": {
- "Dynamic access of 'type'.": 4
- },
"pkg/compiler/lib/src/inferrer/typemasks/type_mask.dart": {
"Dynamic access of 'forwardTo'.": 1,
"Dynamic access of 'isForwarding'.": 1
diff --git a/pkg/compiler/test/serialization/serialization_test_helper.dart b/pkg/compiler/test/serialization/serialization_test_helper.dart
index f50d65b..aa3474c 100644
--- a/pkg/compiler/test/serialization/serialization_test_helper.dart
+++ b/pkg/compiler/test/serialization/serialization_test_helper.dart
@@ -24,17 +24,18 @@
const List<String> dumpInfoExceptions = [
'"compilationMoment":',
'"compilationDuration":',
- '"toJsonDuration":'
+ '"toJsonDuration":',
+ '"ramUsage":'
];
-void generateJavaScriptCode(
- Compiler compiler, GlobalTypeInferenceResults globalTypeInferenceResults) {
+void generateJavaScriptCode(Compiler compiler,
+ GlobalTypeInferenceResults globalTypeInferenceResults) async {
final codegenInputs = compiler.initializeCodegen(globalTypeInferenceResults);
final codegenResults = OnDemandCodegenResults(globalTypeInferenceResults,
codegenInputs, compiler.backendStrategy.functionCompiler);
final programSize = compiler.runCodegenEnqueuer(codegenResults);
if (compiler.options.dumpInfo) {
- compiler.runDumpInfo(codegenResults, programSize);
+ await compiler.runDumpInfo(codegenResults, programSize);
}
}
@@ -44,7 +45,7 @@
Compiler compiler,
SerializationStrategy strategy,
{bool stoppedAfterClosedWorld = false,
- bool stoppedAfterTypeInference = false}) {
+ bool stoppedAfterTypeInference = false}) async {
if (stoppedAfterClosedWorld) {
JsClosedWorld closedWorld = compiler.backendClosedWorldForTesting;
var newClosedWorldAndIndices =
@@ -59,7 +60,7 @@
GlobalTypeInferenceResults newGlobalInferenceResults =
cloneInferenceResults(
indices, compiler, globalInferenceResults, strategy);
- generateJavaScriptCode(compiler, newGlobalInferenceResults);
+ await generateJavaScriptCode(compiler, newGlobalInferenceResults);
}
var actualOutput = actualOutputCollector.clear();
Expect.setEquals(
@@ -181,10 +182,10 @@
});
Expect.isTrue(result3b.isSuccess);
- finishCompileAndCompare(
+ await finishCompileAndCompare(
expectedOutput, collector2, result2.compiler, strategy,
stoppedAfterClosedWorld: true);
- finishCompileAndCompare(
+ await finishCompileAndCompare(
expectedOutput, collector3b, result3b.compiler, strategy,
stoppedAfterTypeInference: true);
await dir.delete(recursive: true);
diff --git a/pkg/dart2js_info/lib/binary_serialization.dart b/pkg/dart2js_info/lib/binary_serialization.dart
index 8f788b2..0d707ce 100644
--- a/pkg/dart2js_info/lib/binary_serialization.dart
+++ b/pkg/dart2js_info/lib/binary_serialization.dart
@@ -75,6 +75,7 @@
void visitProgram(ProgramInfo info) {
visitFunction(info.entrypoint);
sink.writeInt(info.size);
+ sink.writeString(info.ramUsage);
sink.writeStringOrNull(info.dart2jsVersion);
writeDate(info.compilationMoment);
writeDuration(info.compilationDuration);
@@ -324,6 +325,7 @@
ProgramInfo readProgram() {
final entrypoint = readFunction();
final size = source.readInt();
+ final ramUsage = source.readString();
final dart2jsVersion = source.readStringOrNull();
final compilationMoment = readDate();
final compilationDuration = readDuration();
@@ -338,6 +340,7 @@
return ProgramInfo(
entrypoint: entrypoint,
size: size,
+ ramUsage: ramUsage,
dart2jsVersion: dart2jsVersion,
compilationMoment: compilationMoment,
compilationDuration: compilationDuration,
diff --git a/pkg/dart2js_info/lib/info.dart b/pkg/dart2js_info/lib/info.dart
index 1462964..509edb0 100644
--- a/pkg/dart2js_info/lib/info.dart
+++ b/pkg/dart2js_info/lib/info.dart
@@ -141,6 +141,7 @@
class ProgramInfo {
final FunctionInfo entrypoint;
+ final String ramUsage;
final int size;
final String? dart2jsVersion;
final DateTime compilationMoment;
@@ -167,6 +168,7 @@
ProgramInfo(
{required this.entrypoint,
+ required this.ramUsage,
required this.size,
required this.dart2jsVersion,
required this.compilationMoment,
diff --git a/pkg/dart2js_info/lib/json_info_codec.dart b/pkg/dart2js_info/lib/json_info_codec.dart
index 639776a..937ea4d 100644
--- a/pkg/dart2js_info/lib/json_info_codec.dart
+++ b/pkg/dart2js_info/lib/json_info_codec.dart
@@ -206,6 +206,7 @@
final programInfo = ProgramInfo(
entrypoint: parseId(json['entrypoint']) as FunctionInfo,
size: json['size'],
+ ramUsage: json['ramUsage'],
compilationMoment: DateTime.parse(json['compilationMoment']),
dart2jsVersion: json['dart2jsVersion'],
noSuchMethodEnabled: json['noSuchMethodEnabled'],
@@ -455,6 +456,7 @@
return {
'entrypoint': idFor(info.entrypoint).serializedId,
'size': info.size,
+ 'ramUsage': info.ramUsage,
'dart2jsVersion': info.dart2jsVersion,
'compilationMoment': '${info.compilationMoment}',
'compilationDuration': info.compilationDuration.inMicroseconds,
diff --git a/pkg/dart2js_info/test/hello_world/hello_world.js.info.json b/pkg/dart2js_info/test/hello_world/hello_world.js.info.json
index 9006151..c0bf81b 100644
--- a/pkg/dart2js_info/test/hello_world/hello_world.js.info.json
+++ b/pkg/dart2js_info/test/hello_world/hello_world.js.info.json
@@ -47,7 +47,7 @@
"id": "library/dart:_js_helper::",
"kind": "library",
"name": "_js_helper",
- "size": 12952,
+ "size": 12859,
"children": [
"class/dart:_js_helper::BoundClosure",
"class/dart:_js_helper::Closure",
@@ -262,7 +262,7 @@
"children": [
"function/hello_world.dart::main"
],
- "canonicalUri": "file:///usr/local/google/home/natebiggs/dart-sdk/sdk/pkg/dart2js_info/test/hello_world/hello_world.dart"
+ "canonicalUri": "file:///Users/markzipan/Projects/dart-sdk/sdk/pkg/dart2js_info/test/hello_world/hello_world.dart"
}
},
"class": {
@@ -2303,7 +2303,7 @@
"id": "function/dart:_js_helper::Primitives._objectTypeNameNewRti",
"kind": "function",
"name": "_objectTypeNameNewRti",
- "size": 959,
+ "size": 866,
"outputUnit": "outputUnit/main",
"parent": "class/dart:_js_helper::Primitives",
"children": [],
@@ -2324,7 +2324,7 @@
],
"sideEffects": "SideEffects(reads anything; writes anything)",
"inlinedCount": 0,
- "code": "Primitives__objectTypeNameNewRti(object) {\n var interceptor, dispatchName, t1, $constructor, constructorName;\n if (object instanceof A.Object)\n return A._rtiToString(A.instanceType(object), null);\n interceptor = J.getInterceptor$(object);\n if (interceptor === B.Interceptor_methods || interceptor === B.JavaScriptObject_methods || false) {\n dispatchName = B.C_JS_CONST(object);\n t1 = dispatchName !== \"Object\" && dispatchName !== \"\";\n if (t1)\n return dispatchName;\n $constructor = object.constructor;\n if (typeof $constructor == \"function\") {\n constructorName = $constructor.name;\n if (typeof constructorName == \"string\")\n t1 = constructorName !== \"Object\" && constructorName !== \"\";\n else\n t1 = false;\n if (t1)\n return constructorName;\n }\n }\n return A._rtiToString(A.instanceType(object), null);\n }",
+ "code": "Primitives__objectTypeNameNewRti(object) {\n var interceptor, dispatchName, $constructor, constructorName;\n if (object instanceof A.Object)\n return A._rtiToString(A.instanceType(object), null);\n interceptor = J.getInterceptor$(object);\n if (interceptor === B.Interceptor_methods || interceptor === B.JavaScriptObject_methods || false) {\n dispatchName = B.C_JS_CONST(object);\n if (dispatchName !== \"Object\" && dispatchName !== \"\")\n return dispatchName;\n $constructor = object.constructor;\n if (typeof $constructor == \"function\") {\n constructorName = $constructor.name;\n if (typeof constructorName == \"string\" && constructorName !== \"Object\" && constructorName !== \"\")\n return constructorName;\n }\n }\n return A._rtiToString(A.instanceType(object), null);\n }",
"type": "String Function(Object?)",
"functionKind": 0
},
@@ -28711,7 +28711,7 @@
"id": "outputUnit/main",
"kind": "outputUnit",
"name": "main",
- "size": 90362,
+ "size": 90269,
"filename": "hello_world.js",
"imports": []
}
@@ -28721,11 +28721,12 @@
"dump_minor_version": 1,
"program": {
"entrypoint": "function/hello_world.dart::main",
- "size": 90362,
+ "size": 90269,
+ "ramUsage": "194.188 MB",
"dart2jsVersion": null,
- "compilationMoment": "2022-05-26 21:08:43.608041",
- "compilationDuration": 3177312,
- "toJsonDuration": 3000,
+ "compilationMoment": "2022-07-08 12:57:36.142141",
+ "compilationDuration": 1321045,
+ "toJsonDuration": 1000,
"dumpInfoDuration": 0,
"noSuchMethodEnabled": false,
"isRuntimeTypeUsed": false,
diff --git a/pkg/dart2js_info/test/hello_world_deferred/hello_world_deferred.js.info.json b/pkg/dart2js_info/test/hello_world_deferred/hello_world_deferred.js.info.json
index cc4f282..f583ac5 100644
--- a/pkg/dart2js_info/test/hello_world_deferred/hello_world_deferred.js.info.json
+++ b/pkg/dart2js_info/test/hello_world_deferred/hello_world_deferred.js.info.json
@@ -54,7 +54,7 @@
"id": "library/dart:_js_helper::",
"kind": "library",
"name": "_js_helper",
- "size": 47353,
+ "size": 47260,
"children": [
"class/dart:_js_helper::BoundClosure",
"class/dart:_js_helper::Closure",
@@ -274,7 +274,7 @@
"id": "library/dart:async::",
"kind": "library",
"name": "dart.async",
- "size": 41039,
+ "size": 41197,
"children": [
"class/dart:async::AsyncError",
"class/dart:async::Completer",
@@ -397,7 +397,7 @@
"children": [
"function/hello_world_deferred.dart::main"
],
- "canonicalUri": "file:///usr/local/google/home/natebiggs/dart-sdk/sdk/pkg/dart2js_info/test/hello_world_deferred/hello_world_deferred.dart"
+ "canonicalUri": "file:///Users/markzipan/Projects/dart-sdk/sdk/pkg/dart2js_info/test/hello_world_deferred/hello_world_deferred.dart"
}
},
"class": {
@@ -5410,7 +5410,7 @@
"id": "function/dart:_js_helper::Primitives._objectTypeNameNewRti",
"kind": "function",
"name": "_objectTypeNameNewRti",
- "size": 995,
+ "size": 902,
"outputUnit": "outputUnit/main",
"parent": "class/dart:_js_helper::Primitives",
"children": [],
@@ -5431,7 +5431,7 @@
],
"sideEffects": "SideEffects(reads anything; writes anything)",
"inlinedCount": 0,
- "code": "Primitives__objectTypeNameNewRti(object) {\n var interceptor, dispatchName, t1, $constructor, constructorName;\n if (object instanceof A.Object)\n return A._rtiToString(A.instanceType(object), null);\n interceptor = J.getInterceptor$(object);\n if (interceptor === B.Interceptor_methods || interceptor === B.JavaScriptObject_methods || type$.UnknownJavaScriptObject._is(object)) {\n dispatchName = B.C_JS_CONST(object);\n t1 = dispatchName !== \"Object\" && dispatchName !== \"\";\n if (t1)\n return dispatchName;\n $constructor = object.constructor;\n if (typeof $constructor == \"function\") {\n constructorName = $constructor.name;\n if (typeof constructorName == \"string\")\n t1 = constructorName !== \"Object\" && constructorName !== \"\";\n else\n t1 = false;\n if (t1)\n return constructorName;\n }\n }\n return A._rtiToString(A.instanceType(object), null);\n }",
+ "code": "Primitives__objectTypeNameNewRti(object) {\n var interceptor, dispatchName, $constructor, constructorName;\n if (object instanceof A.Object)\n return A._rtiToString(A.instanceType(object), null);\n interceptor = J.getInterceptor$(object);\n if (interceptor === B.Interceptor_methods || interceptor === B.JavaScriptObject_methods || type$.UnknownJavaScriptObject._is(object)) {\n dispatchName = B.C_JS_CONST(object);\n if (dispatchName !== \"Object\" && dispatchName !== \"\")\n return dispatchName;\n $constructor = object.constructor;\n if (typeof $constructor == \"function\") {\n constructorName = $constructor.name;\n if (typeof constructorName == \"string\" && constructorName !== \"Object\" && constructorName !== \"\")\n return constructorName;\n }\n }\n return A._rtiToString(A.instanceType(object), null);\n }",
"type": "String Function(Object?)",
"functionKind": 0
},
@@ -19721,7 +19721,7 @@
"id": "function/dart:async::scheduleMicrotask",
"kind": "function",
"name": "scheduleMicrotask",
- "size": 367,
+ "size": 525,
"outputUnit": "outputUnit/main",
"parent": "library/dart:async::",
"children": [],
@@ -19742,7 +19742,7 @@
],
"sideEffects": "SideEffects(reads anything; writes anything)",
"inlinedCount": 0,
- "code": "scheduleMicrotask(callback) {\n var _null = null,\n currentZone = $.Zone__current;\n if (B.C__RootZone === currentZone) {\n A._rootScheduleMicrotask(_null, _null, B.C__RootZone, callback);\n return;\n }\n A._rootScheduleMicrotask(_null, _null, currentZone, type$.void_Function._as(currentZone.bindCallbackGuarded$1(callback)));\n }",
+ "code": "scheduleMicrotask(callback) {\n var t1, _null = null,\n currentZone = $.Zone__current;\n if (B.C__RootZone === currentZone) {\n A._rootScheduleMicrotask(_null, _null, B.C__RootZone, callback);\n return;\n }\n t1 = false;\n if (t1) {\n A._rootScheduleMicrotask(_null, _null, currentZone, type$.void_Function._as(callback));\n return;\n }\n A._rootScheduleMicrotask(_null, _null, currentZone, type$.void_Function._as(currentZone.bindCallbackGuarded$1(callback)));\n }",
"type": "void Function(void Function())",
"functionKind": 0
},
@@ -67168,7 +67168,7 @@
"id": "outputUnit/main",
"kind": "outputUnit",
"name": "main",
- "size": 188050,
+ "size": 188115,
"filename": "hello_world_deferred.js",
"imports": []
}
@@ -67187,11 +67187,12 @@
"dump_minor_version": 1,
"program": {
"entrypoint": "function/hello_world_deferred.dart::main",
- "size": 188644,
+ "size": 188709,
+ "ramUsage": "218.516 MB",
"dart2jsVersion": null,
- "compilationMoment": "2022-05-26 21:07:11.259922",
- "compilationDuration": 4395336,
- "toJsonDuration": 9000,
+ "compilationMoment": "2022-07-08 12:58:35.955576",
+ "compilationDuration": 1806706,
+ "toJsonDuration": 3000,
"dumpInfoDuration": 0,
"noSuchMethodEnabled": false,
"isRuntimeTypeUsed": false,
diff --git a/pkg/dart2js_info/test/json_to_proto_test.dart b/pkg/dart2js_info/test/json_to_proto_test.dart
index 240f3c2..5b20745 100644
--- a/pkg/dart2js_info/test/json_to_proto_test.dart
+++ b/pkg/dart2js_info/test/json_to_proto_test.dart
@@ -20,11 +20,11 @@
final proto = AllInfoProtoCodec().encode(decoded);
expect(proto.program.entrypointId, isNotNull);
- expect(proto.program.size, 90362);
+ expect(proto.program.size, 90269);
expect(proto.program.compilationMoment.toInt(),
- DateTime.parse("2022-05-26 21:08:43.608041").microsecondsSinceEpoch);
+ DateTime.parse("2022-07-08 12:57:36.142141").microsecondsSinceEpoch);
expect(proto.program.toProtoDuration.toInt(),
- Duration(milliseconds: 3).inMicroseconds);
+ Duration(milliseconds: 1).inMicroseconds);
expect(proto.program.dumpInfoDuration.toInt(),
Duration(milliseconds: 0).inMicroseconds);
expect(proto.program.noSuchMethodEnabled, isFalse);
diff --git a/pkg/dart2js_info/test/parse_test.dart b/pkg/dart2js_info/test/parse_test.dart
index 92c34d0..be06468 100644
--- a/pkg/dart2js_info/test/parse_test.dart
+++ b/pkg/dart2js_info/test/parse_test.dart
@@ -20,11 +20,11 @@
expect(program, isNotNull);
expect(program!.entrypoint, isNotNull);
- expect(program.size, 90362);
+ expect(program.size, 90269);
expect(program.compilationMoment,
- DateTime.parse("2022-05-26 21:08:43.608041"));
- expect(program.compilationDuration, Duration(microseconds: 3177312));
- expect(program.toJsonDuration, Duration(milliseconds: 3));
+ DateTime.parse("2022-07-08 12:57:36.142141"));
+ expect(program.compilationDuration, Duration(microseconds: 1321045));
+ expect(program.toJsonDuration, Duration(milliseconds: 1));
expect(program.dumpInfoDuration, Duration(seconds: 0));
expect(program.noSuchMethodEnabled, false);
expect(program.minified, false);
diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
index 94bac69..5d15880 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -76,8 +76,7 @@
import '../modifier.dart'
show Modifier, constMask, covariantMask, finalMask, lateMask, requiredMask;
import '../names.dart' show emptyName, minusName, plusName;
-import '../problems.dart'
- show internalProblem, unexpected, unhandled, unsupported;
+import '../problems.dart' show internalProblem, unhandled, unsupported;
import '../scope.dart';
import '../source/diet_parser.dart';
import '../source/source_class_builder.dart';
@@ -1415,9 +1414,6 @@
case AsyncMarker.Sync:
break; // skip
- case AsyncMarker.SyncYielding:
- unexpected("async, async*, sync, or sync*", "$asyncModifier",
- member.charOffset, uri);
}
if (problem != null) {
@@ -3284,8 +3280,8 @@
@override
void endIfStatement(Token ifToken, Token? elseToken) {
Statement? elsePart = popStatementIfNotNull(elseToken);
- AssignedVariablesNodeInfo<VariableDeclaration> assignedVariablesInfo =
- pop() as AssignedVariablesNodeInfo<VariableDeclaration>;
+ AssignedVariablesNodeInfo assignedVariablesInfo =
+ pop() as AssignedVariablesNodeInfo;
Statement thenPart = popStatement();
Expression condition = pop() as Expression;
Statement node = forest.createIfStatement(
@@ -3308,7 +3304,7 @@
void endVariableInitializer(Token assignmentOperator) {
debugEvent("VariableInitializer");
assert(assignmentOperator.stringValue == "=");
- AssignedVariablesNodeInfo<VariableDeclaration>? assignedVariablesInfo;
+ AssignedVariablesNodeInfo? assignedVariablesInfo;
bool isLate = (currentLocalVariableModifiers & lateMask) != 0;
Expression initializer = popForValue();
if (isLate) {
@@ -3535,8 +3531,8 @@
/// ends. Since these need to be associated with the try statement created in
/// in [endTryStatement] we store them the stack until the try statement is
/// created.
- Link<AssignedVariablesNodeInfo<VariableDeclaration>> tryStatementInfoStack =
- const Link<AssignedVariablesNodeInfo<VariableDeclaration>>();
+ Link<AssignedVariablesNodeInfo> tryStatementInfoStack =
+ const Link<AssignedVariablesNodeInfo>();
@override
void beginBlock(Token token, BlockKind blockKind) {
@@ -3728,7 +3724,7 @@
// [handleForInitializerEmptyStatement],
// [handleForInitializerExpressionStatement], and
// [handleForInitializerLocalVariableDeclaration].
- AssignedVariablesNodeInfo<VariableDeclaration> assignedVariablesNodeInfo =
+ AssignedVariablesNodeInfo assignedVariablesNodeInfo =
typeInferrer.assignedVariables.popNode();
Object? variableOrExpression = pop();
@@ -3795,7 +3791,7 @@
// [handleForInitializerEmptyStatement],
// [handleForInitializerExpressionStatement], and
// [handleForInitializerLocalVariableDeclaration].
- AssignedVariablesNodeInfo<VariableDeclaration> assignedVariablesNodeInfo =
+ AssignedVariablesNodeInfo assignedVariablesNodeInfo =
typeInferrer.assignedVariables.deferNode();
Object? variableOrExpression = pop();
@@ -4383,8 +4379,8 @@
debugEvent("ConditionalExpression");
Expression elseExpression = popForValue();
Expression thenExpression = pop() as Expression;
- AssignedVariablesNodeInfo<VariableDeclaration> assignedVariablesInfo =
- pop() as AssignedVariablesNodeInfo<VariableDeclaration>;
+ AssignedVariablesNodeInfo assignedVariablesInfo =
+ pop() as AssignedVariablesNodeInfo;
Expression condition = pop() as Expression;
Expression node = forest.createConditionalExpression(
offsetForToken(question), condition, thenExpression, elseExpression);
@@ -5757,8 +5753,8 @@
debugEvent("endIfElseControlFlow");
Object? elseEntry = pop(); // else entry
Object? thenEntry = pop(); // then entry
- AssignedVariablesNodeInfo<VariableDeclaration> assignedVariablesInfo =
- pop() as AssignedVariablesNodeInfo<VariableDeclaration>;
+ AssignedVariablesNodeInfo assignedVariablesInfo =
+ pop() as AssignedVariablesNodeInfo;
Object? condition = pop(); // parenthesized expression
Token ifToken = pop() as Token;
@@ -6232,7 +6228,7 @@
}
// This is matched by the call to [beginNode] in [handleForInLoopParts].
- AssignedVariablesNodeInfo<VariableDeclaration> assignedVariablesNodeInfo =
+ AssignedVariablesNodeInfo assignedVariablesNodeInfo =
typeInferrer.assignedVariables.popNode();
Expression iterable = popForValue();
@@ -6348,7 +6344,7 @@
Token? awaitToken = pop(NullValue.AwaitToken) as Token?;
// This is matched by the call to [beginNode] in [handleForInLoopParts].
- AssignedVariablesNodeInfo<VariableDeclaration> assignedVariablesNodeInfo =
+ AssignedVariablesNodeInfo assignedVariablesNodeInfo =
typeInferrer.assignedVariables.deferNode();
Expression expression = popForValue();
diff --git a/pkg/front_end/lib/src/fasta/source/source_loader.dart b/pkg/front_end/lib/src/fasta/source/source_loader.dart
index 4f62e70..c039e08 100644
--- a/pkg/front_end/lib/src/fasta/source/source_loader.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_loader.dart
@@ -2730,45 +2730,14 @@
class num {}
-class _SyncIterable {}
-
-class _SyncIterator {
- var _current;
- var _yieldEachIterable;
-}
-
class Function {}
""";
/// A minimal implementation of dart:async that is sufficient to create an
/// instance of [CoreTypes] and compile program.
const String defaultDartAsyncSource = """
-_asyncErrorWrapperHelper(continuation) {}
-
void _asyncStarMoveNextHelper(var stream) {}
-_asyncThenWrapperHelper(continuation) {}
-
-_awaitHelper(object, thenCallback, errorCallback) {}
-
-_completeOnAsyncReturn(_future, value, async_jump_var) {}
-
-_completeWithNoFutureOnAsyncReturn(_future, value, async_jump_var) {}
-
-_completeOnAsyncError(_future, e, st, async_jump_var) {}
-
-class _AsyncStarStreamController {
- add(event) {}
-
- addError(error, stackTrace) {}
-
- addStream(stream) {}
-
- close() {}
-
- get stream => null;
-}
-
abstract class Completer {
factory Completer.sync() => null;
diff --git a/pkg/front_end/lib/src/fasta/type_inference/inference_visitor.dart b/pkg/front_end/lib/src/fasta/type_inference/inference_visitor.dart
index 8b82433..5926dff 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/inference_visitor.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/inference_visitor.dart
@@ -4218,7 +4218,7 @@
{required bool isNot}) {
// ignore: unnecessary_null_comparison
assert(isNot != null);
- EqualityInfo<VariableDeclaration, DartType>? equalityInfo =
+ EqualityInfo<DartType>? equalityInfo =
flowAnalysis.equalityOperand_end(left, leftType);
bool typeNeeded = !isTopLevel;
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
index bb686bf..85db894 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
@@ -2456,7 +2456,7 @@
typeChecksNeeded);
}
- List<EqualityInfo<VariableDeclaration, DartType>?>? identicalInfo =
+ List<EqualityInfo<DartType>?>? identicalInfo =
isIdentical && arguments.positional.length == 2 ? [] : null;
int positionalIndex = 0;
int namedIndex = 0;
diff --git a/pkg/front_end/test/id_tests/assigned_variables_test.dart b/pkg/front_end/test/id_tests/assigned_variables_test.dart
index 23a5b09..53af2eb 100644
--- a/pkg/front_end/test/id_tests/assigned_variables_test.dart
+++ b/pkg/front_end/test/id_tests/assigned_variables_test.dart
@@ -74,8 +74,8 @@
_convertVars(_assignedVariables.capturedAnywhere));
}
- Set<String> _convertVars(Iterable<VariableDeclaration> x) =>
- x.map((e) => e.name!).toSet();
+ Set<String> _convertVars(Iterable<int> x) =>
+ x.map((e) => _assignedVariables.variableForKey(e).name!).toSet();
@override
_Data? computeNodeValue(Id id, TreeNode node) {
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index e401254..7105235e 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -1081,6 +1081,7 @@
reexports
ref
refactoring
+reference's
refined
reflect
reflectee
@@ -1496,6 +1497,7 @@
url
urls
usages
+user's
usr
usual
usually
diff --git a/pkg/front_end/testcases/general/issue38253c.dart.strong.transformed.expect b/pkg/front_end/testcases/general/issue38253c.dart.strong.transformed.expect
deleted file mode 100644
index eb54dc9..0000000
--- a/pkg/front_end/testcases/general/issue38253c.dart.strong.transformed.expect
+++ /dev/null
@@ -1,69 +0,0 @@
-library;
-//
-// Problems in library:
-//
-// pkg/front_end/testcases/general/issue38253c.dart:6:3: Error: 'g' isn't a type.
-// g f1() {}
-// ^
-//
-// pkg/front_end/testcases/general/issue38253c.dart:7:3: Error: 'g' isn't a type.
-// g f2() async {}
-// ^
-//
-import self as self;
-import "dart:async" as asy;
-import "dart:core" as core;
-
-static field () →* Null a = () → Null {
- function f1() → invalid-type {}
- function f2() → invalid-type /* originally async */ {
- final asy::_Future<dynamic>* :async_future = new asy::_Future::•<dynamic>();
- core::bool* :is_sync = false;
- FutureOr<dynamic>* :return_value;
- (dynamic) →* dynamic :async_op_then;
- (core::Object*, core::StackTrace*) →* dynamic :async_op_error;
- core::int* :await_jump_var = 0;
- dynamic :await_ctx_var;
- function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
- try {
- #L1:
- {}
- asy::_completeOnAsyncReturn(:async_future, :return_value, :is_sync);
- return;
- }
- on dynamic catch(dynamic exception, core::StackTrace* stack_trace) {
- asy::_completeOnAsyncError(:async_future, exception, stack_trace, :is_sync);
- }
- :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
- :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
- :async_op.call();
- :is_sync = true;
- return :async_future;
- }
- function f3() → core::int* {}
- function f4() → asy::Future<core::int*>* /* originally async */ {
- final asy::_Future<core::int*>* :async_future = new asy::_Future::•<core::int*>();
- core::bool* :is_sync = false;
- FutureOr<core::int*>* :return_value;
- (dynamic) →* dynamic :async_op_then;
- (core::Object*, core::StackTrace*) →* dynamic :async_op_error;
- core::int* :await_jump_var = 0;
- dynamic :await_ctx_var;
- function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
- try {
- #L2:
- {}
- asy::_completeOnAsyncReturn(:async_future, :return_value, :is_sync);
- return;
- }
- on dynamic catch(dynamic exception, core::StackTrace* stack_trace) {
- asy::_completeOnAsyncError(:async_future, exception, stack_trace, :is_sync);
- }
- :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
- :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
- :async_op.call();
- :is_sync = true;
- return :async_future;
- }
-};
-static method main() → dynamic {}
diff --git a/pkg/frontend_server/lib/frontend_server.dart b/pkg/frontend_server/lib/frontend_server.dart
index 5d7088c..9065163 100644
--- a/pkg/frontend_server/lib/frontend_server.dart
+++ b/pkg/frontend_server/lib/frontend_server.dart
@@ -58,8 +58,7 @@
help: 'Whether dart:mirrors is supported. By default dart:mirrors is '
'supported when --aot and --minimal-kernel are not used.',
defaultsTo: null)
- ..addFlag('compact-async',
- help: 'Enable new compact async/await implementation.', defaultsTo: true)
+ ..addFlag('compact-async', help: 'Obsolete, ignored.', hide: true)
..addFlag('tfa',
help:
'Enable global type flow analysis and related transformations in AOT mode.',
@@ -543,7 +542,6 @@
nullSafety: compilerOptions.nnbdMode == NnbdMode.Strong,
supportMirrors: options['support-mirrors'] ??
!(options['aot'] || options['minimal-kernel']),
- compactAsync: options['compact-async'],
);
if (compilerOptions.target == null) {
print('Failed to create front-end target ${options['target']}.');
diff --git a/pkg/kernel/binary.md b/pkg/kernel/binary.md
index 46ecd90..db46616 100644
--- a/pkg/kernel/binary.md
+++ b/pkg/kernel/binary.md
@@ -147,7 +147,7 @@
type ComponentFile {
UInt32 magic = 0x90ABCDEF;
- UInt32 formatVersion = 82;
+ UInt32 formatVersion = 83;
Byte[10] shortSdkHash;
List<String> problemsAsJson; // Described in problems.md.
Library[] libraries;
@@ -510,8 +510,7 @@
Sync,
SyncStar,
Async,
- AsyncStar,
- SyncYielding
+ AsyncStar
}
*/
@@ -1398,7 +1397,7 @@
type YieldStatement extends Statement {
Byte tag = 77;
FileOffset fileOffset;
- Byte flags (isYieldStar, isNative);
+ Byte flags (isYieldStar);
Expression expression;
}
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index e8a0d0e..74fe943 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -3956,43 +3956,6 @@
SyncStar,
Async,
AsyncStar,
-
- // `SyncYielding` is a marker that tells Dart VM that this function is an
- // artificial closure introduced by an async transformer which desugared all
- // async syntax into a combination of native yields and helper method calls.
- //
- // Native yields (formatted as `[yield]`) are semantically close to
- // `yield x` statement: they denote a yield/resume point within a function
- // but are completely decoupled from the notion of iterators. When
- // execution of the closure reaches `[yield] x` it stops and return the
- // value of `x` to the caller. If closure is called again it continues
- // to the next statement after this yield as if it was suspended and resumed.
- //
- // Consider this example:
- //
- // g() {
- // var :await_jump_var = 0;
- // var :await_ctx_var;
- //
- // f(x) yielding {
- // [yield] '${x}:0';
- // [yield] '${x}:1';
- // [yield] '${x}:2';
- // }
- //
- // return f;
- // }
- //
- // print(f('a')); /* prints 'a:0', :await_jump_var = 1 */
- // print(f('b')); /* prints 'b:1', :await_jump_var = 2 */
- // print(f('c')); /* prints 'c:2', :await_jump_var = 3 */
- //
- // Note: currently Dart VM implicitly relies on async transformer to
- // inject certain artificial variables into g (like `:await_jump_var`).
- // As such SyncYielding and native yield are not intended to be used on their
- // own, but are rather an implementation artifact of the async transformer
- // itself.
- SyncYielding,
}
// ------------------------------------------------------------------------
@@ -10523,33 +10486,23 @@
}
/// Statement of form `yield x` or `yield* x`.
-///
-/// For native yield semantics see `AsyncMarker.SyncYielding`.
class YieldStatement extends Statement {
Expression expression;
int flags = 0;
- YieldStatement(this.expression,
- {bool isYieldStar: false, bool isNative: false}) {
+ YieldStatement(this.expression, {bool isYieldStar: false}) {
expression.parent = this;
this.isYieldStar = isYieldStar;
- this.isNative = isNative;
}
static const int FlagYieldStar = 1 << 0;
- static const int FlagNative = 1 << 1;
bool get isYieldStar => flags & FlagYieldStar != 0;
- bool get isNative => flags & FlagNative != 0;
void set isYieldStar(bool value) {
flags = value ? (flags | FlagYieldStar) : (flags & ~FlagYieldStar);
}
- void set isNative(bool value) {
- flags = value ? (flags | FlagNative) : (flags & ~FlagNative);
- }
-
@override
R accept<R>(StatementVisitor<R> v) => v.visitYieldStatement(this);
diff --git a/pkg/kernel/lib/binary/ast_from_binary.dart b/pkg/kernel/lib/binary/ast_from_binary.dart
index a4fd2ba..f37d04d 100644
--- a/pkg/kernel/lib/binary/ast_from_binary.dart
+++ b/pkg/kernel/lib/binary/ast_from_binary.dart
@@ -2895,8 +2895,7 @@
int offset = readOffset();
int flags = readByte();
return new YieldStatement(readExpression(),
- isYieldStar: flags & YieldStatement.FlagYieldStar != 0,
- isNative: flags & YieldStatement.FlagNative != 0)
+ isYieldStar: flags & YieldStatement.FlagYieldStar != 0)
..fileOffset = offset;
}
diff --git a/pkg/kernel/lib/binary/tag.dart b/pkg/kernel/lib/binary/tag.dart
index 754bbd6..2e861b6 100644
--- a/pkg/kernel/lib/binary/tag.dart
+++ b/pkg/kernel/lib/binary/tag.dart
@@ -179,7 +179,7 @@
/// Internal version of kernel binary format.
/// Bump it when making incompatible changes in kernel binaries.
/// Keep in sync with runtime/vm/kernel_binary.h, pkg/kernel/binary.md.
- static const int BinaryFormatVersion = 82;
+ static const int BinaryFormatVersion = 83;
}
abstract class ConstantTag {
diff --git a/pkg/kernel/lib/core_types.dart b/pkg/kernel/lib/core_types.dart
index 70edb2c..1734950 100644
--- a/pkg/kernel/lib/core_types.dart
+++ b/pkg/kernel/lib/core_types.dart
@@ -119,57 +119,15 @@
CoreTypes(Component component)
: index = new LibraryIndex.coreLibraries(component);
- late final Procedure asyncErrorWrapperHelperProcedure =
- index.getTopLevelProcedure('dart:async', '_asyncErrorWrapperHelper');
-
late final Library asyncLibrary = index.getLibrary('dart:async');
- late final Procedure asyncStarStreamControllerAdd =
- index.getProcedure('dart:async', '_AsyncStarStreamController', 'add');
-
- late final Procedure asyncStarStreamControllerAddError = index.getProcedure(
- 'dart:async', '_AsyncStarStreamController', 'addError');
-
- late final Procedure asyncStarStreamControllerAddStream = index.getProcedure(
- 'dart:async', '_AsyncStarStreamController', 'addStream');
-
- late final Class asyncStarStreamControllerClass =
- index.getClass('dart:async', '_AsyncStarStreamController');
-
- late final Procedure asyncStarStreamControllerClose =
- index.getProcedure('dart:async', '_AsyncStarStreamController', 'close');
-
- late final Constructor asyncStarStreamControllerDefaultConstructor =
- index.getConstructor('dart:async', '_AsyncStarStreamController', '');
-
- late final Member asyncStarStreamControllerStream =
- index.getMember('dart:async', '_AsyncStarStreamController', 'get:stream');
-
late final Procedure asyncStarMoveNextHelper =
index.getTopLevelProcedure('dart:async', '_asyncStarMoveNextHelper');
- late final Procedure asyncThenWrapperHelperProcedure =
- index.getTopLevelProcedure('dart:async', '_asyncThenWrapperHelper');
-
- late final Procedure awaitHelperProcedure =
- index.getTopLevelProcedure('dart:async', '_awaitHelper');
-
late final Class boolClass = index.getClass('dart:core', 'bool');
late final Class futureImplClass = index.getClass('dart:async', '_Future');
- late final Constructor futureImplConstructor =
- index.getConstructor('dart:async', '_Future', '');
-
- late final Procedure completeOnAsyncReturn =
- index.getTopLevelProcedure('dart:async', '_completeOnAsyncReturn');
-
- late final Procedure completeWithNoFutureOnAsyncReturn = index
- .getTopLevelProcedure('dart:async', '_completeWithNoFutureOnAsyncReturn');
-
- late final Procedure completeOnAsyncError =
- index.getTopLevelProcedure('dart:async', '_completeOnAsyncError');
-
late final Library coreLibrary = index.getLibrary('dart:core');
late final Class doubleClass = index.getClass('dart:core', 'double');
@@ -287,18 +245,6 @@
late final Class symbolClass = index.getClass('dart:core', 'Symbol');
- late final Constructor syncIterableDefaultConstructor =
- index.getConstructor('dart:core', '_SyncIterable', '');
-
- late final Class syncIteratorClass =
- index.getClass('dart:core', '_SyncIterator');
-
- late final Member syncIteratorCurrent =
- index.getMember('dart:core', '_SyncIterator', '_current');
-
- late final Member syncIteratorYieldEachIterable =
- index.getMember('dart:core', '_SyncIterator', '_yieldEachIterable');
-
late final Class typeClass = index.getClass('dart:core', 'Type');
late final Constructor fallThroughErrorUrlAndLineConstructor =
diff --git a/pkg/kernel/lib/target/targets.dart b/pkg/kernel/lib/target/targets.dart
index c466238..477f255 100644
--- a/pkg/kernel/lib/target/targets.dart
+++ b/pkg/kernel/lib/target/targets.dart
@@ -16,13 +16,11 @@
final bool trackWidgetCreation;
final bool enableNullSafety;
final bool supportMirrors;
- final bool compactAsync;
const TargetFlags(
{this.trackWidgetCreation = false,
this.enableNullSafety = false,
- this.supportMirrors = true,
- this.compactAsync = true});
+ this.supportMirrors = true});
@override
bool operator ==(other) {
@@ -30,8 +28,7 @@
return other is TargetFlags &&
trackWidgetCreation == other.trackWidgetCreation &&
enableNullSafety == other.enableNullSafety &&
- supportMirrors == other.supportMirrors &&
- compactAsync == other.compactAsync;
+ supportMirrors == other.supportMirrors;
}
@override
@@ -40,7 +37,6 @@
hash = 0x3fffffff & (hash * 31 + (hash ^ trackWidgetCreation.hashCode));
hash = 0x3fffffff & (hash * 31 + (hash ^ enableNullSafety.hashCode));
hash = 0x3fffffff & (hash * 31 + (hash ^ supportMirrors.hashCode));
- hash = 0x3fffffff & (hash * 31 + (hash ^ compactAsync.hashCode));
return hash;
}
}
diff --git a/pkg/kernel/lib/text/ast_to_text.dart b/pkg/kernel/lib/text/ast_to_text.dart
index d7946d2..9b97a47 100644
--- a/pkg/kernel/lib/text/ast_to_text.dart
+++ b/pkg/kernel/lib/text/ast_to_text.dart
@@ -818,8 +818,6 @@
return 'async';
case AsyncMarker.AsyncStar:
return 'async*';
- case AsyncMarker.SyncYielding:
- return 'yielding';
default:
return '<Invalid async marker: $marker>';
}
@@ -2414,8 +2412,6 @@
writeIndentation();
if (node.isYieldStar) {
writeWord('yield*');
- } else if (node.isNative) {
- writeWord('[yield]');
} else {
writeWord('yield');
}
diff --git a/pkg/kernel/lib/type_checker.dart b/pkg/kernel/lib/type_checker.dart
index 38dec89..6f2a341 100644
--- a/pkg/kernel/lib/type_checker.dart
+++ b/pkg/kernel/lib/type_checker.dart
@@ -363,26 +363,6 @@
case AsyncMarker.AsyncStar:
return null;
- case AsyncMarker.SyncYielding:
- // The SyncStar transform wraps the original function body twice,
- // where the inner most function returns bool.
- TreeNode? parent = function.parent;
- while (parent is! FunctionNode) {
- parent = parent!.parent;
- }
- FunctionNode enclosingFunction = parent;
- if (enclosingFunction.dartAsyncMarker == AsyncMarker.Sync) {
- parent = enclosingFunction.parent;
- while (parent is! FunctionNode) {
- parent = parent!.parent;
- }
- enclosingFunction = parent;
- if (enclosingFunction.dartAsyncMarker == AsyncMarker.SyncStar) {
- return coreTypes.boolLegacyRawType;
- }
- }
- return null;
-
default:
throw 'Unexpected async marker: ${function.asyncMarker}';
}
@@ -405,9 +385,6 @@
}
return const DynamicType();
- case AsyncMarker.SyncYielding:
- return function.returnType;
-
default:
throw 'Unexpected async marker: ${function.asyncMarker}';
}
diff --git a/pkg/kernel/lib/verifier.dart b/pkg/kernel/lib/verifier.dart
index 20c670e..9202afa 100644
--- a/pkg/kernel/lib/verifier.dart
+++ b/pkg/kernel/lib/verifier.dart
@@ -528,7 +528,6 @@
switch (currentAsyncMarker) {
case AsyncMarker.Sync:
case AsyncMarker.Async:
- case AsyncMarker.SyncYielding:
// ok
break;
case AsyncMarker.SyncStar:
@@ -556,7 +555,6 @@
break;
case AsyncMarker.SyncStar:
case AsyncMarker.AsyncStar:
- case AsyncMarker.SyncYielding:
// ok
break;
}
diff --git a/pkg/nnbd_migration/lib/src/edge_builder.dart b/pkg/nnbd_migration/lib/src/edge_builder.dart
index d6bfd32..ba12bca 100644
--- a/pkg/nnbd_migration/lib/src/edge_builder.dart
+++ b/pkg/nnbd_migration/lib/src/edge_builder.dart
@@ -1595,7 +1595,7 @@
@override
DecoratedType? visitPrefixedIdentifier(PrefixedIdentifier node) {
- if (node.prefix.staticElement is ImportElement) {
+ if (node.prefix.staticElement is ImportElement2) {
// TODO(paulberry)
_unimplemented(node, 'PrefixedIdentifier with a prefix');
} else {
diff --git a/pkg/nnbd_migration/lib/src/fix_aggregator.dart b/pkg/nnbd_migration/lib/src/fix_aggregator.dart
index ddb14d6..1f1a071 100644
--- a/pkg/nnbd_migration/lib/src/fix_aggregator.dart
+++ b/pkg/nnbd_migration/lib/src/fix_aggregator.dart
@@ -60,11 +60,11 @@
FixAggregator._(this.planner, this._changes, this._warnOnWeakCode,
CompilationUnitElement compilationUnitElement) {
- for (var importElement in compilationUnitElement.library.imports) {
+ for (var importElement in compilationUnitElement.library.imports2) {
// TODO(paulberry): the `??=` should ensure that if there are two imports,
// one prefixed and one not, we prefer the prefix. Test this.
_importPrefixes[importElement.importedLibrary] ??=
- importElement.prefix?.name;
+ importElement.prefix?.element.name;
}
}
diff --git a/pkg/vm/lib/kernel_front_end.dart b/pkg/vm/lib/kernel_front_end.dart
index 6e70384..7ffdc7f 100644
--- a/pkg/vm/lib/kernel_front_end.dart
+++ b/pkg/vm/lib/kernel_front_end.dart
@@ -81,8 +81,7 @@
help: 'Whether dart:mirrors is supported. By default dart:mirrors is '
'supported when --aot and --minimal-kernel are not used.',
defaultsTo: null);
- args.addFlag('compact-async',
- help: 'Enable new compact async/await implementation.', defaultsTo: true);
+ args.addFlag('compact-async', help: 'Obsolete, ignored.', hide: true);
args.addOption('depfile', help: 'Path to output Ninja depfile');
args.addOption('from-dill',
help: 'Read existing dill file instead of compiling from sources',
@@ -203,7 +202,6 @@
final String? manifestFilename = options['manifest'];
final String? dataDir = options['component-name'] ?? options['data-dir'];
final bool? supportMirrors = options['support-mirrors'];
- final bool compactAsync = options['compact-async'];
final bool minimalKernel = options['minimal-kernel'];
final bool treeShakeWriteOnlyFields = options['tree-shake-write-only-fields'];
@@ -287,8 +285,7 @@
compilerOptions.target = createFrontEndTarget(targetName,
trackWidgetCreation: options['track-widget-creation'],
nullSafety: compilerOptions.nnbdMode == NnbdMode.Strong,
- supportMirrors: supportMirrors ?? !(aot || minimalKernel),
- compactAsync: compactAsync);
+ supportMirrors: supportMirrors ?? !(aot || minimalKernel));
if (compilerOptions.target == null) {
print('Failed to create front-end target $targetName.');
return badUsageExitCode;
@@ -617,16 +614,14 @@
Target? createFrontEndTarget(String targetName,
{bool trackWidgetCreation = false,
bool nullSafety = false,
- bool supportMirrors = true,
- bool compactAsync = true}) {
+ bool supportMirrors = true}) {
// Make sure VM-specific targets are available.
installAdditionalTargets();
final TargetFlags targetFlags = new TargetFlags(
trackWidgetCreation: trackWidgetCreation,
enableNullSafety: nullSafety,
- supportMirrors: supportMirrors,
- compactAsync: compactAsync);
+ supportMirrors: supportMirrors);
return getTarget(targetName, targetFlags);
}
diff --git a/pkg/vm/lib/target/vm.dart b/pkg/vm/lib/target/vm.dart
index b706bd5..74c4842 100644
--- a/pkg/vm/lib/target/vm.dart
+++ b/pkg/vm/lib/target/vm.dart
@@ -9,11 +9,8 @@
import 'package:kernel/reference_from_index.dart';
import 'package:kernel/target/changed_structure_notifier.dart';
import 'package:kernel/target/targets.dart';
-import 'package:kernel/type_environment.dart';
import '../transformations/call_site_annotator.dart' as callSiteAnnotator;
-import '../transformations/continuation.dart' as transformAsync
- show transformLibraries, transformProcedure;
import '../transformations/lowering.dart' as lowering
show transformLibraries, transformProcedure;
import '../transformations/mixin_full_resolution.dart' as transformMixins
@@ -183,15 +180,9 @@
logger?.call("Transformed ffi annotations");
}
- // TODO(kmillikin): Make this run on a per-method basis.
bool productMode = environmentDefines!["dart.vm.product"] == "true";
- transformAsync.transformLibraries(
- new TypeEnvironment(coreTypes, hierarchy), libraries,
- productMode: productMode, desugarAsync: !flags.compactAsync);
- logger?.call("Transformed async methods");
-
- lowering.transformLibraries(
- libraries, coreTypes, hierarchy, flags.enableNullSafety);
+ lowering.transformLibraries(libraries, coreTypes, hierarchy,
+ nullSafety: flags.enableNullSafety, productMode: productMode);
logger?.call("Lowering transformations performed");
callSiteAnnotator.transformLibraries(
@@ -207,13 +198,8 @@
Map<String, String>? environmentDefines,
{void Function(String msg)? logger}) {
bool productMode = environmentDefines!["dart.vm.product"] == "true";
- transformAsync.transformProcedure(
- new TypeEnvironment(coreTypes, hierarchy), procedure,
- productMode: productMode, desugarAsync: !flags.compactAsync);
- logger?.call("Transformed async functions");
-
- lowering.transformProcedure(
- procedure, coreTypes, hierarchy, flags.enableNullSafety);
+ lowering.transformProcedure(procedure, coreTypes, hierarchy,
+ nullSafety: flags.enableNullSafety, productMode: productMode);
logger?.call("Lowering transformations performed");
}
diff --git a/pkg/vm/lib/transformations/async.dart b/pkg/vm/lib/transformations/async.dart
deleted file mode 100644
index 6a6628b..0000000
--- a/pkg/vm/lib/transformations/async.dart
+++ /dev/null
@@ -1,672 +0,0 @@
-// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import 'package:kernel/kernel.dart';
-import 'package:kernel/type_environment.dart';
-import 'continuation.dart';
-
-/// A transformer that introduces temporary variables for all subexpressions
-/// that are alive across yield points (AwaitExpression).
-///
-/// The transformer is invoked by passing [rewrite] a top-level expression.
-///
-/// All intermediate values that are possible live across an await are named in
-/// local variables.
-///
-/// Await expressions are translated into a call to a helper function and a
-/// native yield.
-class ExpressionLifter extends Transformer {
- final AsyncRewriterBase continuationRewriter;
-
- /// Have we seen an await to the right in the expression tree.
- ///
- /// Subexpressions are visited right-to-left in the reverse of evaluation
- /// order.
- ///
- /// On entry to an expression's visit method, [seenAwait] indicates whether a
- /// sibling to the right contains an await. If so the expression will be
- /// named in a temporary variable because it is potentially live across an
- /// await.
- ///
- /// On exit from an expression's visit method, [seenAwait] indicates whether
- /// the expression itself or a sibling to the right contains an await.
- bool seenAwait = false;
-
- /// The (reverse order) sequence of statements that have been emitted.
- ///
- /// Transformation of an expression produces a transformed expression and a
- /// sequence of statements which are assignments to local variables, calls to
- /// helper functions, and yield points. Only the yield points need to be a
- /// statements, and they are statements so an implementation does not have to
- /// handle unnamed expression intermediate live across yield points.
- ///
- /// The visit methods return the transformed expression and build a sequence
- /// of statements by emitting statements into this list. This list is built
- /// in reverse because children are visited right-to-left.
- ///
- /// If an expression should be named it is named before visiting its children
- /// so the naming assignment appears in the list before all statements
- /// implementing the translation of the children.
- ///
- /// Children that are conditionally evaluated, such as some parts of logical
- /// and conditional expressions, must be delimited so that they do not emit
- /// unguarded statements into [statements]. This is implemented by setting
- /// [statements] to a fresh empty list before transforming those children.
- List<Statement> statements = <Statement>[];
-
- /// The number of currently live named intermediate values.
- ///
- /// This index is used to allocate names to temporary values. Because
- /// children are visited right-to-left, names are assigned in reverse order of
- /// index.
- ///
- /// When an assignment is emitted into [statements] to name an expression
- /// before visiting its children, the index is not immediately reserved
- /// because a child can freely use the same name as its parent. In practice,
- /// this will be the rightmost named child.
- ///
- /// After visiting the children of a named expression, [nameIndex] is set to
- /// indicate one more live value (the value of the expression) than before
- /// visiting the expression.
- ///
- /// After visiting the children of an expression that is not named,
- /// [nameIndex] may still account for names of subexpressions.
- int nameIndex = 0;
-
- final VariableDeclaration asyncResult =
- new VariableDeclaration(':result_or_exception');
- final List<VariableDeclaration> variables = <VariableDeclaration>[];
-
- ExpressionLifter(this.continuationRewriter);
-
- StatefulStaticTypeContext get _staticTypeContext =>
- continuationRewriter.staticTypeContext;
-
- Block blockOf(List<Statement> statements) {
- return new Block(statements.reversed.toList());
- }
-
- /// Rewrite a toplevel expression (toplevel wrt. a statement).
- ///
- /// Rewriting an expression produces a sequence of statements and an
- /// expression. The sequence of statements are added to the given list. Pass
- /// an empty list if the rewritten expression should be delimited from the
- /// surrounding context.
- Expression rewrite(Expression expression, List<Statement> outer) {
- assert(statements.isEmpty);
- var saved = seenAwait;
- seenAwait = false;
- Expression result = transform(expression);
- outer.addAll(statements.reversed);
- statements.clear();
- seenAwait = seenAwait || saved;
- return result;
- }
-
- // Perform an action with a given list of statements so that it cannot emit
- // statements into the 'outer' list.
- Expression delimit(Expression action(), List<Statement> inner) {
- var outer = statements;
- statements = inner;
- Expression result = action();
- statements = outer;
- return result;
- }
-
- // Wraps VariableGet in an unsafeCast if `type` isn't dynamic.
- Expression unsafeCastVariableGet(
- VariableDeclaration variable, DartType type) {
- if (type != const DynamicType()) {
- return StaticInvocation(
- continuationRewriter.helper.unsafeCast,
- Arguments(<Expression>[VariableGet(variable)],
- types: <DartType>[type]));
- }
- return VariableGet(variable);
- }
-
- // Name an expression by emitting an assignment to a temporary variable.
- Expression name(Expression expr) {
- DartType type = expr.getStaticType(_staticTypeContext);
- VariableDeclaration temp = allocateTemporary(nameIndex, type);
- statements.add(ExpressionStatement(VariableSet(temp, expr)));
- // Wrap in unsafeCast to make sure we pass type information even if we later
- // have to re-type the temporary variable to dynamic.
- return unsafeCastVariableGet(temp, type);
- }
-
- VariableDeclaration allocateTemporary(int index,
- [DartType type = const DynamicType()]) {
- if (variables.length > index) {
- // Re-type temporary to dynamic if we detect reuse with different type.
- // Note: We should make sure all uses use `unsafeCast(...)` to pass their
- // type information on, as that is lost otherwise.
- if (variables[index].type != const DynamicType() &&
- variables[index].type != type) {
- variables[index].type = const DynamicType();
- }
- return variables[index];
- }
- for (var i = variables.length; i <= index; i++) {
- variables.add(VariableDeclaration(":async_temporary_${i}", type: type));
- }
- return variables[index];
- }
-
- // Simple literals. These are pure expressions so they can be evaluated after
- // an await to their right.
- @override
- TreeNode visitSymbolLiteral(SymbolLiteral expr) => expr;
- @override
- TreeNode visitTypeLiteral(TypeLiteral expr) => expr;
- @override
- TreeNode visitThisExpression(ThisExpression expr) => expr;
- @override
- TreeNode visitStringLiteral(StringLiteral expr) => expr;
- @override
- TreeNode visitIntLiteral(IntLiteral expr) => expr;
- @override
- TreeNode visitDoubleLiteral(DoubleLiteral expr) => expr;
- @override
- TreeNode visitBoolLiteral(BoolLiteral expr) => expr;
- @override
- TreeNode visitNullLiteral(NullLiteral expr) => expr;
-
- // Nullary expressions with effects.
- Expression nullary(Expression expr) {
- if (seenAwait) {
- expr = name(expr);
- ++nameIndex;
- }
- return expr;
- }
-
- @override
- TreeNode visitSuperPropertyGet(SuperPropertyGet expr) => nullary(expr);
- @override
- TreeNode visitStaticGet(StaticGet expr) => nullary(expr);
- @override
- TreeNode visitStaticTearOff(StaticTearOff expr) => nullary(expr);
- @override
- TreeNode visitRethrow(Rethrow expr) => nullary(expr);
-
- // Getting a final or const variable is not an effect so it can be evaluated
- // after an await to its right.
- @override
- TreeNode visitVariableGet(VariableGet expr) {
- Expression result = expr;
- if (seenAwait && !expr.variable.isFinal && !expr.variable.isConst) {
- result = name(expr);
- ++nameIndex;
- }
- return result;
- }
-
- // Transform an expression given an action to transform the children. For
- // this purposes of the await transformer the children should generally be
- // translated from right to left, in the reverse of evaluation order.
- Expression transformTreeNode(Expression expr, void action()) {
- var shouldName = seenAwait;
-
- // 1. If there is an await in a sibling to the right, emit an assignment to
- // a temporary variable before transforming the children.
- var result = shouldName ? name(expr) : expr;
-
- // 2. Remember the number of live temporaries before transforming the
- // children.
- var index = nameIndex;
-
- // 3. Transform the children. Initially they do not have an await in a
- // sibling to their right.
- seenAwait = false;
- action();
-
- // 4. If the expression was named then the variables used for children are
- // no longer live but the variable used for the expression is.
- // On the other hand, a sibling to the left (yet to be processed) cannot
- // reuse any of the variables used here, as the assignments in the children
- // (here) would overwrite assignments in the siblings to the left,
- // possibly before the use of the overwritten values.
- if (shouldName) {
- if (index + 1 > nameIndex) nameIndex = index + 1;
- seenAwait = true;
- }
- return result;
- }
-
- // Unary expressions.
- Expression unary(Expression expr) {
- return transformTreeNode(expr, () {
- expr.transformChildren(this);
- });
- }
-
- @override
- TreeNode visitInvalidExpression(InvalidExpression expr) => unary(expr);
- @override
- TreeNode visitVariableSet(VariableSet expr) => unary(expr);
- @override
- TreeNode visitInstanceGet(InstanceGet expr) => unary(expr);
- @override
- TreeNode visitDynamicGet(DynamicGet expr) => unary(expr);
- @override
- TreeNode visitInstanceTearOff(InstanceTearOff expr) => unary(expr);
- @override
- TreeNode visitFunctionTearOff(FunctionTearOff expr) => unary(expr);
- @override
- TreeNode visitSuperPropertySet(SuperPropertySet expr) => unary(expr);
- @override
- TreeNode visitStaticSet(StaticSet expr) => unary(expr);
- @override
- TreeNode visitNot(Not expr) => unary(expr);
- @override
- TreeNode visitIsExpression(IsExpression expr) => unary(expr);
- @override
- TreeNode visitAsExpression(AsExpression expr) => unary(expr);
- @override
- TreeNode visitThrow(Throw expr) => unary(expr);
-
- @override
- TreeNode visitInstanceSet(InstanceSet expr) {
- return transformTreeNode(expr, () {
- expr.value = transform(expr.value)..parent = expr;
- expr.receiver = transform(expr.receiver)..parent = expr;
- });
- }
-
- @override
- TreeNode visitDynamicSet(DynamicSet expr) {
- return transformTreeNode(expr, () {
- expr.value = transform(expr.value)..parent = expr;
- expr.receiver = transform(expr.receiver)..parent = expr;
- });
- }
-
- @override
- TreeNode visitArguments(Arguments args) {
- for (var named in args.named.reversed) {
- named.value = transform(named.value)..parent = named;
- }
- var positional = args.positional;
- for (var i = positional.length - 1; i >= 0; --i) {
- positional[i] = transform(positional[i])..parent = args;
- }
- // Returns the arguments, which is assumed at the call sites because they do
- // not replace the arguments or set parent pointers.
- return args;
- }
-
- @override
- TreeNode visitInstanceInvocation(InstanceInvocation expr) {
- return transformTreeNode(expr, () {
- visitArguments(expr.arguments);
- expr.receiver = transform(expr.receiver)..parent = expr;
- });
- }
-
- @override
- TreeNode visitLocalFunctionInvocation(LocalFunctionInvocation expr) {
- return transformTreeNode(expr, () {
- visitArguments(expr.arguments);
- });
- }
-
- @override
- TreeNode visitDynamicInvocation(DynamicInvocation expr) {
- return transformTreeNode(expr, () {
- visitArguments(expr.arguments);
- expr.receiver = transform(expr.receiver)..parent = expr;
- });
- }
-
- @override
- TreeNode visitFunctionInvocation(FunctionInvocation expr) {
- return transformTreeNode(expr, () {
- visitArguments(expr.arguments);
- expr.receiver = transform(expr.receiver)..parent = expr;
- });
- }
-
- @override
- TreeNode visitEqualsNull(EqualsNull expr) => unary(expr);
-
- @override
- TreeNode visitEqualsCall(EqualsCall expr) {
- return transformTreeNode(expr, () {
- expr.right = transform(expr.right)..parent = expr;
- expr.left = transform(expr.left)..parent = expr;
- });
- }
-
- @override
- TreeNode visitSuperMethodInvocation(SuperMethodInvocation expr) {
- return transformTreeNode(expr, () {
- visitArguments(expr.arguments);
- });
- }
-
- @override
- TreeNode visitStaticInvocation(StaticInvocation expr) {
- return transformTreeNode(expr, () {
- visitArguments(expr.arguments);
- });
- }
-
- @override
- TreeNode visitConstructorInvocation(ConstructorInvocation expr) {
- return transformTreeNode(expr, () {
- visitArguments(expr.arguments);
- });
- }
-
- @override
- TreeNode visitStringConcatenation(StringConcatenation expr) {
- return transformTreeNode(expr, () {
- var expressions = expr.expressions;
- for (var i = expressions.length - 1; i >= 0; --i) {
- expressions[i] = transform(expressions[i])..parent = expr;
- }
- });
- }
-
- @override
- TreeNode visitListLiteral(ListLiteral expr) {
- return transformTreeNode(expr, () {
- var expressions = expr.expressions;
- for (var i = expressions.length - 1; i >= 0; --i) {
- expressions[i] = transform(expr.expressions[i])..parent = expr;
- }
- });
- }
-
- @override
- TreeNode visitMapLiteral(MapLiteral expr) {
- return transformTreeNode(expr, () {
- for (var entry in expr.entries.reversed) {
- entry.value = transform(entry.value)..parent = entry;
- entry.key = transform(entry.key)..parent = entry;
- }
- });
- }
-
- // Control flow.
- @override
- TreeNode visitLogicalExpression(LogicalExpression expr) {
- var shouldName = seenAwait;
-
- // Right is delimited because it is conditionally evaluated.
- var rightStatements = <Statement>[];
- seenAwait = false;
- expr.right = delimit(() => transform(expr.right), rightStatements)
- ..parent = expr;
- var rightAwait = seenAwait;
-
- if (rightStatements.isEmpty) {
- // Easy case: right did not emit any statements.
- seenAwait = shouldName;
- return transformTreeNode(expr, () {
- expr.left = transform(expr.left)..parent = expr;
- seenAwait = seenAwait || rightAwait;
- });
- }
-
- // If right has emitted statements we will produce a temporary t and emit
- // for && (there is an analogous case for ||):
- //
- // t = [left] == true;
- // if (t) {
- // t = [right] == true;
- // }
-
- // Recall that statements are emitted in reverse order, so first emit the if
- // statement, then the assignment of [left] == true, and then translate left
- // so any statements it emits occur after in the accumulated list (that is,
- // so they occur before in the corresponding block).
- var rightBody = blockOf(rightStatements);
- final type = _staticTypeContext.typeEnvironment.coreTypes
- .boolRawType(_staticTypeContext.nonNullable);
- final result = allocateTemporary(nameIndex, type);
- final objectEquals = continuationRewriter.helper.coreTypes.objectEquals;
- rightBody.addStatement(new ExpressionStatement(new VariableSet(
- result,
- new EqualsCall(expr.right, new BoolLiteral(true),
- interfaceTarget: objectEquals,
- functionType: objectEquals.getterType as FunctionType))));
- Statement then;
- Statement? otherwise;
- if (expr.operatorEnum == LogicalExpressionOperator.AND) {
- then = rightBody;
- otherwise = null;
- } else {
- then = new EmptyStatement();
- otherwise = rightBody;
- }
- statements.add(
- new IfStatement(unsafeCastVariableGet(result, type), then, otherwise));
-
- final test = new EqualsCall(expr.left, new BoolLiteral(true),
- interfaceTarget: objectEquals,
- functionType: objectEquals.getterType as FunctionType);
- statements.add(new ExpressionStatement(new VariableSet(result, test)));
-
- seenAwait = false;
- test.left = transform(test.left)..parent = test;
-
- ++nameIndex;
- seenAwait = seenAwait || rightAwait;
- return unsafeCastVariableGet(result, type);
- }
-
- @override
- TreeNode visitConditionalExpression(ConditionalExpression expr) {
- // Then and otherwise are delimited because they are conditionally
- // evaluated.
- var shouldName = seenAwait;
-
- final savedNameIndex = nameIndex;
-
- var thenStatements = <Statement>[];
- seenAwait = false;
- expr.then = delimit(() => transform(expr.then), thenStatements)
- ..parent = expr;
- var thenAwait = seenAwait;
-
- final thenNameIndex = nameIndex;
- nameIndex = savedNameIndex;
-
- var otherwiseStatements = <Statement>[];
- seenAwait = false;
- expr.otherwise =
- delimit(() => transform(expr.otherwise), otherwiseStatements)
- ..parent = expr;
- var otherwiseAwait = seenAwait;
-
- // Only one side of this branch will get executed at a time, so just make
- // sure we have enough temps for either, not both at the same time.
- if (thenNameIndex > nameIndex) {
- nameIndex = thenNameIndex;
- }
-
- if (thenStatements.isEmpty && otherwiseStatements.isEmpty) {
- // Easy case: neither then nor otherwise emitted any statements.
- seenAwait = shouldName;
- return transformTreeNode(expr, () {
- expr.condition = transform(expr.condition)..parent = expr;
- seenAwait = seenAwait || thenAwait || otherwiseAwait;
- });
- }
-
- // If `then` or `otherwise` has emitted statements we will produce a
- // temporary t and emit:
- //
- // if ([condition]) {
- // t = [left];
- // } else {
- // t = [right];
- // }
- final result = allocateTemporary(nameIndex, expr.staticType);
- var thenBody = blockOf(thenStatements);
- var otherwiseBody = blockOf(otherwiseStatements);
- thenBody.addStatement(
- new ExpressionStatement(new VariableSet(result, expr.then)));
- otherwiseBody.addStatement(
- new ExpressionStatement(new VariableSet(result, expr.otherwise)));
- var branch = new IfStatement(expr.condition, thenBody, otherwiseBody);
- statements.add(branch);
-
- seenAwait = false;
- branch.condition = transform(branch.condition)..parent = branch;
-
- ++nameIndex;
- seenAwait = seenAwait || thenAwait || otherwiseAwait;
- return unsafeCastVariableGet(result, expr.staticType);
- }
-
- // Others.
- @override
- TreeNode visitAwaitExpression(AwaitExpression expr) {
- final R = continuationRewriter;
- var shouldName = seenAwait;
- var type = expr.getStaticType(_staticTypeContext);
- Expression result = unsafeCastVariableGet(asyncResult, type);
-
- // The statements are in reverse order, so name the result first if
- // necessary and then add the two other statements in reverse.
- if (shouldName) result = name(result);
- Arguments arguments = new Arguments(<Expression>[
- expr.operand,
- new VariableGet(R.thenContinuationVariable),
- new VariableGet(R.catchErrorContinuationVariable),
- ]);
-
- // We are building
- //
- // [yield] (let _ = _awaitHelper(...) in null)
- //
- // to ensure that :await_jump_var and :await_jump_ctx are updated
- // before _awaitHelper is invoked (see BuildYieldStatement in
- // StreamingFlowGraphBuilder for details of how [yield] is translated to
- // IL). This guarantees that recursive invocation of the current function
- // would continue from the correct "jump" position. Recursive invocations
- // arise if future we are awaiting completes synchronously. Builtin Future
- // implementation don't complete synchronously, but Flutter's
- // SynchronousFuture do (see bug http://dartbug.com/32098 for more details).
- statements.add(R.createContinuationPoint(new Let(
- new VariableDeclaration(null,
- initializer: new StaticInvocation(R.helper.awaitHelper, arguments)
- ..fileOffset = expr.fileOffset),
- new NullLiteral()))
- ..fileOffset = expr.fileOffset);
-
- seenAwait = false;
- var index = nameIndex;
- arguments.positional[0] = transform(expr.operand)..parent = arguments;
-
- if (shouldName && index + 1 > nameIndex) nameIndex = index + 1;
- seenAwait = true;
- return result;
- }
-
- @override
- TreeNode visitFunctionExpression(FunctionExpression expr) {
- expr.transformChildren(this);
- return expr;
- }
-
- @override
- TreeNode visitLet(Let expr) {
- var body = transform(expr.body);
-
- VariableDeclaration variable = expr.variable;
- if (seenAwait) {
- // There is an await in the body of `let var x = initializer in body` or
- // to its right. We will produce the sequence of statements:
- //
- // <initializer's statements>
- // var x = <initializer's value>
- // <body's statements>
- //
- // and return the body's value.
- //
- // So x is in scope for all the body's statements and the body's value.
- // This has the unpleasant consequence that all let-bound variables with
- // await in the let's body will end up hoisted out of the expression and
- // allocated to the context in the VM, even if they have no uses
- // (`let _ = e0 in e1` can be used for sequencing of `e0` and `e1`).
- statements.add(variable);
- var index = nameIndex;
- seenAwait = false;
- variable.initializer = transform(variable.initializer!)
- ..parent = variable;
- // Temporaries used in the initializer or the body are not live but the
- // temporary used for the body is.
- if (index + 1 > nameIndex) nameIndex = index + 1;
- seenAwait = true;
- return body;
- } else {
- // The body in `let x = initializer in body` did not contain an await. We
- // can leave a let expression.
- return transformTreeNode(expr, () {
- // The body has already been translated.
- expr.body = body..parent = expr;
- variable.initializer = transform(variable.initializer!)
- ..parent = variable;
- });
- }
- }
-
- @override
- TreeNode visitFunctionNode(FunctionNode node) {
- var nestedRewriter = new RecursiveContinuationRewriter(
- continuationRewriter.helper,
- _staticTypeContext,
- continuationRewriter.desugarAsync);
- return nestedRewriter.transform(node);
- }
-
- @override
- TreeNode visitBlockExpression(BlockExpression expr) {
- return transformTreeNode(expr, () {
- expr.value = transform(expr.value)..parent = expr;
- List<Statement> body = <Statement>[];
- for (Statement stmt in expr.body.statements.reversed) {
- Statement? translation = _rewriteStatement(stmt);
- if (translation != null) body.add(translation);
- }
- expr.body = new Block(body.reversed.toList())..parent = expr;
- });
- }
-
- Statement? _rewriteStatement(Statement stmt) {
- // This method translates a statement nested in an expression (e.g., in a
- // block expression). It produces a translated statement, a list of
- // statements which are side effects necessary for any await, and a flag
- // indicating whether there was an await in the statement or to its right.
- // The translated statement can be null in the case where there was already
- // an await to the right.
-
- // The translation is accumulating two lists of statements, an inner list
- // which is a reversed list of effects needed for the current expression and
- // an outer list which represents the block containing the current
- // statement. We need to preserve both of those from side effects.
- List<Statement> savedInner = statements;
- List<Statement> savedOuter = continuationRewriter.statements;
- statements = <Statement>[];
- continuationRewriter.statements = <Statement>[];
- continuationRewriter.transform(stmt);
-
- List<Statement> results = continuationRewriter.statements;
- statements = savedInner;
- continuationRewriter.statements = savedOuter;
- if (!seenAwait && results.length == 1) return results.first;
- statements.addAll(results.reversed);
- return null;
- }
-
- @override
- TreeNode defaultStatement(Statement stmt) {
- throw new UnsupportedError(
- "Use _rewriteStatement to transform statement: ${stmt}");
- }
-}
diff --git a/pkg/vm/lib/transformations/continuation.dart b/pkg/vm/lib/transformations/continuation.dart
deleted file mode 100644
index d23954f..0000000
--- a/pkg/vm/lib/transformations/continuation.dart
+++ /dev/null
@@ -1,1667 +0,0 @@
-// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import 'dart:math' as math;
-
-import 'package:kernel/ast.dart';
-import 'package:kernel/core_types.dart';
-import 'package:kernel/type_algebra.dart' show Substitution;
-import 'package:kernel/type_environment.dart';
-
-import 'async.dart';
-
-class ContinuationVariables {
- static const awaitJumpVar = ':await_jump_var';
- static const asyncFuture = ':async_future';
- static const isSync = ":is_sync";
- static const awaitContextVar = ':await_ctx_var';
- static const asyncCompleter = ':async_completer';
- static const asyncOp = ':async_op';
- static const asyncOpThen = ':async_op_then';
- static const asyncOpError = ':async_op_error';
- static const controller = ':controller';
- static const controllerStreamVar = ':controller_stream';
- static const forIterator = ':for-iterator';
- static const returnValue = ':return_value';
- static const stream = ':stream';
- static const syncForIterator = ':sync-for-iterator';
- static const syncOpGen = ':sync_op_gen';
- static const syncOp = ':sync_op';
- // sync_op(..) parameter.
- static const iteratorParam = ':iterator';
- // (a)sync_op(..) parameters.
- static const exceptionParam = ':exception';
- static const stackTraceParam = ':stack_trace';
-
- static const savedTryContextVarPrefix = ':saved_try_context_var';
- static const exceptionVarPrefix = ':exception';
- static const stackTraceVarPrefix = ':stack_trace';
-
- static String savedTryContextVar(int depth) =>
- '$savedTryContextVarPrefix$depth';
- static String exceptionVar(int depth) => '$exceptionVarPrefix$depth';
- static String stackTraceVar(int depth) => '$stackTraceVarPrefix$depth';
-}
-
-void transformLibraries(
- TypeEnvironment typeEnvironment, List<Library> libraries,
- {required bool productMode, required bool desugarAsync}) {
- var helper =
- new HelperNodes.fromCoreTypes(typeEnvironment.coreTypes, productMode);
- var rewriter = new RecursiveContinuationRewriter(helper,
- new StatefulStaticTypeContext.stacked(typeEnvironment), desugarAsync);
- for (var library in libraries) {
- rewriter.rewriteLibrary(library);
- }
-}
-
-Component transformComponent(
- TypeEnvironment typeEnvironment, Component component,
- {required bool productMode, required bool desugarAsync}) {
- var helper =
- new HelperNodes.fromCoreTypes(typeEnvironment.coreTypes, productMode);
- var rewriter = new RecursiveContinuationRewriter(helper,
- new StatefulStaticTypeContext.stacked(typeEnvironment), desugarAsync);
- return rewriter.rewriteComponent(component);
-}
-
-Procedure transformProcedure(
- TypeEnvironment typeEnvironment, Procedure procedure,
- {required bool productMode, required bool desugarAsync}) {
- var helper =
- new HelperNodes.fromCoreTypes(typeEnvironment.coreTypes, productMode);
- var rewriter = new RecursiveContinuationRewriter(helper,
- new StatefulStaticTypeContext.stacked(typeEnvironment), desugarAsync);
- return rewriter.transform(procedure);
-}
-
-class RecursiveContinuationRewriter extends RemovingTransformer {
- final HelperNodes helper;
-
- final VariableDeclaration awaitJumpVariable = new VariableDeclaration(
- ContinuationVariables.awaitJumpVar,
- initializer: new IntLiteral(0));
- final VariableDeclaration awaitContextVariable =
- new VariableDeclaration(ContinuationVariables.awaitContextVar);
-
- StatefulStaticTypeContext staticTypeContext;
- final bool desugarAsync;
- final bool desugarAwaitFor;
-
- RecursiveContinuationRewriter(
- this.helper, this.staticTypeContext, this.desugarAsync,
- {this.desugarAwaitFor = false});
-
- Component rewriteComponent(Component node) {
- return transform(node);
- }
-
- Library rewriteLibrary(Library node) {
- return transform(node);
- }
-
- @override
- TreeNode visitField(Field node, TreeNode? removalSentinel) {
- staticTypeContext.enterMember(node);
- final result = super.visitField(node, removalSentinel);
- staticTypeContext.leaveMember(node);
- return result;
- }
-
- @override
- TreeNode visitConstructor(Constructor node, TreeNode? removalSentinel) {
- staticTypeContext.enterMember(node);
- final result = super.visitConstructor(node, removalSentinel);
- staticTypeContext.leaveMember(node);
- return result;
- }
-
- @override
- TreeNode visitProcedure(Procedure node, TreeNode? removalSentinel) {
- staticTypeContext.enterMember(node);
- final result =
- node.isAbstract ? node : super.visitProcedure(node, removalSentinel);
- staticTypeContext.leaveMember(node);
- return result;
- }
-
- @override
- TreeNode visitLibrary(Library node, TreeNode? removalSentinel) {
- staticTypeContext.enterLibrary(node);
- Library result = super.visitLibrary(node, removalSentinel) as Library;
- staticTypeContext.leaveLibrary(node);
- return result;
- }
-
- @override
- TreeNode visitFunctionNode(FunctionNode node, TreeNode? removalSentinel) {
- switch (node.asyncMarker) {
- case AsyncMarker.Sync:
- case AsyncMarker.SyncYielding:
- node.transformOrRemoveChildren(new RecursiveContinuationRewriter(
- helper, staticTypeContext, desugarAsync));
- return node;
- case AsyncMarker.SyncStar:
- if (desugarAsync) {
- return new SyncStarFunctionRewriter(
- helper, node, staticTypeContext, desugarAsync)
- .rewrite();
- } else {
- node.transformOrRemoveChildren(new RecursiveContinuationRewriter(
- helper, staticTypeContext, desugarAsync));
- return node;
- }
- case AsyncMarker.Async:
- if (desugarAsync) {
- return new AsyncFunctionRewriter(
- helper, node, staticTypeContext, desugarAsync)
- .rewrite();
- } else {
- node.transformOrRemoveChildren(new RecursiveContinuationRewriter(
- helper, staticTypeContext, desugarAsync,
- desugarAwaitFor: true));
- return node;
- }
- case AsyncMarker.AsyncStar:
- if (desugarAsync) {
- return new AsyncStarFunctionRewriter(
- helper, node, staticTypeContext, desugarAsync)
- .rewrite();
- } else {
- node.transformOrRemoveChildren(new RecursiveContinuationRewriter(
- helper, staticTypeContext, desugarAsync,
- desugarAwaitFor: true));
- return node;
- }
- }
- }
-
- @override
- TreeNode visitForInStatement(ForInStatement stmt, TreeNode? removalSentinel) {
- if (stmt.isAsync) {
- if (!desugarAwaitFor) {
- return super.visitForInStatement(stmt, removalSentinel);
- }
- // Transform
- //
- // await for (T variable in <stream-expression>) { ... }
- //
- // To (in product mode):
- //
- // {
- // :stream = <stream-expression>;
- // _StreamIterator<T> :for-iterator = new _StreamIterator<T>(:stream);
- // try {
- // while (await :for-iterator.moveNext()) {
- // T <variable> = :for-iterator.current;
- // ...
- // }
- // } finally {
- // if (:for-iterator._subscription != null)
- // await :for-iterator.cancel();
- // }
- // }
- //
- // Or (in non-product mode):
- //
- // {
- // :stream = <stream-expression>;
- // _StreamIterator<T> :for-iterator = new _StreamIterator<T>(:stream);
- // try {
- // while (let _ = _asyncStarMoveNextHelper(:stream) in
- // await :for-iterator.moveNext()) {
- // T <variable> = :for-iterator.current;
- // ...
- // }
- // } finally {
- // if (:for-iterator._subscription != null)
- // await :for-iterator.cancel();
- // }
- // }
- var valueVariable = stmt.variable;
-
- var streamVariable = new VariableDeclaration(ContinuationVariables.stream,
- initializer: stmt.iterable,
- type: stmt.iterable.getStaticType(staticTypeContext));
-
- final streamIteratorType = new InterfaceType(helper.streamIteratorClass,
- staticTypeContext.nullable, [valueVariable.type]);
- var forIteratorVariable = VariableDeclaration(
- ContinuationVariables.forIterator,
- initializer: new ConstructorInvocation(
- helper.streamIteratorConstructor,
- new Arguments(<Expression>[new VariableGet(streamVariable)],
- types: [valueVariable.type])),
- type: streamIteratorType);
-
- // await :for-iterator.moveNext()
- var condition = new AwaitExpression(new InstanceInvocation(
- InstanceAccessKind.Instance,
- VariableGet(forIteratorVariable),
- helper.streamIteratorMoveNext.name,
- new Arguments([]),
- interfaceTarget: helper.streamIteratorMoveNext,
- functionType:
- helper.streamIteratorMoveNext.getterType as FunctionType))
- ..fileOffset = stmt.fileOffset;
-
- Expression whileCondition;
- if (helper.productMode) {
- whileCondition = condition;
- } else {
- // _asyncStarMoveNextHelper(:stream)
- var asyncStarMoveNextCall = new StaticInvocation(
- helper.asyncStarMoveNextHelper,
- new Arguments([new VariableGet(streamVariable)]))
- ..fileOffset = stmt.fileOffset;
-
- // let _ = asyncStarMoveNextCall in (condition)
- whileCondition = new Let(
- new VariableDeclaration(null, initializer: asyncStarMoveNextCall),
- condition);
- }
-
- // T <variable> = :for-iterator.current;
- valueVariable.initializer = new InstanceGet(InstanceAccessKind.Instance,
- VariableGet(forIteratorVariable), helper.streamIteratorCurrent.name,
- interfaceTarget: helper.streamIteratorCurrent,
- resultType: valueVariable.type)
- ..fileOffset = stmt.bodyOffset;
- valueVariable.initializer!.parent = valueVariable;
-
- var whileBody = new Block(<Statement>[valueVariable, stmt.body]);
- var tryBody = new WhileStatement(whileCondition, whileBody);
-
- // if (:for-iterator._subscription != null) await :for-iterator.cancel();
- final DartType subscriptionType =
- Substitution.fromInterfaceType(streamIteratorType).substituteType(
- helper.coreTypes.streamIteratorSubscription.getterType);
- var tryFinalizer = new IfStatement(
- new Not(new EqualsNull(new InstanceGet(
- InstanceAccessKind.Instance,
- VariableGet(forIteratorVariable),
- helper.coreTypes.streamIteratorSubscription.name,
- interfaceTarget: helper.coreTypes.streamIteratorSubscription,
- resultType: subscriptionType))),
- new ExpressionStatement(new AwaitExpression(new InstanceInvocation(
- InstanceAccessKind.Instance,
- VariableGet(forIteratorVariable),
- helper.streamIteratorCancel.name,
- new Arguments(<Expression>[]),
- interfaceTarget: helper.streamIteratorCancel,
- functionType:
- helper.streamIteratorCancel.getterType as FunctionType))),
- null);
-
- var tryFinally = new TryFinally(tryBody, tryFinalizer);
-
- var block = new Block(
- <Statement>[streamVariable, forIteratorVariable, tryFinally]);
- return transform(block);
- }
-
- // Transform
- //
- // for ({var/final} T <variable> in <iterable>) { ... }
- //
- // Into
- //
- // {
- // final Iterator<T> :sync-for-iterator = <iterable>.iterator;
- // for (; :sync-for-iterator.moveNext() ;) {
- // {var/final} T variable = :sync-for-iterator.current;
- // ...
- // }
- // }
- // }
- final CoreTypes coreTypes = staticTypeContext.typeEnvironment.coreTypes;
-
- // The CFE might invoke this transformation despite the program having
- // compile-time errors. So we will not transform this [stmt] if the
- // `stmt.iterable` is an invalid expression or has an invalid type and
- // instead eliminate the entire for-in and replace it with a invalid
- // expression statement.
- final iterable = stmt.iterable;
- final iterableType = iterable.getStaticType(staticTypeContext);
- if (iterableType is InvalidType) {
- return ExpressionStatement(
- InvalidExpression('Invalid iterable type in for-in'));
- }
-
- // The NNBD sdk declares that Iterable.get:iterator returns a non-nullable
- // `Iterator<E>`.
- assert(const [
- Nullability.nonNullable,
- Nullability.legacy
- ].contains(coreTypes.iterableGetIterator.function.returnType.nullability));
-
- final DartType elementType = stmt.getElementType(staticTypeContext);
- final iteratorType = InterfaceType(
- coreTypes.iteratorClass, staticTypeContext.nonNullable, [elementType]);
-
- final syncForIterator = VariableDeclaration(
- ContinuationVariables.syncForIterator,
- initializer: InstanceGet(InstanceAccessKind.Instance, iterable,
- coreTypes.iterableGetIterator.name,
- interfaceTarget: coreTypes.iterableGetIterator,
- resultType: iteratorType)
- ..fileOffset = iterable.fileOffset,
- type: iteratorType)
- ..fileOffset = iterable.fileOffset;
-
- final condition = InstanceInvocation(
- InstanceAccessKind.Instance,
- VariableGet(syncForIterator),
- coreTypes.iteratorMoveNext.name,
- Arguments([]),
- interfaceTarget: coreTypes.iteratorMoveNext,
- functionType: coreTypes.iteratorMoveNext.getterType as FunctionType)
- ..fileOffset = iterable.fileOffset;
-
- final variable = stmt.variable
- ..initializer = (InstanceGet(InstanceAccessKind.Instance,
- VariableGet(syncForIterator), coreTypes.iteratorGetCurrent.name,
- interfaceTarget: coreTypes.iteratorGetCurrent,
- resultType: elementType)
- ..fileOffset = stmt.bodyOffset);
-
- final Block body = Block([variable, stmt.body])
- ..fileOffset = stmt.bodyOffset;
-
- return transform(
- Block([syncForIterator, ForStatement([], condition, [], body)]));
- }
-}
-
-abstract class ContinuationRewriterBase extends RecursiveContinuationRewriter {
- final FunctionNode enclosingFunction;
-
- int currentTryDepth = 0; // Nesting depth for try-blocks.
- int currentCatchDepth = 0; // Nesting depth for catch-blocks.
- int capturedTryDepth = 0; // Deepest yield point within a try-block.
- int capturedCatchDepth = 0; // Deepest yield point within a catch-block.
-
- ContinuationRewriterBase(HelperNodes helper, this.enclosingFunction,
- StatefulStaticTypeContext staticTypeContext, bool desugarAsync,
- {bool desugarAwaitFor = false})
- : super(helper, staticTypeContext, desugarAsync,
- desugarAwaitFor: desugarAwaitFor);
-
- /// Given a container [type], which is an instantiation of the given
- /// [containerClass] extract its element type.
- ///
- /// This is used to extract element type from Future<T>, Iterable<T> and
- /// Stream<T> instantiations.
- ///
- /// If instantiation is not valid (has more than 1 type argument) then
- /// this function returns [InvalidType].
- static DartType elementTypeFrom(Class containerClass, DartType type) {
- if (type is InterfaceType) {
- if (type.classNode == containerClass) {
- if (type.typeArguments.isEmpty) {
- return const DynamicType();
- } else if (type.typeArguments.length == 1) {
- return type.typeArguments[0];
- } else {
- return const InvalidType();
- }
- }
- }
- return const DynamicType();
- }
-
- static DartType elementTypeFromFutureOr(DartType type) {
- if (type is FutureOrType) {
- return type.typeArgument;
- }
- return const DynamicType();
- }
-
- DartType elementTypeFromReturnType(Class expected) =>
- elementTypeFrom(expected, enclosingFunction.returnType);
-
- DartType elementTypeFromAsyncReturnType() =>
- elementTypeFromFutureOr(enclosingFunction.returnType);
-
- Statement createContinuationPoint([Expression? value]) {
- if (value == null) value = new NullLiteral();
- capturedTryDepth = math.max(capturedTryDepth, currentTryDepth);
- capturedCatchDepth = math.max(capturedCatchDepth, currentCatchDepth);
- return new YieldStatement(value, isNative: true);
- }
-
- @override
- TreeNode visitTryCatch(TryCatch node, TreeNode? removalSentinel) {
- // ignore: unnecessary_null_comparison
- if (node.body != null) {
- ++currentTryDepth;
- node.body = transform(node.body);
- node.body.parent = node;
- --currentTryDepth;
- }
-
- ++currentCatchDepth;
- transformCatchList(node.catches, node);
- --currentCatchDepth;
- return node;
- }
-
- @override
- TreeNode visitTryFinally(TryFinally node, TreeNode? removalSentinel) {
- // ignore: unnecessary_null_comparison
- if (node.body != null) {
- ++currentTryDepth;
- node.body = transform(node.body);
- node.body.parent = node;
- --currentTryDepth;
- }
- // ignore: unnecessary_null_comparison
- if (node.finalizer != null) {
- ++currentCatchDepth;
- node.finalizer = transform(node.finalizer);
- node.finalizer.parent = node;
- --currentCatchDepth;
- }
- return node;
- }
-
- Iterable<VariableDeclaration> createCapturedTryVariables() =>
- new Iterable.generate(
- capturedTryDepth,
- (depth) => new VariableDeclaration(
- ContinuationVariables.savedTryContextVar(depth)));
-
- Iterable<VariableDeclaration> createCapturedCatchVariables() =>
- new Iterable.generate(capturedCatchDepth).expand((depth) => [
- new VariableDeclaration(ContinuationVariables.exceptionVar(depth)),
- new VariableDeclaration(ContinuationVariables.stackTraceVar(depth)),
- ]);
-
- List<VariableDeclaration> variableDeclarations() {
- awaitJumpVariable.type = staticTypeContext.typeEnvironment.coreTypes
- .intRawType(staticTypeContext.nonNullable);
- return [awaitJumpVariable, awaitContextVariable]
- ..addAll(createCapturedTryVariables())
- ..addAll(createCapturedCatchVariables());
- }
-}
-
-// Transformer that rewrites all variable references to a given function's
-// parameters.
-// This allows us to e.g. "shadow" the original parameter variables with copies
-// unique to given sub-closure to prevent shared variables being overwritten.
-class ShadowRewriter extends Transformer {
- final FunctionNode enclosingFunction;
- Map<VariableDeclaration, VariableDeclaration?> _shadowedParameters = {};
-
- ShadowRewriter(this.enclosingFunction) {
- for (final parameter in enclosingFunction.positionalParameters
- .followedBy(enclosingFunction.namedParameters)) {
- // Put in placeholders so we can allocate new variables lazily- i.e. only
- // if they're later referenced.
- _shadowedParameters[parameter] = null;
- }
- }
-
- // Return all used parameters.
- Iterable<VariableDeclaration> get shadowedParameters =>
- _shadowedParameters.values.whereType<VariableDeclaration>();
-
- VariableDeclaration _rewrite(VariableDeclaration variable) {
- if (_shadowedParameters.containsKey(variable)) {
- // Fill in placeholder.
- VariableDeclaration? placeholder = _shadowedParameters[variable];
- if (placeholder == null) {
- placeholder = _shadowedParameters[variable] = VariableDeclaration(
- variable.name,
- type: variable.type,
- initializer: VariableGet(variable),
- );
- }
- variable = placeholder;
- }
- return variable;
- }
-
- @override
- TreeNode visitVariableGet(VariableGet node) {
- node = super.visitVariableGet(node) as VariableGet;
- return node..variable = _rewrite(node.variable);
- }
-
- @override
- TreeNode visitVariableSet(VariableSet node) {
- node = super.visitVariableSet(node) as VariableSet;
- return node..variable = _rewrite(node.variable);
- }
-}
-
-class SyncStarFunctionRewriter extends ContinuationRewriterBase {
- final VariableDeclaration iteratorParameter;
-
- SyncStarFunctionRewriter(HelperNodes helper, FunctionNode enclosingFunction,
- StatefulStaticTypeContext staticTypeContext, bool desugarAsync)
- : iteratorParameter =
- VariableDeclaration(ContinuationVariables.iteratorParam)
- ..type = InterfaceType(
- helper.syncIteratorClass, staticTypeContext.nullable, [
- // Note: This is dynamic since nested iterators (of potentially
- // different type) are handled by shared internal synthetic
- // code.
- const DynamicType(),
- ]),
- super(helper, enclosingFunction, staticTypeContext, desugarAsync,
- desugarAwaitFor: false);
-
- FunctionNode rewrite() {
- // We need to preserve the original parameters passed to the sync* function
- // because each iteration should start from those parameters. To achieve
- // this we shadow the original parameters with new variables (which are
- // initialised to the original parameter values) and rewrite
- // the body to use these variables instead.
- final shadowRewriter = ShadowRewriter(enclosingFunction);
- enclosingFunction.body = shadowRewriter.transform(enclosingFunction.body!)
- ..parent = enclosingFunction;
-
- // TODO(cskau): Figure out why inlining this below causes segfaults.
- // Maybe related to http://dartbug.com/41596 ?
- final syncOpFN = FunctionNode(buildClosureBody(),
- positionalParameters: [
- iteratorParameter,
- new VariableDeclaration(ContinuationVariables.exceptionParam),
- new VariableDeclaration(ContinuationVariables.stackTraceParam),
- ],
- requiredParameterCount: 3,
- // Note: SyncYielding functions have no Dart equivalent. Since they are
- // synchronous, we use Sync. (Note also that the Dart VM backend uses
- // the Dart async marker to decide if functions are debuggable.)
- asyncMarker: AsyncMarker.SyncYielding,
- dartAsyncMarker: AsyncMarker.Sync,
- returnType: helper.coreTypes.boolLegacyRawType)
- ..fileOffset = enclosingFunction.fileOffset
- ..fileEndOffset = enclosingFunction.fileEndOffset;
- final syncOpType =
- syncOpFN.computeThisFunctionType(staticTypeContext.nonNullable);
-
- final syncOpGenVariable = VariableDeclaration(
- ContinuationVariables.syncOpGen,
- type: FunctionType([], syncOpType, staticTypeContext.nonNullable));
-
- final syncOpVariable = VariableDeclaration(ContinuationVariables.syncOp);
- final syncOpDecl = FunctionDeclaration(syncOpVariable, syncOpFN)
- ..fileOffset = enclosingFunction.fileOffset;
-
- enclosingFunction.body = Block([
- // :sync_op_gen() {
- // :await_jump_var;
- // :await_ctx_var;
- // bool sync_op(:iterator, e, st) yielding {
- // modified <node.body> ...
- // };
- // return sync_op;
- // }
- FunctionDeclaration(
- syncOpGenVariable,
- FunctionNode(
- Block([
- // :await_jump_var, :await_ctx_var.
- ...variableDeclarations(),
- // Shadow any used function parameters with local copies.
- ...shadowRewriter.shadowedParameters,
- // :sync_op(..) { .. }
- syncOpDecl,
- // return sync_op;
- ReturnStatement(VariableGet(syncOpVariable)),
- ]),
- returnType: syncOpType))
- ..fileOffset = enclosingFunction.fileOffset,
-
- // return _SyncIterable<T>(:sync_op_gen);
- ReturnStatement(ConstructorInvocation(
- helper.syncIterableConstructor,
- Arguments([
- VariableGet(syncOpGenVariable)
- ], types: [
- ContinuationRewriterBase.elementTypeFrom(
- helper.iterableClass, enclosingFunction.returnType)
- ]))),
- ])
- ..parent = enclosingFunction;
- enclosingFunction.asyncMarker = AsyncMarker.Sync;
-
- return enclosingFunction;
- }
-
- Statement buildClosureBody() {
- // The body will insert calls to
- // :iterator.current_=
- // :iterator.isYieldEach=
- // and return `true` as long as it did something and `false` when it's done.
- return new Block(<Statement>[
- transform(enclosingFunction.body!),
- new ReturnStatement(new BoolLiteral(false))
- ..fileOffset = enclosingFunction.fileEndOffset
- ]);
- }
-
- @override
- TreeNode visitYieldStatement(YieldStatement node, TreeNode? removalSentinel) {
- Expression transformedExpression = transform(node.expression);
-
- var statements = <Statement>[];
- if (node.isYieldStar) {
- statements.add(new ExpressionStatement(new InstanceSet(
- InstanceAccessKind.Instance,
- VariableGet(iteratorParameter),
- helper.syncIteratorYieldEachIterable.name,
- transformedExpression,
- interfaceTarget: helper.syncIteratorYieldEachIterable)));
- } else {
- statements.add(new ExpressionStatement(new InstanceSet(
- InstanceAccessKind.Instance,
- VariableGet(iteratorParameter),
- helper.syncIteratorCurrent.name,
- transformedExpression,
- interfaceTarget: helper.syncIteratorCurrent)));
- }
-
- statements.add(createContinuationPoint(new BoolLiteral(true))
- ..fileOffset = node.fileOffset);
- return new Block(statements);
- }
-
- @override
- TreeNode visitReturnStatement(
- ReturnStatement node, TreeNode? removalSentinel) {
- // sync* functions cannot return a value.
- assert(node.expression == null || node.expression is NullLiteral);
- node.expression = new BoolLiteral(false)..parent = node;
- return node;
- }
-}
-
-abstract class AsyncRewriterBase extends ContinuationRewriterBase {
- // :async_op has type (dynamic result_or_exception, StackTrace? s) -> dynamic
- final VariableDeclaration nestedClosureVariable;
-
- // :async_op_then has type (dynamic result) -> dynamic
- final VariableDeclaration thenContinuationVariable;
-
- // :async_op_error has type (Object e, StackTrace s) -> dynamic
- final VariableDeclaration catchErrorContinuationVariable;
-
- LabeledStatement? labeledBody;
-
- ExpressionLifter? expressionRewriter;
-
- AsyncRewriterBase(HelperNodes helper, FunctionNode enclosingFunction,
- StatefulStaticTypeContext staticTypeContext, bool desugarAsync)
- : nestedClosureVariable =
- VariableDeclaration(ContinuationVariables.asyncOp,
- type: FunctionType([
- const DynamicType(),
- helper.coreTypes
- .stackTraceRawType(staticTypeContext.nullable),
- ], const DynamicType(), staticTypeContext.nonNullable)),
- thenContinuationVariable = VariableDeclaration(
- ContinuationVariables.asyncOpThen,
- type: FunctionType(const [const DynamicType()], const DynamicType(),
- staticTypeContext.nonNullable)),
- catchErrorContinuationVariable =
- VariableDeclaration(ContinuationVariables.asyncOpError,
- type: FunctionType([
- helper.coreTypes.objectRawType(staticTypeContext.nonNullable),
- helper.coreTypes
- .stackTraceRawType(staticTypeContext.nonNullable),
- ], const DynamicType(), staticTypeContext.nonNullable)),
- super(helper, enclosingFunction, staticTypeContext, desugarAsync,
- desugarAwaitFor: true) {}
-
- void setupAsyncContinuations(List<Statement> statements) {
- expressionRewriter = new ExpressionLifter(this);
-
- // var :async_op_then;
- statements.add(thenContinuationVariable);
-
- // var :async_op_error;
- statements.add(catchErrorContinuationVariable);
-
- // :async_op(:result_or_exception, :stack_trace) {
- // modified <node.body>;
- // }
- final parameters = <VariableDeclaration>[
- expressionRewriter!.asyncResult,
- new VariableDeclaration(ContinuationVariables.stackTraceParam),
- ];
-
- // Note: SyncYielding functions have no Dart equivalent. Since they are
- // synchronous, we use Sync. (Note also that the Dart VM backend uses the
- // Dart async marker to decide if functions are debuggable.)
- final function = new FunctionNode(buildWrappedBody(),
- positionalParameters: parameters,
- asyncMarker: AsyncMarker.SyncYielding,
- dartAsyncMarker: AsyncMarker.Sync)
- ..fileOffset = enclosingFunction.fileOffset
- ..fileEndOffset = enclosingFunction.fileEndOffset;
-
- // The await expression lifter might have created a number of
- // [VariableDeclarations].
- // TODO(kustermann): If we didn't need any variables we should not emit
- // these.
- statements.addAll(variableDeclarations());
- statements.addAll(expressionRewriter!.variables);
-
- // Now add the closure function itself.
- final closureFunction =
- new FunctionDeclaration(nestedClosureVariable, function)
- ..fileOffset = enclosingFunction.parent!.fileOffset;
- statements.add(closureFunction);
-
- // :async_op_then = _asyncThenWrapperHelper(asyncBody);
- final boundThenClosure = new StaticInvocation(helper.asyncThenWrapper,
- new Arguments(<Expression>[new VariableGet(nestedClosureVariable)]));
- final thenClosureVariableAssign = new ExpressionStatement(
- new VariableSet(thenContinuationVariable, boundThenClosure));
- statements.add(thenClosureVariableAssign);
-
- // :async_op_error = _asyncErrorWrapperHelper(asyncBody);
- final boundCatchErrorClosure = new StaticInvocation(
- helper.asyncErrorWrapper,
- new Arguments(<Expression>[new VariableGet(nestedClosureVariable)]));
- final catchErrorClosureVariableAssign = new ExpressionStatement(
- new VariableSet(
- catchErrorContinuationVariable, boundCatchErrorClosure));
- statements.add(catchErrorClosureVariableAssign);
- }
-
- Statement buildWrappedBody() {
- ++currentTryDepth;
- labeledBody = new LabeledStatement(null);
- labeledBody!.body = visitDelimited(enclosingFunction.body!)
- ..parent = labeledBody;
- --currentTryDepth;
-
- var exceptionVariable = VariableDeclaration('exception');
- var stackTraceVariable = VariableDeclaration('stack_trace',
- type:
- helper.coreTypes.stackTraceRawType(staticTypeContext.nonNullable));
-
- return new TryCatch(
- buildReturn(labeledBody!),
- <Catch>[
- new Catch(
- exceptionVariable,
- new Block(<Statement>[
- buildCatchBody(exceptionVariable, stackTraceVariable)
- ]),
- stackTrace: stackTraceVariable)
- ],
- isSynthetic: true,
- );
- }
-
- Statement buildCatchBody(VariableDeclaration exceptionVariable,
- VariableDeclaration stackTraceVariable);
-
- Statement buildReturn(Statement body);
-
- List<Statement> statements = <Statement>[];
-
- @override
- TreeNode visitExpressionStatement(
- ExpressionStatement stmt, TreeNode? removalSentinel) {
- stmt.expression = expressionRewriter!.rewrite(stmt.expression, statements)
- ..parent = stmt;
- statements.add(stmt);
- return removalSentinel ?? EmptyStatement();
- }
-
- @override
- TreeNode visitBlock(Block stmt, TreeNode? removalSentinel) {
- var saved = statements;
- statements = <Statement>[];
- for (var statement in stmt.statements) {
- transform(statement);
- }
- saved.add(new Block(statements));
- statements = saved;
- return removalSentinel ?? EmptyStatement();
- }
-
- @override
- TreeNode visitEmptyStatement(EmptyStatement stmt, TreeNode? removalSentinel) {
- statements.add(stmt);
- return removalSentinel ?? EmptyStatement();
- }
-
- @override
- TreeNode visitAssertBlock(AssertBlock stmt, TreeNode? removalSentinel) {
- var saved = statements;
- statements = <Statement>[];
- for (var statement in stmt.statements) {
- transform(statement);
- }
- saved.add(new Block(statements));
- statements = saved;
- return removalSentinel ?? EmptyStatement();
- }
-
- @override
- TreeNode visitAssertStatement(
- AssertStatement stmt, TreeNode? removalSentinel) {
- var condEffects = <Statement>[];
- var cond = expressionRewriter!.rewrite(stmt.condition, condEffects);
- if (stmt.message == null) {
- stmt.condition = cond..parent = stmt;
- // If the translation of the condition produced a non-empty list of
- // statements, ensure they are guarded by whether asserts are enabled.
- statements.add(
- condEffects.isEmpty ? stmt : new AssertBlock(condEffects..add(stmt)));
- return removalSentinel ?? EmptyStatement();
- }
-
- // The translation depends on the translation of the message, by cases.
- Statement result;
- var msgEffects = <Statement>[];
- stmt.message = expressionRewriter!.rewrite(stmt.message!, msgEffects)
- ..parent = stmt;
- if (condEffects.isEmpty) {
- if (msgEffects.isEmpty) {
- // The condition rewrote to ([], C) and the message rewrote to ([], M).
- // The result is
- //
- // assert(C, M)
- stmt.condition = cond..parent = stmt;
- result = stmt;
- } else {
- // The condition rewrote to ([], C) and the message rewrote to (S*, M)
- // where S* is non-empty. The result is
- //
- // assert { if (C) {} else { S*; assert(false, M); }}
- stmt.condition = new BoolLiteral(false)..parent = stmt;
- result = new AssertBlock([
- new IfStatement(
- cond, new EmptyStatement(), new Block(msgEffects..add(stmt)))
- ]);
- }
- } else {
- if (msgEffects.isEmpty) {
- // The condition rewrote to (S*, C) where S* is non-empty and the
- // message rewrote to ([], M). The result is
- //
- // assert { S*; assert(C, M); }
- stmt.condition = cond..parent = stmt;
- condEffects.add(stmt);
- } else {
- // The condition rewrote to (S0*, C) and the message rewrote to (S1*, M)
- // where both S0* and S1* are non-empty. The result is
- //
- // assert { S0*; if (C) {} else { S1*; assert(false, M); }}
- stmt.condition = new BoolLiteral(false)..parent = stmt;
- condEffects.add(new IfStatement(
- cond, new EmptyStatement(), new Block(msgEffects..add(stmt))));
- }
- result = new AssertBlock(condEffects);
- }
- statements.add(result);
- return removalSentinel ?? EmptyStatement();
- }
-
- Statement visitDelimited(Statement stmt) {
- var saved = statements;
- statements = <Statement>[];
- transform(stmt);
- Statement result =
- statements.length == 1 ? statements.first : new Block(statements);
- statements = saved;
- return result;
- }
-
- @override
- TreeNode visitLabeledStatement(
- LabeledStatement stmt, TreeNode? removalSentinel) {
- stmt.body = visitDelimited(stmt.body)..parent = stmt;
- statements.add(stmt);
- return removalSentinel ?? EmptyStatement();
- }
-
- @override
- TreeNode visitBreakStatement(BreakStatement stmt, TreeNode? removalSentinel) {
- statements.add(stmt);
- return removalSentinel ?? EmptyStatement();
- }
-
- @override
- TreeNode visitWhileStatement(WhileStatement stmt, TreeNode? removalSentinel) {
- Statement body = visitDelimited(stmt.body);
- List<Statement> effects = <Statement>[];
- Expression cond = expressionRewriter!.rewrite(stmt.condition, effects);
- if (effects.isEmpty) {
- stmt.condition = cond..parent = stmt;
- stmt.body = body..parent = stmt;
- statements.add(stmt);
- } else {
- // The condition rewrote to a non-empty sequence of statements S* and
- // value V. Rewrite the loop to:
- //
- // L: while (true) {
- // S*
- // if (V) {
- // [body]
- // else {
- // break L;
- // }
- // }
- LabeledStatement labeled = new LabeledStatement(stmt);
- stmt.condition = new BoolLiteral(true)..parent = stmt;
- effects.add(new IfStatement(cond, body, new BreakStatement(labeled)));
- stmt.body = new Block(effects)..parent = stmt;
- statements.add(labeled);
- }
- return removalSentinel ?? EmptyStatement();
- }
-
- @override
- TreeNode visitDoStatement(DoStatement stmt, TreeNode? removalSentinel) {
- Statement body = visitDelimited(stmt.body);
- List<Statement> effects = <Statement>[];
- stmt.condition = expressionRewriter!.rewrite(stmt.condition, effects)
- ..parent = stmt;
- if (effects.isNotEmpty) {
- // The condition rewrote to a non-empty sequence of statements S* and
- // value V. Add the statements to the end of the loop body.
- Block block = body is Block ? body : body = new Block(<Statement>[body]);
- for (var effect in effects) {
- block.statements.add(effect);
- effect.parent = body;
- }
- }
- stmt.body = body..parent = stmt;
- statements.add(stmt);
- return removalSentinel ?? EmptyStatement();
- }
-
- @override
- TreeNode visitForStatement(ForStatement stmt, TreeNode? removalSentinel) {
- // Because of for-loop scoping and variable capture, it is tricky to deal
- // with await in the loop's variable initializers or update expressions.
- bool isSimple = true;
- int length = stmt.variables.length;
- List<List<Statement>> initEffects =
- new List<List<Statement>>.generate(length, (int i) {
- VariableDeclaration decl = stmt.variables[i];
- List<Statement> statements = <Statement>[];
- if (decl.initializer != null) {
- decl.initializer = expressionRewriter!
- .rewrite(decl.initializer!, statements)
- ..parent = decl;
- }
- isSimple = isSimple && statements.isEmpty;
- return statements;
- });
-
- length = stmt.updates.length;
- List<List<Statement>> updateEffects =
- new List<List<Statement>>.generate(length, (int i) {
- List<Statement> statements = <Statement>[];
- stmt.updates[i] = expressionRewriter!.rewrite(stmt.updates[i], statements)
- ..parent = stmt;
- isSimple = isSimple && statements.isEmpty;
- return statements;
- });
-
- Statement body = visitDelimited(stmt.body);
- Expression? cond = stmt.condition;
- List<Statement>? condEffects;
- if (cond != null) {
- condEffects = <Statement>[];
- cond = expressionRewriter!.rewrite(stmt.condition!, condEffects);
- }
-
- if (isSimple) {
- // If the condition contains await, we use a translation like the one for
- // while loops, but leaving the variable declarations and the update
- // expressions in place.
- if (condEffects == null || condEffects.isEmpty) {
- if (cond != null) stmt.condition = cond..parent = stmt;
- stmt.body = body..parent = stmt;
- statements.add(stmt);
- } else {
- LabeledStatement labeled = new LabeledStatement(stmt);
- // No condition in a for loop is the same as true.
- stmt.condition = null;
- condEffects
- .add(new IfStatement(cond!, body, new BreakStatement(labeled)));
- stmt.body = new Block(condEffects)..parent = stmt;
- statements.add(labeled);
- }
- return removalSentinel ?? EmptyStatement();
- }
-
- // If the rewrite of the initializer or update expressions produces a
- // non-empty sequence of statements then the loop is desugared. If the loop
- // has the form:
- //
- // label: for (Type x = init; cond; update) body
- //
- // it is translated as if it were:
- //
- // {
- // bool first = true;
- // Type temp;
- // label: while (true) {
- // Type x;
- // if (first) {
- // first = false;
- // x = init;
- // } else {
- // x = temp;
- // update;
- // }
- // if (cond) {
- // body;
- // temp = x;
- // } else {
- // break;
- // }
- // }
- // }
-
- // Place the loop variable declarations at the beginning of the body
- // statements and move their initializers to a guarded list of statements.
- // Add assignments to the loop variables from the previous iterations temp
- // variables before the updates.
- //
- // temps.first is the flag 'first'.
- // TODO(kmillikin) bool type for first.
- List<VariableDeclaration> temps = <VariableDeclaration>[
- new VariableDeclaration.forValue(new BoolLiteral(true), isFinal: false)
- ];
- List<Statement> loopBody = <Statement>[];
- List<Statement> initializers = <Statement>[
- new ExpressionStatement(
- new VariableSet(temps.first, new BoolLiteral(false)))
- ];
- List<Statement> updates = <Statement>[];
- List<Statement> newBody = <Statement>[body];
- for (int i = 0; i < stmt.variables.length; ++i) {
- VariableDeclaration decl = stmt.variables[i];
- temps.add(new VariableDeclaration(null, type: decl.type));
- loopBody.add(decl);
- if (decl.initializer != null) {
- initializers.addAll(initEffects[i]);
- initializers.add(
- new ExpressionStatement(new VariableSet(decl, decl.initializer!)));
- decl.initializer = null;
- }
- updates.add(new ExpressionStatement(
- new VariableSet(decl, new VariableGet(temps.last))));
- newBody.add(new ExpressionStatement(
- new VariableSet(temps.last, new VariableGet(decl))));
- }
- // Add the updates to their guarded list of statements.
- for (int i = 0; i < stmt.updates.length; ++i) {
- updates.addAll(updateEffects[i]);
- updates.add(new ExpressionStatement(stmt.updates[i]));
- }
- // Initializers or updates could be empty.
- loopBody.add(new IfStatement(new VariableGet(temps.first),
- new Block(initializers), new Block(updates)));
-
- LabeledStatement labeled = new LabeledStatement(null);
- if (cond != null) {
- loopBody.addAll(condEffects!);
- } else {
- cond = new BoolLiteral(true);
- }
- loopBody.add(
- new IfStatement(cond, new Block(newBody), new BreakStatement(labeled)));
- labeled.body =
- new WhileStatement(new BoolLiteral(true), new Block(loopBody))
- ..parent = labeled;
- statements.add(new Block(<Statement>[]
- ..addAll(temps)
- ..add(labeled)));
- return removalSentinel ?? EmptyStatement();
- }
-
- @override
- TreeNode visitSwitchStatement(
- SwitchStatement stmt, TreeNode? removalSentinel) {
- stmt.expression = expressionRewriter!.rewrite(stmt.expression, statements)
- ..parent = stmt;
- for (var switchCase in stmt.cases) {
- // Expressions in switch cases cannot contain await so they do not need to
- // be translated.
- switchCase.body = visitDelimited(switchCase.body)..parent = switchCase;
- }
- statements.add(stmt);
- return removalSentinel ?? EmptyStatement();
- }
-
- @override
- TreeNode visitContinueSwitchStatement(
- ContinueSwitchStatement stmt, TreeNode? removalSentinel) {
- statements.add(stmt);
- return removalSentinel ?? EmptyStatement();
- }
-
- @override
- TreeNode visitIfStatement(IfStatement stmt, TreeNode? removalSentinel) {
- stmt.condition = expressionRewriter!.rewrite(stmt.condition, statements)
- ..parent = stmt;
- stmt.then = visitDelimited(stmt.then)..parent = stmt;
- if (stmt.otherwise != null) {
- stmt.otherwise = visitDelimited(stmt.otherwise!)..parent = stmt;
- }
- statements.add(stmt);
- return removalSentinel ?? EmptyStatement();
- }
-
- @override
- TreeNode visitTryCatch(TryCatch stmt, TreeNode? removalSentinel) {
- ++currentTryDepth;
- stmt.body = visitDelimited(stmt.body)..parent = stmt;
- --currentTryDepth;
-
- ++currentCatchDepth;
- for (var clause in stmt.catches) {
- clause.body = visitDelimited(clause.body)..parent = clause;
- }
- --currentCatchDepth;
- statements.add(stmt);
- return removalSentinel ?? EmptyStatement();
- }
-
- @override
- TreeNode visitTryFinally(TryFinally stmt, TreeNode? removalSentinel) {
- ++currentTryDepth;
- stmt.body = visitDelimited(stmt.body)..parent = stmt;
- --currentTryDepth;
- ++currentCatchDepth;
- stmt.finalizer = visitDelimited(stmt.finalizer)..parent = stmt;
- --currentCatchDepth;
- statements.add(stmt);
- return removalSentinel ?? EmptyStatement();
- }
-
- @override
- TreeNode visitYieldStatement(YieldStatement stmt, TreeNode? removalSentinel) {
- stmt.expression = expressionRewriter!.rewrite(stmt.expression, statements)
- ..parent = stmt;
- statements.add(stmt);
- return removalSentinel ?? EmptyStatement();
- }
-
- @override
- TreeNode visitVariableDeclaration(
- VariableDeclaration stmt, TreeNode? removalSentinel) {
- if (stmt.initializer != null) {
- stmt.initializer = expressionRewriter!
- .rewrite(stmt.initializer!, statements)
- ..parent = stmt;
- }
- statements.add(stmt);
- return removalSentinel ?? EmptyStatement();
- }
-
- @override
- TreeNode visitFunctionDeclaration(
- FunctionDeclaration stmt, TreeNode? removalSentinel) {
- stmt.function = transform(stmt.function)..parent = stmt;
- statements.add(stmt);
- return removalSentinel ?? EmptyStatement();
- }
-
- @override
- TreeNode defaultExpression(TreeNode node, TreeNode? removalSentinel) =>
- throw 'unreachable $node';
-}
-
-class AsyncStarFunctionRewriter extends AsyncRewriterBase {
- VariableDeclaration? controllerVariable;
-
- AsyncStarFunctionRewriter(HelperNodes helper, FunctionNode enclosingFunction,
- StatefulStaticTypeContext staticTypeContext, bool desugarAsync)
- : super(helper, enclosingFunction, staticTypeContext, desugarAsync);
-
- FunctionNode rewrite() {
- var statements = <Statement>[];
-
- final elementType = elementTypeFromReturnType(helper.streamClass);
-
- // _AsyncStarStreamController<T> :controller;
- controllerVariable = new VariableDeclaration(
- ContinuationVariables.controller,
- type: new InterfaceType(helper.asyncStarStreamControllerClass,
- staticTypeContext.nullable, [elementType]));
- statements.add(controllerVariable!);
-
- // dynamic :controller_stream;
- VariableDeclaration controllerStreamVariable =
- new VariableDeclaration(ContinuationVariables.controllerStreamVar);
- statements.add(controllerStreamVariable);
-
- setupAsyncContinuations(statements);
-
- // :controller = new _AsyncStarStreamController<T>(:async_op);
- var arguments = new Arguments(
- <Expression>[new VariableGet(nestedClosureVariable)],
- types: [elementType]);
- var buildController = new ConstructorInvocation(
- helper.asyncStarStreamControllerConstructor, arguments)
- ..fileOffset = enclosingFunction.fileOffset;
- var setController = new ExpressionStatement(
- new VariableSet(controllerVariable!, buildController));
- statements.add(setController);
-
- // :controller_stream = :controller.stream;
- var completerGet = new VariableGet(controllerVariable!);
- statements.add(new ExpressionStatement(new VariableSet(
- controllerStreamVariable,
- new InstanceGet(InstanceAccessKind.Instance, completerGet,
- helper.asyncStarStreamControllerStream.name,
- interfaceTarget: helper.asyncStarStreamControllerStream,
- resultType: Substitution.fromInterfaceType(
- controllerVariable!.type as InterfaceType)
- .substituteType(
- helper.asyncStarStreamControllerStream.getterType)))));
-
- // return :controller_stream;
- var returnStatement =
- new ReturnStatement(new VariableGet(controllerStreamVariable));
- statements.add(returnStatement);
-
- enclosingFunction.body = new Block(statements)..parent = enclosingFunction;
- enclosingFunction.asyncMarker = AsyncMarker.Sync;
- return enclosingFunction;
- }
-
- @override
- Statement buildWrappedBody() {
- ++currentTryDepth;
- Statement body = super.buildWrappedBody();
- --currentTryDepth;
-
- var finallyBody = new ExpressionStatement(new InstanceInvocation(
- InstanceAccessKind.Instance,
- new VariableGet(controllerVariable!),
- helper.asyncStarStreamControllerClose.name,
- new Arguments([]),
- interfaceTarget: helper.asyncStarStreamControllerClose,
- functionType:
- helper.asyncStarStreamControllerClose.getterType as FunctionType));
-
- var tryFinally = new TryFinally(body, new Block(<Statement>[finallyBody]));
- return tryFinally;
- }
-
- @override
- Statement buildCatchBody(VariableDeclaration exceptionVariable,
- VariableDeclaration stackTraceVariable) {
- return new ExpressionStatement(new InstanceInvocation(
- InstanceAccessKind.Instance,
- new VariableGet(controllerVariable!),
- helper.asyncStarStreamControllerAddError.name,
- new Arguments(<Expression>[
- new VariableGet(exceptionVariable),
- new VariableGet(stackTraceVariable)
- ]),
- interfaceTarget: helper.asyncStarStreamControllerAddError,
- functionType: helper.asyncStarStreamControllerAddError.getterType
- as FunctionType));
- }
-
- @override
- Statement buildReturn(Statement body) {
- // Async* functions cannot return a value. The returns from the function
- // have been translated into breaks from the labeled body.
- return new Block(<Statement>[
- body,
- new ReturnStatement()..fileOffset = enclosingFunction.fileEndOffset,
- ]);
- }
-
- @override
- TreeNode visitYieldStatement(YieldStatement stmt, TreeNode? removalSentinel) {
- Expression expr = expressionRewriter!.rewrite(stmt.expression, statements);
-
- final Procedure addMethod = stmt.isYieldStar
- ? helper.asyncStarStreamControllerAddStream
- : helper.asyncStarStreamControllerAdd;
- final FunctionType addMethodFunctionType = Substitution.fromInterfaceType(
- controllerVariable!.type as InterfaceType)
- .substituteType(addMethod.getterType) as FunctionType;
- var addExpression = new InstanceInvocation(
- InstanceAccessKind.Instance,
- new VariableGet(controllerVariable!),
- addMethod.name,
- new Arguments(<Expression>[expr]),
- interfaceTarget: addMethod,
- functionType: addMethodFunctionType)
- ..fileOffset = stmt.fileOffset;
-
- if (stmt.isYieldStar) {
- statements.add(ExpressionStatement(addExpression));
- statements.add(createContinuationPoint()..fileOffset = stmt.fileOffset);
- final wasCancelled = StaticInvocation(
- helper.unsafeCast,
- Arguments(<Expression>[VariableGet(expressionRewriter!.asyncResult)],
- types: <DartType>[helper.coreTypes.boolNonNullableRawType]));
- statements
- .add(IfStatement(wasCancelled, ReturnStatement(NullLiteral()), null));
- } else {
- statements.add(new IfStatement(
- addExpression,
- new ReturnStatement(new NullLiteral()),
- createContinuationPoint()..fileOffset = stmt.fileOffset));
- }
- return removalSentinel ?? EmptyStatement();
- }
-
- @override
- TreeNode visitReturnStatement(
- ReturnStatement node, TreeNode? removalSentinel) {
- // Async* functions cannot return a value.
- assert(node.expression == null || node.expression is NullLiteral);
- statements
- .add(new BreakStatement(labeledBody!)..fileOffset = node.fileOffset);
- return removalSentinel ?? EmptyStatement();
- }
-}
-
-class AsyncFunctionRewriter extends AsyncRewriterBase {
- VariableDeclaration? returnVariable;
- VariableDeclaration? asyncFutureVariable;
- VariableDeclaration? isSyncVariable;
-
- // In general an async functions such as
- //
- // Future<X> foo() async { return <expr>; }
- //
- // can return as `<expr>` either X or Future<X>, i.e. it can return
- // FutureOr<X>
- //
- // If we know it doesn't return any object of type `Future`, we can optimize
- // the future completion process by avoiding some expensive `is Future<T>`
- // type checks on the returned value.
- late bool canReturnFuture;
-
- AsyncFunctionRewriter(HelperNodes helper, FunctionNode enclosingFunction,
- StatefulStaticTypeContext staticTypeContext, bool desugarAsync)
- : super(helper, enclosingFunction, staticTypeContext, desugarAsync);
-
- FunctionNode rewrite() {
- var statements = <Statement>[];
-
- // The original function return type should be Future<T> or FutureOr<T>
- // because the function is async. If it was, we make a Completer<T>,
- // otherwise we make a malformed type. In a "Future<T> foo() async {}"
- // function the body can either return a "T" or a "Future<T>" => a
- // "FutureOr<T>".
- DartType valueType = elementTypeFromReturnType(helper.futureClass);
- if (valueType == const DynamicType()) {
- valueType = elementTypeFromAsyncReturnType();
- }
- final DartType returnType =
- FutureOrType(valueType, staticTypeContext.nullable);
- final futureTypeArguments = <DartType>[valueType];
-
- final futureType = InterfaceType(helper.futureImplClass,
- staticTypeContext.nonNullable, futureTypeArguments);
-
- // final _Future<T> :async_future = _Future<T>();
- asyncFutureVariable = VariableDeclaration(ContinuationVariables.asyncFuture,
- initializer: ConstructorInvocation(helper.futureImplConstructor,
- Arguments([], types: futureTypeArguments))
- ..fileOffset = enclosingFunction.body?.fileOffset ?? -1,
- isFinal: true,
- type: futureType);
- statements.add(asyncFutureVariable!);
-
- // bool :is_sync = false;
- isSyncVariable = VariableDeclaration(ContinuationVariables.isSync,
- initializer: BoolLiteral(false),
- type: helper.coreTypes.boolLegacyRawType);
- statements.add(isSyncVariable!);
-
- // asy::FutureOr<T>* :return_value;
- returnVariable = VariableDeclaration(ContinuationVariables.returnValue,
- type: returnType);
- statements.add(returnVariable!);
-
- canReturnFuture = false;
-
- setupAsyncContinuations(statements);
-
- // If we could prove the function doesn't return a `Future` we change the
- // type of `:return_value`.
- if (!canReturnFuture) {
- returnVariable!.type =
- valueType.withDeclaredNullability(Nullability.nullable);
- }
-
- // :async_op(null, null);
- final startStatement = ExpressionStatement(LocalFunctionInvocation(
- nestedClosureVariable, Arguments([NullLiteral(), NullLiteral()]),
- functionType: FunctionType(
- [], const DynamicType(), staticTypeContext.nonNullable))
- ..fileOffset = enclosingFunction.fileOffset);
- statements.add(startStatement);
-
- // :is_sync = true;
- final setIsSync =
- ExpressionStatement(VariableSet(isSyncVariable!, BoolLiteral(true)));
- statements.add(setIsSync);
-
- // return :async_future;
- statements.add(ReturnStatement(VariableGet(asyncFutureVariable!)));
-
- enclosingFunction.body = Block(statements)..parent = enclosingFunction;
- enclosingFunction.asyncMarker = AsyncMarker.Sync;
- return enclosingFunction;
- }
-
- // :async_op's try-catch catch body:
- @override
- Statement buildCatchBody(exceptionVariable, stackTraceVariable) {
- // _completeOnAsyncError(_future, e, st, :is_sync)
- return ExpressionStatement(StaticInvocation(
- helper.completeOnAsyncError,
- Arguments([
- VariableGet(asyncFutureVariable!),
- VariableGet(exceptionVariable),
- VariableGet(stackTraceVariable),
- VariableGet(isSyncVariable!)
- ])));
- }
-
- // :async_op's try-catch try body:
- @override
- Statement buildReturn(Statement body) {
- // Returns from the body have all been translated into assignments to the
- // return value variable followed by a break from the labeled body.
-
- // .. body ..
- // _completeOnAsyncReturn(_future, returnVariable, :is_sync)
- // return;
- return Block(<Statement>[
- body,
- ExpressionStatement(StaticInvocation(
- canReturnFuture
- ? helper.completeOnAsyncReturn
- : helper.completeWithNoFutureOnAsyncReturn,
- Arguments([
- VariableGet(asyncFutureVariable!),
- VariableGet(returnVariable!),
- VariableGet(isSyncVariable!)
- ]))),
- ReturnStatement()..fileOffset = enclosingFunction.fileEndOffset
- ]);
- }
-
- @override
- TreeNode visitReturnStatement(
- ReturnStatement node, TreeNode? removalSentinel) {
- final expression = node.expression;
- if (expression != null && !canReturnFuture) {
- final returnedType = staticTypeContext.getExpressionType(expression);
- canReturnFuture = _canHoldFutureObject(returnedType);
- }
-
- final transformedExpression = node.expression == null
- ? NullLiteral()
- : expressionRewriter!.rewrite(node.expression!, statements);
- statements.add(ExpressionStatement(
- VariableSet(returnVariable!, transformedExpression)
- ..fileOffset = node.fileOffset));
- statements.add(BreakStatement(labeledBody!));
- return removalSentinel ?? EmptyStatement();
- }
-
- bool _canHoldFutureObject(DartType type) {
- // Any supertype or subtype of `FutureOr` may hold a `Future` object.
- final env = staticTypeContext.typeEnvironment;
-
- if (type is TypeParameterType) {
- type = type.parameter.defaultType;
- }
-
- if (type is FutureOrType) return true;
-
- // Any supertype of Future (which includes Future/Object/dynamic) can hold
- // Future objects.
- if (env.isSubtypeOf(
- helper.futureType, type, SubtypeCheckMode.ignoringNullabilities)) {
- return true;
- }
-
- // Any subtype of Future (which includes Future/_Future and any user-defined
- // implementations) can hold Future objects.
- if (env.isSubtypeOf(
- type, helper.futureType, SubtypeCheckMode.ignoringNullabilities)) {
- return true;
- }
- return false;
- }
-}
-
-class HelperNodes {
- final Procedure asyncErrorWrapper;
- final Library asyncLibrary;
- final Procedure asyncStarStreamControllerAdd;
- final Procedure asyncStarStreamControllerAddError;
- final Procedure asyncStarStreamControllerAddStream;
- final Class asyncStarStreamControllerClass;
- final Procedure asyncStarStreamControllerClose;
- final Constructor asyncStarStreamControllerConstructor;
- final Member asyncStarStreamControllerStream;
- final Procedure asyncStarMoveNextHelper;
- final Procedure asyncThenWrapper;
- final Procedure awaitHelper;
- final Procedure completeOnAsyncReturn;
- final Procedure completeWithNoFutureOnAsyncReturn;
- final Procedure completeOnAsyncError;
- final Library coreLibrary;
- final CoreTypes coreTypes;
- final Class futureClass;
- final Class futureOrClass;
- final Class futureImplClass;
- final Constructor futureImplConstructor;
- final Class iterableClass;
- final Class streamClass;
- final Procedure streamIteratorCancel;
- final Class streamIteratorClass;
- final Constructor streamIteratorConstructor;
- final Member streamIteratorCurrent;
- final Procedure streamIteratorMoveNext;
- final Constructor syncIterableConstructor;
- final Class syncIteratorClass;
- final Member syncIteratorCurrent;
- final Member syncIteratorYieldEachIterable;
- final Class boolClass;
- final Procedure unsafeCast;
- final DartType futureType;
-
- bool productMode;
-
- HelperNodes._(
- this.asyncErrorWrapper,
- this.asyncLibrary,
- this.asyncStarStreamControllerAdd,
- this.asyncStarStreamControllerAddError,
- this.asyncStarStreamControllerAddStream,
- this.asyncStarStreamControllerClass,
- this.asyncStarStreamControllerClose,
- this.asyncStarStreamControllerConstructor,
- this.asyncStarStreamControllerStream,
- this.asyncStarMoveNextHelper,
- this.asyncThenWrapper,
- this.awaitHelper,
- this.completeOnAsyncReturn,
- this.completeWithNoFutureOnAsyncReturn,
- this.completeOnAsyncError,
- this.coreLibrary,
- this.coreTypes,
- this.futureClass,
- this.futureOrClass,
- this.futureImplClass,
- this.futureImplConstructor,
- this.iterableClass,
- this.streamClass,
- this.streamIteratorCancel,
- this.streamIteratorClass,
- this.streamIteratorConstructor,
- this.streamIteratorCurrent,
- this.streamIteratorMoveNext,
- this.syncIterableConstructor,
- this.syncIteratorClass,
- this.syncIteratorCurrent,
- this.syncIteratorYieldEachIterable,
- this.boolClass,
- this.productMode,
- this.unsafeCast)
- : futureType = InterfaceType(
- futureClass, Nullability.nonNullable, [DynamicType()]);
-
- factory HelperNodes.fromCoreTypes(CoreTypes coreTypes, bool productMode) {
- return new HelperNodes._(
- coreTypes.asyncErrorWrapperHelperProcedure,
- coreTypes.asyncLibrary,
- coreTypes.asyncStarStreamControllerAdd,
- coreTypes.asyncStarStreamControllerAddError,
- coreTypes.asyncStarStreamControllerAddStream,
- coreTypes.asyncStarStreamControllerClass,
- coreTypes.asyncStarStreamControllerClose,
- coreTypes.asyncStarStreamControllerDefaultConstructor,
- coreTypes.asyncStarStreamControllerStream,
- coreTypes.asyncStarMoveNextHelper,
- coreTypes.asyncThenWrapperHelperProcedure,
- coreTypes.awaitHelperProcedure,
- coreTypes.completeOnAsyncReturn,
- coreTypes.completeWithNoFutureOnAsyncReturn,
- coreTypes.completeOnAsyncError,
- coreTypes.coreLibrary,
- coreTypes,
- coreTypes.futureClass,
- coreTypes.deprecatedFutureOrClass,
- coreTypes.futureImplClass,
- coreTypes.futureImplConstructor,
- coreTypes.iterableClass,
- coreTypes.streamClass,
- coreTypes.streamIteratorCancel,
- coreTypes.streamIteratorClass,
- coreTypes.streamIteratorDefaultConstructor,
- coreTypes.streamIteratorCurrent,
- coreTypes.streamIteratorMoveNext,
- coreTypes.syncIterableDefaultConstructor,
- coreTypes.syncIteratorClass,
- coreTypes.syncIteratorCurrent,
- coreTypes.syncIteratorYieldEachIterable,
- coreTypes.boolClass,
- productMode,
- coreTypes.index.getTopLevelMember('dart:_internal', 'unsafeCast')
- as Procedure);
- }
-}
diff --git a/pkg/vm/lib/transformations/for_in_lowering.dart b/pkg/vm/lib/transformations/for_in_lowering.dart
new file mode 100644
index 0000000..f9576ed
--- /dev/null
+++ b/pkg/vm/lib/transformations/for_in_lowering.dart
@@ -0,0 +1,222 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:kernel/ast.dart';
+import 'package:kernel/core_types.dart' show CoreTypes;
+import 'package:kernel/type_algebra.dart' show Substitution;
+import 'package:kernel/type_environment.dart' show StaticTypeContext;
+
+class ForInVariables {
+ static const stream = ':stream';
+ static const forIterator = ':for-iterator';
+ static const syncForIterator = ':sync-for-iterator';
+}
+
+/// VM-specific desugaring of for-in loops.
+class ForInLowering {
+ final CoreTypes coreTypes;
+ final bool productMode;
+
+ ForInLowering(this.coreTypes, {required this.productMode});
+
+ Statement transformForInStatement(ForInStatement stmt,
+ FunctionNode? enclosingFunction, StaticTypeContext staticTypeContext) {
+ if (stmt.isAsync) {
+ if (enclosingFunction == null ||
+ (enclosingFunction.asyncMarker != AsyncMarker.Async &&
+ enclosingFunction.asyncMarker != AsyncMarker.AsyncStar)) {
+ return stmt;
+ }
+ // Transform
+ //
+ // await for (T variable in <stream-expression>) { ... }
+ //
+ // To (in product mode):
+ //
+ // {
+ // :stream = <stream-expression>;
+ // _StreamIterator<T> :for-iterator = new _StreamIterator<T>(:stream);
+ // try {
+ // while (await :for-iterator.moveNext()) {
+ // T <variable> = :for-iterator.current;
+ // ...
+ // }
+ // } finally {
+ // if (:for-iterator._subscription != null)
+ // await :for-iterator.cancel();
+ // }
+ // }
+ //
+ // Or (in non-product mode):
+ //
+ // {
+ // :stream = <stream-expression>;
+ // _StreamIterator<T> :for-iterator = new _StreamIterator<T>(:stream);
+ // try {
+ // while (let _ = _asyncStarMoveNextHelper(:stream) in
+ // await :for-iterator.moveNext()) {
+ // T <variable> = :for-iterator.current;
+ // ...
+ // }
+ // } finally {
+ // if (:for-iterator._subscription != null)
+ // await :for-iterator.cancel();
+ // }
+ // }
+ final valueVariable = stmt.variable;
+
+ final streamVariable = new VariableDeclaration(ForInVariables.stream,
+ initializer: stmt.iterable,
+ type: stmt.iterable.getStaticType(staticTypeContext));
+
+ final streamIteratorType = new InterfaceType(
+ coreTypes.streamIteratorClass,
+ staticTypeContext.nullable,
+ [valueVariable.type]);
+ final forIteratorVariable = VariableDeclaration(
+ ForInVariables.forIterator,
+ initializer: new ConstructorInvocation(
+ coreTypes.streamIteratorDefaultConstructor,
+ new Arguments(<Expression>[new VariableGet(streamVariable)],
+ types: [valueVariable.type])),
+ type: streamIteratorType);
+
+ // await :for-iterator.moveNext()
+ final condition = new AwaitExpression(new InstanceInvocation(
+ InstanceAccessKind.Instance,
+ VariableGet(forIteratorVariable),
+ coreTypes.streamIteratorMoveNext.name,
+ new Arguments([]),
+ interfaceTarget: coreTypes.streamIteratorMoveNext,
+ functionType:
+ coreTypes.streamIteratorMoveNext.getterType as FunctionType))
+ ..fileOffset = stmt.fileOffset;
+
+ Expression whileCondition;
+ if (productMode) {
+ whileCondition = condition;
+ } else {
+ // _asyncStarMoveNextHelper(:stream)
+ final asyncStarMoveNextCall = new StaticInvocation(
+ coreTypes.asyncStarMoveNextHelper,
+ new Arguments([new VariableGet(streamVariable)]))
+ ..fileOffset = stmt.fileOffset;
+
+ // let _ = asyncStarMoveNextCall in (condition)
+ whileCondition = new Let(
+ new VariableDeclaration(null, initializer: asyncStarMoveNextCall),
+ condition);
+ }
+
+ // T <variable> = :for-iterator.current;
+ valueVariable.initializer = new InstanceGet(
+ InstanceAccessKind.Instance,
+ VariableGet(forIteratorVariable),
+ coreTypes.streamIteratorCurrent.name,
+ interfaceTarget: coreTypes.streamIteratorCurrent,
+ resultType: valueVariable.type)
+ ..fileOffset = stmt.bodyOffset;
+ valueVariable.initializer!.parent = valueVariable;
+
+ final whileBody = new Block(<Statement>[valueVariable, stmt.body]);
+ final tryBody = new WhileStatement(whileCondition, whileBody);
+
+ // if (:for-iterator._subscription != null) await :for-iterator.cancel();
+ final DartType subscriptionType =
+ Substitution.fromInterfaceType(streamIteratorType)
+ .substituteType(coreTypes.streamIteratorSubscription.getterType);
+ final tryFinalizer = new IfStatement(
+ new Not(new EqualsNull(new InstanceGet(
+ InstanceAccessKind.Instance,
+ VariableGet(forIteratorVariable),
+ coreTypes.streamIteratorSubscription.name,
+ interfaceTarget: coreTypes.streamIteratorSubscription,
+ resultType: subscriptionType))),
+ new ExpressionStatement(new AwaitExpression(new InstanceInvocation(
+ InstanceAccessKind.Instance,
+ VariableGet(forIteratorVariable),
+ coreTypes.streamIteratorCancel.name,
+ new Arguments(<Expression>[]),
+ interfaceTarget: coreTypes.streamIteratorCancel,
+ functionType:
+ coreTypes.streamIteratorCancel.getterType as FunctionType))),
+ null);
+
+ final tryFinally = new TryFinally(tryBody, tryFinalizer);
+
+ final block = new Block(
+ <Statement>[streamVariable, forIteratorVariable, tryFinally]);
+ return block;
+ }
+
+ // Transform
+ //
+ // for ({var/final} T <variable> in <iterable>) { ... }
+ //
+ // Into
+ //
+ // {
+ // final Iterator<T> :sync-for-iterator = <iterable>.iterator;
+ // for (; :sync-for-iterator.moveNext() ;) {
+ // {var/final} T variable = :sync-for-iterator.current;
+ // ...
+ // }
+ // }
+ // }
+
+ // The CFE might invoke this transformation despite the program having
+ // compile-time errors. So we will not transform this [stmt] if the
+ // `stmt.iterable` is an invalid expression or has an invalid type and
+ // instead eliminate the entire for-in and replace it with a invalid
+ // expression statement.
+ final iterable = stmt.iterable;
+ final iterableType = iterable.getStaticType(staticTypeContext);
+ if (iterableType is InvalidType) {
+ return ExpressionStatement(
+ InvalidExpression('Invalid iterable type in for-in'));
+ }
+
+ // The NNBD sdk declares that Iterable.get:iterator returns a non-nullable
+ // `Iterator<E>`.
+ assert(const [
+ Nullability.nonNullable,
+ Nullability.legacy
+ ].contains(coreTypes.iterableGetIterator.function.returnType.nullability));
+
+ final DartType elementType = stmt.getElementType(staticTypeContext);
+ final iteratorType = InterfaceType(
+ coreTypes.iteratorClass, staticTypeContext.nonNullable, [elementType]);
+
+ final syncForIterator = VariableDeclaration(ForInVariables.syncForIterator,
+ initializer: InstanceGet(InstanceAccessKind.Instance, iterable,
+ coreTypes.iterableGetIterator.name,
+ interfaceTarget: coreTypes.iterableGetIterator,
+ resultType: iteratorType)
+ ..fileOffset = iterable.fileOffset,
+ type: iteratorType)
+ ..fileOffset = iterable.fileOffset;
+
+ final condition = InstanceInvocation(
+ InstanceAccessKind.Instance,
+ VariableGet(syncForIterator),
+ coreTypes.iteratorMoveNext.name,
+ Arguments([]),
+ interfaceTarget: coreTypes.iteratorMoveNext,
+ functionType: coreTypes.iteratorMoveNext.getterType as FunctionType)
+ ..fileOffset = iterable.fileOffset;
+
+ final variable = stmt.variable
+ ..initializer = (InstanceGet(InstanceAccessKind.Instance,
+ VariableGet(syncForIterator), coreTypes.iteratorGetCurrent.name,
+ interfaceTarget: coreTypes.iteratorGetCurrent,
+ resultType: elementType)
+ ..fileOffset = stmt.bodyOffset);
+ variable.initializer!.parent = variable;
+
+ final Block body = Block([variable, stmt.body])
+ ..fileOffset = stmt.bodyOffset;
+
+ return Block([syncForIterator, ForStatement([], condition, [], body)]);
+ }
+}
diff --git a/pkg/vm/lib/transformations/lowering.dart b/pkg/vm/lib/transformations/lowering.dart
index 4242ff4..f86ff95 100644
--- a/pkg/vm/lib/transformations/lowering.dart
+++ b/pkg/vm/lib/transformations/lowering.dart
@@ -10,6 +10,7 @@
import 'package:vm/transformations/specializer/factory_specializer.dart';
+import 'for_in_lowering.dart' show ForInLowering;
import 'late_var_init_transformer.dart' show LateVarInitTransformer;
import 'list_literals_lowering.dart' show ListLiteralsLowering;
import 'type_casts_optimizer.dart' as typeCastsOptimizer
@@ -20,15 +21,19 @@
///
/// Each transformation is applied locally to AST nodes of certain types
/// after transforming children nodes.
-void transformLibraries(List<Library> libraries, CoreTypes coreTypes,
- ClassHierarchy hierarchy, bool nullSafety) {
- final transformer = _Lowering(coreTypes, hierarchy, nullSafety);
+void transformLibraries(
+ List<Library> libraries, CoreTypes coreTypes, ClassHierarchy hierarchy,
+ {required bool nullSafety, required bool productMode}) {
+ final transformer = _Lowering(coreTypes, hierarchy,
+ nullSafety: nullSafety, productMode: productMode);
libraries.forEach(transformer.visitLibrary);
}
-void transformProcedure(Procedure procedure, CoreTypes coreTypes,
- ClassHierarchy hierarchy, bool nullSafety) {
- final transformer = _Lowering(coreTypes, hierarchy, nullSafety);
+void transformProcedure(
+ Procedure procedure, CoreTypes coreTypes, ClassHierarchy hierarchy,
+ {required bool nullSafety, required bool productMode}) {
+ final transformer = _Lowering(coreTypes, hierarchy,
+ nullSafety: nullSafety, productMode: productMode);
procedure.accept(transformer);
}
@@ -38,15 +43,19 @@
final LateVarInitTransformer lateVarInitTransformer;
final FactorySpecializer factorySpecializer;
final ListLiteralsLowering listLiteralsLowering;
+ final ForInLowering forInLowering;
Member? _currentMember;
+ FunctionNode? _currentFunctionNode;
StaticTypeContext? _cachedStaticTypeContext;
- _Lowering(CoreTypes coreTypes, ClassHierarchy hierarchy, this.nullSafety)
+ _Lowering(CoreTypes coreTypes, ClassHierarchy hierarchy,
+ {required this.nullSafety, required bool productMode})
: env = TypeEnvironment(coreTypes, hierarchy),
lateVarInitTransformer = LateVarInitTransformer(),
factorySpecializer = FactorySpecializer(coreTypes),
- listLiteralsLowering = ListLiteralsLowering(coreTypes);
+ listLiteralsLowering = ListLiteralsLowering(coreTypes),
+ forInLowering = ForInLowering(coreTypes, productMode: productMode);
StaticTypeContext get _staticTypeContext =>
_cachedStaticTypeContext ??= StaticTypeContext(_currentMember!, env);
@@ -70,6 +79,17 @@
}
@override
+ visitFunctionNode(FunctionNode node) {
+ final savedFunctionNode = _currentFunctionNode;
+ _currentFunctionNode = node;
+
+ final result = super.visitFunctionNode(node);
+
+ _currentFunctionNode = savedFunctionNode;
+ return result;
+ }
+
+ @override
visitStaticInvocation(StaticInvocation node) {
node.transformChildren(this);
return factorySpecializer.transformStaticInvocation(node);
@@ -99,4 +119,11 @@
node.transformChildren(this);
return listLiteralsLowering.transformListLiteral(node);
}
+
+ @override
+ visitForInStatement(ForInStatement node) {
+ node.transformChildren(this);
+ return forInLowering.transformForInStatement(
+ node, _currentFunctionNode, _staticTypeContext);
+ }
}
diff --git a/pkg/vm/lib/transformations/type_flow/summary_collector.dart b/pkg/vm/lib/transformations/type_flow/summary_collector.dart
index 6bd2768..b89c8a7 100644
--- a/pkg/vm/lib/transformations/type_flow/summary_collector.dart
+++ b/pkg/vm/lib/transformations/type_flow/summary_collector.dart
@@ -363,17 +363,6 @@
final function = node.function;
function.accept(this);
- if (function.asyncMarker == AsyncMarker.SyncYielding) {
- // Mark parameters of synthetic async_op closures as captured
- // to make sure their updates at yield points are taken into account.
- for (var v in function.positionalParameters) {
- _captureVariable(v);
- }
- for (var v in function.namedParameters) {
- _captureVariable(v);
- }
- }
-
activeStatements = savedActiveStatements;
numVariablesAtActiveStatements = savedNumVariablesAtActiveStatements;
numVariablesAtFunctionEntry = savedNumVariablesAtFunctionEntry;
diff --git a/runtime/docs/index.md b/runtime/docs/index.md
index 8e261b0..23a1a14 100644
--- a/runtime/docs/index.md
+++ b/runtime/docs/index.md
@@ -98,7 +98,7 @@
At this point enough information is loaded from Kernel binary for runtime to successfully resolve and invoke methods. For example, it could resolve and invoke `main` function from a library.
!!! sourcecode "Source to read"
- @{package:kernel/ast.dart} defines classes describing the Kernel AST. @{package:front_end} handles parsing Dart source and building Kernel AST from it. @{dart::kernel::KernelLoader::LoadEntireProgram} is an entry point for deserialization of Kernel AST into corresponding VM objects. @{pkg/vm/bin/kernel_service.dart} implements the Kernel Service isolate, @{runtime/vm/kernel_isolate.cc} glues Dart implementation to the rest of the VM. @{package:vm} hosts most of the Kernel based VM specific functionality, e.g various Kernel-to-Kernel transformations. However some VM specific transformations still live in @{package:kernel} for historical reasons. A good example of a complicated transformation is @{package:kernel/transformations/continuation.dart}, which desugars `async`,`async*` and `sync*` functions.
+ @{package:kernel/ast.dart} defines classes describing the Kernel AST. @{package:front_end} handles parsing Dart source and building Kernel AST from it. @{dart::kernel::KernelLoader::LoadEntireProgram} is an entry point for deserialization of Kernel AST into corresponding VM objects. @{pkg/vm/bin/kernel_service.dart} implements the Kernel Service isolate, @{runtime/vm/kernel_isolate.cc} glues Dart implementation to the rest of the VM. @{package:vm} hosts most of the Kernel based VM specific functionality, e.g various Kernel-to-Kernel transformations.
!!! tryit "Trying it"
diff --git a/runtime/tests/vm/dart/splay_test.dart b/runtime/tests/vm/dart/splay_test.dart
index e508561..ef51ede 100644
--- a/runtime/tests/vm/dart/splay_test.dart
+++ b/runtime/tests/vm/dart/splay_test.dart
@@ -28,6 +28,9 @@
// VMOptions=--stress_write_barrier_elimination
// VMOptions=--old_gen_heap_size=100
// VMOptions=--mark_when_idle
+// VMOptions=--no_load_cse
+// VMOptions=--no_dead_store_elimination
+// VMOptions=--no_load_cse --no_dead_store_elimination
import "dart:math";
import 'package:benchmark_harness/benchmark_harness.dart';
diff --git a/runtime/tests/vm/dart_2/splay_test.dart b/runtime/tests/vm/dart_2/splay_test.dart
index f0474f6..6cd3050 100644
--- a/runtime/tests/vm/dart_2/splay_test.dart
+++ b/runtime/tests/vm/dart_2/splay_test.dart
@@ -32,6 +32,9 @@
// VMOptions=--stress_write_barrier_elimination
// VMOptions=--old_gen_heap_size=100
// VMOptions=--mark_when_idle
+// VMOptions=--no_load_cse
+// VMOptions=--no_dead_store_elimination
+// VMOptions=--no_load_cse --no_dead_store_elimination
import "dart:math";
import 'package:benchmark_harness/benchmark_harness.dart';
diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h
index c7626f7..ad51a54 100644
--- a/runtime/vm/compiler/backend/il.h
+++ b/runtime/vm/compiler/backend/il.h
@@ -9412,7 +9412,7 @@
SIMD_BINARY_FLOAT_OP_LIST(M, BINARY_OP, Float32x4) \
SIMD_BINARY_FLOAT_OP_LIST(M, BINARY_OP, Float64x2) \
SIMD_BINARY_INTEGER_OP_LIST(M, BINARY_OP, Int32x4) \
- SIMD_PER_COMPONENT_XYZW(M, 1, Float32x4Shuffle, (Float32x4), Double) \
+ SIMD_PER_COMPONENT_XYZW(M, 1, Float32x4Get, (Float32x4), Double) \
SIMD_PER_COMPONENT_XYZW(M, 2, Float32x4With, (Double, Float32x4), Float32x4) \
SIMD_PER_COMPONENT_XYZW(M, 1, Int32x4GetFlag, (Int32x4), Bool) \
SIMD_PER_COMPONENT_XYZW(M, 2, Int32x4WithFlag, (Int32x4, Bool), Int32x4) \
diff --git a/runtime/vm/compiler/backend/il_arm.cc b/runtime/vm/compiler/backend/il_arm.cc
index d42f1c2..2775dde 100644
--- a/runtime/vm/compiler/backend/il_arm.cc
+++ b/runtime/vm/compiler/backend/il_arm.cc
@@ -5154,16 +5154,16 @@
// instructions. For arbitrary shuffles, use vtbl.
switch (instr->kind()) {
- case SimdOpInstr::kFloat32x4ShuffleX:
+ case SimdOpInstr::kFloat32x4GetX:
__ vcvtds(result.d(0), value.s(0));
break;
- case SimdOpInstr::kFloat32x4ShuffleY:
+ case SimdOpInstr::kFloat32x4GetY:
__ vcvtds(result.d(0), value.s(1));
break;
- case SimdOpInstr::kFloat32x4ShuffleZ:
+ case SimdOpInstr::kFloat32x4GetZ:
__ vcvtds(result.d(0), value.s(2));
break;
- case SimdOpInstr::kFloat32x4ShuffleW:
+ case SimdOpInstr::kFloat32x4GetW:
__ vcvtds(result.d(0), value.s(3));
break;
case SimdOpInstr::kInt32x4Shuffle:
@@ -5597,10 +5597,10 @@
CASE(Float64x2Mul) \
CASE(Float64x2Div) \
____(Float64x2BinaryOp) \
- CASE(Float32x4ShuffleX) \
- CASE(Float32x4ShuffleY) \
- CASE(Float32x4ShuffleZ) \
- CASE(Float32x4ShuffleW) \
+ CASE(Float32x4GetX) \
+ CASE(Float32x4GetY) \
+ CASE(Float32x4GetZ) \
+ CASE(Float32x4GetW) \
CASE(Int32x4Shuffle) \
CASE(Float32x4Shuffle) \
____(Simd32x4Shuffle) \
diff --git a/runtime/vm/compiler/backend/il_arm64.cc b/runtime/vm/compiler/backend/il_arm64.cc
index 2dfcd42..f0b8f86 100644
--- a/runtime/vm/compiler/backend/il_arm64.cc
+++ b/runtime/vm/compiler/backend/il_arm64.cc
@@ -4414,19 +4414,19 @@
break;
SIMD_OP_SIMPLE_UNARY(EMIT)
#undef EMIT
- case SimdOpInstr::kFloat32x4ShuffleX:
+ case SimdOpInstr::kFloat32x4GetX:
__ vinss(result, 0, value, 0);
__ fcvtds(result, result);
break;
- case SimdOpInstr::kFloat32x4ShuffleY:
+ case SimdOpInstr::kFloat32x4GetY:
__ vinss(result, 0, value, 1);
__ fcvtds(result, result);
break;
- case SimdOpInstr::kFloat32x4ShuffleZ:
+ case SimdOpInstr::kFloat32x4GetZ:
__ vinss(result, 0, value, 2);
__ fcvtds(result, result);
break;
- case SimdOpInstr::kFloat32x4ShuffleW:
+ case SimdOpInstr::kFloat32x4GetW:
__ vinss(result, 0, value, 3);
__ fcvtds(result, result);
break;
@@ -4705,10 +4705,10 @@
CASE(Float64x2Scale) \
____(SimdBinaryOp) \
SIMD_OP_SIMPLE_UNARY(CASE) \
- CASE(Float32x4ShuffleX) \
- CASE(Float32x4ShuffleY) \
- CASE(Float32x4ShuffleZ) \
- CASE(Float32x4ShuffleW) \
+ CASE(Float32x4GetX) \
+ CASE(Float32x4GetY) \
+ CASE(Float32x4GetZ) \
+ CASE(Float32x4GetW) \
CASE(Int32x4Shuffle) \
CASE(Float32x4Shuffle) \
CASE(Float32x4Splat) \
diff --git a/runtime/vm/compiler/backend/il_ia32.cc b/runtime/vm/compiler/backend/il_ia32.cc
index 9d1ffa4..355c0c0 100644
--- a/runtime/vm/compiler/backend/il_ia32.cc
+++ b/runtime/vm/compiler/backend/il_ia32.cc
@@ -4507,19 +4507,19 @@
break;
SIMD_OP_SIMPLE_UNARY(EMIT)
#undef EMIT
- case SimdOpInstr::kFloat32x4ShuffleX:
+ case SimdOpInstr::kFloat32x4GetX:
// Shuffle not necessary.
__ cvtss2sd(value, value);
break;
- case SimdOpInstr::kFloat32x4ShuffleY:
+ case SimdOpInstr::kFloat32x4GetY:
__ shufps(value, value, compiler::Immediate(0x55));
__ cvtss2sd(value, value);
break;
- case SimdOpInstr::kFloat32x4ShuffleZ:
+ case SimdOpInstr::kFloat32x4GetZ:
__ shufps(value, value, compiler::Immediate(0xAA));
__ cvtss2sd(value, value);
break;
- case SimdOpInstr::kFloat32x4ShuffleW:
+ case SimdOpInstr::kFloat32x4GetW:
__ shufps(value, value, compiler::Immediate(0xFF));
__ cvtss2sd(value, value);
break;
@@ -4739,10 +4739,10 @@
CASE(Float32x4WithW) \
____(SimdBinaryOp) \
SIMD_OP_SIMPLE_UNARY(CASE) \
- CASE(Float32x4ShuffleX) \
- CASE(Float32x4ShuffleY) \
- CASE(Float32x4ShuffleZ) \
- CASE(Float32x4ShuffleW) \
+ CASE(Float32x4GetX) \
+ CASE(Float32x4GetY) \
+ CASE(Float32x4GetZ) \
+ CASE(Float32x4GetW) \
CASE(Float32x4Shuffle) \
CASE(Int32x4Shuffle) \
CASE(Float32x4Splat) \
diff --git a/runtime/vm/compiler/backend/il_riscv.cc b/runtime/vm/compiler/backend/il_riscv.cc
index ba8d560..140c21d 100644
--- a/runtime/vm/compiler/backend/il_riscv.cc
+++ b/runtime/vm/compiler/backend/il_riscv.cc
@@ -4990,10 +4990,10 @@
CASE(Float64x2Scale) \
____(SimdBinaryOp) \
SIMD_OP_SIMPLE_UNARY(CASE) \
- CASE(Float32x4ShuffleX) \
- CASE(Float32x4ShuffleY) \
- CASE(Float32x4ShuffleZ) \
- CASE(Float32x4ShuffleW) \
+ CASE(Float32x4GetX) \
+ CASE(Float32x4GetY) \
+ CASE(Float32x4GetZ) \
+ CASE(Float32x4GetW) \
CASE(Int32x4Shuffle) \
CASE(Float32x4Shuffle) \
CASE(Float32x4Splat) \
diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc
index 55fc995..8364d7a 100644
--- a/runtime/vm/compiler/backend/il_x64.cc
+++ b/runtime/vm/compiler/backend/il_x64.cc
@@ -4734,19 +4734,19 @@
break;
SIMD_OP_SIMPLE_UNARY(EMIT)
#undef EMIT
- case SimdOpInstr::kFloat32x4ShuffleX:
+ case SimdOpInstr::kFloat32x4GetX:
// Shuffle not necessary.
__ cvtss2sd(value, value);
break;
- case SimdOpInstr::kFloat32x4ShuffleY:
+ case SimdOpInstr::kFloat32x4GetY:
__ shufps(value, value, compiler::Immediate(0x55));
__ cvtss2sd(value, value);
break;
- case SimdOpInstr::kFloat32x4ShuffleZ:
+ case SimdOpInstr::kFloat32x4GetZ:
__ shufps(value, value, compiler::Immediate(0xAA));
__ cvtss2sd(value, value);
break;
- case SimdOpInstr::kFloat32x4ShuffleW:
+ case SimdOpInstr::kFloat32x4GetW:
__ shufps(value, value, compiler::Immediate(0xFF));
__ cvtss2sd(value, value);
break;
@@ -4965,10 +4965,10 @@
CASE(Float32x4WithW) \
____(SimdBinaryOp) \
SIMD_OP_SIMPLE_UNARY(CASE) \
- CASE(Float32x4ShuffleX) \
- CASE(Float32x4ShuffleY) \
- CASE(Float32x4ShuffleZ) \
- CASE(Float32x4ShuffleW) \
+ CASE(Float32x4GetX) \
+ CASE(Float32x4GetY) \
+ CASE(Float32x4GetZ) \
+ CASE(Float32x4GetW) \
CASE(Float32x4Shuffle) \
CASE(Int32x4Shuffle) \
CASE(Float32x4Splat) \
diff --git a/runtime/vm/compiler/backend/inliner.cc b/runtime/vm/compiler/backend/inliner.cc
index 575121f..4c8e06f 100644
--- a/runtime/vm/compiler/backend/inliner.cc
+++ b/runtime/vm/compiler/backend/inliner.cc
@@ -4058,10 +4058,10 @@
case MethodRecognizer::kFloat32x4Reciprocal:
case MethodRecognizer::kFloat32x4ReciprocalSqrt:
case MethodRecognizer::kFloat32x4Scale:
- case MethodRecognizer::kFloat32x4ShuffleW:
- case MethodRecognizer::kFloat32x4ShuffleX:
- case MethodRecognizer::kFloat32x4ShuffleY:
- case MethodRecognizer::kFloat32x4ShuffleZ:
+ case MethodRecognizer::kFloat32x4GetW:
+ case MethodRecognizer::kFloat32x4GetX:
+ case MethodRecognizer::kFloat32x4GetY:
+ case MethodRecognizer::kFloat32x4GetZ:
case MethodRecognizer::kFloat32x4Splat:
case MethodRecognizer::kFloat32x4Sqrt:
case MethodRecognizer::kFloat32x4ToFloat64x2:
diff --git a/runtime/vm/compiler/backend/redundancy_elimination.cc b/runtime/vm/compiler/backend/redundancy_elimination.cc
index 7d37d1f..5edf25f 100644
--- a/runtime/vm/compiler/backend/redundancy_elimination.cc
+++ b/runtime/vm/compiler/backend/redundancy_elimination.cc
@@ -3197,7 +3197,7 @@
};
void DeadStoreElimination::Optimize(FlowGraph* graph) {
- if (FLAG_dead_store_elimination) {
+ if (FLAG_dead_store_elimination && FLAG_load_cse) {
StoreOptimizer::OptimizeGraph(graph);
}
}
@@ -3599,6 +3599,12 @@
}
void AllocationSinking::Optimize() {
+ // Allocation sinking depends on load forwarding, so give up early if load
+ // forwarding is disabled.
+ if (!FLAG_load_cse || flow_graph_->is_huge_method()) {
+ return;
+ }
+
CollectCandidates();
// Insert MaterializeObject instructions that will describe the state of the
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index ff0c9bc..db660b5 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -5303,188 +5303,108 @@
const uint8_t flags = ReadByte(); // read flags.
- if ((flags & kYieldStatementFlagNative) == 0) {
- Fragment instructions;
- const bool is_yield_star = (flags & kYieldStatementFlagYieldStar) != 0;
+ Fragment instructions;
+ const bool is_yield_star = (flags & kYieldStatementFlagYieldStar) != 0;
- // Load :suspend_state variable using low-level FP-relative load
- // in order to avoid confusing SSA construction (which cannot
- // track its value as it is modified implicitly by stubs).
- LocalVariable* suspend_state = parsed_function()->suspend_state_var();
- ASSERT(suspend_state != nullptr);
- instructions += IntConstant(0);
- instructions += B->LoadFpRelativeSlot(
- compiler::target::frame_layout.FrameSlotForVariable(suspend_state) *
- compiler::target::kWordSize,
- CompileType::Dynamic(), kTagged);
- instructions += LoadNativeField(Slot::SuspendState_function_data());
+ // Load :suspend_state variable using low-level FP-relative load
+ // in order to avoid confusing SSA construction (which cannot
+ // track its value as it is modified implicitly by stubs).
+ LocalVariable* suspend_state = parsed_function()->suspend_state_var();
+ ASSERT(suspend_state != nullptr);
+ instructions += IntConstant(0);
+ instructions += B->LoadFpRelativeSlot(
+ compiler::target::frame_layout.FrameSlotForVariable(suspend_state) *
+ compiler::target::kWordSize,
+ CompileType::Dynamic(), kTagged);
+ instructions += LoadNativeField(Slot::SuspendState_function_data());
- instructions += BuildExpression(); // read expression.
- if (NeedsDebugStepCheck(parsed_function()->function(), pos)) {
- instructions += DebugStepCheck(pos);
- }
-
- if (parsed_function()->function().IsCompactAsyncStarFunction()) {
- // In the async* functions, generate the following code for yield <expr>:
- //
- // _AsyncStarStreamController controller = :suspend_state._functionData;
- // if (controller.add(<expr>)) {
- // return;
- // }
- // suspend();
- //
- // Generate the following code for yield* <expr>:
- //
- // _AsyncStarStreamController controller = :suspend_state._functionData;
- // controller.addStream(<expr>);
- // if (suspend()) {
- // return;
- // }
- //
-
- auto& add_method = Function::ZoneHandle(Z);
- if (is_yield_star) {
- add_method =
- IG->object_store()->async_star_stream_controller_add_stream();
- } else {
- add_method = IG->object_store()->async_star_stream_controller_add();
- }
- instructions += StaticCall(TokenPosition::kNoSource, add_method, 2,
- ICData::kNoRebind);
-
- if (is_yield_star) {
- // Discard result of _AsyncStarStreamController.addStream().
- instructions += Drop();
- // Suspend and test value passed to the resumed async* body.
- instructions += NullConstant();
- instructions += B->Suspend(pos, SuspendInstr::StubId::kYieldAsyncStar);
- } else {
- // Test value returned by _AsyncStarStreamController.add().
- }
-
- TargetEntryInstr* exit;
- TargetEntryInstr* continue_execution;
- instructions += BranchIfTrue(&exit, &continue_execution, false);
-
- Fragment do_exit(exit);
- do_exit += TranslateFinallyFinalizers(nullptr, -1);
- do_exit += NullConstant();
- do_exit += Return(TokenPosition::kNoSource);
-
- instructions = Fragment(instructions.entry, continue_execution);
- if (!is_yield_star) {
- instructions += NullConstant();
- instructions += B->Suspend(pos, SuspendInstr::StubId::kYieldAsyncStar);
- instructions += Drop();
- }
-
- } else if (parsed_function()->function().IsCompactSyncStarFunction()) {
- // In the sync* functions, generate the following code for yield <expr>:
- //
- // _SyncStarIterator iterator = :suspend_state._functionData;
- // iterator._current = <expr>;
- // suspend();
- //
- // Generate the following code for yield* <expr>:
- //
- // _SyncStarIterator iterator = :suspend_state._functionData;
- // iterator._yieldStarIterable = <expr>;
- // suspend();
- //
- auto& field = Field::ZoneHandle(Z);
- if (is_yield_star) {
- field = IG->object_store()->sync_star_iterator_yield_star_iterable();
- } else {
- field = IG->object_store()->sync_star_iterator_current();
- }
- instructions += B->StoreInstanceFieldGuarded(field);
- instructions += NullConstant();
- instructions += B->Suspend(pos, SuspendInstr::StubId::kYieldSyncStar);
- instructions += Drop();
- } else {
- UNREACHABLE();
- }
-
- return instructions;
- }
-
- ASSERT(flags == kYieldStatementFlagNative); // Must have been desugared.
-
- // Setup yield/continue point:
- //
- // ...
- // :await_jump_var = index;
- // :await_ctx_var = :current_context_var
- // return <expr>
- //
- // Continuation<index>:
- // Drop(1)
- // ...
- //
- // BuildGraphOfFunction will create a dispatch that jumps to
- // Continuation<:await_jump_var> upon entry to the function.
- //
- const intptr_t new_yield_pos = yield_continuations().length() + 1;
- Fragment instructions = IntConstant(new_yield_pos);
- instructions +=
- StoreLocal(TokenPosition::kNoSource, scopes()->yield_jump_variable);
- instructions += Drop();
- instructions += LoadLocal(parsed_function()->current_context_var());
- instructions +=
- StoreLocal(TokenPosition::kNoSource, scopes()->yield_context_variable);
- instructions += Drop();
instructions += BuildExpression(); // read expression.
- instructions += Return(pos, new_yield_pos);
-
- // Note: DropTempsInstr serves as an anchor instruction. It will not
- // be linked into the resulting graph.
- DropTempsInstr* anchor = new (Z) DropTempsInstr(0, nullptr);
- yield_continuations().Add(YieldContinuation(anchor, CurrentTryIndex()));
-
- Fragment continuation(instructions.entry, anchor);
- RELEASE_ASSERT(parsed_function()->function().IsAsyncClosure() ||
- parsed_function()->function().IsAsyncGenClosure() ||
- parsed_function()->function().IsSyncGenClosure());
-
- // TODO(43900): Only emit this when needed.
- {
- // Our sync-yielding functions can be invoked with either a yield result or
- // with an non-null exception & stacktrace.
- //
- // We detect the case we're in based on the nullability of stacktrace in
- //
- // :sync_op(:iterator, [:exception, :stack_trace]) { }
- //
- // or:
- //
- // :async_op(:result_or_exception, :stack_trace) { }
- //
- const auto& fun = parsed_function()->function();
- LocalVariable* exception_var =
- parsed_function()->ParameterVariable(fun.IsSyncGenClosure() ? 2 : 1);
- LocalVariable* stack_trace_var =
- parsed_function()->ParameterVariable(fun.IsSyncGenClosure() ? 3 : 2);
- ASSERT(stack_trace_var->name().ptr() ==
- Symbols::StackTraceParameter().ptr());
-
- TargetEntryInstr* no_error;
- TargetEntryInstr* error;
-
- continuation += LoadLocal(stack_trace_var);
- continuation += BranchIfNull(&no_error, &error);
-
- Fragment rethrow(/*instruction=*/error);
- rethrow += LoadLocal(exception_var);
- rethrow += LoadLocal(stack_trace_var);
-
- rethrow += RethrowException(pos, kInvalidTryIndex);
- Drop();
-
- // Set current to the end of the no_error branch.
- continuation = Fragment(/*entry=*/continuation.entry, /*current=*/no_error);
+ if (NeedsDebugStepCheck(parsed_function()->function(), pos)) {
+ instructions += DebugStepCheck(pos);
}
- return continuation;
+ if (parsed_function()->function().IsCompactAsyncStarFunction()) {
+ // In the async* functions, generate the following code for yield <expr>:
+ //
+ // _AsyncStarStreamController controller = :suspend_state._functionData;
+ // if (controller.add(<expr>)) {
+ // return;
+ // }
+ // suspend();
+ //
+ // Generate the following code for yield* <expr>:
+ //
+ // _AsyncStarStreamController controller = :suspend_state._functionData;
+ // controller.addStream(<expr>);
+ // if (suspend()) {
+ // return;
+ // }
+ //
+
+ auto& add_method = Function::ZoneHandle(Z);
+ if (is_yield_star) {
+ add_method =
+ IG->object_store()->async_star_stream_controller_add_stream();
+ } else {
+ add_method = IG->object_store()->async_star_stream_controller_add();
+ }
+ instructions +=
+ StaticCall(TokenPosition::kNoSource, add_method, 2, ICData::kNoRebind);
+
+ if (is_yield_star) {
+ // Discard result of _AsyncStarStreamController.addStream().
+ instructions += Drop();
+ // Suspend and test value passed to the resumed async* body.
+ instructions += NullConstant();
+ instructions += B->Suspend(pos, SuspendInstr::StubId::kYieldAsyncStar);
+ } else {
+ // Test value returned by _AsyncStarStreamController.add().
+ }
+
+ TargetEntryInstr* exit;
+ TargetEntryInstr* continue_execution;
+ instructions += BranchIfTrue(&exit, &continue_execution, false);
+
+ Fragment do_exit(exit);
+ do_exit += TranslateFinallyFinalizers(nullptr, -1);
+ do_exit += NullConstant();
+ do_exit += Return(TokenPosition::kNoSource);
+
+ instructions = Fragment(instructions.entry, continue_execution);
+ if (!is_yield_star) {
+ instructions += NullConstant();
+ instructions += B->Suspend(pos, SuspendInstr::StubId::kYieldAsyncStar);
+ instructions += Drop();
+ }
+
+ } else if (parsed_function()->function().IsCompactSyncStarFunction()) {
+ // In the sync* functions, generate the following code for yield <expr>:
+ //
+ // _SyncStarIterator iterator = :suspend_state._functionData;
+ // iterator._current = <expr>;
+ // suspend();
+ //
+ // Generate the following code for yield* <expr>:
+ //
+ // _SyncStarIterator iterator = :suspend_state._functionData;
+ // iterator._yieldStarIterable = <expr>;
+ // suspend();
+ //
+ auto& field = Field::ZoneHandle(Z);
+ if (is_yield_star) {
+ field = IG->object_store()->sync_star_iterator_yield_star_iterable();
+ } else {
+ field = IG->object_store()->sync_star_iterator_current();
+ }
+ instructions += B->StoreInstanceFieldGuarded(field);
+ instructions += NullConstant();
+ instructions += B->Suspend(pos, SuspendInstr::StubId::kYieldSyncStar);
+ instructions += Drop();
+ } else {
+ UNREACHABLE();
+ }
+
+ return instructions;
}
Fragment StreamingFlowGraphBuilder::BuildVariableDeclaration(
@@ -5667,10 +5587,8 @@
function.set_is_visible(true);
ASSERT(function.IsCompactSyncStarFunction());
} else {
- ASSERT((function_node_helper.async_marker_ ==
- FunctionNodeHelper::kSync) ||
- (function_node_helper.async_marker_ ==
- FunctionNodeHelper::kSyncYielding));
+ ASSERT(function_node_helper.async_marker_ ==
+ FunctionNodeHelper::kSync);
function.set_is_debuggable(function_node_helper.dart_async_marker_ ==
FunctionNodeHelper::kSync);
switch (function_node_helper.dart_async_marker_) {
@@ -5687,8 +5605,7 @@
// no special modifier
break;
}
- function.set_is_generated_body(function_node_helper.async_marker_ ==
- FunctionNodeHelper::kSyncYielding);
+ function.set_is_generated_body(false);
// sync* functions contain two nested synthetic functions,
// the first of which (sync_op_gen) is a regular sync function so we
// need to manually label it generated:
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.h b/runtime/vm/compiler/frontend/kernel_translation_helper.h
index e8067f4..314cab9 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.h
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.h
@@ -307,7 +307,6 @@
kSyncStar = 1,
kAsync = 2,
kAsyncStar = 3,
- kSyncYielding = 4,
};
explicit FunctionNodeHelper(KernelReaderHelper* helper) {
diff --git a/runtime/vm/compiler/frontend/scope_builder.cc b/runtime/vm/compiler/frontend/scope_builder.cc
index 2db5f8f..3e7f2f8 100644
--- a/runtime/vm/compiler/frontend/scope_builder.cc
+++ b/runtime/vm/compiler/frontend/scope_builder.cc
@@ -570,50 +570,6 @@
first_body_token_position_ = helper_.reader_.min_position();
}
- // Ensure that :await_jump_var, :await_ctx_var, :async_op, :is_sync and
- // :async_future are captured.
- if (function_node_helper.async_marker_ == FunctionNodeHelper::kSyncYielding) {
- {
- LocalVariable* temp = nullptr;
- LookupCapturedVariableByName(
- (depth_.function_ == 0) ? &result_->yield_jump_variable : &temp,
- Symbols::AwaitJumpVar());
- }
- {
- LocalVariable* temp = nullptr;
- LookupCapturedVariableByName(
- (depth_.function_ == 0) ? &result_->yield_context_variable : &temp,
- Symbols::AwaitContextVar());
- }
- {
- LocalVariable* temp =
- scope_->LookupVariable(Symbols::AsyncOperation(), true);
- if (temp != nullptr) {
- scope_->CaptureVariable(temp);
- }
- }
- {
- LocalVariable* temp =
- scope_->LookupVariable(Symbols::AsyncFuture(), true);
- if (temp != nullptr) {
- scope_->CaptureVariable(temp);
- }
- }
- {
- LocalVariable* temp = scope_->LookupVariable(Symbols::is_sync(), true);
- if (temp != nullptr) {
- scope_->CaptureVariable(temp);
- }
- }
- {
- LocalVariable* temp =
- scope_->LookupVariable(Symbols::ControllerStream(), true);
- if (temp != nullptr) {
- scope_->CaptureVariable(temp);
- }
- }
- }
-
// Mark known chained futures such as _Future::timeout()'s _future.
if (function.recognized_kind() == MethodRecognizer::kFutureTimeout &&
depth_.function_ == 1) {
@@ -1289,20 +1245,8 @@
}
case kYieldStatement: {
helper_.ReadPosition(); // read position.
- word flags = helper_.ReadByte(); // read flags.
+ helper_.ReadByte(); // read flags.
VisitExpression(); // read expression.
-
- if ((flags & kYieldStatementFlagNative) != 0) {
- if (depth_.function_ == 0) {
- AddSwitchVariable();
- // Promote all currently visible local variables into the context.
- // TODO(27590) CaptureLocalVariables promotes to many variables into
- // the scope. Mark those variables as stack_local.
- // TODO(27590) we don't need to promote those variables that are
- // not used across yields.
- scope_->CaptureLocalVariables(current_function_scope_);
- }
- }
return;
}
case kVariableDeclaration:
@@ -1635,13 +1579,6 @@
variable->set_is_explicit_covariant_parameter();
}
- // The :sync_op and :async_op continuations are called multiple times. So we
- // don't want the parameters from the first invocation to get stored in the
- // context and reused on later invocations with different parameters.
- if (current_function_async_marker_ == FunctionNodeHelper::kSyncYielding) {
- variable->set_is_forced_stack();
- }
-
const bool needs_covariant_check_in_method =
helper.IsCovariant() ||
(helper.IsGenericCovariantImpl() &&
@@ -1730,23 +1667,6 @@
intptr_t nesting_depth) {
LocalVariable* v = NULL;
- // If we are inside a function with yield points then Kernel transformer
- // could have lifted some of the auxiliary exception variables into the
- // context to preserve them across yield points because they might
- // be needed for rethrow.
- // Check if it did and capture such variables instead of introducing
- // new local ones.
- // Note: function that wrap kSyncYielding function does not contain
- // its own try/catches.
- if (current_function_async_marker_ == FunctionNodeHelper::kSyncYielding) {
- ASSERT(current_function_scope_->parent() != NULL);
- v = current_function_scope_->parent()->LocalLookupVariable(
- GenerateName(prefix, nesting_depth - 1));
- if (v != NULL) {
- scope_->CaptureVariable(v);
- }
- }
-
// No need to create variables for try/catch-statements inside
// nested functions.
if (depth_.function_ > 0) return;
diff --git a/runtime/vm/compiler/graph_intrinsifier.cc b/runtime/vm/compiler/graph_intrinsifier.cc
index 148a752..9765e4e 100644
--- a/runtime/vm/compiler/graph_intrinsifier.cc
+++ b/runtime/vm/compiler/graph_intrinsifier.cc
@@ -673,8 +673,8 @@
return BuildSimdOp(flow_graph, kFloat64x2Cid, Token::kADD);
}
-static bool BuildFloat32x4Shuffle(FlowGraph* flow_graph,
- MethodRecognizer::Kind kind) {
+static bool BuildFloat32x4Get(FlowGraph* flow_graph,
+ MethodRecognizer::Kind kind) {
if (!FlowGraphCompiler::SupportsUnboxedDoubles() ||
!FlowGraphCompiler::SupportsUnboxedSimd128()) {
return false;
@@ -702,24 +702,20 @@
return true;
}
-bool GraphIntrinsifier::Build_Float32x4ShuffleX(FlowGraph* flow_graph) {
- return BuildFloat32x4Shuffle(flow_graph,
- MethodRecognizer::kFloat32x4ShuffleX);
+bool GraphIntrinsifier::Build_Float32x4GetX(FlowGraph* flow_graph) {
+ return BuildFloat32x4Get(flow_graph, MethodRecognizer::kFloat32x4GetX);
}
-bool GraphIntrinsifier::Build_Float32x4ShuffleY(FlowGraph* flow_graph) {
- return BuildFloat32x4Shuffle(flow_graph,
- MethodRecognizer::kFloat32x4ShuffleY);
+bool GraphIntrinsifier::Build_Float32x4GetY(FlowGraph* flow_graph) {
+ return BuildFloat32x4Get(flow_graph, MethodRecognizer::kFloat32x4GetY);
}
-bool GraphIntrinsifier::Build_Float32x4ShuffleZ(FlowGraph* flow_graph) {
- return BuildFloat32x4Shuffle(flow_graph,
- MethodRecognizer::kFloat32x4ShuffleZ);
+bool GraphIntrinsifier::Build_Float32x4GetZ(FlowGraph* flow_graph) {
+ return BuildFloat32x4Get(flow_graph, MethodRecognizer::kFloat32x4GetZ);
}
-bool GraphIntrinsifier::Build_Float32x4ShuffleW(FlowGraph* flow_graph) {
- return BuildFloat32x4Shuffle(flow_graph,
- MethodRecognizer::kFloat32x4ShuffleW);
+bool GraphIntrinsifier::Build_Float32x4GetW(FlowGraph* flow_graph) {
+ return BuildFloat32x4Get(flow_graph, MethodRecognizer::kFloat32x4GetW);
}
static bool BuildLoadField(FlowGraph* flow_graph, const Slot& field) {
diff --git a/runtime/vm/compiler/recognized_methods_list.h b/runtime/vm/compiler/recognized_methods_list.h
index 010e653..726488f 100644
--- a/runtime/vm/compiler/recognized_methods_list.h
+++ b/runtime/vm/compiler/recognized_methods_list.h
@@ -386,10 +386,10 @@
V(_Float64x2List, []=, Float64x2ArraySetIndexed, 0x8af8aa58) \
V(_TypedListBase, get:length, TypedListBaseLength, 0x5850f06b) \
V(_ByteDataView, get:length, ByteDataViewLength, 0x5850f06b) \
- V(_Float32x4, get:x, Float32x4ShuffleX, 0x3a398530) \
- V(_Float32x4, get:y, Float32x4ShuffleY, 0x27cae053) \
- V(_Float32x4, get:z, Float32x4ShuffleZ, 0x5d964be9) \
- V(_Float32x4, get:w, Float32x4ShuffleW, 0x3fd6906b) \
+ V(_Float32x4, get:x, Float32x4GetX, 0x3a398530) \
+ V(_Float32x4, get:y, Float32x4GetY, 0x27cae053) \
+ V(_Float32x4, get:z, Float32x4GetZ, 0x5d964be9) \
+ V(_Float32x4, get:w, Float32x4GetW, 0x3fd6906b) \
V(_Float32x4, *, Float32x4Mul, 0xe5507c87) \
V(_Float32x4, /, Float32x4Div, 0xc09f2f62) \
V(_Float32x4, -, Float32x4Sub, 0xdd326c4a) \
diff --git a/runtime/vm/kernel_binary.h b/runtime/vm/kernel_binary.h
index 3801578..b0658b3 100644
--- a/runtime/vm/kernel_binary.h
+++ b/runtime/vm/kernel_binary.h
@@ -20,8 +20,8 @@
static const uint32_t kMagicProgramFile = 0x90ABCDEFu;
// Both version numbers are inclusive.
-static const uint32_t kMinSupportedKernelFormatVersion = 82;
-static const uint32_t kMaxSupportedKernelFormatVersion = 82;
+static const uint32_t kMinSupportedKernelFormatVersion = 83;
+static const uint32_t kMaxSupportedKernelFormatVersion = 83;
// Keep in sync with package:kernel/lib/binary/tag.dart
#define KERNEL_TAG_LIST(V) \
@@ -218,7 +218,6 @@
// Keep in sync with package:kernel/lib/ast.dart
enum YieldStatementFlags {
kYieldStatementFlagYieldStar = 1 << 0,
- kYieldStatementFlagNative = 1 << 1,
};
// Keep in sync with package:kernel/lib/ast.dart
diff --git a/sdk/lib/_internal/js_dev_runtime/patch/developer_patch.dart b/sdk/lib/_internal/js_dev_runtime/patch/developer_patch.dart
index c451d7f..729dc22 100644
--- a/sdk/lib/_internal/js_dev_runtime/patch/developer_patch.dart
+++ b/sdk/lib/_internal/js_dev_runtime/patch/developer_patch.dart
@@ -213,9 +213,9 @@
return existingTag;
}
// Throw an exception if we've reached the maximum number of user tags.
- if (_instances.length == UserTag.MAX_USER_TAGS) {
+ if (_instances.length == UserTag.maxUserTags) {
throw UnsupportedError(
- 'UserTag instance limit (${UserTag.MAX_USER_TAGS}) reached.');
+ 'UserTag instance limit (${UserTag.maxUserTags}) reached.');
}
return _instances[label] = _FakeUserTag.real(label);
}
diff --git a/sdk/lib/_internal/js_runtime/lib/developer_patch.dart b/sdk/lib/_internal/js_runtime/lib/developer_patch.dart
index 54f7ca1..05db6d6 100644
--- a/sdk/lib/_internal/js_runtime/lib/developer_patch.dart
+++ b/sdk/lib/_internal/js_runtime/lib/developer_patch.dart
@@ -136,9 +136,9 @@
return existingTag;
}
// Throw an exception if we've reached the maximum number of user tags.
- if (_instances.length == UserTag.MAX_USER_TAGS) {
+ if (_instances.length == UserTag.maxUserTags) {
throw UnsupportedError(
- 'UserTag instance limit (${UserTag.MAX_USER_TAGS}) reached.');
+ 'UserTag instance limit (${UserTag.maxUserTags}) reached.');
}
return _instances[label] = _FakeUserTag.real(label);
}
diff --git a/sdk/lib/developer/profiler.dart b/sdk/lib/developer/profiler.dart
index 54bfd25..bf6ae6b 100644
--- a/sdk/lib/developer/profiler.dart
+++ b/sdk/lib/developer/profiler.dart
@@ -8,7 +8,12 @@
/// [DevTools CPU profiler](https://flutter.dev/docs/development/tools/devtools/cpu-profiler).
abstract class UserTag {
/// The maximum number of UserTag instances that can be created by a program.
- static const MAX_USER_TAGS = 64;
+ static const maxUserTags = 64;
+
+ @deprecated
+ // TODO: We shouldn't be using SCREAMING_CAPS for constants, so this should
+ // be removed for Dart 3.0.
+ static const MAX_USER_TAGS = maxUserTags;
external factory UserTag(String label);
@@ -112,7 +117,7 @@
/// The current set of registered [Metric]s.
static UnmodifiableMapView<String, Metric> get current =>
UnmodifiableMapView<String, Metric>(_metrics);
- static final Map<String, Metric> _metrics = new Map<String, Metric>();
+ static final _metrics = <String, Metric>{};
/// Register [Metric]s to make them visible to Observatory.
static void register(Metric metric) {
diff --git a/tests/lib/async/syncstar_mixed_iterators_test.dart b/tests/lib/async/syncstar_mixed_iterators_test.dart
index 2b2ecf1..338c52b 100644
--- a/tests/lib/async/syncstar_mixed_iterators_test.dart
+++ b/tests/lib/async/syncstar_mixed_iterators_test.dart
@@ -3,13 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
//
// Test that sync* correctly iterates through nested iterators of both
-// the internal sync* transform _SyncIterable and generic Iterable types.
+// the internal sync* iterable and generic Iterable types.
import "package:expect/expect.dart";
Iterable<int> outerSyncStar() sync* {
yield 1;
- // _SyncIterable<int>:
+ // internal sync* iterable:
yield* innerSyncStar();
yield 4;
// Generic Iterable<int>:
diff --git a/tools/VERSION b/tools/VERSION
index 2c1d3fb..1ea2e37 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 18
PATCH 0
-PRERELEASE 274
+PRERELEASE 275
PRERELEASE_PATCH 0
\ No newline at end of file