From d1cacb814f6d42395184beaaba906ba930e711eb Mon Sep 17 00:00:00 2001
From: Fedor Indutny <fedor@indutny.com>
Date: Wed, 20 Jan 2016 19:34:19 -0500
Subject: 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>

diff --git a/src/node_contextify.cc b/src/node_contextify.cc
index 2e8fd2c..1b3d618 100644
--- a/src/node_contextify.cc
+++ b/src/node_contextify.cc
@@ -18,10 +18,11 @@
 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 // USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 #include "node.h"
+#include "node_buffer.h"
 #include "node_internals.h"
 #include "node_watchdog.h"
 #include "base-object.h"
 #include "base-object-inl.h"
 #include "env.h"
@@ -484,28 +485,60 @@ class ContextifyScript : public BaseObject {
 
     TryCatch try_catch;
     Local<String> code = args[0]->ToString();
     Local<String> filename = GetFilenameArg(args, 1);
     bool display_errors = GetDisplayErrorsArg(args, 1);
+    Local<Value> cached_data_buf = GetCachedData(args, 1);
+    bool produce_cached_data = GetProduceCachedData(args, 1);
     if (try_catch.HasCaught()) {
       try_catch.ReThrow();
       return;
     }
 
+    ScriptCompiler::CachedData* cached_data = NULL;
+    if (!cached_data_buf.IsEmpty()) {
+      cached_data = new ScriptCompiler::CachedData(
+          reinterpret_cast<uint8_t*>(Buffer::Data(cached_data_buf)),
+          Buffer::Length(cached_data_buf));
+    }
+
     ScriptOrigin origin(filename);
-    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() != NULL)
+      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) {
         AppendExceptionLine(env, try_catch.Exception(), try_catch.Message());
       }
       try_catch.ReThrow();
       return;
     }
     contextify_script->script_.Reset(env->isolate(), v8_script);
+
+    if (compile_options == ScriptCompiler::kConsumeCodeCache) {
+      // no 'rejected' field in cachedData
+    } else if (compile_options == ScriptCompiler::kProduceCodeCache) {
+      const ScriptCompiler::CachedData* cached_data = source.GetCachedData();
+      Local<Object> buf = Buffer::New(
+          env,
+          reinterpret_cast<const char*>(cached_data->data),
+          cached_data->length);
+      Local<String> cached_data_string = FIXED_ONE_BYTE_STRING(
+          args.GetIsolate(), "cachedData");
+      args.This()->Set(cached_data_string, buf);
+    }
   }
 
 
   static bool InstanceOf(Environment* env, const Local<Value>& value) {
     return !value.IsEmpty() &&
@@ -656,10 +689,46 @@ class ContextifyScript : public BaseObject {
 
     return value->IsUndefined() ? defaultFilename : value->ToString();
   }
 
 
+  static Local<Value> GetCachedData(
+      const FunctionCallbackInfo<Value>& args,
+      const int i) {
+    if (!args[i]->IsObject()) {
+      return Local<Value>();
+    }
+    Local<String> key = FIXED_ONE_BYTE_STRING(args.GetIsolate(), "cachedData");
+    Local<Value> value = args[i].As<Object>()->Get(key);
+    if (value->IsUndefined()) {
+      return Local<Value>();
+    }
+
+    if (!Buffer::HasInstance(value)) {
+      Environment::ThrowTypeError(
+          args.GetIsolate(),
+          "options.cachedData must be a Buffer instance");
+      return Local<Value>();
+    }
+
+    return value;
+  }
+
+
+  static bool GetProduceCachedData(
+      const FunctionCallbackInfo<Value>& args,
+      const int i) {
+    if (!args[i]->IsObject()) {
+      return false;
+    }
+    Local<String> key = FIXED_ONE_BYTE_STRING(args.GetIsolate(), "produceCachedData");
+    Local<Value> value = args[i].As<Object>()->Get(key);
+
+    return value->IsTrue();
+  }
+
+
   static bool EvalMachine(Environment* env,
                           const int64_t timeout,
                           const bool display_errors,
                           const FunctionCallbackInfo<Value>& args,
                           TryCatch& try_catch) {
