Wasm-bindgen: js-sys: Expose bindings to ALL the global JS things

Created on 19 Jun 2018  ·  152Comments  ·  Source: rustwasm/wasm-bindgen

This is about exposing ALL of the globally available JS APIs through the js-sys crate. Things that are guaranteed by the ECMAScript standard, not Web/Node/etc APIs.

A good overview/list/documentation of these APIs is available here and I've also made a checklist below. As we implement bindings for these APIs, I will check them off.

How to Implement New Bindings

  • Comment here saying which thing you are going to make bindings for (so that we don't accidentally duplicate effort). I'll add your username next to the checkbox item.

  • Open the MDN page for the relevant JS API.

  • Open crates/js-sys/src/lib.rs in your editor; this is the file where we are implementing the bindings.

  • Follow the instructions in crates/js-sys/src/lib.rs about how to add new bindings: https://github.com/rustwasm/wasm-bindgen/blob/aa348f963ff4ae7ddd769365ad1b197cd134e24e/crates/js-sys/src/lib.rs#L28-L44

  • Add a test for the new binding to crates/js-sys/tests/wasm/MyType.rs

  • Run the JS global API bindings tests with cargo test -p js-sys --target wasm32-unknown-unknown

  • Send a pull request! :smile_cat:


Depends on this PR for the initial skeleton and infrastructure:


All String bindings depend on:


  • [x] Array

    • [x] Array.length (@robertDurst)

    • [x] Array.from()

    • [x] Array.isArray()

    • [x] Array.of()

    • [x] Array.prototype.concat()

    • [x] Array.prototype.copyWithin()

    • [x] Array.prototype.entries()

    • [x] Array.prototype.every()

    • [x] Array.prototype.fill()

    • [x] Array.prototype.filter()

    • [x] Array.prototype.find()

    • [x] Array.prototype.findIndex()

    • [x] Array.prototype.forEach()

    • [x] Array.prototype.includes()

    • [x] Array.prototype.indexOf()

    • [x] Array.prototype.join()

    • [x] Array.prototype.keys()

    • [x] Array.prototype.lastIndexOf()

    • [x] Array.prototype.map()

    • [x] Array.prototype.pop() (@sepiropht)

    • [x] Array.prototype.push()

    • [x] Array.prototype.reduce()

    • [x] Array.prototype.reduceRight()

    • [x] Array.prototype.reverse()

    • [x] Array.prototype.shift()

    • [x] Array.prototype.slice()

    • [x] Array.prototype.some()

    • [x] Array.prototype.sort()

    • [x] Array.prototype.splice()

    • [x] Array.prototype.toLocaleString()

    • [x] Array.prototype.toString()

    • [x] Array.prototype.unshift()

    • [x] Array.prototype.values()

  • [x] ArrayBuffer

    • [x] ArrayBuffer.prototype.byteLength

    • [x] ArrayBuffer.isView()

    • [x] ArrayBuffer.prototype.slice()

  • [x] Boolean

  • [x] DataView

    • [x] DataView.prototype.buffer

    • [x] DataView.prototype.byteLength

    • [x] DataView.prototype.byteOffset

    • [x] DataView.prototype.getFloat32()

    • [x] DataView.prototype.getFloat64()

    • [x] DataView.prototype.getInt16()

    • [x] DataView.prototype.getInt32()

    • [x] DataView.prototype.getInt8()

    • [x] DataView.prototype.getUint16()

    • [x] DataView.prototype.getUint32()

    • [x] DataView.prototype.getUint8()

    • [x] DataView.prototype.setFloat32()

    • [x] DataView.prototype.setFloat64()

    • [x] DataView.prototype.setInt16()

    • [x] DataView.prototype.setInt32()

    • [x] DataView.prototype.setInt8()

    • [x] DataView.prototype.setUint16()

    • [x] DataView.prototype.setUint32()

    • [x] DataView.prototype.setUint8()

  • [x] Date

    • [x] Date.UTC()

    • [x] Date.now()

    • [x] Date.parse()

    • [x] Date.prototype.getDate()

    • [x] Date.prototype.getDay()

    • [x] Date.prototype.getFullYear()

    • [x] Date.prototype.getHours()

    • [x] Date.prototype.getMilliseconds()

    • [x] Date.prototype.getMinutes()

    • [x] Date.prototype.getMonth()

    • [x] Date.prototype.getSeconds()

    • [x] Date.prototype.getTime()

    • [x] Date.prototype.getTimezoneOffset()

    • [x] Date.prototype.getUTCDate()

    • [x] Date.prototype.getUTCDay()

    • [x] Date.prototype.getUTCFullYear()

    • [x] Date.prototype.getUTCHours()

    • [x] Date.prototype.getUTCMilliseconds()

    • [x] Date.prototype.getUTCMinutes()

    • [x] Date.prototype.getUTCMonth()

    • [x] Date.prototype.getUTCSeconds()

    • [x] Date.prototype.setDate()

    • [x] Date.prototype.setFullYear()

    • [x] Date.prototype.setHours()

    • [x] Date.prototype.setMilliseconds()

    • [x] Date.prototype.setMinutes()

    • [x] Date.prototype.setMonth()

    • [x] Date.prototype.setSeconds()

    • [x] Date.prototype.setTime()

    • [x] Date.prototype.setUTCDate()

    • [x] Date.prototype.setUTCFullYear()

    • [x] Date.prototype.setUTCHours()

    • [x] Date.prototype.setUTCMilliseconds()

    • [x] Date.prototype.setUTCMinutes()

    • [x] Date.prototype.setUTCMonth()

    • [x] Date.prototype.setUTCSeconds()

    • [x] Date.prototype.toDateString()

    • [x] Date.prototype.toISOString()

    • [x] Date.prototype.toJSON()

    • [x] Date.prototype.toLocaleDateString()

    • [x] Date.prototype.toLocaleString()

    • [x] Date.prototype.toLocaleTimeString()

    • [x] Date.prototype.toString()

    • [x] Date.prototype.toTimeString()

    • [x] Date.prototype.toUTCString()

    • [x] Date.prototype.valueOf()

  • [x] Error

    • [x] Error.prototype.message

    • [x] Error.prototype.name

    • [x] Error.prototype.toString()

  • [x] EvalError

  • [x] Float32Array

  • [x] Float64Array

  • [x] Function

    • [x] Function.length

    • [x] Function.name

    • [x] Function.prototype.apply()

    • [x] Function.prototype.bind()

    • [x] Function.prototype.call()

    • [x] Function.prototype.toString()

  • [x] Generator

    • [x] Generator.prototype.next()

    • [x] Generator.prototype.return()

    • [x] Generator.prototype.throw()

  • [x] Int16Array

  • [x] Int32Array

  • [x] Int8Array

  • [x] Intl

    • [x] Intl.getCanonicalLocales()
  • [x] Intl.Collator

    • [x] Intl.Collator.prototype.compare

    • [x] Intl.Collator.prototype.resolvedOptions()

    • [x] Intl.Collator.supportedLocalesOf()

  • [x] Intl.DateTimeFormat

    • [x] Intl.DateTimeFormat.prototype.format

    • [x] Intl.DateTimeFormat.prototype.formatToParts()

    • [x] Intl.DateTimeFormat.prototype.resolvedOptions()

    • [x] Intl.DateTimeFormat.supportedLocalesOf()

  • [x] Intl.NumberFormat

    • [x] Intl.NumberFormat.prototype.format

    • [x] Intl.NumberFormat.prototype.formatToParts()

    • [x] Intl.NumberFormat.prototype.resolvedOptions()

    • [x] Intl.NumberFormat.supportedLocalesOf()

  • [x] Intl.PluralRules

    • [x] Intl.PluralRules.prototype.resolvedOptions()

    • [x] Intl.PluralRules.select()

    • [x] Intl.PluralRules.supportedLocalesOf()

  • [x] JSON

    • [x] JSON.parse()

    • [x] JSON.stringify()

  • [x] Map

    • [x] Map.prototype.size

    • [x] Map.prototype.clear()

    • [x] Map.prototype.delete()

    • [x] Map.prototype.entries()

    • [x] Map.prototype.forEach()

    • [x] Map.prototype.get()

    • [x] Map.prototype.has()

    • [x] Map.prototype.keys()

    • [x] Map.prototype.set()

    • [x] Map.prototype.values()

  • [x] Math

    • [x] Math.abs()

    • [x] Math.acos()

    • [x] Math.acosh()

    • [x] Math.asin()

    • [x] Math.asinh()

    • [x] Math.atan()

    • [x] Math.atan2()

    • [x] Math.atanh()

    • [x] Math.cbrt()

    • [x] Math.ceil()

    • [x] Math.clz32()

    • [x] Math.cos()

    • [x] Math.cosh()

    • [x] Math.exp()

    • [x] Math.expm1()

    • [x] Math.floor()

    • [x] Math.fround()

    • [x] Math.hypot()

    • [x] Math.imul()

    • [x] Math.log()

    • [x] Math.log10()

    • [x] Math.log1p()

    • [x] Math.log2()

    • [x] Math.max()

    • [x] Math.min()

    • [x] Math.pow()

    • [x] Math.random()

    • [x] Math.round()

    • [x] Math.sign()

    • [x] Math.sin()

    • [x] Math.sinh()

    • [x] Math.sqrt()

    • [x] Math.tan()

    • [x] Math.tanh()

    • [x] Math.trunc()

  • [x] Number

    • [x] Number.isFinite()

    • [x] Number.isInteger()

    • [x] Number.isNaN()

    • [x] Number.isSafeInteger()

    • [x] Number.parseFloat()

    • [x] Number.parseInt()

    • [x] Number.prototype.toExponential()

    • [x] Number.prototype.toFixed()

    • [x] Number.prototype.toLocaleString()

    • [x] Number.prototype.toPrecision()

    • [x] Number.prototype.toString()

    • [x] Number.prototype.valueOf()

  • [x] Object

    • [x] Object.prototype.constructor

    • [x] Object.assign()

    • [x] Object.create()

    • [x] Object.defineProperties()

    • [x] Object.defineProperty()

    • [x] Object.entries()

    • [x] Object.freeze()

    • [x] Object.getOwnPropertyDescriptor()

    • [x] Object.getOwnPropertyDescriptors()

    • [x] Object.getOwnPropertyNames()

    • [x] Object.getOwnPropertySymbols()

    • [x] Object.getPrototypeOf()

    • [x] Object.is()

    • [x] Object.isExtensible()

    • [x] Object.isFrozen()

    • [x] Object.isSealed()

    • [x] Object.keys()

    • [x] Object.preventExtensions()

    • [X] Object.prototype.hasOwnProperty()

    • [X] Object.prototype.isPrototypeOf() (@belfz)

    • [x] Object.prototype.propertyIsEnumerable() (@belfz )

    • [x] Object.prototype.toLocaleString()

    • [X] Object.prototype.toString() (@jonathan-s)

    • [x] Object.prototype.valueOf()

    • [x] Object.seal()

    • [x] Object.setPrototypeOf()

    • [x] Object.values()

  • [x] Promise

    • [x] Promise.all()

    • [x] Promise.prototype.catch()

    • [x] Promise.prototype.finally()

    • [x] Promise.prototype.then()

    • [x] Promise.race()

    • [x] Promise.reject()

    • [x] Promise.resolve()

  • [x] Proxy

  • [x] RangeError

  • [x] ReferenceError

  • [x] Reflect

    • [x] Reflect.apply()

    • [x] Reflect.construct()

    • [x] Reflect.defineProperty()

    • [x] Reflect.deleteProperty()

    • [x] Reflect.get()

    • [x] Reflect.getOwnPropertyDescriptor()

    • [x] Reflect.getPrototypeOf()

    • [x] Reflect.has()

    • [x] Reflect.isExtensible()

    • [x] Reflect.ownKeys()

    • [x] Reflect.preventExtensions()

    • [x] Reflect.set()

    • [x] Reflect.setPrototypeOf()

  • [x] RegExp

    • [x] RegExp.$1-$9

    • [x] RegExp.input ($_)

    • [x] RegExp.lastMatch ($&)

    • [x] RegExp.lastParen ($+)

    • [x] RegExp.leftContext ($)`

    • [x] RegExp.prototype.flags

    • [x] RegExp.prototype.global

    • [x] RegExp.prototype.ignoreCase

    • [x] RegExp.prototype.multiline

    • [x] RegExp.prototype.source

    • [x] RegExp.prototype.sticky

    • [x] RegExp.prototype.unicode

    • [x] RegExp.rightContext ($')

    • [x] regexp.lastIndex

    • [x] RegExp.prototype.exec()

    • [x] RegExp.prototype.test()

    • [x] RegExp.prototype.toString()

  • [x] Set

    • [x] Set.prototype.size

    • [x] Set.prototype.add()

    • [x] Set.prototype.clear()

    • [x] Set.prototype.delete()

    • [x] Set.prototype.entries()

    • [x] Set.prototype.forEach()

    • [x] Set.prototype.has()

    • [x] Set.prototype.values()

  • [x] String

    • [x] string.length

    • [x] String.fromCharCode()

    • [x] String.fromCodePoint()

    • [x] String.prototype.charAt()

    • [x] String.prototype.charCodeAt()

    • [x] String.prototype.codePointAt()

    • [x] String.prototype.concat()

    • [x] String.prototype.endsWith()

    • [x] String.prototype.includes()

    • [x] String.prototype.indexOf()

    • [x] String.prototype.lastIndexOf()

    • [x] String.prototype.localeCompare()

    • [x] String.prototype.match()

    • [x] String.prototype.normalize()

    • [x] String.prototype.padEnd()

    • [x] String.prototype.padStart()

    • [x] String.prototype.repeat()

    • [x] String.prototype.replace()

    • [x] String.prototype.search()

    • [x] String.prototype.slice()

    • [x] String.prototype.split()

    • [x] String.prototype.startsWith()

    • [x] String.prototype.substr()

    • [x] String.prototype.substring()

    • [x] String.prototype.toLocaleLowerCase()

    • [x] String.prototype.toLocaleUpperCase()

    • [x] String.prototype.toLowerCase()

    • [x] String.prototype.toString()

    • [x] String.prototype.toUpperCase()

    • [x] String.prototype.trim()

    • [x] String.prototype.trimEnd()

    • [x] String.prototype.trimStart()

    • [x] String.prototype.valueOf()

    • [x] String.raw()

  • [x] Symbol

    • [x] Symbol.hasInstance

    • [x] Symbol.isConcatSpreadable

    • [x] Symbol.iterator

    • [x] Symbol.match

    • [x] Symbol.replace

    • [x] Symbol.search

    • [x] Symbol.species

    • [x] Symbol.split

    • [x] Symbol.toPrimitive

    • [x] Symbol.toStringTag

    • [x] Symbol.unscopables

    • [x] Symbol.for()

    • [x] Symbol.keyFor()

    • [x] Symbol.prototype.toString()

    • [x] Symbol.prototype.valueOf()

  • [x] SyntaxError

  • [x] TypeError

  • [x] URIError

  • [x] Uint16Array

  • [x] Uint32Array

  • [x] Uint8Array

  • [x] Uint8ClampedArray

  • [x] WeakMap

    • [x] WeakMap.prototype.delete()

    • [x] WeakMap.prototype.get()

    • [x] WeakMap.prototype.has()

    • [x] WeakMap.prototype.set()

  • [x] WeakSet

    • [x] WeakSet.prototype.add()

    • [x] WeakSet.prototype.delete()

    • [x] WeakSet.prototype.has()

  • [x] WebAssembly

    • [x] WebAssembly.compile()

    • [x] WebAssembly.instantiate()

    • [x] WebAssembly.instantiateStreaming()

    • [x] WebAssembly.validate()

  • [x] WebAssembly.Module

    • [x] WebAssembly.Module.customSections()

    • [x] WebAssembly.Module.exports()

    • [x] WebAssembly.Module.imports()

  • [x] WebAssembly.Instance

    • [x] WebAssembly.Instance.prototype.exports
  • [x] WebAssembly.Memory

    • [x] WebAssembly.Memory.prototype.buffer

    • [x] WebAssembly.Memory.prototype.grow

  • [x] WebAssembly.Table

    • [x] WebAssembly.Table.prototype.length

    • [x] WebAssembly.Table.prototype.get

    • [x] WebAssembly.Table.prototype.grow

    • [x] WebAssembly.Table.prototype.set

  • [x] WebAssembly.CompileError

  • [x] WebAssembly.LinkError

  • [x] WebAssembly.RuntimeError

  • [X] decodeURI()

  • [x] decodeURIComponent()

  • [X] encodeURI()

  • [x] encodeURIComponent()

  • [x] escape()

  • [X] eval()

  • [x] isFinite()

  • [x] isNaN()

  • [x] null

  • [x] parseFloat()

  • [x] parseInt()

  • [x] undefined

  • [x] unescape()

good first issue help wanted js-sys more-types

Most helpful comment

@afdw did the bindings for String.raw and we're all done! :tada:

Three months and one day from when this issue was opened to completion. That's almost 5 bindings per day -- not bad!

HUGE thank you to everyone who helped out! We couldn't have done it without you :sparkling_heart:

All 152 comments

Would it make sense to reference (or perhaps even automatically convert?) the TypeScript definitions for these? Additional definitions are separated per ES version and available on this directory.

@coreh we are building a TypeScript frontend to wasm-bindgen (cc @spastorino) but it has a bit of a ways to go before we can rely on it. Additionally, .d.ts files don't give us information about whether a method can throw or not, so we would have to be conservative and assume that they always do, which isn't great to have at the most foundational layer of bindings.

For Web APIs, we intend to use our work-in-progress WebIDL frontend to generate a sys crate for the whole Web platform. WebIDL does give us more info, and crucially whether a method throws or not.

I would like to help! I know some Javascript and i' m currently learning rust. But i want to understand, maybe my question is silly. What is the purpose of all this bindings ?

How would multiple, optional arguments be handled for something like Array.prototype.concat? And on the subject of Array, would the type for this be Array<JsValue> ?

@sepiropht the purpose is so that we don't duplicate bindings across the ecosystem (or even within a single crate dependency graph) and people can be productive more quickly.

@wismer we don't have great support for optional arguments at the moment, so for now it is best to do one of:

  • write bindings for a different function/object
  • ignore the optional bindings
  • always require the optional bindings

different choices may make sense for different methods.

Thanks @fitzgen and @fitzgen for the quick response. But this is still too advanced for me. Maybe i should start first by reading how wasm-bingen works.

@wismer

And on the subject of Array, would the type for this be Array ?

wasm-bindgen doesn't support generics right now (supporting generics across an ABI boundary without a JIT compiler to dynamically compile monomorphizations of generic instantiations is hard) so we would only support JsValue. It would be possible to build newtypes with phantoms on top of that that only let Rust code put in instances of the expected type, and then there would have to also be dynamic checks for the JS side of things somehow.

@fitzgen ok thanks - I'm eager to get started but I've run into a hiccup with the tests and it looks like it's a propagating JS error regarding WebAssembly is not defined - I've tried npm install and the link to the contributing guidelines is broken https://rustwasm.github.io/wasm-bindgen/contributing.html, so I'm not sure what build step I may be missing.

@wismer yeah, I'm trying to fix the gh-pages deploy on travis ci right now >.<

Make sure you've got node 10 installed (there are tests that depend on BigInt, which is >= 10; WebAssembly is >= 8).

Also, the gh-pages should be fixed now!

@fitzgen is this close to what you are looking for? (Array.prototype.indexOf)

extern {
    pub type Array;

    #[wasm_bindgen(method, js_name = indexOf)]
    pub fn index_of(this: &Array, value: JsValue) -> i32;
}
// Array.rs
#[test]
fn index_of() {
    project()
        .file("src/lib.rs", r#"
            #![feature(proc_macro, wasm_custom_section)]

            extern crate wasm_bindgen;
            use wasm_bindgen::prelude::*;
            use wasm_bindgen::js;

            #[wasm_bindgen]
            pub fn get_index_of(this: &js::Array, value: JsValue) -> i32 {
                this.index_of(value)
            }

        "#)
        .file("test.ts", r#"
            import * as assert from "assert";
            import * as wasm from "./out";

            export function test() {
                let characters = ["a", "c", "x", "n"];
                let index = wasm.get_index_of(characters, "x");
                let notFoundIndex = wasm.get_index_of(characters, "z");

                assert.equal(index, 2);
                assert.equal(notFoundIndex, -1);
            }
        "#)
        .test()
}

update: I've done for Array

  • indexOf
  • fill
  • lastIndexOf
  • copyWithin
  • join

Assuming of course that I'm on the right track, though! I'm curious what you think would work for callback functions that are used in methods like find. I've read the bit about closures and the Closure type, but I'd have to implement some traits in order to get something like this to work

fn find(this: &Array, pred_fn: &Closure<&Fn(JsValue) -> bool>) -> JsValue;

but the compiler complains about unimplemented traits like RefIntoWasmAbi or something like that. Satisfying the compiler seems to go beyond the scope of this issue request, so I didn't dive any deeper. Considering how many methods utilize callbacks, I was hoping to get your 2 cents in.

Thanks!

Shouldn't the doc comments (copied from the MDN summary) be updated to use the function names on the Rust side ?

For instance the decode_uri() function doc comment use the names decodeURI and encodeURI. I think it would be better to use the Rust names.

I followed the instructions here -> https://rustwasm.github.io/wasm-bindgen/contributing.html but when running cargo test 99% of the tests fail...

Solved by #277

@UtherII

Shouldn't the doc comments (copied from the MDN summary) be updated to use the function names on the Rust side ?

For instance the decode_uri() function doc comment use the names decodeURI and encodeURI. I think it would be better to use the Rust names.

Sure! Want to send a PR updating existing docs and adding that to the comment in src/js.rs?

@wismer

@fitzgen is this close to what you are looking for? (Array.prototype.indexOf)

Exactly! Just need to also add the summary from MDN as a doc comment, as described in the instructions comment in src/js.rs.

@fitzgen oh great! Would you prefer a PR for each individual one done, or have them batched together. I updated that comment with some other questions just a moment ago so let me know what you think!

@wismer, I would say do ~one PR per day, so batch however many that happens to be together :)

Individual commits for each bindings is still preferred :) :+1:

Hello! For a good start, I'll take care of the Promise constructor and Promise.all() :)

Trying my hands on toString.

@fitzgen

Additionally, .d.ts files don't give us information about whether a method can throw or not, so we would have to be conservative and assume that they always do, which isn't great to have at the most foundational layer of bindings.

Gotcha, that's indeed a current shortcoming of the TypeScript type system, hopefully they'll add support for typing exceptions in the future.

@belfz I think you want Closure<FnMut(JsValue) -> JsValue> rather than Closure<&Fn(JsValue)>. Let me know if that works.

If this doesn't work, we might want to start with non-closure taking methods.

@fitzgen thanks, I removed my last comment because I noticed I was wrong about the shape of Promise's executor ((resolve, reject) => {...} in plain JS). I'll take your hints into account.

Ok, as far as I can tell, the definition of Promise::new should look like:

#[wasm_bindgen(constructor)]
pub fn new(executor: &Closure<FnMut(Fn(JsValue), Fn(JsValue))>) -> Promise;

It is consistent with JavaScript's

new Promise( /* executor */ function(resolve, reject) { ... } );

where resolve and reject are callable.

However, this implementation throws:

92 | #[wasm_bindgen]
   |               ^ `core::ops::Fn(JsValue) + 'static` does not have a constant size known at compile-time

The cause of this error in that particular place is vague to me.

@belfz are you on IRC? can you share your branch?

As I mentioned earlier, it might be easier to start with non-closure methods first.

Hey! To start I'm interested in Array.length

@fitzgen I'm not on IRC right now. I pushed the branch here. I can open the pull request and we can discuss it there, if you prefer.

edit: I opened the PR here.

Picking string.length

I want to do Array.prototype.pop

If #[wasm_bindgen(method, js_name = some_name)] is for instance methods, what's the attribute for static methods (like Promise.all)? I couldn't find any example so far.

We need to add support for something like #[wasm_bindgen(static_method_for = Object)] so we can do

#[wasm_bindgen]
extern {
    type Object;

    #[wasm_bindgen(static_method_for = Object)]
    pub fn keys(object: &Object) -> Array;
}

and have that turn into an impl Object block with a static method. Right now, we have js_namespace for things like console and Math, but that doesn't work for when we want to put the functions into an impl block.

That said, it should be pretty easy to extend crates/backend/src/{ast,codegen}.rs to allow this! Let me know if you want to take a crack at it, and/or want some more info.

Looks like I'm very unlucky tonight 😂 this blocks me from implementing Promise.all too.
Well... I really wanted to help!

There's lots of prototype methods that need implementing too :)

At least we learned what's possible and what is not possible now.

I'll go easy this time. Please assign me to Object.prototype.isPrototypeOf(). I am not giving up yet! 😉

Static methods (and attributes) should already have support on the codegen side of things (I implemented it for WebIDL), but it just needs to be hooked up to the macros.

there is some irc channel where we can ask questions ?

there is some irc channel where we can ask questions ?

https://github.com/rustwasm/team#1-join-our-irc-chat

I'll take Object.prototype.propertyIsEnumerable() next.

I want to pickup String.prototype.charAt() for starters and try to implement it.

@elpiel I'm afraid that String type might be tricky because of the name collision of extern type with rust's String type. But I could be wrong, so let us know!

Note that all String bindings are blocked on https://github.com/rustwasm/wasm-bindgen/issues/287 right now.

@fitzgen For the array methods that return Array Iterator, should this be treated as a separate type in the bindings (assuming it's in the same scope of Array?

like

extern {
  pub type ArrayIter;

  #[wasm_bindgen(method)]
  pub fn entries(this: &Array) -> ArrayIter;
}

@wismer yes, I believe the return value would be a new extern type ArrayIterator.

I'll continue with the Object - picking Object.prototype.toLocaleString() next.

How do I go about understanding how to fix this? Ie sometimes you need a method to accept a function.

the trait `wasm_bindgen::convert::RefFromWasmAbi` is not implemented for `std::ops::Fn(wasm_bindgen::JsValue) -> bool + 'static`

Hello again. I did try to take up on the task taking a look how can I expose Math.PI, but tbh I don't think I understand any of this. I did try to setup a extern for Math and set pub static PI: f64 = f64::consts::PI;, but it's complaining that Static constants in extern blocks can't have value (that's actually from intellij, the compiler is even more vague:

219 |     pub static PI: f64 = f64::consts::PI;
    |                        ^ expected one of `!`, `(`, `+`, `::`, `;`, or `<` here

Have you though or do you have a clue how this can work.

It isn't extern if you're defining its value ;) Basically just put a ; where the = is and then leave off the rest. That should do the trick, I think.

@ohanar @belfz Heads up, I added support for importing static methods via attributes in this PR

Once that lands, we should be able to write bindings for Promise.all, Object.freeze, etc.

That's great news, thanks @fitzgen. I'll take a look at the PR tomorrow!

In the meantime, I'll take Object.prototype.valueOf().

I'd like to start from Function.length and Function.name

I can take a crack at the math functions Math.xx

Edit: Looks like some of the Math stuff is already implemented.. I'll be able to start the rest of the Math functions this weekend.

That is awesome @autodidaddict go ahead 👍

I am again trying the Sting.prototype.charAt(). Is it me or master currently fails? I am not sure why, but I get the JsString is not defined. Nvm I found out the issue, had to specify js_class = 'String'. Maybe a good thing will be to document this at some point? PR #306 opened :smile:
Finally some contribution! :smiley_cat:
Update: And one more with questions as well to better understand the flow #307
String.prototype.startsWith(), String.prototype.indexOf() and String.prototype.substring() #310

Folks who have been running into trouble with passing closures to JS functions: I implemented Array.prototype.filter over in #314

The key to making the higher order functions like Array.prototype.filter work, is to not take values by reference in the closure. That is a little too tricky for the macros in src/{convert,describe}.rs and is also unnecessary since these things are all indices into a table under the hood anyways -- not something that is too big to efficiently pass directly.

I would like to take WeakMap.xx 🚀

Now that static methods are supported, I'll work on Object.seal().

I will take Array.isArray().

I will pick Function.prototype.apply() and Function.prototype.toString() next

I already implemented: String - length, trim, trimLeft, trimRight, trimStart and trimEnd
I will continue and update the comment about which ones I am working on.
Update: + toString and valueOf.

I will take Math.floor() and String.prototype.toLowerCase()
@brisad has made the changes for String.prototype.toLowerCase() already

I'll take Array.prototype.every() next, as an exercise with closures :).

@fitzgen I take Boolean.

@fitzgen I take Date.UTC and Date.now.

Edit: To test Date.UTC, I had to implement Number.new.

I'm going to pick WeakSet.xx.

JSON bindings are also picked. 🚀
Json is open for pick!

I'll have a go at Map.xx for starters. #342

🤚 I'm working on Array.prototype.some()

I'm also going to work on Set.xx. #343

Should probably update the instructions to remind people to npm install after checkout before attempting to run the tests.

I also get 4 tests failing from the master branch. I'm on nightly 1.28.0 on Ubuntu 18.04.

What is the pattern for dealing with a variadic function? For example, Math.hypot can take 2, 3, 4, ... n parameters

@autodidaddict I am afraid there's no pattern yet (unless I'm wrong). In case of fixed number of optional parameters (eg. a second parameter being a config object - see Object.create() as an example), I was thinking that maybe using Rust's Option type could be a good idea. For fully variadic functions (like Array.prototype.push()), which in theory can take any number of arguments, we might need a separate (iterable?) type, most likely with special macro arguments identifying the variadic function.

