mirror of https://github.com/nodejs/node.git
vm: introduce `cachedData`/`produceCachedData`
Introduce `cachedData`/`produceCachedData` options for `v8.Script`. Could be used to consume/produce V8's code cache for speeding up compilation of known code. PR-URL: https://github.com/nodejs/node/pull/4777 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>pull/4845/head
parent
83e43fbb04
commit
96934cbb30
|
@ -38,6 +38,12 @@ The options when creating a script are:
|
|||
code are controlled by the options to the script's methods.
|
||||
- `timeout`: a number of milliseconds to execute `code` before terminating
|
||||
execution. If execution is terminated, an [`Error`][] will be thrown.
|
||||
- `cachedData`: an optional `Buffer` with V8's code cache data for the supplied
|
||||
source. When supplied `cachedDataRejected` value will be set to either
|
||||
`true` or `false` depending on acceptance of the data by V8.
|
||||
- `produceCachedData`: if `true` and no `cachedData` is present - a `Buffer`
|
||||
with V8's code cache data will be produced and stored in `cachedData` property
|
||||
of the returned `vm.Script` instance.
|
||||
|
||||
### script.runInContext(contextifiedSandbox[, options])
|
||||
|
||||
|
|
|
@ -61,6 +61,8 @@ namespace node {
|
|||
V(buffer_string, "buffer") \
|
||||
V(bytes_string, "bytes") \
|
||||
V(bytes_parsed_string, "bytesParsed") \
|
||||
V(cached_data_string, "cachedData") \
|
||||
V(cached_data_rejected_string, "cachedDataRejected") \
|
||||
V(callback_string, "callback") \
|
||||
V(change_string, "change") \
|
||||
V(oncertcb_string, "oncertcb") \
|
||||
|
@ -175,6 +177,7 @@ namespace node {
|
|||
V(preference_string, "preference") \
|
||||
V(priority_string, "priority") \
|
||||
V(processed_string, "processed") \
|
||||
V(produce_cached_data_string, "produceCachedData") \
|
||||
V(prototype_string, "prototype") \
|
||||
V(raw_string, "raw") \
|
||||
V(rdev_string, "rdev") \
|
||||
|
|
|
@ -13,6 +13,7 @@ namespace node {
|
|||
|
||||
using v8::AccessType;
|
||||
using v8::Array;
|
||||
using v8::ArrayBuffer;
|
||||
using v8::Boolean;
|
||||
using v8::Context;
|
||||
using v8::Debug;
|
||||
|
@ -40,6 +41,7 @@ using v8::ScriptCompiler;
|
|||
using v8::ScriptOrigin;
|
||||
using v8::String;
|
||||
using v8::TryCatch;
|
||||
using v8::Uint8Array;
|
||||
using v8::UnboundScript;
|
||||
using v8::V8;
|
||||
using v8::Value;
|
||||
|
@ -507,15 +509,35 @@ class ContextifyScript : public BaseObject {
|
|||
Local<Integer> lineOffset = GetLineOffsetArg(args, 1);
|
||||
Local<Integer> columnOffset = GetColumnOffsetArg(args, 1);
|
||||
bool display_errors = GetDisplayErrorsArg(args, 1);
|
||||
MaybeLocal<Uint8Array> cached_data_buf = GetCachedData(env, args, 1);
|
||||
bool produce_cached_data = GetProduceCachedData(env, args, 1);
|
||||
if (try_catch.HasCaught()) {
|
||||
try_catch.ReThrow();
|
||||
return;
|
||||
}
|
||||
|
||||
ScriptCompiler::CachedData* cached_data = nullptr;
|
||||
if (!cached_data_buf.IsEmpty()) {
|
||||
ArrayBuffer::Contents contents =
|
||||
cached_data_buf.ToLocalChecked()->Buffer()->GetContents();
|
||||
cached_data = new ScriptCompiler::CachedData(
|
||||
static_cast<uint8_t*>(contents.Data()), contents.ByteLength());
|
||||
}
|
||||
|
||||
ScriptOrigin origin(filename, lineOffset, columnOffset);
|
||||
ScriptCompiler::Source source(code, origin);
|
||||
Local<UnboundScript> v8_script =
|
||||
ScriptCompiler::CompileUnbound(env->isolate(), &source);
|
||||
ScriptCompiler::Source source(code, origin, cached_data);
|
||||
ScriptCompiler::CompileOptions compile_options =
|
||||
ScriptCompiler::kNoCompileOptions;
|
||||
|
||||
if (source.GetCachedData() != nullptr)
|
||||
compile_options = ScriptCompiler::kConsumeCodeCache;
|
||||
else if (produce_cached_data)
|
||||
compile_options = ScriptCompiler::kProduceCodeCache;
|
||||
|
||||
Local<UnboundScript> v8_script = ScriptCompiler::CompileUnbound(
|
||||
env->isolate(),
|
||||
&source,
|
||||
compile_options);
|
||||
|
||||
if (v8_script.IsEmpty()) {
|
||||
if (display_errors) {
|
||||
|
@ -525,6 +547,19 @@ class ContextifyScript : public BaseObject {
|
|||
return;
|
||||
}
|
||||
contextify_script->script_.Reset(env->isolate(), v8_script);
|
||||
|
||||
if (compile_options == ScriptCompiler::kConsumeCodeCache) {
|
||||
args.This()->Set(
|
||||
env->cached_data_rejected_string(),
|
||||
Boolean::New(env->isolate(), source.GetCachedData()->rejected));
|
||||
} else if (compile_options == ScriptCompiler::kProduceCodeCache) {
|
||||
const ScriptCompiler::CachedData* cached_data = source.GetCachedData();
|
||||
MaybeLocal<Object> buf = Buffer::Copy(
|
||||
env,
|
||||
reinterpret_cast<const char*>(cached_data->data),
|
||||
cached_data->length);
|
||||
args.This()->Set(env->cached_data_string(), buf.ToLocalChecked());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -677,6 +712,43 @@ class ContextifyScript : public BaseObject {
|
|||
}
|
||||
|
||||
|
||||
static MaybeLocal<Uint8Array> GetCachedData(
|
||||
Environment* env,
|
||||
const FunctionCallbackInfo<Value>& args,
|
||||
const int i) {
|
||||
if (!args[i]->IsObject()) {
|
||||
return MaybeLocal<Uint8Array>();
|
||||
}
|
||||
Local<Value> value = args[i].As<Object>()->Get(env->cached_data_string());
|
||||
if (value->IsUndefined()) {
|
||||
return MaybeLocal<Uint8Array>();
|
||||
}
|
||||
|
||||
if (!value->IsUint8Array()) {
|
||||
Environment::ThrowTypeError(
|
||||
args.GetIsolate(),
|
||||
"options.cachedData must be a Buffer instance");
|
||||
return MaybeLocal<Uint8Array>();
|
||||
}
|
||||
|
||||
return value.As<Uint8Array>();
|
||||
}
|
||||
|
||||
|
||||
static bool GetProduceCachedData(
|
||||
Environment* env,
|
||||
const FunctionCallbackInfo<Value>& args,
|
||||
const int i) {
|
||||
if (!args[i]->IsObject()) {
|
||||
return false;
|
||||
}
|
||||
Local<Value> value =
|
||||
args[i].As<Object>()->Get(env->produce_cached_data_string());
|
||||
|
||||
return value->IsTrue();
|
||||
}
|
||||
|
||||
|
||||
static Local<Integer> GetLineOffsetArg(
|
||||
const FunctionCallbackInfo<Value>& args,
|
||||
const int i) {
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
'use strict';
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
const vm = require('vm');
|
||||
const Buffer = require('buffer').Buffer;
|
||||
|
||||
const originalSource = '(function bcd() { return \'original\'; })';
|
||||
|
||||
// It should produce code cache
|
||||
const original = new vm.Script(originalSource, {
|
||||
produceCachedData: true
|
||||
});
|
||||
assert(original.cachedData instanceof Buffer);
|
||||
|
||||
assert.equal(original.runInThisContext()(), 'original');
|
||||
|
||||
// It should consume code cache
|
||||
const success = new vm.Script(originalSource, {
|
||||
cachedData: original.cachedData
|
||||
});
|
||||
assert(!success.cachedDataRejected);
|
||||
|
||||
assert.equal(success.runInThisContext()(), 'original');
|
||||
|
||||
// It should reject invalid code cache
|
||||
const reject = new vm.Script('(function abc() { return \'invalid\'; })', {
|
||||
cachedData: original.cachedData
|
||||
});
|
||||
assert(reject.cachedDataRejected);
|
||||
assert.equal(reject.runInThisContext()(), 'invalid');
|
||||
|
||||
// It should throw on non-Buffer cachedData
|
||||
assert.throws(() => {
|
||||
new vm.Script('function abc() {}', {
|
||||
cachedData: 'ohai'
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue