Skip to content

Commit

Permalink
Panic on spurious Err(Throw) instead of silently coercing to undefined
Browse files Browse the repository at this point in the history
  • Loading branch information
dherman committed Jul 14, 2020
1 parent fa4812e commit b4bdf87
Show file tree
Hide file tree
Showing 7 changed files with 23 additions and 7 deletions.
6 changes: 3 additions & 3 deletions crates/neon-sys/native/src/neon.cc
Original file line number Diff line number Diff line change
Expand Up @@ -553,10 +553,10 @@ extern "C" try_catch_control_t Neon_TryCatch_With(Neon_TryCatchGlue glue_fn, voi

if (!try_catch.HasCaught()) {
// It's possible, if unlikely, that a Neon user might return `Err(Throw)` even
// though the VM is not actually in a throwing state. In this case we will
// simply produce the JS undefined value.
// though the VM is not actually in a throwing state. In this case we return
// `CONTROL_UNEXPECTED_ERR` to signal that Rust should panic.
if (ctrl == CONTROL_THREW) {
*result = Nan::Undefined();
return CONTROL_UNEXPECTED_ERR;
}
return CONTROL_RETURNED;
} else {
Expand Down
5 changes: 3 additions & 2 deletions crates/neon-sys/native/src/neon.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ typedef struct {
typedef enum : uint8_t {
CONTROL_RETURNED = 0,
CONTROL_THREW = 1,
CONTROL_PANICKED = 2
CONTROL_PANICKED = 2,
CONTROL_UNEXPECTED_ERR = 3
} try_catch_control_t;

extern "C" {
Expand Down Expand Up @@ -154,7 +155,7 @@ extern "C" {
typedef try_catch_control_t (*Neon_TryCatchGlue)(void *rust_thunk, void *cx, v8::Local<v8::Value> *result, void **unwind_value);

// The `result` out-parameter can be assumed to be initialized if and only if this function
// does not return `CONTROL_PANICKED`.
// returns `CONTROL_RETURNED` or `CONTROL_THREW`.
// The `unwind_value` out-parameter can be assumed to be initialized if and only if this
// function returns `CONTROL_PANICKED`.
try_catch_control_t Neon_TryCatch_With(Neon_TryCatchGlue glue, void *rust_thunk, void *cx, v8::Local<v8::Value> *result, void **unwind_value);
Expand Down
3 changes: 2 additions & 1 deletion crates/neon-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ impl Default for CCallback {
pub enum TryCatchControl {
Returned = 0,
Threw = 1,
Panicked = 2
Panicked = 2,
UnexpectedErr = 3
}

#[derive(Clone, Copy)]
Expand Down
5 changes: 4 additions & 1 deletion src/context/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,9 @@ pub trait ContextInternal<'a>: Sized {
let local = unsafe { local.assume_init() };
Err(JsValue::new_internal(local))
}
TryCatchControl::UnexpectedErr => {
panic!("try_catch: unexpected Err(Throw) when VM is not in a throwing state");
}
}
}

Expand All @@ -179,7 +182,7 @@ pub trait ContextInternal<'a>: Sized {
} else if let Ok(result) = result {
Ok(result)
} else {
Ok(JsUndefined::new_internal(self.env()).upcast())
panic!("try_catch: unexpected Err(Throw) when VM is not in a throwing state");
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions test/dynamic/lib/functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ describe('JsFunction', function() {
}, Error, /^internal error in Neon module: oh no$/);
});

it('panics on unexpected Err(Throw) with cx.try_catch', function() {
assert.throw(addon.unexpected_throw_and_catch, Error, /^internal error in Neon module: try_catch: unexpected Err\(Throw\) when VM is not in a throwing state$/);
})

it('computes the right number of arguments', function() {
assert.equal(addon.num_arguments(), 0);
assert.equal(addon.num_arguments('a'), 1);
Expand Down
6 changes: 6 additions & 0 deletions test/dynamic/native/src/js/functions.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use neon::prelude::*;
use neon::object::This;
use neon::result::Throw;

fn add1(mut cx: FunctionContext) -> JsResult<JsNumber> {
let x = cx.argument::<JsNumber>(0)?.value();
Expand Down Expand Up @@ -121,3 +122,8 @@ pub fn panic_and_catch(mut cx: FunctionContext) -> JsResult<JsValue> {
Ok(cx.try_catch(|_| { panic!("oh no") })
.unwrap_or_else(|err| err))
}

pub fn unexpected_throw_and_catch(mut cx: FunctionContext) -> JsResult<JsValue> {
Ok(cx.try_catch(|_| { Err(Throw) })
.unwrap_or_else(|err| err))
}
1 change: 1 addition & 0 deletions test/dynamic/native/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ register_module!(mut cx, {
cx.export_function("throw_and_catch", throw_and_catch)?;
cx.export_function("call_and_catch", call_and_catch)?;
cx.export_function("panic_and_catch", panic_and_catch)?;
cx.export_function("unexpected_throw_and_catch", unexpected_throw_and_catch)?;

cx.export_class::<JsEmitter>("Emitter")?;
cx.export_class::<JsTestEmitter>("TestEmitter")?;
Expand Down

0 comments on commit b4bdf87

Please sign in to comment.