Skip to content

Commit d1cacb8

Browse files
indutnyrvagg
authored andcommittedFeb 9, 2016
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: #4777 Reviewed-By: Ben Noordhuis <[email protected]>
1 parent 60d2048 commit d1cacb8

File tree

4 files changed

+121
-3
lines changed

4 files changed

+121
-3
lines changed
 

‎doc/api/vm.markdown

+6
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ The options when creating a script are:
3838
code are controlled by the options to the script's methods.
3939
- `timeout`: a number of milliseconds to execute `code` before terminating
4040
execution. If execution is terminated, an [`Error`][] will be thrown.
41+
- `cachedData`: an optional `Buffer` with V8's code cache data for the supplied
42+
source. When supplied `cachedDataRejected` value will be set to either
43+
`true` or `false` depending on acceptance of the data by V8.
44+
- `produceCachedData`: if `true` and no `cachedData` is present - a `Buffer`
45+
with V8's code cache data will be produced and stored in `cachedData` property
46+
of the returned `vm.Script` instance.
4147

4248
### script.runInContext(contextifiedSandbox[, options])
4349

‎src/env.h

+3
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ namespace node {
6161
V(buffer_string, "buffer") \
6262
V(bytes_string, "bytes") \
6363
V(bytes_parsed_string, "bytesParsed") \
64+
V(cached_data_string, "cachedData") \
65+
V(cached_data_rejected_string, "cachedDataRejected") \
6466
V(callback_string, "callback") \
6567
V(change_string, "change") \
6668
V(oncertcb_string, "oncertcb") \
@@ -174,6 +176,7 @@ namespace node {
174176
V(preference_string, "preference") \
175177
V(priority_string, "priority") \
176178
V(processed_string, "processed") \
179+
V(produce_cached_data_string, "produceCachedData") \
177180
V(prototype_string, "prototype") \
178181
V(raw_string, "raw") \
179182
V(rdev_string, "rdev") \

‎src/node_contextify.cc

+75-3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ namespace node {
1313

1414
using v8::AccessType;
1515
using v8::Array;
16+
using v8::ArrayBuffer;
1617
using v8::Boolean;
1718
using v8::Context;
1819
using v8::Debug;
@@ -40,6 +41,7 @@ using v8::ScriptCompiler;
4041
using v8::ScriptOrigin;
4142
using v8::String;
4243
using v8::TryCatch;
44+
using v8::Uint8Array;
4345
using v8::UnboundScript;
4446
using v8::V8;
4547
using v8::Value;
@@ -496,15 +498,35 @@ class ContextifyScript : public BaseObject {
496498
Local<Integer> lineOffset = GetLineOffsetArg(args, 1);
497499
Local<Integer> columnOffset = GetColumnOffsetArg(args, 1);
498500
bool display_errors = GetDisplayErrorsArg(args, 1);
501+
MaybeLocal<Uint8Array> cached_data_buf = GetCachedData(env, args, 1);
502+
bool produce_cached_data = GetProduceCachedData(env, args, 1);
499503
if (try_catch.HasCaught()) {
500504
try_catch.ReThrow();
501505
return;
502506
}
503507

508+
ScriptCompiler::CachedData* cached_data = nullptr;
509+
if (!cached_data_buf.IsEmpty()) {
510+
ArrayBuffer::Contents contents =
511+
cached_data_buf.ToLocalChecked()->Buffer()->GetContents();
512+
cached_data = new ScriptCompiler::CachedData(
513+
static_cast<uint8_t*>(contents.Data()), contents.ByteLength());
514+
}
515+
504516
ScriptOrigin origin(filename, lineOffset, columnOffset);
505-
ScriptCompiler::Source source(code, origin);
506-
Local<UnboundScript> v8_script =
507-
ScriptCompiler::CompileUnbound(env->isolate(), &source);
517+
ScriptCompiler::Source source(code, origin, cached_data);
518+
ScriptCompiler::CompileOptions compile_options =
519+
ScriptCompiler::kNoCompileOptions;
520+
521+
if (source.GetCachedData() != nullptr)
522+
compile_options = ScriptCompiler::kConsumeCodeCache;
523+
else if (produce_cached_data)
524+
compile_options = ScriptCompiler::kProduceCodeCache;
525+
526+
Local<UnboundScript> v8_script = ScriptCompiler::CompileUnbound(
527+
env->isolate(),
528+
&source,
529+
compile_options);
508530

509531
if (v8_script.IsEmpty()) {
510532
if (display_errors) {
@@ -514,6 +536,19 @@ class ContextifyScript : public BaseObject {
514536
return;
515537
}
516538
contextify_script->script_.Reset(env->isolate(), v8_script);
539+
540+
if (compile_options == ScriptCompiler::kConsumeCodeCache) {
541+
args.This()->Set(
542+
env->cached_data_rejected_string(),
543+
Boolean::New(env->isolate(), source.GetCachedData()->rejected));
544+
} else if (compile_options == ScriptCompiler::kProduceCodeCache) {
545+
const ScriptCompiler::CachedData* cached_data = source.GetCachedData();
546+
MaybeLocal<Object> buf = Buffer::Copy(
547+
env,
548+
reinterpret_cast<const char*>(cached_data->data),
549+
cached_data->length);
550+
args.This()->Set(env->cached_data_string(), buf.ToLocalChecked());
551+
}
517552
}
518553

519554

@@ -666,6 +701,43 @@ class ContextifyScript : public BaseObject {
666701
}
667702

668703

704+
static MaybeLocal<Uint8Array> GetCachedData(
705+
Environment* env,
706+
const FunctionCallbackInfo<Value>& args,
707+
const int i) {
708+
if (!args[i]->IsObject()) {
709+
return MaybeLocal<Uint8Array>();
710+
}
711+
Local<Value> value = args[i].As<Object>()->Get(env->cached_data_string());
712+
if (value->IsUndefined()) {
713+
return MaybeLocal<Uint8Array>();
714+
}
715+
716+
if (!value->IsUint8Array()) {
717+
Environment::ThrowTypeError(
718+
args.GetIsolate(),
719+
"options.cachedData must be a Buffer instance");
720+
return MaybeLocal<Uint8Array>();
721+
}
722+
723+
return value.As<Uint8Array>();
724+
}
725+
726+
727+
static bool GetProduceCachedData(
728+
Environment* env,
729+
const FunctionCallbackInfo<Value>& args,
730+
const int i) {
731+
if (!args[i]->IsObject()) {
732+
return false;
733+
}
734+
Local<Value> value =
735+
args[i].As<Object>()->Get(env->produce_cached_data_string());
736+
737+
return value->IsTrue();
738+
}
739+
740+
669741
static Local<Integer> GetLineOffsetArg(
670742
const FunctionCallbackInfo<Value>& args,
671743
const int i) {

‎test/parallel/test-vm-cached-data.js

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
'use strict';
2+
require('../common');
3+
const assert = require('assert');
4+
const vm = require('vm');
5+
const Buffer = require('buffer').Buffer;
6+
7+
const originalSource = '(function bcd() { return \'original\'; })';
8+
9+
// It should produce code cache
10+
const original = new vm.Script(originalSource, {
11+
produceCachedData: true
12+
});
13+
assert(original.cachedData instanceof Buffer);
14+
15+
assert.equal(original.runInThisContext()(), 'original');
16+
17+
// It should consume code cache
18+
const success = new vm.Script(originalSource, {
19+
cachedData: original.cachedData
20+
});
21+
assert(!success.cachedDataRejected);
22+
23+
assert.equal(success.runInThisContext()(), 'original');
24+
25+
// It should reject invalid code cache
26+
const reject = new vm.Script('(function abc() { return \'invalid\'; })', {
27+
cachedData: original.cachedData
28+
});
29+
assert(reject.cachedDataRejected);
30+
assert.equal(reject.runInThisContext()(), 'invalid');
31+
32+
// It should throw on non-Buffer cachedData
33+
assert.throws(() => {
34+
new vm.Script('function abc() {}', {
35+
cachedData: 'ohai'
36+
});
37+
});

0 commit comments

Comments
 (0)
Please sign in to comment.