1 #include "cache_builder.h"
2 #include "debug_utils-inl.h"
3 #include "node_native_module.h"
4 #include "util.h"
5
6 #include <iostream>
7 #include <map>
8 #include <sstream>
9 #include <vector>
10 #include <cstdlib>
11
12 namespace node {
13 namespace native_module {
14
15 using v8::Context;
16 using v8::Local;
17 using v8::ScriptCompiler;
18
GetDefName(const std::string & id)19 static std::string GetDefName(const std::string& id) {
20 char buf[64] = {0};
21 size_t size = id.size();
22 CHECK_LT(size, sizeof(buf));
23 for (size_t i = 0; i < size; ++i) {
24 char ch = id[i];
25 buf[i] = (ch == '-' || ch == '/') ? '_' : ch;
26 }
27 return buf;
28 }
29
FormatSize(size_t size)30 static std::string FormatSize(size_t size) {
31 char buf[64] = {0};
32 if (size < 1024) {
33 snprintf(buf, sizeof(buf), "%.2fB", static_cast<double>(size));
34 } else if (size < 1024 * 1024) {
35 snprintf(buf, sizeof(buf), "%.2fKB", static_cast<double>(size / 1024));
36 } else {
37 snprintf(
38 buf, sizeof(buf), "%.2fMB", static_cast<double>(size / 1024 / 1024));
39 }
40 return buf;
41 }
42
GetDefinition(const std::string & id,size_t size,const uint8_t * data)43 static std::string GetDefinition(const std::string& id,
44 size_t size,
45 const uint8_t* data) {
46 std::stringstream ss;
47 ss << "static const uint8_t " << GetDefName(id) << "[] = {\n";
48 for (size_t i = 0; i < size; ++i) {
49 uint8_t ch = data[i];
50 ss << std::to_string(ch) << (i == size - 1 ? '\n' : ',');
51 }
52 ss << "};";
53 return ss.str();
54 }
55
GetInitializer(const std::string & id,std::stringstream & ss)56 static void GetInitializer(const std::string& id, std::stringstream& ss) {
57 std::string def_name = GetDefName(id);
58 ss << " code_cache.emplace(\n";
59 ss << " \"" << id << "\",\n";
60 ss << " std::make_unique<v8::ScriptCompiler::CachedData>(\n";
61 ss << " " << def_name << ",\n";
62 ss << " static_cast<int>(arraysize(" << def_name << ")), policy\n";
63 ss << " )\n";
64 ss << " );";
65 }
66
GenerateCodeCache(const std::map<std::string,ScriptCompiler::CachedData * > & data)67 static std::string GenerateCodeCache(
68 const std::map<std::string, ScriptCompiler::CachedData*>& data) {
69 std::stringstream ss;
70 ss << R"(#include <cinttypes>
71 #include "node_native_module_env.h"
72
73 // This file is generated by mkcodecache (tools/code_cache/mkcodecache.cc)
74
75 namespace node {
76 namespace native_module {
77
78 const bool has_code_cache = true;
79
80 )";
81
82 size_t total = 0;
83 for (const auto& x : data) {
84 const std::string& id = x.first;
85 ScriptCompiler::CachedData* cached_data = x.second;
86 total += cached_data->length;
87 std::string def = GetDefinition(id, cached_data->length, cached_data->data);
88 ss << def << "\n\n";
89 std::string size_str = FormatSize(cached_data->length);
90 std::string total_str = FormatSize(total);
91 per_process::Debug(DebugCategory::CODE_CACHE,
92 "Generated cache for %s, size = %s, total = %s\n",
93 id.c_str(),
94 size_str.c_str(),
95 total_str.c_str());
96 }
97
98 ss << R"(void NativeModuleEnv::InitializeCodeCache() {
99 NativeModuleCacheMap& code_cache =
100 *NativeModuleLoader::GetInstance()->code_cache();
101 CHECK(code_cache.empty());
102 auto policy = v8::ScriptCompiler::CachedData::BufferPolicy::BufferNotOwned;
103 )";
104
105 for (const auto& x : data) {
106 GetInitializer(x.first, ss);
107 ss << "\n\n";
108 }
109
110 ss << R"(
111 }
112
113 } // namespace native_module
114 } // namespace node
115 )";
116 return ss.str();
117 }
118
Generate(Local<Context> context)119 std::string CodeCacheBuilder::Generate(Local<Context> context) {
120 NativeModuleLoader* loader = NativeModuleLoader::GetInstance();
121 std::vector<std::string> ids = loader->GetModuleIds();
122
123 std::map<std::string, ScriptCompiler::CachedData*> data;
124
125 for (const auto& id : ids) {
126 // TODO(joyeecheung): we can only compile the modules that can be
127 // required here because the parameters for other types of builtins
128 // are still very flexible. We should look into auto-generating
129 // the parameters from the source somehow.
130 if (loader->CanBeRequired(id.c_str())) {
131 NativeModuleLoader::Result result;
132 USE(loader->CompileAsModule(context, id.c_str(), &result));
133 ScriptCompiler::CachedData* cached_data =
134 loader->GetCodeCache(id.c_str());
135 if (cached_data == nullptr) {
136 // TODO(joyeecheung): display syntax errors
137 std::cerr << "Failed to compile " << id << "\n";
138 } else {
139 data.emplace(id, cached_data);
140 }
141 }
142 }
143
144 return GenerateCodeCache(data);
145 }
146
147 } // namespace native_module
148 } // namespace node
149