text: // Copyright 2018 the Deno authors. All rights reserved. MIT license.
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <iostream>
  #include <string>
  
  #include "third_party/v8/include/v8.h"
  #include "third_party/v8/src/base/logging.h"
  
  #include "deno.h"
  #include "internal.h"
  
  #define GLOBAL_IMPORT_BUF_SIZE 1024
  
  namespace deno {
  
  std::vector<InternalFieldData*> deserialized_data;
  
  void DeserializeInternalFields(v8::Local<v8::Object> holder, int index,
                                 v8::StartupData payload, void* data) {
    DCHECK_EQ(data, nullptr);
    if (payload.raw_size == 0) {
      holder->SetAlignedPointerInInternalField(index, nullptr);
      return;
    }
    InternalFieldData* embedder_field = new InternalFieldData{0};
    memcpy(embedder_field, payload.data, payload.raw_size);
    holder->SetAlignedPointerInInternalField(index, embedder_field);
    deserialized_data.push_back(embedder_field);
  }
  
  v8::StartupData SerializeInternalFields(v8::Local<v8::Object> holder, int index,
                                          void* data) {
    DCHECK_EQ(data, nullptr);
    InternalFieldData* embedder_field = static_cast<InternalFieldData*>(
        holder->GetAlignedPointerFromInternalField(index));
    if (embedder_field == nullptr) return {nullptr, 0};
    int size = sizeof(*embedder_field);
    char* payload = new char[size];
    // We simply use memcpy to serialize the content.
    memcpy(payload, embedder_field, size);
    return {payload, size};
  }
  
  DenoIsolate* FromIsolate(v8::Isolate* isolate) {
    return static_cast<DenoIsolate*>(isolate->GetData(0));
  }
  
  void AddDataRef(DenoIsolate* d, int32_t req_id, v8::Local<v8::Value> data_v) {
    d->async_data_map_.emplace(std::piecewise_construct, std::make_tuple(req_id),
                               std::make_tuple(d->isolate_, data_v));
  }
  
  void DeleteDataRef(DenoIsolate* d, int32_t req_id) {
    // Delete persistent reference to data ArrayBuffer.
    auto it = d->async_data_map_.find(req_id);
    if (it != d->async_data_map_.end()) {
      it->second.Reset();
      d->async_data_map_.erase(it);
    }
  }
  
  // Extracts a C string from a v8::V8 Utf8Value.
  const char* ToCString(const v8::String::Utf8Value& value) {
    return *value ? *value : "<string conversion failed>";
  }
  
  std::string EncodeExceptionAsJSON(v8::Local<v8::Context> context,
                                    v8::Local<v8::Value> exception) {
    auto* isolate = context->GetIsolate();
    v8::HandleScope handle_scope(isolate);
    v8::Context::Scope context_scope(context);
  
    auto message = v8::Exception::CreateMessage(isolate, exception);
    auto stack_trace = message->GetStackTrace();
  
    // Encode the exception into a JS object, which we will then turn into JSON.
    auto json_obj = v8::Object::New(isolate);
  
    auto exception_str = exception->ToString(context).ToLocalChecked();
    // Alternate and very similar string. Not sure which is appropriate.
    // auto exception_str = message->Get();
    CHECK(json_obj->Set(context, v8_str("message"), exception_str).FromJust());
  
    auto maybe_source_line = message->GetSourceLine(context);
    if (!maybe_source_line.IsEmpty()) {
      CHECK(json_obj
                ->Set(context, v8_str("sourceLine"),
                      maybe_source_line.ToLocalChecked())
                .FromJust());
    }
  
    CHECK(json_obj
              ->Set(context, v8_str("scriptResourceName"),
                    message->GetScriptResourceName())
              .FromJust());
  
    auto maybe_line_number = message->GetLineNumber(context);
    if (maybe_line_number.IsJust()) {
      CHECK(json_obj
                ->Set(context, v8_str("lineNumber"),
                      v8::Integer::New(isolate, maybe_line_number.FromJust()))
                .FromJust());
    }
  
    CHECK(json_obj
              ->Set(context, v8_str("startPosition"),
                    v8::Integer::New(isolate, message->GetStartPosition()))
              .FromJust());
  
    CHECK(json_obj
              ->Set(context, v8_str("endPosition"),
                    v8::Integer::New(isolate, message->GetEndPosition()))
              .FromJust());
  
    CHECK(json_obj
              ->Set(context, v8_str("errorLevel"),
                    v8::Integer::New(isolate, message->ErrorLevel()))
              .FromJust());
  
    auto maybe_start_column = message->GetStartColumn(context);
    if (maybe_start_column.IsJust()) {
      auto start_column =
          v8::Integer::New(isolate, maybe_start_column.FromJust());
      CHECK(
          json_obj->Set(context, v8_str("startColumn"), start_column).FromJust());
    }
  
    auto maybe_end_column = message->GetEndColumn(context);
    if (maybe_end_column.IsJust()) {
      auto end_column = v8::Integer::New(isolate, maybe_end_column.FromJust());
      CHECK(json_obj->Set(context, v8_str("endColumn"), end_column).FromJust());
    }
  
    CHECK(json_obj
              ->Set(context, v8_str("isSharedCrossOrigin"),
                    v8::Boolean::New(isolate, message->IsSharedCrossOrigin()))
              .FromJust());
  
    CHECK(json_obj
              ->Set(context, v8_str("isOpaque"),
                    v8::Boolean::New(isolate, message->IsOpaque()))
              .FromJust());
  
    v8::Local<v8::Array> frames;
    if (!stack_trace.IsEmpty()) {
      uint32_t count = static_cast<uint32_t>(stack_trace->GetFrameCount());
      frames = v8::Array::New(isolate, count);
  
      for (uint32_t i = 0; i < count; ++i) {
        auto frame = stack_trace->GetFrame(isolate, i);
        auto frame_obj = v8::Object::New(isolate);
        CHECK(frames->Set(context, i, frame_obj).FromJust());
        auto line = v8::Integer::New(isolate, frame->GetLineNumber());
        auto column = v8::Integer::New(isolate, frame->GetColumn());
        CHECK(frame_obj->Set(context, v8_str("line"), line).FromJust());
        CHECK(frame_obj->Set(context, v8_str("column"), column).FromJust());
        CHECK(frame_obj
                  ->Set(context, v8_str("functionName"), frame->GetFunctionName())
                  .FromJust());
        // scriptName can be empty in special conditions e.g. eval
        auto scriptName = frame->GetScriptNameOrSourceURL();
        if (scriptName.IsEmpty()) {
          scriptName = v8_str("<unknown>");
        }
        CHECK(
            frame_obj->Set(context, v8_str("scriptName"), scriptName).FromJust());
        CHECK(frame_obj
                  ->Set(context, v8_str("isEval"),
                        v8::Boolean::New(isolate, frame->IsEval()))
                  .FromJust());
        CHECK(frame_obj
                  ->Set(context, v8_str("isConstructor"),
                        v8::Boolean::New(isolate, frame->IsConstructor()))
                  .FromJust());
        CHECK(frame_obj
                  ->Set(context, v8_str("isWasm"),
                        v8::Boolean::New(isolate, frame->IsWasm()))
                  .FromJust());
      }
    } else {
      // No stack trace. We only have one stack frame of info..
      frames = v8::Array::New(isolate, 1);
  
      auto frame_obj = v8::Object::New(isolate);
      CHECK(frames->Set(context, 0, frame_obj).FromJust());
  
      auto line =
          v8::Integer::New(isolate, message->GetLineNumber(context).FromJust());
      auto column =
          v8::Integer::New(isolate, message->GetStartColumn(context).FromJust());
  
      CHECK(frame_obj->Set(context, v8_str("line"), line).FromJust());
      CHECK(frame_obj->Set(context, v8_str("column"), column).FromJust());
      CHECK(frame_obj
                ->Set(context, v8_str("scriptName"),
                      message->GetScriptResourceName())
                .FromJust());
    }
  
    CHECK(json_obj->Set(context, v8_str("frames"), frames).FromJust());
  
    auto json_string = v8::JSON::Stringify(context, json_obj).ToLocalChecked();
    v8::String::Utf8Value json_string_(isolate, json_string);
    return std::string(ToCString(json_string_));
  }
  
  void HandleException(v8::Local<v8::Context> context,
                       v8::Local<v8::Value> exception) {
    v8::Isolate* isolate = context->GetIsolate();
    DenoIsolate* d = FromIsolate(isolate);
    std::string json_str = EncodeExceptionAsJSON(context, exception);
    CHECK(d != nullptr);
    d->last_exception_ = json_str;
  }
  
  void PromiseRejectCallback(v8::PromiseRejectMessage promise_reject_message) {
    auto* isolate = v8::Isolate::GetCurrent();
    DenoIsolate* d = static_cast<DenoIsolate*>(isolate->GetData(0));
    DCHECK_EQ(d->isolate_, isolate);
    v8::HandleScope handle_scope(d->isolate_);
    auto error = promise_reject_message.GetValue();
    auto context = d->context_.Get(d->isolate_);
    auto promise = promise_reject_message.GetPromise();
  
    v8::Context::Scope context_scope(context);
  
    int promise_id = promise->GetIdentityHash();
    switch (promise_reject_message.GetEvent()) {
      case v8::kPromiseRejectWithNoHandler:
        // Insert the error into the pending_promise_map_ using the promise's id
        // as the key.
        d->pending_promise_map_.emplace(std::piecewise_construct,
                                        std::make_tuple(promise_id),
                                        std::make_tuple(d->isolate_, error));
        break;
  
      case v8::kPromiseHandlerAddedAfterReject:
        d->pending_promise_map_.erase(promise_id);
        break;
  
      case v8::kPromiseRejectAfterResolved:
        break;
  
      case v8::kPromiseResolveAfterResolved:
        // Should not warn. See #1272
        break;
  
      default:
        CHECK(false && "unreachable");
    }
  }
  
  void Print(const v8::FunctionCallbackInfo<v8::Value>& args) {
    CHECK_GE(args.Length(), 1);
    CHECK_LE(args.Length(), 3);
    auto* isolate = args.GetIsolate();
    DenoIsolate* d = FromIsolate(isolate);
    auto context = d->context_.Get(d->isolate_);
    v8::HandleScope handle_scope(isolate);
    v8::String::Utf8Value str(isolate, args[0]);
    bool is_err =
        args.Length() >= 2 ? args[1]->BooleanValue(context).ToChecked() : false;
    bool prints_newline =
        args.Length() >= 3 ? args[2]->BooleanValue(context).ToChecked() : true;
    FILE* file = is_err ? stderr : stdout;
    fwrite(*str, sizeof(**str), str.length(), file);
    if (prints_newline) {
      fprintf(file, "\n");
    }
    fflush(file);
  }
  
  v8::Local<v8::Uint8Array> ImportBuf(DenoIsolate* d, deno_buf buf) {
    if (buf.alloc_ptr == nullptr) {
      // If alloc_ptr isn't set, we memcpy.
      // This is currently used for flatbuffers created in Rust.
  
      // To avoid excessively allocating new ArrayBuffers, we try to reuse a
      // single global ArrayBuffer. The caveat is that users must extract data
      // from it before the next tick. We only do this for ArrayBuffers less than
      // 1024 bytes.
      v8::Local<v8::ArrayBuffer> ab;
      void* data;
      if (buf.data_len > GLOBAL_IMPORT_BUF_SIZE) {
        // Simple case. We allocate a new ArrayBuffer for this.
        ab = v8::ArrayBuffer::New(d->isolate_, buf.data_len);
        data = ab->GetContents().Data();
      } else {
        // Fast case. We reuse the global ArrayBuffer.
        if (d->global_import_buf_.IsEmpty()) {
          // Lazily initialize it.
          DCHECK_EQ(d->global_import_buf_ptr_, nullptr);
          ab = v8::ArrayBuffer::New(d->isolate_, GLOBAL_IMPORT_BUF_SIZE);
          d->global_import_buf_.Reset(d->isolate_, ab);
          d->global_import_buf_ptr_ = ab->GetContents().Data();
        } else {
          DCHECK(d->global_import_buf_ptr_);
          ab = d->global_import_buf_.Get(d->isolate_);
        }
        data = d->global_import_buf_ptr_;
      }
      memcpy(data, buf.data_ptr, buf.data_len);
      auto view = v8::Uint8Array::New(ab, 0, buf.data_len);
      return view;
    } else {
      auto ab = v8::ArrayBuffer::New(
          d->isolate_, reinterpret_cast<void*>(buf.alloc_ptr), buf.alloc_len,
          v8::ArrayBufferCreationMode::kInternalized);
      auto view =
          v8::Uint8Array::New(ab, buf.data_ptr - buf.alloc_ptr, buf.data_len);
      return view;
    }
  }
  
  static deno_buf GetContents(v8::Isolate* isolate,
                              v8::Local<v8::ArrayBufferView> view) {
    auto ab = view->Buffer();
    auto contents = ab->GetContents();
    deno_buf buf;
    buf.alloc_ptr = reinterpret_cast<uint8_t*>(contents.Data());
    buf.alloc_len = contents.ByteLength();
    buf.data_ptr = buf.alloc_ptr + view->ByteOffset();
    buf.data_len = view->ByteLength();
    return buf;
  }
  
  // Sets the recv_ callback.
  void Recv(const v8::FunctionCallbackInfo<v8::Value>& args) {
    v8::Isolate* isolate = args.GetIsolate();
    DenoIsolate* d = FromIsolate(isolate);
    DCHECK_EQ(d->isolate_, isolate);
  
    v8::HandleScope handle_scope(isolate);
  
    if (!d->recv_.IsEmpty()) {
      isolate->ThrowException(v8_str("libdeno.recv_ already called."));
      return;
    }
  
    v8::Local<v8::Value> v = args[0];
    CHECK(v->IsFunction());
    v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(v);
  
    d->recv_.Reset(isolate, func);
  }
  
  void Send(const v8::FunctionCallbackInfo<v8::Value>& args) {
    v8::Isolate* isolate = args.GetIsolate();
    DenoIsolate* d = FromIsolate(isolate);
    DCHECK_EQ(d->isolate_, isolate);
  
    v8::Locker locker(d->isolate_);
    v8::HandleScope handle_scope(isolate);
  
    CHECK_EQ(d->current_args_, nullptr);  // libdeno.send re-entry forbidden.
    int32_t req_id = d->next_req_id_++;
  
    v8::Local<v8::Value> control_v = args[0];
    CHECK(control_v->IsArrayBufferView());
    deno_buf control =
        GetContents(isolate, v8::Local<v8::ArrayBufferView>::Cast(control_v));
    deno_buf data = {nullptr, 0u, nullptr, 0u};
    v8::Local<v8::Value> data_v;
    if (args.Length() == 2) {
      if (args[1]->IsArrayBufferView()) {
        data_v = args[1];
        data = GetContents(isolate, v8::Local<v8::ArrayBufferView>::Cast(data_v));
      }
    } else {
      CHECK_EQ(args.Length(), 1);
    }
  
    DCHECK_EQ(d->current_args_, nullptr);
    d->current_args_ = &args;
  
    d->recv_cb_(d->user_data_, req_id, control, data);
  
    if (d->current_args_ == nullptr) {
      // This indicates that deno_repond() was called already.
    } else {
      // Asynchronous.
      d->current_args_ = nullptr;
      // If the data ArrayBuffer was given, we must maintain a strong reference
      // to it until deno_respond is called.
      if (!data_v.IsEmpty()) {
        AddDataRef(d, req_id, data_v);
      }
    }
  }
  
  v8::Local<v8::Object> DenoIsolate::GetBuiltinModules() {
    v8::EscapableHandleScope handle_scope(isolate_);
    if (builtin_modules_.IsEmpty()) {
      builtin_modules_.Reset(isolate_, v8::Object::New(isolate_));
    }
    return handle_scope.Escape(builtin_modules_.Get(isolate_));
  }
  
  void BuiltinModules(v8::Local<v8::Name> property,
                      const v8::PropertyCallbackInfo<v8::Value>& info) {
    v8::Isolate* isolate = info.GetIsolate();
    DenoIsolate* d = FromIsolate(isolate);
    DCHECK_EQ(d->isolate_, isolate);
    v8::Locker locker(d->isolate_);
    info.GetReturnValue().Set(d->GetBuiltinModules());
  }
  
  void Shared(v8::Local<v8::Name> property,
              const v8::PropertyCallbackInfo<v8::Value>& info) {
    v8::Isolate* isolate = info.GetIsolate();
    DenoIsolate* d = FromIsolate(isolate);
    DCHECK_EQ(d->isolate_, isolate);
    v8::Locker locker(d->isolate_);
    v8::EscapableHandleScope handle_scope(isolate);
    if (d->shared_.data_ptr == nullptr) {
      return;
    }
    v8::Local<v8::ArrayBuffer> ab;
    if (d->shared_ab_.IsEmpty()) {
      // Lazily initialize the persistent external ArrayBuffer.
      ab = v8::ArrayBuffer::New(isolate, d->shared_.data_ptr, d->shared_.data_len,
                                v8::ArrayBufferCreationMode::kExternalized);
      d->shared_ab_.Reset(isolate, ab);
    }
    info.GetReturnValue().Set(ab);
  }
  
  v8::ScriptOrigin ModuleOrigin(v8::Local<v8::Value> resource_name,
                                v8::Isolate* isolate) {
    return v8::ScriptOrigin(resource_name, v8::Local<v8::Integer>(),
                            v8::Local<v8::Integer>(), v8::Local<v8::Boolean>(),
                            v8::Local<v8::Integer>(), v8::Local<v8::Value>(),
                            v8::Local<v8::Boolean>(), v8::Local<v8::Boolean>(),
                            v8::True(isolate));
  }
  
  void DenoIsolate::ClearModules() {
    for (auto it = module_map_.begin(); it != module_map_.end(); it++) {
      it->second.Reset();
    }
    module_map_.clear();
    for (auto it = module_info_map_.begin(); it != module_info_map_.end(); it++) {
      it->second.second.Reset();
    }
    module_info_map_.clear();
  }
  
  void DenoIsolate::RegisterModule(const char* filename,
                                   v8::Local<v8::Module> module) {
    int id = module->GetIdentityHash();
  
    module_map_.emplace(std::piecewise_construct, std::make_tuple(filename),
                        std::make_tuple(isolate_, module));
  
    // Identity hash is not necessarily unique
    // Therefore, we store a persistent handle along with filenames
    // such that we can compare the identites and select the correct module
    module_info_map_.emplace(
        std::piecewise_construct, std::make_tuple(id),
        std::make_tuple(std::piecewise_construct, std::make_tuple(filename),
                        std::make_tuple(isolate_, module)));
  }
  
  v8::MaybeLocal<v8::Module> CompileModule(v8::Local<v8::Context> context,
                                           const char* js_filename,
                                           v8::Local<v8::String> source_text) {
    auto* isolate = context->GetIsolate();
  
    v8::Isolate::Scope isolate_scope(isolate);
    v8::EscapableHandleScope handle_scope(isolate);
    v8::Context::Scope context_scope(context);
  
    auto origin = ModuleOrigin(v8_str(js_filename, true), isolate);
    v8::ScriptCompiler::Source source(source_text, origin);
  
    auto maybe_module = v8::ScriptCompiler::CompileModule(isolate, &source);
  
    if (!maybe_module.IsEmpty()) {
      auto module = maybe_module.ToLocalChecked();
      CHECK_EQ(v8::Module::kUninstantiated, module->GetStatus());
      DenoIsolate* d = FromIsolate(isolate);
      d->RegisterModule(js_filename, module);
    }
  
    return handle_scope.EscapeMaybe(maybe_module);
  }
  
  v8::MaybeLocal<v8::Module> ResolveCallback(v8::Local<v8::Context> context,
                                             v8::Local<v8::String> specifier,
                                             v8::Local<v8::Module> referrer) {
    auto* isolate = context->GetIsolate();
    DenoIsolate* d = FromIsolate(isolate);
  
    v8::Isolate::Scope isolate_scope(isolate);
    v8::EscapableHandleScope handle_scope(isolate);
    v8::Context::Scope context_scope(context);
  
    v8::String::Utf8Value specifier_utf8val(isolate, specifier);
    const char* specifier_cstr = ToCString(specifier_utf8val);
  
    auto builtin_modules = d->GetBuiltinModules();
    bool has_builtin = builtin_modules->Has(context, specifier).ToChecked();
    if (has_builtin) {
      auto val = builtin_modules->Get(context, specifier).ToLocalChecked();
      CHECK(val->IsObject());
      auto obj = val->ToObject(isolate);
  
      // In order to export obj as a module, we must iterate over its properties
      // and export them each individually.
      // TODO Find a better way to do this.
      std::string src = "let globalEval = eval\nlet g = globalEval('this');\n";
      auto names = obj->GetOwnPropertyNames(context).ToLocalChecked();
      for (uint32_t i = 0; i < names->Length(); i++) {
        auto name = names->Get(context, i).ToLocalChecked();
        v8::String::Utf8Value name_utf8val(isolate, name);
        const char* name_cstr = ToCString(name_utf8val);
        // TODO use format string.
        src.append("export const ");
        src.append(name_cstr);
        src.append(" = g.libdeno.builtinModules.");
        src.append(specifier_cstr);
        src.append(".");
        src.append(name_cstr);
        src.append(";\n");
      }
      auto export_str = v8_str(src.c_str(), true);
  
      auto module =
          CompileModule(context, specifier_cstr, export_str).ToLocalChecked();
      auto maybe_ok = module->InstantiateModule(context, ResolveCallback);
      CHECK(!maybe_ok.IsNothing());
  
      return handle_scope.Escape(module);
    }
  
    int ref_id = referrer->GetIdentityHash();
    auto range = d->module_info_map_.equal_range(ref_id);
    std::string referrer_filename;
    for (auto it = range.first; it != range.second; ++it) {
      // it->second: <string, v8::Persistent<v8::Module>>
      // operator== compares value identities stored in the handles
      // https://denolib.github.io/v8-docs/include_2v8_8h_source.html#l00487
      // Due to possibilities of identity hash collision, this is necessary
      if (it->second.second == referrer) {
        referrer_filename = it->second.first;
        break;
      }
    }
    CHECK(referrer_filename.size() != 0);
  
    v8::String::Utf8Value specifier_(isolate, specifier);
    const char* specifier_c = ToCString(specifier_);
  
    CHECK_NE(d->resolve_cb_, nullptr);
    d->resolve_cb_(d->user_data_, specifier_c, referrer_filename.c_str());
  
    if (d->resolve_module_.IsEmpty()) {
      // Resolution Error.
      isolate->ThrowException(v8_str("module resolution error"));
      return v8::MaybeLocal<v8::Module>();
    } else {
      auto module = d->resolve_module_.Get(isolate);
      d->resolve_module_.Reset();
      return handle_scope.Escape(module);
    }
  }
  
  void DenoIsolate::ResolveOk(const char* filename, const char* source) {
    CHECK(resolve_module_.IsEmpty());
    auto count = module_map_.count(filename);
    if (count == 1) {
      auto module = module_map_[filename].Get(isolate_);
      resolve_module_.Reset(isolate_, module);
    } else {
      CHECK_EQ(count, 0);
      v8::HandleScope handle_scope(isolate_);
      auto context = context_.Get(isolate_);
      v8::TryCatch try_catch(isolate_);
      auto maybe_module = CompileModule(context, filename, v8_str(source, true));
      if (maybe_module.IsEmpty()) {
        DCHECK(try_catch.HasCaught());
        HandleException(context, try_catch.Exception());
      } else {
        auto module = maybe_module.ToLocalChecked();
        resolve_module_.Reset(isolate_, module);
      }
    }
  }
  
  bool ExecuteMod(v8::Local<v8::Context> context, const char* js_filename,
                  const char* js_source, bool resolve_only) {
    auto* isolate = context->GetIsolate();
    v8::Isolate::Scope isolate_scope(isolate);
    v8::HandleScope handle_scope(isolate);
    v8::Context::Scope context_scope(context);
  
    auto source = v8_str(js_source, true);
  
    v8::TryCatch try_catch(isolate);
  
    auto maybe_module = CompileModule(context, js_filename, source);
  
    if (maybe_module.IsEmpty()) {
      DCHECK(try_catch.HasCaught());
      HandleException(context, try_catch.Exception());
      return false;
    }
    DCHECK(!try_catch.HasCaught());
  
    auto module = maybe_module.ToLocalChecked();
    auto maybe_ok = module->InstantiateModule(context, ResolveCallback);
    if (maybe_ok.IsNothing()) {
      return false;
    }
  
    CHECK_EQ(v8::Module::kInstantiated, module->GetStatus());
  
    if (resolve_only) {
      return true;
    }
  
    auto result = module->Evaluate(context);
  
    if (result.IsEmpty()) {
      DCHECK(try_catch.HasCaught());
      CHECK_EQ(v8::Module::kErrored, module->GetStatus());
      HandleException(context, module->GetException());
      return false;
    }
  
    return true;
  }
  
  bool Execute(v8::Local<v8::Context> context, const char* js_filename,
               const char* js_source) {
    auto* isolate = context->GetIsolate();
    v8::Isolate::Scope isolate_scope(isolate);
    v8::HandleScope handle_scope(isolate);
    v8::Context::Scope context_scope(context);
  
    auto source = v8_str(js_source, true);
    auto name = v8_str(js_filename, true);
  
    v8::TryCatch try_catch(isolate);
  
    v8::ScriptOrigin origin(name);
  
    auto script = v8::Script::Compile(context, source, &origin);
  
    if (script.IsEmpty()) {
      DCHECK(try_catch.HasCaught());
      HandleException(context, try_catch.Exception());
      return false;
    }
  
    auto result = script.ToLocalChecked()->Run(context);
  
    if (result.IsEmpty()) {
      DCHECK(try_catch.HasCaught());
      HandleException(context, try_catch.Exception());
      return false;
    }
  
    return true;
  }
  
  void InitializeContext(v8::Isolate* isolate, v8::Local<v8::Context> context) {
    v8::HandleScope handle_scope(isolate);
    v8::Context::Scope context_scope(context);
  
    auto global = context->Global();
  
    auto deno_val = v8::Object::New(isolate);
    CHECK(global->Set(context, deno::v8_str("libdeno"), deno_val).FromJust());
  
    auto print_tmpl = v8::FunctionTemplate::New(isolate, Print);
    auto print_val = print_tmpl->GetFunction(context).ToLocalChecked();
    CHECK(deno_val->Set(context, deno::v8_str("print"), print_val).FromJust());
  
    auto recv_tmpl = v8::FunctionTemplate::New(isolate, Recv);
    auto recv_val = recv_tmpl->GetFunction(context).ToLocalChecked();
    CHECK(deno_val->Set(context, deno::v8_str("recv"), recv_val).FromJust());
  
    auto send_tmpl = v8::FunctionTemplate::New(isolate, Send);
    auto send_val = send_tmpl->GetFunction(context).ToLocalChecked();
    CHECK(deno_val->Set(context, deno::v8_str("send"), send_val).FromJust());
  
    CHECK(deno_val->SetAccessor(context, deno::v8_str("shared"), Shared)
              .FromJust());
  
    CHECK(
        deno_val
            ->SetAccessor(context, deno::v8_str("builtinModules"), BuiltinModules)
            .FromJust());
  }
  
  void DenoIsolate::AddIsolate(v8::Isolate* isolate) {
    isolate_ = isolate;
    // Leaving this code here because it will probably be useful later on, but
    // disabling it now as I haven't got tests for the desired behavior.
    // d->isolate->SetAbortOnUncaughtExceptionCallback(AbortOnUncaughtExceptionCallback);
    // d->isolate->AddMessageListener(MessageCallback2);
    // d->isolate->SetFatalErrorHandler(FatalErrorCallback2);
    isolate_->SetCaptureStackTraceForUncaughtExceptions(
        true, 10, v8::StackTrace::kDetailed);
    isolate_->SetPromiseRejectCallback(deno::PromiseRejectCallback);
    isolate_->SetData(0, this);
  }
  
  }  // namespace deno
  