tools: implement mkcodecache as an executable
This patch implement a mkcodecache executable on top of the `NativeModuleLoader` singleton. This makes it possible to build a Node.js binary with embedded code cache without building itself using the code cache stub - the cache is now initialized by `NativeModuleEnv` instead which can be refactored out of the mkcodecache dependencies. PR-URL: https://github.com/nodejs/node/pull/27161 Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
This commit is contained in:
parent
1c26169714
commit
4fd7193579
4
Makefile
4
Makefile
@ -115,7 +115,7 @@ with-code-cache:
|
|||||||
$(PYTHON) ./configure $(CONFIG_FLAGS)
|
$(PYTHON) ./configure $(CONFIG_FLAGS)
|
||||||
$(MAKE)
|
$(MAKE)
|
||||||
mkdir -p $(CODE_CACHE_DIR)
|
mkdir -p $(CODE_CACHE_DIR)
|
||||||
out/$(BUILDTYPE)/$(NODE_EXE) --expose-internals tools/generate_code_cache.js $(CODE_CACHE_FILE)
|
out/$(BUILDTYPE)/mkcodecache $(CODE_CACHE_FILE)
|
||||||
$(PYTHON) ./configure --code-cache-path $(CODE_CACHE_FILE) $(CONFIG_FLAGS)
|
$(PYTHON) ./configure --code-cache-path $(CODE_CACHE_FILE) $(CONFIG_FLAGS)
|
||||||
$(MAKE)
|
$(MAKE)
|
||||||
|
|
||||||
@ -1232,6 +1232,8 @@ LINT_CPP_FILES = $(filter-out $(LINT_CPP_EXCLUDE), $(wildcard \
|
|||||||
test/node-api/*/*.h \
|
test/node-api/*/*.h \
|
||||||
tools/icu/*.cc \
|
tools/icu/*.cc \
|
||||||
tools/icu/*.h \
|
tools/icu/*.h \
|
||||||
|
tools/code_cache/*.cc \
|
||||||
|
tools/code_cache/*.h \
|
||||||
))
|
))
|
||||||
|
|
||||||
# Code blocks don't have newline at the end,
|
# Code blocks don't have newline at the end,
|
||||||
|
53
node.gyp
53
node.gyp
@ -1103,6 +1103,59 @@
|
|||||||
}],
|
}],
|
||||||
],
|
],
|
||||||
}, # cctest
|
}, # cctest
|
||||||
|
# TODO(joyeecheung): do not depend on node_lib,
|
||||||
|
# instead create a smaller static library node_lib_base that does
|
||||||
|
# just enough for node_native_module.cc and the cache builder to
|
||||||
|
# compile without compiling the generated code cache C++ file.
|
||||||
|
# So generate_code_cache -> mkcodecache -> node_lib_base,
|
||||||
|
# node_lib -> node_lib_base & generate_code_cache
|
||||||
|
{
|
||||||
|
'target_name': 'mkcodecache',
|
||||||
|
'type': 'executable',
|
||||||
|
|
||||||
|
'dependencies': [
|
||||||
|
'<(node_lib_target_name)',
|
||||||
|
'deps/histogram/histogram.gyp:histogram',
|
||||||
|
],
|
||||||
|
|
||||||
|
'includes': [
|
||||||
|
'node.gypi'
|
||||||
|
],
|
||||||
|
|
||||||
|
'include_dirs': [
|
||||||
|
'src',
|
||||||
|
'tools/msvs/genfiles',
|
||||||
|
'deps/v8/include',
|
||||||
|
'deps/cares/include',
|
||||||
|
'deps/uv/include',
|
||||||
|
],
|
||||||
|
|
||||||
|
'defines': [ 'NODE_WANT_INTERNALS=1' ],
|
||||||
|
|
||||||
|
'sources': [
|
||||||
|
'tools/code_cache/mkcodecache.cc',
|
||||||
|
'tools/code_cache/cache_builder.cc'
|
||||||
|
],
|
||||||
|
|
||||||
|
'conditions': [
|
||||||
|
[ 'node_report=="true"', {
|
||||||
|
'conditions': [
|
||||||
|
['OS=="win"', {
|
||||||
|
'libraries': [
|
||||||
|
'dbghelp.lib',
|
||||||
|
'PsApi.lib',
|
||||||
|
'Ws2_32.lib',
|
||||||
|
],
|
||||||
|
'dll_files': [
|
||||||
|
'dbghelp.dll',
|
||||||
|
'PsApi.dll',
|
||||||
|
'Ws2_32.dll',
|
||||||
|
],
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
}, # cache_builder
|
||||||
], # end targets
|
], # end targets
|
||||||
|
|
||||||
'conditions': [
|
'conditions': [
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
namespace node {
|
namespace node {
|
||||||
namespace native_module {
|
namespace native_module {
|
||||||
|
|
||||||
using v8::ArrayBuffer;
|
|
||||||
using v8::Context;
|
using v8::Context;
|
||||||
using v8::DEFAULT;
|
using v8::DEFAULT;
|
||||||
using v8::Function;
|
using v8::Function;
|
||||||
@ -18,11 +17,9 @@ using v8::Name;
|
|||||||
using v8::None;
|
using v8::None;
|
||||||
using v8::Object;
|
using v8::Object;
|
||||||
using v8::PropertyCallbackInfo;
|
using v8::PropertyCallbackInfo;
|
||||||
using v8::ScriptCompiler;
|
|
||||||
using v8::Set;
|
using v8::Set;
|
||||||
using v8::SideEffectType;
|
using v8::SideEffectType;
|
||||||
using v8::String;
|
using v8::String;
|
||||||
using v8::Uint8Array;
|
|
||||||
using v8::Value;
|
using v8::Value;
|
||||||
|
|
||||||
// TODO(joyeecheung): make these more general and put them into util.h
|
// TODO(joyeecheung): make these more general and put them into util.h
|
||||||
@ -154,26 +151,6 @@ MaybeLocal<Function> NativeModuleEnv::LookupAndCompile(
|
|||||||
return maybe;
|
return maybe;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is supposed to be run only by the main thread in
|
|
||||||
// tools/generate_code_cache.js
|
|
||||||
void NativeModuleEnv::GetCodeCache(const FunctionCallbackInfo<Value>& args) {
|
|
||||||
Environment* env = Environment::GetCurrent(args);
|
|
||||||
Isolate* isolate = env->isolate();
|
|
||||||
CHECK(env->is_main_thread());
|
|
||||||
|
|
||||||
CHECK(args[0]->IsString());
|
|
||||||
node::Utf8Value id_v(isolate, args[0].As<String>());
|
|
||||||
const char* id = *id_v;
|
|
||||||
|
|
||||||
ScriptCompiler::CachedData* cached_data =
|
|
||||||
NativeModuleLoader::GetInstance()->GetCodeCache(id);
|
|
||||||
if (cached_data != nullptr) {
|
|
||||||
Local<ArrayBuffer> buf = ArrayBuffer::New(isolate, cached_data->length);
|
|
||||||
memcpy(buf->GetContents().Data(), cached_data->data, cached_data->length);
|
|
||||||
args.GetReturnValue().Set(Uint8Array::New(buf, 0, cached_data->length));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(joyeecheung): It is somewhat confusing that Class::Initialize
|
// TODO(joyeecheung): It is somewhat confusing that Class::Initialize
|
||||||
// is used to initilaize to the binding, but it is the current convention.
|
// is used to initilaize to the binding, but it is the current convention.
|
||||||
// Rename this across the code base to something that makes more sense.
|
// Rename this across the code base to something that makes more sense.
|
||||||
@ -216,7 +193,6 @@ void NativeModuleEnv::Initialize(Local<Object> target,
|
|||||||
.Check();
|
.Check();
|
||||||
|
|
||||||
env->SetMethod(target, "getCacheUsage", NativeModuleEnv::GetCacheUsage);
|
env->SetMethod(target, "getCacheUsage", NativeModuleEnv::GetCacheUsage);
|
||||||
env->SetMethod(target, "getCodeCache", NativeModuleEnv::GetCodeCache);
|
|
||||||
env->SetMethod(target, "compileFunction", NativeModuleEnv::CompileFunction);
|
env->SetMethod(target, "compileFunction", NativeModuleEnv::CompileFunction);
|
||||||
// internalBinding('native_module') should be frozen
|
// internalBinding('native_module') should be frozen
|
||||||
target->SetIntegrityLevel(context, IntegrityLevel::kFrozen).FromJust();
|
target->SetIntegrityLevel(context, IntegrityLevel::kFrozen).FromJust();
|
||||||
|
@ -52,7 +52,6 @@ class NativeModuleEnv {
|
|||||||
const v8::PropertyCallbackInfo<v8::Value>& info);
|
const v8::PropertyCallbackInfo<v8::Value>& info);
|
||||||
// Compile a specific native module as a function
|
// Compile a specific native module as a function
|
||||||
static void CompileFunction(const v8::FunctionCallbackInfo<v8::Value>& args);
|
static void CompileFunction(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
static void GetCodeCache(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace native_module
|
} // namespace native_module
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// This test verifies that the binary is compiled with code cache and the
|
// This test verifies the code cache generator can generate a C++
|
||||||
// cache is used when built in modules are compiled.
|
// file that contains the code cache. This can be removed once we
|
||||||
|
// actually build that C++ file into our binary.
|
||||||
|
|
||||||
const common = require('../common');
|
const common = require('../common');
|
||||||
|
|
||||||
const tmpdir = require('../common/tmpdir');
|
const tmpdir = require('../common/tmpdir');
|
||||||
const { spawnSync } = require('child_process');
|
const { spawnSync } = require('child_process');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
@ -12,17 +12,29 @@ const path = require('path');
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const readline = require('readline');
|
const readline = require('readline');
|
||||||
|
|
||||||
const generator = path.join(
|
console.log('Looking for mkcodecache executable');
|
||||||
__dirname, '..', '..', 'tools', 'generate_code_cache.js'
|
let buildDir;
|
||||||
);
|
const stat = fs.statSync(process.execPath);
|
||||||
|
if (stat.isSymbolicLink()) {
|
||||||
|
console.log('Binary is a symbolic link');
|
||||||
|
buildDir = path.dirname(fs.readlinkSync(process.execPath));
|
||||||
|
} else {
|
||||||
|
buildDir = path.dirname(process.execPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ext = common.isWindows ? '.exe' : '';
|
||||||
|
const generator = path.join(buildDir, `mkcodecache${ext}`);
|
||||||
|
if (!fs.existsSync(generator)) {
|
||||||
|
common.skip('Could not find mkcodecache');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`mkcodecache is ${generator}`);
|
||||||
|
|
||||||
tmpdir.refresh();
|
tmpdir.refresh();
|
||||||
const dest = path.join(tmpdir.path, 'cache.cc');
|
const dest = path.join(tmpdir.path, 'cache.cc');
|
||||||
|
|
||||||
// Run tools/generate_code_cache.js
|
// Run mkcodecache
|
||||||
const child = spawnSync(
|
const child = spawnSync(generator, [dest]);
|
||||||
process.execPath,
|
|
||||||
['--expose-internals', generator, dest]
|
|
||||||
);
|
|
||||||
assert.ifError(child.error);
|
assert.ifError(child.error);
|
||||||
if (child.status !== 0) {
|
if (child.status !== 0) {
|
||||||
console.log(child.stderr.toString());
|
console.log(child.stderr.toString());
|
||||||
|
165
tools/code_cache/cache_builder.cc
Normal file
165
tools/code_cache/cache_builder.cc
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
#include "cache_builder.h"
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#include "node_native_module.h"
|
||||||
|
|
||||||
|
namespace node {
|
||||||
|
namespace native_module {
|
||||||
|
|
||||||
|
using v8::Context;
|
||||||
|
using v8::Function;
|
||||||
|
using v8::Isolate;
|
||||||
|
using v8::Local;
|
||||||
|
using v8::MaybeLocal;
|
||||||
|
using v8::ScriptCompiler;
|
||||||
|
|
||||||
|
static std::string GetDefName(const std::string& id) {
|
||||||
|
char buf[64] = {0};
|
||||||
|
size_t size = id.size();
|
||||||
|
CHECK_LT(size, sizeof(buf));
|
||||||
|
for (size_t i = 0; i < size; ++i) {
|
||||||
|
char ch = id[i];
|
||||||
|
buf[i] = (ch == '-' || ch == '/') ? '_' : ch;
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string FormatSize(size_t size) {
|
||||||
|
char buf[64] = {0};
|
||||||
|
if (size < 1024) {
|
||||||
|
snprintf(buf, sizeof(buf), "%.2fB", static_cast<double>(size));
|
||||||
|
} else if (size < 1024 * 1024) {
|
||||||
|
snprintf(buf, sizeof(buf), "%.2fKB", static_cast<double>(size / 1024));
|
||||||
|
} else {
|
||||||
|
snprintf(
|
||||||
|
buf, sizeof(buf), "%.2fMB", static_cast<double>(size / 1024 / 1024));
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string GetDefinition(const std::string& id,
|
||||||
|
size_t size,
|
||||||
|
const uint8_t* data) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "static const uint8_t " << GetDefName(id) << "[] = {\n";
|
||||||
|
for (size_t i = 0; i < size; ++i) {
|
||||||
|
uint8_t ch = data[i];
|
||||||
|
ss << std::to_string(ch) << (i == size - 1 ? '\n' : ',');
|
||||||
|
}
|
||||||
|
ss << "};";
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string GetInitializer(const std::string& id) {
|
||||||
|
std::string def_name = GetDefName(id);
|
||||||
|
char buf[256] = {0};
|
||||||
|
snprintf(buf,
|
||||||
|
sizeof(buf),
|
||||||
|
"code_cache->emplace(\n"
|
||||||
|
" \"%s\",\n"
|
||||||
|
" std::make_unique<v8::ScriptCompiler::CachedData>"
|
||||||
|
"(%s, static_cast<int>(arraysize(%s)), policy)\n"
|
||||||
|
");",
|
||||||
|
id.c_str(),
|
||||||
|
def_name.c_str(),
|
||||||
|
def_name.c_str());
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string GenerateCodeCache(
|
||||||
|
std::map<std::string, ScriptCompiler::CachedData*> data,
|
||||||
|
std::vector<std::string> ids,
|
||||||
|
bool log_progress) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << R"(#include <cinttypes>
|
||||||
|
#include "node_native_module_env.h"
|
||||||
|
|
||||||
|
// This file is generated by tools/mkcodecache
|
||||||
|
// and is used when configure is run with \`--code-cache-path\`
|
||||||
|
|
||||||
|
namespace node {
|
||||||
|
namespace native_module {
|
||||||
|
)";
|
||||||
|
|
||||||
|
size_t total = 0;
|
||||||
|
for (const auto& x : data) {
|
||||||
|
const std::string& id = x.first;
|
||||||
|
ScriptCompiler::CachedData* cached_data = x.second;
|
||||||
|
total += cached_data->length;
|
||||||
|
std::string def = GetDefinition(id, cached_data->length, cached_data->data);
|
||||||
|
ss << def << "\n\n";
|
||||||
|
if (log_progress) {
|
||||||
|
std::cout << "Generated cache for " << id
|
||||||
|
<< ", size = " << FormatSize(cached_data->length)
|
||||||
|
<< ", total = " << FormatSize(total) << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ss << R"(void NativeModuleEnv::InitializeCodeCache() {
|
||||||
|
NativeModuleCacheMap* code_cache =
|
||||||
|
NativeModuleLoader::GetInstance()->code_cache();
|
||||||
|
if (!code_cache->empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto policy = v8::ScriptCompiler::CachedData::BufferPolicy::BufferNotOwned;
|
||||||
|
)";
|
||||||
|
|
||||||
|
for (const auto& x : data) {
|
||||||
|
const std::string& id = x.first;
|
||||||
|
ss << GetInitializer(id) << "\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
ss << R"(}
|
||||||
|
|
||||||
|
} // namespace native_module
|
||||||
|
} // namespace node
|
||||||
|
)";
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CodeCacheBuilder::Generate(Local<Context> context) {
|
||||||
|
NativeModuleLoader* loader = NativeModuleLoader::GetInstance();
|
||||||
|
std::vector<std::string> ids = loader->GetModuleIds();
|
||||||
|
|
||||||
|
std::vector<std::string> modules;
|
||||||
|
modules.reserve(ids.size());
|
||||||
|
|
||||||
|
std::map<std::string, ScriptCompiler::CachedData*> data;
|
||||||
|
|
||||||
|
NativeModuleLoader::Result result;
|
||||||
|
for (const auto& id : ids) {
|
||||||
|
// TODO(joyeecheung): we can only compile the modules that can be
|
||||||
|
// required here because the parameters for other types of builtins
|
||||||
|
// are still very flexible. We should look into auto-generating
|
||||||
|
// the paramters from the source somehow.
|
||||||
|
if (loader->CanBeRequired(id.c_str())) {
|
||||||
|
modules.push_back(id);
|
||||||
|
USE(loader->CompileAsModule(context, id.c_str(), &result));
|
||||||
|
ScriptCompiler::CachedData* cached_data =
|
||||||
|
loader->GetCodeCache(id.c_str());
|
||||||
|
if (cached_data == nullptr) {
|
||||||
|
// TODO(joyeecheung): display syntax errors
|
||||||
|
std::cerr << "Failed to complile " << id << "\n";
|
||||||
|
} else {
|
||||||
|
data.emplace(id, cached_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char env_buf[32];
|
||||||
|
size_t env_size = sizeof(env_buf);
|
||||||
|
int ret = uv_os_getenv("NODE_DEBUG", env_buf, &env_size);
|
||||||
|
bool log_progress = false;
|
||||||
|
if (ret == 0 && strcmp(env_buf, "mkcodecache") == 0) {
|
||||||
|
log_progress = true;
|
||||||
|
}
|
||||||
|
return GenerateCodeCache(data, modules, log_progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace native_module
|
||||||
|
} // namespace node
|
16
tools/code_cache/cache_builder.h
Normal file
16
tools/code_cache/cache_builder.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#ifndef TOOLS_CODE_CACHE_CACHE_BUILDER_H_
|
||||||
|
#define TOOLS_CODE_CACHE_CACHE_BUILDER_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "v8.h"
|
||||||
|
|
||||||
|
namespace node {
|
||||||
|
namespace native_module {
|
||||||
|
class CodeCacheBuilder {
|
||||||
|
public:
|
||||||
|
static std::string Generate(v8::Local<v8::Context> context);
|
||||||
|
};
|
||||||
|
} // namespace native_module
|
||||||
|
} // namespace node
|
||||||
|
|
||||||
|
#endif // TOOLS_CODE_CACHE_CACHE_BUILDER_H_
|
62
tools/code_cache/mkcodecache.cc
Normal file
62
tools/code_cache/mkcodecache.cc
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
#include <cstdio>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "cache_builder.h"
|
||||||
|
#include "libplatform/libplatform.h"
|
||||||
|
#include "v8.h"
|
||||||
|
|
||||||
|
using node::native_module::CodeCacheBuilder;
|
||||||
|
using v8::ArrayBuffer;
|
||||||
|
using v8::Context;
|
||||||
|
using v8::HandleScope;
|
||||||
|
using v8::Isolate;
|
||||||
|
using v8::Local;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <VersionHelpers.h>
|
||||||
|
#include <WinError.h>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
int wmain(int argc, wchar_t* argv[]) {
|
||||||
|
#else // UNIX
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
|
if (argc < 2) {
|
||||||
|
std::cerr << "Usage: " << argv[0] << " <path/to/output.cc>\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ofstream out;
|
||||||
|
out.open(argv[1], std::ios::out | std::ios::binary);
|
||||||
|
if (!out.is_open()) {
|
||||||
|
std::cerr << "Cannot open " << argv[1] << "\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
|
||||||
|
v8::V8::InitializePlatform(platform.get());
|
||||||
|
v8::V8::Initialize();
|
||||||
|
|
||||||
|
// Create a new Isolate and make it the current one.
|
||||||
|
Isolate::CreateParams create_params;
|
||||||
|
create_params.array_buffer_allocator =
|
||||||
|
ArrayBuffer::Allocator::NewDefaultAllocator();
|
||||||
|
Isolate* isolate = Isolate::New(create_params);
|
||||||
|
{
|
||||||
|
Isolate::Scope isolate_scope(isolate);
|
||||||
|
v8::HandleScope handle_scope(isolate);
|
||||||
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
||||||
|
v8::Context::Scope context_scope(context);
|
||||||
|
|
||||||
|
std::string cache = CodeCacheBuilder::Generate(context);
|
||||||
|
out << cache;
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -1,135 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
// Flags: --expose-internals
|
|
||||||
|
|
||||||
// This file generates the code cache for builtin modules and
|
|
||||||
// writes them into static char arrays of a C++ file that can be
|
|
||||||
// compiled into the binary using the `--code-cache-path` option
|
|
||||||
// of `configure`.
|
|
||||||
|
|
||||||
const { internalBinding } = require('internal/test/binding');
|
|
||||||
const {
|
|
||||||
moduleCategories: { canBeRequired },
|
|
||||||
getCodeCache,
|
|
||||||
compileFunction,
|
|
||||||
} = internalBinding('native_module');
|
|
||||||
|
|
||||||
const {
|
|
||||||
types: {
|
|
||||||
isUint8Array
|
|
||||||
}
|
|
||||||
} = require('util');
|
|
||||||
|
|
||||||
const fs = require('fs');
|
|
||||||
|
|
||||||
const resultPath = process.argv[2];
|
|
||||||
if (!resultPath) {
|
|
||||||
console.error(`Usage: ${process.argv[0]} ${process.argv[1]}` +
|
|
||||||
'path/to/node_code_cache.cc');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Format a number of a size in bytes into human-readable strings
|
|
||||||
* @param {number} num
|
|
||||||
* @return {string}
|
|
||||||
*/
|
|
||||||
function formatSize(num) {
|
|
||||||
if (num < 1024) {
|
|
||||||
return `${(num).toFixed(2)}B`;
|
|
||||||
} else if (num < 1024 ** 2) {
|
|
||||||
return `${(num / 1024).toFixed(2)}KB`;
|
|
||||||
} else if (num < 1024 ** 3) {
|
|
||||||
return `${(num / (1024 ** 2)).toFixed(2)}MB`;
|
|
||||||
} else {
|
|
||||||
return `${(num / (1024 ** 3)).toFixed(2)}GB`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates the source code of definitions of the char arrays
|
|
||||||
* that contains the code cache and the source code of the
|
|
||||||
* initializers of the code cache.
|
|
||||||
*
|
|
||||||
* @param {string} key ID of the builtin module
|
|
||||||
* @param {Uint8Array} cache Code cache of the builtin module
|
|
||||||
* @return { definition: string, initializer: string }
|
|
||||||
*/
|
|
||||||
function getInitalizer(key, cache) {
|
|
||||||
const defName = `${key.replace(/\//g, '_').replace(/-/g, '_')}_raw`;
|
|
||||||
const definition = `static const uint8_t ${defName}[] = {\n` +
|
|
||||||
`${cache.join(',')}\n};`;
|
|
||||||
const dataDef = 'std::make_unique<v8::ScriptCompiler::CachedData>(' +
|
|
||||||
`${defName}, static_cast<int>(arraysize(${defName})), ` +
|
|
||||||
'policy)';
|
|
||||||
const initializer =
|
|
||||||
'code_cache->emplace(\n' +
|
|
||||||
` "${key}",\n` +
|
|
||||||
` ${dataDef}\n` +
|
|
||||||
');';
|
|
||||||
return {
|
|
||||||
definition, initializer
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const cacheDefinitions = [];
|
|
||||||
const cacheInitializers = [];
|
|
||||||
let totalCacheSize = 0;
|
|
||||||
|
|
||||||
function lexical(a, b) {
|
|
||||||
if (a < b) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (a > b) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(joyeecheung): support non-modules that require different
|
|
||||||
// parameters in the wrapper.
|
|
||||||
for (const key of [...canBeRequired].sort(lexical)) {
|
|
||||||
compileFunction(key); // compile it
|
|
||||||
const cachedData = getCodeCache(key);
|
|
||||||
if (!isUint8Array(cachedData)) {
|
|
||||||
console.error(`Failed to generate code cache for '${key}'`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const size = cachedData.byteLength;
|
|
||||||
totalCacheSize += size;
|
|
||||||
const {
|
|
||||||
definition, initializer,
|
|
||||||
} = getInitalizer(key, cachedData);
|
|
||||||
cacheDefinitions.push(definition);
|
|
||||||
cacheInitializers.push(initializer);
|
|
||||||
console.log(`Generated cache for '${key}', size = ${formatSize(size)}` +
|
|
||||||
`, total = ${formatSize(totalCacheSize)}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = `#include "node_native_module_env.h"
|
|
||||||
|
|
||||||
// This file is generated by tools/generate_code_cache.js
|
|
||||||
// and is used when configure is run with \`--code-cache-path\`
|
|
||||||
|
|
||||||
namespace node {
|
|
||||||
namespace native_module {
|
|
||||||
${cacheDefinitions.join('\n\n')}
|
|
||||||
|
|
||||||
void NativeModuleEnv::InitializeCodeCache() {
|
|
||||||
NativeModuleCacheMap* code_cache =
|
|
||||||
NativeModuleLoader::GetInstance()->code_cache();
|
|
||||||
if (!code_cache->empty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto policy = v8::ScriptCompiler::CachedData::BufferPolicy::BufferNotOwned;
|
|
||||||
${cacheInitializers.join('\n ')}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace native_module
|
|
||||||
} // namespace node
|
|
||||||
`;
|
|
||||||
|
|
||||||
fs.writeFileSync(resultPath, result);
|
|
||||||
console.log(`Generated code cache C++ file to ${resultPath}`);
|
|
Loading…
x
Reference in New Issue
Block a user