Last active
January 9, 2017 21:38
-
-
Save Chaircrusher/48d53ee78567ca213f7a858e91e4771d to your computer and use it in GitHub Desktop.
cut down example of making a C++ 'object' for V8
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
// Example Class instantiation | |
// by Kent Williams [email protected] | |
// | |
#include <iostream> | |
#include <cstring> | |
#include <memory> | |
#include <v8.h> | |
#include <libplatform/libplatform.h> | |
// template for TestClass | |
v8::Eternal<v8::ObjectTemplate> TestClassGlobal; | |
long long TestClassIdentifier(0); | |
// a sort-of destructor in v8 land | |
void TestClassMakeWeak(const v8::WeakCallbackInfo<char> &data) { | |
// the private object is the doc root handle, which doesn't need | |
// deleting. | |
// | |
delete [] data.GetParameter(); | |
} | |
// SetTestClassData -- set the private data in the class methods | |
static void SetTestClassData(v8::Isolate *isolate,v8::Local<v8::Object> obj, char *s) { | |
v8::HandleScope handle_scope(isolate); | |
#if 0 | |
// I think anything wrapped with v8::External needs to be | |
// explicitly freed. | |
// get the saved field | |
v8::Local<v8::Value> oldValue = obj->GetInternalField(0); | |
v8::Local<v8::External> Extern = v8::Local<v8::External>::Cast(oldValue); | |
// get the raw pointer | |
void *oldval = Extern->Value(); | |
// delete it | |
if(oldval) | |
delete [] static_cast<char *>(oldval); | |
#endif | |
obj->SetInternalField(0,v8::External::New(isolate,s)); | |
obj->SetAlignedPointerInInternalField(1,&TestClassIdentifier); | |
v8::Persistent<v8::Object,v8::CopyablePersistentTraits<v8::Object>> pobj(isolate,obj); | |
pobj.SetWeak<char>(s,TestClassMakeWeak,v8::WeakCallbackType::kParameter); | |
} | |
// true if this object is an instance of the TestClass | |
bool IsTestClassObject(v8::Local<v8::Object> obj) { | |
int fieldcount = obj->InternalFieldCount(); | |
const void *id = nullptr; | |
if(fieldcount >= 2) | |
id = obj->GetAlignedPointerFromInternalField(1); | |
return id == static_cast<void *>(&TestClassIdentifier); | |
} | |
// | |
// Constructor function to call from C++ to get a new instance. | |
// the internal field is set to null | |
v8::Local<v8::Object> NewTestClass(v8::Isolate *isolate,v8::Local<v8::Context> context) { | |
v8::EscapableHandleScope handle_scope(isolate); | |
v8::Local<v8::Object> retobj = TestClassGlobal.Get(isolate)->NewInstance(context).ToLocalChecked(); | |
// does this do the right thing? | |
retobj->SetAlignedPointerInInternalField(0,nullptr); | |
retobj->SetAlignedPointerInInternalField(1,static_cast<void *>(&TestClassIdentifier)); | |
return handle_scope.Escape(retobj); | |
} | |
// | |
// given a string argument, set it as the internal value | |
void SetPrivateString(const v8::FunctionCallbackInfo<v8::Value> &args) { | |
v8::Isolate *isolate(args.GetIsolate()); | |
v8::HandleScope handle_scope(isolate); | |
args.GetReturnValue().Set(v8::Null(isolate)); | |
if(!IsTestClassObject(args.This())) | |
return; | |
if(args.Length() < 1 || !args[0]->IsString()) | |
return; | |
v8::String::Utf8Value tmp(args[0]->ToString()); | |
// allocate string | |
const char *val = *tmp ? *tmp : ""; | |
char *setval = new char [strlen(val)+1]; | |
strcpy(setval,val); | |
// set the internal value | |
SetTestClassData(isolate,args.This(),setval); | |
args.GetReturnValue().Set(v8::True(isolate)); | |
} | |
// retrieve string and return it. | |
void GetPrivateString(const v8::FunctionCallbackInfo<v8::Value> &args) { | |
v8::Isolate *isolate(args.GetIsolate()); | |
v8::HandleScope handle_scope(isolate); | |
args.GetReturnValue().Set(v8::Null(isolate)); | |
if(!IsTestClassObject(args.This())) | |
return; | |
v8::Local<v8::External> Extern = | |
v8::Local<v8::External>::Cast(args.This()->GetInternalField(0)); | |
char *rval = static_cast<char *>(Extern->Value()); | |
v8::Local<v8::String> s = v8::String::NewFromUtf8(isolate,rval); | |
args.GetReturnValue().Set(s); | |
} | |
// to list object methods | |
struct v8FunctionDescriptor { | |
const char *name; | |
v8::FunctionCallback func; | |
}; | |
// list of object methods | |
static v8FunctionDescriptor TestClassMethods[] = { | |
// list of name/functions | |
{ "SetPrivateString", SetPrivateString }, | |
{ "GetPrivateString", GetPrivateString }, | |
{ nullptr, nullptr }, | |
}; | |
// constructor called from JS to make | |
static void TestClassConstructor(const v8::FunctionCallbackInfo<v8::Value> &args) { | |
v8::Isolate *isolate(args.GetIsolate()); | |
v8::HandleScope handle_scope(isolate); | |
v8::Local<v8::Context> context = isolate->GetCurrentContext(); | |
// | |
// get upset if this isn't a constructor call | |
if(!args.IsConstructCall()) { | |
isolate->ThrowException(v8::String::NewFromUtf8(isolate, | |
"Cannot call constructor as function")); | |
} | |
v8::Local<v8::Object> holder = args.Holder(); | |
v8::Local<v8::Object> This = args.This(); | |
v8::Local<v8::Value> proto = holder->GetPrototype(); | |
if(proto->IsObject()) | |
This->SetPrototype(context,proto); | |
// | |
// private data | |
char *privateString = new char[512]; | |
std::strcpy(privateString,"Hello From TestClass"); | |
SetTestClassData(isolate,This,privateString); | |
args.GetReturnValue().Set(This); | |
} | |
void init_TestClass(v8::Isolate *isolate, | |
v8::Local<v8::Context> &context) { | |
v8::HandleScope handle_scope(isolate); | |
// name of 'class' | |
v8::Local<v8::String> name = | |
v8::String::NewFromUtf8(isolate, "TestClass", | |
v8::NewStringType::kNormal).ToLocalChecked(); | |
// object template for the class | |
v8::Local<v8::ObjectTemplate> instance_template = v8::ObjectTemplate::New(isolate); | |
// tell it to allocate 2 internal fields | |
instance_template->SetInternalFieldCount(2); | |
// give it the constructor to call | |
instance_template->SetCallAsFunctionHandler(TestClassConstructor); | |
// save Eternal pointer in order to call NewInstance from C++ | |
TestClassGlobal.Set(isolate,instance_template); | |
// add functions to object template | |
for(unsigned i = 0; TestClassMethods[i].name != nullptr; ++i) { | |
v8::Local<v8::FunctionTemplate> ft = | |
v8::FunctionTemplate::New(isolate, TestClassMethods[i].func); | |
ft->SetClassName(name); | |
v8::Local<v8::String> curName = | |
v8::String::NewFromUtf8(isolate, TestClassMethods[i].name, | |
v8::NewStringType::kNormal).ToLocalChecked(); | |
// testClass_PrototypeTemplate->Set(curName, ft->GetFunction(context).ToLocalChecked()); | |
instance_template->Set(curName, ft); | |
} | |
v8::Local<v8::Object> instance = instance_template->NewInstance(context).ToLocalChecked(); | |
context->Global()->Set(context,name, instance).FromJust(); | |
} | |
// Extracts a C string from a V8 Utf8Value. | |
const char* ToCString(const v8::String::Utf8Value& value) { | |
return *value ? *value : "<string conversion failed>"; | |
} | |
void Print(const v8::FunctionCallbackInfo<v8::Value>& args); | |
void ReportException(v8::Isolate* isolate, v8::TryCatch* try_catch); | |
v8::Local<v8::Context> CreateShellContext(v8::Isolate* isolate); | |
const char TestScript[] = | |
"print('1. this is a test');\n" | |
"var x = new TestClass;\n" | |
"x.SetPrivateString('This Is A test');\n" | |
"print('2. ' + x.GetPrivateString());\n" | |
"x.SetPrivateString('This Is A test');\n" | |
"print('3. ' + x.GetPrivateString());\n"; | |
int main(int argc, char **argv) { | |
// Initialize V8. | |
v8::V8::InitializeICUDefaultLocation(argv[0]); | |
v8::V8::InitializeExternalStartupData(argv[0]); | |
v8::Platform* platform = v8::platform::CreateDefaultPlatform(); | |
v8::V8::InitializePlatform(platform); | |
v8::V8::Initialize(); | |
// Create a new Isolate and make it the current one. | |
v8::Isolate::CreateParams create_params; | |
create_params.array_buffer_allocator = | |
v8::ArrayBuffer::Allocator::NewDefaultAllocator(); | |
v8::Isolate* isolate = v8::Isolate::New(create_params); | |
{ | |
v8::Isolate::Scope isolate_scope(isolate); | |
// Create a stack-allocated handle scope. | |
v8::HandleScope handle_scope(isolate); | |
// create context with global template | |
v8::Local<v8::Context> context = CreateShellContext(isolate); | |
if (context.IsEmpty()) { | |
fprintf(stderr, "Error creating context\n"); | |
return 1; | |
} | |
// Enter the context for compiling and running the hello world script. | |
v8::Context::Scope context_scope(context); | |
// Create a string containing the JavaScript source code. | |
v8::Local<v8::String> source = | |
v8::String::NewFromUtf8(isolate, TestScript, | |
v8::NewStringType::kNormal).ToLocalChecked(); | |
v8::TryCatch try_catch(isolate); | |
v8::Local<v8::Script> script; | |
// Compile the source code. | |
if(!v8::Script::Compile(context, source).ToLocal(&script)) { | |
ReportException(isolate, &try_catch); | |
} | |
// Run the script to get the result. | |
v8::MaybeLocal<v8::Value> result = script->Run(context); | |
if(!result.IsEmpty()) { | |
// Convert the result to an UTF8 string and print it. | |
v8::String::Utf8Value utf8(result.ToLocalChecked()); | |
printf("%s\n", *utf8); | |
} else { | |
ReportException(isolate, &try_catch); | |
} | |
} | |
// Dispose the isolate and tear down V8. | |
isolate->Dispose(); | |
v8::V8::Dispose(); | |
v8::V8::ShutdownPlatform(); | |
delete platform; | |
delete create_params.array_buffer_allocator; | |
return 0; | |
} | |
// The callback that is invoked by v8 whenever the JavaScript 'print' | |
// function is called. Prints its arguments on stdout separated by | |
// spaces and ending with a newline. | |
void Print(const v8::FunctionCallbackInfo<v8::Value>& args) { | |
v8::Isolate *isolate(args.GetIsolate()); | |
bool first = true; | |
for (int i = 0; i < args.Length(); i++) { | |
v8::HandleScope handle_scope(isolate); | |
if (first) { | |
first = false; | |
} else { | |
printf(" "); | |
} | |
v8::String::Utf8Value str(args[i]); | |
const char* cstr = ToCString(str); | |
printf("%s", cstr); | |
} | |
printf("\n"); | |
fflush(stdout); | |
args.GetReturnValue().Set(v8::True(isolate)); | |
} | |
v8::Local<v8::Context> CreateShellContext(v8::Isolate* isolate) { | |
// Create a template for the global object. | |
v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate); | |
// Bind the global 'print' function to the C++ Print callback. | |
global->Set( | |
v8::String::NewFromUtf8(isolate, "print", v8::NewStringType::kNormal) | |
.ToLocalChecked(), | |
v8::FunctionTemplate::New(isolate, Print)); | |
v8::Local<v8::Context> rval = v8::Context::New(isolate, NULL, global); | |
// add our test class | |
init_TestClass(isolate,rval); | |
return rval; | |
} | |
void ReportException(v8::Isolate* isolate, v8::TryCatch* try_catch) { | |
v8::HandleScope handle_scope(isolate); | |
v8::String::Utf8Value exception(try_catch->Exception()); | |
const char* exception_string = ToCString(exception); | |
v8::Local<v8::Message> message = try_catch->Message(); | |
if (message.IsEmpty()) { | |
// V8 didn't provide any extra information about this error; just | |
// print the exception. | |
fprintf(stderr, "%s\n", exception_string); | |
} else { | |
// Print (filename):(line number): (message). | |
v8::String::Utf8Value filename(message->GetScriptOrigin().ResourceName()); | |
v8::Local<v8::Context> context(isolate->GetCurrentContext()); | |
const char* filename_string = ToCString(filename); | |
int linenum = message->GetLineNumber(context).FromJust(); | |
fprintf(stderr, "%s:%i: %s\n", filename_string, linenum, exception_string); | |
// Print line of source code. | |
v8::String::Utf8Value sourceline( | |
message->GetSourceLine(context).ToLocalChecked()); | |
const char* sourceline_string = ToCString(sourceline); | |
fprintf(stderr, "%s\n", sourceline_string); | |
// Print wavy underline (GetUnderline is deprecated). | |
int start = message->GetStartColumn(context).FromJust(); | |
for (int i = 0; i < start; i++) { | |
fprintf(stderr, " "); | |
} | |
int end = message->GetEndColumn(context).FromJust(); | |
for (int i = start; i < end; i++) { | |
fprintf(stderr, "^"); | |
} | |
fprintf(stderr, "\n"); | |
v8::Local<v8::Value> stack_trace_string; | |
if (try_catch->StackTrace(context).ToLocal(&stack_trace_string) && | |
stack_trace_string->IsString() && | |
v8::Local<v8::String>::Cast(stack_trace_string)->Length() > 0) { | |
v8::String::Utf8Value stack_trace(stack_trace_string); | |
const char* stack_trace_string = ToCString(stack_trace); | |
fprintf(stderr, "%s\n", stack_trace_string); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment