Skip to content

Commit

Permalink
core: gracefully handle bad op id (denoland#3131)
Browse files Browse the repository at this point in the history
  • Loading branch information
bartlomieju authored and ry committed Oct 22, 2019
1 parent ec44b5b commit 6257b40
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 6 deletions.
36 changes: 35 additions & 1 deletion core/isolate.rs
Expand Up @@ -311,12 +311,19 @@ impl Isolate {
) {
let isolate = unsafe { Isolate::from_raw_ptr(user_data) };

let op = isolate.op_registry.call(
let maybe_op = isolate.op_registry.call(
op_id,
control_buf.as_ref(),
PinnedBuf::new(zero_copy_buf),
);

let op = match maybe_op {
Some(op) => op,
None => {
return isolate.throw_exception(&format!("Unknown op id: {}", op_id))
}
};

// To avoid latency problems we eagerly poll 50 futures and then
// allow to poll ops from `pending_ops`
let op = if isolate.eager_poll_count != 50 {
Expand Down Expand Up @@ -414,6 +421,13 @@ impl Isolate {
}
}

fn throw_exception(&mut self, exception_text: &str) {
let text = CString::new(exception_text).unwrap();
unsafe {
libdeno::deno_throw_exception(self.libdeno_isolate, text.as_ptr())
}
}

fn respond(
&mut self,
maybe_buf: Option<(OpId, &[u8])>,
Expand Down Expand Up @@ -1418,6 +1432,26 @@ pub mod tests {
});
}

#[test]
fn test_pre_dispatch() {
run_in_task(|| {
let (mut isolate, _dispatch_count) = setup(Mode::OverflowResAsync);
js_check(isolate.execute(
"bad_op_id.js",
r#"
let thrown;
try {
Deno.core.dispatch(100, []);
} catch (e) {
thrown = e;
}
assert(thrown == "Unknown op id: 100");
"#,
));
assert_eq!(Async::Ready(()), isolate.poll().unwrap());
});
}

#[test]
fn test_js() {
run_in_task(|| {
Expand Down
1 change: 1 addition & 0 deletions core/libdeno.rs
Expand Up @@ -266,6 +266,7 @@ extern "C" {
pub fn deno_check_promise_errors(i: *const isolate);
pub fn deno_lock(i: *const isolate);
pub fn deno_unlock(i: *const isolate);
pub fn deno_throw_exception(i: *const isolate, text: *const c_char);
pub fn deno_respond(
i: *const isolate,
user_data: *const c_void,
Expand Down
6 changes: 6 additions & 0 deletions core/libdeno/api.cc
Expand Up @@ -160,6 +160,12 @@ void deno_pinned_buf_delete(deno_pinned_buf* buf) {
auto _ = deno::PinnedBuf(buf);
}

void deno_throw_exception(Deno* d_, const char* text) {
auto* d = unwrap(d_);
auto* isolate = d->isolate_;
isolate->ThrowException(deno::v8_str(text));
}

void deno_respond(Deno* d_, void* user_data, deno_op_id op_id, deno_buf buf) {
auto* d = unwrap(d_);
if (d->current_args_ != nullptr) {
Expand Down
2 changes: 2 additions & 0 deletions core/libdeno/deno.h
Expand Up @@ -110,6 +110,8 @@ void deno_execute(Deno* d, void* user_data, const char* js_filename,
// If a JS exception was encountered, deno_last_exception() will be non-NULL.
void deno_respond(Deno* d, void* user_data, deno_op_id op_id, deno_buf buf);

void deno_throw_exception(Deno* d, const char* text);

// consumes zero_copy
void deno_pinned_buf_delete(deno_pinned_buf* buf);

Expand Down
18 changes: 13 additions & 5 deletions core/ops.rs
Expand Up @@ -63,21 +63,26 @@ impl OpRegistry {
op_map_json.as_bytes().to_owned().into_boxed_slice()
}

/// This function returns None only if op with given id doesn't exist in registry.
pub fn call(
&self,
op_id: OpId,
control: &[u8],
zero_copy_buf: Option<PinnedBuf>,
) -> CoreOp {
) -> Option<CoreOp> {
// Op with id 0 has special meaning - it's a special op that is always
// provided to retrieve op id map. The map consists of name to `OpId`
// mappings.
if op_id == 0 {
return Op::Sync(self.json_map());
return Some(Op::Sync(self.json_map()));
}

let d = &*self.dispatchers.get(op_id as usize).expect("Op not found!");
d(control, zero_copy_buf)
let d = match self.dispatchers.get(op_id as usize) {
Some(handler) => &*handler,
None => return None,
};

Some(d(control, zero_copy_buf))
}
}

Expand All @@ -101,11 +106,14 @@ fn test_op_registry() {
expected.insert("test".to_string(), 1);
assert_eq!(op_registry.name_to_id, expected);

let res = op_registry.call(test_id, &[], None);
let res = op_registry.call(test_id, &[], None).unwrap();
if let Op::Sync(buf) = res {
assert_eq!(buf.len(), 0);
} else {
unreachable!();
}
assert_eq!(c.load(atomic::Ordering::SeqCst), 1);

let res = op_registry.call(100, &[], None);
assert!(res.is_none());
}

0 comments on commit 6257b40

Please sign in to comment.