Skip to content
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

fix(napi): functions related to errors #17370

Merged
merged 19 commits into from
Jan 15, 2023
Merged
Prev Previous commit
Next Next commit
wip
  • Loading branch information
bartlomieju committed Jan 12, 2023
commit c66e8d96f4b7595eb6f6464ec3569dd19d415cb1
135 changes: 75 additions & 60 deletions cli/napi/js_native_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ use libc::INT_MAX;
use v8::BackingStore;
use v8::UniqueRef;

use crate::tools::check;

use super::util::get_array_buffer_ptr;
use deno_runtime::deno_napi::function::create_function;
use deno_runtime::deno_napi::function::create_function_template;
Expand Down Expand Up @@ -286,17 +284,15 @@ fn napi_create_double(
}

fn set_error_code(
env: &mut Env,
scope: &mut v8::HandleScope,
error: v8::Local<v8::Value>,
code: napi_value,
code_cstring: *const c_char,
) -> napi_status {
if !code.is_null() || !code_cstring.is_null() {
let err_object: v8::Local<v8::Object> = error.into();

let code_value: v8::Local<v8::Value>;
if code.is_some() || !code_cstring.is_null() {
let err_object: v8::Local<v8::Object> = error.try_into().unwrap();

code_value = if !code.is_null() {
let code_value: v8::Local<v8::Value> = if code.is_some() {
let mut code_value =
unsafe { transmute::<napi_value, v8::Local<v8::Value>>(code) };

Expand All @@ -310,16 +306,13 @@ fn set_error_code(
let code_string =
unsafe { CStr::from_ptr(code_cstring).to_str().unwrap() };
// FIXME(bartlomieju): unsafe unwrap
let name = v8::String::new(&mut env.scope(), code_string).unwrap();
let name = v8::String::new(scope, code_string).unwrap();
name.into()
};

let code_key = v8::String::new(&mut env.scope(), "code").unwrap();
let code_key = v8::String::new(scope, "code").unwrap();

if err_object
.set(&mut env.scope(), code_key.into(), code_value)
.is_none()
{
if err_object.set(scope, code_key.into(), code_value).is_none() {
return napi_generic_failure;
}
}
Expand All @@ -335,21 +328,83 @@ fn napi_create_error(
result: *mut napi_value,
) -> Result {
let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
let msg = msg.as_mut().ok_or(Error::InvalidArg)?;
check_arg_option!(msg);
check_arg!(result);

let message_value = transmute::<napi_value, v8::Local<v8::Value>>(msg);
let mut message_value =
unsafe { transmute::<napi_value, v8::Local<v8::Value>>(msg) };
if !message_value.is_string() {
return Err(Error::StringExpected);
}

let scope = &mut env.scope();
let error_obj =
v8::Exception::error(&mut env.scope(), msg.try_into().unwrap());
set_error_code(env, error_obj.into(), code, std::ptr::null())
.map_err(|e| e.into())?;
v8::Exception::error(scope, message_value.try_into().unwrap());
let status = set_error_code(scope, error_obj.into(), code, std::ptr::null());
if status != napi_ok {
return Err(status.into());
}
*result = error_obj.into();

// TODO(bartlomieju): clear last error here
Ok(())
}

#[napi_sym::napi_sym]
fn napi_create_type_error(
env: *mut Env,
code: napi_value,
msg: napi_value,
result: *mut napi_value,
) -> Result {
let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
check_arg_option!(msg);
check_arg!(result);

let mut message_value =
unsafe { transmute::<napi_value, v8::Local<v8::Value>>(msg) };
if !message_value.is_string() {
return Err(Error::StringExpected);
}

let scope = &mut env.scope();
let error_obj =
v8::Exception::type_error(scope, message_value.try_into().unwrap());
let status = set_error_code(scope, error_obj.into(), code, std::ptr::null());
if status != napi_ok {
return Err(status.into());
}
*result = error_obj.into();
// TODO(bartlomieju): clear last error here
Ok(())
}

#[napi_sym::napi_sym]
fn napi_create_range_error(
env: *mut Env,
code: napi_value,
msg: napi_value,
result: *mut napi_value,
) -> Result {
let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
check_arg_option!(msg);
check_arg!(result);

let mut message_value =
unsafe { transmute::<napi_value, v8::Local<v8::Value>>(msg) };
if !message_value.is_string() {
return Err(Error::StringExpected);
}

let scope = &mut env.scope();
let error_obj =
v8::Exception::range_error(scope, message_value.try_into().unwrap());
let status = set_error_code(scope, error_obj.into(), code, std::ptr::null());
if status != napi_ok {
return Err(status.into());
}
*result = error_obj.into();
// TODO(bartlomieju): clear last error here
Ok(())
}

Expand Down Expand Up @@ -540,26 +595,6 @@ fn napi_create_promise(
Ok(())
}

#[napi_sym::napi_sym]
fn napi_create_range_error(
env: *mut Env,
_code: napi_value,
msg: napi_value,
result: *mut napi_value,
) -> Result {
let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;

// let code = transmute::<napi_value, v8::Local<v8::Value>>(code);
let msg = transmute::<napi_value, v8::Local<v8::Value>>(msg);

let msg = msg.to_string(&mut env.scope()).unwrap();

let error = v8::Exception::range_error(&mut env.scope(), msg);
*result = error.into();

Ok(())
}

#[napi_sym::napi_sym]
fn napi_create_reference(
env: *mut Env,
Expand Down Expand Up @@ -705,26 +740,6 @@ fn napi_create_symbol(
Ok(())
}

#[napi_sym::napi_sym]
fn napi_create_type_error(
env: *mut Env,
_code: napi_value,
msg: napi_value,
result: *mut napi_value,
) -> Result {
let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;

// let code = transmute::<napi_value, v8::Local<v8::Value>>(code);
let msg = transmute::<napi_value, v8::Local<v8::Value>>(msg);

let msg = msg.to_string(&mut env.scope()).unwrap();

let error = v8::Exception::type_error(&mut env.scope(), msg);
*result = error.into();

Ok(())
}

#[napi_sym::napi_sym]
fn napi_create_typedarray(
env: *mut Env,
Expand Down Expand Up @@ -1917,7 +1932,7 @@ fn napi_is_error(
result: *mut bool,
) -> Result {
// TODO(bartlomieju): add `check_env!` macro?
let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
let _env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;
value.ok_or(Error::InvalidArg)?;
check_arg!(result);

Expand Down
69 changes: 42 additions & 27 deletions test_napi/error_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,34 +51,47 @@ Deno.test("napi error", function () {
// Test that non-error primitive is correctly classed
assertEquals(test_error.checkError("non-object"), false);

assertThrows(() => {
test_error.throwExistingError();
}, /^Error: existing error$/);
assertThrows(
() => {
test_error.throwExistingError();
},
Error,
"Error: existing error",
);

assertThrows(() => {
test_error.throwError();
}, /^Error: error$/);
assertThrows(
() => {
test_error.throwError();
},
Error,
"Error: error",
);

assertThrows(() => {
test_error.throwRangeError();
}, /^RangeError: range error$/);
assertThrows(
() => {
test_error.throwRangeError();
},
RangeError,
"RangeError: range error",
);

assertThrows(() => {
test_error.throwTypeError();
}, /^TypeError: type error$/);
assertThrows(
() => {
test_error.throwTypeError();
},
TypeError,
"TypeError: type error",
);

assertThrows(() => {
test_error.throwSyntaxError();
}, /^SyntaxError: syntax error$/);
// assertThrows(() => {
// test_error.throwSyntaxError();
// }, "SyntaxError: syntax error");

[42, {}, [], Symbol("xyzzy"), true, "ball", undefined, null, NaN]
.forEach((value) =>
assertThrows(
() => test_error.throwArbitrary(value),
(err) => {
assertEquals(err, value);
return true;
},
value,
)
);

Expand Down Expand Up @@ -135,6 +148,7 @@ Deno.test("napi error", function () {
);
assertEquals(error.message, "type error");

// TODO(bartlomieju): this is experimental API
// error = test_error.createSyntaxError();
// assert(
// error instanceof SyntaxError,
Expand Down Expand Up @@ -169,12 +183,13 @@ Deno.test("napi error", function () {
assertEquals(error.code, "ERR_TEST_CODE");
assertEquals(error.name, "TypeError");

error = test_error.createSyntaxErrorCode();
assert(
error instanceof SyntaxError,
"expected error to be an instance of SyntaxError",
);
assertEquals(error.message, "SyntaxError [syntax error]");
assertEquals(error.code, "ERR_TEST_CODE");
assertEquals(error.name, "SyntaxError");
// TODO(bartlomieju): this is experimental API
// error = test_error.createSyntaxErrorCode();
// assert(
// error instanceof SyntaxError,
// "expected error to be an instance of SyntaxError",
// );
// assertEquals(error.message, "SyntaxError [syntax error]");
// assertEquals(error.code, "ERR_TEST_CODE");
// assertEquals(error.name, "SyntaxError");
});
Loading