Author of stdweb and cargo-web here. AMA.
Could you also elaborate on some of the implementation details of stdweb?
As it was already mentioned, stdweb
mostly uses emscripten_asm_const_int
, etc. to embed JS code into Rust, and it has a fairly extensive machinery to marshal data between Rust and JS.
Glossing over some implementation details the js!
macro goes through the tokens it was passed twice - in the first pass it stringifies everything, so
js!{ alert("Hello world!" + @{123}); }
becomes (minus some extra whitespace):
const CODE_STR: &'static str = "alert(\"Hello world\" + $0);\0"`;
const CODE: *const u8 = CODE_STR as *const _ as *const u8;
In the second pass it looks for any @{$expr}
where $expr
is a Rust expression, and automatically serializes them. Something like this is emitted (I'm simplifying so not exactly, but it's just so that you get the idea of how it works):
let a0 = $expr;
let a0 = serialize(a0, arena);
let a0 = &a0 as *const _;
Then it finally calls into emscripten:
emscripten_asm_const_int(CODE, a0);
(Not shown here is handling of a return value, and all of the other machinery necessary for serialization.)
Are there various features we need to add to rustc to get things to work? Maybe work nicer and/or less brittle than they may be right no? I’m not even sure how a js! macro would work!
@alexcrichton Some of the features off the top of my head that I would like the compiler and/or cargo to have, in no particular order:
- Fixing cargo #2508 or any of the related issues. (Basically add an easy way to get the path to whatever
cargo
generated.) Yes, I know I can use --message-format=json
, and no - I don't want to. I just want to run cargo
, pipe all of the messages back to the user, grab the path to whatever file was generated and do something with it.
- Stable procedural macros. Currently the
js!
macro is one, big, ugly hack, with ridiculously long compile times.
- Procedural macros which do not tokenize the input and pass the input string exactly as it appears in the source code, e.g. the procedural macro is passed
code: &str, filename: &str, line: u32, column: u32
. Then we can use a full JavaScript parser on code
, and easily report any errors since we know the first character from code
is at filename
:line
:column
.
- The ability to use a custom test harness, or even a predefined harness which would support asynchronous code. Currently (AFAIK, please correct me if I'm wrong) it's not possible to test anything which is asynchronous. In native code this isn't really a problem since you can just block until the thing you're testing finishes, but you can't really do that here. For example, what I'd like is to be able to do something like this (to test setTimeout):
#[test]
fn test_set_timeout(done: Box<FnOnce(Result<(), Box<Error>>)>) {
stdweb::set_timeout(|| { done(Ok()); }, 1);
}
- Have
cargo
install Emscripten automatically so that I don't have to. I have some bastardized Docker build scripts to build Emscripten for Linux, but it's a pain to maintain and since I don't have the infrastructure I'm unable to provide packages for all of the platforms.
- A way to easily mark a function to be exported to JS, and a way to process such functions at compile time so that the marshaling machinery can be automatically generated for them. (I haven't thought about this too much yet, so I'm not really sure yet how exactly it would work; just throwing it out there. This could probably be done with procedural macros, and a tiny bit of supporting code in the compiler which would export the symbol to the JS world.)