#include #include #include #include #include #include #include #include #include #include using namespace v8; typedef struct { const char* data; int raw_size; } SnapshotInfo; typedef struct { Isolate* isolate; ArrayBuffer::Allocator* allocator; StartupData* startup_data; bool interrupted; bool disposed; pid_t pid; // 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 volatile int refs_count; } IsolateInfo; typedef struct { IsolateInfo* isolate_info; Persistent* context; } ContextInfo; typedef struct { bool parsed; bool executed; bool terminated; bool json; Persistent* value; Persistent* message; Persistent* backtrace; } EvalResult; typedef struct { ContextInfo* context_info; Local* eval; Local* filename; useconds_t timeout; EvalResult* result; long max_memory; } EvalParams; static VALUE rb_eScriptTerminatedError; static VALUE rb_eV8OutOfMemoryError; static VALUE rb_eParseError; static VALUE rb_eScriptRuntimeError; static VALUE rb_cJavaScriptFunction; static VALUE rb_eSnapshotError; static VALUE rb_ePlatformAlreadyInitializedError; static VALUE rb_mJSON; 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(); } static void gc_callback(Isolate *isolate, GCType type, GCCallbackFlags flags) { if((bool)isolate->GetData(3)) return; long softlimit = *(long*) isolate->GetData(2); HeapStatistics* stats = new HeapStatistics(); isolate->GetHeapStatistics(stats); long used = stats->used_heap_size(); if(used > softlimit) { isolate->SetData(3, (void*)true); V8::TerminateExecution(isolate); } } 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); v8::ScriptOrigin *origin = NULL; // in gvl flag isolate->SetData(0, (void*)false); // terminate ASAP isolate->SetData(1, (void*)false); // Memory softlimit isolate->SetData(2, (void*)false); // Memory softlimit hit flag isolate->SetData(3, (void*)false); MaybeLocal