isorun 0.1.0.pre-arm64-darwin
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +257 -0
- data/Rakefile +80 -0
- data/app/helpers/isorun/app_helper.rb +38 -0
- data/ext/isorun/Cargo.lock +327 -0
- data/ext/isorun/Cargo.toml +27 -0
- data/ext/isorun/extconf.rb +6 -0
- data/ext/isorun/src/call.js +27 -0
- data/ext/isorun/src/isorun/configure.rs +9 -0
- data/ext/isorun/src/isorun/context.rs +46 -0
- data/ext/isorun/src/isorun/function.rs +60 -0
- data/ext/isorun/src/isorun/mod.rs +7 -0
- data/ext/isorun/src/isorun/module.rs +33 -0
- data/ext/isorun/src/isorun/utils.rs +156 -0
- data/ext/isorun/src/js/mod.rs +3 -0
- data/ext/isorun/src/js/module.rs +51 -0
- data/ext/isorun/src/js/module_item.rs +71 -0
- data/ext/isorun/src/js/worker.rs +265 -0
- data/ext/isorun/src/lib.rs +51 -0
- data/lib/isorun/2.7/isorun.bundle +0 -0
- data/lib/isorun/3.0/isorun.bundle +0 -0
- data/lib/isorun/3.1/isorun.bundle +0 -0
- data/lib/isorun/config/abstract_builder.rb +28 -0
- data/lib/isorun/config/option.rb +82 -0
- data/lib/isorun/config/validations.rb +12 -0
- data/lib/isorun/config.rb +43 -0
- data/lib/isorun/context.rb +84 -0
- data/lib/isorun/engine.rb +20 -0
- data/lib/isorun/function.rb +6 -0
- data/lib/isorun/module.rb +6 -0
- data/lib/isorun/resolver.rb +21 -0
- data/lib/isorun/version.rb +5 -0
- data/lib/isorun.rb +29 -0
- metadata +172 -0
@@ -0,0 +1,156 @@
|
|
1
|
+
use deno_core::error::AnyError;
|
2
|
+
use magnus::r_hash::ForEach;
|
3
|
+
use magnus::value::{Qfalse, Qtrue};
|
4
|
+
use magnus::{
|
5
|
+
Integer, RArray, RFloat, RHash, RString, RStruct, Symbol, Value, QFALSE,
|
6
|
+
QNIL, QTRUE,
|
7
|
+
};
|
8
|
+
use v8::{Array, GetPropertyNamesArgs, HandleScope, Local, Object};
|
9
|
+
|
10
|
+
pub fn convert_v8_to_ruby(
|
11
|
+
value: Local<v8::Value>,
|
12
|
+
scope: &mut HandleScope,
|
13
|
+
) -> Result<Value, AnyError> {
|
14
|
+
if value.is_null() {
|
15
|
+
return Ok(Value::from(QNIL));
|
16
|
+
}
|
17
|
+
|
18
|
+
if value.is_int32() {
|
19
|
+
return Ok(Value::from(Integer::from_i64(
|
20
|
+
value.int32_value(scope).unwrap() as i64,
|
21
|
+
)));
|
22
|
+
}
|
23
|
+
|
24
|
+
if value.is_number() {
|
25
|
+
return Ok(Value::from(
|
26
|
+
RFloat::from_f64(value.number_value(scope).unwrap()).unwrap(),
|
27
|
+
));
|
28
|
+
}
|
29
|
+
|
30
|
+
if value.is_true() {
|
31
|
+
return Ok(Value::from(QTRUE));
|
32
|
+
}
|
33
|
+
|
34
|
+
if value.is_false() {
|
35
|
+
return Ok(Value::from(QFALSE));
|
36
|
+
}
|
37
|
+
|
38
|
+
if value.is_string() {
|
39
|
+
let raw = value.to_rust_string_lossy(scope);
|
40
|
+
return Ok(Value::from(RString::from(raw)));
|
41
|
+
}
|
42
|
+
|
43
|
+
if value.is_array() {
|
44
|
+
let arr = Local::<Array>::try_from(value).unwrap();
|
45
|
+
let length = arr.length();
|
46
|
+
let r_arr = RArray::with_capacity(length as usize);
|
47
|
+
for i in 0..length {
|
48
|
+
let raw = arr.get_index(scope, i).unwrap();
|
49
|
+
let val = convert_v8_to_ruby(raw, scope).unwrap();
|
50
|
+
r_arr.push(val).expect("cannot add item to array");
|
51
|
+
}
|
52
|
+
return Ok(Value::from(r_arr));
|
53
|
+
}
|
54
|
+
|
55
|
+
if value.is_object() {
|
56
|
+
let obj = Local::<Object>::try_from(value).unwrap();
|
57
|
+
let properties = obj
|
58
|
+
.get_own_property_names(scope, GetPropertyNamesArgs::default())
|
59
|
+
.unwrap();
|
60
|
+
let length = properties.length();
|
61
|
+
let r_hash = RHash::new();
|
62
|
+
for i in 0..length {
|
63
|
+
let raw_key = properties.get_index(scope, i).unwrap();
|
64
|
+
let raw_val = obj.get(scope, raw_key).unwrap();
|
65
|
+
let key = convert_v8_to_ruby(raw_key, scope).unwrap();
|
66
|
+
let val = convert_v8_to_ruby(raw_val, scope).unwrap();
|
67
|
+
r_hash.aset(key, val).expect("cannot set item to hash");
|
68
|
+
}
|
69
|
+
return Ok(Value::from(r_hash));
|
70
|
+
}
|
71
|
+
|
72
|
+
Ok(Value::from(QNIL))
|
73
|
+
}
|
74
|
+
|
75
|
+
pub fn convert_ruby_to_v8<'s>(
|
76
|
+
value: Value,
|
77
|
+
scope: &mut HandleScope<'s>,
|
78
|
+
) -> Result<Local<'s, v8::Value>, AnyError> {
|
79
|
+
if value.is_nil() {
|
80
|
+
return Ok(v8::null(scope).into());
|
81
|
+
}
|
82
|
+
|
83
|
+
if let Some(v) = Qtrue::from_value(value) {
|
84
|
+
return Ok(v8::Boolean::new(scope, v.to_bool()).into());
|
85
|
+
}
|
86
|
+
|
87
|
+
if let Some(v) = Qfalse::from_value(value) {
|
88
|
+
return Ok(v8::Boolean::new(scope, v.to_bool()).into());
|
89
|
+
}
|
90
|
+
|
91
|
+
if let Some(v) = Symbol::from_value(value) {
|
92
|
+
return Ok(v8::String::new(scope, v.to_string().as_str())
|
93
|
+
.unwrap()
|
94
|
+
.into());
|
95
|
+
}
|
96
|
+
|
97
|
+
if let Some(v) = Integer::from_value(value) {
|
98
|
+
return Ok(v8::Integer::new(scope, v.to_i32().unwrap()).into());
|
99
|
+
}
|
100
|
+
|
101
|
+
if let Some(v) = RFloat::from_value(value) {
|
102
|
+
return Ok(v8::Number::new(scope, v.to_f64()).into());
|
103
|
+
}
|
104
|
+
|
105
|
+
if let Some(v) = RString::from_value(value) {
|
106
|
+
return Ok(v8::String::new(scope, v.to_string().unwrap().as_str())
|
107
|
+
.unwrap()
|
108
|
+
.into());
|
109
|
+
}
|
110
|
+
|
111
|
+
if let Some(v) = RArray::from_value(value) {
|
112
|
+
let arr;
|
113
|
+
{
|
114
|
+
arr = Array::new(scope, v.len() as i32);
|
115
|
+
}
|
116
|
+
|
117
|
+
for (i, val) in v.each().enumerate() {
|
118
|
+
let v8_value;
|
119
|
+
{
|
120
|
+
v8_value = convert_ruby_to_v8(val.unwrap(), scope).unwrap();
|
121
|
+
}
|
122
|
+
arr.set_index(scope, i as u32, v8_value);
|
123
|
+
}
|
124
|
+
return Ok(arr.into());
|
125
|
+
}
|
126
|
+
|
127
|
+
if let Some(v) = RHash::from_value(value) {
|
128
|
+
let obj = Object::new(scope);
|
129
|
+
v.foreach(|key: Value, val: Value| {
|
130
|
+
let key = convert_ruby_to_v8(key, scope).unwrap();
|
131
|
+
let val = convert_ruby_to_v8(val, scope).unwrap();
|
132
|
+
obj.set(scope, key, val);
|
133
|
+
|
134
|
+
Ok(ForEach::Continue)
|
135
|
+
})
|
136
|
+
.expect("cannot convert hash into JavaScript object");
|
137
|
+
|
138
|
+
return Ok(obj.into());
|
139
|
+
}
|
140
|
+
|
141
|
+
if let Some(v) = RStruct::from_value(value) {
|
142
|
+
let obj = Object::new(scope);
|
143
|
+
for member in v.members().unwrap() {
|
144
|
+
let key = member.to_string();
|
145
|
+
let val = v.getmember::<&str, Value>(key.as_str()).unwrap();
|
146
|
+
let v8_key = v8::String::new(scope, key.as_str()).unwrap();
|
147
|
+
let v8_val = convert_ruby_to_v8(val, scope).unwrap();
|
148
|
+
|
149
|
+
obj.set(scope, v8_key.into(), v8_val);
|
150
|
+
}
|
151
|
+
|
152
|
+
return Ok(obj.into());
|
153
|
+
}
|
154
|
+
|
155
|
+
Ok(v8::null(scope).into())
|
156
|
+
}
|
@@ -0,0 +1,51 @@
|
|
1
|
+
use crate::js::module_item::{Function, ModuleItem, Value as JsValue};
|
2
|
+
use crate::js::worker::WORKER;
|
3
|
+
use deno_core::error::AnyError;
|
4
|
+
use deno_core::{JsRealm, ModuleId};
|
5
|
+
use std::cell::RefCell;
|
6
|
+
use std::rc::Rc;
|
7
|
+
use v8::{Global, Local, Value};
|
8
|
+
|
9
|
+
pub(crate) struct Module {
|
10
|
+
pub(crate) id: ModuleId,
|
11
|
+
pub(crate) realm: Rc<RefCell<JsRealm>>,
|
12
|
+
}
|
13
|
+
|
14
|
+
impl Module {
|
15
|
+
pub(crate) fn import(
|
16
|
+
&self,
|
17
|
+
export_name: &str,
|
18
|
+
) -> Result<ModuleItem, AnyError> {
|
19
|
+
WORKER.with(|worker| {
|
20
|
+
let namespace = {
|
21
|
+
let mut worker = worker.worker.borrow_mut();
|
22
|
+
worker.js_runtime.get_module_namespace(self.id).unwrap()
|
23
|
+
};
|
24
|
+
|
25
|
+
let realm = self.realm.borrow();
|
26
|
+
|
27
|
+
let mut worker = worker.worker.borrow_mut();
|
28
|
+
let mut scope = realm.handle_scope(worker.js_runtime.v8_isolate());
|
29
|
+
|
30
|
+
let namespace = Local::new(&mut scope, namespace);
|
31
|
+
|
32
|
+
let export_name = v8::String::new(&mut scope, export_name).unwrap();
|
33
|
+
|
34
|
+
let binding =
|
35
|
+
namespace.get(&mut scope, export_name.into()).unwrap();
|
36
|
+
let global_binding = Global::<Value>::new(&mut scope, binding);
|
37
|
+
|
38
|
+
if binding.is_function() {
|
39
|
+
Ok(ModuleItem::Function(Function {
|
40
|
+
binding: global_binding,
|
41
|
+
realm: self.realm.clone(),
|
42
|
+
}))
|
43
|
+
} else {
|
44
|
+
Ok(ModuleItem::Value(JsValue {
|
45
|
+
binding: global_binding,
|
46
|
+
realm: self.realm.clone(),
|
47
|
+
}))
|
48
|
+
}
|
49
|
+
})
|
50
|
+
}
|
51
|
+
}
|
@@ -0,0 +1,71 @@
|
|
1
|
+
use crate::js::worker::WORKER;
|
2
|
+
use deno_core::JsRealm;
|
3
|
+
use magnus::gvl::without_gvl;
|
4
|
+
use std::cell::RefCell;
|
5
|
+
use std::ops::Deref;
|
6
|
+
use std::rc::Rc;
|
7
|
+
use v8::Global;
|
8
|
+
|
9
|
+
pub(crate) enum ModuleItem {
|
10
|
+
Value(Value),
|
11
|
+
Function(Function),
|
12
|
+
}
|
13
|
+
|
14
|
+
pub(crate) struct Function {
|
15
|
+
pub(crate) binding: Global<v8::Value>,
|
16
|
+
pub(crate) realm: Rc<RefCell<JsRealm>>,
|
17
|
+
}
|
18
|
+
|
19
|
+
impl Function {
|
20
|
+
pub(crate) fn call(
|
21
|
+
&self,
|
22
|
+
args: &[Global<v8::Value>],
|
23
|
+
) -> Result<magnus::Value, magnus::Error> {
|
24
|
+
WORKER.with(|worker| {
|
25
|
+
let realm = self.realm.borrow();
|
26
|
+
let realm = realm.deref();
|
27
|
+
worker
|
28
|
+
.runtime
|
29
|
+
// we block here instead of the worker, due to a refcell issue
|
30
|
+
// when borrowing within an await
|
31
|
+
.block_on(worker.call(realm, &self.binding, args))
|
32
|
+
})
|
33
|
+
}
|
34
|
+
|
35
|
+
pub(crate) fn call_without_gvl(
|
36
|
+
&self,
|
37
|
+
args: &[Global<v8::Value>],
|
38
|
+
) -> Result<magnus::Value, magnus::Error> {
|
39
|
+
WORKER.with(|worker| {
|
40
|
+
let realm = self.realm.borrow();
|
41
|
+
let realm = realm.deref();
|
42
|
+
let result = without_gvl(
|
43
|
+
|gvl_context| {
|
44
|
+
worker.ruby_context.replace(Some(gvl_context));
|
45
|
+
let result = worker
|
46
|
+
.runtime
|
47
|
+
// we block here instead of the worker, due to a refcell issue
|
48
|
+
// when borrowing within an await
|
49
|
+
.block_on(worker.call(realm, &self.binding, args));
|
50
|
+
worker.ruby_context.replace(None);
|
51
|
+
result
|
52
|
+
},
|
53
|
+
None::<fn()>,
|
54
|
+
);
|
55
|
+
result.0.unwrap()
|
56
|
+
})
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
pub(crate) struct Value {
|
61
|
+
pub(crate) binding: Global<v8::Value>,
|
62
|
+
pub(crate) realm: Rc<RefCell<JsRealm>>,
|
63
|
+
}
|
64
|
+
|
65
|
+
impl Value {
|
66
|
+
pub(crate) fn to_ruby(&self) -> Option<magnus::Value> {
|
67
|
+
let realm = self.realm.borrow();
|
68
|
+
let realm = realm.deref();
|
69
|
+
WORKER.with(|worker| worker.to_ruby(realm, &self.binding))
|
70
|
+
}
|
71
|
+
}
|
@@ -0,0 +1,265 @@
|
|
1
|
+
use crate::isorun::utils::{convert_ruby_to_v8, convert_v8_to_ruby};
|
2
|
+
use deno_core::error::AnyError;
|
3
|
+
use deno_core::serde_v8::from_v8;
|
4
|
+
use deno_core::{op, serde_v8, Extension, FsModuleLoader, JsRealm, ModuleId};
|
5
|
+
use deno_runtime::deno_broadcast_channel::InMemoryBroadcastChannel;
|
6
|
+
use deno_runtime::permissions::Permissions;
|
7
|
+
use deno_runtime::worker::{MainWorker, WorkerOptions};
|
8
|
+
use deno_runtime::BootstrapOptions;
|
9
|
+
use deno_web::BlobStore;
|
10
|
+
use magnus::block::Proc;
|
11
|
+
use magnus::gvl::GVLContext;
|
12
|
+
use magnus::{Error, Value};
|
13
|
+
use std::borrow::BorrowMut;
|
14
|
+
use std::cell::RefCell;
|
15
|
+
use std::collections::{HashMap, HashSet};
|
16
|
+
use std::path::Path;
|
17
|
+
use std::rc::Rc;
|
18
|
+
use std::string::ToString;
|
19
|
+
use std::sync::Arc;
|
20
|
+
use tokio::runtime::Runtime;
|
21
|
+
use v8::{Global, Local};
|
22
|
+
|
23
|
+
fn get_error_class_name(e: &AnyError) -> &'static str {
|
24
|
+
deno_runtime::errors::get_error_class_name(e).unwrap_or("Error")
|
25
|
+
}
|
26
|
+
|
27
|
+
const USER_AGENT: &str = "isorun";
|
28
|
+
|
29
|
+
pub(crate) struct Worker {
|
30
|
+
pub(crate) runtime: Runtime,
|
31
|
+
pub(crate) worker: RefCell<MainWorker>,
|
32
|
+
module_map: RefCell<HashMap<String, ModuleId>>,
|
33
|
+
pub(crate) ruby_context: RefCell<Option<GVLContext>>,
|
34
|
+
pub(crate) ruby_receiver: RefCell<Option<Proc>>,
|
35
|
+
}
|
36
|
+
|
37
|
+
impl Worker {
|
38
|
+
pub(crate) fn create_realm(&self) -> Result<JsRealm, AnyError> {
|
39
|
+
let mut worker = self.worker.borrow_mut();
|
40
|
+
worker.js_runtime.create_realm()
|
41
|
+
}
|
42
|
+
|
43
|
+
pub(crate) fn load_module(&self, path: &str) -> Result<ModuleId, AnyError> {
|
44
|
+
let mut module_map = self.module_map.borrow_mut();
|
45
|
+
if module_map.contains_key(path) {
|
46
|
+
return Ok(*module_map.get(path).unwrap());
|
47
|
+
}
|
48
|
+
|
49
|
+
let module_id = {
|
50
|
+
let mut worker = self.worker.borrow_mut();
|
51
|
+
|
52
|
+
let module_specifier =
|
53
|
+
deno_core::resolve_url_or_path(path).unwrap();
|
54
|
+
let module_id = self
|
55
|
+
.runtime
|
56
|
+
.block_on(worker.preload_side_module(&module_specifier))?;
|
57
|
+
self.runtime.block_on(worker.evaluate_module(module_id))?;
|
58
|
+
|
59
|
+
module_id
|
60
|
+
};
|
61
|
+
|
62
|
+
module_map.insert(path.to_string(), module_id);
|
63
|
+
|
64
|
+
Ok(module_id)
|
65
|
+
}
|
66
|
+
|
67
|
+
pub(crate) async fn call(
|
68
|
+
&self,
|
69
|
+
realm: &JsRealm,
|
70
|
+
callee: &Global<v8::Value>,
|
71
|
+
args: &[Global<v8::Value>],
|
72
|
+
) -> Result<Value, Error> {
|
73
|
+
let promise = {
|
74
|
+
let mut worker = self.worker.borrow_mut();
|
75
|
+
let mut scope = realm.handle_scope(worker.js_runtime.v8_isolate());
|
76
|
+
|
77
|
+
let callee = Local::<v8::Value>::new(&mut scope, callee);
|
78
|
+
let callee = Local::<v8::Function>::try_from(callee).unwrap();
|
79
|
+
|
80
|
+
let mut local_args: Vec<Local<v8::Value>> = vec![];
|
81
|
+
for arg in args {
|
82
|
+
let local_arg = Local::<v8::Value>::new(&mut scope, arg);
|
83
|
+
local_args.push(local_arg);
|
84
|
+
}
|
85
|
+
let receiver = v8::undefined(scope.borrow_mut());
|
86
|
+
let promise = callee
|
87
|
+
.call(&mut scope, receiver.into(), local_args.as_slice())
|
88
|
+
.unwrap();
|
89
|
+
Global::<v8::Value>::new(&mut scope, promise)
|
90
|
+
};
|
91
|
+
|
92
|
+
let value = {
|
93
|
+
let mut worker = self.worker.borrow_mut();
|
94
|
+
worker.js_runtime.resolve_value(promise).await.unwrap()
|
95
|
+
};
|
96
|
+
|
97
|
+
let value = self.to_ruby(realm, &value).unwrap();
|
98
|
+
|
99
|
+
Ok(value)
|
100
|
+
}
|
101
|
+
|
102
|
+
pub(crate) fn to_ruby(
|
103
|
+
&self,
|
104
|
+
realm: &JsRealm,
|
105
|
+
value: &Global<v8::Value>,
|
106
|
+
) -> Option<Value> {
|
107
|
+
let mut worker = self.worker.borrow_mut();
|
108
|
+
let mut scope = realm.handle_scope(worker.js_runtime.v8_isolate());
|
109
|
+
let value = Local::new(&mut scope, value);
|
110
|
+
let result = convert_v8_to_ruby(value, &mut scope);
|
111
|
+
|
112
|
+
match result {
|
113
|
+
Ok(v) => Some(v),
|
114
|
+
Err(_) => None,
|
115
|
+
}
|
116
|
+
}
|
117
|
+
|
118
|
+
pub(crate) fn to_v8(
|
119
|
+
&self,
|
120
|
+
realm: &JsRealm,
|
121
|
+
value: Value,
|
122
|
+
) -> Option<Global<v8::Value>> {
|
123
|
+
let mut worker = self.worker.borrow_mut();
|
124
|
+
let mut scope = realm.handle_scope(worker.js_runtime.v8_isolate());
|
125
|
+
let value = convert_ruby_to_v8(value, &mut scope).unwrap();
|
126
|
+
let value = Global::<v8::Value>::new(&mut scope, value);
|
127
|
+
|
128
|
+
Some(value)
|
129
|
+
}
|
130
|
+
|
131
|
+
fn send(&self, value: Value) -> Result<Value, Error> {
|
132
|
+
// we need to deref the receiver as mut, as it is behind an Option
|
133
|
+
if let (Some(ctx), Some(rec)) = (
|
134
|
+
self.ruby_context.borrow_mut().as_mut(),
|
135
|
+
self.ruby_receiver.borrow_mut().as_mut(),
|
136
|
+
) {
|
137
|
+
ctx.with_gvl(|| {
|
138
|
+
let args: (Value,) = (value,);
|
139
|
+
rec.call::<(Value,), Value>(args)
|
140
|
+
})?
|
141
|
+
} else {
|
142
|
+
Err(Error::runtime_error(
|
143
|
+
"Cannot send to ruby. Is the ruby receiver and context initialized and set?",
|
144
|
+
))
|
145
|
+
}
|
146
|
+
}
|
147
|
+
}
|
148
|
+
|
149
|
+
impl Default for Worker {
|
150
|
+
fn default() -> Self {
|
151
|
+
let module_loader = Rc::new(FsModuleLoader);
|
152
|
+
let create_web_worker_cb = Arc::new(|_| {
|
153
|
+
todo!("Web workers are not supported in the example");
|
154
|
+
});
|
155
|
+
let web_worker_event_cb = Arc::new(|_| {
|
156
|
+
todo!("Web workers are not supported in the example");
|
157
|
+
});
|
158
|
+
|
159
|
+
let extension_send = Extension::builder()
|
160
|
+
.ops(vec![op_send_to_ruby::decl()])
|
161
|
+
.build();
|
162
|
+
let mut extensions = vec![extension_send];
|
163
|
+
|
164
|
+
let options = WorkerOptions {
|
165
|
+
bootstrap: BootstrapOptions {
|
166
|
+
args: vec![],
|
167
|
+
cpu_count: 1,
|
168
|
+
debug_flag: false,
|
169
|
+
enable_testing_features: false,
|
170
|
+
locale: v8::icu::get_language_tag(),
|
171
|
+
location: None,
|
172
|
+
no_color: false,
|
173
|
+
is_tty: false,
|
174
|
+
runtime_version: "x".to_string(),
|
175
|
+
ts_version: "x".to_string(),
|
176
|
+
unstable: false,
|
177
|
+
user_agent: USER_AGENT.to_string(),
|
178
|
+
inspect: false,
|
179
|
+
},
|
180
|
+
extensions: std::mem::take(&mut extensions),
|
181
|
+
startup_snapshot: None,
|
182
|
+
unsafely_ignore_certificate_errors: None,
|
183
|
+
root_cert_store: None,
|
184
|
+
seed: None,
|
185
|
+
source_map_getter: None,
|
186
|
+
format_js_error_fn: None,
|
187
|
+
web_worker_preload_module_cb: web_worker_event_cb.clone(),
|
188
|
+
web_worker_pre_execute_module_cb: web_worker_event_cb,
|
189
|
+
create_web_worker_cb,
|
190
|
+
maybe_inspector_server: None,
|
191
|
+
should_break_on_first_statement: false,
|
192
|
+
module_loader,
|
193
|
+
npm_resolver: None,
|
194
|
+
get_error_class_fn: Some(&get_error_class_name),
|
195
|
+
cache_storage_dir: None,
|
196
|
+
origin_storage_dir: None,
|
197
|
+
blob_store: BlobStore::default(),
|
198
|
+
broadcast_channel: InMemoryBroadcastChannel::default(),
|
199
|
+
shared_array_buffer_store: None,
|
200
|
+
compiled_wasm_module_store: None,
|
201
|
+
stdio: Default::default(),
|
202
|
+
};
|
203
|
+
|
204
|
+
// todo: we don't use the main module at all, but it could be used as an
|
205
|
+
// entry point for "eval" JavaScript.
|
206
|
+
let js_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("src/call.js");
|
207
|
+
let main_module =
|
208
|
+
deno_core::resolve_path(&js_path.to_string_lossy()).unwrap();
|
209
|
+
let permissions = Permissions::allow_all();
|
210
|
+
let mut worker = MainWorker::bootstrap_from_options(
|
211
|
+
main_module.clone(),
|
212
|
+
permissions,
|
213
|
+
options,
|
214
|
+
);
|
215
|
+
|
216
|
+
let runtime = tokio::runtime::Builder::new_current_thread()
|
217
|
+
.enable_all()
|
218
|
+
.build()
|
219
|
+
.unwrap();
|
220
|
+
|
221
|
+
runtime.block_on(async {
|
222
|
+
let module_id =
|
223
|
+
worker.preload_main_module(&main_module).await.unwrap();
|
224
|
+
worker
|
225
|
+
.evaluate_module(module_id)
|
226
|
+
.await
|
227
|
+
.expect("cannot evaluate core module");
|
228
|
+
});
|
229
|
+
|
230
|
+
Worker {
|
231
|
+
runtime,
|
232
|
+
module_map: RefCell::from(HashMap::default()),
|
233
|
+
worker: RefCell::from(worker),
|
234
|
+
ruby_context: RefCell::from(None),
|
235
|
+
ruby_receiver: RefCell::from(None),
|
236
|
+
}
|
237
|
+
}
|
238
|
+
}
|
239
|
+
|
240
|
+
thread_local! {
|
241
|
+
pub(crate) static WORKER: Worker = Worker::default();
|
242
|
+
pub(crate) static MODULE_MAP: HashSet<ModuleId> = HashSet::default();
|
243
|
+
}
|
244
|
+
|
245
|
+
#[allow(clippy::extra_unused_lifetimes)]
|
246
|
+
#[op(v8)]
|
247
|
+
fn op_send_to_ruby<'a>(
|
248
|
+
// do not remove the v8:: prefix, otherwise the macro complains
|
249
|
+
scope: &mut v8::HandleScope,
|
250
|
+
data: serde_v8::Value<'a>,
|
251
|
+
) -> Result<serde_v8::Value<'a>, AnyError> {
|
252
|
+
let value = convert_v8_to_ruby(data.v8_value, scope)?;
|
253
|
+
|
254
|
+
WORKER.with(|worker| {
|
255
|
+
worker
|
256
|
+
.send(value)
|
257
|
+
.map(|v| {
|
258
|
+
let v = convert_ruby_to_v8(v, scope).unwrap();
|
259
|
+
from_v8(scope, v).unwrap()
|
260
|
+
})
|
261
|
+
.map_err(|error| {
|
262
|
+
AnyError::msg(format!("failed to send to ruby: {}", error))
|
263
|
+
})
|
264
|
+
})
|
265
|
+
}
|
@@ -0,0 +1,51 @@
|
|
1
|
+
use crate::isorun::context::Context;
|
2
|
+
use isorun::configure::set_receiver;
|
3
|
+
use isorun::function::Function;
|
4
|
+
use isorun::module::Module;
|
5
|
+
use magnus::{define_module, function, method, Error, Module as M, Object};
|
6
|
+
|
7
|
+
mod isorun;
|
8
|
+
mod js;
|
9
|
+
|
10
|
+
#[magnus::init]
|
11
|
+
fn init() -> Result<(), Error> {
|
12
|
+
let root = define_module("Isorun").expect("cannot define module: Isorun");
|
13
|
+
|
14
|
+
root.define_module_function("receiver=", function!(set_receiver, 1))
|
15
|
+
.expect("cannot define module function: receiver=");
|
16
|
+
|
17
|
+
let context = root
|
18
|
+
.define_class("Context", Default::default())
|
19
|
+
.expect("cannot define class: Isorun::Context");
|
20
|
+
context
|
21
|
+
.define_singleton_method("new", function!(Context::new, 0))
|
22
|
+
.expect("cannot define singelton method: new");
|
23
|
+
context
|
24
|
+
.define_method("load", method!(Context::load, 1))
|
25
|
+
.expect("cannot load module");
|
26
|
+
|
27
|
+
let module = root
|
28
|
+
.define_class("Module", Default::default())
|
29
|
+
.expect("cannot define class: Isorun::Module");
|
30
|
+
module
|
31
|
+
.define_private_method("id", method!(Module::id, 0))
|
32
|
+
.expect("cannot define method: module_id");
|
33
|
+
module
|
34
|
+
.define_method("import", method!(Module::import, 1))
|
35
|
+
.expect("cannot define method: import");
|
36
|
+
|
37
|
+
let function = root
|
38
|
+
.define_class("Function", Default::default())
|
39
|
+
.expect("cannot define class: Isorun::Function");
|
40
|
+
function
|
41
|
+
.define_method("call", method!(Function::call, -1))
|
42
|
+
.expect("cannot define method: call");
|
43
|
+
function
|
44
|
+
.define_method(
|
45
|
+
"call_without_gvl",
|
46
|
+
method!(Function::call_without_gvl, -1),
|
47
|
+
)
|
48
|
+
.expect("cannot define method: call_without_gvl");
|
49
|
+
|
50
|
+
Ok(())
|
51
|
+
}
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Isorun
|
4
|
+
class Config
|
5
|
+
# Abstract base class for isorun and it's extensions configuration
|
6
|
+
# builder. Instantiates and validates gem configuration.
|
7
|
+
#
|
8
|
+
class AbstractBuilder
|
9
|
+
attr_reader :config
|
10
|
+
|
11
|
+
# @param [Class] config class
|
12
|
+
#
|
13
|
+
def initialize(config = Config.new, &block)
|
14
|
+
@config = config
|
15
|
+
instance_eval(&block)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Builds and validates configuration.
|
19
|
+
#
|
20
|
+
# @return [Isorun::Config] config instance
|
21
|
+
#
|
22
|
+
def build
|
23
|
+
@config.validate! if @config.respond_to?(:validate!)
|
24
|
+
@config
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|