Skip to content

Commit 85dfcd8

Browse files
askeksaCommit Queue
authored and
Commit Queue
committed
[dart2wasm] Compute instance method parameter types from Wasm types.
This improves the precision of the Wasm types for Dart function types in instance method signatures. It could in principle also improve the precision of the receiver parameter from the LUB of the representation types to the LUB of the actual structs for the classes implementing the member. However, if at a call site the receiver parameter type is more precise than the representation type of the receiver, the call site will need to cast the receiver to the more precise type. This gives rise to more casts in practice than the ones saved inside the methods by the more precise receiver parameter type. For this reason, the receiver parameter type is kept at the LUB of the representation types of the classes implementing the member. This change is also a stepping stone towards not having `ClassInfo` for the special Wasm types. CoreLibraryReviewExempt: Only affects Wasm-specific libraries. Change-Id: I81ca5aa1107f1c04ed7729a758e983698492a106 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/288400 Reviewed-by: Joshua Litt <[email protected]> Commit-Queue: Aske Simon Christensen <[email protected]>
1 parent 482b4e4 commit 85dfcd8

File tree

5 files changed

+103
-66
lines changed

5 files changed

+103
-66
lines changed

pkg/dart2wasm/lib/class_info.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ class ClassInfo {
130130
/// The class whose struct is used as the type for variables of this type.
131131
/// This is a type which is a superclass of all subtypes of this type.
132132
late final ClassInfo repr = upperBound(
133-
implementedBy.map((c) => identical(c, this) ? this : c.repr).toList());
133+
implementedBy.map((c) => identical(c, this) ? this : c.repr).toSet());
134134

135135
/// All classes which implement this class. This is used to compute `repr`.
136136
final List<ClassInfo> implementedBy = [];
@@ -158,7 +158,7 @@ class ClassInfo {
158158
}
159159
}
160160

