#include #include #include #include #include #include #include #include #include using namespace v8; class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator { public: virtual void* Allocate(size_t length) { void* data = AllocateUninitialized(length); return data == NULL ? data : memset(data, 0, length); } virtual void* AllocateUninitialized(size_t length) { return malloc(length); } virtual void Free(void* data, size_t) { free(data); } }; typedef struct { const char* data; int raw_size; } SnapshotInfo; typedef struct { Isolate* isolate; ArrayBufferAllocator* allocator; StartupData* startup_data; bool interrupted; // how many references to this isolate exist // we can't rely on Ruby's GC for this, because when destroying // objects, Ruby will destroy ruby objects first, then call the // extenstion's deallocators. In this case, that means it would // call `deallocate_isolate` _before_ `deallocate`, causing a segfault int refs_count; } IsolateInfo; typedef struct { IsolateInfo* isolate_info; Persistent* context; } ContextInfo; typedef struct { bool parsed; bool executed; bool terminated; Persistent* value; Persistent* message; Persistent* backtrace; } EvalResult; typedef struct { ContextInfo* context_info; Local* eval; useconds_t timeout; EvalResult* result; } EvalParams; static VALUE rb_eScriptTerminatedError; static VALUE rb_eParseError; static VALUE rb_eScriptRuntimeError; static VALUE rb_cJavaScriptFunction; static VALUE rb_eSnapshotError; static VALUE rb_ePlatformAlreadyInitializedError; static VALUE rb_cFailedV8Conversion; static VALUE rb_cDateTime = Qnil; static Platform* current_platform = NULL; static std::mutex platform_lock; static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) { bool platform_already_initialized = false; platform_lock.lock(); if (current_platform == NULL) { V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), (int)RSTRING_LEN(flag_as_str)); } else { platform_already_initialized = true; } platform_lock.unlock(); // important to raise outside of the lock if (platform_already_initialized) { rb_raise(rb_ePlatformAlreadyInitializedError, "The V8 platform is already initialized"); } return Qnil; } static void init_v8() { // no need to wait for the lock if already initialized if (current_platform != NULL) return; platform_lock.lock(); if (current_platform == NULL) { V8::InitializeICU(); current_platform = platform::CreateDefaultPlatform(); V8::InitializePlatform(current_platform); V8::Initialize(); } platform_lock.unlock(); } void* nogvl_context_eval(void* arg) { EvalParams* eval_params = (EvalParams*)arg; EvalResult* result = eval_params->result; Isolate* isolate = eval_params->context_info->isolate_info->isolate; Isolate::Scope isolate_scope(isolate); HandleScope handle_scope(isolate); TryCatch trycatch(isolate); Local context = eval_params->context_info->context->Get(isolate); Context::Scope context_scope(context); // in gvl flag isolate->SetData(0, (void*)false); // terminate ASAP isolate->SetData(1, (void*)false); MaybeLocal