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

[WIP] js! proc macro #285

Closed
wants to merge 3 commits into from
Closed

Conversation

ForsakenHarmony
Copy link
Contributor

@ForsakenHarmony ForsakenHarmony commented Sep 26, 2018

This is just the first version, looking for feedback

included 2 examples which should obviously be removed later

in

js_new!(
    return new UIEvent(
        @{ClickEvent::EVENT_TYPE},
        {
            detail: 1,
        }
    )
)

js_new! (
    return @{self}.width;
)

out

__js_raw_asm ! ( "return new UIEvent(($1),{detail:1,})" , { ClickEvent :: EVENT_TYPE } )

__js_raw_asm ! ( "return($1).width;" , { self } )

@koute
Copy link
Owner

koute commented Sep 26, 2018

Thanks!

So far this looks simpler than I though it will. (:

A few comments:

  • The macro should be in the stdweb-internal-macros crate instead of in stdweb-derive, and should be reexported like the js_export and async_test macros.
  • Can you add a test which check that the stringification (so only the first argument of the __js_raw_asm!) works as it currently does for the js! macro? (There are a few corner cases with Rust's tokenizations which produce invalid JS when translated naively, e.g. ++ which will be tokenized as + + with a naive macro_rules! macro.) You can just copy-paste the tests::stringify test from src/webcore/macros.rs.
  • We should add a new feature to stdweb's Cargo.toml (e.g. "procedural-js-macro" or something like that, doesn't matter what it's named) which would turn make the old js! macro proxy into this new one - basically gate the current js! with a #[cfg(not(feature = "procedural-js-macro"))], then add another macro_rules! js with #[cfg(feature = "procedural-js-macro")] and just pass all of the tokens into our new macro. (Or I guess maybe we don't need the passthrough js! macro? Do procedural macros automatically appear when you #[macro_use] the create containing them? I don't know.)

@ForsakenHarmony
Copy link
Contributor Author

1 and 2 done

can do 3 when the new version works

the old js! macro does more than just the __js_raw_asm! part, it also adds the result around and some other stuff, what would be the next thing to add?

@koute
Copy link
Owner

koute commented Sep 30, 2018

Thanks!

Yep. The js! macro does a little more. Here's more-or-less what's missing to make it feature complete:

  1. For every argument from @{$arg} this should be generated (where $name is some autogenerated identifier):
let $name = $arg;
let $name = ::stdweb::private::IntoNewtype::into_newtype( $name );
let mut $name = Some( $name );
let $name = ::stdweb::private::JsSerializeOwned::into_js_owned( &mut $name );
let $name = &$name as *const _;

then that $name should be passed to the __js_raw_asm!

  1. The @(no_return) should be stripped from the beginning of the code passed to js!, and depending on whenever it's present or not a slightly different code will be generated.

  2. If @(no_return) was not present the JS code should be wrapped like this:

Module.STDWEB_PRIVATE.from_js($0, (function(){$insert_code_here})());
  1. To the generated code JS the following should be prepended:
$0 = Module.STDWEB_PRIVATE.to_js($0);
$1 = Module.STDWEB_PRIVATE.to_js($1);
$2 = Module.STDWEB_PRIVATE.to_js($2);
...

This will be repeated for every argument. If @(no_return) was present this should start from $0; if it wasn't present this should start from $1. (Or in other words - if there is a return value we use $0 as a pointer to where the return value should be written to.)

  1. Finally the following Rust code should be generated:
if cfg!( test ) {
    ::stdweb::initialize();
}
    
let restore_point = ::stdweb::private::ArenaRestorePoint::new();
$insert_serialization_code_from_1

#[allow(unused_unsafe, unused_parens)]
let result = unsafe {
    let mut result: ::stdweb::private::SerializedValue = ::std::default::Default::default();
    __js_raw_asm( $js_code, (&mut result as *mut _), a1 );
    result.deserialize()
};

::std::mem::drop( restore_point );
result

If @(no_return) was specified then the unsafe block should look like this:

let result = unsafe {
    __js_raw_asm( $js_code, $arg_1, $arg_2, $arg_3, ... );
};

So, for example, this:

js!( return @{10}; );

will result in this:

if cfg!( test ) {
    ::stdweb::initialize();
}
    
let restore_point = ::stdweb::private::ArenaRestorePoint::new();
let a1 = 10;
let a1 = ::stdweb::private::IntoNewtype::into_newtype( a1 );
let mut a1 = Some( a1 );
let a1 = ::stdweb::private::JsSerializeOwned::into_js_owned( &mut a1 );
let a1 = &a1 as *const _;

#[allow(unused_unsafe, unused_parens)]
let result = unsafe {
    let mut result: ::stdweb::private::SerializedValue = ::std::default::Default::default();
    __js_raw_asm( "$1 = Module.STDWEB_PRIVATE.to_js($1);Module.STDWEB_PRIVATE.from_js($0, (function(){return $1;})());", (&mut result as *mut _), a1 );
    result.deserialize()
};

::std::mem::drop( restore_point );
result

This should (unless I've made a mistake) replicate the current js! macro. Once that works we should be able to switch to using custom sections, and maybe make this a little more efficient.

@koute koute closed this Dec 8, 2018
@koute
Copy link
Owner

koute commented Dec 8, 2018

We're finally switching to a procedural macro based js! here: #306

@ForsakenHarmony I've based the code in that PR on yours, so thanks a lot for this!

@ForsakenHarmony
Copy link
Contributor Author

ForsakenHarmony commented Dec 8, 2018

Sorry, I should have continued here

But I'm happy if you could use it in some way

@koute
Copy link
Owner

koute commented Dec 8, 2018

It's no problem! Thanks again. (:

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

Successfully merging this pull request may close these issues.

2 participants