-
-
Save jeromegn/29e089da947a8878bce4fe4751c05bc5 to your computer and use it in GitHub Desktop.
This file contains 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
require "../lib_v8" | |
require "./value" | |
require "./context" | |
module V8 | |
alias FunctionCallback = Proc(FunctionCallbackInfo,Value?) | |
struct FunctionCallbackInfo | |
# private property raw : LibV8::FunctionCallbackInfo | |
# getter this : Value | |
getter args : Slice(Value) | |
getter length : LibC::Int | |
def initialize(ctx : Context, parsed : LibV8::ParsedFunctionCallbackInfo) | |
puts "sizeof parsed thing: #{sizeof(typeof(parsed))}" | |
# puts "this pointer: #{parsed.this}" | |
# @this = Value.new(ctx, parsed.this) | |
@length = parsed.length | |
puts "initial pointer: #{parsed.args}" | |
args = Slice.new(parsed.args, parsed.length.to_i) | |
@args = args.map_with_index do |ptr, i| | |
puts "arg #{i}" | |
p ptr | |
Value.new(ctx, ptr) | |
end | |
puts typeof(args), sizeof(typeof(args)) | |
end | |
end | |
end |
This file contains 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
require "./v8/crystal_string" | |
require "./v8/heap_statistics" | |
require "./v8/value_error_pair" | |
@[Link(ldflags: "#{__DIR__}/v8_c_bridge.o -lstdc++ -L#{__DIR__}/../libv8 -lv8_base -lv8_init -lv8_initializers -lv8_libbase -lv8_libplatform -lv8_libsampler -lv8_nosnapshot")] | |
lib LibV8 | |
alias Char = LibC::Char | |
alias StartupData = V8::CrystalString | |
alias Error = V8::CrystalString | |
type Isolate = Void* | |
type Context = Void* | |
type PersistentValue = Void* | |
type FunctionTemplate = Void* | |
fun v8_init | |
fun v8_Isolate_New(StartupData) : Isolate | |
fun v8_Isolate_GetHeapStatistics(Isolate) : V8::HeapStatistics | |
fun v8_Isolate_Release(Isolate) | |
fun v8_Isolate_NewContext(Isolate) : Context | |
fun v8_Context_Release(Context) | |
fun v8_Context_Global(Context) : PersistentValue | |
fun v8_Context_Run(Context, Char*, Char*) : V8::ValueErrorPair | |
fun v8_Value_Release(Context, PersistentValue) | |
fun v8_Value_Get(Context, PersistentValue, Char*) : V8::ValueErrorPair | |
fun v8_Value_Set(Context, PersistentValue, Char*, PersistentValue) : Error | |
fun v8_Value_String(Context, PersistentValue) : V8::CrystalString | |
fun v8_Value_IsFunction(Context, PersistentValue) : Bool | |
fun v8_Function_Call(Context, PersistentValue, this : PersistentValue, length : Int32, args : PersistentValue*) : V8::ValueErrorPair | |
fun v8_Object_New(Context) : PersistentValue | |
# struct FunctionCallbackInfo | |
# # _implicit_args : | |
# pad : Char[24] | |
# end | |
type FunctionCallbackInfo = Void* | |
fun v8_FunctionTemplate_New(Context, callback : FunctionCallbackInfo->, Char*) : PersistentValue | |
struct ParsedFunctionCallbackInfo | |
length : LibC::Int | |
args : PersistentValue* | |
end | |
fun v8_FunctionCallbackInfo_Data(FunctionCallbackInfo) : V8::CrystalString | |
fun v8_FunctionCallbackInfo(FunctionCallbackInfo) : ParsedFunctionCallbackInfo | |
struct Version | |
major : Int32 | |
minor : Int32 | |
build : Int32 | |
patch : Int32 | |
end | |
fun v8_Version : Version | |
end | |
This file contains 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 "v8_c_bridge.h" | |
#include "libplatform/libplatform.h" | |
#include "v8.h" | |
#include "v8-profiler.h" | |
#include <cstdlib> | |
#include <cstring> | |
#include <string> | |
#include <sstream> | |
#include <stdio.h> | |
#include <iostream> | |
#define ISOLATE_SCOPE(iso) \ | |
v8::Isolate* isolate = (iso); \ | |
fprintf(stderr, "got isolate in ISOLATE_SCOPE\n"); \ | |
v8::Locker locker(isolate); /* Lock to current thread. */ \ | |
v8::Isolate::Scope isolate_scope(isolate); /* Assign isolate to this thread. */ | |
#define VALUE_SCOPE(ctxptr) \ | |
ISOLATE_SCOPE(static_cast<Context*>(ctxptr)->isolate) \ | |
v8::HandleScope handle_scope(isolate); /* Create a scope for handles. */ \ | |
v8::Local<v8::Context> ctx(static_cast<Context*>(ctxptr)->ptr.Get(isolate)); \ | |
v8::Context::Scope context_scope(ctx); /* Scope to this context. */ | |
// extern "C" ValueErrorPair go_callback_handler( | |
// String id, CallerInfo info, int argc, ValueKindsPair* argv); | |
class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator { | |
public: | |
virtual void* Allocate(size_t length); | |
virtual void* AllocateUninitialized(size_t length); | |
virtual void Free(void* data, size_t); | |
}; | |
void* ArrayBufferAllocator::Allocate(size_t length) { | |
void* data = AllocateUninitialized(length); | |
return data == nullptr ? data : memset(data, 0, length); | |
} | |
void* ArrayBufferAllocator::AllocateUninitialized(size_t length) { return malloc(length); } | |
void ArrayBufferAllocator::Free(void* data, size_t) { free(data); } | |
// We only need one, it's stateless. | |
ArrayBufferAllocator allocator; | |
typedef struct { | |
v8::Persistent<v8::Context> ptr; | |
v8::Isolate* isolate; | |
} Context; | |
typedef v8::Persistent<v8::Value> Value; | |
String str_to_cr_str(const v8::String::Utf8Value& src) { | |
char* data = static_cast<char*>(malloc(src.length())); | |
memcpy(data, *src, src.length()); | |
return (String){data, src.length()}; | |
} | |
String str_to_cr_str(const v8::Local<v8::Value>& val) { | |
return str_to_cr_str(v8::String::Utf8Value(val)); | |
} | |
String str_to_cr_str(const char* msg) { | |
const char* data = strdup(msg); | |
return (String){data, int(strlen(msg))}; | |
} | |
String str_to_cr_str(const std::string& src) { | |
char* data = static_cast<char*>(malloc(src.length())); | |
memcpy(data, src.data(), src.length()); | |
return (String){data, int(src.length())}; | |
} | |
ValueKinds v8_Value_KindsFromLocal(v8::Local<v8::Value> value) { | |
std::vector<uint8_t> kinds; | |
if (value->IsUndefined()) kinds.push_back(ValueKind::kUndefined ); | |
if (value->IsNull()) kinds.push_back(ValueKind::kNull ); | |
if (value->IsName()) kinds.push_back(ValueKind::kName ); | |
if (value->IsString()) kinds.push_back(ValueKind::kString ); | |
if (value->IsSymbol()) kinds.push_back(ValueKind::kSymbol ); | |
if (value->IsObject()) kinds.push_back(ValueKind::kObject ); | |
if (value->IsArray()) kinds.push_back(ValueKind::kArray ); | |
if (value->IsBoolean()) kinds.push_back(ValueKind::kBoolean ); | |
if (value->IsTrue()) kinds.push_back(ValueKind::kTrue ); | |
if (value->IsFalse()) kinds.push_back(ValueKind::kFalse ); | |
if (value->IsNumber()) kinds.push_back(ValueKind::kNumber ); | |
if (value->IsExternal()) kinds.push_back(ValueKind::kExternal ); | |
if (value->IsInt32()) kinds.push_back(ValueKind::kInt32 ); | |
if (value->IsUint32()) kinds.push_back(ValueKind::kUint32 ); | |
if (value->IsDate()) kinds.push_back(ValueKind::kDate ); | |
if (value->IsArgumentsObject()) kinds.push_back(ValueKind::kArgumentsObject ); | |
if (value->IsBooleanObject()) kinds.push_back(ValueKind::kBooleanObject ); | |
if (value->IsNumberObject()) kinds.push_back(ValueKind::kNumberObject ); | |
if (value->IsStringObject()) kinds.push_back(ValueKind::kStringObject ); | |
if (value->IsSymbolObject()) kinds.push_back(ValueKind::kSymbolObject ); | |
if (value->IsNativeError()) kinds.push_back(ValueKind::kNativeError ); | |
if (value->IsRegExp()) kinds.push_back(ValueKind::kRegExp ); | |
if (value->IsFunction()) kinds.push_back(ValueKind::kFunction ); | |
if (value->IsAsyncFunction()) kinds.push_back(ValueKind::kAsyncFunction ); | |
if (value->IsGeneratorFunction()) kinds.push_back(ValueKind::kGeneratorFunction); | |
if (value->IsGeneratorObject()) kinds.push_back(ValueKind::kGeneratorObject ); | |
if (value->IsPromise()) kinds.push_back(ValueKind::kPromise ); | |
if (value->IsMap()) kinds.push_back(ValueKind::kMap ); | |
if (value->IsSet()) kinds.push_back(ValueKind::kSet ); | |
if (value->IsMapIterator()) kinds.push_back(ValueKind::kMapIterator ); | |
if (value->IsSetIterator()) kinds.push_back(ValueKind::kSetIterator ); | |
if (value->IsWeakMap()) kinds.push_back(ValueKind::kWeakMap ); | |
if (value->IsWeakSet()) kinds.push_back(ValueKind::kWeakSet ); | |
if (value->IsArrayBuffer()) kinds.push_back(ValueKind::kArrayBuffer ); | |
if (value->IsArrayBufferView()) kinds.push_back(ValueKind::kArrayBufferView ); | |
if (value->IsTypedArray()) kinds.push_back(ValueKind::kTypedArray ); | |
if (value->IsUint8Array()) kinds.push_back(ValueKind::kUint8Array ); | |
if (value->IsUint8ClampedArray()) kinds.push_back(ValueKind::kUint8ClampedArray); | |
if (value->IsInt8Array()) kinds.push_back(ValueKind::kInt8Array ); | |
if (value->IsUint16Array()) kinds.push_back(ValueKind::kUint16Array ); | |
if (value->IsInt16Array()) kinds.push_back(ValueKind::kInt16Array ); | |
if (value->IsUint32Array()) kinds.push_back(ValueKind::kUint32Array ); | |
if (value->IsInt32Array()) kinds.push_back(ValueKind::kInt32Array ); | |
if (value->IsFloat32Array()) kinds.push_back(ValueKind::kFloat32Array ); | |
if (value->IsFloat64Array()) kinds.push_back(ValueKind::kFloat64Array ); | |
if (value->IsDataView()) kinds.push_back(ValueKind::kDataView ); | |
if (value->IsSharedArrayBuffer()) kinds.push_back(ValueKind::kSharedArrayBuffer); | |
if (value->IsProxy()) kinds.push_back(ValueKind::kProxy ); | |
if (value->IsWebAssemblyCompiledModule()) kinds.push_back(ValueKind::kWebAssemblyCompiledModule); | |
uint8_t* data = static_cast<uint8_t*>(calloc(kinds.size(), sizeof(uint8_t))); | |
memcpy(data, kinds.data(), kinds.size() * sizeof(uint8_t)); | |
return (ValueKinds){data, kinds.size()}; | |
} | |
std::string str(v8::Local<v8::Value> value) { | |
v8::String::Utf8Value s(value); | |
if (s.length() == 0) { | |
return ""; | |
} | |
return *s; | |
} | |
std::string report_exception(v8::Isolate* isolate, v8::Local<v8::Context> ctx, v8::TryCatch& try_catch) { | |
std::stringstream ss; | |
ss << "Uncaught exception: "; | |
std::string exceptionStr = str(try_catch.Exception()); | |
ss << exceptionStr; // TODO(aroman) JSON-ify objects? | |
if (!try_catch.Message().IsEmpty()) { | |
if (!try_catch.Message()->GetScriptResourceName()->IsUndefined()) { | |
ss << std::endl | |
<< "at " << str(try_catch.Message()->GetScriptResourceName()); | |
v8::Maybe<int> line_no = try_catch.Message()->GetLineNumber(ctx); | |
v8::Maybe<int> start = try_catch.Message()->GetStartColumn(ctx); | |
v8::Maybe<int> end = try_catch.Message()->GetEndColumn(ctx); | |
v8::MaybeLocal<v8::String> sourceLine = try_catch.Message()->GetSourceLine(ctx); | |
if (line_no.IsJust()) { | |
ss << ":" << line_no.ToChecked(); | |
} | |
if (start.IsJust()) { | |
ss << ":" << start.ToChecked(); | |
} | |
if (!sourceLine.IsEmpty()) { | |
ss << std::endl | |
<< " " << str(sourceLine.ToLocalChecked()); | |
} | |
if (start.IsJust() && end.IsJust()) { | |
ss << std::endl | |
<< " "; | |
for (int i = 0; i < start.ToChecked(); i++) { | |
ss << " "; | |
} | |
for (int i = start.ToChecked(); i < end.ToChecked(); i++) { | |
ss << "^"; | |
} | |
} | |
} | |
} | |
if (!try_catch.StackTrace().IsEmpty()) { | |
ss << std::endl << "Stack trace: " << str(try_catch.StackTrace()); | |
} | |
return ss.str(); | |
} | |
extern "C" { | |
Version v8_Version() { | |
return (Version){V8_MAJOR_VERSION, V8_MINOR_VERSION, V8_BUILD_NUMBER, V8_PATCH_LEVEL}; | |
} | |
void v8_init() { | |
v8::Platform *platform = v8::platform::CreateDefaultPlatform(); | |
v8::V8::InitializePlatform(platform); | |
v8::V8::Initialize(); | |
return; | |
} | |
StartupData v8_CreateSnapshotDataBlob(const char* js) { | |
v8::StartupData data = v8::V8::CreateSnapshotDataBlob(js); | |
return StartupData{data.data, data.raw_size}; | |
} | |
IsolatePtr v8_Isolate_New(StartupData startup_data) { | |
v8::Isolate::CreateParams create_params; | |
create_params.array_buffer_allocator = &allocator; | |
if (startup_data.len > 0 && startup_data.ptr != nullptr) { | |
v8::StartupData* data = new v8::StartupData; | |
data->data = startup_data.ptr; | |
data->raw_size = startup_data.len; | |
create_params.snapshot_blob = data; | |
} | |
return static_cast<IsolatePtr>(v8::Isolate::New(create_params)); | |
} | |
ContextPtr v8_Isolate_NewContext(IsolatePtr isolate_ptr) { | |
ISOLATE_SCOPE(static_cast<v8::Isolate*>(isolate_ptr)); | |
v8::HandleScope handle_scope(isolate); | |
isolate->SetCaptureStackTraceForUncaughtExceptions(true); | |
v8::Local<v8::ObjectTemplate> globals = v8::ObjectTemplate::New(isolate); | |
Context* ctx = new Context; | |
ctx->ptr.Reset(isolate, v8::Context::New(isolate, nullptr, globals)); | |
ctx->isolate = isolate; | |
return static_cast<ContextPtr>(ctx); | |
} | |
void v8_Isolate_Terminate(IsolatePtr isolate_ptr) { | |
v8::Isolate* isolate = static_cast<v8::Isolate*>(isolate_ptr); | |
isolate->TerminateExecution(); | |
} | |
void v8_Isolate_Release(IsolatePtr isolate_ptr) { | |
if (isolate_ptr == nullptr) { | |
return; | |
} | |
v8::Isolate* isolate = static_cast<v8::Isolate*>(isolate_ptr); | |
isolate->Dispose(); | |
} | |
ValueErrorPair v8_Context_Run(ContextPtr ctxptr, const char* code, const char* filename) { | |
Context* ctx = static_cast<Context*>(ctxptr); | |
v8::Isolate* isolate = ctx->isolate; | |
v8::Locker locker(isolate); | |
v8::Isolate::Scope isolate_scope(isolate); | |
v8::HandleScope handle_scope(isolate); | |
v8::Context::Scope context_scope(ctx->ptr.Get(isolate)); | |
v8::TryCatch try_catch(isolate); | |
try_catch.SetVerbose(false); | |
filename = filename ? filename : "(no file)"; | |
ValueErrorPair res = { nullptr, nullptr }; | |
v8::Local<v8::Script> script = v8::Script::Compile( | |
v8::String::NewFromUtf8(isolate, code), | |
v8::String::NewFromUtf8(isolate, filename)); | |
if (script.IsEmpty()) { | |
res.error_msg = str_to_cr_str(report_exception(isolate, ctx->ptr.Get(isolate), try_catch)); | |
return res; | |
} | |
v8::Local<v8::Value> result = script->Run(); | |
if (result.IsEmpty()) { | |
res.error_msg = str_to_cr_str(report_exception(isolate, ctx->ptr.Get(isolate), try_catch)); | |
} else { | |
res.Value = static_cast<PersistentValuePtr>(new Value(isolate, result)); | |
// res.Kinds = v8_Value_KindsFromLocal(result); | |
} | |
return res; | |
} | |
// void v8_FunctionTemplate_New(ContextPtr ctxptr, v8::FunctionCallback crystal_cb); | |
PersistentValuePtr v8_FunctionTemplate_New(ContextPtr ctxptr, v8::FunctionCallback crystal_cb, char* id) { | |
VALUE_SCOPE(ctxptr); | |
return new Value(isolate, v8::FunctionTemplate::New(isolate, crystal_cb, v8::String::NewFromUtf8(isolate, id))->GetFunction()); | |
} | |
typedef struct { | |
// PersistentValuePtr This; | |
int Length; | |
void** Args; | |
} FunctionCallbackInfo; | |
FunctionCallbackInfo v8_FunctionCallbackInfo(const v8::FunctionCallbackInfo<v8::Value>& args) { | |
v8::Isolate* isolate = args.GetIsolate(); | |
v8::HandleScope scope(isolate); | |
int argc = args.Length(); | |
void* argv[argc]; | |
for (int i = 0; i < argc; i++) { | |
std::cout << "to s: " << str(args[i]) << "\n"; | |
argv[i] = new Value(isolate, args[i]); | |
fprintf(stderr, "pointer of arg %i is %p\n", i, argv[i]); | |
} | |
fprintf(stderr, "sizeof argv %lu\n", sizeof(argv)); | |
// auto holder = new Value(isolate, args.Holder()); | |
// fprintf(stderr, "pointer of holder is %p\n", holder); | |
FunctionCallbackInfo ret ={ | |
// holder, | |
argc, | |
argv | |
}; | |
fprintf(stderr, "sizeof ret %lu\n", sizeof(ret)); | |
return ret; | |
} | |
String v8_FunctionCallbackInfo_Data(const v8::FunctionCallbackInfo<v8::Value>& args) { | |
return str_to_cr_str(str(args.Data())); | |
} | |
// String v8_FunctionCallbackInfo_Data(const v8::FunctionCallbackInfo<v8::Value>& args) { | |
// return str_to_cr_str(str(args.Data())); | |
// } | |
// PersistentValuePtr v8_FunctionCallbackInfo_This(const v8::FunctionCallbackInfo<v8::Value>& args) { | |
// v8::Isolate* iso = args.GetIsolate(); | |
// v8::HandleScope scope(iso); | |
// return new Value(iso, args.This()); | |
// } | |
// PersistentValuePtr* v8_FunctionCallbackInfo_Args(const v8::FunctionCallbackInfo<v8::Value>& args) { | |
// v8::Isolate* iso = args.GetIsolate(); | |
// v8::HandleScope scope(iso); | |
// int argc = args.Length(); | |
// PersistentValuePtr argv[argc]; | |
// for (int i = 0; i < argc; i++) { | |
// argv[i] = new Value(iso, args[i]); | |
// } | |
// return argv; | |
// } | |
// PersistentValuePtr fn_cb_info_arg | |
// void crystal_callback(const v8::FunctionCallbackInfo<v8::Value>& args) { | |
// fprintf(stderr, "in c crystal cb"); | |
// v8::Local<v8::Value> data = args.Data(); | |
// if (data->IsExternal()) { | |
// void *cb = v8::External::Cast(*data)->Value(); | |
// (*cb)(args); | |
// } | |
// } | |
// void crystal_callback(const v8::FunctionCallbackInfo<v8::Value>& args); | |
// FunctionTemplatePtr v8_Context_RegisterCallback( | |
// ContextPtr ctxptr, | |
// const char* name, | |
// const char* id | |
// ) { | |
// VALUE_SCOPE(ctxptr); | |
// v8::Local<v8::FunctionTemplate> cb = | |
// v8::FunctionTemplate::New(isolate, crystal_callback, | |
// v8::String::NewFromUtf8(isolate, id)); | |
// cb->SetClassName(v8::String::NewFromUtf8(isolate, name)); | |
// return new Value(isolate, cb->GetFunction()); | |
// } | |
// void go_callback(const v8::FunctionCallbackInfo<v8::Value>& args) { | |
// v8::Isolate* iso = args.GetIsolate(); | |
// v8::HandleScope scope(iso); | |
// std::string id = str(args.Data()); | |
// std::string src_file, src_func; | |
// int line_number = 0, column = 0; | |
// v8::Local<v8::StackTrace> trace(v8::StackTrace::CurrentStackTrace(iso, 1)); | |
// if (trace->GetFrameCount() == 1) { | |
// v8::Local<v8::StackFrame> frame(trace->GetFrame(0)); | |
// src_file = str(frame->GetScriptName()); | |
// src_func = str(frame->GetFunctionName()); | |
// line_number = frame->GetLineNumber(); | |
// column = frame->GetColumn(); | |
// } | |
// int argc = args.Length(); | |
// ValueKindsPair argv[argc]; | |
// for (int i = 0; i < argc; i++) { | |
// argv[i] = (ValueKindsPair){new Value(iso, args[i]), v8_Value_KindsFromLocal(args[i])}; | |
// } | |
// ValueErrorPair result = | |
// go_callback_handler( | |
// (String){id.data(), int(id.length())}, | |
// (CallerInfo){ | |
// (String){src_func.data(), int(src_func.length())}, | |
// (String){src_file.data(), int(src_file.length())}, | |
// line_number, | |
// column | |
// }, | |
// argc, argv); | |
// if (result.error_msg.ptr != nullptr) { | |
// v8::Local<v8::Value> err = v8::Exception::Error( | |
// v8::String::NewFromUtf8(iso, result.error_msg.ptr, v8::NewStringType::kNormal, result.error_msg.len).ToLocalChecked()); | |
// iso->ThrowException(err); | |
// } else if (result.Value == NULL) { | |
// args.GetReturnValue().Set(v8::Undefined(iso)); | |
// } else { | |
// args.GetReturnValue().Set(*static_cast<Value*>(result.Value)); | |
// } | |
// } | |
PersistentValuePtr v8_Context_Global(ContextPtr ctxptr) { | |
VALUE_SCOPE(ctxptr); | |
return new Value(isolate, ctx->Global()); | |
} | |
void v8_Context_Release(ContextPtr ctxptr) { | |
fprintf(stderr, "RELEASING A VALUE NOW\n"); | |
if (ctxptr == nullptr) { | |
return; | |
} | |
Context* ctx = static_cast<Context*>(ctxptr); | |
ISOLATE_SCOPE(ctx->isolate); | |
ctx->ptr.Reset(); | |
} | |
PersistentValuePtr v8_Context_Create(ContextPtr ctxptr, ImmediateValue val) { | |
VALUE_SCOPE(ctxptr); | |
switch (val.Type) { | |
case tSTRING: | |
return new Value(isolate, v8::String::NewFromUtf8( | |
isolate, val.Str.ptr, v8::NewStringType::kNormal, val.Str.len).ToLocalChecked()); | |
case tNUMBER: return new Value(isolate, v8::Number::New(isolate, val.Num)); break; | |
case tBOOL: return new Value(isolate, v8::Boolean::New(isolate, val.BoolVal == 1)); break; | |
case tOBJECT: return new Value(isolate, v8::Object::New(isolate)); break; | |
case tARRAY: return new Value(isolate, v8::Array::New(isolate, val.Len)); break; | |
case tARRAYBUFFER: { | |
v8::Local<v8::ArrayBuffer> buf = v8::ArrayBuffer::New(isolate, val.Len); | |
memcpy(buf->GetContents().Data(), val.Bytes, val.Len); | |
return new Value(isolate, buf); | |
} break; | |
case tUNDEFINED: return new Value(isolate, v8::Undefined(isolate)); break; | |
} | |
return nullptr; | |
} | |
ValueErrorPair v8_Value_Get(ContextPtr ctxptr, PersistentValuePtr valueptr, const char* field) { | |
VALUE_SCOPE(ctxptr); | |
Value* value = static_cast<Value*>(valueptr); | |
v8::Local<v8::Value> maybeObject = value->Get(isolate); | |
if (!maybeObject->IsObject()) { | |
return (ValueErrorPair){nullptr, str_to_cr_str("Not an object")}; | |
} | |
// We can safely call `ToLocalChecked`, because | |
// we've just created the local object above. | |
v8::Local<v8::Object> object = maybeObject->ToObject(ctx).ToLocalChecked(); | |
v8::Local<v8::Value> localValue = object->Get(ctx, v8::String::NewFromUtf8(isolate, field)).ToLocalChecked(); | |
return (ValueErrorPair){new Value(isolate, localValue), nullptr}; | |
} | |
ValueTuple v8_Value_GetIdx(ContextPtr ctxptr, PersistentValuePtr valueptr, int idx) { | |
VALUE_SCOPE(ctxptr); | |
Value* value = static_cast<Value*>(valueptr); | |
v8::Local<v8::Value> maybeObject = value->Get(isolate); | |
if (!maybeObject->IsObject()) { | |
return (ValueTuple){nullptr, nullptr, 0, str_to_cr_str("Not an object")}; | |
} | |
v8::Local<v8::Value> obj; | |
if (maybeObject->IsArrayBuffer()) { | |
v8::ArrayBuffer* bufPtr = v8::ArrayBuffer::Cast(*maybeObject); | |
if (idx < bufPtr->GetContents().ByteLength()) { | |
obj = v8::Number::New(isolate, ((unsigned char*)bufPtr->GetContents().Data())[idx]); | |
} else { | |
obj = v8::Undefined(isolate); | |
} | |
} else { | |
// We can safely call `ToLocalChecked`, because | |
// we've just created the local object above. | |
v8::Local<v8::Object> object = maybeObject->ToObject(ctx).ToLocalChecked(); | |
obj = object->Get(ctx, uint32_t(idx)).ToLocalChecked(); | |
} | |
return (ValueTuple){new Value(isolate, obj), v8_Value_KindsFromLocal(obj), nullptr}; | |
} | |
Error v8_Value_Set(ContextPtr ctxptr, PersistentValuePtr valueptr, | |
const char* field, PersistentValuePtr new_valueptr) { | |
VALUE_SCOPE(ctxptr); | |
Value* value = static_cast<Value*>(valueptr); | |
v8::Local<v8::Value> maybeObject = value->Get(isolate); | |
if (!maybeObject->IsObject()) { | |
return str_to_cr_str("Not an object"); | |
} | |
// We can safely call `ToLocalChecked`, because | |
// we've just created the local object above. | |
v8::Local<v8::Object> object = | |
maybeObject->ToObject(ctx).ToLocalChecked(); | |
Value* new_value = static_cast<Value*>(new_valueptr); | |
v8::Local<v8::Value> new_value_local = new_value->Get(isolate); | |
v8::Maybe<bool> res = | |
object->Set(ctx, v8::String::NewFromUtf8(isolate, field), new_value_local); | |
if (res.IsNothing()) { | |
return str_to_cr_str("Something went wrong -- set returned nothing."); | |
} else if (!res.FromJust()) { | |
return str_to_cr_str("Something went wrong -- set failed."); | |
} | |
return (Error){nullptr, 0}; | |
} | |
Error v8_Value_SetIdx(ContextPtr ctxptr, PersistentValuePtr valueptr, | |
int idx, PersistentValuePtr new_valueptr) { | |
VALUE_SCOPE(ctxptr); | |
Value* value = static_cast<Value*>(valueptr); | |
v8::Local<v8::Value> maybeObject = value->Get(isolate); | |
if (!maybeObject->IsObject()) { | |
return str_to_cr_str("Not an object"); | |
} | |
Value* new_value = static_cast<Value*>(new_valueptr); | |
v8::Local<v8::Value> new_value_local = new_value->Get(isolate); | |
if (maybeObject->IsArrayBuffer()) { | |
v8::ArrayBuffer* bufPtr = v8::ArrayBuffer::Cast(*maybeObject); | |
if (!new_value_local->IsNumber()) { | |
return str_to_cr_str("Cannot assign non-number into array buffer"); | |
} else if (idx >= bufPtr->GetContents().ByteLength()) { | |
return str_to_cr_str("Cannot assign to an index beyond the size of an array buffer"); | |
} else { | |
((unsigned char*)bufPtr->GetContents().Data())[idx] = new_value_local->ToNumber(ctx).ToLocalChecked()->Value(); | |
} | |
} else { | |
// We can safely call `ToLocalChecked`, because | |
// we've just created the local object above. | |
v8::Local<v8::Object> object = maybeObject->ToObject(ctx).ToLocalChecked(); | |
v8::Maybe<bool> res = object->Set(ctx, uint32_t(idx), new_value_local); | |
if (res.IsNothing()) { | |
return str_to_cr_str("Something went wrong -- set returned nothing."); | |
} else if (!res.FromJust()) { | |
return str_to_cr_str("Something went wrong -- set failed."); | |
} | |
} | |
return (Error){nullptr, 0}; | |
} | |
ValueErrorPair v8_Function_Call(ContextPtr ctxptr, | |
PersistentValuePtr funcptr, | |
PersistentValuePtr selfptr, | |
int argc, PersistentValuePtr* argvptr) { | |
VALUE_SCOPE(ctxptr); | |
fprintf(stderr, "call: got value scope\n"); | |
v8::TryCatch try_catch(isolate); | |
try_catch.SetVerbose(false); | |
fprintf(stderr, "call: got try catch\n"); | |
v8::Local<v8::Value> func_val = static_cast<Value*>(funcptr)->Get(isolate); | |
if (!func_val->IsFunction()) { | |
return (ValueErrorPair){nullptr, str_to_cr_str("Not a function")}; | |
} | |
fprintf(stderr, "call: got func val\n"); | |
v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(func_val); | |
fprintf(stderr, "call: got func\n"); | |
v8::Local<v8::Value> self; | |
if (selfptr == nullptr) { | |
self = ctx->Global(); | |
} else { | |
self = static_cast<Value*>(selfptr)->Get(isolate); | |
} | |
fprintf(stderr, "call: got this value\n"); | |
v8::Local<v8::Value>* argv = new v8::Local<v8::Value>[argc]; | |
for (int i = 0; i < argc; i++) { | |
argv[i] = static_cast<Value*>(argvptr[i])->Get(isolate); | |
} | |
fprintf(stderr, "call: made argv\n"); | |
v8::MaybeLocal<v8::Value> result = func->Call(ctx, self, argc, argv); | |
fprintf(stderr, "call: got maybe res\n"); | |
delete[] argv; | |
if (result.IsEmpty()) { | |
return (ValueErrorPair){nullptr, str_to_cr_str(report_exception(isolate, ctx, try_catch))}; | |
} | |
v8::Local<v8::Value> value = result.ToLocalChecked(); | |
fprintf(stderr, "call: value to local checked\n"); | |
return (ValueErrorPair){ | |
static_cast<PersistentValuePtr>(new Value(isolate, value)), | |
nullptr | |
}; | |
} | |
PersistentValuePtr v8_Object_New(ContextPtr ctxptr) { | |
VALUE_SCOPE(ctxptr); | |
v8::Local<v8::Object> obj = v8::Object::New(isolate); | |
return new Value(isolate, obj); | |
} | |
ValueTuple v8_Value_New(ContextPtr ctxptr, | |
PersistentValuePtr funcptr, | |
int argc, PersistentValuePtr* argvptr) { | |
VALUE_SCOPE(ctxptr); | |
v8::TryCatch try_catch(isolate); | |
try_catch.SetVerbose(false); | |
v8::Local<v8::Value> func_val = static_cast<Value*>(funcptr)->Get(isolate); | |
if (!func_val->IsFunction()) { | |
return (ValueTuple){nullptr, nullptr, 0, str_to_cr_str("Not a function")}; | |
} | |
v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(func_val); | |
v8::Local<v8::Value>* argv = new v8::Local<v8::Value>[argc]; | |
for (int i = 0; i < argc; i++) { | |
argv[i] = static_cast<Value*>(argvptr[i])->Get(isolate); | |
} | |
v8::MaybeLocal<v8::Object> result = func->NewInstance(ctx, argc, argv); | |
delete[] argv; | |
if (result.IsEmpty()) { | |
return (ValueTuple){nullptr, nullptr, 0, str_to_cr_str(report_exception(isolate, ctx, try_catch))}; | |
} | |
v8::Local<v8::Value> value = result.ToLocalChecked(); | |
return (ValueTuple){ | |
static_cast<PersistentValuePtr>(new Value(isolate, value)), | |
v8_Value_KindsFromLocal(value), | |
nullptr | |
}; | |
} | |
void v8_Value_Release(ContextPtr ctxptr, PersistentValuePtr valueptr) { | |
if (valueptr == nullptr || ctxptr == nullptr) { | |
return; | |
} | |
ISOLATE_SCOPE(static_cast<Context*>(ctxptr)->isolate); | |
Value* value = static_cast<Value*>(valueptr); | |
value->Reset(); | |
delete value; | |
} | |
String v8_Value_String(ContextPtr ctxptr, PersistentValuePtr valueptr) { | |
VALUE_SCOPE(ctxptr); | |
// ISOLATE_SCOPE(static_cast<Context*>(ctxptr)->isolate); | |
fprintf(stderr, "got my value scope, value %p\n", valueptr); | |
v8::Local<v8::Value> value = static_cast<Value*>(valueptr)->Get(isolate); | |
fprintf(stderr, "got my value\n"); | |
return str_to_cr_str(value->ToString()); | |
} | |
double v8_Value_Float64(ContextPtr ctxptr, PersistentValuePtr valueptr) { | |
VALUE_SCOPE(ctxptr); | |
v8::Local<v8::Value> value = static_cast<Value*>(valueptr)->Get(isolate); | |
v8::Maybe<double> val = value->NumberValue(ctx); | |
// This should never happen since we shouldn't be calling this unless we know | |
// that it's the appropriate type via the kind checks. | |
if (val.IsNothing()) { | |
return 0; | |
} | |
return val.ToChecked(); | |
} | |
int64_t v8_Value_Int64(ContextPtr ctxptr, PersistentValuePtr valueptr) { | |
VALUE_SCOPE(ctxptr); | |
v8::Local<v8::Value> value = static_cast<Value*>(valueptr)->Get(isolate); | |
v8::Maybe<int64_t> val = value->IntegerValue(ctx); | |
// This should never happen since we shouldn't be calling this unless we know | |
// that it's the appropriate type via the kind checks. | |
if (val.IsNothing()) { | |
return 0; | |
} | |
return val.ToChecked(); | |
} | |
int v8_Value_Bool(ContextPtr ctxptr, PersistentValuePtr valueptr) { | |
VALUE_SCOPE(ctxptr); | |
v8::Local<v8::Value> value = static_cast<Value*>(valueptr)->Get(isolate); | |
v8::Maybe<bool> val = value->BooleanValue(ctx); | |
// This should never happen since we shouldn't be calling this unless we know | |
// that it's the appropriate type via the kind checks. | |
if (val.IsNothing()) { | |
return 0; | |
} | |
return val.ToChecked() ? 1 : 0; | |
} | |
unsigned char* v8_Value_Bytes(ContextPtr ctxptr, PersistentValuePtr valueptr, int * length) { | |
VALUE_SCOPE(ctxptr); | |
v8::Local<v8::Value> value = static_cast<Value*>(valueptr)->Get(isolate); | |
v8::ArrayBuffer* bufPtr; | |
if (value->IsTypedArray()) { | |
bufPtr = *v8::TypedArray::Cast(*value)->Buffer(); | |
} else if (value->IsArrayBuffer()) { | |
bufPtr = v8::ArrayBuffer::Cast(*value); | |
} else { | |
return NULL; | |
} | |
if (bufPtr == NULL) { | |
return NULL; | |
} | |
if (length != NULL) { | |
*length = bufPtr->GetContents().ByteLength(); | |
} | |
return static_cast<unsigned char*>(bufPtr->GetContents().Data()); | |
} | |
bool v8_Value_IsFunction(ContextPtr ctxptr, PersistentValuePtr valueptr) { | |
VALUE_SCOPE(ctxptr); | |
fprintf(stderr,"is func: got value scope, isolate: %p, ctx: %p, valueptr: %p\n", isolate, ctxptr, valueptr); | |
v8::Local<v8::Value> value = static_cast<Value*>(valueptr)->Get(isolate); | |
fprintf(stderr,"is func: got value\n"); | |
return value->IsFunction(); | |
} | |
HeapStatistics v8_Isolate_GetHeapStatistics(IsolatePtr isolate_ptr) { | |
if (isolate_ptr == nullptr) { | |
return HeapStatistics{}; | |
} | |
v8::Isolate* isolate = static_cast<v8::Isolate*>(isolate_ptr); | |
v8::HeapStatistics hs; | |
isolate->GetHeapStatistics(&hs); | |
return HeapStatistics{ | |
hs.total_heap_size(), | |
hs.total_heap_size_executable(), | |
hs.total_physical_size(), | |
hs.total_available_size(), | |
hs.used_heap_size(), | |
hs.heap_size_limit(), | |
hs.malloced_memory(), | |
hs.peak_malloced_memory(), | |
hs.does_zap_garbage() | |
}; | |
} | |
void v8_Isolate_LowMemoryNotification(IsolatePtr isolate_ptr) { | |
if (isolate_ptr == nullptr) { | |
return; | |
} | |
ISOLATE_SCOPE(static_cast<v8::Isolate*>(isolate_ptr)); | |
isolate->LowMemoryNotification(); | |
} | |
ValueTuple v8_Value_PromiseResult(ContextPtr ctxptr, PersistentValuePtr valueptr) { | |
VALUE_SCOPE(ctxptr); | |
v8::Local<v8::Value> value = static_cast<Value*>(valueptr)->Get(isolate); | |
v8::Promise* prom = v8::Promise::Cast(*value); | |
if (prom->State() == v8::Promise::PromiseState::kPending) { | |
return (ValueTuple){nullptr, nullptr, 0, str_to_cr_str("Promise is pending")}; | |
} | |
v8::Local<v8::Value> res = prom->Result(); | |
return (ValueTuple){new Value(isolate, res), v8_Value_KindsFromLocal(res), nullptr}; | |
} | |
uint8_t v8_Value_PromiseState(ContextPtr ctxptr, PersistentValuePtr valueptr) { | |
VALUE_SCOPE(ctxptr); | |
v8::Local<v8::Value> value = static_cast<Value*>(valueptr)->Get(isolate); | |
v8::Promise* prom = v8::Promise::Cast(*value); | |
return prom->State(); | |
} | |
class FileOutputStream : public v8::OutputStream { | |
public: | |
FileOutputStream(FILE* stream) : stream_(stream) {} | |
virtual int GetChunkSize() { | |
return 65536; // big chunks == faster | |
} | |
virtual void EndOfStream() {} | |
virtual WriteResult WriteAsciiChunk(char* data, int size) { | |
const size_t len = static_cast<size_t>(size); | |
size_t off = 0; | |
while (off < len && !feof(stream_) && !ferror(stream_)) | |
off += fwrite(data + off, 1, len - off, stream_); | |
return off == len ? kContinue : kAbort; | |
} | |
private: | |
FILE* stream_; | |
}; | |
bool v8_Isolate_TakeHeapSnapshot(IsolatePtr isolate_ptr, const char* filename) { | |
if (isolate_ptr == nullptr) { | |
return false; | |
} | |
ISOLATE_SCOPE(static_cast<v8::Isolate*>(isolate_ptr)) | |
FILE* fp = fopen(filename, "w"); | |
if (fp == NULL) return false; | |
const v8::HeapSnapshot* const snap = isolate->GetHeapProfiler()->TakeHeapSnapshot(); | |
FileOutputStream stream(fp); | |
snap->Serialize(&stream, v8::HeapSnapshot::kJSON); | |
fclose(fp); | |
// Work around a deficiency in the API. The HeapSnapshot object is const | |
// but we cannot call HeapProfiler::DeleteAllHeapSnapshots() because that | |
// invalidates _all_ snapshots, including those created by other tools. | |
const_cast<v8::HeapSnapshot*>(snap)->Delete(); | |
return true; | |
} | |
void v8_Isolate_MemoryPressureNotification(IsolatePtr isolate_ptr, uint8_t level) { | |
v8::Isolate* isolate = static_cast<v8::Isolate*>(isolate_ptr); | |
v8::MemoryPressureLevel levelToSend; | |
if (level == 0) { | |
levelToSend = v8::MemoryPressureLevel::kNone; | |
} else if (level == 1) { | |
levelToSend = v8::MemoryPressureLevel::kModerate; | |
} else { | |
levelToSend = v8::MemoryPressureLevel::kCritical; | |
} | |
isolate->MemoryPressureNotification(static_cast<v8::MemoryPressureLevel>(level)); | |
} | |
} // extern "C" |
This file contains 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 <stdlib.h> | |
#include <string.h> | |
#include <stddef.h> | |
#include <stdint.h> | |
#include <stdbool.h> | |
#ifndef V8_C_BRIDGE_H | |
#define V8_C_BRIDGE_H | |
#ifdef __cplusplus | |
extern "C" { | |
#endif | |
typedef void* IsolatePtr; | |
typedef void* ContextPtr; | |
typedef void* PersistentValuePtr; | |
typedef void* FunctionTemplate; | |
// typedef void* FunctionCallback; | |
typedef struct { | |
const char* ptr; | |
int len; | |
} String; | |
typedef String Error; | |
typedef String StartupData; | |
typedef struct { | |
size_t total_heap_size; | |
size_t total_heap_size_executable; | |
size_t total_physical_size; | |
size_t total_available_size; | |
size_t used_heap_size; | |
size_t heap_size_limit; | |
size_t malloced_memory; | |
size_t peak_malloced_memory; | |
size_t does_zap_garbage; | |
} HeapStatistics; | |
typedef struct { | |
PersistentValuePtr Value; | |
Error error_msg; | |
} ValueErrorPair; | |
// NOTE! These values must exactly match the values in kinds.go. Any mismatch | |
// will cause kinds to be misreported. | |
typedef enum { | |
kUndefined, | |
kNull, | |
kTrue, | |
kFalse, | |
kName, | |
kString, | |
kSymbol, | |
kFunction, | |
kArray, | |
kObject, | |
kBoolean, | |
kNumber, | |
kExternal, | |
kInt32, | |
kUint32, | |
kDate, | |
kArgumentsObject, | |
kBooleanObject, | |
kNumberObject, | |
kStringObject, | |
kSymbolObject, | |
kNativeError, | |
kRegExp, | |
kAsyncFunction, | |
kGeneratorFunction, | |
kGeneratorObject, | |
kPromise, | |
kMap, | |
kSet, | |
kMapIterator, | |
kSetIterator, | |
kWeakMap, | |
kWeakSet, | |
kArrayBuffer, | |
kArrayBufferView, | |
kTypedArray, | |
kUint8Array, | |
kUint8ClampedArray, | |
kInt8Array, | |
kUint16Array, | |
kInt16Array, | |
kUint32Array, | |
kInt32Array, | |
kFloat32Array, | |
kFloat64Array, | |
kDataView, | |
kSharedArrayBuffer, | |
kProxy, | |
kWebAssemblyCompiledModule, | |
} ValueKind; | |
typedef struct { | |
const uint8_t* ptr; | |
size_t len; | |
} ValueKinds; | |
typedef struct { | |
PersistentValuePtr Value; | |
ValueKinds Kinds; | |
} ValueKindsPair; | |
typedef enum { | |
kNone, | |
kModerate, | |
kCritical, | |
} MemoryPressureLevel; | |
typedef struct { | |
PersistentValuePtr Value; | |
ValueKinds Kinds; | |
Error error_msg; | |
} ValueTuple; | |
typedef struct { | |
String Funcname; | |
String Filename; | |
int Line; | |
int Column; | |
} CallerInfo; | |
typedef struct { int Major, Minor, Build, Patch; } Version; | |
extern Version version; | |
typedef unsigned int uint32_t; | |
// v8_init must be called once before anything else. | |
extern void v8_init(); | |
extern StartupData v8_CreateSnapshotDataBlob(const char* js); | |
extern IsolatePtr v8_Isolate_New(StartupData data); | |
extern ContextPtr v8_Isolate_NewContext(IsolatePtr isolate); | |
extern void v8_Isolate_Terminate(IsolatePtr isolate); | |
extern void v8_Isolate_Release(IsolatePtr isolate); | |
extern HeapStatistics v8_Isolate_GetHeapStatistics(IsolatePtr isolate); | |
extern void v8_Isolate_LowMemoryNotification(IsolatePtr isolate); | |
// extern void v8_Context_Run(ContextPtr ctx, | |
// const char* code, const char* filename); | |
extern PersistentValuePtr v8_Context_RegisterCallback(ContextPtr ctx, | |
const char* name, const char* id); | |
extern PersistentValuePtr v8_Context_Global(ContextPtr ctx); | |
extern void v8_Context_Release(ContextPtr ctx); | |
typedef enum { tSTRING, tBOOL, tNUMBER, tOBJECT, tARRAY, tARRAYBUFFER, tUNDEFINED } ImmediateValueType; | |
typedef struct { | |
ImmediateValueType Type; | |
String Str; | |
int BoolVal; | |
double Num; | |
unsigned char* Bytes; | |
int Len; | |
} ImmediateValue; | |
extern Version v8_Version(); | |
extern PersistentValuePtr v8_Context_Create(ContextPtr ctx, ImmediateValue val); | |
// extern ValueTuple v8_Value_Get(ContextPtr ctx, PersistentValuePtr value, const char* field); | |
// extern Error v8_Value_Set(ContextPtr ctx, PersistentValuePtr value, | |
// const char* field, PersistentValuePtr new_value); | |
extern ValueTuple v8_Value_GetIdx(ContextPtr ctx, PersistentValuePtr value, int idx); | |
extern Error v8_Value_SetIdx(ContextPtr ctx, PersistentValuePtr value, | |
int idx, PersistentValuePtr new_value); | |
extern ValueTuple v8_Value_PromiseResult(ContextPtr ctx, PersistentValuePtr value); | |
extern uint8_t v8_Value_PromiseState(ContextPtr ctx, PersistentValuePtr value); | |
// extern ValueTuple v8_Value_Call(ContextPtr ctx, | |
// PersistentValuePtr func, | |
// PersistentValuePtr self, | |
// int argc, PersistentValuePtr* argv); | |
extern ValueTuple v8_Value_New(ContextPtr ctx, | |
PersistentValuePtr func, | |
int argc, PersistentValuePtr* argv); | |
extern void v8_Value_Release(ContextPtr ctx, PersistentValuePtr value); | |
// extern String v8_Value_String(ContextPtr ctx, PersistentValuePtr value); | |
extern double v8_Value_Float64(ContextPtr ctx, PersistentValuePtr value); | |
extern int64_t v8_Value_Int64(ContextPtr ctx, PersistentValuePtr value); | |
extern int v8_Value_Bool(ContextPtr ctx, PersistentValuePtr value); | |
extern unsigned char* v8_Value_Bytes(ContextPtr ctx, PersistentValuePtr value, int * length); | |
extern bool v8_Isolate_TakeHeapSnapshot(IsolatePtr iso, const char* filename); | |
extern void v8_Isolate_MemoryPressureNotification(IsolatePtr iso, uint8_t level); | |
#ifdef __cplusplus | |
} | |
#endif | |
#endif // !defined(V8_C_BRIDGE_H) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment