diff --git a/src/env.h b/src/env.h index 5cf789f9bb7ee9..65995dd14b77fb 100644 --- a/src/env.h +++ b/src/env.h @@ -231,6 +231,7 @@ constexpr size_t kFsStatsBufferLength = V(dns_soa_string, "SOA") \ V(dns_srv_string, "SRV") \ V(dns_txt_string, "TXT") \ + V(domexception_string, "DOMException") \ V(done_string, "done") \ V(duration_string, "duration") \ V(ecdh_string, "ECDH") \ @@ -462,6 +463,7 @@ constexpr size_t kFsStatsBufferLength = V(blocklist_constructor_template, v8::FunctionTemplate) \ V(compiled_fn_entry_template, v8::ObjectTemplate) \ V(dir_instance_template, v8::ObjectTemplate) \ + V(domexception_constructor_template, v8::FunctionTemplate) \ V(fd_constructor_template, v8::ObjectTemplate) \ V(fdclose_constructor_template, v8::ObjectTemplate) \ V(filehandlereadwrap_template, v8::ObjectTemplate) \ diff --git a/src/node_errors.cc b/src/node_errors.cc index be4baa80fd1b80..b015ca5db8cd51 100644 --- a/src/node_errors.cc +++ b/src/node_errors.cc @@ -1,8 +1,10 @@ #include #include +#include "base_object-inl.h" #include "debug_utils-inl.h" #include "node_errors.h" +#include "memory_tracker-inl.h" #include "node_external_reference.h" #include "node_internals.h" #include "node_process-inl.h" @@ -18,6 +20,8 @@ using v8::Context; using v8::Exception; using v8::Function; using v8::FunctionCallbackInfo; +using v8::FunctionTemplate; +using v8::Global; using v8::HandleScope; using v8::Int32; using v8::Isolate; @@ -873,6 +877,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) { registry->Register(SetEnhanceStackForFatalException); registry->Register(NoSideEffectsToString); registry->Register(TriggerUncaughtException); + DOMException::RegisterExternalReferences(registry); } void Initialize(Local target, @@ -889,6 +894,7 @@ void Initialize(Local target, env->SetMethodNoSideEffect( target, "noSideEffectsToString", NoSideEffectsToString); env->SetMethod(target, "triggerUncaughtException", TriggerUncaughtException); + DOMException::Initialize(env, target); } void DecorateErrorStack(Environment* env, @@ -1044,6 +1050,283 @@ void TriggerUncaughtException(Isolate* isolate, const v8::TryCatch& try_catch) { } // namespace errors +namespace { +// This is annoying, but v8 does not give a nice way of getting a good +// stack property so we have to create an error object and capture a +// stack that way. +Global MakeErrorStack( + Environment* env, + const Local& message, + const Local& name) { + Local message_str; + Local name_str; + if (!message->ToDetailString(env->context()).ToLocal(&message_str) || + !name->ToDetailString(env->context()).ToLocal(&name_str)) { + return Global(); + } + Local err = Exception::Error(message_str).As(); + + if (!err->Set(env->context(), env->name_string(), name_str).FromJust()) { + return Global(); + } + + Local stack; + if (!err->Get(env->context(), env->stack_string()).ToLocal(&stack)) { + return Global(); + } + + return Global(env->isolate(), stack); +} +} // namespace + +bool DOMException::HasInstance(Environment* env, v8::Local value) { + return GetConstructorTemplate(env)->HasInstance(value); +} + +Local DOMException::GetConstructorTemplate(Environment* env) { + Local tmpl = env->domexception_constructor_template(); + if (tmpl.IsEmpty()) { + Local proto = v8::FunctionTemplate::New(env->isolate()); + proto->RemovePrototype(); + proto->SetIntrinsicDataProperty( + FIXED_ONE_BYTE_STRING(env->isolate(), "prototype"), + v8::kErrorPrototype); + proto->Inherit(BaseObject::GetConstructorTemplate(env)); + + tmpl = env->NewFunctionTemplate(New); + tmpl->Inherit(proto); + tmpl->InstanceTemplate()->SetInternalFieldCount( + BaseObject::kInternalFieldCount); + tmpl->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "DOMException")); + + tmpl->PrototypeTemplate()->SetAccessorProperty( + env->name_string(), + FunctionTemplate::New(env->isolate(), GetName), + Local(), + v8::PropertyAttribute::ReadOnly); + tmpl->PrototypeTemplate()->SetAccessorProperty( + env->message_string(), + FunctionTemplate::New(env->isolate(), GetMessage), + Local(), + v8::PropertyAttribute::ReadOnly); + tmpl->PrototypeTemplate()->SetAccessorProperty( + env->stack_string(), + FunctionTemplate::New(env->isolate(), GetStack), + Local(), + v8::PropertyAttribute::ReadOnly); + tmpl->PrototypeTemplate()->SetAccessorProperty( + env->code_string(), + FunctionTemplate::New(env->isolate(), GetCode), + Local(), + v8::PropertyAttribute::ReadOnly); + + env->set_domexception_constructor_template(tmpl); + } + return tmpl; +} + +void DOMException::Initialize(Environment* env, v8::Local target) { + env->SetConstructorFunction( + target, + "DOMException", + GetConstructorTemplate(env), + Environment::SetConstructorFunctionFlag::NONE); +} + +void DOMException::RegisterExternalReferences( + ExternalReferenceRegistry* registry) { + registry->Register(New); + registry->Register(GetMessage); + registry->Register(GetName); + registry->Register(GetStack); + registry->Register(GetCode); +} + +BaseObjectPtr DOMException::Create( + Environment* env, + const TransferData& transferData) { + HandleScope scope(env->isolate()); + + Local ctor; + if (!GetConstructorTemplate(env)->GetFunction(env->context()).ToLocal(&ctor)) + return BaseObjectPtr(); + + Local obj; + if (!ctor->NewInstance(env->context()).ToLocal(&obj)) + return BaseObjectPtr(); + + return MakeBaseObject(env, obj, transferData); +} + +BaseObjectPtr DOMException::Create( + Environment* env, + const std::string& message, + const std::string& name) { + HandleScope scope(env->isolate()); + + Local ctor; + if (!GetConstructorTemplate(env)->GetFunction(env->context()).ToLocal(&ctor)) + return BaseObjectPtr(); + + Local obj; + if (!ctor->NewInstance(env->context()).ToLocal(&obj)) + return BaseObjectPtr(); + + return MakeBaseObject(env, obj, message, name); +} + +void DOMException::New(const FunctionCallbackInfo& args) { + CHECK(args.IsConstructCall()); + + new DOMException( + Environment::GetCurrent(args), + args.This(), + args[0], + args[1]); +} + +DOMException::DOMException( + Environment* env, + Local object, + Local message, + Local name) + : BaseObject(env, object), + message(env->isolate(), message), + name(env->isolate(), name), + stack(env->isolate(), MakeErrorStack(env, message, name)) {} + +DOMException::DOMException( + Environment* env, + Local object, + const std::string& message, + const std::string& name) + : BaseObject(env, object) { + Local message_value = + String::NewFromUtf8(env->isolate(), message.c_str()).ToLocalChecked(); + Local name_value = + String::NewFromUtf8(env->isolate(), name.c_str()).ToLocalChecked(); + this->message.Reset(env->isolate(), message_value); + this->name.Reset(env->isolate(), name_value); + this->stack = MakeErrorStack(env, message_value, name_value); +} + +DOMException::DOMException( + Environment* env, + Local object, + const TransferData& transferData) + : BaseObject(env, object), + message( + env->isolate(), + String::NewFromUtf8( + env->isolate(), + transferData.get_message().c_str()).ToLocalChecked()), + name( + env->isolate(), + String::NewFromUtf8( + env->isolate(), + transferData.get_name().c_str()).ToLocalChecked()), + stack( + env->isolate(), + String::NewFromUtf8( + env->isolate(), + transferData.get_stack().c_str()).ToLocalChecked()) {} + +void DOMException::GetName(const FunctionCallbackInfo& args) { + DOMException* ex; + ASSIGN_OR_RETURN_UNWRAP(&ex, args.Holder()); + args.GetReturnValue().Set(ex->name); +} + +void DOMException::GetMessage(const FunctionCallbackInfo& args) { + DOMException* ex; + ASSIGN_OR_RETURN_UNWRAP(&ex, args.Holder()); + args.GetReturnValue().Set(ex->message); +} + +void DOMException::GetStack(const FunctionCallbackInfo& args) { + DOMException* ex; + ASSIGN_OR_RETURN_UNWRAP(&ex, args.Holder()); + args.GetReturnValue().Set(ex->stack); +} + +#define DOMEXCEPTION_CODES(V) \ + V(IndexSize, 1) \ + V(DOMStringSize, 2) \ + V(HierarchyRequest, 3) \ + V(WrongDocument, 4) \ + V(InvalidCharacter, 5) \ + V(NoDataAllowed, 6) \ + V(NoModificationAllowed, 7) \ + V(NotFound, 8) \ + V(NotSupported, 9) \ + V(InUseAttribute, 10) \ + V(InvalidState, 11) \ + V(Syntax, 12) \ + V(InvalidModification, 13) \ + V(Namespace, 14) \ + V(InvalidAccess, 15) \ + V(Validation, 16) \ + V(TypeMismatch, 17) \ + V(Security, 18) \ + V(Network, 19) \ + V(Abort, 20) \ + V(URLMismatch, 21) \ + V(QuotaExceeded, 22) \ + V(Timeout, 23) \ + V(InvalidNodeType, 24) \ + V(DataClone, 25) + +void DOMException::GetCode(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + DOMException* ex; + ASSIGN_OR_RETURN_UNWRAP(&ex, args.Holder()); + + Local name = ex->name.Get(env->isolate()); + Utf8Value val(env->isolate(), name); + int ret = 0; + +#define V(name, code) if (strcmp(#name "Error", *val) == 0) ret = code; + DOMEXCEPTION_CODES(V) + args.GetReturnValue().Set(ret); +#undef V +} +#undef DOMEXCEPTION_CODES + +void DOMException::MemoryInfo(node::MemoryTracker* tracker) const { + tracker->TrackField("message", message); + tracker->TrackField("name", name); + tracker->TrackField("stack", stack); +} + +std::unique_ptr DOMException::CloneForMessaging() const { + return std::make_unique(env(), *this); +} + +DOMException::TransferData::TransferData( + Environment* env, + const DOMException& exception) { + Utf8Value message(env->isolate(), exception.message.Get(env->isolate())); + Utf8Value name(env->isolate(), exception.name.Get(env->isolate())); + Utf8Value stack(env->isolate(), exception.stack.Get(env->isolate())); + this->message = *message; + this->name = *name; + this->stack = *stack; +} + +BaseObjectPtr DOMException::TransferData::Deserialize( + Environment* env, + v8::Local context, + std::unique_ptr self) { + return DOMException::Create(env, *this); +} + +void DOMException::TransferData::MemoryInfo( + node::MemoryTracker* tracker) const { + tracker->TrackField("message", message); + tracker->TrackField("name", name); + tracker->TrackField("stack", stack); +} + } // namespace node NODE_MODULE_CONTEXT_AWARE_INTERNAL(errors, node::errors::Initialize) diff --git a/src/node_errors.h b/src/node_errors.h index f540b3e2a37de4..c1d0ac5d81fbb1 100644 --- a/src/node_errors.h +++ b/src/node_errors.h @@ -1,10 +1,16 @@ #ifndef SRC_NODE_ERRORS_H_ #define SRC_NODE_ERRORS_H_ +#include "v8-debug.h" +#include "v8-function-callback.h" #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#include "base_object.h" #include "debug_utils-inl.h" #include "env.h" +#include "memory_tracker.h" +#include "node_external_reference.h" +#include "node_worker.h" #include "v8.h" // Use ostringstream to print exact-width integer types @@ -270,6 +276,92 @@ void DecorateErrorStack(Environment* env, const errors::TryCatchScope& try_catch); } // namespace errors +class DOMException : public BaseObject { + public: + class TransferData; + + static void RegisterExternalReferences(ExternalReferenceRegistry* registry); + + static v8::Local GetConstructorTemplate( + Environment* env); + + static bool HasInstance(Environment* env, v8::Local value); + + static void Initialize(Environment* env, v8::Local object); + + static BaseObjectPtr Create( + Environment* env, + const TransferData& transferData); + + static BaseObjectPtr Create( + Environment* env, + const std::string& message, + const std::string& name = "DOMException"); + + static void New(const v8::FunctionCallbackInfo& args); + static void GetMessage(const v8::FunctionCallbackInfo& args); + static void GetName(const v8::FunctionCallbackInfo& args); + static void GetStack(const v8::FunctionCallbackInfo& args); + static void GetCode(const v8::FunctionCallbackInfo& args); + + DOMException( + Environment* env, + v8::Local object, + v8::Local message, + v8::Local name); + + DOMException( + Environment* env, + v8::Local object, + const std::string& message, + const std::string& name = "DOMException"); + + DOMException( + Environment* env, + v8::Local object, + const TransferData& transferData); + + void MemoryInfo(node::MemoryTracker* tracker) const override; + SET_MEMORY_INFO_NAME(DOMException); + SET_SELF_SIZE(DOMException); + + inline TransferMode GetTransferMode() const override { + return TransferMode::kCloneable; + } + + std::unique_ptr CloneForMessaging() const override; + + class TransferData : public worker::TransferData { + public: + explicit TransferData(Environment* env, const DOMException& ex); + + BaseObjectPtr Deserialize( + Environment* env, + v8::Local context, + std::unique_ptr self) override; + + SET_MEMORY_INFO_NAME(DOMException::TransferData); + SET_SELF_SIZE(TransferData); + void MemoryInfo(node::MemoryTracker* tracker) const override; + + const std::string& get_message() const { return message; } + const std::string& get_name() const { return name; } + const std::string& get_stack() const { return stack; } + + private: + std::string message; + std::string name; + std::string stack; + }; + + private: + v8::Global message; + v8::Global name; + v8::Global stack; + + friend class TransferData; +}; + } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS