Skip to content

Commit 4efcdfe

Browse files
authored
Handle indirectly JS-exposed types in Unsubtyping (#8353)
We previously updated Unsubtyping to preserve prototype-configuring descriptors on types that are exposed to JS via JS-called functions, but we didn't handle the case where a supertype without a descriptor is exposed to JS, potentially exposing configured prototypes on its subtypes that do have descriptors. Fix this and also newly handle types exposed to JS via extern.convert_any.
1 parent 4017ddd commit 4efcdfe

File tree

2 files changed

+399
-10
lines changed

2 files changed

+399
-10
lines changed

src/passes/Unsubtyping.cpp

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,9 @@ struct TypeTree {
171171
// The index of the described and descriptor types, if they are necessary.
172172
std::optional<Index> described;
173173
std::optional<Index> descriptor;
174+
// Whether this type might flow out to JS from a JS-called function or via
175+
// extern.convert_any.
176+
bool exposedToJS = false;
174177

175178
Node(HeapType type, Index index) : type(type), parent(index) {}
176179
};
@@ -248,6 +251,19 @@ struct TypeTree {
248251
return std::nullopt;
249252
}
250253

254+
void setExposedToJS(HeapType type) {
255+
auto index = getIndex(type);
256+
nodes[index].exposedToJS = true;
257+
}
258+
259+
bool isExposedToJS(HeapType type) const {
260+
auto index = maybeGetIndex(type);
261+
if (!index) {
262+
return false;
263+
}
264+
return nodes[*index].exposedToJS;
265+
}
266+
251267
struct SupertypeIterator {
252268
using value_type = const HeapType;
253269
using difference_type = std::ptrdiff_t;
@@ -404,6 +420,9 @@ struct TypeTree {
404420
for (auto child : node.children) {
405421
std::cerr << " " << ModuleHeapType(wasm, nodes[child].type);
406422
}
423+
if (node.exposedToJS) {
424+
std::cerr << ", exposed to JS";
425+
}
407426
std::cerr << '\n';
408427
}
409428
}
@@ -595,6 +614,15 @@ struct Unsubtyping : Pass, Noter<Unsubtyping> {
595614
work.push_back({Kind::Descriptor, described, descriptor});
596615
}
597616

617+
void noteExposedToJS(HeapType type) {
618+
types.setExposedToJS(type);
619+
// Keep any descriptor that may configure a prototype.
620+
if (auto desc = type.getDescriptorType();
621+
desc && StructUtils::hasPossibleJSPrototypeField(*desc)) {
622+
noteDescriptor(type, *desc);
623+
}
624+
}
625+
598626
void analyzePublicTypes(Module& wasm) {
599627
// We cannot change supertypes for anything public.
600628
for (auto type : ModuleUtils::getPublicHeapTypes(wasm)) {
@@ -625,10 +653,7 @@ struct Unsubtyping : Pass, Noter<Unsubtyping> {
625653
if (Type::isSubType(type, anyref)) {
626654
auto heapType = type.getHeapType();
627655
noteSubtype(heapType, HeapType::any);
628-
if (auto desc = heapType.getDescriptorType();
629-
desc && StructUtils::hasPossibleJSPrototypeField(*desc)) {
630-
noteDescriptor(heapType, *desc);
631-
}
656+
noteExposedToJS(heapType);
632657
}
633658
}
634659
}
@@ -644,6 +669,9 @@ struct Unsubtyping : Pass, Noter<Unsubtyping> {
644669

645670
// Observed (described, descriptor) requirements.
646671
Set<std::pair<HeapType, HeapType>> descriptors;
672+
673+
// Observed externalized types.
674+
Set<HeapType> exposedToJS;
647675
};
648676

649677
struct Collector
@@ -739,6 +767,14 @@ struct Unsubtyping : Pass, Noter<Unsubtyping> {
739767
// expression is removed.
740768
noteDescribed(curr->type.getHeapType());
741769
}
770+
void visitRefAs(RefAs* curr) {
771+
Super::visitRefAs(curr);
772+
// extern.convert_any makes its operand type visible to JS, which may
773+
// require us to keep descriptors that configure prototypes.
774+
if (curr->op == ExternConvertAny && curr->value->type.isRef()) {
775+
info.exposedToJS.insert(curr->value->type.getHeapType());
776+
}
777+
}
742778
};
743779

744780
bool trapsNeverHappen = getPassOptions().trapsNeverHappen;
@@ -758,6 +794,8 @@ struct Unsubtyping : Pass, Noter<Unsubtyping> {
758794
info.subtypings.end());
759795
collectedInfo.descriptors.insert(info.descriptors.begin(),
760796
info.descriptors.end());
797+
collectedInfo.exposedToJS.insert(info.exposedToJS.begin(),
798+
info.exposedToJS.end());
761799
}
762800

763801
// Collect constraints from module-level code as well.
@@ -772,6 +810,9 @@ struct Unsubtyping : Pass, Noter<Unsubtyping> {
772810
}
773811

774812
// Prepare the collected information for the upcoming processing loop.
813+
for (auto type : collectedInfo.exposedToJS) {
814+
noteExposedToJS(type);
815+
}
775816
for (auto& [sub, super] : collectedInfo.subtypings) {
776817
noteSubtype(sub, super);
777818
}
@@ -820,6 +861,11 @@ struct Unsubtyping : Pass, Noter<Unsubtyping> {
820861

821862
types.setSupertype(sub, super);
822863

864+
// If the supertype is exposed to JS, the subtype potentially is as well.
865+
if (types.isExposedToJS(super)) {
866+
noteExposedToJS(sub);
867+
}
868+
823869
// Complete the descriptor squares to the left and right of the new
824870
// subtyping edge if those squares can possibly exist based on the original
825871
// types.
@@ -948,11 +994,7 @@ struct Unsubtyping : Pass, Noter<Unsubtyping> {
948994
// Add all the edges. Don't worry about duplicating existing edges because
949995
// checking whether they're necessary now would be about as expensive as
950996
// discarding them later.
951-
// TODO: We will be able to assume this once we update the descriptor
952-
// validation rules.
953-
if (HeapType::isSubType(*sub, *super)) {
954-
noteSubtype(*sub, *super);
955-
}
997+
noteSubtype(*sub, *super);
956998
noteSubtype(*subDesc, *superDesc);
957999
noteDescriptor(*super, *superDesc);
9581000
noteDescriptor(*sub, *subDesc);

0 commit comments

Comments
 (0)