Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit f6f29df

Browse files
joshualittCommit Bot
authored andcommitted
[dart2wasm] Enable RTI tests for oddball and interface types.
Change-Id: I4b92d4cf30779b78f3eab1acd18193fdd58ab452 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/249641 Reviewed-by: Aske Simon Christensen <askesc@google.com> Commit-Queue: Joshua Litt <joshualitt@google.com>
1 parent a04e697 commit f6f29df

File tree

9 files changed

+154
-39
lines changed

9 files changed

+154
-39
lines changed

pkg/_js_interop_checks/lib/src/transformations/js_util_wasm_optimizer.dart

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,8 @@ class JsUtilWasmOptimizer extends Transformer {
297297
Expression getObjectOffGlobalThis(Procedure node, List<String> selectors) {
298298
Expression currentTarget = _globalThis;
299299
for (String selector in selectors) {
300-
currentTarget = _getProperty(node, currentTarget, selector);
300+
currentTarget = _getProperty(node, currentTarget, selector,
301+
typeArgument: _nonNullableObjectType);
301302
}
302303
return currentTarget;
303304
}
@@ -315,8 +316,9 @@ class JsUtilWasmOptimizer extends Transformer {
315316
type: _nonNullableObjectType);
316317
body.add(object);
317318
for (VariableDeclaration variable in node.function.namedParameters) {
318-
body.add(ExpressionStatement(
319-
_setProperty(node, VariableGet(object), variable.name!, variable)));
319+
body.add(ExpressionStatement(_setProperty(
320+
node, VariableGet(object), variable.name!, variable,
321+
typeArgument: variable.type)));
320322
}
321323
body.add(ReturnStatement(VariableGet(object)));
322324
return Block(body);
@@ -349,12 +351,12 @@ class JsUtilWasmOptimizer extends Transformer {
349351
///
350352
/// The new [Expression] is equivalent to:
351353
/// `js_util.getProperty([object], [getterName])`.
352-
Expression _getProperty(
353-
Procedure node, Expression object, String getterName) =>
354+
Expression _getProperty(Procedure node, Expression object, String getterName,
355+
{DartType? typeArgument}) =>
354356
StaticInvocation(
355357
_getPropertyTarget,
356358
Arguments([object, StringLiteral(getterName)],
357-
types: [node.function.returnType]))
359+
types: [typeArgument ?? node.function.returnType]))
358360
..fileOffset = node.fileOffset;
359361

360362
/// Returns a new function body for the given [node] external getter.
@@ -377,11 +379,11 @@ class JsUtilWasmOptimizer extends Transformer {
377379
/// The new [Expression] is equivalent to:
378380
/// `js_util.setProperty([object], [setterName], [value])`.
379381
Expression _setProperty(Procedure node, Expression object, String setterName,
380-
VariableDeclaration value) =>
382+
VariableDeclaration value, {DartType? typeArgument}) =>
381383
StaticInvocation(
382384
_setPropertyTarget,
383385
Arguments([object, StringLiteral(setterName), VariableGet(value)],
384-
types: [node.function.returnType]))
386+
types: [typeArgument ?? node.function.returnType]))
385387
..fileOffset = node.fileOffset;
386388

387389
/// Returns a new function body for the given [node] external setter.