Update - I looked around the code (search for variadic) and it looks like the logic behind variadic has been thought of, but is not implemented yet.

Hi!
New to wasm, but I'd love to help out and learn things along the way.

I can take String.prototype.toLowerCase() and String.prototype.toUpperCase().

Could a member please update the issue checkboxes for #343 and #349?

Hi! I'm new to both Rust and wasm and would love to help out. I have a question first though:

Is there a reason why most of the static Math.* functions exposed so far take f32s as arguments when JavaScript uses 64-bit double floating-point numbers internally? Wouldn't it make more sense to use f64s everywhere since JavaScript can handle nothing else? There are also a bunch of bindings that take i32s right now when they should be able to handle floats, at least as far as I can tell.

I'll take Object.setPrototypeOf() and Object.values().

I'm working on Function.prototype.bind(), decodeURIComponent() and encodeURIComponent()

@jhenninger You are correct in that there are a couple of bindings that take i32, but should take f32 or possibly f64. (The i32 was a bug that I happened to introduce). You're welcome to fix that, otherwise I was going to get to that today :)

Taking Object.isExtensible(), Object.isFrozen(), Object.isSealed() and Object.preventExtensions() next :)

@jonathan-s I don't know whether to change them to f32 or f64, still waiting on a decision :)

@jhenninger I'm not entirely sure either, but I have a hunch that you are correct. I believe that @fitzgen will know whether it would be desirable to use f64 for the math methods rather than f32.

