1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
use std::path::Path;
use deno_core::{ErrBox, JsRuntime, OpState, RuntimeOptions, ZeroCopyBuf};
use serde::de::DeserializeOwned;
use serde::Serialize;
use crate::JsValue;
pub struct Script {
runtime: JsRuntime,
last_rid: u32,
}
impl Script {
const DEFAULT_FILENAME: &'static str = "sandboxed.js";
pub fn from_string(js_code: &str) -> Result<Self, ErrBox> {
let all_code = "const console = { log: function(expr) { Deno.core.print(expr + '\\n', false); } };".to_string() + js_code;
Self::create_script(&all_code, Self::DEFAULT_FILENAME)
}
pub fn from_file(file: impl AsRef<Path>) -> Result<Self, ErrBox> {
let filename = file.as_ref().file_name().and_then(|s| s.to_str()).unwrap_or(Self::DEFAULT_FILENAME).to_owned();
match std::fs::read_to_string(file) {
Ok(js_code) => {
Self::create_script(&js_code, &filename)
}
Err(e) => {
Err(ErrBox::from(e))
}
}
}
pub fn call<P, R>(&mut self, fn_name: &str, args: &P) -> Result<R, ErrBox>
where P: Serialize, R: DeserializeOwned
{
let json_args = serde_json::to_value(args)?;
let json_result = self.call_json(fn_name, &json_args)?;
let result: R = serde_json::from_value(json_result)?;
Ok(result)
}
pub(crate) fn call_json(&mut self, fn_name: &str, args: &JsValue) -> Result<JsValue, ErrBox> {
let js_code = format!("{{
let __rust_result = {f}({a});
if (typeof __rust_result === 'undefined')
__rust_result = null;
Deno.core.ops();
Deno.core.jsonOpSync(\"__rust_return\", __rust_result);\
}}", f = fn_name, a = args);
self.runtime.execute(Self::DEFAULT_FILENAME, &js_code)?;
let state_rc = self.runtime.op_state();
let mut state = state_rc.borrow_mut();
let table = &mut state.resource_table;
let mut result: Box<JsValue> = table.remove(self.last_rid).unwrap();
self.last_rid += 1;
Ok(result.take())
}
fn create_script(js_code: &str, js_filename: &str) -> Result<Self, ErrBox> {
let options = RuntimeOptions {
module_loader: None,
startup_snapshot: None,
will_snapshot: false,
heap_limits: None,
};
let mut runtime = JsRuntime::new(options);
runtime.execute(js_filename, &js_code)?;
runtime.register_op("__rust_return", deno_core::json_op_sync(Self::op_return));
Ok(Script { runtime, last_rid: 0 })
}
fn op_return(
state: &mut OpState,
args: JsValue,
_buf: &mut [ZeroCopyBuf],
) -> Result<JsValue, ErrBox> {
let resource_table = &mut state.resource_table;
let _rid = resource_table.add("result", Box::new(args));
Ok(serde_json::Value::Null)
}
}