0
0
Fork 0
mirror of https://github.com/denoland/rusty_v8.git synced 2024-12-26 00:59:28 -05:00

add v8::Promise (#56)

This commit is contained in:
Bartek Iwańczuk 2019-12-14 02:18:30 +01:00 committed by Ry Dahl
parent 7fd3bc56d6
commit a6caa5a42d
6 changed files with 322 additions and 0 deletions

View file

@ -20,6 +20,7 @@ v8_static_library("rusty_v8") {
"src/platform/mod.cc",
"src/platform/task.cc",
"src/primitives.cc",
"src/promise.cc",
"src/script.cc",
"src/string.cc",
"src/value.cc",

View file

@ -22,6 +22,7 @@ mod locker;
mod number;
mod object;
mod primitives;
mod promise;
mod script;
mod string;
mod support;
@ -46,6 +47,7 @@ pub use locker::Locker;
pub use number::{Integer, Number};
pub use object::Object;
pub use primitives::*;
pub use promise::{Promise, PromiseResolver, PromiseState};
pub use script::{Script, ScriptOrigin};
pub use string::NewStringType;
pub use string::String;

57
src/promise.cc Normal file
View file

@ -0,0 +1,57 @@
#include "support.h"
#include "v8/include/v8.h"
using namespace support;
extern "C" {
v8::Promise::Resolver *v8__Promise__Resolver__New(v8::Local<v8::Context> context) {
return maybe_local_to_ptr(v8::Promise::Resolver::New(context));
}
v8::Promise *v8__Promise__Resolver__GetPromise(v8::Promise::Resolver *self) {
return local_to_ptr(self->GetPromise());
}
bool v8__Promise__Resolver__Resolve(v8::Promise::Resolver *self,
v8::Local<v8::Context> context,
v8::Local<v8::Value> value) {
return maybe_to_value(self->Resolve(context, value), false);
}
bool v8__Promise__Resolver__Reject(v8::Promise::Resolver *self,
v8::Local<v8::Context> context,
v8::Local<v8::Value> value) {
return maybe_to_value(self->Reject(context, value), false);
}
v8::Promise::PromiseState v8__Promise__State(v8::Promise *self) {
return self->State();
}
bool v8__Promise__HasHandler(v8::Promise *self) {
return self->HasHandler();
}
v8::Value *v8__Promise__Result(v8::Promise *self) {
return local_to_ptr(self->Result());
}
v8::Promise *v8__Promise__Catch(v8::Promise *self,
v8::Local<v8::Context> context,
v8::Local<v8::Function> handler) {
return maybe_local_to_ptr(self->Catch(context, handler));
}
v8::Promise *v8__Promise__Then(v8::Promise *self,
v8::Local<v8::Context> context,
v8::Local<v8::Function> handler) {
return maybe_local_to_ptr(self->Then(context, handler));
}
v8::Promise *v8__Promise__Then2(v8::Promise *self,
v8::Local<v8::Context> context,
v8::Local<v8::Function> on_fulfilled,
v8::Local<v8::Function> on_rejected) {
return maybe_local_to_ptr(self->Then(context, on_fulfilled, on_rejected));
}
}

178
src/promise.rs Normal file
View file

@ -0,0 +1,178 @@
use crate::support::Opaque;
use crate::Context;
use crate::Function;
use crate::HandleScope;
use crate::Local;
use crate::Value;
extern "C" {
fn v8__Promise__Resolver__New(context: *mut Context) -> *mut PromiseResolver;
fn v8__Promise__Resolver__GetPromise(
resolver: *mut PromiseResolver,
) -> *mut Promise;
fn v8__Promise__Resolver__Resolve(
resolver: *mut PromiseResolver,
context: *mut Context,
value: *mut Value,
) -> bool;
fn v8__Promise__Resolver__Reject(
resolver: *mut PromiseResolver,
context: *mut Context,
value: *mut Value,
) -> bool;
fn v8__Promise__State(promise: *mut Promise) -> PromiseState;
fn v8__Promise__HasHandler(promise: *mut Promise) -> bool;
fn v8__Promise__Result(promise: *mut Promise) -> *mut Value;
fn v8__Promise__Catch(
promise: *mut Promise,
context: *mut Context,
handler: *mut Function,
) -> *mut Promise;
fn v8__Promise__Then(
promise: *mut Promise,
context: *mut Context,
handler: *mut Function,
) -> *mut Promise;
fn v8__Promise__Then2(
promise: *mut Promise,
context: *mut Context,
on_fulfilled: *mut Function,
on_rejected: *mut Function,
) -> *mut Promise;
}
#[derive(Debug, PartialEq)]
#[repr(C)]
pub enum PromiseState {
Pending,
Fulfilled,
Rejected,
}
#[repr(C)]
pub struct Promise(Opaque);
/// An instance of the built-in Promise constructor (ES6 draft).
impl Promise {
/// Returns the value of the [[PromiseState]] field.
pub fn state(&mut self) -> PromiseState {
unsafe { v8__Promise__State(&mut *self) }
}
/// Returns true if the promise has at least one derived promise, and
/// therefore resolve/reject handlers (including default handler).
pub fn has_handler(&mut self) -> bool {
unsafe { v8__Promise__HasHandler(&mut *self) }
}
/// Returns the content of the [[PromiseResult]] field. The Promise must not
/// be pending.
pub fn result<'sc>(
&mut self,
_scope: &mut HandleScope<'sc>,
) -> Local<'sc, Value> {
unsafe { Local::from_raw(v8__Promise__Result(&mut *self)).unwrap() }
}
/// Register a rejection handler with a promise.
///
/// See `Self::then2`.
pub fn catch<'sc>(
&mut self,
mut context: Local<'sc, Context>,
mut handler: Local<'sc, Function>,
) -> Option<Local<'sc, Promise>> {
unsafe {
Local::from_raw(v8__Promise__Catch(
&mut *self,
&mut *context,
&mut *handler,
))
}
}
/// Register a resolution handler with a promise.
///
/// See `Self::then2`.
pub fn then<'sc>(
&mut self,
mut context: Local<'sc, Context>,
mut handler: Local<'sc, Function>,
) -> Option<Local<'sc, Promise>> {
unsafe {
Local::from_raw(v8__Promise__Then(
&mut *self,
&mut *context,
&mut *handler,
))
}
}
/// Register a resolution/rejection handler with a promise.
/// The handler is given the respective resolution/rejection value as
/// an argument. If the promise is already resolved/rejected, the handler is
/// invoked at the end of turn.
pub fn then2<'sc>(
&mut self,
mut context: Local<'sc, Context>,
mut on_fulfilled: Local<'sc, Function>,
mut on_rejected: Local<'sc, Function>,
) -> Option<Local<'sc, Promise>> {
unsafe {
Local::from_raw(v8__Promise__Then2(
&mut *self,
&mut *context,
&mut *on_fulfilled,
&mut *on_rejected,
))
}
}
}
#[repr(C)]
pub struct PromiseResolver(Opaque);
impl PromiseResolver {
/// Create a new resolver, along with an associated promise in pending state.
pub fn new(
mut context: Local<'_, Context>,
) -> Option<Local<'_, PromiseResolver>> {
unsafe { Local::from_raw(v8__Promise__Resolver__New(&mut *context)) }
}
/// Extract the associated promise.
pub fn get_promise<'sc>(
&mut self,
_scope: &mut HandleScope<'sc>,
) -> Local<'sc, Promise> {
unsafe {
Local::from_raw(v8__Promise__Resolver__GetPromise(&mut *self)).unwrap()
}
}
/// TODO: in v8 this function returns `Maybe<bool>`
/// Resolve the associated promise with a given value.
/// Ignored if the promise is no longer pending.
pub fn resolve<'sc>(
&mut self,
mut context: Local<'sc, Context>,
mut value: Local<'sc, Value>,
) -> bool {
unsafe {
v8__Promise__Resolver__Resolve(&mut *self, &mut *context, &mut *value)
}
}
/// TODO: in v8 this function returns `Maybe<bool>`
/// Reject the associated promise with a given value.
/// Ignored if the promise is no longer pending.
pub fn reject<'sc>(
&mut self,
mut context: Local<'sc, Context>,
mut value: Local<'sc, Value>,
) -> bool {
unsafe {
v8__Promise__Resolver__Reject(&mut *self, &mut *context, &mut *value)
}
}
}