Keep in mind Math.imul requires integers. The JavaScript tests fail on
floats.

On Sun, Jul 1, 2018, 6:43 PM Jonathan Sundqvist notifications@github.com
wrote:

@jhenninger https://github.com/jhenninger I'm not entirely sure either,
but I have a hunch that you are correct. I believe that @fitzgen
https://github.com/fitzgen will know whether it would be desirable to
use f64 for the math methods rather than f32.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/rustwasm/wasm-bindgen/issues/275#issuecomment-401638452,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ANHJoWVcLRuDWQ0lW1p31J8v_K41uWh4ks5uCVCjgaJpZM4Usmp9
.

I'll pick up String.prototype.endsWith() and String.prototype.lastIndexOf()

@jhenninger @jonathan-s for floating point numbers, we should use f64.

  1. It is what JavaScript uses for numbers by default, so there is less conversions happening

  2. As a general rule of thumb, when in doubt, is is better to use a wider number representation.

For things that specifically take integers rather than floats, I think we should generally use i32, because JavaScript numbers can represent all of them, which isn't true for i64. I feel less strongly about this than I do for f64-for-floats.

I can take Number.isInteger(), Date.prototype.getDate(), and Array.prototype.values().

@autodidaddict Are you sure about that? The Math.imul tests pass if I change the binding to f64. If I understand correctly, the arguments to a function imported from the "JS side" will always be converted to f64, no matter what.

