[dart2js] Use new method encoding in runtime type analysis.
Change-Id: I41e6b6314266203e37badcb084becd95894bdf6c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/352966
Commit-Queue: Mayank Patke <fishythefish@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/compiler/lib/src/ir/impact_data.dart b/pkg/compiler/lib/src/ir/impact_data.dart
index 2416052..6623eed 100644
--- a/pkg/compiler/lib/src/ir/impact_data.dart
+++ b/pkg/compiler/lib/src/ir/impact_data.dart
@@ -170,14 +170,7 @@
@override
void visitStaticGet(ir.StaticGet node) {
- final target = node.target;
- if (target is ir.Procedure && target.kind == ir.ProcedureKind.Method) {
- // TODO(johnniwinther): Remove this when dart2js uses the new method
- // invocation encoding.
- registerStaticTearOff(target, getDeferredImport(node));
- } else {
- registerStaticGet(node.target, getDeferredImport(node));
- }
+ registerStaticGet(node.target, getDeferredImport(node));
}
@override
@@ -602,10 +595,6 @@
void visitDynamicGet(ir.DynamicGet node) {
final receiverType = node.receiver.getStaticType(staticTypeContext);
registerDynamicGet(receiverType, node.name);
- if (node.name.text == Identifiers.runtimeType_) {
- // This handles `runtimeType` access on `Never`.
- handleRuntimeTypeGet(receiverType, node);
- }
node.receiver.accept(this);
}
@@ -708,11 +697,9 @@
node.arguments.accept(this);
}
- // TODO(johnniwinther): Change the key to `InstanceGet` when the old method
- // invocation encoding is no longer used.
- final Map<ir.Expression, RuntimeTypeUseData> _pendingRuntimeTypeUseData = {};
+ final Map<ir.InstanceGet, RuntimeTypeUseData> _pendingRuntimeTypeUseData = {};
- void handleRuntimeTypeGet(ir.DartType receiverType, ir.Expression node) {
+ void handleRuntimeTypeGet(ir.DartType receiverType, ir.InstanceGet node) {
RuntimeTypeUseData data =
computeRuntimeTypeUse(_pendingRuntimeTypeUseData, node);
if (data.leftRuntimeTypeExpression == node) {
diff --git a/pkg/compiler/lib/src/ir/runtime_type_analysis.dart b/pkg/compiler/lib/src/ir/runtime_type_analysis.dart
index b6ba6c1..1660dd3 100644
--- a/pkg/compiler/lib/src/ir/runtime_type_analysis.dart
+++ b/pkg/compiler/lib/src/ir/runtime_type_analysis.dart
@@ -5,7 +5,6 @@
import 'package:kernel/ast.dart' as ir;
import '../common/names.dart';
-import 'util.dart';
/// Enum for recognized use kinds of `Object.runtimeType`.
enum RuntimeTypeUseKind {
@@ -29,9 +28,7 @@
final RuntimeTypeUseKind kind;
/// The property get for the left (or single) occurrence of `.runtimeType`.
- // TODO(johnniwinther): Change this to `InstanceGet` when the old method
- // invocation encoding is no longer used.
- final ir.Expression leftRuntimeTypeExpression;
+ final ir.InstanceGet leftRuntimeTypeExpression;
/// The receiver expression.
final ir.Expression receiver;
@@ -42,9 +39,7 @@
/// The property get for the right occurrence of `.runtimeType` when [kind]
/// is `RuntimeTypeUseKind.equals`.
- // TODO(johnniwinther): Change this to `InstanceGet` when the old method
- // invocation encoding is no longer used.
- final ir.Expression? rightRuntimeTypeExpression;
+ final ir.InstanceGet? rightRuntimeTypeExpression;
/// The argument expression if [kind] is `RuntimeTypeUseKind.equals`.
final ir.Expression? argument;
@@ -80,58 +75,41 @@
///
/// [cache] is used to ensure that only one [RuntimeTypeUseData] object is
/// created per case, even for the `==` case.
-// TODO(johnniwinther): Change [cache] key and [node] to `InstanceGet` when the
-// old method invocation encoding is no longer used.
RuntimeTypeUseData computeRuntimeTypeUse(
- Map<ir.Expression, RuntimeTypeUseData> cache, ir.Expression node) {
+ Map<ir.InstanceGet, RuntimeTypeUseData> cache, ir.InstanceGet node) {
RuntimeTypeUseData? receiverData = cache[node];
if (receiverData != null) return receiverData;
- /// Returns `true` if [node] is of the form `e.runtimeType`.
- bool isGetRuntimeType(ir.TreeNode node) {
- return node is ir.InstanceGet &&
- node.name.text == Identifiers.runtimeType_ ||
- node is ir.DynamicGet && node.name.text == Identifiers.runtimeType_;
- }
+ assert(_isGetRuntimeType(node));
- /// Returns `true` if [node] is of the form `e.toString()`.
- bool isInvokeToString(ir.TreeNode? node) {
- return node is ir.InstanceInvocation && node.name.text == 'toString';
- }
-
- assert(isGetRuntimeType(node));
- // TODO(johnniwinther): Replace this with `node.receiver` when the old method
- // invocation encoding is no longer used.
- _RuntimeTypeAccess runtimeTypeAccess = _getRuntimeTypeAccess(node)!;
-
- // TODO(johnniwinther): Change [receiverGet] and [argumentGet] to
- // `InstanceGet` when the old method invocation encoding is no longer used.
// TODO(johnniwinther): Special-case `this.runtimeType`.
- late final ir.Expression receiverGet;
+ late final ir.InstanceGet receiverGet;
late final ir.Expression receiver;
- ir.Expression? argumentGet;
+ ir.InstanceGet? argumentGet;
ir.Expression? argument;
RuntimeTypeUseKind? kind;
final nodeParent = node.parent;
final nodeParentParent = nodeParent?.parent;
- if (runtimeTypeAccess.receiver is ir.VariableGet &&
+ if (node.receiver is ir.VariableGet &&
nodeParent is ir.ConditionalExpression &&
nodeParentParent is ir.Let) {
- NullAwareExpression? nullAware = getNullAwareExpression(nodeParentParent);
+ _NullAwareExpression? nullAware = getNullAwareExpression(nodeParentParent);
if (nullAware != null) {
// The node is of the form:
//
// let #t1 = e in #t1 == null ? null : #t1.runtimeType
// ^
- if (nullAware.parent is ir.VariableDeclaration &&
- nullAware.parent.parent is ir.Let) {
- NullAwareExpression? outer =
- getNullAwareExpression(nullAware.parent.parent!);
+ final nullAwareParent = nullAware.parent;
+ final nullAwareParentParent = nullAwareParent.parent;
+ if (nullAwareParent is ir.VariableDeclaration &&
+ nullAwareParentParent is ir.Let) {
+ _NullAwareExpression? outer =
+ getNullAwareExpression(nullAwareParentParent);
if (outer != null &&
outer.receiver == nullAware.let &&
- isInvokeToString(outer.expression)) {
+ _isInvokeToString(outer.expression)) {
// Detected
//
// e?.runtimeType?.toString()
@@ -146,17 +124,14 @@
receiver = nullAware.receiver;
receiverGet = node;
}
- } else if (_isObjectMethodInvocation(nullAware.parent)) {
- _EqualsInvocation? equalsInvocation =
- _getEqualsInvocation(nullAware.parent);
- if (equalsInvocation != null &&
- equalsInvocation.left == nullAware.let) {
+ } else if (_isObjectMethodInvocation(nullAwareParent)) {
+ if (nullAwareParent is ir.EqualsCall &&
+ nullAwareParent.left == nullAware.let) {
// Detected
//
// e0?.runtimeType == other
- _RuntimeTypeAccess? otherGetRuntimeType =
- _getRuntimeTypeAccess(equalsInvocation.right);
- if (otherGetRuntimeType != null) {
+ if (_extractGetRuntimeType(nullAwareParent.right)
+ case final getRuntimeType?) {
// Detected
//
// e0?.runtimeType == e1.runtimeType
@@ -169,31 +144,33 @@
kind = RuntimeTypeUseKind.equals;
receiver = nullAware.receiver;
receiverGet = node;
- argument = otherGetRuntimeType.receiver;
- argumentGet = otherGetRuntimeType.node;
+ argument = getRuntimeType.receiver;
+ argumentGet = getRuntimeType;
}
- NullAwareExpression? otherNullAware =
- getNullAwareExpression(equalsInvocation.right);
- if (otherNullAware != null &&
- isGetRuntimeType(otherNullAware.expression)) {
- // Detected
- //
- // e0?.runtimeType == e1?.runtimeType
- // ^
- // encoded as
- //
- // (let #t1 = e0 in #t1 == null ? null : #t1.runtimeType)
- // ^
- // .==(let #t2 = e1 in #t2 == null ? null : #t2.runtimeType)
- //
- kind = RuntimeTypeUseKind.equals;
- receiver = nullAware.receiver;
- receiverGet = node;
- argument = otherNullAware.receiver;
- argumentGet = otherNullAware.expression;
+ _NullAwareExpression? otherNullAware =
+ getNullAwareExpression(nullAwareParent.right);
+ if (otherNullAware != null) {
+ if (_extractGetRuntimeType(otherNullAware.expression)
+ case final getRuntimeType?) {
+ // Detected
+ //
+ // e0?.runtimeType == e1?.runtimeType
+ // ^
+ // encoded as
+ //
+ // (let #t1 = e0 in #t1 == null ? null : #t1.runtimeType)
+ // ^
+ // .==(let #t2 = e1 in #t2 == null ? null : #t2.runtimeType)
+ //
+ kind = RuntimeTypeUseKind.equals;
+ receiver = nullAware.receiver;
+ receiverGet = node;
+ argument = otherNullAware.receiver;
+ argumentGet = getRuntimeType;
+ }
}
- } else if (isInvokeToString(nullAware.parent)) {
+ } else if (_isInvokeToString(nullAwareParent)) {
// Detected
//
// e?.runtimeType.toString()
@@ -208,20 +185,17 @@
receiver = nullAware.receiver;
receiverGet = node;
}
- } else if (nullAware.parent is ir.Arguments &&
- _isObjectMethodInvocation(nullAware.parent.parent)) {
- _EqualsInvocation? equalsInvocation =
- _getEqualsInvocation(nullAware.parent.parent);
- if (equalsInvocation != null &&
- equalsInvocation.right == nullAware.let) {
+ } else if (nullAwareParent is ir.Arguments &&
+ _isObjectMethodInvocation(nullAwareParentParent)) {
+ if (nullAwareParentParent is ir.EqualsCall &&
+ nullAwareParentParent.right == nullAware.let) {
// [nullAware] is the right hand side of ==.
- _RuntimeTypeAccess? otherGetRuntimeType =
- _getRuntimeTypeAccess(equalsInvocation.left);
- NullAwareExpression? otherNullAware =
- getNullAwareExpression(equalsInvocation.left);
+ _NullAwareExpression? otherNullAware =
+ getNullAwareExpression(nullAwareParentParent.left);
- if (otherGetRuntimeType != null) {
+ if (_extractGetRuntimeType(nullAwareParentParent.left)
+ case final getRuntimeType?) {
// Detected
//
// e0.runtimeType == e1?.runtimeType
@@ -232,31 +206,33 @@
// let #t1 = e1 in #t1 == null ? null : #t1.runtimeType)
// ^
kind = RuntimeTypeUseKind.equals;
- receiver = otherGetRuntimeType.receiver;
- receiverGet = otherGetRuntimeType.node;
+ receiver = getRuntimeType.receiver;
+ receiverGet = getRuntimeType;
argument = nullAware.receiver;
argumentGet = node;
}
- if (otherNullAware != null &&
- isGetRuntimeType(otherNullAware.expression)) {
- // Detected
- //
- // e0?.runtimeType == e1?.runtimeType
- // ^
- // encoded as
- //
- // (let #t1 = e0 in #t1 == null ? null : #t1.runtimeType)
- // .==(let #t2 = e1 in #t2 == null ? null : #t2.runtimeType)
- // ^
- kind = RuntimeTypeUseKind.equals;
- receiver = otherNullAware.receiver;
- receiverGet = otherNullAware.expression;
- argument = nullAware.receiver;
- argumentGet = node;
+ if (otherNullAware != null) {
+ if (_extractGetRuntimeType(otherNullAware.expression)
+ case final getRuntimeType?) {
+ // Detected
+ //
+ // e0?.runtimeType == e1?.runtimeType
+ // ^
+ // encoded as
+ //
+ // (let #t1 = e0 in #t1 == null ? null : #t1.runtimeType)
+ // .==(let #t2 = e1 in #t2 == null ? null : #t2.runtimeType)
+ // ^
+ kind = RuntimeTypeUseKind.equals;
+ receiver = otherNullAware.receiver;
+ receiverGet = getRuntimeType;
+ argument = nullAware.receiver;
+ argumentGet = node;
+ }
}
}
- } else if (nullAware.parent is ir.StringConcatenation) {
+ } else if (nullAwareParent is ir.StringConcatenation) {
// Detected
//
// '${e?.runtimeType}'
@@ -284,8 +260,8 @@
}
} else if (nodeParent is ir.VariableDeclaration &&
nodeParentParent is ir.Let) {
- NullAwareExpression? nullAware = getNullAwareExpression(nodeParentParent);
- if (nullAware != null && isInvokeToString(nullAware.expression)) {
+ _NullAwareExpression? nullAware = getNullAwareExpression(nodeParentParent);
+ if (nullAware != null && _isInvokeToString(nullAware.expression)) {
// Detected
//
// e.runtimeType?.toString()
@@ -295,19 +271,16 @@
// let #t1 = e.runtimeType in #t1 == null ? null : #t1.toString()
// ^
kind = RuntimeTypeUseKind.string;
- receiver = runtimeTypeAccess.receiver;
+ receiver = node.receiver;
receiverGet = node;
}
} else if (_isObjectMethodInvocation(nodeParent)) {
- _EqualsInvocation? equalsInvocation = _getEqualsInvocation(nodeParent);
- if (equalsInvocation != null && equalsInvocation.left == node) {
+ if (nodeParent is ir.EqualsCall && nodeParent.left == node) {
// [node] is the left hand side of ==.
- _RuntimeTypeAccess? otherGetRuntimeType =
- _getRuntimeTypeAccess(equalsInvocation.right);
- NullAwareExpression? nullAware =
- getNullAwareExpression(equalsInvocation.right);
- if (otherGetRuntimeType != null) {
+ _NullAwareExpression? nullAware =
+ getNullAwareExpression(nodeParent.right);
+ if (_extractGetRuntimeType(nodeParent.right) case final getRuntimeType?) {
// Detected
//
// e0.runtimeType == e1.runtimeType
@@ -317,47 +290,47 @@
// e0.runtimeType.==(e1.runtimeType)
// ^
kind = RuntimeTypeUseKind.equals;
- receiver = runtimeTypeAccess.receiver;
+ receiver = node.receiver;
receiverGet = node;
- argument = otherGetRuntimeType.receiver;
- argumentGet = otherGetRuntimeType.node;
- } else if (nullAware != null && isGetRuntimeType(nullAware.expression)) {
- // Detected
- //
- // e0.runtimeType == e1?.runtimeType
- // ^
- // encoded as
- //
- // e0.runtimeType.==(
- // ^
- // let #t1 = e1 in #t1 == null ? null : #t1.runtimeType)
- kind = RuntimeTypeUseKind.equals;
- receiver = runtimeTypeAccess.receiver;
- receiverGet = node;
- argument = nullAware.receiver;
- argumentGet = nullAware.expression;
+ argument = getRuntimeType.receiver;
+ argumentGet = getRuntimeType;
+ } else if (nullAware != null) {
+ if (_extractGetRuntimeType(nullAware.expression)
+ case final getRuntimeType?) {
+ // Detected
+ //
+ // e0.runtimeType == e1?.runtimeType
+ // ^
+ // encoded as
+ //
+ // e0.runtimeType.==(
+ // ^
+ // let #t1 = e1 in #t1 == null ? null : #t1.runtimeType)
+ kind = RuntimeTypeUseKind.equals;
+ receiver = node.receiver;
+ receiverGet = node;
+ argument = nullAware.receiver;
+ argumentGet = getRuntimeType;
+ }
}
- } else if (isInvokeToString(nodeParent)) {
+ } else if (_isInvokeToString(nodeParent)) {
// Detected
//
// e.runtimeType.toString()
// ^
kind = RuntimeTypeUseKind.string;
- receiver = runtimeTypeAccess.receiver;
+ receiver = node.receiver;
receiverGet = node;
}
} else if (nodeParent is ir.Arguments &&
_isObjectMethodInvocation(nodeParentParent)) {
- _EqualsInvocation? _equalsInvocation =
- _getEqualsInvocation(nodeParentParent);
- if (_equalsInvocation != null && _equalsInvocation.right == node) {
+ if (nodeParentParent is ir.EqualsCall && nodeParentParent.right == node) {
// [node] is the right hand side of ==.
- _RuntimeTypeAccess? otherGetRuntimeType =
- _getRuntimeTypeAccess(_equalsInvocation.left);
- NullAwareExpression? nullAware =
- getNullAwareExpression(_equalsInvocation.left);
+ _NullAwareExpression? nullAware =
+ getNullAwareExpression(nodeParentParent.left);
- if (otherGetRuntimeType != null) {
+ if (_extractGetRuntimeType(nodeParentParent.left)
+ case final getRuntimeType?) {
// Detected
//
// e0.runtimeType == e1.runtimeType
@@ -367,25 +340,28 @@
// e0.runtimeType.==(e1.runtimeType)
// ^
kind = RuntimeTypeUseKind.equals;
- receiver = otherGetRuntimeType.receiver;
- receiverGet = otherGetRuntimeType.node;
- argument = runtimeTypeAccess.receiver;
+ receiver = getRuntimeType.receiver;
+ receiverGet = getRuntimeType;
+ argument = node.receiver;
argumentGet = node;
- } else if (nullAware != null && isGetRuntimeType(nullAware.expression)) {
- // Detected
- //
- // e0?.runtimeType == e1.runtimeType
- // ^
- // encoded as
- //
- // (let #t1 = e0 in #t1 == null ? null : #t1.runtimeType)
- // .==(e1.runtimeType)
- // ^
- kind = RuntimeTypeUseKind.equals;
- receiver = nullAware.receiver;
- receiverGet = nullAware.expression;
- argument = runtimeTypeAccess.receiver;
- argumentGet = node;
+ } else if (nullAware != null) {
+ if (_extractGetRuntimeType(nullAware.expression)
+ case final getRuntimeType?) {
+ // Detected
+ //
+ // e0?.runtimeType == e1.runtimeType
+ // ^
+ // encoded as
+ //
+ // (let #t1 = e0 in #t1 == null ? null : #t1.runtimeType)
+ // .==(e1.runtimeType)
+ // ^
+ kind = RuntimeTypeUseKind.equals;
+ receiver = nullAware.receiver;
+ receiverGet = getRuntimeType;
+ argument = node.receiver;
+ argumentGet = node;
+ }
}
}
} else if (nodeParent is ir.StringConcatenation) {
@@ -394,7 +370,7 @@
// '${e.runtimeType}'
// ^
kind = RuntimeTypeUseKind.string;
- receiver = runtimeTypeAccess.receiver;
+ receiver = node.receiver;
receiverGet = node;
}
@@ -404,7 +380,7 @@
// e.runtimeType
// ^
kind = RuntimeTypeUseKind.unknown;
- receiver = runtimeTypeAccess.receiver;
+ receiver = node.receiver;
receiverGet = node;
}
@@ -427,37 +403,60 @@
return node is ir.InstanceInvocation || node is ir.EqualsCall;
}
-/// Returns the [_RuntimeTypeAccess] corresponding to [node] if it is an access
-/// of `.runtimeType`, and `null` otherwise.
-_RuntimeTypeAccess? _getRuntimeTypeAccess(ir.TreeNode node) {
- if (node is ir.InstanceGet && node.name.text == 'runtimeType') {
- return _RuntimeTypeAccess(node, node.receiver);
- } else if (node is ir.DynamicGet && node.name.text == 'runtimeType') {
- return _RuntimeTypeAccess(node, node.receiver);
+/// Returns `true` if [node] is of the form `e.runtimeType` and `null`
+/// otherwise.
+bool _isGetRuntimeType(ir.InstanceGet node) =>
+ node.name.text == Identifiers.runtimeType_;
+
+/// Returns `true` if [node] is of the form `e.toString()`.
+bool _isInvokeToString(ir.TreeNode? node) =>
+ node is ir.InstanceInvocation && node.name.text == 'toString';
+
+ir.InstanceGet? _extractGetRuntimeType(ir.TreeNode node) =>
+ node is ir.InstanceGet && _isGetRuntimeType(node) ? node : null;
+
+/// Kernel encodes a null-aware expression `a?.b` as
+///
+/// let final #1 = a in #1 == null ? null : #1.b
+///
+/// [getNullAwareExpression] recognizes such expressions storing the result in
+/// a [_NullAwareExpression] object.
+///
+/// [syntheticVariable] holds the synthesized `#1` variable. [expression] holds
+/// the `#1.b` expression. [receiver] returns `a` expression. [parent] returns
+/// the parent of the let node, i.e. the parent node of the original null-aware
+/// expression. [let] returns the let node created for the encoding.
+class _NullAwareExpression {
+ final ir.Let let;
+ final ir.VariableDeclaration syntheticVariable;
+ final ir.Expression expression;
+
+ _NullAwareExpression(this.let, this.syntheticVariable, this.expression);
+
+ ir.Expression get receiver => syntheticVariable.initializer!;
+
+ ir.TreeNode get parent => let.parent!;
+
+ @override
+ String toString() => let.toString();
+}
+
+_NullAwareExpression? getNullAwareExpression(ir.TreeNode node) {
+ if (node is ir.Let) {
+ ir.Expression body = node.body;
+ if (node.variable.name == null &&
+ node.variable.isFinal &&
+ body is ir.ConditionalExpression) {
+ final condition = body.condition;
+ if (condition is ir.EqualsNull) {
+ ir.Expression receiver = condition.expression;
+ if (receiver is ir.VariableGet && receiver.variable == node.variable) {
+ // We have
+ // let #t1 = e0 in #t1 == null ? null : e1
+ return _NullAwareExpression(node, node.variable, body.otherwise);
+ }
+ }
+ }
}
return null;
}
-
-class _RuntimeTypeAccess {
- final ir.Expression node;
- final ir.Expression receiver;
-
- _RuntimeTypeAccess(this.node, this.receiver);
-}
-
-/// Returns the [_EqualsInvocation] corresponding to [node] if it is a call to
-/// of `==`, and `null` otherwise.
-_EqualsInvocation? _getEqualsInvocation(ir.TreeNode? node) {
- if (node is ir.EqualsCall) {
- return _EqualsInvocation(node, node.left, node.right);
- }
- return null;
-}
-
-class _EqualsInvocation {
- final ir.Expression node;
- final ir.Expression left;
- final ir.Expression right;
-
- _EqualsInvocation(this.node, this.left, this.right);
-}
diff --git a/pkg/compiler/lib/src/ir/util.dart b/pkg/compiler/lib/src/ir/util.dart
index 0a319e3..09c5983 100644
--- a/pkg/compiler/lib/src/ir/util.dart
+++ b/pkg/compiler/lib/src/ir/util.dart
@@ -101,52 +101,6 @@
(node is ir.ConstantExpression && node.constant is ir.NullConstant);
}
-/// Kernel encodes a null-aware expression `a?.b` as
-///
-/// let final #1 = a in #1 == null ? null : #1.b
-///
-/// [getNullAwareExpression] recognizes such expressions storing the result in
-/// a [NullAwareExpression] object.
-///
-/// [syntheticVariable] holds the synthesized `#1` variable. [expression] holds
-/// the `#1.b` expression. [receiver] returns `a` expression. [parent] returns
-/// the parent of the let node, i.e. the parent node of the original null-aware
-/// expression. [let] returns the let node created for the encoding.
-class NullAwareExpression {
- final ir.Let let;
- final ir.VariableDeclaration syntheticVariable;
- final ir.Expression expression;
-
- NullAwareExpression(this.let, this.syntheticVariable, this.expression);
-
- ir.Expression get receiver => syntheticVariable.initializer!;
-
- ir.TreeNode get parent => let.parent!;
-
- @override
- String toString() => let.toString();
-}
-
-NullAwareExpression? getNullAwareExpression(ir.TreeNode node) {
- if (node is ir.Let) {
- ir.Expression body = node.body;
- if (node.variable.name == null &&
- node.variable.isFinal &&
- body is ir.ConditionalExpression) {
- final condition = body.condition;
- if (condition is ir.EqualsNull) {
- ir.Expression receiver = condition.expression;
- if (receiver is ir.VariableGet && receiver.variable == node.variable) {
- // We have
- // let #t1 = e0 in #t1 == null ? null : e1
- return NullAwareExpression(node, node.variable, body.otherwise);
- }
- }
- }
- }
- return null;
-}
-
/// Check whether [node] is immediately guarded by a
/// [ir.CheckLibraryIsLoaded], and hence the node is a deferred access.
ir.LibraryDependency? getDeferredImport(ir.TreeNode node) {