View file

@ -60,4 +60,9 @@ inline static v8::MaybeLocal<T> ptr_to_maybe_local(T* ptr) {
return *reinterpret_cast<v8::MaybeLocal<T>*>(&ptr);
}
template <class T>
inline static T maybe_to_value(v8::Maybe<T> maybe, T default_value) {
return maybe.FromMaybe(default_value);
}
#endif // SUPPORT_H_

View file

@ -306,6 +306,45 @@ fn object() {
});
}
#[test]
fn promise_resolved() {
setup();
let mut params = v8::Isolate::create_params();
params.set_array_buffer_allocator(
v8::array_buffer::Allocator::new_default_allocator(),
);
let mut isolate = v8::Isolate::new(params);
let mut locker = v8::Locker::new(&mut isolate);
v8::HandleScope::enter(&mut locker, |scope| {
let mut context = v8::Context::new(scope);
context.enter();
let maybe_resolver = v8::PromiseResolver::new(context);
assert!(maybe_resolver.is_some());
let mut resolver = maybe_resolver.unwrap();
let mut promise = resolver.get_promise(scope);
assert!(!promise.has_handler());
assert_eq!(promise.state(), v8::PromiseState::Pending);
let str =
v8::String::new(scope, "test", v8::NewStringType::Normal).unwrap();
let value: Local<v8::Value> = cast(str);
resolver.resolve(context, value);
assert_eq!(promise.state(), v8::PromiseState::Fulfilled);
let result = promise.result(scope);
let result_str: v8::Local<v8::String> = cast(result);
assert_eq!(result_str.to_rust_string_lossy(scope), "test".to_string());
// Resolve again with different value, since promise is already in `Fulfilled` state
// it should be ignored.
let str =
v8::String::new(scope, "test2", v8::NewStringType::Normal).unwrap();
let value: Local<v8::Value> = cast(str);
resolver.resolve(context, value);
let result = promise.result(scope);
let result_str: v8::Local<v8::String> = cast(result);
assert_eq!(result_str.to_rust_string_lossy(scope), "test".to_string());
context.exit();
});
}
extern "C" fn callback(info: &FunctionCallbackInfo) {
assert_eq!(info.length(), 0);
let mut locker = v8::Locker::new(info.get_isolate());
@ -321,6 +360,46 @@ extern "C" fn callback(info: &FunctionCallbackInfo) {
});
}
#[test]
fn promise_rejected() {
setup();
let mut params = v8::Isolate::create_params();
params.set_array_buffer_allocator(
v8::array_buffer::Allocator::new_default_allocator(),
);
let mut isolate = v8::Isolate::new(params);
let mut locker = v8::Locker::new(&mut isolate);
v8::HandleScope::enter(&mut locker, |scope| {
let mut context = v8::Context::new(scope);
context.enter();
let maybe_resolver = v8::PromiseResolver::new(context);
assert!(maybe_resolver.is_some());
let mut resolver = maybe_resolver.unwrap();
let mut promise = resolver.get_promise(scope);
assert!(!promise.has_handler());
assert_eq!(promise.state(), v8::PromiseState::Pending);
let str =
v8::String::new(scope, "test", v8::NewStringType::Normal).unwrap();
let value: Local<v8::Value> = cast(str);
let rejected = resolver.reject(context, value);
assert!(rejected);
assert_eq!(promise.state(), v8::PromiseState::Rejected);
let result = promise.result(scope);
let result_str: v8::Local<v8::String> = cast(result);
assert_eq!(result_str.to_rust_string_lossy(scope), "test".to_string());
// Reject again with different value, since promise is already in `Rejected` state
// it should be ignored.
let str =
v8::String::new(scope, "test2", v8::NewStringType::Normal).unwrap();
let value: Local<v8::Value> = cast(str);
resolver.reject(context, value);
let result = promise.result(scope);
let result_str: v8::Local<v8::String> = cast(result);
assert_eq!(result_str.to_rust_string_lossy(scope), "test".to_string());
context.exit();
});
}
#[test]
fn function() {
setup();