vm: add Script.createCodeCache()

PR-URL: https://github.com/nodejs/node/pull/20300
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: John-David Dalton <john.david.dalton@gmail.com>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
Reviewed-By: Yang Guo <yangguo@chromium.org>
This commit is contained in:
Gus Caplan 2018-04-25 12:22:20 -05:00
parent a9d9d7689d
commit 4f67c6f667
No known key found for this signature in database
GPG Key ID: F00BD11880E82F0E
4 changed files with 87 additions and 2 deletions

View File

@ -1005,6 +1005,14 @@ accepted by the legacy `url.parse()` API. The mentioned APIs now use the WHATWG
URL parser that requires strictly valid URLs. Passing an invalid URL is URL parser that requires strictly valid URLs. Passing an invalid URL is
deprecated and support will be removed in the future. deprecated and support will be removed in the future.
<a id="DEP00XX"></a>
### DEP00XX: vm.Script cached data
Type: Documentation-only
The option `produceCachedData` has been deprecated. Use
[`script.createCachedData()`][] instead.
[`--pending-deprecation`]: cli.html#cli_pending_deprecation [`--pending-deprecation`]: cli.html#cli_pending_deprecation
[`Buffer.allocUnsafeSlow(size)`]: buffer.html#buffer_class_method_buffer_allocunsafeslow_size [`Buffer.allocUnsafeSlow(size)`]: buffer.html#buffer_class_method_buffer_allocunsafeslow_size
[`Buffer.from(array)`]: buffer.html#buffer_class_method_buffer_from_array [`Buffer.from(array)`]: buffer.html#buffer_class_method_buffer_from_array
@ -1055,6 +1063,7 @@ deprecated and support will be removed in the future.
[`process.env`]: process.html#process_process_env [`process.env`]: process.html#process_process_env
[`punycode`]: punycode.html [`punycode`]: punycode.html
[`require.extensions`]: modules.html#modules_require_extensions [`require.extensions`]: modules.html#modules_require_extensions
[`script.createCachedData()`]: vm.html#vm_script_create_cached_data
[`setInterval()`]: timers.html#timers_setinterval_callback_delay_args [`setInterval()`]: timers.html#timers_setinterval_callback_delay_args
[`setTimeout()`]: timers.html#timers_settimeout_callback_delay_args [`setTimeout()`]: timers.html#timers_settimeout_callback_delay_args
[`tls.CryptoStream`]: tls.html#tls_class_cryptostream [`tls.CryptoStream`]: tls.html#tls_class_cryptostream

View File

@ -411,6 +411,10 @@ changes:
pr-url: https://github.com/nodejs/node/pull/4777 pr-url: https://github.com/nodejs/node/pull/4777
description: The `cachedData` and `produceCachedData` options are description: The `cachedData` and `produceCachedData` options are
supported now. supported now.
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/20300
description: The `produceCachedData` is deprecated in favour of
`script.createCachedData()`
--> -->
* `code` {string} The JavaScript code to compile. * `code` {string} The JavaScript code to compile.
@ -431,11 +435,39 @@ changes:
`cachedData` property of the returned `vm.Script` instance. `cachedData` property of the returned `vm.Script` instance.
The `cachedDataProduced` value will be set to either `true` or `false` The `cachedDataProduced` value will be set to either `true` or `false`
depending on whether code cache data is produced successfully. depending on whether code cache data is produced successfully.
This option is deprecated in favor of `script.createCachedData`.
Creating a new `vm.Script` object compiles `code` but does not run it. The Creating a new `vm.Script` object compiles `code` but does not run it. The
compiled `vm.Script` can be run later multiple times. The `code` is not bound to compiled `vm.Script` can be run later multiple times. The `code` is not bound to
any global object; rather, it is bound before each run, just for that run. any global object; rather, it is bound before each run, just for that run.
### script.createCachedData()
<!-- YAML
added: REPLACEME
-->
* Returns: {Buffer}
Creates a code cache that can be used with the Script constructor's
`cachedData` option. Returns a Buffer. This method may be called at any
time and any number of times.
```js
const script = new vm.Script(`
function add(a, b) {
return a + b;
}
const x = add(1, 2);
`);
const cacheWithoutX = script.createCachedData();
script.runInThisContext();
const cacheWithX = script.createCachedData();
```
### script.runInContext(contextifiedSandbox[, options]) ### script.runInContext(contextifiedSandbox[, options])
<!-- YAML <!-- YAML
added: v0.3.1 added: v0.3.1

