From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Samuel Attard <samuel.r.attard@gmail.com>
Date: Tue, 3 Nov 2020 16:17:38 -0800
Subject: src: allow embedders to provide a custom PageAllocator to
 NodePlatform

For certain embedder use cases there are more complex memory allocation requirements that the default V8 page allocator does not handle, for example using MAP_JIT when running under a hardened runtime environment on macOS.  This allows such embedders to provide their own allocator that does handle these cases.

diff --git a/src/api/environment.cc b/src/api/environment.cc
index c08fe4b32d4155badb572f15529f903c0ec63146..a8cf0d763f78c2752e3aa22479dadd9fa53c222f 100644
--- a/src/api/environment.cc
+++ b/src/api/environment.cc
@@ -488,8 +488,9 @@ MultiIsolatePlatform* CreatePlatform(
 
 MultiIsolatePlatform* CreatePlatform(
     int thread_pool_size,
-    v8::TracingController* tracing_controller) {
-  return MultiIsolatePlatform::Create(thread_pool_size, tracing_controller)
+    v8::TracingController* tracing_controller,
+    v8::PageAllocator* page_allocator) {
+  return MultiIsolatePlatform::Create(thread_pool_size, tracing_controller, page_allocator)
       .release();
 }
 
@@ -499,8 +500,9 @@ void FreePlatform(MultiIsolatePlatform* platform) {
 
 std::unique_ptr<MultiIsolatePlatform> MultiIsolatePlatform::Create(
     int thread_pool_size,
-    v8::TracingController* tracing_controller) {
-  return std::make_unique<NodePlatform>(thread_pool_size, tracing_controller);
+    v8::TracingController* tracing_controller,
+    v8::PageAllocator* page_allocator) {
+  return std::make_unique<NodePlatform>(thread_pool_size, tracing_controller, page_allocator);
 }
 
 MaybeLocal<Object> GetPerContextExports(Local<Context> context) {
diff --git a/src/node.h b/src/node.h
index b646fdda58ebcbf2dd92ee4fc9cb0d9c039174d1..14893ad605b9f8c64b0b8fc28625e235655dcd63 100644
--- a/src/node.h
+++ b/src/node.h
@@ -333,7 +333,8 @@ class NODE_EXTERN MultiIsolatePlatform : public v8::Platform {
 
   static std::unique_ptr<MultiIsolatePlatform> Create(
       int thread_pool_size,
-      v8::TracingController* tracing_controller = nullptr);
+      v8::TracingController* tracing_controller = nullptr,
+      v8::PageAllocator* page_allocator = nullptr);
 };
 
 enum IsolateSettingsFlags {
@@ -536,7 +537,8 @@ NODE_DEPRECATED("Use variant taking a v8::TracingController* pointer instead",
         node::tracing::TracingController* tracing_controller));
 NODE_EXTERN MultiIsolatePlatform* CreatePlatform(
     int thread_pool_size,
-    v8::TracingController* tracing_controller);
+    v8::TracingController* tracing_controller,
+    v8::PageAllocator* = nullptr);
 NODE_EXTERN void FreePlatform(MultiIsolatePlatform* platform);
 
 // Get/set the currently active tracing controller. Using CreatePlatform()
diff --git a/src/node_platform.cc b/src/node_platform.cc
index aac0682670fcffd235fcf450bc5e2b0d45985b47..96be2281b562c44b276483970c06862250ea8941 100644
--- a/src/node_platform.cc
+++ b/src/node_platform.cc
@@ -324,12 +324,16 @@ void PerIsolatePlatformData::DecreaseHandleCount() {
 }
 
 NodePlatform::NodePlatform(int thread_pool_size,
-                           v8::TracingController* tracing_controller) {
+                           v8::TracingController* tracing_controller,
+                           v8::PageAllocator* page_allocator) {
   if (tracing_controller != nullptr) {
     tracing_controller_ = tracing_controller;
   } else {
     tracing_controller_ = new v8::TracingController();
   }
+  // This being nullptr is acceptable as V8 will default to its built
+  // in allocator if none is provided
+  page_allocator_ = page_allocator;
   // TODO(addaleax): It's a bit icky that we use global state here, but we can't
   // really do anything about it unless V8 starts exposing a way to access the
   // current v8::Platform instance.
@@ -544,6 +548,10 @@ Platform::StackTracePrinter NodePlatform::GetStackTracePrinter() {
   };
 }
 
+v8::PageAllocator* NodePlatform::GetPageAllocator() {
+  return page_allocator_;
+}
+
 std::unique_ptr<v8::JobHandle> NodePlatform::PostJob(v8::TaskPriority priority, std::unique_ptr<v8::JobTask> job_task) {
   return v8::platform::NewDefaultJobHandle(this, priority, std::move(job_task), 1 /* num_worker_threads */);
 }
diff --git a/src/node_platform.h b/src/node_platform.h
index a274be6bbea19a4488bca393712a9ac8b50fe16a..314cf2d1056d30a77ead400d100a4d4c6f844be6 100644
--- a/src/node_platform.h
+++ b/src/node_platform.h
@@ -138,7 +138,8 @@ class WorkerThreadsTaskRunner {
 class NodePlatform : public MultiIsolatePlatform {
  public:
   NodePlatform(int thread_pool_size,
-               v8::TracingController* tracing_controller);
+               v8::TracingController* tracing_controller,
+               v8::PageAllocator* page_allocator = nullptr);
   ~NodePlatform() override;
 
   void DrainTasks(v8::Isolate* isolate) override;
@@ -168,6 +169,7 @@ class NodePlatform : public MultiIsolatePlatform {
       v8::Isolate* isolate) override;
 
   Platform::StackTracePrinter GetStackTracePrinter() override;
+  v8::PageAllocator* GetPageAllocator() override;
 
  private:
   IsolatePlatformDelegate* ForIsolate(v8::Isolate* isolate);
@@ -179,6 +181,7 @@ class NodePlatform : public MultiIsolatePlatform {
   std::unordered_map<v8::Isolate*, DelegatePair> per_isolate_;
 
   v8::TracingController* tracing_controller_;
+  v8::PageAllocator* page_allocator_;
   std::shared_ptr<WorkerThreadsTaskRunner> worker_thread_task_runner_;
   bool has_shut_down_ = false;
 };