161-
ClassInfo upperBound(Iterable<ClassInfo> classes) {
161+
ClassInfo upperBound(Set<ClassInfo> classes) {
162162
while (classes.length > 1) {
163163
Set<ClassInfo> newClasses = {};
164164
int minDepth = 999999999;

pkg/dart2wasm/lib/dispatch_table.dart

Lines changed: 53 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import 'dart:math' show min;
6+
57
import 'package:dart2wasm/class_info.dart';
68
import 'package:dart2wasm/param_info.dart';
79
import 'package:dart2wasm/reference_extensions.dart';
@@ -83,18 +85,17 @@ class SelectorInfo {
8385
w.FunctionType _computeSignature() {
8486
var nameIndex = paramInfo.nameIndex;
8587
final int returnCount = isSetter ? 0 : 1;
86-
List<Set<ClassInfo>> inputSets =
88+
List<Set<w.ValueType>> inputSets =
8789
List.generate(1 + paramInfo.paramCount, (_) => {});
88-
List<Set<ClassInfo>> outputSets = List.generate(returnCount, (_) => {});
89-
List<bool> inputNullable = List.filled(1 + paramInfo.paramCount, false);
90+
List<Set<w.ValueType>> outputSets = List.generate(returnCount, (_) => {});
9091
List<bool> ensureBoxed = List.filled(1 + paramInfo.paramCount, false);
91-
List<bool> outputNullable = List.filled(returnCount, false);
9292
targets.forEach((classId, target) {
93-
ClassInfo receiver = translator.classes[classId];
93+
Member member = target.asMember;
94+
DartType receiver =
95+
InterfaceType(member.enclosingClass!, Nullability.nonNullable);
9496
List<DartType> positional;
9597
Map<String, DartType> named;
9698
List<DartType> returns;
97-
Member member = target.asMember;
9899
if (member is Field) {
99100
if (target.isImplicitGetter) {
100101
positional = const [];
@@ -137,54 +138,79 @@ class SelectorInfo {
137138
}
138139
}
139140
assert(returns.length <= outputSets.length);
140-
inputSets[0].add(receiver);
141-
ensureBoxed[0] = true;
141+
inputSets[0].add(translator.translateType(receiver));
142+
ensureBoxed[0] = member.enclosingClass != translator.ffiPointerClass;
142143
for (int i = 0; i < positional.length; i++) {
143144
DartType type = positional[i];
144-
inputSets[1 + i]
145-
.add(translator.classInfo[translator.classForType(type)]!);
146-
inputNullable[1 + i] |= type.isPotentiallyNullable;
145+
inputSets[1 + i].add(translator.translateType(type));
147146
ensureBoxed[1 + i] |=
148147
paramInfo.positional[i] == ParameterInfo.defaultValueSentinel;
149148
}
150149
for (String name in named.keys) {
151150
int i = nameIndex[name]!;
152151
DartType type = named[name]!;
153-
inputSets[1 + i]
154-
.add(translator.classInfo[translator.classForType(type)]!);
155-
inputNullable[1 + i] |= type.isPotentiallyNullable;
152+
inputSets[1 + i].add(translator.translateType(type));
156153
ensureBoxed[1 + i] |=
157154
paramInfo.named[name] == ParameterInfo.defaultValueSentinel;
158155
}
159156
for (int i = 0; i < returnCount; i++) {
160157
if (i < returns.length) {
161-
outputSets[i]
162-
.add(translator.classInfo[translator.classForType(returns[i])]!);
163-
outputNullable[i] |= returns[i].isPotentiallyNullable;
158+
DartType type = returns[i];
159+
outputSets[i].add(translator.translateType(type));
164160
} else {
165-
outputNullable[i] = true;
161+
outputSets[i].add(translator.topInfo.nullableType);
166162
}
167163
}
168164
});
169165

170166
List<w.ValueType> typeParameters = List.filled(paramInfo.typeParamCount,
171167
translator.classInfo[translator.typeClass]!.nonNullableType);
172-
List<w.ValueType> inputs = List.generate(
173-
inputSets.length,
174-
(i) => translator.typeForInfo(
175-
upperBound(inputSets[i]), inputNullable[i],
176-
ensureBoxed: ensureBoxed[i]) as w.ValueType);
168+
List<w.ValueType> inputs = List.generate(inputSets.length,
169+
(i) => _upperBound(inputSets[i], ensureBoxed: ensureBoxed[i]));
177170
if (name == '==') {
178171
// == can't be called with null
179172
inputs[1] = inputs[1].withNullability(false);
180173
}
181-
List<w.ValueType> outputs = List.generate(
182-
outputSets.length,
183-
(i) => translator.typeForInfo(
184-
upperBound(outputSets[i]), outputNullable[i]) as w.ValueType);
174+
List<w.ValueType> outputs = List.generate(outputSets.length,
175+
(i) => _upperBound(outputSets[i], ensureBoxed: false));
185176
return m.addFunctionType(
186177
[inputs[0], ...typeParameters, ...inputs.sublist(1)], outputs);
187178
}
179+
180+
w.ValueType _upperBound(Set<w.ValueType> types, {required bool ensureBoxed}) {
181+
if (!ensureBoxed && types.length == 1 && types.single.isPrimitive) {
182+
// Unboxed primitive.
183+
return types.single;
184+
}
185+
final bool nullable = types.any((type) => type.nullable);
186+
int minDepth = 999999999;
187+
Set<w.DefType> heapTypes = types
188+
.where((type) => type is! w.RefType || type.heapType is w.DefType)
189+
.map((type) {
190+
w.DefType def = type is w.RefType
191+
? type.heapType as w.DefType
192+
: translator.classInfo[translator.boxedClasses[type]!]!.struct;
193+
minDepth = min(minDepth, def.depth);
194+
return def;
195+
}).toSet();
196+
if (heapTypes.isEmpty) {
197+
// Only abstract heap types.
198+
Set<w.HeapType> heapTypes =
199+
types.map((type) => (type as w.RefType).heapType).toSet();
200+
return w.RefType(heapTypes.single, nullable: nullable);
201+
}
202+
int targetDepth = minDepth;
203+
while (heapTypes.length > 1) {
204+
heapTypes = heapTypes.map((s) {
205+
while (s.depth > targetDepth) {
206+
s = s.superType!;
207+
}
208+
return s;
209+
}).toSet();
210+
targetDepth -= 1;
211+
}
212+
return w.RefType.def(heapTypes.single, nullable: nullable);
213+
}
188214
}
189215

190216
/// Builds the dispatch table for member calls.

pkg/dart2wasm/lib/transformers.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class _WasmTransformer extends Transformer {
3030
StaticTypeContext? _cachedTypeContext;
3131
final Library _coreLibrary;
3232
final InterfaceType _nonNullableTypeType;
33+
final Class _wasmBaseClass;
3334
final List<_AsyncStarFrame> _asyncStarFrames = [];
3435
bool _enclosingIsAsyncStar = false;
3536
late final controllerNullableObjectType = InterfaceType(
@@ -52,6 +53,7 @@ class _WasmTransformer extends Transformer {
5253
_nonNullableTypeType = coreTypes.index
5354
.getClass('dart:core', '_Type')
5455
.getThisType(coreTypes, Nullability.nonNullable),
56+
_wasmBaseClass = coreTypes.index.getClass('dart:wasm', '_WasmBase'),
5557
_coreLibrary = coreTypes.index.getLibrary('dart:core');
5658

5759
@override
@@ -108,6 +110,7 @@ class _WasmTransformer extends Transformer {
108110
// `Type` object.
109111
if (!cls.isAbstract &&
110112
cls != coreTypes.objectClass &&
113+
!env.hierarchy.isSubclassOf(cls, _wasmBaseClass) &&
111114
!canReuseSuperMethod(cls)) {
112115
Procedure getTypeArguments = Procedure(
113116
Name("_typeArguments", _coreLibrary),

pkg/dart2wasm/lib/translator.dart

Lines changed: 41 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -462,43 +462,26 @@ class Translator with KernelNodes {
462462

463463
bool isFfiCompound(Class cls) => _hasSuperclass(cls, ffiCompoundClass);
464464

465-
w.StorageType typeForInfo(ClassInfo info, bool nullable,
466-
{bool ensureBoxed = false}) {
467-
Class? cls = info.cls;
468-
if (cls != null) {
469-
w.StorageType? builtin = builtinTypes[cls];
470-
if (builtin != null) {
471-
if (!nullable && (!ensureBoxed || cls == ffiPointerClass)) {
472-
return builtin;
473-
}
474-
if (isWasmType(cls)) {
475-
if (builtin.isPrimitive) throw "Wasm numeric types can't be nullable";
476-
return (builtin as w.RefType).withNullability(nullable);
477-
}
478-
if (cls == ffiPointerClass) throw "FFI types can't be nullable";
479-
Class? boxedClass = boxedClasses[builtin];
480-
if (boxedClass != null) {
481-
info = classInfo[boxedClass]!;
482-
}
483-
} else if (isFfiCompound(cls)) {
484-
if (nullable) throw "FFI types can't be nullable";
485-
return w.NumType.i32;
486-
} else if (cls == coreTypes.functionClass) {
465+
w.StorageType translateStorageType(DartType type) {
466+
bool nullable = type.isPotentiallyNullable;
467+
if (type is InterfaceType) {
468+
Class cls = type.classNode;
469+
470+
// Abstract `Function`?
471+
if (cls == coreTypes.functionClass) {
487472
return w.RefType.def(closureLayouter.closureBaseStruct,
488473
nullable: nullable);
489474
}
490-
}
491-
return w.RefType.def(info.repr.struct, nullable: nullable);
492-
}
493475

494-
w.StorageType translateStorageType(DartType type) {
495-
if (type is InterfaceType) {
496-
if (type.classNode.superclass == wasmArrayRefClass) {
476+
// Wasm array?
477+
if (cls.superclass == wasmArrayRefClass) {
497478
DartType elementType = type.typeArguments.single;
498479
return w.RefType.def(arrayTypeForDartType(elementType),
499-
nullable: false);
480+
nullable: nullable);
500481
}
501-
if (type.classNode == wasmFunctionClass) {
482+
483+
// Wasm function?
484+
if (cls == wasmFunctionClass) {
502485
DartType functionType = type.typeArguments.single;
503486
if (functionType is! FunctionType) {
504487
throw "The type argument of a WasmFunction must be a function type";
@@ -520,10 +503,31 @@ class Translator with KernelNodes {
520503
if (!voidReturn) translateType(functionType.returnType)
521504
];
522505
w.FunctionType wasmType = m.addFunctionType(inputs, outputs);
523-
return w.RefType.def(wasmType, nullable: type.isPotentiallyNullable);
506+
return w.RefType.def(wasmType, nullable: nullable);
507+
}
508+
509+
// FFI compound?
510+
if (isFfiCompound(cls)) {
511+
if (nullable) throw "FFI types can't be nullable";
512+
return w.NumType.i32;
524513
}
525-
return typeForInfo(
526-
classInfo[type.classNode]!, type.isPotentiallyNullable);
514+
515+
// Other built-in type?
516+
w.StorageType? builtin = builtinTypes[cls];
517+
if (builtin != null) {
518+
if (!nullable) {
519+
return builtin;
520+
}
521+
if (isWasmType(cls)) {
522+
if (builtin.isPrimitive) throw "Wasm numeric types can't be nullable";
523+
return (builtin as w.RefType).withNullability(nullable);
524+
}
525+
if (cls == ffiPointerClass) throw "FFI types can't be nullable";
526+
return classInfo[boxedClasses[builtin]!]!.nullableType;
527+
}
528+
529+
// Regular class.
530+
return classInfo[cls]!.repr.typeWithNullability(nullable);
527531
}
528532
if (type is DynamicType || type is VoidType) {
529533
return topInfo.nullableType;
@@ -532,15 +536,15 @@ class Translator with KernelNodes {
532536
return const w.RefType.none(nullable: true);
533537
}
534538
if (type is TypeParameterType) {
535-
return translateStorageType(type.isPotentiallyNullable
539+
return translateStorageType(nullable
536540
? type.bound.withDeclaredNullability(type.nullability)
537541
: type.bound);
538542
}
539543
if (type is IntersectionType) {
540544
return translateStorageType(type.left);
541545
}
542546
if (type is FutureOrType) {
543-
return topInfo.typeWithNullability(type.isPotentiallyNullable);
547+
return topInfo.typeWithNullability(nullable);
544548
}
545549
if (type is FunctionType) {
546550
ClosureRepresentation? representation =
@@ -552,13 +556,13 @@ class Translator with KernelNodes {
552556
representation != null
553557
? representation.closureStruct
554558
: classInfo[typeClass]!.struct,
555-
nullable: type.isPotentiallyNullable);
559+
nullable: nullable);
556560
}
557561
if (type is InlineType) {
558562
return translateStorageType(type.instantiatedRepresentationType);
559563
}
560564
if (type is RecordType) {
561-
return typeForInfo(getRecordClassInfo(type), type.isPotentiallyNullable);
565+
return getRecordClassInfo(type).typeWithNullability(nullable);
562566
}
563567
throw "Unsupported type ${type.runtimeType}";
564568
}

sdk/lib/wasm/wasm_types.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ class WasmAnyRef extends _WasmBase {
4141
///
4242
/// Will throw if the reference is not a Dart object.
4343
external Object toObject();
44+
}
4445

46+
extension ExternalizeNonNullable on WasmAnyRef {
4547
WasmExternRef externalize() => _externalizeNonNullable(this);
4648
}
4749

@@ -55,7 +57,9 @@ class WasmExternRef extends _WasmBase {
5557
// To avoid conflating the null externref with Dart's null, we provide a
5658
// special getter for the null externref.
5759
external static WasmExternRef? get nullRef;
60+
}
5861

62+
extension InternalizeNonNullable on WasmExternRef {
5963
WasmAnyRef internalize() => _internalizeNonNullable(this);
6064
}
6165

0 commit comments

Comments
 (0)