Store the WASM id in the VirtualMachine, add a (broken) fetch builtin

This commit is contained in:
coolreader18 committed 2019-02-16 23:41:41 -06:00
1 parent 3e22c4f146
commit a86069d9f2
8 files changed
+144 -72

No files matched your search

+1 -1
View File
@@ -28,7 +28,7 @@ extern crate rustpython_parser;
// This is above everything else so that the defined macros are available everywhere
#[macro_use]
mod macros;
pub mod macros;
mod builtins;
pub mod bytecode;
+5
View File
@@ -1,14 +1,17 @@
// see: https://danielkeep.github.io/tlborm/book/blk-counting.html
#[macro_export]
macro_rules! replace_expr {
($_t:tt $sub:expr) => {
$sub
};
}
#[macro_export]
macro_rules! count_tts {
($($tts:tt)*) => {0usize $(+ replace_expr!($tts 1usize))*};
}
#[macro_export]
macro_rules! type_check {
($vm:ident, $args:ident, $arg_count:ident, $arg_name:ident, $arg_type:expr) => {
// None indicates that we have no type requirement (i.e. we accept any type)
@@ -30,6 +33,7 @@ macro_rules! type_check {
};
}
#[macro_export]
macro_rules! arg_check {
( $vm: ident, $args:ident ) => {
// Zero-arg case
@@ -85,6 +89,7 @@ macro_rules! arg_check {
};
}
#[macro_export]
macro_rules! no_kwargs {
( $vm: ident, $args:ident ) => {
// Zero-arg case
+2
View File
@@ -39,6 +39,7 @@ pub struct VirtualMachine {
pub stdlib_inits: HashMap<String, stdlib::StdlibInitFunc>,
pub ctx: PyContext,
pub current_frame: Option<PyObjectRef>,
pub wasm_id: Option<String>,
}
impl VirtualMachine {
@@ -61,6 +62,7 @@ impl VirtualMachine {
stdlib_inits,
ctx,
current_frame: None,
wasm_id: None,
}
}
+8 -1
View File
@@ -19,4 +19,11 @@ console_error_panic_hook = "0.1"
[dependencies.web-sys]
version = "0.3"
features = [ "console", "Document", "Element", "HtmlTextAreaElement", "Window" ]
features = [
"console",
"Document",
"Element",
"HtmlTextAreaElement",
"Window",
"Response"
]
+19 -39
View File
@@ -9,12 +9,11 @@ pub fn py_str_err(vm: &mut VirtualMachine, py_err: &PyObjectRef) -> String {
.unwrap_or_else(|_| "Error, and error getting error message".into())
}
pub fn py_to_js(
vm: &mut VirtualMachine,
py_obj: PyObjectRef,
wasm_vm: Option<WASMVirtualMachine>,
) -> JsValue {
if let Some(wasm_vm) = wasm_vm {
pub fn py_to_js(vm: &mut VirtualMachine, py_obj: PyObjectRef) -> JsValue {
if let Some(ref wasm_id) = vm.wasm_id {
let wasm_vm = WASMVirtualMachine {
id: wasm_id.clone(),
};
if rustpython_vm::obj::objtype::isinstance(&py_obj, &vm.ctx.function_type()) {
let closure =
move |args: Option<Array>, kwargs: Option<Object>| -> Result<JsValue, JsValue> {
@@ -27,22 +26,19 @@ pub fn py_to_js(
let mut py_func_args = rustpython_vm::pyobject::PyFuncArgs::default();
if let Some(ref args) = args {
for arg in args.values() {
py_func_args
.args
.push(js_to_py(vm, arg?, Some(wasm_vm.clone())));
py_func_args.args.push(js_to_py(vm, arg?));
}
}
if let Some(ref kwargs) = kwargs {
for pair in object_entries(kwargs) {
let (key, val) = pair?;
py_func_args.kwargs.push((
js_sys::JsString::from(key).into(),
js_to_py(vm, val, Some(wasm_vm.clone())),
));
py_func_args
.kwargs
.push((js_sys::JsString::from(key).into(), js_to_py(vm, val)));
}
}
let result = vm.invoke(py_obj.clone(), py_func_args);
pyresult_to_jsresult(vm, result, Some(wasm_vm.clone()))
pyresult_to_jsresult(vm, result)
};
let closure = Closure::wrap(Box::new(closure)
as Box<dyn Fn(Option<Array>, Option<Object>) -> Result<JsValue, JsValue>>);
@@ -80,36 +76,20 @@ pub fn object_entries(obj: &Object) -> impl Iterator<Item = Result<(JsValue, JsV
})
}
pub fn pyresult_to_jsresult(
vm: &mut VirtualMachine,
result: PyResult,
wasm_vm: Option<WASMVirtualMachine>,
) -> Result<JsValue, JsValue> {
pub fn pyresult_to_jsresult(vm: &mut VirtualMachine, result: PyResult) -> Result<JsValue, JsValue> {
result
.map(|value| py_to_js(vm, value, wasm_vm))
.map(|value| py_to_js(vm, value))
.map_err(|err| py_str_err(vm, &err).into())
}
pub fn js_to_py(
vm: &mut VirtualMachine,
js_val: JsValue,
// Accept a WASM VM because if js_val is a function it has to be able to convert
// its arguments to JS, and those arguments might include a closure
wasm_vm: Option<WASMVirtualMachine>,
) -> PyObjectRef {
pub fn js_to_py(vm: &mut VirtualMachine, js_val: JsValue) -> PyObjectRef {
if js_val.is_object() {
if Array::is_array(&js_val) {
let js_arr: Array = js_val.into();
let elems = js_arr
.values()
.into_iter()
.map(|val| {
js_to_py(
vm,
val.expect("Iteration over array failed"),
wasm_vm.clone(),
)
})
.map(|val| js_to_py(vm, val.expect("Iteration over array failed")))
.collect();
vm.ctx.new_list(elems)
} else {
@@ -118,7 +98,7 @@ pub fn js_to_py(
let pair = pair.expect("Iteration over object failed");
let key = Reflect::get(&pair, &"0".into()).unwrap();
let val = Reflect::get(&pair, &"1".into()).unwrap();
let py_val = js_to_py(vm, val, wasm_vm.clone());
let py_val = js_to_py(vm, val);
vm.ctx
.set_item(&dict, &String::from(js_sys::JsString::from(key)), py_val);
}
@@ -131,16 +111,16 @@ pub fn js_to_py(
let func = func.clone();
let this = Object::new();
for (k, v) in args.kwargs {
Reflect::set(&this, &k.into(), &py_to_js(vm, v, wasm_vm.clone()))
Reflect::set(&this, &k.into(), &py_to_js(vm, v))
.expect("Couldn't set this property");
}
let js_args = Array::new();
for v in args.args {
js_args.push(&py_to_js(vm, v, wasm_vm.clone()));
js_args.push(&py_to_js(vm, v));
}
func.apply(&this, &js_args)
.map(|val| js_to_py(vm, val, wasm_vm.clone()))
.map_err(|err| js_to_py(vm, err, wasm_vm.clone()))
.map(|val| js_to_py(vm, val))
.map_err(|err| js_to_py(vm, err))
},
)
} else if let Some(err) = js_val.dyn_ref::<js_sys::Error>() {
+4 -3
View File
@@ -3,6 +3,7 @@ mod vm_class;
mod wasm_builtins;
extern crate js_sys;
#[macro_use]
extern crate rustpython_vm;
extern crate wasm_bindgen;
extern crate web_sys;
@@ -107,7 +108,7 @@ pub fn eval_py(source: &str, options: Option<Object>) -> Result<JsValue, JsValue
&JsValue::UNDEFINED,
&wasm_builtins::format_print_args(vm, args)?.into(),
)
.map_err(|err| convert::js_to_py(vm, err, None))?;
.map_err(|err| convert::js_to_py(vm, err))?;
Ok(vm.get_none())
},
)
@@ -131,7 +132,7 @@ pub fn eval_py(source: &str, options: Option<Object>) -> Result<JsValue, JsValue
if let Some(js_vars) = js_vars.clone() {
for pair in convert::object_entries(&js_vars) {
let (key, val) = pair?;
let py_val = convert::js_to_py(&mut vm, val, None);
let py_val = convert::js_to_py(&mut vm, val);
vm.ctx.set_item(
&injections,
&String::from(js_sys::JsString::from(key)),
@@ -143,7 +144,7 @@ pub fn eval_py(source: &str, options: Option<Object>) -> Result<JsValue, JsValue
vm.ctx.set_attr(&vars, "js_vars", injections);
let result = eval(&mut vm, source, vars);
convert::pyresult_to_jsresult(&mut vm, result, None)
convert::pyresult_to_jsresult(&mut vm, result)
}
#[wasm_bindgen(typescript_custom_section)]
+27 -23
View File
@@ -9,7 +9,7 @@ use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::{Rc, Weak};
use wasm_bindgen::prelude::*;
use wasm_builtins;
use wasm_builtins::setup_wasm_builtins;
pub(crate) struct StoredVirtualMachine {
pub vm: VirtualMachine,
@@ -17,23 +17,18 @@ pub(crate) struct StoredVirtualMachine {
}
impl StoredVirtualMachine {
fn new() -> StoredVirtualMachine {
fn new(id: String, inject_builtins: bool) -> StoredVirtualMachine {
let mut vm = VirtualMachine::new();
let builtin = vm.get_builtin_scope();
let scope = vm.context().new_scope(Some(builtin));
setup_vm_scope(&mut vm, &scope);
if inject_builtins {
setup_wasm_builtins(&mut vm, &scope);
}
vm.wasm_id = Some(id);
StoredVirtualMachine { vm, scope }
}
}
fn setup_vm_scope(vm: &mut VirtualMachine, scope: &PyObjectRef) {
vm.ctx.set_attr(
scope,
"print",
vm.ctx.new_rustfunc(wasm_builtins::builtin_print_console),
);
}
// It's fine that it's thread local, since WASM doesn't even have threads yet
thread_local! {
static STORED_VMS: PyRef<HashMap<String, PyRef<StoredVirtualMachine>>> = Rc::default();
@@ -45,14 +40,13 @@ pub struct VMStore;
#[wasm_bindgen(js_class = vmStore)]
impl VMStore {
pub fn init(id: String) -> WASMVirtualMachine {
pub fn init(id: String, inject_builtins: Option<bool>) -> WASMVirtualMachine {
STORED_VMS.with(|cell| {
let mut vms = cell.borrow_mut();
if !vms.contains_key(&id) {
vms.insert(
id.clone(),
Rc::new(RefCell::new(StoredVirtualMachine::new())),
);
let stored_vm =
StoredVirtualMachine::new(id.clone(), inject_builtins.unwrap_or(true));
vms.insert(id.clone(), Rc::new(RefCell::new(stored_vm)));
}
});
WASMVirtualMachine { id }
@@ -91,6 +85,7 @@ impl VMStore {
}
}
#[derive(Clone)]
pub(crate) struct AccessibleVM {
weak: Weak<RefCell<StoredVirtualMachine>>,
id: String,
@@ -103,6 +98,14 @@ impl AccessibleVM {
AccessibleVM { weak, id }
}
pub fn from_vm(vm: &VirtualMachine) -> AccessibleVM {
AccessibleVM::from_id(
vm.wasm_id
.clone()
.expect("VM passed to from_vm to have wasm_id be Some()"),
)
}
pub fn upgrade(&self) -> Option<&mut VirtualMachine> {
let vm_cell = self.weak.upgrade()?;
match vm_cell.try_borrow_mut() {
@@ -135,7 +138,7 @@ impl From<&WASMVirtualMachine> for AccessibleVM {
#[wasm_bindgen(js_name = VirtualMachine)]
#[derive(Clone)]
pub struct WASMVirtualMachine {
id: String,
pub(crate) id: String,
}
#[wasm_bindgen(js_class = VirtualMachine)]
@@ -144,11 +147,12 @@ impl WASMVirtualMachine {
where
F: FnOnce(&mut StoredVirtualMachine) -> R,
{
STORED_VMS.with(|cell| {
let stored_vm = STORED_VMS.with(|cell| {
let mut vms = cell.borrow_mut();
let mut stored_vm = vms.get_mut(&self.id).unwrap().borrow_mut();
f(&mut stored_vm)
})
vms.get_mut(&self.id).unwrap().clone()
});
let mut stored_vm = stored_vm.borrow_mut();
f(&mut stored_vm)
}
pub(crate) fn with<F, R>(&self, f: F) -> Result<R, JsValue>
@@ -187,7 +191,7 @@ impl WASMVirtualMachine {
ref mut vm,
ref mut scope,
}| {
let value = convert::js_to_py(vm, value, Some(self.clone()));
let value = convert::js_to_py(vm, value);
vm.ctx.set_attr(scope, &name, value);
},
)
@@ -211,7 +215,7 @@ impl WASMVirtualMachine {
let result = vm
.run_code_obj(code, scope.clone())
.map_err(|err| convert::py_str_err(vm, &err))?;
Ok(convert::py_to_js(vm, result, Some(self.clone())))
Ok(convert::py_to_js(vm, result))
},
)
}
+78 -5
View File
@@ -8,18 +8,22 @@ extern crate js_sys;
extern crate wasm_bindgen;
extern crate web_sys;
use crate::convert;
use crate::{convert, vm_class::AccessibleVM};
use js_sys::Array;
use rustpython_vm::obj::{objstr, objtype};
use rustpython_vm::pyobject::{IdProtocol, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol};
use rustpython_vm::VirtualMachine;
use wasm_bindgen::{JsCast, JsValue};
use web_sys::{console, window, HtmlTextAreaElement};
use wasm_bindgen::{prelude::*, JsCast};
use web_sys::{console, HtmlTextAreaElement};
fn window() -> web_sys::Window {
web_sys::window().expect("Window to be available")
}
// The HTML id of the textarea element that act as our STDOUT
pub fn print_to_html(text: &str, selector: &str) -> Result<(), JsValue> {
let document = window().unwrap().document().unwrap();
let document = window().document().expect("Document to be available");
let element = document
.query_selector(selector)?
.ok_or_else(|| js_sys::TypeError::new("Couldn't get element"))?;
@@ -85,7 +89,7 @@ pub fn format_print_args(vm: &mut VirtualMachine, args: PyFuncArgs) -> Result<St
pub fn builtin_print_html(vm: &mut VirtualMachine, args: PyFuncArgs, selector: &str) -> PyResult {
let output = format_print_args(vm, args)?;
print_to_html(&output, selector).map_err(|err| convert::js_to_py(vm, err, None))?;
print_to_html(&output, selector).map_err(|err| convert::js_to_py(vm, err))?;
Ok(vm.get_none())
}
@@ -97,3 +101,72 @@ pub fn builtin_print_console(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyRes
console::log(&arr);
Ok(vm.get_none())
}
enum FetchResponseFormat {
Json,
Text,
}
impl FetchResponseFormat {
fn from_str(vm: &mut VirtualMachine, s: &str) -> Result<Self, PyObjectRef> {
match s {
"json" => Ok(FetchResponseFormat::Json),
"text" => Ok(FetchResponseFormat::Text),
_ => Err(vm.new_type_error("Unkown fetch response_format".into())),
}
}
fn get_response(&self, response: &web_sys::Response) -> js_sys::Promise {
let prom = match self {
FetchResponseFormat::Json => response.json(),
FetchResponseFormat::Text => response.text(),
};
prom.expect("response method not to throw error")
}
}
fn builtin_fetch(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
required = [
(url, Some(vm.ctx.str_type())),
(handler, Some(vm.ctx.function_type()))
],
optional = [(response_format, Some(vm.ctx.str_type()))]
);
let response_format = match response_format {
Some(s) => FetchResponseFormat::from_str(vm, &objstr::get_value(s))?,
None => FetchResponseFormat::Text,
};
let window = window();
let response_prom = window.fetch_with_str(&objstr::get_value(url));
let handler = handler.clone();
let acc_vm = AccessibleVM::from_vm(vm);
response_prom.then(&Closure::wrap(Box::new(move |val: JsValue| {
let response = val
.dyn_into::<web_sys::Response>()
.expect("val to be of type Response");
web_sys::console::log_1(&"hey".into());
let prom = response_format.get_response(&response);
let acc_vm = acc_vm.clone();
let handler = handler.clone();
prom.then(&Closure::wrap(Box::new(move |val: JsValue| {
let acc_vm = acc_vm.clone();
let handler = handler.clone();
let vm = acc_vm
.upgrade()
.expect("[Achievement unlucked] How did you get here?");
let val = convert::js_to_py(vm, val);
let mut args = PyFuncArgs::default();
args.args.push(val);
let _ = vm.invoke(handler, args);
})));
})));
Ok(vm.get_none())
}
pub fn setup_wasm_builtins(vm: &mut VirtualMachine, scope: &PyObjectRef) {
let ctx = vm.context();
ctx.set_attr(scope, "print", ctx.new_rustfunc(builtin_print_console));
ctx.set_attr(scope, "fetch", ctx.new_rustfunc(builtin_fetch));
}