View File

@ -25,6 +25,7 @@
#include "base_object-inl.h" #include "base_object-inl.h"
#include "node_contextify.h" #include "node_contextify.h"
#include "node_context_data.h" #include "node_context_data.h"
#include "node_errors.h"
namespace node { namespace node {
namespace contextify { namespace contextify {
@ -596,6 +597,7 @@ class ContextifyScript : public BaseObject {
Local<FunctionTemplate> script_tmpl = env->NewFunctionTemplate(New); Local<FunctionTemplate> script_tmpl = env->NewFunctionTemplate(New);
script_tmpl->InstanceTemplate()->SetInternalFieldCount(1); script_tmpl->InstanceTemplate()->SetInternalFieldCount(1);
script_tmpl->SetClassName(class_name); script_tmpl->SetClassName(class_name);
env->SetProtoMethod(script_tmpl, "createCachedData", CreateCachedData);
env->SetProtoMethod(script_tmpl, "runInContext", RunInContext); env->SetProtoMethod(script_tmpl, "runInContext", RunInContext);
env->SetProtoMethod(script_tmpl, "runInThisContext", RunInThisContext); env->SetProtoMethod(script_tmpl, "runInThisContext", RunInThisContext);
@ -637,7 +639,7 @@ class ContextifyScript : public BaseObject {
Local<Context> parsing_context = context; Local<Context> parsing_context = context;
if (argc > 2) { if (argc > 2) {
// new ContextifyScript(code, filename, lineOffset, columnOffset // new ContextifyScript(code, filename, lineOffset, columnOffset,
// cachedData, produceCachedData, parsingContext) // cachedData, produceCachedData, parsingContext)
CHECK_EQ(argc, 7); CHECK_EQ(argc, 7);
CHECK(args[2]->IsNumber()); CHECK(args[2]->IsNumber());
@ -719,7 +721,7 @@ class ContextifyScript : public BaseObject {
Boolean::New(isolate, source.GetCachedData()->rejected)); Boolean::New(isolate, source.GetCachedData()->rejected));
} else if (produce_cached_data) { } else if (produce_cached_data) {
const ScriptCompiler::CachedData* cached_data = const ScriptCompiler::CachedData* cached_data =
ScriptCompiler::CreateCodeCache(v8_script.ToLocalChecked(), code); ScriptCompiler::CreateCodeCache(v8_script.ToLocalChecked());
bool cached_data_produced = cached_data != nullptr; bool cached_data_produced = cached_data != nullptr;
if (cached_data_produced) { if (cached_data_produced) {
MaybeLocal<Object> buf = Buffer::Copy( MaybeLocal<Object> buf = Buffer::Copy(
@ -745,6 +747,26 @@ class ContextifyScript : public BaseObject {
} }
static void CreateCachedData(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
ContextifyScript* wrapped_script;
ASSIGN_OR_RETURN_UNWRAP(&wrapped_script, args.Holder());
Local<UnboundScript> unbound_script =
PersistentToLocal(env->isolate(), wrapped_script->script_);
std::unique_ptr<ScriptCompiler::CachedData> cached_data(
ScriptCompiler::CreateCodeCache(unbound_script));
if (!cached_data) {
args.GetReturnValue().Set(Buffer::New(env, 0).ToLocalChecked());
} else {
MaybeLocal<Object> buf = Buffer::Copy(
env,
reinterpret_cast<const char*>(cached_data->data),
cached_data->length);
args.GetReturnValue().Set(buf.ToLocalChecked());
}
}
static void RunInThisContext(const FunctionCallbackInfo<Value>& args) { static void RunInThisContext(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args); Environment* env = Environment::GetCurrent(args);

View File

@ -0,0 +1,22 @@
'use strict';
require('../common');
const { Script } = require('vm');
const assert = require('assert');
const source = 'function x() {} const y = x();';
const script = new Script(source);
let cachedData = script.createCachedData();
assert(cachedData instanceof Buffer);
assert(!new Script(source, { cachedData }).cachedDataRejected);
script.runInNewContext();
for (let i = 0; i < 10; i += 1) {
cachedData = script.createCachedData();
assert(!new Script(source, { cachedData }).cachedDataRejected);
}