Skip to content

Commit 0052faf

Browse files
committed
Handle indirectly JS-exposed types in Unsubtyping
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 fd5e86e commit 0052faf

File tree

2 files changed

+392
-10
lines changed

2 files changed

+392
-10
lines changed

src/passes/Unsubtyping.cpp

Lines changed: 49 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,12 @@ 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+
if (curr->op == ExternConvertAny && curr->value->type.isRef()) {
773+
info.exposedToJS.insert(curr->value->type.getHeapType());
774+
}
775+
}
742776
};
743777

744778
bool trapsNeverHappen = getPassOptions().trapsNeverHappen;
@@ -758,6 +792,8 @@ struct Unsubtyping : Pass, Noter<Unsubtyping> {
758792
info.subtypings.end());
759793
collectedInfo.descriptors.insert(info.descriptors.begin(),
760794
info.descriptors.end());
795+
collectedInfo.exposedToJS.insert(info.exposedToJS.begin(),
796+
info.exposedToJS.end());
761797
}
762798

763799
// Collect constraints from module-level code as well.
@@ -772,6 +808,9 @@ struct Unsubtyping : Pass, Noter<Unsubtyping> {
772808
}
773809

774810
// Prepare the collected information for the upcoming processing loop.
811+
for (auto type : collectedInfo.exposedToJS) {
812+
noteExposedToJS(type);
813+
}
775814
for (auto& [sub, super] : collectedInfo.subtypings) {
776815
noteSubtype(sub, super);
777816
}
@@ -820,6 +859,11 @@ struct Unsubtyping : Pass, Noter<Unsubtyping> {
820859

821860
types.setSupertype(sub, super);
822861

862+
// If the supertype is exposed to JS, the subtype potentially is as well.
863+
if (types.isExposedToJS(super)) {
864+
noteExposedToJS(sub);
865+
}
866+
823867
// Complete the descriptor squares to the left and right of the new
824868
// subtyping edge if those squares can possibly exist based on the original
825869
// types.
@@ -948,11 +992,7 @@ struct Unsubtyping : Pass, Noter<Unsubtyping> {
948992
// Add all the edges. Don't worry about duplicating existing edges because
949993
// checking whether they're necessary now would be about as expensive as
950994
// 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-
}
995+
noteSubtype(*sub, *super);
956996
noteSubtype(*subDesc, *superDesc);
957997
noteDescriptor(*super, *superDesc);
958998
noteDescriptor(*sub, *subDesc);

0 commit comments

Comments
 (0)