pkg/dart2wasm/lib/constants.dart

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -803,8 +803,14 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?> {
803803
return createConstant(constant, info.nonNullableType, (function, b) {
804804
b.i32_const(info.classId);
805805
b.i32_const(initialIdentityHash);
806-
types.encodeNullability(b, type);
807-
b.i32_const(environmentIndex);
806+
807+
// A type parameter's type nullability is undetermined when it's
808+
// syntactically not declared nullable and the bound of the type
809+
// parameter is nullable. Because we are encoding the declared
810+
// nullability, we only declare a type parameter to be nullable if it is
811+
// explicitly declared to be nullabe.
812+
b.i32_const(type.declaredNullability == Nullability.nullable ? 1 : 0);
813+
b.i64_const(environmentIndex);
808814
translator.struct_new(b, info);
809815
});
810816
} else {

pkg/dart2wasm/lib/intrinsics.dart

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -593,7 +593,8 @@ class Intrinsifier {
593593
if (className != null) {
594594
List<String> libAndClass = className.split("#");
595595
Class cls = translator.libraries
596-
.firstWhere((l) => l.name == libAndClass[0])
596+
.firstWhere(
597+
(l) => l.name == libAndClass[0] && l.importUri.scheme == 'dart')
597598
.classes
598599
.firstWhere((c) => c.name == libAndClass[1]);
599600
int classId = translator.classInfo[cls]!.classId;
@@ -682,6 +683,8 @@ class Intrinsifier {
682683
return translator.types.makeTypeRulesSupers(b);
683684
case "_getTypeRulesSubstitutions":
684685
return translator.types.makeTypeRulesSubstitutions(b);
686+
case "_getTypeNames":
687+
return translator.types.makeTypeNames(b);
685688
case "_getInterfaceTypeRuntimeType":
686689
Expression object = node.arguments.positional[0];
687690
Expression typeArguments = node.arguments.positional[1];
@@ -793,6 +796,26 @@ class Intrinsifier {
793796
return w.NumType.f64;
794797
case "getID":
795798
return getID(node.arguments.positional.single);
799+
case "makeListFixedLength":
800+
ClassInfo receiverInfo =
801+
translator.classInfo[translator.listBaseClass]!;
802+
codeGen.wrap(
803+
node.arguments.positional.single, receiverInfo.nonNullableType);
804+
w.Local receiverLocal =
805+
codeGen.function.addLocal(receiverInfo.nullableType);
806+
b.local_tee(receiverLocal);
807+
// We ignore the type argument and just update the classID of the
808+
// receiver.
809+
// TODO(joshualitt): If the amount of free space is significant, it
810+
// might be worth doing a copy here.
811+
ClassInfo topInfo = translator.topInfo;
812+
ClassInfo fixedLengthListInfo =
813+
translator.classInfo[translator.fixedLengthListClass]!;
814+
b.i32_const(fixedLengthListInfo.classId);
815+
b.struct_set(topInfo.struct, FieldIndex.classId);
816+
b.local_get(receiverLocal);
817+
b.ref_as_non_null();
818+
return fixedLengthListInfo.nonNullableType;
796819
}
797820
}
798821

pkg/dart2wasm/lib/translator.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ class Translator {
127127
late final Procedure hashImmutableIndexNullable;
128128
late final Procedure isSubtype;
129129
late final Procedure objectRuntimeType;
130+
late final Procedure typeAsNullable;
130131
late final Map<Class, w.StorageType> builtinTypes;
131132
late final Map<w.ValueType, Class> boxedClasses;
132133

@@ -272,6 +273,9 @@ class Translator {
272273
objectRuntimeType = lookupCore("Object")
273274
.procedures
274275
.firstWhere((p) => p.name.text == "_runtimeType");
276+
typeAsNullable = lookupCore("_Type")
277+
.procedures
278+
.firstWhere((p) => p.name.text == "asNullable");
275279
builtinTypes = {
276280
coreTypes.boolClass: w.NumType.i32,
277281
coreTypes.intClass: w.NumType.i64,

pkg/dart2wasm/lib/types.dart

Lines changed: 69 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ class Types {
6363
late final List<List<List<DartType>>> typeRulesSubstitutions =
6464
_buildTypeRulesSubstitutions();
6565

66+
/// A list which maps class ID to the classes [String] name.
67+
late final List<String> typeNames = _buildTypeNames();
68+
6669
Types(this.translator);
6770

6871
w.ValueType classAndFieldToType(Class cls, int fieldIndex) =>
@@ -100,8 +103,14 @@ class Types {
100103
if (superclassInfo.cls == null ||
101104
superclassInfo.cls == coreTypes.objectClass) continue;
102105
Class superclass = superclassInfo.cls!;
103-
Iterable<Class> subclasses =
104-
_getConcreteSubtypes(superclass).where((cls) => cls != superclass);
106+
107+
// TODO(joshualitt): This includes abstract types that can't be
108+
// instantiated, but might be needed for subtype checks. The majority of
109+
// abstract classes are probably unnecessary though. We should filter
110+
// these cases to reduce the size of the type rules.
111+
Iterable<Class> subclasses = translator.subtypes
112+
.getSubtypesOf(superclass)
113+
.where((cls) => cls != superclass);
105114
Iterable<InterfaceType> subtypes = subclasses.map(
106115
(Class cls) => cls.getThisType(coreTypes, Nullability.nonNullable));
107116
for (InterfaceType subtype in subtypes) {
@@ -144,6 +153,17 @@ class Types {
144153
return typeRulesSubstitutions;
145154
}
146155

156+
List<String> _buildTypeNames() {
157+
// This logic assumes `translator.classes` returns the classes indexed by
158+
// class ID. If we ever change that logic, we will need to change this code.
159+
List<String> typeNames = [];
160+
for (ClassInfo classInfo in translator.classes) {
161+
String className = classInfo.cls?.name ?? '';
162+
typeNames.add(className);
163+
}
164+
return typeNames;
165+
}
166+
147167
/// Builds a map of subclasses to the transitive set of superclasses they
148168
/// implement.
149169
/// TODO(joshualitt): This implementation is just temporary. Eventually we
@@ -205,6 +225,25 @@ class Types {
205225
return expectedType;
206226
}
207227

228+
/// Returns a list of string type names for pretty printing types.
229+
w.ValueType makeTypeNames(w.Instructions b) {
230+
w.ValueType expectedType =
231+
translator.classInfo[translator.immutableListClass]!.nonNullableType;
232+
DartType stringType = InterfaceType(
233+
translator.stringBaseClass,
234+
Nullability.nonNullable,
235+
[translator.coreTypes.stringNonNullableRawType]);
236+
List<StringConstant> listStringConstant = [];
237+
for (String name in typeNames) {
238+
listStringConstant.add(StringConstant(name));
239+
}
240+
DartType listStringType = InterfaceType(
241+
translator.immutableListClass, Nullability.nonNullable, [stringType]);
242+
translator.constants.instantiateConstant(null, b,
243+
ListConstant(listStringType, listStringConstant), expectedType);
244+
return expectedType;
245+
}
246+
208247
bool isGenericFunction(FunctionType type) => type.typeParameters.isNotEmpty;
209248

210249
bool isGenericFunctionTypeParameter(TypeParameterType type) =>
@@ -358,7 +397,11 @@ class Types {
358397
type is FutureOrType ||
359398
type is FunctionType);
360399
if (type is TypeParameterType) {
361-
return codeGen.instantiateTypeParameter(type.parameter);
400+
codeGen.instantiateTypeParameter(type.parameter);
401+
if (type.declaredNullability == Nullability.nullable) {
402+
codeGen.call(translator.typeAsNullable.reference);
403+
}
404+
return nonNullableTypeType;
362405
}
363406
ClassInfo info = translator.classInfo[classForType(type)]!;
364407
translator.functions.allocateClass(info.classId);
@@ -390,16 +433,20 @@ class Types {
390433
void emitTypeTest(CodeGenerator codeGen, DartType type, DartType operandType,
391434
TreeNode node) {
392435
w.Instructions b = codeGen.b;
393-
if (type is! InterfaceType) {
394-
// TODO(joshualitt): We can enable this after fixing `.runtimeType`.
395-
// makeType(codeGen, type);
396-
// codeGen.call(translator.isSubtype.reference);
397-
print("Not implemented: Type test with non-interface type $type"
436+
if (type is FunctionType) {
437+
// TODO(joshualitt): We can enable type tests for [FunctionType] after
438+
// enabling `.runtimeType` for [FunctionType].
439+
print("Not implemented: Type test with function type $type"
398440
" at ${node.location}");
399441
b.drop();
400442
b.i32_const(1);
401443
return;
402444
}
445+
if (type is! InterfaceType) {
446+
makeType(codeGen, type);
447+
codeGen.call(translator.isSubtype.reference);
448+
return;
449+
}
403450
bool isPotentiallyNullable = operandType.isPotentiallyNullable;
404451
w.Label? resultLabel;
405452
if (isPotentiallyNullable) {
@@ -412,6 +459,15 @@ class Types {
412459
b.local_get(operand);
413460
b.br_on_null(nullLabel);
414461
}
462+
void _endPotentiallyNullableBlock() {
463+
if (isPotentiallyNullable) {
464+
b.br(resultLabel!);
465+
b.end(); // nullLabel
466+
encodeNullability(b, type);
467+
b.end(); // resultLabel
468+
}
469+
}
470+
415471
if (type.typeArguments.any((t) => t is! DynamicType)) {
416472
// If the tested-against type as an instance of the static operand type
417473
// has the same type arguments as the static operand type, it is not
@@ -421,8 +477,10 @@ class Types {
421477
.getTypeAsInstanceOf(type, cls, codeGen.member.enclosingLibrary)
422478
?.withDeclaredNullability(operandType.declaredNullability);
423479
if (base != operandType) {
424-
print("Not implemented: Type test with type arguments"
425-
" at ${node.location}");
480+
makeType(codeGen, type);
481+
codeGen.call(translator.isSubtype.reference);
482+
_endPotentiallyNullableBlock();
483+
return;
426484
}
427485
}
428486
List<Class> concrete = _getConcreteSubtypes(type.classNode).toList();
@@ -454,12 +512,7 @@ class Types {
454512
b.i32_const(0);
455513
b.end(); // done
456514
}
457-
if (isPotentiallyNullable) {
458-
b.br(resultLabel!);
459-
b.end(); // nullLabel
460-
encodeNullability(b, type);
461-
b.end(); // resultLabel
462-
}
515+
_endPotentiallyNullableBlock();
463516
}
464517

465518
/// Returns true if a given type is nullable, and false otherwise. This

sdk/lib/_internal/wasm/lib/class_id.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@ class ClassID {
1616
external static int get cidUint8ArrayView;
1717
@pragma("wasm:class-id", "dart.core#Object")
1818
external static int get cidObject;
19-
@pragma("wasm:class-id", "dart.async#Future")
20-
external static int get cidFuture;
19+
@pragma("wasm:class-id", "dart.async#_Future")
20+
external static int get cid_Future;
2121
@pragma("wasm:class-id", "dart.core#Function")
2222
external static int get cidFunction;
23+
@pragma("wasm:class-id", "dart.core#_Function")
24+
external static int get cid_Function;
2325

2426
// Class IDs for RTI Types.
2527
@pragma("wasm:class-id", "dart.core#_NeverType")

sdk/lib/_internal/wasm/lib/object_patch.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ class Object {
4545
/// override [runtimeType].
4646
@patch
4747
external Type get runtimeType;
48+
49+
@pragma("wasm:entry-point")
4850
_Type get _runtimeType => _getInterfaceTypeRuntimeType(this, _typeArguments);
4951

5052
@patch

0 commit comments

Comments
 (0)