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:
parent
7fd3bc56d6
commit
a6caa5a42d
6 changed files with 322 additions and 0 deletions
1
BUILD.gn
1
BUILD.gn
|
@ -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",
|
||||
|
|
|
@ -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
57
src/promise.cc
Normal 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
178
src/promise.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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_
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue