///|
pub(all) enum NapiErrorKind {
Error
TypeError
RangeError
}
///|
pub(all) suberror NapiError {
JavaScriptException(NapiReference)
ApiError(
status~ : Int32,
kind~ : NapiErrorKind,
code~ : String,
context~ : String
)
}
///|
fn napi_failure(
status : Int32,
kind : NapiErrorKind,
code : String,
context : String,
) -> NapiError {
ApiError(status~, kind~, code~, context~)
}
///|
fn result_from_status(
env : NapiEnv,
status : Int32,
msg : Bytes,
) -> Unit raise NapiError {
if status != 0 {
if status == 10 {
let value = Ref(moonbit_dummy_napi_value())
let captured = napi_take_exception(env, value)
if captured == 0 {
raise JavaScriptException(NapiReference::new(env, value.val))
}
}
match status {
1004 =>
raise napi_failure(
status,
Error,
"ERR_NAPI_ENV_CLOSED",
"JavaScript environment has closed",
)
1005 =>
raise napi_failure(
status,
Error,
"ERR_NAPI_ENVIRONMENT",
"Value belongs to a different JavaScript environment or thread",
)
1006 =>
raise napi_failure(
status,
Error,
"ERR_NAPI_DISPOSED",
"Native resource has been disposed",
)
1007 =>
raise napi_failure(
status,
TypeError,
"ERR_NAPI_RECEIVER",
"Invalid receiver or native type",
)
_ => ()
}
if status == 1001 {
raise napi_failure(
status,
Error,
"ERR_NAPI_BUFFER_DETACHED",
"Buffer backing store is detached",
)
}
if status == 1002 {
raise napi_failure(
status,
RangeError,
"ERR_NAPI_SIZE",
"Value exceeds MoonBit's supported size",
)
}
let kind = match status {
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 17 | 18 | 19 | 20 => TypeError
_ => Error
}
let code = if kind is TypeError {
"ERR_NAPI_TYPE"
} else if status == 10 {
"ERR_NAPI_PENDING_EXCEPTION"
} else {
"ERR_NAPI_STATUS"
}
raise napi_failure(status, kind, code, @utf8.decode_lossy(msg))
}
}
///|
#borrow(code, context)
extern "c" fn throw_napi_error_raw(
env : NapiEnv,
status : Int32,
kind : Int32,
code : Bytes,
context : Bytes,
) -> Int32 = "moonbit_napi_throw"
///|
// The only JS exception boundary. The C side leaves an existing pending
// exception intact, including errors originating from reentrant JS callbacks.
fn throw_napi_error(env : NapiEnv, error : Error) -> Unit {
if error is NapiError::JavaScriptException(reference) {
let status = napi_reference_throw(env, reference)
if status == 0 || status == 10 {
return
}
ignore(
throw_napi_error_raw(
env, status, 0, b"ERR_NAPI_ENVIRONMENT", b"Restoring JavaScript exception",
),
)
return
}
let (status, kind, code, context) = match error {
NapiError::ApiError(status~, kind~, code~, context~) =>
(status, kind, code, context)
_ => (9, Error, "ERR_NAPI_CALL", error.to_string())
}
let kind = match kind {
Error => 0
TypeError => 1
RangeError => 2
}
// A throwing Node-API operation may itself report pending_exception. The
// adapter must return NULL on either result; it cannot resume user code.
ignore(
throw_napi_error_raw(
env,
status,
kind,
@utf8.encode(code),
@utf8.encode(context),
),
)
}