erksch
erksch
DDeno
Created by erksch on 9/23/2024 in #help
RustyV8: Example of using PromiseResolver / async?
but the External and the Function is created on a scope that is destroyed
16 replies
DDeno
Created by erksch on 9/23/2024 in #help
RustyV8: Example of using PromiseResolver / async?
Oh sorry, yes of course, the promise is the result 🙂
16 replies
DDeno
Created by erksch on 9/23/2024 in #help
RustyV8: Example of using PromiseResolver / async?
@bartlomieju Oh, very nice, that makes sense. Thanks a lot!
let data = PromiseThenCallbackData {
sender: received.1,
promise: v8::Global::new(scope, promise),
};
let data = PromiseThenCallbackData {
sender: received.1,
promise: v8::Global::new(scope, promise),
};
I was thinking that the result of the js call would be saved to the scope, which would be released before the callback is executed, so that I would need to run it in a special callbackscope that is then destroyed with the callback, but apparently this works.
16 replies
DDeno
Created by erksch on 9/23/2024 in #help
RustyV8: Example of using PromiseResolver / async?
The callback looks like this:
fn promise_then_callback(
scope: &mut HandleScope<'_>,
args: v8::FunctionCallbackArguments<'_>,
_: v8::ReturnValue<'_>,
)
{
let cvoid = args.data().cast::<v8::External>().value();
let data = helper::cvoid_to_type::<PromiseThenCallbackData>(cvoid);
let data = unsafe { Box::from_raw(data) };

match data.promise.state() {
PromiseState::Pending => {
exit(1); //TODO
}
PromiseState::Fulfilled => {
let result = data.promise.result(scope);
let number: f64 = v8::Local::<v8::Number>::try_from(result)
.ok()
.unwrap()
.value();
data.sender.send(Ok(Box::new(number))).unwrap();
}
PromiseState::Rejected => {
exit(1); //TODO
}
}
fn promise_then_callback(
scope: &mut HandleScope<'_>,
args: v8::FunctionCallbackArguments<'_>,
_: v8::ReturnValue<'_>,
)
{
let cvoid = args.data().cast::<v8::External>().value();
let data = helper::cvoid_to_type::<PromiseThenCallbackData>(cvoid);
let data = unsafe { Box::from_raw(data) };

match data.promise.state() {
PromiseState::Pending => {
exit(1); //TODO
}
PromiseState::Fulfilled => {
let result = data.promise.result(scope);
let number: f64 = v8::Local::<v8::Number>::try_from(result)
.ok()
.unwrap()
.value();
data.sender.send(Ok(Box::new(number))).unwrap();
}
PromiseState::Rejected => {
exit(1); //TODO
}
}
16 replies
DDeno
Created by erksch on 9/23/2024 in #help
RustyV8: Example of using PromiseResolver / async?
@bartlomieju When I call a JS function that returns a promise, polling if the promise has finished is expensive. So I implemented a callback on .then, but it needs both the promise and the oneshot sender, so I'm embedding it as data:
let promise = js_test_async.call(scope, global.into(), &[]).unwrap().cast::<v8::Promise>();
let data = PromiseThenCallbackData {
sender: x.1,
promise,
};
let data_box: *mut T = Box::leak(Box::new(typed_data));
let cvoid = data_box as *mut c_void;
let data_external = v8::External::new(scope, cvoid);
let f = v8::FunctionBuilder::<v8::Function>::new(promise_then_callback)
.data(data_external.into())
.build(scope)
.unwrap();
promise.then(scope, f);
let promise = js_test_async.call(scope, global.into(), &[]).unwrap().cast::<v8::Promise>();
let data = PromiseThenCallbackData {
sender: x.1,
promise,
};
let data_box: *mut T = Box::leak(Box::new(typed_data));
let cvoid = data_box as *mut c_void;
let data_external = v8::External::new(scope, cvoid);
let f = v8::FunctionBuilder::<v8::Function>::new(promise_then_callback)
.data(data_external.into())
.build(scope)
.unwrap();
promise.then(scope, f);
Unfortunately this either causes a 1) use-after-free, if I create a short-lived scope (callback outlives it) 2) memory leak I tried to use CallbackScope but that isnt accepted as a scope arg. What's the right approach? Basically I want all of the above to get dropped only when the callback has finished.
16 replies
DDeno
Created by erksch on 9/23/2024 in #help
RustyV8: Example of using PromiseResolver / async?
@bartlomieju I managed to get it all to work, but it was quite a fight 🫠
I did manage to implement bidirectional async without MaskFutureAsSend, so that's something 🥂 Can I get back to you if I face another blocking issue? I realize this is a Deno support forum and not really for RustyV8 🙂
16 replies
DDeno
Created by erksch on 9/22/2024 in #help
Embedding RustyV8: Fatal JavaScript out of memory: Reached heap limit
Indeed that solved the issue.
c.bench_function("v8 json deserialize", |b| {
b.iter(|| {
let scope = &mut HandleScope::new(scope);
let json_str = r#"{"key": "value", "number": 42}"#;
let json_v8_str = v8::String::new(scope, json_str).unwrap();
let _json_obj = v8::json::parse(scope, json_v8_str).unwrap();
})
});
c.bench_function("v8 json deserialize", |b| {
b.iter(|| {
let scope = &mut HandleScope::new(scope);
let json_str = r#"{"key": "value", "number": 42}"#;
let json_v8_str = v8::String::new(scope, json_str).unwrap();
let _json_obj = v8::json::parse(scope, json_v8_str).unwrap();
})
});
@bartlomieju I was thinking about implementing a custom deserializer (e.g. from bincode), and did a trivial benchmark of String::new, but it seems rather slow? I assume the V8 deserializer has a more direct/lowlevel approach? Would it be feasible to do in Rust using the RustyV8 bindings?
c.bench_function("v8 1k string new", |b| {
let json_str = r#"{"key": "value", "number": 42}"#;
b.iter(|| {
let scope = &mut HandleScope::new(scope);
for _ in 0..1000 {
black_box(v8::String::new(scope, json_str).unwrap());
}
})
});
c.bench_function("v8 1k string new", |b| {
let json_str = r#"{"key": "value", "number": 42}"#;
b.iter(|| {
let scope = &mut HandleScope::new(scope);
for _ in 0..1000 {
black_box(v8::String::new(scope, json_str).unwrap());
}
})
});
4 replies