diff --git a/src/module_wrap.cc b/src/module_wrap.cc index 7bf7b9dd08c062..48af2daa1391c8 100644 --- a/src/module_wrap.cc +++ b/src/module_wrap.cc @@ -4,6 +4,7 @@ #include "module_wrap.h" #include "env.h" +#include "node_errors.h" #include "node_url.h" #include "util-inl.h" #include "node_internals.h" @@ -677,15 +678,14 @@ void ModuleWrap::Resolve(const FunctionCallbackInfo& args) { URL url(*url_utf8, url_utf8.length()); if (url.flags() & URL_FLAGS_FAILED) { - env->ThrowError("second argument is not a URL string"); - return; + return node::THROW_ERR_INVALID_ARG_TYPE( + env, "second argument is not a URL string"); } Maybe result = node::loader::Resolve(env, specifier_std, url); if (result.IsNothing() || (result.FromJust().flags() & URL_FLAGS_FAILED)) { std::string msg = "Cannot find module " + specifier_std; - env->ThrowError(msg.c_str()); - return; + return node::THROW_ERR_MISSING_MODULE(env, msg.c_str()); } args.GetReturnValue().Set(result.FromJust().ToObject(env)); diff --git a/src/node_buffer.cc b/src/node_buffer.cc index 15e6c7a0c492d3..b00886f5e680ba 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -21,6 +21,7 @@ #include "node.h" #include "node_buffer.h" +#include "node_errors.h" #include "env-inl.h" #include "string_bytes.h" @@ -36,9 +37,12 @@ #define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define THROW_AND_RETURN_UNLESS_BUFFER(env, obj) \ + THROW_AND_RETURN_IF_NOT_BUFFER(env, obj, "argument") + #define THROW_AND_RETURN_IF_OOB(r) \ do { \ - if (!(r)) return env->ThrowRangeError("Index out of range"); \ + if (!(r)) return node::THROW_ERR_INDEX_OUT_OF_RANGE(env); \ } while (0) #define SLICE_START_END(start_arg, end_arg, end_max) \ @@ -544,7 +548,7 @@ void Copy(const FunctionCallbackInfo &args) { return args.GetReturnValue().Set(0); if (source_start > ts_obj_length) - return env->ThrowRangeError("Index out of range"); + return node::THROW_ERR_INDEX_OUT_OF_RANGE(env); if (source_end - source_start > target_length - target_start) source_end = source_start + target_length - target_start; @@ -656,8 +660,7 @@ void StringWrite(const FunctionCallbackInfo& args) { THROW_AND_RETURN_UNLESS_BUFFER(env, args.This()); SPREAD_BUFFER_ARG(args.This(), ts_obj); - if (!args[0]->IsString()) - return env->ThrowTypeError("Argument must be a string"); + THROW_AND_RETURN_IF_NOT_STRING(env, args[0], "argument"); Local str = args[0]->ToString(env->context()).ToLocalChecked(); @@ -665,8 +668,10 @@ void StringWrite(const FunctionCallbackInfo& args) { size_t max_length; THROW_AND_RETURN_IF_OOB(ParseArrayIndex(args[1], 0, &offset)); - if (offset > ts_obj_length) - return env->ThrowRangeError("Offset is out of bounds"); + if (offset > ts_obj_length) { + return node::THROW_ERR_BUFFER_OUT_OF_BOUNDS( + env, "\"offset\" is outside of buffer bounds"); + } THROW_AND_RETURN_IF_OOB(ParseArrayIndex(args[2], ts_obj_length - offset, &max_length)); @@ -728,9 +733,9 @@ void CompareOffset(const FunctionCallbackInfo &args) { THROW_AND_RETURN_IF_OOB(ParseArrayIndex(args[5], ts_obj_length, &source_end)); if (source_start > ts_obj_length) - return env->ThrowRangeError("Index out of range"); + return node::THROW_ERR_INDEX_OUT_OF_RANGE(env); if (target_start > target_length) - return env->ThrowRangeError("Index out of range"); + return node::THROW_ERR_INDEX_OUT_OF_RANGE(env); CHECK_LE(source_start, source_end); CHECK_LE(target_start, target_end); diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 1a1eca297b7a07..b37b7e62c2730b 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -21,6 +21,7 @@ #include "node.h" #include "node_buffer.h" +#include "node_errors.h" #include "node_constants.h" #include "node_crypto.h" #include "node_crypto_bio.h" @@ -45,20 +46,6 @@ #include #include -#define THROW_AND_RETURN_IF_NOT_BUFFER(val, prefix) \ - do { \ - if (!Buffer::HasInstance(val)) { \ - return env->ThrowTypeError(prefix " must be a buffer"); \ - } \ - } while (0) - -#define THROW_AND_RETURN_IF_NOT_STRING(val, prefix) \ - do { \ - if (!val->IsString()) { \ - return env->ThrowTypeError(prefix " must be a string"); \ - } \ - } while (0) - static const char PUBLIC_KEY_PFX[] = "-----BEGIN PUBLIC KEY-----"; static const int PUBLIC_KEY_PFX_LEN = sizeof(PUBLIC_KEY_PFX) - 1; static const char PUBRSA_KEY_PFX[] = "-----BEGIN RSA PUBLIC KEY-----"; @@ -507,7 +494,7 @@ void SecureContext::SetKey(const FunctionCallbackInfo& args) { unsigned int len = args.Length(); if (len < 1) { - return env->ThrowError("Private key argument is mandatory"); + return THROW_ERR_MISSING_ARGS(env, "Private key argument is mandatory"); } if (len > 2) { @@ -518,7 +505,7 @@ void SecureContext::SetKey(const FunctionCallbackInfo& args) { if (args[1]->IsUndefined() || args[1]->IsNull()) len = 1; else - THROW_AND_RETURN_IF_NOT_STRING(args[1], "Pass phrase"); + THROW_AND_RETURN_IF_NOT_STRING(env, args[1], "Pass phrase"); } BIO *bio = LoadBIO(env, args[0]); @@ -705,7 +692,7 @@ void SecureContext::SetCert(const FunctionCallbackInfo& args) { ASSIGN_OR_RETURN_UNWRAP(&sc, args.Holder()); if (args.Length() != 1) { - return env->ThrowTypeError("Certificate argument is mandatory"); + return THROW_ERR_MISSING_ARGS(env, "Certificate argument is mandatory"); } BIO* bio = LoadBIO(env, args[0]); @@ -779,7 +766,7 @@ void SecureContext::AddCACert(const FunctionCallbackInfo& args) { ClearErrorOnReturn clear_error_on_return; if (args.Length() != 1) { - return env->ThrowTypeError("CA certificate argument is mandatory"); + return THROW_ERR_MISSING_ARGS(env, "CA certificate argument is mandatory"); } BIO* bio = LoadBIO(env, args[0]); @@ -810,7 +797,7 @@ void SecureContext::AddCRL(const FunctionCallbackInfo& args) { ASSIGN_OR_RETURN_UNWRAP(&sc, args.Holder()); if (args.Length() != 1) { - return env->ThrowTypeError("CRL argument is mandatory"); + return THROW_ERR_MISSING_ARGS(env, "CRL argument is mandatory"); } ClearErrorOnReturn clear_error_on_return; @@ -913,10 +900,10 @@ void SecureContext::SetCiphers(const FunctionCallbackInfo& args) { ClearErrorOnReturn clear_error_on_return; if (args.Length() != 1) { - return env->ThrowTypeError("Ciphers argument is mandatory"); + return THROW_ERR_MISSING_ARGS(env, "Ciphers argument is mandatory"); } - THROW_AND_RETURN_IF_NOT_STRING(args[0], "Ciphers"); + THROW_AND_RETURN_IF_NOT_STRING(env, args[0], "Ciphers"); const node::Utf8Value ciphers(args.GetIsolate(), args[0]); SSL_CTX_set_cipher_list(sc->ctx_, *ciphers); @@ -929,9 +916,9 @@ void SecureContext::SetECDHCurve(const FunctionCallbackInfo& args) { Environment* env = sc->env(); if (args.Length() != 1) - return env->ThrowTypeError("ECDH curve name argument is mandatory"); + return THROW_ERR_MISSING_ARGS(env, "ECDH curve name argument is mandatory"); - THROW_AND_RETURN_IF_NOT_STRING(args[0], "ECDH curve name"); + THROW_AND_RETURN_IF_NOT_STRING(env, args[0], "ECDH curve name"); node::Utf8Value curve(env->isolate(), args[0]); @@ -952,7 +939,7 @@ void SecureContext::SetDHParam(const FunctionCallbackInfo& args) { // Auto DH is not supported in openssl 1.0.1, so dhparam needs // to be specified explicitly if (args.Length() != 1) - return env->ThrowTypeError("DH argument is mandatory"); + return THROW_ERR_MISSING_ARGS(env, "DH argument is mandatory"); // Invalid dhparam is silently discarded and DHE is no longer used. BIO* bio = LoadBIO(env, args[0]); @@ -969,7 +956,8 @@ void SecureContext::SetDHParam(const FunctionCallbackInfo& args) { DH_get0_pqg(dh, &p, nullptr, nullptr); const int size = BN_num_bits(p); if (size < 1024) { - return env->ThrowError("DH parameter is less than 1024 bits"); + return THROW_ERR_INVALID_ARG_VALUE( + env, "DH parameter is less than 1024 bits"); } else if (size < 2048) { args.GetReturnValue().Set(FIXED_ONE_BYTE_STRING( env->isolate(), "DH parameter is less than 2048 bits")); @@ -989,7 +977,8 @@ void SecureContext::SetOptions(const FunctionCallbackInfo& args) { ASSIGN_OR_RETURN_UNWRAP(&sc, args.Holder()); if (args.Length() != 1 || !args[0]->IntegerValue()) { - return sc->env()->ThrowTypeError("Options must be an integer value"); + return THROW_ERR_INVALID_ARG_TYPE( + sc->env(), "Options must be an integer value"); } SSL_CTX_set_options( @@ -1005,10 +994,11 @@ void SecureContext::SetSessionIdContext( Environment* env = sc->env(); if (args.Length() != 1) { - return env->ThrowTypeError("Session ID context argument is mandatory"); + return THROW_ERR_MISSING_ARGS( + env, "Session ID context argument is mandatory"); } - THROW_AND_RETURN_IF_NOT_STRING(args[0], "Session ID context"); + THROW_AND_RETURN_IF_NOT_STRING(env, args[0], "Session ID context"); const node::Utf8Value sessionIdContext(args.GetIsolate(), args[0]); const unsigned char* sid_ctx = @@ -1043,8 +1033,8 @@ void SecureContext::SetSessionTimeout(const FunctionCallbackInfo& args) { ASSIGN_OR_RETURN_UNWRAP(&sc, args.Holder()); if (args.Length() != 1 || !args[0]->IsInt32()) { - return sc->env()->ThrowTypeError( - "Session timeout must be a 32-bit integer"); + return THROW_ERR_INVALID_ARG_TYPE( + sc->env(), "Session timeout must be a 32-bit integer"); } int32_t sessionTimeout = args[0]->Int32Value(); @@ -1076,7 +1066,7 @@ void SecureContext::LoadPKCS12(const FunctionCallbackInfo& args) { ClearErrorOnReturn clear_error_on_return; if (args.Length() < 1) { - return env->ThrowTypeError("PFX certificate argument is mandatory"); + return THROW_ERR_MISSING_ARGS(env, "PFX certificate argument is mandatory"); } in = LoadBIO(env, args[0]); @@ -1085,7 +1075,7 @@ void SecureContext::LoadPKCS12(const FunctionCallbackInfo& args) { } if (args.Length() >= 2) { - THROW_AND_RETURN_IF_NOT_BUFFER(args[1], "Pass phrase"); + THROW_AND_RETURN_IF_NOT_BUFFER(env, args[1], "Pass phrase"); size_t passlen = Buffer::Length(args[1]); pass = new char[passlen + 1]; memcpy(pass, Buffer::Data(args[1]), passlen); @@ -1209,13 +1199,14 @@ void SecureContext::SetTicketKeys(const FunctionCallbackInfo& args) { Environment* env = wrap->env(); if (args.Length() < 1) { - return env->ThrowTypeError("Ticket keys argument is mandatory"); + return THROW_ERR_MISSING_ARGS(env, "Ticket keys argument is mandatory"); } - THROW_AND_RETURN_IF_NOT_BUFFER(args[0], "Ticket keys"); + THROW_AND_RETURN_IF_NOT_BUFFER(env, args[0], "Ticket keys"); if (Buffer::Length(args[0]) != 48) { - return env->ThrowTypeError("Ticket keys length must be 48 bytes"); + return THROW_ERR_INVALID_ARG_VALUE( + env, "Ticket keys length must be 48 bytes"); } memcpy(wrap->ticket_key_name_, Buffer::Data(args[0]), 16); @@ -1961,10 +1952,10 @@ void SSLWrap::SetSession(const FunctionCallbackInfo& args) { ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); if (args.Length() < 1) { - return env->ThrowError("Session argument is mandatory"); + return THROW_ERR_MISSING_ARGS(env, "Session argument is mandatory"); } - THROW_AND_RETURN_IF_NOT_BUFFER(args[0], "Session"); + THROW_AND_RETURN_IF_NOT_BUFFER(env, args[0], "Session"); size_t slen = Buffer::Length(args[0]); char* sbuf = new char[slen]; memcpy(sbuf, Buffer::Data(args[0]), slen); @@ -2086,9 +2077,9 @@ void SSLWrap::SetOCSPResponse( Environment* env = w->env(); if (args.Length() < 1) - return env->ThrowTypeError("OCSP response argument is mandatory"); + return THROW_ERR_MISSING_ARGS(env, "OCSP response argument is mandatory"); - THROW_AND_RETURN_IF_NOT_BUFFER(args[0], "OCSP response"); + THROW_AND_RETURN_IF_NOT_BUFFER(env, args[0], "OCSP response"); w->ocsp_response_.Reset(args.GetIsolate(), args[0].As()); #endif // NODE__HAVE_TLSEXT_STATUS_CB @@ -3937,11 +3928,11 @@ template & args) { Environment* env = Environment::GetCurrent(args); - THROW_AND_RETURN_IF_NOT_BUFFER(args[0], "Key"); + THROW_AND_RETURN_IF_NOT_BUFFER(env, args[0], "Key"); char* kbuf = Buffer::Data(args[0]); ssize_t klen = Buffer::Length(args[0]); - THROW_AND_RETURN_IF_NOT_BUFFER(args[1], "Data"); + THROW_AND_RETURN_IF_NOT_BUFFER(env, args[1], "Data"); char* buf = Buffer::Data(args[1]); ssize_t len = Buffer::Length(args[1]); @@ -4094,10 +4085,10 @@ void DiffieHellman::DiffieHellmanGroup( DiffieHellman* diffieHellman = new DiffieHellman(env, args.This()); if (args.Length() != 1) { - return env->ThrowError("Group name argument is mandatory"); + return THROW_ERR_MISSING_ARGS(env, "Group name argument is mandatory"); } - THROW_AND_RETURN_IF_NOT_STRING(args[0], "Group name"); + THROW_AND_RETURN_IF_NOT_STRING(env, args[0], "Group name"); bool initialized = false; @@ -4244,9 +4235,10 @@ void DiffieHellman::ComputeSecret(const FunctionCallbackInfo& args) { BIGNUM* key = nullptr; if (args.Length() == 0) { - return env->ThrowError("Other party's public key argument is mandatory"); + return THROW_ERR_MISSING_ARGS( + env, "Other party's public key argument is mandatory"); } else { - THROW_AND_RETURN_IF_NOT_BUFFER(args[0], "Other party's public key"); + THROW_AND_RETURN_IF_NOT_BUFFER(env, args[0], "Other party's public key"); key = BN_bin2bn( reinterpret_cast(Buffer::Data(args[0])), Buffer::Length(args[0]), @@ -4314,12 +4306,12 @@ void DiffieHellman::SetKey(const v8::FunctionCallbackInfo& args, if (args.Length() == 0) { snprintf(errmsg, sizeof(errmsg), "%s argument is mandatory", what); - return env->ThrowError(errmsg); + return THROW_ERR_MISSING_ARGS(env, errmsg); } if (!Buffer::HasInstance(args[0])) { snprintf(errmsg, sizeof(errmsg), "%s must be a buffer", what); - return env->ThrowTypeError(errmsg); + return THROW_ERR_INVALID_ARG_TYPE(env, errmsg); } BIGNUM* num = @@ -4397,12 +4389,13 @@ void ECDH::New(const FunctionCallbackInfo& args) { MarkPopErrorOnReturn mark_pop_error_on_return; // TODO(indutny): Support raw curves? - THROW_AND_RETURN_IF_NOT_STRING(args[0], "ECDH curve name"); + THROW_AND_RETURN_IF_NOT_STRING(env, args[0], "ECDH curve name"); node::Utf8Value curve(env->isolate(), args[0]); int nid = OBJ_sn2nid(*curve); if (nid == NID_undef) - return env->ThrowTypeError("First argument should be a valid curve name"); + return THROW_ERR_INVALID_ARG_VALUE(env, + "First argument should be a valid curve name"); EC_KEY* key = EC_KEY_new_by_curve_name(nid); if (key == nullptr) @@ -4454,7 +4447,7 @@ EC_POINT* ECDH::BufferToPoint(Environment* env, void ECDH::ComputeSecret(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - THROW_AND_RETURN_IF_NOT_BUFFER(args[0], "Data"); + THROW_AND_RETURN_IF_NOT_BUFFER(env, args[0], "Data"); ECDH* ecdh; ASSIGN_OR_RETURN_UNWRAP(&ecdh, args.Holder()); @@ -4557,7 +4550,7 @@ void ECDH::SetPrivateKey(const FunctionCallbackInfo& args) { ECDH* ecdh; ASSIGN_OR_RETURN_UNWRAP(&ecdh, args.Holder()); - THROW_AND_RETURN_IF_NOT_BUFFER(args[0], "Private key"); + THROW_AND_RETURN_IF_NOT_BUFFER(env, args[0], "Private key"); BIGNUM* priv = BN_bin2bn( reinterpret_cast(Buffer::Data(args[0].As())), @@ -4611,7 +4604,7 @@ void ECDH::SetPublicKey(const FunctionCallbackInfo& args) { ECDH* ecdh; ASSIGN_OR_RETURN_UNWRAP(&ecdh, args.Holder()); - THROW_AND_RETURN_IF_NOT_BUFFER(args[0], "Public key"); + THROW_AND_RETURN_IF_NOT_BUFFER(env, args[0], "Public key"); MarkPopErrorOnReturn mark_pop_error_on_return; diff --git a/src/node_dtrace.cc b/src/node_dtrace.cc index ed063fddfafc49..29e6276824ae6d 100644 --- a/src/node_dtrace.cc +++ b/src/node_dtrace.cc @@ -42,6 +42,7 @@ #define NODE_GC_DONE(arg0, arg1, arg2) #endif +#include "node_errors.h" #include "node_internals.h" #include @@ -60,7 +61,7 @@ using v8::Value; #define SLURP_STRING(obj, member, valp) \ if (!(obj)->IsObject()) { \ - return env->ThrowError( \ + return node::THROW_ERR_INVALID_ARG_TYPE(env, \ "expected object for " #obj " to contain string member " #member); \ } \ node::Utf8Value _##member(env->isolate(), \ @@ -70,23 +71,23 @@ using v8::Value; #define SLURP_INT(obj, member, valp) \ if (!(obj)->IsObject()) { \ - return env->ThrowError( \ - "expected object for " #obj " to contain integer member " #member); \ + return node::THROW_ERR_INVALID_ARG_TYPE(env, \ + "expected object for " #obj " to contain integer member " #member);\ } \ *valp = obj->Get(OneByteString(env->isolate(), #member)) \ ->Int32Value(); #define SLURP_OBJECT(obj, member, valp) \ if (!(obj)->IsObject()) { \ - return env->ThrowError( \ - "expected object for " #obj " to contain object member " #member); \ + return node::THROW_ERR_INVALID_ARG_TYPE(env, \ + "expected object for " #obj " to contain object member " #member); \ } \ *valp = Local::Cast(obj->Get(OneByteString(env->isolate(), #member))); #define SLURP_CONNECTION(arg, conn) \ if (!(arg)->IsObject()) { \ - return env->ThrowError( \ - "expected argument " #arg " to be a connection object"); \ + return node::THROW_ERR_INVALID_ARG_TYPE(env, \ + "expected argument " #arg " to be a connection object"); \ } \ node_dtrace_connection_t conn; \ Local _##conn = Local::Cast(arg); \ @@ -103,8 +104,8 @@ using v8::Value; #define SLURP_CONNECTION_HTTP_CLIENT(arg, conn) \ if (!(arg)->IsObject()) { \ - return env->ThrowError( \ - "expected argument " #arg " to be a connection object"); \ + return node::THROW_ERR_INVALID_ARG_TYPE(env, \ + "expected argument " #arg " to be a connection object"); \ } \ node_dtrace_connection_t conn; \ Local _##conn = Local::Cast(arg); \ @@ -115,12 +116,12 @@ using v8::Value; #define SLURP_CONNECTION_HTTP_CLIENT_RESPONSE(arg0, arg1, conn) \ if (!(arg0)->IsObject()) { \ - return env->ThrowError( \ - "expected argument " #arg0 " to be a connection object"); \ + return node::THROW_ERR_INVALID_ARG_TYPE(env, \ + "expected argument " #arg0 " to be a connection object"); \ } \ if (!(arg1)->IsObject()) { \ - return env->ThrowError( \ - "expected argument " #arg1 " to be a connection object"); \ + return node::THROW_ERR_INVALID_ARG_TYPE(env, \ + "expected argument " #arg1 " to be a connection object"); \ } \ node_dtrace_connection_t conn; \ Local _##conn = Local::Cast(arg0); \ @@ -166,8 +167,8 @@ void DTRACE_HTTP_SERVER_REQUEST(const FunctionCallbackInfo& args) { SLURP_OBJECT(arg0, headers, &headers); if (!(headers)->IsObject()) { - return env->ThrowError( - "expected object for request to contain string member headers"); + return node::THROW_ERR_INVALID_ARG_TYPE(env, + "expected object for request to contain string member headers"); } Local strfwdfor = headers->Get(env->x_forwarded_string()); diff --git a/src/node_errors.h b/src/node_errors.h index f34beb6fbc94cd..0f91872474148d 100644 --- a/src/node_errors.h +++ b/src/node_errors.h @@ -17,8 +17,14 @@ namespace node { // a `Local` containing the TypeError with proper code and message #define ERRORS_WITH_CODE(V) \ + V(ERR_BUFFER_OUT_OF_BOUNDS, RangeError) \ + V(ERR_INDEX_OUT_OF_RANGE, RangeError) \ + V(ERR_INVALID_ARG_VALUE, TypeError) \ + V(ERR_INVALID_ARG_TYPE, TypeError) \ V(ERR_MEMORY_ALLOCATION_FAILED, Error) \ - V(ERR_STRING_TOO_LONG, Error) \ + V(ERR_MISSING_ARGS, TypeError) \ + V(ERR_MISSING_MODULE, Error) \ + V(ERR_STRING_TOO_LONG, Error) \ V(ERR_BUFFER_TOO_LARGE, Error) #define V(code, type) \ @@ -32,6 +38,9 @@ namespace node { e->Set(isolate->GetCurrentContext(), OneByteString(isolate, "code"), \ js_code).FromJust(); \ return e; \ + } \ + inline void THROW_ ## code(Environment* env, const char* message) { \ + env->isolate()->ThrowException(code(env->isolate(), message)); \ } ERRORS_WITH_CODE(V) #undef V @@ -39,11 +48,15 @@ namespace node { // Errors with predefined static messages #define PREDEFINED_ERROR_MESSAGES(V) \ + V(ERR_INDEX_OUT_OF_RANGE, "Index out of range") \ V(ERR_MEMORY_ALLOCATION_FAILED, "Failed to allocate memory") #define V(code, message) \ inline v8::Local code(v8::Isolate* isolate) { \ return code(isolate, message); \ + } \ + inline void THROW_ ## code(Environment* env) { \ + env->isolate()->ThrowException(code(env->isolate(), message)); \ } PREDEFINED_ERROR_MESSAGES(V) #undef V @@ -66,6 +79,20 @@ inline v8::Local ERR_STRING_TOO_LONG(v8::Isolate *isolate) { 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 node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS diff --git a/src/node_i18n.cc b/src/node_i18n.cc index 7f462d5aead087..f491d2191d7b55 100644 --- a/src/node_i18n.cc +++ b/src/node_i18n.cc @@ -46,6 +46,7 @@ #include "node.h" #include "node_buffer.h" +#include "node_errors.h" #include "env-inl.h" #include "util-inl.h" #include "base_object-inl.h" @@ -447,7 +448,7 @@ void Transcode(const FunctionCallbackInfo&args) { UErrorCode status = U_ZERO_ERROR; MaybeLocal result; - THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); + CHECK(Buffer::HasInstance(args[0])); SPREAD_BUFFER_ARG(args[0], ts_obj); const enum encoding fromEncoding = ParseEncoding(isolate, args[1], BUFFER); const enum encoding toEncoding = ParseEncoding(isolate, args[2], BUFFER); diff --git a/src/node_serdes.cc b/src/node_serdes.cc index 1995eb1b9b506b..6ace942c29fd56 100644 --- a/src/node_serdes.cc +++ b/src/node_serdes.cc @@ -1,5 +1,6 @@ #include "node_internals.h" #include "node_buffer.h" +#include "node_errors.h" #include "base_object-inl.h" namespace node { @@ -209,7 +210,8 @@ void SerializerContext::TransferArrayBuffer( if (id.IsNothing()) return; if (!args[1]->IsArrayBuffer()) - return ctx->env()->ThrowTypeError("arrayBuffer must be an ArrayBuffer"); + return node::THROW_ERR_INVALID_ARG_TYPE( + ctx->env(), "arrayBuffer must be an ArrayBuffer"); Local ab = args[1].As(); ctx->serializer_.TransferArrayBuffer(id.FromJust(), ab); @@ -255,7 +257,8 @@ void SerializerContext::WriteRawBytes(const FunctionCallbackInfo& args) { ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); if (!args[0]->IsUint8Array()) { - return ctx->env()->ThrowTypeError("source must be a Uint8Array"); + return node::THROW_ERR_INVALID_ARG_TYPE( + ctx->env(), "source must be a Uint8Array"); } ctx->serializer_.WriteRawBytes(Buffer::Data(args[0]), @@ -305,7 +308,8 @@ void DeserializerContext::New(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); if (!args[0]->IsUint8Array()) { - return env->ThrowTypeError("buffer must be a Uint8Array"); + return node::THROW_ERR_INVALID_ARG_TYPE( + env, "buffer must be a Uint8Array"); } new DeserializerContext(env, args.This(), args[0]); @@ -349,8 +353,8 @@ void DeserializerContext::TransferArrayBuffer( return; } - return ctx->env()->ThrowTypeError("arrayBuffer must be an ArrayBuffer or " - "SharedArrayBuffer"); + return node::THROW_ERR_INVALID_ARG_TYPE( + ctx->env(), "arrayBuffer must be an ArrayBuffer or SharedArrayBuffer"); } void DeserializerContext::GetWireFormatVersion( diff --git a/src/stream_base.cc b/src/stream_base.cc index 880e1a968dc020..801b7f4b2f4560 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -3,6 +3,7 @@ #include "node.h" #include "node_buffer.h" +#include "node_errors.h" #include "node_internals.h" #include "env-inl.h" #include "js_stream.h" @@ -175,7 +176,7 @@ int StreamBase::WriteBuffer(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); if (!args[1]->IsUint8Array()) { - env->ThrowTypeError("Second argument must be a buffer"); + node::THROW_ERR_INVALID_ARG_TYPE(env, "Second argument must be a buffer"); return 0; } diff --git a/src/util.h b/src/util.h index e871fc63a5c46a..c8bad8171e3bc1 100644 --- a/src/util.h +++ b/src/util.h @@ -414,12 +414,6 @@ class BufferValue : public MaybeStackBuffer { explicit BufferValue(v8::Isolate* isolate, v8::Local value); }; -#define THROW_AND_RETURN_UNLESS_BUFFER(env, obj) \ - do { \ - if (!Buffer::HasInstance(obj)) \ - return env->ThrowTypeError("argument should be a Buffer"); \ - } while (0) - #define SPREAD_BUFFER_ARG(val, name) \ CHECK((val)->IsArrayBufferView()); \ v8::Local name = (val).As(); \ diff --git a/test/parallel/test-buffer-alloc.js b/test/parallel/test-buffer-alloc.js index bce0ad83814435..daab9c9edc12e9 100644 --- a/test/parallel/test-buffer-alloc.js +++ b/test/parallel/test-buffer-alloc.js @@ -935,9 +935,13 @@ Buffer.poolSize = 0; assert(Buffer.allocUnsafe(1).parent instanceof ArrayBuffer); Buffer.poolSize = ps; -// Test Buffer.copy() segfault -assert.throws(() => Buffer.allocUnsafe(10).copy(), - /TypeError: argument should be a Buffer/); +common.expectsError( + () => Buffer.allocUnsafe(10).copy(), + { + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError, + message: 'argument must be a buffer' + }); const regErrorMsg = new RegExp('The first argument must be one of type string, Buffer, ' + @@ -982,7 +986,11 @@ common.expectsError(() => { const a = Buffer.alloc(1); const b = Buffer.alloc(1); a.copy(b, 0, 0x100000000, 0x100000001); -}, { code: undefined, type: RangeError, message: 'Index out of range' }); +}, { + code: 'ERR_INDEX_OUT_OF_RANGE', + type: RangeError, + message: 'Index out of range' +}); // Unpooled buffer (replaces SlowBuffer) { diff --git a/test/parallel/test-stream-base-typechecking.js b/test/parallel/test-stream-base-typechecking.js index 8d559a42dfc54a..a8652dc06357cc 100644 --- a/test/parallel/test-stream-base-typechecking.js +++ b/test/parallel/test-stream-base-typechecking.js @@ -1,13 +1,16 @@ 'use strict'; const common = require('../common'); -const assert = require('assert'); const net = require('net'); const server = net.createServer().listen(0, common.mustCall(() => { const client = net.connect(server.address().port, common.mustCall(() => { - assert.throws(() => { + common.expectsError(() => { client.write('broken', 'buffer'); - }, /^TypeError: Second argument must be a buffer$/); + }, { + type: TypeError, + code: 'ERR_INVALID_ARG_TYPE', + message: 'Second argument must be a buffer' + }); client.destroy(); server.close(); })); diff --git a/test/parallel/test-tls-basic-validations.js b/test/parallel/test-tls-basic-validations.js index 74d56546ebb469..3840acc0243898 100644 --- a/test/parallel/test-tls-basic-validations.js +++ b/test/parallel/test-tls-basic-validations.js @@ -7,35 +7,71 @@ if (!common.hasCrypto) const assert = require('assert'); const tls = require('tls'); -assert.throws(() => tls.createSecureContext({ ciphers: 1 }), - /TypeError: Ciphers must be a string/); +common.expectsError( + () => tls.createSecureContext({ ciphers: 1 }), + { + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError, + message: 'Ciphers must be a string' + }); -assert.throws(() => tls.createServer({ ciphers: 1 }), - /TypeError: Ciphers must be a string/); +common.expectsError( + () => tls.createServer({ ciphers: 1 }), + { + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError, + message: 'Ciphers must be a string' + }); -assert.throws(() => tls.createSecureContext({ key: 'dummykey', passphrase: 1 }), - /TypeError: Pass phrase must be a string/); +common.expectsError( + () => tls.createSecureContext({ key: 'dummykey', passphrase: 1 }), + { + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError, + message: 'Pass phrase must be a string' + }); -assert.throws(() => tls.createServer({ key: 'dummykey', passphrase: 1 }), - /TypeError: Pass phrase must be a string/); +common.expectsError( + () => tls.createServer({ key: 'dummykey', passphrase: 1 }), + { + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError, + message: 'Pass phrase must be a string' + }); -assert.throws(() => tls.createServer({ ecdhCurve: 1 }), - /TypeError: ECDH curve name must be a string/); +common.expectsError( + () => tls.createServer({ ecdhCurve: 1 }), + { + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError, + message: 'ECDH curve name must be a string' + }); -common.expectsError(() => tls.createServer({ handshakeTimeout: 'abcd' }), - { - code: 'ERR_INVALID_ARG_TYPE', - type: TypeError, - message: 'The "options.handshakeTimeout" property must ' + - 'be of type number. Received type string' - } +common.expectsError( + () => tls.createServer({ handshakeTimeout: 'abcd' }), + { + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError, + message: 'The "options.handshakeTimeout" property must ' + + 'be of type number. Received type string' + } ); -assert.throws(() => tls.createServer({ sessionTimeout: 'abcd' }), - /TypeError: Session timeout must be a 32-bit integer/); +common.expectsError( + () => tls.createServer({ sessionTimeout: 'abcd' }), + { + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError, + message: 'Session timeout must be a 32-bit integer' + }); -assert.throws(() => tls.createServer({ ticketKeys: 'abcd' }), - /TypeError: Ticket keys must be a buffer/); +common.expectsError( + () => tls.createServer({ ticketKeys: 'abcd' }), + { + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError, + message: 'Ticket keys must be a buffer' + }); assert.throws(() => tls.createServer({ ticketKeys: Buffer.alloc(0) }), /TypeError: Ticket keys length must be 48 bytes/);