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
finish tests
  • Loading branch information
bartlomieju committed Jan 12, 2023
commit aab8165bc59fd3cae889da666079f1107ff266e6
64 changes: 46 additions & 18 deletions cli/napi/js_native_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ fn napi_create_error(
let scope = &mut env.scope();
let error_obj =
v8::Exception::error(scope, message_value.try_into().unwrap());
let status = set_error_code(scope, error_obj.into(), code, std::ptr::null());
let status = set_error_code(scope, error_obj, code, std::ptr::null());
if status != napi_ok {
return Err(status.into());
}
Expand Down Expand Up @@ -370,7 +370,7 @@ fn napi_create_type_error(
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());
let status = set_error_code(scope, error_obj, code, std::ptr::null());
if status != napi_ok {
return Err(status.into());
}
Expand Down Expand Up @@ -399,7 +399,7 @@ fn napi_create_range_error(
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());
let status = set_error_code(scope, error_obj, code, std::ptr::null());
if status != napi_ok {
return Err(status.into());
}
Expand Down Expand Up @@ -2243,60 +2243,88 @@ fn napi_throw(env: *mut Env, error: napi_value) -> Result {
#[napi_sym::napi_sym]
fn napi_throw_error(
env: *mut Env,
_code: *const c_char,
code: *const c_char,
msg: *const c_char,
) -> Result {
let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?;

// let code = CStr::from_ptr(code).to_str().unwrap();
// TODO(bartlomieju): graceful handling of strings here
let msg = CStr::from_ptr(msg).to_str().unwrap();

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

let error = v8::Exception::error(&mut env.scope(), msg);
let scope = &mut env.scope();
let error = v8::Exception::error(scope, msg);
let status = set_error_code(
scope,
error,
transmute::<*mut (), napi_value>(std::ptr::null_mut()),
code,
);
if status != napi_ok {
return Err(status.into());
}

env.scope().throw_exception(error);

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

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

// let code = CStr::from_ptr(code).to_str().unwrap();
// TODO(bartlomieju): graceful handling of strings here
let msg = CStr::from_ptr(msg).to_str().unwrap();

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

let error = v8::Exception::range_error(&mut env.scope(), msg);
let scope = &mut env.scope();
let error = v8::Exception::range_error(scope, msg);
let status = set_error_code(
scope,
error,
transmute::<*mut (), napi_value>(std::ptr::null_mut()),
code,
);
if status != napi_ok {
return Err(status.into());
}
env.scope().throw_exception(error);

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

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

// let code = CStr::from_ptr(code).to_str().unwrap();
// TODO(bartlomieju): graceful handling of strings here
let msg = CStr::from_ptr(msg).to_str().unwrap();

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

let error = v8::Exception::type_error(&mut env.scope(), msg);
let scope = &mut env.scope();
let error = v8::Exception::type_error(scope, msg);
let status = set_error_code(
scope,
error,
transmute::<*mut (), napi_value>(std::ptr::null_mut()),
code,
);
if status != napi_ok {
return Err(status.into());
}
env.scope().throw_exception(error);

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

Expand Down
116 changes: 68 additions & 48 deletions test_napi/error_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
loadTestLibrary,
} from "./common.js";

const test_error = loadTestLibrary();
const testError = loadTestLibrary();

const theError = new Error("Some error");
const theTypeError = new TypeError("Some type error");
Expand All @@ -17,146 +17,166 @@ const theReferenceError = new ReferenceError("Some reference error");
const theURIError = new URIError("Some URI error");
const theEvalError = new EvalError("Some eval error");

function assertThrowsWithCode(fn, value) {
let thrown = false;

try {
fn();
} catch (e) {
thrown = true;
assertEquals(e.message, value.message);
assertEquals(e.code, value.code);
} finally {
assert(thrown);
}
}

Deno.test("napi error", function () {
class MyError extends Error {}
const myError = new MyError("Some MyError");

// Test that native error object is correctly classed
assertEquals(test_error.checkError(theError), true);
assertEquals(testError.checkError(theError), true);

// Test that native type error object is correctly classed
assertEquals(test_error.checkError(theTypeError), true);
assertEquals(testError.checkError(theTypeError), true);

// Test that native syntax error object is correctly classed
assertEquals(test_error.checkError(theSyntaxError), true);
assertEquals(testError.checkError(theSyntaxError), true);

// Test that native range error object is correctly classed
assertEquals(test_error.checkError(theRangeError), true);
assertEquals(testError.checkError(theRangeError), true);

// Test that native reference error object is correctly classed
assertEquals(test_error.checkError(theReferenceError), true);
assertEquals(testError.checkError(theReferenceError), true);

// Test that native URI error object is correctly classed
assertEquals(test_error.checkError(theURIError), true);
assertEquals(testError.checkError(theURIError), true);

// Test that native eval error object is correctly classed
assertEquals(test_error.checkError(theEvalError), true);
assertEquals(testError.checkError(theEvalError), true);

// Test that class derived from native error is correctly classed
assertEquals(test_error.checkError(myError), true);
assertEquals(testError.checkError(myError), true);

// Test that non-error object is correctly classed
assertEquals(test_error.checkError({}), false);
assertEquals(testError.checkError({}), false);

// Test that non-error primitive is correctly classed
assertEquals(test_error.checkError("non-object"), false);
assertEquals(testError.checkError("non-object"), false);

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

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

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

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

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

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

assertThrows(
() => test_error.throwErrorCode(),
.forEach((value) => {
let thrown = false;

try {
testError.throwArbitrary(value);
} catch (e) {
thrown = true;
assertEquals(e, value);
} finally {
assert(thrown);
}
});

assertThrowsWithCode(
() => testError.throwErrorCode(),
{
code: "ERR_TEST_CODE",
message: "Error [error]",
},
);

assertThrows(
() => test_error.throwRangeErrorCode(),
assertThrowsWithCode(
() => testError.throwRangeErrorCode(),
{
code: "ERR_TEST_CODE",
message: "RangeError [range error]",
},
);

assertThrows(
() => test_error.throwTypeErrorCode(),
assertThrowsWithCode(
() => testError.throwTypeErrorCode(),
{
code: "ERR_TEST_CODE",
message: "TypeError [type error]",
},
);

assertThrows(
() => test_error.throwSyntaxErrorCode(),
{
code: "ERR_TEST_CODE",
message: "SyntaxError [syntax error]",
},
);
// assertThrowsWithCode(
// () => testError.throwSyntaxErrorCode(),
// {
// code: "ERR_TEST_CODE",
// message: "SyntaxError [syntax error]",
// },
// );

let error = test_error.createError();
let error = testError.createError();
assert(
error instanceof Error,
"expected error to be an instance of Error",
);
assertEquals(error.message, "error");

error = test_error.createRangeError();
error = testError.createRangeError();
assert(
error instanceof RangeError,
"expected error to be an instance of RangeError",
);
assertEquals(error.message, "range error");

error = test_error.createTypeError();
error = testError.createTypeError();
assert(
error instanceof TypeError,
"expected error to be an instance of TypeError",
);
assertEquals(error.message, "type error");

// TODO(bartlomieju): this is experimental API
// error = test_error.createSyntaxError();
// error = testError.createSyntaxError();
// assert(
// error instanceof SyntaxError,
// "expected error to be an instance of SyntaxError",
// );
// assertEquals(error.message, "syntax error");

error = test_error.createErrorCode();
error = testError.createErrorCode();
assert(
error instanceof Error,
"expected error to be an instance of Error",
Expand All @@ -165,7 +185,7 @@ Deno.test("napi error", function () {
assertEquals(error.message, "Error [error]");
assertEquals(error.name, "Error");

error = test_error.createRangeErrorCode();
error = testError.createRangeErrorCode();
assert(
error instanceof RangeError,
"expected error to be an instance of RangeError",
Expand All @@ -174,7 +194,7 @@ Deno.test("napi error", function () {
assertEquals(error.code, "ERR_TEST_CODE");
assertEquals(error.name, "RangeError");

error = test_error.createTypeErrorCode();
error = testError.createTypeErrorCode();
assert(
error instanceof TypeError,
"expected error to be an instance of TypeError",
Expand All @@ -184,7 +204,7 @@ Deno.test("napi error", function () {
assertEquals(error.name, "TypeError");

// TODO(bartlomieju): this is experimental API
// error = test_error.createSyntaxErrorCode();
// error = testError.createSyntaxErrorCode();
// assert(
// error instanceof SyntaxError,
// "expected error to be an instance of SyntaxError",
Expand Down
Loading