mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 10:28:02 +00:00
[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:
parent
8c7d47cd19
commit
9ba4957a31
10 changed files with 108 additions and 32 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue