Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow i64 values to be passed #35

Closed
xtuc opened this issue Feb 20, 2018 · 28 comments

Comments

Projects
None yet
5 participants
@xtuc
Copy link
Member

commented Feb 20, 2018

I mentioned here WebAssembly/design#1172 that we will be able to use the BigInt proposal to represent the i64 values from WebAssembly.

Currently there is not support for BigInt in browsers (nor in Babel). So I believe we can use this kind of tools to allow it.

We use a 64 bit two's-complement (https://github.com/dcodeIO/long.js).

Have you considered to implement it?

Edit:
I just remembered that WebAssembly doesn't allow multiple results for a func, I'm not sure what's the best solution, maybe the linear memory?

When I said "we use" I meant in https://github.com/xtuc/webassemblyjs which is not the same use-case.

@alexcrichton

This comment has been minimized.

Copy link
Collaborator

commented Feb 20, 2018

Seems plausible to me! I initially hesitated in exposing this because I wasn't sure what types these would map to on the JS side of things, but maybe long.js is standard enough that "pulling in a dependency to do this" isn't so bad?

@xtuc

This comment has been minimized.

Copy link
Member Author

commented Feb 20, 2018

Long.js doesn't everything we need and is fast (uses WebAssembly when possible), it's ok to pull in that dependency IMO.

what types these would map to on the JS side

Well i64 can not be represented as a primitive in JavaScript, you'll need to use an object (like Long.js does).

It will also hide the implementation (long.js or bigint).

@alexcrichton

This comment has been minimized.

Copy link
Collaborator

commented Feb 21, 2018

Oh how would the implementation be hidden? Wouldn't JS want to pass values in (aka construct them) and also read the results?

@xtuc

This comment has been minimized.

Copy link
Member Author

commented Feb 21, 2018

My point is that we can't represent a i64 value is JS so you couldn't pass it in the constructor, you could either as string or as a two's complement int.

To be more accurate, we need an abstraction to represent the value. For example:

i64 =
| BigInt if supported
| {i32, i32}

Apart with BigInt all built-in operations won't work (+, -, etc), but that's probably worth having the value at all. Note that Long.js already provides some methods to do the operations.

Depending how far you want to go but we have a PR against Babel to implement BigInt, we could re-use that here to "fake" the built-in operations (until BigInt is supported).

Does that make sense to you?

@alexcrichton

This comment has been minimized.

Copy link
Collaborator

commented Feb 24, 2018

With WebAssembly/design#1186 being created pretty recently, I wonder if it'd be best to hold off on a long.js polyfill and wait for BigInt?

@xtuc

This comment has been minimized.

Copy link
Member Author

commented Feb 26, 2018

That's a good question. BigInt is stage-3 so we can't assume that browser have it available until years (probably).

I think it could act as a good experimentation and make i64 available until BigInt is there, what do you think?

@alexcrichton

This comment has been minimized.

Copy link
Collaborator

commented Feb 26, 2018

Ah ok yeah, if it's that far off then doing the polyfill of long.js for now seems reasonable to me!

@xtuc

This comment has been minimized.

Copy link
Member Author

commented Feb 27, 2018

I think we could mutualize some work here. I would like to use the same mechanism in multiple projects, what do you think if I publish a @webassemblyjs/i64 (or something like that) package that does this abstraction?

The logic would be

i64 =
| BigInt if supported
| Long.js
@alexcrichton

This comment has been minimized.

Copy link
Collaborator

commented Feb 27, 2018

Hm I think unfortunately I'm not familiar enough with idiomatic JS really to make a judgement call here. I'd naively expect that a custom package for this wouldn't be idiomatic, but I also don't know the idioms so I'm not sure.

@xtuc

This comment has been minimized.

Copy link
Member Author

commented Feb 27, 2018

I'm not sure to fully understand your concern.

The package is just here to provide an abstraction, with the same API than BigInt. Ideally for user it's transparent.

@RReverser

This comment has been minimized.

Copy link
Member

commented Feb 27, 2018

I'd naively expect that a custom package for this wouldn't be idiomatic, but I also don't know the idioms so I'm not sure.

I'd agree with @alexcrichton, automatically pulling an external dependency is a concerning decision since now you need to be either responsible for keeping it up-to-date or forcing user to do it, either way keeping code locked to such dependency.

On the other hand, there are no really existing idioms for 64-bit integers in JS yet because they just weren't part of it. Most common was to either pass them as string or loosely convert to floats (then you preserve precision up to 53 bits), but each way has its own pitfalls.

So my suggestion would be to go the way each new API in JS gets adopted: generate calls to upcoming native BigInt API, and so expect it to either be already in the environment or that users polyfill it themselves by window.BigInt = (whatever compatible implementation they prefer).

@xtuc

This comment has been minimized.

Copy link
Member Author

commented Feb 27, 2018

Most common was to either pass them as string or loosely convert to floats

