Skip to content

[WASM] Add row and column info of a syntax error to the JS error #616

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions wasm/lib/src/browser_module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,7 @@ fn promise_then(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
}
}
};
ret.map(|val| convert::py_to_js(vm, val))
.map_err(|err| convert::py_to_js(vm, err))
convert::pyresult_to_jsresult(vm, ret)
});

let ret_promise = future_to_promise(ret_future);
Expand Down Expand Up @@ -254,9 +253,8 @@ fn promise_catch(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
.upgrade()
.expect("that the vm is valid when the promise resolves");
let err = convert::js_to_py(vm, err);
vm.invoke(on_reject, PyFuncArgs::new(vec![err], vec![]))
.map(|val| convert::py_to_js(vm, val))
.map_err(|err| convert::py_to_js(vm, err))
let res = vm.invoke(on_reject, PyFuncArgs::new(vec![err], vec![]));
convert::pyresult_to_jsresult(vm, res)
}
});

Expand Down
57 changes: 51 additions & 6 deletions wasm/lib/src/convert.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,59 @@
use crate::browser_module;
use crate::vm_class::{AccessibleVM, WASMVirtualMachine};
use js_sys::{Array, ArrayBuffer, Object, Promise, Reflect, Uint8Array};
use rustpython_vm::obj::{objbytes, objtype};
use rustpython_vm::pyobject::{self, DictProtocol, PyFuncArgs, PyObjectRef, PyResult};
use num_traits::cast::ToPrimitive;
use rustpython_vm::obj::{objbytes, objint, objsequence, objtype};
use rustpython_vm::pyobject::{
self, AttributeProtocol, DictProtocol, PyFuncArgs, PyObjectRef, PyResult,
};
use rustpython_vm::VirtualMachine;
use wasm_bindgen::{closure::Closure, prelude::*, JsCast};

pub fn py_str_err(vm: &mut VirtualMachine, py_err: &PyObjectRef) -> String {
vm.to_pystr(&py_err)
.unwrap_or_else(|_| "Error, and error getting error message".into())
pub fn py_err_to_js_err(vm: &mut VirtualMachine, py_err: &PyObjectRef) -> JsValue {
macro_rules! map_exceptions {
($py_exc:ident, $msg:expr, { $($py_exc_ty:expr => $js_err_new:expr),*$(,)? }) => {
$(if objtype::isinstance($py_exc, $py_exc_ty) {
JsValue::from($js_err_new($msg))
} else)* {
JsValue::from(js_sys::Error::new($msg))
}
};
}
let msg = match py_err
.get_attr("msg")
.and_then(|msg| vm.to_pystr(&msg).ok())
{
Some(msg) => msg,
None => return js_sys::Error::new("error getting error").into(),
};
let js_err = map_exceptions!(py_err,& msg, {
// TypeError is sort of a catch-all for "this value isn't what I thought it was like"
&vm.ctx.exceptions.type_error => js_sys::TypeError::new,
&vm.ctx.exceptions.value_error => js_sys::TypeError::new,
&vm.ctx.exceptions.index_error => js_sys::TypeError::new,
&vm.ctx.exceptions.key_error => js_sys::TypeError::new,
&vm.ctx.exceptions.attribute_error => js_sys::TypeError::new,
&vm.ctx.exceptions.name_error => js_sys::ReferenceError::new,
&vm.ctx.exceptions.syntax_error => js_sys::SyntaxError::new,
});
if let Some(tb) = py_err.get_attr("__traceback__") {
if objtype::isinstance(&tb, &vm.ctx.list_type()) {
let elements = objsequence::get_elements(&tb).to_vec();
if let Some(top) = elements.get(0) {
if objtype::isinstance(&top, &vm.ctx.tuple_type()) {
let element = objsequence::get_elements(&top);

if let Some(lineno) = objint::to_int(vm, &element[1], 10)
.ok()
.and_then(|lineno| lineno.to_u32())
{
Reflect::set(&js_err, &"row".into(), &lineno.into());
}
}
}
}
}
js_err
}

pub fn js_py_typeerror(vm: &mut VirtualMachine, js_err: JsValue) -> PyObjectRef {
Expand Down Expand Up @@ -110,7 +155,7 @@ pub fn object_entries(obj: &Object) -> impl Iterator<Item = Result<(JsValue, JsV
pub fn pyresult_to_jsresult(vm: &mut VirtualMachine, result: PyResult) -> Result<JsValue, JsValue> {
result
.map(|value| py_to_js(vm, value))
.map_err(|err| py_str_err(vm, &err).into())
.map_err(|err| py_err_to_js_err(vm, &err).into())
}

pub fn js_to_py(vm: &mut VirtualMachine, js_val: JsValue) -> PyObjectRef {
Expand Down
44 changes: 39 additions & 5 deletions wasm/lib/src/vm_class.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::browser_module::setup_browser_module;
use crate::convert;
use crate::wasm_builtins;
use js_sys::{Object, SyntaxError, TypeError};
use js_sys::{Object, Reflect, SyntaxError, TypeError};
use rustpython_vm::{
compile,
frame::ScopeRef,
Expand Down Expand Up @@ -318,10 +318,44 @@ impl WASMVirtualMachine {
}| {
source.push('\n');
let code =
compile::compile(&source, &mode, "<wasm>".to_string(), vm.ctx.code_type())
.map_err(|err| {
SyntaxError::new(&format!("Error parsing Python code: {}", err))
})?;
compile::compile(&source, &mode, "<wasm>".to_string(), vm.ctx.code_type());
let code = code.map_err(|err| {
let js_err = SyntaxError::new(&format!("Error parsing Python code: {}", err));
if let rustpython_vm::error::CompileError::Parse(ref parse_error) = err {
use rustpython_parser::error::ParseError;
if let ParseError::EOF(Some(ref loc))
| ParseError::ExtraToken((ref loc, ..))
| ParseError::InvalidToken(ref loc)
| ParseError::UnrecognizedToken((ref loc, ..), _) = parse_error
{
let _ = Reflect::set(
&js_err,
&"row".into(),
&(loc.get_row() as u32).into(),
);
let _ = Reflect::set(
&js_err,
&"col".into(),
&(loc.get_column() as u32).into(),
);
}
if let ParseError::ExtraToken((_, _, ref loc))
| ParseError::UnrecognizedToken((_, _, ref loc), _) = parse_error
{
let _ = Reflect::set(
&js_err,
&"endrow".into(),
&(loc.get_row() as u32).into(),
);
let _ = Reflect::set(
&js_err,
&"endcol".into(),
&(loc.get_column() as u32).into(),
);
}
}
js_err
})?;
let result = vm.run_code_obj(code, scope.clone());
convert::pyresult_to_jsresult(vm, result)
},
Expand Down