| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent a1f581a commit 34e8331
15 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1179,6 +1179,13 @@ because the `node:domain` module has been loaded at an earlier point in time. | |||
| 1179 | 1179 | The stack trace is extended to include the point in time at which the | |
| 1180 | 1180 | `node:domain` module had been loaded. | |
| 1181 | 1181 | ||
| 1182 | + <a id="ERR_DUPLICATE_STARTUP_SNAPSHOT_MAIN_FUNCTION"></a> | ||
| 1183 | + | ||
| 1184 | + ### `ERR_DUPLICATE_STARTUP_SNAPSHOT_MAIN_FUNCTION` | ||
| 1185 | + | ||
| 1186 | + [`v8.startupSnapshot.setDeserializeMainFunction()`][] could not be called | ||
| 1187 | + because it had already been called before. | ||
| 1188 | + | ||
| 1182 | 1189 | <a id="ERR_ENCODING_INVALID_ENCODED_DATA"></a> | |
| 1183 | 1190 | ||
| 1184 | 1191 | ### `ERR_ENCODING_INVALID_ENCODED_DATA` | |
@@ -2314,6 +2321,13 @@ has occurred when attempting to start the loop. | |||
| 2314 | 2321 | Once no more items are left in the queue, the idle loop must be suspended. This | |
| 2315 | 2322 | error indicates that the idle loop has failed to stop. | |
| 2316 | 2323 | ||
| 2324 | + <a id="ERR_NOT_BUILDING_SNAPSHOT"></a> | ||
| 2325 | + | ||
| 2326 | + ### `ERR_NOT_BUILDING_SNAPSHOT` | ||
| 2327 | + | ||
| 2328 | + An attempt was made to use operations that can only be used when building | ||
| 2329 | + V8 startup snapshot even though Node.js isn't building one. | ||
| 2330 | + | ||
| 2317 | 2331 | <a id="ERR_NO_CRYPTO"></a> | |
| 2318 | 2332 | ||
| 2319 | 2333 | ### `ERR_NO_CRYPTO` | |
@@ -3501,6 +3515,7 @@ The native call from `process.cpuUsage` could not be processed. | |||
| 3501 | 3515 | [`url.parse()`]: url.md#urlparseurlstring-parsequerystring-slashesdenotehost | |
| 3502 | 3516 | [`util.getSystemErrorName(error.errno)`]: util.md#utilgetsystemerrornameerr | |
| 3503 | 3517 | [`util.parseArgs()`]: util.md#utilparseargsconfig | |
| 3518 | + [`v8.startupSnapshot.setDeserializeMainFunction()`]: v8.md#v8startupsnapshotsetdeserializemainfunctioncallback-data | ||
| 3504 | 3519 | [`zlib`]: zlib.md | |
| 3505 | 3520 | [crypto digest algorithm]: crypto.md#cryptogethashes | |
| 3506 | 3521 | [debugger]: debugger.md | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -876,6 +876,137 @@ Called immediately after a promise continuation executes. This may be after a | |||
| 876 | 876 | Called when the promise receives a resolution or rejection value. This may | |
| 877 | 877 | occur synchronously in the case of `Promise.resolve()` or `Promise.reject()`. | |
| 878 | 878 | ||
| 879 | + ## Startup Snapshot API | ||
| 880 | + | ||
| 881 | + <!-- YAML | ||
| 882 | + added: REPLACEME | ||
| 883 | + --> | ||
| 884 | + | ||
| 885 | + > Stability: 1 - Experimental | ||
| 886 | + | ||
| 887 | + The `v8.startupSnapshot` interface can be used to add serialization and | ||
| 888 | + deserialization hooks for custom startup snapshots. Currently the startup | ||
| 889 | + snapshots can only be built into the Node.js binary from source. | ||
| 890 | + | ||
| 891 | + ```console | ||
| 892 | + $ cd /path/to/node | ||
| 893 | + $ ./configure --node-snapshot-main=entry.js | ||
| 894 | + $ make node | ||
| 895 | + # This binary contains the result of the execution of entry.js | ||
| 896 | + $ out/Release/node | ||
| 897 | + ``` | ||
| 898 | + | ||
| 899 | + In the example above, `entry.js` can use methods from the `v8.startupSnapshot` | ||
| 900 | + interface to specify how to save information for custom objects in the snapshot | ||
| 901 | + during serialization and how the information can be used to synchronize these | ||
| 902 | + objects during deserialization of the snapshot. For example, if the `entry.js` | ||
| 903 | + contains the following script: | ||
| 904 | + | ||
| 905 | + ```cjs | ||
| 906 | + 'use strict'; | ||
| 907 | + | ||
| 908 | + const fs = require('fs'); | ||
| 909 | + const zlib = require('zlib'); | ||
| 910 | + const path = require('path'); | ||
| 911 | + const assert = require('assert'); | ||
| 912 | + | ||
| 913 | + const { | ||
| 914 | + isBuildingSnapshot, | ||
| 915 | + addSerializeCallback, | ||
| 916 | + addDeserializeCallback, | ||
| 917 | + setDeserializeMainFunction | ||
| 918 | + } = require('v8').startupSnapshot; | ||
| 919 | + | ||
| 920 | + const filePath = path.resolve(__dirname, '../x1024.txt'); | ||
| 921 | + const storage = {}; | ||
| 922 | + | ||
| 923 | + assert(isBuildingSnapshot()); | ||
| 924 | + | ||
| 925 | + addSerializeCallback(({ filePath }) => { | ||
| 926 | + storage[filePath] = zlib.gzipSync(fs.readFileSync(filePath)); | ||
| 927 | + }, { filePath }); | ||
| 928 | + | ||
| 929 | + addDeserializeCallback(({ filePath }) => { | ||
| 930 | + storage[filePath] = zlib.gunzipSync(storage[filePath]); | ||
| 931 | + }, { filePath }); | ||
| 932 | + | ||
| 933 | + setDeserializeMainFunction(({ filePath }) => { | ||
| 934 | + console.log(storage[filePath].toString()); | ||
| 935 | + }, { filePath }); | ||
| 936 | + ``` | ||
| 937 | + | ||
| 938 | + The resulted binary will simply print the data deserialized from the snapshot | ||
| 939 | + during start up: | ||
| 940 | + | ||
| 941 | + ```console | ||
| 942 | + $ out/Release/node | ||
| 943 | + # Prints content of ./test/fixtures/x1024.txt | ||
| 944 | + ``` | ||
| 945 | + | ||
| 946 | + Currently the API is only available to a Node.js instance launched from the | ||
| 947 | + default snapshot, that is, the application deserialized from a user-land | ||
| 948 | + snapshot cannot use these APIs again. | ||
| 949 | + | ||
| 950 | + ### `v8.startupSnapshot.addSerializeCallback(callback[, data])` | ||
| 951 | + | ||
| 952 | + <!-- YAML | ||
| 953 | + added: REPLACEME | ||
| 954 | + --> | ||
| 955 | + | ||
| 956 | + * `callback` {Function} Callback to be invoked before serialization. | ||
| 957 | + * `data` {any} Optional data that will be passed to the `callback` when it | ||
| 958 | + gets called. | ||
| 959 | + | ||
| 960 | + Add a callback that will be called when the Node.js instance is about to | ||
| 961 | + get serialized into a snapshot and exit. This can be used to release | ||
| 962 | + resources that should not or cannot be serialized or to convert user data | ||
| 963 | + into a form more suitable for serialization. | ||
| 964 | + | ||
| 965 | + ### `v8.startupSnapshot.addDeserializeCallback(callback[, data])` | ||
| 966 | + | ||
| 967 | + <!-- YAML | ||
| 968 | + added: REPLACEME | ||
| 969 | + --> | ||
| 970 | + | ||
| 971 | + * `callback` {Function} Callback to be invoked after the snapshot is | ||
| 972 | + deserialized. | ||
| 973 | + * `data` {any} Optional data that will be passed to the `callback` when it | ||
| 974 | + gets called. | ||
| 975 | + | ||
| 976 | + Add a callback that will be called when the Node.js instance is deserialized | ||
| 977 | + from a snapshot. The `callback` and the `data` (if provided) will be | ||
| 978 | + serialized into the snapshot, they can be used to re-initialize the state | ||
| 979 | + of the application or to re-acquire resources that the application needs | ||
| 980 | + when the application is restarted from the snapshot. | ||
| 981 | + | ||
| 982 | + ### `v8.startupSnapshot.setDeserializeMainFunction(callback[, data])` | ||
| 983 | + | ||
| 984 | + <!-- YAML | ||
| 985 | + added: REPLACEME | ||
| 986 | + --> | ||
| 987 | + | ||
| 988 | + * `callback` {Function} Callback to be invoked as the entry point after the | ||
| 989 | + snapshot is deserialized. | ||
| 990 | + * `data` {any} Optional data that will be passed to the `callback` when it | ||
| 991 | + gets called. | ||
| 992 | + | ||
| 993 | + This sets the entry point of the Node.js application when it is deserialized | ||
| 994 | + from a snapshot. This can be called only once in the snapshot building | ||
| 995 | + script. If called, the deserialized application no longer needs an additional | ||
| 996 | + entry point script to start up and will simply invoke the callback along with | ||
| 997 | + the deserialized data (if provided), otherwise an entry point script still | ||
| 998 | + needs to be provided to the deserialized application. | ||
| 999 | + | ||
| 1000 | + ### `v8.startupSnapshot.isBuildingSnapshot()` | ||
| 1001 | + | ||
| 1002 | + <!-- YAML | ||
| 1003 | + added: REPLACEME | ||
| 1004 | + --> | ||
| 1005 | + | ||
| 1006 | + * Returns: {boolean} | ||
| 1007 | + | ||
| 1008 | + Returns true if the Node.js instance is run to build a snapshot. | ||
| 1009 | + | ||
| 879 | 1010 | [HTML structured clone algorithm]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm | |
| 880 | 1011 | [Hook Callbacks]: #hook-callbacks | |
| 881 | 1012 | [V8]: https://developers.google.com/v8/ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -53,7 +53,6 @@ function prepareMainThreadExecution(expandArgv1 = false, | |||
| 53 | 53 | setupCoverageHooks(process.env.NODE_V8_COVERAGE); | |
| 54 | 54 | } | |
| 55 | 55 | ||
| 56 | - | ||
| 57 | 56 | setupDebugEnv(); | |
| 58 | 57 | ||
| 59 | 58 | // Print stack trace on `SIGINT` if option `--trace-sigint` presents. | |
@@ -84,6 +83,8 @@ function prepareMainThreadExecution(expandArgv1 = false, | |||
| 84 | 83 | initializeDeprecations(); | |
| 85 | 84 | initializeWASI(); | |
| 86 | 85 | ||
| 86 | + require('internal/v8/startup_snapshot').runDeserializeCallbacks(); | ||
| 87 | + | ||
| 87 | 88 | if (!initialzeModules) { | |
| 88 | 89 | return; | |
| 89 | 90 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2,7 +2,10 @@ | |||
| 2 | 2 | ||
| 3 | 3 | const { ObjectDefineProperty } = primordials; | |
| 4 | 4 | const rawMethods = internalBinding('process_methods'); | |
| 5 | - | ||
| 5 | + const { | ||
| 6 | + addSerializeCallback, | ||
| 7 | + isBuildingSnapshot | ||
| 8 | + } = require('v8').startupSnapshot; | ||
| 6 | 9 | // TODO(joyeecheung): deprecate and remove these underscore methods | |
| 7 | 10 | process._debugProcess = rawMethods._debugProcess; | |
| 8 | 11 | process._debugEnd = rawMethods._debugEnd; | |
@@ -134,6 +137,12 @@ function refreshStderrOnSigWinch() { | |||
| 134 | 137 | stderr._refreshSize(); | |
| 135 | 138 | } | |
| 136 | 139 | ||
| 140 | + function addCleanup(fn) { | ||
| 141 | + if (isBuildingSnapshot()) { | ||
| 142 | + addSerializeCallback(fn); | ||
| 143 | + } | ||
| 144 | + } | ||
| 145 | + | ||
| 137 | 146 | function getStdout() { | |
| 138 | 147 | if (stdout) return stdout; | |
| 139 | 148 | stdout = createWritableStdioStream(1); | |
@@ -145,12 +154,14 @@ function getStdout() { | |||
| 145 | 154 | process.on('SIGWINCH', refreshStdoutOnSigWinch); | |
| 146 | 155 | } | |
| 147 | 156 | ||
| 148 | - internalBinding('mksnapshot').cleanups.push(function cleanupStdout() { | ||
| 157 | + addCleanup(function cleanupStdout() { | ||
| 149 | 158 | stdout._destroy = stdoutDestroy; | |
| 150 | 159 | stdout.destroy(); | |
| 151 | 160 | process.removeListener('SIGWINCH', refreshStdoutOnSigWinch); | |
| 152 | 161 | stdout = undefined; | |
| 153 | 162 | }); | |
| 163 | + // No need to add deserialize callback because stdout = undefined above | ||
| 164 | + // causes the stream to be lazily initialized again later. | ||
| 154 | 165 | return stdout; | |
| 155 | 166 | } | |
| 156 | 167 | ||
@@ -164,12 +175,14 @@ function getStderr() { | |||
| 164 | 175 | if (stderr.isTTY) { | |
| 165 | 176 | process.on('SIGWINCH', refreshStderrOnSigWinch); | |
| 166 | 177 | } | |
| 167 | - internalBinding('mksnapshot').cleanups.push(function cleanupStderr() { | ||
| 178 | + addCleanup(function cleanupStderr() { | ||
| 168 | 179 | stderr._destroy = stderrDestroy; | |
| 169 | 180 | stderr.destroy(); | |
| 170 | 181 | process.removeListener('SIGWINCH', refreshStderrOnSigWinch); | |
| 171 | 182 | stderr = undefined; | |
| 172 | 183 | }); | |
| 184 | + // No need to add deserialize callback because stderr = undefined above | ||
| 185 | + // causes the stream to be lazily initialized again later. | ||
| 173 | 186 | return stderr; | |
| 174 | 187 | } | |
| 175 | 188 | ||
@@ -256,10 +269,12 @@ function getStdin() { | |||
| 256 | 269 | } | |
| 257 | 270 | } | |
| 258 | 271 | ||
| 259 | - internalBinding('mksnapshot').cleanups.push(function cleanupStdin() { | ||
| 272 | + addCleanup(function cleanupStdin() { | ||
| 260 | 273 | stdin.destroy(); | |
| 261 | 274 | stdin = undefined; | |
| 262 | 275 | }); | |
| 276 | + // No need to add deserialize callback because stdin = undefined above | ||
| 277 | + // causes the stream to be lazily initialized again later. | ||
| 263 | 278 | return stdin; | |
| 264 | 279 | } | |
| 265 | 280 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -978,6 +978,8 @@ E('ERR_DOMAIN_CANNOT_SET_UNCAUGHT_EXCEPTION_CAPTURE', | |||
| 978 | 978 | 'The `domain` module is in use, which is mutually exclusive with calling ' + | |
| 979 | 979 | 'process.setUncaughtExceptionCaptureCallback()', | |
| 980 | 980 | Error); | |
| 981 | + E('ERR_DUPLICATE_STARTUP_SNAPSHOT_MAIN_FUNCTION', | ||
| 982 | + 'Deserialize main function is already configured.', Error); | ||
| 981 | 983 | E('ERR_ENCODING_INVALID_ENCODED_DATA', function(encoding, ret) { | |
| 982 | 984 | this.errno = ret; | |
| 983 | 985 | return `The encoded data was not valid for encoding ${encoding}`; | |
@@ -1456,6 +1458,8 @@ E('ERR_NETWORK_IMPORT_BAD_RESPONSE', | |||
| 1456 | 1458 | "import '%s' received a bad response: %s", Error); | |
| 1457 | 1459 | E('ERR_NETWORK_IMPORT_DISALLOWED', | |
| 1458 | 1460 | "import of '%s' by %s is not supported: %s", Error); | |
| 1461 | + E('ERR_NOT_BUILDING_SNAPSHOT', | ||
| 1462 | + 'Operation cannot be invoked when not building startup snapshot', Error); | ||
| 1459 | 1463 | E('ERR_NO_CRYPTO', | |
| 1460 | 1464 | 'Node.js is not compiled with OpenSSL crypto support', Error); | |
| 1461 | 1465 | E('ERR_NO_ICU', | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -9,7 +9,7 @@ const { | |||
| 9 | 9 | const binding = internalBinding('mksnapshot'); | |
| 10 | 10 | const { NativeModule } = require('internal/bootstrap/loaders'); | |
| 11 | 11 | const { | |
| 12 | - compileSnapshotMain, | ||
| 12 | + compileSerializeMain, | ||
| 13 | 13 | } = binding; | |
| 14 | 14 | ||
| 15 | 15 | const { | |
@@ -83,7 +83,7 @@ const supportedModules = new SafeSet(new SafeArrayIterator([ | |||
| 83 | 83 | 'v8', | |
| 84 | 84 | // 'vm', | |
| 85 | 85 | // 'worker_threads', | |
| 86 | - // 'zlib', | ||
| 86 | + 'zlib', | ||
| 87 | 87 | ])); | |
| 88 | 88 | ||
| 89 | 89 | const warnedModules = new SafeSet(); | |
@@ -117,25 +117,22 @@ function main() { | |||
| 117 | 117 | } = require('internal/bootstrap/pre_execution'); | |
| 118 | 118 | ||
| 119 | 119 | prepareMainThreadExecution(true, false); | |
| 120 | - process.once('beforeExit', function runCleanups() { | ||
| 121 | - for (const cleanup of binding.cleanups) { | ||
| 122 | - cleanup(); | ||
| 123 | - } | ||
| 124 | - }); | ||
| 125 | 120 | ||
| 126 | 121 | const file = process.argv[1]; | |
| 127 | 122 | const path = require('path'); | |
| 128 | 123 | const filename = path.resolve(file); | |
| 129 | 124 | const dirname = path.dirname(filename); | |
| 130 | 125 | const source = readFileSync(file, 'utf-8'); | |
| 131 | - const snapshotMainFunction = compileSnapshotMain(filename, source); | ||
| 126 | + const serializeMainFunction = compileSerializeMain(filename, source); | ||
| 127 | + | ||
| 128 | + require('internal/v8/startup_snapshot').initializeCallbacks(); | ||
| 132 | 129 | ||
| 133 | 130 | if (getOptionValue('--inspect-brk')) { | |
| 134 | 131 | internalBinding('inspector').callAndPauseOnStart( | |
| 135 | - snapshotMainFunction, undefined, | ||
| 132 | + serializeMainFunction, undefined, | ||
| 136 | 133 | requireForUserSnapshot, filename, dirname); | |
| 137 | 134 | } else { | |
| 138 | - snapshotMainFunction(requireForUserSnapshot, filename, dirname); | ||
| 135 | + serializeMainFunction(requireForUserSnapshot, filename, dirname); | ||
| 139 | 136 | } | |
| 140 | 137 | } | |
| 141 | 138 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments