Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 27564a4

Browse filesBrowse files
devsnekMylesBorins
authored andcommitted
vm: add code cache support for SourceTextModule
PR-URL: #31278 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net>
1 parent c252356 commit 27564a4
Copy full SHA for 27564a4

File tree

Expand file treeCollapse file tree

8 files changed

+162
-10
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

8 files changed

+162
-10
lines changed
Open diff view settings
Collapse file

‎doc/api/errors.md‎

Copy file name to clipboardExpand all lines: doc/api/errors.md
+10Lines changed: 10 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -1988,6 +1988,16 @@ the following reasons:
19881988
* It is being linked (`linkingStatus` is `'linking'`)
19891989
* Linking has failed for this module (`linkingStatus` is `'errored'`)
19901990

1991+
<a id="ERR_VM_MODULE_CACHED_DATA_REJECTED"></a>
1992+
### `ERR_VM_MODULE_CACHED_DATA_REJECTED`
1993+
1994+
The `cachedData` option passed to a module constructor is invalid.
1995+
1996+
<a id="ERR_VM_MODULE_CANNOT_CREATE_CACHED_DATA"></a>
1997+
### `ERR_VM_MODULE_CANNOT_CREATE_CACHED_DATA`
1998+
1999+
Cached data cannot be created for modules which have already been evaluated.
2000+
19912001
<a id="ERR_VM_MODULE_DIFFERENT_CONTEXT"></a>
19922002
### `ERR_VM_MODULE_DIFFERENT_CONTEXT`
19932003

Collapse file

‎doc/api/vm.md‎

Copy file name to clipboardExpand all lines: doc/api/vm.md
+26Lines changed: 26 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,10 @@ defined in the ECMAScript specification.
566566
* `identifier` {string} String used in stack traces.
567567
**Default:** `'vm:module(i)'` where `i` is a context-specific ascending
568568
index.
569+
* `cachedData` {Buffer|TypedArray|DataView} Provides an optional `Buffer` or
570+
`TypedArray`, or `DataView` with V8's code cache data for the supplied
571+
source. The `code` must be the same as the module from which this
572+
`cachedData` was created.
569573
* `context` {Object} The [contextified][] object as returned by the
570574
`vm.createContext()` method, to compile and evaluate this `Module` in.
571575
* `lineOffset` {integer} Specifies the line number offset that is displayed
@@ -621,6 +625,28 @@ const contextifiedObject = vm.createContext({ secret: 42 });
621625
})();
622626
```
623627
628+
### `sourceTextModule.createCachedData()`
629+
<!-- YAML
630+
added: REPLACEME
631+
-->
632+
633+
* Returns: {Buffer}
634+
635+
Creates a code cache that can be used with the SourceTextModule constructor's
636+
`cachedData` option. Returns a Buffer. This method may be called any number
637+
of times before the module has been evaluated.
638+
639+
```js
640+
// Create an initial module
641+
const module = new vm.SourceTextModule('const a = 1;');
642+
643+
// Create cached data from this module
644+
const cachedData = module.createCachedData();
645+
646+
// Create a new module using the cached data. The code must be the same.
647+
const module2 = new vm.SourceTextModule('const a = 1;', { cachedData });
648+
```
649+
624650
## Class: `vm.SyntheticModule`
625651
<!-- YAML
626652
added: v13.0.0
Collapse file

‎lib/internal/errors.js‎

Copy file name to clipboardExpand all lines: lib/internal/errors.js
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1351,6 +1351,8 @@ E('ERR_VALID_PERFORMANCE_ENTRY_TYPE',
13511351
E('ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING',
13521352
'A dynamic import callback was not specified.', TypeError);
13531353
E('ERR_VM_MODULE_ALREADY_LINKED', 'Module has already been linked', Error);
1354+
E('ERR_VM_MODULE_CANNOT_CREATE_CACHED_DATA',
1355+
'Cached data cannot be created for a module which has been evaluated', Error);
13541356
E('ERR_VM_MODULE_DIFFERENT_CONTEXT',
13551357
'Linked modules must use the same context', Error);
13561358
E('ERR_VM_MODULE_LINKING_ERRORED',
Collapse file

‎lib/internal/vm/module.js‎

Copy file name to clipboardExpand all lines: lib/internal/vm/module.js
+27-2Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ const {
1111
} = primordials;
1212

1313
const { isContext } = internalBinding('contextify');
14-
const { isModuleNamespaceObject } = require('internal/util/types');
14+
const {
15+
isModuleNamespaceObject,
16+
isArrayBufferView,
17+
} = require('internal/util/types');
1518
const {
1619
getConstructorOf,
1720
customInspectSymbol,
@@ -21,6 +24,7 @@ const {
2124
ERR_INVALID_ARG_TYPE,
2225
ERR_VM_MODULE_ALREADY_LINKED,
2326
ERR_VM_MODULE_DIFFERENT_CONTEXT,
27+
ERR_VM_MODULE_CANNOT_CREATE_CACHED_DATA,
2428
ERR_VM_MODULE_LINKING_ERRORED,
2529
ERR_VM_MODULE_NOT_MODULE,
2630
ERR_VM_MODULE_STATUS,
@@ -107,7 +111,8 @@ class Module {
107111

108112
if (sourceText !== undefined) {
109113
this[kWrap] = new ModuleWrap(identifier, context, sourceText,
110-
options.lineOffset, options.columnOffset);
114+
options.lineOffset, options.columnOffset,
115+
options.cachedData);
111116

112117
binding.callbackMap.set(this[kWrap], {
113118
initializeImportMeta: options.initializeImportMeta,
@@ -253,6 +258,7 @@ class SourceTextModule extends Module {
253258
importModuleDynamically,
254259
context,
255260
identifier,
261+
cachedData,
256262
} = options;
257263

258264
validateInt32(lineOffset, 'options.lineOffset');
@@ -271,12 +277,21 @@ class SourceTextModule extends Module {
271277
importModuleDynamically);
272278
}
273279

280+
if (cachedData !== undefined && !isArrayBufferView(cachedData)) {
281+
throw new ERR_INVALID_ARG_TYPE(
282+
'options.cachedData',
283+
['Buffer', 'TypedArray', 'DataView'],
284+
cachedData
285+
);
286+
}
287+
274288
super({
275289
sourceText,
276290
context,
277291
identifier,
278292
lineOffset,
279293
columnOffset,
294+
cachedData,
280295
initializeImportMeta,
281296
importModuleDynamically,
282297
});
@@ -348,6 +363,16 @@ class SourceTextModule extends Module {
348363
}
349364
return super.error;
350365
}
366+
367+
createCachedData() {
368+
const { status } = this;
369+
if (status === 'evaluating' ||
370+
status === 'evaluated' ||
371+
status === 'errored') {
372+
throw new ERR_VM_MODULE_CANNOT_CREATE_CACHED_DATA();
373+
}
374+
return this[kWrap].createCachedData();
375+
}
351376
}
352377

353378
class SyntheticModule extends Module {
Collapse file

‎src/module_wrap.cc‎

Copy file name to clipboardExpand all lines: src/module_wrap.cc
+65-8Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22

33
#include "env.h"
44
#include "memory_tracker-inl.h"
5+
#include "node_contextify.h"
56
#include "node_errors.h"
7+
#include "node_internals.h"
8+
#include "node_process.h"
69
#include "node_url.h"
7-
#include "util-inl.h"
8-
#include "node_contextify.h"
910
#include "node_watchdog.h"
10-
#include "node_process.h"
11+
#include "util-inl.h"
1112

1213
#include <sys/stat.h> // S_IFDIR
1314

@@ -22,6 +23,7 @@ using node::contextify::ContextifyContext;
2223
using node::url::URL;
2324
using node::url::URL_FLAGS_FAILED;
2425
using v8::Array;
26+
using v8::ArrayBufferView;
2527
using v8::Context;
2628
using v8::Function;
2729
using v8::FunctionCallbackInfo;
@@ -44,6 +46,7 @@ using v8::Promise;
4446
using v8::ScriptCompiler;
4547
using v8::ScriptOrigin;
4648
using v8::String;
49+
using v8::UnboundModuleScript;
4750
using v8::Undefined;
4851
using v8::Value;
4952

@@ -131,7 +134,7 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
131134
// new ModuleWrap(url, context, exportNames, syntheticExecutionFunction)
132135
CHECK(args[3]->IsFunction());
133136
} else {
134-
// new ModuleWrap(url, context, source, lineOffset, columOffset)
137+
// new ModuleWrap(url, context, source, lineOffset, columOffset, cachedData)
135138
CHECK(args[2]->IsString());
136139
CHECK(args[3]->IsNumber());
137140
line_offset = args[3].As<Integer>();
@@ -167,6 +170,17 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
167170
module = Module::CreateSyntheticModule(isolate, url, export_names,
168171
SyntheticModuleEvaluationStepsCallback);
169172
} else {
173+
ScriptCompiler::CachedData* cached_data = nullptr;
174+
if (!args[5]->IsUndefined()) {
175+
CHECK(args[5]->IsArrayBufferView());
176+
Local<ArrayBufferView> cached_data_buf = args[5].As<ArrayBufferView>();
177+
uint8_t* data = static_cast<uint8_t*>(
178+
cached_data_buf->Buffer()->GetBackingStore()->Data());
179+
cached_data =
180+
new ScriptCompiler::CachedData(data + cached_data_buf->ByteOffset(),
181+
cached_data_buf->ByteLength());
182+
}
183+
170184
Local<String> source_text = args[2].As<String>();
171185
ScriptOrigin origin(url,
172186
line_offset, // line offset
@@ -178,8 +192,15 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
178192
False(isolate), // is WASM
179193
True(isolate), // is ES Module
180194
host_defined_options);
181-
ScriptCompiler::Source source(source_text, origin);
182-
if (!ScriptCompiler::CompileModule(isolate, &source).ToLocal(&module)) {
195+
ScriptCompiler::Source source(source_text, origin, cached_data);
196+
ScriptCompiler::CompileOptions options;
197+
if (source.GetCachedData() == nullptr) {
198+
options = ScriptCompiler::kNoCompileOptions;
199+
} else {
200+
options = ScriptCompiler::kConsumeCodeCache;
201+
}
202+
if (!ScriptCompiler::CompileModule(isolate, &source, options)
203+
.ToLocal(&module)) {
183204
if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
184205
CHECK(!try_catch.Message().IsEmpty());
185206
CHECK(!try_catch.Exception().IsEmpty());
@@ -189,6 +210,13 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
189210
}
190211
return;
191212
}
213+
if (options == ScriptCompiler::kConsumeCodeCache &&
214+
source.GetCachedData()->rejected) {
215+
THROW_ERR_VM_MODULE_CACHED_DATA_REJECTED(
216+
env, "cachedData buffer was rejected");
217+
try_catch.ReThrow();
218+
return;
219+
}
192220
}
193221
}
194222

@@ -1507,8 +1535,7 @@ MaybeLocal<Value> ModuleWrap::SyntheticModuleEvaluationStepsCallback(
15071535
return ret;
15081536
}
15091537

1510-
void ModuleWrap::SetSyntheticExport(
1511-
const v8::FunctionCallbackInfo<v8::Value>& args) {
1538+
void ModuleWrap::SetSyntheticExport(const FunctionCallbackInfo<Value>& args) {
15121539
Isolate* isolate = args.GetIsolate();
15131540
Local<Object> that = args.This();
15141541

@@ -1528,6 +1555,35 @@ void ModuleWrap::SetSyntheticExport(
15281555
USE(module->SetSyntheticModuleExport(isolate, export_name, export_value));
15291556
}
15301557

1558+
void ModuleWrap::CreateCachedData(const FunctionCallbackInfo<Value>& args) {
1559+
Isolate* isolate = args.GetIsolate();
1560+
Local<Object> that = args.This();
1561+
1562+
ModuleWrap* obj;
1563+
ASSIGN_OR_RETURN_UNWRAP(&obj, that);
1564+
1565+
CHECK(!obj->synthetic_);
1566+
1567+
Local<Module> module = obj->module_.Get(isolate);
1568+
1569+
CHECK_LT(module->GetStatus(), v8::Module::Status::kEvaluating);
1570+
1571+
Local<UnboundModuleScript> unbound_module_script =
1572+
module->GetUnboundModuleScript();
1573+
std::unique_ptr<ScriptCompiler::CachedData> cached_data(
1574+
ScriptCompiler::CreateCodeCache(unbound_module_script));
1575+
Environment* env = Environment::GetCurrent(args);
1576+
if (!cached_data) {
1577+
args.GetReturnValue().Set(Buffer::New(env, 0).ToLocalChecked());
1578+
} else {
1579+
MaybeLocal<Object> buf =
1580+
Buffer::Copy(env,
1581+
reinterpret_cast<const char*>(cached_data->data),
1582+
cached_data->length);
1583+
args.GetReturnValue().Set(buf.ToLocalChecked());
1584+
}
1585+
}
1586+
15311587
void ModuleWrap::Initialize(Local<Object> target,
15321588
Local<Value> unused,
15331589
Local<Context> context,
@@ -1543,6 +1599,7 @@ void ModuleWrap::Initialize(Local<Object> target,
15431599
env->SetProtoMethod(tpl, "instantiate", Instantiate);
15441600
env->SetProtoMethod(tpl, "evaluate", Evaluate);
15451601
env->SetProtoMethod(tpl, "setExport", SetSyntheticExport);
1602+
env->SetProtoMethodNoSideEffect(tpl, "createCachedData", CreateCachedData);
15461603
env->SetProtoMethodNoSideEffect(tpl, "getNamespace", GetNamespace);
15471604
env->SetProtoMethodNoSideEffect(tpl, "getStatus", GetStatus);
15481605
env->SetProtoMethodNoSideEffect(tpl, "getError", GetError);
Collapse file

‎src/module_wrap.h‎

Copy file name to clipboardExpand all lines: src/module_wrap.h
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ class ModuleWrap : public BaseObject {
7575
v8::Local<v8::Context> context, v8::Local<v8::Module> module);
7676
static void SetSyntheticExport(
7777
const v8::FunctionCallbackInfo<v8::Value>& args);
78+
static void CreateCachedData(const v8::FunctionCallbackInfo<v8::Value>& args);
7879

7980
static v8::MaybeLocal<v8::Module> ResolveCallback(
8081
v8::Local<v8::Context> context,
Collapse file

‎src/node_errors.h‎

Copy file name to clipboardExpand all lines: src/node_errors.h
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ void PrintErrorString(const char* format, ...);
5959
V(ERR_TLS_INVALID_PROTOCOL_METHOD, TypeError) \
6060
V(ERR_TRANSFERRING_EXTERNALIZED_SHAREDARRAYBUFFER, TypeError) \
6161
V(ERR_TLS_PSK_SET_IDENTIY_HINT_FAILED, Error) \
62+
V(ERR_VM_MODULE_CACHED_DATA_REJECTED, Error) \
6263

6364
#define V(code, type) \
6465
inline v8::Local<v8::Value> code(v8::Isolate* isolate, \
Collapse file
+30Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
'use strict';
2+
3+
// Flags: --experimental-vm-modules
4+
5+
require('../common');
6+
7+
const assert = require('assert');
8+
const { SourceTextModule } = require('vm');
9+
10+
{
11+
const m = new SourceTextModule('const a = 1');
12+
const cachedData = m.createCachedData();
13+
14+
new SourceTextModule('const a = 1', { cachedData });
15+
16+
assert.throws(() => {
17+
new SourceTextModule('differentSource', { cachedData });
18+
}, {
19+
code: 'ERR_VM_MODULE_CACHED_DATA_REJECTED',
20+
});
21+
}
22+
23+
assert.rejects(async () => {
24+
const m = new SourceTextModule('const a = 1');
25+
await m.link(() => {});
26+
m.evaluate();
27+
m.createCachedData();
28+
}, {
29+
code: 'ERR_VM_MODULE_CANNOT_CREATE_CACHED_DATA',
30+
});

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.