Improved string enum bindings #4137
Merged
+104
−63
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Changes summary:
Option<StringEnum>
to be justStringEnum
and notStringEnum | undefined
.Option<StringEnum>
WASM -> JS conversion.1. Fixed outgoing
Option<StringEnum>
TS typeI noticed that the generated TS code of
was
The TS return type should have been
StringEnum | undefined
.The fix was very simple. Someone just forgot to call
.option()
when convertingDescriptor
toAdapterType
.Note that this was a pure TS type bug. The actual JS glue code was correct. Just the type annotations were wrong.
2. Improved WASM -> JS conversion for outgoing
Option<StringEnum>
.The improvement is that the same conversion code is used for outgoing
StringEnum
andOption<StringEnum>
.The current WASM -> JS conversion for
StringEnum
uses the fact that the value of a variant of string enum is its index. This makes the conversion from WASM to JS as simple as an array lookup: e.g.["a","b","c"][someIndex]
. Note that this approach implicitly maps the__Invalid
variant toundefined
(since OOB array accesses in JS yieldundefined
). Since the user (likely) isn't supposed to return the invalid variant anyway, this doesn't matter. However, we can use this behavior to implicitly handleOption<StringEnum>
. Since the hole used to representNone
will an OOB index, the same code["a","b","c"][someIndex]
will correctly returnundefined
forNone
. So we can use the same conversion code for outgoingStringEnum
andOption<StringEnum>
.3. Fixed some edge cases for the JS -> WASM conversion of incoming
StringEnum
The old JS -> WASM conversion code for
StringEnum
did not map certain strings to the__Invalid
variant correctly.This is the old code:
{"a":0,"b":1,"c":2}[someEnumVal] ?? 3
. It used an object like a map. And that's the problem. JS objects have 2 types of fields: fields directly on the objects and fields inherited through their prototype.{"a":0,"b":1,"c":2}
defines the fields a,b,c on the object, but the object has more fields from the prototype, e.g.toString
and__proto__
:To fix these edge cases, I used
Array#indexOf
like this:(["a","b","c"].indexOf(someEnumVal) + 1 || invalid+1) - 1
. This is a bit more tricky, so here is how it works:["a","b","c"].indexOf(someEnumVal)
will return the index of validStringEnum
variants and -1 for everything invalid.+1
to make itindex+1
for all valid variants and 0 for invalid.|| invalid + 1
will then map 0 (which is falsey) toinvalid + 1
.-1
will bring down valid variants fromindex+1
toindex
and invalid values frominvalid+1
toinvalid
.Performance: Let me start by saying that I haven't benchmarked anything. My goal was correctness. Obviously,
indexOf
isO(n)
, but I believe that this doesn't matter, because creating the array (new approach) or object (old approach) is alsoO(n)
. Asymptotic runtime complexity isn't very useful here anyway, since most string enum will have <16 variants (like most enums). If improved performance is wanted, I would suggest to start with outlining the object/array to avoid creating a temporary object for each conversion.(In general, I think it would be a good idea to outline the array.)
Let me know if further changes to the PR are necessary/wanted.