@fitzgen @jonathan-s I'll change all Math.* bindings that take i32s and f32s right now to f64, except for the functions that specifically take i32s

Also taking Math.pow, round, sign, sin, sinh, sqrt, tan, tanh and trunc :)

I've just started working on Symbol .

Working on all Generator methods.

I'm looking into Proxy.

Just started working on `Reflect.

Taken parseInt, parseFloat, Number.parseInt, Number.parseFloat.

I'm new to wasm and rust but I try to work on Date.prototype.getDay() and Date.prototype.getFullYear().

I take WebAssembly.validate.

@fitzgen Date.now and Date.UTC can be checked now :-).

I took ArrayBuffer::new, ::is_view and ::slice.

Right now all Math bindings return Number. Is that good practice? Or should we return plain f64s instead?

Taking Array.prototype.find.

Now, we can write bindings for getter/setter of static props, such as Math.PI .
https://github.com/rustwasm/wasm-bindgen/pull/373

I'm taking Intland its properties.

Taking Date::parse, getHours, getMilliseconds, getMinutes, getMonth, getSeconds and getTime.

I'd like to try tackling Array.prototype.forEach.

Working on isFinite(), Number.isFinite() and Number.isSafeInteger()

Taken Date.getTimezoneOffset, getUTCDate, getUTCDay, getUTCFullYear, getUTCHours, getUTCMilliseconds, getUTCMinutes, getUTCMonth and getUTCSeconds.

I'm working on DataView.

Sorry, I took Date.setDate(), setFullYear, setMilliseconds, setMinutes, setMonth, setMonth, setSeconds and setTime.

I'd like to try tackling Array.prototype.map .

Everyone who has been adding bindings to JS global APIs and is looking to step up to the next challenge level :zap::

We now have a web-sys crate that is mechanically generated from WebIDL interface definitions that we've taken from various web standards. Right now, we are opting into generating bindings for various webidl files. We need help opting into more, extending the webidl frontend, and making sure that the bindings are being generated correctly. For example, adding support for web audio, or the canvas 2D api to web-sys and whatever that requires from the WebIDL frontend.

Issues for tracking web-sys API support: https://github.com/rustwasm/wasm-bindgen/issues?q=is%3Aissue+is%3Aopen+label%3Aweb-sys

General info for contributing to web-sys: https://rustwasm.github.io/wasm-bindgen/web-sys.html

Thanks once again to everyone who has been adding bindings for all the JS things!! :smile_cat: :sparkling_heart:

I picked up Date.setUTCDate, setUTCFullYear, setUTCHours, setUTCMilliseconds, setUTCMinutes, setUTCMonth and setUTCSeconds.

I'd like to try tackling Array.prototype.reduce and Array.prototype.reduceRight.

I picked up Array.prototype.findIndex, and Array.prototype.to_locale_string.

I can try binding String.prototype.lastIndexOf() :smile_cat:

EDIT: I can also take care of String.prototype.padEnd and String.prototype.padStart today

I can try tackling Map.prototype.forEach() and Set.prototype.forEach() today :)

I just picked up String.prototype.repeat(), but it had already been added! Sorry for the duplication:persevere:

Folks who have been active in this thread may have an opinion to share here: https://github.com/rustwasm/wasm-bindgen/issues/519

I'm working on String.prototype.toLocaleLowerCase() and String.prototype.toLocaleUpperCase() thanks to the support of optional arguments: #507

Just finishing up Number.isNaN().

I'll take Object.is() next.

Finished Math.hypot().

Finished Math.min() and Math.max().

Surprised no one has taken on RegExp yet -- anyone want to give it a try?

@fitzgen I can take it :)

I'll take Array.prototype.splice()

I was about to try adding Object.freeze, but looks like it was added in 01ff04d8 and should be checked off.

How should the names of the RegExp.$1-$9 properties be encoded? And how do we refer to the JS names? js_name = $1 won't parse.

@brisad a good question! For that we should probably support js_name = "$1" in addition to js_name = foo, want to open an issue for that?

Once we've got that mapping then I think picking any reasonable name for them in Rust should be fine.

@alexcrichton Sure thing! I'll open an issue.

@fitzgen Array.prototype.splice has been done https://github.com/rustwasm/wasm-bindgen/pull/571

We are super close to finishing here! Let's get js-sys across the finish line, there isn't that much left.

Thanks to everyone involved thus far :)

Here is a summary of our status at the moment / what is still left:

  • I think we can largely ignore the various constant properties on Math, and Infininity and NaN since Rust has equivalent constants and I can't see a use case for using the bindings versions

  • We have a few String methods still left that should be pretty easy to add. Some are instance methods and some are static methods.

  • There are a bunch of the missing types from the Intl namespace, as well as all their methods and properties. This is one of the larger missing pieces. I just filled out Intl.Collator (https://github.com/rustwasm/wasm-bindgen/pull/685/commits/f0444d1614082deed18c651b91378d5d695c9a4c) so it should be easier to see how to do types within a namespace now, if anyone was previously unsure how that worked.

  • Another missing bit is all the WebAssembly APIs -- I would have thought that would be one of the first to get implemented! :-P I think we will need to turn WebAssembly into a module because it is a namespace, not a type, similar to what I did for Intl (https://github.com/rustwasm/wasm-bindgen/pull/685/commits/7f5d0a215810949665db8bdee2398f9507a83e91). This will allow us to define the WebAssembly.Table etc types.

  • All the various symbol methods: I think we can skip them, at least for now. Their use is pretty rare and we would need to extend the proc-macro frontend a bit to allow describing those APIs.

  • TypedArray. We've added all the methods to the derived classes of TypedArray rather than to it itself, and I think this makes sense since they are specialized to the derived class's associated scalar type. So I think we should just add this type without any methods or properties or anything, just for the extends = ... chain to be correct.

  • We need to add extends = ... for all the imported types (except Symbol). That has its own meta issue, if you haven't seen it: https://github.com/rustwasm/wasm-bindgen/issues/670

@fitzgen I’ll try whichever String methods you think are easy :P

Great! Let me know if you have any questions!

(See the unchecked checkboxes for String at the top of this issue; I think they are all good candidates!)

I would give the Intl.DateTimeFormat stuff a try :)

Given my experience with that, I will also do the remaining Intl bindings!

I’ll take the rest of String 🙂

I'm working on WebAssembly, starting with the top level module.

Hey @quelledanielle - how goes String? I found a use for String.localeCompare in a personal project, so looking forward to a release with that one included.

@twilco I'm planning on working through them tomorrow so I'll do that one first :)

~I've been getting this error (yesterday and today) on master~ 🤔

Running `/Users/danielle/Projects/wasm-bindgen/target/debug/wasm-bindgen-test-runner /Users/danielle/Projects/wasm-bindgen/target/wasm32-unknown-unknown/debug/deps/wasm-0940e2d19a2ae6fa.wasm`
TypeError: Cannot read property 'prototype' of undefined
    at Object.<anonymous> (/Users/danielle/Projects/wasm-bindgen/target/wasm32-unknown-unknown/wbg-tmp/wasm-bindgen-test.js:712:72)
    at Module._compile (module.js:662:30)
    at Object.Module._extensions..js (module.js:673:10)
    at Module.load (module.js:575:32)
    at tryModuleLoad (module.js:515:12)
    at Function.Module._load (module.js:507:3)
    at Module.require (module.js:606:17)
    at require (internal/module.js:11:18)
    at main (/Users/danielle/Projects/wasm-bindgen/target/wasm32-unknown-unknown/wbg-tmp/run.js:30:29)
    at Object.<anonymous> (/Users/danielle/Projects/wasm-bindgen/target/wasm32-unknown-unknown/wbg-tmp/run.js:365:9)

Edit: had to update node version

Are you using a Node version > 10? I can recommend using nvm which is
short for node version manager. Allows you to easily install and switch
between node versions!

Sorry, I replied via E-Mail and didn't see your edit.

On Sun, 19 Aug 2018, 02:23 Danielle Pham, notifications@github.com wrote:

I've been getting this error (yesterday and today) on master 🤔

Running /Users/danielle/Projects/wasm-bindgen/target/debug/wasm-bindgen-test-runner /Users/danielle/Projects/wasm-bindgen/target/wasm32-unknown-unknown/debug/deps/wasm-0940e2d19a2ae6fa.wasm
TypeError: Cannot read property 'prototype' of undefined
at Object. (/Users/danielle/Projects/wasm-bindgen/target/wasm32-unknown-unknown/wbg-tmp/wasm-bindgen-test.js:712:72)
at Module._compile (module.js:662:30)
at Object.Module._extensions..js (module.js:673:10)
at Module.load (module.js:575:32)
at tryModuleLoad (module.js:515:12)
at Function.Module._load (module.js:507:3)
at Module.require (module.js:606:17)
at require (internal/module.js:11:18)
at main (/Users/danielle/Projects/wasm-bindgen/target/wasm32-unknown-unknown/wbg-tmp/run.js:30:29)
at Object. (/Users/danielle/Projects/wasm-bindgen/target/wasm32-unknown-unknown/wbg-tmp/run.js:365:9)


You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/rustwasm/wasm-bindgen/issues/275#issuecomment-414069614,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AFO3NaOf9wZr-ya6msF-jqL4pakwNf9sks5uSD9pgaJpZM4Usmp9
.

@thomaseizinger I am using nvm, but assumed I qualified as "most users" 😂 Thanks anyway!

I finished the WebAssembly bindings in https://github.com/rustwasm/wasm-bindgen/pull/795, so we are down to only eight static methods of Object left -- that's it! Anyone want to claim any of them? ;)

@fitzgen I can start off with the first two: Object.defineProperties() and Object.defineProperty()

Great!

I can take the remaining six too :-)

Last four up for review! :-)

Thanks to the awesome work of @brisad, all that is left is String.raw :)

@afdw did the bindings for String.raw and we're all done! :tada:

Three months and one day from when this issue was opened to completion. That's almost 5 bindings per day -- not bad!

HUGE thank you to everyone who helped out! We couldn't have done it without you :sparkling_heart:

Sorry if this is not the right place to ask, but I am trying to use Intl::DateTimeFormat which seems to be covered by this ticket, and I can't get it work.
Could someone help me with that, I've asked about it on StackOverflow.

Thanks.

@renatoathaydes You have to do this:

let options = Intl::DateTimeFormat::new(&Array::new(), &Object::new()).resolved_options();
let tz = Reflect::get(&options, &JsValue::from("timeZone")).unwrap();

@Pauan thanks, that really helps. I think nothing can be done about having to use Reflect as the MDN docs regarding the returned object are pretty vague, but shouldn't there be a default constructor without arguments in DateTimeFormat?

shouldn't there be a default constructor without arguments in DateTimeFormat?

That sounds reasonable to me, but that would be a breaking change.

Maybe Default can be implemented for DateTimeFormat?
cc @renatoathaydes @Pauan

Was this page helpful?
0 / 5 - 0 ratings

Related issues

arilotter picture arilotter  ·  3Comments

MarcAntoine-Arnaud picture MarcAntoine-Arnaud  ·  3Comments

fitzgen picture fitzgen  ·  3Comments

hunterlester picture hunterlester  ·  4Comments

fitzgen picture fitzgen  ·  3Comments