Skip to content

Commit a48e5e5

Browse files
natebiggsCommit Queue
authored andcommitted
[dart2wasm] Fix record representation equality in dynamic modules.
Generated Record `==` methods begin by doing a type check on the argument. Today this is implemented as an interface subtype check like `other is <ThisRecordType>`. This generally ends up being a class ID check. For dynamic modules, the class ID is not statically known so the type test fails. Instead for dynamic modules we can have the `==` do a full check against the RTI object using `_checkInstance`. This will correctly check the structure of the type rather than just the class ID. TEST=N/A small signature change. Change-Id: I86832d2bcdf0e95bd85267c016e5e69b002bcb68 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/416323 Reviewed-by: Martin Kustermann <[email protected]> Commit-Queue: Nate Biggs <[email protected]>
1 parent bd7f26f commit a48e5e5

File tree

6 files changed

+47
-15
lines changed

6 files changed

+47
-15
lines changed

pkg/dart2wasm/lib/compile.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,8 +244,8 @@ Future<CompilationResult> compileToModule(
244244
? null
245245
: js.createRuntimeFinalizer(component, coreTypes, classHierarchy);
246246

247-
final Map<RecordShape, Class> recordClasses =
248-
generateRecordClasses(component, coreTypes);
247+
final Map<RecordShape, Class> recordClasses = generateRecordClasses(
248+
component, coreTypes, isDynamicMainModule || isDynamicModule);
249249
target.recordClasses = recordClasses;
250250

251251
if (options.dumpKernelBeforeTfa != null) {

pkg/dart2wasm/lib/dynamic_modules.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,9 @@ class DynamicMainModuleStrategy extends DefaultModuleStrategy with KernelNodes {
183183
add(procedure, kDynModuleCallablePragmaName);
184184
}
185185

186+
// Mark all record classes as dynamic module extendable.
187+
addPragma(coreTypes.recordClass, kDynModuleExtendablePragmaName, coreTypes);
188+
186189
// SystemHash.combine used by closures.
187190
add(systemHashCombine, kDynModuleCallablePragmaName);
188191
}
@@ -361,6 +364,7 @@ void _recordIdDynamic(w.FunctionBuilder f, Translator translator) {
361364
ib.i32_const(0);
362365
} else {
363366
ib.local_get(ib.locals[0]);
367+
translator.callReference(translator.localizeClassId.reference, ib);
364368
ib.emitClassIdRangeCheck(ranges);
365369
}
366370
ib.end();

pkg/dart2wasm/lib/record_class_generator.dart

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,10 @@ import 'util.dart';
8181
/// }
8282
/// ```
8383
Map<RecordShape, Class> generateRecordClasses(
84-
Component component, CoreTypes coreTypes) {
84+
Component component, CoreTypes coreTypes, bool isDynamicModuleEnabled) {
8585
final Map<RecordShape, Class> recordClasses = {};
86-
final recordClassGenerator = _RecordClassGenerator(recordClasses, coreTypes);
86+
final recordClassGenerator =
87+
_RecordClassGenerator(recordClasses, coreTypes, isDynamicModuleEnabled);
8788
final visitor = _RecordVisitor(recordClassGenerator);
8889
component.libraries.forEach(visitor.visitLibrary);
8990
return recordClasses;
@@ -92,6 +93,7 @@ Map<RecordShape, Class> generateRecordClasses(
9293
class _RecordClassGenerator {
9394
final CoreTypes coreTypes;
9495
final Map<RecordShape, Class> classes;
96+
final bool isDynamicModuleEnabled;
9597

9698
late final Class typeRuntimetypeTypeClass =
9799
coreTypes.index.getClass("dart:core", "_Type");
@@ -190,7 +192,8 @@ class _RecordClassGenerator {
190192
return map;
191193
})();
192194

193-
_RecordClassGenerator(this.classes, this.coreTypes);
195+
_RecordClassGenerator(
196+
this.classes, this.coreTypes, this.isDynamicModuleEnabled);
194197

195198
void generateClassForRecordType(RecordType recordType) {
196199
final shape = RecordShape.fromType(recordType);
@@ -228,9 +231,10 @@ class _RecordClassGenerator {
228231
coreTypes);
229232

230233
library.addClass(cls);
231-
cls.addProcedure(_generateEquals(shape, fields, cls));
234+
final getRti = _generateRecordRuntimeType(shape, fields);
235+
cls.addProcedure(_generateEquals(shape, fields, cls, getRti));
232236
cls.addProcedure(_generateCheckRecordType(shape, fields));
233-
cls.addProcedure(_generateRecordRuntimeType(shape, fields));
237+
cls.addProcedure(getRti);
234238
cls.addProcedure(_generateMasqueradedRecordRuntimeType(shape, fields));
235239
return cls;
236240
}
@@ -385,7 +389,8 @@ class _RecordClassGenerator {
385389
}
386390

387391
/// Generate `bool operator ==` member.
388-
Procedure _generateEquals(RecordShape shape, List<Field> fields, Class cls) {
392+
Procedure _generateEquals(
393+
RecordShape shape, List<Field> fields, Class cls, Procedure getRti) {
389394
final equalsFunctionType = FunctionType(
390395
[nullableObjectType],
391396
boolType,
@@ -397,12 +402,32 @@ class _RecordClassGenerator {
397402

398403
final List<Statement> statements = [];
399404

400-
statements.add(IfStatement(
401-
Not(IsExpression(
402-
VariableGet(parameter), InterfaceType(cls, Nullability.nonNullable))),
403-
ReturnStatement(BoolLiteral(false)),
404-
null,
405-
));
405+
if (isDynamicModuleEnabled) {
406+
final checkInstance = coreTypes.index
407+
.getProcedure('dart:core', '_RecordType', '_checkInstance');
408+
statements.add(IfStatement(
409+
Not(InstanceInvocation(
410+
InstanceAccessKind.Instance,
411+
(InstanceInvocation(InstanceAccessKind.Instance, ThisExpression(),
412+
getRti.name, Arguments([]),
413+
interfaceTarget: getRti,
414+
functionType: getRti.computeSignatureOrFunctionType())),
415+
checkInstance.name,
416+
Arguments([VariableGet(parameter)]),
417+
interfaceTarget: checkInstance,
418+
functionType: checkInstance.computeSignatureOrFunctionType(),
419+
)),
420+
ReturnStatement(BoolLiteral(false)),
421+
null,
422+
));
423+
} else {
424+
statements.add(IfStatement(
425+
Not(IsExpression(VariableGet(parameter),
426+
InterfaceType(cls, Nullability.nonNullable))),
427+
ReturnStatement(BoolLiteral(false)),
428+
null,
429+
));
430+
}
406431

407432
// Compare fields.
408433
for (Field field in fields) {

pkg/vm/test/transformations/type_flow/transformer_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ void runTestCase(
5050
if (target is WasmTarget) {
5151
// Keep these flags in-sync with pkg/dart2wasm/lib/compile.dart
5252
useRapidTypeAnalysis = false;
53-
target.recordClasses = generateRecordClasses(component, coreTypes);
53+
target.recordClasses = generateRecordClasses(component, coreTypes, false);
5454
}
5555

5656
component = transformComponent(

sdk/lib/_internal/wasm/lib/record_patch.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ abstract class Record {
1212
_RecordType get _masqueradedRecordRuntimeType;
1313
_RecordType get _recordRuntimeType;
1414

15+
@pragma('dyn-module:can-be-overridden')
1516
bool _checkRecordType(
1617
WasmArray<_Type> types,
1718
ImmutableWasmArray<String> names,

sdk/lib/_internal/wasm/lib/type.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,7 @@ class _RecordType extends _Type {
580580
_Type get _asNullable => _RecordType(names, fieldTypes, true);
581581

582582
@override
583+
@pragma('dyn-module:callable')
583584
bool _checkInstance(Object o) {
584585
if (!_isRecordClassId(ClassID.getID(o))) return false;
585586
return unsafeCast<Record>(o)._checkRecordType(fieldTypes, names);
@@ -1932,6 +1933,7 @@ _Type _getActualRuntimeType(Object object) {
19321933
}
19331934

19341935
@pragma("wasm:prefer-inline")
1936+
@pragma('dyn-module:callable')
19351937
_Type _getActualRuntimeTypeNullable(Object? object) =>
19361938
object == null ? _literal<Null>() : _getActualRuntimeType(object);
19371939

0 commit comments

Comments
 (0)