• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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