mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 11:02:31 +00:00
[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>
This commit is contained in:
parent
3f5a96bbf4
commit
2fc877a09a
|
@ -170,14 +170,7 @@ class ImpactBuilder extends ir.RecursiveVisitor implements ImpactRegistry {
|
|||
|
||||
@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 @@ class ImpactBuilder extends ir.RecursiveVisitor implements ImpactRegistry {
|
|||
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 @@ class ImpactBuilder extends ir.RecursiveVisitor implements ImpactRegistry {
|
|||
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) {
|
||||
|
|
|
@ -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 @@ class RuntimeTypeUseData {
|
|||
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 @@ class RuntimeTypeUseData {
|
|||
|
||||
/// 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 @@ class RuntimeTypeUseData {
|
|||
///
|
||||
/// [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 @@ RuntimeTypeUseData computeRuntimeTypeUse(
|
|||
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 @@ RuntimeTypeUseData computeRuntimeTypeUse(
|
|||
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 @@ RuntimeTypeUseData computeRuntimeTypeUse(
|
|||
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 @@ RuntimeTypeUseData computeRuntimeTypeUse(
|
|||
// 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 @@ RuntimeTypeUseData computeRuntimeTypeUse(
|
|||
}
|
||||
} 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 @@ RuntimeTypeUseData computeRuntimeTypeUse(
|
|||
// 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 @@ RuntimeTypeUseData computeRuntimeTypeUse(
|
|||
// 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 @@ RuntimeTypeUseData computeRuntimeTypeUse(
|
|||
// e0.runtimeType.==(e1.runtimeType)
|
||||
// ^
|
||||
kind = RuntimeTypeUseKind.equals;
|
||||
receiver = otherGetRuntimeType.receiver;
|
||||
receiverGet = otherGetRuntimeType.node;
|
||||
argument = runtimeTypeAccess.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;
|
||||
receiver = getRuntimeType.receiver;
|
||||
receiverGet = getRuntimeType;
|
||||
argument = node.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 @@ RuntimeTypeUseData computeRuntimeTypeUse(
|
|||
// '${e.runtimeType}'
|
||||
// ^
|
||||
kind = RuntimeTypeUseKind.string;
|
||||
receiver = runtimeTypeAccess.receiver;
|
||||
receiver = node.receiver;
|
||||
receiverGet = node;
|
||||
}
|
||||
|
||||
|
@ -404,7 +380,7 @@ RuntimeTypeUseData computeRuntimeTypeUse(
|
|||
// e.runtimeType
|
||||
// ^
|
||||
kind = RuntimeTypeUseKind.unknown;
|
||||
receiver = runtimeTypeAccess.receiver;
|
||||
receiver = node.receiver;
|
||||
receiverGet = node;
|
||||
}
|
||||
|
||||
|
@ -427,37 +403,60 @@ bool _isObjectMethodInvocation(ir.TreeNode? node) {
|
|||
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);
|
||||
}
|
||||
|
|
|
@ -101,52 +101,6 @@ bool isNullLiteral(ir.Expression node) {
|
|||
(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) {
|
||||
|
|
Loading…
Reference in a new issue