blob: 28122d7ba3940aab9aff08d620d721e8e24ecbef [file] [log] [blame]
// Copyright (c) 2020, 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 '../fasta/kernel/late_lowering.dart';
import '../fasta/source/source_extension_builder.dart' show extensionThisName;
/// Returns `true` if [node] is the field holding the value of a lowered late
/// field.
///
/// For instance
///
/// late int field;
///
/// is lowered to (simplified):
///
/// int? _#field = null;
/// int get field => _#field != null ? _#field : throw 'Uninitialized';
/// void set field(int value) {
/// _#field = value;
/// }
///
/// where '_#field' is the field holding that value.
///
/// The default value of this field is `null`.
bool isLateLoweredField(Field node) {
return node.isInternalImplementation &&
node.name != null &&
node.name.text.startsWith(lateFieldPrefix) &&
!node.name.text.endsWith(lateIsSetSuffix);
}
/// Returns `true` if [node] is the field holding the marker for whether a
/// lowered late field has been set or not.
///
/// For instance
///
/// late int? field;
///
/// is lowered to (simplified):
///
/// bool _#field#isSet = false;
/// int? _#field = null;
/// int get field => _#field#isSet ? _#field : throw 'Uninitialized';
/// void set field(int value) {
/// _#field = value;
/// _#field#isSet = true;
/// }
///
/// where '_#field#isSet' is the field holding the marker.
///
/// The default value of this field is `false`.
bool isLateLoweredIsSetField(Field node) {
return node.isInternalImplementation &&
node.name != null &&
node.name.text.startsWith(lateFieldPrefix) &&
node.name.text.endsWith(lateIsSetSuffix);
}
/// Returns `true` if [node] is the getter for reading the value of a lowered
/// late field.
///
/// For instance
///
/// late int field;
///
/// is lowered to (simplified):
///
/// int? _#field = null;
/// int get field => _#field != null ? _#field : throw 'Uninitialized';
/// void set field(int value) {
/// _#field = value;
/// }
///
/// where 'int get field' is the getter for reading the field.
///
/// Note that the computation of this predicate is _not_ efficient and the
/// result should be cached on the use site if queried repeatedly.
bool isLateLoweredFieldGetter(Procedure node) {
if (node.kind == ProcedureKind.Getter) {
TreeNode parent = node.parent;
if (parent is Class) {
return parent.fields.any((Field field) =>
isLateLoweredField(field) &&
field.name.text.endsWith(node.name.text));
} else if (parent is Library) {
return parent.fields.any((Field field) =>
isLateLoweredField(field) &&
field.name.text.endsWith(node.name.text));
}
}
return false;
}
/// Returns `true` if [node] is the setter for setting the value of a lowered
/// late field.
///
/// For instance
///
/// late int field;
///
/// is lowered to (simplified):
///
/// int? _#field = null;
/// int get field => _#field != null ? _#field : throw 'Uninitialized';
/// void set field(int value) {
/// _#field = value;
/// }
///
/// where 'void set field' is the setter for setting the value of the field.
///
/// Note that the computation of this predicate is _not_ efficient and the
/// result should be cached on the use site if queried repeatedly.
bool isLateLoweredFieldSetter(Procedure node) {
if (node.kind == ProcedureKind.Setter) {
TreeNode parent = node.parent;
if (parent is Class) {
return parent.fields.any((Field field) =>
isLateLoweredField(field) &&
field.name.text.endsWith(node.name.text));
} else if (parent is Library) {
return parent.fields.any((Field field) =>
isLateLoweredField(field) &&
field.name.text.endsWith(node.name.text));
}
}
return false;
}
/// Returns `true` if [node] is the local variable holding the value of a
/// lowered late variable.
///
/// For instance
///
/// late int local;
///
/// is lowered to (simplified):
///
/// int? #local = null;
/// int #local#get() => #local != null ? #local : throw 'Uninitialized';
/// void #local#set(int value) {
/// #local = value;
/// }
///
/// where '#local' is the local variable holding that value.
///
/// The default value of this variable is `null`.
bool isLateLoweredLocal(VariableDeclaration node) {
assert(node.isLowered ||
node.name == null ||
!isLateLoweredLocalName(node.name));
return node.isLowered && isLateLoweredLocalName(node.name);
}
/// Returns `true` if [name] is the name of a local variable holding the value
/// of a lowered late variable.
bool isLateLoweredLocalName(String name) {
return name != extensionThisName &&
name.startsWith(lateLocalPrefix) &&
!name.endsWith(lateIsSetSuffix) &&
!name.endsWith(lateLocalGetterSuffix) &&
!name.endsWith(lateLocalSetterSuffix);
}
/// Returns the name of the original late local variable from the [name] of the
/// local variable holding the value of the lowered late variable.
///
/// This method assumes that `isLateLoweredLocalName(name)` is `true`.
String extractLocalNameFromLateLoweredLocal(String name) {
return name.substring(lateLocalPrefix.length);
}
/// Returns `true` if [node] is the local variable holding the marker for
/// whether a lowered late local variable has been set or not.
///
/// For instance
///
/// late int? local;
///
/// is lowered to (simplified):
///
/// bool #local#isSet = false;
/// int? #local = null;
/// int #local#get() => _#field#isSet ? #local : throw 'Uninitialized';
/// void #local#set(int value) {
/// #local = value;
/// #local#isSet = true;
/// }
///
/// where '#local#isSet' is the local variable holding the marker.
///
/// The default value of this variable is `false`.
bool isLateLoweredIsSetLocal(VariableDeclaration node) {
assert(node.isLowered ||
node.name == null ||
!isLateLoweredIsSetLocalName(node.name));
return node.isLowered && isLateLoweredIsSetLocalName(node.name);
}
/// Returns `true` if [name] is the name of a local variable holding the marker
/// for whether a lowered late local variable has been set or not.
bool isLateLoweredIsSetLocalName(String name) {
return name.startsWith(lateLocalPrefix) && name.endsWith(lateIsSetSuffix);
}
/// Returns the name of the original late local variable from the [name] of the
/// local variable holding the marker for whether the lowered late local
/// variable has been set or not.
///
/// This method assumes that `isLateLoweredIsSetName(name)` is `true`.
String extractLocalNameFromLateLoweredIsSet(String name) {
return name.substring(
lateLocalPrefix.length, name.length - lateIsSetSuffix.length);
}
/// Returns `true` if [node] is the local variable for the local function for
/// reading the value of a lowered late variable.
///
/// For instance
///
/// late int local;
///
/// is lowered to (simplified):
///
/// int? #local = null;
/// int #local#get() => #local != null ? #local : throw 'Uninitialized';
/// void #local#set(int value) {
/// #local = value;
/// }
///
/// where '#local#get' is the local function for reading the variable.
bool isLateLoweredLocalGetter(VariableDeclaration node) {
assert(node.isLowered ||
node.name == null ||
!isLateLoweredLocalGetterName(node.name));
return node.isLowered && isLateLoweredLocalGetterName(node.name);
}
/// Returns `true` if [name] is the name of the local variable for the local
/// function for reading the value of a lowered late variable.
bool isLateLoweredLocalGetterName(String name) {
return name.startsWith(lateLocalPrefix) &&
name.endsWith(lateLocalGetterSuffix);
}
/// Returns the name of the original late local variable from the [name] of the
/// local variable for the local function for reading the value of the lowered
/// late variable.
///
/// This method assumes that `isLateLoweredGetterName(name)` is `true`.
String extractLocalNameFromLateLoweredGetter(String name) {
return name.substring(
lateLocalPrefix.length, name.length - lateLocalGetterSuffix.length);
}
/// Returns `true` if [node] is the local variable for the local function for
/// setting the value of a lowered late variable.
///
/// For instance
///
/// late int local;
///
/// is lowered to (simplified):
///
/// int? #local = null;
/// int #local#get() => #local != null ? #local : throw 'Uninitialized';
/// void #local#set(int value) {
/// #local = value;
/// }
///
/// where '#local#set' is the local function for setting the value of the
/// variable.
bool isLateLoweredLocalSetter(VariableDeclaration node) {
assert(node.isLowered ||
node.name == null ||
!isLateLoweredLocalSetterName(node.name));
return node.isLowered && isLateLoweredLocalSetterName(node.name);
}
/// Returns `true` if [name] is the name of the local variable for the local
/// function for setting the value of a lowered late variable.
bool isLateLoweredLocalSetterName(String name) {
return name.startsWith(lateLocalPrefix) &&
name.endsWith(lateLocalSetterSuffix);
}
/// Returns the name of the original late local variable from the [name] of the
/// local variable for the local function for setting the value of the lowered
/// late variable.
///
/// This method assumes that `isLateLoweredSetterName(name)` is `true`.
String extractLocalNameFromLateLoweredSetter(String name) {
return name.substring(
lateLocalPrefix.length, name.length - lateLocalSetterSuffix.length);
}
/// Returns `true` if [node] is the synthetic parameter holding the `this` value
/// in the encoding of extension instance members.
///
/// For instance
///
/// extension Extension on int {
/// int method() => this;
/// }
///
/// is encoded as
///
/// int Extension|method(int #this) => #this;
///
/// where '#this' is the synthetic "extension this" parameter.
bool isExtensionThis(VariableDeclaration node) {
assert(
node.isLowered || node.name == null || !isExtensionThisName(node.name));
return node.isLowered && isExtensionThisName(node.name);
}
/// Returns `true` if [name] is the name of the synthetic parameter holding the
/// `this` value in the encoding of extension instance members.
bool isExtensionThisName(String name) {
return name == extensionThisName;
}
/// Returns the name of the original variable from the [name] of the synthetic
/// parameter holding the `this` value in the encoding of extension instance
/// members.
///
/// This method assumes that `isExtensionThisName(name)` is `true`.
String extractLocalNameForExtensionThis(String name) {
return 'this';
}
/// Returns the original name of the variable [node].
///
/// If [node] is a lowered variable then the name before lowering is returned.
/// Otherwise the name of the variable itself is returned.
///
/// Note that the name can be `null` in case of a synthetic variable created
/// for instance for encoding of `?.`.
String extractLocalNameFromVariable(VariableDeclaration node) {
if (node.isLowered) {
String name = _extractLocalName(node.name);
if (name == null) {
throw new UnsupportedError("Unrecognized lowered local $node");
}
return name;
}
return node.name;
}
/// Returns the original name of a variable by the given [name].
///
/// If [name] is the name of a lowered variable then the name before lowering is
/// returned. Otherwise the name of the variable itself is returned.
///
/// This assumed that [name] is non-null.
String extractLocalName(String name) {
return _extractLocalName(name) ?? name;
}
/// Returns the original name of a lowered variable by the given [name].
///
/// If [name] doesn't correspond to a lowered name `null` is returned.
String _extractLocalName(String name) {
if (isExtensionThisName(name)) {
return extractLocalNameForExtensionThis(name);
} else if (isLateLoweredLocalName(name)) {
return extractLocalNameFromLateLoweredLocal(name);
} else if (isLateLoweredLocalGetterName(name)) {
return extractLocalNameFromLateLoweredGetter(name);
} else if (isLateLoweredLocalSetterName(name)) {
return extractLocalNameFromLateLoweredSetter(name);
} else if (isLateLoweredIsSetLocalName(name)) {
return extractLocalNameFromLateLoweredIsSet(name);
}
return null;
}