[modular_aot] Records
TEST=ci Issue: https://github.com/dart-lang/sdk/issues/61635 Change-Id: Ie6ba4148ca9193b5b28de3d90efa7bb55427ef53 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/498660 Commit-Queue: Alexander Markov <alexmarkov@google.com> Reviewed-by: Slava Egorov <vegorov@google.com>
This commit is contained in:
committed by
dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent
5ed538a532
commit
ccbcd76265
@@ -2088,6 +2088,40 @@ class AstToIr extends ast.RecursiveVisitor {
|
||||
throw 'Unexpected YieldStatement in $function with ${function.asyncMarker}';
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void visitRecordIndexGet(ast.RecordIndexGet node) {
|
||||
final shape = RecordType(node.receiverType).shape;
|
||||
_translateNode(node.receiver);
|
||||
// TODO: canonicalize record fields
|
||||
builder.addLoadInstanceField(CField(RecordField(shape, node.index)));
|
||||
}
|
||||
|
||||
@override
|
||||
void visitRecordNameGet(ast.RecordNameGet node) {
|
||||
final shape = RecordType(node.receiverType).shape;
|
||||
final namedIndex = shape.named.indexOf(node.name);
|
||||
assert(namedIndex >= 0);
|
||||
_translateNode(node.receiver);
|
||||
// TODO: canonicalize record fields
|
||||
builder.addLoadInstanceField(
|
||||
CField(RecordField(shape, shape.positional + namedIndex)),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitRecordLiteral(ast.RecordLiteral node) {
|
||||
assert(!node.isConst);
|
||||
final type = RecordType(node.recordType);
|
||||
for (final expr in node.positional) {
|
||||
_translateNode(expr);
|
||||
}
|
||||
for (final expr in node.named) {
|
||||
_translateNode(expr.value);
|
||||
}
|
||||
assert(node.positional.length + node.named.length == type.numFields);
|
||||
builder.addAllocateRecordLiteral(type);
|
||||
}
|
||||
}
|
||||
|
||||
/// Mapping between AST nodes and CFG IR [LocalVariable].
|
||||
|
||||
@@ -30,6 +30,7 @@ class AstToIrTypes
|
||||
.doubleType => DoubleType(node),
|
||||
.boolType => BoolType(node),
|
||||
.stringType => StringType(node),
|
||||
.recordType => RecordType(node as ast.RecordType),
|
||||
.objectType => ObjectType(node),
|
||||
.nullType => const NullType(),
|
||||
.neverType => const NeverType(),
|
||||
@@ -107,7 +108,10 @@ class AstToIrTypes
|
||||
node.extensionTypeErasure.accept(this);
|
||||
|
||||
@override
|
||||
TypeKind visitRecordType(ast.RecordType node) => TypeKind.otherDartType;
|
||||
TypeKind visitRecordType(ast.RecordType node) =>
|
||||
node.nullability == .nonNullable
|
||||
? TypeKind.recordType
|
||||
: TypeKind.otherDartType;
|
||||
|
||||
@override
|
||||
TypeKind visitFutureOrType(ast.FutureOrType node) {
|
||||
|
||||
@@ -118,3 +118,16 @@ class ClosureLayout {
|
||||
(hasFunctionTypeArgs ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Field of the record object.
|
||||
final class RecordField extends SyntheticField {
|
||||
final RecordShape shape;
|
||||
final int index;
|
||||
|
||||
RecordField(this.shape, this.index)
|
||||
: super(
|
||||
'#record-field[$index${index >= shape.positional ? ':${shape.named[index - shape.positional]}' : ''}]',
|
||||
type: const ast.DynamicType(),
|
||||
isFinal: true,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -599,6 +599,22 @@ class FlowGraphBuilder {
|
||||
return instr;
|
||||
}
|
||||
|
||||
/// Append [AllocateRecordLiteral] to the graph.
|
||||
/// Takes elements from the stack as inputs.
|
||||
AllocateRecordLiteral addAllocateRecordLiteral(RecordType type) {
|
||||
final inputCount = type.numFields;
|
||||
final instr = AllocateRecordLiteral(
|
||||
graph,
|
||||
currentSourcePosition,
|
||||
type,
|
||||
inputCount: inputCount,
|
||||
);
|
||||
popInputs(instr, 0, inputCount);
|
||||
push(instr);
|
||||
appendInstruction(instr);
|
||||
return instr;
|
||||
}
|
||||
|
||||
/// Append [StringInterpolation] to the graph.
|
||||
StringInterpolation addStringInterpolation(int inputCount) {
|
||||
final instr = StringInterpolation(
|
||||
|
||||
@@ -412,6 +412,9 @@ final class FlowGraphChecker extends Pass implements InstructionVisitor<void> {
|
||||
verifyTypeArgumentsInput(instr.typeArguments, instr);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitAllocateRecordLiteral(AllocateRecordLiteral instr) {}
|
||||
|
||||
@override
|
||||
void visitStringInterpolation(StringInterpolation instr) {}
|
||||
|
||||
@@ -487,6 +490,9 @@ final class FlowGraphChecker extends Pass implements InstructionVisitor<void> {
|
||||
@override
|
||||
void visitSetListElement(SetListElement instr) {}
|
||||
|
||||
@override
|
||||
void visitAllocateRecord(AllocateRecord instr) {}
|
||||
|
||||
@override
|
||||
void visitBoxInt(BoxInt instr) {
|
||||
assert(instr.operand.type is IntType);
|
||||
|
||||
@@ -1392,6 +1392,24 @@ final class AllocateMapLiteral extends Definition with CanThrow, Pure {
|
||||
R accept<R>(InstructionVisitor<R> v) => v.visitAllocateMapLiteral(this);
|
||||
}
|
||||
|
||||
/// Allocate a new Record literal with given elements.
|
||||
final class AllocateRecordLiteral extends Definition with CanThrow, Pure {
|
||||
@override
|
||||
final RecordType type;
|
||||
|
||||
AllocateRecordLiteral(
|
||||
super.graph,
|
||||
super.sourcePosition,
|
||||
this.type, {
|
||||
required super.inputCount,
|
||||
}) : assert(inputCount == type.numFields);
|
||||
|
||||
Definition elementAt(int index) => inputDefAt(index);
|
||||
|
||||
@override
|
||||
R accept<R>(InstructionVisitor<R> v) => v.visitAllocateRecordLiteral(this);
|
||||
}
|
||||
|
||||
/// Interpolate given objects into a String.
|
||||
final class StringInterpolation extends Definition
|
||||
with CanThrow, HasSideEffects {
|
||||
@@ -1755,6 +1773,19 @@ final class SetListElement extends Instruction
|
||||
R accept<R>(InstructionVisitor<R> v) => v.visitSetListElement(this);
|
||||
}
|
||||
|
||||
/// Allocate a Record instance of given type.
|
||||
final class AllocateRecord extends Definition
|
||||
with CanThrow, Pure, BackendInstruction {
|
||||
@override
|
||||
final RecordType type;
|
||||
|
||||
AllocateRecord(super.graph, super.sourcePosition, this.type)
|
||||
: super(inputCount: 0);
|
||||
|
||||
@override
|
||||
R accept<R>(InstructionVisitor<R> v) => v.visitAllocateRecord(this);
|
||||
}
|
||||
|
||||
/// Base class for boxing instructions.
|
||||
abstract base class Box extends Definition
|
||||
with CanThrow, Pure, BackendInstruction {
|
||||
|
||||
@@ -189,6 +189,7 @@ final class IrToText extends VoidInstructionVisitor {
|
||||
UnaryDoubleOp() => 'UnaryDoubleOp ${instr.op.token}',
|
||||
UnaryBoolOp() => 'UnaryBoolOp ${instr.op.token}',
|
||||
ParallelMove() => 'ParallelMove ${instr.stage.name}',
|
||||
AllocateRecord() => 'AllocateRecord ${instr.type}',
|
||||
_ => instr.runtimeType.toString(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ enum TypeKind {
|
||||
doubleType,
|
||||
boolType,
|
||||
stringType,
|
||||
recordType,
|
||||
objectType,
|
||||
nullType,
|
||||
neverType,
|
||||
@@ -193,6 +194,60 @@ final class StringType extends CType {
|
||||
String toString() => 'String';
|
||||
}
|
||||
|
||||
/// Shape of the Dart record.
|
||||
/// Records with the same shape are compatible wrt field access.
|
||||
final class RecordShape {
|
||||
// Number of positional fields.
|
||||
final int positional;
|
||||
// Named fields (sorted lexicographically).
|
||||
final List<String> named;
|
||||
|
||||
const RecordShape(this.positional, this.named);
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
'Record[$positional${named.isNotEmpty ? ', named: $named' : ''}]';
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
other is RecordShape &&
|
||||
this.positional == other.positional &&
|
||||
listEquals(this.named, other.named);
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
finalizeHash(combineHash(positional.hashCode, listHashCode(named)));
|
||||
}
|
||||
|
||||
/// Non-nullable Dart record type.
|
||||
final class RecordType extends CType {
|
||||
@override
|
||||
TypeKind get kind => TypeKind.recordType;
|
||||
|
||||
@override
|
||||
final ast.RecordType dartType;
|
||||
|
||||
late final shape = RecordShape(dartType.positional.length, [
|
||||
for (final namedType in dartType.named) namedType.name,
|
||||
]);
|
||||
|
||||
RecordType(this.dartType) : assert(dartType.nullability == .nonNullable);
|
||||
|
||||
int get numFields => dartType.positional.length + dartType.named.length;
|
||||
|
||||
@override
|
||||
bool get isNullable => false;
|
||||
|
||||
@override
|
||||
CType get toNonNullableType => this;
|
||||
|
||||
@override
|
||||
bool get canBeFuture => false;
|
||||
|
||||
@override
|
||||
String toString() => dartType.getDisplayString();
|
||||
}
|
||||
|
||||
/// Dart `Object` type.
|
||||
final class ObjectType extends CType {
|
||||
final ast.DartType? _dartType;
|
||||
|
||||
@@ -43,6 +43,7 @@ abstract interface class InstructionVisitor<R> {
|
||||
R visitAllocateContext(AllocateContext instr);
|
||||
R visitAllocateListLiteral(AllocateListLiteral instr);
|
||||
R visitAllocateMapLiteral(AllocateMapLiteral instr);
|
||||
R visitAllocateRecordLiteral(AllocateRecordLiteral instr);
|
||||
R visitStringInterpolation(StringInterpolation instr);
|
||||
R visitEnterSuspendableFunction(EnterSuspendableFunction instr);
|
||||
R visitSuspend(Suspend instr);
|
||||
@@ -55,6 +56,7 @@ abstract interface class InstructionVisitor<R> {
|
||||
R visitCompareAndBranch(CompareAndBranch instr);
|
||||
R visitAllocateList(AllocateList instr);
|
||||
R visitSetListElement(SetListElement instr);
|
||||
R visitAllocateRecord(AllocateRecord instr);
|
||||
R visitBoxInt(BoxInt instr);
|
||||
R visitBoxDouble(BoxDouble instr);
|
||||
R visitUnboxInt(UnboxInt instr);
|
||||
@@ -117,6 +119,8 @@ abstract mixin class DefaultInstructionVisitor<R>
|
||||
defaultInstruction(instr);
|
||||
R visitAllocateMapLiteral(AllocateMapLiteral instr) =>
|
||||
defaultInstruction(instr);
|
||||
R visitAllocateRecordLiteral(AllocateRecordLiteral instr) =>
|
||||
defaultInstruction(instr);
|
||||
R visitStringInterpolation(StringInterpolation instr) =>
|
||||
defaultInstruction(instr);
|
||||
R visitEnterSuspendableFunction(EnterSuspendableFunction instr) =>
|
||||
@@ -133,6 +137,8 @@ abstract mixin class DefaultInstructionVisitor<R>
|
||||
R visitAllocateList(AllocateList instr) => defaultBackendInstruction(instr);
|
||||
R visitSetListElement(SetListElement instr) =>
|
||||
defaultBackendInstruction(instr);
|
||||
R visitAllocateRecord(AllocateRecord instr) =>
|
||||
defaultBackendInstruction(instr);
|
||||
R visitBoxInt(BoxInt instr) => defaultBackendInstruction(instr);
|
||||
R visitBoxDouble(BoxDouble instr) => defaultBackendInstruction(instr);
|
||||
R visitUnboxInt(UnboxInt instr) => defaultBackendInstruction(instr);
|
||||
|
||||
@@ -428,6 +428,11 @@ final class ConstantPropagation extends Pass
|
||||
_setNonConstant(instr);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitAllocateRecordLiteral(AllocateRecordLiteral instr) {
|
||||
_setNonConstant(instr);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitStringInterpolation(StringInterpolation instr) {
|
||||
for (int i = 0, n = instr.inputCount; i < n; ++i) {
|
||||
@@ -574,6 +579,11 @@ final class ConstantPropagation extends Pass
|
||||
@override
|
||||
void visitSetListElement(SetListElement instr) {}
|
||||
|
||||
@override
|
||||
void visitAllocateRecord(AllocateRecord instr) {
|
||||
_setNonConstant(instr);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitBoxInt(BoxInt instr) {
|
||||
_setNonConstant(instr);
|
||||
|
||||
@@ -249,6 +249,9 @@ final class Simplification extends Pass
|
||||
@override
|
||||
Instruction visitAllocateMapLiteral(AllocateMapLiteral instr) => instr;
|
||||
|
||||
@override
|
||||
Instruction visitAllocateRecordLiteral(AllocateRecordLiteral instr) => instr;
|
||||
|
||||
@override
|
||||
Instruction visitStringInterpolation(StringInterpolation instr) {
|
||||
final buf = _StringInterpolationBuffer(constantFolding);
|
||||
@@ -292,6 +295,9 @@ final class Simplification extends Pass
|
||||
@override
|
||||
Instruction visitSetListElement(SetListElement instr) => instr;
|
||||
|
||||
@override
|
||||
Instruction visitAllocateRecord(AllocateRecord instr) => instr;
|
||||
|
||||
@override
|
||||
Instruction visitBoxInt(BoxInt instr) => instr;
|
||||
|
||||
|
||||
@@ -155,6 +155,55 @@ void main() {
|
||||
expect(StringType().isSubtypeOf(StaticType(comparableType)), isTrue);
|
||||
});
|
||||
|
||||
test('record', () {
|
||||
final dartType1 = ast.RecordType(
|
||||
[coreTypes.intNonNullableRawType, coreTypes.stringNullableRawType],
|
||||
[],
|
||||
.nonNullable,
|
||||
);
|
||||
final recordType1 = RecordType(dartType1);
|
||||
expect(recordType1.kind, equals(TypeKind.recordType));
|
||||
expect(recordType1.dartType, equals(dartType1));
|
||||
expect(recordType1.hashCode, equals(RecordType(dartType1).hashCode));
|
||||
expect(recordType1.isNullable, isFalse);
|
||||
expect(recordType1.toNonNullableType, equals(recordType1));
|
||||
|
||||
final dartType2 = ast.RecordType(
|
||||
[coreTypes.intNonNullableRawType],
|
||||
[ast.NamedType('foo', coreTypes.boolNonNullableRawType)],
|
||||
.nonNullable,
|
||||
);
|
||||
final recordType2 = RecordType(dartType2);
|
||||
expect(recordType2.kind, equals(TypeKind.recordType));
|
||||
expect(recordType2.dartType, equals(dartType2));
|
||||
expect(recordType2.hashCode, equals(RecordType(dartType2).hashCode));
|
||||
expect(recordType2.isNullable, isFalse);
|
||||
expect(recordType2.toNonNullableType, equals(recordType2));
|
||||
|
||||
expect(recordType1.isSubtypeOf(IntType()), isFalse);
|
||||
expect(recordType1.isSubtypeOf(DoubleType()), isFalse);
|
||||
expect(recordType1.isSubtypeOf(BoolType()), isFalse);
|
||||
expect(recordType1.isSubtypeOf(StringType()), isFalse);
|
||||
expect(recordType1.isSubtypeOf(ObjectType()), isTrue);
|
||||
expect(recordType1.isSubtypeOf(TopType()), isTrue);
|
||||
expect(recordType1.isSubtypeOf(NullType()), isFalse);
|
||||
expect(recordType1.isSubtypeOf(NeverType()), isFalse);
|
||||
expect(recordType1.isSubtypeOf(recordType2), isFalse);
|
||||
|
||||
final recordType3 = RecordType(
|
||||
ast.RecordType(
|
||||
[coreTypes.intNullableRawType, coreTypes.objectNullableRawType],
|
||||
[],
|
||||
.nonNullable,
|
||||
),
|
||||
);
|
||||
expect(recordType1.isSubtypeOf(recordType3), isTrue);
|
||||
expect(
|
||||
recordType1.isSubtypeOf(StaticType(coreTypes.recordNonNullableRawType)),
|
||||
isTrue,
|
||||
);
|
||||
});
|
||||
|
||||
test('object', () {
|
||||
final objectDartType = coreTypes.objectNonNullableRawType;
|
||||
expect(ObjectType(), equals(ObjectType(objectDartType)));
|
||||
|
||||
@@ -33,4 +33,16 @@ void logical(bool x, bool Function() y, bool z) {
|
||||
print(!(x && (y() || z)));
|
||||
}
|
||||
|
||||
void recordLiterals<T>(int a, String b, T c) {
|
||||
print((a,));
|
||||
print((a, bbb: b));
|
||||
print((a, b, c));
|
||||
}
|
||||
|
||||
void recordFields<T>((int, String) r1, (int, {T foo, T bar}) r2) {
|
||||
print(r1.$2);
|
||||
print(r2.$1);
|
||||
print(r2.bar);
|
||||
}
|
||||
|
||||
void main() {}
|
||||
|
||||
@@ -113,6 +113,37 @@ B40 = JoinBlock(B37, B45, B44) idom:B28
|
||||
DirectCall print(v59)
|
||||
Return(v55)
|
||||
|
||||
--- recordLiterals
|
||||
B0 = EntryBlock()
|
||||
v19 = Constant(null)
|
||||
v1 = Parameter(#functionTypeParameters)
|
||||
v2 = Parameter(a)
|
||||
v3 = Parameter(b)
|
||||
v4 = Parameter(c)
|
||||
TypeParameters(v1)
|
||||
v8 = AllocateRecordLiteral(v2)
|
||||
DirectCall print(v8)
|
||||
v12 = AllocateRecordLiteral(v2, v3)
|
||||
DirectCall print(v12)
|
||||
v17 = AllocateRecordLiteral(v2, v3, v4)
|
||||
DirectCall print(v17)
|
||||
Return(v19)
|
||||
|
||||
--- recordFields
|
||||
B0 = EntryBlock()
|
||||
v15 = Constant(null)
|
||||
v1 = Parameter(#functionTypeParameters)
|
||||
v2 = Parameter(r1)
|
||||
v3 = Parameter(r2)
|
||||
TypeParameters(v1)
|
||||
v7 = LoadInstanceField(#record-field[1], v2)
|
||||
DirectCall print(v7)
|
||||
v10 = LoadInstanceField(#record-field[0], v3)
|
||||
DirectCall print(v10)
|
||||
v13 = LoadInstanceField(#record-field[1:bar], v3)
|
||||
DirectCall print(v13)
|
||||
Return(v15)
|
||||
|
||||
--- main
|
||||
B0 = EntryBlock()
|
||||
v1 = Constant(null)
|
||||
|
||||
@@ -1461,6 +1461,50 @@ final class Arm64CodeGenerator extends CodeGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void visitAllocateRecord(AllocateRecord instr) {
|
||||
final instanceSize = roundUp(
|
||||
vmOffsets.Record_elementsStartOffset +
|
||||
instr.type.numFields * objectLayout.compressedWordSize,
|
||||
objectAlignment(wordSize),
|
||||
);
|
||||
final resultReg = AllocationStub.resultReg;
|
||||
assert(outputReg(instr) == resultReg);
|
||||
|
||||
final done = Label();
|
||||
Label slowPath = addSlowPath(() {
|
||||
_asm.unimplemented(
|
||||
'Unimplemented: code generation for AllocateRecord slow path',
|
||||
);
|
||||
_asm.b(done);
|
||||
});
|
||||
|
||||
_asm.loadImmediate(
|
||||
AllocationStub.tagsReg,
|
||||
vmOffsets.computeNewObjectTags(
|
||||
ClassId.RecordCid,
|
||||
instanceSize,
|
||||
log2wordSize,
|
||||
),
|
||||
);
|
||||
_asm.inlineAllocation(
|
||||
resultReg,
|
||||
AllocationStub.tagsReg,
|
||||
AllocationStub.scratch1Reg,
|
||||
AllocationStub.scratch2Reg,
|
||||
instanceSize,
|
||||
slowPath,
|
||||
initializeFields: true,
|
||||
);
|
||||
final fieldReg = AllocationStub.scratch1Reg;
|
||||
_asm.loadFromPool(fieldReg, instr.type.shape);
|
||||
_asm.str(
|
||||
fieldReg,
|
||||
_asm.fieldAddress(resultReg, vmOffsets.Record_shape_offset),
|
||||
);
|
||||
_asm.bind(done);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitBoxInt(BoxInt instr) {
|
||||
var operandReg = inputReg(instr, 0);
|
||||
|
||||
@@ -360,6 +360,14 @@ final class Arm64Constraints extends Constraints {
|
||||
const [anyCpuRegister, anyCpuRegister],
|
||||
);
|
||||
|
||||
@override
|
||||
InstructionConstraints? visitAllocateRecord(AllocateRecord instr) =>
|
||||
const InstructionConstraints(AllocationStub.resultReg, [], [
|
||||
AllocationStub.tagsReg,
|
||||
AllocationStub.scratch1Reg,
|
||||
AllocationStub.scratch2Reg,
|
||||
]);
|
||||
|
||||
@override
|
||||
InstructionConstraints? visitBoxInt(BoxInt instr) =>
|
||||
const InstructionConstraints(
|
||||
|
||||
@@ -297,6 +297,10 @@ abstract base class CodeGenerator extends Pass
|
||||
void visitAllocateMapLiteral(AllocateMapLiteral instr) =>
|
||||
throw 'Unexpected AllocateMapLiteral (should be lowered)';
|
||||
|
||||
@override
|
||||
void visitAllocateRecordLiteral(AllocateRecordLiteral instr) =>
|
||||
throw 'Unexpected AllocateRecordLiteral (should be lowered)';
|
||||
|
||||
@override
|
||||
void visitStringInterpolation(StringInterpolation instr) =>
|
||||
throw 'Unexpected StringInterpolation (should be lowered)';
|
||||
|
||||
@@ -152,6 +152,11 @@ abstract base class Constraints
|
||||
InstructionConstraints? visitAllocateMapLiteral(AllocateMapLiteral instr) =>
|
||||
throw 'Unexpected AllocateMapLiteral (should be lowered)';
|
||||
|
||||
@override
|
||||
InstructionConstraints? visitAllocateRecordLiteral(
|
||||
AllocateRecordLiteral instr,
|
||||
) => throw 'Unexpected AllocateRecordLiteral (should be lowered)';
|
||||
|
||||
@override
|
||||
InstructionConstraints? visitStringInterpolation(StringInterpolation instr) =>
|
||||
throw 'Unexpected StringInterpolation (should be lowered)';
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'package:cfg/ir/constant_value.dart';
|
||||
import 'package:cfg/ir/field.dart';
|
||||
import 'package:cfg/ir/functions.dart';
|
||||
import 'package:cfg/ir/global_context.dart';
|
||||
import 'package:cfg/ir/instructions.dart';
|
||||
@@ -246,6 +247,26 @@ final class Lowering extends Pass with DefaultInstructionVisitor<void> {
|
||||
instr.removeFromGraph();
|
||||
}
|
||||
|
||||
@override
|
||||
void visitAllocateRecordLiteral(AllocateRecordLiteral instr) {
|
||||
final obj = AllocateRecord(graph, instr.sourcePosition, instr.type);
|
||||
obj.insertBefore(instr);
|
||||
for (int i = 0, n = instr.inputCount; i < n; ++i) {
|
||||
final element = instr.elementAt(i);
|
||||
// TODO: canonicalize record fields
|
||||
final setElem = StoreInstanceField(
|
||||
graph,
|
||||
instr.sourcePosition,
|
||||
CField(RecordField(instr.type.shape, i)),
|
||||
obj,
|
||||
element,
|
||||
);
|
||||
setElem.insertBefore(instr);
|
||||
}
|
||||
instr.replaceUsesWith(obj);
|
||||
instr.removeFromGraph();
|
||||
}
|
||||
|
||||
@override
|
||||
void visitStringInterpolation(StringInterpolation instr) {
|
||||
assert(instr.inputCount > 0);
|
||||
|
||||
@@ -53,6 +53,7 @@ class ObjectLayout {
|
||||
return switch (field.asSynthetic) {
|
||||
ContextField(:var index) => vmOffsets.Context_elementOffset(index),
|
||||
ClosureField(:var index) => vmOffsets.Closure_elementOffset(index),
|
||||
RecordField(:var index) => vmOffsets.Record_elementOffset(index),
|
||||
};
|
||||
}
|
||||
_ensureComputed(field.enclosingClass);
|
||||
|
||||
@@ -9,6 +9,7 @@ import 'package:cfg/ir/constant_value.dart';
|
||||
import 'package:cfg/ir/field.dart';
|
||||
import 'package:cfg/ir/functions.dart';
|
||||
import 'package:cfg/ir/global_context.dart';
|
||||
import 'package:cfg/ir/types.dart';
|
||||
import 'package:cfg/utils/misc.dart';
|
||||
import 'package:kernel/ast.dart' as ast;
|
||||
import 'package:kernel/src/printer.dart' as ast_printer show AstPrinter;
|
||||
@@ -63,6 +64,7 @@ enum PredefinedClusters {
|
||||
closureFunctionRefs,
|
||||
closureRefs,
|
||||
argumentsDescriptorRefs,
|
||||
recordShapeRefs,
|
||||
ints,
|
||||
doubles,
|
||||
lists,
|
||||
@@ -320,6 +322,7 @@ class SnapshotSerializer {
|
||||
ArgumentsShape() => getPredefinedCluster(
|
||||
PredefinedClusters.argumentsDescriptorRefs,
|
||||
),
|
||||
RecordShape() => getPredefinedCluster(PredefinedClusters.recordShapeRefs),
|
||||
// Constants.
|
||||
String() => getPredefinedCluster(
|
||||
OneByteStringSerializationCluster.isOneByteString(obj)
|
||||
@@ -385,6 +388,7 @@ class SnapshotSerializer {
|
||||
.closureFunctionRefs => ClosureFunctionRefSerializationCluster(),
|
||||
.closureRefs => ClosureRefSerializationCluster(),
|
||||
.argumentsDescriptorRefs => ArgumentsDescriptorRefSerializationCluster(),
|
||||
.recordShapeRefs => RecordShapeRefSerializationCluster(),
|
||||
.oneByteStrings => OneByteStringSerializationCluster(),
|
||||
.twoByteStrings => TwoByteStringSerializationCluster(),
|
||||
.privateNames => PrivateNameSerializationCluster(),
|
||||
@@ -393,14 +397,14 @@ class SnapshotSerializer {
|
||||
.lists => ListSerializationCluster(),
|
||||
.maps => MapSerializationCluster(),
|
||||
.sets => SetSerializationCluster(),
|
||||
.records => throw 'Unimplemented cluster $clusterId',
|
||||
.records => RecordSerializationCluster(),
|
||||
.instantiatedClosures => throw 'Unimplemented cluster $clusterId',
|
||||
.typeParameters =>
|
||||
throw 'Unimplemented cluster $clusterId', // TypeParametersSerializationCluster(),
|
||||
.typeArguments => TypeArgumentsSerializationCluster(),
|
||||
.interfaceTypes => InterfaceTypeSerializationCluster(),
|
||||
.functionTypes => FunctionTypeSerializationCluster(),
|
||||
.recordTypes => throw 'Unimplemented cluster $clusterId',
|
||||
.recordTypes => RecordTypeSerializationCluster(),
|
||||
.typeParameterTypes => TypeParameterTypeSerializationCluster(),
|
||||
.codes => CodeSerializationCluster(),
|
||||
.icDatas => ICDataSerializationCluster(),
|
||||
@@ -686,6 +690,33 @@ final class ArgumentsDescriptorRefSerializationCluster
|
||||
}
|
||||
}
|
||||
|
||||
final class RecordShapeRefSerializationCluster extends SerializationCluster {
|
||||
final List<RecordShape> _objects = [];
|
||||
|
||||
@override
|
||||
void trace(SnapshotSerializer serializer, Object object) {
|
||||
final shape = object as RecordShape;
|
||||
_objects.add(shape);
|
||||
for (final name in shape.named) {
|
||||
serializer.push(name);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void writePreLoad(SnapshotSerializer serializer) {
|
||||
serializer.writeUint(PredefinedClusters.recordShapeRefs.index);
|
||||
serializer.writeUint(_objects.length);
|
||||
for (final shape in _objects) {
|
||||
serializer.assignRef(shape);
|
||||
serializer.writeUint(shape.positional);
|
||||
serializer.writeUint(shape.named.length);
|
||||
for (final name in shape.named) {
|
||||
serializer.writeRefId(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class OneByteStringSerializationCluster extends SerializationCluster {
|
||||
final List<String> _objects = [];
|
||||
|
||||
@@ -971,6 +1002,56 @@ final class SetSerializationCluster extends SerializationCluster {
|
||||
}
|
||||
}
|
||||
|
||||
/// Serialization cluster for constant records.
|
||||
final class RecordSerializationCluster extends SerializationCluster {
|
||||
final List<ast.RecordConstant> _objects = [];
|
||||
final List<RecordShape> _shapes = [];
|
||||
|
||||
@override
|
||||
void trace(SnapshotSerializer serializer, Object object) {
|
||||
final record = object as ast.RecordConstant;
|
||||
final shape = RecordType(record.recordType).shape;
|
||||
_objects.add(record);
|
||||
_shapes.add(shape);
|
||||
serializer.push(shape);
|
||||
for (final e in record.positional) {
|
||||
serializer.push(e);
|
||||
}
|
||||
for (final e in record.named.values) {
|
||||
serializer.push(e);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void writePreLoad(SnapshotSerializer serializer) {
|
||||
serializer.writeUint(PredefinedClusters.records.index);
|
||||
}
|
||||
|
||||
@override
|
||||
void writeAlloc(SnapshotSerializer serializer) {
|
||||
serializer.writeUint(_objects.length);
|
||||
for (final record in _objects) {
|
||||
serializer.assignRef(record);
|
||||
serializer.writeUint(record.positional.length + record.named.length);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void writeFill(SnapshotSerializer serializer) {
|
||||
for (var i = 0, n = _objects.length; i < n; ++i) {
|
||||
final record = _objects[i];
|
||||
final shape = _shapes[i];
|
||||
serializer.writeRefId(shape);
|
||||
for (final e in record.positional) {
|
||||
serializer.writeRefId(e);
|
||||
}
|
||||
for (final name in shape.named) {
|
||||
serializer.writeRefId(record.named[name]!);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class InstanceSerializationCluster extends SerializationCluster {
|
||||
final ast.Class _cls;
|
||||
final List<ast.InstanceConstant> _objects = [];
|
||||
@@ -1221,6 +1302,54 @@ final class FunctionTypeSerializationCluster extends SerializationCluster {
|
||||
}
|
||||
}
|
||||
|
||||
final class RecordTypeSerializationCluster extends SerializationCluster {
|
||||
final List<ast.RecordType> _objects = [];
|
||||
final List<RecordShape> _shapes = [];
|
||||
final List<ast.ListConstant> _fieldTypes = [];
|
||||
|
||||
@override
|
||||
void trace(SnapshotSerializer serializer, Object object) {
|
||||
final type = object as ast.RecordType;
|
||||
final shape = RecordType(type.withDeclaredNullability(.nonNullable)).shape;
|
||||
final fieldTypes = getListConstant([
|
||||
...type.positional,
|
||||
for (final nt in type.named) nt.type,
|
||||
]);
|
||||
_objects.add(type);
|
||||
_shapes.add(shape);
|
||||
_fieldTypes.add(fieldTypes);
|
||||
serializer.push(shape);
|
||||
serializer.push(fieldTypes);
|
||||
}
|
||||
|
||||
@override
|
||||
void writePreLoad(SnapshotSerializer serializer) {
|
||||
serializer.writeUint(PredefinedClusters.recordTypes.index);
|
||||
}
|
||||
|
||||
@override
|
||||
void writeAlloc(SnapshotSerializer serializer) {
|
||||
serializer.writeUint(_objects.length);
|
||||
for (final type in _objects) {
|
||||
serializer.assignRef(type);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void writeFill(SnapshotSerializer serializer) {
|
||||
for (var i = 0, n = _objects.length; i < n; ++i) {
|
||||
final type = _objects[i];
|
||||
final shape = _shapes[i];
|
||||
final fieldTypes = _fieldTypes[i];
|
||||
serializer.writeUint(
|
||||
type.declaredNullability == ast.Nullability.nullable ? 1 : 0,
|
||||
);
|
||||
serializer.writeRefId(shape);
|
||||
serializer.writeRefId(fieldTypes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class TypeParameterTypeSerializationCluster extends SerializationCluster {
|
||||
final List<ast.DartType> _objects = [];
|
||||
|
||||
|
||||
@@ -65,6 +65,13 @@ void mapLiterals<S, T>(S key, T Function() value, S key2, T value2) {
|
||||
print({key: value(), key2: value2});
|
||||
}
|
||||
|
||||
void recordLiterals<T>(int a, String b, T c) {
|
||||
print((a,));
|
||||
print((a, b, c));
|
||||
print((foo: b, bar: c));
|
||||
print((a, foo: b, bar: c));
|
||||
}
|
||||
|
||||
void stringInterpolation(int x, String s, Object o) {
|
||||
print('$o');
|
||||
print('Hey, x=$x, s=$s, o=$o');
|
||||
|
||||
@@ -325,6 +325,43 @@ B0 = EntryBlock()
|
||||
ParallelMove input(NullConstant(null) -> R0)
|
||||
Return(v11) # RA: (R0)
|
||||
|
||||
--- recordLiterals
|
||||
B0 = EntryBlock()
|
||||
v30 = Constant(null)
|
||||
Parameter(#functionTypeParameters) # RA: param[0] <- ()
|
||||
v2 = Parameter(a) # RA: param[1] <- ()
|
||||
v3 = Parameter(b) # RA: param[2] <- ()
|
||||
v4 = Parameter(c) # RA: param[3] <- ()
|
||||
ParallelMove output(param[3] -> vloc:R6, param[2] -> vloc:R5, param[1] -> vloc:R1)
|
||||
v32 = AllocateRecord (int)() # RA: R0 <- () temps: [R2, R3, R4]
|
||||
ParallelMove output(R0 -> vloc:R0)
|
||||
StoreInstanceField(#record-field[0], v32, v2) # RA: (R0, R1) temps: [R2, R3]
|
||||
DirectCall print(v32) # RA: R0 <- (R0) temps: [R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R19, R20, R23, R25, V0, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14, V15, V16, V17, V18, V19, V20, V21, V22, V23, V24, V25, V26, V27, V28, V29, V30]
|
||||
v34 = AllocateRecord (int, String, recordLiterals.T%)() # RA: R0 <- () temps: [R2, R3, R4]
|
||||
ParallelMove output(R0 -> vloc:R0, param[1] -> R3)
|
||||
StoreInstanceField(#record-field[0], v34, v2) # RA: (R0, R3) temps: [R1, R2]
|
||||
ParallelMove output(param[2] -> R4)
|
||||
StoreInstanceField(#record-field[1], v34, v3) # RA: (R0, R4) temps: [R1, R2]
|
||||
ParallelMove output(param[3] -> R5)
|
||||
StoreInstanceField(#record-field[2], v34, v4) # RA: (R0, R5) temps: [R1, R2]
|
||||
DirectCall print(v34) # RA: R0 <- (R0) temps: [R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R19, R20, R23, R25, V0, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14, V15, V16, V17, V18, V19, V20, V21, V22, V23, V24, V25, V26, V27, V28, V29, V30]
|
||||
v38 = AllocateRecord ({recordLiterals.T% bar, String foo})() # RA: R0 <- () temps: [R2, R3, R4]
|
||||
ParallelMove output(R0 -> vloc:R0, param[3] -> R3)
|
||||
StoreInstanceField(#record-field[0:bar], v38, v4) # RA: (R0, R3) temps: [R1, R2]
|
||||
ParallelMove output(param[2] -> R4)
|
||||
StoreInstanceField(#record-field[1:foo], v38, v3) # RA: (R0, R4) temps: [R1, R2]
|
||||
DirectCall print(v38) # RA: R0 <- (R0) temps: [R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R19, R20, R23, R25, V0, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14, V15, V16, V17, V18, V19, V20, V21, V22, V23, V24, V25, V26, V27, V28, V29, V30]
|
||||
v41 = AllocateRecord (int, {recordLiterals.T% bar, String foo})() # RA: R0 <- () temps: [R2, R3, R4]
|
||||
ParallelMove output(R0 -> vloc:R0, param[1] -> R3)
|
||||
StoreInstanceField(#record-field[0], v41, v2) # RA: (R0, R3) temps: [R1, R2]
|
||||
ParallelMove output(param[3] -> R3)
|
||||
StoreInstanceField(#record-field[1:bar], v41, v4) # RA: (R0, R3) temps: [R1, R2]
|
||||
ParallelMove output(param[2] -> R3)
|
||||
StoreInstanceField(#record-field[2:foo], v41, v3) # RA: (R0, R3) temps: [R1, R2]
|
||||
DirectCall print(v41) # RA: R0 <- (R0) temps: [R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R19, R20, R23, R25, V0, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14, V15, V16, V17, V18, V19, V20, V21, V22, V23, V24, V25, V26, V27, V28, V29, V30]
|
||||
ParallelMove input(NullConstant(null) -> R0)
|
||||
Return(v30) # RA: (R0)
|
||||
|
||||
--- stringInterpolation
|
||||
B0 = EntryBlock()
|
||||
v7 = Constant("Hey, x=")
|
||||
|
||||
@@ -58,6 +58,7 @@ class ModuleSnapshot : public AllStatic {
|
||||
kClosureFunctionRefs,
|
||||
kClosureRefs,
|
||||
kArgumentsDescriptorRefs,
|
||||
kRecordShapeRefs,
|
||||
kInts,
|
||||
kDoubles,
|
||||
kLists,
|
||||
@@ -623,6 +624,42 @@ class ArgumentsDescriptorRefDeserializationCluster
|
||||
Array& args_descriptor_;
|
||||
};
|
||||
|
||||
class RecordShapeRefDeserializationCluster : public DeserializationCluster {
|
||||
public:
|
||||
explicit RecordShapeRefDeserializationCluster(Zone* zone)
|
||||
: DeserializationCluster("RecordShapeRef"),
|
||||
name_(String::Handle(zone)),
|
||||
named_(Array::Handle(zone)),
|
||||
shape_(Smi::Handle(zone)) {}
|
||||
~RecordShapeRefDeserializationCluster() {}
|
||||
|
||||
void PreLoad(Deserializer* d) override {
|
||||
const intptr_t count = d->ReadUnsigned();
|
||||
for (intptr_t i = 0; i < count; i++) {
|
||||
const intptr_t num_positional = d->ReadUnsigned();
|
||||
const intptr_t num_named = d->ReadUnsigned();
|
||||
const intptr_t num_fields = num_positional + num_named;
|
||||
const Array* field_names = &Array::empty_array();
|
||||
if (num_named > 0) {
|
||||
named_ = Array::New(num_named, Heap::kOld);
|
||||
for (intptr_t i = 0; i < num_named; ++i) {
|
||||
name_ ^= d->ReadRef();
|
||||
named_.SetAt(i, name_);
|
||||
}
|
||||
named_.MakeImmutable();
|
||||
field_names = &named_;
|
||||
}
|
||||
shape_ =
|
||||
RecordShape::Register(d->thread(), num_fields, *field_names).AsSmi();
|
||||
d->AssignRefPreLoad(shape_);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
String& name_;
|
||||
Array& named_;
|
||||
Smi& shape_;
|
||||
};
|
||||
class IntDeserializationCluster : public DeserializationCluster {
|
||||
public:
|
||||
IntDeserializationCluster()
|
||||
@@ -796,6 +833,42 @@ class SetDeserializationCluster : public DeserializationCluster {
|
||||
}
|
||||
};
|
||||
|
||||
class RecordDeserializationCluster : public DeserializationCluster {
|
||||
public:
|
||||
RecordDeserializationCluster()
|
||||
: DeserializationCluster(
|
||||
"Record",
|
||||
Object::ShouldHaveDeeplyImmutabilityBitSet(kRecordCid)) {}
|
||||
~RecordDeserializationCluster() {}
|
||||
|
||||
void ReadAlloc(Deserializer* d) override {
|
||||
start_index_ = d->next_index();
|
||||
const intptr_t count = d->ReadUnsigned();
|
||||
for (intptr_t i = 0; i < count; i++) {
|
||||
const intptr_t length = d->ReadUnsigned();
|
||||
d->AssignRef(d->Allocate(Record::InstanceSize(length)));
|
||||
}
|
||||
stop_index_ = d->next_index();
|
||||
}
|
||||
|
||||
void ReadFill(Deserializer* d_) override {
|
||||
Deserializer::Local d(d_);
|
||||
|
||||
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
||||
RecordPtr record = static_cast<RecordPtr>(d.Ref(id));
|
||||
SmiPtr shape = static_cast<SmiPtr>(d.ReadRef());
|
||||
const intptr_t num_fields = RecordShape(shape).num_fields();
|
||||
Deserializer::InitializeHeader(record, kRecordCid,
|
||||
Record::InstanceSize(num_fields),
|
||||
is_deeply_immutable());
|
||||
record->untag()->shape_ = shape;
|
||||
for (intptr_t j = 0; j < num_fields; j++) {
|
||||
record->untag()->data()[j] = d.ReadRef();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class InstanceDeserializationCluster : public DeserializationCluster {
|
||||
public:
|
||||
explicit InstanceDeserializationCluster(const Class& cls)
|
||||
@@ -962,6 +1035,52 @@ class FunctionTypeDeserializationCluster : public DeserializationCluster {
|
||||
}
|
||||
};
|
||||
|
||||
class RecordTypeDeserializationCluster : public DeserializationCluster {
|
||||
public:
|
||||
RecordTypeDeserializationCluster()
|
||||
: DeserializationCluster(
|
||||
"RecordType",
|
||||
Object::ShouldHaveDeeplyImmutabilityBitSet(kRecordTypeCid)) {}
|
||||
~RecordTypeDeserializationCluster() {}
|
||||
|
||||
void ReadAlloc(Deserializer* d) override {
|
||||
ReadAllocFixedSize(d, RecordType::InstanceSize());
|
||||
}
|
||||
|
||||
void ReadFill(Deserializer* d_) override {
|
||||
Deserializer::Local d(d_);
|
||||
|
||||
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
||||
RecordTypePtr type = static_cast<RecordTypePtr>(d.Ref(id));
|
||||
Deserializer::InitializeHeader(type, kRecordTypeCid,
|
||||
RecordType::InstanceSize(),
|
||||
is_deeply_immutable());
|
||||
type->untag()->type_test_stub_entry_point_.store(
|
||||
0, std::memory_order_relaxed);
|
||||
const intptr_t is_nullable = d.ReadUnsigned();
|
||||
const intptr_t flags = UntaggedAbstractType::NullabilityBit::update(
|
||||
is_nullable, UntaggedAbstractType::TypeStateBits::encode(
|
||||
UntaggedAbstractType::kAllocated));
|
||||
type->untag()->set_flags(flags);
|
||||
type->untag()->type_test_stub_ = static_cast<CodePtr>(d.null());
|
||||
type->untag()->hash_ = Smi::New(0);
|
||||
type->untag()->shape_ = static_cast<SmiPtr>(d.ReadRef());
|
||||
type->untag()->field_types_ = static_cast<ArrayPtr>(d.ReadRef());
|
||||
}
|
||||
}
|
||||
|
||||
void PostLoad(Deserializer* d, const Array& refs) override {
|
||||
RecordType& type = RecordType::Handle(d->zone());
|
||||
Code& stub = Code::Handle(d->zone());
|
||||
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
||||
type ^= refs.At(id);
|
||||
stub = TypeTestingStubGenerator::DefaultCodeForType(type);
|
||||
type.InitializeTypeTestingStubNonAtomic(stub);
|
||||
type.SetIsFinalized();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class InterfaceTypeDeserializationCluster : public DeserializationCluster {
|
||||
public:
|
||||
InterfaceTypeDeserializationCluster()
|
||||
@@ -1311,7 +1430,7 @@ class ObjectPoolDeserializationCluster : public DeserializationCluster {
|
||||
continue;
|
||||
}
|
||||
obj = pool.ObjectAt(i);
|
||||
if (obj.IsInstance() && !obj.InVMIsolateHeap()) {
|
||||
if (obj.IsInstance() && !obj.IsSmi() && !obj.InVMIsolateHeap()) {
|
||||
obj = Instance::Cast(obj).Canonicalize(d->thread());
|
||||
pool.SetObjectAt(i, obj);
|
||||
}
|
||||
@@ -1392,6 +1511,8 @@ DeserializationCluster* Deserializer::ReadCluster() {
|
||||
return new (Z) ClosureRefDeserializationCluster(Z);
|
||||
case ModuleSnapshot::kArgumentsDescriptorRefs:
|
||||
return new (Z) ArgumentsDescriptorRefDeserializationCluster(Z);
|
||||
case ModuleSnapshot::kRecordShapeRefs:
|
||||
return new (Z) RecordShapeRefDeserializationCluster(Z);
|
||||
case ModuleSnapshot::kInts:
|
||||
return new (Z) IntDeserializationCluster();
|
||||
case ModuleSnapshot::kDoubles:
|
||||
@@ -1403,9 +1524,7 @@ DeserializationCluster* Deserializer::ReadCluster() {
|
||||
case ModuleSnapshot::kSets:
|
||||
return new (Z) SetDeserializationCluster();
|
||||
case ModuleSnapshot::kRecords:
|
||||
// return new (Z) RecordDeserializationCluster();
|
||||
UNIMPLEMENTED();
|
||||
return nullptr;
|
||||
return new (Z) RecordDeserializationCluster();
|
||||
case ModuleSnapshot::kInstantiatedClosures:
|
||||
// return new (Z) InstantiatedClosureDeserializationCluster();
|
||||
UNIMPLEMENTED();
|
||||
@@ -1419,9 +1538,7 @@ DeserializationCluster* Deserializer::ReadCluster() {
|
||||
case ModuleSnapshot::kFunctionTypes:
|
||||
return new (Z) FunctionTypeDeserializationCluster();
|
||||
case ModuleSnapshot::kRecordTypes:
|
||||
// return new (Z) RecordTypeDeserializationCluster();
|
||||
UNIMPLEMENTED();
|
||||
return nullptr;
|
||||
return new (Z) RecordTypeDeserializationCluster();
|
||||
case ModuleSnapshot::kTypeParameterTypes:
|
||||
return new (Z) TypeParameterTypeDeserializationCluster();
|
||||
case ModuleSnapshot::kTypeArguments:
|
||||
|
||||
@@ -57,6 +57,8 @@ class InterfaceTypeDeserializationCluster;
|
||||
class ListDeserializationCluster;
|
||||
class MapDeserializationCluster;
|
||||
class ObjectPoolDeserializationCluster;
|
||||
class RecordDeserializationCluster;
|
||||
class RecordTypeDeserializationCluster;
|
||||
class SetDeserializationCluster;
|
||||
class SubtypeTestCacheDeserializationCluster;
|
||||
class TypeArgumentsDeserializationCluster;
|
||||
@@ -3088,6 +3090,8 @@ class UntaggedRecordType : public UntaggedAbstractType {
|
||||
VISIT_TO(field_types)
|
||||
|
||||
CompressedObjectPtr* to_snapshot(Snapshot::Kind kind) { return to(); }
|
||||
|
||||
friend class module_snapshot::RecordTypeDeserializationCluster;
|
||||
};
|
||||
|
||||
class UntaggedTypeParameter : public UntaggedAbstractType {
|
||||
@@ -3644,6 +3648,7 @@ class UntaggedRecord : public UntaggedInstance {
|
||||
// Variable length data follows here.
|
||||
COMPRESSED_VARIABLE_POINTER_FIELDS(ObjectPtr, field, data, shape)
|
||||
|
||||
friend class module_snapshot::RecordDeserializationCluster;
|
||||
friend void UpdateLengthField(intptr_t, ObjectPtr,
|
||||
ObjectPtr); // shape_
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user