Skip to content

Commit adde4e7

Browse files
Merge pull request #616 from coolreader18/wasm-syntax-error-location
[WASM] Add row and column info of a syntax error to the JS error
2 parents c1ddcbb + c563a6d commit adde4e7

File tree

3 files changed

+93
-16
lines changed

3 files changed

+93
-16
lines changed

wasm/lib/src/browser_module.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,7 @@ fn promise_then(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
221221
}
222222
}
223223
};
224-
ret.map(|val| convert::py_to_js(vm, val))
225-
.map_err(|err| convert::py_to_js(vm, err))
224+
convert::pyresult_to_jsresult(vm, ret)
226225
});
227226

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

wasm/lib/src/convert.rs

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,59 @@
11
use crate::browser_module;
22
use crate::vm_class::{AccessibleVM, WASMVirtualMachine};
33
use js_sys::{Array, ArrayBuffer, Object, Promise, Reflect, Uint8Array};
4-
use rustpython_vm::obj::{objbytes, objtype};
5-
use rustpython_vm::pyobject::{self, DictProtocol, PyFuncArgs, PyObjectRef, PyResult};
4+
use num_traits::cast::ToPrimitive;
5+
use rustpython_vm::obj::{objbytes, objint, objsequence, objtype};
6+
use rustpython_vm::pyobject::{
7+
self, AttributeProtocol, DictProtocol, PyFuncArgs, PyObjectRef, PyResult,
8+
};
69
use rustpython_vm::VirtualMachine;
710
use wasm_bindgen::{closure::Closure, prelude::*, JsCast};
811

9-
pub fn py_str_err(vm: &mut VirtualMachine, py_err: &PyObjectRef) -> String {
10-
vm.to_pystr(&py_err)
11-
.unwrap_or_else(|_| "Error, and error getting error message".into())
12+
pub fn py_err_to_js_err(vm: &mut VirtualMachine, py_err: &PyObjectRef) -> JsValue {
13+
macro_rules! map_exceptions {
14+
($py_exc:ident, $msg:expr, { $($py_exc_ty:expr => $js_err_new:expr),*$(,)? }) => {
15+
$(if objtype::isinstance($py_exc, $py_exc_ty) {
16+
JsValue::from($js_err_new($msg))
17+
} else)* {
18+
JsValue::from(js_sys::Error::new($msg))
19+
}
20+
};
21+
}
22+
let msg = match py_err
23+
.get_attr("msg")
24+
.and_then(|msg| vm.to_pystr(&msg).ok())
25+
{
26+
Some(msg) => msg,
27+
None => return js_sys::Error::new("error getting error").into(),
28+
};
29+
let js_err = map_exceptions!(py_err,& msg, {
30+
// TypeError is sort of a catch-all for "this value isn't what I thought it was like"
31+
&vm.ctx.exceptions.type_error => js_sys::TypeError::new,
32+
&vm.ctx.exceptions.value_error => js_sys::TypeError::new,
33+
&vm.ctx.exceptions.index_error => js_sys::TypeError::new,
34+
&vm.ctx.exceptions.key_error => js_sys::TypeError::new,
35+
&vm.ctx.exceptions.attribute_error => js_sys::TypeError::new,
36+
&vm.ctx.exceptions.name_error => js_sys::ReferenceError::new,
37+
&vm.ctx.exceptions.syntax_error => js_sys::SyntaxError::new,
38+
});
39+
if let Some(tb) = py_err.get_attr("__traceback__") {
40+
if objtype::isinstance(&tb, &vm.ctx.list_type()) {
41+
let elements = objsequence::get_elements(&tb).to_vec();
42+
if let Some(top) = elements.get(0) {
43+
if objtype::isinstance(&top, &vm.ctx.tuple_type()) {
44+
let element = objsequence::get_elements(&top);
45+
46+
if let Some(lineno) = objint::to_int(vm, &element[1], 10)
47+
.ok()
48+
.and_then(|lineno| lineno.to_u32())
49+
{
50+
Reflect::set(&js_err, &"row".into(), &lineno.into());
51+
}
52+
}
53+
}
54+
}
55+
}
56+
js_err
1257
}
1358

1459
pub fn js_py_typeerror(vm: &mut VirtualMachine, js_err: JsValue) -> PyObjectRef {
@@ -113,7 +158,7 @@ pub fn object_entries(obj: &Object) -> impl Iterator<Item = Result<(JsValue, JsV
113158
pub fn pyresult_to_jsresult(vm: &mut VirtualMachine, result: PyResult) -> Result<JsValue, JsValue> {
114159
result
115160
.map(|value| py_to_js(vm, value))
116-
.map_err(|err| py_str_err(vm, &err).into())
161+
.map_err(|err| py_err_to_js_err(vm, &err).into())
117162
}
118163

119164
pub fn js_to_py(vm: &mut VirtualMachine, js_val: JsValue) -> PyObjectRef {

wasm/lib/src/vm_class.rs

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::browser_module::setup_browser_module;
22
use crate::convert;
33
use crate::wasm_builtins;
4-
use js_sys::{Object, SyntaxError, TypeError};
4+
use js_sys::{Object, Reflect, SyntaxError, TypeError};
55
use rustpython_vm::{
66
compile,
77
frame::ScopeRef,
@@ -343,10 +343,44 @@ impl WASMVirtualMachine {
343343
}| {
344344
source.push('\n');
345345
let code =
346-
compile::compile(&source, &mode, "<wasm>".to_string(), vm.ctx.code_type())
347-
.map_err(|err| {
348-
SyntaxError::new(&format!("Error parsing Python code: {}", err))
349-
})?;
346+
compile::compile(&source, &mode, "<wasm>".to_string(), vm.ctx.code_type());
347+
let code = code.map_err(|err| {
348+
let js_err = SyntaxError::new(&format!("Error parsing Python code: {}", err));
349+
if let rustpython_vm::error::CompileError::Parse(ref parse_error) = err {
350+
use rustpython_parser::error::ParseError;
351+
if let ParseError::EOF(Some(ref loc))
352+
| ParseError::ExtraToken((ref loc, ..))
353+
| ParseError::InvalidToken(ref loc)
354+
| ParseError::UnrecognizedToken((ref loc, ..), _) = parse_error
355+
{
356+
let _ = Reflect::set(
357+
&js_err,
358+
&"row".into(),
359+
&(loc.get_row() as u32).into(),
360+
);
361+
let _ = Reflect::set(
362+
&js_err,
363+
&"col".into(),
364+
&(loc.get_column() as u32).into(),
365+
);
366+
}
367+
if let ParseError::ExtraToken((_, _, ref loc))
368+
| ParseError::UnrecognizedToken((_, _, ref loc), _) = parse_error
369+
{
370+
let _ = Reflect::set(
371+
&js_err,
372+
&"endrow".into(),
373+
&(loc.get_row() as u32).into(),
374+
);
375+
let _ = Reflect::set(
376+
&js_err,
377+
&"endcol".into(),
378+
&(loc.get_column() as u32).into(),
379+
);
380+
}
381+
}
382+
js_err
383+
})?;
350384
let result = vm.run_code_obj(code, scope.clone());
351385
convert::pyresult_to_jsresult(vm, result)
352386
},

0 commit comments

Comments
 (0)