[dart2js] Add record getters as reachable targets from dynamic call infrastructure.

Change-Id: I78387dffa4a7a16f20e630300bd90b08591efe6c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/287480
Reviewed-by: Stephen Adams <sra@google.com>
Commit-Queue: Nate Biggs <natebiggs@google.com>
This commit is contained in:
Nate Biggs 2023-03-09 21:26:52 +00:00 committed by Commit Queue
parent 8c7d47cd19
commit 9ba4957a31
10 changed files with 108 additions and 32 deletions

View file

@ -2020,10 +2020,6 @@ class RecordTypeInformation extends TypeInformation with TracedTypeInformation {
}
}
markAsInferred() {
for (final fieldType in fieldTypes) fieldType.inferred = true;
}
@override
addInput(TypeInformation other) {
throw UnsupportedError('addInput');
@ -2071,10 +2067,14 @@ class RecordTypeInformation extends TypeInformation with TracedTypeInformation {
/// A [FieldInRecordTypeInformation] holds the input of one position of a
/// [RecordTypeInformation].
class FieldInRecordTypeInformation extends InferredTypeInformation {
class FieldInRecordTypeInformation extends TypeInformation {
final int indexInShape;
FieldInRecordTypeInformation(super.abstractValueDomain, super.context,
this.indexInShape, super.parentType);
final TypeInformation parentType;
FieldInRecordTypeInformation(AbstractValueDomain abstractValueDomain,
MemberTypeInformation? context, this.indexInShape, this.parentType)
: super(abstractValueDomain.uncomputedType, context) {
parentType.addUser(this);
}
@override
accept(TypeInformationVisitor visitor) {
@ -2083,6 +2083,11 @@ class FieldInRecordTypeInformation extends InferredTypeInformation {
@override
String toString() => 'Field in Record $type';
@override
AbstractValue computeType(InferrerEngine inferrer) {
return parentType.type;
}
}
/// A [PhiElementTypeInformation] is an union of

View file

@ -517,11 +517,26 @@ class TypeSystem {
TypeInformation allocateRecord(ir.TreeNode node, RecordType recordType,
List<TypeInformation> fieldTypes, bool isConst) {
assert(fieldTypes.length == recordType.shape.fieldCount);
final getters = _closedWorld.recordData.gettersForShape(recordType.shape);
FieldInRecordTypeInformation makeField(int i) {
final field = FieldInRecordTypeInformation(
_abstractValueDomain, currentMember, i, fieldTypes[i]);
allocatedTypes.add(field);
final getter = getters[i] as FunctionEntity;
final getterType = memberTypeInformations[getter] ??=
GetterTypeInformation(
_closedWorld.abstractValueDomain,
getter,
_closedWorld.dartTypes.functionType(
_closedWorld.dartTypes.dynamicType(),
const [],
const [],
const [],
const {},
const [],
const []));
getterType.addInput(field);
return field;
}
@ -529,7 +544,6 @@ class TypeSystem {
final originalType = _abstractValueDomain.recordType;
final record = RecordTypeInformation(
currentMember, originalType, recordType.shape, fields);
if (isConst) record.markAsInferred();
// TODO(50081): When tracing is added for records, use `allocatedRecords`.
allocatedTypes.add(record);
return record;

View file

@ -2019,9 +2019,6 @@ class RecordTypeInformation extends TypeInformation with TracedTypeInformation {
fieldType.addUser(this);
}
}
markAsInferred() {
for (final fieldType in fieldTypes) fieldType.inferred = true;
}
@override
addInput(TypeInformation other) {
@ -2069,10 +2066,15 @@ class RecordTypeInformation extends TypeInformation with TracedTypeInformation {
/// A [FieldInRecordTypeInformation] holds the input of one position of a
/// [RecordTypeInformation].
class FieldInRecordTypeInformation extends InferredTypeInformation {
class FieldInRecordTypeInformation extends TypeInformation {
final int indexInShape;
FieldInRecordTypeInformation(super.abstractValueDomain, super.context,
this.indexInShape, super.parentType);
final TypeInformation parentType;
FieldInRecordTypeInformation(AbstractValueDomain abstractValueDomain,
MemberTypeInformation? context, this.indexInShape, this.parentType)
: super(abstractValueDomain.uncomputedType, context) {
parentType.addUser(this);
}
@override
accept(TypeInformationVisitor visitor) {
return visitor.visitFieldInRecordTypeInformation(this);
@ -2080,6 +2082,11 @@ class FieldInRecordTypeInformation extends InferredTypeInformation {
@override
String toString() => 'Field in Record $type';
@override
AbstractValue computeType(InferrerEngine inferrer) {
return parentType.type;
}
}
/// A [PhiElementTypeInformation] is an union of

View file

@ -543,10 +543,26 @@ class TypeSystem {
TypeInformation allocateRecord(ir.TreeNode node, RecordType recordType,
List<TypeInformation> fieldTypes, bool isConst) {
assert(fieldTypes.length == recordType.shape.fieldCount);
final getters = _closedWorld.recordData.gettersForShape(recordType.shape);
FieldInRecordTypeInformation makeField(int i) {
final field = FieldInRecordTypeInformation(
_abstractValueDomain, currentMember, i, fieldTypes[i]);
allocatedTypes.add(field);
final getter = getters[i] as FunctionEntity;
final getterType = memberTypeInformations[getter] ??=
GetterTypeInformation(
_closedWorld.abstractValueDomain,
getter,
_closedWorld.dartTypes.functionType(
_closedWorld.dartTypes.dynamicType(),
const [],
const [],
const [],
const {},
const [],
const []));
getterType.addInput(field);
return field;
}
@ -554,7 +570,6 @@ class TypeSystem {
final originalType = _abstractValueDomain.recordType;
final record = RecordTypeInformation(
currentMember, originalType, recordType.shape, fields);
if (isConst) record.markAsInferred();
// TODO(50081): When tracing is added for records, use `allocatedRecords`.
allocatedTypes.add(record);
return record;

View file

@ -2195,8 +2195,10 @@ class JsKernelToElementMap implements JsToElementMap, IrToElementMap {
return sb.toString();
}
/// [getters] is an out parameter that gathers all the getters created for
/// this shape.
IndexedClass generateRecordShapeClass(
RecordShape shape, InterfaceType supertype) {
RecordShape shape, InterfaceType supertype, List<MemberEntity> getters) {
JLibrary library = supertype.element.library as JLibrary;
String name = _nameForShape(shape);
@ -2225,6 +2227,7 @@ class JsKernelToElementMap implements JsToElementMap, IrToElementMap {
: shape.fieldNames[i - shape.positionalFieldCount];
Name memberName = Name(name, null);
final getter = JRecordGetter(classEntity, memberName);
getters.add(getter);
// The function type of a dynamic getter is a function of no arguments
// that returns `dynamic` (any other top would be ok too).

View file

@ -52,7 +52,8 @@ class JClosedWorld implements World {
// [_allFunctions] is created lazily because it is not used when we switch
// from a frontend to a backend model before inference.
late final FunctionSet _allFunctions = FunctionSet(liveInstanceMembers);
late final FunctionSet _allFunctions =
FunctionSet(liveInstanceMembers.followedBy(recordData.allGetters));
final Map<ClassEntity, Set<ClassEntity>> mixinUses;

View file

@ -384,13 +384,13 @@ class JClosedWorldBuilder {
}
/// Called once per [shape]. The class can be used for a record with the
/// specified shape, or subclassed to provide specialized methods.
ClassEntity buildRecordShapeClass(RecordShape shape) {
/// specified shape, or subclassed to provide specialized methods. [getters]
/// is an out parameter that gathers all the getters created for this shape.
ClassEntity buildRecordShapeClass(
RecordShape shape, List<MemberEntity> getters) {
ClassEntity superclass = _commonElements.recordArityClass(shape.fieldCount);
IndexedClass recordClass = _elementMap.generateRecordShapeClass(
shape,
_dartTypes.interfaceType(superclass, const []),
);
shape, _dartTypes.interfaceType(superclass, const []), getters);
// Tell the hierarchy about the superclass so we can use
// .getSupertypes(class)

View file

@ -55,10 +55,11 @@ class RecordData {
final JsToElementMap _elementMap;
final List<RecordRepresentation> _representations;
final Map<RecordShape, List<MemberEntity>> _gettersByShape;
final Map<ClassEntity, RecordRepresentation> _classToRepresentation = {};
final Map<RecordShape, RecordRepresentation> _shapeToRepresentation = {};
RecordData._(this._elementMap, this._representations) {
RecordData._(this._elementMap, this._representations, this._gettersByShape) {
// Unpack representations into lookup maps.
for (final info in _representations) {
_classToRepresentation[info.cls] = info;
@ -66,13 +67,23 @@ class RecordData {
}
}
Iterable<MemberEntity> get allGetters =>
_gettersByShape.values.expand((e) => e);
List<MemberEntity> gettersForShape(RecordShape shape) =>
_gettersByShape[shape]!;
factory RecordData.readFromDataSource(
JsToElementMap elementMap, DataSourceReader source) {
source.begin(tag);
List<RecordRepresentation> representations =
source.readList(() => RecordRepresentation.readFromDataSource(source));
final shapes =
source.readList(() => RecordShape.readFromDataSource(source));
final getters = source.readList(source.readMembers);
source.end(tag);
return RecordData._(elementMap, representations);
return RecordData._(
elementMap, representations, Map.fromIterables(shapes, getters));
}
/// Serializes this [RecordData] to [sink].
@ -80,6 +91,9 @@ class RecordData {
sink.begin(tag);
sink.writeList<RecordRepresentation>(
_representations, (info) => info.writeToDataSink(sink));
sink.writeList(
_gettersByShape.keys, (RecordShape v) => v.writeToDataSink(sink));
sink.writeList(_gettersByShape.values, sink.writeMembers);
sink.end(tag);
}
@ -222,6 +236,7 @@ class RecordDataBuilder {
final DiagnosticReporter _reporter;
final JsToElementMap _elementMap;
final AnnotationsData _annotationsData;
final Map<RecordShape, List<MemberEntity>> _gettersByShape = {};
RecordDataBuilder(this._reporter, this._elementMap, this._annotationsData);
@ -238,9 +253,11 @@ class RecordDataBuilder {
List<RecordRepresentation> representations = [];
for (int i = 0; i < shapes.length; i++) {
final shape = shapes[i];
final getters = <MemberEntity>[];
final cls = shape.fieldCount == 0
? _elementMap.commonElements.emptyRecordClass
: closedWorldBuilder.buildRecordShapeClass(shape);
: closedWorldBuilder.buildRecordShapeClass(shape, getters);
_gettersByShape[shape] = getters;
int shapeTag = i;
bool usesList = _computeUsesGeneralClass(cls);
final info =
@ -248,10 +265,7 @@ class RecordDataBuilder {
representations.add(info);
}
return RecordData._(
_elementMap,
representations,
);
return RecordData._(_elementMap, representations, _gettersByShape);
}
bool _computeUsesGeneralClass(ClassEntity? cls) {

View file

@ -369,7 +369,8 @@ class MemberHierarchyBuilder {
void init(void Function(MemberEntity parent, MemberEntity override) join) {
final liveMembers = closedWorld.liveInstanceMembers
.followedBy(closedWorld.liveAbstractInstanceMembers);
.followedBy(closedWorld.liveAbstractInstanceMembers)
.followedBy(closedWorld.recordData.allGetters);
for (final member in liveMembers) {
_processMember(member, join);

View file

@ -4,14 +4,18 @@
/*member: main:[null]*/
main() {
getRecord1();
useRecord1();
useRecord2();
useRecord3();
}
// TODO(50701): This should be a record type.
/*member: getRecord1:[null|subclass=Object]*/
(num, num) getRecord1() => (1, 1);
/*member: getRecord2:[null|subclass=Object]*/
(bool, bool) getRecord2() => (true, false);
/*member: getRecord3:[null|subclass=Object]*/
dynamic getRecord3() => ("a", "b");
// TODO(50701): This should be a constant or JSUint31.
/*member: useRecord1:[subclass=JSNumber]*/
@ -19,3 +23,15 @@ useRecord1() {
final r = getRecord1();
return r.$1;
}
/*member: useRecord2:[subtype=bool]*/
useRecord2() {
final r = getRecord2();
return r.$2;
}
/*member: useRecord3:Union([exact=JSBool], [exact=JSString], [exact=JSUInt31])*/
useRecord3() {
final r = getRecord3();
return r.$2;
}