diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js index d120d8ccae14e6..df5d667aeb7b39 100644 --- a/lib/internal/bootstrap/node.js +++ b/lib/internal/bootstrap/node.js @@ -101,6 +101,10 @@ setupGlobalURL(); } + if (process.binding('config').experimentalWorker) { + setupDOMException(); + } + // On OpenBSD process.execPath will be relative unless we // get the full path before process.execPath is used. if (process.platform === 'openbsd') { @@ -381,6 +385,11 @@ }); } + function setupDOMException() { + // Registers the constructor with C++. + NativeModule.require('internal/domexception'); + } + function setupInspector(originalConsole, wrappedConsole, CJSModule) { if (!process.config.variables.v8_enable_inspector) { return; diff --git a/lib/internal/domexception.js b/lib/internal/domexception.js new file mode 100644 index 00000000000000..fe371e099eb17f --- /dev/null +++ b/lib/internal/domexception.js @@ -0,0 +1,83 @@ +'use strict'; + +const { internalBinding } = require('internal/bootstrap/loaders'); +const { registerDOMException } = internalBinding('messaging'); +const { ERR_INVALID_THIS } = require('internal/errors').codes; + +const internalsMap = new WeakMap(); + +const nameToCodeMap = new Map(); + +class DOMException extends Error { + constructor(message = '', name = 'Error') { + super(); + internalsMap.set(this, { + message: `${message}`, + name: `${name}` + }); + } + + get name() { + const internals = internalsMap.get(this); + if (internals === undefined) { + throw new ERR_INVALID_THIS('DOMException'); + } + return internals.name; + } + + get message() { + const internals = internalsMap.get(this); + if (internals === undefined) { + throw new ERR_INVALID_THIS('DOMException'); + } + return internals.message; + } + + get code() { + const internals = internalsMap.get(this); + if (internals === undefined) { + throw new ERR_INVALID_THIS('DOMException'); + } + const code = nameToCodeMap.get(internals.name); + return code === undefined ? 0 : code; + } +} + +for (const [name, codeName, value] of [ + ['IndexSizeError', 'INDEX_SIZE_ERR', 1], + ['DOMStringSizeError', 'DOMSTRING_SIZE_ERR', 2], + ['HierarchyRequestError', 'HIERARCHY_REQUEST_ERR', 3], + ['WrongDocumentError', 'WRONG_DOCUMENT_ERR', 4], + ['InvalidCharacterError', 'INVALID_CHARACTER_ERR', 5], + ['NoDataAllowedError', 'NO_DATA_ALLOWED_ERR', 6], + ['NoModificationAllowedError', 'NO_MODIFICATION_ALLOWED_ERR', 7], + ['NotFoundError', 'NOT_FOUND_ERR', 8], + ['NotSupportedError', 'NOT_SUPPORTED_ERR', 9], + ['InUseAttributeError', 'INUSE_ATTRIBUTE_ERR', 10], + ['InvalidStateError', 'INVALID_STATE_ERR', 11], + ['SyntaxError', 'SYNTAX_ERR', 12], + ['InvalidModificationError', 'INVALID_MODIFICATION_ERR', 13], + ['NamespaceError', 'NAMESPACE_ERR', 14], + ['InvalidAccessError', 'INVALID_ACCESS_ERR', 15], + ['ValidationError', 'VALIDATION_ERR', 16], + ['TypeMismatchError', 'TYPE_MISMATCH_ERR', 17], + ['SecurityError', 'SECURITY_ERR', 18], + ['NetworkError', 'NETWORK_ERR', 19], + ['AbortError', 'ABORT_ERR', 20], + ['URLMismatchError', 'URL_MISMATCH_ERR', 21], + ['QuotaExceededError', 'QUOTA_EXCEEDED_ERR', 22], + ['TimeoutError', 'TIMEOUT_ERR', 23], + ['InvalidNodeTypeError', 'INVALID_NODE_TYPE_ERR', 24], + ['DataCloneError', 'DATA_CLONE_ERR', 25] + // There are some more error names, but since they don't have codes assigned, + // we don't need to care about them. +]) { + const desc = { enumerable: true, value }; + Object.defineProperty(DOMException, codeName, desc); + Object.defineProperty(DOMException.prototype, codeName, desc); + nameToCodeMap.set(name, value); +} + +module.exports = DOMException; + +registerDOMException(DOMException); diff --git a/node.gyp b/node.gyp index 300a20b6e306c4..207c72cdb56a29 100644 --- a/node.gyp +++ b/node.gyp @@ -106,6 +106,7 @@ 'lib/internal/constants.js', 'lib/internal/dns/promises.js', 'lib/internal/dns/utils.js', + 'lib/internal/domexception.js', 'lib/internal/encoding.js', 'lib/internal/errors.js', 'lib/internal/error-serdes.js', diff --git a/src/env.h b/src/env.h index 6c759c84e7685d..c7e75004bf0ceb 100644 --- a/src/env.h +++ b/src/env.h @@ -322,6 +322,7 @@ struct PackageConfig { V(buffer_prototype_object, v8::Object) \ V(context, v8::Context) \ V(domain_callback, v8::Function) \ + V(domexception_function, v8::Function) \ V(fdclose_constructor_template, v8::ObjectTemplate) \ V(fd_constructor_template, v8::ObjectTemplate) \ V(filehandlereadwrap_template, v8::ObjectTemplate) \ diff --git a/src/node_messaging.cc b/src/node_messaging.cc index be37cd39c366d0..7ddd3c4165c511 100644 --- a/src/node_messaging.cc +++ b/src/node_messaging.cc @@ -144,6 +144,21 @@ void Message::AddMessagePort(std::unique_ptr&& data) { namespace { +void ThrowDataCloneError(Environment* env, Local message) { + Local argv[] = { + message, + FIXED_ONE_BYTE_STRING(env->isolate(), "DataCloneError") + }; + Local exception; + Local domexception_ctor = env->domexception_function(); + CHECK(!domexception_ctor.IsEmpty()); + if (!domexception_ctor->NewInstance(env->context(), arraysize(argv), argv) + .ToLocal(&exception)) { + return; + } + env->isolate()->ThrowException(exception); +} + // This tells V8 how to serialize objects that it does not understand // (e.g. C++ objects) into the output buffer, in a way that our own // DeserializerDelegate understands how to unpack. @@ -153,7 +168,7 @@ class SerializerDelegate : public ValueSerializer::Delegate { : env_(env), context_(context), msg_(m) {} void ThrowDataCloneError(Local message) override { - env_->isolate()->ThrowException(Exception::Error(message)); + ThrowDataCloneError(env_, message); } Maybe WriteHostObject(Isolate* isolate, Local object) override { @@ -688,6 +703,13 @@ static void MessageChannel(const FunctionCallbackInfo& args) { .FromJust(); } +static void RegisterDOMException(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + CHECK_EQ(args.Length(), 1); + CHECK(args[0]->IsFunction()); + env->set_domexception_function(args[0].As()); +} + static void InitMessaging(Local target, Local unused, Local context, @@ -708,6 +730,8 @@ static void InitMessaging(Local target, env->message_port_constructor_string(), GetMessagePortConstructor(env, context).ToLocalChecked()) .FromJust(); + + env->SetMethod(target, "registerDOMException", RegisterDOMException); } } // anonymous namespace