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
Next Next commit
wip, some tests working
  • Loading branch information
bartlomieju committed Jan 12, 2023
commit 8357cc2d274fd36a12282100c0e878e987005086
73 changes: 65 additions & 8 deletions cli/napi/js_native_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ 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 @@ -283,6 +285,48 @@ fn napi_create_double(
Ok(())
}

fn set_error_code(
env: &mut Env,
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>;

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

if !code_value.is_string() {
return napi_string_expected;
}

code_value
} else {
// FIXME(bartlomieju): unsafe unwrap
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();
name.into()
};

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

if err_object
.set(&mut env.scope(), code_key.into(), code_value)
.is_none()
{
return napi_generic_failure;
}
}

napi_ok
}

#[napi_sym::napi_sym]
fn napi_create_error(
env: *mut Env,
Expand All @@ -291,13 +335,20 @@ 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!(result);

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

let msg = msg.to_string(&mut env.scope()).unwrap();
let error = v8::Exception::error(&mut env.scope(), msg);
*result = error.into();
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())?;

*result = error_obj.into();

Ok(())
}
Expand Down Expand Up @@ -1861,13 +1912,19 @@ fn napi_is_detached_arraybuffer(

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

let value = transmute::<napi_value, v8::Local<v8::Value>>(value);
// TODO
*result = value.is_object();
*result = value.is_native_error();

// TODO(bartlomieju): add `napi_clear_last_error(env)`
Ok(())
}

Expand Down
1 change: 1 addition & 0 deletions test_napi/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export {
assert,
assertEquals,
assertRejects,
assertThrows,
} from "../test_util/std/testing/asserts.ts";
export { fromFileUrl } from "../test_util/std/path/mod.ts";

Expand Down
180 changes: 180 additions & 0 deletions test_napi/error_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.

import {
assert,
assertEquals,
assertThrows,
loadTestLibrary,
} from "./common.js";

const test_error = loadTestLibrary();

const theError = new Error("Some error");
const theTypeError = new TypeError("Some type error");
const theSyntaxError = new SyntaxError("Some syntax error");
const theRangeError = new RangeError("Some type error");
const theReferenceError = new ReferenceError("Some reference error");
const theURIError = new URIError("Some URI error");
const theEvalError = new EvalError("Some eval error");

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);

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

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

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

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

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

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

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

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

// 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.throwError();
}, /^Error: error$/);

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

assertThrows(() => {
test_error.throwTypeError();
}, /^TypeError: type 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;
},
)
);

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

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

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

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

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

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

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

// error = test_error.createSyntaxError();
// assert(
// error instanceof SyntaxError,
// "expected error to be an instance of SyntaxError",
// );
// assertEquals(error.message, "syntax error");

error = test_error.createErrorCode();
assert(
error instanceof Error,
"expected error to be an instance of Error",
);
assertEquals(error.code, "ERR_TEST_CODE");
assertEquals(error.message, "Error [error]");
assertEquals(error.name, "Error");

error = test_error.createRangeErrorCode();
assert(
error instanceof RangeError,
"expected error to be an instance of RangeError",
);
assertEquals(error.message, "RangeError [range error]");
assertEquals(error.code, "ERR_TEST_CODE");
assertEquals(error.name, "RangeError");

error = test_error.createTypeErrorCode();
assert(
error instanceof TypeError,
"expected error to be an instance of TypeError",
);
assertEquals(error.message, "TypeError [type error]");
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");
});
Loading