From 2f9bafba08ace96bb69ce2dcbf92bff48c483a26 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Wed, 24 Apr 2019 01:24:23 +0800 Subject: [PATCH] bootstrap: delay the instantiation of maps in per-context scripts Instantiating maps renders the snapshot non-rehashable (v8 currently fails silently during `CreateBlob()`). Then the hash seed would always be the same and not recomputed if custom V8 snapshot is enabled. This patch delays the instantiation of the maps in domexception.js and make it lazy. PR-URL: https://github.com/nodejs/node/pull/27371 Reviewed-By: Anna Henningsen Reviewed-By: Refael Ackermann Reviewed-By: Ruben Bridgewater --- lib/internal/per_context/domexception.js | 82 +++++++++++++++--------- 1 file changed, 51 insertions(+), 31 deletions(-) diff --git a/lib/internal/per_context/domexception.js b/lib/internal/per_context/domexception.js index 83b8d55995201d..3852567ac6bdba 100644 --- a/lib/internal/per_context/domexception.js +++ b/lib/internal/per_context/domexception.js @@ -15,12 +15,28 @@ class ERR_INVALID_THIS extends TypeError { get code() { return 'ERR_INVALID_THIS'; } } -const internalsMap = new SafeWeakMap(); +let internalsMap; +let nameToCodeMap; +let isInitialized = false; -const nameToCodeMap = new SafeMap(); +// We need to instantiate the maps lazily because they render +// the snapshot non-rehashable. +// https://bugs.chromium.org/p/v8/issues/detail?id=6593 +function ensureInitialized() { + if (isInitialized) { + return; + } + internalsMap = new SafeWeakMap(); + nameToCodeMap = new SafeMap(); + forEachCode((name, codeName, value) => { + nameToCodeMap.set(name, value); + }); + isInitialized = true; +} class DOMException extends Error { constructor(message = '', name = 'Error') { + ensureInitialized(); super(); internalsMap.set(this, { message: `${message}`, @@ -29,6 +45,7 @@ class DOMException extends Error { } get name() { + ensureInitialized(); const internals = internalsMap.get(this); if (internals === undefined) { throw new ERR_INVALID_THIS('DOMException'); @@ -37,6 +54,7 @@ class DOMException extends Error { } get message() { + ensureInitialized(); const internals = internalsMap.get(this); if (internals === undefined) { throw new ERR_INVALID_THIS('DOMException'); @@ -45,6 +63,7 @@ class DOMException extends Error { } get code() { + ensureInitialized(); const internals = internalsMap.get(this); if (internals === undefined) { throw new ERR_INVALID_THIS('DOMException'); @@ -61,39 +80,40 @@ Object.defineProperties(DOMException.prototype, { code: { enumerable: true, configurable: true } }); -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] +function forEachCode(fn) { + fn('IndexSizeError', 'INDEX_SIZE_ERR', 1); + fn('DOMStringSizeError', 'DOMSTRING_SIZE_ERR', 2); + fn('HierarchyRequestError', 'HIERARCHY_REQUEST_ERR', 3); + fn('WrongDocumentError', 'WRONG_DOCUMENT_ERR', 4); + fn('InvalidCharacterError', 'INVALID_CHARACTER_ERR', 5); + fn('NoDataAllowedError', 'NO_DATA_ALLOWED_ERR', 6); + fn('NoModificationAllowedError', 'NO_MODIFICATION_ALLOWED_ERR', 7); + fn('NotFoundError', 'NOT_FOUND_ERR', 8); + fn('NotSupportedError', 'NOT_SUPPORTED_ERR', 9); + fn('InUseAttributeError', 'INUSE_ATTRIBUTE_ERR', 10); + fn('InvalidStateError', 'INVALID_STATE_ERR', 11); + fn('SyntaxError', 'SYNTAX_ERR', 12); + fn('InvalidModificationError', 'INVALID_MODIFICATION_ERR', 13); + fn('NamespaceError', 'NAMESPACE_ERR', 14); + fn('InvalidAccessError', 'INVALID_ACCESS_ERR', 15); + fn('ValidationError', 'VALIDATION_ERR', 16); + fn('TypeMismatchError', 'TYPE_MISMATCH_ERR', 17); + fn('SecurityError', 'SECURITY_ERR', 18); + fn('NetworkError', 'NETWORK_ERR', 19); + fn('AbortError', 'ABORT_ERR', 20); + fn('URLMismatchError', 'URL_MISMATCH_ERR', 21); + fn('QuotaExceededError', 'QUOTA_EXCEEDED_ERR', 22); + fn('TimeoutError', 'TIMEOUT_ERR', 23); + fn('InvalidNodeTypeError', 'INVALID_NODE_TYPE_ERR', 24); + fn('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. -]) { +} + +forEachCode((name, codeName, value) => { const desc = { enumerable: true, value }; Object.defineProperty(DOMException, codeName, desc); Object.defineProperty(DOMException.prototype, codeName, desc); - nameToCodeMap.set(name, value); -} +}); exports.DOMException = DOMException;