Files
node/src/node_errors.h
Chengzhong Wu 1986ee4b65 vm: hint module identifier in instantiate errors
PR-URL: https://github.com/nodejs/node/pull/60199
Fixes: https://github.com/nodejs/node/issues/60157
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Darshan Sen <raisinten@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
2025-10-12 14:28:56 +00:00

397 lines
23 KiB
C++

#ifndef SRC_NODE_ERRORS_H_
#define SRC_NODE_ERRORS_H_
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
#include "debug_utils-inl.h"
#include "env.h"
#include "node_buffer.h"
#include "v8.h"
// Use ostringstream to print exact-width integer types
// because the format specifiers are not available on AIX.
#include <sstream>
namespace node {
// This forward declaration is required to have the method
// available in error messages.
namespace errors {
const char* errno_string(int errorno);
}
enum ErrorHandlingMode { CONTEXTIFY_ERROR, FATAL_ERROR, MODULE_ERROR };
void AppendExceptionLine(Environment* env,
v8::Local<v8::Value> er,
v8::Local<v8::Message> message,
enum ErrorHandlingMode mode);
// This function calls backtrace, it should have not be marked as [[noreturn]].
// But it is a public API, removing the attribute can break.
// Prefer UNREACHABLE() internally instead, it doesn't need manually set
// location.
[[noreturn]] void OnFatalError(const char* location, const char* message);
// This function calls backtrace, do not mark as [[noreturn]]. Read more in the
// ABORT macro.
void OOMErrorHandler(const char* location, const v8::OOMDetails& details);
// Helpers to construct errors similar to the ones provided by
// lib/internal/errors.js.
// Example: with `V(ERR_INVALID_ARG_TYPE, TypeError)`, there will be
// `node::ERR_INVALID_ARG_TYPE(isolate, "message")` returning
// a `Local<Value>` containing the TypeError with proper code and message
#define ERRORS_WITH_CODE(V) \
V(ERR_ACCESS_DENIED, Error) \
V(ERR_BUFFER_CONTEXT_NOT_AVAILABLE, Error) \
V(ERR_BUFFER_OUT_OF_BOUNDS, RangeError) \
V(ERR_BUFFER_TOO_LARGE, Error) \
V(ERR_CLOSED_MESSAGE_PORT, Error) \
V(ERR_CONSTRUCT_CALL_REQUIRED, TypeError) \
V(ERR_CONSTRUCT_CALL_INVALID, TypeError) \
V(ERR_CPU_PROFILE_NOT_STARTED, Error) \
V(ERR_CPU_PROFILE_TOO_MANY, Error) \
V(ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED, Error) \
V(ERR_CRYPTO_INITIALIZATION_FAILED, Error) \
V(ERR_CRYPTO_INVALID_ARGON2_PARAMS, TypeError) \
V(ERR_CRYPTO_INVALID_AUTH_TAG, TypeError) \
V(ERR_CRYPTO_INVALID_COUNTER, TypeError) \
V(ERR_CRYPTO_INVALID_CURVE, TypeError) \
V(ERR_CRYPTO_INVALID_DIGEST, TypeError) \
V(ERR_CRYPTO_INVALID_IV, TypeError) \
V(ERR_CRYPTO_INVALID_JWK, TypeError) \
V(ERR_CRYPTO_INVALID_KEYLEN, RangeError) \
V(ERR_CRYPTO_INVALID_KEYPAIR, RangeError) \
V(ERR_CRYPTO_INVALID_KEYTYPE, RangeError) \
V(ERR_CRYPTO_INVALID_MESSAGELEN, RangeError) \
V(ERR_CRYPTO_INVALID_SCRYPT_PARAMS, RangeError) \
V(ERR_CRYPTO_INVALID_STATE, Error) \
V(ERR_CRYPTO_INVALID_TAG_LENGTH, RangeError) \
V(ERR_CRYPTO_JWK_UNSUPPORTED_CURVE, Error) \
V(ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE, Error) \
V(ERR_CRYPTO_OPERATION_FAILED, Error) \
V(ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH, RangeError) \
V(ERR_CRYPTO_UNKNOWN_CIPHER, Error) \
V(ERR_CRYPTO_UNKNOWN_DH_GROUP, Error) \
V(ERR_CRYPTO_UNSUPPORTED_OPERATION, Error) \
V(ERR_CRYPTO_JOB_INIT_FAILED, Error) \
V(ERR_DLOPEN_DISABLED, Error) \
V(ERR_DLOPEN_FAILED, Error) \
V(ERR_ENCODING_INVALID_ENCODED_DATA, TypeError) \
V(ERR_EXECUTION_ENVIRONMENT_NOT_AVAILABLE, Error) \
V(ERR_FS_CP_EINVAL, Error) \
V(ERR_FS_CP_DIR_TO_NON_DIR, Error) \
V(ERR_FS_CP_NON_DIR_TO_DIR, Error) \
V(ERR_FS_CP_SYMLINK_TO_SUBDIRECTORY, Error) \
V(ERR_FS_EISDIR, Error) \
V(ERR_FS_CP_EEXIST, Error) \
V(ERR_FS_CP_SOCKET, Error) \
V(ERR_FS_CP_FIFO_PIPE, Error) \
V(ERR_FS_CP_UNKNOWN, Error) \
V(ERR_HEAP_PROFILE_HAVE_BEEN_STARTED, Error) \
V(ERR_HEAP_PROFILE_NOT_STARTED, Error) \
V(ERR_ILLEGAL_CONSTRUCTOR, Error) \
V(ERR_INVALID_ADDRESS, Error) \
V(ERR_INVALID_ARG_VALUE, TypeError) \
V(ERR_OSSL_EVP_INVALID_DIGEST, Error) \
V(ERR_INVALID_ARG_TYPE, TypeError) \
V(ERR_INVALID_FILE_URL_HOST, TypeError) \
V(ERR_INVALID_FILE_URL_PATH, TypeError) \
V(ERR_INVALID_INVOCATION, TypeError) \
V(ERR_INVALID_PACKAGE_CONFIG, Error) \
V(ERR_INVALID_OBJECT_DEFINE_PROPERTY, TypeError) \
V(ERR_INVALID_MODULE, Error) \
V(ERR_INVALID_STATE, Error) \
V(ERR_INVALID_THIS, TypeError) \
V(ERR_INVALID_URL, TypeError) \
V(ERR_INVALID_URL_PATTERN, TypeError) \
V(ERR_INVALID_URL_SCHEME, TypeError) \
V(ERR_LOAD_SQLITE_EXTENSION, Error) \
V(ERR_MEMORY_ALLOCATION_FAILED, Error) \
V(ERR_MESSAGE_TARGET_CONTEXT_UNAVAILABLE, Error) \
V(ERR_MISSING_ARGS, TypeError) \
V(ERR_MISSING_PASSPHRASE, TypeError) \
V(ERR_MISSING_PLATFORM_FOR_WORKER, Error) \
V(ERR_MODULE_NOT_FOUND, Error) \
V(ERR_MODULE_NOT_INSTANTIATED, Error) \
V(ERR_MODULE_LINK_MISMATCH, TypeError) \
V(ERR_NON_CONTEXT_AWARE_DISABLED, Error) \
V(ERR_OPERATION_FAILED, TypeError) \
V(ERR_OPTIONS_BEFORE_BOOTSTRAPPING, Error) \
V(ERR_OUT_OF_RANGE, RangeError) \
V(ERR_REQUIRE_ASYNC_MODULE, Error) \
V(ERR_SCRIPT_EXECUTION_INTERRUPTED, Error) \
V(ERR_SCRIPT_EXECUTION_TIMEOUT, Error) \
V(ERR_SOURCE_PHASE_NOT_DEFINED, SyntaxError) \
V(ERR_STRING_TOO_LONG, Error) \
V(ERR_TLS_INVALID_PROTOCOL_METHOD, TypeError) \
V(ERR_TLS_PSK_SET_IDENTITY_HINT_FAILED, Error) \
V(ERR_VM_MODULE_CACHED_DATA_REJECTED, Error) \
V(ERR_VM_MODULE_LINK_FAILURE, Error) \
V(ERR_WASI_NOT_STARTED, Error) \
V(ERR_ZLIB_INITIALIZATION_FAILED, Error) \
V(ERR_WORKER_INIT_FAILED, Error) \
V(ERR_PROTO_ACCESS, Error)
// If the macros are used as ERR_*(isolate, message) or
// THROW_ERR_*(isolate, message) with a single string argument, do run
// formatter on the message, and allow the caller to pass in a message
// directly with characters that would otherwise need escaping if used
// as format string unconditionally.
#define V(code, type) \
template <typename... Args> \
inline v8::Local<v8::Object> code( \
v8::Isolate* isolate, std::string_view format, Args&&... args) { \
std::string message; \
if (sizeof...(Args) == 0) { \
message = format; \
} else { \
message = SPrintF(format, std::forward<Args>(args)...); \
} \
v8::Local<v8::String> js_code = FIXED_ONE_BYTE_STRING(isolate, #code); \
v8::Local<v8::String> js_msg = \
v8::String::NewFromUtf8(isolate, \
message.c_str(), \
v8::NewStringType::kNormal, \
message.length()) \
.ToLocalChecked(); \
v8::Local<v8::Object> e = v8::Exception::type(js_msg) \
->ToObject(isolate->GetCurrentContext()) \
.ToLocalChecked(); \
e->Set(isolate->GetCurrentContext(), \
FIXED_ONE_BYTE_STRING(isolate, "code"), \
js_code) \
.Check(); \
return e; \
} \
template <typename... Args> \
inline void THROW_##code( \
v8::Isolate* isolate, std::string_view format, Args&&... args) { \
isolate->ThrowException( \
code(isolate, format, std::forward<Args>(args)...)); \
} \
template <typename... Args> \
inline void THROW_##code( \
Environment* env, std::string_view format, Args&&... args) { \
THROW_##code(env->isolate(), format, std::forward<Args>(args)...); \
} \
template <typename... Args> \
inline void THROW_##code( \
Realm* realm, std::string_view format, Args&&... args) { \
THROW_##code(realm->isolate(), format, std::forward<Args>(args)...); \
}
ERRORS_WITH_CODE(V)
#undef V
// Errors with predefined static messages
#define PREDEFINED_ERROR_MESSAGES(V) \
V(ERR_ACCESS_DENIED, "Access to this API has been restricted") \
V(ERR_BUFFER_CONTEXT_NOT_AVAILABLE, \
"Buffer is not available for the current Context") \
V(ERR_CLOSED_MESSAGE_PORT, "Cannot send data on closed MessagePort") \
V(ERR_CONSTRUCT_CALL_INVALID, "Constructor cannot be called") \
V(ERR_CONSTRUCT_CALL_REQUIRED, "Cannot call constructor without `new`") \
V(ERR_CRYPTO_INITIALIZATION_FAILED, "Initialization failed") \
V(ERR_CRYPTO_INVALID_ARGON2_PARAMS, "Invalid Argon2 params") \
V(ERR_CRYPTO_INVALID_AUTH_TAG, "Invalid authentication tag") \
V(ERR_CRYPTO_INVALID_COUNTER, "Invalid counter") \
V(ERR_CRYPTO_INVALID_CURVE, "Invalid EC curve name") \
V(ERR_CRYPTO_INVALID_DIGEST, "Invalid digest") \
V(ERR_CRYPTO_INVALID_IV, "Invalid initialization vector") \
V(ERR_CRYPTO_INVALID_JWK, "Invalid JWK format") \
V(ERR_CRYPTO_INVALID_KEYLEN, "Invalid key length") \
V(ERR_CRYPTO_INVALID_KEYPAIR, "Invalid key pair") \
V(ERR_CRYPTO_INVALID_KEYTYPE, "Invalid key type") \
V(ERR_CRYPTO_INVALID_MESSAGELEN, "Invalid message length") \
V(ERR_CRYPTO_INVALID_SCRYPT_PARAMS, "Invalid scrypt params") \
V(ERR_CRYPTO_INVALID_STATE, "Invalid state") \
V(ERR_CRYPTO_INVALID_TAG_LENGTH, "Invalid taglength") \
V(ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE, "Unsupported JWK Key Type.") \
V(ERR_CRYPTO_OPERATION_FAILED, "Operation failed") \
V(ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH, \
"Input buffers must have the same byte length") \
V(ERR_CRYPTO_UNKNOWN_CIPHER, "Unknown cipher") \
V(ERR_CRYPTO_UNKNOWN_DH_GROUP, "Unknown DH group") \
V(ERR_CRYPTO_UNSUPPORTED_OPERATION, "Unsupported crypto operation") \
V(ERR_CRYPTO_JOB_INIT_FAILED, "Failed to initialize crypto job config") \
V(ERR_DLOPEN_FAILED, "DLOpen failed") \
V(ERR_EXECUTION_ENVIRONMENT_NOT_AVAILABLE, \
"Context not associated with Node.js environment") \
V(ERR_ILLEGAL_CONSTRUCTOR, "Illegal constructor") \
V(ERR_INVALID_ADDRESS, "Invalid socket address") \
V(ERR_INVALID_INVOCATION, "Invalid invocation") \
V(ERR_INVALID_MODULE, "No such module") \
V(ERR_INVALID_STATE, "Invalid state") \
V(ERR_INVALID_THIS, "Value of \"this\" is the wrong type") \
V(ERR_INVALID_URL_SCHEME, "The URL must be of scheme file:") \
V(ERR_LOAD_SQLITE_EXTENSION, "Failed to load SQLite extension") \
V(ERR_MEMORY_ALLOCATION_FAILED, "Failed to allocate memory") \
V(ERR_OSSL_EVP_INVALID_DIGEST, "Invalid digest used") \
V(ERR_MESSAGE_TARGET_CONTEXT_UNAVAILABLE, \
"A message object could not be deserialized successfully in the target " \
"vm.Context") \
V(ERR_MISSING_PLATFORM_FOR_WORKER, \
"The V8 platform used by this instance of Node does not support " \
"creating Workers") \
V(ERR_MODULE_NOT_INSTANTIATED, "Module is not instantiated") \
V(ERR_NON_CONTEXT_AWARE_DISABLED, \
"Loading non context-aware native addons has been disabled") \
V(ERR_SCRIPT_EXECUTION_INTERRUPTED, \
"Script execution was interrupted by `SIGINT`") \
V(ERR_TLS_PSK_SET_IDENTITY_HINT_FAILED, "Failed to set PSK identity hint") \
V(ERR_WASI_NOT_STARTED, "wasi.start() has not been called") \
V(ERR_WORKER_INIT_FAILED, "Worker initialization failure") \
V(ERR_PROTO_ACCESS, \
"Accessing Object.prototype.__proto__ has been " \
"disallowed with --disable-proto=throw")
#define V(code, message) \
inline v8::Local<v8::Object> code(v8::Isolate* isolate) { \
return code(isolate, message); \
} \
inline void THROW_##code(v8::Isolate* isolate) { \
isolate->ThrowException(code(isolate, message)); \
} \
inline void THROW_##code(Environment* env) { THROW_##code(env->isolate()); }
PREDEFINED_ERROR_MESSAGES(V)
#undef V
// Errors with predefined non-static messages
inline void THROW_ERR_SCRIPT_EXECUTION_TIMEOUT(Environment* env,
int64_t timeout) {
THROW_ERR_SCRIPT_EXECUTION_TIMEOUT(
env, "Script execution timed out after %dms", timeout);
}
inline void THROW_ERR_REQUIRE_ASYNC_MODULE(
Environment* env,
v8::Local<v8::Value> filename,
v8::Local<v8::Value> parent_filename) {
static constexpr const char* prefix =
"require() cannot be used on an ESM graph with top-level await. Use "
"import() instead. To see where the top-level await comes from, use "
"--experimental-print-required-tla.";
std::string message = prefix;
if (!parent_filename.IsEmpty() && parent_filename->IsString()) {
Utf8Value utf8(env->isolate(), parent_filename);
message += "\n From ";
message += utf8.ToStringView();
}
if (!filename.IsEmpty() && filename->IsString()) {
Utf8Value utf8(env->isolate(), filename);
message += "\n Requiring ";
message += utf8.ToStringView();
}
THROW_ERR_REQUIRE_ASYNC_MODULE(env, message);
}
inline v8::Local<v8::Object> ERR_BUFFER_TOO_LARGE(v8::Isolate* isolate) {
char message[128];
snprintf(message,
sizeof(message),
"Cannot create a Buffer larger than 0x%zx bytes",
Buffer::kMaxLength);
return ERR_BUFFER_TOO_LARGE(isolate, message);
}
inline void THROW_ERR_SOURCE_PHASE_NOT_DEFINED(v8::Isolate* isolate,
const std::string& url) {
return THROW_ERR_SOURCE_PHASE_NOT_DEFINED(
isolate,
"Source phase import object is not defined for module '%s'",
url);
}
inline v8::Local<v8::Object> ERR_STRING_TOO_LONG(v8::Isolate* isolate) {
char message[128];
snprintf(message, sizeof(message),
"Cannot create a string longer than 0x%x characters",
v8::String::kMaxLength);
return ERR_STRING_TOO_LONG(isolate, message);
}
#define THROW_AND_RETURN_IF_NOT_BUFFER(env, val, prefix) \
do { \
if (!Buffer::HasInstance(val)) \
return node::THROW_ERR_INVALID_ARG_TYPE(env, \
prefix " must be a buffer"); \
} while (0)
#define THROW_AND_RETURN_IF_NOT_STRING(env, val, prefix) \
do { \
if (!val->IsString()) \
return node::THROW_ERR_INVALID_ARG_TYPE(env, \
prefix " must be a string"); \
} while (0)
namespace errors {
class TryCatchScope : public v8::TryCatch {
public:
enum class CatchMode { kNormal, kFatal };
explicit TryCatchScope(Environment* env, CatchMode mode = CatchMode::kNormal)
: v8::TryCatch(env->isolate()), env_(env), mode_(mode) {}
~TryCatchScope();
// Since the dtor is not virtual we need to make sure no one creates
// object of it in the free store that might be held by polymorphic pointers.
void* operator new(std::size_t count) = delete;
void* operator new[](std::size_t count) = delete;
TryCatchScope(TryCatchScope&) = delete;
TryCatchScope(TryCatchScope&&) = delete;
TryCatchScope operator=(TryCatchScope&) = delete;
TryCatchScope operator=(TryCatchScope&&) = delete;
private:
Environment* env_;
CatchMode mode_;
};
// Trigger the global uncaught exception handler `process._fatalException`
// in JS land (which emits the 'uncaughtException' event). If that returns
// true, continue program execution, otherwise exit the process.
void TriggerUncaughtException(v8::Isolate* isolate,
const v8::TryCatch& try_catch);
void TriggerUncaughtException(v8::Isolate* isolate,
v8::Local<v8::Value> error,
v8::Local<v8::Message> message,
bool from_promise = false);
const char* errno_string(int errorno);
void PerIsolateMessageListener(v8::Local<v8::Message> message,
v8::Local<v8::Value> error);
void DecorateErrorStack(Environment* env,
const errors::TryCatchScope& try_catch);
void DecorateErrorStack(Environment* env,
v8::Local<v8::Value> error,
v8::Local<v8::Message> message);
class PrinterTryCatch : public v8::TryCatch {
public:
enum PrintSourceLine { kPrintSourceLine, kDontPrintSourceLine };
explicit PrinterTryCatch(v8::Isolate* isolate,
PrintSourceLine print_source_line)
: v8::TryCatch(isolate),
isolate_(isolate),
print_source_line_(print_source_line) {}
~PrinterTryCatch();
private:
v8::Isolate* isolate_;
PrintSourceLine print_source_line_;
};
} // namespace errors
v8::ModifyCodeGenerationFromStringsResult ModifyCodeGenerationFromStrings(
v8::Local<v8::Context> context,
v8::Local<v8::Value> source,
bool is_code_like);
} // namespace node
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
#endif // SRC_NODE_ERRORS_H_