-
Notifications
You must be signed in to change notification settings - Fork 40
[spec] JS API changes #8
Conversation
@@ -801,9 +822,37 @@ Assert: |type| is not [=𝗂𝟨𝟦=]. | |||
1. If |type| is [=𝖿𝟨𝟦=], | |||
1. Let |f64| be ? [=ToNumber=](|v|). | |||
1. Return [=𝖿𝟨𝟦.𝖼𝗈𝗇𝗌𝗍=] |f64|. | |||
1. If |type| is [=anyref=], | |||
1. If |v| is a primitive value but not a string, symbol, or null, throw |error|. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the criteria for allowing Strings here (which I think get implicitly boxed), while primitive number/boolean are disallowed although they could also be implicitly boxed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent question, which has also been asked by @gahaas. I believe it would be rather useful to allow any JS value here, but @lukewagner mentioned some reasons why that might be difficult in SpiderMonkey. I have it on my slides as an open question for this week's meeting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, my expectation here is that string promitives wouldn't autobox into String objects. So, e.g., the identity function would preserve primitive-ness.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is the implementation of ref.eq. As far as I understand it, non-primitive JS objects have an identity, so we can implement ref.eq by comparing the references to two JS objects, instead of doing a deep comparison. Primitive values, however, don't have an identity. The same number, or the same string, may be stored multiple times on the heap, in different representations. Therefore we would have to do a deep comparison for primitive values, or we would have to canonicalize primitive values in ToWebAssemblyValue to allow a reference comparison again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gahaas, ref.eq requires anyeqref
, and strings are excluded from that (for the reasons you describe). So would other primitive values be. Hence I don't think there is an issue.
@lukewagner, why would other primitive types require autoboxing either? I think you can treat any tagged pointer as a reference without problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rossberg To make sure I understand: are you talking about being able to stick, e.g., a bool into an anyref
(without autoboxing)? Also number
? Is anyref
just any
then? Is your underlying interest intref
s here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lukewagner, yes, there is no technical reason AFAICS why we cannot allow that. Simply view all JS values as reference types. Some of them may be unboxed/flattened in practice, but that is an implementation detail that we can leave entirely transparent to Wasm, because it can never tell the difference.
Intrefs will certainly be in a similar category, but here the motivation is (a) simplicity, and (b) completeness. I think you want all JS value to be able to round-trip through Wasm without having to distinguish them. That makes it much easier to implement generic JS data structures in Wasm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I certainly see the value of round-tripping any JS value (and any
in general). When it comes to the question of whether to NaN-box (esp on 32-bit), I think there might be perf benefits to having a more restricted union (not including f64
). Probably a good general CG discussion item.
@@ -781,14 +792,24 @@ Assert: |w| is not of the form [=𝗂𝟨𝟦.𝖼𝗈𝗇𝗌𝗍=] |i64|. | |||
1. If |w| is of the form [=𝗂𝟥𝟤.𝖼𝗈𝗇𝗌𝗍=] |i32|, return [=the Number value=] for [=signed_32=](|i32|). | |||
1. If |w| is of the form [=𝖿𝟥𝟤.𝖼𝗈𝗇𝗌𝗍=] |f32|, return [=the Number value=] for |f32|. | |||
1. If |w| is of the form [=𝖿𝟨𝟦.𝖼𝗈𝗇𝗌𝗍=] |f64|, return [=the Number value=] for |f64|. | |||
1. If |w| is of the form [=ref.null=], return null. | |||
1. If |w| is of the form [=ref.func=] |funcaddr|, return the result of creating [=a new Exported Function=] from |funcaddr|. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just checking my understanding here: this will not create a new function each time ToWebAssemblyValue is called, because the Exported Function creation early returns if it's been already created before? (i.e. exported function creation is a projection)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the functype
of such an Exported Function? A new kind of signature anysig
that validates every static signature check but requires a dynamic signature check instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, exported functions are cached and no funcaddr will be wrapped more than once. The type is determined by the function instance it points to.
document/js-api/index.bs
Outdated
1. If |v| has a \[[FunctionAddress]] internal slot, and therefore is an [=Exported Function=], | ||
1. Let |funcaddr| be the value of |v|'s \[[FunctionAddress]] internal slot. | ||
1. Otherwise, | ||
1. [=Create a host function=] from |v| and let |funcaddr| be the result. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How is this function's functype
defined?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, excellent catch! I think it simply won't work, since we don't have anything to deduce a type from. So we have to treat regular JS functions as merely opaque anyref objects, not as anyfunc functions that can be called in Wasm. Changed it accordingly (and discovered a bug in the JS API draft related to this).
document/js-api/index.bs
Outdated
</div> | ||
|
||
<div algorithm> | ||
The algorithm <dfn>ToWebAssemblyValue</dfn>(|v|, |type|, |error|) coerce a JavaScript value to a [=WebAssembly value=] performs the following steps: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
preexisting: there seems to be something wrong with this sentence's grammar, perhaps coerce
=> coerces
and a missing and
before performs
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
First attempt at speccing JS reference value conversions