LCOV - code coverage report
Current view: top level - src/backend - V8Engine.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 59 776 7.6 %
Date: 2025-03-25 01:19:55 Functions: 20 185 10.8 %
Branches: 47 2919 1.6 %

           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 : }

Generated by: LCOV version 1.16