Skip to content
This repository has been archived by the owner on Nov 3, 2021. It is now read-only.

[spec] JS API changes #8

Merged
merged 3 commits into from
Apr 12, 2018
Merged

[spec] JS API changes #8

merged 3 commits into from
Apr 12, 2018

Conversation

rossberg
Copy link
Member

@rossberg rossberg commented Apr 7, 2018

First attempt at speccing JS reference value conversions

@rossberg rossberg requested a review from lukewagner April 7, 2018 17:56
@@ -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|.
Copy link
Member

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?

Copy link
Member Author

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.

Copy link
Member

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.

Copy link
Contributor

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.

Copy link
Member Author

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.

Copy link
Member

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 intrefs here?

Copy link
Member Author

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.

Copy link
Member

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|.
Copy link
Member

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)

Copy link
Member

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?

Copy link
Member Author

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.

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.
Copy link
Member

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?

Copy link
Member Author

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).

</div>

<div algorithm>
The algorithm <dfn>ToWebAssemblyValue</dfn>(|v|, |type|, |error|) coerce a JavaScript value to a [=WebAssembly value=] performs the following steps:
Copy link
Member

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?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants