├── .gitignore ├── src ├── bootstrap.js ├── bin.rs └── lib.rs ├── Cargo.toml ├── tests └── smworker.rs └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /src/bootstrap.js: -------------------------------------------------------------------------------- 1 | var recv = (function() { 2 | var callback; 3 | 4 | var _recv = function(callbackFn) { 5 | if (callbackFn === undefined) { 6 | return callback; 7 | } else { 8 | callback = callbackFn; 9 | } 10 | } 11 | 12 | return _recv; 13 | })(); 14 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "smworker" 3 | version = "0.0.1" 4 | authors = ["Sankha Narayan Guria "] 5 | 6 | [lib] 7 | name = "smworker" 8 | path = "src/lib.rs" 9 | test = true 10 | 11 | [[bin]] 12 | name = "smworker-bin" 13 | path = "src/bin.rs" 14 | 15 | [dependencies.js] 16 | git = "https://github.com/servo/rust-mozjs" 17 | 18 | [dependencies] 19 | libc = "0.1" 20 | -------------------------------------------------------------------------------- /src/bin.rs: -------------------------------------------------------------------------------- 1 | extern crate smworker; 2 | 3 | use smworker::{SMWorker, Function}; 4 | 5 | struct TestFn; 6 | 7 | impl Function for TestFn { 8 | fn callback(&self, message: String) { 9 | println!("{:?}", message); 10 | } 11 | } 12 | 13 | fn main() { 14 | let test_fn = TestFn; 15 | 16 | println!("Hello"); 17 | let worker = SMWorker::new(test_fn); 18 | let code = " \ 19 | _send(\"World\"); \ 20 | "; 21 | worker.execute("test_js".to_string(), code.to_string()); 22 | } 23 | -------------------------------------------------------------------------------- /tests/smworker.rs: -------------------------------------------------------------------------------- 1 | extern crate smworker; 2 | 3 | #[test] 4 | fn test_js() { 5 | let worker = smworker::new(); 6 | let code = " \ 7 | var a = 1 + 2; \ 8 | var b = \"abc\"; \ 9 | a == b; \ 10 | "; 11 | assert!(worker.execute("test_js".to_string(), code.to_string()).is_ok()); 12 | } 13 | 14 | #[test] 15 | fn test_builtin() { 16 | let worker = smworker::new(); 17 | let code = " \ 18 | var a = \"abc\"; \ 19 | a.substring(0, 1); \ 20 | "; 21 | assert!(worker.execute("test_builtin".to_string(), code.to_string()).is_ok()); 22 | } 23 | 24 | #[test] 25 | fn test_builtin_err() { 26 | let worker = smworker::new(); 27 | let code = " \ 28 | var a = \"abc\"; \ 29 | a.abc(0, 1); \ 30 | "; 31 | assert!(worker.execute("test_builtin_err".to_string(), code.to_string()).is_err()); 32 | } 33 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [root] 2 | name = "smworker" 3 | version = "0.0.1" 4 | dependencies = [ 5 | "js 0.1.0 (git+https://github.com/servo/rust-mozjs)", 6 | "libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 7 | ] 8 | 9 | [[package]] 10 | name = "advapi32-sys" 11 | version = "0.1.2" 12 | source = "registry+https://github.com/rust-lang/crates.io-index" 13 | dependencies = [ 14 | "winapi 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", 15 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 16 | ] 17 | 18 | [[package]] 19 | name = "gcc" 20 | version = "0.3.18" 21 | source = "registry+https://github.com/rust-lang/crates.io-index" 22 | dependencies = [ 23 | "advapi32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 24 | "winapi 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", 25 | ] 26 | 27 | [[package]] 28 | name = "heapsize" 29 | version = "0.1.2" 30 | source = "registry+https://github.com/rust-lang/crates.io-index" 31 | dependencies = [ 32 | "libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 33 | ] 34 | 35 | [[package]] 36 | name = "js" 37 | version = "0.1.0" 38 | source = "git+https://github.com/servo/rust-mozjs#01ff3cb8cb253a8a634cf6aecf64c10bf9c7d8c4" 39 | dependencies = [ 40 | "heapsize 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 41 | "libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 42 | "log 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 43 | "mozjs_sys 0.0.0 (git+https://github.com/servo/mozjs)", 44 | "rustc-serialize 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", 45 | ] 46 | 47 | [[package]] 48 | name = "libc" 49 | version = "0.1.10" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | 52 | [[package]] 53 | name = "libz-sys" 54 | version = "0.1.8" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | dependencies = [ 57 | "gcc 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", 58 | "libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 59 | "pkg-config 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 60 | ] 61 | 62 | [[package]] 63 | name = "log" 64 | version = "0.3.2" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | dependencies = [ 67 | "libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 68 | ] 69 | 70 | [[package]] 71 | name = "mozjs_sys" 72 | version = "0.0.0" 73 | source = "git+https://github.com/servo/mozjs#74e7e954f5445ca0297772d5df543c270339dec2" 74 | dependencies = [ 75 | "libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 76 | "libz-sys 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 77 | ] 78 | 79 | [[package]] 80 | name = "pkg-config" 81 | version = "0.3.6" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | 84 | [[package]] 85 | name = "rustc-serialize" 86 | version = "0.3.16" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | 89 | [[package]] 90 | name = "winapi" 91 | version = "0.2.4" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | 94 | [[package]] 95 | name = "winapi-build" 96 | version = "0.1.1" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | 99 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate js; 2 | extern crate libc; 3 | 4 | use std::sync::mpsc::{Sender, Receiver}; 5 | use std::sync::mpsc; 6 | use std::sync::Arc; 7 | use std::ffi::CStr; 8 | use std::ffi; 9 | use std::ptr; 10 | use std::str; 11 | use std::mem; 12 | use libc::{c_uint, c_void}; 13 | 14 | use js::{JSCLASS_RESERVED_SLOTS_MASK,JSCLASS_RESERVED_SLOTS_SHIFT,JSCLASS_GLOBAL_SLOT_COUNT,JSCLASS_IS_GLOBAL,JSCLASS_IMPLEMENTS_BARRIERS}; 15 | use js::jsapi::JS_GlobalObjectTraceHook; 16 | use js::jsapi::{CallArgs,CompartmentOptions,OnNewGlobalHookOption,Rooted,Value}; 17 | use js::jsapi::{JS_DefineFunction,JS_Init,JS_NewGlobalObject, JS_InitStandardClasses,JS_EncodeStringToUTF8, JS_ReportPendingException, JS_BufferIsCompilableUnit, JS_DestroyContext, JS_DestroyRuntime, JS_ShutDown, CurrentGlobalOrNull, JS_ReportError, JS_SetPrivate}; 18 | use js::jsapi::{JSAutoCompartment,JSAutoRequest,JSContext,JSClass}; 19 | use js::jsapi::{JS_SetGCParameter, JSGCParamKey, JSGCMode}; 20 | use js::jsapi::{RootedValue, RootedObject, HandleObject, HandleValue}; 21 | use js::jsval::UndefinedValue; 22 | use js::rust::Runtime; 23 | 24 | const JSCLASS_HAS_PRIVATE: c_uint = 1 << 0; 25 | 26 | static CLASS: &'static JSClass = &JSClass { 27 | name: b"global\0" as *const u8 as *const libc::c_char, 28 | flags: JSCLASS_IS_GLOBAL | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_PRIVATE | ((JSCLASS_GLOBAL_SLOT_COUNT & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT), 29 | addProperty: None, 30 | delProperty: None, 31 | getProperty: None, 32 | setProperty: None, 33 | enumerate: None, 34 | resolve: None, 35 | convert: None, 36 | finalize: None, 37 | call: None, 38 | hasInstance: None, 39 | construct: None, 40 | trace: Some(JS_GlobalObjectTraceHook), 41 | reserved: [0 as *mut _; 25] 42 | }; 43 | 44 | struct JSOptions { 45 | interactive: bool, 46 | disable_baseline: bool, 47 | disable_ion: bool, 48 | disable_asmjs: bool, 49 | disable_native_regexp: bool, 50 | script: String, 51 | } 52 | 53 | pub struct SMWorker { 54 | ac: JSAutoCompartment, 55 | ar: JSAutoRequest, 56 | cx: *mut JSContext, 57 | runtime: Runtime, 58 | cb: T, 59 | tx: Sender, 60 | rx: Receiver 61 | } 62 | 63 | pub trait Function { 64 | fn callback(&self, message: String); 65 | } 66 | 67 | impl SMWorker where T: Function { 68 | pub fn execute(&self, label: String, script: String) -> Result { 69 | let cx = self.cx; 70 | let global = unsafe { CurrentGlobalOrNull(cx) }; 71 | let global_root = Rooted::new(cx, global); 72 | let global = global_root.handle(); 73 | match self.runtime.evaluate_script(global, script, label, 1) { 74 | Err(_) => unsafe { JS_ReportPendingException(cx); Err("Uncaught exception during script execution") }, 75 | _ => Ok(true) 76 | } 77 | } 78 | 79 | pub fn new(mut cb: T) -> SMWorker { 80 | 81 | let (tx, rx): (Sender, Receiver) = mpsc::channel(); 82 | 83 | unsafe { 84 | JS_Init(); 85 | } 86 | 87 | let mut runtime = Runtime::new(); 88 | let cx = runtime.cx(); 89 | let h_option = OnNewGlobalHookOption::FireOnNewGlobalHook; 90 | let c_option = CompartmentOptions::default(); 91 | let ar = JSAutoRequest::new(cx); 92 | let global = unsafe { JS_NewGlobalObject(cx, CLASS, ptr::null_mut(), h_option, &c_option) }; 93 | let global_root = Rooted::new(cx, global); 94 | let global = global_root.handle(); 95 | let ac = JSAutoCompartment::new(cx, global.get()); 96 | let mut worker = SMWorker { ac: ac, ar: ar, cx: cx, runtime: runtime, cb: cb, tx: tx, rx: rx }; 97 | let cb_ptr: *mut c_void = &mut worker.cb as *mut _ as *mut c_void; 98 | 99 | unsafe { 100 | JS_SetPrivate(global.get(), cb_ptr); 101 | JS_SetGCParameter(worker.runtime.rt(), JSGCParamKey::JSGC_MODE, JSGCMode::JSGC_MODE_INCREMENTAL as u32); 102 | JS_InitStandardClasses(cx, global); 103 | let function = JS_DefineFunction(cx, global, b"_send\0".as_ptr() as *const libc::c_char, 104 | Some(_send), 1, 0); 105 | assert!(!function.is_null()); 106 | } 107 | 108 | worker 109 | } 110 | 111 | } 112 | 113 | unsafe extern "C" fn _send(context: *mut JSContext, argc: u32, vp: *mut Value) -> bool { 114 | let args = CallArgs::from_vp(vp, argc); 115 | 116 | if args._base.argc_ != 1 { 117 | JS_ReportError(context, b"_send() requires exactly 1 argument\0".as_ptr() as *const libc::c_char); 118 | return false; 119 | } 120 | 121 | let arg = args.get(0); 122 | let js = js::rust::ToString(context, arg); 123 | let message_root = Rooted::new(context, js); 124 | let message = JS_EncodeStringToUTF8(context, message_root.handle()); 125 | let message = CStr::from_ptr(message); 126 | /*let cb = recv_cb.unwrap(); 127 | cb(str::from_utf8(message.to_bytes()).unwrap());*/ 128 | println!("{}", str::from_utf8(message.to_bytes()).unwrap()); 129 | 130 | args.rval().set(UndefinedValue()); 131 | return true; 132 | } 133 | --------------------------------------------------------------------------------