I would prefer the two's complement because:

  • the arithmetic on the string representation is super slow, they are implementable using a carry and by parsing only a bunch of number at a time (which is generally slow).
  • JavaScript Number or 64 bits floats can't represent numbers > 2^53, as you mentioned.

Some operations are tricky to implement on two i32 but hopefully for us Long.js already done that.

polyfill

I like that idea, we could provide a polyfill (using long.js or whatever), checking first for typeof window.BigInt === "undefined" and the bindings would just return an instance of window.BigInt. It's also future proof.

The package is just here to provide an abstraction, with the same API than BigInt. Ideally for user it's transparent.

I might have misworded that. My idea is that we wouldn't expose/impose anything to the user, the user just gets an object with the BigInt API (backend by BigInt or not).

The package simplifies the creation of that object, just like a factory.

Or the package would contain the polyfill of BigInt (backend by BigInt or not).

Does that make sense to you?

@RReverser

This comment has been minimized.

Copy link
Member

commented Feb 27, 2018

I might have misworded that. My idea is that we wouldn't expose/impose anything to the user, the user just gets an object with the BigInt API (backend by BigInt or not).

Oh ok. It did initially sound like you want to include specifically long.js together with library.

So we agree that wasm-bindgen can just use new BigInt and user of the library will be responsible for ensuring that it's polyfilled when needed using whatever implementation they like?

@alexcrichton

This comment has been minimized.

Copy link
Collaborator

commented Feb 27, 2018

Delegating to proposed BigInt APIs and relying on an external polyfill sounds like a great idea to me!

@xtuc

This comment has been minimized.

Copy link
Member Author

commented Feb 27, 2018

That sounds good but unless we're adding a transpilation phase we won't be able to use the built-in operators (+, -, /...) where BigInt will.

@RReverser

This comment has been minimized.

Copy link
Member

commented Feb 27, 2018

@xtuc Why would we need to use these operators in bindings code? We only need to construct BigInt instances, and it's up to the user of bindings how to use them, whether to transpile their code etc.

@xtuc

This comment has been minimized.

Copy link
Member Author

commented Feb 28, 2018

For example:

someBindingToWasm.exports.getMaxI64() - 1

should work according to the BigInt specification, but the polyfill won't cover that case.

I mentioned transpilation because that could be a possible solution.

@RReverser

This comment has been minimized.

Copy link
Member

commented Feb 28, 2018

But, again, what you posted would be the user code, not the generated one, no? So it's outside of wasm-bindgen's responsibility in any way and it's up to the user to choose how they want to work with BigInts.

@xtuc

This comment has been minimized.

Copy link
Member Author

commented Feb 28, 2018

I agree, but my point is that if you want to add two numbers:

  • BigInt uses the built-in +
  • Long.js uses a add method (or something similar)

As a user you can't use the same code.

@RReverser

This comment has been minimized.

Copy link
Member

commented Mar 1, 2018

I understand what you're saying but I don't understand how it's related or how it affects wasm-bindgen :)

@RReverser

This comment has been minimized.

Copy link
Member

commented Mar 1, 2018

I mean, as long as we just use new BigInt on JS side, difference in operations affects only the end user, and it's up to them to make sure they have needed polyfills, transpiler plugins etc. for their own code - nothing on wasm-bindgen side or in generated bindings will change based on their decision, they will continue to work nevertheless.

@xtuc

This comment has been minimized.

Copy link
Member Author

commented Mar 1, 2018

I don't understand how it's related or how it affects wasm-bindgen

Ok, I get it now, you're right!

@jsheard

This comment has been minimized.

Copy link
Contributor

commented May 2, 2018

FYI: Native BigInt is now available in Chrome Beta/Canary, with intent to ship in Chrome 67 stable.

https://developers.google.com/web/updates/2018/05/bigint

Viewing wasm memory as a BigInt64Array/BigUint64Array should make i64/u64 interop pretty easy.

@alexcrichton

This comment has been minimized.

Copy link
Collaborator

commented May 5, 2018

I've sent a PR to take advantage of the new support in Chrome beta at #188

@tsing80

This comment has been minimized.

Copy link

commented May 17, 2018

is there any polyfill available to work with existing browser?

@alexcrichton

This comment has been minimized.

Copy link
Collaborator

commented May 17, 2018

Unfortunately I'm not currently aware of one myself

@xtuc

This comment has been minimized.

Copy link
Member Author

commented May 17, 2018

Not in Babel. It requires changing somehow the built-in operations.

@xtuc

This comment has been minimized.

Copy link
Member Author

commented Dec 20, 2018

We are working on the conversion between i64 and JS's BigInt; instead of the current polyfilling strategy it could be exposed natively. However, in order to move the proposal forward (WebAssembly/JS-BigInt-integration#15) we would need some toolchain support. I think it would be great to implement something here, behind an experimental flag.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.