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/env.h b/src/env.h
index 7b6ffc8..5c99f80 100644
--- a/src/env.h
+++ b/src/env.h
@@ -53,10 +53,12 @@ namespace node {
   V(blocks_string, "blocks")                                                  \
   V(buffer_string, "buffer")                                                  \
   V(bytes_string, "bytes")                                                    \
   V(bytes_parsed_string, "bytesParsed")                                       \
   V(bytes_read_string, "bytesRead")                                           \
+  V(cached_data_string, "cachedData")                                         \
+  V(cached_data_rejected_string, "cachedDataRejected")                        \
   V(callback_string, "callback")                                              \
   V(change_string, "change")                                                  \
   V(oncertcb_string, "oncertcb")                                              \
   V(onclose_string, "_onclose")                                               \
   V(code_string, "code")                                                      \
@@ -165,10 +167,11 @@ namespace node {
   V(pipe_string, "pipe")                                                      \
   V(port_string, "port")                                                      \
   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")                                                      \
   V(readable_string, "readable")                                              \
   V(received_shutdown_string, "receivedShutdown")                             \
diff --git a/src/node_contextify.cc b/src/node_contextify.cc
index 7404bbb..ecf9444 100644
--- a/src/node_contextify.cc
+++ b/src/node_contextify.cc
@@ -1,6 +1,7 @@
 #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"
@@ -11,10 +12,11 @@
 
 namespace node {
 
 using v8::AccessType;
 using v8::Array;
+using v8::ArrayBuffer;
 using v8::Boolean;
 using v8::Context;
 using v8::Debug;
 using v8::EscapableHandleScope;
 using v8::External;
@@ -474,28 +476,61 @@ class ContextifyScript : public BaseObject {
     Local<String> code = args[0]->ToString(env->isolate());
     Local<String> filename = GetFilenameArg(args, 1);
     Local<Integer> lineOffset = GetLineOffsetArg(args, 1);
     Local<Integer> columnOffset = GetColumnOffsetArg(args, 1);
     bool display_errors = GetDisplayErrorsArg(args, 1);
+    MaybeLocal<Value> 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()) {
+      auto cached_data_local = cached_data_buf.ToLocalChecked();
+      cached_data = new ScriptCompiler::CachedData(
+          reinterpret_cast<uint8_t*>(Buffer::Data(cached_data_local)),
+          Buffer::Length(cached_data_local));
+    }
+
     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) {
         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) {
+      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());
+    }
   }
 
 
   static bool InstanceOf(Environment* env, const Local<Value>& value) {
     return !value.IsEmpty() &&
@@ -644,10 +679,47 @@ class ContextifyScript : public BaseObject {
       return defaultFilename;
     return value->ToString(args.GetIsolate());
   }
 
 
+  static MaybeLocal<Value> GetCachedData(
+      Environment* env,
+      const FunctionCallbackInfo<Value>& args,
+      const int i) {
+    if (!args[i]->IsObject()) {
+      return MaybeLocal<Value>();
+    }
+    Local<Value> value = args[i].As<Object>()->Get(env->cached_data_string());
+    if (value->IsUndefined()) {
+      return MaybeLocal<Value>();
+    }
+
+    if (!Buffer::HasInstance(value)) {
+      Environment::ThrowTypeError(
+          args.GetIsolate(),
+          "options.cachedData must be a Buffer instance");
+      return MaybeLocal<Value>();
+    }
+
+    return value;
+  }
+
+
+  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) {
     Local<Integer> defaultLineOffset = Integer::New(args.GetIsolate(), 0);
 
