Created
October 18, 2017 21:20
-
-
Save dherman/5d862c4736df031a5e098b9e5ab20591 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//! The `neon` crate provides the entire [Neon](https://www.neon-bindings.com/) API. | |
extern crate neon_runtime; | |
extern crate cslice; | |
#[cfg(test)] | |
extern crate rustc_version; | |
pub mod mem; | |
pub mod vm; | |
pub mod scope; | |
pub mod js; | |
pub mod task; | |
#[doc(hidden)] | |
pub mod macro_internal; | |
/// Register the current crate as a Node module, providing startup | |
/// logic for initializing the module object at runtime. | |
/// | |
/// Example: | |
/// | |
/// ```rust,ignore | |
/// register_module!(m, { | |
/// m.export("foo", foo)?; | |
/// m.export("bar", bar)?; | |
/// m.export("baz", baz)?; | |
/// Ok(()) | |
/// }); | |
/// ``` | |
#[macro_export] | |
macro_rules! register_module { | |
($module:ident, $init:block) => { | |
// Mark this function as a global constructor (like C++). | |
#[allow(improper_ctypes)] | |
#[cfg_attr(target_os = "linux", link_section = ".ctors")] | |
#[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")] | |
#[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")] | |
pub static __LOAD_NEON_MODULE: extern "C" fn() = { | |
fn __init_neon_module(mut $module: $crate::vm::Module) -> $crate::vm::VmResult<()> $init | |
extern "C" fn __load_neon_module() { | |
// Put everything else in the ctor fn so the user fn can't see it. | |
#[repr(C)] | |
struct __NodeModule { | |
version: i32, | |
flags: u32, | |
dso_handle: *mut u8, | |
filename: *const u8, | |
register_func: Option<extern "C" fn( | |
$crate::mem::Handle<$crate::js::JsObject>, *mut u8, *mut u8)>, | |
context_register_func: Option<extern "C" fn( | |
$crate::mem::Handle<$crate::js::JsObject>, *mut u8, *mut u8, *mut u8)>, | |
modname: *const u8, | |
priv_data: *mut u8, | |
link: *mut __NodeModule | |
} | |
static mut __NODE_MODULE: __NodeModule = __NodeModule { | |
version: 0, | |
flags: 0, | |
dso_handle: 0 as *mut _, | |
filename: b"neon_source.rs\0" as *const u8, | |
register_func: Some(__register_neon_module), | |
context_register_func: None, | |
modname: b"neon_module\0" as *const u8, | |
priv_data: 0 as *mut _, | |
link: 0 as *mut _ | |
}; | |
extern "C" fn __register_neon_module( | |
m: $crate::mem::Handle<$crate::js::JsObject>, _: *mut u8, _: *mut u8) { | |
$crate::vm::Module::initialize(m, __init_neon_module); | |
} | |
extern "C" { | |
fn node_module_register(module: *mut __NodeModule); | |
} | |
// Suppress the default Rust panic hook, which prints diagnostics to stderr. | |
::std::panic::set_hook(::std::boxed::Box::new(|_| { })); | |
unsafe { | |
// Set the ABI version based on the NODE_MODULE_VERSION constant provided by the current node headers. | |
__NODE_MODULE.version = $crate::macro_internal::runtime::module::get_version(); | |
node_module_register(&mut __NODE_MODULE); | |
} | |
} | |
__load_neon_module | |
}; | |
} | |
} | |
#[doc(hidden)] | |
#[macro_export] | |
macro_rules! class_definition { | |
( $cls:ident ; $cname:ident ; $typ:ty ; $allocator:tt ; $call_ctor:tt ; $new_ctor:tt ; $mnames:tt ; $mdefs:tt ; init($call:pat) $body:block $($rest:tt)* ) => { | |
class_definition!($cls ; | |
$cname ; | |
$typ ; | |
{ | |
fn _______allocator_rust_y_u_no_hygienic_items_______($call: $crate::vm::FunctionCall<$crate::js::JsUndefined>) -> $crate::vm::VmResult<$typ> { | |
$body | |
} | |
$crate::macro_internal::AllocateKernel::new(_______allocator_rust_y_u_no_hygienic_items_______) | |
} ; | |
$call_ctor ; | |
$new_ctor ; | |
$mnames ; | |
$mdefs ; | |
$($rest)*); | |
}; | |
( $cls:ident ; $cname:ident ; $typ:ty ; $allocator:tt ; $call_ctor:tt ; $new_ctor:tt ; ($($mname:tt)*) ; ($($mdef:tt)*) ; method $name:ident($call:pat) $body:block $($rest:tt)* ) => { | |
class_definition!($cls ; | |
$cname ; | |
$typ ; | |
$allocator ; | |
$call_ctor ; | |
$new_ctor ; | |
($($mname)* $name) ; | |
($($mdef)* { | |
fn _______method_rust_y_u_no_hygienic_items_______($call: $crate::vm::FunctionCall<$cls>) -> $crate::vm::JsResult<$crate::js::JsValue> { | |
$body | |
} | |
$crate::macro_internal::MethodKernel::new(_______method_rust_y_u_no_hygienic_items_______) | |
}) ; | |
$($rest)*); | |
}; | |
( $cls:ident ; $cname:ident ; $typ:ty ; $allocator:tt ; $call_ctor:tt ; $new_ctor:tt ; $mnames:tt ; $mdefs:tt ; constructor($call:pat) $body:block $($rest:tt)* ) => { | |
class_definition!($cls ; | |
$cname ; | |
$typ ; | |
$allocator ; | |
$call_ctor ; | |
({ | |
fn _______constructor_rust_y_u_no_hygienic_items_______($call: $crate::vm::FunctionCall<$cls>) -> $crate::vm::VmResult<Option<$crate::mem::Handle<$crate::js::JsObject>>> { | |
$body | |
} | |
$crate::macro_internal::ConstructKernel::new(_______constructor_rust_y_u_no_hygienic_items_______) | |
}) ; | |
$mnames ; | |
$mdefs ; | |
$($rest)*); | |
}; | |
( $cls:ident ; $cname:ident ; $typ:ty ; $allocator:tt ; $call_ctor:tt ; $new_ctor:tt ; $mnames:tt ; $mdefs:tt ; call($call:pat) $body:block $($rest:tt)* ) => { | |
class_definition!($cls ; | |
$cname ; | |
$typ ; | |
$allocator ; | |
({ | |
fn _______call_rust_y_u_no_hygienic_items_______($call: $crate::vm::FunctionCall<$crate::js::JsValue>) -> $crate::vm::JsResult<$crate::js::JsValue> { | |
$body | |
} | |
$crate::macro_internal::ConstructorCallKernel::new(_______call_rust_y_u_no_hygienic_items_______) | |
}) ; | |
$new_ctor ; | |
$mnames ; | |
$mdefs ; | |
$($rest)*); | |
}; | |
( $cls:ident ; $cname:ident ; $typ:ty ; $allocator:block ; ($($call_ctor:block)*) ; ($($new_ctor:block)*) ; ($($mname:ident)*) ; ($($mdef:block)*) ; $($rest:tt)* ) => { | |
impl $crate::js::class::Class for $cls { | |
type Internals = $typ; | |
fn setup<'a, T: $crate::scope::Scope<'a>>(_: &mut T) -> $crate::vm::VmResult<$crate::js::class::ClassDescriptor<'a, Self>> { | |
::std::result::Result::Ok(Self::describe(stringify!($cname), $allocator) | |
$(.construct($new_ctor))* | |
$(.call($call_ctor))* | |
$(.method(stringify!($mname), $mdef))*) | |
} | |
} | |
}; | |
} | |
#[doc(hidden)] | |
#[macro_export] | |
macro_rules! impl_managed { | |
($cls:ident) => { | |
impl $crate::mem::Managed for $cls { | |
fn to_raw(self) -> $crate::macro_internal::runtime::raw::Local { | |
let $cls(raw) = self; | |
raw | |
} | |
fn from_raw(raw: $crate::macro_internal::runtime::raw::Local) -> Self { | |
$cls(raw) | |
} | |
} | |
} | |
} | |
/// Declare custom native JavaScript types with Rust implementations. | |
/// | |
/// Example: | |
/// | |
/// ```rust,ignore | |
/// pub struct Greeter { | |
/// greeting: String | |
/// } | |
/// | |
/// declare_types! { | |
/// | |
/// /// A class for generating greeting strings. | |
/// pub class JsGreeter for Greeter { | |
/// init(call) { | |
/// let scope = call.scope; | |
/// let greeting = call.arguments.require(scope, 0)?.to_string(scope)?.value(); | |
/// Ok(Greeter { | |
/// greeting: greeting | |
/// }) | |
/// } | |
/// | |
/// method hello(call) { | |
/// let scope = call.scope; | |
/// let name = call.arguments.require(scope, 0)?.to_string(scope)?.value(); | |
/// let msg = vm::lock(call.arguments.this(scope), |greeter| { | |
/// format!("{}, {}!", greeter.greeting, name) | |
/// }); | |
/// Ok(JsString::new_or_throw(scope, &msg[..])?.upcast()) | |
/// } | |
/// } | |
/// | |
/// } | |
/// ``` | |
#[macro_export] | |
macro_rules! declare_types { | |
{ $(#[$attr:meta])* pub class $cls:ident for $typ:ident { $($body:tt)* } $($rest:tt)* } => { | |
declare_types! { $(#[$attr])* pub class $cls as $typ for $typ { $($body)* } $($rest)* } | |
}; | |
{ $(#[$attr:meta])* class $cls:ident for $typ:ident { $($body:tt)* } $($rest:tt)* } => { | |
declare_types! { $(#[$attr])* class $cls as $typ for $typ { $($body)* } $($rest)* } | |
}; | |
{ $(#[$attr:meta])* pub class $cls:ident as $cname:ident for $typ:ty { $($body:tt)* } $($rest:tt)* } => { | |
#[derive(Copy, Clone)] | |
#[repr(C)] | |
$(#[$attr])* | |
pub struct $cls($crate::macro_internal::runtime::raw::Local); | |
impl_managed!($cls); | |
class_definition!($cls ; $cname ; $typ ; () ; () ; () ; () ; () ; $($body)*); | |
declare_types! { $($rest)* } | |
}; | |
{ $(#[$attr:meta])* class $cls:ident as $cname:ident for $typ:ty { $($body:tt)* } $($rest:tt)* } => { | |
#[derive(Copy, Clone)] | |
#[repr(C)] | |
$(#[$attr])* | |
struct $cls($crate::macro_internal::runtime::raw::Local); | |
impl_managed!($cls); | |
class_definition!($cls ; $cname ; $typ ; () ; () ; () ; () ; () ; $($body)*); | |
declare_types! { $($rest)* } | |
}; | |
{ } => { }; | |
} | |
#[test] | |
fn cli_test() { | |
use std::process::Command; | |
let cli_status = if cfg!(target_os = "windows") { | |
Command::new("cmd") | |
.args(&["/C", "echo hi"]) | |
.status() | |
.expect("failed to execute process") | |
} else { | |
Command::new("sh") | |
.arg("-c") | |
.arg("cd cli && npm install && npm run transpile") | |
.status() | |
.expect("failed to execute process") | |
}; | |
assert!(cli_status.success()); | |
let cli_test_output = if cfg!(target_os = "windows") { | |
Command::new("cmd") | |
.args(&["/C", "echo hi"]) | |
.status() | |
.expect("failed to execute process") | |
} else { | |
Command::new("sh") | |
.arg("-c") | |
.arg("cd test/cli && npm install && npm run transpile && npm test") | |
.status() | |
.expect("failed to execute process") | |
}; | |
assert!(cli_test_output.success()); | |
} | |
#[test] | |
fn static_test() { | |
use rustc_version::{version_meta, Channel}; | |
#[cfg(windows)] | |
use std::env; | |
if version_meta().unwrap().channel != Channel::Nightly { return }; | |
use std::process::Command; | |
let static_output = if cfg!(target_os = "windows") { | |
Command::new("cmd") | |
.current_dir(env::current_dir().unwrap().join("test").join("static")) | |
.args(&["/C", "cargo test"]) | |
.status() | |
.expect("failed to execute process") | |
} else { | |
Command::new("sh") | |
.arg("-c") | |
.arg("cd test/static && cargo test") | |
.status() | |
.expect("failed to execute process") | |
}; | |
assert!(static_output.success()); | |
} | |
#[test] | |
fn dynamic_test() { | |
use std::process::Command; | |
#[cfg(windows)] | |
use std::env; | |
if cfg!(target_os = "windows") { | |
assert!(Command::new("cmd") | |
.current_dir(env::current_dir().unwrap().join("cli")) | |
.args(&["/C", "npm install"]) | |
.status() | |
.expect("failed to execute process") | |
.success()); | |
assert!(Command::new("cmd") | |
.current_dir(env::current_dir().unwrap().join("test").join("dynamic")) | |
.args(&["/C", "npm install & npm test"]) | |
.status() | |
.expect("failed to execute process") | |
.success()); | |
} else { | |
assert!(Command::new("sh") | |
.arg("-c") | |
.arg("cd cli && npm install && cd ../test/dynamic && npm install && npm test") | |
.status() | |
.expect("failed to execute process") | |
.success()); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment