Skip to content

Instantly share code, notes, and snippets.

@tombatron
Last active December 20, 2019 13:03
Show Gist options
  • Save tombatron/277146712346013fff05dbad5572dffe to your computer and use it in GitHub Desktop.
Save tombatron/277146712346013fff05dbad5572dffe to your computer and use it in GitHub Desktop.
V8 Context Dispose Question
#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;
}
#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