Branch data Line data Source code
1 : : #include "backend/V8Engine.hpp"
2 : :
3 : : #include "backend/Interpreter.hpp"
4 : : #include "backend/WasmOperator.hpp"
5 : : #include "backend/WasmUtil.hpp"
6 : : #include "mutable/util/macro.hpp"
7 : : #include "storage/Store.hpp"
8 : : #include <chrono>
9 : : #include <cstdint>
10 : : #include <cstdlib>
11 : : #include <cstring>
12 : : #include <ctime>
13 : : #include <fstream>
14 : : #include <fstream>
15 : : #include <libplatform/libplatform.h>
16 : : #include <mutable/catalog/Catalog.hpp>
17 : : #include <mutable/IR/PhysicalOptimizer.hpp>
18 : : #include <mutable/IR/Tuple.hpp>
19 : : #include <mutable/Options.hpp>
20 : : #include <mutable/storage/DataLayoutFactory.hpp>
21 : : #include <mutable/storage/Store.hpp>
22 : : #include <mutable/util/DotTool.hpp>
23 : : #include <mutable/util/enum_ops.hpp>
24 : : #include <mutable/util/memory.hpp>
25 : : #include <mutable/util/Timer.hpp>
26 : : #include <sstream>
27 : : #include <stdexcept>
28 : : #include <string_view>
29 : : #include <unordered_set>
30 : :
31 : : // must be included after Binaryen due to conflicts, e.g. with `::wasm::Throw`
32 : 1 : #include "backend/WasmMacro.hpp"
33 : :
34 : :
35 : : using namespace m;
36 : : using namespace m::storage;
37 : : using namespace m::wasm;
38 : : using namespace m::wasm::detail;
39 : : using args_t = v8::Local<v8::Value>[];
40 : :
41 : :
42 : : namespace {
43 : :
44 : : namespace options {
45 : 0 :
46 : : /** The Wasm optimization level. */
47 : : int wasm_optimization_level = 0;
48 : : /** Whether to execute Wasm adaptively. */
49 : : bool wasm_adaptive = false;
50 : : /** Whether compilation cache should be enabled. */
51 : : bool wasm_compilation_cache = true;
52 : : /** Whether to dump the generated WebAssembly code. */
53 : : bool wasm_dump = false;
54 : : /** Whether to dump the generated assembly code. */
55 : : bool asm_dump = false;
56 : : /** The port to use for the Chrome DevTools web socket. */
57 : : uint16_t cdt_port = 0;
58 : :
59 : : }
60 : :
61 : :
62 : : /*======================================================================================================================
63 : : * V8Engine
64 : : *====================================================================================================================*/
65 : :
66 : : /** The `V8Engine` is a `WasmEngine` using [V8, Google's open source high-performance JavaScript and WebAssembly
67 : 0 : * engine] (https://v8.dev/). */
68 : : struct V8Engine : m::WasmEngine
69 : : {
70 : : friend void create_V8Engine();
71 : : friend void destroy_V8Engine();
72 : : friend void register_WasmV8();
73 : :
74 : 1 : private:
75 : : static inline v8::Platform *PLATFORM_ = nullptr;
76 : 0 : v8::ArrayBuffer::Allocator *allocator_ = nullptr;
77 : 0 : v8::Isolate *isolate_ = nullptr;
78 : :
79 : : /*----- Objects for remote debugging via CDT. --------------------------------------------------------------------*/
80 : : std::unique_ptr<V8InspectorClientImpl> inspector_;
81 : :
82 : : public:
83 : : V8Engine();
84 : : V8Engine(const V8Engine&) = delete;
85 : : V8Engine(V8Engine&&) = default;
86 : :
87 : : ~V8Engine();
88 : :
89 : 0 : static v8::Platform * platform() {
90 : 0 : M_insist(bool(PLATFORM_));
91 : 0 : return PLATFORM_;
92 : : }
93 : :
94 : : void initialize();
95 : : void compile(const m::MatchBase &plan) const override;
96 : : void execute(const m::MatchBase &plan) override;
97 : : };
98 : 1 :
99 : :
100 : : /*======================================================================================================================
101 : : * Implementation of V8Inspector and helper classes / methods
102 : : *====================================================================================================================*/
103 : :
104 : 8270 : inline v8::Local<v8::String> to_v8_string(v8::Isolate *isolate, std::string_view sv) {
105 : 8270 : M_insist(isolate);
106 : 8271 : return v8::String::NewFromUtf8(isolate, sv.data(), v8::NewStringType::kNormal, sv.length()).ToLocalChecked();
107 : : }
108 : :
109 : 0 : inline std::string to_std_string(v8::Isolate *isolate, v8::Local<v8::Value> val) {
110 : 0 : v8::String::Utf8Value utf8(isolate, val);
111 [ # # # # ]: 0 : return *utf8;
112 : 0 : }
113 : :
114 : 0 : inline v8::Local<v8::Object> parse_json(v8::Isolate *isolate, std::string_view json) {
115 : 0 : M_insist(isolate);
116 : 0 : auto Ctx = isolate->GetCurrentContext();
117 : 0 : auto value = v8::JSON::Parse(Ctx, to_v8_string(isolate, json)).ToLocalChecked();
118 [ # # ]: 0 : if (value.IsEmpty())
119 : 1 : return v8::Local<v8::Object>();
120 : 0 : return value->ToObject(Ctx).ToLocalChecked();
121 : 0 : }
122 : :
123 : 0 : inline v8_inspector::StringView make_string_view(const std::string &str) {
124 : 0 : return v8_inspector::StringView(reinterpret_cast<const uint8_t*>(str.data()), str.length());
125 : : }
126 : :
127 : 0 : inline std::string to_std_string(v8::Isolate *isolate, const v8_inspector::StringView sv) {
128 : 0 : int length = static_cast<int>(sv.length());
129 : : v8::Local<v8::String> message = (
130 [ # # ]: 0 : sv.is8Bit()
131 : 0 : ? v8::String::NewFromOneByte(isolate, reinterpret_cast<const uint8_t*>(sv.characters8()), v8::NewStringType::kNormal, length)
132 : 0 : : v8::String::NewFromTwoByte(isolate, reinterpret_cast<const uint16_t*>(sv.characters16()), v8::NewStringType::kNormal, length)
133 : 0 : ).ToLocalChecked();
134 : 0 : v8::String::Utf8Value result(isolate, message);
135 [ # # # # : 0 : return std::string(*result, result.length());
# # ]
136 : 0 : }
137 : :
138 : : }
139 : :
140 : 0 : void WebSocketChannel::sendResponse(int, std::unique_ptr<v8_inspector::StringBuffer> message)
141 : : {
142 : 0 : v8::HandleScope handle_scope(isolate_);
143 [ # # # # ]: 0 : auto str = to_std_string(isolate_, message->string());
144 [ # # ]: 0 : conn_.send(str);
145 : 0 : }
146 : :
147 : 0 : void WebSocketChannel::sendNotification(std::unique_ptr<v8_inspector::StringBuffer> message)
148 : : {
149 : 0 : v8::HandleScope handle_scope(isolate_);
150 [ # # # # ]: 0 : auto str = to_std_string(isolate_, message->string());
151 [ # # ]: 0 : conn_.send(str);
152 : 0 : }
153 : :
154 : 0 : V8InspectorClientImpl::V8InspectorClientImpl(int16_t port, v8::Isolate *isolate)
155 [ # # ]: 0 : : isolate_(M_notnull(isolate))
156 [ # # # # : 0 : , server_(port, std::bind(&V8InspectorClientImpl::on_message, this, std::placeholders::_1))
# # ]
157 : 0 : {
158 [ # # ]: 0 : std::cout << "Initiating the V8 inspector server. To attach to the inspector, open Chrome/Chromium and "
159 : : "visit\n\n\t"
160 : : "devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=127.0.0.1:"
161 [ # # # # : 0 : << port << '\n' << std::endl;
# # ]
162 : :
163 [ # # ]: 0 : inspector_ = v8_inspector::V8Inspector::create(isolate, this);
164 [ # # # # ]: 0 : conn_ = std::make_unique<WebSocketServer::Connection>(server_.await());
165 [ # # ]: 0 : channel_ = std::make_unique<WebSocketChannel>(isolate_, *conn_);
166 : :
167 : : /* Create a debugging session by connecting the V8Inspector instance to the channel. */
168 [ # # ]: 0 : std::string state("mutable");
169 [ # # ]: 0 : session_ = inspector_->connect(
170 : : /* contextGroupId= */ 1,
171 : 0 : /* channel= */ channel_.get(),
172 [ # # ]: 0 : /* state= */ make_string_view(state),
173 : : /* trustLevel= */ v8_inspector::V8Inspector::kFullyTrusted,
174 : : /* pauseState= */ v8_inspector::V8Inspector::kWaitingForDebugger
175 : : );
176 : 0 : }
177 : :
178 : 0 : void V8InspectorClientImpl::register_context(v8::Local<v8::Context> context)
179 : : {
180 [ # # ]: 0 : std::string ctx_name("query");
181 [ # # # # : 0 : inspector_->contextCreated(v8_inspector::V8ContextInfo( context, 1, make_string_view(ctx_name)));
# # ]
182 : 0 : }
183 : :
184 : 0 : void V8InspectorClientImpl::deregister_context(v8::Local<v8::Context> context)
185 : : {
186 : 0 : inspector_->contextDestroyed(context);
187 : 0 : }
188 : :
189 : 0 : void V8InspectorClientImpl::on_message(std::string_view sv)
190 : : {
191 : 1 : v8_inspector::StringView msg(reinterpret_cast<const uint8_t*>(sv.data()), sv.length());
192 [ + - ]: 1 :
193 : 0 : auto Ctx = isolate_->GetCurrentContext();
194 : 0 : v8::HandleScope handle_scope(isolate_);
195 [ # # ]: 0 : auto obj = parse_json(isolate_, sv);
196 : :
197 [ # # ]: 0 : session_->dispatchProtocolMessage(msg);
198 : :
199 [ # # # # ]: 1 : if (not obj.IsEmpty()) {
200 [ + - # # : 1 : auto method = obj->Get(Ctx, to_v8_string(isolate_, "method")).ToLocalChecked();
# # # # #
# # # ]
201 [ # # ]: 0 : auto method_name = to_std_string(isolate_, method);
202 : :
203 [ # # # # ]: 0 : if (method_name == "Runtime.runIfWaitingForDebugger") {
204 [ # # ]: 0 : std::string reason("CDT");
205 [ # # # # ]: 0 : session_->schedulePauseOnNextStatement(make_string_view(reason),
206 [ # # ]: 0 : make_string_view(reason));
207 [ # # ]: 0 : waitFrontendMessageOnPause();
208 [ # # ]: 0 : code_(); // execute the code to debug
209 : 0 : }
210 : 0 : }
211 : 0 : }
212 : :
213 : 0 : void V8InspectorClientImpl::runMessageLoopOnPause(int)
214 : : {
215 : : static bool is_nested = false;
216 [ # # ]: 0 : if (is_nested) return;
217 : :
218 : 0 : is_terminated_ = false;
219 : 0 : is_nested = true;
220 [ # # # # ]: 0 : while (not is_terminated_ and conn_->wait_on_message())
221 [ # # ]: 0 : while (v8::platform::PumpMessageLoop(V8Engine::platform(), isolate_)) { }
222 : 0 : is_terminated_ = true;
223 : 0 : is_nested = false;
224 : 0 : }
225 : :
226 : :
227 : : /*======================================================================================================================
228 : : * V8 Callback Functions
229 : : *
230 : : * Functions to be called from the WebAssembly module to give control flow and pass data to the host.
231 : : *====================================================================================================================*/
232 : :
233 : 0 : void m::wasm::detail::insist(const v8::FunctionCallbackInfo<v8::Value> &info)
234 : : {
235 : 0 : M_insist(info.Length() == 1);
236 : 0 : auto idx = info[0].As<v8::BigInt>()->Uint64Value();
237 : 0 : auto [filename, line, msg] = Module::Get().get_message(idx);
238 : :
239 : 0 : std::cout.flush();
240 : 0 : std::cerr << filename << ':' << line << ": Wasm_insist failed.";
241 [ # # ]: 0 : if (msg)
242 : 0 : std::cerr << " " << msg << '.';
243 : 0 : std::cerr << std::endl;
244 : :
245 : 0 : abort();
246 : : }
247 : :
248 : 0 : void m::wasm::detail::_throw(const v8::FunctionCallbackInfo<v8::Value> &info)
249 : : {
250 : 0 : M_insist(info.Length() == 2);
251 : 0 : auto type = static_cast<m::wasm::exception::exception_t>(info[0].As<v8::BigInt>()->Uint64Value());
252 : 0 : auto idx = info[1].As<v8::BigInt>()->Uint64Value();
253 : 0 : auto [filename, line, msg] = Module::Get().get_message(idx);
254 : :
255 : 0 : std::ostringstream oss;
256 [ # # # # : 0 : oss << filename << ':' << line << ": Exception `" << m::wasm::exception::names_[type] << "` thrown.";
# # # # #
# # # ]
257 [ # # ]: 0 : if (*msg)
258 [ # # # # : 0 : oss << " " << msg << '.';
# # ]
259 [ # # ]: 0 : oss << std::endl;
260 : :
261 [ # # # # : 0 : throw m::wasm::exception(type, oss.str());
# # # # ]
262 : 0 : }
263 : :
264 : 0 : void m::wasm::detail::print(const v8::FunctionCallbackInfo<v8::Value> &info)
265 : : {
266 : : #ifndef NDEBUG
267 : 0 : std::cout << "v8 function callback: ";
268 : : #endif
269 [ # # ]: 0 : for (int i = 0; i != info.Length(); ++i) {
270 : 0 : v8::HandleScope handle_scope(info.GetIsolate());
271 [ # # # # ]: 0 : if (i != 0) std::cout << ',';
272 [ # # ]: 0 : v8::Local<v8::Value> v = info[i];
273 [ # # # # : 0 : if (v->IsInt32())
# # ]
274 [ # # # # : 0 : std::cout << "0x" << std::hex << uint32_t(v.As<v8::Int32>()->Value()) << std::dec;
# # # # #
# # # #
# ]
275 : : else
276 [ # # # # : 0 : std::cout << *v8::String::Utf8Value(info.GetIsolate(), v);
# # # # ]
277 : 0 : }
278 : 0 : std::cout << std::endl;
279 : 0 : }
280 : :
281 : 0 : void m::wasm::detail::print_memory_consumption(const v8::FunctionCallbackInfo<v8::Value> &info)
282 : : {
283 : 0 : M_insist(Options::Get().statistics);
284 : :
285 : 0 : auto alloc_total_mem = info[0].As<v8::Uint32>()->Value();
286 : 0 : auto alloc_peak_mem = info[1].As<v8::Uint32>()->Value();
287 : :
288 : 0 : std::cout << "Allocated memory overall consumption: " << alloc_total_mem / (1024.0 * 1024.0) << " MiB"<< std::endl;
289 : 1 : std::cout << "Allocated memory peak consumption: " << alloc_peak_mem / (1024.0 * 1024.0) << " MiB"<< std::endl;
290 : 0 : }
291 : :
292 : 1 : void m::wasm::detail::set_wasm_instance_raw_memory(const v8::FunctionCallbackInfo<v8::Value> &info)
293 : : {
294 : 0 : v8::Local<v8::WasmModuleObject> wasm_instance = info[0].As<v8::WasmModuleObject>();
295 : 1 : v8::Local<v8::Int32> wasm_context_id = info[1].As<v8::Int32>();
296 : :
297 : 0 : auto &wasm_context = WasmEngine::Get_Wasm_Context_By_ID(wasm_context_id->Value());
298 : 1 : #ifndef NDEBUG
299 : 0 : std::cerr << "Setting Wasm instance raw memory of the given instance to the VM of Wasm context "
300 : 0 : << wasm_context_id->Value() << " at " << wasm_context.vm.addr() << " of " << wasm_context.vm.size()
301 : 0 : << " bytes" << std::endl;
302 : : #endif
303 : 0 : v8::SetWasmInstanceRawMemory(wasm_instance, wasm_context.vm.as<uint8_t*>(), wasm_context.vm.size());
304 : 0 : }
305 : :
306 : 0 : void m::wasm::detail::read_result_set(const v8::FunctionCallbackInfo<v8::Value> &info)
307 : : {
308 : 0 : auto &context = WasmEngine::Get_Wasm_Context_By_ID(Module::ID());
309 : :
310 : 0 : auto &root_op = context.plan.get_matched_root();
311 : 0 : auto &schema = root_op.schema();
312 : 0 : auto deduplicated_schema = schema.deduplicate();
313 [ # # ]: 0 : auto deduplicated_schema_without_constants = deduplicated_schema.drop_constants();
314 : :
315 : : /* Get number of result tuples. */
316 [ # # # # : 0 : auto num_tuples = info[1].As<v8::Uint32>()->Value();
# # # # ]
317 [ # # ]: 0 : if (num_tuples == 0)
318 : 0 : return;
319 : :
320 : : /* Compute address of result set. */
321 [ # # # # ]: 0 : M_insist(info.Length() == 2);
322 [ # # # # : 0 : auto result_set_offset = info[0].As<v8::Uint32>()->Value();
# # # # ]
323 [ # # # # ]: 0 : M_insist((result_set_offset == 0) == (deduplicated_schema_without_constants.num_entries() == 0),
324 : : "result set offset equals 0 (i.e. nullptr) iff schema contains only constants");
325 [ # # ]: 0 : auto result_set = context.vm.as<uint8_t*>() + result_set_offset;
326 : :
327 : : /* Find the projection nearest to the plan's root since it will determine the constants omitted in the result set. */
328 : 0 : auto find_projection = [](const Operator &op) -> const ProjectionOperator * {
329 : 0 : auto find_projection_impl = [](const Operator &op, auto &find_projection_ref) -> const ProjectionOperator * {
330 [ # # ]: 0 : if (auto projection_op = cast<const ProjectionOperator>(&op)) {
331 : 0 : return projection_op;
332 [ # # ]: 0 : } else if (auto c = cast<const Consumer>(&op)) {
333 : 0 : M_insist(c->children().size() == 1,
334 : : "at least one projection without siblings in the operator tree must be contained");
335 : 0 : M_insist(c->schema().num_entries() == c->child(0)->schema().num_entries(),
336 : : "at least one projection with the same schema as the plan's root must be contained");
337 : : #ifndef NDEBUG
338 [ # # ]: 0 : for (std::size_t i = 0; i < c->schema().num_entries(); ++i)
339 : 0 : M_insist(c->schema()[i].id == c->child(0)->schema()[i].id,
340 : : "at least one projection with the same schema as the plan's root must be contained");
341 : : #endif
342 : 0 : return find_projection_ref(*c->child(0), find_projection_ref);
343 : : } else {
344 : 0 : return nullptr; // no projection found
345 : : }
346 : 0 : };
347 : 0 : return find_projection_impl(op, find_projection_impl);
348 : : };
349 [ # # ]: 0 : auto projection = find_projection(root_op);
350 : :
351 : : ///> helper function to print given `ast::Constant` \p c of `Type` \p type to \p out
352 : 0 : auto print_constant = [](std::ostringstream &out, const ast::Constant &c, const Type *type){
353 [ # # ]: 0 : if (type->is_none()) {
354 : 0 : out << "NULL";
355 : 0 : return;
356 : : }
357 : :
358 : : /* Interpret constant. */
359 : 0 : auto value = Interpreter::eval(c);
360 : :
361 : 0 : visit(overloaded {
362 : 0 : [&](const Boolean&) { out << (value.as_b() ? "TRUE" : "FALSE"); },
363 : 0 : [&](const Numeric &n) {
364 [ # # # ]: 0 : switch (n.kind) {
365 : : case Numeric::N_Int:
366 : : case Numeric::N_Decimal:
367 : 0 : out << value.as_i();
368 : 0 : break;
369 : : case Numeric::N_Float:
370 [ # # ]: 0 : if (n.size() <= 32) {
371 : 0 : const auto old_precision = out.precision(std::numeric_limits<float>::max_digits10 - 1);
372 : 0 : out << value.as_f();
373 : 0 : out.precision(old_precision);
374 : 0 : } else {
375 : 0 : const auto old_precision = out.precision(std::numeric_limits<double>::max_digits10 - 1);
376 : 0 : out << value.as_d();
377 : 2 : out.precision(old_precision);
378 : : }
379 : 0 : }
380 : 0 : },
381 : 0 : [&](const CharacterSequence&) { out << '"' << reinterpret_cast<char*>(value.as_p()) << '"'; },
382 : 0 : [&](const Date&) {
383 : 0 : const int32_t date = value.as_i(); // signed because year is signed
384 : 0 : const auto oldfill = out.fill('0');
385 : 0 : const auto oldfmt = out.flags();
386 : 0 : out << std::internal
387 : 0 : << std::setw(date >> 9 > 0 ? 4 : 5) << (date >> 9) << '-'
388 : 0 : << std::setw(2) << ((date >> 5) & 0xF) << '-'
389 : 0 : << std::setw(2) << (date & 0x1F);
390 : 0 : out.fill(oldfill);
391 : 0 : out.flags(oldfmt);
392 : 0 : },
393 : 0 : [&](const DateTime&) {
394 : 0 : const time_t time = value.as_i();
395 : : std::tm tm;
396 : 0 : gmtime_r(&time, &tm);
397 : 0 : out << put_tm(tm);
398 : 0 : },
399 : 0 : [](const NoneType&) { M_unreachable("should've been handled earlier"); },
400 : 0 : [](auto&&) { M_unreachable("invalid type"); },
401 : 0 : }, *type);
402 : 0 : };
403 : :
404 [ # # # # ]: 0 : if (deduplicated_schema_without_constants.num_entries() == 0) {
405 : : /* Schema contains only constants. Create simple loop to generate `num_tuples` constant result tuples. */
406 [ # # ]: 0 : M_insist(bool(projection), "projection must be found");
407 [ # # ]: 0 : auto &projections = projection->projections();
408 [ # # # # ]: 0 : if (auto callback_op = cast<const CallbackOperator>(&root_op)) {
409 [ # # ]: 0 : Tuple tup(schema); // tuple entries which are not set are implicitly NULL
410 [ # # # # ]: 0 : for (std::size_t i = 0; i < schema.num_entries(); ++i) {
411 [ # # ]: 0 : auto &e = schema[i];
412 [ # # # # ]: 0 : if (e.type->is_none()) continue; // NULL constant
413 [ # # # # ]: 0 : M_insist(e.id.is_constant());
414 [ # # # # : 0 : tup.set(i, Interpreter::eval(as<const ast::Constant>(projections[i].first)));
# # ]
415 : 0 : }
416 [ # # ]: 0 : for (std::size_t i = 0; i < num_tuples; ++i)
417 [ # # # # ]: 0 : callback_op->callback()(schema, tup);
418 [ # # # # ]: 0 : } else if (auto print_op = cast<const PrintOperator>(&root_op)) {
419 [ # # ]: 0 : std::ostringstream tup;
420 [ # # # # ]: 0 : for (std::size_t i = 0; i < schema.num_entries(); ++i) {
421 [ # # ]: 0 : auto &e = schema[i];
422 [ # # ]: 0 : if (i)
423 [ # # ]: 0 : tup << ',';
424 [ # # # # ]: 0 : M_insist(e.id.is_constant());
425 [ # # # # ]: 0 : print_constant(tup, as<const ast::Constant>(projections[i].first), e.type);
426 : 0 : }
427 [ # # ]: 0 : for (std::size_t i = 0; i < num_tuples; ++i)
428 [ # # # # : 0 : print_op->out << tup.str() << '\n';
# # ]
429 : 0 : }
430 : 0 : return;
431 : : }
432 : :
433 : : /* Create data layout (without constants and duplicates). */
434 [ # # ]: 0 : M_insist(bool(context.result_set_factory), "result set factory must be set");
435 [ # # ]: 0 : auto layout = context.result_set_factory->make(deduplicated_schema_without_constants);
436 : :
437 : : /* Extract results. */
438 [ # # # # ]: 0 : if (auto callback_op = cast<const CallbackOperator>(&root_op)) {
439 [ # # ]: 0 : auto loader = Interpreter::compile_load(deduplicated_schema_without_constants, result_set, layout,
440 : : deduplicated_schema_without_constants);
441 [ # # # # : 0 : if (schema.num_entries() == deduplicated_schema.num_entries()) {
# # ]
442 : : /* No deduplication was performed. Compute `Tuple` with constants. */
443 [ # # # # ]: 0 : M_insist(schema == deduplicated_schema);
444 [ # # ]: 0 : Tuple tup(schema); // tuple entries which are not set are implicitly NULL
445 [ # # # # ]: 0 : for (std::size_t i = 0; i < schema.num_entries(); ++i) {
446 [ # # ]: 0 : auto &e = schema[i];
447 [ # # # # ]: 0 : if (e.type->is_none()) continue; // NULL constant
448 [ # # # # ]: 0 : if (e.id.is_constant()) { // other constant
449 [ # # ]: 0 : M_insist(bool(projection), "projection must be found");
450 [ # # # # : 0 : tup.set(i, Interpreter::eval(as<const ast::Constant>(projection->projections()[i].first)));
# # # # ]
451 : 0 : }
452 : 0 : }
453 : 0 : Tuple *args[] = { &tup };
454 [ # # ]: 0 : for (std::size_t i = 0; i != num_tuples; ++i) {
455 [ # # ]: 0 : loader(args);
456 [ # # # # ]: 0 : callback_op->callback()(schema, tup);
457 [ # # ]: 0 : tup.clear();
458 : 0 : }
459 : 0 : } else {
460 : : /* Deduplication was performed. Compute a `Tuple` with duplicates and constants. */
461 [ # # ]: 0 : Tuple tup_dedupl(deduplicated_schema_without_constants);
462 [ # # ]: 0 : Tuple tup_dupl(schema); // tuple entries which are not set are implicitly NULL
463 [ # # # # ]: 0 : for (std::size_t i = 0; i < schema.num_entries(); ++i) {
464 [ # # ]: 0 : auto &e = schema[i];
465 [ # # # # ]: 0 : if (e.type->is_none()) continue; // NULL constant
466 [ # # # # ]: 0 : if (e.id.is_constant()) { // other constant
467 [ # # ]: 0 : M_insist(bool(projection), "projection must be found");
468 [ # # # # : 0 : tup_dupl.set(i, Interpreter::eval(as<const ast::Constant>(projection->projections()[i].first)));
# # # # ]
469 : 0 : }
470 : 0 : }
471 : 0 : Tuple *args[] = { &tup_dedupl, &tup_dupl };
472 [ # # # # ]: 0 : for (std::size_t i = 0; i != deduplicated_schema_without_constants.num_entries(); ++i) {
473 [ # # ]: 0 : auto &entry = deduplicated_schema_without_constants[i];
474 [ # # # # ]: 0 : if (not entry.type->is_none())
475 [ # # ]: 0 : loader.emit_Ld_Tup(0, i);
476 [ # # # # ]: 0 : for (std::size_t j = 0; j != schema.num_entries(); ++j) {
477 [ # # ]: 0 : auto &e = schema[j];
478 [ # # # # ]: 0 : if (e.id == entry.id) {
479 [ # # ]: 0 : M_insist(e.type == entry.type);
480 [ # # ]: 0 : loader.emit_St_Tup(1, j, e.type);
481 : 0 : }
482 : 0 : }
483 [ # # # # ]: 0 : if (not entry.type->is_none())
484 [ # # ]: 0 : loader.emit_Pop();
485 : 0 : }
486 [ # # ]: 0 : for (std::size_t i = 0; i != num_tuples; ++i) {
487 [ # # ]: 0 : loader(args);
488 [ # # # # ]: 0 : callback_op->callback()(schema, tup_dupl);
489 : 0 : }
490 : 0 : }
491 [ # # # # ]: 0 : } else if (auto print_op = cast<const PrintOperator>(&root_op)) {
492 : : /* Compute a `Tuple` with duplicates and constants. */
493 [ # # ]: 0 : Tuple tup(deduplicated_schema_without_constants);
494 : 0 : Tuple *args[] = { &tup };
495 [ # # ]: 0 : auto printer = Interpreter::compile_load(deduplicated_schema_without_constants, result_set, layout,
496 : : deduplicated_schema_without_constants);
497 [ # # # # ]: 0 : auto ostream_index = printer.add(&print_op->out);
498 : 0 : bool constant_emitted = false;
499 : 0 : std::size_t old_idx = -1UL;
500 [ # # # # ]: 0 : for (std::size_t i = 0; i != schema.num_entries(); ++i) {
501 [ # # ]: 0 : if (i != 0)
502 [ # # ]: 0 : printer.emit_Putc(ostream_index, ',');
503 [ # # ]: 0 : auto &e = schema[i];
504 [ # # # # ]: 0 : if (not e.type->is_none()) {
505 [ # # # # ]: 0 : if (e.id.is_constant()) { // constant except NULL
506 [ # # ]: 0 : M_insist(bool(projection), "projection must be found");
507 [ # # # # : 0 : printer.add_and_emit_load(Interpreter::eval(as<const ast::Constant>(projection->projections()[i].first)));
# # # # ]
508 : 0 : constant_emitted = true;
509 : 0 : } else { // actual value
510 [ # # ]: 0 : auto idx = deduplicated_schema_without_constants[e.id].first;
511 [ # # ]: 0 : if (idx != old_idx) {
512 [ # # ]: 0 : if (old_idx != -1UL)
513 [ # # ]: 0 : printer.emit_Pop(); // to remove last loaded value
514 [ # # ]: 0 : printer.emit_Ld_Tup(0, idx);
515 : 0 : old_idx = idx;
516 : 0 : }
517 : : }
518 : 0 : }
519 [ # # ]: 0 : printer.emit_Print(ostream_index, e.type);
520 [ # # # # : 0 : if (e.type->is_none() or constant_emitted) {
# # ]
521 [ # # ]: 0 : printer.emit_Pop(); // to remove NULL pushed by `emit_Print()` or other constant pushed above
522 : 0 : constant_emitted = false;
523 : 0 : }
524 : 0 : }
525 [ # # ]: 0 : if (old_idx != -1UL)
526 [ # # ]: 0 : printer.emit_Pop(); // to remove last loaded value
527 [ # # ]: 0 : for (std::size_t i = 0; i != num_tuples; ++i) {
528 [ # # ]: 0 : printer(args);
529 [ # # ]: 0 : print_op->out << '\n';
530 : 0 : }
531 : 0 : }
532 [ # # ]: 0 : }
533 : :
534 : : template<typename Index, typename V8ValueT, bool IsLower>
535 : 0 : void m::wasm::detail::index_seek(const v8::FunctionCallbackInfo<v8::Value> &info)
536 : : {
537 : : using key_type = Index::key_type;
538 : :
539 : : /*----- Unpack function parameters -----*/
540 : 0 : auto index_id = info[0].As<v8::BigInt>()->Uint64Value();
541 : : key_type key;
542 : : if constexpr (std::same_as<V8ValueT, v8::BigInt>)
543 : 0 : key = info[1].As<V8ValueT>()->Int64Value();
544 : : else if constexpr (std::same_as<V8ValueT, v8::String>) {
545 : 0 : auto offset = info[1].As<v8::Uint32>()->Value();
546 : 0 : auto &context = WasmEngine::Get_Wasm_Context_By_ID(Module::ID());
547 : 0 : key = reinterpret_cast<const char*>(context.vm.as<uint8_t*>() + offset);
548 : : } else
549 : 0 : key = info[1].As<V8ValueT>()->Value();
550 : :
551 : : /*----- Obtain index and cast to correct type. -----*/
552 : 0 : auto &context = WasmEngine::Get_Wasm_Context_By_ID(Module::ID());
553 : 0 : auto &index = as<const Index>(context.indexes[index_id]);
554 : :
555 : : /*----- Seek index and return offset. -----*/
556 : 0 : std::size_t offset = std::distance(
557 : 0 : index.begin(),
558 : 0 : M_CONSTEXPR_COND(IsLower, index.lower_bound(key), index.upper_bound(key))
559 : : );
560 : 0 : M_insist(std::in_range<uint32_t>(offset), "should fit in uint32_t");
561 : 0 : info.GetReturnValue().Set(uint32_t(offset));
562 : 0 : }
563 : :
564 : : template<typename Index>
565 : 0 : void m::wasm::detail::index_sequential_scan(const v8::FunctionCallbackInfo<v8::Value> &info)
566 : : {
567 : : /*----- Unpack function parameters -----*/
568 : 0 : auto index_id = info[0].As<v8::BigInt>()->Uint64Value();
569 : 0 : auto entry_offset = info[1].As<v8::Uint32>()->Value();
570 : 0 : auto address_offset = info[2].As<v8::Uint32>()->Value();
571 : 0 : auto batch_size = info[3].As<v8::Uint32>()->Value();
572 : :
573 : : /*----- Compute adress to write results to. -----*/
574 : 0 : auto &context = WasmEngine::Get_Wasm_Context_By_ID(Module::ID());
575 : 0 : auto buffer_address = reinterpret_cast<uint32_t*>(context.vm.as<uint8_t*>() + address_offset);
576 : :
577 : : /*----- Obtain index and cast to correct type. -----*/
578 : 0 : auto &index = as<const Index>(context.indexes[index_id]);
579 : :
580 : : /*----- Scan index and write result tuple ids to buffer -----*/
581 : 0 : auto it = index.begin() + entry_offset;
582 [ # # # # : 0 : for (uint32_t i = 0; i < batch_size; ++i, ++it)
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
583 : 0 : buffer_address[i] = it->second;
584 : 0 : }
585 : :
586 : :
587 [ + - ]: 1 : /*======================================================================================================================
588 : : * V8Engine helper classes
589 [ + - ]: 1 : *====================================================================================================================*/
590 [ + - ]: 1 :
591 : : namespace {
592 : :
593 : : struct CollectStringLiterals : ConstOperatorVisitor, ast::ConstASTExprVisitor
594 : : {
595 : : private:
596 : : std::unordered_set<const char*> literals_; ///< the collected literals
597 : :
598 : : public:
599 : 0 : static std::vector<const char*> Collect(const Operator &plan) {
600 : 0 : CollectStringLiterals CSL;
601 [ # # ]: 0 : CSL(plan);
602 [ # # ]: 0 : return { CSL.literals_.begin(), CSL.literals_.end() };
603 : 0 : }
604 : :
605 : : private:
606 [ # # ]: 0 : CollectStringLiterals() = default;
607 : :
608 : : using ConstOperatorVisitor::operator();
609 : : using ConstASTExprVisitor::operator();
610 : :
611 : 0 : void recurse(const Consumer &C) {
612 [ # # ]: 0 : for (auto &c: C.children())
613 : 0 : (*this)(*c);
614 : 0 : }
615 : :
616 : : /*----- Operator -------------------------------------------------------------------------------------------------*/
617 : 0 : void operator()(const ScanOperator&) override { /* nothing to be done */ }
618 : 0 : void operator()(const CallbackOperator &op) override { recurse(op); }
619 : 0 : void operator()(const PrintOperator &op) override { recurse(op); }
620 : 0 : void operator()(const NoOpOperator &op) override { recurse(op); }
621 : 0 : void operator()(const FilterOperator &op) override {
622 : 0 : (*this)(op.filter());
623 : 0 : recurse(op);
624 : 0 : }
625 : 0 : void operator()(const DisjunctiveFilterOperator &op) override {
626 : 0 : (*this)(op.filter());
627 : 0 : recurse(op);
628 : 0 : }
629 : 0 : void operator()(const JoinOperator &op) override {
630 : 0 : (*this)(op.predicate());
631 : 0 : recurse(op);
632 : 0 : }
633 : 0 : void operator()(const ProjectionOperator &op) override {
634 [ # # ]: 0 : for (auto &p : op.projections())
635 : 0 : (*this)(p.first.get());
636 : 0 : recurse(op);
637 : 0 : }
638 : 0 : void operator()(const LimitOperator &op) override { recurse(op); }
639 : 0 : void operator()(const GroupingOperator &op) override {
640 [ # # ]: 0 : for (auto &[grp, alias] : op.group_by())
641 : 0 : (*this)(grp.get());
642 : 0 : recurse(op);
643 : 0 : }
644 : 0 : void operator()(const AggregationOperator &op) override { recurse(op); }
645 : 0 : void operator()(const SortingOperator &op) override { recurse(op); }
646 : :
647 : : /*----- CNF ------------------------------------------------------------------------------------------------------*/
648 : 0 : void operator()(const cnf::CNF &cnf) {
649 [ # # ]: 0 : for (auto &clause: cnf) {
650 [ # # ]: 0 : for (auto &pred: clause)
651 : 0 : (*this)(*pred);
652 : : }
653 : 0 : }
654 : :
655 : : /*----- Expr -----------------------------------------------------------------------------------------------------*/
656 : 0 : void operator()(const ast::ErrorExpr&) override { M_unreachable("no errors at this stage"); }
657 : 0 : void operator()(const ast::Designator&) override { /* nothing to be done */ }
658 : 0 : void operator()(const ast::Constant &e) override {
659 [ # # ]: 0 : if (e.is_string()) {
660 : 0 : auto s = Interpreter::eval(e);
661 : 0 : literals_.emplace(s.as<const char*>());
662 : 0 : }
663 : 0 : }
664 : 0 : void operator()(const ast::FnApplicationExpr&) override { /* nothing to be done */ } // XXX can string literals be arguments?
665 : 0 : void operator()(const ast::UnaryExpr &e) override { (*this)(*e.expr); }
666 : 0 : void operator()(const ast::BinaryExpr &e) override { (*this)(*e.lhs); (*this)(*e.rhs); }
667 : 0 : void operator()(const ast::QueryExpr&) override { /* nothing to be done */ }
668 : : };
669 : :
670 : : struct CollectTables : ConstOperatorVisitor
671 : : {
672 : : private:
673 : : struct hash
674 : : {
675 : 0 : uint64_t operator()(const std::reference_wrapper<const Table> &r) const {
676 : : StrHash h;
677 : 0 : return h((const char*)(r.get().name()));
678 : : }
679 : : };
680 : :
681 : : struct equal
682 : : {
683 : 0 : bool operator()(const std::reference_wrapper<const Table> &first,
684 : : const std::reference_wrapper<const Table> &second) const
685 : : {
686 : 0 : return first.get().name() == second.get().name();
687 : : }
688 : : };
689 : :
690 : :
691 : : std::unordered_set<std::reference_wrapper<const Table>, hash, equal> tables_; ///< the collected tables
692 : :
693 : : public:
694 : 0 : static std::vector<std::reference_wrapper<const Table>> Collect(const Operator &plan) {
695 : 0 : CollectTables CT;
696 [ # # ]: 0 : CT(plan);
697 [ # # ]: 0 : return { CT.tables_.begin(), CT.tables_.end() };
698 : 0 : }
699 : :
700 : : private:
701 : 0 : CollectTables() = default;
702 : :
703 : : using ConstOperatorVisitor::operator();
704 : :
705 : 0 : void recurse(const Consumer &C) {
706 [ # # ]: 0 : for (auto &c: C.children())
707 : 0 : (*this)(*c);
708 : 0 : }
709 : :
710 : 0 : void operator()(const ScanOperator &op) override { tables_.emplace(op.store().table()); }
711 : 0 : void operator()(const CallbackOperator &op) override { recurse(op); }
712 : 0 : void operator()(const PrintOperator &op) override { recurse(op); }
713 : 0 : void operator()(const NoOpOperator &op) override { recurse(op); }
714 : 0 : void operator()(const FilterOperator &op) override { recurse(op); }
715 : 0 : void operator()(const DisjunctiveFilterOperator &op) override { recurse(op); }
716 : 0 : void operator()(const JoinOperator &op) override { recurse(op); }
717 : 0 : void operator()(const ProjectionOperator &op) override { recurse(op); }
718 : 0 : void operator()(const LimitOperator &op) override { recurse(op); }
719 : 0 : void operator()(const GroupingOperator &op) override { recurse(op); }
720 : 0 : void operator()(const AggregationOperator &op) override { recurse(op); }
721 : 0 : void operator()(const SortingOperator &op) override { recurse(op); }
722 : : };
723 : :
724 : :
725 : : /*======================================================================================================================
726 : : * V8Engine implementation
727 : : *====================================================================================================================*/
728 : :
729 [ # # # # ]: 0 : V8Engine::V8Engine() { initialize(); }
730 : :
731 : 0 : V8Engine::~V8Engine()
732 : 0 : {
733 : 0 : inspector_.reset();
734 [ # # ]: 0 : if (isolate_) {
735 [ # # ]: 0 : M_insist(allocator_);
736 [ # # ]: 0 : isolate_->Dispose();
737 [ # # ]: 0 : delete allocator_;
738 : 0 : }
739 : 0 : }
740 : :
741 : 0 : void V8Engine::initialize()
742 : : {
743 : 0 : M_insist(not allocator_);
744 : 0 : M_insist(not isolate_);
745 : :
746 : : /*----- Set V8 flags. --------------------------------------------------------------------------------------------*/
747 : : /* A documentation of these flags can be found at
748 : : * https://chromium.googlesource.com/v8/v8/+/2c22fd50128ad130e9dba77fce828e5661559121/src/flags/flag-definitions.h.*/
749 : 0 : std::ostringstream flags;
750 [ # # ]: 0 : flags << "--stack_size 1000000 ";
751 [ # # ]: 0 : if (options::wasm_adaptive) {
752 [ # # ]: 0 : flags << "--opt "
753 [ # # ]: 0 : << "--liftoff "
754 [ # # ]: 0 : << "--wasm-tier-up "
755 [ # # ]: 0 : << "--wasm-dynamic-tiering "
756 [ # # ]: 0 : << "--wasm-lazy-compilation "; // compile code lazily at runtime if needed
757 : 0 : } else {
758 [ # # ]: 0 : flags << "--no-liftoff "
759 [ # # ]: 0 : << "--no-wasm-lazy-compilation "; // compile code before starting execution
760 : : }
761 [ # # ]: 0 : if (not options::wasm_compilation_cache) {
762 [ # # ]: 0 : flags << "--no-compilation-cache "
763 [ # # ]: 0 : << "--no-wasm-native-module-cache-enabled ";
764 : 0 : }
765 [ # # ]: 0 : if (options::asm_dump) {
766 [ # # ]: 0 : flags << "--code-comments " // include code comments
767 [ # # ]: 0 : << "--print-code ";
768 : 0 : }
769 [ # # ]: 0 : if (options::cdt_port >= 1024) {
770 [ # # ]: 0 : flags << "--wasm-bounds-checks "
771 [ # # ]: 0 : << "--wasm-stack-checks "
772 [ # # ]: 0 : << "--log "
773 [ # # ]: 0 : << "--log-all "
774 [ # # ]: 0 : << "--expose-wasm "
775 [ # # ]: 0 : << "--trace-wasm "
776 [ # # ]: 0 : << "--trace-wasm-instances "
777 [ # # ]: 0 : << "--prof ";
778 : 0 : } else {
779 [ # # ]: 0 : flags << "--no-wasm-bounds-checks "
780 [ # # ]: 0 : << "--no-wasm-stack-checks "
781 [ # # ]: 0 : << "--wasm-simd-ssse3-codegen ";
782 : : }
783 [ # # # # ]: 0 : v8::V8::SetFlagsFromString(flags.str().c_str());
784 : :
785 [ # # ]: 0 : v8::Isolate::CreateParams create_params;
786 [ # # ]: 0 : create_params.array_buffer_allocator = allocator_ = v8::ArrayBuffer::Allocator::NewDefaultAllocator();
787 [ # # ]: 0 : isolate_ = v8::Isolate::New(create_params);
788 : 0 : }
789 : :
790 : 0 : void V8Engine::compile(const m::MatchBase &plan) const
791 : : {
792 : : #if 1
793 : : /*----- Add print function. --------------------------------------------------------------------------------------*/
794 : 0 : Module::Get().emit_function_import<void(uint32_t)>("print");
795 : 0 : Module::Get().emit_function_import<void(uint32_t, uint32_t)>("print_memory_consumption");
796 : : #endif
797 : :
798 : : /*----- Emit code for run function which computes the last pipeline and calls other pipeline functions. ----------*/
799 [ # # # # : 0 : FUNCTION(run, void(void))
# # ]
800 : : {
801 [ # # # # ]: 0 : auto S = CodeGenContext::Get().scoped_environment(); // create scoped environment for this function
802 [ # # # # : 0 : plan.execute(setup_t::Make_Without_Parent(), pipeline_t(), teardown_t::Make_Without_Parent()); // emit code
# # ]
803 : 0 : }
804 : :
805 : : /*----- Create function `main` which executes the given query. ---------------------------------------------------*/
806 [ # # # # ]: 0 : m::wasm::Function<uint32_t(uint32_t)> main("main");
807 [ # # # # ]: 0 : BLOCK_OPEN(main.body())
808 : : {
809 [ # # # # ]: 0 : auto S = CodeGenContext::Get().scoped_environment(); // create scoped environment for this function
810 [ # # ]: 0 : run(); // call run function
811 [ # # # # ]: 0 : if (Options::Get().statistics) {
812 [ # # ]: 0 : std::cout << "Pre-allocated memory overall consumption: "
813 [ # # # # : 0 : << Module::Allocator().pre_allocated_memory_consumption() / (1024.0 * 1024.0)
# # ]
814 [ # # # # ]: 0 : << " MiB" << std::endl;
815 [ # # # # ]: 0 : Module::Get().emit_call<void>("print_memory_consumption",
816 [ # # # # ]: 0 : Module::Allocator().allocated_memory_consumption(),
817 [ # # # # ]: 0 : Module::Allocator().allocated_memory_peak());
818 : 0 : }
819 [ # # # # : 0 : main.emit_return(CodeGenContext::Get().num_tuples()); // return size of result set
# # ]
820 : 0 : }
821 : :
822 : : /*----- Export main. ---------------------------------------------------------------------------------------------*/
823 [ # # # # ]: 0 : Module::Get().emit_function_export("main");
824 : :
825 : : /*----- Dump the generated WebAssembly code ----------------------------------------------------------------------*/
826 [ # # ]: 0 : if (options::wasm_dump)
827 [ # # # # ]: 0 : Module::Get().dump_all(std::cout);
828 : :
829 : : #ifndef NDEBUG
830 : : /*----- Validate module before optimization. ---------------------------------------------------------------------*/
831 [ # # # # ]: 0 : if (not Module::Validate()) {
832 [ # # # # ]: 0 : Module::Get().dump_all();
833 [ # # ]: 0 : throw std::logic_error("invalid module");
834 : : }
835 : : #endif
836 : :
837 : : /*----- Optimize module. -----------------------------------------------------------------------------------------*/
838 : : #ifndef NDEBUG
839 [ # # ]: 0 : std::ostringstream dump_before_opt;
840 [ # # # # ]: 0 : Module::Get().dump(dump_before_opt);
841 : : #endif
842 [ # # ]: 0 : if (options::wasm_optimization_level)
843 [ # # ]: 0 : Module::Optimize(options::wasm_optimization_level);
844 : :
845 : : #ifndef NDEBUG
846 : : /*----- Validate module after optimization. ----------------------------------------------------------------------*/
847 [ # # # # : 0 : if (options::wasm_optimization_level and not Module::Validate()) {
# # ]
848 [ # # # # ]: 0 : std::cerr << "Module invalid after optimization!" << std::endl;
849 [ # # # # : 0 : std::cerr << "WebAssembly before optimization:\n" << dump_before_opt.str() << std::endl;
# # # # ]
850 [ # # ]: 0 : std::cerr << "WebAssembly after optimization:\n";
851 [ # # # # ]: 0 : Module::Get().dump(std::cerr);
852 [ # # ]: 0 : throw std::logic_error("invalid module");
853 : : }
854 : : #endif
855 : 0 : }
856 : :
857 : 0 : void V8Engine::execute(const m::MatchBase &plan)
858 : : {
859 : 0 : Catalog &C = Catalog::Get();
860 : :
861 : 0 : Module::Init();
862 : 0 : CodeGenContext::Init(); // fresh context
863 : :
864 : 0 : M_insist(bool(isolate_), "must have an isolate");
865 : 0 : v8::Locker locker(isolate_);
866 [ # # ]: 0 : isolate_->Enter();
867 : :
868 : : {
869 : : /* Create required V8 scopes. */
870 [ # # ]: 0 : v8::Isolate::Scope isolate_scope(isolate_);
871 [ # # ]: 0 : v8::HandleScope handle_scope(isolate_); // tracks and disposes of all object handles
872 : :
873 : : /* Create global template and context. */
874 [ # # # # ]: 0 : v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate_);
875 [ # # # # : 0 : global->Set(isolate_, "set_wasm_instance_raw_memory", v8::FunctionTemplate::New(isolate_, set_wasm_instance_raw_memory));
# # # # #
# # # ]
876 [ # # # # : 0 : global->Set(isolate_, "read_result_set", v8::FunctionTemplate::New(isolate_, read_result_set));
# # # # #
# # # ]
877 : :
878 : : #define CREATE_TEMPLATES(IDXTYPE, KEYTYPE, V8TYPE, IDXNAME, SUFFIX) \
879 : : global->Set(isolate_, M_STR(idx_lower_bound_##IDXNAME##_##SUFFIX), v8::FunctionTemplate::New(isolate_, index_seek<IDXTYPE<KEYTYPE>, V8TYPE, true>)); \
880 : : global->Set(isolate_, M_STR(idx_upper_bound_##IDXNAME##_##SUFFIX), v8::FunctionTemplate::New(isolate_, index_seek<IDXTYPE<KEYTYPE>, V8TYPE, false>)); \
881 : : global->Set(isolate_, M_STR(idx_scan_##IDXNAME##_##SUFFIX), v8::FunctionTemplate::New(isolate_, index_sequential_scan<IDXTYPE<KEYTYPE>>))
882 : :
883 [ # # # # : 0 : CREATE_TEMPLATES(idx::ArrayIndex, bool, v8::Boolean, array, b);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
884 [ # # # # : 0 : CREATE_TEMPLATES(idx::ArrayIndex, int8_t, v8::Int32, array, i1);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
885 [ # # # # : 0 : CREATE_TEMPLATES(idx::ArrayIndex, int16_t, v8::Int32, array, i2);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
886 [ # # # # : 0 : CREATE_TEMPLATES(idx::ArrayIndex, int32_t, v8::Int32, array, i4);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
887 [ # # # # : 0 : CREATE_TEMPLATES(idx::ArrayIndex, int64_t, v8::BigInt, array, i8);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
888 [ # # # # : 0 : CREATE_TEMPLATES(idx::ArrayIndex, float, v8::Number, array, f);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
889 [ # # # # : 0 : CREATE_TEMPLATES(idx::ArrayIndex, double, v8::Number, array, d);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
890 [ # # # # : 0 : CREATE_TEMPLATES(idx::ArrayIndex, const char*, v8::String, array, p);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
891 [ # # # # : 0 : CREATE_TEMPLATES(idx::RecursiveModelIndex, int8_t, v8::Int32, rmi, i1);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
892 [ # # # # : 0 : CREATE_TEMPLATES(idx::RecursiveModelIndex, int16_t, v8::Int32, rmi, i2);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
893 [ # # # # : 0 : CREATE_TEMPLATES(idx::RecursiveModelIndex, int32_t, v8::Int32, rmi, i4);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
894 [ # # # # : 0 : CREATE_TEMPLATES(idx::RecursiveModelIndex, int64_t, v8::BigInt, rmi, i8);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
895 [ # # # # : 0 : CREATE_TEMPLATES(idx::RecursiveModelIndex, float, v8::Number, rmi, f);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
896 [ # # # # : 0 : CREATE_TEMPLATES(idx::RecursiveModelIndex, double, v8::Number, rmi, d);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
897 : : #undef CREATE_TEMPLATES
898 : :
899 [ # # # # : 0 : v8::Local<v8::Context> context = v8::Context::New(isolate_, /* extensions= */ nullptr, global);
# # # # ]
900 [ # # ]: 0 : v8::Context::Scope context_scope(context);
901 : :
902 : : /* Create the import object for instantiating the WebAssembly module. */
903 : 0 : WasmContext::config_t wasm_config{0};
904 [ # # ]: 0 : if (options::cdt_port < 1024)
905 [ # # ]: 0 : wasm_config |= WasmContext::TRAP_GUARD_PAGES;
906 [ # # # # ]: 0 : auto &wasm_context = Create_Wasm_Context_For_ID(Module::ID(), plan, wasm_config);
907 : :
908 [ # # ]: 0 : auto imports = v8::Object::New(isolate_);
909 [ # # ]: 0 : auto env = create_env(*isolate_, plan);
910 : :
911 : : /* Map the remaining address space to the output buffer. */
912 [ # # # # ]: 0 : M_insist(Is_Page_Aligned(wasm_context.heap));
913 : 0 : const auto bytes_remaining = wasm_context.vm.size() - wasm_context.heap;
914 [ # # # # : 0 : memory::Memory mem = Catalog::Get().allocator().allocate(bytes_remaining);
# # ]
915 [ # # ]: 0 : mem.map(bytes_remaining, 0, wasm_context.vm, wasm_context.heap);
916 : :
917 [ # # # # : 0 : auto compile_time = C.timer().create_timing("Compile SQL to machine code");
# # ]
918 : : /* Compile the plan and thereby build the Wasm module. */
919 [ # # # # : 0 : M_TIME_EXPR(compile(plan), "|- Compile SQL to WebAssembly", C.timer());
# # # # ]
920 : : /* Perform memory pre-allocations and add allocation address initialization to env. */
921 [ # # # # ]: 0 : Module::Get().emit_import<uint32_t>("alloc_addr_init");
922 [ # # # # : 0 : M_DISCARD env->Set(isolate_->GetCurrentContext(), to_v8_string(isolate_, "alloc_addr_init"),
# # # # ]
923 [ # # # # : 0 : v8::Uint32::New(isolate_, Module::Allocator().perform_pre_allocations()));
# # # # ]
924 [ # # # # : 0 : M_DISCARD imports->Set(context, mkstr(*isolate_, "imports"), env);
# # # # #
# ]
925 : : /* Create a WebAssembly instance object. */
926 [ # # # # : 0 : auto instance = M_TIME_EXPR(instantiate(*isolate_, imports), " ` Compile WebAssembly to machine code", C.timer());
# # # # ]
927 [ # # ]: 0 : compile_time.stop();
928 : :
929 : : /* Set the underlying memory for the instance. */
930 [ # # # # ]: 0 : v8::SetWasmInstanceRawMemory(instance, wasm_context.vm.as<uint8_t*>(), wasm_context.vm.size());
931 : :
932 : : /* Get the exports of the created WebAssembly instance. */
933 [ # # # # : 0 : auto exports = instance->Get(context, mkstr(*isolate_, "exports")).ToLocalChecked().As<v8::Object>();
# # # # #
# # # ]
934 [ # # # # : 0 : auto main = exports->Get(context, mkstr(*isolate_, "main")).ToLocalChecked().As<v8::Function>();
# # # # #
# # # ]
935 : :
936 : : /* If a debugging port is specified, set up the inspector and start it. */
937 [ # # # # ]: 0 : if (options::cdt_port >= 1024 and not inspector_)
938 [ # # ]: 0 : inspector_ = std::make_unique<V8InspectorClientImpl>(options::cdt_port, isolate_);
939 [ # # ]: 0 : if (bool(inspector_)) {
940 [ # # ]: 0 : run_inspector(*inspector_, *isolate_, env);
941 : 0 : return;
942 : : }
943 : :
944 : : /* Invoke the exported function `main` of the module. */
945 [ # # # # ]: 0 : args_t args { v8::Int32::New(isolate_, wasm_context.id), };
946 : 0 : const uint32_t num_rows =
947 [ # # # # : 0 : M_TIME_EXPR(main->Call(context, context->Global(), 1, args).ToLocalChecked().As<v8::Uint32>()->Value(),
# # # # #
# # # # #
# # # # #
# ]
948 : : "Execute machine code", C.timer());
949 : :
950 : : /* Print total number of result tuples. */
951 [ # # ]: 0 : auto &root_op = plan.get_matched_root();
952 [ # # ]: 0 : if (auto print_op = cast<const PrintOperator>(&root_op)) {
953 [ # # # # ]: 0 : if (not Options::Get().quiet)
954 [ # # # # ]: 0 : print_op->out << num_rows << " rows\n";
955 [ # # # # ]: 0 : } else if (auto noop_op = cast<const NoOpOperator>(&root_op)) {
956 [ # # # # ]: 0 : if (not Options::Get().quiet)
957 [ # # # # ]: 0 : noop_op->out << num_rows << " rows\n";
958 : 0 : }
959 [ # # ]: 0 : Dispose_Wasm_Context(wasm_context);
960 [ # # ]: 0 : }
961 : :
962 [ # # ]: 0 : isolate_->Exit();
963 [ # # ]: 0 : CodeGenContext::Dispose();
964 [ # # ]: 0 : Module::Dispose();
965 [ # # ]: 0 : }
966 : :
967 : : __attribute__((constructor(101)))
968 : 1 : static void create_V8Engine()
969 : : {
970 [ + - ]: 1 : V8Engine::PLATFORM_ = v8::platform::NewDefaultPlatform().release();
971 : 1 : v8::V8::InitializePlatform(V8Engine::PLATFORM_);
972 : 1 : v8::V8::SetFlagsFromString("--no-freeze-flags-after-init"); // allow changing flags after initialization
973 : 1 : v8::V8::Initialize();
974 : 1 : }
975 : :
976 : : __attribute__((destructor(101)))
977 : 1 : static void destroy_V8Engine()
978 : : {
979 : 1 : v8::V8::Dispose();
980 : 1 : v8::V8::DisposePlatform();
981 : 1 : }
982 : :
983 : : __attribute__((constructor(202)))
984 : 1 : static void register_WasmV8()
985 : : {
986 : 1 : Catalog &C = Catalog::Get();
987 [ + - ]: 1 : C.register_wasm_backend<V8Engine>(C.pool("WasmV8"), "WebAssembly backend using Google's V8 engine");
988 : :
989 : : /*----- Command-line arguments -----------------------------------------------------------------------------------*/
990 : 1 : C.arg_parser().add<int>(
991 : : /* group= */ "Wasm",
992 : : /* short= */ nullptr,
993 : : /* long= */ "--wasm-opt",
994 : : /* description= */ "set the optimization level for Wasm modules (0, 1, or 2)",
995 : 0 : [] (int i) { options::wasm_optimization_level = i; }
996 : : );
997 : 1 : C.arg_parser().add<bool>(
998 : : /* group= */ "WasmV8",
999 : : /* short= */ nullptr,
1000 : : /* long= */ "--wasm-adaptive",
1001 : : /* description= */ "enable adaptive execution of Wasm with Liftoff and dynamic tier-up",
1002 : 0 : [] (bool b) { options::wasm_adaptive = b; }
1003 : : );
1004 : 1 : C.arg_parser().add<bool>(
1005 : : /* group= */ "WasmV8",
1006 : : /* short= */ nullptr,
1007 : : /* long= */ "--no-wasm-compilation-cache",
1008 : : /* description= */ "disable V8's compilation cache",
1009 : 0 : [] (bool) { options::wasm_compilation_cache = false; }
1010 : : );
1011 : 1 : C.arg_parser().add<bool>(
1012 : : /* group= */ "Wasm",
1013 : : /* short= */ nullptr,
1014 : : /* long= */ "--wasm-dump",
1015 : : /* description= */ "dump the generated WebAssembly code to stdout",
1016 : 0 : [] (bool b) { options::wasm_dump = b; }
1017 : : );
1018 : 1 : C.arg_parser().add<bool>(
1019 : : /* group= */ "WasmV8",
1020 : : /* short= */ nullptr,
1021 : : /* long= */ "--asm-dump",
1022 : : /* description= */ "dump the generated assembly code to stdout",
1023 : 0 : [] (bool b) { options::asm_dump = b; }
1024 : : );
1025 : 1 : C.arg_parser().add<int>(
1026 : : /* group= */ "WasmV8",
1027 : : /* short= */ nullptr,
1028 : : /* long= */ "--CDT",
1029 : : /* description= */ "specify the port for debugging via ChromeDevTools",
1030 : 0 : [] (int i) { options::cdt_port = i; }
1031 : : );
1032 : 1 : }
1033 : :
1034 : : }
1035 : :
1036 : :
1037 : : /*======================================================================================================================
1038 : : * Implementation of helper methods
1039 : : *====================================================================================================================*/
1040 : :
1041 : 8270 : v8::Local<v8::String> m::wasm::detail::mkstr(v8::Isolate &isolate, const std::string &str)
1042 : : {
1043 : 8270 : return to_v8_string(&isolate, str);
1044 : : }
1045 : :
1046 : 827 : v8::Local<v8::WasmModuleObject> m::wasm::detail::instantiate(v8::Isolate &isolate, v8::Local<v8::Object> imports)
1047 : : {
1048 : 827 : auto Ctx = isolate.GetCurrentContext();
1049 : 2481 : auto [binary_addr, binary_size] = Module::Get().binary();
1050 : 827 : auto bs = v8::ArrayBuffer::NewBackingStore(
1051 : 827 : /* data = */ binary_addr,
1052 : 827 : /* byte_length= */ binary_size,
1053 : : /* deleter= */ v8::BackingStore::EmptyDeleter,
1054 : : /* deleter_data= */ nullptr
1055 : : );
1056 [ + - + - ]: 827 : auto buffer = v8::ArrayBuffer::New(&isolate, std::move(bs));
1057 : :
1058 [ + - - + ]: 827 : if (Options::Get().statistics)
1059 [ # # # # : 0 : std::cout << "Wasm code size: " << binary_size << std::endl;
# # ]
1060 : :
1061 [ + - ]: 827 : args_t module_args { buffer };
1062 [ + - + - : 827 : auto wasm = Ctx->Global()->Get(Ctx, mkstr(isolate, "WebAssembly")).ToLocalChecked().As<v8::Object>(); // WebAssembly class
+ - + - +
- + - + -
+ - + - ]
1063 [ + - + - : 1654 : auto wasm_module = wasm->Get(Ctx, mkstr(isolate, "Module")).ToLocalChecked().As<v8::Object>()
+ - + - +
- + - + -
+ - ]
1064 [ + - + - : 827 : ->CallAsConstructor(Ctx, 1, module_args).ToLocalChecked().As<v8::WasmModuleObject>();
+ - ]
1065 : 827 : free(binary_addr);
1066 : :
1067 [ + - - + ]: 827 : if (Options::Get().statistics)
1068 [ # # # # : 0 : std::cout << "Machine code size: " << wasm_module->GetCompiledModule().Serialize().size << std::endl;
# # # # #
# # # ]
1069 : :
1070 [ + - + - ]: 827 : args_t instance_args { wasm_module, imports };
1071 [ + - + - : 1654 : return wasm->Get(Ctx, mkstr(isolate, "Instance")).ToLocalChecked().As<v8::Object>()
+ - + - +
- + - + -
+ - ]
1072 [ + - + - : 827 : ->CallAsConstructor(Ctx, 2, instance_args).ToLocalChecked().As<v8::WasmModuleObject>();
+ - ]
1073 : 827 : }
1074 : :
1075 : 0 : v8::Local<v8::Object> m::wasm::detail::create_env(v8::Isolate &isolate, const m::MatchBase &plan)
1076 : : {
1077 : 0 : auto &context = WasmEngine::Get_Wasm_Context_By_ID(Module::ID());
1078 : 0 : auto Ctx = isolate.GetCurrentContext();
1079 : 0 : auto env = v8::Object::New(&isolate);
1080 : :
1081 : : /* Map accessed tables into the Wasm module. */
1082 : 0 : auto tables = CollectTables::Collect(plan.get_matched_root());
1083 [ # # ]: 0 : for (auto &table : tables) {
1084 [ # # ]: 0 : auto off = context.map_table(table.get());
1085 : :
1086 : : /* Add memory address to env. */
1087 [ # # ]: 0 : std::ostringstream oss;
1088 [ # # # # : 0 : oss << table.get().name() << "_mem";
# # ]
1089 [ # # # # : 0 : M_DISCARD env->Set(Ctx, to_v8_string(&isolate, oss.str()), v8::Int32::New(&isolate, off));
# # # # #
# # # #
# ]
1090 [ # # # # : 0 : Module::Get().emit_import<void*>(oss.str().c_str());
# # ]
1091 : :
1092 : : /* Add table size (num_rows) to env. */
1093 [ # # # # ]: 0 : oss.str("");
1094 [ # # # # : 0 : oss << table.get().name() << "_num_rows";
# # ]
1095 [ # # # # : 0 : M_DISCARD env->Set(Ctx, to_v8_string(&isolate, oss.str()), v8::Int32::New(&isolate, table.get().store().num_rows()));
# # # # #
# # # # #
# # # # ]
1096 [ # # # # : 0 : Module::Get().emit_import<uint32_t>(oss.str().c_str());
# # ]
1097 : 0 : }
1098 : :
1099 : : /* Map all string literals into the Wasm module. */
1100 [ # # # # ]: 0 : M_insist(Is_Page_Aligned(context.heap));
1101 [ # # # # ]: 0 : auto literals = CollectStringLiterals::Collect(plan.get_matched_root());
1102 : 0 : std::size_t bytes = 0;
1103 [ # # ]: 0 : for (auto literal : literals)
1104 : 0 : bytes += strlen(literal) + 1;
1105 [ # # ]: 0 : auto aligned_bytes = Ceil_To_Next_Page(bytes);
1106 [ # # ]: 0 : if (aligned_bytes) {
1107 [ # # ]: 0 : auto base_addr = context.vm.as<uint8_t*>() + context.heap;
1108 : 0 : M_DISCARD mmap(base_addr, aligned_bytes, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_ANON|MAP_PRIVATE, -1, 0);
1109 : 0 : char *start_addr = reinterpret_cast<char*>(base_addr);
1110 : 0 : char *dst = start_addr;
1111 [ # # ]: 0 : for (auto literal : literals) {
1112 [ # # # # ]: 0 : CodeGenContext::Get().add_literal(literal, context.heap + (dst - start_addr)); // add literal
1113 : 0 : dst = stpcpy(dst, literal) + 1; // copy into sequential memory
1114 : : }
1115 : 0 : context.heap += aligned_bytes;
1116 [ # # ]: 0 : context.install_guard_page();
1117 : 0 : }
1118 [ # # # # ]: 0 : M_insist(Is_Page_Aligned(context.heap));
1119 : :
1120 : : /* Add functions to environment. */
1121 [ # # # # ]: 0 : Module::Get().emit_function_import<void(void*,uint32_t)>("read_result_set");
1122 : :
1123 : : #define EMIT_FUNC_IMPORTS(KEYTYPE, IDXNAME, SUFFIX) \
1124 : : Module::Get().emit_function_import<uint32_t(std::size_t,KEYTYPE)>(M_STR(idx_lower_bound_##IDXNAME##_##SUFFIX)); \
1125 : : Module::Get().emit_function_import<uint32_t(std::size_t,KEYTYPE)>(M_STR(idx_upper_bound_##IDXNAME##_##SUFFIX)); \
1126 : : Module::Get().emit_function_import<void(std::size_t,uint32_t,void*,uint32_t)>(M_STR(idx_scan_##IDXNAME##_##SUFFIX))
1127 : :
1128 [ # # # # : 0 : EMIT_FUNC_IMPORTS(bool, array, b);
# # # # #
# # # ]
1129 [ # # # # : 0 : EMIT_FUNC_IMPORTS(int8_t, array, i1);
# # # # #
# # # ]
1130 [ # # # # : 0 : EMIT_FUNC_IMPORTS(int16_t, array, i2);
# # # # #
# # # ]
1131 [ # # # # : 0 : EMIT_FUNC_IMPORTS(int32_t, array, i4);
# # # # #
# # # ]
1132 [ # # # # : 0 : EMIT_FUNC_IMPORTS(int64_t, array, i8);
# # # # #
# # # ]
1133 [ # # # # : 0 : EMIT_FUNC_IMPORTS(float, array, f);
# # # # #
# # # ]
1134 [ # # # # : 0 : EMIT_FUNC_IMPORTS(double, array, d);
# # # # #
# # # ]
1135 [ # # # # : 0 : EMIT_FUNC_IMPORTS(const char*, array, p);
# # # # #
# # # ]
1136 [ # # # # : 0 : EMIT_FUNC_IMPORTS(int8_t, rmi, i1);
# # # # #
# # # ]
1137 [ # # # # : 0 : EMIT_FUNC_IMPORTS(int16_t, rmi, i2);
# # # # #
# # # ]
1138 [ # # # # : 0 : EMIT_FUNC_IMPORTS(int32_t, rmi, i4);
# # # # #
# # # ]
1139 [ # # # # : 0 : EMIT_FUNC_IMPORTS(int64_t, rmi, i8);
# # # # #
# # # ]
1140 [ # # # # : 0 : EMIT_FUNC_IMPORTS(float, rmi, f);
# # # # #
# # # ]
1141 [ # # # # : 0 : EMIT_FUNC_IMPORTS(double, rmi, d);
# # # # #
# # # ]
1142 : : #undef EMIT_FUNC_IMPORTS
1143 : :
1144 : : #define ADD_FUNC(FUNC, NAME) { \
1145 : : auto func = v8::Function::New(Ctx, (FUNC)).ToLocalChecked(); \
1146 : : env->Set(Ctx, mkstr(isolate, NAME), func).Check(); \
1147 : : }
1148 : : #define ADD_FUNC_(FUNC) ADD_FUNC(FUNC, #FUNC)
1149 [ # # # # : 0 : ADD_FUNC_(insist)
# # # # #
# # # # #
# # # # #
# ]
1150 [ # # # # : 0 : ADD_FUNC_(print)
# # # # #
# # # # #
# # # # #
# ]
1151 [ # # # # : 0 : ADD_FUNC_(print_memory_consumption)
# # # # #
# # # # #
# # # # #
# ]
1152 [ # # # # : 0 : ADD_FUNC_(read_result_set)
# # # # #
# # # # #
# # # # #
# ]
1153 [ # # # # : 0 : ADD_FUNC(_throw, "throw")
# # # # #
# # # # #
# # # # #
# ]
1154 : :
1155 : : #define ADD_FUNCS(IDXTYPE, KEYTYPE, V8TYPE, IDXNAME, SUFFIX) \
1156 : : ADD_FUNC(index_seek<M_COMMA(IDXTYPE<KEYTYPE>) M_COMMA(V8TYPE) true>, M_STR(idx_lower_bound_##IDXNAME##_##SUFFIX)) \
1157 : : ADD_FUNC(index_seek<M_COMMA(IDXTYPE<KEYTYPE>) M_COMMA(V8TYPE) false>, M_STR(idx_upper_bound_##IDXNAME##_##SUFFIX)) \
1158 : : ADD_FUNC(index_sequential_scan<IDXTYPE<KEYTYPE>>, M_STR(idx_scan_##IDXNAME##_##SUFFIX))
1159 : :
1160 [ # # # # : 0 : ADD_FUNCS(idx::ArrayIndex, bool, v8::Boolean, array, b);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
1161 [ # # # # : 0 : ADD_FUNCS(idx::ArrayIndex, int8_t, v8::Int32, array, i1);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
1162 [ # # # # : 0 : ADD_FUNCS(idx::ArrayIndex, int16_t, v8::Int32, array, i2);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
1163 [ # # # # : 0 : ADD_FUNCS(idx::ArrayIndex, int32_t, v8::Int32, array, i4);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
1164 [ # # # # : 0 : ADD_FUNCS(idx::ArrayIndex, int64_t, v8::BigInt, array, i8);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
1165 [ # # # # : 0 : ADD_FUNCS(idx::ArrayIndex, float, v8::Number, array, f);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
1166 [ # # # # : 0 : ADD_FUNCS(idx::ArrayIndex, double, v8::Number, array, d);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
1167 [ # # # # : 0 : ADD_FUNCS(idx::ArrayIndex, const char*, v8::String, array, p);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
1168 [ # # # # : 0 : ADD_FUNCS(idx::RecursiveModelIndex, int8_t, v8::Int32, rmi, i1);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
1169 [ # # # # : 0 : ADD_FUNCS(idx::RecursiveModelIndex, int16_t, v8::Int32, rmi, i2);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
1170 [ # # # # : 0 : ADD_FUNCS(idx::RecursiveModelIndex, int32_t, v8::Int32, rmi, i4);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
1171 [ # # # # : 0 : ADD_FUNCS(idx::RecursiveModelIndex, int64_t, v8::BigInt, rmi, i8);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
1172 [ # # # # : 0 : ADD_FUNCS(idx::RecursiveModelIndex, float, v8::Number, rmi, f);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
1173 [ # # # # : 0 : ADD_FUNCS(idx::RecursiveModelIndex, double, v8::Number, rmi, d);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
1174 : : #undef ADD_FUNCS
1175 : : #undef ADD_FUNC_
1176 : : #undef ADD_FUNC
1177 : :
1178 : : return env;
1179 : 0 : }
1180 : :
1181 : 0 : v8::Local<v8::String> m::wasm::detail::to_json(v8::Isolate &isolate, v8::Local<v8::Value> val)
1182 : : {
1183 : 0 : M_insist(&isolate);
1184 : 0 : auto Ctx = isolate.GetCurrentContext();
1185 : 0 : return v8::JSON::Stringify(Ctx, val).ToLocalChecked();
1186 : : }
1187 : :
1188 : 0 : std::string m::wasm::detail::create_js_debug_script(v8::Isolate &isolate, v8::Local<v8::Object> env,
1189 : : const WasmEngine::WasmContext &wasm_context)
1190 : : {
1191 : 0 : std::ostringstream oss;
1192 : :
1193 [ # # # # ]: 0 : auto [binary_addr, binary_size] = Module::Get().binary();
1194 : :
1195 [ # # # # ]: 0 : auto json = to_json(isolate, env);
1196 [ # # # # : 0 : std::string env_str = *v8::String::Utf8Value(&isolate, json);
# # # # ]
1197 [ # # # # : 0 : if (env_str != "{}") env_str.insert(env_str.length() - 1, ",");
# # ]
1198 [ # # ]: 0 : env_str.insert(env_str.length() - 1, "\"insist\": function (arg) { assert(arg); },");
1199 [ # # ]: 0 : env_str.insert(env_str.length() - 1, "\"print\": function (arg) { console.log(arg); },");
1200 [ # # ]: 0 : env_str.insert(env_str.length() - 1, "\"throw\": function (ex) { console.error(ex); },");
1201 [ # # ]: 0 : env_str.insert(env_str.length() - 1, "\"read_result_set\": read_result_set,");
1202 : :
1203 : : /* Construct import object. */
1204 [ # # ]: 0 : oss << "\
1205 [ # # # # ]: 0 : let importObject = { \"imports\": " << env_str << " };\n\
1206 : : const bytes = Uint8Array.from([";
1207 [ # # ]: 0 : for (auto p = binary_addr, end = p + binary_size; p != end; ++p) {
1208 [ # # # # : 0 : if (p != binary_addr) oss << ", ";
# # ]
1209 [ # # ]: 0 : oss << unsigned(*p);
1210 : 0 : }
1211 : 0 : free(binary_addr);
1212 : : /* Emit code to instantiate module and invoke exported `run()` function. */
1213 [ # # ]: 0 : oss << "]);\n\
1214 : : WebAssembly.compile(bytes).then(\n\
1215 : : (module) => WebAssembly.instantiate(module, importObject),\n\
1216 : : (error) => console.error(`An error occurred during module compilation: ${error}`)\n\
1217 : : ).then(\n\
1218 : : function(instance) {\n\
1219 [ # # # # ]: 0 : set_wasm_instance_raw_memory(instance, " << wasm_context.id << ");\n\
1220 : : const num_tuples = instance.exports.main();\n\
1221 : : console.log('The result set contains %i tuples.', num_tuples);\n\
1222 : : debugger;\n\
1223 : : },\n\
1224 : : (error) => console.error(`An error occurred during module instantiation: ${error}`)\n\
1225 : : );\n\
1226 : : debugger;";
1227 : :
1228 : : /* Create a new temporary file. */
1229 : 0 : const char *name = "query.js";
1230 [ # # ]: 0 : std::ofstream file(name, std::ios_base::out);
1231 [ # # # # ]: 0 : if (not file)
1232 [ # # # # : 0 : throw runtime_error("I/O error");
# # ]
1233 [ # # # # : 0 : std::cerr << "Creating debug JS script " << name << std::endl;
# # ]
1234 : :
1235 : : /* Write the JS code to instantiate the module and invoke `run()` to the temporary file. */
1236 [ # # # # ]: 0 : file << oss.str();
1237 [ # # # # ]: 0 : if (not file)
1238 [ # # # # : 0 : throw runtime_error("I/O error");
# # ]
1239 [ # # ]: 0 : file.flush();
1240 [ # # # # ]: 0 : if (not file)
1241 [ # # # # : 0 : throw runtime_error("I/O error");
# # ]
1242 : :
1243 : : /* Return the name of the temporary file. */
1244 [ # # ]: 0 : return std::string(name);
1245 : 0 : }
1246 : :
1247 : 0 : void m::wasm::detail::run_inspector(V8InspectorClientImpl &inspector, v8::Isolate &isolate, v8::Local<v8::Object> env)
1248 : : {
1249 : 0 : auto Ctx = isolate.GetCurrentContext();
1250 : 0 : auto &wasm_context = WasmEngine::Get_Wasm_Context_By_ID(Module::ID());
1251 : :
1252 : 0 : inspector.register_context(Ctx);
1253 [ # # ]: 0 : inspector.start([&]() {
1254 : : /* Create JS script file that instantiates the Wasm module and invokes `main()`. */
1255 : 0 : auto filename = create_js_debug_script(isolate, env, wasm_context);
1256 : : /* Create a `v8::Script` for that JS file. */
1257 [ # # ]: 0 : std::ifstream js_in(filename);
1258 [ # # ]: 0 : std::string js(std::istreambuf_iterator<char>(js_in), std::istreambuf_iterator<char>{});
1259 [ # # ]: 0 : v8::Local<v8::String> js_src = mkstr(isolate, js);
1260 [ # # # # ]: 0 : std::string path = std::string("file://./") + filename;
1261 [ # # # # : 0 : v8::ScriptOrigin js_origin = v8::ScriptOrigin(&isolate, mkstr(isolate, path));
# # # # #
# ]
1262 [ # # ]: 0 : auto script = v8::Script::Compile(Ctx, js_src, &js_origin);
1263 [ # # # # ]: 0 : if (script.IsEmpty())
1264 [ # # ]: 0 : throw std::runtime_error("failed to compile script");
1265 : : /* Execute the `v8::Script`. */
1266 [ # # # # : 0 : auto result = script.ToLocalChecked()->Run(Ctx);
# # ]
1267 [ # # # # ]: 0 : if (result.IsEmpty())
1268 [ # # ]: 0 : throw std::runtime_error("execution failed");
1269 : 0 : });
1270 : 0 : inspector.deregister_context(Ctx);
1271 : 0 : }
|