diff --git a/Cargo.lock b/Cargo.lock index ba657ddbe..c3592679f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1778,6 +1778,7 @@ dependencies = [ "rustyline", "schannel", "serde", + "serde_json", "sha-1", "sha2", "sha3", diff --git a/Lib/json/__init__.py b/Lib/json/__init__.py index 1ba8b48bd..4b7d8897b 100644 --- a/Lib/json/__init__.py +++ b/Lib/json/__init__.py @@ -107,6 +107,11 @@ from .decoder import JSONDecoder, JSONDecodeError from .encoder import JSONEncoder import codecs +_use_serde_json = False +def use_serde_json(x=True): + global _use_serde_json + _use_serde_json = x + _default_encoder = JSONEncoder( skipkeys=False, ensure_ascii=True, @@ -354,6 +359,13 @@ def loads(s, *, cls=None, object_hook=None, parse_float=None, if (cls is None and object_hook is None and parse_int is None and parse_float is None and parse_constant is None and object_pairs_hook is None and not kw): + if _use_serde_json: + try: + import _serde_json + except ImportError: + pass + else: + return _serde_json.decode(s) return _default_decoder.decode(s) if cls is None: cls = JSONDecoder diff --git a/Lib/json/decoder.py b/Lib/json/decoder.py index d7d824454..239bacdb0 100644 --- a/Lib/json/decoder.py +++ b/Lib/json/decoder.py @@ -27,6 +27,21 @@ class JSONDecodeError(ValueError): colno: The column corresponding to pos """ + # RUSTPYTHON SPECIFIC + @classmethod + def _from_serde(cls, msg, doc, line, col): + pos = 0 + # 0-indexed + line -= 1 + col -= 1 + while line > 0: + i = doc.index('\n', pos) + line -= 1 + pos = i + pos += col + return cls(msg, doc, pos) + + # Note that this exception is used from _json def __init__(self, msg, doc, pos): lineno = doc.count('\n', 0, pos) + 1 diff --git a/vm/Cargo.toml b/vm/Cargo.toml index da4b71d46..fa064f298 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -47,6 +47,7 @@ rustpython-bytecode = { path = "../bytecode", version = "0.1.2" } rustpython-jit = { path = "../jit", optional = true, version = "0.1.2" } rustpython-pylib = { path = "pylib-crate", optional = true, version = "0.1.0" } serde = { version = "1.0.66", features = ["derive"] } +serde_json = "1.0" byteorder = "1.2.6" regex = "1" rustc_version_runtime = "0.1.*" diff --git a/vm/src/stdlib/mod.rs b/vm/src/stdlib/mod.rs index 92ccd44b6..30a981f79 100644 --- a/vm/src/stdlib/mod.rs +++ b/vm/src/stdlib/mod.rs @@ -26,6 +26,7 @@ mod platform; mod pystruct; mod random; mod re; +mod serde_json; #[cfg(not(target_arch = "wasm32"))] pub mod socket; mod string; @@ -91,6 +92,7 @@ pub fn get_module_inits() -> HashMap { "_platform".to_owned() => Box::new(platform::make_module), "regex_crate".to_owned() => Box::new(re::make_module), "_random".to_owned() => Box::new(random::make_module), + "_serde_json".to_owned() => Box::new(serde_json::make_module), "_string".to_owned() => Box::new(string::make_module), "_struct".to_owned() => Box::new(pystruct::make_module), "time".to_owned() => Box::new(time_module::make_module), diff --git a/vm/src/stdlib/serde_json.rs b/vm/src/stdlib/serde_json.rs new file mode 100644 index 000000000..86c28de23 --- /dev/null +++ b/vm/src/stdlib/serde_json.rs @@ -0,0 +1,38 @@ +pub(crate) use _serde_json::make_module; + +#[pymodule] +mod _serde_json { + use crate::common::borrow::BorrowValue; + use crate::obj::objstr::PyStrRef; + use crate::py_serde; + use crate::pyobject::{PyResult, TryFromObject}; + use crate::VirtualMachine; + + #[pyfunction] + fn decode(s: PyStrRef, vm: &VirtualMachine) -> PyResult { + let res = (|| -> serde_json::Result<_> { + let mut de = serde_json::Deserializer::from_str(s.borrow_value()); + let res = py_serde::deserialize(vm, &mut de)?; + de.end()?; + Ok(res) + })(); + + res.or_else(|err| { + let decode_error = vm.try_class("json", "JSONDecodeError")?; + let from_serde = vm.get_attribute(decode_error.into_object(), "_from_serde")?; + let mut err_msg = err.to_string(); + let pos = err_msg.rfind(" at line ").unwrap(); + err_msg.truncate(pos); + let decode_error = vm.invoke( + &from_serde, + vec![ + vm.ctx.new_str(err_msg), + s.into_object(), + vm.ctx.new_int(err.line()), + vm.ctx.new_int(err.column()), + ], + )?; + TryFromObject::try_from_object(vm, decode_error) + }) + } +}