diff --git a/vm/src/stdlib/json.rs b/vm/src/stdlib/json.rs index 84b82389ed9..81fb364d48d 100644 --- a/vm/src/stdlib/json.rs +++ b/vm/src/stdlib/json.rs @@ -188,15 +188,36 @@ impl<'de> Visitor<'de> for PyObjectDeserializer<'de> { } } +pub fn ser_pyobject(vm: &mut VirtualMachine, obj: &PyObjectRef) -> PyResult { + let serializer = PyObjectSerializer { pyobject: obj, vm }; + serde_json::to_string(&serializer).map_err(|err| vm.new_type_error(err.to_string())) +} + +pub fn de_pyobject(vm: &mut VirtualMachine, s: &str) -> PyResult { + let de = PyObjectDeserializer { vm }; + // TODO: Support deserializing string sub-classes + de.deserialize(&mut serde_json::Deserializer::from_str(s)) + .map_err(|err| { + let json_decode_error = vm + .sys_module + .get_item("modules") + .unwrap() + .get_item("json") + .unwrap() + .get_item("JSONDecodeError") + .unwrap(); + let exc = vm.new_exception(json_decode_error, format!("{}", err)); + vm.ctx.set_attr(&exc, "lineno", vm.ctx.new_int(err.line())); + vm.ctx.set_attr(&exc, "colno", vm.ctx.new_int(err.column())); + exc + }) +} + /// Implement json.dumps fn json_dumps(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // TODO: Implement non-trivial serialisation case arg_check!(vm, args, required = [(obj, None)]); - let res = { - let serializer = PyObjectSerializer { pyobject: obj, vm }; - serde_json::to_string(&serializer) - }; - let string = res.map_err(|err| vm.new_type_error(format!("{}", err)))?; + let string = ser_pyobject(vm, obj)?; Ok(vm.context().new_str(string)) } @@ -204,28 +225,8 @@ fn json_dumps(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn json_loads(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // TODO: Implement non-trivial deserialization case arg_check!(vm, args, required = [(string, Some(vm.ctx.str_type()))]); - let res = { - let de = PyObjectDeserializer { vm }; - // TODO: Support deserializing string sub-classes - de.deserialize(&mut serde_json::Deserializer::from_str(&objstr::get_value( - &string, - ))) - }; - - res.map_err(|err| { - let json_decode_error = vm - .sys_module - .get_item("modules") - .unwrap() - .get_item("json") - .unwrap() - .get_item("JSONDecodeError") - .unwrap(); - let exc = vm.new_exception(json_decode_error, format!("{}", err)); - vm.ctx.set_attr(&exc, "lineno", vm.ctx.new_int(err.line())); - vm.ctx.set_attr(&exc, "colno", vm.ctx.new_int(err.column())); - exc - }) + + de_pyobject(vm, &objstr::get_value(&string)) } pub fn make_module(ctx: &PyContext) -> PyObjectRef { diff --git a/vm/src/stdlib/mod.rs b/vm/src/stdlib/mod.rs index c3bb543f524..b0b61119c3e 100644 --- a/vm/src/stdlib/mod.rs +++ b/vm/src/stdlib/mod.rs @@ -1,6 +1,6 @@ mod ast; mod dis; -mod json; +pub(crate) mod json; mod keyword; mod math; mod platform; diff --git a/vm/src/vm.rs b/vm/src/vm.rs index ac97e8e9c3e..f78d657ccf9 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -626,6 +626,14 @@ impl VirtualMachine { }) } + pub fn serialize(&mut self, obj: &PyObjectRef) -> PyResult { + crate::stdlib::json::ser_pyobject(self, obj) + } + + pub fn deserialize(&mut self, s: &str) -> PyResult { + crate::stdlib::json::de_pyobject(self, s) + } + pub fn _sub(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { self.call_or_reflection(a, b, "__sub__", "__rsub__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "-")) diff --git a/wasm/lib/src/convert.rs b/wasm/lib/src/convert.rs index 1cf8907681b..1113ace8783 100644 --- a/wasm/lib/src/convert.rs +++ b/wasm/lib/src/convert.rs @@ -111,13 +111,14 @@ pub fn py_to_js(vm: &mut VirtualMachine, py_obj: PyObjectRef) -> JsValue { return func; } - // the browser module might not be injected - if let Ok(promise_type) = browser_module::import_promise_type(vm) { - if objtype::isinstance(&py_obj, &promise_type) { - return browser_module::get_promise_value(&py_obj).into(); - } + } + // the browser module might not be injected + if let Ok(promise_type) = browser_module::import_promise_type(vm) { + if objtype::isinstance(&py_obj, &promise_type) { + return browser_module::get_promise_value(&py_obj).into(); } } + if objtype::isinstance(&py_obj, &vm.ctx.bytes_type()) || objtype::isinstance(&py_obj, &vm.ctx.bytearray_type()) { @@ -129,12 +130,8 @@ pub fn py_to_js(vm: &mut VirtualMachine, py_obj: PyObjectRef) -> JsValue { } arr.into() } else { - let dumps = vm.class("json", "dumps"); - match vm.invoke(dumps, PyFuncArgs::new(vec![py_obj], vec![])) { - Ok(value) => { - let json = vm.to_pystr(&value).unwrap(); - js_sys::JSON::parse(&json).unwrap_or(JsValue::UNDEFINED) - } + match vm.serialize(&py_obj) { + Ok(json) => js_sys::JSON::parse(&json).unwrap_or(JsValue::UNDEFINED), Err(_) => JsValue::UNDEFINED, } } @@ -226,15 +223,10 @@ pub fn js_to_py(vm: &mut VirtualMachine, js_val: JsValue) -> PyObjectRef { // Because `JSON.stringify(undefined)` returns undefined vm.get_none() } else { - let loads = vm.class("json", "dumps"); let json = match js_sys::JSON::stringify(&js_val) { Ok(json) => String::from(json), Err(_) => return vm.get_none(), }; - let py_json = vm.new_str(json); - - vm.invoke(loads, PyFuncArgs::new(vec![py_json], vec![])) - // can safely unwrap because we know it's valid JSON - .unwrap() + vm.deserialize(&json).unwrap_or_else(|_| vm.get_none()) } }