Last active
December 20, 2019 13:03
-
-
Save tombatron/277146712346013fff05dbad5572dffe to your computer and use it in GitHub Desktop.
V8 Context Dispose Question
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <assert.h> | |
#include <iostream> | |
#include "include/libplatform/libplatform.h" | |
#include "include/v8.h" | |
#include "v8eval.h" | |
using namespace std; | |
namespace v8eval { | |
static unique_ptr<v8::Platform> platform = nullptr; | |
void set_flags(const std::string& flags) { | |
v8::V8::SetFlagsFromString(flags.c_str(), static_cast<int>(flags.length())); | |
} | |
bool initialize() { | |
if (platform) { | |
return false; | |
} | |
platform = v8::platform::NewDefaultPlatform(); | |
v8::V8::InitializePlatform(platform.get()); | |
v8::V8::InitializeICU(); | |
return v8::V8::Initialize(); | |
} | |
bool dispose() { | |
if (!platform) { | |
return false; | |
} | |
v8::V8::Dispose(); | |
v8::V8::ShutdownPlatform(); | |
platform.reset(); | |
platform = nullptr; | |
return true; | |
} | |
_V8::_V8() { | |
v8::Isolate::CreateParams create_params; | |
create_params.array_buffer_allocator = | |
v8::ArrayBuffer::Allocator::NewDefaultAllocator(); | |
isolate_ = v8::Isolate::New(create_params); | |
v8::Locker locker(isolate_); | |
v8::Isolate::Scope isolate_scope(isolate_); | |
v8::HandleScope handle_scope(isolate_); | |
context_.Reset(isolate_, new_context()); | |
} | |
_V8::~_V8() { | |
isolate_->Dispose(); | |
} | |
v8::Local<v8::Context> _V8::context() { | |
// assert(context_.IsEmpty()); // Why this results in false is for another question... | |
return v8::Local<v8::Context>::New(isolate_, context_); | |
} | |
v8::Local<v8::Context> _V8::new_context(v8::Local<v8::ObjectTemplate> global_tmpl, v8::Local<v8::Value> global_obj) { | |
if (global_tmpl.IsEmpty() && global_obj.IsEmpty()) { | |
v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate_); | |
return v8::Context::New(isolate_, nullptr, global); | |
} else { | |
return v8::Context::New(isolate_, nullptr, global_tmpl, global_obj); | |
} | |
} | |
v8::Local<v8::String> _V8::new_string(const char* str) { | |
return v8::String::NewFromUtf8(isolate_, str ? str : "", v8::NewStringType::kNormal).ToLocalChecked(); | |
} | |
std::string _V8::to_std_string(v8::Local<v8::Value> value) { | |
v8::String::Utf8Value str(isolate_, value); | |
return *str ? *str : "Error: Cannot convert to string"; | |
} | |
v8::Local<v8::Value> _V8::json_parse(v8::Local<v8::Context> context, v8::Local<v8::String> str) { | |
v8::Local<v8::Object> global = context->Global(); | |
v8::Local<v8::Object> json = global->Get(context, new_string("JSON")).ToLocalChecked()->ToObject(context).ToLocalChecked(); | |
v8::Local<v8::Function> parse = v8::Local<v8::Function>::Cast(json->Get(context, new_string("parse")).ToLocalChecked()); | |
v8::Local<v8::Value> result; | |
v8::Local<v8::Value> value = str; | |
if (!parse->Call(context, json, 1, &value).ToLocal(&result)) { | |
return v8::Local<v8::Value>(); // empty | |
} else { | |
return result; | |
} | |
} | |
v8::Local<v8::String> _V8::json_stringify(v8::Local<v8::Context> context, v8::Local<v8::Value> value) { | |
v8::Local<v8::Object> global = context->Global(); | |
v8::Local<v8::Object> json = global->Get(context, new_string("JSON")).ToLocalChecked()->ToObject(context).ToLocalChecked(); | |
v8::Local<v8::Function> stringify = v8::Local<v8::Function>::Cast(json->Get(context, new_string("stringify")).ToLocalChecked()); | |
v8::Local<v8::Value> result; | |
if (!stringify->Call(context, json, 1, &value).ToLocal(&result)) { | |
return new_string(""); | |
} else { | |
return result->ToString(context).ToLocalChecked(); | |
} | |
} | |
std::string _V8::eval(const std::string& src) { | |
v8::Locker locker(isolate_); | |
v8::Isolate::Scope isolate_scope(isolate_); | |
v8::HandleScope handle_scope(isolate_); | |
v8::Local<v8::Context> context = this->context(); | |
v8::Context::Scope context_scope(context); | |
v8::TryCatch try_catch(isolate_); | |
v8::Local<v8::String> source = new_string(src.c_str()); | |
v8::Local<v8::String> name = new_string("v8eval"); | |
v8::ScriptOrigin origin(name); | |
v8::Local<v8::Script> script; | |
if (!v8::Script::Compile(context, source, &origin).ToLocal(&script)) { | |
return to_std_string(try_catch.Exception()); | |
} else { | |
v8::Local<v8::Value> result; | |
if (!script->Run(context).ToLocal(&result)) { | |
v8::Local<v8::Value> stack; | |
if (!try_catch.StackTrace(context).ToLocal(&stack)) { | |
return to_std_string(try_catch.Exception()); | |
} else { | |
return to_std_string(stack); | |
} | |
} else { | |
return to_std_string(json_stringify(context, result)); | |
} | |
} | |
} | |
std::string _V8::call(const std::string& func, const std::string& args) { | |
v8::Locker locker(isolate_); | |
v8::Isolate::Scope isolate_scope(isolate_); | |
v8::HandleScope handle_scope(isolate_); | |
v8::Local<v8::Context> context = this->context(); | |
v8::Context::Scope context_scope(context); | |
v8::TryCatch try_catch(isolate_); | |
v8::Local<v8::Object> global = context->Global(); | |
v8::Local<v8::Value> result; | |
if (!global->Get(context, new_string(func.c_str())).ToLocal(&result)) { | |
return to_std_string(try_catch.Exception()); | |
} else if (!result->IsFunction()) { | |
return "TypeError: '" + func + "' is not a function"; | |
} | |
v8::Local<v8::Function> function = v8::Handle<v8::Function>::Cast(result); | |
v8::Local<v8::Function> apply = v8::Handle<v8::Function>::Cast(function->Get(context, new_string("apply")).ToLocalChecked()); | |
v8::Local<v8::Value> arguments = json_parse(context, new_string(args.c_str())); | |
if (arguments.IsEmpty() || !arguments->IsArray()) { | |
return "TypeError: '" + args + "' is not an array"; | |
} | |
v8::Local<v8::Value> values[] = { function, arguments }; | |
if (!apply->Call(context, function, 2, values).ToLocal(&result)) { | |
return to_std_string(try_catch.Exception()); | |
} else { | |
return to_std_string(json_stringify(context, result)); | |
} | |
} | |
void Heap(const v8::FunctionCallbackInfo<v8::Value>& args) { | |
v8::Isolate* isolate = args.GetIsolate(); | |
v8::Local<v8::Context> context = isolate->GetCurrentContext(); | |
v8::HeapStatistics s; | |
isolate->GetHeapStatistics(&s); | |
v8::Local<v8::Object> obj = v8::Object::New(isolate); | |
(void)obj->Set(context, v8::String::NewFromUtf8(isolate, "totalHeapSize").ToLocalChecked(), v8::Number::New(isolate, s.total_heap_size())); | |
(void)obj->Set(context, v8::String::NewFromUtf8(isolate, "totalHeapSizeExecutable").ToLocalChecked(), v8::Number::New(isolate, s.total_heap_size_executable())); | |
(void)obj->Set(context, v8::String::NewFromUtf8(isolate, "totalPhysicalSize").ToLocalChecked(), v8::Number::New(isolate, s.total_physical_size())); | |
(void)obj->Set(context, v8::String::NewFromUtf8(isolate, "totalAvailableSize").ToLocalChecked(), v8::Number::New(isolate, s.total_available_size())); | |
(void)obj->Set(context, v8::String::NewFromUtf8(isolate, "usedHeapSize").ToLocalChecked(), v8::Number::New(isolate, s.used_heap_size())); | |
(void)obj->Set(context, v8::String::NewFromUtf8(isolate, "heapSizeLimit").ToLocalChecked(), v8::Number::New(isolate, s.heap_size_limit())); | |
(void)obj->Set(context, v8::String::NewFromUtf8(isolate, "mallocedMemory").ToLocalChecked(), v8::Number::New(isolate, s.malloced_memory())); | |
(void)obj->Set(context, v8::String::NewFromUtf8(isolate, "peakMallocedMemory").ToLocalChecked(), v8::Number::New(isolate, s.peak_malloced_memory())); | |
(void)obj->Set(context, v8::String::NewFromUtf8(isolate, "doesZapGarbage").ToLocalChecked(), v8::Number::New(isolate, s.does_zap_garbage())); | |
(void)args.GetReturnValue().Set(obj); | |
} | |
void _V8::enable_heap_report() { | |
v8::Locker locker(isolate_); | |
v8::Isolate::Scope isolate_scope(isolate_); | |
v8::HandleScope handle_scope(isolate_); | |
v8::Local<v8::Context> context = this->context(); | |
v8::Context::Scope context_scope(context); | |
(void)context->Global()->Set(context, new_string("heap"), v8::FunctionTemplate::New(isolate_, Heap)->GetFunction(context).ToLocalChecked()); | |
} | |
} // namespace v8eval | |
int main(int argc, char* argv[]) { | |
v8eval::initialize(); | |
v8eval::_V8 v8; | |
string result = v8.eval("const profits = 2489.8237; profits.toFixed(3);"); | |
printf("%s\n", result.c_str()); | |
v8.~_V8(); | |
v8eval::dispose(); | |
return 0; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef V8EVAL_H_ | |
#define V8EVAL_H_ | |
#include <string> | |
#include "v8.h" | |
/// \file | |
namespace v8eval { | |
/// \brief Set the given V8 flags | |
/// | |
/// This method sets the given V8 flags. | |
void set_flags(const std::string& flags); | |
/// \brief Initialize the V8 runtime environment | |
/// \return success or not as boolean | |
/// | |
/// This method initializes the V8 runtime environment. It must be called before creating any V8 instance. | |
bool initialize(); | |
/// \brief Dispose the V8 runtime environment | |
/// \return success or not as boolean | |
/// | |
/// This method disposes the V8 runtime environment. | |
bool dispose(); | |
/// \class _V8 | |
/// | |
/// _V8 instances can be used in multiple threads. | |
/// But each _V8 instance can be used in only one thread at a time. | |
class _V8 { | |
public: | |
_V8(); | |
virtual ~_V8(); | |
/// \brief Evaluate JavaScript code | |
/// \param src JavaScript code | |
/// \return JSON-encoded result or exception message | |
/// | |
/// This method evaluates the given JavaScript code 'src' and returns the result in JSON. | |
/// If some JavaScript exception happens in runtime, the exception message is returned. | |
std::string eval(const std::string& src); | |
/// \brief Call a JavaScript function | |
/// \param func Name of a JavaScript function | |
/// \param args JSON-encoded argument array | |
/// \return JSON-encoded result or exception message | |
/// | |
/// This method calls the JavaScript function specified by 'func' | |
/// with the JSON-encoded argument array 'args' | |
/// and returns the result in JSON. | |
/// If some JavaScript exception happens in runtime, the exception message is returned. | |
std::string call(const std::string& func, const std::string& args); | |
protected: | |
void enable_heap_report(); | |
v8::Local<v8::Context> context(); | |
v8::Local<v8::Context> new_context( | |
v8::Local<v8::ObjectTemplate> global_tmpl = v8::Local<v8::ObjectTemplate>(), | |
v8::Local<v8::Value> global_obj = v8::Local<v8::Value>()); | |
v8::Local<v8::String> new_string(const char* str); | |
v8::Local<v8::Value> json_parse(v8::Local<v8::Context> context, v8::Local<v8::String> str); | |
v8::Local<v8::String> json_stringify(v8::Local<v8::Context> context, v8::Local<v8::Value> value); | |
std::string to_std_string(v8::Local<v8::Value> value); | |
private: | |
v8::Isolate* isolate_; | |
v8::Persistent<v8::Context> context_; | |
}; | |
} // namespace v8eval | |
#endif // V8EVAL_H_ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment