pub(crate) mod internal;
use std;
use std::cell::RefCell;
use std::convert::Into;
use std::marker::PhantomData;
use std::os::raw::c_void;
use std::panic::UnwindSafe;
use neon_runtime;
use neon_runtime::raw;
use borrow::{Ref, RefMut, Borrow, BorrowMut};
use borrow::internal::Ledger;
use context::internal::Env;
use handle::{Managed, Handle};
use types::{JsValue, Value, JsObject, JsArray, JsFunction, JsBoolean, JsNumber, JsString, StringResult, JsNull, JsUndefined};
use types::binary::{JsArrayBuffer, JsBuffer};
use types::error::JsError;
use object::{Object, This};
use object::class::Class;
use result::{NeonResult, JsResult, Throw};
use self::internal::{ContextInternal, Scope, ScopeMetadata};
#[repr(C)]
pub(crate) struct CallbackInfo<'a> {
info: raw::FunctionCallbackInfo,
_lifetime: PhantomData<&'a raw::FunctionCallbackInfo>,
}
impl CallbackInfo<'_> {
pub fn data(&self, env: Env) -> *mut c_void {
unsafe {
let mut raw_data: *mut c_void = std::mem::zeroed();
neon_runtime::call::data(env.to_raw(), self.info, &mut raw_data);
raw_data
}
}
pub unsafe fn with_cx<T: This, U, F: for<'a> FnOnce(CallContext<'a, T>) -> U>(&self, env: Env, f: F) -> U {
CallContext::<T>::with(env, self, f)
}
pub fn set_return<'a, 'b, T: Value>(&'a self, value: Handle<'b, T>) {
unsafe {
neon_runtime::call::set_return(self.info, value.to_raw())
}
}
#[cfg(feature = "legacy-runtime")]
fn kind(&self) -> CallKind {
if unsafe { neon_runtime::call::is_construct(std::mem::transmute(self)) } {
CallKind::Construct
} else {
CallKind::Call
}
}
#[cfg(feature = "napi-runtime")]
fn kind<'b, C: Context<'b>>(&self, cx: &C) -> CallKind {
if unsafe { neon_runtime::call::is_construct(cx.env().to_raw(), self.info) } {
CallKind::Construct
} else {
CallKind::Call
}
}
pub fn len<'b, C: Context<'b>>(&self, cx: &C) -> i32 {
unsafe {
neon_runtime::call::len(cx.env().to_raw(), self.info)
}
}
#[cfg(feature = "legacy-runtime")]
pub fn get<'b, C: Context<'b>>(&self, cx: &mut C, i: i32) -> Option<Handle<'b, JsValue>> {
if i < 0 || i >= self.len(cx) {
return None;
}
unsafe {
let mut local: raw::Local = std::mem::zeroed();
neon_runtime::call::get(cx.env().to_raw(), self.info, i, &mut local);
Some(Handle::new_internal(JsValue::from_raw(local)))
}
}
#[cfg(feature = "napi-runtime")]
pub fn argv<'b, C: Context<'b>>(&self, cx: &mut C) -> Vec<raw::Local> {
unsafe {
neon_runtime::call::argv(cx.env().to_raw(), self.info)
}
}
pub fn this<'b, C: Context<'b>>(&self, cx: &mut C) -> raw::Local {
let env = cx.env();
unsafe {
let mut local: raw::Local = std::mem::zeroed();
neon_runtime::call::this(env.to_raw(), std::mem::transmute(self.info), &mut local);
local
}
}
}
#[derive(Clone, Copy, Debug)]
pub enum CallKind {
Construct,
Call
}
pub struct Lock<'a> {
pub(crate) ledger: RefCell<Ledger>,
pub(crate) env: Env,
phantom: PhantomData<&'a ()>
}
impl<'a> Lock<'a> {
fn new(env: Env) -> Self {
Lock {
ledger: RefCell::new(Ledger::new()),
env,
phantom: PhantomData,
}
}
}
pub trait Context<'a>: ContextInternal<'a> {
fn lock(&self) -> Lock<'_> {
self.check_active();
Lock::new(self.env())
}
fn borrow<'c, V, T, F>(&self, v: &'c Handle<V>, f: F) -> T
where V: Value,
&'c V: Borrow,
F: for<'b> FnOnce(Ref<'b, <&'c V as Borrow>::Target>) -> T
{
let lock = self.lock();
let contents = v.borrow(&lock);
f(contents)
}
fn borrow_mut<'c, V, T, F>(&self, v: &'c mut Handle<V>, f: F) -> T
where V: Value,
&'c mut V: BorrowMut,
F: for<'b> FnOnce(RefMut<'b, <&'c mut V as Borrow>::Target>) -> T
{
let lock = self.lock();
let contents = v.borrow_mut(&lock);
f(contents)
}
fn execute_scoped<T, F>(&self, f: F) -> T
where F: for<'b> FnOnce(ExecuteContext<'b>) -> T
{
self.check_active();
self.deactivate();
let result = ExecuteContext::with(f);
self.activate();
result
}
fn compute_scoped<V, F>(&self, f: F) -> JsResult<'a, V>
where V: Value,
F: for<'b, 'c> FnOnce(ComputeContext<'b, 'c>) -> JsResult<'b, V>
{
self.check_active();
self.deactivate();
let result = ComputeContext::with(|cx| {
unsafe {
let escapable_handle_scope = cx.scope.handle_scope as *mut raw::EscapableHandleScope;
let escapee = f(cx)?;
let mut result_local: raw::Local = std::mem::zeroed();
neon_runtime::scope::escape(&mut result_local, escapable_handle_scope, escapee.to_raw());
Ok(Handle::new_internal(V::from_raw(result_local)))
}
});
self.activate();
result
}
#[cfg(all(feature = "try-catch-api", feature = "napi-runtime"))]
fn try_catch<'b: 'a, T, F>(&mut self, f: F) -> Result<Handle<'a, T>, Handle<'a, JsValue>>
where T: Value,
F: FnOnce(&mut Self) -> JsResult<'b, T>
{
self.try_catch_internal(f)
}
#[cfg(all(feature = "try-catch-api", feature = "legacy-runtime"))]
fn try_catch<'b: 'a, T, F>(&mut self, f: F) -> Result<Handle<'a, T>, Handle<'a, JsValue>>
where T: Value,
F: UnwindSafe + FnOnce(&mut Self) -> JsResult<'b, T>
{
self.try_catch_internal(f)
}
fn boolean(&mut self, b: bool) -> Handle<'a, JsBoolean> {
JsBoolean::new(self, b)
}
fn number<T: Into<f64>>(&mut self, x: T) -> Handle<'a, JsNumber> {
JsNumber::new(self, x.into())
}
fn string<S: AsRef<str>>(&mut self, s: S) -> Handle<'a, JsString> {
JsString::new(self, s)
}
fn try_string<S: AsRef<str>>(&mut self, s: S) -> StringResult<'a> {
JsString::try_new(self, s)
}
fn null(&mut self) -> Handle<'a, JsNull> {
#[cfg(feature = "legacy-runtime")]
return JsNull::new();
#[cfg(feature = "napi-runtime")]
return JsNull::new(self);
}
fn undefined(&mut self) -> Handle<'a, JsUndefined> {
#[cfg(feature = "legacy-runtime")]
return JsUndefined::new();
#[cfg(feature = "napi-runtime")]
return JsUndefined::new(self);
}
fn empty_object(&mut self) -> Handle<'a, JsObject> {
JsObject::new(self)
}
fn empty_array(&mut self) -> Handle<'a, JsArray> {
JsArray::new(self, 0)
}
fn array_buffer(&mut self, size: u32) -> JsResult<'a, JsArrayBuffer> {
JsArrayBuffer::new(self, size)
}
fn buffer(&mut self, size: u32) -> JsResult<'a, JsBuffer> {
JsBuffer::new(self, size)
}
fn global(&mut self) -> Handle<'a, JsObject> {
JsObject::build(|out| {
unsafe {
neon_runtime::scope::get_global(self.env().to_raw(), out);
}
})
}
fn throw<'b, T: Value, U>(&mut self, v: Handle<'b, T>) -> NeonResult<U> {
unsafe {
neon_runtime::error::throw(self.env().to_raw(), v.to_raw());
}
Err(Throw)
}
fn error<S: AsRef<str>>(&mut self, msg: S) -> JsResult<'a, JsError> {
JsError::error(self, msg)
}
fn type_error<S: AsRef<str>>(&mut self, msg: S) -> JsResult<'a, JsError> {
JsError::type_error(self, msg)
}
fn range_error<S: AsRef<str>>(&mut self, msg: S) -> JsResult<'a, JsError> {
JsError::range_error(self, msg)
}
fn throw_error<S: AsRef<str>, T>(&mut self, msg: S) -> NeonResult<T> {
let err = JsError::error(self, msg)?;
self.throw(err)
}
fn throw_type_error<S: AsRef<str>, T>(&mut self, msg: S) -> NeonResult<T> {
let err = JsError::type_error(self, msg)?;
self.throw(err)
}
fn throw_range_error<S: AsRef<str>, T>(&mut self, msg: S) -> NeonResult<T> {
let err = JsError::range_error(self, msg)?;
self.throw(err)
}
}
pub struct ModuleContext<'a> {
scope: Scope<'a, raw::HandleScope>,
exports: Handle<'a, JsObject>
}
impl<'a> UnwindSafe for ModuleContext<'a> { }
impl<'a> ModuleContext<'a> {
pub(crate) fn with<T, F: for<'b> FnOnce(ModuleContext<'b>) -> T>(env: Env, exports: Handle<'a, JsObject>, f: F) -> T {
#[cfg(feature = "legacy-runtime")]
{
debug_assert!(unsafe { neon_runtime::scope::size() } <= std::mem::size_of::<raw::HandleScope>());
debug_assert!(unsafe { neon_runtime::scope::alignment() } <= std::mem::align_of::<raw::HandleScope>());
}
Scope::with(env, |scope| {
f(ModuleContext {
scope,
exports
})
})
}
pub fn export_function<T: Value>(&mut self, key: &str, f: fn(FunctionContext) -> JsResult<T>) -> NeonResult<()> {
let value = JsFunction::new(self, f)?.upcast::<JsValue>();
self.exports.set(self, key, value)?;
Ok(())
}
pub fn export_class<T: Class>(&mut self, key: &str) -> NeonResult<()> {
let constructor = T::constructor(self)?;
self.exports.set(self, key, constructor)?;
Ok(())
}
pub fn export_value<T: Value>(&mut self, key: &str, val: Handle<T>) -> NeonResult<()> {
self.exports.set(self, key, val)?;
Ok(())
}
pub fn exports_object(&mut self) -> JsResult<'a, JsObject> {
Ok(self.exports)
}
}
impl<'a> ContextInternal<'a> for ModuleContext<'a> {
fn scope_metadata(&self) -> &ScopeMetadata {
&self.scope.metadata
}
}
impl<'a> Context<'a> for ModuleContext<'a> { }
pub struct ExecuteContext<'a> {
scope: Scope<'a, raw::HandleScope>
}
impl<'a> ExecuteContext<'a> {
pub(crate) fn with<T, F: for<'b> FnOnce(ExecuteContext<'b>) -> T>(f: F) -> T {
let env = Env::current();
Scope::with(env, |scope| {
f(ExecuteContext { scope })
})
}
}
impl<'a> ContextInternal<'a> for ExecuteContext<'a> {
fn scope_metadata(&self) -> &ScopeMetadata {
&self.scope.metadata
}
}
impl<'a> Context<'a> for ExecuteContext<'a> { }
pub struct ComputeContext<'a, 'outer> {
scope: Scope<'a, raw::EscapableHandleScope>,
phantom_inner: PhantomData<&'a ()>,
phantom_outer: PhantomData<&'outer ()>
}
impl<'a, 'b> ComputeContext<'a, 'b> {
pub(crate) fn with<T, F: for<'c, 'd> FnOnce(ComputeContext<'c, 'd>) -> T>(f: F) -> T {
let env = Env::current();
Scope::with(env, |scope| {
f(ComputeContext {
scope,
phantom_inner: PhantomData,
phantom_outer: PhantomData
})
})
}
}
impl<'a, 'b> ContextInternal<'a> for ComputeContext<'a, 'b> {
fn scope_metadata(&self) -> &ScopeMetadata {
&self.scope.metadata
}
}
impl<'a, 'b> Context<'a> for ComputeContext<'a, 'b> { }
pub struct CallContext<'a, T: This> {
scope: Scope<'a, raw::HandleScope>,
info: &'a CallbackInfo<'a>,
#[cfg(feature = "napi-runtime")]
arguments: Option<Vec<raw::Local>>,
phantom_type: PhantomData<T>
}
impl<'a, T: This> UnwindSafe for CallContext<'a, T> { }
impl<'a, T: This> CallContext<'a, T> {
pub fn kind(&self) -> CallKind {
#[cfg(feature = "legacy-runtime")]
let kind = self.info.kind();
#[cfg(feature = "napi-runtime")]
let kind = self.info.kind(self);
kind
}
pub(crate) fn with<U, F: for<'b> FnOnce(CallContext<'b, T>) -> U>(env: Env, info: &'a CallbackInfo<'a>, f: F) -> U {
Scope::with(env, |scope| {
f(CallContext {
scope,
info,
#[cfg(feature = "napi-runtime")]
arguments: None,
phantom_type: PhantomData
})
})
}
pub fn len(&self) -> i32 { self.info.len(self) }
pub fn argument_opt(&mut self, i: i32) -> Option<Handle<'a, JsValue>> {
#[cfg(feature = "legacy-runtime")]
{ self.info.get(self, i) }
#[cfg(feature = "napi-runtime")]
{
let local = if let Some(arguments) = &self.arguments {
arguments.get(i as usize).cloned()
} else {
let arguments = self.info.argv(self);
let local = arguments.get(i as usize).cloned();
self.arguments = Some(arguments);
local
};
local.map(|local| Handle::new_internal(JsValue::from_raw(local)))
}
}
pub fn argument<V: Value>(&mut self, i: i32) -> JsResult<'a, V> {
match self.argument_opt(i) {
Some(v) => v.downcast_or_throw(self),
None => self.throw_type_error("not enough arguments")
}
}
pub fn this(&mut self) -> Handle<'a, T> {
#[cfg(feature = "legacy-runtime")]
let this = T::as_this(self.info.this(self));
#[cfg(feature = "napi-runtime")]
let this = T::as_this(self.env(), self.info.this(self));
Handle::new_internal(this)
}
}
impl<'a, T: This> ContextInternal<'a> for CallContext<'a, T> {
fn scope_metadata(&self) -> &ScopeMetadata {
&self.scope.metadata
}
}
impl<'a, T: This> Context<'a> for CallContext<'a, T> { }
pub type FunctionContext<'a> = CallContext<'a, JsObject>;
pub type MethodContext<'a, T> = CallContext<'a, T>;
pub struct TaskContext<'a> {
scope: Scope<'a, raw::InheritedHandleScope>
}
impl<'a> TaskContext<'a> {
pub(crate) fn with<T, F: for<'b> FnOnce(TaskContext<'b>) -> T>(f: F) -> T {
let env = Env::current();
Scope::with(env, |scope| {
f(TaskContext { scope })
})
}
}
impl<'a> ContextInternal<'a> for TaskContext<'a> {
fn scope_metadata(&self) -> &ScopeMetadata {
&self.scope.metadata
}
}
impl<'a> Context<'a> for TaskContext<'a> { }